svn merge --ignore-ancestry svn://svn.insecure.org/nmap@26621 svn://svn.insecure.org/nmap-exp/luis/nmap-os6
This is the IPv6 OS detection branch. "nmap -6 -O" works now, though at
this point it only prints fingerprints and not OS guesses, because we
need to collect more submissions.
Heretofore we have always extracted teh destination address directly
from the packet contents. But the raw packet bytes do not contain enough
information in one case: IPv6 link-local addresses. For those we really
need the scope ID, and for that we must pass this information all the
way down.
Before this, I got "no route to host" on OS link-local addresses. I
think that it was working on Linux only on accident, by the OS picking a
default interface or something.
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
Since OS X 10.7, we must declare whether we want the IPv6 sockets API to
work like RFC 2292 or RFC 3542. As far as I know, we are compatible with
both, so just pick the more recent one, which Apple says will become the
default in the future.
For each rtattr we add to the netlink message, we were adding
RTA_LENGTH(rtattr->rta_len) to the length of the netlink message. But
rtattr->rta_len was already calculated as RTA_LENGTH of something, and
doing RTA_LENGTH twice made the length 4 bytes longer than it should be.
This caused a log in dmesg:
netlink: 4 bytes leftover after parsing attributes.
or
netlink: 8 bytes leftover after parsing attributes.
if there was an IPv6 scope ID (because that causes two rtattrs instead
of one).
The new code is consistent with the rtnetlink(3) man page, which does
rta->rta_len = sizeof(unsigned int);
req.n.nlmsg_len = NLMSG_ALIGN(req.n.nlmsg_len) + RTA_LENGTH(sizeof(unsigned int));
We do the equivalent
rta->rta_len = sizeof(unsigned int);
req.n.nlmsg_len = NLMSG_ALIGN(req.n.nlmsg_len) + rta->rta_len;
This is set nonzero when there is a scope identifier at the end of
an IPv6 address, like fe80::a8bb:ccff:fedd:eeff%eth0 or
fe80::a8bb:ccff:fedd:eeff%1 on Windows. When this happens, we look up
the interface by index and then act as if it was the interface given by
-e. (But -e always has precedence over this.)
This is set nonzero when there is a scope identifier at the end of
an IPv6 address, like fe80::a8bb:ccff:fedd:eeff%eth0. When this
happens, we look up the interface by index and then act as if it was the
interface given by -e. (But -e always has precedence over this.)
This is set nonzero when there is a scope identifier at the end of an
IPv6 address, like fe80::a8bb:ccff:fedd:eeff%eth0. When this happens, we
add an rtattr with type RTA_OIF to request a particular outgoing
interface.
In my tests, this does the right thing when the address is in fact the
assigned address of the interface; the interface becomes lo instead of
the physical interface name.
This was previously gotten by setting the source address to be the same
of the interface address of the matching route. However this can be
wrong; when making a normal socket connection the source address is
chosen differently. We create a SOCK_DGRAM socket, connect it, and read
the local address with getsockname.
These version allow returning an extension header or other
non–upper-layer protocol if it is the final header before the end of the
packet. This is used to parse the broken packets sent as part of
protocol scan.