在 php 中将 IPv6 cidr 拆分为 /64 块
Splitting an IPv6 cidr into /64 blocks, in php
我想创建一个脚本,它将 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。
有这个解决方案符合您的要求,需要您匹配它:D
它的要求是安装 GMP 或 BCMATH 扩展,因为在这种情况下你将处理非常大的小数。
<?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://whosebug.com/users/67332/glavi%C4%87)
* who gave this answer :
*/
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
进行调整而不是 $i++
注2
为什么要使用GMP/BCMATH?答案是大的小数在某些时候会被转换成浮点数,这会导致数字失去精度,这对这种计算不利。
学分
感谢 Glavić for posting this answer 如何将 IPv6 转换为小数,反之亦然
我又看了一遍,接受的答案并不完全是我想要的,因为它列出了 /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
}
我想创建一个脚本,它将 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。
有这个解决方案符合您的要求,需要您匹配它:D
它的要求是安装 GMP 或 BCMATH 扩展,因为在这种情况下你将处理非常大的小数。
<?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://whosebug.com/users/67332/glavi%C4%87)
* who gave this answer :
*/
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
进行调整而不是 $i++
注2
为什么要使用GMP/BCMATH?答案是大的小数在某些时候会被转换成浮点数,这会导致数字失去精度,这对这种计算不利。
学分
感谢 Glavić for posting this answer 如何将 IPv6 转换为小数,反之亦然
我又看了一遍,接受的答案并不完全是我想要的,因为它列出了 /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
}