blob: 17c40deafa4fe47391e98d0f0190d53bd38b678d [file] [log] [blame]
KOVACS Krisztiane8439272008-10-08 11:35:12 +02001/*
2 * Transparent proxy support for Linux/iptables
3 *
Balazs Scheidler6ad78892010-10-21 16:17:26 +02004 * Copyright (c) 2006-2010 BalaBit IT Ltd.
KOVACS Krisztiane8439272008-10-08 11:35:12 +02005 * 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 */
Jan Engelhardtff67e4e2010-03-19 21:08:16 +010012#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
KOVACS Krisztiane8439272008-10-08 11:35:12 +020013#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>
Balazs Scheidlercc6eb432010-10-21 16:21:10 +020019#include <linux/inetdevice.h>
KOVACS Krisztiane8439272008-10-08 11:35:12 +020020#include <linux/netfilter/x_tables.h>
21#include <linux/netfilter_ipv4/ip_tables.h>
KOVACS Krisztiane8439272008-10-08 11:35:12 +020022
23#include <net/netfilter/ipv4/nf_defrag_ipv4.h>
KOVACS Krisztianf6318e52010-10-24 23:38:32 +000024
Igor Maravićc0cd1152011-12-12 02:58:24 +000025#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
KOVACS Krisztianf6318e52010-10-24 23:38:32 +000026#define XT_TPROXY_HAVE_IPV6 1
Balazs Scheidlercc6eb432010-10-21 16:21:10 +020027#include <net/if_inet6.h>
28#include <net/addrconf.h>
29#include <linux/netfilter_ipv6/ip6_tables.h>
Balazs Scheidler6ad78892010-10-21 16:17:26 +020030#include <net/netfilter/ipv6/nf_defrag_ipv6.h>
Balazs Scheidlercc6eb432010-10-21 16:21:10 +020031#endif
32
KOVACS Krisztiane8439272008-10-08 11:35:12 +020033#include <net/netfilter/nf_tproxy_core.h>
Balazs Scheidlercc6eb432010-10-21 16:21:10 +020034#include <linux/netfilter/xt_TPROXY.h>
35
Florian Westphald503b302011-02-17 11:32:38 +010036static 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
Balazs Scheidlercc6eb432010-10-21 16:21:10 +020050static 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}
KOVACS Krisztiane8439272008-10-08 11:35:12 +020070
Balazs Scheidler106e4c22010-10-21 12:45:14 +020071/**
Ben Hutchings2c530402012-07-10 10:55:09 +000072 * tproxy_handle_time_wait4 - handle IPv4 TCP TIME_WAIT reopen redirections
Balazs Scheidler106e4c22010-10-21 12:45:14 +020073 * @skb: The skb being processed.
Balazs Scheidler6ad78892010-10-21 16:17:26 +020074 * @laddr: IPv4 address to redirect to or zero.
75 * @lport: TCP port to redirect to or zero.
Balazs Scheidler106e4c22010-10-21 12:45:14 +020076 * @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 *
Balazs Scheidler6ad78892010-10-21 16:17:26 +020083 * tproxy_handle_time_wait4() consumes the socket reference passed in.
Balazs Scheidler106e4c22010-10-21 12:45:14 +020084 *
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 *
Balazs Scheidler6ad78892010-10-21 16:17:26 +020089tproxy_handle_time_wait4(struct sk_buff *skb, __be32 laddr, __be16 lport,
90 struct sock *sk)
Balazs Scheidler106e4c22010-10-21 12:45:14 +020091{
92 const struct iphdr *iph = ip_hdr(skb);
Balazs Scheidler106e4c22010-10-21 12:45:14 +020093 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,
Balazs Scheidler6ad78892010-10-21 16:17:26 +0200107 iph->saddr, laddr ? laddr : iph->daddr,
108 hp->source, lport ? lport : hp->dest,
109 skb->dev, NFT_LOOKUP_LISTENER);
Balazs Scheidler106e4c22010-10-21 12:45:14 +0200110 if (sk2) {
Balazs Scheidler6ad78892010-10-21 16:17:26 +0200111 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
Florian Westphalfd158d72013-07-29 15:41:52 +0200120/* 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
Balazs Scheidlercc6eb432010-10-21 16:21:10 +0200129static unsigned int
130tproxy_tg4(struct sk_buff *skb, __be32 laddr, __be16 lport,
131 u_int32_t mark_mask, u_int32_t mark_value)
132{
133 const struct iphdr *iph = ip_hdr(skb);
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
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 */
145 sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), iph->protocol,
146 iph->saddr, iph->daddr,
147 hp->source, hp->dest,
148 skb->dev, NFT_LOOKUP_ESTABLISHED);
149
150 laddr = tproxy_laddr4(skb, laddr, iph->daddr);
151 if (!lport)
152 lport = hp->dest;
153
154 /* UDP has no TCP_TIME_WAIT state, so we never enter here */
155 if (sk && sk->sk_state == TCP_TIME_WAIT)
156 /* reopening a TIME_WAIT connection needs special handling */
157 sk = tproxy_handle_time_wait4(skb, laddr, lport, sk);
158 else if (!sk)
159 /* no, there's no established connection, check if
160 * there's a listener on the redirected addr/port */
161 sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), iph->protocol,
162 iph->saddr, laddr,
163 hp->source, lport,
164 skb->dev, NFT_LOOKUP_LISTENER);
165
166 /* NOTE: assign_sock consumes our sk reference */
Florian Westphald503b302011-02-17 11:32:38 +0100167 if (sk && tproxy_sk_is_transparent(sk)) {
Balazs Scheidlercc6eb432010-10-21 16:21:10 +0200168 /* 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);
Florian Westphald503b302011-02-17 11:32:38 +0100175
176 nf_tproxy_assign_sock(skb, sk);
Balazs Scheidlercc6eb432010-10-21 16:21:10 +0200177 return NF_ACCEPT;
178 }
179
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);
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
KOVACS Krisztianf6318e52010-10-24 23:38:32 +0000202#ifdef XT_TPROXY_HAVE_IPV6
Balazs Scheidlercc6eb432010-10-21 16:21:10 +0200203
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
Balazs Scheidler6ad78892010-10-21 16:17:26 +0200231/**
Ben Hutchings2c530402012-07-10 10:55:09 +0000232 * tproxy_handle_time_wait6 - handle IPv6 TCP TIME_WAIT reopen redirections
Balazs Scheidler6ad78892010-10-21 16:17:26 +0200233 * @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,
Balazs Scheidlercc6eb432010-10-21 16:21:10 +0200271 tproxy_laddr6(skb, &tgi->laddr.in6, &iph->daddr),
Balazs Scheidler6ad78892010-10-21 16:17:26 +0200272 hp->source,
273 tgi->lport ? tgi->lport : hp->dest,
274 skb->dev, NFT_LOOKUP_LISTENER);
275 if (sk2) {
Balazs Scheidler106e4c22010-10-21 12:45:14 +0200276 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
KOVACS Krisztiane8439272008-10-08 11:35:12 +0200285static unsigned int
Balazs Scheidler6ad78892010-10-21 16:17:26 +0200286tproxy_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;
Balazs Scheidlercc6eb432010-10-21 16:21:10 +0200292 const struct in6_addr *laddr;
293 __be16 lport;
Hans Schillstrom84018f52012-04-23 03:35:26 +0000294 int thoff = 0;
Balazs Scheidler6ad78892010-10-21 16:17:26 +0200295 int tproto;
296
Hans Schillstrom84018f52012-04-23 03:35:26 +0000297 tproto = ipv6_find_hdr(skb, &thoff, -1, NULL, NULL);
Balazs Scheidler6ad78892010-10-21 16:17:26 +0200298 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,
Balazs Scheidler106e4c22010-10-21 12:45:14 +0200316 par->in, NFT_LOOKUP_ESTABLISHED);
317
Balazs Scheidlercc6eb432010-10-21 16:21:10 +0200318 laddr = tproxy_laddr6(skb, &tgi->laddr.in6, &iph->daddr);
319 lport = tgi->lport ? tgi->lport : hp->dest;
320
Balazs Scheidler106e4c22010-10-21 12:45:14 +0200321 /* UDP has no TCP_TIME_WAIT state, so we never enter here */
322 if (sk && sk->sk_state == TCP_TIME_WAIT)
Balazs Scheidler6ad78892010-10-21 16:17:26 +0200323 /* reopening a TIME_WAIT connection needs special handling */
324 sk = tproxy_handle_time_wait6(skb, tproto, thoff, par, sk);
Balazs Scheidler106e4c22010-10-21 12:45:14 +0200325 else if (!sk)
Balazs Scheidler6ad78892010-10-21 16:17:26 +0200326 /* 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,
Balazs Scheidlercc6eb432010-10-21 16:21:10 +0200329 &iph->saddr, laddr,
330 hp->source, lport,
Balazs Scheidler106e4c22010-10-21 12:45:14 +0200331 par->in, NFT_LOOKUP_LISTENER);
KOVACS Krisztiane8439272008-10-08 11:35:12 +0200332
333 /* NOTE: assign_sock consumes our sk reference */
Florian Westphald503b302011-02-17 11:32:38 +0100334 if (sk && tproxy_sk_is_transparent(sk)) {
KOVACS Krisztiane8439272008-10-08 11:35:12 +0200335 /* 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
Balazs Scheidler6ad78892010-10-21 16:17:26 +0200339 pr_debug("redirecting: proto %hhu %pI6:%hu -> %pI6:%hu, mark: %x\n",
Balazs Scheidlercc6eb432010-10-21 16:21:10 +0200340 tproto, &iph->saddr, ntohs(hp->source),
341 laddr, ntohs(lport), skb->mark);
Florian Westphald503b302011-02-17 11:32:38 +0100342
343 nf_tproxy_assign_sock(skb, sk);
KOVACS Krisztiane8439272008-10-08 11:35:12 +0200344 return NF_ACCEPT;
345 }
346
Balazs Scheidler6ad78892010-10-21 16:17:26 +0200347 pr_debug("no socket, dropping: proto %hhu %pI6:%hu -> %pI6:%hu, mark: %x\n",
Balazs Scheidlercc6eb432010-10-21 16:21:10 +0200348 tproto, &iph->saddr, ntohs(hp->source),
349 &iph->daddr, ntohs(hp->dest), skb->mark);
350
KOVACS Krisztiane8439272008-10-08 11:35:12 +0200351 return NF_DROP;
352}
353
Balazs Scheidler6ad78892010-10-21 16:17:26 +0200354static 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)
KOVACS Krisztiane8439272008-10-08 11:35:12 +0200369{
Jan Engelhardtaf5d6dc2008-10-08 11:35:19 +0200370 const struct ipt_ip *i = par->entryinfo;
KOVACS Krisztiane8439272008-10-08 11:35:12 +0200371
372 if ((i->proto == IPPROTO_TCP || i->proto == IPPROTO_UDP)
373 && !(i->invflags & IPT_INV_PROTO))
Jan Engelhardtd6b00a52010-03-25 16:34:45 +0100374 return 0;
KOVACS Krisztiane8439272008-10-08 11:35:12 +0200375
Jan Engelhardtff67e4e2010-03-19 21:08:16 +0100376 pr_info("Can be used only in combination with "
KOVACS Krisztiane8439272008-10-08 11:35:12 +0200377 "either -p tcp or -p udp\n");
Jan Engelhardtd6b00a52010-03-25 16:34:45 +0100378 return -EINVAL;
KOVACS Krisztiane8439272008-10-08 11:35:12 +0200379}
380
Balazs Scheidler6ad78892010-10-21 16:17:26 +0200381static 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 },
KOVACS Krisztianf6318e52010-10-24 23:38:32 +0000404#ifdef XT_TPROXY_HAVE_IPV6
Balazs Scheidler6ad78892010-10-21 16:17:26 +0200405 {
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
KOVACS Krisztiane8439272008-10-08 11:35:12 +0200418};
419
420static int __init tproxy_tg_init(void)
421{
422 nf_defrag_ipv4_enable();
KOVACS Krisztianf6318e52010-10-24 23:38:32 +0000423#ifdef XT_TPROXY_HAVE_IPV6
Balazs Scheidler6ad78892010-10-21 16:17:26 +0200424 nf_defrag_ipv6_enable();
425#endif
426
427 return xt_register_targets(tproxy_tg_reg, ARRAY_SIZE(tproxy_tg_reg));
KOVACS Krisztiane8439272008-10-08 11:35:12 +0200428}
429
430static void __exit tproxy_tg_exit(void)
431{
Balazs Scheidler6ad78892010-10-21 16:17:26 +0200432 xt_unregister_targets(tproxy_tg_reg, ARRAY_SIZE(tproxy_tg_reg));
KOVACS Krisztiane8439272008-10-08 11:35:12 +0200433}
434
435module_init(tproxy_tg_init);
436module_exit(tproxy_tg_exit);
437MODULE_LICENSE("GPL");
Balazs Scheidler6ad78892010-10-21 16:17:26 +0200438MODULE_AUTHOR("Balazs Scheidler, Krisztian Kovacs");
KOVACS Krisztiane8439272008-10-08 11:35:12 +0200439MODULE_DESCRIPTION("Netfilter transparent proxy (TPROXY) target module.");
440MODULE_ALIAS("ipt_TPROXY");
Balazs Scheidler6ad78892010-10-21 16:17:26 +0200441MODULE_ALIAS("ip6t_TPROXY");