Detecting if an IP address is in a certain range in PHP

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;
}

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.