diff options
author | Balazs Scheidler <bazsi@balabit.hu> | 2010-10-21 07:06:43 -0400 |
---|---|---|
committer | Patrick McHardy <kaber@trash.net> | 2010-10-21 07:06:43 -0400 |
commit | 093d282321daeb19c107e5f1f16d7f68484f3ade (patch) | |
tree | 36e9eed23573068819bf67a91caac6ebf60d0d7c /net/dccp | |
parent | 6006db84a91838813cdad8a6622a4e39efe9ea47 (diff) |
tproxy: fix hash locking issue when using port redirection in __inet_inherit_port()
When __inet_inherit_port() is called on a tproxy connection the wrong locks are
held for the inet_bind_bucket it is added to. __inet_inherit_port() made an
implicit assumption that the listener's port number (and thus its bind bucket).
Unfortunately, if you're using the TPROXY target to redirect skbs to a
transparent proxy that assumption is not true anymore and things break.
This patch adds code to __inet_inherit_port() so that it can handle this case
by looking up or creating a new bind bucket for the child socket and updates
callers of __inet_inherit_port() to gracefully handle __inet_inherit_port()
failing.
Reported by and original patch from Stephen Buck <stephen.buck@exinda.com>.
See http://marc.info/?t=128169268200001&r=1&w=2 for the original discussion.
Signed-off-by: KOVACS Krisztian <hidden@balabit.hu>
Signed-off-by: Patrick McHardy <kaber@trash.net>
Diffstat (limited to 'net/dccp')
-rw-r--r-- | net/dccp/ipv4.c | 10 | ||||
-rw-r--r-- | net/dccp/ipv6.c | 10 |
2 files changed, 14 insertions, 6 deletions
diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index d4a166f0f391..3f69ea114829 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c | |||
@@ -392,7 +392,7 @@ struct sock *dccp_v4_request_recv_sock(struct sock *sk, struct sk_buff *skb, | |||
392 | 392 | ||
393 | newsk = dccp_create_openreq_child(sk, req, skb); | 393 | newsk = dccp_create_openreq_child(sk, req, skb); |
394 | if (newsk == NULL) | 394 | if (newsk == NULL) |
395 | goto exit; | 395 | goto exit_nonewsk; |
396 | 396 | ||
397 | sk_setup_caps(newsk, dst); | 397 | sk_setup_caps(newsk, dst); |
398 | 398 | ||
@@ -409,16 +409,20 @@ struct sock *dccp_v4_request_recv_sock(struct sock *sk, struct sk_buff *skb, | |||
409 | 409 | ||
410 | dccp_sync_mss(newsk, dst_mtu(dst)); | 410 | dccp_sync_mss(newsk, dst_mtu(dst)); |
411 | 411 | ||
412 | if (__inet_inherit_port(sk, newsk) < 0) { | ||
413 | sock_put(newsk); | ||
414 | goto exit; | ||
415 | } | ||
412 | __inet_hash_nolisten(newsk, NULL); | 416 | __inet_hash_nolisten(newsk, NULL); |
413 | __inet_inherit_port(sk, newsk); | ||
414 | 417 | ||
415 | return newsk; | 418 | return newsk; |
416 | 419 | ||
417 | exit_overflow: | 420 | exit_overflow: |
418 | NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS); | 421 | NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS); |
422 | exit_nonewsk: | ||
423 | dst_release(dst); | ||
419 | exit: | 424 | exit: |
420 | NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS); | 425 | NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS); |
421 | dst_release(dst); | ||
422 | return NULL; | 426 | return NULL; |
423 | } | 427 | } |
424 | 428 | ||
diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index 6e3f32575df7..dca711df9b60 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c | |||
@@ -564,7 +564,7 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk, | |||
564 | 564 | ||
565 | newsk = dccp_create_openreq_child(sk, req, skb); | 565 | newsk = dccp_create_openreq_child(sk, req, skb); |
566 | if (newsk == NULL) | 566 | if (newsk == NULL) |
567 | goto out; | 567 | goto out_nonewsk; |
568 | 568 | ||
569 | /* | 569 | /* |
570 | * No need to charge this sock to the relevant IPv6 refcnt debug socks | 570 | * No need to charge this sock to the relevant IPv6 refcnt debug socks |
@@ -632,18 +632,22 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk, | |||
632 | newinet->inet_daddr = newinet->inet_saddr = LOOPBACK4_IPV6; | 632 | newinet->inet_daddr = newinet->inet_saddr = LOOPBACK4_IPV6; |
633 | newinet->inet_rcv_saddr = LOOPBACK4_IPV6; | 633 | newinet->inet_rcv_saddr = LOOPBACK4_IPV6; |
634 | 634 | ||
635 | if (__inet_inherit_port(sk, newsk) < 0) { | ||
636 | sock_put(newsk); | ||
637 | goto out; | ||
638 | } | ||
635 | __inet6_hash(newsk, NULL); | 639 | __inet6_hash(newsk, NULL); |
636 | __inet_inherit_port(sk, newsk); | ||
637 | 640 | ||
638 | return newsk; | 641 | return newsk; |
639 | 642 | ||
640 | out_overflow: | 643 | out_overflow: |
641 | NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS); | 644 | NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS); |
645 | out_nonewsk: | ||
646 | dst_release(dst); | ||
642 | out: | 647 | out: |
643 | NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS); | 648 | NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS); |
644 | if (opt != NULL && opt != np->opt) | 649 | if (opt != NULL && opt != np->opt) |
645 | sock_kfree_s(sk, opt, opt->tot_len); | 650 | sock_kfree_s(sk, opt, opt->tot_len); |
646 | dst_release(dst); | ||
647 | return NULL; | 651 | return NULL; |
648 | } | 652 | } |
649 | 653 | ||