Team LiB
Previous Section Next Section

10.2. Getting Started with libpcap

Now that we have libpcap installed, we can write our first network packet-capture tool. The example we are going to demonstrate is a simple tool for capturing Address Resolution Protocol (ARP) packets from a local network interface. A slightly more complex tool utilizing libpcap to capture and process TCP headers (SYNplescan) is discussed in Chapter 11.

Some of the operations we will undertake with libpcap work only if you are running as the root user. Therefore, tools written that use libpcap (as per the examples) commonly need to be run by the root user, or be SETUID root. Your tool should be careful of what it does with input and captured packets so that it is not vulnerable to buffer overflows and other security vulnerabilities. A well-written tool should generally drop privileges after functions requiring root privileges have been performed.


10.2.1. Overview of Arpsniff

ARP is the protocol used in IP networks to map network protocol addresses (most often, IP addresses) to link layer hardware addresses. When a system on a network needs to communicate with another system on the local subnet (for example, another system on the local TCP/IP subnet), it consults its cache of hardware and protocol addresses (commonly Ethernet Media Access Control, or MAC addresses) to determine if a matching system is known. Otherwise, an ARP exchange is sent to the network device hardware broadcast address, as shown in Figure 10-1.

Figure 10-1. Overview of an ARP exchange


Arpsniff is designed to capture both packets in the ARP packet interchanges occurring on the network, and to output the IP addresses of the machines involved. This could be useful for discovering live hosts on the network, or for some other network reconnaissance purpose. For clarity we can separate Arpsniff into five major sections of libpcap functionality to understand what we are doing at each step:

  1. Identify the network interface.

  2. Open the network interface.

  3. Configure packet-capture options.

  4. Capture and process packets.

  5. Close down gracefully.

Note that Arpsniff captures interchanges on the network only if you are not in a switched environment. If you are in a switched environment you might see only one of the packetsi.e., the packet sent to the subnet broadcast address. Several techniques exist for capturing all packets on a switched environment. The Ettercap sniffer uses several of these techniques, including ARP poisoning. Visit http://ettercap.sourceforge.net/ for more information.


10.2.2. Identify the Network Interface

To capture packets from a network interface, we need to supply libpcap with a network interface to use for packet capture. We have a number of different options, including specifying a network interface, asking libpcap to automatically find an appropriate interface, obtaining a list of the available interfaces, and in recent versions of libpcap, using all available interfaces to capture traffic.

libpcap does not support all network interfaces. Most Ethernet cards will work, as will most wireless cards while capturing packets on the network you are associated to. libpcap generates an error for any network interface supplied to it that it cannot determine how to open.


The easiest way is to let libpcap choose a suitable interface:

#include <pcap.h>

char *device;                         /* device to sniff on */
char errbuf[PCAP_ERRBUF_SIZE];        /* pcap error messages buffer */

device = pcap_lookupdev (errbuf);     /* let pcap find a compatible device */

if (device == NULL)                   /* there was an error */
  {
    fprintf (stderr, "%s", errbuf);
    exit (1);
  }

To use the libpcap functions, we are including the pcap.h header file. This contains the libpcap function definitions as well as other handy, predefined values, such as PCAP_ERRBUF_SIZE.

The prototype for pcap_lookupdev is as follows:

char *pcap_lookupdev(char *errbuf)

This function returns the name of an appropriate interface to be used for packet capture. For Linux this is typically eth0 or something similar, but this might be different for other operating systems.

The function returns NULL and the errbuf array is populated with an error message if an error occursfor example, if no suitable interfaces were located or if the user running the tool did not have sufficient privileges to perform the operation. A number of functions within libpcap use an errbuf array in this way to return meaningful error messages to the calling tool.

Instead of letting libpcap choose a suitable interface, you can allow the user to specify one. For some tools it is useful to be able to obtain a list of usable network interfaces:

pcap_if_t *alldevsp;       /* list of interfaces */

if (pcap_findalldevs (&alldevsp, errbuf) < 0)   
  {
    fprintf (stderr, "%s", errbuf);
    exit (1);
  }
