function ip_range_to_subnet_array_temp($ip1, $ip2) { if (is_ipaddrv4($ip1) && is_ipaddrv4($ip2)) { $proto = 'ipv4'; // for clarity $bits = 32; $ip1bin = decbin(ip2long32($ip1)); $ip2bin = decbin(ip2long32($ip2)); } elseif (is_ipaddrv6($ip1) && is_ipaddrv6($ip2)) { $proto = 'ipv6'; $bits = 128; $ip1bin = Net_IPv6::_ip2Bin($ip1); $ip2bin = Net_IPv6::_ip2Bin($ip2); } else { return array(); } // it's *crucial* that binary strings are guaranteed the expected length; do this for certainty even though for IPv6 it's redundant $ip1bin = str_pad($ip1bin, $bits, '0', STR_PAD_LEFT); $ip2bin = str_pad($ip2bin, $bits, '0', STR_PAD_LEFT); if ($ip1bin === $ip2bin) { return array($ip1 . '/' . $bits); } if (strcmp($ip1bin, $ip2bin) > 0) { list($ip1bin, $ip2bin) = array($ip2bin, $ip1bin); } // swap contents of ip1 <= ip2 $rangesubnets = array(); $netsize = 0; do { // at loop start, $ip1 is guaranteed strictly less than $ip2 (important for edge case trapping and preventing accidental binary wrapround) // which means the assignments $ip1 += 1 and $ip2 -= 1 will always be "binary-wrapround-safe" // step #1 if start ip (as shifted) ends in any '1's, then it must have a single cidr to itself (any cidr would include the '0' below it) if (substr($ip1bin, -1, 1) == '1') { // the start ip must be in a separate one-IP cidr range $new_subnet_ip = substr($ip1bin, $netsize, $bits - $netsize) . str_repeat('0', $netsize); $rangesubnets[$new_subnet_ip] = $bits - $netsize; $n = strrpos($ip1bin, '0'); //can't be all 1's $ip1bin = ($n == 0 ? '' : substr($ip1bin, 0, $n)) . '1' . str_repeat('0', $bits - $n - 1); // BINARY VERSION OF $ip1 += 1 } // step #2, if end ip (as shifted) ends in any zeros then that must have a cidr to itself (as cidr cant span the 1->0 gap) if (substr($ip2bin, -1, 1) == '0') { // the end ip must be in a separate one-IP cidr range $new_subnet_ip = substr($ip2bin, $netsize, $bits - $netsize) . str_repeat('0', $netsize); $rangesubnets[$new_subnet_ip] = $bits - $netsize; $n = strrpos($ip2bin, '1'); //can't be all 0's $ip2bin = ($n == 0 ? '' : substr($ip2bin, 0, $n)) . '0' . str_repeat('1', $bits - $n - 1); // BINARY VERSION OF $ip2 -= 1 // already checked for the edge case where end = start+1 and start ends in 0x1, above, so it's safe } // this is the only edge case arising from increment/decrement. // it happens if the range at start of loop is exactly 2 adjacent ips, that spanned the 1->0 gap. (we will have enumerated both by now) if (strcmp($ip2bin, $ip1bin) < 0) { continue; } // step #3 the start and end ip MUST now end in '0's and '1's respectively // so we have a non-trivial range AND the last N bits are no longer important for CIDR purposes. $shift = $bits - max(strrpos($ip1bin, '0'), strrpos($ip2bin, '1')); // num of low bits which are '0' in ip1 and '1' in ip2 $ip1bin = str_repeat('0', $shift) . substr($ip1bin, 0, $bits - $shift); $ip2bin = str_repeat('0', $shift) . substr($ip2bin, 0, $bits - $shift); $netsize += $shift; if ($ip1bin === $ip2bin) { // we're done. $new_subnet_ip = substr($ip1bin, $netsize, $bits - $netsize) . str_repeat('0', $netsize); $rangesubnets[$new_subnet_ip] = $bits - $netsize; continue; } // at this point there's still a remaining range, and either startip ends with '1', or endip ends with '0'. So repeat cycle. } while (strcmp($ip1bin, $ip2bin) < 0); // subnets are ordered by bit size. Re sort by IP ("naturally") and convert back to IPv4/IPv6 ksort($rangesubnets, SORT_STRING); $out = array(); foreach ($rangesubnets as $ip => $netmask) { if ($proto == 'ipv4') { $i = str_split($ip, 8); $out[] = implode('.', array(bindec($i[0]), bindec($i[1]), bindec($i[2]), bindec($i[3]))) . '/' . $netmask; } else { $out[] = Net_IPv6::compress(Net_IPv6::_bin2Ip($ip)) . '/' . $netmask; } } return $out; }
/** * Returns the lowest and highest IPv6 address * for a given IP and netmask specification * * The netmask may be a part of the $ip or * the number of netwask bits is provided via $bits * * The result is an indexed array. The key 'start' * contains the lowest possible IP adress. The key * 'end' the highest address. * * @param String $ipToParse the IPv6 address * @param String $bits the optional count of netmask bits * * @return Array ['start', 'end'] the lowest and highest IPv6 address * @access public * @static * @author Nicholas Williams */ public static function parseAddress($ipToParse, $bits = null) { $ip = null; $bitmask = null; if (null == $bits) { $elements = explode('/', $ipToParse); if (2 == count($elements)) { $ip = Net_IPv6::uncompress($elements[0]); $bitmask = $elements[1]; } else { include_once 'PEAR.php'; return PEAR::raiseError(NET_IPV6_NO_NETMASK_MSG, NET_IPV6_NO_NETMASK); } } else { $ip = Net_IPv6::uncompress($ipToParse); $bitmask = $bits; } $binNetmask = str_repeat('1', $bitmask) . str_repeat('0', 128 - $bitmask); $maxNetmask = str_repeat('1', 128); $netmask = Net_IPv6::_bin2Ip($binNetmask); $startAddress = Net_IPv6::_bin2Ip(Net_IPv6::_ip2Bin($ip) & $binNetmask); $endAddress = Net_IPv6::_bin2Ip(Net_IPv6::_ip2Bin($ip) | $binNetmask ^ $maxNetmask); return array('start' => $startAddress, 'end' => $endAddress); }
$masks["IPv6"][$i] = str_repeat('1', $i) . str_repeat('0', 128 - $i); } # IPv6 masks, bin str # Read IPs for the sections we need to order foreach ($rlist as $sect_id => $sect_check) { $section_subnets = $Subnets->fetch_section_subnets($sect_id); # skip empty sections if (sizeof($section_subnets) == 0) { continue; } foreach ($section_subnets as &$subnet) { $subnet = (array) $subnet; $subnet['ip'] = $Subnets->transform_to_dotted($subnet['subnet']); $subnet['type'] = $Subnets->identify_address($subnet['ip']); # Precompute subnet in AND format (long for IPv4 and bin str for IPv6) $subnet['andip'] = $subnet['type'] == "IPv4" ? $subnet['subnet'] : $pi6->_ip2Bin($subnet['ip']); # Add to array $edata[$sect_id][] = $subnet; } } $rows = ""; $counters = array(); # Recompute master/nested relations for the selected sections and address families foreach ($rlist as $sect_id => $sect_check) { # Skip empty sections if (!$edata[$sect_id]) { continue; } # Grab a subnet and find its closest master foreach ($edata[$sect_id] as &$c_subnet) { if (!$sect_check[$c_subnet['type']]) {