摘要

  • Barry 的 README 将 ::/0 写作信任锚的默认 IPv6 资源,而议题 7 报告称,相同写法放入证书和 ROA 的资源字段时会被 Repository Descriptor 解析器拒绝。
  • 固定到当前提交的代码允许冒号与斜杠出现在已经开始的无引号字符串里,却只允许字母、数字、$ 或 开始这个字符串。因此,0::/0 能形成 token,::/0 则在 IP 前缀解析器运行前失败。
  • RFC 4291 认可 ::/0 是合法的 IPv6 前缀;RFC 5952 又使它成为规范输出。0::/0 的语义相同,但不是最大压缩后的表示。
  • 现有材料没有证明 Barry 生成过无效对象、依赖方验证程序见过该对象,也没有证明 LACNIC 生产系统或路由受到影响。合适的改进是一张带阶段码的“词法分析到 DER”一致性表。

一个不改变地址空间的零

问题只隔着一个字符:

::/0
0::/0

从 IPv6 的意义看,两者都是 128 位全零地址加零长度前缀,覆盖范围没有任何差别。可是 LACNIC/barry 的议题 7 记录了不同的程序路径:第一种写法触发意外字符错误,第二种写法被报告为当前绕行办法。

这个差异对于 RPKI 测试材料生成器尤其重要。Barry 的用途之一,是构造刻意不合规范的对象,观察依赖方软件如何处理。可是“负面测试”必须从一条可信的正向链路开始:合法文本先变成明确的 IP 值,再变成可核对的对象字节。若规范文本连 token 都没形成,就不能把结果归因于证书、ROA 或依赖方验证。后面的对象根本不存在。

最有利于项目的解释也应写清。Barry 的仓库元数据把它描述为一个生成工具,而不是生产服务。固定到被检查提交的 README 两次说明项目或 Repository Descriptor 规范仍在开发中,并提醒 1.0 之前可能出现不兼容变更。抓取时,release 列表和tag 列表都是空的。公开一个带最小示例的议题,正是原型阶段应有的透明度,不是生产事故证据。

示例与错误轨迹并非逐字节相同

议题在 2026 年 8 月 28 日开启。截至 9 月 6 日证据截止时间,它仍为开放状态,没有标签、评论或后续更新。GitHub 将报告者与仓库的关系标为 NONE。没有维护者公开确认复现、诊断或修复方案。

报告把 ::/0 放在两个位置:CA 证书的 IP 资源扩展,以及 ROA 的 ipAddrBlocks。正文给出的临时办法是改写成 0::/0。但材料内部有一个必须保留的细节:页面展示的复现描述文件在两个字段都写 ::/0;粘贴的错误轨迹却先打印了已经改成 0::/0 的第一个值,随后才在另一个字段开头的冒号处,以 Unexpected character: : (0x3a) 停止。

这并不推翻议题,只是约束结论。轨迹直接展示了一个“首字符为冒号”的失败。报告者称两个字段都会拒绝规范形式。公开记录却没有用同一次、逐字节完全相同的输入,独立证明两个字段都失败。测试收据之所以要保存原始字节与哈希,正是为了防止在复制绕行写法时悄悄改变了实验。

同样重要的是,轨迹里没有畸形证书、ROA、已发布仓库或验证器判决。它没有显示路由改变,也没有把 LACNIC 的生产 RPKI 系统置于路径上。把这些下游事件写进标题,会越过现有证据。

差异来自 token 的第一个字符

本次检查固定在 2026 年 9 月 1 日的提交 994a598321336baf1767f0fbfb460ed96c29fe4f。该提交的主题是实现 AKI 的 authorityCertIssuer,没有声称关闭议题 7。近期提交列表只能确定观察时间点,不能证明这个边界由哪次变更引入或消除。

在该提交的 src/rpki_tree.c 中,next_token 只有在首字符为字母数字、$ 或 时,才会开始读取无引号字符串。冒号不属于这些情况,于是流程落到 try_emoji;普通的 : 最终被报为意外字符。

字符串一旦开始,规则反而更宽。后续字符可以包含冒号和斜杠,主要排除的是空白与描述语言的结构分隔符。这足以解释为什么补零有效:0 满足起始条件,后面的 ::/0 便留在同一个 token 里。这里并不是 IPv6 库突然接受了另一种网络,而是词法分析器终于创建了字符串。

README 让这条边界更清楚。它给出 2001:db8::/64 这样的压缩地址;由于开头是十六进制数字,该示例能跨过入口规则。README 又把 ::/0 列为信任锚默认 IPv6 资源,恰好从入口不允许的字符开始。因此,“支持压缩 IPv6”这一概括过于粗糙:从数字之后开始压缩,与从第一个字符开始压缩,走的是不同词法路径。

真正理解 IP 前缀的代码在后面。src/field.c 中的 parse_ip_node 会在 / 处分割,用冒号判断 IPv6,调用 inet_pton,再读取前缀长度。规范输入在 token 阶段已被挡住,没有抵达这里。因此不能把错误说成 inet_pton 拒绝、前缀长度错误或字段绑定失败。

规范写法本应位于普通路径

RFC 4291 允许用 :: 压缩连续零字段,也明确以 :: 表示全零 IPv6 地址。IPv6 前缀由合法地址写法与斜杠后的长度组成,所以 ::/0 是合法的全空间前缀文本。

RFC 5952 将“接受什么”与“输出什么”分开。实现应接受 RFC 4291 的所有合法形式;生成文本时,则应采用规范表示并最大限度压缩零字段。因此 0::/0 与 ::/0 具有相同的地址族、数值和长度,但前者保留了规范输出会去掉的一个零字段。

一次受限的本地辅助检查用 Ruby IPAddr 读取 ::/0、0::/0、0000::/0 和完整展开的全零地址,得到相同数值与范围。这不是 Barry 的运行证据,也不比 RFC 更权威;它只支持把调查焦点放在词法入口,而不是虚构语义差异。

到对象层时,表示又发生变化。RFC 3779 使用 DER BIT STRING 编码 IP 资源。覆盖全部地址的零长度前缀以 03 01 00 表示;描述文件中的原始拼写不会成为证书或 ROA 的属性。若输入停在词法分析阶段,就没有 DER 值可供验证器接受或拒绝。

一张小表即可锁定责任边界

需要的不是“全面支持 IPv6”这类宽泛承诺,而是一张版本化一致性表。每行保存描述文件的精确字节与哈希、目标字段、token 化结果、规范化后的地址族/数值/前缀长度、规范输出、解析器与生成器提交,以及在完成构造时的对象哈希。

终止阶段应使用固定值:descriptor-tokenize、prefix-parse、field-bind、object-build、DER-encode 或 RP-validate。这样,生成器入口的拒绝就不会被计为验证器失败。核心不变量也很小:parse(format(parse(input))) 必须保持地址族、数值、长度三元组;不同合法写法在成功生成时应汇聚为同一个 DER 前缀值。

这张表不认证 Barry,也不试图概括所有 IPv6 实现。它只回答一个可在本地复核的问题:到底接受了哪些字节、理解成什么值、创建了什么对象,以及最终是哪一层作出决定。对于负面测试生成器,这比一个总括性“通过/失败”更有用。

来源