Work with IPv6 addresses in PHP

After a thorough search, I noticed a slight lack of functions in PHP to handle IPv6 . For my personal satisfaction, I created several features to help transition.

The IPv6ToLong() function is a temporary solution to this: How to store an IPv6-compatible address in a relational database . It will split the IP into two integers and return them to the array.

 /** * Convert an IPv4 address to IPv6 * * @param string IP Address in dot notation (192.168.1.100) * @return string IPv6 formatted address or false if invalid input */ function IPv4To6($Ip) { static $Mask = '::ffff:'; // This tells IPv6 it has an IPv4 address $IPv6 = (strpos($Ip, '::') === 0); $IPv4 = (strpos($Ip, '.') > 0); if (!$IPv4 && !$IPv6) return false; if ($IPv6 && $IPv4) $Ip = substr($Ip, strrpos($Ip, ':')+1); // Strip IPv4 Compatibility notation elseif (!$IPv4) return $Ip; // Seems to be IPv6 already? $Ip = array_pad(explode('.', $Ip), 4, 0); if (count($Ip) > 4) return false; for ($i = 0; $i < 4; $i++) if ($Ip[$i] > 255) return false; $Part7 = base_convert(($Ip[0] * 256) + $Ip[1], 10, 16); $Part8 = base_convert(($Ip[2] * 256) + $Ip[3], 10, 16); return $Mask.$Part7.':'.$Part8; } /** * Replace '::' with appropriate number of ':0' */ function ExpandIPv6Notation($Ip) { if (strpos($Ip, '::') !== false) $Ip = str_replace('::', str_repeat(':0', 8 - substr_count($Ip, ':')).':', $Ip); if (strpos($Ip, ':') === 0) $Ip = '0'.$Ip; return $Ip; } /** * Convert IPv6 address to an integer * * Optionally split in to two parts. * * @see /questions/71571/how-to-store-ipv6-compatible-address-in-a-relational-database */ function IPv6ToLong($Ip, $DatabaseParts= 2) { $Ip = ExpandIPv6Notation($Ip); $Parts = explode(':', $Ip); $Ip = array('', ''); for ($i = 0; $i < 4; $i++) $Ip[0] .= str_pad(base_convert($Parts[$i], 16, 2), 16, 0, STR_PAD_LEFT); for ($i = 4; $i < 8; $i++) $Ip[1] .= str_pad(base_convert($Parts[$i], 16, 2), 16, 0, STR_PAD_LEFT); if ($DatabaseParts == 2) return array(base_convert($Ip[0], 2, 10), base_convert($Ip[1], 2, 10)); else return base_convert($Ip[0], 2, 10) + base_convert($Ip[1], 2, 10); } 

For these functions, I usually implement them by first calling this function:

 /** * Attempt to find the client IP Address * * @param bool Should the IP be converted using ip2long? * @return string|long The IP Address */ function GetRealRemoteIp($ForDatabase= false, $DatabaseParts= 2) { $Ip = '0.0.0.0'; if (isset($_SERVER['HTTP_CLIENT_IP']) && $_SERVER['HTTP_CLIENT_IP'] != '') $Ip = $_SERVER['HTTP_CLIENT_IP']; elseif (isset($_SERVER['HTTP_X_FORWARDED_FOR']) && $_SERVER['HTTP_X_FORWARDED_FOR'] != '') $Ip = $_SERVER['HTTP_X_FORWARDED_FOR']; elseif (isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] != '') $Ip = $_SERVER['REMOTE_ADDR']; if (($CommaPos = strpos($Ip, ',')) > 0) $Ip = substr($Ip, 0, ($CommaPos - 1)); $Ip = IPv4To6($Ip); return ($ForDatabase ? IPv6ToLong($Ip, $DatabaseParts) : $Ip); } 

Someone please tell me if I am reinventing the wheel here, or I did something wrong.

This implementation converts IPv4 to IPv6. Any IPv6 address that it does not touch.

+30
php ip ipv6
Jan 14 '09 at 22:35
source share
5 answers

What about inet_ntop() ? Then, instead of chopping things into integers, you simply use varbinary(16) to save it.

+15
Jan 18 '09 at 18:46
source share

PHP.net The filter extension contains some constants for mapping IPv4 and IPv6 addresses, which can be useful for checking addresses. However, I have not seen any conversion utilities.

+1
Jan 18 '09 at 18:38
source share

You can also save the address in binary format (16) in mysql, so you should be able to output it in binary format from IPv6ToLong ().

This is really something that needs to be added initially in PHP, especially when many IPv6-enabled web servers report: FFFF: 1.2.3.4 as client IP and incompatible with ip2long, and will break a lot of things.

+1
Jan 23 '10 at 14:23
source share

Here is an alternative function using filter_var (PHP> = 5.2)

 function IPv4To6($ip) { if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === true) { if (strpos($ip, '.') > 0) { $ip = substr($ip, strrpos($ip, ':')+1); } else { //native ipv6 return $ip; } } $is_v4 = filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4); if (!$is_v4) { return false; } $iparr = array_pad(explode('.', $ip), 4, 0); $Part7 = base_convert(($iparr[0] * 256) + $iparr[1], 10, 16); $Part8 = base_convert(($iparr[2] * 256) + $iparr[3], 10, 16); return '::ffff:'.$Part7.':'.$Part8; } 
+1
Jan 25 2018-11-11T00:
source share

Returning, I wrote two functions, dtr_pton and dtr_ntop which work with both IPv4 and IPv6. It will convert them back and forth between print and binary.

The first function, dtr_pton , will check if the supplied argument is valid IPv4 or valid IPv6. An exception may be thrown or a binary IP representation may be returned depending on the result. Using this function, you can then perform AND or OR operations with respect to the result (for subnets / whatever). I would advise you to save them in my database as VARBINARY(39) or VARCHAR(39) .

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

The second function, dtr_ntop , converts the binary IP representation back to a printable IP address.

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

Also, here is a quick way to extend the IPv6 addresses found in StackOverflow

 function expand($ip){ $hex = unpack("H*hex", inet_pton($ip)); $ip = substr(preg_replace("/([A-f0-9]{4})/", "$1:", $hex['hex']), 0, -1); return $ip; } 

Also, a good read on this topic can be found on my HighOnPHP blog : 5 tips for working with IPv6 in PHP . This article uses some of the methods described above in an object-oriented class that can be found on GitHub: mikemackintosh \ dTR-IP

+1
Jul 08 '13 at 18:22
source share



All Articles