跳到主要内容

配置参考

ax-net 的配置由结构化 NetworkConfig、Cargo feature、运行时设备注册参数和一组集中常量组成。配置目标是明确表达每个接口的意图,避免旧式单网口全局变量和隐式 eth0 假设。

核心源码:

配置域源码
featureCargo.toml
接口配置模型config.rs
初始化解析与校验lib.rs init_network()
缓冲区/队列常量consts.rs
TCP keepalive / TCP_INFO 默认值tcp.rs
DHCP/DNS 默认值lib.rs, service.rs
Ethernet ARP 默认值device/ethernet.rs

这些源码锚点分别拥有编译 feature、启动输入、运行期常量和协议默认值,配置变更应在对应所有者处完成。跨越多行的调整还需要验证 init_network() 如何把输入转换为控制面和设备运行时,而不是只修改文档中的示例值。

1. 构建配置

构建配置决定是否启用可选协议族。基础 TCP、UDP、raw、Unix domain socket、DNS、DHCP 和 Ethernet 能力不需要额外 feature。

1.1 Cargo Feature

Cargo feature 决定哪些 transport 和集成适配会进入编译结果,并直接影响 ax-net 的依赖边界。维护 feature 组合时需要同时核对 Cargo.toml、条件编译模块和上层 runtime,因为启用一个协议后端并不自动提供对应平台设备或 syscall ABI。

[features]
host-test = ["ax-hal/host-test", "axpoll/host-test", "ax-sync/host-test", "ax-task/host-test"]
vsock = ["dep:rdif-vsock"]

feature 声明只控制编译依赖与条件模块,实际对外能力还取决于 runtime 是否提供相应设备和初始化调用。下表进一步说明每个 feature 在测试或产品构建中的作用范围,避免把 host-test 带入 bare-metal 配置,或在没有 vsock 设备时假定后端可用。

feature作用
host-test在宿主机启用 ArceOS 调度、同步和 poll 测试后端,并开放 tests/std.rs 集成测试;不用于 bare-metal 产品构建
vsock启用 rdif-vsock 依赖、AF_VSOCK socket backend 和 vsock device 初始化

启用 vsock 后导出:

  • init_vsock(vsock_devs)
  • vsock 模块。
  • Socket::Vsock 变体。
  • VsockDevice / VsockDeviceList 类型别名。

导出项列表说明 vsock feature 同时改变初始化 API 和 Socket 枚举,因此上层必须在相同条件下编译调用代码。smoltcp feature 属于 IP 协议核心的固定能力,和这个可选 transport 边界分开维护。

1.2 smoltcp 能力

ax-net 固定启用的 smoltcp feature 决定协议核心能够编译哪些 socket 与介质能力,但不代表外围设备和 Linux ABI 已完整接入。维护这组能力时需要同时检查 ServiceRouter 和具体 backend,下面列出的 feature 才是当前构建实际依赖的集合。

  • alloc
  • log
  • async
  • medium-ethernet
  • medium-ip
  • proto-ipv4
  • proto-ipv6
  • packetmeta-id(携带 RX 侧 ingress 元数据,供 rx_meta 模块传递接收侧 QoS)
  • socket-raw
  • socket-icmp
  • socket-udp
  • socket-tcp
  • socket-dhcpv4
  • socket-dns
  • auto-icmp-echo-reply
  • iface-max-addr-count-8(允许 Interface 同时保存最多 8 个 IP 地址,支撑 loopback + 多 Ethernet 静态地址)

此外 Cargo.toml 中注释保留了 fragmentation-buffer-size / reassembly-buffer-size 等分片/重组能力,但当前未启用。

Router 对 smoltcp 暴露 Medium::Ip,Ethernet frame 处理在 EthernetDevice 中完成。

2. 启动配置模型

启动配置通过 NetworkConfig 传入 init_network()。它描述“哪些设备应该成为哪些接口,以及接口如何获得 IPv4/DNS/metric”。

2.1 网络配置

NetworkConfig 是启动阶段的顶层输入,按接口顺序或匹配规则组织静态地址、DHCP、metric 与 DNS 来源。init_network() 会先校验这一结构再构造全局控制面,因此错误配置必须在发布 SERVICE 前失败,不能留给设备 Worker 运行时猜测。

