digital 千里眼 @abp_jp

アナログな日常とデジタルの接点

国を指定して ipfilter.dat を作成する例3:ipinfodb.com のデータ Complete(Country) を利用した場合

  • 「%03s」は間違い。正しくは「%03d

2010-08-06 修正

  • 数値をゼロ埋めの固定長に変更(こちらの書式が主流?)
  • 改行文字の修正(LF ⇒ CR+LF)を awk スクリプト中に含めた(Linux 等で使う場合は printf の \r を削ってください)

2009-07-05 修正

前回は一般の企業 ipinfodb.com が作成したIP範囲のリストとして「GeoLite Country」を利用した。もう1つ一般企業が作成したデータ「Complete(Country)」を利用した例を示したい(国指定のIP範囲リスト作成は今回が最終回)

前提条件
  • 前回と同じ
  • 違いは元データが前回紹介した GeoLite を加工した ipinfodb.com の「Complete(Country)」であることだけ(ipinfodb.com も maxmind.com 同様に都市データがあるが、こちらはアメリカの都市限定のようだ)

1.元データをダウンロード

  • データは毎月第一週に更新される
wget -nd -nc http://mirrors.portafixe.com/ipinfodb/ip_database/current/ipinfodb_one_table_full_country.csv.zip
2.解凍
unzip -u  ipinfodb_one_table_full_country.csv.zip
bzip2 -dc ip_group_country.csv.bz2 >ip_group_country.csv

3.データ概要

  • 解凍後のファイルサイズは8メガ強。10万行強でセミコロン(;)区切り
フィールド 意味
第1フィールド 10進数化した開始IPアドレス
第2フィールド 可能なIPアドレスの範囲(ネットワークアドレス+CIDR表記)
第3フィールド ISO 3166 ベースの国コード
第4フィールド 国名
"ip_start";"ip_cidr";"country_code";"country_name"
"0";"0.0.0.0/8";"RD";"Reserved"
"16777216";"1.0.0.0/8";"RD";"Reserved"
"33554432";"2.0.0.0/8";"RD";"Reserved"
"50331648";"3.0.0.0/8";"US";"United States"
        中 略
"4227858432";"252.0.0.0/8";"RD";"Reserved"
"4244635648";"253.0.0.0/8";"RD";"Reserved"
"4261412864";"254.0.0.0/8";"RD";"Reserved"

4.ipfilter.dat 形式に変換

  • awk を使います。コードは以下の通り
  • ファイル名は"IPInfoDB2ipfilter.awk"とします
BEGIN {
    FS            = ";";
#    OFS           = ",";
    match_counter = 0;
    CLS_A         = 16777216;
    CLS_B         = 65536;
    CLS_C         = 256;
    OFFSET[1]      = 1;
    OFFSET[2]      = 3;
    OFFSET[3]      = 7;
    OFFSET[4]      = 15;
    OFFSET[5]      = 31;
    OFFSET[6]      = 63;
    OFFSET[7]      = 127;
    OFFSET[8]      = 255;
    OFFSET[9]      = 511;
    OFFSET[10]     = 1023;
    OFFSET[11]     = 2047;
    OFFSET[12]     = 4095;
    OFFSET[13]     = 8191;
    OFFSET[14]     = 16383;
    OFFSET[15]     = 32767;
    OFFSET[16]     = 65535;
    OFFSET[17]     = 131071;
    OFFSET[18]     = 262143;
    OFFSET[19]     = 524287;
    OFFSET[20]     = 1048575;
    OFFSET[21]     = 2097151;
    OFFSET[22]     = 4194303;
    OFFSET[23]     = 8388607;
    OFFSET[24]     = 16777215;
    OFFSET[25]     = 33554431;
    OFFSET[26]     = 67108863;
    OFFSET[27]     = 134217727;
    OFFSET[28]     = 268435455;
    OFFSET[29]     = 536870911;
    OFFSET[30]     = 1073741823;
    OFFSET[31]     = 2147483647;
    OFFSET[32]     = 4294967295;
}

