From cf48ca5e07f6a1fd461f50e48dde02cb591e2cc0 Mon Sep 17 00:00:00 2001 From: cd-amn Date: Tue, 26 May 2026 17:08:43 +0000 Subject: [PATCH] feat: per-tunnel Windows firewall for seamless WG switch --- client/core/controllers/gatewayController.cpp | 2 +- client/core/tunnel.h | 5 + client/core/vpnTrafficGuard.cpp | 84 +++++-- client/core/vpnTrafficGuard.h | 6 +- .../windows/daemon/windowsfirewall.cpp | 233 ++++++++++-------- .../windows/daemon/windowsfirewall.h | 38 +-- client/vpnConnection.cpp | 52 ++-- client/vpnConnection.h | 3 +- ipc/ipc_interface.rep | 3 +- ipc/ipcserver.cpp | 15 +- ipc/ipcserver.h | 3 +- service/server/killswitch.cpp | 29 ++- service/server/killswitch.h | 3 +- 13 files changed, 293 insertions(+), 183 deletions(-) diff --git a/client/core/controllers/gatewayController.cpp b/client/core/controllers/gatewayController.cpp index d400de3a2..d1868ee4a 100644 --- a/client/core/controllers/gatewayController.cpp +++ b/client/core/controllers/gatewayController.cpp @@ -79,7 +79,7 @@ GatewayController::EncryptedRequestData GatewayController::prepareRequest(const QString ip = NetworkUtilities::getIPAddress(host); if (!ip.isEmpty()) { IpcClient::withInterface([&](QSharedPointer iface) { - QRemoteObjectPendingReply reply = iface->addKillSwitchAllowedRange(QStringList { ip }); + QRemoteObjectPendingReply reply = iface->addKillSwitchAllowedRange(QString(), QStringList { ip }); if (!reply.waitForFinished(1000) || !reply.returnValue()) qWarning() << "GatewayController::prepareRequest(): Failed to execute remote addKillSwitchAllowedRange call"; }); diff --git a/client/core/tunnel.h b/client/core/tunnel.h index 2eb0222be..6e1e523c3 100644 --- a/client/core/tunnel.h +++ b/client/core/tunnel.h @@ -41,6 +41,10 @@ public: State state() const { return m_state; } QSharedPointer protocol() const { return m_protocol; } + const QString& handoverIfname() const { return m_handoverIfname; } + void setHandoverIfname(const QString& ifname) { m_handoverIfname = ifname; } + void clearHandoverIfname() { m_handoverIfname.clear(); } + virtual void prepare(); virtual void commit(); virtual void deactivate(); @@ -61,6 +65,7 @@ protected: QString m_ifname; QString m_remoteAddress; + QString m_handoverIfname; amnezia::DockerContainer m_container; QJsonObject m_config; QSharedPointer m_protocol; diff --git a/client/core/vpnTrafficGuard.cpp b/client/core/vpnTrafficGuard.cpp index 1808e50fe..801853a19 100644 --- a/client/core/vpnTrafficGuard.cpp +++ b/client/core/vpnTrafficGuard.cpp @@ -31,37 +31,27 @@ void VpnTrafficGuard::setConfig(const QJsonObject &config) m_config = config; } -bool VpnTrafficGuard::allowEndpoint(const QString &remoteAddress) +bool VpnTrafficGuard::allowEndpoint(const QString &remoteAddress, const QString &ifname) { #ifdef AMNEZIA_DESKTOP if (remoteAddress.isEmpty()) { return false; } - if (!m_allowedEndpoints.contains(remoteAddress)) { - m_allowedEndpoints.append(remoteAddress); + if (m_allowedEndpoints.contains(remoteAddress)) { + return true; } + m_allowedEndpoints.append(remoteAddress); return IpcClient::withInterface([&](QSharedPointer iface) { - QRemoteObjectPendingReply reply = iface->addKillSwitchAllowedRange(QStringList(remoteAddress)); + QRemoteObjectPendingReply reply = iface->addKillSwitchAllowedRange(ifname, QStringList(remoteAddress)); return reply.waitForFinished(1000) && reply.returnValue(); }); #else Q_UNUSED(remoteAddress) + Q_UNUSED(ifname) return true; #endif } -void VpnTrafficGuard::revokeEndpoint(const QString &remoteAddress) -{ -#ifdef AMNEZIA_DESKTOP - m_allowedEndpoints.removeAll(remoteAddress); - IpcClient::withInterface([this](QSharedPointer iface) { - iface->resetKillSwitchAllowedRange(m_allowedEndpoints); - }); -#else - Q_UNUSED(remoteAddress) -#endif -} - void VpnTrafficGuard::setupRoutes(const QJsonObject &vpnConfiguration, const QSharedPointer &protocol, const QString &remoteAddress) { #ifdef AMNEZIA_DESKTOP @@ -175,9 +165,29 @@ void VpnTrafficGuard::addSplitTunnelRoutes(const QString &gw, amnezia::RouteMode #endif } -void VpnTrafficGuard::applyFirewall(const QString &gateway, const QString &localAddress) +void VpnTrafficGuard::finishFirewallHandover(Tunnel* tunnel) +{ +#if defined(AMNEZIA_DESKTOP) && defined(Q_OS_WIN) + if (!tunnel) return; + const QString handoverIfname = tunnel->handoverIfname(); + if (handoverIfname.isEmpty() || handoverIfname == tunnel->ifname()) { + tunnel->clearHandoverIfname(); + return; + } + IpcClient::withInterface([&](QSharedPointer iface) { + iface->disableKillSwitchForTunnel(handoverIfname, QStringList()); + }); + tunnel->clearHandoverIfname(); +#else + Q_UNUSED(tunnel) +#endif +} + +void VpnTrafficGuard::applyKillSwitch(Tunnel* tunnel, const QString &gateway, const QString &localAddress) { #ifdef AMNEZIA_DESKTOP + finishFirewallHandover(tunnel); + QJsonObject updatedConfig = m_config; IpcClient::withInterface([&](QSharedPointer iface) { #ifdef Q_OS_WIN @@ -213,10 +223,10 @@ void VpnTrafficGuard::applyFirewall(const QString &gateway, const QString &local NetworkUtilities::getIPAddress(updatedConfig.value(amnezia::configKey::hostName).toString())); QRemoteObjectPendingReply reply = iface->enableKillSwitch(updatedConfig, 0); //TODO: why it takes so long? - if (!reply.waitForFinished(5000) || !reply.returnValue()) { - qWarning() << "VpnTrafficGuard::applyFirewall: Failed to enable killswitch"; + if (!reply.waitForFinished(1000) || !reply.returnValue()) { + qWarning() << "VpnTrafficGuard::applyKillSwitch: Failed to enable killswitch"; } else { - qDebug() << "VpnTrafficGuard::applyFirewall: Successfully enabled killswitch"; + qDebug() << "VpnTrafficGuard::applyKillSwitch: Successfully enabled killswitch"; } } #endif @@ -250,7 +260,7 @@ void VpnTrafficGuard::flushAll() QRemoteObjectPendingReply reply = iface->disableKillSwitch(); m_allowedEndpoints.clear(); //TODO: why it takes so long? - if (!reply.waitForFinished(5000) || !reply.returnValue()) { + if (!reply.waitForFinished(1000) || !reply.returnValue()) { qWarning() << "VpnTrafficGuard::flushAll: Failed to disable killswitch"; } else { qDebug() << "VpnTrafficGuard::flushAll: Successfully disabled killswitch"; @@ -318,7 +328,7 @@ void VpnTrafficGuard::reserve(Tunnel* tunnel) { if (!tunnel) return; #ifdef AMNEZIA_DESKTOP - allowEndpoint(tunnel->remoteAddress()); + allowEndpoint(tunnel->remoteAddress(), tunnel->ifname()); #else Q_UNUSED(tunnel) #endif @@ -327,8 +337,12 @@ void VpnTrafficGuard::reserve(Tunnel* tunnel) void VpnTrafficGuard::release(Tunnel* tunnel) { if (!tunnel) return; + disconnect(tunnel, nullptr, this, nullptr); #ifdef AMNEZIA_DESKTOP - revokeEndpoint(tunnel->remoteAddress()); + m_allowedEndpoints.removeAll(tunnel->remoteAddress()); + IpcClient::withInterface([this, &tunnel](QSharedPointer iface) { + iface->disableKillSwitchForTunnel(tunnel->ifname(), m_allowedEndpoints); + }); #else Q_UNUSED(tunnel) #endif @@ -396,6 +410,17 @@ void VpnTrafficGuard::commit(Tunnel* tunnel) { if (!tunnel) return; applyPolicy(tunnel); + connect(tunnel, &Tunnel::activated, this, [this, tunnel] { + if (auto p = tunnel->protocol()) { + applyKillSwitch(tunnel, p->vpnGateway(), p->vpnLocalAddress()); + } + }); +#ifdef Q_OS_WIN + connect(tunnel, &Tunnel::addressesUpdated, this, + [this, tunnel](const QString& gw, const QString& la) { + applyKillSwitch(tunnel, gw, la); + }); +#endif tunnel->commit(); } @@ -410,11 +435,18 @@ void VpnTrafficGuard::tearDown(Tunnel* tunnel) void VpnTrafficGuard::swap(Tunnel* from, Tunnel* to) { if (!to) return; - applyPolicy(to); - to->commit(); if (from) { + to->setHandoverIfname(from->ifname()); + } + commit(to); + if (from) { + m_allowedEndpoints.removeAll(from->remoteAddress()); +#ifndef Q_OS_WIN + IpcClient::withInterface([this](QSharedPointer iface) { + iface->resetKillSwitchAllowedRange(m_allowedEndpoints); + }); +#endif revokePolicy(from); - release(from); from->deactivate(); } } diff --git a/client/core/vpnTrafficGuard.h b/client/core/vpnTrafficGuard.h index 141bbd111..f55a9c19e 100644 --- a/client/core/vpnTrafficGuard.h +++ b/client/core/vpnTrafficGuard.h @@ -20,9 +20,8 @@ public: const QString &remoteAddress); void flushAll(); - bool allowEndpoint(const QString &remoteAddress); - void revokeEndpoint(const QString &remoteAddress); - void applyFirewall(const QString &vpnGateway, const QString &vpnLocalAddress); + bool allowEndpoint(const QString &remoteAddress, const QString &ifname = QString()); + void applyKillSwitch(Tunnel* tunnel, const QString &vpnGateway, const QString &vpnLocalAddress); void reserve(Tunnel* tunnel); void release(Tunnel* tunnel); @@ -36,6 +35,7 @@ public: private: void addSplitTunnelRoutes(const QString &gateway, amnezia::RouteMode mode); + void finishFirewallHandover(Tunnel* tunnel); SecureAppSettingsRepository* m_appSettingsRepository; QJsonObject m_config; bool m_ipv6RoutingStopped = false; diff --git a/client/platforms/windows/daemon/windowsfirewall.cpp b/client/platforms/windows/daemon/windowsfirewall.cpp index 2556c417b..0c607b340 100644 --- a/client/platforms/windows/daemon/windowsfirewall.cpp +++ b/client/platforms/windows/daemon/windowsfirewall.cpp @@ -159,7 +159,7 @@ bool WindowsFirewall::initSublayer() { return true; } -bool WindowsFirewall::enableInterface(int vpnAdapterIndex) { +bool WindowsFirewall::enableInterface(int vpnAdapterIndex, const QString& ifname) { // Checks if the FW_Rule was enabled succesfully, // disables the whole killswitch and returns false if not. #define FW_OK(rule) \ @@ -182,31 +182,39 @@ bool WindowsFirewall::enableInterface(int vpnAdapterIndex) { } \ } - logger.info() << "Enabling Killswitch Using Adapter:" << vpnAdapterIndex; - if (vpnAdapterIndex < 0) - { + logger.info() << "Enabling Killswitch Using Adapter:" << vpnAdapterIndex + << "ifname:" << ifname; + + QList& perTunnel = ifname.isEmpty() ? m_globalRules + : m_tunnelRules[ifname]; + if (vpnAdapterIndex < 0) { IPAddress allv4("0.0.0.0/0"); - if (!blockTrafficTo(allv4, MED_WEIGHT, - "Block Internet", "killswitch")) { - return false; - } - IPAddress allv6("::/0"); - if (!blockTrafficTo(allv6, MED_WEIGHT, - "Block Internet", "killswitch")) { + if (!blockTrafficTo(allv4, MED_WEIGHT, "Block Internet", perTunnel)) { return false; } - } else - FW_OK(allowTrafficOfAdapter(vpnAdapterIndex, MED_WEIGHT, - "Allow usage of VPN Adapter")); - FW_OK(allowDHCPTraffic(MED_WEIGHT, "Allow DHCP Traffic")); - FW_OK(allowHyperVTraffic(MAX_WEIGHT, "Allow Hyper-V Traffic")); - FW_OK(allowTrafficForAppOnAll(getCurrentPath(), MAX_WEIGHT, - "Allow all for AmneziaVPN.exe")); - FW_OK(blockTrafficOnPort(53, MED_WEIGHT, "Block all DNS")); - FW_OK(allowLoopbackTraffic(MED_WEIGHT, - "Allow Loopback traffic on device %1")); + IPAddress allv6("::/0"); + if (!blockTrafficTo(allv6, MED_WEIGHT, "Block Internet", perTunnel)) { + return false; + } + } else { + FW_OK(allowTrafficOfAdapter(vpnAdapterIndex, MED_WEIGHT, + "Allow usage of VPN Adapter", perTunnel)); + } - logger.debug() << "Killswitch on! Rules:" << m_activeRules.length(); + if (m_globalRules.isEmpty()) { + FW_OK(allowDHCPTraffic(MED_WEIGHT, "Allow DHCP Traffic", m_globalRules)); + FW_OK(allowHyperVTraffic(MAX_WEIGHT, "Allow Hyper-V Traffic", m_globalRules)); + FW_OK(allowTrafficForAppOnAll(getCurrentPath(), MAX_WEIGHT, + "Allow all for AmneziaVPN.exe", m_globalRules)); + FW_OK(blockTrafficOnPort(53, MED_WEIGHT, "Block all DNS", m_globalRules)); + FW_OK(allowLoopbackTraffic(MED_WEIGHT, + "Allow Loopback traffic on device %1", + m_globalRules)); + } + + logger.debug() << "Killswitch on! Globals:" << m_globalRules.length() + << "Tunnel[" << ifname + << "]:" << m_tunnelRules.value(ifname).length(); return true; #undef FW_OK } @@ -226,7 +234,8 @@ bool WindowsFirewall::enableLanBypass(const QList& ranges) { // Blocking unprotected traffic for (const IPAddress& prefix : ranges) { - if (!allowTrafficTo(prefix, LOW_WEIGHT + 1, "Allow LAN bypass traffic")) { + if (!allowTrafficTo(prefix, LOW_WEIGHT + 1, "Allow LAN bypass traffic", + m_globalRules)) { return false; } } @@ -242,7 +251,10 @@ bool WindowsFirewall::enableLanBypass(const QList& ranges) { } // Allow unprotected traffic sent to the following address ranges. -bool WindowsFirewall::allowTrafficRange(const QStringList& ranges) { +bool WindowsFirewall::allowTrafficRange(const QStringList& ranges, const QString& ifname) { + QList& target = ifname.isEmpty() ? m_globalRules + : m_tunnelRules[ifname]; + // Start the firewall transaction auto result = FwpmTransactionBegin(m_sessionHandle, NULL); if (result != ERROR_SUCCESS) { @@ -255,8 +267,9 @@ bool WindowsFirewall::allowTrafficRange(const QStringList& ranges) { }); for (const QString& addr : ranges) { - logger.debug() << "Allow killswitch exclude: " << addr; - if (!allowTrafficTo(QHostAddress(addr), HIGH_WEIGHT, "Allow killswitch bypass traffic")) { + logger.debug() << "Allow killswitch exclude: " << addr << "ifname:" << ifname; + if (!allowTrafficTo(QHostAddress(addr), HIGH_WEIGHT, + "Allow killswitch bypass traffic", target)) { return false; } } @@ -273,6 +286,10 @@ bool WindowsFirewall::allowTrafficRange(const QStringList& ranges) { bool WindowsFirewall::enablePeerTraffic(const InterfaceConfig& config) { + QList& target = config.m_ifname.isEmpty() + ? m_globalRules + : m_tunnelRules[config.m_ifname]; + // Start the firewall transaction auto result = FwpmTransactionBegin(m_sessionHandle, NULL); if (result != ERROR_SUCCESS) { @@ -288,12 +305,12 @@ bool WindowsFirewall::enablePeerTraffic(const InterfaceConfig& config) { logger.info() << "Enabling traffic for peer" << config.m_serverPublicKey; if (!blockTrafficTo(config.m_allowedIPAddressRanges, LOW_WEIGHT, - "Block Internet", config.m_serverPublicKey)) { + "Block Internet", target)) { return false; } if (!config.m_primaryDnsServer.isEmpty()) { if (!allowTrafficTo(QHostAddress(config.m_primaryDnsServer), 53, HIGH_WEIGHT, - "Allow DNS-Server", config.m_serverPublicKey)) { + "Allow DNS-Server", target)) { return false; } // In some cases, we might configure a 2nd DNS server for IPv6, however @@ -302,7 +319,7 @@ bool WindowsFirewall::enablePeerTraffic(const InterfaceConfig& config) { if (config.m_primaryDnsServer == config.m_serverIpv4Gateway) { if (!allowTrafficTo(QHostAddress(config.m_serverIpv6Gateway), 53, HIGH_WEIGHT, "Allow extra IPv6 DNS-Server", - config.m_serverPublicKey)) { + target)) { return false; } } @@ -310,7 +327,7 @@ bool WindowsFirewall::enablePeerTraffic(const InterfaceConfig& config) { if (!config.m_secondaryDnsServer.isEmpty()) { if (!allowTrafficTo(QHostAddress(config.m_secondaryDnsServer), 53, HIGH_WEIGHT, - "Allow DNS-Server", config.m_serverPublicKey)) { + "Allow DNS-Server", target)) { return false; } // In some cases, we might configure a 2nd DNS server for IPv6, however @@ -319,7 +336,7 @@ bool WindowsFirewall::enablePeerTraffic(const InterfaceConfig& config) { if (config.m_secondaryDnsServer == config.m_serverIpv4Gateway) { if (!allowTrafficTo(QHostAddress(config.m_serverIpv6Gateway), 53, HIGH_WEIGHT, "Allow extra IPv6 DNS-Server", - config.m_serverPublicKey)) { + target)) { return false; } } @@ -328,7 +345,7 @@ bool WindowsFirewall::enablePeerTraffic(const InterfaceConfig& config) { for (const QString& dns : config.m_allowedDnsServers) { logger.debug() << "Allow DNS: " << dns; if (!allowTrafficTo(QHostAddress(dns), 53, HIGH_WEIGHT, - "Allow DNS-Server", config.m_serverPublicKey)) { + "Allow DNS-Server", target)) { return false; } } @@ -338,7 +355,7 @@ bool WindowsFirewall::enablePeerTraffic(const InterfaceConfig& config) { logger.debug() << "excludedAddresses range: " << i; if (!allowTrafficTo(i, HIGH_WEIGHT, - "Allow Ecxlude route", config.m_serverPublicKey)) { + "Allow Ecxlude route", target)) { return false; } } @@ -354,35 +371,6 @@ bool WindowsFirewall::enablePeerTraffic(const InterfaceConfig& config) { return true; } -bool WindowsFirewall::disablePeerTraffic(const QString& pubkey) { - auto result = FwpmTransactionBegin(m_sessionHandle, NULL); - auto cleanup = qScopeGuard([&] { - if (result != ERROR_SUCCESS) { - FwpmTransactionAbort0(m_sessionHandle); - } - }); - if (result != ERROR_SUCCESS) { - logger.error() << "FwpmTransactionBegin0 failed. Return value:.\n" - << result; - return false; - } - - logger.info() << "Disabling traffic for peer" << pubkey; - for (const auto& filterID : m_peerRules.values(pubkey)) { - FwpmFilterDeleteById0(m_sessionHandle, filterID); - m_peerRules.remove(pubkey, filterID); - } - - // Commit! - result = FwpmTransactionCommit0(m_sessionHandle); - if (result != ERROR_SUCCESS) { - logger.error() << "FwpmTransactionCommit0 failed. Return value:.\n" - << result; - return false; - } - return true; -} - bool WindowsFirewall::disableKillSwitch() { return KillSwitch::instance()->disableKillSwitch(); } @@ -400,11 +388,13 @@ bool WindowsFirewall::allowAllTraffic() { return false; } - for (const auto& filterID : m_peerRules.values()) { - FwpmFilterDeleteById0(m_sessionHandle, filterID); + for (const auto& bucket : qAsConst(m_tunnelRules)) { + for (const auto& filterID : bucket) { + FwpmFilterDeleteById0(m_sessionHandle, filterID); + } } - for (const auto& filterID : qAsConst(m_activeRules)) { + for (const auto& filterID : qAsConst(m_globalRules)) { FwpmFilterDeleteById0(m_sessionHandle, filterID); } @@ -415,15 +405,42 @@ bool WindowsFirewall::allowAllTraffic() { << result; return false; } - m_peerRules.clear(); - m_activeRules.clear(); + m_tunnelRules.clear(); + m_globalRules.clear(); logger.debug() << "Firewall Disabled!"; return true; } +bool WindowsFirewall::disableKillSwitchForTunnel(const QString& ifname) { + if (ifname.isEmpty() || !m_tunnelRules.contains(ifname)) { + return true; + } + + auto result = FwpmTransactionBegin(m_sessionHandle, NULL); + if (result != ERROR_SUCCESS) { + logger.error() << "FwpmTransactionBegin0 failed. Return value:" << result; + return false; + } + + const QList filters = m_tunnelRules.take(ifname); + logger.info() << "Disabling killswitch filters for tunnel" << ifname + << "count:" << filters.length(); + for (const auto& filterID : filters) { + FwpmFilterDeleteById0(m_sessionHandle, filterID); + } + + result = FwpmTransactionCommit0(m_sessionHandle); + if (result != ERROR_SUCCESS) { + logger.error() << "FwpmTransactionCommit0 failed. Return value:" << result; + return false; + } + return true; +} + bool WindowsFirewall::allowTrafficForAppOnAll(const QString& exePath, int weight, - const QString& title) { + const QString& title, + QList& target) { DWORD result = ERROR_SUCCESS; Q_ASSERT(weight <= 15); @@ -460,7 +477,7 @@ bool WindowsFirewall::allowTrafficForAppOnAll(const QString& exePath, { QString desc("Permit (out) IPv4 Traffic of: " + appName); filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4; - if (!enableFilter(&filter, title, desc)) { + if (!enableFilter(&filter, title, desc, target)) { return false; } } @@ -468,7 +485,7 @@ bool WindowsFirewall::allowTrafficForAppOnAll(const QString& exePath, { QString desc("Permit (in) IPv4 Traffic of: " + appName); filter.layerKey = FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4; - if (!enableFilter(&filter, title, desc)) { + if (!enableFilter(&filter, title, desc, target)) { return false; } } @@ -476,7 +493,8 @@ bool WindowsFirewall::allowTrafficForAppOnAll(const QString& exePath, } bool WindowsFirewall::allowTrafficOfAdapter(int networkAdapter, uint8_t weight, - const QString& title) { + const QString& title, + QList& target) { FWPM_FILTER_CONDITION0 conds; // Condition: Request must be targeting the TUN interface conds.fieldKey = FWPM_CONDITION_INTERFACE_INDEX; @@ -498,25 +516,25 @@ bool WindowsFirewall::allowTrafficOfAdapter(int networkAdapter, uint8_t weight, // #1 Permit outbound IPv4 traffic. filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4; if (!enableFilter(&filter, title, - description.arg("out").arg(networkAdapter))) { + description.arg("out").arg(networkAdapter), target)) { return false; } // #2 Permit inbound IPv4 traffic. filter.layerKey = FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4; if (!enableFilter(&filter, title, - description.arg("in").arg(networkAdapter))) { + description.arg("in").arg(networkAdapter), target)) { return false; } // #3 Permit outbound IPv6 traffic. filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V6; if (!enableFilter(&filter, title, - description.arg("out").arg(networkAdapter))) { + description.arg("out").arg(networkAdapter), target)) { return false; } // #4 Permit inbound IPv6 traffic. filter.layerKey = FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6; if (!enableFilter(&filter, title, - description.arg("in").arg(networkAdapter))) { + description.arg("in").arg(networkAdapter), target)) { return false; } return true; @@ -524,7 +542,7 @@ bool WindowsFirewall::allowTrafficOfAdapter(int networkAdapter, uint8_t weight, bool WindowsFirewall::allowTrafficTo(const IPAddress& addr, int weight, const QString& title, - const QString& peer) { + QList& target) { GUID layerKeyOut; GUID layerKeyIn; if (addr.type() == QAbstractSocket::IPv4Protocol) { @@ -562,11 +580,11 @@ bool WindowsFirewall::allowTrafficTo(const IPAddress& addr, int weight, // Send the filters down to the firewall. QString description = "Permit traffic %1 " + addr.toString(); filter.layerKey = layerKeyOut; - if (!enableFilter(&filter, title, description.arg("to"), peer)) { + if (!enableFilter(&filter, title, description.arg("to"), target)) { return false; } filter.layerKey = layerKeyIn; - if (!enableFilter(&filter, title, description.arg("from"), peer)) { + if (!enableFilter(&filter, title, description.arg("from"), target)) { return false; } return true; @@ -574,7 +592,7 @@ bool WindowsFirewall::allowTrafficTo(const IPAddress& addr, int weight, bool WindowsFirewall::allowTrafficTo(const QHostAddress& targetIP, uint port, int weight, const QString& title, - const QString& peer) { + QList& target) { bool isIPv4 = targetIP.protocol() == QAbstractSocket::IPv4Protocol; GUID layerOut = isIPv4 ? FWPM_LAYER_ALE_AUTH_CONNECT_V4 : FWPM_LAYER_ALE_AUTH_CONNECT_V6; @@ -623,19 +641,20 @@ bool WindowsFirewall::allowTrafficTo(const QHostAddress& targetIP, uint port, filter.layerKey = layerOut; if (!enableFilter(&filter, title, description.arg("to").arg(targetIP.toString()).arg(port), - peer)) { + target)) { return false; } filter.layerKey = layerIn; if (!enableFilter(&filter, title, description.arg("from").arg(targetIP.toString()).arg(port), - peer)) { + target)) { return false; } return true; } -bool WindowsFirewall::allowDHCPTraffic(uint8_t weight, const QString& title) { +bool WindowsFirewall::allowDHCPTraffic(uint8_t weight, const QString& title, + QList& target) { // Allow outbound DHCPv4 { FWPM_FILTER_CONDITION0 conds[4]; @@ -672,7 +691,7 @@ bool WindowsFirewall::allowDHCPTraffic(uint8_t weight, const QString& title) { filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4; - if (!enableFilter(&filter, title, "Allow Outbound DHCP")) { + if (!enableFilter(&filter, title, "Allow Outbound DHCP", target)) { return false; } } @@ -705,7 +724,7 @@ bool WindowsFirewall::allowDHCPTraffic(uint8_t weight, const QString& title) { filter.subLayerKey = ST_FW_WINFW_BASELINE_SUBLAYER_KEY; filter.layerKey = FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4; - if (!enableFilter(&filter, title, "Allow inbound DHCP")) { + if (!enableFilter(&filter, title, "Allow inbound DHCP", target)) { return false; } } @@ -740,7 +759,7 @@ bool WindowsFirewall::allowDHCPTraffic(uint8_t weight, const QString& title) { filter.subLayerKey = ST_FW_WINFW_BASELINE_SUBLAYER_KEY; filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V6; - if (!enableFilter(&filter, title, "Allow outbound DHCPv6")) { + if (!enableFilter(&filter, title, "Allow outbound DHCPv6", target)) { return false; } } @@ -773,7 +792,7 @@ bool WindowsFirewall::allowDHCPTraffic(uint8_t weight, const QString& title) { filter.weight.uint8 = weight; filter.subLayerKey = ST_FW_WINFW_BASELINE_SUBLAYER_KEY; filter.layerKey = FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6; - if (!enableFilter(&filter, title, "Allow inbound DHCPv6")) { + if (!enableFilter(&filter, title, "Allow inbound DHCPv6", target)) { return false; } } @@ -781,7 +800,8 @@ bool WindowsFirewall::allowDHCPTraffic(uint8_t weight, const QString& title) { } // Allows the internal Hyper-V Switches to work. -bool WindowsFirewall::allowHyperVTraffic(uint8_t weight, const QString& title) { +bool WindowsFirewall::allowHyperVTraffic(uint8_t weight, const QString& title, + QList& target) { FWPM_FILTER_CONDITION0 cond; // Condition: Request must be targeting the TUN interface cond.fieldKey = FWPM_CONDITION_L2_FLAGS; @@ -801,12 +821,12 @@ bool WindowsFirewall::allowHyperVTraffic(uint8_t weight, const QString& title) { // #1 Permit Hyper-V => Hyper-V outbound. filter.layerKey = FWPM_LAYER_OUTBOUND_MAC_FRAME_NATIVE; - if (!enableFilter(&filter, title, "Permit Hyper-V => Hyper-V outbound")) { + if (!enableFilter(&filter, title, "Permit Hyper-V => Hyper-V outbound", target)) { return false; } // #2 Permit Hyper-V => Hyper-V inbound. filter.layerKey = FWPM_LAYER_INBOUND_MAC_FRAME_NATIVE; - if (!enableFilter(&filter, title, "Permit Hyper-V => Hyper-V inbound")) { + if (!enableFilter(&filter, title, "Permit Hyper-V => Hyper-V inbound", target)) { return false; } return true; @@ -814,7 +834,7 @@ bool WindowsFirewall::allowHyperVTraffic(uint8_t weight, const QString& title) { bool WindowsFirewall::blockTrafficTo(const IPAddress& addr, uint8_t weight, const QString& title, - const QString& peer) { + QList& target) { QString description("Block traffic %1 %2 "); auto lower = addr.address(); @@ -852,12 +872,12 @@ bool WindowsFirewall::blockTrafficTo(const IPAddress& addr, uint8_t weight, filter.layerKey = layerKeyOut; if (!enableFilter(&filter, title, description.arg("to").arg(addr.toString()), - peer)) { + target)) { return false; } filter.layerKey = layerKeyIn; if (!enableFilter(&filter, title, - description.arg("from").arg(addr.toString()), peer)) { + description.arg("from").arg(addr.toString()), target)) { return false; } return true; @@ -865,9 +885,9 @@ bool WindowsFirewall::blockTrafficTo(const IPAddress& addr, uint8_t weight, bool WindowsFirewall::blockTrafficTo(const QList& rangeList, uint8_t weight, const QString& title, - const QString& peer) { + QList& target) { for (auto range : rangeList) { - if (!blockTrafficTo(range, weight, title, peer)) { + if (!blockTrafficTo(range, weight, title, target)) { logger.info() << "Setting Range of" << range.toString() << "failed"; return false; } @@ -923,7 +943,8 @@ void WindowsFirewall::importAddress(const QHostAddress& addr, } bool WindowsFirewall::blockTrafficOnPort(uint port, uint8_t weight, - const QString& title) { + const QString& title, + QList& target) { // Allow Traffic to IP with PORT using any protocol FWPM_FILTER_CONDITION0 conds[3]; conds[0].fieldKey = FWPM_CONDITION_IP_PROTOCOL; @@ -953,20 +974,20 @@ bool WindowsFirewall::blockTrafficOnPort(uint port, uint8_t weight, QString description("Block %1 on Port %2"); filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V6; - if (!enableFilter(&filter, title, description.arg("outgoing v6").arg(port))) { + if (!enableFilter(&filter, title, description.arg("outgoing v6").arg(port), target)) { return false; } filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4; - if (!enableFilter(&filter, title, description.arg("outgoing v4").arg(port))) { + if (!enableFilter(&filter, title, description.arg("outgoing v4").arg(port), target)) { return false; } filter.layerKey = FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4; - if (!enableFilter(&filter, title, description.arg("incoming v4").arg(port))) { + if (!enableFilter(&filter, title, description.arg("incoming v4").arg(port), target)) { return false; } filter.layerKey = FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6; - if (!enableFilter(&filter, title, description.arg("incoming v6").arg(port))) { + if (!enableFilter(&filter, title, description.arg("incoming v6").arg(port), target)) { return false; } return true; @@ -974,7 +995,7 @@ bool WindowsFirewall::blockTrafficOnPort(uint port, uint8_t weight, bool WindowsFirewall::enableFilter(FWPM_FILTER0* filter, const QString& title, const QString& description, - const QString& peer) { + QList& target) { uint64_t filterID = 0; auto name = title.toStdWString(); auto desc = description.toStdWString(); @@ -987,16 +1008,12 @@ bool WindowsFirewall::enableFilter(FWPM_FILTER0* filter, const QString& title, return false; } logger.info() << "Filter added: " << title << ":" << description; - if (peer.isEmpty()) { - m_activeRules.append(filterID); - } else { - m_peerRules.insert(peer, filterID); - } + target.append(filterID); return true; } -bool WindowsFirewall::allowLoopbackTraffic(uint8_t weight, - const QString& title) { +bool WindowsFirewall::allowLoopbackTraffic(uint8_t weight, const QString& title, + QList& target) { QList networkInterfaces = QNetworkInterface::allInterfaces(); for (const auto& iface : networkInterfaces) { @@ -1004,7 +1021,7 @@ bool WindowsFirewall::allowLoopbackTraffic(uint8_t weight, continue; } if (!allowTrafficOfAdapter(iface.index(), weight, - title.arg(iface.name()))) { + title.arg(iface.name()), target)) { return false; } } diff --git a/client/platforms/windows/daemon/windowsfirewall.h b/client/platforms/windows/daemon/windowsfirewall.h index 9a0062daa..0bfd0934f 100644 --- a/client/platforms/windows/daemon/windowsfirewall.h +++ b/client/platforms/windows/daemon/windowsfirewall.h @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -38,38 +39,42 @@ class WindowsFirewall final : public QObject { static WindowsFirewall* create(QObject* parent); ~WindowsFirewall() override; - bool enableInterface(int vpnAdapterIndex); + bool enableInterface(int vpnAdapterIndex, const QString& ifname = QString()); bool enableLanBypass(const QList& ranges); bool enablePeerTraffic(const InterfaceConfig& config); - bool disablePeerTraffic(const QString& pubkey); bool disableKillSwitch(); + bool disableKillSwitchForTunnel(const QString& ifname); bool allowAllTraffic(); - bool allowTrafficRange(const QStringList& ranges); + bool allowTrafficRange(const QStringList& ranges, const QString& ifname = QString()); private: static bool initSublayer(); WindowsFirewall(HANDLE session, QObject* parent); HANDLE m_sessionHandle; bool m_init = false; - QList m_activeRules; - QMultiMap m_peerRules; + QList m_globalRules; + QMap> m_tunnelRules; bool allowTrafficForAppOnAll(const QString& exePath, int weight, - const QString& title); + const QString& title, QList& target); bool blockTrafficTo(const QList& range, uint8_t weight, - const QString& title, const QString& peer = QString()); + const QString& title, QList& target); bool blockTrafficTo(const IPAddress& addr, uint8_t weight, - const QString& title, const QString& peer = QString()); - bool blockTrafficOnPort(uint port, uint8_t weight, const QString& title); + const QString& title, QList& target); + bool blockTrafficOnPort(uint port, uint8_t weight, const QString& title, + QList& target); bool allowTrafficTo(const IPAddress& addr, int weight, const QString& title, - const QString& peer = QString()); + QList& target); bool allowTrafficTo(const QHostAddress& targetIP, uint port, int weight, - const QString& title, const QString& peer = QString()); + const QString& title, QList& target); bool allowTrafficOfAdapter(int networkAdapter, uint8_t weight, - const QString& title); - bool allowDHCPTraffic(uint8_t weight, const QString& title); - bool allowHyperVTraffic(uint8_t weight, const QString& title); - bool allowLoopbackTraffic(uint8_t weight, const QString& title); + const QString& title, QList& target); + bool allowDHCPTraffic(uint8_t weight, const QString& title, + QList& target); + bool allowHyperVTraffic(uint8_t weight, const QString& title, + QList& target); + bool allowLoopbackTraffic(uint8_t weight, const QString& title, + QList& target); // Utils QString getCurrentPath(); @@ -78,8 +83,7 @@ class WindowsFirewall final : public QObject { void importAddress(const QHostAddress& addr, OUT FWP_CONDITION_VALUE0_& value, OUT QByteArray* v6DataBuffer); bool enableFilter(FWPM_FILTER0* filter, const QString& title, - const QString& description, - const QString& peer = QString()); + const QString& description, QList& target); }; #endif // WINDOWSFIREWALL_H diff --git a/client/vpnConnection.cpp b/client/vpnConnection.cpp index c5600c2df..fb1a1aac6 100644 --- a/client/vpnConnection.cpp +++ b/client/vpnConnection.cpp @@ -166,20 +166,37 @@ void VpnConnection::connectToVpn(const QString &serverId, DockerContainer contai NetworkUtilities::getIPAddress(vpnConfiguration.value(configKey::hostName).toString()); #ifdef AMNEZIA_DESKTOP - // Seamless WG -> WG switch path: already connected via Tunnel, new container is also WG. - if (m_active - && m_connectionState == Vpn::ConnectionState::Connected - && VpnProtocol::isWireGuardBased(container)) { - if (!m_trafficGuard->allowEndpoint(resolvedRemote)) { + const bool isWg = VpnProtocol::isWireGuardBased(container); + const QString preAllocatedIfname = isWg ? allocateIfname() : QString(); + + bool seamlessSwitch = m_active + && m_connectionState == Vpn::ConnectionState::Connected + && isWg; +#ifdef Q_OS_WIN + if (seamlessSwitch) { + auto clientIpFor = [](const QJsonObject& cfg) { + const QString proto = cfg.value("protocol").toString(); + return cfg.value(proto + "_config_data").toObject().value(configKey::clientIp).toString(); + }; + if (clientIpFor(vpnConfiguration) == clientIpFor(m_active->config())) { + seamlessSwitch = false; + } + } +#endif + + if (seamlessSwitch) { + if (!m_trafficGuard->allowEndpoint(resolvedRemote, preAllocatedIfname)) { + releaseIfname(preAllocatedIfname); setConnectionState(Vpn::ConnectionState::Error); emit vpnProtocolError(ErrorCode::AmneziaServiceConnectionFailed); return; } - startTunnelSwitch(container, vpnConfiguration, resolvedRemote); + startTunnelSwitch(container, vpnConfiguration, resolvedRemote, preAllocatedIfname); return; } - if (!m_trafficGuard->allowEndpoint(resolvedRemote)) { + if (!m_trafficGuard->allowEndpoint(resolvedRemote, preAllocatedIfname)) { + if (isWg) releaseIfname(preAllocatedIfname); setConnectionState(Vpn::ConnectionState::Error); emit vpnProtocolError(ErrorCode::AmneziaServiceConnectionFailed); return; @@ -212,10 +229,9 @@ void VpnConnection::connectToVpn(const QString &serverId, DockerContainer contai m_remoteAddress = resolvedRemote; #ifdef AMNEZIA_DESKTOP - if (VpnProtocol::isWireGuardBased(container)) { - const QString ifname = allocateIfname(); - config.insert("ifname", ifname); - m_active = new Tunnel(ifname, container, config, resolvedRemote, this); + if (isWg) { + config.insert("ifname", preAllocatedIfname); + m_active = new Tunnel(preAllocatedIfname, container, config, resolvedRemote, this); wireTunnelSignals(m_active, /*isActive=*/true); wireDaemonReconnectSignals(); m_trafficGuard->setConfig(config); @@ -255,7 +271,10 @@ void VpnConnection::createProtocolConnections() connect(m_vpnProtocol.data(), &VpnProtocol::protocolError, this, &VpnConnection::vpnProtocolError); connect(m_vpnProtocol.data(), &VpnProtocol::connectionStateChanged, this, &VpnConnection::setConnectionState); connect(m_vpnProtocol.data(), SIGNAL(bytesChanged(quint64, quint64)), this, SLOT(onBytesChanged(quint64, quint64))); - connect(m_vpnProtocol.data(), &VpnProtocol::tunnelAddressesUpdated, m_trafficGuard.data(), &VpnTrafficGuard::applyFirewall); + connect(m_vpnProtocol.data(), &VpnProtocol::tunnelAddressesUpdated, this, + [this](const QString& gateway, const QString& localAddress) { + m_trafficGuard->applyKillSwitch(nullptr, gateway, localAddress); + }); wireDaemonReconnectSignals(); } @@ -532,13 +551,14 @@ void VpnConnection::setConnectionState(Vpn::ConnectionState state) { void VpnConnection::startTunnelSwitch(DockerContainer container, const QJsonObject &vpnConfiguration, - const QString &resolvedRemote) + const QString &resolvedRemote, + const QString &stagingIfname) { QJsonObject config = vpnConfiguration; + config.insert("ifname", stagingIfname); appendKillSwitchConfig(config); appendSplitTunnelingConfig(config); - const QString stagingIfname = allocateIfname(); m_staging = new Tunnel(stagingIfname, container, config, resolvedRemote, this); wireTunnelSignals(m_staging, /*isActive=*/false); @@ -576,10 +596,6 @@ void VpnConnection::onTunnelActivated() if (tunnel == m_active) { setConnectionState(Vpn::ConnectionState::Connected); - if (auto proto = m_active->protocol()) { - m_trafficGuard->applyFirewall(proto->vpnGateway(), - proto->vpnLocalAddress()); - } } } diff --git a/client/vpnConnection.h b/client/vpnConnection.h index b23554cde..aba693833 100644 --- a/client/vpnConnection.h +++ b/client/vpnConnection.h @@ -108,7 +108,8 @@ private: void startTunnelSwitch(DockerContainer container, const QJsonObject &vpnConfiguration, - const QString &resolvedRemote); + const QString &resolvedRemote, + const QString &stagingIfname); private slots: void onTunnelPrepared(); diff --git a/ipc/ipc_interface.rep b/ipc/ipc_interface.rep index 306bb5320..d7223ec65 100644 --- a/ipc/ipc_interface.rep +++ b/ipc/ipc_interface.rep @@ -35,9 +35,10 @@ class IpcInterface SLOT( bool StopRoutingIpv6() ); SLOT( bool disableKillSwitch() ); + SLOT( bool disableKillSwitchForTunnel( const QString &ifname, const QStringList &remainingRanges ) ); SLOT( bool disableAllTraffic() ); SLOT( bool refreshKillSwitch( bool enabled ) ); - SLOT( bool addKillSwitchAllowedRange( const QStringList ranges ) ); + SLOT( bool addKillSwitchAllowedRange( const QString &ifname, const QStringList ranges ) ); SLOT( bool resetKillSwitchAllowedRange( const QStringList ranges ) ); SLOT( bool enablePeerTraffic( const QJsonObject &configStr) ); SLOT( bool enableKillSwitch( const QJsonObject &excludeAddr, int vpnAdapterIndex) ); diff --git a/ipc/ipcserver.cpp b/ipc/ipcserver.cpp index e28261cc7..8865b1de0 100644 --- a/ipc/ipcserver.cpp +++ b/ipc/ipcserver.cpp @@ -282,13 +282,13 @@ bool IpcServer::resetKillSwitchAllowedRange(QStringList ranges) return KillSwitch::instance()->resetAllowedRange(ranges); } -bool IpcServer::addKillSwitchAllowedRange(QStringList ranges) +bool IpcServer::addKillSwitchAllowedRange(const QString &ifname, QStringList ranges) { #ifdef MZ_DEBUG - qDebug() << "IpcServer::addKillSwitchAllowedRange"; + qDebug() << "IpcServer::addKillSwitchAllowedRange" << ifname; #endif - return KillSwitch::instance()->addAllowedRange(ranges); + return KillSwitch::instance()->addAllowedRange(ifname, ranges); } bool IpcServer::disableAllTraffic() @@ -318,6 +318,15 @@ bool IpcServer::disableKillSwitch() return KillSwitch::instance()->disableKillSwitch(); } +bool IpcServer::disableKillSwitchForTunnel(const QString &ifname, const QStringList &remainingRanges) +{ +#ifdef MZ_DEBUG + qDebug() << "IpcServer::disableKillSwitchForTunnel" << ifname; +#endif + + return KillSwitch::instance()->disableKillSwitchForTunnel(ifname, remainingRanges); +} + bool IpcServer::enablePeerTraffic(const QJsonObject &configStr) { #ifdef MZ_DEBUG diff --git a/ipc/ipcserver.h b/ipc/ipcserver.h index 8530e37cf..16dbc9803 100644 --- a/ipc/ipcserver.h +++ b/ipc/ipcserver.h @@ -41,11 +41,12 @@ public: virtual bool StartRoutingIpv6() override; virtual bool StopRoutingIpv6() override; virtual bool disableAllTraffic() override; - virtual bool addKillSwitchAllowedRange(QStringList ranges) override; + virtual bool addKillSwitchAllowedRange(const QString &ifname, QStringList ranges) override; virtual bool resetKillSwitchAllowedRange(QStringList ranges) override; virtual bool enablePeerTraffic(const QJsonObject &configStr) override; virtual bool enableKillSwitch(const QJsonObject &excludeAddr, int vpnAdapterIndex) override; virtual bool disableKillSwitch() override; + virtual bool disableKillSwitchForTunnel(const QString &ifname, const QStringList &remainingRanges) override; virtual bool refreshKillSwitch( bool enabled ) override; virtual bool updateResolvers(const QString& ifname, const QList& resolvers) override; virtual bool restoreResolvers() override; diff --git a/service/server/killswitch.cpp b/service/server/killswitch.cpp index 498fc8fde..144071678 100644 --- a/service/server/killswitch.cpp +++ b/service/server/killswitch.cpp @@ -159,6 +159,16 @@ bool KillSwitch::disableKillSwitch() { return true; } +bool KillSwitch::disableKillSwitchForTunnel(const QString& ifname, const QStringList& remainingRanges) { +#ifdef Q_OS_WIN + Q_UNUSED(remainingRanges) + return WindowsFirewall::create(this)->disableKillSwitchForTunnel(ifname); +#else + Q_UNUSED(ifname) + return resetAllowedRange(remainingRanges); +#endif +} + bool KillSwitch::disableAllTraffic() { #ifdef Q_OS_WIN WindowsFirewall::create(this)->enableInterface(-1); @@ -221,7 +231,15 @@ bool KillSwitch::resetAllowedRange(const QStringList &ranges) { return true; } -bool KillSwitch::addAllowedRange(const QStringList &ranges) { +bool KillSwitch::addAllowedRange(const QString &ifname, const QStringList &ranges) { +#ifdef Q_OS_WIN + if (!ifname.isEmpty()) { + return WindowsFirewall::create(this)->allowTrafficRange(ranges, ifname); + } +#else + Q_UNUSED(ifname) +#endif + for (const QString &range : ranges) { if (!range.isEmpty() && !m_allowedRanges.contains(range)) { m_allowedRanges.append(range); @@ -242,7 +260,11 @@ bool KillSwitch::enablePeerTraffic(const QJsonObject &configStr) { config.m_secondaryDnsServer = configStr.value(amnezia::configKey::dns2).toString(); } - config.m_serverPublicKey = "openvpn"; + config.m_ifname = configStr.value("ifname").toString(); + const QString protocolName = configStr.value(amnezia::configKey::vpnProto).toString(); + const QString pubkey = configStr.value(protocolName + "_config_data").toObject() + .value(amnezia::configKey::serverPubKey).toString(); + config.m_serverPublicKey = pubkey.isEmpty() ? QStringLiteral("openvpn") : pubkey; config.m_serverIpv4Gateway = configStr.value("vpnGateway").toString(); config.m_serverIpv4AddrIn = configStr.value("vpnServer").toString(); int vpnAdapterIndex = resolveVpnAdapterIndex(configStr); @@ -306,10 +328,11 @@ bool KillSwitch::enableKillSwitch(const QJsonObject &configStr, int vpnAdapterIn #ifdef Q_OS_WIN Q_UNUSED(vpnAdapterIndex) const int resolvedIndex = resolveVpnAdapterIndex(configStr); + const QString ifname = configStr.value("ifname").toString(); if (configStr.value("splitTunnelType").toInt() != 0) { WindowsFirewall::create(this)->allowAllTraffic(); } - return WindowsFirewall::create(this)->enableInterface(resolvedIndex); + return WindowsFirewall::create(this)->enableInterface(resolvedIndex, ifname); #endif #if defined(Q_OS_LINUX) || defined(Q_OS_MACOS) diff --git a/service/server/killswitch.h b/service/server/killswitch.h index d4022aae8..8ce773b4f 100644 --- a/service/server/killswitch.h +++ b/service/server/killswitch.h @@ -14,11 +14,12 @@ public: bool init(); bool refresh(bool enabled); bool disableKillSwitch(); + bool disableKillSwitchForTunnel(const QString& ifname, const QStringList& remainingRanges); bool disableAllTraffic(); bool enablePeerTraffic(const QJsonObject &configStr); bool enableKillSwitch(const QJsonObject &configStr, int vpnAdapterIndex); bool resetAllowedRange(const QStringList &ranges); - bool addAllowedRange(const QStringList &ranges); + bool addAllowedRange(const QString &ifname, const QStringList &ranges); bool isStrictKillSwitchEnabled(); private: