libpcap接收数据包(三)

本文以应用程序的api调用为主线,分析libpcap和pfring源码,当然还有内核PF_RING的源码在以后也会分析,以后可能我会分析从网卡驱动一直分析到应用层,争取把这些都讲清楚。Linux开源就是好,呵呵,闲话少说,继续分析,首先分析pcap_next函数吧,搞过winpcap的都知道这个是数据包的函数,一次只读一个数据包。

constu_char * pcap_next(pcap_t *p, struct pcap_pkthdr *h)

{

       struct oneshot_userdata s;

       const u_char *pkt;

       s.hdr = h;

       s.pkt =&pkt;

       s.pd = p;

       if (pcap_dispatch(p,1, p->oneshot_callback, (u_char *)&s) <= 0)

              return (0);

       return (pkt);

}

 

一看这个函数这么短,大家肯定很开心,呵呵,首先要关注的当然是结构体oneshot_userdata,搜了下,发现在pcap-int.h中定义如下:

/*

 * User data structure forthe one-shot callback used for pcap_next()

 * and pcap_next_ex().

 */

structoneshot_userdata {

       struct pcap_pkthdr *hdr;

       const u_char **pkt;

       pcap_t *pd;

};

 

紧接着就是调用pcap_dispatch函数,这个函数呆会也要讲解的,和pcap_loop 一样,也是用来读取数据包的。下面看看p->oneshot_callback,这个回调函数在哪里定义的呢?还是整个文件搜索。发现在我们以前分析过的函数pcap_create_common函数中的initialize_ops中定义了,所有开始的初始化定义的回调函数是有用的,定义如下:

       p->oneshot_callback = pcap_oneshot;

这就告诉我们,这个回调函数实际上调用的是pcap_oneshot函数;再继续找pcap_oneshot函数吧,定义如下:

pcap_oneshot(u_char*user, const struct pcap_pkthdr *h, const u_char *pkt)

{

       struct oneshot_userdata *sp = (structoneshot_userdata *)user;

 

       *sp->hdr = *h;

       *sp->pkt =pkt;

}

从这里可以看到pcap_oneshot就是pcap_dispatch调用的函数,而user就是pcap_dispatch最后一个参数传过来的。

   分析到这里就有点奇怪了,从pcap_next可以看出,s.pkt= &pkt;传递的一个应用,这个指针指向哪里呢,这里都没有说,那么pcap_oneshot函数的sp->pkt指向什么地方呢?带着这个问题,我们就开始分析pcap_dispatch了,我相信在这个函数中会对s.pkt赋值的,先看看在说吧。

Int pcap_dispatch(pcap_t*p, int cnt, pcap_handler callback, u_char *user)

{

       return p->read_op(p, cnt, callback, user);

}

 

从pcap_dispatch的源码看出,这个函数也相当的简单,函数体里面就一行代码,一个回调函数;在这里,我们带着上面的这个问题去分析read_op回调函数,主要要注意的是那个user参数,看对我们上面说的pkt这个域赋值了吗?搜read_op看在哪个地方定义的;在pcap.c 中的pcap_create_common函数中定义了一个  p->read_op= (read_op_t)pcap_not_initialized;但是我们可以看到明显不是这个回调函数,在前面我们分析了,pcap_create_common函数中的初始化就好像c++里面的构造函数中初始化一样,后面还有可能改变的,所以在继续查找。在pcap_activate_linux中找到了这个函数(handle->read_op =pcap_read_linux;)呵呵,果然如我开始打下的伏笔一样,我说pcap_next,pcap_dispatch,pcap_next_ex,pcap_loop等读包函数都会调用read_op这个回调函数,及pcap_read_linux来读取数据包,其实pcap_read_linux调用的就是pcap_read_packet这个函数了,前面已经分析的很清楚了。源码越往后面分析,就越清晰了。奖励一下自己,先吃点东西在继续,呵呵,现在pcap_next,和pcap_dispatch都分析完了,还有2个函数要分析pcap_next_ex和pcap_loop函数。还忘记上面的那个pkt的问题了,我跟踪pcap_read_packet的代码,在函数的最后就找到callback回调函数了,callback(userdata, (structpcap_pkthdr*)&myhdr, bp);,调用的就是pcap_oneshot这个函数了,呵呵。这个函数可以自定义的。所以pkt,的实参就是bp了,明白这个问题了把,bp在pcap_read_packet已经赋值了(bp = handle->buffer;

 

int pcap_next_ex(pcap_t *p, struct pcap_pkthdr**pkt_header, const u_char **pkt_data)

{

       struct oneshot_userdata s;

       s.hdr = &p->pcap_header;

       s.pkt = pkt_data;

       s.pd = p;

       /* Saves a pointer to the packet headers*/

       *pkt_header= &p->pcap_header;

       if (p->sf.rfile != NULL) {

              int status;

              /* We are on an offline capture */

              status = pcap_offline_read(p, 1,pcap_oneshot, (u_char *)&s);

              /*

               * Return codes for pcap_offline_read() are:

               *  -  0: EOF

               *   --1: error

               *   ->1: OK

               * The first one ('0') conflicts with thereturn code of

               * 0 from pcap_read() meaning "no packetsarrived before

               * the timeout expired", so we map it to-2 so you can

               * distinguish between an EOF from a savefileand a

               * "no packets arrived before the timeoutexpired, try

               * again" from a live capture.

               */

              if (status == 0)

                     return (-2);

              else

                     return (status);

       }

       /*

        * Return codes for pcap_read() are:

        *  -  0: timeout

        *   --1: error

        *   --2: loop was broken out of with pcap_breakloop()

        *   ->1: OK

        * The first one ('0') conflicts with thereturn code of 0 from

        * pcap_offline_read() meaning "end offile".

       */

       return (p->read_op(p, 1, pcap_oneshot,(u_char *)&s));

}

 

这个函数没有什么好说的,最终调用的函数也是read_op及pcap_read_packet函数,和上面的那2个函数类似。

 

Int pcap_loop(pcap_t *p, int cnt, pcap_handler callback,u_char *user)

{

       register int n;

 

       for (;;) {

              if (p->sf.rfile != NULL) {

                     /*

                      * 0 means EOF, so don't loop if we get 0.

                      */

                     n = pcap_offline_read(p,cnt, callback, user);

              } else {

                     /*

                      * XXX keep reading until we get something

                      * (or an error occurs)

                      */

                     do{

                            n =p->read_op(p, cnt, callback, user);

                     } while (n== 0);

              }

              if (n <= 0)

                     return (n);

              if (cnt > 0) {

                     cnt -= n;

                     if (cnt <= 0)

                            return (0);

              }

       }

}

 

    这函数和上面的3个函数的主要函数的主要区别是,它有一个do 。。。while循环,就是不断的接收数据包的。接收数据包的过程都介绍完了,讲解到这里,大家对libpcap怎么和pfring进行交互的都搞明白了,那么还有pfring怎么和内核补丁pf­_ring.c进行交互呢,下面接着讲解内核补丁,从现在开始真正的讲解到内核了,主要讲解的是pf_ring.c和pfring.h,内核通过pfring.c和libpcap上层进行传递数据包。

你可能感兴趣的:(libpcap接收数据包(三))