mirror of
https://github.com/openvswitch/ovs
synced 2025-10-21 14:49:41 +00:00
In Open vSwitch, a "modify" or "modify_strict" flow_mod is supposed to refresh the flow's last-modified time even if nothing else changes, because this interpretation makes the "learn" action more useful. As commit308881afb
(ofproto: Reinterpret meaning of OpenFlow hard timeouts with OFPFC_MODIFY.) notes: I finally found a good use for hard timeouts in OpenFlow, but they require a slight reinterpretation of the meaning of hard timeouts. Until now, a hard timeout meant that a flow would be removed the specified number of seconds after a flow was created. Intervening modifications with OFPFC_MODIFY(_STRICT) had no effect on the hard timeout; the flow would still be deleted the specified number of seconds after its original creation. This commit changes the effect of OFPFC_MODIFY(_STRICT). Now, modifying a flow resets its hard timeout counter. A flow will time out the specified number of seconds after creation or after the last time it is modified, whichever comes later. However, commit080437614b
(ofproto: Represent flow cookie changes as operations too.) broke this behavior because it incorrectly optimized out "modify" operations that didn't change the flow's actions or flow cookie. This commit fixes the problem, and adds a test to prevent future regression. Thanks to Amar Padmanabhan <amar@nicira.com> for helping to track this down. Bug #14841. Reported-by: Hiroshi Tanaka <htanaka@vmware.com> CC: Amar Padmanabhan <amar@nicira.com> Signed-off-by: Ben Pfaff <blp@nicira.com>
269 lines
14 KiB
Plaintext
269 lines
14 KiB
Plaintext
AT_BANNER([learning action])
|
|
|
|
AT_SETUP([learning action - parsing and formatting])
|
|
AT_DATA([flows.txt], [[
|
|
actions=learn()
|
|
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])
|
|
]])
|
|
AT_CHECK([ovs-ofctl parse-flows flows.txt], [0],
|
|
[[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])
|
|
]])
|
|
AT_CLEANUP
|
|
|
|
AT_SETUP([learning action - examples])
|
|
AT_DATA([flows.txt], [[
|
|
# These are the examples from nicira-ext.h.
|
|
actions=learn(in_port=99,NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[], load:NXM_OF_IN_PORT[]->NXM_NX_REG1[16..31])
|
|
actions=learn(NXM_OF_VLAN_TCI[0..11], NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],output:NXM_OF_IN_PORT[])
|
|
table=0 actions=learn(table=1,hard_timeout=10, NXM_OF_VLAN_TCI[0..11],output:NXM_OF_IN_PORT[]), resubmit(,1)
|
|
table=1 priority=0 actions=flood
|
|
]])
|
|
AT_CHECK([ovs-ofctl parse-flows flows.txt], [0],
|
|
[[usable protocols: OXM,OpenFlow10+table_id,NXM+table_id
|
|
chosen protocol: OpenFlow10+table_id
|
|
OFPT_FLOW_MOD (xid=0x1): ADD table:255 actions=learn(table=1,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 table:255 actions=learn(table=1,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],output:NXM_OF_IN_PORT[])
|
|
OFPT_FLOW_MOD (xid=0x3): ADD actions=learn(table=1,hard_timeout=10,NXM_OF_VLAN_TCI[0..11],output:NXM_OF_IN_PORT[]),resubmit(,1)
|
|
OFPT_FLOW_MOD (xid=0x4): ADD table:1 priority=0 actions=FLOOD
|
|
]])
|
|
AT_CLEANUP
|
|
|
|
AT_SETUP([learning action - satisfied prerequisites])
|
|
AT_DATA([flows.txt],
|
|
[[actions=learn(eth_type=0x800,load:5->NXM_OF_IP_DST[])
|
|
ip,actions=learn(load:NXM_OF_IP_DST[]->NXM_NX_REG1[])
|
|
ip,actions=learn(eth_type=0x800,OXM_OF_IPV4_DST[])
|
|
]])
|
|
AT_CHECK([ovs-ofctl parse-flows flows.txt], [0],
|
|
[[usable protocols: any
|
|
chosen protocol: OpenFlow10-table_id
|
|
OFPT_FLOW_MOD (xid=0x1): ADD actions=learn(table=1,eth_type=0x800,load:0x5->NXM_OF_IP_DST[])
|
|
OFPT_FLOW_MOD (xid=0x2): ADD ip actions=learn(table=1,load:NXM_OF_IP_DST[]->NXM_NX_REG1[])
|
|
OFPT_FLOW_MOD (xid=0x3): ADD ip actions=learn(table=1,eth_type=0x800,NXM_OF_IP_DST[])
|
|
]])
|
|
AT_CLEANUP
|
|
|
|
AT_SETUP([learning action - invalid prerequisites])
|
|
AT_CHECK([[ovs-ofctl parse-flow 'actions=learn(load:5->NXM_OF_IP_DST[])']],
|
|
[1], [],
|
|
[[ovs-ofctl: load:5->NXM_OF_IP_DST[]: cannot specify destination field ip_dst because prerequisites are not satisfied
|
|
]])
|
|
AT_CHECK([[ovs-ofctl parse-flow 'actions=learn(load:NXM_OF_IP_DST[]->NXM_NX_REG1[])']],
|
|
[1], [],
|
|
[[ovs-ofctl: load:NXM_OF_IP_DST[]->NXM_NX_REG1[]: cannot specify source field ip_dst because prerequisites are not satisfied
|
|
]])
|
|
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 -- \
|
|
add-port br0 p2 -- set Interface p2 type=dummy ofport_request=2 -- \
|
|
add-port br0 p3 -- set Interface p3 type=dummy ofport_request=3])
|
|
# Set up flow table for VLAN+MAC learning.
|
|
AT_DATA([flows.txt], [[
|
|
table=0 actions=learn(table=1, hard_timeout=60, NXM_OF_VLAN_TCI[0..11], NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[], output:NXM_OF_IN_PORT[]), resubmit(,1)
|
|
table=1 priority=0 actions=flood
|
|
]])
|
|
AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
|
|
|
|
# Trace an ARP packet arriving on port 3, to create a MAC learning entry.
|
|
flow="in_port(3),eth(src=50:54:00:00:00:05,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=192.168.0.1,tip=192.168.0.2,op=1,sha=50:54:00:00:00:05,tha=00:00:00:00:00:00)"
|
|
AT_CHECK([ovs-appctl ofproto/trace br0 "$flow" -generate], [0], [stdout])
|
|
actual=`tail -1 stdout | sed 's/Datapath actions: //'`
|
|
|
|
expected="1,2,100"
|
|
AT_CHECK([ovs-dpctl normalize-actions "$flow" "$expected"], [0], [stdout])
|
|
mv stdout expout
|
|
AT_CHECK([ovs-dpctl normalize-actions "$flow" "$actual"], [0], [expout])
|
|
|
|
# Check for the MAC learning entry.
|
|
AT_CHECK([ovs-ofctl dump-flows br0 table=1 | ofctl_strip | sort], [0], [dnl
|
|
table=1, hard_timeout=60, vlan_tci=0x0000/0x0fff,dl_dst=50:54:00:00:00:05 actions=output:3
|
|
table=1, priority=0 actions=FLOOD
|
|
NXST_FLOW reply:
|
|
])
|
|
|
|
# Trace a packet arrival destined for the learned MAC.
|
|
# (This will also learn a MAC.)
|
|
AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(1),eth(src=50:54:00:00:00:06,dst=50:54:00:00:00:05),eth_type(0x0806),arp(sip=192.168.0.2,tip=192.168.0.1,op=2,sha=50:54:00:00:00:06,tha=50:54:00:00:00:05)' -generate], [0], [stdout])
|
|
AT_CHECK([tail -1 stdout], [0], [Datapath actions: 3
|
|
])
|
|
|
|
# Check for both MAC learning entries.
|
|
AT_CHECK([ovs-ofctl dump-flows br0 table=1 | ofctl_strip |sort], [0], [dnl
|
|
table=1, hard_timeout=60, vlan_tci=0x0000/0x0fff,dl_dst=50:54:00:00:00:05 actions=output:3
|
|
table=1, hard_timeout=60, vlan_tci=0x0000/0x0fff,dl_dst=50:54:00:00:00:06 actions=output:1
|
|
table=1, priority=0 actions=FLOOD
|
|
NXST_FLOW reply:
|
|
])
|
|
|
|
# Trace a packet arrival that updates the first learned MAC entry.
|
|
flow="in_port(2),eth(src=50:54:00:00:00:05,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=192.168.0.1,tip=192.168.0.2,op=1,sha=50:54:00:00:00:05,tha=00:00:00:00:00:00)"
|
|
AT_CHECK([ovs-appctl ofproto/trace br0 "$flow" -generate], [0], [stdout])
|
|
actual=`tail -1 stdout | sed 's/Datapath actions: //'`
|
|
|
|
expected="1,3,100"
|
|
AT_CHECK([ovs-dpctl normalize-actions "$flow" "$expected"], [0], [stdout])
|
|
mv stdout expout
|
|
AT_CHECK([ovs-dpctl normalize-actions "$flow" "$actual"], [0], [expout])
|
|
|
|
# Check that the MAC learning entry was updated.
|
|
AT_CHECK([ovs-ofctl dump-flows br0 table=1 | ofctl_strip | sort], [0], [dnl
|
|
table=1, hard_timeout=60, vlan_tci=0x0000/0x0fff,dl_dst=50:54:00:00:00:05 actions=output:2
|
|
table=1, hard_timeout=60, vlan_tci=0x0000/0x0fff,dl_dst=50:54:00:00:00:06 actions=output:1
|
|
table=1, priority=0 actions=FLOOD
|
|
NXST_FLOW reply:
|
|
])
|
|
OVS_VSWITCHD_STOP
|
|
AT_CLEANUP
|
|
|
|
dnl This test checks that repeated uses of a "learn" action cause the
|
|
dnl modified time of the learned flow to advance. Otherwise, the
|
|
dnl learned flow will expire after its hard timeout even though it's
|
|
dnl supposed to be refreshed. (The expiration can be hard to see since
|
|
dnl it gets re-learned again the next time a packet appears, but
|
|
dnl sometimes the expiration can cause temporary flooding etc.)
|
|
AT_SETUP([learning action - learn refreshes hard_age])
|
|
OVS_VSWITCHD_START(
|
|
[add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 -- \
|
|
add-port br0 p2 -- set Interface p2 type=dummy ofport_request=2 -- \
|
|
add-port br0 p3 -- set Interface p3 type=dummy ofport_request=3])
|
|
|
|
ovs-appctl time/stop
|
|
|
|
# Set up flow table for MAC learning.
|
|
AT_DATA([flows.txt], [[
|
|
table=0 actions=learn(table=1, hard_timeout=10, NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[], output:NXM_OF_IN_PORT[]), resubmit(,1)
|
|
table=1 priority=0 actions=flood
|
|
]])
|
|
AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
|
|
|
|
# Trace an ICMP packet arriving on port 3, to create a MAC learning entry.
|
|
flow="in_port(3),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=0,code=0)"
|
|
AT_CHECK([ovs-appctl ofproto/trace br0 "$flow" -generate], [0], [stdout])
|
|
actual=`tail -1 stdout | sed 's/Datapath actions: //'`
|
|
|
|
expected="1,2,100"
|
|
AT_CHECK([ovs-dpctl normalize-actions "$flow" "$expected"], [0], [stdout])
|
|
mv stdout expout
|
|
AT_CHECK([ovs-dpctl normalize-actions "$flow" "$actual"], [0], [expout])
|
|
|
|
# Check that the MAC learning entry appeared.
|
|
AT_CHECK([ovs-ofctl dump-flows br0 table=1 | ofctl_strip | sort], [0], [dnl
|
|
table=1, hard_timeout=10, dl_dst=50:54:00:00:00:07 actions=output:3
|
|
table=1, priority=0 actions=FLOOD
|
|
NXST_FLOW reply:
|
|
])
|
|
|
|
# For 25 seconds, make sure that the MAC learning entry doesn't
|
|
# disappear as long as we refresh it every second.
|
|
for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25; do
|
|
ovs-appctl time/warp 1000
|
|
AT_CHECK([ovs-appctl ofproto/trace br0 "$flow" -generate], [0], [stdout])
|
|
|
|
# Check that the entry is there.
|
|
AT_CHECK([ovs-ofctl dump-flows br0 table=1], [0], [stdout])
|
|
AT_CHECK([ofctl_strip < stdout | sort], [0], [dnl
|
|
table=1, hard_timeout=10, dl_dst=50:54:00:00:00:07 actions=output:3
|
|
table=1, priority=0 actions=FLOOD
|
|
NXST_FLOW reply:
|
|
])
|
|
|
|
if test $i != 1; then
|
|
# Check that hard_age has appeared. We need to do this separately
|
|
# from the above check because ofctl_strip removes it. dump-flows
|
|
# only prints hard_age when it is different from the flow's duration
|
|
# (that is, the number of seconds from the time it was created),
|
|
# so we only check for it after we've refreshed the flow once.
|
|
AT_CHECK([grep dl_dst=50:54:00:00:00:07 stdout | grep -c hard_age],
|
|
[0], [1
|
|
])
|
|
fi
|
|
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
|
|
table=1, priority=0 actions=FLOOD
|
|
NXST_FLOW reply:
|
|
])
|
|
OVS_VSWITCHD_STOP
|
|
AT_CLEANUP
|
|
|
|
AT_SETUP([learning action - TCPv4 port learning])
|
|
OVS_VSWITCHD_START(
|
|
[add-port br0 p1 -- set Interface p1 type=dummy -- \
|
|
add-port br0 p2 -- set Interface p2 type=dummy -- \
|
|
add-port br0 p3 -- set Interface p3 type=dummy])
|
|
# Set up flow table for TCPv4 port learning.
|
|
AT_CHECK([[ovs-ofctl add-flow br0 'table=0 tcp actions=learn(table=1, hard_timeout=60, eth_type=0x800, nw_proto=6, NXM_OF_IP_SRC[]=NXM_OF_IP_DST[], NXM_OF_IP_DST[]=NXM_OF_IP_SRC[], NXM_OF_TCP_SRC[]=NXM_OF_TCP_DST[], NXM_OF_TCP_DST[]=NXM_OF_TCP_SRC[]), flood']])
|
|
|
|
# Trace a TCPv4 packet arriving on port 3.
|
|
flow="in_port(3),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:06),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=6,tos=0,ttl=64,frag=no),tcp(src=40000,dst=80)"
|
|
AT_CHECK([ovs-appctl ofproto/trace br0 "$flow" -generate], [0], [stdout])
|
|
actual=`tail -1 stdout | sed 's/Datapath actions: //'`
|
|
|
|
expected="1,2,100"
|
|
AT_CHECK([ovs-dpctl normalize-actions "$flow" "$expected"], [0], [stdout])
|
|
mv stdout expout
|
|
AT_CHECK([ovs-dpctl normalize-actions "$flow" "$actual"], [0], [expout])
|
|
|
|
# Check for the learning entry.
|
|
AT_CHECK([ovs-ofctl dump-flows br0 table=1 | ofctl_strip | sort], [0], [dnl
|
|
table=1, hard_timeout=60, tcp,nw_src=192.168.0.1,nw_dst=192.168.0.2,tp_src=80,tp_dst=40000 actions=drop
|
|
NXST_FLOW reply:
|
|
])
|
|
OVS_VSWITCHD_STOP
|
|
AT_CLEANUP
|
|
|
|
AT_SETUP([learning action - TCPv6 port learning])
|
|
OVS_VSWITCHD_START(
|
|
[add-port br0 p1 -- set Interface p1 type=dummy -- \
|
|
add-port br0 p2 -- set Interface p2 type=dummy -- \
|
|
add-port br0 p3 -- set Interface p3 type=dummy])
|
|
# Set up flow table for TCPv6 port learning.
|
|
# Also add a 128-bit-wide "load" action and a 128-bit literal match to check
|
|
# that they work.
|
|
AT_CHECK([[ovs-ofctl add-flow br0 'table=0 tcp6 actions=learn(table=1, hard_timeout=60, eth_type=0x86dd, nw_proto=6, NXM_NX_IPV6_SRC[]=NXM_NX_IPV6_DST[], ipv6_dst=2001:0db8:85a3:0000:0000:8a2e:0370:7334, NXM_OF_TCP_SRC[]=NXM_OF_TCP_DST[], NXM_OF_TCP_DST[]=NXM_OF_TCP_SRC[], load(0x20010db885a308d313198a2e03707348->NXM_NX_IPV6_DST[])), flood']])
|
|
|
|
# Trace a TCPv6 packet arriving on port 3.
|
|
flow="in_port(3),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:06),eth_type(0x86dd),ipv6(src=fec0::2,dst=fec0::1,label=0,proto=6,tclass=0,hlimit=255,frag=no),tcp(src=40000,dst=80)"
|
|
AT_CHECK([ovs-appctl ofproto/trace br0 "$flow" -generate], [0], [stdout])
|
|
actual=`tail -1 stdout | sed 's/Datapath actions: //'`
|
|
|
|
expected="1,2,100"
|
|
AT_CHECK([ovs-dpctl normalize-actions "$flow" "$expected"], [0], [stdout])
|
|
mv stdout expout
|
|
AT_CHECK([ovs-dpctl normalize-actions "$flow" "$actual"], [0], [expout])
|
|
|
|
# Check for the learning entry.
|
|
AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
|
|
table=1, hard_timeout=60, tcp6,ipv6_src=fec0::1,ipv6_dst=2001:db8:85a3::8a2e:370:7334,tp_src=80,tp_dst=40000 actions=load:0x13198a2e03707348->NXM_NX_IPV6_DST[[0..63]],load:0x20010db885a308d3->NXM_NX_IPV6_DST[[64..127]]
|
|
tcp6 actions=learn(table=1,hard_timeout=60,eth_type=0x86dd,nw_proto=6,NXM_NX_IPV6_SRC[[]]=NXM_NX_IPV6_DST[[]],ipv6_dst=2001:db8:85a3::8a2e:370:7334,NXM_OF_TCP_SRC[[]]=NXM_OF_TCP_DST[[]],NXM_OF_TCP_DST[[]]=NXM_OF_TCP_SRC[[]],load:0x20010db885a308d313198a2e03707348->NXM_NX_IPV6_DST[[]]),FLOOD
|
|
NXST_FLOW reply:
|
|
])
|
|
OVS_VSWITCHD_STOP
|
|
AT_CLEANUP
|
|
|
|
AT_SETUP([learning action - fin_timeout feature])
|
|
# This is a totally artificial use of the "learn" action. The only purpose
|
|
# is to check that specifying fin_idle_timeout or fin_hard_timeout causes
|
|
# a corresponding fin_timeout action to end up in the learned flows.
|
|
OVS_VSWITCHD_START(
|
|
[add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1])
|
|
AT_CHECK([[ovs-ofctl add-flow br0 'actions=learn(fin_hard_timeout=10, fin_idle_timeout=5, NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[], output:NXM_OF_IN_PORT[])']])
|
|
AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(1),eth(src=50:54:00:00:00:05,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=192.168.0.1,tip=192.168.0.2,op=1,sha=50:54:00:00:00:05,tha=00:00:00:00:00:00)' -generate], [0], [ignore])
|
|
AT_CHECK([ovs-ofctl dump-flows br0 table=1 | ofctl_strip], [0],
|
|
[NXST_FLOW reply:
|
|
table=1, dl_dst=50:54:00:00:00:05 actions=fin_timeout(idle_timeout=5,hard_timeout=10),output:1
|
|
])
|
|
OVS_VSWITCHD_STOP
|
|
AT_CLEANUP
|