On Fri, Aug 21, 2026 at 3:30 AM Xavier Simonart via dev <
[email protected]> wrote:

> Since [1] we do not learn routes with auto-generated link-local next-hops.
> However, routes with auto-generated lla next hops were still created and
> deleted from ovn-ic-sb, causing high cpu usage on ovn-ic and ovn-ic-sb,
> as well as high traffic between AZs and ovn-ic-sb.
>
> Fix this by skipping route advertisement when the derived next-hop is
> an EUI-64 LLA (i.e. fe80::/64).
> Non-EUI-64 link-local addresses (e.g. fe80:10::1/64), are intentionally
> left unfiltered and continue to be advertised and learned normally.
>
> [1] cb0e2b3f44da ("ovn-ic: do not learn routes with link-local next-hops")
>
> Reported-at: https://issues.redhat.com/browse/FDP-2000
> Assisted-by: Claude Sonnet 4.6, OpenCode
> Signed-off-by: Xavier Simonart <[email protected]>
>
> ---
> v2: - Update test based on comment from Roberto in v1, adding non-EUI-64
>       addresses.
>     - Check for NULL lrp in VLOG.
>     - Change IPv6 addresses used (per Claude's review).
> ---
>  ic/ovn-ic.c     | 14 +++++++++
>  tests/ovn-ic.at | 84 +++++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 98 insertions(+)
>
> diff --git a/ic/ovn-ic.c b/ic/ovn-ic.c
> index f7cc41748..7eac87259 100644
> --- a/ic/ovn-ic.c
> +++ b/ic/ovn-ic.c
> @@ -2142,6 +2142,20 @@ add_network_to_routes_ad(struct hmap *routes_ad,
> const char *network,
>          return;
>      }
>
> +    if (in6_is_lla(&nexthop)) {
>

The filter is only added here but add_static_to_routes_ad() and
add_lb_vip_to_routes_ad() also call get_nexthop_from_lport_addresses()
should the same filter be applied to them as well?

If so it might be it cleaner to add the check once, in either
get_nexthop_from_lport_addresses(), returning false when the result is an
LLA, or in add_to_routes_ad()?


> +        if (VLOG_IS_DBG_ENABLED()) {
> +            struct ds msg = DS_EMPTY_INITIALIZER;
> +            ds_put_format(&msg, "Route ad: skip lla nexthop");
> +            if (nb_lrp) {
> +                ds_put_format(&msg, " of lrp %s", nb_lrp->name);
> +            }
> +            ds_put_format(&msg, ".");
> +            VLOG_DBG("%s", ds_cstr(&msg));
> +            ds_destroy(&msg);
> +        }
> +        return;
> +    }
> +
>      if (VLOG_IS_DBG_ENABLED()) {
>          struct ds msg = DS_EMPTY_INITIALIZER;
>
> diff --git a/tests/ovn-ic.at b/tests/ovn-ic.at
> index 1435a19a3..d7d9ad676 100644
> --- a/tests/ovn-ic.at
> +++ b/tests/ovn-ic.at
> @@ -5628,7 +5628,91 @@ OVS_WAIT_FOR_OUTPUT([ovn_as az1 ovn-nbctl
> lr-route-list lr11 | grep 192.168 |
>  ])
>
>  OVN_CLEANUP_IC([az1], [az2])
> +AT_CLEANUP
> +])
> +
> +OVN_FOR_EACH_NORTHD([
> +AT_SETUP([interconnection - LLA])
> +AT_KEYWORDS([LLA])
> +# Logical network:
>
> +#┌──────────────────────────────────────────────────┬────────────────────────────────────────────────────┐
> +#│                       AZ1                        │
>      AZ2                        │
>
> +#├──────────────────────────────────────────────────┼────────────────────────────────────────────────────┤
> +#│ LS11-LR11 (fd11::254) - LR11 - (2001:db8::1) ── TS1 ── (2001:db8::2) -
> LR12 - (fd12::254) - LR12-LS12 │
> +#│ LS21-LR21 (fd21::254) - LR21 - (fe80:10::1) ─────┼──── (fe80:10::2)  -
> LR22 - (fd22::254) - LR22-LS22 │
>
> +#└──────────────────────────────────────────────────┴────────────────────────────────────────────────────┘
> +# LR11/LR12: trigger EUI-64 LLA filtering: they have a global IPv6
> address on the TS port that gets removed,
> +#            causing their stub subnet (fd1x::/64) to fall back to an
> EUI-64 LLA nexthop that should be suppressed.
> +# LR21/LR22: verify non-EUI-64 LLA nexthops (fe80:10::x) work correctly;
> their subnets (fd2x::/64) are advertised and learned.
>
> +ovn_init_ic_db
> +net_add n1
> +
> +for i in 1 2; do
> +    ovn_start az$i
> +    sim_add hv$i
> +    as hv$i
> +    check ovs-vsctl add-br br-phys
> +    ovn_az_attach az$i n1 br-phys 192.168.$i.1 16
> +    ovn_as az$i
> +    check ovs-vsctl set open . external-ids:ovn-is-interconn=true
> +    check ovn-nbctl set nb_global . options:ic-route-learn=true
> +    check ovn-nbctl set nb_global . options:ic-route-adv=true
> +done
> +
> +check ovn-ic-nbctl ts-add ts1
> +check ovn_as az1 ovn-nbctl wait-until logical_switch ts1
> +check ovn_as az2 ovn-nbctl wait-until logical_switch ts1
> +
> +for i in 1 2; do
> +    ovn_as az$i
> +    check ovn-nbctl lr-add lr1$i \
> +                 -- lrp-add lr1$i lr1$i-ts1 00:aa:aa:aa:aa:1$i
> 2001:db8::$i/64 \
> +                 -- lrp-add lr1$i lr1$i-ls1$i 00:00:00:00:0$i:fe
> fd1$i::254/64 \
> +                 -- lsp-add-router-port ts1 ts1-lr1$i lr1$i-ts1
> +    check ovn-nbctl lr-add lr2$i \
> +                 -- lrp-add lr2$i lr2$i-ts1 aa:aa:aa:aa:aa:2$i
> fe80:10::$i/64 \
> +                 -- lsp-add-router-port ts1 ts1-lr2$i lr2$i-ts1
> +
> +    check ovn-nbctl --wait=sb lrp-add lr2$i lr2$i-ls2$i
> 00:00:00:00:0$i:0$i fd2$i::254/64
> +
> +done
> +
> +wait_for_ports_up
> +check ovn-ic-nbctl --wait=sb sync
> +check ovn_as az1 ovn-nbctl --wait=hv sync
> +check ovn_as az2 ovn-nbctl --wait=hv sync
> +
> +# Non-EUI-64 LLA nexthops must be advertised to IC-SB and learned
> normally.
> +OVS_WAIT_UNTIL([ovn_as az1 ovn-nbctl lr-route-list lr21 | grep learned | \
> +                grep fd22::])
> +AT_CHECK([ovn_as az1 ovn-nbctl lr-route-list lr21 | awk '/learned/{print
> $1, $2}'], [0], [dnl
> +fd22::/64 fe80:10::2
> +])
> +OVS_WAIT_UNTIL([ovn_as az2 ovn-nbctl lr-route-list lr22 | grep learned | \
> +                grep fd21::])
> +AT_CHECK([ovn_as az2 ovn-nbctl lr-route-list lr22 | awk '/learned/{print
> $1, $2}'], [0], [dnl
> +fd21::/64 fe80:10::1
> +])
>


