« 2013年1月 | トップページ | 2013年3月 »

2013年2月

2013年2月16日 (土)

OpenFlow性能測定 (第3弾)

前回に引き続き OpenFlow の性能測定をしました。機器の接続構成は前回同様ですが OpenFlow コントローラと OpenFlow スイッチをより性能の高いPCで動かしています。前回 100Mイーサ間のTCPが 5~6Mbits/sec というひどさだったのでどこが悪かったのかを探るためにやってみました。

やってみた結果、前回と今回でいえることは、「できるだけ Packet In/Out はさせるな、さもないとOpenFlow コントローラの性能が足かせになって通信性能が落ちるぞ」、ということでしょうか。当たり前と言えば当たり前ですが数値でみるとはっきりしますね。

■テスト時の環境
各テスト時のハードウェアの構成は以下の通りです。
(1) テスト1
  OpenFlow コントローラ: Trema 0.3.12
    CPU: Intel Core2 Duo E8300 (2.83GHz)
    LAN: 1000Base-T
  OpenFlow スイッチ: Open vSwitch 1.4.0
    CPU: Intel Core2 Duo E8400 (3GHz)
    LAN: 1000Base-T

  コントローラとスイッチ間をギガビットイーサで直結しています。

(2) テスト2
  テスト1 と同じマシンを用いていますがコントローラとスイッチの間を 100Base-TX で接続しています。コントローラとスイッチ間の通信性能が与える影響を見たくてやってみました。

  OpenFlow コントローラ: Trema 0.3.12
    CPU: Intel Core2 Duo E8300 (2.83GHz)
    LAN: 100Base-TX (USB 2.0 の LAN アダプタを使用)
  OpenFlow スイッチ: Open vSwitch 1.4.0
    CPU: Intel Core2 Duo E8400 (3GHz)
    LAN: 1000Base-T のポートだが相手側に合わせて 100Base-TX で動作

(3) テスト3
  テスト2 に対してスイッチをスペックダウンしています。
 前回のテストで用いたスイッチであり、前回テスト時とは単にコントローラを変えた構成です。

  OpenFlow コントローラ: Trema 0.3.12
    CPU: Intel Core2 Duo E8300 (2.83GHz)
    LAN: 100Base-TX (USB 2.0 の LAN アダプタを使用)
  OpenFlow スイッチ: Open vSwitch 1.4.0
    CPU: Intel Core 2 Duo U7500 (1.06GHz)
    LAN: 100Base-TX

OSは、すべて Lubuntu 12.04 Desktop (i386) を使っています。
OpenFlow スイッチ側の OpenFlow ポートは USB 2.0 の LAN アダプタにて 100Base-TX で通信しています。
前回同様に、host1 側で iperf のサーバを動かして、host2 側で実行結果を採取しています。

Photo_4

■テスト時の OpenFlow フレームワーク Trema の定義
前回で用いた、すべてのパケットがOpenFlowコントローラを経由する定義です。

class SimpleHubPacketInOut < Controller
  def packet_in( datapath_id, message )
    send_packet_out(
      datapath_id,
      :packet_in => message,
      :actions => ActionOutput.new( OFPP_FLOOD )
    )
  end
end

OpenFlow 1.0 ではフローエントリに一致しないすべてのパケットがコントローラに送られる仕様なのでフローエントリは登録せずに空のままにしておき packet_in のハンドラだけ定義しています。

■テスト1の結果

TCP も含めて良好な結果がでました。
(1) Ping

$ ping 192.168.0.1
PING 192.168.0.1 (192.168.0.1) 56(84) bytes of data.
64 bytes from 192.168.0.1: icmp_req=1 ttl=64 time=1.81 ms
64 bytes from 192.168.0.1: icmp_req=2 ttl=64 time=1.19 ms
64 bytes from 192.168.0.1: icmp_req=3 ttl=64 time=1.18 ms
64 bytes from 192.168.0.1: icmp_req=4 ttl=64 time=1.19 ms
^C
--- 192.168.0.1 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3005ms
rtt min/avg/max/mdev = 1.181/1.346/1.812/0.272 ms

(2) TCP

$ iperf -c 192.168.0.1
------------------------------------------------------------
Client connecting to 192.168.0.1, TCP port 5001
TCP window size: 21.0 KByte (default)
------------------------------------------------------------
[  3] local 192.168.0.2 port 46517 connected with 192.168.0.1 port 5001
[ ID] Interval      Transfer   Bandwidth
[  3]  0.0-10.0 sec   112 MBytes  94.1 Mbits/sec

(3) UDP

$ iperf -c 192.168.0.1 -u -b 100M
------------------------------------------------------------
Client connecting to 192.168.0.1, UDP port 5001
Sending 1470 byte datagrams
UDP buffer size:  160 KByte (default)
------------------------------------------------------------
[  3] local 192.168.0.2 port 36276 connected with 192.168.0.1 port 5001
[ ID] Interval      Transfer   Bandwidth
[  3]  0.0-10.0 sec   114 MBytes  95.7 Mbits/sec
[  3] Sent 81409 datagrams
[  3] Server Report:
[  3]  0.0-10.0 sec   114 MBytes  95.3 Mbits/sec   0.063 ms    0/81408 (0%)
[  3]  0.0-10.0 sec  1 datagrams received out-of-order

■テスト2の結果

テスト1よりは悪いですがそれほど悪化はしていません。
(1) Ping

$ ping 192.168.0.1
PING 192.168.0.1 (192.168.0.1) 56(84) bytes of data.
64 bytes from 192.168.0.1: icmp_req=1 ttl=64 time=1.82 ms
64 bytes from 192.168.0.1: icmp_req=2 ttl=64 time=1.67 ms
64 bytes from 192.168.0.1: icmp_req=3 ttl=64 time=1.83 ms
^C
--- 192.168.0.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 1.679/1.779/1.832/0.078 ms

(2) TCP

$ iperf -c 192.168.0.1
------------------------------------------------------------
Client connecting to 192.168.0.1, TCP port 5001
TCP window size: 21.0 KByte (default)
------------------------------------------------------------
[  3] local 192.168.0.2 port 46521 connected with 192.168.0.1 port 5001
[ ID] Interval       Transfer     Bandwidth
[  3]  0.0-10.0 sec  96.0 MBytes  80.5 Mbits/sec

(3) UDP

$ iperf -c 192.168.0.1 -u -b 100M
------------------------------------------------------------
Client connecting to 192.168.0.1, UDP port 5001
Sending 1470 byte datagrams
UDP buffer size:  160 KByte (default)
------------------------------------------------------------
[  3] local 192.168.0.2 port 47946 connected with 192.168.0.1 port 5001
[ ID] Interval       Transfer     Bandwidth
[  3]  0.0-10.0 sec   114 MBytes  95.7 Mbits/sec
[  3] Sent 81410 datagrams
[  3] Server Report:
[  3]  0.0-10.1 sec   106 MBytes  88.1 Mbits/sec   0.057 ms 5729/81409 (7%)
[  3]  0.0-10.1 sec  1 datagrams received out-of-order

