Skip to content

Commit 6ad7889

Browse files
bazsikaber
authored andcommitted
tproxy: added IPv6 support to the TPROXY target
This requires a new revision as the old target structure was IPv4 specific. Signed-off-by: Balazs Scheidler <[email protected]> Signed-off-by: KOVACS Krisztian <[email protected]> Signed-off-by: Patrick McHardy <[email protected]>
1 parent 3b9afb2 commit 6ad7889

File tree

2 files changed

+235
-40
lines changed

2 files changed

+235
-40
lines changed

include/linux/netfilter/xt_TPROXY.h

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
#ifndef _XT_TPROXY_H_target
2-
#define _XT_TPROXY_H_target
1+
#ifndef _XT_TPROXY_H
2+
#define _XT_TPROXY_H
33

44
/* TPROXY target is capable of marking the packet to perform
55
* redirection. We can get rid of that whenever we get support for
@@ -11,4 +11,11 @@ struct xt_tproxy_target_info {
1111
__be16 lport;
1212
};
1313

14-
#endif /* _XT_TPROXY_H_target */
14+
struct xt_tproxy_target_info_v1 {
15+
u_int32_t mark_mask;
16+
u_int32_t mark_value;
17+
union nf_inet_addr laddr;
18+
__be16 lport;
19+
};
20+
21+
#endif /* _XT_TPROXY_H */

net/netfilter/xt_TPROXY.c

Lines changed: 225 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
* Transparent proxy support for Linux/iptables
33
*
4-
* Copyright (c) 2006-2007 BalaBit IT Ltd.
4+
* Copyright (c) 2006-2010 BalaBit IT Ltd.
55
* Author: Balazs Scheidler, Krisztian Kovacs
66
*
77
* This program is free software; you can redistribute it and/or modify
@@ -19,32 +19,35 @@
1919

2020
#include <linux/netfilter/x_tables.h>
2121
#include <linux/netfilter_ipv4/ip_tables.h>
22+
#include <linux/netfilter_ipv6/ip6_tables.h>
2223
#include <linux/netfilter/xt_TPROXY.h>
2324

2425
#include <net/netfilter/ipv4/nf_defrag_ipv4.h>
26+
#include <net/netfilter/ipv6/nf_defrag_ipv6.h>
2527
#include <net/netfilter/nf_tproxy_core.h>
2628

2729
/**
28-
* tproxy_handle_time_wait() - handle TCP TIME_WAIT reopen redirections
30+
* tproxy_handle_time_wait4() - handle IPv4 TCP TIME_WAIT reopen redirections
2931
* @skb: The skb being processed.
30-
* @par: Iptables target parameters.
32+
* @laddr: IPv4 address to redirect to or zero.
33+
* @lport: TCP port to redirect to or zero.
3134
* @sk: The TIME_WAIT TCP socket found by the lookup.
3235
*
3336
* We have to handle SYN packets arriving to TIME_WAIT sockets
3437
* differently: instead of reopening the connection we should rather
3538
* redirect the new connection to the proxy if there's a listener
3639
* socket present.
3740
*
38-
* tproxy_handle_time_wait() consumes the socket reference passed in.
41+
* tproxy_handle_time_wait4() consumes the socket reference passed in.
3942
*
4043
* Returns the listener socket if there's one, the TIME_WAIT socket if
4144
* no such listener is found, or NULL if the TCP header is incomplete.
4245
*/
4346
static struct sock *
44-
tproxy_handle_time_wait(struct sk_buff *skb, const struct xt_action_param *par, struct sock *sk)
47+
tproxy_handle_time_wait4(struct sk_buff *skb, __be32 laddr, __be16 lport,
48+
struct sock *sk)
4549
{
4650
const struct iphdr *iph = ip_hdr(skb);
47-
const struct xt_tproxy_target_info *tgi = par->targinfo;
4851
struct tcphdr _hdr, *hp;
4952

5053
hp = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_hdr), &_hdr);
@@ -59,13 +62,64 @@ tproxy_handle_time_wait(struct sk_buff *skb, const struct xt_action_param *par,
5962
struct sock *sk2;
6063

6164
sk2 = nf_tproxy_get_sock_v4(dev_net(skb->dev), iph->protocol,
62-
iph->saddr, tgi->laddr ? tgi->laddr : iph->daddr,
63-
hp->source, tgi->lport ? tgi->lport : hp->dest,
64-
par->in, NFT_LOOKUP_LISTENER);
65+
iph->saddr, laddr ? laddr : iph->daddr,
66+
hp->source, lport ? lport : hp->dest,
67+
skb->dev, NFT_LOOKUP_LISTENER);
68+
if (sk2) {
69+
inet_twsk_deschedule(inet_twsk(sk), &tcp_death_row);
70+
inet_twsk_put(inet_twsk(sk));
71+
sk = sk2;
72+
}
73+
}
74+
75+
return sk;
76+
}
77+
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);
65122
if (sk2) {
66-
/* yeah, there's one, let's kill the TIME_WAIT
67-
* socket and redirect to the listener
68-
*/
69123
inet_twsk_deschedule(inet_twsk(sk), &tcp_death_row);
70124
inet_twsk_put(inet_twsk(sk));
71125
sk = sk2;
@@ -76,29 +130,116 @@ tproxy_handle_time_wait(struct sk_buff *skb, const struct xt_action_param *par,
76130
}
77131

78132
static unsigned int
79-
tproxy_tg(struct sk_buff *skb, const struct xt_action_param *par)
133+
tproxy_tg4(struct sk_buff *skb, __be32 laddr, __be16 lport,
134+
u_int32_t mark_mask, u_int32_t mark_value)
80135
{
81136
const struct iphdr *iph = ip_hdr(skb);
82-
const struct xt_tproxy_target_info *tgi = par->targinfo;
83137
struct udphdr _hdr, *hp;
84138
struct sock *sk;
85139

86140
hp = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_hdr), &_hdr);
87141
if (hp == NULL)
88142
return NF_DROP;
89143

