Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
5f5624cf PS |
2 | #include <linux/export.h> |
3 | #include <linux/icmpv6.h> | |
4 | #include <linux/mutex.h> | |
5 | #include <linux/netdevice.h> | |
6 | #include <linux/spinlock.h> | |
7 | ||
8 | #include <net/ipv6.h> | |
9 | ||
10 | #if IS_ENABLED(CONFIG_IPV6) | |
11 | ||
cc7a21b6 ED |
12 | #if !IS_BUILTIN(CONFIG_IPV6) |
13 | ||
5f5624cf PS |
14 | static ip6_icmp_send_t __rcu *ip6_icmp_send; |
15 | ||
16 | int inet6_register_icmp_sender(ip6_icmp_send_t *fn) | |
17 | { | |
18 | return (cmpxchg((ip6_icmp_send_t **)&ip6_icmp_send, NULL, fn) == NULL) ? | |
67ba4152 | 19 | 0 : -EBUSY; |
5f5624cf PS |
20 | } |
21 | EXPORT_SYMBOL(inet6_register_icmp_sender); | |
22 | ||
23 | int inet6_unregister_icmp_sender(ip6_icmp_send_t *fn) | |
24 | { | |
25 | int ret; | |
26 | ||
27 | ret = (cmpxchg((ip6_icmp_send_t **)&ip6_icmp_send, fn, NULL) == fn) ? | |
28 | 0 : -EINVAL; | |
29 | ||
30 | synchronize_net(); | |
31 | ||
32 | return ret; | |
33 | } | |
34 | EXPORT_SYMBOL(inet6_unregister_icmp_sender); | |
35 | ||
ee576c47 JD |
36 | void __icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info, |
37 | const struct inet6_skb_parm *parm) | |
5f5624cf PS |
38 | { |
39 | ip6_icmp_send_t *send; | |
40 | ||
41 | rcu_read_lock(); | |
42 | send = rcu_dereference(ip6_icmp_send); | |
cc7a21b6 | 43 | if (send) |
ee576c47 | 44 | send(skb, type, code, info, NULL, parm); |
5f5624cf PS |
45 | rcu_read_unlock(); |
46 | } | |
ee576c47 | 47 | EXPORT_SYMBOL(__icmpv6_send); |
cc7a21b6 | 48 | #endif |
0b41713b JD |
49 | |
50 | #if IS_ENABLED(CONFIG_NF_NAT) | |
51 | #include <net/netfilter/nf_conntrack.h> | |
52 | void icmpv6_ndo_send(struct sk_buff *skb_in, u8 type, u8 code, __u32 info) | |
53 | { | |
ee576c47 | 54 | struct inet6_skb_parm parm = { 0 }; |
0b41713b JD |
55 | struct sk_buff *cloned_skb = NULL; |
56 | enum ip_conntrack_info ctinfo; | |
57 | struct in6_addr orig_ip; | |
58 | struct nf_conn *ct; | |
59 | ||
60 | ct = nf_ct_get(skb_in, &ctinfo); | |
61 | if (!ct || !(ct->status & IPS_SRC_NAT)) { | |
ee576c47 | 62 | __icmpv6_send(skb_in, type, code, info, &parm); |
0b41713b JD |
63 | return; |
64 | } | |
65 | ||
66 | if (skb_shared(skb_in)) | |
67 | skb_in = cloned_skb = skb_clone(skb_in, GFP_ATOMIC); | |
68 | ||
69 | if (unlikely(!skb_in || skb_network_header(skb_in) < skb_in->head || | |
70 | (skb_network_header(skb_in) + sizeof(struct ipv6hdr)) > | |
71 | skb_tail_pointer(skb_in) || skb_ensure_writable(skb_in, | |
72 | skb_network_offset(skb_in) + sizeof(struct ipv6hdr)))) | |
73 | goto out; | |
74 | ||
75 | orig_ip = ipv6_hdr(skb_in)->saddr; | |
76 | ipv6_hdr(skb_in)->saddr = ct->tuplehash[0].tuple.src.u3.in6; | |
ee576c47 | 77 | __icmpv6_send(skb_in, type, code, info, &parm); |
0b41713b JD |
78 | ipv6_hdr(skb_in)->saddr = orig_ip; |
79 | out: | |
80 | consume_skb(cloned_skb); | |
81 | } | |
82 | EXPORT_SYMBOL(icmpv6_ndo_send); | |
83 | #endif | |
5f5624cf | 84 | #endif |