本文是《从 IP 到 SRv6 计算编排》新手教材第 2/13 编。 总目录 · 上一篇:第一编:从比特到 IPv6 路由 · 下一篇:第三编:300 节点系统的控制面和数据面
7. Segment Routing的核心思想
7.1 从“只写终点”到“写一串指令”
**SR(Segment Routing,分段路由)**是一种源路由思想:入口把有序segment写入包, 中间网络按顺序执行。**Source Routing(源路由)**不是说业务端侧必须知道全网,而是 指SR domain(分段路由域)的入口节点负责把指令链写进包。
普通IPv6包主要表达:
去目的D
SRv6包可以表达:
先到节点2
再经过节点6提供的一项服务
再到节点9
最后在节点12解封装
7.2 Segment与SID
**Segment(分段/指令)**是要执行的一步。SID(Segment Identifier,分段标识符) 是这条指令的IPv6地址表示。
SID不是单纯“某台机器的地址”。它可以表示:
- 到达并推进指令链;
- 从指定下一跳或接口发出;
- 解封装并查IPv4/IPv6表;
- 进入一个节点已经注册的服务接口。
这正是SRv6的“Network Programming(网络编程)”思想:有序SID列表像一个很小的网络 程序,每个SID对应一个节点上已经安装的行为。这里的“编程”不是允许包携带任意代码, 而是从节点预先支持的有限行为中选择。
7.3 SRv6
**SRv6(Segment Routing over IPv6,基于IPv6的分段路由)**用IPv6地址表示SID, 并通常用SRH携带有序segment列表。它复用IPv6转发、路由表和扩展头,不需要自定义 用户态IPv6协议栈。
本项目坚持Linux原生SRv6:
- 入口用Linux
seg6 mode encap添加外层IPv6和SRH; - 节点用Linux
seg6local执行本地SID行为; - 普通中继用Linux IPv6 FIB转发当前目的地址;
- 不定义私有SRH,不让宿主fabric推进SID。
8. SRH怎样表达有序指令
8.1 SRH是什么
**SRH(Segment Routing Header,分段路由头)**是IPv6 Routing Header Type 4。 重要字段包括:
| 字段 | 中文解释 | 初学者应理解的作用 |
|---|---|---|
| Next Header | 下一头部 | SRH后面是IPv6、UDP等什么协议 |
| Hdr Ext Len | 扩展头长度 | SRH占多少字节 |
| Routing Type | 路由类型 | SRH固定为4 |
| Segments Left | 剩余segment数 | 还有多少指令尚未推进 |
| Last Entry | 最后条目索引 | segment list的边界 |
| Flags | 标志 | SRH处理选项 |
| Tag | 标签 | 可选策略标识;不要与后文的本地策略编号混淆 |
| Segment List | SID数组 | 有序指令的线表示 |
为了便于人阅读,本书总是按执行顺序写:
SID-A → SID-B → SID-C
SRH在线数组的索引布局与人类执行顺序可能看起来相反,Linux会按照Segments Left
和当前IPv6 Destination Address正确推进。操作人员不要手工猜数组顺序,应检查
ip -6 route安装的segment顺序和真实抓包。
8.2 当前Destination Address很重要
外层IPv6 Destination Address保存当前正在生效的SID。普通transit node(中继节点) 只按这个目的地址查FIB,不应该随意检查SRH深处的未来指令。
当当前SID节点执行End类行为时,Linux推进SRH,把下一SID放入Destination Address, 然后继续普通IPv6路由。
8.3 Reduced SRH
**Reduced SRH(精简SRH)**允许第一个segment已经位于IPv6 Destination Address, 从SRH列表中省略重复项。读者只需记住:抓包中当前目的地址和SRH数组必须合起来理解, 不能只看数组就断言“第一个节点丢了”。
8.4 先掌握固定segment list,再学习运行期插入
初学者在本编只处理入口已经写好的固定segment list:读当前Destination Address、执行
当前SID、推进Segments Left。系统还支持一种在链路边界临时插入本地服务SID的高级行为,
但它同时依赖链路速率、算子和业务格式,因此推迟到第八编。现在只需记住:任何高级插入都
必须保存并恢复当时真正的active segment,不能从原始列表猜测当前位置。
9. Linux原生SRv6行为
9.1 seg6与seg6local
**LWT(Lightweight Tunnel,轻量隧道)**允许Linux把封装动作附着在路由上。
encap seg6 mode encap是入口封装动作;encap seg6local action ...是本地SID行为。
抽象命令如下:
ip -6 route replace <业务目的>/128 \
encap seg6 mode encap segs <SID-A>,<SID-B> \
via <下一跳> dev <接口>
这条命令的含义是:命中业务目的后,Linux增加外层IPv6和SRH,并把包送向第一SID。
9.2 End
**End(端点推进行为)**表示当前SID在本节点终止,推进到下一个segment,再按IPv6 FIB继续转发。它适合表达“必须经过这个SRv6节点”。
End不执行图像算法,也不等于最终业务交付。
9.3 End.X
**End.X(Endpoint with Layer-3 cross-connect,带三层交叉连接的端点行为)**推进SRH 后,把包交给一个指定IPv6下一跳和接口。
End.X可以把包交给一个独立服务namespace:
服务SID命中服务namespace的End.X
→ 包进入服务接口
→ 用户态进程处理应用数据
→ 包回注同一namespace
→ 按推进后的SRH继续
第五编会把这种服务具体化为图像算子。此处只需理解:它是一个真实网络转发闭环,不是 入口进程直接调用另一个函数。
9.4 End.DX4与End.DX6
End.DX4(Endpoint Decapsulation and IPv4 Cross-Connect,解封装并IPv4交叉连接) 删除外层SRv6封装,把内层IPv4包交给指定IPv4下一跳。
End.DX6(Endpoint Decapsulation and IPv6 Cross-Connect,解封装并IPv6交叉连接) 做同样的事,但内层是IPv6并交给指定IPv6下一跳。
独立飞机/船舶端侧位于自己的namespace,末端接入卫星使用包含接入节点和endpoint 身份的具体SID:
fd42:4:<access-node>::6:<endpoint>
该SID执行End.DX6,把内层普通IPv6交给真实端侧。
9.5 End.DT6
End.DT6(Endpoint Decapsulation and IPv6 Table Lookup,解封装并查询IPv6路由表) 删除外层后,在指定Linux IPv6表中重新查询内层目的地址。
网络节点内部的integrated endpoint(集成端侧)复用物理节点身份,末段可以使用:
fd42:2::<node>
执行End.DT6并把包交给节点内集成端侧。独立端侧与集成端侧的末端行为不同,但它们 在REQUEST/READY和业务合同上没有第二套模型。
9.6 行为对照表
| 行为 | 是否推进SRH | 是否解封装 | 下一步 |
|---|---|---|---|
| End | 是 | 否 | 普通IPv6 FIB |
| End.X | 是 | 否 | 指定IPv6下一跳/接口 |
| End.DX4 | 到末端 | 是 | 指定IPv4下一跳 |
| End.DX6 | 到末端 | 是 | 指定IPv6下一跳 |
| End.DT6 | 到末端 | 是 | 指定IPv6路由表查询 |
10. uSID:压缩物理逐跳路径
10.1 为什么需要压缩
一个完整IPv6 SID是128 bit,即16字节。若把十几个物理节点都写成完整SID,SRH会 快速变大。**uSID(micro SID,微型SID)**把共享locator后的短指令装入一个128-bit container(容器),减少重复前缀和零填充。
当前IETF标准把这种机制称为Compressed SRv6 Segment List Encoding(压缩SRv6 segment list编码),并定义NEXT-CSID等行为flavor。**Flavor(行为变体)**是在保留 基本End语义的同时改变SID内部推进方式。
10.2 本项目16-bit指令合同
本项目使用:
Locator = fd42:197::/32
每个uSID = 16 bit
高8 bit = node,范围1..255
低8 bit = local function
function 0 = 物理节点transit
一个128-bit container在32-bit locator之后有96 bit,可容纳6个16-bit指令。
例如物理路径1→2→5→8→10由入口1安装,入口本身不重复编码,后四个节点编码为:
node2 = 0x0200
node5 = 0x0500
node8 = 0x0800
node10 = 0x0a00
container = fd42:197:200:500:800:a00::
每到一个支持NEXT-C-SID的节点,Linux把下一条16-bit指令移动到当前SID位置,继续转发。
10.3 为什么当前只压缩物理transit指令
当前uSID只压缩物理transit node。节点上的具体服务继续使用完整SID:
fd42:3:<node>::<function>
原因是每个完整服务SID必须对应真实namespace和经过验证的End.X行为。如果只在控制器 或入口中发明一个微型function数字,而Linux节点没有对应LocalSID行为,这个数字不会 自动变成可执行服务。
10.4 混合segment链
一条高级service chain(服务链)可以交替出现:
uSID container(若干物理跳)
→ 完整服务实例SID
→ uSID container(后续物理跳)
→ 另一个完整服务实例SID
→ ...
→ 最终End.DX6或End.DT6
所有条目仍是合法128-bit IPv6 SID,写入标准SRH。
11. SR Policy是什么
11.1 Policy不是普通最短路
SR Policy(Segment Routing Policy,分段路由策略)是入口节点上的有序segment 集合以及把某类流量导入该集合的规则。入口称为Headend(头端)。
通用IETF模型用三元组识别一条SR Policy:
<Headend, Color, Endpoint>
- Headend:策略安装在哪个入口节点;
- Color:表达意图或类别的非零整数;
- Endpoint:策略面向的目的。
通用标准还允许candidate path(候选路径)、preference(偏好)、priority(优先级) 和多个带weight(权重)的segment list。本项目使用其中更小、可验证的入口本地模型。
11.2 Candidate Path
一条SR Policy可以有多个Candidate Path(候选路径)。候选路径各自带priority (优先级)或preference(偏好),每条候选又可包含一条或多条segment list。最简单的理解 是“同一个策略目标准备一条常用指令链和一条备用指令链”。怎样选择备用、何时切换,属于 使用SR Policy的系统规则,不是SRH头部自己计算。
11.3 标准Color与项目本地编号
RFC 9256中的Color是非零32-bit意图值,可参与标准SR Policy识别。项目后来会借用这个名字 表示入口本地策略编号,但具体位宽、分配和Linux mark映射必须等task身份解释以后再学习。 本编先把Color理解成headend用于区分策略类别的标签,不需要记项目实现。
11.4 Steering
**Steering(流量导入/牵引)**是把匹配的业务包送进某条SR Policy。通用过程是:
包满足本地分类条件
→ policy rule选择专用路由表
→ 该表的路由执行seg6封装
→ 包开始执行segment list
分类条件可以是目的前缀、源地址或本地mark。第四编会在已经定义task之后,完整解释本项目 怎样用eBPF、mark和多路由表实现steering。
11.5 期望策略与已安装策略必须区分
用户态算出segment list,只表示desired state(期望状态);Linux成功安装路由以后才是 applied state(已应用状态)。若业务在策略安装前发送,包仍可能走main table或不可达。 因此任何具体业务协议都必须先确认applied,再允许依赖该策略发送数据。第四编会说明项目 如何用明确的REQUEST/READY事件表达这条边界。
12. 一个不含业务专有概念的SRv6逐跳例子
假设:
- 普通IPv6端侧A接入节点17;
- 普通IPv6端侧B接入节点199;
- 入口策略要求包依次经过节点20和节点36;
- 中间物理路径由uSID container表达;
- 最终通过节点199的End.DX6交付。
概念segment链为:
uSID(17→...→20)
→ uSID(20→...→36)
→ uSID(36→...→199)
→ delivery SID@199
数据执行过程:
- 端侧A发送普通内层IPv6/UDP数据;
- 节点17的本地策略路由命中这类流量;
seg6 mode encap添加outer IPv6和SRH;- 当前uSID container把包逐跳送到节点20;
- 节点20推进container并按自己的IPv6 FIB继续;
- 后续container把包送到节点36,再送到节点199;
- 节点199命中delivery SID,执行End.DX6;
- 外层IPv6/SRH被移除,端侧B收到原内层IPv6/UDP数据。
这个例子只验证“入口写指令、节点逐段执行、末端解封装”。服务函数、任务分类、Anycast、 图片协议和编排算法都还没有加入;它们会在基础路径已经完全理解以后逐层叠加。
12.1 第二编自测
- SID为什么既可以表示位置,也可以表示功能?
- 普通transit node为什么不应随意解析SRH?
- End与End.DX6的最大区别是什么?
- uSID为什么可以压缩一串物理节点指令?
- SR Policy与普通IPv6 FIB各解决什么问题?
- 为什么用户态算出策略不等于Linux已经应用策略?