Генерация списков делегированных IPv4- и IPv6-адресов по странам.

Предыстория

Обнаружилась в какой-то момент проблема, для решения которой необходимо было блокировать весь трафик от наших китайских братьев по разуму. Казалось бы, что может быть проще — найти список делегированных им сетей и прописать на фаерволе. Однако, во-первых, адекватность и свежесть, найденных списков, вызывала сильные сомнения. Во-вторых, скриптов, которые бы генерировали эти списки никто не выкладывал. Как следствие, пришлось изобретать очередной велосипед :(

Как всё непросто.

Итак, исходные данные лежат в следующих файликах:

Там много чего есть, но строки со словами reserved, available, summary нас не интересуют. Кроме того, нас интересуют только 2 типа объектов: ipv4 и ipv6.

Для IPv6 строка имеет вид ripencc|RU|ipv6|2001:640::|32|19991115|allocated, т.е. регистратор, страна, тип объекта, IPv6-сеть, её маска, дата выдачи, и статус. Нам интересны 2е, 4е и 5е поля. Всё просто.

А вот IPv4… Строка имеет похожий вид ripencc|FR|ipv4|2.0.0.0|1048576|20100712|allocated, т.е. регистратор, страна, тип объекта, IPv4-сеть, количество адресов в блоке, дата выдачи, и статус. Казалось бы всё также, кроме того, что маска заменяется на число хостов. Но в этом-то всё и дело!!! Это ни разу не CIDR!!! Вот конкретные примеры:

afrinic|ZA|ipv4|164.146.0.0|393216|19930312|allocated|F363E51A  # тут вначале идёт сеть /15, а потом /14
ripencc|FR|ipv4|194.146.2.0|768|19950428|assigned               # тут вначале идёт сеть /23, а потом /24
afrinic|ZA|ipv4|41.77.96.0|2048|20110701|allocated|F363BEDE     # тут две сети: 41.77.96.0/21 + 41.77.104.0/21, но их нельзя аггрегировать в /20!

Более того, у меня было устойчивое представление, что LIR не может получить сеть размером менее, чем /22, и когда я увидел строки:

ripencc|UA|ipv4|193.164.232.96|32|20011203|assigned
ripencc|RU|ipv4|193.164.232.128|32|20050905|assigned
ripencc|CY|ipv4|193.164.232.160|32|20120914|assigned
ripencc|FR|ipv4|193.164.232.192|32|20060309|assigned
ripencc|GB|ipv4|193.164.232.224|32|20060310|assigned

я понял, что просто не будет :)

Велосипед.

Как итог, в моём воспалённом сознании родился вот такой скриптец:

cidr.pl
#!/usr/bin/perl -w
 
use strict;
 
my $DIR='/tmp/cidr_temp';
 
system "rm -rf $DIR";
system "mkdir -p $DIR/countries";
chdir $DIR;
 
system "wget", "-c",
        "ftp://ftp.ripe.net/pub/stats/afrinic/delegated-afrinic-extended-latest",
        "ftp://ftp.ripe.net/pub/stats/apnic/delegated-apnic-extended-latest",
        "ftp://ftp.ripe.net/pub/stats/arin/delegated-arin-extended-latest",
        "ftp://ftp.ripe.net/pub/stats/lacnic/delegated-lacnic-extended-latest",
        "ftp://ftp.ripe.net/pub/stats/ripencc/delegated-ripencc-extended-latest"
;
 
my %size2mask = (
               1 => 32,        2 => 31,       4 => 30,       8 => 29,
              16 => 28,       32 => 27,      64 => 26,     128 => 25,
             256 => 24,      512 => 23,    1024 => 22,    2048 => 21,
            4096 => 20,     8192 => 19,   16384 => 18,   32768 => 17,
           65536 => 16,   131072 => 15,  262144 => 14,  524288 => 13,
         1048576 => 12,  2097152 => 11, 4194304 => 10, 8388608 =>  9,
        16777216 =>  8, 33554432 =>  4
);
 
