1<?php 2 3/** 4 * IPv6 Address Functions for PHP 5 * 6 * Functions to manipulate IPv6 addresses for PHP 7 * 8 * Copyright (C) 2009, 2011 Ray Patrick Soucy 9 * 10 * LICENSE: 11 * 12 * This program is free software: you can redistribute it and/or modify 13 * it under the terms of the GNU General Public License as published by 14 * the Free Software Foundation, either version 3 of the License, or 15 * (at your option) any later version. 16 * 17 * This program is distributed in the hope that it will be useful, 18 * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 * GNU General Public License for more details. 21 * 22 * You should have received a copy of the GNU General Public License 23 * along with this program. If not, see <http://www.gnu.org/licenses/>. 24 * 25 * @package inet6 26 * @author Ray Soucy <rps@soucy.org> 27 * @version 1.0.2 28 * @copyright 2009, 2011 Ray Patrick Soucy 29 * @link http://www.soucy.org/ 30 * @license GNU General Public License version 3 or later 31 * @since File available since Release 1.0.1 32 */ 33 34 /** 35 * Expand an IPv6 Address 36 * 37 * This will take an IPv6 address written in short form and expand it to include all zeros. 38 * 39 * @param string $addr A valid IPv6 address 40 * @return string The expanded notation IPv6 address 41 */ 42function inet6_expand($addr) 43{ 44 /* Check if there are segments missing, insert if necessary */ 45 if (strpos($addr, '::') !== false) { 46 $part = explode('::', $addr); 47 $part[0] = explode(':', $part[0]); 48 $part[1] = explode(':', $part[1]); 49 $missing = array(); 50 for ($i = 0; $i < (8 - (count($part[0]) + count($part[1]))); $i++) 51 array_push($missing, '0000'); 52 $missing = array_merge($part[0], $missing); 53 $part = array_merge($missing, $part[1]); 54 } else { 55 $part = explode(":", $addr); 56 } // if .. else 57 /* Pad each segment until it has 4 digits */ 58 foreach ($part as &$p) { 59 while (strlen($p) < 4) $p = '0' . $p; 60 } // foreach 61 unset($p); 62 /* Join segments */ 63 $result = implode(':', $part); 64 /* Quick check to make sure the length is as expected */ 65 if (strlen($result) == 39) { 66 return $result; 67 } else { 68 return false; 69 } // if .. else 70} // inet6_expand 71 72 /** 73 * Compress an IPv6 Address 74 * 75 * This will take an IPv6 address and rewrite it in short form. 76 * 77 * @param string $addr A valid IPv6 address 78 * @return string The address in short form notation 79 */ 80function inet6_compress($addr) 81{ 82 /* PHP provides a shortcut for this operation */ 83 $result = inet_ntop(inet_pton($addr)); 84 return $result; 85} // inet6_compress 86 87 /** 88 * Generate an IPv6 mask from prefix notation 89 * 90 * This will convert a prefix to an IPv6 address mask (used for IPv6 math) 91 * 92 * @param integer $prefix The prefix size, an integer between 1 and 127 (inclusive) 93 * @return string The IPv6 mask address for the prefix size 94 */ 95function inet6_prefix_to_mask($prefix) 96{ 97 /* Make sure the prefix is a number between 1 and 127 (inclusive) */ 98 $prefix = intval($prefix); 99 if ($prefix < 0 || $prefix > 128) return false; 100 $mask = '0b'; 101 for ($i = 0; $i < $prefix; $i++) $mask .= '1'; 102 for ($i = strlen($mask) - 2; $i < 128; $i++) $mask .= '0'; 103 $mask = gmp_strval(gmp_init($mask), 16); 104 for ($i = 0; $i < 8; $i++) { 105 $result .= substr($mask, $i * 4, 4); 106 if ($i != 7) $result .= ':'; 107 } // for 108 return inet6_compress($result); 109} // inet6_prefix_to_mask 110 111 /** 112 * Convert an IPv6 address and prefix size to an address range for the network. 113 * 114 * This will take an IPv6 address and prefix and return the first and last address available for the network. 115 * 116 * @param string $addr A valid IPv6 address 117 * @param integer $prefix The prefix size, an integer between 1 and 127 (inclusive) 118 * @return array An array with two strings containing the start and end address for the IPv6 network 119 */ 120function inet6_to_range($addr, $prefix) 121{ 122 $size = 128 - $prefix; 123 $addr = gmp_init('0x' . str_replace(':', '', inet6_expand($addr))); 124 $mask = gmp_init('0x' . str_replace(':', '', inet6_expand(inet6_prefix_to_mask($prefix)))); 125 $prefix = gmp_and($addr, $mask); 126 $start = gmp_strval(gmp_add($prefix, '0x1'), 16); 127 $end = '0b'; 128 for ($i = 0; $i < $size; $i++) $end .= '1'; 129 $end = gmp_strval(gmp_add($prefix, gmp_init($end)), 16); 130 for ($i = 0; $i < 8; $i++) { 131 $start_result .= substr($start, $i * 4, 4); 132 if ($i != 7) $start_result .= ':'; 133 } // for 134 for ($i = 0; $i < 8; $i++) { 135 $end_result .= substr($end, $i * 4, 4); 136 if ($i != 7) $end_result .= ':'; 137 } // for 138 $result = array(inet6_compress($start_result), inet6_compress($end_result)); 139 return $result; 140} // inet6_to_range 141 142 /** 143 * Convert an IPv6 address to two 64-bit integers. 144 * 145 * This will translate an IPv6 address into two 64-bit integer values for storage in an SQL database. 146 * 147 * @param string $addr A valid IPv6 address 148 * @return array An array with two strings containing the 64-bit interger values 149 */ 150function inet6_to_int64($addr) 151{ 152 /* Expand the address if necessary */ 153 if (strlen($addr) != 39) { 154 $addr = inet6_expand($addr); 155 if ($addr == false) return false; 156 } // if 157 $addr = str_replace(':', '', $addr); 158 $p1 = '0x' . substr($addr, 0, 16); 159 $p2 = '0x' . substr($addr, 16); 160 $p1 = gmp_init($p1); 161 $p2 = gmp_init($p2); 162 $result = array(gmp_strval($p1), gmp_strval($p2)); 163 return $result; 164} // inet6_to_int64() 165 166 /** 167 * Convert two 64-bit integer values into an IPv6 address 168 * 169 * This will translate an array of 64-bit integer values back into an IPv6 address 170 * 171 * @param array $val An array containing two strings representing 64-bit integer values 172 * @return string An IPv6 address 173 */ 174function int64_to_inet6($val) 175{ 176 /* Make sure input is an array with 2 numerical strings */ 177 $result = false; 178 if ( ! is_array($val) || count($val) != 2) return $result; 179 $p1 = gmp_strval(gmp_init($val[0]), 16); 180 $p2 = gmp_strval(gmp_init($val[1]), 16); 181 while (strlen($p1) < 16) $p1 = '0' . $p1; 182 while (strlen($p2) < 16) $p2 = '0' . $p2; 183 $addr = $p1 . $p2; 184 for ($i = 0; $i < 8; $i++) { 185 $result .= substr($addr, $i * 4, 4); 186 if ($i != 7) $result .= ':'; 187 } // for 188 return inet6_compress($result); 189} // int64_to_inet6() 190 191// trailing PHP tag omitted to prevent accidental whitespace 192