ax_net/config.rs
1//! Network interface and route configuration types.
2//!
3//! This module is the data model for ax-net's control plane. It is shared by
4//! startup configuration, dynamic IPv4 updates, route table replacement, socket
5//! device binding, DHCP client/server integration, and userspace interface
6//! queries.
7//!
8//! # Design Notes
9//!
10//! `InterfaceId` is stable for the lifetime of the stack and is also exported
11//! as the Linux ifindex. Route and binding structures refer to this identifier
12//! rather than a device vector index so public state survives internal device
13//! ordering details.
14//!
15//! `DeviceBinding` is deliberately small: sockets can bind to an interface, and
16//! the service/router layer performs source-address and next-hop selection from
17//! the route table. Socket implementations should not duplicate route logic.
18
19use alloc::{string::String, vec::Vec};
20use core::net::Ipv4Addr;
21
22use smoltcp::wire::{EthernetAddress, Ipv4Address, Ipv4Cidr};
23
24/// Stable network interface identifier.
25///
26/// The numeric value is also used as the Linux ifindex exposed by StarryOS.
27#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
28pub struct InterfaceId(u32);
29
30impl InterfaceId {
31 pub const LOOPBACK: Self = Self(1);
32
33 pub const fn new(raw: u32) -> Self {
34 Self(raw)
35 }
36
37 pub const fn get(self) -> u32 {
38 self.0
39 }
40
41 /// Convert to Linux ifindex (i32).
42 pub const fn to_linux_ifindex(self) -> i32 {
43 self.0 as i32
44 }
45
46 /// Create from Linux ifindex (i32), rejecting invalid values.
47 pub const fn from_linux_ifindex(ifindex: i32) -> Option<Self> {
48 if ifindex > 0 {
49 Some(Self(ifindex as u32))
50 } else {
51 None
52 }
53 }
54}
55
56/// Network interface kind.
57#[derive(Debug, Clone, Copy, Eq, PartialEq)]
58pub enum InterfaceKind {
59 Loopback,
60 Ethernet,
61}
62
63bitflags::bitflags! {
64 /// Runtime interface flags.
65 #[derive(Debug, Clone, Copy, Eq, PartialEq)]
66 pub struct InterfaceFlags: u32 {
67 const UP = 1 << 0;
68 const RUNNING = 1 << 1;
69 const LOOPBACK = 1 << 2;
70 const BROADCAST = 1 << 3;
71 const MULTICAST = 1 << 4;
72 }
73}
74
75/// Public snapshot of a network interface.
76#[derive(Debug, Clone)]
77pub struct InterfaceInfo {
78 pub id: InterfaceId,
79 pub name: String,
80 pub kind: InterfaceKind,
81 pub mac: Option<EthernetAddress>,
82 pub ipv4: Option<Ipv4InterfaceConfig>,
83 pub mtu: usize,
84 pub flags: InterfaceFlags,
85 pub metric: u32,
86}
87
88/// Interface matching rule for explicit configuration.
89#[derive(Debug, Clone)]
90pub enum InterfaceMatcher {
91 /// Match the Nth probed Ethernet device.
92 ByOrder(usize),
93 /// Match a device by its Ethernet MAC address.
94 ByMac(EthernetAddress),
95 /// Match a device by the name reported by its driver.
96 ByDriverName(String),
97}
98
99/// Network initialization configuration.
100#[derive(Debug, Clone, Default)]
101pub struct NetworkConfig {
102 /// Per-interface configuration.
103 pub interfaces: Vec<InterfaceConfig>,
104 /// DNS servers used when no interface-level DNS server is available.
105 pub default_dns_servers: Vec<Ipv4Addr>,
106}
107
108/// Per-interface network configuration.
109#[derive(Debug, Clone)]
110pub struct InterfaceConfig {
111 /// Public interface name, for example `eth0`.
112 pub name: String,
113 /// Rule used to bind this config to one probed device.
114 pub match_by: InterfaceMatcher,
115 /// Static IPv4 configuration. Mutually exclusive with DHCP.
116 pub static_ip: Option<StaticIpConfig>,
117 /// Whether DHCP client configuration is enabled.
118 pub dhcp: bool,
119 /// Route metric used for routes installed from this interface.
120 pub metric: u32,
121 /// Static DNS servers associated with this interface.
122 pub dns_servers: Vec<Ipv4Addr>,
123}
124
125/// Static IP configuration.
126#[derive(Debug, Clone)]
127pub struct StaticIpConfig {
128 /// IPv4 address assigned to the interface.
129 pub ip: Ipv4Addr,
130 /// CIDR prefix length.
131 pub prefix_len: u8,
132 /// Default gateway; `0.0.0.0` means no gateway.
133 pub gateway: Ipv4Addr,
134}
135
136/// Runtime IPv4 configuration of a network interface.
137#[derive(Debug, Clone, Copy, Eq, PartialEq)]
138pub struct Ipv4InterfaceConfig {
139 /// Interface address and prefix.
140 pub address: Ipv4Cidr,
141 /// Optional default gateway learned or configured for this interface.
142 pub gateway: Option<Ipv4Address>,
143}
144
145/// DNS server origin.
146#[derive(Debug, Clone, Copy, Eq, PartialEq)]
147pub enum DnsSource {
148 /// Learned from DHCP.
149 Dhcp,
150 /// Configured on a matching interface.
151 Static,
152 /// Global fallback DNS server.
153 Fallback,
154}
155
156/// Internal DNS server entry with origin metadata.
157#[derive(Debug, Clone, Copy, Eq, PartialEq)]
158pub(crate) struct DnsServerEntry {
159 /// DNS server address.
160 pub server: Ipv4Address,
161 /// Interface that owns or should route to this server.
162 pub interface_id: InterfaceId,
163 /// Route/DNS priority; lower values are preferred.
164 pub metric: u32,
165 /// Source used for priority and reporting decisions.
166 pub source: DnsSource,
167}
168
169/// Public route snapshot.
170#[derive(Debug, Clone, Copy, Eq, PartialEq)]
171pub struct RouteInfo {
172 /// Destination prefix.
173 pub filter: smoltcp::wire::IpCidr,
174 /// Optional gateway/next hop.
175 pub via: Option<smoltcp::wire::IpAddress>,
176 /// Egress interface.
177 pub interface_id: InterfaceId,
178 /// Source address selected by this route.
179 pub source: smoltcp::wire::IpAddress,
180 /// Route metric; lower values are preferred.
181 pub metric: u32,
182}
183
184/// Ordinary socket interface binding.
185#[derive(Debug, Clone, Copy, Default, Eq, PartialEq)]
186pub struct DeviceBinding {
187 /// If set, route selection is constrained to this interface.
188 pub bound_if: Option<InterfaceId>,
189}