xref: /OK3568_Linux_fs/kernel/net/ipv6/ip6_icmp.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun #include <linux/export.h>
3*4882a593Smuzhiyun #include <linux/icmpv6.h>
4*4882a593Smuzhiyun #include <linux/mutex.h>
5*4882a593Smuzhiyun #include <linux/netdevice.h>
6*4882a593Smuzhiyun #include <linux/spinlock.h>
7*4882a593Smuzhiyun 
8*4882a593Smuzhiyun #include <net/ipv6.h>
9*4882a593Smuzhiyun 
10*4882a593Smuzhiyun #if IS_ENABLED(CONFIG_IPV6)
11*4882a593Smuzhiyun 
12*4882a593Smuzhiyun #if !IS_BUILTIN(CONFIG_IPV6)
13*4882a593Smuzhiyun 
14*4882a593Smuzhiyun static ip6_icmp_send_t __rcu *ip6_icmp_send;
15*4882a593Smuzhiyun 
inet6_register_icmp_sender(ip6_icmp_send_t * fn)16*4882a593Smuzhiyun int inet6_register_icmp_sender(ip6_icmp_send_t *fn)
17*4882a593Smuzhiyun {
18*4882a593Smuzhiyun 	return (cmpxchg((ip6_icmp_send_t **)&ip6_icmp_send, NULL, fn) == NULL) ?
19*4882a593Smuzhiyun 		0 : -EBUSY;
20*4882a593Smuzhiyun }
21*4882a593Smuzhiyun EXPORT_SYMBOL(inet6_register_icmp_sender);
22*4882a593Smuzhiyun 
inet6_unregister_icmp_sender(ip6_icmp_send_t * fn)23*4882a593Smuzhiyun int inet6_unregister_icmp_sender(ip6_icmp_send_t *fn)
24*4882a593Smuzhiyun {
25*4882a593Smuzhiyun 	int ret;
26*4882a593Smuzhiyun 
27*4882a593Smuzhiyun 	ret = (cmpxchg((ip6_icmp_send_t **)&ip6_icmp_send, fn, NULL) == fn) ?
28*4882a593Smuzhiyun 	      0 : -EINVAL;
29*4882a593Smuzhiyun 
30*4882a593Smuzhiyun 	synchronize_net();
31*4882a593Smuzhiyun 
32*4882a593Smuzhiyun 	return ret;
33*4882a593Smuzhiyun }
34*4882a593Smuzhiyun EXPORT_SYMBOL(inet6_unregister_icmp_sender);
35*4882a593Smuzhiyun 
__icmpv6_send(struct sk_buff * skb,u8 type,u8 code,__u32 info,const struct inet6_skb_parm * parm)36*4882a593Smuzhiyun void __icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
37*4882a593Smuzhiyun 		   const struct inet6_skb_parm *parm)
38*4882a593Smuzhiyun {
39*4882a593Smuzhiyun 	ip6_icmp_send_t *send;
40*4882a593Smuzhiyun 
41*4882a593Smuzhiyun 	rcu_read_lock();
42*4882a593Smuzhiyun 	send = rcu_dereference(ip6_icmp_send);
43*4882a593Smuzhiyun 	if (send)
44*4882a593Smuzhiyun 		send(skb, type, code, info, NULL, parm);
45*4882a593Smuzhiyun 	rcu_read_unlock();
46*4882a593Smuzhiyun }
47*4882a593Smuzhiyun EXPORT_SYMBOL(__icmpv6_send);
48*4882a593Smuzhiyun #endif
49*4882a593Smuzhiyun 
50*4882a593Smuzhiyun #if IS_ENABLED(CONFIG_NF_NAT)
51*4882a593Smuzhiyun #include <net/netfilter/nf_conntrack.h>
icmpv6_ndo_send(struct sk_buff * skb_in,u8 type,u8 code,__u32 info)52*4882a593Smuzhiyun void icmpv6_ndo_send(struct sk_buff *skb_in, u8 type, u8 code, __u32 info)
53*4882a593Smuzhiyun {
54*4882a593Smuzhiyun 	struct inet6_skb_parm parm = { 0 };
55*4882a593Smuzhiyun 	struct sk_buff *cloned_skb = NULL;
56*4882a593Smuzhiyun 	enum ip_conntrack_info ctinfo;
57*4882a593Smuzhiyun 	struct in6_addr orig_ip;
58*4882a593Smuzhiyun 	struct nf_conn *ct;
59*4882a593Smuzhiyun 
60*4882a593Smuzhiyun 	ct = nf_ct_get(skb_in, &ctinfo);
61*4882a593Smuzhiyun 	if (!ct || !(ct->status & IPS_SRC_NAT)) {
62*4882a593Smuzhiyun 		__icmpv6_send(skb_in, type, code, info, &parm);
63*4882a593Smuzhiyun 		return;
64*4882a593Smuzhiyun 	}
65*4882a593Smuzhiyun 
66*4882a593Smuzhiyun 	if (skb_shared(skb_in))
67*4882a593Smuzhiyun 		skb_in = cloned_skb = skb_clone(skb_in, GFP_ATOMIC);
68*4882a593Smuzhiyun 
69*4882a593Smuzhiyun 	if (unlikely(!skb_in || skb_network_header(skb_in) < skb_in->head ||
70*4882a593Smuzhiyun 	    (skb_network_header(skb_in) + sizeof(struct ipv6hdr)) >
71*4882a593Smuzhiyun 	    skb_tail_pointer(skb_in) || skb_ensure_writable(skb_in,
72*4882a593Smuzhiyun 	    skb_network_offset(skb_in) + sizeof(struct ipv6hdr))))
73*4882a593Smuzhiyun 		goto out;
74*4882a593Smuzhiyun 
75*4882a593Smuzhiyun 	orig_ip = ipv6_hdr(skb_in)->saddr;
76*4882a593Smuzhiyun 	ipv6_hdr(skb_in)->saddr = ct->tuplehash[0].tuple.src.u3.in6;
77*4882a593Smuzhiyun 	__icmpv6_send(skb_in, type, code, info, &parm);
78*4882a593Smuzhiyun 	ipv6_hdr(skb_in)->saddr = orig_ip;
79*4882a593Smuzhiyun out:
80*4882a593Smuzhiyun 	consume_skb(cloned_skb);
81*4882a593Smuzhiyun }
82*4882a593Smuzhiyun EXPORT_SYMBOL(icmpv6_ndo_send);
83*4882a593Smuzhiyun #endif
84*4882a593Smuzhiyun #endif
85