aboutsummaryrefslogtreecommitdiffstats
path: root/net/netfilter
diff options
context:
space:
mode:
authorBalazs Scheidler <bazsi@balabit.hu>2010-10-21 06:45:14 -0400
committerPatrick McHardy <kaber@trash.net>2010-10-21 06:45:14 -0400
commit106e4c26b1529e559d1aae777f11b4f8f7bafc26 (patch)
treee784c61379e767255bf941f9d78d9ef6e7c58640 /net/netfilter
parentd86bef73b4a24e59e7c1f896a72bbf38430ac2c6 (diff)
tproxy: kick out TIME_WAIT sockets in case a new connection comes in with the same tuple
Without tproxy redirections an incoming SYN kicks out conflicting TIME_WAIT sockets, in order to handle clients that reuse ports within the TIME_WAIT period. The same mechanism didn't work in case TProxy is involved in finding the proper socket, as the time_wait processing code looked up the listening socket assuming that the listener addr/port matches those of the established connection. This is not the case with TProxy as the listener addr/port is possibly changed with the tproxy rule. 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/nf_tproxy_core.c29
-rw-r--r--net/netfilter/xt_TPROXY.c68
-rw-r--r--net/netfilter/xt_socket.c2
3 files changed, 85 insertions, 14 deletions
diff --git a/net/netfilter/nf_tproxy_core.c b/net/netfilter/nf_tproxy_core.c
index 5490fc37c92..8589e5e0811 100644
--- a/net/netfilter/nf_tproxy_core.c
+++ b/net/netfilter/nf_tproxy_core.c
@@ -22,21 +22,34 @@ struct sock *
22nf_tproxy_get_sock_v4(struct net *net, const u8 protocol, 22nf_tproxy_get_sock_v4(struct net *net, const u8 protocol,
23 const __be32 saddr, const __be32 daddr, 23 const __be32 saddr, const __be32 daddr,
24 const __be16 sport, const __be16 dport, 24 const __be16 sport, const __be16 dport,
25 const struct net_device *in, bool listening_only) 25 const struct net_device *in, int lookup_type)
26{ 26{
27 struct sock *sk; 27 struct sock *sk;
28 28
29 /* look up socket */ 29 /* look up socket */
30 switch (protocol) { 30 switch (protocol) {
31 case IPPROTO_TCP: 31 case IPPROTO_TCP:
32 if (listening_only) 32 switch (lookup_type) {
33 sk = __inet_lookup_listener(net, &tcp_hashinfo, 33 case NFT_LOOKUP_ANY:
34 daddr, ntohs(dport),
35 in->ifindex);
36 else
37 sk = __inet_lookup(net, &tcp_hashinfo, 34 sk = __inet_lookup(net, &tcp_hashinfo,
38 saddr, sport, daddr, dport, 35 saddr, sport, daddr, dport,
39 in->ifindex); 36 in->ifindex);
37 break;
38 case NFT_LOOKUP_LISTENER:
39 sk = inet_lookup_listener(net, &tcp_hashinfo,
40 daddr, dport,
41 in->ifindex);
42 break;
43 case NFT_LOOKUP_ESTABLISHED:
44 sk = inet_lookup_established(net, &tcp_hashinfo,
45 saddr, sport, daddr, dport,
46 in->ifindex);
47 break;
48 default:
49 WARN_ON(1);
50 sk = NULL;
51 break;
52 }
40 break; 53 break;
41 case IPPROTO_UDP: 54 case IPPROTO_UDP:
42 sk = udp4_lib_lookup(net, saddr, sport, daddr, dport, 55 sk = udp4_lib_lookup(net, saddr, sport, daddr, dport,
@@ -47,8 +60,8 @@ nf_tproxy_get_sock_v4(struct net *net, const u8 protocol,
47 sk = NULL; 60 sk = NULL;
48 } 61 }
49 62
50 pr_debug("tproxy socket lookup: proto %u %08x:%u -> %08x:%u, listener only: %d, sock %p\n", 63 pr_debug("tproxy socket lookup: proto %u %08x:%u -> %08x:%u, lookup type: %d, sock %p\n",
51 protocol, ntohl(saddr), ntohs(sport), ntohl(daddr), ntohs(dport), listening_only, sk); 64 protocol, ntohl(saddr), ntohs(sport), ntohl(daddr), ntohs(dport), lookup_type, sk);
52 65
53 return sk; 66 return sk;
54} 67}
diff --git a/net/netfilter/xt_TPROXY.c b/net/netfilter/xt_TPROXY.c
index 21bb2aff6b8..e0b6900a92c 100644
--- a/net/netfilter/xt_TPROXY.c
+++ b/net/netfilter/xt_TPROXY.c
@@ -24,6 +24,57 @@
24#include <net/netfilter/ipv4/nf_defrag_ipv4.h> 24#include <net/netfilter/ipv4/nf_defrag_ipv4.h>
25#include <net/netfilter/nf_tproxy_core.h> 25#include <net/netfilter/nf_tproxy_core.h>
26 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 */
43static struct sock *
44tproxy_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
27static unsigned int 78static unsigned int
28tproxy_tg(struct sk_buff *skb, const struct xt_action_param *par) 79tproxy_tg(struct sk_buff *skb, const struct xt_action_param *par)
29{ 80{
@@ -37,11 +88,18 @@ tproxy_tg(struct sk_buff *skb, const struct xt_action_param *par)
37 return NF_DROP; 88 return NF_DROP;
38 89
39 sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), iph->protocol, 90 sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), iph->protocol,
40 iph->saddr, 91 iph->saddr, iph->daddr,
41 tgi->laddr ? tgi->laddr : iph->daddr, 92 hp->source, hp->dest,
42 hp->source, 93 par->in, NFT_LOOKUP_ESTABLISHED);
43 tgi->lport ? tgi->lport : hp->dest, 94
44 par->in, true); 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);
45 103
46 /* NOTE: assign_sock consumes our sk reference */ 104 /* NOTE: assign_sock consumes our sk reference */
47 if (sk && nf_tproxy_assign_sock(skb, sk)) { 105 if (sk && nf_tproxy_assign_sock(skb, sk)) {
diff --git a/net/netfilter/xt_socket.c b/net/netfilter/xt_socket.c
index 1ca89908cba..266faa0a2fb 100644
--- a/net/netfilter/xt_socket.c
+++ b/net/netfilter/xt_socket.c
@@ -142,7 +142,7 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par,
142#endif 142#endif
143 143
144 sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), protocol, 144 sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), protocol,
145 saddr, daddr, sport, dport, par->in, false); 145 saddr, daddr, sport, dport, par->in, NFT_LOOKUP_ANY);
146 if (sk != NULL) { 146 if (sk != NULL) {
147 bool wildcard; 147 bool wildcard;
148 bool transparent = true; 148 bool transparent = true;