Эффективно найти подходящую подсеть в очень большом пуле - PullRequest
0 голосов
/ 19 сентября 2018

Я пишу сценарий, который собирает информацию IPAM по предоставленному IP-адресу.

Это работает, но моя текущая реализация крайне неэффективна.

Я использую этот скрипт для запуска 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 может занять очень много времени!Скажем, я хочу протестировать 20 IP-адресов для целых 2000 подсетей, внезапно этот запрос займет полных 2 минуты.

У кого-нибудь есть идеи, как повысить эффективность этого?

1 Ответ

0 голосов
/ 19 сентября 2018

У меня нет доступа к полному списку подсетей, но тесты по сравнению с опубликованными показали, что это немного быстрее:

сначала «более простая» функция, чтобы увидеть, является ли 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
        }
    }
}

это циклически повторяетфрагменты регулярных выражений '(.*\.).*', '(.*\.).*\..*' и '(.*\.).*\..*\..*', которые при запуске через $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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...