Merge branch 'net-ipv6-Address-checks-need-to-consider-the-L3-domain'

David Ahern says:

====================
net/ipv6: Address checks need to consider the L3 domain

IPv6 prohibits a local address from being used as a gateway for a route.
However, it is ok for the gateway to be a local address in a different L3
domain (e.g., VRF). This allows, for example, veth pairs to connect VRFs.

ip6_route_info_create calls ipv6_chk_addr_and_flags for gateway addresses
to determine if the address is a local one, but ipv6_chk_addr_and_flags
does not currently consider L3 domains. As a result routes can not be
added in one VRF with a nexthop that points to a local address in a
second VRF.

Resolve by comparing the l3mdev for the passed in device and requiring an
l3mdev match with the device containing an address. The intent of checking
for an address on the specified device versus any device in the domain is
mantained by a new argument to skip the check between the passed in device
and the device with the address.

Patch 1 moves the gateway validation from ip6_route_info_create into a
helper; the function is long enough and refactoring drops the indent
level.

Patch 2 adds a skip_dev_check argument to ipv6_chk_addr_and_flags to
allow a device to always be passed yet skip the device check when
looking at addresses and fixes up a few ipv6_chk_addr callers that
pass a NULL device.

Patch 3 adds l3mdev checks to ipv6_chk_addr_and_flags.

Patches 4 and 5 do some refactoring to the fib_tests script and then
patch 6 adds nexthop validation tests.

v4
- separated l3mdev check into a separate patch (patch 3 of this set)
  as suggested by Kirill
- consolidated dev and ipv6_chk_addr_and_flags call into 1 if (Kirill)
- added a temp variable for gw type (Kirill)

v3
- set skip_dev_check in ipv6_chk_addr based on dev == NULL (per
  comment from Ido)

v2
- handle 2 variations of route spec with sane error path
- add test cases
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2018-03-16 11:28:40 -04:00
commit 859844e5c2
8 changed files with 390 additions and 158 deletions

View file

@ -69,8 +69,8 @@ int addrconf_set_dstaddr(struct net *net, void __user *arg);
int ipv6_chk_addr(struct net *net, const struct in6_addr *addr, int ipv6_chk_addr(struct net *net, const struct in6_addr *addr,
const struct net_device *dev, int strict); const struct net_device *dev, int strict);
int ipv6_chk_addr_and_flags(struct net *net, const struct in6_addr *addr, int ipv6_chk_addr_and_flags(struct net *net, const struct in6_addr *addr,
const struct net_device *dev, int strict, const struct net_device *dev, bool skip_dev_check,
u32 banned_flags); int strict, u32 banned_flags);
#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) #if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
int ipv6_chk_home_addr(struct net *net, const struct in6_addr *addr); int ipv6_chk_home_addr(struct net *net, const struct in6_addr *addr);

View file

