Nmap Development mailing list archives
Mac OS X link-local address convention
From: Fyodor <fyodor () insecure org>
Date: Thu, 29 Sep 2011 19:35:29 -0700
Here is an interesting (well, maybe to IPv6 wonks anyway) issue which david discovered and worked around a little bit ago in the way Mac OS X appears to include interface indexes in link-local addresses in an unconventional way. I figured this detailed commit log was worth forwarding along since nmap-svn isn't archived on seclists. This at least lets me link to it from the Nmap CHANGELOG. Cheers, -F ----- Forwarded message from commit-mailer () insecure org ----- Date: Sun, 18 Sep 2011 16:49:34 -0700 From: commit-mailer () insecure org Reply-To: nmap-dev () insecure org To: nmap-svn () insecure org Subject: [nmap-svn] r26618 - nmap/libnetutil Author: david Date: Sun Sep 18 16:49:34 2011 New Revision: 26618 Log: Canonicalize interface addresses returned from libdnet. Mac OS X appears to have a new address convention that I can't find documented. The link-local address fe80:4::X:X:X:X stands for fe80::X:X:X:X%en0, if en0 is the interface with index 4. (I.e., it would be fe80::X:X:X:X%4 on Windows.) The number 4 could be different numbers. The interface addresses seem to be stored with these pseudo-zone IDs at a low level, because they appear that way when they come from libdnet. This is what "nmap --iflist" shows: lo0 (lo0) fe80:1::1/64 loopback up 16384 en0 (en0) fe80:4::xxxx:xxxx:xxxx:xxxx/64 ethernet up 1500 XX:XX:XX:XX:XX:XX The OS X network tools seem to hide this. This is what "ifconfig" and "netstat -rn -f inet6" show: lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384 inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1 en0: flags=8963<UP,BROADCAST,SMART,RUNNING,PROMISC,SIMPLEX,MULTICAST> mtu 1500 inet6 fe80::xxxx:xxxx:xxxx:xxxx%en0 prefixlen 64 scopeid 0x4 fe80::1%lo0 link#1 UHLI lo0 fe80::xxxx:xxxx:xxxx:xxxx%en0 XX:XX:XX:XX:XX:XX UHLI lo0 (Not the "scopeid 0x4".) Also, if you use one of these addresses, it magically transforms into one without the pseudo-ID (getaddrinfo does this): $ ping6 fe80:4::1234 PING6(56=40+8+8 bytes) fe80::xxxx:xxxx:xxxx:xxxx%en0 --> fe80::1234%en0 $ ping6 fe80:1::1234 PING6(56=40+8+8 bytes) fe80::1%lo0 --> fe80::1234%lo0 This translation is messing up our netmask comparisons for the purpose of routing. If you use a normal link-local address starting with fe80:0, then it doesn't compare equal with the fe80:4/64 interface address. If you try to use the 4, then getaddrinfo turns it into fe80:0 anyway, and the comparison still fails. So for this reason I added a canonicalize_address function, which calls getnameinfo on an address, then calls getaddrinfo on the returned string, to mangle an address the same way the OS would do it. One would hope this is a no-op in cases other than the one I have described. Now "nmap --iflist" shows: lo0 (lo0) fe80::1/64 loopback up 16384 en0 (en0) fe80::xxxx:xxxx:xxxx:xxxx/64 ethernet up 1500 XX:XX:XX:XX:XX:XX Modified: nmap/libnetutil/netutil.cc Modified: nmap/libnetutil/netutil.cc ============================================================================== --- nmap/libnetutil/netutil.cc (original) +++ nmap/libnetutil/netutil.cc Sun Sep 18 16:49:34 2011 @@ -1180,10 +1180,48 @@ +/* Convert an address to a string and back again. The first parsing step + eliminates magical OS-specific syntax, for example on OS X, fe80:4::X:X:X:X + becomes "fe80::X:X:X:X" (the "4" in this case is another way of writing the + zone ID, like "%en0"; i.e., in this case en0 is interface number 4. This must + be done before e.g. comparing addresses by netmask. */ +static int canonicalize_address(const struct sockaddr_storage *ss, + struct sockaddr_storage *output) { + char canonical_ip_string[NI_MAXHOST]; + struct addrinfo hints; + struct addrinfo *ai; + int rc; + + /* Convert address to string. */ + rc = getnameinfo((struct sockaddr *) ss, sizeof(*ss), + canonical_ip_string, sizeof(canonical_ip_string), NULL, 0, NI_NUMERICHOST); + if (rc != 0) { + /* Don't care. */ + *output = *ss; + return 0; + } + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = ss->ss_family; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_flags |= AI_NUMERICHOST; + + rc = getaddrinfo(canonical_ip_string, NULL, &hints, &ai); + if (rc != 0 || ai == NULL) + return -1; + assert(ai->ai_addrlen > 0 && ai->ai_addrlen <= (int) sizeof(*output)); + memcpy(output, ai->ai_addr, ai->ai_addrlen); + freeaddrinfo(ai); + + return 0; +} + static int collect_dnet_interfaces(const struct intf_entry *entry, void *arg) { struct dnet_collector_route_nfo *dcrn = (struct dnet_collector_route_nfo *) arg; bool primary_done; unsigned int num_aliases_done; + struct sockaddr_storage tmpss; + int rc; primary_done = false; num_aliases_done = 0; @@ -1198,13 +1236,21 @@ /* The first time through the loop we add the primary interface record. After that we add the aliases one at a time. */ if (!primary_done) { - if (addr_ntos(&entry->intf_addr, (struct sockaddr *) &dcrn->ifaces[dcrn->numifaces].addr) == -1) + if (addr_ntos(&entry->intf_addr, (struct sockaddr *) &tmpss) == -1) { dcrn->ifaces[dcrn->numifaces].addr.ss_family = 0; + } else { + rc = canonicalize_address(&tmpss, &dcrn->ifaces[dcrn->numifaces].addr); + assert(rc == 0); + } dcrn->ifaces[dcrn->numifaces].netmask_bits = entry->intf_addr.addr_bits; primary_done = true; } else if (num_aliases_done < entry->intf_alias_num) { - if (addr_ntos(&entry->intf_alias_addrs[num_aliases_done], (struct sockaddr *) &dcrn->ifaces[dcrn->numifaces].addr) == -1) + if (addr_ntos(&entry->intf_alias_addrs[num_aliases_done], (struct sockaddr *) &tmpss) == -1) { dcrn->ifaces[dcrn->numifaces].addr.ss_family = 0; + } else { + rc = canonicalize_address(&tmpss, &dcrn->ifaces[dcrn->numifaces].addr); + assert(rc == 0); + } dcrn->ifaces[dcrn->numifaces].netmask_bits = entry->intf_alias_addrs[num_aliases_done].addr_bits; num_aliases_done++; } _______________________________________________ Sent through the nmap-svn mailing list http://cgi.insecure.org/mailman/listinfo/nmap-svn ----- End forwarded message ----- _______________________________________________ Sent through the nmap-dev mailing list http://cgi.insecure.org/mailman/listinfo/nmap-dev Archived at http://seclists.org/nmap-dev/
Current thread:
- Mac OS X link-local address convention Fyodor (Sep 29)