On Mon, Aug 24, 2026 at 3:25 PM Tim Rozet <[email protected]> wrote: > > 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,
Thanks Tim for the fix. For a port-less VIP, `unsnat_match` ends up holding just the protocol, so on a gateway router with `lb_force_snat_ip=router_ip` we get: priority=120, match=(ip4 && ip4.dst == 192.0.2.1 && tcp), action=(next;) priority=110, match=(inport == "lr0-public" && ip4.dst == 192.0.2.1), action=(ct_snat;) The 120 flow now covers all TCP to that address and hides the 110 flow, which is the only thing that reverses the force-SNAT for replies. Backend replies never get unSNATed. Could we require `lb_vip->port_str` for the force-SNAT cases? The `external_ips` case is probably better left to a separate patch. Best, Han > 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
