Hyper-V NAT映射端口到Host

于 2022-06-26 发布

起因是想把一些服务搬到虚拟机里,因为Windows的”服务”体验比较差。为了开机可用,不想使用VBox,所以选择Hyper-V。Hyper-V的桥接网络有限问题,会导致Windows自己上不了网。因此,使用NAT+映射端口的方式来完成。

首先参照这篇文章新建一个NAT虚拟交换机。原文是英文系统的名称,本文使用中文系统环境,所以有些内容会对应更改。命令都是用管理员权限的Powershell运行的。名字是NATSwitch,类型是内部:

1
New-VMSwitch -SwitchName "NATSwitch" -SwitchType Internal

原文Windows(下文统一称为主机)的地址是192.168.0.1,但是我的物理LAN网段和它冲突了,所以主机的IP地址设置为192.168.2.1,网络段用192.168.2.0/24,虚拟交换机的名称vEthernet (NATSwitch)。先配置主机的网络:

1
New-NetIPAddress -IPAddress 192.168.2.1 -PrefixLength 24 -InterfaceAlias "vEthernet (NATSwitch)"

然后配置虚拟交换机的网络,注意Name要跟上面的匹配:

1
New-NetNAT -Name "NATNetwork" -InternalIPInterfaceAddressPrefix 192.168.2.0/24

虚拟机使用的是Archlinux,启动之前在设置里把网络适配器更改为NATSwitch

在主机上配置NATSwitch,先给这个名字是”arch”的虚拟机设置一个静态IP(因为DHCP没法用)。虚拟机的IP是192.168.2.2。下面这个要保存到一个.ps1文件里,然后在管理员权限的Powershell里运行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
Function Set-VMNetworkConfiguration {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory=$true,
                Position=1,
                ParameterSetName='DHCP',
                ValueFromPipeline=$true)]
        [Parameter(Mandatory=$true,
                Position=0,
                ParameterSetName='Static',
                ValueFromPipeline=$true)]
        [Microsoft.HyperV.PowerShell.VMNetworkAdapter]$NetworkAdapter,

        [Parameter(Mandatory=$true,
                Position=1,
                ParameterSetName='Static')]
        [String[]]$IPAddress=@(),

        [Parameter(Mandatory=$false,
                Position=2,
                ParameterSetName='Static')]
        [String[]]$Subnet=@(),

        [Parameter(Mandatory=$false,
                Position=3,
                ParameterSetName='Static')]
        [String[]]$DefaultGateway = @(),

        [Parameter(Mandatory=$false,
                Position=4,
                ParameterSetName='Static')]
        [String[]]$DNSServer = @(),

        [Parameter(Mandatory=$false,
                Position=0,
                ParameterSetName='DHCP')]
        [Switch]$Dhcp
    )

    $VM = Get-WmiObject -Namespace 'root\\virtualization\\v2' -Class 'Msvm_ComputerSystem' | Where-Object { $_.ElementName -eq $NetworkAdapter.VMName } 
    $VMSettings = $vm.GetRelated('Msvm_VirtualSystemSettingData') | Where-Object { $_.VirtualSystemType -eq 'Microsoft:Hyper-V:System:Realized' }    
    $VMNetAdapters = $VMSettings.GetRelated('Msvm_SyntheticEthernetPortSettingData') 

    $NetworkSettings = @()
    foreach ($NetAdapter in $VMNetAdapters) {
        if ($NetAdapter.Address -eq $NetworkAdapter.MacAddress) {
            $NetworkSettings = $NetworkSettings + $NetAdapter.GetRelated("Msvm_GuestNetworkAdapterConfiguration")
        }
    }

    $NetworkSettings[0].IPAddresses = $IPAddress
    $NetworkSettings[0].Subnets = $Subnet
    $NetworkSettings[0].DefaultGateways = $DefaultGateway
    $NetworkSettings[0].DNSServers = $DNSServer
    $NetworkSettings[0].ProtocolIFType = 4096

    if ($dhcp) {
        $NetworkSettings[0].DHCPEnabled = $true
    } else {
        $NetworkSettings[0].DHCPEnabled = $false
    }

    $Service = Get-WmiObject -Class "Msvm_VirtualSystemManagementService" -Namespace "root\\virtualization\\v2"
    $setIP = $Service.SetGuestNetworkAdapterConfiguration($VM, $NetworkSettings[0].GetText(1))

    if ($setip.ReturnValue -eq 4096) {
        $job=[WMI]$setip.job 

        while ($job.JobState -eq 3 -or $job.JobState -eq 4) {
            start-sleep 1
            $job=[WMI]$setip.job
        }

        if ($job.JobState -eq 7) {
            write-host "Success"
        }
        else {
            $job.GetError()
        }
    } elseif($setip.ReturnValue -eq 0) {
        Write-Host "Success"
    }
}

Get-VMNetworkAdapter -VMName arch -Name "网络适配器" | Set-VMNetworkConfiguration -IPAddress 192.168.2.2 -Subnet 255.255.255.0 -DNSServer 114.114.114.114 -DefaultGateway 192.168.2.1

接下来启动虚拟机,静态IP可以自己搜索怎么配置,我用dhcpcd就是添加以下到/etc/dhcpcd.conf:

1
2
3
4
interface eth0
static ip_address=192.168.2.2/24
static routers=192.168.2.1
static domain_name_servers=114.114.114.114

关于NAT映射端口,参照这个文章的以下格式:

1
Add-NetNatStaticMapping -ExternalIPAddress "0.0.0.0/24" -ExternalPort 8118 -Protocol UDP -InternalIPAddress "192.168.2.2" -InternalPort 8118 -NatName NATNetwork

目录