从 IP 到 SRv6 计算编排 · 第十一编

第十一编|源码导读、实验与常见误区

2026-08-20 · COMPIN Lab · 阅读约 13 分钟

系统仿真系统验证源码导读实验

91. 推荐学习顺序

第一次阅读不要直接跳到 HEFT 代码。建议按以下顺序:

图 1 推荐学习顺序(13 个能力检查点)

图 1 推荐学习顺序(13 个能力检查点)

  1. 能解释 byte、十六进制、网络字节序和封装;
  2. 能手算 IPv4/IPv6 前缀,并解释最长前缀匹配;
  3. 能区分普通路由下一跳与 SRv6 segment list;
  4. 能从卫星位置推导 LOS、链路时隙、HELLO 邻接和本地 FIB;
  5. 能拿一个 SRH 逐步写出 active segment 和 IPv6 DA;
  6. 能解释控制面事实为何不等于内核已应用状态;
  7. 能区分 task_id、Color、SID 和 Run ID;
  8. 能从逻辑 Anycast SID 推导本节点的 primary、backup 和 provider 漂移;
  9. 能按 SPP 头定位 task/object/tile/epoch,并解释部分成功;
  10. 能逐步证明 RGB→Gray→Edge 经过真实本地算子;
  11. 能手算一次 HEFT,再比较 Exact 与 Resilient;
  12. 最后连接 BNN、Alarm、10% 损毁、handover、前端和证据;
  13. 掌握原理后再按照后端操作手册启动系统。

每完成一项,先用自己的话讲给别人听。能复述术语不等于理解;能根据一个新报文或新拓扑推导结果,才算掌握。

91.1 当前开发功能覆盖总表

本教材覆盖当前完整系统的原理主线与业务功能,但不会把每个命令行 flag 和历史 Issue 都塞进教学正文;实际命令归后端操作手册,历史演进归 Issue。可按下表核对是否漏项:

已开发能力教材位置学完后应能说明
IPv4、IPv6、Ethernet、邻居和路由第 1–6 章一个普通包怎样逐跳交付
Linux 原生 SRv6、SRH、End/End.X/DX4/DX6/DT6、uSID、SR Policy第 7–12 章一串标准 SID 怎样执行而不依赖私有协议栈
200 卫星、50 飞机、50 船与两机架构第 13、16 章每个前端对象为何对应真实节点及怎样运动
圆轨道、ECI/ECEF、LOS、跨轨匹配、端侧仰角第 16 章物理链路通断怎样由规则自然产生
60 秒物理代次、SQLite 事务、AF_XDP 原子重载第 16 章300 节点怎样只看到完整的新链路矩阵
veth、namespace、宿主 XDP 与 AF_XDP第 14–15 章frame、UMEM、四个 ring 和有向链路队列的所有权
限速、burst、传播时延、拥塞与三类 drop第 15 章token bucket 和各类丢包为何不能混算
HELLO/ACK、四控制器、双向拓扑、端点与资源事实第 16–22 章物理变化怎样经真实观察形成协议图
节点本地普通 FIB、LFA、accepted/applied第 20–22 章控制器为何不替节点安装 FIB
统一 DAG、ETS3 REQUEST/READY/REJECTED第 23–24 章普通传输、单算子和多算子为何共用生命周期
task_id、入口本地 Color、eBPF classifier、策略表第 25 章task 怎样在 Linux 入口选择自己的 SR Policy
Exact 与 Resilient 模式边界第 26、45、74、78–79 章两种模式分别由谁选择实例
七类真实算子与独立 namespace第 27–38 章Gray、Edge、两种 Mask、Mean、Blur、BNN 怎样实现
BNN KEEP/DROP、模型摘要与 96% 稀疏结果语义第 38 章正常 DROP 为何不等于网络或算子错误
火情检测到地面 Alarm 闭环第 38.1、86 章新 Alarm task 怎样自然 REQUEST、READY、发送和接收
逻辑 Anycast SID、节点本地 primary/backup FIB第 40–47 章provider 怎样选择、撤销、切换和形成漂移
SPP 64-byte 头、Tile、Morton、对象窗口和部分成功第 48–54 章100 个 Tile 完成 90 个为何是 90% 成功
SPP 与有状态 Anycast 的理论张力第 54 章无网络丢包时窗口为何仍可能拆散
RGB、Gray、Edge 三级自主降档第 55 章链路比例怎样触发本地真实算子 detour 而非伪造格式
HEFT rank、EST/EFT、候选过滤和完整手算第 57–67 章Exact 怎样联合选择具体实例
HEFT primary/backup、动态重算、无解与耗时分层第 68–76 章何时替换策略、怎样如实记录无解和 CPU 竞争
10% 固定卫星损毁与 A→B 可靠性第 16.13、80 章故障集合为何独立于业务,分母为何不能筛选
飞机/船舶 handover第 16.10、81 章几何接入怎样经 HELLO 和端点事实自然变化
控制面 EAGAIN 与 latest-only 背压第 19、82 章旧完整快照为何可被新代替代而不堆积
ClickHouse、MinIO、Gitea、Run ID 与只读前端第 83–86 章观察失败为何不能反向停止后端
八类前端事实:SRv6、HEFT、重算、Anycast、SPP、BNN、Alarm、降档第 86 章每种画面应由哪类真实事件驱动
24/300 统一模型及 install/images/start/status/logs/stop/clean项目后端操作手册怎样在同一入口安全运行和清理系统

