2013年12月26日木曜日

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

今回はSimple-routerのソースを見ていきます。
simple-routerを元にプログラムを作れるようにしっかり理解していきたいところです。

本体のソースを見る前に、Simple-routerが初めに読み込む4つのプログラムの役割をざっくりと説明します。

arp-table.rb
arp情報を管理するクラスです
その名の通りIPアドレスからmacアドレスを調べるのに使います。
ソースは以前使ったFDBクラスとよく似ています

interface.rb
simple-routerのインターフェイス情報を管理するために使用するクラスです
macアドレスやIPアドレス、サブネットマスク、物理ポート番号を管理しています。

routing-table.rb
ルーティングテーブルの情報を保持するクラスです
ルーティングの追加などが行えます。

router-utils.rb
ICMPパケットやarpリクエストのパケットを作るといったルータの機能を実現するためのクラスが用意されています

ではソースの方を見ていきましょう。
  1  require "arp-table"
  2  require "interface"
  3  require "router-utils"
  4  require "routing-table"
  5  
  6  class SimpleRouter < Controller
  7    include RouterUtils
  8  
  9    def start
 10      load "simple_router.conf"
 11      @interfaces = Interfaces.new( $interface )
 12      @arp_table = ARPTable.new
 13      @routing_table = RoutingTable.new( $route )
 14    end
 15  
 16    def packet_in( dpid, message )
 17      return if not to_me?( message )
 18  
 19      if message.arp_request?
 20        handle_arp_request dpid, message
 21      elsif message.arp_reply?
 22        handle_arp_reply message
 23      elsif message.ipv4?
 24        handle_ipv4 dpid, message
 25      else
 26        # noop.
 27      end
 28    end
 29  
 30    private
 31  
 32    def to_me?( message )
 33      return true if message.macda.broadcast?
 34  
 35      interface = @interfaces.find_by_port( message.in_port )
 36      if interface and interface.has?( message.macda )
 37        return true
 38      end
 39    end
 40  
 41    def handle_arp_request( dpid, message )
 42      port = message.in_port
 43      daddr = message.arp_tpa
 44      interface = @interfaces.find_by_port_and_ipaddr( port, daddr )
 45      if interface
 46        arp_reply = create_arp_reply_from( message, interface.hwaddr )
 47        packet_out dpid, arp_reply, SendOutPort.new( interface.port )
 48      end
 49    end
 50  
 51    def handle_arp_reply( message )
 52      @arp_table.update message.in_port, message.arp_spa, message.arp_sha
 53    end
 54  
 55    def handle_ipv4( dpid, message )
 56      if should_forward?( message )
 57        forward dpid, message
 58      elsif message.icmpv4_echo_request?
 59        handle_icmpv4_echo_request dpid, message
 60      else
 61        # noop.
 62      end
 63    end
 64  
 65    def should_forward?( message )
 66      not @interfaces.find_by_ipaddr( message.ipv4_daddr )
 67    end
 68  
 69    def handle_icmpv4_echo_request( dpid, message )
 70      interface = @interfaces.find_by_port( message.in_port )
 71      saddr = message.ipv4_saddr.value
 72      arp_entry = @arp_table.lookup( saddr )
 73      if arp_entry
 74        icmpv4_reply = create_icmpv4_reply( arp_entry, interface, message )
 75        packet_out dpid, icmpv4_reply, SendOutPort.new( interface.port )
 76      else
 77        handle_unresolved_packet dpid, message, interface, saddr
 78      end
 79    end
 80  
 81    def forward( dpid, message )
 82      next_hop = resolve_next_hop( message.ipv4_daddr )
 83  
 84      interface = @interfaces.find_by_prefix( next_hop )
 85      if not interface or interface.port == message.in_port
 86        return
 87      end
 88  
 89      arp_entry = @arp_table.lookup( next_hop )
 90      if arp_entry
 91        macsa = interface.hwaddr
 92        macda = arp_entry.hwaddr
 93        action = create_action_from( macsa, macda, interface.port )
 94        flow_mod dpid, message, action
 95        packet_out dpid, message.data, action
 96      else
 97        handle_unresolved_packet dpid, message, interface, next_hop
 98      end
 99    end