■テスト3の結果
(1) Ping

$ ping 192.168.0.1
PING 192.168.0.1 (192.168.0.1) 56(84) bytes of data.
64 bytes from 192.168.0.1: icmp_req=1 ttl=64 time=1.91 ms
64 bytes from 192.168.0.1: icmp_req=2 ttl=64 time=1.81 ms
64 bytes from 192.168.0.1: icmp_req=3 ttl=64 time=1.81 ms
64 bytes from 192.168.0.1: icmp_req=4 ttl=64 time=1.93 ms
^C
--- 192.168.0.1 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3005ms
rtt min/avg/max/mdev = 1.811/1.870/1.932/0.063 ms

(2) TCP

$ iperf -c 192.168.0.1
------------------------------------------------------------
Client connecting to 192.168.0.1, TCP port 5001
TCP window size: 21.0 KByte (default)
------------------------------------------------------------
[  3] local 192.168.0.2 port 55911 connected with 192.168.0.1 port 5001
[ ID] Interval	     Transfer	  Bandwidth
[  3]  0.0-10.0 sec  83.8 MBytes  70.2 Mbits/sec

(3) UDP

$ iperf -c 192.168.0.1 -u -b 100M
------------------------------------------------------------
Client connecting to 192.168.0.1, UDP port 5001
Sending 1470 byte datagrams
UDP buffer size:  160 KByte (default)
------------------------------------------------------------
[  3] local 192.168.0.2 port 40001 connected with 192.168.0.1 port 5001
[ ID] Interval	     Transfer	  Bandwidth
[  3]  0.0-10.0 sec   114 MBytes  95.8 Mbits/sec
[  3] Sent 81449 datagrams
[  3] Server Report:
[  3]  0.0-10.1 sec   106 MBytes  88.1 Mbits/sec   0.036 ms 5790/81448 (7.1%)
[  3]  0.0-10.1 sec  1 datagrams received out-of-order

前回テスト時は TCP が 5~6Mbits/sec とかなり低い数値でしたが大幅に改善されて 70Mbits/sec となっています。前回テスト時の OpenFlow コントローラは CPU が Core2 Duo SU9300 (1.2GHz)でしたのでコントローラのハード性能アップがかなり影響するということになります。

■ご参考: CPU情報
・Intel Core2 Duo E8400
仕様: http://ark.intel.com/ja/products/33910/Intel-Core2-Duo-Processor-E8400-6M-Cache-3_00-GHz-1333-MHz-FSB
ベンチマーク: http://cpubenchmark.net/cpu.php?cpu=Intel+Core2+Duo+E8400+%40+3.00GHz&id=955
・Intel Core2 Duo E8300
仕様: http://ark.intel.com/ja/products/35070/Intel-Core2-Duo-Processor-E8300-6M-Cache-2_83-GHz-1333-MHz-FSB
ベンチマーク: http://cpubenchmark.net/cpu.php?cpu=Intel+Core2+Duo+E8300+%40+2.83GHz&id=952
・Intel Core2 Duo U7500
仕様: http://ark.intel.com/ja/products/29763/Intel-Core2-Duo-Processor-U7500-2M-Cache-1_06-GHz-533-MHz-FSB-Socket-P
ベンチマーク: http://cpubenchmark.net/cpu.php?cpu=Intel+Core2+Duo+U7500+%40+1.06GHz&id=1015
・Intel Core2 Duo SU9300
仕様: http://ark.intel.com/ja/products/36695/Intel-Core2-Duo-Processor-SU9300-3M-Cache-1_20-GHz-800-MHz-FSB
ベンチマーク: http://cpubenchmark.net/cpu.php?cpu=Intel+Core2+Duo+U9300+%40+1.20GHz&id=1018

2013年2月12日 (火)

OpenFlowスイッチ(Open vSwitch)の性能測定 (続編)

前回の性能テストで OpenFlow スイッチを経由することによる TCP, UDP の性能の低下を確認できなかったので、今度は全パケットを OpenFlow コントローラを経由するようにして測定してみました。
今回のテスト環境では以下の構成です:Photo
OpenFlow スイッチとコントローラを直結しじゃまが一切入らないようにしました。

前回同様に iperf コマンドを用いて測定し、ホスト1(192.168.0.1)をサーバ、ホスト2(192.168.0.2)をクライアントとして動かしています。以下の出力結果はすべて 192.168.0.2 側で採取したものです。

測定結果は、UDP ではほとんど変わりませんが、TCP では劇的に 95M → 5M と性能が悪化しています。

テスト中、OpenFlow コントローラ側にて top コマンドを実行し CPU のアイドル時間を眺めていたのですが UDP の時は 30% ぐらいまで落ち込むのに対して、TCP の時は 70% ぐらいにしかならず暇してました。

これはどう解釈したらよいのでしょう?

Tremaで実行したコード:

class RepeaterHubPacketInOut < Controller
  def packet_in( datapath_id, message )
    send_packet_out(
      datapath_id,
      :packet_in => message,
      :actions => ActionOutput.new( OFPP_FLOOD )
    )
  end
end

(1) Ping

$ ping 192.168.0.1
PING 192.168.0.1 (192.168.0.1) 56(84) bytes of data.
64 bytes from 192.168.0.1: icmp_req=1 ttl=64 time=2.91 ms
64 bytes from 192.168.0.1: icmp_req=2 ttl=64 time=2.58 ms
64 bytes from 192.168.0.1: icmp_req=3 ttl=64 time=2.80 ms
64 bytes from 192.168.0.1: icmp_req=4 ttl=64 time=2.69 ms
64 bytes from 192.168.0.1: icmp_req=5 ttl=64 time=2.40 ms
^C
--- 192.168.0.1 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4007ms
rtt min/avg/max/mdev = 2.407/2.680/2.912/0.184 ms

(2) TCP
3回実行しています。平均すると 5.62Mbits/sec

$ iperf -c 192.168.0.1
------------------------------------------------------------
Client connecting to 192.168.0.1, TCP port 5001
TCP window size: 21.0 KByte (default)
------------------------------------------------------------
[  3] local 192.168.0.2 port 54052 connected with 192.168.0.1 port 5001
[ ID] Interval	     Transfer	  Bandwidth
[  3]  0.0-10.5 sec  7.00 MBytes  5.61 Mbits/sec
$ iperf -c 192.168.0.1
------------------------------------------------------------
Client connecting to 192.168.0.1, TCP port 5001
TCP window size: 21.0 KByte (default)
------------------------------------------------------------
[  3] local 192.168.0.2 port 54053 connected with 192.168.0.1 port 5001
[ ID] Interval	     Transfer	  Bandwidth
[  3]  0.0-10.3 sec  7.38 MBytes  6.03 Mbits/sec
$ iperf -c 192.168.0.1
------------------------------------------------------------
Client connecting to 192.168.0.1, TCP port 5001
TCP window size: 21.0 KByte (default)
------------------------------------------------------------
[  3] local 192.168.0.2 port 54054 connected with 192.168.0.1 port 5001
[ ID] Interval	     Transfer	  Bandwidth
[  3]  0.0-10.4 sec  6.50 MBytes  5.23 Mbits/sec

