Skip to content

Instantly share code, notes, and snippets.

@maninhat
Forked from stibiumz/CIDR.php
Created May 14, 2020 11:22
Show Gist options
  • Select an option

  • Save maninhat/2be53fa639d8a168b2152651a3e878aa to your computer and use it in GitHub Desktop.

Select an option

Save maninhat/2be53fa639d8a168b2152651a3e878aa to your computer and use it in GitHub Desktop.

Revisions

  1. Jonavon Wilcox revised this gist Jul 16, 2014. 1 changed file with 56 additions and 0 deletions.
    56 changes: 56 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,56 @@
    Several useful functions available for IPv4 addresses. It is implemented using mostly bitwise expressions so it should be easily ported to other languages. There is very little done in the way of error checking. References are stated within comment blocks.

    Introduction
    ------------
    For a midsize project I wanted to store IP ranges in the database with
    the option to to also store CIDR blocks. CIDR blocks, though powerful
    are somewhat difficult for a typical user. Additional using them is not
    as precise for all ip ranges. There are a plethora of [tools][iptocidr]
    available on the internet that will do what I would like. However,
    incorporating these tools would not be practical. What I want is to
    mimic the functionality of these tools so that it can be easily
    imported into any of my projects. The most coveted tool for me would be
    to convert an IP range to a precise range of CIDR blocks. This required
    specific functionality not naturally provided in PHP.

    * Check for Valid Netmask
    * Check whether an IP address is within a CIDR block.
    * Take user input and a Netmask and make it into a valid CIDR block.
    * CIDR number into Netmask
    * Netmask to CIDR
    * Take an IP range and fit it into an exact range of CIDR blocks.

    This presents some difficulty in that PHP's [network functions]
    [phpnetwork] are not thorough enough. The revelation came when I
    realized that an IP address is merely a number. In fact the whole
    protocol is rooted in binary using very specific patterns. With that in
    mind I thought we could develop very light weight methods to solve our
    problem.

    The Code
    --------
    It is important to note that the methods provided are meant for IPv4
    addresses only are only tested on a 32bit system. Also, I did not care
    to do much in the way of error checking, but doing so, like testing
    whether the CIDR number is unsigned and less than or equal to 32,
    should be trivial.

    Though the solution I sought after would require PHP I didn't limit
    myself to that language only. In fact the PHP code I found seemed
    inefficient. Most involved a number conversions or parsing the address
    using sprintf using loops and nested if statements. Indeed the most
    efficient code, which shouldn't surprise most was in ANSI C. [Bit
    Twiddling Hacks][bithacks] resource proved very useful.

    ### About Binary ###
    I am not attempting to teach binary math. Since the code does not "read
    like prose" a small amount of knowledge is required in order to
    understand the code. Some excellent resources are Wikipedia's article
    on [CIDR][wpcidr] and [PHP binary operators][phpbitwise].


    [iptocidr]: http://ip2cidr.com/ "IP to CIDR tool"
    [phpnetwork]: http://www.php.net/manual/en/ref.network.php "PHP Network Functions"
    [bithacks]: http://graphics.stanford.edu/~seander/bithacks.html "Bit Twiddling Hacks by Sean Eron Anderson"
    [wpcidr]: http://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing "Classless Inter-Domain Routing"
    [phpbitwise]: http://www.php.net/manual/en/language.operators.bitwise.php "PHP's Bitwise Operators"
  2. Jonavon Wilcox revised this gist Jul 16, 2014. 1 changed file with 32 additions and 8 deletions.
    40 changes: 32 additions & 8 deletions CIDR.php
    Original file line number Diff line number Diff line change
    @@ -2,12 +2,13 @@
    /**
    * CIDR.php
    *
    * Utility Functions for IPv4 ip addresses.
    *
    * Utility Functions for IPv4 ip addresses.
    * Supports PHP 5.3+ (32 & 64 bit)
    * @author Jonavon Wilcox <[email protected]>
    * @version Sat Jun 6 21:26:48 EDT 2009
    * @copyright Copyright (c) 2009 Jonavon Wilcox
    */
    * @revision Carlos Guimarães <[email protected]>
    * @version Wed Mar 12 13:00:00 EDT 2014
    */

    /**
    * class CIDR.
    * Holds static functions for ip address manipulation.
    @@ -30,6 +31,23 @@ public static function CIDRtoMask($int) {
    return long2ip(-1 << (32 - (int)$int));
    }

    /**
    * method validIP.
    * Determine if a given input is a valid IPv4 address.
    * Usage:
    * CIDR::validIP('0.50.45.50');
    * Result:
    * bool(false)
    * @param $ipinput String a IPv4 formatted ip address.
    * @access public
    * @static
    * @return bool True if the input is valid.
    */
    public static function validIP($ipinput)
    {
    return filter_var($ipinput, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4);
    }

    /**
    * method countSetBits.
    * Return the number of bits that are set in an integer.
    @@ -45,9 +63,14 @@ public static function CIDRtoMask($int) {
    * @return int number of bits set.
    */
    public static function countSetbits($int){
    $int = $int - (($int >> 1) & 0x55555555);
    $int = ($int & 0x33333333) + (($int >> 2) & 0x33333333);
    return (($int + ($int >> 4) & 0xF0F0F0F) * 0x1010101) >> 24;
    $int = $int & 0xFFFFFFFF;
    $int = ( $int & 0x55555555 ) + ( ( $int >> 1 ) & 0x55555555 );
    $int = ( $int & 0x33333333 ) + ( ( $int >> 2 ) & 0x33333333 );
    $int = ( $int & 0x0F0F0F0F ) + ( ( $int >> 4 ) & 0x0F0F0F0F );
    $int = ( $int & 0x00FF00FF ) + ( ( $int >> 8 ) & 0x00FF00FF );
    $int = ( $int & 0x0000FFFF ) + ( ( $int >>16 ) & 0x0000FFFF );
    $int = $int & 0x0000003F;
    return $int;
    }

    /**
    @@ -68,6 +91,7 @@ public static function countSetbits($int){
    */
    public static function validNetMask($netmask){
    $netmask = ip2long($netmask);
    if($netmask === false) return false;
    $neg = ((~(int)$netmask) & 0xFFFFFFFF);
    return (($neg + 1) & $neg) === 0;
    }
  3. @cvsguimaraes cvsguimaraes revised this gist Mar 16, 2014. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion CIDR.php
    Original file line number Diff line number Diff line change
    @@ -3,7 +3,7 @@
    * CIDR.php
    *
    * Utility Functions for IPv4 ip addresses.
    * Supports 64bit PHP 5.5+
    * Supports PHP 5.3+ (32 & 64 bit)
    * @author Jonavon Wilcox <[email protected]>
    * @revision Carlos Guimarães <[email protected]>
    * @version Wed Mar 12 13:00:00 EDT 2014
  4. @cvsguimaraes cvsguimaraes revised this gist Mar 14, 2014. No changes.
  5. @cvsguimaraes cvsguimaraes revised this gist Mar 14, 2014. No changes.
  6. @cvsguimaraes cvsguimaraes revised this gist Mar 14, 2014. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions CIDR.php
    Original file line number Diff line number Diff line change
    @@ -33,15 +33,15 @@ public static function CIDRtoMask($int) {

    /**
    * method validIP.
    * Determine if a ip is valid.
    * Determine if a given input is a valid IPv4 address.
    * Usage:
    * CIDR::validIP('0.50.45.50');
    * Result:
    * bool(false)
    * @param $ipinput String a IPv4 formatted ip address.
    * @access public
    * @static
    * return bool True if a valid netmask.
    * @return bool True if the input is valid.
    */
    public static function validIP($ipinput)
    {
  7. @cvsguimaraes cvsguimaraes revised this gist Mar 12, 2014. 1 changed file with 20 additions and 2 deletions.
    22 changes: 20 additions & 2 deletions CIDR.php
    Original file line number Diff line number Diff line change
    @@ -2,12 +2,13 @@
    /**
    * CIDR.php
    *
    * Utility Functions for IPv4 ip addresses.
    *
    * Utility Functions for IPv4 ip addresses.
    * Supports 64bit PHP 5.5+
    * @author Jonavon Wilcox <[email protected]>
    * @revision Carlos Guimarães <[email protected]>
    * @version Wed Mar 12 13:00:00 EDT 2014
    */

    /**
    * class CIDR.
    * Holds static functions for ip address manipulation.
    @@ -30,6 +31,23 @@ public static function CIDRtoMask($int) {
    return long2ip(-1 << (32 - (int)$int));
    }

    /**
    * method validIP.
    * Determine if a ip is valid.
    * Usage:
    * CIDR::validIP('0.50.45.50');
    * Result:
    * bool(false)
    * @param $ipinput String a IPv4 formatted ip address.
    * @access public
    * @static
    * return bool True if a valid netmask.
    */
    public static function validIP($ipinput)
    {
    return filter_var($ipinput, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4);
    }

    /**
    * method countSetBits.
    * Return the number of bits that are set in an integer.
  8. @cvsguimaraes cvsguimaraes revised this gist Mar 12, 2014. 1 changed file with 12 additions and 6 deletions.
    18 changes: 12 additions & 6 deletions CIDR.php
    Original file line number Diff line number Diff line change
    @@ -5,9 +5,9 @@
    * Utility Functions for IPv4 ip addresses.
    *
    * @author Jonavon Wilcox <[email protected]>
    * @version Sat Jun 6 21:26:48 EDT 2009
    * @copyright Copyright (c) 2009 Jonavon Wilcox
    */
    * @revision Carlos Guimarães <[email protected]>
    * @version Wed Mar 12 13:00:00 EDT 2014
    */
    /**
    * class CIDR.
    * Holds static functions for ip address manipulation.
    @@ -45,9 +45,14 @@ public static function CIDRtoMask($int) {
    * @return int number of bits set.
    */
    public static function countSetbits($int){
    $int = $int - (($int >> 1) & 0x55555555);
    $int = ($int & 0x33333333) + (($int >> 2) & 0x33333333);
    return (($int + ($int >> 4) & 0xF0F0F0F) * 0x1010101) >> 24;
    $int = $int & 0xFFFFFFFF;
    $int = ( $int & 0x55555555 ) + ( ( $int >> 1 ) & 0x55555555 );
    $int = ( $int & 0x33333333 ) + ( ( $int >> 2 ) & 0x33333333 );
    $int = ( $int & 0x0F0F0F0F ) + ( ( $int >> 4 ) & 0x0F0F0F0F );
    $int = ( $int & 0x00FF00FF ) + ( ( $int >> 8 ) & 0x00FF00FF );
    $int = ( $int & 0x0000FFFF ) + ( ( $int >>16 ) & 0x0000FFFF );
    $int = $int & 0x0000003F;
    return $int;
    }

    /**
    @@ -68,6 +73,7 @@ public static function countSetbits($int){
    */
    public static function validNetMask($netmask){
    $netmask = ip2long($netmask);
    if($netmask === false) return false;
    $neg = ((~(int)$netmask) & 0xFFFFFFFF);
    return (($neg + 1) & $neg) === 0;
    }
  9. @jonavon jonavon revised this gist May 26, 2012. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion CIDR.php
    Original file line number Diff line number Diff line change
    @@ -209,7 +209,7 @@ public static function rangeToCIDRList($startIPinput,$endIPinput=NULL) {
    public static function cidrToRange($cidr) {
    $range = array();
    $cidr = explode('/', $cidr);
    $range[0] = long2ip((ip2long($cidr[0])) & ((-1 << (32 - (int)$cidr[1])))));
    $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;
    }
  10. @jonavon jonavon revised this gist Mar 13, 2012. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions CIDR.php
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,4 @@
    <?php
    /**
    * CIDR.php
    *
  11. @jonavon jonavon created this gist Mar 13, 2012.
    215 changes: 215 additions & 0 deletions CIDR.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,215 @@
    /**
    * CIDR.php
    *
    * Utility Functions for IPv4 ip addresses.
    *
    * @author Jonavon Wilcox <[email protected]>
    * @version Sat Jun 6 21:26:48 EDT 2009
    * @copyright Copyright (c) 2009 Jonavon Wilcox
    */
    /**
    * class CIDR.
    * Holds static functions for ip address manipulation.
    */
    class CIDR {
    /**
    * method CIDRtoMask
    * Return a netmask string if given an integer between 0 and 32. I am
    * not sure how this works on 64 bit machines.
    * Usage:
    * CIDR::CIDRtoMask(22);
    * Result:
    * string(13) "255.255.252.0"
    * @param $int int Between 0 and 32.
    * @access public
    * @static
    * @return String Netmask ip address
    */
    public static function CIDRtoMask($int) {
    return long2ip(-1 << (32 - (int)$int));
    }

    /**
    * method countSetBits.
    * Return the number of bits that are set in an integer.
    * Usage:
    * CIDR::countSetBits(ip2long('255.255.252.0'));
    * Result:
    * int(22)
    * @param $int int a number
    * @access public
    * @static
    * @see http://stackoverflow.com/questions/109023/best-algorithm-to-co\
    * unt-the-number-of-set-bits-in-a-32-bit-integer
    * @return int number of bits set.
    */
    public static function countSetbits($int){
    $int = $int - (($int >> 1) & 0x55555555);
    $int = ($int & 0x33333333) + (($int >> 2) & 0x33333333);
    return (($int + ($int >> 4) & 0xF0F0F0F) * 0x1010101) >> 24;
    }

    /**
    * method validNetMask.
    * Determine if a string is a valid netmask.
    * Usage:
    * CIDR::validNetMask('255.255.252.0');
    * CIDR::validNetMask('127.0.0.1');
    * Result:
    * bool(true)
    * bool(false)
    * @param $netmask String a 1pv4 formatted ip address.
    * @see http://www.actionsnip.com/snippets/tomo_atlacatl/calculate-if-\
    * a-netmask-is-valid--as2-
    * @access public
    * @static
    * return bool True if a valid netmask.
    */
    public static function validNetMask($netmask){
    $netmask = ip2long($netmask);
    $neg = ((~(int)$netmask) & 0xFFFFFFFF);
    return (($neg + 1) & $neg) === 0;
    }

    /**
    * method maskToCIDR.
    * Return a CIDR block number when given a valid netmask.
    * Usage:
    * CIDR::maskToCIDR('255.255.252.0');
    * Result:
    * int(22)
    * @param $netmask String a 1pv4 formatted ip address.
    * @access public
    * @static
    * @return int CIDR number.
    */
    public static function maskToCIDR($netmask){
    if(self::validNetMask($netmask)){
    return self::countSetBits(ip2long($netmask));
    }
    else {
    throw new Exception('Invalid Netmask');
    }
    }

    /**
    * method alignedCIDR.
    * It takes an ip address and a netmask and returns a valid CIDR
    * block.
    * Usage:
    * CIDR::alignedCIDR('127.0.0.1','255.255.252.0');
    * Result:
    * string(12) "127.0.0.0/22"
    * @param $ipinput String a IPv4 formatted ip address.
    * @param $netmask String a 1pv4 formatted ip address.
    * @access public
    * @static
    * @return String CIDR block.
    */
    public static function alignedCIDR($ipinput,$netmask){
    $alignedIP = long2ip((ip2long($ipinput)) & (ip2long($netmask)));
    return "$alignedIP/" . self::maskToCIDR($netmask);
    }

    /**
    * method IPisWithinCIDR.
    * Check whether an IP is within a CIDR block.
    * Usage:
    * CIDR::IPisWithinCIDR('127.0.0.33','127.0.0.1/24');
    * CIDR::IPisWithinCIDR('127.0.0.33','127.0.0.1/27');
    * Result:
    * bool(true)
    * bool(false)
    * @param $ipinput String a IPv4 formatted ip address.
    * @param $cidr String a IPv4 formatted CIDR block. Block is aligned
    * during execution.
    * @access public
    * @static
    * @return String CIDR block.
    */
    public static function IPisWithinCIDR($ipinput,$cidr){
    $cidr = explode('/',$cidr);
    $cidr = self::alignedCIDR($cidr[0],self::CIDRtoMask((int)$cidr[1]));
    $cidr = explode('/',$cidr);
    $ipinput = (ip2long($ipinput));
    $ip1 = (ip2long($cidr[0]));
    $ip2 = ($ip1 + pow(2, (32 - (int)$cidr[1])) - 1);
    return (($ip1 <= $ipinput) && ($ipinput <= $ip2));
    }

    /**
    * method maxBlock.
    * Determines the largest CIDR block that an IP address will fit into.
    * Used to develop a list of CIDR blocks.
    * Usage:
    * CIDR::maxBlock("127.0.0.1");
    * CIDR::maxBlock("127.0.0.0");
    * Result:
    * int(32)
    * int(8)
    * @param $ipinput String a IPv4 formatted ip address.
    * @access public
    * @static
    * @return int CIDR number.
    */
    public static function maxBlock($ipinput) {
    return self::maskToCIDR(long2ip(-(ip2long($ipinput) & -(ip2long($ipinput)))));
    }

    /**
    * method rangeToCIDRList.
    * Returns an array of CIDR blocks that fit into a specified range of
    * ip addresses.
    * Usage:
    * CIDR::rangeToCIDRList("127.0.0.1","127.0.0.34");
    * Result:
    * array(7) {
    * [0]=> string(12) "127.0.0.1/32"
    * [1]=> string(12) "127.0.0.2/31"
    * [2]=> string(12) "127.0.0.4/30"
    * [3]=> string(12) "127.0.0.8/29"
    * [4]=> string(13) "127.0.0.16/28"
    * [5]=> string(13) "127.0.0.32/31"
    * [6]=> string(13) "127.0.0.34/32"
    * }
    * @param $startIPinput String a IPv4 formatted ip address.
    * @param $startIPinput String a IPv4 formatted ip address.
    * @see http://null.pp.ru/src/php/Netmask.phps
    * @return Array CIDR blocks in a numbered array.
    */
    public static function rangeToCIDRList($startIPinput,$endIPinput=NULL) {
    $start = ip2long($startIPinput);
    $end =(empty($endIPinput))?$start:ip2long($endIPinput);
    while($end >= $start) {
    $maxsize = self::maxBlock(long2ip($start));
    $maxdiff = 32 - intval(log($end - $start + 1)/log(2));
    $size = ($maxsize > $maxdiff)?$maxsize:$maxdiff;
    $listCIDRs[] = long2ip($start) . "/$size";
    $start += pow(2, (32 - $size));
    }
    return $listCIDRs;
    }

    /**
    * method cidrToRange.
    * Returns an array of only two IPv4 addresses that have the lowest ip
    * address as the first entry. If you need to check to see if an IPv4
    * address is within range please use the IPisWithinCIDR method above.
    * Usage:
    * CIDR::cidrToRange("127.0.0.128/25");
    * Result:
    * array(2) {
    * [0]=> string(11) "127.0.0.128"
    * [1]=> string(11) "127.0.0.255"
    * }
    * @param $cidr string CIDR block
    * @return Array low end of range then high end of range.
    */
    public static 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;
    }
    }