Semantics of SO_REUSEADDR and P2P TCP NAT traversal

Previous thread: [patch/backport] CFS scheduler, -v22, for v2.6.23-rc8, v2.6.22.8, v2.6.21.7, v2.6.20.20 by Ingo Molnar on Wednesday, September 26, 2007 - 7:13 am. (9 messages)

Next thread: Re: Chroot bug by Miloslav Semler on Wednesday, September 26, 2007 - 7:34 am. (2 messages)
To: <linux-kernel@...>
Date: Wednesday, September 26, 2007 - 7:04 am

Folks,

I'm working on implementing a TCP NAT traversal scheme for a P2P
application, similar to that described in:

http://www.brynosaurus.com/pub/net/p2pnat/

and also in

http://tools.ietf.org/html/draft-ietf-behave-p2p-state-03 [3.4]

The idea in using TCP is to provide a P2P file transfer architecture
which retains the benefits of TCP's windowing and congestion control and
hence is more efficient and network-friendly than the current UDP-based
ones.

NAT 'hole punching' for TCP essentially depends on the two peers
more-or-less simultaneously opening mirrored connections to each other,
and hoping that the intervening NATs' 'conntrack'-equivalents will allow
the SYN exchange. If the connections are _really_ simultaneous, so that
the SYNs cross on the wire, this might also trigger a simultaneous-open
transition on the peers.

To make this work, the peers have to both be initiating from the same
port they are listening on, so that their SYNs match. This would
apparently break the "4-tuples uniquely identify a socket" rule at each
end, but this is transient - only one of the two sockets at each end
will end up connected.

Ford et al. say in the above paper that the main issue with implementing
this through the BSD sockets API is the ability to have both a
listen()ing socket and an outgoing connection bound to the same local
port, but that SO_REUSEADDR (and SO_REUSEPORT, where defined) comes to
our rescue. However my initial implementation of this fails with
EADDRINUSE (simplified psuedo-code):

==
fd_listen = socket(PF_INET, SOCK_STREAM, 0)
setsockopt(fd_listen, SOL_SOCKET, SO_REUSEADDR, 1)
bind(fd_listen, sockaddr_in(127.0.0.1, 11111))
listen(fd_listen)

fd_out = socket(PF_INET, SOCK_STREAM, 0)
setsockopt(fd_out, SOL_SOCKET, SO_REUSEADDR, 1)
bind(fd_out, socketaddr_in(127.0.0.1, 11111)) => EADDRINUSE
==

Just to note, it also fails in the same way with INADDR_ANY or a real
interface IP in either bind().

However, if I bind() t...

Previous thread: [patch/backport] CFS scheduler, -v22, for v2.6.23-rc8, v2.6.22.8, v2.6.21.7, v2.6.20.20 by Ingo Molnar on Wednesday, September 26, 2007 - 7:13 am. (9 messages)

Next thread: Re: Chroot bug by Miloslav Semler on Wednesday, September 26, 2007 - 7:34 am. (2 messages)