(3) UDP

$ iperf -c 192.168.0.1 -u -b 100M
------------------------------------------------------------
Client connecting to 192.168.0.1, UDP port 5001
Sending 1470 byte datagrams
UDP buffer size:  160 KByte (default)
------------------------------------------------------------
[  3] local 192.168.0.2 port 59215 connected with 192.168.0.1 port 5001
[ ID] Interval	     Transfer	  Bandwidth
[  3]  0.0-10.0 sec   114 MBytes  95.7 Mbits/sec
[  3] Sent 81410 datagrams
[  3] Server Report:
[  3]  0.0-10.1 sec   114 MBytes  95.0 Mbits/sec   0.037 ms   15/81409 (0.018%)
[  3]  0.0-10.1 sec  1 datagrams received out-of-order

2013年2月11日 (月)

OpenFlowスイッチ(Open vSwitch)の性能測定

前回の記事で作成した OpenFlow スイッチの性能テストをしてみました。

テスト環境の構成は前回の記事を参照ください。測定は iperf コマンドを用いて、ホスト1(192.168.0.1)をサーバ、ホスト2(192.168.0.2)をクライアントとして動かしています。UDP のテストの時は、"-b 100M" をつけて 100Mbps の帯域を指定しています。

以下、出力結果はすべてホスト2(192.168.0.2)側で実行した結果です。

■テストケース
以下の3つを試しました。
(1) ホスト1 とホスト2 をクロスケーブルで直結して OpenFlow スイッチを間にはさまずにテスト。
(2) OpenFlow スイッチでリピーターハブを動かしてテスト。
(3) リピーターハブを改造し、全パケットを OpenFlow コントローラに送るようにしてテスト。

■結論:
(1) テストケース1→2→3 と ping の応答は落ちておりやることが増えればターンアラウンドタイムは悪くなっている。
(2) その一方、スループットは大差なし。物理的な制約(100Mbpsのイーサネット)の方が大きいのではないかと思われます。

■テストケース1
ホスト1 とホスト2 を OpenFlow スイッチを使わずにクロスケーブルで直結してテスト。
まずはベースとなる性能の確認です。

(1) Ping

$ ping 192.168.0.1
PING 192.168.0.1 (192.168.0.1) 56(84) bytes of data.
64 bytes from 192.168.0.1: icmp_req=1 ttl=64 time=0.432 ms
64 bytes from 192.168.0.1: icmp_req=2 ttl=64 time=0.272 ms
64 bytes from 192.168.0.1: icmp_req=3 ttl=64 time=0.287 ms
64 bytes from 192.168.0.1: icmp_req=4 ttl=64 time=0.278 ms
^C
--- 192.168.0.1 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3000ms
rtt min/avg/max/mdev = 0.272/0.317/0.432/0.067 ms

(2) TCP

$ iperf -c 192.168.0.1
------------------------------------------------------------
Client connecting to 192.168.0.1, TCP port 5001
TCP window size: 21.0 KByte (default)
------------------------------------------------------------
[  3] local 192.168.0.2 port 57812 connected with 192.168.0.1 port 5001
[ ID] Interval      Transfer   Bandwidth
[  3]  0.0-10.0 sec   113 MBytes  94.7 Mbits/sec

(3) UDP

$ iperf -c 192.168.0.1 -u -b 100M
------------------------------------------------------------
Client connecting to 192.168.0.1, UDP port 5001
Sending 1470 byte datagrams
UDP buffer size:  160 KByte (default)
------------------------------------------------------------
[  3] local 192.168.0.2 port 38938 connected with 192.168.0.1 port 5001
[ ID] Interval      Transfer   Bandwidth
[  3]  0.0-10.0 sec   114 MBytes  95.7 Mbits/sec
[  3] Sent 81409 datagrams
[  3] Server Report:
[  3]  0.0-10.0 sec   114 MBytes  95.6 Mbits/sec   0.034 ms   66/81408 (0.081%)
[  3]  0.0-10.0 sec  1 datagrams received out-of-order

■テストケース2
OpenFlow スイッチで「5行で書いたリピーターハブ」を動かしてテスト。

Tremaで実行したコード:

class RepeaterHub < Controller
  def switch_ready( datapath_id )
    send_flow_mod_add( datapath_id, :actions => SendOutPort.new( OFPP_FLOOD ) )
  end
end

(1) Ping

$ ping 192.168.0.1
PING 192.168.0.1 (192.168.0.1) 56(84) bytes of data.
64 bytes from 192.168.0.1: icmp_req=1 ttl=64 time=1.70 ms
64 bytes from 192.168.0.1: icmp_req=2 ttl=64 time=0.759 ms
64 bytes from 192.168.0.1: icmp_req=3 ttl=64 time=0.593 ms
64 bytes from 192.168.0.1: icmp_req=4 ttl=64 time=0.596 ms
64 bytes from 192.168.0.1: icmp_req=5 ttl=64 time=0.662 ms
^C
--- 192.168.0.1 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4001ms
rtt min/avg/max/mdev = 0.593/0.862/1.704/0.426 ms

(2) TCP

iperf -c 192.168.0.1
------------------------------------------------------------
Client connecting to 192.168.0.1, TCP port 5001
TCP window size: 21.0 KByte (default)
------------------------------------------------------------
[  3] local 192.168.0.2 port 57816 connected with 192.168.0.1 port 5001
[ ID] Interval      Transfer   Bandwidth
[  3]  0.0-10.0 sec   113 MBytes  94.4 Mbits/sec

(3) UDP

$ iperf -c 192.168.0.1 -u -b 100M
------------------------------------------------------------
Client connecting to 192.168.0.1, UDP port 5001
Sending 1470 byte datagrams
UDP buffer size:  160 KByte (default)
------------------------------------------------------------
[  3] local 192.168.0.2 port 40979 connected with 192.168.0.1 port 5001
[ ID] Interval      Transfer   Bandwidth
[  3]  0.0-10.0 sec   114 MBytes  95.8 Mbits/sec
[  3] Sent 81450 datagrams
[  3] Server Report:
[  3]  0.0-10.0 sec   114 MBytes  95.5 Mbits/sec   0.051 ms   10/81449 (0.012%)
[  3]  0.0-10.0 sec  1 datagrams received out-of-order

■テストケース3
「5行で書いたリピーターハブ」を改造し、全パケットを OpenFlow コントローラに「も」送るようにしてテスト。
パケットがコントローラに飛ぶことでの影響を確認しようと思ってやってみました。

Tremaで実行したコード:

class RepeaterHubWithPacketIn < Controller
  def switch_ready( datapath_id )
    send_flow_mod_add(
      datapath_id,
      :actions => [
        SendOutPort.new( OFPP_CONTROLLER ),
        SendOutPort.new( OFPP_FLOOD )
      ]
    )
  end
end

(1) Ping

$ ping 192.168.0.1
PING 192.168.0.1 (192.168.0.1) 56(84) bytes of data.
64 bytes from 192.168.0.1: icmp_req=1 ttl=64 time=1.03 ms
64 bytes from 192.168.0.1: icmp_req=2 ttl=64 time=0.695 ms
64 bytes from 192.168.0.1: icmp_req=3 ttl=64 time=0.823 ms
64 bytes from 192.168.0.1: icmp_req=4 ttl=64 time=0.782 ms
64 bytes from 192.168.0.1: icmp_req=5 ttl=64 time=0.834 ms
^C
--- 192.168.0.1 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 3998ms
rtt min/avg/max/mdev = 0.695/0.832/1.030/0.115 ms

(2) TCP

$ iperf -c 192.168.0.1
------------------------------------------------------------
Client connecting to 192.168.0.1, TCP port 5001
TCP window size: 21.0 KByte (default)
------------------------------------------------------------
[  3] local 192.168.0.2 port 57819 connected with 192.168.0.1 port 5001
[ ID] Interval      Transfer   Bandwidth
[  3]  0.0-10.0 sec   113 MBytes  94.5 Mbits/sec

(3) UDP

$ iperf -c 192.168.0.1 -u -b 100M
------------------------------------------------------------
Client connecting to 192.168.0.1, UDP port 5001
Sending 1470 byte datagrams
UDP buffer size:  160 KByte (default)
------------------------------------------------------------
[  3] local 192.168.0.2 port 42176 connected with 192.168.0.1 port 5001
[ ID] Interval      Transfer   Bandwidth
[  3]  0.0-10.0 sec   114 MBytes  95.7 Mbits/sec
[  3] Sent 81410 datagrams
[  3] Server Report:
[  3]  0.0-10.0 sec   113 MBytes  94.8 Mbits/sec   0.065 ms  549/81409 (0.67%)
[  3]  0.0-10.0 sec  1 datagrams received out-of-order

95Mbits/sec 台が出ていないので再度やり直しました。

(4) UDP 再度

$ iperf -c 192.168.0.1 -u -b 100M
------------------------------------------------------------
Client connecting to 192.168.0.1, UDP port 5001
Sending 1470 byte datagrams
UDP buffer size:  160 KByte (default)
------------------------------------------------------------
[  3] local 192.168.0.2 port 33369 connected with 192.168.0.1 port 5001
[ ID] Interval      Transfer   Bandwidth
[  3]  0.0-10.0 sec   114 MBytes  95.7 Mbits/sec
[  3] Sent 81406 datagrams
[  3] Server Report:
[  3]  0.0-10.0 sec   114 MBytes  95.4 Mbits/sec   0.034 ms   20/81405 (0.025%)
[  3]  0.0-10.0 sec  1 datagrams received out-of-order

今度は、95Mbits/sec 台が出ています。

■補足: ホスト1側で実行したコマンドについて

ホスト1 側で iperf をサーバとして起動した時のコマンドは以下の通りです。

TCP での測定の時:

$ iperf -s

UDP での測定の時:

$ iperf -s -u

2013年2月10日 (日)

2万円で OpenFlow スイッチを自作しよう

ソフトウェア寄りのことばかりしていたので少しだけハード寄りのことをしたくなりました。そこで目に付いたのが、去年の10月に衝動買いで買った中古のノートPC。
買ったものの遊んでいたので OpenFlow スイッチにしました。作業記録を残しておきます。参考までに、構想、見積もりも書いておきます。

■ OpenFlow スイッチの作り方概要

