一些鲜为人知的 IP 地址怪异写法

原文链接:blog.dave.tf/post/ip-add...

原文作者:David Anderson

译者:菜小鸟魔王

为了开发一款高性能的 IPv4/IPv6 解析器,我首先构建了一个虽然效率不高但能确保正确的解析器作为参考。在此过程中,我意外发现了许多鲜为人知的 IP 地址怪异写法,下面我就带大家一探究竟!

先从标准格式入手:IPv4 的"192.168.0.1"采用 dotted quad(纯十进制点分四段的标准格式 )(业界也称 dotted decimal(纯十进制数值+点号分隔)),以点号分隔的各数值对应 1 字节;IPv6 的"1:2:3:4:5:6:7:8"则采用冒号分隔的十六进制格式,每组对应 2 字节数据。

IPv6 的复杂性首先体现在零压缩规则上。标准格式中连续的零值区块可以用双冒号"::"简写。例如"1:2::3:4"实际展开为"1:2:0:0:0:0:3:4",这种语法允许将任意长度的 16 位零值区块压缩为双冒号表示。

有趣的是,出于兼容性的考虑,IPv6 地址的最后 32 位允许采用 IPv4 的 dotted quad 格式。这种设计使得 IPv4 地址可以直接拼接在 IPv6 地址尾部!例如,1:2:3:4:5:6:77.77.88.88 实际上等效于 1:2:3:4:5:6:4d4d:5858(将十进制转换为十六进制后)。

更妙的是,缩写符号(::)也能与之结合使用。譬如 fe80::1.2.3.4 会被解析为 fe80:0:0:0:0:0:102:304。

但"::"的存在给地址解析带来了一种特殊的边界情况:当"::"出现在地址首尾时,需处理"空侧"的抽象范围,解析复杂度更高。例如"::1"展开后是 0:0:0:0:0:0:0:1,而"1::"则对应 1:0:0:0:0:0:0:0,甚至使用单独的"::"代表全零的地址。虽然这是双冒号逻辑的自然延伸,但确实增加了地址解析器的编写复杂度。

最后补充一个书写规范:严格来说,每个冒号分隔的十六进制段应当包含 4 位数字,但前导零可以省略(正如前文示例所示)。在最规范的全写形式中,"::"等价于 0000:0000:0000:0000:0000:0000:0000:0000,密集恐惧症患者请见谅。

以上便是 IPv6 的核心规则,现在让我们将目光投向IPv4!

有趣的是,在 IPv6 需要为其奇怪的"trailing dotted quad"语法制定规则之前,IPv4 的文本表示形式从未在任何文档中被正式标准化。因此,这实际上只是一种事实标准,其核心主要在于"4.2BSD 操作系统如何解析?"以及"其他操作系统在参考 4.2BSD 时保留了哪些规则?"

接下来的内容请做好心理准备,因为 4.2BSD 确实有一些非常古怪的观点!以 192.168.140.255 为例。这是一个大家看一眼就会说:"是的,这是一个 IPv4 地址"的例子。但我们还可以用哪些方式表示这个地址呢?

这也是同一个 IP 地址:3232271615。其原理是将 IP 地址的 4 个字节视为大端序无符号 32 位整数 (big-endian unsigned 32-bit integer)进行解析并输出。这引出了一个经典的小技巧:如果你尝试访问 http://3232271615,Chrome 会加载 http://192.168.140.255。

Okey,这也还算合理,对吧?IPv4 地址包含 4 个字节,将其作为单个数字输出虽然对人类不太友好,但大致上还是合理的,不是吗?

那 0300.0250.0214.0377 呢?这仍然是同一个地址。还是 dotted quad 形式,只是每个字段都采用八进制表示。

既然支持八进制,是不是还会支持十六进制。你的直觉是对的!根据 4.2BSD 的规则,192.168.140.255 还可以表示为 0xc0.0xa8.0x8c.0xff。

请记住,在 CIDR(无类别域间路由)出现之前,IPv4 地址分为 A 类、B 类或 C 类。这是一个奇怪的时代!

而那个时代甚至也影响到了 IP 地址的表示!我们熟悉的 192.168.140.255 本质上是「C 类」表示法。你也可以用「B 类」表示法写作 192.168.36095,或用「A 类」表示法写作 192.11046143。其本质是将地址的末尾字节合并为 16 位或 24 位整数字段。

这也解释了为何像 ping 这样的工具会接受 127.1 这种看似不规范的地址(对应 127.0.0.1)。与 IPv6 不同,它不会进行「缺失字段补零」的扩展,而是将 127.1 视为「A 类」表示法 ------ 即「网络 127 的主机 1」,其中 1 是一个 24 位的数字。

最后,还有一个未被明确定义的争议点:IPv4 地址的每个四元组是否允许无限前导零?还是最多 3 位?001.002.003.004 是公认有效的。但 0000000001.0000000002.0000000003.000000004 呢?

你可能还会疑惑:既然在计算机领域,前导零常被用于表示八进制(如前文所述(0300.0250.0214.0377 )),这些数字是否需要按八进制解析?也要看情况!有些系统两者都支持,但大多数较新的系统已废弃八进制/十六进制表示法,而是将前导零按十进制处理。

前导零的争议也波及到了 IPv6。例如,000001::00001.00002.00003.00004 是否合法(对应常规形式 1::1.2.3.4 或 1::102:304)?大多数较新的解析器似乎允许无限前导零,可能是因为它们依赖的某些「解析整数」的代码库实现了这种行为。

这个参考解析器(虽然效率不高)目前做了大量精简,舍弃了很多过时的规范,只专注支持我认为合理且必要的功能范围。具体来说它能处理:

  • 经典的 IPv4 关于IPv6地址中内嵌点分十进制的写法(例如::192.168.0.1),允许任意数量的前导零

  • 不支持处理 A/B 类网络划分标记法,或十六进制/八进制表示法

  • 不支持处理"uint32整型转写格式"(如将地址转换为3232235521这类单数字形式)

  • 对于 IPv6,它能理解规范的冒号分隔十六进制格式,以及 :: 符号和尾部 IPv4 风格(其中尾部 IPv4 遵循 "dotted decimal")。每个字段允许任意数量的前导零。

关于 IPv6 地址中内嵌 "dotted decimal" 的写法(例如::192.168.0.1),我还在犹豫。我选择作为参考的解析器(Go 的 net.ParseIP)能识别这种格式,但在实际应用场景中这种方式显得非常鸡肋。回想IPv6的初期阶段,这种写法被设想为一种过渡方案------通过在原有IPv4地址前添加双冒号(如::1.2.3.4),就能将其"升级"为IPv6地址。但现在的过渡机制已不再提供如此直白的转换方式,导致这类混合表示法在真实网络环境中近乎绝迹。

相关推荐
想用offer打牌3 小时前
MCP (Model Context Protocol) 技术理解 - 第二篇
后端·aigc·mcp
崔庆才丨静觅4 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60615 小时前
完成前端时间处理的另一块版图
前端·github·web components
KYGALYX5 小时前
服务异步通信
开发语言·后端·微服务·ruby
掘了5 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅5 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅5 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
爬山算法5 小时前
Hibernate(90)如何在故障注入测试中使用Hibernate?
java·后端·hibernate
崔庆才丨静觅5 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment6 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端