multi_fix: harden peer/session handling, tighten foreign-network trust, and improve web client metadata (#1999)

* machine-id should be scoped unbder same user-id
* feat: report device os metadata to console
* fix sync root key cause packet loss
* fix tun packet not invalid
* fix faketcp cause lat jitter
* fix some packet not decrypt
* fix peer info patch, improve performance of update self info
* fix foreign credential identity mismatch handling
This commit is contained in:
KKRainbow
2026-03-21 21:06:07 +08:00
committed by GitHub
parent 77966916c4
commit 2bfdd44759
24 changed files with 1381 additions and 358 deletions
+55 -1
View File
@@ -134,6 +134,15 @@ impl TrustedKeyMapManager {
}
pub fn verify_trusted_key(&self, pubkey: &[u8], network_name: &str) -> bool {
self.verify_trusted_key_with_source(pubkey, network_name, None)
}
pub fn verify_trusted_key_with_source(
&self,
pubkey: &[u8],
network_name: &str,
source: Option<TrustedKeySource>,
) -> bool {
let Some(trusted_keys) = self
.network_trusted_keys
.get(network_name)
@@ -146,7 +155,11 @@ impl TrustedKeyMapManager {
return false;
};
!metadata.is_expired()
if let Some(source) = source {
metadata.source == source && !metadata.is_expired()
} else {
!metadata.is_expired()
}
}
pub fn list_trusted_keys(&self, network_name: &str) -> Vec<(Vec<u8>, TrustedKeyMetadata)> {
@@ -542,6 +555,16 @@ impl GlobalCtx {
false
}
pub fn is_pubkey_trusted_with_source(
&self,
pubkey: &[u8],
network_name: &str,
source: TrustedKeySource,
) -> bool {
self.trusted_keys
.verify_trusted_key_with_source(pubkey, network_name, Some(source))
}
/// Atomically replace all OSPF trusted keys with a new set
/// Called by OSPF route layer after each route update
pub fn update_trusted_keys(&self, keys: TrustedKeyMap, network_name: &str) {
@@ -676,6 +699,37 @@ pub mod tests {
);
}
#[tokio::test]
async fn trusted_key_source_lookup_is_precise() {
let config = TomlConfigLoader::default();
let global_ctx = GlobalCtx::new(config);
let network_name = "net1";
let pubkey = vec![1; 32];
global_ctx.update_trusted_keys(
HashMap::from([(
pubkey.clone(),
TrustedKeyMetadata {
source: TrustedKeySource::OspfCredential,
expiry_unix: None,
},
)]),
network_name,
);
assert!(global_ctx.is_pubkey_trusted(&pubkey, network_name));
assert!(!global_ctx.is_pubkey_trusted_with_source(
&pubkey,
network_name,
TrustedKeySource::OspfNode,
));
assert!(global_ctx.is_pubkey_trusted_with_source(
&pubkey,
network_name,
TrustedKeySource::OspfCredential,
));
}
pub fn get_mock_global_ctx_with_network(
network_identy: Option<NetworkIdentity>,
) -> ArcGlobalCtx {
+1
View File
@@ -24,6 +24,7 @@ pub mod ifcfg;
pub mod log;
pub mod netns;
pub mod network;
pub mod os_info;
pub mod scoped_task;
pub mod stats_manager;
pub mod stun;
+144
View File
@@ -0,0 +1,144 @@
use std::{collections::HashMap, fs, process::Command};
use crate::proto::web::DeviceOsInfo;
pub fn collect_device_os_info() -> DeviceOsInfo {
let os_type = normalize_os_type(std::env::consts::OS);
let (version, distribution) = detect_os_version_and_distribution(&os_type);
DeviceOsInfo {
os_type,
version,
distribution,
}
}
fn normalize_os_type(raw: &str) -> String {
match raw {
"macos" => "macos".to_string(),
"windows" => "windows".to_string(),
"linux" => "linux".to_string(),
"android" => "android".to_string(),
"ios" => "ios".to_string(),
"freebsd" => "freebsd".to_string(),
other => other.to_string(),
}
}
fn detect_os_version_and_distribution(os_type: &str) -> (String, String) {
match os_type {
"linux" | "android" => linux_version_and_distribution(os_type),
"macos" => (
first_non_empty([
command_output("sw_vers", &["-productVersion"]),
unix_kernel_release(),
]),
"macOS".to_string(),
),
"windows" => (
first_non_empty([windows_version(), None]),
"Windows".to_string(),
),
"freebsd" => (
first_non_empty([
command_output("freebsd-version", &[]),
unix_kernel_release(),
]),
"FreeBSD".to_string(),
),
other => (
unix_kernel_release().unwrap_or_else(|| "unknown".to_string()),
other.to_string(),
),
}
}
fn linux_version_and_distribution(os_type: &str) -> (String, String) {
let os_release = parse_os_release().unwrap_or_default();
let version = first_non_empty([
os_release.get("VERSION_ID").cloned(),
os_release.get("VERSION").cloned(),
unix_kernel_release(),
]);
let distribution = first_non_empty([
os_release.get("NAME").cloned(),
os_release.get("ID").cloned().map(title_case),
Some(if os_type == "android" {
"Android".to_string()
} else {
"Linux".to_string()
}),
]);
(version, distribution)
}
fn parse_os_release() -> Option<HashMap<String, String>> {
["/etc/os-release", "/usr/lib/os-release"]
.into_iter()
.find_map(|path| fs::read_to_string(path).ok())
.map(|content| {
content
.lines()
.filter_map(|line| {
let line = line.trim();
if line.is_empty() || line.starts_with('#') {
return None;
}
let (key, value) = line.split_once('=')?;
Some((key.to_string(), trim_os_release_value(value)))
})
.collect()
})
}
fn trim_os_release_value(value: &str) -> String {
value
.trim()
.trim_matches('"')
.trim_matches('\'')
.to_string()
}
fn unix_kernel_release() -> Option<String> {
command_output("uname", &["-r"])
}
fn windows_version() -> Option<String> {
let output = command_output("cmd", &["/C", "ver"])?;
output
.split("Version")
.nth(1)
.map(str::trim)
.map(|part| part.trim_matches(&['[', ']'][..]).to_string())
.filter(|value| !value.is_empty())
}
fn command_output(program: &str, args: &[&str]) -> Option<String> {
let output = Command::new(program).args(args).output().ok()?;
if !output.status.success() {
return None;
}
let value = String::from_utf8(output.stdout).ok()?;
let value = value.trim();
if value.is_empty() {
None
} else {
Some(value.to_string())
}
}
fn first_non_empty<const N: usize>(values: [Option<String>; N]) -> String {
values
.into_iter()
.flatten()
.find(|value| !value.trim().is_empty())
.unwrap_or_else(|| "unknown".to_string())
}
fn title_case(value: String) -> String {
let mut chars = value.chars();
let Some(first) = chars.next() else {
return value;
};
first.to_uppercase().collect::<String>() + chars.as_str()
}