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";
|
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
|
in
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
@@ -203,11 +302,24 @@ in
|
|||||||
upstream = "http://127.0.0.1:11434";
|
upstream = "http://127.0.0.1:11434";
|
||||||
};
|
};
|
||||||
|
|
||||||
# Wi-Fi only host. NetworkManager comes from the base config; the AX9L (mt7921u) becomes
|
# Wi-Fi only host. NetworkManager comes from the base config; the AX9L (mt7921u) is
|
||||||
# the primary once plugged in and the internal CNVi radio stays as fallback. Connection
|
# primary and the internal CNVi radio is fallback. Connection profiles are copied
|
||||||
# profiles are copied from the live-USB session at install time (see the install notes),
|
# from the live-USB session at install time (see the install notes), not stored here.
|
||||||
# not stored in this repo.
|
|
||||||
networking.networkmanager.wifi.backend = "wpa_supplicant";
|
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)
|
# 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.
|
# on tailscale0, and SSH from the LAN (base config opens 22) for bring-up.
|
||||||
|
|||||||
Reference in New Issue
Block a user