AT_BANNER([Rapid Spanning Tree Protocol unit tests])

AT_SETUP([RSTP Single bridge])
AT_KEYWORDS([RSTP])
AT_DATA([test-rstp-num1],
[bridge 0 0x111 = a b
run 1000
check 0 = root
])
AT_CHECK([ovstest test-rstp test-rstp-num1], [0], [])
AT_CLEANUP

AT_SETUP([RSTP Link failure])
AT_KEYWORDS([RSTP])
AT_DATA([test-rstp-num2],
[bridge 0 0x111 = a b
bridge 1 0x222 = a c
bridge 2 0x333 = b c
run 1000
check 0 = root
check 1 = F:200000 F
check 2 = F:200000 Di
# Link b goes down
bridge 2 = X c
run 1000
check 1 = F:200000 F
check 2 = D F:400000
])
AT_CHECK([ovstest test-rstp test-rstp-num2], [0], [])
AT_CLEANUP

AT_SETUP([RSTP Double link Failure])
AT_KEYWORDS([RSTP])
AT_DATA([test-rstp-num3],
[bridge 0 0x111 = a b
bridge 1 0x222 = a c d
bridge 2 0x333 = b c e
bridge 3 0x444 = d f
bridge 4 0x555 = e f
run 1000
check 0 = root
check 1 = F:200000 F F
check 2 = F:200000 Di F
check 3 = F:400000 F
check 4 = F:400000 Di
# Link b goes down
bridge 2 = X c e
run 1000
check 0 = root
check 1 = F:200000 F F
check 2 = D F:400000 F
check 3 = F:400000 F
check 4 = F:600000 Di
# Link e goes down
bridge 4 = X f
run 1000
check 0 = root
check 1 = F:200000 F F
check 2 = D F:400000 F
check 3 = F:400000 F
check 4 = D F:600000
# Link f cost changes
bridge 4 = X f:100000
run 1000
check 4 = D F:500000
# Bridge 4 becomes root and
bridge 4 ^ 31000
run 1000
check 4 = root
])
AT_CHECK([ovstest test-rstp test-rstp-num3], [0], [])
AT_CLEANUP

AT_SETUP([RSTP example from IEEE 802.1D-2004 figures 17.4 and 17.5])
AT_KEYWORDS([RSTP])
AT_DATA([test-rstp-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:200000 Di F F
check 2 = F:200000 Di F F F F
check 3 = F:200000 Di F F F F
check 4 = F:400000 Di F F
check 5 = F:400000 Di F F
check 6 = F:400000 Di F F
check 7 = F:400000 Di 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:200000 Di F F
check 2 = F:200000 Di F F F F
check 3 = F:200000 Di F F F F
check 4 = F:400000 Di F F
check 5 = F:400000 Di F F
check 6 = F:400000 Di F F
check 7 = F:400000 Di F Di
])
AT_CHECK([ovstest test-rstp test-rstp-ieee802.1d-2004-fig17.4], [0], [])
AT_CLEANUP

AT_SETUP([RSTP example from IEEE 802.1D-2004 figure 17.6])
AT_KEYWORDS([RSTP])
AT_DATA([test-rstp-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:200000 F F
check 2 = F:400000 F F
check 3 = F:600000 F Di
check 4 = F:400000 F F
check 5 = F:200000 F F
])
AT_CHECK([ovstest test-rstp test-rstp-ieee802.1d-2004-fig17.6], [0], [])
AT_CLEANUP

AT_SETUP([RSTP example from IEEE 802.1D-2004 figure 17.7])
AT_KEYWORDS([RSTP])
AT_DATA([test-rstp-ieee802.1d-2004-fig17.7],
[bridge 0 0x000 = 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:200000 F F F F F F
check 2 = Di F:400000 F F F F F F
# Link g priority increment
bridge 1 = a b d f h g^112 e c
run 1000
check 0 = root
check 1 = F F:200000 F F F F F F
check 2 = F:400000 Di F F F F F F
])
AT_CHECK([ovstest test-rstp test-rstp-ieee802.1d-2004-fig17.7], [0], [])
AT_CLEANUP

