Marcus Frenkel
Marcus Frenkel

Reputation: 711

Generate a list of IPv6 addresses within a given range

In Maria DB table I have two varbinary(16) fields that represent IPv6 addresses for the start and end of the IPv6 range.

I want to use PHP to loop between this range and generate each IPv6 address that's within the range. I tried to turn binary to decimal to do the loop and increase the decimal number, but the loop does not produce any iteration.

Any help?

//The $IpStart_v6_FromDb/$IpStart_v6_End Vars are produced with INET6_ATON MariaDB function
$IpStartBin = decbin($IpStart_v6_FromDb);
$IpEndBin = decbin($IpEnd_v6_FromDb);
$ArIpRange = array();
$ArIpRange[] = $IpStartBin;
$x=0;
for(;;)
{
    if ($IpStartBin==$IpEndBin) break;
    $tLastIpBin = $ArIpRange[$x];
    $tNextIpBin = decbin( (bindec($tLastIpBin) + 1) );
    if ($tNextIpBin==$IpEndBin) break;
    $ArIpRange[] = $tNextIpBin;
    $x++;
}
foreach ($ArIpRange as $v)
{
    echo "<br>IP range item:<br>".base64_encode($v); //debug
}

[EDIT]

I'm embarrassed to say that I thought the length of an IPv6 address is 64 bits.

Upvotes: 0

Views: 1340

Answers (1)

miken32
miken32

Reputation: 42711

So, some simple troubleshooting or reading the manual would have told you that decbin expects an integer as input. So right off the bat you're getting a zero back for both variables.

Furthermore, even if you did correct that problem (by using bindec,) you're talking about 128 bit numbers which, unless you're from the future, are not something PHP can handle natively.

I'd suggest handling them as strings. First normalize them (fill in missing zeroes and replace :: with zeroes) using code from this answer, find and remove the matching prefix using code from this answer, and then deal with the rest by converting them to much smaller numbers.

And, as mentioned in the comments, make sure you don't try dealing with too big a range or you will make your server unhappy.

<?php
// https://stackoverflow.com/a/55521768/1255289
// Generate a list of IPv6 addresses within a given range

function expand_ipv6(string $ip): ?string
{
    // https://stackoverflow.com/a/12095836/1255289
    $hex = unpack("H*", inet_pton($ip))[1] ?? "";
    return (strlen($hex) === 32) ? implode(":", str_split($hex, 4)) : null;
}

$IpStart_v6_FromDb = "2001:db8::1234";
$IpEnd_v6_FromDb = "2001:db8::1299";

$ip1 = expand_ipv6($IpStart_v6_FromDb);
$ip2 = expand_ipv6($IpEnd_v6_FromDb);
if ($ip1 === null || $ip2 === null) {
    die;
}

// https://stackoverflow.com/a/35838357/1255289
// length is 39 to account for 7 colons
for ($i = 0; $i < 39 && $ip1[$i] === $ip2[$i]; $i++);

$ipv6_prefix = substr($ip1, 0, $i);
$ipv6_start = hexdec(substr($ip1, $i));
$ipv6_end = hexdec(substr($ip2, $i));

if (strlen($ipv6_prefix) < 26) {
    // adjust this to requirements to prevent too large ranges
    die;
}

for ($a = $ipv6_start; $a <= $ipv6_end; $a++) {
    $hex = dechex($a);
    printf("%s%s\n", $ipv6_prefix, implode(":", str_split($hex, 4)));
}

Upvotes: 1

Related Questions