diff options
Diffstat (limited to 'net/ipv6/icmp.c')
-rw-r--r-- | net/ipv6/icmp.c | 145 |
1 files changed, 95 insertions, 50 deletions
diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index 893287ecc628..d42dd16d3487 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c | |||
@@ -64,6 +64,7 @@ | |||
64 | #include <net/addrconf.h> | 64 | #include <net/addrconf.h> |
65 | #include <net/icmp.h> | 65 | #include <net/icmp.h> |
66 | #include <net/xfrm.h> | 66 | #include <net/xfrm.h> |
67 | #include <net/inet_common.h> | ||
67 | 68 | ||
68 | #include <asm/uaccess.h> | 69 | #include <asm/uaccess.h> |
69 | #include <asm/system.h> | 70 | #include <asm/system.h> |
@@ -80,8 +81,10 @@ EXPORT_SYMBOL(icmpv6msg_statistics); | |||
80 | * | 81 | * |
81 | * On SMP we have one ICMP socket per-cpu. | 82 | * On SMP we have one ICMP socket per-cpu. |
82 | */ | 83 | */ |
83 | static DEFINE_PER_CPU(struct socket *, __icmpv6_socket) = NULL; | 84 | static inline struct sock *icmpv6_sk(struct net *net) |
84 | #define icmpv6_socket __get_cpu_var(__icmpv6_socket) | 85 | { |
86 | return net->ipv6.icmp_sk[smp_processor_id()]; | ||
87 | } | ||
85 | 88 | ||
86 | static int icmpv6_rcv(struct sk_buff *skb); | 89 | static int icmpv6_rcv(struct sk_buff *skb); |
87 | 90 | ||
@@ -90,11 +93,11 @@ static struct inet6_protocol icmpv6_protocol = { | |||
90 | .flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL, | 93 | .flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL, |
91 | }; | 94 | }; |
92 | 95 | ||
93 | static __inline__ int icmpv6_xmit_lock(void) | 96 | static __inline__ int icmpv6_xmit_lock(struct sock *sk) |
94 | { | 97 | { |
95 | local_bh_disable(); | 98 | local_bh_disable(); |
96 | 99 | ||
97 | if (unlikely(!spin_trylock(&icmpv6_socket->sk->sk_lock.slock))) { | 100 | if (unlikely(!spin_trylock(&sk->sk_lock.slock))) { |
98 | /* This can happen if the output path (f.e. SIT or | 101 | /* This can happen if the output path (f.e. SIT or |
99 | * ip6ip6 tunnel) signals dst_link_failure() for an | 102 | * ip6ip6 tunnel) signals dst_link_failure() for an |
100 | * outgoing ICMP6 packet. | 103 | * outgoing ICMP6 packet. |
@@ -105,9 +108,9 @@ static __inline__ int icmpv6_xmit_lock(void) | |||
105 | return 0; | 108 | return 0; |
106 | } | 109 | } |
107 | 110 | ||
108 | static __inline__ void icmpv6_xmit_unlock(void) | 111 | static __inline__ void icmpv6_xmit_unlock(struct sock *sk) |
109 | { | 112 | { |
110 | spin_unlock_bh(&icmpv6_socket->sk->sk_lock.slock); | 113 | spin_unlock_bh(&sk->sk_lock.slock); |
111 | } | 114 | } |
112 | 115 | ||
113 | /* | 116 | /* |
@@ -161,6 +164,7 @@ static inline int icmpv6_xrlim_allow(struct sock *sk, int type, | |||
161 | struct flowi *fl) | 164 | struct flowi *fl) |
162 | { | 165 | { |
163 | struct dst_entry *dst; | 166 | struct dst_entry *dst; |
167 | struct net *net = sock_net(sk); | ||
164 | int res = 0; | 168 | int res = 0; |
165 | 169 | ||
166 | /* Informational messages are not limited. */ | 170 | /* Informational messages are not limited. */ |
@@ -176,7 +180,7 @@ static inline int icmpv6_xrlim_allow(struct sock *sk, int type, | |||
176 | * XXX: perhaps the expire for routing entries cloned by | 180 | * XXX: perhaps the expire for routing entries cloned by |
177 | * this lookup should be more aggressive (not longer than timeout). | 181 | * this lookup should be more aggressive (not longer than timeout). |
178 | */ | 182 | */ |
179 | dst = ip6_route_output(sk, fl); | 183 | dst = ip6_route_output(net, sk, fl); |
180 | if (dst->error) { | 184 | if (dst->error) { |
181 | IP6_INC_STATS(ip6_dst_idev(dst), | 185 | IP6_INC_STATS(ip6_dst_idev(dst), |
182 | IPSTATS_MIB_OUTNOROUTES); | 186 | IPSTATS_MIB_OUTNOROUTES); |
@@ -184,7 +188,7 @@ static inline int icmpv6_xrlim_allow(struct sock *sk, int type, | |||
184 | res = 1; | 188 | res = 1; |
185 | } else { | 189 | } else { |
186 | struct rt6_info *rt = (struct rt6_info *)dst; | 190 | struct rt6_info *rt = (struct rt6_info *)dst; |
187 | int tmo = init_net.ipv6.sysctl.icmpv6_time; | 191 | int tmo = net->ipv6.sysctl.icmpv6_time; |
188 | 192 | ||
189 | /* Give more bandwidth to wider prefixes. */ | 193 | /* Give more bandwidth to wider prefixes. */ |
190 | if (rt->rt6i_dst.plen < 128) | 194 | if (rt->rt6i_dst.plen < 128) |
@@ -303,6 +307,7 @@ static inline void mip6_addr_swap(struct sk_buff *skb) {} | |||
303 | void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info, | 307 | void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info, |
304 | struct net_device *dev) | 308 | struct net_device *dev) |
305 | { | 309 | { |
310 | struct net *net = dev_net(skb->dev); | ||
306 | struct inet6_dev *idev = NULL; | 311 | struct inet6_dev *idev = NULL; |
307 | struct ipv6hdr *hdr = ipv6_hdr(skb); | 312 | struct ipv6hdr *hdr = ipv6_hdr(skb); |
308 | struct sock *sk; | 313 | struct sock *sk; |
@@ -332,7 +337,7 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info, | |||
332 | */ | 337 | */ |
333 | addr_type = ipv6_addr_type(&hdr->daddr); | 338 | addr_type = ipv6_addr_type(&hdr->daddr); |
334 | 339 | ||
335 | if (ipv6_chk_addr(&init_net, &hdr->daddr, skb->dev, 0)) | 340 | if (ipv6_chk_addr(net, &hdr->daddr, skb->dev, 0)) |
336 | saddr = &hdr->daddr; | 341 | saddr = &hdr->daddr; |
337 | 342 | ||
338 | /* | 343 | /* |
@@ -389,12 +394,12 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info, | |||
389 | fl.fl_icmp_code = code; | 394 | fl.fl_icmp_code = code; |
390 | security_skb_classify_flow(skb, &fl); | 395 | security_skb_classify_flow(skb, &fl); |
391 | 396 | ||
392 | if (icmpv6_xmit_lock()) | 397 | sk = icmpv6_sk(net); |
393 | return; | ||
394 | |||
395 | sk = icmpv6_socket->sk; | ||
396 | np = inet6_sk(sk); | 398 | np = inet6_sk(sk); |
397 | 399 | ||
400 | if (icmpv6_xmit_lock(sk)) | ||
401 | return; | ||
402 | |||
398 | if (!icmpv6_xrlim_allow(sk, type, &fl)) | 403 | if (!icmpv6_xrlim_allow(sk, type, &fl)) |
399 | goto out; | 404 | goto out; |
400 | 405 | ||
@@ -462,9 +467,7 @@ route_done: | |||
462 | else | 467 | else |
463 | hlimit = np->hop_limit; | 468 | hlimit = np->hop_limit; |
464 | if (hlimit < 0) | 469 | if (hlimit < 0) |
465 | hlimit = dst_metric(dst, RTAX_HOPLIMIT); | 470 | hlimit = ip6_dst_hoplimit(dst); |
466 | if (hlimit < 0) | ||
467 | hlimit = ipv6_get_hoplimit(dst->dev); | ||
468 | 471 | ||
469 | tclass = np->tclass; | 472 | tclass = np->tclass; |
470 | if (tclass < 0) | 473 | if (tclass < 0) |
@@ -500,13 +503,14 @@ out_put: | |||
500 | out_dst_release: | 503 | out_dst_release: |
501 | dst_release(dst); | 504 | dst_release(dst); |
502 | out: | 505 | out: |
503 | icmpv6_xmit_unlock(); | 506 | icmpv6_xmit_unlock(sk); |
504 | } | 507 | } |
505 | 508 | ||
506 | EXPORT_SYMBOL(icmpv6_send); | 509 | EXPORT_SYMBOL(icmpv6_send); |
507 | 510 | ||
508 | static void icmpv6_echo_reply(struct sk_buff *skb) | 511 | static void icmpv6_echo_reply(struct sk_buff *skb) |
509 | { | 512 | { |
513 | struct net *net = dev_net(skb->dev); | ||
510 | struct sock *sk; | 514 | struct sock *sk; |
511 | struct inet6_dev *idev; | 515 | struct inet6_dev *idev; |
512 | struct ipv6_pinfo *np; | 516 | struct ipv6_pinfo *np; |
@@ -537,12 +541,12 @@ static void icmpv6_echo_reply(struct sk_buff *skb) | |||
537 | fl.fl_icmp_type = ICMPV6_ECHO_REPLY; | 541 | fl.fl_icmp_type = ICMPV6_ECHO_REPLY; |
538 | security_skb_classify_flow(skb, &fl); | 542 | security_skb_classify_flow(skb, &fl); |
539 | 543 | ||
540 | if (icmpv6_xmit_lock()) | 544 | sk = icmpv6_sk(net); |
541 | return; | ||
542 | |||
543 | sk = icmpv6_socket->sk; | ||
544 | np = inet6_sk(sk); | 545 | np = inet6_sk(sk); |
545 | 546 | ||
547 | if (icmpv6_xmit_lock(sk)) | ||
548 | return; | ||
549 | |||
546 | if (!fl.oif && ipv6_addr_is_multicast(&fl.fl6_dst)) | 550 | if (!fl.oif && ipv6_addr_is_multicast(&fl.fl6_dst)) |
547 | fl.oif = np->mcast_oif; | 551 | fl.oif = np->mcast_oif; |
548 | 552 | ||
@@ -557,9 +561,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb) | |||
557 | else | 561 | else |
558 | hlimit = np->hop_limit; | 562 | hlimit = np->hop_limit; |
559 | if (hlimit < 0) | 563 | if (hlimit < 0) |
560 | hlimit = dst_metric(dst, RTAX_HOPLIMIT); | 564 | hlimit = ip6_dst_hoplimit(dst); |
561 | if (hlimit < 0) | ||
562 | hlimit = ipv6_get_hoplimit(dst->dev); | ||
563 | 565 | ||
564 | tclass = np->tclass; | 566 | tclass = np->tclass; |
565 | if (tclass < 0) | 567 | if (tclass < 0) |
@@ -586,7 +588,7 @@ out_put: | |||
586 | in6_dev_put(idev); | 588 | in6_dev_put(idev); |
587 | dst_release(dst); | 589 | dst_release(dst); |
588 | out: | 590 | out: |
589 | icmpv6_xmit_unlock(); | 591 | icmpv6_xmit_unlock(sk); |
590 | } | 592 | } |
591 | 593 | ||
592 | static void icmpv6_notify(struct sk_buff *skb, int type, int code, __be32 info) | 594 | static void icmpv6_notify(struct sk_buff *skb, int type, int code, __be32 info) |
@@ -777,19 +779,40 @@ drop_no_count: | |||
777 | return 0; | 779 | return 0; |
778 | } | 780 | } |
779 | 781 | ||
782 | void icmpv6_flow_init(struct sock *sk, struct flowi *fl, | ||
783 | u8 type, | ||
784 | const struct in6_addr *saddr, | ||
785 | const struct in6_addr *daddr, | ||
786 | int oif) | ||
787 | { | ||
788 | memset(fl, 0, sizeof(*fl)); | ||
789 | ipv6_addr_copy(&fl->fl6_src, saddr); | ||
790 | ipv6_addr_copy(&fl->fl6_dst, daddr); | ||
791 | fl->proto = IPPROTO_ICMPV6; | ||
792 | fl->fl_icmp_type = type; | ||
793 | fl->fl_icmp_code = 0; | ||
794 | fl->oif = oif; | ||
795 | security_sk_classify_flow(sk, fl); | ||
796 | } | ||
797 | |||
780 | /* | 798 | /* |
781 | * Special lock-class for __icmpv6_socket: | 799 | * Special lock-class for __icmpv6_sk: |
782 | */ | 800 | */ |
783 | static struct lock_class_key icmpv6_socket_sk_dst_lock_key; | 801 | static struct lock_class_key icmpv6_socket_sk_dst_lock_key; |
784 | 802 | ||
785 | int __init icmpv6_init(struct net_proto_family *ops) | 803 | static int __net_init icmpv6_sk_init(struct net *net) |
786 | { | 804 | { |
787 | struct sock *sk; | 805 | struct sock *sk; |
788 | int err, i, j; | 806 | int err, i, j; |
789 | 807 | ||
808 | net->ipv6.icmp_sk = | ||
809 | kzalloc(nr_cpu_ids * sizeof(struct sock *), GFP_KERNEL); | ||
810 | if (net->ipv6.icmp_sk == NULL) | ||
811 | return -ENOMEM; | ||
812 | |||
790 | for_each_possible_cpu(i) { | 813 | for_each_possible_cpu(i) { |
791 | err = sock_create_kern(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6, | 814 | err = inet_ctl_sock_create(&sk, PF_INET6, |
792 | &per_cpu(__icmpv6_socket, i)); | 815 | SOCK_RAW, IPPROTO_ICMPV6, net); |
793 | if (err < 0) { | 816 | if (err < 0) { |
794 | printk(KERN_ERR | 817 | printk(KERN_ERR |
795 | "Failed to initialize the ICMP6 control socket " | 818 | "Failed to initialize the ICMP6 control socket " |
@@ -798,12 +821,12 @@ int __init icmpv6_init(struct net_proto_family *ops) | |||
798 | goto fail; | 821 | goto fail; |
799 | } | 822 | } |
800 | 823 | ||
801 | sk = per_cpu(__icmpv6_socket, i)->sk; | 824 | net->ipv6.icmp_sk[i] = sk; |
802 | sk->sk_allocation = GFP_ATOMIC; | 825 | |
803 | /* | 826 | /* |
804 | * Split off their lock-class, because sk->sk_dst_lock | 827 | * Split off their lock-class, because sk->sk_dst_lock |
805 | * gets used from softirqs, which is safe for | 828 | * gets used from softirqs, which is safe for |
806 | * __icmpv6_socket (because those never get directly used | 829 | * __icmpv6_sk (because those never get directly used |
807 | * via userspace syscalls), but unsafe for normal sockets. | 830 | * via userspace syscalls), but unsafe for normal sockets. |
808 | */ | 831 | */ |
809 | lockdep_set_class(&sk->sk_dst_lock, | 832 | lockdep_set_class(&sk->sk_dst_lock, |
@@ -814,39 +837,57 @@ int __init icmpv6_init(struct net_proto_family *ops) | |||
814 | */ | 837 | */ |
815 | sk->sk_sndbuf = | 838 | sk->sk_sndbuf = |
816 | (2 * ((64 * 1024) + sizeof(struct sk_buff))); | 839 | (2 * ((64 * 1024) + sizeof(struct sk_buff))); |
817 | |||
818 | sk->sk_prot->unhash(sk); | ||
819 | } | 840 | } |
820 | |||
821 | |||
822 | if (inet6_add_protocol(&icmpv6_protocol, IPPROTO_ICMPV6) < 0) { | ||
823 | printk(KERN_ERR "Failed to register ICMP6 protocol\n"); | ||
824 | err = -EAGAIN; | ||
825 | goto fail; | ||
826 | } | ||
827 | |||
828 | return 0; | 841 | return 0; |
829 | 842 | ||
830 | fail: | 843 | fail: |
831 | for (j = 0; j < i; j++) { | 844 | for (j = 0; j < i; j++) |
832 | if (!cpu_possible(j)) | 845 | inet_ctl_sock_destroy(net->ipv6.icmp_sk[j]); |
833 | continue; | 846 | kfree(net->ipv6.icmp_sk); |
834 | sock_release(per_cpu(__icmpv6_socket, j)); | ||
835 | } | ||
836 | |||
837 | return err; | 847 | return err; |
838 | } | 848 | } |
839 | 849 | ||
840 | void icmpv6_cleanup(void) | 850 | static void __net_exit icmpv6_sk_exit(struct net *net) |
841 | { | 851 | { |
842 | int i; | 852 | int i; |
843 | 853 | ||
844 | for_each_possible_cpu(i) { | 854 | for_each_possible_cpu(i) { |
845 | sock_release(per_cpu(__icmpv6_socket, i)); | 855 | inet_ctl_sock_destroy(net->ipv6.icmp_sk[i]); |
846 | } | 856 | } |
857 | kfree(net->ipv6.icmp_sk); | ||
858 | } | ||
859 | |||
860 | static struct pernet_operations icmpv6_sk_ops = { | ||
861 | .init = icmpv6_sk_init, | ||
862 | .exit = icmpv6_sk_exit, | ||
863 | }; | ||
864 | |||
865 | int __init icmpv6_init(void) | ||
866 | { | ||
867 | int err; | ||
868 | |||
869 | err = register_pernet_subsys(&icmpv6_sk_ops); | ||
870 | if (err < 0) | ||
871 | return err; | ||
872 | |||
873 | err = -EAGAIN; | ||
874 | if (inet6_add_protocol(&icmpv6_protocol, IPPROTO_ICMPV6) < 0) | ||
875 | goto fail; | ||
876 | return 0; | ||
877 | |||
878 | fail: | ||
879 | printk(KERN_ERR "Failed to register ICMP6 protocol\n"); | ||
880 | unregister_pernet_subsys(&icmpv6_sk_ops); | ||
881 | return err; | ||
882 | } | ||
883 | |||
884 | void icmpv6_cleanup(void) | ||
885 | { | ||
886 | unregister_pernet_subsys(&icmpv6_sk_ops); | ||
847 | inet6_del_protocol(&icmpv6_protocol, IPPROTO_ICMPV6); | 887 | inet6_del_protocol(&icmpv6_protocol, IPPROTO_ICMPV6); |
848 | } | 888 | } |
849 | 889 | ||
890 | |||
850 | static const struct icmp6_err { | 891 | static const struct icmp6_err { |
851 | int err; | 892 | int err; |
852 | int fatal; | 893 | int fatal; |
@@ -927,6 +968,10 @@ struct ctl_table *ipv6_icmp_sysctl_init(struct net *net) | |||
927 | table = kmemdup(ipv6_icmp_table_template, | 968 | table = kmemdup(ipv6_icmp_table_template, |
928 | sizeof(ipv6_icmp_table_template), | 969 | sizeof(ipv6_icmp_table_template), |
929 | GFP_KERNEL); | 970 | GFP_KERNEL); |
971 | |||
972 | if (table) | ||
973 | table[0].data = &net->ipv6.sysctl.icmpv6_time; | ||
974 | |||
930 | return table; | 975 | return table; |
931 | } | 976 | } |
932 | #endif | 977 | #endif |