while (alldevsp != NULL)
  {
    printf ("%s\n", alldevsp->name);
    alldevsp = alldevsp->next;
  }

The pcap_findalldevs function takes a pcap_if_t pointer and returns a linked list of information about the interfaces found. The pcap_if_t (a type derived from pcap_if) structure contains several pieces of information that might be useful to a tool:

struct pcap_if
  {
    struct pcap_if *next;
    char *name;             /* interface name */
    char *description;      /* human-readable description of interface, or NULL */
    struct pcap_addr *addresses;
    bpf_u_int32 flags;      /* PCAP_IF_LOOPBACK if a loopback interface */
};

The linked list is populated with the names and descriptions of all the interfaces libpcap can use, as well as the IP address and netmask of the interfaces, as follows:

struct pcap_addr
  {
    struct pcap_addr *next;
    struct sockaddr *addr;          /* interface address */
    struct sockaddr *netmask;       /* netmask for that address */
    struct sockaddr *broadaddr;     /* broadcast address */
    struct sockaddr *dstaddr;       /* point-to-point destination or NULL */
};

You could use the information this returns to allow the person using the tool to select an appropriate interface to use, such as the network to which the interface is attached.

10.2.3. Open the Network Interface

Once we have a network interface supplied by the user, or libpcap has located an appropriate interface, we can open the interface for packet capture:

pcap_t *handle;

handle = pcap_open_live (device,  /* device to sniff on */
       BUFSIZ,  /* maximum number of bytes to capture per packet */
       1, /* promisc - 1 to set card in promiscuous mode, 0 to not */
       0, /* to_ms - amount of time to perform packet capture in milliseconds */
          /* 0 = sniff until error */
       errbuf); /* error message buffer if something goes wrong */

if (handle == NULL)   /* there was an error */
  {
    fprintf (stderr, "%s", errbuf);
    exit (1);
  }

if (strlen (errbuf) > 0)
  {
      fprintf (stderr, "Warning: %s", errbuf);  /* a warning was generated */
      errbuf[0] = 0;    /* reset error buffer */
  }

pcap_t provides a packet-capture descriptor to the opened session which is used throughout the tool. pcap_t is a typedef of the pcap structure that is used internally within libpcap; however, the user should never need to know what this structure actually contains.

The prototype for pcap_open_live is as follows:

pcap_t *pcap_open_live(const char *device, int snaplen, int promisc, 
                       int to_ms, char *errbuf)

The pcap_open_live function is used to open network interfaces for packet capture, and as such it takes several parameters, as shown in Table 10-1.

Table 10-1. Parameters to pcap_open_live

Parameter

Description

device

The interface on which to capture traffic. This is either a string such as eth0, or any, or NULL, and it can be used to capture traffic from all interfaces on recent Linux systems.

snaplen

The maximum number of bytes to capture per packet (snapshot length). If this is less than the length of the packet, the packet is truncated. Note that this has nothing to do with Ethernet SNAP headers.

promisc

Flag to determine whether the interface should be put into promiscuous mode. Promiscuous mode instructs the network interface to capture all traffic on a shared medium network (such as Ethernet), regardless of whether it was intended for the system running the tool. Note that the interface could be in promiscuous mode for some other reason, and it might not be supported for all network interfaces.

to_ms

Timeout in milliseconds before a read operation returns. This is not supported on all platforms. A value of 0 causes the read to wait until an error occurs.

errbuf

Error buffer. If an error or warning occurs, this is populated with a human-readable error message.


Although all options are present for all platforms supported by libpcap, some options will work only if supported by the underlying operating system or device drivers. In particular, promiscuous mode might not work as expected on all devices. A good example is wireless network devices. Most wireless network devices will allow libpcap to capture wireless traffic in promiscuous mode in Unix-like operating systems, allowing a tool to capture packets on the network to which the user is associated. On Windows, this is not supported by all drivers. To capture all packets, including those not on the network to which a user is associated, special driver support is required. This is covered later in this chapter.


10.2.4. Configure Packet-Capture Options

