mirror of
https://github.com/2dust/v2rayN.git
synced 2026-08-12 02:02:04 +03:00
* Always route IPv6 into Xray TUN regardless of EnableIPv6Address
EnableIPv6Address controls whether the TUN interface is assigned an IPv6
address, but it also gated whether ::/0 was added to autoSystemRoutingTable.
With the default (false), IPv6 had no route pointing at the TUN device and
followed the system default route instead, leaving the tunnel unproxied and
exposing the host's real IPv6 address.
The embedded template SampleTunInbound already declares both families; the
generated config discarded it. #9843 restored ::/0 only inside the
EnableIPv6Address == true branch, so the false branch still leaks.
Route both families unconditionally and let the option control only the
interface address. The same conditional existed a second time in the
RouteExcludeAddress branch and is fixed as well.
Fixes #9929
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Add regression tests for IPv6 routing in the Xray TUN inbound
Both assertions fail on 31044f44 and pass with the fix:
Tun_ShouldRouteIPv6IntoTunnel(enableIPv6Address: False)
Expected collection {"0.0.0.0/0"} to contain "::/0".
TunRouteExcludeAddress_ShouldIncludeIPv6Ranges
Expected collection {...44 IPv4 ranges...} to have an item matching x.Contains(:).
The theory also covers enableIPv6Address: true, which passes on both revisions,
so the tests only fail while the defect is present. The gateway count assertion
pins the intended split of responsibilities: EnableIPv6Address governs the
interface address, never the routing table.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: liuclare <177657698+liuclare@users.noreply.github.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
263 lines
9.0 KiB
C#
263 lines
9.0 KiB
C#
using System.Reflection;
|
|
using ServiceLib.Enums;
|
|
using ServiceLib.Manager;
|
|
using ServiceLib.Models;
|
|
|
|
namespace ServiceLib.Tests.CoreConfig;
|
|
|
|
internal static class CoreConfigTestFactory
|
|
{
|
|
public static void BindAppManagerConfig(Config config)
|
|
{
|
|
var field = typeof(AppManager).GetField("_config", BindingFlags.Instance | BindingFlags.NonPublic);
|
|
field?.SetValue(AppManager.Instance, config);
|
|
}
|
|
|
|
public static Config CreateConfig(ECoreType vmessCoreType = ECoreType.Xray)
|
|
{
|
|
return new Config
|
|
{
|
|
CoreBasicItem = new CoreBasicItem { Loglevel = "warning" },
|
|
TunModeItem = new TunModeItem { EnableTun = false, IcmpRouting = "default" },
|
|
KcpItem = new KcpItem(),
|
|
GrpcItem = new GrpcItem(),
|
|
RoutingBasicItem =
|
|
new RoutingBasicItem
|
|
{
|
|
DomainStrategy = Global.AsIs,
|
|
DomainStrategy4Singbox = string.Empty,
|
|
RoutingIndexId = string.Empty,
|
|
},
|
|
GuiItem = new GUIItem { EnableStatistics = false, DisplayRealTimeSpeed = false, EnableLog = false },
|
|
MsgUIItem = new MsgUIItem(),
|
|
UiItem =
|
|
new UIItem
|
|
{
|
|
CurrentLanguage = "en",
|
|
CurrentFontFamily = "sans",
|
|
MainColumnItem = [],
|
|
WindowSizeItem = []
|
|
},
|
|
ConstItem = new ConstItem(),
|
|
SpeedTestItem = new SpeedTestItem
|
|
{
|
|
SpeedPingTestUrl = Global.SpeedPingTestUrls.First(),
|
|
SpeedTestUrl = Global.SpeedTestUrls.First(),
|
|
SpeedTestTimeout = 10,
|
|
MixedConcurrencyCount = 1,
|
|
IPAPIUrl = string.Empty,
|
|
},
|
|
Mux4RayItem = new Mux4RayItem { Concurrency = 8, XudpConcurrency = 16, XudpProxyUDP443 = "reject" },
|
|
Mux4SboxItem = new Mux4SboxItem { Protocol = Global.SingboxMuxs.First(), MaxConnections = 8 },
|
|
HysteriaItem = new HysteriaItem { UpMbps = 100, DownMbps = 100 },
|
|
ClashUIItem = new ClashUIItem { ConnectionsColumnItem = [] },
|
|
SystemProxyItem =
|
|
new SystemProxyItem
|
|
{
|
|
SystemProxyExceptions = string.Empty,
|
|
SystemProxyAdvancedProtocol = string.Empty
|
|
},
|
|
WebDavItem = new WebDavItem(),
|
|
CheckUpdateItem = new CheckUpdateItem(),
|
|
Fragment4RayItem = new Fragment4RayItem { Packets = "tlshello", Lengths = ["100-200"], Delays = ["10-20"] },
|
|
Inbound =
|
|
[
|
|
new InItem
|
|
{
|
|
Protocol = nameof(EInboundProtocol.socks),
|
|
LocalPort = 10808,
|
|
UdpEnabled = true,
|
|
SniffingEnabled = true,
|
|
RouteOnly = false,
|
|
DestOverride = ["http", "tls"],
|
|
}
|
|
],
|
|
GlobalHotkeys = [],
|
|
CoreTypeItem =
|
|
[
|
|
new CoreTypeItem { ConfigType = EConfigType.VMess, CoreType = vmessCoreType }
|
|
],
|
|
SimpleDNSItem = new SimpleDNSItem
|
|
{
|
|
BootstrapDNS = Global.DomainPureIPDNSAddress.FirstOrDefault(),
|
|
ServeStale = false,
|
|
ParallelQuery = false,
|
|
Strategy4Freedom = Global.AsIs,
|
|
Strategy4Proxy = Global.AsIs,
|
|
Strategy4ProxyDial = Global.AsIs,
|
|
},
|
|
IndexId = string.Empty,
|
|
SubIndexId = string.Empty,
|
|
};
|
|
}
|
|
|
|
public static ProfileItem CreateVmessNode(ECoreType coreType, string indexId = "node-1", string remarks = "demo")
|
|
{
|
|
var node = new ProfileItem
|
|
{
|
|
IndexId = indexId,
|
|
ConfigType = EConfigType.VMess,
|
|
CoreType = coreType,
|
|
Remarks = remarks,
|
|
Address = "example.com",
|
|
Port = 443,
|
|
Password = Guid.NewGuid().ToString(),
|
|
Network = nameof(ETransport.raw),
|
|
StreamSecurity = string.Empty,
|
|
Subid = string.Empty,
|
|
};
|
|
|
|
node.SetProtocolExtra(node.GetProtocolExtra() with { AlterId = "0", VmessSecurity = Global.DefaultSecurity, });
|
|
|
|
return node;
|
|
}
|
|
|
|
public static ProfileItem CreateSocksNode(ECoreType coreType, string indexId = "node-socks-1",
|
|
string remarks = "demo-socks")
|
|
{
|
|
return new ProfileItem
|
|
{
|
|
IndexId = indexId,
|
|
ConfigType = EConfigType.SOCKS,
|
|
CoreType = coreType,
|
|
Remarks = remarks,
|
|
Address = "127.0.0.1",
|
|
Port = 1080,
|
|
Password = "pass",
|
|
Username = "user",
|
|
Network = nameof(ETransport.raw),
|
|
StreamSecurity = string.Empty,
|
|
Subid = string.Empty,
|
|
};
|
|
}
|
|
|
|
public static ProfileItem CreateHttpNode(ECoreType coreType, string indexId = "node-http-1",
|
|
string remarks = "demo-http")
|
|
{
|
|
return new ProfileItem
|
|
{
|
|
IndexId = indexId,
|
|
ConfigType = EConfigType.HTTP,
|
|
CoreType = coreType,
|
|
Remarks = remarks,
|
|
Address = "proxy.example.com",
|
|
Port = 8080,
|
|
Password = "pass",
|
|
Username = "user",
|
|
Network = nameof(ETransport.raw),
|
|
StreamSecurity = string.Empty,
|
|
Subid = string.Empty,
|
|
};
|
|
}
|
|
|
|
public static ProfileItem CreateCustomOutboundNode(ECoreType coreType, string indexId = "node-custom-1",
|
|
string remarks = "demo-custom-outbound", string address = "custom_outbound.json")
|
|
{
|
|
return new ProfileItem
|
|
{
|
|
IndexId = indexId,
|
|
ConfigType = EConfigType.Outbound,
|
|
CoreType = coreType,
|
|
Remarks = remarks,
|
|
Address = address,
|
|
Port = 0,
|
|
Network = nameof(ETransport.raw),
|
|
StreamSecurity = string.Empty,
|
|
Subid = string.Empty,
|
|
};
|
|
}
|
|
|
|
public static ProfileItem CreatePolicyGroupNode(ECoreType coreType, string indexId, string remarks,
|
|
IEnumerable<string> childIndexIds)
|
|
{
|
|
var node = new ProfileItem
|
|
{
|
|
IndexId = indexId,
|
|
ConfigType = EConfigType.PolicyGroup,
|
|
CoreType = coreType,
|
|
Remarks = remarks,
|
|
};
|
|
node.SetProtocolExtra(node.GetProtocolExtra() with
|
|
{
|
|
GroupType = nameof(EConfigType.PolicyGroup),
|
|
ChildItems = string.Join(",", childIndexIds),
|
|
});
|
|
|
|
return node;
|
|
}
|
|
|
|
public static ProfileItem CreateProxyChainNode(ECoreType coreType, string indexId, string remarks,
|
|
IEnumerable<string> childIndexIds)
|
|
{
|
|
var node = new ProfileItem
|
|
{
|
|
IndexId = indexId,
|
|
ConfigType = EConfigType.ProxyChain,
|
|
CoreType = coreType,
|
|
Remarks = remarks,
|
|
};
|
|
node.SetProtocolExtra(node.GetProtocolExtra() with
|
|
{
|
|
GroupType = nameof(EConfigType.ProxyChain),
|
|
ChildItems = string.Join(",", childIndexIds),
|
|
});
|
|
|
|
return node;
|
|
}
|
|
|
|
public static CoreConfigContext CreateContext(Config config, ProfileItem node, ECoreType runCoreType)
|
|
{
|
|
return new CoreConfigContext
|
|
{
|
|
Node = node,
|
|
RunCoreType = runCoreType,
|
|
AppConfig = config,
|
|
RoutingItem = new RoutingItem
|
|
{
|
|
Id = "r1",
|
|
Remarks = "default",
|
|
RuleSet = "[]",
|
|
DomainStrategy = Global.AsIs,
|
|
DomainStrategy4Singbox = string.Empty,
|
|
},
|
|
RawDnsItem = null,
|
|
SimpleDnsItem = config.SimpleDNSItem,
|
|
AllProxiesMap = new Dictionary<string, ProfileItem> { [node.IndexId] = node },
|
|
FullConfigTemplate = null,
|
|
IsTunEnabled = config.TunModeItem.EnableTun,
|
|
ProtectDomainList = [],
|
|
};
|
|
}
|
|
|
|
public static Config CreateConfigWithDirectExpectedIPs(ECoreType coreType,
|
|
string directExpectedIPs = "192.168.0.0/16,geoip:cn")
|
|
{
|
|
var config = CreateConfig(coreType);
|
|
config.SimpleDNSItem.DirectExpectedIPs = directExpectedIPs;
|
|
return config;
|
|
}
|
|
|
|
public static Config CreateConfigWithBootstrapDNS(ECoreType coreType, string bootstrapDns = "8.8.8.8")
|
|
{
|
|
var config = CreateConfig(coreType);
|
|
config.SimpleDNSItem.BootstrapDNS = bootstrapDns;
|
|
return config;
|
|
}
|
|
|
|
public static Config CreateConfigWithTunRouteExcludeAddress(ECoreType coreType)
|
|
{
|
|
var config = CreateConfig(coreType);
|
|
config.TunModeItem.EnableTun = true;
|
|
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;
|
|
}
|
|
}
|