diff options
author | stephen hemminger <shemminger@vyatta.com> | 2010-03-02 08:32:45 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-03-04 03:39:33 -0500 |
commit | 5b2a19539c5f59c5a038d213ede723f0245d97cf (patch) | |
tree | 254bd6d2a4e574337c821c00a7ba10d024e52439 /net | |
parent | 122e4519cd5c224d4b8e681d368132b643e28f60 (diff) |
IPv6: addrconf timer race
The Router Solicitation timer races with device state changes
because it doesn't lock the device. Use local variable to avoid
one repeated dereference.
Signed-off-by: Stephen Hemminger <shemminger@vyatta.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv6/addrconf.c | 28 |
1 files changed, 15 insertions, 13 deletions
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index e6cba9c45c6c..5f582f385abb 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c | |||
@@ -2739,28 +2739,29 @@ static int addrconf_ifdown(struct net_device *dev, int how) | |||
2739 | static void addrconf_rs_timer(unsigned long data) | 2739 | static void addrconf_rs_timer(unsigned long data) |
2740 | { | 2740 | { |
2741 | struct inet6_ifaddr *ifp = (struct inet6_ifaddr *) data; | 2741 | struct inet6_ifaddr *ifp = (struct inet6_ifaddr *) data; |
2742 | struct inet6_dev *idev = ifp->idev; | ||
2742 | 2743 | ||
2743 | if (ifp->idev->cnf.forwarding) | 2744 | read_lock(&idev->lock); |
2745 | if (idev->dead || !(idev->if_flags & IF_READY)) | ||
2744 | goto out; | 2746 | goto out; |
2745 | 2747 | ||
2746 | if (ifp->idev->if_flags & IF_RA_RCVD) { | 2748 | if (idev->cnf.forwarding) |
2747 | /* | 2749 | goto out; |
2748 | * Announcement received after solicitation | 2750 | |
2749 | * was sent | 2751 | /* Announcement received after solicitation was sent */ |
2750 | */ | 2752 | if (idev->if_flags & IF_RA_RCVD) |
2751 | goto out; | 2753 | goto out; |
2752 | } | ||
2753 | 2754 | ||
2754 | spin_lock(&ifp->lock); | 2755 | spin_lock(&ifp->lock); |
2755 | if (ifp->probes++ < ifp->idev->cnf.rtr_solicits) { | 2756 | if (ifp->probes++ < idev->cnf.rtr_solicits) { |
2756 | /* The wait after the last probe can be shorter */ | 2757 | /* The wait after the last probe can be shorter */ |
2757 | addrconf_mod_timer(ifp, AC_RS, | 2758 | addrconf_mod_timer(ifp, AC_RS, |
2758 | (ifp->probes == ifp->idev->cnf.rtr_solicits) ? | 2759 | (ifp->probes == idev->cnf.rtr_solicits) ? |
2759 | ifp->idev->cnf.rtr_solicit_delay : | 2760 | idev->cnf.rtr_solicit_delay : |
2760 | ifp->idev->cnf.rtr_solicit_interval); | 2761 | idev->cnf.rtr_solicit_interval); |
2761 | spin_unlock(&ifp->lock); | 2762 | spin_unlock(&ifp->lock); |
2762 | 2763 | ||
2763 | ndisc_send_rs(ifp->idev->dev, &ifp->addr, &in6addr_linklocal_allrouters); | 2764 | ndisc_send_rs(idev->dev, &ifp->addr, &in6addr_linklocal_allrouters); |
2764 | } else { | 2765 | } else { |
2765 | spin_unlock(&ifp->lock); | 2766 | spin_unlock(&ifp->lock); |
2766 | /* | 2767 | /* |
@@ -2768,10 +2769,11 @@ static void addrconf_rs_timer(unsigned long data) | |||
2768 | * assumption any longer. | 2769 | * assumption any longer. |
2769 | */ | 2770 | */ |
2770 | printk(KERN_DEBUG "%s: no IPv6 routers present\n", | 2771 | printk(KERN_DEBUG "%s: no IPv6 routers present\n", |
2771 | ifp->idev->dev->name); | 2772 | idev->dev->name); |
2772 | } | 2773 | } |
2773 | 2774 | ||
2774 | out: | 2775 | out: |
2776 | read_unlock(&idev->lock); | ||
2775 | in6_ifa_put(ifp); | 2777 | in6_ifa_put(ifp); |
2776 | } | 2778 | } |
2777 | 2779 | ||