libpcap库的常用函数
pcap_lookupdev()
作用
成功返回设备名指针(第一个合适的网络接口的字符串指针);
失败返回 NULL,同时,errbuf 存放出错信息字符串
函数原型
char *pcap_lookupdev(char *errbuf);
参数详解
- errbuf:C语言字符串缓存区用于存储错误信息。
C语言代码示例
#include <stdio.h>
#include <pcap.h>
int main(){
char errBuf[PCAP_ERRBUF_SIZE], * device;
device = pcap_lookupdev(errBuf);
if (device){
printf("device: %s\n", device);
}
else{
printf("errbuf: %s\n", errBuf);
}
return 0;
}
pcap_findalldevs()
作用
返回所有的网络接口名称。
函数原型
int pcap_findalldevs(pcap_if_t **alldevsp, char *errbuf);
参数详解
- alldevs:pcap_if_t结构体指针,用于存储查找到的所有网络设备信息。
- errbuf:C语言字符串缓存区用于缓冲错误信息。
C语言代码示例
#include <stdio.h>
#include <pcap.h>
int main(){
char errbuf[PCAP_ERRBUF_SIZE];
pcap_if_t *alldevs;
pcap_findalldevs(&alldevs, errbuf);
for (pcap_if_t *pdev = alldevs; pdev != NULL; pdev = pdev->next){
printf("%s %s\n", pdev->name, pdev->description ? pdev->description : "");
}
pcap_freealldevs(alldevs);
return 0;
}
pcap_open_live()
作用
打开监听的设备, 返回一个可供操作的捕获句柄。
函数原型
pcap_t *pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *errbuf);
参数详解
- device:网络接口设备名称,可通过pcap_lookupdev和pcap_findalldevs获取。
- snaplen:捕获数据包时的缓冲区大小。
- promisc:是否使用混杂模式,0为非混杂模式,其他值混杂模式。
- to_ms:数据从内核态复制到用户态等待的时间。由于从内核态切换到用户态,需要比较大的性能消耗。越低的值,性能消耗越大。如果是0,则会一直等待到有足够的数据,才能复制到用户态。tcpdump使用了1000。
- errbuf:C语言字符串缓存区用于缓冲错误信息。
C语言代码示例
#include <stdio.h>
#include <pcap.h>
#define BUFSIZE 65535
int main(){
char errbuf[PCAP_ERRBUF_SIZE];
pcap_t *handle;
//char device[] = "ens33";
char *device;
device = pcap_lookupdev(errbuf);
if (device){
handle = pcap_open_live(device, BUFSIZE, 1, 1000, errbuf);
if (handle == NULL){
printf("Couldn't open device %s: %s\n", device, errbuf);
return 2;
}
pcap_close(handle);
}
else{
printf("errbuf: %s\n", errbuf);
return 1;
}
return 0;
}
pcap_close()
作用
释放网络接口,关闭 pcap_open_live() 打开的网络接口(即其返回值,pcap_t 类型指针),并释放相关资源。注意,操作完网络接口,应该释放其资源。
函数原型
void pcap_close(pcap_t *p);
参数详解
- p:需要关闭的网络接口,pcap_open_live() 的返回值(pcap_t 类型指针)
C语言代码示例
#include <pcap.h>
#include <stdio.h>
#define BUFSIZE 65535
int main(int argc, char *argv[]){
char *dev; // 设备名称
char errbuf[PCAP_ERRBUF_SIZE]; // 错误信息
pcap_t *handle; // 网卡设备的会话句柄
dev = pcap_lookupdev(errbuf);
if (dev == NULL) {
fprintf(stderr, "couldn't find default device: %s\n", errbuf);
return 2;
}
handle = pcap_open_live(dev, BUFSIZE, 1, 1000, errbuf);
if (handle == NULL) {
fprintf(stderr, "couldn't open device %s: %s\n", dev, errbuf);
return 2;
}
pcap_close(handle);
return 0;
}
pcap_setnonblock()
作用
将捕获句柄设置为“非阻塞”或者“阻塞”模式,具体取决于nonblock参数为非零或零。另外pcap_getnonblock(p, errbuf)函数可用于获取当前模式状态。
函数原型
int pcap_setnonblock(pcap_t *p, int nonblock, char *errbuf);
参数详解
- p:捕获句柄,可以由pcap_open_live或者pcap_create返回得到。
- nonblock:设置为“非阻塞”或者“阻塞”模式,零和非零。
- errbuf:C语言字符串缓存区用于缓冲错误信息。
C语言代码示例
#include <stdio.h>
#include <pcap.h>
#define BUFSIZE 65535
int main(){
char errbuf [PCAP_ERRBUF_SIZE];
pcap_t *handle;
char *device;
device = pcap_lookupdev(errbuf);
if (device){
handle = pcap_open_live(device, BUFSIZE, 1, 1000, errbuf);
printf("%d", pcap_getnonblock(handle, errbuf));
pcap_setnonblock(handle, 1, errbuf);
pcap_close(handle);
}
return 0;
}
pcap_getnonblock()
作用
可用于获取句柄当前模式状态,为“非阻塞”或者“阻塞”模式。
函数原型
int pcap_getnonblock(pcap_t *p, char *errbuf);
参数详解
- p:捕获句柄,可以由pcap_open_live或者pcap_create返回得到。
- errbuf:C语言字符串缓存区用于缓冲错误信息。
C语言代码示例
#include <stdio.h>
#include <pcap.h>
#define BUFSIZE 65535
int main(){
char errbuf [PCAP_ERRBUF_SIZE];
pcap_t *handle;
char *device;
device = pcap_lookupdev(errbuf);
if (device){
handle = pcap_open_live(device, BUFSIZE, 1, 1000, errbuf);
printf("%d", pcap_getnonblock(handle, errbuf));
pcap_close(handle);
}
return 0;
}
pcap_lookupnet()
作用
获取设备的网络掩码。
函数原型
int pcap_lookupnet(const char *device, bpf_u_int32 *net, bpf_u_int32 *mask, char *errbuf);
参数详解
- device:网络接口设备名称,可通过pcap_lookupdev()和pcap_findalldevs()函数获取。
- net:缓冲返回的网络码
- mask:缓冲返回的掩码信息
- errbuf:C语言字符串缓存区用于缓冲错误信息。
C语言代码示例
#include <stdio.h>
#include <pcap.h>
int main(){
char errbuf[PCAP_ERRBUF_SIZE];
bpf_u_int32 net;
bpf_u_int32 mask;
char *device;
device = pcap_lookupdev(errbuf);
if (device){
pcap_lookupnet(device, &net, &mask, errbuf);
printf("net: %x, mask: %x", net, mask);
}
else{
printf("errbuf: %s\n", errbuf);
return 1;
}
return 0;
}
pcap_compile()
作用
用于将字符串str编译为过滤器程序,单独的字符串语句并不能直接作为过滤规则,需要进行所谓的编译。
函数原型
int pcap_compile(pcap_t *p, struct bpf_program *fp, char *str, int optimize, bpf_u_int32 netmask);
参数详解
- p:捕获句柄,可以由pcap_open_live或者pcap_create返回得到。
- fp:过滤条件句柄,过滤条件编译出来的结果。我们不太需要关心其具体的结构内容,只需要把它传给下一步的调用即可。
- str:过滤条件表达式。
- optimize:是否优化表达式。
- netmask:指定网络的掩码。
C语言代码示例
#include <stdio.h>
#include <pcap.h>
#define BUFSIZE 65535
int main(){
char errbuf[PCAP_ERRBUF_SIZE];
bpf_u_int32 net;
bpf_u_int32 mask;
char *device;
pcap_t *handle;
struct bpf_program filter;
char filter_app[] = "port 23";
device = pcap_lookupdev(errbuf);
if (device){
printf("device: %s\n", device);
pcap_lookupnet(device, &net, &mask, errbuf);
handle = pcap_open_live(device, BUFSIZE, 1, 0, errbuf);
if(handle != NULL){
//将过滤条件存储在struct bpf_program filter中
pcap_compile(handle, &filter, filter_app, 0, net);
printf("do pcap_compile()\n");
}
pcap_close(handle);
}
else{
printf("errbuf: %s\n", errbuf);
}
return 0;
}
pcap_setfilter()
作用
设置过滤器程序。
函数原型
int pcap_setfilter(pcap_t *p, struct bpf_program *fp);
参数详解
- p:捕获句柄,可以由pcap_open_live()或者pcap_create()函数返回得到。
- fp:过滤条件句柄,过滤条件编译出来的结果。
C语言代码示例
#include <stdio.h>
#include <pcap.h>
#define BUFSIZE 65535
int main(){
char errbuf[PCAP_ERRBUF_SIZE];
bpf_u_int32 net;
bpf_u_int32 mask;
char *device;
pcap_t *handle;
struct bpf_program filter;
char filter_app[] = "port 23";
device = pcap_lookupdev(errbuf);
if (device){
printf("device: %s\n", device);
pcap_lookupnet(device, &net, &mask, errbuf);
handle = pcap_open_live(device, BUFSIZE, 1, 0, errbuf);
if(handle != NULL){
//将过滤条件存储在struct bpf_program filter中
pcap_compile(handle, &filter, filter_app, 0, net);
printf("do pcap_compile()\n");
//设置过滤条件
pcap_setfilter(handle, &filter);
printf("do pcap_setfilter()\n");
}
pcap_close(handle);
}
else{
printf("errbuf: %s\n", errbuf);
}
return 0;
}
pcap_next()
作用
一次只抓取一个包,成功返回捕获数据包的地址,失败返回 NULL。
函数原型
/*
len 和 caplen的区别:
因为在某些情况下你不能保证捕获的包是完整的,
例如一个包长 1480,但是你捕获到 1000 的时候,可能因为某些原因就中止捕获了,
所以 caplen 是记录实际捕获的包长,也就是 1000,而 len 就是 1480。
*/
struct pcap_pkthdr {
struct timeval ts; // 抓到包的时间
bpf_u_int32 caplen; // 表示抓到的数据长度
bpf_u_int32 len; // 表示数据包的实际长度
};
u_char *pcap_next(pcap_t *p, struct pcap_pkthdr *h);
参数详解
C语言代码示例
#include <pcap.h>
#include <stdio.h>
#define BUFSIZE 65535
int main(int argc, char *argv[]){
char *dev; // 设备名称
char errbuf[PCAP_ERRBUF_SIZE]; // 错误信息
pcap_t *handle; // 网卡设备的会话句柄
char filter_exp[] = "port 80"; // 未编译的过滤语句
struct bpf_program fp; // 编译好的过滤信息
bpf_u_int32 mask; // 网络掩码
bpf_u_int32 net; // IP
struct pcap_pkthdr header; // 数据包头部信息
const u_char *packet; // 数据包
dev = pcap_lookupdev(errbuf);
if (dev == NULL) {
fprintf(stderr, "couldn't find default device: %s\n", errbuf);
return 2;
}
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;
}
handle = pcap_open_live(dev, BUFSIZE, 1, 1000, errbuf);
if (handle == NULL) {
fprintf(stderr, "couldn't open device %s: %s\n", dev, errbuf);
return 2;
}
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;
}
packet = pcap_next(handle, &header);
printf("jacked a packet with length of [%d]\n", header.len);
printf("jacked a packet :[%s]\n", packet);
pcap_close(handle);
return 0;
}
pcap_loop()
作用
循环捕获网络数据包,直到遇到错误或者满足退出条件。每次捕获一个数据包就会调用 callback 指定的回调函数,所以,可以在回调函数中进行数据包的处理操作。
成功返回0,失败返回负数
函数原型
int pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user);
callback 回调函数的定义:
void callback(u_char *userarg, const struct pcap_pkthdr *pkthdr, const u_char *packet)
/*
userarg:pcap_loop() 的最后一个参数,当收到足够数量的包后 pcap_loop 会调用callback 回调函数,同时将pcap_loop()的user参数传递给它
pkthdr:是收到数据包的 pcap_pkthdr 类型的指针,和 pcap_next() 第二个参数是一样的。
packet :收到的数据包数据
*/
参数详解
- p:pcap_open_live()返回的 pcap_t 类型的指针。
- cnt:指定捕获数据包的个数,一旦抓到了 cnt 个数据包,pcap_loop 立即返回。如果是 -1,就会永无休止的捕获,直到出现错误。
- callback:回调函数,名字任意,根据需要自行起名。
- user:向回调函数中传递的参数。
C语言代码示例
#include <pcap.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <net/ethernet.h>
#define BUFSIZE 65535
/*******************************回调函数************************************/
void ethernet_protocol_callback(unsigned char *argument,const struct pcap_pkthdr *packet_heaher,const unsigned char *packet_content){
unsigned char *mac_string; //mac字符串地址
struct ether_header *ethernet_protocol;
unsigned short ethernet_type; //以太网类型
printf("----------------------------------------------------\n");
//转换时间
printf("%s\n", ctime((time_t *)&(packet_heaher->ts.tv_sec)));
ethernet_protocol = (struct ether_header *)packet_content;
//获取源mac地址
mac_string = (unsigned char *)&(ethernet_protocol->ether_shost);
printf("Mac Source Address is %02x:%02x:%02x:%02x:%02x:%02x\n",*(mac_string+0),*(mac_string+1),*(mac_string+2),*(mac_string+3),*(mac_string+4),*(mac_string+5));
//获取目的mac
mac_string = (unsigned char *)&(ethernet_protocol->ether_dhost);
printf("Mac Destination Address is %02x:%02x:%02x:%02x:%02x:%02x\n",*(mac_string+0),*(mac_string+1),*(mac_string+2),*(mac_string+3),*(mac_string+4),*(mac_string+5));
//获得以太网的类型
ethernet_type = ntohs(ethernet_protocol->ether_type);
printf("Ethernet type is :%04x\n",ethernet_type);
switch(ethernet_type){
case 0x0800: //ip
printf("The network layer is IP protocol\n");
break;
case 0x0806: //arp
printf("The network layer is ARP protocol\n");
break;
case 0x0835: //rarp
printf("The network layer is RARP protocol\n");
break;
default:
break;
}
usleep(800*1000);
}
int main(){
char errbuf[PCAP_ERRBUF_SIZE]; // 错误信息
char *dev;
const unsigned char *p_packet_content = NULL; // 保存接收到的数据包的起始地址
pcap_t *pcap_handle = NULL;
struct pcap_pkthdr protocol_header;
// 获取网卡设备名称
dev = pcap_lookupdev(errbuf);
if (dev == NULL) {
fprintf(stderr, "couldn't find default device: %s\n", errbuf);
return 2;
}
// 获取网卡设备的会话句柄
pcap_handle = pcap_open_live(dev, 1024, 1, 0, NULL);
if( pcap_loop(pcap_handle, -1, ethernet_protocol_callback, NULL) < 0 ){
perror("pcap_loop");
}
pcap_close(pcap_handle);
return 0;
}
pcap_dispatch()
作用
这个函数和 pcap_loop() 非常类似,只是在超过 to_ms 毫秒后就会返回( to_ms 是pcap_open_live() 的第4个参数 ),或者接受了cnt个数据包后就返回。
函数原型
int pcap_dispatch(pcap_t *p, int cnt, pcap_handler callback, u_char *user);
callback 回调函数的定义:
void callback(u_char *userarg, const struct pcap_pkthdr *pkthdr, const u_char *packet)
/*
userarg:pcap_loop() 的最后一个参数,当收到足够数量的包后 pcap_dispatch 会调用 callback 回调函数,pcap_dispatch()的user参数传递给它
pkthdr:是收到数据包的 pcap_pkthdr 类型的指针,和 pcap_next() 第二个参数是一样的。
packet :收到的数据包数据
*/
参数详解
- p:pcap_open_live()返回的 pcap_t 类型的指针。
- cnt:cnt 参数指定函数返回前所处理数据包的最大值。cnt=-1表示在一个缓冲区中处理所有的数据包。cnt=0表示处理所有数据包,直到产生以下错误之一:读取到EOF或超时读取。(个人看了man手册之后觉得0和-1的作用其实是一样的)
- callback:回调函数,名字任意,根据需要自行起名。
- user:向回调函数中传递的参数。
C语言代码示例
#include <pcap/pcap.h>
#include <stdio.h>
#include <string.h>
#include <net/ethernet.h>
#include <time.h>
#define BUFSIZE 65535
/*******************************回调函数************************************/
void ethernet_protocol_callback(unsigned char *argument,const struct pcap_pkthdr *packet_heaher,const unsigned char *packet_content){
unsigned char *mac_string; //mac字符串地址
struct ether_header *ethernet_protocol;
unsigned short ethernet_type; //以太网类型
printf("----------------------------------------------------\n");
//转换时间
printf("%s\n", ctime((time_t *)&(packet_heaher->ts.tv_sec)));
ethernet_protocol = (struct ether_header *)packet_content;
//获取源mac地址
mac_string = (unsigned char *)&(ethernet_protocol->ether_shost);
printf("Mac Source qAddress is %02x:%02x:%02x:%02x:%02x:%02x\n",*(mac_string+0),*(mac_string+1),*(mac_string+2),*(mac_string+3),*(mac_string+4),*(mac_string+5));
//获取目的mac
mac_string = (unsigned char *)&(ethernet_protocol->ether_dhost);
printf("Mac Destination Address is %02x:%02x:%02x:%02x:%02x:%02x\n",*(mac_string+0),*(mac_string+1),*(mac_string+2),*(mac_string+3),*(mac_string+4),*(mac_string+5));
//获得以太网的类型
ethernet_type = ntohs(ethernet_protocol->ether_type);
printf("Ethernet type is :%04x\n",ethernet_type);
switch(ethernet_type){
case 0x0800: //ip
printf("The network layer is IP protocol\n");
break;
case 0x0806: //arp
printf("The network layer is ARP protocol\n");
break;
case 0x0835: //rarp
printf("The network layer is RARP protocol\n");
break;
default:
break;
}
usleep(800*1000);
}
int main(){
char errbuf[PCAP_ERRBUF_SIZE]; // 错误信息
char *dev;
const unsigned char *p_packet_content = NULL; // 保存接收到的数据包的起始地址
pcap_t *pcap_handle = NULL;
struct pcap_pkthdr protocol_header;
printf("here\n");
// 获取网卡设备名称
dev = pcap_lookupdev(errbuf);
if (dev == NULL) {
printf("couldn't find default device: %s\n", errbuf);
return 2;
}
// 获取网卡设备的会话句柄
pcap_handle = pcap_open_live(dev, 1024, 1, 0, NULL);
if( pcap_dispatch(pcap_handle, 0, ethernet_protocol_callback, NULL) < 0 ){
perror("pcap_loop");
}
pcap_close(pcap_handle);
return 0;
}