2014年1月22日水曜日

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

今回はrouter-utilsのソースを前回の続きから見ていきます。

 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  


72~82行目
IPパケットを作るクラスです。
73行目はアクセサの宣言をしています。

75行目のinitializeで引数を変数にセットしています。
@idには分割したIPパケットを復元する時に使用される識別子が入ります。
@protocolにはICMPやTCPといったをトランスポート層のプロトコルを識別する番号が入ります。
@daddrには宛先のIPアドレスが入ります。
@saddrには送信元のIPアドレスが入ります。
@payloadにはペイロード(転送したいデータ)が入ります。
@tot_lenにはIPヘッダーのバイト長の20と、ペイロードの長さを足した値が入ります。

84~109行目
IPパケットを作るメソッドです。
csum変数にはIPパケットのチェックサムが入ります。
header変数にはIPヘッダのフォーマットに合わせてデータを追加していきます。

86行目はバージョンとヘッダ長、サービス種別をheader変数に追加しています。
IPv4の場合、バージョンには固定で4が入ります。
ヘッダ長にはIPv4ヘッダの固定長部分の長さである20バイトを4バイトで割った数の5が入ります。
サービス種別はこのプログラムでは0x00が固定でセットされています。

89行目はデータ全長をheader変数に追加しています。
ここでは先頭から8け桁取り出すために8ビット右シフトし、末尾から8ビット取り出すために0xffで論理積をしています。
なぜこんなことをしているのか疑問でしたが、どうやらpackメソッドを使うのに8ビットずつの配列にする必要があるためだと思います。

91行目は識別子をheader変数に追加しています。
95行目はフラグと断片位置をheader変数に追加しています。
このプログラムではIPパケットを分割禁止にしています。
98行目はttlとプロトコルをheader変数に追加しています。
このプログラムではttlは64がセットされています。

100行目から104行目は、宛先IPアドレスと送信元IPアドレスからチェックサムの計算を行っています。
IPヘッダのフォーマットでは、チェックサムはプロトコルの次のフィールドなので、header変数にチェックサムの値を追加する前にチェックサムより後ろのフィールドの値でチェックサムの計算をしています。
104行目はチェックサムをheader変数に追加しています。
105行目は宛先IPアドスと送信元IPアドレスをheader変数に追加しています。

107行目でheader変数をバイナリに変換し、@payload変数をpackメソッドで変換して追加します。

111~119行目
ICMPパケットを作るクラスです。
112行目はアクセサの宣言をしています。

114行目のinitializeで引数を変数にセットしています。
@typeには機能コードが入ります。
@codeには詳細な機能コードが入ります。
@payloadにはペイロード(転送したいデータ)が入ります。
@lengthにはICMPヘッダーのバイト長の4と、ペイロードの長さを足した値が入ります。

121~131行目
ICMPパケットを作るメソッドです。
@checksum変数にはチェックサムの値が入ります。

122行目の@checksum変数はget_checksumメソッドで@type変数と@code変数からチェックサムを求めています。
カッコ内の0x100は@type変数の値を256倍して16bitの先頭8bitに@type変数の値がくるようにします。
そこに@code変数の値を足し、こちらは末尾8bitが@code変数の値がくるようにします。

124行目は@payload変数の値をバイナリにし、unpackで16bitごとの配列にします。
その次のループでチェックサムを計算します。

129行目でICMPヘッダと@payload変数をバイナリにします。

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

superでスーパークラスであるICMPPacketクラスのinitializeメソッドを呼び出します。
1つ目の引数はICMPのタイプでエコー応答の0、2つ目の引数はコードでエコー応答は固定で0が入ります。

今回はここまで。
router-utilsはあと少しで終わりです。

0 件のコメント:

コメントを投稿