Libpcap学习笔记

libpcap是Unix/Linux平台的数据包捕获库,提供了一个跨平台的包捕获API。它通过网络分接头和数据过滤器实现数据包的捕获与过滤,使用BPF算法对链路层数据包进行过滤。主要函数包括pcap_lookupdev、pcap_open_live、pcap_setfilter等,用于设备查找、打开设备、设置过滤规则等。在抓包过程中,需要设置设备、打开设备、编译和设置过滤器,然后通过pcap_loop或pcap_dispatch进行数据包捕获。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考

[1]http://www.tcpdump.org/pcap.html

[2]http://blog.youkuaiyun.com/lsg_down/article/details/78486614

[3]http://e-ghost.deusto.es/docs/2005/conferencias/pcap.pdf

[4]wiki

[5]API

简单介绍

libpcap(Packet Capture Library),即数据包捕获函数库,是Unix/Linux平台下的网络数据包捕获函数库。它是一个独立于系统的用户层包捕获的API接口,为底层网络监测提供了一个可移植的框架。

libpcap工作原理

​ libpcap主要由两部份组成:网络分接头(Network Tap)和数据过滤器(Packet Filter)。网络分接头从网络设备驱动程序中收集数据拷贝,过滤器决定是否接收该数据包。Libpcap利用BSD Packet Filter(BPF)算法对网卡接收到的链路层数据包进行过滤。BPF算法的基本思想是在有BPF监听的网络中,网卡驱动将接收到的数据包复制一份交给BPF过滤器,过滤器根据用户定义的规则决定是否接收此数据包以及需要拷贝该数据包的那些内容,然后将过滤后的数据给与过滤器相关联的上层应用程序。

​ libpcap的包捕获机制就是在数据链路层加一个旁路处理。当一个数据包到达网络接口时,libpcap首先利用已经创建的Socket从链路层驱动程序中获得该数据包的拷贝,再通过Tap函数将数据包发给BPF过滤器。BPF过滤器根据用户已经定义好的过滤规则对数据包进行逐一匹配,匹配成功则放入内核缓冲区,并传递给用户缓冲区,匹配失败则直接丢弃。如果没有设置过滤规则,所有数据包都将放入内核缓冲区,并传递给用户层缓冲区。

常用函数

  • pcap_lookupdev()

    函数用于查找网络设备,返回可被pcap_open_live()函数调用的网络设备名指针。

  • pcap_open_live()

    函数用于打开网络设备,并且返回用于捕获网络数据包的数据包捕获描述字。对于此网络设备的操作都要基于此网络设备描述字。

  • pcap_lookupnet()

    函数获得指定网络设备的网络号和掩码。

  • pcap_compile()

    函数用于将用户制定的过滤策略编译到过滤程序中。

  • pcap_setfilter()

    函数用于设置过滤器。

  • pcap_loop()

    函数pcap_dispatch()函数用于捕获数据包,捕获后还可以进行处理,此外pcap_next()和pcap_next_ex()两个函数也可以用来捕获数据包。

  • pcap_close()

    函数用于关闭网络设备,释放资源。

抓包步骤

  1. 设置设备。In Linux this may be something like eth0, in BSD it may be xl1, etc. We can either define this device in a string, or we can ask pcap to provide us with the name of an interface that will do the job.
  2. 初始化pcap。正式决定在那个设备上用于抓包,通过handles区别不同的设备。
  3. 编译过滤器
  4. 进入执行循环队列。
  5. 关闭会话。

1. 设置设备

  1. 参数输入
#include <stdio.h>
#include <pcap.h>

int main(int argc, char *argv[])
{
  char *dev = argv[1];

  printf("Device: %s\n", dev);
  return(0);
}
  1. 使用pcap_lookupdev查找

    注:pcap_lookupdev只返回第一个可用于嗅探的接口

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

2. 打开设备用于嗅探

使用pcap_open_live打开一个设备

