using System.Diagnostics; using System.Net.NetworkInformation; using System.Runtime.InteropServices; using Microsoft.Extensions.Logging; namespace QemuVmManager.Core; public interface INetworkManager { Task IsBridgeAvailableAsync(string bridgeName); Task CreateBridgeAsync(string bridgeName, string? interfaceName = null); Task DeleteBridgeAsync(string bridgeName); Task> GetAvailableBridgesAsync(); Task> GetNetworkInterfacesAsync(); Task AddInterfaceToBridgeAsync(string bridgeName, string interfaceName); Task RemoveInterfaceFromBridgeAsync(string bridgeName, string interfaceName); Task GetBridgeIpAddressAsync(string bridgeName); Task ConfigureBridgeAsync(string bridgeName, string ipAddress, string netmask); } public class NetworkManager : INetworkManager { private readonly ILogger? _logger; public NetworkManager(ILogger? logger = null) { _logger = logger; } public async Task IsBridgeAvailableAsync(string bridgeName) { try { if (OperatingSystem.IsLinux()) { return await CheckLinuxBridgeAsync(bridgeName); } else if (OperatingSystem.IsWindows()) { return await CheckWindowsBridgeAsync(bridgeName); } else if (OperatingSystem.IsMacOS()) { return await CheckMacOSBridgeAsync(bridgeName); } return false; } catch (Exception ex) { _logger?.LogWarning(ex, "Failed to check bridge availability for {BridgeName}", bridgeName); return false; } } public async Task CreateBridgeAsync(string bridgeName, string? interfaceName = null) { try { if (OperatingSystem.IsLinux()) { return await CreateLinuxBridgeAsync(bridgeName, interfaceName); } else if (OperatingSystem.IsWindows()) { return await CreateWindowsBridgeAsync(bridgeName, interfaceName); } else if (OperatingSystem.IsMacOS()) { return await CreateMacOSBridgeAsync(bridgeName, interfaceName); } return false; } catch (Exception ex) { _logger?.LogError(ex, "Failed to create bridge {BridgeName}", bridgeName); return false; } } public async Task DeleteBridgeAsync(string bridgeName) { try { if (OperatingSystem.IsLinux()) { return await DeleteLinuxBridgeAsync(bridgeName); } else if (OperatingSystem.IsWindows()) { return await DeleteWindowsBridgeAsync(bridgeName); } else if (OperatingSystem.IsMacOS()) { return await DeleteMacOSBridgeAsync(bridgeName); } return false; } catch (Exception ex) { _logger?.LogError(ex, "Failed to delete bridge {BridgeName}", bridgeName); return false; } } public async Task> GetAvailableBridgesAsync() { var bridges = new List(); try { if (OperatingSystem.IsLinux()) { bridges.AddRange(await GetLinuxBridgesAsync()); } else if (OperatingSystem.IsWindows()) { bridges.AddRange(await GetWindowsBridgesAsync()); } else if (OperatingSystem.IsMacOS()) { bridges.AddRange(await GetMacOSBridgesAsync()); } } catch (Exception ex) { _logger?.LogWarning(ex, "Failed to get available bridges"); } return bridges; } public async Task> GetNetworkInterfacesAsync() { var interfaces = new List(); try { var networkInterfaces = NetworkInterface.GetAllNetworkInterfaces(); foreach (var ni in networkInterfaces) { // Filter out loopback and virtual interfaces if (ni.NetworkInterfaceType != NetworkInterfaceType.Loopback && ni.OperationalStatus == OperationalStatus.Up && !ni.Name.Contains("vEthernet") && !ni.Name.Contains("VirtualBox") && !ni.Name.Contains("VMware")) { interfaces.Add(ni.Name); } } } catch (Exception ex) { _logger?.LogWarning(ex, "Failed to get network interfaces"); } return interfaces; } public async Task AddInterfaceToBridgeAsync(string bridgeName, string interfaceName) { try { if (OperatingSystem.IsLinux()) { return await AddInterfaceToLinuxBridgeAsync(bridgeName, interfaceName); } else if (OperatingSystem.IsWindows()) { return await AddInterfaceToWindowsBridgeAsync(bridgeName, interfaceName); } else if (OperatingSystem.IsMacOS()) { return await AddInterfaceToMacOSBridgeAsync(bridgeName, interfaceName); } return false; } catch (Exception ex) { _logger?.LogError(ex, "Failed to add interface {InterfaceName} to bridge {BridgeName}", interfaceName, bridgeName); return false; } } public async Task RemoveInterfaceFromBridgeAsync(string bridgeName, string interfaceName) { try { if (OperatingSystem.IsLinux()) { return await RemoveInterfaceFromLinuxBridgeAsync(bridgeName, interfaceName); } else if (OperatingSystem.IsWindows()) { return await RemoveInterfaceFromWindowsBridgeAsync(bridgeName, interfaceName); } else if (OperatingSystem.IsMacOS()) { return await RemoveInterfaceFromMacOSBridgeAsync(bridgeName, interfaceName); } return false; } catch (Exception ex) { _logger?.LogError(ex, "Failed to remove interface {InterfaceName} from bridge {BridgeName}", interfaceName, bridgeName); return false; } } public async Task GetBridgeIpAddressAsync(string bridgeName) { try { var networkInterfaces = NetworkInterface.GetAllNetworkInterfaces(); foreach (var ni in networkInterfaces) { if (ni.Name.Equals(bridgeName, StringComparison.OrdinalIgnoreCase)) { var ipProps = ni.GetIPProperties(); var unicastAddresses = ipProps.UnicastAddresses; foreach (var addr in unicastAddresses) { if (addr.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) { return addr.Address.ToString(); } } } } return null; } catch (Exception ex) { _logger?.LogWarning(ex, "Failed to get IP address for bridge {BridgeName}", bridgeName); return null; } } public async Task ConfigureBridgeAsync(string bridgeName, string ipAddress, string netmask) { try { if (OperatingSystem.IsLinux()) { return await ConfigureLinuxBridgeAsync(bridgeName, ipAddress, netmask); } else if (OperatingSystem.IsWindows()) { return await ConfigureWindowsBridgeAsync(bridgeName, ipAddress, netmask); } else if (OperatingSystem.IsMacOS()) { return await ConfigureMacOSBridgeAsync(bridgeName, ipAddress, netmask); } return false; } catch (Exception ex) { _logger?.LogError(ex, "Failed to configure bridge {BridgeName}", bridgeName); return false; } } // Linux-specific implementations private async Task CheckLinuxBridgeAsync(string bridgeName) { var process = new Process { StartInfo = new ProcessStartInfo { FileName = "brctl", Arguments = $"show {bridgeName}", UseShellExecute = false, RedirectStandardOutput = true, CreateNoWindow = true } }; var started = process.Start(); if (!started) return false; await process.WaitForExitAsync(); return process.ExitCode == 0; } private async Task CreateLinuxBridgeAsync(string bridgeName, string? interfaceName) { // Create bridge var createProcess = new Process { StartInfo = new ProcessStartInfo { FileName = "sudo", Arguments = $"brctl addbr {bridgeName}", UseShellExecute = false, CreateNoWindow = true } }; var started = createProcess.Start(); if (!started) return false; await createProcess.WaitForExitAsync(); if (createProcess.ExitCode != 0) return false; // Add interface if specified if (!string.IsNullOrEmpty(interfaceName)) { return await AddInterfaceToLinuxBridgeAsync(bridgeName, interfaceName); } return true; } private async Task DeleteLinuxBridgeAsync(string bridgeName) { var process = new Process { StartInfo = new ProcessStartInfo { FileName = "sudo", Arguments = $"brctl delbr {bridgeName}", UseShellExecute = false, CreateNoWindow = true } }; var started = process.Start(); if (!started) return false; await process.WaitForExitAsync(); return process.ExitCode == 0; } private async Task> GetLinuxBridgesAsync() { var bridges = new List(); var process = new Process { StartInfo = new ProcessStartInfo { FileName = "brctl", Arguments = "show", UseShellExecute = false, RedirectStandardOutput = true, CreateNoWindow = true } }; var started = process.Start(); if (!started) return bridges; var output = await process.StandardOutput.ReadToEndAsync(); await process.WaitForExitAsync(); if (process.ExitCode == 0) { var lines = output.Split('\n', StringSplitOptions.RemoveEmptyEntries); foreach (var line in lines.Skip(1)) // Skip header { var parts = line.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); if (parts.Length > 0) { bridges.Add(parts[0]); } } } return bridges; } private async Task AddInterfaceToLinuxBridgeAsync(string bridgeName, string interfaceName) { var process = new Process { StartInfo = new ProcessStartInfo { FileName = "sudo", Arguments = $"brctl addif {bridgeName} {interfaceName}", UseShellExecute = false, CreateNoWindow = true } }; var started = process.Start(); if (!started) return false; await process.WaitForExitAsync(); return process.ExitCode == 0; } private async Task RemoveInterfaceFromLinuxBridgeAsync(string bridgeName, string interfaceName) { var process = new Process { StartInfo = new ProcessStartInfo { FileName = "sudo", Arguments = $"brctl delif {bridgeName} {interfaceName}", UseShellExecute = false, CreateNoWindow = true } }; var started = process.Start(); if (!started) return false; await process.WaitForExitAsync(); return process.ExitCode == 0; } private async Task ConfigureLinuxBridgeAsync(string bridgeName, string ipAddress, string netmask) { var process = new Process { StartInfo = new ProcessStartInfo { FileName = "sudo", Arguments = $"ip addr add {ipAddress}/{netmask} dev {bridgeName}", UseShellExecute = false, CreateNoWindow = true } }; var started = process.Start(); if (!started) return false; await process.WaitForExitAsync(); return process.ExitCode == 0; } // Windows-specific implementations (simplified - would need more complex implementation) private async Task CheckWindowsBridgeAsync(string bridgeName) { // Windows bridge checking would require PowerShell or WMI // For now, return false as Windows bridge setup is more complex return false; } private async Task CreateWindowsBridgeAsync(string bridgeName, string? interfaceName) { // Windows bridge creation requires PowerShell or Hyper-V // For now, return false as this requires more complex implementation return false; } private async Task DeleteWindowsBridgeAsync(string bridgeName) { // Windows bridge deletion requires PowerShell or Hyper-V return false; } private async Task> GetWindowsBridgesAsync() { // Windows bridge enumeration would require PowerShell or WMI return new List(); } private async Task AddInterfaceToWindowsBridgeAsync(string bridgeName, string interfaceName) { return false; } private async Task RemoveInterfaceFromWindowsBridgeAsync(string bridgeName, string interfaceName) { return false; } private async Task ConfigureWindowsBridgeAsync(string bridgeName, string ipAddress, string netmask) { return false; } // macOS-specific implementations private async Task CheckMacOSBridgeAsync(string bridgeName) { try { var process = new Process { StartInfo = new ProcessStartInfo { FileName = "ifconfig", Arguments = bridgeName, UseShellExecute = false, RedirectStandardOutput = true, CreateNoWindow = true } }; var started = process.Start(); if (!started) return false; await process.WaitForExitAsync(); return process.ExitCode == 0; } catch { return false; } } private async Task CreateMacOSBridgeAsync(string bridgeName, string? interfaceName) { try { // On macOS, bridge creation is complex and typically requires system configuration // For now, we'll check if the bridge already exists and return true if it does var exists = await CheckMacOSBridgeAsync(bridgeName); if (exists) { _logger?.LogInformation("Bridge {BridgeName} already exists on macOS", bridgeName); return true; } _logger?.LogWarning("Bridge creation on macOS requires manual system configuration"); _logger?.LogInformation("You can use existing bridge0 or create bridges manually"); return false; } catch (Exception ex) { _logger?.LogError(ex, "Failed to create bridge {BridgeName} on macOS", bridgeName); return false; } } private async Task DeleteMacOSBridgeAsync(string bridgeName) { // Bridge deletion on macOS requires system configuration _logger?.LogWarning("Bridge deletion on macOS requires manual system configuration"); return false; } private async Task> GetMacOSBridgesAsync() { var bridges = new List(); try { var process = new Process { StartInfo = new ProcessStartInfo { FileName = "ifconfig", Arguments = "-a", UseShellExecute = false, RedirectStandardOutput = true, CreateNoWindow = true } }; var started = process.Start(); if (!started) return bridges; var output = await process.StandardOutput.ReadToEndAsync(); await process.WaitForExitAsync(); if (process.ExitCode == 0) { var lines = output.Split('\n', StringSplitOptions.RemoveEmptyEntries); foreach (var line in lines) { if (line.Contains("bridge") && line.Contains(":")) { var parts = line.Split(':'); if (parts.Length > 0) { var bridgeName = parts[0].Trim(); if (!string.IsNullOrEmpty(bridgeName) && !bridges.Contains(bridgeName)) { bridges.Add(bridgeName); } } } } } } catch (Exception ex) { _logger?.LogWarning(ex, "Failed to get macOS bridges"); } return bridges; } private async Task AddInterfaceToMacOSBridgeAsync(string bridgeName, string interfaceName) { // Interface management on macOS bridges requires system configuration _logger?.LogWarning("Interface management on macOS bridges requires manual system configuration"); return false; } private async Task RemoveInterfaceFromMacOSBridgeAsync(string bridgeName, string interfaceName) { // Interface management on macOS bridges requires system configuration _logger?.LogWarning("Interface management on macOS bridges requires manual system configuration"); return false; } private async Task ConfigureMacOSBridgeAsync(string bridgeName, string ipAddress, string netmask) { try { var process = new Process { StartInfo = new ProcessStartInfo { FileName = "sudo", Arguments = $"ifconfig {bridgeName} {ipAddress} netmask 255.255.255.0", UseShellExecute = false, CreateNoWindow = true } }; var started = process.Start(); if (!started) return false; await process.WaitForExitAsync(); return process.ExitCode == 0; } catch (Exception ex) { _logger?.LogError(ex, "Failed to configure bridge {BridgeName} on macOS", bridgeName); return false; } } }