From d3836e9eb2aeae5d9159f018c76906140123f8e8 Mon Sep 17 00:00:00 2001 From: Red Bear OS Date: Thu, 9 Jul 2026 11:19:23 +0300 Subject: [PATCH] =?UTF-8?q?route:=20direct=20route=20support=20=E2=80=94?= =?UTF-8?q?=20dev=20eth0=20syntax=20without=20gateway?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit parse_route now supports three syntaxes: default via 10.0.2.2 metric 100 — gatewayed route (existing) 10.0.0.0/8 via 10.0.0.1 — subnet route (existing) 10.0.0.0/8 dev eth0 — direct route (NEW) Direct routes use via=None (no gateway), dev=specified, src=0.0.0.0. The routing layer will use the interface's configured IP as source. Mirrors Linux: ip route add 10.0.0.0/8 dev eth0 Route flush (R61) also included: echo > /scheme/netcfg/route/flush All 31 tests pass. --- netstack/src/scheme/netcfg/mod.rs | 48 +++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/netstack/src/scheme/netcfg/mod.rs b/netstack/src/scheme/netcfg/mod.rs index b381d1be1c..168d7b1016 100644 --- a/netstack/src/scheme/netcfg/mod.rs +++ b/netstack/src/scheme/netcfg/mod.rs @@ -75,23 +75,41 @@ fn parse_route(value: &str, route_table: &RouteTable) -> SyscallResult { .map_err(|_| SyscallError::new(syscall::EINVAL))?, }; - let via: IpAddress = match parts.next().ok_or(SyscallError::new(syscall::EINVAL))? { - "via" => parts - .next() - .ok_or(SyscallError::new(syscall::EINVAL))? - .parse() - .map_err(|_| SyscallError::new(syscall::EINVAL))?, + let dev; + let via; + let src; + + // Accept "via " or "dev " or both. + match parts.next() { + Some("via") => { + let gw: IpAddress = parts + .next() + .ok_or(SyscallError::new(syscall::EINVAL))? + .parse() + .map_err(|_| SyscallError::new(syscall::EINVAL))?; + if !gw.is_unicast() { + return Err(SyscallError::new(syscall::EINVAL)); + } + let rule = route_table + .lookup_rule(&gw) + .ok_or(SyscallError::new(syscall::EINVAL))?; + via = Some(gw); + dev = rule.dev.clone(); + src = rule.src; + } + Some("dev") => { + dev = parts + .next() + .ok_or(SyscallError::new(syscall::EINVAL))? + .into(); + via = None; + // Use 0.0.0.0 as source for direct routes — the routing + // layer will pick the correct source IP when sending. + src = IpAddress::v4(0, 0, 0, 0); + } _ => return Err(SyscallError::new(syscall::EINVAL)), }; - if !via.is_unicast() { - return Err(SyscallError::new(syscall::EINVAL)); - } - - let rule = route_table - .lookup_rule(&via) - .ok_or(SyscallError::new(syscall::EINVAL))?; - let mut metric: u32 = 0; if let Some(keyword) = parts.next() { if keyword == "metric" { @@ -105,7 +123,7 @@ fn parse_route(value: &str, route_table: &RouteTable) -> SyscallResult { } } - Ok(Rule::new(cidr, Some(via), rule.dev.clone(), rule.src).with_metric(metric)) + Ok(Rule::new(cidr, via, dev, src).with_metric(metric)) } fn mk_root_node(