aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Ahern <dsa@cumulusnetworks.com>2016-04-21 23:56:12 -0400
committerDavid S. Miller <davem@davemloft.net>2016-04-26 11:48:26 -0400
commit38bd10c447f8e8980753149a8a65108159871df5 (patch)
treee9000b56af5d8fcaf89ac54c12f6225bcfb46661
parent6a923934c33c750a595868af6bef5f1a1fa90054 (diff)
net: ipv6: Delete host routes on an ifdown
It was a simple idea -- save IPv6 configured addresses on a link down so that IPv6 behaves similar to IPv4. As always the devil is in the details and the IPv6 stack as too many behavioral differences from IPv4 making the simple idea more complicated than it needs to be. The current implementation for keeping IPv6 addresses can panic or spit out a warning in one of many paths: 1. IPv6 route gets an IPv4 route as its 'next' which causes a panic in rt6_fill_node while handling a route dump request. 2. rt->dst.obsolete is set to DST_OBSOLETE_DEAD hitting the WARN_ON in fib6_del 3. Panic in fib6_purge_rt because rt6i_ref count is not 1. The root cause of all these is references related to the host route for an address that is retained. So, this patch deletes the host route every time the ifdown loop runs. Since the host route is deleted and will be re-generated an up there is no longer a need for the l3mdev fix up. On the 'admin up' side move addrconf_permanent_addr into the NETDEV_UP event handling so that it runs only once versus on UP and CHANGE events. All of the current panics and warnings appear to be related to addresses on the loopback device, but given the catastrophic nature when a bug is triggered this patch takes the conservative approach and evicts all host routes rather than trying to determine when it can be re-used and when it can not. That can be a later optimizaton if desired. Signed-off-by: David Ahern <dsa@cumulusnetworks.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--net/ipv6/addrconf.c48
1 files changed, 15 insertions, 33 deletions
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 23cec53b568a..8ec4b3089e20 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -3176,35 +3176,9 @@ static void addrconf_gre_config(struct net_device *dev)
3176} 3176}
3177#endif 3177#endif
3178 3178
3179#if IS_ENABLED(CONFIG_NET_L3_MASTER_DEV)
3180/* If the host route is cached on the addr struct make sure it is associated
3181 * with the proper table. e.g., enslavement can change and if so the cached
3182 * host route needs to move to the new table.
3183 */
3184static void l3mdev_check_host_rt(struct inet6_dev *idev,
3185 struct inet6_ifaddr *ifp)
3186{
3187 if (ifp->rt) {
3188 u32 tb_id = l3mdev_fib_table(idev->dev) ? : RT6_TABLE_LOCAL;
3189
3190 if (tb_id != ifp->rt->rt6i_table->tb6_id) {
3191 ip6_del_rt(ifp->rt);
3192 ifp->rt = NULL;
3193 }
3194 }
3195}
3196#else
3197static void l3mdev_check_host_rt(struct inet6_dev *idev,
3198 struct inet6_ifaddr *ifp)
3199{
3200}
3201#endif
3202
3203static int fixup_permanent_addr(struct inet6_dev *idev, 3179static int fixup_permanent_addr(struct inet6_dev *idev,
3204 struct inet6_ifaddr *ifp) 3180 struct inet6_ifaddr *ifp)
3205{ 3181{
3206 l3mdev_check_host_rt(idev, ifp);
3207
3208 if (!ifp->rt) { 3182 if (!ifp->rt) {
3209 struct rt6_info *rt; 3183 struct rt6_info *rt;
3210 3184
@@ -3304,6 +3278,9 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
3304 break; 3278 break;
3305 3279
3306 if (event == NETDEV_UP) { 3280 if (event == NETDEV_UP) {
3281 /* restore routes for permanent addresses */
3282 addrconf_permanent_addr(dev);
3283
3307 if (!addrconf_qdisc_ok(dev)) { 3284 if (!addrconf_qdisc_ok(dev)) {
3308 /* device is not ready yet. */ 3285 /* device is not ready yet. */
3309 pr_info("ADDRCONF(NETDEV_UP): %s: link is not ready\n", 3286 pr_info("ADDRCONF(NETDEV_UP): %s: link is not ready\n",
@@ -3337,9 +3314,6 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
3337 run_pending = 1; 3314 run_pending = 1;
3338 } 3315 }
3339 3316
3340 /* restore routes for permanent addresses */
3341 addrconf_permanent_addr(dev);
3342
3343 switch (dev->type) { 3317 switch (dev->type) {
3344#if IS_ENABLED(CONFIG_IPV6_SIT) 3318#if IS_ENABLED(CONFIG_IPV6_SIT)
3345 case ARPHRD_SIT: 3319 case ARPHRD_SIT:
@@ -3556,6 +3530,8 @@ restart:
3556 3530
3557 INIT_LIST_HEAD(&del_list); 3531 INIT_LIST_HEAD(&del_list);
3558 list_for_each_entry_safe(ifa, tmp, &idev->addr_list, if_list) { 3532 list_for_each_entry_safe(ifa, tmp, &idev->addr_list, if_list) {
3533 struct rt6_info *rt = NULL;
3534
3559 addrconf_del_dad_work(ifa); 3535 addrconf_del_dad_work(ifa);
3560 3536
3561 write_unlock_bh(&idev->lock); 3537 write_unlock_bh(&idev->lock);
@@ -3568,6 +3544,9 @@ restart:
3568 ifa->state = 0; 3544 ifa->state = 0;
3569 if (!(ifa->flags & IFA_F_NODAD)) 3545 if (!(ifa->flags & IFA_F_NODAD))
3570 ifa->flags |= IFA_F_TENTATIVE; 3546 ifa->flags |= IFA_F_TENTATIVE;
3547
3548 rt = ifa->rt;
3549 ifa->rt = NULL;
3571 } else { 3550 } else {
3572 state = ifa->state; 3551 state = ifa->state;
3573 ifa->state = INET6_IFADDR_STATE_DEAD; 3552 ifa->state = INET6_IFADDR_STATE_DEAD;
@@ -3578,6 +3557,9 @@ restart:
3578 3557
3579 spin_unlock_bh(&ifa->lock); 3558 spin_unlock_bh(&ifa->lock);
3580 3559
3560 if (rt)
3561 ip6_del_rt(rt);
3562
3581 if (state != INET6_IFADDR_STATE_DEAD) { 3563 if (state != INET6_IFADDR_STATE_DEAD) {
3582 __ipv6_ifa_notify(RTM_DELADDR, ifa); 3564 __ipv6_ifa_notify(RTM_DELADDR, ifa);
3583 inet6addr_notifier_call_chain(NETDEV_DOWN, ifa); 3565 inet6addr_notifier_call_chain(NETDEV_DOWN, ifa);
@@ -5343,10 +5325,10 @@ static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
5343 if (rt) 5325 if (rt)
5344 ip6_del_rt(rt); 5326 ip6_del_rt(rt);
5345 } 5327 }
5346 dst_hold(&ifp->rt->dst); 5328 if (ifp->rt) {
5347 5329 dst_hold(&ifp->rt->dst);
5348 ip6_del_rt(ifp->rt); 5330 ip6_del_rt(ifp->rt);
5349 5331 }
5350 rt_genid_bump_ipv6(net); 5332 rt_genid_bump_ipv6(net);
5351 break; 5333 break;
5352 } 5334 }