Ethernet Channel Bonding (Port Trunking)

Tagged:  •    •    •    •    •  

Channel Bonding (ou Port Trunking) é uma técnica especial que permite agregar interfaces de rede (geralmente ethernet) em interfaces lógicas que se comportam como um único link físico (dependendo da configuração). Esta técnica é utilizada principalmente para prover tolerância a falhas entre links, alta disponibilidade (redundância) e balanceamento.

Configuração: (Slackware 12.0)

O primeiro passo é habilitar o módulo do Kernel do Linux que faz isso tudo funcionar, chama-se "bonding".

CONFIG_BONDING=m

Não configure como builtin, pois não conseguiremos passar parâmetros como fazemos com módulos ao carregá-los.

Para verificar o suporte ao bonding como módulo no Kernel utilizado faça o seguinte:

# modprobe --list | grep bonding
/lib/modules/2.6.21.5-smp/kernel/drivers/net/bonding/bonding.ko

Neste caso temos o Kernel do Linux no Slackware 12.0 com a configuração padrão da distribuição (2.6.21.5 com suporte a SMP), já com o bonding como módulo (tornando desnecessário recompilar o Kernel).

No exemplo a seguir utilizaremos duas placas de rede Gigabit da D-Link e um Switch gerenciável da SMC para prover redundância entre as duas interfaces e tolerância a falhas.

O switch é o SMC-8024L2 e as placas de rede são a DGE-530T.

Configurei o switch para agregar as duas portas utilizadas (já que utilizarei o modo 0); como não faz muito sentido exibir a interface Web do switch aqui, abaixo segue a configuração utilizada obtida através da porta serial (CLI):

> Aggr Configuration
Aggr Configuration:
    Groups:
    11,12
    Lacp status
    None
    Mode: dmac

Operational Status
    Groups:
    11,12

Ou seja, as portas 11 e 12 do switch estão agregadas num único grupo (trunk), não utilizei o 802.3ad (LACP) e o modo de operação é baseado em uma política de destination MAC's.

O próximo passo, após recompilar o Kernel com suporte ao bonding é compilar o utilitário ifenslave, que se encontra na documentação do próprio Kernel:

/usr/src/linux/Documentation/networking/ifenslave.c

Para compilar o ifenslave faça o seguinte (é possível controlar também o Channel Bonding através do sysfs):

# gcc -Wall -O -I/usr/src/linux/include ifenslave.c -o ifenslave
# chown root.bin ifenslave
# chmod 0755 ifenslave
# cp ifenslave /usr/sbin

Agora basta configurar as interfaces, o primeiro passo é remover os módulos das placas de rede e do Port Trunking, caso já estejam carregados.

# rmmod bonding skge 2> /dev/null

skge é o módulo utilizado pelas placas de rede em questão.

Feito isto carregamos novamente os módulos e configuramos o Channel Bonding no modo 0, ou seja, load balancing (round-robin):

# modprobe bonding mode=0 miimon=100
# modprobe skge

miimon especifica em ms a freqüência com que as interfaces configuradas em modo slave serão verificadas e consideradas inativas caso não haja resposta (conseqüentemente removidas da configuração ativa).

Nos logs do sistema teríamos o seguinte:

skge 1.10 addr 0xfdef8000 irq 17 chip Yukon-Lite rev 9
skge eth0: addr 00:15:e9:4a:xx:yy
skge 1.10 addr 0xfdef4000 irq 18 chip Yukon-Lite rev 9
skge eth1: addr 00:15:e9:4a:xx:zz
Ethernet Channel Bonding Driver: v3.1.2 (January 20, 2007)
bonding: MII link monitoring set to 100 ms

Sobre os modos de operação disponíveis:

0 (balance-rr), transmite os pacotes em ordem seqüencial do primeiro slave ao último. Provê tolerância a falhas e balanceamento. É o modo default, é também o único modo que permite com que o tráfego seja distribuído entre as interfaces slave simultaneamente (ex. duas placas 10/1000 serão uma única interface 2x(10/1000), ou seja, 2Gbit max).

1 (active-backup), apenas um slave no etherchannel está ativo, os demais apenas serão ativados (um de cada vez) no caso de falha do slave ativo. Provê tolerância a falhas e alta disponibilidade.

2 (balance-xor), transmite os pacotes adotando uma política baseado em hashs, provê praticamente as mesmas funcionalidades do modo 0. É possível configurar políticas alternativas modificando o parâmetro xmit_hash_policy ao carregar o módulo. A política padrão é baseada em MAC's (origem/destino XOR'd).

3 (broadcast), transmite todos os pacotes em todos os slaves. Provê tolerância a falhas e alta disponibilidade.

4 (802.3ad), agregação dinâmica de links. é necessário um switch que suporte 802.3ad e que o ethtool consiga configurar os parâmetros speed e duplex em todos os slaves. É necessário também alguma configuração extra no switch; no switch utilizado neste artigo esta configuração tem o nome LACP (IEEE 802.3ad Link Aggregation Protocol).

5 (balance-tlb), load-balance adaptável. Não requer nenhum suporte especial do switch, o tráfego de saída é distribuído de acordo com a carga em cada slave. o tráfego de entrada utiliza um único slave, se ele falhar outro assume e clona o MAC do slave que estava ativo.

6 (balance-alb), de forma bem generalizada é modo 5 turbinado (tlb+rlb).

Voltando à configuração, agora ativamos as placas de rede (as duas D-Link Giga) e a interface lógica (o Channel Bonding).

# ip link set dev eth0 up
# ip link set dev eth1 up
# ip link set dev bond0 up

A interface bond0 irá clonar o MAC da primeira interface ethernet carregada no sistema, no caso a eth0. As duas interfaces configuradas como slave terão também o mesmo MAC da primeira eth detectada.

