NIC guard: ignore client EAPOL, re-check on carrier change, SMB-only lease cleanup

- pxe-nic-check: count 802.1X only from an authenticator (EAPOL type 0
  with EAP Request/Success/Failure). iPXE sends EAPOL-Start on every NIC
  open and Windows sends Start/Response, so a PXE-booting bay made the
  verdict 'ge' and the watch stopped dnsmasq for the floor.
- pxe-nic-check: join the STP/PAE/LLDP/CDP/PVST+ multicast groups on
  every capture socket, so CDP-only ports are heard on a plain NIC.
- pxe-nic-check --watch: if dnsmasq was (re)started inside the window,
  re-check (~6 s) before stopping it; skip when already stopped.
  New --state-cmd and --print-imaging.
- pxe-nic-carrier (+ pxe-nic-carrier.service): networkd-dispatcher hook
  for the imaging NIC only. Carrier loss stops dnsmasq; carrier gain
  restarts it through its guard if it was running or the hook stopped it.
- pxe-dhcp-hook.sh: clear only SMB (tcp 445/139) conntrack entries and
  sockets, not all traffic for the client, which raced iPXE's first
  HTTP fetch.
- GetPxeScript.aspx: every boot line has a failure branch (iPXE 2.0
  accepts an unsigned EFI kernel and fails at boot under Secure Boot,
  which aborted the script).
- Tests: client EAPOL, packet_mreq packing, watch race, shell helpers on
  stub PATH, lab scenarios eapol-client, br-eapol-client, race, carrier.
This commit is contained in:
cproudlock
2026-10-15 14:26:39 -04:00
parent 53d39bfc50
commit 21ff95af78
13 changed files with 1022 additions and 36 deletions

View File

@@ -3,10 +3,15 @@
# pxe-dhcp-hook.sh - dnsmasq dhcp-script hook.
#
# Runs every time a PXE client gets/changes/releases a DHCP lease on
# 172.16.9.0/24. Flushes conntrack entries and drops any lingering
# TCP sockets for that client IP. Prevents stale server-side state from
# causing "System error 53 - network path not found" when a WinPE client
# re-images the same machine without a clean SMB session teardown.
# 172.16.9.0/24. Flushes that client's SMB (TCP 445 and 139) conntrack
# entries and drops its lingering SMB sockets. Prevents stale server-side
# state from causing "System error 53 - network path not found" when a
# WinPE client re-images the same machine without a clean SMB session
# teardown.
#
# SMB only, on purpose: the lease that fires this hook is iPXE's, and its
# first HTTP fetch of the boot menu is racing this script. Killing every
# socket or conntrack entry for the client IP broke that fetch.
#
# dnsmasq calls this with argv:
# $1 = action (add | old | del)
@@ -26,6 +31,9 @@
set +e
# dnsmasq's environment may lack sbin; keep any PATH it gave us first.
PATH="${PATH:+$PATH:}/usr/sbin:/usr/bin:/sbin:/bin"
action="$1"
mac="$2"
ip="$3"
@@ -34,11 +42,15 @@ host="${4:-}"
[ -z "$ip" ] && exit 0
clear_state() {
/usr/sbin/conntrack -D -s "$ip" >/dev/null 2>&1
/usr/sbin/conntrack -D -d "$ip" >/dev/null 2>&1
/usr/bin/ss -K "dst = $ip" >/dev/null 2>&1
/usr/bin/ss -K "src = $ip" >/dev/null 2>&1
logger -t pxe-dhcp-hook "cleared stale state for $1 $ip ($mac${host:+ $host})"
local port
for port in 445 139; do
# client -> our share, and anything the other way round
conntrack -D -p tcp -s "$ip" --dport "$port" >/dev/null 2>&1
conntrack -D -p tcp -d "$ip" --sport "$port" >/dev/null 2>&1
done
# our SMB sockets (local port 445/139) whose peer is this client
ss -K -t "( sport = :445 or sport = :139 ) and dst = $ip" >/dev/null 2>&1
logger -t pxe-dhcp-hook "cleared stale SMB state for $1 $ip ($mac${host:+ $host})"
}
case "$action" in