projects/linux·blob master · refs

46 lines · 1.3 KiB

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>
13void 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;
42out:
43 consume_skb(cloned_skb);
44}
45EXPORT_SYMBOL(icmpv6_ndo_send);
46#endif