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

2022-04-07 14:46:49 +08:00
 pray123

在 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);
    }
1694 次点击
所在节点    程序员
7 条回复
Rache1
2022-04-07 15:37:00 +08:00
试试?

s1lentium/iptools - Packagist
https://packagist.org/packages/s1lentium/iptools
pray123
2022-04-07 15:45:26 +08:00
@Rache1 奇怪,我这边访问不了网站 https://packagist.org/packages/s1lentium/iptools
Rache1
2022-04-08 09:16:12 +08:00
pray123
2022-04-08 15:00:06 +08:00
@Rache1 不行,只支持 ipv4 。ipv6 直接报异常
Rache1
2022-04-08 15:52:47 +08:00
Atomo
2022-04-09 02:07:09 +08:00
pray123
2022-04-12 14:23:32 +08:00
@Rache1 这个有用,感谢

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/845482

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX