diff options
Diffstat (limited to 'net/ipv4/icmp.c')
| -rw-r--r-- | net/ipv4/icmp.c | 33 | 
1 files changed, 33 insertions, 0 deletions
diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index 18068ed42f25..f369e7ce685b 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -748,6 +748,39 @@ out:;  }  EXPORT_SYMBOL(__icmp_send); +#if IS_ENABLED(CONFIG_NF_NAT) +#include <net/netfilter/nf_conntrack.h> +void icmp_ndo_send(struct sk_buff *skb_in, int type, int code, __be32 info) +{ +	struct sk_buff *cloned_skb = NULL; +	enum ip_conntrack_info ctinfo; +	struct nf_conn *ct; +	__be32 orig_ip; + +	ct = nf_ct_get(skb_in, &ctinfo); +	if (!ct || !(ct->status & IPS_SRC_NAT)) { +		icmp_send(skb_in, type, code, info); +		return; +	} + +	if (skb_shared(skb_in)) +		skb_in = cloned_skb = skb_clone(skb_in, GFP_ATOMIC); + +	if (unlikely(!skb_in || skb_network_header(skb_in) < skb_in->head || +	    (skb_network_header(skb_in) + sizeof(struct iphdr)) > +	    skb_tail_pointer(skb_in) || skb_ensure_writable(skb_in, +	    skb_network_offset(skb_in) + sizeof(struct iphdr)))) +		goto out; + +	orig_ip = ip_hdr(skb_in)->saddr; +	ip_hdr(skb_in)->saddr = ct->tuplehash[0].tuple.src.u3.ip; +	icmp_send(skb_in, type, code, info); +	ip_hdr(skb_in)->saddr = orig_ip; +out: +	consume_skb(cloned_skb); +} +EXPORT_SYMBOL(icmp_ndo_send); +#endif  static void icmp_socket_deliver(struct sk_buff *skb, u32 info)  {  |