/* * 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 * . * * Authors: * Mark McLoughlin * Daniel P. Berrange */ #include #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" #include "vircommand.h" #if HAVE_GETIFADDRS # include #endif #include #include #include #ifdef __linux__ # include # include # 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; char *ipStr = NULL; char *peerStr = NULL; char *bcastStr = NULL; ipStr = virSocketAddrFormat(addr); if (peer && VIR_SOCKET_ADDR_VALID(peer)) peerStr = virSocketAddrFormat(peer); /* 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) goto cleanup; if (virSocketAddrBroadcastByPrefix(addr, prefix, broadcast) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Failed to determine broadcast address for '%s/%d'"), ipStr, prefix); goto cleanup; } bcastStr = virSocketAddrFormat(broadcast); } VIR_DEBUG("Adding IP address %s/%d%s%s%s%s to %s", NULLSTR(ipStr), prefix, peerStr ? " peer " : "", peerStr ? peerStr : "", bcastStr ? " bcast " : "", bcastStr ? bcastStr : "", ifname); 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, _("Failed to add IP address %s/%d%s%s%s%s to %s"), ipStr, prefix, peerStr ? " peer " : "", peerStr ? peerStr : "", bcastStr ? " bcast " : "", bcastStr ? bcastStr : "", ifname); goto cleanup; } ret = 0; cleanup: VIR_FREE(ipStr); VIR_FREE(peerStr); VIR_FREE(bcastStr); 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; } /* manipulating the virNetDevIPRoute object */ void virNetDevIPRouteFree(virNetDevIPRoutePtr def) { if (!def) return; VIR_FREE(def->family); VIR_FREE(def); } virSocketAddrPtr virNetDevIPRouteGetAddress(virNetDevIPRoutePtr def) { if (def) return &def->address; return NULL; } int virNetDevIPRouteGetPrefix(virNetDevIPRoutePtr def) { int prefix = 0; virSocketAddr zero; if (!def) return -1; /* this creates an all-0 address of the appropriate family */ ignore_value(virSocketAddrParse(&zero, (VIR_SOCKET_ADDR_IS_FAMILY(&def->address, AF_INET) ? VIR_SOCKET_ADDR_IPV4_ALL : VIR_SOCKET_ADDR_IPV6_ALL), VIR_SOCKET_ADDR_FAMILY(&def->address))); if (virSocketAddrEqual(&def->address, &zero)) { if (def->has_prefix && def->prefix == 0) prefix = 0; else if ((VIR_SOCKET_ADDR_IS_FAMILY(&def->netmask, AF_INET) && virSocketAddrEqual(&def->netmask, &zero))) prefix = 0; else prefix = virSocketAddrGetIPPrefix(&def->address, &def->netmask, def->prefix); } else { prefix = virSocketAddrGetIPPrefix(&def->address, &def->netmask, def->prefix); } return prefix; } unsigned int virNetDevIPRouteGetMetric(virNetDevIPRoutePtr def) { if (def && def->has_metric && def->metric > 0) return def->metric; return 1; } virSocketAddrPtr virNetDevIPRouteGetGateway(virNetDevIPRoutePtr def) { if (def) return &def->gateway; return NULL; } /* manipulating the virNetDevIPInfo object */ void virNetDevIPInfoClear(virNetDevIPInfoPtr ip) { size_t i; for (i = 0; i < ip->nips; i++) VIR_FREE(ip->ips[i]); VIR_FREE(ip->ips); for (i = 0; i < ip->nroutes; i++) virNetDevIPRouteFree(ip->routes[i]); VIR_FREE(ip->routes); } /** * virNetDevIPInfoAddToDev: * @ifname: name of device to operate on * @ipInfo: list of routes and IP addresses to add to this device * * All IP routes and IP addresses in ipInfo are added to the named device. * * Returns: 0 on success, -1 (and error reported) on failure. */ int virNetDevIPInfoAddToDev(const char *ifname, virNetDevIPInfo const *ipInfo) { int ret = -1; size_t i; char *ipStr = NULL; int prefix; /* add all IP addresses */ for (i = 0; i < ipInfo->nips; i++) { virNetDevIPAddrPtr ip = ipInfo->ips[i]; ipStr = virSocketAddrFormat(&ip->address); if ((prefix = virSocketAddrGetIPPrefix(&ip->address, NULL, ip->prefix)) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Failed to determine prefix for IP address '%s'"), ipStr); goto cleanup; } if (virNetDevIPAddrAdd(ifname, &ip->address, &ip->peer, prefix) < 0) goto cleanup; VIR_FREE(ipStr); } /* add all routes */ for (i = 0; i < ipInfo->nroutes; i++) { virNetDevIPRoutePtr route = ipInfo->routes[i]; ipStr = virSocketAddrFormat(&route->address); if ((prefix = virNetDevIPRouteGetPrefix(route)) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Failed to determine prefix for route with destination '%s'"), ipStr); goto cleanup; } if (virNetDevIPRouteAdd(ifname, &route->address, prefix, &route->gateway, virNetDevIPRouteGetMetric(route)) < 0) goto cleanup; VIR_FREE(ipStr); } ret = 0; cleanup: VIR_FREE(ipStr); return ret; }