免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1356 | 回复: 0
打印 上一主题 下一主题

Packet Capture With libpcap (一) [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2005-10-11 23:58 |只看该作者 |倒序浏览
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..
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
#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 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!?
"..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...  
Here!
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.


本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/2939/showart_51526.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP