複数の ipfilter.dat を統合(マージ)してオリジナル ipfilter.dat を作成する方法
- 「%03s」は間違い。正しくは「%03d」
2010-08-06 修正
たくさん ipfilter.dat 作ったりダウンロードしても読み込めるファイルは1つだったり...orz
ということで、複数の ipfilter.dat を統合する方法です
前提条件
- ゼロ埋済み ipfilter.dat
- 統合対象のファイルはサブディレクトリに置かれているものとします
2.ソート
cat ./*/*.dat | C:\cygwin\bin\sort.exe -t . -n -k 1 -k 2 -k 3 -k 4 -k 4.10 -k 5 -k 6 -k 7
オプション | 意味 |
---|---|
t | 区切り文字を指定 |
n | 数値として比較するように設定 |
k | オプションでソートの優先度を指定 |
3.開始IPアドレスと終了IPアドレスが同一の行を統合
uniq を使い各行先頭から33文字目まで(IPアドレス部分)を比較し、同一と判定された場合は1行だけ残し隣接行を削除する
cat ./*/*.dat | C:\cygwin\bin\sort.exe -t . -n -k 1 -k 2 -k 3 -k 4 -k 4.10 -k 5 -k 6 -k 7 | uniq -w 33 -i
4.もうちょっと複雑な行の統合
gawk を使います(RemoveDuplicatedRecords.awk)
# Remove duplicated records or merge redundant records into one record. # # Date : 2009-06-24 # Author : k2jp # Condition : Input records must be sorted in advance. # BEGIN{ FS = " , "; # OFS = ","; CLS_A = 16777216; CLS_B = 65536; CLS_C = 256; match_counter = 0; rec_counter = 0; IP_start = ""; IP_end = ""; level = 0; comment = ""; NUMrizedIPstart = 0; NUMrizedIPend = 0; NUMrizedIPRange1 = 0; NUMrizedIPRange2 = 0; } /^[0-9]/ { ++match_counter; if( split($1, IPRange, " - ") != 2) { print "[ERROR]split error"; exit 3; } # trim white space for(eachIP in IPRange) { gsub(" ", "", IPRange[eachIP]); } # remove comma in the comment(comma shuld be used as a separator) gsub(",", " ", $3); if(match_counter != 1) { # calculate NUMrized IPs in advance for better performance NUMrizedIPstart = NUMrize(IP_start); NUMrizedIPend = NUMrize(IP_end); NUMrizedIPRange1 = NUMrize(IPRange[1]); NUMrizedIPRange2 = NUMrize(IPRange[2]); } if(match_counter == 1) { # The first record initialization IP_start = IPRange[1]; IP_end = IPRange[2]; level = int($2); comment = substr($3, 1, 10); }else if( (NUMrizedIPstart == NUMrizedIPRange1) || ( (NUMrizedIPRange1 <= NUMrizedIPend+1) && (NUMrizedIPend <= NUMrizedIPRange2) ) ){ # # Previous Record: Start IP|---->End IP :Ignore # Current Record: Start IP|------>End IP :OK # # or # # Previous Record: Start IP|---->End IP :Use Start IP # Current Record: Start IP |---->End IP :Use End IP # IP_end = IPRange[2]; level = level < int($2) ? level : int($2); comment = sprintf("%s_%s", comment, substr($3, 1, 10)); }else if( (NUMrizedIPRange1 <= NUMrizedIPend+1) && (NUMrizedIPRange2 < NUMrizedIPend) ) { # # Previous Record: Start IP|--------->End IP :OK # Current Record: Start IP |---->End IP :Ignore # level = level < int($2) ? level : int($2); comment = sprintf("%s_%s", comment, substr($3, 1, 10)); }else { # # Previous Record: Start IP|---->End IP :Print buffered record # Current Record: Start IP |---->End IP :Set current record to the next previous # ++rec_counter; printf "%-s - %-s , %03d , %s\r\n", IP_start, IP_end, level, substr(comment, 1, 150); IP_start = IPRange[1]; IP_end = IPRange[2]; level = int($2); comment = substr($3, 1, 10); } } END{ if(match_counter > 0) { ++rec_counter; printf "%-s - %-s , %03d , %s\r\n", IP_start, IP_end, level, substr(comment, 1, 150); # print statistics at final line printf "### The total number of matched records: %d\tCompacted: %d\r\n", match_counter, rec_counter; } } ############################## User-defined Functions ############################## function NUMrize(ip){ if( split(ip, IPs, ".") != 4 ) { print "[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]); }
長くてスンマセン。細かく書くときりがないので else if の部分だけざっくりと説明
- 事前にソートされているので開始IPアドレスは前行のレコードと同じ場合(1つ目のelse if)、前行のIP範囲は読み捨て(開始IPアドレスはどちらのを使っても同じ)
- 開始IPアドレスがずれていて、終了IPアドレスが前行より現在行の方が後の場合(1つ目のelse if)、前行の開始IPアドレスは使うが終了IPアドレスは現在行を使う
- 現在行のIP範囲が前行のIP範囲以内である場合(2つ目のelse if)、現在行は無視
- 全行の終了IPアドレスが現在行の開始IPアドレスより前にある場合(3つ目のelse if)、変数に入っている統合済みのIPアドレス範囲を出力し、現在行を変数の初期値として再設定する
最終的にコマンドはこうなります(たった1行)
cat ./*/*.dat | C:\cygwin\bin\sort.exe -t . -n -k 1 -k 2 -k 3 -k 4 -k 4.10 -k 5 -k 6 -k 7 | uniq -w 33 -i | gawk -f RemoveDuplicatedRecords.awk >ipfilter.dat
手元のデータでやってみたところ、(重複が多かったので)行数ベースで7割程度を削減できた!!