Clear connection tracking state in the logical switch egress pipeline if
the packet is sent to a distributed logical router via a patch port.
This patch will fix the oversampling issue reported in
https://mail.openvswitch.org/pipermail/ovs-discuss/2025-May/053626.html

Reported-at: https://issues.redhat.com/browse/FDP-1408
Signed-off-by: Lorenzo Bianconi <[email protected]>
---
 northd/northd.c     |  16 ++++--
 tests/ovn-northd.at |   4 +-
 tests/system-ovn.at | 129 +++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 141 insertions(+), 8 deletions(-)

diff --git a/northd/northd.c b/northd/northd.c
index 8b5413ef3..2e433bfdd 100644
--- a/northd/northd.c
+++ b/northd/northd.c
@@ -5832,7 +5832,7 @@ build_lswitch_output_port_sec_od(struct ovn_datapath *od,
 
 static void
 skip_port_from_conntrack(const struct ovn_datapath *od, struct ovn_port *op,
-                         bool has_stateful_acl, enum ovn_stage in_stage,
+                         bool apply_ct, enum ovn_stage in_stage,
                          enum ovn_stage out_stage, uint16_t priority,
                          struct lflow_table *lflows,
                          struct lflow_ref *lflow_ref)
@@ -5848,9 +5848,17 @@ skip_port_from_conntrack(const struct ovn_datapath *od, 
struct ovn_port *op,
      * conntrack state across all chassis. */
 
     const char *ingress_action = "next;";
-    const char *egress_action = has_stateful_acl
-                                ? "next;"
-                                : "ct_clear; next;";
+    struct ovn_port *peer = op->peer;
+    if (peer && lsp_is_router(op->nbsp)) {
+        struct ovn_datapath *peer_od = peer->od;
+
+        if (peer_od &&
+            !peer_od->is_gw_router && vector_is_empty(&peer_od->l3dgw_ports)) {
+            /* We can clear ct for distributed routers. */
+            apply_ct = false;
+        }
+    }
+    const char *egress_action = apply_ct ? "next;" : "ct_clear; next;";
 
     char *ingress_match = xasprintf("ip && inport == %s", op->json_key);
     char *egress_match = xasprintf("ip && outport == %s", op->json_key);
diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
index c9e998129..8eb4bae1c 100644
--- a/tests/ovn-northd.at
+++ b/tests/ovn-northd.at
@@ -4819,7 +4819,7 @@ check_stateful_flows() {
   table=??(ls_out_pre_lb      ), priority=100  , match=(ip), action=(reg0[[2]] 
= 1; next;)
   table=??(ls_out_pre_lb      ), priority=110  , match=(eth.mcast), 
action=(next;)
   table=??(ls_out_pre_lb      ), priority=110  , match=(eth.src == 
\$svc_monitor_mac), action=(next;)
-  table=??(ls_out_pre_lb      ), priority=110  , match=(ip && outport == 
"sw0-lr0"), action=($action)
+  table=??(ls_out_pre_lb      ), priority=110  , match=(ip && outport == 
"sw0-lr0"), action=(ct_clear; next;)
   table=??(ls_out_pre_lb      ), priority=110  , match=(nd || nd_rs || nd_ra 
|| mldv1 || mldv2), action=(next;)
   table=??(ls_out_pre_lb      ), priority=110  , match=(reg0[[16]] == 1), 
action=(next;)
 ])
@@ -4886,7 +4886,7 @@ AT_CHECK([grep "ls_out_pre_lb" sw0flows | 
ovn_strip_lflows], [0], [dnl
   table=??(ls_out_pre_lb      ), priority=0    , match=(1), action=(next;)
   table=??(ls_out_pre_lb      ), priority=110  , match=(eth.mcast), 
action=(next;)
   table=??(ls_out_pre_lb      ), priority=110  , match=(eth.src == 
$svc_monitor_mac), action=(next;)
-  table=??(ls_out_pre_lb      ), priority=110  , match=(ip && outport == 
"sw0-lr0"), action=(next;)
+  table=??(ls_out_pre_lb      ), priority=110  , match=(ip && outport == 
"sw0-lr0"), action=(ct_clear; next;)
   table=??(ls_out_pre_lb      ), priority=110  , match=(nd || nd_rs || nd_ra 
|| mldv1 || mldv2), action=(next;)
   table=??(ls_out_pre_lb      ), priority=110  , match=(reg0[[16]] == 1), 
action=(next;)
 ])
diff --git a/tests/system-ovn.at b/tests/system-ovn.at
index 8e356df6f..3e4ff32d1 100644
--- a/tests/system-ovn.at
+++ b/tests/system-ovn.at
@@ -8542,8 +8542,8 @@ check ovn-nbctl lsp-set-addresses sw1p1 
"50:54:00:00:00:03 20.0.0.3"
 
 check ovn-nbctl --apply-after-lb acl-add sw1 from-lport 1001 'inport == 
"sw1p1" && ip4' drop
 
-check ovn-nbctl acl-add sw1 to-lport 1002 'ip4 && tcp && tcp.dst == 80' 
allow-related
-check ovn-nbctl acl-add sw1 to-lport 1001 'ip4' drop
+check ovn-nbctl acl-add sw1 to-lport 1002 'outport == "sw1p1" && ip4 && tcp && 
tcp.dst == 80' allow-related
+check ovn-nbctl acl-add sw1 to-lport 1001 'outport == "sw1p1" && ip4' drop
 
 ADD_NAMESPACES(sw0p1)
 ADD_VETH(sw0p1, sw0p1, br-int, "10.0.0.3/24", "50:54:00:00:00:02", \
@@ -13969,6 +13969,131 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port 
patch-.*/d
 AT_CLEANUP
 ])
 
+OVN_FOR_EACH_NORTHD([
+AT_SETUP([ovn -- ACL Sampling - Stateful ACL - routed traffic])
+AT_KEYWORDS([ACL])
+
+CHECK_CONNTRACK()
+CHECK_CONNTRACK_NAT()
+ovn_start
+OVS_TRAFFIC_VSWITCHD_START()
+ADD_BR([br-int])
+
+dnl Set external-ids in br-int needed for ovn-controller
+check ovs-vsctl \
+        -- set Open_vSwitch . external-ids:system-id=hv1 \
+        -- set Open_vSwitch . 
external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
+        -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
+        -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \
+        -- set bridge br-int fail-mode=secure other-config:disable-in-band=true
+
+dnl Start ovn-controller
+start_daemon ovn-controller
+
+dnl Logical network:
+dnl 1 router
+dnl 2 logical switch
+dnl 2 VIF
+
+check ovn-nbctl                                      \
+  -- lr-add lr                                       \
+  -- lrp-add lr lrp1 00:00:00:00:01:00 42.42.42.1/24 \
+  -- lrp-add lr lrp2 00:00:00:00:02:00 43.43.43.1/24 \
+  -- ls-add ls1                                      \
+  -- ls-add ls2                                      \
+  -- lsp-add ls1 vm1                                 \
+  -- lsp-add ls2 vm2                                 \
+  -- lsp-set-addresses vm1 00:00:00:00:00:01         \
+  -- lsp-set-addresses vm2 00:00:00:00:00:02         \
+  -- lsp-add ls1 ls1-lr                              \
+  -- lsp-add ls2 ls2-lr                              \
+  -- lsp-set-type ls1-lr router                      \
+  -- lsp-set-type ls2-lr router                      \
+  -- lsp-set-options ls1-lr router-port=lrp1         \
+  -- lsp-set-options ls2-lr router-port=lrp2         \
+  -- lsp-set-addresses ls1-lr router                 \
+  -- lsp-set-addresses ls2-lr router
+check ovn-nbctl --wait=hv sync
+
+ADD_NAMESPACES(vm1)
+ADD_VETH(vm1, vm1, br-int, "42.42.42.2/24", "00:00:00:00:00:01", "42.42.42.1")
+ADD_NAMESPACES(vm2)
+ADD_VETH(vm2, vm2, br-int, "43.43.43.2/24", "00:00:00:00:00:02", "43.43.43.1")
+
+wait_for_ports_up
+
+NS_CHECK_EXEC([vm1], [ping -q -c 3 -i 0.3 -w 2 43.43.43.2 | FORMAT_PING], \
+[0], [dnl
+3 packets transmitted, 3 received, 0% packet loss, time 0ms
+])
+
+collector1=$(ovn-nbctl create Sample_Collector id=1 name=c1 probability=65535 
set_id=100)
+collector2=$(ovn-nbctl create Sample_Collector id=2 name=c2 probability=65535 
set_id=200)
+check_row_count nb:Sample_Collector 2
+
+check_uuid ovn-nbctl create Sampling_App type="acl-est" id="43"
+check_row_count nb:Sampling_App 1
+
+check ovn-nbctl pg-add pg vm1 vm2
+check_uuid ovn-nbctl                                                           
              \
+    -- --id=@sample1 create Sample collector="$collector1" metadata=1001       
              \
+    -- --sample-est=@sample1 acl-add pg from-lport 200 "inport == @pg && udp" 
allow-related  \
+    -- --id=@sample2 create Sample collector="$collector2" metadata=1004       
              \
+    -- --sample-est=@sample2 acl-add pg to-lport 200 "outport == @pg && udp" 
allow-related
+check ovn-nbctl --wait=hv sync
+check_row_count nb:ACL 2
+check_row_count nb:Sample 2
+
+NETNS_DAEMONIZE([vm1], [nc -l -u 1234], [udp1.pid])
+NETNS_DAEMONIZE([vm2], [nc -l -u 1235], [udp2.pid])
+
+dnl Start an IPFIX collector.
+DAEMONIZE([nfcapd -B 1024000 -w . -p 4242 2> collector.err], [collector.pid])
+dnl Wait for the collector to be up.
+OVS_WAIT_UNTIL([grep -q 'Startup nfcapd.' collector.err])
+
+dnl Configure the OVS flow sample collector.
+ovs-vsctl --id=@br get Bridge br-int \
+    -- --id=@ipfix1 create IPFIX targets=\"127.0.0.1:4242\" 
template_interval=1   \
+    -- --id=@cs1 create Flow_Sample_Collector_Set id=100 bridge=@br 
ipfix=@ipfix1 \
+    -- --id=@ipfix2 create IPFIX targets=\"127.0.0.1:4242\" 
template_interval=1   \
+    -- --id=@cs2 create Flow_Sample_Collector_Set id=200 bridge=@br 
ipfix=@ipfix2
+
+check ovn-nbctl --wait=hv sync
+dnl And wait for it to be up and running.
+OVS_WAIT_UNTIL([ovs-ofctl dump-ipfix-flow br-int | grep -q '2 ids'])
+
+# Create the connection in the CT table.
+echo "Hello" > Hello
+NS_EXEC([vm1], [nc -p 1234 -u 43.43.43.2 1235 < Hello])
+NS_EXEC([vm2], [nc -p 1235 -u 42.42.42.2 1234 < Hello])
+# Send some more packets.
+for i in $(seq 10); do
+NS_EXEC([vm1], [nc -p 1234 -u 43.43.43.2 1235 < Hello])
+NS_EXEC([vm2], [nc -p 1235 -u 42.42.42.2 1234 < Hello])
+done
+
+OVS_WAIT_UNTIL([ovs-ofctl dump-ipfix-flow br-int | grep 'id 100' | grep -q 
'sampled pkts=21'])
+OVS_WAIT_UNTIL([ovs-ofctl dump-ipfix-flow br-int | grep 'id 200' | grep -q 
'sampled pkts=21'])
+
+OVN_CLEANUP_CONTROLLER([hv1])
+
+as ovn-sb
+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+
+as ovn-nb
+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+
+as northd
+OVS_APP_EXIT_AND_WAIT([ovn-northd])
+
+as
+OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
+/connection dropped.*/d"])
+
+AT_CLEANUP
+])
+
 OVN_FOR_EACH_NORTHD([
 AT_SETUP([SB Disconnect - MAC_Binding])
 ovn_start
-- 
2.51.0

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to