Trema

2014年6月11日 (水)

◆【改訂】OpenFlow フレームワーク Trema 0.4 のインストール手順【0.4.7対応】

[2014-0727 追記: apt-get install で Not Found になる場合について追加しました。]
[2014-0727 追記2: 新規に Lubuntu 13.04 をインストールする場合の注意事項を追加。]

Trema 0.4.7 では、前提となる RubyGems のバージョンがあがっているため、https://github.com/trema/trema に記載のコマンドを実行しただけではインストールに失敗することが分かりました。失敗しないようにするためのインストール手順を整理しておきます。

RubyGems をバージョンアップしないとどうなるかは、前回の記事「◆ Trema 0.4.7インストール失敗とバージョン指定インストール」に記載しています。

■確認環境
・OS: Lubuntu 13.04 Desktop (i386)
    (2014年06月08日にソフトウェアアップデート済)
  未確認ですが Ubuntu 13.04 Desktop (i386)でも同じだと思います。
  ※[2014-0727 追記:新規に Lubuntu13.04 をインストールされる場合は、記事「◆サポート期限切れの Lubuntu 13.04 を新規インストール 」を参照ください。]
・Trema: 0.4.7

■インストール手順

https://github.com/trema/trema の「Getting Started」に従ってインストールしていきます。

1. 「1.Install the prerequisites at the command prompt:」に従い以下のコマンドを実行:

$ sudo apt-get install gcc make git ruby rubygems ruby-dev libpcap-dev libsqlite3-dev libglib2.0-dev

[2014-0727 追記: ここで、「Not Found」が出て失敗する現象が起きる場合は、記事「◆ Lubuntu 13.04 の apt-get install で「Not Found」となる現象への対処」を参照して対処ください。
]

2.前提の確認 (ここがミソ)

「Getting Started」には書いていない手順です。前提を満たしているか確認します。
「Supported Platforms」を見ると以下の記載があります(2014年06月11時点):
•Ruby 1.9.3 or higher
•RubyGems 1.8.25 or higher

実際にインストールされているバージョンを確認します。

$ ruby -v
ruby 1.9.3p194 (2012-04-20 revision 35410) [i686-linux]
$ gem -v
1.8.23

RubyGems のバージョンが 1.8.23 で満たせていません。そこで、RubyGems のバージョンをあげるため以下のコマンドを実行します:

$ sudo gem install rubygems-update -v 1.8.25
$ sudo update_rubygems

update_rubygems コマンドを実行するとバージョンアップ分の変更内容が大量に出力されます。ちょっとびっくりしますが問題ありません。

ここでは rubygem-update のインストールで -v 1.8.25 と指定することで Trema が必要とする最低のバージョンにしています。
バージョン指定なしだと最新版(現時点で 2.3.0)が入るのですが trema のインストール時に妙なメッセージが出るので避けた方が良さそうです。
(ちなみに 1.8 の最新 1.8.29 でも大丈夫でしたが、Trema さえ入れば良いと割り切って 1.8 の最新にする手順にはしていません。)

では、バージョンを再度確認します:

$ gem -v
1.8.25

今度は 1.8.25 なので前提を満たせていることが確認できました。

3. 「Install Trema at the command prompt:」に従い以下のコマンドを実行します:

$ sudo gem install trema

コマンド実行時の画面は以下の通り:

$ sudo gem install trema
Fetching: bundler-1.6.2.gem (100%)
Fetching: gli-2.10.0.gem (100%)
Fetching: Platform-0.4.0.gem (100%)
Fetching: open4-1.3.4.gem (100%)
Fetching: POpen4-0.1.4.gem (100%)
Fetching: rake-10.3.2.gem (100%)
Fetching: paper_house-0.6.2.gem (100%)
Fetching: bindata-2.1.0.gem (100%)
Fetching: pio-0.7.0.gem (100%)
Fetching: json-1.8.1.gem (100%)
Building native extensions.  This could take a while...
Fetching: rdoc-4.1.1.gem (100%)
Depending on your version of ruby, you may need to install ruby rdoc/ri data:

<= 1.8.6 : unsupported
 = 1.8.7 : gem install rdoc-data; rdoc-data --install
 = 1.9.1 : gem install rdoc-data; rdoc-data --install
>= 1.9.2 : nothing to do! Yay!
Fetching: trema-0.4.7.gem (100%)
Building native extensions.  This could take a while...
Successfully installed bundler-1.6.2
Successfully installed gli-2.10.0
Successfully installed Platform-0.4.0
Successfully installed open4-1.3.4
Successfully installed POpen4-0.1.4
Successfully installed rake-10.3.2
Successfully installed paper_house-0.6.2
Successfully installed bindata-2.1.0
Successfully installed pio-0.7.0
Successfully installed json-1.8.1
Successfully installed rdoc-4.1.1
Successfully installed trema-0.4.7
12 gems installed
Installing ri documentation for bundler-1.6.2...
Installing ri documentation for gli-2.10.0...
Installing ri documentation for Platform-0.4.0...
Installing ri documentation for open4-1.3.4...
Installing ri documentation for POpen4-0.1.4...
Installing ri documentation for rake-10.3.2...
Installing ri documentation for paper_house-0.6.2...
Installing ri documentation for bindata-2.1.0...
Installing ri documentation for pio-0.7.0...
Installing ri documentation for json-1.8.1...
Installing ri documentation for rdoc-4.1.1...
Installing ri documentation for trema-0.4.7...
Installing RDoc documentation for bundler-1.6.2...
Installing RDoc documentation for gli-2.10.0...
Installing RDoc documentation for Platform-0.4.0...
Installing RDoc documentation for open4-1.3.4...
Installing RDoc documentation for POpen4-0.1.4...
Installing RDoc documentation for rake-10.3.2...
Installing RDoc documentation for paper_house-0.6.2...
Installing RDoc documentation for bindata-2.1.0...
Installing RDoc documentation for pio-0.7.0...
Installing RDoc documentation for json-1.8.1...
Installing RDoc documentation for rdoc-4.1.1...
Installing RDoc documentation for trema-0.4.7...

4.Trema のバージョン確認

では、Trema のバージョン確認をしてみます:

$ trema version
trema version 0.4.7

無事 Trema がインストールできました。


■ RubyGems 2.3.0 を使うとどうなるのか?

上書きをするか求められて N を選択するとインストールが中断されます:

$ sudo gem install trema
Fetching: json-1.8.1.gem (100%)
Building native extensions.  This could take a while...
Successfully installed json-1.8.1
Fetching: rdoc-4.1.1.gem (100%)
rdoc's executable "rdoc" conflicts with /usr/bin/rdoc
Overwrite the executable? [yN] N
ERROR:	Error installing trema:
	"rdoc" from rdoc conflicts with /usr/bin/rdoc

再度、実行して上書きを y で許可すると今度は大量のメッセージが・・・:

$ sudo gem install trema
rdoc's executable "rdoc" conflicts with /usr/bin/rdoc
Overwrite the executable? [yN]	y
rdoc's executable "ri" conflicts with /usr/bin/ri
Overwrite the executable? [yN]	y
Depending on your version of ruby, you may need to install ruby rdoc/ri data:

<= 1.8.6 : unsupported
 = 1.8.7 : gem install rdoc-data; rdoc-data --install
 = 1.9.1 : gem install rdoc-data; rdoc-data --install
