diff --git a/client/android/awg/src/main/kotlin/Awg.kt b/client/android/awg/src/main/kotlin/Awg.kt index c21caaff..5a3255c4 100644 --- a/client/android/awg/src/main/kotlin/Awg.kt +++ b/client/android/awg/src/main/kotlin/Awg.kt @@ -64,7 +64,7 @@ class Awg : Wireguard() { val configDataJson = config.getJSONObject("awg_config_data") val configData = parseConfigData(configDataJson.getString("config")) return AwgConfig.build { - configWireguard(configData) + configWireguard(configData, configDataJson) configSplitTunneling(config) configData["Jc"]?.let { setJc(it.toInt()) } configData["Jmin"]?.let { setJmin(it.toInt()) } diff --git a/client/android/wireguard/src/main/kotlin/Wireguard.kt b/client/android/wireguard/src/main/kotlin/Wireguard.kt index 40fa4ec6..a71d7936 100644 --- a/client/android/wireguard/src/main/kotlin/Wireguard.kt +++ b/client/android/wireguard/src/main/kotlin/Wireguard.kt @@ -92,12 +92,12 @@ open class Wireguard : Protocol() { val configDataJson = config.getJSONObject("wireguard_config_data") val configData = parseConfigData(configDataJson.getString("config")) return WireguardConfig.build { - configWireguard(configData) + configWireguard(configData, configDataJson) configSplitTunneling(config) } } - protected fun WireguardConfig.Builder.configWireguard(configData: Map) { + protected fun WireguardConfig.Builder.configWireguard(configData: Map, configDataJson: JSONObject) { configData["Address"]?.split(",")?.map { address -> InetNetwork.parse(address.trim()) }?.forEach(::addAddress) @@ -118,7 +118,14 @@ open class Wireguard : Protocol() { if (routes.any { it !in defRoutes }) disableSplitTunneling() addRoutes(routes) - configData["MTU"]?.let { setMtu(it.toInt()) } + configDataJson.optString("mtu").let { mtu -> + if (mtu.isNotEmpty()) { + setMtu(mtu.toInt()) + } else { + configData["MTU"]?.let { setMtu(it.toInt()) } + } + } + configData["Endpoint"]?.let { setEndpoint(InetEndpoint.parse(it)) } configData["PersistentKeepalive"]?.let { setPersistentKeepalive(it.toInt()) } configData["PrivateKey"]?.let { setPrivateKeyHex(it.base64ToHex()) } diff --git a/client/configurators/awg_configurator.cpp b/client/configurators/awg_configurator.cpp index 5b452755..186d2009 100644 --- a/client/configurators/awg_configurator.cpp +++ b/client/configurators/awg_configurator.cpp @@ -41,6 +41,8 @@ QString AwgConfigurator::genAwgConfig(const ServerCredentials &credentials, Dock jsonConfig[config_key::responsePacketMagicHeader] = configMap.value(config_key::responsePacketMagicHeader); jsonConfig[config_key::underloadPacketMagicHeader] = configMap.value(config_key::underloadPacketMagicHeader); jsonConfig[config_key::transportPacketMagicHeader] = configMap.value(config_key::transportPacketMagicHeader); + jsonConfig[config_key::mtu] = containerConfig.value(ProtocolProps::protoToString(Proto::Awg)).toObject(). + value(config_key::mtu).toString(protocols::awg::defaultMtu); return QJsonDocument(jsonConfig).toJson(); } diff --git a/client/configurators/wireguard_configurator.cpp b/client/configurators/wireguard_configurator.cpp index f28ac539..03c9a5b9 100644 --- a/client/configurators/wireguard_configurator.cpp +++ b/client/configurators/wireguard_configurator.cpp @@ -194,6 +194,7 @@ QString WireguardConfigurator::genWireguardConfig(const ServerCredentials &crede config.replace("$WIREGUARD_SERVER_PUBLIC_KEY", connData.serverPubKey); config.replace("$WIREGUARD_PSK", connData.pskKey); + const QJsonObject &wireguarConfig = containerConfig.value(ProtocolProps::protoToString(Proto::WireGuard)).toObject(); QJsonObject jConfig; jConfig[config_key::config] = config; @@ -205,6 +206,8 @@ QString WireguardConfigurator::genWireguardConfig(const ServerCredentials &crede jConfig[config_key::psk_key] = connData.pskKey; jConfig[config_key::server_pub_key] = connData.serverPubKey; + jConfig[config_key::mtu] = wireguarConfig.value(config_key::mtu).toString(protocols::wireguard::defaultMtu); + clientId = connData.clientPubKey; return QJsonDocument(jConfig).toJson(); diff --git a/client/core/controllers/serverController.cpp b/client/core/controllers/serverController.cpp index 99ee5b11..81f638e7 100644 --- a/client/core/controllers/serverController.cpp +++ b/client/core/controllers/serverController.cpp @@ -359,7 +359,33 @@ bool ServerController::isReinstallContainerRequired(DockerContainer container, c } if (container == DockerContainer::Awg) { - return true; + if ((oldProtoConfig.value(config_key::port).toString(protocols::awg::defaultPort) + != newProtoConfig.value(config_key::port).toString(protocols::awg::defaultPort)) + || (oldProtoConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount) + != newProtoConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount)) + || (oldProtoConfig.value(config_key::junkPacketMinSize).toString(protocols::awg::defaultJunkPacketMinSize) + != newProtoConfig.value(config_key::junkPacketMinSize).toString(protocols::awg::defaultJunkPacketMinSize)) + || (oldProtoConfig.value(config_key::junkPacketMaxSize).toString(protocols::awg::defaultJunkPacketMaxSize) + != newProtoConfig.value(config_key::junkPacketMaxSize).toString(protocols::awg::defaultJunkPacketMaxSize)) + || (oldProtoConfig.value(config_key::initPacketJunkSize).toString(protocols::awg::defaultInitPacketJunkSize) + != newProtoConfig.value(config_key::initPacketJunkSize).toString(protocols::awg::defaultInitPacketJunkSize)) + || (oldProtoConfig.value(config_key::responsePacketJunkSize).toString(protocols::awg::defaultResponsePacketJunkSize) + != newProtoConfig.value(config_key::responsePacketJunkSize).toString(protocols::awg::defaultResponsePacketJunkSize)) + || (oldProtoConfig.value(config_key::initPacketMagicHeader).toString(protocols::awg::defaultInitPacketMagicHeader) + != newProtoConfig.value(config_key::initPacketMagicHeader).toString(protocols::awg::defaultInitPacketMagicHeader)) + || (oldProtoConfig.value(config_key::responsePacketMagicHeader).toString(protocols::awg::defaultResponsePacketMagicHeader) + != newProtoConfig.value(config_key::responsePacketMagicHeader).toString(protocols::awg::defaultResponsePacketMagicHeader)) + || (oldProtoConfig.value(config_key::underloadPacketMagicHeader).toString(protocols::awg::defaultUnderloadPacketMagicHeader) + != newProtoConfig.value(config_key::underloadPacketMagicHeader).toString(protocols::awg::defaultUnderloadPacketMagicHeader)) + || (oldProtoConfig.value(config_key::transportPacketMagicHeader).toString(protocols::awg::defaultTransportPacketMagicHeader) + != newProtoConfig.value(config_key::transportPacketMagicHeader).toString(protocols::awg::defaultTransportPacketMagicHeader))) + return true; + } + + if (container == DockerContainer::WireGuard){ + if (oldProtoConfig.value(config_key::port).toString(protocols::wireguard::defaultPort) + != newProtoConfig.value(config_key::port).toString(protocols::wireguard::defaultPort)) + return true; } return false; diff --git a/client/daemon/daemon.cpp b/client/daemon/daemon.cpp index b85b2c33..b0e47817 100644 --- a/client/daemon/daemon.cpp +++ b/client/daemon/daemon.cpp @@ -251,6 +251,19 @@ bool Daemon::parseConfig(const QJsonObject& obj, InterfaceConfig& config) { GETVALUE("serverPskKey", config.m_serverPskKey, String); GETVALUE("serverPort", config.m_serverPort, Double); + if (!obj.contains("deviceMTU") || obj.value("deviceMTU").toString().toInt() == 0) + { + config.m_deviceMTU = 1420; + } else { + config.m_deviceMTU = obj.value("deviceMTU").toString().toInt(); +#ifdef Q_OS_WINDOWS +// For Windows min MTU value is 1280 (the smallest MTU legal with IPv6). + if (config.m_deviceMTU < 1280) { + config.m_deviceMTU = 1280; + } +#endif + } + config.m_deviceIpv4Address = obj.value("deviceIpv4Address").toString(); config.m_deviceIpv6Address = obj.value("deviceIpv6Address").toString(); if (config.m_deviceIpv4Address.isNull() && diff --git a/client/daemon/interfaceconfig.cpp b/client/daemon/interfaceconfig.cpp index 8aa06b9b..b2ad31c6 100644 --- a/client/daemon/interfaceconfig.cpp +++ b/client/daemon/interfaceconfig.cpp @@ -23,6 +23,7 @@ QJsonObject InterfaceConfig::toJson() const { json.insert("serverIpv4AddrIn", QJsonValue(m_serverIpv4AddrIn)); json.insert("serverIpv6AddrIn", QJsonValue(m_serverIpv6AddrIn)); json.insert("serverPort", QJsonValue((double)m_serverPort)); + json.insert("deviceMTU", QJsonValue(m_deviceMTU)); if ((m_hopType == InterfaceConfig::MultiHopExit) || (m_hopType == InterfaceConfig::SingleHop)) { json.insert("serverIpv4Gateway", QJsonValue(m_serverIpv4Gateway)); @@ -85,8 +86,13 @@ QString InterfaceConfig::toWgConf(const QMap& extra) const { if (addresses.isEmpty()) { return ""; } + out << "Address = " << addresses.join(", ") << "\n"; + if (m_deviceMTU) { + out << "MTU = " << m_deviceMTU << "\n"; + } + if (!m_dnsServer.isNull()) { QStringList dnsServers(m_dnsServer); // If the DNS is not the Gateway, it's a user defined DNS diff --git a/client/daemon/interfaceconfig.h b/client/daemon/interfaceconfig.h index 29aef085..f9869661 100644 --- a/client/daemon/interfaceconfig.h +++ b/client/daemon/interfaceconfig.h @@ -33,6 +33,7 @@ class InterfaceConfig { QString m_serverIpv6AddrIn; QString m_dnsServer; int m_serverPort = 0; + int m_deviceMTU = 1420; QList m_allowedIPAddressRanges; QStringList m_excludedAddresses; QStringList m_vpnDisabledApps; diff --git a/client/mozilla/localsocketcontroller.cpp b/client/mozilla/localsocketcontroller.cpp index 54f87a12..68a7963b 100644 --- a/client/mozilla/localsocketcontroller.cpp +++ b/client/mozilla/localsocketcontroller.cpp @@ -132,8 +132,9 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) { json.insert("serverPskKey", wgConfig.value(amnezia::config_key::psk_key)); json.insert("serverIpv4AddrIn", wgConfig.value(amnezia::config_key::hostName)); // json.insert("serverIpv6AddrIn", QJsonValue(hop.m_server.ipv6AddrIn())); - json.insert("serverPort", wgConfig.value(amnezia::config_key::port).toInt()); + json.insert("deviceMTU", wgConfig.value(amnezia::config_key::mtu)); + json.insert("serverPort", wgConfig.value(amnezia::config_key::port).toInt()); json.insert("serverIpv4Gateway", wgConfig.value(amnezia::config_key::hostName)); // json.insert("serverIpv6Gateway", QJsonValue(hop.m_server.ipv6Gateway())); json.insert("dnsServer", rawConfig.value(amnezia::config_key::dns1)); diff --git a/client/platforms/ios/Log.swift b/client/platforms/ios/Log.swift index f7f48b87..d9512dc6 100644 --- a/client/platforms/ios/Log.swift +++ b/client/platforms/ios/Log.swift @@ -38,7 +38,7 @@ struct Log { init(_ str: String) { self.records = str.split(whereSeparator: \.isNewline) .compactMap { - Record(String($0))! + Record(String($0)) } } diff --git a/client/platforms/ios/WGConfig.swift b/client/platforms/ios/WGConfig.swift index 15f99100..b703834a 100644 --- a/client/platforms/ios/WGConfig.swift +++ b/client/platforms/ios/WGConfig.swift @@ -7,6 +7,7 @@ struct WGConfig: Decodable { let initPacketJunkSize, responsePacketJunkSize: String? let dns1: String let dns2: String + let mtu: String let hostName: String let port: Int let clientIP: String @@ -25,6 +26,7 @@ struct WGConfig: Decodable { case initPacketJunkSize = "S1", responsePacketJunkSize = "S2" case dns1 case dns2 + case mtu case hostName case port case clientIP = "client_ip" @@ -58,6 +60,7 @@ struct WGConfig: Decodable { [Interface] Address = \(clientIP) DNS = \(dns1), \(dns2) + MTU = \(mtu) PrivateKey = \(clientPrivateKey) \(settings) [Peer] @@ -74,6 +77,7 @@ struct WGConfig: Decodable { [Interface] Address = \(clientIP) DNS = \(dns1), \(dns2) + MTU = \(mtu) PrivateKey = *** \(settings) [Peer] @@ -88,10 +92,11 @@ struct WGConfig: Decodable { struct OpenVPNConfig: Decodable { let config: String + let mtu: String let splitTunnelType: Int let splitTunnelSites: [String] var str: String { - "splitTunnelType: \(splitTunnelType) splitTunnelSites: \(splitTunnelSites) config: \(config)" + "splitTunnelType: \(splitTunnelType) splitTunnelSites: \(splitTunnelSites) mtu: \(mtu) config: \(config)" } } diff --git a/client/platforms/ios/ios_controller.mm b/client/platforms/ios/ios_controller.mm index c1f0f643..0800acc8 100644 --- a/client/platforms/ios/ios_controller.mm +++ b/client/platforms/ios/ios_controller.mm @@ -358,6 +358,13 @@ bool IosController::setupOpenVPN() QJsonObject openVPNConfig {}; openVPNConfig.insert(config_key::config, ovpnConfig); + + if (ovpn.contains(config_key::mtu)) { + openVPNConfig.insert(config_key::mtu, ovpn[config_key::mtu]); + } else { + openVPNConfig.insert(config_key::mtu, protocols::openvpn::defaultMtu); + } + openVPNConfig.insert(config_key::splitTunnelType, m_rawConfig[config_key::splitTunnelType]); QJsonArray splitTunnelSites = m_rawConfig[config_key::splitTunnelSites].toArray(); @@ -410,7 +417,12 @@ bool IosController::setupCloak() QJsonObject openVPNConfig {}; openVPNConfig.insert(config_key::config, ovpnConfig); - openVPNConfig.insert(config_key::splitTunnelType, m_rawConfig[config_key::splitTunnelType]); + + if (ovpn.contains(config_key::mtu)) { + openVPNConfig.insert(config_key::mtu, ovpn[config_key::mtu]); + } else { + openVPNConfig.insert(config_key::mtu, protocols::openvpn::defaultMtu); + } QJsonArray splitTunnelSites = m_rawConfig[config_key::splitTunnelSites].toArray(); @@ -433,6 +445,13 @@ bool IosController::setupWireGuard() QJsonObject wgConfig {}; wgConfig.insert(config_key::dns1, m_rawConfig[config_key::dns1]); wgConfig.insert(config_key::dns2, m_rawConfig[config_key::dns2]); + + if (config.contains(config_key::mtu)) { + wgConfig.insert(config_key::mtu, config[config_key::mtu]); + } else { + wgConfig.insert(config_key::mtu, protocols::wireguard::defaultMtu); + } + wgConfig.insert(config_key::hostName, config[config_key::hostName]); wgConfig.insert(config_key::port, config[config_key::port]); wgConfig.insert(config_key::client_ip, config[config_key::client_ip]); @@ -456,7 +475,11 @@ bool IosController::setupWireGuard() wgConfig.insert(config_key::allowed_ips, allowed_ips); } - wgConfig.insert("persistent_keep_alive", "25"); + if (config.contains(config_key::persistent_keep_alive)) { + wgConfig.insert(config_key::persistent_keep_alive, config[config_key::persistent_keep_alive]); + } else { + wgConfig.insert(config_key::persistent_keep_alive, "25"); + } QJsonDocument wgConfigDoc(wgConfig); QString wgConfigDocStr(wgConfigDoc.toJson(QJsonDocument::Compact)); @@ -471,6 +494,13 @@ bool IosController::setupAwg() QJsonObject wgConfig {}; wgConfig.insert(config_key::dns1, m_rawConfig[config_key::dns1]); wgConfig.insert(config_key::dns2, m_rawConfig[config_key::dns2]); + + if (config.contains(config_key::mtu)) { + wgConfig.insert(config_key::mtu, config[config_key::mtu]); + } else { + wgConfig.insert(config_key::mtu, protocols::awg::defaultMtu); + } + wgConfig.insert(config_key::hostName, config[config_key::hostName]); wgConfig.insert(config_key::port, config[config_key::port]); wgConfig.insert(config_key::client_ip, config[config_key::client_ip]); @@ -494,7 +524,12 @@ bool IosController::setupAwg() wgConfig.insert(config_key::allowed_ips, allowed_ips); } - wgConfig.insert("persistent_keep_alive", "25"); + if (config.contains(config_key::persistent_keep_alive)) { + wgConfig.insert(config_key::persistent_keep_alive, config[config_key::persistent_keep_alive]); + } else { + wgConfig.insert(config_key::persistent_keep_alive, "25"); + } + wgConfig.insert(config_key::initPacketMagicHeader, config[config_key::initPacketMagicHeader]); wgConfig.insert(config_key::responsePacketMagicHeader, config[config_key::responsePacketMagicHeader]); wgConfig.insert(config_key::underloadPacketMagicHeader, config[config_key::underloadPacketMagicHeader]); diff --git a/client/platforms/linux/daemon/iputilslinux.cpp b/client/platforms/linux/daemon/iputilslinux.cpp index 9a51caad..f0f2fbab 100644 --- a/client/platforms/linux/daemon/iputilslinux.cpp +++ b/client/platforms/linux/daemon/iputilslinux.cpp @@ -16,9 +16,6 @@ #include "leakdetector.h" #include "logger.h" -constexpr uint32_t ETH_MTU = 1500; -constexpr uint32_t WG_MTU_OVERHEAD = 80; - namespace { Logger logger("IPUtilsLinux"); } @@ -38,8 +35,6 @@ bool IPUtilsLinux::addInterfaceIPs(const InterfaceConfig& config) { } bool IPUtilsLinux::setMTUAndUp(const InterfaceConfig& config) { - Q_UNUSED(config); - // Create socket file descriptor to perform the ioctl operations on int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); if (sockfd < 0) { @@ -56,10 +51,10 @@ bool IPUtilsLinux::setMTUAndUp(const InterfaceConfig& config) { // FIXME: We need to know how many layers deep this particular // interface is into a tunnel to work effectively. Otherwise // we will run into fragmentation issues. - ifr.ifr_mtu = ETH_MTU - WG_MTU_OVERHEAD; + ifr.ifr_mtu = config.m_deviceMTU; int ret = ioctl(sockfd, SIOCSIFMTU, &ifr); if (ret) { - logger.error() << "Failed to set MTU -- Return code: " << ret; + logger.error() << "Failed to set MTU -- " << config.m_deviceMTU << " -- Return code: " << ret; return false; } diff --git a/client/platforms/macos/daemon/iputilsmacos.cpp b/client/platforms/macos/daemon/iputilsmacos.cpp index 0af093a4..0599e2eb 100644 --- a/client/platforms/macos/daemon/iputilsmacos.cpp +++ b/client/platforms/macos/daemon/iputilsmacos.cpp @@ -20,9 +20,6 @@ #include "logger.h" #include "macosdaemon.h" -constexpr uint32_t ETH_MTU = 1500; -constexpr uint32_t WG_MTU_OVERHEAD = 80; - namespace { Logger logger("IPUtilsMacos"); } @@ -56,10 +53,10 @@ bool IPUtilsMacos::setMTUAndUp(const InterfaceConfig& config) { // MTU strncpy(ifr.ifr_name, qPrintable(ifname), IFNAMSIZ); - ifr.ifr_mtu = ETH_MTU - WG_MTU_OVERHEAD; + ifr.ifr_mtu = config.m_deviceMTU; int ret = ioctl(sockfd, SIOCSIFMTU, &ifr); if (ret) { - logger.error() << "Failed to set MTU:" << strerror(errno); + logger.error() << "Failed to set MTU -- " << config.m_deviceMTU << " -- Return code: " << ret; return false; } diff --git a/client/protocols/protocols_defs.h b/client/protocols/protocols_defs.h index 8ab5594f..f75ed39e 100644 --- a/client/protocols/protocols_defs.h +++ b/client/protocols/protocols_defs.h @@ -45,7 +45,9 @@ namespace amnezia constexpr char server_priv_key[] = "server_priv_key"; constexpr char server_pub_key[] = "server_pub_key"; constexpr char psk_key[] = "psk_key"; + constexpr char mtu[] = "mtu"; constexpr char allowed_ips[] = "allowed_ips"; + constexpr char persistent_keep_alive[] = "persistent_keep_alive"; constexpr char client_ip[] = "client_ip"; // internal ip address @@ -103,6 +105,7 @@ namespace amnezia constexpr char defaultSubnetAddress[] = "10.8.0.0"; constexpr char defaultSubnetMask[] = "255.255.255.0"; constexpr char defaultSubnetCidr[] = "24"; + constexpr char defaultMtu[] = "1500"; constexpr char serverConfigPath[] = "/opt/amnezia/openvpn/server.conf"; constexpr char caCertPath[] = "/opt/amnezia/openvpn/pki/ca.crt"; @@ -149,6 +152,7 @@ namespace amnezia constexpr char defaultSubnetCidr[] = "24"; constexpr char defaultPort[] = "51820"; + constexpr char defaultMtu[] = "1420"; constexpr char serverConfigPath[] = "/opt/amnezia/wireguard/wg0.conf"; constexpr char serverPublicKeyPath[] = "/opt/amnezia/wireguard/wireguard_server_public_key.key"; constexpr char serverPskKeyPath[] = "/opt/amnezia/wireguard/wireguard_psk.key"; @@ -164,6 +168,7 @@ namespace amnezia namespace awg { constexpr char defaultPort[] = "55424"; + constexpr char defaultMtu[] = "1420"; constexpr char serverConfigPath[] = "/opt/amnezia/awg/wg0.conf"; constexpr char serverPublicKeyPath[] = "/opt/amnezia/awg/wireguard_server_public_key.key"; diff --git a/client/resources.qrc b/client/resources.qrc index b9a69023..2ff7671a 100644 --- a/client/resources.qrc +++ b/client/resources.qrc @@ -224,6 +224,7 @@ ui/qml/Pages2/PageShareFullAccess.qml images/controls/close.svg images/controls/search.svg + ui/qml/Pages2/PageProtocolWireGuardSettings.qml ui/qml/Components/HomeSplitTunnelingDrawer.qml images/controls/split-tunneling.svg ui/qml/Controls2/DrawerType2.qml diff --git a/client/ui/controllers/importController.cpp b/client/ui/controllers/importController.cpp index 9522cfbe..098d77a7 100644 --- a/client/ui/controllers/importController.cpp +++ b/client/ui/controllers/importController.cpp @@ -286,6 +286,10 @@ QJsonObject ImportController::extractWireGuardConfig(const QString &data) return QJsonObject(); } + if (!configMap.value("MTU").isEmpty()) { + lastConfig[config_key::mtu] = configMap.value("MTU"); + } + QJsonArray allowedIpsJsonArray = QJsonArray::fromStringList(configMap.value("AllowedIPs").split(",")); lastConfig[config_key::allowed_ips] = allowedIpsJsonArray; diff --git a/client/ui/controllers/installController.cpp b/client/ui/controllers/installController.cpp index 25ce155f..e25d9c06 100644 --- a/client/ui/controllers/installController.cpp +++ b/client/ui/controllers/installController.cpp @@ -10,6 +10,8 @@ #include "core/errorstrings.h" #include "core/controllers/serverController.h" #include "utilities.h" +#include "ui/models/protocols/awgConfigModel.h" +#include "ui/models/protocols/wireguardConfigModel.h" namespace { @@ -273,12 +275,16 @@ void InstallController::updateContainer(QJsonObject config) const DockerContainer container = ContainerProps::containerFromString(config.value(config_key::container).toString()); QJsonObject oldContainerConfig = m_containersModel->getContainerConfig(container); + ErrorCode errorCode = ErrorCode::NoError; - ServerController serverController(m_settings); - connect(&serverController, &ServerController::serverIsBusy, this, &InstallController::serverIsBusy); - connect(this, &InstallController::cancelInstallation, &serverController, &ServerController::cancelInstallation); + if (isUpdateDockerContainerRequired(container, oldContainerConfig, config)) { + ServerController serverController(m_settings); + connect(&serverController, &ServerController::serverIsBusy, this, &InstallController::serverIsBusy); + connect(this, &InstallController::cancelInstallation, &serverController, &ServerController::cancelInstallation); + + errorCode = serverController.updateContainer(serverCredentials, container, oldContainerConfig, config); + } - auto errorCode = serverController.updateContainer(serverCredentials, container, oldContainerConfig, config); if (errorCode == ErrorCode::NoError) { m_serversModel->updateContainerConfig(container, config); m_protocolModel->updateModel(config); @@ -514,3 +520,29 @@ void InstallController::addEmptyServer() emit installServerFinished(tr("Server added successfully")); } + +bool InstallController::isUpdateDockerContainerRequired(const DockerContainer container, const QJsonObject &oldConfig, const QJsonObject &newConfig) +{ + Proto mainProto = ContainerProps::defaultProtocol(container); + + const QJsonObject &oldProtoConfig = oldConfig.value(ProtocolProps::protoToString(mainProto)).toObject(); + const QJsonObject &newProtoConfig = newConfig.value(ProtocolProps::protoToString(mainProto)).toObject(); + + if (container == DockerContainer::Awg) { + const AwgConfig oldConfig(oldProtoConfig); + const AwgConfig newConfig(newProtoConfig); + + if (!oldConfig.hasEqualServerSettings(newConfig)) { + return true; + } + } else if (container == DockerContainer::WireGuard) { + const WgConfig oldConfig(oldProtoConfig); + const WgConfig newConfig(newProtoConfig); + + if (!oldConfig.hasEqualServerSettings(newConfig)) { + return true; + } + } + + return false; +} diff --git a/client/ui/controllers/installController.h b/client/ui/controllers/installController.h index 6b5295dc..49090349 100644 --- a/client/ui/controllers/installController.h +++ b/client/ui/controllers/installController.h @@ -76,6 +76,8 @@ private: void installContainer(DockerContainer container, QJsonObject &config); bool isServerAlreadyExists(); + bool isUpdateDockerContainerRequired(const DockerContainer container, const QJsonObject &oldConfig, const QJsonObject &newConfig); + QSharedPointer m_serversModel; QSharedPointer m_containersModel; QSharedPointer m_protocolModel; diff --git a/client/ui/models/protocols/awgConfigModel.cpp b/client/ui/models/protocols/awgConfigModel.cpp index 7d0277b9..ba36c2a9 100644 --- a/client/ui/models/protocols/awgConfigModel.cpp +++ b/client/ui/models/protocols/awgConfigModel.cpp @@ -22,6 +22,7 @@ bool AwgConfigModel::setData(const QModelIndex &index, const QVariant &value, in switch (role) { case Roles::PortRole: m_protocolConfig.insert(config_key::port, value.toString()); break; + case Roles::MtuRole: m_protocolConfig.insert(config_key::mtu, value.toString()); break; case Roles::JunkPacketCountRole: m_protocolConfig.insert(config_key::junkPacketCount, value.toString()); break; case Roles::JunkPacketMinSizeRole: m_protocolConfig.insert(config_key::junkPacketMinSize, value.toString()); break; case Roles::JunkPacketMaxSizeRole: m_protocolConfig.insert(config_key::junkPacketMaxSize, value.toString()); break; @@ -57,6 +58,7 @@ QVariant AwgConfigModel::data(const QModelIndex &index, int role) const switch (role) { case Roles::PortRole: return m_protocolConfig.value(config_key::port).toString(); + case Roles::MtuRole: return m_protocolConfig.value(config_key::mtu).toString(); case Roles::JunkPacketCountRole: return m_protocolConfig.value(config_key::junkPacketCount); case Roles::JunkPacketMinSizeRole: return m_protocolConfig.value(config_key::junkPacketMinSize); case Roles::JunkPacketMaxSizeRole: return m_protocolConfig.value(config_key::junkPacketMaxSize); @@ -80,25 +82,21 @@ void AwgConfigModel::updateModel(const QJsonObject &config) QJsonObject protocolConfig = config.value(config_key::awg).toObject(); - m_protocolConfig[config_key::port] = - protocolConfig.value(config_key::port).toString(protocols::awg::defaultPort); + m_protocolConfig[config_key::last_config] = protocolConfig.value(config_key::last_config); + m_protocolConfig[config_key::port] = protocolConfig.value(config_key::port).toString(protocols::awg::defaultPort); + m_protocolConfig[config_key::mtu] = protocolConfig.value(config_key::mtu).toString(protocols::awg::defaultMtu); m_protocolConfig[config_key::junkPacketCount] = protocolConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount); m_protocolConfig[config_key::junkPacketMinSize] = - protocolConfig.value(config_key::junkPacketMinSize) - .toString(protocols::awg::defaultJunkPacketMinSize); + protocolConfig.value(config_key::junkPacketMinSize).toString(protocols::awg::defaultJunkPacketMinSize); m_protocolConfig[config_key::junkPacketMaxSize] = - protocolConfig.value(config_key::junkPacketMaxSize) - .toString(protocols::awg::defaultJunkPacketMaxSize); + protocolConfig.value(config_key::junkPacketMaxSize).toString(protocols::awg::defaultJunkPacketMaxSize); m_protocolConfig[config_key::initPacketJunkSize] = - protocolConfig.value(config_key::initPacketJunkSize) - .toString(protocols::awg::defaultInitPacketJunkSize); + protocolConfig.value(config_key::initPacketJunkSize).toString(protocols::awg::defaultInitPacketJunkSize); m_protocolConfig[config_key::responsePacketJunkSize] = - protocolConfig.value(config_key::responsePacketJunkSize) - .toString(protocols::awg::defaultResponsePacketJunkSize); + protocolConfig.value(config_key::responsePacketJunkSize).toString(protocols::awg::defaultResponsePacketJunkSize); m_protocolConfig[config_key::initPacketMagicHeader] = - protocolConfig.value(config_key::initPacketMagicHeader) - .toString(protocols::awg::defaultInitPacketMagicHeader); + protocolConfig.value(config_key::initPacketMagicHeader).toString(protocols::awg::defaultInitPacketMagicHeader); m_protocolConfig[config_key::responsePacketMagicHeader] = protocolConfig.value(config_key::responsePacketMagicHeader) .toString(protocols::awg::defaultResponsePacketMagicHeader); @@ -114,6 +112,19 @@ void AwgConfigModel::updateModel(const QJsonObject &config) QJsonObject AwgConfigModel::getConfig() { + const AwgConfig oldConfig(m_fullConfig.value(config_key::awg).toObject()); + const AwgConfig newConfig(m_protocolConfig); + + if (!oldConfig.hasEqualServerSettings(newConfig)) { + m_protocolConfig.remove(config_key::last_config); + } else { + auto lastConfig = m_protocolConfig.value(config_key::last_config).toString(); + QJsonObject jsonConfig = QJsonDocument::fromJson(lastConfig.toUtf8()).object(); + jsonConfig[config_key::mtu] = newConfig.mtu; + + m_protocolConfig[config_key::last_config] = QString(QJsonDocument(jsonConfig).toJson()); + } + m_fullConfig.insert(config_key::awg, m_protocolConfig); return m_fullConfig; } @@ -123,6 +134,7 @@ QHash AwgConfigModel::roleNames() const QHash roles; roles[PortRole] = "port"; + roles[MtuRole] = "mtu"; roles[JunkPacketCountRole] = "junkPacketCount"; roles[JunkPacketMinSizeRole] = "junkPacketMinSize"; roles[JunkPacketMaxSizeRole] = "junkPacketMaxSize"; @@ -135,3 +147,47 @@ QHash AwgConfigModel::roleNames() const return roles; } + +AwgConfig::AwgConfig(const QJsonObject &jsonConfig) +{ + port = jsonConfig.value(config_key::port).toString(protocols::awg::defaultPort); + mtu = jsonConfig.value(config_key::mtu).toString(protocols::awg::defaultMtu); + junkPacketCount = jsonConfig.value(config_key::junkPacketCount).toString(protocols::awg::defaultJunkPacketCount); + junkPacketMinSize = + jsonConfig.value(config_key::junkPacketMinSize).toString(protocols::awg::defaultJunkPacketMinSize); + junkPacketMaxSize = + jsonConfig.value(config_key::junkPacketMaxSize).toString(protocols::awg::defaultJunkPacketMaxSize); + initPacketJunkSize = + jsonConfig.value(config_key::initPacketJunkSize).toString(protocols::awg::defaultInitPacketJunkSize); + responsePacketJunkSize = + jsonConfig.value(config_key::responsePacketJunkSize).toString(protocols::awg::defaultResponsePacketJunkSize); + initPacketMagicHeader = + jsonConfig.value(config_key::initPacketMagicHeader).toString(protocols::awg::defaultInitPacketMagicHeader); + responsePacketMagicHeader = jsonConfig.value(config_key::responsePacketMagicHeader) + .toString(protocols::awg::defaultResponsePacketMagicHeader); + underloadPacketMagicHeader = jsonConfig.value(config_key::underloadPacketMagicHeader) + .toString(protocols::awg::defaultUnderloadPacketMagicHeader); + transportPacketMagicHeader = jsonConfig.value(config_key::transportPacketMagicHeader) + .toString(protocols::awg::defaultTransportPacketMagicHeader); +} + +bool AwgConfig::hasEqualServerSettings(const AwgConfig &other) const +{ + if (port != other.port || junkPacketCount != other.junkPacketCount || junkPacketMinSize != other.junkPacketMinSize + || junkPacketMaxSize != other.junkPacketMaxSize || initPacketJunkSize != other.initPacketJunkSize + || responsePacketJunkSize != other.responsePacketJunkSize || initPacketMagicHeader != other.initPacketMagicHeader + || responsePacketMagicHeader != other.responsePacketMagicHeader + || underloadPacketMagicHeader != other.underloadPacketMagicHeader + || transportPacketMagicHeader != other.transportPacketMagicHeader) { + return false; + } + return true; +} + +bool AwgConfig::hasEqualClientSettings(const AwgConfig &other) const +{ + if (mtu != other.mtu) { + return false; + } + return true; +} diff --git a/client/ui/models/protocols/awgConfigModel.h b/client/ui/models/protocols/awgConfigModel.h index e67a3708..e41d172d 100644 --- a/client/ui/models/protocols/awgConfigModel.h +++ b/client/ui/models/protocols/awgConfigModel.h @@ -6,6 +6,27 @@ #include "containers/containers_defs.h" +struct AwgConfig +{ + AwgConfig(const QJsonObject &jsonConfig); + + QString port; + QString mtu; + QString junkPacketCount; + QString junkPacketMinSize; + QString junkPacketMaxSize; + QString initPacketJunkSize; + QString responsePacketJunkSize; + QString initPacketMagicHeader; + QString responsePacketMagicHeader; + QString underloadPacketMagicHeader; + QString transportPacketMagicHeader; + + bool hasEqualServerSettings(const AwgConfig &other) const; + bool hasEqualClientSettings(const AwgConfig &other) const; + +}; + class AwgConfigModel : public QAbstractListModel { Q_OBJECT @@ -13,6 +34,7 @@ class AwgConfigModel : public QAbstractListModel public: enum Roles { PortRole = Qt::UserRole + 1, + MtuRole, JunkPacketCountRole, JunkPacketMinSizeRole, JunkPacketMaxSizeRole, diff --git a/client/ui/models/protocols/wireguardConfigModel.cpp b/client/ui/models/protocols/wireguardConfigModel.cpp index 15e89865..60f6685f 100644 --- a/client/ui/models/protocols/wireguardConfigModel.cpp +++ b/client/ui/models/protocols/wireguardConfigModel.cpp @@ -1,5 +1,7 @@ #include "wireguardConfigModel.h" +#include + #include "protocols/protocols_defs.h" WireGuardConfigModel::WireGuardConfigModel(QObject *parent) : QAbstractListModel(parent) @@ -19,8 +21,8 @@ bool WireGuardConfigModel::setData(const QModelIndex &index, const QVariant &val } switch (role) { - case Roles::PortRole: m_protocolConfig.insert(config_key::port, value.toString()); break; - case Roles::CipherRole: m_protocolConfig.insert(config_key::cipher, value.toString()); break; + case Roles::PortRole: m_protocolConfig.insert(config_key::port, value.toString()); break; + case Roles::MtuRole: m_protocolConfig.insert(config_key::mtu, value.toString()); break; } emit dataChanged(index, index, QList { role }); @@ -34,9 +36,8 @@ QVariant WireGuardConfigModel::data(const QModelIndex &index, int role) const } switch (role) { - case Roles::PortRole: return m_protocolConfig.value(config_key::port).toString(protocols::shadowsocks::defaultPort); - case Roles::CipherRole: - return m_protocolConfig.value(config_key::cipher).toString(protocols::shadowsocks::defaultCipher); + case Roles::PortRole: return m_protocolConfig.value(config_key::port).toString(); + case Roles::MtuRole: return m_protocolConfig.value(config_key::mtu).toString(); } return QVariant(); @@ -50,11 +51,31 @@ void WireGuardConfigModel::updateModel(const QJsonObject &config) m_fullConfig = config; QJsonObject protocolConfig = config.value(config_key::wireguard).toObject(); + m_protocolConfig[config_key::last_config] = protocolConfig.value(config_key::last_config); + m_protocolConfig[config_key::port] = + protocolConfig.value(config_key::port).toString(protocols::wireguard::defaultPort); + + m_protocolConfig[config_key::mtu] = + protocolConfig.value(config_key::mtu).toString(protocols::wireguard::defaultMtu); + endResetModel(); } QJsonObject WireGuardConfigModel::getConfig() { + const WgConfig oldConfig(m_fullConfig.value(config_key::awg).toObject()); + const WgConfig newConfig(m_protocolConfig); + + if (!oldConfig.hasEqualServerSettings(newConfig)) { + m_protocolConfig.remove(config_key::last_config); + } else { + auto lastConfig = m_protocolConfig.value(config_key::last_config).toString(); + QJsonObject jsonConfig = QJsonDocument::fromJson(lastConfig.toUtf8()).object(); + jsonConfig[config_key::mtu] = newConfig.mtu; + + m_protocolConfig[config_key::last_config] = QString(QJsonDocument(jsonConfig).toJson()); + } + m_fullConfig.insert(config_key::wireguard, m_protocolConfig); return m_fullConfig; } @@ -64,7 +85,29 @@ QHash WireGuardConfigModel::roleNames() const QHash roles; roles[PortRole] = "port"; - roles[CipherRole] = "cipher"; + roles[MtuRole] = "mtu"; return roles; } + +WgConfig::WgConfig(const QJsonObject &jsonConfig) +{ + port = jsonConfig.value(config_key::port).toString(protocols::wireguard::defaultPort); + mtu = jsonConfig.value(config_key::mtu).toString(protocols::wireguard::defaultMtu); +} + +bool WgConfig::hasEqualServerSettings(const WgConfig &other) const +{ + if (port != other.port) { + return false; + } + return true; +} + +bool WgConfig::hasEqualClientSettings(const WgConfig &other) const +{ + if (mtu != other.mtu) { + return false; + } + return true; +} diff --git a/client/ui/models/protocols/wireguardConfigModel.h b/client/ui/models/protocols/wireguardConfigModel.h index 1deeacaf..6cec76dd 100644 --- a/client/ui/models/protocols/wireguardConfigModel.h +++ b/client/ui/models/protocols/wireguardConfigModel.h @@ -6,6 +6,18 @@ #include "containers/containers_defs.h" +struct WgConfig +{ + WgConfig(const QJsonObject &jsonConfig); + + QString port; + QString mtu; + + bool hasEqualServerSettings(const WgConfig &other) const; + bool hasEqualClientSettings(const WgConfig &other) const; + +}; + class WireGuardConfigModel : public QAbstractListModel { Q_OBJECT @@ -13,7 +25,7 @@ class WireGuardConfigModel : public QAbstractListModel public: enum Roles { PortRole = Qt::UserRole + 1, - CipherRole + MtuRole }; explicit WireGuardConfigModel(QObject *parent = nullptr); diff --git a/client/ui/qml/Components/SettingsContainersListView.qml b/client/ui/qml/Components/SettingsContainersListView.qml index 89eb727e..794ab0b3 100644 --- a/client/ui/qml/Components/SettingsContainersListView.qml +++ b/client/ui/qml/Components/SettingsContainersListView.qml @@ -58,10 +58,8 @@ ListView { break } case ContainerEnum.WireGuard: { - ProtocolsModel.updateModel(config) - PageController.goToPage(PageEnum.PageProtocolRaw) - // WireGuardConfigModel.updateModel(config) - // goToPage(PageEnum.PageProtocolWireGuardSettings) + WireGuardConfigModel.updateModel(config) + PageController.goToPage(PageEnum.PageProtocolWireGuardSettings) break } case ContainerEnum.Awg: { @@ -72,8 +70,6 @@ ListView { case ContainerEnum.Ipsec: { ProtocolsModel.updateModel(config) PageController.goToPage(PageEnum.PageProtocolRaw) - // Ikev2ConfigModel.updateModel(config) - // goToPage(PageEnum.PageProtocolIKev2Settings) break } case ContainerEnum.Sftp: { diff --git a/client/ui/qml/Pages2/PageProtocolAwgSettings.qml b/client/ui/qml/Pages2/PageProtocolAwgSettings.qml index 16946fdf..68b838a9 100644 --- a/client/ui/qml/Pages2/PageProtocolAwgSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolAwgSettings.qml @@ -106,6 +106,26 @@ PageType { KeyNavigation.tab: junkPacketCountTextField.textField } + TextFieldWithHeaderType { + id: mtuTextField + Layout.fillWidth: true + Layout.topMargin: 16 + + headerText: qsTr("MTU") + textFieldText: mtu + textField.validator: IntValidator { bottom: 576; top: 65535 } + + textField.onEditingFinished: { + if (textFieldText === "") { + textFieldText = "0" + } + if (textFieldText !== mtu) { + mtu = textFieldText + } + } + checkEmptyText: true + } + TextFieldWithHeaderType { id: junkPacketCountTextField Layout.fillWidth: true @@ -337,7 +357,7 @@ PageType { junkPacketCountTextField.errorText === "" && portTextField.errorText === "" - text: qsTr("Save and Restart Amnezia") + text: qsTr("Save") clickedFunc: function() { forceActiveFocus() diff --git a/client/ui/qml/Pages2/PageProtocolCloakSettings.qml b/client/ui/qml/Pages2/PageProtocolCloakSettings.qml index b86397ba..729f248f 100644 --- a/client/ui/qml/Pages2/PageProtocolCloakSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolCloakSettings.qml @@ -174,7 +174,7 @@ PageType { Layout.topMargin: 24 Layout.bottomMargin: 24 - text: qsTr("Save and Restart Amnezia") + text: qsTr("Save") clickedFunc: function() { forceActiveFocus() diff --git a/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml b/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml index 7fb4634c..2346ee9a 100644 --- a/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml @@ -404,7 +404,7 @@ PageType { Layout.topMargin: 24 Layout.bottomMargin: 24 - text: qsTr("Save and Restart Amnezia") + text: qsTr("Save") clickedFunc: function() { forceActiveFocus() diff --git a/client/ui/qml/Pages2/PageProtocolShadowSocksSettings.qml b/client/ui/qml/Pages2/PageProtocolShadowSocksSettings.qml index 5284f807..f9447c78 100644 --- a/client/ui/qml/Pages2/PageProtocolShadowSocksSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolShadowSocksSettings.qml @@ -148,7 +148,7 @@ PageType { Layout.topMargin: 24 Layout.bottomMargin: 24 - text: qsTr("Save and Restart Amnezia") + text: qsTr("Save") clickedFunc: function() { forceActiveFocus() diff --git a/client/ui/qml/Pages2/PageProtocolWireGuardSettings.qml b/client/ui/qml/Pages2/PageProtocolWireGuardSettings.qml new file mode 100644 index 00000000..f9f4a4dc --- /dev/null +++ b/client/ui/qml/Pages2/PageProtocolWireGuardSettings.qml @@ -0,0 +1,172 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import SortFilterProxyModel 0.2 + +import PageEnum 1.0 + +import "./" +import "../Controls2" +import "../Controls2/TextTypes" +import "../Config" +import "../Components" + +PageType { + id: root + + ColumnLayout { + id: backButton + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + anchors.topMargin: 20 + + BackButtonType { + } + } + + FlickableType { + id: fl + anchors.top: backButton.bottom + anchors.bottom: parent.bottom + contentHeight: content.implicitHeight + + Column { + id: content + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + enabled: ServersModel.isCurrentlyProcessedServerHasWriteAccess() + + ListView { + id: listview + + width: parent.width + height: listview.contentItem.height + + clip: true + interactive: false + + model: WireGuardConfigModel + + delegate: Item { + implicitWidth: listview.width + implicitHeight: col.implicitHeight + + ColumnLayout { + id: col + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + anchors.leftMargin: 16 + anchors.rightMargin: 16 + + spacing: 0 + + HeaderType { + Layout.fillWidth: true + headerText: qsTr("WG settings") + } + + TextFieldWithHeaderType { + id: portTextField + Layout.fillWidth: true + Layout.topMargin: 40 + + headerText: qsTr("Port") + textFieldText: port + textField.maximumLength: 5 + textField.validator: IntValidator { bottom: 1; top: 65535 } + + textField.onEditingFinished: { + if (textFieldText !== port) { + port = textFieldText + } + } + + checkEmptyText: true + } + + TextFieldWithHeaderType { + id: mtuTextField + Layout.fillWidth: true + Layout.topMargin: 16 + + headerText: qsTr("MTU") + textFieldText: mtu + textField.validator: IntValidator { bottom: 576; top: 65535 } + + textField.onEditingFinished: { + if (textFieldText === "") { + textFieldText = "0" + } + if (textFieldText !== mtu) { + mtu = textFieldText + } + } + checkEmptyText: true + } + + BasicButtonType { + Layout.topMargin: 24 + Layout.leftMargin: -8 + implicitHeight: 32 + + defaultColor: "transparent" + hoveredColor: Qt.rgba(1, 1, 1, 0.08) + pressedColor: Qt.rgba(1, 1, 1, 0.12) + textColor: "#EB5757" + + text: qsTr("Remove WG") + + onClicked: { + questionDrawer.headerText = qsTr("Remove WG from server?") + questionDrawer.descriptionText = qsTr("All users with whom you shared a connection will no longer be able to connect to it.") + questionDrawer.yesButtonText = qsTr("Continue") + questionDrawer.noButtonText = qsTr("Cancel") + + questionDrawer.yesButtonFunction = function() { + questionDrawer.visible = false + PageController.goToPage(PageEnum.PageDeinstalling) + InstallController.removeCurrentlyProcessedContainer() + } + questionDrawer.noButtonFunction = function() { + questionDrawer.visible = false + } + questionDrawer.visible = true + } + } + + BasicButtonType { + Layout.fillWidth: true + Layout.topMargin: 24 + Layout.bottomMargin: 24 + + enabled: mtuTextField.errorText === "" && + portTextField.errorText === "" + + text: qsTr("Save") + + onClicked: { + forceActiveFocus() + PageController.goToPage(PageEnum.PageSetupWizardInstalling); + InstallController.updateContainer(WireGuardConfigModel.getConfig()) + } + } + } + } + } + } + + QuestionDrawer { + id: questionDrawer + } + } +}