PHP 类或函数,用于消除 ip 阻止列表中的冗余


PHP class or function for eliminate redundancy in an ip block list

我有一个这样的IP块列表,我是通过将不同的来源拼接在一起而获得的。

160.11.0.0/16
160.11.14.0/20
160.12.0.0/14
160.16.5.0/15
160.16.1.0/14
160.18.0.0/16
160.20.0.0/14
160.24.0.0/16
160.26.0.0/15
160.28.0.0/15
160.74.0.0/16
...

我想要一个 php 函数,它可以接受此列表的输入并通过删除不必要的冗余来优化其大小,因为所有这些的最终输出将提供给能够比较 IP 的软件,它的性能将取决于它将获得多少行输入(列表越短, 更好的将是软件性能)。

我的代码是这样的:

$input_array = file("list.txt");
$output_array = optimize_ipblocks($input_array);
file_put_contents(implode("'n", $output));

此optimize_ipblocks函数应该能够:

  • 隔离所有块,看看是否有较小的块已经包含在较大的块中,删除较小的块。
  • 如果存在相同类型的重复项,请将其删除。
  • 如果有块可以连接在一起,因为它们共享部分内容,或者如果他们接触自己,将它们合并到一个更大的块中。
  • 如果可以实现块的某些聚合,则将它们聚合到等等。

我对 ip 块解包和比较的知识有点有限,所以现在我唯一可以摆脱的部分是重复检查建模功能,例如:

function optimize_ipblocks($input_array) {
   $blocks = array();
   foreach($input_array as $key => $val) {
      if(!in_array($val, $blocks)) $blocks[] = $val;
   }
   return $blocks;
}

我不知道如何对块和聚合进行比较。

有用的解决方案链接:

  • 这篇维基百科文章可以帮助 https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing 到它讨论IPv4 CIDR块的部分。

  • 这个用于聚合 cidr 地址的 perl 脚本 http://www.zwitterion.org/software/aggregate-cidr-addresses/

  • http://www.perlmonks.org/?node_id=118346

我编写了一个类,用于将 IP 范围合并到优化的 IP 范围超集中。对于您的使用情况,应首先将 CIDR 地址转换为"从-到"IP 范围,如本用法示例中的 $ranges 数组所示:

$ranges=[
    ["from" => '192.168.0.1',"to" => '192.168.0.9'],
    ["from" => '192.168.0.3',"to" => '192.168.0.6'],
    ["from" => '192.168.0.1',"to" => '192.168.0.5'],
    ["from" => '192.168.0.13',"to" => '192.168.0.17'],
    ["from" => '192.168.0.2',"to" => '192.168.0.4'],
    ["from" => '192.168.0.2',"to" => '192.168.0.7'],
    ["from" => '192.168.0.12',"to" => '192.168.0.14'],
];
$rm=new C_RangeMerger($ranges,'ip');
$mergedRanges=$rm->getMergedRanges();
echo("<pre>".print_r($mergedRanges,1)."</pre>");

类C_RangeMerger

输出:

Array
(
    [0] => Array
        (
            [from] => 192.168.0.1
            [to] => 192.168.0.9
        )
    [1] => Array
        (
            [from] => 192.168.0.12
            [to] => 192.168.0.17
        )
)

希望对您有所帮助!

这个函数应该做你想做的事。

