最直接方式是linux/macos用getifaddrs,windows用getadaptersaddresses;mac仅存于af_packet/af_link地址中,ip在af_inet/af_inet6中;需判空、分族处理、注意长度可变、及时freeifaddrs。

用 getifaddrs 读取本机所有网络接口的 IP 和 MAC
Linux/macOS 下最直接的方式是调用 getifaddrs,它能一次性拿到所有接口的地址族、IP、子网掩码、MAC(链路层地址)等。Windows 不支持这个函数,得换方案——这点容易踩坑,别写完 Linux 跑通就以为万事大吉。
关键点在于:MAC 地址只在 AF_PACKET(Linux)或 AF_LINK(macOS)类型的地址结构里;IPv4/IPv6 地址则在 AF_INET/AF_INET6 里。必须按 ifa_addr->sa_family 分支处理,不能硬转 sockaddr_in。
- 记得检查
ifa_addr非空,有些接口(比如lo)可能没配置 IPv4 地址 - MAC 地址长度不固定(以太网是 6 字节,但其他链路类型可能不同),要用
ifa_data->ifi_addrlen或直接看sll_halen字段 - 调用完必须用
freeifaddrs释放内存,否则泄漏
struct ifaddrs *addrs;
if (getifaddrs(&addrs) == 0) {
for (struct ifaddrs *addr = addrs; addr; addr = addr->ifa_next) {
if (!addr->ifa_addr) continue;
if (addr->ifa_addr->sa_family == AF_PACKET) {
struct sockaddr_ll *sll = (struct sockaddr_ll*)addr->ifa_addr;
// sll->sll_addr 是 MAC,长度 sll->sll_halen
}
}
freeifaddrs(addrs);
}
Windows 上用 GetAdaptersAddresses 替代 getifaddrs
Windows 没有 getifaddrs,得用 iphlpapi.h 里的 GetAdaptersAddresses。它返回的是 IP_ADAPTER_ADDRESSES 链表,每个节点带 IPv4/IPv6 地址列表和物理地址(即 MAC)。
常见错误是传错 Family 参数:想拿 IPv4 就传 AF_INET,想拿全部就传 AF_UNSPEC;但 MAC 地址只在主节点的 PhysicalAddress 字段里,不在地址子项中。
立即学习“C++免费学习笔记(深入)”;
-
GetAdaptersAddresses可能返回ERROR_BUFFER_OVERFLOW,需要先调一次获取所需缓冲区大小,再 malloc 后重试 - 物理地址长度由
PhysicalAddressLength给出,不是固定 6 字节(虽然以太网卡基本都是) - 注意判断
OperStatus == IfOperStatusUp,否则可能拿到已断开的旧接口信息
跨平台封装时,别直接依赖 ioctl 或 SIOCGIFHWADDR
有人图快在 Linux 里用 socket + ioctl 配合 SIOCGIFHWADDR 查单个接口 MAC,这确实能用,但问题很实在:它不跨平台、不支持 IPv6 地址枚举、且要求 root 权限(某些系统上查非本地接口会失败)。
更麻烦的是,ioctl 方式无法区分虚拟接口(如 docker0、veth*)、隧道接口(如 wg0)的真实状态,返回的可能是过期或伪造的 MAC。而 getifaddrs 和 GetAdaptersAddresses 至少保证数据来自内核当前网络栈快照。
- 除非你明确只跑在某发行版的特定内核上,且只要一个接口的 MAC,否则别碰
ioctl -
SIOCGIFADDR这类 ioctl 已被标记为 legacy,新代码尽量避开 - Android NDK 里
getifaddrs支持有限,部分老版本要自己解析/sys/class/net/*/address
获取不到 MAC?先确认接口是否处于 UP 状态且有 L2 地址
很多情况下“读不到 MAC”不是代码问题,而是接口本身没链路层地址:比如纯 IPv6 的 ip -6 addr add 添加的地址不会自动关联 MAC;或者容器网络里用 veth 对接时,宿主机端看到的是 veth 设备,但对端(容器内)可能禁用了 MAC 层(如使用 ipvlan)。
验证方法很简单:ip link show 看接口状态是否为 UP,再看 link/ether 行是否存在。没有的话,代码里拿到空 MAC 是合理的,不是 bug。
- Loopback(lo)接口在多数系统上没有真实 MAC,Linux 返回
00:00:00:00:00:00,Windows 返回全零 - 某些虚拟化环境(如 WSL2)里,主接口的 MAC 是由 Hyper-V 动态分配的,重启后会变,不适合做唯一标识
- IPv6 的 link-local 地址(fe80::/64)对应同一个 MAC,但全局地址可能来自 SLAAC 或 DHCPv6,和 MAC 无直接映射关系
真正难搞的从来不是怎么读,而是读到之后怎么判断哪个 IP/MAC 对才是你要的那个——尤其当机器有多个物理口、多个 VLAN、多个容器网络时,光靠名字(eth0、enp0s3)根本不可靠。










