chore: Better error handling in link_create_and_up in rp

This commit is contained in:
Karolin Varner
2025-07-31 15:46:52 +02:00
parent 21ea526435
commit a85f9b8e63
3 changed files with 65 additions and 26 deletions

1
Cargo.lock generated
View File

@@ -2229,6 +2229,7 @@ dependencies = [
"futures", "futures",
"futures-util", "futures-util",
"genetlink", "genetlink",
"libc",
"netlink-packet-core", "netlink-packet-core",
"netlink-packet-generic", "netlink-packet-generic",
"netlink-packet-wireguard", "netlink-packet-wireguard",

View File

@@ -17,6 +17,7 @@ serde = { workspace = true }
toml = { workspace = true } toml = { workspace = true }
x25519-dalek = { workspace = true, features = ["static_secrets"] } x25519-dalek = { workspace = true, features = ["static_secrets"] }
zeroize = { workspace = true } zeroize = { workspace = true }
libc = { workspace = true }
rosenpass = { workspace = true } rosenpass = { workspace = true }
rosenpass-ciphers = { workspace = true } rosenpass-ciphers = { workspace = true }

View File

@@ -3,7 +3,7 @@ use std::{
sync::Arc, sync::Arc,
}; };
use anyhow::{anyhow, Error, Result}; use anyhow::{anyhow, bail, ensure, Context, Error, Result};
use futures_util::{StreamExt as _, TryStreamExt as _}; use futures_util::{StreamExt as _, TryStreamExt as _};
use serde::Deserialize; use serde::Deserialize;
@@ -18,6 +18,7 @@ use rosenpass::{
}; };
use rosenpass_secret_memory::Secret; use rosenpass_secret_memory::Secret;
use rosenpass_util::file::{LoadValue as _, LoadValueB64}; use rosenpass_util::file::{LoadValue as _, LoadValueB64};
use rosenpass_util::functional::ApplyExt;
use rosenpass_wireguard_broker::brokers::native_unix::{ use rosenpass_wireguard_broker::brokers::native_unix::{
NativeUnixBroker, NativeUnixBrokerConfigBaseBuilder, NativeUnixBrokerConfigBaseBuilderError, NativeUnixBroker, NativeUnixBrokerConfigBaseBuilder, NativeUnixBrokerConfigBaseBuilderError,
}; };
@@ -34,6 +35,7 @@ mod netlink {
/// Re-exports from [::rtnetlink] /// Re-exports from [::rtnetlink]
pub mod rtnl { pub mod rtnl {
pub use ::rtnetlink::Error;
pub use ::rtnetlink::Handle; pub use ::rtnetlink::Handle;
} }
@@ -95,38 +97,73 @@ pub struct ExchangeOptions {
/// operations of creating the link or changing its state to up fails. /// operations of creating the link or changing its state to up fails.
pub async fn link_create_and_up( pub async fn link_create_and_up(
rtnetlink: &netlink::rtnl::Handle, rtnetlink: &netlink::rtnl::Handle,
link_name: String, device_name: &str,
) -> Result<u32> { ) -> Result<u32> {
// Add the link, equivalent to `ip link add <link_name> type wireguard`. let mut rtnl_link = rtnetlink.link();
rtnetlink
.link()
.add()
.wireguard(link_name.clone())
.execute()
.await?;
// Retrieve the link to be able to up it, equivalent to `ip link show` and then // Make sure that there is no device called `device_name` before we start
// using the link shown that is identified by `link_name`. rtnl_link
let link = rtnetlink
.link()
.get() .get()
.match_name(link_name.clone()) .match_name(device_name.to_owned())
.execute() .execute()
.into_stream() // Count the number of occurences
.into_future() .try_fold(0, |acc, _val| async move {
.await Ok(acc + 1)
.0 }).await
.unwrap()?; // Extract the error's raw system error code
.map_err(|e| {
use netlink::rtnl::Error as E;
match e {
E::NetlinkError(msg) => {
let raw_code = -msg.raw_code();
(E::NetlinkError(msg), Some(raw_code))
},
_ => (e, None),
}
})
.apply(|r| {
match r {
// No such device, which is exactly what we are expecting
Ok(0) | Err((_, Some(libc::ENODEV))) => Ok(()),
// Device already exists
Ok(_) => bail!("\
Trying to create a network device for Rosenpass under the name \"{device_name}\", \
but at least one device under the name aready exists."),
// Other error
Err((e, _)) => bail!(e),
}
})?;
// Up the link, equivalent to `ip link set dev <DEV> up`. // Add the link, equivalent to `ip link add <link_name> type wireguard`.
rtnetlink rtnl_link
.link() .add()
.set(link.header.index) .wireguard(device_name.to_owned())
.up()
.execute() .execute()
.await?; .await?;
Ok(link.header.index) // Retrieve a handle for the newly created device
let dev = rtnl_link
.get()
.match_name(device_name.to_owned())
.execute()
.err_into::<anyhow::Error>()
.try_fold(Option::None, |acc, val| async move {
ensure!(acc.is_none(), "\
Created a network device for Rosenpass under the name \"{device_name}\", \
but upon trying to determine the handle for the device using named-based lookup, we received multiple handles. \
We checked beforehand whether the device already exists. \
This should not happen. Unsure how to proceed. Terminating.");
Ok(Some(val))
}).await?
.with_context(|| format!("\
Created a network device for Rosenpass under the name \"{device_name}\", \
but upon trying to determine the handle for the device using named-based lookup, we received no handle. \
This should not happen. Unsure how to proceed. Terminating."))?;
// Activate the link, equivalent to `ip link set dev <DEV> up`.
rtnl_link.set(dev.header.index).up().execute().await?;
Ok(dev.header.index)
} }
/// Deletes a link using rtnetlink. The link is specified using its index in the list of links. /// Deletes a link using rtnetlink. The link is specified using its index in the list of links.
@@ -222,7 +259,7 @@ pub async fn exchange(options: ExchangeOptions) -> Result<()> {
tokio::spawn(connection); tokio::spawn(connection);
let link_name = options.dev.clone().unwrap_or("rosenpass0".to_string()); let link_name = options.dev.clone().unwrap_or("rosenpass0".to_string());
let link_index = link_create_and_up(&rtnetlink, link_name.clone()).await?; let link_index = link_create_and_up(&rtnetlink, &link_name).await?;
// Set up a list of (initiallc empty) cleanup handlers that are to be run if // Set up a list of (initiallc empty) cleanup handlers that are to be run if
// ctrl-c is hit or generally a `SIGINT` signal is received and always in the end. // ctrl-c is hit or generally a `SIGINT` signal is received and always in the end.