diff --git a/client/vpnConnection.cpp b/client/vpnConnection.cpp index 41cf7ea6c..58ea4d181 100644 --- a/client/vpnConnection.cpp +++ b/client/vpnConnection.cpp @@ -125,13 +125,29 @@ Vpn::ConnectionState VpnConnection::connectionState() const QString VpnConnection::allocateIfname() { +#ifdef Q_OS_MACOS + QString kernelAssigned; + IpcClient::withInterface([&kernelAssigned](QSharedPointer iface) { + auto reply = iface->reserveUtunName(); + if (reply.waitForFinished(2000)) { + kernelAssigned = reply.returnValue(); + } + }); + if (kernelAssigned.isEmpty() || m_ifnamesInUse.contains(kernelAssigned)) { + qCritical() << "allocateIfname: kernel utun reservation failed"; + return QString(); + } + m_ifnamesInUse.insert(kernelAssigned); + return kernelAssigned; +#else for (int i = 0; ; ++i) { - const QString name = QStringLiteral("amn") + QString::number(i); + const QString name = QStringLiteral("amn%1").arg(i); if (!m_ifnamesInUse.contains(name)) { m_ifnamesInUse.insert(name); return name; } } +#endif } void VpnConnection::releaseIfname(const QString& ifname) @@ -171,6 +187,11 @@ void VpnConnection::connectToVpn(const QString &serverId, DockerContainer contai const bool isXray = VpnProtocol::isXrayBased(container); const bool useTunnelPath = isWg || isXray; const QString preAllocatedIfname = useTunnelPath ? allocateIfname() : QString(); + if (useTunnelPath && preAllocatedIfname.isEmpty()) { + setConnectionState(Vpn::ConnectionState::Error); + emit vpnProtocolError(ErrorCode::AmneziaServiceConnectionFailed); + return; + } if (m_active && m_connectionState == Vpn::ConnectionState::Connected diff --git a/ipc/ipc_interface.rep b/ipc/ipc_interface.rep index 46edde152..e686f6221 100644 --- a/ipc/ipc_interface.rep +++ b/ipc/ipc_interface.rep @@ -31,6 +31,8 @@ class IpcInterface SLOT( bool createTun(const QString &dev, const QString &subnet) ); SLOT( bool deleteTun(const QString &dev) ); + SLOT( QString reserveUtunName() ); + SLOT( bool applyAdapterAddress(const QString &ifname, const QString &ipv4, const QString &ipv6) ); SLOT( bool removeAdapterAddress(const QString &ifname, const QString &ipv4, const QString &ipv6) ); diff --git a/ipc/ipcserver.cpp b/ipc/ipcserver.cpp index c7ad04726..f8a137ce5 100644 --- a/ipc/ipcserver.cpp +++ b/ipc/ipcserver.cpp @@ -35,6 +35,17 @@ #include "tapcontroller_win.h" #endif +#ifdef Q_OS_MAC + #include + #include + #include + #include + #include + #include + #include + #include +#endif + IpcServer::IpcServer(QObject *parent) : IpcInterfaceSource(parent) { @@ -215,6 +226,53 @@ bool IpcServer::deleteTun(const QString &dev) return Router::deleteTun(dev); } +QString IpcServer::reserveUtunName() +{ +#ifdef Q_OS_MAC + int fd = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL); + if (fd < 0) { + qWarning() << "reserveUtunName: socket() failed:" << strerror(errno); + return QString(); + } + + struct ctl_info info; + std::memset(&info, 0, sizeof(info)); + std::strncpy(info.ctl_name, UTUN_CONTROL_NAME, sizeof(info.ctl_name) - 1); + if (ioctl(fd, CTLIOCGINFO, &info) < 0) { + qWarning() << "reserveUtunName: CTLIOCGINFO failed:" << strerror(errno); + ::close(fd); + return QString(); + } + + struct sockaddr_ctl addr; + std::memset(&addr, 0, sizeof(addr)); + addr.sc_len = sizeof(addr); + addr.sc_family = AF_SYSTEM; + addr.ss_sysaddr = AF_SYS_CONTROL; + addr.sc_id = info.ctl_id; + addr.sc_unit = 0; + + if (::connect(fd, reinterpret_cast(&addr), sizeof(addr)) < 0) { + qWarning() << "reserveUtunName: connect() failed:" << strerror(errno); + ::close(fd); + return QString(); + } + + char ifname[IFNAMSIZ] = {0}; + socklen_t len = sizeof(ifname); + if (getsockopt(fd, SYSPROTO_CONTROL, UTUN_OPT_IFNAME, ifname, &len) < 0) { + qWarning() << "reserveUtunName: getsockopt UTUN_OPT_IFNAME failed:" << strerror(errno); + ::close(fd); + return QString(); + } + + ::close(fd); + return QString::fromUtf8(ifname); +#else + return QString(); +#endif +} + bool IpcServer::applyAdapterAddress(const QString &ifname, const QString &ipv4, const QString &ipv6) { #ifdef Q_OS_WIN diff --git a/ipc/ipcserver.h b/ipc/ipcserver.h index 1d7b0aae8..87c4439b5 100644 --- a/ipc/ipcserver.h +++ b/ipc/ipcserver.h @@ -43,6 +43,7 @@ public: virtual void setLogsEnabled(bool enabled) override; virtual bool createTun(const QString &dev, const QString &subnet) override; virtual bool deleteTun(const QString &dev) override; + virtual QString reserveUtunName() override; virtual bool applyAdapterAddress(const QString &ifname, const QString &ipv4, const QString &ipv6) override; virtual bool removeAdapterAddress(const QString &ifname, const QString &ipv4, const QString &ipv6) override; virtual bool StartRoutingIpv6() override;