仮想ネットワーク

2013年8月10日 (土)

◆ Linux のネットワークネームスペース機能と Open vSwitch で仮想ネットワーク: STP(Spanning Tree Protocol)

Open vSwitch のスパニングツリープロトコル(Spanning Tree Protocol, STP)を有効にして動かしてみました。

動作確認環境:
   OS: Lubuntu 13.04 Desktop (i386)
   Open vSwitch: 1.9.0

■今回作成した仮想ネットワークの構成図

2台のスイッチが2本の線でつながっており、ループしている状態にしています。

Photo
念のため、ovs-vsctl show で確認してみます:

$ sudo ovs-vsctl show
dc50ed1a-4dd2-4169-89d3-fbe717480a70
    Bridge "vswitch1"
        Port "vlink1"
            Interface "vlink1"
        Port "vlinkSW1-0"
            Interface "vlinkSW1-0"
        Port "vlinkSW2-1"
            Interface "vlinkSW2-1"
        Port "vswitch1"
            Interface "vswitch1"
                type: internal
    Bridge "vswitch2"
        Port "vlinkSW2-0"
            Interface "vlinkSW2-0"
        Port "vlink2"
            Interface "vlink2"
        Port "vswitch2"
            Interface "vswitch2"
                type: internal
        Port "vlinkSW1-1"
            Interface "vlinkSW1-1"
    ovs_version: "1.9.0"

■動作テスト

(1) ネットワークを作成後、すぐに、vhost1 から vhost2(192.168.0.2) へ ping

ping に -D オプションをつけてタイムスタンプを出力するようにしてみました。最初に通信ができるようになるまで30秒弱かかっています。

$ sudo ip netns exec vhost1 ping -D 192.168.0.2
PING 192.168.0.2 (192.168.0.2) 56(84) bytes of data.
[1376076296.379297] From 192.168.0.1 icmp_seq=1 Destination Host Unreachable
[1376076296.379366] From 192.168.0.1 icmp_seq=2 Destination Host Unreachable
[1376076296.379376] From 192.168.0.1 icmp_seq=3 Destination Host Unreachable
[1376076299.379759] From 192.168.0.1 icmp_seq=4 Destination Host Unreachable
[1376076299.379797] From 192.168.0.1 icmp_seq=5 Destination Host Unreachable
[1376076299.379806] From 192.168.0.1 icmp_seq=6 Destination Host Unreachable
[1376076302.391493] From 192.168.0.1 icmp_seq=7 Destination Host Unreachable
[1376076302.391527] From 192.168.0.1 icmp_seq=8 Destination Host Unreachable
[1376076302.391535] From 192.168.0.1 icmp_seq=9 Destination Host Unreachable
   略
[1376076317.404299] From 192.168.0.1 icmp_seq=22 Destination Host Unreachable
[1376076317.404333] From 192.168.0.1 icmp_seq=23 Destination Host Unreachable
[1376076317.404342] From 192.168.0.1 icmp_seq=24 Destination Host Unreachable
[1376076320.404558] From 192.168.0.1 icmp_seq=25 Destination Host Unreachable
[1376076320.404599] From 192.168.0.1 icmp_seq=26 Destination Host Unreachable
[1376076320.404613] From 192.168.0.1 icmp_seq=27 Destination Host Unreachable
[1376076322.405310] 64 bytes from 192.168.0.2: icmp_req=28 ttl=64 time=2000 ms
[1376076322.405340] 64 bytes from 192.168.0.2: icmp_req=29 ttl=64 time=1000 ms
[1376076322.405348] 64 bytes from 192.168.0.2: icmp_req=30 ttl=64 time=0.664 ms
[1376076323.404743] 64 bytes from 192.168.0.2: icmp_req=31 ttl=64 time=0.068 ms
[1376076324.404754] 64 bytes from 192.168.0.2: icmp_req=32 ttl=64 time=0.073 ms
^C
--- 192.168.0.2 ping statistics ---
32 packets transmitted, 5 received, +27 errors, 84% packet loss, time 31023ms
rtt min/avg/max/mdev = 0.068/600.459/2000.704/800.205 ms, pipe 4

