1 | // SPDX-License-Identifier: GPL-2.0 |
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 | |
12 | #if !IS_BUILTIN(CONFIG_IPV6) |
13 | |
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) ? |
19 | 0 : -EBUSY; |
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 | |
36 | void __icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info, |
37 | const struct inet6_skb_parm *parm) |
38 | { |
39 | ip6_icmp_send_t *send; |
40 | |
41 | rcu_read_lock(); |
42 | send = rcu_dereference(ip6_icmp_send); |
43 | if (send) |
44 | send(skb, type, code, info, NULL, parm); |
45 | rcu_read_unlock(); |
46 | } |
47 | EXPORT_SYMBOL(__icmpv6_send); |
48 | #endif |
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 | { |
54 | struct inet6_skb_parm parm = { 0 }; |
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: skb_in, ctinfo: &ctinfo); |
61 | if (!ct || !(ct->status & IPS_SRC_NAT)) { |
62 | __icmpv6_send(skb: skb_in, type, code, info, parm: &parm); |
63 | return; |
64 | } |
65 | |
66 | if (skb_shared(skb: skb_in)) |
67 | skb_in = cloned_skb = skb_clone(skb: 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: skb_in)->saddr; |
76 | ipv6_hdr(skb: skb_in)->saddr = ct->tuplehash[0].tuple.src.u3.in6; |
77 | __icmpv6_send(skb: skb_in, type, code, info, parm: &parm); |
78 | ipv6_hdr(skb: skb_in)->saddr = orig_ip; |
79 | out: |
80 | consume_skb(skb: cloned_skb); |
81 | } |
82 | EXPORT_SYMBOL(icmpv6_ndo_send); |
83 | #endif |
84 | #endif |
85 | |