mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-22 13:45:38 +00:00
util: new files virnetdevip.[ch] for IP-related netdev functions
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).
This commit is contained in:
parent
9658e70f7d
commit
cf0568b0af
@ -213,6 +213,7 @@ src/util/virlog.c
|
|||||||
src/util/virnetdev.c
|
src/util/virnetdev.c
|
||||||
src/util/virnetdevbandwidth.c
|
src/util/virnetdevbandwidth.c
|
||||||
src/util/virnetdevbridge.c
|
src/util/virnetdevbridge.c
|
||||||
|
src/util/virnetdevip.c
|
||||||
src/util/virnetdevmacvlan.c
|
src/util/virnetdevmacvlan.c
|
||||||
src/util/virnetdevmidonet.c
|
src/util/virnetdevmidonet.c
|
||||||
src/util/virnetdevopenvswitch.c
|
src/util/virnetdevopenvswitch.c
|
||||||
|
@ -138,6 +138,7 @@ UTIL_SOURCES = \
|
|||||||
util/virnetdev.h util/virnetdev.c \
|
util/virnetdev.h util/virnetdev.c \
|
||||||
util/virnetdevbandwidth.h util/virnetdevbandwidth.c \
|
util/virnetdevbandwidth.h util/virnetdevbandwidth.c \
|
||||||
util/virnetdevbridge.h util/virnetdevbridge.c \
|
util/virnetdevbridge.h util/virnetdevbridge.c \
|
||||||
|
util/virnetdevip.h util/virnetdevip.c \
|
||||||
util/virnetdevmacvlan.c util/virnetdevmacvlan.h \
|
util/virnetdevmacvlan.c util/virnetdevmacvlan.h \
|
||||||
util/virnetdevmidonet.h util/virnetdevmidonet.c \
|
util/virnetdevmidonet.h util/virnetdevmidonet.c \
|
||||||
util/virnetdevopenvswitch.h util/virnetdevopenvswitch.c \
|
util/virnetdevopenvswitch.h util/virnetdevopenvswitch.c \
|
||||||
|
@ -1856,15 +1856,12 @@ virMacAddrSetRaw;
|
|||||||
|
|
||||||
# util/virnetdev.h
|
# util/virnetdev.h
|
||||||
virNetDevAddMulti;
|
virNetDevAddMulti;
|
||||||
virNetDevAddRoute;
|
|
||||||
virNetDevClearIPAddress;
|
|
||||||
virNetDevDelMulti;
|
virNetDevDelMulti;
|
||||||
virNetDevExists;
|
virNetDevExists;
|
||||||
virNetDevFeatureTypeFromString;
|
virNetDevFeatureTypeFromString;
|
||||||
virNetDevFeatureTypeToString;
|
virNetDevFeatureTypeToString;
|
||||||
virNetDevGetFeatures;
|
virNetDevGetFeatures;
|
||||||
virNetDevGetIndex;
|
virNetDevGetIndex;
|
||||||
virNetDevGetIPAddress;
|
|
||||||
virNetDevGetLinkInfo;
|
virNetDevGetLinkInfo;
|
||||||
virNetDevGetMAC;
|
virNetDevGetMAC;
|
||||||
virNetDevGetMTU;
|
virNetDevGetMTU;
|
||||||
@ -1890,7 +1887,6 @@ virNetDevRxFilterFree;
|
|||||||
virNetDevRxFilterModeTypeFromString;
|
virNetDevRxFilterModeTypeFromString;
|
||||||
virNetDevRxFilterModeTypeToString;
|
virNetDevRxFilterModeTypeToString;
|
||||||
virNetDevRxFilterNew;
|
virNetDevRxFilterNew;
|
||||||
virNetDevSetIPAddress;
|
|
||||||
virNetDevSetMAC;
|
virNetDevSetMAC;
|
||||||
virNetDevSetMTU;
|
virNetDevSetMTU;
|
||||||
virNetDevSetMTUFromDevice;
|
virNetDevSetMTUFromDevice;
|
||||||
@ -1903,7 +1899,6 @@ virNetDevSetRcvMulti;
|
|||||||
virNetDevSetupControl;
|
virNetDevSetupControl;
|
||||||
virNetDevSysfsFile;
|
virNetDevSysfsFile;
|
||||||
virNetDevValidateConfig;
|
virNetDevValidateConfig;
|
||||||
virNetDevWaitDadFinish;
|
|
||||||
|
|
||||||
|
|
||||||
# util/virnetdevbandwidth.h
|
# util/virnetdevbandwidth.h
|
||||||
@ -1937,6 +1932,14 @@ virNetDevBridgeSetSTPDelay;
|
|||||||
virNetDevBridgeSetVlanFiltering;
|
virNetDevBridgeSetVlanFiltering;
|
||||||
|
|
||||||
|
|
||||||
|
# util/virnetdevip.h
|
||||||
|
virNetDevIPAddrAdd;
|
||||||
|
virNetDevIPAddrDel;
|
||||||
|
virNetDevIPAddrGet;
|
||||||
|
virNetDevIPRouteAdd;
|
||||||
|
virNetDevIPWaitDadFinish;
|
||||||
|
|
||||||
|
|
||||||
# util/virnetdevmacvlan.h
|
# util/virnetdevmacvlan.h
|
||||||
virNetDevMacVLanCreate;
|
virNetDevMacVLanCreate;
|
||||||
virNetDevMacVLanCreateWithVPortProfile;
|
virNetDevMacVLanCreateWithVPortProfile;
|
||||||
|
@ -66,7 +66,7 @@
|
|||||||
#include "virfile.h"
|
#include "virfile.h"
|
||||||
#include "virusb.h"
|
#include "virusb.h"
|
||||||
#include "vircommand.h"
|
#include "vircommand.h"
|
||||||
#include "virnetdev.h"
|
#include "virnetdevip.h"
|
||||||
#include "virprocess.h"
|
#include "virprocess.h"
|
||||||
#include "virstring.h"
|
#include "virstring.h"
|
||||||
|
|
||||||
@ -528,7 +528,7 @@ static int lxcContainerRenameAndEnableInterfaces(virDomainDefPtr vmDef,
|
|||||||
|
|
||||||
VIR_DEBUG("Adding IP address '%s/%d' to '%s'",
|
VIR_DEBUG("Adding IP address '%s/%d' to '%s'",
|
||||||
ipStr, prefix, newname);
|
ipStr, prefix, newname);
|
||||||
if (virNetDevSetIPAddress(newname, &ip->address, NULL, prefix) < 0) {
|
if (virNetDevIPAddrAdd(newname, &ip->address, NULL, prefix) < 0) {
|
||||||
virReportError(VIR_ERR_SYSTEM_ERROR,
|
virReportError(VIR_ERR_SYSTEM_ERROR,
|
||||||
_("Failed to set IP address '%s' on %s"),
|
_("Failed to set IP address '%s' on %s"),
|
||||||
ipStr, newname);
|
ipStr, newname);
|
||||||
@ -549,11 +549,11 @@ static int lxcContainerRenameAndEnableInterfaces(virDomainDefPtr vmDef,
|
|||||||
for (j = 0; j < netDef->nroutes; j++) {
|
for (j = 0; j < netDef->nroutes; j++) {
|
||||||
virNetworkRouteDefPtr route = netDef->routes[j];
|
virNetworkRouteDefPtr route = netDef->routes[j];
|
||||||
|
|
||||||
if (virNetDevAddRoute(newname,
|
if (virNetDevIPRouteAdd(newname,
|
||||||
virNetworkRouteDefGetAddress(route),
|
virNetworkRouteDefGetAddress(route),
|
||||||
virNetworkRouteDefGetPrefix(route),
|
virNetworkRouteDefGetPrefix(route),
|
||||||
virNetworkRouteDefGetGateway(route),
|
virNetworkRouteDefGetGateway(route),
|
||||||
virNetworkRouteDefGetMetric(route)) < 0) {
|
virNetworkRouteDefGetMetric(route)) < 0) {
|
||||||
goto error_out;
|
goto error_out;
|
||||||
}
|
}
|
||||||
VIR_FREE(toStr);
|
VIR_FREE(toStr);
|
||||||
|
@ -62,10 +62,11 @@
|
|||||||
#include "virdnsmasq.h"
|
#include "virdnsmasq.h"
|
||||||
#include "configmake.h"
|
#include "configmake.h"
|
||||||
#include "virnetdev.h"
|
#include "virnetdev.h"
|
||||||
#include "virpci.h"
|
#include "virnetdevip.h"
|
||||||
#include "virnetdevbridge.h"
|
#include "virnetdevbridge.h"
|
||||||
#include "virnetdevtap.h"
|
#include "virnetdevtap.h"
|
||||||
#include "virnetdevvportprofile.h"
|
#include "virnetdevvportprofile.h"
|
||||||
|
#include "virpci.h"
|
||||||
#include "virdbus.h"
|
#include "virdbus.h"
|
||||||
#include "virfile.h"
|
#include "virfile.h"
|
||||||
#include "virstring.h"
|
#include "virstring.h"
|
||||||
@ -1977,8 +1978,8 @@ networkAddAddrToBridge(virNetworkObjPtr network,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (virNetDevSetIPAddress(network->def->bridge,
|
if (virNetDevIPAddrAdd(network->def->bridge,
|
||||||
&ipdef->address, NULL, prefix) < 0)
|
&ipdef->address, NULL, prefix) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -2025,8 +2026,8 @@ networkAddRouteToBridge(virNetworkObjPtr network,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (virNetDevAddRoute(network->def->bridge, addr,
|
if (virNetDevIPRouteAdd(network->def->bridge, addr,
|
||||||
prefix, gateway, metric) < 0) {
|
prefix, gateway, metric) < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@ -2049,7 +2050,7 @@ networkWaitDadFinish(virNetworkObjPtr network)
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = (naddrs == 0) ? 0 : virNetDevWaitDadFinish(addrs, naddrs);
|
ret = (naddrs == 0) ? 0 : virNetDevIPWaitDadFinish(addrs, naddrs);
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
VIR_FREE(addrs);
|
VIR_FREE(addrs);
|
||||||
@ -4760,7 +4761,7 @@ networkGetNetworkAddress(const char *netname, char **netaddr)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (dev_name) {
|
if (dev_name) {
|
||||||
if (virNetDevGetIPAddress(dev_name, &addr) < 0)
|
if (virNetDevIPAddrGet(dev_name, &addr) < 0)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
addrptr = &addr;
|
addrptr = &addr;
|
||||||
}
|
}
|
||||||
|
@ -34,10 +34,6 @@
|
|||||||
#include "virstring.h"
|
#include "virstring.h"
|
||||||
#include "virutil.h"
|
#include "virutil.h"
|
||||||
|
|
||||||
#if HAVE_GETIFADDRS
|
|
||||||
# include <ifaddrs.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <net/if.h>
|
#include <net/if.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
@ -97,7 +93,6 @@ VIR_LOG_INIT("util.netdev");
|
|||||||
# define FEATURE_BIT_IS_SET(blocks, index, field) \
|
# define FEATURE_BIT_IS_SET(blocks, index, field) \
|
||||||
(FEATURE_WORD(blocks, index, field) & FEATURE_FIELD_FLAG(index))
|
(FEATURE_WORD(blocks, index, field) & FEATURE_FIELD_FLAG(index))
|
||||||
#endif
|
#endif
|
||||||
#define VIR_DAD_WAIT_TIMEOUT 20 /* seconds */
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
VIR_MCAST_TYPE_INDEX_TOKEN,
|
VIR_MCAST_TYPE_INDEX_TOKEN,
|
||||||
@ -1012,712 +1007,6 @@ int virNetDevGetVLanID(const char *ifname ATTRIBUTE_UNUSED,
|
|||||||
#endif /* ! SIOCGIFVLAN */
|
#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:
|
* virNetDevValidateConfig:
|
||||||
* @ifname: Name of the interface
|
* @ifname: Name of the interface
|
||||||
|
778
src/util/virnetdevip.c
Normal file
778
src/util/virnetdevip.c
Normal file
@ -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@redhat.com>
|
||||||
|
* Daniel P. Berrange <berrange@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;
|
||||||
|
}
|
50
src/util/virnetdevip.h
Normal file
50
src/util/virnetdevip.h
Normal file
@ -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@redhat.com>
|
||||||
|
* Daniel P. Berrange <berrange@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__ */
|
Loading…
Reference in New Issue
Block a user