libpcap库的安装
首先,安装相关的支持环境
sudo apt get install flex
sudo apt get install bison
然后下载最新版的libpcap,下载地址:http://www.tcpdump.org/。
解压缩,在解压缩的文件夹输入以下命令
./configure
make
make install
单网卡抓包DEMO
单网卡抓包是libpcap库非常基础的功能,这里放出一个demo作为参考,方便与多网卡扩展做比较。
单网卡demo的主要功能是,由用户自行输入监听的网卡。在用户没有输入网卡时,程序会打印所有可监听的设备名称,由用户选择所要监听的网卡,当监听一定数量的网卡时(10000),程序终止。
//demo=single
#include "pcap.h"
#include "stdlib.h"
#include <time.h>
#include <arpa/inet.h>
#define SNAP_LEN 65536
//prototype of the packet handler
void dispatcher_handler(u_char *temp1,
const struct pcap_pkthdr *header, const u_char *pkt_data);
int main(int argc, char **argv)
{
char *dev = NULL; /* capture device name */
char errbuf[PCAP_ERRBUF_SIZE]; /* error buffer */
pcap_t *handle; /* packet capture handle */
pcap_if_t *alldev, *p;
char filter_exp[] = "tcp"; /* filter expression [3] */
struct bpf_program fp; /* compiled filter program (expression) */
bpf_u_int32 mask; /* subnet mask */
bpf_u_int32 net; /* ip */
int num_packets = 10000; /* number of packets to capture */
/* check for capture device name on command-line */
if (argc == 2) {
dev = argv[1];
}
else if (argc > 2) {
fprintf(stderr, "error: unrecognized command-line options\n\n");
exit(EXIT_FAILURE);
}
else {
/* find a capture device if not specified on command-line */
int i=0,num;
if(pcap_findalldevs(&alldev,errbuf)==-1)
{
printf("find all devices is error\n");
return 0;
}
for(p=alldev;p;p=p->next)
{
printf("%d:%s\n",++i,p->name);
if(p->description)
{
printf("%s\n",p->description);
}
}
printf("please input which interface you want to use\n");
scanf("%d",&num);
if(num<1||num>i)
{
printf("interface is unavillible\n");
return 0;
}
for(p=alldev,i=1;i<=num;p=p->next,i++)
dev=p->name;
if (dev == NULL) {
fprintf(stderr, "Couldn't find default device: %s\n",
errbuf);
exit(EXIT_FAILURE);
}
}
/* print capture info */
printf("Device: %s\n", dev);
printf("Number of packets: %d\n", num_packets);
printf("Filter expression: %s\n", filter_exp);
/* open capture device */
handle = pcap_open_live(dev, SNAP_LEN, 1, 1000, errbuf);
if (handle == NULL) {
fprintf(stderr, "Couldn't open device %s: %s\n", dev, errbuf);
exit(EXIT_FAILURE);
}
/* make sure we're capturing on an Ethernet device [2] */
if (pcap_datalink(handle) != DLT_EN10MB) {
fprintf(stderr, "%s is not an Ethernet\n", dev);
exit(EXIT_FAILURE);
}
/* compile the filter expression */
if (pcap_compile(handle, &fp, filter_exp, 0, 24) == -1) {
fprintf(stderr, "Couldn't parse filter %s: %s\n",
filter_exp, pcap_geterr(handle));
exit(EXIT_FAILURE);
}
/* apply the compiled filter */
if (pcap_setfilter(handle, &fp) == -1) {
fprintf(stderr, "Couldn't install filter %s: %s\n",
filter_exp, pcap_geterr(handle));
exit(EXIT_FAILURE);
}
/* now we can set our callback function */
pcap_loop(handle, num_packets, dispatcher_handler, NULL);
/* cleanup */
pcap_freecode(&fp);
pcap_close(handle);
printf("\nCapture complete.\n");
return 0;
}
void dispatcher_handler(u_char *temp1,
const struct pcap_pkthdr *header, const u_char *pkt_data)
{
printf("I get one packet!\n");
}
多网卡扩展
由于libpcap库没有可以同时监听多个网卡函数,根据网友的建议,我决定使用多线程的方法分别开多个线程监听多个网卡来解决此问题。这里以双网卡监听为例,将解决方案给出,不妥之处还请读者指出:
- 同样,用户可以先行输入所要监听的两张网卡的名称,
sudo ./demo eth0 wlan0
- 当用户没有输入所要监听的网卡参数时,程序给出所有可以监听的设备名称,由用户依次输入,两个参数分别给两个进程,用来监听数据包。
- 关于程序的终止条件,原来的以包数量为判决在多网卡场景显然不合理,改为以监听时间为判决:开第三个线程执行sleep()函数,第三个进程结束时,结束前两个抓包进程。
代码如下:
//demo
#include "pcap.h"
#include <pthread.h>
#include "stdlib.h"
#include <time.h>
#include <arpa/inet.h>
#include "unistd.h"
#define SNAP_LEN 65535
/* prototype of the packet handler */
void dispatcher_handler(u_char *temp1,
const struct pcap_pkthdr *header, const u_char *pkt_data);
void *thr_fn1(void *arg);
void *thr_fn2(void *arg);
void *thr_fn3(void *arg);
pcap_t *handle1, *handle2; /* packet capture handle */
int CAP_TIME = 600;
pthread_t t1, t2, t3;
int main(int argc, char **argv)
{
char *dev1 = NULL;
char *dev2 = NULL;
char errbuf[PCAP_ERRBUF_SIZE]; /* error buffer */
pcap_if_t *alldev, *p;
char filter_exp[] = "tcp"; /* filter expression [3] */
struct bpf_program fp1,fp2; /* compiled filter program (expression) */
bpf_u_int32 mask; /* subnet mask */
bpf_u_int32 net; /* ip */
/* check for capture device name on command-line */
if (argc == 3) { //pre-define the two device's names to be captured
dev1 = argv[1];
dev2 = argv[2];
}
else if (argc > 3 || argc == 2) {
fprintf(stderr, "error: unrecognized command-line options\n\n");
exit(EXIT_FAILURE);
}
else {
/* find all capture device and tell the users to choose 2 if not specified on command-line */
int i=0,num1,num2;
if(pcap_findalldevs(&alldev,errbuf)==-1)
{
printf("find all devices is error\n");
return 0;
}
for(p=alldev;p;p=p->next)
{
printf("%d:%s\n",++i,p->name);
if(p->description)
{
printf("%s\n",p->description);
}
}
printf("please input the 1st interface you want to use\n");
scanf("%d",&num1);
if(num1<1||num1>i)
{
printf("interface is unavillible\n");
return 0;
}
for(p=alldev,i=1;i<=num1;p=p->next,i++)
dev1=p->name;
printf("please input the 2nd interface you want to use\n");
scanf("%d",&num2);
if(num2<1 || num2>i || num2==num1)
{
printf("interface is unavillible\n");
return 0;
}
for(p=alldev,i=1;i<=num2;p=p->next,i++)
dev2=p->name;
}
printf("please input the capture time\n");
scanf("%d",&CAP_TIME);
/* print capture info */
printf("1st device: %s\n", dev1);
/* open capture device */
handle1 = pcap_open_live(dev1, SNAP_LEN, 1, 1000, errbuf);
if (handle1 == NULL) {
fprintf(stderr, "Couldn't open device %s: %s\n", dev1, errbuf);
exit(EXIT_FAILURE);
}
/* make sure we're capturing on an Ethernet device [2] */
if (pcap_datalink(handle1) != DLT_EN10MB) {
fprintf(stderr, "%s is not an Ethernet\n", dev1);
exit(EXIT_FAILURE);
}
/* compile the filter expression */
if (pcap_compile(handle1, &fp1, filter_exp, 0, 24) == -1) {
fprintf(stderr, "Couldn't parse filter %s: %s\n",
filter_exp, pcap_geterr(handle1));
exit(EXIT_FAILURE);
}
/* apply the compiled filter */
if (pcap_setfilter(handle1, &fp1) == -1) {
fprintf(stderr, "Couldn't install filter %s: %s\n",
filter_exp, pcap_geterr(handle1));
exit(EXIT_FAILURE);
}
/* print capture info */
printf("2nd device: %s\n", dev2);
printf("Filter expression: %s\n", filter_exp);
printf("Caputre time: %d\n", CAP_TIME);
/* open capture device */
handle2 = pcap_open_live(dev2, SNAP_LEN, 1, 1000, errbuf);
if (handle2 == NULL) {
fprintf(stderr, "Couldn't open device %s: %s\n", dev2, errbuf);
exit(EXIT_FAILURE);
}
/* make sure we're capturing on an Ethernet device [2] */
if (pcap_datalink(handle2) != DLT_EN10MB) {
fprintf(stderr, "%s is not an Ethernet\n", dev2);
exit(EXIT_FAILURE);
}
/* compile the filter expression */
if (pcap_compile(handle2, &fp2, filter_exp, 0, 24) == -1) {
fprintf(stderr, "Couldn't parse filter %s: %s\n",
filter_exp, pcap_geterr(handle2));
exit(EXIT_FAILURE);
}
/* apply the compiled filter */
if (pcap_setfilter(handle2, &fp2) == -1) {
fprintf(stderr, "Couldn't install filter %s: %s\n",
filter_exp, pcap_geterr(handle2));
exit(EXIT_FAILURE);
}
pthread_create(&t1, NULL, thr_fn1, NULL);
pthread_create(&t2, NULL, thr_fn2, NULL);
pthread_create(&t3, NULL, thr_fn3, NULL);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
pthread_join(t3, NULL);
pcap_freecode(&fp1);
pcap_freecode(&fp2);
pcap_close(handle1);
pcap_close(handle2);
printf("\nCapture complete.\n");
return 0;
}
void dispatcher_handler(u_char *temp1,
const struct pcap_pkthdr *header, const u_char *pkt_data)
{
printf("I get one packet!\n");
}
void *thr_fn1(void *arg)
{
pcap_loop(handle1, 0, dispatcher_handler, NULL);
//pthread_cancel( t2 );
}
void *thr_fn2(void *arg)
{
pcap_loop(handle2, 0, dispatcher_handler, NULL);
//pthread_cancel( t1 );
}
void *thr_fn3(void *arg)
{
sleep(CAP_TIME);
pthread_cancel( t1 );
pthread_cancel( t2 );
}
程序编译需要连接libpcap库和pthread库,使用命令gcc -g -w demo.cpp -o demo -lpcap -lpthread
注:篇幅起见,主循环仅为一句打印指令,实现不同功能可用其他网络资源替换。