Once we have an active packet-capture interface we can determine or set a number of options before we start capturing packets from the interface. For example, we can determine the type of interface that has been opened:

if (pcap_datalink (handle) != DLT_EN10MB)
  {
    fprintf (stderr, "This program only supports Ethernet cards!\n");
    exit (1);
  }

The pcap_datalink function returns the type of the underlying link layer from the pcap_t handle passed to it.

The prototype for pcap_datalink is as follows:

int pcap_datalink(pcap_t *p)

This function will generate an error if the selected network interface was not an Ethernet interface (10MB, 100MB, 1000MB, or more). It is wise to check the data link type before trying to manipulate data captured from the network interface, as this determines what format the data is in.

The data link layers that libpcap can return include network data link types (such as Ethernet), as well as encapsulation types such as the common dial-up Point to Point Protocol (PPP) and OpenBSD pflog. Table 10-2 shows supported link types as of libpcap Version 0.8.3.

Table 10-2. Link layers supported by libpcap

Data link type

Description

DLT_EN10MB

Ethernet devices, including 10MB, 100MB, 1000MB, and up

DLT_IEEE802_11

802.11 wireless devices; can include all the different variants of 802.11, including 802.11, 802.11a, 802.11b, 802.11g, and so on

DLT_NULL

BSD loop-back encapsulation

DLT_IEEE802

802.5 token ring devices

DLT_ARCNET

ARCNET devices

DLT_SLIP

Serial Line Internet Protocol (SLIP; predecessor to PPP)

DLT_PPP

PPP

DLT_SLIP_BSDOS

BSD/OS SLIP

DLT_PPP_BSDOS

BSD/OS PPP

DLT_ATM_CLIP

Linux Classical IP (CLIP) over ATM

DLT_FDDI

Fiber Distributed Data Interface (FDDI; data over fiber optic cable)

DLT_ATM_RFC1483

RFC 1483 encapsulated Asynchronous Transfer Mode (ATM)

DLT_RAW

Raw IP packet

DLT_PPP_SERIAL

PPP in HDLC framing (RFC 1662 or Cisco PPP with HDLC framing)

DLT_PPP_ETHER

PPP over Ethernet (PPPoE); commonly used in DSL networks

DLT_C_HDLC

Cisco PPP with HDLC framing

DLT_FRELAY

Frame relay devices

DLT_LOOP

OpenBSD loop-back encapsulation

DLT_ENC

OpenBSD encapsulated IP

DLT_LINUX_SLL

Linux cooked capture encapsulation

DLT_LTALK

Apple LocalTalk

DLT_PFLOG

OpenBSD pflog firewall log

DLT_PRISM_HEADER

802.11 Prism monitor mode devices

DLT_IP_OVER_FC

RFC 2625 IP over Fiber Channel

DLT_SUNATM

Sun raw ATM devices

DLT_IEEE802_11_RADIO

BSD wireless with Radiotap header

DLT_APPLE_IP_OVER_IEEE1394

Apple IP over IEEE-1394 (FireWire)

DLT_IEEE802_11_RADIO_AVS

AVS wireless monitor mode devices

DLT_ARCNET_LINUX

Linux ARCNET devices

DLT_LINUX_IRDA

Linux IRDA devices


Some platforms and interfaces can have multiple link types available. In this case we need to interrogate the underlying data link layer to see what link types are supported. We can do this using pcap_list_datalinks with the pcap_t handle from the opened session:

int *dlt_buf;         /* array of supported data link types */
int num;              /* number of supported link type */
int i;                /* counter for for loop */

num = pcap_list_datalinks(handle, &dlt_buf);

for (i=0; i<num; i++)
  {
    printf("%d - %s - %s\n",dlt_buf[i],
              pcap_datalink_val_to_name(dlt_buf[i]),
              pcap_datalink_val_to_description(dlt_buf[i]));
  }

This example uses three functions to enumerate the data link types, and to display human-readable names and descriptions for them. The prototypes of these functions are as follows:

