AT_BANNER([Spanning Tree Protocol unit tests])

AT_SETUP([STP example from IEEE 802.1D-1998])
AT_KEYWORDS([STP])
AT_DATA([test-stp-ieee802.1d-1998],
[bridge 0 0x42 = a b
bridge 1 0x97 = c:5 a d:5
bridge 2 0x45 = b e
bridge 3 0x57 = b:5 e:5
bridge 4 0x83 = a:5 e:5
run 1000
check 0 = root
check 1 = F F:10 F
check 2 = F:10 B
check 3 = F:5 F
check 4 = F:5 B
])
AT_CHECK([ovstest test-stp test-stp-ieee802.1d-1998], [0], [], [dnl
stp|INFO|stp42: detected topology change.
stp|INFO|stp42: detected topology change.
stp|INFO|stp97: detected topology change.
stp|INFO|stp97: detected topology change.
stp|INFO|stp97: detected topology change.
])
AT_CLEANUP

AT_SETUP([STP example from IEEE 802.1D-2004 figures 17.4 and 17.5])
AT_KEYWORDS([STP])
AT_DATA([test-stp-ieee802.1d-2004-fig17.4],
[bridge 0 0x111 = a b e c
bridge 1 0x222 = a b d f
bridge 2 0x333 = c d l j h g
bridge 3 0x444 = e f n m k i
bridge 4 0x555 = g i 0 0
bridge 5 0x666 = h k 0 0
bridge 6 0x777 = j m 0 0
bridge 7 0x888 = l n 0 0
run 1000
check 0 = root
check 1 = F:10 B F F
check 2 = F:10 B F F F F
check 3 = F:10 B F F F F
check 4 = F:20 B F F
check 5 = F:20 B F F
check 6 = F:20 B F F
check 7 = F:20 B F F

# Now connect two ports of bridge 7 to the same LAN.
bridge 7 = l n o o
# Same results except for bridge 7:
run 1000
check 0 = root
check 1 = F:10 B F F
check 2 = F:10 B F F F F
check 3 = F:10 B F F F F
check 4 = F:20 B F F
check 5 = F:20 B F F
check 6 = F:20 B F F
check 7 = F:20 B F B
])
AT_CHECK([ovstest test-stp test-stp-ieee802.1d-2004-fig17.4], [0], [], [dnl
stp|INFO|stp111: detected topology change.
stp|INFO|stp111: detected topology change.
stp|INFO|stp111: detected topology change.
stp|INFO|stp111: detected topology change.
stp|INFO|stp222: detected topology change.
])
AT_CLEANUP

AT_SETUP([STP example from IEEE 802.1D-2004 figure 17.6])
AT_KEYWORDS([STP])
AT_DATA([test-stp-ieee802.1d-2004-fig17.6],
[bridge 0 0x111 = a b l
bridge 1 0x222 = b c d
bridge 2 0x333 = d e f
bridge 3 0x444 = f g h
bridge 4 0x555 = j h i
bridge 5 0x666 = l j k
run 1000
check 0 = root
check 1 = F:10 F F
check 2 = F:20 F F
check 3 = F:30 F B
check 4 = F:20 F F
check 5 = F:10 F F
])
AT_CHECK([ovstest test-stp test-stp-ieee802.1d-2004-fig17.6], [0], [], [dnl
stp|INFO|stp111: detected topology change.
stp|INFO|stp111: detected topology change.
stp|INFO|stp111: detected topology change.
stp|INFO|stp222: detected topology change.
stp|INFO|stp222: detected topology change.
])
AT_CLEANUP

AT_SETUP([STP example from IEEE 802.1D-2004 figure 17.7])
AT_KEYWORDS([STP])
AT_DATA([test-stp-ieee802.1d-2004-fig17.7],
[bridge 0 0xaa = b
bridge 1 0x111 = a b d f h g e c
bridge 2 0x222 = g h j l n m k i
run 1000
check 0 = root
check 1 = F F:10 F F F F F F
check 2 = B F:20 F F F F F F

# This is not the port priority change described in that figure,
# but I don't understand what port priority change would cause
# that change.
bridge 2 = g X j l n m k i
run 1000
check 0 = root
check 1 = F F:10 F F F F F F
check 2 = F:20 D F F F F F F
])
AT_CHECK([ovstest test-stp test-stp-ieee802.1d-2004-fig17.7], [0], [], [dnl
stp|INFO|stpaa: detected topology change.
stp|INFO|stp111: detected topology change.
stp|INFO|stp111: detected topology change.
stp|INFO|stp111: detected topology change.
stp|INFO|stp111: detected topology change.
])
AT_CLEANUP

AT_SETUP([STP.io.1.1: Link Failure])
AT_KEYWORDS([STP])
AT_DATA([test-stp-iol-io-1.1],
[# This test file approximates the following test from "Bridge
# Functions Consortium Spanning Tree Interoperability Test Suite
# Version 1.5":
#
# STP.io.1.1: Link Failure
bridge 0 0x111 = a b c
bridge 1 0x222 = a b c
run 1000
check 0 = root
check 1 = F:10 B B
bridge 1 = 0 _ _
run 1000
check 0 = root
check 1 = F F:10 B
bridge 1 = X _ _
run 1000
check 0 = root
check 1 = D F:10 B
bridge 1 = _ 0 _
run 1000
check 0 = root
check 1 = D F F:10
bridge 1 = _ X _
run 1000
check 0 = root
check 1 = D D F:10
])
AT_CHECK([ovstest test-stp test-stp-iol-io-1.1], [0], [], [dnl
stp|INFO|stp111: detected topology change.
stp|INFO|stp111: detected topology change.
stp|INFO|stp111: detected topology change.
stp|INFO|stp222: detected topology change.
stp|INFO|stp111: detected topology change.
])
AT_CLEANUP

AT_SETUP([STP.io.1.2: Repeated Network])
AT_KEYWORDS([STP])
AT_DATA([test-stp-iol-io-1.2],
[# This test file approximates the following test from "Bridge
# Functions Consortium Spanning Tree Interoperability Test Suite
# Version 1.5":
# STP.io.1.2: Repeated Network
bridge 0 0x111 = a a
bridge 1 0x222 = a a
run 1000
check 0 = rootid:0x111 F B
check 1 = rootid:0x111 F:10 B
bridge 1 = a^0x90 _
run 1000
check 0 = rootid:0x111 F B
check 1 = rootid:0x111 B F:10
])
AT_CHECK([ovstest test-stp test-stp-iol-io-1.2], [0], [], [dnl
stp|INFO|stp111: detected topology change.
stp|INFO|stp222: detected topology change.
stp|INFO|stp111: detected topology change.
])
AT_CLEANUP

AT_SETUP([STP.io.1.4: Network Initialization])
AT_KEYWORDS([STP])
AT_DATA([test-stp-iol-io-1.4],
[# This test file approximates the following test from "Bridge
# Functions Consortium Spanning Tree Interoperability Test Suite
# Version 1.5":
# STP.io.1.4: Network Initialization
bridge 0 0x111 = a b c
bridge 1 0x222 = b d e
bridge 2 0x333 = a d f
bridge 3 0x444 = c e f
run 1000
check 0 = root
check 1 = F:10 F F
check 2 = F:10 B F
check 3 = F:10 B B
])
AT_CHECK([ovstest test-stp test-stp-iol-io-1.4], [0], [], [dnl
stp|INFO|stp111: detected topology change.
stp|INFO|stp111: detected topology change.
stp|INFO|stp111: detected topology change.
stp|INFO|stp222: detected topology change.
stp|INFO|stp222: detected topology change.
])
AT_CLEANUP

