libpcap的安装问题

由于最近要做的项目要用到mOS,装mOS之前要用到libpcap。

为了开发需要,我决定使用最新libpcap源码包安装。在Unix环境下安装libpcap库,需要
c编译器,flex,bison等,安装Ubuntu系统时,没有这些包。安装flex需要m4编译环境,否则会提示“GNU M4 is required”错误。

1. 安装系统依赖包
sudo apt-get install gcc libc6-dev

221445_vhlb_614348.png
sudo apt-get install m4 

221541_gX2O_614348.png
sudo apt-get install flex bison
221701_IAll_614348.png
2. 下载libpcap源码并安装
从官网http://www.tcpdump.org/下载最新的libpcap版本
cd /usr/local/src
wget http://www.tcpdump.org/release/libpcap-1.5.3.tar.gz


tar zxvf libpcap-1.5.3.tar.gz
cd libpcap-1.5.3
./configure
make
sudo make install

3. 安装开发需要用到的依赖库
sudo apt-get install libpcap-dev

4. 测试libpcap的小程序,命名为pcap_demo.c,以检验环境是否配置正确
#include <pcap.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
pcap_t *handle; /* Session handle */
char *dev;  /* The device to sniff on */
char errbuf[PCAP_ERRBUF_SIZE]; /* Error string */
struct bpf_program fp; /* The compiled filter */
char filter_exp[] = "port 80"; /* The filter expression */
bpf_u_int32 mask; /* Our netmask */
bpf_u_int32 net; /* Our IP */
struct pcap_pkthdr header; /* The header that pcap gives us */
const u_char *packet; /* The actual packet */

/* Define the device */
dev = pcap_lookupdev(errbuf);
if (dev == NULL) {
fprintf(stderr, "Couldn't find default device: %s\n", errbuf);
return(2);
}
/* Find the properties for the device */
if (pcap_lookupnet(dev, &net, &mask, errbuf) == -1) {
fprintf(stderr, "Couldn't get netmask for device %s: %s\n", dev, errbuf);
net = 0;
mask = 0;
}
/* Open the session in promiscuous mode */
handle = pcap_open_live(dev, BUFSIZ, 1, 1000, errbuf);
if (handle == NULL) {
fprintf(stderr, "Couldn't open device %s: %s\n", dev, errbuf);
return(2);
}
/* Compile and apply the filter */
if (pcap_compile(handle, &fp, filter_exp, 0, net) == -1) {
fprintf(stderr, "Couldn't parse filter %s: %s\n", filter_exp, pcap_geterr(handle));
return(2);
}
if (pcap_setfilter(handle, &fp) == -1) {
fprintf(stderr, "Couldn't install filter %s: %s\n", filter_exp, pcap_geterr(handle));
return(2);
}
/* Grab a packet */
packet = pcap_next(handle, &header);
/* Print its length */
printf("Jacked a packet with length of [%d]\n", header.len);
/* And close the session */
pcap_close(handle);
return(0);
}
开始编译:
    gcc -g pcap_demo.c -o pcap_demo -lpcap  
开始执行
    ./pcap_demo

5. 注意的问题

5.1. 注意使用root用户来执行,或者对普通用户使用sudo来提升权限
    sudo pcap_demo

5.2. 对一些PCAP API函数要有全面地理解,并时刻更新文档,比如pcap_loop这个函数,下面是官网的man page地址
http://www.tcpdump.org/manpages/pcap.3pcap.html

5.3. 对一些函数的理解:
    pcap_loop和pcap_dispatch的区别,前者不会超时返回,而是会尽量去读取更多的包,直至发生一个错误才返回。正常返回的值是0,否则就是负值。我目前需要连续不断地抓包,所以应该使用pcap_loop。pcap_dispatch内部会调用pcap_loop。pcap_loop中的第四个参数在某些应用中很有用,但是在通常情况下被设为NULL。这种定义很有用,比如假如你想给pcap_loop的回调函数再额外传递一些参数,你可以使用u_char类型的指针(字符串),传人到回调函数中再做具体地处理。pcap_dispatch只会处理它收到的第一批包。

