2014年1月18日土曜日

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

今回はrouting-tableを見ていきます。
このソースは名前の通りルーティングテーブルとして動作します。

(routing-table.rb)
 1  require "ipaddr"
 2  
 3  class RoutingTable
 4    ADDR_LEN = 32
 5  
 6    def initialize route = []
 7      @db = Array.new( ADDR_LEN + 1 ) { Hash.new }
 8      route.each do | each |
 9        add( each )
10      end
11    end
12  
13    def add options
14      dest = IPAddr.new( options[ :destination ] )
15      masklen = options[ :masklen ]
16      prefix = dest.mask( masklen )
17      @db[ masklen ][ prefix.to_i ] = IPAddr.new( options[ :nexthop ] )
18    end
19  
20    def delete options
21      dest = IPAddr.new( options[ :destination ] )
22      masklen = options[ :masklen ]
23      prefix = dest.mask( masklen )
24      @db[ masklen ].delete( prefix.to_i )
25    end
26  
27    def lookup dest
28      ( 0..ADDR_LEN ).reverse_each do | masklen |
29        prefix = dest.mask( masklen )
30        entry = @db[ masklen ][ prefix.to_i ]
31        return entry if entry
32      end
33      nil
34    end
35  end

1行目
rubyの標準ライブラリのipaddrファイルを読み込み、IPAddrクラスを使えるようにします。

4行目
定数でプレフィックス長の最大値をセットしています。

6~11行目
initializeメソッドでルーティングテーブルに初期値をセットします。
RoutingTableオブジェクトが作成される時に、引数が渡されていなかったら空の配列で初期化されます。

@dbは、33個の配列を作り、空のハッシュを作成します。
引数のroute変数の値をループして、addメソッドで処理します。

13~18行目
addメソッドは引数のハッシュを@db変数に追加します。
引数にはルーティングの情報が入っています。
dest変数には、ルーティングの宛先IPアドレスをIPAddrクラスを作成してセットします。
masklen変数にはルーティングのプレフィックス長をセットします。
prefix変数には、dest変数にセットしたIPアドレスからmaskメソッドでネットワーク部のアドレスを取得してセットします。
@db変数の配列のmasklen変数の値の要素に、宛先IPアドレスのネットワーク部をハッシュキーにし、引数のoptions変数からnexthopの値を取得してセットします。

20~25行目
deleteメソッドは@adbから引数のハッシュに該当する要素を削除します。
addメソッド同様に、引数にはルーティングの情報が入っています。
dest変数には、ルーティングの宛先IPアドレスからIPAddrクラスを作成してセットします。
masklen変数にはルーティングのプレフィックス長をセットします。
prefix変数には、dest変数にセットしたIPアドレスからmaskメソッドでネットワーク部のアドレスを取得してセットします。
@db変数の配列のmasklen変数の値の要素から、宛先IPアドレスのネットワーク部のハッシュキーと一致するハッシュ要素を削除します。

27~34行目
lookupメソッドは、引数のIPアドレスに合致するルーティングエントリからネクストホップを返します。
reverse_eachでADDR_LEN変数の値から0に減らしながらループを回します。
数字の大きい方から比較することで、ルーティングのロンゲストマッチをしています。

prefix変数には、引数のIPアドレスからmaskメソッドでネットワークアドレスを取得しセットします。
entry変数には、@db変数からprefix変数をハッシュキーにして値を取得しセットします。
31行目は、entry変数に値が取得できていればentryの値であるネクストホップを返します。
31行目のifの結果、returnでメソッドから抜けない場合、メソッドは33行目のnilを返します。

今回は以上になります。

2014年1月17日金曜日

SDN Conference 2014

こんにちは、鯵王です。

SDN Conference 2014が東京と大阪で開催されます。
東京は2月18日(火)、大阪は2月21日(金)です。

1月16日からセミナーの事前登録が開始されました。
自分は早速、2月18日の東京に登録しましたよ。

SDNに関する話がいろいろ聞けそうです。
詳しくは以下のページをご確認ください。

http://www.f2ff.jp/sdnc/2014/

2014年1月16日木曜日

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

今回はinterfaceを見ていきいます。
このソースはsimple_routerのインターフェイス情報を管理するプログラムです。

ソースは今まで見たものがわかれば理解できるレベルと思います。

(interface.rb)
 1  require "arp-table"
 2  require "routing-table"
 3  
 4  class Interface
 5    attr_reader :hwaddr
 6    attr_reader :ipaddr
 7    attr_reader :masklen
 8    attr_reader :port
 9  
10    def initialize options
11      @port = options[ :port ]
12      @hwaddr = Mac.new( options[ :hwaddr ] )
13      @ipaddr = IPAddr.new( options[ :ipaddr ] )
14      @masklen = options[ :masklen ]
15    end
16  
17    def has? mac
18      mac == hwaddr
19    end
20  end
21  
22  class Interfaces
23    def initialize interfaces = []
24      @list = []
25      interfaces.each do | each |
26        @list << Interface.new( each )
27      end
28    end
29  
30    def find_by_port port
31      @list.find do | each |
32        each.port == port
33      end
34    end
35  
36    def find_by_ipaddr ipaddr
37      @list.find do | each |
38        each.ipaddr == ipaddr
39      end
40    end
41  
42    def find_by_prefix ipaddr
43      @list.find do | each |
44        masklen = each.masklen
45        each.ipaddr.mask( masklen ) == ipaddr.mask( masklen )
46      end
47    end
48  
49    def find_by_port_and_ipaddr port, ipaddr
50      @list.find do | each |
51        each.port == port and each.ipaddr == ipaddr
52      end
53    end
54  
55    def ours? port, macda
56      return true if macda.broadcast?
57  
58      interface = find_by_port( port )
59      if not interface.nil? and interface.has?( macda )
60        return true
61      end
62    end
63  end

このソースにはInterfaceクラスとInterfacesクラスが書かれています。
まずはInterfaceクラスです。

1~2行目
Interfaceクラスに入る前に、先頭でarp-table.rbとrouting-table.rbを読み込んでいます。

5~8行目
アクセサの宣言をしています。
これで、クラスの外からインスタンス変数にアクセスすることができます。

10~15行目
initializeはInterfaceオブジェクトが作成される時に実行されます。
ここでは、オブジェクトを作成する時に指定した引数をインスタンス変数に代入しています。

Interfaceオブジェクトを作成する時はハッシュが引数に渡されるので、ハッシュキーを指定してハッシュ値を変数に格納しています。
@portにはルータの物理ポート番号が代入されます。
@hwaddrにはマックアドレスが代入されます。
@ipaddrにはIPアドレスが代入されます。
@masklenにはプレフィックス長が格納されます。

17~19行目
has?メソッドは引数とhwaddrが一致するかを評価し、一致したらtrueを返します。

次はInterfacesクラスです。

23~28行目
initializeはクラスのオブジェクトが作成される時に実行されるメソッドです。
オブジェクトを作成する時の引数は配列で、引数がない場合は空の配列になります。
@list変数を空の配列で初期化します。
引数の配列の要素を@list変数に追加します。

30~34行目
find_by_portメソッドは@list変数から引数で指定したポート番号を検索し、物理ポート番号が一致したInterfaceオブジェクトを返します。

36~40行目
find_by_ipaddrメソッドは@list変数から引数で指定したIPアドレスを検索し、IPアドレスが一致したInterfaceオブジェクトを返します。

42~47行目
find_by_prefixメソッドは@list変数から引数で指定したIPアドレスをマスクした値で検索し、一致したInterfaceオブジェクトを返します。
この時、@list変数の要素のIPアドレスもマスクして比較します。

49~53行目
find_by_port_and_ipaddrメソッドは@list変数から引数で指定した物理ポート番号とIPアドレスを検索し、両方が一致したInterfaceオブジェクトを返します。

55~62行目
ours?メソッドは引数のポート番号とmacアドレスがインターフェイス情報として存在するかを判定するメソッドです。
宛先のmacアドレスがブロードキャストだった場合はtrueを返します。

ブロードキャストでない場合は、find_by_portメソッドで引数のポート番号を元にInterfaceオブジェクトを取得します。
Interfaceオブジェクトが取得できて、かつ、そのmacアドレスが一致した場合はtrueを返します。

今回は以上になります。

2014年1月15日水曜日

rubyのクラスのソースファイルについて

simple_routerのinterface.rbで使われていたMacクラスはどういったクラスなのか調べるため、ソースファイルを探し方を調べてみました。
ただ残念ながら、ソースファイルを探す方法を見つけることができませんでした。

今回は探し方を調べている間に見つけたメソッドや変数が、どこかで役に立つかもしれないのでメモしておきます。
説明は、ほぼリファレンスのコピーです。

■superclassメソッド
親クラスを調べるメソッドです。
戻り値はクラス(Classオブジェクト)です。

親クラスがわかってもどのファイルに書いてあるかはわかりませぬ。

■__FILE__変数
現在のソースファイル名です。
フルパスとは限らないため、フルパスが必要な場合は File.expand_path(__FILE__) とする必要があります。

実行しているファイル名は調べられそうですが、クラスがどのファイルに書かれているかはわからなそうです。

■$0、$PROGRAM_NAME組み込み変数
現在実行中の Ruby スクリプトの名前を表す文字列です。

では試しに__FILE__と$PROGRAM_NAMEを使ってみます。

(Test1.rb)
puts __FILE__
puts $PROGRAM_NAME

$ ruby Test1.rb
Test1.rb
Test1.rb

実行したファイル名が表示されました。

■$:、$LOAD_PATH組み込み変数
rubyがロードするファイルのディレクトリやロードしたファイルを調べる方法です。
これもMacクラスがどのファイルに書かれているかはわかりません。

Kernel.#load や Kernel.#require がファイルをロードする時に検索するディレクトリのリストを含む配列です

以下は、ロードパスをコマンドラインから調べる方法です。

$ ruby -e 'puts $:'

実行してみると、このようにパスが表示されます。
$ ruby -e 'puts $:'
/usr/local/lib/site_ruby/1.8
/usr/local/lib/site_ruby/1.8/i686-linux
/usr/local/lib/site_ruby/1.8/i486-linux
/usr/local/lib/site_ruby
/usr/lib/ruby/vendor_ruby/1.8
/usr/lib/ruby/vendor_ruby/1.8/i686-linux
/usr/lib/ruby/vendor_ruby
/usr/lib/ruby/1.8
/usr/lib/ruby/1.8/i686-linux
/usr/lib/ruby/1.8/i486-linux
.

■$"、$LOADED_FEATURES組み込み変数
Kernel.#require でロードされたファイル名を含む配列です。
Kernel.#require で同じファイルを 複数回ロードしないようにするためのロックとして使われます。

実行してみます。

(Test2.rb)
puts $LOAD_PATH
puts "--------------------"
puts $LOADED_FEATURES

$ ruby Test2.rb
/usr/local/lib/site_ruby/1.8
/usr/local/lib/site_ruby/1.8/i686-linux
/usr/local/lib/site_ruby/1.8/i486-linux
/usr/local/lib/site_ruby
/usr/lib/ruby/vendor_ruby/1.8
/usr/lib/ruby/vendor_ruby/1.8/i686-linux
/usr/lib/ruby/vendor_ruby
/usr/lib/ruby/1.8
/usr/lib/ruby/1.8/i686-linux
/usr/lib/ruby/1.8/i486-linux
.
--------------------
enumerator.so


どうにも調べ方がわからないので、tremaのディレクトリ内を検索して見つけたのが「./ruby/trema/mac.rb」でした。
これですかね?

tremaがこのファイルを読み込んでいるのか確認してみます。

(Test3.rb)
class Test < Controller

  def start
    puts $LOAD_PATH
    puts "--------------------"
    puts $LOADED_FEATURES
  end

end


$LOADED_FEATURESの結果が長いので、一部省略します。
$ trema run Test3.rb
/var/lib/gems/1.8/gems/trema-0.3.20/vendor/ruby-ifconfig-1.2/lib
/var/lib/gems/1.8/gems/trema-0.3.20/ruby
/var/lib/gems/1.8/gems/bundler-1.3.5/lib
/var/lib/gems/1.8/gems/gli-2.6.2/lib
/usr/local/lib/site_ruby/1.8
/usr/local/lib/site_ruby/1.8/i686-linux
/usr/local/lib/site_ruby/1.8/i486-linux
/usr/local/lib/site_ruby
/usr/lib/ruby/vendor_ruby/1.8
/usr/lib/ruby/vendor_ruby/1.8/i686-linux
/usr/lib/ruby/vendor_ruby
/usr/lib/ruby/1.8
/usr/lib/ruby/1.8/i686-linux
/usr/lib/ruby/1.8/i486-linux
.
.
--------------------
enumerator.so
rubygems/defaults.rb
rbconfig.rb
rubygems/deprecate.rb
rubygems/exceptions.rb
<略>
trema/monkey-patch/integer.rb
trema/enqueue.rb
trema/send-out-port.rb
forwardable.rb
trema/mac.rb
trema/set-eth-addr.rb
trema/set-eth-dst-addr.rb
trema/set-eth-src-addr.rb
trema/set-ip-addr.rb
<略>

trema/mac.rbというのを読み込んでいるようです。
あくまで、参考まで。

2014年1月8日水曜日

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

今回はarp-table.rbを見ていきいます。
ソースの内容は前にlearning_switchのところで見たfdb.rbとよく似ています。

(arp-table.rb)
 1  class ARPEntry
 2    include Trema::DefaultLogger
 3  
 4    attr_reader :port
 5    attr_reader :hwaddr
 6    attr_writer :age_max
 7  
 8    def initialize port, hwaddr, age_max
 9      @port = port
10      @hwaddr = hwaddr
11      @age_max = age_max
12      @last_updated = Time.now
13      info "New entry: MAC addr = #{ @hwaddr.to_s }, port = #{ @port }"
14    end
15  
16    def update port, hwaddr
17      @port = port
18      @hwaddr = hwaddr
19      @last_updated = Time.now
20      info "Update entry: MAC addr = #{ @hwaddr.to_s }, port = #{ @port }"
21    end
22  
23    def aged_out?
24      aged_out = Time.now - @last_updated > @age_max
25      info "Age out: An ARP entry (MAC address = #{ @hwaddr.to_s }, port number = #{ @port }) has been aged-out" if aged_out
26      aged_out
27    end
28  end
29  
30  class ARPTable
31    DEFAULT_AGE_MAX = 300
32  
33    def initialize
34      @db = {}
35    end
36  
37    def update port, ipaddr, hwaddr
38      entry = @db[ ipaddr.to_i ]
39      if entry
40        entry.update( port, hwaddr )
41      else
42        new_entry = ARPEntry.new( port, hwaddr, DEFAULT_AGE_MAX )
43        @db[ ipaddr.to_i ] = new_entry
44      end
45    end
46  
47    def lookup ipaddr
48      @db[ ipaddr.to_i ]
49    end
50  
51    def age
52      @db.delete_if do | ipaddr, entry |
53        entry.aged_out?
54      end
55    end
56  end

このソースプログラムにはARPEntryクラスとARPTableクラスが書かれています。
ARPEntryクラスはARPTableクラスが使っているクラスなので、まずARPTableクラスから見ていきます。

31行目
DEFAULT_AGE_MAXは定数です。
ARPEntryクラスに渡され、arp情報の有効期限として指定されます。

33~35行目
initializeはクラスのオブジェクトが作成される時に実行されるメソッドです。
処理の内容は@db変数に空のハッシュを定義しています。
後の処理で@db変数のハッシュ値にARPEntryオブジェクトが代入されます。

37~45行目
updateメソッドは@db変数にハッシュ要素を追加、もしくはハッシュ値を更新するメソッドです。
まず、引数のIPアドレスをハッシュキーにして@db変数からハッシュ値を取得し、entry変数にセットしています。

