0001
0002 #include <linux/export.h>
0003 #include <linux/icmpv6.h>
0004 #include <linux/mutex.h>
0005 #include <linux/netdevice.h>
0006 #include <linux/spinlock.h>
0007
0008 #include <net/ipv6.h>
0009
0010 #if IS_ENABLED(CONFIG_IPV6)
0011
0012 #if !IS_BUILTIN(CONFIG_IPV6)
0013
0014 static ip6_icmp_send_t __rcu *ip6_icmp_send;
0015
0016 int inet6_register_icmp_sender(ip6_icmp_send_t *fn)
0017 {
0018 return (cmpxchg((ip6_icmp_send_t **)&ip6_icmp_send, NULL, fn) == NULL) ?
0019 0 : -EBUSY;
0020 }
0021 EXPORT_SYMBOL(inet6_register_icmp_sender);
0022
0023 int inet6_unregister_icmp_sender(ip6_icmp_send_t *fn)
0024 {
0025 int ret;
0026
0027 ret = (cmpxchg((ip6_icmp_send_t **)&ip6_icmp_send, fn, NULL) == fn) ?
0028 0 : -EINVAL;
0029
0030 synchronize_net();
0031
0032 return ret;
0033 }
0034 EXPORT_SYMBOL(inet6_unregister_icmp_sender);
0035
0036 void __icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
0037 const struct inet6_skb_parm *parm)
0038 {
0039 ip6_icmp_send_t *send;
0040
0041 rcu_read_lock();
0042 send = rcu_dereference(ip6_icmp_send);
0043 if (send)
0044 send(skb, type, code, info, NULL, parm);
0045 rcu_read_unlock();
0046 }
0047 EXPORT_SYMBOL(__icmpv6_send);
0048 #endif
0049
0050 #if IS_ENABLED(CONFIG_NF_NAT)
0051 #include <net/netfilter/nf_conntrack.h>
0052 void icmpv6_ndo_send(struct sk_buff *skb_in, u8 type, u8 code, __u32 info)
0053 {
0054 struct inet6_skb_parm parm = { 0 };
0055 struct sk_buff *cloned_skb = NULL;
0056 enum ip_conntrack_info ctinfo;
0057 struct in6_addr orig_ip;
0058 struct nf_conn *ct;
0059
0060 ct = nf_ct_get(skb_in, &ctinfo);
0061 if (!ct || !(ct->status & IPS_SRC_NAT)) {
0062 __icmpv6_send(skb_in, type, code, info, &parm);
0063 return;
0064 }
0065
0066 if (skb_shared(skb_in))
0067 skb_in = cloned_skb = skb_clone(skb_in, GFP_ATOMIC);
0068
0069 if (unlikely(!skb_in || skb_network_header(skb_in) < skb_in->head ||
0070 (skb_network_header(skb_in) + sizeof(struct ipv6hdr)) >
0071 skb_tail_pointer(skb_in) || skb_ensure_writable(skb_in,
0072 skb_network_offset(skb_in) + sizeof(struct ipv6hdr))))
0073 goto out;
0074
0075 orig_ip = ipv6_hdr(skb_in)->saddr;
0076 ipv6_hdr(skb_in)->saddr = ct->tuplehash[0].tuple.src.u3.in6;
0077 __icmpv6_send(skb_in, type, code, info, &parm);
0078 ipv6_hdr(skb_in)->saddr = orig_ip;
0079 out:
0080 consume_skb(cloned_skb);
0081 }
0082 EXPORT_SYMBOL(icmpv6_ndo_send);
0083 #endif
0084 #endif