|
1 | | -# 01 需求规格:F-Stack 连接级选栈增强 lib |
| 1 | +# 01 需求规格:F-Stack 连接级选栈增强(单 API + 标记 + config 默认开关) |
2 | 2 |
|
3 | 3 | > **文档编号**:SPEC-KE-01 |
4 | | -> **版本**:v2(全量重做) |
| 4 | +> **版本**:v3(范式修正重做) |
5 | 5 | > **日期**:2026-06-15 |
6 | 6 | > **状态**:编写中 |
7 | | -> **作用域**:定义"连接级选栈增强 lib"的问题域、目标/非目标、功能与非功能需求、成功标准。 |
| 7 | +> **作用域**:定义本特性的问题域、目标/非目标、功能与非功能需求、成功标准。 |
8 | 8 |
|
9 | 9 | --- |
10 | 10 |
|
11 | 11 | ## 1. 问题域 |
12 | 12 |
|
13 | | -F-Stack 通过 DPDK 接管网卡后,该网卡流量绕过 Linux 内核协议栈,导致**本机工具(`ping`/`curl`/`ssh` 等)无法访问运行在 F-Stack 用户态栈上的服务**。 |
| 13 | +F-Stack 通过 DPDK 接管网卡后,该网卡流量绕过 Linux 内核协议栈,导致: |
14 | 14 |
|
15 | | -现有解法分两类: |
| 15 | +1. **服务端方向**:本机工具(`ping`/`curl`/`ssh`)无法访问运行在 F-Stack 用户态栈上的服务。 |
| 16 | +2. **客户端方向**:F-Stack 应用作客户端时,无法用内核栈去 `connect` 本机服务(`127.0.0.1`/本机内核栈 IP)或外部内核栈服务。 |
16 | 17 |
|
17 | | -- **报文回灌(KNI/virtio-user/TAP)**:把裸报文送回内核——**本特性不采用**(见非目标)。 |
18 | | -- **连接级选栈**:应用**主动**在内核栈侧也创建/监听 socket,按连接/监听粒度选栈,并在统一事件循环中服务两栈——**本特性采用**。 |
| 18 | +F-Stack 的 `adapter/syscall`(hook/LD_PRELOAD 模式)**已实现"单 POSIX API + `SOCK_KERNEL`/`SOCK_FSTACK` 标记 + 胶水自动适配"**的选栈能力(见 `02`),但: |
| 19 | +- 该标记语义**内嵌**在 syscall 适配层,未被标准化为"任意 F-Stack 应用可依赖的选栈约定"; |
| 20 | +- **缺少 config.ini 级别的"全局默认栈开关"**(只能靠应用逐 socket 设标记); |
| 21 | +- **客户端连本机/外部内核栈服务**未被系统化文档化; |
| 22 | +- **原生 `ff_api` 模式**的 `ff_socket` 现状**不识别**选栈标记(`02 §5`,恒建 F-Stack socket)。 |
19 | 23 |
|
20 | | -F-Stack 自身已有两处"连接级选栈"实现(`02` 机制 A/B),但分别**内嵌**在 nginx 适配与 LD_PRELOAD syscall 适配中,**无法被任意 F-Stack 应用直接复用**。本特性即将其抽象为**应用无关的通用 lib**。 |
| 24 | +本特性即:**标准化现有"单 API + 标记选栈"约定 + 补齐 config.ini 全局默认开关 + 覆盖客户端方向 + 在原生模式补齐标记识别**,使任意 F-Stack 应用无需改用多套 API 即可按需选栈。 |
21 | 25 |
|
22 | 26 | --- |
23 | 27 |
|
24 | 28 | ## 2. 目标与非目标 |
25 | 29 |
|
26 | 30 | ### 2.1 目标(In Scope) |
27 | | -- G1:提供一个 lib,使 F-Stack 应用能在**内核栈侧创建/监听 socket**,本机 `ping`/`curl` 等可直接访问该服务。 |
28 | | -- G2:提供**连接/监听级选栈**能力(某监听走内核栈、某监听走 F-Stack),范式借鉴机制 A 的 `belong_to_host`。 |
29 | | -- G3:提供**统一 fd/event 抽象**,使一个事件循环可同时服务内核栈 fd 与 F-Stack fd(合并事件),范式借鉴机制 B 的 `fstack_kernel_fd_map` + 双栈 epoll。 |
30 | | -- G4:对应用**低侵入**(编译开关 + 少量 API),不强制改造业务逻辑;不绑定 nginx 或 LD_PRELOAD。 |
| 31 | +- **G1(标记标准化)**:以现有 `SOCK_KERNEL`/`SOCK_FSTACK`(`ff_adapter.h:7-8`)为**唯一选栈标记**,标准化为可被任意 F-Stack 应用使用的约定;**不新造 `ff_local_*` 双 API、不新造 `belong_to_host` 参数**。 |
| 32 | +- **G2(config.ini 全局默认开关)**:在 config.ini 新增**一个全局默认栈开关**(仿 `[kni]` 范式),决定本进程默认走 F-Stack 还是内核栈;**优先级:app 标记 > config 默认**。 |
| 33 | +- **G3(服务端选栈)**:应用可让某监听 socket 走内核栈,本机 `ping`/`curl`/`ssh` 直访其服务。 |
| 34 | +- **G4(客户端选栈,新增)**:应用作客户端可经内核栈 `connect` 访问本机回环/本机内核栈 IP 服务**及外部内核栈服务**(承载点 `ff_hook_connect:858`)。 |
| 35 | +- **G5(双模式覆盖)**:选栈标记与客户端能力同时覆盖 **hook 模式**(完整复用)与**原生 `ff_api` 模式**(补齐 `ff_socket` 标记识别,`02 §5`)。 |
| 36 | +- **G6(统一事件)**:单事件循环同时服务内核栈 fd 与 F-Stack fd(复用 `fstack_kernel_fd_map` 双栈合并)。 |
| 37 | +- **G7(低侵入/默认零开销)**:编译开关默认关闭;不设标记/默认 F-Stack 时行为与原 F-Stack 一致。 |
31 | 38 |
|
32 | 39 | ### 2.2 非目标(Out of Scope) |
33 | | -- N1:**不**采用 KNI / `rte_kni` / virtio-user / TAP / AF_XDP 等报文回灌方案(`rte_kni` 已于 DPDK 23.11 移除;gazelle KNI 亦衰退)。 |
34 | | -- N2:本阶段**不写实现代码、不改 f-stack 源码**,仅产出中文 spec。 |
35 | | -- N3:本阶段**不生成英文文档**。 |
36 | | -- N4:不实现内核栈与 F-Stack 之间的 socket 自动迁移/透明代理(连接归属在创建时确定)。 |
| 40 | +- **N1**:**不**新造 `ff_local_*` 双 API / 类 mTCP 双命名空间(v2 做法作废)。 |
| 41 | +- **N2**:**不**做 gazelle 式**线程级选栈**(F-Stack 多进程模型靠不同 config 文件区分,见 `02 §4`)。 |
| 42 | +- **N3**:**不**做 config.ini 端口/地址规则名单(仅"一个全局默认开关")。 |
| 43 | +- **N4**:**不**采用 KNI/`rte_kni`/virtio-user/TAP/AF_XDP 报文回灌。 |
| 44 | +- **N5**:本阶段**不写实现代码、不改 f-stack 源码**,仅产出中文 spec;**不生成英文文档**。 |
| 45 | +- **N6**:不实现内核栈与 F-Stack 间 socket 自动迁移/透明代理(归属在创建时确定)。 |
37 | 46 |
|
38 | 47 | --- |
39 | 48 |
|
40 | 49 | ## 3. 功能需求(FR) |
41 | 50 |
|
42 | 51 | | 编号 | 需求 | 验收要点 | 代码依据 | |
43 | 52 | |---|---|---|---| |
44 | | -| FR-1 | 应用可在内核栈侧创建监听 socket,本机 `curl`/`ssh` 可访问 | 本机访问内核栈监听成功 | 机制 A `ngx_event_connect.c:46-50` | |
| 53 | +| FR-1 | **标记选栈(服务端)**:带 `SOCK_KERNEL` 的监听 socket 走内核栈,本机 `curl`/`ssh` 可访问 | 本机访问内核栈监听成功 | `ff_hook_socket:387-390` | |
45 | 54 | | FR-2 | 本机 `ping`(ICMP)对内核栈侧地址可达 | ping 通 | 内核栈原生处理 ICMP | |
46 | | -| FR-3 | 连接/监听级选栈:可声明某监听走内核栈或 F-Stack | 两类监听并存且各自可达 | 机制 A `belong_to_host` `ngx_http.c:1890` | |
47 | | -| FR-4 | 统一事件循环:单循环同时收 F-Stack 与内核栈事件 | 两栈事件均被正确投递 | 机制 B `ff_hook_syscall.c:2329-2338` | |
48 | | -| FR-5 | fd 归属判定:lib 能区分 fd 属内核栈还是 F-Stack | API 行为按归属正确分流 | 机制 B `is_fstack_fd` `:309` | |
49 | | -| FR-6 | 资源联动:关闭/异常时两栈 fd 一致释放 | 无 fd 泄漏 | 机制 B close 联动 `:1874-1883` | |
50 | | -| FR-7 | 编译开关:本能力可编译期开/关,默认关闭零开销 | 关闭时与原 F-Stack 行为一致 | 机制 B `Makefile -DFF_KERNEL_EVENT` | |
| 55 | +| FR-3 | **标记选栈(客户端,新增)**:F-Stack 应用经内核栈 `connect` 本机回环/本机 IP 服务 | 本机 server + F-Stack client connect 通 | `ff_hook_connect:858` + `is_fstack_fd:309` | |
| 56 | +| FR-4 | **客户端连外部内核栈服务(新增)**:F-Stack 应用作客户端选栈访问外部内核栈服务 | 连外部内核服务成功 | `ff_hook_connect:858` → `ff_linux_connect:144` | |
| 57 | +| FR-5 | **config.ini 全局默认开关**:可配置本进程默认栈(F-Stack/内核),app 标记可覆盖 | 默认栈生效、标记覆盖生效 | `ff_config.c:1011`/`ff_config.h:310-319` 范式 | |
| 58 | +| FR-6 | **双模式覆盖**:hook 模式直接复用标记;原生 `ff_api` 模式补齐 `ff_socket` 标记识别 | 两模式均可标记选栈 | `02 §2`(hook)/`02 §5`(原生差异) | |
| 59 | +| FR-7 | 统一事件循环:单循环同时收 F-Stack 与内核栈事件 | 两栈事件均正确投递 | `ff_hook_syscall.c:2324+` | |
| 60 | +| FR-8 | fd 归属判定 + 资源联动:按归属分流,关闭/异常时两栈 fd 一致释放 | 行为正确、无 fd 泄漏 | `is_fstack_fd:309`、close 联动 `:1874-1883` | |
| 61 | +| FR-9 | 编译开关:本能力可编译期开/关,默认关闭零开销 | 关闭时与原 F-Stack 行为一致 | `Makefile -DFF_KERNEL_EVENT` 范式 | |
51 | 62 |
|
52 | 63 | --- |
53 | 64 |
|
54 | 65 | ## 4. 非功能需求(NFR) |
55 | 66 |
|
56 | 67 | | 编号 | 需求 | |
57 | 68 | |---|---| |
58 | | -| NFR-1 | **默认零开销**:未开启本能力时不引入任何额外分支/内存开销 | |
59 | | -| NFR-2 | **业务快路径无回归**:F-Stack 高速路径性能不受影响(性能基线见 `07`) | |
| 69 | +| NFR-1 | **默认零开销**:未开启/默认 F-Stack 时不引入额外分支/内存开销 | |
| 70 | +| NFR-2 | **业务快路径无回归**:F-Stack 高速路径性能不受影响(基线见 `07`) | |
60 | 71 | | NFR-3 | **可移植**:兼容本工作区 DPDK 23.11.5 / 24.11.6 与移植后的 FreeBSD 栈 | |
61 | 72 | | NFR-4 | **可观测**:提供两栈 fd 数、事件数等基本统计 | |
62 | | -| NFR-5 | **接口稳定**:lib API 语义贴合 POSIX/`ff_api.h`,降低学习成本 | |
| 73 | +| NFR-5 | **接口稳定/低侵入**:复用现有单 API + 标记,不引入多套 API;语义贴合 POSIX/`ff_api.h` | |
| 74 | +| NFR-6 | **多进程一致**:每进程经各自 config.ini 独立设默认栈,互不影响(`ff_config.filename:254`) | |
63 | 75 |
|
64 | 76 | --- |
65 | 77 |
|
66 | 78 | ## 5. 边界与异常场景 |
67 | 79 |
|
68 | | -- 内核栈侧地址/端口与 F-Stack 侧冲突时的行为约定(应报错而非静默)。 |
69 | | -- `maxevents` 过小(机制 B 要求 `>=2`)时的处理。 |
70 | | -- 内核栈 fd 与 F-Stack fd 在同一 epoll/kqueue 中混用时的事件合并正确性。 |
71 | | -- 本能力关闭(编译期/运行期)时,所有 API 退化为纯 F-Stack 行为。 |
72 | | -- 系统前提缺失(参考 gazelle `rp_filter=1`)时的检测与提示。 |
| 80 | +- `SOCK_KERNEL` 与 `SOCK_FSTACK` 同时置位时的优先级(实测 `ff_hook_socket:387` 要求 `SOCK_KERNEL && !SOCK_FSTACK` 才走内核,需在接口契约明确)。 |
| 81 | +- app 标记与 config 默认冲突时:**app 标记优先**。 |
| 82 | +- 内核栈侧地址/端口与 F-Stack 侧冲突时报错而非静默。 |
| 83 | +- `maxevents` 过小(机制要求 `>=2`,`:2212-2218`)时的处理。 |
| 84 | +- 客户端 `connect` 时 fd 归属与目的地址栈不匹配(如内核 fd 连 F-Stack 才可达的地址)时的行为约定。 |
| 85 | +- 原生模式 `ff_socket` 标记识别补齐前后的兼容性(`02 §5`)。 |
| 86 | +- 本能力关闭(编译期)时所有路径退化为纯 F-Stack 行为。 |
| 87 | +- 系统前提缺失(参考 gazelle `rp_filter` 等)时的检测与提示。 |
73 | 88 |
|
74 | 89 | --- |
75 | 90 |
|
76 | 91 | ## 6. 成功标准 |
77 | 92 |
|
78 | | -1. DPDK 接管网卡后,本机 `ping <内核栈侧IP>` 通、`curl <内核栈侧服务>` 成功(FR-1/FR-2)。 |
79 | | -2. 同一应用进程内"内核栈监听"与"F-Stack 监听"并存,单事件循环均正确收发(FR-3/FR-4)。 |
80 | | -3. 本能力关闭时,F-Stack 业务性能与功能**零回归**(NFR-1/NFR-2)。 |
81 | | -4. spec 全集过 `08-review-gate.md` 门禁。 |
| 93 | +1. DPDK 接管网卡后,带 `SOCK_KERNEL` 的内核栈监听:本机 `ping <内核栈IP>` 通、`curl <内核栈服务>` 成功(FR-1/FR-2)。 |
| 94 | +2. F-Stack 应用作客户端:经内核栈 `connect` 本机服务(127.0.0.1/本机 IP)与外部内核栈服务均成功(FR-3/FR-4)。 |
| 95 | +3. config.ini 全局默认栈开关生效,且 app 标记可覆盖(FR-5);多进程用不同 config 文件得到不同默认栈(NFR-6)。 |
| 96 | +4. hook 与原生两模式均可标记选栈(FR-6);单事件循环正确服务两栈(FR-7/FR-8)。 |
| 97 | +5. 本能力关闭/默认 F-Stack 时,业务性能与功能**零回归**(NFR-1/NFR-2)。 |
| 98 | +6. spec 全集过 `08-review-gate.md` 门禁。 |
0 commit comments