前言

此方法已经过气,请参阅 WSL2 2.0 更新

网上为 WSL2 配置静态 IP 的方案有非常多,其中比较方便的一是其自带的桥接,二是这里的提前创建网络法。

然而,桥接实在是不太适合外部网络经常变化的环境(比如笔记本),而提前创建网络的方法则显得有些太过于 hacky 了:ICS 仿佛是 Windows 不愿暴露在外的“隐藏功能”,而且我也完全不知道他那网络的 GUID 是哪里来的,会不会存在向后兼容的问题。

所以,有没有什么相对“正常”一点的方法,能够为 WSL2 创建一个类似 VMware NAT 模式的网络,既做到固定网段,又做到不受外部网络环境影响呢?

这篇文章研究的就是这个问题。

步骤

先说步骤,最后再慢慢解释。

首先,打开设备管理器,选中你的电脑名,点击上方的操作,选择“添加过时硬件”:

选择手动安装:

选择网络适配器:

厂商选择 Microsoft ,找到其下的“Microsoft KM-TEST 环回适配器”:

完成后,你应该就可以在“网络连接”中看到它了,可以随便改个名字啥的(无所谓):

接下来,打开 Hyper-V 的虚拟交换机管理器,创建一个外部网络,将其桥接到刚刚创建的环回适配器上:

完成上一步后,“网络连接”中应该会出现一个新的名为“vEthernet(<你的外部网络名>)”的网络适配器,点开它的属性,为它设置一个 IPv4 地址,这个地址就是以后的宿主机 IP 了:

接下来,需要下载 DHCP Server ,这是一个不需要安装(甚至不需要管理员权限)的 portable 软件。这个软件分为两个部分,一个是 dhcpwiz.exe 用于创建配置文件,另一个是 dhcpsrv.exe 用于提供 DHCP 服务。将下载的压缩包解压到某个位置后双击 dhcpwiz.exe 启动“配置器”。启动后,选择刚刚创建的外部网络,点击下一页:

这一页无需配置,可以直接跳过:

在这一页中,你可以配置 IP 池、租期等信息,别忘了点开 Advanced ,并在其中配置 DNS 和网关地址,网关地址需要被配置为宿主机 IP,同样的, IP 池也得和宿主机 IP 在同一网段 :

↑ 对于上面这一步,如果你只是想要固定 IP 段,像我这样配置即可。但是如果你想要固定 WSL2 的 IP 地址,那么需要将 IP 池进行收缩,收缩到只有一个地址时,WSL2 的 IP 也就固定下来了 xD。

在这一页中,将对刚刚设置的内容进行保存。如果你之前没有配置过,直接点击 Write INI file 即可,如果之前配置过,则需要勾选 Overwrite existing file 来确保配置能够成功覆盖:

接下来这一页,因为我不想给管理员权限,于是什么都不干,直接点击完成:

↑ 至于开机启动,接下来自有办法。

那么配置过程就到此结束了,接下来点开 dhcpsrv.exe 来在我们的网络上启动 DHCP:

↑ 勾选 Don't show this window next time ,然后点击 Continue as tray app 让其最小化到托盘,此时 DHCP 服务已经在后台默默运行了。

接下来我们在不给管理员权限、不安装服务的前提下,为它配置一个超干净的开机启动。打开“运行”,输入 shell:startup ,此时资源管理器会弹出 “启动” 窗口,我们只需要在其中为 dhcpsrv.exe 创建一个快捷方式即可。

此时,网络本身算是配置好了,那就打开 .wslconfig 把 WSL2 接入进去吧:

C:\Users\<用户名>\.wslconfig

[wsl2]
networkingMode=bridged
vmSwitch=vNAT #这个要对应到上面在 Hyper-V 中创建的外部网络名称

好了,现在 wsl --shutdown 然后重新打开 WSL2 ,看一看自己的 IP 地址,诶,是不是在目标网段了?甚至还就是右下角弹出的 DHCP Server 分配的那个:

现在,宿主机和 WSL2 之间已经可以正常通信了,但是,WSL2 还不能访问外网,这是因为现在的网关是宿主机,而宿主机却并没有转发这些数据包的能力。

接下来,我们要为宿主机配置这种能力,上 NAT !

这一步,很遗憾的,我没有找到可以用的 GUI ,只能上 Powershell 了,记得给管理员权限:

