From 052f6f0feccdf89010653912c4656acfe9b6ded1 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Fri, 6 Nov 2026 15:19:31 +0800 Subject: [PATCH] Suggest local interfaces in bind setting --- v2rayN/ServiceLib/Common/NetworkUtils.cs | 80 +++++++++++++++++++ .../Views/OptionSettingWindow.axaml | 19 +++-- .../Views/OptionSettingWindow.axaml.cs | 5 +- v2rayN/v2rayN/Views/OptionSettingWindow.xaml | 5 +- .../v2rayN/Views/OptionSettingWindow.xaml.cs | 1 + 5 files changed, 99 insertions(+), 11 deletions(-) create mode 100644 v2rayN/ServiceLib/Common/NetworkUtils.cs diff --git a/v2rayN/ServiceLib/Common/NetworkUtils.cs b/v2rayN/ServiceLib/Common/NetworkUtils.cs new file mode 100644 index 00000000..209455f4 --- /dev/null +++ b/v2rayN/ServiceLib/Common/NetworkUtils.cs @@ -0,0 +1,80 @@ +namespace ServiceLib.Common; + +public static class NetworkUtils +{ + private static readonly HashSet _allowedPhysicalInterfaceTypes = + [ + NetworkInterfaceType.Ethernet, + NetworkInterfaceType.Wireless80211, + NetworkInterfaceType.GigabitEthernet, + NetworkInterfaceType.FastEthernetFx, + NetworkInterfaceType.FastEthernetT, + ]; + + private static readonly string[] _allowedPhysicalNameKeywords = + [ + // Windows / Generic + "ethernet", "wlan", "wi-fi", "wifi", + + // Linux + "eth", "enp", "ens", "eno", "enx", "wlp", "wlan", + + // macOS + "en0", "en1", "en2", "airport", + + // Chinese + "以太网", "无线", "局域网" + ]; + + private static readonly string[] _virtualNameBlacklistKeywords = + [ + // Generic + "virtual", "loopback", "loop back", + + // Common virtual adapters + "hyper-v", "vmware", "virtualbox", "vbox", "docker", "veth", "virbr", "cni", + "flannel", "cali", "kube", "wintun", "wireguard", "zerotier", "tailscale", "utun", + + // Chinese + "虚拟", "回环", "环回" + ]; + + public static List GetLocalNetworkInterfaceNames() + { + try + { + return NetworkInterface.GetAllNetworkInterfaces() + .Where(IsAllowedPhysicalInterface) + .Select(x => x.Name) + .Where(x => !string.IsNullOrWhiteSpace(x)) + .Distinct(StringComparer.OrdinalIgnoreCase) + .OrderBy(x => x) + .ToList(); + } + catch + { + return []; + } + } + + private static bool IsAllowedPhysicalInterface(NetworkInterface ni) + { + if (ni.OperationalStatus != OperationalStatus.Up) + { + return false; + } + + var interfaceText = $"{ni.Name} {ni.Description}"; + if (_virtualNameBlacklistKeywords.Any(keyword => interfaceText.Contains(keyword, StringComparison.OrdinalIgnoreCase))) + { + return false; + } + + if (_allowedPhysicalInterfaceTypes.Contains(ni.NetworkInterfaceType)) + { + return true; + } + + return _allowedPhysicalNameKeywords.Any(keyword => interfaceText.Contains(keyword, StringComparison.OrdinalIgnoreCase)); + } +} diff --git a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml index 732b579c..046b0e49 100644 --- a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml +++ b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml @@ -53,7 +53,8 @@ Grid.Row="0" Grid.Column="1" Width="200" - Margin="{StaticResource Margin4}" /> + Margin="{StaticResource Margin4}" + HorizontalAlignment="Left" /> + Margin="{StaticResource Margin4}" + HorizontalAlignment="Left" /> + Margin="{StaticResource Margin4}" + HorizontalAlignment="Left" /> - + Width="300" + Margin="{StaticResource Margin4}" + IsEditable="True" /> { - private static Config _config; + private static Config _config; public OptionSettingWindow() { @@ -59,6 +59,7 @@ public partial class OptionSettingWindow : WindowBase cmbRoutingRulesSourceUrl.ItemsSource = Global.RoutingRulesSources; cmbIPAPIUrl.ItemsSource = Global.IPAPIUrls; cmbRootCertificateProvider.ItemsSource = Global.RootCertProviders; + cmbbindInterface.ItemsSource = NetworkUtils.GetLocalNetworkInterfaceNames().AppendEmpty(); cmbMainGirdOrientation.ItemsSource = Utils.GetEnumNames(); @@ -81,7 +82,7 @@ public partial class OptionSettingWindow : WindowBase this.Bind(ViewModel, vm => vm.Loglevel, v => v.cmbloglevel.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.DefFingerprint, v => v.cmbdefFingerprint.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.DefUserAgent, v => v.cmbdefUserAgent.SelectedValue).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.BindInterface, v => v.txtbindInterface.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.BindInterface, v => v.cmbbindInterface.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.Mux4RayConcurrency, v => v.txtmux4RayConcurrency.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.Mux4RayXudpConcurrency, v => v.txtmux4RayXudpConcurrency.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.Mux4RayXudpProxyUDP443, v => v.cmbmux4RayXudpProxyUDP443.SelectedValue).DisposeWith(disposables); diff --git a/v2rayN/v2rayN/Views/OptionSettingWindow.xaml b/v2rayN/v2rayN/Views/OptionSettingWindow.xaml index 7fe9511e..7d96bbeb 100644 --- a/v2rayN/v2rayN/Views/OptionSettingWindow.xaml +++ b/v2rayN/v2rayN/Views/OptionSettingWindow.xaml @@ -497,13 +497,14 @@ VerticalAlignment="Center" Style="{StaticResource ToolbarTextBlock}" Text="{x:Static resx:ResUI.TbSettingsBindInterface}" /> - + IsEditable="True" + Style="{StaticResource DefComboBox}" /> ();