0

I tried to code to give the statistics of a saved .pcap file in C++ using the npcap library.

My IPv4 count is correct and matches Wireshark's IPv4 count, but my TCP and UDP don't match with Wireshark's statistics.

Output of my code:

image

Output of the same .pcap file in Wireshark:

image

Here is my code:

void PcapSolutionFast::generateStats() {
    ipv4_header* ip4;
    ethernet_header* ethernet; /* The ethernet header */
    u_short eth_type;
 
    while (pcap_next_ex(pcap, &header, &data) >= 0) {
        //count every packets
        
        ethernet = (ethernet_header*)(data);
        eth_type = ntohs(ethernet->ether_type);

        if (eth_type == 0x0800) {
            

            ip4 = (ipv4_header*)(data + 14); // 14 is header length of ethernet
            if (ip4->proto == 6 /* tcp protocol number */) {
               tcpCount++;
            }
            else if (ip4->proto == 17) {//udp protocol number
                udpCount++; 
            }
            
            ipv4Count++; //count total ipv4 packets
        }
       
        ++packetCount; //count all the packets
    }

These are the data structure used:

#define ETHER_ADDR_LEN  6 //mac address length is 6
#define ETHER_HEADER_LEN 14 //header length of ethernet is fixed i.e 14
/* Ethernet or MAC addresses are 6 bytes */


/* Ethernet header */
struct ethernet_header {
    u_char ether_dhost[ETHER_ADDR_LEN]; /* Destination Mac address */
    u_char ether_shost[ETHER_ADDR_LEN]; /* Source Mac address */
    u_short ether_type; /* IP/ ARP/ RARP/ etc */
};

//divided each ip_address octet into 4 u_char
typedef struct ip_address {
    u_char byte1;
    u_char byte2;
    u_char byte3;
    u_char byte4;
}ip_address;


//ipv4 ip header
typedef struct ipv4_header {
    u_char  ver_ihl;        // Version (4 bits) + Internet header length (4 bits)
    u_char  tos;            // Type of service 
    u_short tlen;           // Total length 
    u_short identification; // Identification
    u_short flags_fo;       // Flags (3 bits) + Fragment offset (13 bits)
    u_char  ttl;            // Time to live
    u_char  proto;          // Protocol
    u_short crc;            // Header checksum
    ip_address  saddr;      // Source address
    ip_address  daddr;      // Destination address
    u_int   op_pad;         // Option + Padding
}ipv4_header;

/* IPv6 header */
typedef struct ipv6_header
{
    unsigned int
        version : 4,
        traffic_class : 8,
        flow_label : 20;
    uint16_t length;
    uint8_t  next_header;
    uint8_t  hop_limit;
    struct in6_addr saddr;
    struct in6_addr daddr;
} ipv6_header;

Why is it that my TCP and UDP counts are not the same as Wireshark's statistics?

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • FYI, the ethernet header is not *fixed* at 14 bytes. It is *at least* 14 bytes, but *can* be larger. Specially, if a [802.1Q](https://en.wikipedia.org/wiki/IEEE_802.1Q) tag is present, it "*adds a 32-bit field between the source MAC address and the EtherType fields of the original frame*", thus if your packets are from a VLAN, your code won't access the `ether_type` and `ipv4_header` at the correct offsets within the `data`. Also, make sure your `struct`s don't have any alignment padding inside of them to begin with, either. – Remy Lebeau Jul 13 '22 at 17:45
  • 1
    Do you have a small sample capture file you can share somewhere whereby the Wireshark counts and counts from your program differ? – Christopher Maynard Jul 13 '22 at 18:06
  • @RemyLebeau So what can I do to distinguish between VLAN and LAN.? I have already shared the struct for all. – Nikhil Pradhan Jul 14 '22 at 12:44
  • @ChristopherMaynard this mismatch doesn't happen usually with small packet capture file. And I have noticed mismatch happens only with TCP and UDP of IPv4 ether_type not with TCP and UDP of IPv6 ether_type. So as Remy has suggested its because of VLAN type which has bigger ethernet header length than LAN. So how can one distinguish its ethernet header length between VLAN and LAN – Nikhil Pradhan Jul 14 '22 at 13:36
  • @NikhilPradhan did you read the page I linked to in my earlier comment? "*Tag protocol identifier (TPID): A 16-bit field set to a value of 0x8100 in order to identify the frame as an IEEE 802.1Q-tagged frame. **This field is located at the same position as the EtherType field in untagged frames**, and is thus used to distinguish the frame from untagged frames.*" So, `if (eth_type == 0x8100)` is true, the ethernet header is VLAN tagged – Remy Lebeau Jul 14 '22 at 15:15
  • 1
    For completeness, you may want to consider handling 0x88a8 and even 0x9100 as well. – Christopher Maynard Jul 14 '22 at 16:55
  • 1
    Oh, and possibly even 0x9200. Ref: https://www.cisco.com/en/US/docs/ios/lanswitch/configuration/guide/lsw_ieee_802.1q.pdf – Christopher Maynard Jul 14 '22 at 17:06
  • @RemyLebeau ``` ethernet = (ethernet_header*)(data); eth_type = ntohs(ethernet->ether_type); if (eth_type == 0x8100) { cout << "vlan detected" << endl; vlanEthernet = (vlan_ethhdr*)(data); } ``` This never got True so does it means there was no VLAN ethernet? – Nikhil Pradhan Jul 15 '22 at 13:50
  • @ChristopherMaynard I tried with every value you mentioned nothing got true... eth_type for ipv4 and ipv6 is getting TRUE and what ever is the total count of ipv4 and ipv6 is given by my output is same as wireshark's output. That means its not about VLAN because IPv4 and IPv6 eth_type total count is correct but only the TCP count is being mismatched for IPv4. – Nikhil Pradhan Jul 15 '22 at 13:57
  • Is it possible some packets are doubly tagged, i.e. QinQ? Is it possible some packets are not Ethernet frames at all? I recommend a divide and conquer approach: use a tool like editcap to repeatedly split the capture file in half until you are left with a small enough subset of packets in one of the remaining halves to be able to tell why there's a discrepancy. – Christopher Maynard Jul 15 '22 at 14:12
  • 1
    Without seeing a capture file to be sure, my guess at what the problem really here is that you're only looking at the first IPv4 header and there could be more than one. For example, you could have packets looking something like `Ethernet/IP/ICMP/IP/TCP`. Wireshark looks at all layers. – Christopher Maynard Jul 15 '22 at 16:52
  • @ChristopherMaynard Thank you so much for your help I will look into this and will update if I am able to figure out what exactly I am missing out – Nikhil Pradhan Jul 16 '22 at 05:26

0 Answers0