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(a)redhat.com>
+ * Daniel P. Berrange <berrange(a)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(a)redhat.com>
+ * Daniel P. Berrange <berrange(a)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