pcap_t *pcap_open_live(char *device, int snaplen, int promisc, int to_ms,
        char *ebuf)

​ 其第一个参数是我们在上一节中指定的设备,snaplen是整形的,它定义了将被pcap捕捉的最大字节数。当promisc设为true时将置指定接口为混杂模式(然而,当它置为false时接口仍处于混杂模式的非凡情况也是有可能的)。to_ms是读取时的超时值,单位是毫秒(假如为0则一直嗅探直到错误发生,为-1则不确定)。最后,ebuf是一个我们可以存入任何错误信息的字符串(就像上面的errbuf)。此函数返回其会话句柄。

​ 混杂模式与非混杂模式的区别:这两种方式区别很大。一般来说,非混杂模式的嗅探器中,主机仅嗅探那些跟它直接有关的通信,如发向它的,从它发出的,或经它路由的等都会被嗅探器捕捉。而在混杂模式中则嗅探传输线路上的所有通信。在非交换式网络中,这将是整个网络的通信。这样做最明显的优点就是使更多的包被嗅探到,它们因你嗅探网络的原因或者对你有帮助,或者没有。但是,混杂模式是可被探测到的。一个主机可以通过高强度的测试判定另一台主机是否正在进行混杂模式的嗅探。其次,它仅在非交换式的网络环境中有效工作(如集线器,或者交换中的ARP层面)。再次,在高负荷的网络中,主机的系统资源将消耗的非常严重。

示例程序:

 #include <pcap.h>
//...
 pcap_t *handle;
/*This code fragment opens the device stored in the strong "dev", tells it to read however many bytes are specified in BUFSIZ (which is defined in pcap.h). We are telling it to put the device into promiscuous mode, to sniff until an error occurs, and if there is an error, store it in the string errbuf; it uses that string to print an error message.*/
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);
}

不是所有包的链路层头都一样。

# If your program doesn't support the link-layer header type provided by the device, it has to give up; this would be done with code such as

if (pcap_datalink(handle) != DLT_EN10MB) {
  fprintf(stderr, "Device %s doesn't provide Ethernet headers - not supported\n", dev);
  return(2);
}

常用的链路层类别有,具体区别见【http://www.tcpdump.org/linktypes.html】:

DLT_NULL:BSD loopback encapsulation
DLT_EN10MB:Ethernet (10Mb, 100Mb, 1000Mb, and up)
DLT_IEEE802:IEEE 802.5 Token Ring
DLT_ARCNET:ARCNET
DLT_SLIP:SLIP
DLT_PPP:PPP
DLT_FDDI:FDDI
DLT_ATM_RFC1483:RFC 1483 LLCSNAP-encapsulated ATM
DLT_RAW:raw IP, el paquete comienza con una cabecera IP
DLT_PPP_SERIAL:PPP en modo HDLC-like framing (RFC 1662)
DLT_PPP_ETHER:PPPoE
DLT_C_HDLC:Cisco PPP con HDLC framing, definido en 4.3.1 RFC 1547
DLT_IEEE802_11:IEEE 802.11 wireless LAN
DLT_LOOP:OpenBSD loopback encapsulation
DLT_LINUX_SLL:Linux cooked capture encapsulation
LT_LTALK:Apple LocalTal

3. 过滤通信

3.1. 编译过滤器

int pcap_compile(pcap_t *p, struct bpf_program *fp, char *str, int optimize, 
        bpf_u_int32 netmask)

​ 第一个参数是会话句柄。接下来的是我们存储被编译的过滤器版本的地址的引用。再接下来的则是表达式本身,存储在规定的字符串格式里。再下边是一个定义表达式是否被优化的整形量(0为false,1为true,标准规定)。最后,我们必须指定应用此过滤器的网络掩码。函数返回-1为失败,其他的任何值都表明是成功的。

3.2. 设置过滤器

​ 表达式被编译之后就可以使用了。现在进入pcap_setfilter()。

int pcap_setfilter(pcap_t *p, struct bpf_program *fp)

