一. 什么是bond?
bond用于将多个网络接口,聚合成一个逻辑网口,从而实现高带宽、高可用性等目标。
Linux的bond支持以下7种工作模式:
balance-rr: Round-robin policy
active-backup: Active-backup policy
balance-xor: XOR policy
broadcast: Broadcast policy
802.3ad: IEEE 802.3ad Dynamic link aggregation
balance-tlb: Adaptive transmit load balancing
balance-alb: Adaptive load balancing。
各个模式的详细描述,可以参考内核源码包文件: Documentation/networking/bonding.txt
下面摘录一点内核源码包对此描述的描述。
a) drivers/net/Kconfig
config BONDING
tristate "Bonding driver support"
depends on INET
---help---
Say ‘Y‘ or ‘M‘ if you wish to be able to ‘bond‘ multiple Ethernet
Channels together. This is called ‘Etherchannel‘ by Cisco,
‘Trunking‘ by Sun, 802.3ad by the IEEE, and ‘Bonding‘ in Linux.
The driver supports multiple bonding modes to allow for both high
performance and high availability operation.
b) Documentation/networking/bonding.txt
The Linux bonding driver provides a method for aggregating
multiple network interfaces into a single logical "bonded" interface.
The behavior of the bonded interfaces depends upon the mode; generally
speaking, modes provide either hot standby or load balancing services.
Additionally, link integrity monitoring may be performed.
二、如何使用bond?
1. 创建bond口
如果bond模块直接被编译进了内核,则无须创建,就已经有bond口了。
否则的话,则需要通过命令“insmod bonding.ko”将bond模块加载到内核。
bond模块默认是创建一个bond口,即bond0。如果有多个bond口,则他们的名称为bond0, bond1, bond2, bond3, ...依次类推。
通过命令“ifconfig -a”可以查看系统中的所有网口,如eth0, eth1, bond0, bond1等。
如果想创建多个bond口,可以在加载bond模块时,通过模块参数max_bonds指定。
例如,“insmod bonding.ko max_bonds=3”就创建了3个bond口。
bond模块还提供了很多其他的模块参数,用于对此模块进行不同方面的配置。
例如,mode模块参数,用于指定bond模块的工作模式,即上面提到的7种模式中的一种。
因此,我们可以通过命令“insmod bonding.ko max_bonds=3 mode=balance-rr”来加载bond模块,这就创建了3个bond口,并且工作模式是balance-rr。
2. 配置并启用bond口
假如我们已经创建了bond口bond0,并且工作模式是balance-rr。
接下来,通过如下命令,即可将两块以太网卡聚合成一个逻辑口,并使用起来。
ifconfig bond0 192.168.1.1 netmask 255.255.255.0 up
ifenslave bond0 eth0
ifenslave bond0 eth1
注意,每块网卡,如eth0,只能属于一个bond口。
bond口叫做master,而组成bond口的各个网口,如eth0,叫作slave。
3. 在运行时改变bond口的行为
bond模块提供了很多模块参数。但是这些参数,只是在加载模块的时候使用。如果模块已经加载,想改变bond的行为怎么办呢?
或者还有另一种情况,即bond没有被编译成模块,而是直接编译进了内核,此时根本无法使用模块参数定制bond的行为。
这时有什么办法可以改变模块的行为呢?答案是通过sysfs。
每个bond口,在sysfs中会有一个对应的目录,目录下列出了该bond口的各种属性,每个属性是一个文件。
例如,/sys/class/net/bond0 里面就包含了bond0口的各种属性,每个属性都是一个文件。
这些属性文件,在用户看来,就像文本文件一样。
想查看属性,通过命令“cat 属性文件”即可。
要修改属性,通过命令“echo 属性值 > 属性文件”即可。
三、bond模块部分内核代码实现
对内核的上层代码来说,bond口就是一个网口,对应一个net_device结构,与普通口无异。
上层代码,通过某网口发送报文,通过一样的逻辑走到net_device->hard_start_xmit函数中。
上层并不关心某网口的hard_start_xmit函数如何发送报文。而bond口的重要特色就在这里了。
bond口的hard_start_xmit函数,依工作模式的不同,通过不同的算法,选择一个slave,然后通过slave的hard_start_xmit函数将报文发送出去。
另外,在一个网口通过netif_receive_skb函数接收报文时,需要知道报文所属的实际网口,即bond口。
这是通过调用如下的skb_bond函数完成的。为了便于理解,这里只保留了核心代码,并做了排版的修改。
原始代码参见:net/core/dev.c
static inline struct net_device *skb_bond(struct sk_buff *skb)
{
struct net_device *dev = skb->dev;
if (dev->master)
{
skb->dev = dev->master;
}
return dev;
}
可见每个slave的net_device结构,通过master成员指向其所属的bond的net_device结构。
当然,内核中也有相应的数据结构,让bond口对其所有的slave进行管理。
用户态通过ifconfig命令对网口的操作,很多是通过网口对应该的net_device的do_ioctl函数完成的。
对于bond口来说,这个do_ioctl函数就是bond_do_ioctl函数。
例如,前面的命令“ifenslave bond0 eth1”走到bond_do_ioctl中,对应如下的代码。
case SIOCBONDENSLAVE:
res = bond_enslave(bond_dev, slave_dev);
break;