Windows下使用C++与Npcap库区分IPv4和IPv6数据包的方法问询
如何用C++和Npcap判断捕获的数据包是IPv4还是IPv6
原代码的问题
- EtherType注释与值不匹配:比如0x0806是ARP协议,并非Wi-Fi WEP;0x888E是802.1X认证协议,不是Wi-Fi加密帧,错误的注释和条件会导致误判。
- 逻辑错误:将加密协议(WEP/WPA)的EtherType直接作为IPv4/IPv6的判断条件,实际上加密帧内部才封装IP包,不能直接通过这些字段判断IP版本。
- 未区分链路层类型:没有考虑以太网、Wi-Fi等不同链路层的帧结构差异,统一用固定位置判断EtherType会失效。
正确实现思路
Npcap捕获的数据包链路层类型可通过pcap_datalink()获取,不同链路层的帧结构不同,需分情况解析:
- 以太网链路层(DLT_EN10MB):
- 标准以太网头部为14字节,EtherType字段位于第12、13字节(大端序),0x0800对应IPv4,0x86DD对应IPv6。
- 若为802.3帧(长度字段<1500),需解析LLC+SNAP封装,SNAP中的EtherType字段对应IP版本。
- Wi-Fi链路层(DLT_IEEE802_11):
- 先解析802.11帧头的Frame Control字段,确认是数据帧。
- 若帧加密(WEP/WPA),需确保Npcap已配置自动解密(或自行处理解密),之后找到SNAP封装中的EtherType字段判断IP版本。
示例代码
#include <pcap.h> #include <stdint.h> #include <arpa/inet.h> // 以太网头部结构 typedef struct ethernet_header { uint8_t dst_mac[6]; uint8_t src_mac[6]; uint16_t ether_type; // 大端序 } ethernet_header_t; // LLC+SNAP头部结构(用于802.3帧) typedef struct llc_snap_header { uint8_t dsap; uint8_t ssap; uint8_t control; uint8_t oui[3]; uint16_t ether_type; // 大端序 } llc_snap_header_t; // 802.11帧控制字段结构 typedef struct ieee80211_frame_control { uint8_t protocol_version : 2; uint8_t type : 2; uint8_t subtype : 4; uint8_t to_ds : 1; uint8_t from_ds : 1; uint8_t more_frag : 1; uint8_t retry : 1; uint8_t pwr_mgmt : 1; uint8_t more_data : 1; uint8_t wep : 1; uint8_t order : 1; } ieee80211_frame_control_t; // 判断是否为IPv4数据包 bool IsIpv4Packet(const u_char* packetData, int datalink_type) { switch (datalink_type) { case DLT_EN10MB: { // 以太网链路层 ethernet_header_t* eth_hdr = (ethernet_header_t*)packetData; uint16_t ether_type = ntohs(eth_hdr->ether_type); if (ether_type == 0x0800) { // IPv4 return true; } else if (ether_type <= 1500) { // 802.3帧,解析LLC+SNAP llc_snap_header_t* llc_snap = (llc_snap_header_t*)(packetData + sizeof(ethernet_header_t)); // 检查是否为SNAP封装(DSAP=SSAP=0xAA,Control=0x03) if (llc_snap->dsap == 0xAA && llc_snap->ssap == 0xAA && llc_snap->control == 0x03) { return ntohs(llc_snap->ether_type) == 0x0800; } } break; } case DLT_IEEE802_11: { // Wi-Fi链路层 ieee80211_frame_control_t* fc = (ieee80211_frame_control_t*)packetData; // 仅处理数据帧(Type=2) if (fc->type != 2) { return false; } // 计算802.11头部长度:根据地址字段数量调整 int hdr_len = 24; // 基本数据帧头部长度 if (fc->to_ds && fc->from_ds) { hdr_len += 6; // 包含地址4字段 } // 跳过加密部分(Npcap已自动解密时),解析LLC+SNAP llc_snap_header_t* llc_snap = (llc_snap_header_t*)(packetData + hdr_len); if (llc_snap->dsap == 0xAA && llc_snap->ssap == 0xAA && llc_snap->control == 0x03) { return ntohs(llc_snap->ether_type) == 0x0800; } break; } // 可扩展支持其他链路层类型,如DLT_IP等 default: break; } return false; } // 判断是否为IPv6数据包 bool IsIpv6Packet(const u_char* packetData, int datalink_type) { switch (datalink_type) { case DLT_EN10MB: { // 以太网链路层 ethernet_header_t* eth_hdr = (ethernet_header_t*)packetData; uint16_t ether_type = ntohs(eth_hdr->ether_type); if (ether_type == 0x86DD) { // IPv6 return true; } else if (ether_type <= 1500) { // 802.3帧,解析LLC+SNAP llc_snap_header_t* llc_snap = (llc_snap_header_t*)(packetData + sizeof(ethernet_header_t)); if (llc_snap->dsap == 0xAA && llc_snap->ssap == 0xAA && llc_snap->control == 0x03) { return ntohs(llc_snap->ether_type) == 0x86DD; } } break; } case DLT_IEEE802_11: { // Wi-Fi链路层 ieee80211_frame_control_t* fc = (ieee80211_frame_control_t*)packetData; if (fc->type != 2) { return false; } int hdr_len = 24; if (fc->to_ds && fc->from_ds) { hdr_len += 6; } llc_snap_header_t* llc_snap = (llc_snap_header_t*)(packetData + hdr_len); if (llc_snap->dsap == 0xAA && llc_snap->ssap == 0xAA && llc_snap->control == 0x03) { return ntohs(llc_snap->ether_type) == 0x86DD; } break; } default: break; } return false; }
使用说明
- 在捕获数据包前,通过
pcap_datalink(handle)获取当前设备的链路层类型,传入判断函数。 - Wi-Fi帧的处理需确保Npcap已启用“Monitor Mode”并配置自动解密(若需处理加密帧),否则需自行实现解密逻辑。
- 可根据实际需求扩展其他链路层类型的支持,如DLT_IP(直接IP数据包)等。
内容的提问来源于stack exchange,提问作者Stephen
相关产品推荐
相关产品推荐

