V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
pray123
V2EX  ›  程序员

一段 PHP 代码求助,谢谢大家! IP 段转 CIDR 代码 ,重点支持 ipv6

  •  
  •   pray123 · 2022-04-07 14:46:49 +08:00 · 1680 次点击
    这是一个创建于 940 天前的主题,其中的信息可能已经有所发展或是发生改变。

    在 goole 和 github 上搜索 IP 段转 cidr ,整整找了两天都没有发现支持 ipv6 段转 cidr 的 PHP 版本代码。无奈只能找个 ipv4 的版本作参考模仿着实现支持 ipv6 ,但似乎行不通(经调试发现“$startaddr & $mask”与运算有问题,已在代码块标注),路过的大佬麻烦帮忙指点下


    github 上 ipv4 版本

    
        /**
        * ip 段转 cidr (仅支持 ipv4 )
        * @param $startip IP 起始地址
        * @param $endip IP 结束地址
        * @return array 返回 cidr 数组
        */
        function ipv4_range_to_cidr($startip, $endip)
        {
            $cidr2mask = [
                0x00000000, 0x80000000, 0xC0000000,
                0xE0000000, 0xF0000000, 0xF8000000,
                0xFC000000, 0xFE000000, 0xFF000000,
                0xFF800000, 0xFFC00000, 0xFFE00000,
                0xFFF00000, 0xFFF80000, 0xFFFC0000,
                0xFFFE0000, 0xFFFF0000, 0xFFFF8000,
                0xFFFFC000, 0xFFFFE000, 0xFFFFF000,
                0xFFFFF800, 0xFFFFFC00, 0xFFFFFE00,
                0xFFFFFF00, 0xFFFFFF80, 0xFFFFFFC0,
                0xFFFFFFE0, 0xFFFFFFF0, 0xFFFFFFF8,
                0xFFFFFFFC, 0xFFFFFFFE, 0xFFFFFFFF
             ];
    
            $startaddr = ip2long($startip);
            $endaddr = ip2long($endip);
    
            $cidrlist = array();
            while ($endaddr >= $startaddr) {
                $maxsize = 32;
                while ($maxsize > 0) {
                    $mask = $cidr2mask[$maxsize - 1];
                    $maskedbase = $startaddr & $mask;
                    if ($maskedbase != $startaddr) {
                        break;
                    }
                    $maxsize -=1;
                }
                $x = log($endaddr - $startaddr + 1) / log(2);
                $maxdiff = 32 - floor($x);
                $new_maxsize = $maxsize < $maxdiff ? $maxdiff : $maxsize;
                $cidrlist[] = long2ip($startaddr) . "/" . $new_maxsize;
                $startaddr += pow(2, (32 - $new_maxsize));
            }
            return $cidrlist;
        }
    
    

    模仿写的 ipv6 版本

    
        /**
        * ip 段转 cidr (仅支持 ipv6 )
        * @param $startip IP 起始地址
        * @param $endip IP 结束地址
        * @return array 返回 cidr 数组
        */
        function ipv6_range_to_cidr($startip, $endip) {
            $max = ip2long6('FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF');
            $cidr2mask = [];
            $count = 0;
            while(bccomp($max, "0") !== -1) {
                $cidr2mask[] = $max;
                $e = bcpow(2, $count);
                $max = bcsub($max, $e);
                $count++;
            }
            $cidr2mask = array_reverse($cidr2mask);
    
            $startaddr = ip2long6($startip);
            $endaddr = ip2long6($endip);
    
            $cidrlist = array();
            while (bccomp($endaddr, $startaddr) !== -1) {
                $maxsize = 128;
                while ($maxsize > 0) {
                    $mask = $cidr2mask[$maxsize - 1];
                    $maskedbase = $startaddr & $mask; //与运算数据溢出?求路过朋友帮忙指点下
                    if ($maskedbase != $startaddr) {
                        break;
                    }
                    $maxsize -=1;
                }
                $x = log(bcadd(bcsub($endaddr, $startaddr), "1")) / log(2);
                $maxdiff = 128 - floor($x);
                $new_maxsize = $maxsize < $maxdiff ? $maxdiff : $maxsize;
                $cidrlist[] = long2ip6($startaddr) . "/" . $new_maxsize;
                $startaddr = bcadd($startaddr, bcpow(2, (128 - $new_maxsize)));
            }
            return $cidrlist;
        }
    
        /**
        * 整数转换为 IPV6 地址
        * @param $ipv6long
        * @return string
        */
        function long2ip6($ipv6long)
        {
    
            $bin = gmp_strval(gmp_init($ipv6long, 10), 2);
            if (strlen($bin) < 128) {
                $pad = 128 - strlen($bin);
                for ($i = 1; $i <= $pad; $i++) {
                    $bin = "0" . $bin;
                }
            }
            $bits = 0;
            $ipv6 = '';
            while ($bits <= 7) {
                $bin_part = substr($bin, ($bits * 16), 16);
                $ipv6 .= dechex(bindec($bin_part)) . ":";
                $bits++;
            }
            // compress
            return inet_ntop(inet_pton(substr($ipv6, 0, -1)));
        }
    
        /**
        * IPV6 地址转换为整数
        * @param $ipv6
        * @return string
        */
        function ip2long6($ipv6)
        {
            $ip_n = inet_pton($ipv6);
            $bits = 15; // 16 x 8 bit = 128bit
            $ipv6long = '';
            while ($bits >= 0) {
                $bin = sprintf("%08b", (ord($ip_n[$bits])));
                $ipv6long = $bin . $ipv6long;
                $bits--;
            }
            return gmp_strval(gmp_init($ipv6long, 2), 10);
        }
    
    第 1 条附言  ·  2022-04-12 14:23:11 +08:00
    结贴
    感谢所有回复的 v 友,已在 github 上找到支持 ipv6 版本 https://github.com/mlocati/ip-lib
    7 条回复    2022-04-12 14:23:32 +08:00
    Rache1
        1
    Rache1  
       2022-04-07 15:37:00 +08:00
    试试?

    s1lentium/iptools - Packagist
    https://packagist.org/packages/s1lentium/iptools
    pray123
        2
    pray123  
    OP
       2022-04-07 15:45:26 +08:00
    @Rache1 奇怪,我这边访问不了网站 https://packagist.org/packages/s1lentium/iptools
    Rache1
        3
    Rache1  
       2022-04-08 09:16:12 +08:00
    pray123
        4
    pray123  
    OP
       2022-04-08 15:00:06 +08:00
    @Rache1 不行,只支持 ipv4 。ipv6 直接报异常
    Rache1
        5
    Rache1  
       2022-04-08 15:52:47 +08:00
    Atomo
        6
    Atomo  
       2022-04-09 02:07:09 +08:00
    pray123
        7
    pray123  
    OP
       2022-04-12 14:23:32 +08:00
    @Rache1 这个有用,感谢
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1225 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 18:17 · PVG 02:17 · LAX 11:17 · JFK 14:17
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.