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

P3-06 · host_if 打通:让 FRR/BGP 跑在纯软件转发上

PacketForge
2026-08-18 / 0 评论 / 2 阅读 /
对应路线图:Phase 3 (Day 92-119) | DPDK 24.11.6 LTS | 内核对照 v6.6
上一篇:P3-05 sai-dpdk-mini | 下一篇:P4-01 PCIe

一句话定位

交换机光会转发没用,BGP/OSPF 这些协议要能收发包。SAI 为此定义了一整套 hostif(host interface)抽象:控制面报文(BGP 的 TCP 179、ARP、ICMP)从数据面 trap 上 CPU,控制面要发的包经 hostif 送回数据面——这就是把 FRR 接进纯软件转发的最后一公里。

hostif 三件套:类型、创建、收发

① 三种通道类型(saihostif.h:848-859)

typedef enum _sai_hostif_type_t
{
    /** Netdevice */
    SAI_HOSTIF_TYPE_NETDEV,

    /** File descriptor */
    SAI_HOSTIF_TYPE_FD,

    /** Generic netlink */
    SAI_HOSTIF_TYPE_GENETLINK

} sai_hostif_type_t;

三种类型的语义差异:NETDEV 让 SAI 实现去创建一个 Linux 网卡(hostif 就是"内核能看到的那张卡");FD 用文件描述符收发包(最高效,syncd 直接读 socket);GENETLINK 走 generic netlink family。创建时的 SAI_HOSTIF_ATTR_TYPE 是 MANDATORY_ON_CREATE(saihostif.h:906-908):

    /**
     * @brief Host interface type
     *
     * @type sai_hostif_type_t
     * @flags MANDATORY_ON_CREATE | CREATE_ONLY
     */
    SAI_HOSTIF_ATTR_TYPE = SAI_HOSTIF_ATTR_START,

② 创建与收发(saihostif.h:1003-1007、1442-1447、1385-1390)

typedef sai_status_t (*sai_create_hostif_fn)(
        _Out_ sai_object_id_t *hostif_id,
        _In_ sai_object_id_t switch_id,
        _In_ uint32_t attr_count,
        _In_ const sai_attribute_t *attr_list);

typedef void (*sai_packet_event_notification_fn)(
        _In_ sai_object_id_t switch_id,
        _In_ sai_size_t buffer_size,
        _In_ const void *buffer,
        _In_ uint32_t attr_count,
        _In_ const sai_attribute_t *attr_list);

(sai_send_hostif_packet_fn 与回调同构,只是返回值是 sai_status_t。)收包靠回调(packet_event 挂在 switch 属性上,saiswitch.h:1970-1972 注释),发包靠 send_hostif_packet_fn,全套在 sai_hostif_api_t(saihostif.h:1452 起)。

端到端:一个 BGP 报文的旅程

hostif 与 BGP 端到端报文路径

进(BGP 邻居 → FRR):数据面收到 TCP 179 报文 → 查表 miss(不是转发流量)→ 命中 trap 规则 → 沿 packet_event 回调进 syncd → syncd 经 NETDEV 写进 Linux 内核协议栈 → FRR 的 zebra/bgpd socket 收到。

出(FRR → BGP 邻居):bgpd 发 TCP 179 报文 → 内核路由表把流量指向 hostif 网卡 → SAI 实现的 ndo_start_xmit(NETDEV 型)或 syncd 读 FD → 调 send_hostif_packet_fn → 数据面封装后从物理口发出。

对比:内核 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,
    // ...
};

NETDEV 型 hostif 本质就是一个"SAI 实现的 tun":内核侧是标准网卡,驱动侧不是 DMA 而是 send_hostif_packet_fn。这是理解 hostif 最快的类比。

设计取舍:NETDEV vs FD vs GENETLINK

NETDEVFDGENETLINK
内核集成原生(就是网卡)无,syncd 自己读需要 family 注册
性能中(内核协议栈必经)高(绕过内核)低(netlink 序列化)
实现复杂度低(tun 参考现成)中高
适用快速打通 FRR生产数据面工具类通道

学习路线选 NETDEV:sai-dpdk-mini 阶段第一目标是把 FRR 跑起来,NETDEV 让 BGP 报文"看起来像普通网卡流量",内核协议栈、FRR 全都不动。等 perf 数据显示 hostif 成为瓶颈,再切 FD 型绕内核。

🐧 内核对照:tun 的收包路径

tun_get_user(drivers/net/tun.c:1750)是字符设备读写的核心——应用写 /dev/net/tun 就进这里(节选):

static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
                void *msg_control, struct iov_iter *from,
                int noblock, bool more)
{
    struct tun_pi pi = { 0, cpu_to_be16(ETH_P_IP) };
    struct sk_buff *skb;
    // ...
    if (!(tun->flags & IFF_NO_PI)) {
        if (len < sizeof(pi))
            return -EINVAL;
        len -= sizeof(pi);

        if (!copy_from_iter_full(&pi, sizeof(pi), from))
            return -EFAULT;
    }
    // ...(IFF_TAP 时校验 ETH_HLEN 等,省略)...
环节tun 驱动SAI hostif (NETDEV)
内核侧对象/dev/net/tun 字符设备标准 net_device
发包进"网卡"写 char dev → tun_get_userndo_start_xmit → send_hostif_packet_fn
收包出"网卡"tun_net_xmit → skb 进协议栈packet_event 回调 → 内核协议栈
数据面另一端用户态进程读 fdSAI 数据面(硬件或 DPDK)

方向正好相反:tun 的"网线那头"是用户态进程,hostif 的"网线那头"是交换机数据面。同一份 net_device_ops 机制,两端换了个位置。

面试 3 问

  1. 三种 hostif 类型的区别? → NETDEV 创建 Linux 网卡、FD 用文件描述符、GENETLINK 走 generic netlink(saihostif.h:848-859);学习期选 NETDEV 最快打通 FRR。
  2. BGP 报文怎么从数据面到 FRR? → trap 命中 → packet_event 回调(saihostif.h:1442)→ syncd 经 NETDEV 入内核协议栈 → bgpd socket。
  3. hostif 和 tun 是什么关系? → NETDEV 型 hostif 就是"SAI 实现的 tun":内核侧同一套 net_device_ops(tun.c:1251-1262 对照),驱动侧从"读写 fd"换成 SAI 收发函数。

🔧 动手实验

  • ip tuntap add dev bgp0 mode tap 建一个真 tun,跑 FRR 邻居建连,抓包看 TCP 179 的路径(先在内核侧建立直觉);
  • 在 sai-dpdk-mini 里实现 create_hostif + send_hostif_packet 的 NETDEV 桩,ip link 能看到这张"SAI 网卡";
  • FRR zebra 把 10.0.0.0/24 路由注入,观察路由经 APPL_DB → ASIC_DB 最终变成 rte_lpm_add(衔接 P3-03/05);
  • 端到端验证:BGP 邻居建连 + 数据面转发 + ping 通。
  • 产出 perf-data/P3/06-hostif-frr.md(邻居建连日志 + route 注入链路截图 + 转发吞吐)。

下一篇:P4-01 · PCIe Configuration Space 与 BAR 解码(Phase 4 学到时发布)

0

评论 (0)

取消