/* Allow all traffic destined to the bridge, with a valid network address */ static int iptablesForwardAllowIn(iptablesContext *ctx, virSocketAddr *netaddr, unsigned int prefix, const char *iface, const char *physdev, int action) { int ret; char *networkstr; if (!(networkstr = iptablesFormatNetwork(netaddr, prefix))) return -1; if (physdev && physdev[0]) { ret = iptablesAddRemoveRule(ctx->forward_filter, VIR_SOCKET_ADDR_FAMILY(netaddr), action, "--destination", networkstr, "--in-interface", physdev, "--out-interface", iface, "--jump", "ACCEPT", NULL); } else { ret = iptablesAddRemoveRule(ctx->forward_filter, VIR_SOCKET_ADDR_FAMILY(netaddr), action, "--destination", networkstr, "--out-interface", iface, "--jump", "ACCEPT", NULL); } VIR_FREE(networkstr); return ret; }
/* Allow all traffic coming from the bridge, with a valid network address * to proceed to WAN */ static int iptablesForwardAllowOut(iptablesContext *ctx, virSocketAddr *netaddr, unsigned int prefix, const char *iface, const char *physdev, int action) { int ret; char *networkstr; virCommandPtr cmd = NULL; if (!(networkstr = iptablesFormatNetwork(netaddr, prefix))) return -1; cmd = iptablesCommandNew(ctx->forward_filter, VIR_SOCKET_ADDR_FAMILY(netaddr), action); virCommandAddArgList(cmd, "--source", networkstr, "--in-interface", iface, NULL); if (physdev && physdev[0]) virCommandAddArgList(cmd, "--out-interface", physdev, NULL); virCommandAddArgList(cmd, "--jump", "ACCEPT", NULL); ret = iptablesCommandRunAndFree(cmd); VIR_FREE(networkstr); return ret; }
/* Allow all traffic destined to the bridge, with a valid network address */ static int iptablesForwardAllowIn(virFirewallPtr fw, virSocketAddr *netaddr, unsigned int prefix, const char *iface, const char *physdev, int action) { virFirewallLayer layer = VIR_SOCKET_ADDR_FAMILY(netaddr) == AF_INET ? VIR_FIREWALL_LAYER_IPV4 : VIR_FIREWALL_LAYER_IPV6; VIR_AUTOFREE(char *) networkstr = NULL; if (!(networkstr = iptablesFormatNetwork(netaddr, prefix))) return -1; if (physdev && physdev[0]) virFirewallAddRule(fw, layer, "--table", "filter", action == ADD ? "--insert" : "--delete", "FORWARD", "--destination", networkstr, "--in-interface", physdev, "--out-interface", iface, "--jump", "ACCEPT", NULL); else virFirewallAddRule(fw, layer, "--table", "filter", action == ADD ? "--insert" : "--delete", "FORWARD", "--destination", networkstr, "--out-interface", iface, "--jump", "ACCEPT", NULL); return 0; }
/* Allow all traffic destined to the bridge, with a valid network address * and associated with an existing connection */ static int iptablesForwardAllowRelatedIn(virSocketAddr *netaddr, unsigned int prefix, const char *iface, const char *physdev, int action) { int ret; char *networkstr; if (!(networkstr = iptablesFormatNetwork(netaddr, prefix))) return -1; if (physdev && physdev[0]) { ret = iptablesAddRemoveRule("filter", "FORWARD", VIR_SOCKET_ADDR_FAMILY(netaddr), action, "--destination", networkstr, "--in-interface", physdev, "--out-interface", iface, "--match", "conntrack", "--ctstate", "ESTABLISHED,RELATED", "--jump", "ACCEPT", NULL); } else { ret = iptablesAddRemoveRule("filter", "FORWARD", VIR_SOCKET_ADDR_FAMILY(netaddr), action, "--destination", networkstr, "--out-interface", iface, "--match", "conntrack", "--ctstate", "ESTABLISHED,RELATED", "--jump", "ACCEPT", NULL); } VIR_FREE(networkstr); return ret; }
static int virNetDevGetIPAddressBinary(virSocketAddr *addr, void **data, size_t *len) { if (!addr) return -1; switch (VIR_SOCKET_ADDR_FAMILY(addr)) { case AF_INET: *data = &addr->data.inet4.sin_addr; *len = sizeof(struct in_addr); break; case AF_INET6: *data = &addr->data.inet6.sin6_addr; *len = sizeof(struct in6_addr); break; default: return -1; } return 0; }
static struct nl_msg * virNetDevCreateNetlinkAddressMessage(int messageType, const char *ifname, virSocketAddr *addr, unsigned int prefix, virSocketAddr *broadcast, virSocketAddr *peer) { struct nl_msg *nlmsg = NULL; struct ifaddrmsg ifa; unsigned int ifindex; void *addrData = NULL; void *peerData = NULL; void *broadcastData = NULL; size_t addrDataLen; if (virNetDevGetIPAddressBinary(addr, &addrData, &addrDataLen) < 0) return NULL; if (peer && VIR_SOCKET_ADDR_VALID(peer)) { if (virNetDevGetIPAddressBinary(peer, &peerData, &addrDataLen) < 0) return NULL; } else if (broadcast) { if (virNetDevGetIPAddressBinary(broadcast, &broadcastData, &addrDataLen) < 0) return NULL; } /* Get the interface index */ if ((ifindex = if_nametoindex(ifname)) == 0) return NULL; if (!(nlmsg = nlmsg_alloc_simple(messageType, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL))) { virReportOOMError(); return NULL; } memset(&ifa, 0, sizeof(ifa)); ifa.ifa_prefixlen = prefix; ifa.ifa_family = VIR_SOCKET_ADDR_FAMILY(addr); ifa.ifa_index = ifindex; ifa.ifa_scope = 0; if (nlmsg_append(nlmsg, &ifa, sizeof(ifa), NLMSG_ALIGNTO) < 0) goto buffer_too_small; if (nla_put(nlmsg, IFA_LOCAL, addrDataLen, addrData) < 0) goto buffer_too_small; if (peerData) { if (nla_put(nlmsg, IFA_ADDRESS, addrDataLen, peerData) < 0) goto buffer_too_small; } if (broadcastData) { if (nla_put(nlmsg, IFA_BROADCAST, addrDataLen, broadcastData) < 0) goto buffer_too_small; } return nlmsg; buffer_too_small: virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("allocated netlink buffer is too small")); nlmsg_free(nlmsg); return NULL; }
/** * virNetDevIPRouteAdd: * @ifname: the interface name * @addr: the IP network address (IPv4 or IPv6) * @prefix: number of 1 bits in the netmask * @gateway: via address for route (same as @addr) * * Add a route for a network IP address to an interface. This function * *does not* remove any previously added IP static routes. * * Returns 0 in case of success or -1 in case of error. */ int virNetDevIPRouteAdd(const char *ifname, virSocketAddrPtr addr, unsigned int prefix, virSocketAddrPtr gateway, unsigned int metric) { int ret = -1; struct nl_msg *nlmsg = NULL; struct nlmsghdr *resp = NULL; unsigned int recvbuflen; unsigned int ifindex; struct rtmsg rtmsg; void *gatewayData = NULL; void *addrData = NULL; size_t addrDataLen; int errCode; virSocketAddr defaultAddr; virSocketAddrPtr actualAddr; char *toStr = NULL; char *viaStr = NULL; actualAddr = addr; /* If we have no valid network address, then use the default one */ if (!addr || !VIR_SOCKET_ADDR_VALID(addr)) { VIR_DEBUG("computing default address"); int family = VIR_SOCKET_ADDR_FAMILY(gateway); if (family == AF_INET) { if (virSocketAddrParseIPv4(&defaultAddr, VIR_SOCKET_ADDR_IPV4_ALL) < 0) goto cleanup; } else { if (virSocketAddrParseIPv6(&defaultAddr, VIR_SOCKET_ADDR_IPV6_ALL) < 0) goto cleanup; } actualAddr = &defaultAddr; } toStr = virSocketAddrFormat(actualAddr); viaStr = virSocketAddrFormat(gateway); VIR_DEBUG("Adding route %s/%d via %s", toStr, prefix, viaStr); if (virNetDevGetIPAddressBinary(actualAddr, &addrData, &addrDataLen) < 0 || virNetDevGetIPAddressBinary(gateway, &gatewayData, &addrDataLen) < 0) goto cleanup; /* Get the interface index */ if ((ifindex = if_nametoindex(ifname)) == 0) goto cleanup; if (!(nlmsg = nlmsg_alloc_simple(RTM_NEWROUTE, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL))) { virReportOOMError(); goto cleanup; } memset(&rtmsg, 0, sizeof(rtmsg)); rtmsg.rtm_family = VIR_SOCKET_ADDR_FAMILY(gateway); rtmsg.rtm_table = RT_TABLE_MAIN; rtmsg.rtm_scope = RT_SCOPE_UNIVERSE; rtmsg.rtm_protocol = RTPROT_BOOT; rtmsg.rtm_type = RTN_UNICAST; rtmsg.rtm_dst_len = prefix; if (nlmsg_append(nlmsg, &rtmsg, sizeof(rtmsg), NLMSG_ALIGNTO) < 0) goto buffer_too_small; if (prefix > 0 && nla_put(nlmsg, RTA_DST, addrDataLen, addrData) < 0) goto buffer_too_small; if (nla_put(nlmsg, RTA_GATEWAY, addrDataLen, gatewayData) < 0) goto buffer_too_small; if (nla_put_u32(nlmsg, RTA_OIF, ifindex) < 0) goto buffer_too_small; if (metric > 0 && nla_put_u32(nlmsg, RTA_PRIORITY, metric) < 0) goto buffer_too_small; if (virNetlinkCommand(nlmsg, &resp, &recvbuflen, 0, 0, NETLINK_ROUTE, 0) < 0) goto cleanup; if ((errCode = virNetlinkGetErrorCode(resp, recvbuflen)) < 0) { virReportSystemError(errCode, _("Error adding route to %s"), ifname); goto cleanup; } ret = 0; cleanup: VIR_FREE(toStr); VIR_FREE(viaStr); nlmsg_free(nlmsg); return ret; buffer_too_small: virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("allocated netlink buffer is too small")); goto cleanup; }
/** * virNetDevIPAddrAdd: * @ifname: the interface name * @addr: the IP address (IPv4 or IPv6) * @peer: The IP address of peer (IPv4 or IPv6) * @prefix: number of 1 bits in the netmask * * Add an IP address to an interface. This function *does not* remove * any previously added IP addresses - that must be done separately with * virNetDevIPAddrClear. * * Returns 0 in case of success or -1 in case of error. */ int virNetDevIPAddrAdd(const char *ifname, virSocketAddr *addr, virSocketAddr *peer, unsigned int prefix) { virSocketAddr *broadcast = NULL; int ret = -1; struct nl_msg *nlmsg = NULL; struct nlmsghdr *resp = NULL; unsigned int recvbuflen; char *ipStr = NULL; char *peerStr = NULL; char *bcastStr = NULL; ipStr = virSocketAddrFormat(addr); if (peer && VIR_SOCKET_ADDR_VALID(peer)) peerStr = virSocketAddrFormat(peer); /* The caller needs to provide a correct address */ if (VIR_SOCKET_ADDR_FAMILY(addr) == AF_INET && !(peer && VIR_SOCKET_ADDR_VALID(peer))) { /* compute a broadcast address if this is IPv4 */ if (VIR_ALLOC(broadcast) < 0) goto cleanup; if (virSocketAddrBroadcastByPrefix(addr, prefix, broadcast) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Failed to determine broadcast address for '%s/%d'"), ipStr, prefix); goto cleanup; } bcastStr = virSocketAddrFormat(broadcast); } VIR_DEBUG("Adding IP address %s/%d%s%s%s%s to %s", NULLSTR(ipStr), prefix, peerStr ? " peer " : "", peerStr ? peerStr : "", bcastStr ? " bcast " : "", bcastStr ? bcastStr : "", ifname); if (!(nlmsg = virNetDevCreateNetlinkAddressMessage(RTM_NEWADDR, ifname, addr, prefix, broadcast, peer))) goto cleanup; if (virNetlinkCommand(nlmsg, &resp, &recvbuflen, 0, 0, NETLINK_ROUTE, 0) < 0) goto cleanup; if (virNetlinkGetErrorCode(resp, recvbuflen) < 0) { virReportError(VIR_ERR_SYSTEM_ERROR, _("Failed to add IP address %s/%d%s%s%s%s to %s"), ipStr, prefix, peerStr ? " peer " : "", peerStr ? peerStr : "", bcastStr ? " bcast " : "", bcastStr ? bcastStr : "", ifname); goto cleanup; } ret = 0; cleanup: VIR_FREE(ipStr); VIR_FREE(peerStr); VIR_FREE(bcastStr); nlmsg_free(nlmsg); VIR_FREE(resp); VIR_FREE(broadcast); return ret; }
/** * virSocketGetRange: * @start: start of an IP range * @end: end of an IP range * @network: IP address of network that should completely contain this range * @prefix: prefix of the network * * Check the order of the 2 addresses and compute the range, this will * return 1 for identical addresses. Errors can come from incompatible * addresses type, excessive range (>= 2^^16) where the two addresses * are unrelated, inverted start and end, or a range that is not * within network/prefix. * * Returns the size of the range or -1 in case of failure */ int virSocketAddrGetRange(virSocketAddrPtr start, virSocketAddrPtr end, virSocketAddrPtr network, int prefix) { int ret = 0; size_t i; virSocketAddr netmask; char *startStr = NULL, *endStr = NULL, *netStr = NULL; if (start == NULL || end == NULL) { virReportError(VIR_ERR_INTERNAL_ERROR, _("NULL argument - %p %p"), start, end); goto error; } startStr = virSocketAddrFormat(start); endStr = virSocketAddrFormat(end); if (!startStr || !endStr) goto error; /*error already reported */ if (VIR_SOCKET_ADDR_FAMILY(start) != VIR_SOCKET_ADDR_FAMILY(end)) { virReportError(VIR_ERR_INTERNAL_ERROR, _("mismatch of address family in range %s - %s"), startStr, endStr); goto error; } if (network) { /* some checks can only be done if we have details of the * network the range should be within */ if (!(netStr = virSocketAddrFormat(network))) goto error; if (VIR_SOCKET_ADDR_FAMILY(start) != VIR_SOCKET_ADDR_FAMILY(network)) { virReportError(VIR_ERR_INTERNAL_ERROR, _("mismatch of address family in " "range %s - %s for network %s"), startStr, endStr, netStr); goto error; } if (prefix < 0 || virSocketAddrPrefixToNetmask(prefix, &netmask, VIR_SOCKET_ADDR_FAMILY(network)) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("bad prefix %d for network %s when " " checking range %s - %s"), prefix, netStr, startStr, endStr); goto error; } /* both start and end of range need to be within network */ if (virSocketAddrCheckNetmask(start, network, &netmask) <= 0 || virSocketAddrCheckNetmask(end, network, &netmask) <= 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("range %s - %s is not entirely within " "network %s/%d"), startStr, endStr, netStr, prefix); goto error; } if (VIR_SOCKET_ADDR_IS_FAMILY(start, AF_INET)) { virSocketAddr netaddr, broadcast; if (virSocketAddrBroadcast(network, &netmask, &broadcast) < 0 || virSocketAddrMask(network, &netmask, &netaddr) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("failed to construct broadcast or network " "address for network %s/%d"), netStr, prefix); goto error; } /* Don't allow the start of the range to be the network * address (usually "...0") or the end of the range to be the * broadcast address (usually "...255"). (the opposite also * isn't allowed, but checking for that is implicit in all the * other combined checks) (IPv6 doesn't have broadcast and * network addresses, so this check is only done for IPv4) */ if (virSocketAddrEqual(start, &netaddr)) { virReportError(VIR_ERR_INTERNAL_ERROR, _("start of range %s - %s in network %s/%d " "is the network address"), startStr, endStr, netStr, prefix); goto error; } if (virSocketAddrEqual(end, &broadcast)) { virReportError(VIR_ERR_INTERNAL_ERROR, _("end of range %s - %s in network %s/%d " "is the broadcast address"), startStr, endStr, netStr, prefix); goto error; } } } if (VIR_SOCKET_ADDR_IS_FAMILY(start, AF_INET)) { virSocketAddrIPv4 t1, t2; if (virSocketAddrGetIPv4Addr(start, &t1) < 0 || virSocketAddrGetIPv4Addr(end, &t2) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("failed to get IPv4 address " "for start or end of range %s - %s"), startStr, endStr); goto error; } /* legacy check that everything except the last two bytes * are the same */ for (i = 0; i < 2; i++) { if (t1[i] != t2[i]) { virReportError(VIR_ERR_INTERNAL_ERROR, _("range %s - %s is too large (> 65535)"), startStr, endStr); goto error; } } ret = (t2[2] - t1[2]) * 256 + (t2[3] - t1[3]); if (ret < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("range %s - %s is reversed "), startStr, endStr); goto error; } ret++; } else if (VIR_SOCKET_ADDR_IS_FAMILY(start, AF_INET6)) { virSocketAddrIPv6 t1, t2; if (virSocketAddrGetIPv6Addr(start, &t1) < 0 || virSocketAddrGetIPv6Addr(end, &t2) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("failed to get IPv6 address " "for start or end of range %s - %s"), startStr, endStr); goto error; } /* legacy check that everything except the last two bytes are * the same */ for (i = 0; i < 7; i++) { if (t1[i] != t2[i]) { virReportError(VIR_ERR_INTERNAL_ERROR, _("range %s - %s is too large (> 65535)"), startStr, endStr); goto error; } } ret = t2[7] - t1[7]; if (ret < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("range %s - %s start larger than end"), startStr, endStr); goto error; } ret++; } else { virReportError(VIR_ERR_INTERNAL_ERROR, _("unsupported address family " "for range %s - %s, must be ipv4 or ipv6"), startStr, endStr); goto error; } cleanup: VIR_FREE(startStr); VIR_FREE(endStr); VIR_FREE(netStr); return ret; error: ret = -1; goto cleanup; }
/* * virSocketAddrFormatFull: * @addr: an initialized virSocketAddrPtr * @withService: if true, then service info is appended * @separator: separator between hostname & service. * * Returns a string representation of the given address. If a format conforming * to URI specification is required, NULL should be passed to separator. * Set @separator only if non-URI format is required, e.g. passing ';' for * @separator if the address should be used with SASL. * Caller must free the returned string. */ char * virSocketAddrFormatFull(const virSocketAddr *addr, bool withService, const char *separator) { char host[NI_MAXHOST], port[NI_MAXSERV]; char *addrstr; int err; if (addr == NULL) { virReportError(VIR_ERR_INVALID_ARG, "%s", _("Missing address")); return NULL; } /* Short-circuit since getnameinfo doesn't work * nicely for UNIX sockets */ if (addr->data.sa.sa_family == AF_UNIX) { if (withService) { if (virAsprintf(&addrstr, "127.0.0.1%s0", separator ? separator : ":") < 0) goto error; } else { if (VIR_STRDUP(addrstr, "127.0.0.1") < 0) goto error; } return addrstr; } if ((err = getnameinfo(&addr->data.sa, addr->len, host, sizeof(host), port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV)) != 0) { virReportError(VIR_ERR_SYSTEM_ERROR, _("Cannot convert socket address to string: %s"), gai_strerror(err)); return NULL; } if (withService) { char *ipv6_host = NULL; /* sasl_new_client demands the socket address to be in an odd format: * a.b.c.d;port or e:f:g:h:i:j:k:l;port, so use square brackets for * IPv6 only if no separator is passed to the function */ if (!separator && VIR_SOCKET_ADDR_FAMILY(addr) == AF_INET6) { if (virAsprintf(&ipv6_host, "[%s]", host) < 0) goto error; } if (virAsprintf(&addrstr, "%s%s%s", ipv6_host ? ipv6_host : host, separator ? separator : ":", port) == -1) goto error; VIR_FREE(ipv6_host); } else { if (VIR_STRDUP(addrstr, host) < 0) goto error; } return addrstr; error: return NULL; }