diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index aa776b4801..65862ec87f 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 35149784bd..9dfaadc169 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 eab888f6e7..1a2b7a1b0d 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 c478691238..1a3e06d913 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,