From 818a19f8508681857420ce6ed2bc61a5de1933fe Mon Sep 17 00:00:00 2001 From: Michael Zimmermann Date: Mon, 11 Nov 2024 22:29:00 +0100 Subject: [PATCH 1/2] network: add support for network-specific options Signed-off-by: Michael Zimmermann --- src/network/types.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/network/types.rs b/src/network/types.rs index 9c3659f80..0e82bee12 100644 --- a/src/network/types.rs +++ b/src/network/types.rs @@ -112,6 +112,10 @@ pub struct PerNetworkOptions { /// MAC address for the container interface. #[serde(rename = "static_mac")] pub static_mac: Option, + + /// Driver-specific options for this container. + #[serde(rename = "options")] + pub options: Option>, } /// PortMapping is one or more ports that will be mapped into the container. From dc99b01fcf96b372f58dec3d0128338eaa94611f Mon Sep 17 00:00:00 2001 From: Michael Zimmermann Date: Mon, 11 Nov 2024 22:29:43 +0100 Subject: [PATCH 2/2] network: bridge: add support for host_interface_name option Fixes containers/netavark#24523 Signed-off-by: Michael Zimmermann --- src/network/bridge.rs | 30 +++++++++++++++----- src/network/constants.rs | 1 + test/610-bridge-vethname.bats | 30 ++++++++++++++++++++ test/testfiles/bridge-vethname-exists.json | 32 ++++++++++++++++++++++ test/testfiles/bridge-vethname.json | 32 ++++++++++++++++++++++ 5 files changed, 118 insertions(+), 7 deletions(-) create mode 100644 test/610-bridge-vethname.bats create mode 100644 test/testfiles/bridge-vethname-exists.json create mode 100644 test/testfiles/bridge-vethname.json diff --git a/src/network/bridge.rs b/src/network/bridge.rs index cc003f926..ce89427a8 100644 --- a/src/network/bridge.rs +++ b/src/network/bridge.rs @@ -20,8 +20,8 @@ use crate::{ use super::{ constants::{ ISOLATE_OPTION_FALSE, ISOLATE_OPTION_STRICT, ISOLATE_OPTION_TRUE, - NO_CONTAINER_INTERFACE_ERROR, OPTION_ISOLATE, OPTION_METRIC, OPTION_MTU, - OPTION_NO_DEFAULT_ROUTE, OPTION_VRF, + NO_CONTAINER_INTERFACE_ERROR, OPTION_HOST_INTERFACE_NAME, OPTION_ISOLATE, OPTION_METRIC, + OPTION_MTU, OPTION_NO_DEFAULT_ROUTE, OPTION_VRF, }, core_utils::{self, get_ipam_addresses, join_netns, parse_option, CoreUtils}, driver::{self, DriverInfo}, @@ -38,6 +38,8 @@ const NO_BRIDGE_NAME_ERROR: &str = "no bridge interface name given"; struct InternalData { /// interface name of the veth pair inside the container netns container_interface_name: String, + /// interace name of the veth pair in the host netns + host_interface_name: String, /// interface name of the bridge for on the host bridge_interface_name: String, /// static mac address @@ -86,6 +88,11 @@ impl driver::NetworkDriver for Bridge<'_> { let no_default_route: bool = parse_option(&self.info.network.options, OPTION_NO_DEFAULT_ROUTE)?.unwrap_or(false); let vrf: Option = parse_option(&self.info.network.options, OPTION_VRF)?; + let host_interface_name = parse_option( + &self.info.per_network_opts.options, + OPTION_HOST_INTERFACE_NAME, + )? + .unwrap_or_else(|| "".to_string()); let static_mac = match &self.info.per_network_opts.static_mac { Some(mac) => Some(CoreUtils::decode_address_from_hex(mac)?), @@ -95,6 +102,7 @@ impl driver::NetworkDriver for Bridge<'_> { self.data = Some(InternalData { bridge_interface_name: bridge_name, container_interface_name: self.info.per_network_opts.interface_name.clone(), + host_interface_name, mac_address: static_mac, ipam, mtu, @@ -647,17 +655,25 @@ fn create_veth_pair<'fd>( let mut peer = LinkMessage::default(); netlink::parse_create_link_options(&mut peer, peer_opts); - let mut host_veth = netlink::CreateLinkOptions::new(String::from(""), InfoKind::Veth); + let mut host_veth = + netlink::CreateLinkOptions::new(data.host_interface_name.clone(), InfoKind::Veth); host_veth.mtu = data.mtu; host_veth.primary_index = primary_index; host_veth.info_data = Some(InfoData::Veth(InfoVeth::Peer(peer))); host.create_link(host_veth).map_err(|err| match err { NetavarkError::Netlink(ref e) if -e.raw_code() == libc::EEXIST => NetavarkError::wrap( - format!( - "create veth pair: interface {} already exists on container namespace", - data.container_interface_name - ), + if data.host_interface_name.is_empty() { + format!( + "create veth pair: interface {} already exists on container namespace", + data.container_interface_name + ) + } else { + format!( + "create veth pair: interface {} already exists on container namespace or {} exists on host namespace", + data.container_interface_name, data.host_interface_name, + ) + }, err, ), _ => NetavarkError::wrap("create veth pair", err), diff --git a/src/network/constants.rs b/src/network/constants.rs index e4dc34d6d..8c7b7a656 100644 --- a/src/network/constants.rs +++ b/src/network/constants.rs @@ -22,6 +22,7 @@ pub const OPTION_METRIC: &str = "metric"; pub const OPTION_NO_DEFAULT_ROUTE: &str = "no_default_route"; pub const OPTION_BCLIM: &str = "bclim"; pub const OPTION_VRF: &str = "vrf"; +pub const OPTION_HOST_INTERFACE_NAME: &str = "host_interface_name"; /// 100 is the default metric for most Linux networking tools. pub const DEFAULT_METRIC: u32 = 100; diff --git a/test/610-bridge-vethname.bats b/test/610-bridge-vethname.bats new file mode 100644 index 000000000..563e2827f --- /dev/null +++ b/test/610-bridge-vethname.bats @@ -0,0 +1,30 @@ +#!/usr/bin/env bats -*- bats -*- +# +# bridge driver tests with static veth names +# + +load helpers + +@test bridge - valid veth name { + run_netavark --file ${TESTSDIR}/testfiles/bridge-vethname.json setup $(get_container_netns_path) + + run_in_host_netns ip -j --details link show my-veth + link_info="$output" + assert_json "$link_info" '.[].flags[] | select(.=="UP")' == "UP" "Host veth is up" + assert_json "$link_info" ".[].linkinfo.info_kind" == "veth" "The veth interface is actually a veth" + assert_json "$link_info" ".[].master" "==" "podman0" "veth is part of the correct bridge" + + run_netavark --file ${TESTSDIR}/testfiles/bridge-vethname.json teardown $(get_container_netns_path) + + # check if the interface gets removed + expected_rc=1 run_in_host_netns ip -j --details link show my-veth + assert "$output" "==" 'Device "my-veth" does not exist.' +} + +@test bridge - existing veth name { + expected_rc=1 run_netavark --file ${TESTSDIR}/testfiles/bridge-vethname-exists.json setup $(get_container_netns_path) + assert_json ".error" "create veth pair: interface eth0 already exists on container namespace or podman0 exists on host namespace: Netlink error: File exists (os error 17)" + + expected_rc=1 run_in_host_netns ip -j --details link show my-veth + assert "$output" "==" 'Device "my-veth" does not exist.' +} diff --git a/test/testfiles/bridge-vethname-exists.json b/test/testfiles/bridge-vethname-exists.json new file mode 100644 index 000000000..736560d3c --- /dev/null +++ b/test/testfiles/bridge-vethname-exists.json @@ -0,0 +1,32 @@ +{ + "container_id": "6ce776ea58b5", + "container_name": "testcontainer", + "networks": { + "podman": { + "interface_name": "eth0", + "static_ips": [ + "10.88.0.2" + ], + "options": { + "host_interface_name": "podman0" + } + } + }, + "network_info": { + "podman": { + "dns_enabled": false, + "driver": "bridge", + "id": "53ce4390f2adb1681eb1a90ec8b48c49c015e0a8d336c197637e7f65e365fa9e", + "internal": false, + "ipv6_enabled": false, + "name": "podman", + "network_interface": "podman0", + "subnets": [ + { + "gateway": "10.88.0.1", + "subnet": "10.88.0.0/16" + } + ] + } + } +} diff --git a/test/testfiles/bridge-vethname.json b/test/testfiles/bridge-vethname.json new file mode 100644 index 000000000..2b3b174e2 --- /dev/null +++ b/test/testfiles/bridge-vethname.json @@ -0,0 +1,32 @@ +{ + "container_id": "6ce776ea58b5", + "container_name": "testcontainer", + "networks": { + "podman": { + "interface_name": "eth0", + "static_ips": [ + "10.88.0.2" + ], + "options": { + "host_interface_name": "my-veth" + } + } + }, + "network_info": { + "podman": { + "dns_enabled": false, + "driver": "bridge", + "id": "53ce4390f2adb1681eb1a90ec8b48c49c015e0a8d336c197637e7f65e365fa9e", + "internal": false, + "ipv6_enabled": false, + "name": "podman", + "network_interface": "podman0", + "subnets": [ + { + "gateway": "10.88.0.1", + "subnet": "10.88.0.0/16" + } + ] + } + } +}