Skip to contentSkip to conversation

port_reuse listen fix (allow simultaneous single listen + outgoing connects from same port)

PATCH In review Ilya Pashkovsky 2 participants 3 messages Dec 9, 2004 3 messages not in the archive

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 port_reuse listen fix (allow simultaneous single listen + outgoing connects from same port)

This is the latest patch with removed bool > 1 check and ipv6 support.
http://puding.mine.nu/patches/
http://puding.mine.nu/patches/patch-reuse-bool-ipv6

to check, you can use netcat (sets SO_REUSEADDR by default). on one host (host A): nc -v -l -p 9999
on another/same host (host B): nc -v -l -p 9000
on host A: nc -v -p 9999 host.B.ip.addr 9000
on host B: nc -v host.A.ip.addr 9999

nothing should fail.

net/ipv4/tcp_ipv4.c2004-12-08 16:20:32.018896416 +0200 renamed from net/ipv4/tcp_ipv4.c.orig2004-12-07 14:54:12.597084704 +0200 +10 -4
@@ -50,6 +50,8 @@
  *YOSHIFUJI Hideaki @USAGI and:Support IPV6_V6ONLY socket option, which
  *Alexey Kuznetsovallow both IPv4 and IPv6 sockets to bind
  *a single port at the same time.
+ *Ilya Pashkovsky:fix TCP_LISTEN check on reuse
+ *sk_reuse boolean fix
  */
 
 #include <linux/config.h>
@@ -184,7 +186,8 @@ static inline int tcp_bind_conflict(stru
 const u32 sk_rcv_saddr = tcp_v4_rcv_saddr(sk);
 struct sock *sk2;
 struct hlist_node *node;
-int reuse = sk->sk_reuse;
+unsigned char reuse = sk->sk_reuse;
+unsigned char state = sk->sk_state;
 
 sk_for_each_bound(sk2, node, &tb->owners) {
 if (sk != sk2 &&
@@ -193,7 +196,7 @@ static inline int tcp_bind_conflict(stru
      !sk2->sk_bound_dev_if ||
      sk->sk_bound_dev_if == sk2->sk_bound_dev_if)) {
 if (!reuse || !sk2->sk_reuse ||
-    sk2->sk_state == TCP_LISTEN) {
+    (state == TCP_LISTEN && sk2->sk_state == TCP_LISTEN)) {
 const u32 sk2_rcv_saddr = tcp_v4_rcv_saddr(sk2);
 if (!sk2_rcv_saddr || !sk_rcv_saddr ||
     sk2_rcv_saddr == sk_rcv_saddr)
@@ -259,8 +262,11 @@ static int tcp_v4_get_port(struct sock *
 goto tb_not_found;
 tb_found:
 if (!hlist_empty(&tb->owners)) {
-if (sk->sk_reuse > 1)
-goto success;
+/*
+ * sk_reuse is boolean
+ * if (sk->sk_reuse > 1)
+ *goto success;
+ */
 if (tb->fastreuse > 0 &&
     sk->sk_reuse && sk->sk_state != TCP_LISTEN) {
 goto success;
net/ipv6/tcp_ipv6.c2004-12-09 01:34:38.162714320 +0200 renamed from net/ipv6/tcp_ipv6.c.orig2004-12-09 01:35:33.162353104 +0200 +1 -1
@@ -111,7 +111,7 @@ static inline int tcp_v6_bind_conflict(s
      !sk2->sk_bound_dev_if ||
      sk->sk_bound_dev_if == sk2->sk_bound_dev_if) &&
     (!sk->sk_reuse || !sk2->sk_reuse ||
-     sk2->sk_state == TCP_LISTEN) &&
+     (sk->sk_state == TCP_LISTEN && sk2->sk_state == TCP_LISTEN)) &&
      ipv6_rcv_saddr_equal(sk, sk2))
 break;
 }
#1 lore raw
Quoted 9 lines · Ilya Pashkovsky

This is the latest patch with removed bool > 1 check and ipv6 support.
http://puding.mine.nu/patches/
http://puding.mine.nu/patches/patch-reuse-bool-ipv6

to check, you can use netcat (sets SO_REUSEADDR by default). on one host (host A): nc -v -l -p 9999
on another/same host (host B): nc -v -l -p 9000
on host A: nc -v -p 9999 host.B.ip.addr 9000
on host B: nc -v host.A.ip.addr 9999

What happens if on host B you do

nc -v -p 9000 host.A.ip.addr 9999?

Seems to me you will break the rule that a connection is uniquely identified by (srcpip, destip, srcport, destport).

Ross

#5 lore raw

PATCH 1/1 port_reuse listen fix (allow simultaneous single listen + outgoing connects from same port)

In reply to a message not in the archive

if the SYN of clientA is accepted before clientB called connect and clientB is listening on that port, the connection will be accepted no matter what, and this is the expected and good behavior. In process of calling connect(), clientB will get an EADDRINUSE error and will stop connecting.
In case the calls are already underway to connect (ports bound) then the new packets will get into the new cross-connection by default and not into the listening socket, since the new cross-connection tuple exists. This is guaranteed by setting the connection state flag before calling get_port.
Can still see no added ambiguities in this patch yet...If you can help find some, it would be very nice of you indeed.

Thanks for comments up to now.

Quoted 64 lines · Ross Biro

But what if the tuple is not taken. This code exposes a race condition.

Imagine if first you bind the servers and listne.

Then you bind the clients.

Then the clients send the syn packets.

If the syn's cross on the wire, then the clients will connect to each other. If one of the syns arrives before the other machine calls connect, then one machine will have a minisocket for the server, but the other will still be able to send a syn, which will cause a bogus reset and kill one of the connections. I'm not 100% sure which one, but my guess would be the new one.

In any event, you have a bunch of bad behaviour at the boundary and need to do something about it.

Ross

On Thu, 9 Dec 2004 20:10:27 +0200, Ilya Pashkovsky

Quoted 39 lines · <[email protected]> wrote:

if this tuple (srcip,destip,srcport,destport) is already taken, you'll get an EADDRINUSE error as you should. The fix only fixes the behaviour of not allowing even a single listener to coexist with outgoing connections from same port. In fact, SO_REUSEADDR on linux should and does implement the behaviour of SO_REUSEPORT of BSD, except for listener preemption (since its not useful and would require several security checks).
The current fix allows piercing firewalls for the needing and maybe TCP NAT traversal in the future (if some vendor produces a Full-cone TCP NAT).

Quoted 23 lines · Ross Biro

On Thu, 9 Dec 2004 13:25:26 +0200, Ilya Pashkovsky

Quoted 9 lines · <[email protected]> wrote:

This is the latest patch with removed bool > 1 check and ipv6 support.
http://puding.mine.nu/patches/
http://puding.mine.nu/patches/patch-reuse-bool-ipv6

to check, you can use netcat (sets SO_REUSEADDR by default). on one host (host A): nc -v -l -p 9999
on another/same host (host B): nc -v -l -p 9000
on host A: nc -v -p 9999 host.B.ip.addr 9000
on host B: nc -v host.A.ip.addr 9999

What happens if on host B you do

nc -v -p 9000 host.A.ip.addr 9999?

Seems to me you will break the rule that a connection is uniquely identified by (srcpip, destip, srcport, destport).

Ross