162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#include <linux/export.h> 362306a36Sopenharmony_ci#include <linux/icmpv6.h> 462306a36Sopenharmony_ci#include <linux/mutex.h> 562306a36Sopenharmony_ci#include <linux/netdevice.h> 662306a36Sopenharmony_ci#include <linux/spinlock.h> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <net/ipv6.h> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#if !IS_BUILTIN(CONFIG_IPV6) 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_cistatic ip6_icmp_send_t __rcu *ip6_icmp_send; 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ciint inet6_register_icmp_sender(ip6_icmp_send_t *fn) 1762306a36Sopenharmony_ci{ 1862306a36Sopenharmony_ci return (cmpxchg((ip6_icmp_send_t **)&ip6_icmp_send, NULL, fn) == NULL) ? 1962306a36Sopenharmony_ci 0 : -EBUSY; 2062306a36Sopenharmony_ci} 2162306a36Sopenharmony_ciEXPORT_SYMBOL(inet6_register_icmp_sender); 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ciint inet6_unregister_icmp_sender(ip6_icmp_send_t *fn) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci int ret; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci ret = (cmpxchg((ip6_icmp_send_t **)&ip6_icmp_send, fn, NULL) == fn) ? 2862306a36Sopenharmony_ci 0 : -EINVAL; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci synchronize_net(); 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci return ret; 3362306a36Sopenharmony_ci} 3462306a36Sopenharmony_ciEXPORT_SYMBOL(inet6_unregister_icmp_sender); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_civoid __icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info, 3762306a36Sopenharmony_ci const struct inet6_skb_parm *parm) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci ip6_icmp_send_t *send; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci rcu_read_lock(); 4262306a36Sopenharmony_ci send = rcu_dereference(ip6_icmp_send); 4362306a36Sopenharmony_ci if (send) 4462306a36Sopenharmony_ci send(skb, type, code, info, NULL, parm); 4562306a36Sopenharmony_ci rcu_read_unlock(); 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ciEXPORT_SYMBOL(__icmpv6_send); 4862306a36Sopenharmony_ci#endif 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_NF_NAT) 5162306a36Sopenharmony_ci#include <net/netfilter/nf_conntrack.h> 5262306a36Sopenharmony_civoid icmpv6_ndo_send(struct sk_buff *skb_in, u8 type, u8 code, __u32 info) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci struct inet6_skb_parm parm = { 0 }; 5562306a36Sopenharmony_ci struct sk_buff *cloned_skb = NULL; 5662306a36Sopenharmony_ci enum ip_conntrack_info ctinfo; 5762306a36Sopenharmony_ci struct in6_addr orig_ip; 5862306a36Sopenharmony_ci struct nf_conn *ct; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci ct = nf_ct_get(skb_in, &ctinfo); 6162306a36Sopenharmony_ci if (!ct || !(ct->status & IPS_SRC_NAT)) { 6262306a36Sopenharmony_ci __icmpv6_send(skb_in, type, code, info, &parm); 6362306a36Sopenharmony_ci return; 6462306a36Sopenharmony_ci } 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci if (skb_shared(skb_in)) 6762306a36Sopenharmony_ci skb_in = cloned_skb = skb_clone(skb_in, GFP_ATOMIC); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci if (unlikely(!skb_in || skb_network_header(skb_in) < skb_in->head || 7062306a36Sopenharmony_ci (skb_network_header(skb_in) + sizeof(struct ipv6hdr)) > 7162306a36Sopenharmony_ci skb_tail_pointer(skb_in) || skb_ensure_writable(skb_in, 7262306a36Sopenharmony_ci skb_network_offset(skb_in) + sizeof(struct ipv6hdr)))) 7362306a36Sopenharmony_ci goto out; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci orig_ip = ipv6_hdr(skb_in)->saddr; 7662306a36Sopenharmony_ci ipv6_hdr(skb_in)->saddr = ct->tuplehash[0].tuple.src.u3.in6; 7762306a36Sopenharmony_ci __icmpv6_send(skb_in, type, code, info, &parm); 7862306a36Sopenharmony_ci ipv6_hdr(skb_in)->saddr = orig_ip; 7962306a36Sopenharmony_ciout: 8062306a36Sopenharmony_ci consume_skb(cloned_skb); 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ciEXPORT_SYMBOL(icmpv6_ndo_send); 8362306a36Sopenharmony_ci#endif 8462306a36Sopenharmony_ci#endif 85