需调用 net.interfaces() 获取接口列表,再遍历并结合 flags、hardwareaddr、接口名等综合判断:排除 flagloopback、hardwareaddr 为空或全零、以及 docker/veth/tun 等虚拟前缀,保留 flagup 且支持广播/组播的接口。

怎么用 net.Interfaces() 获取所有网卡信息
Go 标准库的 net.Interfaces() 是入口,但它只返回基础元数据(名字、索引、标志),不包含 MAC 或 IP。想拿到 MAC 地址或判断是否物理网卡,必须对每个 Interface 调用 Interface.Addrs() 和 Interface.HardwareAddr。
常见错误是直接遍历 net.Interfaces() 后就结束,结果发现 HardwareAddr.String() 为空,或者误把 lo 当成物理网卡。
-
HardwareAddr在某些虚拟接口(如docker0、veth*)上可能为 nil 或全零,不能仅靠非空就判定是物理网卡 - Windows 下回环接口的
HardwareAddr可能返回一个固定值(如00-00-00-00-00-00),需额外过滤 - Linux 下部分设备(如
bond0、team0)虽有 MAC,但属于逻辑聚合口,业务上通常不视为“物理网卡”
如何可靠识别“物理网卡”而非虚拟/回环接口
没有跨平台统一 API,得组合判断:看接口名、标志位、MAC 是否有效、是否存在底层设备路径(Linux)、驱动类型(Windows)。最实用的是用 Interface.Flags 过滤掉明显非物理的。
关键标志:net.FlagUp(启用)、net.FlagBroadcast(支持广播)、net.FlagMulticast(支持组播)是物理以太网卡的常见组合;而 net.FlagLoopback 必须排除,net.FlagPointToPoint 多见于 PPP/隧道,也建议跳过。
立即学习“go语言免费学习笔记(深入)”;
- Linux 下可读取
/sys/class/net/<name>/device</name>路径是否存在(存在则大概率是物理 PCI 设备) -
macOS 下检查
Interface.Name是否匹配en[0-9]+模式(但注意en0可能是 Wi-Fi,en1可能是 Thunderbolt 网卡) - 避免依赖
Interface.MTU == 1500判断——很多物理口支持 Jumbo Frame,MTU 可能是 9000
HardwareAddr 为空或异常时的处理策略
不是所有启用的接口都返回有效 MAC。比如 Linux 的 bridge 接口默认无 MAC,vlan 子接口继承父口 MAC 但有时未初始化,Windows 的 Hyper-V 虚拟交换机端口也可能返回空。
遇到 iface.HardwareAddr == nil 或 len(iface.HardwareAddr) == 0,别 panic,先查 iface.Flags 和名字,再决定是否跳过或 fallback 到其他标识(如 IPv4 地址 + 子网掩码推断所属物理口)。
- 若
iface.HardwareAddr非 nil 但全零(如00:00:00:00:00:00),大概率是虚拟接口,直接忽略 - 某些嵌入式设备(如 OpenWrt)中,物理口可能被重命名(如
eth0.1),此时 MAC 通常有效,应保留 - 不要用
strings.Contains(iface.Name, "eth")做判断——现代 Linux 默认使用可预测网卡名(ens33、enp0s3),老代码容易漏掉
跨平台获取 MAC 的最小可靠代码片段
以下逻辑在 Linux/macOS/Windows 上均能稳定拿到已启用物理网卡的 MAC(排除 loopback、tun、docker、veth 等):
interfaces, err := net.Interfaces()
if err != nil {
log.Fatal(err)
}
for _, iface := range interfaces {
if iface.Flags&net.FlagUp == 0 || iface.Flags&net.FlagLoopback != 0 {
continue
}
if iface.HardwareAddr == nil || len(iface.HardwareAddr) == 0 {
continue
}
// 过滤常见虚拟接口名前缀
if strings.HasPrefix(iface.Name, "docker") || strings.HasPrefix(iface.Name, "veth") ||
strings.HasPrefix(iface.Name, "br-") || strings.HasPrefix(iface.Name, "tun") {
continue
}
fmt.Printf("物理网卡: %s, MAC: %s\n", iface.Name, iface.HardwareAddr.String())
}
注意:Windows 上 vEthernet 类接口名需额外加判断;真实部署时建议把过滤规则抽成配置或 map,方便运维调整。
真正难的不是读 MAC,而是定义什么叫“物理网卡”——业务场景不同,边界就不同。比如 Kubernetes 节点要选 host 网络出口,就得优先选有默认路由的那张;而嵌入式设备做硬件绑定,则必须确认该 MAC 对应真实 NIC 芯片。别指望一次判断通吃所有情况。