​ 这非常直观,第一个参数是会话句柄,第二个参数是被编译表达式版本的引用(可推测出它与pcap_compile()的第二个参数相同)。

3.3. 示例代码

#include <pcap.h>
...
  pcap_t *handle;       /* Session handle */
char dev[] = "rl0";     /* Device to sniff on */
char errbuf[PCAP_ERRBUF_SIZE];  /* Error string */
struct bpf_program fp;      /* The compiled filter expression */
char filter_exp[] = "port 23";  /* The filter expression */
bpf_u_int32 mask;       /* The netmask of our sniffing device */
bpf_u_int32 net;        /* The IP of our sniffing device */

if (pcap_lookupnet(dev, &net, &mask, errbuf) == -1) {
  fprintf(stderr, "Can't get netmask for device %s\n", dev);
  net = 0;
  mask = 0;
}
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);
}
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);
}

这个程序使嗅探器嗅探经由端口23的所有通信,使用混杂模式,设备是rl0.

# 官网注释:不起作用?
You may notice that the previous example contains a function that we have not yet discussed. pcap_lookupnet() is a function that, given the name of a device, returns one of its IPv4 network numbers and corresponding network mask (the network number is the IPv4 address ANDed with the network mask, so it contains only the network part of the address). This was essential because we needed to know the network mask in order to apply the filter. This function is described in the Miscellaneous section at the end of the document.

It has been my experience that this filter does not work across all operating systems. In my test environment, I found that OpenBSD 2.9 with a default kernel does support this type of filter, but FreeBSD 4.3 with a default kernel does not. Your mileage may vary.

4. 真实的嗅探

​ 有两种手段捕捉包。我们可以一次只捕捉一个包,也可以进入一个循环,等捕捉到多个包再进行处理。我们将先看看怎样去捕捉单个包,然后再看看使用循环的方法。为此,我们使用函数pcap_next()。

u_char *pcap_next(pcap_t *p, struct pcap_pkthdr *h)

​ 第一个参数是会话句柄,第二个参数是指向一个包括了当前数据包总体信息(被捕捉时的时间,包的长度,其被指定的部分长度)的结构体的指针(在这里只有一个片断,只作为一个示例)。pcap_next()返回一个u_char指针给被这个结构体描述的包。我们将稍后讨论这种实际读取包本身的手段。

   这里有一个演示怎样使用pcap_next()来嗅探一个包的例子:

#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 23";    /* 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);
}

这个程序嗅探被pcap_lookupdev()返回的设备并将它置为混杂模式。它发现第一个包经过端口23(telnet)并且告诉用户此包的大小(以字 节为单位)。这个程序又包含了一个新的调用pcap_close(),我们将在后面讨论(尽管它的名字就足够证实它自己的作用)。

实际上很少有嗅探程序会真正的使用pcap_next()。通常,它们使用pcap_loop()或者 pcap_dispatch()(它就是用了pcap_loop())。

pcap_loop()的原型如下:

int pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user)

​ 第一个参数是会话句柄,接下来是一个整型,它告诉pcap_loop()在返回前应捕捉多少个数据包(若为负值则表示应该一直工作直至错误发生)。第三个参数是回调函数的名称(正像其标识符所指,无括号)。最后一个参数在有些应用里有用,但更多时候则置为NULL。假设我们有我们自己的想送往回调函数的参数,另外还有pcap_loop()发送的参数,这就需要用到它。很明显,必须是一个u_char类型的指针以确保结果正确;正像我们稍后见到的,pcap使用了很有意思的方法以u_char指针的形势传递信息。pcap_dispatch()的用法几乎相同。唯一不同的是它们如何处理超时(还记得在调用pcap_open_live()时怎样设置超时吗?这就是它起作用的地方)。Pcap_loop()忽略超时而pcap_dispatch()则不。关于它们之间区别的更深入的讨论请参见pcap的手册页。

callback的原型如下:

