阅读关于如何以最佳方式表示IPv6地址的RFC建议(RFC 5952),我尝试在c++中实现一个函数,将字节数组转换为适当的文本表示,即std::string
。
为了检查代码的正确性,我将结果与inet_ntop
(#include <arpa/inet.h>
)返回的结果进行比较。请注意,我实际上使用的是windows等效的#include <ws2tcpip.h>
。
在大多数情况下,我的函数具有相同的行为,并且我完全理解底层规则(省略前导零,通过将其替换为": "来压缩最长的零块,等等)。
但是有趣的部分是下面的:据我所知,对于一些特殊地址(IPv4映射、IPv4兼容和IPv4转换的IPv6地址,请参见RFC 2765),建议用IPv4地址常用的点分十进制表示最后4个字节的十六进制。
例如,::ffff:0:168.0.0.1
,::ffff:168.0.0.1
和::168.0.0.1
在其推荐的文本表示中都是有效的IPv6地址,inet_ntop
也得出了这个结论。
现在是我的问题:根据inet_ntop
, IPv6地址0:0:0:0:0:ffff:0.0.1.1
被缩短为::ffff:0:101
,再次选择十六进制表示。这种行为的原因是什么?我认为,因为我们在这里有一个特殊的地址前缀,无论最后4字节块的前两个字节是零,都将使用点分十进制表示法,因此将其写为::ffff:0.0.1.1
。是我误解了RFC的建议,还是inet_ntop
在这方面不一致?我观察到inet_ntop
在我所有其他的测试用例中都非常符合rfc。
我希望你能帮助我。
编辑:经过更多的测试,inet_ntop
似乎总是选择十六进制记数法而不是点分十进制,即使IPv6地址实际上只是一些特殊的嵌入式IPv4地址,如果最后4字节块的前两个字节为零。
在我的Ubuntu (glibc 2.31)上,我得到:
::ffff:0:168.0.0.1 -> ::ffff:0:a800:1 *
::ffff:168.0.0.1 -> ::ffff:168.0.0.1
::168.0.0.1 -> ::168.0.0.1
0:0:0:0:0:ffff:0.0.1.1 -> ::ffff:0.0.1.1 *
,其中两个标记的似乎与您的结果不同,表明此实现不识别::ffff:0
前缀,但不太讨厌嵌入的0.0.1.1
。
然而,使用全零前缀,我得到了类似的结果:如果IPv4地址的前两个字节为零,输出格式改变:
::1.2.3.4 -> ::1.2.3.4
::0.2.3.4 -> ::0.2.3.4
::0.0.3.4 -> ::304
::0.0.0.4 -> ::4
这可能是因为::1
不应该变成::0.0.0.1
,但这意味着必须为混合表法中显示的IPv4地址画一条线,至少要用全零前缀。
那么,这可能只是你的库的一个错误或怪癖吗?如果它确实对全零前缀和其他前缀表现出类似的行为,也许他们只是决定采取简单的方式,平等对待所有前缀。据我所知,整个0/8
块仍然保留给"本地识别",所以我想知道0.0.x.x
这样的地址是否会嵌入到IPv6中。(虽然我不知道)
RFC还将混合表示法仅指定为"recommended &",因此我们不能真的说您的实现是错误的。