[libvirt] [PATCH 13/28] util: new files virnetdevip.[ch] for IP-related netdev functions

Laine Stump laine at laine.org
Wed Jun 22 17:37:12 UTC 2016


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 822cfbc..f27d448 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 a14cb3f..f56dcc2 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 807ffce..bd7b730 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1850,15 +1850,12 @@ virMacAddrSetRaw;
 
 # util/virnetdev.h
 virNetDevAddMulti;
-virNetDevAddRoute;
-virNetDevClearIPAddress;
 virNetDevDelMulti;
 virNetDevExists;
 virNetDevFeatureTypeFromString;
 virNetDevFeatureTypeToString;
 virNetDevGetFeatures;
 virNetDevGetIndex;
-virNetDevGetIPAddress;
 virNetDevGetLinkInfo;
 virNetDevGetMAC;
 virNetDevGetMTU;
@@ -1884,7 +1881,6 @@ virNetDevRxFilterFree;
 virNetDevRxFilterModeTypeFromString;
 virNetDevRxFilterModeTypeToString;
 virNetDevRxFilterNew;
-virNetDevSetIPAddress;
 virNetDevSetMAC;
 virNetDevSetMTU;
 virNetDevSetMTUFromDevice;
@@ -1897,7 +1893,6 @@ virNetDevSetRcvMulti;
 virNetDevSetupControl;
 virNetDevSysfsFile;
 virNetDevValidateConfig;
-virNetDevWaitDadFinish;
 
 
 # util/virnetdevbandwidth.h
@@ -1931,6 +1926,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 304aa86..a95472c 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"
 
@@ -527,7 +527,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);
@@ -548,11 +548,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 130b77d..135f7bd 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"
@@ -1986,8 +1987,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;
@@ -2034,8 +2035,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;
@@ -2058,7 +2059,7 @@ networkWaitDadFinish(virNetworkObjPtr network)
             goto cleanup;
     }
 
-    ret = (naddrs == 0) ? 0 : virNetDevWaitDadFinish(addrs, naddrs);
+    ret = (naddrs == 0) ? 0 : virNetDevIPWaitDadFinish(addrs, naddrs);
 
  cleanup:
     VIR_FREE(addrs);
@@ -4769,7 +4770,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 fb71c7b..729c121 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 <ifaddrs.h>
-#endif
-
 #include <sys/ioctl.h>
 #include <net/if.h>
 #include <fcntl.h>
@@ -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 0000000..044f2bc
--- /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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *     Mark McLoughlin <markmc at redhat.com>
+ *     Daniel P. Berrange <berrange at redhat.com>
+ */
+
+#include <config.h>
+
+#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 <ifaddrs.h>
+#endif
+
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <fcntl.h>
+
+#ifdef __linux__
+# include <linux/sockios.h>
+# include <linux/if_vlan.h>
+# 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 0000000..f60465d
--- /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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *     Mark McLoughlin <markmc at redhat.com>
+ *     Daniel P. Berrange <berrange at redhat.com>
+ */
+
+#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__ */
-- 
2.5.5




More information about the libvir-list mailing list