Linux下最直接轻量的MAC获取方式是ioctl(SIOCGIFHWADDR),需socket(AF_INET, SOCK_DGRAM, 0)、正确初始化ifreq(ifr_hwaddr.sa_family=AF_UNSPEC、memset清零)、接口名≤15字节;但不支持veth等虚拟接口,且需root或CAP_NET_ADMIN权限。

Linux下用ioctl(SIOCGIFHWADDR)读取网卡MAC
这是最直接、最轻量的方式,不需要额外依赖,但必须有对应接口的读权限(通常需root或CAP_NET_ADMIN)。关键点在于:必须先用socket(AF_INET, SOCK_DGRAM, 0)打开一个控制套接字,再对指定接口名(如"eth0")调用ioctl。
常见错误是传入非法接口名或未初始化ifreq结构体——尤其是ifr_hwaddr.sa_family字段没设为AF_UNSPEC,会导致返回EINVAL;还有人忘记用memset清零ifreq,残留垃圾数据让sa_data前2字节不可靠。
示例要点:
- 接口名长度不能超过
IFNAMSIZ-1(通常是15字节),否则ioctl失败并返回ENODEV -
sa_data里MAC地址从第0字节开始,共6字节,不是从第2字节起 - 该方法不支持虚拟接口(如
veth、docker0)在某些内核版本下返回ENODEV,需fallback
Windows上用GetAdaptersAddresses()获取物理地址
Windows没有ioctl等价物,必须走IP Helper API。注意GetAdaptersAddresses()返回的是链表结构,每项含PhysicalAddress和PhysicalAddressLength,但只对InterfaceType == IF_TYPE_ETHERNET_CSMACD(或IF_TYPE_IEEE80211)才有效——Wi-Fi接口也可能返回MAC,但蓝牙、隧道、NDIS中间层接口的PhysicalAddressLength常为0,直接读会越界。
立即学习“C++免费学习笔记(深入)”;
容易踩的坑:
Linux 安全审计工具,检查 SSH 配置、开放/监听端口、防火墙规则(ufw/iptables/nftables)、失败登录尝试、sudoers 权限...
- 忘记检查
ReturnCode是否为ERROR_SUCCESS,某些系统(如精简版IoT)可能因服务未启动返回ERROR_NOT_SUPPORTED - 调用时
Family参数误设为AF_INET6,导致部分适配器被过滤掉(即使它们有IPv4配置) - 未处理
AdapterFlags & IP_ADAPTER_NO_MULTICAST这类标记,但不影响MAC读取,只是提醒该接口可能不参与组播
跨平台可移植方案:读取/sys/class/net/*/address(Linux)和注册表(Windows)
绕过API差异,直接读系统暴露的路径更稳定。Linux下每个网卡在/sys/class/net/下有address文件,内容为小写十六进制MAC,带冒号分隔;Windows则查注册表键HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{4d36e972-e325-11ce-bfc1-08002be10318}\*\NetworkAddress,但该值仅当驱动显式提供时存在(多数以太网卡有,Wi-Fi芯片厂商不一定填)。
实操建议:
- Linux路径拼接时,确保接口名不含
".."或"./",防止路径遍历(虽然/sys是只读伪文件系统,但仍是安全习惯) - Windows注册表方式需用
RegOpenKeyEx逐个枚举子键,不能硬编码索引,因为*是通配符,实际键名是数字字符串(如"0000"、"0001") - 两种方式都需处理空值或格式异常——比如
/sys里文件内容可能是"00:00:00:00:00:00"(表示未启用),注册表值可能是空字符串或全F
避免getifaddrs()误取MAC地址
getifaddrs()返回的ifa_addr指向的是IP地址结构(sockaddr_in等),不是MAC。有人试图强制转成sockaddr_ll并读sll_addr,这在glibc中不保证有效:Linux下AF_PACKET地址族才带MAC,而getifaddrs()默认不返回AF_PACKET条目(除非显式请求AF_PACKET且内核支持)。
真正能用getifaddrs()提取MAC的场景极少,且依赖_GNU_SOURCE和特定编译选项。更稳妥的做法是:先用getifaddrs()枚举接口名,再对每个名字单独调用ioctl(SIOCGIFHWADDR)或查/sys。
性能上,getifaddrs()本身开销不大,但误用它解析MAC会导致逻辑错误——比如把IPv4地址的前6字节当MAC,结果得到"c0:a8:01:01:00:00"这种明显错值。
真正麻烦的是容器环境:宿主机能看到所有接口,但容器内/sys/class/net/只挂载了veth和lo,ioctl对宿主机物理网卡根本不可见,此时必须依赖CNI插件注入的环境变量或元数据服务,而不是硬编码接口名。

















