Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/commands/setup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ impl Setup {
container_hostname: &network_options.container_hostname,
container_dns_servers: &network_options.dns_servers,
netns_host: hostns.file.as_fd(),
netns_container: netns.file.as_fd(),
netns_container: Some(netns.file.as_fd()),
netns_path: &self.network_namespace_path,
network,
per_network_opts,
Expand Down Expand Up @@ -175,7 +175,7 @@ fn teardown_drivers<'a, I>(
I: Iterator<Item = &'a Box<dyn NetworkDriver + 'a>>,
{
for driver in drivers {
if let Err(e) = driver.teardown((host, netns)) {
if let Err(e) = driver.teardown((host, Some(netns))) {
error!(
"failed to cleanup network {} after setup failed: {}",
driver.network_name(),
Expand Down
24 changes: 20 additions & 4 deletions src/commands/teardown.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,24 @@ impl Teardown {

let firewall_driver = firewall::get_supported_firewall_driver(firewall_driver)?;

let (mut hostns, mut netns) =
core_utils::open_netlink_sockets(&self.network_namespace_path)?;
let mut hostns = core_utils::open_host_netlink_socket()?;
let (netns_file, mut netns_netlink) =
match core_utils::open_netns_netlink_socket(&self.network_namespace_path, &hostns) {
Ok(netns) => (Some(netns.file), Some(netns.netlink)),
Err(e) => {
match e.unwrap() {
NetavarkError::Io(io_err)
if io_err.kind() == std::io::ErrorKind::NotFound =>
{
log::warn!("container netns not found: {e}");
}
_ => {
error_list.push(e);
}
}
(None, None)
}
};

for named_network_opts in &network_options.networks {
let per_network_opts = &named_network_opts.opts;
Expand All @@ -116,7 +132,7 @@ impl Teardown {
container_hostname: &network_options.container_hostname,
container_dns_servers: &network_options.dns_servers,
netns_host: hostns.file.as_fd(),
netns_container: netns.file.as_fd(),
netns_container: netns_file.as_ref().map(|f| f.as_fd()), // None when no netns
netns_path: &self.network_namespace_path,
network,
per_network_opts,
Expand All @@ -134,7 +150,7 @@ impl Teardown {
}
};

match driver.teardown((&mut hostns.netlink, &mut netns.netlink)) {
match driver.teardown((&mut hostns.netlink, netns_netlink.as_mut())) {
Ok(_) => {}
Err(err) => {
error_list.push(err);
Expand Down
178 changes: 105 additions & 73 deletions src/network/bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,9 @@ impl driver::NetworkDriver for Bridge<'_> {
self.info.network.internal,
self.info.rootless,
self.info.netns_host,
self.info.netns_container,
self.info
.netns_container
.ok_or_else(|| NetavarkError::msg("netns_container required for setup"))?,
)?;

// StatusBlock response
Expand Down Expand Up @@ -374,33 +376,116 @@ impl driver::NetworkDriver for Bridge<'_> {

fn teardown(
&self,
netlink_sockets: (&mut Socket<NetlinkRoute>, &mut Socket<NetlinkRoute>),
netlink_sockets: (&mut Socket<NetlinkRoute>, Option<&mut Socket<NetlinkRoute>>),
) -> NetavarkResult<()> {
let mode: Option<String> = parse_option(&self.info.network.options, OPTION_MODE)?;
let mode = get_bridge_mode_from_string(mode.as_deref())?;
let (host_sock, netns_sock) = netlink_sockets;

let mut error_list = NetavarkErrorList::new();

dhcp_teardown(&self.info, netns_sock)?;
if let Some(netns_sock) = netns_sock {
self.netns_teardown(netns_sock, &mut error_list);
}
self.host_teardown(host_sock, &mut error_list);

let routes = core_utils::create_route_list(&self.info.network.routes)?;
for route in routes.iter() {
netns_sock
.del_route(route)
.unwrap_or_else(|err| error_list.push(err))
if !error_list.is_empty() {
return Err(NetavarkError::List(error_list));
}

let bridge_name = get_interface_name(self.info.network.network_interface.clone())?;
Ok(())
}
}

let complete_teardown = match remove_link(
host_sock,
netns_sock,
mode,
&bridge_name,
&self.info.per_network_opts.interface_name,
) {
Ok(teardown) => teardown,
fn get_interface_name(name: Option<String>) -> NetavarkResult<String> {
let name = match name {
None => return Err(NetavarkError::msg(NO_BRIDGE_NAME_ERROR)),
Some(n) => {
if n.is_empty() {
return Err(NetavarkError::msg(NO_BRIDGE_NAME_ERROR));
}
n
}
};
Ok(name)
}

impl<'a> Bridge<'a> {
/// Remove container-side resources: DHCP lease, routes, and veth.
fn netns_teardown(&self, netns: &mut Socket<NetlinkRoute>, error_list: &mut NetavarkErrorList) {
if let Err(err) = dhcp_teardown(&self.info, netns) {
error_list.push(err);
return;
}

match core_utils::create_route_list(&self.info.network.routes) {
Ok(routes) => {
for route in routes.iter() {
netns
.del_route(route)
.unwrap_or_else(|err| error_list.push(err))
}
}
Err(err) => error_list.push(err),
}

netns
.del_link(LinkID::Name(
self.info.per_network_opts.interface_name.to_string(),
))
.wrap(format!(
"failed to delete container veth {}",
self.info.per_network_opts.interface_name
))
.unwrap_or_else(|err| error_list.push(err));
}

/// Remove host resources: bridge, sysctl config, and firewall rules.
fn host_teardown(&self, host: &mut Socket<NetlinkRoute>, error_list: &mut NetavarkErrorList) {
let mode = match parse_option(&self.info.network.options, OPTION_MODE)
.and_then(|m: Option<String>| get_bridge_mode_from_string(m.as_deref()))
{
Ok(mode) => mode,
Err(err) => {
error_list.push(err);
return;
}
};
let bridge_name = match get_interface_name(self.info.network.network_interface.clone()) {
Ok(name) => name,
Err(err) => {
error_list.push(err);
return;
}
};

let complete_teardown = match host
.get_link(LinkID::Name(bridge_name.to_string()))
.wrap("failed to get bridge interface")
{
Ok(br) => {
match host
.dump_links(&mut vec![LinkAttribute::Controller(br.header.index)])
.wrap("failed to get connected bridge interfaces")
{
Ok(links) if links.is_empty() && matches!(mode, BridgeMode::Managed) => {
log::info!("removing bridge {bridge_name}");
match host
.del_link(LinkID::ID(br.header.index))
.wrap(format!("failed to delete bridge {bridge_name}"))
{
Ok(_) => true,
Err(err) => {
error_list.push(err);
false
}
}
}
Ok(_) => false,
Err(err) => {
error_list.push(err);
false
}
}
}
Err(err) => {
error_list.push(err);
false
Expand All @@ -418,7 +503,7 @@ impl driver::NetworkDriver for Bridge<'_> {
e.into(),
));
}
};
}
}

match self.teardown_firewall(complete_teardown, bridge_name) {
Expand All @@ -428,29 +513,8 @@ impl driver::NetworkDriver for Bridge<'_> {
}
}
}

if !error_list.is_empty() {
return Err(NetavarkError::List(error_list));
}

Ok(())
}
}

fn get_interface_name(name: Option<String>) -> NetavarkResult<String> {
let name = match name {
None => return Err(NetavarkError::msg(NO_BRIDGE_NAME_ERROR)),
Some(n) => {
if n.is_empty() {
return Err(NetavarkError::msg(NO_BRIDGE_NAME_ERROR));
}
n
}
};
Ok(name)
}

impl<'a> Bridge<'a> {
fn get_firewall_conf(
&'a self,
container_addresses: &Vec<IpNet>,
Expand Down Expand Up @@ -1114,38 +1178,6 @@ fn validate_vrf_link(msg: LinkMessage, vrf_name: &str) -> NetavarkResult<(u32, O
)))
}

fn remove_link(
host: &mut Socket<NetlinkRoute>,
netns: &mut Socket<NetlinkRoute>,
mode: BridgeMode,
br_name: &str,
container_veth_name: &str,
) -> NetavarkResult<bool> {
netns
.del_link(LinkID::Name(container_veth_name.to_string()))
.wrap(format!(
"failed to delete container veth {container_veth_name}"
))?;

let br = host
.get_link(LinkID::Name(br_name.to_string()))
.wrap("failed to get bridge interface")?;

let links = host
.dump_links(&mut vec![LinkAttribute::Controller(br.header.index)])
.wrap("failed to get connected bridge interfaces")?;
// no connected interfaces on that bridge we can remove it
if links.is_empty() {
if let BridgeMode::Managed = mode {
log::info!("removing bridge {br_name}");
host.del_link(LinkID::ID(br.header.index))
.wrap(format!("failed to delete bridge {container_veth_name}"))?;
return Ok(true);
}
}
Ok(false)
}

fn get_isolate_option(opts: &Option<HashMap<String, String>>) -> NetavarkResult<IsolateOption> {
let isolate: String = match parse_option(opts, OPTION_ISOLATE)? {
Some(i) => i,
Expand Down
28 changes: 28 additions & 0 deletions src/network/core_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,34 @@ pub fn open_netlink_sockets(
))
}

/// Open the host netlink socket. Used during teardown when the
/// container network namespace is no longer available.
pub fn open_host_netlink_socket() -> NetavarkResult<NamespaceOptions> {
let hostns = open_netlink_socket("/proc/self/ns/net").wrap("open host netns")?;
let host_socket = netlink::Socket::<NetlinkRoute>::new().wrap("host netlink socket")?;
Ok(NamespaceOptions {
file: hostns,
netlink: host_socket,
})
}

/// Open the container netns netlink socket, given an already-opened host ns.
pub fn open_netns_netlink_socket(
netns_path: &str,
hostns: &NamespaceOptions,
) -> NetavarkResult<NamespaceOptions> {
let netns = open_netlink_socket(netns_path).wrap("open container netns")?;
let netns_sock = exec_netns!(
hostns.file.as_fd(),
netns.as_fd(),
netlink::Socket::<NetlinkRoute>::new().wrap("netns netlink socket")
)?;
Ok(NamespaceOptions {
file: netns,
netlink: netns_sock,
})
}

fn open_netlink_socket(netns_path: &str) -> NetavarkResult<File> {
wrap!(File::open(netns_path), format!("open {netns_path}"))
}
Expand Down
4 changes: 2 additions & 2 deletions src/network/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pub struct DriverInfo<'a> {
pub container_name: &'a String,
pub container_dns_servers: &'a Option<Vec<IpAddr>>,
pub netns_host: BorrowedFd<'a>,
pub netns_container: BorrowedFd<'a>,
pub netns_container: Option<BorrowedFd<'a>>,
pub netns_path: &'a str,
pub network: &'a Network,
pub per_network_opts: &'a PerNetworkOptions,
Expand All @@ -45,7 +45,7 @@ pub trait NetworkDriver {
/// teardown the network interfaces/firewall rules for this driver
fn teardown(
&self,
netlink_sockets: (&mut Socket<NetlinkRoute>, &mut Socket<NetlinkRoute>),
netlink_sockets: (&mut Socket<NetlinkRoute>, Option<&mut Socket<NetlinkRoute>>),
) -> NetavarkResult<()>;

/// return the network name
Expand Down
2 changes: 1 addition & 1 deletion src/network/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ impl NetworkDriver for PluginDriver<'_> {

fn teardown(
&self,
_netlink_sockets: (&mut Socket<NetlinkRoute>, &mut Socket<NetlinkRoute>),
_netlink_sockets: (&mut Socket<NetlinkRoute>, Option<&mut Socket<NetlinkRoute>>),
) -> NetavarkResult<()> {
self.exec_plugin(false, self.info.netns_path).wrap(format!(
"plugin {:?} failed",
Expand Down
17 changes: 12 additions & 5 deletions src/network/vlan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,9 @@ impl driver::NetworkDriver for Vlan<'_> {
&self.info.per_network_opts.interface_name,
data,
self.info.netns_host,
self.info.netns_container,
self.info
.netns_container
.expect("netns_container required for setup"),
&data.kind,
)?;

Expand Down Expand Up @@ -218,16 +220,21 @@ impl driver::NetworkDriver for Vlan<'_> {

fn teardown(
&self,
netlink_sockets: (&mut Socket<NetlinkRoute>, &mut Socket<NetlinkRoute>),
netlink_sockets: (&mut Socket<NetlinkRoute>, Option<&mut Socket<NetlinkRoute>>),
) -> NetavarkResult<()> {
dhcp_teardown(&self.info, netlink_sockets.1)?;
let netns_sock = match netlink_sockets.1 {
Some(s) => s,
None => return Ok(()),
};

dhcp_teardown(&self.info, netns_sock)?;

let routes = core_utils::create_route_list(&self.info.network.routes)?;
for route in routes.iter() {
netlink_sockets.1.del_route(route)?;
netns_sock.del_route(route)?;
}

netlink_sockets.1.del_link(LinkID::Name(
netns_sock.del_link(LinkID::Name(
self.info.per_network_opts.interface_name.to_string(),
))?;
Ok(())
Expand Down
Loading