63 votes

Faire correspondre une IP à un masque CIDR en php5 ?

Je cherche une méthode rapide/simple/(peut-être intégrée que je n'ai jamais remarquée auparavant), pour faire correspondre une IP4 quadruple pointillée donnée à un masque en notation CIDR.

J'ai un tas d'IPs dont j'ai besoin pour voir si elles correspondent à une gamme d'IPs.

exemple :

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

résoudre pour cidr_match()

il n'est pas nécessaire que ce soit simple, mais rapide serait bien. tout ce qui n'utilise que des fonctions intégrées/communes est un bonus (car il est probable qu'une personne me montre quelque chose dans pear qui fait cela, mais je ne peux pas dépendre du fait que pear ou ce paquet soit installé là où mon code est déployé)

0 votes

Pour la gestion des adresses IPv6, voir stackoverflow.com/questions/7951061

98voto

Alnitak Points 143355

Si vous utilisez uniquement IPv4 :

  • utiliser ip2long() pour convertir les IPs et la gamme de sous-réseau en nombres entiers longs.
  • convertir le /xx en un masque de sous-réseau
  • faire un "and" au sens du bit (c'est-à-dire ip & mask) et vérifier que le "résultat = subnet".

quelque chose comme ça devrait fonctionner :

function cidr_match($ip, $range)
{
    list ($subnet, $bits) = explode('/', $range);
    $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;
}

0 votes

Peut-être que si vous postez votre version C#, nous pourrons comprendre pourquoi ?

3 votes

L'important, c'est que les bits de poids faible corrects soient nuls. Les bits de poids fort supplémentaires que vous obtiendriez sur une machine 64 bits seraient ignorés.

0 votes

@Alnitak - désolé, vous avez tout à fait raison. J'ai déployé ma propre fonction inspirée par votre soumission et elle était nécessaire dans la mienne pour une raison quelconque. Mes excuses pour le bruit.

47voto

Sam Points 860

J'ai constaté que la plupart de ces méthodes ne fonctionnent plus après PHP 5.2. Cependant, la solution suivante fonctionne sur les versions 5.2 et supérieures :

function cidr_match($ip, $cidr)
{
    list($subnet, $mask) = explode('/', $cidr);

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

    return false;
}

Exemples de résultats

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

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

0 votes

cidr_match("1.2.3.4", "0.0.0.0/0") renvoie false sur ma machine (PHP 5.5.13, Windows x64).

1 votes

@Samuel Parkinson, juste pour que tu saches qu'il y a une bibliothèque construite sur ta réponse : github.com/tholu/php-cidr-match . L'auteur a donné le crédit.

0 votes

Notez que si $cidr ne contient pas de / cette fonction retournera toujours vrai (puisque $mask sera NULL ). En d'autres termes, cette fonction a besoin de quelque chose comme if($mask === null) return false; Aussi, $subnet doit être vérifié pour être au moins une adresse IP valide. En raison de ces deux problèmes, quelque chose de stupide comme cidr_match("8.8.8.8", "blah"); retournera true .

5voto

decebal Points 558

Certaines fonctions ont été modifiées :

  • diviser avec exploser

function cidr_match($ip, $range)
{
    list ($subnet, $bits) = explode('/', $range);
    $ip = ip2long($ip);
    $subnet = ip2long($subnet);
    $mask = -1 << (32 - $bits);
    $subnet &= $mask; 
    return ($ip & $mask) == $subnet;
}

0 votes

Voici une implémentation supplémentaire pour vérifier l'IP de CloudFlare par exemple : [code] $ip = $_SERVER["REMOTE_ADDR"] ; $c1 = cidr_match($ip, '204.93.240.0/24') ; $c2 = cidr_match($ip, '204.93.177.0/24') ; $c3 = etc. $cTotal = round($c1+$c2+$c3+...) ; if($cTotal < 1) die() ; [/code]

3voto

Ma technique utilise la correspondance bit à bit en utilisant le sous-réseau et le masque.

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){
    $ips = explode(".",$ip) ;
    foreach ($ips as $iptmp){
        $ipbin .= sprintf("%08b",$iptmp) ;
    }
    return $ipbin ;
}

1voto

vartec Points 53382
function cidr_match($ipStr, $cidrStr) {
  $ip = ip2long($ipStr);
  $cidrArr = split('/',$cidrStr);
  $maskIP = ip2long($cidrArr[0]);
  $maskBits = 32 - $cidrArr[1];
  return (($ip>>$maskBits) == ($maskIP>>$maskBits));
}

2 votes

Cela ne fonctionnera pas sur les systèmes 32 bits : match(1.2.3.4, 0.0.0.0/0) renvoie false, alors qu'il devrait renvoyer true.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X