100  
101    def resolve_next_hop( daddr )
102      interface = @interfaces.find_by_prefix( daddr.value )
103      if interface
104        daddr.value
105      else
106        @routing_table.lookup( daddr.value )
107      end
108    end
109  
110    def flow_mod( dpid, message, action )
111      send_flow_mod_add(
112        dpid,
113        :match => ExactMatch.from( message ),
114        :actions => action
115      )
116    end
117  
118    def packet_out( dpid, packet, action )
119      send_packet_out(
120        dpid,
121        :data => packet,
122        :actions => action
123      )
124    end
125  
126    def handle_unresolved_packet( dpid, message, interface, ipaddr )
127      arp_request = create_arp_request_from( interface, ipaddr )
128      packet_out dpid, arp_request, SendOutPort.new( interface.port )
129    end
130  
131    def create_action_from( macsa, macda, port )
132      [
133        SetEthSrcAddr.new( macsa ),
134        SetEthDstAddr.new( macda ),
135        SendOutPort.new( port )
136      ]
137    end
138  end

では最初から見ていきます。

1~4行目
最初に説明した4つのソースプログラムを読み込み、使用できるようにしています。

7行目
simple-routerのプログラムからRouterUtils.rbに定義されたモジュールを使えるようにしています。

9~10行目
startメソッドは起動時に実行されます。
同じディレクトリにあるsimple_router.confファイルを読み込みます。
このファイルには$interface変数と$route変数が定義されます。

$interface変数にはsimple-routerのインターフェイス設定、$route変数にはsimple-routerで使うルーティングの初期値が書かれています。
先頭に$がついているのでグローバル変数になります。

11~13行目
クラスのオブジェクトを作成しています。
@interfaces変数はsimple-routerのインターフェイスの情報を保持します。
@arp_table変数にはsimple-routerのarp情報を格納します。
@routing_table変数はsimple-routerのルーティングテーブルとして使用します。

16~17行目
packet_inイベントハンドラはsimple-routerがパケットを受信すると実行されます。
to_me?メソッドは、受け取ったパケットがブロードキャストかsimple-router宛てのパケットの場合はtrueを返します。
ただし、ここではifの条件にnotがついているため、パケットがブロードキャストでもsimple-router宛てでもない場合はpacket_inメソッドから抜けます。

19~28行目
受け取ったパケットの種類により処理を行います。

arp_request?メソッドは、パケットがarpリクエストの場合にtrueを返します。
メソッドの結果がtrueの場合、handle_arp_requestメソッド実行します。
handle_arp_requestメソッドはarpリクエストに対しarpリプライを返します。

arp_reply?メソッドは、パケットがarpリプライの場合にtrueを返します。
メソッドの結果がtrueの場合、handle_arp_replyメソッドを実行します。
handle_arp_replyメソッドは受け取ったarpパケットを元にsimple-routerのarp情報を更新します。

ipv4?メソッドは、パケットがipv4パケットの場合にtrueを返します。
メソッドの実行結果がtrueの場合、handle_ipv4メソッドを実行します。
handle_ipv4メソッドはパケットの転送やicmpのリプライなどを行います。

これらにあてはまらないパケットの場合は何もしません。

32~39行目
to_me?メソッドはパケットの宛先がsimple-router宛てかを判定し、trueかfalseを返します。

パケットがブロードキャストの場合はtrueを返します。
ブロードキャストではない場合、Interfacesクラスのfind_by_portメソッドを実行し、パケットを受け取ったインターフェイス情報をinterface変数にセットします。
次に、そのインターフェイスのmacアドレスとパケットの宛先macアドレスが一致した場合はto_me?メソッドはtrueを返します。

41~49行目
handle_arp_requestメソッドはarpリプライを返すメソッドです。

port変数にはパケットを受け取った物理ポート番号をセットします。
addr変数にはパケットの宛先のIPアドレスをセットします。
interface変数にはパケットを受け取った物理ポート番号とIPアドレスが一致するインターフェイス情報をセットします。
interface変数にインターフェイス情報がセットされた場合、以下の処理でarpリプライを返します。

create_arp_reply_fromはarpリプライパケットを作るメソッドです。
引数にはarpリプライで返すハードウェアアドレスがセットされています。
packet_outメソッドでarpリプライを送ります

51~53行目
handle_arp_replyメソッドは、arp情報を更新するメソッドです。