my %countries = ();
 
for my $i ( "afrinic", "apnic", "arin", "lacnic", "ripencc" ) {
        open INPUT, "delegated-${i}-extended-latest" || die "Cannot open input file: delegated-${i}-extended-latest\n";
        while (<INPUT>) {
                next if     (/reserved|available|summary/);
                next unless (/ipv4/);
 
                #ripencc|FR|ipv4|2.0.0.0|1048576|20100712|allocated
                my ($registry, $country, $ip_version, $first_ip, $count, $date, $status) = split /\|/;
                my @prefixes = sort {$b <=> $a} (keys %size2mask);
 
                while ( $count > 0 ) {
 
                        while ($prefixes[0] > $count ) {
                                shift @prefixes;
                        }
 
                        my @first_ip = (split /\./, $first_ip);
                        my $bin_ip = ( ( ( ( ( $first_ip[0] << 8 ) + $first_ip[1] ) << 8 ) + $first_ip[2] ) << 8 ) + $first_ip[3];
 
                        if ( ($bin_ip & ( 0xffffffff << ( 32 - $size2mask{$prefixes[0]} ))) == $bin_ip ) {
                                $countries{$country} = [] if ( not defined $countries{$country} );
                                push @{$countries{$country}}, ($first_ip . '/' . $size2mask{$prefixes[0]});
 
                                $first_ip = join '.', (($bin_ip + $prefixes[0] >> 24) & 0xff, ($bin_ip + $prefixes[0] >> 16) & 0xff, ($bin_ip + $prefixes[0] >> 8) & 0xff, ($bin_ip + $prefixes[0]) & 0xff);
                                $count -= $prefixes[0];
                        } else {
                                shift @prefixes;
                        }
                }
        }
        close INPUT;
}
 
foreach my $country ( sort keys %countries ) {
        open DUMP, "| sort | aggregate | sort -t. -k 1,1n -k 2,2n -k 3,3n -k 4,4n > $DIR/countries/${country}-ipv4.cidr" || die;
        map { print DUMP $_, "\n" } @{$countries{$country}};
        close DUMP;
}
 
 
%countries = ();
 
for my $i ( "afrinic", "apnic", "arin", "lacnic", "ripencc" ) {
        open INPUT, "delegated-${i}-extended-latest" || die "Cannot open input file: delegated-${i}-extended-latest\n";
        while (<INPUT>) {
                next if     (/reserved|available|summary/);
                next unless (/ipv6/);
 
                #ripencc|RU|ipv6|2001:640::|32|19991115|allocated
                my ($registry, $country, $ip_version, $network, $mask, $date, $status) = split /\|/;
                $countries{$country} = [] if ( not defined $countries{$country} );
                push @{$countries{$country}}, ($network . '/' . $mask);
        }
        close INPUT;
}
 
foreach my $country ( sort keys %countries ) {
        open DUMP, "| sort > $DIR/countries/${country}-ipv6.cidr" || die;
        map { print DUMP $_, "\n" } @{$countries{$country}};
        close DUMP;
}

Из всего скрипта пояснения заслуживает только момент с выводом IPv4-адресов. Если, как в примере выше, в начале блока идёт «маленькая» сеть, а после неё идёт «большая», то надо либо в логике скрипта массив префиксов восстанавливать в исходное состояние каждый раз, как только мы запоминаем CIDR-сеть и пересчитываем значение first_ip, либо мы получаем набор мелких сетей, которые можно аггрегировать в более крупные. Я пошел вторым путём, т.к. пакет net-misc/aggregate (домашняя страница: http://dist.automagic.org) у меня уже стоял.

Результат.

И таки да, результаты работы скрипта доступны здесь: http://cidr.sabitov.ru/countries/ Обновляться они будут раз в неделю. На самом деле и это часто, но я уже прописал:)


net/списки_делегированных_ip_адресов_по_странам.txt · Последние изменения: 2013-12-25 17:10 — Andrew A. Sabitov