2014年1月19日日曜日

Tremaを試す ~simple-ruter その6~

今回はからrouter-utilsを見ていきいます。
このプログラムはルータに必要な機能を使えるようにします。

今回はこのプログラムに定義されているクラスとメソッドについて簡単に説明します。

(router-utils.rb)
  1  require 'ipaddr'
  2  
  3  def get_checksum csum, val
  4    sum = ( ~csum & 0xffff ) + val
  5    while sum > 0xffff
  6      sum = ( sum & 0xffff ) + ( sum >> 16 )
  7    end
  8    ~sum & 0xffff
  9  end
 10  
 11  class IPAddr
 12    def to_a
 13      self.to_s.split( "." ).collect do | each |
 14        each.to_i
 15      end
 16    end
 17  end
 18  
 19  class EthernetHeader
 20    attr_accessor :macda, :macsa, :eth_type
 21  
 22    def initialize macda, macsa, eth_type
 23      @macda = macda
 24      @macsa = macsa
 25      @eth_type = eth_type
 26    end
 27  
 28    def pack
 29      ( @macda.to_a + @macsa.to_a + [ eth_type ] ).pack( "C12n" )
 30    end
 31  end
 32  
 33  class ARPPacket
 34    attr_accessor :type, :tha, :sha, :tpa, :spa
 35  
 36    def initialize type, tha, sha, tpa, spa
 37      @type = type
 38      @tha = tha
 39      @sha = sha
 40      @tpa = tpa
 41      @spa = spa
 42    end
 43  
 44    def pack
 45      eth_header = EthernetHeader.new( @tha, @sha, 0x0806 )
 46  
 47      # arp
 48      arp = [ 0x00, 0x01, 0x08, 0x00, 0x06, 0x04, 0x00, @type ]
 49      arp += @sha.to_a + @spa.to_a + @tha.to_a + @tpa.to_a
 50  
 51      while arp.length < 46 do
 52        arp += [ 0x00 ]
 53      end
 54  
 55      eth_header.pack + arp.pack( "C*" )
 56    end
 57  end
 58  
 59  class ARPRequest < ARPPacket
 60    def initialize sha, tpa, spa
 61      tha = [ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff ]
 62      super( 1, tha, sha, tpa, spa )
 63    end
 64  end
 65  
 66  class ARPReply < ARPPacket
 67    def initialize tha, sha, tpa, spa
 68      super( 2, tha, sha, tpa, spa )
 69    end
 70  end
 71  
 72  class IPPacket
 73    attr_accessor :id, :protocol, :daddr, :saddr, :payload
 74  
 75    def initialize options
 76      @id = options[ :id ]
 77      @protocol = options[ :protocol ]
 78      @daddr = options[ :daddr ]
 79      @saddr = options[ :saddr ]
 80      @payload = options[ :payload ]
 81      @tot_len = 20 + payload.length
 82    end
 83  
 84    def pack
 85      csum = get_checksum( 0, 0x4500 )
 86      header = [ 0x45, 0x00 ] # Version, IHL, ToS
 87  
 88      csum = get_checksum( csum, @tot_len )
 89      header += [ @tot_len >> 8, @tot_len & 0xff ] # len
 90  
 91      csum = get_checksum( csum, @id )
 92      header += [ @id >> 8, @id & 0xff ] # ID
 93  
 94      csum = get_checksum( csum, 0x4000 )
 95      header += [ 0x40, 0x00 ] # Flags, Frag offset
 96  
 97      csum = get_checksum( csum, 0x40 * 0x100 + @protocol )
 98      header += [ 0x40, @protocol ] # ttl, protocol
 99  
100      csum = get_checksum( csum, @saddr.to_i >> 16 )
101      csum = get_checksum( csum, @saddr.to_i & 0xffff )
102      csum = get_checksum( csum, @daddr.to_i >> 16 )
103      csum = get_checksum( csum, @daddr.to_i & 0xffff )
104      header += [ csum >> 8, csum & 0xff ] # checksum
105      header += @saddr.to_a + @daddr.to_a
106  
107      header.pack( "C*" ) + @payload.pack
108    end
109  end
110  
111  class ICMPPacket
112    attr_reader :payload, :length
113  
114    def initialize type, code, payload
115      @type = type
116      @code = code
117      @payload = payload
118      @length = 4 + payload.length
119    end
120  
121    def pack
122      @checksum = get_checksum( 0, @type * 0x100 + @code )
123  
124      words = @payload.pack( "C*" ).unpack( "n*" )
125      words.each do | each |
126        @checksum = get_checksum( @checksum, each )
127      end
128  
129      [ @type, @code, @checksum ].pack( "C2n" ) + @payload.pack( "C*" )
130    end
131  end
132  
133  class ICMPEchoReply < ICMPPacket
134    def initialize payload
135      super( 0x00, 0x00, payload )
136    end
137  end
138  
139  module RouterUtils
140    def create_arp_request_from interface, addr
141      arp = ARPRequest.new( interface.hwaddr, addr, interface.ipaddr )
142      arp.pack
143    end
144  
145    def create_arp_reply_from message, replyaddr
146      arp = ARPReply.new( message.macsa, replyaddr, message.arp_spa, message.arp_tpa )
147      arp.pack
148    end
149  
150    def create_icmpv4_reply entry, interface, message
151      offset = 14 + 20 + 4
152      payload = message.data.unpack( "C*" )[ offset .. message.data.length - 1 ]
153      icmp = ICMPEchoReply.new( payload )
154      ip_packet = IPPacket.new( :id => message.ipv4_id,
155                                :protocol => message.ipv4_protocol,
156                                :ttl => message.ipv4_ttl,
157                                :daddr => message.ipv4_saddr,
158                                :saddr => message.ipv4_daddr,
159                                :payload => icmp )
160      eth_header = EthernetHeader.new( entry.hwaddr, interface.hwaddr, 0x0800 )
161  
162      eth_header.pack + ip_packet.pack
163    end
164  end


get_checksumメソッド
IPv4パケットのチェックサムを計算する時に使用するメソッドです。

IPAddrクラス
1行目のrequireでipaddrを読み込む事で、標準ライブラリIPAddrクラス使えるようになっていますが、そのIPAddrクラスに定義を追加しています。

EthernetHeaderクラス
イーサネットヘッダーを構成するためのクラスです。

ARPPacketクラス
ARPパケットを構成するためのクラスです。

ARPRequestクラス
ARPリクエストのパケットを作るクラスです。
ARPPacketクラスを継承しています。

ARPReplyクラス
ARPリプライのパケットを作るクラスです。
ARPPacketクラスを継承しています。

IPPacketクラス
IPパケットを作るクラスです
ここではICMPのリプライパケットを送るのに使われています。

ICMPPacketクラス
ICMPパケットを構成するためのクラスです。

ICMPEchoReplyクラス
ICMPのリプライパケットを作るクラスです。
ICMPPacketクラスを継承しています。

RouterUtilsモジュール
create_arp_request_fromメソッドとcreate_arp_reply_fromメソッドとcreate_icmpv4_replyメソッドを定義しています。


今回は大まかな説明だけになります。
個人的にはsimple-router全体でこのソースが一番難しかったです。

次回以降、何度かに分けてこのソースを読んでいきます。

0 件のコメント:

コメントを投稿