>= 1.9.2 : nothing to do! Yay!
Successfully installed rdoc-4.1.1
Fetching: rake-10.3.2.gem (100%)
Successfully installed rake-10.3.2
Fetching: bindata-2.1.0.gem (100%)
Successfully installed bindata-2.1.0
Fetching: pio-0.7.0.gem (100%)
Successfully installed pio-0.7.0
Fetching: Platform-0.4.0.gem (100%)
Successfully installed Platform-0.4.0
Fetching: open4-1.3.4.gem (100%)
Successfully installed open4-1.3.4
Fetching: POpen4-0.1.4.gem (100%)
Successfully installed POpen4-0.1.4
Fetching: paper_house-0.6.2.gem (100%)
Successfully installed paper_house-0.6.2
Fetching: gli-2.10.0.gem (100%)
Successfully installed gli-2.10.0
Fetching: bundler-1.6.2.gem (100%)
Successfully installed bundler-1.6.2
Fetching: trema-0.4.7.gem (100%)
Building native extensions.  This could take a while...
Successfully installed trema-0.4.7
Installing ri documentation for rdoc-4.1.1
Installing ri documentation for rake-10.3.2
unable to convert "\xC5" from ASCII-8BIT to UTF-8 for examples/NBT.txt, skipping
Installing ri documentation for bindata-2.1.0
Installing ri documentation for pio-0.7.0
Installing ri documentation for Platform-0.4.0
Installing ri documentation for open4-1.3.4
Installing ri documentation for POpen4-0.1.4
unable to convert "\xCF" from ASCII-8BIT to UTF-8 for examples/fail/hello, skipping
Installing ri documentation for paper_house-0.6.2
Installing ri documentation for gli-2.10.0
Installing ri documentation for bundler-1.6.2
unable to convert "\xD0" from ASCII-8BIT to UTF-8 for objects/tremashark/syslog_relay, skipping
unable to convert "\xA0" from ASCII-8BIT to UTF-8 for objects/tremashark/stdin_relay, skipping
unable to convert "\xA0" from ASCII-8BIT to UTF-8 for objects/tremashark/tremashark, skipping
unable to convert "\xC0" from ASCII-8BIT to UTF-8 for objects/tremashark/packet_capture, skipping
unable to convert "\xE4" from ASCII-8BIT to UTF-8 for objects/openvswitch/bin/ovs-vsctl, skipping
unable to convert "\x98" from ASCII-8BIT to UTF-8 for objects/openvswitch/bin/ovsdb-client, skipping
unable to convert "\x94" from ASCII-8BIT to UTF-8 for objects/openvswitch/bin/ovs-appctl, skipping
unable to convert "\xE0" from ASCII-8BIT to UTF-8 for objects/openvswitch/bin/ovs-controller, skipping
unable to convert "\xEC" from ASCII-8BIT to UTF-8 for objects/openvswitch/bin/ovs-openflowd, skipping
unable to convert "\xE4" from ASCII-8BIT to UTF-8 for objects/openvswitch/bin/ovs-dpctl, skipping
unable to convert "\xAC" from ASCII-8BIT to UTF-8 for objects/openvswitch/bin/ovsdb-tool, skipping
unable to convert "\xB8" from ASCII-8BIT to UTF-8 for objects/openvswitch/bin/ovs-ofctl, skipping
unable to convert "\xB0" from ASCII-8BIT to UTF-8 for objects/openvswitch/bin/ovs-benchmark, skipping
unable to convert "\x8C" from ASCII-8BIT to UTF-8 for objects/openvswitch/sbin/ovsdb-server, skipping
unable to convert "\xD4" from ASCII-8BIT to UTF-8 for objects/openvswitch/sbin/ovs-brcompatd, skipping
unable to convert "\xD8" from ASCII-8BIT to UTF-8 for objects/openvswitch/sbin/ovs-vlan-bug-workaround, skipping
unable to convert "\xD4" from ASCII-8BIT to UTF-8 for objects/openvswitch/sbin/ovs-vswitchd, skipping
unable to convert "\xD0" from ASCII-8BIT to UTF-8 for objects/packetin_filter/packetin_filter, skipping
unable to convert "\xF0" from ASCII-8BIT to UTF-8 for objects/examples/openflow_switch/echo_switch, skipping
unable to convert "\xF0" from ASCII-8BIT to UTF-8 for objects/examples/openflow_switch/hello_switch, skipping
unable to convert "\xA6" from ASCII-8BIT to UTF-8 for objects/examples/dumper/dumper, skipping
unable to convert "\xA6" from ASCII-8BIT to UTF-8 for objects/examples/switch_monitor/switch_monitor, skipping
unable to convert "\xA6" from ASCII-8BIT to UTF-8 for objects/examples/packet_in/packet_in, skipping
unable to convert "\xC0" from ASCII-8BIT to UTF-8 for objects/examples/traffic_monitor/traffic_monitor, skipping
unable to convert "\xA6" from ASCII-8BIT to UTF-8 for objects/examples/switch_info/switch_info, skipping
unable to convert "\xA7" from ASCII-8BIT to UTF-8 for objects/examples/multi_learning_switch/multi_learning_switch, skipping
unable to convert "\xA8" from ASCII-8BIT to UTF-8 for objects/examples/switch_event_config/delete_forward_entry, skipping
unable to convert "\xA8" from ASCII-8BIT to UTF-8 for objects/examples/switch_event_config/dump_forward_entries, skipping
unable to convert "\xA8" from ASCII-8BIT to UTF-8 for objects/examples/switch_event_config/add_forward_entry, skipping
unable to convert "\xA8" from ASCII-8BIT to UTF-8 for objects/examples/switch_event_config/set_forward_entries, skipping
unable to convert "\xA6" from ASCII-8BIT to UTF-8 for objects/examples/cbench_switch/cbench_switch, skipping
unable to convert "\xA6" from ASCII-8BIT to UTF-8 for objects/examples/repeater_hub/repeater_hub, skipping
unable to convert "\x90" from ASCII-8BIT to UTF-8 for objects/examples/packetin_filter_config/dump_filter, skipping
unable to convert "\xA0" from ASCII-8BIT to UTF-8 for objects/examples/packetin_filter_config/add_filter, skipping
unable to convert "\xA0" from ASCII-8BIT to UTF-8 for objects/examples/packetin_filter_config/delete_filter_strict, skipping
unable to convert "\x90" from ASCII-8BIT to UTF-8 for objects/examples/packetin_filter_config/dump_filter_strict, skipping
unable to convert "\xA0" from ASCII-8BIT to UTF-8 for objects/examples/packetin_filter_config/delete_filter, skipping
unable to convert "\xB0" from ASCII-8BIT to UTF-8 for objects/examples/learning_switch/learning_switch, skipping
unable to convert "\xB0" from ASCII-8BIT to UTF-8 for objects/examples/list_switches/list_switches, skipping
unable to convert "\xA6" from ASCII-8BIT to UTF-8 for objects/examples/openflow_message/features_request, skipping
unable to convert "\xA6" from ASCII-8BIT to UTF-8 for objects/examples/openflow_message/hello, skipping
unable to convert "\xA6" from ASCII-8BIT to UTF-8 for objects/examples/openflow_message/vendor_action, skipping
unable to convert "\xA6" from ASCII-8BIT to UTF-8 for objects/examples/openflow_message/echo, skipping
unable to convert "\xA6" from ASCII-8BIT to UTF-8 for objects/examples/openflow_message/set_config, skipping
unable to convert "\xA6" from ASCII-8BIT to UTF-8 for objects/management/set_logging_level, skipping
unable to convert "\xD0" from ASCII-8BIT to UTF-8 for objects/management/application, skipping
unable to convert "\xA6" from ASCII-8BIT to UTF-8 for objects/management/echo, skipping
unable to convert "\xA6" from ASCII-8BIT to UTF-8 for objects/management/show_stats, skipping
unable to convert "\x9D" from ASCII-8BIT to UTF-8 for objects/oflops/bin/oflops, skipping
unable to convert "\x94" from ASCII-8BIT to UTF-8 for objects/oflops/bin/cbench, skipping
unable to convert "\x95" from ASCII-8BIT to UTF-8 for objects/phost/phost, skipping
unable to convert "\xEC" from ASCII-8BIT to UTF-8 for objects/phost/cli, skipping
unable to convert "\xAA" from ASCII-8BIT to UTF-8 for objects/switch_manager/switch, skipping
unable to convert "\xC0" from ASCII-8BIT to UTF-8 for objects/switch_manager/switch_manager, skipping
unable to convert "\x9D" from ASCII-8BIT to UTF-8 for vendor/oflops-0.03.trema1/oflops, skipping
unable to convert "\x94" from ASCII-8BIT to UTF-8 for vendor/oflops-0.03.trema1/cbench/cbench, skipping
unable to convert "\xE4" from ASCII-8BIT to UTF-8 for vendor/openvswitch-1.2.2.trema1/utilities/ovs-vsctl, skipping
unable to convert "\xE3" from ASCII-8BIT to UTF-8 for vendor/openvswitch-1.2.2.trema1/utilities/nlmon, skipping
unable to convert "\x94" from ASCII-8BIT to UTF-8 for vendor/openvswitch-1.2.2.trema1/utilities/ovs-appctl, skipping
unable to convert "\xE0" from ASCII-8BIT to UTF-8 for vendor/openvswitch-1.2.2.trema1/utilities/ovs-controller, skipping
unable to convert "\xE4" from ASCII-8BIT to UTF-8 for vendor/openvswitch-1.2.2.trema1/utilities/ovs-dpctl, skipping
unable to convert "\xD8" from ASCII-8BIT to UTF-8 for vendor/openvswitch-1.2.2.trema1/utilities/ovs-vlan-bug-workaround, skipping
unable to convert "\xB8" from ASCII-8BIT to UTF-8 for vendor/openvswitch-1.2.2.trema1/utilities/ovs-ofctl, skipping
unable to convert "\xB0" from ASCII-8BIT to UTF-8 for vendor/openvswitch-1.2.2.trema1/utilities/ovs-benchmark, skipping
unable to convert "\xE0" from ASCII-8BIT to UTF-8 for vendor/openvswitch-1.2.2.trema1/tests/test-lockfile, skipping
unable to convert "\xD5" from ASCII-8BIT to UTF-8 for vendor/openvswitch-1.2.2.trema1/tests/test-random, skipping
unable to convert "\xF0" from ASCII-8BIT to UTF-8 for vendor/openvswitch-1.2.2.trema1/tests/test-sha1, skipping
unable to convert "\xFE" from ASCII-8BIT to UTF-8 for vendor/openvswitch-1.2.2.trema1/tests/test-classifier, skipping
unable to convert "\xD4" from ASCII-8BIT to UTF-8 for vendor/openvswitch-1.2.2.trema1/tests/test-aes128, skipping
unable to convert "\xD4" from ASCII-8BIT to UTF-8 for vendor/openvswitch-1.2.2.trema1/tests/test-byte-order, skipping
unable to convert "\x88" from ASCII-8BIT to UTF-8 for vendor/openvswitch-1.2.2.trema1/tests/test-list, skipping
unable to convert "\xD7" from ASCII-8BIT to UTF-8 for vendor/openvswitch-1.2.2.trema1/tests/test-uuid, skipping
unable to convert "\xD8" from ASCII-8BIT to UTF-8 for vendor/openvswitch-1.2.2.trema1/tests/test-reconnect, skipping
unable to convert "\x88" from ASCII-8BIT to UTF-8 for vendor/openvswitch-1.2.2.trema1/tests/test-hmap, skipping
unable to convert "\xDC" from ASCII-8BIT to UTF-8 for vendor/openvswitch-1.2.2.trema1/tests/test-bundle, skipping
unable to convert "\x86" from ASCII-8BIT to UTF-8 for vendor/openvswitch-1.2.2.trema1/tests/test-strtok_r, skipping
unable to convert "\xD3" from ASCII-8BIT to UTF-8 for vendor/openvswitch-1.2.2.trema1/tests/test-file_name, skipping
unable to convert "\xCC" from ASCII-8BIT to UTF-8 for vendor/openvswitch-1.2.2.trema1/tests/test-ovsdb, skipping
unable to convert "\xDC" from ASCII-8BIT to UTF-8 for vendor/openvswitch-1.2.2.trema1/tests/test-json, skipping
unable to convert "\xF0" from ASCII-8BIT to UTF-8 for vendor/openvswitch-1.2.2.trema1/tests/testsuite, skipping
unable to convert "\xE3" from ASCII-8BIT to UTF-8 for vendor/openvswitch-1.2.2.trema1/tests/test-packets, skipping
unable to convert "\xC8" from ASCII-8BIT to UTF-8 for vendor/openvswitch-1.2.2.trema1/tests/test-jsonrpc, skipping
unable to convert "\xD4" from ASCII-8BIT to UTF-8 for vendor/openvswitch-1.2.2.trema1/tests/test-unix-socket, skipping
unable to convert "\xE2" from ASCII-8BIT to UTF-8 for vendor/openvswitch-1.2.2.trema1/tests/test-timeval, skipping
unable to convert "\xD3" from ASCII-8BIT to UTF-8 for vendor/openvswitch-1.2.2.trema1/tests/test-util, skipping
unable to convert "\xB7" from ASCII-8BIT to UTF-8 for vendor/openvswitch-1.2.2.trema1/tests/test-flows, skipping
unable to convert "\xB8" from ASCII-8BIT to UTF-8 for vendor/openvswitch-1.2.2.trema1/tests/test-multipath, skipping
unable to convert "\xF8" from ASCII-8BIT to UTF-8 for vendor/openvswitch-1.2.2.trema1/tests/test-hash, skipping
unable to convert "\xA0" from ASCII-8BIT to UTF-8 for vendor/openvswitch-1.2.2.trema1/tests/test-csum, skipping
unable to convert "\x80" from ASCII-8BIT to UTF-8 for vendor/openvswitch-1.2.2.trema1/tests/test-vconn, skipping
unable to convert "\xEC" from ASCII-8BIT to UTF-8 for vendor/openvswitch-1.2.2.trema1/tests/test-openflowd, skipping
unable to convert "\x91" from ASCII-8BIT to UTF-8 for vendor/openvswitch-1.2.2.trema1/tests/test-type-props, skipping
unable to convert "\x8C" from ASCII-8BIT to UTF-8 for vendor/openvswitch-1.2.2.trema1/ovsdb/ovsdb-server, skipping
unable to convert "\x98" from ASCII-8BIT to UTF-8 for vendor/openvswitch-1.2.2.trema1/ovsdb/ovsdb-client, skipping
unable to convert "\xAC" from ASCII-8BIT to UTF-8 for vendor/openvswitch-1.2.2.trema1/ovsdb/ovsdb-tool, skipping
unable to convert "\xD4" from ASCII-8BIT to UTF-8 for vendor/openvswitch-1.2.2.trema1/vswitchd/ovs-brcompatd, skipping
unable to convert "\xD4" from ASCII-8BIT to UTF-8 for vendor/openvswitch-1.2.2.trema1/vswitchd/ovs-vswitchd, skipping
unable to convert "\x95" from ASCII-8BIT to UTF-8 for vendor/phost/src/phost, skipping
unable to convert "\xEC" from ASCII-8BIT to UTF-8 for vendor/phost/src/cli, skipping
Installing ri documentation for trema-0.4.7
11 gems installed

ここから RubyGems 1.8.25 にダウングレードしてインストールさせてみます:

$ sudo gem uninstall rubygems-update
$ sudo gem install rubygems-update -v 1.8.25 Fetching: rubygems-update-1.8.25.gem (100%) Successfully installed rubygems-update-1.8.25 Parsing documentation for rubygems-update-1.8.25 Installing ri documentation for rubygems-update-1.8.25 Done installing documentation for rubygems-update after 0 seconds 1 gem installed
$ sudo update_rubygems RubyGems 1.8.25 installed == 1.8.25/ 2013-01-24 * 6 bug fixes:   * Added 11627 to setup bin_file location to protect against errors. Fixes #328 by ConradIrwin   * Specification#ruby_code didn't handle Requirement with multiple   * Fix error on creating a Version object with a frozen string.   * Fix incremental index updates   * Fix missing load_yaml in YAML-related requirement.rb code.   * Manually backport encoding-aware YAML gemspec ------------------------------------------------------------------------------ RubyGems installed the following executables: /usr/bin/gem1.9.1 $ sudo gem uninstall trema Remove executables: trema, trema-config in addition to the gem? [Yn] Y Removing trema Removing trema-config Successfully uninstalled trema-0.4.7 $ sudo gem install trema Fetching: trema-0.4.7.gem (100%) Building native extensions.  This could take a while... Successfully installed trema-0.4.7 1 gem installed Installing ri documentation for trema-0.4.7... Installing RDoc documentation for trema-0.4.7... $ trema version trema version 0.4.7

2014年6月 8日 (日)

◆ Trema 0.4.7インストール失敗とバージョン指定インストール

[2014/6/8 16:30追記:
インストール失敗はすでに https://github.com/trema/trema/issues/366 にて報告されている現象でした。
ここに記載があるように事前に RubyGems を更新する作業をしたところ Trema 0.4.7 のインストールに成功しました。以下の手順になります:
  sudo gem install update-rubygems
  sudo update_rubygems
  sudo gem install trema
2014/6/9 00:15追記:

  上記の方法で成功はしたものの Warning がやたら出たりしてまだ少し変。RubyGems のバージョンが 2.2.2 になっていたので、以下のようにして 1.8.x のままにした方が良さそう。

 
sudo gem install update-rubygems -v 1.8.25
あとでもう少し確認します。

]
[2014/06/11 追記:
インストール方法を整理して 「
◆【改訂】OpenFlow フレームワーク Trema 0.4 のインストール手順【0.4.7対応】」を書きました。
]

パソコンの入れ替えがあって OpenFlow フレームワーク Trema のテスト環境を新規に作ろうとしたら Trema のインストールに失敗してはまったのでここに記録を残しておきます。
最新の 0.4.7 でだめなら一つ前の 0.4.6 を入れられないかと調べてみたら gem コマンドには --version オプションがあってバージョン指定ができるんですね。
0.4.6 にしたら無事インストール成功しました。

■現象発生環境
  OS: Lubuntu 13.04 Desktop (i386, 32bit)
  Trema: 0.4.7 … インストール失敗
            0.4.6 … インストール成功

■現象発生時のインストール画面とエラー箇所

以下の通り:

$ sudo gem install trema
Fetching: bundler-1.6.2.gem (100%)
Fetching: gli-2.10.0.gem (100%)
Fetching: Platform-0.4.0.gem (100%)
Fetching: open4-1.3.4.gem (100%)
Fetching: POpen4-0.1.4.gem (100%)
Fetching: rake-10.3.2.gem (100%)
Fetching: paper_house-0.6.2.gem (100%)
Fetching: bindata-2.1.0.gem (100%)
Fetching: pio-0.7.0.gem (100%)
Fetching: json-1.8.1.gem (100%)
Building native extensions.  This could take a while...
Fetching: rdoc-4.1.1.gem (100%)
Depending on your version of ruby, you may need to install ruby rdoc/ri data:

<= 1.8.6 : unsupported
= 1.8.7 : gem install rdoc-data; rdoc-data --install
= 1.9.1 : gem install rdoc-data; rdoc-data --install
>= 1.9.2 : nothing to do! Yay!
Fetching: trema-0.4.7.gem (100%)
Building native extensions.  This could take a while...
ERROR: Error installing trema:
ERROR: Failed to build gem native extension.

"/usr/bin/ruby1.9.1" -rubygems /var/lib/gems/1.9.1/gems/rake-10.3.2/bin/rake RUBYARCHDIR=/var/lib/gems/1.9.1/gems/trema-0.4.7/ruby RUBYLIBDIR=/var/lib/gems/1.9.1/gems/trema-0.4.7/ruby
rake aborted!
Bundler::GemspecError: There was a RuntimeError while loading trema.gemspec:
can't modify frozen String from
  /var/lib/gems/1.9.1/gems/trema-0.4.7/trema.gemspec:7:in `block in <main>'

/var/lib/gems/1.9.1/gems/bundler-1.6.2/lib/bundler.rb:393:in `rescue in eval_gemspec'
/var/lib/gems/1.9.1/gems/bundler-1.6.2/lib/bundler.rb:382:in `eval_gemspec'
/var/lib/gems/1.9.1/gems/bundler-1.6.2/lib/bundler.rb:353:in `block in load_gemspec_uncached'
/var/lib/gems/1.9.1/gems/bundler-1.6.2/lib/bundler/shared_helpers.rb:52:in `chdir'
/var/lib/gems/1.9.1/gems/bundler-1.6.2/lib/bundler/shared_helpers.rb:52:in `chdir'
/var/lib/gems/1.9.1/gems/bundler-1.6.2/lib/bundler.rb:348:in `load_gemspec_uncached'
/var/lib/gems/1.9.1/gems/bundler-1.6.2/lib/bundler.rb:338:in `load_gemspec'
/var/lib/gems/1.9.1/gems/bundler-1.6.2/lib/bundler/gem_helper.rb:31:in `initialize'
/var/lib/gems/1.9.1/gems/bundler-1.6.2/lib/bundler/gem_helper.rb:13:in `new'
/var/lib/gems/1.9.1/gems/bundler-1.6.2/lib/bundler/gem_helper.rb:13:in `install_tasks'
/var/lib/gems/1.9.1/gems/bundler-1.6.2/lib/bundler/gem_tasks.rb:2:in `<top (required)>'
/var/lib/gems/1.9.1/gems/trema-0.4.7/Rakefile:760:in `<top (required)>'
(See full trace by running task with --trace)

Gem files will remain installed in /var/lib/gems/1.9.1/gems/trema-0.4.7 for inspection.
Results logged to /var/lib/gems/1.9.1/gems/trema-0.4.7/./gem_make.out

赤字で示したところがエラー発生個所。trema.gemspec の7行目で問題起きています。ソースコードから7行目を見てみると以下の通り:
  gem.version = Trema::VERSION

この Trema::VERSION はというと、ruby/trema/version.rb で定義されていました。0.4.7 で VERSION の記述のしかたがfreeze 指定に変更されていることが影響していそうです:
Trema 0.4.7 の場合の VERSION の記述:
  VERSION = '0.4.7'.freeze
Trema 0.4.6 の場合の VERSION の記述:
  VERSION = "0.4.6"

■暫定的な回避策
Trema 0.4.6 をインストールします。--version でバージョンを指定します。

  sudo gem install trema --version 0.4.6

以下の通りエラーなくインストールが完了しました:

$ sudo gem install trema --version 0.4.6
Fetching: gli-2.8.1.gem (100%)
Fetching: rake-10.1.1.gem (100%)
Fetching: paper_house-0.5.0.gem (100%)
Fetching: bindata-1.6.0.gem (100%)
Fetching: pio-0.3.0.gem (100%)
Fetching: rdoc-4.0.1.gem (100%)
Depending on your version of ruby, you may need to install ruby rdoc/ri data:

<= 1.8.6 : unsupported
= 1.8.7 : gem install rdoc-data; rdoc-data --install
= 1.9.1 : gem install rdoc-data; rdoc-data --install
>= 1.9.2 : nothing to do! Yay!
Fetching: trema-0.4.6.gem (100%)
Building native extensions.  This could take a while...
Successfully installed gli-2.8.1
Successfully installed rake-10.1.1
Successfully installed paper_house-0.5.0
Successfully installed bindata-1.6.0
Successfully installed pio-0.3.0
Successfully installed rdoc-4.0.1
Successfully installed trema-0.4.6
7 gems installed
Installing ri documentation for gli-2.8.1...
Installing ri documentation for rake-10.1.1...
Installing ri documentation for paper_house-0.5.0...
Installing ri documentation for bindata-1.6.0...
Installing ri documentation for pio-0.3.0...
Installing ri documentation for rdoc-4.0.1...
Installing ri documentation for trema-0.4.6...
Installing RDoc documentation for gli-2.8.1...
Installing RDoc documentation for rake-10.1.1...
Installing RDoc documentation for paper_house-0.5.0...
Installing RDoc documentation for bindata-1.6.0...
Installing RDoc documentation for pio-0.3.0...
Installing RDoc documentation for rdoc-4.0.1...
Installing RDoc documentation for trema-0.4.6...

2013年10月 6日 (日)

◆ OpenFlowフレームワーク Trema 0.4 のインストール

[2014年06月11日追記: Trema 0.4.7 からは、ここに記載の方法だけだとインストールエラーになります。改訂版の記事「◆【改訂】OpenFlow フレームワーク Trema 0.4 のインストール手順【0.4.7対応】」を書きました。]
[2013年10月12日追記。Trema作者様から Readme 修正のコメントいただきました!と、いうことで、今はhttps://github.com/trema/trema の「Getting Started」に従ってインストールすれば本記事に書いたエラーなくインストールできます。]

OpenFlow フレームワークの Trema のバージョンが 0.3.x から 0.4.x にあがっていたのでインストールしてみました。

0.4 では最新の環境への対応がされています。
対象OSには、Ubuntu 13.04 もサポートに入っています。
Rubyも「Ruby 1.8.7 or higher」と記載されていて、主流の 1.9 がサポートされています。

すんなりインストールできると思ったのですがドキュメント不備があるようでちょっと引っかかりました。

■確認環境
・OS: Lubuntu 13.04 Desktop (i386)
    (2013年10月5日にソフトウェアアップデート済)
・Trema: 0.4.3

■インストール

https://github.com/trema/trema の「Getting Started」に従ってインストールしていきます。

1. 「1.Install the prerequisites at the command prompt:」に従い以下のコマンドを実行:

sudo apt-get install gcc make ruby rubygems ruby-dev libpcap-dev libsqlite3-dev libglib2.0-dev

2. 「Install Trema at the command prompt:」に従い以下のコマンドを実行:

sudo gem install trema

すると、エラーになりました:

$ sudo gem install trema
Fetching: bundler-1.3.5.gem (100%)
Fetching: gli-2.8.0.gem (100%)
Fetching: Platform-0.4.0.gem (100%)
Fetching: open4-1.3.0.gem (100%)
Fetching: POpen4-0.1.4.gem (100%)
Fetching: rake-10.1.0.gem (100%)
Fetching: paper_house-0.4.0.gem (100%)
Fetching: bindata-1.6.0.gem (100%)
Fetching: pio-0.2.4.gem (100%)
Fetching: json-1.8.0.gem (100%)
Building native extensions.  This could take a while...
Fetching: rdoc-4.0.1.gem (100%)
Depending on your version of ruby, you may need to install ruby rdoc/ri data:

<= 1.8.6 : unsupported
= 1.8.7 : gem install rdoc-data; rdoc-data --install
= 1.9.1 : gem install rdoc-data; rdoc-data --install
>= 1.9.2 : nothing to do! Yay!
Fetching: trema-0.4.3.gem (100%)
Building native extensions.  This could take a while...
ERROR:  Error installing trema:
ERROR: Failed to build gem native extension
.

        "/usr/bin/ruby1.9.1" -rubygems /var/lib/gems/1.9.1/gems/rake-10.1.0/bin/rake RUBYARCHDIR=/var/lib/gems/1.9.1/gems/trema-0.4.3/ruby RUBYLIBDIR=/var/lib/gems/1.9.1/gems/trema-0.4.3/ruby
rake aborted!
There was a Errno::ENOENT while loading trema.gemspec:
No such file or directory - git ls-files from

  /var/lib/gems/1.9.1/gems/trema-0.4.3/trema.gemspec:20:in ``'
/var/lib/gems/1.9.1/gems/bundler-1.3.5/lib/bundler.rb:355:in `rescue in eval_gemspec'
/var/lib/gems/1.9.1/gems/bundler-1.3.5/lib/bundler.rb:344:in `eval_gemspec'
/var/lib/gems/1.9.1/gems/bundler-1.3.5/lib/bundler.rb:324:in `block in load_gemspec_uncached'
/var/lib/gems/1.9.1/gems/bundler-1.3.5/lib/bundler.rb:319:in `chdir'
/var/lib/gems/1.9.1/gems/bundler-1.3.5/lib/bundler.rb:319:in `load_gemspec_uncached'
/var/lib/gems/1.9.1/gems/bundler-1.3.5/lib/bundler.rb:309:in `load_gemspec'
/var/lib/gems/1.9.1/gems/bundler-1.3.5/lib/bundler/gem_helper.rb:32:in `initialize'
/var/lib/gems/1.9.1/gems/bundler-1.3.5/lib/bundler/gem_helper.rb:14:in `new'
/var/lib/gems/1.9.1/gems/bundler-1.3.5/lib/bundler/gem_helper.rb:14:in `install_tasks'
/var/lib/gems/1.9.1/gems/bundler-1.3.5/lib/bundler/gem_tasks.rb:2:in `<top (required)>'
/var/lib/gems/1.9.1/gems/trema-0.4.3/Rakefile:747:in `<top (required)>'
(See full trace by running task with --trace)


Gem files will remain installed in /var/lib/gems/1.9.1/gems/trema-0.4.3 for inspection.
Results logged to /var/lib/gems/1.9.1/gems/trema-0.4.3/./gem_make.out

エラーメッセージからすると trema のインストーラの中で git を使っているようです。そこで、git を追加することにします。

先の 1. の前提のインストールを以下のように git を追加して実行しました:

sudo apt-get install git gcc make ruby rubygems ruby-dev libpcap-dev libsqlite3-dev libglib2.0-dev

その後に、再度、trema のインストールを実施するとインストールが成功しました:

$ sudo gem install trema
Building native extensions.  This could take a while...
Successfully installed trema-0.4.3
1 gem installed
Installing ri documentation for trema-0.4.3...
Installing RDoc documentation for trema-0.4.3...

3.バージョン確認

では、バージョン確認してみます:

$ trema --version
trema version 0.4.3

無事 Trema 0.4.3 がインストールできました。

2013年5月12日 (日)

◆ 【TremaでOpenFlowプログラミング】VLAN ID コンバータ

OpenFlow フレームワーク Trema を使って OpenFlow スイッチで VLAN ID を変換する機能を実装してみました。
テスト用の仮想ネットワークは、前回の記事で作った共通ファイル vnetcommon.sh を利用して Open vSwitch を VLAN スイッチと OpenFlow スイッチの両方で使った構成としています。

使用した環境: (前回の記事と同じです)
  OS: Lubuntu 12.04 Desktop (i386) (※ 13.04 ではありません)
  Trema: Trema 0.3.19

補足:
Open vSwitch のインストール方法は以下の記事の「■ステップ3. OpenVSwitch のインストール」を参照ください:
記事: 「2万円で OpenFlow スイッチを自作しよう」
http://ranosgrant.cocolog-nifty.com/blog/2013/02/2-openflow-4b98.html

■今回の仮想ネットワークの構成図
こんなストーリーを想定しています:
・2つの拠点があって、拠点ごとに別々に VLAN ID を管理していた。
・拠点1の VLAN ID 10, 20 の L2 ネットワークを拠点2 にも延ばす必要が生じた。
・しかし、拠点2 では VLAN ID 10, 20 は使えず、代わりに 拠点2 では LAN ID 30, 40 を割り当て、拠点間で VLAN ID を変換することにした。
・拠点1 の VLAN ID: 10 と 拠点2 の VLAN ID: 30 を接続する。
・拠点1 の VLAN ID: 20 と 拠点2 の VLAN ID: 40 を接続する。

Photo
■ 構成の確認
後述するシェルスクリプトで仮想ネットワークを作成したら OpenFlow ポートの1番が拠点1側、ポートの2番が拠点2側との接続となっていることを確認します。

$ sudo ovs-ofctl show ofs1
OFPT_FEATURES_REPLY (xid=0x1): ver:0x1, dpid:0000a67c7e48d940
n_tables:255, n_buffers:256
features: capabilities:0xc7, actions:0xfff
 1(vlinkSW1-1): addr:c6:9c:ee:c6:c9:15
     config:	 0
     state:	 0
     current:	 10GB-FD COPPER
 2(vlinkSW2-1): addr:46:d6:29:cd:8d:3f
     config:	 0
     state:	 0
     current:	 10GB-FD COPPER
 LOCAL(ofs1): addr:a6:7c:7e:48:d9:40
     config:	 PORT_DOWN
     state:	 LINK_DOWN
OFPT_GET_CONFIG_REPLY (xid=0x3): frags=normal miss_send_len=0

■ OpenFlow コントローラ
OpenFlow フレームワーク Trema を用いて VLAN ID コンバータを実装しています。ソースコードは以下:

class VLANIDConverter < Controller
  @conversion_table

  def start
    @convesion_table = [
      [ { :port => 1, :tag => 10 }, { :port => 2, :tag => 30 } ],
      [ { :port => 1, :tag => 20 }, { :port => 2, :tag => 40 } ]
    ]
  end

  def switch_ready( datapath_id )
    puts "switch #{ datapath_id.to_hex } is connected."
    @convesion_table.each do | each |
      flow_mod( datapath_id, each[0], each[1] )
      flow_mod( datapath_id, each[1], each[0] )
    end
    send_flow_mod_add( datapath_id, :priority => 100, :actions => [ ] )
  end

  def flow_mod( datapath_id, e1, e2 )
    send_flow_mod_add(
      datapath_id,
      :match => Match.new( :in_port => e1[ :port ] , :dl_vlan => e1[ :tag ] ),
      :actions => [
        SetVlanVid.new( e2[ :tag ] ),
        SendOutPort.new( e2[ :port ] )
      ]
    )
  end
end

やっていることを文字にすると以下の通り:
・ポート1(拠点1) から来た VLAN ID: 10 のパケットなら、VLAN ID を 30 にすげかえてポート2(拠点2)へ出力。
・ポート2(拠点2) から来た VLAN ID: 30 のパケットなら、VLAN ID を 10 にすげかえてポート1(拠点1)へ出力。
・ポート1(拠点1) から来た VLAN ID: 20 のパケットなら、VLAN ID を 40 にすげかえてポート2(拠点2)へ出力。
・ポート2(拠点2) から来た VLAN ID: 40 のパケットなら、VLAN ID を 20 にすげかえてポート1(拠点1)へ出力。
・上記に当てはまらないパケットが来たら破棄する。(すべてのパケットに合致するフローエントリですが優先度 priority を他のフローエントリより下げることで「上記に当てはまらない」という動きにしています。)

ソース中の @conversion_table を変更すれば異なる変換ルールにできます。

コントローラは仮想ネットワークを作成した後に、以下で起動します:
  trema run コントローラのソースコードのファイル

■ OpenFlow スイッチのフローテーブルの確認

$ sudo ovs-ofctl dump-flows ofs1
NXST_FLOW reply (xid=0x4):
 cookie=0x4, duration=22.022s, table=0, n_packets=0, n_bytes=0, priority=65535,in_port=2,dl_vlan=40 actions=mod_vlan_vid:20,output:1
 cookie=0x1, duration=22.022s, table=0, n_packets=0, n_bytes=0, priority=65535,in_port=1,dl_vlan=10 actions=mod_vlan_vid:30,output:2
 cookie=0x3, duration=22.022s, table=0, n_packets=0, n_bytes=0, priority=65535,in_port=1,dl_vlan=20 actions=mod_vlan_vid:40,output:2
 cookie=0x2, duration=22.022s, table=0, n_packets=0, n_bytes=0, priority=65535,in_port=2,dl_vlan=30 actions=mod_vlan_vid:10,output:1
 cookie=0x5, duration=22.022s, table=0, n_packets=0, n_bytes=0, priority=100 actions=drop

■ 動作確認
vhost1(192.168.0.1, 拠点1, VLAN ID: 10) から ping で vhost3(192.168.0.3, 拠点2, VLAN ID: 30) とのみ通信できることを確認:

$ sudo ip netns exec vhost1 ping -c2 192.168.0.2
PING 192.168.0.2 (192.168.0.2) 56(84) bytes of data.
From 192.168.0.1 icmp_seq=1 Destination Host Unreachable
From 192.168.0.1 icmp_seq=2 Destination Host Unreachable

--- 192.168.0.2 ping statistics ---
2 packets transmitted, 0 received, +2 errors, 100% packet loss, time 1008ms
pipe 2

$ sudo ip netns exec vhost1 ping -c2 192.168.0.3 PING 192.168.0.3 (192.168.0.3) 56(84) bytes of data. 64 bytes from 192.168.0.3: icmp_req=1 ttl=64 time=1.13 ms 64 bytes from 192.168.0.3: icmp_req=2 ttl=64 time=0.160 ms --- 192.168.0.3 ping statistics --- 2 packets transmitted, 2 received, 0% packet loss, time 1001ms rtt min/avg/max/mdev = 0.160/0.649/1.138/0.489 ms $ sudo ip netns exec vhost1 ping -c2 192.168.0.4 PING 192.168.0.4 (192.168.0.4) 56(84) bytes of data. From 192.168.0.1 icmp_seq=1 Destination Host Unreachable From 192.168.0.1 icmp_seq=2 Destination Host Unreachable --- 192.168.0.4 ping statistics --- 2 packets transmitted, 0 received, +2 errors, 100% packet loss, time 1007ms pipe 2

vhost2(192.168.0.2, 拠点1, VLAN ID: 20) から ping で vhost4(192.168.0.4, 拠点2, VLAN ID: 40) とのみ通信できることを確認:

$ sudo ip netns exec vhost2 ping -c2 192.168.0.1
PING 192.168.0.1 (192.168.0.1) 56(84) bytes of data.
From 192.168.0.2 icmp_seq=1 Destination Host Unreachable
From 192.168.0.2 icmp_seq=2 Destination Host Unreachable

--- 192.168.0.1 ping statistics ---
2 packets transmitted, 0 received, +2 errors, 100% packet loss, time 1008ms
pipe 2

$ sudo ip netns exec vhost2 ping -c2 192.168.0.3
PING 192.168.0.3 (192.168.0.3) 56(84) bytes of data.
From 192.168.0.2 icmp_seq=1 Destination Host Unreachable
From 192.168.0.2 icmp_seq=2 Destination Host Unreachable

--- 192.168.0.3 ping statistics ---
2 packets transmitted, 0 received, +2 errors, 100% packet loss, time 1008ms
pipe 2

$ sudo ip netns exec vhost2 ping -c2 192.168.0.4
PING 192.168.0.4 (192.168.0.4) 56(84) bytes of data.
64 bytes from 192.168.0.4: icmp_req=1 ttl=64 time=1.05 ms
64 bytes from 192.168.0.4: icmp_req=2 ttl=64 time=0.153 ms

--- 192.168.0.4 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 0.153/0.605/1.058/0.453 ms

■今回の仮想ネットワークの作成・削除シェルスクリプト
引数 delete で削除、add で追加です。
共通ファイル vnetcommon.sh がカレントディレクトリにおいてあることを前提としています。

#!/bin/sh
. ./vnetcommon.sh

delete() {
    deleteVSwitch "ofs1" "vswitch1" "vswitch2"
    deleteVLink "vlinkSW1" "vlinkSW2"
    deleteVHost "vhost1" "vhost2" "vhost3" "vhost4"
    deleteVLink "vlink1" "vlink2" "vlink3" "vlink4"
}

add() {
    addOFSwitch "ofs1"
    addVSwitch "vswitch1"
    addVSwitch "vswitch2"
    connectVSwitchToVSwitch "vswitch1" "vlinkSW1" "ofs1"
    connectVSwitchToVSwitch "vswitch2" "vlinkSW2" "ofs1"

    addVHost "vhost1"
    connectVHostToVSwitch "vhost1" "vlink1" "vswitch1" tag=10
    vhostExec "vhost1" ifconfig "vlink1" "192.168.0.1"

    addVHost "vhost2"
    connectVHostToVSwitch "vhost2" "vlink2" "vswitch1" tag=20
    vhostExec "vhost2" ifconfig "vlink2" "192.168.0.2"

    addVHost "vhost3"
    connectVHostToVSwitch "vhost3" "vlink3" "vswitch2" tag=30
    vhostExec "vhost3" ifconfig "vlink3" "192.168.0.3"

    addVHost "vhost4"
    connectVHostToVSwitch "vhost4" "vlink4" "vswitch2" tag=40
    vhostExec "vhost4" ifconfig "vlink4" "192.168.0.4"
}

case "$1" in
add)
    sudo sh -c exit
    set -x
    add
    ;;
delete)
    sudo sh -c exit
    delete > /dev/null 2>&1
    ;;
*)
    echo "usage: $0 { add | delete }"
esac

vnetcommon.sh のコードは、前回記事の末尾を参照ください。

2013年5月11日 (土)

◆ Linux のネットワークネームスペース機能と Open vSwitch で仮想ネットワーク(OpenFlowスイッチとTrema)

今回は、Open vSwitch を OpenFlow スイッチとして使ってみました。OpenFlow コントローラは、OpenFlow フレームワーク Trema を使い OpenFlow スイッチをリピーターハブとして動かしています。

今回の構成程度だと、Trema の仮想ネットワーク機能を使った方がお手軽です。ですが、このシェルスクリプトを用いた方法では今までの記事で書いてきたように VLAN や仮想ルータ、仮想スイッチを組み合わせることができるのでTrema の仮想ネットワークよりも色々なネットワーク構成が可能です。

使用した環境:
  OS: Lubuntu 12.04 Desktop (i386) (※ 13.04 ではありません)
  Trema: Trema 0.3.19

■今回作成した仮想ネットワークの構成図
2つの OpenFlow スイッチにそれぞれ1つ仮想ホストをつないだだけの簡単な構成です。
図には書いていませんが、各 OpenFlow スイッチはローカルループバック 127.0.0.1 経由で OpenFlow フレームワーク Trema と接続します。
Photo_5

仮想ホストで使うインターフェース名は、仮想スイッチ側のインターフェース名と同じ名前を使うようにしました。仮想ホスト側は独自の名前空間なのでこういうことができます。

■OpenFlowコントローラ

OpenFlow フレームワーク Trema で、OpenFlow スイッチをリピーターハブとして動作させるコントローラを動かします。ソースコードは以下:

class RepetaerHub < Controller
  def switch_ready( datapath_id )
    puts "switch #{ datapath_id.to_hex } is connected."
    send_flow_mod_add( datapath_id, :actions => SendOutPort.new( OFPP_FLOOD ))
  end
end

コントローラは仮想ネットワークを作成した後に、以下で起動します:
  trema run コントローラのソースコードのファイル

■構成の確認
OpenFlow ポートの1番が OpenFlow スイッチ間の接続、2番が仮想マシンとの接続となっていることを確認します。

vswitch1 の OpenFlow スイッチとしての情報:

$ sudo ovs-ofctl show vswitch1
OFPT_FEATURES_REPLY (xid=0x1): ver:0x1, dpid:0000000000000001
n_tables:255, n_buffers:256
features: capabilities:0xc7, actions:0xfff
1(vlinkSW1-0): addr:9a:51:e2:54:c1:69
     config:     0
     state:      0
     current:    10GB-FD COPPER
2(vlink1): addr:86:98:54:0d:32:08
     config:     0
     state:      0
     current:    10GB-FD COPPER
LOCAL(vswitch1): addr:66:40:c5:44:72:41
     config:     PORT_DOWN
     state:      LINK_DOWN
OFPT_GET_CONFIG_REPLY (xid=0x3): frags=normal miss_send_len=0

vswitch2 の OpenFlow スイッチとしての情報:

$ sudo ovs-ofctl show vswitch2
OFPT_FEATURES_REPLY (xid=0x1): ver:0x1, dpid:00008a25a0cdf842
n_tables:255, n_buffers:256
features: capabilities:0xc7, actions:0xfff
1(vlinkSW1-1): addr:1a:71:45:3a:ec:32
     config:     0
     state:      0
     current:    10GB-FD COPPER
2(vlink2): addr:7a:f8:28:27:67:f7
     config:     0
     state:      0
     current:    10GB-FD COPPER
LOCAL(vswitch2): addr:8a:25:a0:cd:f8:42
     config:     PORT_DOWN
     state:      LINK_DOWN
OFPT_GET_CONFIG_REPLY (xid=0x3): frags=normal miss_send_len=0

■ OpenFlow スイッチのフローテーブルの確認

vswitch1 のフローテーブル:

$ sudo ovs-ofctl dump-flows vswitch1
NXST_FLOW reply (xid=0x4):
cookie=0x1, duration=113.232s, table=0, n_packets=0, n_bytes=0, priority=65535 actions=FLOOD

vswitch2 のフローテーブル:

$ sudo ovs-ofctl dump-flows vswitch2
NXST_FLOW reply (xid=0x4):
cookie=0x1, duration=121.102s, table=0, n_packets=0, n_bytes=0, priority=65535 actions=FLOOD

■動作確認
vhost1 から ping で vhost2(192.168.0.2)へ通信できることを確認:

$ sudo ip netns exec vhost1 ping -c 3 192.168.0.2
PING 192.168.0.2 (192.168.0.2) 56(84) bytes of data.
64 bytes from 192.168.0.2: icmp_req=1 ttl=64 time=3.12 ms
64 bytes from 192.168.0.2: icmp_req=2 ttl=64 time=0.196 ms
64 bytes from 192.168.0.2: icmp_req=3 ttl=64 time=0.096 ms

--- 192.168.0.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2000ms
rtt min/avg/max/mdev = 0.096/1.140/3.128/1.406 ms

■今回の仮想ネットワークの作成・削除シェルスクリプト
引数 delete で削除、add で追加です。
今までのシェルスクリプトの経験から関数化して他でも使える共通部分を別ファイル vnetcommon.sh に分離しました。共通部分のファイル vnetcommon.sh はカレントディレクトリにおいてあることを前提としています。

#!/bin/sh
. ./vnetcommon.sh

delete() {
    deleteVSwitch "vswitch1" "vswitch2"
    deleteVLink "vlinkSW1"
    deleteVHost "vhost1" "vhost2"
    deleteVLink "vlink1" "vlink2"
}

add() {
    addOFSwitch "vswitch1" "0x1"
    addOFSwitch "vswitch2"
    connectVSwitchToVSwitch "vswitch1" "vlinkSW1" "vswitch2"

    addVHost "vhost1"
    connectVHostToVSwitch "vhost1" "vlink1" "vswitch1"
    vhostExec "vhost1" ifconfig "vlink1" "192.168.0.1"

    addVHost "vhost2"
    connectVHostToVSwitch "vhost2" "vlink2" "vswitch2"
    vhostExec "vhost2" ifconfig "vlink2" "192.168.0.2"
}

case "$1" in
add)
    sudo sh -c exit
    set -x
    add
    ;;
delete)
    sudo sh -c exit
    delete > /dev/null 2>&1
    ;;
*)
    echo "usage: $0 { add | delete }"
esac

上記で、「sudo sh -c exit」と一見無意味なことをしているのは事前に sudo のパスワード入力を済ませたかったからです。

共通ファイル vnetcommon.sh の内容は以下:

deleteVHost() {
   local host
   for host in "$@"; do
       sudo ip netns delete "$host"
   done
}

deleteVSwitch() {
   local switch
   for switch in "$@"; do
       sudo ovs-vsctl del-br "$switch"
   done
}

deleteVLink() {
   local link
   for link in "$@"; do
       sudo ip link delete "$link"
       sudo ip link delete "${link}-0"
       sudo ip link delete "${link}-1"
   done
}

addVHost() {
    local host="$1"
    sudo ip netns add "$host"
    sudo ip netns exec "$host" ifconfig lo 127.0.0.1
}

addVSwitch() {
    local switch="$1"
    sudo ovs-vsctl add-br "$switch"
}

setupVSwitchAsOpenFlowSwitch() {
    local switch="$1"
    local datapath_id="$2"

    if [ "$datapath_id" != "" ]; then
        sudo ovs-vsctl set bridge "$switch" other-config:datapath-id=`printf \"%016x\" $datapath_id`
    fi
    sudo ovs-vsctl set-fail-mode "$switch" secure
    sudo ovs-vsctl set-controller "$switch" tcp:127.0.0.1 -- \
        set controller "$switch" connection-mode=out-of-band -- \
        set controller "$switch" inactivity-probe=180 -- \
        set controller "$switch" controller-rate-limit=40000 -- \
        set controller "$switch" controller-burst-limit=20000
}

addOFSwitch() {
    addVSwitch "$1"
    setupVSwitchAsOpenFlowSwitch "$1" "$2"
}

connectVSwitchToVSwitch() {
    local switch1="$1"
    local link="$2"
    local switch2="$3"

    sudo ip link add name "${link}-0" type veth peer name "${link}-1"

    sudo ip link set "${link}-0" up
    sudo ovs-vsctl add-port "$switch1" "${link}-0"

    sudo ip link set "${link}-1" up
    sudo ovs-vsctl add-port "$switch2" "${link}-1"
}

connectVHostToVSwitch() {
    local host="$1"
    local link="$2"
    local switch="$3"
    local opt="$4"

    sudo ip link add name "${link}" type veth peer name "${link}-1"

    sudo ip link set "${link}-1" netns "$host"
    sudo ip netns exec "$host" ip link set "${link}-1" name "${link}"
    sudo ip netns exec "$host" ifconfig "${link}" up

    sudo ip link set "${link}" up
    sudo ovs-vsctl add-port "$switch" "${link}" $opt
}

vhostExec() {
    sudo ip netns exec "$@"
}

OpenFlow スイッチとしてのオプションは、Trema が内部的に使っているユーザースペース版 Open vSwitch の起動オプションを参考にしています。
『OpenFlow実践入門』(初版 P.195) 「仮想スイッチ」に記載されているコマンドラインオプションを参考にさせていただきました。

addOFSwitch 関数の第2引数は datapath_id ですが、省略可能です。

2013年5月 8日 (水)

◆ OpenFlow 1.0.0 メッセージと Trema API(Ruby) との対応表

OpenFlow 1.0.0 メッセージとOpenFlowフレームワーク Trema の Ruby API との対応をまとめてみました。OpenFlow 1.0.0 の仕様を読んでから Trema で実装したいけどどうすればできるんだろう、とよく悩むことがあったので整理しました。
Trema のメソッド名、クラス名の箇所には Trema API のページへのリンクを埋め込んであります。



本記事の作成においては、http://rubydoc.info/github/trema/trema/master/file/README.md にて gem version 0.3.19 と表示され、かつ、同ページ下部に「Generated on Wed May  1 04:44:45 2013」と出ているものを参照しています。
今後のエンハンスで変更されるかもしれません。

2013年4月 9日 (火)

Trema のコントローラと仮想ネットワークをそれぞれ別々に動かす

[2013/04/13 追記: 間違い訂正しました。誤った記載箇所を取消線で消して青字で新しく書き直しています。試そうとしてうまく動かなかった方、すみません m(_ _)m]
[2013/04/13 22:00 追記: 書き直し箇所でまた訂正 orz。誤: class Empty.rb、正: class Empty]

