Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 21 Jan 2002 14:36:35 +0000
From:      Ian Dowse <iedowse@maths.tcd.ie>
To:        freebsd-net@freebsd.org
Subject:   `options INET6' allows duplicate IPv4 binding 
Message-ID:   <200201211436.aa82109@salmon.maths.tcd.ie>

next in thread | raw e-mail | index | archive | help

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




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi? <200201211436.aa82109>