ラベル 小野秀貴-OpenFlow関連記事 の投稿を表示しています。 すべての投稿を表示
ラベル 小野秀貴-OpenFlow関連記事 の投稿を表示しています。 すべての投稿を表示

2012-08-09

OpenBlocks AX3でOpenFlowを試してみる

本文中に記載のLinuxソースコードは、開発段階のものです。
実際にお試しになる場合は、正式公開するバージョンに適宜読み替えて下さい。
また検証環境では、ストレージ併用モードを使用しています。


◆はじめに

以前OpenBlocks 600DにOpenvSwitchを導入しました。今回はOpenBlocks AX3に導入してみてどの程度の性能がでるか確認を行います。
さらに、以前OpenFlow ControllerとしてTremaを試してみましたが、今回はOepnFlowコントローラのNOXおよびPOXを試してみます。


◆OpenBlocks AX3へのOpen vSwitch導入

まずはopenvswitchコンパイルの準備としてlibssl-dev, pkg-configパッケージをインストールします。
 # aptitude install libssl-dev pkg-config
openvswitchの転送処理は前回同様カーネルモジュールで行うため、まずはlinuxカーネルのソースツリーが必要となります。
そこでソースを取得してbuildを行います。ちなみに、openvswitchのカーネルモジュールはlinux 3.3以降ではカーネルに含まれるようになりましたが、OpenBlocks AX3で採用しているlinux 3.0.6ではまだ含まれていませんので別途モジュール作成が必要となります。
 $ cd /tmp
 $ wget ftp://ftp.plathome.co.jp/pub/OBSAX3/CURRENT-20120703/source/ax3/linux-3.0.6.tar.gz
 $ su
 # cd /usr/src
 # tar zxvf /tmp/linux-3.0.6.tar.gz
 (今回はAX3上でtarの展開がうまく行かなかったため他のマシンで展開したものをコピーして利用しました)
 # ln -s linux-3.0.6 linux
 # cd linux
 # make obsax3_defconfig
 # make menuconfig
 # make uImage modules
 # make modules_install
つぎにopenvswitchのソースを取得してbuildを行います。今回は7月29日時点での最新版の1.6.1を使用しました。
 $ wget http://openvswitch.org/releases/openvswitch-1.6.1.tar.gz
 $ tar zxvf openvswitch-1.6.1.tar.gz
 $ cd openvswitch-1.6.1
 $ ./configure --with-linux=/lib/modules/`uname -r`/build
 $ make
makeが問題なく通ったらmake installを行います。/usr/local以下にインストールされます。
さらに作成したモジュールをinsmodします。
 $ su
 # make install
 # insmod datapath/linux/openvswitch_mod.ko
特にエラーメッセージ等が出なければ準備完了となります。

次にopenvswitchの基本動作を確認します。
まずはデータベースを新たに作成しデータベースサーバを起動、初期化をします。
続いてovs-vswitchdデーモンを起動します。
 # mkdir -p /usr/local/etc/openvswitch
 # ovsdb-tool create /usr/local/etc/openvswitch/conf.db vswitchd/vswitch.ovsschema
 2012-07-30T02:18:38Z|00001|lockfile|INFO|/usr/local/etc/openvswitch/.conf.db.~lock~: lock file does not exist, creating

 # ovsdb-server --remote=punix:/usr/local/var/run/openvswitch/db.sock \
                     --remote=db:Open_vSwitch,manager_options \
                     --private-key=db:SSL,private_key \
                     --certificate=db:SSL,certificate \
                     --bootstrap-ca-cert=db:SSL,ca_cert \
                     --pidfile --detach

 # ovs-vsctl --no-wait init

 # ovs-vswitchd --pidfile --detach
 2012-07-30T02:19:50Z|00001|reconnect|INFO|unix:/usr/local/var/run/openvswitch/db.sock: connecting...
 2012-07-30T02:19:50Z|00002|reconnect|INFO|unix:/usr/local/var/run/openvswitch/db.sock: connected
基本動作としてeth0, eth1, eth2, eth3をブリッジさせる動作を確認します。
 # ovs-vsctl add-br br0
 # ovs-vsctl add-port br0 eth0
 # ovs-vsctl add-port br0 eth1
 # ovs-vsctl add-port br0 eth2
 # ovs-vsctl add-port br0 eth3
 # ifconfig br0 up
ここまでで準備は完了です。


◆OpenBlocks AX3でのOpen vSwitch性能測定

検証用のネットワーク構成は以下のようにします。
















OBSAX3#1でopenvswitchを動作させた状態でOBSAX3#2からPC#1へのpingやsshできることを確認します。

基本動作が確認できたところでOBSAX3#1のopenvswitchの性能測定を行います。
OBSAX3#2とPC#1間でnuttcpを使用して確認します。なお確認はmtu 1500のみで行いました。

まずPC#1でnuttcpをサーバモードで起動します。
 PC-1# nuttcp -S
そして、OBSAX3#2ではnuttcpをクライアントモードで起動し、10秒間測定します。
 obsax3-2# nuttcp -i1 -T10 192.168.111.14
 1119.3750 MB /  10.02 sec =  937.1064 Mbps 35 %TX 24 %RX 0 retrans 0.75 msRTT
結果は上記のように約940Mbpsの性能となりました。openvswitchなしでPC#1とobsax3#2を直結した場合でもほぼ同様の結果となりました。
測定に使用した機材の関係上この性能までしか確認できませんでしたががまだ余力がありそうな感じです。


◆Openflowパケット解析用のwireshark pluginをインストール

Openflowパケット解析のためにwiresharkのpluginをインストールします。
その前にまずはwiresharkパッケージをインストールします。
 $ su
 # aptitude install wireshark
次にpluginのソースを取得しbuildします。
 $ git clone git://gitosis.stanford.edu/openflow.git
 $ cd openflow/utilities/wireshark_dissectors/
 $ make
 # make install