144+
/* check if there's an ongoing connection on the packet
145+
* addresses, this happens if the redirect already happened
146+
* and the current packet belongs to an already established
147+
* connection */
90148
sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), iph->protocol,
91149
iph->saddr, iph->daddr,
92150
hp->source, hp->dest,
93-
par->in, NFT_LOOKUP_ESTABLISHED);
151+
skb->dev, NFT_LOOKUP_ESTABLISHED);
94152

95153
/* UDP has no TCP_TIME_WAIT state, so we never enter here */
96154
if (sk && sk->sk_state == TCP_TIME_WAIT)
97-
sk = tproxy_handle_time_wait(skb, par, sk);
155+
/* reopening a TIME_WAIT connection needs special handling */
156+
sk = tproxy_handle_time_wait4(skb, laddr, lport, sk);
98157
else if (!sk)
158+
/* no, there's no established connection, check if
159+
* there's a listener on the redirected addr/port */
99160
sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), iph->protocol,
100-
iph->saddr, tgi->laddr ? tgi->laddr : iph->daddr,
101-
hp->source, tgi->lport ? tgi->lport : hp->dest,
161+
iph->saddr, laddr ? laddr : iph->daddr,
162+
hp->source, lport ? lport : hp->dest,
163+
skb->dev, NFT_LOOKUP_LISTENER);
164+
165+
/* NOTE: assign_sock consumes our sk reference */
166+
if (sk && nf_tproxy_assign_sock(skb, sk)) {
167+
/* This should be in a separate target, but we don't do multiple
168+
targets on the same rule yet */
169+
skb->mark = (skb->mark & ~mark_mask) ^ mark_value;
170+
171+
pr_debug("redirecting: proto %hhu %pI4:%hu -> %pI4:%hu, mark: %x\n",
172+
iph->protocol, &iph->daddr, ntohs(hp->dest),
173+
&laddr, ntohs(lport), skb->mark);
174+
return NF_ACCEPT;
175+
}
176+
177+
pr_debug("no socket, dropping: proto %hhu %08x:%hu -> %08x:%hu, mark: %x\n",
178+
iph->protocol, ntohl(iph->daddr), ntohs(hp->dest),
179+
ntohl(laddr), ntohs(lport), skb->mark);
180+
return NF_DROP;
181+
}
182+
183+
static unsigned int
184+
tproxy_tg4_v0(struct sk_buff *skb, const struct xt_action_param *par)
185+
{
186+
const struct xt_tproxy_target_info *tgi = par->targinfo;
187+
188+
return tproxy_tg4(skb, tgi->laddr, tgi->lport, tgi->mark_mask, tgi->mark_value);
189+
}
190+
191+
static unsigned int
192+
tproxy_tg4_v1(struct sk_buff *skb, const struct xt_action_param *par)
193+
{
194+
const struct xt_tproxy_target_info_v1 *tgi = par->targinfo;
195+
196+
return tproxy_tg4(skb, tgi->laddr.ip, tgi->lport, tgi->mark_mask, tgi->mark_value);
197+
}
198+
199+
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
200+
static unsigned int
201+
tproxy_tg6_v1(struct sk_buff *skb, const struct xt_action_param *par)
202+
{
203+
const struct ipv6hdr *iph = ipv6_hdr(skb);
204+
const struct xt_tproxy_target_info_v1 *tgi = par->targinfo;
205+
struct udphdr _hdr, *hp;
206+
struct sock *sk;
207+
int thoff;
208+
int tproto;
209+
210+
tproto = ipv6_find_hdr(skb, &thoff, -1, NULL);
211+
if (tproto < 0) {
212+
pr_debug("unable to find transport header in IPv6 packet, dropping\n");
213+
return NF_DROP;
214+
}
215+
216+
hp = skb_header_pointer(skb, thoff, sizeof(_hdr), &_hdr);
217+
if (hp == NULL) {
218+
pr_debug("unable to grab transport header contents in IPv6 packet, dropping\n");
219+
return NF_DROP;
220+
}
221+
222+
/* check if there's an ongoing connection on the packet
223+
* addresses, this happens if the redirect already happened
224+
* and the current packet belongs to an already established
225+
* connection */
226+
sk = nf_tproxy_get_sock_v6(dev_net(skb->dev), tproto,
227+
&iph->saddr, &iph->daddr,
228+
hp->source, hp->dest,
229+
par->in, NFT_LOOKUP_ESTABLISHED);
230+
231+
/* UDP has no TCP_TIME_WAIT state, so we never enter here */
232+
if (sk && sk->sk_state == TCP_TIME_WAIT)
233+
/* reopening a TIME_WAIT connection needs special handling */
234+
sk = tproxy_handle_time_wait6(skb, tproto, thoff, par, sk);
235+
else if (!sk)
236+
/* no there's no established connection, check if
237+
* there's a listener on the redirected addr/port */
238+
sk = nf_tproxy_get_sock_v6(dev_net(skb->dev), tproto,
239+
&iph->saddr,
240+
!ipv6_addr_any(&tgi->laddr.in6) ? &tgi->laddr.in6 : &iph->daddr,
241+
hp->source,
242+
tgi->lport ? tgi->lport : hp->dest,
102243
par->in, NFT_LOOKUP_LISTENER);
103244

104245
/* NOTE: assign_sock consumes our sk reference */
@@ -107,19 +248,33 @@ tproxy_tg(struct sk_buff *skb, const struct xt_action_param *par)
107248
targets on the same rule yet */
108249
skb->mark = (skb->mark & ~tgi->mark_mask) ^ tgi->mark_value;
109250

110-
pr_debug("redirecting: proto %u %08x:%u -> %08x:%u, mark: %x\n",
111-
iph->protocol, ntohl(iph->daddr), ntohs(hp->dest),
112-
ntohl(tgi->laddr), ntohs(tgi->lport), skb->mark);
251+
pr_debug("redirecting: proto %hhu %pI6:%hu -> %pI6:%hu, mark: %x\n",
252+
tproto, &iph->saddr, ntohs(hp->dest),
253+
&tgi->laddr.in6, ntohs(tgi->lport), skb->mark);
113254
return NF_ACCEPT;
114255
}
115256

