Trema/MyRoutingSwitch(5), Topology Change and Re-route(3)

はじめに

前回の話を実際に確認してみます。まずは、単純に、port_status (リンクダウン) が発生したときに、リンクダウンしたポートに対して output しているフローを消すだけだとあまり良くない、という話ところです。

テスト1

変更点
  • MyRoutingSwitch
    • start: トポロジ状態変化の処理は Topology で行われるのですが、Controller 側にフロー変更要求を通知させられるように self をわたしておきます。
  def start
    # 略
    @topology = Topology.new( @command_line.view, self )
    # 略
  end
    • flow_delete_by_port: port_status でトポロジ変化が検出されたときに呼び出します。
  def flow_delete_by_port dpid, port
    puts "[MyRoutingSwitch::flow_remove_by_port] switch:#{dpid}, port:#{port}"

    send_flow_mod_delete(
      dpid,
      :out_port => port
    )
  end
  • Topology
    • delete_link_by: MyRoutingSwitch::port_status でポート状態変化(リンクダウン)が検出されると最終的にこれが呼び出されて、observer に通知されます。
  def delete_link_by dpid, port
    puts "[topology::delete_link_by] switch:#{dpid}, port:#{port.number}"

    link = @linkindex.link_of dpid, port.number
    if link
      puts "delete link: #{link.dpid1}/#{link.port1} - #{link.dpid2}/#{link.port2}"
      @controller.flow_delete_by_port link.dpid1, link.port1
      @controller.flow_delete_by_port link.dpid2, link.port2
      changed
      @links -= [ link ]
    end

    notify_observers self
  end
トポロジ
vswitch("sw1") { dpid "0x1" }
vswitch("sw2") { dpid "0x2" }
vswitch("sw3") { dpid "0x3" }
vswitch("sw4") { dpid "0x4" }
vswitch("sw5") { dpid "0x5" }

vhost ("host1") {
  ip "192.168.0.1"
  netmask "255.255.255.0"
  mac "00:00:00:01:00:01"
}
vhost ("host2") {
  ip "192.168.0.2"
  netmask "255.255.255.0"
  mac "00:00:00:01:00:02"
}
link "sw1", "sw2"
link "sw2", "sw3"
link "sw3", "sw4"
link "sw2", "sw5"
link "sw3", "sw5"
link "sw1", "host1"
link "sw4", "host2"
シナリオ

以下の一連の流れをシナリオとしてスクリプト化しておきます。テキトーだけど。

  • Step.1: host1-host2 間で通信を行ってスイッチ内に双方向にフローを入れます。
  • Step.2: sw2-sw3 間のリンクをダウンさせます。
  • Step.3: 再度 host1-host2 間で通信を行って各スイッチ内のフローを確認します。
  • シナリオ共通機能(test-common.sh)
sendpackets_host1() {
    echo "# send packets 2times"
    for time in 1 2
    do
        echo "## turn: $time"
        for host in $*
        do
            trema send_packets --source host1 --dest $host --n_pkts 5
            sleep 1
            trema send_packets --source $host --dest host1 --n_pkts 5
            sleep 1
        done
    done
    sleep 3
}

sendpackets_a2b() {
    echo "# send packets $1 to $2"
    trema send_packets --source $1 --dest $2 --n_pkts 5
    sleep 1
}

showstats() {
    echo "# show host status"
    for host in $*
    do
        echo "## $host packet stats"
        trema show_stats $host
    done
}

dumpflows() {
    echo "# dump switch flows"
    for sw in $*
    do
        echo "## $sw flows"
        sudo trema dump_flows $sw
    done
}

linkdown() {
    echo "# linkdown switch:$1, port:$2"
    trema port_down --switch $1 --port $2
    sleep 1
}

step() {
    echo "##"
    echo "## step $1 ####################################"
    echo "##"
    sleep 1
}
  • シナリオ
#!/bin/sh

. ./test-common.sh

# scenario

step 1
sendpackets_host1 host2
showstats host1 host2
dumpflows sw1 sw2 sw3 sw4 sw5

step 2
linkdown sw2 1
dumpflows sw1 sw2 sw3 sw4 sw5

step 3
sendpackets_host1 host2
showstats host1 host2
dumpflows sw1 sw2 sw3 sw4 sw5
実行結果

ステップごとにフロー状態を見ていきます。ARP Table までは出してないですが。

