clone_xlate_actions() has two code paths. The non-reversible path
saves and restores ctx->conntracked across the inner actions, but
the reversible path does not.
This is a problem for flows like:
clone(ct_clear, <actions>), ct_clear, resubmit(,N)
The ct_clear inside the clone sets ctx->conntracked to false. After
the clone returns, the flag stays false but flow.ct_state is
restored by xretain_state_restore_and_free(). The ct_clear that
follows the clone then does nothing, because OFPACT_CT_CLEAR runs
only when ctx->conntracked is true. The original packet keeps its old
ct_state, and the ct_clear did not take effect.
Save and restore ctx->conntracked in the reversible path too, so it
behaves the same as the non-reversible path.
Add a kernel datapath system test to cover this scenario.
Signed-off-by: Naveen Yerramneni <[email protected]>
---
ofproto/ofproto-dpif-xlate.c | 6 +++
tests/system-traffic.at | 89 ++++++++++++++++++++++++++++++++++++
2 files changed, 95 insertions(+)
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index 0b38dcf55..d4c5f266d 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -6199,6 +6199,8 @@ clone_xlate_actions(const struct ofpact *actions, size_t
actions_len,
retained_state = xretain_state_save(ctx);
if (reversible_actions(actions, actions_len) || is_last_action) {
+ bool old_conntracked = ctx->conntracked;
+
do_xlate_actions(actions, actions_len, ctx, is_last_action, false);
if (!ctx->freezing) {
xlate_action_set(ctx);
@@ -6206,6 +6208,10 @@ clone_xlate_actions(const struct ofpact *actions, size_t
actions_len,
if (ctx->freezing) {
finish_freezing(ctx);
}
+
+ /* The clone's conntrack execution should have no effect on the
+ * original packet. */
+ ctx->conntracked = old_conntracked;
goto xlate_done;
}
diff --git a/tests/system-traffic.at b/tests/system-traffic.at
index f67e7d17a..f1db5a1de 100644
--- a/tests/system-traffic.at
+++ b/tests/system-traffic.at
@@ -3739,6 +3739,95 @@ AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep
"10\.1\.1\.1,"], [1])
OVS_TRAFFIC_VSWITCHD_STOP
AT_CLEANUP
+
+AT_SETUP([conntrack - verify ct_clear with clone])
+AT_SKIP_IF([test $HAVE_TCPDUMP = no])
+CHECK_CONNTRACK()
+OVS_TRAFFIC_VSWITCHD_START()
+
+dnl Validate that ct_clear works correctly with clone() in below cases:
+dnl
+dnl 1. ct_clear placed inside clone(ct_clear, ....) clears the
+dnl conntrack state of the cloned packet only. Subsequent OF rule
+dnl lookups on the original packet must still see the original
+dnl ct_state.
+dnl
+dnl 2. ct_clear placed outside the clone (on the original packet's
+dnl path) must still happen, even when an inner ct_clear has already
+dnl run inside the preceding clone.
+dnl
+dnl Scenario:
+dnl 3 ports on the same subnet; an inbound ICMP session (p0 -> p1) is
+dnl committed to conntrack zone 1 and mirrored to a third port (p2).
+dnl Inbound conns (both the request and the reply) are cloned to the
+dnl mirror dest, with ct_clear applied inside the clone (cloned copy)
+dnl and again outside the clone (original packet).
+dnl
+dnl Topology:
+dnl p0 (of-port 1) - 192.168.1.1 client
+dnl p1 (of-port 2) - 192.168.1.2 server (mirror source)
+dnl p2 (of-port 3) - 192.168.1.3 mirror dest
+dnl
+dnl Pipeline:
+dnl table=0 request (in_port=1) ct(zone=1, table=1) [lookup]
+dnl table=0 reply (in_port=2) ct(zone=1, table=3) [lookup]
+dnl table=1 +trk ct(commit, zone=1, table=2)
+dnl table=2 +trk -rpl, nw_dst=192.168.1.2 clone(output:3), output:2
+dnl table=3 +trk +rpl, in_port=2 clone(ct_clear, output:3),
+dnl ct_clear, resubmit(,4)
+dnl table=4 -trk, nw_dst=192.168.1.1 output:1
+dnl table=4 +trk, nw_dst=192.168.1.1 drop (safety: ct_clear missed)
+ADD_NAMESPACES(at_ns0, at_ns1, at_ns2)
+
+ADD_VETH(p0, at_ns0, br0, "192.168.1.1/24")
+ADD_VETH(p1, at_ns1, br0, "192.168.1.2/24")
+ADD_VETH(p2, at_ns2, br0, "192.168.1.3/24")
+
+AT_CHECK([ovs-vsctl -- set interface ovs-p0 ofport_request=1 \
+ -- set interface ovs-p1 ofport_request=2 \
+ -- set interface ovs-p2 ofport_request=3])
+
+AT_DATA([flows.txt], [dnl
+priority=1,action=drop
+priority=10,arp,action=normal
+priority=100,in_port=1,icmp,ct_state=-trk,actions=ct(zone=1,table=1)
+priority=100,in_port=2,icmp,ct_state=-trk,actions=ct(zone=1,table=3)
+table=1,priority=100,icmp,ct_state=+trk,actions=ct(commit,zone=1,table=2)
+table=2,priority=100,icmp,ct_state=+trk-rpl,nw_dst=192.168.1.2,actions=clone(output:3),output:2
+table=3,priority=100,in_port=2,icmp,ct_state=+trk+rpl,actions=clone(ct_clear,output:3),ct_clear,resubmit(,4)
+table=4,priority=100,icmp,nw_dst=192.168.1.1,ct_state=+trk,actions=drop
+table=4,priority=99,icmp,nw_dst=192.168.1.1,ct_state=-trk,actions=output:1
+])
+
+AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt])
+
+dnl Capture mirrored ICMP traffic on the mirror dest port (p2 inside at_ns2).
+NETNS_DAEMONIZE([at_ns2], [tcpdump -l -n -U -i p2 icmp > p2.pcap 2>p2.err],
[tcpdump.pid])
+OVS_WAIT_UNTIL([grep "listening" p2.err])
+
+dnl Send 3 ICMP echo requests from the client (p0) to the server (p1).
+NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -W 2 192.168.1.2 | FORMAT_PING],
[0], [dnl
+3 packets transmitted, 3 received, 0% packet loss, time 0ms
+])
+
+dnl A single conntrack entry should exist in zone 1 for the icmp session.
+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.1.2)], [0], [dnl
+icmp,orig=(src=192.168.1.1,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=192.168.1.1,id=<cleared>,type=0,code=0),zone=1
+])
+
+sleep 1
+AT_CHECK([kill -15 $(cat tcpdump.pid)])
+OVS_WAIT_WHILE([kill -0 $(cat tcpdump.pid)])
+
+dnl The mirror dest port should have received 6 ICMP packets in total.
+AT_CHECK([grep -c "ICMP" p2.pcap], [0], [dnl
+6
+])
+
+OVS_TRAFFIC_VSWITCHD_STOP
+AT_CLEANUP
+
+
AT_SETUP([conntrack - IPv4 ping])
CHECK_CONNTRACK()
OVS_TRAFFIC_VSWITCHD_START()
--
2.43.5
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev