今日も秋田で IoT

旧 Trema 日記

Ubuntu に最新版の Open vSwitch をインストールする

今回は Ubuntu に、最新版の Open vSwitch をインストールする方法を紹介します。Open vSwitch は、今まさに開発が進行中のプロジェクトなので、使いたい機能が最新版においてのみサポートといったことが多々有ります。そのため、なるべく最新版を使えるようにしておきたいものです。

この記事を書いている段階での Open vSwitch の最新版は 1.10.0 ですが(注 8/27 に 1.11.0 が出ました)、apt-get で入れようとすると 1.9.0 が入ります。最新版を入れるためには、ソースコードからインストールする必要があります。もちろん、make install でインストールしてもいいのですが、後々の管理を考えるとパッケージにしておいたほうがベターです。なので今回はパッケージを作り、インストールする方法の紹介です。(といっても、ほとんどはソース中に同梱の INSTALL.Debian を読めば書いてあることですが。)

なお、RPM パッケージに作り方は、ここ にあります。Redhat 系のディストリビューションを使用している人は、こちらをご参考に。

準備

ビルドに必要なパッケージを予めインストールしておきます。

$ sudo apt-get install gcc make build-essential fakeroot dkms procps 
$ sudo apt-get install debhelper autoconf automake libssl-dev pkg-config openssl
$ sudo apt-get install python-all python-qt4 python-zopeinterface python-twisted-conch

ダウンロード、ビルド、インストール

Open vSwitch 1.10 をダウンロードし、ソースを展開します。

$ wget http://openvswitch.org/releases/openvswitch-1.10.0.tar.gz
$ tar zxvf ./openvswitch-1.10.0.tar.gz

Debian のパッケージを作成します。ビルドからパッケージの作成まで一括して行うためのスクリプトが用意されているので、それを fakeroot を使って実行します。

$ cd openvswitch-1.10.0
$ fakeroot debian/rules binary

一つ下のディレクトリにパッケージ類が作成されているので、以下のパッケージをインストールします。

$ cd ..
$ sudo dpkg -i ./openvswitch-common_1.10.0-1_amd64.deb
$ sudo dpkg -i ./openvswitch-switch_1.10.0-1_amd64.deb
$ sudo dpkg -i ./openvswitch-datapath-dkms_1.10.0-1_all.deb

起動

openvswitch.ko は、bridge.ko と共存できません。lsmod を使って bridge.ko がロードされているかを確認し、されていればアンロードしておきます。

$ sudo rmmod bridge

ここまで準備ができたら、以下のコマンドで openvswitch を起動します。

$ sudo service openvswitch-switch start

ovs-vsctl コマンドで、インストールされた openvswitch のバージョンを確認します。

$ sudo ovs-vsctl show
...
    ovs_version: "1.10.0"

Flow エントリをポート指定で消す (その2)

今回は、ポート番号の指定によりフローエントリを消す方法について、実際に動かして試してみましょう。

以下の内容のコントローラを outport_test.rb というファイル名で用意します。

class OutportTest < Controller
  def switch_ready dpid
    send_flow_mod_add(
      dpid,
      :match => Match.new( :in_port => 1 ),
      :actions => SendOutPort.new( 2 )
    )
    send_flow_mod_add(
      dpid,
      :priority => 10000,
      :match => Match.new( :in_port => 1 ),
      :actions => SendOutPort.new( 3 )
    )
    send_message dpid, FeaturesRequest.new
  end


  def features_reply dpid, message
    message.ports.each do | each |
      puts "Port no: #{ each.number }, name: #{ each.name }"
    end
  end


  def port_status dpid, message
    return if message.phy_port.state & Port::OFPPS_LINK_DOWN == 0
    puts "Port #{ message.phy_port.number } is down."
    send_flow_mod_delete(
      dpid,
      :match => Match.new,
      :out_port => message.phy_port.number
    )
  end
end

エミュレータ用の設定ファイル (outport_test.conf) も用意しましょう。

vswitch {  datapath_id "0x1" }

vhost ( "host1" )
vhost ( "host2" )
vhost ( "host3" ) 

