Php ipv6 to int

PHP IPv6 to 128 bit int

Two functions to convert IPv6 & IPv4 addresses into 128 bit ints and back again.

They use the BCMath extension if installed or the pure PHP Math_BigInteger class (included in the ZIP) if not.

/** * Converts human readable representation to a 128 bit int * which can be stored in MySQL using DECIMAL(39,0). * * Requires PHP to be compiled with IPv6 support. * This could be made to work without IPv6 support but * I don't think there would be much use for it if PHP * doesn't support IPv6. * * @param string $ip IPv4 or IPv6 address to convert * @return string 128 bit string that can be used with DECIMNAL(39,0) or false */ if(!function_exists('inet_ptoi')) < function inet_ptoi($ip) < // make sure it is an ip if (filter_var($ip, FILTER_VALIDATE_IP) === false) return false; $parts = unpack('N*', inet_pton($ip)); // fix IPv4 if (strpos($ip, '.') !== false) $parts = array(1=>0, 2=>0, 3=>0, 4=>$parts[1]); foreach ($parts as &$part) < // convert any unsigned ints to signed from unpack. // this should be OK as it will be a PHP float not an int if ($part < 0) $part += 4294967296; >// Use BCMath if available if (function_exists('bcadd')) < $decimal = $parts[4]; $decimal = bcadd($decimal, bcmul($parts[3], '4294967296')); $decimal = bcadd($decimal, bcmul($parts[2], '18446744073709551616')); $decimal = bcadd($decimal, bcmul($parts[1], '79228162514264337593543950336')); >// Otherwise use the pure PHP BigInteger class else < $decimal = new Math_BigInteger($parts[4]); $part3 = new Math_BigInteger($parts[3]); $part2 = new Math_BigInteger($parts[2]); $part1 = new Math_BigInteger($parts[1]); $decimal = $decimal->add($part3->multiply(new Math_BigInteger('4294967296'))); $decimal = $decimal->add($part2->multiply(new Math_BigInteger('18446744073709551616'))); $decimal = $decimal->add($part1->multiply(new Math_BigInteger('79228162514264337593543950336'))); $decimal = $decimal->toString(); > return $decimal; > > /** * Converts a 128 bit int to a human readable representation. * * Requires PHP to be compiled with IPv6 support. * This could be made to work without IPv6 support but * I don't think there would be much use for it if PHP * doesn't support IPv6. * * @param string $decimal 128 bit int * @return string IPv4 or IPv6 */ if(!function_exists('inet_itop')) < function inet_itop($decimal) < $parts = array(); // Use BCMath if available if (function_exists('bcadd')) < $parts[1] = bcdiv($decimal, '79228162514264337593543950336', 0); $decimal = bcsub($decimal, bcmul($parts[1], '79228162514264337593543950336')); $parts[2] = bcdiv($decimal, '18446744073709551616', 0); $decimal = bcsub($decimal, bcmul($parts[2], '18446744073709551616')); $parts[3] = bcdiv($decimal, '4294967296', 0); $decimal = bcsub($decimal, bcmul($parts[3], '4294967296')); $parts[4] = $decimal; >// Otherwise use the pure PHP BigInteger class else < $decimal = new Math_BigInteger($decimal); list($parts[1],) = $decimal->divide(new Math_BigInteger('79228162514264337593543950336')); $decimal = $decimal->subtract($parts[1]->multiply(new Math_BigInteger('79228162514264337593543950336'))); list($parts[2],) = $decimal->divide(new Math_BigInteger('18446744073709551616')); $decimal = $decimal->subtract($parts[2]->multiply(new Math_BigInteger('18446744073709551616'))); list($parts[3],) = $decimal->divide(new Math_BigInteger('4294967296')); $decimal = $decimal->subtract($parts[3]->multiply(new Math_BigInteger('4294967296'))); $parts[4] = $decimal; $parts[1] = $parts[1]->toString(); $parts[2] = $parts[2]->toString(); $parts[3] = $parts[3]->toString(); $parts[4] = $parts[4]->toString(); > foreach ($parts as &$part) < // convert any signed ints to unsigned for pack // this should be fine as it will be treated as a float if ($part >2147483647) $part -= 4294967296; > $ip = inet_ntop(pack('N4', $parts[1], $parts[2], $parts[3], $parts[4])); // fix IPv4 by removing :: from the beginning if (strpos($ip, '.') !== false) return substr($ip, 2); return $ip; > > 
echo inet_ptoi('::FFFF:FFFF:FFFF:FFFF') . "\n"; echo inet_ptoi('::FFFF:FFFF') . "\n"; echo inet_ptoi('255.255.255.255') . "\n"; echo inet_itop(inet_ptoi('::FFFF:FFFF:FFFF:FFFF')) . "\n"; echo inet_itop(inet_ptoi('::FFFF:FFFF')) . "\n"; 
18446744073709551615 4294967295 4294967295 ::ffff:ffff:ffff:ffff 255.255.255.255 

License: MIT license (not really big or complex enough to need a license but for people that want one)
Download: PHP IPv6 to 128 bit ints

Читайте также:  Php скрипт пароль на страницу

Note
If you just want to store the IP addresses in MySQL you can store them as a VARBINARY(16) and use the built in PHP inet_pton and inet_ntop functions. They will convert IPv6 and IPv4 addresses into binary and back again.

Comments

© 2022 Sam Clarke Code is licensed under the MIT, unless specified otherwise.

Источник

IntSystem.org

Случаи из опыта разработки различных WEB проектов. Интересные факты, статьи, впечатления. Программирование и все о нем в сфере WEB.

Как работать с IPv6 в PHP

Работа с IPv6 в PHP

Вольный перевод:
https://www.mikemackintosh.com/5-tips-for-working-with-ipv6-in-php/

Работать с IPv4 в php было очень просто, для этого существовало две функции ip2long и long2ip. Эти функции давали возможность переводить IP адрес в обычное число и обратно. К сожалению, в IPv6 такой возможности нет.

Адрес IPv4 состоит из 32 бит — размер который изначально поддерживает большинство операционных систем и языков программирования. 32-ух и более битные платформы поддерживают работу с числами от 0 до 4 294 967 295 (или 2 32 , которое и является максимальным количеством IP адресов в сети). Это давало возможность преобразовывать адрес в число, что в свою очередь давало возможность экономично использовать память и ресурсы системы.

С IPv6 же другая история. На данный момент большинство компьютеров используют 64-ех битную архитектуру, и работают под управлением 64-ех битной ОС. Самое большое беззнаковое число допустимое на 64bit платформе — 18 446 744 073 709 551 616 (или 2 64 ). Но IPv6 допускает несоизмеримо больше количество адресов — 340 282 366 920 938 463 463 374 607 431 770 000 000 (или 2 128 ). Это очень огромное число, и, к сожалению, это приводит к проблемам при работе с ним.

Работа с таким количеством битов в числе является громоздким даже для современных языков программирования, и из за проблем работы с памятью на таких числах алгоритмы поддержки IPv6 обычно неправильны.

Прим.автора: Обратите внимание: IPv6 не имеет широковещательных адресов (broadcast). В IPv4 последний адрес в диапазоне зарезервированы для трансляции. В IPv6, нет концепции широковещания, вместо этого используется «многоадресная рассылка» по локальной ссылке для всех нод, ff02::1 .

Валидация IPv4 и IPv6.

Это самая простая задача из всех возможных. Некоторые из программистов используют конструкции strpos( $ip , «:») для определения IPv6, и substr_count( $ip , «.») == 3 для проверки IPv4 адреса.

Читайте также:  Php обработка ошибок от mysql

Но это в корне неправильно. Во первых такие проверки небезопасны, по причине того что легко обходятся. А во вторых они просто неточны. Так например, эти функции не смогут правильно обработать адреса IPv6, записанные в IPv4 совместимом формате, такие как, например, этот ::127.0.0.1 или этот ::ffff:10.10.1.1 .

В php есть функция filter_var , которая осуществляет проверку входящих данных. В нашем случае, для валидации IP адреса, следует использовать фильтр FILTER_VALIDATE_IP . А для определения версии — флаги FILTER_FLAG_IPV4 или FILTER_FLAG_IPV6 . Например:

Источник

Php convert ipv6 to number

$ip_2 is not a valid IPv6 address. You need «::» somewhere in there, to indicate the zero omission point.

$ip_2='2001::11ff:ffff:f'; $ip_2='2001:11ff::ffff:f'; $ip_2='2001:11ff:ffff::f'; 

then inet_pton() should work fine.

As already hinted, PHP doesn’t have a 128 integer type, so the best you can get is a numeric string out of the binary string inet_pton() gives you. yes, that’s what it is, and that’s why it looks weird. If you look at the bits of that strings, you’ll see they’re exactly what you’d expect.

Here’s how to expand the binary string into a numeric string (argument «0» was missing from str_pad() originally):

/** * @param string $ip A human readable IPv4 or IPv6 address. * @return string Decimal number, written out as a string due to limits on the size of int and float. */ function ipv6_numeric($ip) < $binNum = ''; foreach (unpack('C*', inet_pton($ip)) as $byte) < $binNum .= str_pad(decbin($byte), 8, "0", STR_PAD_LEFT); >// $binNum is now a human readable string, but in binary. // If you have the gmp PHP extension, you can convert it to decimal return gmp_strval(gmp_init(ltrim($binNum, '0'), 2), 10); > 

Solution 2

$ip = 'fe80:0:0:0:202:b3ff:fe1e:8329'; $dec = ip2long_v6($ip); $ip2 = long2ip_v6($dec); // $ip = fe80:0:0:0:202:b3ff:fe1e:8329 // $dec = 338288524927261089654163772891438416681 // $ip2 = fe80::202:b3ff:fe1e:8329 

Functions:

With enabled GMP or BCMATH extension.

