#[cfg(target_os = "linux")]
mod imp {
use std::path::Path;
pub(super) const NETWORKD_DROPIN: &str = "/run/systemd/network/10-zlayer-unmanaged.network";
pub(super) const NM_DROPIN: &str = "/run/NetworkManager/conf.d/10-zlayer-unmanaged.conf";
pub(super) const NETWORKD_BODY: &str = "# Managed by ZLayer overlayd — do not edit.\n\
# Overlay links are configured by overlayd via netlink; keep systemd-networkd\n\
# from managing them (it otherwise hits its watchdog on a zl-* Link UP).\n\
[Match]\n\
Name=zl-* veth-*\n\
\n\
[Link]\n\
Unmanaged=yes\n";
pub(super) const NM_BODY: &str = "# Managed by ZLayer overlayd — do not edit.\n\
[keyfile]\n\
unmanaged-devices=interface-name:zl-*;interface-name:veth-*\n";
pub(super) fn mark() {
if Path::new("/run/systemd").is_dir() {
if let Err(e) = std::fs::create_dir_all("/run/systemd/network") {
tracing::warn!(error = %e, "could not create /run/systemd/network for unmanaged drop-in");
} else if let Err(e) = std::fs::write(NETWORKD_DROPIN, NETWORKD_BODY) {
tracing::warn!(error = %e, path = NETWORKD_DROPIN, "failed to write systemd-networkd unmanaged drop-in");
} else {
let _ = std::process::Command::new("networkctl")
.arg("reload")
.status();
tracing::info!(
path = NETWORKD_DROPIN,
"marked overlay interfaces Unmanaged for systemd-networkd"
);
}
}
if Path::new("/run/NetworkManager").is_dir() {
if let Err(e) = std::fs::create_dir_all("/run/NetworkManager/conf.d") {
tracing::warn!(error = %e, "could not create /run/NetworkManager/conf.d for unmanaged drop-in");
} else if let Err(e) = std::fs::write(NM_DROPIN, NM_BODY) {
tracing::warn!(error = %e, path = NM_DROPIN, "failed to write NetworkManager unmanaged drop-in");
} else {
let _ = std::process::Command::new("nmcli")
.args(["general", "reload"])
.status();
tracing::info!(
path = NM_DROPIN,
"marked overlay interfaces unmanaged for NetworkManager"
);
}
}
}
pub(super) fn unmark() {
let mut reload_networkd = false;
match std::fs::remove_file(NETWORKD_DROPIN) {
Ok(()) => reload_networkd = true,
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {}
Err(e) => {
tracing::warn!(error = %e, path = NETWORKD_DROPIN, "failed to remove networkd unmanaged drop-in");
}
}
if reload_networkd {
let _ = std::process::Command::new("networkctl")
.arg("reload")
.status();
}
let mut reload_nm = false;
match std::fs::remove_file(NM_DROPIN) {
Ok(()) => reload_nm = true,
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {}
Err(e) => {
tracing::warn!(error = %e, path = NM_DROPIN, "failed to remove NetworkManager unmanaged drop-in");
}
}
if reload_nm {
let _ = std::process::Command::new("nmcli")
.args(["general", "reload"])
.status();
}
}
}
pub fn mark_overlay_interfaces_unmanaged() {
#[cfg(target_os = "linux")]
imp::mark();
}
pub fn unmark_overlay_interfaces_unmanaged() {
#[cfg(target_os = "linux")]
imp::unmark();
}
#[cfg(all(test, target_os = "linux"))]
mod tests {
use super::imp::{NETWORKD_BODY, NM_BODY};
#[test]
fn networkd_dropin_marks_overlay_links_unmanaged() {
assert!(NETWORKD_BODY.contains("[Match]"));
assert!(
NETWORKD_BODY.contains("Name=zl-* veth-*"),
"must match both zl-* and veth-* overlay links"
);
assert!(NETWORKD_BODY.contains("[Link]"));
assert!(
NETWORKD_BODY.contains("Unmanaged=yes"),
"must mark the matched links Unmanaged"
);
}
#[test]
fn networkmanager_dropin_marks_overlay_links_unmanaged() {
assert!(NM_BODY.contains("[keyfile]"));
assert!(
NM_BODY.contains("unmanaged-devices=interface-name:zl-*;interface-name:veth-*"),
"must mark zl-* and veth-* unmanaged for NetworkManager"
);
}
}