Open vSwitch という OpenFlow にも対応した仮想スイッチのソフトウェアがあるのでこれを使います。ちなみに、OpenFlowフレームワーク Trema の仮想ネットワーク機能の仮想 OpenFlow スイッチもこの Open vSwitch を使っているとのこと(書籍『OpenFlow実践入門』(初版、「仮想ネットワーク」P.195)。

ハード側での悩みどころは、複数のLANポートをどうやって用意するかです。複数ポートを持つLANカードは非常に高価だったり、かといって複数のカードを刺すとしても物理的に増設できるスロットが限られます。そこは、USB LAN アダプタで解決できると考えました。USB なら USB ハブで接続できる個数を容易に増やせます。価格も100Mイーサで USB 2.0 のものなら安いのだと 1000円程度で手に入ります。

まとめると、
(1) Open vSwitch をインストールし、OpenFlow スイッチとして設定する。
(2) USB LAN アダプタを使って複数ポート用意して OpenFlow 専用のポートとする。
で、OpenFlow スイッチが作れる。

用意するもの:
  ・USB LAN アダプタ を数個
  ・Open vSwitch が動き、USB LAN アダプタが使える OS (Linux)
  ・その OS が動く PC
     (USB LAN アダプタが使えれば良いので、実は VMwarePlayer や VMwareWorkstation 上の仮想マシンでも構いません。)
  ・インターネット接続環境 (インストール時のみ)
  ・必要に応じて USB ハブ(ACアダプタ付きが安心)
  ・LAN ケーブル(クロス) (ストレートでなくクロスです。注意!)

■懸案あれこれ

その USB LAN アダプタが Linux で使えるかどうかが心配な点。インターネットで検索して動作実績の報告があるものを選んだほうが良いでしょう。

また、入手のしやすさという点で、LAN ケーブルがストレートケーブルではなくクロスケーブルでつなぐ必要があることがちょっとやっかいかもしれません。あまり取り扱いがなくて店舗では在庫ありませんって言われる可能性も。

USB ハブで増やせるとはいっても、USB 2.0 は高速モードで 480Mbps です。100Mbps の USB LAN アダプタを5つ接続すると単純計算で 100Mbps × 5 = 500Mbps なので 480 を超えてしまいます。ピーク性能を考えると4つまでにしておいた方が良さそう。

また、LAN で通信するのでUSB LAN アダプタの消費電流も心配。ググってみて見つけた I-O DATA のETX3-US2 のスペックを見てみると最大 250mA と書いてあります。USB 2.0 が供給できるは 500mA ですから、AC アダプタを使わないバスパワーの USB ハブにつなぐとすると最大2つまで。USB ハブはACアダプタ付きがよさそうです。 

■概算見積もり

2ポートの場合:
中古ノートPC(LAN内蔵)   ... 約 10,000 円
USB LANアダプタ          ... 約  4,000 円 (1個2000円を2個)
LANケーブル(クロス)       ... 約  1,200 円 (1本600円を2本)
USB HUB(ACアダプタ付) ... 約 3,000 円
----------------------------------------
合計                              18,200 円

高めに設定しているので、LAN アダプタ+ケーブルをもう1つ増やして3ポートにできると思います。

実際に購入した金額:
 中古ノートPC1台 10,500円  (内蔵: LANポート×1、USB 2.0ポート×3)
  USB LAN アダプタ1個680円を4個 (ノーブランドだが、Linux で動作すると記載有り)
  クロスケーブル1m 100円を4本(ノーブランド。たまたま特価で購入)
 概算では2ポートとしたが今後の拡張に備えて4ポート分を購入。
  USB HUB は無しにして0円(内蔵で3つあるのでとりあえず不要)
合計:
  10500 + (680 + 100)×4 = 13,620円

ちなみに、VMwarePlayer や VMwareWorkstation にはホストOSのUSBをうばって仮想マシンにつなぐ機能があるので、これを使って仮想マシンですませれば1万円以下!。


■ 今回実際に用意した環境
・本体
  Panasonic Let's Note CF-T7
    CPU: Core Duo U7500 (1.06GHz)
    USB 2.0 ポートが3つ。
    100Mbps イーサネットポートが1つ。
・OS
  Lubuntu 12.04 Desktop (i386)
・USB LAN アダプタを2個 (もう2個あるが未使用)
  ノーブランドの USB 2.0 の 100/10Mbps LAN アダプタ
 (型名らしきもの: JP208B NO:88772A)

上記環境にて Lubuntu 12.04 がインストール済で、インターネットに接続されている状態まで準備が完了したところから書いていきます。

Photo_4

■ステップ1. USB LAN アダプタを接続する

接続すると自動で認識してくれます。
ifconfig コマンドで確認して増えていれば OK。
以下の出力結果は、eth0 が内蔵LAN で、eth1, eth2 が追加したものです。

$ ifconfig
eth0	  Link encap:イーサネット  ハードウェアアドレス 00:0b:97:51:1e:3b
	  inetアドレス:192.168.42.17  ブロードキャスト:192.168.42.255  マスク:255.255.255.0
	  inet6アドレス: fe80::20b:97ff:fe51:1e3b/64 範囲:リンク
	  UP BROADCAST RUNNING MULTICAST  MTU:1500  メトリック:1
	  RXパケット:26401 エラー:0 損失:0 オーバラン:0 フレーム:0
	  TXパケット:16068 エラー:0 損失:0 オーバラン:0 キャリア:0
	  衝突(Collisions):0 TXキュー長:1000
	  RXバイト:35715361 (35.7 MB)  TXバイト:1346516 (1.3 MB)
	  割り込み:16

eth1	  Link encap:イーサネット  ハードウェアアドレス 00:0e:c6:f0:4b:26
	  UP BROADCAST MULTICAST  MTU:1500  メトリック:1
	  RXパケット:0 エラー:0 損失:0 オーバラン:0 フレーム:0
	  TXパケット:0 エラー:0 損失:0 オーバラン:0 キャリア:0
	  衝突(Collisions):0 TXキュー長:1000
	  RXバイト:0 (0.0 B)  TXバイト:0 (0.0 B)

eth2	  Link encap:イーサネット  ハードウェアアドレス 00:0e:c6:f0:2e:32
	  UP BROADCAST MULTICAST  MTU:1500  メトリック:1
	  RXパケット:0 エラー:0 損失:0 オーバラン:0 フレーム:0
	  TXパケット:0 エラー:0 損失:0 オーバラン:0 キャリア:0
	  衝突(Collisions):0 TXキュー長:1000
	  RXバイト:0 (0.0 B)  TXバイト:0 (0.0 B)

lo	  Link encap:ローカルループバック
	  inetアドレス:127.0.0.1  マスク:255.0.0.0
	  inet6アドレス: ::1/128 範囲:ホスト
	  UP LOOPBACK RUNNING  MTU:16436  メトリック:1
	  RXパケット:953 エラー:0 損失:0 オーバラン:0 フレーム:0
	  TXパケット:953 エラー:0 損失:0 オーバラン:0 キャリア:0
	  衝突(Collisions):0 TXキュー長:0
	  RXバイト:88680 (88.6 KB)  TXバイト:88680 (88.6 KB)

■ステップ2. ネットワークマネージャからはずす

新しく追加したものは、OpenFlow 専用としたいので余計なことをされたくありません。
ネットワークマネージャの設定にて「自動接続する」のチェックをはずしておきます。
腕に覚えがある方なら、ネットワークマネージャ自体を削除しておくのも良いと思います。

1) [設定]-[ネットワーク接続]で「ネットワーク接続」ウィンドウを起動。
2) 「有線」タブから、それっぽいのを選んで「編集」ボタンを押す。
3) MACアドレスが追加した USB LAN アダプタのものであることを確認した上で「自動接続する」のチェックをはずす。
Autoconnection

先ほどの ifconfig コマンドの結果で「ハードウェアアドレス」の箇所が MAC アドレスです。

4) 「保存...」ボタンを押す。

■ステップ3. OpenVSwitch のインストール

以下のコマンドでインストールします:

$ sudo apt-get install openvswitch-switch

以下がその後に表示されるメッセージです。途中でエラーらしきメッセージ(bad exit status: 2)が2つ出ますが、その後、動作に問題は見つかっていません。無視できるメッセージのようです。

パッケージリストを読み込んでいます... 完
依存関係ツリーを作成しています... 完
状態情報を読み取っています... 完
以下の特別パッケージがインストールされます:
  binutils dkms fakeroot gcc gcc-4.6 libc-dev-bin libc6-dev libgomp1
  libquadmath0 linux-libc-dev make manpages-dev openvswitch-common
  openvswitch-datapath-dkms
提案パッケージ:
  binutils-doc gcc-multilib autoconf automake1.9 libtool flex bison gdb
  gcc-doc gcc-4.6-multilib libmudflap0-4.6-dev gcc-4.6-doc gcc-4.6-locales
  libgcc1-dbg libgomp1-dbg libquadmath0-dbg libmudflap0-dbg binutils-gold
  glibc-doc make-doc ethtool
以下のパッケージが新たにインストールされます:
  binutils dkms fakeroot gcc gcc-4.6 libc-dev-bin libc6-dev libgomp1
  libquadmath0 linux-libc-dev make manpages-dev openvswitch-common
  openvswitch-datapath-dkms openvswitch-switch
アップグレード: 0 個、新規インストール: 15 個、削除: 0 個、保留: 0 個。
22.0 MB のアーカイブを取得する必要があります。
この操作後に追加で 66.5 MB のディスク容量が消費されます。
続行しますか [Y/n]? Y

     【途中省略】