New-NetNat -Name NAT -InternalIPInterfaceAddressPrefix 192.168.114.0/24

这里指定的内部网段要与上面分配的网段相对应。

好了,现在在 WSL2 中 ping 一下,网已经通了:

如果它没有通,那么你就得检查一下防火墙了,比如我用的卡巴斯基就得把这个虚拟网络设置为“本地网络”或者“受信任网络”才能正常使用 NAT 。

完成了,我们得到了一个类似 VMware NAT 模式的 WSL2 网络。

解释

接下来,解释一下这个过程,以及我为什么这么做。

首先的首先,最根本的原因,WSL2 是基于 Hyper-V 的,而 Hyper-V 压根就不支持创建 NAT 模式的网络。嗯,它自带的 Default Switch 是一个 NAT 模式的,甚至自带 DHCP 的网络,为 WSL2 自动创建的名为 WSL 的网络也是如此。但,它们都是基于 ICS 服务的,在开机时/开机后首次打开 WSL2 时会被重新创建,因而地址全都在乱跳。ICS 几乎没有什么比较正常的手动配置方法,或者说,微软似乎很不情愿让你感知到它们的存在,它们不仅不在任务管理器中显示,也不在“网络连接”中显示,但它们确确实实是网络😂。文章“前言”中提到的那种直接调用 api 的方式算是一种非常 hacky 的 ICS 配置方法,由于没什么文档,我也没法在此处深入探究了。

嗯,Windows 既有 NAT 功能又有 DHCP 功能,但就是不让你用,气不气?

按照上面我们的搭建方式,其得到网络的拓扑结构是这样的:

↑ 图中的命名就按上文中的来。

我们最开始创建的回环网卡,被 Hyper-V 的“外部网络”交换机一分为二,分别连接到了宿主机和 WSL2 中,这是通信的物理基础。你可能会问,为什么我不直接创建一个“内部网络”,这样不也可以得到一对相互连接的虚拟网卡吗?是的,对于一般的 Hyper-V 虚拟机,这样确实是可以的。但是,WSL2 的 networkingMode=bridged 只支持类型为“外部网络”的网络,“内部网络”压根就接不进去🤣。所以,这算是一种迫不得已的 workaround 。不得不说,微软在优雅的地方真的能做到非常优雅,但是这种屑的地方又能做到非常蛋疼。

这样建立的网络仅仅只是在数据链路层完成了配置,网络层的配置并没有完成。此时其实在 WSL2 中手动配置一个同网段的 IP 地址是能够通信的,但是对于 WSL2 这种东西来说,从外部配置远比从内部配置来的简单和稳定,这便是为什么需要搭建一个 DHCP 服务让 WSL2 能够自动获取 IP 地址,或者说,我只是在复现 ICS 自带的功能罢了。Windows 完全是自带了 DHCP 服务能力的,比如 ICS 自身具备的那个,又或者 Windows Server 系统中提供的那个,但,对于我们这些普通用户,微软就是不让你用,气不气?又是一次迫不得已,选择了 DHCP Server 。

接下来说一说 NAT ,这个东西的文档是在这里找到的,我取了其中创建 NAT 的一行命令,因为这个东西是真的找不到 GUI 。另外,这个 NAT 是不支持 IPv6 的(微软:怎么样就问你服不服?)。

问题

首先是 IPv6 ,上面也说到了,New-NetNat 创建的 NAT 不支持 IPv6 ,我们使用的 DHCP Server 也不支持 DHCPv6 。

其次,假如宿主机采用的是拨号上网,那么我们这样创建的网络是无法正常连接外网的,表现为 ping 都正常,但是 https 全部 insecure ,tls 握手直接爆炸。这并不是我们的配置哪里有问题,而是 Hyper-V 的 bug ,不信你试试 Hyper-V 自带的 Default Switch ,在拨号上网环境下也是这个鸟样。真有你的啊微软。

↑ 但是 WSL2 自动创建的那个名为 WSL 的网络并没有这个问题,可能是修复了但又没有完全修复(翻了翻 issue 发现以前好像也有这个问题,但好像被修复了,那为什么普通的 Hyper-V 就没修???)。

后记

由于拨号上网是刚需,这篇文章中的经验对我来说已经是答辩了。如果它能对你有所帮助,那我的努力也算没有白费🤣。