diff --git a/.cargo/config b/.cargo/config.toml similarity index 89% rename from .cargo/config rename to .cargo/config.toml index 74438e7d..296abef6 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 fb2d30ce..9a7f1905 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 3dc6e306..cd0657b1 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 bd1ae95c..0e2e9205 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 dfedce55..c9e50876 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 e36c184c..80166642 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 4e6be22b..63cb2bb2 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 6c80c27e..b19c362d 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 c98b2315..bf702f88 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 ad4a4723..6ce7ce36 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 9fbc786e..d337cfbb 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 947a1a88..aac1b777 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 1b947701..bbb0a016 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 61b53f7e..6130cdad 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 73822665..938ffc3c 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 5564a959..9604e373 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 9a71f10f..d574311e 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 1feb461b..0eab45e7 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 33c53a82..44dc2230 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 3bbc25fa..f4ddeff9 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 b7b9624c..59bdbf43 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 fda9e5a3..2ce0a05d 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 2423ccdb..9ef3428b 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