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.
Also combine some duplicated code for bridge/macvlan.

Ref containers/common#967

Signed-off-by: Paul Holzinger <[email protected]>
  • Loading branch information
Luap99 committed Mar 29, 2022
1 parent 4e2f6af commit e880a5d
Show file tree
Hide file tree
Showing 7 changed files with 278 additions and 133 deletions.
5 changes: 5 additions & 0 deletions src/network/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,8 @@ pub const MACVLAN_MODE_VEPA: u32 = 2;
pub const MACVLAN_MODE_BRIDGE: u32 = 4;
pub const MACVLAN_MODE_PASSTHRU: u32 = 8;
pub const MACVLAN_MODE_SOURCE: u32 = 16;

// IPAM drivers
pub const IPAM_HOSTLOCAL: &str = "host-local";
pub const IPAM_DHCP: &str = "dhcp";
pub const IPAM_NONE: &str = "none";
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
111 changes: 110 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, internal_types, types};
use futures::stream::TryStreamExt;
use futures::StreamExt;
use libc;
Expand All @@ -25,6 +25,115 @@ pub struct CoreUtils {
pub networkns: String,
}

pub fn get_ipam_addresses(
per_network_opts: &types::PerNetworkOptions,
network: &types::Network,
) -> Result<internal_types::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(constants::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,
});
}
internal_types::IPAMAddresses {
container_addresses,
gateway_addresses,
net_addresses,
nameservers,
ipv6_enabled,
}
}
Some(constants::IPAM_NONE) => {
// no ipam just return empty vectors
internal_types::IPAMAddresses {
container_addresses: vec![],
gateway_addresses: vec![],
net_addresses: vec![],
nameservers: vec![],
ipv6_enabled: false,
}
}
Some(constants::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
11 changes: 11 additions & 0 deletions src/network/internal_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,14 @@ pub struct PortForwardConfig {
// Must be set if the v6 address is set.
pub subnet_v6: Option<Subnet>,
}

/// IPAMAddresses is used to pass ipam information around
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>,
}
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 e880a5d

Please sign in to comment.