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:
parent
d94c576df2
commit
5251bf7290
1 changed files with 44 additions and 5 deletions
|
|
@ -93,12 +93,48 @@ in
|
||||||
# interface arrives DHCP-managed -- assigning an address without turning DHCP
|
# 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
|
# off first does not stick, which is how a VM meant to be at a fixed address
|
||||||
# ended up holding a lease instead.
|
# 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" ''
|
staticIPScript = pkgs.writeText "vmix-static-ip.cmd" ''
|
||||||
@echo off
|
@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
|
powershell -NoProfile -ExecutionPolicy Bypass -File C:\vmix-static-ip.ps1 > 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
|
|
||||||
'';
|
'';
|
||||||
|
|
||||||
dataDriveLetter = if dataDisk != null then (dataDisk.driveLetter or "D") else "D";
|
dataDriveLetter = if dataDisk != null then (dataDisk.driveLetter or "D") else "D";
|
||||||
|
|
@ -413,7 +449,10 @@ in {
|
||||||
{ source = bootDataProfileScript; dest = "/vmix-data-profile.cmd"; }
|
{ source = bootDataProfileScript; dest = "/vmix-data-profile.cmd"; }
|
||||||
]
|
]
|
||||||
++ lib.optional (writeFilter != null) { source = uwfConfigScript; dest = "/vmix-uwf-config.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
|
# delayOobeRun: sysprep + shutdown — OOBE runs on real hardware
|
||||||
# generalize: sysprep + reboot into OOBE in the same QEMU session
|
# generalize: sysprep + reboot into OOBE in the same QEMU session
|
||||||
auditScript = ''
|
auditScript = ''
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue