#!/usr/bin/env bash # # bt-netconfig - apply Ethernet configuration via NetworkManager. # # Usage: # bt-netconfig dhcp # bt-netconfig static
[gateway] [dns_csv] # # Intended to be invoked through sudo by the (unprivileged) web configurator. # Do NOT use ip/ifconfig directly — NetworkManager will undo manual changes. set -euo pipefail err() { echo "bt-netconfig: $*" >&2; exit 1; } [ "$#" -ge 2 ] || err "usage: bt-netconfig ..." IFACE="$1" MODE="$2" PROFILE="buttontask-$IFACE" [[ "$IFACE" =~ ^[A-Za-z0-9_.:-]{1,32}$ ]] || err "invalid interface name" command -v nmcli >/dev/null 2>&1 || err "nmcli not found" # Ensure NM manages this interface (manual ip addr fights with NM). nmcli device set "$IFACE" managed yes 2>/dev/null || true # Use a dedicated profile per interface so we never fight "Wired connection 1". if ! nmcli -t -f NAME connection show | grep -Fxq "$PROFILE"; then nmcli connection add type ethernet ifname "$IFACE" con-name "$PROFILE" \ connection.autoconnect yes >/dev/null fi CON="$PROFILE" # Disable autoconnect on other ethernet profiles for this interface. disable_other_profiles() { local iface="$1" con="$2" name bound typ while IFS= read -r name; do [ -z "$name" ] && continue [ "$name" = "$con" ] && continue typ="$(nmcli -g connection.type connection show "$name" 2>/dev/null || true)" [ "$typ" = "802-3-ethernet" ] || continue bound="$(nmcli -g connection.interface-name connection show "$name" 2>/dev/null || true)" if [ -n "$bound" ] && [ "$bound" != "--" ] && [ "$bound" != "$iface" ]; then continue fi nmcli connection modify "$name" connection.autoconnect no 2>/dev/null || true done < <(nmcli -t -f NAME connection show 2>/dev/null || true) } disable_other_profiles "$IFACE" "$CON" nmcli connection modify "$CON" \ connection.interface-name "$IFACE" \ connection.autoconnect yes \ connection.autoconnect-priority 100 case "$MODE" in dhcp) nmcli connection modify "$CON" \ ipv4.method auto \ ipv4.addresses "" \ ipv4.gateway "" \ ipv4.dns "" \ ipv4.ignore-auto-dns no \ ipv4.never-default no ;; static) [ "$#" -ge 4 ] || err "static requires
" ADDR="$3" PREFIX="$4" GW="${5:-}" DNS="${6:-}" [[ "$PREFIX" =~ ^[0-9]{1,2}$ ]] && [ "$PREFIX" -ge 1 ] && [ "$PREFIX" -le 32 ] \ || err "invalid prefix" if [ -n "$GW" ]; then nmcli connection modify "$CON" \ ipv4.method manual \ ipv4.addresses "${ADDR}/${PREFIX}" \ ipv4.gateway "$GW" \ ipv4.dns "$DNS" \ ipv4.ignore-auto-dns yes \ ipv4.never-default no else nmcli connection modify "$CON" \ ipv4.method manual \ ipv4.addresses "${ADDR}/${PREFIX}" \ ipv4.gateway "" \ ipv4.dns "$DNS" \ ipv4.ignore-auto-dns yes \ ipv4.never-default yes fi ;; *) err "mode must be dhcp or static" ;; esac # Drop the active profile so the new settings apply cleanly. nmcli device disconnect "$IFACE" 2>/dev/null || true nmcli connection down "$CON" 2>/dev/null || true if ! nmcli connection up "$CON" ifname "$IFACE" 2>/dev/null; then nmcli connection up "$CON" >/dev/null fi # Brief settle, then report what NM actually applied. sleep 1 APPLIED="$(nmcli -t -f IP4.ADDRESS device show "$IFACE" 2>/dev/null \ | sed 's/^IP4.ADDRESS://' | head -n1 | tr '\n' ' ')" echo "applied $MODE on $IFACE ($CON)${APPLIED:+ ip=$APPLIED}"