AT_SETUP([STP.io.1.5: Topology Change])
AT_KEYWORDS([STP])
AT_DATA([test-stp-iol-io-1.5],
[# This test file approximates the following test from "Bridge
# Functions Consortium Spanning Tree Interoperability Test Suite
# Version 1.5":
# STP.io.1.5: Topology Change
bridge 0 0x111 = a b d c
bridge 1 0x222 = a b f e
bridge 2 0x333 = c d g h
bridge 3 0x444 = e f g h
run 1000
check 0 = root
check 1 = F:10 B F F
check 2 = B F:10 F F
check 3 = B F:20 B B
bridge 1^0x7000
run 1000
check 0 = F:10 B F F
check 1 = root
check 2 = B F:20 B B
check 3 = B F:10 F F
bridge 2^0x6000
run 1000
check 0 = F F B F:10
check 1 = F:20 B B B
check 2 = root
check 3 = F F F:10 B
bridge 3^0x5000
run 1000
check 0 = B B B F:20
check 1 = F F B F:10
check 2 = F F F:10 B
check 3 = root
bridge 0^0x4000
bridge 1^0x4001
bridge 2^0x4002
bridge 3^0x4003
run 1000
check 0 = root
check 1 = F:10 B F F
check 2 = B F:10 F F
check 3 = B F:20 B B
])
AT_CHECK([ovstest test-stp test-stp-iol-io-1.5], [0], [], [dnl
stp|INFO|stp111: detected topology change.
stp|INFO|stp111: detected topology change.
stp|INFO|stp111: detected topology change.
stp|INFO|stp111: detected topology change.
stp|INFO|stp222: detected topology change.
])
AT_CLEANUP

AT_SETUP([STP.op.1.1 and STP.op.1.2])
AT_KEYWORDS([STP])
AT_DATA([test-stp-iol-op-1.1],
[# This test file approximates the following tests from "Bridge
# Functions Consortium Spanning Tree Protocol Operations Test Suite
# Version 2.3":
# Test STP.op.1.1: Root ID Initialized to Bridge ID
# Test STP.op.1.2: Root Path Cost Initialized to Zero
bridge 0 0x123 =
check 0 = root
])
AT_CHECK([ovstest test-stp test-stp-iol-op-1.1])
AT_CLEANUP

AT_SETUP([STP.op.1.4: All Ports Initialized to Designated Ports])
AT_KEYWORDS([STP])
AT_DATA([test-stp-iol-op-1.4],
[# This test file approximates the following test from "Bridge
# Functions Consortium Spanning Tree Protocol Operations Test Suite
# Version 2.3":
# Test STP.op.1.4: All Ports Initialized to Designated Ports
bridge 0 0x123 = a b c d e f
check 0 = Li Li Li Li Li Li
run 1000
check 0 = F F F F F F
])
AT_CHECK([ovstest test-stp test-stp-iol-op-1.4], [0], [], [dnl
stp|INFO|stp123: detected topology change.
stp|INFO|stp123: detected topology change.
stp|INFO|stp123: detected topology change.
stp|INFO|stp123: detected topology change.
stp|INFO|stp123: detected topology change.
])
AT_CLEANUP

AT_SETUP([STP.op.3.1: Root Bridge Selection: Root ID Values])
AT_KEYWORDS([STP])
AT_DATA([test-stp-iol-op-3.1],
[# This test file approximates the following test from "Bridge
# Functions Consortium Spanning Tree Protocol Operations Test Suite
# Version 2.3":
# Test STP.op.3.1: Root Bridge Selection: Root ID Values
bridge 0 0x111 = a
bridge 1 0x222 = a
check 0 = rootid:0x111 Li
check 1 = rootid:0x222 Li
run 1000
check 0 = rootid:0x111 root
check 1 = rootid:0x111 F:10
])
AT_CHECK([ovstest test-stp test-stp-iol-op-3.1], [0], [], [dnl
stp|INFO|stp111: detected topology change.
])
AT_CLEANUP

AT_SETUP([STP.op.3.3: Root Bridge Selection: Bridge ID Values])
AT_KEYWORDS([STP])
AT_DATA([test-stp-iol-op-3.3],
[# This test file approximates the following test from "Bridge
# Functions Consortium Spanning Tree Protocol Operations Test Suite
# Version 2.3":
# Test STP.op.3.3: Root Bridge Selection: Bridge ID Values
bridge 0 0x333^0x6000 = a
bridge 1 0x222^0x7000 = b
bridge 2 0x111 = a b
run 1000
check 0 = rootid:0x333^0x6000 root
check 1 = rootid:0x333^0x6000 F:20
check 2 = rootid:0x333^0x6000 F:10 F
])
AT_CHECK([ovstest test-stp test-stp-iol-op-3.3], [0], [], [dnl
stp|INFO|stp333: detected topology change.
stp|INFO|stp111: detected topology change.
stp|INFO|stp111: detected topology change.
stp|INFO|stp333: detected topology change.
])
AT_CLEANUP

