我正在使用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可用on 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(从trunk构建)还是GCC 4.7(MacPorts端口)编译都存在问题,因此我认为问题在于套接字实现(与Mac OS一起打包)或Mac OS X本身.
最佳答案 BSD平台套件(不包括OpenBSD)以主机字节顺序显示IP偏移量和长度.所有其他平台以接收的网络字节顺序出现.这是一个“功能”,在
IP(4) – Internet Protocol的手册页中引用(FreeBSD,
OS X).
The ip_len and ip_off fields must be provided in host byte order .
All other fields must be provided in network byte order.
IP长度可以等于数据包长度 – FreeBSD / NetBSD中的IP头长度.
参考:Stevens / Fenner / Rudolph,Unix网络编程第1卷,第739页
我必须使用PGM网络协议的用户空间实现来处理这些异常,具体代码如下:
https://code.google.com/p/openpgm/source/browse/trunk/openpgm/pgm/packet_parse.c#76
检测AutoConf实际上非常烦人,我认为所有软件包都是基于每个平台进行硬编码的.我已经看到本周在这个问题上提出的错误报告(header byte order config options detected incorrectly).