Well now we sort of know the nature of packet capture, we have
identified that we do in fact have an interface to pull things from, how
about we go ahead and grab a packet!
"Just give me the damn example
and let me hack...", you cry
Very well..... Here you go.. download
from here.. testpcap1.c or just cut and paste
below.
/*************************************************** * file: testpcap1.c * Date: Thu Mar 08 17:14:36 MST 2001 * Author: Martin Casado * Location: LAX Airport (hehe) * * Simple single packet capture program *****************************************************/ #include <stdio.h> #include <stdlib.h> #include <pcap.h> /* if this gives you an error try pcap/pcap.h */ #include <errno.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netinet/if_ether.h> /* includes net/ethernet.h */ int main(int argc, char **argv) { int i; char *dev; char errbuf[PCAP_ERRBUF_SIZE]; pcap_t* descr; const u_char *packet; struct pcap_pkthdr hdr; /* pcap.h */ struct ether_header *eptr; /* net/ethernet.h */ u_char *ptr; /* printing out hardware header info */ /* grab a device to peak into... */ dev = pcap_lookupdev(errbuf); if(dev == NULL) { printf("%s\n",errbuf); exit(1); } printf("DEV: %s\n",dev); /* open the device for sniffing. pcap_t *pcap_open_live(char *device,int snaplen, int prmisc,int to_ms, char *ebuf) snaplen - maximum size of packets to capture in bytes promisc - set card in promiscuous mode? to_ms - time to wait for packets in miliseconds before read times out errbuf - if something happens, place error string here Note if you change "prmisc" param to anything other than zero, you will get all packets your device sees, whether they are intendeed for you or not!! Be sure you know the rules of the network you are running on before you set your card in promiscuous mode!! */ descr = pcap_open_live(dev,BUFSIZ,0,-1,errbuf); if(descr == NULL) { printf("pcap_open_live(): %s\n",errbuf); exit(1); } /* grab a packet from descr (yay!) u_char *pcap_next(pcap_t *p,struct pcap_pkthdr *h) so just pass in the descriptor we got from our call to pcap_open_live and an allocated struct pcap_pkthdr */ packet = pcap_next(descr,&hdr); if(packet == NULL) {/* dinna work *sob* */ printf("Didn't grab packet\n"); exit(1); } /* struct pcap_pkthdr { struct timeval ts; time stamp bpf_u_int32 caplen; length of portion present bpf_u_int32; lebgth this packet (off wire) } */ printf("Grabbed packet of length %d\n",hdr.len); printf("Recieved at ..... %s\n",ctime((const time_t*)&hdr.ts.tv_sec)); printf("Ethernet address length is %d\n",ETHER_HDR_LEN); /* lets start with the ether header... */ eptr = (struct ether_header *) packet; /* Do a couple of checks to see what packet type we have..*/ if (ntohs (eptr->ether_type) == ETHERTYPE_IP) { printf("Ethernet type hex:%x dec:%d is an IP packet\n", ntohs(eptr->ether_type), ntohs(eptr->ether_type)); }else if (ntohs (eptr->ether_type) == ETHERTYPE_ARP) { printf("Ethernet type hex:%x dec:%d is an ARP packet\n", ntohs(eptr->ether_type), ntohs(eptr->ether_type)); }else { printf("Ethernet type %x not IP", ntohs(eptr->ether_type)); exit(1); } /* copied from Steven's UNP */ ptr = eptr->ether_dhost; i = ETHER_ADDR_LEN; printf(" Destination Address: "); do{ printf("%s%x",(i == ETHER_ADDR_LEN) ? " " : ":",*ptr++); }while(--i>0); printf("\n"); ptr = eptr->ether_shost; i = ETHER_ADDR_LEN; printf(" Source Address: "); do{ printf("%s%x",(i == ETHER_ADDR_LEN) ? " " : ":",*ptr++); }while(--i>0); printf("\n"); return 0; }
Well, that wasn't too bad was it?! Lets give her a test run ..
[root@pepe libpcap]# ./a.out DEV: eth0 Grabbed packet of length 76 Recieved at time..... Mon Mar 12 22:23:29 2001 Ethernet address length is 14 Ethernet type hex:800 dec:2048 is an IP packet Destination Address: 0:20:78:d1:e8:1 Source Address: 0:a0:cc:56:c2:91 [root@pepe libpcap]#After typing a.out I jumped into another terminal and tried to ping www.google.com. The output captured the ICMP packet used to ping www.google.com. If you don't know exactly what goes on under the covers of a network you may be curios how the computer obtained the destination ethernet address. Aha! You don't actually think that the destination address of the ethernet packet is the same as the machine at www.google.com do you!?
[root@pepe libpcap]# /sbin/route Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 192.168.1.0 * 255.255.255.0 U 0 0 0 eth0 127.0.0.0 * 255.0.0.0 U 0 0 0 lo default 192.168.1.1 0.0.0.0 UG 0 0 0 eth0we can then use the arpcommand determine the hardware address for 192.168.1.1.
[root@pepe libpcap]# /sbin/arp Address HWtype HWaddress Flags Mask Iface 192.168.1.1 ether 00:20:78:D1:E8:01 C eth0If your gateway is not in your arp cache, try and ping it, and then retry the arp command. The point is this, in order for your computer to send the packet it must first get the MAC address of the next hop (00:20:78:D1:E8:01 for my network).
An obvious follow-up question is, "how did my computer know the gateway hardware address"? Let me then digress for a moment. My computer knows the IP address of the gateway. As you can see from the handy-dandy arp command there is an internal table (the arp cache) which maps IP addresses to hardware addresses.
Hardware addresses on ethernet are obtained using the Address Resolution Protocol or ARP. ARP is is described in RFC826 which can be found... Here! It works as follows. If my computer wants to know the hardware address for the computer with IP 1.2.3.4, it sends and ARP request packet to Ethernet broadcast out of the Interface which 1.2.3.4. as attached. All computers connected to this interface (including 1.2.3.4) should recevie the packet and process the requests. However, only 1.2.3.4 should issue a reply which will contain its Ethernet address. On receipt of the reply, my computer will "cache" out the hardware address for all subsequent packets sent to 1.2.3.4 (until the cache entry times out). ARP packets are of Thernet type... ETHERTYPE_ARP which is defined in net/ethernet.h as follows.
#define ETHERTYPE_ARP 0x0806 /* Address resolution */You can force an Ethernet ARP request by clearing your computer's ARP cache. Below I do this, and then run the above program again to grab the outgoing ARP request.
[root@pepe libpcap]# /sbin/arp -n # look at arp cache Address HWtype HWaddress Flags Mask Iface 192.168.1.1 ether 00:20:78:D1:E8:01 C eth0 [root@pepe libpcap]# /sbin/arp -n -d 192.168.1.1 #delete gateqay entrance [root@pepe libpcap]# /sbin/arp -n #make sure gateway hardware addy is empty Address HWtype HWaddress Flags Mask Iface 192.168.1.1 (incomplete) eth0 [root@pepe libpcap]# ./a.out DEV: eth0 Grabbed packet of length 42 Recieved at time..... Tue Mar 13 00:36:49 2001 Ethernet address length is 14 Ethernet type hex:806 dec:2054 is an ARP packet Destination Address: ff:ff:ff:ff:ff:ff Source Address: 0:a0:cc:56:c2:91 [root@pepe libpcap]So as you can see, once the hardware address was removed the the cache, my computer needed to send an arp request to broadcast (i.e. ff:ff:ff:ff:ff:ff) looking for the owner of the higher level address, in this case IP 192.168.1.1. What do you think would happen if you cleared your arp cache and modified testpcap1.c to capture 2 packets?! Hey I know why don't you try it :-P~~~~
Lets now disect the packet by checking out <net/ethernet.h> right now we are not concerned with the network or transport protocol, we just want to peer into the ethernet headers.... Lets say that we are runnig at 10Mb/s...
/* 10Mb/s ethernet header */ struct ether_header { u_int8_t ether_dhost[ETH_ALEN]; /* destination eth addr */ u_int8_t ether_shost[ETH_ALEN]; /* source ether addr */ u_int16_t ether_type; /* packet type ID field */ } __attribute__ ((__packed__));So it looks like the first ETH_ALEN bytes are the destination ethernet address (look at linux/if_ether.h for the definition of ETH_ALEN :-) of the packet (presumedly your machine). The next ETH_ALEN bytes are the source. Finally, the last word is the packet type. Here are the protocol ID's on my machine from net/ethernet.h
/* Ethernet protocol ID's */ #define ETHERTYPE_PUP 0x0200 /* Xerox PUP */ #define ETHERTYPE_IP 0x0800 /* IP */ #define ETHERTYPE_ARP 0x0806 /* Address resolution */ #define ETHERTYPE_REVARP 0x8035 /* Reverse ARP */For the purpose of this tutorial I will be focusing on IP and perhaps a little bit on ARP... the truth is I have no idea what the hell Xerox PUP is.
Allright so where are we now? We know the most basic of methods for grabbing a packet. We covered how hardware addresses are resolved and what a basic ethernet packet looks like. Still we are using a ver small subset of the functionality of libpcap, and we haven't even begun to peer into the packets themselves (other than the hardware headers) so much to do and so little time :-) As you can probably tell by now, it would be near impossible to do any real protocol analysis with a program that simply captures one packet at a time. What we really want to do is write a simple packet capturing engine that will nab as many packets as possible while filtering out those we dont want. In the next section we will construct a simple packet capturing engine which will aid us in packet dissection later on.