Skip to content

Commit

Permalink
Merge pull request #1159 from Luap99/bridge-dhcp
Browse files Browse the repository at this point in the history
bridge: support DHCP ipam driver
  • Loading branch information
openshift-merge-bot[bot] authored Jan 29, 2025
2 parents 56a0d3f + f57ae66 commit 8f7024b
Show file tree
Hide file tree
Showing 7 changed files with 117 additions and 44 deletions.
43 changes: 41 additions & 2 deletions src/network/bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use netlink_packet_route::link::{
LinkMessage,
};

use crate::network::dhcp::{dhcp_teardown, get_dhcp_lease};
use crate::{
dns::aardvark::AardvarkEntry,
error::{ErrorWrap, NetavarkError, NetavarkErrorList, NetavarkResult},
Expand Down Expand Up @@ -113,6 +114,18 @@ impl driver::NetworkDriver for Bridge<'_> {
None => None,
};

let mode = get_bridge_mode_from_string(mode.as_deref())?;

// Cannot chain both conditions with "&&"
// until https://github.com/rust-lang/rust/issues/53667 is stable
if ipam.dhcp_enabled {
if let BridgeMode::Managed = mode {
return Err(NetavarkError::msg(
"cannot use dhcp ipam driver without using the option mode=unmanaged",
));
}
}

