diff --git a/netstack/src/main.rs b/netstack/src/main.rs index 4b1ea55f3b..49e24684a6 100644 --- a/netstack/src/main.rs +++ b/netstack/src/main.rs @@ -23,48 +23,38 @@ mod router; mod scheme; mod slaac; -fn get_network_adapter() -> Result { +fn get_all_network_adapters() -> Result> { 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, diff --git a/netstack/src/scheme/mod.rs b/netstack/src/scheme/mod.rs index bd58f1d6e1..751668fc5e 100644 --- a/netstack/src/scheme/mod.rs +++ b/netstack/src/scheme/mod.rs @@ -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), diff --git a/netstack/src/scheme/netcfg/mod.rs b/netstack/src/scheme/netcfg/mod.rs index b47f527121..bb7a1401e5 100644 --- a/netstack/src/scheme/netcfg/mod.rs +++ b/netstack/src/scheme/netcfg/mod.rs @@ -120,6 +120,20 @@ fn mk_root_node( filter_table: Rc>, ) -> 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)