make installを行うと/usr/lib/wireshark/plugins/packet-openflow.so
がインストールされwireshark, tsharkコマンドでこのpluginが利用可能となります。


◆OpenFlowコントローラNOXとPOXについて

NOXは最近VMware社に買収されたことでも話題になったNicira社がオープンソースとして公開しているOpenFlowコントローラです。
以前はC++およびPythonをサポートしていましたが、最近はC++のみのNOX、PythonのみのPOXと別物になっています。
以前のC++,Python両方をサポートしたNOXはNOX Classicという名前になっています。


















新しいNOX(C++版)をbuildするためにはlibtbbが必要となります。ところがlibtbbはIntelプロセッサのみのサポートとなっていますので、ARMプロセッサのOpenBlocks AX3では利用できません。
そこで今回は、NOX ClassicおよびPOXの二つのコントローラを試してみることにします。
なお、今回もOpen vSwitchを動作させるマシンとOpenflowコントローラを動作させるマシンは同一のマシン(OBSAX3#1)とします。


◆NOX Classicの導入

まずはNOX Classicのコンパイルに必要なパッケージをインストールします。
 $ su
 # aptitude install doxygen build-essential autoconf automake make g++ libtool python libssl-dev libboost-dev
 # aptitude install libboost-filesystem-dev libboost-test-dev libboost-serialization-dev python-twisted swig
続いてNOX Classicのソースをダウンロードしてbuildします。
今回利用したバージョンはcommit aee2a3fb696a497a87a30cfc3285d142a464cbc0 です。
 $ cd ~
 $ git clone http://github.com/noxrepo/nox-classic.git
 $ cd nox-classic
 $ ./boot.sh
 $ mkdir build
 $ cd build
 $ ../configure
 $ make
 $ cd src
 $ make check
make checkで特に問題なければ導入は完了です。


◆NOX Classicを動かしてみる

NOXにはスイッチ動作を行うサンプルが附属しているので試してみます。
C++版は ~/nox-classic/build/src/nox/coreapps/switch/ にあります。
 $ cd ~/nox-classic/build/src
 $ ./nox_core -v -i ptcp:6633 switch
これでOpenflowコントローラが起動しました。
次にOpen vSwitchをOpenflowコントローラに接続します。
 $ su
 # ovs-vsctl set-controller br0 tcp:127.0.0.1
OBSAX3#2からPC#1へpingを行って応答が返ることを確認します。
 obsax3-2$ ping -n 192.168.111.14
 PING 192.168.111.14 (192.168.111.14) 56(84) bytes of data.
 64 bytes from 192.168.111.14: icmp_req=1 ttl=64 time=8.05 ms
 64 bytes from 192.168.111.14: icmp_req=2 ttl=64 time=0.502 ms
 64 bytes from 192.168.111.14: icmp_req=3 ttl=64 time=0.347 ms
 64 bytes from 192.168.111.14: icmp_req=4 ttl=64 time=0.348 ms
このときtsharkでOpenflowパケットを観察してみると以下のようになりました。
 $ su
 # tshark -R of -i lo port 6633
   1.089456 PlatC2_00:05:35 -> Broadcast    OFP+ARP Packet In (AM) (BufID=8105) (78B) => Who has 192.168.111.14?  Tell 192.168.111.221
   1.089771    127.0.0.1 -> 127.0.0.1    OFP Packet Out (CSM) (BufID=8105) (24B)
   1.090701 XX:XX:XX:XX:XX:XX -> PlatC2_00:05:35 OFP+ARP Packet In (AM) (BufID=8106) (78B) => 192.168.111.14 is at XX:XX:XX:XX:XX:XX
   1.090953    127.0.0.1 -> 127.0.0.1    OFP Flow Mod (CSM) (80B)
   1.092047 192.168.111.221 -> 192.168.111.14 OFP+ICMP Packet In (AM) (BufID=8107) (116B) => Echo (ping) request
   1.092298    127.0.0.1 -> 127.0.0.1    OFP Flow Mod (CSM) (80B)
   1.093204 192.168.111.14 -> 192.168.111.221 OFP+ICMP Packet In (AM) (BufID=8108) (116B) => Echo (ping) reply
   1.093452    127.0.0.1 -> 127.0.0.1    OFP Flow Mod (CSM) (80B)

◆POXの導入

POXの利用には正式にはPython 2.7が必要なようですが、OpenBlocks AX3(Debian Squeeze)にインストールされているPython 2.6.6 でも動くようなのでこれを利用します。

Python版のPOXはC++版のNOXに比べて必要なものが少ないため導入が簡単です。
POXのソースをダウンロードします。今回利用したものはcommit f95dd1a81584d716823bbf565fa68254416af603 です。
 $ cd ~
 $ git clone http://github.com/noxrepo/pox.git
全てpythonなのでコンパイルは不要ですぐに使用できます。


◆POXを動かしてみる

まずはOpen vSwitchをNOXから切り離しNOXをCtrl-Cで中断します。
 $ su
 # ovs-vsctl del-controller br0

 # ./nox_core ... (Ctrl-C)
準備ができたのでPOXに附属のl2スイッチを試してみます。
参考までにソースは~/pox/pox/forwarding/l2_learning.py です。
 $ cd ~/pox
 $ ./pox.py forwarding.l2_learning
 POX 0.0.0 / Copyright 2011 James McCauley
 DEBUG:core:POX 0.0.0 going up...
 DEBUG:core:Running on CPython (2.6.6/Dec 27 2010 21:57:32)
 INFO:core:POX 0.0.0 is up.
 This program comes with ABSOLUTELY NO WARRANTY.  This program is free software,
 and you are welcome to redistribute it under certain conditions.
 Type 'help(pox.license)' for details.
 DEBUG:openflow.of_01:Listening for connections on 0.0.0.0:6633
 Ready.
 POX>
これでOpenflowコントローラPOXが起動しました。
次にOpen vSwitchをOpenflowコントローラに接続します。
 $ su
 # ovs-vsctl set-controller br0 tcp:127.0.0.1
OBSAX3#2からPC#1へpingを行って応答が返ることを確認します。
 obsax3-2$ ping -n 192.168.111.14
 PING 192.168.111.14 (192.168.111.14) 56(84) bytes of data.
 64 bytes from 192.168.111.14: icmp_req=1 ttl=64 time=33.9 ms
 64 bytes from 192.168.111.14: icmp_req=2 ttl=64 time=0.787 ms
 64 bytes from 192.168.111.14: icmp_req=3 ttl=64 time=0.482 ms
 64 bytes from 192.168.111.14: icmp_req=4 ttl=64 time=0.471 ms
このときPOXを起動したコンソールには以下のようにDEBUGメッセージが表示されます。
 DEBUG:forwarding.l2_learning:installing flow for 00:0a:85:00:05:35.1 -> XX:XX:XX:XX:XX:XX.4
 DEBUG:forwarding.l2_learning:installing flow for XX:XX:XX:XX:XX:XX.4 -> 00:0a:85:00:05:35.1
またtsharkでOpenflowパケットを観察してみると以下のようになりました。
  4.554923 PlatC2_00:05:35 -> Broadcast    OFP+ARP Packet In (AM) (BufID=281) (78B) => Who has 192.168.111.14?  Tell 192.168.111.221
  4.580751    127.0.0.1 -> 127.0.0.1    OFP Packet Out (CSM) (BufID=281) (16B)
  5.554998 PlatC2_00:05:35 -> Broadcast    OFP+ARP Packet In (AM) (BufID=282) (78B) => Who has 192.168.111.14?  Tell 192.168.111.221
  5.590975    127.0.0.1 -> 127.0.0.1    OFP Packet Out (CSM) (BufID=282) (24B)
  5.591595 XX:XX:XX:XX:XX:XX -> PlatC2_00:05:35 OFP+ARP Packet In (AM) (BufID=283) (78B) => 192.168.111.14 is at XX:XX:XX:XX:XX:XX
  5.595215    127.0.0.1 -> 127.0.0.1    OFP Flow Mod (CSM) (80B)
  5.595942 192.168.111.221 -> 192.168.111.14 OFP+ICMP Packet In (AM) (BufID=284) (116B) => Echo (ping) request
  5.595998 192.168.111.221 -> 192.168.111.14 OFP+ICMP Packet In (AM) (BufID=285) (116B) => Echo (ping) request
  5.600189    127.0.0.1 -> 127.0.0.1    OFP Flow Mod (CSM) (80B)
  5.600744 192.168.111.14 -> 192.168.111.221 OFP+ICMP Packet In (AM) (BufID=286) (116B) => Echo (ping) reply
  5.604300    127.0.0.1 -> 127.0.0.1    OFP Flow Mod (CSM) (80B)
  5.604831 192.168.111.14 -> 192.168.111.221 OFP+ICMP Packet In (AM) (BufID=287) (116B) => Echo (ping) reply
  5.608876    127.0.0.1 -> 127.0.0.1    OFP Flow Mod (CSM) (80B)
  5.639848    127.0.0.1 -> 127.0.0.1    OFP Flow Mod (CSM) (80B)
コントローラにはARPやICMPが2個ずつ届いていますがNOXとほぼ同様のフローとなっています。


◆まとめ

OpenBlocks AX3でOpen vSwitchを動作させてみました。転送性能は約940Mbpsであることを確認しました。
またOpenflowコントローラのNOX ClassicおよびPOXをOpenBlocks AX3上で動作できることを確認しました。

2012-04-04

小野秀貴: OpenFlow Controllerを用いてマルチホーム環境を活用する

はじめに

前回はOpenFlowスイッチの実装であるOpen vSwitchを動かしてみまし
た。今回はOpenFlowコントローラを導入して、Open vSwitchと接続
して制御してみます。

OpenFlowコントローラにはNOX, Tremaなどいくつかのオープンソース
実装があります。今回はおもにNECで開発されているTremaを利用して
みます。Rubyで容易に記述できるらしいということから選んでみました。

今回の目標は、
「マルチホーム環境でopenvswitchの配下にあるクライアントからの
  上流へのアクセス時に、特定のTCPポート(今回はSMTP=25)の通信
  だけ異なるrouterを経由するようにする」
ということです。

前回はOpen vSwitch単体動作で上記を実現しましたが、今回はOpenFlow
コントローラ経由でこれを実現したいと思います。



◆OpenFlowコントローラTrema導入

まずはTremaの導入に必要なものをインストールします。
 # aptitude install gcc make ruby ruby-dev irb file libpcap-dev libsqlite3-dev rubygems rake arping
なおarpingはTrema導入には必要ないですが、今回利用するためイン
ストールしています。

今回、Tremaはgemを利用してインストールします。
ただ、そのまま試してみるとrake1.8がないというエラーになるため
以下の対処をしておきます。
 # cd /usr/bin
 # ln -s rake rake1.8
ようやくtremaのインストールです。
 # gem install trema
これにはOpenBlockS 600Dでは30分以上を要しました。

2012年3月11日時点ではtrema 0.2.2.1がインストールされました。
 # gem list trema
 *** LOCAL GEMS ***

 trema (0.2.2.1)
実行ファイルtremaは/var/lib/gems/1.8/binにインストールされるので
ここにPATHを通しておきます。
 # PATH=/var/lib/gems/1.8/bin:$PATH
れで準備は完了です。



TremaでOpen vSwitchを制御

まずはTremaを起動してみます。
Tremaにはいくつかサンプルが附属しています。
L2スイッチ機能を実現したlearning_switchというサンプルを試して
みることにします。
 # cd /var/lib/gems/1.8/gems/trema-0.2.2.1/src/examples/learning_switch
 # trema run learning-switch.rb
tremaのswitch_managerが起動して、OpenFlowプロトコルのデフォルトTCPポート
である6633でOpenFlowスイッチからの接続を待っている状態になります。
 # lsof -i:6633
 COMMAND     PID USER   FD   TYPE   DEVICE SIZE/OFF NODE NAME
 switch_ma 32074 root    5u  IPv4 11451496      0t0  TCP *:6633 (LISTEN)
通常はOpenFlowスイッチとOpenFlowコントローラは別ホストですが、
今回は同一ホスト(OpenBlockS 600D)でOpen vSwitchとTrema両者を実行します。
なお、tremaを起動したコンソールはアタッチされたままなので、別
コンソールでOpen vSwitchの作業を行います。

Open vSwitchは起動している状態になっているとします。
Open vSwitchからOpenFlowコントローラ(Trema)への接続はovs-vsctlの
set-controllerコマンドで行います。同一ホスト内なのでlocalhost(127.0.0.1)
に接続します。
 # ovs-vsctl set-controller br0 tcp:127.0.0.1
なお、コントローラからの離脱は
 # ovs-vsctl del-controller br0
で行います。

前回と同様に以下のようなネットワークを構成します。


tremaとopenvswitchはobs600-3で動いていて、openvswitchはtremaに
接続している状態になっているとします。

ここでobs600-1上でrouter-1へのpingを実行します。
 obs600-1# ping -c 1 192.168.100.1
このときtremaからopenvswitchへはARP REPLY, ICMP ECHO REQUEST,
ICMP ECHO REPLYの3つのフローが登録されています。なお、ARP REQUEST
についてはtremaのlearning-switch.rbでは出力先のポートが未定
なためこの時点ではフローは登録されません。
 # ovs-ofctl dump-flows br0
 NXST_FLOW reply (xid=0x4):
  cookie=0x2, duration=4.156s, table=0, n_packets=1, n_bytes=98, priority=65535,icmp,in_port=2,vlan_tci=0x0000,dl_src=00:0a:85:04:93:99,dl_dst=00:00:00:00:00:01,nw_src=192.168.100.123,nw_dst=192.168.100.1,nw_tos=0,icmp_type=8,icmp_code=0 actions=output:1
  cookie=0x3, duration=4.146s, table=0, n_packets=1, n_bytes=98, priority=65535,icmp,in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:01,dl_dst=00:0a:85:04:93:99,nw_src=192.168.100.1,nw_dst=192.168.100.123,nw_tos=0,icmp_type=0,icmp_code=0 actions=output:2
  cookie=0x1, duration=4.17s, table=0, n_packets=0, n_bytes=0, priority=65535,arp,in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:01,dl_dst=00:0a:85:04:93:99,nw_src=192.168.100.1,nw_dst=192.168.100.123,arp_op=2 actions=output:2
これでlearning-switch.rbが期待通りに動作しているのが確認できました。

Trema確認中に少し気になった点があります。TremaはCtrl-Cで終了
するのですが、switch_managerプロセスが動作したままで、次回の
trema起動がうまくいかない状況になりました。とりあえずtrema run
の前にkillall switch_managerを行うことで暫定対処としました。



コントローラ用コード作成

今回の目的の
「マルチホーム環境でopenvswitchの配下にあるクライアントからの
  上流へのアクセス時に、特定のTCPポート(今回はSMTP=25)の通信
  だけ異なるrouterを経由するようにする」
を実現することを考えます。

基本的にはL2スイッチなのでtremaサンプルのlearning-switch.rbを
ベースにします。
上記を実現するためのコードを少し加えます(modified-learning-switch.rb)。
なお、既存部分で変更を加えていない箇所は掲載を省略しています。

 class ModifiedLearningSwitch < Trema::Controller
  add_timer_event :age_fdb, 5, :periodic
  add_timer_event :periodic_arp_request, 10, :periodic


  def start
    @fdb = FDB.new
    @target_routes = []
    @target_routes.push({ :route_dst => "192.168.100.1",
                          :route_dst_modified => "192.168.100.2",
                          :ip_src => "192.168.100.123",
                          :port => 25})
    periodic_arp_request
  end

  def packet_in datapath_id, message
    @fdb.learn message.macsa, message.in_port
    matched_route = nil

    @target_routes.each do |route|
      if message.tcp? &&
         message.tcp_dst_port == route[:port] &&
         message.ipv4_saddr.to_s == route[:ip_src] &&
         message.macda.to_s == route[:mac_dst]
        matched_route = route
      end
    end

    port_no = @fdb.port_no_of( message.macda )
    if matched_route.nil?
      if port_no
        flow_mod datapath_id, message, port_no
        packet_out datapath_id, message, port_no
      else
        flood datapath_id, message
      end
    else
      if port_no
        flow_mod_dstmac datapath_id, message, port_no, matched_route[:mac_dst_modified]
        packet_out_dstmac datapath_id, message, port_no, matched_route[:mac_dst_modified]
      else
        flood_dstmac datapath_id, message, matched_route[:mac_dst_modified]
      end
    end
  end

  ##############################################################################
  private
  ##############################################################################

  def periodic_arp_request
    @target_routes.each do |route|
      reply=`arping -0 -c 1 -w 100 #{route[:route_dst]}`
      if /from (\w{2}:\w{2}:\w{2}:\w{2}:\w{2}:\w{2}) \(([\d\.]+)\)/ =~ reply
        info "arping " + $1 + "=" + $2
        route[:mac_dst] = $1
      end
      reply=`arping -0 -c 1 -w 100 #{route[:route_dst_modified]}`
      if /from (\w{2}:\w{2}:\w{2}:\w{2}:\w{2}:\w{2}) \(([\d\.]+)\)/ =~ reply
        info "arping " + $1 + "=" + $2
        route[:mac_dst_modified] = $1
      end
    end
  end

  def flow_mod_dstmac datapath_id, message, port_no, new_mac_dst
    send_flow_mod_add(
      datapath_id,
      :match => ExactMatch.from( message ),
      :actions => [
                Trema::ActionSetDlDst.new( :dl_dst => Mac.new( new_mac_dst )),
                Trema::ActionOutput.new( :port => port_no )
        ]      
    )
  end

  def packet_out_dstmac datapath_id, message, port_no, new_mac_dst
    send_packet_out(
      datapath_id,
      :packet_in => message,
      :actions => [
                Trema::ActionSetDlDst.new( :dl_dst => Mac.new( new_mac_dst )),
                Trema::ActionOutput.new( :port => port_no )
        ]      
    )
  end

  def flood_dstmac datapath_id, message, new_mac_dst
    packet_out_dstmac datapath_id, message, OFPP_FLOOD, new_mac_dst
  end
 end
ざっくり動作を説明します。

まずは変更点として、periodic_arp_request methodで定期的にarp requestをrouter-1
とrouter-2に送信してMACアドレスを解決しておきます。これは処理するパケットのDST MAC
がrouter-1のMACにマッチした場合にrouter-2のMACに変更するということを実現する
ために必要になるためです。

openvswitchでフローが未解決パケットが到着したときには、OpenFlow プロト
コルでtremaに送信します。そしてtremaではpacket_inメソッドが呼ばれます。
learning-switch.rbではpacket_inでは通常は宛先MACアドレスを学習している
場合は、
 1. フローをOpenFlowスイッチに登録(flow_mod)
 2. 受信したパケットを該当ポートへ出力(packet_out)
という処理を行います。

今回の変更点として、特定の条件(ip_src = 192.168.100.123, dst_port = 25)
にマッチする場合は、
 1. フローをOpenFlowスイッチに登録
    ただし、DST MACアドレスをrouter-2のものに変更(flow_mod_dstmac)
 2. 受信したパケットを該当ポートへ出力
    ただし、DST MACアドレスをrouter-2のものに変更(packet_out_dstmac)
という処理を行うように修正しました。



動作実験

上記で作成したmodified-learning-switch.rbを実行します。
 # trema run modified-learning-switch.rb
Open vSwitchをTremaに接続します。
 # ovs-vsctl set-controller br0 tcp:127.0.0.1
obs600-1から外部のホスト(10.0.0.1)にsmtp接続します。
 obs600-1# telnet 10.0.0.1 25
 Connected to 10.0.0.1.
 Escape character is '^]'.
 220 smtp.example.org. ESMTP Postfix
このときのOpen vSwitchのフローの状態を確認します。
 # ovs-ofctl dump-flows br0
  - 省略 -
  cookie=0x9, duration=5.993s, table=0, n_packets=5, n_bytes=336, priority=65535,tcp,in_port=2,vlan_tci=0x0000,dl_src=00:0a:85:04:93:99,dl_dst=00:00:00:00:00:01,nw_src=192.168.100.123,nw_dst=10.0.0.1,nw_tos=16,tp_src=50729,tp_dst=25 actions=mod_dl_dst:00:00:00:00:00:02,output:1
  - 省略 -
dump-flowsでの確認結果には、mod_dl_dstが表示されています。
また、10.0.0.1のmailログでもrouter-2経由でアクセスされたログを
確認できので期待通りの動作を実現できました。



まとめ
今回はOpenFlowコントーラのTremaを利用し、Open vSwitchをTremaに
接続してOpenFlowプロトコルでフローが設定されることを確認しました。

また、Tremaのコードに修正を加えることで、特定の条件にマッチする場合
には異なるノードへ転送するようなフローをOpen vSwitchに設定する
ことができることを確認しました。

2012-03-23

小野秀貴: Open vSwitch によるOpenFlow の利用

◆OpenFlow/Open vSwitchとは

OpenFlowは、1台のOpenFlowコントローラで複数のOpenFlowスイッチを制御する
仕組みです。OpenFlowスイッチは転送機能を提供します。OpenFlowコントロー
ラは経路制御をまとめて行います。OpenFlowスイッチは必要に応じて経路情報
をOpenFlowコントローラに問い合わせる形となります。OpenFlowコントローラ
とOpenFlowスイッチ間はOpenFlowプロトコルで通信が行われます。

Open vSwitchはオープンソースの仮想ソフトウェアスイッチで、XenServer,
KVM, VirtualBoxなどの仮想化プラットフォームでも使用されています。
Open vSwitchは仮想化プラットフォームと組み合わせなくても普通のLinux上で
動作可能です。Linux上で動作させる場合はユーザ空間でのパケット転送処理の
他にカーネルモジュールを利用して高速にパケット転送処理を行うことも可能
です。
またOpen vSwitchは設定によりOpenFlowに対応したOpenFlowスイッチとしての
動作も可能となっています。




◆動向/必要性

OpenFlow 1.0仕様は2009年12月、OpenFlow 1.1仕様は2011年2月にそれぞれリリー
スされました。2011年12月にIPv6のサポートなどが追加されたOpenFlow 1.2仕
様がONF(Open Networking Foundation)で承認されました。1.2はまだ出たばか
りなので現状では1.0/1.1が主流となっています。
OpenFlowは技術的には比較的新しく、またセキュリティ上の懸念なども議論さ
れていたりなどまだ実用段階には至っていません。一方、集中制御できること
から大規模データセンターなどでの利用が期待されています。

このようにOpenFlowは大規模データセンターなどをおもなターゲットとして検
討されているようですが、L2〜L4の情報に基いて細かい制御が可能ということ
で比較的小規模のネットワーク環境で効果的に使える可能性も考えられます。

Open vSwitchではバージョン1.4.0(2012年1月リリース)でOpenFlow 1.0
に加え、OpenFlow 1.1/1.2の一部の機能(IPv6など)に対応しています。



◆Open vSwitch導入

OpenBlockS 600DにOpen vSwitchを導入してみます。

まずはOpenBlockS 600Dでセルフ開発環境を整えます。
今回は、ファームウェア、および開発環境として以下を用いました。
 ftp://ftp.plathome.co.jp/pub/OBS600/debian/files/squeeze/2.6.29-4/uImage.initrd.squeeze
 ftp://ftp.plathome.co.jp/pub/OBS600/debian/files/squeeze/obs600d_squeeze_develop_edition_20110726.tgz
パッケージはaptitude upgradeで2012年2月21日時点での最新版にしました。
 # aptitude update
 # aptitude safe-upgrade
またopenvswitchコンパイルのための準備としてlibssl-dev, pkg-configパッケー
ジを追加しました。
 # aptitude install libssl-dev pkg-config
openvswitchでは、転送処理をカーネル(モジュール)で行う方法とユーザランド
で行う方法の2通りがありコンパイル時に選択します。今回は準備が多少面倒で
すが性能が出るであろう前者のカーネルモジュールを選択することにします。

カーネルモジュールを作成するためにモジュール生成のための環境が必要とな
るので、まずはその準備をします。

開発環境でカーネルのソースはすでに入っていますが、最新のファームと同じ
環境にするために、最新ファーム用のカーネルソースを取得します。
 # cd /usr/src
 # wget ftp://ftp.plathome.co.jp/pub/OBS600/debian/linux/linux-2.6.29-20111128-00.tgz
 # mv linux-2.6.29 linux-2.6.29.old
 # tar xvf linux-2.6.29-20111128-00.tgz
モジュール生成時にはautoconf.h等が必要になるためその準備をします。今回
はモジュールシンボル(Module.symvers)も生成したかったのでmake modulesま
で行いました。make vmlinux modulesは数時間を要します。確認はしていませ
んがmake prepareまで行えばopenvswitchはコンパイル可能かもしれません。
 # cd /usr/src/linux
 # wget -O /usr/src/linux/.config ftp://ftp.plathome.co.jp/pub/OBS600/debian/files/lenny/LATEST/dot.config
 # cd /usr/src/linux/include
 # rm asm
 # ln -s /usr/src/linux/arch/powerpc/include/asm .
 # make oldconfig
 # make prepare
 # make vmlinux modules
さて、これで準備は整ったのでようやくopenvswitchソフトウェアのコンパイル
にうつります。まずは2012年2月21日時点での最新版(1.4.0)のソースを取得し
展開します。
 # wget http://openvswitch.org/releases/openvswitch-1.4.0.tar.gz
 # tar zxvf openvswitch-1.4.0.tar.gz
powerpcではオリジナルのままだと一ヶ所だけ関数未定義でコンパイルを通らな
い箇所があるため以下の修正を加えました。
--- datapath/tunnel.c.orig    2012-01-31 16:08:18.000000000 +0900
+++ datapath/tunnel.c        2012-02-22 17:53:23.000000000 +0900
@@ -38,6 +38,7 @@
 #include <net/ip.h>
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 #include <net/ipv6.h>
+#include <net/ip6_checksum.h>
 #endif
 #include <net/route.h>
 #include <net/xfrm.h>
次にopenvswitchのmakeを行う。
 # cd openvswitch-1.4.0
 # ./configure --with-linux=/lib/modules/`uname -r`/build
 # make
makeが問題なく通ったらmake installを行います。デフォルトでは/usr/local
以下にインストールされます。さらに作成したモジュールをinsmodします。
 # make install
 # insmod datapath/linux/openvswitch_mod.ko
特にエラーメッセージ等が出なければ準備完了となります。




◆構成と設定ファイル初期化

openvswitchの構成は以下の図のようになります。






各種設定は設定用のデータベースで管理されます。データベースへのアクセス
はデータベースアクセス用デーモン(ovsdb-server) を通してアクセスされます。
その他のツールはovsdb-serverと通信して動作を行います。今回はunix domain
socketを用いる設定にしました。
ovs-vswitchdはスイッチのデータパスの設定等を行うメインのデーモンです。
ovs-vsctlはスイッチ関連の各種設定を行うコマンドです。

まずは、初期化を行います。
設定ファイル用のディレクトリを作成し、データベースを新たに作成します。
データベースはovsdb-toolコマンドを用いて生成します。
 # mkdir -p /usr/local/etc/openvswitch
 # ovsdb-tool create /usr/local/etc/openvswitch/conf.db vswitchd/vswitch.ovsschema
 Feb 22 09:50:54|00001|lockfile|INFO|/usr/local/etc/openvswitch/.conf.db.~lock~: lock file does not exist, creating
次にデータベースを操作するためのサーバを起動します。
データベースサーバへはunix domain socket(db.sock)を通してアクセスします。
 # ovsdb-server --remote=punix:/usr/local/var/run/openvswitch/db.sock \
                     --remote=db:Open_vSwitch,manager_options \
                     --private-key=db:SSL,private_key \
                     --certificate=db:SSL,certificate \
                     --bootstrap-ca-cert=db:SSL,ca_cert \
                     --pidfile --detach
ovs-vsctlコマンドでデータベースの初期化を行います。
 # ovs-vsctl --no-wait init
open vswitchのメインサーバ ovs-vswitchdを起動します。
 # ovs-vswitchd --pidfile --detach
 Feb 22 09:54:27|00001|reconnect|INFO|unix:/usr/local/var/run/openvswitch/db.sock: connecting...
 Feb 22 09:54:27|00002|reconnect|INFO|unix:/usr/local/var/run/openvswitch/db.sock: connected



◆Open vSwitch基本動作確認

最も単純なeth0, eth1 をブリッジさせる動作を確認します。

まずはブリッジデバイスを作成します。
 # ovs-vsctl add-br br0
 device br0 entered promiscuous mode

 # ifconfig br0
 br0       Link encap:Ethernet  HWaddr fe:b2:9d:a2:84:45 
           BROADCAST MULTICAST  MTU:1500  Metric:1
           RX packets:0 errors:0 dropped:0 overruns:0 frame:0
           TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
           collisions:0 txqueuelen:0
           RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)