OpenFlow フレームワーク Trema を、コントローラと仮想ネットワークそれぞれ別々に動かすと便利そうだと思い調べてみたらできたのでやり方を書いておきます。

確認は、Trema 0.3.16 と 0.3.19 でしています。

■やりたいこと
Trema はコントローラの起動・停止で、仮想ネットワークも同時に起動・停止します。
それをあえて、Trema コントローラと Trema の仮想ネットワークを別々に起動・停止したい。

■背景
Trema コントローラの開発中は頻繁にバグで停止してしまうことがあります。
Trema の仮想ネットワーク設定ファイルのネットワークネームスペースの機能は非常に便利ですが、そのネットワークネームスペースでサーバープロセスを動かしているときは、Trema コントローラが停止しても動き続けて欲しい(再起動が面倒なので)。また、ネットワークネームスペースで動いているプロセスが残っていると Trema 終了時にネットワークネームスペースの削除に失敗してしまい、次の起動がおかしくなることもあります。

過去に書いた関連記事:
「Trema の仮想ネットワーク機能には『OpenFlow実践入門』に書いてない強力な機能がありました。」
「ネットワークネームスペース機能使用時の Trema 停止エラー軽減方法」

■解決案
2つOSを用意して、片方はコントローラ用、もう片方は仮想ネットワーク用と役割を分けて用いる。

■コントローラを動かす側
何もする必要はありません。
普通に trema run でコントローラを起動するだけです。

■仮想ネットワークを動かす側
(1)何もしないコントローラを用意する。
(1)空のファイルを用意してコントローラとして用いる。
(2)仮想ネットワーク設定ファイルにおいて、vswitch に ip アドレスとしてコントローラを動かす側の OS の IP アドレスを指定する。

■仮想ネットワーク側の例
コントローラ側が IPアドレス 192.168.83.156 とします。

・仮想ネットワーク設定ファイル vnet.conf

  vswitch("swtich0") {
    datapath_id "0x1"
    ip "192.168.83.156"
  }
  vhost("host1") {
    ip "192.168.0.1"
    netmask "255.255.255.0"
  }
  vhost("host2") {
    ip "192.168.0.2"
    netmask "255.255.255.0"
  }
  link "host1", "switch0"
  link "host2", "switch0"

・何もしないコントローラ empty.rb

   class Empty < Controller
   end

・空のコントローラ empty.rb
  空ファイルは以下のようにすれば作れます:

   touch empty.rb        # (empty.rb が存在しない場合のみ使える方法)

     または

   cp /dev/null emtpy.rb

・起動方法

   trema run empty.rb -c vnet.conf

trema dump_flows や trema send_packet など仮想ネットワークに対する操作は普通に実行できます。

■おまけ

vswitch を複数書くときにそれぞれに同じ IP アドレスを書くのは保守性に欠けます。仮想ネットワーク設定ファイルが実は ruby のプログラムであることを利用して関数化してみてはどうでしょう。

  def config_vswitch( name, dpid )
    vswitch("#{ name }") {
      datapath_id "#{ dpid }"
      ip "192.168.83.156"
    }
  end
  config_vswitch "switch1", "0x1"
  config_vswitch "switch2", "0x2"
  config_vswitch "switch3", "0x3"

もっと?

  def config_vswitch( name, dpid )
    vswitch("#{ name }") {
      datapath_id "#{ dpid }"
      ip "192.168.83.156"
    }
  end
  1.upto(3) do | each |
    config_vswitch "switch#{ each }", "#{ each }"
  end

2013年4月 7日 (日)

Trema を改造して仮想ネットワーク機能にポート番号指定を追加

[ 2013/04/07 21:00 追記: すみません。公開後にバグを見つけたのでパッチファイル trema.diff を差し替えました。詳細は本記事の最後を見てください ]

OpenFlow フレームワーク Trema (0.3.19)を改造してみました。
改造内容は、仮想ネットワーク機能において仮想スイッチで使われるポート番号を指定できる機能の追加です。実装を楽にするため制約をつけてはいますが、それでも十分使えると思います。

ちなみに、GitHub 上の Trema の Issue リストには、ポート番号指定について以下があがっていて現時点ではまだクローズしていない状態です:
https://github.com/trema/trema/issues/52
https://github.com/trema/trema/issues/286
ここで議論されているポート番号の表記方法をパクりました(^^;

動作確認は、Lubuntu 12.04 Desktop (i386) で確認しています。

■ポート番号を指定できると何が嬉しいか?

当たり前のことですが、どのポートに何がつながっているかの構成が決まっていて、その構成に依存してなにかしたいときにポート番号指定が必要です。
たとえば、ロードバランサをOpenFlow で作ることを考えると、どのポートにサーバがつながっているのか分かっていないと振り分け先を決めるのに困ります。

また、書籍『OpenFlow実践入門』「第7章 インテリジェントなパッチパネル」(初版 P.105)はポート番号を利用した例ですがテストには実機を必要としています。ポート番号指定ができれば仮想ネットワーク機能だけで試すことができます。

書籍『OpenFlow実践入門』(初版)の出版後にサポートされた機能でポート番号を指定して port_up, port_down する機能を使うにも仮想ネットワーク設定ファイルでポート番号が指定できれば便利です。
以下が port_up/port_down についての Trema プロジェクトの Issue:
https://github.com/trema/trema/issues/253

■使い方

仮想ネットワーク設定ファイルにて link 文でスイッチ名を指定するときに名前の後ろに「コロン+OpenFlowでのポート番号」を追加します。
以下に使用例を示します。赤字がポート番号指定箇所です:

例:

  vswitch("sw1") {
    datapath_id "1"
  }
  vhost("host1") {
    ip "192.168.0.1"
    netmask "255.255.255.0"
  }
  vhost("host2") {
    ip "192.168.0.2"
    netmask "255.255.255.0"
  }
  link "sw1:1","host1"
  link "sw1:2","host2"

制約:
  1) 仮想スイッチ名、仮想ホスト名、ネットワークネームスペース名にコロンを使わないこと。
  2) 指定するポートの番号は各スイッチごとに以下を守ること:
   a) 指定するポート番号のうち一番小さい番号は 1 であること。
   b) 指定するポート番号に抜けがないこと。

  NG: ポート番号 2を飛ばして 3 を使っているのでダメ
    link "sw1:1", "host1"
    link "sw1:3", "host2"
  NG: 一番小さいポート番号が 1 ではないのでダメ
    link "sw1:2", "host1"
    link "sw1:3", "host2"
  OK: ポート番号は 1,2, 3, ... と昇順で出現する必要はありません。
    link "sw1:2", "host1"
    link "sw1:1", "host2"
  OK: ポート番号指定の有り無しが混在しても構いません。
    link "sw1:1", "host1"
    link "sw1", "host2"

■改造版 Trema の作り方

Trema でソースからビルドする方法についての説明は省きます。書籍『OpenFlow実践入門』(初版  P.70)などを参照ください。

今回改造した箇所のパッチファイルは下記においておきます:
 http://ranosgrant.cocolog-nifty.com/trema-port/trema.diff

改造版を作る上での手順の違いは、ソースからビルドする手順において、./build.rb を実行する前にこのパッチをあてる手順を追加で実施するだけです。

wget http://ranosgrant.cocolog-nifty.com/trema-port/trema.diff
patch -p0 < trema.diff
./build.rb

パッチ作成に使った Trema は 2013年4月5日頃のものです。
バージョンは、trema version 0.3.19

本改造版 Trema が既存機能に悪さしていないことは Trema が提供する cruise.rb を実行し全テストケースがパスしたことで確認してあります(ただし、spec を一部修正してあります)。
[ 2013/04/07 追記: パスしたのですがバグありました。過信は禁物ですね ]

■改造版 Trema の起動方法

ソースからビルドした Trema はビルド環境に置いたまま使います。

例: ソースのありかが /home/ranosgrant/TREMA-git/trema/ の時にバージョン表示

 /home/ranosgrant/TREMA-git/trema/trema --version

または、

 cd /home/ranosgrant/TREMA-git/trema/
 ./trema --version

Ubuntu や Lubuntu のログインシェルのデフォルトである bash であればコマンドのエイリアスが使えるのでこれを使うと便利です。

例: bash で、改造版 Trema を mtrema というコマンド名で扱うための設定例
  alias mtrema=/home/ranosgrant/TREMA-git/trema/trema

これでパス名ではなく、
  mtrema --version
と呼び出せます。
.bashrc などを使って自動で alias を設定するようにしておくとよいでしょう。

■ソース改造箇所について少し説明

(1) Open vSwtich 側
vendor/openvswitch-1.2.2.trema1/tests/test-openflowd.c

  Trema が仮想スイッチとして利用している Open vSwitch (ovs-openflowd プロセス)の改造をしています。

  OpenFlow ポートとして使用する Linux のネットワークインターフェース名は、起動時のコマンドラインで --ports オプションを使ってコンマで区切って指定します。
こんな感じです: --ports=trema1-0,trema2-0,trema0-0
(trema が動いている時に ifconfig コマンドでみれば分かるように trema が使うネットワークインタフェース名は trema#-0, trema#-1 の形式です。ソースファイル ruby/trema/link.rb の initialize メソッドで名前を作っています。)
  --ports で指定された値を解析する parse_ports 関数では1つ1つ取り出したインターフェース名をハッシュに格納しています。引数で指定されたインターフェース名に対して処理をする箇所ではこのハッシュから取り出しながら処理をしています。ハッシュを使っているので取り出し順は登録順とは異なった順番になります。

  そこで、ハッシュを使わない方式に変更して --ports で指定した順に登録処理が行われるように変更しました。これで、--ports に書いた順にポート番号が1から順にふられるようになりました。

test-openflowd.c への変更分だけ抜き出した修正パッチはこちら:
http://ranosgrant.cocolog-nifty.com/trema-port/openvswitch-1.2.2-test-openflowd.diff

Trema では Open  vSwitch のソースファイルを tar.gz で固めた形で提供しているので tar.gz を展開した後にこのパッチが適用されるように Rakefile に変更を入れました。

(2) Trema 本体側

ruby/trema/dsl/parser.rb

  仮想ネットワーク設定ファイル内の link 指定にもとづいて生成した Linux のネットワークインターフェースの名前を仮想スイッチのオブジェクトに登録する処理をポート番号指定に対応するように変更しています。

ruby/trema/open-vswitch.rb

  ovs-openflowd プロセスの起動時オプションの1つ --ports に指定する文字列を生成する処理を変更しています。
  指定されたポート番号の順で、対応する Linux のインターフェース名を並べるようにしています。

(3) テストケース

spec/trema/packet-in_spec.rb
spec/trema/packet-out_spec.rb

  ポート番号を指定する機能がないのにポート番号を仮定したテストケースがあったので、今回のポート番号指定機能を使うように修正しました。

p.s.

動けば良いを優先したので、修正ソースはイマイチな感じがします...
誰かが本改造に刺激を受けてきれいに実装しなおして Trema プロジェクトに提供してくれたら嬉しいな。


■公開後のバグ修正情報

2013/04/07 21:00頃
最初に公開したパッチ trema.diff は、ruby/trema/dsl/parser.rb にバグを作りこんでいたので修正済みのものに置き換えました。
仮想ネットワーク設定ファイルで link 文に trema の仮想ネットワーク機能で作ったものではないすでに Linux にあるネットワークインタフェースを指定したときにエラーになります。具体例としては、書籍『OpenFlow実践入門』「リスト13-2 逆流防止フィルタ(OneWayBridge コントローラ)の設定ファイル」(初版 P.219)の eth0, eth1 が該当します。
すでにダウンロードしてビルドしている場合は行を1つ追加してください(赤字箇所)。
      def configure_switch(config, peer_str, ifname)
        return until peer_str
        pa = peer_str.split(PORT_DELIMITER_CHAR)

2013年3月22日 (金)

MZK-WNH/MZK-W300NH2 用 OpenWrt を OpenFlow 1.0 スイッチにカスタマイズする

PLANEX MZK-WNH および MZK-W300NH2 用に先日公開した OpenWrt を OpenFlow スイッチ化したのでその手順を書いておきます。

OpenWrt 用の OpenFlow 1.0 の実装には以下の Pantou を利用しました:
http://www.openflow.org/wk/index.php/Pantou_:_OpenFlow_1.0_for_OpenWRT

Pantou がサポート対象としている機種には、当たり前ですが MZK-WNH および MZK-W300NH2 は含まれていません。また、対象としている OpenWrt も BackFire と古いバージョンです。
簡単な動作確認はしていますが、MZK-WNH/MZK-W300NH2 用の OpenWrt Attitude Adjustment で動作する保証があるものではないことをあらかじめご了承ください。

■今回用いたビルド環境
  ビルド環境は、先日書いた OpenWrt の Attitude Adjustment ブランチのビルド環境です。
なので、
  OSは、Lubuntu 12.04 Desktop(i386) を使用。
 OpenWrt があるディレクトリは、 ~/openwrt/attitude_adjustment を想定。

OpenWrt のファームウェアのファイルが作成でき、入れ替えて起動できるものがすでに作成できていることが前提です。

■どういう OpenFlow スイッチができるのか?

今回作成したファームウェアにより MZK-WNH、MZK-W300NH2 が、4ポートの OpenFlow 1.0 のスイッチとして動作します。

あまり期待はしすぎないでください。
Pantou OpenFlow 1.0 がどれだけ標準に準拠しているのかは分かりません。
また、MZK-WNH/MZK-W300NH2 では、ハードウェア構成上 VLAN タグの入ったパケットが扱えないことがわかっています。
ポート単位のリンクアップ/ダウンも扱えません。
他にもまだまだあると思います。

■手順1) Pantou のソース入手

