示例#1
0
文件: CIDR.php 项目: 0151n/vichan
 /**
  * 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;
 }