Skip to content

Commit aa55f4a

Browse files
LACP in WSL2 post
1 parent 278c8cf commit aa55f4a

File tree

2 files changed

+186
-0
lines changed

2 files changed

+186
-0
lines changed

site/content/posts/lacp-in-wsl2/featured.svg

Lines changed: 4 additions & 0 deletions
Loading
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
+++
2+
title = "LACP 802.3ad bonding for Ubuntu hosts in WSL2"
3+
date = "2025-07-01"
4+
description = "A guide to getting LACP functional for Ubuntu hosts on Containerlab in Windows"
5+
tags = [
6+
"bonding",
7+
"containerlab",
8+
"lacp",
9+
"wsl2",
10+
"linux",
11+
"ubuntu",
12+
]
13+
showComments = "true"
14+
robots = "all"
15+
+++
16+
17+
## Introduction
18+
19+
This article will show you how to get 802.3ad LACP bonding working easily on Linux hosts in Containerlab inside of a WSL2 instance, despite search results an your friendly LLM telling you this is not possible (unless you build your own WSL2 kernel).
20+
21+
## Issue
22+
23+
If you have tried to enable bonding in the traditional way, you will see the below error message:
24+
25+
```bash
26+
admin@host1:~$ sudo modprobe bonding mode=802.3ad miimon=100 lacp_rate=fast
27+
modprobe: FATAL: Module bonding not found in directory /lib/modules/6.6.87.2-microsoft-standard-WSL2
28+
```
29+
30+
Searching around you will find the below explanation:
31+
32+
> WSL2 does not support loading arbitrary kernel modules, including bonding, using modprobe by default. The WSL2 kernel is a custom kernel with pre-compiled modules and does not include the bonding module or support for loading new modules. To use bonding in WSL2, you would need to build a custom WSL2 kernel with the module included and then configure WSL2 to use that custom kernel.
33+
34+
## Solution
35+
36+
To fix this, we can simply include the iproute2 package in the Dockerfile builds of your relevant container, i.e.
37+
38+
```Dockerfile
39+
FROM ubuntu:latest
40+
41+
ENV DEBIAN_FRONTEND=noninteractive
42+
43+
# Install iproute2
44+
RUN apt-get update -y && apt-get install -y \
45+
iproute2 \
46+
&& apt-get autoremove -y \
47+
&& apt-get clean -y \
48+
&& rm -rf /var/lib/apt/lists/*
49+
```
50+
51+
A full example host Dockerfile is available [here](https://github.com/commitconfirmed/npa-showcases/blob/main/containers/lab-host/Dockerfile)
52+
53+
{{< alert >}}
54+
**Note**: I also build the iperf3 application from source since in this Dockerfile since the ubuntu package manager has an older version. You can remove this and the gcc / make packages if you don't want to use iperf3
55+
{{< /alert >}}
56+
57+
From here, you can simply execute the below in a script on the host or via the Containerlab [exec](https://containerlab.dev/cmd/exec/) option in your clab file:
58+
59+
```bash
60+
ip link add bond0 type bond
61+
ip link set bond0 type bond mode 802.3ad lacp_active on lacp_rate fast
62+
ip link set eth1 down
63+
ip link set eth2 down
64+
ip link set eth1 master bond0
65+
ip link set eth2 master bond0
66+
ip addr add 192.168.100.10/24 dev bond0
67+
ip link set bond0 up
68+
```
69+
70+
A full list of options can be seen by executing `ip link help bond`
71+
72+
<details>
73+
<summary>Expand / Collapse command output</summary>
74+
75+
```bash
76+
admin@host1:~$ ip link help bond
77+
Usage: ... bond [ mode BONDMODE ] [ active_slave SLAVE_DEV ]
78+
[ clear_active_slave ] [ miimon MIIMON ]
79+
[ updelay UPDELAY ] [ downdelay DOWNDELAY ]
80+
[ peer_notify_delay DELAY ]
81+
[ use_carrier USE_CARRIER ]
82+
[ arp_interval ARP_INTERVAL ]
83+
[ arp_validate ARP_VALIDATE ]
84+
[ arp_all_targets ARP_ALL_TARGETS ]
85+
[ arp_ip_target [ ARP_IP_TARGET, ... ] ]
86+
[ ns_ip6_target [ NS_IP6_TARGET, ... ] ]
87+
[ primary SLAVE_DEV ]
88+
[ primary_reselect PRIMARY_RESELECT ]
89+
[ fail_over_mac FAIL_OVER_MAC ]
90+
[ xmit_hash_policy XMIT_HASH_POLICY ]
91+
[ resend_igmp RESEND_IGMP ]
92+
[ num_grat_arp|num_unsol_na NUM_GRAT_ARP|NUM_UNSOL_NA ]
93+
[ all_slaves_active ALL_SLAVES_ACTIVE ]
94+
[ min_links MIN_LINKS ]
95+
[ lp_interval LP_INTERVAL ]
96+
[ packets_per_slave PACKETS_PER_SLAVE ]
97+
[ tlb_dynamic_lb TLB_DYNAMIC_LB ]
98+
[ lacp_rate LACP_RATE ]
99+
[ lacp_active LACP_ACTIVE]
100+
[ ad_select AD_SELECT ]
101+
[ ad_user_port_key PORTKEY ]
102+
[ ad_actor_sys_prio SYSPRIO ]
103+
[ ad_actor_system LLADDR ]
104+
[ arp_missed_max MISSED_MAX ]
105+
106+
BONDMODE := balance-rr|active-backup|balance-xor|broadcast|802.3ad|balance-tlb|balance-alb
107+
ARP_VALIDATE := none|active|backup|all|filter|filter_active|filter_backup
108+
ARP_ALL_TARGETS := any|all
109+
PRIMARY_RESELECT := always|better|failure
110+
FAIL_OVER_MAC := none|active|follow
111+
XMIT_HASH_POLICY := layer2|layer2+3|layer3+4|encap2+3|encap3+4|vlan+srcmac
112+
LACP_ACTIVE := off|on
113+
LACP_RATE := slow|fast
114+
AD_SELECT := stable|bandwidth|count
115+
```
116+
</details>
117+
118+
## Demonstration
119+
120+
See the below output from a host in my [npa-showcases](/npa-showcases) repo in the [basic-ceos-clos](https://github.com/commitconfirmed/npa-showcases/tree/main/examples/basic-ceos-clos) lab.
121+
122+
```bash
123+
admin@host1:~$ ip addr list bond0.100
124+
9: bond0.100@bond0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
125+
link/ether aa:c1:ab:44:7a:12 brd ff:ff:ff:ff:ff:ff
126+
inet 192.168.100.10/24 scope global bond0.100
127+
valid_lft forever preferred_lft forever
128+
inet6 fe80::a8c1:abff:fe44:7a12/64 scope link
129+
valid_lft forever preferred_lft forever
130+
131+
admin@host1:~$ cat /proc/net/bonding/bond0
132+
Ethernet Channel Bonding Driver: v6.6.87.2-microsoft-standard-WSL2
133+
134+
Bonding Mode: IEEE 802.3ad Dynamic link aggregation
135+
Transmit Hash Policy: layer2 (0)
136+
MII Status: up
137+
MII Polling Interval (ms): 100
138+
Up Delay (ms): 0
139+
Down Delay (ms): 0
140+
Peer Notification Delay (ms): 0
141+
142+
802.3ad info
143+
LACP active: on
144+
LACP rate: fast
145+
Min links: 0
146+
Aggregator selection policy (ad_select): stable
147+
148+
Slave Interface: eth1
149+
MII Status: up
150+
Speed: 10000 Mbps
151+
Duplex: full
152+
Link Failure Count: 0
153+
Permanent HW addr: aa:c1:ab:44:7a:12
154+
Slave queue ID: 0
155+
Aggregator ID: 2
156+
Actor Churn State: monitoring
157+
Partner Churn State: monitoring
158+
Actor Churned Count: 0
159+
Partner Churned Count: 0
160+
161+
Slave Interface: eth2
162+
MII Status: up
163+
Speed: 10000 Mbps
164+
Duplex: full
165+
Link Failure Count: 0
166+
Permanent HW addr: aa:c1:ab:9e:04:09
167+
Slave queue ID: 0
168+
Aggregator ID: 2
169+
Actor Churn State: monitoring
170+
Partner Churn State: monitoring
171+
Actor Churned Count: 0
172+
Partner Churned Count: 0
173+
174+
admin@host1:~$ ping 192.168.100.1
175+
PING 192.168.100.1 (192.168.100.1) 56(84) bytes of data.
176+
64 bytes from 192.168.100.1: icmp_seq=1 ttl=64 time=1.91 ms
177+
64 bytes from 192.168.100.1: icmp_seq=2 ttl=64 time=1.24 ms
178+
^C
179+
--- 192.168.100.1 ping statistics ---
180+
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
181+
rtt min/avg/max/mdev = 1.241/1.577/1.913/0.336 ms
182+
```

0 commit comments

Comments
 (0)