From 3590b2d323996dd6a8f6645c0fe235a5e4fcb2a0 Mon Sep 17 00:00:00 2001 From: cd-amn Date: Wed, 27 May 2026 14:26:15 +0000 Subject: [PATCH] feat: seamless WG switch on Windows for shared client IPs --- client/core/vpnTrafficGuard.cpp | 8 +- client/daemon/daemon.cpp | 22 +++- client/daemon/daemon.h | 4 +- client/daemon/interfaceconfig.h | 1 + client/daemon/wireguardutils.h | 3 + .../windows/daemon/windowsroutemonitor.cpp | 103 ++++++++++++------ .../windows/daemon/windowsroutemonitor.h | 8 +- .../windows/daemon/wireguardutilswindows.cpp | 75 ++++++++++++- .../windows/daemon/wireguardutilswindows.h | 3 + client/vpnConnection.cpp | 20 +--- ipc/ipc_interface.rep | 4 +- ipc/ipcserver.cpp | 8 +- ipc/ipcserver.h | 4 +- 13 files changed, 184 insertions(+), 79 deletions(-) diff --git a/client/core/vpnTrafficGuard.cpp b/client/core/vpnTrafficGuard.cpp index 801853a19..b45c44f97 100644 --- a/client/core/vpnTrafficGuard.cpp +++ b/client/core/vpnTrafficGuard.cpp @@ -360,9 +360,9 @@ void VpnTrafficGuard::applyPolicy(Tunnel* tunnel) const QString peer = tunnel->remoteAddress(); IpcClient::withInterface([&](QSharedPointer iface) { - if (!peer.isEmpty()) iface->addExclusionRoute(peer); + if (!peer.isEmpty()) iface->addExclusionRoute(ifname, peer); for (const QString& addr : excluded) { - iface->addExclusionRoute(addr); + iface->addExclusionRoute(ifname, addr); } for (const QString& prefix : prefixes) { iface->addAllowedIp(ifname, prefix); @@ -390,9 +390,9 @@ void VpnTrafficGuard::revokePolicy(Tunnel* tunnel) iface->delAllowedIp(ifname, prefix); } 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 Q_UNUSED(tunnel) diff --git a/client/daemon/daemon.cpp b/client/daemon/daemon.cpp index e5dcd9d3c..21d81baf9 100644 --- a/client/daemon/daemon.cpp +++ b/client/daemon/daemon.cpp @@ -85,7 +85,9 @@ bool Daemon::activate(const QString& ifname, const InterfaceConfig& config) { // Bring up the wireguard interface if not already done. 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."; return false; } @@ -126,6 +128,14 @@ bool Daemon::setPrimary(const QString& ifname, const InterfaceConfig& config) { const QString priorPrimary = m_primaryIfname; 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] { deactivateTunnel(ifname); m_primaryIfname = priorPrimary; @@ -190,13 +200,14 @@ bool Daemon::parseStringList(const QJsonObject& obj, const QString& name, return true; } -bool Daemon::addExclusionRoute(const QString &addr) { +bool Daemon::addExclusionRoute(const QString &ifname, const QString &addr) { IPAddress prefix(addr); if (m_excludedAddrSet.contains(prefix)) { m_excludedAddrSet[prefix]++; return true; } - WireguardUtils* wg = primaryWgutils(); + WireguardUtils* wg = wgutilsFor(ifname); + if (!wg) wg = primaryWgutils(); if (!wg || !wg->addExclusionRoute(prefix)) { return false; } @@ -204,7 +215,7 @@ bool Daemon::addExclusionRoute(const QString &addr) { return true; } -bool Daemon::delExclusionRoute(const QString &addr) { +bool Daemon::delExclusionRoute(const QString &ifname, const QString &addr) { IPAddress prefix(addr); if (!m_excludedAddrSet.contains(prefix)) { return false; @@ -214,7 +225,8 @@ bool Daemon::delExclusionRoute(const QString &addr) { return true; } m_excludedAddrSet.remove(prefix); - WireguardUtils* wg = primaryWgutils(); + WireguardUtils* wg = wgutilsFor(ifname); + if (!wg) wg = primaryWgutils(); return wg && wg->deleteExclusionRoute(prefix); } diff --git a/client/daemon/daemon.h b/client/daemon/daemon.h index fa1311067..138b543fa 100644 --- a/client/daemon/daemon.h +++ b/client/daemon/daemon.h @@ -37,8 +37,8 @@ class Daemon : public QObject { virtual bool deactivate(bool emitSignals = true); virtual QJsonObject getStatus(); - bool addExclusionRoute(const QString &addr); - bool delExclusionRoute(const QString &addr); + bool addExclusionRoute(const QString &ifname, const QString &addr); + bool delExclusionRoute(const QString &ifname, const QString &addr); bool addAllowedIp(const QString &ifname, const QString &prefix); bool delAllowedIp(const QString &ifname, const QString &prefix); bool setTunnelResolvers(const QString &ifname, const QStringList &resolvers); diff --git a/client/daemon/interfaceconfig.h b/client/daemon/interfaceconfig.h index aa845f065..fd484a892 100644 --- a/client/daemon/interfaceconfig.h +++ b/client/daemon/interfaceconfig.h @@ -60,6 +60,7 @@ class InterfaceConfig { QString m_transportPacketMagicHeader; QMap m_specialJunk; QString m_ifname; + bool m_deferAddressSetup = false; QJsonObject toJson() const; QString toWgConf( diff --git a/client/daemon/wireguardutils.h b/client/daemon/wireguardutils.h index 3f927c3ab..1e893c321 100644 --- a/client/daemon/wireguardutils.h +++ b/client/daemon/wireguardutils.h @@ -37,6 +37,9 @@ class WireguardUtils : public QObject { virtual bool addInterface(const InterfaceConfig& config) = 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 deletePeer(const InterfaceConfig& config) = 0; virtual QList getPeerStatus() = 0; diff --git a/client/platforms/windows/daemon/windowsroutemonitor.cpp b/client/platforms/windows/daemon/windowsroutemonitor.cpp index 1d0ce4c21..8e0b3ea1a 100644 --- a/client/platforms/windows/daemon/windowsroutemonitor.cpp +++ b/client/platforms/windows/daemon/windowsroutemonitor.cpp @@ -58,9 +58,12 @@ static int prefixcmp(const void* a, const void* b, size_t bits) { return 0; } +QSet WindowsRouteMonitor::s_vpnLuids; + WindowsRouteMonitor::WindowsRouteMonitor(quint64 luid, QObject* parent) : QObject(parent), m_luid(luid) { MZ_COUNT_CTOR(WindowsRouteMonitor); + s_vpnLuids.insert(luid); logger.debug() << "WindowsRouteMonitor created."; NotifyRouteChange2(AF_INET, routeChangeCallback, this, FALSE, &m_routeHandle); @@ -69,8 +72,8 @@ WindowsRouteMonitor::WindowsRouteMonitor(quint64 luid, QObject* parent) WindowsRouteMonitor::~WindowsRouteMonitor() { MZ_COUNT_DTOR(WindowsRouteMonitor); CancelMibChangeNotify2(m_routeHandle); + s_vpnLuids.remove(m_luid); - flushRouteTable(m_exclusionRoutes); flushRouteTable(m_clonedRoutes); logger.debug() << "WindowsRouteMonitor destroyed."; } @@ -95,7 +98,8 @@ void WindowsRouteMonitor::updateInterfaceMetrics(int family) { // Rebuild the list of interfaces that are valid for routing. for (ULONG i = 0; i < table->NumEntries; 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; } if (!row->Connected) { @@ -126,8 +130,8 @@ void WindowsRouteMonitor::updateExclusionRoute(MIB_IPFORWARD_ROW2* data, nexthop.si_family = data->DestinationPrefix.Prefix.si_family; for (ULONG i = 0; i < table->NumEntries; i++) { MIB_IPFORWARD_ROW2* row = &table->Table[i]; - // Ignore routes into the VPN interface. - if (row->InterfaceLuid.Value == m_luid) { + // Skip any VPN wintun (own or sibling). + if (s_vpnLuids.contains(row->InterfaceLuid.Value)) { continue; } if (row->DestinationPrefix.PrefixLength < bestMatch) { @@ -239,14 +243,16 @@ QHostAddress WindowsRouteMonitor::prefixToAddress( } } -bool WindowsRouteMonitor::isRouteExcluded(const IP_ADDRESS_PREFIX* dest) const { - auto i = m_exclusionRoutes.constBegin(); - while (i != m_exclusionRoutes.constEnd()) { - const MIB_IPFORWARD_ROW2* row = i.value(); +bool WindowsRouteMonitor::isRouteExcluded(void* ptable, + const IP_ADDRESS_PREFIX* dest) const { + PMIB_IPFORWARD_TABLE2 table = reinterpret_cast(ptable); + 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)) { return true; } - i++; } return false; } @@ -272,8 +278,8 @@ void WindowsRouteMonitor::updateCapturedRoutes(int family, void* ptable) { for (ULONG i = 0; i < table->NumEntries; i++) { MIB_IPFORWARD_ROW2* row = &table->Table[i]; - // Ignore routes into the VPN interface. - if (row->InterfaceLuid.Value == m_luid) { + // Skip any VPN wintun (own or sibling). + if (s_vpnLuids.contains(row->InterfaceLuid.Value)) { continue; } // Ignore the default route @@ -286,7 +292,7 @@ void WindowsRouteMonitor::updateCapturedRoutes(int family, void* ptable) { continue; } // Ignore routes which should be excluded. - if (isRouteExcluded(&row->DestinationPrefix)) { + if (isRouteExcluded(table, &row->DestinationPrefix)) { continue; } QHostAddress destination = prefixToAddress(&row->DestinationPrefix); @@ -375,11 +381,6 @@ bool WindowsRouteMonitor::addExclusionRoute(const IPAddress& prefix) { return true; } - if (m_exclusionRoutes.contains(prefix)) { - logger.warning() << "Exclusion route already exists"; - return false; - } - // Allocate and initialize the MIB routing table row. MIB_IPFORWARD_ROW2* data = new MIB_IPFORWARD_ROW2; InitializeIpForwardEntry(data); @@ -427,8 +428,8 @@ bool WindowsRouteMonitor::addExclusionRoute(const IPAddress& prefix) { updateCapturedRoutes(family, table); updateExclusionRoute(data, table); FreeMibTable(table); - - m_exclusionRoutes[prefix] = data; + delete data; + m_ownedExclusionRoutes.insert(prefix); return true; } @@ -436,23 +437,39 @@ bool WindowsRouteMonitor::deleteExclusionRoute(const IPAddress& prefix) { logger.debug() << "Deleting exclusion route for" << prefix.address().toString(); - MIB_IPFORWARD_ROW2* data = m_exclusionRoutes.take(prefix); - if (data == nullptr) { - return true; + m_ownedExclusionRoutes.remove(prefix); + + 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); - if ((result != ERROR_NOT_FOUND) && (result != NO_ERROR)) { - logger.error() << "Failed to delete route to" - << prefix.toString() - << "result:" << result; + const bool isV4 = prefix.address().protocol() == QAbstractSocket::IPv4Protocol; + const ADDRESS_FAMILY addrFamily = + isV4 ? static_cast(AF_INET) + : static_cast(AF_INET6); + 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; } - - // Captured routes might have changed. - updateCapturedRoutes(data->DestinationPrefix.Prefix.si_family); - - delete data; - return true; + FreeMibTable(table); + updateCapturedRoutes(addrFamily); + return deleted; } void WindowsRouteMonitor::flushRouteTable( @@ -492,8 +509,24 @@ void WindowsRouteMonitor::routeChanged() { updateInterfaceMetrics(AF_UNSPEC); 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(AF_INET) + : static_cast(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(©, table); + break; + } } FreeMibTable(table); diff --git a/client/platforms/windows/daemon/windowsroutemonitor.h b/client/platforms/windows/daemon/windowsroutemonitor.h index fa04f646d..cfe12f61a 100644 --- a/client/platforms/windows/daemon/windowsroutemonitor.h +++ b/client/platforms/windows/daemon/windowsroutemonitor.h @@ -14,6 +14,7 @@ #include #include #include +#include #include "ipaddress.h" @@ -28,7 +29,6 @@ class WindowsRouteMonitor final : public QObject { bool addExclusionRoute(const IPAddress& prefix); bool deleteExclusionRoute(const IPAddress& prefix); - void flushExclusionRoutes() { return flushRouteTable(m_exclusionRoutes); }; quint64 getLuid() const { return m_luid; } @@ -36,7 +36,7 @@ class WindowsRouteMonitor final : public QObject { void routeChanged(); 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, 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* table); - QHash m_exclusionRoutes; + QSet m_ownedExclusionRoutes; QMap m_interfaceMetricsIpv4; QMap m_interfaceMetricsIpv6; @@ -57,6 +57,8 @@ class WindowsRouteMonitor final : public QObject { const quint64 m_luid = 0; HANDLE m_routeHandle = INVALID_HANDLE_VALUE; + + static QSet s_vpnLuids; }; #endif /* WINDOWSROUTEMONITOR_H */ diff --git a/client/platforms/windows/daemon/wireguardutilswindows.cpp b/client/platforms/windows/daemon/wireguardutilswindows.cpp index 21edee64d..10eba5ad9 100644 --- a/client/platforms/windows/daemon/wireguardutilswindows.cpp +++ b/client/platforms/windows/daemon/wireguardutilswindows.cpp @@ -107,12 +107,21 @@ bool WireguardUtilsWindows::addInterface(const InterfaceConfig& config) { configString.truncate(peerStart); } - qsizetype dnsStart = configString.indexOf("DNS = "); - if (dnsStart >= 0) { - qsizetype dnsEnd = configString.indexOf('\n', dnsStart); - if (dnsEnd >= 0) { - configString.remove(dnsStart, dnsEnd - dnsStart + 1); - } + auto stripLine = [&](const QString& key) { + qsizetype start = configString.startsWith(key + " = ") + ? 0 + : configString.indexOf("\n" + key + " = "); + 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; @@ -143,6 +152,60 @@ bool WireguardUtilsWindows::deleteInterface() { 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(family); + row.OnLinkPrefixLength = prefix; + row.DadState = IpDadStatePreferred; + void* dst = (family == AF_INET) + ? static_cast(&row.Address.Ipv4.sin_addr) + : static_cast(&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(family); + void* dst = (family == AF_INET) + ? static_cast(&row.Address.Ipv4.sin_addr) + : static_cast(&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) { QByteArray publicKey = QByteArray::fromBase64(qPrintable(config.m_serverPublicKey)); diff --git a/client/platforms/windows/daemon/wireguardutilswindows.h b/client/platforms/windows/daemon/wireguardutilswindows.h index 8828cb82f..f1931522b 100644 --- a/client/platforms/windows/daemon/wireguardutilswindows.h +++ b/client/platforms/windows/daemon/wireguardutilswindows.h @@ -32,6 +32,9 @@ class WireguardUtilsWindows final : public WireguardUtils { bool addInterface(const InterfaceConfig& config) 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 deletePeer(const InterfaceConfig& config) override; QList getPeerStatus() override; diff --git a/client/vpnConnection.cpp b/client/vpnConnection.cpp index fb1a1aac6..589c99b44 100644 --- a/client/vpnConnection.cpp +++ b/client/vpnConnection.cpp @@ -169,22 +169,10 @@ void VpnConnection::connectToVpn(const QString &serverId, DockerContainer contai 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) { + // Seamless WG -> WG switch path: already connected via Tunnel, new container is also WG. + if (m_active + && m_connectionState == Vpn::ConnectionState::Connected + && isWg) { if (!m_trafficGuard->allowEndpoint(resolvedRemote, preAllocatedIfname)) { releaseIfname(preAllocatedIfname); setConnectionState(Vpn::ConnectionState::Error); diff --git a/ipc/ipc_interface.rep b/ipc/ipc_interface.rep index d7223ec65..9823d99fd 100644 --- a/ipc/ipc_interface.rep +++ b/ipc/ipc_interface.rep @@ -12,8 +12,8 @@ class IpcInterface SLOT( int routeAddList(const QString &gw, const QStringList &ips) ); SLOT( bool clearSavedRoutes() ); SLOT( bool routeDeleteList(const QString &gw, const QStringList &ip) ); - SLOT( bool addExclusionRoute(const QString &addr) ); - SLOT( bool delExclusionRoute(const QString &addr) ); + SLOT( bool addExclusionRoute(const QString &ifname, const QString &addr) ); + SLOT( bool delExclusionRoute(const QString &ifname, const QString &addr) ); SLOT( bool addAllowedIp(const QString &ifname, const QString &prefix) ); SLOT( bool delAllowedIp(const QString &ifname, const QString &prefix) ); SLOT( bool setTunnelResolvers(const QString &ifname, const QStringList &resolvers) ); diff --git a/ipc/ipcserver.cpp b/ipc/ipcserver.cpp index 8865b1de0..94bb9c553 100644 --- a/ipc/ipcserver.cpp +++ b/ipc/ipcserver.cpp @@ -93,14 +93,14 @@ bool IpcServer::routeDeleteList(const QString &gw, const QStringList &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) diff --git a/ipc/ipcserver.h b/ipc/ipcserver.h index 16dbc9803..2e0a9724b 100644 --- a/ipc/ipcserver.h +++ b/ipc/ipcserver.h @@ -23,8 +23,8 @@ public: virtual int routeAddList(const QString &gw, const QStringList &ips) override; virtual bool clearSavedRoutes() override; virtual bool routeDeleteList(const QString &gw, const QStringList &ips) override; - virtual bool addExclusionRoute(const QString &addr) override; - virtual bool delExclusionRoute(const QString &addr) override; + virtual bool addExclusionRoute(const QString &ifname, 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 delAllowedIp(const QString &ifname, const QString &prefix) override; virtual bool setTunnelResolvers(const QString &ifname, const QStringList &resolvers) override;