[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#520628: libc6: getaddrinfo now prefers listening on ipv4-only over ipv4+ipv6.



Package: libc6
Version: 2.9-6
Severity: normal
Tags: ipv6

I've written a test program, gaitest.c, attached...

On Sid:
em@amd64:/tmp$ gcc -o gt gaitest.c && ./gt 
When given unspecified protocol, host:(null) and port:12345, as hints...
This machine prefers listening on (in order of preference):
	* ipv4 (only)
	* ipv6+ipv4
gem@amd64:/tmp$ grep '^#*label' /etc/gai.conf
label ::1/128       0
label ::/0          1
#label 2002::/16     2
label ::/96         3
label ::ffff:0:0/96 4
label fec0::/10     5
label fc00::/7      6
label 2001:0::/32   7
gem@amd64:/tmp$ grep localhost /etc/hosts
127.0.0.1 localhost localhost.localdomain
::1     localhost ip6-localhost ip6-loopback
gem@amd64:/tmp$ getent hosts localhost
::1             localhost ip6-localhost ip6-loopback
gem@amd64:/tmp$ ./gt localhost
When given unspecified protocol, host:localhost and port:12345, as hints...
This machine prefers listening on (in order of preference):
	* ipv6+ipv4
	* ipv4 (only)

Test program on Etch for comparison:
gem@gamezone:/tmp$ gcc -o gt gaitest.c && ./gt
When given unspecified protocol, NULL host and port 12345, as hints...
This machine prefers listening on (in order of preference):
        * ipv6+ipv4
        * ipv4 (only)


Both machines have both ipv4 and ipv6 connectivity.


I don't understand why it should be preferred to listen for
ipv4-only connections over *both*.

Listening for ipv6 connections never hurts, even if noone will ever
reach the server via ipv6.....
Right now the socket option IPV6_V6ONLY is pretty useless, since we'll have to
explicitly set up an ipv6 socket if we ever want anything else then ipv4.

I hope this is not a duplicate, couldn't find a suitable existing bug.
I know there's been a long history of getaddrinfo issues. While some are
debatable I really wonder about this one since I can't think of any use
for looking up without address for anything but listening. I'm really starting
to wonder how many times getaddrinfo can be implemented incorrectly. With
everchanging behaviour it'll probably become one of those functions you can't
rely on and every application writer starts having their own version of.

I hope someone with clue on what's going on upstream can help me sort this out!

Regards,
Andreas Henriksson

-- System Information:
Debian Release: squeeze/sid
  APT prefers unstable
  APT policy: (300, 'unstable'), (100, 'experimental')
Architecture: amd64 (x86_64)

Kernel: Linux 2.6.26-1-amd64 (SMP w/1 CPU core)
Locale: LANG=en_US.UTF-8, LC_CTYPE=sv_SE.UTF-8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/bash

Versions of packages libc6 depends on:
ii  libgcc1                       1:4.3.3-5  GCC support library

libc6 recommends no packages.

Versions of packages libc6 suggests:
ii  glibc-doc                     2.9-6      GNU C Library: Documentation
ii  locales                       2.9-6      GNU C Library: National Language (

-- debconf-show failed
/* A test program that prints out what the machines preferred options
 * when getaddrinfo gets free hands.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>

int main(int argc, char *argv[])
{
	int listenfd, err;
	struct addrinfo hints, *res, *curr;
	char *host = NULL, *serv = "12345";

	if (argc > 1)
		host = argv[1];
	if (argc > 2)
		serv = argv[2];

	memset(&hints, 0, sizeof(hints));
	hints.ai_family = AF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_protocol = IPPROTO_TCP;
	hints.ai_flags = AI_PASSIVE;

	err = getaddrinfo(host, serv, &hints, &res);
	if (err) {
		fprintf(stderr, "ERROR: getaddrinfo() failed: %s\n",
				gai_strerror(err));
		return -1;
	}

#if 1
	printf("When given unspecified protocol, host:%s and port:%s, as hints...\n", host, serv);

	printf("This machine prefers listening on (in order of preference):\n");
	for (curr = res; curr != NULL; curr = curr->ai_next) {
		listenfd = socket(curr->ai_family, curr->ai_socktype, curr->ai_protocol);
		if (listenfd < 0)
			continue;

		
		switch (curr->ai_family) {
		case AF_INET6:
		{
			int v6only;
			socklen_t len = sizeof(v6only);
			getsockopt(listenfd, IPPROTO_IPV6, IPV6_V6ONLY,
					&v6only, &len);
			if (v6only)
				printf("\t* ipv6 (only)\n");
			else
				printf("\t* ipv6+ipv4\n");
			break;
		}
		case AF_INET:
			printf("\t* ipv4 (only)\n");
			break;
		default:
			printf("\t* other protocol\n");
			break;
		}

#else /* usually you'd do something like this */
		/* make sure IPV6_V6ONLY is off (Linux default) */
		if (curr->ai_family == AF_INET6) {
			int off = 0;

			setsockopt(listenfd, IPPROTO_IPV6, IPV6_V6ONLY,
					&off, sizeof(off));

		}

		err = bind(listenfd, curr->ai_addr, curr->ai_addrlen);
		if (err) {
			close(listenfd);
			continue;
		}

		err = listen(listenfd, 5);
		if (err) {
			close(listenfd);
			continue;
		}

		/* listening socket successfully created! */
		break;
#endif
	}

	if (curr == NULL) {
		listenfd = -2;
	}

	freeaddrinfo(res);

	return 0;
}

Reply to: