diff options
author | Florian Westphal <fwestphal@astaro.com> | 2011-02-17 05:32:38 -0500 |
---|---|---|
committer | Patrick McHardy <kaber@trash.net> | 2011-02-17 05:32:38 -0500 |
commit | d503b30bd648b3cb4e5f50b65d27e389960cc6d9 (patch) | |
tree | ebb3e2ad85f32cd95c767dda75d25873c384e78d | |
parent | de9963f0f2dfad128b26ae7bf6005f5948416a6d (diff) |
netfilter: tproxy: do not assign timewait sockets to skb->sk
Assigning a socket in timewait state to skb->sk can trigger
kernel oops, e.g. in nfnetlink_log, which does:
if (skb->sk) {
read_lock_bh(&skb->sk->sk_callback_lock);
if (skb->sk->sk_socket && skb->sk->sk_socket->file) ...
in the timewait case, accessing sk->sk_callback_lock and sk->sk_socket
is invalid.
Either all of these spots will need to add a test for sk->sk_state != TCP_TIME_WAIT,
or xt_TPROXY must not assign a timewait socket to skb->sk.
This does the latter.
If a TW socket is found, assign the tproxy nfmark, but skip the skb->sk assignment,
thus mimicking behaviour of a '-m socket .. -j MARK/ACCEPT' re-routing rule.
The 'SYN to TW socket' case is left unchanged -- we try to redirect to the
listener socket.
Cc: Balazs Scheidler <bazsi@balabit.hu>
Cc: KOVACS Krisztian <hidden@balabit.hu>
Signed-off-by: Florian Westphal <fwestphal@astaro.com>
Signed-off-by: Patrick McHardy <kaber@trash.net>
-rw-r--r-- | include/net/netfilter/nf_tproxy_core.h | 12 | ||||
-rw-r--r-- | net/netfilter/nf_tproxy_core.c | 27 | ||||
-rw-r--r-- | net/netfilter/xt_TPROXY.c | 22 | ||||
-rw-r--r-- | net/netfilter/xt_socket.c | 13 |
4 files changed, 44 insertions, 30 deletions
diff --git a/include/net/netfilter/nf_tproxy_core.h b/include/net/netfilter/nf_tproxy_core.h index cd85b3bc8327..e505358d8999 100644 --- a/include/net/netfilter/nf_tproxy_core.h +++ b/include/net/netfilter/nf_tproxy_core.h | |||
@@ -201,18 +201,8 @@ nf_tproxy_get_sock_v6(struct net *net, const u8 protocol, | |||
201 | } | 201 | } |
202 | #endif | 202 | #endif |
203 | 203 | ||
204 | static inline void | ||
205 | nf_tproxy_put_sock(struct sock *sk) | ||
206 | { | ||
207 | /* TIME_WAIT inet sockets have to be handled differently */ | ||
208 | if ((sk->sk_protocol == IPPROTO_TCP) && (sk->sk_state == TCP_TIME_WAIT)) | ||
209 | inet_twsk_put(inet_twsk(sk)); | ||
210 | else | ||
211 | sock_put(sk); | ||
212 | } | ||
213 | |||
214 | /* assign a socket to the skb -- consumes sk */ | 204 | /* assign a socket to the skb -- consumes sk */ |
215 | int | 205 | void |
216 | nf_tproxy_assign_sock(struct sk_buff *skb, struct sock *sk); | 206 | nf_tproxy_assign_sock(struct sk_buff *skb, struct sock *sk); |
217 | 207 | ||
218 | #endif | 208 | #endif |
diff --git a/net/netfilter/nf_tproxy_core.c b/net/netfilter/nf_tproxy_core.c index 4d87befb04c0..474d621cbc2e 100644 --- a/net/netfilter/nf_tproxy_core.c +++ b/net/netfilter/nf_tproxy_core.c | |||
@@ -28,26 +28,23 @@ nf_tproxy_destructor(struct sk_buff *skb) | |||
28 | skb->destructor = NULL; | 28 | skb->destructor = NULL; |
29 | 29 | ||
30 | if (sk) | 30 | if (sk) |
31 | nf_tproxy_put_sock(sk); | 31 | sock_put(sk); |
32 | } | 32 | } |
33 | 33 | ||
34 | /* consumes sk */ | 34 | /* consumes sk */ |
35 | int | 35 | void |
36 | nf_tproxy_assign_sock(struct sk_buff *skb, struct sock *sk) | 36 | nf_tproxy_assign_sock(struct sk_buff *skb, struct sock *sk) |
37 | { | 37 | { |
38 | bool transparent = (sk->sk_state == TCP_TIME_WAIT) ? | 38 | /* assigning tw sockets complicates things; most |
39 | inet_twsk(sk)->tw_transparent : | 39 | * skb->sk->X checks would have to test sk->sk_state first */ |
40 | inet_sk(sk)->transparent; | 40 | if (sk->sk_state == TCP_TIME_WAIT) { |
41 | 41 | inet_twsk_put(inet_twsk(sk)); | |
42 | if (transparent) { | 42 | return; |
43 | skb_orphan(skb); | 43 | } |
44 | skb->sk = sk; | 44 | |
45 | skb->destructor = nf_tproxy_destructor; | 45 | skb_orphan(skb); |
46 | return 1; | 46 | skb->sk = sk; |
47 | } else | 47 | skb->destructor = nf_tproxy_destructor; |
48 | nf_tproxy_put_sock(sk); | ||
49 | |||
50 | return 0; | ||
51 | } | 48 | } |
52 | EXPORT_SYMBOL_GPL(nf_tproxy_assign_sock); | 49 | EXPORT_SYMBOL_GPL(nf_tproxy_assign_sock); |
53 | 50 | ||
diff --git a/net/netfilter/xt_TPROXY.c b/net/netfilter/xt_TPROXY.c index 640678f47a2a..dcfd57eb9d02 100644 --- a/net/netfilter/xt_TPROXY.c +++ b/net/netfilter/xt_TPROXY.c | |||
@@ -33,6 +33,20 @@ | |||
33 | #include <net/netfilter/nf_tproxy_core.h> | 33 | #include <net/netfilter/nf_tproxy_core.h> |
34 | #include <linux/netfilter/xt_TPROXY.h> | 34 | #include <linux/netfilter/xt_TPROXY.h> |
35 | 35 | ||
36 | static bool tproxy_sk_is_transparent(struct sock *sk) | ||
37 | { | ||
38 | if (sk->sk_state != TCP_TIME_WAIT) { | ||
39 | if (inet_sk(sk)->transparent) | ||
40 | return true; | ||
41 | sock_put(sk); | ||
42 | } else { | ||
43 | if (inet_twsk(sk)->tw_transparent) | ||
44 | return true; | ||
45 | inet_twsk_put(inet_twsk(sk)); | ||
46 | } | ||
47 | return false; | ||
48 | } | ||
49 | |||
36 | static inline __be32 | 50 | static inline __be32 |
37 | tproxy_laddr4(struct sk_buff *skb, __be32 user_laddr, __be32 daddr) | 51 | tproxy_laddr4(struct sk_buff *skb, __be32 user_laddr, __be32 daddr) |
38 | { | 52 | { |
@@ -141,7 +155,7 @@ tproxy_tg4(struct sk_buff *skb, __be32 laddr, __be16 lport, | |||
141 | skb->dev, NFT_LOOKUP_LISTENER); | 155 | skb->dev, NFT_LOOKUP_LISTENER); |
142 | 156 | ||
143 | /* NOTE: assign_sock consumes our sk reference */ | 157 | /* NOTE: assign_sock consumes our sk reference */ |
144 | if (sk && nf_tproxy_assign_sock(skb, sk)) { | 158 | if (sk && tproxy_sk_is_transparent(sk)) { |
145 | /* This should be in a separate target, but we don't do multiple | 159 | /* This should be in a separate target, but we don't do multiple |
146 | targets on the same rule yet */ | 160 | targets on the same rule yet */ |
147 | skb->mark = (skb->mark & ~mark_mask) ^ mark_value; | 161 | skb->mark = (skb->mark & ~mark_mask) ^ mark_value; |
@@ -149,6 +163,8 @@ tproxy_tg4(struct sk_buff *skb, __be32 laddr, __be16 lport, | |||
149 | pr_debug("redirecting: proto %hhu %pI4:%hu -> %pI4:%hu, mark: %x\n", | 163 | pr_debug("redirecting: proto %hhu %pI4:%hu -> %pI4:%hu, mark: %x\n", |
150 | iph->protocol, &iph->daddr, ntohs(hp->dest), | 164 | iph->protocol, &iph->daddr, ntohs(hp->dest), |
151 | &laddr, ntohs(lport), skb->mark); | 165 | &laddr, ntohs(lport), skb->mark); |
166 | |||
167 | nf_tproxy_assign_sock(skb, sk); | ||
152 | return NF_ACCEPT; | 168 | return NF_ACCEPT; |
153 | } | 169 | } |
154 | 170 | ||
@@ -306,7 +322,7 @@ tproxy_tg6_v1(struct sk_buff *skb, const struct xt_action_param *par) | |||
306 | par->in, NFT_LOOKUP_LISTENER); | 322 | par->in, NFT_LOOKUP_LISTENER); |
307 | 323 | ||
308 | /* NOTE: assign_sock consumes our sk reference */ | 324 | /* NOTE: assign_sock consumes our sk reference */ |
309 | if (sk && nf_tproxy_assign_sock(skb, sk)) { | 325 | if (sk && tproxy_sk_is_transparent(sk)) { |
310 | /* This should be in a separate target, but we don't do multiple | 326 | /* This should be in a separate target, but we don't do multiple |
311 | targets on the same rule yet */ | 327 | targets on the same rule yet */ |
312 | skb->mark = (skb->mark & ~tgi->mark_mask) ^ tgi->mark_value; | 328 | skb->mark = (skb->mark & ~tgi->mark_mask) ^ tgi->mark_value; |
@@ -314,6 +330,8 @@ tproxy_tg6_v1(struct sk_buff *skb, const struct xt_action_param *par) | |||
314 | pr_debug("redirecting: proto %hhu %pI6:%hu -> %pI6:%hu, mark: %x\n", | 330 | pr_debug("redirecting: proto %hhu %pI6:%hu -> %pI6:%hu, mark: %x\n", |
315 | tproto, &iph->saddr, ntohs(hp->source), | 331 | tproto, &iph->saddr, ntohs(hp->source), |
316 | laddr, ntohs(lport), skb->mark); | 332 | laddr, ntohs(lport), skb->mark); |
333 | |||
334 | nf_tproxy_assign_sock(skb, sk); | ||
317 | return NF_ACCEPT; | 335 | return NF_ACCEPT; |
318 | } | 336 | } |
319 | 337 | ||
diff --git a/net/netfilter/xt_socket.c b/net/netfilter/xt_socket.c index 00d6ae838303..9cc46356b577 100644 --- a/net/netfilter/xt_socket.c +++ b/net/netfilter/xt_socket.c | |||
@@ -35,6 +35,15 @@ | |||
35 | #include <net/netfilter/nf_conntrack.h> | 35 | #include <net/netfilter/nf_conntrack.h> |
36 | #endif | 36 | #endif |
37 | 37 | ||
38 | static void | ||
39 | xt_socket_put_sk(struct sock *sk) | ||
40 | { | ||
41 | if (sk->sk_state == TCP_TIME_WAIT) | ||
42 | inet_twsk_put(inet_twsk(sk)); | ||
43 | else | ||
44 | sock_put(sk); | ||
45 | } | ||
46 | |||
38 | static int | 47 | static int |
39 | extract_icmp4_fields(const struct sk_buff *skb, | 48 | extract_icmp4_fields(const struct sk_buff *skb, |
40 | u8 *protocol, | 49 | u8 *protocol, |
@@ -164,7 +173,7 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par, | |||
164 | (sk->sk_state == TCP_TIME_WAIT && | 173 | (sk->sk_state == TCP_TIME_WAIT && |
165 | inet_twsk(sk)->tw_transparent)); | 174 | inet_twsk(sk)->tw_transparent)); |
166 | 175 | ||
167 | nf_tproxy_put_sock(sk); | 176 | xt_socket_put_sk(sk); |
168 | 177 | ||
169 | if (wildcard || !transparent) | 178 | if (wildcard || !transparent) |
170 | sk = NULL; | 179 | sk = NULL; |
@@ -298,7 +307,7 @@ socket_mt6_v1(const struct sk_buff *skb, struct xt_action_param *par) | |||
298 | (sk->sk_state == TCP_TIME_WAIT && | 307 | (sk->sk_state == TCP_TIME_WAIT && |
299 | inet_twsk(sk)->tw_transparent)); | 308 | inet_twsk(sk)->tw_transparent)); |
300 | 309 | ||
301 | nf_tproxy_put_sock(sk); | 310 | xt_socket_put_sk(sk); |
302 | 311 | ||
303 | if (wildcard || !transparent) | 312 | if (wildcard || !transparent) |
304 | sk = NULL; | 313 | sk = NULL; |