libpcap库的常用函数

1,543 阅读9分钟

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;
}