Subject: operationally (if not yet WG) related

From: Bill Manning <[email-masked]>
Date: Wed, 19 Sep 2001 01:16:55 -0700 (PDT)

The operational recommendation is at the end.
------------------------------------------------------------

In most tested DNS servers, it is possible to instanciate records with
opcode 28, the AAAA type.  Unfortunately, nearly all DNS servers will
allow either the native form, e.g.

        foo.example.com in aaaa  f5:0:1:dead:beef

or in mapped IPv4 form, e.g.

        foo.example.com in aaaa ::ffff:192.0.2.1

and either form seems to work, However, with the latest versions of
BIND <all versions of 9, through 9.2.0rc3>, on a number of different
Operating Systems, generate inconsistant results, with the following log
message being typical:

Sep  7 09:49:41 : socket.c:1085: unexpected error:
Sep  7 09:49:41 : internal_send: ::ffff192.0.2.1:#53: Invalid argument

Hum...  Whats happening?

Nothing in section 5 of RFC 2292 says that mapped addresses
are disallowed in in6_pktinfo.  The only requirement is that
they are unicast addresses.

So, does everyone do in6_pktinfo the same? Are the rest of the calls
there?  Do stack developers follow RFC 2553, esp. section 3.7?

This from a stack developer:
"The ways of handling of the IPv4-mapped IPv6 address varies the
operating systems.
When writing a dual stack application, on some OSes you need single
socket opened for IPv6 and an IPv4 packet comes with an IPv4-mapped
IPv6 address. Linux would be one of this category.
On the other OSes, you need to open two sockets; one for IPv4 and the
other for IPv6. BSD/OS and NetBSD fall under this category.
Both behaviors are considered to be valid.
This inconsistency would introduce portability problem, however."

With the following reply from a BIND developer:
"So when did RFC 2553 Section 3.7 become optional to implement?
Its always been the application writers choice whether to use
mapped addresses or not.  Now we have stacks forcing you to
use Section 3.7, stacks forcing you not to use Section 3.7 and
stacks that allow you to use Section 3.7 if you wish to.

This is bloody crazy."

        So... from an operational perspective, I would like to
        float what might actually be a legitimate Best Current
        Practice Recommendation; to wit:

"We recommend DNS administrators DO NOT use mapped IPv6 addresses with
opcode 28 <type AAAA> resource records in ANY zone file."

Again, YMMV.

-- 
--bill

---

From: [email-masked]
Date: Wed, 19 Sep 2001 17:46:25 +0900

>	I agree 120%.

	more words:
	from what RFC2553 3.7 says, IPv4 mapped addresses are not supposed to
	appear on the wire.  they are OS-internal representation of IPv4 peers
	on top of AF_INET6 socket.  they are not supposed to appear in the wild
	like AAAA records.

itojun

---

From: [email-masked]
Date: Wed, 19 Sep 2001 17:43:47 +0900

>there?  Do stack developers follow RFC 2553, esp. section 3.7?

	no, everyone do in6_pktinfo differently, because the specification
	itself is vague, and there are people convinced that RFC2553 section
	3.7 leads us to security vulnerability.

	there's no documentation available as to how far we need to support
	RFC2553 section 3.7.  for example:
	- if we use IPv4 mapped address in IPv6 ioctls/setsockopt/ancillary
	  data, does that mean we need to translate them into IPv4 ioctls?
	also, there are a lot of missing pieces in RFC2553 section 3.7.
	everyone can have different idea out of this ambiguous specification:
	- bind(2) ordering/conflict table.  when we have an AF_INET6 socket,
	  bound to speicfic IPv4 mapped address, should we forbid bind(2) to
	  an IPv4 address with the same port, on AF_INET socket?
	regarding to security issue, see:
	http://www.kame.net/dev/cvsweb.cgi/kame/kame/kame/v6test/conf/transition-abuse.conf
	draft-itojun-ipv6-transition-abuse-xx.txt

	yes, the implementation differences are painful for application
	developers, however, implementers failed to reach the consensus
	after a long long holy war.  the safest way for application
	implementers is to avoid using IPv4 mapped address at all and use
	AF_INET6/AF_INET sockets separately.

>This inconsistency would introduce portability problem, however."

	this part is talking about behavior of an listening socket.
	BIND9 behavior against IPv4-mapped-address-in-AAAA has things to do
	with outgoing (sending) socket.  i believe the above sentence is
	not really related to the issue.

	I agree 120%.

itojun

---

From: Erik Nordmark <[email-masked]>
Date: Wed, 19 Sep 2001 14:02:10 +0200 (CEST)

> >>opcode 28 <type AAAA> resource records in ANY zone file."

I also agree with Bill's suggestion for DNS operation.

> 	on top of AF_INET6 socket.  they are not supposed to appear in the wild

I think it would make sense to figure out whether it makes sense to
extend the restriction on the IPv4-mapped address format to other places
where it would appear in essentially textual form.

While IPv4-mapped addresses are quite useful in the API I have not seen
any need to have them appear in textual format e.g. in configuration files.
Thus instead of having configuration files (or other textual literal addresses)
use ::ffff:a.b.c.d they should just use a.b.c.d.

It makes sense discussing the general (non-DNS specific) issue in ipng.
For example, I can envision getting implementors modify 
getnameinfo and inet_ntop to always present an IPv4-mapped address
as the IPv4 address i.e. without the leading "::ffff:" text.
That would help prevent these addresses appearing in undesired places
in the future.
(Of course, one could also entertain having getaddrinfo/inet_pton refuse
to *parse* such addresses but that might be more difficult to introduce
immediately.)

  Erik

---

From: Erik Nordmark <[email-masked]>
Date: Wed, 19 Sep 2001 16:15:41 +0200 (CEST)

[I think we should move this to the ipng list.]

> 	Hiding the fact that it is a mapped address will just make
> 	it harder for developers to see that they are dealing with
> 	mapped address.  People generally print raw addresses for
> 	debugging / security reasons.  In both cases knowing that
> 	you are using mapped addresses is useful knowledge.

I understand that for debugging folks need to see the bits.
But I'm concerned about the textual mapped addresses appearing in
other unwanted places because the average ported application which
uses getnameinfo or inet_ntop will print mapped addresses with a leading 
"::ffff:".

So if we don't want the mapped literal addresses appear in application output
but we do want them to appear in debugging output the question is which
code we want to burden to do extra work.

Thus we have a choice between making the normal application printing
code look like
	if (IN6_IS_ADDR_V4MAPPED(...)) {
		struct in_addr ina;

		ina = sin6->...
		printf("Trying %s ...\n", inet_ntop(AF_INET, &ina, ...));
	} else {
		printf("Trying %s ...\n",
		 inet_ntop(AF_INET6,&sin6->sin6_addr...));
	}
or making the it just do
	printf("Trying %s ...\n", inet_ntop(AF_INET6,&sin6->sin6_addr...));

and have debugging code have to do the logical inverse as in
	if (IN6_IS_ADDR_V4MAPPED(...))
		prefix = "::ffff:";
	else
		prefix = "";
	printf("?? bad address %s%s\n", prefix, 
	    inet_ntop(AF_INET6,&sin6->sin6_addr...));

Or we could get the best of both worlds by 
1) recommending getnameinfo with the numeric flags instead of inet_ntop
2) making the default getnameinfo ommit the "::ffff:" for mapped address
3) adding a flag to getnameinfo to get the "debug" style output for mapped
   addresses.

> 	What we should be doing is making mapped address a more of
> 	a first class IPv6 address.  Taking each of the currently
> 	defined options and saying if they apply to mapped addresses
> 	or not and if so which option they map to in the IPv4 world.

While I agree that this would be ideal, I'm not certain we can get all
the implementations to do this.
But currently we don't even have a list of what it means to be a first class -
some recent experience with building Java IPv6 support indicates that
it isn't sufficient to handle IPv4-mapped address everywhere but that
one also has to handle ifindicies for IPv4-only interfaces in e.g.
IPV6_MULTICAST_IF and the like.

I think it would be a good idea for somebody to run down the list
of the IPv6 basic and advanced APIs and see what it would mean to
make IPv4-mapped addresses first class. The only thing I think can be
included up front is the SOCK_RAW related parts - raw socket applications
need to be aware whether they are sending and receiving IPv4 or IPv6 header
formats thus IPv4-mapped addresses don't buy much for such applications.

  Erik

