2013年11月29日金曜日

プレフィックスとは何だっ!?

今度はsimple-routerを見たいところですが、さすがに難しいですね。
パッと見てわかるようになりたいです。

何よりルータのプログラムを作る(読む)には、ルータの仕組みが分かっていないと難しいです。
「クラウド時代のネットワーク技術 OpenFlow実践入門」にも簡単に仕組みが書いてありました。

今回はsimple-routerから離れて、IPアドレスのプレフィックスについて調べてみました。
こんな事はWebに詳しく書いてあるじゃんって思うかもしれませんが、自分の理解のためにまとめてみます。

ご存じのとおり、IPアドレスは32bitで表します(IPv4)。
プレフィックスとは、IPアドレスをネットワーク部分とホスト部分に分けたうちのネットワーク部分の長さを表すものです。
よく「192.168.1.0/24」という書き方を見ると思いますが、この「/24」の部分をプレフィックス(プレフィックス長)と言います。

「/24」の24はビット数の事で、IPアドレスの先頭から何ビットまでがネットワーク部分かを表します。
そのプレフィックスからどうやってネットワーク部分を表すかというと、以下のようになります。

1.IPアドレスを2進数で表現すると32桁の0か1の並びになります
(例)192.168.1.10の場合は以下のようになります。
※見やすいように8けたで区切っています
11000000.10101000.00000001.00001010  ・・・①

2.IPアドレス32桁のうち、プレフィックスの数だけ先頭から1を並べ残りは0にします
この値をサブネットマスクといいます。

(例)192.168.1.10/24の場合は以下のようになります。
11111111.11111111.11111111.00000000  ・・・②

この2つのIPアドレスを縦にAND演算するとネットワーク部分のIPアドレスが取り出せます。
※AND演算は0 AND 0 = 0、1 AND 0 = 0、1 AND 1 = 1 になります。

①11000000.10101000.00000001.00001010
②11111111.11111111.11111111.00000000
-----------------------
 11000000.10101000.00000001.00000000

この値を10進数表示にすると、192.168.1.0になります

以上です

2013年11月24日日曜日

Tremaを試す ~traffic monitor その2~

今回はtraffic-monitor.rbから呼ばれているfdb.rbファイルを見ていきます。

その前にRubyのブロック引数について少し説明をしておきます。

ブロックとは、{ } や do ~ end で囲まれた処理を言います。
このブロックをメソッドの引数として渡す事が出来ます。

簡単な例を書きました。

(test.rb)
  def zandaka &block
    bank = { "taro" => 10000, "jiro" => 5000, "sabro" => 20000 }
    bank.each &block
  end

  zandaka { | k, v | puts "#{k}氏の残高は#{v}円ナリ" }

  zandaka do | k ,v |
    puts "#{k}君は貯金が#{v}円もあるらしい"
  end

$ ruby test.rb
sabro氏の残高は20000円ナリ jiro氏の残高は5000円ナリ taro氏の残高は10000円ナリ sabro君は貯金が20000円もあるらしい jiro君は貯金が5000円もあるらしい taro君は貯金が10000円もあるらしい


今回はtraffic_monitorのソースコードの内容に近い形の説明だけしておきます。
簡単な例ではありますが、zandakaメソッドを実行すると { } や do ~ end の間に書かれた処理が実行されているのが分かると思います。


ではfdbのソースを見てみましょう。
examplesの中のtraffic_monitor配下にあるfdb.rbファイルを見てください。

(fdb.rb)
 1  class FDB
 2    def initialize
 3      @db = {}
 4    end
 5  
 6    def lookup mac
 7      @db[ mac ]
 8    end
 9  
10    def learn mac, port_number
11      @db[ mac ] = port_number
12    end
13  end


2~4行目
initializeでは、@dbというインスタンス変数に空のハッシュ(連想配列)を定義しています。
この@dbには11行目の処理でmacアドレスをハッシュキーにして物理ポート番号を代入している事がわかります。

6~8行目
lookupメソッドは引数のmacアドレスをハッシュキーにして物理ポートの番号を返すメソッドです。
@dbからハッシュ値が取得できない場合はnilを返します。

10~12行目
learnメソッドは引数のmacアドレスをハッシュキーにして物理ポート番号を@dbに代入しています。
@dbにハッシュキーがあれば物理ポート番号を更新、無ければ新たにハッシュ要素を追加します。


続いてCounterのソースコードを見ていきます。
examplesの中のtraffic_monitor配下にあるcounter.rbファイルを見てください。

