diff --git a/src/network/bridge.rs b/src/network/bridge.rs index ce89427a..457713fb 100644 --- a/src/network/bridge.rs +++ b/src/network/bridge.rs @@ -21,7 +21,7 @@ use super::{ constants::{ ISOLATE_OPTION_FALSE, ISOLATE_OPTION_STRICT, ISOLATE_OPTION_TRUE, NO_CONTAINER_INTERFACE_ERROR, OPTION_HOST_INTERFACE_NAME, OPTION_ISOLATE, OPTION_METRIC, - OPTION_MTU, OPTION_NO_DEFAULT_ROUTE, OPTION_VRF, + OPTION_MODE, OPTION_MTU, OPTION_NO_DEFAULT_ROUTE, OPTION_VRF, }, core_utils::{self, get_ipam_addresses, join_netns, parse_option, CoreUtils}, driver::{self, DriverInfo}, @@ -35,6 +35,12 @@ use super::{ const NO_BRIDGE_NAME_ERROR: &str = "no bridge interface name given"; +#[derive(Clone, Copy, PartialEq)] +enum BridgeMode { + L2, + L3, +} + struct InternalData { /// interface name of the veth pair inside the container netns container_interface_name: String, @@ -52,6 +58,8 @@ struct InternalData { isolate: IsolateOption, /// Route metric for any default routes added for the network metric: Option, + /// Management mode of the bridge. + mode: BridgeMode, /// if set, no default gateway will be added no_default_route: bool, /// sef vrf for bridge @@ -82,6 +90,7 @@ impl driver::NetworkDriver for Bridge<'_> { } let ipam = get_ipam_addresses(self.info.per_network_opts, self.info.network)?; + let mode: Option = parse_option(&self.info.network.options, OPTION_MODE)?; let mtu: u32 = parse_option(&self.info.network.options, OPTION_MTU)?.unwrap_or(0); let isolate: IsolateOption = get_isolate_option(&self.info.network.options)?; let metric: u32 = parse_option(&self.info.network.options, OPTION_METRIC)?.unwrap_or(100); @@ -108,6 +117,7 @@ impl driver::NetworkDriver for Bridge<'_> { mtu, isolate, metric: Some(metric), + mode: get_bridge_mode_from_string(mode.as_deref())?, no_default_route, vrf, }); @@ -133,9 +143,11 @@ impl driver::NetworkDriver for Bridge<'_> { data.bridge_interface_name, data.ipam.gateway_addresses ); - setup_ipv4_fw_sysctl()?; - if data.ipam.ipv6_enabled { - setup_ipv6_fw_sysctl()?; + if let BridgeMode::L3 = data.mode { + setup_ipv4_fw_sysctl()?; + if data.ipam.ipv6_enabled { + setup_ipv6_fw_sysctl()?; + } } let (host_sock, netns_sock) = netlink_sockets; @@ -225,7 +237,7 @@ impl driver::NetworkDriver for Bridge<'_> { }; // if the network is internal block routing and do not setup firewall rules - if self.info.network.internal { + if self.info.network.internal && data.mode == BridgeMode::L3 { CoreUtils::apply_sysctl_value( format!( "/proc/sys/net/ipv4/conf/{}/forwarding", @@ -246,7 +258,9 @@ impl driver::NetworkDriver for Bridge<'_> { return Ok((response, aardvark_entry)); } - self.setup_firewall(data)?; + if let BridgeMode::L3 = data.mode { + self.setup_firewall(data)?; + } Ok((response, aardvark_entry)) } @@ -255,6 +269,8 @@ impl driver::NetworkDriver for Bridge<'_> { &self, netlink_sockets: (&mut netlink::Socket, &mut netlink::Socket), ) -> NetavarkResult<()> { + let mode: Option = 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(); @@ -271,6 +287,7 @@ impl driver::NetworkDriver for Bridge<'_> { let complete_teardown = match remove_link( host_sock, netns_sock, + mode, &bridge_name, &self.info.per_network_opts.interface_name, ) { @@ -288,12 +305,14 @@ impl driver::NetworkDriver for Bridge<'_> { return Ok(()); } - match self.teardown_firewall(complete_teardown, bridge_name) { - Ok(_) => {} - Err(err) => { - error_list.push(err); - } - }; + if let BridgeMode::L3 = mode { + match self.teardown_firewall(complete_teardown, bridge_name) { + Ok(_) => {} + Err(err) => { + error_list.push(err); + } + }; + } if !error_list.is_empty() { return Err(NetavarkError::List(error_list)); @@ -550,6 +569,11 @@ fn create_interfaces( // for all other errors we want to return the error return Err(err).wrap("get bridge interface"); } + + if let BridgeMode::L2 = data.mode { + return Err(err).wrap("l2 bridge interface not found"); + } + let mut create_link_opts = netlink::CreateLinkOptions::new( data.bridge_interface_name.to_string(), InfoKind::Bridge, @@ -703,41 +727,44 @@ fn create_veth_pair<'fd>( )); } - exec_netns!(hostns_fd, netns_fd, res, { - disable_ipv6_autoconf(&data.container_interface_name)?; - if data.ipam.ipv6_enabled { - // Disable dad inside the container too - let disable_dad_in_container = format!( - "/proc/sys/net/ipv6/conf/{}/accept_dad", + if let BridgeMode::L3 = data.mode { + exec_netns!(hostns_fd, netns_fd, res, { + disable_ipv6_autoconf(&data.container_interface_name)?; + if data.ipam.ipv6_enabled { + // Disable dad inside the container too + let disable_dad_in_container = format!( + "/proc/sys/net/ipv6/conf/{}/accept_dad", + &data.container_interface_name + ); + core_utils::CoreUtils::apply_sysctl_value(disable_dad_in_container, "0")?; + } + let enable_arp_notify = format!( + "/proc/sys/net/ipv4/conf/{}/arp_notify", &data.container_interface_name ); - core_utils::CoreUtils::apply_sysctl_value(disable_dad_in_container, "0")?; - } - let enable_arp_notify = format!( - "/proc/sys/net/ipv4/conf/{}/arp_notify", - &data.container_interface_name - ); - core_utils::CoreUtils::apply_sysctl_value(enable_arp_notify, "1")?; + core_utils::CoreUtils::apply_sysctl_value(enable_arp_notify, "1")?; - // disable strict reverse path search validation - let rp_filter = format!( - "/proc/sys/net/ipv4/conf/{}/rp_filter", - &data.container_interface_name - ); - CoreUtils::apply_sysctl_value(rp_filter, "2")?; - Ok::<(), NetavarkError>(()) - }); - // check the result and return error - res?; - - if data.ipam.ipv6_enabled { - let host_veth = host.get_link(netlink::LinkID::ID(host_link))?; + // disable strict reverse path search validation + let rp_filter = format!( + "/proc/sys/net/ipv4/conf/{}/rp_filter", + &data.container_interface_name + ); + CoreUtils::apply_sysctl_value(rp_filter, "2")?; + Ok::<(), NetavarkError>(()) + }); + // check the result and return error + res?; - for nla in host_veth.attributes.into_iter() { - if let LinkAttribute::IfName(name) = nla { - // Disable dad inside on the host too - let disable_dad_in_container = format!("/proc/sys/net/ipv6/conf/{name}/accept_dad"); - core_utils::CoreUtils::apply_sysctl_value(disable_dad_in_container, "0")?; + if data.ipam.ipv6_enabled { + let host_veth = host.get_link(netlink::LinkID::ID(host_link))?; + + for nla in host_veth.attributes.into_iter() { + if let LinkAttribute::IfName(name) = nla { + // Disable dad inside on the host too + let disable_dad_in_container = + format!("/proc/sys/net/ipv6/conf/{name}/accept_dad"); + core_utils::CoreUtils::apply_sysctl_value(disable_dad_in_container, "0")?; + } } } } @@ -830,6 +857,7 @@ fn check_link_is_vrf(msg: LinkMessage, vrf_name: &str) -> NetavarkResult NetavarkResult { @@ -847,7 +875,7 @@ fn remove_link( .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 links.is_empty() && mode == BridgeMode::L3 { log::info!("removing bridge {}", br_name); host.del_link(netlink::LinkID::ID(br.header.index)) .wrap(format!("failed to delete bridge {container_veth_name}"))?; @@ -866,3 +894,14 @@ fn get_isolate_option(opts: &Option>) -> NetavarkResult< _ => IsolateOption::Never, }) } + +fn get_bridge_mode_from_string(mode: Option<&str>) -> NetavarkResult { + match mode { + // default to l3 when unset + None | Some("") | Some("l3") => Ok(BridgeMode::L3), + Some("l2") => Ok(BridgeMode::L2), + Some(name) => Err(NetavarkError::msg(format!( + "invalid bridge mode \"{name}\"" + ))), + } +}