net: ipv6: Keep nexthop of multipath route on admin down
authorDavid Ahern <dsa@cumulusnetworks.com>
Wed, 18 Jan 2017 15:40:36 +0000 (07:40 -0800)
committerDavid S. Miller <davem@davemloft.net>
Fri, 20 Jan 2017 04:38:51 +0000 (23:38 -0500)
IPv6 deletes route entries associated with multipath routes on an
admin down where IPv4 does not. For example:
    $ ip ro ls vrf red
    unreachable default metric 8192
    1.1.1.0/24 metric 64
            nexthop via 10.100.1.254  dev eth1 weight 1
            nexthop via 10.100.2.254  dev eth2 weight 1
    10.100.1.0/24 dev eth1 proto kernel scope link src 10.100.1.4
    10.100.2.0/24 dev eth2 proto kernel scope link src 10.100.2.4

    $ ip -6 ro ls vrf red
    2001:db8:1::/120 dev eth1 proto kernel metric 256  pref medium
    2001:db8:2:: dev red proto none metric 0  pref medium
    2001:db8:2::/120 dev eth2 proto kernel metric 256  pref medium
    2001:db8:11::/120 via 2001:db8:1::16 dev eth1 metric 1024  pref medium
    2001:db8:11::/120 via 2001:db8:2::17 dev eth2 metric 1024  pref medium
    ...

Set link down:
    $ ip li set eth1 down

IPv4 retains the multihop route but flags eth1 route as dead:

    $ ip ro ls vrf red
    unreachable default metric 8192
    1.1.1.0/24
            nexthop via 10.100.1.16  dev eth1 weight 1 dead linkdown
            nexthop via 10.100.2.16  dev eth2 weight 1
    10.100.2.0/24 dev eth2 proto kernel scope link src 10.100.2.4

and IPv6 deletes the route as part of flushing all routes for the device:

    $ ip -6 ro ls vrf red
    2001:db8:2:: dev red proto none metric 0  pref medium
    2001:db8:2::/120 dev eth2 proto kernel metric 256  pref medium
    2001:db8:11::/120 via 2001:db8:2::17 dev eth2 metric 1024  pref medium
    ...

Worse, on admin up of the device the multipath route has to be deleted
to get this leg of the route re-added.

This patch keeps routes that are part of a multipath route if
ignore_routes_with_linkdown is set with the dead and linkdown flags
enabling consistency between IPv4 and IPv6:

    $ ip -6 ro ls vrf red
    2001:db8:2:: dev red proto none metric 0  pref medium
    2001:db8:2::/120 dev eth2 proto kernel metric 256  pref medium
    2001:db8:11::/120 via 2001:db8:1::16 dev eth1 metric 1024 dead linkdown  pref medium
    2001:db8:11::/120 via 2001:db8:2::17 dev eth2 metric 1024  pref medium
    ...

Signed-off-by: David Ahern <dsa@cumulusnetworks.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/ipv6/route.c

index 5585c501a5403a6955dd484cdf074f2e673c935c..4b1f0f98a0e92d1078c8c4f467d482ed60e32c9d 100644 (file)
@@ -2711,13 +2711,16 @@ struct arg_dev_net {
        struct net *net;
 };
 
+/* called with write lock held for table with rt */
 static int fib6_ifdown(struct rt6_info *rt, void *arg)
 {
        const struct arg_dev_net *adn = arg;
        const struct net_device *dev = adn->dev;
 
        if ((rt->dst.dev == dev || !dev) &&
-           rt != adn->net->ipv6.ip6_null_entry)
+           rt != adn->net->ipv6.ip6_null_entry &&
+           (rt->rt6i_nsiblings == 0 ||
+            !rt->rt6i_idev->cnf.ignore_routes_with_linkdown))
                return -1;
 
        return 0;
@@ -3216,7 +3219,7 @@ static int rt6_fill_node(struct net *net,
        else
                rtm->rtm_type = RTN_UNICAST;
        rtm->rtm_flags = 0;
-       if (!netif_carrier_ok(rt->dst.dev)) {
+       if (!netif_running(rt->dst.dev) || !netif_carrier_ok(rt->dst.dev)) {
                rtm->rtm_flags |= RTNH_F_LINKDOWN;
                if (rt->rt6i_idev->cnf.ignore_routes_with_linkdown)
                        rtm->rtm_flags |= RTNH_F_DEAD;