Skip to content

Latest commit

 

History

History
260 lines (179 loc) · 21.1 KB

topology.adoc

File metadata and controls

260 lines (179 loc) · 21.1 KB

ネットワークトポロジを検出する

大規模なネットワークを構築する前準備として、ネットワークトポロジを OpenFlow で検出してみましょう

美しい大規模ネットワーク

筆者はネットワーク研究者という仕事柄、よくさまざまなネットワークを目にします。その中でいつも「すごい!」とうならされるのが、ネットワークエンジニアの憧れ、ShowNet です。ShowNet はネットワーク系最大の展示会 Interop Tokyo の期間中だけ運用されるネットワークで、最新ネットワーク技術のいわばショーケースと言えます。普段は触れることのできない、ネットワーク界の F1 マシンとも言える最新機器を集めたライブデモンストレーションは圧巻の一言です。

ShowNet の魅力をもっともよく伝えてくれるのが、Interop Tokyo で毎年公開される ShowNet のトポロジ図です (図 15-1)。注目すべきは、ShowNet の複雑な情報をたった一枚の図に収めているところです。「この部分は、いったいどんなプロトコルで動いているんだろう?」「実際の詳しいトポロジはどうなっているのかな?」こうした気になる部分が、すべて一枚の図にきれいに収まっています。ネットワークが好きな人であれば、気がつくと何時間でも眺めてしまうほどの魅力を持つトポロジ図なのです。

shownet topology
図 15-1: 2015 年 Interop Tokyo の ShowNet トポロジ図。引用元: http://www.interop.jp/2015/shownet/images/topology02.pdf Copyright © Interop Tokyo 2015 ShowNet NOC Team Member and NANO OPT Media, Inc. All Rights Reserved.

ShowNet のようにいくつものスイッチやルータがつながるネットワークの動作では、トポロジ情報の把握が1つの鍵です。パケットが迷子になったりループしたりせずに正しく目的地まで届くためには、スイッチやルータ同士がどのような接続関係にあるかをお互いに把握しなければなりません。

OpenFlow では、コントローラがこのトポロジ情報を管理します。ネットワーク全体を集中管理するコントローラがトポロジを把握することで、パケットを思いのままに転送できます。たとえば、パケットの転送に、最短パスを使うだけではなく、回り道をさせたり、複数のパス (マルチパス) を使うことも自由自在です。

トポロジ検出の仕組み

コントローラがトポロジ情報を検出するには、スイッチ間のリンクをすべて発見する必要があります。ネットワーク中のスイッチとポート情報は、switch_ready ハンドラや Features Request/Reply メッセージを使えばすべて発見できます。したがって、発見したスイッチ間のリンクがすべて発見できれば、ネットワークトポロジを検出できます。

リンクの発見

OpenFlow でリンクを発見する方法として代表的なのは、Link Layer Discovery Protocol (LLDP) パケットを使った方法です (図 15-2)。コントローラはどこにリンクがあるかあたりをつけるために、適当なスイッチ A に LLDP パケットを試しに送ります。もし、スイッチ Aに別のスイッチ B がリンクでつながっていれば、LLDPはそこのリンクを通りスイッチ Bを経由してブーメランのようにコントローラへと戻ってきます。このように LLDP パケットが無事に戻ってくれば、スイッチ A と B はリンクでつながっているとわかります。また、LLDP パケットには通過したリンクの詳しい情報が書き込まれるので、スイッチ A と B がどのポート番号で接続しているかということまでわかります。これを繰り返していけば、最終的にはすべてのリンクを発見できるわけです。

lldp overview
図 15-2: LLDP を使ってリンクを発見する

「なぜ、LLDP パケットはきちんとリンクを通ってコントローラまで戻ってくるんだろう?スイッチに LLDP 固有のしかけが必要なのかな?」こう思った方もいるかもしれません。実は、LLDPによるリンクは今まで学んできた OpenFlow の仕組みだけを使って実現できます。つまり、OpenFlow に対応したスイッチであれば LLDPでリンクを発見できるのです。

LLDP によるリンク発見を OpenFlow で実現する方法を見ていきましょう。図 15-3 のように、スイッチ 0x1 のポート 5 とスイッチ 0x2 のポート 1 が接続されていたとします。このリンクを発見するために、コントローラは次の動作をします。

