Four VLANs, two wireless networks, one controller, and a config flag that reads as broken every time anyone looks at it. This is the networking layer under the rest of the homelab series. Most of it is unremarkable until you try to audit it, which is where the interesting part starts.
Legacy binding
- Flat boolean plus a numeric VLAN ID
- Current firmware never writes it
- Sits at its default, false
- Reads as broken every time
Current binding
- Points at a LAN network object
- The VLAN comes from that object
- This is what firmware actually writes
- Check this field, not the boolean
A false boolean here is the default, not a misconfiguration.
The layout
Four networks, split by how much each group of devices is trusted rather than by what the devices are:
- management, for the router, the switch, the access points and the controller
- trusted, for laptops and phones, the things that belong to people
- homelab, for the hypervisors and everything they run
- IoT, for everything that ships with a cloud account and no update policy
The hardware is a TP-Link ER7206 router, a PoE switch, two EAP670 access points, and an Omada controller running in a single container on a Raspberry Pi, which is deliberately not one of the cluster nodes.
Two wireless networks, not four. One lands on the trusted VLAN, one on the IoT VLAN. Fast roaming is on for the first and deliberately off for the second, because 802.11r is exactly the sort of optional extension a cheap smart plug claims to support right up until it associates. ๐
The flag that is not a bug
Read a wireless network’s config out of the controller and you find this:
{ "vlan_enable": false, "vlan_setting": { "lan_network_id": "6489..." } }vlan_enable: false on a network that is definitely tagging. Every instinct says something is misconfigured.
It is not. There are two generations of VLAN binding in this controller. The old one is a flat vlan_enable boolean plus a numeric VLAN ID. The new one binds to a LAN network object by ID, and the VLAN comes from that object. Modern firmware writes the second and leaves the first at its default, which is false.
So the check is vlan_setting.lan_network_id, and the legacy flag means nothing. It’s a feature not a bug. Worth writing down, because this is the kind of thing that gets “fixed” at midnight by someone who then spends an hour wondering why the network stopped ๐
Per session balancing
- Each new connection goes to one link
- Assigned by weight, then stays there
- Parallel downloads can exceed either link
- One download gets one link's speed
- Failover comes free
Real link aggregation
- Splits a single flow across members
- Both ends have to cooperate
- The ISP has to participate
- Two unrelated consumer ISPs will not
A second link is the wrong purchase if the reason is one big file.
Reading the config without the UI
Auditing a network through a web UI is fine for one setting and useless for forty. The controller stores everything in MongoDB, listening on loopback on the Pi. Convenient. Except the container ships mongod and no shell client, and installing one inside a vendor image is a fight you do not need.
The wire protocol is small enough to speak directly. Modern MongoDB uses OP_MSG, opcode 2013, and a query is a BSON document in a framed envelope. About sixty lines of Python with a hand rolled BSON encoder and decoder is enough to list databases, list collections and read documents. No installs, nothing written, read only by construction.
python3 mongoread.py dbs
python3 mongoread.py cols omada
python3 mongoread.py find omada lannetwork 20The collections worth knowing: lannetwork for VLANs and their DHCP settings, ssid, acl, dhcpreservation, firewallconfig, device, client. Reading them side by side takes a minute and answers questions the UI needs twenty clicks to almost answer.
The lesson generalises past this one product. When a device hides its config behind a UI, the config is still a file or a database underneath, and speaking to it directly is usually easier than it looks. 60 lines is less work than 40 clicks. ๐ฑ๏ธ
Dual WAN is not bonding
Two internet links terminate on the router, and the router load balances across them.
Load balancing here means per session. A new connection is assigned to one link or the other by weight and stays there. Ten downloads in parallel will use both links and can total more than either. One download uses one link and gets that link’s speed, full stop.
This gets called bonding constantly and it is not bonding. Real link aggregation splits a single flow across members, and it needs both ends to cooperate, which means your ISP has to be in on it. Two unrelated consumer ISPs are not going to cooperate on anything, least of all that.
What the second link is worth: failover, and headroom for concurrency. What it is not worth: a faster single download. If the reason for a second connection is one big file, it is the wrong purchase. If the reason is that a household of devices stops fighting each other, it is a good one, and the failover comes free. ๐
What is worth doing next
Two items sit on the list from the last audit. The controller runs on an SD card and writes about 5.7 GB a day, because a Java application backed by MongoDB is close to the worst possible workload for SD flash. Random writes, constantly, on the one medium that hates them. That wants an NVMe hat, and it wants one before the card decides the matter. ๐พ
The other is DHCP lease time, currently two hours everywhere. Short leases are for guest networks, where devices come and go. Infrastructure that has held the same address for a year does not need to renew forty times a week, and the extra churn is only useful if you enjoy reading DHCP logs.
Next: split horizon DNS, and the day my own domain broke a cron job.
