| From 2c861cc65ef4604011a0082e4dcdba2819aa191a Mon Sep 17 00:00:00 2001 |
| From: =?UTF-8?q?Michal=20Kube=C4=8Dek?= <mkubecek@suse.cz> |
| Date: Mon, 9 Sep 2013 21:45:04 +0200 |
| Subject: ipv6: don't call fib6_run_gc() until routing is ready |
| |
| commit 2c861cc65ef4604011a0082e4dcdba2819aa191a upstream. |
| |
| When loading the ipv6 module, ndisc_init() is called before |
| ip6_route_init(). As the former registers a handler calling |
| fib6_run_gc(), this opens a window to run the garbage collector |
| before necessary data structures are initialized. If a network |
| device is initialized in this window, adding MAC address to it |
| triggers a NETDEV_CHANGEADDR event, leading to a crash in |
| fib6_clean_all(). |
| |
| Take the event handler registration out of ndisc_init() into a |
| separate function ndisc_late_init() and move it after |
| ip6_route_init(). |
| |
| Signed-off-by: Michal Kubecek <mkubecek@suse.cz> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Zefan Li <lizefan@huawei.com> |
| --- |
| include/net/ndisc.h | 2 ++ |
| net/ipv6/af_inet6.c | 6 ++++++ |
| net/ipv6/ndisc.c | 18 +++++++++++------- |
| 3 files changed, 19 insertions(+), 7 deletions(-) |
| |
| --- a/include/net/ndisc.h |
| +++ b/include/net/ndisc.h |
| @@ -117,7 +117,9 @@ static inline struct neighbour *__ipv6_n |
| } |
| |
| extern int ndisc_init(void); |
| +extern int ndisc_late_init(void); |
| |
| +extern void ndisc_late_cleanup(void); |
| extern void ndisc_cleanup(void); |
| |
| extern int ndisc_rcv(struct sk_buff *skb); |
| --- a/net/ipv6/af_inet6.c |
| +++ b/net/ipv6/af_inet6.c |
| @@ -1161,6 +1161,9 @@ static int __init inet6_init(void) |
| err = ip6_route_init(); |
| if (err) |
| goto ip6_route_fail; |
| + err = ndisc_late_init(); |
| + if (err) |
| + goto ndisc_late_fail; |
| err = ip6_flowlabel_init(); |
| if (err) |
| goto ip6_flowlabel_fail; |
| @@ -1221,6 +1224,8 @@ ipv6_exthdrs_fail: |
| addrconf_fail: |
| ip6_flowlabel_cleanup(); |
| ip6_flowlabel_fail: |
| + ndisc_late_cleanup(); |
| +ndisc_late_fail: |
| ip6_route_cleanup(); |
| ip6_route_fail: |
| #ifdef CONFIG_PROC_FS |
| @@ -1288,6 +1293,7 @@ static void __exit inet6_exit(void) |
| ipv6_exthdrs_exit(); |
| addrconf_cleanup(); |
| ip6_flowlabel_cleanup(); |
| + ndisc_late_cleanup(); |
| ip6_route_cleanup(); |
| #ifdef CONFIG_PROC_FS |
| |
| --- a/net/ipv6/ndisc.c |
| +++ b/net/ipv6/ndisc.c |
| @@ -1867,24 +1867,28 @@ int __init ndisc_init(void) |
| if (err) |
| goto out_unregister_pernet; |
| #endif |
| - err = register_netdevice_notifier(&ndisc_netdev_notifier); |
| - if (err) |
| - goto out_unregister_sysctl; |
| out: |
| return err; |
| |
| -out_unregister_sysctl: |
| #ifdef CONFIG_SYSCTL |
| - neigh_sysctl_unregister(&nd_tbl.parms); |
| out_unregister_pernet: |
| -#endif |
| unregister_pernet_subsys(&ndisc_net_ops); |
| goto out; |
| +#endif |
| } |
| |
| -void ndisc_cleanup(void) |
| +int __init ndisc_late_init(void) |
| +{ |
| + return register_netdevice_notifier(&ndisc_netdev_notifier); |
| +} |
| + |
| +void ndisc_late_cleanup(void) |
| { |
| unregister_netdevice_notifier(&ndisc_netdev_notifier); |
| +} |
| + |
| +void ndisc_cleanup(void) |
| +{ |
| #ifdef CONFIG_SYSCTL |
| neigh_sysctl_unregister(&nd_tbl.parms); |
| #endif |