2016-06-13 21:01:27 +00:00
|
|
|
/*
|
|
|
|
* 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/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#include "virnetdevip.h"
|
|
|
|
#include "virnetdev.h"
|
|
|
|
#include "virnetlink.h"
|
|
|
|
#include "virfile.h"
|
|
|
|
#include "virerror.h"
|
|
|
|
#include "virlog.h"
|
|
|
|
#include "virstring.h"
|
2016-06-27 08:33:00 +00:00
|
|
|
#include "vircommand.h"
|
2019-04-01 14:28:05 +00:00
|
|
|
#include "viralloc.h"
|
2016-06-13 21:01:27 +00:00
|
|
|
|
2020-09-01 11:27:44 +00:00
|
|
|
#if WITH_GETIFADDRS
|
2016-06-13 21:01:27 +00:00
|
|
|
# include <ifaddrs.h>
|
|
|
|
#endif
|
|
|
|
|
2020-01-17 12:17:58 +00:00
|
|
|
#ifndef WIN32
|
|
|
|
# include <sys/ioctl.h>
|
|
|
|
#endif
|
2020-09-01 11:27:44 +00:00
|
|
|
#ifdef WITH_NET_IF_H
|
2020-01-22 11:48:45 +00:00
|
|
|
# include <net/if.h>
|
|
|
|
#endif
|
2016-06-13 21:01:27 +00:00
|
|
|
#include <fcntl.h>
|
|
|
|
|
|
|
|
#ifdef __linux__
|
|
|
|
# include <linux/sockios.h>
|
|
|
|
# include <linux/if_vlan.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_NONE
|
|
|
|
|
|
|
|
VIR_LOG_INIT("util.netdevip");
|
|
|
|
|
2020-09-01 11:27:44 +00:00
|
|
|
#if defined(__linux__) && defined(WITH_LIBNL)
|
2016-06-13 21:01:27 +00:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
unsigned int recvbuflen;
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virNetlinkMsg) nlmsg = NULL;
|
|
|
|
g_autoptr(virSocketAddr) broadcast = NULL;
|
2019-10-15 13:16:31 +00:00
|
|
|
g_autofree struct nlmsghdr *resp = NULL;
|
|
|
|
g_autofree char *ipStr = NULL;
|
|
|
|
g_autofree char *peerStr = NULL;
|
|
|
|
g_autofree char *bcastStr = NULL;
|
2016-06-15 19:27:47 +00:00
|
|
|
|
|
|
|
ipStr = virSocketAddrFormat(addr);
|
|
|
|
if (peer && VIR_SOCKET_ADDR_VALID(peer))
|
|
|
|
peerStr = virSocketAddrFormat(peer);
|
2016-06-13 21:01:27 +00:00
|
|
|
|
|
|
|
/* 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)
|
2018-08-09 04:12:17 +00:00
|
|
|
return -1;
|
2016-06-13 21:01:27 +00:00
|
|
|
|
2016-06-15 19:27:47 +00:00
|
|
|
if (virSocketAddrBroadcastByPrefix(addr, prefix, broadcast) < 0) {
|
2018-08-09 04:12:17 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Failed to determine broadcast address for '%s/%d'"),
|
|
|
|
ipStr, prefix);
|
|
|
|
return -1;
|
2016-06-15 19:27:47 +00:00
|
|
|
}
|
|
|
|
bcastStr = virSocketAddrFormat(broadcast);
|
2016-06-13 21:01:27 +00:00
|
|
|
}
|
|
|
|
|
2016-06-15 19:27:47 +00:00
|
|
|
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);
|
|
|
|
|
2016-06-13 21:01:27 +00:00
|
|
|
if (!(nlmsg = virNetDevCreateNetlinkAddressMessage(RTM_NEWADDR, ifname,
|
|
|
|
addr, prefix,
|
|
|
|
broadcast, peer)))
|
2018-08-09 04:12:17 +00:00
|
|
|
return -1;
|
2016-06-13 21:01:27 +00:00
|
|
|
|
2016-06-15 19:27:47 +00:00
|
|
|
if (virNetlinkCommand(nlmsg, &resp, &recvbuflen,
|
|
|
|
0, 0, NETLINK_ROUTE, 0) < 0)
|
2018-08-09 04:12:17 +00:00
|
|
|
return -1;
|
2016-06-13 21:01:27 +00:00
|
|
|
|
|
|
|
|
|
|
|
if (virNetlinkGetErrorCode(resp, recvbuflen) < 0) {
|
|
|
|
virReportError(VIR_ERR_SYSTEM_ERROR,
|
2016-06-15 19:27:47 +00:00
|
|
|
_("Failed to add IP address %s/%d%s%s%s%s to %s"),
|
|
|
|
ipStr, prefix,
|
|
|
|
peerStr ? " peer " : "", peerStr ? peerStr : "",
|
|
|
|
bcastStr ? " bcast " : "", bcastStr ? bcastStr : "",
|
|
|
|
ifname);
|
2018-08-09 04:12:17 +00:00
|
|
|
return -1;
|
2016-06-13 21:01:27 +00:00
|
|
|
}
|
|
|
|
|
2018-08-09 04:12:17 +00:00
|
|
|
return 0;
|
2016-06-13 21:01:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
|
|
|
unsigned int recvbuflen;
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virNetlinkMsg) nlmsg = NULL;
|
2019-10-15 13:16:31 +00:00
|
|
|
g_autofree struct nlmsghdr *resp = NULL;
|
2016-06-13 21:01:27 +00:00
|
|
|
|
|
|
|
if (!(nlmsg = virNetDevCreateNetlinkAddressMessage(RTM_DELADDR, ifname,
|
|
|
|
addr, prefix,
|
|
|
|
NULL, NULL)))
|
2018-08-09 04:12:17 +00:00
|
|
|
return -1;
|
2016-06-13 21:01:27 +00:00
|
|
|
|
|
|
|
if (virNetlinkCommand(nlmsg, &resp, &recvbuflen, 0, 0,
|
|
|
|
NETLINK_ROUTE, 0) < 0)
|
2018-08-09 04:12:17 +00:00
|
|
|
return -1;
|
2016-06-13 21:01:27 +00:00
|
|
|
|
|
|
|
if (virNetlinkGetErrorCode(resp, recvbuflen) < 0) {
|
|
|
|
virReportError(VIR_ERR_SYSTEM_ERROR,
|
|
|
|
_("Error removing IP address from %s"), ifname);
|
2018-08-09 04:12:17 +00:00
|
|
|
return -1;
|
2016-06-13 21:01:27 +00:00
|
|
|
}
|
|
|
|
|
2018-08-09 04:12:17 +00:00
|
|
|
return 0;
|
2016-06-13 21:01:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
|
|
|
unsigned int recvbuflen;
|
|
|
|
unsigned int ifindex;
|
|
|
|
struct rtmsg rtmsg;
|
|
|
|
void *gatewayData = NULL;
|
|
|
|
void *addrData = NULL;
|
|
|
|
size_t addrDataLen;
|
|
|
|
int errCode;
|
|
|
|
virSocketAddr defaultAddr;
|
|
|
|
virSocketAddrPtr actualAddr;
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virNetlinkMsg) nlmsg = NULL;
|
2019-10-15 13:16:31 +00:00
|
|
|
g_autofree char *toStr = NULL;
|
|
|
|
g_autofree char *viaStr = NULL;
|
|
|
|
g_autofree struct nlmsghdr *resp = NULL;
|
2016-06-13 21:01:27 +00:00
|
|
|
|
|
|
|
actualAddr = addr;
|
|
|
|
|
|
|
|
/* If we have no valid network address, then use the default one */
|
|
|
|
if (!addr || !VIR_SOCKET_ADDR_VALID(addr)) {
|
|
|
|
int family = VIR_SOCKET_ADDR_FAMILY(gateway);
|
2020-08-03 15:28:06 +00:00
|
|
|
|
|
|
|
VIR_DEBUG("computing default address");
|
|
|
|
|
2016-06-13 21:01:27 +00:00
|
|
|
if (family == AF_INET) {
|
|
|
|
if (virSocketAddrParseIPv4(&defaultAddr, VIR_SOCKET_ADDR_IPV4_ALL) < 0)
|
2018-08-09 04:12:17 +00:00
|
|
|
return -1;
|
2016-06-13 21:01:27 +00:00
|
|
|
} else {
|
|
|
|
if (virSocketAddrParseIPv6(&defaultAddr, VIR_SOCKET_ADDR_IPV6_ALL) < 0)
|
2018-08-09 04:12:17 +00:00
|
|
|
return -1;
|
2016-06-13 21:01:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
2018-08-09 04:12:17 +00:00
|
|
|
return -1;
|
2016-06-13 21:01:27 +00:00
|
|
|
|
|
|
|
/* Get the interface index */
|
|
|
|
if ((ifindex = if_nametoindex(ifname)) == 0)
|
2018-08-09 04:12:17 +00:00
|
|
|
return -1;
|
2016-06-13 21:01:27 +00:00
|
|
|
|
|
|
|
if (!(nlmsg = nlmsg_alloc_simple(RTM_NEWROUTE,
|
|
|
|
NLM_F_REQUEST | NLM_F_CREATE |
|
|
|
|
NLM_F_EXCL))) {
|
|
|
|
virReportOOMError();
|
2018-08-09 04:12:17 +00:00
|
|
|
return -1;
|
2016-06-13 21:01:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
2018-08-09 04:12:17 +00:00
|
|
|
return -1;
|
2016-06-13 21:01:27 +00:00
|
|
|
|
|
|
|
if ((errCode = virNetlinkGetErrorCode(resp, recvbuflen)) < 0) {
|
|
|
|
virReportSystemError(errCode, _("Error adding route to %s"), ifname);
|
2018-08-09 04:12:17 +00:00
|
|
|
return -1;
|
2016-06-13 21:01:27 +00:00
|
|
|
}
|
|
|
|
|
2018-08-09 04:12:17 +00:00
|
|
|
return 0;
|
2016-06-13 21:01:27 +00:00
|
|
|
|
|
|
|
buffer_too_small:
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("allocated netlink buffer is too small"));
|
2018-08-09 04:12:17 +00:00
|
|
|
return -1;
|
2016-06-13 21:01:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-03 13:14:51 +00:00
|
|
|
static int
|
|
|
|
virNetDevIPGetAcceptRA(const char *ifname)
|
|
|
|
{
|
2019-10-15 13:16:31 +00:00
|
|
|
g_autofree char *path = NULL;
|
|
|
|
g_autofree char *buf = NULL;
|
2017-03-03 13:14:51 +00:00
|
|
|
char *suffix;
|
|
|
|
int accept_ra = -1;
|
|
|
|
|
2019-10-22 13:26:14 +00:00
|
|
|
path = g_strdup_printf("/proc/sys/net/ipv6/conf/%s/accept_ra",
|
|
|
|
ifname ? ifname : "all");
|
2017-03-03 13:14:51 +00:00
|
|
|
|
|
|
|
if ((virFileReadAll(path, 512, &buf) < 0) ||
|
|
|
|
(virStrToLong_i(buf, &suffix, 10, &accept_ra) < 0))
|
2018-07-28 18:01:27 +00:00
|
|
|
return -1;
|
2017-03-03 13:14:51 +00:00
|
|
|
|
|
|
|
return accept_ra;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct virNetDevIPCheckIPv6ForwardingData {
|
|
|
|
bool hasRARoutes;
|
|
|
|
|
|
|
|
/* Devices with conflicting accept_ra */
|
|
|
|
char **devices;
|
|
|
|
size_t ndevices;
|
|
|
|
};
|
|
|
|
|
2019-01-07 20:55:31 +00:00
|
|
|
|
|
|
|
static int
|
|
|
|
virNetDevIPCheckIPv6ForwardingAddIF(struct virNetDevIPCheckIPv6ForwardingData *data,
|
|
|
|
char **ifname)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
/* add ifname to the array if it's not already there
|
|
|
|
* (ifname is char** so VIR_APPEND_ELEMENT() will move the
|
|
|
|
* original pointer out of the way and avoid having it freed)
|
|
|
|
*/
|
|
|
|
for (i = 0; i < data->ndevices; i++) {
|
|
|
|
if (STREQ(data->devices[i], *ifname))
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return VIR_APPEND_ELEMENT(data->devices, data->ndevices, *ifname);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-03 13:14:51 +00:00
|
|
|
static int
|
2019-01-06 22:35:47 +00:00
|
|
|
virNetDevIPCheckIPv6ForwardingCallback(struct nlmsghdr *resp,
|
2017-03-03 13:14:51 +00:00
|
|
|
void *opaque)
|
|
|
|
{
|
|
|
|
struct rtmsg *rtmsg = NLMSG_DATA(resp);
|
|
|
|
struct virNetDevIPCheckIPv6ForwardingData *data = opaque;
|
2019-01-08 19:13:00 +00:00
|
|
|
struct rtattr *rta_attr;
|
|
|
|
int accept_ra = -1;
|
|
|
|
int ifindex = -1;
|
2019-10-15 13:16:31 +00:00
|
|
|
g_autofree char *ifname = NULL;
|
2017-03-03 13:14:51 +00:00
|
|
|
|
|
|
|
/* Ignore messages other than route ones */
|
|
|
|
if (resp->nlmsg_type != RTM_NEWROUTE)
|
2018-07-28 18:01:27 +00:00
|
|
|
return 0;
|
2017-03-03 13:14:51 +00:00
|
|
|
|
|
|
|
/* No need to do anything else for non RA routes */
|
|
|
|
if (rtmsg->rtm_protocol != RTPROT_RA)
|
2018-07-28 18:01:27 +00:00
|
|
|
return 0;
|
2017-03-03 13:14:51 +00:00
|
|
|
|
2019-01-08 19:13:00 +00:00
|
|
|
rta_attr = (struct rtattr *)nlmsg_find_attr(resp, sizeof(struct rtmsg), RTA_OIF);
|
|
|
|
if (rta_attr) {
|
|
|
|
/* This is a single path route, with interface used to reach
|
|
|
|
* nexthop in the RTA_OIF attribute.
|
|
|
|
*/
|
|
|
|
ifindex = *(int *)RTA_DATA(rta_attr);
|
|
|
|
ifname = virNetDevGetName(ifindex);
|
2017-03-03 13:14:51 +00:00
|
|
|
|
2019-01-08 19:13:00 +00:00
|
|
|
if (!ifname)
|
|
|
|
return -1;
|
2017-03-03 13:14:51 +00:00
|
|
|
|
2019-01-08 19:13:00 +00:00
|
|
|
accept_ra = virNetDevIPGetAcceptRA(ifname);
|
|
|
|
|
|
|
|
VIR_DEBUG("Checking route for device %s (%d), accept_ra: %d",
|
|
|
|
ifname, ifindex, accept_ra);
|
|
|
|
|
|
|
|
if (accept_ra != 2 && virNetDevIPCheckIPv6ForwardingAddIF(data, &ifname) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
data->hasRARoutes = true;
|
|
|
|
return 0;
|
|
|
|
}
|
2017-03-03 13:14:51 +00:00
|
|
|
|
2019-01-08 22:07:12 +00:00
|
|
|
/* if no RTA_OIF was found, see if this is a multipath route (one
|
|
|
|
* which has an array of nexthops, each with its own interface)
|
|
|
|
*/
|
|
|
|
|
|
|
|
rta_attr = (struct rtattr *)nlmsg_find_attr(resp, sizeof(struct rtmsg), RTA_MULTIPATH);
|
|
|
|
if (rta_attr) {
|
|
|
|
/* The data of the attribute is an array of rtnexthop */
|
|
|
|
struct rtnexthop *nh = RTA_DATA(rta_attr);
|
|
|
|
size_t len = RTA_PAYLOAD(rta_attr);
|
|
|
|
|
|
|
|
/* validate the attribute array length */
|
|
|
|
len = MIN(len, ((char *)resp + NLMSG_PAYLOAD(resp, 0) - (char *)rta_attr));
|
|
|
|
|
|
|
|
while (len >= sizeof(*nh) && len >= nh->rtnh_len) {
|
|
|
|
/* check accept_ra for the interface of each nexthop */
|
|
|
|
|
|
|
|
ifname = virNetDevGetName(nh->rtnh_ifindex);
|
|
|
|
|
2019-01-12 21:14:50 +00:00
|
|
|
if (!ifname)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
accept_ra = virNetDevIPGetAcceptRA(ifname);
|
2019-01-08 22:07:12 +00:00
|
|
|
|
|
|
|
VIR_DEBUG("Checking multipath route nexthop device %s (%d), accept_ra: %d",
|
|
|
|
ifname, nh->rtnh_ifindex, accept_ra);
|
|
|
|
|
2019-01-12 21:14:50 +00:00
|
|
|
if (accept_ra != 2 && virNetDevIPCheckIPv6ForwardingAddIF(data, &ifname) < 0)
|
2019-01-08 22:07:12 +00:00
|
|
|
return -1;
|
|
|
|
|
2019-01-12 21:14:50 +00:00
|
|
|
VIR_FREE(ifname);
|
2019-01-08 22:07:12 +00:00
|
|
|
data->hasRARoutes = true;
|
|
|
|
|
|
|
|
len -= NLMSG_ALIGN(nh->rtnh_len);
|
2019-01-15 12:00:52 +00:00
|
|
|
VIR_WARNINGS_NO_CAST_ALIGN
|
2019-01-08 22:07:12 +00:00
|
|
|
nh = RTNH_NEXT(nh);
|
2019-01-15 12:00:52 +00:00
|
|
|
VIR_WARNINGS_RESET
|
2019-01-08 22:07:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-28 18:01:27 +00:00
|
|
|
return 0;
|
2017-03-03 13:14:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
virNetDevIPCheckIPv6Forwarding(void)
|
|
|
|
{
|
|
|
|
bool valid = false;
|
|
|
|
struct rtgenmsg genmsg;
|
|
|
|
size_t i;
|
|
|
|
struct virNetDevIPCheckIPv6ForwardingData data = {
|
|
|
|
.hasRARoutes = false,
|
|
|
|
.devices = NULL,
|
|
|
|
.ndevices = 0
|
|
|
|
};
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virNetlinkMsg) nlmsg = NULL;
|
2017-03-03 13:14:51 +00:00
|
|
|
|
|
|
|
|
|
|
|
/* Prepare the request message */
|
|
|
|
if (!(nlmsg = nlmsg_alloc_simple(RTM_GETROUTE,
|
|
|
|
NLM_F_REQUEST | NLM_F_DUMP))) {
|
|
|
|
virReportOOMError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(&genmsg, 0, sizeof(genmsg));
|
|
|
|
genmsg.rtgen_family = AF_INET6;
|
|
|
|
|
|
|
|
if (nlmsg_append(nlmsg, &genmsg, sizeof(genmsg), NLMSG_ALIGNTO) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("allocated netlink buffer is too small"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Send the request and loop over the responses */
|
|
|
|
if (virNetlinkDumpCommand(nlmsg, virNetDevIPCheckIPv6ForwardingCallback,
|
|
|
|
0, 0, NETLINK_ROUTE, 0, &data) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Failed to loop over IPv6 routes"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
valid = !data.hasRARoutes || data.ndevices == 0;
|
|
|
|
|
|
|
|
/* Check the global accept_ra if at least one isn't set on a
|
|
|
|
per-device basis */
|
|
|
|
if (!valid && data.hasRARoutes) {
|
|
|
|
int accept_ra = virNetDevIPGetAcceptRA(NULL);
|
|
|
|
valid = accept_ra == 2;
|
|
|
|
VIR_DEBUG("Checked global accept_ra: %d", accept_ra);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!valid) {
|
2020-07-03 02:30:20 +00:00
|
|
|
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
|
2017-03-03 13:14:51 +00:00
|
|
|
for (i = 0; i < data.ndevices; i++) {
|
|
|
|
virBufferAdd(&buf, data.devices[i], -1);
|
|
|
|
if (i < data.ndevices - 1)
|
|
|
|
virBufferAddLit(&buf, ", ");
|
|
|
|
}
|
|
|
|
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Check the host setup: enabling IPv6 forwarding with "
|
|
|
|
"RA routes without accept_ra set to 2 is likely to cause "
|
|
|
|
"routes loss. Interfaces to look at: %s"),
|
|
|
|
virBufferCurrentContent(&buf));
|
|
|
|
}
|
|
|
|
|
|
|
|
cleanup:
|
2018-08-13 09:16:06 +00:00
|
|
|
virStringListFreeCount(data.devices, data.ndevices);
|
2017-03-03 13:14:51 +00:00
|
|
|
return valid;
|
|
|
|
}
|
2016-06-13 21:01:27 +00:00
|
|
|
|
2020-09-01 11:27:44 +00:00
|
|
|
#else /* defined(__linux__) && defined(WITH_LIBNL) */
|
2016-06-13 21:01:27 +00:00
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virNetDevIPAddrAdd(const char *ifname,
|
|
|
|
virSocketAddr *addr,
|
|
|
|
virSocketAddr *peer,
|
|
|
|
unsigned int prefix)
|
|
|
|
{
|
|
|
|
virSocketAddr broadcast;
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virCommand) cmd = NULL;
|
2019-10-15 13:16:31 +00:00
|
|
|
g_autofree char *addrstr = NULL;
|
|
|
|
g_autofree char *bcaststr = NULL;
|
|
|
|
g_autofree char *peerstr = NULL;
|
2016-06-13 21:01:27 +00:00
|
|
|
|
|
|
|
if (!(addrstr = virSocketAddrFormat(addr)))
|
2018-08-09 04:12:17 +00:00
|
|
|
return -1;
|
2016-06-13 21:01:27 +00:00
|
|
|
|
|
|
|
if (peer && VIR_SOCKET_ADDR_VALID(peer) && !(peerstr = virSocketAddrFormat(peer)))
|
2018-08-09 04:12:17 +00:00
|
|
|
return -1;
|
2016-06-13 21:01:27 +00:00
|
|
|
|
|
|
|
/* format up a broadcast address if this is IPv4 */
|
2018-09-19 08:38:14 +00:00
|
|
|
if (!peerstr &&
|
|
|
|
((VIR_SOCKET_ADDR_IS_FAMILY(addr, AF_INET)) &&
|
|
|
|
((virSocketAddrBroadcastByPrefix(addr, prefix, &broadcast) < 0) ||
|
|
|
|
!(bcaststr = virSocketAddrFormat(&broadcast))))) {
|
2018-08-09 04:12:17 +00:00
|
|
|
return -1;
|
2016-06-13 21:01:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
# 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)
|
2018-08-09 04:12:17 +00:00
|
|
|
return -1;
|
2016-06-13 21:01:27 +00:00
|
|
|
|
2018-08-09 04:12:17 +00:00
|
|
|
return 0;
|
2016-06-13 21:01:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virNetDevIPAddrDel(const char *ifname,
|
|
|
|
virSocketAddr *addr,
|
|
|
|
unsigned int prefix)
|
|
|
|
{
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virCommand) cmd = NULL;
|
2019-10-15 13:16:31 +00:00
|
|
|
g_autofree char *addrstr = NULL;
|
2016-06-13 21:01:27 +00:00
|
|
|
|
|
|
|
if (!(addrstr = virSocketAddrFormat(addr)))
|
2018-08-09 04:12:17 +00:00
|
|
|
return -1;
|
2016-06-13 21:01:27 +00:00
|
|
|
# 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)
|
2018-08-09 04:12:17 +00:00
|
|
|
return -1;
|
2016-06-13 21:01:27 +00:00
|
|
|
|
2018-08-09 04:12:17 +00:00
|
|
|
return 0;
|
2016-06-13 21:01:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virNetDevIPRouteAdd(const char *ifname,
|
|
|
|
virSocketAddrPtr addr,
|
|
|
|
unsigned int prefix,
|
|
|
|
virSocketAddrPtr gateway,
|
|
|
|
unsigned int metric)
|
|
|
|
{
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virCommand) cmd = NULL;
|
2019-10-15 13:16:31 +00:00
|
|
|
g_autofree char *addrstr = NULL;
|
|
|
|
g_autofree char *gatewaystr = NULL;
|
2016-06-13 21:01:27 +00:00
|
|
|
|
|
|
|
if (!(addrstr = virSocketAddrFormat(addr)))
|
2018-08-09 04:12:17 +00:00
|
|
|
return -1;
|
2016-06-13 21:01:27 +00:00
|
|
|
if (!(gatewaystr = virSocketAddrFormat(gateway)))
|
2018-08-09 04:12:17 +00:00
|
|
|
return -1;
|
2016-06-13 21:01:27 +00:00
|
|
|
cmd = virCommandNew(IP_PATH);
|
|
|
|
virCommandAddArgList(cmd, "route", "add", NULL);
|
|
|
|
virCommandAddArgFormat(cmd, "%s/%u", addrstr, prefix);
|
|
|
|
virCommandAddArgList(cmd, "via", gatewaystr, "dev", ifname,
|
2018-09-19 08:38:14 +00:00
|
|
|
"proto", "static", "metric", NULL);
|
2016-06-13 21:01:27 +00:00
|
|
|
virCommandAddArgFormat(cmd, "%u", metric);
|
|
|
|
|
|
|
|
if (virCommandRun(cmd, NULL) < 0)
|
2018-08-09 04:12:17 +00:00
|
|
|
return -1;
|
2016-06-13 21:01:27 +00:00
|
|
|
|
2018-08-09 04:12:17 +00:00
|
|
|
return 0;
|
2016-06-13 21:01:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-03 13:14:51 +00:00
|
|
|
bool
|
|
|
|
virNetDevIPCheckIPv6Forwarding(void)
|
|
|
|
{
|
|
|
|
VIR_WARN("built without libnl: unable to check if IPv6 forwarding can be safely enabled");
|
|
|
|
return true;
|
|
|
|
}
|
2016-06-13 21:01:27 +00:00
|
|
|
|
2020-09-01 11:27:44 +00:00
|
|
|
#endif /* defined(__linux__) && defined(WITH_LIBNL) */
|
2016-06-13 21:01:27 +00:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
2020-09-01 11:27:44 +00:00
|
|
|
#if defined(SIOCGIFADDR) && defined(WITH_STRUCT_IFREQ)
|
2016-06-13 21:01:27 +00:00
|
|
|
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
|
2019-10-14 12:45:33 +00:00
|
|
|
virNetDevGetIPv4AddressIoctl(const char *ifname G_GNUC_UNUSED,
|
|
|
|
virSocketAddrPtr addr G_GNUC_UNUSED)
|
2016-06-13 21:01:27 +00:00
|
|
|
{
|
|
|
|
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.
|
|
|
|
*/
|
2020-09-01 11:27:44 +00:00
|
|
|
#if WITH_GETIFADDRS
|
2016-06-13 21:01:27 +00:00
|
|
|
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) {
|
|
|
|
if (STRNEQ_NULLABLE(ifa->ifa_name, ifname))
|
|
|
|
continue;
|
util: check ifa_addr pointer before accessing its elements
Reported by Rafał Wojciechowski <it@rafalwojciechowski.pl>.
Thread 1 (Thread 0x7f194b99d700 (LWP 5631)):
0 virNetDevGetifaddrsAddress (addr=0x7f194b99c7c0, ifname=0x7f193400e2b0 "ovirtmgmt") at util/virnetdevip.c:738
1 virNetDevIPAddrGet (ifname=0x7f193400e2b0 "ovirtmgmt", addr=addr@entry=0x7f194b99c7c0) at util/virnetdevip.c:795
2 0x00007f19467800d6 in networkGetNetworkAddress (netname=<optimized out>, netaddr=netaddr@entry=0x7f1924013f18) at network/bridge_driver.c:4780
3 0x00007f193e43a33c in qemuProcessGraphicsSetupNetworkAddress (listenAddr=0x7f19340f7650 "127.0.0.1", glisten=0x7f1924013f10) at qemu/qemu_process.c:4062
4 qemuProcessGraphicsSetupListen (vm=<optimized out>, graphics=0x7f1924014f10, cfg=0x7f1934119f00) at qemu/qemu_process.c:4133
5 qemuProcessSetupGraphics (flags=17, vm=0x7f19240155d0, driver=0x7f193411f1d0) at qemu/qemu_process.c:4196
6 qemuProcessPrepareDomain (conn=conn@entry=0x7f192c00ab50, driver=driver@entry=0x7f193411f1d0, vm=vm@entry=0x7f19240155d0, flags=flags@entry=17) at qemu/qemu_process.c:4969
7 0x00007f193e4417c0 in qemuProcessStart (conn=conn@entry=0x7f192c00ab50, driver=driver@entry=0x7f193411f1d0, vm=0x7f19240155d0,asyncJob=asyncJob@entry=QEMU_ASYNC_JOB_START, migrateFrom=migrateFrom@entry=0x0, migrateFd=migrateFd@entry=-1, migratePath=migratePath@entry=0x0,snapshot=snapshot@entry=0x0, vmop=vmop@entry=VIR_NETDEV_VPORT_PROFILE_OP_CREATE, flags=17, flags@entry=1) at qemu/qemu_process.c:5553
Man page for getifaddrs also states that the "ifa_addr" may contain
a null pointer which happens if there is an existing network interface
on the host without IP address.
Signed-off-by: Pavel Hrdina <phrdina@redhat.com>
2017-04-21 08:50:12 +00:00
|
|
|
|
|
|
|
if (!ifa->ifa_addr)
|
|
|
|
continue;
|
2016-06-13 21:01:27 +00:00
|
|
|
|
util: refactor code to workaround gcc 10.1.0 bug
gcc 10.1.0 on Debian sid has a bug where the bounds checking gets
confused beteen two branches:
In file included from /usr/include/string.h:495,
from ../../src/internal.h:28,
from ../../src/util/virsocket.h:21,
from ../../src/util/virsocketaddr.h:21,
from ../../src/util/virnetdevip.h:21,
from ../../src/util/virnetdevip.c:21:
In function 'memcpy',
inlined from 'virNetDevGetifaddrsAddress' at ../../src/util/virnetdevip.c:914:13,
inlined from 'virNetDevIPAddrGet' at ../../src/util/virnetdevip.c:962:16:
/usr/include/arm-linux-gnueabihf/bits/string_fortified.h:34:10: error: '__builtin_memcpy' offset [16, 27] from the object at 'addr' is out of the bounds of referenced subobject 'inet4' with type 'struct sockaddr_in' at offset 0 [-Werror=array-bounds]
34 | return __builtin___memcpy_chk (__dest, __src, __len, __bos0 (__dest));
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from ../../src/util/virnetdevip.h:21,
from ../../src/util/virnetdevip.c:21:
../../src/util/virnetdevip.c: In function 'virNetDevIPAddrGet':
../../src/util/virsocketaddr.h:29:28: note: subobject 'inet4' declared here
29 | struct sockaddr_in inet4;
| ^~~~~
cc1: all warnings being treated as errors
Note the source location is pointing to the "inet6" / AF_INET6 branch of
the "if", but is complaining about bounds of the "inet4" field. Changing
the code into a switch() is sufficient to avoid triggering the bug and
is arguably better code too.
Reviewed-by: Laine Stump <laine@redhat.com>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
2020-07-22 14:58:16 +00:00
|
|
|
switch (ifa->ifa_addr->sa_family) {
|
|
|
|
case AF_INET6:
|
2016-06-13 21:01:27 +00:00
|
|
|
addr->len = sizeof(addr->data.inet6);
|
|
|
|
memcpy(&addr->data.inet6, ifa->ifa_addr, addr->len);
|
util: refactor code to workaround gcc 10.1.0 bug
gcc 10.1.0 on Debian sid has a bug where the bounds checking gets
confused beteen two branches:
In file included from /usr/include/string.h:495,
from ../../src/internal.h:28,
from ../../src/util/virsocket.h:21,
from ../../src/util/virsocketaddr.h:21,
from ../../src/util/virnetdevip.h:21,
from ../../src/util/virnetdevip.c:21:
In function 'memcpy',
inlined from 'virNetDevGetifaddrsAddress' at ../../src/util/virnetdevip.c:914:13,
inlined from 'virNetDevIPAddrGet' at ../../src/util/virnetdevip.c:962:16:
/usr/include/arm-linux-gnueabihf/bits/string_fortified.h:34:10: error: '__builtin_memcpy' offset [16, 27] from the object at 'addr' is out of the bounds of referenced subobject 'inet4' with type 'struct sockaddr_in' at offset 0 [-Werror=array-bounds]
34 | return __builtin___memcpy_chk (__dest, __src, __len, __bos0 (__dest));
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from ../../src/util/virnetdevip.h:21,
from ../../src/util/virnetdevip.c:21:
../../src/util/virnetdevip.c: In function 'virNetDevIPAddrGet':
../../src/util/virsocketaddr.h:29:28: note: subobject 'inet4' declared here
29 | struct sockaddr_in inet4;
| ^~~~~
cc1: all warnings being treated as errors
Note the source location is pointing to the "inet6" / AF_INET6 branch of
the "if", but is complaining about bounds of the "inet4" field. Changing
the code into a switch() is sufficient to avoid triggering the bug and
is arguably better code too.
Reviewed-by: Laine Stump <laine@redhat.com>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
2020-07-22 14:58:16 +00:00
|
|
|
break;
|
|
|
|
case AF_INET:
|
2016-06-13 21:01:27 +00:00
|
|
|
addr->len = sizeof(addr->data.inet4);
|
|
|
|
memcpy(&addr->data.inet4, ifa->ifa_addr, addr->len);
|
util: refactor code to workaround gcc 10.1.0 bug
gcc 10.1.0 on Debian sid has a bug where the bounds checking gets
confused beteen two branches:
In file included from /usr/include/string.h:495,
from ../../src/internal.h:28,
from ../../src/util/virsocket.h:21,
from ../../src/util/virsocketaddr.h:21,
from ../../src/util/virnetdevip.h:21,
from ../../src/util/virnetdevip.c:21:
In function 'memcpy',
inlined from 'virNetDevGetifaddrsAddress' at ../../src/util/virnetdevip.c:914:13,
inlined from 'virNetDevIPAddrGet' at ../../src/util/virnetdevip.c:962:16:
/usr/include/arm-linux-gnueabihf/bits/string_fortified.h:34:10: error: '__builtin_memcpy' offset [16, 27] from the object at 'addr' is out of the bounds of referenced subobject 'inet4' with type 'struct sockaddr_in' at offset 0 [-Werror=array-bounds]
34 | return __builtin___memcpy_chk (__dest, __src, __len, __bos0 (__dest));
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from ../../src/util/virnetdevip.h:21,
from ../../src/util/virnetdevip.c:21:
../../src/util/virnetdevip.c: In function 'virNetDevIPAddrGet':
../../src/util/virsocketaddr.h:29:28: note: subobject 'inet4' declared here
29 | struct sockaddr_in inet4;
| ^~~~~
cc1: all warnings being treated as errors
Note the source location is pointing to the "inet6" / AF_INET6 branch of
the "if", but is complaining about bounds of the "inet4" field. Changing
the code into a switch() is sufficient to avoid triggering the bug and
is arguably better code too.
Reviewed-by: Laine Stump <laine@redhat.com>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
2020-07-22 14:58:16 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
continue;
|
2016-06-13 21:01:27 +00:00
|
|
|
}
|
util: refactor code to workaround gcc 10.1.0 bug
gcc 10.1.0 on Debian sid has a bug where the bounds checking gets
confused beteen two branches:
In file included from /usr/include/string.h:495,
from ../../src/internal.h:28,
from ../../src/util/virsocket.h:21,
from ../../src/util/virsocketaddr.h:21,
from ../../src/util/virnetdevip.h:21,
from ../../src/util/virnetdevip.c:21:
In function 'memcpy',
inlined from 'virNetDevGetifaddrsAddress' at ../../src/util/virnetdevip.c:914:13,
inlined from 'virNetDevIPAddrGet' at ../../src/util/virnetdevip.c:962:16:
/usr/include/arm-linux-gnueabihf/bits/string_fortified.h:34:10: error: '__builtin_memcpy' offset [16, 27] from the object at 'addr' is out of the bounds of referenced subobject 'inet4' with type 'struct sockaddr_in' at offset 0 [-Werror=array-bounds]
34 | return __builtin___memcpy_chk (__dest, __src, __len, __bos0 (__dest));
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from ../../src/util/virnetdevip.h:21,
from ../../src/util/virnetdevip.c:21:
../../src/util/virnetdevip.c: In function 'virNetDevIPAddrGet':
../../src/util/virsocketaddr.h:29:28: note: subobject 'inet4' declared here
29 | struct sockaddr_in inet4;
| ^~~~~
cc1: all warnings being treated as errors
Note the source location is pointing to the "inet6" / AF_INET6 branch of
the "if", but is complaining about bounds of the "inet4" field. Changing
the code into a switch() is sufficient to avoid triggering the bug and
is arguably better code too.
Reviewed-by: Laine Stump <laine@redhat.com>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
2020-07-22 14:58:16 +00:00
|
|
|
addr->data.stor.ss_family = ifa->ifa_addr->sa_family;
|
2016-06-13 21:01:27 +00:00
|
|
|
ret = 0;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("no IP address found for interface '%s'"),
|
|
|
|
ifname);
|
|
|
|
cleanup:
|
|
|
|
freeifaddrs(ifap);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2020-09-01 11:27:44 +00:00
|
|
|
#else /* ! WITH_GETIFADDRS */
|
2016-06-13 21:01:27 +00:00
|
|
|
|
|
|
|
static int
|
2019-10-14 12:45:33 +00:00
|
|
|
virNetDevGetifaddrsAddress(const char *ifname G_GNUC_UNUSED,
|
|
|
|
virSocketAddrPtr addr G_GNUC_UNUSED)
|
2016-06-13 21:01:27 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
2016-06-14 17:40:04 +00:00
|
|
|
|
|
|
|
/* 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;
|
|
|
|
}
|
2016-06-06 19:19:23 +00:00
|
|
|
|
|
|
|
/* 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);
|
2017-02-09 14:13:37 +00:00
|
|
|
ip->nips = 0;
|
2016-06-06 19:19:23 +00:00
|
|
|
|
|
|
|
for (i = 0; i < ip->nroutes; i++)
|
|
|
|
virNetDevIPRouteFree(ip->routes[i]);
|
|
|
|
VIR_FREE(ip->routes);
|
2017-02-09 14:13:37 +00:00
|
|
|
ip->nroutes = 0;
|
2016-06-06 19:19:23 +00:00
|
|
|
}
|
2016-06-16 16:22:07 +00:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
int prefix;
|
2019-10-15 13:16:31 +00:00
|
|
|
g_autofree char *ipStr = NULL;
|
2016-06-16 16:22:07 +00:00
|
|
|
|
|
|
|
/* add all IP addresses */
|
|
|
|
for (i = 0; i < ipInfo->nips; i++) {
|
|
|
|
virNetDevIPAddrPtr ip = ipInfo->ips[i];
|
|
|
|
|
|
|
|
if ((prefix = virSocketAddrGetIPPrefix(&ip->address,
|
|
|
|
NULL, ip->prefix)) < 0) {
|
2018-07-28 18:01:27 +00:00
|
|
|
ipStr = virSocketAddrFormat(&ip->address);
|
2016-06-16 16:22:07 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Failed to determine prefix for IP address '%s'"),
|
|
|
|
NULLSTR(ipStr));
|
2018-07-28 18:01:27 +00:00
|
|
|
return -1;
|
2016-06-16 16:22:07 +00:00
|
|
|
}
|
2016-06-10 16:37:37 +00:00
|
|
|
if (virNetDevIPAddrAdd(ifname, &ip->address, &ip->peer, prefix) < 0)
|
2018-07-28 18:01:27 +00:00
|
|
|
return -1;
|
2016-06-16 16:22:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* add all routes */
|
|
|
|
for (i = 0; i < ipInfo->nroutes; i++) {
|
|
|
|
virNetDevIPRoutePtr route = ipInfo->routes[i];
|
|
|
|
|
|
|
|
if ((prefix = virNetDevIPRouteGetPrefix(route)) < 0) {
|
2018-07-28 18:01:27 +00:00
|
|
|
ipStr = virSocketAddrFormat(&route->address);
|
2016-06-16 16:22:07 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Failed to determine prefix for route with destination '%s'"),
|
|
|
|
NULLSTR(ipStr));
|
2018-07-28 18:01:27 +00:00
|
|
|
return -1;
|
2016-06-16 16:22:07 +00:00
|
|
|
}
|
|
|
|
if (virNetDevIPRouteAdd(ifname, &route->address, prefix,
|
|
|
|
&route->gateway,
|
|
|
|
virNetDevIPRouteGetMetric(route)) < 0)
|
2018-07-28 18:01:27 +00:00
|
|
|
return -1;
|
2016-06-16 16:22:07 +00:00
|
|
|
}
|
|
|
|
|
2018-07-28 18:01:27 +00:00
|
|
|
return 0;
|
2016-06-16 16:22:07 +00:00
|
|
|
}
|
2018-07-28 18:01:26 +00:00
|
|
|
|
|
|
|
void
|
|
|
|
virNetDevIPAddrFree(virNetDevIPAddrPtr ip)
|
|
|
|
{
|
|
|
|
VIR_FREE(ip);
|
|
|
|
}
|