Stripping layer 2 in pcap
Say you want to sniff TCP/IP packets on your network. That's pretty
easy, right? Use libpcap
, receive packets from the network interface
and we're done. But before you can extract the IP header from a
received packet you need to strip
layer 2 header.
It's not that easy. Libpcap
tries to help only a bit - it is
possible to get the data link type of the network interface. But this
is useless without the knowledge of how to extract IP from given data
link type.
Most software use hardcoded offsets for the most common data link types. Here's the relevant code in few popular programs:
Hardcoded offsets:
Data Link | p0f v3 | nmap | hping | libnids |
---|---|---|---|---|
DLT_RAW | 0 | 0 | 0 | 0 |
DLT_NULL | 4 | 4 | 4 | 4 |
DLT_LOOP | 8 | 4 | 4 | - |
DLT_PPP | 4 | 4† or 8‡ else 24 | 4 | 4 |
DLT_PPP_BSDOS | - | 4† or 8‡ else 24 | 24 | - |
DLT_PPP_SERIAL | 8 | 4† or 8‡ else 24 | 4 | 4 |
DLT_PPP_ETHER | 8 | 4† or 8‡ else 24 | - | - |
DLT_EN10MB | 14 | 14 | 14 | 14 or 18 if VLAN |
DLT_LINUX_SLL | 16 | 16 | 16 | 16 |
DLT_PFLOG | 28 | - | - | - |
DLT_IEEE802_11 | 32 | - | 14 | 24-32 |
DLT_IEEE802 | - | 22 | 14 | 22 |
DLT_MIAMI | - | 16 | - | - |
DLT_SLIP | - | 16† else 24 | 16 | 0 |
DLT_SLIP_BSDOS | - | 16† else 24 | 16 | - |
DLT_FDDI | - | 21 | 13 | 21 |
DLT_ENC | - | 12 | - | - |
DLT_IPNET | - | 24 | - | - |
DLT_ATM_RFC1483 | - | - | 8 | - |
DLT_CIP | - | - | 8 | - |
DLT_ATM_CLIP | - | - | 8 | - |
DLT_C_HDLC | - | - | 4 | - |
DLT_LANE8023 | - | - | 16 | - |
-
†:
#if (FREEBSD||OPENBSD||NETBSD||BSDI||MACOSX)
-
‡:
#ifdef SOLARIS
Those programs are in agreement only about:
Data Link | offset |
---|---|
DLT_RAW | 0 |
DLT_NULL | 4 |
DLT_EN10MB | 14 |
DLT_LINUX_SLL | 16 |
Additionally it may be important to remove 802.1Q VLAN header from
DLT_EN10MB, for example libnids
has the following code:
case DLT_EN10MB: if (hdr->caplen < 14) return; /* Only handle IP and 802.1Q VLAN tagged packets */ if (data[12] == 8 && data[13] == 0) { /* Regular ethernet */ nids_linkoffset = 14; } else if (data[12] == 0x81 && data[13] == 0) { /* Skip 802.1Q VLAN and priority information */ nids_linkoffset = 18; } else /* non-ip frame */ return; break;
Furthermore, decoding of DLT_IEEE802_11 in libnids
contains pretty
complex logic.
Opposite strategy is chosen by p0f
- it tries to guess the offset of
IPv4 or IPv6 headers if the data link type is unrecognized.
Finally, here's the official tcpdump/libpcap documentation: