如何使用php加入或拆分ip网络

时间:2015-01-23 11:44:33

标签: php network-programming ip

我有一个似乎包含子任务的任务。取两个或多个IP段(可能是192.168.1.32 / 27,192.168.1.64 / 28和192.168.1.128/25)并将它们连接到最近的段(使用以前的ip段192.168.1.0/24)。检查是否可以加入细分受众群。

任何人都知道是否有一个可以做到这一点的工具的php类 - 或者同样的几个函数?

/ Lars

编辑: [代码]

$ip_id_array = array();
$ip_level_array = array();
$ip_segment_array = array();
$ip_cidr_array = array();
$toplevel_array = array();

foreach ($_POST["net_ip_id"] AS $ip_id)
{
    $sql = "
    SELECT
        ip_segments.ip_id,
        ip_segments.ip_segment,
        ip_segments.ip_level,
        ip_segments.ip_cidr
    FROM
        ip_segments
    WHERE
        ip_id = '".$ip_id."'
    ";
    $relip_res = mysqli_query($db, $sql) or cc("ERROR: SQL Select subsegments related", $sql, mysqli_error($db) , $_SESSION["u_id"], $this_document);
    $ip_array = mysqli_fetch_assoc($relip_res);
    $ip_id_array[] = $ip_array["ip_id"];
    $ip_level_array[] = $ip_array["ip_level"];
    $ip_segment_array[] = $ip_array["ip_segment"];
    $ip_cidr_array[] = $ip_array["ip_cidr"];

    if ($ip_array["ip_level"] != 0 && !empty($ip_array["ip_level"]))
    {
        $sql = "
        SELECT
            ip_segments.ip_id,
            ip_segments.ip_segment,
            ip_segments.ip_level,
            ip_segments.ip_cidr
        FROM
            ip_segments
        WHERE
            ip_id = '".$ip_array["ip_level"]."'
        ";
        $relip_res = mysqli_query($db, $sql) or cc("ERROR: SQL Select subsegments related", $sql, mysqli_error($db) , $_SESSION["u_id"], $this_document);
        $toplevel[] = mysqli_fetch_assoc($relip_res);
    }
}

$ip_level_array = array_unique($ip_level_array);
$ip_cidr_array = array_unique($ip_cidr_array);
$toplevel = array_unique($toplevel);

if(sizeof($ip_level_array) > 1)
    $field_alerts[] = "IP Segments must be within the same segment.!";
if ($ip_cidr_array[0] <= 1)
    $field_alerts[] = "Subnetmasks must be larger than or equal to 1";
if ($toplevel <= 1)
    $field_alerts[] = "No Toplevel to merge to!";

if (sizeof($field_alerts) < 1)
{
    $new_segment = $ip_id_array[0];
    $new_cidr = $toplevel[0]["ip_cidr"];
}

[/代码]

编辑: 一个段是顶级192.168.1.0/24(id 1 - 级别0) 它可以划分为几个不同类型的子网,范围从/ 25和下至/ 32(主机)。 假设我们分段到/ 26。这给出了以下内容:

ID, Level, IP, CIDR
2,1,192.168.1.0,26
3,1,192.168.1.64,26
4,1,192.168.1.128,26
5,1,192.168.1.192,26

请参阅:http://jodies.de/ipcalc?host=192.168.1.0&mask1=24&mask2=26

我需要的是一段可以获取id的数组的代码,查看段和cidr并查看将段加入到最近的超网的可能性(在这种情况下为/ 25或/ 24) ) 选项:

Join ID, Result
2,3 => true (/25)
2,4 => false (networks not "next" to each other (a /26 between))
3,4 => false (subsegments will split toplevel (/25) segment
4,5 => true (/25)

如果需要更多信息,请告诉我。

1 个答案:

答案 0 :(得分:2)

根据您上次的编辑,我认为您希望找到所提供子网的最佳摘要。

假设你的例子中有子网:

192.168.1.0/26
192.168.1.64/26
192.168.1.128/26
192.168.1.192/26

由于计算子网属性所涉及的所有操作可能更容易查看子网的二进制表示:

11000000101010000000000100000000
11000000101010000000000101000000
11000000101010000000000110000000
11000000101010000000000111000000

检查是否可以连接两个子网的方法是查看最后一个子网位。屏蔽主机部分(在此示例中为最后6位),两个子网ID之间唯一允许的区别是它们必须连接在最后一个子网位(管道之间)。

1100000010101000000000010|0|hhhhhh
1100000010101000000000010|1|hhhhhh
1100000010101000000000011|0|hhhhhh
1100000010101000000000011|1|hhhhhh

在此示例中,前两个子网具有相同的前25位,因此它们可以连接在一起。同样代表最后两个子网。但是子网2和3具有不同的第25位,因此它们无法连接。因此,对于要连接的两个子网,它们需要具有长度为N的相同子网掩码,并且第一个N-1地址位应该相同。

使用IP数组实现此功能时,您需要解决两个问题:

  1. 如何处理数组以及以何种顺序通过它
  2. 如何配对最佳候选人,并检查是否可以进行总结
  3. 这两个问题都可以通过构造和排序数组来解决。所以,假设你从数据库中获得了数组,并且想要总结子网。

    $ips = array(
        array("ip" => "192.168.1.0", "cidr" => "26"),
        array("ip" => "192.168.1.64", "cidr" => "26"),
        array("ip" => "192.168.1.128", "cidr" => "26"),
        array("ip" => "192.168.1.192", "cidr" => "26")
    );
    

    我解决问题的方法是构建数组,地址表示为数字,CIDR长度作为键。请注意& (-1 << (32 - $ips[$i]['cidr'])部分:如果数据库中的地址是子网地址,则不需要这样做,但我只是为了大小写而包含它。这将对IP地址和子网掩码执行按位AND,并计算任何IP的子网地址。

    for($i = 0; $i < sizeof($ips); $i++){
        $net = ip2long($ips[$i]['ip']) & (-1 << (32 - $ips[$i]['cidr']));
        $n_ips[$ips[$i]['cidr']][]= $net;
    }
    

    这将为您提供$n_ips数组(如下所示),并允许您通过CIDR对数组进行排序,并首先处理最小的子网,然后可以将该摘要与更大的子网连接。

    array
      26 => 
        array
          0 => int -1062731520
          1 => int -1062731456
          2 => int -1062731392
          3 => int -1062731328
    

    你应该对二级数组做同样的事情。对地址进行排序将使最佳候选者彼此相邻。然后,您将遍历每个阵列并检查是否可以使用前面提到的规则连接子网:

    两个子网需要具有长度为N的相同子网掩码(由于您使用CIDR前缀对子网进行分类,它们会执行此操作),并且第一个N-1地址位应该相同。第二件事很容易通过使用1位较短的CIDR前缀计算两个子网地址:$ip & (-1 << 32 - ($cidr+1))并检查它们是否相同。

    因此,最终函数(将您之前创建的$n_ips数组作为参数)可能如下所示:

    function summarize($n_ips){
        $changed = false; // Did you change anything in this iteration?
        $new = array();   // Array with summarized scopes
        krsort($n_ips);  // Sort array keys (CIDR)
        foreach($n_ips as $cidr => $ips){
            sort($n_ips[$cidr]);  // Sort the scopes from lowest to highest
            for($i = 0; $i < sizeof($n_ips[$cidr]); $i++){
                if($n_ips[$cidr][$i] == $n_ips[$cidr][$i+1]){   //Skip if you have two same subnets (not needed if your list of scopes is clean)
                    continue;
                }
                if(($n_ips[$cidr][$i] & (-1 << 33 - $cidr)) == ($n_ips[$cidr][$i+1] & (-1 << 33 - $cidr))){ //Are subnet IDs from current and next subnet the same if you have smaller subnet mask?
                    $new[$cidr-1][] = $n_ips[$cidr][$i] & (-1 << 33 - $cidr);    //If yes add the summarized subnet to the new array
                    $i++;                                                       //Skip the next subnet
                    $changed = true;                                            //And raise the changed flag
                }else{
                    $new[$cidr][] = $n_ips[$cidr][$i];                          //If not just copy the current subnet
                }
            }
        }
        return $changed ? summarize($new) : $n_ips; //If there were no changes you have the optimized summarization, otherwise summarize the new array
    }
    

    输出将是汇总子网的数组,其中包含地址的数字表示,您可以将其转换为点小数表示:

    $s_ips = summarize($n_ips)
    
    foreach($s_ips as $cidr => $ips){
        foreach($ips as $ip){
            echo long2ip($ip) . "/" . $cidr . "<br/>";
        }
    }
    

    你说如果总结是可能的话你只需要真/假,但我故意提供了更一般的答案。只有在汇总数组中剩下一个子网时,才能修改该函数以返回true。