(2) ブロックされているポートの確認

Open vSwitch の Port テーブルのカラム status で状態が確認できます。出力カラムを name と status だけに絞ると出力したの以下の結果です。
今回のテストでは、vlinkSW1-0 のポートがブロックされていることが確認できます。

$ sudo ovs-vsctl -- --columns=name,status list Port
name                : "vlinkSW2-0"
status              : {stp_port_id="8001", stp_role=designated, stp_sec_in_state="433", stp_state=forwarding}

name                : "vlink1"
status              : {stp_port_id="8001", stp_role=designated, stp_sec_in_state="433", stp_state=forwarding}

name                : "vlinkSW2-1"
status              : {stp_port_id="8002", stp_role=root, stp_sec_in_state="433", stp_state=forwarding}

name                : "vlink2"
status              : {stp_port_id="8003", stp_role=designated, stp_sec_in_state="433", stp_state=forwarding}

name                : "vswitch1"
status              : {}

name                : "vlinkSW1-0"
status              : {stp_port_id="8003", stp_role=alternate, stp_sec_in_state="462", stp_state=blocking}

name                : "vlinkSW1-1"
status              : {stp_port_id="8002", stp_role=designated, stp_sec_in_state="433", stp_state=forwarding}

name                : "vswitch2"
status              : {}

(3) 切断してみる

上記で確認した結果から、vlinkSW2-0 と vlinkSW2-1 の間で通信がされていることが分かるのでこの通信を切断し、vlinkSW1-0 のブロックが解除されて vlinkSW1-0 と vlinkSW1-1 の間の通信が回復することを確認してみます。

ping を実行中に以下のコマンドでインタフェースをダウンさせて通信を切断します:

$ sudo ifconfig vlinkSW2-0 down

以下、ping の結果。回復までに約50秒かかっています。

$ sudo ip netns exec vhost1 ping -D 192.168.0.2
PING 192.168.0.2 (192.168.0.2) 56(84) bytes of data.
[1376077074.541945] 64 bytes from 192.168.0.2: icmp_req=1 ttl=64 time=0.714 ms
[1376077075.543771] 64 bytes from 192.168.0.2: icmp_req=2 ttl=64 time=0.071 ms
[1376077076.543824] 64 bytes from 192.168.0.2: icmp_req=3 ttl=64 time=0.063 ms
[1376077125.551195] From 192.168.0.1 icmp_seq=49 Destination Host Unreachable
[1376077125.551250] From 192.168.0.1 icmp_seq=50 Destination Host Unreachable
[1376077125.551258] From 192.168.0.1 icmp_seq=51 Destination Host Unreachable
[1376077125.552571] 64 bytes from 192.168.0.2: icmp_req=52 ttl=64 time=0.664 ms
[1376077126.555236] 64 bytes from 192.168.0.2: icmp_req=53 ttl=64 time=0.060 ms
[1376077127.555274] 64 bytes from 192.168.0.2: icmp_req=54 ttl=64 time=0.060 ms
[1376077128.555355] 64 bytes from 192.168.0.2: icmp_req=55 ttl=64 time=0.074 ms
[1376077129.555414] 64 bytes from 192.168.0.2: icmp_req=56 ttl=64 time=0.058 ms
^C
--- 192.168.0.2 ping statistics ---
56 packets transmitted, 8 received, +3 errors, 85% packet loss, time 55015ms
rtt min/avg/max/mdev = 0.058/0.220/0.714/0.271 ms, pipe 3

(4) ポートのステータス確認

vlinkSW1-0 のブロック状態が解除されていることが確認できます:

$ sudo ip netns exec vsudo ovs-vsctl -- --columns=name,status list Port
name                : "vlinkSW2-0"
status              : {stp_port_id="8001", stp_role=designated, stp_sec_in_state="1175", stp_state=forwarding}

name                : "vlink1"
status              : {stp_port_id="8001", stp_role=designated, stp_sec_in_state="1175", stp_state=forwarding}

name                : "vlinkSW2-1"
status              : {stp_port_id="8002", stp_role=designated, stp_sec_in_state="1175", stp_state=forwarding}

