Una técnica muy utilizada para proporcionar mayor disponibilidad y capacidad a la conexión entre dos equipos consiste en utilizar simultáneamente varios enlaces físicos entre ambos, formando un grupo de agregación de enlaces o LAG (Link Aggregation Group). LAG considero que es el término más correcto, aunque también son conocidos como port channel en el mundo de las redes o bonding en el mundo GNU/Linux. También, aunque de forma incorrecta en mi opinión, se les llama a veces trunk (de una tecnología propietaria llamada port trunking), esto a mí me resulta ambiguo, porque el uso más generalizado del término es para referrirse a un puerto de un switch por el que se permite tráfico de varias VLAN.

El caso es que para proporcionar mayor ancho de banda a un nuevo servidor de ficheros se quiere establecer un LAG entre este y el correspondiente conmutador (bonito término castellano para un switch). Esto, en principio, es bastante sencillo, sobre todo teniendo en cuenta que el servidor corre un Red Hat Enterprise Linux 7.3 (suficientemente moderno, su núcleo es la versión 3.10 de Linux) y el switch es un Cisco que ha costado más de lo que amortizo de hipoteca en un año.

Para establecer el LAG entre el servidor y el switch se va a utilizar el protocolo LACP (Link Aggregation Control Protocol), soportado por ambos.

El switch se configura creando un nuevo interfaz del tipo PortChannel, que será un interfaz virtual que representa al grupo de agregación, y añadiendo los interfaces físicos a utilizar a dicho grupo.

Aquí llamaremos al port channel port-channel1 (Po1 para los amigos) y añadiremos a su grupo los interfaces Ethernet1/1 al Ethernet1/4.

interface Po1

interface Eth1/1 - 4
  channel-group 1

Con esto es suficiente, ya que aunque hay varias formas de establecer el LAG, el modelo de switch utilizado usa el protocolo LACP por defecto. Como se verá más adelante, suponer que LACP era utilizado por defecto fue la causa del problema.

En el servidor se hace de forma similar creando un ficheo de configuración para el bonding que llamaremos bond0 y cambiando la configuración de los interfaces físicos a añadir al LAG.

/etc/sysconfig/network-scripts/ifcfg-bond0:

TYPE=Bond
BOOTPROTO=none
NAME=bond0
DEVICE=bond0
ONBOOT=yes
BONDING_MASTER=yes
IPADDR=192.168.10.10
GATEWAY=192.168.10.1
PREFIX=24
BONDING_OPTS="mode=4 miimon=100 lacp_rate=1"

Aquí es necesario indicar el modo del bonding, ya que soporta varios tipos de funcionamiento, el modo 4 utilizado es el que corresponde al protocolo 802.3ad, es decir LACP. El parámetro miimon indica cada cuantos milisegundos se debe comprobar si un interfaz del bonding tiene enlace, si se detecta que no tiene enlace será extraído del LAG inmediatamente. El parámetro lacp_rate indica cada cuantos segundos se envía un paquete de control de LACP (LACPDU).

Los interfaces miembro del LAG se configuran así:

/etc/sysconfig/network-scripts/ifcfg-eth0

TYPE=Ethernet
BOOTPROTO=none
NAME=eth0
DEVICE=eth0
ONBOOT=yes
MASTER=bond0
SLAVE=yes

Una vez configurados los interfaces del servidor (habrá que reiniciar los servicios de red) y el switch se debe establecer el LAG entre ambos.

Para ver el estado del interfaz bond0 se puede consultar el fichero /proc/net/bonding/bond0, aunque también podemos obtener suficiente información con un simple listado de los interfaces:

#ip link
...
8: eth0: <BROADCAST,MULTICAST,SLAVE,UP,LOWER_UP> mtu 1500 qdisc mq master bond0 state UP mode DEFAULT qlen 1000
    link/ether a0:b6:cf:d0:2d:f8 brd ff:ff:ff:ff:ff:ff
9: eth1: <BROADCAST,MULTICAST,SLAVE,UP,LOWER_UP> mtu 1500 qdisc mq master bond0 state UP mode DEFAULT qlen 1000
    link/ether a0:b6:cf:d0:2d:f8 brd ff:ff:ff:ff:ff:ff