116-
pr_debug("no socket, dropping: proto %u %08x:%u -> %08x:%u, mark: %x\n",
117-
iph->protocol, ntohl(iph->daddr), ntohs(hp->dest),
118-
ntohl(tgi->laddr), ntohs(tgi->lport), skb->mark);
257+
pr_debug("no socket, dropping: proto %hhu %pI6:%hu -> %pI6:%hu, mark: %x\n",
258+
tproto, &iph->saddr, ntohs(hp->dest),
259+
&tgi->laddr.in6, ntohs(tgi->lport), skb->mark);
119260
return NF_DROP;
120261
}
121262

122-
static int tproxy_tg_check(const struct xt_tgchk_param *par)
263+
static int tproxy_tg6_check(const struct xt_tgchk_param *par)
264+
{
265+
const struct ip6t_ip6 *i = par->entryinfo;
266+
267+
if ((i->proto == IPPROTO_TCP || i->proto == IPPROTO_UDP)
268+
&& !(i->flags & IP6T_INV_PROTO))
269+
return 0;
270+
271+
pr_info("Can be used only in combination with "
272+
"either -p tcp or -p udp\n");
273+
return -EINVAL;
274+
}
275+
#endif
276+
277+
static int tproxy_tg4_check(const struct xt_tgchk_param *par)
123278
{
124279
const struct ipt_ip *i = par->entryinfo;
125280

@@ -132,31 +287,64 @@ static int tproxy_tg_check(const struct xt_tgchk_param *par)
132287
return -EINVAL;
133288
}
134289