/"CN";"China"/ || /"HK";"Hong Kong"/ || /"TW";"Taiwan"/ || /"JP";"Japan"/ {
    gsub( /"/, "" );
    if(split($2, ip_cidr, "/") != 2)
    {
        print "[ERROR]split error";
        exit 2;
    }else if(NUMrize(ip_cidr[1]) != $1)
    {
        print "[ERROR]NUMrize error",ip_cidr[1],$1;
        exit 3;
    }
    gsub(",", " ", $4);
    printf "%-s - %-s , %03d , [%s]%s\r\n", ip_cidr[1], IPrize($1 + OFFSET[32-ip_cidr[2]]), 58, $3, $4;
    ++match_counter;
}

END {
    printf "### The total number of matched records in the IPInfoDB: %d\r\n", match_counter;
}

############################## User-defined Functions ##############################
function NUMrize(ip){
    if( split(ip, IPs, ".") != 4 )
    {
        print "[IP Field Error]Total number of IP field is invalid";
        exit 4;
    }
    return int(IPs[1])*CLS_A + int(IPs[2])*CLS_B + int(IPs[3])*CLS_C + int(IPs[4]);
}

function IPrize(num){
    a = int(num/CLS_A);
    b = int( (num-a*CLS_A)/CLS_B );
    c = int( (num - a*CLS_A - b*CLS_B)/CLS_C );
    d = num - a*CLS_A - b*CLS_B - c*CLS_C;
    return sprintf("%d.%d.%d.%d", a, b, c, d );
}

処理の流れとしては、

  1. BEGIN ブロックで FS に読み込むファイルのセパレータを設定。カウンターを初期化。OFFSET 配列は CIDR 計算用に使う。配列番号をビット幅に見立ててその取り得る値の数から1を引いた数(例:番目の配列なら2の乗 - 1で15となる)
  2. /正規表現/ ブロックで抽出条件(今回の場合は中国 or 香港 or 台湾 or 日本)に一致したレコードのみを処理する(awk では or を 釗釗 と書く)
    1. 二重引用符(ダブルクォーテーション)を削除
    2. 第2フィールドをスラッシュ(/)の前後で分割し、それぞれを ip_cidr 配列に格納
    3. 10進数化した ip_cidr[1](開始IPアドレス) が第1フィールドと同じか確認
    4. 終了アドレスは第1フィールドに CIDR から計算した OFFSET を加えて IP 文字列に変換(IPrize)する
    5. match_counter を加算(+1)
  3. END ブロックで処理した行数(match_counter)をコメント文として出力

下記のようにコマンドを実行する

gawk -f IPInfoDB2ipfilter.awk ip_group_country.csv

出力結果のサンプル

17.80.0.0       - 17.80.127.255   , 058 , [HK]Hong Kong
17.83.128.0     - 17.83.255.255   , 058 , [JP]Japan
24.233.71.245   - 24.233.71.245   , 058 , [JP]Japan
32.60.34.16     - 32.60.34.23     , 058 , [JP]Japan
         中 略
222.240.0.0     - 222.247.255.255 , 058 , [CN]China
222.248.0.0     - 222.249.255.255 , 058 , [CN]China
222.250.0.0     - 222.250.255.255 , 058 , [TW]Taiwan
222.251.0.0     - 222.251.127.255 , 058 , [TW]Taiwan
### The total number of matched records in the IPInfoDB: 5855

5.数値をゼロ埋めに変更する(例:1 ⇒ 001)

下記のようにコマンドを実行する

gawk -f IPInfoDB2ipfilter.awk ip_group_country.csv | gawk -f FillByZero.awk

出力結果のサンプル

017.080.000.000 - 017.080.127.255 , 058 , [HK]Hong Kong
017.083.128.000 - 017.083.255.255 , 058 , [JP]Japan
024.233.071.245 - 024.233.071.245 , 058 , [JP]Japan
032.060.034.016 - 032.060.034.023 , 058 , [JP]Japan
         中 略
222.240.000.000 - 222.247.255.255 , 058 , [CN]China
222.248.000.000 - 222.249.255.255 , 058 , [CN]China
222.250.000.000 - 222.250.255.255 , 058 , [TW]Taiwan
222.251.000.000 - 222.251.127.255 , 058 , [TW]Taiwan
### The total number of matched records in the IPInfoDB: 5855

6.IPアドレスの範囲が連続しているレコードを連結してファイルサイズを減らす

  • 前々回同様、IP範囲で連結可能なものはつなげてデータ量を削減する
  • まったく同じ awkスクリプト(ConnectRecords.awk)を使うのでここでは割愛
gawk -f IPInfoDB2ipfilter.awk ip_group_country.csv | gawk -f FillByZero.awk | gawk -f ConnectRecords.awk
  • 約半分に削減される。処理後の出力のサンプルは以下の通り
017.080.000.000 - 017.080.127.255 , 058 , [HK]Hong Kong
017.083.128.000 - 017.083.255.255 , 058 , [JP]Japan
024.233.071.245 - 024.233.071.245 , 058 , [JP]Japan
032.060.034.016 - 032.060.034.023 , 058 , [JP]Japan
         中 略
222.166.000.000 - 222.229.079.255 , 058 , [HK]Hong Kong_[CN]China_[CN]China_以後省略
222.229.096.000 - 222.230.255.255 , 058 , [JP]Japan_[JP]Japan_[JP]Japan
222.231.064.000 - 222.231.255.255 , 058 , [JP]Japan_[JP]Japan
222.240.000.000 - 222.251.127.255 , 058 , [CN]China_[CN]China_[TW]Taiwan_[TW]Taiwan
### The total number of matched records: 5855   Compacted: 2795
7.使い方

作成した ipfilter.dat を対応アプリに読み込ませます
さすがに飽きてきた...国別のIPアドレスを抽出するのは今回が最後

まとめ

4行で ipfilter.dat が作成できた

wget -nd -nc http://mirrors.portafixe.com/ipinfodb/ip_database/current/ipinfodb_one_table_full_country.csv.zip
unzip -u  ipinfodb_one_table_full_country.csv.zip
bzip2 -dc ip_group_country.csv.bz2 >ip_group_country.csv
gawk -f IPInfoDB2ipfilter.awk ip_group_country.csv | gawk -f FillByZero.awk | gawk -f ConnectRecords.awk >ipfilter.dat
備考
  • 前々回同様、第3フィールドが長すぎる場合、awk で出力(print)しているところを substr(comment, 1, 文字数) とすることで長さを制限することができます