11using System ;
22using System . Collections . Generic ;
33using System . ComponentModel ;
4+ using System . Diagnostics ;
45using System . Linq ;
56using System . Net ;
67using System . Net . NetworkInformation ;
@@ -16,17 +17,20 @@ namespace NETworkManager.Models.Network;
1617/// </summary>
1718public sealed class NetworkInterface
1819{
19- #region Events
20-
21- /// <summary>
22- /// Occurs when the user has canceled an operation (e.g. UAC prompt).
23- /// </summary>
24- public event EventHandler UserHasCanceled ;
25-
26- private void OnUserHasCanceled ( )
27- {
28- UserHasCanceled ? . Invoke ( this , EventArgs . Empty ) ;
29- }
20+ #region Variables
21+
22+ /* Ref #3286
23+ private static List<string> NetworkInterfacesBlacklist =
24+ [
25+ "Hyper-V Virtual Switch Extension Filter",
26+ "WFP Native MAC Layer LightWeight Filter",
27+ "Npcap Packet Driver (NPCAP)",
28+ "QoS Packet Scheduler",
29+ "WFP 802.3 MAC Layer LightWeight Filter",
30+ "Ethernet (Kerneldebugger)",
31+ "Filter Driver"
32+ ];
33+ */
3034
3135 #endregion
3236
@@ -47,7 +51,7 @@ public static Task<List<NetworkInterfaceInfo>> GetNetworkInterfacesAsync()
4751 /// <returns>A list of <see cref="NetworkInterfaceInfo"/> describing the available network interfaces.</returns>
4852 public static List < NetworkInterfaceInfo > GetNetworkInterfaces ( )
4953 {
50- List < NetworkInterfaceInfo > listNetworkInterfaceInfo = new ( ) ;
54+ List < NetworkInterfaceInfo > listNetworkInterfaceInfo = [ ] ;
5155
5256 foreach ( var networkInterface in System . Net . NetworkInformation . NetworkInterface . GetAllNetworkInterfaces ( ) )
5357 {
@@ -58,6 +62,13 @@ public static List<NetworkInterfaceInfo> GetNetworkInterfaces()
5862 ( int ) networkInterface . NetworkInterfaceType != 53 )
5963 continue ;
6064
65+ // Check if part of the Name is in blacklist Ref #3286
66+ //if (NetworkInterfacesBlacklist.Any(networkInterface.Name.Contains))
67+ // continue;
68+
69+ //Debug.WriteLine(networkInterface.Name);
70+ //Debug.WriteLine($" Description: {networkInterface.Description}");
71+
6172 var listIPv4Address = new List < Tuple < IPAddress , IPAddress > > ( ) ;
6273 var listIPv6AddressLinkLocal = new List < IPAddress > ( ) ;
6374 var listIPv6Address = new List < IPAddress > ( ) ;
@@ -107,7 +118,7 @@ public static List<NetworkInterfaceInfo> GetNetworkInterfaces()
107118 }
108119 }
109120
110- // Check if autoconfiguration for DNS is enabled (only via registry key)
121+ // Check if autoconfiguration for DNS is enabled (only possible via registry key)
111122 var nameServerKey =
112123 Registry . LocalMachine . OpenSubKey (
113124 $@ "SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\{ networkInterface . Id } ") ;
@@ -150,47 +161,47 @@ public static List<NetworkInterfaceInfo> GetNetworkInterfaces()
150161 IsOperational = networkInterface . OperationalStatus == OperationalStatus . Up ,
151162 Speed = networkInterface . Speed ,
152163 IPv4ProtocolAvailable = ipv4ProtocolAvailable ,
153- IPv4Address = listIPv4Address . ToArray ( ) ,
154- IPv4Gateway = listIPv4Gateway . ToArray ( ) ,
164+ IPv4Address = [ .. listIPv4Address ] ,
165+ IPv4Gateway = [ .. listIPv4Gateway ] ,
155166 DhcpEnabled = ipv4Properties is { IsDhcpEnabled : true } ,
156- DhcpServer = ipProperties . DhcpServerAddresses . Where ( dhcpServerIPAddress =>
157- dhcpServerIPAddress . AddressFamily == AddressFamily . InterNetwork ) . ToArray ( ) ,
167+ DhcpServer = [ .. ipProperties . DhcpServerAddresses . Where ( dhcpServerIPAddress =>
168+ dhcpServerIPAddress . AddressFamily == AddressFamily . InterNetwork ) ] ,
158169 DhcpLeaseObtained = dhcpLeaseObtained ,
159170 DhcpLeaseExpires = dhcpLeaseExpires ,
160171 IPv6ProtocolAvailable = ipv6ProtocolAvailable ,
161- IPv6AddressLinkLocal = listIPv6AddressLinkLocal . ToArray ( ) ,
162- IPv6Address = listIPv6Address . ToArray ( ) ,
163- IPv6Gateway = listIPv6Gateway . ToArray ( ) ,
172+ IPv6AddressLinkLocal = [ .. listIPv6AddressLinkLocal ] ,
173+ IPv6Address = [ .. listIPv6Address ] ,
174+ IPv6Gateway = [ .. listIPv6Gateway ] ,
164175 DNSAutoconfigurationEnabled = dnsAutoconfigurationEnabled ,
165176 DNSSuffix = ipProperties . DnsSuffix ,
166- DNSServer = ipProperties . DnsAddresses . ToArray ( )
177+ DNSServer = [ .. ipProperties . DnsAddresses ]
167178 } ) ;
168179 }
169180
170181 return listNetworkInterfaceInfo ;
171182 }
172183
173184 /// <summary>
174- /// Detects the local IP address based on routing to a remote IP address asynchronously.
185+ /// Detects the local IP address from routing to a remote IP address asynchronously.
175186 /// </summary>
176187 /// <param name="remoteIPAddress">The remote IP address to check routing against.</param>
177- /// <returns>A task that represents the asynchronous operation. The task result contains the local <see cref="IPAddress"/> used to reach the remote address.</returns>
188+ /// <returns>A task that represents the asynchronous operation.
189+ /// The task result contains the local <see cref="IPAddress"/> used to reach the remote address or null on error.</returns>
178190 public static Task < IPAddress > DetectLocalIPAddressBasedOnRoutingAsync ( IPAddress remoteIPAddress )
179191 {
180- return Task . Run ( ( ) => DetectLocalIPAddressBasedOnRouting ( remoteIPAddress ) ) ;
192+ return Task . Run ( ( ) => DetectLocalIPAddressFromRouting ( remoteIPAddress ) ) ;
181193 }
182194
183195 /// <summary>
184- /// Detects the local IP address based on routing to a remote IP address.
196+ /// Detects the local IP address from routing to a remote IP address.
185197 /// </summary>
186198 /// <param name="remoteIPAddress">The remote IP address to check routing against.</param>
187- /// <returns>The local <see cref="IPAddress"/> used to reach the remote address.</returns>
188- private static IPAddress DetectLocalIPAddressBasedOnRouting ( IPAddress remoteIPAddress )
199+ /// <returns>The local <see cref="IPAddress"/> used to reach the remote address or null on error .</returns>
200+ private static IPAddress DetectLocalIPAddressFromRouting ( IPAddress remoteIPAddress )
189201 {
190202 var isIPv4 = remoteIPAddress . AddressFamily == AddressFamily . InterNetwork ;
191203
192- using var socket = new Socket ( isIPv4 ? AddressFamily . InterNetwork : AddressFamily . InterNetworkV6 ,
193- SocketType . Dgram , ProtocolType . Udp ) ;
204+ using var socket = new Socket ( remoteIPAddress . AddressFamily , SocketType . Dgram , ProtocolType . Udp ) ;
194205
195206 // return null on error...
196207 try
@@ -201,45 +212,123 @@ private static IPAddress DetectLocalIPAddressBasedOnRouting(IPAddress remoteIPAd
201212 if ( socket . LocalEndPoint is IPEndPoint ipAddress )
202213 return ipAddress . Address ;
203214 }
204- catch ( SocketException )
215+ catch ( SocketException ) { }
216+
217+ return null ;
218+ }
219+
220+ /// <summary>
221+ /// Asynchronously detects the local IP address associated with a network interface that matches the specified
222+ /// address family.
223+ /// </summary>
224+ /// <param name="addressFamily">The address family to use when searching for a local IP address. Typically, use AddressFamily.InterNetwork for
225+ /// IPv4 or AddressFamily.InterNetworkV6 for IPv6.</param>
226+ /// <returns>A task that represents the asynchronous operation. The task result contains the detected local IP address, or
227+ /// null if no suitable address is found.</returns>
228+ public static Task < IPAddress > DetectLocalIPAddressFromNetworkInterfaceAsync ( AddressFamily addressFamily )
229+ {
230+ return Task . Run ( ( ) => DetectLocalIPAddressFromNetworkInterface ( addressFamily ) ) ;
231+ }
232+
233+ /// <summary>
234+ /// Detects and returns the first local IP address assigned to an operational network interface that matches the
235+ /// specified address family.
236+ /// </summary>
237+ /// <remarks>For IPv4, the method prefers non-link-local addresses but will return a link-local address if
238+ /// no other is available. For IPv6, the method returns the first global or unique local address if present;
239+ /// otherwise, it returns a link-local address if available. The returned address is selected from operational
240+ /// network interfaces only.</remarks>
241+ /// <param name="addressFamily">The address family to search for. Specify <see cref="AddressFamily.InterNetwork"/> for IPv4 addresses or <see
242+ /// cref="AddressFamily.InterNetworkV6"/> for IPv6 addresses.</param>
243+ /// <returns>An <see cref="IPAddress"/> representing the first detected local IP address for the specified address family, or
244+ /// <see langword="null"/> if no suitable address is found.</returns>
245+ public static IPAddress DetectLocalIPAddressFromNetworkInterface ( AddressFamily addressFamily )
246+ {
247+ // Filter operational network interfaces
248+ var networkInterfaces = GetNetworkInterfaces ( )
249+ . Where ( x => x . IsOperational ) ;
250+
251+ var candidates = new List < IPAddress > ( ) ;
252+
253+ // IPv4
254+ if ( addressFamily == AddressFamily . InterNetwork )
255+ {
256+ foreach ( var networkInterface in networkInterfaces )
257+ {
258+ foreach ( var ipAddress in networkInterface . IPv4Address )
259+ candidates . Add ( ipAddress . Item1 ) ;
260+ }
261+
262+ // Prefer non-link-local addresses
263+ var nonLinkLocal = candidates . Where ( x =>
264+ {
265+ var bytes = x . GetAddressBytes ( ) ;
266+
267+ return ! ( bytes [ 0 ] == 169 && bytes [ 1 ] == 254 ) ;
268+ } ) ;
269+
270+ // Return first non-link-local or first candidate if none found (might be null - no addresses at all)
271+ return nonLinkLocal . Any ( ) ? nonLinkLocal . First ( ) : candidates . First ( ) ;
272+ }
273+
274+ // IPv6
275+ if ( addressFamily == AddressFamily . InterNetworkV6 )
205276 {
277+ // First try to get global or unique local addresses
278+ foreach ( var networkInterface in networkInterfaces )
279+ {
280+ candidates . AddRange ( networkInterface . IPv6Address ) ;
281+ }
282+
283+ // Return first candidate if any found
284+ if ( candidates . Count != 0 )
285+ return candidates . First ( ) ;
286+
287+ // Fallback to link-local addresses
288+ foreach ( var networkInterface in networkInterfaces )
289+ {
290+ if ( networkInterface . IPv6AddressLinkLocal . Length != 0 )
291+ return networkInterface . IPv6AddressLinkLocal . First ( ) ;
292+ }
206293 }
207294
208295 return null ;
209296 }
210297
211298 /// <summary>
212- /// Detects the gateway IP address based on a local IP address asynchronously.
299+ /// Detects the gateway IP address from a local IP address asynchronously.
213300 /// </summary>
214301 /// <param name="localIPAddress">The local IP address to find the gateway for.</param>
215- /// <returns>A task that represents the asynchronous operation. The task result contains the gateway <see cref="IPAddress"/>.</returns>
216- public static Task < IPAddress > DetectGatewayBasedOnLocalIPAddressAsync ( IPAddress localIPAddress )
302+ /// <returns>A task that represents the asynchronous operation.
303+ /// The task result contains the gateway as <see cref="IPAddress"/> or null if not found.</returns>
304+ public static Task < IPAddress > DetectGatewayFromLocalIPAddressAsync ( IPAddress localIPAddress )
217305 {
218- return Task . Run ( ( ) => DetectGatewayBasedOnLocalIPAddress ( localIPAddress ) ) ;
306+ return Task . Run ( ( ) => DetectGatewayFromLocalIPAddress ( localIPAddress ) ) ;
219307 }
220308
221309 /// <summary>
222- /// Detects the gateway IP address based on a local IP address.
310+ /// Detects the gateway IP address from a local IP address.
223311 /// </summary>
224312 /// <param name="localIPAddress">The local IP address to find the gateway for.</param>
225- /// <returns>The gateway <see cref="IPAddress"/>.</returns>
226- private static IPAddress DetectGatewayBasedOnLocalIPAddress ( IPAddress localIPAddress )
313+ /// <returns>The gateway as <see cref="IPAddress"/> or null if not found .</returns>
314+ private static IPAddress DetectGatewayFromLocalIPAddress ( IPAddress localIPAddress )
227315 {
228316 foreach ( var networkInterface in GetNetworkInterfaces ( ) )
317+ {
318+ // IPv4
229319 if ( localIPAddress . AddressFamily == AddressFamily . InterNetwork )
230320 {
231321 if ( networkInterface . IPv4Address . Any ( x => x . Item1 . Equals ( localIPAddress ) ) )
232322 return networkInterface . IPv4Gateway . FirstOrDefault ( ) ;
233323 }
234- else if ( localIPAddress . AddressFamily == AddressFamily . InterNetworkV6 )
324+
325+ // IPv6
326+ if ( localIPAddress . AddressFamily == AddressFamily . InterNetworkV6 )
235327 {
236328 if ( networkInterface . IPv6Address . Contains ( localIPAddress ) )
237329 return networkInterface . IPv6Gateway . FirstOrDefault ( ) ;
238- }
239- else
240- {
241- throw new Exception ( "IPv4 or IPv6 address is required to detect the gateway." ) ;
242- }
330+ }
331+ }
243332
244333 return null ;
245334 }
@@ -394,4 +483,20 @@ private static void RemoveIPAddressFromNetworkInterface(NetworkInterfaceConfig c
394483 }
395484
396485 #endregion
486+
487+
488+ #region Events
489+
490+ /// <summary>
491+ /// Occurs when the user has canceled an operation (e.g. UAC prompt).
492+ /// </summary>
493+ public event EventHandler UserHasCanceled ;
494+
495+ private void OnUserHasCanceled ( )
496+ {
497+ UserHasCanceled ? . Invoke ( this , EventArgs . Empty ) ;
498+ }
499+
500+ #endregion
501+
397502}
0 commit comments