#include <pcap/pcap.h>
char errbuf[PCAP_ERRBUF_SIZE];
pcap_t *pcap_create(const char *source, char *errbuf);
pcap_create() 用于创建数据包捕获句柄(handle)以查看网络上的数据包。 source 是一个字符串,指定要打开的网络设备;在具有 2.2 或更高版本内核的 Linux 系统上,可以使用“any”或 NULL 的源参数来捕获来自所有接口的数据包。
返回的句柄必须先用 pcap_activate(3PCAP) 激活,然后才能用它捕获数据包;捕获的选项,例如混杂模式,可以在激活之前在句柄上设置。
pcap_create() 成功时返回 pcap_t *,失败时返回 NULL。如果返回 NULL,则 errbuf 会填充适当的错误消息。假设 errbuf 至少能够保存 PCAP_ERRBUF_SIZE (自己设置)字符。
#include <pcap/pcap.h>
int pcap_activate(pcap_t *p);
pcap_activate() 用于激活数据包捕获句柄以查看网络上的数据包,并且在句柄上设置的选项生效。
pcap_activate() 成功且没有警告返回0,成功有警告返回非0正值,错误时返回负值;非零返回值指示发生了什么警告或错误情况。
具体参考:https://www.tcpdump.org/manpages/pcap_activate.3pcap.html
#include <pcap/pcap.h>
int pcap_datalink(pcap_t *p);
pcap_datalink() 返回由 p 指定的实时捕获或“保存文件”的链路层标头类型。
它不能在 pcap_create(3PCAP) 创建的 pcap_activate(3PCAP) 尚未激活的 pcap 描述符上调用。
https://www.tcpdump.org/linktypes.html 列出了 pcap_datalink() 可以返回的值,并描述了与这些值对应的数据包格式。
不要假设给定捕获或“保存文件”的数据包将具有任何给定的链路层标头类型,例如以太网的 DLT_EN10MB。例如,Linux 上的“any”设备将具有 DLT_LINUX_SLL 或 DLT_LINUX_SLL2 的链路层标头类型,即使在打开“any”设备时系统上的所有设备都具有一些其他数据链路类型,例如 DLT_EN10MB 用于以太网。
pcap_datalink()成功时返回链路层标头类型,如果在已创建但未激活的捕获句柄上调用会抛出 PCAP_ERROR_NOT_ACTIVATED。
参考地址:https://www.tcpdump.org/manpages/libpcap-1.10.1/pcap_datalink.3pcap.html
创建嗅探会话的任务非常简单。为此,我们使用 pcap_open_live(3PCAP)。这个函数的原型如下:
pcap_t *pcap_open_live(char *device, int snaplen, int promisc, int to_ms,
char *ebuf)
| 参数字段 | 说明 |
|---|---|
| 第一个参数 | 第一个参数是我们在上一节中指定的设备。 |
| 第二个参数 | snaplen 是一个整数,它定义了 pcap 捕获的最大字节数。 |
| 第三个参数 | promisc,当设置为 true 时,使接口进入混杂模式(然而,即使它设置为 false,在特定情况下,接口也可能处于混杂模式,无论如何)。 |
| 第四个参数 | to_ms 是以毫秒为单位的读取超时时间(值 0 表示没有超时;至少在某些平台上,这意味着您可能要等到足够数量的数据包到达才能看到任何数据包,因此您应该使用非零暂停) |
| 第五个参数 | ebuf 是一个字符串,我们可以在其中存储任何错误消息(就像我们在上面使用 errbuf 所做的那样)。 |
| 返回值 | 该函数返回我们的会话处理程序 |
为了编译程序,我们调用 pcap_compile()。原型将其定义为:
int pcap_compile(pcap_t *p, struct bpf_program *fp, char *str, int optimize,
bpf_u_int32 netmask)
| 参数字段说明 | 描述 |
|---|---|
| 第一个参数 | 第一个参数是我们的会话句柄(pcap_t *handle 在我们之前的例子中)。 |
| 第二个参数 | 我们将存储 过滤器编译版本的位置引用。 |
| 第三个参数 | 表达式本身;格式:常规字符串 |
| 第四个参数 | 是一个整数,它决定表达式是否应该“优化”(0 为假,1 为真——标准内容)。 |
| 第五个参数 | 我们必须指定过滤器应用到的网络的网络掩码。 |
| 返回值 | 失败返回-1,其他值都是成功 |
表达式编译完成后,就可以应用它了。输入 pcap_setfilter()。按照我们解释 pcap 的格式,我们来看看原型:
int pcap_setfilter(pcap_t *p, struct bpf_program *fp)
| 参数说明 | 描述 |
|---|---|
| 第一个参数 | 我们的会话处理程序 |
| 第二个参数 | 是对表达式的编译版本的引用(可能与 pcap_compile() 的第二个参数相同的变量) |
从 pcap_t 读取下一个数据包
一次捕获一个数据包
# u_char *pcap_next(pcap_t *p, struct pcap_pkthdr *h)
const u_char *pcap_next(pcap_t *p, struct pcap_pkthdr *h);
| 参数具体说明 | 说明 |
|---|---|
| 第一个参数 | session handler |
| 第二个参数 | 是一个指向结构的指针,该结构包含有关数据包的一般信息,特别是它被嗅探的时间、该数据包的长度以及该特定部分的长度(例如,如果它被分段)。 |
| 返回值 | 返回指向此结构描述的数据包的 u_char 指针 |
进入循环
typedef void (*pcap_handler)(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes);
int pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
| 参数具体说明 | 说明 |
|---|---|
| 第一个参数 | 第一个参数是我们会话句柄 |
| 第二个参数 | 是一个整数,它告诉 pcap_loop() 在返回之前它应该嗅探多少数据包(负值意味着它应该嗅探直到发生错误) |
| 第三个参数 | 是回调函数的名称(只是它的标识符,没有括号) |
| 第四个参数 | 最后一个参数在某些应用程序中很有用,但很多时候只是简单地设置为 NULL。假设除了 pcap_loop() 发送的参数之外,我们还有自己希望发送给回调函数的参数 |
pcap_loop() 处理来自实时捕获或“保存文件”的数据包,直到处理完 cnt 数据包,从“保存文件”读取时到达“保存文件”的末尾,调用 pcap_breakloop(3PCAP),或发生错误。当实时数据包缓冲区超时发生时,它不会返回。 cnt 的值 -1 或 0 等价于无穷大,因此数据包将一直处理,直到出现另一个结束条件。
pcap_dispatch() 的用法几乎和pcap_loop相同。这两个函数之间的唯一区别是 pcap_dispatch() 将只处理它从系统接收到的第一批数据包,而 pcap_loop() 将继续处理数据包或批量数据包,直到数据包计数用完。有关它们之间差异的更深入讨论,请参见手册页。
#include <pcap/pcap.h>
char *pcap_geterr(pcap_t *p);
void pcap_perror(pcap_t *p, const char *prefix);
pcap_geterr() 返回与最后一个 pcap 库错误有关的错误文本。
注意:在传递给它的 pcap_t 关闭后,它返回的指针将不再指向有效的错误消息字符串;您必须在关闭 pcap_t 之前使用或复制该字符串。
pcap_perror() 在 stderr 上打印最后一个 pcap 库错误的文本,前缀为 prefix。
参考地址: