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 a port-specific VIP also matches a DNAT or load balancer force-SNAT address, or a router port address selected by lb_force_snat_ip=router_ip. Do not extend the bypass to port-less VIPs in the new force-SNAT cases. Such a flow would match replies sent to the force-SNAT address and shadow the flow that reverses SNAT. Keep the existing NAT external-IP behavior unchanged. Add coverage using port-specific and port-less VIPs with a different masquerade SNAT address. A single-stream OVN-Kubernetes DPU test with the port-specific NodePort VIP 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]> --- v2: - Require a port-specific VIP for the new force-SNAT cases. - Add coverage ensuring a port-less VIP does not bypass UNSNAT. - Preserve the existing NAT external-IP behavior. northd/northd.c | 37 +++++++++++++++++++++++++++++-------- tests/ovn-northd.at | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 8 deletions(-) diff --git a/northd/northd.c b/northd/northd.c index 88e3ece88..47f76b108 100644 --- a/northd/northd.c +++ b/northd/northd.c @@ -13761,6 +13761,28 @@ 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 struct ovn_lb_vip *lb_vip) +{ + const char *vip = lb_vip->vip_str; + + if (sset_contains(&lrnat_rec->external_ips, vip)) { + return true; + } + + /* A port-less bypass would also match replies sent to a force-SNAT + * address and prevent them from reaching the UNSNAT flow. */ + return lb_vip->port_str + && (lport_addresses_contains_ip( + &lrnat_rec->dnat_force_snat_addrs, 1, vip) + || lport_addresses_contains_ip( + &lrnat_rec->lb_force_snat_addrs, 1, vip) + || (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 +13932,15 @@ 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)) { + /* 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..d19978a62 100644 --- a/tests/ovn-northd.at +++ b/tests/ovn-northd.at @@ -1882,6 +1882,48 @@ 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 lb-add lb1 192.0.2.1 198.51.100.11 +check ovn-nbctl lr-lb-add lr0 lb0 +check ovn-nbctl lr-lb-add lr0 lb1 +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
