I need to check if visitor IPv6 is in given prefix (I have some whitelisted ipv6 prefixes).
I adjusted the below function using two different functions from StackOverflow (one was converting an ipv6 prefix to first and last address and another answer was testing if a given IP is in given first+last ip range), but looking at the code, there are many functions I am unfamiliar with, plus the ipv6 confuse me.
Can someone tell me if this code looks correct ? From my tests it validates the IPs I send to it:
<?php
if(!function_exists('is_ipv6_in_prefix')){
function is_ipv6_in_prefix($ipv6_ip_to_check, $ipv6_prefix_to_check){
$ipv6_ip_to_check = inet_pton($ipv6_ip_to_check);
// Split in address and prefix length
list($firstaddrstr, $prefixlen) = explode('/', $ipv6_prefix_to_check);
// Parse the address into a binary string
$firstaddrbin = inet_pton($firstaddrstr);
// Convert the binary string to a string with hexadecimal characters
if(function_exists('bin2hex')){
$firstaddrhex = bin2hex($firstaddrbin);
} else {
// older php versions can use pack/unpack
$firstaddrhex = reset(unpack('H*', $firstaddrbin));
}
// Overwriting first address string to make sure notation is optimal
$firstaddrstr = inet_ntop($firstaddrbin);
// Calculate the number of 'flexible' bits
$flexbits = 128 - $prefixlen;
// Build the hexadecimal string of the last address
$lastaddrhex = $firstaddrhex;
// We start at the end of the string (which is always 32 characters long)
$pos = 31;
while ($flexbits > 0) {
// Get the character at this position
$orig = substr($lastaddrhex, $pos, 1);
// Convert it to an integer
$origval = hexdec($orig);
// OR it with (2^flexbits)-1, with flexbits limited to 4 at a time
$newval = $origval | (pow(2, min(4, $flexbits)) - 1);
// Convert it back to a hexadecimal character
$new = dechex($newval);
// And put that character back in the string
$lastaddrhex = substr_replace($lastaddrhex, $new, $pos, 1);
// We processed one nibble, move to previous position
$flexbits -= 4;
$pos -= 1;
}
// Convert the hexadecimal string to a binary string
if(function_exists('hex2bin')){
$lastaddrbin = hex2bin($lastaddrhex);
} else {
// older PHP versions can use pack/unpack
$lastaddrbin = pack('H*', $lastaddrhex);
}
// And create an IPv6 address from the binary string
$lastaddrstr = inet_ntop($lastaddrbin);
// print string values for user
// echo "\nPrefix: $ipv6_prefix_to_check";
// echo "\nFirst: $firstaddrstr";
// echo "\nLast: $lastaddrstr";
if ((strlen($ipv6_ip_to_check) == strlen($firstaddrbin)) && ($ipv6_ip_to_check >= $firstaddrbin && $ipv6_ip_to_check <= $lastaddrbin)) {
// In range
return true;
} else {
// Not in range
return false;
}
}
}
if(is_ipv6_in_prefix('2a03:2880:f800:4::', '2a03:2880:f800::/48')){
echo "\nTRUE";
} else {
echo "\nFALSE";
}
?>
I am unsure because another answer there comes with a correction and says:
This is a fix to the accepted answer, which incorrectly assumes the "first address" should be identical to the inputted string. Rather, it needs to have its value modified via an AND operator against its mask.
To demonstrate the problem, consider this example input:
2001:db8:abc:1403::/54Expected result:
First: 2001:db8:abc:1400:: Actual result:
First: 2001:db8:abc:1403::[...] full code for the fix [...]
When I tried to apply the code in the "correction" above, it failed to match IP 2a03:2880:f800:4:: inside this prefix: 2a03:2880:f800::/48.
Maybe I applied the fix wrong, but still, does this correction mentioned at the end apply to my final code above ?