次に、eth0, eth1 をこのブリッジのポートして追加します。
 # ovs-vsctl add-port br0 eth0
 device eth0 entered promiscuous mode
 # ovs-vsctl add-port br0 eth1
 device eth1 entered promiscuous mode
br0をupするとブリッジ動作を行うようになります。
 # ifconfig br0 up
ovs-vsctlコマンドでブリッジの情報を確認できます。
 # ovs-vsctl show
 a54365a7-c6a5-43b0-b920-bf56e53f37f5
    Bridge "br0"
        Port "eth1"
            Interface "eth1"
        Port "br0"
            Interface "br0"
                type: internal
        Port "eth0"
            Interface "eth0"
なお、注意点としてopenvswitchを導入したマシンでsshなどのIP通信を行うに
は、eth0, eth1ではなくbr0にIPアドレスをふる必要があります。
試しにDHCPでアドレスをふってみます。
 # dhclient br0
 # ifconfig br0
 br0       Link encap:Ethernet  HWaddr 00:0a:85:04:15:5f 
           inet addr:192.168.100.211  Bcast:192.168.100.255  Mask:255.255.255.0
           inet6 addr: fe80::20a:85ff:fe04:155f/64 Scope:Link
           UP BROADCAST RUNNING ALLMULTI MULTICAST  MTU:1500  Metric:1
           RX packets:833632 errors:0 dropped:0 overruns:0 frame:0
           TX packets:52700 errors:0 dropped:0 overruns:0 carrier:0
           collisions:0 txqueuelen:0
           RX bytes:90350673 (86.1 MiB)  TX bytes:18998763 (18.1 MiB)
