2026-10-30。里程碑 H3、H5、H6,以及测试中查出的网关转发问题。设计见 docs/hosting-h3-h5-h6-design.md。
内核:只改了路由容器建立网络的几行(IPv6 地址、网关 fe80::1)。接口:模块 API 没有变。重启:内核有改动,发布时重启一次。
节点 API 新增:ct.export、ct.import;route.set 接受 IPv6;vrrp.set 增加 mac;ct.create 和 ct.set_addr 的 addr6 开始生效。
路由和 NAT 模块只处理发给网卡自身 MAC 的帧,所以此前任何版本经虚拟地址都转发不了,只能 ping 通。原测试只 ping,没有发现。现在 VRRP 模块把发给虚拟 MAC 的帧交给后面的模块,转发出去的帧用虚拟 MAC 作源。这项修复已在 2026.10.30.7 发布。
同时加了:指定虚拟 MAC(<组>.mac),用于接管旧路由器时上游不用换绑;网卡不配自己的地址(ip nicN via 网关/掩码),这时上游只看到这个 MAC,备机完全静默。测试 vrrp_gw_test 用抓包确认了这两点。这种模式下同组路由器的优先级必须不同。
新模块 hostroutes。主机每 5 秒、容器变化时立即,用带密钥的 UDP 包告诉路由器自己运行中的路由容器地址;路由器写成自己的路由,所有者是 learned。不用 BGP。
| 情况 | 结果 |
|---|---|
| 另一台主机通告同一地址(容器迁移) | 路由立即改到新主机,写入日志 |
| 主机不再通告 | 约 20 秒后撤掉 |
| 密钥错误、重放、来源不是下一跳、不在 allow 内 | 不采纳 |
| 手工或面板设置的路由 | 不覆盖 |
默认用 ULA 加网关上的 NAT66,路由容器也一样;公网 IPv6 是网关上的规则,换地址不用动容器。写全局地址时,网关不转换,直接路由。容器的 /128 地址配网关 fe80::1,也可以只有 IPv6。
测试 routed6_test 通过:全局地址从外部可达、出站保持自己的地址;ULA 容器经 NAT66 出站,对方看到的是我们的地址;路由模块自己的 IPv6 路由生效;只有 IPv6 的容器可达;链路本地地址被拒绝。
测试的限制:这个测试是一台虚拟机同时做主机和网关,没有跑两台分开的情况。通过路由把 ULA 送到另一台网关再做 NAT66,要到面板实际绑定时才会走到。节点 API 的 ct.create 仍要求 addr4,只有 IPv6 的容器现在只能用底层设置建立。
面板依次调用:源主机 ct.stop、ct.export,目标主机 ct.import。传输的是容器自己的上层目录,不写临时文件,源主机不需要额外空间。使用一次性密钥,ChaCha20 加密并校验整个数据流。源主机上的数据不会被删,面板确认后再 ct.delete。
node_test 迁移了一个真实的 Debian 容器:内容、属主、硬链接数、符号链接、稀疏文件、目录权限、文件时间、FIFO、被删除的镜像文件(whiteout)、SSH 主机密钥都保留,原来的密钥能登录;密钥错误时导入失败、不启动;源容器之后能再启动,删除副本不影响它。
测试的限制:这个测试在同一台虚拟机上导出、导入,没有跨两台机器。接收方失败后,发送方要等到发送超时(最长 120 秒)才结束,这期间源容器不能启动。
tar、rsync -H、du 会把它们当成两个文件)。下一步修。完整回归 281 个单元测试通过,转发 TCP 19.0 Gbit/s,64 字节 372 kpps。完整回归里只有 cake 的整形下限失败过:机器繁忙时测试自己的发送方发不够量,单独运行正常;已放宽下限,它检查的上限没有变。