Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

icmp: introduce helper for nat'd source address in network device context

This introduces a helper function to be called only by network drivers
that wraps calls to icmp[v6]_send in a conntrack transformation, in case
NAT has been used. We don't want to pollute the non-driver path, though,
so we introduce this as a helper to be called by places that actually
make use of this, as suggested by Florian.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
Cc: Florian Westphal <fw@strlen.de>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Jason A. Donenfeld and committed by
David S. Miller
0b41713b 07134cf6

+79
+6
include/linux/icmpv6.h
··· 31 31 } 32 32 #endif 33 33 34 + #if IS_ENABLED(CONFIG_NF_NAT) 35 + void icmpv6_ndo_send(struct sk_buff *skb_in, u8 type, u8 code, __u32 info); 36 + #else 37 + #define icmpv6_ndo_send icmpv6_send 38 + #endif 39 + 34 40 extern int icmpv6_init(void); 35 41 extern int icmpv6_err_convert(u8 type, u8 code, 36 42 int *err);
+6
include/net/icmp.h
··· 43 43 __icmp_send(skb_in, type, code, info, &IPCB(skb_in)->opt); 44 44 } 45 45 46 + #if IS_ENABLED(CONFIG_NF_NAT) 47 + void icmp_ndo_send(struct sk_buff *skb_in, int type, int code, __be32 info); 48 + #else 49 + #define icmp_ndo_send icmp_send 50 + #endif 51 + 46 52 int icmp_rcv(struct sk_buff *skb); 47 53 int icmp_err(struct sk_buff *skb, u32 info); 48 54 int icmp_init(void);
+33
net/ipv4/icmp.c
··· 748 748 } 749 749 EXPORT_SYMBOL(__icmp_send); 750 750 751 + #if IS_ENABLED(CONFIG_NF_NAT) 752 + #include <net/netfilter/nf_conntrack.h> 753 + void icmp_ndo_send(struct sk_buff *skb_in, int type, int code, __be32 info) 754 + { 755 + struct sk_buff *cloned_skb = NULL; 756 + enum ip_conntrack_info ctinfo; 757 + struct nf_conn *ct; 758 + __be32 orig_ip; 759 + 760 + ct = nf_ct_get(skb_in, &ctinfo); 761 + if (!ct || !(ct->status & IPS_SRC_NAT)) { 762 + icmp_send(skb_in, type, code, info); 763 + return; 764 + } 765 + 766 + if (skb_shared(skb_in)) 767 + skb_in = cloned_skb = skb_clone(skb_in, GFP_ATOMIC); 768 + 769 + if (unlikely(!skb_in || skb_network_header(skb_in) < skb_in->head || 770 + (skb_network_header(skb_in) + sizeof(struct iphdr)) > 771 + skb_tail_pointer(skb_in) || skb_ensure_writable(skb_in, 772 + skb_network_offset(skb_in) + sizeof(struct iphdr)))) 773 + goto out; 774 + 775 + orig_ip = ip_hdr(skb_in)->saddr; 776 + ip_hdr(skb_in)->saddr = ct->tuplehash[0].tuple.src.u3.ip; 777 + icmp_send(skb_in, type, code, info); 778 + ip_hdr(skb_in)->saddr = orig_ip; 779 + out: 780 + consume_skb(cloned_skb); 781 + } 782 + EXPORT_SYMBOL(icmp_ndo_send); 783 + #endif 751 784 752 785 static void icmp_socket_deliver(struct sk_buff *skb, u32 info) 753 786 {
+34
net/ipv6/ip6_icmp.c
··· 45 45 rcu_read_unlock(); 46 46 } 47 47 EXPORT_SYMBOL(icmpv6_send); 48 + 49 + #if IS_ENABLED(CONFIG_NF_NAT) 50 + #include <net/netfilter/nf_conntrack.h> 51 + void icmpv6_ndo_send(struct sk_buff *skb_in, u8 type, u8 code, __u32 info) 52 + { 53 + struct sk_buff *cloned_skb = NULL; 54 + enum ip_conntrack_info ctinfo; 55 + struct in6_addr orig_ip; 56 + struct nf_conn *ct; 57 + 58 + ct = nf_ct_get(skb_in, &ctinfo); 59 + if (!ct || !(ct->status & IPS_SRC_NAT)) { 60 + icmpv6_send(skb_in, type, code, info); 61 + return; 62 + } 63 + 64 + if (skb_shared(skb_in)) 65 + skb_in = cloned_skb = skb_clone(skb_in, GFP_ATOMIC); 66 + 67 + if (unlikely(!skb_in || skb_network_header(skb_in) < skb_in->head || 68 + (skb_network_header(skb_in) + sizeof(struct ipv6hdr)) > 69 + skb_tail_pointer(skb_in) || skb_ensure_writable(skb_in, 70 + skb_network_offset(skb_in) + sizeof(struct ipv6hdr)))) 71 + goto out; 72 + 73 + orig_ip = ipv6_hdr(skb_in)->saddr; 74 + ipv6_hdr(skb_in)->saddr = ct->tuplehash[0].tuple.src.u3.in6; 75 + icmpv6_send(skb_in, type, code, info); 76 + ipv6_hdr(skb_in)->saddr = orig_ip; 77 + out: 78 + consume_skb(cloned_skb); 79 + } 80 + EXPORT_SYMBOL(icmpv6_ndo_send); 81 + #endif 48 82 #endif