From df281a38b3a69068ef7d6bb06989be0d47e2ffdc Mon Sep 17 00:00:00 2001 From: Gris Ge Date: Fri, 18 Sep 2026 15:01:09 +0800 Subject: [PATCH] addrlabel: add `AddrLabelHandle` Provide `add()`, `del()` and `get()` requests sending `RTM_NEWADDRLABEL`, `RTM_DELADDRLABEL` and `RTM_GETADDRLABEL` netlink messages, equivalent to the `ip addrlabel` commands. Signed-off-by: Gris Ge --- src/addrlabel/add.rs | 89 +++++++++++++++++++++++++++++++++++++++++ src/addrlabel/del.rs | 86 +++++++++++++++++++++++++++++++++++++++ src/addrlabel/get.rs | 52 ++++++++++++++++++++++++ src/addrlabel/handle.rs | 30 ++++++++++++++ src/addrlabel/mod.rs | 11 +++++ src/handle.rs | 9 ++++- src/lib.rs | 5 +++ 7 files changed, 281 insertions(+), 1 deletion(-) create mode 100644 src/addrlabel/add.rs create mode 100644 src/addrlabel/del.rs create mode 100644 src/addrlabel/get.rs create mode 100644 src/addrlabel/handle.rs create mode 100644 src/addrlabel/mod.rs diff --git a/src/addrlabel/add.rs b/src/addrlabel/add.rs new file mode 100644 index 00000000..4deef7d9 --- /dev/null +++ b/src/addrlabel/add.rs @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: MIT + +use std::net::IpAddr; + +use futures_util::stream::StreamExt; +use netlink_packet_core::{ + NetlinkMessage, NLM_F_ACK, NLM_F_CREATE, NLM_F_EXCL, NLM_F_REQUEST, +}; +use netlink_packet_route::{ + addrlabel::{AddrLabelAttribute, AddrLabelMessage}, + AddressFamily, RouteNetlinkMessage, +}; + +use crate::{try_nl, Error, Handle}; + +/// A request to create a new address label. This is equivalent to the +/// `ip addrlabel add` command. +#[derive(Debug, Clone)] +pub struct AddrLabelAddRequest { + handle: Handle, + message: AddrLabelMessage, +} + +impl AddrLabelAddRequest { + pub(crate) fn new(handle: Handle) -> Self { + let mut message = AddrLabelMessage::default(); + // Kernel only registers `RTM_NEWADDRLABEL` handler for the IPv6 + // address family, hence default to IPv6 like iproute2 does. + message.header.family = AddressFamily::Inet6; + AddrLabelAddRequest { handle, message } + } + + /// Sets the address family of the address label. + pub fn family(mut self, family: AddressFamily) -> Self { + self.message.header.family = family; + self + } + + /// Sets the address of the address label. + pub fn address(mut self, address: IpAddr) -> Self { + self.message + .attributes + .push(AddrLabelAttribute::Address(address)); + self + } + + /// Sets the prefix length of the address label. + pub fn prefix_len(mut self, prefix_len: u8) -> Self { + self.message.header.prefix_len = prefix_len; + self + } + + /// Sets the interface index of the address label. + pub fn index(mut self, index: u32) -> Self { + self.message.header.index = index; + self + } + + /// Sets the label of the address label. + pub fn label(mut self, label: u32) -> Self { + self.message + .attributes + .push(AddrLabelAttribute::Label(label)); + self + } + + /// Execute the request. + pub async fn execute(self) -> Result<(), Error> { + let AddrLabelAddRequest { + mut handle, + message, + } = self; + let mut req = + NetlinkMessage::from(RouteNetlinkMessage::NewAddrLabel(message)); + req.header.flags = + NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL; + + let mut response = handle.request(req)?; + while let Some(message) = response.next().await { + try_nl!(message); + } + + Ok(()) + } + + pub fn message_mut(&mut self) -> &mut AddrLabelMessage { + &mut self.message + } +} diff --git a/src/addrlabel/del.rs b/src/addrlabel/del.rs new file mode 100644 index 00000000..5f236562 --- /dev/null +++ b/src/addrlabel/del.rs @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: MIT + +use std::net::IpAddr; + +use futures_util::stream::StreamExt; +use netlink_packet_core::{NetlinkMessage, NLM_F_ACK, NLM_F_REQUEST}; +use netlink_packet_route::{ + addrlabel::{AddrLabelAttribute, AddrLabelMessage}, + AddressFamily, RouteNetlinkMessage, +}; + +use crate::{try_nl, Error, Handle}; + +/// A request to delete an existing address label. This is equivalent to the +/// `ip addrlabel del` command. +#[derive(Debug, Clone)] +pub struct AddrLabelDelRequest { + handle: Handle, + message: AddrLabelMessage, +} + +impl AddrLabelDelRequest { + pub(crate) fn new(handle: Handle) -> Self { + let mut message = AddrLabelMessage::default(); + // Kernel only registers `RTM_DELADDRLABEL` handler for the IPv6 + // address family, hence default to IPv6 like iproute2 does. + message.header.family = AddressFamily::Inet6; + AddrLabelDelRequest { handle, message } + } + + /// Sets the address family of the address label. + pub fn family(mut self, family: AddressFamily) -> Self { + self.message.header.family = family; + self + } + + /// Sets the address of the address label. + pub fn address(mut self, address: IpAddr) -> Self { + self.message + .attributes + .push(AddrLabelAttribute::Address(address)); + self + } + + /// Sets the prefix length of the address label. + pub fn prefix_len(mut self, prefix_len: u8) -> Self { + self.message.header.prefix_len = prefix_len; + self + } + + /// Sets the interface index of the address label. + pub fn index(mut self, index: u32) -> Self { + self.message.header.index = index; + self + } + + /// Sets the label of the address label. + pub fn label(mut self, label: u32) -> Self { + self.message + .attributes + .push(AddrLabelAttribute::Label(label)); + self + } + + /// Execute the request. + pub async fn execute(self) -> Result<(), Error> { + let AddrLabelDelRequest { + mut handle, + message, + } = self; + let mut req = + NetlinkMessage::from(RouteNetlinkMessage::DelAddrLabel(message)); + req.header.flags = NLM_F_REQUEST | NLM_F_ACK; + + let mut response = handle.request(req)?; + while let Some(message) = response.next().await { + try_nl!(message); + } + + Ok(()) + } + + pub fn message_mut(&mut self) -> &mut AddrLabelMessage { + &mut self.message + } +} diff --git a/src/addrlabel/get.rs b/src/addrlabel/get.rs new file mode 100644 index 00000000..0eb34ef3 --- /dev/null +++ b/src/addrlabel/get.rs @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: MIT + +use futures_util::{ + future::{self, Either}, + stream::{Stream, StreamExt}, + FutureExt, +}; +use netlink_packet_core::{NetlinkMessage, NLM_F_DUMP, NLM_F_REQUEST}; +use netlink_packet_route::{addrlabel::AddrLabelMessage, RouteNetlinkMessage}; + +use crate::{try_rtnl, Error, Handle, IpVersion}; + +#[derive(Debug, Clone)] +pub struct AddrLabelGetRequest { + handle: Handle, + message: AddrLabelMessage, +} + +impl AddrLabelGetRequest { + pub(crate) fn new(handle: Handle, ip_version: IpVersion) -> Self { + let mut message = AddrLabelMessage::default(); + message.header.family = ip_version.family(); + AddrLabelGetRequest { handle, message } + } + + pub fn message_mut(&mut self) -> &mut AddrLabelMessage { + &mut self.message + } + + /// Execute the request + pub fn execute( + self, + ) -> impl Stream> { + let AddrLabelGetRequest { + mut handle, + message, + } = self; + + let mut req = + NetlinkMessage::from(RouteNetlinkMessage::GetAddrLabel(message)); + req.header.flags = NLM_F_REQUEST | NLM_F_DUMP; + + match handle.request(req) { + Ok(response) => Either::Left(response.map(move |msg| { + Ok(try_rtnl!(msg, RouteNetlinkMessage::NewAddrLabel)) + })), + Err(e) => Either::Right( + future::err::(e).into_stream(), + ), + } + } +} diff --git a/src/addrlabel/handle.rs b/src/addrlabel/handle.rs new file mode 100644 index 00000000..bf361476 --- /dev/null +++ b/src/addrlabel/handle.rs @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT + +use crate::{ + AddrLabelAddRequest, AddrLabelDelRequest, AddrLabelGetRequest, Handle, + IpVersion, +}; + +#[derive(Debug, Clone)] +pub struct AddrLabelHandle(Handle); + +impl AddrLabelHandle { + pub fn new(handle: Handle) -> Self { + AddrLabelHandle(handle) + } + + /// List address labels (equivalent to `ip addrlabel show`) + pub fn get(&self, ip_version: IpVersion) -> AddrLabelGetRequest { + AddrLabelGetRequest::new(self.0.clone(), ip_version) + } + + /// Add an address label (equivalent to `ip addrlabel add`) + pub fn add(&self) -> AddrLabelAddRequest { + AddrLabelAddRequest::new(self.0.clone()) + } + + /// Delete an address label (equivalent to `ip addrlabel del`) + pub fn del(&self) -> AddrLabelDelRequest { + AddrLabelDelRequest::new(self.0.clone()) + } +} diff --git a/src/addrlabel/mod.rs b/src/addrlabel/mod.rs new file mode 100644 index 00000000..ab97fcd2 --- /dev/null +++ b/src/addrlabel/mod.rs @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT + +mod add; +mod del; +mod get; +mod handle; + +pub use self::{ + add::AddrLabelAddRequest, del::AddrLabelDelRequest, + get::AddrLabelGetRequest, handle::AddrLabelHandle, +}; diff --git a/src/handle.rs b/src/handle.rs index d320cdfe..867a41f5 100644 --- a/src/handle.rs +++ b/src/handle.rs @@ -6,7 +6,8 @@ use netlink_packet_route::RouteNetlinkMessage; use netlink_proto::{sys::SocketAddr, ConnectionHandle}; use crate::{ - AddressHandle, Error, LinkHandle, NeighbourHandle, RouteHandle, RuleHandle, + AddrLabelHandle, AddressHandle, Error, LinkHandle, NeighbourHandle, + RouteHandle, RuleHandle, }; #[cfg(not(target_os = "freebsd"))] use crate::{ @@ -53,6 +54,12 @@ impl Handle { AddressHandle::new(self.clone()) } + /// Create a new handle, specifically for address label requests + /// (equivalent to `ip addrlabel` commands) + pub fn addrlabel(&self) -> AddrLabelHandle { + AddrLabelHandle::new(self.clone()) + } + /// Create a new handle, specifically for routing table requests (equivalent /// to `ip route` commands) pub fn route(&self) -> RouteHandle { diff --git a/src/lib.rs b/src/lib.rs index 8223f20c..61672a55 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,7 @@ pub use netlink_proto as proto; pub use netlink_sys as sys; mod addr; +mod addrlabel; mod connection; pub mod constants; mod errors; @@ -44,6 +45,10 @@ pub use crate::{ AddressAddRequest, AddressDelRequest, AddressGetRequest, AddressHandle, AddressMessageBuilder, }, + addrlabel::{ + AddrLabelAddRequest, AddrLabelDelRequest, AddrLabelGetRequest, + AddrLabelHandle, + }, connection::{ from_socket, new_connection_with_socket, new_multicast_connection_with_socket,