diff options
author | Balazs Scheidler <bazsi@balabit.hu> | 2010-10-21 10:17:26 -0400 |
---|---|---|
committer | Patrick McHardy <kaber@trash.net> | 2010-10-21 10:17:26 -0400 |
commit | 6ad7889327a5ee6ab4220bd34e4428c7d0de0f32 (patch) | |
tree | 19c90cbc4b00aa4aaf5e65b5b65e1b9a95443890 /net/netfilter | |
parent | 3b9afb29917f4ab08decf358ecfd354a72a91ac0 (diff) |
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 <bazsi@balabit.hu>
Signed-off-by: KOVACS Krisztian <hidden@balabit.hu>
Signed-off-by: Patrick McHardy <kaber@trash.net>
Diffstat (limited to 'net/netfilter')
-rw-r--r-- | net/netfilter/xt_TPROXY.c | 262 |
1 files changed, 225 insertions, 37 deletions
diff --git a/net/netfilter/xt_TPROXY.c b/net/netfilter/xt_TPROXY.c index e0b6900a92c1..d5f97e2302b8 100644 --- a/net/netfilter/xt_TPROXY.c +++ b/net/netfilter/xt_TPROXY.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * Transparent proxy support for Linux/iptables | 2 | * Transparent proxy support for Linux/iptables |
3 | * | 3 | * |
4 | * Copyright (c) 2006-2007 BalaBit IT Ltd. | 4 | * Copyright (c) 2006-2010 BalaBit IT Ltd. |
5 | * Author: Balazs Scheidler, Krisztian Kovacs | 5 | * Author: Balazs Scheidler, Krisztian Kovacs |
6 | * | 6 | * |
7 | * This program is free software; you can redistribute it and/or modify | 7 | * This program is free software; you can redistribute it and/or modify |
@@ -19,15 +19,18 @@ | |||
19 | 19 | ||
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> | ||
22 | #include <linux/netfilter/xt_TPROXY.h> | 23 | #include <linux/netfilter/xt_TPROXY.h> |
23 | 24 | ||
24 | #include <net/netfilter/ipv4/nf_defrag_ipv4.h> | 25 | #include <net/netfilter/ipv4/nf_defrag_ipv4.h> |
26 | #include <net/netfilter/ipv6/nf_defrag_ipv6.h> | ||
25 | #include <net/netfilter/nf_tproxy_core.h> | 27 | #include <net/netfilter/nf_tproxy_core.h> |
26 | 28 | ||
27 | /** | 29 | /** |
28 | * tproxy_handle_time_wait() - handle TCP TIME_WAIT reopen redirections | 30 | * tproxy_handle_time_wait4() - handle IPv4 TCP TIME_WAIT reopen redirections |
29 | * @skb: The skb being processed. | 31 | * @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. | ||
31 | * @sk: The TIME_WAIT TCP socket found by the lookup. | 34 | * @sk: The TIME_WAIT TCP socket found by the lookup. |
32 | * | 35 | * |
33 | * We have to handle SYN packets arriving to TIME_WAIT sockets | 36 | * We have to handle SYN packets arriving to TIME_WAIT sockets |
@@ -35,16 +38,16 @@ | |||
35 | * redirect the new connection to the proxy if there's a listener | 38 | * redirect the new connection to the proxy if there's a listener |
36 | * socket present. | 39 | * socket present. |
37 | * | 40 | * |
38 | * tproxy_handle_time_wait() consumes the socket reference passed in. | 41 | * tproxy_handle_time_wait4() consumes the socket reference passed in. |
39 | * | 42 | * |
40 | * Returns the listener socket if there's one, the TIME_WAIT socket if | 43 | * Returns the listener socket if there's one, the TIME_WAIT socket if |
41 | * no such listener is found, or NULL if the TCP header is incomplete. | 44 | * no such listener is found, or NULL if the TCP header is incomplete. |
42 | */ | 45 | */ |
43 | static struct sock * | 46 | 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) | ||
45 | { | 49 | { |
46 | const struct iphdr *iph = ip_hdr(skb); | 50 | const struct iphdr *iph = ip_hdr(skb); |
47 | const struct xt_tproxy_target_info *tgi = par->targinfo; | ||
48 | struct tcphdr _hdr, *hp; | 51 | struct tcphdr _hdr, *hp; |
49 | 52 | ||
50 | hp = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_hdr), &_hdr); | 53 | 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, | |||
59 | struct sock *sk2; | 62 | struct sock *sk2; |
60 | 63 | ||
61 | sk2 = nf_tproxy_get_sock_v4(dev_net(skb->dev), iph->protocol, | 64 | sk2 = nf_tproxy_get_sock_v4(dev_net(skb->dev), iph->protocol, |
62 | iph->saddr, tgi->laddr ? tgi->laddr : iph->daddr, | 65 | iph->saddr, laddr ? laddr : iph->daddr, |
63 | hp->source, tgi->lport ? tgi->lport : hp->dest, | 66 | hp->source, lport ? lport : hp->dest, |
64 | par->in, NFT_LOOKUP_LISTENER); | 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); | ||
65 | if (sk2) { | 122 | if (sk2) { |
66 | /* yeah, there's one, let's kill the TIME_WAIT | ||
67 | * socket and redirect to the listener | ||
68 | */ | ||
69 | inet_twsk_deschedule(inet_twsk(sk), &tcp_death_row); | 123 | inet_twsk_deschedule(inet_twsk(sk), &tcp_death_row); |
70 | inet_twsk_put(inet_twsk(sk)); | 124 | inet_twsk_put(inet_twsk(sk)); |
71 | sk = sk2; | 125 | sk = sk2; |
@@ -76,10 +130,10 @@ tproxy_handle_time_wait(struct sk_buff *skb, const struct xt_action_param *par, | |||
76 | } | 130 | } |
77 | 131 | ||
78 | static unsigned int | 132 | 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) | ||
80 | { | 135 | { |
81 | const struct iphdr *iph = ip_hdr(skb); | 136 | const struct iphdr *iph = ip_hdr(skb); |
82 | const struct xt_tproxy_target_info *tgi = par->targinfo; | ||
83 | struct udphdr _hdr, *hp; | 137 | struct udphdr _hdr, *hp; |
84 | struct sock *sk; | 138 | struct sock *sk; |
85 | 139 | ||
@@ -87,18 +141,105 @@ tproxy_tg(struct sk_buff *skb, const struct xt_action_param *par) | |||
87 | if (hp == NULL) | 141 | if (hp == NULL) |
88 | return NF_DROP; | 142 | return NF_DROP; |
89 | 143 | ||
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 */ | ||
90 | sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), iph->protocol, | 148 | sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), iph->protocol, |
91 | iph->saddr, iph->daddr, | 149 | iph->saddr, iph->daddr, |
92 | hp->source, hp->dest, | 150 | hp->source, hp->dest, |
93 | par->in, NFT_LOOKUP_ESTABLISHED); | 151 | skb->dev, NFT_LOOKUP_ESTABLISHED); |
94 | 152 | ||
95 | /* UDP has no TCP_TIME_WAIT state, so we never enter here */ | 153 | /* UDP has no TCP_TIME_WAIT state, so we never enter here */ |
96 | if (sk && sk->sk_state == TCP_TIME_WAIT) | 154 | 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); | ||
98 | else if (!sk) | 157 | else if (!sk) |
158 | /* no, there's no established connection, check if | ||
159 | * there's a listener on the redirected addr/port */ | ||
99 | sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), iph->protocol, | 160 | sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), iph->protocol, |
100 | iph->saddr, tgi->laddr ? tgi->laddr : iph->daddr, | 161 | iph->saddr, laddr ? laddr : iph->daddr, |
101 | hp->source, tgi->lport ? tgi->lport : hp->dest, | 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, | ||
102 | par->in, NFT_LOOKUP_LISTENER); | 243 | par->in, NFT_LOOKUP_LISTENER); |
103 | 244 | ||
104 | /* NOTE: assign_sock consumes our sk reference */ | 245 | /* NOTE: assign_sock consumes our sk reference */ |
@@ -107,19 +248,33 @@ tproxy_tg(struct sk_buff *skb, const struct xt_action_param *par) | |||
107 | targets on the same rule yet */ | 248 | targets on the same rule yet */ |
108 | skb->mark = (skb->mark & ~tgi->mark_mask) ^ tgi->mark_value; | 249 | skb->mark = (skb->mark & ~tgi->mark_mask) ^ tgi->mark_value; |
109 | 250 | ||
110 | pr_debug("redirecting: proto %u %08x:%u -> %08x:%u, mark: %x\n", | 251 | pr_debug("redirecting: proto %hhu %pI6:%hu -> %pI6:%hu, mark: %x\n", |
111 | iph->protocol, ntohl(iph->daddr), ntohs(hp->dest), | 252 | tproto, &iph->saddr, ntohs(hp->dest), |
112 | ntohl(tgi->laddr), ntohs(tgi->lport), skb->mark); | 253 | &tgi->laddr.in6, ntohs(tgi->lport), skb->mark); |
113 | return NF_ACCEPT; | 254 | return NF_ACCEPT; |
114 | } | 255 | } |
115 | 256 | ||
116 | pr_debug("no socket, dropping: proto %u %08x:%u -> %08x:%u, mark: %x\n", | 257 | pr_debug("no socket, dropping: proto %hhu %pI6:%hu -> %pI6:%hu, mark: %x\n", |
117 | iph->protocol, ntohl(iph->daddr), ntohs(hp->dest), | 258 | tproto, &iph->saddr, ntohs(hp->dest), |
118 | ntohl(tgi->laddr), ntohs(tgi->lport), skb->mark); | 259 | &tgi->laddr.in6, ntohs(tgi->lport), skb->mark); |
119 | return NF_DROP; | 260 | return NF_DROP; |
120 | } | 261 | } |
121 | 262 | ||
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) | ||
123 | { | 278 | { |
124 | const struct ipt_ip *i = par->entryinfo; | 279 | const struct ipt_ip *i = par->entryinfo; |
125 | 280 | ||
@@ -132,31 +287,64 @@ static int tproxy_tg_check(const struct xt_tgchk_param *par) | |||
132 | return -EINVAL; | 287 | return -EINVAL; |
133 | } | 288 | } |
134 | 289 | ||
135 | static struct xt_target tproxy_tg_reg __read_mostly = { | 290 | static struct xt_target tproxy_tg_reg[] __read_mostly = { |
136 | .name = "TPROXY", | 291 | { |
137 | .family = NFPROTO_IPV4, | 292 | .name = "TPROXY", |
138 | .table = "mangle", | 293 | .family = NFPROTO_IPV4, |
139 | .target = tproxy_tg, | 294 | .table = "mangle", |
140 | .targetsize = sizeof(struct xt_tproxy_target_info), | 295 | .target = tproxy_tg4_v0, |
141 | .checkentry = tproxy_tg_check, | 296 | .revision = 0, |
142 | .hooks = 1 << NF_INET_PRE_ROUTING, | 297 | .targetsize = sizeof(struct xt_tproxy_target_info), |
143 | .me = THIS_MODULE, | 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 | |||
144 | }; | 327 | }; |
145 | 328 | ||
146 | static int __init tproxy_tg_init(void) | 329 | static int __init tproxy_tg_init(void) |
147 | { | 330 | { |
148 | nf_defrag_ipv4_enable(); | 331 | 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)); | ||
150 | } | 337 | } |
151 | 338 | ||
152 | static void __exit tproxy_tg_exit(void) | 339 | static void __exit tproxy_tg_exit(void) |
153 | { | 340 | { |
154 | xt_unregister_target(&tproxy_tg_reg); | 341 | xt_unregister_targets(tproxy_tg_reg, ARRAY_SIZE(tproxy_tg_reg)); |
155 | } | 342 | } |
156 | 343 | ||
157 | module_init(tproxy_tg_init); | 344 | module_init(tproxy_tg_init); |
158 | module_exit(tproxy_tg_exit); | 345 | module_exit(tproxy_tg_exit); |
159 | MODULE_LICENSE("GPL"); | 346 | MODULE_LICENSE("GPL"); |
160 | MODULE_AUTHOR("Krisztian Kovacs"); | 347 | MODULE_AUTHOR("Balazs Scheidler, Krisztian Kovacs"); |
161 | MODULE_DESCRIPTION("Netfilter transparent proxy (TPROXY) target module."); | 348 | MODULE_DESCRIPTION("Netfilter transparent proxy (TPROXY) target module."); |
162 | MODULE_ALIAS("ipt_TPROXY"); | 349 | MODULE_ALIAS("ipt_TPROXY"); |
350 | MODULE_ALIAS("ip6t_TPROXY"); | ||