Files
newbieandClaude Code 71c90bb632 fix: uNOS 硬件基线对齐 jb3455b 实测
- 1G 大页规划(×2-4) vs 实机实际 1G=0,须加内核引导参数
- 数据面单核 → 1-2 核:纯转发单核够,叠加 NAT/ACL/QoS 后无余量
- unos-design §2.2 物理口笔误 82576 × 2 → X540-T2 10G × 2
- home-gateway 补「研发机 vs 量产 BOM」分工说明
- §4.0「单核转发无法扩展」限定为 Cloud Edge 目标,与 Home CPE 区分

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-10 00:13:26 +08:00

63 KiB
Executable File
Raw Permalink Blame History

uNOS 详细设计说明书

版本: v2.0 — 生产级架构 (RSS 多核 + Graph Pipeline + 内联 TCP) 日期: 2026-07-29 关联文档: uNOS_REQUIREMENTS.md(需求分析)、lazy-puzzling-rocket.md(开发执行计划)


目录

  1. 设计概述
  2. 架构设计
  3. 双产品设计
  4. 数据面详细设计
  5. 控制面详细设计
  6. 自研 TCP/IP 协议栈详细设计
  7. 容器网络详细设计
  8. 管理面详细设计
  9. 高可用详细设计
  10. 构建系统设计
  11. 测试设计
  12. 接口契约

一、设计概述

1.1 设计目标

设计并实现一个自研用户态网络操作系统(uNOS),核心特性:

  • 自研 TCP/IP 协议栈:从 RFC 793 状态机到 CUBIC 拥塞控制,全部手写
  • DPDK 高性能数据面:L2/L3 线速转发 + NAT + ACL + QoS
  • 双产品架构:同一份源码,编译出 Cloud Edge(T140 容器网关)和 Home CPE(J3455 家庭网关)
  • 容器网络支持:memif 零拷贝接入容器
  • FRR 动态路由集成:BGP/OSPF 路由自动同步到数据面
  • 完整管理面:CLI + REST API + Web Dashboard

1.2 产品矩阵

⚠ 2026-09-07 更新: Cloud Edge 硬件(T140)已售,该产品线暂缓;Home CPE 为唯一在研产品,硬件已升级(X540-AT2 10G × 2,8GB RAM)。模块拆解与路线对齐见 dpdk_study/docs/uNOS_PRODUCT_FUSION_ROADMAP.md(附录含 M0-M7 模块清单)。

uNOS Cloud Edge ⏸ uNOS Home CPE ▶
目标硬件 Dell T140(已售,暂缓) J3455B-ITX(Celeron, 8GB)
网卡 X540-T2 10G × 2 + E1G42ET 1G × 4 X540-T2 10G × 2
容器支持 ✅ memif + CNI ❌
BGP ✅ full table ❌
OSPF ✅ ✅ stub
HA ✅ VRRP ❌
应用层服务 ✅ HTTP/DNS/LB ❌
编译宏 PRODUCT_CLOUD_EDGE PRODUCT_HOME_CPE

1.3 设计原则

  1. 零拷贝优先:数据面路径上不产生任何 mbuf 拷贝
  2. 编译时剪裁:产品差异通过 #ifdef 在编译时决定,不在运行时判断
  3. 固定 Pipeline:转发路径不动态改变模块注册顺序,保证分支预测友好
  4. Cache Line 对齐:所有热路径数据结构按 64B 对齐,避免 false sharing
  5. 错误即崩溃:数据面关键路径不检查返回值(如 mbuf 分配),由预分配 mempool 保证;控制面优雅处理所有错误

二、架构设计

2.1 系统分层

┌─────────────────────────────────────────────────────────┐
│                    Management Plane                      │
│  CLI (vtysh) │ REST API │ Web Dashboard │ Prometheus    │
│                      │                                   │
│                 Redis DB (配置存储 + Pub/Sub)             │
└──────────────────────────┬──────────────────────────────┘
                           │ Unix socket / TCP
┌──────────────────────────┼──────────────────────────────┐
│                   Control Plane (lcore 0)                │
│                                                          │
│  Config Manager │ Route Manager │ Interface Manager      │
│  ARP/NDP        │ DHCP          │ Health Monitor         │
│  CNI Manager ◄── Cloud Edge only                        │
│                      │                                   │
│                 IPC (rte_ring)                            │
└──────────────────────────┼──────────────────────────────┘
                           │
┌──────────────────────────┼──────────────────────────────┐
│                    Data Plane (lcore 1)                   │
│                                                          │
│  ┌────────────────────────────────────────────────────┐ │
│  │             Forwarding Pipeline                     │ │
│  │  RX → Parser → Classifier → ACL → LPM → NAT → QoS │ │
│  │                                        │            │ │
│  │                                        ▼            │ │
│  │                              TX (physical / memif)  │ │
│  └────────────────────────────────────────────────────┘ │
│                                                          │
│  ┌────────────────────────────────────────────────────┐ │
│  │          自研 TCP/IP Stack (lcore 2)                │ │
│  │  TCP State Machine → Congestion → Retransmit → TX  │ │
│  └────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────┘

2.2 lcore 分配

Cloud Edge (T140, E-2236 6C/12T)

Core 0 ─→ lcore 0: Control Plane (main thread, 不参与数据面轮询)
  ├── Config Manager (YAML/Redis)
  ├── Route Manager (FRR FPM Netlink)
  ├── Interface Manager (UP/DOWN/MTU/Stats)
  ├── ARP/NDP Manager
  ├── CNI Manager (memif create/delete, IPAM)
  ├── Health Monitor (rte_timer 5s)
  ├── CLI + REST Server (基于自研 TCP 栈)
  └── Flow Table GC Worker (全局老化)

Core 1-2 ─→ lcore 1-2: Data Plane Workers (RSS 对称,每个核独立)
  每个 worker 的数据面:
    ┌─────────────────────────────────────────────┐
    │    RX (本 worker 独占的 RSS queues)          │
    │              │                               │
    │    ┌─────────┴──────────┐                   │
    │    │   Graph Pipeline   │  ← 独立转发实例    │
    │    │   (per-core graph) │                   │
    │    └─────────┬──────────┘                   │
    │              │                               │
    │    TX (本 worker 独占的 TX queues)           │
    │                                              │
    │  Per-core 状态 (无需锁):                      │
    │    - Flow Cache (rte_hash)                   │
    │    - Connection Track Table                  │
    │    - TCP Stack (集成在 Graph 中)              │
    │    - ARP Cache (只读,控制面写入)             │
    │    - LPM Route Table (只读,控制面写入)       │
    └─────────────────────────────────────────────┘

  RSS 保证同一五元组永远落在同一个 worker 上,
  因此 per-core 的状态表天然无锁。

Core 3 ─→ lcore 3: TCP App Server (从数据面 worker 中接应用层流量)
  └── HTTP Server / DNS Server / TCP Proxy
      通过 per-core ring 从数据面 TCP 栈接收应用层请求

Core 4 ─→ (非 DPDK) FRR 进程 (bgpd + ospfd)
  └── taskset 绑定,通过 Netlink FPM 推送路由到控制面

Core 5 ─→ (非 DPDK) 可观测 + Redis + 系统
  └── Prometheus exporter / sFlow / 日志 / Redis Server

HT 线程 (6 个) ─→ 内核中断 + 辅助任务
  └── isolcpus 隔离数据面核心 (core 1-3)

Home CPE (J3455, 4 cores)

Core 0 ─→ lcore 0: Control Plane (main thread)
  ├── Config Manager / Route Manager (ospfd FPM)
  ├── ARP Manager / DHCP Client / Health Monitor
  └── CLI (Unix socket)

Core 1 ─→ lcore 1: Data Plane Worker (纯转发单核;叠加 NAT/ACL/QoS 后按实测决定是否用 core2)
  └── 物理口 (X540-T2 10G × 2, 绑 vfio-pci) RSS queue 0
      Graph Pipeline: L2/L3/NAT/ACL/QoS
      Per-core Flow Cache + Connection Track Table
      (不包含 TCP 栈 — Home CPE 不跑应用层服务)

Core 2 ─→ 数据面备用核 (状态化/流控压不住时启用) ;否则 (非 DPDK) FRR ospfd

Core 3 ─→ 空闲 / Prometheus 轻量 exporter

