diff --git a/playbook/pxe-server-helpers/pxe-dhcp-hook.sh b/playbook/pxe-server-helpers/pxe-dhcp-hook.sh index df454db..6eb72da 100755 --- a/playbook/pxe-server-helpers/pxe-dhcp-hook.sh +++ b/playbook/pxe-server-helpers/pxe-dhcp-hook.sh @@ -14,6 +14,14 @@ # $3 = client IP # $4 = hostname (may be empty) # +# A re-image usually comes back to the SAME lease (same MAC, same IP), which +# dnsmasq reports as "old", not "add" - and only at all because dnsmasq.conf +# sets script-on-renewal. "old" also fires when a running Windows PC renews +# its lease, so on "old" we only clear state when the request came from PXE +# firmware or iPXE (vendor class PXEClient...): that machine has just +# rebooted into a network boot, so any server-side session for its IP is +# dead. A renewal from Windows (vendor class "MSFT 5.0") is left alone. +# # Must exit quickly; dnsmasq waits for it. Logs to syslog via logger. set +e @@ -25,13 +33,22 @@ 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})" +} + case "$action" in add|del) - /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 $action $ip ($mac${host:+ $host})" + clear_state "$action" + ;; + old) + case "${DNSMASQ_VENDOR_CLASS:-}" in + PXEClient*) clear_state "old (network boot, ${DNSMASQ_VENDOR_CLASS%%:UNDI*})" ;; + esac ;; esac diff --git a/playbook/pxe-server-helpers/smb-soft-reset.sh b/playbook/pxe-server-helpers/smb-soft-reset.sh index 388956e..e2f409d 100755 --- a/playbook/pxe-server-helpers/smb-soft-reset.sh +++ b/playbook/pxe-server-helpers/smb-soft-reset.sh @@ -15,7 +15,7 @@ # Step 3: restart smbd (full smbd restart, kills all child sessions) # Step 4: kill any leftover smbd child processes that survived restart # Step 5: flush conntrack for 172.16.9.0/24 (kernel connection tracking) -# Step 6: flush ARP / neighbour cache on br-pxe +# Step 6: flush ARP / neighbour cache # Step 7: drop TCP sockets on port 445 via ss -K # Step 8: restart dnsmasq (DHCP/TFTP state as a last resort before reboot) @@ -65,8 +65,8 @@ else fi pause "Step 5 done" -echo "=== Step 6/8: flush ARP / neighbour cache on br-pxe ===" -ip neigh flush dev br-pxe 2>&1 || ip neigh flush all 2>&1 || true +echo "=== Step 6/8: flush ARP / neighbour cache ===" +ip neigh flush all 2>&1 || true pause "Step 6 done" echo "=== Step 7/8: drop tcp sockets on port 445 ===" diff --git a/playbook/pxe_server_setup.yml b/playbook/pxe_server_setup.yml index c36307e..b8a879e 100644 --- a/playbook/pxe_server_setup.yml +++ b/playbook/pxe_server_setup.yml @@ -205,6 +205,11 @@ # client re-images the same machine and hits a stale SMB session. # Script runs as root by default (dnsmasq --dhcp-scriptuser default). dhcp-script=/usr/local/sbin/pxe-dhcp-hook.sh + # Also call it when a client comes back to its existing lease (a + # re-image of the same PC), which dnsmasq otherwise reports only if + # the hostname or MAC changed. The hook acts on those only for PXE + # firmware / iPXE requests. + script-on-renewal - name: "Create TFTP directory" file: @@ -628,6 +633,15 @@ mode: '0644' notify: reload sysctl + - name: "Load nf_conntrack at boot so the conntrack sysctl applies" + # systemd-sysctl runs before ufw loads nf_conntrack, so without this the + # net.netfilter.* keys do not exist yet at boot and the 1 h timeout is + # silently skipped (the kernel default of 5 days stays in force). + copy: + dest: /etc/modules-load.d/pxe-conntrack.conf + content: "nf_conntrack\n" + mode: '0644' + - name: "Deploy SMB diagnostic + soft-reset helper scripts" copy: src: "{{ usb_mount }}/pxe-server-helpers/{{ item }}"