using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; using NetworkTool.Client.Models; namespace NetworkTool.Client.Services; public sealed class NetworkAdapterService { public IReadOnlyList GetEthernetAdapters() { return NetworkInterface .GetAllNetworkInterfaces() .Where(IsSupportedEthernetAdapter) .Select(BuildAdapterInfo) .OrderByDescending(adapter => adapter.RecommendationScore) .ThenBy(adapter => adapter.Name) .ToList(); } public AdapterInfo? GetRecommendedAdapter(IReadOnlyList adapters) { return adapters .OrderByDescending(adapter => adapter.RecommendationScore) .ThenBy(adapter => adapter.Name) .FirstOrDefault(); } public async Task ProbeMaintenanceReachabilityAsync(IReadOnlyList adapters, CancellationToken cancellationToken = default) { foreach (var adapter in adapters) { if (!adapter.HasLink || string.IsNullOrWhiteSpace(adapter.IPv4Address)) { adapter.ProbeStatus = "不可达"; adapter.ProbeReason = "该网卡当前没有可用的 IPv4 或链路未连接。"; adapter.IsReachableToMaintenance = false; continue; } var reachable = await CanReachMaintenanceAsync(adapter.IPv4Address, cancellationToken); adapter.IsReachableToMaintenance = reachable; if (reachable) { adapter.ProbeStatus = "可达"; adapter.ProbeReason = "该网卡可连接到管理口 169.254.100.2:48888。"; adapter.RecommendationScore += 100; adapter.RecommendationLabel = "推荐"; adapter.RecommendationReason = "该网卡已实际探测到可与 Linux 管理口建立连接,优先使用。"; } else { adapter.ProbeStatus = "未通"; adapter.ProbeReason = "该网卡当前无法连接到管理口 169.254.100.2:48888。"; } } } private static bool IsSupportedEthernetAdapter(NetworkInterface adapter) { if (adapter.NetworkInterfaceType is not NetworkInterfaceType.Ethernet and not NetworkInterfaceType.GigabitEthernet) { return false; } return adapter.Description.Contains("VMware", System.StringComparison.OrdinalIgnoreCase) || !adapter.Description.Contains("Virtual", System.StringComparison.OrdinalIgnoreCase); } private static AdapterInfo BuildAdapterInfo(NetworkInterface adapter) { var ipv4Address = adapter .GetIPProperties() .UnicastAddresses .FirstOrDefault(address => address.Address.AddressFamily == AddressFamily.InterNetwork) ?.Address .ToString() ?? string.Empty; var score = GetRecommendationScore(adapter, ipv4Address); var (label, reason) = GetRecommendation(score, adapter, ipv4Address); return AdapterInfo.FromNetworkInterface(adapter, ipv4Address, score, label, reason); } private static int GetRecommendationScore(NetworkInterface adapter, string ipv4Address) { var score = 0; var description = adapter.Description; var isVirtual = IsVirtualAdapter(description); if (adapter.NetworkInterfaceType is NetworkInterfaceType.Ethernet or NetworkInterfaceType.GigabitEthernet) { score += 50; } if (adapter.OperationalStatus == OperationalStatus.Up) { score += 40; } if (ipv4Address.StartsWith("169.254.", System.StringComparison.Ordinal)) { score += 30; } if (!isVirtual) { score += 20; } if (description.Contains("Intel", System.StringComparison.OrdinalIgnoreCase) || description.Contains("Realtek", System.StringComparison.OrdinalIgnoreCase) || description.Contains("Broadcom", System.StringComparison.OrdinalIgnoreCase)) { score += 10; } if (isVirtual) { score -= 40; } return score; } private static (string Label, string Reason) GetRecommendation(int score, NetworkInterface adapter, string ipv4Address) { if (score >= 90) { if (ipv4Address.StartsWith("169.254.", System.StringComparison.Ordinal)) { return ("推荐", "已连接且当前 IPv4 为 169.254 网段,最像初始化直连网卡。"); } return ("推荐", "已检测到已连接的有线网卡,适合作为初始化连接口。"); } if (score >= 40) { return ("可选", "该网卡可用,但不是最优候选,请确认是否为实际直连接口。"); } return ("不建议", "该网卡更像虚拟或非直连接口,通常不建议用于设备初始化。"); } private static bool IsVirtualAdapter(string description) { return description.Contains("VMware", System.StringComparison.OrdinalIgnoreCase) || description.Contains("Virtual", System.StringComparison.OrdinalIgnoreCase) || description.Contains("Hyper-V", System.StringComparison.OrdinalIgnoreCase) || description.Contains("TAP", System.StringComparison.OrdinalIgnoreCase) || description.Contains("VPN", System.StringComparison.OrdinalIgnoreCase) || description.Contains("Loopback", System.StringComparison.OrdinalIgnoreCase); } private static async Task CanReachMaintenanceAsync(string localIPv4, CancellationToken cancellationToken) { try { using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); socket.Bind(new IPEndPoint(IPAddress.Parse(localIPv4), 0)); using var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); timeoutCts.CancelAfter(TimeSpan.FromMilliseconds(500)); await socket.ConnectAsync(IPAddress.Parse("169.254.100.2"), 48888, timeoutCts.Token); return socket.Connected; } catch { return false; } } }