AT_SETUP([STP.op.3.3: Root Bridge Selection: Bridge ID Values])
AT_KEYWORDS([STP])
AT_DATA([test-stp-iol-op-3.4],
[# This test file approximates the following test from "Bridge
# Functions Consortium Spanning Tree Protocol Operations Test Suite
# Version 2.3":
# Test STP.op.3.3: Root Bridge Selection: Bridge ID Values
bridge 0 0x333^0x6000 = a
bridge 1 0x222^0x7000 = b
bridge 2 0x111 = a b
run 1000
check 0 = rootid:0x333^0x6000 root
check 1 = rootid:0x333^0x6000 F:20
check 2 = rootid:0x333^0x6000 F:10 F
])
AT_CHECK([ovstest test-stp test-stp-iol-op-3.4], [0], [], [dnl
stp|INFO|stp333: detected topology change.
stp|INFO|stp111: detected topology change.
stp|INFO|stp111: detected topology change.
stp|INFO|stp333: detected topology change.
])
AT_CLEANUP

# Strips out uninteresting parts of flow output, as well as parts
# that vary from one run to another (e.g., timing and bond actions).
m4_define([STRIP_USED], [[sed '
    s/used:[0-9\.][0-9\.]*/used:0.0/
    s/duration=[0-9.]*s*/duration=Xs/
    s/idle_age=[0-9]*,/idle_age=X,/
']])
m4_define([FILTER_STP_TOPOLOGY], [[
grep 'STP state change' | sed '
    s/.*ofproto_dpif|.*|port .*:/port <>:/
']])

m4_define([FILTER_STP_TOPOLOGY_LISTENING], [[
grep 'disabled to listening' | sed '
  s/.*ofproto_dpif|.*|port .*:/port <>:/
']])

m4_define([FILTER_STP_TOPOLOGY_FORWARDING], [[
grep 'learning to forwarding' | sed '
  s/.*ofproto_dpif|.*|port .*:/port <>:/
']])

AT_SETUP([STP - dummy interface])
# Create br0 with interfaces p1 and p7
#    and br1 with interfaces p2 and p8
# with p1 and p2 connected via unix domain socket
OVS_VSWITCHD_START(
  [set port br0 other_config:stp-enable=false -- \
   set bridge br0 stp_enable=true -- \
   add-br br1 -- \
   set bridge br1 other-config:hwaddr=aa:66:aa:66:00:00 -- \
   set bridge br1 datapath-type=dummy other-config:datapath-id=1234 \
                  fail-mode=secure -- \
   set port br1 other_config:stp-enable=false -- \
   set bridge br1 stp_enable=true --])

AT_CHECK([ovs-appctl vlog/set ofproto_dpif:dbg])

AT_CHECK([ovs-vsctl add-port br0 p1 -- \
   set interface p1 type=dummy options:pstream=punix:$OVS_RUNDIR/p0.sock ofport_request=1 -- \
   set port p1 other_config:stp-enable=true -- \
])
AT_CHECK([ovs-vsctl add-port br0 p7 -- \
   set interface p7 ofport_request=7 type=dummy -- \
   set port p7 other_config:stp-enable=false -- \
])
AT_CHECK([ovs-vsctl add-port br1 p2 -- \
   set interface p2 type=dummy options:stream=unix:$OVS_RUNDIR/p0.sock ofport_request=2 -- \
   set port p2 other_config:stp-enable=true -- \
])
AT_CHECK([ovs-vsctl add-port br1 p8 -- \
   set interface p8 ofport_request=8 type=dummy -- \
   set port p8 other_config:stp-enable=false -- \
])

ovs-appctl netdev-dummy/set-admin-state up
ovs-appctl time/stop

AT_CHECK([ovs-ofctl add-flow br0 "in_port=7 icmp actions=1"])
AT_CHECK([ovs-ofctl add-flow br0 "in_port=1 icmp actions=7"])
AT_CHECK([ovs-ofctl add-flow br1 "in_port=8 icmp actions=2"])
AT_CHECK([ovs-ofctl add-flow br1 "in_port=2 icmp actions=8"])

# give time for STP to move initially
ovs-appctl time/warp 6000 3000

AT_CHECK([cat ovs-vswitchd.log | FILTER_STP_TOPOLOGY_LISTENING], [0], [dnl
port <>: STP state changed from disabled to listening
port <>: STP state changed from disabled to listening
])

AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(7),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' | grep STP], [0], [dnl
     >> STP not in forwarding state, skipping output
])
AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(8),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' | grep STP], [0], [dnl
     >> STP not in forwarding state, skipping output
])

# give time for STP to synchronize
ovs-appctl time/warp 30000 3000

AT_CHECK([cat ovs-vswitchd.log | FILTER_STP_TOPOLOGY], [0], [dnl
port <>: STP state changed from disabled to listening
port <>: STP state changed from disabled to listening
port <>: STP state changed from listening to learning
port <>: STP state changed from listening to learning
port <>: STP state changed from learning to forwarding
port <>: STP state changed from learning to forwarding
])

AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(7),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' | grep Datapath], [0], [dnl
Datapath actions: 1
])
AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(8),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' | grep Datapath], [0], [dnl
Datapath actions: 2
])

AT_CLEANUP

AT_SETUP([STP - patch ports])
# Create br0 with interfaces p1 and p7
#    and br1 with interfaces p2 and p8
# with p1 and p2 being connected patch ports.
OVS_VSWITCHD_START(
   [set port br0 other_config:stp-enable=false -- \
    set bridge br0 stp-enable=true
])

AT_CHECK([add_of_br 1 \
           set port br1 other_config:stp-enable=false -- \
           set bridge br1 stp-enable=true])

ovs-appctl time/stop

AT_CHECK([ovs-vsctl \
    add-port br0 p1 -- \
        set interface p1 type=patch options:peer=p2 ofport_request=1 -- \
        set port p1 other_config:stp-enable=true -- \
    add-port br1 p2 -- \
        set interface p2 type=patch options:peer=p1 ofport_request=2 -- \
        set port p2 other_config:stp-enable=true -- \
])

AT_CHECK([ovs-vsctl \
    add-port br0 p7 -- \
        set interface p7 ofport_request=7 type=dummy -- \
        set port p7 other_config:stp-enable=false -- \
    add-port br1 p8 -- \
        set interface p8 ofport_request=8 type=dummy -- \
        set port p8 other_config:stp-enable=false -- \
])

AT_CHECK([ovs-ofctl add-flow br0 "in_port=7 icmp actions=1"])
AT_CHECK([ovs-ofctl add-flow br0 "in_port=1 icmp actions=7"])
AT_CHECK([ovs-ofctl add-flow br1 "in_port=8 icmp actions=2"])
AT_CHECK([ovs-ofctl add-flow br1 "in_port=2 icmp actions=8"])

# Give time for STP to synchronize.
ovs-appctl time/warp 30000 3000

OVS_WAIT_UNTIL_EQUAL([cat ovs-vswitchd.log | FILTER_STP_TOPOLOGY], [dnl
port <>: STP state changed from disabled to listening
port <>: STP state changed from disabled to listening
port <>: STP state changed from listening to learning
port <>: STP state changed from listening to learning
port <>: STP state changed from learning to forwarding
port <>: STP state changed from learning to forwarding])

AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(7),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' | grep Datapath], [0], [dnl
Datapath actions: 8
])
AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(8),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' | grep Datapath], [0], [dnl
Datapath actions: 7
])

OVS_VSWITCHD_STOP
AT_CLEANUP

AT_SETUP([STP - flush the fdb and mdb when topology changed])
OVS_VSWITCHD_START([])

# setting as below, the br0 will be root bridge and p5 will be blocked.
AT_CHECK([
    ovs-vsctl -- \
    set port br0 other_config:stp-enable=false -- \
    set bridge br0 datapath-type=dummy -- \
    set bridge br0 stp_enable=true mcast_snooping_enable=true \
    other-config:hwaddr=aa:66:aa:66:00:00 -- \
    add-br br1 -- \
    set port br1 other_config:stp-enable=false -- \
    set bridge br1 datapath-type=dummy -- \
    set bridge br1 stp_enable=true mcast_snooping_enable=true \
    other-config:hwaddr=aa:66:aa:66:00:01 -- \
    add-br br2 -- \
    set port br2 other_config:stp-enable=false -- \
    set bridge br2 datapath-type=dummy -- \
    set bridge br2 stp_enable=true mcast_snooping_enable=true \
    other-config:hwaddr=aa:66:aa:66:00:02
], [0])

AT_CHECK([ovs-appctl vlog/set ofproto_dpif:dbg])
AT_CHECK([ovs-appctl vlog/set ofproto_dpif_xlate:dbg])