因此,Anycast 和三级降档现在不仅“出现过”,而是分别拥有完整的第六编和第八编。若今后新增正式业务能力,应先在这张表增加学习位置和源码入口,防止功能只存在于 Issue 或前端卡片而没有进入教材。

92. 源码地图

源码会演进,文件名以当前 checkout 为准。阅读入口如下:

想理解的内容首先阅读
不可违反的系统边界docs/simulation-compass.md
当前研发原理摘要docs/technical-principles.md
裸机操作docs/backend-operations.md
SPP 二进制头include/spp.h
SPP 实现与 Tile 坐标src/spp.c 及相关测试
图像算子算法src/gyr_tile_ops.c、src/bnn_fire_model.c 及相关头文件
算子 worker/AF_XDP 循环src/gray_af_xdp_operator.c、src/gyr_tile_pipeline.c 及 SPP runtime
BNN 检测与 Alarm 业务src/gray_af_xdp_operator.c、scripts/fire_alarm.py、scripts/endpoint_business.py
节点本地 Anycast FIBscripts/anycast_fib.py、scripts/sdn_node_agent.py
三级降档与 SRH detoursrc/spp_degradation.c、src/spp_srh_detour.c、src/host_link_fabric.c
HEFT 算法scripts/heft_dag.py
任务 classifierbpf/heft_task_classifier.bpf.c
Color map 操作src/heft_task_map.c 及节点启动脚本
节点 FIB 与任务状态机scripts/sdn_node_agent.py、scripts/local_heft_policy.py 和节点脚本
控制协议scripts/sdn_wire.py、四个 sdn_*_service.py 和节点 agent
宿主 XDP 入口bpf/xdp_link_fabric.bpf.c、include/link_fabric.h
宿主 AF_XDP fabricsrc/host_link_fabric.c、scripts/setup_issue_76_physical_fabric.sh
场景生成configs/issue255-physical-scene-300.json、scripts/physical_scene_provider.py、scripts/polar_topology_provider.py
拓扑事务与时隙scripts/topology_slot_store.py、scripts/topology_scene_runtime.py、scripts/topology_slot_fabric.py
拓扑到 fabric 激活scripts/topology_snapshot_to_fabric.py、scripts/continuous_polar_fabric.py
HELLO 邻居发现scripts/neighbor_hello_wire.py、scripts/neighbor_hello.py
协议拓扑与本地 FIBscripts/sdn_topology_module.py、scripts/sdn_node_agent.py
前端真实事实投影src/frontend/src/App.tsx、src/frontend/src/business.ts 及前端测试
统一部署入口scripts/envsim_host.sh
事件分析scripts/analyze_*.py、scripts/analyze_*.sh

阅读代码时先找 wire contract(线上契约)和 state owner(状态所有者),再看循环和优化。不要从一个日志字符串反推整套架构。

93. 如何安全做只读学习

学习者在 .5 源码机上可以执行静态命令,不会启动仿真:

git status --short --branch
git log -3 --oneline
sed -n '1,240p' include/spp.h
python3 scripts/heft_dag.py --help
bash scripts/envsim_host.sh --help
bash scripts/envsim_host.sh doctor

doctor 只检查能力;install 默认也只输出计划,只有 install --apply 才改变依赖。不要在.5 启动 300 节点仿真。

在 .2 查看运行状态也应先使用操作手册中的 status 和 logs。不要随意执行全局docker prune、删除 network namespace、清空 bpffs 或修改宿主 sysctl,因为这些动作可能影响本项目以外的工作。

94. 实验一:手算 IPv6 前缀

给出:

图 2 六个只读动手实验

图 2 六个只读动手实验

地址 A = fd42:3:14::100
地址 B = fd42:3:14::200
地址 C = fd42:3:15::100
前缀 P = fd42:3:14::/64

问题:A、B、C 谁属于 P?

方法:IPv6 每个 hextet 是 16 bit,/64 比较前四个 hextet:

