诅咒的IP地址表示

2020-12-27 07:27:28

为了编写一个快速的IPv4 + 6解析器,我编写了aslow-but-I-think-correct解析器,用作比较的基础。这样做时,我发现了我以前没有意识到的更多被诅咒的IP地址表示形式。让我们一起探索吧!

我们以IPv4和IPv6(我称之为它们的“规范形式”)开始:192.168.0.1和1:2:3:4:5:6:7:8。各种规范将其称为“点分四边形”(更具体地说,是“点分十进制”),点分隔的字段分别代表1个字节;和“冒号十六进制”,冒号分隔的字段,每个字段代表2个字节。

复杂性的头几位来自IPv6。以规范的形式,通用地址将在中间以长期的零结束。因此,::允许您忽略1个或多个16位零块:1:2 :: 3:4表示1:2:0:0:0:0:3:4

接下来,出于被诅咒的历史原因,IPv6允许您以点分四边形形式写入地址的最后32位。有效地,您可以将IPv4地址放到IPv6地址的末尾! 1:2:3:4:5:6:77.77.88.88表示1:2:3:4:5:6:4d4d:5858。

::的存在在解析中引入了一个令人烦恼的边缘情况::: ::可以位于地址的开头或结尾,并且地址的“空”面不是16位字段之一。 :: 1表示0:0:0:0:0:0:0,1 ::表示1:0:0:0:0:0:0:0,::表示0:0:0: 0:0:0:0:0。这是::规则的自然结果,但是它使解析器的编写有点烦人。

IPv6的最后一条规则:从技术上讲,每个冒号十六进制字段都是4个十六进制数字,但是您可以忽略前导零,就像我一直做的那样。完全规范地说,::是0000:0000:0000:0000:0000:0000:0000:0000:0000。我向疏散恐惧者致歉。

有趣的是,IPv4的文本表示从未在任何文档中标准化,直到IPv6的古怪的“点缀四边形”表示法需要语法。因此,这是一个事实上的标准,主要归结为“ 4.2BSD了解什么?”,以及“其他OS复制4.2BSD时保留了什么?”

哦,伙计,把自己绑起来,因为4.2BSD肯定有一些怪胎!让我们以192.168.140.255为例。人们会看着那是一个IPv4地址,“是的,那肯定是IPv4地址。”我们还能怎么写那个完全相同的地址?

这是相同的IP地址:3232271615。将IP地址的4个字节解释为big-endian unsigned32-bit integer,然后打印出来即可。这导致了一个经典的客厅技巧:如果您尝试访问http:// 3232271615,Chrome将加载http://192.168.140.255。

好的,但这有点明智,对吧? IPv4地址为4个字节,因此将其打印为一个数字会有点不友好,但是看起来很合理吧?

0300.0250.0214.0377怎么样?地址仍然相同。虚线四边形,但每个字段均以八进制写出。

如果支持八进制,则您可能想知道十六进制。而且您是对的!根据4.2BSD,192.168.140.255也是0xc0.0xa8.0x8c.0xff。

现在,请记住在我们使用CIDR(无类域间路由)之前,IPv4地址是Class A,Class B还是ClassC。那是一个奇怪的时代。

那段奇怪的时间变成了IP地址!从技术上讲,熟悉的192.168.140.255表示法是“ C类”表示法。您也可以用“ B类”表示法将该地址写为192.168.36095,或者用“ A类”表示法则写为192.11046143。我们正在做的是将地址的最后字节转换为16位或24位整数字段。

顺便说一下,这就是为什么ping之类的实用程序会接受127.0.0.1之类的看起来怪异的地址(如127.1)的原因。与IPv6不同,它没有进行某种“缺失字段为零”的扩展。 127.1是“网络127的主机1”的A类表示法,其中1是24位数字。

最后,我们来谈谈未指定的行为的最后一点:IPv4地址是否在每个四边形中允许无数个前导零?豪利时(Oris)最多3位数字?普遍认为001.002.003.004有效。 0000000001.0000000002.0000000003.000000004呢?

您可能还想知道是否应该将这些数字中的任何一个都读为八进制,因为我们前面说过,前导零可能被解释为八进制。这取决于!有两种实现方式,但是大多数现代实现方式都放弃了八进制和十六进制表示法,并将前导0视为十进制。

零开头的辩论在某种程度上也影响了IPv6。 000001 :: 00001.00002.00003.00004是有效的IPv6地址(“常见”格式为1 :: 1.2.3.4或1 :: 102:304)吗?大多数现代解析器似乎在表示中都允许无限制的前导零,这可能是因为它们依赖于某些实现该行为的“解析整数”库。

因此,我们到达了痛苦的终点。如果要真正解析IP地址,这是您必须忍受的废话。

目前,我的慢速参考解析器放弃了很多旧行李,并坚持我认为是这些可能性的明智子集。它了解:

对于IPv6,它了解规范的冒号十六进制形式以及::和尾随IPv4样式(尾随IPv4遵循与上一条推文相同的规则)。每个字段都可以使用任意数量的前导零。

关于最后一个,“带有嵌入的点分十进制的IPv6”格式,我感到不安。 我的参考解析器(Go的net.ParseIP)可以理解它,但是在现实世界中它不再那么有用了。 在IPv6诞生之初,您的想法是可以通过在冒号前面加上一对冒号来将地址升级到IPv6,如:: 1.2.3.4中所示,但是现代的转换机制不再提供像这样明确的功能,因此表示法不 真的在野外露面。