Npcap Development Tutorial

Abstract

A step-by-step guide to writing software that uses Npcap to list network adapters, capture packets, and send network traffic.

This section shows how to use the features of the Npcap API. It is organized as a tutorial, subdivided into a set of lessons that will introduce the reader, in a step-by-step fashion, to program development using Npcap, from the basic functions (obtaining the adapter list, starting a capture, etc.) to the most advanced ones (handling send queues and gathering statistics about network traffic).

The samples are written in plain C, so a basic knowledge of C programming is required. Also, since this is a tutorial about a library dealing with "raw" networking packets, good knowledge of networks and network protocols is assumed.

The code in this section is copied from the the section called “Examples” in the source distribution and the SDK. The code is released under a BSD-3-clause license and copyright: NetGroup, Politecnico di Torino (Italy); CACE Technologies, Davis (California); and Insecure.com, LLC. Full text of the code license can be found in each source file.

Obtaining the device list

Typically, the first thing that a Npcap-based application does is get a list of attached network adapters. Both libpcap and Npcap provide the pcap_findalldevs_ex() function for this purpose: this function returns a linked list of pcap_if structures, each of which contains comprehensive information about an attached adapter. In particular, the fields name and description contain the name and a human readable description, respectively, of the corresponding device.

The following code retrieves the adapter list and shows it on the screen, printing an error if no adapters are found.

#include "pcap.h"

main()
{
  pcap_if_t *alldevs;
  pcap_if_t *d;
  int i=0;
  char errbuf[PCAP_ERRBUF_SIZE];
  
  /* Retrieve the device list from the local machine */
  if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING,
    NULL /* auth is not needed */,
    &alldevs, errbuf) == -1)
  {
    fprintf(stderr,
      "Error in pcap_findalldevs_ex: %s\n",
      errbuf);
    exit(1);
  }
  
  /* Print the list */
  for(d= alldevs; d != NULL; d= d->next)
  {
    printf("%d. %s", ++i, d->name);
    if (d->description)
      printf(" (%s)\n", d->description);
    else
      printf(" (No description available)\n");
  }
  
  if (i == 0)
  {
    printf("\nNo interfaces found! Make sure Npcap is installed.\n");
    return;
  }

  /* We don't need any more the device list. Free it */
  pcap_freealldevs(alldevs);
}

Some comments about this code.

First of all, pcap_findalldevs_ex(), like other libpcap functions, has an errbuf parameter. This parameter points to a string filled by libpcap with a description of the error if something goes wrong.

Second, remember that not all the OSes supported by libpcap provide a description of the network interfaces, therefore if we want to write a portable application, we must consider the case in which description is null: we print the string "No description available" in that situation.

Note finally that we free the list with pcap_freealldevs() once when we have finished with it.

Assuming we have compiled the program, let's try to run it. On a particular Windows workstation, the result we optained is

1. \Device\NPF_{4E273621-5161-46C8-895A-48D0E52A0B83} (Realtek RTL8029(AS) Ethernet Adapter)
2. \Device\NPF_{5D24AE04-C486-4A96-83FB-8B5EC6C7F430} (3Com EtherLink PCI)

As you can see, the name of the network adapters (that will be passed to libpcap when opening the devices) under Windows are quite unreadable, so the parenthetical descriptions can be very helpful.

Obtaining advanced information about installed devices

Lesson 1 (the section called “Obtaining the device list”) demonstrated how to get basic information (i.e. device name and description) about available adapters. Actually, Npcap provides also other advanced information. In particular, every pcap_if structure returned by pcap_findalldevs_ex() contains also a list of pcap_addr structures, with:

  • a list of addresses for that interface.
  • a list of netmasks (each of which corresponds to an entry in the addresses list).
  • a list of broadcast addresses (each of which corresponds to an entry in the addresses list).
  • a list of destination addresses (each of which corresponds to an entry in the addresses list).

Additionally, pcap_findalldevs_ex() can also return remote adapters and a list of pcap files that are located in a given local folder.

The following sample provides an ifprint() function that prints the complete contents of a pcap_if structure. It is invoked by the program for every entry returned by pcap_findalldevs_ex().

/* Print all the available information on the given interface */
void ifprint(pcap_if_t *d)
{
  pcap_addr_t *a;
  char ip6str[128];

  /* Name */
  printf("%s\n",d->name);

  /* Description */
  if (d->description)
    printf("\tDescription: %s\n",d->description);

  /* Loopback Address*/
  printf("\tLoopback: %s\n",(d->flags & PCAP_IF_LOOPBACK)?"yes":"no");

  /* IP addresses */
  for(a=d->addresses;a;a=a->next) {
    printf("\tAddress Family: #%d\n",a->addr->sa_family);
  
    switch(a->addr->sa_family)
    {
      case AF_INET:
        printf("\tAddress Family Name: AF_INET\n");
        if (a->addr)
          printf("\tAddress: %s\n",iptos(((struct sockaddr_in *)a->addr)->sin_addr.s_addr));
        if (a->netmask)
          printf("\tNetmask: %s\n",iptos(((struct sockaddr_in *)a->netmask)->sin_addr.s_addr));
        if (a->broadaddr)
          printf("\tBroadcast Address: %s\n",iptos(((struct sockaddr_in *)a->broadaddr)->sin_addr.s_addr));
        if (a->dstaddr)
          printf("\tDestination Address: %s\n",iptos(((struct sockaddr_in *)a->dstaddr)->sin_addr.s_addr));
        break;

      case AF_INET6:
        printf("\tAddress Family Name: AF_INET6\n");
        if (a->addr)
          printf("\tAddress: %s\n", ip6tos(a->addr, ip6str, sizeof(ip6str)));
       break;

      default:
        printf("\tAddress Family Name: Unknown\n");
        break;
    }
  }
  printf("\n");
}

Opening an adapter and capturing the packets

Now that we've seen how to obtain an adapter to play with, let's start the real job, opening an adapter and capturing some traffic. In this lesson we'll write a program that prints some information about each packet flowing through the adapter.

The function that opens a capture device is pcap_open(). The parameters, snaplen, flags and to_ms deserve some explanation.

snaplen specifies the portion of the packet to capture. On some OSes (like xBSD and Win32), the packet driver can be configured to capture only the initial part of any packet: this decreases the amount of data to copy to the application and therefore improves the efficiency of the capture. In this case we use the value 65536 which is higher than the greatest MTU that we could encounter. In this manner we ensure that the application will always receive the whole packet.