動作確認のため3台のOpenBlockS 600D(OBS600D)を用いて以下のネットワークを構成しました。
obs600-3は上記でopenvswitchを導入済のマシンです。





















openvswitchのブリッジが動作をしているかをobs600-1からobs600-2へのpingで
確認します。
 obs600-1# ping 192.168.100.219
 PING 192.168.100.219 (192.168.100.219) 56(84) bytes of data.
 64 bytes from 192.168.100.219: icmp_req=1 ttl=64 time=116 ms
 64 bytes from 192.168.100.219: icmp_req=2 ttl=64 time=0.366 ms
 64 bytes from 192.168.100.219: icmp_req=3 ttl=64 time=0.307 ms
 64 bytes from 192.168.100.219: icmp_req=4 ttl=64 time=0.368 ms
openvswitchでは一方向のデータパスのテーブルをもとにスイッチングを行って
います。データパスの情報を確認するにはovs-dpctlを用います。
 # ovs-dpctl show
 system@br0:
    lookups: hit:2284446 missed:151252 lost:86
    flows: 6
    port 0: br0 (internal)
    port 1: eth0
    port 2: eth1
br0のデータパスを確認してみます。
 # ovs-dpctl dump-flows br0 |grep 192.168.100.123
 in_port(2),eth(src=00:0a:85:04:93:99,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=192.168.100.123,tip=192.168.100.219,op=1,sha=00:0a:85:04:93:99,tha=00:00:00:00:00:00), packets:0, bytes:0, used:never, actions:0,1
 in_port(1),eth(src=00:0a:85:04:15:5e,dst=00:0a:85:04:93:99),eth_type(0x0800),ipv4(src=192.168.100.219,dst=192.168.100.123,proto=1,tos=0,ttl=64,frag=no),icmp(type=0,code=0), packets:4, bytes:392, used:0.145s, actions:2
 in_port(1),eth(src=00:0a:85:04:15:5e,dst=00:0a:85:04:93:99),eth_type(0x0806),arp(sip=192.168.100.219,tip=192.168.100.123,op=2,sha=00:0a:85:04:15:5e,tha=00:0a:85:04:93:99), packets:0, bytes:0, used:never, actions:2
 in_port(2),eth(src=00:0a:85:04:93:99,dst=00:0a:85:04:15:5e),eth_type(0x0800),ipv4(src=192.168.100.123,dst=192.168.100.219,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:4, bytes:392, used:0.145s, actions:1
eth1側からの入力はin_port(2)に対応し、arpおよびICMP echo request
(type=8)のデータパスが確認できます。actions:1は出力ポートが1(=eth0)
となっています。arpはactions:0,1となっていますが、ブロードキャスト
の場合は自分自身用にport 0(=br0) へも出力していることが確認できます。

なお、このデータパスは最大256個となっています。使用されなくなったら数秒
で消えてしまうようです。





◆Open vSwitch性能測定

OpenBlockS 600Dでopenvswitchをカーネルモードで利用した場合の性能を測定してみます。
今回は機材の関係上、obs600-1 <--> obs600-2 間のTCP転送性能を測定します。
測定にはnuttcpを用いました。
まずobs600-2でnuttcpをサーバモードで起動します。
 obs600-2# nuttcp -S
そして、obs600-1ではクライアントモードで起動し、10秒間測定します。
 obs600-1# nuttcp -i1 -T10 192.168.111.219
比較のため、まずはopenvswitchなしの場合で、obs600-1, obs600-2 間を測定
します。ネットワーク図は以下のとおりです。
なお、obs600-1, obs600-2のeth0をmtu 1500にした場合と mtu 9000の場合と2
パターンで測定しました。
なお、今回はカーネルパラメータなどのチューニングは行っていません。










         
結果
mtu 1500
  224.1250 MB /  10.02 sec =  187.6814 Mbps 41 %TX 99 %RX 0 retrans 0.44 msRTT

mtu 9000
  322.0000 MB /  10.02 sec =  269.6913 Mbps 48 %TX 99 %RX 0 retrans 0.46 msRTT
次にopenvswitchを間に挟んで同様の測定を行いました。
ネットワーク図は以下になります。mtu 1500, mtu 9000の2パターンで測定を行
いましたが、この際openvswitchのmtu(br0,eth0,eth1)も同一にしました。

 
結果
mtu 1500
  209.5625 MB /  10.03 sec =  175.2363 Mbps 40 %TX 98 %RX 0 retrans 1.56 msRTT

mtu 9000
  324.0000 MB /  10.01 sec =  271.3858 Mbps 43 %TX 99 %RX 0 retrans 1.53 msRTT
測定結果を比較すると明白に違いはみられません。
今回の測定では末端のOpenBlockS 600Dの性能がボトルネックとなってしまい、上限の正確な
値は確認できていませんが、すくなくとも、
 mtu 1500では 170 Mbps
 mtu 9000では 270 Mbps
程度の性能は出ることが確認できました。



◆OpenFlow風にOpen vSwitchをコントロール

Open vSwitchではovs-vsctl set-controllerコマンドによりOpenFlowコントロー
ラに接続してコントーラからフロー設定できるようになります。
今回はその前段階としてOpenFlowコントローラは使用せずに、ovs-ofctlコマン
ドを用いることで直接Open vSwitch上のフロー情報を書き換えて動作を確認し
ます。

今回の実験は、マルチホーム環境でopenvswitchの配下にあるクライアントから
の上流へのアクセス時に、特定のTCPポート(今回はSMTP=25)の通信だけ異なる
router を経由するように変更します。
このとき、routerやclientの設定は一切変更せずにopenvswitchの操作だけで上
記制御を行います。

今回使用したネットワーク構成は以下のようになります。






















obs600-1ではデフォルトルートは192.168.100.1(router-1)となっていて、
通常の外部への通信はISP 1側を通して行います。
 obs600-1# ip route
 192.168.100.0/24 dev eth1  proto kernel  scope link  src 192.168.100.123
 default via 192.168.100.1 dev eth1
初期状態でのopenvswitchのフローを確認します。
 # ovs-ofctl dump-flows br0
 NXST_FLOW reply (xid=0x4):
  cookie=0x0, duration=40.911s, table=0, n_packets=404, n_bytes=70012, priority=0 actions=NORMAL

actions=NORMALとなっているひとつのエントリが存在します。任意のパケット
はこのエントリにマッチして通常のブリッジ動作を行う。

次に新たなエントリを追加します。
宛先MACアドレスがrouter-1でTCP宛先ポートが25の場合のみ、宛先MACアドレス
を強制的にrouter-2に向かうように変更します。
これを行うにはovs-ofctl add-flowでフローを追加します。
 # ovs-ofctl add-flow br0 'priority=100,in_port=2,dl_dst=00:00:00:00:00:01,tcp,tp_dst=25, actions=mod_dl_dst:00:00:00:00:00:02,output:1'
priority=100を指定することで、条件にマッチする場合このエントリが優先さ
れます。in_port=2(eth1)から入力したパケットはmod_dl_dstにより宛先MACア
ドレスを変更してoutput:1でPort 1(eth0)に出力されます。

再びovs-ofctl dump-flowsでフローを確認します。
 # ovs-ofctl dump-flows br0
 NXST_FLOW reply (xid=0x4):
  cookie=0x0, duration=1171.997s, table=0, n_packets=0, n_bytes=0, priority=100,tcp,in_port=2,dl_dst=00:00:00:00:00:01,tp_dst=25 actions=mod_dl_dst:00:00:00:00:00:02,output:1
  cookie=0x0, duration=1304.627s, table=0, n_packets=2753, n_bytes=347468, priority=0 actions=NORMAL

openvswitch配下のクライアント(obs600-1)で実際に外部SMTPサーバに接続をしてみます。
 obs600-1# telnet smtp.example.org 25
 Connected to smtp.example.org.
 Escape character is '^]'.
 220 smtp.example.org. ESMTP Postfix
このとき外部サーバでアクセスログを確認すると、ISP-2経由でアクセスされて
いることが確認できます。
WWWサーバ(Port 80)へも同様に接続してみます。
 obs600-1# telnet www.example.org 80
 Connected to www.example.org.
 Escape character is '^]'.
外部サーバで同様にアクセスログを確認すると、ISP-1経由のアクセスであるこ
とが確認できます。

これにより当初の目的が達成できました。

このときのOpen vSwitch上でのデータパスも確認してみます。
 # ovs-dpctl dump-flows br0
  - 省略 -
 in_port(2),eth(src=00:0a:85:04:93:99,dst=00:00:00:00:00:01),eth_type(0x0800),ipv4(src=192.168.111.123,dst=175.41.248.246,proto=6,tos=0x10,ttl=64,frag=no),tcp(src=45737,dst=25), packets:3, bytes:206, used:0.645s, actions:set(eth(src=00:0a:85:04:93:99,dst=00:00:00:00:00:02)),1
  - 省略 -

 # ovs-dpctl dump-flows br0
  - 省略 -
 in_port(2),eth(src=00:0a:85:04:93:99,dst=00:00:00:00:00:01),eth_type(0x0800),ipv4(src=192.168.111.123,dst=175.41.248.246,proto=6,tos=0x10,ttl=64,frag=no),tcp(src=35688,dst=80), packets:0, bytes:0, used:never, actions:1
  - 省略 -

dst=80の場合はactions:1に対して、dst=25の場合はMACアドレスが変更
されていることが確認できます。



◆まとめ

今回はOpenFlowに対応した仮想スイッチOpen vSwitchをOpenBlockS 600D上に導入し実際
の動作確認を行いました。転送処理はカーネルモードで行うようにビルドを行
いました。

Open vSwitchをブリッジとして動作するように設定しOpen vSwitch の各種デー
モンやコマンドを使い動作確認を行いました。

OpenBlockS 600D上でのOpen vSwitchの転送性能を測定したところ、最低でも
 mtu 1500では 170 Mbps
 mtu 9000では 270 Mbps
程度はでそうなことが確認できました。

Open vSwitch上でフローを設定することで、特定のフローについての
動作を変更できることを確認しました。実際にはTCP宛先ポート25(=smtp)
のパケットを異なるルータに転送するという例を実現しました。

以上で今回目的としていたことは一通り確認できました。