Wi-Fi Without VLANs

Spine & Leaf Wi-Fi

Layer-3 Wi-Fi Fabric

Wi-Fi is like a layer-2 Oscar-Mayer hotdog. It looks better than it tastes. Every access point belongs to the same foot-long fabric. Does wireless mobility truly require that digital heartburn? Or is there a better choice on the menu?

The Low-Down

Most Wi-Fi designs extend Layer 2 so a client can keep the same IP while it roams. That gives you one big broadcast domain, tunnels, controllers in the data path, and extra glue when you leave the building. VLANs show up everywhere because the fabric itself never really left Layer 2.

I wanted something simpler. Treat the client’s IP as a stable identity. Announce it as a /32 host route in BGP from whichever access point the client is currently on. When the client moves, the route moves with it. No bridges, no GRE-style tunnels, no shared broadcast domain, and no VLANs.

The controller never sees client traffic. It only watches associations and pushes or withdraws the /32 on the APs. BGP does the rest.

Why bother?

A flat Layer 2 network (or a tunneled one) gives you a few familiar problems:

  • ARP and mDNS floods hit every radio at the lowest management rate.
  • Controllers become single points of failure and performance bottlenecks.
  • Trunks, VLANs, and spanning tree touch every AP.
  • Client isolation is usually a CPU-heavy bridge filter that breaks other things.
  • Going between sites means yet another overlay.

A purely routed fabric sidesteps most of that. Each client lives on its own isolated segment. The IP follows the device. Policy can ride on BGP communities. Distance becomes just another BGP peer. And there are no VLANs to provision or trunk.

Layer-3 Roaming

Client associates to a new AP. Syslog fires. Controller sees the event, withdraws the /32 from the old AP, and announces it on the new one. BGP converges. Traffic follows.

With BFD and tight timers the whole thing can stay under a second on a small fabric. Existing TCP sessions survive. The client does not need to know anything changed.

Four pieces

1. Access points

MikroTik APs acting as leaves. Each runs BGP to the core. Base config is almost identical. The controller adds and removes network statements over the API. If the controller dies, existing routes stay up and traffic keeps flowing.

2. BGP core

Route reflector (CHR or physical). It accepts the client /32s tagged with the right communities and rejects everything else. Policy lives here. One connection-tracking rule can cover the whole client pool.

3. Controller

Lightweight Python container. Listens to CAPsMAN syslog, keeps a MAC→IP→AP table, and drives the RouterOS API. Never in the data path. Failure only stops new associations.

4. Dashboard

Shows who is where, roam history, BGP session health, and a simple isolation toggle. The BGP table itself is the second source of truth.

Communities for policy

Every /32 carries a community that says what the device is allowed to do:

65000:100  Corporate
65000:200  Guest
65000:300  IoT
65000:900  Quarantine

The controller maps authentication context to the community at association time. The spine enforces it. No per-client firewall rules, no VLAN sprawl. Change the community and the policy moves with the route.

What this buys you

  • Roaming works from anywhere.  The controller can be down the hall, on another floor, across the campus, or on the other side of the state. 
  • The IP belongs to the device. Firewall rules and logs follow the client, not the AP or the subnet.
  • No vendor licensing.  It's simply aligning dynamic routing with clients.
  • HA is simple.  Just add another controller and use local preference.  Existing sessions survive controller failure.
  • Client isolation is topological instead of filter-based.
  • No VLANs anywhere in the client path.

Requirements for a small POC

  • Two MikroTik APs that support containers (RB5009, L009, hAP ax³). The older hAP ac² does not.
  • One BGP route reflector (CHR or physical).
  • A couple of client devices, including at least one Android.
  • RouterOS 7.x on the controller host.
  • Python container with a syslog listener, librouteros, and SQLite. Target is under 500 lines.

Optional later: 802.1X so communities come from RADIUS attributes, RSSI polling for flap prevention, mDNS reflection if you need discovery across the isolated segments.

Compared with the usual options

TCP session on roam: L2 bridge drops, L2 tunnel survives only while the controller is up, L3 per-AP subnet drops, this design survives.

Controller in data path: L2 bridge no, L2 tunnel yes, L3 per-AP no, this design no.

Broadcast scope: L2 bridge fabric-wide, L2 tunnel fabric-wide, L3 per-AP per AP, this design per client.

Geographic scale: L2 bridge poor, L2 tunnel moderate, L3 per-AP good, this design excellent.

Role-based policy: L2 bridge none, L2 tunnel proprietary, L3 per-AP static, this design BGP communities.

Licensing: L2 bridge low, L2 tunnel high, L3 per-AP low, this design zero.

Caveats

Convergence time.  Aggressive BGP and BFD timers will help.  Roam events, reassociation timing, and queueing must be measured.  The first working version is MikroTik-specific even if the idea is not.

None of that makes the approach nonsense. It's simply applying something BGP already does well to the access layer. The concept should be welcome for anyone who has fought with L2 roaming, GRE tunnels, or inter-controller mobility.  

The controller and dashboard are still in POC shape. The fabric itself is mostly ordinary BGP and RouterOS. That is the point.

0 Comments:

Post a Comment

My Instagram