#!/bin/bash # Reproduces pve-test's wifi-primary networking: vmbr0 bridged over a wifi # NIC in 4addr (WDS) client-bridge mode, active-backup bonded with a wired # NIC as an automatic LAN fallback. See docs/06-pve-test-wifi-network.md for # why this exists and how it was validated. Idempotent - safe to re-run. # # Requires: a wifi NIC whose driver/AP both support 4addr mode (verify with # docs/06-pve-test-wifi-network.md's isolated-namespace test *before* # trusting this against a live management IP - a wifi NIC or AP that # doesn't support 4addr will associate fine but silently drop bridged # frames from any MAC other than the card's own). # # Usage (run as root on the target PVE host): # WIFI_SSID="..." WIFI_PASSPHRASE="..." \ # MGMT_ADDR=192.168.2.251/24 MGMT_GATEWAY=192.168.2.254 \ # ./setup-wifi-bond-network.sh # # Optional overrides: WIFI_IFACE (default wlp3s0), LAN_IFACE (default nic0) set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # shellcheck source=lib/common.sh source "${SCRIPT_DIR}/lib/common.sh" require_root WIFI_IFACE="${WIFI_IFACE:-wlp3s0}" LAN_IFACE="${LAN_IFACE:-nic0}" for var in WIFI_SSID WIFI_PASSPHRASE MGMT_ADDR MGMT_GATEWAY; do if [ -z "${!var:-}" ]; then echo "$var is not set. See usage in this script's header." >&2 exit 1 fi done if ! ip link show "$WIFI_IFACE" >/dev/null 2>&1; then echo "No interface named $WIFI_IFACE on this host. Run 'ip -br link' and set WIFI_IFACE=..." >&2 exit 1 fi if ! ip link show "$LAN_IFACE" >/dev/null 2>&1; then echo "No interface named $LAN_IFACE on this host. Run 'ip -br link' and set LAN_IFACE=..." >&2 exit 1 fi echo "=== 1/4: wifi tooling ===" apt-get install -y iw wpasupplicant >/dev/null echo "installed iw, wpasupplicant" echo echo "=== 2/4: wpa_supplicant config (SSID: $WIFI_SSID, iface: $WIFI_IFACE) ===" WPA_CONF="/etc/wpa_supplicant/wpa_supplicant-${WIFI_IFACE}.conf" wpa_passphrase "$WIFI_SSID" "$WIFI_PASSPHRASE" > "$WPA_CONF" sed -i '/^\s*#psk=/d' "$WPA_CONF" chmod 600 "$WPA_CONF" echo "wrote $WPA_CONF (passphrase hashed, not stored in plaintext)" echo echo "=== 3/4: systemd unit to set 4addr mode + start wpa_supplicant ===" UNIT="/etc/systemd/system/wpa-4addr-${WIFI_IFACE}.service" write_if_changed "$UNIT" "[Unit] Description=wpa_supplicant on ${WIFI_IFACE} with 4addr mode enabled Before=network-pre.target Wants=network-pre.target [Service] Type=simple ExecStartPre=/sbin/ip link set ${WIFI_IFACE} down ExecStartPre=/sbin/iw dev ${WIFI_IFACE} set 4addr on ExecStartPre=/sbin/ip link set ${WIFI_IFACE} up ExecStart=/sbin/wpa_supplicant -i ${WIFI_IFACE} -c ${WPA_CONF} Restart=on-failure RestartSec=3 [Install] WantedBy=multi-user.target" systemctl daemon-reload systemctl enable --now "wpa-4addr-${WIFI_IFACE}.service" sleep 5 if ! iw dev "$WIFI_IFACE" link | grep -q "^Connected"; then echo "WARNING: ${WIFI_IFACE} did not associate to '$WIFI_SSID' within 5s - check:" >&2 echo " systemctl status wpa-4addr-${WIFI_IFACE}.service" >&2 echo " journalctl -u wpa-4addr-${WIFI_IFACE}.service" >&2 exit 1 fi echo "associated: $(iw dev "$WIFI_IFACE" link | grep '^Connected')" echo echo "=== 4/4: /etc/network/interfaces (bond0 active-backup: ${WIFI_IFACE} primary, ${LAN_IFACE} backup) ===" IFACES_FILE="/etc/network/interfaces" backup_file "$IFACES_FILE" cat > "$IFACES_FILE" <