Linux 下获取网卡 ip 和名称

0. 概述

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

1. shell 操作

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

[root@liqiang.io]# 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

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

  • 网卡名字
  • mac 地址
  • ip
  • mask
  • 网关

如果不额外安装库,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 选项获取到所有的接口以及他们的信息,调用的代码如下:

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

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

[root@liqiang.io]# 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 语言的话,事情就变得更加简单了,直接通过标准库就可以实现了:

[root@liqiang.io]# cat get_interface.go
... ...
ifaces, err := net.Interfaces()
    if err != nil {
        panic(err)
    }
... ...

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

4. Ref

发表评论

CAPTCHAis initialing...