flags: the most important flag is the one that indicates if the adapter will be put in promiscuous mode. In normal operation, an adapter only captures packets from the network that are destined to it; the packets exchanged by other hosts are therefore ignored. Instead, when the adapter is in promiscuous mode it captures all packets whether they are destined to it or not. This means that on shared media (like non-switched Ethernet), Npcap will be able to capture the packets of other hosts. Promiscuous mode is the default for most capture applications, so we enable it in the following example.

to_ms specifies the read timeout, in milliseconds. A read on the adapter (for example, with pcap_dispatch() or pcap_next_ex()) will always return after to_ms milliseconds, even if no packets are available from the network. to_ms also defines the interval between statistical reports if the adapter is in statistical mode (see the lesson "\ref wpcap_tut9" for information about statistical mode). Setting to_ms to 0 means no timeout, a read on the adapter never returns if no packets arrive. A -1 timeout on the other side causes a read on the adapter to always return immediately.

#include <pcap.h>
#include "misc.h" /* LoadNpcapDlls */

/* prototype of the packet handler */
void packet_handler(
  u_char *param,
  const struct pcap_pkthdr *header,
  const u_char *pkt_data);

int main()
{
  pcap_if_t *alldevs;
  pcap_if_t *d;
  int inum;
  int i=0;
  pcap_t *adhandle;
  char errbuf[PCAP_ERRBUF_SIZE];
  
  /* Load Npcap and its functions. */
  if (!LoadNpcapDlls())
  {
    fprintf(stderr, "Couldn't load Npcap\n");
    exit(1);
  }

  /* Retrieve the device list on the local machine */
  if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING,
    NULL, &alldevs, errbuf) == -1)
  {
    fprintf(stderr,"Error in pcap_findalldevs: %s\n", errbuf);
    exit(1);
  }
  
  /* Print the list */
  for(d=alldevs; d; d=d->next)
  {
    printf("%d. %s", ++i, d->name);
    if (d->description)
      printf(" (%s)\n", d->description);
    else
      printf(" (No description available)\n");
  }
  
  if(i==0)
  {
    printf("\nNo interfaces found! Make sure Npcap is installed.\n");
    return -1;
  }
  
  printf("Enter the interface number (1-%d):",i);
  scanf_s("%d", &inum);
  
  if(inum < 1 || inum > i)
  {
    printf("\nInterface number out of range.\n");
    /* Free the device list */
    pcap_freealldevs(alldevs);
    return -1;
  }
  
  /* Jump to the selected adapter */
  for(d=alldevs, i=0; i< inum-1 ;d=d->next, i++);
  
  /* Open the device */
  if ( (adhandle= pcap_open(d->name, // name of the device
          65536, // portion of the packet to capture
                 // 65536 guarantees that the whole packet will
                 // be captured on all the link layers
          PCAP_OPENFLAG_PROMISCUOUS, // promiscuous mode
          1000, // read timeout
          NULL, // authentication on the remote machine
          errbuf // error buffer
          ) ) == NULL)
  {
    fprintf(stderr,
      "\nUnable to open the adapter. %s is not supported by Npcap\n",
      d->name);
    /* Free the device list */
    pcap_freealldevs(alldevs);
    return -1;
  }
  
  printf("\nlistening on %s...\n", d->description);
  
  /* At this point, we don't need any more the device list. Free it */
  pcap_freealldevs(alldevs);
  
  /* start the capture */
  pcap_loop(adhandle, 0, packet_handler, NULL);
  
  return 0;
}


/* Callback function invoked by libpcap for every incoming packet */
void packet_handler(u_char *param,
  const struct pcap_pkthdr *header,
  const u_char *pkt_data)
{
  struct tm ltime;
  char timestr[16];
  time_t local_tv_sec;

  /*
   * unused variables
   */
  (VOID)(param);
  (VOID)(pkt_data);

  /* convert the timestamp to readable format */
  local_tv_sec = header->ts.tv_sec;
  localtime_s(&ltime, &local_tv_sec);
  strftime( timestr, sizeof timestr, "%H:%M:%S", &ltime);
  
  printf("%s,%.6d len:%d\n",
    timestr, header->ts.tv_usec, header->len);
}

Once the adapter is opened, the capture can be started with pcap_dispatch() or pcap_loop(). These two functions are very similar, the difference is that pcap_dispatch() returns (although not guaranteed) when the timeout expires while pcap_loop() doesn't return until cnt packets have been captured, so it can block for an arbitrary period on an under-utilized network. pcap_loop() is enough for the purpose of this sample, while pcap_dispatch() is normally used in a more complex program.

Both of these functions have a callback parameter, packet_handler, pointing to a function that will receive the packets. This function is invoked by libpcap for every new packet coming from the network and receives a generic status (corresponding to the user parameter of pcap_loop() and pcap_dispatch()), a header with some information on the packet like the timestamp and the length and the actual data of the packet including all the protocol headers. Note that the frame CRC is normally not present, because it is removed by the network adapter after frame validation. Note also that most adapters discard packets with wrong CRCs, therefore Npcap is normally not able to capture them.

The above example extracts the timestamp and the length of every packet from the pcap_pkthdr header and prints them on the screen.

Please note that there may be a drawback using pcap_loop() mainly related to the fact that the handler is called by the packet capture driver; therefore the user application does not have direct control over it. Another approach (and to have more readable programs) is to use the pcap_next_ex() function, which is presented in the next example (the section called “Capturing the packets without the callback”).

Capturing the packets without the callback

The example program in this lesson behaves exactly like the previous program (the section called “Opening an adapter and capturing the packets”), but it uses pcap_next_ex() instead of pcap_loop().

The callback-based capture mechanism of pcap_loop() is elegant and it could be a good choice in some situations. However, handling a callback is sometimes not practical—it often makes the program more complex especially in situations with multithreaded applications or C++ classes.

In these cases, pcap_next_ex() retrievs a packet with a direct call—using pcap_next_ex(), packets are received only when the programmer wants them.

The parameters of this function are the same as a capture callback. It takes an adapter descriptor and a couple of pointers that will be initialized and returned to the user (one to a pcap_pkthdr structure and another to a buffer with the packet data).

In the following program, we recycle the callback code of the previous lesson's example and move it inside main() right after the call to pcap_next_ex().

