/* * Copyright (c) 2025 Proton AG * * This file is part of ProtonVPN. * * ProtonVPN is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * ProtonVPN is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ProtonVPN. If not, see . */ using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; using ProtonVPN.Common.Core.Networking; using ProtonVPN.OperatingSystems.Network.Contracts; using Vanara.PInvoke; using static Vanara.PInvoke.IpHlpApi; using static Vanara.PInvoke.Ws2_32; namespace ProtonVPN.OperatingSystems.Network.NetworkInterfaces; /// /// Provides access to network interface on the system. /// public class SystemNetworkInterface : INetworkInterface, IEquatable { private readonly NetworkInterface _networkInterface; public SystemNetworkInterface(NetworkInterface networkInterface) { _networkInterface = networkInterface ?? throw new ArgumentNullException(nameof(networkInterface), "NetworkInterface cannot be null."); } public string Id => _networkInterface.Id; public string Name => _networkInterface.Name; public string Description => _networkInterface.Description; public bool IsLoopback => _networkInterface.NetworkInterfaceType == NetworkInterfaceType.Loopback; public bool IsActive => _networkInterface.OperationalStatus == OperationalStatus.Up; public bool IsIPv4ForwardingEnabled { get { MIB_IPINTERFACE_ROW row = new() { Family = ADDRESS_FAMILY.AF_INET, InterfaceIndex = Index, }; Win32Error result = GetIpInterfaceEntry(ref row); return result.Succeeded && row.ForwardingEnabled; } } public IPAddress DefaultGateway { get { GatewayIPAddressInformation? ipv4Gateway = _networkInterface .GetIPProperties() .GatewayAddresses .FirstOrDefault(g => g.Address.AddressFamily == AddressFamily.InterNetwork && !g.Address.Equals(IPAddress.Any)); return ipv4Gateway?.Address ?? IPAddress.None; } } public uint Index { get { try { IPv4InterfaceProperties ipv4Props = _networkInterface.GetIPProperties().GetIPv4Properties(); if (ipv4Props != null) { return Convert.ToUInt32(ipv4Props.Index); } IPv6InterfaceProperties ipv6Props = _networkInterface.GetIPProperties().GetIPv6Properties(); if (ipv6Props != null) { return Convert.ToUInt32(ipv6Props.Index); } } catch (Exception) { } return 0; } } public List GetUnicastAddresses() { return _networkInterface .GetIPProperties().UnicastAddresses .Select(a => new NetworkAddress(a.Address)) .ToList(); } public IPAddress? GetPreferredIpv6UnicastAddress() { UnicastIPAddressInformationCollection unicastAddresses = _networkInterface.GetIPProperties().UnicastAddresses; List eligibleAddresses = unicastAddresses .Where(info => info.Address.AddressFamily == AddressFamily.InterNetworkV6) .Where(info => info.DuplicateAddressDetectionState == DuplicateAddressDetectionState.Preferred) .Where(info => info.AddressPreferredLifetime > 0) .Where(info => !info.Address.Equals(IPAddress.IPv6None)) .Where(info => !info.Address.IsIPv6LinkLocal) .Where(info => !info.Address.IsIPv6Multicast) .ToList(); UnicastIPAddressInformation? preferredTemporaryAddress = eligibleAddresses .Where(info => info.SuffixOrigin == SuffixOrigin.Random) .OrderByDescending(info => info.AddressPreferredLifetime) .FirstOrDefault(); if (preferredTemporaryAddress is not null) { return preferredTemporaryAddress.Address; } UnicastIPAddressInformation? preferredStableAddress = eligibleAddresses .Where(info => info.SuffixOrigin != SuffixOrigin.Random) .OrderByDescending(info => info.AddressPreferredLifetime) .FirstOrDefault(); return preferredStableAddress?.Address; } public bool Equals(SystemNetworkInterface? other) { if (other is null) { return false; } if (ReferenceEquals(this, other)) { return true; } return Id == other.Id; } public override bool Equals(object? obj) { return Equals(obj as SystemNetworkInterface); } public override int GetHashCode() { return Id != null ? Id.GetHashCode() : 0; } }