【问题标题】:Splitting an IPv6 cidr into /64 blocks, in php在 php 中将 IPv6 cidr 拆分为 /64 块
【发布时间】:2017-09-21 14:37:41
【问题描述】:

我希望创建一个脚本,该脚本将 ipv6 范围或 cidr 作为输入,并输出 /64 块的列表(或每个 /64 块中的第一个 IP)。

我有一个对 IPv4 IP 执行类似操作的函数,但我缺乏将其重新用于 ipv6 的理解。

Function BreakTo30($CIDR)
{
    $CIDR = explode("/", $CIDR); // this breaks the CIDR block into octlets and /notation
    $octet = ip2long($CIDR[0]); //turn the first 3 octets into a long for calculating later
    $NumberOf30s = pow(2,(30-$CIDR[1]))-1; //calculate the number of /30s in the CIDR block
    $OutputArray = array();
    for ($i=-4; $i<4 * $NumberOf30s; $OutputArray[] = (long2ip($octet + ($i += 4)))); //fancy math to output each /30
    return $OutputArray; //returns an array of ranges

}

ip2long 和 long2ip 仅适用于 ipv4。

【问题讨论】:

    标签: php ipv6 cidr


    【解决方案1】:

    有这个解决方案符合您的要求,需要您匹配它:D

    它的要求是安装 GMP 或 BMATH 扩展,因为在这种情况下您将处理非常大的小数。

    <?php
    $cidr ="2001:adb8:85a3:1111:1111:8a2e:3270:7334/120";
    $all = listIPv6InBlock($cidr);
    
    echo "CIDR is $cidr<br/>\r\n";
    echo "Count is ". count($all)."<br/>\r\n";
    
    printAddresses($all);
    
    function listIPv6InBlock($CIDR)
    {
        $CIDR = explode("/", $CIDR); // this breaks the CIDR block into octlets and /notation
        $octet = ip2long_v6($CIDR[0]); //turn the first 3 octets into a long for calculating later
        $NumberOfIPs = pow(2,(128-$CIDR[1]))-1; //calculate the number of /30s in the CIDR block
        $OutputArray = array();
        for ($i=0; $i< $NumberOfIPs; $i++){
            $OutputArray[] = long2ip_v6(bcadd($octet,"$i"));
        }
        return $OutputArray; //returns an array of ranges
    }
    
    function printAddresses($arr){
        foreach($arr as $ip){
            echo "$ip <br/>\r\n";       
        }
    }
    
    /*
     *The following two functions are credited to (https://stackoverflow.com/users/67332/glavi%C4%87) 
     * who gave this answer :https://stackoverflow.com/a/19497446/896244
     */
    function ip2long_v6($ip) {
        $ip_n = inet_pton($ip);
        $bin = '';
        for ($bit = strlen($ip_n) - 1; $bit >= 0; $bit--) {
            $bin = sprintf('%08b', ord($ip_n[$bit])) . $bin;
        }
    
        if (function_exists('gmp_init')) {
            return gmp_strval(gmp_init($bin, 2), 10);
        } elseif (function_exists('bcadd')) {
            $dec = '0';
            for ($i = 0; $i < strlen($bin); $i++) {
                $dec = bcmul($dec, '2', 0);
                $dec = bcadd($dec, $bin[$i], 0);
            }
            return $dec;
        } else {
            trigger_error('GMP or BCMATH extension not installed!', E_USER_ERROR);
        }
    }
    
    function long2ip_v6($dec) {
        if (function_exists('gmp_init')) {
            $bin = gmp_strval(gmp_init($dec, 10), 2);
        } elseif (function_exists('bcadd')) {
            $bin = '';
            do {
                $bin = bcmod($dec, '2') . $bin;
                $dec = bcdiv($dec, '2', 0);
            } while (bccomp($dec, '0'));
        } else {
            trigger_error('GMP or BCMATH extension not installed!', E_USER_ERROR);
        }
    
        $bin = str_pad($bin, 128, '0', STR_PAD_LEFT);
        $ip = array();
        for ($bit = 0; $bit <= 7; $bit++) {
            $bin_part = substr($bin, $bit * 16, 16);
            $ip[] = dechex(bindec($bin_part));
        }
        $ip = implode(':', $ip);
        return inet_ntop(inet_pton($ip));
    }
    ?>
    

    如您所见,此解决方案对小数(作为字符串)执行计算。

    注1

    您作为 IPv4 示例提供的解决方案是列出块中的每四个 IP 地址,我提供的解决方案是列出块中的所有 IP 地址,您可以使用 $i+=4 而不是 @987654325 来调整它@

    注2

    我们为什么要使用 GMP/BMATH?答案是大小数在某些时候会被转换成浮点数,这会导致数字失去精度,这对这种计算不利。

    学分

    感谢 Glavić 发布此answer 关于如何将 IPv6 转换为小数,反之亦然

    【讨论】:

    • @Moist_Gerbil 那么我什么时候能拿到赏金呢?
    • 现在应该奖励了。
    【解决方案2】:

    我又看了一遍,接受的答案并不是我想要的,因为它列出了 /128 块,而不是 /64。 listIPv6InBlock 需要改成这样:

    function listIPv6InBlock($CIDR)
    {
        $CIDR = explode("/", $CIDR); // this breaks the CIDR block into octlets and /notation
        $octet = ip2long_v6($CIDR[0]); 
        $NumberOfIPs = pow(2,(64-$CIDR[1])); //calculate the number of /64s in the CIDR block
        $OutputArray = array();
        $a = gmp_init($octet);
        $b = gmp_init('18446744073709551616'); // long /64
    
        for ($i=0; $i< $NumberOfIPs; $i++){
            $c = gmp_mul($b,$i);
            $d = gmp_add($a,$c);
    
            $OutputArray[] = long2ip_v6(gmp_strval($d));
        }
        return $OutputArray; //returns an array of ranges
    }
    

    【讨论】:

      猜你喜欢
      • 2022-07-05
      • 2016-09-29
      • 2013-08-20
      • 1970-01-01
      • 2020-10-19
      • 2022-11-22
      • 2023-01-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多