Skip to content
This repository has been archived by the owner on Oct 26, 2022. It is now read-only.

Commit

Permalink
rtnl/link: add match_name in addition to add_name_filter
Browse files Browse the repository at this point in the history
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]: torvalds/linux@a3d1289
[2]: https://github.com/thom311/libnl/blob/master/lib/route/link.c#L1376
  • Loading branch information
Tuetuopay committed Nov 29, 2021
1 parent 5836d73 commit 71ed382
Show file tree
Hide file tree
Showing 15 changed files with 31 additions and 65 deletions.
2 changes: 1 addition & 1 deletion rtnetlink/examples/add_address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion rtnetlink/examples/add_neighbour.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion rtnetlink/examples/add_route_pref_src.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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?
Expand Down
6 changes: 1 addition & 5 deletions rtnetlink/examples/create_macvlan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion rtnetlink/examples/create_vxlan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
2 changes: 1 addition & 1 deletion rtnetlink/examples/del_link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion rtnetlink/examples/flush_addresses.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
2 changes: 1 addition & 1 deletion rtnetlink/examples/get_address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
2 changes: 1 addition & 1 deletion rtnetlink/examples/get_links.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion rtnetlink/examples/get_links_async.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion rtnetlink/examples/get_links_thread_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion rtnetlink/examples/property_altname.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ async fn get_link(link_name: &str, handle: Handle) -> Result<LinkMessage, Error>
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? {
Expand Down
2 changes: 1 addition & 1 deletion rtnetlink/examples/set_link_down.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
61 changes: 17 additions & 44 deletions rtnetlink/src/link/get.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use futures::{
future::{self, Either},
stream::{StreamExt, TryStream, TryStreamExt},
stream::{StreamExt, TryStream},
FutureExt,
};

Expand All @@ -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 {
Expand All @@ -31,7 +31,6 @@ impl LinkGetRequest {
handle,
message: LinkMessage::default(),
dump: true,
filter_builder: LinkFilterBuilder::new(),
}
}

Expand All @@ -48,7 +47,6 @@ impl LinkGetRequest {
mut handle,
message,
dump,
filter_builder,
} = self;

let mut req = NetlinkMessage::from(RtnlMessage::GetLink(message));
Expand All @@ -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::<LinkMessage, Error>(e).into_stream()),
}
}
Expand All @@ -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<String>,
}

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
}
}
}
}
5 changes: 1 addition & 4 deletions rtnetlink/src/link/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,7 @@ async fn _create_wg() -> Result<LinkHandle, Error> {
}

async fn _get_wg(handle: &mut LinkHandle) -> Result<LinkMessage, Error> {
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)
}
Expand Down

0 comments on commit 71ed382

Please sign in to comment.