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 static ip6_icmp_send_t __rcu *ip6_icmp_send; 13 14 int inet6_register_icmp_sender(ip6_icmp_send_t *fn) 15 { 16 return (cmpxchg((ip6_icmp_send_t **)&ip6_icmp_send, NULL, fn) == NULL) ? 17 0 : -EBUSY; 18 } 19 EXPORT_SYMBOL(inet6_register_icmp_sender); 20 21 int inet6_unregister_icmp_sender(ip6_icmp_send_t *fn) 22 { 23 int ret; 24 25 ret = (cmpxchg((ip6_icmp_send_t **)&ip6_icmp_send, fn, NULL) == fn) ? 26 0 : -EINVAL; 27 28 synchronize_net(); 29 30 return ret; 31 } 32 EXPORT_SYMBOL(inet6_unregister_icmp_sender); 33 34 void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info) 35 { 36 ip6_icmp_send_t *send; 37 38 rcu_read_lock(); 39 send = rcu_dereference(ip6_icmp_send); 40 41 if (!send) 42 goto out; 43 send(skb, type, code, info, NULL); 44 out: 45 rcu_read_unlock(); 46 } 47 EXPORT_SYMBOL(icmpv6_send); 48 49 #if IS_ENABLED(CONFIG_NF_NAT) 50 #include <net/netfilter/nf_conntrack.h> 51 void icmpv6_ndo_send(struct sk_buff *skb_in, u8 type, u8 code, __u32 info) 52 { 53 struct sk_buff *cloned_skb = NULL; 54 enum ip_conntrack_info ctinfo; 55 struct in6_addr orig_ip; 56 struct nf_conn *ct; 57 58 ct = nf_ct_get(skb_in, &ctinfo); 59 if (!ct || !(ct->status & IPS_SRC_NAT)) { 60 icmpv6_send(skb_in, type, code, info); 61 return; 62 } 63 64 if (skb_shared(skb_in)) 65 skb_in = cloned_skb = skb_clone(skb_in, GFP_ATOMIC); 66 67 if (unlikely(!skb_in || skb_network_header(skb_in) < skb_in->head || 68 (skb_network_header(skb_in) + sizeof(struct ipv6hdr)) > 69 skb_tail_pointer(skb_in) || skb_ensure_writable(skb_in, 70 skb_network_offset(skb_in) + sizeof(struct ipv6hdr)))) 71 goto out; 72 73 orig_ip = ipv6_hdr(skb_in)->saddr; 74 ipv6_hdr(skb_in)->saddr = ct->tuplehash[0].tuple.src.u3.in6; 75 icmpv6_send(skb_in, type, code, info); 76 ipv6_hdr(skb_in)->saddr = orig_ip; 77 out: 78 consume_skb(cloned_skb); 79 } 80 EXPORT_SYMBOL(icmpv6_ndo_send); 81 #endif 82 #endif 83