From 71ed382d7a4525c8c554bbd75d8c41eb83f68ebf Mon Sep 17 00:00:00 2001 From: Tuetuopay Date: Mon, 29 Nov 2021 10:54:13 +0100 Subject: [PATCH] rtnl/link: add match_name in addition to add_name_filter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since kernel commit [a3d1289126e7b][1], it is possible to set the IFLA_IFNAME attribute in a RTM_GETLINK message, allowing to get a link by its name without dumping the whole link list. In large setups, this speeds up massively the library, going from "painfully slow" to "really quick" (in my application with thousands of links, a lookup by name went from hundreds of ms to hundreds of µs). This is based off the equivalent function in [libnl][2]. [1]: https://github.com/torvalds/linux/commit/a3d1289126e7b14307074b76bf1677015ea5036f [2]: https://github.com/thom311/libnl/blob/master/lib/route/link.c#L1376 --- rtnetlink/examples/add_address.rs | 2 +- rtnetlink/examples/add_neighbour.rs | 2 +- rtnetlink/examples/add_route_pref_src.rs | 2 +- rtnetlink/examples/create_macvlan.rs | 6 +- rtnetlink/examples/create_vxlan.rs | 2 +- rtnetlink/examples/del_link.rs | 2 +- rtnetlink/examples/flush_addresses.rs | 2 +- rtnetlink/examples/get_address.rs | 2 +- rtnetlink/examples/get_links.rs | 2 +- rtnetlink/examples/get_links_async.rs | 2 +- .../examples/get_links_thread_builder.rs | 2 +- rtnetlink/examples/property_altname.rs | 2 +- rtnetlink/examples/set_link_down.rs | 2 +- rtnetlink/src/link/get.rs | 61 ++++++------------- rtnetlink/src/link/test.rs | 5 +- 15 files changed, 31 insertions(+), 65 deletions(-) diff --git a/rtnetlink/examples/add_address.rs b/rtnetlink/examples/add_address.rs index 96cf46ff..b23dc286 100644 --- a/rtnetlink/examples/add_address.rs +++ b/rtnetlink/examples/add_address.rs @@ -33,7 +33,7 @@ async fn add_address(link_name: &str, ip: IpNetwork, handle: Handle) -> Result<( let mut links = handle .link() .get() - .set_name_filter(link_name.to_string()) + .match_name(link_name.to_string()) .execute(); if let Some(link) = links.try_next().await? { handle diff --git a/rtnetlink/examples/add_neighbour.rs b/rtnetlink/examples/add_neighbour.rs index 61f7ddfe..c3d3a85f 100644 --- a/rtnetlink/examples/add_neighbour.rs +++ b/rtnetlink/examples/add_neighbour.rs @@ -31,7 +31,7 @@ async fn add_neighbour(link_name: &str, ip: IpAddr, handle: Handle) -> Result<() let mut links = handle .link() .get() - .set_name_filter(link_name.to_string()) + .match_name(link_name.to_string()) .execute(); if let Some(link) = links.try_next().await? { handle diff --git a/rtnetlink/examples/add_route_pref_src.rs b/rtnetlink/examples/add_route_pref_src.rs index 1700fced..df27ee25 100644 --- a/rtnetlink/examples/add_route_pref_src.rs +++ b/rtnetlink/examples/add_route_pref_src.rs @@ -45,7 +45,7 @@ async fn add_route( let iface_idx = handle .link() .get() - .set_name_filter(iface) + .match_name(iface) .execute() .try_next() .await? diff --git a/rtnetlink/examples/create_macvlan.rs b/rtnetlink/examples/create_macvlan.rs index 154c7c0c..6cbe4808 100644 --- a/rtnetlink/examples/create_macvlan.rs +++ b/rtnetlink/examples/create_macvlan.rs @@ -22,11 +22,7 @@ async fn main() -> Result<(), String> { } async fn create_macvlan(handle: Handle, veth_name: String) -> Result<(), Error> { - let mut links = handle - .link() - .get() - .set_name_filter(veth_name.clone()) - .execute(); + let mut links = handle.link().get().match_name(veth_name.clone()).execute(); if let Some(link) = links.try_next().await? { // hard code mode: 4u32 i.e bridge mode let request = handle diff --git a/rtnetlink/examples/create_vxlan.rs b/rtnetlink/examples/create_vxlan.rs index c7a3a516..ec99d96d 100644 --- a/rtnetlink/examples/create_vxlan.rs +++ b/rtnetlink/examples/create_vxlan.rs @@ -22,7 +22,7 @@ async fn main() -> Result<(), String> { } async fn create_vxlan(handle: Handle, name: String) -> Result<(), Error> { - let mut links = handle.link().get().set_name_filter(name.clone()).execute(); + let mut links = handle.link().get().match_name(name.clone()).execute(); if let Some(link) = links.try_next().await? { handle .link() diff --git a/rtnetlink/examples/del_link.rs b/rtnetlink/examples/del_link.rs index b42463a5..295b9c6f 100644 --- a/rtnetlink/examples/del_link.rs +++ b/rtnetlink/examples/del_link.rs @@ -23,7 +23,7 @@ async fn main() -> Result<(), ()> { } async fn del_link(handle: Handle, name: String) -> Result<(), Error> { - let mut links = handle.link().get().set_name_filter(name.clone()).execute(); + let mut links = handle.link().get().match_name(name.clone()).execute(); if let Some(link) = links.try_next().await? { handle.link().del(link.header.index).execute().await } else { diff --git a/rtnetlink/examples/flush_addresses.rs b/rtnetlink/examples/flush_addresses.rs index bec3b640..b29f1b45 100644 --- a/rtnetlink/examples/flush_addresses.rs +++ b/rtnetlink/examples/flush_addresses.rs @@ -24,7 +24,7 @@ async fn main() -> Result<(), ()> { } async fn flush_addresses(handle: Handle, link: String) -> Result<(), Error> { - let mut links = handle.link().get().set_name_filter(link.clone()).execute(); + let mut links = handle.link().get().match_name(link.clone()).execute(); if let Some(link) = links.try_next().await? { // We should have received only one message assert!(links.try_next().await?.is_none()); diff --git a/rtnetlink/examples/get_address.rs b/rtnetlink/examples/get_address.rs index b4d22fab..0db4e7d3 100644 --- a/rtnetlink/examples/get_address.rs +++ b/rtnetlink/examples/get_address.rs @@ -19,7 +19,7 @@ async fn main() -> Result<(), ()> { } async fn dump_addresses(handle: Handle, link: String) -> Result<(), Error> { - let mut links = handle.link().get().set_name_filter(link.clone()).execute(); + let mut links = handle.link().get().match_name(link.clone()).execute(); if let Some(link) = links.try_next().await? { let mut addresses = handle .address() diff --git a/rtnetlink/examples/get_links.rs b/rtnetlink/examples/get_links.rs index 68b5b0c9..12f6ec3b 100644 --- a/rtnetlink/examples/get_links.rs +++ b/rtnetlink/examples/get_links.rs @@ -70,7 +70,7 @@ async fn get_link_by_index(handle: Handle, index: u32) -> Result<(), Error> { } async fn get_link_by_name(handle: Handle, name: String) -> Result<(), Error> { - let mut links = handle.link().get().set_name_filter(name.clone()).execute(); + let mut links = handle.link().get().match_name(name.clone()).execute(); if let Some(_) = links.try_next().await? { println!("found link {}", name); // We should only have one link with that name diff --git a/rtnetlink/examples/get_links_async.rs b/rtnetlink/examples/get_links_async.rs index fc06ba17..8ca456a0 100644 --- a/rtnetlink/examples/get_links_async.rs +++ b/rtnetlink/examples/get_links_async.rs @@ -70,7 +70,7 @@ async fn get_link_by_index(handle: Handle, index: u32) -> Result<(), Error> { } async fn get_link_by_name(handle: Handle, name: String) -> Result<(), Error> { - let mut links = handle.link().get().set_name_filter(name.clone()).execute(); + let mut links = handle.link().get().match_name(name.clone()).execute(); if let Some(_) = links.try_next().await? { println!("found link {}", name); // We should only have one link with that name diff --git a/rtnetlink/examples/get_links_thread_builder.rs b/rtnetlink/examples/get_links_thread_builder.rs index 0e6d3317..82ffc958 100644 --- a/rtnetlink/examples/get_links_thread_builder.rs +++ b/rtnetlink/examples/get_links_thread_builder.rs @@ -70,7 +70,7 @@ async fn get_link_by_index(handle: Handle, index: u32) -> Result<(), Error> { } async fn get_link_by_name(handle: Handle, name: String) -> Result<(), Error> { - let mut links = handle.link().get().set_name_filter(name.clone()).execute(); + let mut links = handle.link().get().match_name(name.clone()).execute(); if let Some(_) = links.try_next().await? { println!("found link {}", name); // We should only have one link with that name diff --git a/rtnetlink/examples/property_altname.rs b/rtnetlink/examples/property_altname.rs index 447f6e57..2a10f38e 100644 --- a/rtnetlink/examples/property_altname.rs +++ b/rtnetlink/examples/property_altname.rs @@ -106,7 +106,7 @@ async fn get_link(link_name: &str, handle: Handle) -> Result let mut links = handle .link() .get() - .set_name_filter(link_name.to_string()) + .match_name(link_name.to_string()) .execute(); match links.try_next().await? { diff --git a/rtnetlink/examples/set_link_down.rs b/rtnetlink/examples/set_link_down.rs index 11475108..e6cc2523 100644 --- a/rtnetlink/examples/set_link_down.rs +++ b/rtnetlink/examples/set_link_down.rs @@ -22,7 +22,7 @@ async fn main() -> Result<(), String> { } async fn set_link_down(handle: Handle, name: String) -> Result<(), Error> { - let mut links = handle.link().get().set_name_filter(name.clone()).execute(); + let mut links = handle.link().get().match_name(name.clone()).execute(); if let Some(link) = links.try_next().await? { handle .link() diff --git a/rtnetlink/src/link/get.rs b/rtnetlink/src/link/get.rs index 6e65dd39..4ed4d706 100644 --- a/rtnetlink/src/link/get.rs +++ b/rtnetlink/src/link/get.rs @@ -2,7 +2,7 @@ use futures::{ future::{self, Either}, - stream::{StreamExt, TryStream, TryStreamExt}, + stream::{StreamExt, TryStream}, FutureExt, }; @@ -17,12 +17,12 @@ pub struct LinkGetRequest { handle: Handle, message: LinkMessage, // There are two ways to retrieve links: we can either dump them - // all and filter the result, or if we already know the index of - // the link we're looking for, we can just retrieve that one. If - // `dump` is `true`, all the links are fetched. Otherwise, only - // the link that match the given index is fetched. + // all and filter the result, or if we already know the index or + // the name of the link we're looking for, we can just retrieve + // that one. If `dump` is `true`, all the links are fetched. + // Otherwise, only the link that match the given index or name + // is fetched. dump: bool, - filter_builder: LinkFilterBuilder, } impl LinkGetRequest { @@ -31,7 +31,6 @@ impl LinkGetRequest { handle, message: LinkMessage::default(), dump: true, - filter_builder: LinkFilterBuilder::new(), } } @@ -48,7 +47,6 @@ impl LinkGetRequest { mut handle, message, dump, - filter_builder, } = self; let mut req = NetlinkMessage::from(RtnlMessage::GetLink(message)); @@ -59,13 +57,10 @@ impl LinkGetRequest { req.header.flags = NLM_F_REQUEST; } - let filter = filter_builder.build(); match handle.request(req) { - Ok(response) => Either::Left( - response - .map(move |msg| Ok(try_rtnl!(msg, RtnlMessage::NewLink))) - .try_filter(move |msg| future::ready(filter(msg))), - ), + Ok(response) => { + Either::Left(response.map(move |msg| Ok(try_rtnl!(msg, RtnlMessage::NewLink)))) + } Err(e) => Either::Right(future::err::(e).into_stream()), } } @@ -75,42 +70,20 @@ impl LinkGetRequest { &mut self.message } + /// Lookup a link by index pub fn match_index(mut self, index: u32) -> Self { self.dump = false; self.message.header.index = index; self } - pub fn set_name_filter(mut self, name: String) -> Self { - self.filter_builder.name = Some(name); + /// Lookup a link by name + /// + /// This function requires support from your kernel (>= 2.6.33). If yours is + /// older, consider filtering the resulting stream of links. + pub fn match_name(mut self, name: String) -> Self { + self.dump = false; + self.message.nlas.push(Nla::IfName(name)); self } } - -#[derive(Default)] -struct LinkFilterBuilder { - name: Option, -} - -impl LinkFilterBuilder { - fn new() -> Self { - Default::default() - } - - fn build(self) -> impl Fn(&LinkMessage) -> bool { - move |msg: &LinkMessage| { - if let Some(name) = &self.name { - for nla in msg.nlas.iter() { - if let Nla::IfName(s) = nla { - if s == name { - return true; - } - } - } - false - } else { - true - } - } - } -} diff --git a/rtnetlink/src/link/test.rs b/rtnetlink/src/link/test.rs index 5e797201..2cc44496 100644 --- a/rtnetlink/src/link/test.rs +++ b/rtnetlink/src/link/test.rs @@ -49,10 +49,7 @@ async fn _create_wg() -> Result { } async fn _get_wg(handle: &mut LinkHandle) -> Result { - let mut links = handle - .get() - .set_name_filter(IFACE_NAME.to_owned()) - .execute(); + let mut links = handle.get().match_name(IFACE_NAME.to_owned()).execute(); let msg = links.try_next().await?; msg.ok_or_else(|| Error::RequestFailed) }