IPv6.php 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. <?php
  2. /* * ********************************************************************
  3. * Proxmox Addon Product developed. (2013-11-18)
  4. * *
  5. *
  6. * CREATED BY MODULESGARDEN -> http://modulesgarden.com
  7. * CONTACT -> contact@modulesgarden.com
  8. *
  9. *
  10. * This software is furnished under a license and may be used and copied
  11. * only in accordance with the terms of such license and with the
  12. * inclusion of the above copyright notice. This software or any other
  13. * copies thereof may not be provided or otherwise made available to any
  14. * other person. No title to and ownership of the software is hereby
  15. * transferred.
  16. *
  17. *
  18. * ******************************************************************** */
  19. namespace ModulesGarden\ProxmoxAddon\App\Libs;
  20. if (!function_exists('ipv6_range'))
  21. {
  22. function ipv6_range($prefix)
  23. {
  24. // Split in address and prefix length
  25. list($firstaddrstr, $prefixlen) = explode('/', $prefix);
  26. // Parse the address into a binary string
  27. $firstaddrbin = inet_pton($firstaddrstr);
  28. // Convert the binary string to a string with hexadecimal characters
  29. # unpack() can be replaced with bin2hex()
  30. # unpack() is used for symmetry with pack() below
  31. $unpacked = unpack('H*', $firstaddrbin);
  32. $firstaddrhex = reset($unpacked);
  33. // Overwriting first address string to make sure notation is optimal
  34. $firstaddrstr = inet_ntop($firstaddrbin);
  35. // Calculate the number of 'flexible' bits
  36. $flexbits = 128 - $prefixlen;
  37. // Build the hexadecimal string of the last address
  38. $lastaddrhex = $firstaddrhex;
  39. // We start at the end of the string (which is always 32 characters long)
  40. $pos = 31;
  41. while ($flexbits > 0)
  42. {
  43. // Get the character at this position
  44. $orig = substr($lastaddrhex, $pos, 1);
  45. // Convert it to an integer
  46. $origval = hexdec($orig);
  47. // OR it with (2^flexbits)-1, with flexbits limited to 4 at a time
  48. $newval = $origval | (pow(2, min(4, $flexbits)) - 1);
  49. // Convert it back to a hexadecimal character
  50. $new = dechex($newval);
  51. // And put that character back in the string
  52. $lastaddrhex = substr_replace($lastaddrhex, $new, $pos, 1);
  53. // We processed one nibble, move to previous position
  54. $flexbits -= 4;
  55. $pos -= 1;
  56. }
  57. // Convert the hexadecimal string to a binary string
  58. # Using pack() here
  59. # Newer PHP version can use hex2bin()
  60. $lastaddrbin = pack('H*', $lastaddrhex);
  61. // And create an IPv6 address from the binary string
  62. $lastaddrstr = inet_ntop($lastaddrbin);
  63. return [$prefix, $firstaddrstr, $lastaddrstr];
  64. }
  65. }
  66. /**
  67. * Converts human readable representation to a 128bit int
  68. * which can be stored in MySQL using DECIMAL(39,0).
  69. *
  70. * Requires PHP to be compiled with IPv6 support.
  71. * This could be made to work without IPv6 support but
  72. * I don't think there would be much use for it if PHP
  73. * doesn't support IPv6.
  74. *
  75. * @param string $ip IPv4 or IPv6 address to convert
  76. * @return string 128bit string that can be used with DECIMNAL(39,0) or false
  77. */
  78. if (!function_exists('inet_ptoi'))
  79. {
  80. function inet_ptoi($ip)
  81. {
  82. // make sure it is an ip
  83. if (filter_var($ip, FILTER_VALIDATE_IP) === false)
  84. {
  85. return false;
  86. }
  87. $parts = unpack('N*', inet_pton($ip));
  88. // fix IPv4
  89. if (strpos($ip, '.') !== false)
  90. {
  91. $parts = [1 => 0, 2 => 0, 3 => 0, 4 => $parts[1]];
  92. }
  93. foreach ($parts as &$part)
  94. {
  95. // convert any unsigned ints to signed from unpack.
  96. // this should be OK as it will be a PHP float not an int
  97. if ($part < 0)
  98. {
  99. $part += 4294967296;
  100. }
  101. }
  102. // use BCMath if the extension exists
  103. if (function_exists('bcadd'))
  104. {
  105. $decimal = $parts[4];
  106. $decimal = bcadd($decimal, bcmul($parts[3], '4294967296'));
  107. $decimal = bcadd($decimal, bcmul($parts[2], '18446744073709551616'));
  108. $decimal = bcadd($decimal, bcmul($parts[1], '79228162514264337593543950336'));
  109. }
  110. // otherwise use the pure PHP BigInteger
  111. else
  112. {
  113. $decimal = new BigInteger($parts[4]);
  114. $part3 = new BigInteger($parts[3]);
  115. $part2 = new BigInteger($parts[2]);
  116. $part1 = new BigInteger($parts[1]);
  117. $decimal = $decimal->add($part3->multiply(new BigInteger('4294967296')));
  118. $decimal = $decimal->add($part2->multiply(new BigInteger('18446744073709551616')));
  119. $decimal = $decimal->add($part1->multiply(new BigInteger('79228162514264337593543950336')));
  120. $decimal = $decimal->toString();
  121. }
  122. return $decimal;
  123. }
  124. }
  125. /**
  126. * Converts a 128bit int to a human readable representation.
  127. *
  128. * Requires PHP to be compiled with IPv6 support.
  129. * This could be made to work without IPv6 support but
  130. * I don't think there would be much use for it if PHP
  131. * doesn't support IPv6.
  132. *
  133. * @param string $decimal 128bit int
  134. * @return string IPv4 or IPv6
  135. */
  136. if (!function_exists('inet_itop'))
  137. {
  138. function inet_itop($decimal)
  139. {
  140. $parts = [];
  141. // use BCMath if the extension exists
  142. if (function_exists('bcadd'))
  143. {
  144. $parts[1] = bcdiv($decimal, '79228162514264337593543950336', 0);
  145. $decimal = bcsub($decimal, bcmul($parts[1], '79228162514264337593543950336'));
  146. $parts[2] = bcdiv($decimal, '18446744073709551616', 0);
  147. $decimal = bcsub($decimal, bcmul($parts[2], '18446744073709551616'));
  148. $parts[3] = bcdiv($decimal, '4294967296', 0);
  149. $decimal = bcsub($decimal, bcmul($parts[3], '4294967296'));
  150. $parts[4] = $decimal;
  151. }
  152. // otherwise use the pure PHP BigInteger
  153. else
  154. {
  155. $decimal = new BigInteger($decimal);
  156. list($parts[1],) = $decimal->divide(new BigInteger('79228162514264337593543950336'));
  157. $decimal = $decimal->subtract($parts[1]->multiply(new BigInteger('79228162514264337593543950336')));
  158. list($parts[2],) = $decimal->divide(new BigInteger('18446744073709551616'));
  159. $decimal = $decimal->subtract($parts[2]->multiply(new BigInteger('18446744073709551616')));
  160. list($parts[3],) = $decimal->divide(new BigInteger('4294967296'));
  161. $decimal = $decimal->subtract($parts[3]->multiply(new BigInteger('4294967296')));
  162. $parts[4] = $decimal;
  163. $parts[1] = $parts[1]->toString();
  164. $parts[2] = $parts[2]->toString();
  165. $parts[3] = $parts[3]->toString();
  166. $parts[4] = $parts[4]->toString();
  167. }
  168. foreach ($parts as &$part)
  169. {
  170. // convert any signed ints to unsigned for pack
  171. // this should be fine as it will be treated as a float
  172. if ($part > 2147483647)
  173. {
  174. $part -= 4294967296;
  175. }
  176. }
  177. $ip = inet_ntop(pack('N4', $parts[1], $parts[2], $parts[3], $parts[4]));
  178. // fix IPv4 by removing :: from the beginning
  179. if (strpos($ip, '.') !== false)
  180. {
  181. return substr($ip, 2);
  182. }
  183. return $ip;
  184. }
  185. }