aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/ipv6/Makefile2
-rw-r--r--net/ipv6/icmp.c35
-rw-r--r--net/ipv6/ip6_icmp.c47
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
40obj-$(CONFIG_IPV6_TUNNEL) += ip6_tunnel.o 40obj-$(CONFIG_IPV6_TUNNEL) += ip6_tunnel.o
41obj-$(CONFIG_IPV6_GRE) += ip6_gre.o 41obj-$(CONFIG_IPV6_GRE) += ip6_gre.o
42 42
43obj-y += addrconf_core.o exthdrs_core.o ip6_checksum.o 43obj-y += addrconf_core.o exthdrs_core.o ip6_checksum.o ip6_icmp.o
44obj-$(CONFIG_INET) += output_core.o protocol.o $(ipv6-offload) 44obj-$(CONFIG_INET) += output_core.o protocol.o $(ipv6-offload)
45 45
46obj-$(subst m,y,$(CONFIG_IPV6)) += inet6_hashtables.o 46obj-$(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 */
129void 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 */
384void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info) 375static 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:
529out: 520out:
530 icmpv6_xmit_unlock(sk); 521 icmpv6_xmit_unlock(sk);
531} 522}
532EXPORT_SYMBOL(icmpv6_send); 523
524/* Slightly more convenient version of icmp6_send.
525 */
526void 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
534static void icmpv6_echo_reply(struct sk_buff *skb) 532static 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
892sender_reg_err:
893 inet6_del_protocol(&icmpv6_protocol, IPPROTO_ICMPV6);
890fail: 894fail:
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
896void icmpv6_cleanup(void) 900void 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
11static ip6_icmp_send_t __rcu *ip6_icmp_send;
12
13int 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}
18EXPORT_SYMBOL(inet6_register_icmp_sender);
19
20int 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}
31EXPORT_SYMBOL(inet6_unregister_icmp_sender);
32
33void 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);
43out:
44 rcu_read_unlock();
45}
46EXPORT_SYMBOL(icmpv6_send);
47#endif