dixie: fail over USB Wi-Fi to internal CNVi
NM dispatcher plus a boot oneshot keep the AX9L (mt7921u) primary and bring up the on-box iwlwifi profile if the dongle is gone. Connection PSKs stay on-box; this only selects the radio.
This commit is contained in:
+116
-4
@@ -128,6 +128,105 @@ let
|
||||
TimeoutStartSec = "600";
|
||||
};
|
||||
};
|
||||
|
||||
# USB AX9L (mt7921u) is primary; internal CNVi (iwlwifi/wlo1) is fallback.
|
||||
# Connection profiles stay on-box (PSK); this only selects which radio is up.
|
||||
usbWifiMac = "74:19:f8:17:5e:53";
|
||||
usbWifiConn = "ATTjkThnDS";
|
||||
internalWifiConn = "ATTjkThnDS-iwlwifi";
|
||||
internalWifiIf = "wlo1";
|
||||
wifiUsbFailover = pkgs.writeShellScript "wifi-usb-failover" ''
|
||||
set -eu
|
||||
nmcli=${pkgs.networkmanager}/bin/nmcli
|
||||
logger=${pkgs.util-linux}/bin/logger
|
||||
systemd_run=${pkgs.systemd}/bin/systemd-run
|
||||
tr=${pkgs.coreutils}/bin/tr
|
||||
|
||||
USB_MAC="${usbWifiMac}"
|
||||
USB_CONN="${usbWifiConn}"
|
||||
FALLBACK_CONN="${internalWifiConn}"
|
||||
INTERNAL_IF="${internalWifiIf}"
|
||||
|
||||
log() { "$logger" -t wifi-usb-failover -- "$*"; }
|
||||
|
||||
iface_mac() {
|
||||
"$tr" 'A-F' 'a-f' < "/sys/class/net/$1/address" 2>/dev/null || true
|
||||
}
|
||||
|
||||
usb_iface() {
|
||||
for n in /sys/class/net/*; do
|
||||
n=''${n##*/}
|
||||
[ "$n" = "lo" ] && continue
|
||||
[ "$(iface_mac "$n")" = "$USB_MAC" ] && { echo "$n"; return 0; }
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
is_usb_iface() {
|
||||
[ "$(iface_mac "$1")" = "$USB_MAC" ]
|
||||
}
|
||||
|
||||
demote_internal() {
|
||||
"$nmcli" device set "$INTERNAL_IF" autoconnect no >/dev/null 2>&1 || true
|
||||
"$nmcli" connection down "$FALLBACK_CONN" >/dev/null 2>&1 || true
|
||||
"$nmcli" device disconnect "$INTERNAL_IF" >/dev/null 2>&1 || true
|
||||
}
|
||||
|
||||
promote_internal() {
|
||||
if usb_iface >/dev/null 2>&1; then
|
||||
log "USB still present; not failing over"
|
||||
return 0
|
||||
fi
|
||||
log "USB absent; bringing up $FALLBACK_CONN on $INTERNAL_IF"
|
||||
"$nmcli" device set "$INTERNAL_IF" autoconnect yes >/dev/null 2>&1 || true
|
||||
"$nmcli" connection up "$FALLBACK_CONN" ifname "$INTERNAL_IF" >/dev/null 2>&1 || true
|
||||
}
|
||||
|
||||
settle() {
|
||||
i=0
|
||||
while [ "$i" -lt 20 ]; do
|
||||
if usb_iface >/dev/null 2>&1; then
|
||||
u=$(usb_iface)
|
||||
log "settle: USB $u present; demoting internal"
|
||||
demote_internal
|
||||
if ${pkgs.iproute2}/bin/ip -4 -o addr show dev "$u" | ${pkgs.gnugrep}/bin/grep -q ' inet '; then
|
||||
return 0
|
||||
fi
|
||||
"$nmcli" connection up "$USB_CONN" ifname "$u" >/dev/null 2>&1 || true
|
||||
return 0
|
||||
fi
|
||||
i=$((i + 1))
|
||||
sleep 1
|
||||
done
|
||||
log "settle: no USB after 20s; failing over"
|
||||
promote_internal
|
||||
}
|
||||
|
||||
IFACE=''${1-}
|
||||
ACTION=''${2-}
|
||||
|
||||
case "$IFACE" in
|
||||
boot|settle) settle; exit 0 ;;
|
||||
after-usb-down) sleep 3; promote_internal; exit 0 ;;
|
||||
esac
|
||||
|
||||
case "$ACTION" in
|
||||
up)
|
||||
if is_usb_iface "$IFACE"; then
|
||||
log "USB $IFACE up; demoting $INTERNAL_IF"
|
||||
demote_internal
|
||||
fi
|
||||
;;
|
||||
down)
|
||||
if is_usb_iface "$IFACE"; then
|
||||
log "USB $IFACE down; scheduling failover check"
|
||||
"$systemd_run" --quiet --collect --unit=wifi-usb-failover-down \
|
||||
--property=Type=oneshot "$0" after-usb-down >/dev/null 2>&1 || true
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
exit 0
|
||||
'';
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
@@ -203,11 +302,24 @@ in
|
||||
upstream = "http://127.0.0.1:11434";
|
||||
};
|
||||
|
||||
# Wi-Fi only host. NetworkManager comes from the base config; the AX9L (mt7921u) becomes
|
||||
# the primary once plugged in and the internal CNVi radio stays as fallback. Connection
|
||||
# profiles are copied from the live-USB session at install time (see the install notes),
|
||||
# not stored in this repo.
|
||||
# Wi-Fi only host. NetworkManager comes from the base config; the AX9L (mt7921u) is
|
||||
# primary and the internal CNVi radio is fallback. Connection profiles are copied
|
||||
# from the live-USB session at install time (see the install notes), not stored here.
|
||||
networking.networkmanager.wifi.backend = "wpa_supplicant";
|
||||
networking.networkmanager.dispatcherScripts = [
|
||||
{ source = wifiUsbFailover; type = "basic"; }
|
||||
];
|
||||
systemd.services.wifi-usb-failover-boot = {
|
||||
description = "Prefer USB Wi-Fi; fail over to internal CNVi if the dongle is absent";
|
||||
after = [ "NetworkManager.service" ];
|
||||
wants = [ "NetworkManager.service" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
ExecStart = "${wifiUsbFailover} boot";
|
||||
};
|
||||
};
|
||||
|
||||
# Reachable only over the tailnet: 11434 (router), 11433 (vision), 11435 (tether)
|
||||
# on tailscale0, and SSH from the LAN (base config opens 22) for bring-up.
|
||||
|
||||
Reference in New Issue
Block a user