TCP/ICMP头部长度非2字节倍数时的校验和计算问题
TCP/ICMP校验和单字节处理及代码修复
问题1:单字节剩余时的校验和计算规则
- TCP、ICMP的校验和计算遵循大端字节序规则,当数据长度为奇数(剩余1字节)时,将该字节作为16位数值的高字节,低字节补0,组成完整的16位字后参与求和计算。
- 示例:剩余字节为
0x63时,转换为0x6300这个16位值,再纳入校验和累加流程。
问题2:代码修复与单字节处理实现
你的代码仅处理了偶数长度的分段(size/2次循环),完全遗漏了最后一个奇数字节。以下是修复后的代码,同时修正原代码中初始化和进位处理的问题:
#include <stdint.h> #include <arpa/inet.h> #include <stdio.h> int calculate_checksum(void *vdata, size_t size) { uint16_t *ptr = vdata; uint32_t sum = 0; // 用32位变量存储累加和,简化溢出处理 printf("size = %zu\n", size); // 处理所有完整的16位字 while (size >= 2) { sum += ntohs(*ptr); printf("0x%x 0x%x\n", ntohs(*ptr), (uint16_t)(sum & 0xFFFF)); ptr++; size -= 2; } // 处理剩余的单个字节 if (size == 1) { uint16_t last_word = *(uint8_t *)ptr << 8; // 单字节作为高字节,低字节补0 sum += last_word; printf("0x%x 0x%x\n", last_word, (uint16_t)(sum & 0xFFFF)); } // 统一处理累加过程中的进位:将高16位加到低16位,直到无进位 while (sum >> 16) { sum = (sum & 0xFFFF) + (sum >> 16); } // 取反得到校验和,转换为网络字节序返回 uint16_t checksum = ~sum & 0xFFFF; printf("checksum = 0x%x\n", checksum); return htons(checksum); }
关键修复点说明
- 单字节处理逻辑:当
size剩余1时,将最后一个字节左移8位(大端序补0),组成16位字加入累加和。 - 累加和变量优化:改用
uint32_t存储累加和,避免频繁判断溢出,最后统一处理进位更简洁。 - 进位处理标准化:循环将高16位的进位合并到低16位,符合TCP/IP校验和的标准计算流程。
- 初始化修正:原代码
sum = -0xffff错误,标准校验和初始化应为0,累加完成后取反得到结果。
内容的提问来源于stack exchange,提问作者user786
相关产品推荐
相关产品推荐

