OpenFlow

2013年8月10日 (土)

◆ 【Open vSwitchのみで OpenFlowプログラミング】VLAN ID コンバータ 改

OpenFlow コントーラを使わずに(※) Open vSwitch を OpenFlow スイッチとして使って VLAN ID を変換する機能を実装してみましたという前回の記事の最後に書いた気になった点を改善しました。OpenFlow コントローラなしの OpenFlow スイッチを仮想ネットワークに追加する addStandaloneOFSwitch を共通ファイル vnetcommon.sh に追加しました。

※OpenFlow コントローラなしで OpenFlow スイッチを使うというのは OpenFlow の仕様上はありえない構成です。でも、Open vSwitch では可能であり、お手軽に OpenFlow を試すには便利な機能だと思います。

ネットワーク構成図などは、前回記事を参照ください。

■仮想ネットワークの作成・削除シェルスクリプト

引数 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"
}

config_ofs1() {
    sudo ovs-ofctl add-flow "ofs1" in_port=1,dl_vlan=10,actions=mod_vlan_vid:30,output:2
    sudo ovs-ofctl add-flow "ofs1" in_port=2,dl_vlan=30,actions=mod_vlan_vid:10,output:1
    sudo ovs-ofctl add-flow "ofs1" in_port=1,dl_vlan=20,actions=mod_vlan_vid:40,output:2
    sudo ovs-ofctl add-flow "ofs1" in_port=2,dl_vlan=40,actions=mod_vlan_vid:20,output:1
    sudo ovs-ofctl add-flow "ofs1" priority=100,actions=drop
}

add() {
    addStandaloneOFSwitch "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"

    config_ofs1
}

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

修正版の vnetcommon.sh コードは以下です。addStandaloneOFSwitch を追加しました。

# version: 2013-08-10

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"
}

addStandaloneOFSwitch() {
    local switch="$1"

    addVSwitch "$switch"
    sudo ovs-vsctl set-fail-mode "$switch" secure
    sudo ovs-ofctl del-flows "$switch"
}


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 "$@"
}

2013年8月 5日 (月)

◆ 【Open vSwitchのみで OpenFlowプログラミング】VLAN ID コンバータ

OpenFlow コントーラを使わずに、Open vSwitch を OpenFlow スイッチとして使って VLAN ID を変換する機能を実装してみました。

Open vSwitch では、ovs-ofctl コマンドを使うことで OpenFlow スイッチとしての機能に対してフローを登録・変更・削除することができます。
動的にフローを変更する必要がなければ、このコマンドで事前に登録して動作させれば OpenFlow コントローラなしでも動かすことができそうだと思い以前作った VLAN ID を変換する例に対して試してみました。

Lubuntu 13.04 のパッケージの Open vSwitch で入る ovs-ofctl コマンドの man ページは、以下で参照できます:
http://manpages.ubuntu.com/manpages/raring/en/man8/ovs-ofctl.8.html

仮想ネットワークの構成は、以前書いた記事「◆ 【TremaでOpenFlowプログラミング】VLAN ID コンバータ」と全く一緒です。

使用した環境:
  OS: Lubuntu 13.04 Desktop (i386) (※ 12.04 ではありません)
  Open vSwitch: 1.9.0 (Lubuntu のパッケージでインストール)

補足:
Open vSwitch のインストールについては記事「◆ Lubuntu 13.04 に Open vSwitch をインストールした時のログ」を参照ください。

■今回の仮想ネットワークの構成図
記事「◆ 【TremaでOpenFlowプログラミング】VLAN ID コンバータ」と全く一緒です。
こんなストーリーを想定しています:
・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): dpid:00003670612aed45
n_tables:255, n_buffers:256
capabilities: FLOW_STATS TABLE_STATS PORT_STATS QUEUE_STATS ARP_MATCH_IP
actions: OUTPUT SET_VLAN_VID SET_VLAN_PCP STRIP_VLAN SET_DL_SRC SET_DL_DST SET_NW_SRC SET_NW_DST SET_NW_TOS SET_TP_SRC SET_TP_DST ENQUEUE
1(vlinkSW1-1): addr:06:be:3f:87:9f:18
     config:     0
     state:      0
     current:    10GB-FD COPPER
     speed: 10000 Mbps now, 100 Mbps max
