首页 > 解决方案 > 在非常大的池中有效地找到匹配的子网

问题描述

我一直在编写一个脚本,在给定 IP 地址的情况下收集 IPAM 信息。

它正在工作,但我目前的实施效率非常低。

我正在使用此脚本运行checkSubnet,它确定 IP 是否在子网中。

首先,我查询 IPAM 来收集这个$allSubnets对象:

Address         CIDR Description                               VLAN                     
-------         ---- -----------                               ----                     
10.15.10.0        24 DMZ                                       3000                     
10.15.11.0        24 Voice                                     3010                     
10.15.12.0        24 Wireless                                  3020
10.15.13.0        28 Management                                3030
...              ... ...                                        ...

然后像这样搜索:

$testCon = Test-Connection hostname -Count 1

$allSubnets | ForEach-Object { 
  if((checkSubnet -addr1 ('{0}/{1}' -f $_.Address, $_.CIDR) -addr2 $testCon.IPV4Address.IPAddressToString).Condition -eq $true)
  {
    [pscustomobject]@{
      subnet = ('{0}/{1}' -f $_.Address, $_.CIDR)
      desc = $_.Description
    }
  }
}

这非常适用于较小的查询。 但是,运行!中的所有项目可能需要很长时间。$allSubnets假设我想针对完整的 2000 个子网测试 20 个 IP 地址,突然这个查询将需要整整 2 分钟才能完成。

有人对如何提高效率有任何想法吗?

标签: powershell

解决方案


我无权访问子网的完整列表,但是针对发布的子网的测试表明这要快一些:

首先是一个“更简单”的函数来查看 IP 是否在某个范围内:

Function Find-Subnet ([IPAddress]$SubnetAddress,[byte]$CIDR,[IPAddress]$MatchIP){
    [IPAddress]$Mask = [System.Convert]::ToUInt64(('1'*$CIDR).PadRight(32,'0'),2)
    return (($SubnetAddress.Address -band $Mask.Address) -eq ($MatchIP.Address -band $Mask.Address))
}

这利用了位移,[System.IPAddress]对象可能会对性能产生一点影响,但它似乎仍然比链接函数快得多,而且更简洁,[IPAddress]如果你真的可以将转换重写为另一个二进制函数需要额外的性能,因为我们最后只使用地址的数字表示。

然后我希望限制搜索的数量,当搜索 2000 多个子网时,这应该是一个巨大的性能提升,虽然只搜索问题中的少数几个,但它只是一个轻微的负面影响。

foreach ($Reg in ('(.*\.).*','(.*\.).*\..*','(.*\.).*\..*\..*')){
    $Prefix = $ToMatch -replace $Reg,'$1'
    Write-Host "Searching subnets beginning with '$($Prefix)'..." -Fore Yellow
    $AllSubnets | ? {$_.Address.StartsWith($Prefix)} | ForEach-Object {
        if (Find-Subnet -SubnetAddress $_.Address -CIDR $_.CIDR -MatchIP $ToMatch){
            $_ ; break
        }
    }
}

this 循环通过 regex snippets '(.*\.).*''(.*\.).*\..*'并且'(.*\.).*\..*\..*'在运行时$Prefix = $ToMatch -replace $Reg,'$1'将导致如下所示:

'10.11.12.13' -> '(.*\.).*'         -> '10.11.12.'
'10.11.12.13' -> '(.*\.).*\..*'     -> '10.11.'
'10.11.12.13' -> '(.*\.).*\..*\..*' -> '10.'

然后我们遍历整个列表并提取地址以前缀开头的子网,对于任何 A 类子网,这将比不包括它要慢,对于 B 和 C,它将具有大致相同或更快的性能,而 B 是更可能更慢,而 C 更可能更快。

您当前的功能在找到 IP 后也不会停止搜索。通过;break在返回匹配的 IP 对象之后包含 ,我们会立即返回它并停止搜索,如果您想将子网存储在一个变量中,您可以$Result = @(foreach ($Reg in ...) { ... })将它最终保存在$Result.


推荐阅读