<?php
function merge_cidr(array $cidr_or_ipv4_list)
{ // Main function
$valid_ip='0*?((?:0)|(?:2(?:(?:[0-4][0-9])|(?:5[0-5])))|(?:1?[0-9]{1,2}))'; // Build the valid ipv4 regex
$valid_ip.=str_repeat(".$valid_ip",3); // Finalize the ipv4 regex accepting leading zeros for each part
$valid_routing_prefix='(?:0*?((?:(?:0)|(?:3[0-2])|(?:[1-2]?[0-9]))))'; // Build a regex for the routing prefix (accepting leading zeros)
    foreach($cidr_or_ipv4_list as $a) // For each entry you pass to the function
    if (is_string($a) && preg_match("#^[^0-9]*$valid_ip(?:/$valid_routing_prefix)?[^0-9]*$#", $a, $m))
    { // Extracting the valid ipv4 and optionnaly the routing prefix
        $m[5] = ctype_digit($m[5]) ? ((int)$m[5]) : 32; // Initialize the valid routing prefix to the extracted value or 32 if mismatch
        $c[$m[5]][] = ip2long("$m[1].$m[2].$m[3].$m[4]") & (-1 << (32 - $m[5])); // Initialize the working array with key (prefix) and value as subnet by bitwise the decimal ip
    }
    if ($c) // If any valid ipv4 with optional routing prefix matched
    {
        foreach($c as &$unique) $unique=array_unique($unique); //Make unique as possible before processing
        $c = merge_cidr_summarize($c); // Pass the valid array to the slave function
        foreach($c as $d => & $e) // For each result as routing prefix => Decimal value
        $e = array_map(
        function ($a) use($d)
        {
            return [$a, $a + (1 << (32 - $d)) - 1];
        }
        , $e); // Change it to an array containing the range of ip
        foreach($c as $f => $g) // For each result as routing prefix => array of decimal value
        foreach($c as $h => $i) // For each result as routing prefix => array of decimal value
        if ($f > $h) // If we are not in the same line and the second line have a lower routing prefix
        foreach($g as $j => $k) // For each line as id => array of decimal values
        foreach($i as $l) // For each line as decimal value in the second foreach
        if ($k[0] >= $l[0] && $k[1] <= $l[1]) // If the block with lower routing prefix is totally including the first
        unset($c[$f][$j]); // Unset the smaller block
        foreach($c as $f => $g) //  For each result as routing prefix => array of decimal value
        {
            usort($g,
            function (array $a, array $b)
            {
                return  $b[0]>$a[0]?1:($b[0]<$a[0]?-1:0);
            }); // Order the result "naturally" inversed
            foreach($g as $h) // For each ordered result
            $z[] = long2ip($h[0]) . '/' . $f; // Convert it to human readable
        }
        return array_reverse($z); // And return the reversed result (order by routing prefix DESC and ip DESC)
    }
}
function merge_cidr_summarize(array $a)
{ // Slave function
    $b = false; // Did we make a change ?
    $c = []; // Initialize the result to an empty array
    krsort($a); // Order the input by routing prefix DESC
    foreach($a as $d => $e) { // For each entry as cidr => Array of decimal values
        sort($a[$d]); // Order the values "naturally"
        $k = count($a[$d]); // Count the values for the loop
        for ($i = 0; $i < $k; $i++) // Loop to check all values with this routing prefix
        if ($a[$d][$i] == $a[$d][$i + 1]) continue; // If the subnet is the same as the next, then directly goto the next
        elseif (($a[$d][$i] & (-1 << 33 - $d)) == ($a[$d][$i + 1] & (-1 << 33 - $d))) { // Check if subnet of this line and the next line are equals
            $c[$d - 1][] = $a[$d][$i++] & (-1 << 33 - $d); //  If yes add the new subnet in result array and skip the next line
            $b = true; // And tell the script to run again
        }
        else $c[$d][] = $a[$d][$i]; //  Else don't make anything
    }
    return $b ? merge_cidr_summarize($c) : $a; // If any change run again else return the result
}

要尝试一下

<?php
$your_array=['160.11.0.0/16','160.11.14.0/20','160.12.0.0/14','160.16.5.0/15','160.16.1.0/14','160.18.0.0/16','160.20.0.0/14','160.24.0.0/16','160.26.0.0/15','160.28.0.0/15','160.74.0.0/16'];
print_r(merge_cidr($your_array));

输出。。在 https://eval.in/745895 中检查

/*
    Array
    (
        [0] => 160.16.0.0/13
        [1] => 160.12.0.0/14
        [2] => 160.28.0.0/15
        [3] => 160.26.0.0/15
        [4] => 160.74.0.0/16
        [5] => 160.24.0.0/16
        [6] => 160.11.0.0/16
    )
*/