Basic Fun with IPs in PHP

Posted by Nessa | Posted in | Posted on August 7, 2012

1

I was working on an application a couple weeks ago that required the ability to extract data about an IP range when only a CIDR is provider.  Of course I checked the oracle first because I’m too lazy to write my own code, but was disappointed to be unable to find a PHP script that did exactly what I needed.  I did manage to find this nifty little function which made things so much easier:

function cidrToRange($cidr) {
    $range = array();
    $cidr = explode('/', $cidr);
    $range[0] = long2ip((ip2long($cidr[0])) & ((-1 << (32 - (int)$cidr[1]))));
    $range[1] = long2ip((ip2long($cidr[0])) + pow(2, (32 - (int)$cidr[1])) - 1);
    return $range;
}

This function, those short in stature, is actually all you need to extract data from a provided CIDR range. Firstly, let’s set the value of $cidr to something simple, and get the value of the returned $range array:

$cidr="192.168.1.0/24";
$range=cidrToRange($cidr);
print_r($range);

Result:

Array
(
    [0] => 192.168.1.0
    [1] => 192.168.1.255
)

If you notice, the array contains the values of the first and last IPs in the CIDR range.  Therefore, you can grab these values out rather easily:

$first_ip = $range[0];
$last_ip = $range[1];

Getting the gateway, broadcast, and subnet mask

To get the gateway and broadcast IPs, well, you’ve already done it. This is the first (usually) and last IP in the block.  All you need now is the subnet mask, and to pull this out you’ll need to separate the CIDR mask (what follows the “/”) from the value of $cidr:

$cidrmask = explode("/",$cidr);
$cidrmask = $cidrmask[1];

Once we have the bitmask, which in my example is “24”,  you can calculate the subnet mask:

$subnet_mask = long2ip(-1 &lt;&lt; (32 - (int)$cidrmask));

Listing all IPs in the CIDR range

Now for the fun part, which was not so fun when you’re automatically conditioned to think that everything has to be harder than it needs to be.  PHP has an ip2long function that basically converts an IPv4 IP address to a long forgettable number. The simplest way I found to pull out a list of IPs in a range is to convert the IP to its long format, increment it, then convert it back.  Here’s the full script:

function cidrToRange($cidr) {
    $range = array();
    $cidr = explode('/', $cidr);
    $range[0] = long2ip((ip2long($cidr[0])) & ((-1 << (32 - (int)$cidr[1]))));
    $range[1] = long2ip((ip2long($cidr[0])) + pow(2, (32 - (int)$cidr[1])) - 1);
    return $range;
}
 
$cidr="192.168.1.0/24";
 
$range = cidrToRange($cidr);
$first_ip = ip2long($range[0]);
$last_ip = ip2long($range[1]);
 
$first_ip++;
while($first_ip < $last_ip){
    $real_ip = long2ip($first_ip);
 
    if(!preg_match('/\.0$/',$real_ip)){ // Don't include IPs that end in .0
        echo $real_ip . "\n";
    }
 
    $first_ip++;
}

This will list out all the usable IPs in the CIDR provided (typically all the IPs that are not the gateway or broadcast addresses, or that end in in a “.0″.  Note the preg_match function within the while loop – this will prevent ips ending in .0 from being included, which is useful when dealing with blocks of /23 or higher.  This is of course just a starting point – you can adjust to meet the needs of how your networks are set up.

Be Sociable, Share!

Comments (1)

I’m glad I stumbled onto this. I deal with IP ranges with cidr all the time ( Network Administrator for a ISP ) and am constantly being asked by the 1st line support guys, whats the gateway, how many ip’s in the block, etc. Now I’ve got the idea to build a tool for them to figure it out themselves!

Post a comment