Überprüfen Sie, ob ein CIDR-Subnetz eine IP-Adresse enthält

Lesezeit: 13 Minuten

Benutzer-Avatar
Überschwamm

Ich suche nach einer schnellen/einfachen Methode, um eine bestimmte gepunktete IP4-Quad-IP mit einer CIDR-Notationsmaske abzugleichen.

Ich habe eine Reihe von IPs, die ich überprüfen muss, ob sie mit einer Reihe von IPs übereinstimmen.

Beispiel:

$ips = array('10.2.1.100', '10.2.1.101', '10.5.1.100', '1.2.3.4');

foreach ($ips as $IP) {
    if (cidr_match($IP, '10.2.0.0/16') == true) {
        print "you're in the 10.2 subnet\n"; 
    }
}

Was würde cidr_match() aussehen?

Es muss nicht wirklich einfach sein, aber schnell wäre gut. Alles, was nur integrierte/allgemeine Funktionen verwendet, ist ein Bonus (da ich wahrscheinlich eine Person dazu bringen werde, mir etwas in Pear zu zeigen, das dies tut, aber ich kann mich nicht darauf verlassen, dass Pear oder dieses Paket dort installiert ist, wo mein Code ist eingesetzt).

  • Informationen zum Umgang mit IPv6-Adressen finden Sie unter stackoverflow.com/questions/7951061

    – Sean die Bohne

    20. März 2018 um 15:22 Uhr

Benutzer-Avatar
Alnitak

Wenn Sie nur IPv4 verwenden:

  • verwenden ip2long() um die IPs und den Subnetzbereich in Long Integers umzuwandeln
  • Konvertieren Sie /xx in eine Subnetzmaske
  • machen Sie ein bitweises ‘and’ (dh ip & mask)’ und überprüfen Sie, ob ‘result = subnet’

so etwas sollte funktionieren:

function cidr_match($ip, $range)
{
    list ($subnet, $bits) = explode("https://stackoverflow.com/", $range);
    if ($bits === null) {
        $bits = 32;
    }
    $ip = ip2long($ip);
    $subnet = ip2long($subnet);
    $mask = -1 << (32 - $bits);
    $subnet &= $mask; # nb: in case the supplied subnet wasn't correctly aligned
    return ($ip & $mask) == $subnet;
}

  • Wenn Sie Ihre C#-Version gepostet haben, können wir vielleicht herausfinden, warum?

    – Alnitak

    6. Mai 2009 um 22:59 Uhr

  • @Guillaume – bist du dir da sicher? der wichtige Teil ist, dass die korrekten niedrigstwertigen Bits Null sind. Die extra hohen Bits, die Sie auf einem 64-Bit-Computer erhalten würden, würden ignoriert.

    – Alnitak

    8. April 2011 um 7:15 Uhr

  • Dies funktioniert nicht auf 32-Bit-Systemen: match(1.2.3.4, 0.0.0.0/0) gibt false zurück, sollte true zurückgeben

    – Jaka Jančar

    25. Juni 2011 um 12:29 Uhr

  • @JakaJančar bist du sicher, dass dein Test gültig ist? Vielleicht hast du die Funktion falsch verstanden

    – Decebal

    17. Dezember 2012 um 8:36 Uhr

  • @rinogo wäre wahrscheinlich eine bessere Lösung zu setzen $bits = 32 wenn es keine gäbe /.

    – Alnitak

    6. November 2018 um 2:24 Uhr


Benutzer-Avatar
David Veszelovski

In einer ähnlichen Situation habe ich symfony/http-foundation verwendet.

Wenn Sie dieses Paket verwenden, würde Ihr Code wie folgt aussehen:

$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";
    }
}

Es verarbeitet auch IPv6.

