Main Page | Modules | Data Structures | File List | Data Fields | Globals | Related Pages

# Interpreting the packets
[WinPcap tutorial: a step by step guide to using WinPcap]

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:

    /*
     * Copyright (c) 1999 - 2003
     * NetGroup, Politecnico di Torino (Italy)
     * 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 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 "pcap.h"

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

    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;

        /* 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 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 */
            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 WinPcap\n");
            /* 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;

        /* convert the timestamp to readable format */
        ltime=localtime(&header->ts.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);
    }

    00001 /*
    00002  * Copyright (c) 1999 - 2003
    00003  * NetGroup, Politecnico di Torino (Italy)
    00004  * All rights reserved.
    00005  *
    00006  * Redistribution and use in source and binary forms, with or without
    00007  * modification, are permitted provided that the following conditions
    00008  * are met:
    00009  *
    00010  * 1. Redistributions of source code must retain the above copyright
    00011  * notice, this list of conditions and the following disclaimer.
    00012  * 2. Redistributions in binary form must reproduce the above copyright
    00013  * notice, this list of conditions and the following disclaimer in the
    00014  * documentation and/or other materials provided with the distribution.
    00015  * 3. Neither the name of the Politecnico di Torino nor the names of its
    00016  * contributors may be used to endorse or promote products derived from
    00017  * this software without specific prior written permission.
    00018  *
    00019  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    00020  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    00021  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    00022  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    00023  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    00024  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    00025  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    00026  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    00027  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    00028  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    00029  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    00030  *
    00031  */
    00032
    00033
    00034 #include "pcap.h"
    00035
    00036 /* 4 bytes IP address */
    00037 typedef struct ip_address{
    00038     u_char byte1;
    00039     u_char byte2;
    00040     u_char byte3;
    00041     u_char byte4;
    00042 }ip_address;
    00043
    00044 /* IPv4 header */
    00045 typedef struct ip_header{
    00046     u_char  ver_ihl;        // Version (4 bits) + Internet header length (4 bits)
    00047     u_char  tos;            // Type of service
    00048     u_short tlen;           // Total length
    00049     u_short identification; // Identification
    00050     u_short flags_fo;       // Flags (3 bits) + Fragment offset (13 bits)
    00051     u_char  ttl;            // Time to live
    00052     u_char  proto;          // Protocol
    00053     u_short crc;            // Header checksum
    00054     ip_address  saddr;      // Source address
    00055     ip_address  daddr;      // Destination address
    00056     u_int   op_pad;         // Option + Padding
    00057 }ip_header;
    00058
    00059 /* UDP header*/
    00060 typedef struct udp_header{
    00061     u_short sport;          // Source port
    00062     u_short dport;          // Destination port
    00063     u_short len;            // Datagram length
    00064     u_short crc;            // Checksum
    00065 }udp_header;
    00066
    00067 /* prototype of the packet handler */
    00068 void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data);
    00069
    00070
    00071 main()
    00072 {
    00073 pcap_if_t *alldevs;
    00074 pcap_if_t *d;
    00075 int inum;
    00076 int i=0;
    00077 pcap_t *adhandle;
    00078 char errbuf[PCAP_ERRBUF_SIZE];
    00079 u_int netmask;
    00080 char packet_filter[] = "ip and udp";
    00081 struct bpf_program fcode;
    00082
    00083     /* Retrieve the device list */
    00084     if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1)
    00085     {
    00086         fprintf(stderr,"Error in pcap_findalldevs: %s\n", errbuf);
    00087         exit(1);
    00088     }
    00089
    00090     /* Print the list */
    00091     for(d=alldevs; d; d=d->next)
    00092     {
    00093         printf("%d. %s", ++i, d->name);
    00094         if (d->description)
    00095             printf(" (%s)\n", d->description);
    00096         else
    00097             printf(" (No description available)\n");
    00098     }
    00099
    00100     if(i==0)
    00101     {
    00102         printf("\nNo interfaces found! Make sure WinPcap is installed.\n");
    00103         return -1;
    00104     }
    00105
    00106     printf("Enter the interface number (1-%d):",i);
    00107     scanf("%d", &inum);
    00108
    00109     if(inum < 1 || inum > i)
    00110     {
    00111         printf("\nInterface number out of range.\n");
    00112         /* Free the device list */
    00113         pcap_freealldevs(alldevs);
    00114         return -1;
    00115     }
    00116
    00117     /* Jump to the selected adapter */
    00118     for(d=alldevs, i=0; i< inum-1 ;d=d->next, i++);
    00119
    00120     /* Open the adapter */
    00121     if ( (adhandle= pcap_open(d->name,  // name of the device
    00122                              65536,     // portion of the packet to capture.
    00123                                         // 65536 grants that the whole packet will be captured on all the MACs.
    00124                              PCAP_OPENFLAG_PROMISCUOUS,         // promiscuous mode
    00125                              1000,      // read timeout
    00126                              NULL,      // remote authentication
    00127                              errbuf     // error buffer
    00128                              ) ) == NULL)
    00129     {
    00130         fprintf(stderr,"\nUnable to open the adapter. %s is not supported by WinPcap\n");
    00131         /* Free the device list */
    00132         pcap_freealldevs(alldevs);
    00133         return -1;
    00134     }
    00135
    00136     /* Check the link layer. We support only Ethernet for simplicity. */
    00137     if(pcap_datalink(adhandle) != DLT_EN10MB)
    00138     {
    00139         fprintf(stderr,"\nThis program works only on Ethernet networks.\n");
    00140         /* Free the device list */
    00141         pcap_freealldevs(alldevs);
    00142         return -1;
    00143     }
    00144
    00145     if(d->addresses != NULL)
    00146         /* Retrieve the mask of the first address of the interface */
    00147         netmask=((struct sockaddr_in *)(d->addresses->netmask))->sin_addr.S_un.S_addr;
    00148     else
    00149         /* If the interface is without addresses we suppose to be in a C class network */
    00150         netmask=0xffffff;
    00151
    00152
    00153     //compile the filter
    00154     if (pcap_compile(adhandle, &fcode, packet_filter, 1, netmask) <0 )
    00155     {
    00156         fprintf(stderr,"\nUnable to compile the packet filter. Check the syntax.\n");
    00157         /* Free the device list */
    00158         pcap_freealldevs(alldevs);
    00159         return -1;
    00160     }
    00161
    00162     //set the filter
    00163     if (pcap_setfilter(adhandle, &fcode)<0)
    00164     {
    00165         fprintf(stderr,"\nError setting the filter.\n");
    00166         /* Free the device list */
    00167         pcap_freealldevs(alldevs);
    00168         return -1;
    00169     }
    00170
    00171     printf("\nlistening on %s...\n", d->description);
    00172
    00173     /* At this point, we don't need any more the device list. Free it */
    00174     pcap_freealldevs(alldevs);
    00175
    00176     /* start the capture */
    00177     pcap_loop(adhandle, 0, packet_handler, NULL);
    00178
    00179     return 0;
    00180 }
    00181
    00182 /* Callback function invoked by libpcap for every incoming packet */
    00183 void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data)
    00184 {
    00185     struct tm *ltime;
    00186     char timestr[16];
    00187     ip_header *ih;
    00188     udp_header *uh;
    00189     u_int ip_len;
    00190     u_short sport,dport;
    00191
    00192     /* convert the timestamp to readable format */
    00193     ltime=localtime(&header->ts.tv_sec);
    00194     strftime( timestr, sizeof timestr, "%H:%M:%S", ltime);
    00195
    00196     /* print timestamp and length of the packet */
    00197     printf("%s.%.6d len:%d ", timestr, header->ts.tv_usec, header->len);
    00198
    00199     /* retireve the position of the ip header */
    00200     ih = (ip_header *) (pkt_data +
    00201         14); //length of ethernet header
    00202
    00203     /* retireve the position of the udp header */
    00204     ip_len = (ih->ver_ihl & 0xf) * 4;
    00205     uh = (udp_header *) ((u_char*)ih + ip_len);
    00206
    00207     /* convert from network byte order to host byte order */
    00208     sport = ntohs( uh->sport );
    00209     dport = ntohs( uh->dport );
    00210
    00211     /* print ip addresses and udp ports */
    00212     printf("%d.%d.%d.%d.%d -> %d.%d.%d.%d.%d\n",
    00213         ih->saddr.byte1,
    00214         ih->saddr.byte2,
    00215         ih->saddr.byte3,
    00216         ih->saddr.byte4,
    00217         sport,
    00218         ih->daddr.byte1,
    00219         ih->daddr.byte2,
    00220         ih->daddr.byte3,
    00221         ih->daddr.byte4,
    00222         dport);
    00223 }

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:

`1. {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.

<<< Previous Next >>>

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