/* Open the device */
if ( (adhandle= pcap_open(d->name, // name of the device
      65536, // portion of the packet to capture. 
             // 65536 guarantees that the whole packet will
             // be captured on all the link layers
      PCAP_OPENFLAG_PROMISCUOUS, // promiscuous mode
      1000, // read timeout
      NULL, // authentication on the remote machine
      errbuf // error buffer
  ) ) == NULL)
{
  fprintf(stderr,
    "\nUnable to open the adapter. %s is not supported by Npcap\n",
    d->name);
  /* Free the device list */
  pcap_freealldevs(alldevs);
  return -1;
}

printf("\nlistening on %s...\n", d->description);

/* At this point, we don't need any more the device list. Free it */
pcap_freealldevs(alldevs);

/* Retrieve the packets */
while((res = pcap_next_ex( adhandle, &header, &pkt_data)) >= 0){

  if(res == 0)
    /* Timeout elapsed */
    continue;

  /* convert the timestamp to readable format */
  local_tv_sec = header->ts.tv_sec;
  localtime_s(&ltime, &local_tv_sec);
  strftime( timestr, sizeof timestr, "%H:%M:%S", &ltime);

  printf("%s,%.6d len:%d\n", timestr, header->ts.tv_usec, header->len);
}

if(res == -1){
  printf("Error reading the packets: %s\n", pcap_geterr(adhandle));
  return -1;
}

Why do we use pcap_next_ex() instead of the old pcap_next()? Because pcap_next() has some drawbacks. First of all, it is inefficient because it hides the callback method but still relies on pcap_dispatch(). Second, it is not able to detect EOF, so it's not very useful when gathering packets from a file.

Notice also that pcap_next_ex() returns different values for success, timeout elapsed, error and EOF conditions.

Filtering the traffic

One of the most powerful features offered by Npcap (and by libpcap as well) is the filtering engine. It provides a very efficient way to receive subsets of the network traffic, and is (usually) integrated with the capture mechanism provided by Npcap. The functions used to filter packets are pcap_compile() and pcap_setfilter().

pcap_compile() takes a string containing a high-level Boolean (filter) expression and produces a low-level byte code that can be interpreted by the fileter engine in the packet driver. The syntax of the boolean expression can be found in the Filtering expression syntax section of this documentation.

pcap_setfilter() associates a filter with a capture session in the kernel driver. Once pcap_setfilter() is called, the associated filter will be applied to all the packets coming from the network, and all the conformant packets (i.e., packets for which the Boolean expression evaluates to true) will be actually copied to the application.

The following code shows how to compile and set a filter. Note that we must retrieve the netmask from the pcap_if structure that describes the adapter, because some filters created by pcap_compile() require it.

The filter passed to pcap_compile() in this code snippet is "ip and tcp", which means to "keep only the packets that are both IPv4 and TCP and deliver them to the application".

if (d->addresses != NULL)
  /* Retrieve the mask of the first address of the interface */
  netmask=((struct sockaddr_in *)(d->addresses->netmask))->sin_addr.S_un.S_addr;
else
  /* If the interface is without an address
   * we suppose to be in a C class network */
  netmask=0xffffff; 


//compile the filter
if (pcap_compile(adhandle, &fcode, "ip and tcp", 1, netmask) < 0)
{
  fprintf(stderr,
    "\nUnable to compile the packet filter. Check the syntax.\n");
  /* Free the device list */
  pcap_freealldevs(alldevs);
  return -1;
}

//set the filter
if (pcap_setfilter(adhandle, &fcode) < 0)
{
  fprintf(stderr,"\nError setting the filter.\n");
  /* Free the device list */
  pcap_freealldevs(alldevs);
  return -1;
}

If you want to see some code that uses the filtering functions shown in this lesson, look at the example presented in the next Lesson, the section called “Interpreting the packets”.

Interpreting the packets

Now that we are able to capture and filter network traffic, we want to put our knowledge to work with a simple "real world" application.

In this lesson we will take code from the previous lessons and use these pieces to build a more useful program. the main purpose of the current program is to show how the protocol headers of a captured packet can be parsed and interpreted. The resulting application, called UDPdump, prints a summary of the UDP traffic on our network.

We have chosen to parse and display the UDP protocol because it is more accessible than other protocols such as TCP and consequently is an excellent initial example. Let's look at the code:

#include <pcap.h>
#include <Winsock2.h>
#include <tchar.h>
BOOL LoadNpcapDlls()
{
  _TCHAR npcap_dir[512];
  UINT len;
  len = GetSystemDirectory(npcap_dir, 480);
  if (!len) {
    fprintf(stderr, "Error in GetSystemDirectory: %x", GetLastError());
    return FALSE;
  }
  _tcscat_s(npcap_dir, 512, _T("\\Npcap"));
  if (SetDllDirectory(npcap_dir) == 0) {
    fprintf(stderr, "Error in SetDllDirectory: %x", GetLastError());
    return FALSE;
  }
  return TRUE;
}


/* 4 bytes IP address */
typedef struct ip_address{
  u_char byte1;
  u_char byte2;
  u_char byte3;
  u_char byte4;
}ip_address;

/* IPv4 header */
typedef struct ip_header{
  u_char  ver_ihl; // Version (4 bits) + IP 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
}ip_header;

/* UDP header*/
typedef struct udp_header{
  u_short sport; // Source port
  u_short dport; // Destination port
  u_short len;   // Datagram length
  u_short crc;   // Checksum
}udp_header;

/* prototype of the packet handler */
void packet_handler(u_char *param,
  const struct pcap_pkthdr *header,
  const u_char *pkt_data);


