Chinaunix首页 | 论坛 | 博客
  • 博客访问: 312248
  • 博文数量: 94
  • 博客积分: 2220
  • 博客等级: 大尉
  • 技术积分: 975
  • 用 户 组: 普通用户
  • 注册时间: 2004-12-17 21:17
文章分类

全部博文(94)

文章存档

2011年(5)

2010年(11)

2009年(1)

2008年(2)

2006年(1)

2005年(65)

2004年(9)

我的朋友

分类: 系统运维

2005-10-11 23:58:49

Intro: Well here it is, the beginning of my packet capture tutorial a la libpcap. Inevitably the questions will arise.. "what the hell is packet capture?!" or "Who is libpcap!?" ... so I guess I'll start off by answering these questions...

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!!!

Capturing Our First Packet


(P.S. Hey peeps, sorry this was such a long time coming... graduating and getting a job is a major pain in the ass... as things settle down I will certainly have more time to work on this :-) :-) :-) )

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.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 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!?
"..uhhh of course not",you stammer
The destination address is most likely your gateway... aka the computer that ties your network to the internet. The packet must first find its way to your gateway which will then forward it to a router or make its own routing decisions as to where the packet should go next. Lets do a quick sanity check to see if we in fact are sending to the router.... ho hum!! You can use the route command to get your gateways IP.
[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 eth0
and then use the arpcommand to get the cached ethernet address...
[root@pepe libpcap]# /sbin/arp 
Address HWtype HWaddress Flags Mask Iface
192.168.1.1 ether 00:20:78:D1:E8:01 C eth0
If 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...

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 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]#echo YAY
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 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.

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.


阅读(1764) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~