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);
}
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:
- \Device\Packet_{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 >>>