redbear-netctl: wait for interface to appear before waiting for DHCP address
wait_for_address assumed eth0 already existed and polled for an address with a 1s window -> on a fresh boot (smolnetd/driver-manager bring the interface up async) it spun on a non-existent interface and logged "timed out waiting for DHCP address on eth0". Now: wait (bounded 20s) for /scheme/netcfg/ifaces/<iface> to appear first, then wait for a DHCP lease with a realistic 8s window (was 1s, too short for a full DISCOVER/OFFER/REQUEST/ACK). Both bounded + oneshot_async so boot never blocks. Pairs with the smolnetd/dhcpd async-NIC-attach fix.
This commit is contained in:
@@ -542,7 +542,10 @@ fn dhcp_wait_timeout() -> Duration {
|
||||
.ok()
|
||||
.and_then(|value| value.parse::<u64>().ok())
|
||||
.map(Duration::from_millis)
|
||||
.unwrap_or_else(|| Duration::from_millis(1000))
|
||||
// 8s: a real DHCP DISCOVER/OFFER/REQUEST/ACK handshake over a freshly
|
||||
// brought-up link needs more than the old 1s (which expired before the
|
||||
// lease ever arrived).
|
||||
.unwrap_or_else(|| Duration::from_millis(8000))
|
||||
}
|
||||
|
||||
fn dhcp_poll_interval() -> Duration {
|
||||
@@ -553,10 +556,42 @@ fn dhcp_poll_interval() -> Duration {
|
||||
.unwrap_or_else(|| Duration::from_millis(50))
|
||||
}
|
||||
|
||||
fn iface_appear_timeout() -> Duration {
|
||||
env::var("REDBEAR_NETCTL_IFACE_WAIT_MS")
|
||||
.ok()
|
||||
.and_then(|value| value.parse::<u64>().ok())
|
||||
.map(Duration::from_millis)
|
||||
.unwrap_or_else(|| Duration::from_millis(20_000))
|
||||
}
|
||||
|
||||
fn interface_exists(interface: &str) -> bool {
|
||||
netcfg_root()
|
||||
.join("ifaces")
|
||||
.join(interface)
|
||||
.join("mac")
|
||||
.exists()
|
||||
}
|
||||
|
||||
fn wait_for_address(interface: &str) -> Result<(), String> {
|
||||
let deadline = Instant::now() + dhcp_wait_timeout();
|
||||
let poll = dhcp_poll_interval();
|
||||
|
||||
// The NIC driver + smolnetd bring /scheme/netcfg/ifaces/<iface> up
|
||||
// ASYNCHRONOUSLY (driver-manager attaches drivers after this service
|
||||
// starts), so on a fresh boot the interface may not exist yet. Wait for it
|
||||
// to appear FIRST, then for a DHCP-assigned address. Both bounded so a
|
||||
// NIC-less / no-DHCP machine still returns (this runs oneshot_async, so the
|
||||
// wait never blocks boot/login).
|
||||
let iface_deadline = Instant::now() + iface_appear_timeout();
|
||||
while !interface_exists(interface) {
|
||||
if Instant::now() >= iface_deadline {
|
||||
return Err(format!(
|
||||
"interface {interface} never appeared in /scheme/netcfg (no NIC, or its driver failed to attach)"
|
||||
));
|
||||
}
|
||||
thread::sleep(poll);
|
||||
}
|
||||
|
||||
let deadline = Instant::now() + dhcp_wait_timeout();
|
||||
loop {
|
||||
match current_addr(interface).as_deref() {
|
||||
Some(addr) if addr != "Not configured" && !addr.is_empty() => return Ok(()),
|
||||
|
||||
Reference in New Issue
Block a user