go 中 net.interfaceaddrs 仅返回已配置且启用的 ipv4/ipv6 地址,不包含 link-local、tentative 等状态地址,故常仅见 loopback;应遍历 net.interfaces() 后对非回环接口调用 iface.addrs(),并按类型断言提取 ip 或 mac。

Go 里 Net.InterfaceAddrs 返回的地址为什么经常只有 loopback?
因为 InterfaceAddrs 只返回「已配置且启用」的 IPv4/IPv6 地址,不包含 link-local、tentative、deprecated 或 disabled 状态的地址。很多物理网卡在 DHCP 获取前、或 systemd-networkd 尚未完成配置时,InterfaceAddrs 就只吐出 127.0.0.1 和 ::1。
常见错误现象:net.InterfaceAddrs() 返回切片长度为 1 或 2,全是本地回环;但 ip addr show 明明能看到 eth0 的 192.168.1.10。
- 先用
net.Interfaces()拿到所有接口,过滤掉Flags & net.FlagLoopback == 0的(即非回环) - 对每个接口再调
iface.Addrs(),而非全局调一次net.InterfaceAddrs() - 注意:某些系统(如 macOS)对虚拟接口(
utun、bridge)返回地址不稳定,需加strings.HasPrefix(iface.Name, "en") || strings.HasPrefix(iface.Name, "eth")白名单
如何区分 IPv4、IPv6 和硬件 MAC 地址
InterfaceAddrs() 返回的是 []net.Addr,类型混杂。直接类型断言容易 panic,必须逐个判断底层实现类型。
使用场景:你想只取 IPv4 地址做服务绑定,或提取 MAC 做设备指纹。
立即学习“go语言免费学习笔记(深入)”;
-
*net.IPNet对应 IP 子网地址(含 IPv4/IPv6),用ip := addr.(*net.IPNet).IP.To4()判断是否为 IPv4 -
*net.IPAddr在某些系统(如 Windows)下可能出现,IP 字段可直接用.IP -
*net.HardwareAddr才是 MAC,仅在调用iface.HardwareAddr时返回,InterfaceAddrs()永远不会返回 MAC - 别用
strings.Contains(addr.String(), ":")判 IPv6——IPv4-mapped IPv6 地址(如::ffff:192.168.1.1)会误判
为什么有些地址带 `/32` 或 `/128`,有些没有?
这取决于地址来源和操作系统抽象方式。/32(IPv4)或 /128(IPv6)表示该地址是“主机地址”,不是子网地址。Linux 内核在通过 ioctl(SIOCGIFADDR) 获取单地址时,会补上这个掩码;而通过 netlink(Go 默认路径)拿到的是完整 IPNet,所以有掩码。
性能影响:无。但如果你用它生成监听地址(如 http.Listen(":8080")),得先调 .IP 提取裸 IP,否则 "192.168.1.10/24:8080" 会报 lookup 192.168.1.10/24: no such host。
- 安全做法:一律用
ipnet.IP,不要拼接addr.String() - IPv6 地址要加方括号:
fmt.Sprintf("[%s]:8080", ipnet.IP),否则http.Listen解析失败 - Windows 下某些隧道接口(如 WSL2 虚拟网卡)可能返回
fe80::/64地址,这种 link-local 地址不能用于公网通信,需额外过滤
替代方案:什么时候该放弃 InterfaceAddrs 改用 netlink 或 shell?
当你需要:UP/DOWN 状态、MTU、广播地址、作用域(global/link-local)、DHCP 获取的网关、或正在 tentative 的 IPv6 地址时,InterfaceAddrs 就不够用了——它压根不提供这些元信息。
兼容性影响:纯 Go 实现跨平台,但功能残缺;调 exec.Command("ip", "addr") 或用 github.com/vishvananda/netlink 能拿到全量数据,代价是 Linux-only 或引入 Cgo。
- 简单脚本场景:直接跑
ip -j addr(JSON 输出)+encoding/json解析,比写 netlink 逻辑快得多 - 生产服务中避免 exec:用
netlink库,注意其LinkByIndex返回的Link.Attrs().Flags才能判断是否 UP - macOS 用户注意:
netlink不可用,只能走ifconfig -l+ifconfig en0组合解析,且输出格式随系统版本浮动
真正难的不是获取地址,而是决定「哪个地址该被选中」——比如多网卡环境下的默认路由出口、容器内 bridge vs host 网络优先级、IPv6 privacy extensions 导致的地址轮换。这些逻辑没法靠一个函数调用解决。










