fix: Support multiple mac address on static host binding
Some checks failed
Run Check Script / check (pull_request) Failing after 30s

This commit is contained in:
Jean-Gabriel Gill-Couture 2025-09-03 08:38:30 -04:00
parent ceea03d6ce
commit fed4a8076c
6 changed files with 370 additions and 359 deletions

661
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -11,15 +11,16 @@ use super::{LogicalHost, k8s::K8sClient};
#[derive(Debug)] #[derive(Debug)]
pub struct DHCPStaticEntry { pub struct DHCPStaticEntry {
pub name: String, pub name: String,
pub mac: MacAddress, pub mac: Vec<MacAddress>,
pub ip: Ipv4Addr, pub ip: Ipv4Addr,
} }
impl std::fmt::Display for DHCPStaticEntry { impl std::fmt::Display for DHCPStaticEntry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mac = self.mac.iter().map(|m| m.to_string()).collect::<Vec<String>>().join(",");
f.write_fmt(format_args!( f.write_fmt(format_args!(
"DHCPStaticEntry : name {}, mac {}, ip {}", "DHCPStaticEntry : name {}, mac {}, ip {}",
self.name, self.mac, self.ip self.name, mac, self.ip
)) ))
} }
} }
@ -41,6 +42,7 @@ impl std::fmt::Debug for dyn Firewall {
pub struct NetworkDomain { pub struct NetworkDomain {
pub name: String, pub name: String,
} }
#[async_trait] #[async_trait]
pub trait K8sclient: Send + Sync { pub trait K8sclient: Send + Sync {
async fn k8s_client(&self) -> Result<Arc<K8sClient>, String>; async fn k8s_client(&self) -> Result<Arc<K8sClient>, String>;

View File

@ -17,13 +17,13 @@ impl DhcpServer for OPNSenseFirewall {
} }
async fn add_static_mapping(&self, entry: &DHCPStaticEntry) -> Result<(), ExecutorError> { async fn add_static_mapping(&self, entry: &DHCPStaticEntry) -> Result<(), ExecutorError> {
let mac: String = String::from(&entry.mac); let mac: Vec<String> = entry.mac.iter().map(MacAddress::to_string).collect();
{ {
let mut writable_opnsense = self.opnsense_config.write().await; let mut writable_opnsense = self.opnsense_config.write().await;
writable_opnsense writable_opnsense
.dhcp() .dhcp()
.add_static_mapping(&mac, entry.ip, &entry.name) .add_static_mapping(&mac, &entry.ip, &entry.name)
.unwrap(); .unwrap();
} }

View File

@ -179,7 +179,7 @@ impl DhcpHostBindingInterpret {
DHCPStaticEntry { DHCPStaticEntry {
name, name,
mac: binding.physical_host.cluster_mac(), mac: binding.physical_host.get_mac_address(),
ip, ip,
} }
}) })

View File

@ -21,7 +21,7 @@ impl From<&MacAddress> for String {
impl std::fmt::Display for MacAddress { impl std::fmt::Display for MacAddress {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("MacAddress {}", String::from(self))) f.write_str(&String::from(self))
} }
} }

View File

