xref: /linux/net/ipv6/ip6_icmp.c (revision dfecb0c5af3b07ebfa84be63a7a21bfc9e29a872)
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) && IS_ENABLED(CONFIG_NF_NAT)
11 
12 #include <net/netfilter/nf_conntrack.h>
13 void icmpv6_ndo_send(struct sk_buff *skb_in, u8 type, u8 code, __u32 info)
14 {
15 	struct inet6_skb_parm parm = { 0 };
16 	struct sk_buff *cloned_skb = NULL;
17 	enum ip_conntrack_info ctinfo;
18 	enum ip_conntrack_dir dir;
19 	struct in6_addr orig_ip;
20 	struct nf_conn *ct;
21 
22 	ct = nf_ct_get(skb_in, &ctinfo);
23 	if (!ct || !(READ_ONCE(ct->status) & IPS_NAT_MASK)) {
24 		icmp6_send(skb_in, type, code, info, NULL, &parm);
25 		return;
26 	}
27 
28 	if (skb_shared(skb_in))
29 		skb_in = cloned_skb = skb_clone(skb_in, GFP_ATOMIC);
30 
31 	if (unlikely(!skb_in || skb_network_header(skb_in) < skb_in->head ||
32 	    (skb_network_header(skb_in) + sizeof(struct ipv6hdr)) >
33 	    skb_tail_pointer(skb_in) || skb_ensure_writable(skb_in,
34 	    skb_network_offset(skb_in) + sizeof(struct ipv6hdr))))
35 		goto out;
36 
37 	orig_ip = ipv6_hdr(skb_in)->saddr;
38 	dir = CTINFO2DIR(ctinfo);
39 	ipv6_hdr(skb_in)->saddr = ct->tuplehash[dir].tuple.src.u3.in6;
40 	icmp6_send(skb_in, type, code, info, NULL, &parm);
41 	ipv6_hdr(skb_in)->saddr = orig_ip;
42 out:
43 	consume_skb(cloned_skb);
44 }
45 EXPORT_SYMBOL(icmpv6_ndo_send);
46 #endif
47