openvswitch-datapath-dkms (1.4.0-1ubuntu1.3) を設定しています ...

Creating symlink /var/lib/dkms/openvswitch/1.4.0/source ->
		 /usr/src/openvswitch-1.4.0

DKMS: add completed.

Kernel preparation unnecessary for this kernel.  Skipping...

Building module:
cleaning build area....(bad exit status: 2)
./configure --with-linux=/usr/src/linux-headers-3.2.0-37-generic ; make -C datapath/linux.................
cleaning build area....(bad exit status: 2)

DKMS: build completed.

openvswitch_mod:
Running module version sanity check.
 - Original module
   - No original module exists within this kernel
 - Installation
   - Installing to /lib/modules/3.2.0-37-generic/updates/dkms/

brcompat_mod.ko:
Running module version sanity check.
 - Original module
   - No original module exists within this kernel
 - Installation
   - Installing to /lib/modules/3.2.0-37-generic/updates/dkms/

depmod........

DKMS: install completed.
openvswitch-switch (1.4.0-1ubuntu1.3) を設定しています ...
 * Inserting openvswitch module
 * /etc/openvswitch/conf.db does not exist
 * Creating empty database /etc/openvswitch/conf.db
 * Starting ovsdb-server
 * Configuring Open vSwitch system IDs
 * Starting ovs-vswitchd
 * Enabling gre with iptables
libc-bin のトリガを処理しています ...
ldconfig deferred processing now taking place

サービスのステータスを確認してみると起動しています

$ service openvswitch-switch status
ovsdb-server is running with pid 8980
ovs-vswitchd is running with pid 8989

■ステップ4. Open vSwitch にブリッジ設定を追加する

新規にブリッジをつくります。ここでは、br0 という名前にしています。

$ sudo ovs-vsctl add-br br0

作ったブリッジに OpenFlow のポートとして使うインタフェースを追加します:

$ sudo ovs-vsctl add-port br0 eth1
$ sudo ovs-vsctl add-port br0 eth2

追加されていることを確認してみます:

$ sudo ovs-vsctl show
94f17419-c73c-4592-9ee9-539f90d1057b
    Bridge "br0"
	Port "eth1"
	    Interface "eth1"
	Port "eth2"
	    Interface "eth2"
	Port "br0"
	    Interface "br0"
		type: internal
    ovs_version: "1.4.0+build0"

■ステップ5. OpenFlowコントローラの LAN セグメントに接続

このステップはご使用の環境によって色々なやり方があると思います。

今回の環境では OpenFlow コントローラはインターネット接続できる LAN とは別の独立した LAN としており、OpenFlow スイッチ側の IPアドレスは192.168.2.20 として設定します。
まず、LAN ケーブルを引き抜きます。

次に、内蔵 LAN である eth0 をネットワークマネージャの設定画面で「手動」として192.168.2.20 に固定のIPアドレスで設定します。
これで、インターネットとは通信できなくなりますが、必要な時はまた設定を DHCP に戻してインターネットにつなげれば良いだけなので問題ありません。

設定後、LAN ケーブルを OpenFlow コントローラ側の LAN セグメントにつないで、OpenFlow コントローラの IP アドレスに対して ping するなどして通信確認をしておきます。

■ステップ6. OpenFlow コントローラを指定する

今回は OpenFlow コントローラは、IPアドレス 192.168.2.10、ポートはデフォルト、TCP での接続として設定しています。

コントローラの設定を表示させても最初は何も表示されません:

$ sudo ovs-vsctl get-controller br0

設定を追加します:

$ sudo ovs-vsctl set-controller br0 tcp:192.168.2.10

追加後に再度コントローラの情報を表示させた結果:

$ sudo ovs-vsctl get-controller br0
tcp:192.168.2.10

Open vSwitch の情報を表示させるとコントローラの設定も確認できます:

$ sudo ovs-vsctl show
94f17419-c73c-4592-9ee9-539f90d1057b
    Bridge "br0"
	Controller "tcp:192.168.2.10"
	Port "eth1"
	    Interface "eth1"
	Port "eth2"
	    Interface "eth2"
	Port "br0"
	    Interface "br0"
		type: internal
    ovs_version: "1.4.0+build0"

■ステップ7. OpenFlow コントローラとの接続確認

ここでは OpenFlow コントローラ用に、OpenFlow フレームワークの Trema を用意しました。
以前の記事で紹介した5行で書いたリピーターハブ を用いました。

Trema 起動前で未接続のときのフローエントリは空っぽです:

$ sudo ovs-ofctl dump-flows br0
NXST_FLOW reply (xid=0x4):

Trema を起動し接続後のときのフローエントリの表示:

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

■ステップ8. ホストと LAN ケーブルでつなぐ

クロスケーブルをつかって、USB LAN アダプタとホストを直接つなぎます。

今回は、Lubuntu 12.04 を入れた PC を2台用意して接続しました。IPアドレスは、192.168.0.1 と 192.168.0.2 としました。

オートネゴシエーションやLINK状態の変化を確認したかったので両ホストとも起動した状態でケーブルを接続します。変化したところを赤字にしてあります。

接続前の状態:

$ sudo ovs-ofctl show br0
OFPT_FEATURES_REPLY (xid=0x1): ver:0x1, dpid:0000000ec6f02e32
n_tables:255, n_buffers:256
features: capabilities:0xc7, actions:0xfff
 1(eth1): addr:00:0e:c6:f0:2e:32
     config:	 0
     state:	 LINK_DOWN
     current:	 10MB-HD AUTO_NEG
     advertised: 10MB-HD 10MB-FD 100MB-HD 100MB-FD COPPER AUTO_NEG
     supported:  10MB-HD 10MB-FD 100MB-HD 100MB-FD COPPER AUTO_NEG
 2(eth2): addr:00:0e:c6:f0:4b:26
     config:	 0
     state:	 LINK_DOWN
     current:	 10MB-HD AUTO_NEG
     advertised: 10MB-HD 10MB-FD 100MB-HD 100MB-FD COPPER AUTO_NEG
     supported:  10MB-HD 10MB-FD 100MB-HD 100MB-FD COPPER AUTO_NEG
 LOCAL(br0): addr:00:0e:c6:f0:2e:32
     config:	 PORT_DOWN
     state:	 LINK_DOWN
OFPT_GET_CONFIG_REPLY (xid=0x3): frags=normal miss_send_len=0

2台のホストを OpenFlowスイッチに接続後の状態:

