Cómo determinar la dirección MAC de la tarjeta de red física real, no las interfaces de red virtuales creadas por VPN (.NET C #)

Fondo

Estoy tratando de obtener un identificador único de una computadora y quiero poder devolver la misma dirección MAC de manera confiable cada vez. Créanme, tengo mis razones para usar la dirección MAC y he leído muchas publicaciones sobre métodos alternativos de identificación únicos (y sí, he considerado si no tienen ninguna tarjeta de red).

Problema

El problema está en .NET. No veo de todos modos si una NetworkInterface específica es una tarjeta de red de hardware físico de algo así como un “Nortel IPSECSHM Adapter – Packet Scheduler Miniport” que se agrega cuando se conecta a ciertas VPN o redes WiFi.

Sé cómo obtener las direcciones Mac usando un código similar a este:

foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces()) { log.Debug("NIC " + nic.OperationalStatus + " " + nic.NetworkInterfaceType + " " + nic.Speed + " " + nic.GetPhysicalAddress() + " " + nic.Description); } 

Es comprensible que no haya un 100% de seguridad para obtener una tarjeta de red interna, pero me gustaría elegir la dirección MAC para devolverla para una máquina determinada que es menos probable que cambie . Independiente de factores como, ya sea si está conectado a wifi … se conecta a través de algún tipo de conexión de atadura … o instalan un nuevo software vpn que agrega una nueva interfaz.

Estrategias consideradas

1) Elija la primera interfaz que sea “Arriba”. Esto falla en mi computadora portátil porque el “minipuerto de paquetes” siempre está activo. Además, si conecto mi teléfono a mi computadora portátil, esto también aparece como la primera tarjeta.

2) Elija el tipo más apropiado … Esto falla b / c, básicamente, todo se muestra como “Ethernet”, incluidos los adaptadores WiFi y mi conexión a Internet tethering iPHone.

3) Elija el NIC que tiene una dirección IP. Se produce un error por varias razones: 1) Es posible que la tarjeta de red no esté conectada a la LAN 2) Hay varios nics que pueden tener direcciones IP.

4) Simplemente envíe todas las direcciones MAC … El problema es que la lista cambiará en función del software instalado y será difícil de comparar.

5) Elija la dirección MAC con la velocidad más rápida. Creo que esta es probablemente mi mejor apuesta. Creo que es seguro decir que la interfaz más rápida suele ser la más permanente.

Alternativamente, puede haber alguna otra forma de detectar tarjetas físicas en .NET o consideraría invocar otras llamadas API si pudiera recomendar una que brinde información diferente.

¿Alguna otra idea?

Para demostrar aquí está el resultado de mi código de muestra anterior cuando tengo mi iPhone atado:

 DEBUG - NIC Down Ethernet 500000 0021E98BFBEF Apple Mobile Device Ethernet - Packet Scheduler Miniport DEBUG - NIC Up Ethernet 10000000 444553544200 Nortel IPSECSHM Adapter - Packet Scheduler Miniport DEBUG - NIC Down Ethernet 54000000 00166FAC94C7 Intel(R) PRO/Wireless 2200BG Network Connection - Packet Scheduler Miniport DEBUG - NIC Down Ethernet 1000000000 0016D326E957 Broadcom NetXtreme Gigabit Ethernet - Packet Scheduler Miniport DEBUG - NIC Up Loopback 10000000 MS TCP Loopback interface 

Sin Iphone conectado:

 DEBUG - NIC Up Ethernet 10000000 444553544200 Nortel IPSECSHM Adapter - Packet Scheduler Miniport DEBUG - NIC Down Ethernet 54000000 00166FAC94C7 Intel(R) PRO/Wireless 2200BG Network Connection - Packet Scheduler Miniport DEBUG - NIC Down Ethernet 1000000000 0016D326E957 Broadcom NetXtreme Gigabit Ethernet - Packet Scheduler Miniport DEBUG - NIC Up Loopback 10000000 MS TCP Loopback interface 

Este es mi método: utiliza el hecho de que la tarjeta física está conectada a la interfaz PCI

 ManagementObjectSearcher searcher = new ManagementObjectSearcher ("Select MACAddress,PNPDeviceID FROM Win32_NetworkAdapter WHERE MACAddress IS NOT NULL AND PNPDeviceID IS NOT NULL"); ManagementObjectCollection mObject = searcher.Get(); foreach (ManagementObject obj in mObject) { string pnp = obj["PNPDeviceID"].ToString(); if (pnp.Contains("PCI\\")) { string mac = obj["MACAddress"].ToString(); mac = mac.Replace(":", string.Empty); return mac; } } 

Los primeros tres bytes de la dirección MAC son una identificación del fabricante. Puede poner en la lista negra ciertas ID de fabricantes que se sabe que no son aptas para sus fines, e ignorar esas interfaces.

No es probable que valga la pena confiar en la velocidad, porque no hay ninguna razón por la que una interfaz VPN no pueda informarse a sí misma de que tiene velocidad de gigabits.

Las direcciones MAC son direcciones de hardware físico. No puedo probar en esta PC, pero no creo que obtenga una nueva dirección MAC si se agrega una conexión virtual porque no es una pieza de hardware real. Será otra conexión, pero no otra dirección MAC.

Por lo tanto, garantizar que obtenga la misma dirección MAC todo el tiempo depende de los mismos elementos de hardware conectados a la máquina y de que utilice el mismo algoritmo para elegir de ese hardware.

He estado viendo este problema también, y creo que sin un estado persistente va a ser difícil mantener un MAC estable.

La solución con la que estoy jugando es tomar el primer NIC que se encuentra en el orden del adaptador y usarlo y guardarlo . Luego, en las siguientes generaciones de UUID, use el MAC guardado si se encuentra en cualquier lugar de la stack, incluso si no es el primero. De esta forma, el orden del adaptador puede moverse y no nos preocupamos por explotar lo que dependa del MAC estable (como un módulo de licencia).

Si el MAC guardado no se encuentra en la stack, se descarta y simplemente usamos el primer MAC en el orden de enlace y comenzamos de nuevo.

  public static string GetMacAddressPhysicalNetworkInterface() { ManagementObjectSearcher searcher = new ManagementObjectSearcher ("Select MACAddress,PNPDeviceID FROM Win32_NetworkAdapter WHERE MACAddress IS NOT NULL AND" + " PNPDeviceID IS NOT NULL AND" + " PhysicalAdapter = true"); ManagementObjectCollection mObject = searcher.Get(); string macs = (from ManagementObject obj in mObject let pnp = obj["PNPDeviceID"].ToString() where !(pnp.Contains("ROOT\\")) //where pnp.Contains("PCI\\") || pnp.Contains("USB\\") select obj).Select(obj => obj["MACAddress"].ToString()) .Aggregate(null, (mac, currentMac) => mac + currentMac.Replace(":", string.Empty) + ","); return !string.IsNullOrEmpty(macs) ? macs.Substring(0, macs.Length - 1) : macs; } public static NetworkInterface GetPhysicalNetworkInterface(string macAddressPhysicalNetworkInterface) { return NetworkInterface.GetAllNetworkInterfaces().FirstOrDefault(currentNetworkInterface => string.Equals(currentNetworkInterface.GetPhysicalAddress().ToString(), macAddressPhysicalNetworkInterface, StringComparison.CurrentCultureIgnoreCase)); }