AT_CHECK([ovs-ofctl add-flow br0 action=normal])
AT_CHECK([ovs-ofctl add-flow br1 action=normal])
AT_CHECK([ovs-ofctl add-flow br2 action=normal])

AT_CHECK([
    ovs-vsctl add-port br0 p1 -- \
        set interface p1 type=dummy options:pstream=punix:$OVS_RUNDIR/p1.sock ofport_request=1
    ovs-vsctl add-port br0 p2 -- \
        set interface p2 type=dummy options:stream=unix:$OVS_RUNDIR/p6.sock ofport_request=2
    ovs-vsctl add-port br1 p3 -- \
        set interface p3 type=dummy options:stream=unix:$OVS_RUNDIR/p1.sock ofport_request=3
    ovs-vsctl add-port br1 p4 -- \
        set interface p4 type=dummy options:pstream=punix:$OVS_RUNDIR/p4.sock ofport_request=4
    ovs-vsctl add-port br2 p5 -- \
        set interface p5 type=dummy options:stream=unix:$OVS_RUNDIR/p4.sock ofport_request=5
    ovs-vsctl add-port br2 p6 -- \
        set interface p6 type=dummy options:pstream=punix:$OVS_RUNDIR/p6.sock ofport_request=6
], [0])

ovs-appctl netdev-dummy/set-admin-state up
ovs-appctl time/stop

# give time for STP to move initially
ovs-appctl time/warp 6000 3000

AT_CHECK([cat ovs-vswitchd.log | FILTER_STP_TOPOLOGY_LISTENING], [0], [dnl
port <>: STP state changed from disabled to listening
port <>: STP state changed from disabled to listening
port <>: STP state changed from disabled to listening
port <>: STP state changed from disabled to listening
port <>: STP state changed from disabled to listening
port <>: STP state changed from disabled to listening
])

# give time for STP to synchronize
ovs-appctl time/warp 30000 3000

AT_CHECK([cat ovs-vswitchd.log | FILTER_STP_TOPOLOGY_FORWARDING], [0], [dnl
port <>: STP state changed from learning to forwarding
port <>: STP state changed from learning to forwarding
port <>: STP state changed from learning to forwarding
port <>: STP state changed from learning to forwarding
port <>: STP state changed from learning to forwarding
])

# When topology is changed or the root brdige receives the TCN BPDU, the
# root bridge will start the topology change timer. We should wait the
# topology change timer to stop after 35s (max age 20 + forward delay 15).
# After 35s, the root bridge will stop send CONF BPDU with
# STP_CONFIG_TOPOLOGY_CHANGE flag and the topology will be stable. More
# importantly, we should make time warp (in a second) because the hold timer
# of stp ports will stop after 1s. So the root bridge can send quickly
# topology change ack (other bridges may send TCN BPDU to root bridge) for
# avoiding root brdige to flush fdb and mdb frequently.
ovs-appctl time/warp 36000 1000

# root bridge sends query packet
# we don't want to lose that message, so send it twice
AT_CHECK([ovs-appctl netdev-dummy/receive br0 \
        '01005E010101000C29A027D18100000108004500001C000100004002CBCBAC102201E00101011114EEEB00000000'])

ovs-appctl time/warp 1000
AT_CHECK([ovs-appctl netdev-dummy/receive br0 \
        '01005E010101000C29A027D18100000108004500001C000100004002CBCBAC102201E00101011114EEEB00000000'])

