Matching an IP to a CIDR mask in PHP 5?

前端 未结 13 1086
无人共我
无人共我 2020-11-28 04:04

I\'m looking for quick/simple method for matching a given IP4 dotted quad IP to a CIDR notation mask.

I have a bunch of IPs I need to see if they match a range of IP

相关标签:
13条回答
  • 2020-11-28 04:37

    In a similar situation, I ended up using symfony/http-foundation.

    When using this package, your code would look like:

    $ips = array('10.2.1.100', '10.2.1.101', '10.5.1.100', '1.2.3.4');
    
    foreach($ips as $IP) {
        if (\Symfony\Component\HttpFoundation\IpUtils::checkIp($IP, '10.2.0.0/16')) {
            print "you're in the 10.2 subnet\n";
        }
    }
    

    It also handles IPv6.

    Link: https://packagist.org/packages/symfony/http-foundation

    0 讨论(0)
  • 2020-11-28 04:37
    function cidr_match($ipStr, $cidrStr) {
      $ip = ip2long($ipStr);
      $cidrArr = split('/',$cidrStr);
      $maskIP = ip2long($cidrArr[0]);
      $maskBits = 32 - $cidrArr[1];
      return (($ip>>$maskBits) == ($maskIP>>$maskBits));
    }
    
    0 讨论(0)
  • 2020-11-28 04:37

    Just a note, Alnitak's answer works 32/64 bit.

    Here is a cooked version of it, for quick spam protection based on country IP lists that you can get everywhere. google for country ip list or country ip block (Have to give one here, really difficult to find it in that sites page navigation:Country ip block generator)

    Copy-paste your cidr ip list to string $cidrs. And put this code just before page html, possibly in the header.php file.

    Can also be used to filter adsense use in page templates based on country.

    This is only a in-the-middle-of-the-night-urgency solution. Sometimes one needs to come up with something like this for a client quickly yesterday, so here it is.

    //++++++++++++++++++++++
    //COUNTRY SPAM PROTECTOR
    //speed: ~5ms @ 2000 cidrs
    //comments start with #
    //++++++++++++++++++++++
    $cidrs=
    '
    #yourcountry
    1.3.4.5/21
    #mycountry
    6.7.8.9/20
    ';
    //$cidrs.="\n".'123.12.12.12/32';//test, your ip
    $cidrs_ar=preg_split('/\s+/',$cidrs,-1,PREG_SPLIT_NO_EMPTY);
    $ip=@$_SERVER['REMOTE_ADDR'];
    $iplong=ip2long($ip);
    //var_export($cidrs_ar);var_export($ip);var_export($iplong);
    if($iplong)
      foreach($cidrs_ar as $cidr)
        {
        $ar=explode ('/', $cidr);
        $netiplong=ip2long($ar[0]);
        if($netiplong===false) continue;
        $mask=intval(@$ar[1]);
        if(!$mask) continue;
        $bitmask=-1 <<(32-$mask);
        if(($iplong & $bitmask) == ($netiplong & $bitmask))
            {
            header('Location: http://www.someotherwebsite.com/',true,303);
            exit;
            }
        }
    
    0 讨论(0)
  • 2020-11-28 04:43

    I also needed to test IP's against CIDR masks. I've found a website with excellent explanation and sourcecode which works perfectly well.

    The website http://pgregg.com/blog/2009/04/php-algorithms-determining-if-an-ip-is-within-a-specific-range/

    Because the website can one day cease to exist, here is the code

    <?php
    
    /*
     * ip_in_range.php - Function to determine if an IP is located in a
     *                   specific range as specified via several alternative
     *                   formats.
     *
     * Network ranges can be specified as:
     * 1. Wildcard format:     1.2.3.*
     * 2. CIDR format:         1.2.3/24  OR  1.2.3.4/255.255.255.0
     * 3. Start-End IP format: 1.2.3.0-1.2.3.255
     *
     * Return value BOOLEAN : ip_in_range($ip, $range);
     *
     * Copyright 2008: Paul Gregg <pgregg@pgregg.com>
     * 10 January 2008
     * Version: 1.2
     *
     * Source website: http://www.pgregg.com/projects/php/ip_in_range/
     * Version 1.2
     *
     * This software is Donationware - if you feel you have benefited from
     * the use of this tool then please consider a donation. The value of
     * which is entirely left up to your discretion.
     * http://www.pgregg.com/donate/
     *
     * Please do not remove this header, or source attibution from this file.
     */
    
    
    // decbin32
    // In order to simplify working with IP addresses (in binary) and their
    // netmasks, it is easier to ensure that the binary strings are padded
    // with zeros out to 32 characters - IP addresses are 32 bit numbers
    Function decbin32 ($dec) {
      return str_pad(decbin($dec), 32, '0', STR_PAD_LEFT);
    }
    
    // ip_in_range
    // This function takes 2 arguments, an IP address and a "range" in several
    // different formats.
    // Network ranges can be specified as:
    // 1. Wildcard format:     1.2.3.*
    // 2. CIDR format:         1.2.3/24  OR  1.2.3.4/255.255.255.0
    // 3. Start-End IP format: 1.2.3.0-1.2.3.255
    // The function will return true if the supplied IP is within the range.
    // Note little validation is done on the range inputs - it expects you to
    // use one of the above 3 formats.
    Function ip_in_range($ip, $range) {
      if (strpos($range, '/') !== false) {
        // $range is in IP/NETMASK format
        list($range, $netmask) = explode('/', $range, 2);
        if (strpos($netmask, '.') !== false) {
          // $netmask is a 255.255.0.0 format
          $netmask = str_replace('*', '0', $netmask);
          $netmask_dec = ip2long($netmask);
          return ( (ip2long($ip) & $netmask_dec) == (ip2long($range) & $netmask_dec) );
        } else {
          // $netmask is a CIDR size block
          // fix the range argument
          $x = explode('.', $range);
          while(count($x)<4) $x[] = '0';
          list($a,$b,$c,$d) = $x;
          $range = sprintf("%u.%u.%u.%u", empty($a)?'0':$a, empty($b)?'0':$b,empty($c)?'0':$c,empty($d)?'0':$d);
          $range_dec = ip2long($range);
          $ip_dec = ip2long($ip);
    
          # Strategy 1 - Create the netmask with 'netmask' 1s and then fill it to 32 with 0s
          #$netmask_dec = bindec(str_pad('', $netmask, '1') . str_pad('', 32-$netmask, '0'));
    
          # Strategy 2 - Use math to create it
          $wildcard_dec = pow(2, (32-$netmask)) - 1;
          $netmask_dec = ~ $wildcard_dec;
    
          return (($ip_dec & $netmask_dec) == ($range_dec & $netmask_dec));
        }
      } else {
        // range might be 255.255.*.* or 1.2.3.0-1.2.3.255
        if (strpos($range, '*') !==false) { // a.b.*.* format
          // Just convert to A-B format by setting * to 0 for A and 255 for B
          $lower = str_replace('*', '0', $range);
          $upper = str_replace('*', '255', $range);
          $range = "$lower-$upper";
        }
    
        if (strpos($range, '-')!==false) { // A-B format
          list($lower, $upper) = explode('-', $range, 2);
          $lower_dec = (float)sprintf("%u",ip2long($lower));
          $upper_dec = (float)sprintf("%u",ip2long($upper));
          $ip_dec = (float)sprintf("%u",ip2long($ip));
          return ( ($ip_dec>=$lower_dec) && ($ip_dec<=$upper_dec) );
        }
    
        echo 'Range argument is not in 1.2.3.4/24 or 1.2.3.4/255.255.255.0 format';
        return false;
      }
    
    }
    ?>
    

    (I did not develop this; this is developed by Paul Gregg (http://pgregg.com/)

    0 讨论(0)
  • 2020-11-28 04:43

    I want to have you look at my few lines. The examples that people suggested before me don't seem to work. One reason being, as far as I understand it, is that CIDR mask bits are binary numbers, so the bit shift must be done on a binary number. I have tried converting the long IP's into binaries, but ran into a max binary number limit. OK, here my few lines ... I await your comments.

    function cidr_match($ipStr, $cidrStr) {
    
    $ipStr = explode('.', $ipStr);
    foreach ($ipStr as $key => $val) {
        $ipStr[$key] = str_pad(decbin($val), 8, '0', STR_PAD_LEFT);
        }
    $ip = '';
    foreach ($ipStr as $binval) {
        $ip = $ip . $binval;
        }
    
    $cidrArr = explode('/',$cidrStr);
    
    $maskIP = explode('.', $cidrArr[0]);
    foreach ($maskIP as $key => $val) {
        $maskIP[$key] = str_pad(decbin($val), 8, '0', STR_PAD_LEFT);
        }
    $maskIP = '';
    foreach ($ipStr as $binval) {
        $maskIP = $maskIP . $binval;
        }
    $maskBits = 32 - $cidrArr[1];
    return (($ip>>$maskBits) == ($maskIP>>$maskBits));  
    }
    
    0 讨论(0)
  • 2020-11-28 04:45

    My technique uses bit to bit matching using subnet and mask.

    function cidr_match($ip, $range){
        list ($subnet, $bits) = explode('/', $range);
        $ip = substr(IP2bin($ip),0,$bits) ;
        $subnet = substr(IP2Bin($subnet),0,$bits) ;
        return ($ip == $subnet) ;
    }
    
    function IP2Bin($ip){
        $ipbin = '';
        $ips = explode(".",$ip) ;
        foreach ($ips as $iptmp){
            $ipbin .= sprintf("%08b",$iptmp) ;
        }
        return $ipbin ;
    }
    
    0 讨论(0)
提交回复
热议问题