Skip to main content

netavark/commands/
teardown.rs

1use crate::commands::get_config_dir;
2use crate::dns::aardvark::{Aardvark, AardvarkEntry};
3use crate::error::{NetavarkError, NetavarkErrorList, NetavarkResult};
4use crate::network::constants::DRIVER_BRIDGE;
5use crate::network::core_utils;
6use crate::network::driver::{get_network_driver, DriverInfo};
7
8use crate::{firewall, network};
9use clap::builder::NonEmptyStringValueParser;
10use clap::Parser;
11use log::debug;
12use nix::sys::signal;
13use std::ffi::OsString;
14use std::os::fd::AsFd;
15use std::path::Path;
16
17#[derive(Parser, Debug)]
18pub struct Teardown {
19    /// Network namespace path
20    #[clap(required = true, value_parser = NonEmptyStringValueParser::new())]
21    network_namespace_path: String,
22}
23
24impl Teardown {
25    /// The teardown command is the inverse of the setup command, undoing any configuration applied. Some interfaces may not be deleted (bridge interfaces, for example, will not be removed).
26    pub fn new(network_namespace_path: String) -> Self {
27        Self {
28            network_namespace_path,
29        }
30    }
31
32    pub fn exec(
33        &self,
34        input_file: Option<OsString>,
35        config_dir: Option<OsString>,
36        firewall_driver: Option<String>,
37        aardvark_bin: OsString,
38        plugin_directories: Option<Vec<OsString>>,
39        rootless: bool,
40    ) -> NetavarkResult<()> {
41        debug!("Tearing down..");
42
43        // SAFETY:  signal handlers are considered unsafe due to the care that must
44        //          taken when running code inside the handler function. We however
45        //          only ignore the signal so this is safe without having to worry
46        //          about any code restrictions.
47        // Also we ignore the returned error, this is best effort anyway and can only error when we would pass a invalid signal number.
48        let _ = unsafe { signal::signal(signal::SIGTERM, signal::SigHandler::SigIgn) };
49        let _ = unsafe { signal::signal(signal::SIGINT, signal::SigHandler::SigIgn) };
50
51        let network_options = network::types::NetworkOptions::load(input_file)?;
52
53        let mut error_list = NetavarkErrorList::new();
54
55        let dns_port = core_utils::get_netavark_dns_port()?;
56        let config_dir = get_config_dir(config_dir, "teardown")?;
57
58        let mut aardvark_entries = Vec::new();
59        for (key, network) in &network_options.network_info {
60            if network.dns_enabled && network.driver == DRIVER_BRIDGE {
61                match network_options.container_id.as_str().try_into() {
62                    Ok(id) => {
63                        aardvark_entries.push(AardvarkEntry {
64                            network_name: key,
65                            network_gateways: Vec::new(),
66                            network_dns_servers: &None,
67                            container_id: id,
68                            container_ips_v4: Vec::new(),
69                            container_ips_v6: Vec::new(),
70                            container_names: Vec::new(),
71                            container_dns_servers: &None,
72                            is_internal: network.internal,
73                        });
74                    }
75                    Err(err) => log::warn!(
76                        "invalid container id {}: {err}",
77                        network_options.container_id
78                    ),
79                }
80            }
81        }
82
83        if !aardvark_entries.is_empty() {
84            // stop dns server first before netavark clears the interface
85            let path = Path::new(&config_dir).join("aardvark-dns");
86
87            let aardvark_interface = Aardvark::new(path, rootless, aardvark_bin, dns_port);
88            if let Err(err) = aardvark_interface.delete_from_netavark_entries(&aardvark_entries) {
89                error_list.push(NetavarkError::wrap("remove aardvark entries", err));
90            }
91        }
92
93        let firewall_driver = firewall::get_supported_firewall_driver(firewall_driver)?;
94
95        let (mut hostns, mut netns) =
96            core_utils::open_netlink_sockets(&self.network_namespace_path)?;
97
98        for (net_name, network) in network_options.network_info.iter() {
99            let per_network_opts = match network_options.networks.get(net_name) {
100                Some(opts) => opts,
101                None => {
102                    error_list.push(NetavarkError::Message(format!(
103                        "network options for network {net_name} not found"
104                    )));
105                    continue;
106                }
107            };
108
109            let driver = match get_network_driver(
110                DriverInfo {
111                    firewall: firewall_driver.as_ref(),
112                    container_id: &network_options.container_id,
113                    container_name: &network_options.container_name,
114                    container_hostname: &network_options.container_hostname,
115                    container_dns_servers: &network_options.dns_servers,
116                    netns_host: hostns.file.as_fd(),
117                    netns_container: netns.file.as_fd(),
118                    netns_path: &self.network_namespace_path,
119                    network,
120                    per_network_opts,
121                    port_mappings: &network_options.port_mappings,
122                    dns_port,
123                    config_dir: Path::new(&config_dir),
124                    rootless,
125                },
126                &plugin_directories,
127            ) {
128                Ok(driver) => driver,
129                Err(err) => {
130                    error_list.push(err);
131                    continue;
132                }
133            };
134
135            match driver.teardown((&mut hostns.netlink, &mut netns.netlink)) {
136                Ok(_) => {}
137                Err(err) => {
138                    error_list.push(err);
139                    continue;
140                }
141            };
142        }
143
144        if !error_list.is_empty() {
145            return Err(NetavarkError::List(error_list));
146        }
147
148        debug!("Teardown complete");
149        Ok(())
150    }
151}