| 1 | |
| 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
|