2. SR-MPLS 数据平面与标签栈转发
SR-MPLS 的数据平面并不引入新的报文封装格式,它复用了 MPLS 的标签转发机制,把“路径信息”编码进标签栈里。理解 SR-MPLS 的转发行为,核心是理解三件事:SID 如何编码成标签、标签栈如何被逐跳操作、以及 PHP 与显式空标签如何影响最后一跳。本节围绕这三条主线展开,并结合一个可复现的示例,说明报文在 SR-MPLS 域内逐跳转发的完整过程。
SID 编码与标签值
在 SR-MPLS 中,一个 SID 被编码为一个 32 位标签值,其结构与 MPLS 标签一致:高 20 位是标签值(Label Value),随后 3 位是 TC(Traffic Class,原 EXP),1 位是 S 标志(Bottom of Stack),低 8 位是 TTL。SR-MPLS 的 SID 通常落在 16000~23999 的标签区间(具体范围由设备实现决定),这个区间被称为 SR 专用标签空间,与 LDP、RSVP-TE 等传统 MPLS 标签空间区分开,避免冲突。
SID 分为两类。全局 SID(Global SID)在整个 SR 域内唯一,通常由 IGP(IS-IS 或 OSPF)扩展通告,常见形式是前缀 SID(Prefix-SID),它绑定到某个节点的 Loopback 地址,代表“到达该节点”。本地 SID(Local SID)只在本地节点有效,由节点自行分配,常见形式是邻接 SID(Adjacency-SID),它绑定到某条具体链路,代表“从这条链路发出”。全局 SID 用于构建端到端路径,本地 SID 用于强制指定某一段链路,两者组合可以表达任意显式路径。
需要特别区分“SID”和“转发表项”。SID 是控制平面通告的标识,节点收到 SID 后,会在本地生成对应的转发表项(Incoming Label Map,ILM),把入标签映射到出标签和出接口。转发时真正查的是这张表,而不是 SID 本身。
标签栈操作:Push、Swap、Pop、Continue
SR-MPLS 的转发动作可以归纳为四类,它们决定了报文在每一跳的标签栈变化:
- Push(压栈):在报文头部压入一个或多个标签。入口 PE 通常执行 Push,把整条路径的 SID 列表按顺序压入,栈顶是第一个要经过的节点。
- Swap(交换):把栈顶标签替换为另一个标签。传统 MPLS 中常见,但在 SR-MPLS 中,如果使用全局 SID,中间节点通常不需要 Swap,直接按栈顶标签转发即可。
- Pop(弹栈):弹出栈顶标签。当栈顶 SID 表示“到达本节点”时,节点弹出该标签,继续按新的栈顶转发。
- Continue(继续):不改变标签栈,仅按栈顶标签查表转发。这是 SR-MPLS 中间节点最常见的动作。
这四类动作的组合,使得 SR-MPLS 可以在不依赖每跳信令的情况下,让报文沿着标签栈中预先编排的 SID 序列前进。栈顶 SID 决定当前跳,弹出后下一个 SID 成为新的栈顶,如此逐跳推进。
PHP 与显式空标签
PHP(Penultimate Hop Popping,倒数第二跳弹出)是 MPLS 的经典优化:倒数第二跳节点在把报文发给最后一跳之前,提前弹出栈顶标签,使最后一跳节点直接按 IP 或内层标签转发,减轻其负担。在 SR-MPLS 中,PHP 依然存在,但需要与 SID 语义配合。
当某个前缀 SID 被通告为“显式空标签”(Explicit NULL,标签值 0)时,表示该 SID 对应的节点希望自己执行弹出动作,而不是让上一跳提前弹出。此时倒数第二跳不会弹出标签,而是把标签 0 压入并转发;最后一跳收到标签 0 后,弹出标签并按内层继续处理。显式空标签的价值在于:它保留了最后一跳对 SID 的可见性,便于最后一跳做基于 SID 的统计、策略或保护,代价是多消耗一个标签栈深度。
与之相对的是“隐式空标签”(Implicit NULL,标签值 3)。当节点通告隐式空标签时,倒数第二跳执行 PHP,直接把标签弹出,最后一跳收到的是无标签报文。隐式空标签节省栈深,但最后一跳无法感知该 SID。工程上选择哪种,取决于是否需要最后一跳做 SID 级处理。
逐跳转发示例分析
假设一个 SR-MPLS 域内有四台设备:PE1、P1、P2、PE2。PE1 的 Loopback 为 1.1.1.1/32,P1 为 2.2.2.2/32,P2 为 3.3.3.3/32,PE2 为 4.4.4.4/32。IGP 为每个 Loopback 分配前缀 SID:PE1=16001,P1=16002,P2=16003,PE2=16004。现在 PE1 要向 PE2 发送报文,并希望路径为 PE1 → P1 → P2 → PE2。
PE1 作为入口,执行 Push,把路径 SID 列表按“从远到近”的顺序压栈,栈顶是第一个要经过的节点。因此栈内容自顶向下为:16002(P1)、16003(P2)、16004(PE2)。报文发出后:
- P1 收到报文,查看栈顶标签 16002。该标签是 P1 自己的前缀 SID,表示“到达 P1”。P1 执行 Pop,弹出 16002,此时栈顶变为 16003。
- P1 按新的栈顶 16003 查转发表,发现下一跳是 P2,执行 Continue,把报文转发给 P2。
- P2 收到报文,栈顶为 16003,命中自己的前缀 SID,执行 Pop,栈顶变为 16004。
- P2 按栈顶 16004 查表,下一跳是 PE2。此时若 PE2 通告的是隐式空标签,P2 执行 PHP,弹出 16004,把无标签报文发给 PE2;若 PE2 通告的是显式空标签,P2 不弹出,而是压入标签 0 后转发。
- PE2 收到报文:隐式空场景下直接按内层 IP 转发;显式空场景下先弹出标签 0,再按内层 IP 转发。
这个过程中,P1 和 P2 都没有执行 Swap,只做了 Pop 和 Continue,这正是 SR-MPLS 使用全局 SID 时的典型行为:中间节点不需要维护每条路径的标签映射,只需要认识自己的 SID 即可。转发表规模从“路径数量级”降到“节点数量级”,这是 SR-MPLS 可扩展性的关键来源。
工程要点与常见误区
第一,SID 与标签值不是一一对应的固定关系,不同厂商、不同域可能使用不同的标签区间,工程上要以实际通告为准。第二,全局 SID 的唯一性由 IGP 保证,若两个节点通告了相同的前缀 SID,会导致转发冲突,必须通过规划或冲突检测避免。第三,PHP 与显式空标签的选择会影响最后一跳的可观测性,若需要在 PE2 上做基于 SID 的流量统计或策略,应使用显式空标签。第四,标签栈深度受设备能力和 MTU 限制,路径越长栈越深,规划时需要评估。
掌握以上内容后,读者应能根据给定的 SID 列表,推导出报文在每一跳的标签栈变化和转发动作,并能判断 PHP 与显式空标签对最后一跳行为的影响。这是分析 SR-MPLS 数据平面问题的基础能力。