10: bond0: <BROADCAST,MULTICAST,MASTER,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT qlen 1000
    link/ether a0:b6:cf:d0:2d:f8 brd ff:ff:ff:ff:ff:ff
...

Aquí se ve que hay dos interfaces eth0 y eth1 en modo SLAVE (parte de un bonding) y que hay un interfaz en modo MASTER (el bonding), todos están UP (tienen enlace) y todos tienen la misma MAC (como debe ser), ya que todos los interfaces físicos actúan «en representación» del interfaz de bonding.

En el switch también parece estar todo bien:

#sh int Po1
port-channel1 is up
admin state is up,
  Hardware: Port-Channel, address: a4bd.c6db.aac9 (bia a4bd.c6db.aac9)
  MTU 1500 bytes, BW 20000000 Kbit, DLY 10 usec
  reliability 255/255, txload 1/255, rxload 1/255
  Encapsulation ARPA, medium is broadcast
  Port mode is access
  full-duplex, 10 Gb/s
  Input flow-control is off, output flow-control is off
  Auto-mdix is turned off
  Switchport monitor is off
  EtherType is 0x8100
  Members in this channel: Eth1/1, Eth1/2, Eth1/3, Eth1/4
  Last clearing of "show interface" counters never
  1 interface resets
  30 seconds input rate 2328 bits/sec, 1 packets/sec
  30 seconds output rate 2520 bits/sec, 1 packets/sec
  Load-Interval #2: 5 minute (300 seconds)
    input rate 12.83 Mbps, 1.02 Kpps; output rate 139.13 Kbps, 181 pps
  RX
    2705286 unicast packets  3316 multicast packets  3932 broadcast packets
    2712534 input packets  3986560464 bytes
    0 jumbo packets  0 storm suppression packets
    0 runts  0 giants  0 CRC  0 no buffer
    0 input error  0 short frame  0 overrun   0 underrun  0 ignored
    0 watchdog  0 bad etype drop  0 bad proto drop  0 if down drop
    0 input with dribble  0 input discard
    0 Rx pause
  TX
    510610 unicast packets  29736 multicast packets  3699 broadcast packets
    544045 output packets  53042671 bytes
    0 jumbo packets
    0 output error  0 collision  0 deferred  0 late collision
    0 lost carrier  0 no carrier  0 babble  0 output discard
    0 Tx pause

En esta información lo importante es que el port channel está UP y que el ancho de banda del mismo es 20.000.000 Kbit. El ancho de banda indica que hay dos puertos, de los cuatro que son miembros del port channel, que están conectados (se trata de puertos de 10 Gbps).

Viendo esto todo parece estar bien, sin embargo, al hacer un ping a otro equipo que se envía por dicho interfaz resulta que no hay respuesta:

# ping 192.168.10.20
PING 192.168.10.20 (192.168.10.10) 56(84) bytes of data.
From 192.168.10.10 icmp_seq=1 Destination Host Unreachable
From 192.168.10.10 icmp_seq=2 Destination Host Unreachable

Habrá que investigar, para ello lo primero comprobar qué pasa por el interfaz bond0:

# tcpdump -nn -i bond0
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on bond0, link-type EN10MB (Ethernet), capture size 65535 bytes
11:59:12.717212 ARP, Request who-has 192.168.10.20 tell 192.168.10.10, length 28
11:59:13.719184 ARP, Request who-has 192.168.10.20 tell 192.168.10.10, length 28

Y así sucesivamente, es decir, los ARP no obtienen respuesta, pero eso ¿por qué? Indaguemos un poco más y veamos el tráfico en cada interfaz:

# tcpdump -nn -i eth0
tcpdump: WARNING: eth0: no IPv4 address assigned
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
12:01:53.985192 ARP, Request who-has 192.168.10.20 tell 192.168.10.10, length 28
12:01:54.987189 ARP, Request who-has 192.168.10.20 tell 192.168.10.10, length 28

Vale, el bond0 está utilizando el interfaz eth0 para envíar las consultas ARP y no obtiene respuesta. Veamos entonces si hay algo extraño en el eth1:

# tcpdump -nn -i eth1
tcpdump: WARNING: eth1: no IPv4 address assigned
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth1, link-type EN10MB (Ethernet), capture size 65535 bytes
12:03:21.343287 ARP, Reply 192.168.10.20 is-at a0:d6:cf:d0:32:b0, length 46
12:03:22.940519 ARP, Reply 192.168.10.20 is-at a0:b6:cf:d0:32:b0, length 46

