> $startbit; $len = strlen($binstart); //fprintf(STDERR, "Checking %s/%s for validity.\nstartbyte=%d\nstartbit=%d\nmask=0x%02X\n\n", // inet_ntop($binstart), $netlen, $startbyte, $startbit, $mask); while ($startbyte < $len) { if (ord($binstart[$startbyte]) & $mask != 0) return false; $mask = 0xFF; ++$startbyte; } return true; } /* * Zero out "host" bits in $binstart and return the result. */ function ipblock_make_valid_network($binstart, $netlen) { $startbyte = (int)($netlen / 8); $startbit = $netlen % 8; $mask = 0xFF - (0xFF >> $startbit); $len = strlen($binstart); //fprintf(STDERR, "Making %s/%s valid.\nstartbyte=%d\nstartbit=%d\nmask=0x%02X\n", // inet_ntop($binstart), $netlen, $startbyte, $startbit, $mask); while ($startbyte < $len) { $binstart[$startbyte] = chr(ord($binstart[$startbyte]) & $mask); $mask = 0x00; $startbyte++; } //fprintf(STDERR, "Result: %s/%s.\n\n", inet_ntop($binstart), $netlen); return $binstart; } /* * Find the broadcast for $binstart/$netlen. Basically, * set all host bits to 1 in $binstart. */ function ipblock_find_broadcast($binstart, $netlen) { $startbyte = (int)($netlen / 8); $startbit = $netlen % 8; $mask = 0xFF >> $startbit; $len = strlen($binstart); //fprintf(STDERR, "Making %s/%s valid.\nstartbyte=%d\nstartbit=%d\nmask=0x%02X\n", // inet_ntop($binstart), $netlen, $startbyte, $startbit, $mask); while ($startbyte < $len) { $binstart[$startbyte] = chr(ord($binstart[$startbyte]) | $mask); $mask = 0xFF; $startbyte++; } //fprintf(STDERR, "Result: %s/%s.\n\n", inet_ntop($binstart), $netlen); return $binstart; } /* * Checks whether $inner/$innerlen is a subnet of $outer/$outerlen */ function ipblock_is_subnet_of($inner, $innerlen, $outer, $outerlen) { if (strlen($inner) != strlen($outer)) return false; if ($innerlen < $outerlen) return false; $inner = ipblock_make_valid_network($inner, $outerlen); return $inner == $outer; } function ipblock_in_list($list, $binstart, $netlen) { foreach ($list as $base => $len) { if (ipblock_is_subnet_of($binstart, $netlen, $base, $len)) return true; } return false; } /* * Adds $binstart/$netlen into $list. */ function ipblock_add_to(&$list, $binstart, $netlen) { foreach ($list as $base => $len) { /* Insert block is a subnet of a block already in the list */ if (ipblock_is_subnet_of($binstart, $netlen, $base, $len)) return; /* Inserted block encompasses the in-list block, so we can remove * the currently inserted block */ if (ipblock_is_subnet_of($base, $len, $binstart, $netlen)) { unset($list[$base]); continue; } if ($len == $netlen) { $base2 = ipblock_make_valid_network($base, $len - 1); if (!isset($binstart2)) $binstart2 = ipblock_make_valid_network($binstart, $len - 1); if ($base2 == $binstart2) { /* adjacent, mergable blocks */ unset($list[$base]); return ipblock_add_to($list, $base2, $netlen - 1); } } } $list[$binstart] = $netlen; } /* * Merge IP ranges into an existing list. Essentially this just adds * $source list into $target. */ function ipblock_merge_into(&$target, $source) { foreach ($source as $b => $l) ipblock_add_to($target, $b, $l); } /* * Subtracts the IP pairs listed in $used from $base/$len, and * store the resulting blocks in $available. It's assumed that * $base/$len is valid. */ function ipblock_subtract_into(&$available, $base, $len, $used) { foreach ($used as $b => $l) { /* Entire base/len is unavailable */ if ($base == $b && $len == $l) return; if (ipblock_is_subnet_of($b, $l, $base, $len)) { ipblock_subtract_into($available, $base, $len + 1, $used); $flip_byte = (int)($len / 8); $flip_bit = $len % 8; $flip_mask = pow(2, 7 - $flip_bit); $base[$flip_byte] = chr(ord($base[$flip_byte]) | $flip_mask); return ipblock_subtract_into($available, $base, $len + 1, $used); } } return ipblock_add_to($available, $base, $len); } /* * Removes $base/$len from $list if $base/$len forms part of $list, if any sub * entries of $base/$len is in $list, they too are removed. */ function ipblock_subtract_from(&$list, $base, $len) { foreach ($list as $b => $l) { if (ipblock_is_subnet_of($base, $len, $b, $l)) { unset($list[$b]); ipblock_subtract_into($list, $b, $l, [$base => $len]); } if (ipblock_is_subnet_of($b, $l, $base, $len)) { unset($list[$b]); } } } /* * Remove all subnets in $rlist from $target. */ function ipblock_remove_from(&$target, $rlist) { foreach ($rlist as $b => $l) ipblock_subtract_from($target, $b, $l); } /* * Simply outputs a list to a FP */ function ipblock_output_list($fp, &$list) { foreach ($list as $binstart => $size) fprintf($fp, "%s/%d\n", inet_ntop($binstart), $size); } /* * Calculate the total amount of v4 and v6 space covered by * the blocklist. * * v6prefix_length can be used to manipulate the IPv6 Prefix length * during calculation (any remaining blocks smaller than this will * be ignored), and the count will be in blocks of this size. For * example, let's say we have: * * dead:beaf:dead:beef::/64 * dead:beaf:dead:baaf:dead:baaf::/96 * * Then calling ipblock_calc_spac([...]) will return [ 6 => 1 ] since there is * only one *full* ipv6 block available. */ function ipblock_calc_space($list, $v6prefix_length = 64) { $r = [ 4 => 0, 6 => 0, ]; $v4len = 32; $v6len = 64; foreach ($list as $b => $l) { $v = strlen($b); switch ($v) { case 4: $r[4] += pow(2, 32 - $l); break; case 16: if ($l <= $v6prefix_length) $r[6] += pow(2, $v6prefix_length - $l); break; default: do_die("Invalid IP address located in ipblock list."); } } return $r; } ?>