ハッシュ値が取得できた場合は、ARPEntryクラスのupdateメソッドを実行して変数の値を更新します。
ハッシュ値が取得できなかった場合は、引数の物理ポート番号、macアドレス、DEFAULT_AGE_MAX定数を元に新たにARPEntryオブジェクトを作り、IPアドレスをハッシュキーにして@db変数にハッシュ要素を追加しています。

47~49行目
lookupメソッドは引数のIPアドレスに紐づくハッシュ値であるARPEntryのオブジェクトを返すメソッドです。

51~55行目
ageメソッドは@db変数から一定時間が経過したハッシュ要素を削除します。
ここでの一定時間は、定数「DEFAULT_AGE_MAX」の値で300秒になります。

ハッシュ値の削除はdelete_ifメソッドで行っています。
delete_ifは指定した条件にマッチした場合、ハッシュから要素を削除するメソッドです。
ここでの条件はARPEntryクラスのage_out?メソッドの結果です。

ipaddr変数にはハッシュキー、entry変数にはハッシュ値が入り、@dbの全ての要素に対して条件にマッチするか評価をします。

続いてARPEntryクラスを見ていきます。

2行目
includeでモジュールを読み込みます。
DefaultLoggerモジュールが使えるようになります。

4~6行目
アクセサの宣言をしています。
これで、クラスの外からインスタンス変数にアクセスすることができます。

8~14行目
initializeはARPEntryオブジェクトが作成される時に実行されます。
ここではオブジェクトを作成するときに指定した引数をインスタンス変数に代入しています。

@last_update変数にはRuby組み込みメソッドのTimeメソッドを使い現在時刻を代入しています。 infoメソッドでmacアドレスと物理ポート番号をログに出力します。

16~21行目
updateメソッドは、引数を元にインスタンス変数の値を更新します。
@port_no変数と@hwaddr変数の値は引数の物理ポート番号とmacアドレスの値に更新し、@last_update変数の値は現在時刻に書き換えます。
infoでmacアドレスと物理ポート番号をログに出力します。

23~27行目
aged_out?メソッドは、ARPEntryオブジェクトを作成、もしくはupdateメソッドを実行してから所定の時間が経過しているか否かを返します。
現在時刻から@last_update変数を引いた値が、FDBクラスの「DEFAULT_AGE_MAX」定数である300より大きい場合はaged_out変数にはtrueがセットされます。

25行目は、末尾に書かれたifの式がTrueの場合、左側の式が実行されます。
ここでは、age_out変数の値がTrueの場合は、infoメソッドでmacアドレスと物理ポート番号をログに出力します。
26行目は、24行目の式の結果(trueかfalse)をaged_out?メソッドの戻り値にしています。

今回は以上です。

2014年1月7日火曜日

補数とは何だ!?

simple-routerのrouter_util.rbのソースに関係があったので、今回は補数について調べました。

補数とは、ある数字に足すと桁が一つ増える値の最小値の事です。
10進数の72の場合、桁が一つ増えた値は100になります。
72+28=100なので、72の「10の補数」は28です

2進数の1001の場合、桁が一つ増えた値は10000になります。
1001+0111=10000なので、1001の「2の補数」は0111です
2の補数の求め方は、元の値の「0」と「1」を反転して「1」を足します。

1001
 ↓
0110
これに+1をする
0111

2の補数の他に「1の補数」というのがあって、こちらは最後に「1」を足しません。
元の値の「0」と「1」を反転するだけです。

元の値に1の補数を足すと1埋めの数になります。
2進数1001の「1の補数」は以下のようにして求めます。

1001
 ↓
0110

元の値と1の補数を足すと1111になります。

1001+0110=1111

rubyの「~」で「1の補数」を求められます。
以上です。

2014年1月6日月曜日

新年のごあいさつ

明けましておめでとうございます
鯵王です

今日から仕事初めです。
年の初めではありますが、この先1年でSDNがどうなるのか予想ができませんね。
新しい機器も出て来るでしょうし、仮想化もますます進んでいく事でしょう。

SDNに限らず、情報収集して勉強していきたいと思います。

今年もよろしくお願いします。