【问题标题】:Getting available CIDR netblocks in PHP在 PHP 中获取可用的 CIDR 网络块
【发布时间】:2018-06-29 13:41:16
【问题描述】:

给定一个父子网和许多“已使用”子网,确定“未使用”子网的最佳方法/算法是什么?

例如,给定:

$parent = '10.0.0.0/24';
$used = [
    '10.0.0.0/29',
    '10.0.0.8/29',
    '10.0.0.144/29'
];

程序需要提出最小的未使用子网数组:

$unused = [
    '10.0.0.16/28',
    '10.0.0.32/27',
    '10.0.0.64/26',
    '10.0.0.128/28',
    '10.0.0.152/29',
    '10.0.0.160/27',
    '10.0.0.192/26'
];

我打算使用以下伪代码来实现这一点:

create array $unused
add $parent to $unused
iterate through used subnets:
    if $used in $unused (exact match):
        remove from $unused
    else
        find subnet in $unused that contains $used
            split into smaller subnets

但是,我无法确切知道如何执行 split into smaller subnets 部分。如果有人可以建议最好的方法,或者更好/更有效的方法,将不胜感激!

【问题讨论】:

    标签: php ip-address


    【解决方案1】:

    一种可能的方法是递归二分搜索。

    子网是一个 IP 地址范围,因此第一步是开发将子网转换为范围并再次返回的函数(在您的示例中,您只显示了 IPv4 地址,所以我将使用整数来表示范围):

    function subnetToRange($subnet)
    {
        list($ip_address, $mask_length) = explode('/', $subnet);
        $ip = ip2long($ip_address);
        $mask_right = pow(2, 32 - $mask_length) - 1;
        $mask_left = 0xffffffff ^ $mask_right;
        $ip_low = $ip & $mask_left;
        $ip_high = $ip_low | $mask_right;
        $range = [$ip_low, $ip_high];
        return $range;
    }
    
    function rangeToSubnet(array $range)
    {
        $subnet = sprintf(
            '%s/%d',
            long2ip($range[0]),
            32 - log($range[1] - $range[0] + 1, 2)
        );
        return $subnet;
    }
    

    现在我们正在处理由下限和上限表示的范围,拆分范围的过程涉及创建两个较小的范围,一个覆盖原始范围的下半部分,另一个覆盖上半部分,例如:如果原始范围是 8 到 15,则两个新范围将是 8 到 11 和 12 到 15。

    鉴于此,我们可以实现一种递归二分搜索形式,从父范围开始,然后如果它与任何使用的范围重叠,则递归地将其分成两半,直到找到完全未使用的范围,或完全使用的范围:

    function findUnusedSubnets($parent, array $used)
    {
        $parent = subnetToRange($parent);
        $used = array_map('subnetToRange', $used);
        $unused = array();
        findUnusedRanges($parent, $used, $unused);
        $unused = array_map('rangeToSubnet', $unused);
        return $unused;
    }
    
    function findUnusedRanges($subnet, $used, &$unused)
    {
        foreach ($used as $range) {
            if ($subnet[0] > $range[1] || $subnet[1] < $range[0]) {
                continue; // subnet does not overlap used range
            }
            if ($subnet[0] >= $range[0] && $subnet[1] <= $range[1]) {
                return; // subnet completely within used range
            }
            // split subnet into two smaller subnets and check again
            $split = ($subnet[1] - $subnet[0] - 1) >> 1;
            findUnusedRanges([$subnet[0], $subnet[0] + $split], $used, $unused);
            findUnusedRanges([$subnet[1] - $split, $subnet[1]], $used, $unused);
            return;
        }
        $unused[] = $subnet; // subnet does not overlap any used range
    }
    

    结合这四个功能将允许您找到未使用的子网:

    $parent = '10.0.0.0/24';
    $used = [
        '10.0.0.0/29',
        '10.0.0.8/29',
        '10.0.0.144/29'
    ];
    var_dump(findUnusedSubnets($parent, $used));
    

    给出输出(用 php 5.6 测试):

    array(7) {
      [0]=>
      string(12) "10.0.0.16/28"
      [1]=>
      string(12) "10.0.0.32/27"
      [2]=>
      string(12) "10.0.0.64/26"
      [3]=>
      string(13) "10.0.0.128/28"
      [4]=>
      string(13) "10.0.0.152/29"
      [5]=>
      string(13) "10.0.0.160/27"
      [6]=>
      string(13) "10.0.0.192/26"
    }
    

    【讨论】:

      猜你喜欢
      • 2016-08-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-18
      • 1970-01-01
      • 1970-01-01
      • 2016-05-22
      • 2021-08-01
      相关资源
      最近更新 更多