@ -1851,22 +1851,42 @@ static int ipv6_count_addresses(const struct inet6_dev *idev)
int ipv6_chk_addr(struct net *net, const struct in6_addr *addr, int ipv6_chk_addr(struct net *net, const struct in6_addr *addr,
const struct net_device *dev, int strict) const struct net_device *dev, int strict)
{ {
return ipv6_chk_addr_and_flags(net, addr, dev, strict, IFA_F_TENTATIVE); return ipv6_chk_addr_and_flags(net, addr, dev, !dev,
strict, IFA_F_TENTATIVE);
} }
EXPORT_SYMBOL(ipv6_chk_addr); EXPORT_SYMBOL(ipv6_chk_addr);
/* device argument is used to find the L3 domain of interest. If
* skip_dev_check is set, then the ifp device is not checked against
* the passed in dev argument. So the 2 cases for addresses checks are:
* 1. does the address exist in the L3 domain that dev is part of
* (skip_dev_check = true), or
*
* 2. does the address exist on the specific device
* (skip_dev_check = false)
*/
int ipv6_chk_addr_and_flags(struct net *net, const struct in6_addr *addr, int ipv6_chk_addr_and_flags(struct net *net, const struct in6_addr *addr,
const struct net_device *dev, int strict, const struct net_device *dev, bool skip_dev_check,
u32 banned_flags) int strict, u32 banned_flags)
{ {
unsigned int hash = inet6_addr_hash(net, addr); unsigned int hash = inet6_addr_hash(net, addr);
const struct net_device *l3mdev;
struct inet6_ifaddr *ifp; struct inet6_ifaddr *ifp;
u32 ifp_flags; u32 ifp_flags;
rcu_read_lock(); rcu_read_lock();
l3mdev = l3mdev_master_dev_rcu(dev);
if (skip_dev_check)
dev = NULL;
hlist_for_each_entry_rcu(ifp, &inet6_addr_lst[hash], addr_lst) { hlist_for_each_entry_rcu(ifp, &inet6_addr_lst[hash], addr_lst) {
if (!net_eq(dev_net(ifp->idev->dev), net)) if (!net_eq(dev_net(ifp->idev->dev), net))
continue; continue;
if (l3mdev_master_dev_rcu(ifp->idev->dev) != l3mdev)
continue;
/* Decouple optimistic from tentative for evaluation here. /* Decouple optimistic from tentative for evaluation here.
* Ban optimistic addresses explicitly, when required. * Ban optimistic addresses explicitly, when required.
*/ */

View file

@ -66,7 +66,11 @@ int ipv6_sock_ac_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
return -EPERM; return -EPERM;
if (ipv6_addr_is_multicast(addr)) if (ipv6_addr_is_multicast(addr))
return -EINVAL; return -EINVAL;
if (ipv6_chk_addr(net, addr, NULL, 0))
if (ifindex)
dev = __dev_get_by_index(net, ifindex);
if (ipv6_chk_addr_and_flags(net, addr, dev, true, 0, IFA_F_TENTATIVE))
return -EINVAL; return -EINVAL;
pac = sock_kmalloc(sk, sizeof(struct ipv6_ac_socklist), GFP_KERNEL); pac = sock_kmalloc(sk, sizeof(struct ipv6_ac_socklist), GFP_KERNEL);
@ -90,8 +94,7 @@ int ipv6_sock_ac_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
dev = __dev_get_by_flags(net, IFF_UP, dev = __dev_get_by_flags(net, IFF_UP,
IFF_UP | IFF_LOOPBACK); IFF_UP | IFF_LOOPBACK);
} }
} else }
dev = __dev_get_by_index(net, ifindex);
if (!dev) { if (!dev) {
err = -ENODEV; err = -ENODEV;

View file

@ -801,8 +801,9 @@ int ip6_datagram_send_ctl(struct net *net, struct sock *sk,
if (addr_type != IPV6_ADDR_ANY) { if (addr_type != IPV6_ADDR_ANY) {
int strict = __ipv6_addr_src_scope(addr_type) <= IPV6_ADDR_SCOPE_LINKLOCAL; int strict = __ipv6_addr_src_scope(addr_type) <= IPV6_ADDR_SCOPE_LINKLOCAL;
if (!(inet_sk(sk)->freebind || inet_sk(sk)->transparent) && if (!(inet_sk(sk)->freebind || inet_sk(sk)->transparent) &&
!ipv6_chk_addr(net, &src_info->ipi6_addr, !ipv6_chk_addr_and_flags(net, &src_info->ipi6_addr,
strict ? dev : NULL, 0) && dev, !strict, 0,
IFA_F_TENTATIVE) &&
!ipv6_chk_acast_addr_src(net, dev, !ipv6_chk_acast_addr_src(net, dev,
&src_info->ipi6_addr)) &src_info->ipi6_addr))
err = -EINVAL; err = -EINVAL;

View file

@ -758,9 +758,11 @@ int ip6_tnl_rcv_ctl(struct ip6_tnl *t,
ldev = dev_get_by_index_rcu(net, p->link); ldev = dev_get_by_index_rcu(net, p->link);
if ((ipv6_addr_is_multicast(laddr) || if ((ipv6_addr_is_multicast(laddr) ||
likely(ipv6_chk_addr(net, laddr, ldev, 0))) && likely(ipv6_chk_addr_and_flags(net, laddr, ldev, false,
0, IFA_F_TENTATIVE))) &&
((p->flags & IP6_TNL_F_ALLOW_LOCAL_REMOTE) || ((p->flags & IP6_TNL_F_ALLOW_LOCAL_REMOTE) ||
likely(!ipv6_chk_addr(net, raddr, NULL, 0)))) likely(!ipv6_chk_addr_and_flags(net, raddr, ldev, true,
0, IFA_F_TENTATIVE))))
ret = 1; ret = 1;
} }
return ret; return ret;
@ -990,12 +992,14 @@ int ip6_tnl_xmit_ctl(struct ip6_tnl *t,
if (p->link) if (p->link)
ldev = dev_get_by_index_rcu(net, p->link); ldev = dev_get_by_index_rcu(net, p->link);
if (unlikely(!ipv6_chk_addr(net, laddr, ldev, 0))) if (unlikely(!ipv6_chk_addr_and_flags(net, laddr, ldev, false,
0, IFA_F_TENTATIVE)))
pr_warn("%s xmit: Local address not yet configured!\n", pr_warn("%s xmit: Local address not yet configured!\n",
p->name); p->name);
else if (!(p->flags & IP6_TNL_F_ALLOW_LOCAL_REMOTE) && else if (!(p->flags & IP6_TNL_F_ALLOW_LOCAL_REMOTE) &&
!ipv6_addr_is_multicast(raddr) && !ipv6_addr_is_multicast(raddr) &&
unlikely(ipv6_chk_addr(net, raddr, NULL, 0))) unlikely(ipv6_chk_addr_and_flags(net, raddr, ldev,
true, 0, IFA_F_TENTATIVE)))
pr_warn("%s xmit: Routing loop! Remote address found on this node!\n", pr_warn("%s xmit: Routing loop! Remote address found on this node!\n",
p->name); p->name);
else else

View file

@ -707,7 +707,7 @@ static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb)
int probes = atomic_read(&neigh->probes); int probes = atomic_read(&neigh->probes);
if (skb && ipv6_chk_addr_and_flags(dev_net(dev), &ipv6_hdr(skb)->saddr, if (skb && ipv6_chk_addr_and_flags(dev_net(dev), &ipv6_hdr(skb)->saddr,
dev, 1, dev, false, 1,
IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) IFA_F_TENTATIVE|IFA_F_OPTIMISTIC))
saddr = &ipv6_hdr(skb)->saddr; saddr = &ipv6_hdr(skb)->saddr;
probes -= NEIGH_VAR(neigh->parms, UCAST_PROBES); probes -= NEIGH_VAR(neigh->parms, UCAST_PROBES);

View file

@ -2550,7 +2550,7 @@ static struct rt6_info *ip6_nh_lookup_table(struct net *net,
static int ip6_route_check_nh_onlink(struct net *net, static int ip6_route_check_nh_onlink(struct net *net,
struct fib6_config *cfg, struct fib6_config *cfg,
struct net_device *dev, const struct net_device *dev,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
{ {
u32 tbid = l3mdev_fib_table(dev) ? : RT_TABLE_MAIN; u32 tbid = l3mdev_fib_table(dev) ? : RT_TABLE_MAIN;
@ -2626,6 +2626,79 @@ static int ip6_route_check_nh(struct net *net,
return err; return err;
} }
static int ip6_validate_gw(struct net *net, struct fib6_config *cfg,
struct net_device **_dev, struct inet6_dev **idev,
struct netlink_ext_ack *extack)
{
const struct in6_addr *gw_addr = &cfg->fc_gateway;
int gwa_type = ipv6_addr_type(gw_addr);
bool skip_dev = gwa_type & IPV6_ADDR_LINKLOCAL ? false : true;
const struct net_device *dev = *_dev;
bool need_addr_check = !dev;
int err = -EINVAL;
/* if gw_addr is local we will fail to detect this in case
* address is still TENTATIVE (DAD in progress). rt6_lookup()
* will return already-added prefix route via interface that
* prefix route was assigned to, which might be non-loopback.
*/
if (dev &&
ipv6_chk_addr_and_flags(net, gw_addr, dev, skip_dev, 0, 0)) {
NL_SET_ERR_MSG(extack, "Gateway can not be a local address");
goto out;
}
if (gwa_type != (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_UNICAST)) {
/* IPv6 strictly inhibits using not link-local
* addresses as nexthop address.
* Otherwise, router will not able to send redirects.
* It is very good, but in some (rare!) circumstances
* (SIT, PtP, NBMA NOARP links) it is handy to allow
* some exceptions. --ANK
* We allow IPv4-mapped nexthops to support RFC4798-type
* addressing
*/
if (!(gwa_type & (IPV6_ADDR_UNICAST | IPV6_ADDR_MAPPED))) {
NL_SET_ERR_MSG(extack, "Invalid gateway address");
goto out;
}
if (cfg->fc_flags & RTNH_F_ONLINK)
err = ip6_route_check_nh_onlink(net, cfg, dev, extack);
else
err = ip6_route_check_nh(net, cfg, _dev, idev);
if (err)
goto out;
}
/* reload in case device was changed */
dev = *_dev;
err = -EINVAL;
if (!dev) {
NL_SET_ERR_MSG(extack, "Egress device not specified");
goto out;
} else if (dev->flags & IFF_LOOPBACK) {
NL_SET_ERR_MSG(extack,
"Egress device can not be loopback device for this route");
goto out;
}
/* if we did not check gw_addr above, do so now that the
* egress device has been resolved.
*/
if (need_addr_check &&
ipv6_chk_addr_and_flags(net, gw_addr, dev, skip_dev, 0, 0)) {
NL_SET_ERR_MSG(extack, "Gateway can not be a local address");
goto out;
}
err = 0;
out:
return err;
}
static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg, static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
{ {
@ -2808,61 +2881,11 @@ static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg,
} }
if (cfg->fc_flags & RTF_GATEWAY) { if (cfg->fc_flags & RTF_GATEWAY) {
const struct in6_addr *gw_addr; err = ip6_validate_gw(net, cfg, &dev, &idev, extack);
int gwa_type; if (err)
gw_addr = &cfg->fc_gateway;
gwa_type = ipv6_addr_type(gw_addr);
/* if gw_addr is local we will fail to detect this in case
* address is still TENTATIVE (DAD in progress). rt6_lookup()
* will return already-added prefix route via interface that
* prefix route was assigned to, which might be non-loopback.
*/
err = -EINVAL;
if (ipv6_chk_addr_and_flags(net, gw_addr,
gwa_type & IPV6_ADDR_LINKLOCAL ?
dev : NULL, 0, 0)) {
NL_SET_ERR_MSG(extack, "Invalid gateway address");
goto out; goto out;
}
rt->rt6i_gateway = *gw_addr;
if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) { rt->rt6i_gateway = cfg->fc_gateway;
/* IPv6 strictly inhibits using not link-local
addresses as nexthop address.
Otherwise, router will not able to send redirects.
It is very good, but in some (rare!) circumstances
(SIT, PtP, NBMA NOARP links) it is handy to allow
some exceptions. --ANK
We allow IPv4-mapped nexthops to support RFC4798-type
addressing
*/
if (!(gwa_type & (IPV6_ADDR_UNICAST |
IPV6_ADDR_MAPPED))) {
NL_SET_ERR_MSG(extack,
"Invalid gateway address");
goto out;
}
if (cfg->fc_flags & RTNH_F_ONLINK) {
err = ip6_route_check_nh_onlink(net, cfg, dev,
extack);
} else {
err = ip6_route_check_nh(net, cfg, &dev, &idev);
}
if (err)
goto out;
}
err = -EINVAL;
if (!dev) {
NL_SET_ERR_MSG(extack, "Egress device not specified");
goto out;
} else if (dev->flags & IFF_LOOPBACK) {
NL_SET_ERR_MSG(extack,
"Egress device can not be loopback device for this route");
goto out;
}
} }
err = -ENODEV; err = -ENODEV;

View file

@ -6,7 +6,9 @@
ret=0 ret=0
VERBOSE=${VERBOSE:=0}
PAUSE_ON_FAIL=${PAUSE_ON_FAIL:=no} PAUSE_ON_FAIL=${PAUSE_ON_FAIL:=no}
IP="ip -netns testns"
log_test() log_test()
{ {
@ -15,10 +17,10 @@ log_test()
local msg="$3" local msg="$3"
if [ ${rc} -eq ${expected} ]; then if [ ${rc} -eq ${expected} ]; then
printf " %-60s [ OK ]\n" "${msg}" printf " TEST: %-60s [ OK ]\n" "${msg}"
else else
ret=1 ret=1
printf " %-60s [FAIL]\n" "${msg}" printf " TEST: %-60s [FAIL]\n" "${msg}"
if [ "${PAUSE_ON_FAIL}" = "yes" ]; then if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
echo echo
echo "hit enter to continue, 'q' to quit" echo "hit enter to continue, 'q' to quit"
@ -32,22 +34,44 @@ setup()
{ {
set -e set -e
ip netns add testns ip netns add testns
ip -netns testns link set dev lo up $IP link set dev lo up
ip -netns testns link add dummy0 type dummy $IP link add dummy0 type dummy
ip -netns testns link set dev dummy0 up $IP link set dev dummy0 up
ip -netns testns address add 198.51.100.1/24 dev dummy0 $IP address add 198.51.100.1/24 dev dummy0
ip -netns testns -6 address add 2001:db8:1::1/64 dev dummy0 $IP -6 address add 2001:db8:1::1/64 dev dummy0
set +e set +e
} }
cleanup() cleanup()
{ {
ip -netns testns link del dev dummy0 &> /dev/null $IP link del dev dummy0 &> /dev/null
ip netns del testns ip netns del testns
} }
get_linklocal()
{
local dev=$1
local addr
addr=$($IP -6 -br addr show dev ${dev} | \
awk '{
for (i = 3; i <= NF; ++i) {
if ($i ~ /^fe80/)
print $i
}
}'
)
addr=${addr/\/*}
[ -z "$addr" ] && return 1
echo $addr
return 0
}
fib_unreg_unicast_test() fib_unreg_unicast_test()
{ {
echo echo
@ -56,19 +80,19 @@ fib_unreg_unicast_test()
setup setup
echo " Start point" echo " Start point"
ip -netns testns route get fibmatch 198.51.100.2 &> /dev/null $IP route get fibmatch 198.51.100.2 &> /dev/null
log_test $? 0 "IPv4 fibmatch" log_test $? 0 "IPv4 fibmatch"
ip -netns testns -6 route get fibmatch 2001:db8:1::2 &> /dev/null $IP -6 route get fibmatch 2001:db8:1::2 &> /dev/null
log_test $? 0 "IPv6 fibmatch" log_test $? 0 "IPv6 fibmatch"
set -e set -e
ip -netns testns link del dev dummy0 $IP link del dev dummy0
set +e set +e
echo " Nexthop device deleted" echo " Nexthop device deleted"
ip -netns testns route get fibmatch 198.51.100.2 &> /dev/null $IP route get fibmatch 198.51.100.2 &> /dev/null
log_test $? 2 "IPv4 fibmatch - no route" log_test $? 2 "IPv4 fibmatch - no route"
ip -netns testns -6 route get fibmatch 2001:db8:1::2 &> /dev/null $IP -6 route get fibmatch 2001:db8:1::2 &> /dev/null
log_test $? 2 "IPv6 fibmatch - no route" log_test $? 2 "IPv6 fibmatch - no route"
cleanup cleanup
@ -83,43 +107,43 @@ fib_unreg_multipath_test()
setup setup
set -e set -e
ip -netns testns link add dummy1 type dummy $IP link add dummy1 type dummy
ip -netns testns link set dev dummy1 up $IP link set dev dummy1 up
ip -netns testns address add 192.0.2.1/24 dev dummy1 $IP address add 192.0.2.1/24 dev dummy1
ip -netns testns -6 address add 2001:db8:2::1/64 dev dummy1 $IP -6 address add 2001:db8:2::1/64 dev dummy1
ip -netns testns route add 203.0.113.0/24 \ $IP route add 203.0.113.0/24 \
nexthop via 198.51.100.2 dev dummy0 \ nexthop via 198.51.100.2 dev dummy0 \
nexthop via 192.0.2.2 dev dummy1 nexthop via 192.0.2.2 dev dummy1
ip -netns testns -6 route add 2001:db8:3::/64 \ $IP -6 route add 2001:db8:3::/64 \
nexthop via 2001:db8:1::2 dev dummy0 \ nexthop via 2001:db8:1::2 dev dummy0 \
nexthop via 2001:db8:2::2 dev dummy1 nexthop via 2001:db8:2::2 dev dummy1
set +e set +e
echo " Start point" echo " Start point"
ip -netns testns route get fibmatch 203.0.113.1 &> /dev/null $IP route get fibmatch 203.0.113.1 &> /dev/null
log_test $? 0 "IPv4 fibmatch" log_test $? 0 "IPv4 fibmatch"
ip -netns testns -6 route get fibmatch 2001:db8:3::1 &> /dev/null $IP -6 route get fibmatch 2001:db8:3::1 &> /dev/null
log_test $? 0 "IPv6 fibmatch" log_test $? 0 "IPv6 fibmatch"
set -e set -e
ip -netns testns link del dev dummy0 $IP link del dev dummy0
set +e set +e
echo " One nexthop device deleted" echo " One nexthop device deleted"
ip -netns testns route get fibmatch 203.0.113.1 &> /dev/null $IP route get fibmatch 203.0.113.1 &> /dev/null
log_test $? 2 "IPv4 - multipath route removed on delete" log_test $? 2 "IPv4 - multipath route removed on delete"
ip -netns testns -6 route get fibmatch 2001:db8:3::1 &> /dev/null $IP -6 route get fibmatch 2001:db8:3::1 &> /dev/null
# In IPv6 we do not flush the entire multipath route. # In IPv6 we do not flush the entire multipath route.
log_test $? 0 "IPv6 - multipath down to single path" log_test $? 0 "IPv6 - multipath down to single path"
set -e set -e
ip -netns testns link del dev dummy1 $IP link del dev dummy1
set +e set +e
echo " Second nexthop device deleted" echo " Second nexthop device deleted"
ip -netns testns -6 route get fibmatch 2001:db8:3::1 &> /dev/null $IP -6 route get fibmatch 2001:db8:3::1 &> /dev/null
log_test $? 2 "IPv6 - no route" log_test $? 2 "IPv6 - no route"
cleanup cleanup
@ -139,19 +163,19 @@ fib_down_unicast_test()
setup setup
echo " Start point" echo " Start point"
ip -netns testns route get fibmatch 198.51.100.2 &> /dev/null $IP route get fibmatch 198.51.100.2 &> /dev/null
log_test $? 0 "IPv4 fibmatch" log_test $? 0 "IPv4 fibmatch"
ip -netns testns -6 route get fibmatch 2001:db8:1::2 &> /dev/null $IP -6 route get fibmatch 2001:db8:1::2 &> /dev/null
log_test $? 0 "IPv6 fibmatch" log_test $? 0 "IPv6 fibmatch"
set -e set -e
ip -netns testns link set dev dummy0 down $IP link set dev dummy0 down
set +e set +e
echo " Route deleted on down" echo " Route deleted on down"
ip -netns testns route get fibmatch 198.51.100.2 &> /dev/null $IP route get fibmatch 198.51.100.2 &> /dev/null
log_test $? 2 "IPv4 fibmatch" log_test $? 2 "IPv4 fibmatch"
ip -netns testns -6 route get fibmatch 2001:db8:1::2 &> /dev/null $IP -6 route get fibmatch 2001:db8:1::2 &> /dev/null
log_test $? 2 "IPv6 fibmatch" log_test $? 2 "IPv6 fibmatch"
cleanup cleanup
@ -162,31 +186,31 @@ fib_down_multipath_test_do()
local down_dev=$1 local down_dev=$1
local up_dev=$2 local up_dev=$2
ip -netns testns route get fibmatch 203.0.113.1 \ $IP route get fibmatch 203.0.113.1 \
oif $down_dev &> /dev/null oif $down_dev &> /dev/null
log_test $? 2 "IPv4 fibmatch on down device" log_test $? 2 "IPv4 fibmatch on down device"
ip -netns testns -6 route get fibmatch 2001:db8:3::1 \ $IP -6 route get fibmatch 2001:db8:3::1 \
oif $down_dev &> /dev/null oif $down_dev &> /dev/null
log_test $? 2 "IPv6 fibmatch on down device" log_test $? 2 "IPv6 fibmatch on down device"
ip -netns testns route get fibmatch 203.0.113.1 \ $IP route get fibmatch 203.0.113.1 \
oif $up_dev &> /dev/null oif $up_dev &> /dev/null
log_test $? 0 "IPv4 fibmatch on up device" log_test $? 0 "IPv4 fibmatch on up device"
ip -netns testns -6 route get fibmatch 2001:db8:3::1 \ $IP -6 route get fibmatch 2001:db8:3::1 \
oif $up_dev &> /dev/null oif $up_dev &> /dev/null
log_test $? 0 "IPv6 fibmatch on up device" log_test $? 0 "IPv6 fibmatch on up device"
ip -netns testns route get fibmatch 203.0.113.1 | \ $IP route get fibmatch 203.0.113.1 | \
grep $down_dev | grep -q "dead linkdown" grep $down_dev | grep -q "dead linkdown"
log_test $? 0 "IPv4 flags on down device" log_test $? 0 "IPv4 flags on down device"
ip -netns testns -6 route get fibmatch 2001:db8:3::1 | \ $IP -6 route get fibmatch 2001:db8:3::1 | \
grep $down_dev | grep -q "dead linkdown" grep $down_dev | grep -q "dead linkdown"
log_test $? 0 "IPv6 flags on down device" log_test $? 0 "IPv6 flags on down device"
ip -netns testns route get fibmatch 203.0.113.1 | \ $IP route get fibmatch 203.0.113.1 | \
grep $up_dev | grep -q "dead linkdown" grep $up_dev | grep -q "dead linkdown"
log_test $? 1 "IPv4 flags on up device" log_test $? 1 "IPv4 flags on up device"
ip -netns testns -6 route get fibmatch 2001:db8:3::1 | \ $IP -6 route get fibmatch 2001:db8:3::1 | \
grep $up_dev | grep -q "dead linkdown" grep $up_dev | grep -q "dead linkdown"
log_test $? 1 "IPv6 flags on up device" log_test $? 1 "IPv6 flags on up device"
} }
@ -199,53 +223,53 @@ fib_down_multipath_test()
setup setup
set -e set -e
ip -netns testns link add dummy1 type dummy $IP link add dummy1 type dummy
ip -netns testns link set dev dummy1 up $IP link set dev dummy1 up
ip -netns testns address add 192.0.2.1/24 dev dummy1 $IP address add 192.0.2.1/24 dev dummy1
ip -netns testns -6 address add 2001:db8:2::1/64 dev dummy1 $IP -6 address add 2001:db8:2::1/64 dev dummy1
ip -netns testns route add 203.0.113.0/24 \ $IP route add 203.0.113.0/24 \
nexthop via 198.51.100.2 dev dummy0 \ nexthop via 198.51.100.2 dev dummy0 \
nexthop via 192.0.2.2 dev dummy1 nexthop via 192.0.2.2 dev dummy1
ip -netns testns -6 route add 2001:db8:3::/64 \ $IP -6 route add 2001:db8:3::/64 \
nexthop via 2001:db8:1::2 dev dummy0 \ nexthop via 2001:db8:1::2 dev dummy0 \
nexthop via 2001:db8:2::2 dev dummy1 nexthop via 2001:db8:2::2 dev dummy1
set +e set +e
echo " Verify start point" echo " Verify start point"
ip -netns testns route get fibmatch 203.0.113.1 &> /dev/null $IP route get fibmatch 203.0.113.1 &> /dev/null
log_test $? 0 "IPv4 fibmatch" log_test $? 0 "IPv4 fibmatch"
ip -netns testns -6 route get fibmatch 2001:db8:3::1 &> /dev/null $IP -6 route get fibmatch 2001:db8:3::1 &> /dev/null
log_test $? 0 "IPv6 fibmatch" log_test $? 0 "IPv6 fibmatch"
set -e set -e
ip -netns testns link set dev dummy0 down $IP link set dev dummy0 down
set +e set +e
echo " One device down, one up" echo " One device down, one up"
fib_down_multipath_test_do "dummy0" "dummy1" fib_down_multipath_test_do "dummy0" "dummy1"
set -e set -e
ip -netns testns link set dev dummy0 up $IP link set dev dummy0 up
ip -netns testns link set dev dummy1 down $IP link set dev dummy1 down
set +e set +e
echo " Other device down and up" echo " Other device down and up"
fib_down_multipath_test_do "dummy1" "dummy0" fib_down_multipath_test_do "dummy1" "dummy0"
set -e set -e
ip -netns testns link set dev dummy0 down $IP link set dev dummy0 down
set +e set +e
echo " Both devices down" echo " Both devices down"
ip -netns testns route get fibmatch 203.0.113.1 &> /dev/null $IP route get fibmatch 203.0.113.1 &> /dev/null
log_test $? 2 "IPv4 fibmatch" log_test $? 2 "IPv4 fibmatch"
ip -netns testns -6 route get fibmatch 2001:db8:3::1 &> /dev/null $IP -6 route get fibmatch 2001:db8:3::1 &> /dev/null
log_test $? 2 "IPv6 fibmatch" log_test $? 2 "IPv6 fibmatch"
ip -netns testns link del dev dummy1 $IP link del dev dummy1
cleanup cleanup
} }
@ -264,55 +288,55 @@ fib_carrier_local_test()
setup setup
set -e set -e
ip -netns testns link set dev dummy0 carrier on $IP link set dev dummy0 carrier on
set +e set +e
echo " Start point" echo " Start point"
ip -netns testns route get fibmatch 198.51.100.1 &> /dev/null $IP route get fibmatch 198.51.100.1 &> /dev/null
log_test $? 0 "IPv4 fibmatch" log_test $? 0 "IPv4 fibmatch"
ip -netns testns -6 route get fibmatch 2001:db8:1::1 &> /dev/null $IP -6 route get fibmatch 2001:db8:1::1 &> /dev/null
log_test $? 0 "IPv6 fibmatch" log_test $? 0 "IPv6 fibmatch"
ip -netns testns route get fibmatch 198.51.100.1 | \ $IP route get fibmatch 198.51.100.1 | \
grep -q "linkdown" grep -q "linkdown"
log_test $? 1 "IPv4 - no linkdown flag" log_test $? 1 "IPv4 - no linkdown flag"
ip -netns testns -6 route get fibmatch 2001:db8:1::1 | \ $IP -6 route get fibmatch 2001:db8:1::1 | \
grep -q "linkdown" grep -q "linkdown"
log_test $? 1 "IPv6 - no linkdown flag" log_test $? 1 "IPv6 - no linkdown flag"
set -e set -e
ip -netns testns link set dev dummy0 carrier off $IP link set dev dummy0 carrier off
sleep 1 sleep 1
set +e set +e
echo " Carrier off on nexthop" echo " Carrier off on nexthop"
ip -netns testns route get fibmatch 198.51.100.1 &> /dev/null $IP route get fibmatch 198.51.100.1 &> /dev/null
log_test $? 0 "IPv4 fibmatch" log_test $? 0 "IPv4 fibmatch"
ip -netns testns -6 route get fibmatch 2001:db8:1::1 &> /dev/null $IP -6 route get fibmatch 2001:db8:1::1 &> /dev/null
log_test $? 0 "IPv6 fibmatch" log_test $? 0 "IPv6 fibmatch"
ip -netns testns route get fibmatch 198.51.100.1 | \ $IP route get fibmatch 198.51.100.1 | \
grep -q "linkdown" grep -q "linkdown"
log_test $? 1 "IPv4 - linkdown flag set" log_test $? 1 "IPv4 - linkdown flag set"
ip -netns testns -6 route get fibmatch 2001:db8:1::1 | \ $IP -6 route get fibmatch 2001:db8:1::1 | \
grep -q "linkdown" grep -q "linkdown"
log_test $? 1 "IPv6 - linkdown flag set" log_test $? 1 "IPv6 - linkdown flag set"
set -e set -e
ip -netns testns address add 192.0.2.1/24 dev dummy0 $IP address add 192.0.2.1/24 dev dummy0
ip -netns testns -6 address add 2001:db8:2::1/64 dev dummy0 $IP -6 address add 2001:db8:2::1/64 dev dummy0
set +e set +e
echo " Route to local address with carrier down" echo " Route to local address with carrier down"
ip -netns testns route get fibmatch 192.0.2.1 &> /dev/null $IP route get fibmatch 192.0.2.1 &> /dev/null
log_test $? 0 "IPv4 fibmatch" log_test $? 0 "IPv4 fibmatch"
ip -netns testns -6 route get fibmatch 2001:db8:2::1 &> /dev/null $IP -6 route get fibmatch 2001:db8:2::1 &> /dev/null
log_test $? 0 "IPv6 fibmatch" log_test $? 0 "IPv6 fibmatch"
ip -netns testns route get fibmatch 192.0.2.1 | \ $IP route get fibmatch 192.0.2.1 | \
grep -q "linkdown" grep -q "linkdown"
log_test $? 1 "IPv4 linkdown flag set" log_test $? 1 "IPv4 linkdown flag set"
ip -netns testns -6 route get fibmatch 2001:db8:2::1 | \ $IP -6 route get fibmatch 2001:db8:2::1 | \
grep -q "linkdown" grep -q "linkdown"
log_test $? 1 "IPv6 linkdown flag set" log_test $? 1 "IPv6 linkdown flag set"
@ -329,54 +353,54 @@ fib_carrier_unicast_test()
setup setup
set -e set -e
ip -netns testns link set dev dummy0 carrier on $IP link set dev dummy0 carrier on
set +e set +e
echo " Start point" echo " Start point"
ip -netns testns route get fibmatch 198.51.100.2 &> /dev/null $IP route get fibmatch 198.51.100.2 &> /dev/null
log_test $? 0 "IPv4 fibmatch" log_test $? 0 "IPv4 fibmatch"
ip -netns testns -6 route get fibmatch 2001:db8:1::2 &> /dev/null $IP -6 route get fibmatch 2001:db8:1::2 &> /dev/null
log_test $? 0 "IPv6 fibmatch" log_test $? 0 "IPv6 fibmatch"
ip -netns testns route get fibmatch 198.51.100.2 | \ $IP route get fibmatch 198.51.100.2 | \
grep -q "linkdown" grep -q "linkdown"
log_test $? 1 "IPv4 no linkdown flag" log_test $? 1 "IPv4 no linkdown flag"
ip -netns testns -6 route get fibmatch 2001:db8:1::2 | \ $IP -6 route get fibmatch 2001:db8:1::2 | \
grep -q "linkdown" grep -q "linkdown"
log_test $? 1 "IPv6 no linkdown flag" log_test $? 1 "IPv6 no linkdown flag"
set -e set -e
ip -netns testns link set dev dummy0 carrier off $IP link set dev dummy0 carrier off
set +e set +e
echo " Carrier down" echo " Carrier down"
ip -netns testns route get fibmatch 198.51.100.2 &> /dev/null $IP route get fibmatch 198.51.100.2 &> /dev/null
log_test $? 0 "IPv4 fibmatch" log_test $? 0 "IPv4 fibmatch"
ip -netns testns -6 route get fibmatch 2001:db8:1::2 &> /dev/null $IP -6 route get fibmatch 2001:db8:1::2 &> /dev/null
log_test $? 0 "IPv6 fibmatch" log_test $? 0 "IPv6 fibmatch"
ip -netns testns route get fibmatch 198.51.100.2 | \ $IP route get fibmatch 198.51.100.2 | \
grep -q "linkdown" grep -q "linkdown"
log_test $? 0 "IPv4 linkdown flag set" log_test $? 0 "IPv4 linkdown flag set"
ip -netns testns -6 route get fibmatch 2001:db8:1::2 | \ $IP -6 route get fibmatch 2001:db8:1::2 | \
grep -q "linkdown" grep -q "linkdown"
log_test $? 0 "IPv6 linkdown flag set" log_test $? 0 "IPv6 linkdown flag set"
set -e set -e
ip -netns testns address add 192.0.2.1/24 dev dummy0 $IP address add 192.0.2.1/24 dev dummy0
ip -netns testns -6 address add 2001:db8:2::1/64 dev dummy0 $IP -6 address add 2001:db8:2::1/64 dev dummy0
set +e set +e
echo " Second address added with carrier down" echo " Second address added with carrier down"
ip -netns testns route get fibmatch 192.0.2.2 &> /dev/null $IP route get fibmatch 192.0.2.2 &> /dev/null
log_test $? 0 "IPv4 fibmatch" log_test $? 0 "IPv4 fibmatch"
ip -netns testns -6 route get fibmatch 2001:db8:2::2 &> /dev/null $IP -6 route get fibmatch 2001:db8:2::2 &> /dev/null
log_test $? 0 "IPv6 fibmatch" log_test $? 0 "IPv6 fibmatch"
ip -netns testns route get fibmatch 192.0.2.2 | \ $IP route get fibmatch 192.0.2.2 | \
grep -q "linkdown" grep -q "linkdown"
log_test $? 0 "IPv4 linkdown flag set" log_test $? 0 "IPv4 linkdown flag set"
ip -netns testns -6 route get fibmatch 2001:db8:2::2 | \ $IP -6 route get fibmatch 2001:db8:2::2 | \
grep -q "linkdown" grep -q "linkdown"
log_test $? 0 "IPv6 linkdown flag set" log_test $? 0 "IPv6 linkdown flag set"
@ -389,11 +413,168 @@ fib_carrier_test()
fib_carrier_unicast_test fib_carrier_unicast_test
} }
################################################################################
# Tests on nexthop spec
# run 'ip route add' with given spec
add_rt()
{
local desc="$1"
local erc=$2
local vrf=$3
local pfx=$4
local gw=$5
local dev=$6
local cmd out rc
[ "$vrf" = "-" ] && vrf="default"
[ -n "$gw" ] && gw="via $gw"
[ -n "$dev" ] && dev="dev $dev"
cmd="$IP route add vrf $vrf $pfx $gw $dev"
if [ "$VERBOSE" = "1" ]; then
printf "\n COMMAND: $cmd\n"
fi
out=$(eval $cmd 2>&1)
rc=$?
if [ "$VERBOSE" = "1" -a -n "$out" ]; then
echo " $out"
fi
log_test $rc $erc "$desc"
}
fib4_nexthop()
{
echo
echo "IPv4 nexthop tests"
echo "<<< write me >>>"
}
fib6_nexthop()
{
local lldummy=$(get_linklocal dummy0)
local llv1=$(get_linklocal dummy0)
if [ -z "$lldummy" ]; then
echo "Failed to get linklocal address for dummy0"
return 1
fi
if [ -z "$llv1" ]; then
echo "Failed to get linklocal address for veth1"
return 1
fi
echo
echo "IPv6 nexthop tests"
add_rt "Directly connected nexthop, unicast address" 0 \
- 2001:db8:101::/64 2001:db8:1::2
add_rt "Directly connected nexthop, unicast address with device" 0 \
- 2001:db8:102::/64 2001:db8:1::2 "dummy0"
add_rt "Gateway is linklocal address" 0 \
- 2001:db8:103::1/64 $llv1 "veth0"
# fails because LL address requires a device
add_rt "Gateway is linklocal address, no device" 2 \
- 2001:db8:104::1/64 $llv1
# local address can not be a gateway
add_rt "Gateway can not be local unicast address" 2 \
- 2001:db8:105::/64 2001:db8:1::1
add_rt "Gateway can not be local unicast address, with device" 2 \
- 2001:db8:106::/64 2001:db8:1::1 "dummy0"
add_rt "Gateway can not be a local linklocal address" 2 \
- 2001:db8:107::1/64 $lldummy "dummy0"
# VRF tests
add_rt "Gateway can be local address in a VRF" 0 \
- 2001:db8:108::/64 2001:db8:51::2
add_rt "Gateway can be local address in a VRF, with device" 0 \
- 2001:db8:109::/64 2001:db8:51::2 "veth0"
add_rt "Gateway can be local linklocal address in a VRF" 0 \
- 2001:db8:110::1/64 $llv1 "veth0"
add_rt "Redirect to VRF lookup" 0 \
- 2001:db8:111::/64 "" "red"
add_rt "VRF route, gateway can be local address in default VRF" 0 \
red 2001:db8:112::/64 2001:db8:51::1
# local address in same VRF fails
add_rt "VRF route, gateway can not be a local address" 2 \
red 2001:db8:113::1/64 2001:db8:2::1
add_rt "VRF route, gateway can not be a local addr with device" 2 \
red 2001:db8:114::1/64 2001:db8:2::1 "dummy1"
}
# Default VRF:
# dummy0 - 198.51.100.1/24 2001:db8:1::1/64
# veth0 - 192.0.2.1/24 2001:db8:51::1/64
#
# VRF red:
# dummy1 - 192.168.2.1/24 2001:db8:2::1/64
# veth1 - 192.0.2.2/24 2001:db8:51::2/64
#
# [ dummy0 veth0 ]--[ veth1 dummy1 ]
fib_nexthop_test()
{
setup
set -e
$IP -4 rule add pref 32765 table local
$IP -4 rule del pref 0
$IP -6 rule add pref 32765 table local
$IP -6 rule del pref 0
$IP link add red type vrf table 1
$IP link set red up
$IP -4 route add vrf red unreachable default metric 4278198272
$IP -6 route add vrf red unreachable default metric 4278198272
$IP link add veth0 type veth peer name veth1
$IP link set dev veth0 up
$IP address add 192.0.2.1/24 dev veth0
$IP -6 address add 2001:db8:51::1/64 dev veth0
$IP link set dev veth1 vrf red up
$IP address add 192.0.2.2/24 dev veth1
$IP -6 address add 2001:db8:51::2/64 dev veth1
$IP link add dummy1 type dummy
$IP link set dev dummy1 vrf red up
$IP address add 192.168.2.1/24 dev dummy1
$IP -6 address add 2001:db8:2::1/64 dev dummy1
set +e
sleep 1
fib4_nexthop
fib6_nexthop
(
$IP link del dev dummy1
$IP link del veth0
$IP link del red
) 2>/dev/null
cleanup
}
################################################################################
#
fib_test() fib_test()
{ {
fib_unreg_test if [ -n "$TEST" ]; then
fib_down_test eval $TEST
fib_carrier_test else
fib_unreg_test
fib_down_test
fib_carrier_test
fib_nexthop_test
fi
} }
if [ "$(id -u)" -ne 0 ];then if [ "$(id -u)" -ne 0 ];then