int pcap_list_datalinks(pcap_t *p, int **dlt_buf);
const char *pcap_datalink_val_to_name(int dlt);
const char *pcap_datalink_val_to_description(int dlt);

In most cases, the preceding code displays only one link type and the output usually is something such as the following:

> ./example
> 1 - EN10MB - Ethernet

However, when multiple data link types are supported, something such as the following can be displayed. This was run on FreeBSD 5.2 with an Atheros-based wireless network card:

> ./example
> 127 - IEEE802_11_RADIO - 802.11 plus BSD radio information header
> 105 - IEEE802_11 - 802.11
> 1 - EN10MB - Ethernet

In this case, in which multiple link types are returned, we can select the desired link type using pcap_set_datalink, which has the following prototype:

int pcap_set_datalink(pcap_t *p, int dlt);

For example, the following code is required on recent versions of FreeBSD to capture data in Radiotap format from supported wireless cards:

if (pcap_set_datalink (handle, DLT_IEEE802_11_RADIO) == -1)
    {
      pcap_perror (handle, "Error on pcap_set_datalink: ");
      exit (1);
    }

Now that we have determined that the link type we are capturing on is Ethernet-based, we can assume the interface has an IP address and netmask (as Arpsniff does not work on a non-IP network). We can determine the IP address and netmask as follows:

bpf_u_int32 netp;     /* ip address of interface */
bpf_u_int32 maskp;    /* subnet mask of interface */

if (pcap_lookupnet (device, &netp, &maskp, errbuf) == -1)
    {
      fprintf (stderr, "%s", errbuf);
      exit (1);
    }

The pcap_lookupnet function has the following prototype:

int pcap_lookupnet(const char *device, bpf_u_int32 *netp, bpf_u_int32 *maskp,
                          char *errbuf)

This function returns the network address and netmask as an integer value. To convert these to a human-readable format, you can do something such as the following:

char *net_addr;
struct in_addr addr;
addr.s_addr = netp;
net_addr = inet_ntoa(addr);

The pcap_lookupnet function does not take a pcap_t argument, as it can be run on an interface before it is opened for packet capture. This could be used to locate a particular interface as an alternative to using pcap_findalldevs. You also can use this information when setting a Berkeley Packet Filter (BPF) on the capture, which requires the netmask of the network to be capturing on.

libpcap supports BPF filter programs for filtering incoming packets. BPF is a powerful filtering language based on a programmable state engine running pseudo-Assembly language instructions, as shown in Example 10-1.

Example 10-1. tcpdump -d output for "arp" filter
(000) ldh      [12]
(001) jeq      #0x806           jt 2    jf 3
(002) ret      #68
(003) ret      #0

libpcap supports BPF at the kernel level for systems that have operating system support for BPF, such as AIX, and in a user-space implementation in the libpcap library for systems that do not have kernel BPF implementations. On systems that have BPF support at the kernel level, filtering can be done very quickly and efficiently, as the packets the filter drops do not have to be copied from the kernel space to the tool running in user space.

Using libpcap we can generate a BPF filter from a tcpdump-style, human-readable filter string using the pcap_compile function, as shown here:

char *filter = "arp";   /* filter for BPF (human readable) */
struct bpf_program fp;  /* compiled BPF filter */

if (pcap_compile (handle, &fp, filter, 0, maskp) == -1)
    {
      fprintf (stderr, "%s", pcap_geterr (handle));
      exit (1);
    }

The prototype for pcap_compile is as follows:

int pcap_compile(pcap_t *p, struct bpf_program *fp, char *str, int optimize,
                      bpf_u_int32 netmask)

This function supports the same human-readable filter syntax used by tcpdump. Read the full syntax from the tcpdump manpage, or online at http://www.tcpdump.org. Table 10-3 shows some examples of the syntax.

Table 10-3. Example of human-readable filters

Filter syntax

Description

udp or arp

Only UDP or ARP packets are passed.

icmp[icmptype] != icmp-echo

All ICMP packets that are not echo requests/replies.

host 192.168.0.12

All packets to/from 192.168.0.12.

ip proto 47

Only IP protocol 47 (GRE) packets.