Bueno, no es muy extraño, la política de reparto de carga entre los enlaces no es la misma en el switch que en el servidor, por lo que el servidor envía una consulta ARP por un interfaz y la recibe por el otro. A mí me parece normal.

Entonces, ¿por qué no pasan las respuetas ARP del interfaz eth1 al bond0? Pues porque el bonding está medio sordo. Pero, ¿por qué está medio sordo el bonding?

Comienza la investigación por el Interné: Google, a ver qué encuentras sobre esto: «Linux bonding medio sordo». Pero parece que nadie ha escrito aún sobre esto, bueno a ver en inglés: «Linux bonding half deaf». Tampoco.

En fin, toca leer la documentación del módulo de bonding y relacionada. Así, que tras no encontrar nada muy claro y buscar de todo relacionado con el bonding y 802.3ad dí con el artículo que me dio la clave (aunque trataba de otro problema): [Linux Bonding] 802.3ad bond interface has shown RX dropped packets.

En ese artículo hablaba de que el bonding, de manera premeditada y (en otros casos, desde luego) correcta, descartaba los paquetes recibidos por los interfaces no activos del bonding. Esto está muy bien cuando el modo del bonding es tal que unos interfaces están activos y otros no, que no es el caso del modo 4, 802.3ad o LACP.

Lo importante es que daba la clave de como evitar eso, el parámetro all_slaves_active. Así, estableciendo ese parámetro a uno se puede hacer un apaño y permitir que los paquetes recibidos por el interfaz eth1 sean admitidos y lleguen como recibidos por el interfaz bond0.

# echo 1 > /sys/class/net/bond0/bonding/all_slaves_active

Pero, como he dicho, eso es solo un apaño, ya que el problema de fondo aún está ahí. ¿Y cuál es ese problema? Pues que el bond0 no considera activo el interfaz eth1, pasa de él, como se deduce de lo siguiente:

# ethtool bond0
Settings for bond0:
        Supported ports: [ ]
        Supported link modes:   Not reported
        Supported pause frame use: No
        Supports auto-negotiation: No
        Advertised link modes:  Not reported
        Advertised pause frame use: No
        Advertised auto-negotiation: No
        Speed: 10000Mb/s
        Duplex: Full
        Port: Other
        PHYAD: 0
        Transceiver: internal
        Auto-negotiation: off
        Link detected: yes

El ancho de banda del interfaz es 10.000Mbps, es decir, lo que da un interfaz. Si estuviera utilizando los dos sería 20.000Mbps, como se vió en el estado del LAG en el lado del switch.

Por tanto el switch ha activado ambos enlaces del LAG (como se ve al recibir tráfico por los dos interfaces en el servidor), pero el servidor solo ha activado uno.

Mirando el detalle del estado del bonding se ve lo siguiente:

# cat /proc/net/bonding/bond0
Ethernet Channel Bonding Driver: v3.7.1 (April 27, 2011)

Bonding Mode: IEEE 802.3ad Dynamic link aggregation
Transmit Hash Policy: layer3+4 (1)
MII Status: up
MII Polling Interval (ms): 100
Up Delay (ms): 0
Down Delay (ms): 0

802.3ad info
LACP rate: slow
Min links: 0
Aggregator selection policy (ad_select): stable
System priority: 65535
System MAC address: a0:b6:cf:d0:2d:f8
Active Aggregator Info:
        Aggregator ID: 1
        Number of ports: 1
        Actor Key: 13
        Partner Key: 1
        Partner Mac Address: 00:00:00:00:00:00

Slave Interface: eth0
MII Status: up
Speed: 10000 Mbps
Duplex: full
Link Failure Count: 0
Permanent HW addr: a0:b6:cf:d0:2d:f8
Slave queue ID: 0
Aggregator ID: 1
Actor Churn State: none
Partner Churn State: churned
Actor Churned Count: 0
Partner Churned Count: 1
details actor lacp pdu:
    system priority: 65535
    system mac address: a0:b6:cf:d0:2d:f8
    port key: 13
    port priority: 255
    port number: 1
    port state: 77
