MINIBLOG

Blog Note Tags Links About
Home Search
Apr 16, 2026
miniyuan

计算机网络 Lab4 路由协议


libpcap 库

libpcap 的整体执行流程如下:

图 1:libpcap 整体执行流程
图 1:libpcap 整体执行流程

核心数据结构

  1. 会话句柄

    typedef struct pcap pcap_t;           // 捕获会话句柄(不透明结构)
    typedef struct pcap_dumper pcap_dumper_t;  // 转储文件句柄

    注:句柄可以类比函数指针,但是不完全相同。函数指针是内存地址,可以直接调用;句柄只是一个对象的标识,只能传入接口函数由其内部调用,实际上它可能是函数指针也可能仅仅是一个整数标识符。

  2. 数据包头部

    struct pcap_pkthdr {
        struct timeval ts;        /* 时间戳(捕获时间) */
        bpf_u_int32 caplen;       /* 实际捕获的数据长度(可能截断) */
        bpf_u_int32 len;          /* 数据包原始长度 */
    };

    其中:

    #include <sys/time.h>
    
    struct timeval {
        time_t      tv_sec;       // 秒数(从 1970-01-01 00:00:00 UTC 开始)
        suseconds_t tv_usec;      // 微秒数(0-999999)
    };

    注:caplen ⩽\leqslant⩽ len,当设置 snaplen 截断捕获时,两者可能不等。

  3. 回调函数

    在抓到包后调用,按用户自定义的方式处理数据包。

    typedef void (*pcap_handler)(u_char *user, const struct pcap_pkthdr *h, 
                                const u_char *bytes);
    • user:用户自定义数据
    • h:libpcap 提供的数据包的头部
    • bytes:libpcap 提供的数据包的实际内容(以太网帧开始)
  4. BPF 过滤器程序

    伯克利数据包过滤器(Berkeley Packet Filter)。

    struct bpf_program {
        u_int bf_len;             /* 指令数量 */
        struct bpf_insn *bf_insns;/* 指向过滤指令数组的指针 */
    };
  5. 网络设备信息

    typedef struct pcap_if {
        struct pcap_if *next;     /* 下一个设备 */
        char *name;               /* 设备名(如 "eth0") */
        char *description;        /* 设备描述 */
        struct pcap_addr *addresses; /* 地址列表 */
        u_int flags;              /* 标志(PCAP_IF_LOOPBACK等) */
    } pcap_if_t;
  6. 统计信息

    struct pcap_stat {
        u_int ps_recv;            /* 收到的包数 */
        u_int ps_drop;            /* 丢弃的包数(缓冲区满) */
        u_int ps_ifdrop;          /* 接口层丢弃的包数 */
    };

