Skip to content

Commit

Permalink
add ipam driver none support
Browse files Browse the repository at this point in the history
Allow creating interfaces only with no ip addresses assigned.
I combined the macvlan and bridge code to prevent unessesary duplication
and bugs.

Ref containers/common#967

Signed-off-by: Paul Holzinger <[email protected]>
  • Loading branch information
Luap99 committed Mar 28, 2022
1 parent 4e2f6af commit c8f869d
Show file tree
Hide file tree
Showing 5 changed files with 276 additions and 133 deletions.
145 changes: 17 additions & 128 deletions src/network/core.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use crate::network::types::NetAddress;
use crate::network::{constants, core_utils, types};
use ipnet;
use log::debug;
Expand Down Expand Up @@ -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<NetAddress> = Vec::new();
// nameservers which can be configured for this container
let mut nameservers: Vec<IpAddr> = Vec::new();
// interfaces map, but we only ever expect one, for response
let mut interfaces: HashMap<String, types::NetInterface> = HashMap::new();

Expand All @@ -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::<u32>());

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) => {
Expand All @@ -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
Expand Down Expand Up @@ -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<NetAddress> = Vec::new();
// interfaces map, but we only ever expect one, for response
let mut interfaces: HashMap<String, types::NetInterface> = 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,
Expand All @@ -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);
Expand Down
125 changes: 124 additions & 1 deletion src/network/core_utils.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::network::constants;
use crate::network::{constants, types};
use futures::stream::TryStreamExt;
use futures::StreamExt;
use libc;
Expand All @@ -25,6 +25,129 @@ pub struct CoreUtils {
pub networkns: String,
}

pub struct IPAMAddresses {
// ip addresses for netlink
pub container_addresses: Vec<ipnet::IpNet>,
pub gateway_addresses: Vec<ipnet::IpNet>,
pub ipv6_enabled: bool,
// result for podman
pub net_addresses: Vec<types::NetAddress>,
pub nameservers: Vec<IpAddr>,
}

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<IPAMAddresses, std::io::Error> {
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<types::NetAddress> = Vec::new();
// bool for ipv6
let mut ipv6_enabled = false;

// nameservers which can be configured for this container
let mut nameservers: Vec<IpAddr> = 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
Expand Down
5 changes: 5 additions & 0 deletions src/network/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ pub struct Network {
#[serde(rename = "options")]
pub options: Option<HashMap<String, String>>,

/// IPAM options is a set of key-value options that have been applied to
/// the Network.
#[serde(rename = "ipam_options")]
pub ipam_options: Option<HashMap<String, String>>,

/// Subnets to use for this network.
#[serde(rename = "subnets")]
pub subnets: Option<Vec<Subnet>>,
Expand Down
Loading

0 comments on commit c8f869d

Please sign in to comment.