第156篇:MPLS 转发过程——标签压入、交换、弹出
关键词
MPLS 转发、标签压入(Push)、标签交换(Swap)、标签弹出(Pop)、标签栈、LFIB、ILM
一、MPLS 数据面与转发模型
前几篇我们了解了 LDP 如何分发标签,本文从数据面的角度,完整追踪一个 IP 包在 MPLS 域中的转发过程。
1.1 关键概念:标签栈(Label Stack)
MPLS 支持多层标签,最多可嵌套数层。每一层标签是一个 4 字节的条目:
IP 数据包(不带标签)
┌─────────────────┐
│ IP 报文 │
└─────────────────┘
单层标签(MPLS 基本转发)
| Label | IP 报文 |
|---|---|
双层标签(MPLS VPN 等场景)
| Lable2 | Label1 | IP 报文 | ← Label1 是栈底(S=1) |
|---|---|---|---|
- S 位标识栈底(1 = 最后一层标签)
- 标签栈可以嵌套,用于:外层(隧道标签)+ 内层(VPN 标签)
二、三种标签操作全解析
2.1 Push(压入)
操作:在标签栈顶部添加一个新标签。
操作前: ┌─────────────────────┐ │ IP 报文 │ └─────────────────────┘
PUSH Label = 100: | Label 100 | IP 报文 | ← S=1(栈底) | | --- | --- | --- |
执行位置:Ingress LSR(数据包进入 MPLS 域时)
R1(普通 IP)→ R2(Ingress LSR):检查路由 → "目标匹配 MPLS 转发"
→ 查找 LFIB → push 标签 300 → 发送到 R3
2.2 Swap(交换)
操作:用新标签替换栈顶标签,栈的深度不变。
操作前: | Label 300 | IP 报文 | ← S=1 | | --- | --- | --- |
SWAP Label 300 → Label 200: | Label 200 | IP 报文 | ← S=1 | | --- | --- | --- |
执行位置:Transit LSR(中间节点)
R3(Transit LSR):
收到标签 300 → 查 LFIB → 出标签 200
→ 交换(300 → 200)→ 发送到 R4
2.3 Pop(弹出)
操作:移除栈顶标签。如果是栈底标签(S=1),弹出后恢复为 IP 包。
操作前: | Label 200 | IP 报文 | ← S=1 | | --- | --- | --- |
POP: ┌─────────────────────┐ │ IP 报文 │ └─────────────────────┘
执行位置:Egress LSR(数据包离开 MPLS 域时)
R4(Egress LSR):
收到标签 200 → 查 LFIB → 操作为 Pop
→ 弹出标签 → 恢复 IP 包 → 普通 IP 转发
三、完整转发过程(逐跳详解)
3.1 场景设定
网络拓扑: R1 ── R2 ── R3 ── R4 │ 10.0.0.0/24
路由及标签信息:
节点 LFIB 条目 R1 路由:10.0.0.0/24 via R2(下一跳) 无 MPLS 标签(普通 IP 转发) R2 入标签:- 出标签:200 下一跳:R3 (Ingress) R3 入标签:200 出标签:100 下一跳:R4 (Transit) R4 入标签:100 出标签:- 弹出标签 (Egress)
3.2 转发过程
步骤 1:R1 发送 IP 包
R1 ───────────── IP(src=192.168.1.1, dst=10.0.0.1)──────────▶ R2
R1 不知道 MPLS,只做普通 IP 路由
步骤 2:R2(Ingress)压入标签
R2 收到 IP 包
① IP 路由查找 → 匹配 10.0.0.0/24,下一跳为 R3
② 发现该 FEC 有 MPLS 标签绑定
③ 查 LFIB:入标签=none,出标签=200,下一跳=R3
④ 执行 Push:压入标签 200
⑤ 沿链路转发
R2 ─────── MPLS(Label=200)───────▶ R3
步骤 3:R3(Transit)交换标签
R3 收到带标签 200 的包
① 查 LFIB:入标签=200 → 出标签=100,下一跳=R4
② 执行 Swap:把标签 200 换成 100
③ 沿链路转发
R3 ─────── MPLS(Label=100)───────▶ R4
步骤 4:R4(Egress)弹出标签
R4 收到带标签 100 的包
① 查 LFIB:入标签=100 → 操作=Pop
② 执行 Pop:移除标签
③ 恢复为普通 IP 包
④ 查 IP 路由表 → 匹配 10.0.0.0/24 → 从出接口转发
R4 ───────────── IP(src=192.168.1.1, dst=10.0.0.1)──────────▶ 目标网络
四、ILM(Incoming Label Map)和 NHLFE(Next Hop Label Forwarding Entry)
4.1 ILM 和 NHLFE 的关系
MPLS 转发依赖于两个重要的数据结构:
ILM NHLFE
| 入标签 → NHLFE 指针 ───────────────────── In-Label NHLFE Index 200 1 300 2 500 3 | ───────▶ └─────────────────────────┘ | • 出标签 • 出接口 • 下一跳 • 操作(Swap/Pop/Push) • 标签栈操作 |
|---|---|---|
| 组件 | 作用 | 查找方式 |
|---|---|---|
| ILM | 入标签到 NHLFE 的映射表 | 收到标签后查 ILM |
| NHLFE | 定义了对标签的详细操作 | ILM 返回 NHLFE 指针后执行 |
4.2 完整的标签查找流程
收到带标签的分组
│
▼
提取入标签(栈顶)
│
▼
查 ILM
│
▼
获取 NHLFE 条目:操作(Swap/Pop)、出标签、出接口、下一跳
│
▼
执行标签操作
│
▼
封装新的 MAC 头部 → 发送
五、隐式空标签(Implicit NULL)与 PHP
隐式空标签(Implicit NULL,标签值 = 3)是 PHP(Penultimate Hop Popping)机制的核心,详见第157篇。这里只简述在转发中的作用:
没有 PHP 的转发(Egress 承担额外负载):
R2(push 200)→ R3(swap 100)→ R4(pop 100 + IP 转发)
有 PHP 的转发(Egress 只做 IP 转发):
R2(push 200)→ R3(pop → 裸 IP)→ R4(仅 IP 转发)
PHP 让 Egress LSR 免于做标签查找,直接做 IP 转发,降低了 Egress 的负担。
六、双层标签转发(VPN 场景预览)
在 MPLS VPN(L3VPN)中,通常使用两层标签:
外层标签(隧道标签) 内层标签(VPN 标签) ↓ ↓ | LDP标签 (L2→L3) | VPN标签 (L3→L4) | IP 报文 | | --- | --- | --- | S=0 S=1
转发过程:
PE1(Ingress):
push VPN 标签(内层)
push LDP 标签(外层)
→ P 节点
P(Transit):
只交换外层标签(LDP)
不看内层 VPN 标签 → 透明传输
PE2(Egress):
PHP 弹出外层标签 → 看到内层 VPN 标签
根据 VPN 标签找到对应 VRF → 解封装 IP → IP 转发到 CE
七、MPLS 转发 vs IP 转发对比
| 对比项 | IP 转发 | MPLS 转发 |
|---|---|---|
| 查找内容 | 目的 IP 地址(32 位,前缀匹配) | 标签(20 位,精确匹配) |
| 查找表 | 路由表(RIB)+ FIB | ILM + NHLFE |
| 查找方式 | 最长匹配(LPM) | 精确匹配 |
| 每跳查表 | 是 | 是(查的是标签表) |
| 路由决策 | 每跳独立 | 在 Ingress 一次性确定整条路径 |
| 支持多路径 | ECMP(基于 IP) | ECMP(基于标签) |
八、总结
| 知识点 | 核心要点 |
|---|---|
| Push | Ingress LSR 在标签栈顶部添加新标签 |
| Swap | Transit LSR 用新标签替换栈顶标签 |
| Pop | Egress LSR 移除栈顶标签,恢复为 IP 包 |
| ILM | 入标签到 NHLFE 的映射(入口查表) |
| NHLFE | 定义出标签、出接口、操作类型 |
| 标签栈 | 支持多层标签嵌套,S 位标识栈底 |
| 双层转发 | 外层 LDP 标签 + 内层 VPN 标签,用于 MPLS VPN |
九、思考
- MPLS 域中有哪三种标签操作?分别由哪类 LSR 执行?
- 什么是 ILM?什么是 NHLFE?两者之间的关系是什么?
- 在两层标签的转发过程中,Transit LSR 对内层标签做什么操作?
- 为什么 Egress LSR 不需要做标签查找(PHP 之后)?
- MPLS 标签查找和 IP 路由查找的根本区别是什么?
下篇预告:第157篇《PHP(次末跳弹出)机制》——为什么 MPLS 需要在次末跳就弹出标签?Implicit NULL 和 Explicit NULL 有什么区别?