diff options
-rw-r--r-- | net/netfilter/xt_TPROXY.c | 202 |
1 files changed, 132 insertions, 70 deletions
diff --git a/net/netfilter/xt_TPROXY.c b/net/netfilter/xt_TPROXY.c index d5f97e2302b8..19c482caf30b 100644 --- a/net/netfilter/xt_TPROXY.c +++ b/net/netfilter/xt_TPROXY.c | |||
@@ -16,15 +16,41 @@ | |||
16 | #include <net/checksum.h> | 16 | #include <net/checksum.h> |
17 | #include <net/udp.h> | 17 | #include <net/udp.h> |
18 | #include <net/inet_sock.h> | 18 | #include <net/inet_sock.h> |
19 | 19 | #include <linux/inetdevice.h> | |
20 | #include <linux/netfilter/x_tables.h> | 20 | #include <linux/netfilter/x_tables.h> |
21 | #include <linux/netfilter_ipv4/ip_tables.h> | 21 | #include <linux/netfilter_ipv4/ip_tables.h> |
22 | #include <linux/netfilter_ipv6/ip6_tables.h> | ||
23 | #include <linux/netfilter/xt_TPROXY.h> | ||
24 | 22 | ||
25 | #include <net/netfilter/ipv4/nf_defrag_ipv4.h> | 23 | #include <net/netfilter/ipv4/nf_defrag_ipv4.h> |
24 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | ||
25 | #include <net/if_inet6.h> | ||
26 | #include <net/addrconf.h> | ||
27 | #include <linux/netfilter_ipv6/ip6_tables.h> | ||
26 | #include <net/netfilter/ipv6/nf_defrag_ipv6.h> | 28 | #include <net/netfilter/ipv6/nf_defrag_ipv6.h> |
29 | #endif | ||
30 | |||
27 | #include <net/netfilter/nf_tproxy_core.h> | 31 | #include <net/netfilter/nf_tproxy_core.h> |
32 | #include <linux/netfilter/xt_TPROXY.h> | ||
33 | |||
34 | static inline __be32 | ||
35 | tproxy_laddr4(struct sk_buff *skb, __be32 user_laddr, __be32 daddr) | ||
36 | { | ||
37 | struct in_device *indev; | ||
38 | __be32 laddr; | ||
39 | |||
40 | if (user_laddr) | ||
41 | return user_laddr; | ||
42 | |||
43 | laddr = 0; | ||
44 | rcu_read_lock(); | ||
45 | indev = __in_dev_get_rcu(skb->dev); | ||
46 | for_primary_ifa(indev) { | ||
47 | laddr = ifa->ifa_local; | ||
48 | break; | ||
49 | } endfor_ifa(indev); | ||
50 | rcu_read_unlock(); | ||
51 | |||
52 | return laddr ? laddr : daddr; | ||
53 | } | ||
28 | 54 | ||
29 | /** | 55 | /** |
30 | * tproxy_handle_time_wait4() - handle IPv4 TCP TIME_WAIT reopen redirections | 56 | * tproxy_handle_time_wait4() - handle IPv4 TCP TIME_WAIT reopen redirections |
@@ -75,60 +101,6 @@ tproxy_handle_time_wait4(struct sk_buff *skb, __be32 laddr, __be16 lport, | |||
75 | return sk; | 101 | return sk; |
76 | } | 102 | } |
77 | 103 | ||
78 | /** | ||
79 | * tproxy_handle_time_wait6() - handle IPv6 TCP TIME_WAIT reopen redirections | ||
80 | * @skb: The skb being processed. | ||
81 | * @tproto: Transport protocol. | ||
82 | * @thoff: Transport protocol header offset. | ||
83 | * @par: Iptables target parameters. | ||
84 | * @sk: The TIME_WAIT TCP socket found by the lookup. | ||
85 | * | ||
86 | * We have to handle SYN packets arriving to TIME_WAIT sockets | ||
87 | * differently: instead of reopening the connection we should rather | ||
88 | * redirect the new connection to the proxy if there's a listener | ||
89 | * socket present. | ||
90 | * | ||
91 | * tproxy_handle_time_wait6() consumes the socket reference passed in. | ||
92 | * | ||
93 | * Returns the listener socket if there's one, the TIME_WAIT socket if | ||
94 | * no such listener is found, or NULL if the TCP header is incomplete. | ||
95 | */ | ||
96 | static struct sock * | ||
97 | tproxy_handle_time_wait6(struct sk_buff *skb, int tproto, int thoff, | ||
98 | const struct xt_action_param *par, | ||
99 | struct sock *sk) | ||
100 | { | ||
101 | const struct ipv6hdr *iph = ipv6_hdr(skb); | ||
102 | struct tcphdr _hdr, *hp; | ||
103 | const struct xt_tproxy_target_info_v1 *tgi = par->targinfo; | ||
104 | |||
105 | hp = skb_header_pointer(skb, thoff, sizeof(_hdr), &_hdr); | ||
106 | if (hp == NULL) { | ||
107 | inet_twsk_put(inet_twsk(sk)); | ||
108 | return NULL; | ||
109 | } | ||
110 | |||
111 | if (hp->syn && !hp->rst && !hp->ack && !hp->fin) { | ||
112 | /* SYN to a TIME_WAIT socket, we'd rather redirect it | ||
113 | * to a listener socket if there's one */ | ||
114 | struct sock *sk2; | ||
115 | |||
116 | sk2 = nf_tproxy_get_sock_v6(dev_net(skb->dev), tproto, | ||
117 | &iph->saddr, | ||
118 | !ipv6_addr_any(&tgi->laddr.in6) ? &tgi->laddr.in6 : &iph->daddr, | ||
119 | hp->source, | ||
120 | tgi->lport ? tgi->lport : hp->dest, | ||
121 | skb->dev, NFT_LOOKUP_LISTENER); | ||
122 | if (sk2) { | ||
123 | inet_twsk_deschedule(inet_twsk(sk), &tcp_death_row); | ||
124 | inet_twsk_put(inet_twsk(sk)); | ||
125 | sk = sk2; | ||
126 | } | ||
127 | } | ||
128 | |||
129 | return sk; | ||
130 | } | ||
131 | |||
132 | static unsigned int | 104 | static unsigned int |
133 | tproxy_tg4(struct sk_buff *skb, __be32 laddr, __be16 lport, | 105 | tproxy_tg4(struct sk_buff *skb, __be32 laddr, __be16 lport, |
134 | u_int32_t mark_mask, u_int32_t mark_value) | 106 | u_int32_t mark_mask, u_int32_t mark_value) |
@@ -150,6 +122,10 @@ tproxy_tg4(struct sk_buff *skb, __be32 laddr, __be16 lport, | |||
150 | hp->source, hp->dest, | 122 | hp->source, hp->dest, |
151 | skb->dev, NFT_LOOKUP_ESTABLISHED); | 123 | skb->dev, NFT_LOOKUP_ESTABLISHED); |
152 | 124 | ||
125 | laddr = tproxy_laddr4(skb, laddr, iph->daddr); | ||
126 | if (!lport) | ||
127 | lport = hp->dest; | ||
128 | |||
153 | /* UDP has no TCP_TIME_WAIT state, so we never enter here */ | 129 | /* UDP has no TCP_TIME_WAIT state, so we never enter here */ |
154 | if (sk && sk->sk_state == TCP_TIME_WAIT) | 130 | if (sk && sk->sk_state == TCP_TIME_WAIT) |
155 | /* reopening a TIME_WAIT connection needs special handling */ | 131 | /* reopening a TIME_WAIT connection needs special handling */ |
@@ -158,8 +134,8 @@ tproxy_tg4(struct sk_buff *skb, __be32 laddr, __be16 lport, | |||
158 | /* no, there's no established connection, check if | 134 | /* no, there's no established connection, check if |
159 | * there's a listener on the redirected addr/port */ | 135 | * there's a listener on the redirected addr/port */ |
160 | sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), iph->protocol, | 136 | sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), iph->protocol, |
161 | iph->saddr, laddr ? laddr : iph->daddr, | 137 | iph->saddr, laddr, |
162 | hp->source, lport ? lport : hp->dest, | 138 | hp->source, lport, |
163 | skb->dev, NFT_LOOKUP_LISTENER); | 139 | skb->dev, NFT_LOOKUP_LISTENER); |
164 | 140 | ||
165 | /* NOTE: assign_sock consumes our sk reference */ | 141 | /* NOTE: assign_sock consumes our sk reference */ |
@@ -174,9 +150,9 @@ tproxy_tg4(struct sk_buff *skb, __be32 laddr, __be16 lport, | |||
174 | return NF_ACCEPT; | 150 | return NF_ACCEPT; |
175 | } | 151 | } |
176 | 152 | ||
177 | pr_debug("no socket, dropping: proto %hhu %08x:%hu -> %08x:%hu, mark: %x\n", | 153 | pr_debug("no socket, dropping: proto %hhu %pI4:%hu -> %pI4:%hu, mark: %x\n", |
178 | iph->protocol, ntohl(iph->daddr), ntohs(hp->dest), | 154 | iph->protocol, &iph->saddr, ntohs(hp->source), |
179 | ntohl(laddr), ntohs(lport), skb->mark); | 155 | &iph->daddr, ntohs(hp->dest), skb->mark); |
180 | return NF_DROP; | 156 | return NF_DROP; |
181 | } | 157 | } |
182 | 158 | ||
@@ -197,6 +173,88 @@ tproxy_tg4_v1(struct sk_buff *skb, const struct xt_action_param *par) | |||
197 | } | 173 | } |
198 | 174 | ||
199 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | 175 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) |
176 | |||
177 | static inline const struct in6_addr * | ||
178 | tproxy_laddr6(struct sk_buff *skb, const struct in6_addr *user_laddr, | ||
179 | const struct in6_addr *daddr) | ||
180 | { | ||
181 | struct inet6_dev *indev; | ||
182 | struct inet6_ifaddr *ifa; | ||
183 | struct in6_addr *laddr; | ||
184 | |||
185 | if (!ipv6_addr_any(user_laddr)) | ||
186 | return user_laddr; | ||
187 | laddr = NULL; | ||
188 | |||
189 | rcu_read_lock(); | ||
190 | indev = __in6_dev_get(skb->dev); | ||
191 | if (indev) | ||
192 | list_for_each_entry(ifa, &indev->addr_list, if_list) { | ||
193 | if (ifa->flags & (IFA_F_TENTATIVE | IFA_F_DEPRECATED)) | ||
194 | continue; | ||
195 | |||
196 | laddr = &ifa->addr; | ||
197 | break; | ||
198 | } | ||
199 | rcu_read_unlock(); | ||
200 | |||
201 | return laddr ? laddr : daddr; | ||
202 | } | ||
203 | |||
204 | /** | ||
205 | * tproxy_handle_time_wait6() - handle IPv6 TCP TIME_WAIT reopen redirections | ||
206 | * @skb: The skb being processed. | ||
207 | * @tproto: Transport protocol. | ||
208 | * @thoff: Transport protocol header offset. | ||
209 | * @par: Iptables target parameters. | ||
210 | * @sk: The TIME_WAIT TCP socket found by the lookup. | ||
211 | * | ||
212 | * We have to handle SYN packets arriving to TIME_WAIT sockets | ||
213 | * differently: instead of reopening the connection we should rather | ||
214 | * redirect the new connection to the proxy if there's a listener | ||
215 | * socket present. | ||
216 | * | ||
217 | * tproxy_handle_time_wait6() consumes the socket reference passed in. | ||
218 | * | ||
219 | * Returns the listener socket if there's one, the TIME_WAIT socket if | ||
220 | * no such listener is found, or NULL if the TCP header is incomplete. | ||
221 | */ | ||
222 | static struct sock * | ||
223 | tproxy_handle_time_wait6(struct sk_buff *skb, int tproto, int thoff, | ||
224 | const struct xt_action_param *par, | ||
225 | struct sock *sk) | ||
226 | { | ||
227 | const struct ipv6hdr *iph = ipv6_hdr(skb); | ||
228 | struct tcphdr _hdr, *hp; | ||
229 | const struct xt_tproxy_target_info_v1 *tgi = par->targinfo; | ||
230 | |||
231 | hp = skb_header_pointer(skb, thoff, sizeof(_hdr), &_hdr); | ||
232 | if (hp == NULL) { | ||
233 | inet_twsk_put(inet_twsk(sk)); | ||
234 | return NULL; | ||
235 | } | ||
236 | |||
237 | if (hp->syn && !hp->rst && !hp->ack && !hp->fin) { | ||
238 | /* SYN to a TIME_WAIT socket, we'd rather redirect it | ||
239 | * to a listener socket if there's one */ | ||
240 | struct sock *sk2; | ||
241 | |||
242 | sk2 = nf_tproxy_get_sock_v6(dev_net(skb->dev), tproto, | ||
243 | &iph->saddr, | ||
244 | tproxy_laddr6(skb, &tgi->laddr.in6, &iph->daddr), | ||
245 | hp->source, | ||
246 | tgi->lport ? tgi->lport : hp->dest, | ||
247 | skb->dev, NFT_LOOKUP_LISTENER); | ||
248 | if (sk2) { | ||
249 | inet_twsk_deschedule(inet_twsk(sk), &tcp_death_row); | ||
250 | inet_twsk_put(inet_twsk(sk)); | ||
251 | sk = sk2; | ||
252 | } | ||
253 | } | ||
254 | |||
255 | return sk; | ||
256 | } | ||
257 | |||
200 | static unsigned int | 258 | static unsigned int |
201 | tproxy_tg6_v1(struct sk_buff *skb, const struct xt_action_param *par) | 259 | tproxy_tg6_v1(struct sk_buff *skb, const struct xt_action_param *par) |
202 | { | 260 | { |
@@ -204,6 +262,8 @@ tproxy_tg6_v1(struct sk_buff *skb, const struct xt_action_param *par) | |||
204 | const struct xt_tproxy_target_info_v1 *tgi = par->targinfo; | 262 | const struct xt_tproxy_target_info_v1 *tgi = par->targinfo; |
205 | struct udphdr _hdr, *hp; | 263 | struct udphdr _hdr, *hp; |
206 | struct sock *sk; | 264 | struct sock *sk; |
265 | const struct in6_addr *laddr; | ||
266 | __be16 lport; | ||
207 | int thoff; | 267 | int thoff; |
208 | int tproto; | 268 | int tproto; |
209 | 269 | ||
@@ -228,6 +288,9 @@ tproxy_tg6_v1(struct sk_buff *skb, const struct xt_action_param *par) | |||
228 | hp->source, hp->dest, | 288 | hp->source, hp->dest, |
229 | par->in, NFT_LOOKUP_ESTABLISHED); | 289 | par->in, NFT_LOOKUP_ESTABLISHED); |
230 | 290 | ||
291 | laddr = tproxy_laddr6(skb, &tgi->laddr.in6, &iph->daddr); | ||
292 | lport = tgi->lport ? tgi->lport : hp->dest; | ||
293 | |||
231 | /* UDP has no TCP_TIME_WAIT state, so we never enter here */ | 294 | /* UDP has no TCP_TIME_WAIT state, so we never enter here */ |
232 | if (sk && sk->sk_state == TCP_TIME_WAIT) | 295 | if (sk && sk->sk_state == TCP_TIME_WAIT) |
233 | /* reopening a TIME_WAIT connection needs special handling */ | 296 | /* reopening a TIME_WAIT connection needs special handling */ |
@@ -236,10 +299,8 @@ tproxy_tg6_v1(struct sk_buff *skb, const struct xt_action_param *par) | |||
236 | /* no there's no established connection, check if | 299 | /* no there's no established connection, check if |
237 | * there's a listener on the redirected addr/port */ | 300 | * there's a listener on the redirected addr/port */ |
238 | sk = nf_tproxy_get_sock_v6(dev_net(skb->dev), tproto, | 301 | sk = nf_tproxy_get_sock_v6(dev_net(skb->dev), tproto, |
239 | &iph->saddr, | 302 | &iph->saddr, laddr, |
240 | !ipv6_addr_any(&tgi->laddr.in6) ? &tgi->laddr.in6 : &iph->daddr, | 303 | hp->source, lport, |
241 | hp->source, | ||
242 | tgi->lport ? tgi->lport : hp->dest, | ||
243 | par->in, NFT_LOOKUP_LISTENER); | 304 | par->in, NFT_LOOKUP_LISTENER); |
244 | 305 | ||
245 | /* NOTE: assign_sock consumes our sk reference */ | 306 | /* NOTE: assign_sock consumes our sk reference */ |
@@ -249,14 +310,15 @@ tproxy_tg6_v1(struct sk_buff *skb, const struct xt_action_param *par) | |||
249 | skb->mark = (skb->mark & ~tgi->mark_mask) ^ tgi->mark_value; | 310 | skb->mark = (skb->mark & ~tgi->mark_mask) ^ tgi->mark_value; |
250 | 311 | ||
251 | pr_debug("redirecting: proto %hhu %pI6:%hu -> %pI6:%hu, mark: %x\n", | 312 | pr_debug("redirecting: proto %hhu %pI6:%hu -> %pI6:%hu, mark: %x\n", |
252 | tproto, &iph->saddr, ntohs(hp->dest), | 313 | tproto, &iph->saddr, ntohs(hp->source), |
253 | &tgi->laddr.in6, ntohs(tgi->lport), skb->mark); | 314 | laddr, ntohs(lport), skb->mark); |
254 | return NF_ACCEPT; | 315 | return NF_ACCEPT; |
255 | } | 316 | } |
256 | 317 | ||
257 | pr_debug("no socket, dropping: proto %hhu %pI6:%hu -> %pI6:%hu, mark: %x\n", | 318 | pr_debug("no socket, dropping: proto %hhu %pI6:%hu -> %pI6:%hu, mark: %x\n", |
258 | tproto, &iph->saddr, ntohs(hp->dest), | 319 | tproto, &iph->saddr, ntohs(hp->source), |
259 | &tgi->laddr.in6, ntohs(tgi->lport), skb->mark); | 320 | &iph->daddr, ntohs(hp->dest), skb->mark); |
321 | |||
260 | return NF_DROP; | 322 | return NF_DROP; |
261 | } | 323 | } |
262 | 324 | ||