我可以使用来自各种在线资源的代码片段在IPv4上完成此操作。我想知道是否有一种方法可以在IPv6上完成此操作。
基本上,我只需要一个表单,可以输入IPv6地址和前缀(例如:f080:42d2:581a::0/68),然后它会计算网络地址、第一个可用地址、最后一个可用地址和广播地址。然后只需打印到屏幕上。暂时不需要将其存储在数据库或其他任何地方。
<?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
这是对得到认可的答案的修复,该答案错误地假设“第一个地址”应与输入的字符串相同。相反,需要通过使用其掩码的AND运算符进行修改来修改其值。
为了演示问题,考虑以下示例输入:2001:db8:abc:1403::/54
First: 2001:db8:abc:1400::
实际结果:
First: 2001:db8:abc:1403::
计算给定4位序列的掩码所需的相关数学如下:
// Calculate the subnet mask. min() prevents the comparison from being negative
$mask = 0xf << (min(4, $flexbits));
// AND the original against its mask
$newval = $origval & $mask;
<?php
/*
* This is definitely not the fastest way to do it!
*/
// An example prefix
$prefix = '2001:db8:abc:1403::/54';
// Split in address and prefix length
list($addr_given_str, $prefixlen) = explode('/', $prefix);
// Parse the address into a binary string
$addr_given_bin = inet_pton($addr_given_str);
// Convert the binary string to a string with hexadecimal characters
$addr_given_hex = bin2hex($addr_given_bin);
// Overwriting first address string to make sure notation is optimal
$addr_given_str = inet_ntop($addr_given_bin);
// Calculate the number of 'flexible' bits
$flexbits = 128 - $prefixlen;
// Build the hexadecimal strings of the first and last addresses
$addr_hex_first = $addr_given_hex;
$addr_hex_last = $addr_given_hex;
// We start at the end of the string (which is always 32 characters long)
$pos = 31;
while ($flexbits > 0) {
// Get the characters at this position
$orig_first = substr($addr_hex_first, $pos, 1);
$orig_last = substr($addr_hex_last, $pos, 1);
// Convert them to an integer
$origval_first = hexdec($orig_first);
$origval_last = hexdec($orig_last);
// First address: calculate the subnet mask. min() prevents the comparison from being negative
$mask = 0xf << (min(4, $flexbits));
// AND the original against its mask
$new_val_first = $origval_first & $mask;
// Last address: OR it with (2^flexbits)-1, with flexbits limited to 4 at a time
$new_val_last = $origval_last | (pow(2, min(4, $flexbits)) - 1);
// Convert them back to hexadecimal characters
$new_first = dechex($new_val_first);
$new_last = dechex($new_val_last);
// And put those character back in their strings
$addr_hex_first = substr_replace($addr_hex_first, $new_first, $pos, 1);
$addr_hex_last = substr_replace($addr_hex_last, $new_last, $pos, 1);
// We processed one nibble, move to previous position
$flexbits -= 4;
$pos -= 1;
}
// Convert the hexadecimal strings to a binary string
$addr_bin_first = hex2bin($addr_hex_first);
$addr_bin_last = hex2bin($addr_hex_last);
// And create an IPv6 address from the binary string
$addr_str_first = inet_ntop($addr_bin_first);
$addr_str_last = inet_ntop($addr_bin_last);
// Report to user
echo "Prefix: $prefix\n";
echo "First: $addr_str_first\n";
echo "Last: $addr_str_last\n";
输出:
Prefix: 2001:db8:abc:1403::/54
First: 2001:db8:abc:1400::
Last: 2001:db8:abc:17ff:ffff:ffff:ffff:ffff
dtr_pton
和dtr_ntop
函数以及GitHub上找到的dTRIP
类来完成。/**
* 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;
}
/**
* 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"
为了记录,我在这里添加我的代码
。同时也要感谢帮助我完成这个ipv6/ip2country脚本的人们。
这段代码受到@mikemacintosh和@Sander Steffann发布的代码的一些启发,并稍作改进(仅有愿望)。它会返回一个包含您需要或不需要的所有数据的对象:
/**
* 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;
}
public static function rangeToCIDRv6($startIP, $endIP) {
//转换成bin字符串格式
$startBinary = inet_pton($startIP);
$startBin = '';
$bits = 15;
while($bits >= 0) {
$bin = sprintf("%08b",(ord($startBinary[$bits])));
$startBin = $bin.$startBin;
$bits--;
}
$endBinary = inet_pton($endIP);
$endBin = '';
$bits = 15;
while($bits >= 0) {
$bin = sprintf("%08b",(ord($endBinary[$bits])));
$endBin = $bin.$endBin;
$bits--;
}
//按位查询
$cidrArray = array();
$mask = 127;
$diffFirst = false;
while($mask>0){
//位数不同
if($startBin[$mask]!=$endBin[$mask]){
while($startBin[$mask]!=$endBin[$mask]){
if($diffFirst){
$ipBin = str_pad(substr($startBin, 0, $mask).'1', 128, '0', STR_PAD_RIGHT);
$ip = '';
$Offset = 0;
while ($Offset <= 7) {
$bin_part = substr($ipBin, ($Offset*16), 16);
$ip .= dechex(bindec($bin_part));
if($Offset !=7){
$ip .= ":";
}
$Offset++;
}
$cidrArray[] = inet_ntop(inet_pton($ip)).'/'.($mask+1);
}
$mask--;
}
//首次不同
if($diffFirst==false){
$diffFirst = true;
$ipBin = str_pad(substr($startBin, 0, $mask).'1', 128, '0', STR_PAD_RIGHT);
$ip = '';
$Offset = 0;
while ($Offset <= 7) {
$bin_part = substr($ipBin, ($Offset*16), 16);
$ip .= dechex(bindec($bin_part));
if($Offset !=7){
$ip .= ":";
}
$Offset++;
}
$cidrArray[] = inet_ntop(inet_pton($ip)).'/'.($mask+1);
}
}else{
$mask--;
}
}
return $cidrArray;
}