generalize: keep the static address from stranding the box after a reboot

It worked on the first boot and was gone after a later internal reboot -- no
IPv4 at all, not even DHCP. DHCP is turned off before the address is set, so
anything that stops the set mid-way leaves the interface with nothing. A stale
ARP entry for the address, left on the network by the previous instance,
tripped duplicate-address detection and made New-NetIPAddress throw; with
-ErrorAction Stop that aborted the script with DHCP already off.

DadTransmits 0 turns that detection off so the static binds regardless of what
the network remembers, and the assignment is now retried a few times rather
than fatal on the first throw. Moved to its own .ps1 -- a wait loop and a retry
are not worth keeping correct inside a cmd one-liner.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117qMyjpuXsjpVAcpJbFD8g
This commit is contained in:
Git Sagar 2026-09-10 19:49:40 -03:00
parent d94c576df2
commit 5251bf7290

View file

@ -93,12 +93,48 @@ in
# interface arrives DHCP-managed -- assigning an address without turning DHCP
# off first does not stick, which is how a VM meant to be at a fixed address
# ended up holding a lease instead.
# PowerShell in its own file: it grew a wait loop and a retry, which are no
# fun to keep correct inside a cmd one-liner.
#
# Two things it must survive. DHCP is turned off before the address is set,
# so any failure to set it strands the box with no address at all -- which is
# exactly what happened after an internal reboot, where a stale ARP entry for
# the address from the previous instance tripped duplicate-address detection
# and New-NetIPAddress threw. DadTransmits 0 turns that detection off so the
# static always binds, and the assignment is retried rather than fatal.
staticIPScriptPs1 = pkgs.writeText "vmix-static-ip.ps1" ''
$a = $null
for ($n = 0; $n -lt 30; $n++) {
$a = Get-NetAdapter -Physical | Where-Object Status -eq 'Up' | Sort-Object ifIndex | Select-Object -First 1
if ($a) { break }
Start-Sleep -Seconds 2
}
if (-not $a) { Write-Output 'vmix: no adapter came up'; exit 1 }
$i = $a.ifIndex
Set-NetIPInterface -InterfaceIndex $i -Dhcp Disabled -DadTransmits 0 -ErrorAction SilentlyContinue
Remove-NetIPAddress -InterfaceIndex $i -AddressFamily IPv4 -Confirm:$false -ErrorAction SilentlyContinue
Remove-NetRoute -InterfaceIndex $i -AddressFamily IPv4 -Confirm:$false -ErrorAction SilentlyContinue
$ok = $false
for ($k = 0; $k -lt 5 -and -not $ok; $k++) {
try {
New-NetIPAddress -InterfaceIndex $i -IPAddress '${staticIP.address}' -PrefixLength ${toString staticIP.prefixLength} -DefaultGateway '${staticIP.gateway}' -ErrorAction Stop | Out-Null
$ok = $true
} catch {
Write-Output ('vmix: assign attempt ' + $k + ' failed: ' + $_.Exception.Message)
Start-Sleep -Seconds 2
Remove-NetIPAddress -InterfaceIndex $i -AddressFamily IPv4 -Confirm:$false -ErrorAction SilentlyContinue
}
}
if (-not $ok) { Write-Output 'vmix: could not set static address'; exit 1 }
Set-DnsClientServerAddress -InterfaceIndex $i -ServerAddresses ${staticDnsList}
New-NetFirewallRule -DisplayName 'ICMPv4 Echo' -Protocol ICMPv4 -IcmpType 8 -Direction Inbound -Action Allow -Profile Any -Enabled True -ErrorAction SilentlyContinue | Out-Null
Write-Output ('vmix: set ${staticIP.address} on ifIndex ' + $i)
'';
# Thin launcher, so the scheduled task has a cmd to point at.
staticIPScript = pkgs.writeText "vmix-static-ip.cmd" ''
@echo off
powershell -NoProfile -ExecutionPolicy Bypass -Command "$a = $null; for ($n = 0; $n -lt 30; $n++) { $a = Get-NetAdapter -Physical | Where-Object Status -eq 'Up' | Sort-Object ifIndex | Select-Object -First 1; if ($a) { break }; Start-Sleep -Seconds 2 }; if (-not $a) { Write-Output 'vmix: no adapter came up'; exit 1 }; $i = $a.ifIndex; Set-NetIPInterface -InterfaceIndex $i -Dhcp Disabled -ErrorAction SilentlyContinue; Remove-NetIPAddress -InterfaceIndex $i -AddressFamily IPv4 -Confirm:$false -ErrorAction SilentlyContinue; Remove-NetRoute -InterfaceIndex $i -AddressFamily IPv4 -Confirm:$false -ErrorAction SilentlyContinue; New-NetIPAddress -InterfaceIndex $i -IPAddress '${staticIP.address}' -PrefixLength ${toString staticIP.prefixLength} -DefaultGateway '${staticIP.gateway}' -ErrorAction Stop | Out-Null; Set-DnsClientServerAddress -InterfaceIndex $i -ServerAddresses ${staticDnsList}; Write-Output ('vmix: set ' + '${staticIP.address}' + ' on ifIndex ' + $i)" > C:\Windows\Temp\vmix-static-ip.log 2>&1
:: Answer pings. Windows blocks ICMP by default, which makes a box with a
:: fixed address look dead to everything that checks it the obvious way.
powershell -NoProfile -Command "New-NetFirewallRule -DisplayName 'ICMPv4 Echo' -Protocol ICMPv4 -IcmpType 8 -Direction Inbound -Action Allow -Profile Any -Enabled True | Out-Null" > nul 2>&1
powershell -NoProfile -ExecutionPolicy Bypass -File C:\vmix-static-ip.ps1 > C:\Windows\Temp\vmix-static-ip.log 2>&1
'';
dataDriveLetter = if dataDisk != null then (dataDisk.driveLetter or "D") else "D";
@ -413,7 +449,10 @@ in {
{ source = bootDataProfileScript; dest = "/vmix-data-profile.cmd"; }
]
++ lib.optional (writeFilter != null) { source = uwfConfigScript; dest = "/vmix-uwf-config.cmd"; }
++ lib.optional (staticIP != null) { source = staticIPScript; dest = "/vmix-static-ip.cmd"; };
++ lib.optionals (staticIP != null) [
{ source = staticIPScript; dest = "/vmix-static-ip.cmd"; }
{ source = staticIPScriptPs1; dest = "/vmix-static-ip.ps1"; }
];
# delayOobeRun: sysprep + shutdown — OOBE runs on real hardware
# generalize: sysprep + reboot into OOBE in the same QEMU session
auditScript = ''