feat: seamless WG switch on Windows for shared client IPs

This commit is contained in:
cd-amn
2026-05-27 14:26:15 +00:00
parent 9a1e380ffb
commit 3590b2d323
13 changed files with 184 additions and 79 deletions
+4 -4
View File
@@ -360,9 +360,9 @@ void VpnTrafficGuard::applyPolicy(Tunnel* tunnel)
const QString peer = tunnel->remoteAddress(); const QString peer = tunnel->remoteAddress();
IpcClient::withInterface([&](QSharedPointer<IpcInterfaceReplica> iface) { IpcClient::withInterface([&](QSharedPointer<IpcInterfaceReplica> iface) {
if (!peer.isEmpty()) iface->addExclusionRoute(peer); if (!peer.isEmpty()) iface->addExclusionRoute(ifname, peer);
for (const QString& addr : excluded) { for (const QString& addr : excluded) {
iface->addExclusionRoute(addr); iface->addExclusionRoute(ifname, addr);
} }
for (const QString& prefix : prefixes) { for (const QString& prefix : prefixes) {
iface->addAllowedIp(ifname, prefix); iface->addAllowedIp(ifname, prefix);
@@ -390,9 +390,9 @@ void VpnTrafficGuard::revokePolicy(Tunnel* tunnel)
iface->delAllowedIp(ifname, prefix); iface->delAllowedIp(ifname, prefix);
} }
for (const QString& addr : excluded) { for (const QString& addr : excluded) {
iface->delExclusionRoute(addr); iface->delExclusionRoute(ifname, addr);
} }
if (!peer.isEmpty()) iface->delExclusionRoute(peer); if (!peer.isEmpty()) iface->delExclusionRoute(ifname, peer);
}); });
#else #else
Q_UNUSED(tunnel) Q_UNUSED(tunnel)
+17 -5
View File
@@ -85,7 +85,9 @@ bool Daemon::activate(const QString& ifname, const InterfaceConfig& config) {
// Bring up the wireguard interface if not already done. // Bring up the wireguard interface if not already done.
if (!wg->interfaceExists()) { if (!wg->interfaceExists()) {
if (!wg->addInterface(config)) { InterfaceConfig bringupConfig = config;
bringupConfig.m_deferAddressSetup = (m_primaryIfname != ifname);
if (!wg->addInterface(bringupConfig)) {
logger.error() << "Interface creation failed."; logger.error() << "Interface creation failed.";
return false; return false;
} }
@@ -126,6 +128,14 @@ bool Daemon::setPrimary(const QString& ifname, const InterfaceConfig& config) {
const QString priorPrimary = m_primaryIfname; const QString priorPrimary = m_primaryIfname;
m_primaryIfname = ifname; m_primaryIfname = ifname;
if (!priorPrimary.isEmpty() && priorPrimary != ifname) {
if (WireguardUtils* oldWg = m_tunnels.value(priorPrimary)) {
const InterfaceConfig& oldConfig = m_connections.value(priorPrimary).m_config;
oldWg->removeDeviceAddresses(oldConfig.m_deviceIpv4Address, oldConfig.m_deviceIpv6Address);
}
}
wg->applyDeviceAddresses(config.m_deviceIpv4Address, config.m_deviceIpv6Address);
auto failure_guard = qScopeGuard([this, ifname, priorPrimary] { auto failure_guard = qScopeGuard([this, ifname, priorPrimary] {
deactivateTunnel(ifname); deactivateTunnel(ifname);
m_primaryIfname = priorPrimary; m_primaryIfname = priorPrimary;
@@ -190,13 +200,14 @@ bool Daemon::parseStringList(const QJsonObject& obj, const QString& name,
return true; return true;
} }
bool Daemon::addExclusionRoute(const QString &addr) { bool Daemon::addExclusionRoute(const QString &ifname, const QString &addr) {
IPAddress prefix(addr); IPAddress prefix(addr);
if (m_excludedAddrSet.contains(prefix)) { if (m_excludedAddrSet.contains(prefix)) {
m_excludedAddrSet[prefix]++; m_excludedAddrSet[prefix]++;
return true; return true;
} }
WireguardUtils* wg = primaryWgutils(); WireguardUtils* wg = wgutilsFor(ifname);
if (!wg) wg = primaryWgutils();
if (!wg || !wg->addExclusionRoute(prefix)) { if (!wg || !wg->addExclusionRoute(prefix)) {
return false; return false;
} }
@@ -204,7 +215,7 @@ bool Daemon::addExclusionRoute(const QString &addr) {
return true; return true;
} }
bool Daemon::delExclusionRoute(const QString &addr) { bool Daemon::delExclusionRoute(const QString &ifname, const QString &addr) {
IPAddress prefix(addr); IPAddress prefix(addr);
if (!m_excludedAddrSet.contains(prefix)) { if (!m_excludedAddrSet.contains(prefix)) {
return false; return false;
@@ -214,7 +225,8 @@ bool Daemon::delExclusionRoute(const QString &addr) {
return true; return true;
} }
m_excludedAddrSet.remove(prefix); m_excludedAddrSet.remove(prefix);
WireguardUtils* wg = primaryWgutils(); WireguardUtils* wg = wgutilsFor(ifname);
if (!wg) wg = primaryWgutils();
return wg && wg->deleteExclusionRoute(prefix); return wg && wg->deleteExclusionRoute(prefix);
} }
+2 -2
View File
@@ -37,8 +37,8 @@ class Daemon : public QObject {
virtual bool deactivate(bool emitSignals = true); virtual bool deactivate(bool emitSignals = true);
virtual QJsonObject getStatus(); virtual QJsonObject getStatus();
bool addExclusionRoute(const QString &addr); bool addExclusionRoute(const QString &ifname, const QString &addr);
bool delExclusionRoute(const QString &addr); bool delExclusionRoute(const QString &ifname, const QString &addr);
bool addAllowedIp(const QString &ifname, const QString &prefix); bool addAllowedIp(const QString &ifname, const QString &prefix);
bool delAllowedIp(const QString &ifname, const QString &prefix); bool delAllowedIp(const QString &ifname, const QString &prefix);
bool setTunnelResolvers(const QString &ifname, const QStringList &resolvers); bool setTunnelResolvers(const QString &ifname, const QStringList &resolvers);
+1
View File
@@ -60,6 +60,7 @@ class InterfaceConfig {
QString m_transportPacketMagicHeader; QString m_transportPacketMagicHeader;
QMap<QString, QString> m_specialJunk; QMap<QString, QString> m_specialJunk;
QString m_ifname; QString m_ifname;
bool m_deferAddressSetup = false;
QJsonObject toJson() const; QJsonObject toJson() const;
QString toWgConf( QString toWgConf(
+3
View File
@@ -37,6 +37,9 @@ class WireguardUtils : public QObject {
virtual bool addInterface(const InterfaceConfig& config) = 0; virtual bool addInterface(const InterfaceConfig& config) = 0;
virtual bool deleteInterface() = 0; virtual bool deleteInterface() = 0;
virtual bool applyDeviceAddresses(const QString& ipv4Address, const QString& ipv6Address) { return true; }
virtual bool removeDeviceAddresses(const QString& ipv4Address, const QString& ipv6Address) { return true; }
virtual bool updatePeer(const InterfaceConfig& config) = 0; virtual bool updatePeer(const InterfaceConfig& config) = 0;
virtual bool deletePeer(const InterfaceConfig& config) = 0; virtual bool deletePeer(const InterfaceConfig& config) = 0;
virtual QList<PeerStatus> getPeerStatus() = 0; virtual QList<PeerStatus> getPeerStatus() = 0;
@@ -58,9 +58,12 @@ static int prefixcmp(const void* a, const void* b, size_t bits) {
return 0; return 0;
} }
QSet<quint64> WindowsRouteMonitor::s_vpnLuids;
WindowsRouteMonitor::WindowsRouteMonitor(quint64 luid, QObject* parent) WindowsRouteMonitor::WindowsRouteMonitor(quint64 luid, QObject* parent)
: QObject(parent), m_luid(luid) { : QObject(parent), m_luid(luid) {
MZ_COUNT_CTOR(WindowsRouteMonitor); MZ_COUNT_CTOR(WindowsRouteMonitor);
s_vpnLuids.insert(luid);
logger.debug() << "WindowsRouteMonitor created."; logger.debug() << "WindowsRouteMonitor created.";
NotifyRouteChange2(AF_INET, routeChangeCallback, this, FALSE, &m_routeHandle); NotifyRouteChange2(AF_INET, routeChangeCallback, this, FALSE, &m_routeHandle);
@@ -69,8 +72,8 @@ WindowsRouteMonitor::WindowsRouteMonitor(quint64 luid, QObject* parent)
WindowsRouteMonitor::~WindowsRouteMonitor() { WindowsRouteMonitor::~WindowsRouteMonitor() {
MZ_COUNT_DTOR(WindowsRouteMonitor); MZ_COUNT_DTOR(WindowsRouteMonitor);
CancelMibChangeNotify2(m_routeHandle); CancelMibChangeNotify2(m_routeHandle);
s_vpnLuids.remove(m_luid);
flushRouteTable(m_exclusionRoutes);
flushRouteTable(m_clonedRoutes); flushRouteTable(m_clonedRoutes);
logger.debug() << "WindowsRouteMonitor destroyed."; logger.debug() << "WindowsRouteMonitor destroyed.";
} }
@@ -95,7 +98,8 @@ void WindowsRouteMonitor::updateInterfaceMetrics(int family) {
// Rebuild the list of interfaces that are valid for routing. // Rebuild the list of interfaces that are valid for routing.
for (ULONG i = 0; i < table->NumEntries; i++) { for (ULONG i = 0; i < table->NumEntries; i++) {
MIB_IPINTERFACE_ROW* row = &table->Table[i]; MIB_IPINTERFACE_ROW* row = &table->Table[i];
if (row->InterfaceLuid.Value == m_luid) { // Skip any VPN wintun (own or sibling) so exclusion routes never pick one.
if (s_vpnLuids.contains(row->InterfaceLuid.Value)) {
continue; continue;
} }
if (!row->Connected) { if (!row->Connected) {
@@ -126,8 +130,8 @@ void WindowsRouteMonitor::updateExclusionRoute(MIB_IPFORWARD_ROW2* data,
nexthop.si_family = data->DestinationPrefix.Prefix.si_family; nexthop.si_family = data->DestinationPrefix.Prefix.si_family;
for (ULONG i = 0; i < table->NumEntries; i++) { for (ULONG i = 0; i < table->NumEntries; i++) {
MIB_IPFORWARD_ROW2* row = &table->Table[i]; MIB_IPFORWARD_ROW2* row = &table->Table[i];
// Ignore routes into the VPN interface. // Skip any VPN wintun (own or sibling).
if (row->InterfaceLuid.Value == m_luid) { if (s_vpnLuids.contains(row->InterfaceLuid.Value)) {
continue; continue;
} }
if (row->DestinationPrefix.PrefixLength < bestMatch) { if (row->DestinationPrefix.PrefixLength < bestMatch) {
@@ -239,14 +243,16 @@ QHostAddress WindowsRouteMonitor::prefixToAddress(
} }
} }
bool WindowsRouteMonitor::isRouteExcluded(const IP_ADDRESS_PREFIX* dest) const { bool WindowsRouteMonitor::isRouteExcluded(void* ptable,
auto i = m_exclusionRoutes.constBegin(); const IP_ADDRESS_PREFIX* dest) const {
while (i != m_exclusionRoutes.constEnd()) { PMIB_IPFORWARD_TABLE2 table = reinterpret_cast<PMIB_IPFORWARD_TABLE2>(ptable);
const MIB_IPFORWARD_ROW2* row = i.value(); for (ULONG i = 0; i < table->NumEntries; i++) {
const MIB_IPFORWARD_ROW2* row = &table->Table[i];
if (row->Protocol != MIB_IPPROTO_NETMGMT) continue;
if (row->Metric != EXCLUSION_ROUTE_METRIC) continue;
if (routeContainsDest(&row->DestinationPrefix, dest)) { if (routeContainsDest(&row->DestinationPrefix, dest)) {
return true; return true;
} }
i++;
} }
return false; return false;
} }
@@ -272,8 +278,8 @@ void WindowsRouteMonitor::updateCapturedRoutes(int family, void* ptable) {
for (ULONG i = 0; i < table->NumEntries; i++) { for (ULONG i = 0; i < table->NumEntries; i++) {
MIB_IPFORWARD_ROW2* row = &table->Table[i]; MIB_IPFORWARD_ROW2* row = &table->Table[i];
// Ignore routes into the VPN interface. // Skip any VPN wintun (own or sibling).
if (row->InterfaceLuid.Value == m_luid) { if (s_vpnLuids.contains(row->InterfaceLuid.Value)) {
continue; continue;
} }
// Ignore the default route // Ignore the default route
@@ -286,7 +292,7 @@ void WindowsRouteMonitor::updateCapturedRoutes(int family, void* ptable) {
continue; continue;
} }
// Ignore routes which should be excluded. // Ignore routes which should be excluded.
if (isRouteExcluded(&row->DestinationPrefix)) { if (isRouteExcluded(table, &row->DestinationPrefix)) {
continue; continue;
} }
QHostAddress destination = prefixToAddress(&row->DestinationPrefix); QHostAddress destination = prefixToAddress(&row->DestinationPrefix);
@@ -375,11 +381,6 @@ bool WindowsRouteMonitor::addExclusionRoute(const IPAddress& prefix) {
return true; return true;
} }
if (m_exclusionRoutes.contains(prefix)) {
logger.warning() << "Exclusion route already exists";
return false;
}
// Allocate and initialize the MIB routing table row. // Allocate and initialize the MIB routing table row.
MIB_IPFORWARD_ROW2* data = new MIB_IPFORWARD_ROW2; MIB_IPFORWARD_ROW2* data = new MIB_IPFORWARD_ROW2;
InitializeIpForwardEntry(data); InitializeIpForwardEntry(data);
@@ -427,8 +428,8 @@ bool WindowsRouteMonitor::addExclusionRoute(const IPAddress& prefix) {
updateCapturedRoutes(family, table); updateCapturedRoutes(family, table);
updateExclusionRoute(data, table); updateExclusionRoute(data, table);
FreeMibTable(table); FreeMibTable(table);
delete data;
m_exclusionRoutes[prefix] = data; m_ownedExclusionRoutes.insert(prefix);
return true; return true;
} }
@@ -436,23 +437,39 @@ bool WindowsRouteMonitor::deleteExclusionRoute(const IPAddress& prefix) {
logger.debug() << "Deleting exclusion route for" logger.debug() << "Deleting exclusion route for"
<< prefix.address().toString(); << prefix.address().toString();
MIB_IPFORWARD_ROW2* data = m_exclusionRoutes.take(prefix); m_ownedExclusionRoutes.remove(prefix);
if (data == nullptr) {
return true; PMIB_IPFORWARD_TABLE2 table;
DWORD result = GetIpForwardTable2(AF_UNSPEC, &table);
if (result != NO_ERROR) {
logger.error() << "Failed to fetch routing table:" << result;
return false;
} }
DWORD result = DeleteIpForwardEntry2(data); const bool isV4 = prefix.address().protocol() == QAbstractSocket::IPv4Protocol;
if ((result != ERROR_NOT_FOUND) && (result != NO_ERROR)) { const ADDRESS_FAMILY addrFamily =
logger.error() << "Failed to delete route to" isV4 ? static_cast<ADDRESS_FAMILY>(AF_INET)
<< prefix.toString() : static_cast<ADDRESS_FAMILY>(AF_INET6);
<< "result:" << result; bool deleted = false;
for (ULONG i = 0; i < table->NumEntries; i++) {
MIB_IPFORWARD_ROW2* row = &table->Table[i];
if (row->Protocol != MIB_IPPROTO_NETMGMT) continue;
if (row->Metric != EXCLUSION_ROUTE_METRIC) continue;
if (row->DestinationPrefix.Prefix.si_family != addrFamily) continue;
if (row->DestinationPrefix.PrefixLength != prefix.prefixLength()) continue;
if (prefixToAddress(&row->DestinationPrefix) != prefix.address()) continue;
DWORD r = DeleteIpForwardEntry2(row);
if (r == NO_ERROR || r == ERROR_NOT_FOUND) {
deleted = true;
} else {
logger.error() << "Failed to delete route to" << prefix.toString()
<< "result:" << r;
}
break;
} }
FreeMibTable(table);
// Captured routes might have changed. updateCapturedRoutes(addrFamily);
updateCapturedRoutes(data->DestinationPrefix.Prefix.si_family); return deleted;
delete data;
return true;
} }
void WindowsRouteMonitor::flushRouteTable( void WindowsRouteMonitor::flushRouteTable(
@@ -492,8 +509,24 @@ void WindowsRouteMonitor::routeChanged() {
updateInterfaceMetrics(AF_UNSPEC); updateInterfaceMetrics(AF_UNSPEC);
updateCapturedRoutes(AF_UNSPEC, table); updateCapturedRoutes(AF_UNSPEC, table);
for (MIB_IPFORWARD_ROW2* data : m_exclusionRoutes) {
updateExclusionRoute(data, table); for (const IPAddress& prefix : m_ownedExclusionRoutes) {
const bool isV4 =
prefix.address().protocol() == QAbstractSocket::IPv4Protocol;
const ADDRESS_FAMILY addrFamily =
isV4 ? static_cast<ADDRESS_FAMILY>(AF_INET)
: static_cast<ADDRESS_FAMILY>(AF_INET6);
for (ULONG i = 0; i < table->NumEntries; i++) {
MIB_IPFORWARD_ROW2* row = &table->Table[i];
if (row->Protocol != MIB_IPPROTO_NETMGMT) continue;
if (row->Metric != EXCLUSION_ROUTE_METRIC) continue;
if (row->DestinationPrefix.Prefix.si_family != addrFamily) continue;
if (row->DestinationPrefix.PrefixLength != prefix.prefixLength()) continue;
if (prefixToAddress(&row->DestinationPrefix) != prefix.address()) continue;
MIB_IPFORWARD_ROW2 copy = *row;
updateExclusionRoute(&copy, table);
break;
}
} }
FreeMibTable(table); FreeMibTable(table);
@@ -14,6 +14,7 @@
#include <QHash> #include <QHash>
#include <QMap> #include <QMap>
#include <QObject> #include <QObject>
#include <QSet>
#include "ipaddress.h" #include "ipaddress.h"
@@ -28,7 +29,6 @@ class WindowsRouteMonitor final : public QObject {
bool addExclusionRoute(const IPAddress& prefix); bool addExclusionRoute(const IPAddress& prefix);
bool deleteExclusionRoute(const IPAddress& prefix); bool deleteExclusionRoute(const IPAddress& prefix);
void flushExclusionRoutes() { return flushRouteTable(m_exclusionRoutes); };
quint64 getLuid() const { return m_luid; } quint64 getLuid() const { return m_luid; }
@@ -36,7 +36,7 @@ class WindowsRouteMonitor final : public QObject {
void routeChanged(); void routeChanged();
private: private:
bool isRouteExcluded(const IP_ADDRESS_PREFIX* dest) const; bool isRouteExcluded(void* table, const IP_ADDRESS_PREFIX* dest) const;
static bool routeContainsDest(const IP_ADDRESS_PREFIX* route, static bool routeContainsDest(const IP_ADDRESS_PREFIX* route,
const IP_ADDRESS_PREFIX* dest); const IP_ADDRESS_PREFIX* dest);
static QHostAddress prefixToAddress(const IP_ADDRESS_PREFIX* dest); static QHostAddress prefixToAddress(const IP_ADDRESS_PREFIX* dest);
@@ -47,7 +47,7 @@ class WindowsRouteMonitor final : public QObject {
void updateCapturedRoutes(int family); void updateCapturedRoutes(int family);
void updateCapturedRoutes(int family, void* table); void updateCapturedRoutes(int family, void* table);
QHash<IPAddress, MIB_IPFORWARD_ROW2*> m_exclusionRoutes; QSet<IPAddress> m_ownedExclusionRoutes;
QMap<quint64, ULONG> m_interfaceMetricsIpv4; QMap<quint64, ULONG> m_interfaceMetricsIpv4;
QMap<quint64, ULONG> m_interfaceMetricsIpv6; QMap<quint64, ULONG> m_interfaceMetricsIpv6;
@@ -57,6 +57,8 @@ class WindowsRouteMonitor final : public QObject {
const quint64 m_luid = 0; const quint64 m_luid = 0;
HANDLE m_routeHandle = INVALID_HANDLE_VALUE; HANDLE m_routeHandle = INVALID_HANDLE_VALUE;
static QSet<quint64> s_vpnLuids;
}; };
#endif /* WINDOWSROUTEMONITOR_H */ #endif /* WINDOWSROUTEMONITOR_H */
@@ -107,12 +107,21 @@ bool WireguardUtilsWindows::addInterface(const InterfaceConfig& config) {
configString.truncate(peerStart); configString.truncate(peerStart);
} }
qsizetype dnsStart = configString.indexOf("DNS = "); auto stripLine = [&](const QString& key) {
if (dnsStart >= 0) { qsizetype start = configString.startsWith(key + " = ")
qsizetype dnsEnd = configString.indexOf('\n', dnsStart); ? 0
if (dnsEnd >= 0) { : configString.indexOf("\n" + key + " = ");
configString.remove(dnsStart, dnsEnd - dnsStart + 1); if (start < 0) return;
} if (start != 0) start += 1;
qsizetype end = configString.indexOf('\n', start);
if (end < 0) return;
configString.remove(start, end - start + 1);
};
stripLine("DNS");
if (config.m_deferAddressSetup) {
// Wintun rejects duplicate IPv4; daemon will assign at swap time.
stripLine("Address");
} }
m_ifname = config.m_ifname.isEmpty() ? s_defaultInterfaceName() : config.m_ifname; m_ifname = config.m_ifname.isEmpty() ? s_defaultInterfaceName() : config.m_ifname;
@@ -143,6 +152,60 @@ bool WireguardUtilsWindows::deleteInterface() {
return true; return true;
} }
bool WireguardUtilsWindows::applyDeviceAddresses(const QString& ipv4Address,
const QString& ipv6Address) {
auto applyOne = [&](const QString& addr, int family, uint8_t prefix) -> bool {
if (addr.isEmpty()) return true;
const QByteArray ip = addr.section('/', 0, 0).toUtf8();
MIB_UNICASTIPADDRESS_ROW row;
InitializeUnicastIpAddressEntry(&row);
row.InterfaceLuid.Value = m_luid;
row.Address.si_family = static_cast<ADDRESS_FAMILY>(family);
row.OnLinkPrefixLength = prefix;
row.DadState = IpDadStatePreferred;
void* dst = (family == AF_INET)
? static_cast<void*>(&row.Address.Ipv4.sin_addr)
: static_cast<void*>(&row.Address.Ipv6.sin6_addr);
if (InetPtonA(family, ip.constData(), dst) != 1) {
logger.error() << "applyDeviceAddresses: cannot parse" << addr;
return false;
}
DWORD r = CreateUnicastIpAddressEntry(&row);
logger.debug() << "Apply" << addr << "to" << m_ifname << "result:" << r;
return r == NO_ERROR || r == ERROR_OBJECT_ALREADY_EXISTS;
};
if (!applyOne(ipv4Address, AF_INET, 32)) return false;
if (!applyOne(ipv6Address, AF_INET6, 128)) return false;
return true;
}
bool WireguardUtilsWindows::removeDeviceAddresses(const QString& ipv4Address,
const QString& ipv6Address) {
auto removeOne = [&](const QString& addr, int family) -> bool {
if (addr.isEmpty()) return true;
const QByteArray ip = addr.section('/', 0, 0).toUtf8();
MIB_UNICASTIPADDRESS_ROW row;
InitializeUnicastIpAddressEntry(&row);
row.InterfaceLuid.Value = m_luid;
row.Address.si_family = static_cast<ADDRESS_FAMILY>(family);
void* dst = (family == AF_INET)
? static_cast<void*>(&row.Address.Ipv4.sin_addr)
: static_cast<void*>(&row.Address.Ipv6.sin6_addr);
if (InetPtonA(family, ip.constData(), dst) != 1) {
logger.error() << "removeDeviceAddresses: cannot parse" << addr;
return false;
}
DWORD r = DeleteUnicastIpAddressEntry(&row);
logger.debug() << "Remove" << addr << "from" << m_ifname << "result:" << r;
return r == NO_ERROR || r == ERROR_NOT_FOUND;
};
bool ok = removeOne(ipv4Address, AF_INET);
ok &= removeOne(ipv6Address, AF_INET6);
return ok;
}
bool WireguardUtilsWindows::updatePeer(const InterfaceConfig& config) { bool WireguardUtilsWindows::updatePeer(const InterfaceConfig& config) {
QByteArray publicKey = QByteArray publicKey =
QByteArray::fromBase64(qPrintable(config.m_serverPublicKey)); QByteArray::fromBase64(qPrintable(config.m_serverPublicKey));
@@ -32,6 +32,9 @@ class WireguardUtilsWindows final : public WireguardUtils {
bool addInterface(const InterfaceConfig& config) override; bool addInterface(const InterfaceConfig& config) override;
bool deleteInterface() override; bool deleteInterface() override;
bool applyDeviceAddresses(const QString& ipv4Address, const QString& ipv6Address) override;
bool removeDeviceAddresses(const QString& ipv4Address, const QString& ipv6Address) override;
bool updatePeer(const InterfaceConfig& config) override; bool updatePeer(const InterfaceConfig& config) override;
bool deletePeer(const InterfaceConfig& config) override; bool deletePeer(const InterfaceConfig& config) override;
QList<PeerStatus> getPeerStatus() override; QList<PeerStatus> getPeerStatus() override;
+4 -16
View File
@@ -169,22 +169,10 @@ void VpnConnection::connectToVpn(const QString &serverId, DockerContainer contai
const bool isWg = VpnProtocol::isWireGuardBased(container); const bool isWg = VpnProtocol::isWireGuardBased(container);
const QString preAllocatedIfname = isWg ? allocateIfname() : QString(); const QString preAllocatedIfname = isWg ? allocateIfname() : QString();
bool seamlessSwitch = m_active // Seamless WG -> WG switch path: already connected via Tunnel, new container is also WG.
&& m_connectionState == Vpn::ConnectionState::Connected if (m_active
&& isWg; && m_connectionState == Vpn::ConnectionState::Connected
#ifdef Q_OS_WIN && isWg) {
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)) { if (!m_trafficGuard->allowEndpoint(resolvedRemote, preAllocatedIfname)) {
releaseIfname(preAllocatedIfname); releaseIfname(preAllocatedIfname);
setConnectionState(Vpn::ConnectionState::Error); setConnectionState(Vpn::ConnectionState::Error);
+2 -2
View File
@@ -12,8 +12,8 @@ class IpcInterface
SLOT( int routeAddList(const QString &gw, const QStringList &ips) ); SLOT( int routeAddList(const QString &gw, const QStringList &ips) );
SLOT( bool clearSavedRoutes() ); SLOT( bool clearSavedRoutes() );
SLOT( bool routeDeleteList(const QString &gw, const QStringList &ip) ); SLOT( bool routeDeleteList(const QString &gw, const QStringList &ip) );
SLOT( bool addExclusionRoute(const QString &addr) ); SLOT( bool addExclusionRoute(const QString &ifname, const QString &addr) );
SLOT( bool delExclusionRoute(const QString &addr) ); SLOT( bool delExclusionRoute(const QString &ifname, const QString &addr) );
SLOT( bool addAllowedIp(const QString &ifname, const QString &prefix) ); SLOT( bool addAllowedIp(const QString &ifname, const QString &prefix) );
SLOT( bool delAllowedIp(const QString &ifname, const QString &prefix) ); SLOT( bool delAllowedIp(const QString &ifname, const QString &prefix) );
SLOT( bool setTunnelResolvers(const QString &ifname, const QStringList &resolvers) ); SLOT( bool setTunnelResolvers(const QString &ifname, const QStringList &resolvers) );
+4 -4
View File
@@ -93,14 +93,14 @@ bool IpcServer::routeDeleteList(const QString &gw, const QStringList &ips)
return Router::routeDeleteList(gw, ips); return Router::routeDeleteList(gw, ips);
} }
bool IpcServer::addExclusionRoute(const QString &addr) bool IpcServer::addExclusionRoute(const QString &ifname, const QString &addr)
{ {
return Daemon::instance() && Daemon::instance()->addExclusionRoute(addr); return Daemon::instance() && Daemon::instance()->addExclusionRoute(ifname, addr);
} }
bool IpcServer::delExclusionRoute(const QString &addr) bool IpcServer::delExclusionRoute(const QString &ifname, const QString &addr)
{ {
return Daemon::instance() && Daemon::instance()->delExclusionRoute(addr); return Daemon::instance() && Daemon::instance()->delExclusionRoute(ifname, addr);
} }
bool IpcServer::addAllowedIp(const QString &ifname, const QString &prefix) bool IpcServer::addAllowedIp(const QString &ifname, const QString &prefix)
+2 -2
View File
@@ -23,8 +23,8 @@ public:
virtual int routeAddList(const QString &gw, const QStringList &ips) override; virtual int routeAddList(const QString &gw, const QStringList &ips) override;
virtual bool clearSavedRoutes() override; virtual bool clearSavedRoutes() override;
virtual bool routeDeleteList(const QString &gw, const QStringList &ips) override; virtual bool routeDeleteList(const QString &gw, const QStringList &ips) override;
virtual bool addExclusionRoute(const QString &addr) override; virtual bool addExclusionRoute(const QString &ifname, const QString &addr) override;
virtual bool delExclusionRoute(const QString &addr) override; virtual bool delExclusionRoute(const QString &ifname, const QString &addr) override;
virtual bool addAllowedIp(const QString &ifname, const QString &prefix) override; virtual bool addAllowedIp(const QString &ifname, const QString &prefix) override;
virtual bool delAllowedIp(const QString &ifname, const QString &prefix) override; virtual bool delAllowedIp(const QString &ifname, const QString &prefix) override;
virtual bool setTunnelResolvers(const QString &ifname, const QStringList &resolvers) override; virtual bool setTunnelResolvers(const QString &ifname, const QStringList &resolvers) override;