2012-04-10 42 views
5

Jestem w stanie to zrobić z IPv4 przy użyciu fragmentów kodu z różnych źródeł internetowych. Zastanawiam się, czy istnieje sposób, aby to zrobić z IPv6.PHP5 obliczyć zakres IPv6 z prefiksu cidr?

Zasadniczo potrzebuję tylko formularza, w którym mogę wprowadzić adres IPv6 i prefiks (np. Adres/68) i oblicza adres sieciowy, pierwszy adres użytkowy, ostatni adres użytkowy i adres rozgłoszeniowy. Następnie po prostu drukuje na ekranie. Nie chce przechowywać go w bazie danych ani nic takiego.

Jak miałbym to zrobić?

Dziękuję wszystkim z góry!

Odpowiedz

5

Przede wszystkim: IPv6 nie ma adresów sieciowych i rozgłoszeniowych. Możesz użyć wszystkich adresów w przedrostku. Po drugie: w sieci LAN długość prefiksu zawsze wynosi (dobrze, 99.x% czasu) a/64. Routing a/68 złamie funkcje IPv6, takie jak automatyczna konfiguracja bezstanowa.

Poniżej jest gadatliwy implementacja kalkulatora prefiksu IPv6:

<?php 

/* 
* This is definitely not the fastest way to do it! 
*/ 

// An example prefix 
$prefix = '2001:db8:abc:1400::/54'; 

// Split in address and prefix length 
list($firstaddrstr, $prefixlen) = explode('/', $prefix); 

// Parse the address into a binary string 
$firstaddrbin = inet_pton($firstaddrstr); 

// Convert the binary string to a string with hexadecimal characters 
# unpack() can be replaced with bin2hex() 
# unpack() is used for symmetry with pack() below 
$firstaddrhex = reset(unpack('H*', $firstaddrbin)); 

// Overwriting first address string to make sure notation is optimal 
$firstaddrstr = inet_ntop($firstaddrbin); 

// Calculate the number of 'flexible' bits 
$flexbits = 128 - $prefixlen; 

// Build the hexadecimal string of the last address 
$lastaddrhex = $firstaddrhex; 

// We start at the end of the string (which is always 32 characters long) 
$pos = 31; 
while ($flexbits > 0) { 
    // Get the character at this position 
    $orig = substr($lastaddrhex, $pos, 1); 

    // Convert it to an integer 
    $origval = hexdec($orig); 

    // OR it with (2^flexbits)-1, with flexbits limited to 4 at a time 
    $newval = $origval | (pow(2, min(4, $flexbits)) - 1); 

    // Convert it back to a hexadecimal character 
    $new = dechex($newval); 

    // And put that character back in the string 
    $lastaddrhex = substr_replace($lastaddrhex, $new, $pos, 1); 

    // We processed one nibble, move to previous position 
    $flexbits -= 4; 
    $pos -= 1; 
} 

// Convert the hexadecimal string to a binary string 
# Using pack() here 
# Newer PHP version can use hex2bin() 
$lastaddrbin = pack('H*', $lastaddrhex); 

// And create an IPv6 address from the binary string 
$lastaddrstr = inet_ntop($lastaddrbin); 

// Report to user 
echo "Prefix: $prefix\n"; 
echo "First: $firstaddrstr\n"; 
echo "Last: $lastaddrstr\n"; 

?> 

Należy wyjściowa:

Prefix: 2001:db8:abc:1400::/54 
First: 2001:db8:abc:1400:: 
Last: 2001:db8:abc:17ff:ffff:ffff:ffff:ffff 
+0

Dokładnie to, czego potrzebowałem !!. Od kilku dni próbuję to obejść, a obecnie jestem na kursie Cisco, ale nie osiągnęliśmy jeszcze ipv6, więc próbuję zrozumieć tę koncepcję na własną rękę. Szukałem kodu, ile zmiany kodu należałoby zrobić, gdybym chciała również drugiego IP i IP tuż przed ostatnim? Próbowałem to rozgryźć, ale poza moim zakresem php wiedzy :( – Damainman

+0

Cóż, drugi adres ma 1 zamiast 0 jako ostatniego znaku, a drugi do ostatniego ma e zamiast f. –

+0

PS : jeśli podoba ci się odpowiedź, proszę oznaczyć ją jako zaakceptowaną :-) –

2

Dla tych, którzy potknie to pytanie, można to zrobić bardziej efektywnie za pomocą dtr_pton i dtr_ntop funkcje i klasa dTRIP znaleziona na GitHub.

Zauważyliśmy także brak skupienia i narzędzi z IPv6 w PHP i razem z tym artykułem, http://www.highonphp.com/5-tips-for-working-with-ipv6-in-php, które mogą być pomocne dla innych.

funkcji źródłowej

To konwertuje i IP binarnej reprezentacji:

/** 
* dtr_pton 
* 
* Converts a printable IP into an unpacked binary string 
* 
* @author Mike Mackintosh - [email protected] 
* @param string $ip 
* @return string $bin 
*/ 
function dtr_pton($ip){ 

    if(filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)){ 
     return current(unpack("A4", inet_pton($ip))); 
    } 
    elseif(filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)){ 
     return current(unpack("A16", inet_pton($ip))); 
    } 

    throw new \Exception("Please supply a valid IPv4 or IPv6 address"); 

    return false; 
} 

ten przekształca binarną reprezentację do wydruku IP:

/** 
* dtr_ntop 
* 
* Converts an unpacked binary string into a printable IP 
* 
* @author Mike Mackintosh - [email protected] 
* @param string $str 
* @return string $ip 
*/ 
function dtr_ntop($str){ 
    if(strlen($str) == 16 OR strlen($str) == 4){ 
     return inet_ntop(pack("A".strlen($str) , $str)); 
    } 

    throw new \Exception("Please provide a 4 or 16 byte string"); 

    return false; 
} 