name                : "vlink2"
status              : {stp_port_id="8003", stp_role=designated, stp_sec_in_state="1175", stp_state=forwarding}

name                : "vswitch1"
status              : {}

name                : "vlinkSW1-0"
status              : {stp_port_id="8003", stp_role=root, stp_sec_in_state="372", stp_state=forwarding}

name                : "vlinkSW1-1"
status              : {stp_port_id="8002", stp_role=designated, stp_sec_in_state="1175", stp_state=forwarding}

name                : "vswitch2"
status              : {}

(5) 停止したインタフェースを回復させる

先ほど停止したインタフェースを回復させるとどうなるでしょう?
ping 実行中に以下のコマンドで復活させます:

$ sudo ifconfig vlinkSW2-0 up

回復したときに、少しもたつきます:

$ sudo ip netns exec vhost1 ping -D 192.168.0.2
PING 192.168.0.2 (192.168.0.2) 56(84) bytes of data.
[1376077760.643086] 64 bytes from 192.168.0.2: icmp_req=1 ttl=64 time=0.049 ms
[1376077761.642098] 64 bytes from 192.168.0.2: icmp_req=2 ttl=64 time=0.057 ms
[1376077762.643821] 64 bytes from 192.168.0.2: icmp_req=3 ttl=64 time=0.057 ms
[1376077763.645079] 64 bytes from 192.168.0.2: icmp_req=4 ttl=64 time=0.076 ms
[1376077764.645113] 64 bytes from 192.168.0.2: icmp_req=5 ttl=64 time=0.078 ms
[1376077765.645305] 64 bytes from 192.168.0.2: icmp_req=6 ttl=64 time=0.072 ms
[1376077766.689862] 64 bytes from 192.168.0.2: icmp_req=7 ttl=64 time=9.23 ms
[1376077767.853087] 64 bytes from 192.168.0.2: icmp_req=8 ttl=64 time=10.1 ms

[1376077768.669838] 64 bytes from 192.168.0.2: icmp_req=9 ttl=64 time=0.328 ms
[1376077769.668223] 64 bytes from 192.168.0.2: icmp_req=10 ttl=64 time=0.122 ms
[1376077770.667189] 64 bytes from 192.168.0.2: icmp_req=11 ttl=64 time=0.092 ms
[1376077771.666140] 64 bytes from 192.168.0.2: icmp_req=12 ttl=64 time=0.054 ms
[1376077772.666320] 64 bytes from 192.168.0.2: icmp_req=13 ttl=64 time=0.113 ms
[1376077773.666751] 64 bytes from 192.168.0.2: icmp_req=14 ttl=64 time=0.065 ms
^C
--- 192.168.0.2 ping statistics ---
14 packets transmitted, 14 received, 0% packet loss, time 13023ms
rtt min/avg/max/mdev = 0.049/1.470/10.194/3.371 ms

(6) ポートのステータス確認

再び vlinkSW1-0 がブロックされていました:

$ sudo ovs-vsctl -- --columns=name,status list Port
name                : "vlinkSW2-0"
status              : {stp_port_id="8001", stp_role=designated, stp_sec_in_state="1777", stp_state=forwarding}

name                : "vlink1"
status              : {stp_port_id="8001", stp_role=designated, stp_sec_in_state="1777", stp_state=forwarding}

name                : "vlinkSW2-1"
status              : {stp_port_id="8002", stp_role=root, stp_sec_in_state="1777", stp_state=forwarding}

name                : "vlink2"
status              : {stp_port_id="8003", stp_role=designated, stp_sec_in_state="1777", stp_state=forwarding}

name                : "vswitch1"
status              : {}

name                : "vlinkSW1-0"
status              : {stp_port_id="8003", stp_role=alternate, stp_sec_in_state="331", stp_state=blocking}

name                : "vlinkSW1-1"
status              : {stp_port_id="8002", stp_role=designated, stp_sec_in_state="1777", stp_state=forwarding}

name                : "vswitch2"
status              : {}

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

