WinPcap: Handling offline dump files

# Handling offline dump files

In this lession we are going to learn how to handle packet capture to a file (dump to file). WinPcap 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. We'll see also how to use the kernel dump feature of WinPcap to obtain high-performance dumps (NOTE: At the moment, due to some problems with the new kernel buffer, this feature has been disabled).

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, Ethereal 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"

    /* 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;

        /* 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 WinPcap 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 WinPcap\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 WinPcap/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_createsrcsrc(). This function is required to create a source string that begins with a marker used to tell WinPcap 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>

    #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];

        if(argc != 2){

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

        }

        /* 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 -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>

    #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;

        if(argc != 2)
        {
            printf("usage: %s filename", argv[0]);
            return -1;
        }

        /* 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 -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;
    }

Writing packets to a dump file with pcap_live_dump

NOTE: At the moment, due to some problems with the new kernel buffer, this feature has been disabled.

Recent versions of WinPcap provide a further way to save network traffic to disk, the pcap_live_dump() function. pcap_live_dump() takes three parameters: a file name, the maximum size (in bytes) that this file is allowed to reach and the maximum amount of packets that the file is allowed to contain. Zero means no limit for both these values. Notice that the program can set a filter (with pcap_setfilter(), see the tutorial Filtering the traffic) before calling pcap_live_dump() to define the subset of the traffic that will be saved.

pcap_live_dump() is non-blocking, therefore it starts the dump and returns immediately: The dump process goes on asynchronously until the maximum file size or the maximum amount of packets has been reached.

The application can wait or check the end of the dump with pcap_live_dump_ended(). Beware that if the sync parameter is nonzero, this function will block your application forever if the limits are both 0.

    /*
     * Copyright (c) 1999 - 2005 NetGroup, Politecnico di Torino (Italy)
     * Copyright (c) 2005 - 2006 CACE Technologies, Davis (California)
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions
     * are met:
     *
     * 1. Redistributions of source code must retain the above copyright
     * notice, this list of conditions and the following disclaimer.
     * 2. Redistributions in binary form must reproduce the above copyright
     * notice, this list of conditions and the following disclaimer in the
     * documentation and/or other materials provided with the distribution.
     * 3. Neither the name of the Politecnico di Torino, CACE Technologies
     * nor the names of its contributors may be used to endorse or promote
     * products derived from this software without specific prior written
     * permission.
     *
     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     *
     */

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

    #include <pcap.h>

    #error At the moment the kernel dump feature is not supported in the driver

    main(int argc, char **argv) {

        pcap_if_t *alldevs, *d;
        pcap_t *fp;
        u_int inum, i=0;
        char errbuf[PCAP_ERRBUF_SIZE];

        printf("kdump: saves the network traffic to file using WinPcap kernel-level dump faeature.\n");
        printf("\t Usage: %s [adapter] | dump_file_name max_size max_packs\n", argv[0]);
        printf("\t Where: max_size is the maximum size that the dump file will reach (0 means no limit)\n");
        printf("\t Where: max_packs is the maximum number of packets that will be saved (0 means no limit)\n\n");

        if(argc < 5){

            /* The user didn't provide a packet source: Retrieve the device list */
            if (pcap_findalldevs(&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 WinPcap is installed.\n");
                return -1;
            }

            printf("Enter the interface number (1-%d):",i);
            scanf("%d", &inum);

            if(inum < 1 || inum > i)
            {
                printf("\nInterface number out of range.\n");
                /* Free the device list */
                return -1;
            }

            /* Jump to the selected adapter */
            for(d=alldevs, i=0; i< inum-1 ;d=d->next, i++);

            /* Open the device */
            if ( (fp = pcap_open_live(d->name, 100, 1, 20, errbuf) ) == NULL)
            {
                fprintf(stderr,"\nError opening adapter\n");
                return -1;
            }

            /* Free the device list */
            pcap_freealldevs(alldevs);

            /* Start the dump */
            if(pcap_live_dump(fp, argv[1], atoi(argv[2]), atoi(argv[3]))==-1){
                printf("Unable to start the dump, %s\n", pcap_geterr(fp));
                return -1;
            }
        }
        else{

            /* Open the device */
            if ( (fp= pcap_open_live(argv[1], 100, 1, 20, errbuf) ) == NULL)
            {
                fprintf(stderr,"\nError opening adapter\n");
                return -1;
            }

            /* Start the dump */
            if(pcap_live_dump(fp, argv[0], atoi(argv[1]), atoi(argv[2]))==-1){
                printf("Unable to start the dump, %s\n", pcap_geterr(fp));
                return -1;
            }
        }

        /* Wait until the dump finishes, i.e. when  max_size or max_packs is reached*/
        pcap_live_dump_ended(fp, TRUE);

        /* Close the adapter, so that the file is correctly flushed */
        pcap_close(fp);

        return 0;
    }

The difference between pcap_live_dump() and pcap_dump(), apart from the possibility to set limits, is performance. pcap_live_dump() exploits the ability of the WinPcap NPF driver (see NPF driver internals manual) to write dumps from kernel level, minimizing the number of context switches and memory copies.

Obviously, since this feature is currently not available on other operating systems, pcap_live_dump() is WinPcap specific and is present only under Win32.

<<< Previous Next >>>

documentation. Copyright (c) 2002-2005 Politecnico di Torino. Copyright (c) 2005-2009 CACE Technologies. All rights reserved.