diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv6/Makefile | 2 | ||||
-rw-r--r-- | net/ipv6/icmp.c | 35 | ||||
-rw-r--r-- | net/ipv6/ip6_icmp.c | 47 |
3 files changed, 68 insertions, 16 deletions
diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile index 309af19a0a0a..9af088d2cdaa 100644 --- a/net/ipv6/Makefile +++ b/net/ipv6/Makefile | |||
@@ -40,7 +40,7 @@ obj-$(CONFIG_IPV6_SIT) += sit.o | |||
40 | obj-$(CONFIG_IPV6_TUNNEL) += ip6_tunnel.o | 40 | obj-$(CONFIG_IPV6_TUNNEL) += ip6_tunnel.o |
41 | obj-$(CONFIG_IPV6_GRE) += ip6_gre.o | 41 | obj-$(CONFIG_IPV6_GRE) += ip6_gre.o |
42 | 42 | ||
43 | obj-y += addrconf_core.o exthdrs_core.o ip6_checksum.o | 43 | obj-y += addrconf_core.o exthdrs_core.o ip6_checksum.o ip6_icmp.o |
44 | obj-$(CONFIG_INET) += output_core.o protocol.o $(ipv6-offload) | 44 | obj-$(CONFIG_INET) += output_core.o protocol.o $(ipv6-offload) |
45 | 45 | ||
46 | obj-$(subst m,y,$(CONFIG_IPV6)) += inet6_hashtables.o | 46 | obj-$(subst m,y,$(CONFIG_IPV6)) += inet6_hashtables.o |
diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index 71b900c3f4ff..2a53a790514d 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c | |||
@@ -124,15 +124,6 @@ static __inline__ void icmpv6_xmit_unlock(struct sock *sk) | |||
124 | } | 124 | } |
125 | 125 | ||
126 | /* | 126 | /* |
127 | * Slightly more convenient version of icmpv6_send. | ||
128 | */ | ||
129 | void icmpv6_param_prob(struct sk_buff *skb, u8 code, int pos) | ||
130 | { | ||
131 | icmpv6_send(skb, ICMPV6_PARAMPROB, code, pos); | ||
132 | kfree_skb(skb); | ||
133 | } | ||
134 | |||
135 | /* | ||
136 | * Figure out, may we reply to this packet with icmp error. | 127 | * Figure out, may we reply to this packet with icmp error. |
137 | * | 128 | * |
138 | * We do not reply, if: | 129 | * We do not reply, if: |
@@ -332,7 +323,7 @@ static struct dst_entry *icmpv6_route_lookup(struct net *net, struct sk_buff *sk | |||
332 | * anycast. | 323 | * anycast. |
333 | */ | 324 | */ |
334 | if (((struct rt6_info *)dst)->rt6i_flags & RTF_ANYCAST) { | 325 | if (((struct rt6_info *)dst)->rt6i_flags & RTF_ANYCAST) { |
335 | LIMIT_NETDEBUG(KERN_DEBUG "icmpv6_send: acast source\n"); | 326 | LIMIT_NETDEBUG(KERN_DEBUG "icmp6_send: acast source\n"); |
336 | dst_release(dst); | 327 | dst_release(dst); |
337 | return ERR_PTR(-EINVAL); | 328 | return ERR_PTR(-EINVAL); |
338 | } | 329 | } |
@@ -381,7 +372,7 @@ relookup_failed: | |||
381 | /* | 372 | /* |
382 | * Send an ICMP message in response to a packet in error | 373 | * Send an ICMP message in response to a packet in error |
383 | */ | 374 | */ |
384 | void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info) | 375 | static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info) |
385 | { | 376 | { |
386 | struct net *net = dev_net(skb->dev); | 377 | struct net *net = dev_net(skb->dev); |
387 | struct inet6_dev *idev = NULL; | 378 | struct inet6_dev *idev = NULL; |
@@ -406,7 +397,7 @@ void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info) | |||
406 | /* | 397 | /* |
407 | * Make sure we respect the rules | 398 | * Make sure we respect the rules |
408 | * i.e. RFC 1885 2.4(e) | 399 | * i.e. RFC 1885 2.4(e) |
409 | * Rule (e.1) is enforced by not using icmpv6_send | 400 | * Rule (e.1) is enforced by not using icmp6_send |
410 | * in any code that processes icmp errors. | 401 | * in any code that processes icmp errors. |
411 | */ | 402 | */ |
412 | addr_type = ipv6_addr_type(&hdr->daddr); | 403 | addr_type = ipv6_addr_type(&hdr->daddr); |
@@ -444,7 +435,7 @@ void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info) | |||
444 | * and anycast addresses will be checked later. | 435 | * and anycast addresses will be checked later. |
445 | */ | 436 | */ |
446 | if ((addr_type == IPV6_ADDR_ANY) || (addr_type & IPV6_ADDR_MULTICAST)) { | 437 | if ((addr_type == IPV6_ADDR_ANY) || (addr_type & IPV6_ADDR_MULTICAST)) { |
447 | LIMIT_NETDEBUG(KERN_DEBUG "icmpv6_send: addr_any/mcast source\n"); | 438 | LIMIT_NETDEBUG(KERN_DEBUG "icmp6_send: addr_any/mcast source\n"); |
448 | return; | 439 | return; |
449 | } | 440 | } |
450 | 441 | ||
@@ -452,7 +443,7 @@ void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info) | |||
452 | * Never answer to a ICMP packet. | 443 | * Never answer to a ICMP packet. |
453 | */ | 444 | */ |
454 | if (is_ineligible(skb)) { | 445 | if (is_ineligible(skb)) { |
455 | LIMIT_NETDEBUG(KERN_DEBUG "icmpv6_send: no reply to icmp error\n"); | 446 | LIMIT_NETDEBUG(KERN_DEBUG "icmp6_send: no reply to icmp error\n"); |
456 | return; | 447 | return; |
457 | } | 448 | } |
458 | 449 | ||
@@ -529,7 +520,14 @@ out_dst_release: | |||
529 | out: | 520 | out: |
530 | icmpv6_xmit_unlock(sk); | 521 | icmpv6_xmit_unlock(sk); |
531 | } | 522 | } |
532 | EXPORT_SYMBOL(icmpv6_send); | 523 | |
524 | /* Slightly more convenient version of icmp6_send. | ||
525 | */ | ||
526 | void icmpv6_param_prob(struct sk_buff *skb, u8 code, int pos) | ||
527 | { | ||
528 | icmp6_send(skb, ICMPV6_PARAMPROB, code, pos); | ||
529 | kfree_skb(skb); | ||
530 | } | ||
533 | 531 | ||
534 | static void icmpv6_echo_reply(struct sk_buff *skb) | 532 | static void icmpv6_echo_reply(struct sk_buff *skb) |
535 | { | 533 | { |
@@ -885,8 +883,14 @@ int __init icmpv6_init(void) | |||
885 | err = -EAGAIN; | 883 | err = -EAGAIN; |
886 | if (inet6_add_protocol(&icmpv6_protocol, IPPROTO_ICMPV6) < 0) | 884 | if (inet6_add_protocol(&icmpv6_protocol, IPPROTO_ICMPV6) < 0) |
887 | goto fail; | 885 | goto fail; |
886 | |||
887 | err = inet6_register_icmp_sender(icmp6_send); | ||
888 | if (err) | ||
889 | goto sender_reg_err; | ||
888 | return 0; | 890 | return 0; |
889 | 891 | ||
892 | sender_reg_err: | ||
893 | inet6_del_protocol(&icmpv6_protocol, IPPROTO_ICMPV6); | ||
890 | fail: | 894 | fail: |
891 | pr_err("Failed to register ICMP6 protocol\n"); | 895 | pr_err("Failed to register ICMP6 protocol\n"); |
892 | unregister_pernet_subsys(&icmpv6_sk_ops); | 896 | unregister_pernet_subsys(&icmpv6_sk_ops); |
@@ -895,6 +899,7 @@ fail: | |||
895 | 899 | ||
896 | void icmpv6_cleanup(void) | 900 | void icmpv6_cleanup(void) |
897 | { | 901 | { |
902 | inet6_unregister_icmp_sender(icmp6_send); | ||
898 | unregister_pernet_subsys(&icmpv6_sk_ops); | 903 | unregister_pernet_subsys(&icmpv6_sk_ops); |
899 | inet6_del_protocol(&icmpv6_protocol, IPPROTO_ICMPV6); | 904 | inet6_del_protocol(&icmpv6_protocol, IPPROTO_ICMPV6); |
900 | } | 905 | } |
diff --git a/net/ipv6/ip6_icmp.c b/net/ipv6/ip6_icmp.c new file mode 100644 index 000000000000..4578e23834f7 --- /dev/null +++ b/net/ipv6/ip6_icmp.c | |||
@@ -0,0 +1,47 @@ | |||
1 | #include <linux/export.h> | ||
2 | #include <linux/icmpv6.h> | ||
3 | #include <linux/mutex.h> | ||
4 | #include <linux/netdevice.h> | ||
5 | #include <linux/spinlock.h> | ||
6 | |||
7 | #include <net/ipv6.h> | ||
8 | |||
9 | #if IS_ENABLED(CONFIG_IPV6) | ||
10 | |||
11 | static ip6_icmp_send_t __rcu *ip6_icmp_send; | ||
12 | |||
13 | int inet6_register_icmp_sender(ip6_icmp_send_t *fn) | ||
14 | { | ||
15 | return (cmpxchg((ip6_icmp_send_t **)&ip6_icmp_send, NULL, fn) == NULL) ? | ||
16 | 0 : -EBUSY; | ||
17 | } | ||
18 | EXPORT_SYMBOL(inet6_register_icmp_sender); | ||
19 | |||
20 | int inet6_unregister_icmp_sender(ip6_icmp_send_t *fn) | ||
21 | { | ||
22 | int ret; | ||
23 | |||
24 | ret = (cmpxchg((ip6_icmp_send_t **)&ip6_icmp_send, fn, NULL) == fn) ? | ||
25 | 0 : -EINVAL; | ||
26 | |||
27 | synchronize_net(); | ||
28 | |||
29 | return ret; | ||
30 | } | ||
31 | EXPORT_SYMBOL(inet6_unregister_icmp_sender); | ||
32 | |||
33 | void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info) | ||
34 | { | ||
35 | ip6_icmp_send_t *send; | ||
36 | |||
37 | rcu_read_lock(); | ||
38 | send = rcu_dereference(ip6_icmp_send); | ||
39 | |||
40 | if (!send) | ||
41 | goto out; | ||
42 | send(skb, type, code, info); | ||
43 | out: | ||
44 | rcu_read_unlock(); | ||
45 | } | ||
46 | EXPORT_SYMBOL(icmpv6_send); | ||
47 | #endif | ||