mirror of
https://github.com/2dust/v2rayN.git
synced 2026-11-06 12:35:00 +03:00
Add custom outbound support (#9817)
* Add custom outbound support * Add test * Add inner fmt support * Full config to outbounds * Rename to `Outbound` * AI optimized * Fix * Add bind interface placeholder --------- Co-authored-by: 2dust <31833384+2dust@users.noreply.github.com>
This commit is contained in:
@@ -626,71 +626,40 @@ public class CoreConfigSingboxServiceTests
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GenerateClientConfigContent_TunEnabled_ShouldKeepEmbeddedTunRules()
|
||||
{
|
||||
// The embedded tun rules reject local-network noise (NetBIOS/mDNS, multicast).
|
||||
// They are deserialized into List<Rule4Sbox>, so a schema mismatch in the
|
||||
// embedded template makes JsonUtils.Deserialize return null and silently
|
||||
// drops every one of them.
|
||||
var config = CoreConfigTestFactory.CreateConfig(ECoreType.sing_box);
|
||||
config.TunModeItem.EnableTun = 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())!;
|
||||
|
||||
cfg.route.rules.Should().Contain(
|
||||
r => r.action == "reject"
|
||||
&& r.network != null && r.network.Contains("udp")
|
||||
&& r.port != null && r.port.Contains(5353),
|
||||
"the embedded tun rules must reject mDNS/NetBIOS noise");
|
||||
cfg.route.rules.Should().Contain(
|
||||
r => r.action == "reject"
|
||||
&& r.ip_cidr != null && r.ip_cidr.Contains("224.0.0.0/3"),
|
||||
"the embedded tun rules must reject multicast traffic");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GenerateClientConfigContent_TunEnabled_ShouldRejectTrafficToTunOwnAddresses()
|
||||
public void GenerateClientConfigContent_CustomOutbound_ShouldReplaceWithUserCustomOutboundJson()
|
||||
{
|
||||
// Regression test: traffic addressed to the TUN interface's own addresses must
|
||||
// never reach an outbound. auto_route hijacks the default route, so `direct`
|
||||
// writes such a packet straight back into the TUN, which routes it to the
|
||||
// outbound again - an infinite loop that pins a CPU core. Observed in the wild
|
||||
// with WebRTC ICE connectivity checks against the TUN's own fc00::/7 ULA
|
||||
// address, sustaining ~8k packets/s out of the TUN interface.
|
||||
var config = CoreConfigTestFactory.CreateConfig(ECoreType.sing_box);
|
||||
config.TunModeItem.EnableTun = true;
|
||||
config.TunModeItem.EnableIPv6Address = true;
|
||||
CoreConfigTestFactory.BindAppManagerConfig(config);
|
||||
|
||||
var node = CoreConfigTestFactory.CreateVmessNode(ECoreType.sing_box);
|
||||
var context = CoreConfigTestFactory.CreateContext(config, node, ECoreType.sing_box) with
|
||||
var customNode = CoreConfigTestFactory.CreateCustomOutboundNode(ECoreType.sing_box, "n-custom", "custom-singbox");
|
||||
var customJsonContent = """
|
||||
{
|
||||
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!)
|
||||
{
|
||||
cfg.route.rules.Should().Contain(
|
||||
r => r.action == "reject" && r.ip_cidr != null && r.ip_cidr.Contains(address),
|
||||
$"traffic to the TUN's own address '{address}' must be rejected, not routed");
|
||||
"type": "shadowsocks",
|
||||
"server": "1.2.3.4",
|
||||
"server_port": 8388,
|
||||
"method": "aes-128-gcm",
|
||||
"password": "custom_password"
|
||||
}
|
||||
""";
|
||||
|
||||
var context = CoreConfigTestFactory.CreateContext(config, customNode, ECoreType.sing_box);
|
||||
context.CustomOutboundContent[customNode.IndexId] = customJsonContent;
|
||||
|
||||
var result = new CoreConfigSingboxService(context).GenerateClientConfigContent();
|
||||
|
||||
result.Success.Should().BeTrue($"ret msg: {result.Msg}");
|
||||
result.Data.Should().NotBeNull();
|
||||
|
||||
var cfg = JsonUtils.Deserialize<SingboxConfig>(result.Data!.ToString());
|
||||
cfg.Should().NotBeNull();
|
||||
var proxyOutbound = cfg!.outbounds.FirstOrDefault(o => o.tag == Global.ProxyTag);
|
||||
proxyOutbound.Should().NotBeNull();
|
||||
proxyOutbound!.type.Should().Be("shadowsocks");
|
||||
proxyOutbound.server.Should().Be("1.2.3.4");
|
||||
proxyOutbound.server_port.Should().Be(8388);
|
||||
proxyOutbound.method.Should().Be("aes-128-gcm");
|
||||
proxyOutbound.password.Should().Be("custom_password");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user