引数 delete で削除、add で追加です。
共通ファイル vnetcommon.sh がカレントディレクトリにおいてあることを前提としています。
赤字にしたところが STP を有効に設定している箇所です。

#!/bin/sh

. ./vnetcommon.sh

delete() {
    deleteVSwitch "vswitch1" "vswitch2"
    deleteVHost "vhost1" "vhost2"
}

add() {
    addVSwitch "vswitch1"
    sudo ovs-vsctl set Bridge "vswitch1" stp_enable=true

    addVSwitch "vswitch2"
    sudo ovs-vsctl set Bridge "vswitch2" stp_enable=true

    connectVSwitchToVSwitch "vswitch1" "vlinkSW1" "vswitch2"
    connectVSwitchToVSwitch "vswitch2" "vlinkSW2" "vswitch1"

    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

現時点の Open vSwitch の FAQ には STP はあまりテストしていないということが書いてあり使用はおすすめしないようです。

FAQ の URL: http://git.openvswitch.org/cgi-bin/gitweb.cgi?p=openvswitch;a=blob_plain;f=FAQ;hb=HEAD

The Open vSwitch implementation of STP is not well tested. Please report any bugs you observe, but if you'd rather avoid acting as a beta tester then another option might be your best shot.

◆ 【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月 1日 (水)

◆ Linux のネットワークネームスペース機能と Open vSwitch で仮想ネットワーク(VLANホスト追加)

前々回の記事の仮想ネットワークに、VLANインターフェースを持つ仮想ホストを追加してみました。

■今回作成した仮想ネットワークの構成図

vhost6 を追加して VLAN ID=10 で接続しています。vlink6-0 のインターフェース対して VLAN インタフェースを vlink6-0.10 という名前で作っています。
※ vhost5 は、ありません。
Photo
■追加した仮想ホストでのインタフェース設定の確認

$ sudo ip netns exec vhost6 ip link show
19: vlink6-0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether aa:df:6d:3b:6c:b1 brd ff:ff:ff:ff:ff:ff 24: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue state UNKNOWN     link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 25: vlink6-0.10@vlink6-0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP     link/ether aa:df:6d:3b:6c:b1 brd ff:ff:ff:ff:ff:ff

■動作確認

vhost6 から ping で通信確認。VLAN ID が異なる vhost2 とは通信できない。

(1) vhost6 から vhost1(192.168.1.1) への ping

$ sudo ip netns exec vhost6 ping 192.168.1.1 -c 3
PING 192.168.1.1 (192.168.1.1) 56(84) bytes of data.
64 bytes from 192.168.1.1: icmp_req=1 ttl=64 time=0.703 ms
64 bytes from 192.168.1.1: icmp_req=2 ttl=64 time=0.149 ms
64 bytes from 192.168.1.1: icmp_req=3 ttl=64 time=0.126 ms

--- 192.168.1.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 0.126/0.326/0.703/0.266 ms

(2) vhost6 から vhost2(192.168.1.2) への ping

$ sudo ip netns exec vhost6 ping 192.168.1.2 -c 3
PING 192.168.1.2 (192.168.1.2) 56(84) bytes of data.
From 192.168.1.6 icmp_seq=1 Destination Host Unreachable
From 192.168.1.6 icmp_seq=2 Destination Host Unreachable
From 192.168.1.6 icmp_seq=3 Destination Host Unreachable

--- 192.168.1.2 ping statistics ---
3 packets transmitted, 0 received, +3 errors, 100% packet loss, time 2001ms
pipe 3

(3) vhost6 から vhost3(192.168.1.3) への ping

$ sudo ip netns exec vhost6 ping 192.168.1.3 -c 3
PING 192.168.1.3 (192.168.1.3) 56(84) bytes of data.
64 bytes from 192.168.1.3: icmp_req=1 ttl=64 time=0.633 ms
64 bytes from 192.168.1.3: icmp_req=2 ttl=64 time=0.103 ms
64 bytes from 192.168.1.3: icmp_req=3 ttl=64 time=0.107 ms

--- 192.168.1.3 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1999ms
rtt min/avg/max/mdev = 0.103/0.281/0.633/0.248 ms

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

引数 delete で削除、add で追加です。

VLANインタフェースを持つ仮想ホストの追加箇所を赤字にしています。

#!/bin/sh
delete() {
    sudo ovs-vsctl del-br vswitch1
    sudo ovs-vsctl del-br vswitch2
    sudo ip netns delete vhost1
    sudo ip netns delete vhost2
    sudo ip netns delete vhost3
    sudo ip netns delete vhost4
    sudo ip link delete vlink5-0
    sudo ip link delete vlink5-1
    sudo ip netns delete vhost6
}
add() {
    sudo ovs-vsctl add-br vswitch1
    sudo ovs-vsctl add-br vswitch2
   
    sudo ip link add name vlink1-0 type veth peer name vlink1-1
    sudo ip link add name vlink2-0 type veth peer name vlink2-1
    sudo ip link add name vlink3-0 type veth peer name vlink3-1
    sudo ip link add name vlink4-0 type veth peer name vlink4-1
    sudo ip link add name vlink5-0 type veth peer name vlink5-1
    sudo ip link add name vlink6-0 type veth peer name vlink6-1
   
    sudo ip link set vlink1-0 up
    sudo ip link set vlink2-0 up
    sudo ip link set vlink3-0 up
    sudo ip link set vlink4-0 up
   
    sudo ip link set vlink1-1 up
    sudo ip link set vlink2-1 up
    sudo ip link set vlink3-1 up
    sudo ip link set vlink4-1 up
    sudo ip link set vlink6-1 up
   
    sudo ip netns add vhost1
    sudo ip netns add vhost2
    sudo ip netns add vhost3
    sudo ip netns add vhost4
    sudo ip netns add vhost6
   
    sudo ip netns exec vhost1 ifconfig lo 127.0.0.1
    sudo ip netns exec vhost2 ifconfig lo 127.0.0.1
    sudo ip netns exec vhost3 ifconfig lo 127.0.0.1
    sudo ip netns exec vhost4 ifconfig lo 127.0.0.1
    sudo ip netns exec vhost6 ifconfig lo 127.0.0.1

    sudo ip link set vlink1-0 netns vhost1
    sudo ip link set vlink2-0 netns vhost2
    sudo ip link set vlink3-0 netns vhost3
    sudo ip link set vlink4-0 netns vhost4
    sudo ip link set vlink6-0 netns vhost6
    sudo ip netns exec vhost6 ip link add link vlink6-0 name vlink6-0.10 type vlan id 10
    sudo ip netns exec vhost6 ip link set vlink6-0 up

   
    sudo ip netns exec vhost1 ifconfig vlink1-0 192.168.1.1
    sudo ip netns exec vhost2 ifconfig vlink2-0 192.168.1.2
    sudo ip netns exec vhost3 ifconfig vlink3-0 192.168.1.3
    sudo ip netns exec vhost4 ifconfig vlink4-0 192.168.1.4
    sudo ip netns exec vhost6 ifconfig vlink6-0.10 192.168.1.6
   
    sudo ovs-vsctl add-port vswitch1 vlink5-0
    sudo ovs-vsctl add-port vswitch2 vlink5-1
   
    sudo ovs-vsctl add-port vswitch1 vlink1-1 tag=10
    sudo ovs-vsctl add-port vswitch1 vlink2-1 tag=20
    sudo ovs-vsctl add-port vswitch1 vlink6-1
   
    sudo ovs-vsctl add-port vswitch2 vlink3-1 tag=10
    sudo ovs-vsctl add-port vswitch2 vlink4-1 tag=20
    sudo ip link set vlink5-0 up
    sudo ip link set vlink5-1 up
}
case "$1" in
add)
    set -x
    add
    ;;
