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