I am pretty sure the following code with static routes will expose the
issue. Unless I am misunderstanding.

+# Add a static route on lr11. After removing the global IPv6 address,
+# add_static_to_routes_ad() will use the EUI-64 LLA as nexthop because
+# it lacks the in6_is_lla() filter that add_network_to_routes_ad() has.
+ovn_as az1 check ovn-nbctl lr-route-add lr11 fd99::/64 2001:db8::2
+check ovn-ic-nbctl --wait=sb sync
+OVS_WAIT_UNTIL([ovn-ic-sbctl list route | grep fd99])





> +
> +ovn_as az1 check ovn-nbctl remove logical_router_port lr11-ts1 networks
> "2001\:db8\:\:1/64"
> +check ovn-ic-nbctl --wait=sb sync
> +
> +# ic-sb route should not contain lla next hops.
> +# Such routes used to be created and deleted in ic-sb.
> +# Do not use OVS_WAIT_WHILE or WAIT_UNTIL as the wrong (lla) route
> appears and disappears.
> +for i in $(seq 1 50); do
> +    AT_CHECK([ovn-ic-sbctl list route | grep "fe80:" | sort], [0], [dnl
> +nexthop             : "fe80:10::1"
> +nexthop             : "fe80:10::2"
> +])
> +done
> +
> +OVN_CLEANUP_SBOX([hv1])
> +
> +OVN_CLEANUP_SBOX([hv2])
> +
> +OVN_CLEANUP_IC([az1], [az2])
>

Nit: The blank lines are not necessary.


>  AT_CLEANUP
>  ])
>
> --
> 2.47.1
>
> _______________________________________________
> 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