从pcap_loop返回后,我们应该显式调用pcap_close来关闭pcap以便释放资源。为了表示连续不间断的抓包,在pcap_loop中要尽量使用-1而不是0.关于其中回调函数的说明,里面有3个参数。第一个参数user是pcap_loop中最后一个参数,它表示某个不太重要的信息,可以忽略,第二个参数是包头信息,含有包的时间戳和长度,第三个参数是包的数据,从链路层头开始,它在该回调函数中并不会被释放,但是当该回调返回时不包装这些数据再合法,所以要再次使用它们,请事先复制出来。

下面再深入研究一下pcap_loop这个回调函数的定义:
首先注意函数的返回值是void类型,这完全符合逻辑,因为pcap_loop根本不知道如何处理回调函数的返回值,就是给他传出返回值也没有意义。然后就是回调函数的第一个参数对应pcap_loop中的最后一个参数,不管你给pcap_loop的最后一个参数传递什么值,当回调函数被调用时,它都会作为第一个参数传递给回调函数,第二个参数是pcap头,它包含一些信息:抓包时间,包多大,等等,这个结构在pcap.h中定义,如下:
struct pcap_pkthdr {
struct timeval ts;          /* time stamp */
bpf_u_int32 caplen;     /* length of portion present */
bpf_u_int32 len;           /* length this packet (off wire) */
};

抓包方式有两种,一种是每次抓一个包,另一种是循环抓包,分为循环抓有限个数的包,或者是无限循环抓包。其中每次抓一个包使用pcap_next()函数。pcap_next()表示只抓取一个数据包,它的第一个参数是session handle,第二个参数是包头,返回值是包数据。函数原型如下:
u_char *pcap_next(pcap_t *p, struct pcap_pkthdr *h)
另一种循环抓包的方法,就是使用pcap_loop和回调函数,代码样例参见我整理出的两个例子。

5.4. 网卡设备的设置有两种方法,第一种方法是用户直接指定网卡名称(必须要真实可用),从命令行参数传递进去,比如
#include <stdio.h>
#include <pcap.h>

int main(int argc, char *argv[])
{
char *dev = argv[1];
printf("Device: %s\n", dev);
return(0);
}
另一种方法,是让pcap自己去探测网卡设备,如果出错,会给出出错信息,但是这种方法很多情况下不靠谱,对此的改进是,让pcap探测出所有的网卡设备,让用户区选择使用那个网络设备抓包,参见下面的代码
#include <stdio.h>
#include <pcap.h>

int main(int argc, char *argv[])
{
char *dev, errbuf[PCAP_ERRBUF_SIZE];

dev = pcap_lookupdev(errbuf);
if (dev == NULL) {
fprintf(stderr, "Couldn't find default device: %s\n", errbuf);
return(2);
}
printf("Device: %s\n", dev);
return(0);
}

5.5.对我们抓到的数据包,它的结构,大体分为Ethernet header,IP header,TCP header等等,分别对应14,20,20个字节,IP header至少20个字节,TCP头也至少20个字节。
data=recv_data[42:]这样做的原因是,recv_data 抓到的都是raw packet, tcp/ip是分了5层,一个udp包,会带有14Bytes的Ethenet_II Frame的头,然后是20个字节的ip包头,而udp包头有8个字节,所以偏移量42之后的才是udp的实际内容。tcp包的包头大小为20Bytes,所以其实际内容在偏移量54之后。

5.6.对数据包的解包和组包,需要用到Ethernet header,IP header,TCP header,UDP header等常用数据结构的定义,我们并不需要重新定义这些头,直接引用相关的头文件定义就可以了。
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <netinet/ip_icmp.h> 

5.6.对我们目前的程序,通常需要从命令行传人如下参数信息:
wlan0             待抓包的网卡名称
tcp port 80    过滤表达式
10000 or -1   抓包模式,是抓指定个数的包(这里是10000个包),还是无限抓包下去
当然,对这些参数,你也可以写死在程序里。

转载于:https://my.oschina.net/wangsifangyuan/blog/994033

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值