#[derive(Debug, Clone, Default)]
pub struct NetworkConfig {
pub interfaces: Vec<InterfaceConfig>,
pub default_dns_servers: Vec<Ipv4Addr>,
}

语义:

  • interfaces 是显式接口配置列表。
  • 未显式匹配的 Ethernet 设备按默认策略注册。
  • default_dns_servers 是 fallback DNS 来源,metric 为 u32::MAX
  • lo 固定由 ax-net 创建,不出现在 NetworkConfig 中。

顶层配置列表定义全局 fallback 和接口集合,单接口的名字、匹配与地址策略由 InterfaceConfig 进一步收敛。这样全局默认不会覆盖已经明确配置的设备角色。

2.2 接口配置

InterfaceConfig 描述单个发现设备应采用的名字、匹配条件和 IPv4 策略,并把路由优先级与 DNS 来源绑定到同一接口。该结构用于产生 NetInterfaceRouteTable 初始规则,字段默认值变化会直接改变多网卡启动行为。

#[derive(Debug, Clone)]
pub struct InterfaceConfig {
pub name: String,
pub match_by: InterfaceMatcher,
pub static_ip: Option<StaticIpConfig>,
pub dhcp: bool,
pub metric: u32,
pub dns_servers: Vec<Ipv4Addr>,
}

字段语义:

字段语义
name对外接口名,例如 eth0uplink0
match_by将配置绑定到某个探测到的 Ethernet driver
static_ip静态 IPv4 配置;与 dhcp 互斥
dhcp是否启用 DHCP client
metric接口路由和接口级 DNS 优先级,值越小越优先
dns_servers绑定到该接口的静态 DNS server

字段表说明 InterfaceConfig 把身份匹配、地址策略和路由偏好作为一个接口角色维护。下一节的 InterfaceMatcher 只负责找到设备,不应承载 IP 或 DNS fallback 等配置语义。

2.3 接口匹配

InterfaceMatcher 决定一项接口配置如何关联到实际 driver,可使用发现顺序、MAC 或驱动名等稳定属性。匹配结果必须唯一且可诊断,否则同一配置可能被多个设备消费,或设备落入默认 DHCP 策略而掩盖部署错误。

#[derive(Debug, Clone)]
pub enum InterfaceMatcher {
ByOrder(usize),
ByMac(EthernetAddress),
ByDriverName(String),
}

匹配规则:

  • ByOrder(0) 匹配第一个发现的 Ethernet device。
  • ByMac(mac) 按 MAC 地址匹配。
  • ByDriverName(name) 按 driver 暴露的设备名匹配。
  • 同一设备不能被多个配置匹配。
  • 每个显式配置必须匹配到一个设备。

匹配规则只回答“配置属于哪个设备”,其优先级和唯一性在初始化校验中确定。设备匹配成功后,静态地址结构才决定本地 CIDR、gateway 和 DNS 等网络属性。

2.4 静态地址配置

StaticIpConfig 把 CIDR、可选 gateway 和 DNS server 作为一个完整静态网络角色提交。Router::ipv4_rules() 根据这些字段生成 connected/default route,因而 prefix、gateway 和本地地址必须在初始化校验阶段保持同一子网语义。

#[derive(Debug, Clone)]
pub struct StaticIpConfig {
pub ip: Ipv4Addr,
pub prefix_len: u8,
pub gateway: Ipv4Addr,
}

静态接口初始化会:

  • ip/prefix_len 加入 smoltcp Interface address list。
  • 安装直连路由。
  • 如果 gateway != 0.0.0.0,安装默认路由。
  • dns_servers 记录为 DnsSource::Static

gateway = 0.0.0.0 表示不安装默认路由。

3. 初始化校验

init_network() 对配置执行 fail-fast 校验。启动阶段配置错误直接 panic,避免系统在半初始化网络状态下运行。

3.1 校验规则

初始化校验用于在后台 Worker 启动前拒绝歧义配置,覆盖接口匹配、地址前缀、重复名字和静态路由等约束。下表把输入字段与失败条件对应起来,维护 NetworkConfig::validate() 时应让错误信息保留具体接口和冲突来源。

