本文以应用程序的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上层进行传递数据包。