> 	Things like in6_pktinfo are very useful when applied with
> 	mapped addresses.  Being able to send replies from the same
> 	address that the packet was sent to using one socket rather
> 	than hundreds or thousands of sockets which is what you
> 	have to do in the IPv4 world today.
> 
> 	As for the threat models when mapped addresses are sent
> 	over the wire.  Publish them so that they can be taken into
> 	account when the application is designed.  Provide switches
> 	like IPV6ONLY so that you can eliminate part of the threat.
> 
> 	Mark
> --
> Mark Andrews, Internet Software Consortium
> 1 Seymour St., Dundas Valley, NSW 2117, Australia
> PHONE: +61 2 9871 4742                 INTERNET: [email-masked]

---

From: Bill Manning <[email-masked]>
Date: Wed, 19 Sep 2001 09:51:50 -0700 (PDT)

Trying to stay DNSops oriented (w/o falling to far down the API rabbithole)
and w/o taking the time to try it out, I pose the question:

	foo.example.com  in A6 0  ::ffff:192.0.2.1

and if so, can I build a chain w/ mapped addresses?

-- 
--bill

---

From: Bill Manning <[email-masked]>
Date: Wed, 19 Sep 2001 10:25:05 -0700 (PDT)

% Trying to stay DNSops oriented (w/o falling to far down the API rabbithole)
% and w/o taking the time to try it out, I pose the question:
% 
% 	foo.example.com  in A6 0  ::ffff:192.0.2.1
% 
% and if so, can I build a chain w/ mapped addresses?
% 
% --bill

The answer is...  Yes to both.  Icky fun.

-- 
--bill

---

From: Philip Hazel <[email-masked]>
Date: Thu, 20 Sep 2001 09:11:04 +0100 (BST)

>	AF_INET6/AF_INET sockets separately.

Data point for information:

You cannot at present do that on all OS, as I found when sorting this
out for Exim.

  . On Solaris, an IPv6 socket will accept IPv4 calls, and give them as mapped
    addresses. However, if an IPv4 socket is also listening, calls are
    directed to the appropriate socket.

  . On (some versions of) Linux, an IPv6 socket will accept IPv4 calls, and
    give them as mapped addresses, but an attempt also to listen on an IPv4
    socket causes an error.

  . On OpenBSD, an IPv6 socket will not accept IPv4 calls. You have to set up
    two sockets if you want to accept both kinds of call.

  . FreeBSD is like OpenBSD, but it has the IPV6_V6ONLY socket option, which
    can be turned off, to make it behave like the versions of Linux described
    above.

  . I heard a report that the USAGI IPv6 stack for Linux has implemented
    IPV6_V6ONLY.

-- 
Philip Hazel            University of Cambridge Computing Service,
[email-masked]      Cambridge, England. Phone: +44 1223 334714.

---

From: [email-masked]
Date: Thu, 20 Sep 2001 14:28:36 +0900

>	To be more precise the sendmsg() in the following fails on
>	certian platforms.

	with what kind of argv?

itojun

---

From: [email-masked]
Date: Wed, 19 Sep 2001 23:42:44 +1000

Mark

---

From: [email-masked]
Date: Thu, 20 Sep 2001 19:05:59 +0900

>You cannot at present do that on all OS, as I found when sorting this

	(assuming that you are talking about the listener side) if you ignore
	errors from bind(2) you should be able to do this.  On Solaris/OpenBSD/
	FreeBSD/whatever, you will open two sockets with the code.  On Linux,
	you will open AF_INET6 socket only (and you will grab both IPv4 and
	IPv6 traffic).

itojun

	struct addrinfo hints, *res, *res0;
	int sock[MAXSOCK], nsock;

	/* grab list of addresses by getaddrinfo */
	memset(&hints, 0, sizeof(hints));
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_family = PF_UNSPEC;
	hints.ai_flags = AI_PASSIVE;
	getaddrinfo(NULL, "smtp", &hints, &res0);
	nsock = 0;
	for (res = res0; res; res = res->ai_next) {
		sock[nsock] = socket(res->ai_family, res->ai_socktype, res->ai_aprotocol); 
		if (sock[nsock] < 0)
			continue;

		if (bind(sock[nsock], res->ai_addr, res->ai_addrlen) < 0) {
			close(sock[nsock]);
			continue;
		}

		nsock++;
		break;
	}

	if (nsock == 0)
		/* no socket ready for listening */