delete)
    delete > /dev/null 2>&1
    ;;
*)
    echo "usage: $0 (add|delete)"
esac

2013年4月25日 (木)

◆ Linux のネットワークネームスペース機能と Open vSwitch で仮想ネットワーク(ルータ追加)

前回の記事の仮想ネットワークに仮想ルータを追加してみました。仮想ルータといってもネットワークネームスペースを1つ追加してそこに2つの仮想ネットワークインターフェースをつなげただけ。

こんなに簡単に仮想ルータができて驚いた。

■今回作成した仮想ネットワークの論理構成図

Kousei2_2


■今回作成した仮想ネットワークの構成図(物理?)

各 vhost のIPアドレスは振りなおした上で、vrouter1を追加しています。

Kousei1_6

■動作確認

(1) vhost1(192.168.1.1) から vhost2(192.168.2.1)へ

$ sudo ip netns exec vhost1 ping 192.168.2.1 -c 3
PING 192.168.2.1 (192.168.2.1) 56(84) bytes of data.
64 bytes from 192.168.2.1: icmp_req=1 ttl=63 time=0.720 ms
64 bytes from 192.168.2.1: icmp_req=2 ttl=63 time=0.145 ms
64 bytes from 192.168.2.1: icmp_req=3 ttl=63 time=0.117 ms

