最速IPアドレスマッチ研修会B!

もうすぐ2010年になるわけですが、携帯のサイトなどでアクセス元のIPアドレスがキャリアのIPアドレス帯域内にあるかどうかを確認したりすることがあると思います。

例えば、ローカルネットワークのアドレス(192.168.1.0/24)にマッチするかどうか

$env->{REMOTE_ADDR} =~ s/^192\.168\.1\.(?:\d+)$/

なんて書くことがあると思います。ここであげたローカルのアドレスのような/24のIPアドレス帯域であれば簡単に正規表現を書くことが可能ですが、/25, /26 /23、/22などのCIDRは正規表現で表すことはなかなか難しいです。

また、携帯キャリアのアドレスであることを確認しようとした場合、DoCoMoであれば

  • 210.153.84.0/24
  • 210.136.161.0/24
  • 210.153.86.0/24
  • 124.146.174.0/24
  • 124.146.175.0/24
  • 210.153.87.0/24
  • 203.138.180.0/24
  • 203.138.181.0/24
  • 203.138.203.0/24

と9つの/24の帯域を利用しており、1つ1つ正規表現に変換しマッチさせていくのは手作業も必要となりますし、ミスも起こりやすく、加えて正規表現のパフォーマンスも問題となってしまいます。

そこであるIPアドレスが、指定した帯域にマッチするモジュールを探すわけですが、CPANにはいくつも見つかります。
代表的なモジュールは以下になります

  • Net::CIDR::Lite
  • Net::IP::Match
  • Net::IP::Match::XS
  • Net::IP::Match::Regexp
  • Net::IP::Match::Bin
  • Net::Patricia

それぞれ特徴があるわけですが、まずはベンチマークです。Net::IP::Matchはベンチマークから除外しています

ベンチマーク結果
(モジュールはすべて2009-12-01時点で最新のバージョンを利用しています)

% perl ipmatch.pl
Benchmark: timing 300000 iterations of bin, cidr, patricia, regexp, xs...
       bin:  6 wallclock secs ( 5.46 usr +  0.01 sys =  5.47 CPU) @ 54844.61/s (n=300000)
      cidr:  6 wallclock secs ( 5.78 usr +  0.00 sys =  5.78 CPU) @ 51903.11/s (n=300000)
  patricia:  2 wallclock secs ( 1.85 usr +  0.00 sys =  1.85 CPU) @ 162162.16/s (n=300000)
    regexp:  2 wallclock secs ( 1.94 usr +  0.00 sys =  1.94 CPU) @ 154639.18/s (n=300000)
        xs:  1 wallclock secs ( 0.52 usr +  0.00 sys =  0.52 CPU) @ 576923.08/s (n=300000)

xsがかなり高速なことがわかりました。
ベンチマークscriptはgistに張り付けました。

実際の運用の場面では、Net::IP::Match::XSだけではチェックが不足してしまう場面があります。それは、確認するIPアドレスが正しいIPアドレスかどうかという点です。
力技ですがIPアドレスの書式を以下のような正規表現で確認した上で、使うといいかもしれません

$ip =~ m!^(?:2(?:5[0-5]|[0-4][0-9])|[0-1]?[0-9]{1,2})\.(?:2(?:5[0-5]|[0-4][0-9])|[0-1]?[0-9]{1,2})\.(?:2(?:5[0-5]|[0-4][0-9])|[0-1]?[0-9]{1,2})\.(?:2(?:5[0-5]|[0-4][0-9])|[0-1]?[0-9]{1,2})$!;

まとめ

今回紹介したモジュールのうち、IPv6に対応しているのはNet::CIDRのみだったりしますが、
IPv6にむけてIPアドレスの確認をしているコードを見直すのはどうでしょうか。