2026-10-03 勘误:① 原来写 "单核,1G 线速足够" 只在纯转发成立——64B 小包打满即 1 Gbps 线速(=1.488 Mpps,算术恒等),叠加 conntrack/NAT/ACL/QoS 后单核无余量,故 core2 预留为第二数据面核;② 物理口笔误 82576 × 2 更正为实机 X540-T2 10G × 2;③ 管理口用板载 RTL8111(无上游 DPDK PMD,固定归 Linux)。

2.3 模块依赖图

                    ┌─────────────────┐
                    │     main.c      │
                    │  EAL init       │
                    │  lcore launch   │
                    └────────┬────────┘
                             │
            ┌────────────────┼────────────────┐
            ▼                ▼                ▼
      ┌──────────┐   ┌──────────┐    ┌──────────────┐
      │pipeline.c│   │control.c │    │tcp_main.c    │  ← Cloud Edge only
      │(lcore 1) │   │(lcore 0) │    │(lcore 2)     │
      └────┬─────┘   └────┬─────┘    └──────┬───────┘
           │              │                  │
    ┌──────┼──────┐  ┌────┼──────┐     ┌─────┼──────┐
    ▼      ▼      ▼  ▼    ▼      ▼     ▼     ▼      ▼
 parser  l3fwd  nat arp dhcp  iface ipv4  tcp_state http
 l2fwd   acl    qos ndp frr   db    icmp  tcp_cong  dns
                                  (Redis) frag  tcp_retx  proxy
                                        │     tcp_flow
                                  ┌─────┘     tcp_socket
                                  ▼
                            Redis Server
                           (external process)

三、双产品设计

3.1 编译时产品选择

# meson_options.txt
option('product', type: 'combo',
       choices: ['cloud_edge', 'home_cpe'],
       value: 'cloud_edge',
       description: 'Target product: cloud_edge (T140) or home_cpe (J3455)')

# src/meson.build
product = get_option('product')

product_flags = []
if product == 'cloud_edge'
    product_flags += '-DPRODUCT_CLOUD_EDGE'
    sources += files(
        'cni/memif.c',
        'cni/cni_api.c',
        'cni/ipam.c',
        'app/http_server.c',
        'app/dns_server.c',
        'app/tcp_proxy.c',
        'stcp/tcp_socket.c',
        'stcp/tcp_txrx.c',
        'ha/vrrp.c',
    )
elif product == 'home_cpe'
    product_flags += '-DPRODUCT_HOME_CPE'
    # Home CPE excludes: CNI, HTTP Server, DNS, TCP Proxy,
    #                   VRRP, and full TCP socket layer
endif

3.2 模块编译矩阵

模块                     Cloud Edge    Home CPE    说明
────                     ──────────    ────────    ──
src/core/pipeline.c          ✅           ✅        共享
src/core/ipc.c               ✅           ✅        共享
src/core/main.c              ✅           ✅        共享 (lcore 分配不同)
src/dp/parser.c              ✅           ✅        共享
src/dp/l2fwd.c               ✅           ✅        共享
src/dp/l3fwd.c               ✅           ✅        共享
src/dp/nat.c                 ✅           ✅        共享
src/dp/acl.c                 ✅           ✅        共享
src/dp/qos.c                 ✅           ✅        共享
src/cp/arp.c                 ✅           ✅        共享
src/cp/iface.c               ✅           ✅        共享
src/cp/dhcp.c                ❌           ✅        Home CPE only
src/cp/frr_sync.c            ✅           ✅        共享 (协议不同)
src/cp/db.c                  ✅           ✅        共享
src/stcp/ipv4.c              ✅           ❌        Cloud Edge only
src/stcp/tcp_state.c         ✅           ❌        Cloud Edge only
src/stcp/tcp_cong.c          ✅           ❌        Cloud Edge only
src/stcp/tcp_retx.c          ✅           ❌        Cloud Edge only
src/stcp/tcp_flow.c          ✅           ❌        Cloud Edge only
src/stcp/tcp_socket.c        ✅           ❌        Cloud Edge only
src/cni/memif.c              ✅           ❌        Cloud Edge only
src/cni/cni_api.c            ✅           ❌        Cloud Edge only
src/cni/ipam.c               ✅           ❌        Cloud Edge only
src/app/http_server.c        ✅           ❌        Cloud Edge only
src/app/dns_server.c         ✅           ❌        Cloud Edge only
src/app/tcp_proxy.c          ✅           ❌        Cloud Edge only
src/mgmt/cli.c               ✅           ✅        共享 (命令集不同)
src/mgmt/rest.c              ✅           ❌        Cloud Edge only
src/ha/vrrp.c                ✅           ❌        Cloud Edge only
src/telemetry/prometheus.c   ✅           ✅        共享 (指标集不同)

3.3 关键差异点

// src/core/main.c — lcore 分配

void unos_launch_lcores(void) {
#ifdef PRODUCT_CLOUD_EDGE
    // T140: 6+ cores, full allocation
    rte_eal_remote_launch(dataplane_forward_loop, NULL, 1);  // lcore 1
    rte_eal_remote_launch(tcp_stack_loop, NULL, 2);           // lcore 2
    rte_eal_remote_launch(memif_rx_loop, NULL, 3);            // lcore 3 (optional)
    // lcore 0: control plane (main thread)
    // lcore 4-5: FRR + observability (external processes pinned)
#else
    // J3455: 4 cores, minimal allocation
    rte_eal_remote_launch(dataplane_forward_loop, NULL, 1);  // lcore 1
    // lcore 0: control plane (main thread)
    // lcore 2: FRR ospfd (external process pinned)
    // lcore 3: idle
#endif
}

四、数据面详细设计(生产级:RSS 多核 + Graph Pipeline)

4.0 架构决策

为什么不用固定 Pipeline(v1.0 设计):
  ❌ 所有包走完全部阶段,即使不需要
  ❌ 单核转发,无法线性扩展
  ❌ TCP 栈在独立 lcore,跨核 IPC 引入延迟 + cache miss

为什么用 RSS + Graph Pipeline(v2.0 生产级):
  ✅ RSS 把流量按五元组分散到多核,每个核独立运行,无锁、
     线性扩展,加核就加吞吐
  ✅ Graph DAG 按包类型走不同路径,ARP 包跳过 NAT,
     本地包直接进 TCP 栈,不经过多余阶段
  ✅ TCP 栈作为 Graph 节点内联在数据面,本机包不跨核
  ✅ Per-core Flow Cache 跳过 LPM 查找(首包查路由,
     后续包命中 cache 直接转发)
  ✅ 对标 VPP (FD.io) 的 Graph Node 架构,业界验证过

边界说明(2026-10-03):上面 "❌ 单核转发" 针对的是 Cloud Edge / 多 Gbps 目标;Home CPE 的 1 Gbps 定位下单核可作为基线(见 §2.2 的 lcore 划分),但这是性能余量换成本的取舍,不是"单核更好"——所以 Home CPE 仍保留 core2 作为第二数据面核的退路。

4.1 Graph Pipeline 核心数据结构

// src/dp/graph.h

#define MAX_NEXT_NODES  8
#define NODE_NAME_LEN   32

// 前向声明
struct graph_node;

// Graph Node 处理函数签名:
//   输入: mbuf 数组 + 包计数
//   输出: 每个包走哪个 next_nodes[idx] 的索引数组
//   返回: 实际处理的包数
typedef uint16_t (*graph_node_process_fn_t)(
    struct rte_mbuf **mbufs,
    uint16_t          nb_pkts,
    uint8_t           *next_indices  // [nb_pkts]: 每个包的下一跳
);

struct graph_node {
    char                    name[NODE_NAME_LEN];
    graph_node_process_fn_t process;
    uint16_t                next_count;
    struct graph_node       *next_nodes[MAX_NEXT_NODES];
    char                    *next_labels[MAX_NEXT_NODES]; // 分支标签

    // 统计 (per-lcore, cacheline 对齐)
    uint64_t                pkts_processed;
    uint64_t                cycles_spent;
    uint64_t                pkts_dropped;
} __rte_cache_aligned;

// Graph 路由辅助宏: 让 process_fn 返回的 next_indices 更有语义
enum {
    NEXT_DROP = 0,          // 丢包
    NEXT_TX   = 1,          // 直接发送
    NEXT_L2   = 2,          // L2 转发
    NEXT_L3   = 3,          // L3 转发
    NEXT_NAT  = 4,          // NAT 转换
    NEXT_QOS  = 5,          // QoS 调度
    NEXT_TCP  = 6,          // 进 TCP 栈
    NEXT_ARP  = 7,          // ARP 处理
};