Once the human-readable syntax has been compiled into the state machine pseudocode, we can set the filter on the capture session we have initiated as follows:

if (pcap_setfilter (handle, &fp) == -1)
    {
      fprintf (stderr, "%s", pcap_geterr (handle));
      exit (1);
    }

Here is the prototype for the pcap_setfilter function:

int pcap_setfilter(pcap_t *p, struct bpf_program *fp)q

The pcap_setfilter function sets the BPF program in the kernel where BPF support is present or in a user-space implementation if there is no kernel support for BPF.

After we have successfully set the filter on our capture, we can free the memory used for the filter (in this case, a rather trivial amount) as follows:

pcap_freecode (&fp);

Now we are ready to capture some packets from the interface we have opened, with the BPF filter we have set. For Arpsniff we have set a filter of arp, so we should only have ARP packets passed to us by the filter.

10.2.5. Capture and Process Packets

libpcap has several options for handling the capture and processing of packets. The three main functions for capturing and processing packets are shown in Table 10-4.

Table 10-4. libpcap packet-capture functions

Function

Prototype

Description

pcap_next_ex

int pcap_next_ex 
(pcap_t *p,
struct pcap_pkthdr **pkt_header,
const u_char **pkt_data)

Reads the next packet from the capture session, returning success or failure. The following values are returned:


1

Packet was read.


0

Timeout expired.


-1

An error occurred.


-2

Packets are being read from a saved file, and no more packets are available.

If the packet was read, the pkt_header and pkt_data pointers are set to the packet header and packet data, respectively.

pcap_dispatch

int pcap_dispatch
(pcap_t *p,
int cnt,
pcap_handler callback,
u_char *user)

Reads up to cnt packets from the session. A cnt value of -1 reads all packets in the buffer. pcap_dispatch uses a callback function (discussed in a bit) to process packets, and returns the number of packets processed. pcap_dispatch returns when a read timeout occurs on supported platforms.

The user value is a user-specified value to be passed to the callback function, and can be NULL.

pcap_loop

int pcap_loop
(pcap_t *p,
int cnt,
pcap_handler callback,
u_char *user)

Reads cnt packets from the session. pcap_loop uses a callback function to process packets, loops forever until cnt packets are processed (a value of -1 loops forever), and returns the following:


0

cnt packets read.


-1

An error occurred.


-2

Loop was terminated by pcap_breakloop.

The user value is a user-specified value to be passed to the callback function, and can be NULL.


Also available to the user for simple tasks is the pcap_next function. This is a wrapper to the pcap_dispatch function with a cnt of 1.

Read timeouts specified in pcap_open_live are not supported consistently across platforms, and as such you can't rely on pcap_dispatch returning after the read timeout on all platforms. For this reason you should not use pcap_dispatch as a polling mechanism.


pcap_next has the following prototype:

const u_char *pcap_next(pcap_t *p, struct pcap_pkthdr *h)

As the pcap_next function doesn't support error messages, you should use pcap_next_ex instead if capturing single packets.

For Arpsniff we are going to use pcap_loop as follows:

if ((r = pcap_loop (handle, -1, process_packet, NULL)) < 0)
  {
    if (r == -1)    /* pcap error */
      {
        fprintf (stderr, "%s", pcap_geterr (handle));
        exit (1);
       }
    /* otherwise return should be -2, meaning pcap_breakloop has been called */
}

The process_packet parameter passed to pcap_loop is the name of the function we have written to handle the packet in whichever way we want when it has been captured. Both pcap_dispatch and pcap_loop use a callback function with the same parameters as follows:

void process_packet (u_char *user, const struct pcap_pkthdr *header,
                      const u_char *packet)

The callback function does not return anything, as pcap_loop would not know what to do with the returned value. As parameters, pcap_loop passes in a header with information about the packet, as well as a pointer to the body of the packet itself. The user value is the value specified in pcap_loop and is not commonly used.

Now we can write the main functionality of the tool within the callback function, and we can run this every time a packet matching the filter is run.

10.2.6. Close Down

Once we are finished capturing packets, we should gracefully close down the connection before we exit the tool. Two functions can come in handy in this case.

Arpsniff uses a trivial signal handler to intercept the Ctrl-C break sequence. Because the tool is in an endless loop, due to the pcap_loop function, the signal handler calls the pcap_breakloop function. This function, which is available only in recent versions of libpcap, is designed for use in signal handlers or similar tools, and allows the packet- capture loop to be interrupted smoothly and the tool to exit gracefully. pcap_breakloop takes only one argument and has the following prototype:

void pcap_breakloop(pcap_t *)

Now that we have exited the packet-capture loop, we can close the packet-capture handler and associated resources using the pcap_close function, which has the following prototype:

void pcap_close(pcap_t *p)

10.2.7. Arpsniff

Example 10-2 shows the complete code for the Arpsniff tool we have been discussing. You should be able to compile this on most Linux distributions as follows:

gcc -o arpsniff arpsniff.c -lpcap

The -lpcap option instructs gcc to link the final binary tool against the pcap library.

Note that this has been developed on Gentoo Linux on x86, and with the removal of the pcap_breakloop call on Red Hat Enterprise Linux on x86. Although it should work on other Linux variants, it might not work on other Unix-like systems without a little tweaking.

Example 10-2. Arpsniff source code
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <net/if.h>
#include <pcap.h>
#include <netinet/if_ether.h>

/* ugly shortcut -- Ethernet packet headers are 14 bytes */
#define ETH_HEADER_SIZE 14

/* for the sake of clarity we'll use globals for a few things */
char *device;       /* device to sniff on */
int verbose = 0;    /* verbose output about device */
pcap_t *handle;     /* handle for the opened pcap session */

/* gracefully handle a Control C */
void
ctrl_c ( )
{
  printf ("Exiting\n");
  pcap_breakloop (handle);  /* tell pcap_loop or pcap_dispatch to stop capturing */
  pcap_close(handle);
  exit (0);
}

/* usage */
void
usage (char *name)
{
  printf ("%s - simple ARP sniffer\n", name);
  printf ("Usage: %s [-i interface] [-l] [-v]\n", name);
  printf ("    -i    interface to sniff on\n");
  printf ("    -l    list available interfaces\n");
  printf ("    -v    print verbose info\n\n");
  exit (1);
}

/* callback function to process a packet when captured */
void
process_packet (u_char *user, const struct pcap_pkthdr *header,
    const u_char * packet)
{
  struct ether_header *eth_header;  /* in ethernet.h included by if_eth.h */
  struct ether_arp *arp_packet; /* from if_eth.h */

  eth_header = (struct ether_header *) packet;
  arp_packet = (struct ether_arp *) (packet + ETH_HEADER_SIZE);

  if (ntohs (eth_header->ether_type) == ETHERTYPE_ARP)  /* if it is an ARP packet */
    {
      printf ("Source: %d.%d.%d.%d\t\tDestination: %d.%d.%d.%d\n",
        arp_packet->arp_spa[0],
        arp_packet->arp_spa[1],
        arp_packet->arp_spa[2],
        arp_packet->arp_spa[3],
        arp_packet->arp_tpa[0],
        arp_packet->arp_tpa[1],
        arp_packet->arp_tpa[2],
        arp_packet->arp_tpa[3]);
    }
}

