Reputation: 6985
I have written some simple code that uses ioctl SIOCGIFCONF to query all network interfaces on a system and, using inet_ntop, return the textual representation of the address found. The odd thing is that when a link-local IPv6 address is discovered, the OSX version of the code appears to embed the scope within the address.
Here's a line from /sbin/ifconfig on OSX after autoconfiguring the interfaces (:
en1: flags=8963<UP,BROADCAST,SMART,RUNNING,PROMISC,SIMPLEX,MULTICAST> mtu 1500
ether 00:17:f2:0b:52:73
inet6 fe80::217:f2ff:fe0b:5273%en1 prefixlen 64 scopeid 0x5
and the IP address as returned by ioctl SIOCGIFCONF:
IPv6 addr: fe80:5::217:f2ff:fe0b:5273
It looks like the value for scope (5) was inserted immediately after fe80.
The same code on Linux returns the ipv6 address without any extra data.
Two questions occur to me: 1) Is it legitimate to write an ipv6 address like this? 2) Is the OSX behavior documented anywhere?
References please!
Upvotes: 3
Views: 2275
Reputation: 13947
I'm not sure about your second question, but as for your first question, yes, it is common to see IPv6 addresses which much be scoped (such as link-local addresses) written like this, but it is definitely not consistent across platforms. The reason is because a link-local address would be ambiguous without it.
My answer to this other question might be helpful.
Edit: I just realized the subtlety in this question. The BSD IPv6 stack internally stores the interface index in the 2nd 16-bit word of a link-local IPv6 address. This should never go out on the wire. It's actually a RFC violation, because link-local addresses are defined to have 0
bits in this area. (which is, incidentally, why they can get away with storing extra information here) I believe this is a bug, and the scope should really be communicated to the rest of the system in some other way. So you should probably check for it and strip it out by hand.
Edit 2: I went and dug up the place in the kernel source where they set this value:
466 static int
467 in6_ifattach_linklocal(
468 struct ifnet *ifp,
469 struct ifnet *altifp, /* secondary EUI64 source */
470 struct in6_aliasreq *ifra_passed)
471 {
...
494 ifra.ifra_addr.sin6_family = AF_INET6;
495 ifra.ifra_addr.sin6_len = sizeof(struct sockaddr_in6);
496 ifra.ifra_addr.sin6_addr.s6_addr16[0] = htons(0xfe80);
497 #if SCOPEDROUTING
498 ifra.ifra_addr.sin6_addr.s6_addr16[1] = 0
499 #else
500 ifra.ifra_addr.sin6_addr.s6_addr16[1] = htons(ifp->if_index); /* XXX */
501 #endif
Note the /* XXX */
on line 500. ;-) My guess is that this was some sort of temporary workaround/hack to get routing to work correctly without rewriting parts of the routing code. With link-local addresses you would need to make routing decisions based on the source and destination interfaces. By putting the if_index
at that place in the address, they can probably just do a longest prefix match on the 128-bit address alone rather than relying on some kind of metadata.
Upvotes: 4