从CIDR前缀PHP5计算IPv6的范围是多少?(PHP5 calculate IPv6 range

2019-06-23 12:21发布

我能够用IPv4和其他在线资源的代码片断做到这一点。 我想知道是否有办法使用IPv6做到这一点。

基本上,我只需要我可以进入IPv6地址和前缀的形式(例如:地址/ 68),并计算出网络地址,第一可用地址,最后可用的地址和广播地址。 然后,只需打印到屏幕。 并不想将其存储在数据库或任何东西。

我将如何去这样做呢?

感谢大家提前!

Answer 1:

首先:IPv6没有网络和广播地址。 您可以使用所有地址的前缀。 第二:在LAN前缀长度总是(当然,时间99.x%)A / 64。 路由/ 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


Answer 2:

对于那些在这个问题谁绊倒,你可以在此更有效地利用做dtr_ptondtr_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 - mike@bakeryphp.com
 * @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 - mike@bakeryphp.com
 * @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::");

让您的网络和广播:

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"


Answer 3:

那么,对于后人,我将我的code在这里。 同时也可作为一个感谢你们谁帮我钉下来,因为我需要它对于IPv6 / ip2country脚本

稍微由@ mikemacintosh和@ 桑德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的专家很远。



文章来源: PHP5 calculate IPv6 range from cidr prefix?