Resolved: the BPF filter dit not work with vlan packets


I captured some packets with pcapplusplus on our Ubuntu server, and wrote to .pcap files, then I read the .pcap files, it just worked fine; but when I set the filter with BPF Syntax,it could not read from the .pcap files, the filter is just a tcp string, and it worked well with the example input.pcap, but not work with my pcap files,
pcpp::IFileReaderDevice* reader = pcpp::IFileReaderDevice::getReader("input.pcap");

// verify that a reader interface was indeed created
if (reader == NULL)
    printf("Cannot determine reader for file type\n");

// open the reader for reading
if (!reader->open())
    printf("Cannot open input.pcap for reading\n");

// create a pcap file writer. Specify file name and link type of all packets that
// will be written to it
pcpp::PcapFileWriterDevice pcapWriter("output.pcap", pcpp::LINKTYPE_ETHERNET);

// try to open the file for writing
if (!
    printf("Cannot open output.pcap for writing\n");

// create a pcap-ng file writer. Specify file name. Link type is not necessary because
// pcap-ng files can store multiple link types in the same file
pcpp::PcapNgFileWriterDevice pcapNgWriter("output.pcapng");

// try to open the file for writing
if (!
    printf("Cannot open output.pcapng for writing\n");

// set a BPF filter for the reader - only packets that match the filter will be read
if (!reader->setFilter("tcp"))
    printf("Cannot set filter for file reader\n");

// the packet container
pcpp::RawPacket rawPacket;

// a while loop that will continue as long as there are packets in the input file
// matching the BPF filter
while (reader->getNextPacket(rawPacket))
    // write each packet to both writers
    printf("matched ...\n");
Here are some packets:[enter image description here][1]
[1]: , Anyone can help ?


TL;DR. You need to use the filter vlan and tcp to catch TCP packets with a VLAN tag.
We can look at what BPF filter is generated when using only tcp:
$ tcpdump -d -i eth0 tcp
(000) ldh      [12]
(001) jeq      #0x86dd          jt 2    jf 7
(002) ldb      [20]
(003) jeq      #0x6             jt 10   jf 4
(004) jeq      #0x2c            jt 5    jf 11
(005) ldb      [54]
(006) jeq      #0x6             jt 10   jf 11
(007) jeq      #0x800           jt 8    jf 11
(008) ldb      [23]
(009) jeq      #0x6             jt 10   jf 11
(010) ret      #262144
(011) ret      #0
We can see 2 bytes are first loaded from offset 12 in the packet. That corresponds to the Ethertype in the Ethernet header. It is then used to check if we are parsing an IPv6 (jeq #0x86dd) or IPv4 (jeq #0x800) packet.
However, when there is a VLAN tag, the Ethertype field is shifted by 4 bytes (the length of the VLAN tag field). For packets with a VLAN tag, we should therefore be reading the Ethertype at offset 16.
Using filter vlan and tcp implements this change, by first checking that there is a VLAN tag and then taking it into account when reading the Ethertype. Therefore, to filter TCP packets regardless of whether they have a VLAN tag, you’ll need tcp or (vlan and tcp).

If you have better answer, please add a comment about this, thank you!