C++构建原始UDP数据包异常:Wireshark无法正确识别数据
问题排查与修复方案
1. 字节序(端序)错误
IP和UDP头的核心字段(源/目的端口、IP总长度、UDP总长度等)必须使用网络字节序(大端序),如果直接用主机字节序(小端)赋值,会导致Wireshark解析时字节错位,进而错误计算数据偏移,把数据当成头的一部分、或头当成数据。
检查代码中这些字段的赋值,必须用htons()/htonl()转换:
// UDP头字段转换 udp_header.uh_sport = htons(local_port); udp_header.uh_dport = htons(target_port); udp_header.uh_ulen = htons(sizeof(UDPHeader) + data_len); // IP头字段转换 ip_header.ip_len = htons(sizeof(IPHeader) + sizeof(UDPHeader) + data_len); ip_header.ip_id = htons(12345); // 示例ID,按需修改
2. IP头校验和未正确计算
IP头要求必须计算校验和(仅覆盖IP头部分),校验和错误会让Wireshark判定IP包损坏,进而乱解析后续的UDP头和数据。
用标准校验和算法实现:
unsigned short checksum(unsigned short *buf, int nwords) { unsigned long sum = 0; for (; nwords > 0; nwords--) sum += *buf++; sum = (sum >> 16) + (sum & 0xffff); sum += (sum >> 16); return ~sum; } // 赋值IP头后计算校验和 ip_header.ip_sum = 0; // 先清零 ip_header.ip_sum = checksum((unsigned short *)&ip_header, sizeof(IPHeader)/2);
3. UDP校验和错误(关键可选项)
UDP校验和虽可选,但错误的校验和会让Wireshark标记包损坏。UDP校验和需要覆盖UDP头、数据,还要加上伪IP头(含源IP、目的IP、协议类型、UDP长度)。
实现示例:
#pragma pack(1) struct PseudoHeader { unsigned int src_ip; unsigned int dst_ip; unsigned char zero; unsigned char protocol; unsigned short udp_len; }; #pragma pack() // 构造伪头并计算校验和 PseudoHeader ph; ph.src_ip = inet_addr(source_ip); ph.dst_ip = inet_addr(target_ip); ph.zero = 0; ph.protocol = IPPROTO_UDP; ph.udp_len = htons(sizeof(UDPHeader) + data_len); // 拼接伪头、UDP头、数据到缓冲区 char checksum_buf[sizeof(PseudoHeader) + sizeof(UDPHeader) + 1024]; memcpy(checksum_buf, &ph, sizeof(PseudoHeader)); memcpy(checksum_buf + sizeof(PseudoHeader), &udp_header, sizeof(UDPHeader)); memcpy(checksum_buf + sizeof(PseudoHeader) + sizeof(UDPHeader), data, data_len); udp_header.uh_sum = 0; udp_header.uh_sum = checksum((unsigned short *)checksum_buf, (sizeof(PseudoHeader) + sizeof(UDPHeader) + data_len + 1)/2);
若不想用校验和,直接把uh_sum设为0即可,但部分环境会对0校验和的包做特殊处理。
4. 结构体内存对齐问题
C++结构体默认会有内存填充,若IP/UDP头结构体因对齐产生多余字节,会导致实际发送的头与协议标准结构不符,数据偏移完全错误。
给结构体加上强制对齐属性:
#pragma pack(1) struct IPHeader { unsigned char ip_hl:4, ip_v:4; unsigned char ip_tos; unsigned short ip_len; unsigned short ip_id; unsigned short ip_off; unsigned char ip_ttl; unsigned char ip_p; unsigned short ip_sum; unsigned int ip_src; unsigned int ip_dst; }; struct UDPHeader { unsigned short uh_sport; unsigned short uh_dport; unsigned short uh_ulen; unsigned short uh_sum; }; #pragma pack()
GCC/Clang也可使用__attribute__((packed))替代#pragma pack。
5. IP头长度字段错误
IP头的ip_hl字段表示头长度(单位是4字节),默认IP头为20字节,所以必须设为5:
ip_header.ip_hl = 5; // 5*4=20字节,标准IPv4头长度 ip_header.ip_v = 4; // 指定IPv4协议
你提到十六进制可见68-69但Wireshark显示ff11,大概率是UDP头长度字段或IP头总长度的字节序错误,导致Wireshark计算数据起始位置时偏移出错,把数据解析成了错误内容。优先检查字节序和头长度的赋值,再验证校验和问题。
内容的提问来源于stack exchange,提问作者ni1
相关产品推荐
相关产品推荐

