Link Aggregation
Link Aggregation
Link aggregation (LAG / bonding / etherchannel-class ideas) combines multiple physical links into one logical link: more bandwidth, faster failover, and fewer STP blocked paths between the same pair of devices—when both ends agree.
Learning goals
By the end of this chapter you can:
- Explain when LAG helps vs when it hides problems
- Contrast static bundle vs LACP
- Configure a Linux bond in a lab
- Predict traffic behavior and failover
- Verify member health and hashing limitations
Concepts
One logical link
+---- eth1 ----+
sw1 -+ +- sw2
+---- eth2 ----+
\____ bond0 / lag0 ____/
Upper protocols (STP, L3 adjacency) see one link if bundling is correct.
Static vs LACP
| Mode | Pros | Cons |
|---|---|---|
| Static / on | Simple | Mis-wiring may blackhole; no negotiation |
| LACP | Detects partner; protects against some mistakes | Both ends must support/enable |
Labs may start static; production almost always prefers LACP.
Hashing
Flows distribute across members by a hash (MAC/IP/L4 ports depending on config). One TCP flow typically stays on one member—do not expect a single iperf stream to fill N× bandwidth.
Split brain / misconfig
Classic fail: one side bundles, the other has independent ports—loops or blackholes. Always verify both ends.
Linux bonding quickstart
Kernel bonding modes (selection depends on goal):
| Mode | Name | Notes |
|---|---|---|
| 0 | balance-rr | Round-robin |
| 1 | active-backup | Simple failover |
| 4 | 802.3ad | LACP |
# Example active-backup (conceptual host commands)
modprobe bonding
ip link add bond0 type bond mode active-backup miimon 100
ip link set eth1 down
ip link set eth2 down
ip link set eth1 master bond0
ip link set eth2 master bond0
ip addr add 10.0.0.1/24 dev bond0
ip link set bond0 up
ip link set eth1 up
ip link set eth2 up
cat /proc/net/bonding/bond0In containers, bonding support depends on privileges and kernel modules on the host. Containerlab labs sometimes simulate aggregation at the NOS layer instead. If bonding is blocked in your environment, run this chapter on a Linux VM/nested host and keep Containerlab for endpoints.
Containerlab topology (two paths, bond on routers)
name: l2-lag
topology:
nodes:
r1:
kind: linux
image: alpine:3.20
binds:
- ./config/r1-bond.sh:/startup.sh
cmd: /bin/sh /startup.sh
# may need privileged: true depending on clab version/kind options
r2:
kind: linux
image: alpine:3.20
binds:
- ./config/r2-bond.sh:/startup.sh
cmd: /bin/sh /startup.sh
h1:
kind: linux
image: alpine:3.20
exec:
- apk add --no-cache iproute2 iputils
- ip addr add 192.168.1.10/24 dev eth1 && ip link set eth1 up
- ip route add default via 192.168.1.1
h2:
kind: linux
image: alpine:3.20
exec:
- apk add --no-cache iproute2 iputils
- ip addr add 192.168.2.10/24 dev eth1 && ip link set eth1 up
- ip route add default via 192.168.2.1
links:
- endpoints: ["h1:eth1", "r1:eth3"]
- endpoints: ["h2:eth1", "r2:eth3"]
- endpoints: ["r1:eth1", "r2:eth1"]
- endpoints: ["r1:eth2", "r2:eth2"]config/r1-bond.sh
#!/bin/sh
set -e
apk add --no-cache iproute2 iputils >/dev/null
# fallback if bonding unavailable: sequential routes demo only
if ip link add bond0 type bond mode 802.3ad 2>/dev/null; then
ip link set eth1 down
ip link set eth2 down
ip link set eth1 master bond0
ip link set eth2 master bond0
ip addr add 10.0.0.1/24 dev bond0
ip link set bond0 up
ip link set eth1 up
ip link set eth2 up
else
echo "bonding unavailable; configure two links manually for failover drill" >&2
ip addr add 10.0.0.1/24 dev eth1
ip link set eth1 up
ip link set eth2 up
fi
ip addr add 192.168.1.1/24 dev eth3
ip link set eth3 up
sysctl -w net.ipv4.ip_forward=1
ip route add 192.168.2.0/24 via 10.0.0.2
sleep infinitySymmetric on r2 with 10.0.0.2, LAN 192.168.2.1/24, route to 192.168.1.0/24 via 10.0.0.1.
If LACP mode fails without a partner daemon, use mode active-backup for reliable lab failover learning.
Predict → observe → fix
Predict
- h1→h2 ping works through logical path
- Shutting one member keeps session alive (active-backup or LACP with remaining member)
- Single flow throughput ≤ one member speed
Observe
docker exec clab-l2-lag-h1 ping -c 5 192.168.2.10
docker exec clab-l2-lag-r1 cat /proc/net/bonding/bond0 2>/dev/null || true
docker exec clab-l2-lag-r1 ip -br linkFailover drill
docker exec clab-l2-lag-r1 ip link set eth1 down
docker exec clab-l2-lag-h1 ping -c 10 192.168.2.10
docker exec clab-l2-lag-r1 ip link set eth1 upMeasure loss count. Journal recovery time.
Mismatch drill
On r2, do not enslave eth2; leave as separate interface with another IP.
Predict: loops, flaps, or partial connectivity—document what your kernel does; fix by matching configs.
Hashing awareness drill
# multiple parallel streams may use more members than one stream
# on hosts with iperf3
iperf3 -s # h2
iperf3 -c 192.168.2.10 -P 8Compare -P 1 vs -P 8.
STP interaction
Between two switches, a multi-link LAG is one logical link ⇒ STP sees one edge. Independent unbundled links ⇒ STP blocks all but one (or you loop). Bundle before you celebrate “redundant cables.”
Operational checks (NOS-neutral)
| Check | Intent |
|---|---|
| Members all up | Physical/L1 |
| LACP PDUs both ways | Negotiation |
| Same speed/duplex | Bundle eligibility |
| Counters increment on members | Actual use |
| Control plane adjacency single | Logical link view |
Common mistakes
| Mistake | Result |
|---|---|
| One end LACP, other static only | Stuck negotation / down |
| Different VLANs on members | Inconsistent bundle |
| Expect one flow = N x bandwidth | Disappointed benchmarks |
| Forget host firewall on bond MAC | Odd ARP |
Summary
- LAG = one logical link from multiple physical members
- Prefer LACP in real life; static only with tight control
- Hashing limits single-flow throughput
- Verify both ends; failover-test by shutting members
- Unbundled parallel L2 links reintroduce STP/loop problems
Next: L2 failure drills—deliberate breakage across MAC, VLAN, loop, and bundle scenarios with evidence.