zlayer_overlay/
networkd.rs1#[cfg(target_os = "linux")]
17mod imp {
18 use std::path::Path;
19
20 pub(super) const NETWORKD_DROPIN: &str = "/run/systemd/network/10-zlayer-unmanaged.network";
21 pub(super) const NM_DROPIN: &str = "/run/NetworkManager/conf.d/10-zlayer-unmanaged.conf";
22
23 pub(super) const NETWORKD_BODY: &str = "# Managed by ZLayer overlayd — do not edit.\n\
27# Overlay links are configured by overlayd via netlink; keep systemd-networkd\n\
28# from managing them (it otherwise hits its watchdog on a zl-* Link UP).\n\
29[Match]\n\
30Name=zl-* veth-*\n\
31\n\
32[Link]\n\
33Unmanaged=yes\n";
34
35 pub(super) const NM_BODY: &str = "# Managed by ZLayer overlayd — do not edit.\n\
38[keyfile]\n\
39unmanaged-devices=interface-name:zl-*;interface-name:veth-*\n";
40
41 pub(super) fn mark() {
42 if Path::new("/run/systemd").is_dir() {
45 if let Err(e) = std::fs::create_dir_all("/run/systemd/network") {
46 tracing::warn!(error = %e, "could not create /run/systemd/network for unmanaged drop-in");
47 } else if let Err(e) = std::fs::write(NETWORKD_DROPIN, NETWORKD_BODY) {
48 tracing::warn!(error = %e, path = NETWORKD_DROPIN, "failed to write systemd-networkd unmanaged drop-in");
49 } else {
50 let _ = std::process::Command::new("networkctl")
51 .arg("reload")
52 .status();
53 tracing::info!(
54 path = NETWORKD_DROPIN,
55 "marked overlay interfaces Unmanaged for systemd-networkd"
56 );
57 }
58 }
59 if Path::new("/run/NetworkManager").is_dir() {
62 if let Err(e) = std::fs::create_dir_all("/run/NetworkManager/conf.d") {
63 tracing::warn!(error = %e, "could not create /run/NetworkManager/conf.d for unmanaged drop-in");
64 } else if let Err(e) = std::fs::write(NM_DROPIN, NM_BODY) {
65 tracing::warn!(error = %e, path = NM_DROPIN, "failed to write NetworkManager unmanaged drop-in");
66 } else {
67 let _ = std::process::Command::new("nmcli")
68 .args(["general", "reload"])
69 .status();
70 tracing::info!(
71 path = NM_DROPIN,
72 "marked overlay interfaces unmanaged for NetworkManager"
73 );
74 }
75 }
76 }
77
78 pub(super) fn unmark() {
79 let mut reload_networkd = false;
82 match std::fs::remove_file(NETWORKD_DROPIN) {
83 Ok(()) => reload_networkd = true,
84 Err(e) if e.kind() == std::io::ErrorKind::NotFound => {}
85 Err(e) => {
86 tracing::warn!(error = %e, path = NETWORKD_DROPIN, "failed to remove networkd unmanaged drop-in");
87 }
88 }
89 if reload_networkd {
90 let _ = std::process::Command::new("networkctl")
91 .arg("reload")
92 .status();
93 }
94
95 let mut reload_nm = false;
96 match std::fs::remove_file(NM_DROPIN) {
97 Ok(()) => reload_nm = true,
98 Err(e) if e.kind() == std::io::ErrorKind::NotFound => {}
99 Err(e) => {
100 tracing::warn!(error = %e, path = NM_DROPIN, "failed to remove NetworkManager unmanaged drop-in");
101 }
102 }
103 if reload_nm {
104 let _ = std::process::Command::new("nmcli")
105 .args(["general", "reload"])
106 .status();
107 }
108 }
109}
110
111pub fn mark_overlay_interfaces_unmanaged() {
115 #[cfg(target_os = "linux")]
116 imp::mark();
117}
118
119pub fn unmark_overlay_interfaces_unmanaged() {
122 #[cfg(target_os = "linux")]
123 imp::unmark();
124}
125
126#[cfg(all(test, target_os = "linux"))]
127mod tests {
128 use super::imp::{NETWORKD_BODY, NM_BODY};
129
130 #[test]
131 fn networkd_dropin_marks_overlay_links_unmanaged() {
132 assert!(NETWORKD_BODY.contains("[Match]"));
133 assert!(
134 NETWORKD_BODY.contains("Name=zl-* veth-*"),
135 "must match both zl-* and veth-* overlay links"
136 );
137 assert!(NETWORKD_BODY.contains("[Link]"));
138 assert!(
139 NETWORKD_BODY.contains("Unmanaged=yes"),
140 "must mark the matched links Unmanaged"
141 );
142 }
143
144 #[test]
145 fn networkmanager_dropin_marks_overlay_links_unmanaged() {
146 assert!(NM_BODY.contains("[keyfile]"));
147 assert!(
148 NM_BODY.contains("unmanaged-devices=interface-name:zl-*;interface-name:veth-*"),
149 "must mark zl-* and veth-* unmanaged for NetworkManager"
150 );
151 }
152}