配置项规则
接口名不能是 lo,不能重复
static_ip + dhcp不能同时启用
静态 IP不能是 0.0.0.0
prefix不能大于 32
gateway可以是 0.0.0.0,表示无默认路由
DNS server不能是 0.0.0.0
matcher每个显式配置必须匹配唯一设备

校验表中的规则都在全局状态发布前执行,因此失败不会留下部分接口或后台 Worker。通过校验后,未命中显式配置的设备才会进入下一节描述的统一默认策略。

3.2 默认策略

未显式匹配 InterfaceConfig 的 Ethernet 设备会落入确定的默认策略,而不是被忽略或猜测静态地址。该策略由初始化配置逻辑统一生成,保证新增普通 NIC 至少能以 DHCP 角色进入接口 registry,并具有可预测的名字和 metric。

  • 名称为 eth{order}
  • InterfaceId = order + 2
  • metric 为 100
  • 默认启用 DHCP。
  • 无静态接口级 DNS。

loopback:

  • 名称为 lo
  • InterfaceId::LOOPBACK == 1
  • 地址为 127.0.0.1/8
  • metric 为 0
  • flags 包含 UP | RUNNING | LOOPBACK

默认策略确保未配置 NIC 仍能进入控制面,但不会凭空提供 fallback DNS 或静态 gateway。DNS 配置必须保留来源和 metric,才能与这些 DHCP 接口的动态 server 正确合并。

4. DNS 配置

DNS server registry 同时接收静态接口配置、DHCP lease 和 fallback 来源,并保留来源接口与 metric。NetControl::dns_servers() 会按路由优先级排序后去重,因此解析顺序与多网卡出口策略一致,而不是简单按配置出现顺序覆盖。

来源创建时机metricinterface_id
DHCPDHCP ACK对应接口 metric对应接口
StaticInterfaceConfig::dns_servers对应接口 metric对应接口
FallbackNetworkConfig::default_dns_serversu32::MAXloopback

对外 dns_servers() 只返回地址列表。DNS 查询时还会过滤不可路由 server:

dns_servers()
-> sort by (metric, interface_id, server_ip)
-> dedup
-> dns_query_timeout()
-> select_route(server) must succeed

调用链说明 server 排序与可达性检查是两个步骤:metric 决定尝试顺序,route lookup 决定某个地址当前是否可用。路由 Metric 同时影响这两类决策以及普通 socket 出口,因而需要统一解释。

5. 路由 Metric

RouteTable 使用前缀长度、metric 和插入顺序形成稳定的选路优先级,socket 查询与 Router::dispatch() 读取同一份规则。配置 metric 时应把数值视为接口出口偏好;它不会替代源地址和 DeviceBinding 对候选路由的过滤。

  1. 最长前缀匹配。
  2. 低 metric 优先。
  3. 同 metric 按插入顺序稳定选择。

每个静态或 DHCP IPv4 接口会生成:

  • 直连路由:interface_cidr -> dev
  • 默认路由:0.0.0.0/0 -> gateway,仅 gateway 存在时安装。

多网口场景下,metric 用于选择默认路由和 DNS server 优先级;socket 已绑定接口时,route lookup 还会叠加 DeviceBinding 过滤。

6. 运行时设备配置

运行期可以注册静态 IPv4 Ethernet 设备,主要用于 Wi-Fi AP 等晚于启动阶段出现的设备。

6.1 设备配置

NetConfig 是运行期静态设备注册使用的窄配置,不等同于启动阶段可表达 DHCP、gateway 和多源 DNS 的 InterfaceConfig。它主要服务 Wi-Fi SoftAP 等已知地址角色,并通过 dedicated_poll 选择是否使用 OOB readiness 模式。

pub struct NetConfig {
pub name: String,
pub ip: [u8; 4],
pub prefix_len: u8,
pub dhcp_server_client_ip: Option<[u8; 4]>,
pub dedicated_poll: bool,
}

该结构没有 gateway、DHCP client 或多地址字段,体现运行期注册 API 的窄用途。动态设备注册会把这些字段转换为静态接口、connected route 与可选 DHCP server,而不是复用完整启动配置解析。

6.2 动态设备注册

register_device_with_config() 在网络服务已初始化后追加设备,随后由 Service::register_static_device() 分配接口 ID、生成 connected route 并启动专属 Worker。这个入口必须把接口快照和 smoltcp 地址同步提交,不能只把 driver 塞入 Router 后再异步补状态。