int main()
{
  pcap_if_t *alldevs;
  pcap_if_t *d;
  int inum;
  int i=0;
  pcap_t *adhandle;
  char errbuf[PCAP_ERRBUF_SIZE];
  u_int netmask;
  char packet_filter[] = "ip and udp";
  struct bpf_program fcode;

  /* Load Npcap and its functions. */
  if (!LoadNpcapDlls())
  {
    fprintf(stderr, "Couldn't load Npcap\n");
    exit(1);
  }

  /* Retrieve the device list */
  if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING,
     NULL, &alldevs, errbuf) == -1)
  {
    fprintf(stderr,"Error in pcap_findalldevs: %s\n", errbuf);
    exit(1);
  }
  
  /* Print the list */
  for(d=alldevs; d; d=d->next)
  {
    printf("%d. %s", ++i, d->name);
    if (d->description)
      printf(" (%s)\n", d->description);
    else
      printf(" (No description available)\n");
  }

  if(i==0)
  {
    printf("\nNo interfaces found! Make sure Npcap is installed.\n");
    return -1;
  }
  
  printf("Enter the interface number (1-%d):",i);
  scanf_s("%d", &inum);
  
  if(inum < 1 || inum > i)
  {
    printf("\nInterface number out of range.\n");
    /* Free the device list */
    pcap_freealldevs(alldevs);
    return -1;
  }

  /* Jump to the selected adapter */
  for(d=alldevs, i=0; i< inum-1 ;d=d->next, i++);
  
  /* Open the adapter */
  if ( (adhandle= pcap_open(d->name, // name of the device
               65536, // portion of the packet to capture. 
                      // 65536 grants that the whole packet
                      // will be captured on all the MACs.
               PCAP_OPENFLAG_PROMISCUOUS, // promiscuous mode
               1000, // read timeout
               NULL, // remote authentication
               errbuf // error buffer
               ) ) == NULL)
  {
    fprintf(stderr,
      "\nUnable to open the adapter. %s is not supported by Npcap\n",
      d->name);
    /* Free the device list */
    pcap_freealldevs(alldevs);
    return -1;
  }
  
  /* Check the link layer. We support only Ethernet for simplicity. */
  if(pcap_datalink(adhandle) != DLT_EN10MB)
  {
    fprintf(stderr,"\nThis program works only on Ethernet networks.\n");
    /* Free the device list */
    pcap_freealldevs(alldevs);
    return -1;
  }
  
  if(d->addresses != NULL)
    /* Retrieve the mask of the first address of the interface */
    netmask=((struct sockaddr_in *)(d->addresses->netmask))->sin_addr.S_un.S_addr;
  else
    /* If the interface is without addresses
     * we suppose to be in a C class network */
    netmask=0xffffff; 


  //compile the filter
  if (pcap_compile(adhandle, &fcode, packet_filter, 1, netmask) <0 )
  {
    fprintf(stderr,"\nUnable to compile the packet filter. Check the syntax.\n");
    /* Free the device list */
    pcap_freealldevs(alldevs);
    return -1;
  }
  
  //set the filter
  if (pcap_setfilter(adhandle, &fcode)<0)
  {
    fprintf(stderr,"\nError setting the filter.\n");
    /* Free the device list */
    pcap_freealldevs(alldevs);
    return -1;
  }
  
  printf("\nlistening on %s...\n", d->description);
  
  /* At this point, we don't need any more the device list. Free it */
  pcap_freealldevs(alldevs);
  
  /* start the capture */
  pcap_loop(adhandle, 0, packet_handler, NULL);
  
  return 0;
}

/* Callback function invoked by libpcap for every incoming packet */
void packet_handler(u_char *param,
  const struct pcap_pkthdr *header,
  const u_char *pkt_data)
{
  struct tm ltime;
  char timestr[16];
  ip_header *ih;
  udp_header *uh;
  u_int ip_len;
  u_short sport,dport;
  time_t local_tv_sec;

  /*
   * Unused variable
   */
  (VOID)(param);

  /* convert the timestamp to readable format */
  local_tv_sec = header->ts.tv_sec;
  localtime_s(&ltime, &local_tv_sec);
  strftime( timestr, sizeof timestr, "%H:%M:%S", &ltime);

  /* print timestamp and length of the packet */
  printf("%s.%.6d len:%d ", timestr, header->ts.tv_usec, header->len);

  /* retireve the position of the ip header */
  ih = (ip_header *) (pkt_data +
    14); //length of ethernet header

  /* retireve the position of the udp header */
  ip_len = (ih->ver_ihl & 0xf) * 4;
  uh = (udp_header *) ((u_char*)ih + ip_len);

  /* convert from network byte order to host byte order */
  sport = ntohs( uh->sport );
  dport = ntohs( uh->dport );

  /* print ip addresses and udp ports */
  printf("%d.%d.%d.%d.%d -> %d.%d.%d.%d.%d\n",
    ih->saddr.byte1,
    ih->saddr.byte2,
    ih->saddr.byte3,
    ih->saddr.byte4,
    sport,
    ih->daddr.byte1,
    ih->daddr.byte2,
    ih->daddr.byte3,
    ih->daddr.byte4,
    dport);
}

First of all, we set the filter to "ip and udp". In this way we are sure that packet_handler() will receive only UDP packets over IPv4: this simplifies the parsing and increases the efficiency of the program.

We have also created a couple of structs that describe the IP and UDP headers. These structs are used by packet_handler() to properly locate the various header fields.

packet_handler(), although limited to a single protocol dissector (UDP over IPv4), shows how complex "sniffers" like tcpdump/WinDump decode the network traffic. Since we aren't interested in the MAC header, we skip it. For simplicity and before starting the capture, we check the MAC layer with pcap_datalink() to make sure that we are dealing with an Ethernet network. This way we can be sure that the MAC header is exactly 14 bytes.

The IP header is located just after the MAC header. We will extract the IP source and destination addresses from the IP header.

Reaching the UDP header is a bit more complicated, because the IP header doesn't have a fixed length. Therefore, we use the IP header's length field to know its size. Once we know the location of the UDP header, we extract the source and destination ports.

The extracted values are printed on the screen, and the result is something like:

\Device\Packet_{A7FD048A-5D4B-478E-B3C1-34401AC3B72F} (Xircom t 10/100 Adapter)
Enter the interface number (1-2):1
 listening on Xircom CardBus Ethernet 10/100 Adapter...
16:13:15.312784 len:87 130.192.31.67.2682 -> 130.192.3.21.53
16:13:15.314796 len:137 130.192.3.21.53 -> 130.192.31.67.2682
16:13:15.322101 len:78 130.192.31.67.2683 -> 130.192.3.21.53

Each of the final 3 lines represents a different packet.

Handling offline dump files

In this lession we are going to learn how to handle packet capture to a file (dump to file). Npcap offers a wide range of functions to save the network traffic to a file and to read the content of dumps—this lesson will teach how to use all of these functions.

The format for dump files is the libpcap one. This format contains the data of the captured packets in binary form and is a standard used by many network tools including WinDump, Wireshark and Snort.

Saving packets to a dump file

First of all, let's see how to write packets in libpcap format.

The following example captures the packets from the selected interface and saves them on a file whose name is provided by the user.