2(vlinkSW2-1): addr:f6:42:dc:fe:0d:ff
     config:     0
     state:      0
     current:    10GB-FD COPPER
     speed: 10000 Mbps now, 100 Mbps max
LOCAL(ofs1): addr:36:70:61:2a:ed:45
     config:     PORT_DOWN
     state:      LINK_DOWN
     speed: 100 Mbps now, 100 Mbps max
OFPT_GET_CONFIG_REPLY (xid=0x3): frags=normal miss_send_len=0

ちなみに、Lubuntu 12.04 のパッケージとしてインストールした Open vSwitch で ovs-ofctl show を実行した時はcapabilities と actions が16進数表記だったのが、今回はシンボル名に変わっていました。

■ OpenFlow コントローラ

OpenFlow コントローラ自体はありませんが、後述するスクリプトから、フローテーブルを設定している部分を以下に抜粋します:

config_ofs1() {
    sudo ovs-ofctl del-flows "ofs1"


    sudo ovs-ofctl add-flow "ofs1" in_port=1,dl_vlan=10,actions=mod_vlan_vid:30,output:2
    sudo ovs-ofctl add-flow "ofs1" in_port=2,dl_vlan=30,actions=mod_vlan_vid:10,output:1
    sudo ovs-ofctl add-flow "ofs1" in_port=1,dl_vlan=20,actions=mod_vlan_vid:40,output:2
    sudo ovs-ofctl add-flow "ofs1" in_port=2,dl_vlan=40,actions=mod_vlan_vid:20,output:1
    sudo ovs-ofctl add-flow "ofs1" priority=100,actions=drop
}

 やっていることを文字にすると以下の通り:
・ポート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 を他のフローエントリより下げることで「上記に当てはまらない」という動きにしています。)

先頭で、フローを削除しているのはデフォルトで登録されている actions=NORMAL のフローを削除するためです。
フローの編集を何もしていない時、ovs-ofctl dump-flow を行うと以下のような出力が得られます:

 cookie=0x0, duration=5.009s, table=0, n_packets=15, n_bytes=1690, idle_age=0, priority=0 actions=NORMAL

この actions=NORMAL は、通常のスイッチとして機能するということを意味します。

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

$ sudo ovs-ofctl dump-flows ofs1
NXST_FLOW reply (xid=0x4):
cookie=0x0, duration=223.515s, table=0, n_packets=84, n_bytes=17360, idle_age=36, priority=100 actions=drop
cookie=0x0, duration=223.54s, table=0, n_packets=4, n_bytes=308, idle_age=214, in_port=2,dl_vlan=40 actions=mod_vlan_vid:20,output:1
cookie=0x0, duration=223.57s, table=0, n_packets=4, n_bytes=308, idle_age=214, in_port=1,dl_vlan=20 actions=mod_vlan_vid:40,output:2
cookie=0x0, duration=223.621s, table=0, n_packets=4, n_bytes=308, idle_age=214, in_port=1,dl_vlan=10 actions=mod_vlan_vid:30,output:2
cookie=0x0, duration=223.596s, table=0, n_packets=4, n_bytes=308, idle_age=215, in_port=2,dl_vlan=30 actions=mod_vlan_vid:10,output:1

■ 動作確認
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 999ms
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=0.849 ms
64 bytes from 192.168.0.3: icmp_req=2 ttl=64 time=0.060 ms

--- 192.168.0.3 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1003ms
rtt min/avg/max/mdev = 0.060/0.454/0.849/0.395 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 1010ms
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 1005ms
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 1004ms
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=0.809 ms
64 bytes from 192.168.0.4: icmp_req=2 ttl=64 time=0.068 ms

--- 192.168.0.4 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1009ms
rtt min/avg/max/mdev = 0.068/0.438/0.809/0.371 ms

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

赤字にした箇所は記事「◆ 【TremaでOpenFlowプログラミング】VLAN ID コンバータ」の仮想ネットワークのスクリプトからの変更点です。フローテーブルの設定を追加しています。また、スイッチ ofs1 は、OpenFlow コントローラの設定無しにするため addOFSwitch ではなくて addVSwitch を使って追加しています。

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

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

config_ofs1() {
    sudo ovs-ofctl del-flows "ofs1"

    sudo ovs-ofctl add-flow "ofs1" in_port=1,dl_vlan=10,actions=mod_vlan_vid:30,output:2
    sudo ovs-ofctl add-flow "ofs1" in_port=2,dl_vlan=30,actions=mod_vlan_vid:10,output:1
    sudo ovs-ofctl add-flow "ofs1" in_port=1,dl_vlan=20,actions=mod_vlan_vid:40,output:2
    sudo ovs-ofctl add-flow "ofs1" in_port=2,dl_vlan=40,actions=mod_vlan_vid:20,output:1
    sudo ovs-ofctl add-flow "ofs1" priority=100,actions=drop
}


add() {
    #addOFSwitch "ofs1"
    addVSwitch "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"

    config_ofs1
}

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 のコードは、以前の記事の末尾を参照ください。


記事をひと通り書いてから、気が付いたのですが上記では、ovs-ofctl del-flow をするタイミングが遅いような気がします(一般論として)。del-flow するまでは普通のスイッチとして動いているので、トラブルのもとになりそう。addOFSwitch のOpenfFlow コントローラなし版を作ったほうが良いかもしれません。
[2013/08/10 追記: 上記の気になる点を修正した記事を書きました。]

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年5月 6日 (月)

◆ OpenFlow 1.0.0 メッセージ一覧

『次世代ネットワーク制御技術 OpenFlow入門』(発行 アスキー・メディアワークス)が5月1日に発売されていたのでさっそく買ってみました。「2.4 コントローラ」は、 OpenFlow 1.0.0 仕様書の OpenFlow メッセージの説明箇所を日本語訳したような内容で仕様書を読むうえで便利そうだったのですが、目次の充実度がいまいちなのが残念でした。

ちょうど OpenFlow メッセージについて整理したいと思っていたので、仕様書と対応付ける形で目次のような感じで記載箇所をまとめてみました。

OpenFlow 1.0.0 仕様書においてメッセージについて記載している箇所は本文が "4.1 OpenFlow Protocol Overview"、詳細は "5 Appendix A: The OpenFlow Protocol" です。
以下では基本構成を、"4.1 OpenFlow Protocol Overview" にしています。

ページ番号は、
 ・『OpenFlow入門』と書いた箇所は『次世代ネットワーク制御技術 OpenFlow入門』初版でのページ
  ・その他は、OpenFlow Switch Specification Version 1.0.0 のページ
です。
また、OpenFlow Switch Specification Version 1.0.0 の PDF への各ページへのリンクを設定してあります。


"4.1 OpenFlow Protocol Overview" (P.10)
  OpenFlowでは3つのメッセージのタイプがある:
  ・Controller-to-Switch
      コントローラからスイッチに対して発行されるメッセージ。
    スイッチからの応答があるものとないものがある。
  ・Asynchronous
      スイッチからコントローラに対して発行されるメッセージ。
  ・Symmetric
      スイッチからコントローラ、コントローラからスイッチの双方向で発行されるメッセージ。

