0. 概述

在平时配置系统的时候,总是可以遇到类似于获取网卡地址之类的需求,用 shell 略显复杂,如果可以安装各种第三方包,那么用 python 还是很舒服的,但是,如果不额外安装的话,如何用标准库实现呢,本文尝试解决一番。

1. shell 操作

在平时日常使用中,我们一般情况下都是用 shell 来查看(当然有同学是用 GUI 啦)的,例如 ifconfig 以及更新的 ip 命令都是常见的选择,例如我常用的 ip 命令:

[[email protected]]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 52:54:00:de:11:11 brd ff:ff:ff:ff:ff:ff
    inet 192.168.111.111/20 brd 192.168.31.255 scope global noprefixroute eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::5054:ff:fede:1f76/64 scope link noprefixroute 
       valid_lft forever preferred_lft forever
3: virbr0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default qlen 1000
    link/ether 52:54:00:c9:0c:07 brd ff:ff:ff:ff:ff:ff
    inet 192.168.122.1/24 brd 192.168.122.255 scope global virbr0
       valid_lft forever preferred_lft forever
5: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 02:42:15:49:a8:8d brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:15ff:fe49:a88d/64 scope link 
       valid_lft forever preferred_lft forever

这个信息是很丰富的,足够我们获取很多信息,基本上可以应对日常的需要,例如:

如果不额外安装库,shell 也不精通的情况下,直接使用 python 执行这条命令,并且解析也是一种不错的选择。

2. ioctl 获取

我肯定对调用 shell 是不满意的,因为我个人更喜欢直接调用标准库,所以一番搜索之后,我发现原来还可以用 ioctl 来获取 socket 的信息:fcntl.ioctl

ioctl 是 APUE 中结果过的一个 io 的高级操作,可以完成很多事情,对于与不同的 io 类型,可以完成的功能也是不同的,对应到 socket 中,可以做到的事情有:

选项 功能
SIOCGIFCONF 获取所有接口的清单
SIOCSIFADDR 设置接口地址
SIOCGIFADDR 获取接口地址
SIOCSIFFLAGS 设置接口标志
SIOCGIFFLAGS 获取接口标志
SIOCSIFDSTADDR 设置点到点地址
SIOCGIFDSTADDR 获取点到点地址
SIOCGIFBRDADDR 获取广播地址
SIOCSIFBRDADDR 设置广播地址
SIOCGIFNETMASK 获取子网掩码
SIOCSIFNETMASK 设置子网掩码
SIOCGIFMETRIC 获取接口的测度
SIOCSIFMETRIC 设置接口的测度
SIOCGIFMTU 获取接口MTU
SIOCGXXX 根据 os 不同支持也不同

所以这里很简单,可以通过 SIOCGIFCONF 选项获取到所有的接口以及他们的信息,调用的代码如下:

[[email protected]]# cat get_inet.py
    ... ... 
    outbytes = struct.unpack('iL', fcntl.ioctl(
        s.fileno(),
        0x8912,  # SIOCGIFCONF
        struct.pack('iL', bytes, names.buffer_info()[0])
    ))[0]
    ... ...

然后再一一解析输出的 interface 信息:

[[email protected]]# cat get_inet.py
    ... ...
    for i in range(0, outbytes, 40):
        name = (namestr[i:i + 16]).decode().split('\0', 1)[0]
        ip = format_ip(namestr[i + 20:i + 24])
        rst[name] = ip
    ... ...

完整的代码见: get_interface.py

3. Golang 获取接口地址

如果你使用 Go 语言的话,事情就变得更加简单了,直接通过标准库就可以实现了:

[[email protected]]# cat get_interface.go
... ...
ifaces, err := net.Interfaces()
    if err != nil {
        panic(err)
    }
... ...

完整的代码可以参见:get_interface.go

4. Ref