Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to handle IPv6 Addresses in PHP?

Tags:

php

ip

ipv6

How about inet_ntop()? Then instead of chopping things into integers, you just use a varbinary(16) to store it.


PHP.net's Filter extension contains some constants for matching IPv4 and IPv6 addresses, which might be useful for checking the address. I haven't seen any conversion utilities though.


You could also store the address in a binary(16) in mysql, so you should have an option to output it in binary from IPv6ToLong().

This is really something that need to be added natively in PHP, especially when many IPv6 enabled web-servers report ::FFFF:1.2.3.4 as the client IP and it's incompatible with ip2long, and will break alot of stuff.


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

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

The first function, dtr_pton will check if the supplied argument is valid IPv4 or valid IPv6. Depending on the outcome, an exception could be thrown, or the binary representation of the IP could be returned. By using this function, you can then perform AND'ing or OR'ing against the result (for subnetting/whathaveyou). I would suggest you store these in your database as a VARBINARY(39) or VARCHAR(39).

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

The second function, dtr_ntop will convert the binary representation of the IP back to a printable IP address.

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

Also, here is a quick way of expanding IPv6 addresses found on 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 the subject could be found on my blog at HighOnPHP: 5 Tips for Working With IPv6 in PHP. This article uses some of the methods described above in an Object Oriented class which can be found at GitHub: mikemackintosh\dTR-IP