From owner-freebsd-net Mon Jan 21 6:36:40 2002 Delivered-To: freebsd-net@freebsd.org Received: from salmon.maths.tcd.ie (salmon.maths.tcd.ie [134.226.81.11]) by hub.freebsd.org (Postfix) with SMTP id 8CCEE37B404 for ; Mon, 21 Jan 2002 06:36:36 -0800 (PST) Received: from walton.maths.tcd.ie by salmon.maths.tcd.ie with SMTP id ; 21 Jan 2002 14:36:35 +0000 (GMT) To: freebsd-net@freebsd.org Subject: `options INET6' allows duplicate IPv4 binding Date: Mon, 21 Jan 2002 14:36:35 +0000 From: Ian Dowse Message-ID: <200201211436.aa82109@salmon.maths.tcd.ie> Sender: owner-freebsd-net@FreeBSD.ORG Precedence: bulk List-ID: List-Archive: (Web Archive) List-Help: (List Instructions) List-Subscribe: List-Unsubscribe: X-Loop: FreeBSD.org I was just trying to track down a weird behaviour I had observed involving VNC and sshd. When a user logs in via sshd, their $DISPLAY would often end up being the same as that of an existing Xvnc session: > sockstat | grep 6013 |grep '\*' root sshd 25710 7 tcp4 *:6013 *:* userX Xvnc 51102 0 tcp4 *:6013 *:* root sshd 25710 6 tcp46 *:6013 *:* As you can see, sshd has successfully bound its tcp4 socket to the same wildcard address as Xvnc was already using, but sshd does not set any of the SO_REUSE* options. It seems that this is caused by the following code in in_pcb.c: t = in_pcblookup_local(pcbinfo, sin->sin_addr, lport, prison ? 0 : wild); if (t && (reuseport & t->inp_socket->so_options) == 0) { #if defined(INET6) if (ntohl(sin->sin_addr.s_addr) != INADDR_ANY || ntohl(t->inp_laddr.s_addr) != INADDR_ANY || INP_SOCKAF(so) == INP_SOCKAF(t->inp_socket)) #endif /* defined(INET6) */ return (EADDRINUSE); } Here is a nasty perl script that demonstrates the problem (tested on RELENG_4 and CURRENT). We are able to bind 2 IPv4 sockets to a single wildcard port. ------------------------------------- #!/usr/bin/perl use Socket; $PF_INET6 = 28; $port = pack("n", 12345); socket(S4, PF_INET, SOCK_STREAM, 0) || die "S4: socket: $!\n"; $sin4 = "\x10\x02$port\0\0\0\0\0\0\0\0\0\0\0\0"; bind(S4, $sin4) || die "S4: bind: $!\n"; socket(S6, $PF_INET6, SOCK_STREAM, 0) || die "S6: socket: $!\n"; $sin6 = "\x1c\x1c$port\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; bind(S6, $sin6) || die "S6: bind: $!\n"; socket(S4B, PF_INET, SOCK_STREAM, 0) || die "S4B: socket: $!\n"; bind(S4B, $sin4) || die "S4B: bind: $! [expected]\n"; die "second V4 bind succeeded! [not expected]\n"; ------------------------------------- Setting net.inet6.ip6.v6only to 1 restores the expected behaviour, and the second IPv4 bind returns EADDRINUSE. I guess that the code is attempting to allow simultaneous tcp46 and tcp4 bindings, but it ends up allowing two tcp4 sockets to bind to the same address if a tcp46 bind comes in between. Any ideas on how to fix this? Ian To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-net" in the body of the message