From d72f0d5533f9aea78ab9535a125551ba28570f18 Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Mon, 28 Mar 2022 15:44:40 +0200 Subject: [PATCH] add ipam driver none support Allow creating interfaces only with no ip addresses assigned. Ref https://github.com/containers/common/pull/967 Signed-off-by: Paul Holzinger --- src/network/core.rs | 145 ++++------------------------------ src/network/core_utils.rs | 125 ++++++++++++++++++++++++++++- src/network/types.rs | 5 ++ test/100-bridge-iptables.bats | 47 +++++++++++ test/300-macvlan.bats | 57 ++++++++++++- 5 files changed, 246 insertions(+), 133 deletions(-) diff --git a/src/network/core.rs b/src/network/core.rs index 62d7c2f67..7f2aed151 100644 --- a/src/network/core.rs +++ b/src/network/core.rs @@ -1,4 +1,3 @@ -use crate::network::types::NetAddress; use crate::network::{constants, core_utils, types}; use ipnet; use log::debug; @@ -38,14 +37,6 @@ impl Core { } Some(i) => i, }; - // static ip vector - let mut address_vector = Vec::new(); - // gateway ip vector - let mut gw_ipaddr_vector = Vec::new(); - // network addresses for response - let mut response_net_addresses: Vec = Vec::new(); - // nameservers which can be configured for this container - let mut nameservers: Vec = Vec::new(); // interfaces map, but we only ever expect one, for response let mut interfaces: HashMap = HashMap::new(); @@ -65,85 +56,31 @@ impl Core { }; let container_veth_name: String = per_network_opts.interface_name.to_owned(); - let static_ips = match per_network_opts.static_ips.as_ref() { - None => { - return Err(std::io::Error::new( - std::io::ErrorKind::Other, - "no static ips provided".to_string(), - )) - } - Some(i) => i, - }; let static_mac = match &per_network_opts.static_mac { Some(mac) => mac, None => "", }; - // is ipv6 enabled, we need to propogate this to lower stack - let mut ipv6_enabled = network.ipv6_enabled; - // for dual-stack network.ipv6_enabled could be false do explicit check - for ip in static_ips.iter() { - if ip.is_ipv6() { - ipv6_enabled = true; - break; - } - } - - //we have the bridge name but we must iterate for all the available gateways - for (idx, subnet) in network.subnets.iter().flatten().enumerate() { - let subnet_mask_cidr = subnet.subnet.prefix_len(); - if let Some(gw) = subnet.gateway { - let gw_net = match ipnet::IpNet::new(gw, subnet_mask_cidr) { - Ok(dest) => dest, - Err(err) => { - return Err(std::io::Error::new( - std::io::ErrorKind::Other, - format!( - "failed to parse address {}/{}: {}", - gw, subnet_mask_cidr, err - ), - )) - } - }; - gw_ipaddr_vector.push(gw_net) - } + let ipam = core_utils::get_ipam_addresses(per_network_opts, network)?; - // Build up response information - let container_address: ipnet::IpNet = - match format!("{}/{}", static_ips[idx], subnet_mask_cidr).parse() { - Ok(i) => i, - Err(e) => { - return Err(Error::new(std::io::ErrorKind::Other, e)); - } - }; - // Add the IP to the address_vector - address_vector.push(container_address); - if let Some(gw) = subnet.gateway { - nameservers.push(gw); - } - response_net_addresses.push(types::NetAddress { - gateway: subnet.gateway, - ipnet: container_address, - }); - } debug!("Container veth name: {:?}", container_veth_name); debug!("Brige name: {:?}", bridge_name); - debug!("IP address for veth vector: {:?}", address_vector); - debug!("Gateway ip address vector: {:?}", gw_ipaddr_vector); + debug!("IP address for veth vector: {:?}", ipam.container_addresses); + debug!("Gateway ip address vector: {:?}", ipam.gateway_addresses); // get random name for host veth let host_veth_name = format!("veth{:x}", rand::thread_rng().gen::()); let container_veth_mac = match Core::add_bridge_and_veth( &bridge_name, - address_vector, - gw_ipaddr_vector, + ipam.container_addresses, + ipam.gateway_addresses, static_mac, &container_veth_name, &host_veth_name, netns, mtu_config, - ipv6_enabled, + ipam.ipv6_enabled, ) { Ok(addr) => addr, Err(err) => { @@ -156,13 +93,13 @@ impl Core { debug!("Container veth mac: {:?}", container_veth_mac); let interface = types::NetInterface { mac_address: container_veth_mac, - subnets: Option::from(response_net_addresses), + subnets: Option::from(ipam.net_addresses), }; // Add interface to interfaces (part of StatusBlock) interfaces.insert(container_veth_name, interface); let _ = response.interfaces.insert(interfaces); if network.dns_enabled { - let _ = response.dns_server_ips.insert(nameservers); + let _ = response.dns_server_ips.insert(ipam.nameservers); // Note: this is being added so podman setup is backward compatible with the design // which we had with dnsname/dnsmasq. I belive this can be fixed in later releases. let _ = response @@ -353,75 +290,27 @@ impl Core { Some(interface) => interface.to_string(), }; - // static ip vector - let mut address_vector = Vec::new(); - // gateway ip vector - let mut gw_ipaddr_vector = Vec::new(); - // network addresses for response - let mut response_net_addresses: Vec = Vec::new(); // interfaces map, but we only ever expect one, for response let mut interfaces: HashMap = HashMap::new(); - let container_macvlan_name: String = per_network_opts.interface_name.to_owned(); - let static_ips = match per_network_opts.static_ips.as_ref() { - None => { - return Err(std::io::Error::new( - std::io::ErrorKind::Other, - "no static ips provided", - )) - } - Some(i) => i.clone(), - }; - - // prepare a vector of static aps with appropriate cidr - for (idx, subnet) in network.subnets.iter().flatten().enumerate() { - let subnet_mask_cidr = subnet.subnet.prefix_len(); - // Only add gateway to route if macvlan is not marked as an internal network - if !network.internal { - if let Some(gw) = subnet.gateway { - let gw_net = match ipnet::IpNet::new(gw, subnet_mask_cidr) { - Ok(dest) => dest, - Err(err) => { - return Err(std::io::Error::new( - std::io::ErrorKind::Other, - format!( - "failed to parse address {}/{}: {}", - gw, subnet_mask_cidr, err - ), - )) - } - }; - gw_ipaddr_vector.push(gw_net) - } - } - - // Build up response information - let container_address: ipnet::IpNet = - match format!("{}/{}", static_ips[idx], subnet_mask_cidr).parse() { - Ok(i) => i, - Err(e) => { - return Err(Error::new(std::io::ErrorKind::Other, e)); - } - }; - // Add the IP to the address_vector - address_vector.push(container_address); - response_net_addresses.push(types::NetAddress { - gateway: subnet.gateway, - ipnet: container_address, - }); + let mut ipam = core_utils::get_ipam_addresses(per_network_opts, network)?; + // Remove gateways when marked as internal network + if network.internal { + ipam.gateway_addresses = Vec::new(); } + debug!("Container macvlan name: {:?}", container_macvlan_name); debug!("Master interface name: {:?}", master_ifname); - debug!("IP address for macvlan: {:?}", address_vector); + debug!("IP address for macvlan: {:?}", ipam.container_addresses); // create macvlan let container_macvlan_mac = match Core::add_macvlan( &master_ifname, &container_macvlan_name, - gw_ipaddr_vector, + ipam.gateway_addresses, macvlan_mode, mtu_config, - address_vector, + ipam.container_addresses, netns, ) { Ok(addr) => addr, @@ -435,7 +324,7 @@ impl Core { debug!("Container macvlan mac: {:?}", container_macvlan_mac); let interface = types::NetInterface { mac_address: container_macvlan_mac, - subnets: Option::from(response_net_addresses), + subnets: Option::from(ipam.net_addresses), }; // Add interface to interfaces (part of StatusBlock) interfaces.insert(container_macvlan_name, interface); diff --git a/src/network/core_utils.rs b/src/network/core_utils.rs index 1e580b5a8..dabef2b6f 100644 --- a/src/network/core_utils.rs +++ b/src/network/core_utils.rs @@ -1,4 +1,4 @@ -use crate::network::constants; +use crate::network::{constants, types}; use futures::stream::TryStreamExt; use futures::StreamExt; use libc; @@ -25,6 +25,129 @@ pub struct CoreUtils { pub networkns: String, } +pub struct IPAMAddresses { + // ip addresses for netlink + pub container_addresses: Vec, + pub gateway_addresses: Vec, + pub ipv6_enabled: bool, + // result for podman + pub net_addresses: Vec, + pub nameservers: Vec, +} + +const IPAM_HOSTLOCAL: &str = "host-local"; +const IPAM_DHCP: &str = "dhcp"; +const IPAM_NONE: &str = "none"; + +pub fn get_ipam_addresses( + per_network_opts: &types::PerNetworkOptions, + network: &types::Network, +) -> Result { + let addresses = match network + .ipam_options + .as_ref() + .and_then(|map| map.get("driver").cloned()) + .as_deref() + { + // when option is none default to host local + Some(IPAM_HOSTLOCAL) | None => { + // static ip vector + let mut container_addresses = Vec::new(); + // gateway ip vector + let mut gateway_addresses = Vec::new(); + // network addresses for response + let mut net_addresses: Vec = Vec::new(); + // bool for ipv6 + let mut ipv6_enabled = false; + + // nameservers which can be configured for this container + let mut nameservers: Vec = Vec::new(); + + let static_ips = match per_network_opts.static_ips.as_ref() { + None => { + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + "no static ips provided", + )) + } + Some(i) => i, + }; + + // prepare a vector of static aps with appropriate cidr + for (idx, subnet) in network.subnets.iter().flatten().enumerate() { + let subnet_mask_cidr = subnet.subnet.prefix_len(); + if let Some(gw) = subnet.gateway { + let gw_net = match ipnet::IpNet::new(gw, subnet_mask_cidr) { + Ok(dest) => dest, + Err(err) => { + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + format!( + "failed to parse address {}/{}: {}", + gw, subnet_mask_cidr, err + ), + )) + } + }; + gateway_addresses.push(gw_net); + nameservers.push(gw); + } + + // for dual-stack network.ipv6_enabled could be false do explicit check + if subnet.subnet.addr().is_ipv6() { + ipv6_enabled = true; + } + + // Build up response information + let container_address: ipnet::IpNet = + match format!("{}/{}", static_ips[idx], subnet_mask_cidr).parse() { + Ok(i) => i, + Err(e) => { + return Err(Error::new(std::io::ErrorKind::Other, e)); + } + }; + // Add the IP to the address_vector + container_addresses.push(container_address); + net_addresses.push(types::NetAddress { + gateway: subnet.gateway, + ipnet: container_address, + }); + } + IPAMAddresses { + container_addresses, + gateway_addresses, + net_addresses, + nameservers, + ipv6_enabled, + } + } + Some(IPAM_NONE) => { + // no ipam just return empty vectors + IPAMAddresses { + container_addresses: vec![], + gateway_addresses: vec![], + net_addresses: vec![], + nameservers: vec![], + ipv6_enabled: false, + } + } + Some(IPAM_DHCP) => { + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + "dhcp ipam driver is not yet supported", + )); + } + Some(driver) => { + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + format!("unsupported ipam driver {}", driver), + )); + } + }; + + Ok(addresses) +} + impl CoreUtils { fn encode_address_to_hex(bytes: &[u8]) -> String { let address: String = bytes diff --git a/src/network/types.rs b/src/network/types.rs index 3545bf836..8c681f0f9 100644 --- a/src/network/types.rs +++ b/src/network/types.rs @@ -41,6 +41,11 @@ pub struct Network { #[serde(rename = "options")] pub options: Option>, + /// IPAM options is a set of key-value options that have been applied to + /// the Network. + #[serde(rename = "ipam_options")] + pub ipam_options: Option>, + /// Subnets to use for this network. #[serde(rename = "subnets")] pub subnets: Option>, diff --git a/test/100-bridge-iptables.bats b/test/100-bridge-iptables.bats index 71b319eac..156ff61a3 100644 --- a/test/100-bridge-iptables.bats +++ b/test/100-bridge-iptables.bats @@ -243,3 +243,50 @@ fw_driver=iptables add_dummy_interface_on_host dummy0 "fd65:8371:648b:0c06::1/64" test_port_fw ip=6 proto=udp hostip="fd65:8371:648b:0c06::1" } + +@test "bridge ipam none" { + read -r -d '\0' config <