Skip to content

地址概念模型(addressing model)

本文把项目里所有类型的地址盘一遍:每个地址是什么、有什么用、真相源在哪、影响半径多大,并按对"单一真相源"最关键的维度给每个地址分类。这是概念参考;fleet 的具体数值分配(各节点 loopback、/27 + /29 分区)见 ../guides/addressing-and-renumber.md

相关阅读:

三种分类:源 / 派生 / 副本

每个地址都标两件事——影响半径真相源分类。后者直接决定它是不是 renumber 的危险点:

分类含义renumber 时例子
源(source)被人手 authored 的权威值,改这里才算改改这一处Node.loopback_ipv4DnsGroup.bind_addressesNodeSpec.link_local
派生(derived)渲染 / normalize / materialize 时从源算出来,不落独立存储不用动,自动跟随OWNIPdns-anycast 接口地址、外部 eBGP 接口上的 LLA
副本(copy)源的独立第二份,没有机制保证一致必须手动同步,漏一处就坏internal_topology.hosts[*].ownipbgp.source_address

重构北极星:把"副本"逐个变成"派生"。 代码里已有两个黄金范例:

  1. _normalize_dns_anycast(../../packages/dn42_schemas/dn42_schemas/desired_state.py):dns.bind_addresses 是 DNS 服务地址的唯一真源dns-anycast dummy 接口地址、它进 BGP 的任播前缀全部在 normalize 阶段派生。
  2. _interface_payload 里的 LLA 派生(../../apps/control-server/app/services/materializer.py:214):NodeSpec.link_local 是节点 LLA 的唯一真源,materialize 时派生到所有外部 eBGP WG 接口的 addresses

以最贵的节点 loopback 为例,一处真值的下游全景(实线 = 自动派生,虚线 = 仍需手动同步的副本):

所有"副本"都应朝这个形态收敛。重构进度见 ../ROADMAP.md


按影响半径分层

影响半径最大(动一下牵动全 fleet / 注册表)到最小(节点内部、链路层)。

第 1 层:节点身份地址 —— 影响半径:全 fleet + dn42 注册表 + DNS

这层是最贵的地址:一处真值,但被复制 / 派生到很多地方。

地址是什么 / 用途真相源分类影响半径
router_idBIRD router id;iBGP/OSPF 身份;large-community origin_node_id 取它低 16 位Node.router_idiBGP/OSPF 会话重建;community 编号变化
loopback_ipv4 / loopback_ipv6节点身份 loopback。渲染成 OWNIP/OWNIPv6:内核导出源地址 krt_prefsrc、iBGP next-hop、iBGP source address、router id 兜底。必须 ∈ ipv4_prefixes/ipv6_prefixesNode.loopback_ipv4/ipv6全 fleet(见 1.1 副本)+ DNS 正反向记录 + 注册表
ipv4_prefixes / ipv6_prefixes节点自有、对外宣告的聚合前缀。派生 static reject 路由、is_self_net ipset、iBGP 宣告前缀Node.ipv4_prefixes/ipv6_prefixes节点对 DN42 起源什么(注册表 route 对象)

派生自本层(安全,自动跟随)OWNIP/OWNIPv6router id OWNIPkrt_prefsrcorigin_node_idownnets*_ipset、iBGP 的 source address/next hop——全部在 ../../packages/dn42_templates/dn42_templates/bird2.py / 模板里从 loopback、prefixes 算出。

1.1 危险副本:internal_topology.hosts[*].ownip / ownip6

bird.internal_topology.hosts[<node>].ownip/ownip6(BirdHostSpec)是每个节点 loopback 的独立第二份,而且按 fleet 不变量要复制进每一个节点base_template

  • 影响半径:全 fleet。renumber 一个节点的 loopback,要同步 N 个节点里这份 hosts。
  • 历史上"改不干净"最严重的点:pvg2 renumber 事故(运维叙事档案已移出本仓库),以及 deploy/unify_internal_topology.py 硬编码旧地址会回退的坑。
  • 重构方向(未完成,见 ../ROADMAP.md):hosts 的 ownip/ownip6 应从各节点 Node.loopback_* 派生,而不是各自存一份。

第 2 层:eBGP 会话地址 —— 影响半径:单个对端

地址是什么 / 用途真相源分类影响半径
BgpSessionSpec.neighbor对端(远端)BGP 地址;可带 %zone会话 spec一条会话
BgpSessionSpec.source_address本端建会话的源地址会话 spec副本一条会话;必须等于绑定接口上的某个地址

2.1 危险副本:source_address vs 接口地址

source_addressInterfaceSpec.addresses(第 3 层)里的本端地址是同一个值、独立写两遍,没有任何东西强制相等。renumber 链路时改了接口漏了 source(或反之),BGP 就起不来。重构方向(未完成):让 source 从绑定接口按地址族派生。

第 3 层:接口 / 链路地址 —— 影响半径:单条链路(两端)

