minor link handling improvements

This commit is contained in:
Robin Appelman 2026-02-23 20:02:25 +01:00
commit 35c8f5cc6c
2 changed files with 28 additions and 30 deletions

View file

@ -1,4 +1,3 @@
use crate::namespace::{NamespaceEnterError, NamespaceHandle};
use neli::consts::nl::NlmF; use neli::consts::nl::NlmF;
use neli::consts::rtnl::Ifla; use neli::consts::rtnl::Ifla;
use neli::consts::rtnl::RtAddrFamily; use neli::consts::rtnl::RtAddrFamily;
@ -12,25 +11,28 @@ use neli::rtnl::{Ifinfomsg, RtattrBuilder};
use neli::types::{Buffer, RtBuffer}; use neli::types::{Buffer, RtBuffer};
use neli::utils::Groups; use neli::utils::Groups;
use nix::libc::c_int; use nix::libc::c_int;
use std::fmt::Debug;
use std::os::fd::AsRawFd; use std::os::fd::AsRawFd;
use thiserror::Error; use thiserror::Error;
use tracing::info; use tracing::info;
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum LinkError { pub enum LinkError {
#[error("failed to communicate with netlink")] #[error("Failed to communicate with netlink:")]
Netlink, Netlink(String),
#[error("failed to code netlink response")] #[error("Failed to parse netlink response")]
Parse, Parse(String),
#[error("Link not found: {0}")] #[error("Link not found: {0}")]
NotFound(String), NotFound(String),
#[error(transparent)]
Enter(#[from] NamespaceEnterError),
} }
impl<T, P> From<RouterError<T, P>> for LinkError { impl<T, P> From<RouterError<T, P>> for LinkError
fn from(_value: RouterError<T, P>) -> Self { where
LinkError::Netlink T: Debug,
P: Debug,
{
fn from(value: RouterError<T, P>) -> Self {
LinkError::Netlink(value.to_string())
} }
} }
@ -89,7 +91,7 @@ impl LinkManager {
.rtattrs() .rtattrs()
.get_attr_handle() .get_attr_handle()
.get_attr_payload_as_with_len::<String>(Ifla::Ifname) .get_attr_payload_as_with_len::<String>(Ifla::Ifname)
.map_err(|_| LinkError::Parse)?; .map_err(|e| LinkError::Parse(e.to_string()))?;
Ok(Some(Link { Ok(Some(Link {
family: *payload.ifi_family(), family: *payload.ifi_family(),
index: *payload.ifi_index(), index: *payload.ifi_index(),
@ -103,7 +105,7 @@ impl LinkManager {
} }
/// Move a link to a namespace /// Move a link to a namespace
pub fn move_link<Fd: AsRawFd>(&self, link: &Link, namespace: Fd) -> Result<(), LinkError> { pub fn move_link<Fd: AsRawFd>(&self, link: &Link, namespace: &Fd) -> Result<(), LinkError> {
let ns_handle = namespace.as_raw_fd(); let ns_handle = namespace.as_raw_fd();
let mut info_attrs = RtBuffer::<Ifla, Buffer>::new(); let mut info_attrs = RtBuffer::<Ifla, Buffer>::new();
@ -143,27 +145,21 @@ pub fn link_up(link_name: &str) -> Result<(), LinkError> {
} }
/// Move a link into a namespace /// Move a link into a namespace
pub fn move_link_into(link_name: &str, namespace: &NamespaceHandle) -> Result<(), LinkError> { pub fn move_link_into<Fd: AsRawFd>(link_name: &str, namespace: &Fd) -> Result<(), LinkError> {
let manager = LinkManager::new()?; let manager = LinkManager::new()?;
let link = manager.get_link(link_name)?; let link = manager.get_link(link_name)?;
info!(name = &link.name, "moving link into namespace"); info!(name = &link.name, "moving link into namespace");
manager.move_link(&link, namespace) manager.move_link(&link, namespace)
} }
/// Move all links out of a namespace, except for lo /// Move all links from the current namespace (except lo) into a namespace
pub fn move_all_links_out( pub fn move_all_links<Fd: AsRawFd>(namespace: &Fd) -> Result<(), LinkError> {
namespace: &NamespaceHandle,
parent: &NamespaceHandle,
) -> Result<(), LinkError> {
namespace.run_in(|| {
let manager = LinkManager::new()?; let manager = LinkManager::new()?;
for link in manager.get_links()?.flatten() { for link in manager.get_links()?.flatten() {
if link.name != "lo" { if link.name != "lo" {
info!(name = &link.name, "moving link out of namespace"); info!(name = &link.name, "moving link");
manager.move_link(&link, parent)? manager.move_link(&link, namespace)?
} }
} }
Ok::<_, LinkError>(()) Ok::<_, LinkError>(())
})??;
Ok(())
} }

View file

@ -2,7 +2,7 @@ mod handle;
mod raw; mod raw;
use crate::config::{DeviceName, NamespaceName}; use crate::config::{DeviceName, NamespaceName};
use crate::link::{link_up, move_all_links_out, move_link_into, LinkError}; use crate::link::{link_up, move_all_links, move_link_into, LinkError};
pub use crate::namespace::handle::{NamespaceHandle, NamespaceHandleError}; pub use crate::namespace::handle::{NamespaceHandle, NamespaceHandleError};
use crate::namespace::raw::{create_network_namespace, NamespaceSetupError}; use crate::namespace::raw::{create_network_namespace, NamespaceSetupError};
use either::Either; use either::Either;
@ -162,7 +162,9 @@ impl NetNs {
pub fn delete(self) -> Result<(), NamespaceError> { pub fn delete(self) -> Result<(), NamespaceError> {
let parent_namespace = NamespaceHandle::parent()?; let parent_namespace = NamespaceHandle::parent()?;
move_all_links_out(self.handle(), &parent_namespace)?; self.handle.run_in(|| {
move_all_links(&parent_namespace)
})??;
let name = self.path.file_name().unwrap().to_str().unwrap(); let name = self.path.file_name().unwrap().to_str().unwrap();
info!(name, "deleting network namespace"); info!(name, "deleting network namespace");
match umount2(&self.path, MntFlags::MNT_DETACH) { match umount2(&self.path, MntFlags::MNT_DETACH) {