I'm looking to create a script that will take an ipv6 range or cidr as input, and spit out a list of /64 blocks (or the first IP in each /64 block).
I have a function that does something similar for IPv4 IPs, but I lack the understanding to re-purpose it for 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 and long2ip are ipv4 only.
There is this solution that matches your requirements and needs you to match its :D
Its requirement is to have GMP or BCMATH extensions installed, because in this case you will be dealing with very big decimals.
<?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));
}
?>
As you can see, this solution is performing calculations on decimals (as strings).
Note 1
The solution you provided as an example for IPv4 is listing every forth IP address in the block, the one that I provided is listing all the IP addresses in the block, you can adjust this by using $i+=4
instead of $i++
Note 2
Why do we use GMP/BCMATH? The answer is that big decimals at some point will be converted into floats which will cause the numbers to lose precision, which is not good for this kind of calculations.
Credits
Thanks to Glavić for posting this answer on how to convert IPv6 to decimals and vice versa
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With