Ray Patrick Soucy

IPv6 Address Functions for PHP

Release 1.0.2

This file provides functions to work with IPv6 addresses, mainly for storing them numerically in an SQL database.  Available under the GNU General Public License.

Download

API Reference Documentation

Example:

<?php

    require 'inet6.php';

    $addr = inet6_to_int64('2001:DB8::1');
    $addr_p1 = $addr[0];
    $addr_p2 = $addr[1];

    $query = "INSERT INTO ipv6 (addr_p1, addr_p2) VALUES ('$addr_p1', '$addr_p2')";

?>

Source:

<?php

/**
 * IPv6 Address Functions for PHP
 *
 * Functions to manipulate IPv6 addresses for PHP
 *
 * Copyright (C) 2009, 2011 Ray Patrick Soucy
 *
 * LICENSE:
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @package   inet6
 * @author    Ray Soucy <rps@soucy.org>
 * @version   1.0.2
 * @copyright 2009, 2011 Ray Patrick Soucy 
 * @link      http://www.soucy.org/
 * @license   GNU General Public License version 3 or later
 * @since     File available since Release 1.0.1
 */

 /**
  * Expand an IPv6 Address
  *
  * This will take an IPv6 address written in short form and expand it to include all zeros. 
  *
  * @param  string  $addr A valid IPv6 address
  * @return string  The expanded notation IPv6 address
  */
function inet6_expand($addr)
{
    /* Check if there are segments missing, insert if necessary */
    if (strpos($addr, '::') !== false) {
        $part = explode('::', $addr);
        $part[0] = explode(':', $part[0]);
        $part[1] = explode(':', $part[1]);
        $missing = array();
        for ($i = 0; $i < (8 - (count($part[0]) + count($part[1]))); $i++)
            array_push($missing, '0000');
        $missing = array_merge($part[0], $missing);
        $part = array_merge($missing, $part[1]);
    } else {
        $part = explode(":", $addr);
    } // if .. else
    /* Pad each segment until it has 4 digits */
    foreach ($part as &$p) {
        while (strlen($p) < 4) $p = '0' . $p;
    } // foreach
    unset($p);
    /* Join segments */
    $result = implode(':', $part);
    /* Quick check to make sure the length is as expected */ 
    if (strlen($result) == 39) {
        return $result;
    } else {
        return false;
    } // if .. else
} // inet6_expand

 /**
  * Compress an IPv6 Address
  *
  * This will take an IPv6 address and rewrite it in short form. 
  *
  * @param  string  $addr A valid IPv6 address
  * @return string  The address in short form notation
  */
function inet6_compress($addr)
{
    /* PHP provides a shortcut for this operation */
    $result = inet_ntop(inet_pton($addr));
    return $result;
} // inet6_compress

 /**
  * Generate an IPv6 mask from prefix notation
  *
  * This will convert a prefix to an IPv6 address mask (used for IPv6 math) 
  *
  * @param  integer $prefix The prefix size, an integer between 1 and 127 (inclusive)
  * @return string  The IPv6 mask address for the prefix size
  */
function inet6_prefix_to_mask($prefix)
{
    /* Make sure the prefix is a number between 1 and 127 (inclusive) */
    $prefix = intval($prefix);
    if ($prefix < 0 || $prefix > 128) return false;
    $mask = '0b';
    for ($i = 0; $i < $prefix; $i++) $mask .= '1';
    for ($i = strlen($mask) - 2; $i < 128; $i++) $mask .= '0';
    $mask = gmp_strval(gmp_init($mask), 16);
    for ($i = 0; $i < 8; $i++) {
        $result .= substr($mask, $i * 4, 4);
        if ($i != 7) $result .= ':';
    } // for
    return inet6_compress($result);
} // inet6_prefix_to_mask

 /**
  * Convert an IPv6 address and prefix size to an address range for the network.
  *
  * This will take an IPv6 address and prefix and return the first and last address available for the network. 
  *
  * @param  string  $addr A valid IPv6 address
  * @param  integer $prefix The prefix size, an integer between 1 and 127 (inclusive)
  * @return array   An array with two strings containing the start and end address for the IPv6 network
  */
function inet6_to_range($addr, $prefix)
{
    $size = 128 - $prefix;
    $addr = gmp_init('0x' . str_replace(':', '', inet6_expand($addr)));
    $mask = gmp_init('0x' . str_replace(':', '', inet6_expand(inet6_prefix_to_mask($prefix))));
    $prefix = gmp_and($addr, $mask);
    $start = gmp_strval(gmp_add($prefix, '0x1'), 16);
    $end = '0b';
    for ($i = 0; $i < $size; $i++) $end .= '1';
    $end = gmp_strval(gmp_add($prefix, gmp_init($end)), 16);
    for ($i = 0; $i < 8; $i++) {
        $start_result .= substr($start, $i * 4, 4);
        if ($i != 7) $start_result .= ':';
    } // for
    for ($i = 0; $i < 8; $i++) {
        $end_result .= substr($end, $i * 4, 4);
        if ($i != 7) $end_result .= ':';
    } // for
    $result = array(inet6_compress($start_result), inet6_compress($end_result));
    return $result;
} // inet6_to_range

 /**
  * Convert an IPv6 address to two 64-bit integers.
  *
  * This will translate an IPv6 address into two 64-bit integer values for storage in an SQL database. 
  *
  * @param  string  $addr A valid IPv6 address
  * @return array   An array with two strings containing the 64-bit interger values
  */
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()

 /**
  * Convert two 64-bit integer values into an IPv6 address
  *
  * This will translate an array of 64-bit integer values back into an IPv6 address 
  *
  * @param  array  $val An array containing two strings representing 64-bit integer values
  * @return string An IPv6 address
  */
function int64_to_inet6($val)
{
    /* Make sure input is an array with 2 numerical strings */
    $result = false;
    if ( ! is_array($val) || count($val) != 2) return $result;
    $p1 = gmp_strval(gmp_init($val[0]), 16);
    $p2 = gmp_strval(gmp_init($val[1]), 16);
    while (strlen($p1) < 16) $p1 = '0' . $p1;
    while (strlen($p2) < 16) $p2 = '0' . $p2;
    $addr = $p1 . $p2;
    for ($i = 0; $i < 8; $i++) {
        $result .= substr($addr, $i * 4, 4);
        if ($i != 7) $result .= ':';
    } // for
    return inet6_compress($result);
} // int64_to_inet6()

// trailing PHP tag omitted to prevent accidental whitespace