mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 12:11:56 +00:00
Merge branch 'next/major' of github.com:Start9Labs/start-os into feat/restart
This commit is contained in:
@@ -103,7 +103,7 @@ impl OsPartitionInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const BIOS_BOOT_TYPE_GUID: &str = "21686148-6449-6e6f-744e-656564726548";
|
const BIOS_BOOT_TYPE_GUID: &str = "21686148-6449-6E6F-744E-656564454649";
|
||||||
|
|
||||||
/// Find the BIOS boot partition on the same disk as `known_part`.
|
/// Find the BIOS boot partition on the same disk as `known_part`.
|
||||||
async fn find_bios_boot_partition(known_part: &Path) -> Result<Option<PathBuf>, Error> {
|
async fn find_bios_boot_partition(known_part: &Path) -> Result<Option<PathBuf>, Error> {
|
||||||
|
|||||||
@@ -765,6 +765,7 @@ async fn watcher(
|
|||||||
}
|
}
|
||||||
changed
|
changed
|
||||||
});
|
});
|
||||||
|
gc_policy_routing(&ifaces).await;
|
||||||
for result in futures::future::join_all(jobs).await {
|
for result in futures::future::join_all(jobs).await {
|
||||||
result.log_err();
|
result.log_err();
|
||||||
}
|
}
|
||||||
@@ -806,15 +807,43 @@ async fn get_wan_ipv4(iface: &str, base_url: &Url) -> Result<Option<Ipv4Addr>, E
|
|||||||
Ok(Some(trimmed.parse()?))
|
Ok(Some(trimmed.parse()?))
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PolicyRoutingCleanup {
|
struct PolicyRoutingGuard {
|
||||||
table_id: u32,
|
table_id: u32,
|
||||||
iface: String,
|
|
||||||
}
|
}
|
||||||
impl Drop for PolicyRoutingCleanup {
|
|
||||||
fn drop(&mut self) {
|
/// Remove stale per-interface policy-routing state (fwmark rules, routing
|
||||||
let table_str = self.table_id.to_string();
|
/// tables, iptables CONNMARK rules) for interfaces that no longer exist.
|
||||||
let iface = std::mem::take(&mut self.iface);
|
async fn gc_policy_routing(active_ifaces: &BTreeSet<GatewayId>) {
|
||||||
tokio::spawn(async move {
|
let active_tables: BTreeSet<u32> = active_ifaces
|
||||||
|
.iter()
|
||||||
|
.filter_map(|iface| if_nametoindex(iface.as_str()).ok().map(|idx| 1000 + idx))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// GC fwmark ip rules at priority 50 and their routing tables.
|
||||||
|
if let Ok(rules) = Command::new("ip")
|
||||||
|
.arg("rule")
|
||||||
|
.arg("show")
|
||||||
|
.invoke(ErrorKind::Network)
|
||||||
|
.await
|
||||||
|
.and_then(|b| String::from_utf8(b).with_kind(ErrorKind::Utf8))
|
||||||
|
{
|
||||||
|
for line in rules.lines() {
|
||||||
|
let line = line.trim();
|
||||||
|
if !line.starts_with("50:") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let Some(pos) = line.find("lookup ") else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let token = line[pos + 7..].split_whitespace().next().unwrap_or("");
|
||||||
|
let Ok(table_id) = token.parse::<u32>() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if table_id < 1000 || active_tables.contains(&table_id) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let table_str = table_id.to_string();
|
||||||
|
tracing::debug!("gc_policy_routing: removing stale table {table_id}");
|
||||||
Command::new("ip")
|
Command::new("ip")
|
||||||
.arg("rule")
|
.arg("rule")
|
||||||
.arg("del")
|
.arg("del")
|
||||||
@@ -835,25 +864,46 @@ impl Drop for PolicyRoutingCleanup {
|
|||||||
.invoke(ErrorKind::Network)
|
.invoke(ErrorKind::Network)
|
||||||
.await
|
.await
|
||||||
.ok();
|
.ok();
|
||||||
Command::new("iptables")
|
}
|
||||||
.arg("-t")
|
}
|
||||||
.arg("mangle")
|
|
||||||
.arg("-D")
|
// GC iptables CONNMARK set-mark rules for defunct interfaces.
|
||||||
.arg("PREROUTING")
|
if let Ok(rules) = Command::new("iptables")
|
||||||
.arg("-i")
|
.arg("-t")
|
||||||
.arg(&iface)
|
.arg("mangle")
|
||||||
.arg("-m")
|
.arg("-S")
|
||||||
.arg("conntrack")
|
.arg("PREROUTING")
|
||||||
.arg("--ctstate")
|
.invoke(ErrorKind::Network)
|
||||||
.arg("NEW")
|
.await
|
||||||
.arg("-j")
|
.and_then(|b| String::from_utf8(b).with_kind(ErrorKind::Utf8))
|
||||||
.arg("CONNMARK")
|
{
|
||||||
.arg("--set-mark")
|
// Rules look like:
|
||||||
.arg(&table_str)
|
// -A PREROUTING -i wg0 -m conntrack --ctstate NEW -j CONNMARK --set-mark 1005
|
||||||
.invoke(ErrorKind::Network)
|
for line in rules.lines() {
|
||||||
.await
|
let parts: Vec<&str> = line.split_whitespace().collect();
|
||||||
.ok();
|
if parts.first() != Some(&"-A") {
|
||||||
});
|
continue;
|
||||||
|
}
|
||||||
|
if !parts.contains(&"--set-mark") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let Some(iface_idx) = parts.iter().position(|&p| p == "-i") else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let Some(&iface) = parts.get(iface_idx + 1) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if active_ifaces.contains(&GatewayId::from(InternedString::intern(iface))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
tracing::debug!("gc_policy_routing: removing stale iptables rule for {iface}");
|
||||||
|
let mut cmd = Command::new("iptables");
|
||||||
|
cmd.arg("-t").arg("mangle").arg("-D");
|
||||||
|
for &arg in &parts[1..] {
|
||||||
|
cmd.arg(arg);
|
||||||
|
}
|
||||||
|
cmd.invoke(ErrorKind::Network).await.ok();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -985,11 +1035,8 @@ async fn watch_ip(
|
|||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
let policy_guard: Option<PolicyRoutingCleanup> =
|
let policy_guard: Option<PolicyRoutingGuard> =
|
||||||
policy_table_id.map(|t| PolicyRoutingCleanup {
|
policy_table_id.map(|t| PolicyRoutingGuard { table_id: t });
|
||||||
table_id: t,
|
|
||||||
iface: iface.as_str().to_owned(),
|
|
||||||
});
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
until
|
until
|
||||||
@@ -1016,7 +1063,7 @@ async fn watch_ip(
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn apply_policy_routing(
|
async fn apply_policy_routing(
|
||||||
guard: &PolicyRoutingCleanup,
|
guard: &PolicyRoutingGuard,
|
||||||
iface: &GatewayId,
|
iface: &GatewayId,
|
||||||
lan_ip: &OrdSet<IpAddr>,
|
lan_ip: &OrdSet<IpAddr>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
@@ -1250,7 +1297,7 @@ async fn poll_ip_info(
|
|||||||
ip4_proxy: &Ip4ConfigProxy<'_>,
|
ip4_proxy: &Ip4ConfigProxy<'_>,
|
||||||
ip6_proxy: &Ip6ConfigProxy<'_>,
|
ip6_proxy: &Ip6ConfigProxy<'_>,
|
||||||
dhcp4_proxy: &Option<Dhcp4ConfigProxy<'_>>,
|
dhcp4_proxy: &Option<Dhcp4ConfigProxy<'_>>,
|
||||||
policy_guard: &Option<PolicyRoutingCleanup>,
|
policy_guard: &Option<PolicyRoutingGuard>,
|
||||||
iface: &GatewayId,
|
iface: &GatewayId,
|
||||||
echoip_ratelimit_state: &mut BTreeMap<Url, Instant>,
|
echoip_ratelimit_state: &mut BTreeMap<Url, Instant>,
|
||||||
db: Option<&TypedPatchDb<Database>>,
|
db: Option<&TypedPatchDb<Database>>,
|
||||||
@@ -1299,6 +1346,49 @@ async fn poll_ip_info(
|
|||||||
apply_policy_routing(guard, iface, &lan_ip).await?;
|
apply_policy_routing(guard, iface, &lan_ip).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Write IP info to the watch immediately so the gateway appears in the
|
||||||
|
// DB without waiting for the (slow) WAN IP fetch. The echoip HTTP
|
||||||
|
// request has a 5-second timeout per URL and is easily cancelled by
|
||||||
|
// D-Bus signals via the Until mechanism, which would prevent the
|
||||||
|
// gateway from ever appearing if we waited.
|
||||||
|
let mut ip_info = IpInfo {
|
||||||
|
name: name.clone(),
|
||||||
|
scope_id,
|
||||||
|
device_type,
|
||||||
|
subnets: subnets.clone(),
|
||||||
|
lan_ip,
|
||||||
|
wan_ip: None,
|
||||||
|
ntp_servers,
|
||||||
|
dns_servers,
|
||||||
|
};
|
||||||
|
|
||||||
|
write_to.send_if_modified(|m: &mut OrdMap<GatewayId, NetworkInterfaceInfo>| {
|
||||||
|
let (name, secure, gateway_type, prev_wan_ip) =
|
||||||
|
m.get(iface).map_or((None, None, None, None), |i| {
|
||||||
|
(
|
||||||
|
i.name.clone(),
|
||||||
|
i.secure,
|
||||||
|
i.gateway_type,
|
||||||
|
i.ip_info.as_ref().and_then(|i| i.wan_ip),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
ip_info.wan_ip = prev_wan_ip;
|
||||||
|
let ip_info = Arc::new(ip_info);
|
||||||
|
m.insert(
|
||||||
|
iface.clone(),
|
||||||
|
NetworkInterfaceInfo {
|
||||||
|
name,
|
||||||
|
secure,
|
||||||
|
ip_info: Some(ip_info.clone()),
|
||||||
|
gateway_type,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.filter(|old| &old.ip_info == &Some(ip_info))
|
||||||
|
.is_none()
|
||||||
|
});
|
||||||
|
|
||||||
|
// Now fetch the WAN IP in a second pass. Even if this is slow or
|
||||||
|
// gets cancelled, the gateway already has valid ip_info above.
|
||||||
let echoip_urls = if let Some(db) = db {
|
let echoip_urls = if let Some(db) = db {
|
||||||
db.peek()
|
db.peek()
|
||||||
.await
|
.await
|
||||||
@@ -1349,41 +1439,25 @@ async fn poll_ip_info(
|
|||||||
);
|
);
|
||||||
tracing::debug!("{e:?}");
|
tracing::debug!("{e:?}");
|
||||||
}
|
}
|
||||||
let mut ip_info = IpInfo {
|
|
||||||
name: name.clone(),
|
|
||||||
scope_id,
|
|
||||||
device_type,
|
|
||||||
subnets,
|
|
||||||
lan_ip,
|
|
||||||
wan_ip,
|
|
||||||
ntp_servers,
|
|
||||||
dns_servers,
|
|
||||||
};
|
|
||||||
|
|
||||||
write_to.send_if_modified(|m: &mut OrdMap<GatewayId, NetworkInterfaceInfo>| {
|
// Update with WAN IP if we obtained one
|
||||||
let (name, secure, gateway_type, prev_wan_ip) =
|
if wan_ip.is_some() {
|
||||||
m.get(iface).map_or((None, None, None, None), |i| {
|
write_to.send_if_modified(|m: &mut OrdMap<GatewayId, NetworkInterfaceInfo>| {
|
||||||
(
|
let Some(entry) = m.get_mut(iface) else {
|
||||||
i.name.clone(),
|
return false;
|
||||||
i.secure,
|
};
|
||||||
i.gateway_type,
|
let Some(ref existing_ip) = entry.ip_info else {
|
||||||
i.ip_info.as_ref().and_then(|i| i.wan_ip),
|
return false;
|
||||||
)
|
};
|
||||||
});
|
if existing_ip.wan_ip == wan_ip {
|
||||||
ip_info.wan_ip = ip_info.wan_ip.or(prev_wan_ip);
|
return false;
|
||||||
let ip_info = Arc::new(ip_info);
|
}
|
||||||
m.insert(
|
let mut updated = (**existing_ip).clone();
|
||||||
iface.clone(),
|
updated.wan_ip = wan_ip;
|
||||||
NetworkInterfaceInfo {
|
entry.ip_info = Some(Arc::new(updated));
|
||||||
name,
|
true
|
||||||
secure,
|
});
|
||||||
ip_info: Some(ip_info.clone()),
|
}
|
||||||
gateway_type,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.filter(|old| &old.ip_info == &Some(ip_info))
|
|
||||||
.is_none()
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user