--- 192.168.2.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 0.117/0.327/0.720/0.278 ms

(2) vhost1(192.168.1.1) から vhost3(192.168.1.2)へ

$ sudo ip netns exec vhost1 ping 192.168.1.2 -c 3
PING 192.168.1.2 (192.168.1.2) 56(84) bytes of data.
64 bytes from 192.168.1.2: icmp_req=1 ttl=64 time=0.122 ms
64 bytes from 192.168.1.2: icmp_req=2 ttl=64 time=0.095 ms
64 bytes from 192.168.1.2: icmp_req=3 ttl=64 time=0.120 ms

--- 192.168.1.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1998ms
rtt min/avg/max/mdev = 0.095/0.112/0.122/0.015 ms

(3) vhost1(192.168.1.1) から vhost4(192.168.2.2)へ

$ sudo ip netns exec vhost1 ping 192.168.2.2 -c 3
PING 192.168.2.2 (192.168.2.2) 56(84) bytes of data.
64 bytes from 192.168.2.2: icmp_req=1 ttl=63 time=1.32 ms
64 bytes from 192.168.2.2: icmp_req=2 ttl=63 time=0.183 ms
64 bytes from 192.168.2.2: icmp_req=3 ttl=63 time=0.131 ms

--- 192.168.2.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 0.131/0.545/1.323/0.550 ms

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

引数 delete で削除、add で追加です。

ルータ用の設定箇所、ルーティング情報の設定箇所を赤字にしています。

#!/bin/sh

delete() {
    sudo ovs-vsctl del-br vswitch1
    sudo ovs-vsctl del-br vswitch2
    sudo ip netns delete vhost1
    sudo ip netns delete vhost2
    sudo ip netns delete vhost3
    sudo ip netns delete vhost4
    sudo ip link delete vlink5-0
    sudo ip link delete vlink5-1
    sudo ip netns delete vrouter1

}