受け取ったarpパケットからARPTableクラスのupdateメソッドを実行してarp情報を更新します。

55~63行目
handle_ipv4メソッドはパケットの転送処理やicmpパケットの返信処理を行います。

should_forward?メソッドは、パケットの宛先IPアドレスがsimple-routerのインターフェイスに振られているIPアドレスかを調べます。
simple-routerのインターフェイスにそのIPアドレスが無い場合はtrueになり、forwardメソッドを実行してパケットの転送を行います。
パケットの宛先IPアドレスがsimple-router宛ての場合はfalseで、かつパケットがicmpパケットだった場合は、handle_icmpv4_echo_requestメソッドを実行してicmpに返信を返します。

それ以外の場合は何もしません。

65~67行目
should_forward?メソッドはパケットの宛先IPアドレスがsimple-routerのインターフェイスに振られれているIPアドレスかを調べるメソッドです。
パケットを転送するか判断するのに使用します。

Interfaceクラスのfind_by_ipaddrメソッドを実行し、simple-routerにIPアドレスが一致するインターフェイスがある場合はfalseを返します。

69~79行目
handle_icmpv4_echo_requestメソッドはicmpパケットに返信を返すメソッドです。

interface変数には、Interfacesクラスのfind_by_portメソッドを実行し、パケットを受け取った物理ポートのインターフェイス情報をセットします。
saddr変数には、パケット送信元のIPアドレスをセットします。
arp_entry変数には、ARPTableクラスのlookupメソッド実行し、送信元のIPアドレスを元に@arp_table変数からarp情報を取得しセットします。

arp_entry変数にarp情報がセットされた場合、create_icmpv4_replyメソッドを実行して返信するパケットを作成し、packe_outメソッドで送信元に返します。

@arp_table変数からarp情報が取得できなかった場合は、handle_unresolved_packetメソッドを実行してarp情報を取得します。

81~87行目
forwardメソッドはパケットの転送処理を行います。

next_hop変数には、resolve_next_hopメソッドでパケットの次の転送先IPアドレスを取得してセットします。
interface変数には、Interfacesクラスのfind_by_prefixメソッドを実行しsimple-routerの出力先ポートをセットします。
出力先インターフェイスが見つからないか、出力先ポートがパケットを受け取ったポートだった場合はforwardメソッドを終了します。

89~99行目
arp_entry変数には、next_hop変数を元にlookupメソッドでarp情報を取得しセットします。
arp情報が取得できた場合、macsa変数にはsimple-routerの出力インターフェイスのmacアドレスをセットします。
macda変数にはarp情報から取得したパケットの送信先のmacアドレスをセットします。
action変数には、create_action_fromメソッドでパケットを送る時のaction(処理)を作成します。
flow_modメソッドでフローエントリを追加し、packet_outメソッドでパケットを送信します。

arp情報が取得できなかった場合は、handle_unresolved_packetメソッドでnext_hopに指定されているIPアドレスに対してarpリクエストを送ります。

101~108行目
resolve_next_hopメソッドはパケットの転送先のIPアドレスを取得します。

まず、送信先IPアドレスを元にSimple-routerのインターフェイスの中からIPアドレスのネットワーク部分が一致するインターフェイスを探します。
simple-routerに送信先IPアドレスのネットワークと一致するインターフェイスがある場合、メソッドは送信先IPアドレスを返します。
一致するIPアドレスがな無い場合はルーティングテーブルから次の転送先を取得します。
RoutingTableクラスのlookupメソッドでパケットの次の転送先のIPアドレスを取得します。

110~116行目
flow_mod_メソッドはフローテーブルにフロールールを追加するメソッドです。
引数のdpid、message、actionを元にフローエントリを追加します。

118~124行目
packe_outメソッドはパケットを送信するメソッドです。
引数のdpid、packet、actionを元にパケットを送信します。

126~129行目
handle_unresolved_packetメソッドはarpリクエストを送るメソッドです。
arp_request変数にcreate_arp_request_fromメソッドでarpパケットを作ります。
packet_outメソッドでarpリクエストを送ります。

131~137行目
create_action_fromメソッドはflowエントリを追加する時や、パケットを送る時のactionを作成します。

長かったですね。
今度はsimple-routerで使われている他のソースも見て行きたいと思います。

以上です。

0 件のコメント:

コメントを投稿