Compare commits

...

12 Commits

Author SHA1 Message Date
2dust 92c3df45ae up 7.24.3 2026-07-26 21:25:31 +08:00
hyjhyj111 ee7e21268a Fix TUN core elevation: decide sudo from the config context snapshot (#9830)
CoreManager.RunProcess decided sudo elevation from the live mutable
_config.TunModeItem.EnableTun while the launched config was generated
from the immutable CoreConfigContext snapshot. If the TUN state changed
while a reload was in flight, a core whose config contains a TUN
inbound could launch without elevation and die within the 100ms health
check ("Failed to run core"), with no stderr shown.

The elevation decision now follows context.IsTunEnabled /
preContext.IsTunEnabled, so the generated config and the launch mode
always agree. With legacy TUN protect, the sing-box pre-core hosting
TUN is elevated and the main core no longer runs as root needlessly.
2026-07-26 21:24:11 +08:00
2dust dc216c2b02 Rename TunIp to TunIP 2026-07-26 21:07:36 +08:00
2dust beffa919d3 Use /126 prefixes for TUN IPv6 addresses
https://github.com/2dust/v2rayN/pull/9830
2026-07-26 20:46:32 +08:00
2dust a654826231 Enable legacy TUN protect by default 2026-07-26 20:46:01 +08:00
2dust b01476d147 Fix tun inbound dns setting 2026-07-26 10:29:35 +08:00
2dust cf8dd45abe up 7.24.2 2026-07-24 16:57:53 +08:00
2dust 9bba6f53f8 Update Directory.Packages.props 2026-07-24 16:57:22 +08:00
2dust cd77f1d882 Code clean 2026-07-24 16:57:19 +08:00
mymyme 0427037638 fix: sanitize system proxy exceptions to avoid macOS ExceptionsList b… (#9815)
* fix: sanitize system proxy exceptions to avoid macOS ExceptionsList being dropped

On macOS/Linux the exception list is comma-separated and passed to networksetup. It was only stripped of spaces, so a stray newline or a trailing comma left a malformed/empty entry. macOS configd then silently rejects the whole ExceptionsList (scutil --proxy shows no ExceptionsList), so the bypass never takes effect. Split on ',' with TrimEntries + RemoveEmptyEntries and rejoin; behavior-preserving for well-formed input. Windows path (';') is left unchanged.

Note: not built locally (no .NET SDK on hand); relying on CI.

* Update SysProxyHandler.cs

---------

Co-authored-by: 2dust <31833384+2dust@users.noreply.github.com>
2026-07-24 14:12:10 +08:00
2dust e749b81ecf Add configurable TUN IPv4/IPv6 addresses
https://github.com/2dust/v2rayN/issues/9810
2026-07-24 10:13:08 +08:00
JieXu fd2c942231 Update package-rhel-loong.sh (#9792)
* Update package-rhel-loong.sh

* Update package-debian-loong.sh

* Update Directory.Packages.props
2026-07-20 09:30:52 +08:00
28 changed files with 321 additions and 72 deletions
+2 -2
View File
@@ -13,8 +13,8 @@ PKGROOT="v2rayN-publish"
PROJECT_HINT="v2rayN.Desktop/v2rayN.Desktop.csproj"
OUTPUT_DIR="${HOME}/debbuild"
DOTNET_TFM="net10.0"
DOTNET_LOONGARCH_VERSION="10.0.109"
DOTNET_LOONGARCH_TAG="v10.0.109-loongarch64"
DOTNET_LOONGARCH_VERSION="10.0.110"
DOTNET_LOONGARCH_TAG="v10.0.110-loongarch64"
DOTNET_LOONGARCH_BASE="https://github.com/loongson/dotnet/releases/download"
DOTNET_LOONGARCH_FILE="dotnet-sdk-${DOTNET_LOONGARCH_VERSION}-linux-loongarch64.tar.gz"
DOTNET_SDK_URL="${DOTNET_LOONGARCH_BASE}/${DOTNET_LOONGARCH_TAG}/${DOTNET_LOONGARCH_FILE}"
+2 -2
View File
@@ -12,8 +12,8 @@ MIN_KERNEL="6.12"
PKGROOT="v2rayN-publish"
PROJECT_HINT="v2rayN.Desktop/v2rayN.Desktop.csproj"
RPM_TOPDIR="${HOME}/rpmbuild"
DOTNET_LOONGARCH_VERSION="10.0.109"
DOTNET_LOONGARCH_TAG="v10.0.109-loongarch64"
DOTNET_LOONGARCH_VERSION="10.0.110"
DOTNET_LOONGARCH_TAG="v10.0.110-loongarch64"
DOTNET_LOONGARCH_BASE="https://github.com/loongson/dotnet/releases/download"
DOTNET_LOONGARCH_FILE="dotnet-sdk-${DOTNET_LOONGARCH_VERSION}-linux-loongarch64.tar.gz"
DOTNET_SDK_URL="${DOTNET_LOONGARCH_BASE}/${DOTNET_LOONGARCH_TAG}/${DOTNET_LOONGARCH_FILE}"
+1 -1
View File
@@ -1,7 +1,7 @@
<Project>
<PropertyGroup>
<Version>7.24.1</Version>
<Version>7.24.3</Version>
</PropertyGroup>
<PropertyGroup>
+3 -3
View File
@@ -9,12 +9,12 @@
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="12.1.0" />
<PackageVersion Include="Avalonia.Desktop" Version="12.1.0" />
<PackageVersion Include="AvaloniaUI.DiagnosticsSupport" Version="2.2.3" />
<PackageVersion Include="AwesomeAssertions" Version="9.4.0" />
<PackageVersion Include="AwesomeAssertions" Version="9.5.0" />
<PackageVersion Include="DialogHost.Avalonia" Version="0.12.3" />
<PackageVersion Include="IPNetwork2" Version="4.3.0" />
<PackageVersion Include="ReactiveUI.Avalonia" Version="12.0.3" />
<PackageVersion Include="CliWrap" Version="3.10.2" />
<PackageVersion Include="Downloader" Version="5.9.4" />
<PackageVersion Include="Downloader" Version="5.9.5" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.8.1" />
<PackageVersion Include="H.NotifyIcon.Wpf" Version="2.4.1" />
<PackageVersion Include="MaterialDesignThemes" Version="5.3.2" />
@@ -27,7 +27,7 @@
<PackageVersion Include="Semi.Avalonia.DataGrid" Version="12.1.0" />
<PackageVersion Include="NLog" Version="6.1.4" />
<PackageVersion Include="sqlite-net-e" Version="1.11.285" />
<PackageVersion Include="Repobot.SQLite.Unofficial" Version="3.53.3" />
<PackageVersion Include="Repobot.SQLite.Unofficial" Version="3.53.3.10" />
<PackageVersion Include="TaskScheduler" Version="2.12.2" />
<PackageVersion Include="WebDav.Client" Version="2.9.0" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
@@ -592,4 +592,38 @@ public class CoreConfigSingboxServiceTests
proxy.realm.stun_servers.Should().Contain("turn.cloudflare.com:3478");
proxy.server.Should().BeNull();
}
[Fact]
public void GenerateClientConfigContent_TunSystemStackWithIpv6_ShouldUsePrefixWithPeerAddress()
{
// Regression test for #9820: sing-box fails with "need one more IPv6 address in
// first prefix for system stack" when the TUN inbound uses a /128 IPv6 prefix.
var config = CoreConfigTestFactory.CreateConfig(ECoreType.sing_box);
config.TunModeItem.EnableTun = true;
config.TunModeItem.Stack = "system";
config.TunModeItem.EnableIPv6Address = true;
CoreConfigTestFactory.BindAppManagerConfig(config);
var node = CoreConfigTestFactory.CreateVmessNode(ECoreType.sing_box);
var context = CoreConfigTestFactory.CreateContext(config, node, ECoreType.sing_box) with
{
IsTunEnabled = true,
};
var result = new CoreConfigSingboxService(context).GenerateClientConfigContent();
result.Success.Should().BeTrue($"ret msg: {result.Msg}");
var cfg = JsonUtils.Deserialize<SingboxConfig>(result.Data!.ToString())!;
var tun = cfg.inbounds.First(i => i.type == "tun");
tun.address.Should().NotBeNullOrEmpty();
foreach (var address in tun.address!)
{
var prefixLength = int.Parse(address[(address.LastIndexOf('/') + 1)..]);
var isIpv6 = address.Contains(':');
prefixLength.Should().BeLessThanOrEqualTo(isIpv6 ? 126 : 30,
$"'{address}' must leave room for the peer address the system stack derives");
}
}
}
@@ -0,0 +1,44 @@
using AwesomeAssertions;
using ServiceLib.Enums;
using ServiceLib.Manager;
using Xunit;
namespace ServiceLib.Tests.Manager;
public class CoreManagerTests
{
[Theory]
[InlineData(ECoreType.sing_box)]
[InlineData(ECoreType.mihomo)]
[InlineData(ECoreType.Xray)]
public void ShouldRunAsSudo_TunLaunchOnNonWindows_RequiresElevation(ECoreType coreType)
{
CoreManager.ShouldRunAsSudo(isTunLaunch: true, coreType, isNonWindows: true).Should().BeTrue();
}
[Fact]
public void ShouldRunAsSudo_NonTunLaunch_ShouldNotElevate()
{
// Regression guard for the macOS TUN failure: the elevation decision must follow
// the context snapshot that generated the config. A launch whose snapshot has TUN
// disabled must never elevate, and a launch whose snapshot has TUN enabled must
// elevate regardless of later changes to the live config.
CoreManager.ShouldRunAsSudo(isTunLaunch: false, ECoreType.sing_box, isNonWindows: true).Should().BeFalse();
CoreManager.ShouldRunAsSudo(isTunLaunch: false, ECoreType.Xray, isNonWindows: true).Should().BeFalse();
}
[Fact]
public void ShouldRunAsSudo_OnWindows_ShouldNotElevate()
{
CoreManager.ShouldRunAsSudo(isTunLaunch: true, ECoreType.sing_box, isNonWindows: false).Should().BeFalse();
}
[Theory]
[InlineData(ECoreType.v2fly)]
[InlineData(ECoreType.hysteria)]
[InlineData(null)]
public void ShouldRunAsSudo_UnsupportedCoreType_ShouldNotElevate(ECoreType? coreType)
{
CoreManager.ShouldRunAsSudo(isTunLaunch: true, coreType, isNonWindows: true).Should().BeFalse();
}
}
+25 -5
View File
@@ -2,8 +2,6 @@ namespace ServiceLib;
public class Global
{
#region const
public const string AppName = "v2rayN";
public const string GithubUrl = "https://github.com";
public const string GithubApiUrl = "https://api.github.com/repos";
@@ -106,9 +104,7 @@ public class Global
public const string SingboxFakeDNSTag = "fake_dns";
public const int Hysteria2DefaultHopInt = 30;
public const string PolicyGroupExcludeKeywords = @"剩余|过期|到期|重置|[Rr]emaining|[Ee]xpir|[Rr]eset";
public const string PolicyGroupDefaultAllFilter = $"^(?!.*(?:{PolicyGroupExcludeKeywords})).*$";
public static readonly List<string> PolicyGroupDefaultFilterList =
@@ -749,5 +745,29 @@ public class Global
MozillaRootProvider,
];
#endregion const
public static readonly IReadOnlyList<string> TunIPv4Address =
[
"172.18.0.1/30",
"172.31.0.1/30",
"172.20.0.1/30",
"172.16.0.1/30",
"192.168.100.1/30",
"10.10.14.1/30",
"10.1.0.1/30",
"10.0.0.1/30",
];
// Prefixes must leave room for a peer address (max /126); the sing-box system
// stack derives a gateway from the first prefix and rejects single-address prefixes.
public static readonly IReadOnlyList<string> TunIPv6Address =
[
"fc00::172:18:0:1/126",
"fc00::172:31:0:1/126",
"fc00::172:20:0:1/126",
"fc00::172:16:0:1/126",
"fc00::192:168:100:1/126",
"fc00::10:10:14:1/126",
"fc00::10:1:0:1/126",
"fc00::10:0:0:1/126",
];
}
+1 -1
View File
@@ -30,8 +30,8 @@ global using ServiceLib.Handler.Fmt;
global using ServiceLib.Handler.SysProxy;
global using ServiceLib.Helper;
global using ServiceLib.Manager;
global using ServiceLib.Models.CoreConfigs;
global using ServiceLib.Models.Configs;
global using ServiceLib.Models.CoreConfigs;
global using ServiceLib.Models.Dto;
global using ServiceLib.Models.Entities;
global using ServiceLib.Resx;
+1 -1
View File
@@ -92,7 +92,7 @@ public static class ConfigHandler
EnableTun = false,
Mtu = 9000,
IcmpRouting = Global.TunIcmpRoutingPolicies.First(),
EnableLegacyProtect = false,
EnableLegacyProtect = true,
};
config.GuiItem ??= new();
if (!Global.RootCertProviders.Contains(config.GuiItem.RootCertProvider))
@@ -16,7 +16,6 @@ public static class SysProxyHandler
try
{
var port = AppManager.Instance.GetLocalPort(EInboundProtocol.socks);
var exceptions = config.SystemProxyItem.SystemProxyExceptions.Replace(" ", "");
if (port <= 0)
{
return false;
@@ -24,17 +23,18 @@ public static class SysProxyHandler
switch (type)
{
case ESysProxyType.ForcedChange when Utils.IsWindows():
{
GetWindowsProxyString(config, port, out var strProxy, out var strExceptions);
ProxySettingWindows.SetProxy(strProxy, strExceptions, 2);
break;
}
var (strProxy, strExceptions) = GetWindowsProxyString(config, port);
ProxySettingWindows.SetProxy(strProxy, strExceptions, 2);
break;
case ESysProxyType.ForcedChange when Utils.IsLinux():
var exceptions = SanitizeExceptions(config);
await ProxySettingLinux.SetProxy(Global.Loopback, port, exceptions);
break;
case ESysProxyType.ForcedChange when Utils.IsMacOS():
await ProxySettingOSX.SetProxy(Global.Loopback, port, exceptions);
var exceptions2 = SanitizeExceptions(config);
await ProxySettingOSX.SetProxy(Global.Loopback, port, exceptions2);
break;
case ESysProxyType.ForcedClear when Utils.IsWindows():
@@ -66,15 +66,31 @@ public static class SysProxyHandler
return true;
}
private static void GetWindowsProxyString(Config config, int port, out string strProxy, out string strExceptions)
private static string SanitizeExceptions(Config config)
{
strExceptions = config.SystemProxyItem.SystemProxyExceptions.Replace(" ", "");
var exceptions = config.SystemProxyItem.SystemProxyExceptions;
if (exceptions.IsNullOrEmpty())
{
return string.Empty;
}
var items = exceptions
.Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries)
.Select(item => item.Replace(" ", string.Empty))
.Where(item => item.Length > 0);
return string.Join(',', items);
}
private static (string strProxy, string strExceptions) GetWindowsProxyString(Config config, int port)
{
var strExceptions = config.SystemProxyItem.SystemProxyExceptions.Replace(" ", "");
if (config.SystemProxyItem.NotProxyLocalAddress)
{
strExceptions = $"<local>;{strExceptions}";
}
strProxy = string.Empty;
var strProxy = string.Empty;
if (config.SystemProxyItem.SystemProxyAdvancedProtocol.IsNullOrEmpty())
{
strProxy = $"{Global.Loopback}:{port}";
@@ -86,6 +102,8 @@ public static class SysProxyHandler
.Replace("{http_port}", port.ToString())
.Replace("{socks_port}", port.ToString());
}
return (strProxy, strExceptions);
}
[SupportedOSPlatform("windows")]
+17 -6
View File
@@ -183,7 +183,7 @@ public class CoreManager
var coreInfo = CoreInfoManager.Instance.GetCoreInfo(coreType);
var displayLog = node.ConfigType != EConfigType.Custom || node.DisplayLog;
var proc = await RunProcess(coreInfo, Global.CoreConfigFileName, displayLog, true);
var proc = await RunProcess(coreInfo, Global.CoreConfigFileName, displayLog, true, context.IsTunEnabled);
if (proc is null)
{
return;
@@ -201,7 +201,7 @@ public class CoreManager
if (result.Success)
{
var coreInfo = CoreInfoManager.Instance.GetCoreInfo(preCoreType);
var proc = await RunProcess(coreInfo, Global.CorePreConfigFileName, true, true);
var proc = await RunProcess(coreInfo, Global.CorePreConfigFileName, true, true, preContext.IsTunEnabled);
if (proc is null)
{
return;
@@ -289,7 +289,20 @@ public class CoreManager
#region Process
private async Task<ProcessService?> RunProcess(CoreInfo? coreInfo, string configPath, bool displayLog, bool mayNeedSudo)
/// <summary>
/// Decides whether a core launch must be elevated on non-Windows platforms.
/// The TUN state comes from the immutable <see cref="CoreConfigContext" /> snapshot that
/// generated the config, never from the live mutable config: the generated config and the
/// launch mode must always agree, even if TUN is toggled while a reload is in flight.
/// </summary>
public static bool ShouldRunAsSudo(bool isTunLaunch, ECoreType? coreType, bool isNonWindows)
{
return isTunLaunch
&& coreType is ECoreType.sing_box or ECoreType.mihomo or ECoreType.Xray
&& isNonWindows;
}
private async Task<ProcessService?> RunProcess(CoreInfo? coreInfo, string configPath, bool displayLog, bool mayNeedSudo, bool isTunLaunch = false)
{
var fileName = CoreInfoManager.Instance.GetCoreExecFile(coreInfo, out var msg);
if (fileName.IsNullOrEmpty())
@@ -301,9 +314,7 @@ public class CoreManager
try
{
if (mayNeedSudo
&& _config.TunModeItem.EnableTun
&& (coreInfo.CoreType is ECoreType.sing_box or ECoreType.mihomo or ECoreType.Xray)
&& Utils.IsNonWindows())
&& ShouldRunAsSudo(isTunLaunch, coreInfo.CoreType, Utils.IsNonWindows()))
{
_linuxSudo = true;
await CoreAdminManager.Instance.Init(_config, _updateFunc);
@@ -147,8 +147,10 @@ public class TunModeItem
public int Mtu { get; set; }
public bool EnableIPv6Address { get; set; }
public string IcmpRouting { get; set; }
public bool EnableLegacyProtect { get; set; }
public bool EnableLegacyProtect { get; set; } = true;
public List<string>? RouteExcludeAddress { get; set; }
public string IPv4Address { get; set; }
public string IPv6Address { get; set; }
}
[Serializable]
@@ -255,6 +257,7 @@ public class Fragment4RayItem
// For migration from old version, remove those properties in the future
public string? Length { get; set; }
public string? Interval { get; set; }
// migration end
}
@@ -560,6 +560,7 @@ public class Sockopt4Ray
[JsonPropertyName("interface")]
public string? Interface { get; set; }
public HappyEyeballs4Ray? happyEyeballs { get; set; }
}
+18
View File
@@ -3348,6 +3348,24 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 Ipv4 Address 的本地化字符串。
/// </summary>
public static string TbIpv4Address {
get {
return ResourceManager.GetString("TbIpv4Address", resourceCulture);
}
}
/// <summary>
/// 查找类似 Ipv6 Address 的本地化字符串。
/// </summary>
public static string TbIpv6Address {
get {
return ResourceManager.GetString("TbIpv6Address", resourceCulture);
}
}
/// <summary>
/// 查找类似 Most Stable 的本地化字符串。
/// </summary>
+6
View File
@@ -1842,4 +1842,10 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="TbEnableHappyEyeballsTip" xml:space="preserve">
<value>Requires the UseIP Strategy. When enabled, it attempts IPv4 and IPv6 connections simultaneously and automatically selects the faster available path.</value>
</data>
<data name="TbIpv6Address" xml:space="preserve">
<value>Ipv6 Address</value>
</data>
<data name="TbIpv4Address" xml:space="preserve">
<value>Ipv4 Address</value>
</data>
</root>
@@ -1839,4 +1839,10 @@
<data name="TbEnableHappyEyeballsTip" xml:space="preserve">
<value>需配合 UseIP 策略使用。启用后将同时尝试 IPv4 和 IPv6 连接,并自动选择更快可用的路径。</value>
</data>
<data name="TbIpv6Address" xml:space="preserve">
<value>Ipv6 地址</value>
</data>
<data name="TbIpv4Address" xml:space="preserve">
<value>Ipv4 地址</value>
</data>
</root>
@@ -1839,4 +1839,10 @@
<data name="TbEnableHappyEyeballsTip" xml:space="preserve">
<value>需搭配 UseIP 策略,啟用後會同時嘗試 IPv4 與 IPv6 連線,並自動選擇速度較快的路徑</value>
</data>
<data name="TbIpv6Address" xml:space="preserve">
<value>Ipv6 位址</value>
</data>
<data name="TbIpv4Address" xml:space="preserve">
<value>Ipv4 位址</value>
</data>
</root>
+27 -26
View File
@@ -1,27 +1,28 @@
{
"tag": "tun",
"protocol": "tun",
"settings": {
"name": "xray_tun",
"MTU": 9000,
"gateway": [
"172.18.0.1/30",
"fdfe:dcba:9876::1/126"
],
"dns": [
"172.18.0.1"
],
"autoSystemRoutingTable": [
"0.0.0.0/0",
"::/0"
],
"autoOutboundsInterface": "auto"
},
"sniffing": {
"enabled": true,
"destOverride": [
"http",
"tls"
]
}
}
"tag": "tun",
"protocol": "tun",
"settings": {
"name": "xray_tun",
"MTU": 9000,
"gateway": [
"172.18.0.1/30",
"fdfe:dcba:9876::1/126"
],
"dns": [
"1.1.1.1",
"8.8.8.8"
],
"autoSystemRoutingTable": [
"0.0.0.0/0",
"::/0"
],
"autoOutboundsInterface": "auto"
},
"sniffing": {
"enabled": true,
"destOverride": [
"http",
"tls"
]
}
}
@@ -67,9 +67,13 @@ public partial class CoreConfigSingboxService
tunInbound.auto_route = _config.TunModeItem.AutoRoute;
tunInbound.strict_route = _config.TunModeItem.StrictRoute;
tunInbound.stack = _config.TunModeItem.Stack;
if (_config.TunModeItem.EnableIPv6Address == false)
var address = _config.TunModeItem.IPv4Address.NullIfEmpty() ?? Global.TunIPv4Address.First();
tunInbound.address = [address];
if (_config.TunModeItem.EnableIPv6Address == true)
{
tunInbound.address = ["172.18.0.1/30"];
var address6 = _config.TunModeItem.IPv6Address.NullIfEmpty() ?? Global.TunIPv6Address.First();
tunInbound.address.Add(address6);
}
tunInbound.route_exclude_address = _config.TunModeItem.RouteExcludeAddress;
@@ -63,11 +63,16 @@ public partial class CoreConfigV2rayService
new Inbounds4Ray();
tunInbound.settings.name = context.IsMacOS ? $"utun{new Random().Next(99)}" : "xray_tun";
tunInbound.settings.MTU = _config.TunModeItem.Mtu;
if (!_config.TunModeItem.EnableIPv6Address)
var address = _config.TunModeItem.IPv4Address.NullIfEmpty() ?? Global.TunIPv4Address.First();
tunInbound.settings.gateway = [address];
if (_config.TunModeItem.EnableIPv6Address == true)
{
tunInbound.settings.gateway = ["172.18.0.1/30"];
tunInbound.settings.autoSystemRoutingTable = ["0.0.0.0/0"];
var address6 = _config.TunModeItem.IPv6Address.NullIfEmpty() ?? Global.TunIPv6Address.First();
tunInbound.settings.gateway.Add(address6);
}
tunInbound.settings.autoSystemRoutingTable = ["0.0.0.0/0"];
var bindInterface = _config.CoreBasicItem.BindInterface?.TrimEx();
if (!bindInterface.IsNullOrEmpty())
{
@@ -3,6 +3,7 @@ namespace ServiceLib.ViewModels;
public class AddServer2ViewModel : MyReactiveObject, ICloseable
{
public event EventHandler? RequestClose;
public Interaction<Unit, string?> BrowseConfigFileInteraction { get; } = new();
[Reactive]
@@ -95,6 +95,8 @@ public class OptionSettingViewModel : MyReactiveObject, ICloseable
[Reactive] public string TunIcmpRouting { get; set; }
[Reactive] public bool TunEnableLegacyProtect { get; set; }
[Reactive] public string TunRouteExcludeAddress { get; set; }
[Reactive] public string TunIPv4Address { get; set; }
[Reactive] public string TunIPv6Address { get; set; }
#endregion Tun mode
@@ -214,6 +216,8 @@ public class OptionSettingViewModel : MyReactiveObject, ICloseable
TunIcmpRouting = _config.TunModeItem.IcmpRouting;
TunEnableLegacyProtect = _config.TunModeItem.EnableLegacyProtect;
TunRouteExcludeAddress = Utils.List2String(_config.TunModeItem.RouteExcludeAddress, true);
TunIPv4Address = _config.TunModeItem.IPv4Address;
TunIPv6Address = _config.TunModeItem.IPv6Address;
#endregion Tun mode
@@ -378,6 +382,8 @@ public class OptionSettingViewModel : MyReactiveObject, ICloseable
_config.TunModeItem.IcmpRouting = TunIcmpRouting;
_config.TunModeItem.EnableLegacyProtect = TunEnableLegacyProtect;
_config.TunModeItem.RouteExcludeAddress = Utils.String2List(TunRouteExcludeAddress);
_config.TunModeItem.IPv4Address = TunIPv4Address;
_config.TunModeItem.IPv6Address = TunIPv6Address;
//coreType
await SaveCoreType();
@@ -3,6 +3,7 @@ namespace ServiceLib.ViewModels;
public class ProfilesSelectViewModel : MyReactiveObject, ICloseable
{
public event EventHandler? RequestClose;
public Interaction<Unit, Unit> ProfilesFocusInteraction { get; } = new();
#region private prop
@@ -3,6 +3,7 @@ namespace ServiceLib.ViewModels;
public class RoutingRuleSettingViewModel : MyReactiveObject, ICloseable
{
public event EventHandler? RequestClose;
public Interaction<string, bool> ShowYesNoInteraction { get; } = new();
public Interaction<string, Unit> SetClipboardDataInteraction { get; } = new();
public Interaction<Unit, string?> ReadTextFromClipboardInteraction { get; } = new();
@@ -961,7 +961,7 @@
Margin="{StaticResource Margin4}"
ColumnDefinitions="Auto,Auto,Auto"
DockPanel.Dock="Top"
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
<TextBlock
Grid.Row="2"
@@ -1087,6 +1087,34 @@
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbRouteExcludeAddressTip}"
TextWrapping="Wrap" />
<TextBlock
Grid.Row="10"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbIpv4Address}" />
<ComboBox
x:Name="cmbIpv4Address"
Grid.Row="10"
Grid.Column="1"
Width="200"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" />
<TextBlock
Grid.Row="11"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbIpv6Address}" />
<ComboBox
x:Name="cmbIpv6Address"
Grid.Row="11"
Grid.Column="1"
Width="200"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" />
</Grid>
</ScrollViewer>
</TabItem>
@@ -33,6 +33,8 @@ public partial class OptionSettingWindow : WindowBase<OptionSettingViewModel>
cmbMtu.ItemsSource = Global.TunMtus;
cmbStack.ItemsSource = Global.TunStacks;
cmbIcmpRoutingPolicy.ItemsSource = Global.TunIcmpRoutingPolicies;
cmbIpv4Address.ItemsSource = Global.TunIPv4Address;
cmbIpv6Address.ItemsSource = Global.TunIPv6Address;
cmbFragmentPackets.ItemsSource = Global.FragmentPacketsOptions;
cmbCoreType1.ItemsSource = Global.CoreTypes;
@@ -129,6 +131,8 @@ public partial class OptionSettingWindow : WindowBase<OptionSettingViewModel>
this.Bind(ViewModel, vm => vm.TunIcmpRouting, v => v.cmbIcmpRoutingPolicy.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunEnableLegacyProtect, v => v.togEnableLegacyProtect.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunRouteExcludeAddress, v => v.txtRouteExcludeAddress.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunIPv4Address, v => v.cmbIpv4Address.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunIPv6Address, v => v.cmbIpv6Address.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CoreType1, v => v.cmbCoreType1.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CoreType2, v => v.cmbCoreType2.SelectedValue).DisposeWith(disposables);
@@ -1198,6 +1198,8 @@
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
@@ -1346,6 +1348,39 @@
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbRouteExcludeAddressTip}"
TextWrapping="Wrap" />
<TextBlock
Grid.Row="10"
Grid.Column="0"
Margin="{StaticResource Margin8}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbIpv4Address}" />
<ComboBox
x:Name="cmbIpv4Address"
Grid.Row="10"
Grid.Column="1"
Width="200"
Margin="{StaticResource Margin8}"
HorizontalAlignment="Left"
Style="{StaticResource DefComboBox}" />
<TextBlock
Grid.Row="11"
Grid.Column="0"
Margin="{StaticResource Margin8}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbIpv6Address}" />
<ComboBox
x:Name="cmbIpv6Address"
Grid.Row="11"
Grid.Column="1"
Width="200"
Margin="{StaticResource Margin8}"
HorizontalAlignment="Left"
Style="{StaticResource DefComboBox}" />
</Grid>
</ScrollViewer>
</TabItem>
@@ -29,6 +29,8 @@ public partial class OptionSettingWindow
cmbMtu.ItemsSource = Global.TunMtus;
cmbStack.ItemsSource = Global.TunStacks;
cmbIcmpRoutingPolicy.ItemsSource = Global.TunIcmpRoutingPolicies;
cmbIpv4Address.ItemsSource = Global.TunIPv4Address;
cmbIpv6Address.ItemsSource = Global.TunIPv6Address;
cmbFragmentPackets.ItemsSource = Global.FragmentPacketsOptions;
cmbCoreType1.ItemsSource = Global.CoreTypes;
@@ -86,14 +88,6 @@ public partial class OptionSettingWindow
this.Bind(ViewModel, vm => vm.FragmentDelays, v => v.txtFragmentDelays.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.FragmentMaxSplit, v => v.txtFragmentMaxSplit.Text).DisposeWith(disposables);
//this.Bind(ViewModel, vm => vm.Kcpmtu, v => v.txtKcpmtu.Text).DisposeWith(disposables);
//this.Bind(ViewModel, vm => vm.Kcptti, v => v.txtKcptti.Text).DisposeWith(disposables);
//this.Bind(ViewModel, vm => vm.KcpuplinkCapacity, v => v.txtKcpuplinkCapacity.Text).DisposeWith(disposables);
//this.Bind(ViewModel, vm => vm.KcpdownlinkCapacity, v => v.txtKcpdownlinkCapacity.Text).DisposeWith(disposables);
//this.Bind(ViewModel, vm => vm.KcpreadBufferSize, v => v.txtKcpreadBufferSize.Text).DisposeWith(disposables);
//this.Bind(ViewModel, vm => vm.KcpwriteBufferSize, v => v.txtKcpwriteBufferSize.Text).DisposeWith(disposables);
//this.Bind(ViewModel, vm => vm.Kcpcongestion, v => v.togKcpcongestion.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.AutoRun, v => v.togAutoRun.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.EnableStatistics, v => v.togEnableStatistics.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.DisplayRealTimeSpeed, v => v.togDisplayRealTimeSpeed.IsChecked).DisposeWith(disposables);
@@ -132,6 +126,8 @@ public partial class OptionSettingWindow
this.Bind(ViewModel, vm => vm.TunIcmpRouting, v => v.cmbIcmpRoutingPolicy.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunEnableLegacyProtect, v => v.togEnableLegacyProtect.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunRouteExcludeAddress, v => v.txtRouteExcludeAddress.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunIPv4Address, v => v.cmbIpv4Address.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunIPv6Address, v => v.cmbIpv6Address.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CoreType1, v => v.cmbCoreType1.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CoreType2, v => v.cmbCoreType2.Text).DisposeWith(disposables);