135-
static struct xt_target tproxy_tg_reg __read_mostly = {
136-
.name = "TPROXY",
137-
.family = NFPROTO_IPV4,
138-
.table = "mangle",
139-
.target = tproxy_tg,
140-
.targetsize = sizeof(struct xt_tproxy_target_info),
141-
.checkentry = tproxy_tg_check,
142-
.hooks = 1 << NF_INET_PRE_ROUTING,
143-
.me = THIS_MODULE,
290+
static struct xt_target tproxy_tg_reg[] __read_mostly = {
291+
{
292+
.name = "TPROXY",
293+
.family = NFPROTO_IPV4,
294+
.table = "mangle",
295+
.target = tproxy_tg4_v0,
296+
.revision = 0,
297+
.targetsize = sizeof(struct xt_tproxy_target_info),
298+
.checkentry = tproxy_tg4_check,
299+
.hooks = 1 << NF_INET_PRE_ROUTING,
300+
.me = THIS_MODULE,
301+
},
302+
{
303+
.name = "TPROXY",
304+
.family = NFPROTO_IPV4,
305+
.table = "mangle",
306+
.target = tproxy_tg4_v1,
307+
.revision = 1,
308+
.targetsize = sizeof(struct xt_tproxy_target_info_v1),
309+
.checkentry = tproxy_tg4_check,
310+
.hooks = 1 << NF_INET_PRE_ROUTING,
311+
.me = THIS_MODULE,
312+
},
313+
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
314+
{
315+
.name = "TPROXY",
316+
.family = NFPROTO_IPV6,
317+
.table = "mangle",
318+
.target = tproxy_tg6_v1,
319+
.revision = 1,
320+
.targetsize = sizeof(struct xt_tproxy_target_info_v1),
321+
.checkentry = tproxy_tg6_check,
322+
.hooks = 1 << NF_INET_PRE_ROUTING,
323+
.me = THIS_MODULE,
324+
},
325+
#endif
326+
144327
};
145328

146329
static int __init tproxy_tg_init(void)
147330
{
148331
nf_defrag_ipv4_enable();
149-
return xt_register_target(&tproxy_tg_reg);
332+
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
333+
nf_defrag_ipv6_enable();
334+
#endif
335+
336+
return xt_register_targets(tproxy_tg_reg, ARRAY_SIZE(tproxy_tg_reg));
150337
}
151338

152339
static void __exit tproxy_tg_exit(void)
153340
{
154-
xt_unregister_target(&tproxy_tg_reg);
341+
xt_unregister_targets(tproxy_tg_reg, ARRAY_SIZE(tproxy_tg_reg));
155342
}
156343

157344
module_init(tproxy_tg_init);
158345
module_exit(tproxy_tg_exit);
159346
MODULE_LICENSE("GPL");
160-
MODULE_AUTHOR("Krisztian Kovacs");
347+
MODULE_AUTHOR("Balazs Scheidler, Krisztian Kovacs");
161348
MODULE_DESCRIPTION("Netfilter transparent proxy (TPROXY) target module.");
162349
MODULE_ALIAS("ipt_TPROXY");
350+
MODULE_ALIAS("ip6t_TPROXY");

0 commit comments

Comments
 (0)