AT_SETUP([learning action - parsing and formatting])
AT_DATA([flows.txt], [[
actions=learn()
+actions=learn(send_flow_rem)
+actions=learn(delete_learned)
+actions=learn(send_flow_rem,delete_learned)
actions=learn(NXM_OF_VLAN_TCI[0..11], NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[], output:NXM_OF_IN_PORT[], load:10->NXM_NX_REG0[5..10])
actions=learn(table=1,idle_timeout=10, hard_timeout=20, fin_idle_timeout=5, fin_hard_timeout=10, priority=10, cookie=0xfedcba9876543210, in_port=99,NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],load:NXM_OF_IN_PORT[]->NXM_NX_REG1[16..31])
]])
[[usable protocols: any
chosen protocol: OpenFlow10-table_id
OFPT_FLOW_MOD (xid=0x1): ADD actions=learn(table=1)
-OFPT_FLOW_MOD (xid=0x2): ADD actions=learn(table=1,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],output:NXM_OF_IN_PORT[],load:0xa->NXM_NX_REG0[5..10])
-OFPT_FLOW_MOD (xid=0x3): ADD actions=learn(table=1,idle_timeout=10,hard_timeout=20,fin_idle_timeout=5,fin_hard_timeout=10,priority=10,cookie=0xfedcba9876543210,in_port=99,NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],load:NXM_OF_IN_PORT[]->NXM_NX_REG1[16..31])
+OFPT_FLOW_MOD (xid=0x2): ADD actions=learn(table=1,send_flow_rem)
+OFPT_FLOW_MOD (xid=0x3): ADD actions=learn(table=1,delete_learned)
+OFPT_FLOW_MOD (xid=0x4): ADD actions=learn(table=1,send_flow_rem,delete_learned)
+OFPT_FLOW_MOD (xid=0x5): ADD actions=learn(table=1,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],output:NXM_OF_IN_PORT[],load:0xa->NXM_NX_REG0[5..10])
+OFPT_FLOW_MOD (xid=0x6): ADD actions=learn(table=1,idle_timeout=10,hard_timeout=20,fin_idle_timeout=5,fin_hard_timeout=10,priority=10,cookie=0xfedcba9876543210,in_port=99,NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],load:NXM_OF_IN_PORT[]->NXM_NX_REG1[16..31])
]])
AT_CLEANUP
]])
AT_CLEANUP
+AT_SETUP([learning action - too-long immediate value])
+dnl 129 bits is too long.
+AT_CHECK([[ovs-ofctl parse-flow 'actions=learn(load:0x1fedbca9876543210fedbca9876543210->NXM_NX_IPV6_DST[])']],
+ [1], [], [[ovs-ofctl: 0x1fedbca9876543210fedbca9876543210->NXM_NX_IPV6_DST[]: value does not fit into 128 bits
+]])
+
+dnl 128 bits is merely a bad prerequisite.
+AT_CHECK([[ovs-ofctl parse-flow 'actions=learn(load:0xfedbca9876543210fedbca9876543210->NXM_NX_IPV6_DST[])']], [1], [], [stderr])
+AT_CHECK([sed -e 's/.*|meta_flow|WARN|//' < stderr], [0],
+ [[destination field ipv6_dst lacks correct prerequisites
+ovs-ofctl: actions are invalid with specified match (OFPBAC_MATCH_INCONSISTENT)
+]], [[]])
+AT_CLEANUP
+
AT_SETUP([learning action - standard VLAN+MAC learning])
OVS_VSWITCHD_START(
[add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 -- \
done
# Make sure that 15 seconds without refreshing makes the flow time out.
-ovs-appctl time/warp 5000
-ovs-appctl time/warp 5000
-ovs-appctl time/warp 5000
- AT_CHECK([ovs-ofctl dump-flows br0 table=1 | ofctl_strip | sort], [0], [dnl
+ovs-appctl time/warp 15000 5000
+sleep 1
+AT_CHECK([ovs-ofctl dump-flows br0 table=1 | ofctl_strip | sort], [0], [dnl
table=1, priority=0 actions=FLOOD
NXST_FLOW reply:
])
for i in `seq 1 10`; do
ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:06,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'
ovs-appctl time/warp 1000
- if [[ $i -eq 1 ]]; then
+ # Note: netdev-dummy/receive merely queues the packet.
+ # We need to wait for other thread to process the packet
+ # and update the flow's 'used' for the packet.
+ # (i % 3 == 0) below is somehow arbitrary but chosen to ensure
+ # that we update the flow's 'used' frequently enough to prevent
+ # idle_timeout.
+ if [[ $i -eq 1 -o $((i % 3)) -eq 0 ]]; then
sleep 1
fi
done
# it's difficult to predict the exact timing of rule expiry
# because it's affected by flow dumper thread via udpif_dump_seq.
# hard_timeout value for this test was chosen to overcome the uncertainty.
- if [[ $i -eq 1 -o $i -eq 6 -o $i -eq 7 ]]; then
+ #
+ # receive #1 learn, install flow with hard_timeout=10
+ # sleep to ensure the flow installation
+ # (warp, timeout left 8s)
+ # receive #2 the learned flow
+ # (warp, timeout left 6s)
+ # receive #3
+ # (warp, timeout left 4s)
+ # receive #4
+ # (warp, timeout left 2s)
+ # receive #5
+ # (warp, timeout left 0s)
+ # NOTE: OVS does not consider this expired yet. cf. rule_expire()
+ # receive #6
+ # (warp, timeout left -2s)
+ # sleep to ensure flow expiration
+ # receive #7 learn, install flow with hard_timeout=10
+ # sleep to ensure the flow installation
+ # (warp, timeout left 8s)
+ # receive #8
+ # (warp, timeout left 6s)
+ # receive #9
+ # (warp, timeout left 4s)
+ # receive #10
+ # (warp, timeout left 2s)
+ if [[ $i -eq 1 -o $i -eq 7 ]]; then
sleep 1
fi
ovs-appctl time/warp 2000
+ if [[ $i -eq 6 ]]; then
+ sleep 1
+ fi
done
# Check that the first packet of each flow went out port 2 and the rest out
])
OVS_VSWITCHD_STOP
AT_CLEANUP
+
+AT_SETUP([learning action - delete_learned feature])
+OVS_VSWITCHD_START
+
+# Add some initial flows and check that it was successful.
+AT_DATA([flows.txt], [dnl
+ reg0=0x1 actions=learn(delete_learned,cookie=0x123)
+ reg0=0x2 actions=learn(delete_learned,cookie=0x123)
+cookie=0x123, table=1, reg0=0x3 actions=drop
+cookie=0x123, table=1, reg0=0x4 actions=drop
+cookie=0x123, table=2, reg0=0x5 actions=drop
+cookie=0x234, table=1, reg0=0x6 actions=drop
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ cookie=0x123, table=1, reg0=0x3 actions=drop
+ cookie=0x123, table=1, reg0=0x4 actions=drop
+ cookie=0x123, table=2, reg0=0x5 actions=drop
+ cookie=0x234, table=1, reg0=0x6 actions=drop
+ reg0=0x1 actions=learn(table=1,delete_learned,cookie=0x123)
+ reg0=0x2 actions=learn(table=1,delete_learned,cookie=0x123)
+NXST_FLOW reply:
+])
+
+# Delete one of the learn actions. The learned flows should stay, since there
+# is another learn action with the identical target.
+AT_CHECK([ovs-ofctl del-flows br0 'table=0 reg0=1'])
+AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ cookie=0x123, table=1, reg0=0x3 actions=drop
+ cookie=0x123, table=1, reg0=0x4 actions=drop
+ cookie=0x123, table=2, reg0=0x5 actions=drop
+ cookie=0x234, table=1, reg0=0x6 actions=drop
+ reg0=0x2 actions=learn(table=1,delete_learned,cookie=0x123)
+NXST_FLOW reply:
+])
+
+# Change the flow with the learn action by adding a second action. The learned
+# flows should stay because the learn action is still there.
+AT_CHECK([ovs-ofctl mod-flows br0 'table=0 reg0=2 actions=output:1,learn(delete_learned,cookie=0x123)'])
+AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ cookie=0x123, table=1, reg0=0x3 actions=drop
+ cookie=0x123, table=1, reg0=0x4 actions=drop
+ cookie=0x123, table=2, reg0=0x5 actions=drop
+ cookie=0x234, table=1, reg0=0x6 actions=drop
+ reg0=0x2 actions=output:1,learn(table=1,delete_learned,cookie=0x123)
+NXST_FLOW reply:
+])
+
+# Change the flow with the learn action by replacing its learn action by one
+# with a different target. The (previous) learned flows disappear.
+AT_CHECK([ovs-ofctl mod-flows br0 'table=0 reg0=2 actions=learn(delete_learned,cookie=0x234)'])
+AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ cookie=0x123, table=2, reg0=0x5 actions=drop
+ cookie=0x234, table=1, reg0=0x6 actions=drop
+ reg0=0x2 actions=learn(table=1,delete_learned,cookie=0x234)
+NXST_FLOW reply:
+])
+
+# Use add-flow to replace the flow with the learn action by one with the
+# same learn action and an extra action. The (new) learned flow remains.
+AT_CHECK([ovs-ofctl add-flow br0 'table=0 reg0=2 actions=learn(delete_learned,cookie=0x234),output:2'])
+AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ cookie=0x123, table=2, reg0=0x5 actions=drop
+ cookie=0x234, table=1, reg0=0x6 actions=drop
+ reg0=0x2 actions=learn(table=1,delete_learned,cookie=0x234),output:2
+NXST_FLOW reply:
+])
+
+# Delete the flow with the learn action. The learned flow disappears too.
+AT_CHECK([ovs-ofctl del-flows br0 table=0])
+AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ cookie=0x123, table=2, reg0=0x5 actions=drop
+NXST_FLOW reply:
+])
+
+# Add a new set of flows to check on a corner case: the learned flows
+# contain their own learn actions which cascade to further deletions.
+# This can't happen if the learned flows were actually created by a
+# learn action, since the learn action has very restricted action
+# support, but there's no restriction that the deleted flows were
+# created by a learn action.
+AT_DATA([flows.txt], [dnl
+ reg0=0x1 actions=learn(table=1,delete_learned,cookie=0x123)
+ reg0=0x2 actions=learn(table=2,delete_learned,cookie=0x234)
+cookie=0x123, table=1, reg0=0x3 actions=learn(table=3,delete_learned,cookie=0x345)
+cookie=0x234, table=2, reg0=0x3 actions=learn(table=4,delete_learned,cookie=0x456)
+cookie=0x345, table=3, reg0=0x4 actions=learn(table=5,delete_learned,cookie=0x567)
+cookie=0x456, table=4, reg0=0x5 actions=learn(table=5,delete_learned,cookie=0x567)
+cookie=0x567, table=5, reg0=0x6 actions=drop
+cookie=0x567, table=5, reg0=0x7 actions=drop
+cookie=0x567, table=5, reg0=0x8 actions=drop
+])
+AT_CHECK([ovs-ofctl del-flows br0])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ cookie=0x123, table=1, reg0=0x3 actions=learn(table=3,delete_learned,cookie=0x345)
+ cookie=0x234, table=2, reg0=0x3 actions=learn(table=4,delete_learned,cookie=0x456)
+ cookie=0x345, table=3, reg0=0x4 actions=learn(table=5,delete_learned,cookie=0x567)
+ cookie=0x456, table=4, reg0=0x5 actions=learn(table=5,delete_learned,cookie=0x567)
+ cookie=0x567, table=5, reg0=0x6 actions=drop
+ cookie=0x567, table=5, reg0=0x7 actions=drop
+ cookie=0x567, table=5, reg0=0x8 actions=drop
+ reg0=0x1 actions=learn(table=1,delete_learned,cookie=0x123)
+ reg0=0x2 actions=learn(table=2,delete_learned,cookie=0x234)
+NXST_FLOW reply:
+])
+
+# Deleting the flow with reg0=1 should cascade to delete a few levels
+# of learned flows, but the ones with cookie=0x567 stick around
+# because of the flow with cookie=0x456.
+AT_CHECK([ovs-ofctl del-flows br0 'table=0 reg0=1'])
+AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ cookie=0x234, table=2, reg0=0x3 actions=learn(table=4,delete_learned,cookie=0x456)
+ cookie=0x456, table=4, reg0=0x5 actions=learn(table=5,delete_learned,cookie=0x567)
+ cookie=0x567, table=5, reg0=0x6 actions=drop
+ cookie=0x567, table=5, reg0=0x7 actions=drop
+ cookie=0x567, table=5, reg0=0x8 actions=drop
+ reg0=0x2 actions=learn(table=2,delete_learned,cookie=0x234)
+NXST_FLOW reply:
+])
+
+# Deleting the flow with reg0=2 should cascade to delete all the rest:
+AT_CHECK([ovs-ofctl del-flows br0 'table=0 reg0=2'])
+AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
+NXST_FLOW reply:
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP