First published on the Cu.be blog : link
If you ever need to detect whether an IP address (let’s say 192.168.0.2) is inside a certain IP range/subnet (let’s say 192.168.0.0/24), you can use the following code in PHP :
/**
* Check IPv4 address is within a range
*
* @param string $ip A valid IPv4 address (xxx.xxx.xxx.xxx)
* @param string $subnet A valid IPv4 subnet (xxx.xxx.xxx.xxx)
* @param string $mask A valid IPv4 subnet mask (a number from 0-32)
* @return boolean True if the address is within the range, false if it isn't
*/
function ip4_in_network($ip, $subnet, $mask)
{
if ($mask <= 0) {
return false;
}
$ip_bin_string = sprintf("%032b", ip2long($ip));
$net_bin_string = sprintf("%032b", ip2long($subnet));
return (substr_compare($ip_bin_string, $net_bin_string, 0, $mask) === 0);
}
For IPv6, the code is slightly more complicated:
/**
* Check IPv6 address is within a range
*
* @param string $ip A valid IPv6 address
* @param string $subnet A valid IPv6 subnet
* @param string $mask A valid IPv6 subnet mask (a number from 0-128)
* @return boolean True if the address is within the range, false if it isn't
*/
function ip6_in_network($ip, $subnet, $mask)
{
$subnet = inet_pton($subnet);
$ip = inet_pton($ip);
// thanks to MW on http://stackoverflow.com/questions/7951061/matching-ipv6-address-to-a-cidr-subnet
$binMask = str_repeat("f", $mask / 4);
switch ($mask % 4) {
case 0:
break;
case 1:
$binMask .= "8";
break;
case 2:
$binMask .= "c";
break;
case 3:
$binMask .= "e";
break;
}
$binMask = str_pad($binMask, 32, '0');
$binMask = pack("H*", $binMask);
return ($ip & $binMask) == $subnet;
}