18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#include <linux/export.h> 38c2ecf20Sopenharmony_ci#include <linux/icmpv6.h> 48c2ecf20Sopenharmony_ci#include <linux/mutex.h> 58c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 68c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <net/ipv6.h> 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#if !IS_BUILTIN(CONFIG_IPV6) 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_cistatic ip6_icmp_send_t __rcu *ip6_icmp_send; 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ciint inet6_register_icmp_sender(ip6_icmp_send_t *fn) 178c2ecf20Sopenharmony_ci{ 188c2ecf20Sopenharmony_ci return (cmpxchg((ip6_icmp_send_t **)&ip6_icmp_send, NULL, fn) == NULL) ? 198c2ecf20Sopenharmony_ci 0 : -EBUSY; 208c2ecf20Sopenharmony_ci} 218c2ecf20Sopenharmony_ciEXPORT_SYMBOL(inet6_register_icmp_sender); 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ciint inet6_unregister_icmp_sender(ip6_icmp_send_t *fn) 248c2ecf20Sopenharmony_ci{ 258c2ecf20Sopenharmony_ci int ret; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci ret = (cmpxchg((ip6_icmp_send_t **)&ip6_icmp_send, fn, NULL) == fn) ? 288c2ecf20Sopenharmony_ci 0 : -EINVAL; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci synchronize_net(); 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci return ret; 338c2ecf20Sopenharmony_ci} 348c2ecf20Sopenharmony_ciEXPORT_SYMBOL(inet6_unregister_icmp_sender); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_civoid __icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info, 378c2ecf20Sopenharmony_ci const struct inet6_skb_parm *parm) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci ip6_icmp_send_t *send; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci rcu_read_lock(); 428c2ecf20Sopenharmony_ci send = rcu_dereference(ip6_icmp_send); 438c2ecf20Sopenharmony_ci if (send) 448c2ecf20Sopenharmony_ci send(skb, type, code, info, NULL, parm); 458c2ecf20Sopenharmony_ci rcu_read_unlock(); 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__icmpv6_send); 488c2ecf20Sopenharmony_ci#endif 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_NF_NAT) 518c2ecf20Sopenharmony_ci#include <net/netfilter/nf_conntrack.h> 528c2ecf20Sopenharmony_civoid icmpv6_ndo_send(struct sk_buff *skb_in, u8 type, u8 code, __u32 info) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci struct inet6_skb_parm parm = { 0 }; 558c2ecf20Sopenharmony_ci struct sk_buff *cloned_skb = NULL; 568c2ecf20Sopenharmony_ci enum ip_conntrack_info ctinfo; 578c2ecf20Sopenharmony_ci struct in6_addr orig_ip; 588c2ecf20Sopenharmony_ci struct nf_conn *ct; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci ct = nf_ct_get(skb_in, &ctinfo); 618c2ecf20Sopenharmony_ci if (!ct || !(ct->status & IPS_SRC_NAT)) { 628c2ecf20Sopenharmony_ci __icmpv6_send(skb_in, type, code, info, &parm); 638c2ecf20Sopenharmony_ci return; 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci if (skb_shared(skb_in)) 678c2ecf20Sopenharmony_ci skb_in = cloned_skb = skb_clone(skb_in, GFP_ATOMIC); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci if (unlikely(!skb_in || skb_network_header(skb_in) < skb_in->head || 708c2ecf20Sopenharmony_ci (skb_network_header(skb_in) + sizeof(struct ipv6hdr)) > 718c2ecf20Sopenharmony_ci skb_tail_pointer(skb_in) || skb_ensure_writable(skb_in, 728c2ecf20Sopenharmony_ci skb_network_offset(skb_in) + sizeof(struct ipv6hdr)))) 738c2ecf20Sopenharmony_ci goto out; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci orig_ip = ipv6_hdr(skb_in)->saddr; 768c2ecf20Sopenharmony_ci ipv6_hdr(skb_in)->saddr = ct->tuplehash[0].tuple.src.u3.in6; 778c2ecf20Sopenharmony_ci __icmpv6_send(skb_in, type, code, info, &parm); 788c2ecf20Sopenharmony_ci ipv6_hdr(skb_in)->saddr = orig_ip; 798c2ecf20Sopenharmony_ciout: 808c2ecf20Sopenharmony_ci consume_skb(cloned_skb); 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ciEXPORT_SYMBOL(icmpv6_ndo_send); 838c2ecf20Sopenharmony_ci#endif 848c2ecf20Sopenharmony_ci#endif 85