2011-10-31 11 views
6

¿Hay una buena manera de hacer coincidir una dirección IPv6 con una subred IPv6 usando la notación CIDR? Lo que estoy buscando es el IPv6 equivalente a esto: Matching an IP to a CIDR mask in PHP 5?Coincidencia de la dirección IPv6 con una subred CIDR

El ejemplo anterior no puede ser utilizado desde una dirección IPv6 es de 128 bits de longitud, la prevención de la izquierda en modo bit-shift funcione correctamente. ¿Puedes pensar de otra manera?

EDITAR: Agregué mi propia solución a la lista de respuestas.

Respuesta

8

Dado que no se puede convertir direcciones IPv6 a un entero, que debe operar pedacitos, así:

$ip='21DA:00D3:0000:2F3B:02AC:00FF:FE28:9C5A'; 
$cidrnet='21DA:00D3:0000:2F3B::/64'; 

// converts inet_pton output to string with bits 
function inet_to_bits($inet) 
{ 
    $unpacked = unpack('A16', $inet); 
    $unpacked = str_split($unpacked[1]); 
    $binaryip = ''; 
    foreach ($unpacked as $char) { 
      $binaryip .= str_pad(decbin(ord($char)), 8, '0', STR_PAD_LEFT); 
    } 
    return $binaryip; 
}  

$ip = inet_pton($ip); 
$binaryip=inet_to_bits($ip); 

list($net,$maskbits)=explode('/',$cidrnet); 
$net=inet_pton($net); 
$binarynet=inet_to_bits($net); 

$ip_net_bits=substr($binaryip,0,$maskbits); 
$net_bits =substr($binarynet,0,$maskbits); 

if($ip_net_bits!==$net_bits) echo 'Not in subnet'; 
else echo 'In subnet'; 

Además, si se utiliza alguna base de datos para almacenar direcciones IP, es posible que ya tenga todas las funciones para comparar ellos. Por ejemplo, Postgres tiene un tipo de inet y puede determinar, si la IP está contenida dentro de la subred de esta manera:

SELECT 
    '21DA:00D3:0000:2F3B:02AC:00FF:FE28:9C5A'::inet << 
    '21DA:00D3:0000:2F3B::/64'::inet; 

9.11. Network Address Functions and Operators in PostgreSQL

+0

Niza - Nunca consideré cadenas binarias por alguna razón . –

1

he creado mi propia solución, usando el siguiente código:

function iPv6MaskToByteArray($subnetMask) { 
    $addr = str_repeat("f", $subnetMask/4); 
    switch ($subnetMask % 4) { 
    case 0: 
     break; 
    case 1: 
     $addr .= "8"; 
     break; 
    case 2: 
     $addr .= "c"; 
     break; 
    case 3: 
     $addr .= "e"; 
     break; 
    } 
    $addr = str_pad($addr, 32, '0'); 
    $addr = pack("H*" , $addr); 
    return $addr; 
} 

function iPv6CidrMatch($address, $subnetAddress, $subnetMask) { 
    $binMask = iPv6MaskToByteArray($subnetMask); 
    return ($address & $binMask) == $subnetAddress; 
} 

Tenga en cuenta que $ address y $ subnetAddress se obtuvieron al ejecutar la dirección de cadena a través de inet_pton. Llame a la función de la siguiente manera:

$subnet = inet_pton("2001:06b8::"); 
$mask = 32; 
$addr = inet_pton("2001:06b8:0000:0000:0000:0000:1428:07ab"); 
$match = iPv6CidrMatch($addr, $subnet, $mask); // TRUE 
+0

¡Esa es una buena también! – Snifff

2

Si sus máscaras son siempre divisibles por cuatro (lo cual es bastante común en ipv6). Se puede utilizar:

function checkIPv6WithinRange($ipv6, $range) { 
    list ($net, $mask) = preg_split("/\//", $range); 

    if ($mask % 4) 
     throw new NotImplementedException("Only masks divisible by 4 are supported"); 
    $stripChars = (128-$mask)/4; 

    $hexNet = bin2hex(inet_pton($net)); 
    $reducedNet = substr($hexNet, 0, 0 - $stripChars); 

    $hexIp = bin2hex(inet_pton($ipv6)); 
    $reducedIp = substr($hexIp, 0, 0 - $stripChars); 

    return $reducedIp === $reducedNet; 
} 
+0

Tengo un montón de máscaras que no son divisibles por 4 - _at home_! –

+0

Simplificación: substr ($ hexNet, 0, $ mask/4). De esa manera funciona en ipv4 también :) –

2

También puede utilizar la clase de IpUtilssymfony/http-foundation paquete:

IpUtils::checkIp6('2a01:8760:2:3001::1', '2a01:8760:2:3001::1/64') 

Esto se compruebe la validez y rango partido IPv6. Devolverá false si no es el caso.

0

Aquí es un ejemplo que funciona mediante el control de una dirección IP con una lista de direcciones IP o CIDRs individuales, IPv4 e IPv6:

https://gist.github.com/lyquix-owner/2620da22d927c99d57555530aab3279b

<?php 
// IP to check 
$ip_check = $_SERVER['REMOTE_ADDR']; 

// Array of allowed IPs and subnets, both IPv4 and IPv6 
$ips_allowed = array(
    '192.30.252.0/22' 
    '2620:112:3000::/44' 
    '192.168.16.104' 
); 

// Flag for IP match allowed list 
$ip_match = false; 

foreach($ips_allowed as $ip_allow) { 
    // If IP has/means CIDR notation 
    if(strpos($ip_allow, '/') === false) { 
     // Check Single IP 
     if(inet_pton($ip_check) == inet_pton($ip_allow)) { 
      $allow = true; 
      break; 
     } 
    } 
    else { 
     // Check IP range 
     list($subnet, $bits) = explode('/', $ip_allow); 

     // Convert subnet to binary string of $bits length 
     $subnet = unpack('H*', inet_pton($subnet)); // Subnet in Hex 
     foreach($subnet as $i => $h) $subnet[$i] = base_convert($h, 16, 2); // Array of Binary 
     $subnet = substr(implode('', $subnet), 0, $bits); // Subnet in Binary, only network bits 

     // Convert remote IP to binary string of $bits length 
     $ip = unpack('H*', inet_pton($ip_check)); // IP in Hex 
     foreach($ip as $i => $h) $ip[$i] = base_convert($h, 16, 2); // Array of Binary 
     $ip = substr(implode('', $ip), 0, $bits); // IP in Binary, only network bits 

     // Check network bits match 
     if($subnet == $ip) { 
      $allow = true; 
      break; 
     } 
    } 
} 
if(!$allow) { 
    die('IP not allowed'); 
} 
Cuestiones relacionadas