Przykłady

Korzystanie z funkcji dtr_pton można:

$ip = dtr_pton("fe80:1:2:3:a:bad:1dea:dad"); 
$mask = dtr_pton("ffff:ffff:ffff:ffff:ffff:fff0::"); 

zdobycie Network i Broadcast:

var_dump(dtr_ntop($ip & $mask)); 
var_dump(dtr_ntop($ip | ~ $mask)); 

A twoje wyjście będzie:

string(18) "fe80:1:2:3:a:ba0::" 
string(26) "fe80:1:2:3:a:baf:ffff:ffff" 
+2

IPv6 nie obsługuje emisji, dlatego nie ma koncepcji adresu rozgłoszeniowego. –

+0

@ Steve-o Dokładnie, ale ponieważ termin broadcast został użyty w sieci przez bardzo długi czas, gdy kładzie się nacisk na zakres w IPv6, jest on łatwiejszy do zrozumienia. Jest to podobne do segmentu podsieci, ale wiele sieci znacznie wyprzedza ten segment. –

0

Cóż, dla potomności, jestem dodając tutaj moją code. A także jako podziękowania dla was, którzy pomogli mi przygwoździć to, ponieważ potrzebowałem tego dla ipv6/ip2country script.

To nieco inspirowane kodem zamieszczone tutaj przez @mikemacintosh i @Sander Steffann, nieznacznie poprawiła (whishful myśląca) i zwraca piękny obiekt opakowania wszystkie dane, które są/nie potrzeba:

/** 
* This: 
* <code> 
* Ipv6_Prefix2Range('2001:43f8:10::/48'); 
* </code> 
* returns this: 
* <code> 
* object(stdClass)#2 (4) { 
* ["Prefix"]=> 
* string(17) "2001:43f8:10::/48" 
* ["FirstHex"]=> 
* string(32) "200143f8001000000000000000000000" 
* ["LastHex"]=> 
* string(32) "200143f80010ffffffffffffffffffff" 
* ["MaskHex"]=> 
* string(32) "ffffffffffff00000000000000000000" 
* // Optional bin equivalents available 
* } 
* </code> 
* 
* Tested against: 
* @link https://www.ultratools.com/tools/ipv6CIDRToRange 
* 
* @param string $a_Prefix 
* @param bool $a_WantBins 
* @return object 
*/ 
function Ipv6_Prefix2Range($a_Prefix, $a_WantBins = false){ 
    // Validate input superficially with a RegExp and split accordingly 
    if(!preg_match('~^([0-9a-f:]+)[[:punct:]]([0-9]+)$~i', trim($a_Prefix), $v_Slices)){ 
     return false; 
    } 
    // Make sure we have a valid ipv6 address 
    if(!filter_var($v_FirstAddress = $v_Slices[1], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)){ 
     return false; 
    } 
    // The /## end of the range 
    $v_PrefixLength = intval($v_Slices[2]); 
    if($v_PrefixLength > 128){ 
     return false; // kind'a stupid :) 
    } 
    $v_SuffixLength = 128 - $v_PrefixLength; 

    // Convert the binary string to a hexadecimal string 
    $v_FirstAddressBin = inet_pton($v_FirstAddress); 
    $v_FirstAddressHex = bin2hex($v_FirstAddressBin); 

    // Build the hexadecimal string of the network mask 
    // (if the manually formed binary is too large, base_convert() chokes on it... so we split it up) 
    $v_NetworkMaskHex = str_repeat('1', $v_PrefixLength) . str_repeat('0', $v_SuffixLength); 
    $v_NetworkMaskHex_parts = str_split($v_NetworkMaskHex, 8); 
    foreach($v_NetworkMaskHex_parts as &$v_NetworkMaskHex_part){ 
     $v_NetworkMaskHex_part = base_convert($v_NetworkMaskHex_part, 2, 16); 
     $v_NetworkMaskHex_part = str_pad($v_NetworkMaskHex_part, 2, '0', STR_PAD_LEFT); 
    } 
    $v_NetworkMaskHex = implode(null, $v_NetworkMaskHex_parts); 
    unset($v_NetworkMaskHex_part, $v_NetworkMaskHex_parts); 
    $v_NetworkMaskBin = inet_pton(implode(':', str_split($v_NetworkMaskHex, 4))); 

    // We have the network mask so we also apply it to First Address 
    $v_FirstAddressBin &= $v_NetworkMaskBin; 
    $v_FirstAddressHex = bin2hex($v_FirstAddressBin); 

    // Convert the last address in hexadecimal 
    $v_LastAddressBin = $v_FirstAddressBin | ~$v_NetworkMaskBin; 
    $v_LastAddressHex = bin2hex($v_LastAddressBin); 

    // Return a neat object with information 
    $v_Return = array(
     'Prefix' => "{$v_FirstAddress}/{$v_PrefixLength}", 
     'FirstHex' => $v_FirstAddressHex, 
     'LastHex' => $v_LastAddressHex, 
     'MaskHex' => $v_NetworkMaskHex, 
    ); 
    // Bins are optional... 
    if($a_WantBins){ 
     $v_Return = array_merge($v_Return, array(
      'FirstBin' => $v_FirstAddressBin, 
      'LastBin' => $v_LastAddressBin, 
      'MaskBin' => $v_NetworkMaskBin, 
     )); 
    } 
    return (object)$v_Return; 
} 

Lubię funkcje i klasy i nie lubię kod wielokrotnego użytku, gdzie wielokrotnego użytku funkcjonalność jest realizowana.

PS: Jeśli napotkasz problemy, proszę o kontakt. Nie jestem ekspertem od IPv6.