#include <pcap.h>
#include "misc.h" /* LoadNpcapDlls */

/* prototype of the packet handler */
void packet_handler(u_char *param,
  const struct pcap_pkthdr *header,
  const u_char *pkt_data);

int main(int argc, char **argv)
{
  pcap_if_t *alldevs;
  pcap_if_t *d;
  int inum;
  int i=0;
  pcap_t *adhandle;
  char errbuf[PCAP_ERRBUF_SIZE];
  pcap_dumper_t *dumpfile;

  /* Load Npcap and its functions. */
  if (!LoadNpcapDlls())
  {
    fprintf(stderr, "Couldn't load Npcap\n");
    exit(1);
  }

  /* Check command line */
  if(argc != 2)
  {
    printf("usage: %s filename", argv[0]);
    return -1;
  }
    
  /* Retrieve the device list on the local machine */
  if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING,
    NULL, &alldevs, errbuf) == -1)
  {
    fprintf(stderr,"Error in pcap_findalldevs: %s\n", errbuf);
    exit(1);
  }
    
  /* Print the list */
  for(d=alldevs; d; d=d->next)
  {
    printf("%d. %s", ++i, d->name);
    if (d->description)
      printf(" (%s)\n", d->description);
    else
      printf(" (No description available)\n");
  }

  if(i==0)
  {
    printf("\nNo interfaces found! Make sure Npcap is installed.\n");
    return -1;
  }
  
  printf("Enter the interface number (1-%d):",i);
  scanf_s("%d", &inum);
  
  if(inum < 1 || inum > i)
  {
    printf("\nInterface number out of range.\n");
    /* Free the device list */
    pcap_freealldevs(alldevs);
    return -1;
  }
  
  /* Jump to the selected adapter */
  for(d=alldevs, i=0; i< inum-1 ;d=d->next, i++);
    
    
  /* Open the device */
  if ( (adhandle= pcap_open(d->name, // name of the device
                65536, // portion of the packet to capture
                       // 65536 guarantees that the whole packet
                       // will be captured on all the link layers
                PCAP_OPENFLAG_PROMISCUOUS, // promiscuous mode
                1000, // read timeout
                NULL, // authentication on the remote machine
                errbuf // error buffer
                ) ) == NULL)
  {
    fprintf(stderr,
      "\nUnable to open the adapter. %s is not supported by Npcap\n",
      d->name);
    /* Free the device list */
    pcap_freealldevs(alldevs);
    return -1;
  }

  /* Open the dump file */
  dumpfile = pcap_dump_open(adhandle, argv[1]);

  if(dumpfile==NULL)
  {
    fprintf(stderr,"\nError opening output file\n");
    return -1;
  }

  printf("\nlistening on %s... Press Ctrl+C to stop...\n", d->description);

  /* At this point, we no longer need the device list. Free it */
  pcap_freealldevs(alldevs);
  
  /* start the capture */
  pcap_loop(adhandle, 0, packet_handler, (unsigned char *)dumpfile);

  return 0;
}

/* Callback function invoked by libpcap for every incoming packet */
void packet_handler(u_char *dumpfile,
  const struct pcap_pkthdr *header,
  const u_char *pkt_data)
{
  /* save the packet on the dump file */
  pcap_dump(dumpfile, header, pkt_data);
}

As you can see, the structure of the program is very similar to the ones we have seen in the previous lessons. The differences are:

  • a call to pcap_dump_open() is issued once the interface is opened. This call opens a dump file and associates it with the interface.
  • the packets are written to this file with a pcap_dump() from the packet_handler() callback. The parameters of pcap_dump() are in 1-1 correspondence with the parameters of pcap_handler().

Reading packets from a dump file

Now that we have a dump file available, we can try to read its content. The following code opens a Npcap/libpcap dump file and displays every packet contained in the file. The file is opened with pcap_open_offline(), then the usual pcap_loop() is used to sequence through the packets. As you can see, reading packets from an offline capture is nearly identical to receiving them from a physical interface.

This example introduces another function: pcap_createsrcstr(). This function is required to create a source string that begins with a marker used to tell Npcap the type of the source, e.g. "rpcap://" if we are going to open an adapter, or "file://" if we are going to open a file. This step is not required when pcap_findalldevs_ex() is used (the returned values already contain these strings). However, it is required in this example because the name of the file is read from the user input.

#include <stdio.h>
#include <pcap.h>
#include "misc.h" /* LoadNpcapDlls */

#define LINE_LEN 16

void dispatcher_handler(u_char *,
  const struct pcap_pkthdr *,
  const u_char *);

int main(int argc, char **argv)
{
  pcap_t *fp;
  char errbuf[PCAP_ERRBUF_SIZE];
  char source[PCAP_BUF_SIZE];

  /* Load Npcap and its functions. */
  if (!LoadNpcapDlls())
  {
    fprintf(stderr, "Couldn't load Npcap\n");
    exit(1);
  }

  if(argc != 2){

    printf("usage: %s filename", argv[0]);
    return -1;

  }

  /* Create the source string according to the new Npcap syntax */
  if ( pcap_createsrcstr(  source, // variable that will keep the source string
              PCAP_SRC_FILE,  // we want to open a file
              NULL,      // remote host
              NULL,      // port on the remote host
              argv[1],    // name of the file we want to open
              errbuf      // error buffer
              ) != 0)
  {
    fprintf(stderr,"\nError creating a source string\n");
    return -1;
  }
  
  /* Open the capture file */
  if ( (fp= pcap_open(source, // name of the device
            65536, // portion of the packet to capture
                   // 65536 guarantees that the whole packet
                   // will be captured on all the link layers
             PCAP_OPENFLAG_PROMISCUOUS, // promiscuous mode
             1000, // read timeout
             NULL, // authentication on the remote machine
             errbuf // error buffer
             ) ) == NULL)
  {
    fprintf(stderr,"\nUnable to open the file %s.\n", source);
    return -1;
  }

  // read and dispatch packets until EOF is reached
  pcap_loop(fp, 0, dispatcher_handler, NULL);

  return 0;
}


void dispatcher_handler(u_char *temp1, 
  const struct pcap_pkthdr *header,
  const u_char *pkt_data)
{
  u_int i=0;

  /*
   * Unused variable
   */
  (VOID)temp1;

  /* print pkt timestamp and pkt len */
  printf("%ld:%ld (%ld)\n", header->ts.tv_sec, header->ts.tv_usec, header->len);      
  
  /* Print the packet */
  for (i=1; (i < header->caplen + 1 ) ; i++)
  {
    printf("%.2x ", pkt_data[i-1]);
    if ( (i % LINE_LEN) == 0) printf("\n");
  }
  
  printf("\n\n");    
  
}

