Open vSwitch のみで 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 追記: 上記の気になる点を修正した記事を書きました。]

最近のトラックバック

無料ブログはココログ