]> git.karo-electronics.de Git - karo-tx-linux.git/blob - net/netfilter/xt_TPROXY.c
tproxy: added IPv6 support to the TPROXY target
[karo-tx-linux.git] / net / netfilter / xt_TPROXY.c
1 /*
2  * Transparent proxy support for Linux/iptables
3  *
4  * Copyright (c) 2006-2010 BalaBit IT Ltd.
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  */
12 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
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>
19
20 #include <linux/netfilter/x_tables.h>
21 #include <linux/netfilter_ipv4/ip_tables.h>
22 #include <linux/netfilter_ipv6/ip6_tables.h>
23 #include <linux/netfilter/xt_TPROXY.h>
24
25 #include <net/netfilter/ipv4/nf_defrag_ipv4.h>
26 #include <net/netfilter/ipv6/nf_defrag_ipv6.h>
27 #include <net/netfilter/nf_tproxy_core.h>
28
29 /**
30  * tproxy_handle_time_wait4() - handle IPv4 TCP TIME_WAIT reopen redirections
31  * @skb:        The skb being processed.
32  * @laddr:      IPv4 address to redirect to or zero.
33  * @lport:      TCP port to redirect to or zero.
34  * @sk:         The TIME_WAIT TCP socket found by the lookup.
35  *
36  * We have to handle SYN packets arriving to TIME_WAIT sockets
37  * differently: instead of reopening the connection we should rather
38  * redirect the new connection to the proxy if there's a listener
39  * socket present.
40  *
41  * tproxy_handle_time_wait4() consumes the socket reference passed in.
42  *
43  * Returns the listener socket if there's one, the TIME_WAIT socket if
44  * no such listener is found, or NULL if the TCP header is incomplete.
45  */
46 static struct sock *
47 tproxy_handle_time_wait4(struct sk_buff *skb, __be32 laddr, __be16 lport,
48                         struct sock *sk)
49 {
50         const struct iphdr *iph = ip_hdr(skb);
51         struct tcphdr _hdr, *hp;
52
53         hp = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_hdr), &_hdr);
54         if (hp == NULL) {
55                 inet_twsk_put(inet_twsk(sk));
56                 return NULL;
57         }
58
59         if (hp->syn && !hp->rst && !hp->ack && !hp->fin) {
60                 /* SYN to a TIME_WAIT socket, we'd rather redirect it
61                  * to a listener socket if there's one */
62                 struct sock *sk2;
63
64                 sk2 = nf_tproxy_get_sock_v4(dev_net(skb->dev), iph->protocol,
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);
122                 if (sk2) {
123                         inet_twsk_deschedule(inet_twsk(sk), &tcp_death_row);
124                         inet_twsk_put(inet_twsk(sk));
125                         sk = sk2;
126                 }
127         }
128
129         return sk;
130 }
131
132 static unsigned int
133 tproxy_tg4(struct sk_buff *skb, __be32 laddr, __be16 lport,
134            u_int32_t mark_mask, u_int32_t mark_value)
135 {
136         const struct iphdr *iph = ip_hdr(skb);
137         struct udphdr _hdr, *hp;
138         struct sock *sk;
139
140         hp = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_hdr), &_hdr);
141         if (hp == NULL)
142                 return NF_DROP;
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 */
148         sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), iph->protocol,
149                                    iph->saddr, iph->daddr,
150                                    hp->source, hp->dest,
151                                    skb->dev, NFT_LOOKUP_ESTABLISHED);
152
153         /* UDP has no TCP_TIME_WAIT state, so we never enter here */
154         if (sk && sk->sk_state == TCP_TIME_WAIT)
155                 /* reopening a TIME_WAIT connection needs special handling */
156                 sk = tproxy_handle_time_wait4(skb, laddr, lport, sk);
157         else if (!sk)
158                 /* no, there's no established connection, check if
159                  * there's a listener on the redirected addr/port */
160                 sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), iph->protocol,
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,
243                                            par->in, NFT_LOOKUP_LISTENER);
244
245         /* NOTE: assign_sock consumes our sk reference */
246         if (sk && nf_tproxy_assign_sock(skb, sk)) {
247                 /* This should be in a separate target, but we don't do multiple
248                    targets on the same rule yet */
249                 skb->mark = (skb->mark & ~tgi->mark_mask) ^ tgi->mark_value;
250
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);
254                 return NF_ACCEPT;
255         }
256
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);
260         return NF_DROP;
261 }
262
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)
278 {
279         const struct ipt_ip *i = par->entryinfo;
280
281         if ((i->proto == IPPROTO_TCP || i->proto == IPPROTO_UDP)
282             && !(i->invflags & IPT_INV_PROTO))
283                 return 0;
284
285         pr_info("Can be used only in combination with "
286                 "either -p tcp or -p udp\n");
287         return -EINVAL;
288 }
289
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
327 };
328
329 static int __init tproxy_tg_init(void)
330 {
331         nf_defrag_ipv4_enable();
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));
337 }
338
339 static void __exit tproxy_tg_exit(void)
340 {
341         xt_unregister_targets(tproxy_tg_reg, ARRAY_SIZE(tproxy_tg_reg));
342 }
343
344 module_init(tproxy_tg_init);
345 module_exit(tproxy_tg_exit);
346 MODULE_LICENSE("GPL");
347 MODULE_AUTHOR("Balazs Scheidler, Krisztian Kovacs");
348 MODULE_DESCRIPTION("Netfilter transparent proxy (TPROXY) target module.");
349 MODULE_ALIAS("ipt_TPROXY");
350 MODULE_ALIAS("ip6t_TPROXY");