link "0x1", "host1"
link "0x1", "host2"
link "0x1", "host3"

まずは作成したコントローラを実行してみましょう。Features Request を用いて取得したポート番号と名前の一覧が表示されます。

$ trema run ./outport_test.rb  -c ./test.conf 
Port no: 3, name: trema0-0
Port no: 2, name: trema2-0
Port no: 65534, name: vsw_0x1
Port no: 1, name: trema1-0

このコントローラは、switch_ready の段階で二つのフローエントリを設定します。trema apps に用意されている flow_dumper を用いて、フローエントリが二つ設定されていることを確認してみましょう。

$ cd ../apps/flow_dumper/
$ trema run ./flow-dumper.rb 
[0x1] table_id = 0, priority = 65535, cookie = 0x357c000000000001, idle_timeout = 0, hard_timeout = 0, duration = 2, packet_count = 0, byte_count = 0, match = [wildcards = 0x3820fe(dl_src|dl_dst|dl_type|dl_vlan|dl_vlan_pcp|nw_proto|nw_tos|nw_src(32)|nw_dst(32)|tp_src|tp_dst), in_port = 1, dl_src = 00:00:00:00:00:00, dl_dst = 00:00:00:00:00:00, dl_vlan = 0, dl_vlan_pcp = 0, dl_type = 0, nw_tos = 0, nw_proto = 0, nw_src = 0.0.0.0/0, nw_dst = 0.0.0.0/0, tp_src = 0, tp_dst = 0], actions = [Trema::SendOutPort: port_number=2, max_len=65535]
[0x1] table_id = 0, priority = 10000, cookie = 0x357c000000000002, idle_timeout = 0, hard_timeout = 0, duration = 2, packet_count = 0, byte_count = 0, match = [wildcards = 0x3820fe(dl_src|dl_dst|dl_type|dl_vlan|dl_vlan_pcp|nw_proto|nw_tos|nw_src(32)|nw_dst(32)|tp_src|tp_dst), in_port = 1, dl_src = 00:00:00:00:00:00, dl_dst = 00:00:00:00:00:00, dl_vlan = 0, dl_vlan_pcp = 0, dl_type = 0, nw_tos = 0, nw_proto = 0, nw_src = 0.0.0.0/0, nw_dst = 0.0.0.0/0, tp_src = 0, tp_dst = 0], actions = [Trema::SendOutPort: port_number=3, max_len=65535]

次にポート 2 をダウンさせ、該当するフローエントリの削除を確認してみましょう。ポート 2 の名前は、trema2-0 なので、これを ifconfig でダウンさせてみます。その後、flow-dumper.rb でフローエントリを確認してみましょう。

$ sudo ifconfig trema2-0 down
$ trema run ./flow-dumper.rb 
[0x1] table_id = 0, priority = 10000, cookie = 0x357c000000000002, idle_timeout = 0, hard_timeout = 0, duration = 10, packet_count = 0, byte_count = 0, match = [wildcards = 0x3820fe(dl_src|dl_dst|dl_type|dl_vlan|dl_vlan_pcp|nw_proto|nw_tos|nw_src(32)|nw_dst(32)|tp_src|tp_dst), in_port = 1, dl_src = 00:00:00:00:00:00, dl_dst = 00:00:00:00:00:00, dl_vlan = 0, dl_vlan_pcp = 0, dl_type = 0, nw_tos = 0, nw_proto = 0, nw_src = 0.0.0.0/0, nw_dst = 0.0.0.0/0, tp_src = 0, tp_dst = 0], actions = [Trema::SendOutPort: port_number=3, max_len=65535]

出力ポートが 2 であるフローエントリが削除されていることが確認できたでしょうか?

Flow エントリをポート指定で消す (その1)

今回は、出力ポートが同じである複数のフローエントリを消す方法を紹介します。

早速ですが、Flow Mod メッセージフォーマットを見てみましょう。

/* Flow setup and teardown (controller -> datapath). */
struct ofp_flow_mod {
    struct ofp_header header;
    struct ofp_match match;      /* Fields to match */
    uint64_t cookie;             /* Opaque controller-issued identifier. */

