分类: 系统运维
2005-10-11 23:58:49
Getting Started
Well there is an awful lot to cover.. so lets just get familiar
with libpcap. Like I stated before, all the code in this
section is assuming that you are sitting on an Ethernet. If this
is not the case, then the tutorial basics are still pertinent, but
the code presented later on involving decoding the Ethernet header
obviously isn't :-( *sorry*. Allright... crack your knuckles *crunch*
and lets get ready to code our FIRST LIBPCAP PROGRAM!!!!.
Go ahead and copy the following program into your
favorite editor (which should be vim if you have any sense :-)
save, and compile with...
%>gcc ldev.c -lpcap
/* ldev.c
Martin Casado
To compile:
>gcc ldev.c -lpcap
Looks for an interface, and lists the network ip
and mask associated with that interface.
*/
#include
#include
#include/* GIMME a libpcap plz! */
#include
#include
#include
#include
int main(int argc, char **argv)
{
char *dev; /* name of the device to use */
char *net; /* dot notation of the network address */
char *mask;/* dot notation of the network mask */
int ret; /* return code */
char errbuf[PCAP_ERRBUF_SIZE];
bpf_u_int32 netp; /* ip */
bpf_u_int32 maskp;/* subnet mask */
struct in_addr addr;
/* ask pcap to find a valid device for use to sniff on */
dev = pcap_lookupdev(errbuf);
/* error checking */
if(dev == NULL)
{
printf("%s ",errbuf);
exit(1);
}
/* print out device name */
printf("DEV: %s ",dev);
/* ask pcap for the network address and mask of the device */
ret = pcap_lookupnet(dev,&netp,&maskp,errbuf);
if(ret == -1)
{
printf("%s ",errbuf);
exit(1);
}
/* get the network address in a human readable form */
addr.s_addr = netp;
net = inet_ntoa(addr);
if(net == NULL)/* thanks Scott :-P */
{
perror("inet_ntoa");
exit(1);
}
printf("NET: %s ",net);
/* do the same as above for the device's mask */
addr.s_addr = maskp;
mask = inet_ntoa(addr);
if(mask == NULL)
{
perror("inet_ntoa");
exit(1);
}
printf("MASK: %s ",mask);
return 0;
}
Did you run the program? If not, run it :-) Assuming it compiled,
and ran correctly your output should be something like...
DEV: eth0
NET: 192.168.12.0
MASK: 255.255.255.0
Now if your DEV is not eth0, or eth1 or eth followed by some
number then we are going to have problems because this document
is geared toward sniffing ethernet packets. Obviously the NET and
MASK numbers will be different than the ones I posted, however
the actual values are not important to this discussion.
"So what did we just do?", you ask. Well, we just asked libpcap
to give us some specs on an interface to listen on.
"Whats an interface?"
Just think of an interface as your computers hardware connection
to whatever network your computer is connected to. In Unix, eth0
denotes the first ethernet card in your computer this is the network
interface that I am going to use to demonstrate libpcap. All you
really have to be concerned with right now is that we grabbed the
device name "eth0", since this is what we have to pass to libpcap
to tell where to grab packets from. The NET and MASK are simply the
network number and mask associated with the card which are for
informative purposes only. There are much better ways to enumerate
and list the specifications of the system interfaces than going through
libpcap which I'll hopefully write about someday :-).
Allright, by now you should know how to write, run and compile
a libpcap program, grab the name of the interface card we are going
to capture packets from, and have a basic understanding of what we
are doing. Next, we'll grab our very first packet.. WohoO!!!
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..
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
#include
#include/* if this gives you an error try pcap/pcap.h */
#include
#include
#include
#include
#include/* 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 ",errbuf);
exit(1);
}
printf("DEV: %s ",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 ",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 ");
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 ",hdr.len);
printf("Recieved at ..... %s ",ctime((const time_t*)&hdr.ts.tv_sec));
printf("Ethernet address length is %d ",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 ",
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 ",
ntohs(eptr->ether_type),
ntohs(eptr->ether_type));
}else {
printf("Ethernet type %x not IP", ntohs(eptr->ether_type));
exit(1);
}
/* THANK YOU RICHARD STEVENS!!! RIP*/
ptr = eptr->ether_dhost;
i = ETHER_ADDR_LEN;
printf(" Destination Address: ");
do{
printf("%s%x",(i == ETHER_ADDR_LEN) ? " " : ":",*ptr++);
}while(--i>0);
printf(" ");
ptr = eptr->ether_shost;
i = ETHER_ADDR_LEN;
printf(" Source Address: ");
do{
printf("%s%x",(i == ETHER_ADDR_LEN) ? " " : ":",*ptr++);
}while(--i>0);
printf(" ");
return 0;
}
Well, that wasn't too bad was it?! Lets give her a test run ..
[root@pepe libpcap]# ./a.outAfter typing a.out I jumped into another terminal and tried to ping The output captured the ICMP packet used to ping 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 do you!?
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]#
[root@pepe libpcap]# /sbin/routeand then use the arpcommand to get the cached ethernet address...
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 eth0
[root@pepe libpcap]# /sbin/arpIf your gateway is not in your arp cache, try and telnet to it, and then retry the arp command. Hey, by the way, this could certainly be the long, painful, bloody, ignorant way of getting the gateway hardware address but I couldn't think of another way...
Address HWtype HWaddress Flags Mask Iface
192.168.1.1 ether 00:20:78:D1:E8:01 C eth0
Notice that my gateway's address matches the destination address of the packet that I captured. All packets leaving my machine that are not sent to a machine on my network must go through the gateway. Alas!!!! We have still not answered the question... "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 and is certainly savy enough to send outbound packets to it. 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. "AAAUUGHH!!! BUT HOW DID IT CONSTUCT THE ARP CACHE!!!!", you scream!
Hardware addresses on ethernet are obtained using the Address Resolution Protocol or ARP. ARP is is described in RFC826 which can be found... Pretty much what happenes is when you send a packet, the kernel first checks the arp cache to see if you already have the hardware address for the higher level destination address. If not, the kernel sends an arp request which is of type... ETHERTYPE_ARP which is defined in net/ethernet.h as follows.
#define ETHERTYPE_ARP 0x0806 /* Address resolution */On recieveing the arp packet, the machine with the high level address (in my case the gateway) will reply with an arp reply, basically saying.. I DO! send it here! Shall we test it out?! (to bad... I'm gonna do it anyways :-P)
[root@pepe libpcap]# /sbin/arp -n # look at arp cacheSo 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~~~~
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]#echo YAY
Lets now disect the packet by checking out
/* 10Mb/s ethernet header */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
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__));
/* Ethernet protocol ID's */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.
#define ETHERTYPE_PUP 0x0200 /* Xerox PUP */
#define ETHERTYPE_IP 0x0800 /* IP */
#define ETHERTYPE_ARP 0x0806 /* Address resolution */
#define ETHERTYPE_REVARP 0x8035 /* Reverse ARP */
Ack! 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 sad, sad 1% 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 (eww, that kinda sounds gross) later on.