Add group in traffic splitting support

This commit is contained in:
DHR60 2025-09-11 17:18:53 +08:00
parent af62d9e0f4
commit 2d5a5465df
9 changed files with 115 additions and 17 deletions

View file

@ -50,6 +50,7 @@ public class Global
public const string DirectTag = "direct";
public const string BlockTag = "block";
public const string DnsTag = "dns-module";
public const string BalancerTagSuffix = "-round";
public const string StreamSecurity = "tls";
public const string StreamSecurityReality = "reality";
public const string Loopback = "127.0.0.1";

View file

@ -398,12 +398,12 @@ public partial class CoreConfigSingboxService(Config config)
ret.Msg = ResUI.FailedGenDefaultConfiguration;
return ret;
}
singboxConfig.outbounds.RemoveAt(0);
await GenLog(singboxConfig);
await GenInbounds(singboxConfig);
await GenRouting(singboxConfig);
await GenExperimental(singboxConfig);
singboxConfig.outbounds.RemoveAt(0);
var proxyProfiles = new List<ProfileItem>();
foreach (var it in selecteds)

View file

@ -368,6 +368,39 @@ public partial class CoreConfigSingboxService
}
var node = await AppManager.Instance.GetProfileItemViaRemarks(outboundTag);
if (node.ConfigType is EConfigType.PolicyGroup or EConfigType.ProxyChain)
{
ProfileGroupItemManager.Instance.TryGet(node.IndexId, out var profileGroupItem);
if (profileGroupItem == null || profileGroupItem.ChildItems.IsNullOrEmpty())
{
return Global.ProxyTag;
}
var childProfiles = (await Task.WhenAll(
Utils.String2List(profileGroupItem.ChildItems)
.Where(p => !p.IsNullOrEmpty())
.Select(AppManager.Instance.GetProfileItem)
)).Where(p => p != null).ToList();
if (childProfiles.Count <= 0)
{
return Global.ProxyTag;
}
var childBaseTagName = $"{Global.ProxyTag}-{node.IndexId}";
var ret = node.ConfigType switch
{
EConfigType.PolicyGroup =>
await GenOutboundsList(childProfiles, singboxConfig, profileGroupItem.MultipleLoad, childBaseTagName),
EConfigType.ProxyChain =>
await GenChainOutboundsList(childProfiles, singboxConfig, childBaseTagName),
_ => throw new NotImplementedException()
};
if (ret == 0)
{
return childBaseTagName;
}
return Global.ProxyTag;
}
if (node == null
|| !Global.SingboxSupportConfigType.Contains(node.ConfigType))
{

View file

@ -126,13 +126,13 @@ public partial class CoreConfigV2rayService(Config config)
ret.Msg = ResUI.FailedGenDefaultConfiguration;
return ret;
}
v2rayConfig.outbounds.RemoveAt(0);
await GenLog(v2rayConfig);
await GenInbounds(v2rayConfig);
await GenRouting(v2rayConfig);
await GenDns(null, v2rayConfig);
await GenStatistic(v2rayConfig);
v2rayConfig.outbounds.RemoveAt(0);
var proxyProfiles = new List<ProfileItem>();
foreach (var it in selecteds)
@ -183,18 +183,37 @@ public partial class CoreConfigV2rayService(Config config)
await GenOutboundsList(proxyProfiles, v2rayConfig);
//add balancers
await GenObservatory(v2rayConfig, multipleLoad);
await GenBalancer(v2rayConfig, multipleLoad);
var balancer = v2rayConfig.routing.balancers.First();
var defaultBalancerTag = $"{Global.ProxyTag}{Global.BalancerTagSuffix}";
//add rule
var rules = v2rayConfig.routing.rules.Where(t => t.outboundTag == Global.ProxyTag).ToList();
var rules = v2rayConfig.routing.rules;
if (rules?.Count > 0)
{
var balancerTagSet = v2rayConfig.routing.balancers
.Select(b => b.tag)
.ToHashSet();
foreach (var rule in rules)
{
if (rule.outboundTag == null)
continue;
if (balancerTagSet.Contains(rule.outboundTag))
{
rule.balancerTag = rule.outboundTag;
rule.outboundTag = null;
rule.balancerTag = balancer.tag;
continue;
}
var outboundWithSuffix = rule.outboundTag + Global.BalancerTagSuffix;
if (balancerTagSet.Contains(outboundWithSuffix))
{
rule.balancerTag = outboundWithSuffix;
rule.outboundTag = null;
}
}
}
if (v2rayConfig.routing.domainStrategy == Global.IPIfNonMatch)
@ -202,7 +221,7 @@ public partial class CoreConfigV2rayService(Config config)
v2rayConfig.routing.rules.Add(new()
{
ip = ["0.0.0.0/0", "::/0"],
balancerTag = balancer.tag,
balancerTag = defaultBalancerTag,
type = "field"
});
}
@ -211,7 +230,7 @@ public partial class CoreConfigV2rayService(Config config)
v2rayConfig.routing.rules.Add(new()
{
network = "tcp,udp",
balancerTag = balancer.tag,
balancerTag = defaultBalancerTag,
type = "field"
});
}

