aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/addrconf.c
diff options
context:
space:
mode:
authorstephen hemminger <shemminger@vyatta.com>2010-03-02 08:32:45 -0500
committerDavid S. Miller <davem@davemloft.net>2010-03-04 03:39:33 -0500
commit5b2a19539c5f59c5a038d213ede723f0245d97cf (patch)
tree254bd6d2a4e574337c821c00a7ba10d024e52439 /net/ipv6/addrconf.c
parent122e4519cd5c224d4b8e681d368132b643e28f60 (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/ipv6/addrconf.c')
-rw-r--r--net/ipv6/addrconf.c28
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)
2739static void addrconf_rs_timer(unsigned long data) 2739static 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
2774out: 2775out:
2776 read_unlock(&idev->lock);
2775 in6_ifa_put(ifp); 2777 in6_ifa_put(ifp);
2776} 2778}
2777 2779