Poderíamos omitir os "set dev ethx up", pois ao ativarmos o bond0 isso é feito automaticamente.

Feito isto configuramos os endereços de rede e a rota padrão da forma habitual:

# ip address add x.y.z.26/29 broadcast x.y.z.31 dev bond0
# ip route add default via x.y.z.25 dev bond0

O passo final é executar o utilitário que compilamos para ativar efetivamente o Port Trunking/Channel Bonding no sistema:

# ifenslave bond0 eth0 eth1

As interfaces estarão configuradas da seguinte maneira:

7: eth0: <BROADCAST,MULTICAST,SLAVE,UP,10000> [...] master bond0 qlen 1000
    link/ether 00:15:e9:4a:xx:zz brd ff:ff:ff:ff:ff:ff
8: eth1: <BROADCAST,MULTICAST,SLAVE,UP,10000> [...] master bond0 qlen 1000
    link/ether 00:15:e9:4a:xx:zz brd ff:ff:ff:ff:ff:ff
9: bond0: <BROADCAST,MULTICAST,MASTER,UP,10000> mtu 1500 qdisc noqueue
    link/ether 00:15:e9:4a:xx:zz brd ff:ff:ff:ff:ff:ff
    inet x.y.z.26/29 brd x.y.z.31 scope global bond0

Notem que os MAC's estão todos clonados (são todos iguais) e que as interfaces eth0 e eth1 estão ativas e configuradas como slave. A única interface com endereços configurados é o Port Trunking criado.

Para verificar o status da configuração (Channel Bonding), faça o seguinte:

# cat /proc/net/bonding/bond0
Ethernet Channel Bonding Driver: v3.1.2 (January 20, 2007)

Bonding Mode: load balancing (round-robin)
MII Status: up
MII Polling Interval (ms): 100
Up Delay (ms): 0
Down Delay (ms): 0

Slave Interface: eth0
MII Status: up
Link Failure Count: 0
Permanent HW addr: 00:15:e9:4a:xx:yy

Slave Interface: eth1
MII Status: up
Link Failure Count: 0
Permanent HW addr: 00:15:e9:4a:xx:zz

O interessante dessa configuração (modo 0) é que se um dos links "cair" o sistema continua ativo e o link ethernet não para, na verdade nem percebemos o que aconteceu.

Exemplo (desconectei um dos cabos cat6 e num dos terminais deixei monitorando o link):

Nov 14 16:26:49 xxxxx kernel: skge eth1: Link is down.
Nov 14 16:26:50 xxxxx kernel: bonding: bond0: link status \
definitely down for interface eth1, disabling it

Ou seja, a interface foi removida do Channel Bonding e o link continuou ativo. Ao reconectar novamente o cabo a interface automaticamente será reinserida no Channel Bonding.

Os modos 0, 2 e 3 requerem suporte especial e configuração do switch (as portas devem ser configuradas como membros de grupos Trunk/etherchannel).

O modo 6 requer suporte do switch ao padrão 802.3ad. Os demais não requerem suporte especial do switch.

Lembre-se de remover as rotas nas interfaces agregadas como slave, apenas a rota no etherchannel deve ser configurada (e a rota default, claro).

Exemplo:

# ip route show
x.y.z.24/29 dev bond0  proto kernel  scope link  src x.y.z.26
127.0.0.0/8 dev lo  scope link
default via x.y.z.25 dev bond0

Podemos também utilizar o sysfs (como já foi citado) para configurar o Port Trunking, tornando desnecessário o uso do ifenslave, um exemplo que executa a configuração citada acima seria:

# modprobe bonding
# modprobe skge
# echo 0 > /sys/class/net/bond0/bonding/mode
# ip address add x.y.z.26/29 broadcast x.y.z.31 dev bond0
# ip route add default via x.y.z.25 dev bond0
# echo 100 > /sys/class/net/bond0/bonding/miimon
# echo +eth0 /sys/class/net/bond0/bonding/slaves
# echo +eth1 /sys/class/net/bond0/bonding/slaves

Ou seja, carregamos os módulos, setamos o modo de operação, configuramos o endereço e rota padrão, setamos a frequencia de checagem e incluímos as interfaces em modo slave.

Para verificar o uso das interfaces slave, poderíamos fazer algo como:

# for x in 0 1; do ifconfig eth$x | grep bytes; done
          RX bytes:4267502419 (3.9 GiB)  TX bytes:2742529903 (2.5 GiB)
          RX bytes:1341729996 (1.2 GiB)  TX bytes:2464703001 (2.2 GiB)

Poderíamos fazer o mesmo via SNMP:

# snmpwalk -v1 -c public x.y.z.26 | grep -i eth | head -2
IF-MIB::ifDescr.7 = STRING: eth0
IF-MIB::ifDescr.8 = STRING: eth1

E as taxas recuperaríamos da seguinte maneira (agora sabemos quais IfDescr utilizar):

# snmpget -v1 -c public x.y.z.26 IF-MIB::ifOutOctets.8
IF-MIB::ifOutOctets.8 = Counter32: 2617173305
# snmpget -v1 -c public x.y.z.26 IF-MIB::ifOutOctets.7
IF-MIB::ifOutOctets.7 = Counter32: 2894673522
# snmpget -v1 -c public x.y.z.26 IF-MIB::ifInOctets.8
IF-MIB::ifInOctets.8 = Counter32: 1604170366
# snmpget -v1 -c public x.y.z.26 IF-MIB::ifInOctets.7
IF-MIB::ifInOctets.7 = Counter32: 4267713431

Para tornar as informações acima mais user-friendly podemos utilizar softwares que utilizam SNMP e geram gráficos, como o mrtg ou o cacti.

--
referência: /usr/src/linux/Documentation/networking/bonding.txt