侧边栏壁纸
  • 累计撰写 53 篇文章
  • 累计收到 3 条评论

P3-04 · SAI 头文件全景:15 个 MVP API 矩阵

PacketForge
2026-08-18 / 0 评论 / 3 阅读 /
对应路线图:Phase 3 (Day 92-119) | DPDK 24.11.6 LTS | 内核对照 v6.6
代码出处:references/sai-headers/inc/(真实 SAI 头文件),引用均标 文件:行号
上一篇:P3-03 SONiC 控制面链路 | 下一篇:P3-05 sai-dpdk-mini

一句话定位

SAI(Switch Abstraction Interface)是一套纯 C 函数指针 API 集合:所有对象共用一个 sai_object_id_t 句柄,所有属性共用一个 sai_attribute_t 结构,所有能力通过 sai_api_query 按模块换函数表。看完头文件你会发现:它比内核的 net_device_ops 还要"接口化"——连对象都不是结构体,而是 ID。

三个地基类型

① OID:64 位不透明句柄(saitypes.h:131)

typedef uint64_t sai_object_id_t;

端口、VLAN、路由表项、交换机本身……万物皆 64 位。对象类型枚举(saitypes.h:188-227 节选):

typedef enum _sai_object_type_t
{
    SAI_OBJECT_TYPE_NULL                     =  0, /**< invalid object type */
    // ...
    SAI_OBJECT_TYPE_ROUTE_ENTRY              = 37,
    // ...

OID 的高位编码对象类型、低位编码索引,所以 sai_object_type_query() 能从一个 OID 反查类型(sai.h:286)。对比内核:内核每种对象是一个 struct(net_device/fib_info),SAI 统一成一个整数——因为厂商 SDK 在进程边界之外,句柄比指针跨得了界。

② attribute:id + union(saitypes.h:1815-1822)

typedef struct _sai_attribute_t
{
    /** @passparam meta */
    sai_attr_id_t id;

    /** @passparam meta */
    sai_attribute_value_t value;
} sai_attribute_t;

value 是个大 union(bool/u8/u16/u32/u64/oid/指针全塞里面)。所有 API 的参数都是 attr_count + attr_list——新增属性不破坏二进制接口,这是 SAI 能兼容十多家 ASIC 厂商的协议基础。

③ API 表:每个模块一张函数指针表

sai_switch_api_t(saiswitch.h:4261-4279)节选:

typedef struct _sai_switch_api_t
{
    sai_create_switch_fn                   create_switch;
    sai_remove_switch_fn                   remove_switch;
    sai_set_switch_attribute_fn            set_switch_attribute;
    sai_get_switch_attribute_fn            get_switch_attribute;
    sai_get_switch_stats_fn                get_switch_stats;
    // ...
} sai_switch_api_t;

应用启动时 sai_api_query(SAI_API_SWITCH, ...) 换出这张表,剩下全是函数指针间接调用。没有虚函数、没有对象模型、没有 ABI 版本地狱——C ABI 是跨厂商 SDK 的唯一公约数。

15 个 MVP API 矩阵

SAI 头文件体系与内核 net_device_ops 对照

#API签名位置作用
1sai_create_switch_fnsaiswitch.h:4113创建/连接交换机
2sai_create_port_fnsaiport.h:4296创建端口
3sai_set_port_attribute_fnsaiport.h:4320端口 admin up/down
4sai_create_vlan_fnsaivlan.h:563创建 VLAN
5sai_create_fdb_entry_fnsaifdb.h:357静态 MAC 表项
6sai_create_route_entry_fnsairoute.h:195L3 路由
7sai_create_neighbor_entry_fnsaineighbor.h:219ARP 邻接
8sai_create_next_hop_fnsainexthop.h:297下一跳对象
9sai_create_acl_table_fnsaiacl.h:3713ACL 表
10sai_create_hostif_fnsaihostif.h:1003上送 CPU 的 host 接口
11sai_send_hostif_packet_fnsaihostif.h:1385从 CPU 发包
12sai_packet_event_notification_fnsaihostif.h:1442收包回调(RX trap)
13set_switch_attribute(switch 级属性)saiswitch.h:4265switch 级属性
14sai_api_query 换表 + sai_remove_* 删除族sai.h:252 / 同名文件相邻行全生命周期

读两个有代表性的完整定义:

saifdb.h:357-360:

typedef sai_status_t (*sai_create_fdb_entry_fn)(
        _In_ const sai_fdb_entry_t *fdb_entry,
        _In_ uint32_t attr_count,
        _In_ const sai_attribute_t *attr_list);

saihostif.h:1385-1390:

typedef sai_status_t (*sai_send_hostif_packet_fn)(
        _In_ sai_object_id_t hostif_id,
        _In_ sai_size_t buffer_size,
        _In_ const void *buffer,
        _In_ uint32_t attr_count,
        _In_ const sai_attribute_t *attr_list);

套路完全一致:entry 结构描述 key(sai_fdb_entry_t 是 MAC+VLAN+port,sai_route_entry_t 是 prefix,saineighbor.h:206 的 neighbor 是 IP+rif),属性列表描述 value。把 key 和 value 分开是 SAI 和内核 API 最大的风格差异。

🐧 内核对照:net_device_ops 的"每对象一张表"

内核把操作挂在对象自身上(include/linux/netdevice.h:1410 起),tun 驱动实例(drivers/net/tun.c:1251-1262):

static const struct net_device_ops tun_netdev_ops = {
    .ndo_init        = tun_net_init,
    .ndo_uninit        = tun_net_uninit,
    .ndo_open        = tun_net_open,
    .ndo_stop        = tun_net_close,
    .ndo_start_xmit        = tun_net_xmit,
    // ...
};
维度内核 net_deviceSAI
对象表示struct net_device(结构体+指针)sai_object_id_t(64 位句柄)
操作接口net_device_ops 挂在对象里每模块一张全局 API 表
属性访问内核态直接读写字段set/get_attribute(id+union)
表项对象fib_info/neighbour 结构体entry 结构体(key 值分离)
跨进程不可(内核态)可(序列化成 Redis/grpc)
热插拔实现指针直接替换换 API 表指针

SAI 是为"控制面和数据面分属不同进程/不同厂商"设计的——它的每个设计决策(句柄、属性 union、函数指针表)都在为进程边界和二进制兼容让路。这正好解释 P3-03 里 orchagent 和 syncd 为什么能隔着 Redis 工作。

面试 3 问

  1. SAI 为什么不把属性做成结构体字段? → sai_attribute_t = id + union(saitypes.h:1815-1822),增删属性不改 ABI;对比内核直接访问 net_device 字段,SAI 必须保证跨厂商 SDK 的二进制兼容。
  2. sai_fdb_entry_t 和 attribute 怎么分工? → entry 描述 key(MAC/VLAN/port,saifdb.h:357 参数),attr_list 描述 value(如端口绑定)——和内核把 key、value 都塞进一个结构体的习惯不同。
  3. 为什么 OID 用 64 位整数而不是指针? → 对象在厂商 SDK 进程里,跨进程只能传句柄(saitypes.h:131);类型编码进 OID 高位,sai_object_type_query 反查(sai.h:286)。

🔧 动手实验

  • 把 references/sai-headers/inc/ 下 10 个头文件的 typedef sai_status_t (*sai_create_* 全部 grep 出来,做一张"对象 × 模块"对照表。
  • 写一个只含 sai_api_query 换表 + create_switch(INIT_SWITCH=false)的最小程序链接 sairedis。
  • 产出 perf-data/P3/04-sai-api-matrix.md。

下一篇:P3-05 · sai-dpdk-mini:用 DPDK 实现 SAI 最小适配器

0

评论 (0)

取消