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(-)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index aa776b4..65862ec 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1740,6 +1740,7 @@ virNetlinkEventServiceLocalPid;
virNetlinkEventServiceStart;
virNetlinkEventServiceStop;
virNetlinkEventServiceStopAll;
+virNetlinkGetErrorCode;
virNetlinkShutdown;
virNetlinkStartup;
diff --git a/src/util/virnetdev.c b/src/util/virnetdev.c
index 3514978..9dfaadc 100644
--- a/src/util/virnetdev.c
+++ b/src/util/virnetdev.c
@@ -823,6 +823,88 @@ 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)
+{
+ 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:
@@ -836,6 +918,52 @@ int virNetDevGetVLanID(const char *ifname ATTRIBUTE_UNUSED,
*
* 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,
@@ -854,7 +982,7 @@ int virNetDevSetIPv4Address(const char *ifname,
!(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))
@@ -865,14 +993,14 @@ int virNetDevSetIPv4Address(const char *ifname,
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;
@@ -885,6 +1013,8 @@ int virNetDevSetIPv4Address(const char *ifname,
return ret;
}
+#endif /* defined(__linux__) && defined(HAVE_LIBNL) */
+
/**
* virNetDevAddRoute:
* @ifname: the interface name
diff --git a/src/util/virnetlink.c b/src/util/virnetlink.c
index eab888f..1a2b7a1 100644
--- a/src/util/virnetlink.c
+++ b/src/util/virnetlink.c
@@ -276,6 +276,44 @@ int virNetlinkCommand(struct nl_msg *nl_msg,
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)
{
diff --git a/src/util/virnetlink.h b/src/util/virnetlink.h
index c478691..1a3e06d 100644
--- a/src/util/virnetlink.h
+++ b/src/util/virnetlink.h
@@ -52,6 +52,8 @@ int virNetlinkCommand(struct nl_msg *nl_msg,
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,
--
2.1.2