Trema の場合、Switch Daemon が Flow の Cookie を変換していて、Cookie 指定して複数フローを束ねることをしていなければ、スイッチへのフロー設定順に連番になっているようです。なので、スイッチ名 + Cookie でフローエントリを参照できます。

  • トポロジ(初期状態)
[LinkIndex::update]
0x1 (port 1) <-> 0x2 (port 3)
0x2 (port 1) <-> 0x3 (port 2)
0x2 (port 2) <-> 0x5 (port 2)
0x2 (port 3) <-> 0x1 (port 1)
0x3 (port 1) <-> 0x5 (port 1)
0x3 (port 2) <-> 0x2 (port 1)
0x3 (port 3) <-> 0x4 (port 1)
0x4 (port 1) <-> 0x3 (port 3)
0x5 (port 1) <-> 0x3 (port 1)
0x5 (port 2) <-> 0x2 (port 2)
topology updated
  • Step.1
##
## step 1 ####################################
##
# send packets 2times
## turn: 1
## turn: 2
# show host status
## host1 packet stats
Sent packets:
ip_dst,tp_dst,ip_src,tp_src,n_pkts,n_octets
192.168.0.2,1,192.168.0.1,1,10,500
Received packets:
ip_dst,tp_dst,ip_src,tp_src,n_pkts,n_octets
192.168.0.1,1,192.168.0.2,1,10,500
## host2 packet stats
Sent packets:
ip_dst,tp_dst,ip_src,tp_src,n_pkts,n_octets
192.168.0.1,1,192.168.0.2,1,10,500
Received packets:
ip_dst,tp_dst,ip_src,tp_src,n_pkts,n_octets
192.168.0.2,1,192.168.0.1,1,10,500
# dump switch flows
## sw1 flows
NXST_FLOW reply (xid=0x4):
cookie=0x4, duration=14.256s, table=0, n_packets=4, n_bytes=256, priority=65535,ip,dl_src=00:00:00:01:00:01,dl_dst=00:00:00:01:00:02,nw_src=192.168.0.1,nw_dst=192.168.0.2 actions=output:1
cookie=0x1, duration=494.504s, table=0, n_packets=0, n_bytes=0, priority=65535,ip,nw_src=169.254.0.0/16 actions=drop
cookie=0x3, duration=19.509s, table=0, n_packets=9, n_bytes=576, priority=65535,ip,dl_dst=00:00:00:01:00:01,nw_dst=192.168.0.1 actions=output:2
cookie=0x2, duration=494.504s, table=0, n_packets=0, n_bytes=0, priority=65535,ip,nw_src=224.0.0.0/24 actions=drop
## sw2 flows
NXST_FLOW reply (xid=0x4):
cookie=0x4, duration=14.52s, table=0, n_packets=4, n_bytes=256, priority=65535,ip,dl_src=00:00:00:01:00:01,dl_dst=00:00:00:01:00:02,nw_src=192.168.0.1,nw_dst=192.168.0.2 actions=output:1
cookie=0x3, duration=19.777s, table=0, n_packets=9, n_bytes=576, priority=65535,ip,dl_src=00:00:00:01:00:02,dl_dst=00:00:00:01:00:01,nw_src=192.168.0.2,nw_dst=192.168.0.1 actions=output:3
cookie=0x1, duration=494.647s, table=0, n_packets=0, n_bytes=0, priority=65535,ip,nw_src=169.254.0.0/16 actions=drop
cookie=0x2, duration=494.647s, table=0, n_packets=0, n_bytes=0, priority=65535,ip,nw_src=224.0.0.0/24 actions=drop
## sw3 flows
NXST_FLOW reply (xid=0x4):
cookie=0x4, duration=14.797s, table=0, n_packets=4, n_bytes=256, priority=65535,ip,dl_src=00:00:00:01:00:01,dl_dst=00:00:00:01:00:02,nw_src=192.168.0.1,nw_dst=192.168.0.2 actions=output:3
cookie=0x3, duration=20.056s, table=0, n_packets=9, n_bytes=576, priority=65535,ip,dl_src=00:00:00:01:00:02,dl_dst=00:00:00:01:00:01,nw_src=192.168.0.2,nw_dst=192.168.0.1 actions=output:2
cookie=0x1, duration=495.835s, table=0, n_packets=0, n_bytes=0, priority=65535,ip,nw_src=169.254.0.0/16 actions=drop
cookie=0x2, duration=495.835s, table=0, n_packets=0, n_bytes=0, priority=65535,ip,nw_src=224.0.0.0/24 actions=drop
## sw4 flows
NXST_FLOW reply (xid=0x4):
cookie=0x3, duration=20.33s, table=0, n_packets=9, n_bytes=576, priority=65535,ip,dl_src=00:00:00:01:00:02,dl_dst=00:00:00:01:00:01,nw_src=192.168.0.2,nw_dst=192.168.0.1 actions=output:1
cookie=0x1, duration=496.085s, table=0, n_packets=0, n_bytes=0, priority=65535,ip,nw_src=169.254.0.0/16 actions=drop
cookie=0x4, duration=15.071s, table=0, n_packets=4, n_bytes=256, priority=65535,ip,dl_dst=00:00:00:01:00:02,nw_dst=192.168.0.2 actions=output:2
cookie=0x2, duration=496.085s, table=0, n_packets=0, n_bytes=0, priority=65535,ip,nw_src=224.0.0.0/24 actions=drop
## sw5 flows
NXST_FLOW reply (xid=0x4):
cookie=0x1, duration=496.345s, table=0, n_packets=0, n_bytes=0, priority=65535,ip,nw_src=169.254.0.0/16 actions=drop
cookie=0x2, duration=496.345s, table=0, n_packets=0, n_bytes=0, priority=65535,ip,nw_src=224.0.0.0/24 actions=drop
    • 初期状態では sw1-sw4 は双方向にフローが入ります。(各スイッチの Cookie=0x3, 0x4 のエントリ)。sw5 はフローが経由しないので特に何もなし。


  • Step.2
##
## step 2 ####################################
##
# linkdown switch:sw2, port:1
# dump switch flows
## sw1 flows
NXST_FLOW reply (xid=0x4):
cookie=0x4, duration=18.186s, table=0, n_packets=4, n_bytes=256, priority=65535,ip,dl_src=00:00:00:01:00:01,dl_dst=00:00:00:01:00:02,nw_src=192.168.0.1,nw_dst=192.168.0.2 actions=output:1
cookie=0x1, duration=498.434s, table=0, n_packets=0, n_bytes=0, priority=65535,ip,nw_src=169.254.0.0/16 actions=drop
cookie=0x3, duration=23.439s, table=0, n_packets=9, n_bytes=576, priority=65535,ip,dl_dst=00:00:00:01:00:01,nw_dst=192.168.0.1 actions=output:2
cookie=0x2, duration=498.434s, table=0, n_packets=0, n_bytes=0, priority=65535,ip,nw_src=224.0.0.0/24 actions=drop
## sw2 flows
NXST_FLOW reply (xid=0x4):
cookie=0x3, duration=23.719s, table=0, n_packets=9, n_bytes=576, priority=65535,ip,dl_src=00:00:00:01:00:02,dl_dst=00:00:00:01:00:01,nw_src=192.168.0.2,nw_dst=192.168.0.1 actions=output:3
cookie=0x1, duration=498.589s, table=0, n_packets=0, n_bytes=0, priority=65535,ip,nw_src=169.254.0.0/16 actions=drop
cookie=0x2, duration=498.589s, table=0, n_packets=0, n_bytes=0, priority=65535,ip,nw_src=224.0.0.0/24 actions=drop
## sw3 flows
NXST_FLOW reply (xid=0x4):
cookie=0x4, duration=18.737s, table=0, n_packets=4, n_bytes=256, priority=65535,ip,dl_src=00:00:00:01:00:01,dl_dst=00:00:00:01:00:02,nw_src=192.168.0.1,nw_dst=192.168.0.2 actions=output:3
cookie=0x1, duration=499.775s, table=0, n_packets=0, n_bytes=0, priority=65535,ip,nw_src=169.254.0.0/16 actions=drop
cookie=0x2, duration=499.775s, table=0, n_packets=0, n_bytes=0, priority=65535,ip,nw_src=224.0.0.0/24 actions=drop
## sw4 flows
NXST_FLOW reply (xid=0x4):
cookie=0x3, duration=24.377s, table=0, n_packets=9, n_bytes=576, priority=65535,ip,dl_src=00:00:00:01:00:02,dl_dst=00:00:00:01:00:01,nw_src=192.168.0.2,nw_dst=192.168.0.1 actions=output:1
cookie=0x1, duration=500.132s, table=0, n_packets=0, n_bytes=0, priority=65535,ip,nw_src=169.254.0.0/16 actions=drop
cookie=0x4, duration=19.118s, table=0, n_packets=4, n_bytes=256, priority=65535,ip,dl_dst=00:00:00:01:00:02,nw_dst=192.168.0.2 actions=output:2
cookie=0x2, duration=500.132s, table=0, n_packets=0, n_bytes=0, priority=65535,ip,nw_src=224.0.0.0/24 actions=drop
## sw5 flows
NXST_FLOW reply (xid=0x4):
cookie=0x1, duration=500.402s, table=0, n_packets=0, n_bytes=0, priority=65535,ip,nw_src=169.254.0.0/16 actions=drop
cookie=0x2, duration=500.402s, table=0, n_packets=0, n_bytes=0, priority=65535,ip,nw_src=224.0.0.0/24 actions=drop
    • リンクダウンを発生させた直後のフローエントリの状態です。落ちたリンクに対して output していた以下のエントリが消えます。
      • sw2, cookie=0x4 [dl_src=00:00:00:01:00:01,dl_dst=00:00:00:01:00:02,nw_src=192.168.0.1,nw_dst=192.168.0.2 actions=output:1]
      • sw3, cookie=0x3 [dl_src=00:00:00:01:00:02,dl_dst=00:00:00:01:00:01,nw_src=192.168.0.2,nw_dst=192.168.0.1 actions=output:2]


  • Step.3
