Skip to content

Commit 23fddd5

Browse files
committed
route: add support for MPLS routes and nexthop label stacks
Extend `RouteMessageBuilder` to support the creation of MPLS routes and nexthops with MPLS label stacks. This enables configuration of IP-to-MPLS, MPLS-to-MPLS, and MPLS-to-IP routes. Signed-off-by: Renato Westphal <[email protected]>
1 parent 47ea04c commit 23fddd5

File tree

2 files changed

+155
-1
lines changed

2 files changed

+155
-1
lines changed

examples/add_route_mpls.rs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
use std::env;
4+
5+
use ipnetwork::IpNetwork;
6+
use netlink_packet_route::route::MplsLabel;
7+
use rtnetlink::{new_connection, Error, Handle, RouteMessageBuilder};
8+
9+
#[tokio::main]
10+
async fn main() -> Result<(), ()> {
11+
let args: Vec<String> = env::args().collect();
12+
if args.len() != 4 {
13+
usage();
14+
return Ok(());
15+
}
16+
17+
let input_label = args[1]
18+
.parse::<u32>()
19+
.map(|label| MplsLabel {
20+
label,
21+
traffic_class: 0,
22+
bottom_of_stack: true,
23+
ttl: 0,
24+
})
25+
.unwrap_or_else(|_| {
26+
eprintln!("invalid MPLS input label");
27+
std::process::exit(1);
28+
});
29+
30+
let gateway: IpNetwork = args[2].parse().unwrap_or_else(|_| {
31+
eprintln!("invalid gateway");
32+
std::process::exit(1);
33+
});
34+
35+
let output_label = args[3]
36+
.parse::<u32>()
37+
.map(|label| MplsLabel {
38+
label,
39+
traffic_class: 0,
40+
bottom_of_stack: true,
41+
ttl: 0,
42+
})
43+
.unwrap_or_else(|_| {
44+
eprintln!("invalid MPLS output label");
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_mpls(input_label, &gateway, output_label, handle.clone())
53+
.await
54+
{
55+
eprintln!("{e}");
56+
} else {
57+
println!("Route has been added");
58+
}
59+
Ok(())
60+
}
61+
62+
async fn add_route_mpls(
63+
input_label: MplsLabel,
64+
gateway: &IpNetwork,
65+
output_label: MplsLabel,
66+
handle: Handle,
67+
) -> Result<(), Error> {
68+
let route = RouteMessageBuilder::<MplsLabel>::new()
69+
.label(input_label)
70+
.via(gateway.ip().into())
71+
.output_mpls(vec![output_label])
72+
.build();
73+
handle.route().add(route).execute().await?;
74+
Ok(())
75+
}
76+
77+
fn usage() {
78+
eprintln!(
79+
"\
80+
usage:
81+
cargo run --example add_route_mpls -- <input_label> <gateway> <output_label>
82+
83+
Note that you need to run this program as root:
84+
85+
env CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER='sudo -E' \\
86+
cargo run --example add_route_mpls -- <input_label> <gateway> \
87+
<output_label>"
88+
);
89+
}

src/route/builder.rs

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ use std::{
77

88
use netlink_packet_route::{
99
route::{
10-
RouteAddress, RouteAttribute, RouteFlags, RouteHeader, RouteMessage,
10+
MplsLabel, RouteAddress, RouteAttribute, RouteFlags, RouteHeader,
11+
RouteLwEnCapType, RouteLwTunnelEncap, RouteMessage, RouteMplsIpTunnel,
1112
RouteProtocol, RouteScope, RouteType,
1213
},
1314
AddressFamily,
@@ -52,6 +53,29 @@ impl<T> RouteMessageBuilder<T> {
5253
self
5354
}
5455

56+
/// Sets the output MPLS encapsulation labels.
57+
pub fn output_mpls(mut self, labels: Vec<MplsLabel>) -> Self {
58+
if labels.is_empty() {
59+
return self;
60+
}
61+
if self.message.header.address_family == AddressFamily::Mpls {
62+
self.message
63+
.attributes
64+
.push(RouteAttribute::NewDestination(labels));
65+
} else {
66+
self.message
67+
.attributes
68+
.push(RouteAttribute::EncapType(RouteLwEnCapType::Mpls));
69+
let encap = RouteLwTunnelEncap::Mpls(
70+
RouteMplsIpTunnel::Destination(labels),
71+
);
72+
self.message
73+
.attributes
74+
.push(RouteAttribute::Encap(vec![encap]));
75+
}
76+
self
77+
}
78+
5579
/// Sets the route priority (metric)
5680
pub fn priority(mut self, priority: u32) -> Self {
5781
self.message
@@ -402,3 +426,44 @@ impl Default for RouteMessageBuilder<IpAddr> {
402426
Self::new()
403427
}
404428
}
429+
430+
impl RouteMessageBuilder<MplsLabel> {
431+
/// Create default RouteMessage with header set to:
432+
/// * route: [RouteHeader::RT_TABLE_MAIN]
433+
/// * protocol: [RouteProtocol::Static]
434+
/// * scope: [RouteScope::Universe]
435+
/// * kind: [RouteType::Unicast]
436+
/// * address_family: [AddressFamily::Mpls]
437+
///
438+
/// For using this message in querying routes, these settings
439+
/// are ignored unless `NETLINK_GET_STRICT_CHK` been enabled.
440+
pub fn new() -> Self {
441+
let mut builder = Self::new_no_address_family();
442+
builder.get_mut().header.address_family = AddressFamily::Mpls;
443+
builder
444+
}
445+
446+
/// Sets the destination MPLS label.
447+
pub fn label(mut self, label: MplsLabel) -> Self {
448+
self.message.header.address_family = AddressFamily::Mpls;
449+
self.message.header.destination_prefix_length = 20;
450+
self.message
451+
.attributes
452+
.push(RouteAttribute::Destination(RouteAddress::Mpls(label)));
453+
self
454+
}
455+
456+
/// Sets the gateway (via) address.
457+
pub fn via(mut self, addr: IpAddr) -> Self {
458+
self.message
459+
.attributes
460+
.push(RouteAttribute::Via(addr.into()));
461+
self
462+
}
463+
}
464+
465+
impl Default for RouteMessageBuilder<MplsLabel> {
466+
fn default() -> Self {
467+
Self::new()
468+
}
469+
}

0 commit comments

Comments
 (0)