Public/Ics/Reset-IcsSharing.ps1
|
<#
.NOTES Do not run this file directly. Dot-sourced by provision.ps1 and by Assert-HostNetworkPreflight (the auto-repair path). #> # --------------------------------------------------------------------------- # Reset-IcsSharing # Programmatic equivalent of the GUI "uncheck -> re-check the Sharing # checkbox on the WiFi adapter" gesture: the canonical kick that # makes ICS rebind its DNS proxy + NAT mappings + auto-generated # firewall rules. Uses the HNetCfg.HNetShare COM API - the same # surface the Sharing tab calls into - so it tears down and rebuilds # the persisted ICS config, not just bounces the SharedAccess # service. # # Why this matters: Restart-Service SharedAccess re-reads the same # persisted state, including whatever broke it. The COM teardown # (DisableSharing) wipes the "shared" attribute on the WAN adapter, # releases ICS's UDP/53 listener on the LAN adapter, and clears # NAT state - re-EnableSharing rebuilds all of that from scratch. # # Failure mode this defends against (seen 2026-06): ICS DNS proxy # answers UDP/53 queries with TCP RSTs to the VM, host-side # Resolve-DnsName -Server 192.168.137.1 returns "An existing # connection was forcibly closed". Toggling sharing fixes it # every time; a service restart does not. # --------------------------------------------------------------------------- function Reset-IcsSharing { [CmdletBinding()] param( # WAN-side interface (the one with internet). e.g. 'Wi-Fi'. # This is the connection whose Sharing tab checkbox we are # effectively toggling. [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string] $WanInterfaceName, # LAN-side interface (the ICS-served network). e.g. # 'vEthernet (ExternalSwitch-Shared)'. This is the # connection ICS shares INTO. [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string] $LanInterfaceName ) # HNetCfg.HNetShare is the COM surface Windows uses for ICS # itself. EnumEveryConnection returns each NetConnection; for # each, NetConnectionProps gives us the visible name to match # on, and INetSharingConfigurationForINetConnection gives us # the Enable/Disable methods. $hnetcfg = New-Object -ComObject HNetCfg.HNetShare $wanCfg = $null $lanCfg = $null foreach ($conn in $hnetcfg.EnumEveryConnection) { $name = $hnetcfg.NetConnectionProps.Invoke($conn).Name if ($name -eq $WanInterfaceName) { $wanCfg = $hnetcfg.INetSharingConfigurationForINetConnection.Invoke($conn) } elseif ($name -eq $LanInterfaceName) { $lanCfg = $hnetcfg.INetSharingConfigurationForINetConnection.Invoke($conn) } } if (-not $wanCfg) { throw "Reset-IcsSharing: WAN interface '$WanInterfaceName' not found via HNetCfg. Run Get-NetAdapter to confirm the name." } if (-not $lanCfg) { throw "Reset-IcsSharing: LAN interface '$LanInterfaceName' not found via HNetCfg. Run Get-NetAdapter to confirm the name." } # Tear down first (matches GUI uncheck). Idempotent - DisableSharing # is safe even when sharing was already off, so we don't bother # branching on SharingEnabled. if ($wanCfg.SharingEnabled) { $wanCfg.DisableSharing() } if ($lanCfg.SharingEnabled) { $lanCfg.DisableSharing() } # Rebuild. The constants ICSSHARINGTYPE are: # 0 = ICSSHARINGTYPE_PUBLIC (the WAN side, gets to share) # 1 = ICSSHARINGTYPE_PRIVATE (the LAN side, gets shared into) $wanCfg.EnableSharing(0) $lanCfg.EnableSharing(1) } |