int
main (int argc, char *argv[])
{
  char o;     /* for option processing */
  char errbuf[PCAP_ERRBUF_SIZE];  /* pcap error messages buffer */
  struct pcap_pkthdr header;  /* packet header from pcap */
  const u_char *packet;   /* packet */
  bpf_u_int32 netp;   /* ip address of interface */
  bpf_u_int32 maskp;    /* subnet mask of interface */
  char *filter = "arp";   /* filter for BPF (human readable) */
  struct bpf_program fp;  /* compiled BPF filter */
  int r;      /* generic return value */
  pcap_if_t *alldevsp;    /* list of interfaces */

  while ((o = getopt (argc, argv, "i:vl")) > 0)
    {
      switch (o)
  {
  case 'i':
    device = optarg;
    break;
  case 'l':
    if (pcap_findalldevs (&alldevsp, errbuf) < 0)
      {
        fprintf (stderr, "%s", errbuf);
        exit (1);
      }
    while (alldevsp != NULL)
      {
        printf ("%s\n", alldevsp->name);
        alldevsp = alldevsp->next;
      }
    exit (0);
  case 'v':
    verbose = 1;
    break;
  default:
    usage (argv[0]);
    break;
  }
    }

  /* setup signal handler so Control-C will gracefully exit */
  signal (SIGINT, ctrl_c);

  /* find device for sniffing if needed */
  if (device == NULL)   /* if user hasn't specified a device */
    {
      device = pcap_lookupdev (errbuf); /* let pcap find a compatible device */
      if (device == NULL) /* there was an error */
  {
    fprintf (stderr, "%s", errbuf);
    exit (1);
  }
    }

  /* set errbuf to 0 length string to check for warnings */
  errbuf[0] = 0;

  /* open device for sniffing */
  handle = pcap_open_live (device,  /* device to sniff on */
         BUFSIZ,  /* maximum number of bytes to capture per packet */
                  /* BUFSIZE is defined in pcap.h */
         1, /* promisc - 1 to set card in promiscuous mode, 0 to not */
         0, /* to_ms - amount of time to perform packet capture in milliseconds */
            /* 0 = sniff until error */
         errbuf); /* error message buffer if something goes wrong */

  if (handle == NULL)   /* there was an error */
    {
      fprintf (stderr, "%s", errbuf);
      exit (1);
    }

  if (strlen (errbuf) > 0)
    {
      fprintf (stderr, "Warning: %s", errbuf);  /* a warning was generated */
      errbuf[0] = 0;    /* re-set error buffer */
    }

  if (verbose)
    {
      printf ("Using device: %s\n", device);
      /* printf ("Using libpcap version %s", pcap_lib_version); */
    }
  /* find out the datalink type of the connection */
  if (pcap_datalink (handle) != DLT_EN10MB)
    {
      fprintf (stderr, "This program only supports Ethernet cards!\n");
      exit (1);
    }

  /* get the IP subnet mask of the device, so we set a filter on it */
  if (pcap_lookupnet (device, &netp, &maskp, errbuf) == -1)
    {
      fprintf (stderr, "%s", errbuf);
      exit (1);
    }

  /* compile the filter, so we can capture only stuff we are interested in */
  if (pcap_compile (handle, &fp, filter, 0, maskp) == -1)
    {
      fprintf (stderr, "%s", pcap_geterr (handle));
      exit (1);
    }

  /* set the filter for the device we have opened */
  if (pcap_setfilter (handle, &fp) == -1)
    {
      fprintf (stderr, "%s", pcap_geterr (handle));
      exit (1);
    }

  /* we'll be nice and free the memory used for the compiled filter */
  pcap_freecode (&fp);

  if ((r = pcap_loop (handle, -1, process_packet, NULL)) < 0)
    {
      if (r == -1)    /* pcap error */
  {
    fprintf (stderr, "%s", pcap_geterr (handle));
    exit (1);
  }
      /* otherwise return should be -2, meaning pcap_breakloop has been called */
    }

  /* close our devices */
  pcap_close (handle);
}

Example 10-3 shows a sample run of the Arpsniff tool, capturing the IP address ranges in use on this network by capturing ARP packets.

Example 10-3. Sample run of the Arpsniff tool
clarkju@home$ sudo arpsniff
Source: 192.168.0.123   Destination: 192.168.0.1
Source: 192.168.0.1     Destination: 192.168.0.123
Source: 192.168.0.123   Destination: 192.168.0.101
Source: 192.168.0.101   Destination: 192.168.0.123
Source: 192.168.0.123   Destination: 192.168.0.138
Source: 192.168.0.138   Destination: 192.168.0.123
Source: 192.168.0.138   Destination: 192.168.0.123
Source: 192.168.0.123   Destination: 192.168.0.138

    Team LiB
    Previous Section Next Section