8b53cc62260a10292621aff854ff5aede0562cf2
[openwrt/staging/aparcar.git] /
1 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
2 From: "Jason A. Donenfeld" <Jason@zx2c4.com>
3 Date: Tue, 11 Feb 2020 20:47:05 +0100
4 Subject: [PATCH] icmp: introduce helper for nat'd source address in network
5 device context
6
7 commit 0b41713b606694257b90d61ba7e2712d8457648b upstream.
8
9 This introduces a helper function to be called only by network drivers
10 that wraps calls to icmp[v6]_send in a conntrack transformation, in case
11 NAT has been used. We don't want to pollute the non-driver path, though,
12 so we introduce this as a helper to be called by places that actually
13 make use of this, as suggested by Florian.
14
15 Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
16 Cc: Florian Westphal <fw@strlen.de>
17 Signed-off-by: David S. Miller <davem@davemloft.net>
18 Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
19 ---
20 include/linux/icmpv6.h | 10 ++++++++++
21 include/net/icmp.h | 6 ++++++
22 net/ipv4/icmp.c | 33 +++++++++++++++++++++++++++++++++
23 net/ipv6/ip6_icmp.c | 34 ++++++++++++++++++++++++++++++++++
24 4 files changed, 83 insertions(+)
25
26 --- a/include/linux/icmpv6.h
27 +++ b/include/linux/icmpv6.h
28 @@ -22,12 +22,22 @@ extern int inet6_unregister_icmp_sender(
29 int ip6_err_gen_icmpv6_unreach(struct sk_buff *skb, int nhs, int type,
30 unsigned int data_len);
31
32 +#if IS_ENABLED(CONFIG_NF_NAT)
33 +void icmpv6_ndo_send(struct sk_buff *skb_in, u8 type, u8 code, __u32 info);
34 +#else
35 +#define icmpv6_ndo_send icmpv6_send
36 +#endif
37 +
38 #else
39
40 static inline void icmpv6_send(struct sk_buff *skb,
41 u8 type, u8 code, __u32 info)
42 {
43 +}
44
45 +static inline void icmpv6_ndo_send(struct sk_buff *skb,
46 + u8 type, u8 code, __u32 info)
47 +{
48 }
49 #endif
50
51 --- a/include/net/icmp.h
52 +++ b/include/net/icmp.h
53 @@ -43,6 +43,12 @@ static inline void icmp_send(struct sk_b
54 __icmp_send(skb_in, type, code, info, &IPCB(skb_in)->opt);
55 }
56
57 +#if IS_ENABLED(CONFIG_NF_NAT)
58 +void icmp_ndo_send(struct sk_buff *skb_in, int type, int code, __be32 info);
59 +#else
60 +#define icmp_ndo_send icmp_send
61 +#endif
62 +
63 int icmp_rcv(struct sk_buff *skb);
64 int icmp_err(struct sk_buff *skb, u32 info);
65 int icmp_init(void);
66 --- a/net/ipv4/icmp.c
67 +++ b/net/ipv4/icmp.c
68 @@ -750,6 +750,39 @@ out:;
69 }
70 EXPORT_SYMBOL(__icmp_send);
71
72 +#if IS_ENABLED(CONFIG_NF_NAT)
73 +#include <net/netfilter/nf_conntrack.h>
74 +void icmp_ndo_send(struct sk_buff *skb_in, int type, int code, __be32 info)
75 +{
76 + struct sk_buff *cloned_skb = NULL;
77 + enum ip_conntrack_info ctinfo;
78 + struct nf_conn *ct;
79 + __be32 orig_ip;
80 +
81 + ct = nf_ct_get(skb_in, &ctinfo);
82 + if (!ct || !(ct->status & IPS_SRC_NAT)) {
83 + icmp_send(skb_in, type, code, info);
84 + return;
85 + }
86 +
87 + if (skb_shared(skb_in))
88 + skb_in = cloned_skb = skb_clone(skb_in, GFP_ATOMIC);
89 +
90 + if (unlikely(!skb_in || skb_network_header(skb_in) < skb_in->head ||
91 + (skb_network_header(skb_in) + sizeof(struct iphdr)) >
92 + skb_tail_pointer(skb_in) || skb_ensure_writable(skb_in,
93 + skb_network_offset(skb_in) + sizeof(struct iphdr))))
94 + goto out;
95 +
96 + orig_ip = ip_hdr(skb_in)->saddr;
97 + ip_hdr(skb_in)->saddr = ct->tuplehash[0].tuple.src.u3.ip;
98 + icmp_send(skb_in, type, code, info);
99 + ip_hdr(skb_in)->saddr = orig_ip;
100 +out:
101 + consume_skb(cloned_skb);
102 +}
103 +EXPORT_SYMBOL(icmp_ndo_send);
104 +#endif
105
106 static void icmp_socket_deliver(struct sk_buff *skb, u32 info)
107 {
108 --- a/net/ipv6/ip6_icmp.c
109 +++ b/net/ipv6/ip6_icmp.c
110 @@ -45,4 +45,38 @@ out:
111 rcu_read_unlock();
112 }
113 EXPORT_SYMBOL(icmpv6_send);
114 +
115 +#if IS_ENABLED(CONFIG_NF_NAT)
116 +#include <net/netfilter/nf_conntrack.h>
117 +void icmpv6_ndo_send(struct sk_buff *skb_in, u8 type, u8 code, __u32 info)
118 +{
119 + struct sk_buff *cloned_skb = NULL;
120 + enum ip_conntrack_info ctinfo;
121 + struct in6_addr orig_ip;
122 + struct nf_conn *ct;
123 +
124 + ct = nf_ct_get(skb_in, &ctinfo);
125 + if (!ct || !(ct->status & IPS_SRC_NAT)) {
126 + icmpv6_send(skb_in, type, code, info);
127 + return;
128 + }
129 +
130 + if (skb_shared(skb_in))
131 + skb_in = cloned_skb = skb_clone(skb_in, GFP_ATOMIC);
132 +
133 + if (unlikely(!skb_in || skb_network_header(skb_in) < skb_in->head ||
134 + (skb_network_header(skb_in) + sizeof(struct ipv6hdr)) >
135 + skb_tail_pointer(skb_in) || skb_ensure_writable(skb_in,
136 + skb_network_offset(skb_in) + sizeof(struct ipv6hdr))))
137 + goto out;
138 +
139 + orig_ip = ipv6_hdr(skb_in)->saddr;
140 + ipv6_hdr(skb_in)->saddr = ct->tuplehash[0].tuple.src.u3.in6;
141 + icmpv6_send(skb_in, type, code, info);
142 + ipv6_hdr(skb_in)->saddr = orig_ip;
143 +out:
144 + consume_skb(cloned_skb);
145 +}
146 +EXPORT_SYMBOL(icmpv6_ndo_send);
147 +#endif
148 #endif