diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index de98b815..9a42820e 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -669,7 +669,7 @@ dependencies = [ [[package]] name = "boringtun" version = "0.6.0" -source = "git+https://github.com/DefGuard/wireguard-rs?rev=886186c1e088e4805ab8049436c28cf3ea26d727#886186c1e088e4805ab8049436c28cf3ea26d727" +source = "git+https://github.com/DefGuard/wireguard-rs?rev=c99c0b209b19d9ce82e0f5d0d727261f8f9916b3#c99c0b209b19d9ce82e0f5d0d727261f8f9916b3" dependencies = [ "aead", "base64 0.22.1", @@ -1478,7 +1478,7 @@ dependencies = [ [[package]] name = "defguard_wireguard_rs" version = "0.9.0" -source = "git+https://github.com/DefGuard/wireguard-rs?rev=886186c1e088e4805ab8049436c28cf3ea26d727#886186c1e088e4805ab8049436c28cf3ea26d727" +source = "git+https://github.com/DefGuard/wireguard-rs?rev=c99c0b209b19d9ce82e0f5d0d727261f8f9916b3#c99c0b209b19d9ce82e0f5d0d727261f8f9916b3" dependencies = [ "base64 0.22.1", "boringtun", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 26774378..ea2930b8 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -4,7 +4,7 @@ default-members = [".", "cli"] [workspace.dependencies] clap = { version = "4.5", features = ["cargo", "derive", "env"] } -defguard_wireguard_rs = { git = "https://github.com/DefGuard/wireguard-rs", rev = "886186c1e088e4805ab8049436c28cf3ea26d727" } +defguard_wireguard_rs = { git = "https://github.com/DefGuard/wireguard-rs", rev = "c99c0b209b19d9ce82e0f5d0d727261f8f9916b3" } dirs-next = "2.0" prost = "0.14" reqwest = { version = "0.12", features = ["cookies", "json"] } diff --git a/src-tauri/proto b/src-tauri/proto index 96249ebd..db6741ae 160000 --- a/src-tauri/proto +++ b/src-tauri/proto @@ -1 +1 @@ -Subproject commit 96249ebde0556f4ae8c47eebc6015efb04ed0104 +Subproject commit db6741ae29d030d0ffecd2ef52aebfb51e9c7f37 diff --git a/src-tauri/src/app_config.rs b/src-tauri/src/app_config.rs index 492d9023..0fd9fb23 100644 --- a/src-tauri/src/app_config.rs +++ b/src-tauri/src/app_config.rs @@ -71,6 +71,8 @@ pub struct AppConfig { pub log_level: LevelFilter, /// In seconds. How much time after last network activity the connection is automatically dropped. pub peer_alive_period: u32, + /// Maximal transmission unit. 0 means default value. + pub mtu: u32, } // Important: keep in sync with client store default in frontend @@ -82,6 +84,7 @@ impl Default for AppConfig { tray_theme: AppTrayTheme::Color, log_level: LevelFilter::Info, peer_alive_period: 300, + mtu: 0, } } } @@ -130,4 +133,14 @@ impl AppConfig { } } } + + /// Wraps MTU in an Option. We don't store Option directly in AppConfig to avoid struct-patch + /// ambiguity when applying updates coming from the frontend. An incoming MTU value of 0 is + /// interpreted as a request to fall back to the default. + pub fn get_mtu(&self) -> Option { + match self.mtu { + 0 => None, + v => Some(v), + } + } } diff --git a/src-tauri/src/database/models/location.rs b/src-tauri/src/database/models/location.rs index 3b092b50..bfaa553d 100644 --- a/src-tauri/src/database/models/location.rs +++ b/src-tauri/src/database/models/location.rs @@ -257,7 +257,7 @@ impl Location { } #[cfg(not(target_os = "macos"))] - pub(crate) async fn interface_configurarion<'e, E>( + pub(crate) async fn interface_configuration<'e, E>( &self, executor: E, interface_name: String, diff --git a/src-tauri/src/service/mod.rs b/src-tauri/src/service/mod.rs index b81a70c1..cb90ebf4 100644 --- a/src-tauri/src/service/mod.rs +++ b/src-tauri/src/service/mod.rs @@ -575,6 +575,7 @@ impl From for proto::InterfaceConfig { .join(","), port: u32::from(config.port), peers: config.peers.into_iter().map(Into::into).collect(), + mtu: config.mtu, } } } @@ -592,7 +593,7 @@ impl From for InterfaceConfiguration { addresses, port: config.port as u16, peers: config.peers.into_iter().map(Into::into).collect(), - mtu: None, + mtu: config.mtu, } } } diff --git a/src-tauri/src/utils.rs b/src-tauri/src/utils.rs index d518655c..7797602a 100644 --- a/src-tauri/src/utils.rs +++ b/src-tauri/src/utils.rs @@ -59,6 +59,7 @@ pub(crate) async fn setup_interface( location: &Location, name: &str, preshared_key: Option, + mtu: Option, pool: &DbPool, ) -> Result { debug!("Setting up interface for location: {location}"); @@ -76,9 +77,10 @@ pub(crate) async fn setup_interface( }; debug!("Found free port: {port} for interface {interface_name}."); - let interface_config = location - .interface_configurarion(pool, interface_name.clone(), preshared_key) + let mut interface_config = location + .interface_configuration(pool, interface_name.clone(), preshared_key) .await?; + interface_config.mtu = mtu; debug!("Creating interface for location {location} with configuration {interface_config:?}"); let request = CreateInterfaceRequest { config: Some(interface_config.clone().into()), @@ -120,6 +122,7 @@ pub(crate) async fn setup_interface( location: &Location, name: &str, preshared_key: Option, + _mtu: Option, pool: &DbPool, ) -> Result { debug!("Setting up interface for location: {location}"); @@ -307,7 +310,11 @@ pub fn get_service_log_dir() -> &'static Path { } /// Setup client interface -pub async fn setup_interface_tunnel(tunnel: &Tunnel, name: &str) -> Result { +pub async fn setup_interface_tunnel( + tunnel: &Tunnel, + name: &str, + mtu: Option, +) -> Result { debug!("Setting up interface for tunnel {tunnel}"); let interface_name = get_interface_name(name); // prepare peer config @@ -397,7 +404,7 @@ pub async fn setup_interface_tunnel(tunnel: &Tunnel, name: &str) -> Result Result<(), Error> { debug!("Setting up the connection for location {}", location.name); let state = handle.state::(); - let interface_name = setup_interface(location, &location.name, preshared_key, &DB_POOL).await?; + let mtu = state.app_config.lock().unwrap().get_mtu(); + let interface_name = + setup_interface(location, &location.name, preshared_key, mtu, &DB_POOL).await?; state .add_connection(location.id, &interface_name, ConnectionType::Location) .await; @@ -607,7 +616,8 @@ pub(crate) async fn handle_connection_for_tunnel( ) -> Result<(), Error> { debug!("Setting up the connection for tunnel: {}", tunnel.name); let state = handle.state::(); - let interface_name = setup_interface_tunnel(tunnel, &tunnel.name).await?; + let mtu = state.app_config.lock().unwrap().get_mtu(); + let interface_name = setup_interface_tunnel(tunnel, &tunnel.name, mtu).await?; state .add_connection(tunnel.id, &interface_name, ConnectionType::Tunnel) .await; diff --git a/src/i18n/en/index.ts b/src/i18n/en/index.ts index 2dca9b4f..c598695e 100644 --- a/src/i18n/en/index.ts +++ b/src/i18n/en/index.ts @@ -161,6 +161,11 @@ If you are an admin/devops - all your customers (instances) and all their tunnel helper: 'If active connection exceeds given time without making an handshake with the server. The connection will be considered invalid and disconnected automatically.', }, + mtu: { + title: 'MTU (Maximum Transmission Unit)', + helper: + 'MTU sets the largest packet size sent through the network. Lowering it can improve connection stability in restrictive or unreliable ISP networks. The default value on most systems is 1500. Try lowering it to 1300-1400 if you encounter ISP-related issues. 0 = default.', + }, tray: { title: 'System tray', label: 'Tray icon theme', diff --git a/src/i18n/i18n-types.ts b/src/i18n/i18n-types.ts index 088f2ae0..7eeaf5be 100644 --- a/src/i18n/i18n-types.ts +++ b/src/i18n/i18n-types.ts @@ -376,6 +376,16 @@ type RootTranslation = { */ helper: string } + mtu: { + /** + * M​T​U​ ​(​M​a​x​i​m​u​m​ ​T​r​a​n​s​m​i​s​s​i​o​n​ ​U​n​i​t​) + */ + title: string + /** + * M​T​U​ ​s​e​t​s​ ​t​h​e​ ​l​a​r​g​e​s​t​ ​p​a​c​k​e​t​ ​s​i​z​e​ ​s​e​n​t​ ​t​h​r​o​u​g​h​ ​t​h​e​ ​n​e​t​w​o​r​k​.​ ​L​o​w​e​r​i​n​g​ ​i​t​ ​c​a​n​ ​i​m​p​r​o​v​e​ ​c​o​n​n​e​c​t​i​o​n​ ​s​t​a​b​i​l​i​t​y​ ​i​n​ ​r​e​s​t​r​i​c​t​i​v​e​ ​o​r​ ​u​n​r​e​l​i​a​b​l​e​ ​I​S​P​ ​n​e​t​w​o​r​k​s​.​ ​T​h​e​ ​d​e​f​a​u​l​t​ ​v​a​l​u​e​ ​o​n​ ​m​o​s​t​ ​s​y​s​t​e​m​s​ ​i​s​ ​1​5​0​0​.​ ​T​r​y​ ​l​o​w​e​r​i​n​g​ ​i​t​ ​t​o​ ​1​3​0​0​-​1​4​0​0​ ​i​f​ ​y​o​u​ ​e​n​c​o​u​n​t​e​r​ ​I​S​P​-​r​e​l​a​t​e​d​ ​i​s​s​u​e​s​.​ ​0​ ​=​ ​d​e​f​a​u​l​t​. + */ + helper: string + } tray: { /** * S​y​s​t​e​m​ ​t​r​a​y @@ -2066,6 +2076,16 @@ export type TranslationFunctions = { */ helper: () => LocalizedString } + mtu: { + /** + * MTU (Maximum Transmission Unit) + */ + title: () => LocalizedString + /** + * MTU sets the largest packet size sent through the network. Lowering it can improve connection stability in restrictive or unreliable ISP networks. The default value on most systems is 1500. Try lowering it to 1300-1400 if you encounter ISP-related issues. 0 = default. + */ + helper: () => LocalizedString + } tray: { /** * System tray diff --git a/src/pages/client/clientAPI/types.ts b/src/pages/client/clientAPI/types.ts index 9bb6dce6..b4eeb624 100644 --- a/src/pages/client/clientAPI/types.ts +++ b/src/pages/client/clientAPI/types.ts @@ -80,6 +80,7 @@ export type AppConfig = { tray_theme: TrayIconTheme; check_for_updates: boolean; peer_alive_period: number; + mtu: number; }; export type PlatformInfo = { diff --git a/src/pages/client/hooks/useClientStore.tsx b/src/pages/client/hooks/useClientStore.tsx index 52d16890..a722d8c5 100644 --- a/src/pages/client/hooks/useClientStore.tsx +++ b/src/pages/client/hooks/useClientStore.tsx @@ -35,6 +35,7 @@ const defaultValues: StoreValues = { tray_theme: 'color', check_for_updates: true, peer_alive_period: 300, + mtu: 0, }, platformInfo: { client_version: '', diff --git a/src/pages/client/pages/ClientSettingsPage/components/GlobalSettingsTab/GlobalSettingsTab.tsx b/src/pages/client/pages/ClientSettingsPage/components/GlobalSettingsTab/GlobalSettingsTab.tsx index 4d6dea45..8f2ff8a5 100644 --- a/src/pages/client/pages/ClientSettingsPage/components/GlobalSettingsTab/GlobalSettingsTab.tsx +++ b/src/pages/client/pages/ClientSettingsPage/components/GlobalSettingsTab/GlobalSettingsTab.tsx @@ -81,6 +81,12 @@ export const GlobalSettingsTab = () => { required_error: LL.form.errors.required(), }) .gte(120, LL.form.errors.minValue({ min: 120 })), + mtu: z + .number({ + invalid_type_error: LL.form.errors.required(), + required_error: LL.form.errors.required(), + }) + .lte(65535, LL.form.errors.maxValue({ max: 65535 })), }), [LL.form.errors], ); @@ -139,6 +145,15 @@ export const GlobalSettingsTab = () => { +
+
+

{localLL.mtu.title()}

+ +

{localLL.mtu.helper()}

+
+
+ +
); };