"4.1.1 Controller-to-Switch" (P.10)
 □ "Features:" (P.10)
    (『OpenFlow入門』 「・Feature」 P.46) 

    ■ メッセージ OFPT_FEATURES_REQUEST / 応答メッセージ OFPT_FEATURES_REPLY
    "5.3.1 Handshake"  (P.25)
    (『OpenFlow入門』 「Feature」 P.62) 

  □ "Configuration:"  (P.10)
    (『OpenFlow入門』 「・Configuration」 P.47) 

    ■ メッセージ OFPT_GET_CONFIG_REQUEST / 応答メッセージ OFPT_GET_CONFIG_REPLY
    "5.3.2 Switch Configuration" (P.26)
    (『OpenFlow入門』 「Configuration」 P.64) 

    ■ メッセージ OFPT_SET_CONFIG
    "5.3.2 Switch Configuration" (P.26)
    (『OpenFlow入門』 「Configuration」 P.64) 

    ■ メッセージ OFPT_QUEUE_GET_CONFIG_REQUEST / 応答メッセージ OFPT_QUEUE_GET_CONFIG_REPLY
    "5.3.4 Queue Configuration Message" (P.29)

  □ "Modify-State:" (P.10)
    (『OpenFlow入門』 「・Modify-State」 P.49) 

    ■ メッセージ OFPT_FLOW_MOD
    "4.6 Flow Table Modification Message" (P.13)
    "5.3.3 Modify State Messages" "Modify Flow Entry Message" (P.27)
    (『OpenFlow入門』 「Modify-State1(フローエントリ変更メッセージ)」 P.65) 

    メッセージのタイプ:
        OFPFC_ADD
        OFPFC_MODIFTY
        OFPFC_MODIFY_STRICT
        OFPFC_DELTE
        OFPFC_DELETE_STRICT

    ■ メッセージ OFPT_PORT_MOD
    "5.3.3 Modify State Messages" "Port Modification Message" (P.29)
    (『OpenFlow入門』 「Modify-State2 (ポート変更メッセージ)」 P.75) 

  □ "Read-State:" (P.10)
    (『OpenFlow入門』 「・Read-State」 P.50) 

    ■ メッセージ OFPT_STATS_REQUEST / 応答メッセージ OFPT_STATS_REPLY
    "5.3.5 Read State Messages" (P.30)
    (『OpenFlow入門』 「Read-State」 P.75) 

    メッセージのタイプ:
       OFPST_DESC      ("Description Statistics" P.31)
       OFPST_FLOW      ("Individual Flow Statistics" P.31)
       OFPST_AGGREGATE ("Aggregate Flow Statistics" P.33)
       OFPST_TABLE     ("Table Statistics" P.33)
       OFPST_PORT      ("Port Statistics" P.34)
       OFPST_QUEUE     ("Queue Statistics" P.35)
       OFPST_VENDOR    ("Vendor Statistics" P.35)

  □ "Send-Packet:" (P.10)
    (『OpenFlow入門』 「・Send-Packet」 P.50) 

    ■ メッセージ OFPT_PACKET_OUT
    "5.3.6 Send Packet Message" (P.35)
    (『OpenFlow入門』 「Send-Packet」 P.82) 

  □ "Barrier:" (P.10)
    (『OpenFlow入門』 「・Barrier」 P.50) 

    ■ メッセージ OFPT_BARRIER_REQUEST / 応答メッセージ OFPT_BARRIER_REPLY
    "5.3.7 Barrier Message" (P.36)
    (『OpenFlow入門』 「Barrier」 P.82) 

"4.1.2 Asynchronous" (P.10)
  (『OpenFlow入門』「非同期メッセージ」 P.44) 

  □ "Packet-in:" (P.11)
    (『OpenFlow入門』「・Packet-In」 P.44) 

    ■ メッセージ OFPT_PACKET_IN
    "5.4.1 Packet-In Message" (P.36)
    (『OpenFlow入門』「Packet-in」 P.52) 

  □ "Flow-Removed:" (P.11)
    "4.7 Flow Removal" (P.15)
    (『OpenFlow入門』 「・Flow-Removed」 P.45) 

    ■ メッセージ OFPT_FLOW_REMOVED
    "5.4.2 Flow Removed Message" (P.37)
    (『OpenFlow入門』「Flow-Removed」 P.53) 
 
  □ "Port-status:" (P.11)
    (『OpenFlow入門』 「・Port-Status」 P.46) 

    ■ メッセージ OFPT_PORT_STATUS
    "5.4.3 Port Status Message" (P.38)
    (『OpenFlow入門』「Port-Status」 P.55) 
   
  □ "Error:" (P.11)
    (『OpenFlow入門』 「・Error」 P.46) 

    ■ メッセージ OFPT_ERROR_MSG
    "5.4.4 Error Message" (P.38)
    (『OpenFlow入門』 「Error」 P.58) 

