netfilter: tproxy: remove nf_tproxy_core, keep tw sk assigned to skb
[deliverable/linux.git] / net / netfilter / xt_TPROXY.c
CommitLineData
e8439270
KK
1/*
2 * Transparent proxy support for Linux/iptables
3 *
6ad78893 4 * Copyright (c) 2006-2010 BalaBit IT Ltd.
e8439270
KK
5 * Author: Balazs Scheidler, Krisztian Kovacs
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 *
11 */
ff67e4e4 12#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
e8439270
KK
13#include <linux/module.h>
14#include <linux/skbuff.h>
15#include <linux/ip.h>
16#include <net/checksum.h>
17#include <net/udp.h>
18#include <net/inet_sock.h>
cc6eb433 19#include <linux/inetdevice.h>
e8439270
KK
20#include <linux/netfilter/x_tables.h>
21#include <linux/netfilter_ipv4/ip_tables.h>
e8439270
KK
22
23#include <net/netfilter/ipv4/nf_defrag_ipv4.h>
f6318e55 24
c0cd1156 25#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
f6318e55 26#define XT_TPROXY_HAVE_IPV6 1
cc6eb433
BS
27#include <net/if_inet6.h>
28#include <net/addrconf.h>
29#include <linux/netfilter_ipv6/ip6_tables.h>
6ad78893 30#include <net/netfilter/ipv6/nf_defrag_ipv6.h>
cc6eb433
BS
31#endif
32
e8439270 33#include <net/netfilter/nf_tproxy_core.h>
cc6eb433
BS
34#include <linux/netfilter/xt_TPROXY.h>
35
d503b30b
FW
36static bool tproxy_sk_is_transparent(struct sock *sk)
37{
38 if (sk->sk_state != TCP_TIME_WAIT) {
39 if (inet_sk(sk)->transparent)
40 return true;
41 sock_put(sk);
42 } else {
43 if (inet_twsk(sk)->tw_transparent)
44 return true;
45 inet_twsk_put(inet_twsk(sk));
46 }
47 return false;
48}
49
cc6eb433
BS
50static inline __be32
51tproxy_laddr4(struct sk_buff *skb, __be32 user_laddr, __be32 daddr)
52{
53 struct in_device *indev;
54 __be32 laddr;
55
56 if (user_laddr)
57 return user_laddr;
58
59 laddr = 0;
60 rcu_read_lock();
61 indev = __in_dev_get_rcu(skb->dev);
62 for_primary_ifa(indev) {
63 laddr = ifa->ifa_local;
64 break;
65 } endfor_ifa(indev);
66 rcu_read_unlock();
67
68 return laddr ? laddr : daddr;
69}
e8439270 70
106e4c26 71/**
2c53040f 72 * tproxy_handle_time_wait4 - handle IPv4 TCP TIME_WAIT reopen redirections
106e4c26 73 * @skb: The skb being processed.
6ad78893
BS
74 * @laddr: IPv4 address to redirect to or zero.
75 * @lport: TCP port to redirect to or zero.
106e4c26
BS
76 * @sk: The TIME_WAIT TCP socket found by the lookup.
77 *
78 * We have to handle SYN packets arriving to TIME_WAIT sockets
79 * differently: instead of reopening the connection we should rather
80 * redirect the new connection to the proxy if there's a listener
81 * socket present.
82 *
6ad78893 83 * tproxy_handle_time_wait4() consumes the socket reference passed in.
106e4c26
BS
84 *
85 * Returns the listener socket if there's one, the TIME_WAIT socket if
86 * no such listener is found, or NULL if the TCP header is incomplete.
87 */
88static struct sock *
6ad78893
BS
89tproxy_handle_time_wait4(struct sk_buff *skb, __be32 laddr, __be16 lport,
90 struct sock *sk)
106e4c26
BS
91{
92 const struct iphdr *iph = ip_hdr(skb);
106e4c26
BS
93 struct tcphdr _hdr, *hp;
94
95 hp = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_hdr), &_hdr);
96 if (hp == NULL) {
97 inet_twsk_put(inet_twsk(sk));
98 return NULL;
99 }
100
101 if (hp->syn && !hp->rst && !hp->ack && !hp->fin) {
102 /* SYN to a TIME_WAIT socket, we'd rather redirect it
103 * to a listener socket if there's one */
104 struct sock *sk2;
105
106 sk2 = nf_tproxy_get_sock_v4(dev_net(skb->dev), iph->protocol,
6ad78893
BS
107 iph->saddr, laddr ? laddr : iph->daddr,
108 hp->source, lport ? lport : hp->dest,
109 skb->dev, NFT_LOOKUP_LISTENER);
110 if (sk2) {
111 inet_twsk_deschedule(inet_twsk(sk), &tcp_death_row);
112 inet_twsk_put(inet_twsk(sk));
113 sk = sk2;
114 }
115 }
116
117 return sk;
118}
119
fd158d79
FW
120/* assign a socket to the skb -- consumes sk */
121static void
122nf_tproxy_assign_sock(struct sk_buff *skb, struct sock *sk)
123{
124 skb_orphan(skb);
125 skb->sk = sk;
126 skb->destructor = sock_edemux;
127}
128
e8439270 129static unsigned int
6ad78893
BS
130tproxy_tg4(struct sk_buff *skb, __be32 laddr, __be16 lport,
131 u_int32_t mark_mask, u_int32_t mark_value)
e8439270
KK
132{
133 const struct iphdr *iph = ip_hdr(skb);
e8439270
KK
134 struct udphdr _hdr, *hp;
135 struct sock *sk;
136
137 hp = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_hdr), &_hdr);
138 if (hp == NULL)
139 return NF_DROP;
140
6ad78893
BS
141 /* check if there's an ongoing connection on the packet
142 * addresses, this happens if the redirect already happened
143 * and the current packet belongs to an already established
144 * connection */
e8439270 145 sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), iph->protocol,
106e4c26
BS
146 iph->saddr, iph->daddr,
147 hp->source, hp->dest,
6ad78893 148 skb->dev, NFT_LOOKUP_ESTABLISHED);
106e4c26 149
cc6eb433
BS
150 laddr = tproxy_laddr4(skb, laddr, iph->daddr);
151 if (!lport)
152 lport = hp->dest;
153
106e4c26
BS
154 /* UDP has no TCP_TIME_WAIT state, so we never enter here */
155 if (sk && sk->sk_state == TCP_TIME_WAIT)
6ad78893
BS
156 /* reopening a TIME_WAIT connection needs special handling */
157 sk = tproxy_handle_time_wait4(skb, laddr, lport, sk);
106e4c26 158 else if (!sk)
6ad78893
BS
159 /* no, there's no established connection, check if
160 * there's a listener on the redirected addr/port */
106e4c26 161 sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), iph->protocol,
cc6eb433
BS
162 iph->saddr, laddr,
163 hp->source, lport,
6ad78893
BS
164 skb->dev, NFT_LOOKUP_LISTENER);
165
166 /* NOTE: assign_sock consumes our sk reference */
d503b30b 167 if (sk && tproxy_sk_is_transparent(sk)) {
6ad78893
BS
168 /* This should be in a separate target, but we don't do multiple
169 targets on the same rule yet */
170 skb->mark = (skb->mark & ~mark_mask) ^ mark_value;
171
172 pr_debug("redirecting: proto %hhu %pI4:%hu -> %pI4:%hu, mark: %x\n",
173 iph->protocol, &iph->daddr, ntohs(hp->dest),
174 &laddr, ntohs(lport), skb->mark);
d503b30b
FW
175
176 nf_tproxy_assign_sock(skb, sk);
6ad78893
BS
177 return NF_ACCEPT;
178 }
179
cc6eb433
BS
180 pr_debug("no socket, dropping: proto %hhu %pI4:%hu -> %pI4:%hu, mark: %x\n",
181 iph->protocol, &iph->saddr, ntohs(hp->source),
182 &iph->daddr, ntohs(hp->dest), skb->mark);
6ad78893
BS
183 return NF_DROP;
184}
185
186static unsigned int
187tproxy_tg4_v0(struct sk_buff *skb, const struct xt_action_param *par)
188{
189 const struct xt_tproxy_target_info *tgi = par->targinfo;
190
191 return tproxy_tg4(skb, tgi->laddr, tgi->lport, tgi->mark_mask, tgi->mark_value);
192}
193
194static unsigned int
195tproxy_tg4_v1(struct sk_buff *skb, const struct xt_action_param *par)
196{
197 const struct xt_tproxy_target_info_v1 *tgi = par->targinfo;
198
199 return tproxy_tg4(skb, tgi->laddr.ip, tgi->lport, tgi->mark_mask, tgi->mark_value);
200}
201
f6318e55 202#ifdef XT_TPROXY_HAVE_IPV6
cc6eb433
BS
203
204static inline const struct in6_addr *
205tproxy_laddr6(struct sk_buff *skb, const struct in6_addr *user_laddr,
206 const struct in6_addr *daddr)
207{
208 struct inet6_dev *indev;
209 struct inet6_ifaddr *ifa;
210 struct in6_addr *laddr;
211
212 if (!ipv6_addr_any(user_laddr))
213 return user_laddr;
214 laddr = NULL;
215
216 rcu_read_lock();
217 indev = __in6_dev_get(skb->dev);
218 if (indev)
219 list_for_each_entry(ifa, &indev->addr_list, if_list) {
220 if (ifa->flags & (IFA_F_TENTATIVE | IFA_F_DEPRECATED))
221 continue;
222
223 laddr = &ifa->addr;
224 break;
225 }
226 rcu_read_unlock();
227
228 return laddr ? laddr : daddr;
229}
230
231/**
2c53040f 232 * tproxy_handle_time_wait6 - handle IPv6 TCP TIME_WAIT reopen redirections
cc6eb433
BS
233 * @skb: The skb being processed.
234 * @tproto: Transport protocol.
235 * @thoff: Transport protocol header offset.
236 * @par: Iptables target parameters.
237 * @sk: The TIME_WAIT TCP socket found by the lookup.
238 *
239 * We have to handle SYN packets arriving to TIME_WAIT sockets
240 * differently: instead of reopening the connection we should rather
241 * redirect the new connection to the proxy if there's a listener
242 * socket present.
243 *
244 * tproxy_handle_time_wait6() consumes the socket reference passed in.
245 *
246 * Returns the listener socket if there's one, the TIME_WAIT socket if
247 * no such listener is found, or NULL if the TCP header is incomplete.
248 */
249static struct sock *
250tproxy_handle_time_wait6(struct sk_buff *skb, int tproto, int thoff,
251 const struct xt_action_param *par,
252 struct sock *sk)
253{
254 const struct ipv6hdr *iph = ipv6_hdr(skb);
255 struct tcphdr _hdr, *hp;
256 const struct xt_tproxy_target_info_v1 *tgi = par->targinfo;
257
258 hp = skb_header_pointer(skb, thoff, sizeof(_hdr), &_hdr);
259 if (hp == NULL) {
260 inet_twsk_put(inet_twsk(sk));
261 return NULL;
262 }
263
264 if (hp->syn && !hp->rst && !hp->ack && !hp->fin) {
265 /* SYN to a TIME_WAIT socket, we'd rather redirect it
266 * to a listener socket if there's one */
267 struct sock *sk2;
268
269 sk2 = nf_tproxy_get_sock_v6(dev_net(skb->dev), tproto,
270 &iph->saddr,
271 tproxy_laddr6(skb, &tgi->laddr.in6, &iph->daddr),
272 hp->source,
273 tgi->lport ? tgi->lport : hp->dest,
274 skb->dev, NFT_LOOKUP_LISTENER);
275 if (sk2) {
276 inet_twsk_deschedule(inet_twsk(sk), &tcp_death_row);
277 inet_twsk_put(inet_twsk(sk));
278 sk = sk2;
279 }
280 }
281
282 return sk;
283}
284
6ad78893
BS
285static unsigned int
286tproxy_tg6_v1(struct sk_buff *skb, const struct xt_action_param *par)
287{
288 const struct ipv6hdr *iph = ipv6_hdr(skb);
289 const struct xt_tproxy_target_info_v1 *tgi = par->targinfo;
290 struct udphdr _hdr, *hp;
291 struct sock *sk;
cc6eb433
BS
292 const struct in6_addr *laddr;
293 __be16 lport;
84018f55 294 int thoff = 0;
6ad78893
BS
295 int tproto;
296
84018f55 297 tproto = ipv6_find_hdr(skb, &thoff, -1, NULL, NULL);
6ad78893
BS
298 if (tproto < 0) {
299 pr_debug("unable to find transport header in IPv6 packet, dropping\n");
300 return NF_DROP;
301 }
302
303 hp = skb_header_pointer(skb, thoff, sizeof(_hdr), &_hdr);
304 if (hp == NULL) {
305 pr_debug("unable to grab transport header contents in IPv6 packet, dropping\n");
306 return NF_DROP;
307 }
308
309 /* check if there's an ongoing connection on the packet
310 * addresses, this happens if the redirect already happened
311 * and the current packet belongs to an already established
312 * connection */
313 sk = nf_tproxy_get_sock_v6(dev_net(skb->dev), tproto,
314 &iph->saddr, &iph->daddr,
315 hp->source, hp->dest,
316 par->in, NFT_LOOKUP_ESTABLISHED);
317
cc6eb433
BS
318 laddr = tproxy_laddr6(skb, &tgi->laddr.in6, &iph->daddr);
319 lport = tgi->lport ? tgi->lport : hp->dest;
320
6ad78893
BS
321 /* UDP has no TCP_TIME_WAIT state, so we never enter here */
322 if (sk && sk->sk_state == TCP_TIME_WAIT)
323 /* reopening a TIME_WAIT connection needs special handling */
324 sk = tproxy_handle_time_wait6(skb, tproto, thoff, par, sk);
325 else if (!sk)
326 /* no there's no established connection, check if
327 * there's a listener on the redirected addr/port */
328 sk = nf_tproxy_get_sock_v6(dev_net(skb->dev), tproto,
cc6eb433
BS
329 &iph->saddr, laddr,
330 hp->source, lport,
106e4c26 331 par->in, NFT_LOOKUP_LISTENER);
e8439270
KK
332
333 /* NOTE: assign_sock consumes our sk reference */
d503b30b 334 if (sk && tproxy_sk_is_transparent(sk)) {
e8439270
KK
335 /* This should be in a separate target, but we don't do multiple
336 targets on the same rule yet */
337 skb->mark = (skb->mark & ~tgi->mark_mask) ^ tgi->mark_value;
338
6ad78893 339 pr_debug("redirecting: proto %hhu %pI6:%hu -> %pI6:%hu, mark: %x\n",
cc6eb433
BS
340 tproto, &iph->saddr, ntohs(hp->source),
341 laddr, ntohs(lport), skb->mark);
d503b30b
FW
342
343 nf_tproxy_assign_sock(skb, sk);
e8439270
KK
344 return NF_ACCEPT;
345 }
346
6ad78893 347 pr_debug("no socket, dropping: proto %hhu %pI6:%hu -> %pI6:%hu, mark: %x\n",
cc6eb433
BS
348 tproto, &iph->saddr, ntohs(hp->source),
349 &iph->daddr, ntohs(hp->dest), skb->mark);
350
e8439270
KK
351 return NF_DROP;
352}
353
6ad78893
BS
354static int tproxy_tg6_check(const struct xt_tgchk_param *par)
355{
356 const struct ip6t_ip6 *i = par->entryinfo;
357
358 if ((i->proto == IPPROTO_TCP || i->proto == IPPROTO_UDP)
359 && !(i->flags & IP6T_INV_PROTO))
360 return 0;
361
362 pr_info("Can be used only in combination with "
363 "either -p tcp or -p udp\n");
364 return -EINVAL;
365}
366#endif
367
368static int tproxy_tg4_check(const struct xt_tgchk_param *par)
e8439270 369{
af5d6dc2 370 const struct ipt_ip *i = par->entryinfo;
e8439270
KK
371
372 if ((i->proto == IPPROTO_TCP || i->proto == IPPROTO_UDP)
373 && !(i->invflags & IPT_INV_PROTO))
d6b00a53 374 return 0;
e8439270 375
ff67e4e4 376 pr_info("Can be used only in combination with "
e8439270 377 "either -p tcp or -p udp\n");
d6b00a53 378 return -EINVAL;
e8439270
KK
379}
380
6ad78893
BS
381static struct xt_target tproxy_tg_reg[] __read_mostly = {
382 {
383 .name = "TPROXY",
384 .family = NFPROTO_IPV4,
385 .table = "mangle",
386 .target = tproxy_tg4_v0,
387 .revision = 0,
388 .targetsize = sizeof(struct xt_tproxy_target_info),
389 .checkentry = tproxy_tg4_check,
390 .hooks = 1 << NF_INET_PRE_ROUTING,
391 .me = THIS_MODULE,
392 },
393 {
394 .name = "TPROXY",
395 .family = NFPROTO_IPV4,
396 .table = "mangle",
397 .target = tproxy_tg4_v1,
398 .revision = 1,
399 .targetsize = sizeof(struct xt_tproxy_target_info_v1),
400 .checkentry = tproxy_tg4_check,
401 .hooks = 1 << NF_INET_PRE_ROUTING,
402 .me = THIS_MODULE,
403 },
f6318e55 404#ifdef XT_TPROXY_HAVE_IPV6
6ad78893
BS
405 {
406 .name = "TPROXY",
407 .family = NFPROTO_IPV6,
408 .table = "mangle",
409 .target = tproxy_tg6_v1,
410 .revision = 1,
411 .targetsize = sizeof(struct xt_tproxy_target_info_v1),
412 .checkentry = tproxy_tg6_check,
413 .hooks = 1 << NF_INET_PRE_ROUTING,
414 .me = THIS_MODULE,
415 },
416#endif
417
e8439270
KK
418};
419
420static int __init tproxy_tg_init(void)
421{
422 nf_defrag_ipv4_enable();
f6318e55 423#ifdef XT_TPROXY_HAVE_IPV6
6ad78893
BS
424 nf_defrag_ipv6_enable();
425#endif
426
427 return xt_register_targets(tproxy_tg_reg, ARRAY_SIZE(tproxy_tg_reg));
e8439270
KK
428}
429
430static void __exit tproxy_tg_exit(void)
431{
6ad78893 432 xt_unregister_targets(tproxy_tg_reg, ARRAY_SIZE(tproxy_tg_reg));
e8439270
KK
433}
434
435module_init(tproxy_tg_init);
436module_exit(tproxy_tg_exit);
437MODULE_LICENSE("GPL");
6ad78893 438MODULE_AUTHOR("Balazs Scheidler, Krisztian Kovacs");
e8439270
KK
439MODULE_DESCRIPTION("Netfilter transparent proxy (TPROXY) target module.");
440MODULE_ALIAS("ipt_TPROXY");
6ad78893 441MODULE_ALIAS("ip6t_TPROXY");
This page took 0.321737 seconds and 5 git commands to generate.