diff --git a/AmneziaVPN.entitlements b/AmneziaVPN.entitlements
new file mode 100644
index 00000000..10636b1b
--- /dev/null
+++ b/AmneziaVPN.entitlements
@@ -0,0 +1,14 @@
+
+
+
+
+ com.apple.developer.networking.networkextension
+
+ packet-tunnel-provider
+
+ com.apple.developer.networking.vpn.api
+
+ allow-vpn
+
+
+
diff --git a/client/cmake/macos.cmake b/client/cmake/macos.cmake
index 7b7cd381..17b6387a 100644
--- a/client/cmake/macos.cmake
+++ b/client/cmake/macos.cmake
@@ -25,10 +25,12 @@ set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15)
set(HEADERS ${HEADERS}
${CMAKE_CURRENT_SOURCE_DIR}/ui/macos_util.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/protocols/ikev2_vpn_protocol_mac.h
)
set(SOURCES ${SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/ui/macos_util.mm
+ ${CMAKE_CURRENT_SOURCE_DIR}/protocols/ikev2_vpn_protocol_mac.mm
)
set(ICON_FILE ${CMAKE_CURRENT_SOURCE_DIR}/images/app.icns)
diff --git a/client/containers/containers_defs.cpp b/client/containers/containers_defs.cpp
index 7647c166..b7df2ab5 100644
--- a/client/containers/containers_defs.cpp
+++ b/client/containers/containers_defs.cpp
@@ -294,7 +294,7 @@ bool ContainerProps::isSupportedByCurrentPlatform(DockerContainer c)
#elif defined(Q_OS_MAC)
switch (c) {
case DockerContainer::WireGuard: return true;
- case DockerContainer::Ipsec: return false;
+ case DockerContainer::Ipsec: return true;
default: return true;
}
diff --git a/client/protocols/ikev2_vpn_protocol_mac.h b/client/protocols/ikev2_vpn_protocol_mac.h
new file mode 100644
index 00000000..8d2e52f1
--- /dev/null
+++ b/client/protocols/ikev2_vpn_protocol_mac.h
@@ -0,0 +1,62 @@
+#pragma once
+
+#include
+#include
+
+
+#include "openvpnprotocol.h"
+
+
+class Ikev2Protocol : public VpnProtocol
+{
+ Q_OBJECT
+public:
+ explicit Ikev2Protocol(const QJsonObject& configuration, QObject* parent = nullptr);
+ virtual ~Ikev2Protocol() override;
+
+ void readIkev2Configuration(const QJsonObject &configuration);
+ bool create_new_vpn(const QString &vpn_name, const QString &serv_addr);
+ bool delete_vpn_connection(const QString &vpn_name);
+ bool connect_to_vpn(const QString & vpn_name);
+ bool disconnect_vpn();
+ void closeWindscribeActiveConnection();
+
+ ErrorCode start() override;
+ void stop() override;
+
+ static QString tunnelName() { return "AmneziaVPN IKEv2"; }
+
+private slots:
+ void handleNotificationImpl(int status);
+
+private:
+ enum {STATE_DISCONNECTED, STATE_START_CONNECT, STATE_START_DISCONNECTING, STATE_CONNECTED, STATE_DISCONNECTING_AUTH_ERROR, STATE_DISCONNECTING_ANY_ERROR};
+
+ int state_;
+
+ bool bConnected_;
+ mutable QRecursiveMutex mutex_;
+ void *notificationId_;
+ bool isStateConnectingAfterClick_;
+ bool isDisconnectClicked_;
+
+ QString overrideDnsIp_;
+
+ QJsonObject m_config;
+
+ static constexpr int STATISTICS_UPDATE_PERIOD = 1000;
+ QTimer statisticsTimer_;
+ QString ipsecAdapterName_;
+
+ int prevConnectionStatus_;
+ bool isPrevConnectionStatusInitialized_;
+
+ // True if startConnect() method was called and NEVPNManager emitted notification NEVPNStatusConnecting.
+ // False otherwise.
+ bool isConnectingStateReachedAfterStartingConnection_;
+
+ void handleNotification(void *notification);
+ bool isFailedAuthError(QMap &logs);
+ bool isSocketError(QMap &logs);
+ bool setCustomDns(const QString &overrideDnsIpAddress);
+};
diff --git a/client/protocols/ikev2_vpn_protocol_mac.mm b/client/protocols/ikev2_vpn_protocol_mac.mm
new file mode 100644
index 00000000..0c55c310
--- /dev/null
+++ b/client/protocols/ikev2_vpn_protocol_mac.mm
@@ -0,0 +1,491 @@
+#include "ikev2_vpn_protocol_mac.h"
+
+
+
+#include
+#include
+#include
+#include
+#import
+#import
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+static NSString * const IKEv1ServiceName = @"AmneziaVPN";
+static NSString * const IKEv2ServiceName = @"AmneziaVPN IKEv2";
+
+static Ikev2Protocol* self = nullptr;
+
+
+Ikev2Protocol::Ikev2Protocol(const QJsonObject &configuration, QObject* parent) :
+ VpnProtocol(configuration, parent)
+{
+ qDebug() << "IpsecProtocol::IpsecProtocol()";
+ self = this;
+ readIkev2Configuration(configuration);
+}
+
+Ikev2Protocol::~Ikev2Protocol()
+{
+ qDebug() << "IpsecProtocol::~IpsecProtocol()";
+ disconnect_vpn();
+ Ikev2Protocol::stop();
+}
+
+void Ikev2Protocol::stop()
+{
+ setConnectionState(Vpn::ConnectionState::Disconnected);
+ qDebug() << "IpsecProtocol::stop()";
+}
+
+
+void Ikev2Protocol::readIkev2Configuration(const QJsonObject &configuration)
+{
+ qDebug() << "IpsecProtocol::readIkev2Configuration";
+ QJsonObject ikev2_data = configuration.value(ProtocolProps::key_proto_config_data(Proto::Ikev2)).toObject();
+ m_config = QJsonDocument::fromJson(ikev2_data.value(config_key::config).toString().toUtf8()).object();
+}
+
+CFDataRef CreatePersistentRefForIdentity(SecIdentityRef identity)
+{
+ CFTypeRef persistent_ref = NULL;
+ const void *keys[] = { kSecReturnPersistentRef, kSecValueRef };
+ const void *values[] = { kCFBooleanTrue, identity };
+ CFDictionaryRef dict = CFDictionaryCreate(NULL, keys, values,
+ sizeof(keys) / sizeof(*keys), NULL, NULL);
+
+
+ if (SecItemCopyMatching(dict, &persistent_ref) != 0) {
+ SecItemAdd(dict, &persistent_ref);
+ }
+
+ if (dict)
+ CFRelease(dict);
+
+ return (CFDataRef)persistent_ref;
+}
+
+
+ErrorCode Ikev2Protocol::start()
+{
+
+ qDebug() << "IpsecProtocol::start";
+
+ static QMutex mutexLocal;
+ mutexLocal.lock();
+
+ setConnectionState(Vpn::ConnectionState::Disconnected);
+ NEVPNManager *manager = [NEVPNManager sharedManager];
+
+ NSString *nsUsername = m_config.value(amnezia::config_key::hostName).toString().toNSString();
+ NSString *nsIp = m_config.value(amnezia::config_key::hostName).toString().toNSString();
+ NSString *nsRemoteId = m_config.value(amnezia::config_key::hostName).toString().toNSString();
+
+ [manager loadFromPreferencesWithCompletionHandler:^(NSError *err)
+ {
+ mutexLocal.lock();
+
+ if (err)
+ {
+ qDebug() << "First load vpn preferences failed:" << QString::fromNSString(err.localizedDescription);
+ setConnectionState(Vpn::ConnectionState::Disconnected);
+ mutexLocal.unlock();
+ }
+ else
+ {
+
+ NSData *output = NULL;
+
+ BIO *ibio, *obio = NULL;
+ BUF_MEM *bptr;
+
+
+ STACK_OF(X509) *certstack = sk_X509_new_null();
+ BIO *p12 = BIO_new(BIO_s_mem());
+
+ EVP_PKEY *pkey;
+ X509 *cert;
+
+ BIO_write(p12, QByteArray::fromBase64(m_config[config_key::cert].toString().toUtf8()),
+ QByteArray::fromBase64(m_config[config_key::cert].toString().toUtf8()).size());
+
+ PKCS12 *pkcs12 = d2i_PKCS12_bio(p12, NULL);
+ PKCS12_parse(pkcs12, m_config[config_key::password].toString().toStdString().c_str(), &pkey, &cert, &certstack);
+
+ // We output everything in PEM
+ obio = BIO_new(BIO_s_mem());
+
+ // TODO: support protecting the private key with a PEM passphrase
+ if (pkey)
+ {
+ PEM_write_bio_PrivateKey(obio, pkey, NULL, NULL, 0, NULL, NULL);
+ }
+
+ if (cert)
+ {
+ PEM_write_bio_X509(obio, cert);
+ }
+
+ if (certstack && sk_X509_num(certstack))
+ {
+ for (int i = 0; i < sk_X509_num(certstack); i++)
+ PEM_write_bio_X509_AUX(obio, sk_X509_value(certstack, i));
+ }
+
+ BIO_get_mem_ptr(obio, &bptr);
+
+ output = [NSData dataWithBytes: bptr->data length: bptr->length];
+
+ NSData *PKCS12Data = [[NSData alloc] initWithBase64EncodedString:m_config[config_key::cert].toString().toNSString() options:0] ;
+
+ CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
+ OSStatus ret = SecPKCS12Import(
+ (__bridge CFDataRef)output,
+ (__bridge CFDictionaryRef)@{(id)kSecImportExportPassphrase:@""},
+ &items);
+
+ if (ret != errSecSuccess) {
+ qDebug() << "import err ret " << ret;
+ }
+
+ NSDictionary *firstItem = [(__bridge_transfer NSArray *)items firstObject];
+ SecIdentityRef identity = (__bridge SecIdentityRef)(firstItem[(__bridge id)kSecImportItemIdentity]);
+
+ NEVPNProtocolIKEv2 *protocol = [[NEVPNProtocolIKEv2 alloc] init];
+ protocol.serverAddress = nsIp;
+ protocol.certificateType = NEVPNIKEv2CertificateTypeRSA;
+
+ protocol.remoteIdentifier = m_config.value(amnezia::config_key::hostName).toString().toNSString();
+
+ protocol.authenticationMethod = NEVPNIKEAuthenticationMethodCertificate;
+ protocol.identityReference = (__bridge NSData *)CreatePersistentRefForIdentity(identity);
+
+ protocol.useExtendedAuthentication = YES;
+ protocol.enablePFS = YES;
+
+ protocol.IKESecurityAssociationParameters.encryptionAlgorithm = NEVPNIKEv2EncryptionAlgorithmAES256;
+ protocol.IKESecurityAssociationParameters.diffieHellmanGroup = NEVPNIKEv2DiffieHellmanGroup19;
+ protocol.IKESecurityAssociationParameters.integrityAlgorithm = NEVPNIKEv2IntegrityAlgorithmSHA256;
+ protocol.IKESecurityAssociationParameters.lifetimeMinutes = 1440;
+
+ protocol.childSecurityAssociationParameters.encryptionAlgorithm = NEVPNIKEv2EncryptionAlgorithmAES256;
+ protocol.childSecurityAssociationParameters.diffieHellmanGroup = NEVPNIKEv2DiffieHellmanGroup19;
+ protocol.childSecurityAssociationParameters.integrityAlgorithm = NEVPNIKEv2IntegrityAlgorithmSHA256;
+ protocol.childSecurityAssociationParameters.lifetimeMinutes = 1440;
+
+ [manager setEnabled:YES];
+ [manager setProtocolConfiguration:(protocol)];
+ [manager setOnDemandEnabled:NO];
+ [manager setLocalizedDescription:@"Amnezia VPN"];
+
+ NSString *strProtocol = [NSString stringWithFormat:@"{Protocol: %@", protocol];
+ qDebug() << QString::fromNSString(strProtocol);
+
+ // do config stuff
+ [manager saveToPreferencesWithCompletionHandler:^(NSError *err)
+ {
+ if (err)
+ {
+ qDebug() << "First save vpn preferences failed:" << QString::fromNSString(err.localizedDescription);
+ setConnectionState(Vpn::ConnectionState::Disconnected);
+ mutexLocal.unlock();
+ }
+ else
+ {
+ // load and save preferences again, otherwise Mac bug (https://forums.developer.apple.com/thread/25928)
+ [manager loadFromPreferencesWithCompletionHandler:^(NSError *err)
+ {
+ if (err)
+ {
+ qDebug() << "Second load vpn preferences failed:" << QString::fromNSString(err.localizedDescription);
+ setConnectionState(Vpn::ConnectionState::Disconnected);
+ mutexLocal.unlock();
+ }
+ else
+ {
+ [manager saveToPreferencesWithCompletionHandler:^(NSError *err)
+ {
+ if (err)
+ {
+ qDebug() << "Second Save vpn preferences failed:" << QString::fromNSString(err.localizedDescription);
+ setConnectionState(Vpn::ConnectionState::Disconnected);
+ mutexLocal.unlock();
+ }
+ else
+ {
+ notificationId_ = [[NSNotificationCenter defaultCenter] addObserverForName: (NSString *)NEVPNStatusDidChangeNotification object: manager.connection queue: nil usingBlock: ^ (NSNotification *notification)
+ {
+ this->handleNotification(notification);
+ }];
+
+ qDebug() << "NEVPNConnection current status:" << (int)manager.connection.status;
+
+ NSError *startError;
+ [manager.connection startVPNTunnelAndReturnError:&startError];
+ if (startError)
+ {
+ qDebug() << "Error starting ikev2 connection:" << QString::fromNSString(startError.localizedDescription);
+ [[NSNotificationCenter defaultCenter] removeObserver: (id)notificationId_ name: (NSString *)NEVPNStatusDidChangeNotification object: manager.connection];
+ setConnectionState(Vpn::ConnectionState::Disconnected);
+ }
+ mutexLocal.unlock();
+ }
+ }];
+ }
+ }];
+ }
+ }];
+ }
+ }];
+
+ // waitConditionLocal.wait(&mutexLocal);
+ mutexLocal.unlock();
+
+ setConnectionState(Vpn::ConnectionState::Connected);
+ return ErrorCode::NoError;
+}
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool Ikev2Protocol::create_new_vpn(const QString & vpn_name,
+ const QString & serv_addr){
+ qDebug() << "Ikev2Protocol::create_new_vpn()";
+ return true;
+}
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool Ikev2Protocol::delete_vpn_connection(const QString &vpn_name){
+
+ return false;
+}
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool Ikev2Protocol::connect_to_vpn(const QString & vpn_name){
+ return false;
+}
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool Ikev2Protocol::disconnect_vpn() {
+
+ QMutexLocker locker(&mutex_);
+
+ NEVPNManager *manager = [NEVPNManager sharedManager];
+
+ // #713: If user had started connecting to IKev2 on Mac and quickly started after this connecting to Wireguard
+
+ // then manager.connection.status doesn't have time to change to NEVPNStatusConnecting
+ // and remains NEVPNStatusDisconnected as it was before connection tries.
+ // Then we should check below isConnectingStateReachedAfterStartingConnection_ flag to be sure that connecting started.
+ // Without this check we will start connecting to the Wireguard when IKEv2 connecting process hasn't finished yet.
+ if (manager.connection.status == NEVPNStatusDisconnected && isConnectingStateReachedAfterStartingConnection_)
+ {
+ [[NSNotificationCenter defaultCenter] removeObserver: (id)notificationId_ name: (NSString *)NEVPNStatusDidChangeNotification object: manager.connection];
+ setConnectionState(Vpn::ConnectionState::Disconnected);
+ }
+ else
+ {
+ [manager.connection stopVPNTunnel];
+ }
+
+ return true;
+}
+
+
+void Ikev2Protocol::closeWindscribeActiveConnection()
+{
+ static QWaitCondition waitCondition;
+ static QMutex mutex;
+
+ mutex.lock();
+
+ NEVPNManager *manager = [NEVPNManager sharedManager];
+ if (manager)
+ {
+ [manager loadFromPreferencesWithCompletionHandler:^(NSError *err)
+ {
+ mutex.lock();
+ if (!err)
+ {
+ NEVPNConnection * connection = [manager connection];
+ if (connection.status == NEVPNStatusConnected || connection.status == NEVPNStatusConnecting)
+ {
+ if ([manager.localizedDescription isEqualToString:@"Amnezia VPN"] == YES)
+ {
+ qDebug() << "Previous IKEv2 connection is active. Stop it.";
+ [connection stopVPNTunnel];
+ }
+ }
+ }
+ waitCondition.wakeAll();
+ mutex.unlock();
+ }];
+ }
+ waitCondition.wait(&mutex);
+ mutex.unlock();
+}
+
+void Ikev2Protocol::handleNotificationImpl(int status)
+{
+ QMutexLocker locker(&mutex_);
+
+ NEVPNManager *manager = [NEVPNManager sharedManager];
+
+ if (status == NEVPNStatusInvalid)
+ {
+ qDebug() << "Connection status changed: NEVPNStatusInvalid";
+ [[NSNotificationCenter defaultCenter] removeObserver: (id)notificationId_ name: (NSString *)NEVPNStatusDidChangeNotification object: manager.connection];
+ setConnectionState(Vpn::ConnectionState::Disconnected);
+
+ }
+ else if (status == NEVPNStatusDisconnected)
+ {
+ qDebug() << "Connection status changed: NEVPNStatusDisconnected";
+
+ if (state_ == STATE_DISCONNECTING_ANY_ERROR)
+ {
+ [[NSNotificationCenter defaultCenter] removeObserver: (id)notificationId_ name: (NSString *)NEVPNStatusDidChangeNotification object: manager.connection];
+ // state_ = STATE_DISCONNECTED;
+ // emit error(IKEV_FAILED_TO_CONNECT);
+ setConnectionState(Vpn::ConnectionState::Disconnected);
+ }
+ else if (state_ != STATE_DISCONNECTED)
+ {
+
+ [[NSNotificationCenter defaultCenter] removeObserver: (id)notificationId_ name: (NSString *)NEVPNStatusDidChangeNotification object: manager.connection];
+ // state_ = STATE_DISCONNECTED;
+ setConnectionState(Vpn::ConnectionState::Disconnected);
+ }
+ }
+ else if (status == NEVPNStatusConnecting)
+ {
+ isConnectingStateReachedAfterStartingConnection_ = true;
+ qDebug() << "Connection status changed: NEVPNStatusConnecting";
+ }
+ else if (status == NEVPNStatusConnected)
+ {
+ if (!overrideDnsIp_.isEmpty()) {
+ if (!setCustomDns(overrideDnsIp_)) {
+ qDebug() << "Failed to set custom DNS ip for ikev2";
+ }
+ }
+
+ qDebug() << "Connection status changed: NEVPNStatusConnected";
+
+ setConnectionState(Vpn::ConnectionState::Connected);
+ // note: route gateway not used for ikev2 in AdapterGatewayInfo
+ // AdapterGatewayInfo cai;
+ // ipsecAdapterName_ = NetworkUtils_mac::lastConnectedNetworkInterfaceName();
+ // cai.setAdapterName(ipsecAdapterName_);
+ // cai.setAdapterIp(NetworkUtils_mac::ipAddressByInterfaceName(ipsecAdapterName_));
+ //cai.setDnsServers(NetworkUtils_mac::getDnsServersForInterface(ipsecAdapterName_));
+ }
+ else if (status == NEVPNStatusReasserting)
+ {
+ qDebug() << "Connection status changed: NEVPNStatusReasserting";
+ setConnectionState(Vpn::ConnectionState::Connecting);
+ }
+ else if (status == NEVPNStatusDisconnecting)
+ {
+ qDebug() << "Connection status changed: NEVPNStatusDisconnecting";
+ setConnectionState(Vpn::ConnectionState::Disconnecting);
+ /* if (state_ == STATE_START_CONNECT)
+ {
+ QMap logs = networkExtensionLog_.collectNext();
+ for (QMap::iterator it = logs.begin(); it != logs.end(); ++it)
+ {
+ qDebug() << it.value();
+ }
+ if (isSocketError(logs))
+ {
+ state_ = STATE_DISCONNECTING_ANY_ERROR;
+ }
+ else
+ {
+ if (isFailedAuthError(logs))
+ {
+ state_ = STATE_DISCONNECTING_AUTH_ERROR;
+ }
+ else
+ {
+ state_ = STATE_DISCONNECTING_ANY_ERROR;
+ }
+ }
+ }*/
+ }
+
+ prevConnectionStatus_ = status;
+ isPrevConnectionStatusInitialized_ = true;
+}
+
+
+void Ikev2Protocol::handleNotification(void *notification)
+{
+ QMutexLocker locker(&mutex_);
+ NSNotification *nsNotification = (NSNotification *)notification;
+ NEVPNConnection *connection = nsNotification.object;
+ QMetaObject::invokeMethod(this, "handleNotificationImpl", Q_ARG(int, (int)connection.status));
+}
+
+bool Ikev2Protocol::isFailedAuthError(QMap &logs)
+{
+ for (QMap::iterator it = logs.begin(); it != logs.end(); ++it)
+ {
+ if (it.value().contains("Failed", Qt::CaseInsensitive) && it.value().contains("IKE", Qt::CaseInsensitive) && it.value().contains("Auth", Qt::CaseInsensitive))
+ {
+ if (!(it.value().contains("Failed", Qt::CaseInsensitive) && it.value().contains("IKEv2 socket", Qt::CaseInsensitive)))
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool Ikev2Protocol::isSocketError(QMap &logs)
+{
+ for (QMap::iterator it = logs.begin(); it != logs.end(); ++it)
+ {
+ if (it.value().contains("Failed", Qt::CaseInsensitive) && it.value().contains("initialize", Qt::CaseInsensitive) && it.value().contains("socket", Qt::CaseInsensitive))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool Ikev2Protocol::setCustomDns(const QString &overrideDnsIpAddress)
+{
+ // get list of entries of interest
+ // QStringList networkServices = NetworkUtils_mac::getListOfDnsNetworkServiceEntries();
+
+ // filter list to only ikev2 entries
+ QStringList dnsNetworkServices;
+ // for (const QString &service : networkServices)
+ // if (MacUtils::dynamicStoreEntryHasKey(service, "ConfirmedServiceID"))
+ // dnsNetworkServices.append(service);
+
+ qDebug() << "Applying custom 'while connected' DNS change to network services: " << dnsNetworkServices;
+
+ if (dnsNetworkServices.isEmpty()) {
+ qDebug() << "No network services to configure 'while connected' DNS";
+ return false;
+ }
+
+ // change DNS on each entry
+ bool successAll = true;
+ for (const QString &service : dnsNetworkServices) {
+ // if (!helper_->setDnsOfDynamicStoreEntry(overrideDnsIpAddress, service)) {
+ // successAll = false;
+ // qDebug() << "Failed to set network service DNS: " << service;
+ // break;
+ // }
+ }
+
+ return successAll;
+}
+
+
diff --git a/client/protocols/vpnprotocol.cpp b/client/protocols/vpnprotocol.cpp
index 40b22dca..7524b483 100644
--- a/client/protocols/vpnprotocol.cpp
+++ b/client/protocols/vpnprotocol.cpp
@@ -17,7 +17,11 @@
#endif
#ifdef Q_OS_LINUX
-#include "ikev2_vpn_protocol_linux.h"
+ #include "ikev2_vpn_protocol_linux.h"
+#endif
+
+#ifdef Q_OS_MACX
+ #include "ikev2_vpn_protocol_mac.h"
#endif
VpnProtocol::VpnProtocol(const QJsonObject &configuration, QObject *parent)
@@ -110,9 +114,6 @@ QString VpnProtocol::vpnGateway() const
VpnProtocol *VpnProtocol::factory(DockerContainer container, const QJsonObject &configuration)
{
switch (container) {
-#if defined(Q_OS_WINDOWS) || defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
- case DockerContainer::Ipsec: return new Ikev2Protocol(configuration);
-#endif
#if defined(Q_OS_WINDOWS) || defined(Q_OS_MACX) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
case DockerContainer::OpenVpn: return new OpenVpnProtocol(configuration);
case DockerContainer::Cloak: return new OpenVpnOverCloakProtocol(configuration);
@@ -121,6 +122,7 @@ VpnProtocol *VpnProtocol::factory(DockerContainer container, const QJsonObject &
case DockerContainer::Awg: return new WireguardProtocol(configuration);
case DockerContainer::Xray: return new XrayProtocol(configuration);
case DockerContainer::SSXray: return new XrayProtocol(configuration);
+ case DockerContainer::Ipsec: return new Ikev2Protocol(configuration);
#endif
default: return nullptr;
}