add() {
    sudo ovs-vsctl add-br vswitch1
    sudo ovs-vsctl add-br vswitch2
    
    sudo ip link add name vlink1-0 type veth peer name vlink1-1
    sudo ip link add name vlink2-0 type veth peer name vlink2-1
    sudo ip link add name vlink3-0 type veth peer name vlink3-1
    sudo ip link add name vlink4-0 type veth peer name vlink4-1
    sudo ip link add name vlink5-0 type veth peer name vlink5-1
    sudo ip link add name vlink6-0 type veth peer name vlink6-1
    sudo ip link add name vlink7-0 type veth peer name vlink7-1
    
    sudo ip link set vlink1-0 up
    sudo ip link set vlink2-0 up
    sudo ip link set vlink3-0 up
    sudo ip link set vlink4-0 up
    sudo ip link set vlink6-0 up
    sudo ip link set vlink7-0 up
    
    sudo ip link set vlink1-1 up
    sudo ip link set vlink2-1 up
    sudo ip link set vlink3-1 up
    sudo ip link set vlink4-1 up
    sudo ip link set vlink6-1 up
    sudo ip link set vlink7-1 up
    
    sudo ip netns add vhost1
    sudo ip netns add vhost2
    sudo ip netns add vhost3
    sudo ip netns add vhost4
    sudo ip netns add vrouter1
    
    sudo ip netns exec vhost1 ifconfig lo 127.0.0.1
    sudo ip netns exec vhost2 ifconfig lo 127.0.0.1
    sudo ip netns exec vhost3 ifconfig lo 127.0.0.1
    sudo ip netns exec vhost4 ifconfig lo 127.0.0.1
    sudo ip netns exec vrouter1 ifconfig lo 127.0.0.1

    sudo ip link set vlink1-0 netns vhost1
    sudo ip link set vlink2-0 netns vhost2
    sudo ip link set vlink3-0 netns vhost3
    sudo ip link set vlink4-0 netns vhost4

    sudo ip link set vlink6-0 netns vrouter1
    sudo ip link set vlink7-0 netns vrouter1
    
    sudo ip netns exec vhost1 ifconfig vlink1-0 192.168.1.1 netmask 255.255.255.0
    sudo ip netns exec vhost2 ifconfig vlink2-0 192.168.2.1 netmask 255.255.255.0
    sudo ip netns exec vhost3 ifconfig vlink3-0 192.168.1.2 netmask 255.255.255.0
    sudo ip netns exec vhost4 ifconfig vlink4-0 192.168.2.2 netmask 255.255.255.0

    sudo ip netns exec vrouter1 ifconfig vlink6-0 192.168.1.3 netmask 255.255.255.0
    sudo ip netns exec vrouter1 ifconfig vlink7-0 192.168.2.3 netmask 255.255.255.0
    
    sudo ovs-vsctl add-port vswitch1 vlink5-0
    sudo ovs-vsctl add-port vswitch2 vlink5-1
    
    sudo ovs-vsctl add-port vswitch1 vlink1-1 tag=10
    sudo ovs-vsctl add-port vswitch1 vlink2-1 tag=20
    
    sudo ovs-vsctl add-port vswitch2 vlink3-1 tag=10
    sudo ovs-vsctl add-port vswitch2 vlink4-1 tag=20

    sudo ovs-vsctl add-port vswitch1 vlink6-1 tag=10
    sudo ovs-vsctl add-port vswitch1 vlink7-1 tag=20

    sudo ip link set vlink5-0 up
    sudo ip link set vlink5-1 up

    sudo ip netns exec vhost1 route add default gw 192.168.1.3
    sudo ip netns exec vhost2 route add default gw 192.168.2.3
    sudo ip netns exec vhost3 route add default gw 192.168.1.3
    sudo ip netns exec vhost4 route add default gw 192.168.2.3
}

case "$1" in
add)
    set -x
    add
    ;;
delete)
    delete > /dev/null 2>&1
    ;;
*)
    echo "usage: $0 (add|delete)"
esac

2013年4月22日 (月)

◆ Linux のネットワークネームスペース機能と Open vSwitch で仮想ネットワーク

Linux のネットワークネームスペースと、Open vSwitch、ip link コマンドを組み合わせたら仮想ネットワークを Linux 内に作って遊べそうだと思ってシェルスクリプトを作って確認してみました。
今回は1例を試しただけですが、さらに色々な仮想ネットワークを Linux 内に作って遊べそうだという感触を得ることができました。Open vSwitch を OpenFlow スイッチとして用いるのも面白いでしょう。

■確認に用いた環境
  Lubuntu 12.04 (i386)

  Lubuntu 12.10 で試される場合は、NetworkManagerが邪魔すると思うので停止しておいた方がよいと思います。以下の記事の「■回避策」を参考にしてくださ:
記事: 「Lubuntu 12.10 で Trema の仮想ネットワーク機能を使うときはネットワークマネージャーを止めましょう」
http://ranosgrant.cocolog-nifty.com/blog/2013/01/lubuntu-1210-tr.html

■今回作成する仮想ネットワークの構成図

VLAN スイッチが2つ、ホストは4台。
VLAN スイッチは Open vSwitch を使用。
ホストは、ネットワークネームスペースを使用。

vhost1 と vhost3 は、tag=10 の VLAN に接続。
vhost2 と vhost4 は、 tag=20 の VLAN に接続。

Photo
■Open vSwitch のインストールについて

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

