aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/addrconf.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6/addrconf.c')
-rw-r--r--net/ipv6/addrconf.c104
1 files changed, 64 insertions, 40 deletions
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 88fd8c5877ee..3381b4317c27 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -1380,6 +1380,8 @@ static void addrconf_dad_stop(struct inet6_ifaddr *ifp, int dad_failed)
1380 if (dad_failed) 1380 if (dad_failed)
1381 ifp->flags |= IFA_F_DADFAILED; 1381 ifp->flags |= IFA_F_DADFAILED;
1382 spin_unlock_bh(&ifp->lock); 1382 spin_unlock_bh(&ifp->lock);
1383 if (dad_failed)
1384 ipv6_ifa_notify(0, ifp);
1383 in6_ifa_put(ifp); 1385 in6_ifa_put(ifp);
1384#ifdef CONFIG_IPV6_PRIVACY 1386#ifdef CONFIG_IPV6_PRIVACY
1385 } else if (ifp->flags&IFA_F_TEMPORARY) { 1387 } else if (ifp->flags&IFA_F_TEMPORARY) {
@@ -2615,7 +2617,7 @@ static void addrconf_bonding_change(struct net_device *dev, unsigned long event)
2615static int addrconf_ifdown(struct net_device *dev, int how) 2617static int addrconf_ifdown(struct net_device *dev, int how)
2616{ 2618{
2617 struct inet6_dev *idev; 2619 struct inet6_dev *idev;
2618 struct inet6_ifaddr *ifa, **bifa; 2620 struct inet6_ifaddr *ifa, *keep_list, **bifa;
2619 struct net *net = dev_net(dev); 2621 struct net *net = dev_net(dev);
2620 int i; 2622 int i;
2621 2623
@@ -2649,11 +2651,11 @@ static int addrconf_ifdown(struct net_device *dev, int how)
2649 write_lock_bh(&addrconf_hash_lock); 2651 write_lock_bh(&addrconf_hash_lock);
2650 while ((ifa = *bifa) != NULL) { 2652 while ((ifa = *bifa) != NULL) {
2651 if (ifa->idev == idev && 2653 if (ifa->idev == idev &&
2652 (how || !(ifa->flags&IFA_F_PERMANENT))) { 2654 (how || !(ifa->flags&IFA_F_PERMANENT) ||
2655 ipv6_addr_type(&ifa->addr) & IPV6_ADDR_LINKLOCAL)) {
2653 *bifa = ifa->lst_next; 2656 *bifa = ifa->lst_next;
2654 ifa->lst_next = NULL; 2657 ifa->lst_next = NULL;
2655 addrconf_del_timer(ifa); 2658 __in6_ifa_put(ifa);
2656 in6_ifa_put(ifa);
2657 continue; 2659 continue;
2658 } 2660 }
2659 bifa = &ifa->lst_next; 2661 bifa = &ifa->lst_next;
@@ -2689,31 +2691,51 @@ static int addrconf_ifdown(struct net_device *dev, int how)
2689 write_lock_bh(&idev->lock); 2691 write_lock_bh(&idev->lock);
2690 } 2692 }
2691#endif 2693#endif
2692 bifa = &idev->addr_list; 2694 keep_list = NULL;
2693 while ((ifa = *bifa) != NULL) { 2695 bifa = &keep_list;
2694 if (how == 0 && (ifa->flags&IFA_F_PERMANENT)) { 2696 while ((ifa = idev->addr_list) != NULL) {
2695 /* Retain permanent address on admin down */ 2697 idev->addr_list = ifa->if_next;
2698 ifa->if_next = NULL;
2699
2700 addrconf_del_timer(ifa);
2701
2702 /* If just doing link down, and address is permanent
2703 and not link-local, then retain it. */
2704 if (how == 0 &&
2705 (ifa->flags&IFA_F_PERMANENT) &&
2706 !(ipv6_addr_type(&ifa->addr) & IPV6_ADDR_LINKLOCAL)) {
2707
2708 /* Move to holding list */
2709 *bifa = ifa;
2696 bifa = &ifa->if_next; 2710 bifa = &ifa->if_next;
2697 2711
2698 /* Restart DAD if needed when link comes back up */ 2712 /* If not doing DAD on this address, just keep it. */
2699 if ( !((dev->flags&(IFF_NOARP|IFF_LOOPBACK)) || 2713 if ((dev->flags&(IFF_NOARP|IFF_LOOPBACK)) ||
2700 idev->cnf.accept_dad <= 0 || 2714 idev->cnf.accept_dad <= 0 ||
2701 (ifa->flags & IFA_F_NODAD))) 2715 (ifa->flags & IFA_F_NODAD))
2702 ifa->flags |= IFA_F_TENTATIVE; 2716 continue;
2703 } else {
2704 *bifa = ifa->if_next;
2705 ifa->if_next = NULL;
2706 2717
2718 /* If it was tentative already, no need to notify */
2719 if (ifa->flags & IFA_F_TENTATIVE)
2720 continue;
2721
2722 /* Flag it for later restoration when link comes up */
2723 ifa->flags |= IFA_F_TENTATIVE;
2724 in6_ifa_hold(ifa);
2725 } else {
2707 ifa->dead = 1; 2726 ifa->dead = 1;
2708 write_unlock_bh(&idev->lock); 2727 }
2728 write_unlock_bh(&idev->lock);
2709 2729
2710 __ipv6_ifa_notify(RTM_DELADDR, ifa); 2730 __ipv6_ifa_notify(RTM_DELADDR, ifa);
2711 atomic_notifier_call_chain(&inet6addr_chain, NETDEV_DOWN, ifa); 2731 atomic_notifier_call_chain(&inet6addr_chain, NETDEV_DOWN, ifa);
2712 in6_ifa_put(ifa); 2732 in6_ifa_put(ifa);
2713 2733
2714 write_lock_bh(&idev->lock); 2734 write_lock_bh(&idev->lock);
2715 }
2716 } 2735 }
2736
2737 idev->addr_list = keep_list;
2738
2717 write_unlock_bh(&idev->lock); 2739 write_unlock_bh(&idev->lock);
2718 2740
2719 /* Step 5: Discard multicast list */ 2741 /* Step 5: Discard multicast list */
@@ -2739,28 +2761,29 @@ static int addrconf_ifdown(struct net_device *dev, int how)
2739static void addrconf_rs_timer(unsigned long data) 2761static void addrconf_rs_timer(unsigned long data)
2740{ 2762{
2741 struct inet6_ifaddr *ifp = (struct inet6_ifaddr *) data; 2763 struct inet6_ifaddr *ifp = (struct inet6_ifaddr *) data;
2764 struct inet6_dev *idev = ifp->idev;
2742 2765
2743 if (ifp->idev->cnf.forwarding) 2766 read_lock(&idev->lock);
2767 if (idev->dead || !(idev->if_flags & IF_READY))
2744 goto out; 2768 goto out;
2745 2769
2746 if (ifp->idev->if_flags & IF_RA_RCVD) { 2770 if (idev->cnf.forwarding)
2747 /* 2771 goto out;
2748 * Announcement received after solicitation 2772
2749 * was sent 2773 /* Announcement received after solicitation was sent */
2750 */ 2774 if (idev->if_flags & IF_RA_RCVD)
2751 goto out; 2775 goto out;
2752 }
2753 2776
2754 spin_lock(&ifp->lock); 2777 spin_lock(&ifp->lock);
2755 if (ifp->probes++ < ifp->idev->cnf.rtr_solicits) { 2778 if (ifp->probes++ < idev->cnf.rtr_solicits) {
2756 /* The wait after the last probe can be shorter */ 2779 /* The wait after the last probe can be shorter */
2757 addrconf_mod_timer(ifp, AC_RS, 2780 addrconf_mod_timer(ifp, AC_RS,
2758 (ifp->probes == ifp->idev->cnf.rtr_solicits) ? 2781 (ifp->probes == idev->cnf.rtr_solicits) ?
2759 ifp->idev->cnf.rtr_solicit_delay : 2782 idev->cnf.rtr_solicit_delay :
2760 ifp->idev->cnf.rtr_solicit_interval); 2783 idev->cnf.rtr_solicit_interval);
2761 spin_unlock(&ifp->lock); 2784 spin_unlock(&ifp->lock);
2762 2785
2763 ndisc_send_rs(ifp->idev->dev, &ifp->addr, &in6addr_linklocal_allrouters); 2786 ndisc_send_rs(idev->dev, &ifp->addr, &in6addr_linklocal_allrouters);
2764 } else { 2787 } else {
2765 spin_unlock(&ifp->lock); 2788 spin_unlock(&ifp->lock);
2766 /* 2789 /*
@@ -2768,10 +2791,11 @@ static void addrconf_rs_timer(unsigned long data)
2768 * assumption any longer. 2791 * assumption any longer.
2769 */ 2792 */
2770 printk(KERN_DEBUG "%s: no IPv6 routers present\n", 2793 printk(KERN_DEBUG "%s: no IPv6 routers present\n",
2771 ifp->idev->dev->name); 2794 idev->dev->name);
2772 } 2795 }
2773 2796
2774out: 2797out:
2798 read_unlock(&idev->lock);
2775 in6_ifa_put(ifp); 2799 in6_ifa_put(ifp);
2776} 2800}
2777 2801
@@ -2850,9 +2874,9 @@ static void addrconf_dad_timer(unsigned long data)
2850 struct inet6_dev *idev = ifp->idev; 2874 struct inet6_dev *idev = ifp->idev;
2851 struct in6_addr mcaddr; 2875 struct in6_addr mcaddr;
2852 2876
2853 read_lock_bh(&idev->lock); 2877 read_lock(&idev->lock);
2854 if (idev->dead) { 2878 if (idev->dead || !(idev->if_flags & IF_READY)) {
2855 read_unlock_bh(&idev->lock); 2879 read_unlock(&idev->lock);
2856 goto out; 2880 goto out;
2857 } 2881 }
2858 2882
@@ -2864,7 +2888,7 @@ static void addrconf_dad_timer(unsigned long data)
2864 2888
2865 ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC|IFA_F_DADFAILED); 2889 ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC|IFA_F_DADFAILED);
2866 spin_unlock(&ifp->lock); 2890 spin_unlock(&ifp->lock);
2867 read_unlock_bh(&idev->lock); 2891 read_unlock(&idev->lock);
2868 2892
2869 addrconf_dad_completed(ifp); 2893 addrconf_dad_completed(ifp);
2870 2894
@@ -2874,7 +2898,7 @@ static void addrconf_dad_timer(unsigned long data)
2874 ifp->probes--; 2898 ifp->probes--;
2875 addrconf_mod_timer(ifp, AC_DAD, ifp->idev->nd_parms->retrans_time); 2899 addrconf_mod_timer(ifp, AC_DAD, ifp->idev->nd_parms->retrans_time);
2876 spin_unlock(&ifp->lock); 2900 spin_unlock(&ifp->lock);
2877 read_unlock_bh(&idev->lock); 2901 read_unlock(&idev->lock);
2878 2902
2879 /* send a neighbour solicitation for our addr */ 2903 /* send a neighbour solicitation for our addr */
2880 addrconf_addr_solict_mult(&ifp->addr, &mcaddr); 2904 addrconf_addr_solict_mult(&ifp->addr, &mcaddr);