digital 千里眼 @abp_jp

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

国を指定して ipfilter.dat を作成する例2:maxmind.com のデータ GeoIP Country を利用した場合

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

2009-07-05 修正

前回は apnic.net のデータを使って ipfilter.dat を作成した。Wikipedia によると APNIC はアジアパシフィック地域におけるIPアドレス等を管理している非営利の地域インターネットレジストリ(RIR)だ。ここのデータが最も信頼できるわけだが、一方で一般企業によるIP範囲のリストもある。ということで、検証ついでに同条件で抽出てみる

前提条件
  • 前回と同じ
  • 違いは元データが maxmind.com の「GeoLite Country」であること

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

  • データは毎月初に更新される
wget -nd -nc http://geolite.maxmind.com/download/geoip/database/GeoIPCountryCSV.zip
  • 短期間で何回もダウンロードしようとするとアクセスブロックされるので注意
2.解凍
unzip -u ./GeoIPCountryCSV.zip

3.データ概要

  • 解凍後のファイルサイズは8メガ弱。約10万行でカンマ区切り
  • 各フィールドについての資料(英語)はここ
フィールド 意味
1 開始IPアドレス
2 終了IPアドレス
3 10進数化した開始IPアドレス
4 10進数化した終了IPアドレス
5 ISO 3166 ベースの国コード
6 国名
"2.6.190.56","2.6.190.63","33996344","33996351","GB","United Kingdom"
"3.0.0.0","4.17.135.31","50331648","68257567","US","United States"
"4.17.135.32","4.17.135.63","68257568","68257599","CA","Canada"
          中 略
"222.250.0.0","222.251.127.255","3740925952","3741024255","TW","Taiwan"
"222.251.128.0","222.251.255.255","3741024256","3741057023","KR","Korea, Republic of"
"222.252.0.0","222.255.255.255","3741057024","3741319167","VN","Vietnam"

4.ipfilter.dat 形式に変換

  • awk を使います。コードは以下の通り
  • ファイル名は"GeoIPCountry2ipfilter.awk"とします
BEGIN {
    FS = ",";
#    OFS = ",";
    match_counter = 0;
}

