diff options
Diffstat (limited to 'net/ipv6/addrconf.c')
-rw-r--r-- | net/ipv6/addrconf.c | 104 |
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) | |||
2615 | static int addrconf_ifdown(struct net_device *dev, int how) | 2617 | static 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) | |||
2739 | static void addrconf_rs_timer(unsigned long data) | 2761 | static 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 | ||
2774 | out: | 2797 | out: |
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); |