获取静态的接口及其 DNS 服务器(未分配 dhcp)

Getting interfaces and their DNS servers that are STATIC (not dhcp allocated)

我正在尝试通过静态(由用户放置)的 WMI 获取网络接口的 DNS 服务器。我有这个有效的脚本,当然除了静态部分:

Get-WmiObject Win32_NetworkAdapterConfiguration | Where-Object {$_.DNSServerSearchOrder -ne $null} | Select DnsServerSearchOrder,Index,InterfaceIndex

这会导致如下输出:

DnsServerSearchOrder   Index InterfaceIndex
--------------------   ----- --------------
{192.168.122.1}            1              6
{1.1.1.1}                  2             10

192.168.122.1 的接口将其 DNS 设置为 DHCP,因此该值对我不利。如何过滤掉 dns 不是静态的接口?有什么想法吗?

netsh interface ip show config:

Configuration for interface "Ethernet"
    DHCP enabled:                         Yes
    IP Address:                           192.168.122.130
    Subnet Prefix:                        192.168.122.0/24 (mask 255.255.255.0)
    Default Gateway:                      192.168.122.1
    Gateway Metric:                       0
    InterfaceMetric:                      35
    DNS servers configured through DHCP:  192.168.122.1
    Register with which suffix:           Primary only
    WINS servers configured through DHCP: None

Configuration for interface "Ethernet 2"
    DHCP enabled:                         Yes
    IP Address:                           10.0.0.17
    Subnet Prefix:                        10.0.0.0/24 (mask 255.255.255.0)
    Default Gateway:                      10.0.0.1
    Gateway Metric:                       0
    InterfaceMetric:                      35
    Statically Configured DNS Servers:    1.1.1.1
    Register with which suffix:           Primary only
    WINS servers configured through DHCP: None

请注意 Statically Configured DNS ServersDNS servers configured through DHCP 之间的差异。我想我可能会解析这个输出,但我不确定如果 windows language/locale 被更改我是否可以依赖这个文本,如果可能的话我宁愿使用 WMI 接口。

经过充分的抨击后,我找到了解决方案,但我并不是 100% 肯定这是正确的方法。在所有 DHCP DNS 服务器上,DNS 值都包含一个 IP 地址,并且该 IP 地址等于 Default Gateway 值。当这些值匹配时,我们处理的是 DHCP DNS 服务器,而不是静态配置的。

我认为这可能在 netsh.exe 上的 System.Net.NetworkInformation namespace, but evidently not. I looked through a few lower-level Windows networking APIs thinking certainly it must exist somewhere in there, but no such luck. After running dumpbin 中可用,以查看它消耗的 libraries/functions 类型,我确实想到了另一个可以查看的地方:注册表。

碰巧,如果您在 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\ 键下查看注册表,您会看到每个网络接口的键都具有 GUID 格式的名称。在接口的键中,您会发现两个感兴趣的值:DhcpNameServerNameServer。在我的 DNS 服务器由 DHCP 设置的客户端系统上,DhcpNameServer 包含该 DNS 服务器的 IP 地址,而 NameServer 包含一个空的 [String]。如果我手动设置 DNS 服务器,同时保留接口本身的自动分配地址,NameServer 将包含手动设置的 DNS 服务器地址,而 DhcpNameServer 仍包含由 DHCP 指定的相同 DNS 服务器。

根据这些观察,似乎...

  • DhcpNameServer 值始终包含 DHCP 指定的 DNS 服务器。
  • NameServer 值始终包含手动指定的 DNS 服务器。
  • 当您查询系统的名称服务器时(例如通过 Win32_NetworkAdapterConfiguration.DNSServerSearchOrder 属性),如果提供,结果将包含 NameServer 的值,否则 DhcpNameServer,如果提供的话。换句话说,系统会告诉您哪些 DNS 服务器当前正在使用,但不会告诉您它们的地址是如何指定的。
  • 要确定接口是否具有手动分配的 DNS 服务器,请检查 NameServer 是否具有非空值。