// 每个 worker 拥有独立的 Graph 实例
struct graph_worker {
    struct rte_mbuf  *rx_bufs[BURST_SIZE];     // 收包暂存
    struct rte_mbuf  *tx_bufs[RTE_MAX_ETHPORTS][BURST_SIZE];
    uint16_t          tx_counts[RTE_MAX_ETHPORTS];

    struct rte_mbuf  *tcp_rx_bufs[BURST_SIZE]; // TCP 栈的收包队列

    // Per-core 状态表(无锁——RSS 保证流的亲和性)
    struct flow_cache        *flow_cache;       // 流缓存
    struct conntrack_table   *ct_table;         // 连接跟踪
    struct tcp_stack         *tcp_stack;        // TCP 协议栈实例

    // 全局只读状态(控制面写入,数据面只读)
    struct rte_lpm           *lpm_ipv4;         // 路由表 (RSS workers 共享)
    struct arp_cache         *arp_cache;        // ARP 表 (RSS workers 共享)
    struct acl_engine        *acl;              // ACL (RSS workers 共享)
} __rte_cache_aligned;

4.2 Graph 拓扑(完整转发图)

                       ┌─────────┐
                       │   RX    │
                       │ (NIC RX │
                       │  queues)│
                       └────┬────┘
                            │
                       ┌────┴────┐
                       │ Parser  │ ← 所有包的入口
                       │ L2-L4   │
                       └────┬────┘
                            │
                   ┌───────┼───────────┐
                   ▼       ▼           ▼
              dst_is_me  ARP pkt    L2 (MAC查表)
                   │       │           │
                   ▼       ▼           ▼
              ┌────────┐ ┌────────┐ ┌─────────┐
              │Classify│ │ARP     │ │L2 Fwd   │
              │本机流  │ │Handler │ │MAC学习  │
              └───┬────┘ └───┬────┘ └────┬────┘
                  │          │           │
        ┌────┬────┤          ▼           ▼
        ▼    ▼    ▼      ┌────────┐  ┌─────────┐
      TCP  DNS  HTTP    │  TX    │  │   TX    │
     Stack Server Server └────────┘  └─────────┘
        │
        ▼ (TCP栈处理后)
   ┌──────────┐
   │  TX      │
   │ (本机发包)│
   └──────────┘

                   ┌─────────────────┐
                   │   dst != me     │ ← 转发路径
                   │   (非本机IP)     │
                   └────────┬────────┘
                            │
                    ┌───────┴───────┐
                    ▼               ▼
              ┌──────────┐   ┌──────────┐
              │Flow Cache│   │   LPM    │  ← 首包走 LPM,后续命中 cache
              │(per-core)│   │(路由查找)│
              └────┬─────┘   └────┬─────┘
                   │              │
                   │    miss      │
                   └──────┬───────┘
                          ▼
                    ┌──────────┐
                    │   ACL    │ ← 安全策略
                    └────┬─────┘
                         │
              ┌──────────┼──────────┐
              ▼          ▼          ▼
         ┌────────┐ ┌──────────┐ ┌──────┐
         │  NAT   │ │  No NAT  │ │ DENY │
         │ SNAT/  │ │(纯转发)  │ │(丢弃)│
         │ DNAT   │ └────┬─────┘ └──┬───┘
         └───┬────┘      │         │
             │           │      rte_pktmbuf_free()
             └─────┬─────┘
                   ▼
             ┌──────────┐
             │   ARP    │ ← 查下一跳 MAC
             │  Lookup  │
             └────┬─────┘
                  │
                  ▼
             ┌──────────┐
             │   QOS    │ ← 入队 + 调度
             │8队列调度  │
             └────┬─────┘
                  │
                  ▼
             ┌──────────┐
             │    TX    │
             └──────────┘

4.3 数据面核心循环(每个 RSS worker 独立运行)

// src/dp/worker.c — 每个 lcore worker 的主循环

static __rte_always_inline int
dataplane_worker_loop(void *arg) {
    struct graph_worker *gw = (struct graph_worker *)arg;
    struct rte_mbuf *pkts[BURST_SIZE];
    uint8_t next_indices[BURST_SIZE];
    uint16_t nb_pkts;

    while (likely(!force_quit)) {
        // ==== STEP 1: RX — 轮询本 worker 独占的 RSS queues ====
        uint16_t total_rx = 0;
        for (int port = 0; port < gw->port_count; port++) {
            nb_pkts = rte_eth_rx_burst(port, gw->queue_id, pkts + total_rx,
                                       BURST_SIZE - total_rx);
            if (nb_pkts == 0) continue;

            // 预取第一层 Graph Node(parser_node)的数据
            for (int i = 0; i < nb_pkts; i++)
                rte_prefetch0(rte_pktmbuf_mtod(pkts[total_rx + i], void *));
            total_rx += nb_pkts;
        }
        if (unlikely(total_rx == 0)) continue;

        // ==== STEP 2: Graph Traversal — 每个包沿 DAG 路径前进 ====
        // 用 SIMD 批次追踪每个包当前所在的 node
        struct graph_node *current_nodes[BURST_SIZE]; // 每个包当前在哪个 node
        uint16_t active_mask = 0;                     // bitmask: 还有包没走完

        // 所有包起点: Parser Node
        struct graph_node *first = &gw->graph->parser_node;
        for (int i = 0; i < total_rx; i++) {
            current_nodes[i] = first;
            active_mask |= (1 << i);
        }

        while (active_mask) {
            // 按当前 node 分组 → 同一 node 的包 batch 处理
            for (int batch_start = 0; batch_start < total_rx; ) {
                if (!(active_mask & (1 << batch_start))) {
                    batch_start++;
                    continue;
                }

                // 收集所有在同一个 node 的包
                struct graph_node *batch_node = current_nodes[batch_start];
                uint16_t batch_nb = 0;
                struct rte_mbuf *batch_pkts[BURST_SIZE];

                for (int i = batch_start; i < total_rx; i++) {
                    if (current_nodes[i] == batch_node) {
                        batch_pkts[batch_nb++] = pkts[i];
                        active_mask &= ~(1 << i);  // 从活跃集移除
                    }
                }

                // 对该 node 的包 batch 处理
                if (batch_node->next_count == 0) {
                    // 终端节点 (DROP / TX): 直接释放或发送
                    if (batch_node == &gw->graph->tx_node)
                        gw_tx_burst(gw, batch_pkts, batch_nb);
                    else  // drop_node
                        for (int i = 0; i < batch_nb; i++)
                            rte_pktmbuf_free(batch_pkts[i]);
                } else {
                    // 内部节点: 调用 process_fn
                    uint16_t processed = batch_node->process(
                        batch_pkts, batch_nb, next_indices);

                    // 更新每个包的下一跳
                    for (int i = 0; i < processed; i++) {
                        uint8_t nidx = next_indices[i];
                        if (nidx < batch_node->next_count) {
                            current_nodes[batch_start + i] =
                                batch_node->next_nodes[nidx];
                            active_mask |= (1 << (batch_start + i));
                        } else {
                            // 错误: 丢包
                            rte_pktmbuf_free(batch_pkts[i]);
                        }
                    }
                    // 处理失败的包
                    for (int i = processed; i < batch_nb; i++)
                        rte_pktmbuf_free(batch_pkts[i]);
                }

                // 统计
                batch_node->pkts_processed += batch_nb;
                batch_start = batch_start + batch_nb;  // 跳过已分组的包
            }

            // 重新调整 active 包的索引 (释放掉的包对应的位置清空)
            total_rx = compact_active_packets(pkts, current_nodes, total_rx,
                                              &active_mask);
        }

        // ==== STEP 3: TX — 批量发送所有出端口队列 ====
        for (int port = 0; port < gw->port_count; port++) {
            if (gw->tx_counts[port] > 0) {
                uint16_t sent = rte_eth_tx_burst(port, gw->queue_id,
                                                  gw->tx_bufs[port],
                                                  gw->tx_counts[port]);
                if (unlikely(sent < gw->tx_counts[port])) {
                    for (int i = sent; i < gw->tx_counts[port]; i++)
                        rte_pktmbuf_free(gw->tx_bufs[port][i]);
                }
                gw->tx_counts[port] = 0;
            }
        }
    }
    return 0;
}