以下のようにコマンドを実行します:

cd ~/openwrt
git clone git://gitosis.stanford.edu/openflow-openwrt

するとカレントディレクトリに openflow-openwrt というディレクトリができます。

■手順2) 省略→ ブランチ作成 

公式HPにはありますが、不要なので省略します。
ブランチを作ってみたい人は以下のような感じでやるとよいと思います。

cd ~/openwrt/openflow-openwrt
git branch origin/openflow-1.0/mzkwnh
git checkout -b openflow-1.0/mzkwnh

ブランチ origin/openflow-1.0/mzkwnh は、オリジナルの状態のソース参照用。
ブランチ openflow-1.0/mzkwnh は、MZK-WNH/MZK-W300NH2 用のソース。

■手順3) Pantou を OpenWrt のビルド環境に組み込む

cd ~/openwrt/attitude_adjustment/package
ln -s ../../openflow-openwrt/openflow-1.0 .

※ 個人的な好みの理由で、公式HPでは絶対パスでシンボリックリンクしているところを相対パスにしています。

■手順4) 省略→ 設定ファイルの OpenWRT への追加

公式HPにはありますが、不要なので省略します。
公式HPの「Add basic configuration files for OpenWRT」という手順に該当します。

この手順では、OpenWrt のルートファイルシステムを作るときに、追加でコピーするファイルを指定できる仕掛けを利用して OpenFlow の各ファイルのコピーをするようにしています。
しかし、パッケージのインストール処理でそれらのファイルをコピーするようにしてあるので、この仕掛けを利用する必要がありません。
~/openwrt/attitude_adjustment/openflow-openwrt/openflow-1.0/Makefile の中の "define Package/openflow/install" の箇所がパッケージのインストール処理の箇所です。

■手順5) make menuconfig によるビルド項目カスタマイズ

cd ~/openwrt/attitude_adjustment
make menuconfig

make menuconfigを使って、openflow 自体、および前提として必要な tc のパッケージを有効にします。
また、公式HPには記載がないですが、完全に OpenFlow スイッチとして動かすことを考えると Firewall などの処理は不要なので削除します。

対象項目にカーソルを移動したらスペースキーで種別を切り替えます。各項目名の先頭に表示される「< >」のところが種別を表しています。<*> を選ぶとファームウェアに組込まれます。削除は < > の状態。<M> はコンパイルしてパッケージ形式にしますがファームウェアのイメージには含まれません。

以下の3つの項目を <*> にします:
・ [Network] → [OpenFlow]
・ [Network] → [tc]
・ [Kernel modules] → [Network Support] → [kmod-tun]

以下の2つの項目を < > にします:
・ [Base system]→[dnsmasq]
・ [Base system]→[firewall]

■手順6) make kernel_menuconfig によるカーネルオプション変更

cd ~/openwrt/attitude_adjustment
make kernel_menuconfig

以下の項目を <*> にします:
・ [Networking support]→[Networking options]→[QoS and/or fair queueing]→[Hierarchical Token Bucket (HTB)]

■手順7) Patnou へのパッチの適用

Pantou 公式 HP にはない手順です。
openflow 用定義ファイル /etc/config/openflow ファイルで指定できる起動時オプションを追加したいので修正をしました。
修正パッチは以下においておきます:
http://ranosgrant.cocolog-nifty.com/PantouOpenFlow_1_0/pantou.diff

以下の手順でパッチをあててください:

cd ~/openwrt/openflow-openwrt
wget http://ranosgrant.cocolog-nifty.com/PantouOpenFlow_1_0/pantou.diff
patch -p0 < pantou.diff

実行時出力

$ patch -p0 < pantou.diff
patching file openflow-1.0/files/lib/openflow/ofprotocol.sh

パッチの中身

diff --git openflow-1.0/files/lib/openflow/ofprotocol.sh openflow-

1.0/files/lib/openflow/ofprotocol.sh
index 745ef3c..25b6543 100755
--- openflow-1.0/files/lib/openflow/ofprotocol.sh
+++ openflow-1.0/files/lib/openflow/ofprotocol.sh
@@ -5,11 +5,15 @@ setup_ofprotocol() {
 	local config="$1"
 	local ofctl
 	local dp
+	local fail
+	local inactivity_probe
 
 	[ -x "/usr/bin/ofprotocol" ] || ( echo "ofprotocol not executable" && return 0 )
 	config_get ofctl "$config" ofctl
 	config_get dp "$config" dp
 	config_get mode "$config" mode
+	config_get fail "$config" fail "open"
+	config_get inactivity_probe "$config" inactivity_probe "15"
 
 
 	pidfile="/var/run/ofprotocol.pid"
@@ -25,9 +29,9 @@ setup_ofprotocol() {
 		[ -z "$dp" -o -z "$ofctl" ] && echo "no controller specified" && return 1
 		if [[ "$mode" == "inband" ]]			
 		then
-			ofprotocol unix:/var/run/dp0.sock "$ofctl" --fail=open "-D" "--

pidfile=$pidfile" --listen=ptcp: --log-file="/var/log/ofprotocol.log" &
+			ofprotocol unix:/var/run/dp0.sock "$ofctl" --fail="$fail" "-D" 

"--pidfile=$pidfile" --listen=ptcp: --log-file="/var/log/ofprotocol.log" --inactivity-

probe="$inactivity_probe" &
 		else
-			ofprotocol unix:/var/run/dp0.sock "$ofctl" --fail=open "-D" "--

pidfile=$pidfile" --out-of-band --listen=ptcp: --log-file="/var/log/ofprotocol.log" &
+			ofprotocol unix:/var/run/dp0.sock "$ofctl" --fail="$fail" "-D" 

"--pidfile=$pidfile" --out-of-band --listen=ptcp: --log-file="/var/log/ofprotocol.log" 

--inactivity-probe="$inactivity_probe" &
 		fi
 		lock -u "/var/lock/ofprotocol"
 	fi

このパッチは以下のコマンドで出力しました:

cd ~/openwrt/openflow-openwrt
git diff --no-prefix

■手順8) 定義ファイルの OpenWrt への追加

こちらも Pantou 公式HPにはない手順です。
MZK-WNH/MZK-W300NH2 を OpenFlow スイッチとして動作させるためのカスタマイズをした定義ファイルを用意します。

カスタマイズ済のファイルは tar でアーカイブして、http://ranosgrant.cocolog-nifty.com/PantouOpenFlow_1_0/files.tar においておきました。

以下の手順でビルド環境に取り込んでください:

cd ~/openwrt/attitude_adjustment
wget http://ranosgrant.cocolog-nifty.com/PantouOpenFlow_1_0/files.tar
tar xvf files.tar

これで2つのファイルが生成されます。

1) files/etc/config/openflow

openflow 用の定義ファイル /etc/config/openflow に対して少し手を加えたもので置き換えます。
OpenFlow の Datapath ID を OpenFlow 1.0 の規格に従って MAC アドレスを指定しやすくするため少し手を加えてあります。
openflow の中身は以下です:

config ofswitch 'ofswitch0'
	option dp 'dp0'
	option ofports 'eth0.1 eth0.2 eth0.3 eth0.4'
	option ofctl 'tcp:192.168.1.10:6633'
	option mode  'outofband'

2) files/etc/uci-defaults/network

OpenWrt では、初期ブート時に /etc/config/network ファイルを作成するために /etc/uci-defaults/network というファイルが起動します。
このファイルを、OpenFlow スイッチ用の定義を作成するファイルにおきかえます。置き換え後の network ファイルは、MKZ-WNH/MZK-W300NH2 固有の定義内容です。

/etc/config/openflow ファイルの dpid (Datapath ID)の値の設定処理も入れてあります。

network の内容は、以下です:

#!/bin/sh

. /etc/functions.sh
. /lib/ramips.sh
. /lib/functions/uci-defaults.sh


[ -e /etc/config/network ] && exit 0
if [ ! -x /usr/sbin/maccalc ]; then
	echo "$0: maccalc not found!"
	exit 1
fi

lan_mac=$(ramips_get_mac_binary factory 4)
wan_mac=`/usr/sbin/maccalc add "$lan_mac" 1`

dpid=`echo "$wan_mac" | sed -e 's/://g'`
uci set openflow.ofswitch0.dpid=$dpid
uci commit openflow

touch /etc/config/network
ucidef_set_interface_loopback

ucidef_add_switch "switch0" 1 1
ucidef_add_switch_vlan "switch0" 1 "0 6t"
ucidef_add_switch_vlan "switch0" 2 "1 6t"
ucidef_add_switch_vlan "switch0" 3 "2 6t"
ucidef_add_switch_vlan "switch0" 4 "3 6t"
ucidef_add_switch_vlan "switch0" 5 "4 6t"


uci batch << EOF
set network.eth0='interface'
set network.eth0.ifname='eth0'
set network.eth0.proto='static'
set network.eth0.macaddr='$lan_mac'
EOF

ucidef_set_interface_raw "eth0_1" "eth0.1"
ucidef_set_interface_raw "eth0_2" "eth0.2"
ucidef_set_interface_raw "eth0_3" "eth0.3"
ucidef_set_interface_raw "eth0_4" "eth0.4"

uci batch << EOF
set network.eth0_5='interface'
set network.eth0_5.ifname='eth0.5'
set network.eth0_5.proto='static'
set network.eth0_5.ipaddr='192.168.1.1'
set network.eth0_5.netmask='255.255.255.0'
set network.eth0_5.macaddr='$wan_mac'
EOF

uci commit network

exit 0

■手順9) イメージのビルド

準備は以上です。あとは make するだけです。

cd ~/openwrt/attitude_adjustment
make

エラーが出るようだったら make V=99 として詳細なログを出力して確認してください。

■手順10) ファームウェアの入替え

イメージファイルのありか、ファームウェアの入替え方法などは変わりないので説明は省きます。

■ファームウェア入れ替え後の構成

