Separately locate external interfaces for IPv4 and IPv6
Now that the back end allows passt/pasta to use different external interfaces for IPv4 and IPv6, use that to do the right thing in the case that the host has IPv4 and IPv6 connectivity via different interfaces. If the user hasn't explicitly chosen an interface, separately search for a suitable external interface for each protocol. As a bonus, this substantially simplifies the external interface probe. It also eliminates a subtle confusing case where in some circumstances we would pick the first interface in interface index order, and sometimes in order of routes returned from netlink. On some network configurations that could cause tests to fail, because the logic in the tests was subtly different (it always used route order). Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
This commit is contained in:
parent
3eaf9f5320
commit
06abfcf6d9
7 changed files with 33 additions and 80 deletions
19
conf.c
19
conf.c
|
@ -630,8 +630,23 @@ static void conf_ip(struct ctx *c)
|
||||||
v4 = v6 = IP_VERSION_PROBE;
|
v4 = v6 = IP_VERSION_PROBE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!c->ifi4 && !c->ifi6)
|
if (v4 != IP_VERSION_DISABLED) {
|
||||||
c->ifi4 = c->ifi6 = nl_get_ext_if(&v4, &v6);
|
if (!c->ifi4)
|
||||||
|
c->ifi4 = nl_get_ext_if(AF_INET);
|
||||||
|
if (!c->ifi4) {
|
||||||
|
warn("No external routable interface for IPv4");
|
||||||
|
v4 = IP_VERSION_DISABLED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (v6 != IP_VERSION_DISABLED) {
|
||||||
|
if (!c->ifi6)
|
||||||
|
c->ifi6 = nl_get_ext_if(AF_INET6);
|
||||||
|
if (!c->ifi6) {
|
||||||
|
warn("No external routable interface for IPv6");
|
||||||
|
v6 = IP_VERSION_DISABLED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (v4 != IP_VERSION_DISABLED) {
|
if (v4 != IP_VERSION_DISABLED) {
|
||||||
if (!c->gw4)
|
if (!c->gw4)
|
||||||
|
|
77
netlink.c
77
netlink.c
|
@ -126,13 +126,13 @@ static int nl_req(int ns, char *buf, const void *req, ssize_t len)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* nl_get_ext_if() - Get interface index supporting IP versions being probed
|
* nl_get_ext_if() - Get interface index supporting IP version being probed
|
||||||
* @v4: Probe IPv4 support, set to ENABLED or DISABLED on return
|
* @af: Address family (AF_INET or AF_INET6) to look for connectivity
|
||||||
* @v6: Probe IPv4 support, set to ENABLED or DISABLED on return
|
* for.
|
||||||
*
|
*
|
||||||
* Return: interface index, 0 if not found
|
* Return: interface index, 0 if not found
|
||||||
*/
|
*/
|
||||||
unsigned int nl_get_ext_if(int *v4, int *v6)
|
unsigned int nl_get_ext_if(sa_family_t af)
|
||||||
{
|
{
|
||||||
struct { struct nlmsghdr nlh; struct rtmsg rtm; } req = {
|
struct { struct nlmsghdr nlh; struct rtmsg rtm; } req = {
|
||||||
.nlh.nlmsg_type = RTM_GETROUTE,
|
.nlh.nlmsg_type = RTM_GETROUTE,
|
||||||
|
@ -143,32 +143,14 @@ unsigned int nl_get_ext_if(int *v4, int *v6)
|
||||||
.rtm.rtm_table = RT_TABLE_MAIN,
|
.rtm.rtm_table = RT_TABLE_MAIN,
|
||||||
.rtm.rtm_scope = RT_SCOPE_UNIVERSE,
|
.rtm.rtm_scope = RT_SCOPE_UNIVERSE,
|
||||||
.rtm.rtm_type = RTN_UNICAST,
|
.rtm.rtm_type = RTN_UNICAST,
|
||||||
|
.rtm.rtm_family = af,
|
||||||
};
|
};
|
||||||
unsigned int i, first_v4 = 0, first_v6 = 0;
|
|
||||||
uint8_t has_v4[PAGE_SIZE * 8 / 8] = { 0 }; /* See __dev_alloc_name() */
|
|
||||||
uint8_t has_v6[PAGE_SIZE * 8 / 8] = { 0 }; /* in kernel */
|
|
||||||
struct nlmsghdr *nh;
|
struct nlmsghdr *nh;
|
||||||
struct rtattr *rta;
|
struct rtattr *rta;
|
||||||
struct rtmsg *rtm;
|
struct rtmsg *rtm;
|
||||||
char buf[BUFSIZ];
|
char buf[BUFSIZ];
|
||||||
long *word, tmp;
|
|
||||||
uint8_t *vmap;
|
|
||||||
ssize_t n;
|
ssize_t n;
|
||||||
size_t na;
|
size_t na;
|
||||||
int *v;
|
|
||||||
|
|
||||||
if (*v4 == IP_VERSION_PROBE) {
|
|
||||||
v = v4;
|
|
||||||
req.rtm.rtm_family = AF_INET;
|
|
||||||
vmap = has_v4;
|
|
||||||
} else if (*v6 == IP_VERSION_PROBE) {
|
|
||||||
v6:
|
|
||||||
v = v6;
|
|
||||||
req.rtm.rtm_family = AF_INET6;
|
|
||||||
vmap = has_v6;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((n = nl_req(0, buf, &req, sizeof(req))) < 0)
|
if ((n = nl_req(0, buf, &req, sizeof(req))) < 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -178,7 +160,7 @@ v6:
|
||||||
for ( ; NLMSG_OK(nh, n); nh = NLMSG_NEXT(nh, n)) {
|
for ( ; NLMSG_OK(nh, n); nh = NLMSG_NEXT(nh, n)) {
|
||||||
rtm = (struct rtmsg *)NLMSG_DATA(nh);
|
rtm = (struct rtmsg *)NLMSG_DATA(nh);
|
||||||
|
|
||||||
if (rtm->rtm_dst_len || rtm->rtm_family != req.rtm.rtm_family)
|
if (rtm->rtm_dst_len || rtm->rtm_family != af)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
for (rta = RTM_RTA(rtm), na = RTM_PAYLOAD(nh); RTA_OK(rta, na);
|
for (rta = RTM_RTA(rtm), na = RTM_PAYLOAD(nh); RTA_OK(rta, na);
|
||||||
|
@ -190,57 +172,10 @@ v6:
|
||||||
|
|
||||||
ifi = *(unsigned int *)RTA_DATA(rta);
|
ifi = *(unsigned int *)RTA_DATA(rta);
|
||||||
|
|
||||||
if (*v4 == IP_VERSION_DISABLED ||
|
|
||||||
*v6 == IP_VERSION_DISABLED) {
|
|
||||||
*v = IP_VERSION_ENABLED;
|
|
||||||
return ifi;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (v == v4 && !first_v4)
|
|
||||||
first_v4 = ifi;
|
|
||||||
|
|
||||||
if (v == v6 && !first_v6)
|
|
||||||
first_v6 = ifi;
|
|
||||||
|
|
||||||
bitmap_set(vmap, ifi);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (v == v4 && *v6 == IP_VERSION_PROBE) {
|
|
||||||
req.nlh.nlmsg_seq = nl_seq++;
|
|
||||||
goto v6;
|
|
||||||
}
|
|
||||||
|
|
||||||
word = (long *)has_v4;
|
|
||||||
for (i = 0; i < ARRAY_SIZE(has_v4) / sizeof(long); i++, word++) {
|
|
||||||
tmp = *word;
|
|
||||||
while ((n = ffsl(tmp))) {
|
|
||||||
int ifi = i * sizeof(long) * 8 + n - 1;
|
|
||||||
|
|
||||||
if (!first_v4)
|
|
||||||
first_v4 = ifi;
|
|
||||||
|
|
||||||
tmp &= ~(1UL << (n - 1));
|
|
||||||
if (bitmap_isset(has_v6, ifi)) {
|
|
||||||
*v4 = *v6 = IP_VERSION_ENABLED;
|
|
||||||
return ifi;
|
return ifi;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (first_v4) {
|
|
||||||
*v4 = IP_VERSION_ENABLED;
|
|
||||||
*v6 = IP_VERSION_DISABLED;
|
|
||||||
return first_v4;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (first_v6) {
|
|
||||||
*v4 = IP_VERSION_DISABLED;
|
|
||||||
*v6 = IP_VERSION_ENABLED;
|
|
||||||
return first_v6;
|
|
||||||
}
|
|
||||||
|
|
||||||
err("No external routable interface for any IP protocol");
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
#define NETLINK_H
|
#define NETLINK_H
|
||||||
|
|
||||||
int nl_sock_init(const struct ctx *c);
|
int nl_sock_init(const struct ctx *c);
|
||||||
unsigned int nl_get_ext_if(int *v4, int *v6);
|
unsigned int nl_get_ext_if(sa_family_t af);
|
||||||
void nl_route(int ns, unsigned int ifi, sa_family_t af, void *gw);
|
void nl_route(int ns, unsigned int ifi, sa_family_t af, void *gw);
|
||||||
void nl_addr(int ns, unsigned int ifi, sa_family_t af,
|
void nl_addr(int ns, unsigned int ifi, sa_family_t af,
|
||||||
void *addr, int *prefix_len, void *addr_l);
|
void *addr, int *prefix_len, void *addr_l);
|
||||||
|
|
|
@ -17,6 +17,7 @@ htools ip jq sed tr head
|
||||||
test Interface name
|
test Interface name
|
||||||
gout IFNAME ip -j link show | jq -rM '.[] | select(.link_type == "ether").ifname'
|
gout IFNAME ip -j link show | jq -rM '.[] | select(.link_type == "ether").ifname'
|
||||||
hout HOST_IFNAME ip -j -4 route show|jq -rM '[.[] | select(.dst == "default").dev] | .[0]'
|
hout HOST_IFNAME ip -j -4 route show|jq -rM '[.[] | select(.dst == "default").dev] | .[0]'
|
||||||
|
hout HOST_IFNAME6 ip -j -6 route show|jq -rM '[.[] | select(.dst == "default").dev] | .[0]'
|
||||||
check [ -n "__IFNAME__" ]
|
check [ -n "__IFNAME__" ]
|
||||||
|
|
||||||
test DHCP: address
|
test DHCP: address
|
||||||
|
@ -49,7 +50,7 @@ check [ "__SEARCH__" = "__HOST_SEARCH__" ]
|
||||||
test DHCPv6: address
|
test DHCPv6: address
|
||||||
guest /sbin/dhclient -6 __IFNAME__
|
guest /sbin/dhclient -6 __IFNAME__
|
||||||
gout ADDR6 ip -j -6 addr show|jq -rM '.[] | select(.ifname == "__IFNAME__").addr_info[] | select(.prefixlen == 128).local'
|
gout ADDR6 ip -j -6 addr show|jq -rM '.[] | select(.ifname == "__IFNAME__").addr_info[] | select(.prefixlen == 128).local'
|
||||||
hout HOST_ADDR6 ip -j -6 addr show|jq -rM '.[] | select(.ifname == "__HOST_IFNAME__").addr_info[] | select(.scope == "global").local'
|
hout HOST_ADDR6 ip -j -6 addr show|jq -rM '.[] | select(.ifname == "__HOST_IFNAME6__").addr_info[] | select(.scope == "global").local'
|
||||||
check [ "__ADDR6__" = "__HOST_ADDR6__" ]
|
check [ "__ADDR6__" = "__HOST_ADDR6__" ]
|
||||||
|
|
||||||
test DHCPv6: route
|
test DHCPv6: route
|
||||||
|
|
|
@ -35,8 +35,9 @@ check [ __MTU__ = 65520 ]
|
||||||
|
|
||||||
test DHCPv6: address
|
test DHCPv6: address
|
||||||
ns /sbin/dhclient -6 --no-pid __IFNAME__
|
ns /sbin/dhclient -6 --no-pid __IFNAME__
|
||||||
|
hout HOST_IFNAME6 ip -j -6 route show|jq -rM '[.[] | select(.dst == "default").dev] | .[0]'
|
||||||
nsout ADDR6 ip -j -6 addr show|jq -rM '.[] | select(.ifname == "__IFNAME__").addr_info[] | select(.prefixlen == 128).local'
|
nsout ADDR6 ip -j -6 addr show|jq -rM '.[] | select(.ifname == "__IFNAME__").addr_info[] | select(.prefixlen == 128).local'
|
||||||
hout HOST_ADDR6 ip -j -6 addr show|jq -rM '.[] | select(.ifname == "__IFNAME__").addr_info[] | select(.scope == "global").local'
|
hout HOST_ADDR6 ip -j -6 addr show|jq -rM '.[] | select(.ifname == "__HOST_IFNAME6__").addr_info[] | select(.scope == "global").local'
|
||||||
check [ __ADDR6__ = __HOST_ADDR6__ ]
|
check [ __ADDR6__ = __HOST_ADDR6__ ]
|
||||||
|
|
||||||
test DHCPv6: route
|
test DHCPv6: route
|
||||||
|
|
|
@ -17,13 +17,13 @@ htools ip jq sipcalc grep cut
|
||||||
test Interface name
|
test Interface name
|
||||||
gout IFNAME ip -j link show | jq -rM '.[] | select(.link_type == "ether").ifname'
|
gout IFNAME ip -j link show | jq -rM '.[] | select(.link_type == "ether").ifname'
|
||||||
guest ip link set dev __IFNAME__ up && sleep 2
|
guest ip link set dev __IFNAME__ up && sleep 2
|
||||||
hout HOST_IFNAME ip -j -4 route show|jq -rM '.[] | select(.dst == "default").dev'
|
hout HOST_IFNAME6 ip -j -6 route show|jq -rM '.[] | select(.dst == "default").dev'
|
||||||
check [ -n "__IFNAME__" ]
|
check [ -n "__IFNAME__" ]
|
||||||
|
|
||||||
test SLAAC: prefix
|
test SLAAC: prefix
|
||||||
gout ADDR6 ip -j -6 addr show|jq -rM '.[] | select(.ifname == "__IFNAME__").addr_info[] | select(.scope == "global" and .prefixlen == 64).local'
|
gout ADDR6 ip -j -6 addr show|jq -rM '.[] | select(.ifname == "__IFNAME__").addr_info[] | select(.scope == "global" and .prefixlen == 64).local'
|
||||||
gout PREFIX6 sipcalc __ADDR6__/64 | grep prefix | cut -d' ' -f4
|
gout PREFIX6 sipcalc __ADDR6__/64 | grep prefix | cut -d' ' -f4
|
||||||
hout HOST_ADDR6 ip -j -6 addr show|jq -rM '.[] | select(.ifname == "__HOST_IFNAME__").addr_info[] | select(.scope == "global").local'
|
hout HOST_ADDR6 ip -j -6 addr show|jq -rM '.[] | select(.ifname == "__HOST_IFNAME6__").addr_info[] | select(.scope == "global").local'
|
||||||
hout HOST_PREFIX6 sipcalc __HOST_ADDR6__/64 | grep prefix | cut -d' ' -f4
|
hout HOST_PREFIX6 sipcalc __HOST_ADDR6__/64 | grep prefix | cut -d' ' -f4
|
||||||
check [ "__PREFIX6__" = "__HOST_PREFIX6__" ]
|
check [ "__PREFIX6__" = "__HOST_PREFIX6__" ]
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ test Interface names
|
||||||
g1out IFNAME1 ip -j link show | jq -rM '.[] | select(.link_type == "ether").ifname'
|
g1out IFNAME1 ip -j link show | jq -rM '.[] | select(.link_type == "ether").ifname'
|
||||||
g2out IFNAME2 ip -j link show | jq -rM '.[] | select(.link_type == "ether").ifname'
|
g2out IFNAME2 ip -j link show | jq -rM '.[] | select(.link_type == "ether").ifname'
|
||||||
hout HOST_IFNAME ip -j -4 route show|jq -rM '[.[] | select(.dst == "default").dev] | .[0]'
|
hout HOST_IFNAME ip -j -4 route show|jq -rM '[.[] | select(.dst == "default").dev] | .[0]'
|
||||||
|
hout HOST_IFNAME6 ip -j -6 route show|jq -rM '[.[] | select(.dst == "default").dev] | .[0]'
|
||||||
check [ -n "__IFNAME1__" ]
|
check [ -n "__IFNAME1__" ]
|
||||||
check [ -n "__IFNAME2__" ]
|
check [ -n "__IFNAME2__" ]
|
||||||
|
|
||||||
|
@ -40,7 +41,7 @@ guest1 /sbin/dhclient -6 __IFNAME1__
|
||||||
guest2 /sbin/dhclient -6 __IFNAME2__
|
guest2 /sbin/dhclient -6 __IFNAME2__
|
||||||
g1out ADDR1_6 ip -j -6 addr show|jq -rM '.[] | select(.ifname == "__IFNAME1__").addr_info[] | select(.prefixlen == 128).local'
|
g1out ADDR1_6 ip -j -6 addr show|jq -rM '.[] | select(.ifname == "__IFNAME1__").addr_info[] | select(.prefixlen == 128).local'
|
||||||
g2out ADDR2_6 ip -j -6 addr show|jq -rM '.[] | select(.ifname == "__IFNAME2__").addr_info[] | select(.prefixlen == 128).local'
|
g2out ADDR2_6 ip -j -6 addr show|jq -rM '.[] | select(.ifname == "__IFNAME2__").addr_info[] | select(.prefixlen == 128).local'
|
||||||
hout HOST_ADDR6 ip -j -6 addr show|jq -rM '.[] | select(.ifname == "__HOST_IFNAME__").addr_info[] | select(.scope == "global").local'
|
hout HOST_ADDR6 ip -j -6 addr show|jq -rM '.[] | select(.ifname == "__HOST_IFNAME6__").addr_info[] | select(.scope == "global").local'
|
||||||
check [ "__ADDR1_6__" = "__HOST_ADDR6__" ]
|
check [ "__ADDR1_6__" = "__HOST_ADDR6__" ]
|
||||||
check [ "__ADDR2_6__" = "__HOST_ADDR6__" ]
|
check [ "__ADDR2_6__" = "__HOST_ADDR6__" ]
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue