From cf0568b0af4ef4f903da1b184fc9b232c6f29457 Mon Sep 17 00:00:00 2001 From: Laine Stump Date: Mon, 13 Jun 2016 17:01:27 -0400 Subject: [PATCH] util: new files virnetdevip.[ch] for IP-related netdev functions This patch splits virnetdev.[ch] into multiple files, with the new virnetdevip.[ch] containing all the functions related to setting and retrieving IP-related info for a device (both addresses and routes). --- po/POTFILES.in | 1 + src/Makefile.am | 1 + src/libvirt_private.syms | 13 +- src/lxc/lxc_container.c | 14 +- src/network/bridge_driver.c | 15 +- src/util/virnetdev.c | 711 -------------------------------- src/util/virnetdevip.c | 778 ++++++++++++++++++++++++++++++++++++ src/util/virnetdevip.h | 50 +++ 8 files changed, 853 insertions(+), 730 deletions(-) create mode 100644 src/util/virnetdevip.c create mode 100644 src/util/virnetdevip.h diff --git a/po/POTFILES.in b/po/POTFILES.in index 67838f5210..f44a50199b 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -213,6 +213,7 @@ src/util/virlog.c src/util/virnetdev.c src/util/virnetdevbandwidth.c src/util/virnetdevbridge.c +src/util/virnetdevip.c src/util/virnetdevmacvlan.c src/util/virnetdevmidonet.c src/util/virnetdevopenvswitch.c diff --git a/src/Makefile.am b/src/Makefile.am index b91ff746c4..275bfc750c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -138,6 +138,7 @@ UTIL_SOURCES = \ util/virnetdev.h util/virnetdev.c \ util/virnetdevbandwidth.h util/virnetdevbandwidth.c \ util/virnetdevbridge.h util/virnetdevbridge.c \ + util/virnetdevip.h util/virnetdevip.c \ util/virnetdevmacvlan.c util/virnetdevmacvlan.h \ util/virnetdevmidonet.h util/virnetdevmidonet.c \ util/virnetdevopenvswitch.h util/virnetdevopenvswitch.c \ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 894000613e..36191addac 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1856,15 +1856,12 @@ virMacAddrSetRaw; # util/virnetdev.h virNetDevAddMulti; -virNetDevAddRoute; -virNetDevClearIPAddress; virNetDevDelMulti; virNetDevExists; virNetDevFeatureTypeFromString; virNetDevFeatureTypeToString; virNetDevGetFeatures; virNetDevGetIndex; -virNetDevGetIPAddress; virNetDevGetLinkInfo; virNetDevGetMAC; virNetDevGetMTU; @@ -1890,7 +1887,6 @@ virNetDevRxFilterFree; virNetDevRxFilterModeTypeFromString; virNetDevRxFilterModeTypeToString; virNetDevRxFilterNew; -virNetDevSetIPAddress; virNetDevSetMAC; virNetDevSetMTU; virNetDevSetMTUFromDevice; @@ -1903,7 +1899,6 @@ virNetDevSetRcvMulti; virNetDevSetupControl; virNetDevSysfsFile; virNetDevValidateConfig; -virNetDevWaitDadFinish; # util/virnetdevbandwidth.h @@ -1937,6 +1932,14 @@ virNetDevBridgeSetSTPDelay; virNetDevBridgeSetVlanFiltering; +# util/virnetdevip.h +virNetDevIPAddrAdd; +virNetDevIPAddrDel; +virNetDevIPAddrGet; +virNetDevIPRouteAdd; +virNetDevIPWaitDadFinish; + + # util/virnetdevmacvlan.h virNetDevMacVLanCreate; virNetDevMacVLanCreateWithVPortProfile; diff --git a/src/lxc/lxc_container.c b/src/lxc/lxc_container.c index d94a4838a1..945366ae8b 100644 --- a/src/lxc/lxc_container.c +++ b/src/lxc/lxc_container.c @@ -66,7 +66,7 @@ #include "virfile.h" #include "virusb.h" #include "vircommand.h" -#include "virnetdev.h" +#include "virnetdevip.h" #include "virprocess.h" #include "virstring.h" @@ -528,7 +528,7 @@ static int lxcContainerRenameAndEnableInterfaces(virDomainDefPtr vmDef, VIR_DEBUG("Adding IP address '%s/%d' to '%s'", ipStr, prefix, newname); - if (virNetDevSetIPAddress(newname, &ip->address, NULL, prefix) < 0) { + if (virNetDevIPAddrAdd(newname, &ip->address, NULL, prefix) < 0) { virReportError(VIR_ERR_SYSTEM_ERROR, _("Failed to set IP address '%s' on %s"), ipStr, newname); @@ -549,11 +549,11 @@ static int lxcContainerRenameAndEnableInterfaces(virDomainDefPtr vmDef, for (j = 0; j < netDef->nroutes; j++) { virNetworkRouteDefPtr route = netDef->routes[j]; - if (virNetDevAddRoute(newname, - virNetworkRouteDefGetAddress(route), - virNetworkRouteDefGetPrefix(route), - virNetworkRouteDefGetGateway(route), - virNetworkRouteDefGetMetric(route)) < 0) { + if (virNetDevIPRouteAdd(newname, + virNetworkRouteDefGetAddress(route), + virNetworkRouteDefGetPrefix(route), + virNetworkRouteDefGetGateway(route), + virNetworkRouteDefGetMetric(route)) < 0) { goto error_out; } VIR_FREE(toStr); diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index b2e234a216..ba96f2ef59 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -62,10 +62,11 @@ #include "virdnsmasq.h" #include "configmake.h" #include "virnetdev.h" -#include "virpci.h" +#include "virnetdevip.h" #include "virnetdevbridge.h" #include "virnetdevtap.h" #include "virnetdevvportprofile.h" +#include "virpci.h" #include "virdbus.h" #include "virfile.h" #include "virstring.h" @@ -1977,8 +1978,8 @@ networkAddAddrToBridge(virNetworkObjPtr network, return -1; } - if (virNetDevSetIPAddress(network->def->bridge, - &ipdef->address, NULL, prefix) < 0) + if (virNetDevIPAddrAdd(network->def->bridge, + &ipdef->address, NULL, prefix) < 0) return -1; return 0; @@ -2025,8 +2026,8 @@ networkAddRouteToBridge(virNetworkObjPtr network, return -1; } - if (virNetDevAddRoute(network->def->bridge, addr, - prefix, gateway, metric) < 0) { + if (virNetDevIPRouteAdd(network->def->bridge, addr, + prefix, gateway, metric) < 0) { return -1; } return 0; @@ -2049,7 +2050,7 @@ networkWaitDadFinish(virNetworkObjPtr network) goto cleanup; } - ret = (naddrs == 0) ? 0 : virNetDevWaitDadFinish(addrs, naddrs); + ret = (naddrs == 0) ? 0 : virNetDevIPWaitDadFinish(addrs, naddrs); cleanup: VIR_FREE(addrs); @@ -4760,7 +4761,7 @@ networkGetNetworkAddress(const char *netname, char **netaddr) } if (dev_name) { - if (virNetDevGetIPAddress(dev_name, &addr) < 0) + if (virNetDevIPAddrGet(dev_name, &addr) < 0) goto cleanup; addrptr = &addr; } diff --git a/src/util/virnetdev.c b/src/util/virnetdev.c index 8f4e949874..fa695d4a6e 100644 --- a/src/util/virnetdev.c +++ b/src/util/virnetdev.c @@ -34,10 +34,6 @@ #include "virstring.h" #include "virutil.h" -#if HAVE_GETIFADDRS -# include -#endif - #include #include #include @@ -97,7 +93,6 @@ VIR_LOG_INIT("util.netdev"); # define FEATURE_BIT_IS_SET(blocks, index, field) \ (FEATURE_WORD(blocks, index, field) & FEATURE_FIELD_FLAG(index)) #endif -#define VIR_DAD_WAIT_TIMEOUT 20 /* seconds */ typedef enum { VIR_MCAST_TYPE_INDEX_TOKEN, @@ -1012,712 +1007,6 @@ int virNetDevGetVLanID(const char *ifname ATTRIBUTE_UNUSED, #endif /* ! SIOCGIFVLAN */ -#if defined(__linux__) && defined(HAVE_LIBNL) - -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; -} - -/** - * virNetDevSetIPAddress: - * @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 - * brDelInetAddress. - * - * Returns 0 in case of success or -1 in case of error. - */ -int virNetDevSetIPAddress(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; - - /* 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) - return -1; - - if (virSocketAddrBroadcastByPrefix(addr, prefix, broadcast) < 0) - goto cleanup; - } - - 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, - _("Error adding IP address to %s"), ifname); - goto cleanup; - } - - ret = 0; - cleanup: - nlmsg_free(nlmsg); - VIR_FREE(resp); - VIR_FREE(broadcast); - return ret; -} - -/** - * virNetDevAddRoute: - * @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 -virNetDevAddRoute(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; -} - -/** - * virNetDevClearIPAddress: - * @ifname: the interface name - * @addr: the IP address (IPv4 or IPv6) - * @prefix: number of 1 bits in the netmask - * - * Delete an IP address from an interface. - * - * Returns 0 in case of success or -1 in case of error. - */ -int virNetDevClearIPAddress(const char *ifname, - virSocketAddr *addr, - unsigned int prefix) -{ - int ret = -1; - struct nl_msg *nlmsg = NULL; - struct nlmsghdr *resp = NULL; - unsigned int recvbuflen; - - if (!(nlmsg = virNetDevCreateNetlinkAddressMessage(RTM_DELADDR, ifname, - addr, prefix, - NULL, NULL))) - 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, - _("Error removing IP address from %s"), ifname); - goto cleanup; - } - - ret = 0; - cleanup: - nlmsg_free(nlmsg); - VIR_FREE(resp); - return ret; -} - -/* return true if there is a known address with 'tentative' flag set */ -static bool -virNetDevParseDadStatus(struct nlmsghdr *nlh, int len, - virSocketAddrPtr *addrs, size_t count) -{ - struct ifaddrmsg *ifaddrmsg_ptr; - unsigned int ifaddrmsg_len; - struct rtattr *rtattr_ptr; - size_t i; - struct in6_addr *addr; - - VIR_WARNINGS_NO_CAST_ALIGN - for (; NLMSG_OK(nlh, len); nlh = NLMSG_NEXT(nlh, len)) { - VIR_WARNINGS_RESET - if (NLMSG_PAYLOAD(nlh, 0) < sizeof(struct ifaddrmsg)) { - /* Message without payload is the last one. */ - break; - } - - ifaddrmsg_ptr = (struct ifaddrmsg *)NLMSG_DATA(nlh); - if (!(ifaddrmsg_ptr->ifa_flags & IFA_F_TENTATIVE)) { - /* Not tentative: we are not interested in this entry. */ - continue; - } - - ifaddrmsg_len = IFA_PAYLOAD(nlh); - VIR_WARNINGS_NO_CAST_ALIGN - rtattr_ptr = (struct rtattr *) IFA_RTA(ifaddrmsg_ptr); - for (; RTA_OK(rtattr_ptr, ifaddrmsg_len); - rtattr_ptr = RTA_NEXT(rtattr_ptr, ifaddrmsg_len)) { - VIR_WARNINGS_RESET - if (RTA_PAYLOAD(rtattr_ptr) != sizeof(struct in6_addr)) { - /* No address: ignore. */ - continue; - } - - /* We check only known addresses. */ - for (i = 0; i < count; i++) { - addr = &addrs[i]->data.inet6.sin6_addr; - if (!memcmp(addr, RTA_DATA(rtattr_ptr), - sizeof(struct in6_addr))) { - /* We found matching tentative address. */ - return true; - } - } - } - } - return false; -} - -/* return after DAD finishes for all known IPv6 addresses or an error */ -int -virNetDevWaitDadFinish(virSocketAddrPtr *addrs, size_t count) -{ - struct nl_msg *nlmsg = NULL; - struct ifaddrmsg ifa; - struct nlmsghdr *resp = NULL; - unsigned int recvbuflen; - int ret = -1; - bool dad = true; - time_t max_time = time(NULL) + VIR_DAD_WAIT_TIMEOUT; - - if (!(nlmsg = nlmsg_alloc_simple(RTM_GETADDR, - NLM_F_REQUEST | NLM_F_DUMP))) { - virReportOOMError(); - return -1; - } - - memset(&ifa, 0, sizeof(ifa)); - /* DAD is for IPv6 adresses only. */ - ifa.ifa_family = AF_INET6; - if (nlmsg_append(nlmsg, &ifa, sizeof(ifa), NLMSG_ALIGNTO) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("allocated netlink buffer is too small")); - goto cleanup; - } - - /* Periodically query netlink until DAD finishes on all known addresses. */ - while (dad && time(NULL) < max_time) { - if (virNetlinkCommand(nlmsg, &resp, &recvbuflen, 0, 0, - NETLINK_ROUTE, 0) < 0) - goto cleanup; - - if (virNetlinkGetErrorCode(resp, recvbuflen) < 0) { - virReportError(VIR_ERR_SYSTEM_ERROR, "%s", - _("error reading DAD state information")); - goto cleanup; - } - - /* Parse response. */ - dad = virNetDevParseDadStatus(resp, recvbuflen, addrs, count); - if (dad) - usleep(1000 * 10); - - VIR_FREE(resp); - } - /* Check timeout. */ - if (dad) { - virReportError(VIR_ERR_SYSTEM_ERROR, - _("Duplicate Address Detection " - "not finished in %d seconds"), VIR_DAD_WAIT_TIMEOUT); - } else { - ret = 0; - } - - cleanup: - VIR_FREE(resp); - nlmsg_free(nlmsg); - return ret; -} - -#else /* defined(__linux__) && defined(HAVE_LIBNL) */ - -int virNetDevSetIPAddress(const char *ifname, - virSocketAddr *addr, - virSocketAddr *peer, - unsigned int prefix) -{ - virCommandPtr cmd = NULL; - char *addrstr = NULL, *bcaststr = NULL, *peerstr = NULL; - virSocketAddr broadcast; - int ret = -1; - - if (!(addrstr = virSocketAddrFormat(addr))) - goto cleanup; - - if (peer && VIR_SOCKET_ADDR_VALID(peer) && !(peerstr = virSocketAddrFormat(peer))) - goto cleanup; - - /* format up a broadcast address if this is IPv4 */ - if (!peerstr && ((VIR_SOCKET_ADDR_IS_FAMILY(addr, AF_INET)) && - ((virSocketAddrBroadcastByPrefix(addr, prefix, &broadcast) < 0) || - !(bcaststr = virSocketAddrFormat(&broadcast))))) { - goto cleanup; - } - -# ifdef IFCONFIG_PATH - cmd = virCommandNew(IFCONFIG_PATH); - virCommandAddArg(cmd, ifname); - if (VIR_SOCKET_ADDR_IS_FAMILY(addr, AF_INET6)) - virCommandAddArg(cmd, "inet6"); - else - virCommandAddArg(cmd, "inet"); - virCommandAddArgFormat(cmd, "%s/%u", addrstr, prefix); - if (peerstr) - virCommandAddArgList(cmd, "pointopoint", peerstr, NULL); - if (bcaststr) - virCommandAddArgList(cmd, "broadcast", bcaststr, NULL); - virCommandAddArg(cmd, "alias"); -# else - cmd = virCommandNew(IP_PATH); - virCommandAddArgList(cmd, "addr", "add", NULL); - virCommandAddArgFormat(cmd, "%s/%u", addrstr, prefix); - if (peerstr) - virCommandAddArgList(cmd, "peer", peerstr, NULL); - if (bcaststr) - virCommandAddArgList(cmd, "broadcast", bcaststr, NULL); - virCommandAddArgList(cmd, "dev", ifname, NULL); -# endif - - if (virCommandRun(cmd, NULL) < 0) - goto cleanup; - - ret = 0; - cleanup: - VIR_FREE(addrstr); - VIR_FREE(bcaststr); - VIR_FREE(peerstr); - virCommandFree(cmd); - return ret; -} - -int -virNetDevAddRoute(const char *ifname, - virSocketAddrPtr addr, - unsigned int prefix, - virSocketAddrPtr gateway, - unsigned int metric) -{ - virCommandPtr cmd = NULL; - char *addrstr = NULL, *gatewaystr = NULL; - int ret = -1; - - if (!(addrstr = virSocketAddrFormat(addr))) - goto cleanup; - if (!(gatewaystr = virSocketAddrFormat(gateway))) - goto cleanup; - cmd = virCommandNew(IP_PATH); - virCommandAddArgList(cmd, "route", "add", NULL); - virCommandAddArgFormat(cmd, "%s/%u", addrstr, prefix); - virCommandAddArgList(cmd, "via", gatewaystr, "dev", ifname, - "proto", "static", "metric", NULL); - virCommandAddArgFormat(cmd, "%u", metric); - - if (virCommandRun(cmd, NULL) < 0) - goto cleanup; - - ret = 0; - cleanup: - VIR_FREE(addrstr); - VIR_FREE(gatewaystr); - virCommandFree(cmd); - return ret; -} - -int virNetDevClearIPAddress(const char *ifname, - virSocketAddr *addr, - unsigned int prefix) -{ - virCommandPtr cmd = NULL; - char *addrstr; - int ret = -1; - - if (!(addrstr = virSocketAddrFormat(addr))) - goto cleanup; -# ifdef IFCONFIG_PATH - cmd = virCommandNew(IFCONFIG_PATH); - virCommandAddArg(cmd, ifname); - if (VIR_SOCKET_ADDR_IS_FAMILY(addr, AF_INET6)) - virCommandAddArg(cmd, "inet6"); - else - virCommandAddArg(cmd, "inet"); - virCommandAddArgFormat(cmd, "%s/%u", addrstr, prefix); - virCommandAddArg(cmd, "-alias"); -# else - cmd = virCommandNew(IP_PATH); - virCommandAddArgList(cmd, "addr", "del", NULL); - virCommandAddArgFormat(cmd, "%s/%u", addrstr, prefix); - virCommandAddArgList(cmd, "dev", ifname, NULL); -# endif - - if (virCommandRun(cmd, NULL) < 0) - goto cleanup; - - ret = 0; - cleanup: - VIR_FREE(addrstr); - virCommandFree(cmd); - return ret; -} - -/* return after DAD finishes for all known IPv6 addresses or an error */ -int -virNetDevWaitDadFinish(virSocketAddrPtr *addrs ATTRIBUTE_UNUSED, - size_t count ATTRIBUTE_UNUSED) -{ - virReportSystemError(ENOSYS, "%s", - _("Unable to wait for IPv6 DAD on this platform")); - return -1; -} - -#endif /* defined(__linux__) && defined(HAVE_LIBNL) */ - -/** - * virNetDevGetIPv4AddressIoctl: - * @ifname: name of the interface whose IP address we want - * @addr: filled with the IPv4 address - * - * This function gets the IPv4 address for the interface @ifname - * and stores it in @addr - * - * Returns 0 on success, -errno on failure. - */ -#if defined(SIOCGIFADDR) && defined(HAVE_STRUCT_IFREQ) -static int -virNetDevGetIPv4AddressIoctl(const char *ifname, - virSocketAddrPtr addr) -{ - int fd = -1; - int ret = -1; - struct ifreq ifr; - - if ((fd = virNetDevSetupControl(ifname, &ifr)) < 0) - return -1; - - if (ioctl(fd, SIOCGIFADDR, (char *)&ifr) < 0) { - virReportSystemError(errno, - _("Unable to get IPv4 address for interface %s via ioctl"), - ifname); - goto cleanup; - } - - addr->data.stor.ss_family = AF_INET; - addr->len = sizeof(addr->data.inet4); - memcpy(&addr->data.inet4, &ifr.ifr_addr, addr->len); - ret = 0; - - cleanup: - VIR_FORCE_CLOSE(fd); - return ret; -} - -#else /* ! SIOCGIFADDR */ - -static int -virNetDevGetIPv4AddressIoctl(const char *ifname ATTRIBUTE_UNUSED, - virSocketAddrPtr addr ATTRIBUTE_UNUSED) -{ - return -2; -} - -#endif /* ! SIOCGIFADDR */ - -/** - * virNetDevGetifaddrsAddress: - * @ifname: name of the interface whose IP address we want - * @addr: filled with the IP address - * - * This function gets the IP address for the interface @ifname - * and stores it in @addr - * - * Returns 0 on success, -1 on failure, -2 on unsupported. - */ -#if HAVE_GETIFADDRS -static int -virNetDevGetifaddrsAddress(const char *ifname, - virSocketAddrPtr addr) -{ - struct ifaddrs *ifap, *ifa; - int ret = -1; - - if (getifaddrs(&ifap) < 0) { - virReportSystemError(errno, - _("Could not get interface list for '%s'"), - ifname); - return -1; - } - - for (ifa = ifap; ifa; ifa = ifa->ifa_next) { - int family = ifa->ifa_addr->sa_family; - - if (STRNEQ_NULLABLE(ifa->ifa_name, ifname)) - continue; - if (family != AF_INET6 && family != AF_INET) - continue; - - if (family == AF_INET6) { - addr->len = sizeof(addr->data.inet6); - memcpy(&addr->data.inet6, ifa->ifa_addr, addr->len); - } else { - addr->len = sizeof(addr->data.inet4); - memcpy(&addr->data.inet4, ifa->ifa_addr, addr->len); - } - addr->data.stor.ss_family = family; - ret = 0; - goto cleanup; - } - - virReportError(VIR_ERR_INTERNAL_ERROR, - _("no IP address found for interface '%s'"), - ifname); - cleanup: - freeifaddrs(ifap); - return ret; -} - -#else /* ! HAVE_GETIFADDRS */ - -static int -virNetDevGetifaddrsAddress(const char *ifname ATTRIBUTE_UNUSED, - virSocketAddrPtr addr ATTRIBUTE_UNUSED) -{ - return -2; -} - -#endif - -/** - * virNetDevGetIPAddress: - * @ifname: name of the interface whose IP address we want - * @addr: filled with the IPv4 address - * - * This function gets the IPv4 address for the interface @ifname - * and stores it in @addr - * - * Returns 0 on success, -errno on failure. - */ -int -virNetDevGetIPAddress(const char *ifname, - virSocketAddrPtr addr) -{ - int ret; - - memset(addr, 0, sizeof(*addr)); - addr->data.stor.ss_family = AF_UNSPEC; - - if ((ret = virNetDevGetifaddrsAddress(ifname, addr)) != -2) - return ret; - - if ((ret = virNetDevGetIPv4AddressIoctl(ifname, addr)) != -2) - return ret; - - virReportSystemError(ENOSYS, "%s", - _("Unable to get IP address on this platform")); - return -1; -} - /** * virNetDevValidateConfig: * @ifname: Name of the interface diff --git a/src/util/virnetdevip.c b/src/util/virnetdevip.c new file mode 100644 index 0000000000..044f2bc0d6 --- /dev/null +++ b/src/util/virnetdevip.c @@ -0,0 +1,778 @@ +/* + * Copyright (C) 2007-2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + * + * Authors: + * Mark McLoughlin + * Daniel P. Berrange + */ + +#include + +#include "virnetdevip.h" +#include "virnetdev.h" +#include "virnetlink.h" +#include "virfile.h" +#include "virerror.h" +#include "viralloc.h" +#include "virlog.h" +#include "virstring.h" +#include "virutil.h" + +#if HAVE_GETIFADDRS +# include +#endif + +#include +#include +#include + +#ifdef __linux__ +# include +# include +# define VIR_NETDEV_FAMILY AF_PACKET +#elif defined(HAVE_STRUCT_IFREQ) && defined(AF_LOCAL) +# define VIR_NETDEV_FAMILY AF_LOCAL +#else +# undef HAVE_STRUCT_IFREQ +#endif + +#define VIR_DAD_WAIT_TIMEOUT 20 /* seconds */ + +#define VIR_FROM_THIS VIR_FROM_NONE + +VIR_LOG_INIT("util.netdevip"); + +#if defined(__linux__) && defined(HAVE_LIBNL) + +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; +} + +/** + * 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; + + /* 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) + return -1; + + if (virSocketAddrBroadcastByPrefix(addr, prefix, broadcast) < 0) + goto cleanup; + } + + 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, + _("Error adding IP address to %s"), ifname); + goto cleanup; + } + + ret = 0; + cleanup: + nlmsg_free(nlmsg); + VIR_FREE(resp); + VIR_FREE(broadcast); + return ret; +} + + +/** + * virNetDevIPAddrDel: + * @ifname: the interface name + * @addr: the IP address (IPv4 or IPv6) + * @prefix: number of 1 bits in the netmask + * + * Delete an IP address from an interface. + * + * Returns 0 in case of success or -1 in case of error. + */ +int +virNetDevIPAddrDel(const char *ifname, + virSocketAddr *addr, + unsigned int prefix) +{ + int ret = -1; + struct nl_msg *nlmsg = NULL; + struct nlmsghdr *resp = NULL; + unsigned int recvbuflen; + + if (!(nlmsg = virNetDevCreateNetlinkAddressMessage(RTM_DELADDR, ifname, + addr, prefix, + NULL, NULL))) + 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, + _("Error removing IP address from %s"), ifname); + goto cleanup; + } + + ret = 0; + cleanup: + nlmsg_free(nlmsg); + VIR_FREE(resp); + return ret; +} + + +/** + * 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; +} + + +/* return true if there is a known address with 'tentative' flag set */ +static bool +virNetDevIPParseDadStatus(struct nlmsghdr *nlh, int len, + virSocketAddrPtr *addrs, size_t count) +{ + struct ifaddrmsg *ifaddrmsg_ptr; + unsigned int ifaddrmsg_len; + struct rtattr *rtattr_ptr; + size_t i; + struct in6_addr *addr; + + VIR_WARNINGS_NO_CAST_ALIGN + for (; NLMSG_OK(nlh, len); nlh = NLMSG_NEXT(nlh, len)) { + VIR_WARNINGS_RESET + if (NLMSG_PAYLOAD(nlh, 0) < sizeof(struct ifaddrmsg)) { + /* Message without payload is the last one. */ + break; + } + + ifaddrmsg_ptr = (struct ifaddrmsg *)NLMSG_DATA(nlh); + if (!(ifaddrmsg_ptr->ifa_flags & IFA_F_TENTATIVE)) { + /* Not tentative: we are not interested in this entry. */ + continue; + } + + ifaddrmsg_len = IFA_PAYLOAD(nlh); + VIR_WARNINGS_NO_CAST_ALIGN + rtattr_ptr = (struct rtattr *) IFA_RTA(ifaddrmsg_ptr); + for (; RTA_OK(rtattr_ptr, ifaddrmsg_len); + rtattr_ptr = RTA_NEXT(rtattr_ptr, ifaddrmsg_len)) { + VIR_WARNINGS_RESET + if (RTA_PAYLOAD(rtattr_ptr) != sizeof(struct in6_addr)) { + /* No address: ignore. */ + continue; + } + + /* We check only known addresses. */ + for (i = 0; i < count; i++) { + addr = &addrs[i]->data.inet6.sin6_addr; + if (!memcmp(addr, RTA_DATA(rtattr_ptr), + sizeof(struct in6_addr))) { + /* We found matching tentative address. */ + return true; + } + } + } + } + return false; +} + + +/* return after DAD finishes for all known IPv6 addresses or an error */ +int +virNetDevIPWaitDadFinish(virSocketAddrPtr *addrs, size_t count) +{ + struct nl_msg *nlmsg = NULL; + struct ifaddrmsg ifa; + struct nlmsghdr *resp = NULL; + unsigned int recvbuflen; + int ret = -1; + bool dad = true; + time_t max_time = time(NULL) + VIR_DAD_WAIT_TIMEOUT; + + if (!(nlmsg = nlmsg_alloc_simple(RTM_GETADDR, + NLM_F_REQUEST | NLM_F_DUMP))) { + virReportOOMError(); + return -1; + } + + memset(&ifa, 0, sizeof(ifa)); + /* DAD is for IPv6 adresses only. */ + ifa.ifa_family = AF_INET6; + if (nlmsg_append(nlmsg, &ifa, sizeof(ifa), NLMSG_ALIGNTO) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("allocated netlink buffer is too small")); + goto cleanup; + } + + /* Periodically query netlink until DAD finishes on all known addresses. */ + while (dad && time(NULL) < max_time) { + if (virNetlinkCommand(nlmsg, &resp, &recvbuflen, 0, 0, + NETLINK_ROUTE, 0) < 0) + goto cleanup; + + if (virNetlinkGetErrorCode(resp, recvbuflen) < 0) { + virReportError(VIR_ERR_SYSTEM_ERROR, "%s", + _("error reading DAD state information")); + goto cleanup; + } + + /* Parse response. */ + dad = virNetDevIPParseDadStatus(resp, recvbuflen, addrs, count); + if (dad) + usleep(1000 * 10); + + VIR_FREE(resp); + } + /* Check timeout. */ + if (dad) { + virReportError(VIR_ERR_SYSTEM_ERROR, + _("Duplicate Address Detection " + "not finished in %d seconds"), VIR_DAD_WAIT_TIMEOUT); + } else { + ret = 0; + } + + cleanup: + VIR_FREE(resp); + nlmsg_free(nlmsg); + return ret; +} + + +#else /* defined(__linux__) && defined(HAVE_LIBNL) */ + + +int +virNetDevIPAddrAdd(const char *ifname, + virSocketAddr *addr, + virSocketAddr *peer, + unsigned int prefix) +{ + virCommandPtr cmd = NULL; + char *addrstr = NULL, *bcaststr = NULL, *peerstr = NULL; + virSocketAddr broadcast; + int ret = -1; + + if (!(addrstr = virSocketAddrFormat(addr))) + goto cleanup; + + if (peer && VIR_SOCKET_ADDR_VALID(peer) && !(peerstr = virSocketAddrFormat(peer))) + goto cleanup; + + /* format up a broadcast address if this is IPv4 */ + if (!peerstr && ((VIR_SOCKET_ADDR_IS_FAMILY(addr, AF_INET)) && + ((virSocketAddrBroadcastByPrefix(addr, prefix, &broadcast) < 0) || + !(bcaststr = virSocketAddrFormat(&broadcast))))) { + goto cleanup; + } + +# ifdef IFCONFIG_PATH + cmd = virCommandNew(IFCONFIG_PATH); + virCommandAddArg(cmd, ifname); + if (VIR_SOCKET_ADDR_IS_FAMILY(addr, AF_INET6)) + virCommandAddArg(cmd, "inet6"); + else + virCommandAddArg(cmd, "inet"); + virCommandAddArgFormat(cmd, "%s/%u", addrstr, prefix); + if (peerstr) + virCommandAddArgList(cmd, "pointopoint", peerstr, NULL); + if (bcaststr) + virCommandAddArgList(cmd, "broadcast", bcaststr, NULL); + virCommandAddArg(cmd, "alias"); +# else + cmd = virCommandNew(IP_PATH); + virCommandAddArgList(cmd, "addr", "add", NULL); + virCommandAddArgFormat(cmd, "%s/%u", addrstr, prefix); + if (peerstr) + virCommandAddArgList(cmd, "peer", peerstr, NULL); + if (bcaststr) + virCommandAddArgList(cmd, "broadcast", bcaststr, NULL); + virCommandAddArgList(cmd, "dev", ifname, NULL); +# endif + + if (virCommandRun(cmd, NULL) < 0) + goto cleanup; + + ret = 0; + cleanup: + VIR_FREE(addrstr); + VIR_FREE(bcaststr); + VIR_FREE(peerstr); + virCommandFree(cmd); + return ret; +} + + +int +virNetDevIPAddrDel(const char *ifname, + virSocketAddr *addr, + unsigned int prefix) +{ + virCommandPtr cmd = NULL; + char *addrstr; + int ret = -1; + + if (!(addrstr = virSocketAddrFormat(addr))) + goto cleanup; +# ifdef IFCONFIG_PATH + cmd = virCommandNew(IFCONFIG_PATH); + virCommandAddArg(cmd, ifname); + if (VIR_SOCKET_ADDR_IS_FAMILY(addr, AF_INET6)) + virCommandAddArg(cmd, "inet6"); + else + virCommandAddArg(cmd, "inet"); + virCommandAddArgFormat(cmd, "%s/%u", addrstr, prefix); + virCommandAddArg(cmd, "-alias"); +# else + cmd = virCommandNew(IP_PATH); + virCommandAddArgList(cmd, "addr", "del", NULL); + virCommandAddArgFormat(cmd, "%s/%u", addrstr, prefix); + virCommandAddArgList(cmd, "dev", ifname, NULL); +# endif + + if (virCommandRun(cmd, NULL) < 0) + goto cleanup; + + ret = 0; + cleanup: + VIR_FREE(addrstr); + virCommandFree(cmd); + return ret; +} + + +int +virNetDevIPRouteAdd(const char *ifname, + virSocketAddrPtr addr, + unsigned int prefix, + virSocketAddrPtr gateway, + unsigned int metric) +{ + virCommandPtr cmd = NULL; + char *addrstr = NULL, *gatewaystr = NULL; + int ret = -1; + + if (!(addrstr = virSocketAddrFormat(addr))) + goto cleanup; + if (!(gatewaystr = virSocketAddrFormat(gateway))) + goto cleanup; + cmd = virCommandNew(IP_PATH); + virCommandAddArgList(cmd, "route", "add", NULL); + virCommandAddArgFormat(cmd, "%s/%u", addrstr, prefix); + virCommandAddArgList(cmd, "via", gatewaystr, "dev", ifname, + "proto", "static", "metric", NULL); + virCommandAddArgFormat(cmd, "%u", metric); + + if (virCommandRun(cmd, NULL) < 0) + goto cleanup; + + ret = 0; + cleanup: + VIR_FREE(addrstr); + VIR_FREE(gatewaystr); + virCommandFree(cmd); + return ret; +} + + +/* return after DAD finishes for all known IPv6 addresses or an error */ +int +virNetDevWaitDadFinish(virSocketAddrPtr *addrs ATTRIBUTE_UNUSED, + size_t count ATTRIBUTE_UNUSED) +{ + virReportSystemError(ENOSYS, "%s", + _("Unable to wait for IPv6 DAD on this platform")); + return -1; +} + + +#endif /* defined(__linux__) && defined(HAVE_LIBNL) */ + + +/** + * virNetDevGetIPv4AddressIoctl: + * @ifname: name of the interface whose IP address we want + * @addr: filled with the IPv4 address + * + * This function gets the IPv4 address for the interface @ifname + * and stores it in @addr + * + * Returns 0 on success, -errno on failure. + */ +#if defined(SIOCGIFADDR) && defined(HAVE_STRUCT_IFREQ) +static int +virNetDevGetIPv4AddressIoctl(const char *ifname, + virSocketAddrPtr addr) +{ + int fd = -1; + int ret = -1; + struct ifreq ifr; + + if ((fd = virNetDevSetupControl(ifname, &ifr)) < 0) + return -1; + + if (ioctl(fd, SIOCGIFADDR, (char *)&ifr) < 0) { + virReportSystemError(errno, + _("Unable to get IPv4 address for interface %s via ioctl"), + ifname); + goto cleanup; + } + + addr->data.stor.ss_family = AF_INET; + addr->len = sizeof(addr->data.inet4); + memcpy(&addr->data.inet4, &ifr.ifr_addr, addr->len); + ret = 0; + + cleanup: + VIR_FORCE_CLOSE(fd); + return ret; +} + +#else /* ! SIOCGIFADDR */ + +static int +virNetDevGetIPv4AddressIoctl(const char *ifname ATTRIBUTE_UNUSED, + virSocketAddrPtr addr ATTRIBUTE_UNUSED) +{ + return -2; +} + +#endif /* ! SIOCGIFADDR */ + +/** + * virNetDevGetifaddrsAddress: + * @ifname: name of the interface whose IP address we want + * @addr: filled with the IP address + * + * This function gets the IP address for the interface @ifname + * and stores it in @addr + * + * Returns 0 on success, -1 on failure, -2 on unsupported. + */ +#if HAVE_GETIFADDRS +static int +virNetDevGetifaddrsAddress(const char *ifname, + virSocketAddrPtr addr) +{ + struct ifaddrs *ifap, *ifa; + int ret = -1; + + if (getifaddrs(&ifap) < 0) { + virReportSystemError(errno, + _("Could not get interface list for '%s'"), + ifname); + return -1; + } + + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { + int family = ifa->ifa_addr->sa_family; + + if (STRNEQ_NULLABLE(ifa->ifa_name, ifname)) + continue; + if (family != AF_INET6 && family != AF_INET) + continue; + + if (family == AF_INET6) { + addr->len = sizeof(addr->data.inet6); + memcpy(&addr->data.inet6, ifa->ifa_addr, addr->len); + } else { + addr->len = sizeof(addr->data.inet4); + memcpy(&addr->data.inet4, ifa->ifa_addr, addr->len); + } + addr->data.stor.ss_family = family; + ret = 0; + goto cleanup; + } + + virReportError(VIR_ERR_INTERNAL_ERROR, + _("no IP address found for interface '%s'"), + ifname); + cleanup: + freeifaddrs(ifap); + return ret; +} + +#else /* ! HAVE_GETIFADDRS */ + +static int +virNetDevGetifaddrsAddress(const char *ifname ATTRIBUTE_UNUSED, + virSocketAddrPtr addr ATTRIBUTE_UNUSED) +{ + return -2; +} + +#endif + +/** + * virNetDevIPIPAddrGet: + * @ifname: name of the interface whose IP address we want + * @addr: filled with the IPv4 address + * + * This function gets the IPv4 address for the interface @ifname + * and stores it in @addr + * + * Returns 0 on success, -errno on failure. + */ +int +virNetDevIPAddrGet(const char *ifname, + virSocketAddrPtr addr) +{ + int ret; + + memset(addr, 0, sizeof(*addr)); + addr->data.stor.ss_family = AF_UNSPEC; + + if ((ret = virNetDevGetifaddrsAddress(ifname, addr)) != -2) + return ret; + + if ((ret = virNetDevGetIPv4AddressIoctl(ifname, addr)) != -2) + return ret; + + virReportSystemError(ENOSYS, "%s", + _("Unable to get IP address on this platform")); + return -1; +} diff --git a/src/util/virnetdevip.h b/src/util/virnetdevip.h new file mode 100644 index 0000000000..f60465d7ca --- /dev/null +++ b/src/util/virnetdevip.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2007-2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + * + * Authors: + * Mark McLoughlin + * Daniel P. Berrange + */ + +#ifndef __VIR_NETDEVIP_H__ +# define __VIR_NETDEVIP_H__ + +# include "virsocketaddr.h" + +/* manipulating/querying the netdev */ +int virNetDevIPAddrAdd(const char *ifname, + virSocketAddr *addr, + virSocketAddr *peer, + unsigned int prefix) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK; +int virNetDevIPRouteAdd(const char *ifname, + virSocketAddrPtr addr, + unsigned int prefix, + virSocketAddrPtr gateway, + unsigned int metric) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(4) + ATTRIBUTE_RETURN_CHECK; +int virNetDevIPAddrDel(const char *ifname, + virSocketAddr *addr, + unsigned int prefix) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK; +int virNetDevIPAddrGet(const char *ifname, virSocketAddrPtr addr) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK; +int virNetDevIPWaitDadFinish(virSocketAddrPtr *addrs, size_t count) + ATTRIBUTE_NONNULL(1); + +#endif /* __VIR_NETDEVIP_H__ */