接続するポートに注意してください!。今回作成したファームウェアがログイン用に受け付けるのは「Internet」というラベルのついたポートです。

1)「Internet」と書いてあるポート
  IP アドレス 192.168.1.1
  OpenFlow コントローラとの接続用です。
  ログインして設定を変更するにも使います。パスワード設定前なら telnet でパスワードなしで入れます。

  IPアドレスは、/etc/config/network ファイルで変更可能です。

2)「1」~「4」と書いてあるポート
 OpenFlow 専用ポート

3) OpenFlowコントローラ
  192.168.1.10 の TCP ポート 6633 にめがけて接続しにいきます。

  IPアドレスは、/etc/config/openflow ファイルで変更可能です。後述する OpenFlow フレームワーク Trema と接続する場合、この設定のままで大丈夫です。

4) ログファイル
 /var/log/ofprotocol.log ファイルにログが出力されます。
OpenFlowコントローラとの接続状況などが出力されています。

■簡単な動作確認

dpctl コマンドで OpenFlow のプロセスを制御できます。まずは、showサブコマンドを使ってみましょう。

root@OpenWrt:/# dpctl show unix:/var/run/dp0.sock
features_reply (xid=0xdd8a589b): ver:0x1, dpid:22cf212a29
n_tables:2, n_buffers:256
features: capabilities:0xc7, actions:0xeff
 1(eth0.1): addr:00:22:cf:21:2a:28, config: 0, state:0
 2(eth0.2): addr:00:22:cf:21:2a:28, config: 0, state:0
 3(eth0.3): addr:00:22:cf:21:2a:28, config: 0, state:0
 4(eth0.4): addr:00:22:cf:21:2a:28, config: 0, state:0
get_config_reply (xid=0xba967ffb): miss_send_len=128

ポートが4つ eth0.1 ~ eth0.4 が表示されるはずです。
また、dpid には、MZK-WNH/MZK-W300NH2 の底部に貼ってあるシールの「Node ID WAN」の箇所の MAC アドレスと一致しているはずです(先頭の 00 は表示上省略されているのは気にしなくて構いません)。
OpenFlow 1.0 の規格に従って MAC アドレスを datapath id (dpid) としています。

記載箇所:
http://www.openflow.org/documents/openflow-spec-v1.0.0.pdf
"OpenFlow Switch Specification Version 1.0.0"(p.25) "5.3.1 Handshake" より:

The datapath_id field uniquely identifies a datapath. The lower 48 bits are
intended for the switch MAC address, while the top 16 bits are up to the implementer.

直訳:
datapath_id フィールドは、データパスを一意に識別します。下位48ビットはスイッチの MAC アドレスを用いることを意図しているが、上位16ビットは実装者にまかせる。

■簡単なフローエントリの確認

OpenFlow コントローラを用意しなくてもコマンドラインでフローエントリの登録ができるので以下でポート1 と 2 だけを接続してみてください。

dpctl add-flow unix:/var/run/dp0.sock idle_timeout=0,hard_timeout=0,in_port=1,actions=output:2
dpctl add-flow unix:/var/run/dp0.sock idle_timeout=0,hard_timeout=0,in_port=2,actions=output:1

フローエントリをダンプして登録されていることを確認してみます:

# dpctl dump-flows unix:/var/run/dp0.sock
stats_reply (xid=0x811a0216): flags=none type=1(flow)
  cookie=0, duration_sec=24s, duration_nsec=633000000s, table_id=1, priority=32768, n_packets=0, n_bytes=0, idle_timeout=0,hard_timeout=0,in_port=2,actions=output:1
  cookie=0, duration_sec=24s, duration_nsec=653000000s, table_id=1, priority=32768, n_packets=0, n_bytes=0, idle_timeout=0,hard_timeout=0,in_port=1,actions=output:2

これで、「1」「2」の印があるポート同士だけが通信できる状態になっています。
ケーブルを差し替えて例えば「1」と「3」にすると通信できなくなります。

dpctl コマンドの詳細は、以下のようにしてソース中の man ページを見てください:

nroff -man ~/openwrt/attitude_adjustment/build_dir/target-mipsel_r2_uClibc-0.9.33.2/openflow-1.0.0/utilities/dpctl.8 | more

■ OpenFlow コントローラとの接続確認

ここまで動いたらOpenFlow コントローラとの接続確認に挑戦してみてください。
私は、OpenFlow フレームワーク Trema を使って接続確認をしました。
Tremaについては、書籍「OpenFlow実践入門」を読んでください。

まずは ping で OpenFlow スイッチと OpenFlow コントローラの通信確認をしておいてください。

それができたら、OpenFlow フレームワーク Trema に付属するサンプルの repeater-hub あたりでまずは試してみるとよいと思います。

OpenFlow コントローラを起動したら、OpenFlow スイッチ側で tail -f /var/log/ofprotocol.log でログを監視して以下のように OpenFlow コントローラの IPアドレスで "connected" のメッセージが出たら接続完了です。

Jan 01 00:59:27|00588|rconn|INFO|tcp:192.168.1.10:6633: connected

もし、接続がなかなかできないとかすぐに切断してしまうような場合は以下のようにして openflow を再起動してみてください。

/etc/init.d/openflow restart

■ Pantou OpenFlow 1.0 のデフォルト動作の変更点について

/etc/config/openflow にて起動オプションのパラメタを変更しています。

1) option inactivity_probe '90'

デフォルトは15秒です。
Trema との接続するためには --inactivity-probe オプションを 90 秒にするように以下のブログで報告があるので採用しました。
http://trema.hatenablog.jp/entry/20120312/1331548450

2) option fail 'closed'

デフォルト値は、'open' です。
デフォルト動作は、OpenFlow コントローラとの接続失敗を繰り返したときにブリッジとして動きだす設定です。
'closed' に変更して、そのような動作をしないようにしています。

上記起動オプションの詳細は、以下のようにしてソース中の man ページを見てください:

nroff -man ~/openwrt/attitude_adjustment/build_dir/target-mipsel_r2_uClibc-0.9.33.2/openflow-1.0.0/secchan/ofprotocol.8 | more

■おまけ: Pantou に関して

ダウンロードしたソースを見てみると非常に少なくてどこにも OpenFlow のコードがないのが不思議でした。が、OpenFlow のソースは別のところからビルド時にダウンロードする仕掛けでした。

以下がソースツリーです:

$ cd ~/openwrt
$ tree openflow-1.0
openflow-1.0/
├── Makefile
├── files
│   ├── etc
│   │   ├── config
│   │   │   └── openflow
│   │   ├── hotplug.d
│   │   │   └── ofiface
│   │   │       └── 00-netstate
│   │   └── init.d
│   │       └── openflow
│   ├── lib
│   │   └── openflow
│   │       ├── ofinterfaces.sh
│   │       ├── ofprotocol.sh
│   │       └── ofswitch.sh
│   └── sbin
│       ├── ofdown
│       └── ofup
└── patches
    ├── 001-secchan-Fix-port-status-detection.patch
    ├── 002-BSD_cross_compile_fix.patch
    ├── 003_FLOW_ENTIES_limit.patch
    ├── 004-lib-use-bitwise-AND-when-checking-for-ARP.patch
    └── 005-Remove-hardcoded-path-for-tc.patch

10 directories, 14 files

Pantou のホームページに書いてあるように、OpenFlow部分は Standford reference の実装を使っています。

The OpenFlow module is based on the Stanford reference implementation (userspace).

files ディレクトリにはそのままインストールするファイル群、patches の下には元となるソースに対するパッチファイル群がおいてあります。

もともと OpenWrt のパッケージのビルドの仕掛けには、他からソースを持ってきてパッチをあててビルドするという仕掛けがあります。
OpenWrt のパッケージの仕掛けの詳細は以下を参照ください(英語です):
http://wiki.openwrt.org/doc/devel/packages

~/openwrt/attitude_adjustment/openflow-1.0/Makefile の以下の箇所がソースの入手元:

PKG_NAME_SHORT:=openflow
PKG_NAME:=kmod-$(PKG_NAME_SHORT)
PKG_VERSION:=1.0.0
PKG_RELEASE:=1

PKG_SOURCE:=$(PKG_NAME_SHORT)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:= http://openflowswitch.org/downloads/

今回の場合、実際にコンパイルに使っているソースや生成したオブジェクトがあるディレクトリは以下でした:
~/openwrt/attitude_adjustment/build_dir/target-mipsel_r2_uClibc-0.9.33.2/openflow-1.0.0/

ちなみに、パッケージだけコンパイルしたい場合は以下の手順です:

cd ~openwrt/attitude_adjustment
make package/openflow-1.0/compile

(情報源: http://wiki.openwrt.org/doc/howtobuild/single.package)

p.s.
以前、公開した OpenFlow 対応のファームウェアは Attitude Adjustment が trunk (開発用ブランチ) だった古いものです。
当時は、試行錯誤でつくったので手順を公開できませんでしたが、今回はできるだけ最小限の手間でビルドできる手順としました...したつもりです。

2013年3月16日 (土)

ネットワークネームスペース機能使用時の Trema 停止エラー軽減方法

OpenFlow フレームワーク Trema のネットワークネームスペース機能について以前「Trema の仮想ネットワーク機能には『OpenFlow実践入門』に書いてない強力な機能がありました。」という記事を書きましたが、使ってみると Trema の停止時に失敗することが良く起こり使いづらいところがありました。回避方法を見つけたので書いておきます。

■本記事の対象
Trema のネットワークネームスペース機能。
・確認した Trema のバージョンは、trema 0.3.14、および 0.3.16(最新パッケージ版)
・確認した OSは、Lubuntu 12.04 Desktop (i386)
  (Ubuntu 12.04 でも同じだと思います)

■前提知識
Trema の仮想ネットワーク機能で指定したネットワークネームスペース内でコマンドを起動するためには、まず「trema netns ネットワークネームスペース名」を実行してシェルを起動します。このシェル内で実行されたコマンドはそのネットワークネームスペース内で動作します。

■問題点
「trema netns ネットワークネームスペース名」コマンドで起動したシェルを終了するのを忘れて Trema を終了しようとするというミスが頻発しました。特に複数のネットワークネームスペースを利用する場合です。
このシェルを終了せずに Trema を終了しようとすると以下のようにエラーが出て失敗します。

例: ネットワークネームスペース host1 でシェルを起動したまま停止を試みた時

$ trema killall
Cannot remove /var/run/netns/host1: Device or resource busy
error: Command 'sudo up netns delete host1' failed!

また、このエラーが出たあとにシェルを終了させて正常に Tremaを停止させても、次の Trema 起動時に一回失敗します。

例: 上記例のエラー後の起動では2回目で起動成功

$ trema run empty.rb -c netns.conf   ← 1回目はエラー
Could not create /var/run/netns/hostt1: File exists
error: Command 'sudo ip netns add host1' failed!
$ trema run empty.rb -c netns.conf   ← 2回目は成功

■問題の回避方法
シェルを起動することなく、ネットワークネームスペースを指定してコマンドを実行する方法があれば良いと考え OS のコマンドを探してみたらありました。以下の構文です:

  ip netns exec ネットワークネームスペース名 コマンド

例: ネットワークネームスペース host1 で arp -a を実行

$ sudo ip netns exec host1 arp -a

この方法だとコマンドが終了すればそのネットワークネームスペースを使用しているプロセスがなくなります。これによりネットワークネームスペースを使っているプロセスを起動したままにしておくことがなくなり、Trema 終了時の失敗を減らすことができます。

■注意点
ip netns exec にはルート権限が必要なので一般ユーザで使うには sudo が必要です。

p.s.
Trema の netns サブコマンドで引数としてコマンドライン指定もサポートしてくれたら嬉しいな。

最近のトラックバック

無料ブログはココログ