核心函数接口

  1. 设备查找与打开

    由于 PC 上有多个网卡(设备),比如有线网卡 wth0、无线网卡 wlan0、回环网卡 lo,所以我们必须指定捕获设备。

    获取设备信息:

    函数原型功能说明
    char *pcap_lookupdev(char *errbuf)返回第一个可用的非回环设备名(已废弃,建议用 pcap_findalldevs)
    int pcap_findalldevs(pcap_if_t **alldevsp, char *errbuf)获取所有可用网络设备列表,alldevsp 是一个在堆上的链表
    void pcap_freealldevs(pcap_if_t *alldevs)释放设备列表,也即释放堆上分配的内存
    int pcap_lookupnet(const char *device, bpf_u_int32 *netp, bpf_u_int32 *maskp, char *errbuf)获取设备的网络地址和掩码

    打开实时捕获设备:

    pcap_t *pcap_open_live(const char *device, int snaplen, 
                        int promisc, int to_ms, char *errbuf);
    • snaplen: 最大捕获字节数(通常设 65535 或 BUFSIZ)。
    • promisc: 1 开启混杂模式,0 非混杂。混杂模式下会接收所有经过设备的数据包。
    • to_ms: 读取超时(毫秒),0 表示无超时阻塞等待。

    离线操作:

    // 打开一个之前保存的抓包文件
    pcap_t *pcap_open_offline(const char *fname, char *errbuf);
    // 创建虚拟句柄(用于编译 BPF 过滤器)
    pcap_t *pcap_open_dead(int linktype, int snaplen); 
  2. 数据包捕获

    函数原型功能说明
    int pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user)循环捕获 cnt 个包(-1 表示无限),每次捕获调用 callback
    int pcap_dispatch(pcap_t *p, int cnt, pcap_handler callback, u_char *user)类似 loop,但超时或处理完缓冲区内数据即返回
    const u_char *pcap_next(pcap_t *p, struct pcap_pkthdr *h)捕获单个数据包,阻塞等待,返回指向原始数据包的指针
    int pcap_next_ex(pcap_t *p, struct pcap_pkthdr **pkt_header, const u_char **pkt_data)捕获单个包(返回 1:成功, 0:超时, -1:错误, -2:离线文件EOF)
    void pcap_breakloop(pcap_t *p)中断正在运行的 pcap_loop/dispatch
  3. 数据包过滤

    // 编译过滤表达式
    int pcap_compile(pcap_t *p, struct bpf_program *fp, 
                    char *str, int optimize, bpf_u_int32 netmask);
    
    // 应用过滤器
    int pcap_setfilter(pcap_t *p, struct bpf_program *fp);
    
    // 释放编译后的 BPF 程序内存
    void pcap_freecode(struct bpf_program *fp);
    • p:提供编译所需的链路层类型、快照长度。
    • fp:是输出参数,编译生成的 BPF 指令会存储在这里。
    • str:人类可读的过滤表达式字符串,例如 "tcp port 80" 或 "host 192.168.1.1"。
    • optimize:是否优化生成的 BPF 指令,1 表示优化,0 表示不优化。
    • netmask:网络掩码,用于解析 net/mask 这类过滤器;不需要时可传 PCAP_NETMASK_UNKNOWN(通常就是 0)。

    示例流程:

    pcap_t *handle = pcap_open_live("eth0", 65535, 1, 1000, errbuf);
    struct bpf_program fp;
    pcap_compile(handle, &fp, "icmp or tcp port 80", 1, net);
    pcap_setfilter(handle, &fp);
    pcap_freecode(&fp);
  4. 数据包转储

    // 打开转储文件
    pcap_dumper_t *pcap_dump_open(pcap_t *p, const char *fname);
    pcap_dumper_t *pcap_dump_fopen(pcap_t *p, FILE *fp);
    
    // 写入数据包(可作为 pcap_loop 的回调函数)
    void pcap_dump(u_char *user, struct pcap_pkthdr *h, u_char *sp);
    
    // 刷新缓冲区到磁盘
    int pcap_dump_flush(pcap_dumper_t *p);
    
    // 关闭转储文件
    void pcap_dump_close(pcap_dumper_t *p);

    注:

  5. 辅助与清理函数

    函数说明
    int pcap_datalink(pcap_t *p)获取链路层类型(如 Ethernet 为 1)
    int pcap_snapshot(pcap_t *p)获取 snaplen
    int pcap_stats(pcap_t *p, struct pcap_stat *ps)获取捕获统计
    char *pcap_geterr(pcap_t *p)获取错误信息
    void pcap_perror(pcap_t *p, char *prefix)打印错误信息
    void pcap_close(pcap_t *p)关闭会话并释放资源

典型使用流程

char errbuf[PCAP_ERRBUF_SIZE];
pcap_if_t *alldevs;
pcap_t *handle;
struct bpf_program fp;

// 1. 查找设备
pcap_findalldevs(&alldevs, errbuf);
// 2. 打开设备
handle = pcap_open_live(alldevs->name, 65535, 1, 1000, errbuf);
// 3. 编译并设置过滤器
pcap_compile(handle, &fp, "port 80", 0, PCAP_NETMASK_UNKNOWN);
pcap_setfilter(handle, &fp);
// 4. 循环捕获
pcap_loop(handle, 0, packet_handler, NULL);
// 5. 清理
pcap_freecode(&fp);
pcap_close(handle);
pcap_freealldevs(alldevs);

参考文档:

  • TCPDUMP & LIBPCAP 官方文档
  • Linux man page - pcap
  • Stanford libpcap Tutorial

common.h

网络设备有关数据结构

/* Network device information */
typedef struct {
    char name[32];       // Device name
    pcap_t *handle;      // pcap handle
    pthread_t thread_id; // Capture thread ID
    int index;           // Device index
} net_device_t;

/* Packet buffer entry */
typedef struct {
    net_device_t *device;          // Ingress device
    uint8_t data[PACKET_BUF_SIZE]; // Packet data
    uint32_t len;                  // Packet length
    uint64_t timestamp;            // Capture timestamp
} packet_entry_t;

/* Global packet buffer */
typedef struct {
    packet_entry_t packets[MAX_PACKETS];
    int head;
    int tail;             // circular buffer
    pthread_mutex_t lock; // mutex lock
} packet_buffer_t;

lab

第一次执行会写路由表,所以 Hub 中会出现 IPv6 包。

Makefile 的 test_hub 增加 sleep 1,保证避免过快 killall 导致 ping reply 没有记录

目录
  • libpcap 库
    • 核心数据结构
    • 核心函数接口
    • 典型使用流程
  • common.h
    • 网络设备有关数据结构
  • lab
© 2026 miniyuan. All rights reserved.
Go to miniyuan's GitHub repo