C COMPIN BLOG

COMPIN TECHNICAL NOTE

新手教材(2/13)|第二编:SRv6 与 SR Policy

解释 Segment、SID、SRH、Linux SRv6 行为、uSID 与 SR Policy 怎样共同表达一条可执行路径。

本文是《从 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:

  • 入口用Linuxseg6 mode encap添加外层IPv6和SRH;
  • 节点用Linuxseg6local执行本地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 ListSID数组有序指令的线表示

为了便于人阅读,本书总是按执行顺序写:

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

数据执行过程:

  1. 端侧A发送普通内层IPv6/UDP数据;
  2. 节点17的本地策略路由命中这类流量;
  3. seg6 mode encap添加outer IPv6和SRH;
  4. 当前uSID container把包逐跳送到节点20;
  5. 节点20推进container并按自己的IPv6 FIB继续;
  6. 后续container把包送到节点36,再送到节点199;
  7. 节点199命中delivery SID,执行End.DX6;
  8. 外层IPv6/SRH被移除,端侧B收到原内层IPv6/UDP数据。

这个例子只验证“入口写指令、节点逐段执行、末端解封装”。服务函数、任务分类、Anycast、 图片协议和编排算法都还没有加入;它们会在基础路径已经完全理解以后逐层叠加。

12.1 第二编自测

  1. SID为什么既可以表示位置,也可以表示功能?
  2. 普通transit node为什么不应随意解析SRH?
  3. End与End.DX6的最大区别是什么?
  4. uSID为什么可以压缩一串物理节点指令?
  5. SR Policy与普通IPv6 FIB各解决什么问题?
  6. 为什么用户态算出策略不等于Linux已经应用策略?

总目录 · 上一篇:第一编:从比特到 IPv6 路由 · 下一篇:第三编:300 节点系统的控制面和数据面