本文是《从 IP 到 SRv6 计算编排》新手教材第 4/13 编。 总目录 · 上一篇:第三编:300 节点系统的控制面和数据面 · 下一篇:第五编:算子让网络路径执行计算
23. DAG把普通传输和计算链统一起来
23.1 DAG
**DAG(Directed Acyclic Graph,有向无环图)**由vertex(顶点/任务节点)和有向edge (边/依赖)组成,且不存在沿有向边回到自身的cycle(环)。
在本项目中:
source → sink
是没有中间算子的普通传输DAG;
source → gray → mean2x2 → blur3x3 → edge → sink
是图像处理DAG。它们共享同一个REQUEST、Color、SR Policy、READY和数据发送生命周期。
23.2 Task Request合同
TaskRequest包含:
| 字段 | 范围 | 意义 |
|---|---|---|
task_id | 非零uint32 | 幂等任务身份 |
origin_endpoint | 非零uint16 | 业务发起端身份 |
destination_endpoint | 非零uint16 | 业务目的端身份 |
dag_profile | 1或2 | 1=plain,2=带规范DAG |
dag_json | 最大1400 bytes | profile 2的规范JSON |
placement_mode | exact/resilient | 明确选择放置模型 |
**uint32(unsigned 32-bit integer,无符号32位整数)**范围为0..2^32-1;uint16同理。
任务合同禁止源和目的相同,禁止未知profile,禁止超长或非canonical DAG JSON。
**Canonical JSON(规范JSON)**使用确定键顺序和紧凑编码,让同一DAG只有一种字节表示, 便于严格校验和摘要。
23.3 固定支持的DAG
当前主要模板:
dag_id | 链 |
|---|---|
spp-transport-v1 | source→sink |
image-pipeline-v1 | source→gray→mean→blur→edge→sink |
fire-tile-filter-v1 | source→bnn→sink |
普通传输固定使用exact,因为它没有算子Anycast放置问题;图像DAG显式选择exact或 resilient,不在任务中途自动切换模式。
24. REQUEST、READY和REJECTED
24.1 ETS3线格式
当前端点任务信令magic为ETS3。**Magic(魔数)**是协议开头的固定字节,用来快速
判断payload类型和版本。
magic | kind | task_id | origin | destination | profile | placement | dag_length | DAG
4B 1B 4B 2B 2B 1B 1B 2B
kind取值:
REQUEST=1:请求建立任务;READY=2:入口已成功安装策略;REJECTED=3:请求明确不合法或不能接受。
“事实暂缺、正在等待资源”通常是pending(待处理),不等于永久REJECTED。
24.2 来源验证
入口不能只相信REQUEST正文里的origin_endpoint,还要验证:
- 外部端侧是否从真实data interface发来;
- IPv6源地址是否对应声明的endpoint;
- 集成端侧是否从本地business veth发来;
- 声明origin是否等于当前入口身份或已本地接入身份。
这防止普通容器伪造另一个endpoint发起任务。
24.3 幂等与重试
**Idempotent(幂等)**表示相同请求重复一次不会创建第二份不同任务。端侧可以在暂时
没有READY时按协议重试;入口按task_id合并客户地址并返回已有READY。
重试不由外部脚本注入,且不能靠延长固定超时掩盖规划错误。
24.4 完整状态机
端侧常驻
└─ 自然产生任务
└─ REQUEST
├─ 来源/合同非法 ─► REJECTED
├─ 当前事实不足 ─► pending,等待事实或端侧重试
└─ 规划和安装成功
└─ READY
└─ 端侧发送业务数据
没有FLOW_START,控制器不产生任务,观察窗口也不激活任务。
25. Color的完整实现
25.1 Color到底是什么
本项目的**Color(策略分类编号)**是入口本地非零16-bit整数,把task_id映射到Linux
策略路由表。它是转发资源,不是业务含义本身。
分配算法:
start = ((task_id - 1) mod 65535) + 1
从start循环寻找本入口尚未使用的Color
因此通常Color接近task ID的低16-bit,但冲突时会选择下一个空闲值。不同入口可以为 不同任务使用相同Color,因为它们的Linux路由表互相隔离。
25.2 分类器识别哪几种业务
入口eBPF分类器读取IPv6/UDP payload的magic:
| magic | 协议 | task字段位置 |
|---|---|---|
SPP1 | SPP Tile | SPP头offset 12的uint64 task_id |
CFV2 | 普通业务流 | magic后offset 4的uint32 flow/task ID |
FAL1 | 火灾Alarm | magic后offset 4的uint32 alarm task ID |
查map成功时:
skb->mark = Color
已识别任务格式但map没有条目时使用保留mark 65535,避免未知任务意外进入main route。
非组播、非link-local的普通UDP/TCP可以使用generic mark 10000;HELLO等本地控制流量
不被任务分类器改写。
25.3 安装顺序
新任务第一次安装包含:
- 删除可能存在的同Color旧rule,保证幂等;
- 增加
fwmark Color lookup table priority Color; - 在table
10000+Color安装目的endpoint/128路由; - exact路由直接携带
encap seg6 mode encap segs ...; - resilient路由引用一个携带不变SRH的稳定nexthop object;
- 把
task_id→Color写入pinned eBPF map; - 全部成功后才发送READY。
**Pinned Map(固定挂载的eBPF映射)**通过bpffs路径让不同进程访问同一个内核map。 本项目路径为:
/sys/fs/bpf/tc/globals/heft_task_colors
25.4 示例
假设task_id=70001,入口分到Color=4466:
table = 10000 + 4466 = 14466
概念命令是:
ip -6 rule add fwmark 4466 lookup 14466 priority 4466
ip -6 route replace fd42:10:12c::2/128 table 14466 \
encap seg6 mode encap segs <segments> via <gateway> dev eth0
heft-task-map ... 70001 4466
业务包本身仍携带task_id=70001,不携带4466。Color只存在入口内核。
25.5 策略替换和撤销
同一task重算时保持task、Color、目的和placement mode不变,只用route replace更新 segment或nexthop,避免delete/add产生人为流量空窗。
源端真实detach后,旧入口依次删除:
- eBPF map中的task key;
- task table的目的route;
- Color对应的ip rule;
- resilient任务的nexthop object。
随后释放Color供未来本地任务使用。
26. REQUEST为什么必须先声明Exact或Resilient
任务请求中的placement_mode先声明入口要采用哪一种实例选择模型。本章只建立最小区别;
算子实例尚未学完,所以暂时不展开算法和逻辑地址。
26.1 Exact只先理解为“入口明确选实例”
**Exact Placement(精确放置)**由入口根据一份事实快照,为每个计算阶段明确选择某个 物理实例,最终形成具体服务SID的完整策略。
抽象阶段A -> 明确实例A@某节点
抽象阶段B -> 明确实例B@某节点
第九编才讲入口为什么用HEFT、怎样计算primary/backup以及何时重算。本章不要求读者提前 接受一个尚未推导的算法答案。
26.2 Resilient只先理解为“路径到达时选择实例”
**Resilient Placement(弹性放置)**不在入口固定具体实例。task只引用逻辑服务身份, 数据到达该服务时由当前节点的普通IPv6 FIB选择provider。
逻辑阶段A -> 当时可达的某个A实例
逻辑阶段B -> 当时可达的某个B实例
第六编从普通路由开始完整讲Anycast地址、provider候选和FIB切换。
26.3 不能混用两套推理
Exact把实例选择当成一次全链编排;Resilient把实例选择当成每项服务的局部路由。二者都 遵守同一REQUEST/READY和Color安装生命周期,但不能混用对方的重算或备份语义。全部细节 等学完第六编Anycast和第九编HEFT以后再比较。
26.4 第三、四编自测
- 四个控制器模块分别拥有什么状态?
generation与sequence有什么区别?- accepted拓扑为什么不能立即用于task planning?
task_id、Color和SRH分别是什么身份?- Color是否由控制器下发?
- Exact与Resilient在哪里选择具体算子实例?