前言
在学校生活时经常有一种需求:访问某些网站(如校内系统)时需要使用校园网,访问另一些网站(如Steam、Github等)校园网很慢,需要使用手机浏览器等其他网络加速。此时我就觉得如果能同时使用两个网络该多好。
本文就介绍如何用PowerShell实现这个功能(需要Windows平台,版本XP及以上)。
原理
首先需要明确,同一张网卡是无法同时连接两个网络的(除非同时使用多个代理达到这种效果)。幸运的是,现在绝大多数电脑都同时拥有有线和无线两张网卡,所以可以实现这个效果。
其次,一般应用程序无法直接指定网卡上网(例如虚拟机软件除外),不过我们有代替的方法:设置路由表。
路由表的基本原理是:要求IP的某些网段,走指定的网关。而网关又和网络有密不可分的联系,从而实现隐式地指定网卡。
查看电脑信息
首先可以同时连上有线和无线网,方便查看
网络适配器
一台电脑上会有多个网络适配器,其中有物理网卡,也有虚拟网卡。我们要找出其中物理网卡里的有线和无线网卡:首先键入ipconfig
,可以对此有一个大致印象:
十分明显地看到,有好多适配器。但比如说名字里带virtual的,或者vEthernet这种显然是虚拟网卡。剩下很快就能发现有线和无线网络适配器分别是:以太网和WLAN(不同电脑可能名字不一样,连接状态也不尽相同)。
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 PS C:\Users\poker> ipconfig Windows IP 配置 以太网适配器 以太网: 连接特定的 DNS 后缀 . . . . . . . : hgd 本地链接 IPv6 地址. . . . . . . . : XXXX::XXXX:XXXX:XXXX:XXXX%XX IPv4 地址 . . . . . . . . . . . . : XXX.XXX.XXX.XXX 子网掩码 . . . . . . . . . . . . : XXX.XXX.XXX.XXX 默认网关. . . . . . . . . . . . . : XXX.XXX.XXX.XXX 无线局域网适配器 本地连接* 5: 媒体状态 . . . . . . . . . . . . : 媒体已断开连接 连接特定的 DNS 后缀 . . . . . . . : 无线局域网适配器 本地连接* 6: 媒体状态 . . . . . . . . . . . . : 媒体已断开连接 连接特定的 DNS 后缀 . . . . . . . : 无线局域网适配器 WLAN: 连接特定的 DNS 后缀 . . . . . . . : 本地链接 IPv6 地址. . . . . . . . : XXXX::XXXX:XXXX:XXXX:XXXX%XX IPv4 地址 . . . . . . . . . . . . : XXX.XXX.XXX.XXX 子网掩码 . . . . . . . . . . . . : XXX.XXX.XXX.XXX 默认网关. . . . . . . . . . . . . : XXX.XXX.XXX.XXX 以太网适配器 蓝牙网络连接 3: 媒体状态 . . . . . . . . . . . . : 媒体已断开连接 连接特定的 DNS 后缀 . . . . . . . :
接下来使用Get-NetIPConfiguration
指令,就可以看到物理网卡的信息了:
其中我们要注意Gateway的项,这就是网关。
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 PS C:\Users\poker> Get-NetIPConfiguration InterfaceAlias : 以太网 InterfaceIndex : 18 InterfaceDescription : Realtek PCIe GbE Family Controller NetProfile.Name : XXXXXXXXXXXXXXX IPv4Address : XXX.XXX.XXX.XXX IPv6DefaultGateway : IPv4DefaultGateway : XXX.XXX.XXX.XXX DNSServer : XXX.XXX.XXX.XXX XXX.XXX.XXX.XXX XXX.XXX.XXX.XXX XXX.XXX.XXX.XXX XXX.XXX.XXX.XXX XXX.XXX.XXX.XXX InterfaceAlias : WLAN InterfaceIndex : 14 InterfaceDescription : Intel(R) Wi-Fi 6 AX200 160MHz NetProfile.Name : XXXXXXXXXXXXXXX IPv4Address : XXX.XXX.XXX.XXX IPv6DefaultGateway : IPv4DefaultGateway : XXX.XXX.XXX.XXX DNSServer : XXX.XXX.XXX.XXX InterfaceAlias : 蓝牙网络连接 3 InterfaceIndex : 10 InterfaceDescription : Bluetooth Device (Personal Area Network) #3 NetAdapter.Status : Disconnected
其他相关指令:netsh interface show interface
、Get-NetAdapter
,感兴趣可以看看。
路由
使用Get-NetRoute
指令(或route print
)可以查看路由表:
表中每一条代表一个路由,注意他所有的属性:
ifIndex
(interface
index)(接口序号):即和上图同名的属性。
DestinationPrefix
(目标前缀):即指定的网段,属于这个网段的会找对应的网关(其中前面四段句点分开的是IP,斜杠后的是子网掩码从左往右数1的位数)。
NextHop
(下一跳):即指定的网关。
两个Metric
(跃点):大致表示这个路由的优先级,数字越小越优先。
PolicyStore
(存储策略):表示这是临时路由(ActiveStore
)还是永久路由(PersistentStore
),其中临时路由会在重启后删除重设。
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 PS C:\Users\poker> Get-NetRoute ifIndex DestinationPrefix NextHop RouteMetric ifMetric PolicyStore ------- ----------------- ------- ----------- -------- ----------- 10 255.255.255.255/32 0.0.0.0 256 65 ActiveStore 13 255.255.255.255/32 0.0.0.0 256 25 ActiveStore 11 255.255.255.255/32 0.0.0.0 256 25 ActiveStore 18 255.255.255.255/32 0.0.0.0 256 25 ActiveStore 14 255.255.255.255/32 0.0.0.0 256 50 ActiveStore 1 255.255.255.255/32 0.0.0.0 256 75 ActiveStore 10 224.0.0.0/4 0.0.0.0 256 65 ActiveStore 13 224.0.0.0/4 0.0.0.0 256 25 ActiveStore 11 224.0.0.0/4 0.0.0.0 256 25 ActiveStore 18 224.0.0.0/4 0.0.0.0 256 25 ActiveStore 14 224.0.0.0/4 0.0.0.0 256 50 ActiveStore 1 224.0.0.0/4 0.0.0.0 256 75 ActiveStore 14 192.168.1.255/32 0.0.0.0 256 50 ActiveStore 14 192.168.1.226/32 0.0.0.0 256 50 ActiveStore 14 192.168.1.0/24 0.0.0.0 256 50 ActiveStore 18 172.19.73.255/32 0.0.0.0 256 25 ActiveStore 18 172.19.73.29/32 0.0.0.0 256 25 ActiveStore 18 172.19.73.0/24 0.0.0.0 256 25 ActiveStore 1 127.255.255.255/32 0.0.0.0 256 75 ActiveStore 1 127.0.0.1/32 0.0.0.0 256 75 ActiveStore 1 127.0.0.0/8 0.0.0.0 256 75 ActiveStore 14 0.0.0.0/0 XXX.XXX.XXX.XXX 0 50 ActiveStore 18 0.0.0.0/0 XXX.XXX.XXX.XXX 0 25 ActiveStore 10 ff00::/8 :: 256 65 ActiveStore 13 ff00::/8 :: 256 25 ActiveStore 11 ff00::/8 :: 256 25 ActiveStore 18 ff00::/8 :: 256 25 ActiveStore 14 ff00::/8 :: 256 50 ActiveStore 1 ff00::/8 :: 256 75 ActiveStore 13 fe80::ddf5:310e:d09b:1326/128 :: 256 25 ActiveStore 10 fe80::8b75:5968:ee8a:42d6/128 :: 256 65 ActiveStore 18 fe80::5c8c:97bf:72c:71e9/128 :: 256 25 ActiveStore 11 fe80::4fbb:4f7c:2d3c:426a/128 :: 256 25 ActiveStore 14 fe80::3c65:d5a8:6d1:95cd/128 :: 256 50 ActiveStore 10 fe80::/64 :: 256 65 ActiveStore 13 fe80::/64 :: 256 25 ActiveStore 11 fe80::/64 :: 256 25 ActiveStore 18 fe80::/64 :: 256 25 ActiveStore 14 fe80::/64 :: 256 50 ActiveStore 1 ::1/128 :: 256 75 ActiveStore 18 ::/0 XXXX::XXXX:XXXX:XXXX:XXXX 256 25 ActiveStore
注意NextHop
不为空的路由(一般Metric
也最小),这就是有线和无线网默认的路由,我们新建路由时为避免冲突,要先删除这个(不用担心失误,重启后会恢复)
这些路由前面网段都是0.0.0.0/0(或::/0),表示全网段都可以使用这个路由。
从Metric
可以发现,绝大多数电脑同时连有线和无线网时,都是有线网的Metric
比较小,这也是为什么路由总是会优先选择有线网连接。Windows默认开启了AutomaticMetric
,会按照带宽网速自动设定Metric
,有需要可以去设置关闭
设备地址
输入指令Get-CimInstance Win32_NetworkAdapterConfiguration | Select-Object Description, SettingID
,可以看到网卡以及对应的Guid
我们将来可以通过这个Guid,从注册表HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Network\{4D36E972-E325-11CE-BFC1-08002BE10318}\{对应设备Guid}\Connection
的PnPInstanceId
项判断是不是物理网卡(值以PCI
开头是物理网卡,BTH
开头是蓝牙网卡,其他是虚拟网卡)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 PS C:\Users\poker> Get-CimInstance Win32_NetworkAdapterConfiguration | Select-Object Description, SettingID Description SettingID ----------- --------- Microsoft Kernel Debug Network Adapter {2A714150-362D-497E-AC8B-A9F963B02478} Realtek PCIe GbE Family Controller {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} Intel(R) Wi-Fi 6 AX200 160MHz {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} Microsoft Wi-Fi Direct Virtual Adapter {33A4093B-99C7-478F-AF06-FD0A698B1FF5} Microsoft Wi-Fi Direct Virtual Adapter {F1F71D7B-D35B-4C65-8A91-DC94EE7C0A2E} WAN Miniport (SSTP) {FA29F161-184C-4773-898D-A104510EE7F5} WAN Miniport (IKEv2) {9859D6F0-2451-47BB-8748-7B1C5C542200} WAN Miniport (L2TP) {EF4DCC46-BE1D-4FC7-8CFB-CF00F37AB9C8} WAN Miniport (PPTP) {5E553B27-3FA5-445B-8A15-8249DF31AF3F} WAN Miniport (PPPOE) {91899BE4-723E-43A6-8006-6BB2AFA4FC32} WAN Miniport (IP) {CD5DD751-23AA-44F2-940F-4B565365892D} WAN Miniport (IPv6) {DC0056A3-472D-4178-901C-131B8D8261E9} WAN Miniport (Network Monitor) {218400DD-182F-4BC4-A266-EBDB68EDD6DF} Bluetooth Device (Personal Area Network) {2CA478BD-DADE-48B3-949B-66A882E18A72}
实现思路
接下来就是实现环节了,虽然指定网关听起来很容易。但如果切换网络之后,不仅网关可能会变,而且接口别名(InterfaceAlias)也会变,到时候再调整比较麻烦。
如果可以指定网卡,并指定网段就很方便了。实际上我们确实可以这样做,因为Get-NetIPConfiguration
(或Get-NetAdapter
)可以看出来:接口描述(InterfaceDescription
)、接口别名(InterfaceAlias
)和网关(Gateway
)几乎是一一对应的关系,所以我们可以通过PowerShell(或C/C++)从接口描述查找网关,并设置路由,就可以达成目的。
至于网段分配,一般校园网都是连续的网段,比如说172.18.XXX.XXX都是校园网IP,那么就设置目标前缀(DestinationPrefix)为172.18.0.0/16,表示子网掩码是255.255.0.0,即前16位是固定的,后面可以变化。将这段路由优先级设最高,然后再设置剩下的路由(直接用0.0.0.0/0)全部走另一个网络(优先级第二高)。
另外还可以结合Proxifier等软件管理,如用Proxifier强大的代理筛选功能将校园网所有IP或程序都代理到某一台学校的服务器上,这样路由规则就可以只要指定一个IP了(如172.18.6.57/32)。
脚本代码
需要使用PowerShell7及以上运行。
在使用的时候,只需要按照需要修改# Main
部分。
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 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 class NetAdapterInfo { [guid ] $SettingID [string ] $Description [string ] $DefaultIPGateway NetAdapterInfo ( [guid ] $settingID , [string ] $description , [string ] $defaultIPGateway ) { $this .SettingID = $settingID $this .Description = $description $this .DefaultIPGateway = $defaultIPGateway } } function Test-PhysicalNic { param ( [Parameter ( Mandatory , ValueFromPipeline )] [NetAdapterInfo ] $Info ) $str = Get-ItemProperty ` -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Network\{4D36E972-E325-11CE-BFC1-08002BE10318}\{$ ($Info .SettingID)}\Connection" ` -Name PnPInstanceId $str .PnPInstanceId.StartsWith("PCI" ) } function Get-PhysicalNics { Get-CimInstance ` -Class Win32_NetworkAdapterConfiguration ` | ForEach-Object { [NetAdapterInfo ]::new( $_ .SettingID, $_ .Description, $_ .DefaultIPGateway ) } ` | Where-Object { Test-PhysicalNic $_ } } function Remove-AdapterRoute { param ( [string ] $DestinationPrefix = "0.0.0.0/0" ) Remove-NetRoute $DestinationPrefix } function Set-AdapterRoute { param ( [NetAdapterInfo []] $List , [string ] $AdapterDescription , [string ] $DestinationPrefix = "0.0.0.0/0" , [UInt16 ] $RouteMetric = 0 , [bool ] $PersistentStore = $false ) $Private:NextHop = ($List | Where-Object Description -EQ $AdapterDescription )[0 ].DefaultIPGateway New-NetRoute ` $DestinationPrefix ` -InterfaceAlias $AdapterDescription ` -NextHop $Private:NextHop ` -PolicyStore ($PersistentStore ? "PersistentStore" : "ActiveStore" ) } function Set-AdapterRoute { param ( [NetAdapterInfo []] $List , [string ] $InterfaceDescription , [string ] $DestinationPrefix = "0.0.0.0/0" , [string ] $IsIpv4 = $true , [UInt16 ] $RouteMetric = 0 , [bool ] $PersistentStore = $false ) $InterfaceAlias = (Get-NetAdapter | Where-Object Name -EQ $InterfaceDescription )[0 ].Name $gateways = ($List | Where-Object Description -EQ $InterfaceAlias )[0 ].DefaultIPGateway if ($gateways -EQ "" ) { return } foreach ($gateway in $gateways .Split(' ' )) { if (($IsIpv4 -and ([System.Net.IPAddress ]::Parse($gateway ).AddressFamily -EQ [System.Net.Sockets.AddressFamily ]::InterNetwork)) -or ` (!$IsIpv4 -and ([System.Net.IPAddress ]::Parse($gateway ).AddressFamily -EQ [System.Net.Sockets.AddressFamily ]::InterNetworkV6))) { New-NetRoute ` $DestinationPrefix ` -InterfaceAlias $InterfaceAlias ` -NextHop $gateway ` -PolicyStore ($PersistentStore ? "PersistentStore" : "ActiveStore" ) ` -RouteMetric $RouteMetric return } } } $info = Get-PhysicalNics Remove-AdapterRoute Remove-AdapterRoute ::/0 Set-AdapterRoute $info "Realtek PCIe GbE Family Controller" 172.18 .6.57 /32 Set-AdapterRoute $info "Intel(R) Wi-Fi 6 AX200 160MHz" 0.0 .0.0 /0
我的代码仓库里包含完整PowerShell脚本和部分C++方法。