self.data = Some(InternalData {
bridge_interface_name: bridge_name,
container_interface_name: self.info.per_network_opts.interface_name.clone(),
Expand All @@ -122,7 +135,7 @@ impl driver::NetworkDriver for Bridge<'_> {
mtu,
isolate,
metric: Some(metric),
mode: get_bridge_mode_from_string(mode.as_deref())?,
mode,
no_default_route,
vrf,
vlan,
Expand Down Expand Up @@ -178,9 +191,33 @@ impl driver::NetworkDriver for Bridge<'_> {
// interfaces map, but we only ever expect one, for response
let mut interfaces: HashMap<String, types::NetInterface> = HashMap::new();

// if dhcp is enabled, we need to call the dhcp proxy to perform
// a dhcp lease. it will also perform the IP address assignment
// to the container interface.
let subnets = if data.ipam.dhcp_enabled {
let (subnets, dns_servers, domain_name) = get_dhcp_lease(
&data.bridge_interface_name,
&data.container_interface_name,
self.info.netns_path,
&container_veth_mac,
self.info.container_hostname.as_deref().unwrap_or(""),
self.info.container_id,
)?;
// do not overwrite dns servers set by dns podman flag
if !self.info.container_dns_servers.is_some() {
response.dns_server_ips = dns_servers;
}
if domain_name.is_some() {
response.dns_search_domains = domain_name;
}
subnets
} else {
data.ipam.net_addresses.clone()
};

let interface = types::NetInterface {
mac_address: container_veth_mac,
subnets: Option::from(data.ipam.net_addresses.clone()),
subnets: Option::from(subnets),
};
// Add interface to interfaces (part of StatusBlock)
interfaces.insert(data.container_interface_name.clone(), interface);
Expand Down Expand Up @@ -278,6 +315,8 @@ impl driver::NetworkDriver for Bridge<'_> {

let mut error_list = NetavarkErrorList::new();

dhcp_teardown(&self.info, netns_sock)?;

let routes = core_utils::create_route_list(&self.info.network.routes)?;
for route in routes.iter() {
netns_sock
Expand Down
13 changes: 13 additions & 0 deletions src/network/core_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ use sysctl::{Sysctl, SysctlError};

use super::netlink;

use netlink_packet_route::link::LinkAttribute;

pub struct CoreUtils {
pub networkns: String,
}
Expand Down Expand Up @@ -435,3 +437,14 @@ pub fn disable_ipv6_autoconf(if_name: &str) -> NetavarkResult<()> {
};
Ok(())
}

pub fn get_mac_address(v: Vec<LinkAttribute>) -> NetavarkResult<String> {
for nla in v.into_iter() {
if let LinkAttribute::Address(ref addr) = nla {
return Ok(CoreUtils::encode_address_to_hex(addr));
}
}
Err(NetavarkError::msg(
"failed to get the the container mac address",
))
}
28 changes: 27 additions & 1 deletion src/network/macvlan_dhcp.rs → src/network/dhcp.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::error::{NetavarkError, NetavarkResult};
use crate::error::{ErrorWrap, NetavarkError, NetavarkResult};
use crate::network::types::NetAddress;
use ipnet::IpNet;
use std::net::IpAddr;
Expand All @@ -7,6 +7,9 @@ use std::str::FromStr;
use crate::dhcp_proxy::lib::g_rpc::NetworkConfig;
use crate::dhcp_proxy::proxy_conf::DEFAULT_UDS_PATH;

use super::driver::DriverInfo;
use super::{core_utils, netlink};

pub type DhcpLeaseInfo = (Vec<NetAddress>, Option<Vec<IpAddr>>, Option<Vec<String>>);

/// dhcp performs the connection to the nv-proxy over grpc where it
Expand Down Expand Up @@ -155,3 +158,26 @@ pub fn release_dhcp_lease(
};
Ok(())
}

pub fn dhcp_teardown(info: &DriverInfo, sock: &mut netlink::Socket) -> NetavarkResult<()> {
let ipam = core_utils::get_ipam_addresses(info.per_network_opts, info.network)?;
let if_name = info.per_network_opts.interface_name.clone();

// If we are using DHCP, we need to at least call to the proxy so that
// the proxy's cache can get updated and the current lease can be released.
if ipam.dhcp_enabled {
let dev = sock.get_link(netlink::LinkID::Name(if_name)).wrap(format!(
"get container interface {}",
&info.per_network_opts.interface_name
))?;

let container_mac_address = core_utils::get_mac_address(dev.attributes)?;
release_dhcp_lease(
&info.network.network_interface.clone().unwrap_or_default(),
&info.per_network_opts.interface_name,
info.netns_path,
&container_mac_address,
)?
}
Ok(())
}
2 changes: 1 addition & 1 deletion src/network/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ use crate::{
pub mod bridge;
pub mod constants;
pub mod core_utils;
mod dhcp;
pub mod driver;
pub mod internal_types;
mod macvlan_dhcp;
pub mod netlink;
pub mod plugin;
pub mod vlan;
Expand Down
43 changes: 3 additions & 40 deletions src/network/vlan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use netlink_packet_route::link::{
};
use rand::distributions::{Alphanumeric, DistString};

use crate::network::macvlan_dhcp::{get_dhcp_lease, release_dhcp_lease};
use crate::network::dhcp::{dhcp_teardown, get_dhcp_lease};
use crate::{
dns::aardvark::AardvarkEntry,
error::{ErrorWrap, NetavarkError, NetavarkResult},
Expand All @@ -20,7 +20,7 @@ use super::{
NO_CONTAINER_INTERFACE_ERROR, OPTION_BCLIM, OPTION_METRIC, OPTION_MODE, OPTION_MTU,
OPTION_NO_DEFAULT_ROUTE,
},
core_utils::{self, get_ipam_addresses, parse_option, CoreUtils},
core_utils::{self, get_ipam_addresses, get_mac_address, parse_option, CoreUtils},
driver::{self, DriverInfo},
internal_types::IPAMAddresses,
netlink::{self, CreateLinkOptions},
Expand Down Expand Up @@ -218,33 +218,7 @@ impl driver::NetworkDriver for Vlan<'_> {
&self,
netlink_sockets: (&mut netlink::Socket, &mut netlink::Socket),
) -> NetavarkResult<()> {
let ipam = get_ipam_addresses(self.info.per_network_opts, self.info.network)?;
let if_name = self.info.per_network_opts.interface_name.clone();

// If we are using DHCP macvlan, we need to at least call to the proxy so that
// the proxy's cache can get updated and the current lease can be released.
if ipam.dhcp_enabled {
let dev = netlink_sockets
.1
.get_link(netlink::LinkID::Name(if_name))
.wrap(format!(
"get macvlan interface {}",
&self.info.per_network_opts.interface_name
))?;

let container_mac_address = get_mac_address(dev.attributes)?;
release_dhcp_lease(
&self
.info
.network
.network_interface
.clone()
.unwrap_or_default(),
&self.info.per_network_opts.interface_name,
self.info.netns_path,
&container_mac_address,
)?
}
dhcp_teardown(&self.info, netlink_sockets.1)?;

let routes = core_utils::create_route_list(&self.info.network.routes)?;
for route in routes.iter() {
Expand Down Expand Up @@ -387,17 +361,6 @@ fn setup(
get_mac_address(dev.attributes)
}

fn get_mac_address(v: Vec<LinkAttribute>) -> NetavarkResult<String> {
for nla in v.into_iter() {
if let LinkAttribute::Address(ref addr) = nla {
return Ok(CoreUtils::encode_address_to_hex(addr));
}
}
Err(NetavarkError::msg(
"failed to get the the container mac address",
))
}

fn get_default_route_interface(host: &mut netlink::Socket) -> NetavarkResult<String> {
let routes = host.dump_routes().wrap("dump routes")?;

Expand Down
4 changes: 4 additions & 0 deletions test/620-bridge-mode.bats
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,7 @@ load helpers
assert_json "$link_info" '.[].flags[] | select(.=="UP")' == "UP" "Host bridge interface is up"
}

@test "bridge - managed mode with dhcp" {
expected_rc=1 run_netavark --file ${TESTSDIR}/testfiles/bridge-managed-dhcp.json setup $(get_container_netns_path)
assert_json ".error" "cannot use dhcp ipam driver without using the option mode=unmanaged" "dhcp error"
}
28 changes: 28 additions & 0 deletions test/testfiles/bridge-managed-dhcp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"container_id": "6ce776ea58b5",
"container_name": "testcontainer",
"networks": {
"podman": {
"interface_name": "eth0",
"static_ips": []
}
},
"network_info": {
"podman": {
"dns_enabled": false,
"driver": "bridge",
"id": "53ce4390f2adb1681eb1a90ec8b48c49c015e0a8d336c197637e7f65e365fa9e",
"internal": false,
"ipv6_enabled": false,
"name": "podman",
"network_interface": "podman0",
"subnets": [],
"options": {
"mode": "managed"
},
"ipam_options": {
"driver": "dhcp"
}
}
}
}

0 comments on commit 8f7024b

Please sign in to comment.