The following example has the same purpose of the last one, but pcap_next_ex() is used instead of the pcap_loop() callback method.

#include <stdio.h>
#include <pcap.h>
#include "misc.h" /* LoadNpcapDlls */

#define LINE_LEN 16

int main(int argc, char **argv)
{
  pcap_t *fp;
  char errbuf[PCAP_ERRBUF_SIZE];
  char source[PCAP_BUF_SIZE];
  struct pcap_pkthdr *header;
  const u_char *pkt_data;
  u_int i=0;
  int res;

  /* Load Npcap and its functions. */
  if (!LoadNpcapDlls())
  {
    fprintf(stderr, "Couldn't load Npcap\n");
    exit(1);
  }

  if(argc != 2)
  {
    printf("usage: %s filename", argv[0]);
    return -1;
  }
  
  /* Create the source string according to the new Npcap syntax */
  if ( pcap_createsrcstr(  source, // variable that will keep the source string
              PCAP_SRC_FILE,  // we want to open a file
              NULL,      // remote host
              NULL,      // port on the remote host
              argv[1],    // name of the file we want to open
              errbuf      // error buffer
              ) != 0)
  {
    fprintf(stderr,"\nError creating a source string\n");
    return -1;
  }
  
  /* Open the capture file */
  if ( (fp= pcap_open(source, // name of the device
            65536, // portion of the packet to capture
                   // 65536 guarantees that the whole packet
                   // will be captured on all the link layers
             PCAP_OPENFLAG_PROMISCUOUS, // promiscuous mode
             1000, // read timeout
             NULL, // authentication on the remote machine
             errbuf // error buffer
             ) ) == NULL)
  {
    fprintf(stderr,"\nUnable to open the file %s.\n", source);
    return -1;
  }
  
  /* Retrieve the packets from the file */
  while((res = pcap_next_ex( fp, &header, &pkt_data)) >= 0)
  {
    /* print pkt timestamp and pkt len */
    printf("%ld:%ld (%ld)\n", header->ts.tv_sec, header->ts.tv_usec, header->len);      
    
    /* Print the packet */
    for (i=1; (i < header->caplen + 1 ) ; i++)
    {
      printf("%.2x ", pkt_data[i-1]);
      if ( (i % LINE_LEN) == 0) printf("\n");
    }
    
    printf("\n\n");    
  }
  
  
  if (res == -1)
  {
    printf("Error reading the packets: %s\n", pcap_geterr(fp));
  }
  
  return 0;
}

Sending Packets

Although the name Npcap indicates clearly that the purpose of the library is packet capture, other useful features for raw networking are provided. Among them, the user can find a complete set of functions to send packets.

Sending a single packet with pcap_sendpacket()

The simplest way to send a packet is shown in the following code snippet. After opening an adapter, pcap_sendpacket() is called to send a hand-crafted packet. pcap_sendpacket() takes as arguments a buffer containing the data to send, the length of the buffer and the adapter that will send it. Notice that the buffer is sent to the net as is, without any manipulation. This means that the application has to create the correct protocol headers in order to send something meaningful.

#include <stdlib.h>
#include <stdio.h>

#include <pcap.h>
#include "misc.h" /* LoadNpcapDlls */

void main(int argc, char **argv)
{
  pcap_t *fp;
  char errbuf[PCAP_ERRBUF_SIZE];
  u_char packet[100];
  int i;

  /* Load Npcap and its functions. */
  if (!LoadNpcapDlls())
  {
    fprintf(stderr, "Couldn't load Npcap\n");
    exit(1);
  }

  /* Check the validity of the command line */
  if (argc != 2)
  {
    printf("usage: %s interface (e.g. 'rpcap://eth0')", argv[0]);
    return;
  }
    
  /* Open the output device */
  if ( (fp= pcap_open(argv[1], // name of the device
            100, // portion of the packet to capture
            PCAP_OPENFLAG_PROMISCUOUS, // promiscuous mode
            1000, // read timeout
            NULL, // authentication on the remote machine
            errbuf // error buffer
            ) ) == NULL)
  {
    fprintf(stderr,
      "\nUnable to open the adapter. %s is not supported by Npcap\n",
      argv[1]);
    return;
  }

  /* Supposing to be on ethernet, set mac destination to 1:1:1:1:1:1 */
  packet[0]=1;
  packet[1]=1;
  packet[2]=1;
  packet[3]=1;
  packet[4]=1;
  packet[5]=1;
  
  /* set mac source to 2:2:2:2:2:2 */
  packet[6]=2;
  packet[7]=2;
  packet[8]=2;
  packet[9]=2;
  packet[10]=2;
  packet[11]=2;
  
  /* Fill the rest of the packet */
  for(i=12;i<100;i++)
  {
    packet[i]=(u_char)i;
  }

  /* Send down the packet */
  if (pcap_sendpacket(fp, packet, 100 /* size */) != 0)
  {
    fprintf(stderr,"\nError sending the packet: %s\n", pcap_geterr(fp));
    return;
  }

  return;
}

Send queues

While pcap_sendpacket() offers a simple and immediate way to send a single packet, send queues provide an advanced, powerful and optimized mechanism to send a collection of packets. A send queue is a container for a variable number of packets that will be sent to the network. It has a size, that represents the maximum amount of bytes it can store.

A send queue is created calling the pcap_sendqueue_alloc() function, specifying the size of the new send queue.

Once the send queue is created, pcap_sendqueue_queue() can be used to add a packet to the send queue. This function takes a pcap_pkthdr with the timestamp and the length and a buffer with the data of the packet. These parameters are the same as those received by pcap_next_ex() and pcap_handler(), therefore queuing a packet that was just captured or read from a file is a matter of passing these parameters to pcap_sendqueue_queue().

To transmit a send queue, Npcap provides the pcap_sendqueue_transmit() function. Note the third parameter: if nonzero, the send will be synchronized, i.e. the relative timestamps of the packets will be respected. This operation requires a remarkable amount of CPU, because the synchronization takes place in the kernel driver using "busy wait" loops. Although this operation is quite CPU intensive, it often results in very high precision packet transmissions (often around few microseconds or less).