function ip2long_v6($ip) < $ip_n = inet_pton($ip); $bin = ''; for ($bit = strlen($ip_n) - 1; $bit >= 0; $bit--) < $bin = sprintf('%08b', ord($ip_n[$bit])) . $bin; >if (function_exists('gmp_init')) < return gmp_strval(gmp_init($bin, 2), 10); >elseif (function_exists('bcadd')) < $dec = '0'; for ($i = 0; $i < strlen($bin); $i++) < $dec = bcmul($dec, '2', 0); $dec = bcadd($dec, $bin[$i], 0); >return $dec; > else < trigger_error('GMP or BCMATH extension not installed!', E_USER_ERROR); >> function long2ip_v6($dec) < if (function_exists('gmp_init')) < $bin = gmp_strval(gmp_init($dec, 10), 2); >elseif (function_exists('bcadd')) < $bin = ''; do < $bin = bcmod($dec, '2') . $bin; $dec = bcdiv($dec, '2', 0); >while (bccomp($dec, '0')); > else < trigger_error('GMP or BCMATH extension not installed!', E_USER_ERROR); >$bin = str_pad($bin, 128, '0', STR_PAD_LEFT); $ip = array(); for ($bit = 0; $bit $ip = implode(':', $ip); return inet_ntop(inet_pton($ip)); > 

Solution 3

Note that all answers will result in incorrect results for big IP addresses or are going through a highly complicated process to receive the actual numbers. Retrieving the actual integer value from an IPv6 address requires two things:

With both prerequisites in place conversion is as simple as:

$ip = 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'; $int = gmp_import(inet_pton($ip)); echo $int; // 340282366920938463463374607431768211455 

The binary numeric packed in_addr representation that is returned by inet_pton is already an integer and can directly be imported to GMP, as illustrated above. There is no need for special conversions or anything.

Читайте также:  Css атрибут не равен

Note that the other way around is equally simple:

$int = '340282366920938463463374607431768211455'; $ip = inet_ntop(str_pad(gmp_export($int), 16, "\0", STR_PAD_LEFT)); echo $ip; // ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff 

Hence building the two desired functions is as simple as:

function ipv6_to_integer($ip) < return (string) gmp_import(inet_pton($ip)); >function ipv6_from_integer($integer)

Solution 4

Here’s two functions that convert hex to decimal and decimal to hex. This only works with IPv6 hex and IPv6 integer representations (so, use ip2long() and long2ip() for IPv4 numbers). Theoretically, one could convert an IPv4 dotted notation number to hex values and use these, but that would probably be overkill.

  • Convert all complete IPv6 numbers to a stringified long int (up to 39 characters, left padded for sorting if the flag is set to true.
  • Convert a stringified «long» back to a hexidecimal IPv6 representation, left padding as necessary to the full 32 bit hex string. Colons are optionally placed, right to left, if the appropriate flag is set to true.

These can be modified to handle virtually any length hex value or virtually any length integer, and placement of colons and padding can be adjusted accordingly.

HEX to DECIMAL

 function bchexdec($hex,$padl) // Input: A hexadecimal number as a String. // Output: The equivalent decimal number as a String. // - if padl==true then pad left to fill 39 characters for string sort < if (strlen($hex) != 39) < $hex = inet6_expand($hex); if ($hex == false) return false; >$hex=str_replace(":","",$hex); $dec = 0; $len = strlen($hex); for ($i = 1; $i if ($padl==true) < $dec=str_pad($dec,39,"0",STR_PAD_LEFT); >return $dec; > 

DECIMAL to HEX

 function bcdechex($dec,$colon) // Input: A decimal as a String. // Output: The equivalent hex value. // - if $colon==true then add colons. < $hex = ''; // RFC 5952, A Recommendation for IPv6 Address Text Representation, Section 4.3 Lowercase specifies letters should be lowercase. Though that generally doesn't matter, use lowercase // to conform with the RFC for those rare systems that do so. do < $last = bcmod($dec, 16); $hex = dechex($last).$hex; $dec = bcdiv(bcsub($dec, $last), 16); >while($dec>0); $hex=str_pad($hex,32,"0",STR_PAD_LEFT); // Add colons if $colon==true if ($colon==true) < $hex=strrev($hex); $chunks=str_split($hex,4); $hex=implode(":", $chunks); $hex=strrev($hex); >return $hex; > 

This is based off of ideas and examples found in a variety of places, as well as my own needs for easily sortable and easily storable IPv6 addresses.

Solution 5

function inet6_to_int64($addr) < /* Expand the address if necessary */ if (strlen($addr) != 39) < $addr = inet6_expand($addr); if ($addr == false) return false; >// if $addr = str_replace(':', '', $addr); $p1 = '0x' . substr($addr, 0, 16); $p2 = '0x' . substr($addr, 16); $p1 = gmp_init($p1); $p2 = gmp_init($p2); $result = array(gmp_strval($p1), gmp_strval($p2)); return $result; > // inet6_to_int64() 

Источник

Оцените статью