wiki-vault/Sources/Homelab/2026-05-08-homelab-vlan-renumber-switch-chip-to-bridge-alignment.md
Travis Herbranson 34a268d8dc migration: copy 62 notes from pbs-projects and homelab-projects
Two-layer structure: Sources (raw notes) + Wiki (compile output)
Four domains: Dev (40), Venture (3), Homelab (23), Reference (0)
Includes CLAUDE.md spec, index pages at all levels, compile log

Co-Authored-By: Lovebug <lovebug@herbylab.dev>
2026-05-08 17:21:13 -04:00

106 lines
5.8 KiB
Markdown

---
created: '2026-05-08'
path: Sources/Homelab
project: homelab-vlan-renumber
status: completed
tags:
- proxmox
- automation
type: session-notes
updated: '2026-05-08'
---
# Homelab VLAN Renumber — Switch Chip to Bridge Alignment
## Outcome
Renumbered OpenWrt's hardware switch chip VLANs to match the bridge VLAN filtering layer. Lab and private VLANs now use the same VID end-to-end (11 and 21) from the wire through the chip, sub-interface, and bridge. Unblocked Phase 1.3 — Knot LXC successfully reaches `10.0.11.1` over a tagged trunk from PVE.
## The Problem
OpenWrt was running two layers of VLAN configuration with different VID numbering:
- **Switch chip (swconfig):** VID 1, 2, 3 (lab), 4 (private)
- **Bridge VLAN filtering:** VID 11, 21, 31, 61, 71
Bridge VLAN filtering rules quietly translated between them via `:u*` (untagged + PVID) on the sub-interface ports. NAS frames came up tagged VID 3 from the chip, hit `eth1.3` configured as `:u*` on bridge VID 11 — bridge applied PVID 11 to ingressing untagged frames. PVE same pattern: VID 4 → `eth1.4:u*` → bridge PVID 21.
The translation worked, but it meant configuring PVE to send `tag=11` for "VLAN 11 lab" actually required sending `tag=3` (the chip's wire VID), which is the kind of hidden mismatch that loses an hour of debugging six months later.
## The Architecture Change
**Switch chip — added entries with bridge-matching VIDs:**
```
VLAN 11: ports='3 4t 6t' (NAS untagged on 3, PVE tagged on 4, CPU tagged on 6)
VLAN 21: ports='4 6t' (PVE untagged on 4, CPU tagged on 6)
```
**Linux — created explicit 8021q sub-interfaces:**
```
config device
option type '8021q'
option ifname 'eth1'
option vid '11'
option name 'eth1.11'
config device
option type '8021q'
option ifname 'eth1'
option vid '21'
option name 'eth1.21'
```
These were **created explicitly** rather than relying on the legacy implicit `eth1.<N>` auto-creation. The previous `eth1.3` and `eth1.4` ports never had explicit device entries — Linux auto-spawned them when referenced in the bridge ports list. The explicit form is the modern OpenWrt-recommended pattern and makes the config self-documenting.
**Bridge — `homebridge` ports updated:**
```
ports='eth1.3' → ports='eth1.21' 'eth1.11'
```
(`eth1.3` retained for ports 1/2 still on legacy VID 3 — see Open Items.)
**Bridge VLAN filtering — sub-interfaces wired to matching VIDs:**
```
VID 11: 'eth1.11' tagged member, 'eth1.3:u*' (NAS legacy translation, retained)
VID 21: 'eth1.21:u*' (PVE untagged + PVID)
```
## How the Migration Stayed Live
Parallel-build pattern. Built the new VID 21 plumbing alongside the existing VID 4 path before tearing anything down. PVE management kept flowing on the legacy path until the new path was verified, then `eth1.4` was removed in one cutover. Same approach for VID 11. PVE never dropped, NAS never dropped.
## Hybrid Trunk on PVE Port
Port 4 ended as a hybrid trunk: VID 21 untagged (PVE management default) + VID 11 tagged (LXC traffic). This required the LXC's `tag=11` parameter to match the wire VID for the first time — previously this would have needed `tag=3` due to the translation layer.
## Key Learnings
- **swconfig VIDs are real wire VIDs.** Earlier in the session I (Claude) misframed them as "internal slots" — they're actual 802.1Q tags on frames between the switch silicon and the CPU. The number you see in `swconfig` is the number on the wire.
- **Tagged port ≠ port adds tag.** Tagged means "expect frames to already carry an 802.1Q tag and preserve it." The sending device adds the tag. PVID on an untagged port is what tags ingressing untagged frames.
- **VLANs only do anything when something downstream enforces them.** A VID on a frame is just a number unless a switch or bridge filters on it. The original setup "worked" because the bridge wasn't VLAN-aware yet — turning on `bridge-vlan-aware yes` is what surfaced the latent chip/bridge mismatch.
- **Two VID namespaces stacked on top of each other is a footgun.** The translation pattern works but creates cognitive overhead every time you debug. Aligning the layers eliminates a class of "which number do I use here?" questions forever.
- **Explicit `8021q` device entries beat implicit sub-interface auto-spawn.** Self-documenting, idiomatic for current OpenWrt, easier to grep for in the config. The implicit form is legacy and hides the dependency on bridge `ports` list references.
- **`uci add_list` is non-destructive on bridge ports.** Adding a sub-interface to a bridge port list during a parallel build doesn't disrupt existing members. Both paths run side-by-side until the old one is removed.
- **Parallel-build > rename for live infrastructure.** Renaming `eth1.4``eth1.21` in place would have required atomically updating the device, bridge, and switch_vlan in one reload. Building `eth1.21` alongside, verifying, then removing `eth1.4` is recoverable at every step.
## Open Items
- [ ] Switch chip still has VID 3 with ports 1 and 2 as members (link down on 1, link up on 2). Need to identify what's on port 2 before retiring VID 3 entirely.
- [ ] Cosmetic: `eth1.21` listed as redundant tagged member on bridge VID 11 (harmless, future cleanup).
- [ ] Cosmetic: switch_vlan descriptions still say `lab-new` / `private-new`. Should rename to `lab` / `private` once VID 3 is fully retired.
- [ ] Document new wiring on Knot LXC — Phase 1.3 resume notes can now move from "blocked" to "infrastructure ready."
## Final State
| Layer | VID 11 (lab) | VID 21 (private) |
|---|---|---|
| Switch chip | `3 4t 6t` | `4 6t` |
| Sub-interface | `eth1.11` (explicit 8021q) | `eth1.21` (explicit 8021q) |
| Bridge port | `eth1.11` tagged, `eth1.3:u*` legacy | `eth1.21:u*` |
| End-to-end VID | 11 (NAS untagged on chip, all else 11) | 21 (PVE untagged on chip, all else 21) |
NAS reachable, PVE management reachable, Knot LXC pinging gateway. Phase 1.3 unblocked.