Note that transmitting a send queue with pcap_sendqueue_transmit() is much more efficient than performing a series of pcap_sendpacket(), because the send queue is buffered at kernel level drastically decreasing the number of context switches.

When a queue is no longer needed, it can be deleted with pcap_sendqueue_destroy() that frees all the buffers associated with the send queue.

The next program shows how to use send queues. It opens a capture file with pcap_open_offline(), then it moves the packets from the file to a properly allocated send queue. At his point it transmits the queue, synchronizing it if requested by the user.

Note that the link-layer of the dumpfile is compared with the one of the interface that will send the packets using pcap_datalink(), and a warning is printed if they are different—it is important that the capture-file link-layer be the same as the adapter's link layer for otherwise the transmission is pointless.

#include <stdlib.h>
#include <stdio.h>

#include <pcap.h>

#ifdef _WIN32
#include <tchar.h>
BOOL LoadNpcapDlls()
{
  TCHAR npcap_dir[512];
  UINT len;
  len = GetSystemDirectory(npcap_dir, 480);
  if (!len) {
    fprintf(stderr, "Error in GetSystemDirectory: %x", GetLastError());
    return FALSE;
  }
  _tcscat_s(npcap_dir, 512, TEXT("\\Npcap"));
  if (SetDllDirectory(npcap_dir) == 0) {
    fprintf(stderr, "Error in SetDllDirectory: %x", GetLastError());
    return FALSE;
  }
  return TRUE;
}
#endif

void usage();

void main(int argc, char **argv)
{
  pcap_t *indesc,*outdesc;
  char errbuf[PCAP_ERRBUF_SIZE];
  char source[PCAP_BUF_SIZE];
  FILE *capfile;
  int caplen, sync;
  u_int res;
  pcap_send_queue *squeue;
  struct pcap_pkthdr *pktheader;
  u_char *pktdata;
  float cpu_time;
  u_int npacks = 0;
  errno_t fopen_error;

#ifdef _WIN32
  /* Load Npcap and its functions. */
  if (!LoadNpcapDlls())
  {
    fprintf(stderr, "Couldn't load Npcap\n");
    exit(1);
  }
#endif

  /* Check the validity of the command line */
  if (argc <= 2 || argc >= 5)
  {
    usage();
    return;
  }
    
  /* Retrieve the length of the capture file */
  fopen_error = fopen_s(&capfile, argv[1],"rb");
  if(fopen_error != 0){
    printf("Error opening the file, errno %d.\n", fopen_error);
    return;
  }
  
  fseek(capfile , 0, SEEK_END);
  caplen= ftell(capfile)- sizeof(struct pcap_file_header);
  fclose(capfile);
      
  /* Chek if the timestamps must be respected */
  if(argc == 4 && argv[3][0] == 's')
    sync = TRUE;
  else
    sync = FALSE;

  /* Open the capture */
  /* Create the source string according to the new WinPcap syntax */
  if ( pcap_createsrcstr(
              source, // variable that will keep the source string
              PCAP_SRC_FILE,  // we want to open a file
              NULL,      // remote host
              NULL,      // port on the remote host
              argv[1],    // name of the file we want to open
              errbuf      // error buffer
              ) != 0)
  {
    fprintf(stderr,"\nError creating a source string\n");
    return;
  }
  
  /* Open the capture file */
  if ( (indesc= pcap_open(source, 65536, PCAP_OPENFLAG_PROMISCUOUS,
                          1000, NULL, errbuf) ) == NULL)
  {
    fprintf(stderr,"\nUnable to open the file %s.\n", source);
    return;
  }

  /* Open the output adapter */
  if ( (outdesc= pcap_open(argv[2], 100, PCAP_OPENFLAG_PROMISCUOUS,
                           1000, NULL, errbuf) ) == NULL)
  {
    fprintf(stderr,"\nUnable to open adapter %s.\n", source);
    return;
  }

  /* Check the MAC type */
  if (pcap_datalink(indesc) != pcap_datalink(outdesc))
  {
    printf("Warning: the datalink of the capture differs"
           " from the one of the selected interface.\n");
    printf("Press a key to continue, or CTRL+C to stop.\n");
    getchar();
  }

  /* Allocate a send queue */
  squeue = pcap_sendqueue_alloc(caplen);

  /* Fill the queue with the packets from the file */
  while ((res = pcap_next_ex( indesc, &pktheader, &pktdata)) == 1)
  {
    if (pcap_sendqueue_queue(squeue, pktheader, pktdata) == -1)
    {
      printf("Warning: packet buffer too small, not all the packets will be sent.\n");
      break;
    }

    npacks++;
  }

  if (res == -1)
  {
    printf("Corrupted input file.\n");
    pcap_sendqueue_destroy(squeue);
    return;
  }

  /* Transmit the queue */
  
  cpu_time = (float)clock ();

  if ((res = pcap_sendqueue_transmit(outdesc, squeue, sync)) < squeue->len)
  {
    printf("An error occurred sending the packets: %s."
           " Only %d bytes were sent\n", pcap_geterr(outdesc), res);
  }
  
  cpu_time = (clock() - cpu_time)/CLK_TCK;
  
  printf ("\n\nElapsed time: %5.3f\n", cpu_time);
  printf ("\nTotal packets generated = %d", npacks);
  printf ("\nAverage packets per second = %d", (int)((double)npacks/cpu_time));
  printf ("\n");

  /* free the send queue */
  pcap_sendqueue_destroy(squeue);

  /* Close the input file */
  pcap_close(indesc);

  /* 
   * close the output adapter 
   * IMPORTANT: remember to close the adapter, otherwise there will be no
   * guarantee that all the packets will be sent!
   */
  pcap_close(outdesc);

  return;
}


void usage()
{
  
  printf("\nSendcap, sends a libpcap/tcpdump capture file to the net."
         " Copyright (C) 2002 Loris Degioanni.\n");
  printf("\nUsage:\n");
  printf("\t sendcap file_name adapter [s]\n");
  printf("\nParameters:\n");
  printf("\nfile_name: the name of the dump file that will be sent to the network\n");
  printf("\nadapter: the device to use. Use \"WinDump -D\" for a list of valid devices\n");
  printf("\ns: if present, forces the packets to be sent synchronously,"
         " i.e. respecting the timestamps in the dump file.\n\n");

  exit(0);
}

Gathering Statistics on the network traffic

