引入

windows 开启 Hyper-V 后,会创建一个如图所示的默认虚拟 Switch 用于向虚拟机提供网络:
DefaultSwitch

上图是 Hyper-V 管理器的界面,如图所示,它指示这个默认网络使用 NAT 向虚拟机提供网络,事实上,它使用的是 NAT 的丐版 —— ICS。

究竟两者有什么区别呢?在 Windows 2000 的帮助文件中 ICS 和 NAT 分别叫做 Internet 的转换连接和路由连接,其实说白了 ICS 就是 NAT 的简化版,使用 ICS 无需理解 IP 地址和路由方面的一些知识,并且提供一种局域网中使用 Windows 2000 路由器共享 Internet 的简化配置,不过 ICS 可能不允许局域网和 Internet 主机之间所有的 IP 通信,如《暗黑破坏神》一类的多玩家游戏、实时通讯及其他对等服务,如果在公用 Internet上使用专用地址或同时使用同一端口号,这些应用程序就会中止。而 NAT 的配置需要有关于IP地址和路由配置方面的知识,它的配置比 ICS 要复杂,它允许在局域网和 Internet 主机间所有的IP通信。此外,ICS 只能使用一个合法的公用IP地址,而 NAT 可以通过配置地址池的方式使用ISP提供的多个合法的公用IP地址供客户机共享。

此外,Hyper-V HCN 配置端口映射,要求 Switch 的类型为真正的 NAT 类型,这就带出了这篇文章 —— 如何利用 Hyper-V HCN 成功配置端口映射。

创建 Switch

因为默认的网络的类型为 ICS,因此直接在现有网络上进行配置成为了奢望,需要为此重新创建一个网络。参考HCN-JSON-Guide进行配置。

一个可用的示例配置如下

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
"SchemaVersion": {
"Major": 2,
"Minor": 0
},
"Name" : "NATTest",
"Type" : "NAT",
"Ipams" : [
{
"Type" : "Dhcp",
"Subnets" : [
{
"IpAddressPrefix" : "192.168.100.0/24",
"Routes" : [
{
"NextHop" : "192.168.100.1",
"DestinationPrefix" : "0.0.0.0/0"
}
]
}
]
}
],
"MacPool": {
"Ranges" : [
{
"EndMacAddress": "00-15-5D-52-CF-FF",
"StartMacAddress": "00-15-5D-52-C0-00"
}
]
}
  1. SchemaVersion 为 JSON SchemaVersion 版本
  2. Name 为将要创建的虚拟 Switch 名字。
  3. Type 为将要创建的虚拟 Switch 类型,这里要写为 NAT。才能让下文的 EndPoint 成功生效。
  4. Ipams 配置子网参数。
  5. MacPool 配置该虚拟 Switch 可为连接到其的 EndPoint 的 MacAddress。

配置 EndPoint

创建好 NAT 类型的 Switch 后,就可以创建 EndPoint 连接到该 Switch,虚拟机则连接到该 EndPoint。同样参考HCN-JSON-Guide进行配置。

一个可用的示例配置如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
"SchemaVersion": {
"Major": 2,
"Minor": 0
},
"Owner": "Sample",
"HostComputeNetwork": "AD7CE019-F3DB-463F-9E57-9164583F6C03",
"MacAddress": "00-15-5D-52-C1-89",
"Policies": [
{
"Type" : "PortMapping",
"Settings" : {
"Protocol": 6,
"InternalPort" : 22,
"ExternalPort" : 22
}
}
]
  1. HostComputeNetwork 填写 NAT Switch 的 GUID
  2. MacAddress 填写为 NAT Switch 提供的 MacPool 中的任意一个 MacAddress
  3. 通过 Policies 的 PortMapping 配置端口映射
  4. Protocol 指定端口映射在哪个协议上进行,这里的 6 为 TCP
  5. InternalPort 指定虚拟机暴露的端口
  6. ExternalPort 指定Host暴露的端口

效果是,虚拟机 22 端口被转换到了 Host 的 22 端口上。

组装这些配置

这里利用微软 HCN 的 API 即可完成

  1. HcnCreateNetwork 创建该 NAT 网络并启动。
  2. HcnCreateEndpoint 创建该 EndPoint 并连接到参数所传入的网络上,并启动。
1
2
3
4
5
6
7
8
9
10
11
12
unique_hcn_network virtual_net_work;
wil::unique_cotaskmem_string result;
EXPECT_EQ(S_OK, HcnCreateNetwork(network_guid, kNetworkSettings.c_str(), virtual_net_work.put(), result.put()));
// Transform GUID format EndpointId
GUID endpoint_id_guid;
StringToGuid(endpoint_id, endpoint_id_guid);
// Configure endPoint
unique_hcn_endpoint virtual_end_point;
// Create endPoint in network
EXPECT_EQ(S_OK, HcnCreateEndpoint(virtual_net_work.get(), endpoint_id_guid,
end_point_setting.c_str(), virtual_end_point.put(),
result.put()));

具体可以参考,HCN-API-Sample学习。

写在最后

最近在学习 Hyper-V 的 HCN 、HCS、HDV,个人认为这是一个比较有潜力的虚拟化实现方案,至少在 API 封装上微软将其做到了简单易用,简单组装一些 API,学习其 JSON Schema 即可用 Hyper-V 启用虚拟机。

这里有一些参考实现:

  1. Hcsshim,GO 语言实现,较完备。
  2. Nanabox,C++ 实现。简单易用。