/** * Convert an IPv4 address into an IPv6 address. * * One use-case for this is IP 6to4 tunnels used in networking. * * @example * to_ipv6("10.10.10.10") == a0a:a0a * * @param string $ip IPv4 address. * @param boolean $mapped If true a Full IPv6 address is returned within the * official ipv4to6 mapped space "0:0:0:0:0:ffff:x:x" */ public static function to_ipv6($ip, $mapped = false) { if (!self::isIPv4($ip)) { throw new \InvalidArgumentException("Invalid IPv4 address \"{$ip}\""); } $num = IP::inet_ptod($ip); $o1 = dechex($num >> 16); $o2 = dechex($num & 0xffff); return $mapped ? "0:0:0:0:0:ffff:{$o1}:{$o2}" : "{$o1}:{$o2}"; }
/** * Return a contiguous list of true CIDR blocks that span the range given. * * Note: It's not a good idea to call this with IPv6 addresses. While it may * work for certain ranges this can be very slow. Also an IPv6 list won't be * as accurate as an IPv4 list. * * @example * range_to_cidrlist(192.168.0.0, 192.168.0.15) == * 192.168.0.0/28 * range_to_cidrlist(192.168.0.0, 192.168.0.20) == * 192.168.0.0/28 * 192.168.0.16/30 * 192.168.0.20/32 */ public static function range_to_cidrlist($start, $end) { $ver = false === filter_var($start, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) ? 6 : 4; $start = IP::inet_ptod($start); $end = IP::inet_ptod($end); $len = $ver == 4 ? 32 : 128; $log2 = $ver == 4 ? log(2) : BC::bclog(2); $list = array(); while (BC::cmp($end, $start) >= 0) { // $end >= $start $prefix = self::max_prefix(IP::inet_dtop($start), $len); if ($ver == 4) { $diff = $len - floor(log($end - $start + 1) / $log2); } else { // this is not as accurate due to the bclog function $diff = bcsub($len, BC::bcfloor(bcdiv(BC::bclog(bcadd(bcsub($end, $start), '1')), $log2))); } if ($prefix < $diff) { $prefix = $diff; } $list[] = IP::inet_dtop($start) . "/" . $prefix; if ($ver == 4) { $start += pow(2, $len - $prefix); } else { $start = bcadd($start, bcpow(2, $len - $prefix)); } } return $list; }