mirror of
https://github.com/EasyTier/EasyTier.git
synced 2026-09-21 11:24:58 +00:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5a0e0cd43b | ||
|
|
8c64949dde | ||
|
|
6e3460db7d |
@@ -119,7 +119,11 @@ fn enum_options(kind: Kind) -> Vec<FieldOption> {
|
||||
.values()
|
||||
.map(|value| FieldOption {
|
||||
label: value.name().to_string(),
|
||||
value: value.number().to_string(),
|
||||
// protobuf JSON uses enum names rather than their numeric wire values.
|
||||
// Returning the number here made ArkTS write (for example) `1`, while
|
||||
// NetworkConfig deserialization expects `"None"`, so field-level saves
|
||||
// were rejected by the repository validation step.
|
||||
value: value.name().to_string(),
|
||||
})
|
||||
.collect(),
|
||||
_ => Vec::new(),
|
||||
@@ -410,5 +414,17 @@ mod tests {
|
||||
.iter()
|
||||
.any(|option| option.label == "PublicServer")
|
||||
);
|
||||
|
||||
let data_compress_algo = schema
|
||||
.children
|
||||
.iter()
|
||||
.find(|field| field.name == "data_compress_algo")
|
||||
.expect("data_compress_algo field");
|
||||
let none = data_compress_algo
|
||||
.enum_options
|
||||
.iter()
|
||||
.find(|option| option.label == "None")
|
||||
.expect("compression None option");
|
||||
assert_eq!(none.value, "None");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,7 +162,7 @@ pub fn import_config_share_link(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::config_repo::{create_config_record, init_config_store};
|
||||
use crate::config::repository::{create_config_record, init_config_store};
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
fn test_root() -> String {
|
||||
|
||||
@@ -54,17 +54,14 @@ pub(crate) fn get_runtime_config_snapshot(config_id: &str) -> Option<RuntimeConf
|
||||
.and_then(|guard| guard.get(config_id).cloned())
|
||||
}
|
||||
|
||||
pub(crate) fn get_runtime_config_route_overrides(config_id: &str) -> (Vec<String>, Vec<String>) {
|
||||
pub(crate) fn get_runtime_config_manual_routes(config_id: &str) -> Vec<String> {
|
||||
RUNTIME_CONFIG_SNAPSHOTS
|
||||
.lock()
|
||||
.ok()
|
||||
.and_then(|guard| {
|
||||
guard.get(config_id).map(|snapshot| {
|
||||
(
|
||||
snapshot.config.routes.clone(),
|
||||
snapshot.config.proxy_cidrs.clone(),
|
||||
)
|
||||
})
|
||||
guard
|
||||
.get(config_id)
|
||||
.map(|snapshot| snapshot.config.routes.clone())
|
||||
})
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::config::repository::get_runtime_config_route_overrides;
|
||||
use crate::config::repository::get_runtime_config_manual_routes;
|
||||
use crate::runtime::state::runtime_state::RuntimeInstanceState;
|
||||
use ipnet::IpNet;
|
||||
use std::collections::HashSet;
|
||||
@@ -59,8 +59,7 @@ pub(crate) fn aggregate_tun_routes(instance: &RuntimeInstanceState) -> Vec<Strin
|
||||
.my_node_info
|
||||
.as_ref()
|
||||
.and_then(|info| info.virtual_ipv4_cidr.clone());
|
||||
let (manual_routes, config_proxy_cidrs) =
|
||||
get_runtime_config_route_overrides(&instance.config_id);
|
||||
let manual_routes = get_runtime_config_manual_routes(&instance.config_id);
|
||||
let runtime_proxy_cidrs = instance
|
||||
.routes
|
||||
.iter()
|
||||
@@ -73,7 +72,9 @@ pub(crate) fn aggregate_tun_routes(instance: &RuntimeInstanceState) -> Vec<Strin
|
||||
}
|
||||
|
||||
raw_routes.extend(manual_routes.iter().cloned());
|
||||
raw_routes.extend(config_proxy_cidrs.iter().cloned());
|
||||
// Locally configured proxy CIDRs are advertisements for networks reached
|
||||
// through this node. Installing them into this node's TUN would recapture
|
||||
// the proxy's own destination sockets instead of using the physical LAN.
|
||||
raw_routes.extend(runtime_proxy_cidrs.iter().cloned());
|
||||
simplify_routes(raw_routes)
|
||||
}
|
||||
@@ -90,3 +91,76 @@ pub(crate) fn aggregate_requested_tun_routes(instances: &[RuntimeInstanceState])
|
||||
}
|
||||
aggregated_routes
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::config::repository::{cache_runtime_config_snapshot, clear_runtime_config_snapshot};
|
||||
use crate::runtime::state::runtime_state::{MyNodeInfo, RouteView};
|
||||
use easytier::proto::api::manage::NetworkConfig;
|
||||
|
||||
fn runtime_instance(config_id: &str) -> RuntimeInstanceState {
|
||||
RuntimeInstanceState {
|
||||
config_id: config_id.to_string(),
|
||||
instance_id: "test-instance".to_string(),
|
||||
display_name: "test".to_string(),
|
||||
running: true,
|
||||
tun_required: true,
|
||||
tun_attached: false,
|
||||
magic_dns_enabled: false,
|
||||
need_exit_node: false,
|
||||
error_message: None,
|
||||
my_node_info: Some(MyNodeInfo {
|
||||
virtual_ipv4: Some("10.144.144.1".to_string()),
|
||||
virtual_ipv4_cidr: Some("10.144.144.1/24".to_string()),
|
||||
hostname: None,
|
||||
version: None,
|
||||
peer_id: Some(1),
|
||||
listeners: Vec::new(),
|
||||
vpn_portal_cfg: None,
|
||||
udp_nat_type: None,
|
||||
tcp_nat_type: None,
|
||||
}),
|
||||
events: Vec::new(),
|
||||
routes: vec![RouteView {
|
||||
peer_id: 2,
|
||||
hostname: None,
|
||||
ipv4: Some("10.144.144.2".to_string()),
|
||||
ipv4_cidr: Some("10.144.144.2/24".to_string()),
|
||||
ipv6_cidr: None,
|
||||
proxy_cidrs: vec!["10.20.0.0/16".to_string()],
|
||||
next_hop_peer_id: Some(2),
|
||||
cost: Some(1),
|
||||
path_latency: None,
|
||||
udp_nat_type: None,
|
||||
tcp_nat_type: None,
|
||||
inst_id: None,
|
||||
version: None,
|
||||
is_public_server: None,
|
||||
}],
|
||||
peers: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn local_proxy_cidr_is_not_installed_in_tun_routes() {
|
||||
let config_id = "routing-test-local-proxy";
|
||||
cache_runtime_config_snapshot(
|
||||
config_id.to_string(),
|
||||
"test".to_string(),
|
||||
NetworkConfig {
|
||||
routes: vec!["172.16.0.0/16".to_string()],
|
||||
proxy_cidrs: vec!["192.168.1.0/24".to_string()],
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
let routes = aggregate_tun_routes(&runtime_instance(config_id));
|
||||
clear_runtime_config_snapshot(config_id);
|
||||
|
||||
assert!(routes.contains(&"10.144.144.0/24".to_string()));
|
||||
assert!(routes.contains(&"172.16.0.0/16".to_string()));
|
||||
assert!(routes.contains(&"10.20.0.0/16".to_string()));
|
||||
assert!(!routes.contains(&"192.168.1.0/24".to_string()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@ use easytier::common::{
|
||||
use easytier::instance::factory::{NativeInstanceManager, native_instance_manager_with_runtime};
|
||||
use easytier::proto::api::manage::NetworkConfig;
|
||||
use easytier::proto::api::manage::NetworkingMethod;
|
||||
use easytier::proto::common::CompressionAlgoPb;
|
||||
use easytier::web_client::{WebClient, WebClientHooks, run_web_client};
|
||||
use kernel_bridge::{
|
||||
start_local_socket_server as start_local_socket_server_inner,
|
||||
@@ -669,8 +670,11 @@ fn resolve_instance_id_inner(instance_name: &str) -> Option<String> {
|
||||
}
|
||||
|
||||
pub(crate) fn build_default_network_config_json() -> Result<String, String> {
|
||||
let config = NetworkConfig::new_from_config(TomlConfigLoader::default())
|
||||
let mut config = NetworkConfig::new_from_config(TomlConfigLoader::default())
|
||||
.map_err(|e| format!("default_network_config failed {}", e))?;
|
||||
// HarmonyOS 的配置编辑页将压缩算法作为显式选项展示。新建实例默认
|
||||
// 使用 NONE,避免在用户没有主动选择时增加压缩开销。
|
||||
config.data_compress_algo = Some(CompressionAlgoPb::None as i32);
|
||||
serde_json::to_string(&config).map_err(|e| format!("default_network_config failed {}", e))
|
||||
}
|
||||
|
||||
|
||||
@@ -590,6 +590,8 @@ where
|
||||
})?;
|
||||
let overlay_destination = if local_virtual_destination {
|
||||
true
|
||||
} else if dst_ip.is_loopback() {
|
||||
false
|
||||
} else {
|
||||
let (peers, _) = options
|
||||
.deadline
|
||||
|
||||
@@ -256,6 +256,17 @@ where
|
||||
Arc::downgrade(&connections),
|
||||
"TCP port-forward connections",
|
||||
));
|
||||
if self.runtime_config.snapshot().services.proxy.force_smoltcp
|
||||
&& bind_addr.is_ipv4()
|
||||
&& bind_addr.ip().is_unspecified()
|
||||
{
|
||||
self.spawn_data_plane_tcp_port_forward(
|
||||
bind_addr.port(),
|
||||
dst_addr,
|
||||
cancel.clone(),
|
||||
connections.clone(),
|
||||
);
|
||||
}
|
||||
let host = self.host.clone();
|
||||
self.tasks.lock().unwrap().spawn(async move {
|
||||
let mut listener = Some(listener);
|
||||
@@ -303,6 +314,98 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn spawn_data_plane_tcp_port_forward(
|
||||
&self,
|
||||
local_port: u16,
|
||||
dst_addr: SocketAddr,
|
||||
cancel: CancellationToken,
|
||||
connections: Arc<std::sync::Mutex<JoinSet<()>>>,
|
||||
) {
|
||||
let data_plane = self.data_plane.clone();
|
||||
self.tasks.lock().unwrap().spawn(async move {
|
||||
loop {
|
||||
let mut listener = match select! {
|
||||
biased;
|
||||
_ = cancel.cancelled() => break,
|
||||
result = data_plane.data_plane_tcp_bind(local_port, Duration::from_secs(10)) => result,
|
||||
} {
|
||||
Ok(listener) => listener,
|
||||
Err(error) => {
|
||||
tracing::error!(
|
||||
?error,
|
||||
local_port,
|
||||
"data-plane TCP port-forward bind failed"
|
||||
);
|
||||
select! {
|
||||
biased;
|
||||
_ = cancel.cancelled() => break,
|
||||
_ = crate::foundation::time::sleep(TCP_PORT_FORWARD_REBIND_DELAY) => continue,
|
||||
}
|
||||
}
|
||||
};
|
||||
tracing::info!(
|
||||
?dst_addr,
|
||||
local_addr = ?listener.local_addr(),
|
||||
"data-plane TCP port-forward listener bound"
|
||||
);
|
||||
|
||||
loop {
|
||||
let accepted = select! {
|
||||
biased;
|
||||
_ = cancel.cancelled() => return,
|
||||
result = listener.accept() => result,
|
||||
};
|
||||
let (mut incoming, source_addr) = match accepted {
|
||||
Ok(accepted) => accepted,
|
||||
Err(error) => {
|
||||
tracing::error!(
|
||||
?error,
|
||||
local_port,
|
||||
"data-plane TCP port-forward accept failed; rebinding"
|
||||
);
|
||||
break;
|
||||
}
|
||||
};
|
||||
let data_plane = data_plane.clone();
|
||||
connections.lock().unwrap().spawn(async move {
|
||||
let options = DataPlaneTcpConnectOptions::gateway(
|
||||
Duration::from_secs(10),
|
||||
TcpSocketPurpose::PortForward,
|
||||
source_addr,
|
||||
);
|
||||
let mut outgoing = match data_plane.connect_tcp(dst_addr, options).await {
|
||||
Ok(stream) => stream,
|
||||
Err(error) => {
|
||||
tracing::error!(?error, ?dst_addr, "port-forward connect failed");
|
||||
return;
|
||||
}
|
||||
};
|
||||
match tokio::io::copy_bidirectional(&mut incoming, &mut outgoing).await {
|
||||
Ok((from_client, from_server)) => tracing::info!(
|
||||
?dst_addr,
|
||||
from_client,
|
||||
from_server,
|
||||
"port-forward connection finished"
|
||||
),
|
||||
Err(error) => tracing::error!(
|
||||
?error,
|
||||
?dst_addr,
|
||||
"port-forward connection failed"
|
||||
),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
drop(listener);
|
||||
select! {
|
||||
biased;
|
||||
_ = cancel.cancelled() => break,
|
||||
_ = crate::foundation::time::sleep(TCP_PORT_FORWARD_REBIND_DELAY) => {}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async fn add_udp_port_forward(&self, cfg: &PortForwardConfig) -> anyhow::Result<()> {
|
||||
let (bind_addr, dst_addr) = (cfg.bind_addr, cfg.dst_addr);
|
||||
let forward = cfg.clone();
|
||||
|
||||
Reference in New Issue
Block a user