Linux – IPv6 link-local address with ’embedded’ range on OSX

IPv6 link-local address with ’embedded’ range on OSX… here is a solution to the problem.

IPv6 link-local address with ’embedded’ range on OSX

I wrote some simple code that uses ioctl SIOCGIFCONF to query all the network interfaces on the system and uses inet_ntop to return a text representation of the found address. Oddly enough, when a link-local IPv6 address is discovered, the OSX version of the code appears to embed the range into the address.

After the interface is automatically configured, this is a line (:) in /sbin/ifconfig on OSX

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 returned by ioctl SIOCGIFCONF:

IPv6 address: fe80:5::217:f2ff:fe0b:5273

It looks like the value of range (5) is inserted immediately after fe80.

The same code on Linux returns an ipv6 address without any extra data.

Two questions come to mind:
1) Is it legal to write the IPv6 address like this?
2) Is OSX behavior logged anywhere?

Please quote!

Solution

I’m not sure about your second question, but for your first question, yes, it’s common to see scopes for IPv6 addresses (e.g. link-local addresses) written like this, but definitely not cross-platform consistency. The reason is because without it, the link-local address will be ambiguous.

My answer to this other question might be helpful.


EDIT: I just realized the subtleties of this issue. The BSD IPv6 stack internally stores the interface index in the second 16-bit word of the link-local IPv6 address. This should disappear on the wire forever. This is actually a violation of the RFC, because link-local addresses are defined to have 0 bits in this area。 (by the way, that’s why they can store extra information here) I believe this is a bug and the scope should otherwise communicate with the rest of the system. So, you should probably check it and peel it off by hand.


Edit 2: I dug 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 /* XXX */ on line 500. 😉 My guess is that this is some kind of temporary workaround/trick to get routing to work without rewriting part of the routing code. With link-local addresses, you need to make routing decisions based on the source and destination interfaces. By placing the if_index in that place in the address, they might only do the longest prefix match for a 128-bit address instead of relying on some kind of metadata.

Related Problems and Solutions