A前四组 = fd42:0003:0014:0000
B前四组 = fd42:0003:0014:0000
C前四组 = fd42:0003:0015:0000

答案是 A、B 属于,C 不属于。这就是本项目用 fd42:3:<node>::/64 覆盖某节点全部具体算子 SID的原因。

95. 实验二:手推 SRH

假设概念执行顺序为:

S1 → S2 → S3

入口封装后:

  • IPv6 DA 是 S1;
  • SRH 保存足够信息以继续到 S2、S3;
  • S1 执行 End 后,DA 变为 S2;
  • S2 执行 End 后,DA 变为 S3;
  • S3 执行最终 endpoint behavior。

练习:画出每一步的 Segments Left。它表示还有多少 segment 需要推进,而不是“数组里共有多少项”。抓包工具显示 segment list 的顺序可能与概念执行顺序相反,必须同时看 DA 和Segments Left。

96. 实验三:解码一个 SPP 头

在受控测试文件中取 64 byte 头,按偏移读取:

0..3    magic
7       payload format
10..11  payload length
12..19  task id
20..27  object id
28..35  tile id
52..55  object epoch

每读取多字节字段都先确认网络字节序。然后验证:

actual_frame_payload >= 64 + declared_payload_length
task_id != 0
object_id != 0
format与payload length相容
tile坐标在grid边界内

学习目标不是背偏移,而是形成“先验证边界,再解释字段”的解析习惯。

97. 实验四:手算 Color 路由

假设:

task_id = 70001
Color   = 4466
dst     = fd42:10:12c::2

推导:

  1. classifier 识别 70001 并查 map;
  2. skb->mark=4466;
  3. ip rule 选择 table 10000+4466=14466;
  4. table 14466 的目的 /128 路由执行 SRv6 封装;
  5. 后续节点不需要知道 Color。

若 map 没有 task key,先查为什么安装未完成;不要为了“让包走起来”把所有未知 task 都塞到某条默认正确策略。

98. 实验五:手算 HEFT

先完全遮住第 67 章答案,使用表格独立计算:

  1. 两个 stage 的平均计算时间;
  2. 四个候选组合的平均通信时间;
  3. upward rank;
  4. Gray 两个候选的 EST/EFT;
  5. Edge 两个候选含 sink 的完成时间;
  6. 最终 placement 和 makespan。

然后改变一个条件:把 G5 计算时间从 500 us 改成 100 us。重新计算,观察入口传输代价是否仍使 G2 获胜。这个练习能建立“局部快不等于端到端快”的直觉。

99. 实验六:区分真实失败与观察失败

构造下面四组证据并判断:

源端发送算子处理目的端接收前端显示结论
有有有无观察面失败,业务成功
有DROP无正确显示 DROPBNN 正常业务裁决
有无无无需继续定位数据面边界
无无无无先查业务是否自然产生,不应先改网络

这张表训练“缺少显示不等于缺少事实”。

100. 常见误区与纠正

100.1 “SRv6 就是 IPv6 源路由”

不完整。SRv6 不仅列路径,还把 SID 解释为网络程序行为,例如 End.X、End.DX6 和算子实例。

100.2 “控制器知道全网,所以应该直接写所有节点 FIB”

本项目明确禁止。控制器分发事实,节点根据自己已经应用的事实形成 FIB 和 SR Policy。

100.3 “Color 是画面颜色或业务优先级”

不是。Color 是入口本地把 task 导向专用策略表的短标识。

100.4 “Anycast 会把同一个包复制给多个容器”

普通 Anycast 选择一个 provider,不复制包。出现五个实例记录通常意味着不同包或不同时刻的FIB 选择发生了漂移。

100.5 “有一个 Tile 缺失,整条 task 失败”

不符合当前 SPP 部分成功语义。应按完成 Tile/预期 Tile 统计。

100.6 “HEFT 没有解就是 Bug”

不是。资源、算子或路径约束可能使问题真实不可行,应记录无解原因。

100.7 “HEFT 超过 100 ms 必须强行短路”

不是。先区分算法 CPU 时间、调度等待、快照传播和内核安装;观察阈值不是修改协议语义的命令。

100.8 “把超时加长就能提高可靠性”

加长可能只推迟错误并增加旧状态堆积。先证明第一个未闭环的组件边界。

100.9 “前端页面是系统真相”

前端是事实投影。真实来源是节点、内核、fabric、控制器和对象证据。

100.10 “24 节点是简化假模型”

24 与 300 使用相同镜像、PID 1、协议和启动逻辑,只改变规模和参数。

100.11 “10% 损毁后只统计仍有路的任务更合理”

这会改变实验分母。可以另报可行任务完成率,但不能替代损毁后的总业务成功率 B。

