diff --git a/v2rayN/ServiceLib.Tests/CoreConfig/CoreConfigTestFactory.cs b/v2rayN/ServiceLib.Tests/CoreConfig/CoreConfigTestFactory.cs index 4109ea6e..cb901252 100644 --- a/v2rayN/ServiceLib.Tests/CoreConfig/CoreConfigTestFactory.cs +++ b/v2rayN/ServiceLib.Tests/CoreConfig/CoreConfigTestFactory.cs @@ -251,4 +251,12 @@ internal static class CoreConfigTestFactory config.TunModeItem.RouteExcludeAddress = ["10.0.0.1/32", "192.168.1.0/24", "fc00::/7"]; return config; } + + public static Config CreateConfigWithTun(ECoreType coreType, bool enableIPv6Address) + { + var config = CreateConfig(coreType); + config.TunModeItem.EnableTun = true; + config.TunModeItem.EnableIPv6Address = enableIPv6Address; + return config; + } } diff --git a/v2rayN/ServiceLib.Tests/CoreConfig/V2ray/CoreConfigV2rayServiceTests.cs b/v2rayN/ServiceLib.Tests/CoreConfig/V2ray/CoreConfigV2rayServiceTests.cs index 73f3218e..bad78c06 100644 --- a/v2rayN/ServiceLib.Tests/CoreConfig/V2ray/CoreConfigV2rayServiceTests.cs +++ b/v2rayN/ServiceLib.Tests/CoreConfig/V2ray/CoreConfigV2rayServiceTests.cs @@ -570,6 +570,51 @@ public class CoreConfigV2rayServiceTests directOutbound!.streamSettings.sockopt!.domainStrategy.Should().Be("UseIPv4"); } + [Theory] + [InlineData(false)] + [InlineData(true)] + public void GenerateClientConfigContent_Tun_ShouldRouteIPv6IntoTunnel(bool enableIPv6Address) + { + var config = CoreConfigTestFactory.CreateConfigWithTun(ECoreType.Xray, enableIPv6Address); + CoreConfigTestFactory.BindAppManagerConfig(config); + + var node = CoreConfigTestFactory.CreateVmessNode(ECoreType.Xray, "n-main", "main"); + var context = CoreConfigTestFactory.CreateContext(config, node, ECoreType.Xray); + + var result = new CoreConfigV2rayService(context).GenerateClientConfigContent(); + + result.Success.Should().BeTrue(); + var cfg = JsonUtils.Deserialize(result.Data!.ToString())!; + var tunInbound = cfg.inbounds.FirstOrDefault(i => i.protocol == "tun"); + + tunInbound.Should().NotBeNull(); + tunInbound!.settings.autoSystemRoutingTable.Should().Contain("0.0.0.0/0"); + tunInbound.settings.autoSystemRoutingTable.Should().Contain("::/0"); + + // EnableIPv6Address governs the interface address only, never the routing table. + tunInbound.settings.gateway.Should().HaveCount(enableIPv6Address ? 2 : 1); + } + + [Fact] + public void GenerateClientConfigContent_TunRouteExcludeAddress_ShouldIncludeIPv6Ranges() + { + var config = CoreConfigTestFactory.CreateConfigWithTunRouteExcludeAddress(ECoreType.Xray); + config.TunModeItem.EnableIPv6Address = false; + CoreConfigTestFactory.BindAppManagerConfig(config); + + var node = CoreConfigTestFactory.CreateVmessNode(ECoreType.Xray, "n-main", "main"); + var context = CoreConfigTestFactory.CreateContext(config, node, ECoreType.Xray); + + var result = new CoreConfigV2rayService(context).GenerateClientConfigContent(); + + result.Success.Should().BeTrue(); + var cfg = JsonUtils.Deserialize(result.Data!.ToString())!; + var tunInbound = cfg.inbounds.FirstOrDefault(i => i.protocol == "tun"); + + tunInbound.Should().NotBeNull(); + tunInbound!.settings.autoSystemRoutingTable.Should().Contain(x => x.Contains(':')); + } + [Fact] public void GenerateClientConfigContent_TunRouteExcludeAddress() { diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayInboundService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayInboundService.cs index 22c8826a..515ee917 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayInboundService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayInboundService.cs @@ -67,12 +67,14 @@ public partial class CoreConfigV2rayService var address = _config.TunModeItem.IPv4Address.NullIfEmpty() ?? Global.TunIPv4Address.First(); tunInbound.settings.gateway = [address]; - tunInbound.settings.autoSystemRoutingTable = ["0.0.0.0/0"]; + // Route both families into the tunnel regardless of EnableIPv6Address. That option only + // controls whether the interface gets an IPv6 address; leaving ::/0 out of the routing + // table makes IPv6 follow the system default route and bypass the tunnel entirely. + tunInbound.settings.autoSystemRoutingTable = ["0.0.0.0/0", "::/0"]; if (_config.TunModeItem.EnableIPv6Address == true) { var address6 = _config.TunModeItem.IPv6Address.NullIfEmpty() ?? Global.TunIPv6Address.First(); tunInbound.settings.gateway.Add(address6); - tunInbound.settings.autoSystemRoutingTable.Add("::/0"); } var bindInterface = _config.CoreBasicItem.BindInterface?.TrimEx(); @@ -118,15 +120,8 @@ public partial class CoreConfigV2rayService includeList = IPNetwork2.Supernet(includeList.ToArray()).ToList(); includeListV6 = IPNetwork2.Supernet(includeListV6.ToArray()).ToList(); - if (_config.TunModeItem.EnableIPv6Address) - { - tunInbound.settings.autoSystemRoutingTable = includeList.Select(x => x.ToString()) - .Concat(includeListV6.Select(x => x.ToString())).ToList(); - } - else - { - tunInbound.settings.autoSystemRoutingTable = includeList.Select(x => x.ToString()).ToList(); - } + tunInbound.settings.autoSystemRoutingTable = includeList.Select(x => x.ToString()) + .Concat(includeListV6.Select(x => x.ToString())).ToList(); } _coreConfig.inbounds.Add(tunInbound);