From d24207427f00f08ff7e0490c9859853d455bef79 Mon Sep 17 00:00:00 2001 From: josh Date: Fri, 17 Apr 2026 22:57:49 -0400 Subject: [PATCH] Fix quick-register broadcast detection on Proxmox bridges Two bugs compounded on Proxmox hosts: primary_iface walked `ip link show` and picked the physical NIC (e.g. enp1s0), which has no IPv4 on Proxmox because the address lives on vmbr0. Even if vmbr0 had been picked, the kernel reports its broadcast as 0.0.0.0, so the script fell all the way back to 255.255.255.255. Now we prefer the default-route interface (vmbr0 on Proxmox, eno1 on bare metal) and, when `ip` doesn't surface a usable `brd`, compute the broadcast from the inet CIDR instead of giving up. --- internal/web/register/quick.sh.tmpl | 49 ++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/internal/web/register/quick.sh.tmpl b/internal/web/register/quick.sh.tmpl index 94f74d2..b4e21d8 100644 --- a/internal/web/register/quick.sh.tmpl +++ b/internal/web/register/quick.sh.tmpl @@ -26,16 +26,40 @@ if [[ -z "${ORCH_URL}" ]]; then fi primary_iface() { - # Pick the first physical ethernet-style interface that isn't a loopback, - # bridge, veth, docker, virbr, bond, tun, or tap. - ip -o link show 2>/dev/null \ - | awk '$0 ~ /link\/ether/ { - name=$2; sub(":","",name) - if (name ~ /^(lo|docker|br-|veth|virbr|bond|tun|tap|wlan|wlp)/) next + # Prefer the interface carrying the default route — that's the + # canonical "primary" iface (e.g. vmbr0 on Proxmox, eno1 on bare + # metal). `ip link show` order picks the physical NIC on Proxmox, + # but that NIC has no IPv4, so we'd miss the broadcast address. + local iface + iface="$(ip -o -4 route show default 2>/dev/null \ + | awk '{for(i=1;i<=NF;i++) if($i=="dev") {print $(i+1); exit}}')" + if [[ -n "${iface}" ]]; then + echo "${iface}" + return + fi + # Fallback: first non-virtual interface that has an IPv4 address. + ip -o -4 addr show 2>/dev/null \ + | awk '{ + name=$2 + if (name ~ /^(lo|docker|br-|veth|virbr|bond|tun|tap|fwbr|fwpr|fwln|wlan|wlp)/) next print name; exit }' } +# compute_broadcast "192.168.1.250/24" → "192.168.1.255" +compute_broadcast() { + local cidr="$1" ip prefix a b c d host mask inv bc + ip="${cidr%/*}" + prefix="${cidr#*/}" + [[ "${ip}" == *.*.*.* && "${prefix}" =~ ^[0-9]+$ ]] || return 1 + IFS=. read -r a b c d <<<"${ip}" + host=$(( (a<<24) | (b<<16) | (c<<8) | d )) + mask=$(( (0xFFFFFFFF << (32 - prefix)) & 0xFFFFFFFF )) + inv=$(( (~mask) & 0xFFFFFFFF )) + bc=$(( host | inv )) + printf '%d.%d.%d.%d' $(( (bc>>24)&0xFF )) $(( (bc>>16)&0xFF )) $(( (bc>>8)&0xFF )) $(( bc&0xFF )) +} + IFACE="$(primary_iface || true)" if [[ -z "${IFACE}" ]]; then echo "ERROR: could not pick a primary network interface." >&2 @@ -50,8 +74,17 @@ if [[ -z "${MAC}" ]]; then fi if [[ -z "${WOL_BROADCAST:-}" ]]; then - WOL_BROADCAST="$(ip -o -4 addr show dev "${IFACE}" 2>/dev/null \ - | awk '{for(i=1;i<=NF;i++) if($i=="brd") {print $(i+1); exit}}')" + ipinfo="$(ip -o -4 addr show dev "${IFACE}" 2>/dev/null || true)" + WOL_BROADCAST="$(awk '{for(i=1;i<=NF;i++) if($i=="brd") {print $(i+1); exit}}' <<<"${ipinfo}")" + # Bridges (vmbr0 on Proxmox, br0 on Linux bridges) often report + # brd 0.0.0.0 or omit it entirely. Compute from the inet CIDR + # before giving up on 255.255.255.255. + if [[ -z "${WOL_BROADCAST}" || "${WOL_BROADCAST}" == "0.0.0.0" ]]; then + cidr="$(awk '{for(i=1;i<=NF;i++) if($i=="inet") {print $(i+1); exit}}' <<<"${ipinfo}")" + if [[ "${cidr}" == */* ]]; then + WOL_BROADCAST="$(compute_broadcast "${cidr}" || true)" + fi + fi fi WOL_BROADCAST="${WOL_BROADCAST:-255.255.255.255}" WOL_PORT="${WOL_PORT:-9}"