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



我正在尝试通过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的接口具有DHCP的DNS设置,因此该值对我不利。如何过滤掉 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 语言/区域设置发生变化,我是否可以依赖此文本,如果可能的话,我宁愿使用 WMI 接口。

我认为这可能在System.Net.NetworkInformation命名空间中可用,但显然不是。 我浏览了一些较低级别的Windows网络API,认为它肯定存在于其中的某个地方,但没有这样的运气。 在netsh.exe上运行dumpbin以查看它正在使用的库/函数类型后,我确实想到了另一个要查看的地方:注册表。

碰巧的是,如果在注册表中查看HKEY_LOCAL_MACHINESYSTEMCurrentControlSetServicesTcpipParametersInterfaces项下的注册表项,您将看到每个网络接口的密钥,其名称采用 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:SYSTEMCurrentControlSetServicesTcpipParametersInterfaces$interfaceID"

。然后检索动态分配和手动分配的名称服务器值,如下所示...

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

这只是留下了从哪里获得$interfaceID值的问题,并且有许多来源,尽管诀窍是排除不需要的接口(例如,在我的 Windows 10 系统上,我有一个数据包捕获环回适配器和一个虚拟机管理程序适配器,我希望从此类查询中排除)。 最兼容的方式(可以追溯到 .NET 2.0)是NetworkInterface类的Id属性...

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

。尽管要过滤的唯一有用属性是NameNetworkInterfaceType.

在 Windows Vista 及更高版本上,Win32_NetworkAdapter类提供了一个GUID属性...

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

。尽管即使在PhysicalAdapter过滤时,它仍然返回环回和虚拟机管理程序适配器,并且我没有看到任何可用于仅选择硬件适配器的明确属性或类关系。

Win32_NetworkAdapterConfiguration类大致相同...

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

。没有属性来筛选出非硬件甚至非物理适配器。

在(我认为)Windows 8及更高版本上,有Get-NetConnectionProfilecmdlet...

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

它被记录为获取"与一个或多个物理网络适配器关联的连接配置文件",并且在我的系统上,它只返回我的物理适配器。

还有Get-NetAdaptercmdlet...

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

我发现传递-Physical参数会排除虚拟机管理程序适配器,但不会排除环回适配器,因此过滤掉EndPointInterface$true的位置是消除这种情况的必要条件。HardwareInterfaceVirtual属性也可能令人感兴趣。

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

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

上面的Get-Net*cmdlet 返回 CIM 实例,因此,例如,您可以使用类似以下内容的内容,而不是Get-NetAdapter -Physical...

Get-WmiObject -Namespace 'RootStandardCimv2' -Class 'MSFT_NetAdapter' `
-Property 'InterfaceGuid' -Filter 'HardwareInterface = true AND EndPointInterface = false'

以同样的方式检索MSFT_NetAdapter实例。 我不太确定使用一个与另一个的指导是什么。 似乎人们应该更喜欢 cmdlet,但是,与 WMI/CIM 不同,它们提供有限/没有参数来有效地筛选输出或指定所需的属性,因此您必须在管道中执行此操作。 不过,我认为值得注意的是,我无法找到这些MSFT_*类的任何当前文档;他们都说他们不再更新,除了我根本找不到任何文档页面的MSFT_NetConnectionProfile类。 这对我来说Microsoft不希望您依赖这些类的任何明确结构,但是如果 cmdlet 只是传递这些类实例......我不确定如果没有记录,您如何与他们进行有意义和可靠的互动。

另外,请记住,在可能的情况下,您会更喜欢Get-CimInstance及其同类而不是Get-WmiObject。 我认为我还没有遇到过比将Get-WmiObject更改为Get-CimInstance更复杂的实例,尽管差异(不一定是坏的)比名称更多。

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

既然Netsh都知道了,为什么不走这条路呢?喜欢

get-netadapter | % { $adapter=$_ | Add-Member -PassThru -Name staticDNS -Type NoteProperty -Value "" | Add-Member -PassThru -Name dhcpDNS -Type NoteProperty -Value ""; $dnsInfo=netsh interface ipv4 show dns name="$($_.Name)"; $dnsInfo -split '`n' | % { if ($_ -like '*stat*10*' ) { $dummy=$_ -split ' '; $adapter.staticDNS+=@($dummy[$dummy.count-1]) }; if ($_ -like '*dhcp*' ) { $dummy=$_ -split ' '; $adapter.dhcpDNS+=@($dummy[$dummy.count-1]) } }; $adapter }

最新更新