$ sudo ovs-ofctl show br0
OFPT_FEATURES_REPLY (xid=0x1): ver:0x1, dpid:0000000ec6f02e32
n_tables:255, n_buffers:256
features: capabilities:0xc7, actions:0xfff
 1(eth1): addr:00:0e:c6:f0:2e:32
     config:	 0
     state:	 0
     current:	 100MB-FD AUTO_NEG
     advertised: 10MB-HD 10MB-FD 100MB-HD 100MB-FD COPPER AUTO_NEG
     supported:  10MB-HD 10MB-FD 100MB-HD 100MB-FD COPPER AUTO_NEG
 2(eth2): addr:00:0e:c6:f0:4b:26
     config:	 0
     state:	 0
     current:	 100MB-FD AUTO_NEG
     advertised: 10MB-HD 10MB-FD 100MB-HD 100MB-FD COPPER AUTO_NEG
     supported:  10MB-HD 10MB-FD 100MB-HD 100MB-FD COPPER AUTO_NEG
 LOCAL(br0): addr:00:0e:c6:f0:2e:32
     config:	 PORT_DOWN
     state:	 LINK_DOWN
OFPT_GET_CONFIG_REPLY (xid=0x3): frags=normal miss_send_len=0

接続前後で、state の箇所が変わって、LINK Up/Down を認識しています。また、100MB-FD つまり 100MB 全二重(Full Duplex)で接続できています。

■ステップ9. ホスト間での ping 確認

2台のホストで互いに相手めがけて ping を実行し応答があることを確認します。

これで完了!
さて、どれくらい性能が出るのか気になるところ。次回は測定結果を書いてみたいと思います。

■補足
(1) ovs-vsctl での設定はOS起動後も残っています。
(2) インターネット未接続で使うなら Lubuntu の設定をこうしたら良いということがあるかもしれません。気がついたら追記しておきます。

■おまけ: 安上がりにすませるには

この記事を見てやってみたいなと思った人がいたとして、マシンの確保が困ると思い代替案を考えてみました。

1) OpenFlow コントローラ用のPC。これは、別マシンにしなくてもOpenFlow スイッチ側に Trema をインストールしてしまえば不要です。その場合、Open vSwitch に OpenFlow コントローラを設定をする箇所で tcp:127.0.0.1 とローカルループバックを指定すれば良い。

2) 残るは、ホスト2台を用意するところ。このホストも仮想マシンで動かしてしまえば良い。ホスト用に2つ USB LAN アダプタを用意しておき、VMwarePlayer か VMwareWorkstation で仮想マシンをつくり、その USB LAN アダプタを割り当てる。軽量 Linux を使えばメモリの使用量もおさえられて複数起動も困らないでしょう。

...

と、そこまでするなら OpenFlow スイッチの作成も USB LAN アダプタは使わず、すべてVMware のなかだけですませてしまうという方法もありですね...

2013年2月 3日 (日)

Trema の仮想ネットワーク機能には『OpenFlow実践入門』に書いてない強力な機能がありました。

『OpenFlow実践入門』(初版)の第10章と第11章で解説されている「シンプルなルーター」のテストをするには実機のホストを必要とします。しかし、実機不要でテストする方法があることが分かりました。

実機のホストの代わりに、ネットワークネームスペース機能を利用します。ただし、この機能を使うには、Ubuntu 12.04 以降のバージョンが必要だそうです。

ネットワークネームスペース機能は書籍では全く触れていないのですが、trema help でコマンドラインのヘルプを表示させると netns というサブコマンドがあり、気になってググってみました。すると、開発者の方のブログで仮想ネットワークで任意のアプリを動かすという記事がヒットして発見しました。

これ、すごく便利で強力な機能です!

「シンプルなルーター」のテスト環境、および実行方法は「11.4 実行してみよう」(P.183~) に記載があります。ここの仮想ネットワークをネットワークネームスペースを用いたものに置き換えて試します。

■仮想ネットワーク定義

netns のところがネットワークネームスペースで、仮想ホストのように IP アドレスを割り当てるだけでなく、さらにルーティング情報も追加できます。

今回は、デフォルトルートを設定としたので "0.0.0.0" にしましたが、特定のネットワークを指定する場合は "192.168.2.0/24" のようにビットマスクも付けて指定します。

vswitch( "switch" ) {
  datapath_id "0x1"
}
netns( "host1" ) {
  ip "192.168.1.2"
  netmask "255.255.255.0"
  route :net => "0.0.0.0", :gw => "192.168.1.1"
}
netns( "host2" ) {
  ip "192.168.2.2"
  netmask "255.255.255.0"
  route :net => "0.0.0.0", :gw => "192.168.2.1"
}
link "switch", "host1"
link "switch", "host2"

■ネットワークネームスペース内でシェル起動

Trema で仮想ネットワークを起動後に以下のようにして該当するネットワークネームスペース内でシェルを起動します。以下のコマンドでは先ほどの仮想ネットワークの定義で記述した host1 を起動しています。

$ trema netns host1

コマンドを実行すると新たにシェルが起動します。ここで実行されるコマンドはすべてこのネットワークネームスペース内に閉じたネットワーク環境で動きます。

■host1 での実行結果

まずは、ネットワーク環境を確認してみます。

インタフェース情報:

# ifconfig
lo	  Link encap:ローカルループバック
	  inetアドレス:127.0.0.1  マスク:255.0.0.0
	  inet6アドレス: ::1/128 範囲:ホスト
	  UP LOOPBACK RUNNING  MTU:16436  メトリック:1
	  RXパケット:0 エラー:0 損失:0 オーバラン:0 フレーム:0
	  TXパケット:0 エラー:0 損失:0 オーバラン:0 キャリア:0
	  衝突(Collisions):0 TXキュー長:0
	  RXバイト:0 (0.0 B)  TXバイト:0 (0.0 B)
trema0-1  Link encap:イーサネット  ハードウェアアドレス 86:5e:1c:38:95:28
	  inetアドレス:192.168.1.2  ブロードキャスト:192.168.1.255  マスク:255.255.255.0
	  inet6アドレス: fe80::845e:1cff:fe38:9528/64 範囲:リンク
	  UP BROADCAST RUNNING MULTICAST  MTU:1500  メトリック:1
	  RXパケット:0 エラー:0 損失:0 オーバラン:0 フレーム:0
	  TXパケット:6 エラー:0 損失:0 オーバラン:0 キャリア:0
	  衝突(Collisions):0 TXキュー長:1000
	  RXバイト:0 (0.0 B)  TXバイト:468 (468.0 B)

ルーティング情報:

# netstat -r
カーネルIP経路テーブル
受信先サイト	ゲートウェイ	ネットマスク   フラグ	MSS Window  irtt インタフェース
default 	192.168.1.1	0.0.0.0 	UG	  0 0	       0 trema0-1
192.168.1.0	*		255.255.255.0	U	  0 0	       0 trema0-1

書籍と同様に ping を実行していきます。