##
## step 3 ####################################
##
# send packets 2times
## turn: 1
## turn: 2
# show host status
## host1 packet stats
Sent packets:
ip_dst,tp_dst,ip_src,tp_src,n_pkts,n_octets
192.168.0.2,1,192.168.0.1,1,20,1000
Received packets:
ip_dst,tp_dst,ip_src,tp_src,n_pkts,n_octets
192.168.0.1,1,192.168.0.2,1,20,1000
## host2 packet stats
Sent packets:
ip_dst,tp_dst,ip_src,tp_src,n_pkts,n_octets
192.168.0.1,1,192.168.0.2,1,20,1000
Received packets:
ip_dst,tp_dst,ip_src,tp_src,n_pkts,n_octets
192.168.0.2,1,192.168.0.1,1,20,1000
# dump switch flows
## sw1 flows
NXST_FLOW reply (xid=0x4):
cookie=0x4, duration=45.6s, table=0, n_packets=14, n_bytes=896, priority=65535,ip,dl_src=00:00:00:01:00:01,dl_dst=00:00:00:01:00:02,nw_src=192.168.0.1,nw_dst=192.168.0.2 actions=output:1
cookie=0x1, duration=525.848s, table=0, n_packets=0, n_bytes=0, priority=65535,ip,nw_src=169.254.0.0/16 actions=drop
cookie=0x3, duration=50.853s, table=0, n_packets=19, n_bytes=1216, priority=65535,ip,dl_dst=00:00:00:01:00:01,nw_dst=192.168.0.1 actions=output:2
cookie=0x2, duration=525.848s, table=0, n_packets=0, n_bytes=0, priority=65535,ip,nw_src=224.0.0.0/24 actions=drop
## sw2 flows
NXST_FLOW reply (xid=0x4):
cookie=0x5, duration=24.909s, table=0, n_packets=9, n_bytes=576, priority=65535,ip,dl_src=00:00:00:01:00:01,dl_dst=00:00:00:01:00:02,nw_src=192.168.0.1,nw_dst=192.168.0.2 actions=output:2
cookie=0x3, duration=51.122s, table=0, n_packets=18, n_bytes=1152, priority=65535,ip,dl_src=00:00:00:01:00:02,dl_dst=00:00:00:01:00:01,nw_src=192.168.0.2,nw_dst=192.168.0.1 actions=output:3
cookie=0x1, duration=525.992s, table=0, n_packets=0, n_bytes=0, priority=65535,ip,nw_src=169.254.0.0/16 actions=drop
cookie=0x6, duration=19.645s, table=0, n_packets=0, n_bytes=0, priority=65535,ip,dl_dst=00:00:00:01:00:01,nw_dst=192.168.0.1 actions=output:3
cookie=0x2, duration=525.992s, table=0, n_packets=0, n_bytes=0, priority=65535,ip,nw_src=224.0.0.0/24 actions=drop
## sw3 flows
NXST_FLOW reply (xid=0x4):
cookie=0x5, duration=25.181s, table=0, n_packets=9, n_bytes=576, priority=65535,ip,dl_src=00:00:00:01:00:01,dl_dst=00:00:00:01:00:02,nw_src=192.168.0.1,nw_dst=192.168.0.2 actions=output:3
cookie=0x6, duration=19.919s, table=0, n_packets=9, n_bytes=576, priority=65535,ip,dl_src=00:00:00:01:00:02,dl_dst=00:00:00:01:00:01,nw_src=192.168.0.2,nw_dst=192.168.0.1 actions=output:1
cookie=0x1, duration=527.172s, table=0, n_packets=0, n_bytes=0, priority=65535,ip,nw_src=169.254.0.0/16 actions=drop
cookie=0x2, duration=527.172s, table=0, n_packets=0, n_bytes=0, priority=65535,ip,nw_src=224.0.0.0/24 actions=drop
## sw4 flows
NXST_FLOW reply (xid=0x4):
cookie=0x3, duration=51.666s, table=0, n_packets=19, n_bytes=1216, priority=65535,ip,dl_src=00:00:00:01:00:02,dl_dst=00:00:00:01:00:01,nw_src=192.168.0.2,nw_dst=192.168.0.1 actions=output:1
cookie=0x1, duration=527.421s, table=0, n_packets=0, n_bytes=0, priority=65535,ip,nw_src=169.254.0.0/16 actions=drop
cookie=0x5, duration=25.454s, table=0, n_packets=9, n_bytes=576, priority=65535,ip,dl_dst=00:00:00:01:00:02,nw_dst=192.168.0.2 actions=output:2
cookie=0x2, duration=527.421s, table=0, n_packets=0, n_bytes=0, priority=65535,ip,nw_src=224.0.0.0/24 actions=drop
## sw5 flows
NXST_FLOW reply (xid=0x4):
cookie=0x3, duration=25.721s, table=0, n_packets=9, n_bytes=576, priority=65535,ip,dl_src=00:00:00:01:00:01,dl_dst=00:00:00:01:00:02,nw_src=192.168.0.1,nw_dst=192.168.0.2 actions=output:1
cookie=0x4, duration=20.458s, table=0, n_packets=9, n_bytes=576, priority=65535,ip,dl_src=00:00:00:01:00:02,dl_dst=00:00:00:01:00:01,nw_src=192.168.0.2,nw_dst=192.168.0.1 actions=output:2
cookie=0x1, duration=527.683s, table=0, n_packets=0, n_bytes=0, priority=65535,ip,nw_src=169.254.0.0/16 actions=drop
cookie=0x2, duration=527.683s, table=0, n_packets=0, n_bytes=0, priority=65535,ip,nw_src=224.0.0.0/24 actions=drop
    • host1→host2 後の状態を取得していないので、host2→host1 の折り返し送信が終わったときの状態ですね。cookie がスイッチへのフロー投入順になっている点に注目してください。sw1-sw4 について、
      • Cookie=0x5 は host1 → host2 のフローで設定されたフローエントリです。これは sw2 で発生した packet-in で生成されています。sw2-sw5-sw3-sw4 で経路計算されています。
