Getting list IPs from CIDR notation in PHP

前端 未结 13 1335
慢半拍i
慢半拍i 2020-12-08 05:38

Is there a way (or function/class) to get the list of IP addresses from a CIDR notation?

For example, I have 73.35.143.32/27 CIDR and want to get the list of all IP\

相关标签:
13条回答
  • 2020-12-08 06:06

    Extension to @jonavon 's answer. If you need flat list of IPs. You can convert his function like below.

    function cidrToRange($value) {
        $range = array();
        $split = explode('/', $value);
        if (!empty($split[0]) && is_scalar($split[1]) && filter_var($split[0], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
            $rangeStart = ip2long($split[0]) & ((-1 << (32 - (int)$split[1])));
            $rangeEnd = ip2long($split[0]) + pow(2, (32 - (int)$split[1])) - 1;
    
            for ($i = $rangeStart; $i <= $rangeEnd; $i++) {
                $range[] = long2ip($i);
            }
            return $range;
        } else {
            return $value;
        }
    }
    
    0 讨论(0)
  • 2020-12-08 06:06

    This iterates over each IP address in a CIDR block using an anonymous function:

    cidrl('194.168.0.1/28', $error_code, function($address) {
        // Do something.
        print "$address\n";
    });
    

    Or, as an array:

    $addresses = cidrl('194.168.0.1/28', $error_code);
    

    Source:

    function cidrl($cidr, &$error_code = 0, $callback = null) {
        $error_code = 0;
        sscanf($cidr, "%[^/]/%u", $network, $bits); 
        $addr = ip2long($network);
        if ($addr === false) {
            $error_code = 2;
            return false;
        }       
    
        if ($bits == 32) {
            if (is_callable($callback)) {
                $callback(long2ip($addr));
                return array(); 
            }           
            return array(long2ip($addr));
        }       
    
        if ($bits > 32) { 
            $error_code = 3;
            return false;
        }       
    
        $mask = ~(0xFFFFFFFF >> $bits); 
    
        $addr_start = $addr & $mask;
        $addr_end = ($addr & $mask) | ~$mask; 
    
        $addresses = array();
        for ($i = $addr_start; $i <= $addr_end; $i++) { 
            if (is_callable($callback)) $callback(long2ip($i));
            else $addresses[] = long2ip($i);
        }       
        return $addresses;
    }
    

    Code from: https://github.com/emden-norfolk/cidrl/tree/master/php

    0 讨论(0)
  • 2020-12-08 06:07

    I'll edit the aforementioned class to contain a method for that. Here is the code I came up with that might help you until then.

    function cidrToRange($cidr) {
      $range = array();
      $cidr = explode('/', $cidr);
      $range[0] = long2ip((ip2long($cidr[0])) & ((-1 << (32 - (int)$cidr[1]))));
      $range[1] = long2ip((ip2long($range[0])) + pow(2, (32 - (int)$cidr[1])) - 1);
      return $range;
    }
    var_dump(cidrToRange("73.35.143.32/27"));
    
    //////////////////OUTPUT////////////////////////
    // array(2) {
    //   [0]=>
    //   string(12) "73.35.143.32"
    //   [1]=>
    //   string(12) "73.35.143.63"
    // }
    /////////////////////////////////////////////////
    

    Returns the low end of the ip range as the first entry in the array, then returns the high end as the second entry.

    0 讨论(0)
  • 2020-12-08 06:07

    Well, it's a bitmask - 73.35.143.32/27 means that 27 bits are the network mask, and the rest is available for assigning to the nodes in the network:

    73.35.143.32
    

    in binary is this (dots shown for legibility):

    01001001.00100011.10001111.00100000
    

    The netmask is 27 bits:

    11111111.11111111.11111111.11100000
    

    So you can just AND them together and get this:

    01001001.00100011.10001111.001 00000
     network prefix (27 bits)     | node address (5 bits)
    

    From here, you can just enumerate all the combinations in the node address (00000 is 0, 11111 is 31, so a simple loop is enough), and you'll have all the available hosts.

    Converting this pseudocode to PHP is left as an exercise to the reader ;)

    Oh, and the obligatory deprecation warning: IPv4 is now full, consider also IPv6.

    0 讨论(0)
  • 2020-12-08 06:12

    There is small fix - when someone place ip prefix like: 127.0.0.15/26 this function returns bad last IP adress. In this function is at line 4 (starts: $range[1]...) changed $cidr[0] to $range[0] - now its return last IP address good for every IP in range.

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

    Before fix:

    array(2) {
      [0]=>
      string(9) "127.0.0.0"
      [1]=>
      string(10) "127.0.0.78"
    }
    string(41) "127.0.0.15/26 >> 2130706432 -> 2130706510"
    

    After fix:

    array(2) {
      [0]=>
      string(9) "127.0.0.0"
      [1]=>
      string(10) "127.0.0.63"
    }
    string(41) "127.0.0.15/26 >> 2130706432 -> 2130706495"
    
    0 讨论(0)
  • 2020-12-08 06:13

    Here is one fast 64bits function to do it, please comment the return line you don't need. Accepting any valid Ipv4 with or without valid CIDR Routing Prefix for example 63.161.156.0/24 or 63.161.156.0

    <?php
    function cidr2range($ipv4){
    if ($ip=strpos($ipv4,'/'))
    {$n_ip=(1<<(32-substr($ipv4,1+$ip)))-1;   $ip_dec=ip2long(substr($ipv4,0,$ip)); }
    else
    {$n_ip=0;                                   $ip_dec=ip2long($ipv4);             }
    $ip_min=$ip_dec&~$n_ip;
    $ip_max=$ip_min+$n_ip;
    #Array(2) of Decimal Values Range
    return [$ip_min,$ip_max];
    #Array(2) of Ipv4 Human Readable Range
    return [long2ip($ip_min),long2ip($ip_max)];
    #Array(2) of Ipv4 and Subnet Range
    return [long2ip($ip_min),long2ip(~$n_ip)];
    #Array(2) of Ipv4 and Wildcard Bits
    return [long2ip($ip_min),long2ip($n_ip)];
    #Integer Number of Ipv4 in Range
    return ++$n_ip;
    }
    

    To run fast the function don't check input but formally it should be a string matching the following regex

    #^(?:((?:0)|(?:2(?:(?:[0-4][0-9])|(?:5[0-5])))|(?:1?[0-9]{1,2}))\.((?:0)|(?:2(?:(?:[0-4][0-9])|(?:5[0-5])))|(?:1?[0-9]{1,2}))\.((?:0)|(?:2(?:(?:[0-4][0-9])|(?:5[0-5])))|(?:1?[0-9]{1,2}))\.((?:0)|(?:2(?:(?:[0-4][0-9])|(?:5[0-5])))|(?:1?[0-9]{1,2}))(?:/((?:(?:0)|(?:3[0-2])|(?:[1-2]?[0-9]))))?)$#
    

    If you want to verify the input before using the function

    <?php
    if (is_string($ipv4) && preg_match('#^(?:((?:0)|(?:2(?:(?:[0-4][0-9])|(?:5[0-5])))|(?:1?[0-9]{1,2}))\.((?:0)|(?:2(?:(?:[0-4][0-9])|(?:5[0-5])))|(?:1?[0-9]{1,2}))\.((?:0)|(?:2(?:(?:[0-4][0-9])|(?:5[0-5])))|(?:1?[0-9]{1,2}))\.((?:0)|(?:2(?:(?:[0-4][0-9])|(?:5[0-5])))|(?:1?[0-9]{1,2}))(?:/((?:(?:0)|(?:3[0-2])|(?:[1-2]?[0-9]))))?)$#',$ipv4))
    {
    #This is a valid ipv4 with or without CIDR Routing Prefix
    $result=cidr2range($ipv4);
    print_r($result);
    }
    

    To get the full range as an array for a given IP (with or without CIDR Routing Prefix) you can use the following code but be carefull because for example 25.25.25.25/16 return an array with 65536 elements and you can easily run out of memory using a smaller Routing Prefix

    <?php
    $result=cidr2range($ipv4);
    for($ip_dec=$result[0];$ip_dec<=$result[1];$ip_dec++)
    $full_range[$ip_dec]=long2ip($ip_dec);
    print_r($full_range);
    

    To fast check if a given ipv4 is matching a given CIDR you can do it inline like in this example

    <?php
    $given_cidr='55.55.55.0/24';
    $given_ipv4='55.55.55.55';
    if(($range=cidr2range($given_cidr)) &&
    ($check=ip2long($given_ipv4))!==false &&
    $check>=$range[0] && $check<=$range[1])
    {
    echo 'Yes, '.$given_ipv4.' is included in '.$given_cidr;
    }
    else
    {
    echo 'No, '.$given_ipv4.' is not included in '.$given_cidr;
    }
    

    To fast check if a given ipv4 is matching a given array of IP (with or without CIDR Routing Prefix)

    <?php
    #This code is checking if a given ip belongs to googlebot
    $given_ipv4='74.125.61.208';
    $given_cidr_array=['108.59.93.43/32','108.59.93.40/31','108.59.93.44/30','108.59.93.32/29','108.59.93.48/28','108.59.93.0/27','108.59.93.64/26','108.59.93.192/26','108.59.92.192/27','108.59.92.128/26','108.59.92.96/27','108.59.92.0/27','108.59.94.208/29','108.59.94.192/28','108.59.94.240/28','108.59.94.128/26','108.59.94.16/29','108.59.94.0/28','108.59.94.32/27','108.59.94.64/26','108.59.95.0/24','108.59.88.0/22','108.59.81.0/27','108.59.80.0/24','108.59.82.0/23','108.59.84.0/22','108.170.217.128/28','108.170.217.160/27','108.170.217.192/26','108.170.217.0/25','108.170.216.0/24','108.170.218.0/23','108.170.220.0/22','108.170.208.0/21','108.170.192.0/20','108.170.224.0/19','108.177.0.0/17','104.132.0.0/14','104.154.0.0/15','104.196.0.0/14','107.167.160.0/19','107.178.192.0/18','125.17.82.112/30','125.16.7.72/30','74.125.0.0/16','72.14.192.0/18','77.109.131.208/28','77.67.50.32/27','66.102.0.0/20','66.227.77.144/29','66.249.64.0/19','67.148.177.136/29','64.124.98.104/29','64.71.148.240/29','64.68.64.64/26','64.68.80.0/20','64.41.221.192/28','64.41.146.208/28','64.9.224.0/19','64.233.160.0/19','65.171.1.144/28','65.170.13.0/28','65.167.144.64/28','65.220.13.0/24','65.216.183.0/24','70.32.132.0/23','70.32.128.0/22','70.32.136.0/21','70.32.144.0/20','85.182.250.128/26','85.182.250.0/25','80.239.168.192/26','80.149.20.0/25','61.246.224.136/30','61.246.190.124/30','63.237.119.112/29','63.226.245.56/29','63.158.137.224/29','63.166.17.128/25','63.161.156.0/24','63.88.22.0/23','41.206.188.128/26','12.234.149.240/29','12.216.80.0/24','8.34.217.24/29','8.34.217.0/28','8.34.217.32/27','8.34.217.64/26','8.34.217.128/25','8.34.216.0/24','8.34.218.0/23','8.34.220.0/22','8.34.208.128/29','8.34.208.144/28','8.34.208.160/27','8.34.208.192/26','8.34.208.0/25','8.34.209.0/24','8.34.210.0/23','8.34.212.0/22','8.35.195.128/28','8.35.195.160/27','8.35.195.192/26','8.35.195.0/25','8.35.194.0/24','8.35.192.0/23','8.35.196.0/22','8.35.200.0/21','8.8.8.0/24','8.8.4.0/24','8.6.48.0/21','4.3.2.0/24','23.236.48.0/20','23.251.128.0/19','216.239.32.0/19','216.252.220.0/22','216.136.145.128/27','216.33.229.160/29','216.33.229.144/29','216.34.7.176/28','216.58.192.0/19','216.109.75.80/28','216.74.130.48/28','216.74.153.0/27','217.118.234.96/28','208.46.199.160/29','208.44.48.240/29','208.21.209.0/28','208.184.125.240/28','209.185.108.128/25','209.85.128.0/17','213.200.103.128/26','213.200.99.192/26','213.155.151.128/26','199.192.112.224/29','199.192.112.192/27','199.192.112.128/26','199.192.112.0/25','199.192.113.176/28','199.192.113.128/27','199.192.113.192/26','199.192.113.0/25','199.192.115.80/28','199.192.115.96/27','199.192.115.0/28','199.192.115.128/25','199.192.114.192/26','199.192.114.0/25','199.223.232.0/21','198.108.100.192/28','195.16.45.144/29','192.104.160.0/23','192.158.28.0/22','192.178.0.0/15','206.160.135.240/28','207.223.160.0/20','203.222.167.144/28','173.255.125.72/29','173.255.125.80/28','173.255.125.96/27','173.255.125.0/27','173.255.125.128/25','173.255.124.240/29','173.255.124.232/29','173.255.124.192/27','173.255.124.128/29','173.255.124.144/28','173.255.124.160/27','173.255.124.48/29','173.255.124.32/28','173.255.124.0/27','173.255.124.64/26','173.255.126.0/23','173.255.122.128/26','173.255.122.64/26','173.255.123.0/24','173.255.121.128/26','173.255.121.0/25','173.255.120.0/24','173.255.117.32/27','173.255.117.64/26','173.255.117.128/25','173.255.116.192/27','173.255.116.128/26','173.255.116.0/25','173.255.118.0/23','173.255.112.0/22','173.194.0.0/16','172.102.8.0/21','172.253.0.0/16','172.217.0.0/16','162.216.148.0/22','162.222.176.0/21','180.87.33.64/26','128.177.109.0/26','128.177.119.128/25','128.177.163.0/25','130.211.0.0/16','142.250.0.0/15','146.148.0.0/17'];
    echo '<pre>';
    $in_range=false;
    if (($given_ipv4_dec=ip2long($given_ipv4))!==false)
    {
    foreach($given_cidr_array as $given_cidr){
    if(($range=cidr2range($given_cidr)) &&
    $given_ipv4_dec>=$range[0] && $given_ipv4_dec<=$range[1])
    {
    $in_range=true;
    echo $given_ipv4.' matched '.$given_cidr.' ('.join(array_map('long2ip',$range),' - ').")\n";
    }
    }
    }
    echo $given_ipv4.' is probably'.($in_range?'':' not').' a Googlebot IP';
    

    hope that these few lines have helped you…

    0 讨论(0)
提交回复
热议问题