simple-routerを元にプログラムを作れるようにしっかり理解していきたいところです。
本体のソースを見る前に、Simple-routerが初めに読み込む4つのプログラムの役割をざっくりと説明します。
arp-table.rb
arp情報を管理するクラスです
その名の通りIPアドレスからmacアドレスを調べるのに使います。
ソースは以前使ったFDBクラスとよく似ています
その名の通りIPアドレスからmacアドレスを調べるのに使います。
ソースは以前使ったFDBクラスとよく似ています
interface.rb
simple-routerのインターフェイス情報を管理するために使用するクラスです
macアドレスやIPアドレス、サブネットマスク、物理ポート番号を管理しています。
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行目同じディレクトリにあるsimple_router.confファイルを読み込みます。
このファイルには$interface変数と$route変数が定義されます。
$interface変数にはsimple-routerのインターフェイス設定、$route変数にはsimple-routerで使うルーティングの初期値が書かれています。
先頭に$がついているのでグローバル変数になります。
クラスのオブジェクトを作成しています。
@interfaces変数はsimple-routerのインターフェイスの情報を保持します。
@arp_table変数にはsimple-routerのarp情報を格納します。
@routing_table変数はsimple-routerのルーティングテーブルとして使用します。
16~17行目@interfaces変数はsimple-routerのインターフェイスの情報を保持します。
@arp_table変数にはsimple-routerのarp情報を格納します。
@routing_table変数はsimple-routerのルーティングテーブルとして使用します。
packet_inイベントハンドラはsimple-routerがパケットを受信すると実行されます。
to_me?メソッドは、受け取ったパケットがブロードキャストかsimple-router宛てのパケットの場合はtrueを返します。
ただし、ここではifの条件にnotがついているため、パケットがブロードキャストでもsimple-router宛てでもない場合はpacket_inメソッドから抜けます。
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行目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のリプライなどを行います。
これらにあてはまらないパケットの場合は何もしません。
to_me?メソッドはパケットの宛先がsimple-router宛てかを判定し、trueかfalseを返します。
パケットがブロードキャストの場合はtrueを返します。
ブロードキャストではない場合、Interfacesクラスのfind_by_portメソッドを実行し、パケットを受け取ったインターフェイス情報をinterface変数にセットします。
次に、そのインターフェイスのmacアドレスとパケットの宛先macアドレスが一致した場合はto_me?メソッドはtrueを返します。
パケットがブロードキャストの場合は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リプライを送ります
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情報を更新します。
受け取った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に返信を返します。
それ以外の場合は何もしません。
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を返します。
パケットを転送するか判断するのに使用します。
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情報を取得します。
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メソッドを終了します。
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リクエストを送ります。
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アドレスを取得します。
まず、送信先IPアドレスを元にSimple-routerのインターフェイスの中からIPアドレスのネットワーク部分が一致するインターフェイスを探します。
simple-routerに送信先IPアドレスのネットワークと一致するインターフェイスがある場合、メソッドは送信先IPアドレスを返します。
一致するIPアドレスがな無い場合はルーティングテーブルから次の転送先を取得します。
RoutingTableクラスのlookupメソッドでパケットの次の転送先のIPアドレスを取得します。
110~116行目
flow_mod_メソッドはフローテーブルにフロールールを追加するメソッドです。
引数のdpid、message、actionを元にフローエントリを追加します。
引数のdpid、message、actionを元にフローエントリを追加します。
118~124行目
packe_outメソッドはパケットを送信するメソッドです。
引数のdpid、packet、actionを元にパケットを送信します。
引数のdpid、packet、actionを元にパケットを送信します。
126~129行目
handle_unresolved_packetメソッドはarpリクエストを送るメソッドです。
arp_request変数にcreate_arp_request_fromメソッドでarpパケットを作ります。
packet_outメソッドでarpリクエストを送ります。
arp_request変数にcreate_arp_request_fromメソッドでarpパケットを作ります。
packet_outメソッドでarpリクエストを送ります。
131~137行目
create_action_fromメソッドはflowエントリを追加する時や、パケットを送る時のactionを作成します。
長かったですね。
今度はsimple-routerで使われている他のソースも見て行きたいと思います。
以上です。
0 件のコメント:
コメントを投稿