因此,给定 ID 为 $interfaceID 的接口,您可以像这样构建其注册表项的路径...

$interfaceKeyPath = "HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces$interfaceID"

...然后像这样检索动态分配和手动分配的名称服务器值...

Get-ItemProperty -Path $interfaceKeyPath -Name 'DhcpNameServer', 'NameServer'

剩下的问题就是你从哪里得到 $interfaceID 的值,而且有很多来源,尽管技巧将排除不需要的接口(例如,在我的 Windows 10 系统上我有一个数据包捕获环回适配器和一个管理程序适配器,我想将其排除在此类查询之外)。最兼容的方式(可追溯到 .NET 2.0)是 Id property of the NetworkInterface class...

[System.Net.NetworkInformation.NetworkInterface]::GetAllNetworkInterfaces() `
    | Select-Object -ExpandProperty 'Id'

...虽然唯一有用的properties on which to filter are Name and NetworkInterfaceType.

在 Windows Vista 和更高版本上 Win32_NetworkAdapter class 提供 GUID 属性...

Get-WmiObject -Class 'Win32_NetworkAdapter' -Property 'GUID' -Filter 'PhysicalAdapter = true'

...尽管即使在 PhysicalAdapter 上进行过滤,它仍然是 return 环回和管理程序适配器,我没有看到任何确定的 属性 或 class只能用于 select 硬件适配器的关系。

Win32_NetworkAdapterConfiguration class 大同小异...

Get-WmiObject -Class 'Win32_NetworkAdapterConfiguration' -Property 'SettingID'

...没有过滤掉非硬件甚至非物理适配器的属性。

在(我认为)Windows 8 及以上有 Get-NetConnectionProfile cmdlet...

Get-NetConnectionProfile | Select-Object -ExpandProperty 'InstanceID'

记录为 "a connection profile associated with one or more physical network adapters",在我的系统上,它只 return 我的物理适配器。

还有Get-NetAdapter cmdlet...

Get-NetAdapter -Physical `
    | Where-Object -Property 'EndPointInterface' -NE -Value $true `
    | Select-Object -ExpandProperty 'InterfaceGuid'

我发现传递 -Physical 参数排除了管理程序适配器而不是环回适配器,因此有必要过滤掉 EndPointInterface$true 的地方以消除它。 HardwareInterfaceVirtual 属性可能也很有趣。

另一种选择是调用 Get-NetAdapterHardwareInfo cmdlet,它似乎知道如何区分真正的硬件适配器,并让它确定哪些适配器由 Get-NetAdapter...

Get-NetAdapterHardwareInfo `
    | Get-NetAdapter `
    | Select-Object -ExpandProperty 'InterfaceGuid'

return CIM 实例上方的 Get-Net* cmdlet,因此,例如,您可以使用诸如...

之类的东西来代替 Get-NetAdapter -Physical
Get-WmiObject -Namespace 'Root\StandardCimv2' -Class 'MSFT_NetAdapter' `
    -Property 'InterfaceGuid' -Filter 'HardwareInterface = true AND EndPointInterface = false'

以相同的方式检索 MSFT_NetAdapter 个实例。我不太确定关于使用一个与另一个的指导是什么。看起来人们应该更喜欢 cmdlet,但是,与 WMI/CIM 不同,它们提供 limited/no 参数来有效地过滤输出或指定所需的属性,因此您必须在管道中执行此操作。不过,我认为值得注意的是,我无法找到这些 MSFT_* class 的任何 current 文档;他们都说他们不再更新,除了 MSFT_NetConnectionProfile class 我根本找不到任何文档页面。这对我说微软不希望你依赖这些 classes 的任何明确结构,但如果 cmdlet 只是传递那些 class 实例......我不确定你怎么能如果没有任何记录,则与他们进行有意义且可靠的交互。

此外,请记住,如果可能,您会 want to prefer Get-CimInstance and its ilk over Get-WmiObject。我认为我还没有遇到过比将 Get-WmiObject 更改为 Get-CimInstance 更复杂的实例,尽管有比名称更多的差异(不一定是坏的)。