Amy Neville
Amy Neville

Reputation: 10581

Calculating IP Address Long Form Number for IPv6 Compared to IPv4 Using PHP

Currently I have two ways of calculating IPv4 long-form integers:

//IPv4 format: w.x.y.z
$ip_number = (16777216 * $w) + (65536 * $x) + (256 * $y) + $z;
$ip_number = ip2long($ip);

But how do I calculate IPv6 into a long-form integer?

I'm looking for a function or something that doesn't require me to install any extensions to PHP.

Edit:

I need the long form integer so I can compare it for an IP range query. Here's an example row for a IPv6 range query:

"58569075684585412230123897272863293440","58569076873007849944088961176022548479","MU","Mauritius"

Upvotes: 1

Views: 1410

Answers (2)

Amy Neville
Amy Neville

Reputation: 10581

Ok, here's how I eventually solved it:

function ipaddress_to_ipnumber($ipaddress) {
    $pton = @inet_pton($ipaddress);
    if (!$pton) { return false; }
    $number = '';
    foreach (unpack('C*', $pton) as $byte) {
        $number .= str_pad(decbin($byte), 8, '0', STR_PAD_LEFT);
    }
    return base_convert(ltrim($number, '0'), 2, 10);
}

https://inkplant.com/code/ipv6-to-number

Upvotes: 0

Sazzad Hissain Khan
Sazzad Hissain Khan

Reputation: 40156

Suggestions:

Storing in long is not a proper way for that. Use,

string inet_ntop ( string $in_addr )

This function converts a 32bit IPv4, or 128bit IPv6 address (if PHP was built with IPv6 support enabled) into an address family appropriate string representation.

You should also see,

string inet_pton ( string $address )

inet_ntop() Example

<?php
$packed = chr(127) . chr(0) . chr(0) . chr(1);
$expanded = inet_ntop($packed);

/* Outputs: 127.0.0.1 */
echo $expanded;

$packed = str_repeat(chr(0), 15) . chr(1);
$expanded = inet_ntop($packed);

/* Outputs: ::1 */
echo $expanded;
?>

Comparing in range

The following two functions were introduced in PHP 5.1.0, inet_pton and inet_pton. Their purpose is to convert human readable IP addresses into their packed in_addr representation. Since the result is not pure binary, we need to use the unpack function in order to apply bitwise operators.

Both functions support IPv6 as well as IPv4. The only difference is how you unpack the address from the results. With IPv6, you will unpack with contents with A16, and with IPv4, you will unpack with A4.

To put the previous in a perspective here is a little sample output to help clarify:

// Our Example IP's
$ip4= "10.22.99.129";
$ip6= "fe80:1:2:3:a:bad:1dea:dad";


// ip2long examples
var_dump( ip2long($ip4) ); // int(169239425)
var_dump( ip2long($ip6) ); // bool(false)


// inet_pton examples
var_dump( inet_pton( $ip4 ) ); // string(4)
var_dump( inet_pton( $ip6 ) ); // string(16)

We demonstrate above that the inet_* family supports both IPv6 and v4. Our next step will be to translate the packed result into an unpacked variable.

// Unpacking and Packing
$_u4 = current( unpack( "A4", inet_pton( $ip4 ) ) );
var_dump( inet_ntop( pack( "A4", $_u4 ) ) ); // string(12) "10.22.99.129"


$_u6 = current( unpack( "A16", inet_pton( $ip6 ) ) );
var_dump( inet_ntop( pack( "A16", $_u6 ) ) ); //string(25) "fe80:1:2:3:a:bad:1dea:dad"

Note : The current function returns the first index of an array. It is equivelant to saying $array[0].

After the unpacking and packing, we can see we achieved the same result as input. This is a simple proof of concept to ensure we are not losing any data.

Finally use,

if ($ip <= $high_ip && $low_ip <= $ip) {
  echo "in range";
}

Reference: php.net

Upvotes: 2

Related Questions