    /* Flow actions. */
    uint16_t command;             /* One of OFPFC_*. */
    uint16_t idle_timeout;        /* Idle time before discarding (seconds). */
    uint16_t hard_timeout;        /* Max time before discarding (seconds). */
    uint16_t priority;            /* Priority level of flow entry. */
    uint32_t buffer_id;           /* Buffered packet to apply to (or -1).
                                     Not meaningful for OFPFC_DELETE*. */
    uint16_t out_port;            /* For OFPFC_DELETE* commands, require
                                     matching entries to include this as an
                                     output port.  A value of OFPP_NONE
                                     indicates no restriction. */
    uint16_t flags;               /* One of OFPFF_*. */
    struct ofp_action_header actions[0]; /* The action length is inferred
                                            from the length field in the
                                            header. */
};
OFP_ASSERT(sizeof(struct ofp_flow_mod) == 72);

ポイントは、このフィールド中の out_port です。ここにポート番号が指定された場合、Output アクションで指定されたポート番号と一致したエントリのみ削除されることになります。特に指定する必要がない場合には OFPP_NONE を指定する必要があります。

通常は削除したい Flow エントリの match を含む Flow Mod メッセージを作り、command に OFPFC_DELETE または OFPFC_DELETE_STRICT でスイッチに送れば該当するエントリが削除されます。しかし、out_port に値を指定すると、該当するエントリのうち、アクションの出力ポートが out_port で指定された値と同一の場合のみ、そのエントリが削除されます。

この out_port の指定を使えば、例えばあるリンクが故障した場合、そのリンクに接続するポートに対して出力するアクションを含むフローエントリを一括して削除することができます。match はすべてワイルドカードに、command は OFPFC_DELETE にした上で、out_port にそのポート番号を格納した Flow Mod メッセージを作り、スイッチに送ればよいです。

次回以降、具体例を紹介したいと思います。

Features Request でスイッチから情報を取得する (その5)

前回からだいぶ時間がたってしまいましたが、Features Request を用いた情報取得に関する記事の最終回です。今回は、第一回で作成した ShowFeatures コントローラを改造して、Features Request で取得できる情報を更に出力できるようにしてみます。

class ShowFeatures < Controller
  def switch_ready datapath_id
    send_message datapath_id, FeaturesRequest.new
  end

  def features_reply datapath_id, message
    puts "Datapath ID: #{ datapath_id.to_hex }"
    puts "Max packets: #{ message.n_buffers }"
    puts "Number of tables: #{ message.n_tables }"
    puts "Capabilities: #{ message.capabilities.to_s( 2 ) }"
    puts "Supported actions: #{ message.actions.to_s( 2 ) }"

    message.ports.each do | each |
      puts "Port no: #{ each.number }"
      puts "  Hardware address: #{ each.hw_addr.to_s }"
      puts "  Port name: #{ each.name }"
      puts "  Config: #{ each.config.to_s( 2 ) }"
      puts "  State: #{ each.state.to_s( 2 ) }"
      puts "  Features: #{ each.curr.to_s( 2 ) }"
      puts "    Advertised: #{ each.advertised.to_s( 2 ) }"
      puts "    Supported: #{ each.supported.to_s( 2 ) }"
      puts "    Peer: #{ each.peer.to_s( 2 ) }"
    end
  end
end

実行してみる

ShowFeatures コントローラを実行してみましょう。

$ sudo trema run ./show-features.rb -c ./network.conf 
Datapath ID: 0xabc
Max packets: 256
Number of tables: 1
Capabilities: 10000111
Supported actions: 111111111111
Port no: 2
  Hardware address: 1a:18:a9:7d:35:75
  Port name: trema0-1
  Config: 0
  State: 0
  Features: 11000000
    Advertised: 0
    Supported: 0
    Peer: 0
Port no: 65534
  Hardware address: 02:2a:33:5d:6c:bd
  Port name: vsw_0xabc
  Config: 1
  State: 1
  Features: 10000010
    Advertised: 0
    Supported: 0
    Peer: 0
