1769b083ab
- init: add condition_path_exists so optional-daemon units (dbus, seatd, thermald, evdevd) are silently skipped when their binary is absent in a minimal image, instead of emitting [FAILED] 'No such file or directory' on the bare target. Boot-critical units omit the field and still hard-fail. - ahcid: an empty ATAPI/optical drive (QEMU's default DVD-ROM, most bare-metal optical bays) failed READ CAPACITY and logged 4 ERROR lines every boot; register it with a zero block count as a normal no-media state and drop the HBA register dump to debug level. - netstack: a machine with no NIC (bare, or an unsupported NIC) logged an ERROR and exited non-zero; treat 'no network adapter' as a normal idle state (info + clean exit). - dhcpd: cut the socket timeout 30s -> 8s so the network stage fails fast when no DHCP server answers instead of stalling boot.
235 lines
7.4 KiB
Rust
235 lines
7.4 KiB
Rust
#[macro_use]
|
|
extern crate log;
|
|
use std::process;
|
|
|
|
use anyhow::{anyhow, Context, Result};
|
|
use event::{EventFlags, EventQueue};
|
|
use libredox::flag::{O_NONBLOCK, O_RDWR};
|
|
use libredox::Fd;
|
|
|
|
use redox_scheme::Socket;
|
|
use scheme::Smolnetd;
|
|
use smoltcp::wire::EthernetAddress;
|
|
|
|
mod buffer_pool;
|
|
mod error;
|
|
mod filter;
|
|
mod icmp_error;
|
|
mod link;
|
|
mod logger;
|
|
mod observer;
|
|
mod port_set;
|
|
mod router;
|
|
mod scheme;
|
|
mod slaac;
|
|
|
|
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; }
|
|
adapters.push(scheme);
|
|
}
|
|
|
|
if adapters.is_empty() {
|
|
// No NIC present is a normal state on a diskless/headless/bare machine,
|
|
// or simply before an unsupported NIC's driver attaches. Report it as
|
|
// info and let the caller exit cleanly instead of emitting a spurious
|
|
// ERROR + non-zero exit on every boot of a NIC-less system.
|
|
info!("no network adapter found; network stack idle");
|
|
return Ok(adapters);
|
|
}
|
|
info!("Found {} network adapter(s): {:?}", adapters.len(), adapters);
|
|
Ok(adapters)
|
|
}
|
|
|
|
fn run(daemon: daemon::Daemon) -> Result<()> {
|
|
let adapters = get_all_network_adapters()?;
|
|
if adapters.is_empty() {
|
|
// Nothing to service; exit cleanly (see get_all_network_adapters).
|
|
return Ok(());
|
|
}
|
|
trace!("found {} network adapter(s)", adapters.len());
|
|
|
|
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()
|
|
.map_err(|e| anyhow!("failed to open create ip scheme socket: {:?}", e))?;
|
|
|
|
trace!("opening udp scheme socket");
|
|
let udp_fd =
|
|
Socket::nonblock().map_err(|e| anyhow!("failed to open udp scheme socket: {:?}", e))?;
|
|
|
|
trace!("opening tcp scheme socket");
|
|
let tcp_fd =
|
|
Socket::nonblock().map_err(|e| anyhow!("failed to open tcp scheme socket: {:?}", e))?;
|
|
|
|
trace!("opening icmp scheme socket");
|
|
let icmp_fd =
|
|
Socket::nonblock().map_err(|e| anyhow!("failed to open icmp scheme socket: {:?}", e))?;
|
|
|
|
trace!("opening netcfg scheme socket");
|
|
let netcfg_fd =
|
|
Socket::nonblock().map_err(|e| anyhow!("failed to open netcfg scheme socket {:?}", e))?;
|
|
|
|
trace!("opening netfilter scheme socket");
|
|
let netfilter_fd =
|
|
Socket::nonblock().map_err(|e| anyhow!("failed to open netfilter scheme socket: {:?}", e))?;
|
|
|
|
trace!("opening tun scheme socket");
|
|
let tun_fd =
|
|
Socket::nonblock().map_err(|e| anyhow!("failed to open tun scheme socket: {:?}", e))?;
|
|
|
|
let time_path = format!("/scheme/time/{}", syscall::CLOCK_MONOTONIC);
|
|
let time_fd = Fd::open(&time_path, O_RDWR, 0).context("failed to open /scheme/time")?;
|
|
|
|
event::user_data! {
|
|
enum EventSource {
|
|
Network,
|
|
Time,
|
|
IpScheme,
|
|
UdpScheme,
|
|
TcpScheme,
|
|
IcmpScheme,
|
|
NetcfgScheme,
|
|
NetfilterScheme,
|
|
TunScheme,
|
|
}
|
|
}
|
|
|
|
let event_queue = EventQueue::<EventSource>::new()
|
|
.map_err(|e| anyhow!("failed to create event queue: {:?}", e))?;
|
|
|
|
daemon.ready();
|
|
|
|
// 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)
|
|
.map_err(|e| anyhow!("failed to listen to timer events: {:?}", e))?;
|
|
|
|
event_queue
|
|
.subscribe(ip_fd.inner().raw(), EventSource::IpScheme, EventFlags::READ)
|
|
.map_err(|e| anyhow!("failed to listen to ip scheme events: {:?}", e))?;
|
|
|
|
event_queue
|
|
.subscribe(
|
|
udp_fd.inner().raw(),
|
|
EventSource::UdpScheme,
|
|
EventFlags::READ,
|
|
)
|
|
.map_err(|e| anyhow!("failed to listen to udp scheme events: {:?}", e))?;
|
|
|
|
event_queue
|
|
.subscribe(
|
|
tcp_fd.inner().raw(),
|
|
EventSource::TcpScheme,
|
|
EventFlags::READ,
|
|
)
|
|
.map_err(|e| anyhow!("failed to listen to tcp scheme events: {:?}", e))?;
|
|
|
|
event_queue
|
|
.subscribe(
|
|
icmp_fd.inner().raw(),
|
|
EventSource::IcmpScheme,
|
|
EventFlags::READ,
|
|
)
|
|
.map_err(|e| anyhow!("failed to listen to icmp scheme events: {:?}", e))?;
|
|
|
|
event_queue
|
|
.subscribe(
|
|
netcfg_fd.inner().raw(),
|
|
EventSource::NetcfgScheme,
|
|
EventFlags::READ,
|
|
)
|
|
.map_err(|e| anyhow!("failed to listen to netcfg scheme events: {:?}", e))?;
|
|
|
|
event_queue
|
|
.subscribe(
|
|
netfilter_fd.inner().raw(),
|
|
EventSource::NetfilterScheme,
|
|
EventFlags::READ,
|
|
)
|
|
.map_err(|e| anyhow!("failed to listen to netfilter scheme events: {:?}", e))?;
|
|
|
|
event_queue
|
|
.subscribe(
|
|
tun_fd.inner().raw(),
|
|
EventSource::TunScheme,
|
|
EventFlags::READ,
|
|
)
|
|
.map_err(|e| anyhow!("failed to listen to tun scheme events: {:?}", e))?;
|
|
|
|
let mut smolnetd = Smolnetd::new(
|
|
network_fds,
|
|
ip_fd,
|
|
udp_fd,
|
|
tcp_fd,
|
|
icmp_fd,
|
|
time_fd,
|
|
netcfg_fd,
|
|
netfilter_fd,
|
|
tun_fd,
|
|
)
|
|
.context("smolnetd: failed to initialize smolnetd")?;
|
|
|
|
libredox::call::setrens(0, 0).context("smolnetd: failed to enter null namespace")?;
|
|
|
|
let all = {
|
|
use EventSource::*;
|
|
[Network, Time, IpScheme, UdpScheme, TcpScheme, IcmpScheme, NetcfgScheme, NetfilterScheme, TunScheme].map(Ok)
|
|
};
|
|
|
|
for event_res in all
|
|
.into_iter()
|
|
.chain(event_queue.map(|r| r.map(|e| e.user_data)))
|
|
{
|
|
match event_res.map_err(|e| anyhow!("event result is error: {:?}", e))? {
|
|
EventSource::Network => smolnetd.on_network_scheme_event(),
|
|
EventSource::Time => smolnetd.on_time_event(),
|
|
EventSource::IpScheme => smolnetd.on_ip_scheme_event(),
|
|
EventSource::UdpScheme => smolnetd.on_udp_scheme_event(),
|
|
EventSource::TcpScheme => smolnetd.on_tcp_scheme_event(),
|
|
EventSource::IcmpScheme => smolnetd.on_icmp_scheme_event(),
|
|
EventSource::NetcfgScheme => smolnetd.on_netcfg_scheme_event(),
|
|
EventSource::NetfilterScheme => smolnetd.on_netfilter_scheme_event(),
|
|
EventSource::TunScheme => smolnetd.on_tun_scheme_event(),
|
|
}
|
|
.map_err(|e| error!("Received packet error: {:?}", e));
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn main() {
|
|
daemon::Daemon::new(daemon_runner);
|
|
}
|
|
|
|
fn daemon_runner(daemon: daemon::Daemon) -> ! {
|
|
logger::init_logger("smolnetd");
|
|
|
|
if let Err(err) = run(daemon) {
|
|
error!("smoltcpd: {}", err);
|
|
process::exit(1);
|
|
}
|
|
process::exit(0);
|
|
}
|