From 4dc04d3ab492e0571a1e849f86ef0716b1ec5a07 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?C=C3=A9dric=20Bosdonnat?= <cbosdonnat@suse.com>
Date: Tue, 5 Aug 2014 19:15:11 +0200
Subject: [PATCH 02/17] virNetDevSetIPv4Address: libnl implementation

Add a default implementation of virNetDevSetIPv4Address using netlink
and libnl. This avoids requiring /usr/sbin/ip or /usr/sbin/ifconfig
external binaries.
---
 src/libvirt_private.syms |   1 +
 src/util/virnetdev.c     | 136 +++++++++++++++++++++++++++++++++++++++++++++--
 src/util/virnetlink.c    |  38 +++++++++++++
 src/util/virnetlink.h    |   2 +
 4 files changed, 174 insertions(+), 3 deletions(-)

Index: libvirt-1.2.5/src/libvirt_private.syms
===================================================================
--- libvirt-1.2.5.orig/src/libvirt_private.syms
+++ libvirt-1.2.5/src/libvirt_private.syms
@@ -1644,6 +1644,7 @@ virNetlinkEventServiceLocalPid;
 virNetlinkEventServiceStart;
 virNetlinkEventServiceStop;
 virNetlinkEventServiceStopAll;
+virNetlinkGetErrorCode;
 virNetlinkShutdown;
 virNetlinkStartup;
 
Index: libvirt-1.2.5/src/util/virnetdev.c
===================================================================
--- libvirt-1.2.5.orig/src/util/virnetdev.c
+++ libvirt-1.2.5/src/util/virnetdev.c
@@ -786,6 +786,88 @@ int virNetDevGetVLanID(const char *ifnam
 #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)
+{
+    struct nl_msg *nlmsg = NULL;
+    struct ifaddrmsg ifa;
+    unsigned int ifindex;
+    void *addrData = NULL;
+    void *broadcastData = NULL;
+    size_t addrDataLen;
+
+    if (virNetDevGetIPAddressBinary(addr, &addrData, &addrDataLen) < 0)
+        return NULL;
+
+    if (broadcast && 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 (nla_put(nlmsg, IFA_ADDRESS, addrDataLen, addrData) < 0)
+        goto buffer_too_small;
+
+    if (broadcastData &&
+        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;
+}
 
 /**
  * virNetDevSetIPv4Address:
@@ -799,6 +881,52 @@ int virNetDevGetVLanID(const char *ifnam
  *
  * Returns 0 in case of success or -1 in case of error.
  */
+int virNetDevSetIPv4Address(const char *ifname,
+                            virSocketAddr *addr,
+                            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) {
+        /* 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)))
+        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;
+}
+
+#else /* defined(__linux__) && defined(HAVE_LIBNL) */
 
 int virNetDevSetIPv4Address(const char *ifname,
                             virSocketAddr *addr,
@@ -817,7 +945,7 @@ int virNetDevSetIPv4Address(const char *
          !(bcaststr = virSocketAddrFormat(&broadcast)))) {
         goto cleanup;
     }
-#ifdef IFCONFIG_PATH
+# ifdef IFCONFIG_PATH
     cmd = virCommandNew(IFCONFIG_PATH);
     virCommandAddArg(cmd, ifname);
     if (VIR_SOCKET_ADDR_IS_FAMILY(addr, AF_INET6))
@@ -828,14 +956,14 @@ int virNetDevSetIPv4Address(const char *
     if (bcaststr)
         virCommandAddArgList(cmd, "broadcast", bcaststr, NULL);
     virCommandAddArg(cmd, "alias");
-#else
+# else
     cmd = virCommandNew(IP_PATH);
     virCommandAddArgList(cmd, "addr", "add", NULL);
     virCommandAddArgFormat(cmd, "%s/%u", addrstr, prefix);
     if (bcaststr)
         virCommandAddArgList(cmd, "broadcast", bcaststr, NULL);
     virCommandAddArgList(cmd, "dev", ifname, NULL);
-#endif
+# endif
 
     if (virCommandRun(cmd, NULL) < 0)
         goto cleanup;
@@ -848,6 +976,8 @@ int virNetDevSetIPv4Address(const char *
     return ret;
 }
 
+#endif /* defined(__linux__) && defined(HAVE_LIBNL) */
+
 /**
  * virNetDevAddRoute:
  * @ifname: the interface name
Index: libvirt-1.2.5/src/util/virnetlink.c
===================================================================
--- libvirt-1.2.5.orig/src/util/virnetlink.c
+++ libvirt-1.2.5/src/util/virnetlink.c
@@ -276,6 +276,44 @@ int virNetlinkCommand(struct nl_msg *nl_
     return ret;
 }
 
+int virNetlinkGetErrorCode(struct nlmsghdr *resp, unsigned int recvbuflen)
+{
+    struct nlmsgerr *err;
+    int result = 0;
+
+    if (recvbuflen < NLMSG_LENGTH(0) || resp == NULL)
+        goto malformed_resp;
+
+    switch (resp->nlmsg_type) {
+    case NLMSG_ERROR:
+        err = (struct nlmsgerr *)NLMSG_DATA(resp);
+        if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
+            goto malformed_resp;
+
+        switch (err->error) {
+        case 0: /* ACK */
+            break;
+
+        default:
+            result = err->error;
+        }
+        break;
+
+    case NLMSG_DONE:
+        break;
+
+    default:
+        goto malformed_resp;
+    }
+
+    return result;
+
+ malformed_resp:
+    virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                   _("malformed netlink response message"));
+    return -EINVAL;
+}
+
 static void
 virNetlinkEventServerLock(virNetlinkEventSrvPrivatePtr driver)
 {
Index: libvirt-1.2.5/src/util/virnetlink.h
===================================================================
--- libvirt-1.2.5.orig/src/util/virnetlink.h
+++ libvirt-1.2.5/src/util/virnetlink.h
@@ -52,6 +52,8 @@ int virNetlinkCommand(struct nl_msg *nl_
                       uint32_t src_pid, uint32_t dst_pid,
                       unsigned int protocol, unsigned int groups);
 
+int virNetlinkGetErrorCode(struct nlmsghdr *resp, unsigned int recvbuflen);
+
 typedef void (*virNetlinkEventHandleCallback)(struct nlmsghdr *,
                                               unsigned int length,
                                               struct sockaddr_nl *peer,
