
Damainman
Ich kann dies mit IPv4 tun, indem ich Code-Snippets aus verschiedenen Online-Quellen verwende. Ich habe mich gefragt, ob es eine Möglichkeit gibt, dies mit IPv6 zu tun.
Im Grunde brauche ich nur ein Formular, in das ich eine IPv6-Adresse und ein Präfix eingeben kann (z. B.: f080:42d2:581a::0/68), und es berechnet die Netzwerkadresse, die erste verwendbare Adresse, die letzte verwendbare Adresse und die Broadcast-Adresse. Dann druckt einfach auf den Bildschirm. Ich möchte es noch nicht in einer Datenbank oder so speichern.
Zunächst einmal: IPv6 hat keine Netzwerk- und Broadcast-Adressen. Sie können alle Adressen in einem Präfix verwenden. Zweitens: In einem LAN ist die Präfixlänge immer (naja, 99,x% der Zeit) ein /64. Das Routing von /68 würde IPv6-Funktionen wie die zustandslose automatische Konfiguration beeinträchtigen.
Nachfolgend finden Sie eine ausführliche Implementierung eines IPv6-Präfixrechners:
<?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("https://stackoverflow.com/", $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";
?>
Es sollte ausgeben:
Prefix: 2001:db8:abc:1400::/54
First: 2001:db8:abc:1400::
Last: 2001:db8:abc:17ff:ffff:ffff:ffff:ffff
Für diejenigen, die auf diese Frage stoßen, können Sie dies effektiver tun, indem Sie die verwenden dtr_pton
und dtr_ntop
Funktionen u dTRIP
Klasse gefunden auf GitHub.
Wir haben auch einen Mangel an Fokus und Tools mit IPv6 in PHP festgestellt und diesen Artikel zusammengestellt, http://www.highonphp.com/5-tips-for-working-with-ipv6-in-phpwas für andere hilfreich sein kann.
Funktionsquelle
Dies konvertiert und IP in eine binäre Darstellung:
/**
* 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;
}
Dies wandelt eine binäre Darstellung in druckbare IP um:
/**
* 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;
}
Beispiele
Verwendung der dtr_pton
Funktion können Sie:
$ip = dtr_pton("fe80:1:2:3:a:bad:1dea:dad");
$mask = dtr_pton("ffff:ffff:ffff:ffff:ffff:fff0::");
Holen Sie sich Ihr Netzwerk und senden Sie:
var_dump( dtr_ntop( $ip & $mask ) );
var_dump( dtr_ntop( $ip | ~ $mask ) );
Und Ihre Ausgabe wäre:
string(18) "fe80:1:2:3:a:ba0::"
string(26) "fe80:1:2:3:a:baf:ffff:ffff"
Dies ist ein Fix für die akzeptierte Antwort, die fälschlicherweise davon ausgeht, dass die “erste Adresse” mit der eingegebenen Zeichenfolge identisch sein sollte. Vielmehr muss es seinen Wert haben modifiziert über eine AND
Operator gegen seine Maske.
Um das Problem zu demonstrieren, betrachten Sie diese Beispieleingabe: 2001:db8:abc:1403::/54
Erwartetes Ergebnis:
First: 2001:db8:abc:1400::
Tatsächliche Ergebnis:
First: 2001:db8:abc:1403::
Die relevante Mathematik zur Berechnung der Maske für eine gegebene 4-Bit-Sequenz lautet:
// 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;
Vollständiger Code
<?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("https://stackoverflow.com/", $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";
Ausgänge:
Prefix: 2001:db8:abc:1403::/54
First: 2001:db8:abc:1400::
Last: 2001:db8:abc:17ff:ffff:ffff:ffff:ffff

CodeWütend
Nun, für die Nachwelt füge ich meine hinzu code
hier. Und auch als Dankeschön an euch, die mir geholfen haben, das festzunageln, da ich es für eine brauchte ipv6/ip2country-Skript.
Es ist leicht inspiriert per Code hier gepostet von @mikemacintosh und @Sander Stefanleicht verbessert (Wunschdenken) und gibt ein schönes Objekt zurück, das alle Daten enthält, die Sie benötigen / nicht benötigen:
/**
* 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;
}
Ich mag Funktionen und Klassen und mag keinen nicht wiederverwendbaren Code, in dem wiederverwendbare Funktionen implementiert sind.
PS: Wenn Sie Probleme damit finden, wenden Sie sich bitte an mich. Ich bin weit davon entfernt, ein IPv6-Experte zu sein.
10119600cookie-checkEinen IPv6-Bereich aus einem CIDR-Präfix berechnen?yes