From 16c73f444102dd430de9b082bf825b3a7d4a02f7 Mon Sep 17 00:00:00 2001 From: Aperence Date: Mon, 20 Apr 2026 08:53:31 +0200 Subject: [PATCH] Add API for Seg6 Encapsulation Following https://github.com/rust-netlink/netlink-packet-route/pull/227, we can now create Seg6Header outside of the netlink-packet-route crate. This commit adds a new `output_seg6` function to RouteMessageBuilder, allowing to easily create new routes with SRv6 encapsulation. An example of route creation is present in `examples/`. Signed-off-by: Aperence --- examples/add_route_seg6.rs | 89 ++++++++++++++++++++++++++++++++++++++ src/route/builder.rs | 28 ++++++++++++ 2 files changed, 117 insertions(+) create mode 100644 examples/add_route_seg6.rs diff --git a/examples/add_route_seg6.rs b/examples/add_route_seg6.rs new file mode 100644 index 0000000..35b640f --- /dev/null +++ b/examples/add_route_seg6.rs @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: MIT + +use std::{ + env, + net::{AddrParseError, Ipv6Addr}, +}; + +use ipnetwork::Ipv6Network; +use netlink_packet_route::route::Seg6Mode; +use rtnetlink::{new_connection, Error, Handle, RouteMessageBuilder}; + +#[tokio::main] +async fn main() -> Result<(), ()> { + let args: Vec = env::args().collect(); + if args.len() < 5 { + usage(); + return Ok(()); + } + + let dst = args[1].parse::().unwrap_or_else(|_| { + eprintln!("invalid destination address"); + std::process::exit(1); + }); + + let gateway: Ipv6Addr = args[2].parse().unwrap_or_else(|_| { + eprintln!("invalid gateway"); + std::process::exit(1); + }); + + let mode: Seg6Mode = match args[3].as_str() { + "inline" => Seg6Mode::Inline, + "encap" => Seg6Mode::Encap, + _ => { + eprintln!("invalid SRv6 mode"); + std::process::exit(1); + } + }; + + let segments = args[4..] + .iter() + .map(|seg| seg.parse::()) + .collect::, AddrParseError>>() + .unwrap_or_else(|_| { + eprintln!("invalid SRv6 segments"); + std::process::exit(1); + }); + + let (connection, handle, _) = new_connection().unwrap(); + tokio::spawn(connection); + + if let Err(e) = + add_route_seg6(&dst, &gateway, mode, segments, handle.clone()).await + { + eprintln!("{e}"); + } else { + println!("Route has been added"); + } + Ok(()) +} + +async fn add_route_seg6( + dst: &Ipv6Network, + gateway: &Ipv6Addr, + mode: Seg6Mode, + segments: Vec, + handle: Handle, +) -> Result<(), Error> { + let route = RouteMessageBuilder::::new() + .destination_prefix(dst.ip(), dst.prefix()) + .gateway(*gateway) + .output_seg6(mode, segments) + .build(); + handle.route().add(route).execute().await?; + Ok(()) +} + +fn usage() { + eprintln!( + "\ +usage: + cargo run --example add_route_seg6 -- + +Note that you need to run this program as root: + + env CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER='sudo -E' \\ + cargo run --example add_route_seg6 -- \ + " + ); +} diff --git a/src/route/builder.rs b/src/route/builder.rs index 9a97734..2a92444 100644 --- a/src/route/builder.rs +++ b/src/route/builder.rs @@ -8,6 +8,7 @@ use std::{ #[cfg(not(target_os = "android"))] use netlink_packet_route::route::{ MplsLabel, RouteLwEnCapType, RouteLwTunnelEncap, RouteMplsIpTunnel, + RouteSeg6IpTunnel, Seg6Header, Seg6Mode, }; use netlink_packet_route::{ route::{ @@ -87,6 +88,33 @@ impl RouteMessageBuilder { self } + /// Sets the output SRv6 encapsulation segments. + #[cfg(not(target_os = "android"))] + pub fn output_seg6( + mut self, + mode: Seg6Mode, + segments: Vec, + ) -> Self { + if segments.is_empty() { + return self; + } + self.message + .attributes + .push(RouteAttribute::EncapType(RouteLwEnCapType::Seg6)); + + let mut header = Seg6Header::default(); + header.mode = mode; + header.segments = segments; + + let encap = RouteLwTunnelEncap::Seg6(RouteSeg6IpTunnel::Seg6(header)); + + self.message + .attributes + .push(RouteAttribute::Encap(vec![encap])); + + self + } + /// Sets multiple nexthop entries for the route. pub fn multipath(mut self, nexthops: Vec) -> Self { self.message