Port no: 1
  Hardware address: 56:e5:fe:83:bc:9f
  Port name: trema1-1
  Config: 0
  State: 0
  Features: 11000000
    Advertised: 0
    Supported: 0
    Peer: 0
^C
terminated

スイッチのケイパビリティは、10000111 と表示されています。第三回の記事に記載した enum ofp_capabilities と見比べると、このスイッチでは OFPC_FLOW_STATS, OFPC_TABLE_STATS, OFPC_PORT_STATS, OFPC_ARP_MATCH_IP がサポートされていることがわかります。一方で、OFPC_STP, OFPC_RESERVED, OFPC_IP_REASM, OFPC_QUEUE_STATS はサポートされていません。

また、サポートアクションに関しては、111111111111 と表示されています。OpenFlow 1.0 の仕様で定義されているアクションの数は 12 ですので、このスイッチはこれらをすべてサポートしていることを意味しています。

次に各ポートごとの情報を見ていきます。Config と State に関しては、Port 番号 1 と 2 に関しては両方共 0 となっています。Config が 0 ということは定義されている設定は特に行われていないことを意味します。また State が 0 ということは、STP も特に利用していないので、単に Link up していることを意味します。一方で Port 番号 65534 に関しては、Administrative down されていることがわかります。

ポートのフィーチャーに関しては、Port 番号 1, 2 のポートでは OFPPF_10GB_FD, OFPPF_COPPER のフラグが立っています。今回使用している OpenFlow スイッチでは 10GbE のカッパーを使用しておらず、リンクに関してはソフトウェア的に仮想的に作られてものです。今回使用しているソフトウェアスイッチは、ソフトウェア的なリンクを 10GbE のカッパーとして返す実装を行なっているようです。

レビュー「マスタリングTCP/IP OpenFlow編」

ネットワーク技術者御用達のマスタリング TCP/IP シリーズから、OpenFlow 編が出ました。著者の方から献本頂きましたので、レビューをしたいと思います。

マスタリングTCP/IP OpenFlow編

マスタリングTCP/IP OpenFlow編

第 1 章 OpenFlow 概要

成り立ちから動作例まで、OpenFlow の概要について書かれた章です。成り立ちについても紋切り的な説明に終始せず、なぜこのような仕様になったのか、仕様策定の裏側が伺えるような説明がされています。

また、OpenFlow チャネルで使用するためのネットワークについてもきちんと説明されており、一般的なネットワーク技術者が疑問に思うだろう点が丁寧に取り上げられている印象です。

OpenFlow の概要を抑えるのに、まさに十分な章となっています。

第 2 章 OpenFlow 1.0 の仕組み

この章以降で、概要からさらに踏み込んだ説明がされていきます。まずは第 2 章で OpenFlow 1.0 の仕様の詳細が説明されます。

ここで嬉しいのはメッセージのパケットフォーマットが記載されているところですね。デバッグ時、セキュアチャネルを tcpdump で覗く時に役立つこと間違いなしです。

第 3 章 〜 第 6 章

これらの章では、LLDP によるトポロジー検出の仕組みから、ユースケースまで、主に OpenFlow の活用例が紹介されています。LLDP 等 OpenFlow を活用する上で必要不可欠な周辺技術についても丁寧に説明してある点が、この本の良いところです。

第 7 章 〜 第 9 章

OpenFlow 1.1, 1.2, 1.3 で追加された仕様について説明された章です。昨今 OpenFlow 1.3 対応機器が増えつつある中、仕様を理解するために日本語での説明は非常にありがたいです。

これらの章も単に仕様の説明に終始せず、関連するネットワーク技術についても合わせて紹介されており、どのように利用するのかがイメージできるようわかりやすく説明されています。

第 10 章 〜 第 11 章

OpenFlow の留意事項とこれからについて書かれています。使用時に気をつけるべきポイントがまとめてあるため、OpenFlow を実際に使う人には役に立つ章となっています。

全体を通して

この本は、単に仕様や使い方を説明するのみならず、読者が気になるだろう点に関して踏み込んだ説明をしてくれています。OpenFlow の仕組みを理解したい人や、OpenFlow を使って何かをしたい人など、多くの人にとって役立つ本だと思います。