From d86b662b1493bf2abdb4f80d9f7f6e601e0007e3 Mon Sep 17 00:00:00 2001 From: Aaron Bernardino Date: Tue, 14 Jul 2026 16:10:13 +0000 Subject: [PATCH 1/5] [vpp] VXLAN dataplane enablement: inner-aware encap hash, underlay ECMP, source-independent VNET decap Add three VPP patch-series entries required to enable VXLAN dataplane features on sonic-vpp, and bump VPP_VERSION so the buildkite deb cache misses and the patches are recompiled into the image: - 0012 VXLAN encap inner-aware flow hash: hash the inner IP 5-tuple on L3 VXLAN encap so inner flows spread across underlay ECMP / LAG paths instead of collapsing onto a single next-hop / bond member. - 0013 VXLAN underlay ECMP load-balance tolerance: tighten the multipath load-balance error tolerance so all equal-cost underlay paths are retained (buckets:256), fixing near-ideal 3-way distribution. - 0014 VXLAN VNET source-independent ("decap-any") decap: match on local dst + VNI ignoring the outer source, supporting RIOT / secondary-VTEP VNET decap, plus an l2_bvi helper. rules/vpp.mk: VPP_VERSION 2606-0.3 -> 2606-0.5 (patch-series changed). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3446b9ac-fa7f-4d26-914b-a4f076c603aa Signed-off-by: Aaron Bernardino --- rules/vpp.mk | 2 +- ...12-vxlan-encap-inner-aware-flow-hash.patch | 89 +++++++ ...-underlay-ecmp-loadbalance-tolerance.patch | 21 ++ ...-vxlan-vnet-source-independent-decap.patch | 248 ++++++++++++++++++ vppbld/patches/series | 9 + 5 files changed, 368 insertions(+), 1 deletion(-) create mode 100644 vppbld/patches/0012-vxlan-encap-inner-aware-flow-hash.patch create mode 100644 vppbld/patches/0013-sonic-vxlan-underlay-ecmp-loadbalance-tolerance.patch create mode 100644 vppbld/patches/0014-sonic-vxlan-vnet-source-independent-decap.patch diff --git a/rules/vpp.mk b/rules/vpp.mk index 56ee659f..1d59ed0c 100644 --- a/rules/vpp.mk +++ b/rules/vpp.mk @@ -7,7 +7,7 @@ VPP_VERSION_BASE = 2606 # https://packages.buildkite.com/sonic-vpp/vpp; if the suffix isn't bumped, # downstream sonic-buildimage builds will silently pull stale debs that # pre-date the new patch series and end up with VPP/SAI CRC drift. -VPP_VERSION = $(VPP_VERSION_BASE)-0.3 +VPP_VERSION = $(VPP_VERSION_BASE)-0.5 VPP_VERSION_SONIC = $(VPP_VERSION)+b1sonic1 VPP_SRC_PATH = platform/vpp/vppbld diff --git a/vppbld/patches/0012-vxlan-encap-inner-aware-flow-hash.patch b/vppbld/patches/0012-vxlan-encap-inner-aware-flow-hash.patch new file mode 100644 index 00000000..d6e2bf22 --- /dev/null +++ b/vppbld/patches/0012-vxlan-encap-inner-aware-flow-hash.patch @@ -0,0 +1,89 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Aaron Bernardino +Date: Fri, 10 Jul 2026 00:00:00 +0000 +Subject: [PATCH] vxlan: inner-aware flow hash in the encap node + +The VXLAN encap node derives the outer UDP source port (and the buffer +flow hash used for underlay ECMP) from vnet_l2_compute_flow_hash(). That +helper reads the inner ethertype at current_data + l2.l2_len. For an +L3-routed VXLAN tunnel the inner packet arrives already routed (the +ip4/ip6-rewrite adjacency prepends the inner Ethernet header) and +l2.l2_len is 0/unset, so the helper misparses the frame and returns a +value that does not depend on the inner L3/L4 headers. Every inner flow +to a given tunnel endpoint then maps to the same outer UDP source port +and collapses onto a single underlay ECMP path / bond member. + +This is the exact case that patch 0011 (inner-aware flow hash) left out of +scope on the assumption that "VXLAN ... their outer UDP source port is the +standard-mandated entropy carrier (RFC 7348 section 4.2), so the existing +outer-5-tuple hash already distributes inner flows." That assumption only +holds if the encap node actually populates the outer UDP source port from +the inner flow -- which it does not for L3-routed tunnels. This patch +fixes the entropy carrier at its source rather than adding the peek-inner +machinery of 0011 to the underlay. + +Read the inner Ethernet ethertype directly at the buffer current pointer +(the inner Ethernet header is present for both L2 and L3 tunnels at the +encap call sites) and hash the inner IPv4/IPv6 5-tuple. Fall back to the +generic vnet_l2_compute_flow_hash() (which honours l2_len and VLAN tags) +for anything that is not plain inner IPv4/IPv6. + +Signed-off-by: Aaron Bernardino +--- +diff --git a/src/plugins/vxlan/encap.c b/src/plugins/vxlan/encap.c +index 9d304b76d..71024b3b4 100644 +--- a/src/plugins/vxlan/encap.c ++++ b/src/plugins/vxlan/encap.c +@@ -57,6 +57,30 @@ format_vxlan_encap_trace (u8 * s, va_list * args) + } + #endif + ++static_always_inline u32 ++vxlan_encap_inner_flow_hash (vlib_buffer_t * b) ++{ ++ /* For L3 VXLAN tunnels the inner packet is routed (not bridged) into this ++ * encap node, so vnet_buffer(b)->l2.l2_len is not set. The generic ++ * vnet_l2_compute_flow_hash() then misparses the frame and returns a value ++ * that does not depend on the inner L3/L4 headers, collapsing every inner ++ * flow for a given tunnel endpoint onto a single outer UDP source port and ++ * a single underlay ECMP path. The inner Ethernet header is present at the ++ * current data pointer, so read its ethertype directly and hash the inner ++ * IP 5-tuple. Fall back to the generic L2 hash (which honours l2_len and ++ * VLAN tags) for anything that is not plain inner IPv4/IPv6. */ ++ ethernet_header_t *eh = vlib_buffer_get_current (b); ++ u16 ethertype = clib_net_to_host_u16 (eh->type); ++ u8 *l3h = (u8 *) (eh + 1); ++ ++ if (ethertype == ETHERNET_TYPE_IP4) ++ return ip4_compute_flow_hash ((ip4_header_t *) l3h, IP_FLOW_HASH_DEFAULT); ++ else if (ethertype == ETHERNET_TYPE_IP6) ++ return ip6_compute_flow_hash ((ip6_header_t *) l3h, IP_FLOW_HASH_DEFAULT); ++ ++ return vnet_l2_compute_flow_hash (b); ++} ++ + always_inline uword + vxlan_encap_inline (vlib_main_t *vm, vlib_node_runtime_t *node, + vlib_frame_t *from_frame, u8 is_ip4) +@@ -125,8 +149,8 @@ vxlan_encap_inline (vlib_main_t *vm, vlib_node_runtime_t *node, + vlib_buffer_t *b1 = b[1]; + b += 2; + +- u32 flow_hash0 = vnet_l2_compute_flow_hash (b0); +- u32 flow_hash1 = vnet_l2_compute_flow_hash (b1); ++ u32 flow_hash0 = vxlan_encap_inner_flow_hash (b0); ++ u32 flow_hash1 = vxlan_encap_inner_flow_hash (b1); + + /* Get next node index and adj index from tunnel next_dpo */ + if (sw_if_index0 != vnet_buffer (b0)->sw_if_index[VLIB_TX]) +@@ -351,7 +375,7 @@ vxlan_encap_inline (vlib_main_t *vm, vlib_node_runtime_t *node, + vlib_buffer_t *b0 = b[0]; + b += 1; + +- u32 flow_hash0 = vnet_l2_compute_flow_hash (b0); ++ u32 flow_hash0 = vxlan_encap_inner_flow_hash (b0); + + /* Get next node index and adj index from tunnel next_dpo */ + if (sw_if_index0 != vnet_buffer (b0)->sw_if_index[VLIB_TX]) +-- +2.34.1 diff --git a/vppbld/patches/0013-sonic-vxlan-underlay-ecmp-loadbalance-tolerance.patch b/vppbld/patches/0013-sonic-vxlan-underlay-ecmp-loadbalance-tolerance.patch new file mode 100644 index 00000000..cd9b964f --- /dev/null +++ b/vppbld/patches/0013-sonic-vxlan-underlay-ecmp-loadbalance-tolerance.patch @@ -0,0 +1,21 @@ +Subject: [PATCH] load-balance: tighten multipath error tolerance for SONiC + +SONiC VXLAN underlay ECMP tests expect near-uniform traffic distribution +across equal-cost underlay paths. The default multipath_next_hop_error_ +tolerance of 0.1 (10%) allows enough skew that a small number of paths can +be dropped from the load-balance bucket set, quantizing an N-way ECMP to +fewer buckets. Tighten it to 0.01 so all equal-cost paths are retained. +--- +diff --git a/src/vnet/dpo/load_balance.c b/src/vnet/dpo/load_balance.c +index 8bec28f4a..245033ece 100644 +--- a/src/vnet/dpo/load_balance.c ++++ b/src/vnet/dpo/load_balance.c +@@ -20,7 +20,7 @@ + /* + * distribution error tolerance for load-balancing + */ +-const f64 multipath_next_hop_error_tolerance = 0.1; ++const f64 multipath_next_hop_error_tolerance = 0.01; + + static const char *load_balance_attr_names[] = LOAD_BALANCE_ATTR_NAMES; + diff --git a/vppbld/patches/0014-sonic-vxlan-vnet-source-independent-decap.patch b/vppbld/patches/0014-sonic-vxlan-vnet-source-independent-decap.patch new file mode 100644 index 00000000..4180ce7a --- /dev/null +++ b/vppbld/patches/0014-sonic-vxlan-vnet-source-independent-decap.patch @@ -0,0 +1,248 @@ +Subject: [PATCH] vxlan: source-independent (decap-any) VNET decap + +SONiC VNET decap must accept VXLAN packets from any outer source IP for a +given local VTEP IP + VNI (RIOT / secondary-VTEP decap), whereas VPP keys +decap on the exact (src,dst,vni) tuple. Register an additional source- +independent bihash entry keyed on local dst ip + vni (outer src wildcarded) +on tunnel add, maintain it on delete, and consult it in vxlan4/6_find_tunnel +only after the exact lookup misses so existing decap behaviour is unchanged. +Includes the L3-promiscuous VNET-decap BVI helper (l2_bvi) used to route the +decapped inner packet into the tenant VRF. +--- +diff --git a/src/plugins/vxlan/decap.c b/src/plugins/vxlan/decap.c +index 89422fda5..301ec5966 100644 +--- a/src/plugins/vxlan/decap.c ++++ b/src/plugins/vxlan/decap.c +@@ -86,6 +86,20 @@ vxlan4_find_tunnel (vxlan_main_t * vxm, last_tunnel_cache4 * cache, + return di; + } + ++ /* try source-independent decap (SONiC VNET decap-any): match on local ++ * dst ip + vni, ignoring the outer source. Consulted only after the exact ++ * (src,dst,vni) lookup misses, so it never changes existing decap. */ ++ { ++ vxlan4_tunnel_key_t key4w = key4; ++ key4w.key[0] = ((u64) dst << 32); ++ if (clib_bihash_search_inline_16_8 (&vxm->vxlan4_tunnel_by_key, &key4w) == 0) ++ { ++ vxlan_decap_info_t diw = {.as_u64 = key4w.value }; ++ *stats_sw_if_index = diw.sw_if_index; ++ return diw; ++ } ++ } ++ + /* try multicast */ + if (PREDICT_TRUE (!ip4_address_is_multicast (&ip4_0->dst_address))) + return decap_not_found; +@@ -135,7 +149,24 @@ vxlan6_find_tunnel (vxlan_main_t * vxm, last_tunnel_cache6 * cache, + int rv = + clib_bihash_search_inline_24_8 (&vxm->vxlan6_tunnel_by_key, &key6); + if (PREDICT_FALSE (rv != 0)) +- return decap_not_found; ++ { ++ /* source-independent decap (SONiC VNET decap-any): match on local ++ * dst ip6 + vni, ignoring the outer source. Consulted only after the ++ * exact (src,dst,vni) lookup misses, so existing decap is unchanged. */ ++ vxlan6_tunnel_key_t key6w = key6; ++ key6w.key[0] = ip6_0->dst_address.as_u64[0]; ++ key6w.key[1] = ip6_0->dst_address.as_u64[1]; ++ if (clib_bihash_search_inline_24_8 (&vxm->vxlan6_tunnel_by_key, ++ &key6w) != 0) ++ return decap_not_found; ++ vxlan_tunnel_t *tw = pool_elt_at_index (vxm->tunnels, key6w.value); ++ *stats_sw_if_index = tw->sw_if_index; ++ vxlan_decap_info_t diw = { ++ .sw_if_index = tw->sw_if_index, ++ .next_index = tw->decap_next_index, ++ }; ++ return diw; ++ } + + *cache = key6; + } +diff --git a/src/plugins/vxlan/vxlan.c b/src/plugins/vxlan/vxlan.c +index 46c014912..b5e62065d 100644 +--- a/src/plugins/vxlan/vxlan.c ++++ b/src/plugins/vxlan/vxlan.c +@@ -480,6 +480,18 @@ int vnet_vxlan_add_del_tunnel + key6.value = (u64) dev_instance; + add_failed = clib_bihash_add_del_24_8 (&vxm->vxlan6_tunnel_by_key, + &key6, 1 /*add */ ); ++ /* SONiC VNET decap-any: also register a source-independent decap ++ * entry keyed on local dst ip6 + vni (outer src wildcarded). */ ++ if (!ip46_address_is_multicast (&t->dst)) ++ { ++ vxlan6_tunnel_key_t key6w; ++ key6w.key[0] = t->src.ip6.as_u64[0]; ++ key6w.key[1] = t->src.ip6.as_u64[1]; ++ key6w.key[2] = key6.key[2]; ++ key6w.value = (u64) dev_instance; ++ clib_bihash_add_del_24_8 (&vxm->vxlan6_tunnel_by_key, &key6w, ++ 1 /*add */ ); ++ } + } + else + { +@@ -491,6 +503,17 @@ int vnet_vxlan_add_del_tunnel + key4.value = di.as_u64; + add_failed = clib_bihash_add_del_16_8 (&vxm->vxlan4_tunnel_by_key, + &key4, 1 /*add */ ); ++ /* SONiC VNET decap-any: also register a source-independent decap ++ * entry keyed on local dst ip + vni (outer src wildcarded to 0). */ ++ if (!ip46_address_is_multicast (&t->dst)) ++ { ++ vxlan4_tunnel_key_t key4w; ++ key4w.key[0] = ((u64) t->src.ip4.as_u32) << 32; ++ key4w.key[1] = key4.key[1]; ++ key4w.value = di.as_u64; ++ clib_bihash_add_del_16_8 (&vxm->vxlan4_tunnel_by_key, &key4w, ++ 1 /*add */ ); ++ } + } + + if (add_failed) +@@ -633,6 +656,76 @@ int vnet_vxlan_add_del_tunnel + clib_bihash_add_del_24_8 (&vxm->vxlan6_tunnel_by_key, &key6, + 0 /*del */ ); + ++ /* SONiC VNET decap-any: keep or drop the source-independent entry. ++ * If another tunnel shares the same local ip + vni it must stay, so ++ * re-point the wildcard entry at that survivor; otherwise remove it. */ ++ if (!is_ip6 && !ip46_address_is_multicast (&t->dst)) ++ { ++ vxlan4_tunnel_key_t key4w; ++ key4w.key[0] = ((u64) t->src.ip4.as_u32) << 32; ++ key4w.key[1] = key4.key[1]; ++ vxlan_tunnel_t *st, *survivor = 0; ++ pool_foreach (st, vxm->tunnels) ++ { ++ if (st == t || ip46_address_is_multicast (&st->dst)) ++ continue; ++ if (st->src.ip4.as_u32 == t->src.ip4.as_u32 && ++ st->vni == t->vni && ++ st->encap_fib_index == t->encap_fib_index && ++ st->src_port == t->src_port) ++ { ++ survivor = st; ++ break; ++ } ++ } ++ if (survivor) ++ { ++ vxlan_decap_info_t diw = { ++ .sw_if_index = survivor->sw_if_index, ++ .next_index = survivor->decap_next_index ++ }; ++ key4w.value = diw.as_u64; ++ clib_bihash_add_del_16_8 (&vxm->vxlan4_tunnel_by_key, &key4w, ++ 1 /*add */ ); ++ } ++ else ++ clib_bihash_add_del_16_8 (&vxm->vxlan4_tunnel_by_key, &key4w, ++ 0 /*del */ ); ++ } ++ ++ /* SONiC VNET decap-any (ip6): keep or drop the source-independent ++ * entry; re-point at a surviving same-local-ip+vni tunnel if any. */ ++ if (is_ip6 && !ip46_address_is_multicast (&t->dst)) ++ { ++ vxlan6_tunnel_key_t key6w; ++ key6w.key[0] = t->src.ip6.as_u64[0]; ++ key6w.key[1] = t->src.ip6.as_u64[1]; ++ key6w.key[2] = key6.key[2]; ++ vxlan_tunnel_t *st, *survivor = 0; ++ pool_foreach (st, vxm->tunnels) ++ { ++ if (st == t || ip46_address_is_multicast (&st->dst)) ++ continue; ++ if (ip6_address_is_equal (&st->src.ip6, &t->src.ip6) && ++ st->vni == t->vni && ++ st->encap_fib_index == t->encap_fib_index && ++ st->src_port == t->src_port) ++ { ++ survivor = st; ++ break; ++ } ++ } ++ if (survivor) ++ { ++ key6w.value = (u64) (survivor - vxm->tunnels); ++ clib_bihash_add_del_24_8 (&vxm->vxlan6_tunnel_by_key, &key6w, ++ 1 /*add */ ); ++ } ++ else ++ clib_bihash_add_del_24_8 (&vxm->vxlan6_tunnel_by_key, &key6w, ++ 0 /*del */ ); ++ } ++ + if (!ip46_address_is_multicast (&t->dst)) + { + if (t->flow_index != ~0) +diff --git a/src/vnet/l2/l2_bvi.c b/src/vnet/l2/l2_bvi.c +index a3c665404..b5e9b88bf 100644 +--- a/src/vnet/l2/l2_bvi.c ++++ b/src/vnet/l2/l2_bvi.c +@@ -9,6 +9,36 @@ + #include + #include + #include ++#include ++ ++/* SONiC VNET decap-and-route (RIOT): a frame that ingressed on a VXLAN tunnel ++ * and is being flooded to a BVI is a decap-and-route packet. On real hardware ++ * VXLAN decap-and-route is MAC-agnostic, but VPP's BVI applies a strict my-mac ++ * L2 filter, so decapped frames whose inner dst MAC != the router/BVI MAC are ++ * dropped instead of being L3-routed in the VNET VRF. When the ingress ++ * interface is a VXLAN tunnel, treat the BVI as L3-promiscuous and route ++ * regardless of dst MAC. Normal L2 ports keep strict my-mac semantics. */ ++int ++l2_bvi_l3_promiscuous (u32 rx_sw_if_index) ++{ ++ vnet_main_t *vnm = vnet_get_main (); ++ vnet_sw_interface_t *si; ++ vnet_hw_interface_t *hi; ++ vnet_device_class_t *dc; ++ ++ if (rx_sw_if_index == (u32) ~0) ++ return 0; ++ si = vnet_get_sw_interface_or_null (vnm, rx_sw_if_index); ++ if (!si) ++ return 0; ++ hi = vnet_get_sup_hw_interface (vnm, rx_sw_if_index); ++ if (!hi) ++ return 0; ++ dc = vnet_get_device_class (vnm, hi->dev_class_index); ++ if (dc && dc->name && !strncmp ((char *) dc->name, "VXLAN", 6)) ++ return 1; ++ return 0; ++} + + /* Allocated BVI instances */ + static uword *l2_bvi_instances; +diff --git a/src/vnet/l2/l2_bvi.h b/src/vnet/l2/l2_bvi.h +index 1087190e6..2028493e8 100644 +--- a/src/vnet/l2/l2_bvi.h ++++ b/src/vnet/l2/l2_bvi.h +@@ -17,6 +17,8 @@ + #define TO_BVI_ERR_BAD_MAC 1 + #define TO_BVI_ERR_ETHERTYPE 2 + ++extern int l2_bvi_l3_promiscuous (u32 sw_if_index); ++ + static_always_inline u32 + l2_to_bvi_dmac_check (vnet_hw_interface_t * hi, u8 * dmac, + ethernet_interface_t * ei, u8 have_sec_dmac) +@@ -69,7 +71,12 @@ l2_to_bvi (vlib_main_t * vlib_main, + 0 /* have_sec_dmac */ ); + + if (rv != TO_BVI_ERR_OK) +- return rv; ++ { ++ /* VXLAN decap-and-route (RIOT): if this frame ingressed on a VXLAN ++ tunnel, route via the BVI regardless of inner dst MAC. */ ++ if (!l2_bvi_l3_promiscuous (vnet_buffer (b0)->sw_if_index[VLIB_RX])) ++ return rv; ++ } + } + + /* Save L2 header position which may be changed due to packet replication */ diff --git a/vppbld/patches/series b/vppbld/patches/series index 4c3a30ea..784c2695 100644 --- a/vppbld/patches/series +++ b/vppbld/patches/series @@ -23,3 +23,12 @@ # scope. Existing hash-eth-l34 and IP_FLOW_HASH_DEFAULT are byte-for-byte # unchanged. 0011-sonic-inner-aware-flow-hash.patch +# 12. VXLAN encap inner-aware flow hash: hash inner IP 5-tuple on L3 VXLAN +# encap so inner flows spread across underlay ECMP / LAG paths +0012-vxlan-encap-inner-aware-flow-hash.patch +# 13. VXLAN underlay ECMP: tighten multipath load-balance error tolerance so +# all equal-cost underlay paths are retained +0013-sonic-vxlan-underlay-ecmp-loadbalance-tolerance.patch +# 14. VXLAN VNET source-independent ("decap-any") decap: match local dst+vni +# ignoring outer src (RIOT / secondary-VTEP VNET decap) + l2_bvi helper +0014-sonic-vxlan-vnet-source-independent-decap.patch From 49b076e9f6673db5c1a246a631eccc0070e742b5 Mon Sep 17 00:00:00 2001 From: Aaron Bernardino Date: Tue, 14 Jul 2026 22:58:57 +0000 Subject: [PATCH 2/5] vxlan: harden source-independent (decap-any) VNET decap Address code-review findings on the VXLAN VNET source-independent ("decap-any") decap patch series: - Scope the source-independent wildcard bihash entry and the L3-promiscuous BVI receive to secondary-VTEP decap terms only, via a decap-any flag SAI sets in the high bit (VXLAN_DECAP_ANY_FLAG) of the wire decap_next_index. Ordinary VXLAN tunnels keep exact outer-source matching and strict BVI my-mac. - Check all wildcard clib_bihash_add_del return codes. An add failure rolls back the exact entry and fails the tunnel; a delete-time re-point failure drops the stale entry instead of leaving it pointing at a freed pool slot. - Replace the per-packet device-class string match in l2_to_bvi with an O(1) sw_if_index bitmap owned by l2_bvi and populated by the vxlan plugin, scoping L3-promiscuous receive to decap-any ingress interfaces. - Patch headers: clarify 0013 as a global FIB-ECMP tolerance change (LAG unaffected; inner-aware spreading is 0012), and document the L2-EVPN and mixed-family limitations in the 0014 header. Bump VPP_VERSION 0.5 -> 0.6 so the prebuilt-deb cache misses and the updated patches reach the built VPP. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3446b9ac-fa7f-4d26-914b-a4f076c603aa Signed-off-by: Aaron Bernardino --- rules/vpp.mk | 2 +- ...-underlay-ecmp-loadbalance-tolerance.patch | 9 + ...-vxlan-vnet-source-independent-decap.patch | 248 ++++++++++++++---- 3 files changed, 204 insertions(+), 55 deletions(-) diff --git a/rules/vpp.mk b/rules/vpp.mk index 1d59ed0c..739c82c6 100644 --- a/rules/vpp.mk +++ b/rules/vpp.mk @@ -7,7 +7,7 @@ VPP_VERSION_BASE = 2606 # https://packages.buildkite.com/sonic-vpp/vpp; if the suffix isn't bumped, # downstream sonic-buildimage builds will silently pull stale debs that # pre-date the new patch series and end up with VPP/SAI CRC drift. -VPP_VERSION = $(VPP_VERSION_BASE)-0.5 +VPP_VERSION = $(VPP_VERSION_BASE)-0.6 VPP_VERSION_SONIC = $(VPP_VERSION)+b1sonic1 VPP_SRC_PATH = platform/vpp/vppbld diff --git a/vppbld/patches/0013-sonic-vxlan-underlay-ecmp-loadbalance-tolerance.patch b/vppbld/patches/0013-sonic-vxlan-underlay-ecmp-loadbalance-tolerance.patch index cd9b964f..a4504b76 100644 --- a/vppbld/patches/0013-sonic-vxlan-underlay-ecmp-loadbalance-tolerance.patch +++ b/vppbld/patches/0013-sonic-vxlan-underlay-ecmp-loadbalance-tolerance.patch @@ -5,6 +5,15 @@ across equal-cost underlay paths. The default multipath_next_hop_error_ tolerance of 0.1 (10%) allows enough skew that a small number of paths can be dropped from the load-balance bucket set, quantizing an N-way ECMP to fewer buckets. Tighten it to 0.01 so all equal-cost paths are retained. + +Scope note: multipath_next_hop_error_tolerance is a GLOBAL FIB load-balance +constant (ip_multipath_normalize_next_hops), not VXLAN-specific. Tightening it +improves fairness for every ECMP group but also allocates more load-balance +buckets for non-power-of-2 groups (e.g. a 3-way group grows from 16 to 256 +buckets), increasing per-load-balance memory and rebuild cost at large +multipath scale. This does NOT affect LAG member hashing: bonding uses +l2_flow_hash, not the multipath bucket allocator. (Inner-aware LAG/ECMP +spreading for tunnelled traffic is provided separately by patch 0012.) --- diff --git a/src/vnet/dpo/load_balance.c b/src/vnet/dpo/load_balance.c index 8bec28f4a..245033ece 100644 diff --git a/vppbld/patches/0014-sonic-vxlan-vnet-source-independent-decap.patch b/vppbld/patches/0014-sonic-vxlan-vnet-source-independent-decap.patch index 4180ce7a..49806f34 100644 --- a/vppbld/patches/0014-sonic-vxlan-vnet-source-independent-decap.patch +++ b/vppbld/patches/0014-sonic-vxlan-vnet-source-independent-decap.patch @@ -6,11 +6,26 @@ decap on the exact (src,dst,vni) tuple. Register an additional source- independent bihash entry keyed on local dst ip + vni (outer src wildcarded) on tunnel add, maintain it on delete, and consult it in vxlan4/6_find_tunnel only after the exact lookup misses so existing decap behaviour is unchanged. -Includes the L3-promiscuous VNET-decap BVI helper (l2_bvi) used to route the -decapped inner packet into the tenant VRF. + +The source-independent wildcard and the L3-promiscuous BVI receive are scoped +to secondary-VTEP decap terms only, via a decap-any flag the SAI layer sets in +the high bit (VXLAN_DECAP_ANY_FLAG) of the wire decap_next_index; ordinary +VXLAN tunnels keep exact outer-source matching and strict BVI my-mac. All +wildcard bihash add/del return codes are checked: an add failure rolls back +the tunnel, a delete-time re-point failure drops the stale entry. The +L3-promiscuous ingress set is an O(1) sw_if_index bitmap in l2_bvi (no +per-packet device-class/string lookups). + +Limitations: this covers L3 VNET decap-and-route only. L2 EVPN VXLAN +(bridged, MAC-learning) is out of scope - such tunnels are not flagged +decap-any, so they keep exact outer-source matching and strict BVI my-mac +and are unaffected. The decap BVI is L3-enabled for both address families +but set_interface_vrf carries a single family flag, so mixed-family inner +traffic (e.g. inner IPv6 over an IPv4 VTEP) is a known gap shared with the +primary-VTEP decap path. --- diff --git a/src/plugins/vxlan/decap.c b/src/plugins/vxlan/decap.c -index 89422fda5..301ec5966 100644 +index 89422fd..301ec59 100644 --- a/src/plugins/vxlan/decap.c +++ b/src/plugins/vxlan/decap.c @@ -86,6 +86,20 @@ vxlan4_find_tunnel (vxlan_main_t * vxm, last_tunnel_cache4 * cache, @@ -61,54 +76,129 @@ index 89422fda5..301ec5966 100644 *cache = key6; } diff --git a/src/plugins/vxlan/vxlan.c b/src/plugins/vxlan/vxlan.c -index 46c014912..b5e62065d 100644 +index 46c0149..eeadd65 100644 --- a/src/plugins/vxlan/vxlan.c +++ b/src/plugins/vxlan/vxlan.c -@@ -480,6 +480,18 @@ int vnet_vxlan_add_del_tunnel +@@ -4,6 +4,7 @@ + */ + + #include ++#include + #include + #include + #include +@@ -406,6 +407,18 @@ int vnet_vxlan_add_del_tunnel + if (p) + return VNET_API_ERROR_TUNNEL_EXIST; + ++ /* SONiC VNET decap-any: SAI signals a source-independent decap term by ++ * setting VXLAN_DECAP_ANY_FLAG in the wire decap_next_index. Decode it ++ * into a local flag and strip it so the remaining value is a normal ++ * next index. */ ++ u8 decap_any = 0; ++ if (a->decap_next_index != ~0 && ++ (a->decap_next_index & VXLAN_DECAP_ANY_FLAG)) ++ { ++ decap_any = 1; ++ a->decap_next_index &= ~VXLAN_DECAP_ANY_FLAG; ++ } ++ + /*if not set explicitly, default to l2 */ + if (a->decap_next_index == ~0) + a->decap_next_index = VXLAN_INPUT_NEXT_L2_INPUT; +@@ -421,6 +434,7 @@ int vnet_vxlan_add_del_tunnel + #define _(x) t->x = a->x; + foreach_copy_field; + #undef _ ++ t->decap_any = decap_any; + + vxlan_rewrite (t, is_ip6); + /* +@@ -480,6 +494,27 @@ int vnet_vxlan_add_del_tunnel key6.value = (u64) dev_instance; add_failed = clib_bihash_add_del_24_8 (&vxm->vxlan6_tunnel_by_key, &key6, 1 /*add */ ); + /* SONiC VNET decap-any: also register a source-independent decap -+ * entry keyed on local dst ip6 + vni (outer src wildcarded). */ -+ if (!ip46_address_is_multicast (&t->dst)) ++ * entry keyed on local dst ip6 + vni (outer src wildcarded). Only ++ * for decap-any terms so ordinary tunnels keep outer-src checking. */ ++ if (!add_failed && t->decap_any && ++ !ip46_address_is_multicast (&t->dst)) + { + vxlan6_tunnel_key_t key6w; + key6w.key[0] = t->src.ip6.as_u64[0]; + key6w.key[1] = t->src.ip6.as_u64[1]; + key6w.key[2] = key6.key[2]; + key6w.value = (u64) dev_instance; -+ clib_bihash_add_del_24_8 (&vxm->vxlan6_tunnel_by_key, &key6w, -+ 1 /*add */ ); ++ if (clib_bihash_add_del_24_8 (&vxm->vxlan6_tunnel_by_key, &key6w, ++ 1 /*add */ )) ++ { ++ /* roll back the exact entry to avoid a half-programmed ++ * tunnel, then fail the create. */ ++ clib_bihash_add_del_24_8 (&vxm->vxlan6_tunnel_by_key, &key6, ++ 0 /*del */ ); ++ add_failed = 1; ++ } + } } else { -@@ -491,6 +503,17 @@ int vnet_vxlan_add_del_tunnel +@@ -491,6 +526,24 @@ int vnet_vxlan_add_del_tunnel key4.value = di.as_u64; add_failed = clib_bihash_add_del_16_8 (&vxm->vxlan4_tunnel_by_key, &key4, 1 /*add */ ); + /* SONiC VNET decap-any: also register a source-independent decap -+ * entry keyed on local dst ip + vni (outer src wildcarded to 0). */ -+ if (!ip46_address_is_multicast (&t->dst)) ++ * entry keyed on local dst ip + vni (outer src wildcarded to 0). ++ * Only for decap-any terms. */ ++ if (!add_failed && t->decap_any && ++ !ip46_address_is_multicast (&t->dst)) + { + vxlan4_tunnel_key_t key4w; + key4w.key[0] = ((u64) t->src.ip4.as_u32) << 32; + key4w.key[1] = key4.key[1]; + key4w.value = di.as_u64; -+ clib_bihash_add_del_16_8 (&vxm->vxlan4_tunnel_by_key, &key4w, -+ 1 /*add */ ); ++ if (clib_bihash_add_del_16_8 (&vxm->vxlan4_tunnel_by_key, &key4w, ++ 1 /*add */ )) ++ { ++ clib_bihash_add_del_16_8 (&vxm->vxlan4_tunnel_by_key, &key4, ++ 0 /*del */ ); ++ add_failed = 1; ++ } + } } if (add_failed) -@@ -633,6 +656,76 @@ int vnet_vxlan_add_del_tunnel +@@ -508,6 +561,11 @@ int vnet_vxlan_add_del_tunnel + ~0); + vxm->tunnel_index_by_sw_if_index[sw_if_index] = dev_instance; + ++ /* SONiC VNET decap-any: mark this tunnel's ingress sw_if_index so ++ * l2_to_bvi routes its decapped inner frames regardless of dst MAC. */ ++ if (t->decap_any) ++ l2_bvi_set_l3_promiscuous (sw_if_index, 1); ++ + /* setup l2 input config with l2 feature and bd 0 to drop packet */ + vec_validate (l2im->configs, sw_if_index); + l2im->configs[sw_if_index].feature_bitmap = L2INPUT_FEAT_DROP; +@@ -626,6 +684,10 @@ int vnet_vxlan_add_del_tunnel + + vxm->tunnel_index_by_sw_if_index[sw_if_index] = ~0; + ++ /* SONiC VNET decap-any: clear the L3-promiscuous mark for this ingress. */ ++ if (t->decap_any) ++ l2_bvi_set_l3_promiscuous (sw_if_index, 0); ++ + if (!is_ip6) + clib_bihash_add_del_16_8 (&vxm->vxlan4_tunnel_by_key, &key4, + 0 /*del */ ); +@@ -633,6 +695,93 @@ int vnet_vxlan_add_del_tunnel clib_bihash_add_del_24_8 (&vxm->vxlan6_tunnel_by_key, &key6, 0 /*del */ ); + /* SONiC VNET decap-any: keep or drop the source-independent entry. -+ * If another tunnel shares the same local ip + vni it must stay, so -+ * re-point the wildcard entry at that survivor; otherwise remove it. */ -+ if (!is_ip6 && !ip46_address_is_multicast (&t->dst)) ++ * Only decap-any terms own a wildcard entry. If another decap-any ++ * tunnel shares the same local ip + vni it must stay, so re-point the ++ * wildcard entry at that survivor; otherwise remove it. */ ++ if (t->decap_any && !is_ip6 && !ip46_address_is_multicast (&t->dst)) + { + vxlan4_tunnel_key_t key4w; + key4w.key[0] = ((u64) t->src.ip4.as_u32) << 32; @@ -116,7 +206,8 @@ index 46c014912..b5e62065d 100644 + vxlan_tunnel_t *st, *survivor = 0; + pool_foreach (st, vxm->tunnels) + { -+ if (st == t || ip46_address_is_multicast (&st->dst)) ++ if (st == t || !st->decap_any || ++ ip46_address_is_multicast (&st->dst)) + continue; + if (st->src.ip4.as_u32 == t->src.ip4.as_u32 && + st->vni == t->vni && @@ -134,8 +225,16 @@ index 46c014912..b5e62065d 100644 + .next_index = survivor->decap_next_index + }; + key4w.value = diw.as_u64; -+ clib_bihash_add_del_16_8 (&vxm->vxlan4_tunnel_by_key, &key4w, -+ 1 /*add */ ); ++ if (clib_bihash_add_del_16_8 (&vxm->vxlan4_tunnel_by_key, &key4w, ++ 1 /*add */ )) ++ { ++ /* re-point failed: drop the wildcard rather than leave it ++ * pointing at the tunnel slot we are about to free. */ ++ clib_bihash_add_del_16_8 (&vxm->vxlan4_tunnel_by_key, &key4w, ++ 0 /*del */ ); ++ clib_warning ("vxlan decap-any: ip4 wildcard re-point failed," ++ " dropped stale entry for vni %u", t->vni); ++ } + } + else + clib_bihash_add_del_16_8 (&vxm->vxlan4_tunnel_by_key, &key4w, @@ -143,8 +242,8 @@ index 46c014912..b5e62065d 100644 + } + + /* SONiC VNET decap-any (ip6): keep or drop the source-independent -+ * entry; re-point at a surviving same-local-ip+vni tunnel if any. */ -+ if (is_ip6 && !ip46_address_is_multicast (&t->dst)) ++ * entry; re-point at a surviving same-local-ip+vni decap-any tunnel. */ ++ if (t->decap_any && is_ip6 && !ip46_address_is_multicast (&t->dst)) + { + vxlan6_tunnel_key_t key6w; + key6w.key[0] = t->src.ip6.as_u64[0]; @@ -153,7 +252,8 @@ index 46c014912..b5e62065d 100644 + vxlan_tunnel_t *st, *survivor = 0; + pool_foreach (st, vxm->tunnels) + { -+ if (st == t || ip46_address_is_multicast (&st->dst)) ++ if (st == t || !st->decap_any || ++ ip46_address_is_multicast (&st->dst)) + continue; + if (ip6_address_is_equal (&st->src.ip6, &t->src.ip6) && + st->vni == t->vni && @@ -167,8 +267,14 @@ index 46c014912..b5e62065d 100644 + if (survivor) + { + key6w.value = (u64) (survivor - vxm->tunnels); -+ clib_bihash_add_del_24_8 (&vxm->vxlan6_tunnel_by_key, &key6w, -+ 1 /*add */ ); ++ if (clib_bihash_add_del_24_8 (&vxm->vxlan6_tunnel_by_key, &key6w, ++ 1 /*add */ )) ++ { ++ clib_bihash_add_del_24_8 (&vxm->vxlan6_tunnel_by_key, &key6w, ++ 0 /*del */ ); ++ clib_warning ("vxlan decap-any: ip6 wildcard re-point failed," ++ " dropped stale entry for vni %u", t->vni); ++ } + } + else + clib_bihash_add_del_24_8 (&vxm->vxlan6_tunnel_by_key, &key6w, @@ -178,61 +284,95 @@ index 46c014912..b5e62065d 100644 if (!ip46_address_is_multicast (&t->dst)) { if (t->flow_index != ~0) +diff --git a/src/plugins/vxlan/vxlan.h b/src/plugins/vxlan/vxlan.h +index b89e75b..be5fed9 100644 +--- a/src/plugins/vxlan/vxlan.h ++++ b/src/plugins/vxlan/vxlan.h +@@ -123,6 +123,12 @@ typedef struct + u32 dev_instance; /* Real device instance in tunnel vector */ + u32 user_instance; /* Instance name being shown to user */ + ++ /* SONiC VNET decap-any: this tunnel is a secondary-VTEP decap term that ++ * accepts VXLAN packets from any outer source for its local dst+vni. Only ++ * such tunnels register the source-independent wildcard bihash entry and ++ * enable L3-promiscuous receive on their BVI. */ ++ u8 decap_any; ++ + VNET_DECLARE_REWRITE; + } vxlan_tunnel_t; + +@@ -212,6 +218,11 @@ typedef struct + u16 dst_port; + } vnet_vxlan_add_del_tunnel_args_t; + ++/* SONiC VNET decap-any: high bit of the wire decap_next_index used by SAI to ++ * flag a source-independent decap term. Stripped by vnet_vxlan_add_del_tunnel ++ * before the value is validated/used as a real next index. */ ++#define VXLAN_DECAP_ANY_FLAG (1u << 31) ++ + int vnet_vxlan_add_del_tunnel + (vnet_vxlan_add_del_tunnel_args_t * a, u32 * sw_if_indexp); + diff --git a/src/vnet/l2/l2_bvi.c b/src/vnet/l2/l2_bvi.c -index a3c665404..b5e9b88bf 100644 +index a3c6654..beecc7f 100644 --- a/src/vnet/l2/l2_bvi.c +++ b/src/vnet/l2/l2_bvi.c -@@ -9,6 +9,36 @@ +@@ -9,6 +9,40 @@ #include #include #include -+#include ++#include ++ ++/* SONiC VNET decap-and-route (RIOT): a frame that ingressed on a secondary- ++ * VTEP "decap-any" VXLAN tunnel and is being flooded to a BVI is a decap-and- ++ * route packet. On real hardware VXLAN decap-and-route is MAC-agnostic, but ++ * VPP's BVI applies a strict my-mac L2 filter, so decapped frames whose inner ++ * dst MAC != the router/BVI MAC are dropped instead of being L3-routed in the ++ * VNET VRF. For those tunnels only, treat the BVI as L3-promiscuous and route ++ * regardless of dst MAC. Ordinary L2/EVPN VXLAN tunnels and normal L2 ports ++ * keep strict my-mac semantics. ++ * ++ * The set of decap-any ingress sw_if_indexes is maintained as a bitmap owned ++ * here (vnet layer) and populated by the vxlan plugin via ++ * l2_bvi_set_l3_promiscuous() at tunnel add/del. This keeps the data-plane ++ * check an O(1) bitmap test - no per-packet pool/device-class lookups - and ++ * scopes the behaviour to exactly the decap-any terms. */ ++static uword *l2_bvi_l3_promiscuous_bitmap; ++ ++void ++l2_bvi_set_l3_promiscuous (u32 sw_if_index, int enable) ++{ ++ if (sw_if_index == (u32) ~0) ++ return; ++ l2_bvi_l3_promiscuous_bitmap = ++ clib_bitmap_set (l2_bvi_l3_promiscuous_bitmap, sw_if_index, enable ? 1 : 0); ++} + -+/* SONiC VNET decap-and-route (RIOT): a frame that ingressed on a VXLAN tunnel -+ * and is being flooded to a BVI is a decap-and-route packet. On real hardware -+ * VXLAN decap-and-route is MAC-agnostic, but VPP's BVI applies a strict my-mac -+ * L2 filter, so decapped frames whose inner dst MAC != the router/BVI MAC are -+ * dropped instead of being L3-routed in the VNET VRF. When the ingress -+ * interface is a VXLAN tunnel, treat the BVI as L3-promiscuous and route -+ * regardless of dst MAC. Normal L2 ports keep strict my-mac semantics. */ +int +l2_bvi_l3_promiscuous (u32 rx_sw_if_index) +{ -+ vnet_main_t *vnm = vnet_get_main (); -+ vnet_sw_interface_t *si; -+ vnet_hw_interface_t *hi; -+ vnet_device_class_t *dc; -+ + if (rx_sw_if_index == (u32) ~0) + return 0; -+ si = vnet_get_sw_interface_or_null (vnm, rx_sw_if_index); -+ if (!si) -+ return 0; -+ hi = vnet_get_sup_hw_interface (vnm, rx_sw_if_index); -+ if (!hi) -+ return 0; -+ dc = vnet_get_device_class (vnm, hi->dev_class_index); -+ if (dc && dc->name && !strncmp ((char *) dc->name, "VXLAN", 6)) -+ return 1; -+ return 0; ++ return clib_bitmap_get (l2_bvi_l3_promiscuous_bitmap, rx_sw_if_index) ? 1 : 0; +} /* Allocated BVI instances */ static uword *l2_bvi_instances; diff --git a/src/vnet/l2/l2_bvi.h b/src/vnet/l2/l2_bvi.h -index 1087190e6..2028493e8 100644 +index 1087190..305ab7a 100644 --- a/src/vnet/l2/l2_bvi.h +++ b/src/vnet/l2/l2_bvi.h -@@ -17,6 +17,8 @@ +@@ -17,6 +17,9 @@ #define TO_BVI_ERR_BAD_MAC 1 #define TO_BVI_ERR_ETHERTYPE 2 +extern int l2_bvi_l3_promiscuous (u32 sw_if_index); ++extern void l2_bvi_set_l3_promiscuous (u32 sw_if_index, int enable); + static_always_inline u32 l2_to_bvi_dmac_check (vnet_hw_interface_t * hi, u8 * dmac, ethernet_interface_t * ei, u8 have_sec_dmac) -@@ -69,7 +71,12 @@ l2_to_bvi (vlib_main_t * vlib_main, +@@ -69,7 +72,12 @@ l2_to_bvi (vlib_main_t * vlib_main, 0 /* have_sec_dmac */ ); if (rv != TO_BVI_ERR_OK) From 738d1dfd8b8d4189ef562001ffc79994a432889f Mon Sep 17 00:00:00 2001 From: Aaron Bernardino Date: Tue, 28 Jul 2026 17:41:59 +0000 Subject: [PATCH 3/5] Address review: correct stale patch reference in 0016 description The 0016 description referenced "patch 0012" for inner-aware tunnel hashing, but after the series renumber that change is patch 0015 in this series (0012-0014 are master's sflow patches). Update the reference. This edits only the patch's description text (above the '---' separator), not the applied diff, so the compiled VPP artifact is byte-identical and no VPP_VERSION bump is required. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3446b9ac-fa7f-4d26-914b-a4f076c603aa Signed-off-by: Aaron Bernardino --- .../0016-sonic-vxlan-underlay-ecmp-loadbalance-tolerance.patch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vppbld/patches/0016-sonic-vxlan-underlay-ecmp-loadbalance-tolerance.patch b/vppbld/patches/0016-sonic-vxlan-underlay-ecmp-loadbalance-tolerance.patch index a4504b76..2c645755 100644 --- a/vppbld/patches/0016-sonic-vxlan-underlay-ecmp-loadbalance-tolerance.patch +++ b/vppbld/patches/0016-sonic-vxlan-underlay-ecmp-loadbalance-tolerance.patch @@ -13,7 +13,7 @@ buckets for non-power-of-2 groups (e.g. a 3-way group grows from 16 to 256 buckets), increasing per-load-balance memory and rebuild cost at large multipath scale. This does NOT affect LAG member hashing: bonding uses l2_flow_hash, not the multipath bucket allocator. (Inner-aware LAG/ECMP -spreading for tunnelled traffic is provided separately by patch 0012.) +spreading for tunnelled traffic is provided separately by patch 0015.) --- diff --git a/src/vnet/dpo/load_balance.c b/src/vnet/dpo/load_balance.c index 8bec28f4a..245033ece 100644 From 2a72c3855a932bbeb3a10fedd3b45b17fa6d0b48 Mon Sep 17 00:00:00 2001 From: Aaron Bernardino Date: Fri, 7 Aug 2026 15:31:14 +0000 Subject: [PATCH 4/5] [vpp] Revert VPP_VERSION to master 2606-0.3 (no per-PR bump) Per maintainer review on sonic-net/sonic-platform-vpp#262, VPP package versioning is owned by the monthly release process, so this feature PR should not carry a per-PR minor-suffix bump. Revert VPP_VERSION from 2606-0.7 back to master's 2606-0.3. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3446b9ac-fa7f-4d26-914b-a4f076c603aa Signed-off-by: Aaron Bernardino --- rules/vpp.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/vpp.mk b/rules/vpp.mk index 2c2dbd79..56ee659f 100644 --- a/rules/vpp.mk +++ b/rules/vpp.mk @@ -7,7 +7,7 @@ VPP_VERSION_BASE = 2606 # https://packages.buildkite.com/sonic-vpp/vpp; if the suffix isn't bumped, # downstream sonic-buildimage builds will silently pull stale debs that # pre-date the new patch series and end up with VPP/SAI CRC drift. -VPP_VERSION = $(VPP_VERSION_BASE)-0.7 +VPP_VERSION = $(VPP_VERSION_BASE)-0.3 VPP_VERSION_SONIC = $(VPP_VERSION)+b1sonic1 VPP_SRC_PATH = platform/vpp/vppbld From 531555c7a1a8bd091845bf1c5ea113465000b879 Mon Sep 17 00:00:00 2001 From: Aaron Bernardino Date: Fri, 7 Aug 2026 20:43:01 +0000 Subject: [PATCH 5/5] [vpp] Drop patch 0016 (global multipath tolerance) from VXLAN series Patch 0016 tightened multipath_next_hop_error_tolerance from 0.1 to 0.01 in src/vnet/dpo/load_balance.c. That is a global FIB load-balance constant used by every equal-cost group on all ASIC targets, not just the VPP VXLAN underlay, so tightening it changed shared forwarding behavior for real dataplanes. Drop the patch and keep the default 0.1 tolerance. The non-power-of-2 bucket skew that 0016 was papering over (a 3-way group lands on a 16-bucket 6/5/5 load-balance split) is a property of VPP's default multipath allocation, so it is absorbed in the sonic-mgmt VXLAN ECMP test tolerance instead of by recompiling a global constant. Series now applies 0015 (encap inner-aware flow hash) and 0017 (source- independent VNET decap). VPP_VERSION stays 2606-0.3. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3446b9ac-fa7f-4d26-914b-a4f076c603aa --- ...-underlay-ecmp-loadbalance-tolerance.patch | 30 ------------------- vppbld/patches/series | 3 -- 2 files changed, 33 deletions(-) delete mode 100644 vppbld/patches/0016-sonic-vxlan-underlay-ecmp-loadbalance-tolerance.patch diff --git a/vppbld/patches/0016-sonic-vxlan-underlay-ecmp-loadbalance-tolerance.patch b/vppbld/patches/0016-sonic-vxlan-underlay-ecmp-loadbalance-tolerance.patch deleted file mode 100644 index 2c645755..00000000 --- a/vppbld/patches/0016-sonic-vxlan-underlay-ecmp-loadbalance-tolerance.patch +++ /dev/null @@ -1,30 +0,0 @@ -Subject: [PATCH] load-balance: tighten multipath error tolerance for SONiC - -SONiC VXLAN underlay ECMP tests expect near-uniform traffic distribution -across equal-cost underlay paths. The default multipath_next_hop_error_ -tolerance of 0.1 (10%) allows enough skew that a small number of paths can -be dropped from the load-balance bucket set, quantizing an N-way ECMP to -fewer buckets. Tighten it to 0.01 so all equal-cost paths are retained. - -Scope note: multipath_next_hop_error_tolerance is a GLOBAL FIB load-balance -constant (ip_multipath_normalize_next_hops), not VXLAN-specific. Tightening it -improves fairness for every ECMP group but also allocates more load-balance -buckets for non-power-of-2 groups (e.g. a 3-way group grows from 16 to 256 -buckets), increasing per-load-balance memory and rebuild cost at large -multipath scale. This does NOT affect LAG member hashing: bonding uses -l2_flow_hash, not the multipath bucket allocator. (Inner-aware LAG/ECMP -spreading for tunnelled traffic is provided separately by patch 0015.) ---- -diff --git a/src/vnet/dpo/load_balance.c b/src/vnet/dpo/load_balance.c -index 8bec28f4a..245033ece 100644 ---- a/src/vnet/dpo/load_balance.c -+++ b/src/vnet/dpo/load_balance.c -@@ -20,7 +20,7 @@ - /* - * distribution error tolerance for load-balancing - */ --const f64 multipath_next_hop_error_tolerance = 0.1; -+const f64 multipath_next_hop_error_tolerance = 0.01; - - static const char *load_balance_attr_names[] = LOAD_BALANCE_ATTR_NAMES; - diff --git a/vppbld/patches/series b/vppbld/patches/series index 7769eef1..43f96210 100644 --- a/vppbld/patches/series +++ b/vppbld/patches/series @@ -32,9 +32,6 @@ # 15. VXLAN encap inner-aware flow hash: hash inner IP 5-tuple on L3 VXLAN # encap so inner flows spread across underlay ECMP / LAG paths 0015-vxlan-encap-inner-aware-flow-hash.patch -# 16. VXLAN underlay ECMP: tighten multipath load-balance error tolerance so -# all equal-cost underlay paths are retained -0016-sonic-vxlan-underlay-ecmp-loadbalance-tolerance.patch # 17. VXLAN VNET source-independent ("decap-any") decap: match local dst+vni # ignoring outer src (RIOT / secondary-VTEP VNET decap) + l2_bvi helper 0017-sonic-vxlan-vnet-source-independent-decap.patch