Skip to content

Commit 7fcba62

Browse files
committed
route: Support defining ECMP weight
Introduced these functions: * `RouteNextHopBuilder::new_ipv4()` * `RouteNextHopBuilder::new_ipv6()` * `RouteNextHopBuilder::weight()` Example code included. Signed-off-by: Gris Ge <[email protected]>
1 parent e1184c0 commit 7fcba62

File tree

2 files changed

+132
-0
lines changed

2 files changed

+132
-0
lines changed

examples/add_route_ecmp.rs

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
use std::{env, net::Ipv4Addr};
4+
5+
use ipnetwork::Ipv4Network;
6+
use rtnetlink::{
7+
new_connection, Error, Handle, RouteMessageBuilder, RouteNextHopBuilder,
8+
};
9+
10+
#[tokio::main]
11+
async fn main() -> Result<(), ()> {
12+
let args: Vec<String> = env::args().collect();
13+
if args.len() != 7 {
14+
usage();
15+
return Ok(());
16+
}
17+
18+
let iface_index = args[1].parse::<u32>().unwrap_or_else(|_| {
19+
eprintln!("invalid interface index");
20+
std::process::exit(1);
21+
});
22+
23+
let dst = args[2].parse::<Ipv4Network>().unwrap_or_else(|_| {
24+
eprintln!("invalid destination");
25+
std::process::exit(1);
26+
});
27+
28+
let via1 = args[3].parse::<Ipv4Addr>().unwrap_or_else(|_| {
29+
eprintln!("invalid via");
30+
std::process::exit(1);
31+
});
32+
33+
let weight1 = args[4].parse::<u8>().unwrap_or_else(|_| {
34+
eprintln!("invalid weight");
35+
std::process::exit(1);
36+
});
37+
38+
let via2 = args[5].parse::<Ipv4Addr>().unwrap_or_else(|_| {
39+
eprintln!("invalid via");
40+
std::process::exit(1);
41+
});
42+
43+
let weight2 = args[6].parse::<u8>().unwrap_or_else(|_| {
44+
eprintln!("invalid weight");
45+
std::process::exit(1);
46+
});
47+
48+
let (connection, handle, _) = new_connection().unwrap();
49+
tokio::spawn(connection);
50+
51+
if let Err(e) =
52+
add_route_ecmp(iface_index, &dst, via1, weight1, via2, weight2, handle)
53+
.await
54+
{
55+
eprintln!("{e}");
56+
} else {
57+
println!("Route has been added");
58+
}
59+
Ok(())
60+
}
61+
62+
async fn add_route_ecmp(
63+
iface_index: u32,
64+
dst: &Ipv4Network,
65+
via1: Ipv4Addr,
66+
weight1: u8,
67+
via2: Ipv4Addr,
68+
weight2: u8,
69+
handle: Handle,
70+
) -> Result<(), Error> {
71+
let route = RouteMessageBuilder::<Ipv4Addr>::new()
72+
.destination_prefix(dst.ip(), dst.prefix())
73+
.multipath(vec![
74+
RouteNextHopBuilder::new_ipv4()
75+
.interface(iface_index)
76+
.weight(weight1)
77+
.via(via1.into())
78+
.unwrap()
79+
.build(),
80+
RouteNextHopBuilder::new_ipv4()
81+
.interface(iface_index)
82+
.weight(weight2)
83+
.via(via2.into())
84+
.unwrap()
85+
.build(),
86+
])
87+
.build();
88+
handle.route().add(route).execute().await?;
89+
Ok(())
90+
}
91+
92+
fn usage() {
93+
eprintln!(
94+
"\
95+
usage:
96+
cargo run --example add_route_ecmp -- <iface_index> <dst> <via> <weight> \\
97+
<via> <weight>
98+
99+
Note that you need to run this program as root:
100+
101+
env CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER='sudo' \\
102+
cargo run --example add_route_ecmp -- <iface_index> \\
103+
<dst> <via> <weight> <via> <weight>"
104+
);
105+
}

src/route/builder.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,22 @@ impl RouteNextHopBuilder {
498498
}
499499
}
500500

501+
/// Create IPv4 RouteNexthop
502+
pub fn new_ipv4() -> Self {
503+
Self {
504+
address_family: AddressFamily::Inet,
505+
nexthop: Default::default(),
506+
}
507+
}
508+
509+
/// Create IPv6 RouteNexthop
510+
pub fn new_ipv6() -> Self {
511+
Self {
512+
address_family: AddressFamily::Inet6,
513+
nexthop: Default::default(),
514+
}
515+
}
516+
501517
/// Sets the nexthop interface index.
502518
pub fn interface(mut self, index: u32) -> Self {
503519
self.nexthop.interface_index = index;
@@ -555,6 +571,17 @@ impl RouteNextHopBuilder {
555571
self
556572
}
557573

574+
/// Set the nexthop weight
575+
///
576+
/// Equal to `weight` property in `ip route`, but please be advised the
577+
/// number shown in `ip route` command has plus 1. Meaning kernel has
578+
/// `weight 0`, but `ip route` shows as `weight 1`. This function is using
579+
/// kernel number from range of 0 to 255.
580+
pub fn weight(mut self, weight: u8) -> Self {
581+
self.nexthop.hops = weight;
582+
self
583+
}
584+
558585
pub fn build(self) -> RouteNextHop {
559586
self.nexthop
560587
}

0 commit comments

Comments
 (0)