lldp openflow
図 15-3: LLDP パケットと OpenFlow の仕組みを使ってリンクを発見する
  1. コントローラは、接続関係を調べたいスイッチの Datapath ID 0x1 とポート番号 5 を埋め込んだ Link Layer Discovery Protocol (LLDP) パケットを作る

  2. ポート 5 から出力するというアクションを含む Packet Out メッセージを作り、先ほど作った LLDPパケットをスイッチ 0x1 へと送る

  3. Packet Out を受け取ったスイッチはアクションに従い、LLDPパケットを指定されたポート 5 から出力する。その結果、LLDP パケットは、ポート 5 の先につながるスイッチ 0x2 へと到着する

  4. LLDP パケットを受け取ったスイッチ 0x2 は、自身のフローテーブルを参照し、パケットの処理方法を調べる。このとき LLDP に対するフローエントリはあえて設定していないため、今回受信した LLDPパケットは、Packet In としてコントローラまで戻される

  5. コントローラは、受け取った Packet In メッセージを解析することで、リンクの発見を行う。スイッチ 0x2 からは図 15-4 の Packet In メッセージが送られてくる。この中身を見ることで、スイッチ 0x1 のポート 5 と、スイッチ 0x2 のポート 1 の間にリンクを発見できる

lldp packet in
図 15-5: スイッチ 0x2 から送られてくる Packet In メッセージ

このように、Packet Out で送られた LLDP パケットは、リンクを通過し、隣のスイッチから Packet In でコントローラへと戻ってきます。この一連の動作によりコントローラはリンクを発見できます。この方法自体は、OpenFlow 仕様でとくに規定されているわけではありません。それぞれのスイッチは OpenFlow 仕様で定められた動作を行っているだけです。つまり、Packet Out と Packet In をうまく使った “OpenFlow ならでは” のリンク発見方法だと言えます。

トポロジの検出

このリンク発見方法をネットワーク中のすべてのスイッチのすべてのポートに順に適用していけば、ネットワーク全体のスイッチの接続関係、つまりトポロジを検出できます。たとえば図 15-5のような 3 台の OpenFlow スイッチからなるネットワークにおいて、どうやってトポロジを検出できるかを見ていきましょう。各 OpenFlow スイッチがコントローラに接続した直後の状態では、コントローラはスイッチ同士がどのように接続されているかを知りません。

topology before
図 15-5: トポロジ検出前のコントローラ

まずスイッチ 0x1 から調べていきます。はじめに Features Request メッセージを送ることで、スイッチ 0x1 が持つポート一覧を取得します。そして、それぞれのポートに対して、前述のリンク発見手順を行います (図 15-6)。その結果、スイッチ 0x1 からスイッチ 0x2 およびスイッチ 0x3 へと至るリンクそれぞれを発見できます。

topology after
図 15-6: スイッチ 0x1 から出るリンクを発見

あとは同様の手順を、ネットワーク中の各スイッチに対して順に行なっていくだけです。スイッチ 0x2, 0x3 に接続するリンクを順に調べていくことで、ネットワークの完全なトポロジ情報を検出できます。

実行してみよう

