Linux 网络相关 

Last Update: 2024-02-21

目录

网络配置写法

Debian 系发行版中的 /etc/network/interfaces 文件存储了网络相关的配置,两个接口的配置必须用空行隔开。更全面的配置信息可以查看 man interfaces。

RedHat 系发行版一定带有 nmcli,直接使用这个工具配置网络即可。

静态 IP

/etc/network/interfaces 中写:

# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).

source /etc/network/interfaces.d/*

# The loopback network interface
auto lo
iface lo inet loopback

# The primary network interface
# eno1 is the interface name
auto eno1
iface eno1 inet static
    address 192.168.2.110/24
    gateway 192.168.2.3
    # dns-* options are implemented by the resolvconf package, if installed
    dns-nameservers 192.168.2.20
    dns-search debian.local

对应 nmcli 命令模板为:

sudo nmcli con add con-name <connection-name> type ethernet ifname <interface-name> ipv4.method manual ipv4.addresses <some-ip> ipv4.gateway <some-ip> ipv4.dns <some-ip>

从 DHCP server 获取 IP

# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).

source /etc/network/interfaces.d/*

# The loopback network interface
auto lo
iface lo inet loopback

# The primary network interface
# eno1 is the interface name
auto eno1
iface eno1 inet dhcp

对应 nmcli 命令模板为:

sudo nmcli con add con-name <connection-name> type ethernet ifname <interface-name> ipv4.method DHCP

Debian 系配置细节

网卡的配置顺序会影响默认路由

给两段网卡配置:

source /etc/network/interfaces.d/*

auto lo
iface lo inet loopback

auto ens1f0np0
iface ens1f0np0 inet dhcp

iface enp3s0f0 inet manual

iface enp3s0f1 inet manual

auto bond0
iface bond0 inet dhcp
    hwaddress ether B8:83:03:46:05:3C
    bond-slaves enp3s0f0 enp3s0f1
    bond-mode balance-xor
    bond-xmit-hash-policy layer3+4
    bond-miimon 100
source /etc/network/interfaces.d/*

auto lo
iface lo inet loopback

iface enp3s0f0 inet manual

iface enp3s0f1 inet manual

auto bond0
iface bond0 inet dhcp
    hwaddress ether B8:83:03:46:05:3C
    bond-slaves enp3s0f0 enp3s0f1
    bond-mode balance-xor
    bond-xmit-hash-policy layer3+4
    bond-miimon 100

auto ens1f0np0
iface ens1f0np0 inet dhcp

它们分别对应路由 (ip route show):

default via 192.168.2.3 dev ens1f0np0 
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 
192.168.2.0/24 dev ens1f0np0 proto kernel scope link src 192.168.2.32 
192.168.2.0/24 dev bond0 proto kernel scope link src 192.168.2.30 
default via 192.168.2.3 dev bond0 
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 
192.168.2.0/24 dev bond0 proto kernel scope link src 192.168.2.30 
192.168.2.0/24 dev ens1f0np0 proto kernel scope link src 192.168.2.32 

可以看到,如果把 ens1f0np0 的配置放到最后,那么到 192.168.2.3 这个网关的默认端口就从 ens1f0np0 变成了 bond0。而这之中,ens1f0np0 是 10G 网卡的端口,bond0 的两个 slave 都是 1G 网卡的端口。

想要让 10G NIC 作为默认端口就要把这个端口的网络配置写在所有其它配置之前。

nmcli 使用细节

bond 接口的 MAC 地址在重启后改变

如果 bond 使用 balance-xor 等链路聚合模式,那么每次重启后的 MAC 地址会从所有的 slave 接口中随机选择一个,取决于哪个接口先被加载。

要解决这个问题,可以在配置 bond 时给它手动指定一个固定的 MAC 地址,这样,即使发生重启 bond 的 MAC 地址也不会改变。通常,从 bond 的 slave 接口中选一个地址作为 bond 的固定 MAC 地址。

使用 nmcli 来配置 bond: sudo nmcli con mod <bond-con-name> 802-3-ethernet.cloned-mac-address <bond-fixed-mac-addr>

也可以手动修改 /etc/sysconfig/network-scripts/ifcfg-<bond-name> 配置文件里 MACADDR 字段的值为指定的 MAC 地址。需要注意赋值对象是 MACADDR 不是 HWADDR。HWADDR 指定的是接口的物理 MAC 地址,用途在于保证拥有该 MAC 地址的设备被分配正确的设备名。MACADDR 字段的值的含义是向接口分配指定的 MAC 地址,覆盖其原有的 MAC 地址。两个字段不应一起使用。

bond 接口在系统启动后不可用,一段时间后才可用

这是 NetworkManager 的 bug,它在 NetworkManager-1.30.0-7.el8 及 RHSA-2021:1574 中被修复。

使用 active-backup 模式的 bond 接口在被创建时,Linux 内核会先给这个新的 bond 接口分配一个随机的 MAC 地址。接着,按照正常流程,后续如果有可用的 slave 接口被加入到 bond 中,就把 bond 接口的 MAC 地址改成第一个 slave 接口的 MAC 地址。但如果设置了 fail_over_mac 参数,在发生接口切换时,内核只更新链路状态 (bond 是否连接到网络),不会主动更新 MAC 地址。

而 NetworkManager 把内核最开始分配给 bond 的随机地址也作为可用地址来处理,所以,开机时 NetworkManager 把这个随机地址给了 bond 接口,在检测到链路不可用后才发生主备切换。

内核参数 fail_over_mac 指定了分配 MAC 的行为,它只对 active-backup 模式生效,其取值为:

链路聚合

链路聚合前置概念

IEEE 802.3ad 标准定义了 Static LAGs 和 LACP 两种链路聚合方式。但是,标准早期只定义了 Static LAGs,后来才更新了 LACP 相关的内容。

要注意,Static LAGs 和 LACP 都符合 802.3ad 标准。它们的区别在于,Static LAGs 只符合了 802.3ad 标准;LACP 除了符合标准外,还用了 802.3ad 中 LACP 这个协议 (会产生额外的,用于协议协商的数据包)。

Static LAGs 是静态聚合,群晖管 Static LAGs 叫 IEEE 802.3ad draft v1 ,有些地方也管它叫手工聚合,TP-Link 管它叫静态端口汇聚。LACP 是动态聚合,没有其他奇怪的叫法。

LACP 也分 Static LACP 和 Dynamic LACP,要注意把 Static LACP (符合 802.3ad 标准并使用 LACP 协议) 和 Static LAGs (只符合 802.3ad 标准) 区分开,不做更多展开。

在使用链路聚合的端口组中,主机或交换机通过对数据包头中的信息计算 hash 值来为流量选择流出端口 (hash 算法可以指定)。需要注意,主机与交换机的 hash 算法需要分别配置,主机与交换机使用的 hash 算法可以不同,但最好相同。

链路聚合中的负载均衡算法

Linux 系统中的 xmit_hash_policy (cat /sys/class/net/<bond-name>/bonding/xmit_hash_policy) 指定了 hash 算法,对于一台连接到交换机的 Linux 主机来说,算法的可选值及其效果为:

需要注意的是,上面的内容只提及连接到交换机的设备的 bond 接口的出向流量的负载均衡策略,即,从 bond 接口流出的流量会均匀分布在 bond 接口中的每个物理接口上。想要让 bond 接口的入向流量也做到负载均衡还需要交换机上的 hash 策略的配合,让交换机做好交换机的出向流量的负载均衡,这就是前文提到 主机与交换机的 hash 算法可以不同,但最好相同 的原因。

交换机做好出向流量的负载均衡,就会使得流出交换机的数据被均匀分布在被聚合的端口间,连接到这些端口的设备也能在其自身的 bond 接口上收到被负载均衡过的流量,即,bond 接口所包含的物理接口收到的流量也是均匀分布的。

设备对出向流量做好负载均衡,交换机也对出向流量做好负载均衡,就可以让设备的双向流量都做到负载均衡。下面给个例子

环境:

hash 算法的不当配置:

现在 B 给 C 发数据,可以观察到:

原因出在交换机上: 与 bond_c 连接的 bond1 使用的 layer2 算法把流向同一 MAC 地址 (即 bond_c 的 MAC 地址) 的流量分配到了同一条链路上,所以 bond1 所属的两个物理接口中只有一个接口有出向流量,所以 bond_c 所属的两个物理接口中也就只有一个接口有入向流量了。

要纠正这个问题,只需要把 bond1 的 hash 算法改成 layer3+4 即可。使用 layer3+4 算法的 bond1 会把流向不同 ip:port 地址的流量分布在不同的端口上。在本例中有 2 个 TCP 连接,会使用 2 个端口,对应 2 条链路。bond1 的每条物理链路负责一个连接的出向流量,所以 bond_c 的每条链路都能接收到一个入向流量的连接。然后就可以观察到 bond_c 所属的两个物理接口都有入向流量,且入向流量带宽接近,并且 bond_b 可以跑满出向带宽,bond_c 也可以跑满入向带宽。

但是这还没完,上面的方案只解决了数据从 B 流向 C 的问题。果数据是从 C 流向 B 的,那么还会再在 bond_b 上观察到和 bond_c 一样的现象,即,bond_b 所属的两个物理接口中,一个端口承担了所有流量,另一个端口几乎没有流量。

所以,还需要把 bond0 的 hash 算法改成 layer3+4。此时,整个系统中的 hash 算法被统一成了 layer3+4。

Linux kernel v[4.2, 4.14) 中的 layer3+4 算法存在问题

Linux kernel v4.2 之前,程序调用 connect() 建立 TCP 连接时,内核会按照使用顺序分配端口号。连接较多时,可能会造成 bind(0) 调用失败。主机端口被扫描时也可能出现性能问题。

Linux kernel v4.2 (2015) 中,为了解决上述问题,Google 的开发人员修改了端口分配逻辑 (commit 07f4c90)。程序调用 connect() 建立 TCP 连接时,内核偏向于为程序分配一个偶数数字的端口号。但这个改动影响了链路聚合中所有涉及 layer4 (src-dest-port) 的算法: 程序被分配的端口号有了特征 。即,增加了端口号的二进制值的末位为 0 的概率。

本应被分配在两个链路上的两个连接被分配在了同一个链路上的情况出现的概率变大了

Linux kernel v4.14 (2017) 中,为了解决偶数端口号产生的问题,Linux kernel 的开发人员修改了链路聚合的 hash 算法 (commit b5f8621): 抛弃计算结果的最后一位。

其基本思路是,抛弃计算结果中受特征影响的部分。端口号的特征是二进制值末位为 0 的概率变大,其特征在二进制值最后一位上。根据 layer3+4 hash 算法的逻辑,在 hash 运算的结果中,端口号的特征也被保留在了二进制值的最后一位上。

所以,只要抛弃计算结果中保留了端口号特征的最后一位,就可以使计算结果恢复到不带特征的状态,从而让连接尽量均匀地分布在所有链路上。

bond 网络接口

执行 sudo apt install ifenslave 安装 bond 功能包。

bond 是 Linux 上实现了链路聚合的驱动程序,可以让多个网络接口被组合成一个虚拟的网络接口。

bond 提供了七种工作模式,在使用的时候需要指定一种:

工作模式简写需要交换机支持容错负载均衡简略描述
balance-rr 默认mode=0YYY各网口轮流发包,吞吐量均衡。
active-backupmode=1Y仅一个接口在工作,故障时切换到另一个接口。
balance-xormode=2YYY根据算法选择数据包流出端口。
broadcastmode=3Y每个接口都要发送每个要发出的数据包。
802.3admode=4YYYIEEE 802.3ad 动态链路聚合。
balance-tlbmode=5YY用 ARP 协商实现发包负载均衡,但只有一个接口收包。
balance-albmode=6YY基于 balance-tlb,再用 ARP 协商实现收包负载均衡。

在需要链路聚合时,高端交换机常用 4 模式;中低端交换机常用 0 2 模式;傻瓜交换机常用 6 模式。下面是详细说明。

balance-rr

特点: 传输数据包顺序是依次传输。比如,第 1 个包走 eth0,第 2 个包走 eth1,第 n 个包走 eth<n-1>,第 n+1 个包走 ethn … 如此排列循环。需要在交换机上配置静态聚合。

效果: 支持负载均衡,容错。

缺点: 如果一个连接的数据包从不同的接口发出,中途再经过不同的链路到客户端,有可能出现数据包无序到达的问题。如果数据包无序地到达目标机器,目标机器会要求服务器重发数据包,从而导致网络吞吐量降低,负载均衡效果也受影响。

active-backup

特点: 只有一个接口在 active 工作状态,剩下的所有接口在 standby 待机状态。当工作状态的接口不工作,系统将会按顺序选择下一个待机状态的接口作为主接口。所有接口共享一个 MAC 地址。不需要交换机配置。

效果: 支持容错。

缺点: 只有一个接口处于工作状态,资源利用率低。在有 N 个网络接口的情况下,资源利用率为 1/N。

balance-xor

特点: 基于 xmit_hash_policy (cat /sys/class/net/<bond-name>/bonding/xmit_hash_policy) 参数指定的 hash 策略来选择发送数据包的端口。需要在交换机上配置静态聚合。

效果: 多个物理接口合并为一个逻辑接口。保证到达特定对端的流量总是从同一个接口上发出。支持负载均衡,容错。

缺点: 任何连接的速度上限都不能大于一个接口的速度上限。但如果程序支持使用多个连接,配合对应的 hash 算法,那么该程序可以使用的带宽上限为接口带宽*接口数量。本机网络中负载均衡的效果取决于 xmit_hash_policy 所选择的算法,但系统整体的网络性能还与交换机配置的 hash 算法有关。但并不是所有的 hash 策略都兼容 802.3ad 协议。

broadcast

特点: 在每个网络接口上传输每个数据包。适用于金融等需要有高可靠性的网络的行业。

效果: 所有包从所有网络接口发出。

缺点: 只有提供了冗余机制,浪费资源。

LACP

特点: 根据 802.3ad 标准中的 LACP 协议将多个网络接口汇聚在一起。需要交换机配置 LACP 聚合。

效果: 支持负载均衡,支持容错。

缺点: 任何连接的速度上限都不能大于一个接口的速度上限。但如果程序支持使用多个连接,配合对应的 hash 算法,那么该程序可以使用的带宽上限为接口带宽*接口数量。本机网络中负载均衡的效果取决于 xmit_hash_policy 所选择的算法,但系统整体的网络性能还与交换机配置的 hash 算法有关。但并不是所有的 hash 策略都兼容 802.3ad 协议。

balance-tlb

特点: 单向 (发送) 负载均衡。不需要交换机配置。

效果: 单向 (发送) 负载均衡,容错。

缺点: 收包时仅使用一个网卡的 MAC 地址,即,仅有一张网卡用于收包。这种模式适合用在 出向流量巨大而入向流量小 的情况下。如果 server 也要负责大量数据的接收,那么无法充分利用多个接口。

发送负载均衡实现思路: 当服务器要传数据出去给单一一个客户端时,bonding 模块会主动的拦截封包,通过 ARP 协商机制,将不同的网卡要送出到同一个客户端的封包,都改写成单一一个固定的发送端 MAC 地址。

balance-alb

特点: 在 balance-tlb 基础上,加入针对 IPv4 流量的接收负载均衡 RLB (receive load balance) 策略。不需要交换机配合。接收负载均衡通过 ARP 协商实现。不需要交换机配置

效果: 收发双向负载均衡,容错。

缺点: 在实际使用上 balance-alb 与 balance-rr 的主要区别在于,balance-alb 先把 eth0 流量占满,再占 eth1 … ethX。也就是第一个端口负载很高,后面的接口负载很小。而 balance-rr 所有接口的流量都很稳定,负载基本平衡。

接收负载均衡实现思路: 当有数据封包要送出到多个不同的客户端时,bonding 模块通过 ARP 协商机制,找出 bonding 管理的比较闲置的网卡 MAC 分配给下个客户端。如此,不同的用户端回传给服务器的数据,就可以透过不同的网卡来接收,达到接收流量也负载均衡的功能。

配置写法

Debian 系:

# The loopback network interface
auto lo
iface lo inet loopback

iface eno1 inet manual

iface enp4s0 inet manual

auto bond0
iface bond0 inet dhcp
    bond-mode balance-alb
    bond-slaves eno1 enp4s0
    bond-miimon 100

bond-miimon 100 表示系统每 100ms 监测一次链路连接状态,根据链路状态决定是否进行其他操作。比如 active-backup 策略下,如果检测到正在使用的网络接口无法使用导致链路不通,则切换到另一个接口,两次检测间隔 100ms。

RedHat 系:

# /etc/sysconfig/network-scripts/ifcfg-enp3s0f0
TYPE=Ethernet
HWADDR=B8:83:03:46:05:3C
NAME=enp3s0f0
UUID=0adca574-60fe-48a5-b50d-b71a1fbaf8f9
DEVICE=enp3s0f0
ONBOOT=yes
MASTER=bond0
SLAVE=yes
BOND_PORT_QUEUE_ID=0

# /etc/sysconfig/network-scripts/ifcfg-enp3s0f1
TYPE=Ethernet
HWADDR=B8:83:03:46:05:3D
NAME=enp3s0f1
UUID=332633b2-25f1-40d1-8048-f5d1b4e3dc8d
DEVICE=enp3s0f1
ONBOOT=yes
MASTER=bond0
SLAVE=yes
BOND_PORT_QUEUE_ID=0

# /etc/sysconfig/network-scripts/ifcfg-bond0con
BONDING_OPTS="mode=balance-xor downdelay=0 miimon=100 updelay=0 xmit_hash_policy=layer3+4"
TYPE=Bond
BONDING_MASTER=yes
PROXY_METHOD=none
BROWSER_ONLY=no
BOOTPROTO=dhcp
DEFROUTE=yes
IPV4_FAILURE_FATAL=no
IPV6INIT=yes
IPV6_AUTOCONF=yes
IPV6_DEFROUTE=yes
IPV6_FAILURE_FATAL=no
IPV6_ADDR_GEN_MODE=stable-privacy
NAME=bond0con
UUID=91425ae3-4179-4b43-8b99-79a4c5b68473
DEVICE=bond0
ONBOOT=yes
MACADDR=b8:83:03:46:05:3d

以上内容对应命令:

sudo nmcli con add con-name bond0con type bond ifname bond0 bond.options mode=balance-xor,miimon=100,xmit_hash_policy=layer3+4
sudo nmcli con add con-name enp3s0f0 type bond-slave master bond0 ifname enp3s0f0
sudo nmcli con add con-name enp3s0f1 type bond-slave master bond0 ifname enp3s0f1