pcap文件头需用#pragma pack(1)禁用填充并严格按spec顺序定义字段,注意小端字节序;包头后紧接帧数据,须区分caplen与len;linktype决定解析逻辑,需校验;mmap加速读取但须检查magic和边界。

pcap文件头怎么映射成C++结构体
直接用 struct 按字节对齐硬套是错的,多数人栽在这儿。pcap文件头前24字节是固定格式,但平台默认对齐(如x86_64下uint32_t可能被编译器塞填充字节)会导致sizeof不等于24,一读就错位。
- 必须显式禁用结构体填充:
#pragma pack(1)或__attribute__((packed))(GCC/Clang),MSVC用#pragma pack(push, 1) - 字段顺序必须严格按pcap spec:magic、version_major、version_minor、thiszone、sigfigs、snaplen、linktype,一个不能换
- 注意字节序:pcap是小端(little-endian),x86系没问题;但若在ARM大端模式下解析,得手动翻转字段值
#pragma pack(1)
struct pcap_file_header {
uint32_t magic;
uint16_t version_major;
uint16_t version_minor;
int32_t thiszone;
uint32_t sigfigs;
uint32_t snaplen;
uint32_t linktype;
};
pcap包头(pcap_pkthdr)解析常见崩溃点
读完文件头后,每个数据包前都有一个16字节的包头,它不是独立结构体——它和后续原始帧数据紧挨着。很多人用 fread(&hdr, sizeof(hdr), 1, fp) 后直接访问 hdr.len,却忘了这个长度是原始帧长度(含padding),而 hdr.caplen 才是实际捕获长度(受snaplen限制)。
- 别假设
caplen == len,Wireshark抓包时若设了snapshot length(如65535→68),caplen就会远小于len - 包头本身不带时间戳单位信息,
ts_sec和ts_usec是秒+微秒,但pcap-ng里可能是纳秒,混用会出时间乱跳 - 用
fread读包头后,必须检查返回值是否为1,文件末尾或损坏时容易少读,导致后续所有指针偏移全错
链路层类型(linktype)决定怎么解帧
linktype 不是装饰字段,它直接决定你接下来该用哪套解析逻辑。比如值为1(DLT_EN10MB)是Ethernet II,值为101(DLT_LINUX_SLL)是Linux cooked capture,字段布局完全不同。
- 查表用
pcap_datalink_val_to_name()(libpcap)或硬编码对照:常见值有1(Ethernet)、12(PPP)、101(SLL)、228(IPv4 raw) - 别跳过校验:如果
linktype是228(raw IP),帧数据开头就是IP头,没有以太网头;误当Ethernet解析会把IP头当MAC地址读,立刻崩溃 - 某些设备导出pcap时会伪造
linktype(如写成1但实际是PPPoE),需结合帧内容首字节(如0x45=IPv4)做二次验证
内存映射(mmap)读pcap比fread快,但要注意边界
大pcap文件(>1GB)用 fread 循环读,IO和拷贝开销明显;用 mmap 直接映射到内存,解析速度能提3–5倍,但新手常因越界访问段错误。
- 映射长度必须是文件大小,且需用
stat()精确获取,不能靠循环读到EOF估算 - 映射后不能直接把文件头地址强转为
pcap_file_header*——要确保映射起始地址满足结构体对齐要求(通常没问题),但必须检查magic字段是否合法(如0xa1b2c3d4或0xd4c3b2a1),否则说明文件损坏或非pcap - 映射区域包含所有包数据,但每个包的
caplen是运行时才知道的,必须逐个计算偏移,不能预分配固定大小缓冲区
最易被忽略的是:pcap文件末尾可能有不完整包(只剩包头没帧数据),caplen > 剩余字节数 时必须截断或跳过,否则 memcpy 会越界——这个判断不在标准文档里,得自己加。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