承载在 InterfaceSpec(network.py)/ WgInterface.spec

地址是什么 / 用途真相源分类影响半径
addressesip addr add 到接口的本端地址。eBGP wg:本端 /31(v4)+/127(v6);外部 eBGP 接口的 fe80::X/64 LLA 现由 materialize 派生注入(见下)接口 spec(LLA 部分为派生)源(LLA 派生)一条链路本端
peer_routes经该接口可达的对端地址(peer_v4/32peer_v6/128、对端 LLA)接口 spec副本(对端本地地址的镜像)一条链路;跨节点
wireguard_peer.endpoint拨向对端的 underlay host:port(公网/NAT 地址,可为域名,agent 周期重解析)接口 spec一条链路
wireguard_peer.allowed_ipsWG 加密选路范围,通常 0.0.0.0/0 + ::/0接口 spec一条链路

3.1 已解决的副本:节点 LLA fe80::X(已收敛为派生)

节点本端 link-local(fe80::X,一节点一个,所有外部 eBGP WG 接口复用)已从"副本"收敛为"派生"

  • 唯一真源:NodeSpec.link_local(network.py,裸 fe80::/10 地址,不带 %zone)。
  • 派生:materialize()_interface_payload(materializer.py:214)对 peering.is_internal=False 的 WG 接口,把 <link_local>/64 注入接口 addresses(已存在则去重)。渲染器照旧与各接口 fe80 peer_route 配成 ip addr add fe80::X/64 peer fe80::Y,输出不变。
  • 剥副本:存量节点接口 addresses 里的 fe80::X/64 副本由 deploy / scripts/tools/backfill_node_lla.py 一次性剥除,之后由派生重新提供(见 ../reference/cli-and-scripts.md)。
  • 注意:内部互联(iBGP/OSPF)WG 接口用各自的 LL,不复用 node.link_local;对端 LL(fe80::Y)是 per-peer,留在 peer_routes 不动。

第 4 层:任播服务地址 —— 影响半径:跨节点共享 + 注册表

地址是什么 / 用途真相源分类影响半径
dns.bind_addressesDNS 任播服务地址。唯一真源:normalize 派生 dns-anycast dummy 接口(v4→/32、v6→/128)+ 登记 track_service ⇒ BIRD 起源任播前缀进 BGPDnsGroup.bind_addresses订阅同组的所有节点(anycast);注册表 .56/29 双 origin
dns-anycast 接口地址 + 任播前缀上者的派生产物—(派生自 bind_addresses派生(黄金范例)同上
DNS 记录值(A/AAAA/PTR 的 content)解析到 loopback / 任播地址DnsRecord.content副本(指向 loopback 的手工绑定)解析正确性;renumber 后必须跟改

dns.bind_addresses → dns-anycast 这条链是全项目最干净的单一真相源实现。详见 ../guides/dns-and-anycast.md

第 5 层:runtime / underlay(容器层)—— 影响半径:节点内部

仅在单节点容器编排内有意义,不进公网 /26/48 身份/任播分区。

地址是什么 / 用途真相源分类影响半径
runtime.underlay.subnet / gateway / ipv6_subnetrouter netns 容器互联网(10.254.42.0/24 一类)。ipv6_subnet 纳入容器 config_hash(声明式重建)UnderlayNetworkSpec节点内部
runtime.rpki.listen_hostunderlay 内 RPKI cache 地址(默认 10.254.42.3);渲染成模板 rpki_ipRpkiSpec.listen_host节点内部
RuntimeServiceSpec.ipv4_address某服务在 underlay 的显式地址;必须 ∈ underlay 子网且 ≠ 网关service spec节点内部

不随节点变的字面量

is_valid_network / is_valid_network_v6 里的 DN42 可接受前缀范围、anycast 172.2x.0.0/24 段等,是协议级常量,不随本节点 renumber 变化,不属于"源/派生/副本"。


副本清单(renumber 危险点速查)

按重构优先级排(✅ = 已收敛为派生,⬜ = 仍是副本)。完整进度见 ../ROADMAP.md

#副本应派生自状态
1internal_topology.hosts[*].ownip/ownip6各节点 Node.loopback_*⬜ 影响半径全 fleet,历史事故最多
2bgp_sessions[*].source_address绑定 interface.addresses⬜ 牵动寻址模型决策
3interfaces[*].peer_routes 对端地址对端 addresses(peering 聚合根作单源)
4DNS A/AAAA/PTR 记录指向 loopback 的手工绑定⬜ renumber 必须连带改
5节点 LLA fe80::XNodeSpec.link_local已派生(materialize 注入 + backfill 剥副本)
内部对端 WG 公钥对端 Node.wireguard_public_key已派生(materialize 注入)
dns-anycast 接口地址 / 任播前缀DnsGroup.bind_addresses已派生(黄金范例)

每消灭一个副本,就少一处"改不干净"。范例形态见 _normalize_dns_anycast_interface_payload