void got_packet(u_char *args, const struct pcap_pkthdr *header,
        const u_char *packet);

​ 让我们更细致的考察它。首先,你会注重到该函数返回void类型,这是符合逻辑的,因为pcap_loop()不知道如何去处理一个回调返回值。第一个参数相应于pcap_loop()的最后一个参数。每当回调函数被老婆 调用时,无论最后一个参数传给pcap_loop()什么值,这个值都会传给我们回调函数的第一个参数。第二个参数是pcap头文件定义的,它包括数据包被嗅探的时间、大小等信息。

​ 结构体pcap_pkhdr在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程序新手感到迷惑。这又是一个u_char指针,它包含了被pcap_loop()嗅探到的所有包。

​ 但是你怎样使用这个我们在原型里称为packet的变量呢?一个数据包包含许多属性,因此你可以想象它不只是一个字符串,而实质上是一个结构体的集合(比如,一个TCP/IP包会有一个以太网的头部,一个IP头部,一个TCP头部,还有此包的有效载荷)。这个u_char就是这些结构体的串联版本。为了使用它,我们必须作一些有趣的匹配工作。

​ 下面这些是一些数据包的结构体(libpcap中已经包含有一些预定好的eternet头,ip header,tcp header,分别在net/ethernet.h、netinet/ip.h,netinet/tcp.h

/* Ethernet addresses are 6 bytes */
#define ETHER_ADDR_LEN  6

    /* Ethernet header */
    struct sniff_ethernet {
        u_char ether_dhost[ETHER_ADDR_LEN]; /* Destination host address */
        u_char ether_shost[ETHER_ADDR_LEN]; /* Source host address */
        u_short ether_type; /* IP? ARP? RARP? etc */
    };

    /* IP header */
    struct sniff_ip {
        u_char ip_vhl;      /* version << 4 | header length >> 2 */
        u_char ip_tos;      /* type of service */
        u_short ip_len;     /* total length */
        u_short ip_id;      /* identification */
        u_short ip_off;     /* fragment offset field */
    #define IP_RF 0x8000        /* reserved fragment flag */
    #define IP_DF 0x4000        /* dont fragment flag */
    #define IP_MF 0x2000        /* more fragments flag */
    #define IP_OFFMASK 0x1fff   /* mask for fragmenting bits */
        u_char ip_ttl;      /* time to live */
        u_char ip_p;        /* protocol */
        u_short ip_sum;     /* checksum */
        struct in_addr ip_src,ip_dst; /* source and dest address */
    };
    #define IP_HL(ip)       (((ip)->ip_vhl) & 0x0f)
    #define IP_V(ip)        (((ip)->ip_vhl) >> 4)

    /* TCP header */
    typedef u_int tcp_seq;

    struct sniff_tcp {
        u_short th_sport;   /* source port */
        u_short th_dport;   /* destination port */
        tcp_seq th_seq;     /* sequence number */
        tcp_seq th_ack;     /* acknowledgement number */
        u_char th_offx2;    /* data offset, rsvd */
    #define TH_OFF(th)  (((th)->th_offx2 & 0xf0) >> 4)
        u_char th_flags;
    #define TH_FIN 0x01
    #define TH_SYN 0x02
    #define TH_RST 0x04
    #define TH_PUSH 0x08
    #define TH_ACK 0x10
    #define TH_URG 0x20
    #define TH_ECE 0x40
    #define TH_CWR 0x80
    #define TH_FLAGS (TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG|TH_ECE|TH_CWR)
        u_short th_win;     /* window */
        u_short th_sum;     /* checksum */
        u_short th_urp;     /* urgent pointer */
};

​ pcap嗅探数据包时正是使用的这些结构。接下来,它简单的创建一个u_char字符串并且将这些结构体填入。那么我们怎样才能区分它们呢?预备好见证指针最实用的好处之一吧。

​ 我们再一次假定要对以太网上的TCP/IP包进行处理。同样的手段可以应用于任何数据包,唯一的区别是你实际所使用的结构体的类型。让我们从声明分解u_char包的变量开始:

#define SIZE_ETHERNET 14

const struct sniff_ethernet *ethernet; /* The ethernet header */
const struct sniff_ip *ip; /* The IP header */
const struct sniff_tcp *tcp; /* The TCP header */
const char *payload; /* Packet payload */

u_int size_ip;
u_int size_tcp;

开始赋值

ethernet = (struct sniff_ethernet*)(packet);
ip = (struct sniff_ip*)(packet + SIZE_ETHERNET);
size_ip = IP_HL(ip)*4;
if (size_ip < 20) {
  printf("   * Invalid IP header length: %u bytes\n", size_ip);
  return;
}
tcp = (struct sniff_tcp*)(packet + SIZE_ETHERNET + size_ip);
size_tcp = TH_OFF(tcp)*4;
if (size_tcp < 20) {
  printf("   * Invalid TCP header length: %u bytes\n", size_tcp);
  return;
}
payload = (u_char *)(packet + SIZE_ETHERNET + size_ip + size_tcp);

​ 此处如何工作?考虑u_char在内存中的层次。基本的,当pcap将这些结构体填入u_char的时候是将这些数据存入一个字符串中,那个字符串将被送入我们的回调函数中。反向转换是这样的,不考虑这些结构体制中的值,它们的大小将是一致的。例如在我的平台上,一个sniff_ethernet结构体的大小是14字节。一个sniff_ip结构体是20字节,一个sniff_tcp结构体也是20字节。 u_char指针正是包含了内存地址的一个变量,这也是指针的实质,它指向内存的一个区域。简单而言,我们说指针指向的地址为x,假如三个结构体恰好线性排列,第一个(sniff_ethernet)被装载到内存地址的x处则我们很轻易的发现其他结构体的地址,让我们以表格显示之:

VariableLocation (in bytes)
sniff_ethernetX
sniff_ipX + SIZE_ETHERNET
sniff_tcpX + SIZE_ETHERNET + {IP header length}
payloadX + SIZE_ETHERNET + {IP header length} + {TCP header length}

​ 结构体sniff_ethernet正好在x处,紧接着它的sniff_ip则位于x加上它本身占用的空间(此例为14字节),依此类推可得全部地址。

完整的示例程序【http://www.tcpdump.org/sniffex.c

主要的函数接口

  • pcap_t *pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf)

    获得用于捕获网络数据包的数据包捕获描述字。device参数为指定打开的网络设备名。snaplen参数定义捕获数据的最大字节数。promisc指定是否将网络接口置于混杂模式。to_ms参数指定超时时间(毫秒)。ebuf参数则仅在pcap_open_live()函数出错返回NULL时用于传递错误消息。

  • pcap_t *pcap_open_offline(char *fname, char *ebuf)

    打开以前保存捕获数据包的文件,用于读取。fname参数指定打开的文件名。该文件中的数据格式与tcpdump和tcpslice兼容。”-“为标准输入。ebuf参数则仅在pcap_open_offline()函数出错返回NULL时用于传递错误消息。

  • pcap_dumper_t *pcap_dump_open(pcap_t *p, char *fname)

    打开用于保存捕获数据包的文件,用于写入。fname参数为”-“时表示标准输出。出错时返回NULL。p参数为调用pcap_open_offline()或pcap_open_live()函数后返回的pcap结构指针。fname参数指定打开的文件名。如果返回NULL,则可调用pcap_geterr()函数获取错误消息。

  • char *pcap_lookupdev(char *errbuf)

    用于返回可被pcap_open_live()或pcap_lookupnet()函数调用的网络设备名指针。如果函数出错,则返回NULL,同时errbuf中存放相关的错误消息。

  • int pcap_lookupnet(char *device, bpf_u_int32 *netp,bpf_u_int32 *maskp, char *errbuf)

    获得指定网络设备的网络号和掩码。netp参数和maskp参数都是bpf_u_int32指针。如果函数出错,则返回-1,同时errbuf中存放相关的错误消息。

  • int pcap_dispatch(pcap_t *p, int cnt,pcap_handler callback, u_char *user)

    捕获并处理数据包。cnt参数指定函数返回前所处理数据包的最大值。cnt=-1表示在一个缓冲区中处理所有的数据包。cnt=0表示处理所有数据包,直到产生以下错误之一:读取到EOF;超时读取。callback参数指定一个带有三个参数的回调函数,这三个参数为:一个从pcap_dispatch()函数传递过来的u_char指针,一个pcap_pkthdr结构的指针,和一个数据包大小的u_char指针。如果成功则返回读取到的字节数。读取到EOF时则返回零值。出错时则返回-1,此时可调用pcap_perror()或pcap_geterr()函数获取错误消息。

  • int pcap_loop(pcap_t *p, int cnt,pcap_handler callback, u_char *user)

    功能基本与pcap_dispatch()函数相同,只不过此函数在cnt个数据包被处理或出现错误时才返回,但读取超时不会返回。而如果为pcap_open_live()函数指定了一个非零值的超时设置,然后调用pcap_dispatch()函数,则当超时发生时pcap_dispatch()函数会返回。cnt参数为负值时pcap_loop()函数将始终循环运行,除非出现错误。

  • void pcap_dump(u_char *user, struct pcap_pkthdr *h,u_char *sp)

    向调用pcap_dump_open()函数打开的文件输出一个数据包。该函数可作为pcap_dispatch()函数的回调函数。

  • int pcap_compile(pcap_t *p, struct bpf_program *fp,char *str, int optimize, bpf_u_int32 netmask)

    将str参数指定的字符串编译到过滤程序中。fp是一个bpf_program结构的指针,在pcap_compile()函数中被赋值。optimize参数控制结果代码的优化。netmask参数指定本地网络的网络掩码。

  • int pcap_setfilter(pcap_t *p, struct bpf_program *fp)

    指定一个过滤程序。fp参数是bpf_program结构指针,通常取自pcap_compile()函数调用。出错时返回-1;成功时返回0。

  • u_char *pcap_next(pcap_t *p, struct pcap_pkthdr *h)

    返回指向下一个数据包的u_char指针。

  • int pcap_datalink(pcap_t *p)

    返回数据链路层类型,例如DLT_EN10MB。

  • int pcap_snapshot(pcap_t *p)

    返回pcap_open_live被调用后的snapshot参数值。

  • int pcap_is_swapped(pcap_t *p)

    返回当前系统主机字节与被打开文件的字节顺序是否不同。

  • int pcap_major_version(pcap_t *p)

    返回写入被打开文件所使用的pcap函数的主版本号。

  • int pcap_minor_version(pcap_t *p)

    返回写入被打开文件所使用的pcap函数的辅版本号。

  • int pcap_stats(pcap_t *p, struct pcap_stat *ps)

    向pcap_stat结构赋值。成功时返回0。这些数值包括了从开始捕获数据以来至今共捕获到的数据包统计。如果出错或不支持数据包统计,则返回-1,且可调用pcap_perror()或pcap_geterr()函数来获取错误消息。

  • FILE *pcap_file(pcap_t *p)

    返回被打开文件的文件名。

  • int pcap_fileno(pcap_t *p)

    返回被打开文件的文件描述字号码。

  • void pcap_perror(pcap_t *p, char *prefix)

    在标准输出设备上显示最后一个pcap库错误消息。以prefix参数指定的字符串为消息头。

  • char *pcap_geterr(pcap_t *p)

    返回最后一个pcap库错误消息。

  • char *pcap_strerror(int error)

    如果strerror()函数不可用,则可调用pcap_strerror函数替代。

  • void pcap_close(pcap_t *p)

    关闭p参数相应的文件,并释放资源。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值