Search code examples
pcaplibpcapvlan

Distinguish between VLAN-tagged and non-VLAN frames


I need to capture ethernet frames on a Linux-based system, for which there are no tools like wireshark or even tcpdump available. And since I only need some specific, very small functionality, I am writing my own tool, that uses libpcap.

What I do not understand is, how to distinguish VLAN tagged frames from those that do not have a VLAN tag? AFAIU from wikipedia, VLAN tag is inserted between EtherType field and SourceAddress. But there is no indication (no flag or smth) that the frame includes 802.1Q header.

Besides, there is no explicit frame length field in the Ethernet header, and the system just returns as many bytes, as NIC receives.

Can I assume, that since EtherType bytes are always 0x00 0x80 (I am only interested in IPv4), if I get frameBuf[12] != 0x00 && frameBuf[13] != 0x80 (two bytes past the MAC addresses), then I have no-VLAN frame and a VLAN one otherwise?

Is there a better way to distinguish them?


Solution

  • AFAIU from wikipedia, VLAN tag is inserted between EtherType field and SourceAddress. But there is no indication (no flag or smth) that the frame includes 802.1Q header.

    See the image in https://en.wikipedia.org/wiki/IEEE_802.1Q#Frame_format; it shows the format of untagged and tagged frames.

    An Ethernet frame begins with, in order:

    • a 6-octet (6-byte) destination address;
    • a 6-octet source address;

    Following that:

    • in untagged frames, there's a 2-octet type/length field;
    • in 802.1Q tagged frames, there's a 2-octet "Tag protocol identifier (TPID)" field with a value of 0x8100.

    So the first part of decoding an Ethernet frame is looking at the 2-octet field after the source address.

    To fetch the value of that field, shift the first octet (first byte) of the field left by 8 bits and then "or" in the second octet. In C and C-derived languages such as C++, for example, that would be ((frameBuf[12] << 8) | frameBuf[13]), to use the packet buffer array in your question.

    Then:

    • if it's 1500 (decimal) or less, it's a length field, not a type field;
    • if it's between 1501 and 1535, it's an invalid frame;
    • if it's 1536 (0x0600), it's a type field.

    If it's a type field, then:

    • if it's 0x0800, it's a non-VLAN-encapsulated IPv4 packet;
    • if it's 0x86dd, it's a non-VLAN-encapsulated IPv6 packet;
    • if it's 0x8100, it's a VLAN-tagged frame;
    • (other values have other meanings, or are unassigned).

    If it's a VLAN-tagged frame, the type field is the TPID, and is followed by a 2-octet "Tag control information (TCI)" field. The TCI field is then followed by a type/length field indicating the type of packet.

    I.e., the indication of a tagged field is a value of 0x8100 following the source address, with the first byte after the source address being 0x81 and the next byte after it being 0x00.

    Can I assume, that since EtherType bytes are always 0x00 0x80 (I am only interested in IPv4), if I get frameBuf[12] != 0x00 && frameBuf[13] != 0x80 (two bytes past the MAC addresses), then I have no-VLAN frame and a VLAN one otherwise?

    No.

    First, for an IPv4 packet, the type value is 0x0800, not 0x0080, so frameBuf[12] will be 0x08 and frameBuf[13] will be 0x00 for an IPv4 packet.

    Second, you may only be interested in IPv4, but there is not necessarily a guarantee that all packets on your network will be IPv4 packets - i.e., Ethertype bytes are not always 0x08 0x00, they're only 0x08 0x00 for IPv4 packets.

    And third, a VLAN-tagged frame will have frameBuf[12] equal to 0x81 and frameBuf[13] equal to 0x00, so frameBuf[12] != 0x08 && frameBuf[13] != 0x00 will be true for a VLAN-tagged frame - and will also be true for non-VLAN-tagged, non-IPv4 frames.

    You should explicitly check for 0x0800 to find non-VLAN-tagged IPv4 frames and explicitly check for 0x8100 and then check in the following Ethertype field for 0x0800 for VLAN-tagged IPv4 frames.