
the cw917x ap series came out end 2k24 w/ wi-fi 7 + a global-use “day 0” meraki “guap” algorithm to configure the ap either on on meraki || catalyst internal software at first boot
for this cw917x guap global use ap onboarding software, cisco has add an extension to the dhcp 43 option (the f3) && remove the ability to instruct commands via cli (only the offline-migration-info command is available)
the c9k8 must run ios-xe 17.15.2 or higher. licensing moved to the “cisco networking subscription” (wireless essentials && advantage, not the “dna” ones anymore). c9k8 needs capwap-discovery onboarding all command on the ap join profile to accept broadcast discovery on the same subnet
single global sku is not baked-in regulatory domain. country code needs to be resolved at runtime (gps, proximity ap that has one, migration or raf), else radios are down w/ -UN country code
it took me days to troubleshoot && add a c9176i ap to a c9k8 wlc, taking wireshark traces, reading docs that misses some parts (and is mostly meraki oriented). so i decided to post here my findings, troubleshoot && how i made a lab running on my laptop
the setup#
here are the elements of my lab, i ran the vms on my laptop (ryzen 7 7840hs, 32gb, debian 13)
i used a meraki ms130-12x for a l2 sw w/ poe capability
i used my room wi-fi only because i only have one rj45 nic (enxf4a80d5addf0), any default gw will do
%%{init: {'theme':'dark'}}%%
graph TD
internet(("the global cat videos kingdom (internet)"))
subgraph laptop
wifi(wlan
personal wlan, cause i only have 1 rj45 nic)
virbr0(virbr0 libvirt nat
192.168.122.1/24)
br0(br0
10.0.0.2/24)
win["win server 2k25
(dhcp + dns)
10.0.0.5"]
wlc[c9k8-cl
gi1 -> no use
gi2 -> svi vlan10 10.0.0.10]
end
sw("ms130-12x
no config, for poe only")
ap[cw9176i
10.0.0.101
degraded @ 30w]
clients[wi-fi clients
dhcp 10.0.0.100-200]
internet --- wifi
virbr0 -. nat masquerade .- wifi
wlc ---|virtio| virbr0
wlc ---|virtio| br0
win ---|virtio| br0
br0 ---|enxf4a80d5addf0 untagged| sw
sw ---|mgig 2.5g poe+ 30w| ap
ap .- clients
ap -. capwap dtls on udp 5246/5247 .- wlc
here are the commands i used to setup my lab
checking kvm acceleration
egrep -c '(vmx|svm)' /proc/cpuinfo # 0 == unsupported, >=1 == supported (enabled in bios)
kvm-ok # expect "KVM acceleration can be used" (package cpu-checker)
ls -l /dev/kvm # device must exist
lsmod | grep kvm # kvm_intel or kvm_amd loaded
systemctl status libvirtd # active (running)
virsh -c qemu:///system versionpackages i used specifically
you can the add the genisoimage package if you want to boostrap the iosxe_config.txt file into the wlc
apt install -y --no-install-recommends \
qemu-system-x86 qemu-utils libvirt-daemon-system \
libvirt-daemon-config-network dnsmasq-base libvirt-clients \
virtinst ovmf virt-viewer swtpm-toolsadd yourself to the appropriate groups to avoid using root for the actions regarding the vms
usermod -aG libvirt,kvm <your_user>
# log out && back in, then
groups # should list libvirt + kvmto list the vms + see their states, you can use thevirsh list --all command
you can load the vhost_net (virtio network accelerator) kernel module w/ out restarting like this + enable it at start
modprobe vhost_net && echo vhost_net | tee /etc/modules-load.d/vhost_net.confbridge br0 creation
nmcli connection add type bridge ifname br0 con-name br0
nmcli connection modify br0 ipv4.method manual ipv4.addresses 10.0.0.2/24
nmcli connection modify br0 bridge.stp noattaching the nic as a bridge port to the rj45 nic + activating it
nmcli connection add type ethernet ifname enxf4a80d5addf0 master br0 con-name br0-port
nmcli connection up br0
nmcli connection up br0-portlook if br0 has the associated ip using ip addr show br0 && if your nic is attached to br0 using bridge link
you can download the latest virtio here && the c9k8 virtual iso here
i used a windows server for the dhcp server because various docs from cisco was using it && showing configuration pictures
virt-install \
--name srv2k25 \
--os-variant win2k22 \
--cpu host-passthrough \
--vcpus 4 \
--memory 8192 \
--disk path=/var/lib/libvirt/images/srv2k25.qcow2,size=64,bus=virtio,format=qcow2,cache=none,io=native,discard=unmap \
--disk path=<your_windows_srv_install_iso_here>,device=cdrom,bus=sata \
--disk path=<your_virtiowin_iso_here>,device=cdrom,bus=sata \
--network bridge=br0,model=virtio \
--graphics vnc \
--video virtio \
--tpm backend.type=emulator,backend.version=2.0,model=tpm-crb \
--boot uefi
virt-viewer --connect qemu:///system --wait srv2k25i installed windows, virtiotools, setup ip, enabled dhcp (Install-WindowsFeature DHCP -IncludeManagementTools), config via dhcpmgmt.msc -> adding a scope && add options
going through the options later
for the access to the kingdom of cat pictures for the c9k8
virsh net-define /usr/share/libvirt/networks/default.xml
virsh net-autostart default
virsh net-start default&& the installation of the c9k8 vm
virt-install \
--name c9k8-cl \
--virt-type kvm \
--os-variant rhel8.0 \
--cpu host-passthrough \
--vcpus 4 \
--memory 8192 \
--import \
--disk path=/<your_c9k8_qcow2_image_here>,bus=virtio,format=qcow2,cache=none,io=native \
--network network=default,model=virtio \
--network bridge=br0,model=virtio \
--rng /dev/urandom,model=virtio \
--graphics vnc \
--console pty,target_type=serial \
--noautoconsoleclick to see my wlc config
en
conf t
hostname c9k8-xeylou
!
aaa new-model
aaa authentication login default local
aaa authorization exec default local
aaa authentication login CONSOLE none
aaa authorization exec CONSOLE none
username admin privilege 15 secret Cisco123!
enable secret Cisco123!
ip domain name xeyloulab.local
service password-encryption
clock timezone CET 1 0
clock summer-time CEST recurring last Sun Mar 2:00 last Sun Oct 3:00
vlan 10
name WLAB
vlan 999
name CAT_KINGDOM
!
interface GigabitEthernet1
description nat-virbr0
switchport mode access
switchport access vlan 999
no shutdown
!
interface GigabitEthernet2
description LAB
switchport mode access
switchport access vlan 10
spanning-tree portfast
no shutdown
exit
!
interface Vlan10
description Wireless-Management
ip address 10.0.0.10 255.255.255.0
no shutdown
exit
interface Vlan999
description CAT_+_access
ip address 192.168.122.10 255.255.255.0
no shutdown
exit
ip route 0.0.0.0 0.0.0.0 192.168.122.1
ip name-server 8.8.8.8
!
ip http server
ip http secure-server
ip http authentication local
wireless management interface Vlan10
wireless country FR
crypto key generate rsa modulus 2048
wireless config vwlc-ssc key-size 2048 signature-algo sha256 password 0 Soleil123
capwap-discovery onboarding all
!
line vty 0 4
login authentication default
transport input ssh
end
wroption 43#
option 43 vendor-specific information is an opaque byte string, it stores && ships. the parsing happens on the client : the option carries a sequence of tlv blocks (type, length, value), each ap walks the sequence, consumes the tlv it knows, ignores the rest
f1 classic capwap discovery#
historical “241” (hexa f1), read by every ap (including the c91xx) && by the “standard” offline-migration path of global-use ap
its organization:
| field | size | value |
|---|---|---|
| type | 1 byte | f1 (241) |
| length | 1 byte | n * 4 - n = number of wlcs (04 1 wlc, for 2 wlcs length become 08) |
| value | n * 4 bytes | wlc ipv4s, raw hex |
e.g. for the wlc 10.0.0.10
(10 -> 0a, 0 -> 00, 0 -> 00, 10 -> 0a)
it would be : f1 (option) + 04 (length) + 0a 00 00 0a (ip in hex) => f1 04 0a 00 00 0a
for 2 wlcs, relace the length by 08 && add the 2nd wlc ipv4 address in hex after the other one
the ap adds these ips to its capwap discovery list && unicasts discovery requests to them, starting w/ the 1st ip
f3 fast offline migration#
introduced w/ the cw917x guap firmware, same idea but extra byte
| field | size | value |
|---|---|---|
| type | 1 byte | f3 (243) |
| length | 1 byte | n * 4 + 1 (bc of the mode byte) |
| value | n * 4 bytes | wlc ipv4s |
| mode | 1 byte | 01 == meraki, 02 == catalyst |
for the same 10.0.0.10 wlc, it would become :
f3 (option) + 05 (length) + 0a 00 00 0a (ip in hex) + 02 (mode) => f3 05 0a 00 00 0a 02
the f3 is processed right at dhcp time in the day 0 state, it skips the 8 mn offline wait. the ap validates the wlc target by a ping (if the ap had a proper ip, gw even if same network, fw OK…), then flips persona && reboots on the mode (02 catalyst or 01 meraki)
if you do not use f3, it would try to reach 8.8.8.8 or 1.1.1.1 confirm the access to the meraki cloud later, all the time and spams it. so it would first join the meraki cloud if you have f1 option configured, even though you have configured the f1 option properly. whereas for catalyst wlc, you have to wait a full 8 mn, && if it fails, wait another 8 mn for it to retry
worth repeating : f3 only gets a chance if the state machine reaches it. no gateway in the scope (option 3) -> uplink validation fails (blink red twices) -> the ap loops forever without ever parsing f1 nor f3. ask me how i know (my take, bc it could never ever reach the meraki cloud, it refuses to read the f1/f3 either…)
(it stops completely if no gw, even if in the same network)
concatenation#
both tlvs can live happily inside the same option 43
for our f1 && f3 examples, we could type
f1 04 0a 00 00 0a f3 05 0a 00 00 0a 02&& both c91xx + cw917x aps could be on the same network
bc each ap would read itw own tlv && skips the unknown one, could also filter on the vendor class (option 60) in the dhcp discover, && the server would answer a different 43 option based on “Cisco Wireless AP” for cw917x or “Cisco AP” for the c91xx family. it is a cleaner solution, for a lab i choose the concatenation
feeding the win dhcp server#
doing it by powershell, bc the gui binary editor once shipped 5e 00 on the wire instead of my string, sry windows but wireshark don’t lie
so it would be :
Set-DhcpServerv4OptionValue -ScopeId 10.0.0.0 -OptionId 43 `
-Value 0xF1,0x04,0x0A,0x00,0x00,0x0A,0xF3,0x05,0x0A,0x00,0x00,0x0A,0x02on infoblox, two clean ways:
- raw
vendor-encapsulated-options (43), type string, colon-separated hex, no quotes — quotes = ascii = the same trap again :f1:04:0a:00:00:0a:f3:05:0a:00:00:0a:02 - typed a custom option space w/ sub-option
241as array of ip-address (nios computes type + length for you, multi-wlc friendly) && sub-option243as string w/ payload0a:00:00:0a:02only. the trailing mode byte breaks the ip-array typing, so f3 stays hex. never both raw && option-space on the same object : encapsulation conflict
my only trust source was wireshark, where you should see on the offer + the ack:
Option: (43) Vendor-Specific Information
Length: 13
Value: f1040a00000af3050a00000a02