测试与限制
ax-net 测试分三层:net/ax-net crate 内单元测试覆盖协议栈内部数据结构和路由/绑定语义;StarryOS system 测试覆盖 Linux ABI 观测面;apps/starry/qemu/dual-net 覆盖双网口 DHCP、路由和并发数据面。
1. 测试资产
网络测试按纯数据结构、host-test、StarryOS system case 和双网卡集成场景分层,每层验证不同故障面。下表把测试位置与职责对应起来,选择验证命令时应优先使用能覆盖原始行为边界的最低层测试,再补充跨系统路径。
| 层级 | 位置 | 作用 |
|---|---|---|
| crate 单元测试 | net/ax-net/src 内各 #[cfg(test)] 模块 | 验证 RouteTable、NetControl/Service、TCP/UDP 设备绑定、UDP bind 表、TCP listen 表和通用 socket option |
| host 集成测试 | net/ax-net/tests/std.rs | 验证 public 配置/快照、option dispatch、credentials 和 route 数据类型;需要 host-test |
| StarryOS system 测试 | test-suit/starryos/qemu/system | 验证 Linux socket syscall、ioctl、AF_PACKET、netlink、procfs 等 ABI |
| dual-net 集成测试 | apps/starry/qemu/dual-net | 验证两张 virtio-net、双 DHCP、接口绑定下载和并发数据面 |
| xtask 结构自检 | scripts/axbuild/src/starry/test/tests/asset_network_tests.rs | 验证 dual-net app 配置必须包含双网卡、host HTTP fixture 和 guest probe |
资产表说明每层测试使用不同运行环境和观察面,不能用单元测试替代 ABI 或双网卡验证。单元测试首先固定纯状态结构的确定语义,为更高层失败提供可快速排除的基础。
2. 单元测试
ax-net 单元测试围绕无需设备或 QEMU 即可确定验证的状态结构组织,包括路由排序、DHCP 提交、端口仲裁和通用选项。各小节给出测试所在模块和必须保持的行为契约,便于在最低层快速定位回归。
2.1 运行方式
ax-net 单元测试通过 host-test feature 编译可在宿主机执行的路由、绑定和控制面逻辑,不启动 QEMU 或真实 NIC。该命令适合快速验证纯 Rust 状态转换,但不能替代 StarryOS ABI 或设备 Worker 的端到端覆盖。
cargo test -p ax-net --features host-test
ax-net 在 std 白名单中,完整仓库入口是 cargo xtask test;该入口会为 ax-net 自动选择 host-test profile。上面的命令用于单独迭代这个 crate。单元/集成测试主要覆盖不依赖真实 QEMU 设备的内部逻辑。部分测试会使用 lib.rs 中的 test_support 构造一个 split-route 测试网络:
LOCAL_IF = InterfaceId(2), LOCAL_ADDR = 10.0.2.15
PEER_IF = InterfaceId(3), PEER_ADDR = 10.0.3.15
network_test_guard() 用全局 mutex 串行化会初始化全局网络状态的测试,避免 SERVICE、NET_CONTROL、SOCKET_SET 这类全局单例在并发 host test 中互相污染。
2.2 路由表
路由表测试位于 router.rs,直接构造 Rule 与 RouteTable 验证最长前缀、metric、源地址和设备绑定过滤。它们是多网卡选路的最低层契约,失败时应先修正规则实现,而不是调整上层集成测试预期。
| 测试 | 覆盖点 |
|---|---|
route_lookup_uses_longest_prefix | 最长前缀优先,10.0.1.0/24 优先于默认路由 |
route_lookup_uses_metric_for_same_prefix | 同前缀按 metric 小者优先 |
route_lookup_keeps_stable_order_for_equal_metric | 同前缀、同 metric 时保持插入顺序 |
route_lookup_skips_unusable_interface | select_route_if() 可通过闭包跳过不可用接口 |
default_routes_only_reports_zero_prefix_ipv4_rules | default_routes() 只导出 IPv4 0.0.0.0/0 规则 |
bounded_packet_queue_reports_full_and_preserves_order | 有界队列满时返回错误,并保持 FIFO |
rx_backpressure_preserves_frame_len_pairing | shared RX queue 背压时,本地 batch 保留 packet 与 L2 frame 长度的 1:1 配对 |
no_route_does_not_count_interface_tx_dropped | IP 层无路由不错误归入某个网卡 tx_dropped |
stats_reflects_current_counters_after_counting | NetDevStats 快照反映累计原子计数 |
这些测试对应多网口 route decision 的核心排序规则:最长前缀、metric、稳定顺序和接口可用性过滤。
2.3 DHCP 地址状态
DHCP 地址状态测试位于 service.rs,验证 lease 提交或清理时 smoltcp 地址、接口快照、路由和 DNS 来源保持同步。测试应覆盖 ACK、NAK、更新与删除等确定状态转换,防止控制面出现部分更新。
| 测试 | 覆盖点 |
|---|---|
dhcp_configured_is_true_once_any_interface_has_address | 多 DHCP 接口中只要任一接口已获得地址,bootstrap 状态即可视为完成 |
interface_address_table_handles_loopback_and_two_ethernet_addresses | smoltcp Interface address list 能同时保存 loopback、eth0、eth1 IPv4 |
这组测试防止网络初始化重新退化为“只看第一个网卡”或“接口地址表只能容纳单 Ethernet 地址”的模型。
2.4 TCP 设备绑定
TCP 设备绑定测试位于 tcp.rs,检查具体本地地址、SO_BINDTODEVICE 和路由结果如何约束 connect、listen 与 readiness。测试重点是拒绝不匹配接口,而不是仅确认正常主接口可以连接。
| 测试 | 覆盖点 |
|---|---|
tcp_info_reports_default_socket_metrics | TCP_INFO 在 closed socket 上返回稳定默认字段 |
connect_preserves_bound_interface | TCP bind 到具体本地地址后,connect 不会被 peer route 改写绑定接口 |
connect_uses_peer_route_when_unbound | wildcard bind 的 TCP connect 根据目的地址 route decision 选择接口 |
connect_rejects_unroutable_bound_device | 显式绑定到不可达接口后,connect 返回错误并保留原绑定 |
这组测试覆盖 SO_BINDTODEVICE 和本地地址推导出的 DeviceBinding 对 TCP connect 的影响。
2.5 UDP 设备绑定
UDP 设备绑定测试位于 udp.rs,验证 connected 与 unconnected datagram 在显式接口约束下选择本地地址和出口。用例还需要区分 bind endpoint、peer endpoint 与每次 sendto 目标,避免把 TCP 的连接语义错误套用到 UDP。
| 测试 | 覆盖点 |
|---|---|
connect_preserves_bound_interface | UDP bind 到具体本地地址后,connect 不会改写绑定接口 |
connect_uses_peer_route_when_unbound | wildcard bind 的 UDP connect 根据目的地址 route decision 选择接口 |
connect_rejects_unroutable_bound_device | 显式绑定到不可达接口后,connect 返回错误并保留原绑定 |
UDP 的测试与 TCP 对齐,重点是 datagram socket 的 connected peer 不应破坏本地地址绑定语义。
2.6 UDP 绑定表
UDP 绑定 side table 测试位于 wrapper.rs,覆盖 wildcard 与具体地址冲突、设备约束以及 SO_REUSEPORT 组规则。SO_REUSEADDR 只保存选项而不会跳过 side table,相关负向用例应保持这一实现边界。
| 测试 | 覆盖点 |
|---|---|
udp_bind_rules_allow_distinct_specific_addresses | 相同端口可绑定到不同具体本地地址;相同地址冲突;wildcard 与具体地址冲突 |
udp_bind_rejects_specific_after_wildcard | 已存在 wildcard bind 时拒绝后续具体地址 bind |
udp_reuseport_group_shares_a_port_while_plain_binders_conflict | 只有双方 SO_REUSEPORT 且 endpoint 完全相同才允许共同绑定 |
这些测试补齐 smoltcp UDP socket 之外的 Linux 风格 wildcard/specific bind 仲裁。
2.7 TCP 监听表
TCP 监听表测试位于 listen_table.rs,验证 wildcard/具体地址 listener 的查找、SYN child 预创建和 accept queue 生命周期。它们保证单协议核心上的 passive open 语义不会因设备数量增加而复制 listener 或遗漏端口冲突。
| 测试 | 覆盖点 |
|---|---|
allows_same_port_on_distinct_specific_addresses | 同端口可以在不同具体地址上 listen |
wildcard_listener_conflicts_with_specific_addresses | wildcard listener 与任一具体地址 listener 冲突 |
reuseport_group_shares_a_listen_endpoint | reuseport listener 可共享完全相同 endpoint |
plain_listener_rejects_reuseport_join | 普通 listener 与 reuseport group 不能混合加入 |
这组测试覆盖 per-address listen 的冲突规则,是 wildcard listen、0.0.0.0:port 和多本地地址共存语义的基础。
2.8 通用选项
通用选项测试位于 general.rs,检查 nonblocking、timeout、reuse、设备绑定和 socket identity 的原子读写及默认值。只回显但不影响后端行为的选项也必须被明确识别,不能通过测 试一个 setter/getter 就声称完整支持。
| 测试 | 覆盖点 |
|---|---|
device_binding_round_trips_none_and_some_interface | DeviceBinding 在 GeneralOptions 中可以从 none 到指定接口再回到 none |
reuse_address_and_reuse_port_are_independent_flags | 两个 reuse option 独立保存 |
socket_priority_matches_unprivileged_linux_range | SO_PRIORITY 只接受 0..=6 |
ip_tos_storage_masks_user_controlled_ecn_bits | IP_TOS 清除用户可控 ECN 位 |
轮询、Ethernet 与统计还有专门覆盖:synchronous_flush_waits_for_active_poll_owner 验证 required ownership;poll_timeout_wakes_devices_as_polling_fallback 验证定时兜底;send_to_wire_len_respects_eth_zlen_padding、ARP deferred frame、malformed frame 和 pending buffer 测试验证 /proc/net/dev 的 L2 长度/error/drop 口径。net/ax-net/tests/std.rs 的 6 个 public API 集成测试仅在 host-test feature 下构建。
DeviceBinding 使用 atomic raw ifindex 保存,这个测试验证 public 语义不会因为内部原子编码而丢失。
3. StarryOS 系统测试
StarryOS 系统测试在 QEMU 中运行真实用户态程序和 syscall 路径,覆盖单元测试无法观察的 ABI 编解码、fd 生命周期和 proc/netlink 输出。测试分组位于 test-suit/starryos/qemu/system,应通过 xtask 入口运行以保持镜像、参数和成功正则一致。
3.1 运行方式
完整 QEMU system 组会构建 StarryOS、启动 QEMU 并按 case 配置执行用户态测试,适合验证网络改动的主要 ABI 回归。定向调试时可以缩小 case,但最终仍应回到相同 xtask 流程,避免原生命令遗漏 feature 或运行参数。
cargo xtask starry test qemu --arch riscv64 -c qemu-smp1/system
常用跨架构回归:
cargo xtask starry test qemu --arch riscv64
cargo xtask starry test qemu --arch loongarch64
system 测试使用 StarryOS guest 内的 Linux 用户态程序验证 syscall/ABI 层。网络相关用例主要覆盖以下几类。
3.2 Socket 数据面
Socket 数据面用例从 StarryOS 用户态调用真实 syscall,覆盖连接、收发、poll 和选项语义,再由 ax-net 与 smoltcp 完成协议推进。表中的测试位置用于定位失败属于 syscall 翻译、socket backend 还是底层数据路径。
| 测试 | 位置 | 覆盖点 |
|---|---|---|
syscall-test-socket-dataplane | test-suit/starryos/qemu/system/syscall-test-socket-dataplane | TCP/UDP/raw socket 数据面基础行为 |
bugfix-bug-tcp-send-no-epoll-notify | test-suit/starryos/qemu/system/bugfix-bug-tcp-send-no-epoll-notify | TCP send 后 epoll waiter 唤醒 |
bugfix-bug-ip-mtu-discover-udp-flush | test-suit/starryos/qemu/system/bugfix-bug-ip-mtu-discover-udp-flush | IP_MTU_DISCOVER 读回和 UDP send 后立即 close 的交付 |
syscall-test-so-reuseport | test-suit/starryos/qemu/system/syscall-test-so-reuseport | TCP/UDP SO_REUSEPORT 共同绑定边界 |
c-regression-test-icmp-ping-socket | test-suit/starryos/qemu/system/c-regression-test-icmp-ping-socket | IPv4 ICMP ping datagram socket |
Socket 数据面表覆盖真实 syscall 到 smoltcp 的主路径,但不检查管理接口是否读取同一控制面。下一节通过 ioctl、netlink 与 procfs 补充这些只读和运行期更新视图。
3.3 Linux 管理 ABI
Linux 管理 ABI 用例验证 ioctl、rtnetlink 和 procfs 是否观察到同一份 NetControl 与设备统计快照。这里不仅检查结 构体编码,还要确认运行期地址更新会实际改变路由和后续发包行为。
| 测试 | 位置 | 覆盖点 |
|---|---|---|
bugfix-bug-netlink-getlink | test-suit/starryos/qemu/system/bugfix-bug-netlink-getlink | RTM_GETLINK、SIOCGIFTXQLEN、link 属性 |
bugfix-bug-netlink-getaddr | test-suit/starryos/qemu/system/bugfix-bug-netlink-getaddr | RTM_GETADDR、loopback address、link/address dump |
c-regression-test-netlink-rtnetlink | test-suit/starryos/qemu/system/c-regression-test-netlink-rtnetlink | route dump、IPv4 RTM_NEWADDR/DELADDR 及错误映射 |
c-regression-test-socket-device-ioctl | test-suit/starryos/qemu/system/c-regression-test-socket-device-ioctl | 跨 socket family 的 SIOCGIFNAME/device ioctl |
syscall-test-netlink-recvmsg | test-suit/starryos/qemu/system/syscall-test-netlink-recvmsg | netlink recvmsg 基础语义 |
bugfix-bug-proc-net-arp | test-suit/starryos/qemu/system/bugfix-bug-proc-net-arp | /proc/net/arp 格式和真实 device 字段 |
syscall-test-procstats | test-suit/starryos/qemu/system/syscall-test-procstats | /proc/net/dev 列格式与 loopback 真实计数增长 |
管理 ABI 表要求多个接口共享 InterfaceId 和状态来源,能够捕获结构编码正确但数据源分裂的问题。AF_PACKET 使用二层地址与 frame 语义,因此需要在相同身份基础上另行验证。