details partner lacp pdu:
    system priority: 65535
    system mac address: 00:00:00:00:00:00
    oper key: 1
    port priority: 255
    port number: 1
    port state: 1

Slave Interface: eth1
MII Status: up
Speed: 10000 Mbps
Duplex: full
Link Failure Count: 0
Permanent HW addr: a0:b6:cf:d0:2d:fa
Slave queue ID: 0
Aggregator ID: 2
Actor Churn State: churned
Partner Churn State: churned
Actor Churned Count: 1
Partner Churned Count: 1
details actor lacp pdu:
    system priority: 65535
    system mac address: a0:b6:cf:d0:2d:f8
    port key: 13
    port priority: 255
    port number: 2
    port state: 69
details partner lacp pdu:
    system priority: 65535
    system mac address: 00:00:00:00:00:00
    oper key: 1
    port priority: 255
    port number: 1
    port state: 1

Una mente avezada no hubiera pasado por alto (al contrario de como hice yo inicialmente) la discordancia en los «Aggregator ID» de ambos interfaces miembros del bonding. Estaba empeñado en que la culpa era de la configuración del bonding pero, preguntándole a Google por qué puede haber diferentes aggregator ID, me dice un par de cosas. En StackExchange ya me confirman algo que no terminaba de interpretar correctamente en la documentación del bonding, y es que los diferentes aggregator ID están destinados a hacer grupos de interfaces separados dentro del bonding, utilizando solo uno de ellos. Esto me hace pensar que, tal vez, el switch esté haciendo algo mal.

La confirmación de esto la tuve en esta entrada del foro de CentOS, Only 1 NIC used in the bond, ahí se apuntaba a una entrada de un blog en la que se trataba otro síntoma, la MAC 00:00:00:00:00:00 del otro extremo. En esta entrada se remarca en negrita que lo que hay que hacer es comprobar que el port channel está en modo activo (LACP). Así que fui a hacer la comprobación:

# show port-channel database
port-channel1
    Last membership update is successful
    4 ports in total, 2 ports up
    First operational port is Ethernet1/1
    Age of the port-channel is 0d:20h:20m:41s
    Time since last bundle is 0d:20h:20m:51s
    Last bundled member is Ethernet1/4
    Ports:   Ethernet1/1    [on] [up]
             Ethernet1/2    [on] [up] *
             Ethernet1/3    [on] [down]
             Ethernet1/4    [on] [down]

 Había supuesto erróneamente que el modo por defecto era LACP, cuando en realidad era ON. En el modo on el switch simplemente añade los puertos al port channel si tienen enlace, sin más. Por tanto para el switch los dos puertos conectados al servidor formaban parte del port channel y, consecuentemente repartía el tráfico de salida entre ellos. Pero como el servidor estaba en modo LACP y no lograba negociar la agregación con este protocolo, asigna cada interfaz a un grupo de agregación distinto y utiliza solo uno de los grupos, formado por un solo interfaz.

La solución parecía ya al alcance de las manos.

# configure terminal
(config)# interface Eth1/1 - 4
(config-if-range)# no channel-group 1
(config-if-range)# channel-group 1 mode active
LACP process needs to be started before configuring active or passive mode

Resulta que ni siquiera estaba activada la capacidad LACP en el switch. En NX-OS la mayoría de capacidades del switch vienen desactivadas por defecto y hay que activarlas antes de utilizarlas. Activémosla pues y repitamos:

(config)# feature lacp
(config)# interface Eth1/1 - 4
(config-if-range)# channel-group 1 mode active
(config-if-range)# sh port-channel database
port-channel1
    Last membership update is successful
    4 ports in total, 0 ports up
    Age of the port-channel is 0d:20h:34m:57s
    Time since last bundle is 0d:00h:00m:48s
    Last bundled member is Ethernet1/1
    Time since last unbundle is 0d:00h:04m:50s
    Last unbundled member is Ethernet1/4
    Ports:   Ethernet1/1    [active ] [up]
             Ethernet1/2    [active ] [up] *
             Ethernet1/3    [active ] [down]
             Ethernet1/4    [active ] [down]
(config-if-range)# copy running-config startup-config
[########################################] 100%
Copy complete, now saving to disk (please wait)...

Ahora.

Comprobemos el otro lado:

# cat /proc/net/bonding/bond0
Ethernet Channel Bonding Driver: v3.7.1 (April 27, 2011)

Bonding Mode: IEEE 802.3ad Dynamic link aggregation
Transmit Hash Policy: layer3+4 (1)
MII Status: up
MII Polling Interval (ms): 100
Up Delay (ms): 0
Down Delay (ms): 0

802.3ad info
LACP rate: slow
Min links: 0
Aggregator selection policy (ad_select): stable
System priority: 65535
System MAC address: a0:b6:cf:d0:2d:f8
Active Aggregator Info:
        Aggregator ID: 1
        Number of ports: 2
        Actor Key: 13
        Partner Key: 110
        Partner Mac Address: 00:01:02:03:aa:fc

Slave Interface: p4p1
MII Status: up
Speed: 10000 Mbps
Duplex: full
Link Failure Count: 2
Permanent HW addr: a0:b6:cf:d0:2d:f8
Slave queue ID: 0
Aggregator ID: 1
Actor Churn State: none
Partner Churn State: none
Actor Churned Count: 1
Partner Churned Count: 2
details actor lacp pdu:
    system priority: 65535
    system mac address: a0:b6:cf:d0:2d:f8
    port key: 13
    port priority: 255
    port number: 1
    port state: 61
details partner lacp pdu:
    system priority: 32768
    system mac address: 00:01:02:03:aa:fc
    oper key: 110
    port priority: 32768
    port number: 389
    port state: 61

Slave Interface: p4p2
MII Status: up
Speed: 10000 Mbps
Duplex: full
Link Failure Count: 2
Permanent HW addr: a0:b6:cf:d0:2d:fa
Slave queue ID: 0
Aggregator ID: 1
Actor Churn State: none
Partner Churn State: none
Actor Churned Count: 2
Partner Churned Count: 2
details actor lacp pdu:
    system priority: 65535
    system mac address: a0:b6:cf:d0:2d:f8
    port key: 13
    port priority: 255
    port number: 2
    port state: 61
details partner lacp pdu:
    system priority: 32768
    system mac address: 00:01:02:03:aa:fc
    oper key: 110
    port priority: 32768
    port number: 385
    port state: 61
# ethtool bond0
Settings for bond0:
        Supported ports: [ ]
        Supported link modes:   Not reported
        Supported pause frame use: No
        Supports auto-negotiation: No
        Advertised link modes:  Not reported
        Advertised pause frame use: No
        Advertised auto-negotiation: No
        Speed: 20000Mb/s
        Duplex: Full
        Port: Other
        PHYAD: 0
        Transceiver: internal
        Auto-negotiation: off
        Link detected: yes

Todo listo, ambos interfaces en el mismo grupo de agregación, el ancho de banda de ambos interfaces agregado al bonding y la MAC del switch detectada.

¡El bonding ya no está medio sordo!

Ya se puede desactivar el apaño del all_slaves_active, el LAG está ya funcionando correctamente (aunque esta configuración no era persistente).

# echo 0 > /sys/class/net/bond0/bonding/all_slaves_active

Otras referencias:

Para entender el significado de los estados de los interfaces del bonding (port state) ver las definiciones AD_STATE_… en el código del fichero bond_3ad.c.

El estado mostrado en /proc/net/bonding/bond0 es la representación en decimal del resultado de unir (OR) los valores que corresponden al estado del puerto según las definiciones encontradas en dicho código fuente. Es decir, cuando se estableció correctamente el LAG, el estado del bonding era 61 = 0x3d = 0x20+ 0x10 + 0x08 + 0x04 + 0x01 = DISTRIBUTING + COLLECTING + SYNCHRONIZATION + AGGREGATION + LACP_ACTIVITY.

Antes, cuando el bonding estaba medio sordo, un interfaz del bonding estabe en estado 77 = 0x4d = 0x40 + 0x08 + 0x04 + 0x01 = DEFAULTED + SYNCHRONIZATION + AGGREGATION + LACP_ACTIVITY. El otro puerto estaba en estado 69 = 0x45 = 0x40 + 0x04 + 0x01 = DEFAULTED + AGGREGATION + LACP_ACTIVITY.

5 comentarios en “El extraño caso del bonding medio sordo

Dejar una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Puedes utilizar estas etiquetas y atributos HTML:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.