diff options
Diffstat (limited to 'net/ipv6/addrconf.c')
| -rw-r--r-- | net/ipv6/addrconf.c | 110 |
1 files changed, 67 insertions, 43 deletions
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index de7a194a64ab..88fd8c5877ee 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c | |||
| @@ -278,31 +278,31 @@ static void addrconf_mod_timer(struct inet6_ifaddr *ifp, | |||
| 278 | 278 | ||
| 279 | static int snmp6_alloc_dev(struct inet6_dev *idev) | 279 | static int snmp6_alloc_dev(struct inet6_dev *idev) |
| 280 | { | 280 | { |
| 281 | if (snmp_mib_init((void **)idev->stats.ipv6, | 281 | if (snmp_mib_init((void __percpu **)idev->stats.ipv6, |
| 282 | sizeof(struct ipstats_mib)) < 0) | 282 | sizeof(struct ipstats_mib)) < 0) |
| 283 | goto err_ip; | 283 | goto err_ip; |
| 284 | if (snmp_mib_init((void **)idev->stats.icmpv6, | 284 | if (snmp_mib_init((void __percpu **)idev->stats.icmpv6, |
| 285 | sizeof(struct icmpv6_mib)) < 0) | 285 | sizeof(struct icmpv6_mib)) < 0) |
| 286 | goto err_icmp; | 286 | goto err_icmp; |
| 287 | if (snmp_mib_init((void **)idev->stats.icmpv6msg, | 287 | if (snmp_mib_init((void __percpu **)idev->stats.icmpv6msg, |
| 288 | sizeof(struct icmpv6msg_mib)) < 0) | 288 | sizeof(struct icmpv6msg_mib)) < 0) |
| 289 | goto err_icmpmsg; | 289 | goto err_icmpmsg; |
| 290 | 290 | ||
| 291 | return 0; | 291 | return 0; |
| 292 | 292 | ||
| 293 | err_icmpmsg: | 293 | err_icmpmsg: |
| 294 | snmp_mib_free((void **)idev->stats.icmpv6); | 294 | snmp_mib_free((void __percpu **)idev->stats.icmpv6); |
| 295 | err_icmp: | 295 | err_icmp: |
| 296 | snmp_mib_free((void **)idev->stats.ipv6); | 296 | snmp_mib_free((void __percpu **)idev->stats.ipv6); |
| 297 | err_ip: | 297 | err_ip: |
| 298 | return -ENOMEM; | 298 | return -ENOMEM; |
| 299 | } | 299 | } |
| 300 | 300 | ||
| 301 | static void snmp6_free_dev(struct inet6_dev *idev) | 301 | static void snmp6_free_dev(struct inet6_dev *idev) |
| 302 | { | 302 | { |
| 303 | snmp_mib_free((void **)idev->stats.icmpv6msg); | 303 | snmp_mib_free((void __percpu **)idev->stats.icmpv6msg); |
| 304 | snmp_mib_free((void **)idev->stats.icmpv6); | 304 | snmp_mib_free((void __percpu **)idev->stats.icmpv6); |
| 305 | snmp_mib_free((void **)idev->stats.ipv6); | 305 | snmp_mib_free((void __percpu **)idev->stats.ipv6); |
| 306 | } | 306 | } |
| 307 | 307 | ||
| 308 | /* Nobody refers to this device, we may destroy it. */ | 308 | /* Nobody refers to this device, we may destroy it. */ |
| @@ -502,8 +502,11 @@ static int addrconf_fixup_forwarding(struct ctl_table *table, int *p, int old) | |||
| 502 | if (p == &net->ipv6.devconf_dflt->forwarding) | 502 | if (p == &net->ipv6.devconf_dflt->forwarding) |
| 503 | return 0; | 503 | return 0; |
| 504 | 504 | ||
| 505 | if (!rtnl_trylock()) | 505 | if (!rtnl_trylock()) { |
| 506 | /* Restore the original values before restarting */ | ||
| 507 | *p = old; | ||
| 506 | return restart_syscall(); | 508 | return restart_syscall(); |
| 509 | } | ||
| 507 | 510 | ||
| 508 | if (p == &net->ipv6.devconf_all->forwarding) { | 511 | if (p == &net->ipv6.devconf_all->forwarding) { |
| 509 | __s32 newf = net->ipv6.devconf_all->forwarding; | 512 | __s32 newf = net->ipv6.devconf_all->forwarding; |
| @@ -989,8 +992,7 @@ struct ipv6_saddr_dst { | |||
| 989 | 992 | ||
| 990 | static inline int ipv6_saddr_preferred(int type) | 993 | static inline int ipv6_saddr_preferred(int type) |
| 991 | { | 994 | { |
| 992 | if (type & (IPV6_ADDR_MAPPED|IPV6_ADDR_COMPATv4| | 995 | if (type & (IPV6_ADDR_MAPPED|IPV6_ADDR_COMPATv4|IPV6_ADDR_LOOPBACK)) |
| 993 | IPV6_ADDR_LOOPBACK|IPV6_ADDR_RESERVED)) | ||
| 994 | return 1; | 996 | return 1; |
| 995 | return 0; | 997 | return 0; |
| 996 | } | 998 | } |
| @@ -2646,7 +2648,8 @@ static int addrconf_ifdown(struct net_device *dev, int how) | |||
| 2646 | 2648 | ||
| 2647 | write_lock_bh(&addrconf_hash_lock); | 2649 | write_lock_bh(&addrconf_hash_lock); |
| 2648 | while ((ifa = *bifa) != NULL) { | 2650 | while ((ifa = *bifa) != NULL) { |
| 2649 | if (ifa->idev == idev) { | 2651 | if (ifa->idev == idev && |
| 2652 | (how || !(ifa->flags&IFA_F_PERMANENT))) { | ||
| 2650 | *bifa = ifa->lst_next; | 2653 | *bifa = ifa->lst_next; |
| 2651 | ifa->lst_next = NULL; | 2654 | ifa->lst_next = NULL; |
| 2652 | addrconf_del_timer(ifa); | 2655 | addrconf_del_timer(ifa); |
| @@ -2686,18 +2689,30 @@ static int addrconf_ifdown(struct net_device *dev, int how) | |||
| 2686 | write_lock_bh(&idev->lock); | 2689 | write_lock_bh(&idev->lock); |
| 2687 | } | 2690 | } |
| 2688 | #endif | 2691 | #endif |
| 2689 | while ((ifa = idev->addr_list) != NULL) { | 2692 | bifa = &idev->addr_list; |
| 2690 | idev->addr_list = ifa->if_next; | 2693 | while ((ifa = *bifa) != NULL) { |
| 2691 | ifa->if_next = NULL; | 2694 | if (how == 0 && (ifa->flags&IFA_F_PERMANENT)) { |
| 2692 | ifa->dead = 1; | 2695 | /* Retain permanent address on admin down */ |
| 2693 | addrconf_del_timer(ifa); | 2696 | bifa = &ifa->if_next; |
| 2694 | write_unlock_bh(&idev->lock); | 2697 | |
| 2698 | /* Restart DAD if needed when link comes back up */ | ||
| 2699 | if ( !((dev->flags&(IFF_NOARP|IFF_LOOPBACK)) || | ||
| 2700 | idev->cnf.accept_dad <= 0 || | ||
| 2701 | (ifa->flags & IFA_F_NODAD))) | ||
| 2702 | ifa->flags |= IFA_F_TENTATIVE; | ||
| 2703 | } else { | ||
| 2704 | *bifa = ifa->if_next; | ||
| 2705 | ifa->if_next = NULL; | ||
| 2695 | 2706 | ||
| 2696 | __ipv6_ifa_notify(RTM_DELADDR, ifa); | 2707 | ifa->dead = 1; |
| 2697 | atomic_notifier_call_chain(&inet6addr_chain, NETDEV_DOWN, ifa); | 2708 | write_unlock_bh(&idev->lock); |
| 2698 | in6_ifa_put(ifa); | ||
| 2699 | 2709 | ||
| 2700 | write_lock_bh(&idev->lock); | 2710 | __ipv6_ifa_notify(RTM_DELADDR, ifa); |
| 2711 | atomic_notifier_call_chain(&inet6addr_chain, NETDEV_DOWN, ifa); | ||
| 2712 | in6_ifa_put(ifa); | ||
| 2713 | |||
| 2714 | write_lock_bh(&idev->lock); | ||
| 2715 | } | ||
| 2701 | } | 2716 | } |
| 2702 | write_unlock_bh(&idev->lock); | 2717 | write_unlock_bh(&idev->lock); |
| 2703 | 2718 | ||
| @@ -2789,14 +2804,14 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags) | |||
| 2789 | read_lock_bh(&idev->lock); | 2804 | read_lock_bh(&idev->lock); |
| 2790 | if (ifp->dead) | 2805 | if (ifp->dead) |
| 2791 | goto out; | 2806 | goto out; |
| 2792 | spin_lock_bh(&ifp->lock); | ||
| 2793 | 2807 | ||
| 2808 | spin_lock(&ifp->lock); | ||
| 2794 | if (dev->flags&(IFF_NOARP|IFF_LOOPBACK) || | 2809 | if (dev->flags&(IFF_NOARP|IFF_LOOPBACK) || |
| 2795 | idev->cnf.accept_dad < 1 || | 2810 | idev->cnf.accept_dad < 1 || |
| 2796 | !(ifp->flags&IFA_F_TENTATIVE) || | 2811 | !(ifp->flags&IFA_F_TENTATIVE) || |
| 2797 | ifp->flags & IFA_F_NODAD) { | 2812 | ifp->flags & IFA_F_NODAD) { |
| 2798 | ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC|IFA_F_DADFAILED); | 2813 | ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC|IFA_F_DADFAILED); |
| 2799 | spin_unlock_bh(&ifp->lock); | 2814 | spin_unlock(&ifp->lock); |
| 2800 | read_unlock_bh(&idev->lock); | 2815 | read_unlock_bh(&idev->lock); |
| 2801 | 2816 | ||
| 2802 | addrconf_dad_completed(ifp); | 2817 | addrconf_dad_completed(ifp); |
| @@ -2804,7 +2819,7 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags) | |||
| 2804 | } | 2819 | } |
| 2805 | 2820 | ||
| 2806 | if (!(idev->if_flags & IF_READY)) { | 2821 | if (!(idev->if_flags & IF_READY)) { |
| 2807 | spin_unlock_bh(&ifp->lock); | 2822 | spin_unlock(&ifp->lock); |
| 2808 | read_unlock_bh(&idev->lock); | 2823 | read_unlock_bh(&idev->lock); |
| 2809 | /* | 2824 | /* |
| 2810 | * If the device is not ready: | 2825 | * If the device is not ready: |
| @@ -2824,7 +2839,7 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags) | |||
| 2824 | ip6_ins_rt(ifp->rt); | 2839 | ip6_ins_rt(ifp->rt); |
| 2825 | 2840 | ||
| 2826 | addrconf_dad_kick(ifp); | 2841 | addrconf_dad_kick(ifp); |
| 2827 | spin_unlock_bh(&ifp->lock); | 2842 | spin_unlock(&ifp->lock); |
| 2828 | out: | 2843 | out: |
| 2829 | read_unlock_bh(&idev->lock); | 2844 | read_unlock_bh(&idev->lock); |
| 2830 | } | 2845 | } |
| @@ -2840,14 +2855,15 @@ static void addrconf_dad_timer(unsigned long data) | |||
| 2840 | read_unlock_bh(&idev->lock); | 2855 | read_unlock_bh(&idev->lock); |
| 2841 | goto out; | 2856 | goto out; |
| 2842 | } | 2857 | } |
| 2843 | spin_lock_bh(&ifp->lock); | 2858 | |
| 2859 | spin_lock(&ifp->lock); | ||
| 2844 | if (ifp->probes == 0) { | 2860 | if (ifp->probes == 0) { |
| 2845 | /* | 2861 | /* |
| 2846 | * DAD was successful | 2862 | * DAD was successful |
| 2847 | */ | 2863 | */ |
| 2848 | 2864 | ||
| 2849 | ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC|IFA_F_DADFAILED); | 2865 | ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC|IFA_F_DADFAILED); |
| 2850 | spin_unlock_bh(&ifp->lock); | 2866 | spin_unlock(&ifp->lock); |
| 2851 | read_unlock_bh(&idev->lock); | 2867 | read_unlock_bh(&idev->lock); |
| 2852 | 2868 | ||
| 2853 | addrconf_dad_completed(ifp); | 2869 | addrconf_dad_completed(ifp); |
| @@ -2857,7 +2873,7 @@ static void addrconf_dad_timer(unsigned long data) | |||
| 2857 | 2873 | ||
| 2858 | ifp->probes--; | 2874 | ifp->probes--; |
| 2859 | addrconf_mod_timer(ifp, AC_DAD, ifp->idev->nd_parms->retrans_time); | 2875 | addrconf_mod_timer(ifp, AC_DAD, ifp->idev->nd_parms->retrans_time); |
| 2860 | spin_unlock_bh(&ifp->lock); | 2876 | spin_unlock(&ifp->lock); |
| 2861 | read_unlock_bh(&idev->lock); | 2877 | read_unlock_bh(&idev->lock); |
| 2862 | 2878 | ||
| 2863 | /* send a neighbour solicitation for our addr */ | 2879 | /* send a neighbour solicitation for our addr */ |
| @@ -2905,12 +2921,12 @@ static void addrconf_dad_run(struct inet6_dev *idev) { | |||
| 2905 | 2921 | ||
| 2906 | read_lock_bh(&idev->lock); | 2922 | read_lock_bh(&idev->lock); |
| 2907 | for (ifp = idev->addr_list; ifp; ifp = ifp->if_next) { | 2923 | for (ifp = idev->addr_list; ifp; ifp = ifp->if_next) { |
| 2908 | spin_lock_bh(&ifp->lock); | 2924 | spin_lock(&ifp->lock); |
| 2909 | if (!(ifp->flags & IFA_F_TENTATIVE)) { | 2925 | if (!(ifp->flags & IFA_F_TENTATIVE)) { |
| 2910 | spin_unlock_bh(&ifp->lock); | 2926 | spin_unlock(&ifp->lock); |
| 2911 | continue; | 2927 | continue; |
| 2912 | } | 2928 | } |
| 2913 | spin_unlock_bh(&ifp->lock); | 2929 | spin_unlock(&ifp->lock); |
| 2914 | addrconf_dad_kick(ifp); | 2930 | addrconf_dad_kick(ifp); |
| 2915 | } | 2931 | } |
| 2916 | read_unlock_bh(&idev->lock); | 2932 | read_unlock_bh(&idev->lock); |
| @@ -3027,14 +3043,14 @@ static const struct file_operations if6_fops = { | |||
| 3027 | .release = seq_release_net, | 3043 | .release = seq_release_net, |
| 3028 | }; | 3044 | }; |
| 3029 | 3045 | ||
| 3030 | static int if6_proc_net_init(struct net *net) | 3046 | static int __net_init if6_proc_net_init(struct net *net) |
| 3031 | { | 3047 | { |
| 3032 | if (!proc_net_fops_create(net, "if_inet6", S_IRUGO, &if6_fops)) | 3048 | if (!proc_net_fops_create(net, "if_inet6", S_IRUGO, &if6_fops)) |
| 3033 | return -ENOMEM; | 3049 | return -ENOMEM; |
| 3034 | return 0; | 3050 | return 0; |
| 3035 | } | 3051 | } |
| 3036 | 3052 | ||
| 3037 | static void if6_proc_net_exit(struct net *net) | 3053 | static void __net_exit if6_proc_net_exit(struct net *net) |
| 3038 | { | 3054 | { |
| 3039 | proc_net_remove(net, "if_inet6"); | 3055 | proc_net_remove(net, "if_inet6"); |
| 3040 | } | 3056 | } |
| @@ -3752,8 +3768,8 @@ static inline size_t inet6_if_nlmsg_size(void) | |||
| 3752 | ); | 3768 | ); |
| 3753 | } | 3769 | } |
| 3754 | 3770 | ||
| 3755 | static inline void __snmp6_fill_stats(u64 *stats, void **mib, int items, | 3771 | static inline void __snmp6_fill_stats(u64 *stats, void __percpu **mib, |
| 3756 | int bytes) | 3772 | int items, int bytes) |
| 3757 | { | 3773 | { |
| 3758 | int i; | 3774 | int i; |
| 3759 | int pad = bytes - sizeof(u64) * items; | 3775 | int pad = bytes - sizeof(u64) * items; |
| @@ -3772,10 +3788,10 @@ static void snmp6_fill_stats(u64 *stats, struct inet6_dev *idev, int attrtype, | |||
| 3772 | { | 3788 | { |
| 3773 | switch(attrtype) { | 3789 | switch(attrtype) { |
| 3774 | case IFLA_INET6_STATS: | 3790 | case IFLA_INET6_STATS: |
| 3775 | __snmp6_fill_stats(stats, (void **)idev->stats.ipv6, IPSTATS_MIB_MAX, bytes); | 3791 | __snmp6_fill_stats(stats, (void __percpu **)idev->stats.ipv6, IPSTATS_MIB_MAX, bytes); |
| 3776 | break; | 3792 | break; |
| 3777 | case IFLA_INET6_ICMP6STATS: | 3793 | case IFLA_INET6_ICMP6STATS: |
| 3778 | __snmp6_fill_stats(stats, (void **)idev->stats.icmpv6, ICMP6_MIB_MAX, bytes); | 3794 | __snmp6_fill_stats(stats, (void __percpu **)idev->stats.icmpv6, ICMP6_MIB_MAX, bytes); |
| 3779 | break; | 3795 | break; |
| 3780 | } | 3796 | } |
| 3781 | } | 3797 | } |
| @@ -4028,12 +4044,15 @@ int addrconf_sysctl_forward(ctl_table *ctl, int write, | |||
| 4028 | { | 4044 | { |
| 4029 | int *valp = ctl->data; | 4045 | int *valp = ctl->data; |
| 4030 | int val = *valp; | 4046 | int val = *valp; |
| 4047 | loff_t pos = *ppos; | ||
| 4031 | int ret; | 4048 | int ret; |
| 4032 | 4049 | ||
| 4033 | ret = proc_dointvec(ctl, write, buffer, lenp, ppos); | 4050 | ret = proc_dointvec(ctl, write, buffer, lenp, ppos); |
| 4034 | 4051 | ||
| 4035 | if (write) | 4052 | if (write) |
| 4036 | ret = addrconf_fixup_forwarding(ctl, valp, val); | 4053 | ret = addrconf_fixup_forwarding(ctl, valp, val); |
| 4054 | if (ret) | ||
| 4055 | *ppos = pos; | ||
| 4037 | return ret; | 4056 | return ret; |
| 4038 | } | 4057 | } |
| 4039 | 4058 | ||
| @@ -4075,8 +4094,11 @@ static int addrconf_disable_ipv6(struct ctl_table *table, int *p, int old) | |||
| 4075 | if (p == &net->ipv6.devconf_dflt->disable_ipv6) | 4094 | if (p == &net->ipv6.devconf_dflt->disable_ipv6) |
| 4076 | return 0; | 4095 | return 0; |
| 4077 | 4096 | ||
| 4078 | if (!rtnl_trylock()) | 4097 | if (!rtnl_trylock()) { |
| 4098 | /* Restore the original values before restarting */ | ||
| 4099 | *p = old; | ||
| 4079 | return restart_syscall(); | 4100 | return restart_syscall(); |
| 4101 | } | ||
| 4080 | 4102 | ||
| 4081 | if (p == &net->ipv6.devconf_all->disable_ipv6) { | 4103 | if (p == &net->ipv6.devconf_all->disable_ipv6) { |
| 4082 | __s32 newf = net->ipv6.devconf_all->disable_ipv6; | 4104 | __s32 newf = net->ipv6.devconf_all->disable_ipv6; |
| @@ -4095,12 +4117,15 @@ int addrconf_sysctl_disable(ctl_table *ctl, int write, | |||
| 4095 | { | 4117 | { |
| 4096 | int *valp = ctl->data; | 4118 | int *valp = ctl->data; |
| 4097 | int val = *valp; | 4119 | int val = *valp; |
| 4120 | loff_t pos = *ppos; | ||
| 4098 | int ret; | 4121 | int ret; |
| 4099 | 4122 | ||
| 4100 | ret = proc_dointvec(ctl, write, buffer, lenp, ppos); | 4123 | ret = proc_dointvec(ctl, write, buffer, lenp, ppos); |
| 4101 | 4124 | ||
| 4102 | if (write) | 4125 | if (write) |
| 4103 | ret = addrconf_disable_ipv6(ctl, valp, val); | 4126 | ret = addrconf_disable_ipv6(ctl, valp, val); |
| 4127 | if (ret) | ||
| 4128 | *ppos = pos; | ||
| 4104 | return ret; | 4129 | return ret; |
| 4105 | } | 4130 | } |
| 4106 | 4131 | ||
| @@ -4402,8 +4427,7 @@ static void __addrconf_sysctl_unregister(struct ipv6_devconf *p) | |||
| 4402 | 4427 | ||
| 4403 | static void addrconf_sysctl_register(struct inet6_dev *idev) | 4428 | static void addrconf_sysctl_register(struct inet6_dev *idev) |
| 4404 | { | 4429 | { |
| 4405 | neigh_sysctl_register(idev->dev, idev->nd_parms, NET_IPV6, | 4430 | neigh_sysctl_register(idev->dev, idev->nd_parms, "ipv6", |
| 4406 | NET_IPV6_NEIGH, "ipv6", | ||
| 4407 | &ndisc_ifinfo_sysctl_change); | 4431 | &ndisc_ifinfo_sysctl_change); |
| 4408 | __addrconf_sysctl_register(dev_net(idev->dev), idev->dev->name, | 4432 | __addrconf_sysctl_register(dev_net(idev->dev), idev->dev->name, |
| 4409 | idev, &idev->cnf); | 4433 | idev, &idev->cnf); |
| @@ -4418,7 +4442,7 @@ static void addrconf_sysctl_unregister(struct inet6_dev *idev) | |||
| 4418 | 4442 | ||
| 4419 | #endif | 4443 | #endif |
| 4420 | 4444 | ||
| 4421 | static int addrconf_init_net(struct net *net) | 4445 | static int __net_init addrconf_init_net(struct net *net) |
| 4422 | { | 4446 | { |
| 4423 | int err; | 4447 | int err; |
| 4424 | struct ipv6_devconf *all, *dflt; | 4448 | struct ipv6_devconf *all, *dflt; |
| @@ -4467,7 +4491,7 @@ err_alloc_all: | |||
| 4467 | return err; | 4491 | return err; |
| 4468 | } | 4492 | } |
| 4469 | 4493 | ||
| 4470 | static void addrconf_exit_net(struct net *net) | 4494 | static void __net_exit addrconf_exit_net(struct net *net) |
| 4471 | { | 4495 | { |
| 4472 | #ifdef CONFIG_SYSCTL | 4496 | #ifdef CONFIG_SYSCTL |
| 4473 | __addrconf_sysctl_unregister(net->ipv6.devconf_dflt); | 4497 | __addrconf_sysctl_unregister(net->ipv6.devconf_dflt); |
