diff --git a/.cargo/config b/.cargo/config.toml similarity index 90% rename from .cargo/config rename to .cargo/config.toml index 74438e7..296abef 100644 --- a/.cargo/config +++ b/.cargo/config.toml @@ -1,8 +1,14 @@ +[target.x86_64-unknown-linux-musl] +linker = "rust-lld" +rustflags = ["-C", "linker-flavor=ld.lld"] + [target.aarch64-unknown-linux-gnu] linker = "aarch64-linux-gnu-gcc" + [target.aarch64-unknown-linux-musl] linker = "aarch64-linux-musl-gcc" rustflags = ["-C", "target-feature=+crt-static"] + [target.'cfg(all(windows, target_env = "msvc"))'] rustflags = ["-C", "target-feature=+crt-static"] diff --git a/.gitignore b/.gitignore index fb2d30c..9a7f190 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,6 @@ nohup.out components.d.ts musl_gcc + +# log +easytier-panic.log diff --git a/Cargo.lock b/Cargo.lock index 3dc6e30..cd0657b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -387,7 +387,7 @@ dependencies = [ [[package]] name = "boringtun" version = "0.6.0" -source = "git+https://github.com/KKRainbow/boringtun.git#449204c3eca736dc23b075d81426527a357e2f2a" +source = "git+https://github.com/EasyTier/boringtun.git?rev=449204c#449204c3eca736dc23b075d81426527a357e2f2a" dependencies = [ "aead", "atomic-shim", @@ -1266,7 +1266,7 @@ checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" [[package]] name = "easytier" -version = "1.0.0" +version = "1.0.1-pre" dependencies = [ "aes-gcm", "anyhow", @@ -1309,6 +1309,7 @@ dependencies = [ "quinn", "rand 0.8.5", "rcgen", + "regex", "reqwest", "ring 0.17.8", "rstest", @@ -1348,6 +1349,7 @@ dependencies = [ "chrono", "dashmap", "easytier", + "gethostname", "once_cell", "privilege", "serde", diff --git a/Cargo.toml b/Cargo.toml index bd1ae95..0e2e920 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,3 +8,8 @@ panic = "unwind" [profile.release] panic = "unwind" +# panic = "abort" +# lto = true +# codegen-units = 1 +# strip = true +# opt-level = "z" diff --git a/README.md b/README.md index dfedce5..c9e5087 100644 --- a/README.md +++ b/README.md @@ -218,19 +218,22 @@ After successfully starting easytier-core, use easytier-cli to obtain the WireGu $> easytier-cli vpn-portal portal_name: wireguard -client_config: +############### client_config_start ############### + [Interface] PrivateKey = 9VDvlaIC9XHUvRuE06hD2CEDrtGF+0lDthgr9SZfIho= -Address = 10.14.14.0/24 # should assign an ip from this cidr manually +Address = 10.14.14.0/32 # should assign an ip from this cidr manually [Peer] PublicKey = zhrZQg4QdPZs8CajT3r4fmzcNsWpBL9ImQCUsnlXyGM= -AllowedIPs = 192.168.80.0/20,10.147.223.0/24,10.144.144.0/24 -Endpoint = 0.0.0.0:11013 # should be the public ip of the vpn server +AllowedIPs = 10.144.144.0/24,10.14.14.0/24 +Endpoint = 0.0.0.0:11013 # should be the public ip(or domain) of the vpn server +PersistentKeepalive = 25 + +############### client_config_end ############### connected_clients: [] - ``` Before using the Client Config, you need to modify the Interface Address and Peer Endpoint to the client's IP and the IP of the EasyTier node, respectively. Import the configuration file into the WireGuard client to access the EasyTier network. diff --git a/README_CN.md b/README_CN.md index e36c184..8016664 100644 --- a/README_CN.md +++ b/README_CN.md @@ -219,19 +219,22 @@ easytier-core 启动成功后,使用 easytier-cli 获取 WireGuard Client 的 $> easytier-cli vpn-portal portal_name: wireguard -client_config: +############### client_config_start ############### + [Interface] PrivateKey = 9VDvlaIC9XHUvRuE06hD2CEDrtGF+0lDthgr9SZfIho= -Address = 10.14.14.0/24 # should assign an ip from this cidr manually +Address = 10.14.14.0/32 # should assign an ip from this cidr manually [Peer] PublicKey = zhrZQg4QdPZs8CajT3r4fmzcNsWpBL9ImQCUsnlXyGM= -AllowedIPs = 192.168.80.0/20,10.147.223.0/24,10.144.144.0/24 -Endpoint = 0.0.0.0:11013 # should be the public ip of the vpn server +AllowedIPs = 10.144.144.0/24,10.14.14.0/24 +Endpoint = 0.0.0.0:11013 # should be the public ip(or domain) of the vpn server +PersistentKeepalive = 25 + +############### client_config_end ############### connected_clients: [] - ``` 使用 Client Config 前,需要将 Interface Address 和 Peer Endpoint 分别修改为客户端的 IP 和 EasyTier 节点的 IP。将配置文件导入 WireGuard 客户端,即可访问 EasyTier 网络。 diff --git a/easytier-gui/locales/cn.yml b/easytier-gui/locales/cn.yml index 4e6be22..63cb2bb 100644 --- a/easytier-gui/locales/cn.yml +++ b/easytier-gui/locales/cn.yml @@ -32,6 +32,7 @@ settings: 设置 exchange_language: Switch to English exit: 退出 chips_placeholder: 例如: {0}, 按回车添加 +hostname_placeholder: '留空默认为主机名: {0}' off_text: 点击关闭 on_text: 点击开启 show_config: 显示配置 diff --git a/easytier-gui/locales/en.yml b/easytier-gui/locales/en.yml index 6c80c27..b19c362 100644 --- a/easytier-gui/locales/en.yml +++ b/easytier-gui/locales/en.yml @@ -33,6 +33,7 @@ exchange_language: 切换中文 exit: Exit chips_placeholder: 'e.g: {0}, press Enter to add' +hostname_placeholder: 'Leave blank and default to host name: {0}' off_text: Press to disable on_text: Press to enable show_config: Show Config diff --git a/easytier-gui/src-tauri/Cargo.toml b/easytier-gui/src-tauri/Cargo.toml index c98b231..bf702f8 100644 --- a/easytier-gui/src-tauri/Cargo.toml +++ b/easytier-gui/src-tauri/Cargo.toml @@ -11,7 +11,11 @@ edition = "2021" tauri-build = { version = "1", features = [] } [dependencies] -tauri = { version = "1", features = [ "process-exit", "system-tray", "shell-open"] } +tauri = { version = "1", features = [ + "process-exit", + "system-tray", + "shell-open", +] } serde = { version = "1", features = ["derive"] } serde_json = "1" @@ -24,7 +28,7 @@ once_cell = "1.18.0" dashmap = "5.5.3" privilege = "0.3" - +gethostname = "0.4.3" [features] # This feature is used for production builds or when a dev server is not specified, DO NOT REMOVE!! custom-protocol = ["tauri/custom-protocol"] diff --git a/easytier-gui/src-tauri/src/main.rs b/easytier-gui/src-tauri/src/main.rs index ad4a472..6ce7ce3 100644 --- a/easytier-gui/src-tauri/src/main.rs +++ b/easytier-gui/src-tauri/src/main.rs @@ -42,6 +42,7 @@ struct NetworkConfig { instance_id: String, virtual_ipv4: String, + hostname: Option, network_name: String, network_secret: String, networking_method: NetworkingMethod, @@ -70,6 +71,7 @@ impl NetworkConfig { .parse() .with_context(|| format!("failed to parse instance id: {}", self.instance_id))?, ); + cfg.set_hostname(self.hostname.clone()); cfg.set_inst_name(self.network_name.clone()); cfg.set_network_identity(NetworkIdentity::new( self.network_name.clone(), @@ -281,6 +283,11 @@ fn collect_network_infos() -> Result { Ok(serde_json::to_string(&ret).map_err(|e| e.to_string())?) } +#[tauri::command] +fn get_os_hostname() -> Result { + Ok(gethostname::gethostname().to_string_lossy().to_string()) +} + fn toggle_window_visibility(window: &Window) { if window.is_visible().unwrap() { window.hide().unwrap(); @@ -318,7 +325,8 @@ fn main() { parse_network_config, run_network_instance, retain_network_instance, - collect_network_infos + collect_network_infos, + get_os_hostname ]) .system_tray(SystemTray::new().with_menu(tray_menu)) .on_system_tray_event(|app, event| match event { diff --git a/easytier-gui/src/auto-imports.d.ts b/easytier-gui/src/auto-imports.d.ts index 9fbc786..d337cfb 100644 --- a/easytier-gui/src/auto-imports.d.ts +++ b/easytier-gui/src/auto-imports.d.ts @@ -20,6 +20,7 @@ declare global { const getActivePinia: typeof import('pinia')['getActivePinia'] const getCurrentInstance: typeof import('vue')['getCurrentInstance'] const getCurrentScope: typeof import('vue')['getCurrentScope'] + const getOsHostname: typeof import('./composables/network')['getOsHostname'] const h: typeof import('vue')['h'] const inject: typeof import('vue')['inject'] const isProxy: typeof import('vue')['isProxy'] @@ -108,6 +109,7 @@ declare module 'vue' { readonly getActivePinia: UnwrapRef readonly getCurrentInstance: UnwrapRef readonly getCurrentScope: UnwrapRef + readonly getOsHostname: UnwrapRef readonly h: UnwrapRef readonly inject: UnwrapRef readonly isProxy: UnwrapRef @@ -189,6 +191,7 @@ declare module '@vue/runtime-core' { readonly getActivePinia: UnwrapRef readonly getCurrentInstance: UnwrapRef readonly getCurrentScope: UnwrapRef + readonly getOsHostname: UnwrapRef readonly h: UnwrapRef readonly inject: UnwrapRef readonly isProxy: UnwrapRef diff --git a/easytier-gui/src/components/Config.vue b/easytier-gui/src/components/Config.vue index 947a1a8..aac1b77 100644 --- a/easytier-gui/src/components/Config.vue +++ b/easytier-gui/src/components/Config.vue @@ -1,6 +1,7 @@ diff --git a/easytier-gui/src/composables/network.ts b/easytier-gui/src/composables/network.ts index 1b94770..bbb0a01 100644 --- a/easytier-gui/src/composables/network.ts +++ b/easytier-gui/src/composables/network.ts @@ -16,3 +16,7 @@ export async function retainNetworkInstance(instanceIds: string[]): Promise> { return JSON.parse(await invoke('collect_network_infos')) } + +export async function getOsHostname(): Promise { + return await invoke('get_os_hostname') +} \ No newline at end of file diff --git a/easytier-gui/src/types/network.ts b/easytier-gui/src/types/network.ts index 61b53f7..6130cda 100644 --- a/easytier-gui/src/types/network.ts +++ b/easytier-gui/src/types/network.ts @@ -10,6 +10,7 @@ export interface NetworkConfig { instance_id: string virtual_ipv4: string + hostname?: string network_name: string network_secret: string diff --git a/easytier/Cargo.toml b/easytier/Cargo.toml index 7382266..938ffc3 100644 --- a/easytier/Cargo.toml +++ b/easytier/Cargo.toml @@ -3,7 +3,7 @@ name = "easytier" description = "A full meshed p2p VPN, connecting all your devices in one network with one command." homepage = "https://github.com/KKRainbow/EasyTier" repository = "https://github.com/KKRainbow/EasyTier" -version = "1.0.0" +version = "1.0.1-pre" edition = "2021" authors = ["kkrainbow"] keywords = ["vpn", "p2p", "network", "easytier"] @@ -58,6 +58,8 @@ async-trait = "0.1.74" dashmap = "5.5.3" timedmap = "=1.0.1" +regex = "1" + # for full-path zero-copy zerocopy = { version = "0.7.32", features = ["derive", "simd"] } bytes = "1.5.0" @@ -125,7 +127,7 @@ network-interface = "1.1.1" pathfinding = "4.9.1" # for encryption -boringtun = { git = "https://github.com/EasyTier/boringtun.git", optional = true } +boringtun = { git = "https://github.com/EasyTier/boringtun.git", optional = true, rev = "449204c" } ring = { version = "0.17", optional = true } bitflags = "2.5" aes-gcm = { version = "0.10.3", optional = true } diff --git a/easytier/src/common/config.rs b/easytier/src/common/config.rs index 5564a95..9604e37 100644 --- a/easytier/src/common/config.rs +++ b/easytier/src/common/config.rs @@ -14,6 +14,9 @@ pub trait ConfigLoader: Send + Sync { fn get_id(&self) -> uuid::Uuid; fn set_id(&self, id: uuid::Uuid); + fn get_hostname(&self) -> String; + fn set_hostname(&self, name: Option); + fn get_inst_name(&self) -> String; fn set_inst_name(&self, name: String); @@ -152,6 +155,7 @@ pub struct Flags { #[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] struct Config { netns: Option, + hostname: Option, instance_name: Option, instance_id: Option, ipv4: Option, @@ -190,6 +194,7 @@ impl TomlConfigLoader { config_str, config_str ) })?; + Ok(TomlConfigLoader { config: Arc::new(Mutex::new(config)), }) @@ -216,6 +221,36 @@ impl ConfigLoader for TomlConfigLoader { self.config.lock().unwrap().instance_name = Some(name); } + fn get_hostname(&self) -> String { + let hostname = self.config.lock().unwrap().hostname.clone(); + + match hostname { + Some(hostname) => { + if !hostname.is_empty() { + let re = regex::Regex::new(r"[^\u4E00-\u9FA5a-zA-Z0-9\-]*").unwrap(); + let mut name = re.replace_all(&hostname, "").to_string(); + + if name.len() > 32 { + name = name.chars().take(32).collect::(); + } + + if hostname != name { + self.set_hostname(Some(name.clone())); + } + name + } else { + self.set_hostname(None); + gethostname::gethostname().to_string_lossy().to_string() + } + } + None => gethostname::gethostname().to_string_lossy().to_string(), + } + } + + fn set_hostname(&self, name: Option) { + self.config.lock().unwrap().hostname = name; + } + fn get_netns(&self) -> Option { self.config.lock().unwrap().netns.clone() } diff --git a/easytier/src/common/global_ctx.rs b/easytier/src/common/global_ctx.rs index 9a71f10..d574311 100644 --- a/easytier/src/common/global_ctx.rs +++ b/easytier/src/common/global_ctx.rs @@ -54,7 +54,7 @@ pub struct GlobalCtx { ip_collector: Arc, - hotname: AtomicCell>, + hostname: String, stun_info_collection: Box, @@ -80,6 +80,7 @@ impl GlobalCtx { let id = config_fs.get_id(); let network = config_fs.get_network_identity(); let net_ns = NetNS::new(config_fs.get_netns()); + let hostname = config_fs.get_hostname(); let (event_bus, _) = tokio::sync::broadcast::channel(100); @@ -96,7 +97,7 @@ impl GlobalCtx { ip_collector: Arc::new(IPCollector::new(net_ns)), - hotname: AtomicCell::new(None), + hostname, stun_info_collection: Box::new(StunInfoCollector::new_with_default_servers()), @@ -165,15 +166,8 @@ impl GlobalCtx { self.ip_collector.clone() } - pub fn get_hostname(&self) -> Option { - if let Some(hostname) = self.hotname.take() { - self.hotname.store(Some(hostname.clone())); - return Some(hostname); - } - - let hostname = gethostname::gethostname().to_string_lossy().to_string(); - self.hotname.store(Some(hostname.clone())); - return Some(hostname); + pub fn get_hostname(&self) -> String { + return self.hostname.clone(); } pub fn get_stun_info_collector(&self) -> impl StunInfoCollectorTrait + '_ { diff --git a/easytier/src/connector/manual.rs b/easytier/src/connector/manual.rs index 1feb461..0eab45e 100644 --- a/easytier/src/connector/manual.rs +++ b/easytier/src/connector/manual.rs @@ -167,7 +167,6 @@ impl ManualConnectorManager { let mut reconn_interval = tokio::time::interval(std::time::Duration::from_millis( use_global_var!(MANUAL_CONNECTOR_RECONNECT_INTERVAL_MS), )); - let mut reconn_tasks = JoinSet::new(); let (reconn_result_send, mut reconn_result_recv) = mpsc::channel(100); loop { @@ -176,8 +175,8 @@ impl ManualConnectorManager { if let Ok(event) = event { Self::handle_event(&event, data.clone()).await; } else { - log::warn!("event_recv closed"); - panic!("event_recv closed"); + tracing::warn!(?event, "event_recv got error"); + panic!("event_recv got error, err: {:?}", event); } } @@ -193,7 +192,7 @@ impl ManualConnectorManager { let insert_succ = data.reconnecting.insert(dead_url.clone()); assert!(insert_succ); - reconn_tasks.spawn(async move { + tokio::spawn(async move { let reconn_ret = Self::conn_reconnect(data_clone.clone(), dead_url.clone(), connector.clone()).await; sender.send(reconn_ret).await.unwrap(); @@ -205,8 +204,7 @@ impl ManualConnectorManager { } ret = reconn_result_recv.recv() => { - log::warn!("reconn_tasks done, out: {:?}", ret); - let _ = reconn_tasks.join_next().await.unwrap(); + log::warn!("reconn_tasks done, reconn result: {:?}", ret); } } } diff --git a/easytier/src/easytier-cli.rs b/easytier/src/easytier-cli.rs index 33c53a8..44dc223 100644 --- a/easytier/src/easytier-cli.rs +++ b/easytier/src/easytier-cli.rs @@ -25,7 +25,7 @@ use humansize::format_size; use tabled::settings::Style; #[derive(Parser, Debug)] -#[command(author, version, about, long_about = None)] +#[command(name = "easytier-cli", author, version, about, long_about = None)] struct Cli { /// the instance name #[arg(short = 'p', long, default_value = "127.0.0.1:15888")] @@ -360,8 +360,15 @@ async fn main() -> Result<(), Error> { .into_inner() .vpn_portal_info .unwrap_or_default(); - println!("portal_name: {}\n", resp.vpn_type); - println!("client_config:{}", resp.client_config); + println!("portal_name: {}", resp.vpn_type); + println!( + r#" +############### client_config_start ############### +{} +############### client_config_end ############### +"#, + resp.client_config + ); println!("connected_clients:\n{:#?}", resp.connected_clients); } } diff --git a/easytier/src/easytier-core.rs b/easytier/src/easytier-core.rs index 3bbc25f..f4ddeff 100644 --- a/easytier/src/easytier-core.rs +++ b/easytier/src/easytier-core.rs @@ -41,7 +41,7 @@ use mimalloc_rust::*; static GLOBAL_MIMALLOC: GlobalMiMalloc = GlobalMiMalloc; #[derive(Parser, Debug)] -#[command(author, version, about, long_about = None)] +#[command(name = "easytier-core", author, version, about, long_about = None)] struct Cli { #[arg( short, @@ -111,6 +111,9 @@ struct Cli { #[arg(long, help = "directory to store log files")] file_log_dir: Option, + #[arg(long, help = "host name to identify this device")] + hostname: Option, + #[arg( short = 'm', long, @@ -177,6 +180,9 @@ impl From for TomlConfigLoader { let cfg = TomlConfigLoader::default(); cfg.set_inst_name(cli.instance_name.clone()); + + cfg.set_hostname(cli.hostname.clone()); + cfg.set_network_identity(NetworkIdentity::new( cli.network_name.clone(), cli.network_secret.clone(), @@ -422,7 +428,7 @@ pub async fn async_main(cli: Cli) { }); println!("Starting easytier with config:"); - println!("############### TOML ##############\n"); + println!("############### TOML ###############\n"); println!("{}", cfg.dump()); println!("-----------------------------------"); diff --git a/easytier/src/peers/peer_ospf_route.rs b/easytier/src/peers/peer_ospf_route.rs index b7b9624..59bdbf4 100644 --- a/easytier/src/peers/peer_ospf_route.rs +++ b/easytier/src/peers/peer_ospf_route.rs @@ -101,7 +101,7 @@ impl RoutePeerInfo { .map(|x| x.to_string()) .chain(global_ctx.get_vpn_portal_cidr().map(|x| x.to_string())) .collect(), - hostname: global_ctx.get_hostname(), + hostname: Some(global_ctx.get_hostname()), udp_stun_info: global_ctx .get_stun_info_collector() .get_stun_info() @@ -138,11 +138,7 @@ impl Into for RoutePeerInfo { next_hop_peer_id: 0, cost: self.cost as i32, proxy_cidrs: self.proxy_cidrs.clone(), - hostname: if let Some(hostname) = &self.hostname { - hostname.clone() - } else { - "".to_string() - }, + hostname: self.hostname.unwrap_or_default(), stun_info: { let mut stun_info = StunInfo::default(); if let Ok(udp_nat_type) = NatType::try_from(self.udp_stun_info as i32) { diff --git a/easytier/src/peers/peer_rip_route.rs b/easytier/src/peers/peer_rip_route.rs index fda9e5a..2ce0a05 100644 --- a/easytier/src/peers/peer_rip_route.rs +++ b/easytier/src/peers/peer_rip_route.rs @@ -52,7 +52,7 @@ impl SyncPeerInfo { .map(|x| x.to_string()) .chain(global_ctx.get_vpn_portal_cidr().map(|x| x.to_string())) .collect(), - hostname: global_ctx.get_hostname(), + hostname: Some(global_ctx.get_hostname()), udp_stun_info: global_ctx .get_stun_info_collector() .get_stun_info() @@ -585,11 +585,7 @@ impl Route for BasicRoute { route.next_hop_peer_id = route_info.peer_id; route.cost = route_info.cost as i32; route.proxy_cidrs = route_info.proxy_cidrs.clone(); - route.hostname = if let Some(hostname) = &route_info.hostname { - hostname.clone() - } else { - "".to_string() - }; + route.hostname = route_info.hostname.clone().unwrap_or_default(); let mut stun_info = StunInfo::default(); if let Ok(udp_nat_type) = NatType::try_from(route_info.udp_stun_info as i32) { diff --git a/easytier/src/vpn_portal/wireguard.rs b/easytier/src/vpn_portal/wireguard.rs index 2423ccd..9ef3428 100644 --- a/easytier/src/vpn_portal/wireguard.rs +++ b/easytier/src/vpn_portal/wireguard.rs @@ -264,32 +264,35 @@ impl VpnPortal for WireGuard { break; } + let vpn_cfg = global_ctx.config.get_vpn_portal_config().unwrap(); + let client_cidr = vpn_cfg.client_cidr; + + allow_ips.push(client_cidr.to_string()); + let allow_ips = allow_ips .into_iter() .map(|x| x.to_string()) .collect::>() .join(","); - let vpn_cfg = global_ctx.config.get_vpn_portal_config().unwrap(); - let client_cidr = vpn_cfg.client_cidr; - let cfg = self.inner.as_ref().unwrap().wg_config.clone(); let cfg_str = format!( r#" [Interface] PrivateKey = {peer_secret_key} -Address = {client_cidr} # should assign an ip from this cidr manually +Address = {address} # should assign an ip from this cidr manually [Peer] PublicKey = {my_public_key} AllowedIPs = {allow_ips} -Endpoint = {listenr_addr} # should be the public ip of the vpn server +Endpoint = {listenr_addr} # should be the public ip(or domain) of the vpn server +PersistentKeepalive = 25 "#, peer_secret_key = BASE64_STANDARD.encode(cfg.peer_secret_key()), my_public_key = BASE64_STANDARD.encode(cfg.my_public_key()), listenr_addr = self.inner.as_ref().unwrap().listenr_addr, allow_ips = allow_ips, - client_cidr = client_cidr, + address = client_cidr.first_address().to_string() + "/32", ); cfg_str