xref: /openbmc/linux/net/ipv6/ip6_icmp.c (revision ee576c47)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
25f5624cfSPravin B Shelar #include <linux/export.h>
35f5624cfSPravin B Shelar #include <linux/icmpv6.h>
45f5624cfSPravin B Shelar #include <linux/mutex.h>
55f5624cfSPravin B Shelar #include <linux/netdevice.h>
65f5624cfSPravin B Shelar #include <linux/spinlock.h>
75f5624cfSPravin B Shelar 
85f5624cfSPravin B Shelar #include <net/ipv6.h>
95f5624cfSPravin B Shelar 
105f5624cfSPravin B Shelar #if IS_ENABLED(CONFIG_IPV6)
115f5624cfSPravin B Shelar 
12cc7a21b6SEric Dumazet #if !IS_BUILTIN(CONFIG_IPV6)
13cc7a21b6SEric Dumazet 
145f5624cfSPravin B Shelar static ip6_icmp_send_t __rcu *ip6_icmp_send;
155f5624cfSPravin B Shelar 
inet6_register_icmp_sender(ip6_icmp_send_t * fn)165f5624cfSPravin B Shelar int inet6_register_icmp_sender(ip6_icmp_send_t *fn)
175f5624cfSPravin B Shelar {
185f5624cfSPravin B Shelar 	return (cmpxchg((ip6_icmp_send_t **)&ip6_icmp_send, NULL, fn) == NULL) ?
195f5624cfSPravin B Shelar 		0 : -EBUSY;
205f5624cfSPravin B Shelar }
215f5624cfSPravin B Shelar EXPORT_SYMBOL(inet6_register_icmp_sender);
225f5624cfSPravin B Shelar 
inet6_unregister_icmp_sender(ip6_icmp_send_t * fn)235f5624cfSPravin B Shelar int inet6_unregister_icmp_sender(ip6_icmp_send_t *fn)
245f5624cfSPravin B Shelar {
255f5624cfSPravin B Shelar 	int ret;
265f5624cfSPravin B Shelar 
275f5624cfSPravin B Shelar 	ret = (cmpxchg((ip6_icmp_send_t **)&ip6_icmp_send, fn, NULL) == fn) ?
285f5624cfSPravin B Shelar 	      0 : -EINVAL;
295f5624cfSPravin B Shelar 
305f5624cfSPravin B Shelar 	synchronize_net();
315f5624cfSPravin B Shelar 
325f5624cfSPravin B Shelar 	return ret;
335f5624cfSPravin B Shelar }
345f5624cfSPravin B Shelar EXPORT_SYMBOL(inet6_unregister_icmp_sender);
355f5624cfSPravin B Shelar 
__icmpv6_send(struct sk_buff * skb,u8 type,u8 code,__u32 info,const struct inet6_skb_parm * parm)36*ee576c47SJason A. Donenfeld void __icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
37*ee576c47SJason A. Donenfeld 		   const struct inet6_skb_parm *parm)
385f5624cfSPravin B Shelar {
395f5624cfSPravin B Shelar 	ip6_icmp_send_t *send;
405f5624cfSPravin B Shelar 
415f5624cfSPravin B Shelar 	rcu_read_lock();
425f5624cfSPravin B Shelar 	send = rcu_dereference(ip6_icmp_send);
43cc7a21b6SEric Dumazet 	if (send)
44*ee576c47SJason A. Donenfeld 		send(skb, type, code, info, NULL, parm);
455f5624cfSPravin B Shelar 	rcu_read_unlock();
465f5624cfSPravin B Shelar }
47*ee576c47SJason A. Donenfeld EXPORT_SYMBOL(__icmpv6_send);
48cc7a21b6SEric Dumazet #endif
490b41713bSJason A. Donenfeld 
500b41713bSJason A. Donenfeld #if IS_ENABLED(CONFIG_NF_NAT)
510b41713bSJason A. Donenfeld #include <net/netfilter/nf_conntrack.h>
icmpv6_ndo_send(struct sk_buff * skb_in,u8 type,u8 code,__u32 info)520b41713bSJason A. Donenfeld void icmpv6_ndo_send(struct sk_buff *skb_in, u8 type, u8 code, __u32 info)
530b41713bSJason A. Donenfeld {
54*ee576c47SJason A. Donenfeld 	struct inet6_skb_parm parm = { 0 };
550b41713bSJason A. Donenfeld 	struct sk_buff *cloned_skb = NULL;
560b41713bSJason A. Donenfeld 	enum ip_conntrack_info ctinfo;
570b41713bSJason A. Donenfeld 	struct in6_addr orig_ip;
580b41713bSJason A. Donenfeld 	struct nf_conn *ct;
590b41713bSJason A. Donenfeld 
600b41713bSJason A. Donenfeld 	ct = nf_ct_get(skb_in, &ctinfo);
610b41713bSJason A. Donenfeld 	if (!ct || !(ct->status & IPS_SRC_NAT)) {
62*ee576c47SJason A. Donenfeld 		__icmpv6_send(skb_in, type, code, info, &parm);
630b41713bSJason A. Donenfeld 		return;
640b41713bSJason A. Donenfeld 	}
650b41713bSJason A. Donenfeld 
660b41713bSJason A. Donenfeld 	if (skb_shared(skb_in))
670b41713bSJason A. Donenfeld 		skb_in = cloned_skb = skb_clone(skb_in, GFP_ATOMIC);
680b41713bSJason A. Donenfeld 
690b41713bSJason A. Donenfeld 	if (unlikely(!skb_in || skb_network_header(skb_in) < skb_in->head ||
700b41713bSJason A. Donenfeld 	    (skb_network_header(skb_in) + sizeof(struct ipv6hdr)) >
710b41713bSJason A. Donenfeld 	    skb_tail_pointer(skb_in) || skb_ensure_writable(skb_in,
720b41713bSJason A. Donenfeld 	    skb_network_offset(skb_in) + sizeof(struct ipv6hdr))))
730b41713bSJason A. Donenfeld 		goto out;
740b41713bSJason A. Donenfeld 
750b41713bSJason A. Donenfeld 	orig_ip = ipv6_hdr(skb_in)->saddr;
760b41713bSJason A. Donenfeld 	ipv6_hdr(skb_in)->saddr = ct->tuplehash[0].tuple.src.u3.in6;
77*ee576c47SJason A. Donenfeld 	__icmpv6_send(skb_in, type, code, info, &parm);
780b41713bSJason A. Donenfeld 	ipv6_hdr(skb_in)->saddr = orig_ip;
790b41713bSJason A. Donenfeld out:
800b41713bSJason A. Donenfeld 	consume_skb(cloned_skb);
810b41713bSJason A. Donenfeld }
820b41713bSJason A. Donenfeld EXPORT_SYMBOL(icmpv6_ndo_send);
830b41713bSJason A. Donenfeld #endif
845f5624cfSPravin B Shelar #endif
85