---

From: [email-masked]
Date: Thu, 20 Sep 2001 12:59:47 +1000

Mark

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/uio.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>

int
main(int argc, char **argv) {
	struct sockaddr_in6 sa;
	int fd;
	int res;
	int on = 1;

	fd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
	if (fd == -1) {
		perror("socket");
		exit(1);
	}

	fprintf(stderr, "socket options");
#ifdef SO_BSDCOMPAT
	fprintf(stderr, " SO_BSDCOMPAT");
	res = setsockopt(fd, SOL_SOCKET, SO_BSDCOMPAT, (void *)&on, sizeof(on));
	if (res == -1)
		perror("setsockopt(SO_BSDCOMPAT)");
#endif

#ifdef SO_TIMESTAMP
	fprintf(stderr, " SO_TIMESTAMP");
	res = setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, (void *)&on, sizeof(on));
	if (res == -1)
		perror("setsockopt(SO_TIMESTAMP)");
#endif

#ifdef IPV6_RECVPKTINFO
	fprintf(stderr, " IPV6_RECVPKTINFO");
	res = setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO,
			 (void *)&on, sizeof(on));
	if (res == -1)
		perror("setsockopt(IPV6_RECVPKTINFO)");
#else
	fprintf(stderr, " IPV6_PKTINFO");
	res = setsockopt(fd, IPPROTO_IPV6, IPV6_PKTINFO,
			 (void *)&on, sizeof(on));
	if (res == -1)
		perror("setsockopt(IPV6_PKTINFO)");
#endif
	fprintf(stderr, "\n");

	memset(&sa, 0, sizeof(sa));
	sa.sin6_addr = in6addr_any;
	sa.sin6_family = PF_INET6;
	sa.sin6_port = htons(0);
#ifdef SIN6_LEN
	sa.sin6_len = sizeof(sa);
#endif

	res = bind(fd, (struct sockaddr *)&sa, sizeof(sa));
	if (res == -1) {
		perror("bind");
		exit(1);
	}
		
	while (*++argv != NULL) {
		struct msghdr msg;
		struct iovec io;

		memset(&sa, 0, sizeof(sa));
		if (inet_pton(PF_INET6, *argv, &sa.sin6_addr) != 1)
			continue;
		sa.sin6_family = PF_INET6;
		sa.sin6_port = htons(9);	/* discard */
#ifdef SIN6_LEN
		sa.sin6_len = sizeof(sa);
#endif

		/* some data */
		io.iov_base = "1234";
		io.iov_len = 4;

	        msg.msg_name = &sa;
		msg.msg_namelen = sizeof(sa);
		msg.msg_iov = &io;
		msg.msg_iovlen = 1;
		msg.msg_control = NULL;
		msg.msg_controllen = 0;
		msg.msg_flags = 0;

		fprintf(stderr, "trying %s#9 ... ", *argv);

		errno = 0;
		res = sendmsg(fd, &msg, 0);
		if (res != 4)
			perror("sendto");
		else
			fprintf(stderr, "success\n");
		
	}
	close(fd);
	return (0);
}

---

From: Philip Hazel <[email-masked]>
Date: Thu, 20 Sep 2001 16:14:09 +0100 (BST)

>	you will open AF_INET6 socket only (and you will grab both IPv4 and

Oh, sure. I've made Exim work in all cases.[*] I was just pointing out that
you can't just "listen on two sockets" on all platforms, without taking
into account these differences.

------------
[*] It is listen() rather than bind() that fails. Exim ignores the error
iff it is EADDRINUSE and previously a successful listen() was done on an
IPv6 socket.

---

From: [email-masked]
Date: Fri, 21 Sep 2001 00:12:33 +1000

> >	To be more precise the sendmsg() in the following fails on

	::ffff:127.0.0.1

	Pick any IPv4 unicast address that you have a route for and
	use the mapped form.  It succeeds on some Linux, FreeBSD 4.3
	Solaris 8 and fails on NetBSD at least.

	Mark