diff --git a/src/network/bridge.rs b/src/network/bridge.rs index cc003f92..ce89427a 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 e4dc34d6..8c7b7a65 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 00000000..91f52256 --- /dev/null +++ b/test/610-bridge-vethname.bats @@ -0,0 +1,26 @@ +#!/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 +} + +@test bridge - existing veth name { + expected_rc=1 run_netavark --file ${TESTSDIR}/testfiles/bridge-vethname-exists.json setup $(get_container_netns_path) + expected_rc=1 run_in_host_netns ip -j --details link show my-veth +} diff --git a/test/testfiles/bridge-vethname-exists.json b/test/testfiles/bridge-vethname-exists.json new file mode 100644 index 00000000..736560d3 --- /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 00000000..2b3b174e --- /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" + } + ] + } + } +}