@ -75,16 +75,16 @@ impl<'a> DhcpConfigDnsMasq<'a> {
/// ambiguous state. /// ambiguous state.
pub fn add_static_mapping( pub fn add_static_mapping(
&mut self, &mut self,
mac: &str, mac: &Vec<String>,
ipaddr: Ipv4Addr, ipaddr: &Ipv4Addr,
hostname: &str, hostname: &str,
) -> Result<(), DhcpError> { ) -> Result<(), DhcpError> {
let mut hostname_split = hostname.split("."); let mut hostname_split = hostname.split(".");
let hostname = hostname_split.next().expect("hostname cannot be empty"); let hostname = hostname_split.next().expect("hostname cannot be empty");
let domain_name = hostname_split.collect::<Vec<&str>>().join("."); let domain_name = hostname_split.collect::<Vec<&str>>().join(".");
if !Self::is_valid_mac(mac) { if let Some(m) = mac.iter().find(|m| !Self::is_valid_mac(m)) {
return Err(DhcpError::InvalidMacAddress(mac.to_string())); return Err(DhcpError::InvalidMacAddress(m.to_string()));
} }
let ip_str = ipaddr.to_string(); let ip_str = ipaddr.to_string();
@ -120,17 +120,19 @@ impl<'a> DhcpConfigDnsMasq<'a> {
let mut all_indices: Vec<&usize> = ip_set.union(&hostname_set).collect(); let mut all_indices: Vec<&usize> = ip_set.union(&hostname_set).collect();
all_indices.sort(); all_indices.sort();
let mac_list = mac.join(",");
match all_indices.len() { match all_indices.len() {
0 => { 0 => {
info!( info!(
"Creating new static host for {} ({}) with MAC {}", "Creating new static host for {} ({}) with MAC {}",
hostname, ipaddr, mac hostname, ipaddr, mac_list
); );
let new_host = DnsmasqHost { let new_host = DnsmasqHost {
uuid: Uuid::new_v4().to_string(), uuid: Uuid::new_v4().to_string(),
host: hostname.to_string(), host: hostname.to_string(),
ip: ip_str.into(), ip: ip_str.into(),
hwaddr: mac.to_string().into(), hwaddr: mac_list.into(),
local: MaybeString::from("1"), local: MaybeString::from("1"),
ignore: Some(0), ignore: Some(0),
domain: domain_name.into(), domain: domain_name.into(),
@ -145,38 +147,40 @@ impl<'a> DhcpConfigDnsMasq<'a> {
if host_to_modify_ip != ip_str { if host_to_modify_ip != ip_str {
warn!( warn!(
"Hostname '{}' already exists with a different IP ({}). Setting new IP {ip_str}. Appending MAC {}.", "Hostname '{}' already exists with a different IP ({}). Setting new IP {ip_str}. Appending MAC {}.",
hostname, host_to_modify_ip, mac hostname, host_to_modify_ip, mac_list
); );
host_to_modify.ip.content = Some(ip_str); host_to_modify.ip.content = Some(ip_str);
} else if host_to_modify.host != hostname { } else if host_to_modify.host != hostname {
warn!( warn!(
"IP {} already exists with a different hostname ('{}'). Setting hostname to {hostname}. Appending MAC {}.", "IP {} already exists with a different hostname ('{}'). Setting hostname to {hostname}. Appending MAC {}.",
ipaddr, host_to_modify.host, mac ipaddr, host_to_modify.host, mac_list
); );
host_to_modify.host = hostname.to_string(); host_to_modify.host = hostname.to_string();
} }
for single_mac in mac.iter() {
if !host_to_modify if !host_to_modify
.hwaddr .hwaddr
.content_string() .content_string()
.split(',') .split(',')
.any(|m| m.eq_ignore_ascii_case(mac)) .any(|m| m.eq_ignore_ascii_case(single_mac))
{ {
info!( info!(
"Appending MAC {} to existing static host for {} ({})", "Appending MAC {} to existing static host for {} ({})",
mac, host_to_modify.host, host_to_modify_ip single_mac, host_to_modify.host, host_to_modify_ip
); );
let mut updated_macs = host_to_modify.hwaddr.content_string().to_string(); let mut updated_macs = host_to_modify.hwaddr.content_string().to_string();
updated_macs.push(','); updated_macs.push(',');
updated_macs.push_str(mac); updated_macs.push_str(single_mac);
host_to_modify.hwaddr.content = updated_macs.into(); host_to_modify.hwaddr.content = updated_macs.into();
} else { } else {
info!( debug!(
"MAC {} already present in static host entry for {} ({}). No changes made.", "MAC {} already present in static host entry for {} ({}). No changes made.",
mac, host_to_modify.host, host_to_modify_ip single_mac, host_to_modify.host, host_to_modify_ip
); );
} }
} }
}
_ => { _ => {
return Err(DhcpError::Configuration(format!( return Err(DhcpError::Configuration(format!(
"Configuration conflict: Found multiple host entries matching IP {} and/or hostname '{}'. Cannot resolve automatically.", "Configuration conflict: Found multiple host entries matching IP {} and/or hostname '{}'. Cannot resolve automatically.",