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:
#include "pcap.h"
typedef struct ip_address{
u_char byte1;
u_char byte2;
u_char byte3;
u_char byte4;
}ip_address;
typedef struct ip_header{
u_char ver_ihl;
u_char tos;
u_short tlen;
u_short identification;
u_short flags_fo;
u_char ttl;
u_char proto;
u_short crc;
ip_address saddr;
ip_address daddr;
u_int op_pad;
}ip_header;
typedef struct udp_header{
u_short sport;
u_short dport;
u_short len;
u_short crc;
}udp_header;
void packet_handler(u_char *param,
const struct pcap_pkthdr *header,
const u_char *pkt_data);
int main()
{
int inum;
int i=0;
u_int netmask;
char packet_filter[] = "ip and udp";
struct bpf_program fcode;
{
fprintf(stderr,"Error in pcap_findalldevs: %s\n", errbuf);
exit(1);
}
for(d=alldevs; d; d=d->
next)
{
printf(
"%d. %s", ++i, d->
name);
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");
return -1;
}
for(d=alldevs, i=0; i< inum-1 ;d=d->
next, i++);
65536,
1000,
NULL,
errbuf
) ) == NULL)
{
fprintf(stderr,"\nUnable to open the adapter. %s is not supported by WinPcap\n");
return -1;
}
{
fprintf(stderr,"\nThis program works only on Ethernet networks.\n");
return -1;
}
else
netmask=0xffffff;
if (
pcap_compile(adhandle, &fcode, packet_filter, 1, netmask) <0 )
{
fprintf(stderr,"\nUnable to compile the packet filter. Check the syntax.\n");
return -1;
}
{
fprintf(stderr,"\nError setting the filter.\n");
return -1;
}
pcap_loop(adhandle, 0, packet_handler, NULL);
return 0;
}
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;
time_t local_tv_sec;
(VOID)(param);
local_tv_sec = header->
ts.tv_sec;
localtime_s(<ime, &local_tv_sec);
strftime( timestr, sizeof timestr, "%H:%M:%S", <ime);
printf(
"%s.%.6d len:%d ", timestr, header->
ts.tv_usec, header->
len);
ih = (ip_header *) (pkt_data +
14);
ip_len = (ih->ver_ihl & 0xf) * 4;
uh = (udp_header *) ((u_char*)ih + ip_len);
sport = ntohs( uh->sport );
dport = ntohs( uh->dport );
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);
}
#define PCAP_OPENFLAG_PROMISCUOUS
Defines if the adapter has to go in promiscuous mode.
#define PCAP_SRC_IF_STRING
String that will be used to determine the type of source in use (file, remote/local interface).
struct pcap_if pcap_if_t
Item in a list of interfaces, see pcap_if.
struct pcap pcap_t
Descriptor of an open capture instance. This structure is opaque to the user, that handles its conten...
#define PCAP_ERRBUF_SIZE
Size to use when allocating the buffer that contains the libpcap errors.
pcap_t * pcap_open(const char *source, int snaplen, int flags, int read_timeout, struct pcap_rmtauth *auth, char *errbuf)
Open a generic source in order to capture / send (WinPcap only) traffic.
void pcap_freealldevs(pcap_if_t *alldevsp)
Free an interface list returned by pcap_findalldevs().
int pcap_compile(pcap_t *p, struct bpf_program *fp, char *str, int optimize, bpf_u_int32 netmask)
Compile a packet filter, converting an high level filtering expression (see Filtering expression synt...
int pcap_datalink(pcap_t *p)
Return the link layer of an adapter.
int pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
Collect a group of packets.
int pcap_findalldevs_ex(char *source, struct pcap_rmtauth *auth, pcap_if_t **alldevs, char *errbuf)
Create a list of network devices that can be opened with pcap_open().
int pcap_setfilter(pcap_t *p, struct bpf_program *fp)
Associate a filter to a capture.
struct sockaddr * netmask
if not NULL, a pointer to a struct sockaddr that contains the netmask corresponding to the address po...
struct pcap_addr * addresses
a pointer to the first element of a list of addresses for the interface
char * name
a pointer to a string giving a name for the device to pass to pcap_open_live()
struct pcap_if * next
if not NULL, a pointer to the next element in the list; NULL for the last element of the list
char * description
if not NULL, a pointer to a string giving a human-readable description of the device
Header of a packet in the dump file.
struct timeval ts
time stamp
bpf_u_int32 len
length this packet (off wire)
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:
Each of the final 3 lines represents a different packet.