/"CN","China"/ || /"HK","Hong Kong"/ || /"TW","Taiwan"/ || /"JP","Japan"/ {
   gsub( /"/, "" );
   gsub( /,/, " ", $6);
   printf "%-s - %-s , %3s , [%s]%s\r\n", $1, $2, 57, $5, $6;
   ++match_counter;
}

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

処理の流れとしては

  1. BEGIN ブロックで FS に読み込むファイルのセパレータを設定。カウンターを初期化
  2. /正規表現/ ブロックで抽出条件(今回の場合は中国 or 香港 or 台湾 or 日本)に一致したレコードのみを処理する(awk では or 条件を ¦¦ と書く)
    1. 二重引用符(ダブルクォーテーション)を削除
    2. 国名にカンマを含む場合はホワイトスペースに置換
    3. 入力ファイルのフィールドの順番を変更
    4. match_counter を加算(+1)
  3. END ブロックで処理した行数(match_counter)をコメント文として出力

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

gawk -f GeoIPCountry2ipfilter.awk GeoIPCountryWhois.csv

出力結果のサンプル

32.60.34.16     - 32.60.34.23     , 057 , [JP]Japan
32.60.34.192    - 32.60.34.207    , 057 , [JP]Japan
43.0.0.0        - 43.255.255.255  , 057 , [JP]Japan
         中 略
222.231.64.0    - 222.231.255.255 , 057 , [JP]Japan
222.240.0.0     - 222.249.255.255 , 057 , [CN]China
222.250.0.0     - 222.251.127.255 , 057 , [TW]Taiwan
### The total number of matched records in the GeoIPCountry List: 3554

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

  • awk を使います。コードは以下の通り
  • ファイル名は"FillByZero.awk"とします
BEGIN {
    FS = " , ";
#    OFS = ",";
    match_counter = 0;
}

/^ *[^#]/ && /-/ && /,/ {
  if( split($1, IPFromTo, " - ") != 2 ){
    print "[ERROR]Total number of IP field is invalid\r\n";
    exit 2;
  }
  gsub(",", " ", $3);
  printf "%-s - %-s , %03d , %s\r\n", FillByZero(IPFromTo[1]), FillByZero(IPFromTo[2]), int($2), $3;
  ++match_counter;
}

END {
  if(match_counter > 0){
    printf "### The total number of matched records: %d\r\n", match_counter;
  }
}

############################## User-defined Functions ##############################
function FillByZero(ip){
  gsub(" ", "", ip);
  if( split(ip, IPs, ".") != 4 ){
    print "[ERROR]Total number of IP field is invalid\r\n";
    exit 4;
  }
  return sprintf("%03d.%03d.%03d.%03d", int(IPs[1]), int(IPs[2]), int(IPs[3]), int(IPs[4]) );
}
  1. BEGIN ブロックで FS に読み込むファイルのセパレータを設定。カウンターを初期化
  2. /正規表現/ ブロックでコメント行以外のレコードのみ処理する
    1. 第1フィールドを " - " で分割
    2. 第3フィールドにカンマを含む場合はホワイトスペースに置換
    3. printf で出力(IPアドレス部は FillByZero 関数でゼロ埋め)
    4. match_counter を加算(+1)
  3. END ブロックで処理した行数(match_counter)をコメント文として出力
  • FillByZero 関数でIPアドレスの文字列を
    1. ピリオドで分割
    2. それぞれを数値化
    3. ゼロ埋め
    4. 再びピリオドで連結しゼロ埋めIPアドレスの完成

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

gawk -f GeoIPCountry2ipfilter.awk GeoIPCountryWhois.csv | gawk -f FillByZero.awk

出力結果のサンプル

032.060.034.016 - 032.060.034.023 , 057 , [JP]Japan
032.060.034.192 - 032.060.034.207 , 057 , [JP]Japan
043.000.000.000 - 043.255.255.255 , 057 , [JP]Japan
         中 略
222.231.064.000 - 222.231.255.255 , 057 , [JP]Japan
222.240.000.000 - 222.249.255.255 , 057 , [CN]China
222.250.000.000 - 222.251.127.255 , 057 , [TW]Taiwan
### The total number of matched records in the GeoIPCountry List: 3554

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

  • 前回同様、IP範囲で連結可能なものはつなげてデータ量を削減する
  • まったく同じ awkスクリプト(ConnectRecords.awk)を使うので説明は割愛
gawk -f GeoIPCountry2ipfilter.awk GeoIPCountryWhois.csv | gawk -f FillByZero.awk | gawk -f ConnectRecords.awk >ipfilter.dat
  • 20%強削減される。処理後の出力サンプルは下記の通り
032.060.034.016 - 032.060.034.023 , 057 , [JP]Japan
032.060.034.192 - 032.060.034.207 , 057 , [JP]Japan
043.000.000.000 - 043.255.255.255 , 057 , [JP]Japan
          中 略
222.229.096.000 - 222.230.255.255 , 057 , [JP]Japan
222.231.064.000 - 222.231.255.255 , 057 , [JP]Japan
222.240.000.000 - 222.251.127.255 , 057 , [CN]China_[TW]Taiwan
### The total number of matched records: 3554	Compacted: 2737

できあがったデータを前回の APNIC ベースのデータと比べるとけっこう違いがあります。何故でしょう...(?_?) これについては後日改めて調べてみることにします

7.使い方

作成した ipfilter.dat を対応アプリに読み込ませます

まとめ

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

wget -nd -nc http://geolite.maxmind.com/download/geoip/database/GeoIPCountryCSV.zip
unzip -u ./GeoIPCountryCSV.zip
gawk -f GeoIPCountry2ipfilter.awk GeoIPCountryWhois.csv | gawk -f FillByZero.awk | gawk -f ConnectRecords.awk >ipfilter.dat
備考
  • 前回同様、第3フィールドが長すぎる場合、awk で出力(printf)しているところを substr(comment, 1, 文字数) とすることで長さを制限することができる