100.12 “SPP 和 Anycast 不能同时使用”

过于绝对。无状态算子自然兼容;有状态算子会承受 provider 漂移带来的窗口损失,当前将其按Tile 可靠性量化,更高层状态安置是后续理论问题。

101. 综合练习

101.1 基础题

  1. IPv6 地址 128 bit 共有多少 byte?
  2. /64 表示哪部分是前缀?
  3. FIB 与 RIB 分别解决什么问题?
  4. SRH 中的 active segment 与 IPv6 DA 有什么关系?
  5. End.X 与 End.DX6 的作用有什么不同?

101.2 系统题

  1. 为什么宿主 XDP 只按 MAC redirect,而不自己计算 IPv6 路由?
  2. 为什么控制器的 accepted generation 不能直接当作节点 applied generation?
  3. 为什么 resource report 必须有 instance、sequence 和 lifetime?
  4. task_id、Color、Run ID 的作用域各是什么?
  5. Exact 策略中为什么使用具体算子 SID?
  6. Resilient 策略中为什么使用逻辑 Anycast SID?

101.3 SPP 题

  1. 两个包 object_id 相同、epoch 不同,能否进入同一缓存窗口?
  2. Tile Mean 为什么不能按“任意四个包”聚合?
  3. 100 个预期 Tile 到 95 个,Tile 成功率是多少?
  4. BNN DROP 与算子 ERROR 有什么区别?

101.4 HEFT 题

  1. upward rank 使用平均成本,最终 placement 也使用平均成本吗?
  2. EST 为何可能晚于所有前驱完成时间?
  3. 一个 stage 在 node-5 的 EFT 更小,但它到 sink 很远;出口选择应比较什么?
  4. primary 排除首边与具体实例后 backup 无解,正确记录是什么?
  5. Resilient provider 变化为什么不触发 HEFT?

101.5 诊断题

  1. 前端没有图但目的端有接收事件,应先修数据面还是观察面?
  2. 控制 socket 出现 EAGAIN 时,为什么不应无限累积旧拓扑快照?
  3. HEFT 墙钟耗时变大,至少还要观察哪三类数据?
  4. 一轮运行有业务 ENETUNREACH,为什么不能立即断言宿主网络安装失败?
  5. 观察器失败后,分析脚本应该怎样继续?

102. 综合练习答案

  1. 128÷8=16 byte。
  2. 前 64 bit 是网络前缀,其余 64 bit 是接口标识部分;具体地址规划仍由项目定义。
  3. RIB 保存控制面候选路由知识,FIB 保存内核实际用于快速转发的结果。
  4. active segment 作为当前 IPv6 DA;端点行为推进后,DA 变为下一个 active segment。
  5. End.X 推进 SRH 并交指定下一跳;End.DX6 解封装并交付内层 IPv6。
  6. 保持 Linux 拥有路由语义,XDP 只做最小可证的 L2 分流,避免宿主暗中形成第二套路由器。
  7. accepted 仅表示消息被接收,节点还要通过单一 worker 验证并安装,成功后才是 applied。
  8. 用来区分进程重启、消息新旧和事实过期,防止旧实例“复活”。
  9. task_id 是端到端业务任务;Color 是某入口本地策略选择;Run ID 是一整轮实验和证据。
  10. Exact 已由 HEFT 选择具体实例,策略必须把选择落实成可执行地址。
  11. Resilient 把实例选择留给每个逻辑服务的当前 FIB。
  12. 不能,epoch 不同表示对象版本不同。
  13. 必须是同对象、同 epoch、同 2×2 空间 group 的四块。
  14. 95/100=95%。
  15. DROP 是模型作出的正常负类裁决;ERROR 表示格式、模型或运行异常。
  16. 不使用;最终 placement 使用候选间具体计算和通信代价。
  17. 目标 processor 可能仍在执行此前放置的 stage,所以还要等待 resource available time。
  18. 比较 EFT 加该候选到 sink 的具体通信时间。
  19. 记录 primary 成功、backup no solution 及约束原因,不伪造 backup。
  20. Resilient 不运行 HEFT;它只更新逻辑服务 FIB,task SRH 稳定。
  21. 先查观察面查询、Run ID、权限和投影。
  22. 旧拓扑已被新代完整取代;积累历史会使节点永远追赶过期状态。
  23. 线程 CPU 时间、调度/run queue、输入候选规模;还应分开快照传播与内核安装。
  24. 业务路径在动态拓扑/损毁下可以自然不可达,需先区分宿主能力、控制面、FIB 和任务结果。
  25. 捕获该观察器状态并继续其他领域分析,最后统一汇总,不能让 set -e 提前退出。

< 返回技术专栏