[LinkIndex::update]
[MyRoutingSwitch::handle_ipv4]
[ARPTable::update] DPID=2, port=3, ipaddr=192.168.0.1, hwaddr=00:00:00:01:00:01
[ARPEntry::update] Update entry: DPID=2, MAC addr=00:00:00:01:00:01, port=3
IPv4: dpid:2, port:3, 192.168.0.1->192.168.0.2
(略)
flow_mod: dpid:2/port:2 -> dpid:5
flow_mod: dpid:5/port:1 -> dpid:3
flow_mod: dpid:3/port:3 -> dpid:4
      • Cookie=0x6 は host2 → host1 のフローで設定されたフローエントリです。これは sw3 で発生した packet-in で生成されています。sw3-sw5-sw2 で経路計算されています。
[MyRoutingSwitch::handle_ipv4]
[ARPTable::update] DPID=3, port=3, ipaddr=192.168.0.2, hwaddr=00:00:00:01:00:02
[ARPEntry::update] Update entry: DPID=3, MAC addr=00:00:00:01:00:02, port=3
IPv4: dpid:3, port:3, 192.168.0.2->192.168.0.1
(略)
flow_mod: dpid:3/port:1 -> dpid:5
flow_mod: dpid:5/port:2 -> dpid:2


テスト2

"ANY→host" で宛先ホストが接続されているスイッチ(last hop)にルールを設定して、フローエントリが多くなることを避けています。これをやめて中間経路と同等の規則でフローを入れれば、テスト1の実行結果は arp table の中身と実際の接続が食い違ってもフローテーブルとしては統一されるように見えます。

ただ、リンクダウン箇所の近傍に対して arp table が再設定されてしまう(実際のホスト接続位置と、リンクダウン後の arp table 情報が異なる)という状況が残ってしまうのがやっぱりよろしくないです。例えば下図のような状況。

  • (前回までの例と同様) host1-host2 間で相互に通信を行った後で sw2-sw3 間リンクダウン
  • リンクダウン発生後再度 host1-host2 間で相互に通信
    • host1 → host2 通信では、packet-in/arp-table 的には host1' → host2 として計算
    • host2 → host1 通信では、packet-in/arp-table 的には host2' → host1' として計算
  • この状態では、controller は host1 は host1' の位置に、host2 は host2' の位置にいるように見えているので、host3 → host1 で通信を行おうとすると、sw6-sw5(or sw6)-sw2 という計算をすることになります。集約ルール(ANY→host)を使わないことにするなら、sw2 には "host3→host1 to outurt:X" というルールが入ってここまで。sw1 には host3→host1 のルールは入りません。

ということで、この場合は集約ルールを使わないと通信が成立しません。集約ルール使っていれば、sw1,sw2 には "ANY→host1, output:1"(sw1), "ANY→host1, output:3"(sw2) なルールがはいっているはずです。

まあ試してみりゃいいね。

シナリオ
  • シナリオ (Step.3 まではテスト1と同様)
#!/bin/sh

. ./test-common.sh

# scenario

step 1
sendpackets_host1 host2
showstats host1 host2 host3
dumpflows sw1 sw2 sw3 sw4 sw5 sw6

step 2
linkdown sw2 1
dumpflows sw1 sw2 sw3 sw4 sw5 sw6

step 3
sendpackets_host1 host2
showstats host1 host2 host3
dumpflows sw1 sw2 sw3 sw4 sw5 sw6

step 4
sendpackets_a2b host3 host1
showstats host1 host2 host3
dumpflows sw1 sw2 sw3 sw4 sw5 sw6
  • トポロジ
[LinkIndex::update]
0x1 (port 2) <-> 0x2 (port 3)
0x1 (port 3) <-> 0x6 (port 3)
0x2 (port 1) <-> 0x3 (port 2)
0x2 (port 2) <-> 0x5 (port 3)
0x2 (port 3) <-> 0x1 (port 2)
0x3 (port 1) <-> 0x5 (port 2)
0x3 (port 2) <-> 0x2 (port 1)
0x3 (port 3) <-> 0x4 (port 1)
0x4 (port 1) <-> 0x3 (port 3)
0x5 (port 1) <-> 0x6 (port 2)
0x5 (port 2) <-> 0x3 (port 1)
0x5 (port 3) <-> 0x2 (port 2)
0x6 (port 2) <-> 0x5 (port 1)
0x6 (port 3) <-> 0x1 (port 3)
topology updated
実行結果
  • Step.4 実行時のコントローラの状態 (host3 通信開始時の packet-in および経路計算)
[MyRoutingSwitch::handle_ipv4]
[ARPTable::update] DPID=1, port=2, ipaddr=192.168.0.3, hwaddr=00:00:00:01:00:03
[ARPEntry::update] Update entry: DPID=1, MAC addr=00:00:00:01:00:03, port=2
IPv4: dpid:1, port:2, 192.168.0.3->192.168.0.1
[get_path], start:2, goal:1
(略)
flow_mod: dpid:1/port:2 -> dpid:2
  • Step.4 後の各スイッチおよびホストの状態
