From bc132b963c58575531c84f1f716fc1862fb0b8e0 Mon Sep 17 00:00:00 2001 From: david Date: Fri, 12 Mar 2010 05:23:50 +0000 Subject: [PATCH] Make read_arp_reply_pcap work with a libpcap datalink type of DLT_LINUX_SLL, as well as the DLT_EN10MB that was formerly supported. This type is used on some interfaces where Linux doesn't reliably retain the link-layer header, and when capturing on the "any" device. On such interfaces, you would get the error read_arp_reply_pcap called on interfaces that is datatype 113 rather than DLT_EN10MB (1) For more on this datalink type, see pcap-linktype(7) http://wiki.wireshark.org/SLL I tested this by hacking scan_engine.cc to use the "any" device: --- scan_engine.cc (revision 16972) +++ scan_engine.cc (working copy) @@ -4882,7 +4882,7 @@ } } - USI->pd = my_pcap_open_live(Targets[0]->deviceName(), 100, (o.spoofsource)? 1 : 0, pcap_selectable_fd_valid()? 200 : 2); + USI->pd = my_pcap_open_live("any" /*Targets[0]->deviceName()*/, 100, (o.spoofsource)? 1 : 0, pcap_selectable_fd_valid()? 200 : 2); if (USI->ping_scan_arp){ /* Some OSs including Windows 7 and Solaris 10 have been seen to send their ARP replies to the broadcast address, not to the (unicast) address that I had to use a newer version of libpcap from git. The 1.0.0 that we ship has a bug that keeps "any" from working. You would get SIOCGIFHWADDR: No such device. --- CHANGELOG | 9 +++++++++ tcpip.cc | 40 +++++++++++++++++++++++++++++++--------- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 51805db12..27c4c81b7 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,6 +2,15 @@ [NOT YET RELEASED] +o ARP requests now work with libpcap Linux "cooked" encapsulation. + According to http://wiki.wireshark.org/SLL, this encapsulation is + used on devices "where the native link layer header isn't available + or can't be used." Before this, attempting any ARP operation on such + an interface would fail with the error + read_arp_reply_pcap called on interfaces that is datatype 113 + rather than DLT_EN10MB (1) + [David] + o Nmap now honors routing table entries that override interface addresses and netmasks. For example, with this configuration: diff --git a/tcpip.cc b/tcpip.cc index d2f3487a0..4dcb0f8dd 100644 --- a/tcpip.cc +++ b/tcpip.cc @@ -95,6 +95,7 @@ #endif #include "portreasons.h" #include +#include #include "tcpip.h" #include "NmapOps.h" #include "Target.h" @@ -2522,6 +2523,20 @@ static bool NmapArpCache(int command, struct sockaddr_storage *ss, u8 *mac) { return true; } +/* Returns true if the captured frame is ARP. This function understands the + datalink types DLT_EN10MB and DLT_LINUX_SLL. */ +static bool frame_is_arp(const u8 *frame, int datalink) { + if (datalink == DLT_EN10MB) { + return ntohs(*((u16 *) (frame + 12))) == ETH_TYPE_ARP; + } else if (datalink == DLT_LINUX_SLL) { + return ntohs(*((u16 *) (frame + 2))) == ARPHRD_ETHER && /* sll_hatype */ + ntohs(*((u16 *) (frame + 4))) == 6 && /* sll_halen */ + ntohs(*((u16 *) (frame + 14))) == ETH_TYPE_ARP; /* sll_protocol */ + } else { + return false; + } +} + /* Attempts to read one IPv4/Ethernet ARP reply packet from the pcap descriptor pd. If it receives one, fills in sendermac (must pass in 6 bytes), senderIP, and rcvdtime (can be NULL if you don't care) @@ -2538,6 +2553,7 @@ int read_arp_reply_pcap(pcap_t *pd, u8 *sendermac, u8 *p; int timedout = 0; int badcounter = 0; + unsigned int offset; struct timeval tv_start, tv_end; if (!pd) @@ -2555,8 +2571,14 @@ int read_arp_reply_pcap(pcap_t *pd, u8 *sendermac, if ((datalink = pcap_datalink(pd)) < 0) fatal("Cannot obtain datalink information: %s", pcap_geterr(pd)); - if (datalink != DLT_EN10MB) - fatal("%s called on interfaces that is datatype %d rather than DLT_EN10MB (%d)", __func__, datalink, DLT_EN10MB); + if (datalink == DLT_EN10MB) { + offset = ETH_HDR_LEN; + } else if (datalink == DLT_LINUX_SLL) { + /* The datalink type is Linux "cooked" sockets. See pcap-linktype(7). */ + offset = 16; + } else { + fatal("%s called on interface that is datatype %d rather than DLT_EN10MB (%d) or DLT_LINUX_SLL (%d)", __func__, datalink, DLT_EN10MB, DLT_LINUX_SLL); + } if (to_usec > 0) { gettimeofday(&tv_start, NULL); @@ -2582,14 +2604,14 @@ int read_arp_reply_pcap(pcap_t *pd, u8 *sendermac, else p = (u8 *) pcap_next(pd, &head); - if (p && head.caplen >= 42) { /* >= because Ethernet padding makes 60 */ - /* frame type 0x0806 (arp), hw type eth (0x0001), prot ip (0x0800), + if (p && head.caplen >= offset + 28) { + /* hw type eth (0x0001), prot ip (0x0800), hw size (0x06), prot size (0x04) */ - if (memcmp(p + 12, "\x08\x06\x00\x01\x08\x00\x06\x04\x00\x02", 10) == - 0) { - memcpy(sendermac, p + 22, 6); + if (frame_is_arp(p, datalink) && + memcmp(p + offset, "\x00\x01\x08\x00\x06\x04\x00\x02", 8) == 0) { + memcpy(sendermac, p + offset + 8, 6); /* I think alignment should allow this ... */ - memcpy(&senderIP->s_addr, p + 28, 4); + memcpy(&senderIP->s_addr, p + offset + 14, 4); break; } } @@ -2631,7 +2653,7 @@ int read_arp_reply_pcap(pcap_t *pd, u8 *sendermac, assert(head.ts.tv_sec); #endif } - PacketTrace::traceArp(PacketTrace::RCVD, (u8 *) p + ETH_HDR_LEN, ARP_HDR_LEN + ARP_ETHIP_LEN, rcvdtime); + PacketTrace::traceArp(PacketTrace::RCVD, (u8 *) p + offset, ARP_HDR_LEN + ARP_ETHIP_LEN, rcvdtime); return 1; }