This lesson shows another advanced feature of Npcap: the ability to collect statistics about network traffic. The statistical engine makes use of the kernel-level packet filter to efficiently classify the incoming packet.

In order to use this feature, the programmer must open an adapter and put it in statistical mode. This can be done with pcap_setmode(). In particular, MODE_STAT must be used as the mode argument of this function.

With statistical mode, making an application that monitors the TCP traffic load is a matter of few lines of code. The following sample shows how to do it.

#include <stdlib.h>
#include <stdio.h>

#include <pcap.h>

#include <tchar.h>
BOOL LoadNpcapDlls()
{
  _TCHAR npcap_dir[512];
  UINT len;
  len = GetSystemDirectory(npcap_dir, 480);
  if (!len) {
    fprintf(stderr, "Error in GetSystemDirectory: %x", GetLastError());
    return FALSE;
  }
  _tcscat_s(npcap_dir, 512, _T("\\Npcap"));
  if (SetDllDirectory(npcap_dir) == 0) {
    fprintf(stderr, "Error in SetDllDirectory: %x", GetLastError());
    return FALSE;
  }
  return TRUE;
}

void usage();

void dispatcher_handler(u_char *, const struct pcap_pkthdr *, const u_char *);


void main(int argc, char **argv)
{
  pcap_t *fp;
  char errbuf[PCAP_ERRBUF_SIZE];
  struct timeval st_ts;
  u_int netmask;
  struct bpf_program fcode;
  
  /* Load Npcap and its functions. */
  if (!LoadNpcapDlls())
  {
    fprintf(stderr, "Couldn't load Npcap\n");
    exit(1);
  }

  /* Check the validity of the command line */
  if (argc != 2)
  {
    usage();
    return;
  }
    
  /* Open the output adapter */
  if ( (fp= pcap_open(argv[1], 100, PCAP_OPENFLAG_PROMISCUOUS,
                      1000, NULL, errbuf) ) == NULL)
  {
    fprintf(stderr,"\nUnable to open adapter %s.\n", errbuf);
    return;
  }

  /* Don't care about netmask, it won't be used for this filter */
  netmask=0xffffff; 

  //compile the filter
  if (pcap_compile(fp, &fcode, "tcp", 1, netmask) <0 )
  {
    fprintf(stderr,"\nUnable to compile the packet filter. Check the syntax.\n");
    /* Free the device list */
    return;
  }
    
  //set the filter
  if (pcap_setfilter(fp, &fcode)<0)
  {
    fprintf(stderr,"\nError setting the filter.\n");
    pcap_close(fp);
    /* Free the device list */
    return;
  }

  /* Put the interface in statstics mode */
  if (pcap_setmode(fp, MODE_STAT)<0)
  {
    fprintf(stderr,"\nError setting the mode.\n");
    pcap_close(fp);
    /* Free the device list */
    return;
  }


  printf("TCP traffic summary:\n");

  /* Start the main loop */
  pcap_loop(fp, 0, dispatcher_handler, (PUCHAR)&st_ts);

  pcap_close(fp);
  return;
}

void dispatcher_handler(u_char *state,
  const struct pcap_pkthdr *header,
  const u_char *pkt_data)
{
  struct timeval *old_ts = (struct timeval *)state;
  u_int delay;
  LARGE_INTEGER Bps,Pps;
  struct tm ltime;
  char timestr[16];
  time_t local_tv_sec;

  /* Calculate the delay in microseconds from the last sample. This value
   * is obtained from the timestamp that the associated with the sample. */
  delay = (header->ts.tv_sec - old_ts->tv_sec) * 1000000
    - old_ts->tv_usec + header->ts.tv_usec;
  /* Get the number of Bits per second */
  Bps.QuadPart=(((*(LONGLONG*)(pkt_data + 8)) * 8 * 1000000) / (delay));
  /*                                            ^      ^
                                                  |      |
                                                  |      | 
                                                  |      |
                         converts bytes in bits --       |
                                                         |
                    delay is expressed in microseconds --
  */

  /* Get the number of Packets per second */
  Pps.QuadPart=(((*(LONGLONG*)(pkt_data)) * 1000000) / (delay));

  /* Convert the timestamp to readable format */
  local_tv_sec = header->ts.tv_sec;
  localtime_s(&ltime, &local_tv_sec);
  strftime( timestr, sizeof timestr, "%H:%M:%S", &ltime);

  /* Print timestamp*/
  printf("%s ", timestr);

  /* Print the samples */
  printf("BPS=%I64u ", Bps.QuadPart);
  printf("PPS=%I64u\n", Pps.QuadPart);

  //store current timestamp
  old_ts->tv_sec=header->ts.tv_sec;
  old_ts->tv_usec=header->ts.tv_usec;
}


void usage()
{
  printf("\nShows the TCP traffic load, in bits per second and packets per second."
         "\nCopyright (C) 2002 Loris Degioanni.\n");
  printf("\nUsage:\n");
  printf("\t tcptop adapter\n");
  printf("\t You can use \"WinDump -D\" if you don't know the name of your adapters.\n");

  exit(0);
}

Before enabling statistical mode, the user has the option to set a filter that defines the subset of network traffic that will be monitored. See the Filtering expression syntax documentation for details. If no filter has been set, all of the traffic will be monitored.

Once

  • the filter is set
  • pcap_setmode() is called
  • callback invocation is enabled with pcap_loop()

the interface descriptor starts to work in statistical mode. Notice the fourth parameter (to_ms) of pcap_open(): it defines the interval among the statistical samples. The callback function receives the samples calculated by the driver every to_ms milliseconds. These samples are encapsulated in the second and third parameters of the callback function. Two 64-bit counters are provided: the number of packets and the amount of bytes received during the last interval.

In the example, the adapter is opened with a timeout of 1000 ms. This means that dispatcher_handler() is called once per second. At this point a filter that keeps only tcp packets is compiled and set. Then pcap_setmode() and pcap_loop() are called. Note that a struct timeval pointer is passed to pcap_loop() as the user parameter. This structure will be used to store a timestamp in order to calculate the interval between two samples. dispatcher_handler()uses this interval to obtain the bits per second and the packets per second and then prints these values on the screen.

Note finally that this example is by far more efficient than a program that captures the packets in the traditional way and calculates statistics at user-level. Statistical mode requires the minumum amount of data copies and context switches and therefore the CPU is optimized. Moreover, a very small amount of memory is required.