Verknüpfung: https://packagist.org/packages/symfony/http-foundation

  • Vielen Dank! Irgendwie funktioniert die Symfony-Lösung in meinem Fall korrekt, während eine akzeptierte – nicht.

    – Xardas

    21. Januar 2015 um 11:46 Uhr


  • Das liegt daran, dass PHP, Composer und Symphony damals, als die Frage 2009 gestellt wurde, sehr unterschiedlich waren

    – Überschwamm

    14. Januar 2016 um 17:30 Uhr

  • Dies ist die beste Antwort IMO.

    – Tek

    28. April 2016 um 12:22 Uhr

  • Vielen Dank, dass Sie uns über diese – ziemlich verdeckte, aber nützliche – Utility-Methode informiert haben. Es gibt jedoch einen Fehler in Ihrem Code, das zweite Argument für die IpUtils::checkIp-Methode ist eine fest codierte Zeichenfolge, die iterierten ips werden nicht verwendet.

    – Rafark

    14. Dezember 2019 um 23:31 Uhr

  • Dies ist der Weg, wenn Sie ein Symphonie-basiertes Framework wie Laravel verwenden

    – darryn.ten

    27. März 2021 um 4:11 Uhr

Benutzer-Avatar
Samuel Parkinson

Ich fand, dass viele dieser Methoden nach PHP 5.2 kaputt gingen. Die folgende Lösung funktioniert jedoch auf Version 5.2 und höher:

function cidr_match($ip, $cidr)
{
    list($subnet, $mask) = explode("https://stackoverflow.com/", $cidr);

    if ((ip2long($ip) & ~((1 << (32 - $mask)) - 1) ) == ip2long($subnet))
    { 
        return true;
    }

    return false;
}

Beispielergebnisse

cidr_match("1.2.3.4", "0.0.0.0/0"):         true
cidr_match("127.0.0.1", "127.0.0.1/32"):    true
cidr_match("127.0.0.1", "127.0.0.2/32"):    false

Quelle http://www.php.net/manual/en/function.ip2long.php#82397.

  • cidr_match("1.2.3.4", "0.0.0.0/0") gibt auf meinem Rechner false zurück (PHP 5.5.13, Windows x64).

    – Simon Osten

    20. Mai 2016 um 6:03 Uhr


  • @Samuel Parkinson, nur damit Sie wissen, dass auf Ihrer Antwort eine Bibliothek aufgebaut ist: github.com/tholu/php-cidr-match. Der Autor hat die Anerkennung gegeben.

    – Bhargav Nanekalva

    9. Dezember 2017 um 7:20 Uhr

  • Beachten Sie, dass wenn $cidr enthält keine /gibt diese Funktion immer wahr zurück (seit $mask wird sein NULL). Mit anderen Worten, diese Funktion braucht so etwas wie if($mask === null) return false; Ebenfalls, $subnet sollte verifiziert werden, dass es sich zumindest um eine gültige IP-Adresse handelt. Aufgrund dieser beiden Probleme etwas Dummes wie cidr_match("8.8.8.8", "blah"); wird zurückkehren true.

    – Rinogo

    5. November 2018 um 19:52 Uhr


Einige Funktionen geändert:

  • Split mit explodieren

function cidr_match($ip, $range)
{
    list ($subnet, $bits) = explode("https://stackoverflow.com/", $range);
    $ip = ip2long($ip);
    $subnet = ip2long($subnet);
    $mask = -1 << (32 - $bits);
    $subnet &= $mask; 
    return ($ip & $mask) == $subnet;
}

Benutzer-Avatar
Php’Regex

Hier ist eine schnelle 64-Bit-Funktion, um dies zu tun. Bitte kommentieren Sie die Rückgabezeile, die Sie nicht benötigen. Akzeptieren von gültigem IPv4 mit oder ohne gültigem CIDR-Routing-Präfix, z. B. 63.161.156.0/24 oder 63.161.156.0

<?php
function cidr2range($ipv4){
if ($ip=strpos($ipv4,"https://stackoverflow.com/"))
{$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;
}

Um schnell zu überprüfen, ob eine gegeben ist ipv4 stimmt mit einem bestimmten CIDR überein Sie können dies wie in diesem Beispiel inline tun

<?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;
}

