]> git.karo-electronics.de Git - karo-tx-linux.git/blob - net/netfilter/xt_TPROXY.c
tproxy: kick out TIME_WAIT sockets in case a new connection comes in with the same...
[karo-tx-linux.git] / net / netfilter / xt_TPROXY.c
1 /*
2  * Transparent proxy support for Linux/iptables
3  *
4  * Copyright (c) 2006-2007 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/xt_TPROXY.h>
23
24 #include <net/netfilter/ipv4/nf_defrag_ipv4.h>
25 #include <net/netfilter/nf_tproxy_core.h>
26
27 /**
28  * tproxy_handle_time_wait() - handle TCP TIME_WAIT reopen redirections
29  * @skb:        The skb being processed.
30  * @par:        Iptables target parameters.
31  * @sk:         The TIME_WAIT TCP socket found by the lookup.
32  *
33  * We have to handle SYN packets arriving to TIME_WAIT sockets
34  * differently: instead of reopening the connection we should rather
35  * redirect the new connection to the proxy if there's a listener
36  * socket present.
37  *
38  * tproxy_handle_time_wait() consumes the socket reference passed in.
39  *
40  * 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.
42  */
43 static struct sock *
44 tproxy_handle_time_wait(struct sk_buff *skb, const struct xt_action_param *par, struct sock *sk)
45 {
46         const struct iphdr *iph = ip_hdr(skb);
47         const struct xt_tproxy_target_info *tgi = par->targinfo;
48         struct tcphdr _hdr, *hp;
49
50         hp = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_hdr), &_hdr);
51         if (hp == NULL) {
52                 inet_twsk_put(inet_twsk(sk));
53                 return NULL;
54         }
55
56         if (hp->syn && !hp->rst && !hp->ack && !hp->fin) {
57                 /* SYN to a TIME_WAIT socket, we'd rather redirect it
58                  * to a listener socket if there's one */
59                 struct sock *sk2;
60
61                 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                 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);
70                         inet_twsk_put(inet_twsk(sk));
71                         sk = sk2;
72                 }
73         }
74
75         return sk;
76 }
77
78 static unsigned int
79 tproxy_tg(struct sk_buff *skb, const struct xt_action_param *par)
80 {
81         const struct iphdr *iph = ip_hdr(skb);
82         const struct xt_tproxy_target_info *tgi = par->targinfo;
83         struct udphdr _hdr, *hp;
84         struct sock *sk;
85
86         hp = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_hdr), &_hdr);
87         if (hp == NULL)
88                 return NF_DROP;
89
90         sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), iph->protocol,
91                                    iph->saddr, iph->daddr,
92                                    hp->source, hp->dest,
93                                    par->in, NFT_LOOKUP_ESTABLISHED);
94
95         /* UDP has no TCP_TIME_WAIT state, so we never enter here */
96         if (sk && sk->sk_state == TCP_TIME_WAIT)
97                 sk = tproxy_handle_time_wait(skb, par, sk);
98         else if (!sk)
99                 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,
102                                            par->in, NFT_LOOKUP_LISTENER);
103
104         /* NOTE: assign_sock consumes our sk reference */
105         if (sk && nf_tproxy_assign_sock(skb, sk)) {
106                 /* This should be in a separate target, but we don't do multiple
107                    targets on the same rule yet */
108                 skb->mark = (skb->mark & ~tgi->mark_mask) ^ tgi->mark_value;
109
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);
113                 return NF_ACCEPT;
114         }
115
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);
119         return NF_DROP;
120 }
121
122 static int tproxy_tg_check(const struct xt_tgchk_param *par)
123 {
124         const struct ipt_ip *i = par->entryinfo;
125
126         if ((i->proto == IPPROTO_TCP || i->proto == IPPROTO_UDP)
127             && !(i->invflags & IPT_INV_PROTO))
128                 return 0;
129
130         pr_info("Can be used only in combination with "
131                 "either -p tcp or -p udp\n");
132         return -EINVAL;
133 }
134
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,
144 };
145
146 static int __init tproxy_tg_init(void)
147 {
148         nf_defrag_ipv4_enable();
149         return xt_register_target(&tproxy_tg_reg);
150 }
151
152 static void __exit tproxy_tg_exit(void)
153 {
154         xt_unregister_target(&tproxy_tg_reg);
155 }
156
157 module_init(tproxy_tg_init);
158 module_exit(tproxy_tg_exit);
159 MODULE_LICENSE("GPL");
160 MODULE_AUTHOR("Krisztian Kovacs");
161 MODULE_DESCRIPTION("Netfilter transparent proxy (TPROXY) target module.");
162 MODULE_ALIAS("ipt_TPROXY");