View file

@ -2,7 +2,7 @@ namespace ServiceLib.Services.CoreConfig;
public partial class CoreConfigV2rayService
{
private async Task<int> GenBalancer(V2rayConfig v2rayConfig, EMultipleLoad multipleLoad)
private async Task<int> GenObservatory(V2rayConfig v2rayConfig, EMultipleLoad multipleLoad)
{
if (multipleLoad == EMultipleLoad.LeastPing)
{
@ -30,6 +30,11 @@ public partial class CoreConfigV2rayService
};
v2rayConfig.burstObservatory = burstObservatory;
}
return await Task.FromResult(0);
}
private async Task<string> GenBalancer(V2rayConfig v2rayConfig, EMultipleLoad multipleLoad, string selector = Global.ProxyTag)
{
var strategyType = multipleLoad switch
{
EMultipleLoad.Random => "random",
@ -38,9 +43,10 @@ public partial class CoreConfigV2rayService
EMultipleLoad.LeastLoad => "leastLoad",
_ => "roundRobin",
};
var balancerTag = $"{selector}{Global.BalancerTagSuffix}";
var balancer = new BalancersItem4Ray
{
selector = [Global.ProxyTag],
selector = [selector],
strategy = new()
{
type = strategyType,
@ -49,9 +55,10 @@ public partial class CoreConfigV2rayService
expected = 1,
},
},
tag = $"{Global.ProxyTag}-round",
tag = balancerTag,
};
v2rayConfig.routing.balancers = [balancer];
return await Task.FromResult(0);
v2rayConfig.routing.balancers ??= new();
v2rayConfig.routing.balancers.Add(balancer);
return await Task.FromResult(balancerTag);
}
}

View file

@ -746,14 +746,15 @@ public partial class CoreConfigV2rayService
}
else
{
outbound.tag = baseTagName + i.ToString();
// avoid v2ray observe
outbound.tag = "chain-" + baseTagName + i.ToString();
}
if (i != nodes.Count - 1)
{
outbound.streamSettings.sockopt = new()
{
dialerProxy = baseTagName + (i + 1).ToString()
dialerProxy = "chain-" + baseTagName + (i + 1).ToString()
};
}

View file

@ -125,6 +125,43 @@ public partial class CoreConfigV2rayService
}
var node = await AppManager.Instance.GetProfileItemViaRemarks(outboundTag);
if (node.ConfigType is EConfigType.PolicyGroup or EConfigType.ProxyChain)
{
ProfileGroupItemManager.Instance.TryGet(node.IndexId, out var profileGroupItem);
if (profileGroupItem == null || profileGroupItem.ChildItems.IsNullOrEmpty())
{
return Global.ProxyTag;
}
var childProfiles = (await Task.WhenAll(
Utils.String2List(profileGroupItem.ChildItems)
.Where(p => !p.IsNullOrEmpty())
.Select(AppManager.Instance.GetProfileItem)
)).Where(p => p != null).ToList();
if (childProfiles.Count <= 0)
{
return Global.ProxyTag;
}
var childBaseTagName = $"{Global.ProxyTag}-{node.IndexId}";
var ret = node.ConfigType switch
{
EConfigType.PolicyGroup =>
await GenOutboundsList(childProfiles, v2rayConfig, childBaseTagName),
EConfigType.ProxyChain =>
await GenChainOutboundsList(childProfiles, v2rayConfig, childBaseTagName),
_ => throw new NotImplementedException()
};
if (node.ConfigType == EConfigType.PolicyGroup)
{
await GenBalancer(v2rayConfig, profileGroupItem.MultipleLoad, childBaseTagName);
}
if (ret == 0)
{
return childBaseTagName;
}
return Global.ProxyTag;
}
if (node == null
|| !Global.XraySupportConfigType.Contains(node.ConfigType))
{

View file

@ -97,7 +97,7 @@ public partial class RoutingRuleDetailsWindow : WindowBase<RoutingRuleDetailsVie
private async void BtnSelectProfile_Click(object? sender, RoutedEventArgs e)
{
var selectWindow = new ProfilesSelectWindow();
selectWindow.SetConfigTypeFilter(new[] { EConfigType.Custom, EConfigType.PolicyGroup, EConfigType.ProxyChain }, exclude: true);
selectWindow.SetConfigTypeFilter(new[] { EConfigType.Custom }, exclude: true);
var result = await selectWindow.ShowDialog<bool?>(this);
if (result == true)
{

View file

@ -91,7 +91,7 @@ public partial class RoutingRuleDetailsWindow
private async void BtnSelectProfile_Click(object sender, RoutedEventArgs e)
{
var selectWindow = new ProfilesSelectWindow();
selectWindow.SetConfigTypeFilter(new[] { EConfigType.Custom, EConfigType.PolicyGroup, EConfigType.ProxyChain }, exclude: true);
selectWindow.SetConfigTypeFilter(new[] { EConfigType.Custom }, exclude: true);
if (selectWindow.ShowDialog() == true)
{
var profile = await selectWindow.ProfileItem;