Building.Open.Source.Network.Security.Tools.Components.And.Techniques [Electronic resources]

Mike D. Schiffman

نسخه متنی -صفحه : 135/ 26
نمايش فراداده

Sample Code—Stroke

The following three source files comprise the Stroke codebase. The 5500 line oui.h headerfile is abridged for obvious reasons. To preserve readability, the code is richly commented but no book-text appears inside the code. You can download the full source files from this book's companion Web site at http://www.wiley.com/compbooks/schiffman.

oui.h

/*
* $Id: oui.h,v 1.1.1.1 2002/27/04 00:16:48 route Exp $
*
* Building Open Source Network Security Tools
* oui.h - pcap example code
*
* Copyright (c) 2002 Mike D. Schiffman <mike@infonexus.com>
* All rights reserved.
*
*/
struct oui
{
u_char prefix[3];     /* 24 bit global prefix */
char *vendor;         /* vendor id string */
};
struct oui oui_table[] = {
{ { 0x00, 0x00, 0x01 }, "XEROX CORPORATION" },
{ { 0x00, 0x00, 0x02 }, "XEROX CORPORATION" },
/* about 5400 lines cut for readability */
{ { 0xAA, 0x00, 0x04 }, "DIGITAL EQUIPMENT CORPORATION" },
{ { 0x00, 0x00, 0x00 }, " }
};
/* EOF */

stroke.h

/*
* $Id: stroke.h,v 1.1.1.1 2001/11/29 00:16:48 route Exp $
*
* Building Open Source Network Security Tools
* stroke.h - pcap example code
*
* Copyright (c) 2002 Mike D. Schiffman <mike@infonexus.com>
* All rights reserved.
*
*/
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <pcap.h>
#include <signal.h>
#include "./oui.h"
#define SNAPLEN         34
#define PROMISC         1
#define TIMEOUT         500
#define FILTER          "ip"
#define HASH_TABLE_SIZE 1009     /* should be tunable to network size */
struct table_entry
{
u_char mac[6];              /* holds the MAC address */
struct table_entry *next;   /* pointer to the next entry */
};
const char *b_search(u_char *);
char *eprintf(u_char *);
char *iprintf(u_char *);
int interesting(u_char *, struct table_entry **);
int ht_dup_check(u_char *, struct table_entry **, int);
int ht_add_entry(u_char *, struct table_entry **, int);
u_long ht_hash(u_char *);
void ht_int_table(struct table_entry **);
void cleanup(int);
int catch_sig(int, void(*)());
/* EOF */

stroke.c

/*
* $Id: stroke.c,v 1.1.1.1 2001/11/29 00:16:48 route Exp $
*
* Building Open Source Network Security Tools
* stroke.c - pcap example code
*
* Copyright (c) 2002 Mike D. Schiffman <mike@infonexus.com>
* All rights reserved.
*
*/
#include "./stroke.h"
int loop = 1;
u_long mac = 0;
int
main(int argc, char **argv)
{
int c;
pcap_t *p;                    /* pcap descriptor */
char *device;                 /* network interface to use */
u_char *packet;
int print_ip;
struct pcap_pkthdr h;
struct pcap_stat ps;
char errbuf[PCAP_ERRBUF_SIZE];
struct bpf_program filter_code;
bpf_u_int32 local_net, netmask;
struct table_entry *hash_table[HASH_TABLE_SIZE];
device = NULL;
print_ip = 0;
while ((c = getopt(argc, argv, "Ii:")) != EOF)
{
switch (c)
{
case ’I’:
print_ip = 1;
break;
case ’i’:
device = optarg;
break;
default:
exit(EXIT_FAILURE);
}
}
printf("Stroke 1.0 [passive MAC -> OUI mapping tool]\n");
/*
* If device is NULL, that means the user did not specify one and
* is leaving it up libpcap to find one.
*/
if (device == NULL)
{
device = pcap_lookupdev(errbuf);
if (device == NULL)
{
fprintf(stderr, "pcap_lookupdev() failed: %s\n", errbuf);
exit(EXIT_FAILURE);
}
}
/*
* Open the packet capturing device with the following values:
*
* SNAPLEN: 34 bytes
* We only need the 14 byte ethernet header and possibly an IP
* header if the user specified ‘-I’ at the command line.
* PROMISC: on
* The interface needs to be in promiscuous mode to capture all
* network traffic on the localnet.
* TIMEOUT: 500ms
* A 500 ms timeout is probably fine for most networks. For
* architectures that support it, you might want tune this value
* depending on how much traffic you're seeing on the network.
*/
p = pcap_open_live(device, SNAPLEN, PROMISC, TIMEOUT, errbuf);
if (p == NULL)
{
fprintf(stderr, "pcap_open_live() failed: %s\n", errbuf);
exit(EXIT_FAILURE);
}
/*
* Set the BPF filter. We're only interested in IP packets so we
* can ignore all others.
*/
if (pcap_lookupnet(device, &local_net, &netmask, errbuf) == -1)
{
fprintf(stderr, "pcap_lookupnet() failed: %s\n", errbuf);
pcap_close(p);
exit(EXIT_FAILURE);
}
if (pcap_compile(p, &filter_code, FILTER, 1, netmask) == -1)
{
fprintf(stderr, "pcap_compile() failed: %s\n", pcap_geterr(p));
pcap_close(p);
exit(EXIT_FAILURE);
}
if (pcap_setfilter(p, &filter_code) == -1)
{
fprintf(stderr, "pcap_setfilter() failed: %s\n",
pcap_geterr(p));
pcap_close(p);
exit(EXIT_FAILURE);
}
/*
* We need to make sure this is Ethernet. The DLTEN10MB specifies
* standard 10MB and higher Ethernet.
*/
if (pcap_datalink(p) != DLT_EN10MB)
{
fprintf(stderr, "Stroke only works with ethernet.\n");
pcap_close(p);
exit(EXIT_FAILURE);
}
/*
* We want to catch the interrupt signal so we can inform the user
* how many packets we captured before we exit. We should probably
* clean up memory and free up the hashtable before we go, but we
* can't always have all the nice things we want, can we?
*/
if (catch_sig(SIGINT, cleanup) == -1)
{
fprintf(stderr, "can't catch signal.\n");
pcap_close(p);
exit(EXIT_FAILURE);
}
/*
* Here we initialize the hash table and start looping. We'll exit
* from the loop only when the user hits ctrl-c and the command
* prompt which will set the loop sentinel variable to 0.
*/
for (ht_init_table(hash_table); loop;)
{
/*
*  pcap_next() gives us the next packet from pcap's internal
*  packet buffer.
*/
packet = (u_char *)pcap_next(p, &h);
if (packet == NULL)
{
/*
* We have to be careful here as pcap_next() can return
* NULL if the timer expires with no data in the packet
* buffer or in some special circumstances with linux.
*/
continue;
}
/*
* Check to see if the packet is from a new MAC address, and if
* so we'll add it to hash table.
*/
if (interesting(packet, hash_table))
{
/*
* The packet's source MAC address is six bytes into the
* packet and the IP address is 26 bytes into the packet.
* We submit the MAC to the binary search function which
* will return the OUI string corresponding to the MAC
* entry.
*/
if (print_ip)
{
printf("%s @ %s -> %s\n", eprintf(packet),
iprintf(packet + 26),
b_search(packet + 6));
}
else
{
printf("%s -> %s\n", eprintf(packet),
b_search(packet + 6));
}
}
}
/*
* If we get here, the user hit ctrl-c at the command prompt and
* it's time to dump the statistics.
*/
if (pcap_stats(p, &ps) == -1)
{
fprintf(stderr, "pcap_stats() failed: %s\n", pcap_geterr(p));
}
else
{
/*
* Remember that the ps statistics changes slightly depending
* on the underlying architecture. We gloss over that here.
*/
printf("\nPackets received by libpcap:\t%6d\n"
"Packets dropped by libpcap:\t%6d\n"
"Unique MAC addresses stored:\t%6ld\n"
ps.ps_recv, ps.ps_drop, mac);
}
/*
* This can fail but since we're exiting either way, who cares?
*/
pcap_close(p);
return (EXIT_SUCCESS);
}
const char *
b_search(u_char *prefix)
{
struct oui *ent;
int start, end, diff, mid;
start = 0;
end = sizeof(oui_table) / sizeof(oui_table[0]);
/* approximately 0(log n) running time */
while (end > start)
{
mid = (start + end) / 2;
ent = &oui_table[mid];
diff = prefix[0] - ent->prefix[0];
if (diff == 0)
{
/* first byte matches */
diff = prefix[1] - ent->prefix[1];
}
if (diff == 0)
{
/* second byte matches */
diff = prefix[2] - ent->prefix[2];
}
if (diff == 0)
{
/* third byte matches */
return (ent->vendor);
}
if (diff < 0)
{
/* cut the list in half from the front half */
end = mid;
}
else
{
/* cut the list in half from the last half */
start = mid + 1;
}
}
/* no match */
return ("Unknown Vendor");
}
char *
eprintf(u_char *packet)
{
int n;
static char address[18];
n = sprintf(address, "%.2x:", packet[6]);
n += sprintf(address + n, "%.2x:", packet[7]);
n += sprintf(address + n, "%.2x:", packet[8]);
n += sprintf(address + n, "%.2x:", packet[9]);
n += sprintf(address + n, "%.2x:", packet[10]);
n += sprintf(address + n, "%.2x", packet[11]);
address[n] = NULL;
return (address);
}
char *
iprintf(u_char *address)
{
static char ip[17];
/* cheap way to print an IP address */
sprintf(ip, "%3d.%3d.%3d.%3d", (address[0] & 255), (address[1] &
255), (address[2] & 255), (address[3] & 255));
return (ip);
}
int
interesting(u_char *packet, struct table_entry **hash_table)
{
u_long n;
n = ht_hash(packet);
/* check to see if the entry we've hashed to is free or used */
if (hash_table[n])
{
/* check to see if this is a duplicate entry or a collision */
if (!ht_dup_check(packet, hash_table, n))
{
/* this is a collision, let's add a bucket */
if (ht_add_entry(packet, hash_table, n))
{
mac++;
return (1);
}
}
else
{
/* this is a duplicate entry, ignore it */
return (0);
}
}
else
{
/* this table slot is free */
if (ht_add_entry(packet, hash_table, n))
{
mac++;
return (1);
}
}
/* if we've gotten here an error has occurred, which we duly ignore */
return (0);
}
int
ht_dup_check(u_char *packet, struct table_entry **hash_table, int loc)
{
struct table_entry *p;
for (p = hash_table[loc]; p; p = p->next)
{
if (p->mac[0] == packet[6] && p->mac[1] == packet[7] &&
p->mac[2] == packet[8] && p->mac[3] == packet[9] &&
p->mac[4] == packet[10] && p->mac[5] == packet[11])
{
/* this MAC is already in our table */
return (1);
}
}
/* this MAC has collided with another entry in our table */
return (0);
}
int
ht_add_entry(u_char *packet, struct table_entry **hash_table, int loc)
{
struct table_entry *p;
if (hash_table[loc] == NULL)
{
/* this is the first entry in this location in the table */
hash_table[loc] = malloc(sizeof(struct table_entry));
if (hash_table[loc] == NULL)
{
return(0);
}
hash_table[loc]->mac[0] = packet[6];
hash_table[loc]->mac[1] = packet[7];
hash_table[loc]->mac[2] = packet[8];
hash_table[loc]->mac[3] = packet[9];
hash_table[loc]->mac[4] = packet[10];
hash_table[loc]->mac[5] = packet[11];
hash_table[loc]->next = NULL;
return (1);
}
else
{
/* this is a chain, find the end of it */
for (p = hash_table[loc]; p- next; p = p- next);
p-> next = malloc(sizeof(struct table_entry));
if (p-> next == NULL)
{
return (0);
}
p = p-> next;
p-> mac[0] = packet[6];
p-> mac[1] = packet[7];
p-> mac[2] = packet[8];
p-> mac[3] = packet[9];
p-> mac[4] = packet[10];
p-> mac[5] = packet[11];
p-> next = NULL;
}
return (1);
}
u_long
ht_hash(u_char *packet)
{
int i;
u_long j;
for (i = 6, j = 0; i != 12; i++)
{
/* decent amount of entropy */
j = (j * 13) + packet[i];
}
return (j %= HASH_TABLE_SIZE);
}
void
ht_init_table(struct table_entry **hash_table)
{
int c;
for (c = 0; c < HASH_TABLE_SIZE; C++)
{
hash_table[c] = NULL;
}
}
void
cleanup (int signo)
{
loop = 0;
printf("Interrupt signal caught…\n");
}
int
catch_sig(int signo, void (*handler)())
{
struct sigaction action;
action.sa_handler = handler;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
if (sigaction(signo, &action, NULL) == -1)
{
return (-1);
}
else return (1);
}
/* EOF */