在 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);
}
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.