gerben.dev

Get IP range by CIDR notation

Calculate the lowest and highest IP in a range by giving a (incomplete) CIDR notation string in PHP

I wanted to get the IPv4 range of a WHOIS record provided by LACNIC, because unlike other RIRs they don't supply you with the range, but with just a CIDR notation.

To make matters worse, they give 'incomplete' net numbers. The only working "CIDR to IP range" function I could find didn't accept it, so I had to sprinkle some magic.

$ipdata = explode('/', ltrim($incomplete_cidr_notation, '0'));
$dotcount = substr_count($ipdata[0], '.');
if ($dotcount != 3) {
    $ipdata[0] .= str_repeat('.0', (3-$dotcount));
}
$finalip = sprintf('%s/%s', $ipdata[0], $ipdata[1]);
$range = cidr_conv($finalip);

The final cidr_conv() is written by someone unknown in 2003 and can be found in the PHP.net comments of the Network functions page.

function cidr_conv($cidr_address) {
    $first = substr($cidr_address, 0, strpos($cidr_address, "/"));
    $netmask = substr(strstr($cidr_address, "/"), 1);

    $first_bin = str_pad(decbin(ip2long($first)), 32, "0", STR_PAD_LEFT);
    $netmask_bin = str_pad(str_repeat("1", (integer)$netmask), 32, "0", STR_PAD_RIGHT);

    for ($i = 0; $i < 32; $i++) {
        if ($netmask_bin[$i] == "1")
            $last_bin .= $first_bin[$i];
        else
            $last_bin .= "1";
    }

    $last = long2ip(bindec($last_bin));

    return array($first, $last);
}

So you can enter 190.151.192/18 as $incomplete_cidr_notation and get

(
    [0] => 190.151.192.0
    [1] => 190.151.255.255
)

But even if you enter 17/8 (or with a leading zero, straight out of IANAs IP allocation file) it will work, and you'll get in this case Apple's IP range

(
    [0] => 17.0.0.0
    [1] => 17.255.255.255
)

Update: Since this article is getting quite a few hits and when I read this back it seemed kinda confusing, I've recreated this a bit better as a Gist. https://gist.github.com/gerbenjacobs/6973722


function getIPRangeByCIDR($cidr) {
    // Making sure IPs are valid
    $ipdata = explode('/', ltrim($cidr, '0'));
    $dotcount = substr_count($ipdata[0], '.');
    if ($dotcount != 3) {
        $ipdata[0] .= str_repeat('.0', (3-$dotcount));
    }
    $cidr_address = sprintf('%s/%s', $ipdata[0], $ipdata[1]);
    
    // By 'unknown' http://www.php.net/manual/en/ref.network.php#30910
    $first = substr($cidr_address, 0, strpos($cidr_address, "/"));
    $netmask = substr(strstr($cidr_address, "/"), 1);
    
    $first_bin = str_pad(decbin(ip2long($first)), 32, "0", STR_PAD_LEFT);
    $last_bin = '';
    $netmask_bin = str_pad(str_repeat("1", (integer)$netmask), 32, "0", STR_PAD_RIGHT);
    
    for ($i = 0; $i < 32; $i++) {
        if ($netmask_bin[$i] == "1")
            $last_bin .= $first_bin[$i];
        else
            $last_bin .= "1";
    }
    
    $last = long2ip(bindec($last_bin));
    return array($first, $last);
}

print_r(getIPRangeByCIDR('190.151.192/18')); 
// Array ( [0] => 190.151.192.0 [1] => 190.151.255.255 ) 

print_r(getIPRangeByCIDR('17/8')); 
// (Apple) Array ( [0] => 17.0.0.0 [1] => 17.255.255.255 ) 

print_r(getIPRangeByCIDR('65.192/11')); 
// (NSA?) Array ( [0] => 65.192.0.0 [1] => 65.223.255.255 )

Leave a note

This website uses WebMentions and ActivityPub (via brid.gy), you can respond to this post by linking from your page or replying directly to the Fediverse post.

WebMention

or manually.

Fediverse

On the Fediverse or BlueSky you can search for the URL https://gerben.dev/posts/get-ip-range-by-cidr-notation or search for my handle @gerben.dev@gerben.dev.

View source

✨ It's dangerous out there, please take some space dust! ✨
This work is licensed under CC BY-NC-SA 4.0 | πŸ€– No AI | πŸ“Š Uptime Status