■動作確認結果

vhost1 からは、同じ VLAN タグを持つ vhost3 としか通信できないことを確認してみます。

まずは、vhost1 から vhost3 へ ping で通信できることを確認。

$ sudo ip netns exec vhost1 ping 192.168.1.3
PING 192.168.1.3 (192.168.1.3) 56(84) bytes of data.
64 bytes from 192.168.1.3: icmp_req=1 ttl=64 time=1.29 ms
64 bytes from 192.168.1.3: icmp_req=2 ttl=64 time=0.121 ms
64 bytes from 192.168.1.3: icmp_req=3 ttl=64 time=0.100 ms
^C
--- 192.168.1.3 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2002ms
rtt min/avg/max/mdev = 0.100/0.504/1.292/0.557 ms

vhost1 から vhost4 への ping は VLAN が異なるので通信ができず、Ctrl-C で中断。

$ sudo ip netns exec vhost1 ping 192.168.1.4
PING 192.168.1.4 (192.168.1.4) 56(84) bytes of data.
^C
--- 192.168.1.4 ping statistics ---
3 packets transmitted, 0 received, 100% packet loss, time 2001ms

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

引数は、delete で削除、add で作成。

#!/bin/sh

delete() {
    sudo ovs-vsctl del-br vswitch1
    sudo ovs-vsctl del-br vswitch2
    sudo ip netns delete vhost1
    sudo ip netns delete vhost2
    sudo ip netns delete vhost3
    sudo ip netns delete vhost4
    sudo ip link delete vlink5-0
    sudo ip link delete vlink5-1
}

add() {
    sudo ovs-vsctl add-br vswitch1
    sudo ovs-vsctl add-br vswitch2
    
    sudo ip link add name vlink1-0 type veth peer name vlink1-1
    sudo ip link add name vlink2-0 type veth peer name vlink2-1
    sudo ip link add name vlink3-0 type veth peer name vlink3-1
    sudo ip link add name vlink4-0 type veth peer name vlink4-1
    sudo ip link add name vlink5-0 type veth peer name vlink5-1
    
    sudo ip link set vlink1-0 up
    sudo ip link set vlink2-0 up
    sudo ip link set vlink3-0 up
    sudo ip link set vlink4-0 up
    
    sudo ip link set vlink1-1 up
    sudo ip link set vlink2-1 up
    sudo ip link set vlink3-1 up
    sudo ip link set vlink4-1 up
    
    sudo ip netns add vhost1
    sudo ip netns add vhost2
    sudo ip netns add vhost3
    sudo ip netns add vhost4
    
    sudo ip netns exec vhost1 ifconfig lo 127.0.0.1
    sudo ip netns exec vhost2 ifconfig lo 127.0.0.1
    sudo ip netns exec vhost3 ifconfig lo 127.0.0.1
    sudo ip netns exec vhost4 ifconfig lo 127.0.0.1

    sudo ip link set vlink1-0 netns vhost1
    sudo ip link set vlink2-0 netns vhost2
    sudo ip link set vlink3-0 netns vhost3
    sudo ip link set vlink4-0 netns vhost4
    
    sudo ip netns exec vhost1 ifconfig vlink1-0 192.168.1.1
    sudo ip netns exec vhost2 ifconfig vlink2-0 192.168.1.2
    sudo ip netns exec vhost3 ifconfig vlink3-0 192.168.1.3
    sudo ip netns exec vhost4 ifconfig vlink4-0 192.168.1.4
    
    sudo ovs-vsctl add-port vswitch1 vlink5-0
    sudo ovs-vsctl add-port vswitch2 vlink5-1
    
    sudo ovs-vsctl add-port vswitch1 vlink1-1 tag=10
    sudo ovs-vsctl add-port vswitch1 vlink2-1 tag=20
    
    sudo ovs-vsctl add-port vswitch2 vlink3-1 tag=10
    sudo ovs-vsctl add-port vswitch2 vlink4-1 tag=20

    sudo ip link set vlink5-0 up
    sudo ip link set vlink5-1 up
}

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

最近のトラックバック

無料ブログはココログ