4.4 Per-Core Flow Cache(关键优化)

每个 RSS worker 拥有独立的 Flow Cache,避免对 Established 流的重复 LPM 查找:

// src/dp/flow_cache.h

// Flow Cache 条目
struct flow_cache_entry {
    uint32_t              src_ip;
    uint32_t              dst_ip;
    uint16_t              src_port;
    uint16_t              dst_port;
    uint8_t               protocol;
    uint8_t               action;           // ACTION_FWD / ACTION_NAT / ACTION_DROP
    uint8_t               out_port;         // 出端口
    struct rte_ether_addr dst_mac;          // 下一跳 MAC (不变时缓存)
    uint32_t              nat_ip;           // NAT 后的地址 (不变时缓存)
    uint16_t              nat_port;         // NAT 后的端口
    uint64_t              last_seen;
} __rte_cache_aligned;

// Flow Cache 查找 — 在 Parser 后直接调用
// 命中 → 跳过 LPM + ACL + NAT,直接填 MAC + 发往 QoS → TX
int flow_cache_lookup(struct flow_cache *fc, struct parser_result *pr,
                      struct flow_cache_entry *result) {
    uint32_t hash_key = pr->src_ip ^ pr->dst_ip ^
                        (pr->src_port << 16 | pr->dst_port) ^ pr->protocol;
    uint32_t bucket = hash_key & fc->mask;

    // 开放寻址 (cuckoo hash, 最多查 2 个 bucket)
    // 命中 → O(1), no locks
    // miss  → 走完整 LPM + ACL + NAT 路径,然后插入 cache
}

4.5 核心数据结构(不变部分,续用 v1.0 设计)

以下结构体保持 v1.0 设计,仅从固定 Pipeline 改为 Graph Node 使用:

Parser Result (见 v1.0 的 4.1.3)

// src/dp/parser.h — 保持不变,但增加 graph_next 字段
struct parser_result {
    // ... (同 v1.0 设计) ...

    uint8_t graph_next;  // 新增: Parser 根据包类型设置下一跳
    // PARSER_NEXT_L2   (ether_type == 0x0800 && dst_mac is not me)
    // PARSER_NEXT_L3   (ether_type == 0x0800 && dst_mac is me)
    // PARSER_NEXT_ARP  (ether_type == 0x0806)
    // PARSER_NEXT_DROP (unknown protocol)
};

L3 Forwarder (见 v1.0 的 4.1.4)

// 不变,但变为所有 RSS worker 共享的只读数据
// 控制面通过 IPC 更新 (RCU 模式: 控制面写入新副本,数据面原子切换指针)

NAT / Connection Track (见 v1.0 的 4.1.5)

// Per-core 的 Connection Track Table
// 关键变化: 每个 RSS worker 拥有独立的 ct_table,因为
// RSS 保证同一五元组永远落在同一个 worker,所以无需跨核锁
#endif

---

## 五、控制面详细设计

### 5.1 IPC 消息协议

