Northd adds a priority-120 UNSNAT bypass when a load balancer VIP
also appears as a NAT external IP. This prevents packets for the VIP
from repeatedly entering an SNAT zone lookup that never commits the
pre-DNAT tuple. Such packets remain ct.new and cannot be offloaded.

Gateway routers using lb_force_snat_ip=router_ip may use a router
port IP as the VIP while their NAT external IP is a different
masquerade address. Northd then omits the bypass even though it
creates an UNSNAT flow for the router port IP.

Generate the bypass when the VIP also matches an explicit DNAT or
load balancer force-SNAT address, or a router port address selected
by lb_force_snat_ip=router_ip.

Add coverage using a NodePort-style VIP with a different masquerade
SNAT address. A single-stream OVN-Kubernetes DPU test improved from
8.8 Gbit/s to 19.8 Gbit/s.

Reported-at: https://github.com/ovn-kubernetes/ovn-kubernetes/issues/6422
Assisted-by: GPT-5, OpenAI Codex
Signed-off-by: Tim Rozet <[email protected]>
---
 northd/northd.c     | 33 +++++++++++++++++++++++++--------
 tests/ovn-northd.at | 40 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 65 insertions(+), 8 deletions(-)

diff --git a/northd/northd.c b/northd/northd.c
index 88e3ece88..59791d694 100644
--- a/northd/northd.c
+++ b/northd/northd.c
@@ -13761,6 +13761,23 @@ build_gw_lrouter_nat_flows_for_lb(struct 
lrouter_nat_lb_flows_ctx *ctx,
     bitmap_free(dp_non_meter);
 }
 
+static bool
+lrouter_lb_vip_is_unsnat_ip(const struct ovn_datapath *od,
+                            const struct lr_nat_record *lrnat_rec,
+                            const char *vip)
+{
+    if (sset_contains(&lrnat_rec->external_ips, vip)
+        || lport_addresses_contains_ip(
+               &lrnat_rec->dnat_force_snat_addrs, 1, vip)
+        || lport_addresses_contains_ip(
+               &lrnat_rec->lb_force_snat_addrs, 1, vip)) {
+        return true;
+    }
+
+    return lrnat_rec->lb_force_snat_router_ip
+           && sset_contains(&od->router_ips, vip);
+}
+
 static void
 build_lrouter_nat_flows_for_lb(
     struct ovn_lb_vip *lb_vip,
@@ -13910,16 +13927,16 @@ build_lrouter_nat_flows_for_lb(
             bitmap_set1(aff_dp_bitmap[type], index);
         }
 
-        if (sset_contains(&lrnat_rec->external_ips, lb_vip->vip_str)) {
-            /* The load balancer vip is also present in the NAT entries.
-             * So add a high priority lflow to advance the the packet
-             * destined to the vip (and the vip port if defined)
-             * in the S_ROUTER_IN_UNSNAT stage.
+        if (lrouter_lb_vip_is_unsnat_ip(od, lrnat_rec,
+                                        lb_vip->vip_str)) {
+            /* The load balancer VIP is also present in an UNSNAT flow.
+             * Add a high priority lflow to advance packets destined to the
+             * VIP (and the VIP port if defined) in S_ROUTER_IN_UNSNAT.
              * There seems to be an issue with ovs-vswitchd. When the new
-             * connection packet destined for the lb vip is received,
-             * it is dnat'ed in the S_ROUTER_IN_DNAT stage in the dnat
+             * connection packet destined for the LB VIP is received,
+             * it is DNATed in the S_ROUTER_IN_DNAT stage in the DNAT
              * conntrack zone. For the next packet, if it goes through
-             * unsnat stage, the conntrack flags are not set properly, and
+             * UNSNAT stage, the conntrack flags are not set properly, and
              * it doesn't hit the established state flows in
              * S_ROUTER_IN_DNAT stage. */
             ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 120,
diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
index 6d191c1a0..a60e5d012 100644
--- a/tests/ovn-northd.at
+++ b/tests/ovn-northd.at
@@ -1882,6 +1882,46 @@ OVN_CLEANUP_NORTHD
 AT_CLEANUP
 ])
 
+OVN_FOR_EACH_NORTHD_NO_HV([
+AT_SETUP([Load balancer VIP in force-SNAT addresses])
+ovn_start
+
+check ovn-nbctl ls-add public
+check ovn-nbctl lr-add lr0
+check ovn-nbctl set logical_router lr0 options:chassis=ch1
+check ovn-nbctl lrp-add lr0 lr0-public 00:00:00:00:00:01 \
+    192.0.2.1/24
+check ovn-nbctl lsp-add-router-port public public-lr0 lr0-public
+
+check ovn-nbctl lb-add lb0 192.0.2.1:30663 198.51.100.10:5201
+check ovn-nbctl lr-lb-add lr0 lb0
+check ovn-nbctl lr-nat-add lr0 snat 169.254.0.47 198.51.100.0/24
+check ovn-nbctl --wait=sb sync
+
+ovn-sbctl dump-flows lr0 > sbflows
+AT_CAPTURE_FILE([sbflows])
+AT_CHECK([grep "lr_in_unsnat.*priority=120" sbflows], [1])
+
+check ovn-nbctl --wait=sb set logical_router lr0 \
+    options:lb_force_snat_ip=192.0.2.1
+
+AT_CHECK([ovn-sbctl dump-flows lr0 | \
+    grep "lr_in_unsnat.*priority=120" | ovn_strip_lflows], [0], [dnl
+  table=??(lr_in_unsnat       ), priority=120  , match=(ip4 && ip4.dst == 
192.0.2.1 && tcp && tcp.dst == 30663), action=(next;)
+])
+
+check ovn-nbctl --wait=sb set logical_router lr0 \
+    options:lb_force_snat_ip=router_ip
+
+AT_CHECK([ovn-sbctl dump-flows lr0 | \
+    grep "lr_in_unsnat.*priority=120" | ovn_strip_lflows], [0], [dnl
+  table=??(lr_in_unsnat       ), priority=120  , match=(ip4 && ip4.dst == 
192.0.2.1 && tcp && tcp.dst == 30663), action=(next;)
+])
+
+OVN_CLEANUP_NORTHD
+AT_CLEANUP
+])
+
 OVN_FOR_EACH_NORTHD_NO_HV([
 AT_SETUP([LRP same IP as VIP or SNAT])
 ovn_start
-- 
2.55.0

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

Reply via email to