本文是《从 IP 到 SRv6 计算编排》新手教材第 11/13 编。 总目录 · 上一篇:第十编:业务、故障、前端和证据 · 下一篇:第十二编:英中术语表
91. 推荐学习顺序
第一次阅读不要直接跳到HEFT代码。建议按以下顺序:
- 能解释byte、十六进制、网络字节序和封装;
- 能手算IPv4/IPv6前缀,并解释最长前缀匹配;
- 能区分普通路由下一跳与SRv6 segment list;
- 能从卫星位置推导LOS、链路时隙、HELLO邻接和本地FIB;
- 能拿一个SRH逐步写出active segment和IPv6 DA;
- 能解释控制面事实为何不等于内核已应用状态;
- 能区分task_id、Color、SID和Run ID;
- 能从逻辑Anycast SID推导本节点的primary、backup和provider漂移;
- 能按SPP头定位task/object/tile/epoch,并解释部分成功;
- 能逐步证明RGB→Gray→Edge经过真实本地算子;
- 能手算一次HEFT,再比较Exact与Resilient;
- 最后连接BNN、Alarm、10%损毁、handover、前端和证据;
- 掌握原理后再按照后端操作手册启动系统。
每完成一项,先用自己的话讲给别人听。能复述术语不等于理解;能根据一个新报文或新拓扑 推导结果,才算掌握。
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 FIB | scripts/anycast_fib.py、scripts/sdn_node_agent.py |
| 三级降档与SRH detour | src/spp_degradation.c、src/spp_srh_detour.c、src/host_link_fabric.c |
| HEFT算法 | scripts/heft_dag.py |
| 任务classifier | bpf/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 fabric | src/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 |
| 协议拓扑与本地FIB | scripts/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前缀
给出:
地址 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
推导:
- classifier识别70001并查map;
skb->mark=4466;ip rule选择table10000+4466=14466;- table 14466的目的
/128路由执行SRv6封装; - 后续节点不需要知道Color。
若map没有task key,先查为什么安装未完成;不要为了“让包走起来”把所有未知task都塞到某 条默认正确策略。
98. 实验五:手算HEFT
先完全遮住第67章答案,使用表格独立计算:
- 两个stage的平均计算时间;
- 四个候选组合的平均通信时间;
- upward rank;
- Gray两个候选的EST/EFT;
- Edge两个候选含sink的完成时间;
- 最终placement和makespan。
然后改变一个条件:把G5计算时间从500 us改成100 us。重新计算,观察入口传输代价是否仍 使G2获胜。这个练习能建立“局部快不等于端到端快”的直觉。
99. 实验六:区分真实失败与观察失败
构造下面四组证据并判断:
| 源端发送 | 算子处理 | 目的端接收 | 前端显示 | 结论 |
|---|---|---|---|---|
| 有 | 有 | 有 | 无 | 观察面失败,业务成功 |
| 有 | DROP | 无 | 正确显示DROP | BNN正常业务裁决 |
| 有 | 无 | 无 | 无 | 需继续定位数据面边界 |
| 无 | 无 | 无 | 无 | 先查业务是否自然产生,不应先改网络 |
这张表训练“缺少显示不等于缺少事实”。
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 基础题
- IPv6地址128 bit共有多少byte?
/64表示哪部分是前缀?- FIB与RIB分别解决什么问题?
- SRH中的active segment与IPv6 DA有什么关系?
- End.X与End.DX6的作用有什么不同?
101.2 系统题
- 为什么宿主XDP只按MAC redirect,而不自己计算IPv6路由?
- 为什么控制器的accepted generation不能直接当作节点applied generation?
- 为什么resource report必须有instance、sequence和lifetime?
- task_id、Color、Run ID的作用域各是什么?
- Exact策略中为什么使用具体算子SID?
- Resilient策略中为什么使用逻辑Anycast SID?
101.3 SPP题
- 两个包object_id相同、epoch不同,能否进入同一缓存窗口?
- Tile Mean为什么不能按“任意四个包”聚合?
- 100个预期Tile到95个,Tile成功率是多少?
- BNN DROP与算子ERROR有什么区别?
101.4 HEFT题
- upward rank使用平均成本,最终placement也使用平均成本吗?
- EST为何可能晚于所有前驱完成时间?
- 一个stage在node-5的EFT更小,但它到sink很远;出口选择应比较什么?
- primary排除首边与具体实例后backup无解,正确记录是什么?
- Resilient provider变化为什么不触发HEFT?
101.5 诊断题
- 前端没有图但目的端有接收事件,应先修数据面还是观察面?
- 控制socket出现EAGAIN时,为什么不应无限累积旧拓扑快照?
- HEFT墙钟耗时变大,至少还要观察哪三类数据?
- 一轮运行有业务ENETUNREACH,为什么不能立即断言宿主网络安装失败?
- 观察器失败后,分析脚本应该怎样继续?
102. 综合练习答案
128÷8=16 byte。- 前64 bit是网络前缀,其余64 bit是接口标识部分;具体地址规划仍由项目定义。
- RIB保存控制面候选路由知识,FIB保存内核实际用于快速转发的结果。
- active segment作为当前IPv6 DA;端点行为推进后,DA变为下一个active segment。
- End.X推进SRH并交指定下一跳;End.DX6解封装并交付内层IPv6。
- 保持Linux拥有路由语义,XDP只做最小可证的L2分流,避免宿主暗中形成第二套路由器。
- accepted仅表示消息被接收,节点还要通过单一worker验证并安装,成功后才是applied。
- 用来区分进程重启、消息新旧和事实过期,防止旧实例“复活”。
- task_id是端到端业务任务;Color是某入口本地策略选择;Run ID是一整轮实验和证据。
- Exact已由HEFT选择具体实例,策略必须把选择落实成可执行地址。
- Resilient把实例选择留给每个逻辑服务的当前FIB。
- 不能,epoch不同表示对象版本不同。
- 必须是同对象、同epoch、同2×2空间group的四块。
95/100=95%。- DROP是模型作出的正常负类裁决;ERROR表示格式、模型或运行异常。
- 不使用;最终placement使用候选间具体计算和通信代价。
- 目标processor可能仍在执行此前放置的stage,所以还要等待resource available time。
- 比较EFT加该候选到sink的具体通信时间。
- 记录primary成功、backup no solution及约束原因,不伪造backup。
- Resilient不运行HEFT;它只更新逻辑服务FIB,task SRH稳定。
- 先查观察面查询、Run ID、权限和投影。
- 旧拓扑已被新代完整取代;积累历史会使节点永远追赶过期状态。
- 线程CPU时间、调度/run queue、输入候选规模;还应分开快照传播与内核安装。
- 业务路径在动态拓扑/损毁下可以自然不可达,需先区分宿主能力、控制面、FIB和任务结果。
- 捕获该观察器状态并继续其他领域分析,最后统一汇总,不能让
set -e提前退出。