任意のIPv4アドレスRangeをPrefix表記に変換する[はじめてのRuby]
"192.168.192.48 - 192.168.192.79"といった始点と終点によって定義されるIPアドレス空間を,"192.168.192.48/27"というPrefix表記に変換するスクリプトです.
サブネットマスクの計算方法は下のリンクを参照してください.
基礎から学ぶWindowsネットワーク:第7回 IPアドレスとネットマスク (3/3) - @IT
Input Data
IPアドレスのリストをI-Blocklistというサイトから入手しました.このサイトは,世界中の政府機関,教育機関,研究機関などが公開しているIPアドレスのリストを提供しています.
今回は教育機関のIPアドレスのリストを使いました.
bt_edu.txt
China Education and Research Network - Nanjing:1.51.0.0-1.51.255.255 Jinan University:1.184.0.0-1.184.127.255 China Education and Research Network - Guangzhou:1.184.0.0-1.185.255.255 Istituto Statale Secondario Superiore Pacifici de Magistris Priverno:2.119.234.168-2.119.234.175 s0-1.championsschoolofrea.bbnplanet.net:4.0.32.62-4.0.33.4 Education Management Corporation:4.2.144.0-4.2.144.31 Concordia University:4.2.160.32-4.2.160.63 American Education Centers:4.2.161.32-4.2.161.39 United Schools District:4.2.161.96-4.2.161.127 Education Management Corporation:4.2.170.0-4.2.170.31 American Education Centers, Inc:4.2.173.16-4.2.173.23 United Schools District:4.2.173.96-4.2.173.127 Sewickley Academy:4.2.174.0-4.2.175.255 City of Pittsfield Schools:4.2.176.192-4.2.176.223 Taft School:4.2.176.240-4.2.176.255 Taft School:4.2.179.128-4.2.179.191 Alice Lloyd College:4.2.224.160-4.2.224.175 Learning Tree:4.2.225.0-4.2.225.63 Kennedy Krieger Institute:4.17.69.128-4.17.69.159 New Bedford Public Schools:4.17.112.0-4.17.112.255 ...
このように,1行ごとに「"組織名":"IPレンジ"」という形式で書かれています.
Script
はじめてRuby書きました.
def inet_aton ip ip.split(/\./).map{|c| c.to_i}.pack("C*").unpack("N").first end def inet_ntoa n [n].pack("N").unpack("C4").join(".") end def get_forward_prefixes(s,e) prefixes = [] while s <= e len = 32 - ((Math.log(e - s + 1) / Math.log(2)).to_i).floor prefixes << "#{inet_ntoa(s)}/#{len}" s = s + 2 ** (32 - len) end return prefixes end def get_backward_prefixes(s,e) prefixes = [] while s <= e len = 32 - ((Math.log(e - s + 1) / Math.log(2)).to_i).floor prefix = inet_ntoa(e - 2 ** (32 - len) + 1) prefixes << "#{prefix}/#{len}" e = e - 2 ** (32 - len) end return prefixes.reverse end def get_prefixes(s, e) return [] if e < s prefixes = [] if (s & 0x000000ff == 0) prefixes = get_forward_prefixes(s,e) else if (e & 0xffffff00) > (s & 0xffffff00) prefixes = get_backward_prefixes(s, (e & 0xffffff00) - 1) + get_forward_prefixes(e & 0xffffff00, e) else l = e - s + 1 base = s & 0xffffff00 width = 2 ** ((Math.log(l) / Math.log(2)).to_i.floor - 1) i = 0 while i < (s & 0x000000ff) i += width end prefixes = get_backward_prefixes(s, base + i - 1) + get_forward_prefixes(base + i, e) end end return prefixes end File.open("out.txt","w") do |out| open ("bt_edu.txt"){|file| puts "start" while l = file.gets a = l.split(/:/) b = a[1].split(/-/) for p in get_prefixes(inet_aton(b[0]),inet_aton(b[1])) do out.write(a[0] + ":" + p + "\n") end end } end puts "finished"
Output Data
実行結果は次のようになります.
out.txt
China Education and Research Network - Nanjing:1.51.0.0/16 Jinan University:1.184.0.0/17 China Education and Research Network - Guangzhou:1.184.0.0/15 Istituto Statale Secondario Superiore Pacifici de Magistris Priverno:2.119.234.168/29 s0-1.championsschoolofrea.bbnplanet.net:4.0.32.62/31 s0-1.championsschoolofrea.bbnplanet.net:4.0.32.64/26 s0-1.championsschoolofrea.bbnplanet.net:4.0.32.128/25 s0-1.championsschoolofrea.bbnplanet.net:4.0.33.0/30 s0-1.championsschoolofrea.bbnplanet.net:4.0.33.4/32 Education Management Corporation:4.2.144.0/27 Concordia University:4.2.160.32/27 American Education Centers:4.2.161.32/29 United Schools District:4.2.161.96/27 Education Management Corporation:4.2.170.0/27 American Education Centers, Inc:4.2.173.16/29 United Schools District:4.2.173.96/27 Sewickley Academy:4.2.174.0/23 City of Pittsfield Schools:4.2.176.192/27 Taft School:4.2.176.240/28 Taft School:4.2.179.128/26 Alice Lloyd College:4.2.224.160/28 Learning Tree:4.2.225.0/26 Kennedy Krieger Institute:4.17.69.128/27 New Bedford Public Schools:4.17.112.0/24 ...
このように,IPアドレスがPrefix表記に変換されています.
"s0-1.championsschoolofrea.bbnplanet.net"の行を見ると分かりますが,1通りのPrefix表記では表せない場合でも無理やり変換してくれます.
個人的にこれで何をしたいかというと,IPアドレスの探索やマッチングに使えたらいいなと思っています.そのプログラムは現在Pythonで書いているので後ほど...
補足
ちなみにPythonで書くと、こんなかんじになります。
>>> from ipaddress import ip_address, summarize_address_range >>> first = ip_address(u'192.168.1.0') >>> last = ip_address(u'192.168.1.200') >>> pre = list(summarize_address_range(first,last)) >>> for h in pre: ... print str(h) ... 192.168.1.0/25 192.168.1.128/26 192.168.1.192/29 192.168.1.200/32