pub fn register_device_with_config(dev: Box<dyn EthernetDriver>, config: NetConfig);
pub fn wake_net_task_irq();

注册过程:

  • 根据 dedicated_poll 创建普通或 OOB RX EthernetDevice
  • 分配新的 InterfaceId
  • 将静态 IPv4 加入 smoltcp address list。
  • 添加接口 registry、route table 和 worker。
  • dhcp_server_client_ip 存在时启用内置单客户端 DHCP server。
  • 调用 request_poll() 让 net-poll worker 看到新状态。

dedicated_poll = true 时,驱动侧收到 out-of-band RX 事件后调用 wake_net_task_irq()。源码不会创建专门的 OOB poll 线程;该调用通知 NET_IRQ_NOTIFY 并从 IRQ 上下文唤醒 NET_POLL_WAKE,不修改普通 NET_POLL_REQUESTED 位。net-poll worker 看到 IRQ 后调用 wake_all_devices(),对应 RX worker 随后重新检查设备。

6.3 运行期 IPv4 地址

已注册的 Ethernet 接口还可通过 set_interface_ipv4() / remove_interface_ipv4() 修改地址。当前控制面有意保持单地址模型:

  • 每个接口最多一个 IPv4;已有地址时再次设置返回 AlreadyExists
  • 设置操作验证 prefix_len <= 32,关闭该接口 DHCP,安装 IPv4 地址和 connected route。
  • API 没有 gateway 参数,因此不会增加 default route;需要默认网关的场景仍应使用启动期 StaticIpConfig 或 DHCP。
  • 删除必须精确匹配现有 IP/prefix,并会移除该接口的地址、路由、DHCP 状态和 DHCP DNS。

StarryOS 的 RTM_NEWADDR / RTM_DELADDR 直接映射到这两个入口。

7. 资源预算

缓冲区和队列常量集中定义在 consts.rs。这些值共同决定嵌入式目标上的默认内存占用。

7.1 Socket 缓冲区

SOCKET_BUFFER_SIZE 影响 Router 协议侧 packet buffer 以及多个 socket 后端的默认容量,是内存预算和吞吐之间的全局权衡。修改该常量时需要区分字节流缓冲区与 packet metadata 容量,不能仅根据 MTU 线性推断所有队列占用。

pub const TCP_RX_BUF_LEN: usize = 64 * 1024;
pub const TCP_TX_BUF_LEN: usize = 64 * 1024;
pub const UDP_RX_BUF_LEN: usize = 64 * 1024;
pub const UDP_TX_BUF_LEN: usize = 64 * 1024;
pub const RAW_RX_BUF_LEN: usize = 64 * 1024;
pub const RAW_TX_BUF_LEN: usize = 64 * 1024;

这些是每个 socket 的默认协议缓冲区大小。

7.2 设备队列

设备队列常量控制共享 RX queue 和每设备 TX queue 能承受的突发长度,并决定背压出现的位置。RX Worker 在共享队列满时保留本地 batch 重试,而 TX 入队失败会记入 drop,因此两个容量即使数值相同也具有不同的丢包语义。

pub const STANDARD_MTU: usize = 1500;
pub const SOCKET_BUFFER_SIZE: usize = 64;
pub const DEVICE_RX_QUEUE_SIZE: usize = 256;
pub const DEVICE_TX_QUEUE_SIZE: usize = 128;
pub const ETHERNET_MAX_PENDING_PACKETS: usize = 128;
pub const LISTEN_QUEUE_SIZE: usize = 512;

这些常量同时包含 MTU 字节上限、packet slot 数和监听 backlog 上限,不能按同一单位比较。下表把每个值映射到具体所有者和拥塞行为,便于评估修改会增加全局、每设备还是每 listener 的资源占用。

常量含义
STANDARD_MTURouter 和 Ethernet 默认 MTU
SOCKET_BUFFER_SIZERouter RX/TX smoltcp-facing packet buffer 槽位数
DEVICE_RX_QUEUE_SIZE所有真实设备共享的 device-to-Router RX queue 槽位数
DEVICE_TX_QUEUE_SIZE每设备 TX queue 槽位数
ETHERNET_MAX_PENDING_PACKETSARP resolution pending packet 上限
LISTEN_QUEUE_SIZETCP listen backlog clamp 上限

Router RX/TX queue 中的 IP packet 使用 inline [u8; STANDARD_MTU] + len,不为每个 queued packet 分配 Box<[u8]>。Ethernet ARP pending queue 保存二层帧,单槽容量为 STANDARD_MTU + 14;因此估算 pending 内存时不能只按 1500 B 计算。 更完整的拷贝边界、队列满行为和内存预算见内存与队列

7.3 Unix 流缓冲区

Unix stream transport 使用 ringbuf::HeapRb<u8> 作为每个方向的字节流缓冲区,两组 ring 共同组成全双工连接。容量直接影响本地 socket 的背压和每连接堆内存占用,但不占用 smoltcp SocketSet 或 Router packet queue。

const BUF_SIZE: usize = 64 * 1024;

socketpair 两个方向各 64 KiB,总计约 128 KiB 数据缓冲区。

8. 协议默认值

协议默认值分布在 TCP、DHCP、DNS、ARP 和端口分配模块中,控制未显式设置选项时的超时、重试和资源边界。修改这些值会改变用户可见时序或启动行为,因而需要结合对应状态机和集成测试评估,而不能只把它当作常量整理。

8.1 TCP Keepalive

TCP keepalive 默认值由 socket 后端在启用选项时转换为 smoltcp 定时参数,并影响失活连接的探测频率。配置只在真正启用 keepalive 后生效;仅保存用户值而未更新 socket 状态会造成 getsockopt() 与线上行为不一致。

const TCP_KEEPIDLE_DEFAULT_SECS: u32 = 7200;
const TCP_KEEPINTVL_DEFAULT_SECS: u32 = 75;
const TCP_KEEPCNT_DEFAULT: u32 = 9;
const TCP_USER_TIMEOUT_DEFAULT_MS: u32 = 0;

const TCP_KEEPIDLE_MAX_SECS: u32 = 32767;
const TCP_KEEPINTVL_MAX_SECS: u32 = 32767;
const TCP_KEEPCNT_MAX: u32 = 127;

TCP_USER_TIMEOUT_DEFAULT_MS = 0 表示使用协议栈默认策略。

8.2 TCP 状态信息

TCP 状态信息常量用于把 smoltcp 连接状态和计时数据转换为 Linux TCP_INFO 可观察字段。该映射是 ABI 兼容层而非拥塞控制实现,新增字段时需要区分精确来源、近似值和当前无法提供的统计。

const TCP_INFO_DEFAULT_MSS: u32 = 1460;
const TCP_INFO_DEFAULT_PMTU: u32 = 1500;
const TCP_INFO_INITIAL_RTO_MICROS: u32 = 1_000_000;
const TCP_INFO_DEFAULT_REORDERING: u32 = 3;

这些值用于填充 TcpInfo 中无法直接从 smoltcp 获得或需要 Linux 兼容默认值的字段。

8.3 控制协议参数

DHCP、DNS 与 ARP 参数决定控制协议的超时、重试和缓存上限,会同时影响启动等待、运行期恢复及内存占用。下表列出的常量属于行为契约,调整时应结合 Service::poll() 的定时推进和设备 Worker 的兜底唤醒验证。

常量含义
DNS_DEFAULT_TIMEOUT5sdns_query() 默认超时
DHCP_BOOTSTRAP_ATTEMPTS200DHCP bootstrap 最大轮数
DHCP_BOOTSTRAP_POLL_INTERVAL10msDHCP bootstrap 每轮 sleep
DHCP_MAX_RETRY_SHIFT4DHCP 指数退避上限,最大 16s
DHCP_SERVER_LEASE_SECS86400s内置 SoftAP DHCP server 返回的固定租约时间
NEIGHBOR_TTL300sARP neighbor cache TTL
ARP_REQUEST_RETRY1sARP request 重试间隔

控制协议常量共同决定超时与缓存上限,但各自状态机仍拥有独立生命周期。临时端口分配属于 socket 端口仲裁,不受 DHCP、DNS 或 ARP 定时参数影响,因此单独维护其范围与冲突规则。

8.4 临时端口

