2012-04-10 3 views
5

Я могу сделать это с помощью IPv4, используя фрагменты кода из различных онлайн-источников. Мне было интересно, есть ли способ сделать это с помощью IPv6.PHP5 вычисляет диапазон IPv6 от префикса cidr?

В принципе, мне нужна форма, в которой я могу ввести адрес и префикс IPv6 (например: адрес/68), и он вычисляет сетевой адрес, первый полезный адрес, последний полезный адрес и широковещательный адрес. Затем просто печатает на экране. Не хотите хранить его в базе данных или что-то еще.

Как мне это сделать?

Спасибо всем!

ответ

5

Прежде всего: IPv6 не имеет сетевых и широковещательных адресов. Вы можете использовать все адреса в префиксе. Второе: в локальной сети длина префикса всегда (ну, 99.x% времени) a/64. Маршрутизация a/68 приведет к поломке функций IPv6, таких как автоматическая настройка без сохранения состояния.

Ниже многословная реализация приставки калькулятора 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"; 

?> 

Он должен вывести:

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

Именно то, что мне было нужно !!. Я пытаюсь обернуть голову вокруг этого в течение нескольких дней, и сейчас я нахожусь в курсе cisco, но мы еще не достигли ipv6, но пытались понять концепцию самостоятельно. Я смотрю на код, сколько потребуется изменить код, если я также захочу второй IP-адрес и IP-адрес перед последним? Я пытался понять это, но из моего объема знаний о php :( – Damainman

+0

Ну, второй адрес имеет 1 вместо 0 в качестве последнего символа, а второй для последнего имеет e вместо f. –

+0

PS : если вам нравится ответ, пожалуйста, отметьте его как принято :-) –

2

Для тех, кто наткнуться на этот вопрос, вы можете сделать это более эффективно использовать dtr_pton и dtr_ntop функции и dTRIP класс найден на GitHub.

Мы также заметили отсутствие фокуса и инструментов с IPv6 в PHP и собрали эту статью, http://www.highonphp.com/5-tips-for-working-with-ipv6-in-php, что может помочь другим.

Функция Источник

Это преобразует и IP в двоичном представлении:

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

Это преобразует двоичное представление для печати 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; 
} 

Примеры

Использование dtr_pton функции вы можете:

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

Получите вашу сеть и Broadcast:

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

И ваш вывод будет:

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

IPv6 не поддерживает широковещательную рассылку, поэтому нет понятия широковещательного адреса. –

+0

@ Steve-o Точно, но поскольку термин «широковещательная передача» используется в сети очень долго, подчеркивая диапазон IPv6, людям легче понять. Это похоже на сегмент подсети, но многие сети распространяются далеко за этот сегмент. –

+0

Ссылка на сообщение в блоге мертва. Можете ли вы обновить или удалить его? –

0

Ну, для потомков, я добавив мой номер code. А также спасибо вам, ребята, которые помогли мне прибить это, поскольку мне это было нужно для ipv6/ip2country script.

Это немного вдохновило кода размещено здесь @mikemacintosh и @Sander Steffann, немного улучшилось (whishful мышления) и возвращает хороший объект упаковок всех данных вы/не нужны:

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

Мне нравятся функции и классы и не нравятся коды, которые нельзя использовать повторно, где реализована функция повторного использования.

PS: Если у вас есть проблемы с ним, пожалуйста, свяжитесь со мной. Я далеко от эксперта по IPv6.

Смежные вопросы