m4_define([FILTER_STP_TOPOLOGY], [[
grep 'STP state changed' | sed '
    s/.*ofproto_dpif|.*|//
']])

AT_SETUP([RSTP - 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:rstp-enable=false -- \
   set bridge br0 rstp_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:rstp-enable=false -- \
   set bridge br1 rstp_enable=false -- \
])

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:rstp-enable=true -- \
])
AT_CHECK([ovs-vsctl add-port br0 p7 -- \
   set interface p7 ofport_request=7 type=dummy -- \
   set port p7 other_config:rstp-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:rstp-enable=false -- \
])
AT_CHECK([ovs-vsctl add-port br1 p8 -- \
   set interface p8 ofport_request=8 type=dummy -- \
   set port p8 other_config:rstp-enable=false -- \
])

#
# RSTP needs link to be in admin-state up, netdev-dummy is by default down
#
AT_CHECK([ovs-appctl netdev-dummy/set-admin-state p1 up], [], [dnl
OK
])

#
# Turn RSTP on in br1 after the ports have been added.
#
AT_CHECK([ovs-vsctl set bridge br1 rstp_enable=true])

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"])

AT_CHECK([ovs-vsctl set port p2 other_config:rstp-enable=true])

OVS_WAIT_UNTIL([test `cat ovs-vswitchd.log | FILTER_STP_TOPOLOGY | wc -l` -ge 2])
AT_CHECK([cat ovs-vswitchd.log | FILTER_STP_TOPOLOGY], [0], [dnl
port p1: RSTP state changed from Disabled to Discarding
port p2: RSTP state changed from Disabled to Discarding
])

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
     >> RSTP 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
     >> RSTP not in forwarding state, skipping output
])

#
# RSTP needs link to be in admin-state up, netdev-dummy is by default down
#
AT_CHECK([ovs-appctl netdev-dummy/set-admin-state p2 up], [], [dnl
OK
])

# give time for RSTP to move initially

ovs-appctl time/warp 1000
ovs-appctl time/warp 1000

OVS_WAIT_UNTIL([test `cat ovs-vswitchd.log | FILTER_STP_TOPOLOGY | wc -l` -ge 4])
AT_CHECK([cat ovs-vswitchd.log | FILTER_STP_TOPOLOGY], [0], [dnl
port p1: RSTP state changed from Disabled to Discarding
port p2: RSTP state changed from Disabled to Discarding
port p2: RSTP state changed from Discarding to Forwarding
port p1: RSTP state changed from Discarding 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_CHECK([ovs-vsctl del-br br1])

AT_CHECK([ovs-vsctl del-port br0 p7])
AT_CHECK([ovs-vsctl del-port br0 p1])

OVS_VSWITCHD_STOP
AT_CLEANUP

AT_SETUP([RSTP - 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:rstp-enable=false -- \
    set bridge br0 rstp-enable=true
])

AT_CHECK([add_of_br 1 \
           set port br1 other_config:rstp-enable=false -- \
           set bridge br1 rstp-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:rstp-enable=true -- \
    add-port br1 p2 -- \
        set interface p2 type=patch options:peer=p1 ofport_request=2 -- \
        set port p2 other_config:rstp-enable=true -- \
])

AT_CHECK([ovs-vsctl \
    add-port br0 p7 -- \
        set interface p7 ofport_request=7 type=dummy -- \
        set port p7 other_config:rstp-enable=false -- \
    add-port br1 p8 -- \
        set interface p8 ofport_request=8 type=dummy -- \
        set port p8 other_config:rstp-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 RSTP to synchronize.
ovs-appctl time/warp 5000 500

OVS_WAIT_UNTIL_EQUAL([cat ovs-vswitchd.log | FILTER_STP_TOPOLOGY], [dnl
port p1: RSTP state changed from Disabled to Discarding
port p2: RSTP state changed from Disabled to Discarding
port p2: RSTP state changed from Discarding to Forwarding
port p1: RSTP state changed from Discarding 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
