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

Re: broken IPv6 code (attached exampled app)



>> I think there are two solutions:
>> 
>> 1. getaddrinfo should not return IPv4 addresses if an IPv6 address exist,
>> since using the IPv6 address implies usage of the IPv4 address.
>> 
>> 2. IPv4 and IPv6 protocols kept completely separate.
>> 
>> Either one or the other, it doesn't really matter, either way it is
>> possible to create portable applications that aren't broken. The
>> choice could vary depending on operating system too, I think.
>Completely right here. I think for most applications it doesn't matter which
>solution the OS takes. Maybe (2) would be the better solution because it allows
>to listen *only* on IPv6 without having to listen on every IPv6 interface
>exlicitely.
>If Linux/glibc is wrong here, who should be contacted ? [Read: Who is easier to
>convince that something should be changed - the kernel IPv6 maintainer(s) or the
>glibc maintainers ?] Or is it a glibc problem at all (could (2) be done in glibc
>without changes to the kernel part) ?

	again, there's no concrete standard to chase, so you will have hard
	time convincing people.  I believe KAME's behavior is the safest, but
	I'm of course biased.
	details below.  please comment if i made any mistakes.

itojun


let us assume that we are talking about RFC2553 (not 2553bis), and
we are okay with multiple listening sockets.
(*) there are people NOT okay with multiple listening sockets!
they insist on having single AF_INET6 listening socket.

RFC2553 defines nothing about the following items, and adds confusion
to our story.
- ordering of getaddrinfo results.  AF_INET6 first, of AF_INET first?
- how many results will getaddrinfo return with
  getaddrinfo(AI_PASSIVE, host = NULL) ?
- bind(2) ordering constraints.
- after bind(2) is done, how packets will be routed to multiple sockets
  on the same listening port.


first, let us think about bind(2) ordering options.
normal linux falls into 1 + a.  netbsd/openbsd falls into 1 + d.

option 1: getaddrinfo returns ::, then 0.0.0.0
option 2: getaddrinfo returns 0.0.0.0, then ::
option 3: getaddrinfo returns :: only
option 4: getaddrinfo returns 0.0.0.0 only
	(RFC2553 does not say we must return both!)

option a: kernel forbids bind(0.0.0.0) after bind(::), but
	bind(::) after bind(0.0.0.0) is okay
option b: kernel forbids bind(::) after bind(0.0.0.0)
	bind(0.0.0.0) after bind(::) is okay
option c: only one of them is allowed
option d: kernel works okay with any order

1 + a -> only bind(::) will success
1 + b -> both okay
1 + c -> only bind(::) will success
1 + d -> both okay
2 + a -> both okay
2 + b -> only bind(0.0.0.0) will success
2 + c -> only bind(0.0.0.0) will success
2 + d -> both okay
3 + * -> only bind(::) will success
4 + * -> only bind(0.0.0.0) will success


next, let us think about how the packets will be routed.
even if bind(2) is okay, RFC2553 3.7 adds more fun.
normal linux has only ::, and picks option y.
netbsd/openbsd has both 0.0.0.0 and ::, and picks option alpha.

if you have 0.0.0.0 only:
	no question, IPv4 traffic only
if you have :: only:
	option x: IPv6 traffic comes up.  no IPv4 traffic received.
	option y: both IPv6/v6 traffic comes up.  IPv4 traffic will
		be presented as being from IPv4 mapped address
		(RFC2553 3.7)
if you have both 0.0.0.0 and ::, then:
	option alpha: IPv4 trafific to AF_INET socket, IPv6 traffic
		to AF_INET6 socket
	option beta: both IPv4 and IPv6 traffic to AF_INET6 socket
		(like option y)
	option gamma: other random broken behaviors.



for kernel designers:

i believe the safest option to take is 1 + d + alpha.
when there's AF_INET6 listening socket only, either x or y is fine.
i believe x is better (violates RFC2553 3.7, but should be more safer,
see draft-itojun-ipv6-transition-abuse-01.txt for my reasoning about it).


for userland programmer:

what you may want to do is to ignore bind(2) failures, and die only
when you have no listening sockets.  this is the best thing you can
do when you have no assumption on the kernel.  you may not be able to
receive all inbound traffic to the port, but if there's no standard,
what we can do?

i = 0;
for (res = res0; res; res = res->ai_next) {
	s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
	if (s < 0)
		continue;
	if (bind(s, res->ai_addr, res->ai_addrlen) < 0)
		close(s);
		continue;

	sockpool[i++] = s;
}
if (i == 0) {
	printf("no listening socket\n");
	exit(1);
}



Reply to: