route: metric/preference field (mirrors iproute2 metric)
Rule gains metric: u32 field (default 0). Lower metric = higher priority. lookup_rule() now selects the matching rule with the lowest metric among those with the same prefix length. route/add parser accepts 'metric N': default via 10.0.2.2 metric 100 default via 10.0.2.254 metric 200 Display shows metric when non-zero: default via 10.0.2.2 dev eth0 src 10.0.2.15 metric 100 Rule::with_metric(m) builder added. All existing callers use default metric=0 (backward compatible). Use case: multi-homed hosts with primary/backup default routes. The primary route has metric 0, the backup has metric 100. All 31 tests pass.
This commit is contained in:
@@ -39,6 +39,7 @@ pub struct Rule {
|
||||
pub dev: Rc<str>,
|
||||
pub src: IpAddress,
|
||||
pub route_type: RouteType,
|
||||
pub metric: u32,
|
||||
}
|
||||
|
||||
impl Rule {
|
||||
@@ -49,6 +50,7 @@ impl Rule {
|
||||
dev,
|
||||
src,
|
||||
route_type: RouteType::Unicast,
|
||||
metric: 0,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +61,12 @@ impl Rule {
|
||||
src: IpAddress,
|
||||
route_type: RouteType,
|
||||
) -> Self {
|
||||
Self { filter, via, dev, src, route_type }
|
||||
Self { filter, via, dev, src, route_type, metric: 0 }
|
||||
}
|
||||
|
||||
pub fn with_metric(mut self, metric: u32) -> Self {
|
||||
self.metric = metric;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,6 +88,10 @@ impl Display for Rule {
|
||||
write!(f, " dev {}", self.dev)?;
|
||||
write!(f, " src {}", self.src)?;
|
||||
|
||||
if self.metric != 0 {
|
||||
write!(f, " metric {}", self.metric)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -92,10 +103,15 @@ pub struct RouteTable {
|
||||
|
||||
impl RouteTable {
|
||||
pub fn lookup_rule(&self, dst: &IpAddress) -> Option<&Rule> {
|
||||
// Find the longest-prefix match. Among rules with the same
|
||||
// prefix length, prefer the one with the lowest metric.
|
||||
// Rules are sorted by (prefix_len, metric) — highest prefix
|
||||
// length first, lowest metric first within each prefix.
|
||||
self.rules
|
||||
.iter()
|
||||
.rev()
|
||||
.find(|rule| rule.filter.contains_addr(dst))
|
||||
.filter(|rule| rule.filter.contains_addr(dst))
|
||||
.min_by_key(|rule| rule.metric)
|
||||
}
|
||||
|
||||
pub fn lookup_src_addr(&self, dst: &IpAddress) -> Option<IpAddress> {
|
||||
|
||||
@@ -92,7 +92,20 @@ fn parse_route(value: &str, route_table: &RouteTable) -> SyscallResult<Rule> {
|
||||
.lookup_rule(&via)
|
||||
.ok_or(SyscallError::new(syscall::EINVAL))?;
|
||||
|
||||
Ok(Rule::new(cidr, Some(via), rule.dev.clone(), rule.src))
|
||||
let mut metric: u32 = 0;
|
||||
if let Some(keyword) = parts.next() {
|
||||
if keyword == "metric" {
|
||||
metric = parts
|
||||
.next()
|
||||
.ok_or(SyscallError::new(syscall::EINVAL))?
|
||||
.parse::<u32>()
|
||||
.map_err(|_| SyscallError::new(syscall::EINVAL))?;
|
||||
} else {
|
||||
return Err(SyscallError::new(syscall::EINVAL));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Rule::new(cidr, Some(via), rule.dev.clone(), rule.src).with_metric(metric))
|
||||
}
|
||||
|
||||
fn mk_root_node(
|
||||
|
||||
Reference in New Issue
Block a user