"4.1.3 Symmetric" (P.11)
  "5.5 Symmetric Messages" (P.41)

  □ "Hello:" (P.11)
    (『OpenFlow入門』 「・Hello」 P.51) 

    ■ メッセージ OFPT_HELLO
    "5.5.1 Hello" (P.41)
    (『OpenFlow入門』 「Hello」 P.83) 

  □ "Echo:" (P.11)
    (『OpenFlow入門』 「・Echo」 P.51) 

    ■ メッセージ OFPT_ECHO_REQUEST
     "5.5.2 Echo Reqest" (P.41)
    (『OpenFlow入門』 「Echo」 P.83) 

    ■ メッセージ OFPT_ECHO_REPLY
    "5.5.3 Echo Reply" (P.41)

  □ "Vendor:" (P.11)
    (『OpenFlow入門』 「・Echo」 P.52) 

    ■ メッセージ OFPT_VENDOR
    "5.5.4 Vendor" (P.41)
    (『OpenFlow入門』 「Vendor」 P.84) 

 


OpenFlow Switch Specification Version 1.0.0 は以下の Web ページで公開されています:
  http://www.openflow.org/wp/documents/

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月24日 (日)

PLANEX MZK-WNH および MZK-W300NH2 で動く OpenWrt with OpenFlow 1.0 のイメージをアップロードしました(Ver.2)」

前回書いた記事「MZK-WNH/MZK-W300NH2 用 OpenWrt を OpenFlow 1.0 スイッチにカスタマイズする」で作成したファームウェアのイメージをアップロードしておきます。

おことわり: ここで公開したファームを入れようとしたことで二度と起動しなくなる可能性があります。リスクを承知の上でお使いください。

■ OpeFlow スイッチ化した OpenWrt のイメージファイル

このブログで使っている ココログでは1ファイルのサイズ上限が 1Mバイトとのことで、面倒ですが3つに分割して置いてあります:
  concat.bat     image1.bin    image2.bin    image3.bin

3つの image*.bin ファイルを結合して1つのファイルにしてください。 Windows 用にバッチファイル concat.bat を用意しました。このバッチファイルを実行すると  openwrt-ramips-rt305x-mzk-wnh-squashfs-factory.bin というファイルができます。
  UNIX/Linux なら、cat image1.bin image2.bin image3.bin > openwrt-ramips-rt305x-mzk-wnh-squashfs-factory.bin でできあがり。
もし、ファイルが壊れていたらファームの入替え時にファイルのフォーマット不正か、チェックサムのエラーではじかれます。

■ファームの入れ替え方

先日書いた記事「PLANEX MZK-WNH と MZK-W300NH2 用 OpenWrt 12.09 ファームウェア(Attitude Adjustment, r35864)をアップロードしました」の「■ファームの入れ替え方」を参考にしてください。

あるいは、以前の記事 「MZK-WNH を純正ファームに戻す方法(シリアル接続不要)」の方法で純正ファームのイメージファイルの代わりに今回のイメージファイルを用いることで入れ替えることもできます。

■入れ替えたあと

先日書いた記事「MZK-WNH/MZK-W300NH2 用 OpenWrt を OpenFlow 1.0 スイッチにカスタマイズする」の「■ファームウェア入れ替え後の構成」以降を参考にしてください。

■前回公開版との違い

去年の10月に「MZK-WNH で動かす OpenFlow 1.0 のイメージをアップロードしました」で公開したものとの違いを以下にあげておきます。

1) Datapath ID の初期値(/etc/config/openflow ファイルの dpid)は MAC アドレスになるようにしてあります。 (OpenFlow 1.0 準拠にした)
2) Pantou: OpenFlow 1.0 の ofprotocol プロセスの起動時オプション --fail と --inactivity-probe を /etc/config/openflow ファイルで指定できるようにした。
3) --fail オプションをデフォルトの open から closed にして、OpenFlow コントローラとの接続失敗が続いてもブリッジとして動くことがないようにした。
4) LED(WPS, Wireless, Power) や ボタン(WPS, Reset)が扱える。
5) 使用している OpenWrt が新しい。
  「PLANEX MZK-WNH と MZK-W300NH2 用 OpenWrt 12.09 ファームウェア(Attitude Adjustment, r35864)をアップロードしました」がベースになっています。

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 (開発用ブランチ) だった古いものです。
当時は、試行錯誤でつくったので手順を公開できませんでしたが、今回はできるだけ最小限の手間でビルドできる手順としました...したつもりです。

最近のトラックバック

無料ブログはココログ