このトポロジ検出機能を持つ Topology コントローラを実行してみましょう。ソースコードと仮想ネットワークの設定ファイルは GitHub の trema/topology リポジトリ (https://github.com/trema/topology) からダウンロードできます。今までと同じく、git clone でソースコードを取得し bundle install で必要な gem をインストールしてください。

$ git clone https://github.com/trema/topology.git
$ cd topology
$ bundle install --binstubs

ソースコードに含まれる triangle.conf はスイッチ 3 台を三角形に接続したトライアングル型のトポロジです (図 15-7)。

triangle conf
図 15-7: triangle.confのトポロジ

これをトポロジコントローラで検出するには、次のように実行します。

$ ./bin/trema run ./lib/topology_controller.rb -c triangle.conf
Topology started (text mode).
Port 0x1:1 added: 1
Port 0x1:2 added: 1, 2
Switch 0x1 added: 0x1
Port 0x3:1 added: 1
Port 0x3:2 added: 1, 2
Switch 0x3 added: 0x1, 0x3
Port 0x2:1 added: 1
Port 0x2:2 added: 1, 2
Switch 0x2 added: 0x1, 0x2, 0x3
Link 0x1-0x2 added: 0x1-0x2
Link 0x1-0x3 added: 0x1-0x2, 0x1-0x3
Link 0x2-0x3 added: 0x1-0x2, 0x1-0x3, 0x2-0x3

先に説明したように、コントローラはまず Features Reply メッセージによってスイッチとポートの一覧を取得します。たとえば、Port 0x1:1 added の行はスイッチ 0x1 のポート 1 番をコントローラが検出したという意味です。Switch 0x1 added のメッセージも同じく Features Reply メッセージを返したスイッチのデータパス ID を表示しています。

リンクの検出は LLDP を使って一本ずつ行います。たとえば Link 0x1-0x2 added はスイッチ 0x1 から 0x2 に LLDP パケットが通り、コントローラに PacketIn したことからリンクを一本発見したという意味です。これを繰り返すことで最終的に三角形のトポロジ (Link 0x2-0x3 added: 0x1-0x2, 0x1-0x3, 0x2-0x3 のメッセージ) を発見しています。

トポロジコントローラはトポロジの変化も検出できます。

triangle port down
図 15-8: スイッチ 0x1 のポート 1 番を落としたときのトポロジ

たとえば図 15-8のようにスイッチ 0x1 のポート 1 番を落としてみましょう。

$ ./bin/trema port_down --switch 0x1 --port 1

すると、コントローラを実行したターミナルには次の表示が出ます。たしかに 0x1-0x2 間のリンクが消滅し、残りは 0x1-0x3 と 0x2-0x3 の二本になりました。

Link 0x1-0x2 deleted: 0x1-0x3, 0x2-0x3
Port 0x1:1 deleted: 2

逆に再びポートを上げると、三角形トポロジが復活します。

$ ./bin/trema port_up --switch 0x1 --port 1
Port 0x1:1 added: 1, 2
Link 0x1-0x2 added: 0x1-0x2, 0x1-0x3, 0x2-0x3

トポロジコントローラはトポロジを画像で表示することもできます。この機能を使うためには、システムに graphviz をあらかじめ apt-get でインストールしておきます。そして、trema run の引数に --graphviz トポロジ画像出力ファイル名 を指定します。

$ ./bin/trema run ./lib/topology_controller.rb -c triangle.conf -- graphviz /tmp/topology.png

実行すると、図 15-9 のようにトポロジ画像が生成されます。

graphviz triangle
図 15-9: トポロジコントローラで生成した三角形トポロジの画像

トポロジコントローラのソースコード

トポロジコントローラは大きく分けて 3 つの部品からなります (図 15-10)。

TopologyController クラス

コントローラの本体で、LLDPパケットの送信とトポロジに関する OpenFlow メッセージの処理をします

Topology クラス

収集したトポロジ情報を管理し、トポロジの変化を View クラスへ通知します

View::Text, View::Graphviz クラス

トポロジをテキストまたは画像で表示します

topology classes
図 15-10: トポロジのクラス構成

このクラス分けは、いわゆる MVC モデル (Model-View-Controller) に従っています。TopologyController クラスは MVC の Controller にあたり、OpenFlow スイッチとメッセージをやりとりしたり他のクラスをセットアップしたりといった制御を担当します。Topology クラスは Model にあたり、ネットワークのモデルすなわちトポロジ情報を管理します。View::TextView::Graphviz はその名の通り View にあたり、モデルである Topology を可視化します。

このようにクラスを MVC で構成するとそれぞれのクラスの役割りがすっきりし、拡張性も向上します。たとえばトポロジを HTML で表示したくなった場合には、新たに View::Html クラスを追加するだけで実現できます。しかも、TopologyControllerTopology クラスへの変更はほとんど必要ありません。また、次章で紹介するルーティングスイッチでは、トポロジを部品として使うことで複雑なパケット制御を可能にしています。このように比較的複雑な機能を実現したい場合には、クラスを MVC で構成できるかどうか検討するとよいでしょう。

モデルとビューのセットアップ

TopologyController の仕事の1つは、MVC のモデルとビューのセットアップです。次の start ハンドラでは、起動時のコマンドライン引数をパースし、トポロジ表示をテキスト表示 (View::Text) にするかまたは画像表示 (View::Graphviz) にするかを決定します。そして、決定したビューをモデル (Topology) のオブザーバとして追加 (@topology.add_observer) します。

TopologyController#start
link:vendor/topology/lib/topology_controller.rb[role=include]

このオブザーバは、デザインパターンにおけるいわゆるオブザーバ・パターンの一例です。Topology のオブザーバとして追加されたビューのクラス (View::Text または View::Graphviz) は、トポロジに変化があった場合に変化イベントを Topology から受け取ります。そして、それぞれのビューの方法でトポロジを表示します。

オブザーバが受け取れるトポロジの変化イベントは次の通りです:

  • add_switch: スイッチの追加イベント

  • delete_switch: スイッチの削除イベント

  • add_port: ポートの追加イベント

  • delete_port: ポートの削除イベント

  • add_link: リンクの追加イベント

  • delete_link: リンクの削除イベント

オブザーバとして追加できるオブジェクトは、これらのイベントを受け取れば何でもかまいません。たとえば View::Text は次のように add_switchadd_port といったトポロジイベントハンドラを持っており、イベントに応じてトポロジをテキストベースで表示します。

lib/view/text.rb
link:vendor/topology/lib/view/text.rb[role=include]

MVC で説明したように、未知の外部クラスと連携したい場合にオブザーバ・パターンは便利です。Topology からのイベントを受け取るには Topology#add_observer でオブザーバとして登録するだけで良く、Topology クラスにはオブザーバのクラスに依存するコードはありません。このため、ビューに限らずトポロジ情報を利用するクラスを自由にオブザーバとして追加できます。たとえば次章その次の章で実装するコントローラでは、Topology にコントローラ自身をオブザーバとして登録することで、トポロジ情報を利用してパケットの転送を制御します。

OpenFlow メッセージの処理

TopologyController クラスはスイッチから届く OpenFlow メッセージに応じた処理をします。

switch_ready ハンドラでは、新しく接続してきたスイッチのポート一覧をを知るために、Features Request メッセージをスイッチに投げます。そして、features_reply ハンドラでスイッチから届いた Features Reply が持つポート一覧情報のうち、物理ポートでポートが上がっているものを @topology に追加します。このポート一覧は、LLDP パケットを作って送る際に使います。

TopologyController#switch_ready, TopologyController#features_reply
link:vendor/topology/lib/topology_controller.rb[role=include]

そのほかのハンドラでは、届いたメッセージの種類に応じてトポロジ情報を更新します。

  • switch_disconnected: コントローラとの接続が切れたスイッチをトポロジ情報 (@topology) から削除する

  • port_modify: ポート情報の変更 (ポートのUPとDOWN) を識別し、どちらの場合も @topology に反映する

  • packet_in: 帰ってきた LLDP パケットから発見したリンク情報、または新規ホスト情報を @topology に登録する

TopologyController#switch_disconnected, TopologyController#port_modify, TopologyController#packet_in
link:vendor/topology/lib/topology_controller.rb[role=include]

LLDP パケットをスイッチへ送る

LLDP パケットの定期送信は、flood_lldp_frames メソッドをタイマで呼び出すことで行います。@topology が管理する発見済みポートすべて (@topology.ports) に対して、Packet Out で LLDP パケットを送信します。

TopologyController#flood_lldp_frames
link:vendor/topology/lib/topology_controller.rb[role=include]

トポロジ情報の管理

Topology クラスはトポロジ情報のデータベースです。TopologyController が生の OpenFlow メッセージから解釈したトポロジの変化を、ポート一覧、スイッチ一覧などのデータ構造として保存します。そして、変化イベントをオブザーバへ通知します。たとえば add_switch メソッドでは、新しいスイッチとポート一覧を登録し、オブザーバの add_switch メソッドを呼びます。

Topology#add_switch (lib/topology.rb)
link:vendor/topology/lib/topology.rb[role=include]

スイッチのポート、スイッチにつながっているリンクなど、関連するもの同士は自動的に処理します。たとえば delete_switch メソッドでは、スイッチを消すだけでなくスイッチのポートやスイッチとつながるリンクもすべて消します。

Topology#add_switch (lib/topology.rb)
link:vendor/topology/lib/topology.rb[role=include]

まとめ

ネットワークトポロジをOpenFlowで検出できる、トポロジコントローラの仕組みを見てきました。この章で学んだことを簡単にまとめておきましょう。

  • LLDP でトポロジを検出する仕組み

  • トポロジの変化を検出する OpenFlow メッセージとその処理の実装方法

  • オブザーバーパターンを使った外部クラスとの連携方法

次の章では、ネットワーク仮想化の最初の一歩として、たくさんのスイッチを一台の L2 スイッチとして仮想化できる、ルーティングスイッチコントローラを見ていきます。