struct AVS_header
{
uint32 version;
uint32 length;
uint64 mactime;
uint64 hosttime;
uint32 phytype;
uint32 channel;
uint32 datarate;
uint32 antenna;
uint32 priority;
uint32 ssi_type;
int32 ssi_signal;
int32 ssi_noise;
uint32 preamble;
uint32 encoding;
};
Most recent wireless drivers that are capable of entering monitor
mode support either a Prism monitor mode header or the AVS capture
header, or both. Where possible you should use the AVS capture
format, as this is better documented (i.e., it
is documented, period) and is designed to be
extensible to support newer technologies.
10.3.2. Adapting Arpsniff to 802.11
To adapt Arpsniff to capture information
from a wireless packet-capture source, we need to make a few changes
to the application logic. We assume the wireless device used in this
example supports the AVS wireless capture format.
First of all, we need to specify the sizes of some of the additional
frames captured:
/* ugly shortcuts - Defining our header types */
#define ETH_HEADER_SIZE 14
#define AVS_HEADER_SIZE 64 /* AVS capture header size */
#define DATA_80211_FRAME_SIZE 24 /* header for 802.11 data packet */
#define LLC_HEADER_SIZE 8 /* LLC frame for encapsulation */
We are specifying additional header sizes because of the additional
headers our ARP packet has when
capturing from a wireless source due to RFC 1042 IP encapsulation, as
shown in Figure 10-4.
Figure 10-4. ARP packet format on 802.11 from an AVS capture source
To determine the type of packet embedded in the 802.11 packet, we
need to have a definition for the LLC header so that we can extract
the ether_type value:
/* SNAP LLC header format */
struct snap_header
{
u_int8_t dsap;
u_int8_t ssap;
u_int8_t ctl;
u_int16_t org;
u_int8_t org2;
u_int16_t ether_type; /* ethernet type */
} _ _attribute_ _ ((_ _packed_ _));
Now we can alter the
process_packet function to work with a captured
802.11 packet from an AVS wireless source:
/* callback function to process a packet when captured */
void
process_packet (u_char * args, const struct pcap_pkthdr *header,
const u_char * packet)
{
struct ether_header *eth_header; /* in ethernet.h included by if_eth.h */
struct snap_header *llc_header; /* RFC 1042 encapsulation header */
struct ether_arp *arp_packet; /* from if_eth.h */
if (wired) /* global flag - wired or wireless? */
{
eth_header = (struct ether_header *) packet;
arp_packet = (struct ether_arp *) (packet + ETH_HEADER_SIZE);
if (ntohs (eth_header->ether_type) != ETHERTYPE_ARP) return;
} else { /* wireless */
llc_header = (struct snap_header *)
(packet + AVS_HEADER_SIZE + DATA_80211_FRAME_SIZE);
arp_packet = (struct ether_arp *)
(packet + AVS_HEADER_SIZE + DATA_80211_FRAME_SIZE + LLC_HEADER_SIZE);
if (ntohs (llc_header->ether_type) != ETHERTYPE_ARP) return;
}
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]);
}
You might have noticed that we have introduced a global flag called
wired which we are going to use to determine a
packet's framing type. We will set this further down
in Arpsniff when we check the link type using
pcap_datalink:
/* find out the datalink type of the connection */
if (pcap_datalink (handle) == DLT_EN10MB)
{
wired = 1; /* ethernet link */
} else {
if (pcap_datalink (handle) == DLT_IEEE802_11_RADIO_AVS)
{
wired = 0; /* wireless */
} else {
fprintf (stderr, "I don't support this interface type!\n");
exit (1);
}
}
Once we have made the preceding changes, Arpsniff is ready to capture
ARP packets from a wireless network interface in monitor mode. The
full source code for the updated version of Arpsniff is included in
Example 10-4.
Example 10-4. Arpsniff2 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
#define AVS_HEADER_SIZE 64 /* AVS capture header size */
#define DATA_80211_FRAME_SIZE 24 /* header for 802.11 data packet */
#define LLC_HEADER_SIZE 8 /* LLC frame for encapsulation */
/* SNAP LLC header format */
struct snap_header
{
u_int8_t dsap;
u_int8_t ssap;
u_int8_t ctl;
u_int16_t org;
u_int8_t org2;
u_int16_t ether_type; /* ethernet type */
} __attribute__ ((__packed_ _));
/* 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 */
int wired=0; /* flag for wired/wireless */
/* 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 * args, const struct pcap_pkthdr *header,
const u_char * packet)
{
struct ether_header *eth_header; /* in ethernet.h included by if_eth.h */
struct snap_header *llc_header; /* RFC 1042 encapsulation header */
struct ether_arp *arp_packet; /* from if_eth.h */
if (wired) /* global flag - wired or wireless? */
{
eth_header = (struct ether_header *) packet;
arp_packet = (struct ether_arp *) (packet + ETH_HEADER_SIZE);
if (ntohs (eth_header->ether_type) != ETHERTYPE_ARP) return;
} else { /* wireless */
llc_header = (struct snap_header *)
(packet + AVS_HEADER_SIZE + DATA_80211_FRAME_SIZE);
arp_packet = (struct ether_arp *)
(packet + AVS_HEADER_SIZE + DATA_80211_FRAME_SIZE + LLC_HEADER_SIZE);
if (ntohs (llc_header->ether_type) != ETHERTYPE_ARP) return;
}
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)
{
wired = 1; /* ethernet link */
} else {
if (pcap_datalink (handle) == DLT_IEEE802_11_RADIO_AVS)
{
wired = 0; /* wireless */
} else {
fprintf (stderr, "I don't support this interface type!\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);
}
If the wireless network is using encryption, we are not going to be
able to intercept all traffic in a readable format. Unfortunately, we
cannot be in monitor mode and have the wireless card decrypting data
for us, so any data requiring decryption should be captured while not
in monitor mode, or else the tool will have to implement decryption
for the
captured
data.