我正在使用 Mac 10.6.8 中的套接字进行编程。每当我收到一个数据包时,它都会从 IP 标头开始。我一直在使用 Wireshark 来分析传入的数据包,我注意到我机器的套接字实现会不断地更改 IP 标头中的“总长度”字段。具体来说,它将减去 IP 标头长度并反转字节(从网络到主机顺序)。
例如,这是 Wireshark 报告的 IP 标头的开头:
45 c0 00 38 ...
分解如下:
- 4 位 (0x4):IP 版本:4
- 4 位 (0x5):IP 标头长度:5 个字(20 字节)
- 8 位 (0xc0):区分服务标志
- 16位(0x0038):总长度:56字节
但是,当我打印为同一个数据包填充的缓冲区内容时recvfrom
,我得到了不同的 lede:
ssize_t recvbytes = recvfrom(sock->fd, buffer, size, /*flags=*/0,
(struct sockaddr*)src, &src_len);
返回
45 c0 24 00 ...
- 4 位 (0x4):IP 版本:4
- 4 位 (0x5):IP 标头长度:5 个字(20 字节)
- 8 位 (0xc0):区分服务标志
- 16 位(0x2400):总长度:9216 字节(?!)
我发现在访问缓冲区之前,套接字实现是读取总长度,减去 IP 标头长度,然后按主机顺序(我机器上的小端序)而不是网络顺序(大端序)写回. 在此示例中,这意味着:
- 读取总长度:0x0038 = 56
- 减去标题长度:56 - 20 = 36
- 按主机顺序写回:36 = 0x0024(大端)= 0x2400(小端=我机器上的主机顺序)
问题变得更糟。它不仅会改变最外层 IP 标头的总长度。它还将更改内部 IP 报头的总长度字段,例如,埋在 ICMP“超时”消息中的字段(它必须包括丢弃数据包的原始 IP 报头)。更有趣的是,它不会从内部标头中减去 IP 标头长度;它只是颠倒了字节顺序。
这是否发生在其他人身上?它是我不知道的标准的一部分吗?有没有办法修复我机器的套接字实现以停止篡改数据包?Wireshark 如何解决这个问题?
提前感谢您的考虑。
编辑:我的代码和 Makefile在 GitHub 上可用。我编写了一个fixip_osx
函数来验证 IP 校验和:
https://github.com/thejohnfreeman/netutils/blob/master/lib/ip.c
void fixip_osx(struct ip* ip) {
/* Something on my Mac subtracts the header length from `ip_len` and stores
* it in host order (little endian). */
u16_t ip_hdrlen = ip->ip_hl << 2;
u16_t ip_totlen = ip->ip_len + ip_hdrlen;
ip->ip_len = htons(ip_totlen);
}
但是,当有效负载包含另一个 IP 标头时,验证 ICMP 校验和仍然是一个问题。
无论我使用 Clang 3.2(从主干构建)还是 GCC 4.7(MacPorts 端口)编译都存在问题,所以我认为问题在于套接字实现(与 Mac OS 一起打包)或 Mac OS X 本身。