diff --git a/v2rayN/ServiceLib/Global.cs b/v2rayN/ServiceLib/Global.cs index 6c4a4362..c7f18d2c 100644 --- a/v2rayN/ServiceLib/Global.cs +++ b/v2rayN/ServiceLib/Global.cs @@ -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"; diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/CoreConfigSingboxService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/CoreConfigSingboxService.cs index 61b43567..4c89afec 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/CoreConfigSingboxService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/CoreConfigSingboxService.cs @@ -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(); foreach (var it in selecteds) diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxRoutingService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxRoutingService.cs index 21dfb101..522ba86a 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxRoutingService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxRoutingService.cs @@ -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)) { diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs index 9f8646b3..5555e67e 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs @@ -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(); 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) { - rule.outboundTag = null; - rule.balancerTag = balancer.tag; + if (rule.outboundTag == null) + continue; + + if (balancerTagSet.Contains(rule.outboundTag)) + { + rule.balancerTag = rule.outboundTag; + rule.outboundTag = null; + 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" }); } diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayBalancerService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayBalancerService.cs index 634c6f97..f703e8d1 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayBalancerService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayBalancerService.cs @@ -2,7 +2,7 @@ namespace ServiceLib.Services.CoreConfig; public partial class CoreConfigV2rayService { - private async Task GenBalancer(V2rayConfig v2rayConfig, EMultipleLoad multipleLoad) + private async Task 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 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); } } diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs index e827946f..f383c216 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs @@ -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() }; } diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayRoutingService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayRoutingService.cs index d39ea833..27bffeb6 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayRoutingService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayRoutingService.cs @@ -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)) { diff --git a/v2rayN/v2rayN.Desktop/Views/RoutingRuleDetailsWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/RoutingRuleDetailsWindow.axaml.cs index cbf156bd..b19fc923 100644 --- a/v2rayN/v2rayN.Desktop/Views/RoutingRuleDetailsWindow.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/RoutingRuleDetailsWindow.axaml.cs @@ -97,7 +97,7 @@ public partial class RoutingRuleDetailsWindow : WindowBase(this); if (result == true) { diff --git a/v2rayN/v2rayN/Views/RoutingRuleDetailsWindow.xaml.cs b/v2rayN/v2rayN/Views/RoutingRuleDetailsWindow.xaml.cs index 4ad8099d..d3e06cb3 100644 --- a/v2rayN/v2rayN/Views/RoutingRuleDetailsWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/RoutingRuleDetailsWindow.xaml.cs @@ -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;