1
1
/*
2
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
5
* Author: Balazs Scheidler, Krisztian Kovacs
6
6
*
7
7
* This program is free software; you can redistribute it and/or modify
19
19
20
20
#include <linux/netfilter/x_tables.h>
21
21
#include <linux/netfilter_ipv4/ip_tables.h>
22
+ #include <linux/netfilter_ipv6/ip6_tables.h>
22
23
#include <linux/netfilter/xt_TPROXY.h>
23
24
24
25
#include <net/netfilter/ipv4/nf_defrag_ipv4.h>
26
+ #include <net/netfilter/ipv6/nf_defrag_ipv6.h>
25
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
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
34
* @sk: The TIME_WAIT TCP socket found by the lookup.
32
35
*
33
36
* We have to handle SYN packets arriving to TIME_WAIT sockets
34
37
* differently: instead of reopening the connection we should rather
35
38
* redirect the new connection to the proxy if there's a listener
36
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
43
* Returns the listener socket if there's one, the TIME_WAIT socket if
41
44
* no such listener is found, or NULL if the TCP header is incomplete.
42
45
*/
43
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
50
const struct iphdr * iph = ip_hdr (skb );
47
- const struct xt_tproxy_target_info * tgi = par -> targinfo ;
48
51
struct tcphdr _hdr , * hp ;
49
52
50
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
62
struct sock * sk2 ;
60
63
61
64
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 );
65
122
if (sk2 ) {
66
- /* yeah, there's one, let's kill the TIME_WAIT
67
- * socket and redirect to the listener
68
- */
69
123
inet_twsk_deschedule (inet_twsk (sk ), & tcp_death_row );
70
124
inet_twsk_put (inet_twsk (sk ));
71
125
sk = sk2 ;
@@ -76,29 +130,116 @@ tproxy_handle_time_wait(struct sk_buff *skb, const struct xt_action_param *par,
76
130
}
77
131
78
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
136
const struct iphdr * iph = ip_hdr (skb );
82
- const struct xt_tproxy_target_info * tgi = par -> targinfo ;
83
137
struct udphdr _hdr , * hp ;
84
138
struct sock * sk ;
85
139
86
140
hp = skb_header_pointer (skb , ip_hdrlen (skb ), sizeof (_hdr ), & _hdr );
87
141
if (hp == NULL )
88
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
148
sk = nf_tproxy_get_sock_v4 (dev_net (skb -> dev ), iph -> protocol ,
91
149
iph -> saddr , iph -> daddr ,
92
150
hp -> source , hp -> dest ,
93
- par -> in , NFT_LOOKUP_ESTABLISHED );
151
+ skb -> dev , NFT_LOOKUP_ESTABLISHED );
94
152
95
153
/* UDP has no TCP_TIME_WAIT state, so we never enter here */
96
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
157
else if (!sk )
158
+ /* no, there's no established connection, check if
159
+ * there's a listener on the redirected addr/port */
99
160
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 ,
102
243
par -> in , NFT_LOOKUP_LISTENER );
103
244
104
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
248
targets on the same rule yet */
108
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" ,
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 );
113
254
return NF_ACCEPT ;
114
255
}
115
256
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 );
119
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
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
287
return - EINVAL ;
133
288
}
134
289
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
+
144
327
};
145
328
146
329
static int __init tproxy_tg_init (void )
147
330
{
148
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
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
344
module_init (tproxy_tg_init );
158
345
module_exit (tproxy_tg_exit );
159
346
MODULE_LICENSE ("GPL" );
160
- MODULE_AUTHOR ("Krisztian Kovacs" );
347
+ MODULE_AUTHOR ("Balazs Scheidler, Krisztian Kovacs" );
161
348
MODULE_DESCRIPTION ("Netfilter transparent proxy (TPROXY) target module." );
162
349
MODULE_ALIAS ("ipt_TPROXY" );
350
+ MODULE_ALIAS ("ip6t_TPROXY" );
0 commit comments