如何确定实际物理网络卡的MAC地址 -- 不包括VPN创建的虚拟网络接口 (.NET C#)

21

背景

我正在尝试从计算机中获取唯一标识符,并希望能够可靠地返回相同的MAC地址。请相信我有使用MAC地址的原因,并阅读了许多关于替代唯一ID方法的帖子(是的,我已考虑过如果它们没有任何网络卡的情况)。

问题

问题在于,在.NET中,我看不到任何方法可以判断特定的NetworkInterface是否是物理硬件网络卡,例如连接到某些VPN或WiFi网络时会添加“Nortel IPSECSHM Adapter - Packet Scheduler Miniport”类似的东西。

我知道如何使用类似以下代码获取MAC地址:

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

理解上,没有办法百分之百地确定我会得到一张内部网络卡,但我想选择要返回的MAC地址,该地址针对于一个给定的机器是最不容易改变的,与其是否连接到wifi之类的因素无关,也无论它是通过某种类型的绑定连接还是安装了新的VPN软件添加了新接口。

考虑的策略

1)选择“已启动”的第一个接口。这在我的笔记本电脑上失败了,因为“Packet Miniport”始终处于启动状态。此外,如果我将手机绑定到笔记本电脑上,它也会显示为第一张卡。

2)选择最适当的类型...这种方法失败了,因为基本上所有设备都显示为“以太网”,包括WiFi适配器和我的iPhone绑定的互联网连接。

3)选择具有IP地址的NIC。由于以下几个原因而失败:1)网络卡可能未连接到局域网2)存在多个可能具有IP地址的网络接口卡。

4)只发送所有MAC地址...问题是列表将根据安装的软件而改变,并且很难进行比较。

5)选择速度最快的MAC地址。我认为这可能是最好的选择。我认为可以肯定的是,最快的接口通常会是最常驻的。

或者,可能有其他方法在.NET中检测物理卡,如果您能推荐一个提供不同信息的API调用,我会考虑调用它。

还有其他想法吗?

为了演示,在我将手机与笔记本电脑绑定时,这里是我的示例代码的输出:

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

没有连接iPhone:

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

您还应考虑具有相同类型多个NIC的计算机。 - Dolphin
你不能依赖MAC地址作为唯一标识符。如果你想要一个唯一的标识符,生成一个GUID,加密它并存储在计算机上。 - John Saunders
@John Saunders,我确实使用GUID。MAC地址是备用选项,以防存储GUID的位置被删除。 - blak3r
请查看我的答案 https://dev59.com/eGsz5IYBdhLWcg3wJUfJ#40088578 以检查物理设备。 - Arci
5个回答

12

这是我的方法: 它利用了物理卡连接到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;
    }
}

我认为这不适用于像这样的东西:http://www.amazon.co.uk/Belkin-Ethernet-Adapter-Patch-Cable/dp/B0002AFKN0 - 然而它们并不是非常普遍。 - Rob
是的,这是一个问题。我没有这样的设备,很有趣,这种情况下“PNPDeviceID”中写了什么。 第二个问题是当有多张卡时。因为我总是选择“第一”张卡,所以在“select”操作符中卡的顺序未确定的情况下可能会导致错误。我还没有在这个“WMI的SQL”方言中找到“order by”构造 :) - Sergey
1
我有一个带有标题“Broadcom BCM5709C NetXtreme II GigE(NDIS VBD客户端)”和PNPDeviceID但没有“PCI \”的适配器。它是物理的。 - shoren
如何获取物理适配器的IPv4地址?(远程计算机) - Kiquenet
物理适配器?网络适配器属性名称:NetEnabled - 值:True 网络适配器属性名称:PhysicalAdapter - 值:True 网络适配器属性名称:PNPDeviceID - 值:PCI\VEN_15AD&DE - Kiquenet
关于 Win32_NetworkAdapterConfiguration 和 Win32_NetworkAdapter 之间的关系?NetworkAdapterConfiguration 给我16个项目,而 Win32_NetworkAdapter 给我12个项目。 - Kiquenet

2

MAC地址的前三个字节是制造商ID。您可以黑名单某些已知不适合您目的的制造商ID,并忽略这些接口。

依赖速度可能不是一个好主意,因为VPN接口可以报告自己具有千兆速度,而没有任何理由不这样做。


我考虑过黑名单...我想,基于出现在描述中的字符串(例如:“VPN”,“IPSECSHM”等)进行过滤可能会更容易,但我认为这可能很难实现。作为替代方案,我想我可以创建一个最受欢迎的网络接口的白名单...“Realtek”,“Gigabit”等,但我真的想避免创建和维护白名单所带来的麻烦。 - blak3r

0
 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<string, string>(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));
    }

一些解释/文档链接可以说明为什么这个答案比其他答案更好。我喜欢'physicaladapter = true'的检查,但它可靠吗? - Macke
检查此属性也足够。 - reza.Nikmaram
如何使用 scope = "\\\\" + ip + "\\root\\cimv2"; 获取 GetPhysicalNetworkInterface - Kiquenet

0

我也一直在研究这个问题,认为如果没有持久状态,要维护稳定的MAC地址将会很困难。

我正在尝试的解决方案是使用适配器顺序中找到的第一个NIC,并将其保存下来。然后在后续的UUID生成中,如果在堆栈中找到了保存的MAC地址,即使不是第一个,也使用它。这样,适配器顺序可以移动,我们就不必担心会破坏依赖于稳定MAC地址的任何内容(例如许可模块)。

如果在堆栈中找不到保存的MAC地址,则将其丢弃并重新使用绑定顺序中的第一个MAC地址。


0

MAC地址是物理硬件地址。我无法在这台计算机上进行测试,但我认为如果添加虚拟连接,您将不会获得新的MAC地址,因为它不是实际的硬件部件。这将是另一个连接,但不是另一个MAC地址。

因此,保证每次获得相同的MAC地址取决于相同的硬件部件连接到计算机,并且您使用相同的算法从该硬件中选择。


1
@Brian - MAC地址也被用于虚拟接口...在我的情况下是Nortel Packet接口。 - blak3r

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接