Subject: kern/21713: INET6_IPV6ONLY causes inbound IPv4 connections to ECONNABORTED
To: None <gnats-bugs@gnats.netbsd.org>
From: None <tv@pobox.com>
List: netbsd-bugs
Date: 05/29/2003 12:33:52
>Number:         21713
>Category:       kern
>Synopsis:       INET6_IPV6ONLY causes inbound IPv4 connections to ECONNABORTED
>Confidential:   no
>Severity:       critical
>Priority:       high
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Thu May 29 16:35:00 UTC 2003
>Closed-Date:
>Last-Modified:
>Originator:     Todd Vierling
>Release:        NetBSD 1.6.1
>Organization:
	DUH.ORG:  Pointing out the obvious since 1994.
>Environment:
System: NetBSD netbsd.int.duh.org 1.6.1 NetBSD 1.6.1 (GENERIC) #0: Tue Apr 8 12:05:52 UTC 2003 autobuild@tgm.daemon.org:/autobuild/netbsd-1-6/i386/OBJ/autobuild/netbsd-1-6/src/sys/arch/i386/compile/GENERIC i386
Architecture: i386
Machine: i386
>Description:

If sysctl "net.inet6.ip6.v6only" is set to 0, in6addr_any listening
sockets accept IPv4 connections just fine.

However, if this sysctl is 1 (the default), accepting IPv4 connections
requires setting option INET6_IPV6ONLY before bind(2).  This doesn't work.  
IPv6 connections are still accepted fine, but IPv4 connections cause
accept() to return -1 with errno ECONNABORTED.

(Context:  This causes KDE3 apps which open listen sockets, such as krfbd,
to fail after applying a patch discussed in KDE bug 59100 to implement use
of INET6_IPV6ONLY.)

>How-To-Repeat:

Run the following, then try connecting with telnet or similar to both ::1
port 5901, and 127.0.0.1 port 5901.  The accept() should always work
regardless of the sysctl setting above, but fails when
net.inet6.ip6.v6only=1 and connecting via IPv4 (127.0.0.1).

===== BEGIN =====
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <err.h>
#include <unistd.h>

int main(void) {
	int sock = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP);
	int opt = 0;
	int optlen = sizeof(opt);
	struct sockaddr_in6 sa = {0};

	if (sock < 0)
		err(1, "socket");

	if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&opt, optlen))
		err(1, "setsockopt(v6only)");

	opt = 1;

	if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&opt, optlen))
		err(1, "setsockopt(reuseaddr)");

	sa.sin6_family = AF_INET6;
	sa.sin6_addr = in6addr_any;
	sa.sin6_port = htons(5901);

	if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)))
		err(1, "bind");

	if (listen(sock, 3))
		err(1, "listen");

	if (accept(sock, NULL, NULL) < 0)
		err(1, "accept");
}
===== END =====

>Fix:

Unknown.  This is probably a very roundabout issue deep in the netinet6
stack, and I can't find it (a netinet6 guru needs to look at this).

in6_pcballoc() appears to set the flag IN6P_IPV6_V6ONLY if the sysctl is
on; this is the same flag changed by the setsockopt call.  Something must
not be properly handling the socket-internal data when this is not set at
pcballoc time and is set by setsockopt later.
>Release-Note:
>Audit-Trail:
>Unformatted: