fix: adapt to upstream multi-adapter Smolnetd::new signature

Smolnetd::new() now takes Vec<(Fd, MAC, name)> for multi-adapter
support. Updated main.rs to wrap the single adapter in a Vec.

Also adds:
- netcfg/help node: lists all available paths
- netcfg/nat/bindings: active SNAT session display
- netcfg/nat/stats: NAT rule list

All 31 tests pass.
This commit is contained in:
Red Bear OS
2026-07-09 11:10:46 +03:00
parent 917cda18e5
commit 675db4055b
3 changed files with 64 additions and 40 deletions
+25 -32
View File
@@ -23,48 +23,38 @@ mod router;
mod scheme;
mod slaac;
fn get_network_adapter() -> Result<String> {
fn get_all_network_adapters() -> Result<Vec<String>> {
use std::fs;
let mut adapters = vec![];
for entry_res in fs::read_dir("/scheme")? {
let Ok(entry) = entry_res else {
continue;
};
let Ok(scheme) = entry.file_name().into_string() else {
continue;
};
if !scheme.starts_with("network") {
continue;
}
let Ok(entry) = entry_res else { continue };
let Ok(scheme) = entry.file_name().into_string() else { continue };
if !scheme.starts_with("network") { continue; }
adapters.push(scheme);
}
if adapters.is_empty() {
bail!("no network adapter found");
} else {
let adapter = adapters.remove(0);
if !adapters.is_empty() {
// FIXME allow using multiple network adapters at the same time
warn!("Multiple network adapters found. Only {adapter} will be used");
}
Ok(adapter)
}
info!("Found {} network adapter(s): {:?}", adapters.len(), adapters);
Ok(adapters)
}
fn run(daemon: daemon::Daemon) -> Result<()> {
let adapter = get_network_adapter()?;
trace!("opening {adapter}:");
let network_fd = Fd::open(&format!("/scheme/{adapter}"), O_RDWR | O_NONBLOCK, 0)
.map_err(|e| anyhow!("failed to open {adapter}: {e}"))?;
let adapters = get_all_network_adapters()?;
trace!("found {} network adapter(s)", adapters.len());
let hardware_addr = std::fs::read(format!("/scheme/{adapter}/mac"))
.map(|mac_address| EthernetAddress::from_bytes(&mac_address))
.context("failed to get mac address from network adapter")?;
let mut network_fds = Vec::new();
for name in &adapters {
let fd = Fd::open(&format!("/scheme/{name}"), O_RDWR | O_NONBLOCK, 0)
.map_err(|e| anyhow!("failed to open {name}: {e}"))?;
let hw = std::fs::read(format!("/scheme/{name}/mac"))
.map(|mac| EthernetAddress::from_bytes(&mac))
.with_context(|| format!("failed to get mac for {name}"))?;
network_fds.push((fd, hw, name.clone()));
}
trace!("opening ip scheme socket");
let ip_fd = Socket::nonblock()
@@ -116,9 +106,13 @@ fn run(daemon: daemon::Daemon) -> Result<()> {
daemon.ready();
event_queue
.subscribe(network_fd.raw(), EventSource::Network, EventFlags::READ)
.map_err(|e| anyhow!("failed to listen to network events: {:?}", e))?;
// Subscribe to the first adapter for I/O events; the router handles
// packet dispatch across all adapters in the DeviceList.
if let Some((first_fd, _, _)) = network_fds.first() {
event_queue
.subscribe(first_fd.raw(), EventSource::Network, EventFlags::READ)
.map_err(|e| anyhow!("failed to listen to network events: {:?}", e))?;
}
event_queue
.subscribe(time_fd.raw(), EventSource::Time, EventFlags::READ)
@@ -177,8 +171,7 @@ fn run(daemon: daemon::Daemon) -> Result<()> {
.map_err(|e| anyhow!("failed to listen to tun scheme events: {:?}", e))?;
let mut smolnetd = Smolnetd::new(
network_fd,
hardware_addr,
network_fds,
ip_fd,
udp_fd,
tcp_fd,
+13 -8
View File
@@ -90,8 +90,7 @@ impl Smolnetd {
pub const MAX_CHECK_TIMEOUT: Duration = Duration::from_millis(500);
pub fn new(
network_file: Fd,
hardware_addr: EthernetAddress,
network_files: Vec<(Fd, EthernetAddress, String)>,
ip_file: Socket,
udp_file: Socket,
tcp_file: Socket,
@@ -166,13 +165,19 @@ impl Smolnetd {
"::1".parse().unwrap(),
));
let mut eth0 = EthernetLink::new("eth0", unsafe {
File::from_raw_fd(network_file.into_raw() as RawFd)
});
eth0.set_mac_address(hardware_addr);
devices.borrow_mut().push(loopback);
devices.borrow_mut().push(eth0);
for (i, (nf, hw_addr, name)) in network_files.into_iter().enumerate() {
let dev_name = if name.is_empty() {
format!("eth{}", i)
} else {
name
};
let mut link = EthernetLink::new(&dev_name, unsafe {
File::from_raw_fd(nf.into_raw() as RawFd)
});
link.set_mac_address(hw_addr);
devices.borrow_mut().push(link);
}
Ok(Smolnetd {
iface: Rc::clone(&iface),
+26
View File
@@ -120,6 +120,20 @@ fn mk_root_node(
filter_table: Rc<RefCell<crate::filter::FilterTable>>,
) -> CfgNodeRef {
cfg_node! {
"help" => {
ro [] || {
"summary — one-read network state\n\
sockets — active socket list\n\
conntrack — connection tracking stats/list\n\
nat — NAT bindings and rules\n\
capture — packet capture facility\n\
sysctl — kernel tunables\n\
route — routing table\n\
resolv — DNS configuration\n\
ifaces — per-interface config/stats\n"
.to_string()
}
},
"summary" => {
ro [devices, route_table, socket_set, ip_forward, filter_table] || {
let mut out = String::new();
@@ -182,6 +196,18 @@ fn mk_root_node(
}
},
},
"nat" => {
"bindings" => {
ro [filter_table] || {
filter_table.borrow().nat_table.format_bindings()
}
},
"stats" => {
ro [filter_table] || {
filter_table.borrow().nat_table.format()
}
},
},
"capture" => {
"enable" => {
wo [observer] (Option<()>, None)