Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e47e30df19 | |||
| e077c6e30c | |||
| 6f8d499ec2 | |||
| d0859d4c5c |
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(nix eval:*)",
|
||||
"Bash(nix flake check:*)",
|
||||
"Bash(nix build:*)",
|
||||
"Bash(check-system:*)",
|
||||
"Bash(build-system:*)",
|
||||
"Bash(build-home:*)",
|
||||
"Bash(git status:*)",
|
||||
"Bash(git diff:*)",
|
||||
"Bash(git log:*)",
|
||||
"Bash(git show:*)",
|
||||
"Bash(drill:*)",
|
||||
"Bash(ping:*)"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -4,3 +4,4 @@ result*
|
||||
!/.vms/.gitkeep
|
||||
/.keys/*.key
|
||||
*.swp
|
||||
/.claude/settings.local.json
|
||||
|
||||
@@ -9,6 +9,11 @@ tools refuse to write through a symlink and will error on `CLAUDE.md`).
|
||||
convention, a workflow gotcha, a design rationale — record it here (or in a repo doc this file points
|
||||
to, e.g. `home-switches.md`), not in agent memory. AGENTS.md is versioned and shared; memory is not.
|
||||
|
||||
Claude Code permissions live in two files: `.claude/settings.json` (versioned, shared — the
|
||||
committed allow list of safe-to-auto-approve commands) and `.claude/settings.local.json` (personal,
|
||||
gitignored — where interactive "always allow" grants accumulate). Put durable, generally-safe
|
||||
commands in the shared file; leave one-off or machine-specific grants in the local one.
|
||||
|
||||
## Overview
|
||||
|
||||
Personal Nix flake managing NixOS systems and home-manager configurations for a set of
|
||||
@@ -53,6 +58,12 @@ Check everything (what CI runs): `nix flake check --no-build`.
|
||||
CI builds each attr of `.#ci.x86_64-linux` (systems, homes, packages, shell) and pushes to the
|
||||
Harmonia binary cache; see `.gitea/workflows/ci.yaml` and `ci/push-to-cache.sh`.
|
||||
|
||||
For DNS lookups use **`drill`** (ldns) — `dig` isn't installed in this environment (it fails with
|
||||
exit 127, which is easy to miss if stderr is redirected). E.g. `drill -Q @<resolver> <name> A`.
|
||||
|
||||
For privilege escalation use **`doas`**, not `sudo` — the boxes don't install `sudo` (it fails with
|
||||
`command not found`). E.g. `doas ip link set <if> up`.
|
||||
|
||||
## Architecture
|
||||
|
||||
### The custom module system
|
||||
@@ -141,6 +152,14 @@ touching anything WAN/VLAN-related, and update it when the switch layout changes
|
||||
agree on VLAN numbering (e.g. `lib.my.c.home.vlans`), so a switch-side change usually pairs with a
|
||||
box change; `home-switches.md` documents the switch layout and per-switch config for the WAN design.
|
||||
|
||||
### Home wireless APs (`vibe` / `wave`)
|
||||
The home Wi-Fi APs are also **not** managed by this flake: `vibe` (MikroTik cAP ax, RouterOS) and
|
||||
`wave` (Cudy AX3000 running OpenWrt/UCI). They are dumb APs — bridge clients onto the right VLAN,
|
||||
routers do DHCP/RA/firewall. The trunk/VLAN design, the OpenWrt flash + config for `wave`, and the
|
||||
per-AP management addressing live in **`home-aps.md`** at the repo root — read it before touching AP
|
||||
config, and update it when an AP changes. Only the DNS records live in the flake
|
||||
(`routing-common/dns.nix`).
|
||||
|
||||
## Secrets
|
||||
|
||||
age-encrypted secrets in `secrets/`, managed with **ragenix**. Each module declares
|
||||
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
# Home wireless APs
|
||||
|
||||
Reference for the home Wi-Fi access points. Like the switches (`home-switches.md`), these are **not**
|
||||
managed by this flake — they are configured on-device (RouterOS on the MikroTik, OpenWrt/UCI on the
|
||||
Cudy). This file documents the shared VLAN/trunk design and each AP.
|
||||
|
||||
Only the DNS records live in the flake (`nixos/boxes/home/routing-common/dns.nix`, `h.nul.ie` zone).
|
||||
Everything else here is applied by hand on the device.
|
||||
|
||||
## The APs
|
||||
|
||||
| | vibe | wave |
|
||||
|---|---|---|
|
||||
| Model | MikroTik **cAP ax** (`cAPGi-5HaxD2HaxD`) | Cudy **AX3000** (OpenWrt id `cudy,ap3000-v1`) |
|
||||
| OS | RouterOS 7.x | OpenWrt 25.12.x (MT7981B / Filogic 820) |
|
||||
| Radio | 2×2 both bands | 2×2 both bands (2 spatial streams) |
|
||||
| Uplink | trunk (multi-port; `ether2` is a wired LAN port) | single **2.5 GbE** trunk |
|
||||
| Management | core/hi/lo `.15` | core/lo `.14` (**not** `hi`; see below) |
|
||||
|
||||
Both mirror the same two SSIDs. `wave` replaced an older AP of the same name; the new one is the
|
||||
Cudy running OpenWrt.
|
||||
|
||||
> **Note on "AX3000":** MT7981B is 2×2 (2 spatial streams). The "3000" is *aggregate* Mbps —
|
||||
> 574 (2.4 GHz 2ss) + 2402 (5 GHz 2ss @ **160 MHz**) — not three streams. `iw` confirms 2×2
|
||||
> (`Available Antennas TX/RX 0x3`; "3 streams: not supported"). 160 MHz is what earns the "3000".
|
||||
|
||||
## Shared design (dumb AP)
|
||||
|
||||
Every AP is a **dumb AP**: it bridges wireless clients onto the right VLAN and does **no** routing,
|
||||
DHCP, RA or firewalling. The home routers (`river`/`stream`) own DHCP/RA/gateway (per-VLAN VRRP VIPs)
|
||||
and firewalling. The uplink is a **tagged trunk**:
|
||||
|
||||
| VLAN | `lib.my.c.home.vlans` | Role | AP use |
|
||||
|---|---|---|---|
|
||||
| — (native) | `core` | switch/fabric management (1500) | backup management (untagged) |
|
||||
| 100 | `hi` | trusted LAN, **high-MTU** (jumbo 9000) | `vibe` management (it's jumbo-capable) |
|
||||
| 110 | `lo` | trusted LAN (1500) | main SSID `wlan0`; `wave` management |
|
||||
| 120 | `untrusted` | guest network | guest SSID `wlan1` |
|
||||
|
||||
`hi` and `lo` are both **trusted** client VLANs — the only difference is MTU (`hi` carries jumbo
|
||||
9000, `lo` is standard 1500). An AP puts its own management on whichever it can do: `vibe` (jumbo)
|
||||
sits on `hi`, `wave` (eth0 capped at 2026) sits on `lo`. The main SSID lands on `lo` because Wi-Fi
|
||||
clients are 1500 regardless.
|
||||
|
||||
### SSIDs
|
||||
|
||||
| SSID | Bands | Security | VLAN |
|
||||
|---|---|---|---|
|
||||
| `wlan0` (main) | 5 GHz + 2.4 GHz | WPA2/WPA3-PSK (`sae-mixed`) | 110 (`lo`) |
|
||||
| `wlan1` (guest) | 2.4 GHz | WPA2-PSK (`psk2`) | 120 (`untrusted`) |
|
||||
|
||||
**Passphrases are never stored in this repo.** `vibe` is the source of truth; read them out-of-band
|
||||
with `ssh admin@vibe '/interface wifi export show-sensitive'` (`.passphrase=` prints **unquoted**).
|
||||
|
||||
## vibe (MikroTik cAP ax)
|
||||
|
||||
RouterOS, one hardware-offloaded bridge `main` with `vlan-filtering=yes`. Access: `ssh admin@vibe`
|
||||
— key auth for `admin` is installed (`~/.ssh/id_rsa`), with `admin`/`admin` as a fallback.
|
||||
|
||||
- **Uplink `ether1`** — trunk, tagged VLANs 100/110/120; native/untagged is the default VLAN 1
|
||||
(PVID, no IP). `ether2` is a wired **access port** on VLAN 110 (`lo`). `l2mtu 9214`.
|
||||
- **Radios** — `wifi1` (5 GHz, 20/40/80) + `wifi2` (2.4 GHz, 20/40) both broadcast `wlan0`
|
||||
(WPA2/WPA3-PSK), untagged onto VLAN 110. `wifi3` is a virtual AP on `wifi2` broadcasting `wlan1`
|
||||
(WPA2-PSK), untagged onto VLAN 120. `country=Ireland`.
|
||||
- **Bridge VLANs** — 100 tagged `main,ether1`; 110 tagged `main,ether1` + untagged
|
||||
`ether2,wifi1,wifi2`; 120 tagged `main,ether1` + untagged `wifi3`.
|
||||
- **Management** — `jim`/`dave`-style (core/hi/lo), on host `.15`: `192.168.64.15` on core
|
||||
(native/untagged, backup), `192.168.68.15/22` + `2a0e:97c0:4d0:1::1:6` on the `hi` VLAN-100
|
||||
interface (holds the default route, via the hi VIP `192.168.71.254`), and
|
||||
`192.168.72.15/21` + `2a0e:97c0:4d0:2::1:6` on `lo` VLAN 110. No IP on `untrusted`.
|
||||
`l2mtu 9214`, so `hi` carries jumbo (9000) here — `vibe` sits on `hi` because it *can* jumbo,
|
||||
unlike `wave` (see its MTU note).
|
||||
- **Roaming** — 802.11k/v via a `/interface wifi steering` profile (`rrm=yes wnm=yes`,
|
||||
`neighbor-group=home-aps`) assigned to `wifi1`/`wifi2`/`wifi3`.
|
||||
- **Resolver** — the hi VIP `192.168.71.254` / `2a0e:97c0:4d0:1::ffff`.
|
||||
|
||||
## wave (Cudy AX3000, OpenWrt)
|
||||
|
||||
Single-port AP, so the port is a VLAN **trunk** carrying management + both SSIDs.
|
||||
|
||||
### Management addressing
|
||||
|
||||
`wave` takes host `.14`, on **`lo` (primary) and `core` (backup)** — deliberately **not** `hi`,
|
||||
unlike the switches and `vibe`. `hi` is the jumbo (9000) VLAN, but `wave`'s eth0 caps at 2026 (see
|
||||
MTU note), so there's no reason to put it there; `lo` is 1500 with a proper VRRP VIP for the default
|
||||
route + resolver, and `core` has no VIP/v6 so it can only be a backup. No IP on `untrusted`. Records
|
||||
in `dns.nix`:
|
||||
|
||||
| Name | VLAN | Address |
|
||||
|---|---|---|
|
||||
| `wave-core` | core (native/untagged) | `192.168.64.14/24` — backup, like the switches (no VIP → backup only) |
|
||||
| `wave` | lo 110 | `192.168.72.14/21`, `2a0e:97c0:4d0:2::1:5` — primary; holds the default route + resolver (lo VIP `192.168.79.254` / `2a0e:97c0:4d0:2::ffff`) |
|
||||
|
||||
**Firewall:** management (SSH/LuCI) reachable from `core`/`lo` only; `untrusted` is a separate
|
||||
zone with `input REJECT` (and `wave` has no IP there) — **no management via the guest VLAN**.
|
||||
|
||||
### brian switch port
|
||||
|
||||
`wave` hangs off **brian** (UniFi). Its port is a **trunk**: tagged VLAN **110/120** (`lo` + guest),
|
||||
and **native/untagged = core** (the fabric's management VLAN, carrying `wave-core`). VLAN 100 (`hi`)
|
||||
is **not** needed here — `wave` isn't on `hi` (see Management addressing). Configure via the UniFi
|
||||
controller (brian has no CLI); see `home-switches.md`.
|
||||
|
||||
### Flashing OpenWrt (Cudy AX3000 / `cudy_ap3000-v1`)
|
||||
|
||||
Hardware: MT7981B, 512 MB RAM, 256 MB SPI-NAND, 1× 2.5 GbE (RTL8221B), 2×2 WiFi 6.
|
||||
|
||||
> ⚠️ **Serial caveat:** units with a serial starting `2543…` (post ~Nov 2025) use a different flash
|
||||
> chip and can brick with older firmware. Match firmware to the unit.
|
||||
|
||||
OpenWrt can't be flashed directly over stock. Two-stage, via a Cudy **transition** firmware (Cudy
|
||||
OpenWrt download page / `support@cudy.com`; `warnning.txt` in that bundle has the steps):
|
||||
1. Stock Cudy UI: update to **≥ 2.4.7** (adds TFTP `recovery.bin` recovery), then flash the Cudy
|
||||
**intermediate** firmware (`cudy_ap3000-v1-sysupgrade_*.bin`), "keep settings" **unchecked**.
|
||||
It reboots into an OpenWrt-based build at `192.168.1.1` (SSH `root`, empty password).
|
||||
2. From there, `sysupgrade -n` to vanilla OpenWrt (`…-cudy_ap3000-v1-squashfs-sysupgrade.bin` from
|
||||
`downloads.openwrt.org`; this release ships **no** factory image — sysupgrade only).
|
||||
|
||||
Stock default (out of box) is a DHCP client falling back to **`192.168.10.254`**; the stock UI is a
|
||||
customised LuCI (only 80/443, no SSH) with a first-boot "create admin password" wizard — so the
|
||||
stock-side flashing is done from a browser, not headless.
|
||||
|
||||
### On-device config notes
|
||||
|
||||
- Package manager is **`apk`** (not `opkg`). WiFi runs **`wpad-mbedtls`** (full — swapped from the
|
||||
default `wpad-basic-mbedtls`, which lacks 802.11v). **802.11k + 802.11v** (`ieee80211k` +
|
||||
`bss_transition`) are enabled on all SSIDs. ⚠️ Swapping wpad **live** leaves the mac80211 vifs
|
||||
stuck in a start→teardown loop (`nl80211 ... No such device`); a `wifi reload`/`network restart`
|
||||
won't recover it — **reboot** after `apk add wpad-mbedtls`.
|
||||
- Radios: `radio0` = 2.4 GHz, `radio1` = 5 GHz (keyed by `band`, don't assume). 5 GHz is pinned to
|
||||
**channel 36 / HE160** (any 160 MHz block in IE is DFS; ch36 has the shortest ~60 s CAC).
|
||||
- Bridge: `br-lan` with `vlan_filtering`, single port `eth0` — tagged `110/120`, untagged/PVID
|
||||
VLAN 1 (= native/core). SSIDs attach via `network` = `lo`/`untrusted` (= `br-lan.110`/`.120`).
|
||||
- Dumb-AP: no DHCP pools, `odhcpd.maindhcp=0`, `delegate=0` on the L3 interfaces.
|
||||
- **MTU:** all interfaces are **1500**. The `mtk_eth_soc` 2.5 GbE (`eth0`) caps at **2026 bytes**
|
||||
(`ip link set eth0 mtu 9000` → `SIOCSIFMTU: Invalid argument`), so `wave` can't join `hi`'s jumbo
|
||||
(9000) fabric like `vibe` does — which is precisely **why `wave` is managed on `lo`, not `hi`**
|
||||
(see Management addressing). Nothing on `wave` needs > 1500.
|
||||
- **LuCI:** enabled, login `root` / `admin`. **SSH:** key-only (`PasswordAuth`/`RootPasswordAuth off`).
|
||||
- `iperf3` installed for throughput testing.
|
||||
|
||||
### Access
|
||||
|
||||
- SSH: `ssh root@wave` (key-only; `wave`/`wave-core` resolve once `dns.nix` is deployed).
|
||||
- LuCI: `http://192.168.72.14/` (or `http://wave/`), `root` / `admin`.
|
||||
@@ -216,6 +216,7 @@ in
|
||||
jq
|
||||
yq-go
|
||||
nix-tree
|
||||
treemd
|
||||
];
|
||||
|
||||
sessionVariables = {
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
# Home switches
|
||||
|
||||
Reference for the two MikroTik switches on the home network — **jim** and **dave** — plus the
|
||||
Ubiquiti switch **brian**, and how the home boxes and the Digiweb WAN hang off them. These switches
|
||||
are **not** managed by this flake; they are configured by hand (RouterOS on jim/dave, UniFi on
|
||||
brian). It covers the physical topology, the VLAN map, and how the Digiweb WAN reaches river.
|
||||
|
||||
In short: the Digiweb ISP VLAN (10) is trunked straight through to river (which runs PPPoE on it),
|
||||
and the ONT's untagged management is PVID'd onto VLAN 140 at brian, its edge switch. VLAN 10 is
|
||||
carried untranslated because a single ONT makes it unique on the fabric — see
|
||||
[the WAN path](#the-digiweb-wan-path-trunked-vlan-10--pvid-140) and
|
||||
[why not translation](#why-not-translation-for-one-ont).
|
||||
|
||||
## The switches
|
||||
|
||||
| | jim | dave | brian |
|
||||
|---|---|---|---|
|
||||
| Identity | `jim-sw` | `dave-sw` | (UniFi) |
|
||||
| Model | CRS326-24G-2S+ | CRS504-4XQ | Ubiquiti Switch Pro XG 8 PoE |
|
||||
| Switch chip | Marvell 98DX3236 | Marvell 98DX4310 (+ Atheros 8227 for the 1G mgmt port) | — |
|
||||
| OS | RouterOS 7.18 | RouterOS 7.18 | UniFi |
|
||||
| Ports | 24×1G + 2×SFP+ | 4×QSFP28 (100G, breakout-capable) + 1G mgmt | 8×10GBASE-T PoE + 2×25G SFP28 |
|
||||
| Bridge | `main`, `vlan-filtering=yes` | `main`, `vlan-filtering=yes` | UniFi VLAN profiles |
|
||||
|
||||
jim and dave run a single hardware-offloaded bridge (`main`) with VLAN filtering. Access to the
|
||||
MikroTiks is SSH as `admin` / `admin` by short hostname (see [Accessing the switches](#accessing-the-switches)).
|
||||
Only jim and dave can do hardware VLAN translation (`/interface ethernet switch rule` on the Marvell
|
||||
chips); brian cannot rewrite tags, only trunk/PVID them.
|
||||
|
||||
## Physical topology
|
||||
|
||||
The ONT terminates on brian; jim's `wan-pon-in` (`sfp-sfpplus2`) is a spare SFP+ port.
|
||||
|
||||
```
|
||||
Virgin Media cable modem
|
||||
│ VLAN 130 (wan1 / wan2 / wan-in)
|
||||
┌───────────────┴──────────────────────────────────────┐
|
||||
│ jim CRS326-24G-2S+ (Marvell 98DX3236) │
|
||||
│ 1G edge: fort, pronter, laptop-dock, palace-kvm, │
|
||||
│ ups, ether15-20, wan1/wan2/wan-in │
|
||||
│ wan-pon-in = sfp-sfpplus2 (spare SFP+) │
|
||||
└───────┬──────────────────────────────────────────────┘
|
||||
│ dave-uplink = sfp-sfpplus1 (10G trunk)
|
||||
│ also: palace, stream (1G secondaries),
|
||||
│ castle (2.5G, normally down)
|
||||
┌───────┴──────────────────────────────────────────────┐
|
||||
│ dave CRS504-4XQ (Marvell 98DX4310) │
|
||||
│ jim-downlink = qsfp28-3-1 │
|
||||
└──┬──────────────────┬───────────────────┬────────────┘
|
||||
│ palace │ castle │ brian-downlink
|
||||
│ = qsfp28-1-1 │ (100G) │ 802.3ad LAG
|
||||
│ (100G) │ │ (brian1 + brian2)
|
||||
│ … │
|
||||
┌───────┴────────┐ ┌───────┴─────────────┐
|
||||
│ palace host │ │ brian │
|
||||
│ └ river (VM) │ │ Switch Pro XG 8 PoE │
|
||||
└────────────────┘ └───────┬─────────────┘
|
||||
river WAN + LAN ride the 100G link │ hosts the ONT
|
||||
│
|
||||
┌───────┴─────────────┐
|
||||
│ ONT (Digiweb) │ untagged mgmt
|
||||
│ PPPoE via ONT │ 192.168.100.1
|
||||
└─────────────────────┘ + VLAN 10; PVID 140
|
||||
```
|
||||
|
||||
Notes:
|
||||
- **river** runs as a VM on the **palace** host; its uplink is dave's 100G `palace` port. jim also
|
||||
has 1G `palace`/`stream` ports, but those are secondary links and do **not** carry the WAN.
|
||||
- **stream** (the second router box) is dual-homed to both jim and dave (STP picks the active path).
|
||||
- **castle** is dual-homed but **not** via STP: its primary uplink is dave's **100G** `castle` port
|
||||
(`et100g`, active), and it has a secondary **2.5G** link to jim's `castle` edge port (`et2.5g`,
|
||||
**normally down** — no live failover). ⚠️ **castle's root disk is NVMe-oF over the fabric** (via
|
||||
`et100g`→dave), so rebooting **dave** — or downing castle's `et100g` — freezes castle mid-I/O.
|
||||
Do dave maintenance (upgrades/reboots) from a host that doesn't depend on dave for storage or
|
||||
network, or with castle cleanly powered off; don't drive it from castle.
|
||||
- **brian** is a Ubiquiti **Switch Pro XG 8 PoE** (8×10GBASE-T), downlinked from dave over an
|
||||
**802.3ad LAG** (`brian-downlink` = `brian1` + `brian2`, layer-2 hash). It hosts the ONT.
|
||||
|
||||
## VLANs
|
||||
|
||||
| VLAN | Name | Purpose |
|
||||
|---|---|---|
|
||||
| — (native) | core | Switch management, `192.168.64.0/24` (jim `.10`, dave `.11`, brian `.13`) |
|
||||
| 100 | hi | High-performance / jumbo network (MTU 9000) |
|
||||
| 110 | lo | Standard LAN |
|
||||
| 120 | untrusted | Guest / untrusted network |
|
||||
| 130 | wan | **stream's WAN** — Virgin Media cable modem (untagged on jim's `wan1`/`wan2`/`wan-in`) |
|
||||
| 140 | wan-pon-ont | ONT management, `192.168.100.0/24` (PVID'd at the ONT edge) |
|
||||
| 10 | pon-isp | Digiweb ISP transport — **trunked straight through** to river, PPPoE runs on it |
|
||||
| 141 | wan-pon-isp | **Reserved** — the translated ISP VLAN for the future multi-ONT design |
|
||||
|
||||
Switch L3 presence (`/interface vlan` on `main`) exists **only** for VLANs the switch is managed
|
||||
from — `hi` (100) and `lo` (110), plus native core. WAN and guest VLANs deliberately have no switch
|
||||
L3 interface.
|
||||
|
||||
## The Digiweb WAN path (trunked VLAN 10 + PVID 140)
|
||||
|
||||
The ONT presents two things on one wire:
|
||||
- **untagged** management traffic (`192.168.100.x`), and
|
||||
- **tagged VLAN 10** carrying the Digiweb ISP session (the BRAS requires VLAN 10).
|
||||
|
||||
With a **single ONT** there's no reason to translate anything — VLAN 10 is unique on the fabric, so
|
||||
we just carry it end to end and let river run PPPoE directly on it:
|
||||
|
||||
1. **Untagged mgmt → VLAN 140, at the ONT's edge switch (brian).** brian sets the ONT port's PVID to
|
||||
140 so the untagged management traffic becomes VLAN 140, and allows tagged VLAN 10 through the
|
||||
same port. river takes `192.168.100.100/24` on VLAN 140 (matching stream's modem-mgmt `.100`) to
|
||||
reach the ONT web UI at `192.168.100.1`. Doing the PVID at the ONT-facing edge keeps it clean —
|
||||
the untagged frames never share a domain with anything else.
|
||||
|
||||
2. **VLAN 10 (ISP) trunked straight through, untranslated.** brian → dave → palace carry tagged
|
||||
VLAN 10 by ordinary bridge-VLAN membership. No `/interface ethernet switch rule`, no pinning, no
|
||||
asymmetric-learning issues — it's just a normal tagged VLAN. river attaches PPPoE to VLAN 10
|
||||
directly (`wan-pon-isp` netdev = VLAN `pon-isp` = 10; baby-jumbo MTU 1508 so PPP nets a clean
|
||||
1500).
|
||||
|
||||
Net result: **river runs PPPoE single-tagged on VLAN 10 and holds a VLAN 140 address to reach the
|
||||
ONT.** See `nixos/boxes/home/palace/vms/river.nix` for the river side.
|
||||
|
||||
```
|
||||
ONT ──(untagged + VLAN10)── brian ──(VLAN140 + VLAN10)── dave ──(VLAN140 + VLAN10)── river
|
||||
ONT port │ PVID140 + tagged 10 │ plain bridging
|
||||
└─ brian-downlink LAG ── dave ┘
|
||||
```
|
||||
|
||||
### Why not translation (for one ONT)?
|
||||
|
||||
Translation would swap VLAN 10 → 141 with two pinned hardware ACL rules to keep VLAN 10 off the rest
|
||||
of the fabric. That buys nothing with a single ONT — VLAN 10 is already unique, so trunking it is
|
||||
simpler and rule-free. Translation only earns its keep when **two** ONTs both deliver VLAN 10 and
|
||||
would collide (below).
|
||||
|
||||
## Switch configuration
|
||||
|
||||
How each switch is set up for the Digiweb WAN path. **Confirm any change on the box before applying**
|
||||
(see [Accessing the switches](#accessing-the-switches)).
|
||||
|
||||
**brian (UniFi)** — hosts the ONT:
|
||||
- The ONT port has **native/untagged network = VLAN 140** (PVID) and is a **tagged member of VLAN 10**,
|
||||
so the ONT's untagged management lands on 140 and its tagged ISP frames pass through.
|
||||
- The `brian-downlink` LAG up to dave trunks **tagged 140 + tagged 10** (alongside the LAN VLANs).
|
||||
|
||||
**dave (RouterOS)** — trunks both WAN-pon VLANs to `brian-downlink` and `palace`. The ISP VLAN 10 row:
|
||||
```
|
||||
/interface bridge vlan add bridge=main vlan-ids=10 tagged=brian-downlink,palace
|
||||
```
|
||||
VLAN 140 also spans `brian-downlink,palace` (it carries a few other members too). No switch rules —
|
||||
this is plain tagged bridging.
|
||||
|
||||
**jim (RouterOS)** — carries **none** of the Digiweb WAN path: no translation rules, and no VLAN
|
||||
10/140/141 rows. `wan-pon-in` (`sfp-sfpplus2`) sits at `pvid=1` as a spare port. jim only handles
|
||||
stream's VLAN-130 WAN and the LAN VLANs.
|
||||
|
||||
## Future: multiple ONTs (per-port VLAN translation)
|
||||
|
||||
If a second ONT arrives (e.g. a Digiweb line for stream, or a second river), trunking breaks: both
|
||||
ONTs deliver **tagged VLAN 10**, and plain bridge-VLAN filtering can't tell them apart. That's when
|
||||
translation earns its place — a switch rule matches on the **ingress port**, so each ONT's VLAN 10
|
||||
becomes a *distinct* fabric VLAN:
|
||||
|
||||
- ONT-A port: VLAN 10 → **141** (→ river)
|
||||
- ONT-B port: VLAN 10 → **142** (→ stream / second river)
|
||||
- mgmt: PVID each ONT port onto its own VLAN (140, 143, …) so both ONTs' `192.168.100.1` stay in
|
||||
separate L2/L3 domains.
|
||||
|
||||
The forward direction isolates naturally (each ONT maps to a different fabric VLAN). The **return**
|
||||
direction is where port targeting is mandatory: both translate *back* to VLAN 10, so bridge VLAN 10
|
||||
now has two members and a plain FDB-miss flood would leak one ONT's upstream to the other. Each
|
||||
return must be pinned to its port with `new-dst-ports`:
|
||||
```
|
||||
# ONT-A: 141 in on palace → 10, forced out ONT-A's port
|
||||
# ONT-B: 142 in on stream → 10, forced out ONT-B's port
|
||||
```
|
||||
Each ONT port must also be a tagged member of bridge VLAN 10 for correct egress tagging (the missing
|
||||
piece that otherwise shows up as pppd "Timeout waiting for PADO"). The pins bypass the FDB, so the
|
||||
two ISP sessions never mix.
|
||||
|
||||
**Why a new switch:** jim (the only box with spare SFP+ *and* the translation feature) has just
|
||||
**one** free SFP+ port, so it can't host two ONTs. The plan is a dedicated
|
||||
**CRS305-1G-4S+** (4×SFP+, same Marvell rule support) to land multiple ONTs and do the per-port
|
||||
translation there, feeding distinct fabric VLANs up to dave.
|
||||
|
||||
## Accessing the switches
|
||||
|
||||
The switches resolve by **short hostname** on the home network — the home routers serve their
|
||||
records in the home zone (`nixos/boxes/home/routing-common/dns.nix`: `jim` → hi `.10`, `dave` → hi
|
||||
`.11`, `brian` → core `.13`). From a box on the home network just `ssh admin@jim` / `admin@dave`.
|
||||
|
||||
**Key auth** for `admin` is installed on jim/dave (and the `vibe` AP) — `ssh -i ~/.ssh/id_rsa
|
||||
admin@jim` works keyless (imported via `/user ssh-keys import`). Password `admin`/`admin` remains as
|
||||
a fallback. Non-interactive password pattern (avoids the ssh-agent hang) if the key isn't available:
|
||||
|
||||
```
|
||||
sshpass -p admin ssh -o IdentityAgent=none -o PubkeyAuthentication=no \
|
||||
-o PreferredAuthentications=password -o StrictHostKeyChecking=accept-new \
|
||||
-o UserKnownHostsFile=/tmp/sw_known_hosts admin@jim
|
||||
```
|
||||
|
||||
**Always confirm config changes on the switch** (print the affected menu, apply, re-verify). brian
|
||||
is UniFi — configured through its controller, not RouterOS CLI.
|
||||
|
||||
## Management IPs
|
||||
|
||||
| | core (`192.168.64.0/24`) | hi (`192.168.68.0/22`) | lo (`192.168.72.0/21`) |
|
||||
|---|---|---|---|
|
||||
| jim | `.10` (on `main`) | `.10` | `.10` |
|
||||
| dave | `.11` (on `management`, the 1G Atheros port) | `.11` | `.11` |
|
||||
| brian | `.13` (core) | — | — |
|
||||
@@ -242,10 +242,14 @@ in
|
||||
shytzel IN A ${net.cidr.host 12 prefixes.core.v4}
|
||||
brian IN A ${net.cidr.host 13 prefixes.core.v4}
|
||||
|
||||
wave IN A ${net.cidr.host 12 prefixes.hi.v4}
|
||||
; wave IN AAAA ${net.cidr.host (65536+3) prefixes.hi.v6}
|
||||
vibe IN A ${net.cidr.host 13 prefixes.hi.v4}
|
||||
vibe IN AAAA ${net.cidr.host (65536+4) prefixes.hi.v6}
|
||||
vibe-core IN A ${net.cidr.host 15 prefixes.core.v4}
|
||||
vibe IN A ${net.cidr.host 15 prefixes.hi.v4}
|
||||
vibe IN AAAA ${net.cidr.host (65536+6) prefixes.hi.v6}
|
||||
vibe-lo IN A ${net.cidr.host 15 prefixes.lo.v4}
|
||||
vibe-lo IN AAAA ${net.cidr.host (65536+6) prefixes.lo.v6}
|
||||
wave-core IN A ${net.cidr.host 14 prefixes.core.v4}
|
||||
wave IN A ${net.cidr.host 14 prefixes.lo.v4}
|
||||
wave IN AAAA ${net.cidr.host (65536+5) prefixes.lo.v6}
|
||||
|
||||
ups IN A ${net.cidr.host 20 prefixes.lo.v4}
|
||||
palace-kvm IN A ${net.cidr.host 21 prefixes.lo.v4}
|
||||
|
||||
Reference in New Issue
Block a user