Thank you very much @Mj ponsonby <[email protected]>, I have tested
this against both Master and 24.03.09, and it is actually solved my issue!



On Thu, Oct 29, 2026 at 3:37 PM MJ Ponsonby via dev <[email protected]>
wrote:

> For a neighbor that is not part of any of the LRP's configured prefixes,
> e.g. an off-subnet next hop selected with an explicit output_port route,
> mac_binding_probe_stats_run() left the source address as in6addr_any and
> silently suppressed the ARP/ND refresh probe.  Such neighbors are still
> reachable through the port (find_static_route_outport() already falls back
> to the port's first address for this case), so their MAC bindings were no
> longer refreshed when aging is enabled.
>
> Fall back to the port's first address when no prefix contains the neighbor,
> matching the routing decision, while keeping the matching-subnet selection
> introduced by the original fix.
>
> Add a regression test where an LRP only has /32 and /128 addresses and the
> off-subnet next hops are reached via explicit output_port routes.
>
> Ubuntu tracking bug: https://bugs.launchpad.net/bugs/2172542.
>
> Fixes: 05005d6e78db ("mac-cache: Make sure we re-arp for proper IP for LRP
> with multiple IPs.")
> Assisted-by: OpenCode (deepseek/deepseek-pro-latest)
> Signed-off-by: MJ Ponsonby <[email protected]>
> ---
> v2:
>   - Shorten the subject line to fit within 70 characters
>     (checkpatch warning).
>   - Use a 12-character abbreviated hash in the Fixes tag
>     (checkpatch error).
>  controller/mac-cache.c | 14 ++++++++
>  tests/ovn.at           | 77 ++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 91 insertions(+)
>
> diff --git a/controller/mac-cache.c b/controller/mac-cache.c
> index b25115f19..4b743adf8 100644
> --- a/controller/mac-cache.c
> +++ b/controller/mac-cache.c
> @@ -1053,6 +1053,20 @@ mac_binding_probe_stats_run(struct vector
> *stats_vec, uint64_t *req_delay,
>              }
>          }
>
> +        /* The neighbor is not part of any of the LRP's configured
> prefixes.
> +         * This is the case for off-subnet next hops selected with an
> explicit
> +         * output_port route (e.g. /32 addressing with a reachable default
> +         * gateway).  Fall back to the port's first address, matching
> +         * find_static_route_outport(). */
> +        if (ipv6_addr_equals(&local, &in6addr_any)) {
> +            if (IN6_IS_ADDR_V4MAPPED(&mb->data.ip) && laddr.n_ipv4_addrs)
> {
> +                local = in6_addr_mapped_ipv4(laddr.ipv4_addrs[0].addr);
> +            } else if (!IN6_IS_ADDR_V4MAPPED(&mb->data.ip)
> +                       && laddr.n_ipv6_addrs) {
> +                local = laddr.ipv6_addrs[0].addr;
> +            }
> +        }
> +
>          if (!ipv6_addr_equals(&local, &in6addr_any)) {
>              struct eth_addr eth_dst =
>                  mb->arp_attempts < PROBE_MULICAST_THRESHOLD
> diff --git a/tests/ovn.at b/tests/ovn.at
> index 780149467..8e48a1556 100644
> --- a/tests/ovn.at
> +++ b/tests/ovn.at
> @@ -38910,6 +38910,83 @@ OVN_CLEANUP([hv1])
>  AT_CLEANUP
>  ])
>
> +OVN_FOR_EACH_NORTHD([
> +AT_SETUP([MAC binding aging - probing off-subnet next hop])
> +CHECK_SCAPY
> +ovn_start
> +
> +aging_th=10
> +net_add n1
> +sim_add hv1
> +as hv1
> +check ovs-vsctl add-br br-phys
> +ovn_attach n1 br-phys 192.168.0.1
> +ovn-appctl -t ovn-controller vlog/set mac_cache:file:dbg pinctrl:file:dbg
> +
> +check ovn-nbctl
>    \
> +    -- ls-add ls1
>    \
> +    -- lr-add lr
>   \
> +    -- set logical_router lr options:mac_binding_age_threshold=$aging_th
>   \
> +    -- lrp-add lr lr-ls1 00:00:00:00:10:00 10.10.10.1/32 fd11::1/128
>    \
> +    -- lsp-add-router-port ls1 ls1-lr lr-ls1
>   \
> +    -- lsp-add ls1 vif1
>    \
> +    -- lsp-set-addresses vif1 "unknown"
> +
> +# Off-subnet next hops reachable through the router port via explicit
> +# output_port routes, as documented in ovn-nb.xml.
> +check ovn-nbctl lr-route-add lr 10.10.30.0/24 10.10.20.100 lr-ls1
> +check ovn-nbctl lr-route-add lr fd30::/64 fd20::64 lr-ls1
> +
> +check ovs-vsctl
>    \
> +    -- add-port br-int vif1
>    \
> +    -- set interface vif1 external-ids:iface-id=vif1
>   \
> +    options:tx_pcap=hv1/vif1-tx.pcap options:rxq_pcap=hv1/vif1-rx.pcap
> +
> +OVN_POPULATE_ARP
> +wait_for_ports_up
> +check ovn-nbctl --wait=hv sync
> +
> +# Wait for pinctrl thread to be connected.
> +OVS_WAIT_UNTIL([grep pinctrl hv1/ovn-controller.log | grep -q connected])
> +
> +# Learn MAC bindings for the off-subnet next hops.
> +send_garp hv1 vif1 2 00:00:00:00:10:1a ff:ff:ff:ff:ff:ff 10.10.20.100
> 10.10.20.100
> +wait_row_count mac_binding 1 ip="10.10.20.100" logical_port="lr-ls1"
> +
> +send_na hv1 vif1 00:00:00:00:10:1a 00:00:00:00:10:00 fd20::64 fd11::1
> +wait_row_count mac_binding 1 ip=\"fd20::64\" logical_port=\"lr-ls1\"
> +
> +# Send IPv4 and IPv6 UDP traffic so the router resolves and uses the
> learned
> +# bindings (this refreshes the OFTABLE_MAC_BINDING entries).  Use a src
> MAC
> +# different from the one learned via GARP/NA so OFTABLE_MAC_CACHE_USE is
> not
> +# reset (that would keep the timestamp fresh and suppress probing).
> +send_udp hv1 vif1 00:00:00:00:10:00 00:00:00:00:10:2a 10.10.30.1
> 172.16.1.1
> +send_udp6 hv1 vif1 00:00:00:00:10:00 00:00:00:00:10:2a fd30::1 fd55::1
> +
> +OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int table=OFTABLE_MAC_BINDING | \
> +                  sed
> 's/reg15=0x.,metadata=0x./reg15=<cleared>,metadata=<cleared>/g' | \
> +                  grep -q
> "reg0=0xa0a1464,reg15=<cleared>,metadata=<cleared>
> actions=mod_dl_dst:00:00:00:00:10:1a"])
> +
> +# Wait until entries in OFTABLE_MAC_CACHE_USE are stale.
> +OVS_WAIT_UNTIL([test $(ovs-ofctl dump-flows br-int
> table=OFTABLE_MAC_CACHE_USE | \
> +                       awk '/nw_src=10.10.20.100/{print
> <http://10.10.20.100/%7Bprint> substr($6,10,1)}') -ge $((aging_th/2))])
> +OVS_WAIT_UNTIL([test $(ovs-ofctl dump-flows br-int
> table=OFTABLE_MAC_CACHE_USE | \
> +                       awk '/ipv6_src=fd20::64/{print substr($6,10,1)}')
> -ge $((aging_th/2))])
> +
> +# Send traffic to trigger probing for both entries.
> +send_udp hv1 vif1 00:00:00:00:10:00 00:00:00:00:10:2a 10.10.30.1
> 172.16.1.1
> +send_udp6 hv1 vif1 00:00:00:00:10:00 00:00:00:00:10:2a fd30::1 fd55::1
> +
> +# Verify ARP/ND probes use the router port's (only) address as the source.
> +dump_arp 1 00:00:00:00:10:00 00:00:00:00:10:1a 10.10.10.1 10.10.20.100
> 00:00:00:00:10:1a > expected
> +OVN_CHECK_PACKETS_CONTAIN([hv1/vif1-tx.pcap], [expected])
> +
> +dump_ns 00:00:00:00:10:1a 00:00:00:00:10:00 fd20::64 fd11::1 fd20::64 >
> expected_v6
> +OVN_CHECK_PACKETS_CONTAIN([hv1/vif1-tx.pcap], [expected_v6])
> +
> +OVN_CLEANUP([hv1])
> +AT_CLEANUP
> +])
>  OVN_FOR_EACH_NORTHD([
>  AT_SETUP([MAC binding aging - probing unicast to broadcast transition])
>  CHECK_SCAPY
> --
> 2.43.0
>
> _______________________________________________
> dev mailing list
> [email protected]
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to