##
## step 4 ####################################
##
# send packets host3 to host1
# show host status
## host1 packet stats
Sent packets:
ip_dst,tp_dst,ip_src,tp_src,n_pkts,n_octets
192.168.0.2,1,192.168.0.1,1,20,1000
Received packets:
ip_dst,tp_dst,ip_src,tp_src,n_pkts,n_octets
192.168.0.1,1,192.168.0.2,1,20,1000
## host2 packet stats
Sent packets:
ip_dst,tp_dst,ip_src,tp_src,n_pkts,n_octets
192.168.0.1,1,192.168.0.2,1,20,1000
Received packets:
ip_dst,tp_dst,ip_src,tp_src,n_pkts,n_octets
192.168.0.2,1,192.168.0.1,1,20,1000
## host3 packet stats
Sent packets:
ip_dst,tp_dst,ip_src,tp_src,n_pkts,n_octets
192.168.0.1,1,192.168.0.3,1,5,250
Received packets:

# dump switch flows
## sw1 flows
NXST_FLOW reply (xid=0x4):
cookie=0x4, duration=53.488s, table=0, n_packets=14, n_bytes=896, priority=65535,ip,dl_src=00:00:00:01:00:01,dl_dst=
00:00:00:01:00:02,nw_src=192.168.0.1,nw_dst=192.168.0.2 actions=output:2
cookie=0x5, duration=6.178s, table=0, n_packets=5, n_bytes=320, priority=65535,ip,dl_src=00:00:00:01:00:03,dl_dst=00
:00:00:01:00:01,nw_src=192.168.0.3,nw_dst=192.168.0.1 actions=output:2
cookie=0x3, duration=58.747s, table=0, n_packets=19, n_bytes=1216, priority=65535,ip,dl_src=00:00:00:01:00:02,dl_dst
=00:00:00:01:00:01,nw_src=192.168.0.2,nw_dst=192.168.0.1 actions=output:1
cookie=0x1, duration=81.482s, table=0, n_packets=0, n_bytes=0, priority=65535,ip,nw_src=169.254.0.0/16 actions=drop
cookie=0x2, duration=81.482s, table=0, n_packets=0, n_bytes=0, priority=65535,ip,nw_src=224.0.0.0/24 actions=drop
## sw2 flows
NXST_FLOW reply (xid=0x4):
cookie=0x5, duration=34.338s, table=0, n_packets=9, n_bytes=576, priority=65535,ip,dl_src=00:00:00:01:00:01,dl_dst=0
0:00:00:01:00:02,nw_src=192.168.0.1,nw_dst=192.168.0.2 actions=output:2
cookie=0x8, duration=6.481s, table=0, n_packets=4, n_bytes=256, priority=65535,ip,dl_src=00:00:00:01:00:03,dl_dst=00
:00:00:01:00:01,nw_src=192.168.0.3,nw_dst=192.168.0.1 actions=output:3
cookie=0x6, duration=29.056s, table=0, n_packets=9, n_bytes=576, priority=65535,ip,dl_src=00:00:00:01:00:02,dl_dst=0
0:00:00:01:00:01,nw_src=192.168.0.2,nw_dst=192.168.0.1 actions=output:3
cookie=0x1, duration=81.755s, table=0, n_packets=0, n_bytes=0, priority=65535,ip,nw_src=169.254.0.0/16 actions=drop
cookie=0x2, duration=81.755s, table=0, n_packets=0, n_bytes=0, priority=65535,ip,nw_src=224.0.0.0/24 actions=drop
## sw3 flows
NXST_FLOW reply (xid=0x4):
cookie=0x5, duration=34.619s, table=0, n_packets=9, n_bytes=576, priority=65535,ip,dl_src=00:00:00:01:00:01,dl_dst=00:00:00:01:00:02,nw_src=192.168.0.1,nw_dst=192.168.0.2 actions=output:3
cookie=0x6, duration=29.349s, table=0, n_packets=9, n_bytes=576, priority=65535,ip,dl_src=00:00:00:01:00:02,dl_dst=00:00:00:01:00:01,nw_src=192.168.0.2,nw_dst=192.168.0.1 actions=output:1
cookie=0x1, duration=82.002s, table=0, n_packets=0, n_bytes=0, priority=65535,ip,nw_src=169.254.0.0/16 actions=drop
cookie=0x2, duration=82.002s, table=0, n_packets=0, n_bytes=0, priority=65535,ip,nw_src=224.0.0.0/24 actions=drop
## sw4 flows
NXST_FLOW reply (xid=0x4):
cookie=0x5, duration=34.919s, table=0, n_packets=9, n_bytes=576, priority=65535,ip,dl_src=00:00:00:01:00:01,dl_dst=00:00:00:01:00:02,nw_src=192.168.0.1,nw_dst=192.168.0.2 actions=output:2
cookie=0x3, duration=59.634s, table=0, n_packets=19, n_bytes=1216, priority=65535,ip,dl_src=00:00:00:01:00:02,dl_dst=00:00:00:01:00:01,nw_src=192.168.0.2,nw_dst=192.168.0.1 actions=output:1
cookie=0x1, duration=82.274s, table=0, n_packets=0, n_bytes=0, priority=65535,ip,nw_src=169.254.0.0/16 actions=drop
cookie=0x2, duration=82.274s, table=0, n_packets=0, n_bytes=0, priority=65535,ip,nw_src=224.0.0.0/24 actions=drop
## sw5 flows
NXST_FLOW reply (xid=0x4):
cookie=0x3, duration=35.201s, table=0, n_packets=9, n_bytes=576, priority=65535,ip,dl_src=00:00:00:01:00:01,dl_dst=00:00:00:01:00:02,nw_src=192.168.0.1,nw_dst=192.168.0.2 actions=output:2
cookie=0x5, duration=7.371s, table=0, n_packets=4, n_bytes=256, priority=65535,ip,dl_src=00:00:00:01:00:03,dl_dst=00:00:00:01:00:01,nw_src=192.168.0.3,nw_dst=192.168.0.1 actions=output:3
cookie=0x4, duration=29.927s, table=0, n_packets=9, n_bytes=576, priority=65535,ip,dl_src=00:00:00:01:00:02,dl_dst=00:00:00:01:00:01,nw_src=192.168.0.2,nw_dst=192.168.0.1 actions=output:3
cookie=0x1, duration=82.523s, table=0, n_packets=0, n_bytes=0, priority=65535,ip,nw_src=169.254.0.0/16 actions=drop
cookie=0x2, duration=82.523s, table=0, n_packets=0, n_bytes=0, priority=65535,ip,nw_src=224.0.0.0/24 actions=drop
## sw6 flows
NXST_FLOW reply (xid=0x4):
cookie=0x3, duration=7.653s, table=0, n_packets=4, n_bytes=256, priority=65535,ip,dl_src=00:00:00:01:00:03,dl_dst=00:00:00:01:00:01,nw_src=192.168.0.3,nw_dst=192.168.0.1 actions=output:2
cookie=0x1, duration=82.764s, table=0, n_packets=0, n_bytes=0, priority=65535,ip,nw_src=169.254.0.0/16 actions=drop
cookie=0x2, duration=82.764s, table=0, n_packets=0, n_bytes=0, priority=65535,ip,nw_src=224.0.0.0/24 actions=drop

