2011-10-31 11 views
6

Esiste un buon metodo per associare un indirizzo IPv6 a una subnet IPv6 utilizzando la notazione CIDR? Quello che sto cercando è l'IPv6 equivalente a questo: Matching an IP to a CIDR mask in PHP 5?Corrispondenza dell'indirizzo IPv6 a una subnet CIDR

L'esempio di cui sopra non può essere utilizzato poiché un indirizzo IPv6 è lungo 128 bit, impedendo il bit shift sinistro di funzionare correttamente. Puoi pensare in un altro modo?

MODIFICA: aggiunta la mia soluzione all'elenco di risposte.

risposta

8

Dal momento che non è possibile convertire indirizzi IPv6 a intero, si dovrebbe operare bit, in questo modo:

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

Inoltre, se si utilizzano alcuni database per memorizzare indirizzi IP, potrebbe già avere tutte le funzioni per confrontarli. Ad esempio, Postgres ha un tipo di inet e in grado di determinare, se IP è contenuto all'interno di sottorete in questo modo:

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

Nizza - non ho mai considerato stringhe binarie per qualche motivo . –

1

ho creato la mia soluzione, utilizzando il seguente codice:

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

Si noti che $ indirizzo e $ subnetAddress sono stati ottenuti eseguendo l'indirizzo di stringa tramite inet_pton. Chiamare la funzione nel seguente modo:

$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

Anche questo è buono! – Snifff

2

Se le maschere sono sempre divisibili per quattro (che è abbastanza comune in IPv6). È possibile utilizzare:

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

Ho un sacco di maschere che non sono divisibili per 4 - _at home_! –

+0

Semplificazione: substr ($ hexNet, 0, $ mask/4). In questo modo funziona anche su ipv4 :) –

2

È possibile anche utilizzare la classe IpUtils da symfony/http-foundation pacchetto:

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

Questa verificherà la validità e la gamma partita IPv6. Restituirà false se non è il caso.

0

Ecco un esempio che funziona da controllo di un indirizzo IP con un elenco di singoli indirizzi IP o CIDRs, sia IPv4 che 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'); 
}