Расчет CIDR по диапазону ip-адресов

У замечательного сервера nginx есть очень полезный модуль ngx_http_geo_module, который позволяет в зависимости от ip посетителя присваивать переменной различные значения. Диапазоны ip в его настройках надо указывать в формате CIDR (127.0.0.1/31), доступные же базы geo-ip содержат диапазоны в формате 127.0.0.0-127.0.0.1. В связи с чем возникла необходимость все это конвертировать.

Идя по пути наименьшего сопротивления, попытался найти существующие реализации  этого простого алгоритма, но, к сожалению, поиски не увенчались успехом. Пришлось разбираться самому. Ниже привожу простую реализацию этой задачи.


class cidr_converter{

    static function convert($ip1, $ip2){
       
        $l_ip1 = ip2long($ip1);
        $l_ip2 = ip2long($ip2);
       
        $m2a = array();
        for($i = 0; $i<32; $i++){
            $m2a[32-$i] = pow(2, $i);
        }

        $masks = array();
        $addr = $l_ip1;
       
        do{
           
            $bit_mask = self::get_ip_bitmask($addr);

            if($addr+pow(2, 32 - $bit_mask)>$l_ip2){
               
                $bit_mask = self::get_nearest_in_array($m2a, $l_ip2-$addr+1);
            }
           
            $masks[] = long2ip($addr).'/'.$bit_mask;
           
            $addr += pow(2, 32 - $bit_mask);
           
        }while ($addr<=$l_ip2);
       
        return $masks;
    }

    static function get_ip_bitmask($long_ip){

        $lb = 0;
       
        while($long_ip!=0){
           
            $long_ip = $long_ip<<1;
            $lb++;
        }
        return $lb;
    }
   
    static function get_nearest_in_array($haystack, $needle){
       
        $prev_addr = 0;
        $prev_m = 0;
        foreach ($haystack as $m=>$addr){

            if($needle>$addr){
               
                $prev_m = $m;
                $prev_addr = $addr;
            }elseif($addr==$needle){
               
                return $m;
            }elseif($needle<$addr){
               
                return $prev_m?$prev_m:-1;
            }
        }
       
        return $prev_m?$prev_m:-1;
    }
}

print_r(cidr_converter::convert('127.0.0.0', '127.255.0.1'));

Комментарии

  • anonym 2009-02-18 07:57
    Плохо искал
    http://www.irbs.net/internet/postfix/0401/3032.html
  • AntiGun 2010-10-11 20:45
    Пробовал этот код, вылетает с сообщением о переполнении. А вот URL, подсказанный анонимом, помог реально.
    С его помощью сделал вот это:
    http://ip2cidr.savesoul.ru/
Ваше имя*
Комментарий*
Email*
Сайт
Введите код:

BlogMemes.ru