(counter.rb)
 1  class Counter
 2    def initialize
 3      @db = {}
 4    end
 5  
 6    def add mac, packet_count, byte_count
 7      @db[ mac ] ||= { :packet_count => 0, :byte_count => 0 }
 8      @db[ mac ][ :packet_count ] += packet_count
 9      @db[ mac ][ :byte_count ] += byte_count
10    end
11  
12    def each_pair &block
13      @db.each_pair &block
14    end
15  end

2~4行目
initializeでは@dbというインスタンス変数に空のハッシュ(連想配列)を定義しています。
この@dbには、8~9行目の処理でmacアドレスをハッシュキーにし、さらにパケットの送信回数とバイト数のハッシュを作っています。

6行目
addメソッドでは引数のmacアドレスをハッシュ値にしてパケットの送信回数とバイト数を管理します。

7行目
||=は左辺がnilだった場合に右辺の代入を行います。
@dbのハッシュ値に指定したmacアドレスが無い場合、パケットの送信回数とバイト数に0が代入されます。

8~9行目
@dbからmacアドレスをハッシュキーにしてハッシュ値にパケット送信回数とバイト数を加算しています。

12~14行目
eache_pairメソッドは&blockというブロック引数の内容を実行します。
実行するブロックの内容はtraffic-monitor.rbでメソッドを実行する時に指定しています。
@dbオブジェクトが保持しているmacアドレスをハッシュキーにして@dbオブジェクトのパケット送信回数とバイト数を画面に出力します。


次に、traffic_monitor.confの仮想環境の構成です。

(traffic_monitor.conf)
 1  vswitch {
 2    datapath_id "0xabc"
 3  }
 4  
 5  vhost ("host1") {
 6    ip "192.168.0.1"
 7    mac "00:00:00:00:00:01"
 8  }
 9  
10  vhost ("host2") {
11    ip "192.168.0.2"
12    mac "00:00:00:00:00:02"
13  }
14  
15  link "0xabc", "host1"
16  link "0xabc", "host2"


1~13行目
スイッチ1台、ホスト2台の構成になっています。
15~16行目
スイッチとホスト1、スイッチとホスト2を接続する構成にしています。


では、以下のコマンドでtraffic_monitorを実行してみましょう。

trema run ./traffic-monitor.rb -c ./traffic_monitor.conf


$ trema run ./traffic-monitor.rb -c ./traffic_monitor.conf


traffic_monitorを起動した端末を端末1とします。
プログラムを実行して10秒後すると時間が表示されます。
まだホスト1とホスト2でパケットの送受信をしていないため、パケットの送信回数と送信バイト数は表示されません。

$ trema run ./traffic-monitor.rb -c ./traffic_monitor.conf
Wed Oct 02 00:40:02 +0900 2013
Wed Oct 02 00:40:12 +0900 2013
Wed Oct 02 00:40:22 +0900 2013
…

Ctl-Cで停止します。


ではこれから以下の事を確認していきます。
・端末2でホスト1とホスト2でパケットの送受信をすると、端末1に表示されるパケットの送信回数と送信バイト数が増える事
・所定の時間が経過するとフローエントリが削除される事
・フローエントリが削除されたタイミングでパケットの送信回数と送信バイト数が計上される事

確認しやすいようにtraffic-monitor.rbのソースコードを少し修正します。
画面表示が多すぎて見づらいため、パケットの送信回数と送信バイト数の表示間隔を20秒にします。
 periodic_timer_eventの引数を20に変更
 (変更前)periodic_timer_event :show_counter, 10
 (変更後)periodic_timer_event :show_counter, 20

フローエントリが10秒で削除されてしまい確認しづらいため、フローエントリの有効期限を60秒にします。
 send_flow_mod_addメソッドの:hard_timeoutを600に変更
 (変更前) :hard_timeout => 10
 (変更後) :hard_timeout => 60


動作確認をするにあたり、traffic-monitor.rbを実行した端末とは別に、もう一つ端末を起動して操作します。
こちらの端末を端末2とします。

まずは端末1で先ほどと同じコマンドを実行し、traffic_monitorを起動します。

trema run ./traffic-monitor.rb -c ./traffic_monitor.conf

では端末2から以下のコマンドで、ホスト1からホスト2、ホスト2からホスト1へパケットを送ります。
コマンドを実行して20秒したら、もう一度ホスト1からホスト2、ホスト2からホスト1へパケットを送ります。

trema send_packets --source host1 --dest host2
trema send_packets --source host2 --dest host1
(20秒後)
trema send_packets --source host1 --dest host2
trema send_packets --source host2 --dest host1
trema show_stats host1
trema show_stats host2

