| From cac1789112679260f2298555c7546139ad82116f Mon Sep 17 00:00:00 2001 |
| From: WANG Cong <xiyou.wangcong@gmail.com> |
| Date: Mon, 8 May 2017 10:12:13 -0700 |
| Subject: [PATCH] ipv6: reorder ip6_route_dev_notifier after ipv6_dev_notf |
| |
| commit 242d3a49a2a1a71d8eb9f953db1bcaa9d698ce00 upstream. |
| |
| For each netns (except init_net), we initialize its null entry |
| in 3 places: |
| |
| 1) The template itself, as we use kmemdup() |
| 2) Code around dst_init_metrics() in ip6_route_net_init() |
| 3) ip6_route_dev_notify(), which is supposed to initialize it after |
| loopback registers |
| |
| Unfortunately the last one still happens in a wrong order because |
| we expect to initialize net->ipv6.ip6_null_entry->rt6i_idev to |
| net->loopback_dev's idev, thus we have to do that after we add |
| idev to loopback. However, this notifier has priority == 0 same as |
| ipv6_dev_notf, and ipv6_dev_notf is registered after |
| ip6_route_dev_notifier so it is called actually after |
| ip6_route_dev_notifier. This is similar to commit 2f460933f58e |
| ("ipv6: initialize route null entry in addrconf_init()") which |
| fixes init_net. |
| |
| Fix it by picking a smaller priority for ip6_route_dev_notifier. |
| Also, we have to release the refcnt accordingly when unregistering |
| loopback_dev because device exit functions are called before subsys |
| exit functions. |
| |
| Acked-by: David Ahern <dsahern@gmail.com> |
| Tested-by: David Ahern <dsahern@gmail.com> |
| Signed-off-by: Cong Wang <xiyou.wangcong@gmail.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/include/net/addrconf.h b/include/net/addrconf.h |
| index 9826d3a9464c..863d06e823a2 100644 |
| --- a/include/net/addrconf.h |
| +++ b/include/net/addrconf.h |
| @@ -19,6 +19,8 @@ |
| #define ADDRCONF_TIMER_FUZZ (HZ / 4) |
| #define ADDRCONF_TIMER_FUZZ_MAX (HZ) |
| |
| +#define ADDRCONF_NOTIFY_PRIORITY 0 |
| + |
| #include <linux/in.h> |
| #include <linux/in6.h> |
| |
| diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c |
| index fc01b9a4fdde..6fe120fc7851 100644 |
| --- a/net/ipv6/addrconf.c |
| +++ b/net/ipv6/addrconf.c |
| @@ -3487,6 +3487,7 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event, |
| */ |
| static struct notifier_block ipv6_dev_notf = { |
| .notifier_call = addrconf_notify, |
| + .priority = ADDRCONF_NOTIFY_PRIORITY, |
| }; |
| |
| static void addrconf_type_change(struct net_device *dev, unsigned long event) |
| diff --git a/net/ipv6/route.c b/net/ipv6/route.c |
| index 6c45a3e797a2..c0e536142ab7 100644 |
| --- a/net/ipv6/route.c |
| +++ b/net/ipv6/route.c |
| @@ -3439,7 +3439,10 @@ static int ip6_route_dev_notify(struct notifier_block *this, |
| struct net_device *dev = netdev_notifier_info_to_dev(ptr); |
| struct net *net = dev_net(dev); |
| |
| - if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) { |
| + if (!(dev->flags & IFF_LOOPBACK)) |
| + return NOTIFY_OK; |
| + |
| + if (event == NETDEV_REGISTER) { |
| net->ipv6.ip6_null_entry->dst.dev = dev; |
| net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev); |
| #ifdef CONFIG_IPV6_MULTIPLE_TABLES |
| @@ -3448,6 +3451,12 @@ static int ip6_route_dev_notify(struct notifier_block *this, |
| net->ipv6.ip6_blk_hole_entry->dst.dev = dev; |
| net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev); |
| #endif |
| + } else if (event == NETDEV_UNREGISTER) { |
| + in6_dev_put(net->ipv6.ip6_null_entry->rt6i_idev); |
| +#ifdef CONFIG_IPV6_MULTIPLE_TABLES |
| + in6_dev_put(net->ipv6.ip6_prohibit_entry->rt6i_idev); |
| + in6_dev_put(net->ipv6.ip6_blk_hole_entry->rt6i_idev); |
| +#endif |
| } |
| |
| return NOTIFY_OK; |
| @@ -3754,7 +3763,7 @@ static struct pernet_operations ip6_route_net_late_ops = { |
| |
| static struct notifier_block ip6_route_dev_notifier = { |
| .notifier_call = ip6_route_dev_notify, |
| - .priority = 0, |
| + .priority = ADDRCONF_NOTIFY_PRIORITY - 10, |
| }; |
| |
| void __init ip6_route_init_special_entries(void) |
| -- |
| 2.12.0 |
| |