diff options
author | Balazs Scheidler <bazsi@balabit.hu> | 2010-10-21 06:45:14 -0400 |
---|---|---|
committer | Patrick McHardy <kaber@trash.net> | 2010-10-21 06:45:14 -0400 |
commit | 106e4c26b1529e559d1aae777f11b4f8f7bafc26 (patch) | |
tree | e784c61379e767255bf941f9d78d9ef6e7c58640 /net/netfilter | |
parent | d86bef73b4a24e59e7c1f896a72bbf38430ac2c6 (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.c | 29 | ||||
-rw-r--r-- | net/netfilter/xt_TPROXY.c | 68 | ||||
-rw-r--r-- | net/netfilter/xt_socket.c | 2 |
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 * | |||
22 | nf_tproxy_get_sock_v4(struct net *net, const u8 protocol, | 22 | nf_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 | */ | ||
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 | |||
27 | static unsigned int | 78 | static unsigned int |
28 | tproxy_tg(struct sk_buff *skb, const struct xt_action_param *par) | 79 | tproxy_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; |