# ping 192.168.1.1
PING 192.168.1.1 (192.168.1.1) 56(84) bytes of data.
64 bytes from 192.168.1.1: icmp_req=2 ttl=64 time=40.0 ms
64 bytes from 192.168.1.1: icmp_req=3 ttl=64 time=34.3 ms
64 bytes from 192.168.1.1: icmp_req=4 ttl=64 time=22.1 ms
^C
--- 192.168.1.1 ping statistics ---
4 packets transmitted, 3 received, 25% packet loss, time 3011ms
rtt min/avg/max/mdev = 22.125/32.200/40.081/7.495 ms

# ping 192.168.2.2
PING 192.168.2.2 (192.168.2.2) 56(84) bytes of data.
64 bytes from 192.168.2.2: icmp_req=2 ttl=64 time=40.4 ms
64 bytes from 192.168.2.2: icmp_req=3 ttl=64 time=27.7 ms
64 bytes from 192.168.2.2: icmp_req=4 ttl=64 time=0.978 ms
64 bytes from 192.168.2.2: icmp_req=5 ttl=64 time=1.60 ms
^C
--- 192.168.2.2 ping statistics ---
5 packets transmitted, 4 received, 20% packet loss, time 4003ms
rtt min/avg/max/mdev = 0.978/17.684/40.457/17.005 ms

このときの arp キャッシュは、以下のようにルータ側がキャッシュされています:

# arp -a
? (192.168.1.1) at 00:00:00:01:00:01 [ether] on trema0-1

■終了手順

trema netns で起動したシェルを先に終了してから trema のコントローラ側を終了してください。

たとえば、host1 を起動したまま、trema コントローラ側を Ctrl-C で停止した場合、以下のように削除できないとのメッセージがでます:

Cannot remove /var/run/netns/host1: Device or resouce busy
Command 'sudo ip netns delete host1' failed!

ip netns list コマンドでネームスペースの一覧を確認できますのでゴミが残ってしまった場合は手動で削除してください。

以下は、ゴミが残ってしまっていた場合:

$ ip netns list
host2
host1

この場合、以下のように手動で削除:

$ sudo ip netns delete host1
$ sudo ip netns delete host2

■おまけ:確認に用いた環境

(1)  OS: Lubuntu 12.04 Desktop (i386)

  パッケージで入れた trema version 0.3.5

(2)  OS: Lubuntu 12.10 Desktop (i386)

  パッケージで入れた trema version 0.3.3

2013年2月 2日 (土)

Trema の仮想ネットワークの落とし穴。ARPなしで通信してしまう

 

[ 2013年2月3日 追記。 回避策がありました。新しい OS を必要としますが、こちらに書いたネットワークネームスペース機能を使うことでARPも含んだテストが仮想ネットワーク機能でもできます。 ]


OpenFlow フレームワークの Trema 用に書いた5行のリピーターハブを改造して、UDPのポート番号で通信を制限するファイアウォールのようなものを作ろうとして落とし穴にはまりました。失敗事例紹介です。

■教訓

   Trema の仮想ネットワーク機能で仮想ホスト同士の通信ができても現実のネットワークで通信できるとは限らない。
   ARP が通るフローを定義しているか確認せよ。

  目的によっては ARP だけでなく、例えば ICMP Echo を通すべきかなども考慮しないといけないかもしれません。

■失敗コード

やろうとしたことは宛先がUDPポート60000のパケットだけを通すというものです。

class Test1 < Controller
  def switch_ready( datapath_id )
    send_flow_mod_add(
      datapath_id,
      :match => Match.new(
        :dl_type => 0x0800, # IP
        :nw_proto => 17, # UDP
        :tp_dst => 60000 # UDP Port
      ),
      :actions => SendOutPort.new( OFPP_FLOOD )
    )
  end
end

マッチングルールを書くにあたって、書籍『OpenFlow ネットワーク入門』(初版、コロナ社)のP.136 に記載の「表6.10 Matchクラスのインスタンス化オプションのキー」を参考にしました。:dl_type も書かないとダメだということはこの本のおかげで気がつきました。

■仮想ネットワークでのテスト

きちんと通信できました。

仮想ネットワーク定義:

vswitch("vsw1") {
  datapath_id "0x1"
}
vhost("host2") {
  ip "192.168.0.2"
}
vhost("host1") {
  ip "192.168.0.1"
}
link "vsw1", "host2"
link "vsw1", "host1"

実行結果:

$ trema dump_flow vsw1 
NXST_FLOW reply (xid=0x4):
 cookie=0x1, duration=227.119s, table=0, n_packets=1, n_bytes=64, priority=65535,
udp,tp_dst=60000 actions=FLOOD
$ trema show_stats host2
Sent packets:

Received packets:

$ trema send_packets --source host1 --dest host2 --tp_dst 50000
$ trema show_stats host2Sent packets:

Received packets:

$ trema send_packets --source host1 --dest host2 --tp_dst 60000
$ trema show_stats host2Sent packets:

Received packets:
ip_dst,tp_dst,ip_src,tp_src,n_pkts,n_octets
192.168.0.2,60000,192.168.0.1,1,1,50

host2 めがけてUDPポート50000で通信した時には host2 の統計情報は空ですがUDPポート60000だとパケットが受信されています。

■リアルの世界では

2台のLinuxで nc コマンドで通信を試しました。

・構成:

     [Linux(IP: 10.0.0.3)] --- [OpenFlowスイッチ] --- [Linux(IP: 10.0.0.4)]

・1台目(IPアドレス 10.0.0.4): udp 60000 で待ち受け

 実行コマンド:

$ nc -u -l 60000

これでUDPポート60000でデータが来るのを待ちデータが来たら表示するようにしておきます。

・2台目(IPアドレス 10.0.0.3): udp 60000 めがけて送信

  実行コマンド:

$ nc -u 10.0.0.4 60000
aaaa  --- 入力して改行
bbbb  --- 入力して改行

 2台目にて、nc コマンドを実行すると標準入力からの入力待ちになるので、文字を入力して改行するとその行が1台目側で表示されるはずですが、何もでません。

 2台目側で arp -a コマンドで確認してみると...解決できていない

$ arp -a
? (10.0.0.4) at <incomplete> on eth0

本当なら <imcomplete> のところは MAC アドレスがでるべきです。

■修正後のコード

  ARP が通るようにフローエントリを追加しました。

class Test1 < Controller
  def switch_ready( datapath_id )
    send_flow_mod_add(
      datapath_id,
      :match => Match.new(
        :dl_type => 0x0806 # ARP
      ),
      :actions => SendOutPort.new( OFPP_FLOOD )
    )
    send_flow_mod_add(
      datapath_id,
      :match => Match.new(
        :dl_type => 0x0800, # IP
        :nw_proto => 17, # UDP
        :tp_dst => 60000 # UDP Port
      ),
      :actions => SendOutPort.new( OFPP_FLOOD )
    )
  end
end

このコードに変更して、2台で再度ncコマンドを起動し直したらうまく動きました。

« 2013年1月 | トップページ | 2013年3月 »

最近のトラックバック

無料ブログはココログ