以下のルールが host3 → host1 のフローですが、まあおかしいですね。host1 の受信パケットカウント(Received packets)をみると host3 からのパケットを受信できていないことを示しています。

対策

arp table の固定

問題は集約ルール使うかどうかじゃなくて arp table の情報が実際にホストが接続されている位置と食い違ってしまうこと、ということになります。なぜ食い違ってしまうのかというと、

  1. arp table update は packet-in で行っている
  2. リンクダウンが起こると、本来ホストが接続されている位置ではないところで packet-in が起きる

からです。したがって、packet-in + トポロジ情報を元に arp table update を行えば良いことになります。LinkIndex では endpoint の接続されているポートと switch 間接続ポートの情報を持っています。問題を起こすのは、スイッチ間リンクになっているポートで起こる packet-in です。

プロアクティブ経路計算の廃止

あるいは、packet-in に応じて個別に経路計算してしまうのをやめてしまって、トポロジ変更検出→全スイッチ間の最短経路を計算してキャッシュしておく、なんて考え方もありそうです。全対最短経路計算のアルゴリズムで、フロイド・ワーシャル(Floyd-Warshall)法ってのがあるそう。ただ、これはアルゴリズム理解できていないし実装もできていないので当面見送り…。

アルゴリズムクイックリファレンス

アルゴリズムクイックリファレンス

まとめ

arp table の情報と物理位置が食い違うことに夜問題点を、実際に試してみました。これにより、arp table は「ホストが接続されている位置をキャッシュするもの」として固定されるべき、ということがわかりました。次回は、arp table を「固定」させた上で再度テストを行ってどうなるのかを実験してみます。