TCP 和 UDP 的 bind(0) 从 IANA dynamic/private 范围下界开始分配,并通过各自的全局端口仲裁状态避免与现有绑定冲突。分配器必须覆盖 wildcard、具体地址和 reuse 规则,不能只检查 smoltcp socket 当前是否已经建立连接。

const PORT_START: u16 = 0xc000; // 49152
const PORT_END: u16 = 0xffff;

TCP 分配会避开任何已 listen 或已 bind 的同端口;UDP 分配使用 UDP bind side table 检查 wildcard/specific-address 冲突。

9. 配置示例

配置示例展示 NetworkConfig 如何把接口身份、地址来源、metric 和 DNS 组合成可验证的启动输入。示例重点是字段之间的约束和生成的控制面结果,实际部署仍应使用稳定的 MAC 或驱动属性匹配设备,而不是照搬发现顺序。

9.1 双静态网口

双静态网口示例展示如何用不同 metric 建立主备默认出口,同时保留每个接口的 connected route。示例中的匹配器和 DNS 来源应与实际设备稳定属性绑定,避免发现顺序变化后把地址角色交换到另一块网卡。

use alloc::{string::ToString, vec};
use core::net::Ipv4Addr;

use ax_net::{InterfaceConfig, InterfaceMatcher, NetworkConfig, StaticIpConfig};

let config = NetworkConfig {
interfaces: vec![
InterfaceConfig {
name: "eth0".to_string(),
match_by: InterfaceMatcher::ByOrder(0),
static_ip: Some(StaticIpConfig {
ip: Ipv4Addr::new(10, 0, 2, 15),
prefix_len: 24,
gateway: Ipv4Addr::new(10, 0, 2, 2),
}),
dhcp: false,
metric: 100,
dns_servers: vec![Ipv4Addr::new(10, 0, 2, 3)],
},
InterfaceConfig {
name: "eth1".to_string(),
match_by: InterfaceMatcher::ByOrder(1),
static_ip: Some(StaticIpConfig {
ip: Ipv4Addr::new(192, 168, 100, 10),
prefix_len: 24,
gateway: Ipv4Addr::new(192, 168, 100, 1),
}),
dhcp: false,
metric: 200,
dns_servers: vec![],
},
],
default_dns_servers: vec![],
};

该示例最终产生两个 connected route 和按 metric 排序的默认路由/DNS 候选,具体选择仍由共享 RouteTable 在运行时完成。下一示例改用 DHCP 来源,展示动态 DNS 与最低优先级 fallback 的组合。

9.2 DHCP 主接口与备用 DNS

DHCP 主接口可以从 lease 获得地址、gateway 和 DNS,而备用 DNS 仅在动态来源缺失或排序靠后时参与解析。该组合依赖 DnsServerEntry 的来源接口和 metric 排序,因此 fallback 不应被描述为无条件覆盖 DHCP server。

let config = NetworkConfig {
interfaces: vec![InterfaceConfig {
name: "eth0".to_string(),
match_by: InterfaceMatcher::ByOrder(0),
static_ip: None,
dhcp: true,
metric: 100,
dns_servers: vec![],
}],
default_dns_servers: vec![Ipv4Addr::new(8, 8, 8, 8)],
};

DHCP 成功后,动态 server 以接口 metric 排在 fallback 前;没有可用 lease 时,fallback 仍需通过 route lookup 才能使用。配置建议将这类来源与身份约束总结为部署时的检查规则。

10. 配置建议

配置选择应围绕接口身份、路由优先级和资源预算建立明确约束,而不是依赖发现顺序或隐式 fallback。以下建议对应 InterfaceMatcher、route metric、队列常量和运行期配置 API,可作为部署配置评审时的最低检查项。

  • 多网口默认路由通过 metric 控制,主出口使用较小 metric。
  • gateway = 0.0.0.0 用于只有直连路由的静态接口。
  • 需要稳定接口名时优先使用 ByMacByDriverName,避免依赖探测顺序。
  • 通过 ipv4_config(name) 查询指定接口地址,避免固定 eth0 假设。
  • 提高队列常量时应按“每 socket”或“每设备”的乘数估算内存,而不是只看单个 buffer。

这些建议共同要求配置来源可追踪、接口身份稳定且资源变化可量化。若部署需求超出单 IPv4 运行期 API,应先扩展 NetworkConfig 或控制面提交模型,再让 ABI 层调用新的稳定边界。