From b8629032d6b24f54d95c683445c0691db798cc64 Mon Sep 17 00:00:00 2001 From: cd-amn Date: Tue, 7 Apr 2026 23:52:06 +0400 Subject: [PATCH] fix: outbound freedom for xray on linux --- client/platforms/linux/daemon/linuxfirewall.cpp | 6 ++++++ service/server/killswitch.cpp | 7 +++++++ service/server/xray.cpp | 2 ++ service/server/xray_defs.h | 11 +++++++++++ 4 files changed, 26 insertions(+) create mode 100644 service/server/xray_defs.h diff --git a/client/platforms/linux/daemon/linuxfirewall.cpp b/client/platforms/linux/daemon/linuxfirewall.cpp index de88c9625..c04770f1a 100644 --- a/client/platforms/linux/daemon/linuxfirewall.cpp +++ b/client/platforms/linux/daemon/linuxfirewall.cpp @@ -32,6 +32,7 @@ #include "linuxfirewall.h" #include "logger.h" +#include "xray_defs.h" #include #define BRAND_CODE "amn" @@ -282,6 +283,10 @@ void LinuxFirewall::install() QStringLiteral("-o tun2+ -j ACCEPT"), }); + installAnchor(Both, QStringLiteral("130.allowMarkedXray"), { + QStringLiteral("-m mark --mark %1 -j ACCEPT").arg(amnezia::xray::xrayTrafficMark), + }); + installAnchor(IPv4, QStringLiteral("120.blockNets"), {}); installAnchor(IPv4, QStringLiteral("110.allowNets"), {}); @@ -358,6 +363,7 @@ void LinuxFirewall::uninstall() uninstallAnchor(IPv6, QStringLiteral("250.blockIPv6")); uninstallAnchor(Both, QStringLiteral("200.allowVPN")); uninstallAnchor(IPv4, QStringLiteral("120.blockNets")); + uninstallAnchor(Both, QStringLiteral("130.allowMarkedXray")); uninstallAnchor(IPv4, QStringLiteral("110.allowNets")); uninstallAnchor(Both, QStringLiteral("100.blockAll")); diff --git a/service/server/killswitch.cpp b/service/server/killswitch.cpp index a0e5c7e8a..a2799d59f 100644 --- a/service/server/killswitch.cpp +++ b/service/server/killswitch.cpp @@ -81,6 +81,7 @@ bool KillSwitch::disableKillSwitch() { LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("100.blockAll"), true); LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("110.allowNets"), false); LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("120.blockNets"), false); + LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("130.allowMarkedXray"), false); LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("200.allowVPN"), false); LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv6, QStringLiteral("250.blockIPv6"), true); LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("290.allowDHCP"), false); @@ -93,6 +94,7 @@ bool KillSwitch::disableKillSwitch() { LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("100.blockAll"), false); LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("110.allowNets"), false); LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("120.blockNets"), false); + LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("130.allowMarkedXray"), false); LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("200.allowVPN"), false); LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv6, QStringLiteral("250.blockIPv6"), false); LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("290.allowDHCP"), true); @@ -140,6 +142,7 @@ bool KillSwitch::disableAllTraffic() { LinuxFirewall::install(); } LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("100.blockAll"), true); + LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("130.allowMarkedXray"), false); LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("000.allowLoopback"), true); LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv6, QStringLiteral("250.blockIPv6"), true); #endif @@ -276,15 +279,18 @@ bool KillSwitch::enableKillSwitch(const QJsonObject &configStr, int vpnAdapterIn bool blockAll = 0; bool allowNets = 0; bool blockNets = 0; + bool allowMarkedXray = 0; QStringList allownets; QStringList blocknets; if (splitTunnelType == 0) { blockAll = true; allowNets = true; + allowMarkedXray = true; allownets.append(configStr.value("vpnServer").toString()); } else if (splitTunnelType == 1) { blockNets = true; + allowMarkedXray = true; for (auto v : splitTunnelSites) { blocknets.append(v.toString()); } @@ -310,6 +316,7 @@ bool KillSwitch::enableKillSwitch(const QJsonObject &configStr, int vpnAdapterIn LinuxFirewall::updateAllowNets(allownets); LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("120.blockNets"), blockAll); LinuxFirewall::updateBlockNets(blocknets); + LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("130.allowMarkedXray"), true); LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("200.allowVPN"), true); LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv6, QStringLiteral("250.blockIPv6"), true); LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("290.allowDHCP"), true); diff --git a/service/server/xray.cpp b/service/server/xray.cpp index b0408d49a..487a46435 100644 --- a/service/server/xray.cpp +++ b/service/server/xray.cpp @@ -25,6 +25,7 @@ #endif #ifdef Q_OS_LINUX #include + #include "xray_defs.h" #endif bool Xray::startXray(const QString &cfg) @@ -99,6 +100,7 @@ void Xray::sockCallback(uintptr_t fd) #ifdef Q_OS_LINUX if (!m_defaultIfaceName.isEmpty()) { setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, m_defaultIfaceName.data(), m_defaultIfaceName.size()); + setsockopt(fd, SOL_SOCKET, SO_MARK, &amnezia::xray::xrayTrafficMark, sizeof(amnezia::xray::xrayTrafficMark)); } #endif } diff --git a/service/server/xray_defs.h b/service/server/xray_defs.h new file mode 100644 index 000000000..76864d93e --- /dev/null +++ b/service/server/xray_defs.h @@ -0,0 +1,11 @@ +#ifndef XRAY_DEFS_H +#define XRAY_DEFS_H + +namespace amnezia +{ + namespace xray + { + constexpr unsigned int xrayTrafficMark = 0x82; + } +} +#endif // XRAY_DEFS_H \ No newline at end of file