Zu erhalten Sie die volle Bandbreite Als Array für eine bestimmte IP (mit oder ohne CIDR-Routing-Präfix) können Sie den folgenden Code verwenden, aber seien Sie vorsichtig, da beispielsweise 25.25.25.25/16 ein Array mit 65536 Elementen zurückgibt und Ihnen mit einem kleineren Routing leicht der Speicher ausgehen kann Präfix

<?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);

Um schnell zu überprüfen, ob eine gegeben ist ipv4 stimmt mit einem bestimmten IP-Array überein (mit oder ohne CIDR-Routing-Präfix)

<?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';

Um schnell zu laufen, überprüft die Funktion die Eingabe nicht, aber formal sollte es eine Zeichenfolge sein, die der folgenden Regex entspricht

#^(?:((?: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]))))?)$#

Wenn Sie wollen überprüfen Sie die Eingabe bevor Sie die Funktion verwenden

<?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);
}

Dann das Formale Antwort auf deine Frage ist das Folgende

<?php
#Requiring cidr2range shown above function
function cidr_match($mixed_ip,$mixed_cidr){
if (!is_array($mixed_ip)){
$string_mode=true;
$mixed_ip=[$mixed_ip=>0];
}
else $mixed_ip=array_fill_keys($mixed_ip,0);
if (!is_array($mixed_cidr)) $mixed_cidr=[$mixed_cidr];
foreach($mixed_ip   as $ip => &$result)
foreach($mixed_cidr as $cidr)
{
if(($range=cidr2range($cidr)) &&
($check=ip2long($ip))!==false &&
$check>=$range[0] && $check<=$range[1]){
$result=$cidr;
break;
}
}
$mixed_ip=array_filter($mixed_ip);
return $string_mode?($mixed_ip?true:false):$mixed_ip;
}

print '<pre>';

#Your example
$ips = array('10.2.1.100', '10.2.1.101', '10.5.1.100', '1.2.3.4');

foreach ($ips as $IP) {
    if (cidr_match($IP, '10.2.0.0/16') == true) {
        print "you're in the 10.2 subnet\n"; 
    }
}


#Also working with IP array and/or CIDR array
#If IP array is given then return an array containing IP (keys) matching CIDR (values)
$result=cidr_match($ips,['20.2.0.0/16','10.2.0.0/15']);
foreach($result as $ip => $cidr){
print "$ip is in the $cidr subnet\n"; 
}

Sie können anhand dieser Beispiele Ihre eigene Funktion kompilieren, ich hoffe, diese wenigen Zeilen haben Ihnen geholfen …

  • Sehr umfassende Lösung. Funktioniert ohne Probleme. Tausend Dank!

    – Iwan

    20. April 2020 um 13:31 Uhr


Benutzer-Avatar
Youssef

Meine Technik verwendet Bit-zu-Bit-Abgleich mit Subnetz und Maske.

function cidr_match($ip, $range){
    list ($subnet, $bits) = explode("https://stackoverflow.com/", $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 ;
}

  • Sehr umfassende Lösung. Funktioniert ohne Probleme. Tausend Dank!

    – Iwan

    20. April 2020 um 13:31 Uhr


Benutzer-Avatar
erik404

Ich musste auch IPs gegen CIDR-Masken testen. Ich habe eine Website mit ausgezeichneter Erklärung und Quellcode gefunden, die perfekt funktioniert.

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

Da die Website eines Tages nicht mehr existieren kann, hier der 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 <[email protected]>
 * 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, "https://stackoverflow.com/") !== false) {
    // $range is in IP/NETMASK format
    list($range, $netmask) = explode("https://stackoverflow.com/", $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;
  }

}
?>

(Ich habe das nicht entwickelt; das wurde von Paul Gregg entwickelt (http://pgregg.com/)

1018740cookie-checkÜberprüfen Sie, ob ein CIDR-Subnetz eine IP-Adresse enthält

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy