netifaces.gateways如何选择Linux系统的默认网关?
问题描述
Linux主机配置2块网卡,路由表信息如下:
$ ip route default via 10.192.244.254 dev enp0s31f6 proto static metric 100 default via 192.168.100.1 dev enp2s0 proto dhcp src 192.168.100.106 metric 200 10.192.244.0/24 dev enp0s31f6 proto kernel scope link src 10.192.244.193 169.254.0.0/16 dev docker0 scope link metric 1000 172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 192.168.100.0/23 dev enp2s0 proto kernel scope link src 192.168.100.106 192.168.100.1 dev enp2s0 proto dhcp scope link src 192.168.100.106 metric 200
按照路由度量值规则,通往同一目标的多条路由中,流量会优先走metric值最低的路径,也就是网关10.192.244.254(metric=100)优先级高于192.168.100.1(metric=200)。
使用如下测试代码调用netifaces库获取网关:
# try.py import netifaces gateways = netifaces.gateways() print(gateways)
执行输出为:
$ python3 try.py {'default': {2: ('192.168.100.1', 'enp2s0')}, 2: [('10.192.244.254', 'enp0s31f6', True), ('192.168.100.1', 'enp2s0', True)]}
返回结果中默认IPv4网关被判定为metric更高的192.168.100.1,既不符合路由优先级规则,也没有同时列出两个默认网关。
netifaces.gateways() 接口的默认网关选择逻辑
这个接口的默认网关选择逻辑完全不参考路由metric值,和内核实际选路逻辑不一致,具体规则如下:
- Linux平台下,该接口不会直接读取内核FIB(转发信息库)的优先级排序结果,而是按固定优先级顺序遍历多个配置源,取第一个找到的默认网关作为
default字段的返回值:- 首先读取
/etc/sysconfig/network-scripts/(RedHat系)、/etc/network/interfaces(Debian系)等静态网络配置文件中写死的gateway配置 - 其次读取NetworkManager、dhclient等网络管理服务在运行目录(通常是
/var/run/、/run/下)生成的状态文件中记录的网关信息 - 上述两个位置都没找到的话,才会读取
/proc/net/route中的默认路由条目,而且读取时按网卡在文件中出现的顺序返回,不会按metric值排序
- 首先读取
- 该场景中,enp2s0的IP是DHCP获取的,dhclient/NetworkManager会把DHCP拿到的网关
192.168.100.1写入运行时状态文件,netifaces遍历的时候先读到这个条目,直接就把它设为默认IPv4网关,根本没处理后面找到的、metric更低的静态网关。 - 返回值中地址族编号(2对应IPv4,10对应IPv6)对应的列表,会把所有扫描到的默认网关都列出来,只是
default字段只存遍历到的第一个条目,不会做优先级排序。
正确获取实际生效默认网关的方法
如果需要拿到和内核选路逻辑一致的默认网关,不要直接使用netifaces.gateways()['default']的返回值,可以选择以下方案:
- 解析
ip route命令的输出,筛选default条目后按metric值从小到大排序,取第一条的网关地址 - 读取
/proc/net/route文件,筛选flag为0003(即RTF_UP|RTF_GATEWAY,代表生效的默认网关)的条目,按metric字段排序后取第一条 - 使用
pyroute2这类直接和内核netlink接口通信的库,获取的路由表自带metric参数,排序后结果和系统实际选路完全一致
内容的提问来源于stack exchange,提问作者atline
相关产品推荐
相关产品推荐

