数据面转发逻辑分层详解 —— L2/L3/ACL 在 SAI 链路上的完整映射
本文回答:一个报文的转发行为(L2 交换、L3 路由、ACL 过滤、上 CPU),在 SONiC 的每一层分别对应什么对象、什么芯片表?配置变更时数据到底流向哪里、会在哪些"墙"上撞住?
0. 总览:转发对象的依赖图
┌────────────── SAI 对象依赖关系 ──────────────┐
│ │
L2 线: VLAN ──→ VLAN Member ──→ Bridge Port ──→ FDB Entry │
│ │
L3 线: Virtual Router ──→ Router Interface ──→ Neighbor Entry │
│ │
├──────→ Next Hop ──→ Route Entry │
│ │ │
│ Next Hop Group ─┘ (ECMP) │
│ │
ACL 线: ACL Table ──→ ACL Entry ──→ ACL Counter │
│ │
└→ Table Group ──→ Group Member ──→ 绑定到端口/VLAN/LAG │
│ │
上 CPU: Hostif Trap ──→ Trap Group(限速) ──→ Hostif(内核网口) │
│ │
└──────────────────────────────────────────────┘依赖方向就是删除顺序的约束:守门员层(meta)的引用计数强制"先删引用者,再删被引用者"。看懂这张图,就看懂了 syncd 下发操作的顺序,也看懂了为什么"删不掉一个还在被用的下一跳"。
1. L2 交换路径
1.1 逐层映射
配置层 VLAN / VLAN_MEMBER 配置
│
编排层 orchagent 直接消费配置(L2 不绕应用库)
│ 组装出 VLAN、VLAN Member、Bridge Port 三类 SAI 对象
│
1 号库 出现 VLAN / VLAN_MEMBER / BRIDGE_PORT / FDB_ENTRY 四类表项
│
厂商 SAI VLAN、桥接、FDB 三个实现文件分别接手
│
BCM SDK 建 VLAN、加成员端口、写 MAC 地址项
│
芯片 VLAN 表 + L2 地址表(TD3/TH3 上是共享存储池里的地址项)1.2 FDB 的特殊性:唯一的"硬件也会说话"的 L2 对象
下行(静态 MAC) 上行(芯片学习/老化)
配置 → orchagent 芯片学到新 MAC / 老化删除
→ 1 号库 FDB 表项 → SDK 线程捕获
→ 厂商 SAI → SAI 通知回调
→ 芯片 L2 表(标记静态) → syncd 通知处理器
├→ 发通知给 orchagent
└→ 直接回写 1 号库 FDB 表 ★注意上行路径里那个"直接回写"——它是保证 1 号库和芯片状态一致的机制。排障"MAC 学了但库里没有"时,第一嫌疑是通知通道,而不是学习本身。
1.3 Bridge Port 的隐含角色
SONiC 的 L2 模型里,"端口加入 VLAN"实际是桥端口 + VLAN 成员两个对象的组合。端口相关转发异常时,先确认桥端口对象存在、类型正确——这是 L2 问题的常用第一手检查。
2. L3 路由路径(博通平台的主战场)
2.1 对象创建顺序与职责分工
① Virtual Router (VRF)
└─ 一张独立的路由表命名空间
│
② Router Interface (RIF)
└─ "哪个口/哪个 VLAN 属于哪个 VRF" + 本端 MAC
│
③ Neighbor Entry ────── 管解析:邻居 IP → 目的 MAC(ARP/ND 的结果)
│
④ Next Hop ──────────── 管方向:下一跳 IP + 出接口(RIF)
│
⑤ Route Entry ───────── 管命中:VRF + 前缀 → 指向 Next Hop 或 Group
│
⑥ Next Hop Group + Member ── 等价多路径:一组 Next Hop 的集合邻居和下一跳是解耦的:邻居管"IP 到 MAC 的解析",下一跳管"路由的出方向和下一跳 IP",两者在芯片的 L3 egress 对象处汇合。理解这个分工,才能理解为什么邻居失败和路由失败是两种不同的报错。
2.2 下沉到博通芯片的对照表
┌─────────────┬───────────────────────┬──────────────────────────────┐
│ SAI 对象 │ SDK 侧动作 │ 芯片表(TD3/TH3) │
├─────────────┼───────────────────────┼──────────────────────────────┤
│ Route Entry │ 路由添加 │ ALPM:pivot TCAM + 数据桶 │
├─────────────┼───────────────────────┼──────────────────────────────┤
│ Neighbor │ 主机表项添加 │ L3 主机表;溢出时可转 ALPM │
├─────────────┼───────────────────────┼──────────────────────────────┤
│ Next Hop │ 创建 egress 对象 │ L3 出方向下一跳表 │
├─────────────┼───────────────────────┼──────────────────────────────┤
│ NH Group │ 创建 ECMP 组 │ ECMP 组表 │
├─────────────┼───────────────────────┼──────────────────────────────┤
│ RIF │ 创建 L3 接口 │ L3 入接口表 / 出接口表 │
├─────────────┼───────────────────────┼──────────────────────────────┤
│ VRF │ 创建/复用 VRF │ VRF 表 │
└─────────────┴───────────────────────┴──────────────────────────────┘2.3 容量链:转发问题先定位"哪一层先满"
邻居/路由下不去,本质是一条容量链上某一环先到顶。每一环满了,上层表现一模一样,但修法完全不同:
第 1 环:厂商 SAI 软件数据库的上限
└─ 纯软件计数,芯片可能还空着。满了报"表满",但硬件无辜
第 2 环:L3 主机表(邻居的家)
└─ TH3 上固定 16K,硬件锁死,配置改不动
v6 邻居占双倍宽度 → 实际只装得下 8192 条 v6 ND
第 3 环:host-as-route 溢出通道
└─ 主机表满了以后,把邻居以主机路由形式压进 ALPM
需要芯片开关和标志位同时满足才启用
★ v6 额外门槛:128 位路由模式开关必须开
→ ipv6-64 模式下此路不通,8192 就是 v6 ND 的天花板
第 4 环:ALPM(路由的家)
└─ pivot TCAM 规模有限,同前缀共享 pivot 的条目便宜、
随机散列的主机路由是 pivot 消耗大户
数据桶可用 bank 数由 FPEM 是否占用决定(4 或 8,无中间档)
第 5 环:L3 出方向下一跳表
└─ 每个唯一的 (MAC, 端口, VLAN) 组合占一个 egress 对象定位方法:用芯片交互命令看硬件真实用量,逐环对比——哪一环先贴到顶,问题就在哪一环。
2.4 DEF.CHECK:ALPM 模式的隐藏规则
ALPM 组合模式有一条硬件级约束:非默认 VRF 里的第一条路由,必须是默认路由(0.0.0.0/0 或 ::/0)。违反时 SDK 直接报参数非法。
这是"路由莫名 Invalid parameter"的最高频根因,特征是:
- 报错参数非法,但路由前缀本身完全合法;
- 只在某些 VRF 上出现;
- 和下发顺序强相关——第一条不是默认路由就触发。
取证靠调用录像文件(第三篇讲),确认该 VRF 第一条路由的下发顺序即可实锤。SDK 侧的详细报错只进芯片日志通道,不进系统日志——光 grep 系统日志会误判为"SDK 没报错"。
2.5 IP2ME:到本机的流量走特殊路径
目的地址是本机接口/环回地址的主机路由,SAI 层走"IP2ME"分支:理想路径是下一条芯片路由并把报文 punt 上 CPU。但在 TH3 的 ipv6-64 模式下,v6 的 128 位主机路由塞不进 64 位的 ALPM key,这条路必然失败——此时改由 hostif trap 兜底上 CPU。
现象特征:路由配置失败的报错,配对的是 v6 /128 + 本机接口。看到这对组合先想 IP2ME,不要当成普通路由故障。
2.6 上 CPU 路径:hostif trap
控制面报文(ARP/ND/BGP/LLDP……)的"转发"不经过前面任何一张表,走独立的 trap 通道:
配置(COPP 策略/协议使能)
→ 编排层组装 trap 对象、限速组、内核网口对象
→ 厂商 SAI 配置芯片的"上 CPU 原因"和限速
→ 芯片命中 reason → DMA 上 CPU → 内核网络接口 → 协议栈"三层表项全对但 ping 不通/邻居学不到"的案子,十有八九要查这条通道。
3. ACL 路径
3.1 对象模型与芯片映射
SAI 侧 芯片侧
┌────────────────────────┐ ┌──────────────────────────┐
│ ACL Table │ │ 字段处理器 group │
│ · 阶段(入/出/前置) │ ───────→ │ · 按阶段落 IFP/VFP/EFP │
│ · 绑定点类型 │ │ · 占用 TCAM slice │
│ · 使能哪些匹配字段 │ │ │
│ │ │ │ │
│ ▼ │ │ │
│ ACL Entry │ ───────→ │ TCAM 表项 │
│ · 优先级 │ │ · 匹配域 + 动作 │
│ · 匹配值 + 动作 │ │ · 可选硬件计数器 │
│ │ │ │ │
│ ▼ │ │ │
│ Table Group + Member │ ───────→ │ 表项真正 install 进芯片 ★ │
│ (绑定到端口/VLAN/LAG) │ │ │
└────────────────────────┘ └──────────────────────────┘3.2 "创建成功 ≠ 下进芯片"
ACL 链条上最重要的认知:对象创建成功只代表软件结构建好,表项 install 进芯片是独立一步,可能单独失败——TCAM 满、slice 冲突都在这一步暴雷,且这类失败在系统日志里往往只有一句笼统的资源不足。ACL"配了不拦"的案子,标准动作是直接在芯片交互 shell 里 dump 字段处理器的 group 和表项。
3.3 与视图比对的交互
ACL 条目是最佳匹配器的重点关照对象(按优先级预匹配)。改规则时 syncd 倾向原地修改;一旦匹配失败走删除重建,会出现瞬时放行窗口——安全敏感场景必须意识到这个行为。
4. QoS / 缓存路径(速览)
配置(缓冲池/水线/队列/调度/丢弃曲线/优先级映射)
→ SAI 对象:缓冲池、缓冲水线、队列、调度器、调度组、WRED、映射表
→ 厂商 SAI 的 MMU 子系统(按架构分叉)
→ 芯片 MMU:cell 分配、队列调度树、WRED 曲线调度树(调度组的层级结构)是 QoS 问题的定位核心——芯片交互命令可以把芯片侧真实的树形结构打出来,和 SAI 对象树逐层对照。
5. 隧道与 VXLAN(速览)
隧道配置
→ SAI 对象:隧道本体 + 映射表 + 映射条目 + 终结项
→ 厂商 SAI 隧道子系统(分架构变体)
→ 芯片 overlay 引擎一个容易踩的坑:TD3 上 VXLAN 相关芯片参数是按"有没有开 VXLAN"动态增删的——开不开 VXLAN,芯片的表分配方案不一样。排障前先确认当前处于哪种形态。
6. 总结:现象 → 嫌疑层 → 验证手段
┌────────────────────┬────────────────────────┬─────────────────────────┐
│ 现象 │ 先怀疑 │ 第一手验证 │
├────────────────────┼────────────────────────┼─────────────────────────┤
│ 邻居下不去 │ 容量链五环逐一对比 │ 芯片主机表用量 vs 软件上限 │
├────────────────────┼────────────────────────┼─────────────────────────┤
│ 路由报参数非法 │ DEF.CHECK(首条非默认) │ 调用录像看 VRF 内下发顺序 │
├────────────────────┼────────────────────────┼─────────────────────────┤
│ 静态 MAC 不生效 │ FDB 通道 │ 1 号库表项 vs 芯片 L2 表 │
├────────────────────┼────────────────────────┼─────────────────────────┤
│ ACL 配了不拦 │ install 失败/绑定点错 │ 芯片字段处理器 dump │
├────────────────────┼────────────────────────┼─────────────────────────┤
│ 协议报文不上 CPU │ trap 通道 │ 芯片上 CPU 配置 + 网口存在性│
├────────────────────┼────────────────────────┼─────────────────────────┤
│ 改配置后转发闪断 │ 匹配失败走了删除重建 │ syncd 日志里的操作单 │
├────────────────────┼────────────────────────┼─────────────────────────┤
│ v6/128 到本机失败 │ IP2ME 特殊路径 │ 看报错是否配对 RIF+全1掩码 │
└────────────────────┴────────────────────────┴─────────────────────────┘下一篇:用 1 号数据库把上面每一层变得"看得见"——库里存了什么、怎么读、调用录像怎么回放、五层日志怎么开、错误码怎么解码。
评论 (0)