<端末2>
$ trema send_packets --source host1 --dest host2$ trema send_packets --source host2 --dest host1
$ trema show_stats host1
Sent packets:
ip_dst,tp_dst,ip_src,tp_src,n_pkts,n_octets
192.168.0.2,1,192.168.0.1,1,2,100
Received packets:
ip_dst,tp_dst,ip_src,tp_src,n_pkts,n_octets
192.168.0.1,1,192.168.0.2,1,2,100
$ trema show_stats host2
Sent packets:
ip_dst,tp_dst,ip_src,tp_src,n_pkts,n_octets
192.168.0.1,1,192.168.0.2,1,2,100
Received packets:
ip_dst,tp_dst,ip_src,tp_src,n_pkts,n_octets
192.168.0.2,1,192.168.0.1,1,2,100

この時、端末1にはホスト1とホスト2のmacアドレスとパケットの送信回数と送信バイト数が表示されます。
パケットを送るたびに送信回数と送信バイト数が増えていく事が確認出来ます。

<端末1>
$ trema run ./traffic-monitor.rb -c ./traffic_monitor.conf
Wed Oct 02 00:48:57 +0900 2013
Wed Oct 02 00:49:17 +0900 2013
00:00:00:00:00:01 1 packets (64 bytes)
00:00:00:00:00:02 1 packets (64 bytes)
Wed Oct 02 00:49:37 +0900 2013
00:00:00:00:00:01 2 packets (128 bytes)
00:00:00:00:00:02 1 packets (64 bytes)

次は、以下のコマンドで端末2にフローテーブルの内容を表示します。
1分後にもう一度フローテーブルの内容を表示します。

trema dump_flows 0xabc
(1分後)
trema dump_flows 0xabc

<端末2>
$ trema dump_flows 0xabc
NXST_FLOW reply (xid=0x4):
 cookie=0x2, duration=40.845s, table=0, n_packets=0, n_bytes=0, hard_timeout=60,priority=65535,dl_src=00:00:00:00:00:01,dl_dst=00:00:00:00:00:02 actions=output:1
 cookie=0x1, duration=52.259s, table=0, n_packets=1, n_bytes=64, hard_timeout=60,priority=65535,dl_src=00:00:00:00:00:02,dl_dst=00:00:00:00:00:01 actions=output:2
$ trema dump_flows 0xabc
NXST_FLOW reply (xid=0x4):


1分後にはフローテーブルからフローエントリーが削除された事が確認出来ます。
1つ目のコマンドでフローテーブルが表示されなかった場合は、もう一度ホスト1からホスト2、ホスト2からホスト1へパケットを送ってからすぐにフローテーブルの内容を表示してください。

次は、以下のコマンドでホスト2からホスト1へパケットを送った後、ホスト1からホスト2へパケットを3回送ります。
trema send_packets --source host2 --dest host1
trema send_packets --source host1 --dest host2
(20秒後)
trema send_packets --source host1 --dest host2
trema send_packets --source host1 --dest host2
trema send_packets --source host1 --dest host2
trema dump_flows 0xabc
(1分後)
trema dump_flows 0xabc

<端末2>
$ trema send_packets --source host2 --dest host1
$ trema send_packets --source host1 --dest host2
$
$ trema send_packets --source host1 --dest host2
$ trema send_packets --source host1 --dest host2
$ trema send_packets --source host1 --dest host2
$ trema dump_flows 0xabc
NXST_FLOW reply (xid=0x4):
 cookie=0x1, duration=36.986s, table=0, n_packets=3, n_bytes=192, hard_timeout=60,priority=65535,dl_src=00:00:00:00:00:01,dl_dst=00:00:00:00:00:02 actions=output:1
$
$ trema dump_flows 0xabc
NXST_FLOW reply (xid=0x4):


端末1の表示を見ると、ホスト1からホスト2にパケットを送っても2回目以降はすぐには送信回数と送信バイト数は増えません。
パケットを送ってから60秒するとフローエントリが削除され、そのタイミングで送信回数と送信バイト数が計上されます。

<端末1>
$ trema run ./traffic-monitor.rb -c ./traffic_monitor.conf
Wed Oct 02 01:01:22 +0900 2013
00:00:00:00:00:01 1 packets (64 bytes)
00:00:00:00:00:02 1 packets (64 bytes)
Wed Oct 02 01:01:42 +0900 2013
00:00:00:00:00:01 1 packets (64 bytes)
00:00:00:00:00:02 1 packets (64 bytes)
Wed Oct 02 01:02:02 +0900 2013
00:00:00:00:00:01 1 packets (64 bytes)
00:00:00:00:00:02 1 packets (64 bytes)
Wed Oct 02 01:02:22 +0900 2013
00:00:00:00:00:01 4 packets (256 bytes)
00:00:00:00:00:02 1 packets (64 bytes)
…


以上になります。