```c
// src/core/ipc.h

#define IPC_MSG_MAX_PAYLOAD 1024
#define IPC_RING_SIZE       2048

enum ipc_msg_type {
    IPC_MSG_ROUTE_ADD = 1,
    IPC_MSG_ROUTE_DEL,
    IPC_MSG_INTERFACE_UP,
    IPC_MSG_INTERFACE_DOWN,
    IPC_MSG_ACL_ADD,
    IPC_MSG_ACL_DEL,
    IPC_MSG_NAT_CONFIG,
    IPC_MSG_QOS_CONFIG,
    IPC_MSG_STATS_REPORT,
    IPC_MSG_HEALTH_REPORT,
#ifdef PRODUCT_CLOUD_EDGE
    IPC_MSG_MEMIF_CREATE,
    IPC_MSG_MEMIF_DELETE,
    IPC_MSG_CNI_ADD,
    IPC_MSG_CNI_DEL,
#endif
};

struct ipc_msg {
    uint32_t              type;           // enum ipc_msg_type
    uint32_t              seq_num;        // 序列号 (用于配对 request/response)
    uint32_t              payload_len;    // payload 实际长度
    uint32_t              _pad;
    uint64_t              timestamp;      // rdtsc
    uint8_t               payload[IPC_MSG_MAX_PAYLOAD];
} __rte_cache_aligned;

// 控制面 → 数据面: cp_to_dp_ring (MPSC: 控制面单生产者, 数据面单消费者)
// 数据面 → 控制面: dp_to_cp_ring (SPMC: 数据面单生产者, 控制面单消费者)

5.2 控制面主循环

// src/cp/control.c

void control_plane_loop(void) {
    while (!force_quit) {
        // 1. 定时器处理 (ARP 老化, 统计收集, 健康检查)
        rte_timer_manage();

        // 2. 处理数据面上报 (统计 + 事件)
        struct ipc_msg msg;
        while (rte_ring_dequeue(dp_to_cp_ring, &msg) == 0) {
            switch (msg.type) {
            case IPC_MSG_STATS_REPORT:
                handle_stats_report(&msg);
                break;
            case IPC_MSG_HEALTH_REPORT:
                handle_health_report(&msg);
                break;
            }
        }

        // 3. 处理 Redis Pub/Sub 消息 (配置变更)
        redis_handle_pubsub();

        // 4. CLI 连接 (Unix socket accept)
        cli_accept_connections();

        // 5. FRR 路由同步 (Netlink socket poll, non-blocking)
        frr_poll_netlink();
    }
}

5.3 Redis DB Schema

CONFIG_DB (持久化):
  ROUTE|<vrf>|<prefix>       → {"nexthop":"10.0.0.1","iface":"wan0","metric":10}
  INTERFACE|<name>           → {"ip":"192.168.1.1/24","mtu":1500,"state":"up"}
  ACL|<rule_id>              → {"priority":100,"action":"permit","src":"10.0.0.0/8","dst":"0.0.0.0/0","proto":"tcp","dport":"80"}
  NAT|<rule_id>              → {"type":"snat","src":"10.0.0.0/8","nat_ip":"203.0.113.1"}
  QOS|<port>|<queue_id>      → {"scheduler":"sp","weight":0}
#ifdef PRODUCT_CLOUD_EDGE
  CNI|<container_id>         → {"ip":"10.244.1.5/24","memif_socket":"/tmp/unos-memif-abc123"}
#endif

STATE_DB (运行时, 不持久化):
  ARP|<ip>                   → {"mac":"00:11:22:33:44:55","iface":"wan0","age":12}
  CONNTRACK|summary          → {"total":1234,"tcp":1000,"udp":200,"icmp":34}
  INTERFACE|<name>|status    → {"admin":"up","oper":"up","speed":10000}

STATS_DB (运行时):
  PORT|<name>|stats          → {"rx_bytes":1234567890,"tx_bytes":9876543210,"rx_pps":1234567}
#ifdef PRODUCT_CLOUD_EDGE
  CONTAINER|<id>|stats       → {"rx_bytes":1234,"tx_bytes":5678,"memif_errors":0}
#endif

六、自研 TCP/IP 协议栈详细设计

6.0 架构决策: TCP 栈在 Graph 中内联

v1.0 (弃用):  TCP 栈在独立 lcore → 跨核 IPC → 额外拷贝 + cache miss
v2.0 (生产级): TCP 栈作为 Graph Node → 同一 lcore 直接函数调用 → 零跨核开销

Graph 路径:
  物理口 RX → Parser → (dst_is_me?) → TCP Stack Node → App → TX
               │
               └── (dst_is_not_me) → L2/L3/ACL/NAT → TX

本机收包路径 (全程在同一个 worker lcore 内):
  rte_eth_rx_burst → Parser → graph_next=NEXT_TCP
  → tcp_stack_process_node()  ← 这就是 TCP 栈入口
     内部:
       1. IP 层处理 (分片重组, 校验)
       2. TCP 层处理 (状态机, 序列号, 重传)
       3. 应用层数据放入 recv buffer
       4. 待发送数据构造 mbuf → TX ring
  → 应用层 (通过 tcp_recv() 获取数据)

同一个 mbuf 全程不拷贝,不跨核,不经过 rte_ring IPC。

6.1 TCP Stack Graph Node

// src/stcp/tcp_node.c — TCP 栈作为 Graph 处理的实现

static uint16_t
tcp_stack_process_node(struct rte_mbuf **mbufs, uint16_t nb_pkts,
                       uint8_t *next_indices)
{
    for (int i = 0; i < nb_pkts; i++) {
        struct rte_mbuf *m = mbufs[i];
        struct parser_result pr;
        parser_process(m, &pr);  // 快速解析

        // === IP 层处理 ===
        if (pr.ip_version == 4) {
            if (unlikely(!ipv4_validate_checksum(m)))
                goto drop;
            // 分片重组
            if (unlikely(ipv4_is_fragment(m))) {
                m = ipv4_reassemble(m);
                if (m == NULL) continue;  // 还没收全
            }
        }

        // === TCP 层处理 ===
        struct tcp_control_block *tcb;
        tcb = tcb_lookup(pr.src_ip, pr.dst_ip, pr.src_port, pr.dst_port);

        if (unlikely(tcb == NULL && (pr.tcp_flags & TCP_FLAG_SYN))) {
            // 新连接请求 → 查 LISTEN socket → 创建 TCB
            tcb = tcp_listen_accept(&pr);
        }

        if (unlikely(tcb == NULL))
            goto drop;

        // HTTP Server / DNS 跑在 STCP Socket API 上
        // 当 recv buffer 有数据 → 唤醒应用层 epoll → HTTP handler 调用 tcp_send() 回复

        // 状态机转换
        tcp_state_process(tcb, &pr, m);

        // 如果 tcp_state_process 内部调用了 tcp_send(),TX mbuf 已经入队
        // 如果是纯 ACK,不需要回复,直接释放

        // 本机接收路径: mbuf 交给 recv buffer
        // 本机发送路径: TCP 栈自己构造的 mbuf 在 TX ring 里,
        //              在 graph traversal 的 TX 阶段发出
        continue;

drop:
        next_indices[i] = NEXT_DROP;
    }
    return nb_pkts;
}

// 这个函数注册为 Graph Node:
REGISTER_GRAPH_NODE(tcp_stack, tcp_stack_process_node, {
    { NEXT_TCP,  "tcp_stack"    },  // 回环 (重传/ACK)
    { NEXT_TX,   "tx"           },  // 出队发送
    { NEXT_DROP, "drop"         },  // 错误丢包
});

6.2 TCB (TCP Control Block) — 不变

(参见 v1.0 设计,TCB 结构不变。区别仅在于 TCB 挂载在 per-worker 的 tcp_stack 实例中,而非全局哈希表)

6.3 TCP 状态机 — 不变

(11 状态转换表不变,参见 v1.0 6.2 节)

6.4 数据面 TCP 的收发包路径

收到 SYN (dst_ip == 本机 IP, dst_port == 80):
─────────────────────────────────────────────
物理口 RX:
  → Parser: ether_type=0x0800, protocol=TCP, dst_port=80, SYN=1
  → graph_next = NEXT_TCP

TCP Stack Node:
  → tcb_lookup() → NULL (新连接)
  → tcp_listen_accept() → 创建 TCB (状态=SYN_RCVD)
  → tcp_send_syn_ack(tcb) → 构造 SYN+ACK mbuf
  → mbuf 入 TX ring
  → 数据面 TX 阶段发出

收到 ACK (of SYN+ACK):
  → tcb_lookup() → 找到 TCB
  → tcp_state_process() → SYN_RCVD → ESTABLISHED
  → 无应用数据,不唤醒

收到 HTTP GET (ESTABLISHED 状态下):
  → tcp_state_process() → 验证 seq, 更新 rcv_nxt, 回复 ACK
  → 应用数据放入 tcb->rcv_buf
  → 唤醒 HTTP handler (通过 eventfd)
  → HTTP handler 调用 tcp_send(tcb->sockfd, response, len)
    → TCP 栈构造 DATA mbuf → TX ring → 发出

关闭连接:
  → 客户端发 FIN → tcp_state: ESTABLISHED → CLOSE_WAIT
  → HTTP handler 收到 EOF → close(sockfd)
  → tcp_send_fin(tcb) → tcp_state: CLOSE_WAIT → LAST_ACK → 发 FIN
  → 收到 ACK → CLOSED → 删除 TCB
// src/stcp/tcb.h

#define TCB_HASH_BUCKETS   65536
#define TCB_MAX_CONNECTIONS 100000  // Cloud Edge
#define TCP_MSS_DEFAULT    1460
#define TCP_RTO_MIN_MS     200
#define TCP_RTO_MAX_MS     120000
#define TCP_2MSL_MS        120000  // TIME_WAIT duration

enum tcp_state {
    TCP_CLOSED = 0,
    TCP_LISTEN,
    TCP_SYN_SENT,
    TCP_SYN_RCVD,
    TCP_ESTABLISHED,
    TCP_FIN_WAIT_1,
    TCP_FIN_WAIT_2,
    TCP_CLOSE_WAIT,
    TCP_CLOSING,
    TCP_LAST_ACK,
    TCP_TIME_WAIT,
};

struct tcp_control_block {
    // === 四元组 ===
    uint32_t              src_ip;
    uint32_t              dst_ip;
    uint16_t              src_port;
    uint16_t              dst_port;

    // === 状态 ===
    enum tcp_state        state;

    // === 发送序列号 ===
    uint32_t              iss;            // Initial Send Sequence
    uint32_t              snd_una;        // 最老未确认的序列号
    uint32_t              snd_nxt;        // 下一个要发送的序列号
    uint32_t              snd_wnd;        // 发送窗口
    uint32_t              snd_wl1;        // 上次窗口更新的 seq
    uint32_t              snd_wl2;        // 上次窗口更新的 ack

    // === 接收序列号 ===
    uint32_t              irs;            // Initial Receive Sequence
    uint32_t              rcv_nxt;        // 期望收到的下一个序列号
    uint32_t              rcv_wnd;        // 接收窗口 (通告给对方)
    uint32_t              rcv_adv;        // 上次通告的窗口右边界

    // === 拥塞控制 ===
    uint32_t              cwnd;           // 拥塞窗口 (MSS 为单位)
    uint32_t              ssthresh;       // 慢启动阈值
    uint32_t              cwnd_cnt;       // cwnd 增长计数器

    // === RTT / RTO ===
    uint32_t              srtt_us;        // Smoothed RTT (微秒)
    uint32_t              rttvar_us;      // RTT 方差 (微秒)
    uint32_t              rto_us;         // 当前 RTO (微秒)
    uint64_t              rto_deadline;   // rte_rdtsc 到期时间
    uint8_t               backoff;        // 指数退避次数 (0-6)
    uint64_t              last_rtt_ts;    // 上次 RTT 测量时间

    // === 重传 ===
    uint32_t              dup_ack_count;  // 重复 ACK 计数 (快速重传)
    uint32_t              retrans_count;  // 连续重传次数
    uint64_t              last_data_ts;   // 最后发送数据的时间

    // === Options ===
    uint16_t              mss;            // 协商的 MSS
    uint8_t               wscale_shift;   // Window scale shift
    uint8_t               sack_permitted; // 对端支持 SACK

    // === 发送缓冲区 (环形) ===
    uint8_t               *snd_buf;
    uint32_t              snd_buf_size;
    uint32_t              snd_buf_head;   // 写入指针
    uint32_t              snd_buf_tail;   // 确认指针

    // === 接收缓冲区 (环形) ===
    uint8_t               *rcv_buf;
    uint32_t              rcv_buf_size;
    uint32_t              rcv_buf_head;   // 写入指针
    uint32_t              rcv_buf_tail;   // 读取指针

    // === Socket 层 ===
    int                   sockfd;
    uint8_t               nonblocking;
    void                  *user_data;     // 应用层上下文

    // === 链表 ===
    struct tcp_control_block *next;       // 哈希冲突链表
    struct tcp_control_block *prev;
    uint64_t              tcb_id;         // 唯一 ID

} __rte_cache_aligned;

6.2 TCP 状态机实现

// src/stcp/tcp_state.c

// 事件类型
enum tcp_event {
    TCP_EV_SYN,
    TCP_EV_SYN_ACK,
    TCP_EV_ACK,
    TCP_EV_FIN,
    TCP_EV_RST,
    TCP_EV_TIMEOUT,
    TCP_EV_CLOSE,        // 应用层调用 close()
    TCP_EV_SEND,         // 应用层调用 send()
};

// 每个状态的事件处理表
typedef int (*tcp_state_handler_t)(struct tcp_control_block *tcb,
                                    struct tcp_event event,
                                    void *data);

// 状态转换表: [state][event] → new_state + handler
static const struct {
    enum tcp_state new_state;
    tcp_state_handler_t handler;
} tcp_transition[11][8] = {
    // TCP_CLOSED
    [TCP_CLOSED] = {
        [TCP_EV_SYN]    = { TCP_SYN_RCVD,   tcp_handle_listen_syn },
        [TCP_EV_SEND]   = { TCP_SYN_SENT,   tcp_handle_active_open },
    },
    // TCP_LISTEN
    [TCP_LISTEN] = {
        [TCP_EV_SYN]    = { TCP_SYN_RCVD,   tcp_handle_listen_syn },
        [TCP_EV_RST]    = { TCP_LISTEN,     tcp_handle_listen_rst },
    },
    // TCP_SYN_SENT
    [TCP_SYN_SENT] = {
        [TCP_EV_SYN_ACK]= { TCP_ESTABLISHED, tcp_handle_syn_sent_syn_ack },
        [TCP_EV_SYN]    = { TCP_SYN_RCVD,   tcp_handle_simultaneous_open },
        [TCP_EV_RST]    = { TCP_CLOSED,     tcp_handle_rst },
        [TCP_EV_TIMEOUT]= { TCP_CLOSED,     tcp_handle_timeout },
    },
    // TCP_SYN_RCVD
    [TCP_SYN_RCVD] = {
        [TCP_EV_ACK]    = { TCP_ESTABLISHED, tcp_handle_syn_rcvd_ack },
        [TCP_EV_RST]    = { TCP_LISTEN,     tcp_handle_syn_rcvd_rst },
        [TCP_EV_TIMEOUT]= { TCP_CLOSED,     tcp_handle_timeout },
    },
    // TCP_ESTABLISHED
    [TCP_ESTABLISHED] = {
        [TCP_EV_FIN]    = { TCP_CLOSE_WAIT, tcp_handle_established_fin },
        [TCP_EV_CLOSE]  = { TCP_FIN_WAIT_1, tcp_handle_close },
        [TCP_EV_RST]    = { TCP_CLOSED,     tcp_handle_rst },
    },
    // TCP_FIN_WAIT_1
    [TCP_FIN_WAIT_1] = {
        [TCP_EV_ACK]    = { TCP_FIN_WAIT_2, tcp_handle_fin_wait1_ack },
        [TCP_EV_FIN]    = { TCP_TIME_WAIT,  tcp_handle_fin_wait1_fin },
        [TCP_EV_FIN|TCP_EV_ACK] = { TCP_TIME_WAIT, tcp_handle_simultaneous_close },
        [TCP_EV_RST]    = { TCP_CLOSED,     tcp_handle_rst },
    },
    // TCP_FIN_WAIT_2
    [TCP_FIN_WAIT_2] = {
        [TCP_EV_FIN]    = { TCP_TIME_WAIT,  tcp_handle_fin_wait2_fin },
        [TCP_EV_RST]    = { TCP_CLOSED,     tcp_handle_rst },
    },
    // TCP_CLOSE_WAIT
    [TCP_CLOSE_WAIT] = {
        [TCP_EV_CLOSE]  = { TCP_LAST_ACK,   tcp_handle_close_wait_close },
        [TCP_EV_RST]    = { TCP_CLOSED,     tcp_handle_rst },
    },
    // TCP_CLOSING
    [TCP_CLOSING] = {
        [TCP_EV_ACK]    = { TCP_TIME_WAIT,  tcp_handle_closing_ack },
        [TCP_EV_RST]    = { TCP_CLOSED,     tcp_handle_rst },
    },
    // TCP_LAST_ACK
    [TCP_LAST_ACK] = {
        [TCP_EV_ACK]    = { TCP_CLOSED,     tcp_handle_last_ack_ack },
        [TCP_EV_RST]    = { TCP_CLOSED,     tcp_handle_rst },
    },
    // TCP_TIME_WAIT
    [TCP_TIME_WAIT] = {
        [TCP_EV_TIMEOUT]= { TCP_CLOSED,     tcp_handle_time_wait_timeout },
        [TCP_EV_RST]    = { TCP_CLOSED,     tcp_handle_rst },
    },
};

七、容器网络详细设计

7.1 memif 接口架构

┌───────────────────────────────────────────────────────┐
│                     Host (uNOS)                        │
│                                                        │
│  ┌──────────────────┐    ┌──────────────────┐         │
│  │   memif master   │    │   memif master   │         │
│  │   socket: /tmp/  │    │   socket: /tmp/  │         │
│  │   unos-memif-1   │    │   unos-memif-2   │         │
│  └────────┬─────────┘    └────────┬─────────┘         │
│           │ shared memory         │ shared memory      │
│           │ (hugepage)            │ (hugepage)         │
│  ┌────────┴───────────────────────┴─────────┐         │
│  │              uNOS Data Plane              │         │
│  │  memif1 → Pipeline → memif2              │         │
│  │  (container-to-container: zero copy)      │         │
│  └───────────────────────────────────────────┘         │
└───────────────────────────────────────────────────────┘
         │                              │
    ┌────┴────┐                    ┌────┴────┐
    │Container│                    │Container│
    │  A      │                    │  B      │
    │ (nginx) │                    │ (redis) │
    │         │                    │         │
    │ memif   │                    │ memif   │
    │ slave   │                    │ slave   │
    └─────────┘                    └─────────┘

7.2 memif 接口管理

// src/cni/memif.h

#define MAX_MEMIF_SOCKETS  128
#define MEMIF_SOCKET_DIR   "/var/run/unos/memif/"

struct memif_port {
    int                   id;               // memif interface ID
    char                  socket_path[256]; // /var/run/unos/memif/container-xxx
    uint8_t               role;             // MEMIF_ROLE_MASTER (uNOS) / MEMIF_ROLE_SLAVE (container)
    struct rte_mempool    *mempool;         // per-port mempool
    struct rte_ring       *rx_queue;        // RX ring (shared memory)
    struct rte_ring       *tx_queue;        // TX ring (shared memory)

    // === uNOS data plane reference ===
    uint8_t               unos_port_id;     // uNOS internal port ID
    struct rte_ether_addr mac;              // container side MAC
    uint32_t              ip;               // container IP (from IPAM)
    uint8_t               prefix_len;

    // === Stats ===
    uint64_t              rx_pkts;
    uint64_t              tx_pkts;
    uint64_t              rx_bytes;
    uint64_t              tx_bytes;
} __rte_cache_aligned;

struct memif_manager {
    struct memif_port     ports[MAX_MEMIF_SOCKETS];
    int                   port_count;
    struct rte_hash       *port_hash;       // socket_path → index
};

7.3 CNI Plugin 接口

// src/cni/cni_api.h

// CNI 标准 JSON 结构
struct cni_args {
    char container_id[64];
    char netns_path[256];
    char ifname[16];            // e.g. "eth0"
    char args[1024];            // extra CNI_ARGS
};

struct cni_result {
    char ip_addr[64];           // e.g. "10.244.1.5/24"
    char gateway[32];           // e.g. "10.244.1.1"
    char routes[4096];          // JSON array of routes
    char dns[256];              // JSON object of DNS config
};

// CNI 操作
// /opt/cni/bin/unos-cni ADD   < CNI_CONFIG_JSON
// /opt/cni/bin/unos-cni DEL   < CNI_CONFIG_JSON
// /opt/cni/bin/unos-cni CHECK < CNI_CONFIG_JSON (optional)

// uNOS CNI 实现:
//   ADD:
//     1. 解析 CNI JSON → 提取 container_id, netns_path, ifname
//     2. 连接 uNOS Unix socket (/var/run/unos/cni.sock)
//     3. 发送 IPC_MSG_CNI_ADD → uNOS 控制面:
//        a. IPAM 分配 IP (10.244.x.y/24)
//        b. 创建 memif master socket
//        c. 返回 memif socket path + IP config
//     4. CNI plugin 创建 memif slave 在容器 netns 内
//     5. 返回 CNI result JSON
//
//   DEL:
//     1. 连接 uNOS Unix socket
//     2. 发送 IPC_MSG_CNI_DEL → uNOS 控制面:
//        a. 删除 memif socket
//        b. 回收 IP 地址
//     3. CNI plugin 清理容器网络命名空间

7.4 IPAM 设计

// src/cni/ipam.h

#define POD_CIDR      "10.244.0.0/16"        // 整体 Pod 网络
#define NODE_CIDR     "10.244.0.0/24"        // 本节点子网
#define GATEWAY_IP    "10.244.0.1"           // 网关 IP (uNOS 自己)

struct ipam_pool {
    uint32_t              subnet;             // 10.244.0.0
    uint8_t               prefix_len;         // 24
    uint64_t              allocated_bitmap[4];// 256 IPs bitmap
    uint32_t              allocated_count;
    uint32_t              last_allocated;     // 上次分配的 IP (减少扫描)
};

struct ipam {
    struct ipam_pool      pool;
    uint32_t              gateway_ip;         // 10.244.0.1
    struct rte_hash       *allocations;       // container_id → IP
};

// IP 分配算法: bitmap + last_allocated 加速
static inline int
ipam_allocate(struct ipam *ipam, uint32_t *ip_out) {
    int host_id = -1;
    int start = ipam->pool.last_allocated;

    // 从上次位置开始扫描 (利用局部性)
    for (int i = 0; i < 254; i++) {
        int idx = (start + i) % 254;
        if (!(ipam->pool.allocated_bitmap[idx / 64] & (1ULL << (idx % 64)))) {
            host_id = idx + 2;  // skip .0 (network) and .1 (gateway)
            break;
        }
    }
    if (host_id < 0)
        return -1;  // pool exhausted

    ipam->pool.allocated_bitmap[host_id / 64] |= (1ULL << (host_id % 64));
    ipam->pool.allocated_count++;
    ipam->pool.last_allocated = host_id;
    *ip_out = ipam->pool.subnet | host_id;
    return 0;
}

八、管理面详细设计

8.1 CLI 命令树

Command Tree (vtysh style):
─────────────────────────────

unos> enable
unos# show [running-config|interfaces|route|arp|nat|acl|qos|system|tech-support]
unos# configure terminal
unos(config)#

  [interface <ifname>]
    [ip address <ip/len>]
    [ipv6 address <ipv6/len>]
    [mtu <1500|9000>]
    [shutdown|no shutdown]
    [description <text>]
    [switchport mode <access|trunk>]
    [switchport access vlan <vlan_id>]
    [switchport trunk allowed vlan <vlan_list>]

  [router ospf]
    [router-id <ip>]
    [network <ip/len> area <area_id>]
    [redistribute static]

  [router bgp <asn>]
    [neighbor <ip> remote-as <asn>]
    [network <ip/len>]
    [redistribute connected]

  [ip route <prefix> <nexthop> [metric <n>]]
  [ip route <prefix> null0]                     # 黑洞路由

  [access-list <100-199> <permit|deny> <proto> <src> <dst> [eq <port>]]

  [nat source <internal_ip/len> interface <ifname> overload]  # SNAT
  [nat destination <external_ip> <port> <internal_ip> <port>] # DNAT

  [qos class <0-7> <priority|bandwidth> <value>]

#ifdef PRODUCT_CLOUD_EDGE
  [container-network]
    [cidr <ip/len>]
    [show containers]

  [load-balancer]
    [backend add <name> <ip:port>]
    [frontend add <name> <ip:port> backend <name>]
    [algorithm <rr|leastconn|hash>]

  [vrrp <vrid>]
    [ip <virtual_ip>]
    [priority <1-254>]
    [preempt]
#endif

8.2 REST API 路由表

Method  Path                                  Controller
──────  ────                                  ──────────

# 路由管理
GET     /api/v1/routes                        list_routes
POST    /api/v1/routes                        add_route
DELETE  /api/v1/routes/{prefix}               del_route

# 接口管理
GET     /api/v1/interfaces                    list_interfaces
GET     /api/v1/interfaces/{name}             get_interface
PUT     /api/v1/interfaces/{name}             update_interface
POST    /api/v1/interfaces/{name}/reload      reload_interface

# ACL
GET     /api/v1/firewall/rules                list_acl_rules
POST    /api/v1/firewall/rules                add_acl_rule
DELETE  /api/v1/firewall/rules/{id}           del_acl_rule

# NAT
GET     /api/v1/nat/rules                     list_nat_rules
POST    /api/v1/nat/rules                     add_nat_rule
DELETE  /api/v1/nat/rules/{id}                del_nat_rule
GET     /api/v1/nat/connections               list_connections
GET     /api/v1/nat/connections/summary       connection_summary

# QoS
GET     /api/v1/qos/classes                   list_qos_classes
PUT     /api/v1/qos/classes/{id}              update_qos_class

# 统计
GET     /api/v1/stats/ports                   port_stats
GET     /api/v1/stats/ports/{name}            port_stats_detail
GET     /api/v1/stats/system                  system_stats

# 系统
GET     /api/v1/system/health                 health_check
POST    /api/v1/system/config/export          export_config
POST    /api/v1/system/config/import          import_config

#ifdef PRODUCT_CLOUD_EDGE
# 容器网络
GET     /api/v1/containers                    list_containers
GET     /api/v1/containers/{id}               get_container

# 负载均衡
GET     /api/v1/loadbalancer/backends         list_lb_backends
POST    /api/v1/loadbalancer/backends         add_lb_backend
DELETE  /api/v1/loadbalancer/backends/{name}  del_lb_backend
#endif

九、高可用详细设计

9.1 VRRP 状态机

// src/ha/vrrp.h

#define VRRP_VERSION      3           // RFC 5798
#define VRRP_ADVERT_INT   1000        // Advertisement interval (ms)
#define VRRP_MASTER_DOWN  3000        // Master down detection (3 × advert)

enum vrrp_state {
    VRRP_STATE_INITIALIZE = 0,
    VRRP_STATE_BACKUP,
    VRRP_STATE_MASTER,
};

struct vrrp_instance {
    uint8_t               vrid;           // Virtual Router ID
    uint8_t               priority;       // 本节点优先级 (1-254)
    uint8_t               adv_interval;   // Advertisement 间隔 (centiseconds)
    uint8_t               preempt;        // 抢占模式
    uint32_t              virtual_ip;
    uint8_t               virtual_mac[6]; // 00:00:5E:00:01:{VRID}
    enum vrrp_state       state;
    uint64_t              master_down_deadline; // 超时时间 (rdtsc)
    struct rte_ring       *conn_sync_queue;     // 连接表同步队列
};

// 状态转换:
//   INIT → BACKUP (priority != 255)
//   INIT → MASTER (priority == 255, IP owner)
//   BACKUP → MASTER (master_down timer expired)
//   MASTER → BACKUP (received higher priority advertisement)

9.2 连接表同步协议

// src/ha/sync.h

struct conn_sync_msg {
    uint8_t               type;       // SYNC_ADD / SYNC_DEL / SYNC_FULL
    uint8_t               version;    // 协议版本
    uint16_t              count;      // 批量条目数
    uint32_t              seq_num;    // 序列号 (用于去重)
    struct conntrack_key  keys[32];   // 批量同步 (最多 32 条)
};

十、构建系统设计

10.1 项目结构

unos/
├── meson.build                     # 顶层构建
├── meson_options.txt               # 产品选项
├── src/
│   ├── meson.build                 # 源码构建
│   ├── core/
│   │   ├── meson.build
│   │   ├── main.c
│   │   ├── pipeline.c / pipeline.h
│   │   ├── ipc.c / ipc.h
│   │   └── timer.c / timer.h
│   ├── dp/
│   │   ├── meson.build
│   │   ├── forward.c
│   │   ├── parser.c / parser.h
│   │   ├── l2fwd.c / l2fwd.h
│   │   ├── l3fwd.c / l3fwd.h
│   │   ├── nat.c / nat.h
│   │   ├── acl.c / acl.h
│   │   └── qos.c / qos.h
│   ├── cp/
│   │   ├── meson.build
│   │   ├── control.c / control.h
│   │   ├── arp.c / arp.h
│   │   ├── dhcp.c / dhcp.h
│   │   ├── iface.c / iface.h
│   │   ├── frr_sync.c / frr_sync.h
│   │   └── db.c / db.h
│   ├── stcp/
│   │   ├── meson.build       # 仅 Cloud Edge
│   │   ├── stcp.h
│   │   ├── tcb.h
│   │   ├── tcp_state.c
│   │   ├── tcp_cong.c
│   │   ├── tcp_retx.c
│   │   ├── tcp_flow.c
│   │   ├── tcp_socket.c
│   │   ├── tcp_txrx.c
│   │   ├── tcp_option.c
│   │   ├── ipv4.c
│   │   ├── icmp.c
│   │   └── frag.c
│   ├── cni/
│   │   ├── meson.build       # 仅 Cloud Edge
│   │   ├── memif.c / memif.h
│   │   ├── cni_api.c / cni_api.h
│   │   └── ipam.c / ipam.h
│   ├── app/
│   │   ├── meson.build       # 仅 Cloud Edge
│   │   ├── http_server.c
│   │   ├── dns_server.c
│   │   └── tcp_proxy.c
│   ├── mgmt/
│   │   ├── meson.build
│   │   ├── cli.c / cli.h
│   │   └── rest.c / rest.h
│   ├── ha/
│   │   ├── meson.build       # 仅 Cloud Edge
│   │   ├── vrrp.c / vrrp.h
│   │   └── sync.c / sync.h
│   ├── telemetry/
│   │   ├── meson.build
│   │   ├── prometheus.c / prometheus.h
│   │   └── stats.c / stats.h
│   └── common/
│       ├── meson.build
│       ├── types.h
│       ├── utils.c / utils.h
│       └── log.c / log.h
├── web/
│   ├── index.html
│   └── dashboard.js
├── scripts/
│   ├── setup.sh
│   ├── bench.sh
│   └── profile.sh
├── config/
│   ├── cloud_edge.yaml
│   └── home_cpe.yaml
├── test/
│   ├── meson.build
│   ├── test_parser.c
│   ├── test_lpm.c
│   ├── test_nat.c
│   └── test_tcp_state.c
├── docs/
│   ├── ARCHITECTURE.md
│   └── REQUIREMENTS.md
├── .gitignore
├── LICENSE
└── README.md

10.2 顶层 meson.build

# unos/meson.build
project('unos', 'c',
    version: '0.1.0',
    default_options: [
        'c_std=c17',
        'warning_level=3',
        'buildtype=release',
        'default_library=static',
    ],
)

add_project_arguments('-march=native', '-mtune=native', language: 'c')

# DPDK dependency
dpdk = dependency('libdpdk', required: true, static: true)

# Other dependencies
threads = dependency('threads')
numa    = dependency('numa', required: true)
hiredis = dependency('hiredis', required: true)

subdir('src')

10.3 src/meson.build (产品条件编译)

# unos/src/meson.build

product = get_option('product')

# === Common sources (both products) ===
common_sources = files(
    'core/main.c',
    'core/pipeline.c',
    'core/ipc.c',
    'core/timer.c',
    'dp/forward.c',
    'dp/parser.c',
    'dp/l2fwd.c',
    'dp/l3fwd.c',
    'dp/nat.c',
    'dp/acl.c',
    'dp/qos.c',
    'cp/control.c',
    'cp/arp.c',
    'cp/iface.c',
    'cp/frr_sync.c',
    'cp/db.c',
    'mgmt/cli.c',
    'telemetry/prometheus.c',
    'telemetry/stats.c',
    'common/utils.c',
    'common/log.c',
)

# === Product-specific sources ===
cloud_edge_sources = files(
    'stcp/ipv4.c',
    'stcp/icmp.c',
    'stcp/frag.c',
    'stcp/tcp_state.c',
    'stcp/tcp_cong.c',
    'stcp/tcp_retx.c',
    'stcp/tcp_flow.c',
    'stcp/tcp_socket.c',
    'stcp/tcp_txrx.c',
    'stcp/tcp_option.c',
    'cni/memif.c',
    'cni/cni_api.c',
    'cni/ipam.c',
    'app/http_server.c',
    'app/dns_server.c',
    'app/tcp_proxy.c',
    'mgmt/rest.c',
    'ha/vrrp.c',
    'ha/sync.c',
)

home_cpe_sources = files(
    'cp/dhcp.c',
)

# === Assemble ===
sources = common_sources
product_args = []

if product == 'cloud_edge'
    sources += cloud_edge_sources
    product_args += ['-DPRODUCT_CLOUD_EDGE']
    c_args = product_args
elif product == 'home_cpe'
    sources += home_cpe_sources
    product_args += ['-DPRODUCT_HOME_CPE']
    c_args = product_args
endif

executable('unos',
    sources,
    c_args: c_args,
    dependencies: [dpdk, threads, numa, hiredis],
    install: true,
)

# === CNI plugin binary (Cloud Edge only) ===
if product == 'cloud_edge'
    executable('unos-cni',
        files('cni/cni_api.c'),
        dependencies: [],
        install: true,
        install_dir: '/opt/cni/bin',
    )
endif

十一、测试设计

11.1 单元测试

// test/meson.build — 每个模块对应一个测试

parser_test = executable('test_parser',
    'test_parser.c', '../src/dp/parser.c', '../src/common/utils.c',
    dependencies: [dpdk],
)
test('Parser Test', parser_test)

lpm_test = executable('test_lpm',
    'test_lpm.c', '../src/dp/l3fwd.c',
    dependencies: [dpdk],
)
test('LPM Test', lpm_test)

nat_test = executable('test_nat',
    'test_nat.c', '../src/dp/nat.c', '../src/dp/parser.c',
    dependencies: [dpdk],
)
test('NAT Test', nat_test)

tcp_state_test = executable('test_tcp_state',
    'test_tcp_state.c', '../src/stcp/tcp_state.c', '../src/stcp/tcb.h',
    dependencies: [dpdk],
)
test('TCP State Machine Test', tcp_state_test)

11.2 集成测试脚本

# scripts/bench.sh — 自动化压测

#!/bin/bash
echo "=== uNOS Performance Benchmark Suite ==="

# 1. L2 Forwarding (64B)
echo "[1/6] L2 Forwarding 64B line rate..."
dperf -c 0-1 -n 2 -- --forward-mode mac --pkt-size 64 --duration 30
# Expected: 1.488 Mpps (1G) / 14.88 Mpps (10G), 0% loss

# 2. L3 Forwarding (64B)
echo "[2/6] L3 Forwarding 64B..."
dperf -c 0-1 -n 2 -- --forward-mode lpm --pkt-size 64 --duration 30

# 3. NAT (64B)
echo "[3/6] NAT Forwarding 64B..."
# Start uNOS with NAT enabled, then:
dperf -c 0-1 -n 2 -- --pkt-size 64 --duration 30

# 4. QoS (mixed traffic)
echo "[4/6] QoS mixed traffic test..."
# Background: iperf3 -c target -t 30 -b 900M (big packets)
# Foreground: ping -i 0.001 target -c 10000 (small packets)
# Expected: ping p99 < 2ms even with background load

# 5. TCP Stack throughput
echo "[5/6] TCP Stack throughput..."
iperf3 -c localhost -p 8080 -t 30
# Expected: >= 800 Mbps (J3455) / >= 9.4 Gbps (T140)

# 6. HTTP Server QPS
echo "[6/6] HTTP Server QPS..."
wrk -t 2 -c 100 -d 30s http://localhost:8080/
# Expected: >= 50K (J3455) / >= 200K (T140)

十二、接口契约

12.1 内部模块 API 契约

生产者 → 消费者 接口 契约
Pipeline Stage typedef uint16_t (*process_fn)(mbuf**, uint16_t) — 输入/输出都是 mbuf 数组,返回处理后的包数
Parser Rest parser_result_t 写入 mbuf 私有区域,一次解析、多次使用
L3Fwd ARP arp_lookup(ip) → mac — 非阻塞,miss 返回 NULL
NAT Conntrack conntrack_lookup(key) → entry — O(1) 平均,miss 返回创建的新 entry
Control Dataplane ipc_send(msg) — 非阻塞 ring enqueue
Dataplane Control ipc_send(msg) — 非阻塞 ring enqueue

12.2 外部接口契约

接口 协议 格式
CNI Plugin ↔ uNOS Unix socket (SOCK_STREAM) JSON
CLI ↔ uNOS Unix socket Text (vtysh)
REST ↔ uNOS HTTP/1.1 / HTTP/2 JSON
FRR ↔ uNOS Netlink (AF_NETLINK) Binary (FPM)
Redis ↔ uNOS TCP (localhost:6379) Redis Protocol (RESP)
Prometheus ↔ uNOS HTTP Text (Prometheus exposition format)

文档结束

版本: v1.0 | 日期: 2026-07-29 随项目推进持续更新设计细节