This repository has been archived on 2026-08-17. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
debian-configuration/docs/06-pve-test-wifi-network.md
T
beatzaplenty 143eebcbf3 Correct AP-isolation guess: confirmed as a 4addr forwarding gap instead
Tested live: AP isolation was already off on the TP-Link BE9300, and a
second wireless device (phone) failed identically to the laptop - rules
out isolation entirely. Real cause is the router's 4addr/WDS handling
not forwarding wireless-client broadcast/ARP traffic to pve-test's WDS
association, even though wired forwarding to it works fine. Since ARP
never resolves for wireless peers, this affects every protocol (web UI
included), not just ICMP - corrects an earlier draft of this doc that
speculated TCP might still work.
2026-07-21 21:35:08 +00:00

231 lines
12 KiB
Markdown

# pve-test's Wifi-Primary Network
Non-standard, deliberately chosen against normal Proxmox guidance (a
hypervisor's management network is not supposed to be wireless). Applies
**only** to `pve-test` as a standalone sandbox node — see
`05-node-roles.md` for why this and cluster membership don't mix, and
never apply this pattern to a node that's joined (or might join) a
cluster.
## Why this works at all
Wifi (802.11) normally cannot be bridged the way Ethernet can: an access
point only accepts frames whose source MAC matches the MAC that
associated with it. A Linux bridge with a wifi port as a member sends
frames tagged with whatever MAC actually generated them (the bridge's own
MAC, a VM's MAC, etc.) — none of which match the wifi card's hardware
MAC, so the AP silently drops them. This isn't a Linux/Proxmox limitation,
it's how WiFi association works.
**4addr (WDS) client mode** is the exception: if both the wifi driver and
the AP support it, the client tags frames with a 4th address field
carrying the original MAC, and the AP forwards them like a real Ethernet
segment. Most consumer routers do **not** support this (it's common on
OpenWrt/DD-WRT/enterprise APs, rare on stock ISP hardware) — it has to be
confirmed empirically, per-AP, before trusting it with a management IP.
pve-test's card (Intel AX200, `iwlwifi` driver, interface `wlp3s0`)
supports 4addr client-side generically (this is a `mac80211` core
feature, not driver-specific). The home AP it associates to
(`nbn-fttp-net-5G`) was confirmed to honor it — see validation method
below.
## Validate before ever touching a live management IP
Don't add the wifi NIC straight into your real bridge to "see if it
works" — if 4addr isn't actually honored, you lose access to the box with
no diagnostic trail (association succeeds; only the *data plane* silently
fails). Test in an isolated scratch namespace first:
```bash
# assumes wlp3s0 already associated with 4addr on (see systemd unit below)
ip link add testbr0 type bridge && ip link set testbr0 up
ip link set wlp3s0 master testbr0 && ip link set wlp3s0 up
ip netns add wifitest
ip link add veth-host type veth peer name veth-ns
ip link set veth-host master testbr0 && ip link set veth-host up
ip link set veth-ns netns wifitest
ip netns exec wifitest ip link set veth-ns address 02:11:22:33:44:55 # deliberately NOT the wifi card's MAC
ip netns exec wifitest ip link set veth-ns up
ip netns exec wifitest ip link set lo up
ip netns exec wifitest dhclient -v -1 veth-ns # real DHCP lease = AP forwards foreign MACs = 4addr genuinely works
```
If that gets a lease (or at minimum a ping reply from the gateway) from a
MAC that was never the one that associated, the AP is forwarding
arbitrary-MAC frames — real bridge mode will work. If not, don't proceed
to bridge mode; fall back to a routed/NAT design instead (wifi gets its
own IP via DHCP, VMs NAT out through it — never covered here since it
wasn't needed, but keep this fallback in mind if reproducing on different
hardware/AP).
Tear the test namespace/bridge down afterwards (`ip netns del wifitest;
ip link del veth-host; ip link set wlp3s0 nomaster; ip link del testbr0`)
— it's scratch, not part of the real config.
## Final architecture
```
wlp3s0 (wifi, 4addr client mode) ─┐
├─ bond0 (active-backup, wlp3s0 primary) ─ vmbr0 (192.168.2.251/24)
nic0 (wired, LAN backup) ─┘
```
- **`wpa-4addr-<iface>.service`** (systemd unit, `Before=network-pre.target`) —
sets 4addr mode and starts `wpa_supplicant` *before* ifupdown2 processes
`/etc/network/interfaces`. Deliberately not using `wpasupplicant`'s own
`wpa-conf` ifupdown2 hook integration — its ordering relative to a
custom `pre-up iw ... set 4addr on` line is implementation-specific and
not worth gambling on for something this hard to debug if it's wrong.
The systemd unit gives full, deterministic control over sequencing
instead.
- **`bond0`** — active-backup bonding, `wlp3s0` as `bond-primary`,
`nic0` as backup, `bond-miimon 100`. Bonding a wifi interface works here
because `mac80211`/`iwlwifi` properly reports carrier state
(`netif_carrier_on/off`) on association/disassociation, which is what
bonding's `miimon` (with the default `use_carrier=1`) actually watches
— it doesn't require a "real" MII-capable NIC.
- **`vmbr0`** — unchanged IP (`192.168.2.251/24`), bridged over `bond0`
instead of directly over a physical NIC.
Reproduce with `scripts/setup-wifi-bond-network.sh` — see its header
comment for usage and required env vars (SSID/passphrase, management
address/gateway). It stages `/etc/network/interfaces` and the systemd
unit but does **not** auto-apply (`ifreload -a`) — see the next section
for why that step needs care, not automation.
## Applying a live bridge-port change: what went wrong once, and what worked
Moving `vmbr0`'s underlying port live — while SSH'd in over the very
address that lives on that bridge — is inherently risky: if the new port
doesn't actually pass traffic, you lose the connection you're using to
fix it.
**What failed**: doing the port swap via raw `ip link set nic0 nomaster`
+ `ip link set nic0 down` (with `wlp3s0` already added as a second bridge
member) caused an extended outage that needed a physical power-cycle to
recover — `nic0` never came back up on its own even well past a 60s
auto-revert watchdog's deadline. Importantly, the *host itself* never
crashed — `journalctl -b -1` showed completely normal operation (a
`pvestatd` polling loop, no gaps, no panic/OOM/watchdog trigger) right up
until a keyboard was plugged in and it was manually reset. So the failure
was specifically in the live network transition, not the OS — root cause
unconfirmed, but plausibly bridge FDB/MAC-identity handling when a port
carrying an already-live IP is swapped out via raw `ip link` rather than
a coordinated reconfiguration.
**What worked**: writing the final state into `/etc/network/interfaces`
and applying with `ifreload -a` (Proxmox's own supported hot-reload path)
instead. Same end state, same risk window, but ifupdown2 evidently
sequences the transition in a way raw `ip link` surgery didn't. This
succeeded on the first retry using this method.
**Either way, expect ~30-60 seconds of apparent breakage even on a
successful change** — the upstream switch/AP needs to relearn which port
`192.168.2.251`'s MAC now lives behind. Don't judge success/failure
before that window passes; a `ping`/SSH failure at the 5-10s mark is not
yet a sign anything is wrong.
**Recommended safety net for any future live change here**: arm a
backgrounded auto-revert-on-timeout before applying, e.g.:
```bash
cp /etc/network/interfaces /etc/network/interfaces.bak
cat > /root/revert.sh <<'EOF'
#!/bin/bash
cp /etc/network/interfaces.bak /etc/network/interfaces
ifreload -a
EOF
chmod +x /root/revert.sh
setsid nohup bash -c "sleep 45 && /root/revert.sh" >/root/revert.log 2>&1 < /dev/null &
disown
# now apply the real change, e.g.: ifreload -a
# once confirmed working: pkill -f "sleep 45 && /root/revert.sh"
```
This only self-heals if the box stays responsive enough for the
backgrounded job to run to completion — it is not a substitute for
physical/console access being available, just a way to avoid needing it
for the common case.
## Troubleshooting: "can't ping it, web UI won't load" isn't necessarily an outage
Two independent things can make `pve-test` look dead when it isn't:
1. **Stale ARP after any change to `vmbr0`'s active port.** `vmbr0`'s MAC
address follows whichever interface is currently active in `bond0`
(`wlp3s0`'s MAC while wifi is primary, `nic0`'s if it fails over) — it
is *not* fixed. Other devices on the LAN (including your own
workstation) that cached the old MAC before a change won't notice
until their ARP entry naturally expires or gets flushed
(`ip neigh flush <ip>` / `arp -d <ip>`). Symptom: intermittent or
totally dead connectivity from one specific device while others (or
the host itself) are fine.
2. **ICMP ping is blocked by the management firewall, unrelated to
network health.** See `04-security-hardening.md`'s firewall section —
`cluster.fw`'s `policy_in: DROP` only explicitly allows TCP 22/8006
(now also ICMP echo-request, after this was hit) from the mgmt
network. Before that rule was added, `ping` failed 100% *even with a
perfectly correct, fresh ARP entry and full SSH/web UI access* —
nothing to do with wifi, bonding, or the network being down. This is
exactly what happened once: alarming "can't ping, web UI not loading"
turned out to be a stale-ARP moment (which resolved itself) plus a
pre-existing firewall policy (ping was never going to work,
regardless of wifi).
**When `pve-test` seems unreachable, check in this order before assuming
a real outage**: SSH (`ssh root@pve-test.sweet.home`) → web UI via `curl
-sk -o /dev/null -w '%{http_code}' https://pve-test.sweet.home:8006/`
only then worry about `ping` specifically, and check
`/etc/pve/firewall/cluster.fw` before blaming the network.
## Confirmed limitation: no wireless client on the same AP can reach pve-test
**This is not an AP-isolation toggle** — tested and ruled out (router:
TP-Link BE9300, AP Isolation confirmed unchecked). Two independent
wireless devices (a laptop and a phone) on the same SSID pve-test uses
(`nbn-fttp-net-5G`) were both completely unable to reach it — not
intermittent, not slow, no ARP entry ever resolves (`arp -a` on Windows /
`ip neigh` on Linux/Mac shows nothing at all, not a stale entry). Every
wired device tested (this repo's own Claude session, `pve1`) reaches it
fine, every time.
Root cause: pve-test's wifi connection uses 4-address (WDS) framing to
make bridging possible at all (see above) — that's what lets the AP
forward frames from arbitrary MACs (VM traffic, the bridge's own
identity) instead of only the wifi card's own hardware MAC. This
router's firmware evidently handles wired↔pve-test forwarding for that
4addr peer correctly, but doesn't correctly forward *wireless-client*
broadcast/ARP traffic to it — an asymmetric gap in the AP's own 4addr
handling, not a policy setting. Likely not fixable short of a firmware
update from TP-Link (if one ever improves 4addr/WDS handling), possibly
not fixable on this hardware at all.
**Practical implication**: anything on `vmbr0` — pve-test itself, or any
VM/CT created on it — is unreachable from a wireless client on this AP,
full stop, for *any* protocol, not just ICMP. ARP resolution is a
prerequisite for sending any IP packet at all (TCP included); since ARP
itself never resolves for a wireless peer here, the web UI (8006) fails
identically to ping, not just ping — confirmed live, not theoretical.
This includes VMs on pve-test too, since they ride the same bridge/wifi
uplink as the host. **Wired access is the only reliable path to
pve-test and anything running on it** — including reaching a VM's
console through the Proxmox web UI, which still requires the accessing
device to reach pve-test's own IP on port 8006 first, so it inherits the
same requirement.
Don't waste time on ARP flushes, firewall rules, or bonding config for
this specific symptom (no ARP entry from a wireless peer, wired fine) —
none of those are the cause.
## Known limitation history
`nic0` (the bond's backup slave) initially had no cable physically
connected when this was first set up — the bond was correctly configured
but inert until a cable was plugged in. As of this writing a cable is
connected and the backup slave is live (confirm with `cat
/proc/net/bonding/bond0`) — if reproducing this setup, verify the same
before assuming LAN failover will actually work.