OVS_WAIT_UNTIL([ovs-appctl fdb/show br0 | grep '00:0c:29:a0:27:d1'])
OVS_WAIT_UNTIL([ovs-appctl fdb/show br1 | grep '00:0c:29:a0:27:d1'])
OVS_WAIT_UNTIL([ovs-appctl fdb/show br2 | grep '00:0c:29:a0:27:d1'])

OVS_WAIT_UNTIL([ovs-appctl mdb/show br0 | grep 'querier'])
OVS_WAIT_UNTIL([ovs-appctl mdb/show br1 | grep 'querier'])
OVS_WAIT_UNTIL([ovs-appctl mdb/show br2 | grep 'querier'])

# Make sure that  validation of flows was before changing of topology.
ovs-appctl revalidator/wait

# del p2 on the br0, the topology will be changed
AT_CHECK([ovs-vsctl del-port br0 p2])

# give time for STP to synchronize
ovs-appctl time/warp 36000 3000

# check fdb and mdb
AT_CHECK([ovs-appctl fdb/show br0], [0], [dnl
 port  VLAN  MAC                Age
])
AT_CHECK([ovs-appctl fdb/show br1], [0], [dnl
 port  VLAN  MAC                Age
])
AT_CHECK([ovs-appctl fdb/show br2], [0], [dnl
 port  VLAN  MAC                Age
])

AT_CHECK([ovs-appctl mdb/show br0], [0], [dnl
 port  VLAN  protocol  GROUP                Age
])
AT_CHECK([ovs-appctl mdb/show br1], [0], [dnl
 port  VLAN  protocol  GROUP                Age
])
AT_CHECK([ovs-appctl mdb/show br2], [0], [dnl
 port  VLAN  protocol  GROUP                Age
])

AT_CLEANUP

AT_SETUP([STP - check link-state when stp is running])
OVS_VSWITCHD_START([])

AT_CHECK([
    ovs-vsctl -- \
    set port br0 other_config:stp-enable=false -- \
    set bridge br0 datapath-type=dummy stp_enable=true \
    other-config:hwaddr=aa:66:aa:66:00:00
], [0])

AT_CHECK([
    ovs-vsctl add-port br0 p1 -- \
        set interface p1 type=dummy -- \
        set port p1 other_config:stp-port-num=1
    ovs-vsctl add-port br0 p2 -- \
        set interface p2 type=dummy -- \
        set port p2 other_config:stp-port-num=2
], [0])

ovs-appctl netdev-dummy/set-admin-state up
ovs-appctl time/stop

# give time for STP to move initially
ovs-appctl time/warp 31000 1000

AT_CHECK([ovs-appctl stp/show br0 | grep p1], [0], [dnl
  p1         designated forwarding 2     128.1
])
AT_CHECK([ovs-appctl stp/show br0 | grep p2], [0], [dnl
  p2         designated forwarding 2     128.2
])

# add a stp port
AT_CHECK([
    ovs-vsctl add-port br0 p3 -- \
        set interface p3 type=dummy -- \
        set port p3 other_config:stp-port-num=3
], [0])

ovs-appctl netdev-dummy/set-admin-state p3 down

# We should not show the p3 because its link-state is down
AT_CHECK([ovs-appctl stp/show br0 | grep p1], [0], [dnl
  p1         designated forwarding 2     128.1
])
AT_CHECK([ovs-appctl stp/show br0 | grep p2], [0], [dnl
  p2         designated forwarding 2     128.2
])
AT_CHECK([ovs-appctl stp/show br0 | grep p3], [1], [dnl
])

ovs-appctl netdev-dummy/set-admin-state p3 up

AT_CHECK([ovs-appctl stp/show br0 | grep p1], [0], [dnl
  p1         designated forwarding 2     128.1
])
AT_CHECK([ovs-appctl stp/show br0 | grep p2], [0], [dnl
  p2         designated forwarding 2     128.2
])
AT_CHECK([ovs-appctl stp/show br0 | grep p3], [0], [dnl
  p3         designated listening  2     128.3
])


OVS_VSWITCHD_STOP
AT_CLEANUP
