On Thu, Jan 22, 2026 at 11:52 AM Mairtin O'Loingsigh via dev <
[email protected]> wrote:

> Add "dynamic-routing-no-learning" option to router and router port options.
> This option will disable learning on a router/router port and remove
> existing learned routes. The port option takes priority over the router
> option.
>
> Reported-at: https://issues.redhat.com/browse/FDP-2750
> Signed-off-by: Mairtin O'Loingsigh <[email protected]>
> ---
>

Hi Mairtin,

thank you for the patch. I needs a rebase and
I have some comments down below.

 NEWS                        |  7 ++++
>  controller/route-exchange.c | 10 ++++++
>  northd/northd.c             | 15 +++++++++
>  ovn-nb.xml                  | 27 +++++++++++++++
>  ovn-sb.xml                  | 15 +++++++++
>  tests/system-ovn.at         | 66 +++++++++++++++++++++++++++++++++++++
>  6 files changed, 140 insertions(+)
>
> diff --git a/NEWS b/NEWS
> index 9883fb81d..8442f6be1 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -1,5 +1,12 @@
>  Post v25.09.0
>  -------------
> +   - Dynamic Routing:
> +     * Add the "options:dynamic-routing-no-learning" to Logical Routers.
> If
> +       set to true router will not learn routes and will forget learned
> +       routes.
> +     * Add the "options:dynamic-routing-no-learning" to Logical Routers
> ports.
> +       If set to true, router port will not learn routes and will forget
> +       learned routes. This option has priority over its router
> counterpart.
>     - Added support for TLS Server Name Indication (SNI) with the new
>       --ssl-server-name option in OVN utilities and daemons. This allows
>       specifying the server name for SNI, which is useful when connecting
> diff --git a/controller/route-exchange.c b/controller/route-exchange.c
> index 8fced5f3c..e909cedfa 100644
> --- a/controller/route-exchange.c
> +++ b/controller/route-exchange.c
> @@ -189,6 +189,16 @@ sb_sync_learned_routes(const struct vector
> *learned_routes,
>              }
>              route_e = route_lookup(&sync_routes, datapath,
>                                     logical_port, ip_prefix, nexthop);
> +
> +            bool no_learning = smap_get_bool(&logical_port->options,
> +                                       "dynamic-routing-no-learning",
> false);
>

The route_e->stale is already set by the SB loop above.
So this can be simplified to only check and continue.
We can also avoid the lookup if this check is done right after
the logical_port check.

+            if (no_learning) {
> +                if (route_e) {
> +                    route_e->stale = true;
> +                }
> +                continue;
> +            }
> +
>              if (route_e) {
>                  route_e->stale = false;
>              } else {
> diff --git a/northd/northd.c b/northd/northd.c
> index d79fe40c9..b531a87f9 100644
> --- a/northd/northd.c
> +++ b/northd/northd.c
> @@ -3919,6 +3919,21 @@ sync_pb_for_lrp(struct ovn_port *op,
>          if (redistribute_local_only_val) {
>              smap_add(&new, redistribute_local_only_name, "true");
>          }
> +        /* Set no-learning on ports based on NB router/router port config
> */
> +        bool no_learning = smap_get_bool(&op->od->nbr->options,
> +                                         "dynamic-routing-no-learning",
> +                                         false);
> +        if (no_learning) {
> +            smap_add(&new, "dynamic-routing-no-learning", "true");
> +        }
> +
> +        no_learning =
> +            smap_get_bool(&op->nbrp->options,
> "dynamic-routing-no-learning",
> +                          false);
> +        if (no_learning) {
> +            smap_add(&new, "dynamic-routing-no-learning", "true");
> +        }
> +
>

The logic here needs to be more elaborate (maybe separate function).
For LR that fine we can use the default, however for LRP we need to
also distinguish between the option being set for LRP or not being
set at all e.g.:

LR=true -> disable learning for all ports
LRP=true -> disable learning for given port
LR=true + LRP=true -> disable learning for all ports
LR=true + LRP=false -> disable learning for all ports but the LRP

Which I'm afraid won't be the case with this patch. But if we do the
same trick as we did with "redistribute_local_only_val" above it
should work:

        const char *no_learn_name = "dynamic-routing-no-learning";
        bool no_learning = smap_get_bool(&op->nbrp->options, no_learn_name,

 smap_get_bool(&op->od->nbr->options,
                                                       no_learn_name,
false));
        if (no_learning) {
            smap_add(&new, "dynamic-routing-no-learning", "true");
        }


>      }
>
>      const char *ipv6_pd_list = smap_get(&op->sb->options,
> "ipv6_ra_pd_list");
> diff --git a/ovn-nb.xml b/ovn-nb.xml
> index e74c0d010..260385670 100644
> --- a/ovn-nb.xml
> +++ b/ovn-nb.xml
> @@ -3442,6 +3442,19 @@ or
>          </p>
>        </column>
>
> +      <column name="options" key="dynamic-routing-no-learning"
> +              type='{"type": "boolean"}'>
> +        <p>
> +          Only relevant if <ref column="options" key="dynamic-routing"/>
> +          is set to <code>true</code>.
> +        </p>
> +
> +        <p>
> +          This option disables learning on a specific router and will also
>

nit: disables route learning


> +          remove learned routes.
> +        </p>
> +      </column>
> +
>        <column name="options" key="dynamic-routing-v4-prefix-nexthop"
>                type='{"type": "string"}'>
>          <p>
> @@ -4582,6 +4595,20 @@ or
>            routes in <code>ovn-ic</code> daemon.
>          </p>
>        </column>
> +
> +      <column name="options" key="dynamic-routing-no-learning"
> +              type='{"type": "boolean"}'>
> +        <p>
> +          Only relevant if <ref column="options" key="dynamic-routing"/>
> +          is set to <code>true</code>.
> +        </p>
> +
> +        <p>
> +          This option disables learning on a specific router port and will
>

nit: disables route learning


> +          also remove learned routes. It also has priority over the router
> +          version of this option.
> +        </p>
> +      </column>
>      </group>
>
>      <group title="Attachment">
> diff --git a/ovn-sb.xml b/ovn-sb.xml
> index 623aaeffd..00bae26bf 100644
> --- a/ovn-sb.xml
> +++ b/ovn-sb.xml
> @@ -3925,6 +3925,21 @@ tcp.flags = RST;
>            bound.  Default: <code>false</code>.
>          </p>
>        </column>
> +
> +      <column name="options" key="dynamic-routing-no-learning"
> +              type='{"type": "boolean"}'>
> +        <p>
> +          Only relevant if <ref column="options" key="dynamic-routing"/>
> +          is set to <code>true</code>.
> +        </p>
> +
> +        <p>
> +          This option disables adding routes to
> +          <ref table="Learned_Route" db="OVN_Southbound"/> and will also
> +          remove learned routes.
> +        </p>
> +      </column>
> +
>      </group>
>
>      <group title="Nested Containers">
> diff --git a/tests/system-ovn.at b/tests/system-ovn.at
> index 636b1e4d9..ec3e388b2 100644
> --- a/tests/system-ovn.at
> +++ b/tests/system-ovn.at
> @@ -19867,6 +19867,72 @@ AT_CHECK([ip route del 10.10.3.1 via 20.0.0.25
> vrf vrf-$vni])
>  OVS_WAIT_FOR_OUTPUT([ovn-sbctl list Learned_Route | grep ip_prefix |
> sort], [0], [dnl
>  ])
>
> +# Disable learning on router
> +AS_BOX([$(date +%H:%M:%S.%03N) Disable dynamic-route learning])
> +
> +# Add a route to the VRF (simulating BGP learning a route)
> +AT_CHECK([ip route add 10.10.3.1 via 20.0.0.25 vrf vrf-$vni proto zebra])
> +
> +# Verify learned route appears in SB database
> +OVS_WAIT_UNTIL([ovn-sbctl list Learned_Route | grep ip_prefix | grep -Fe
> 10.10.3.1])
>

Please replace those greps with wait_row_count, this applies to everything
added here.


> +
> +check ovn-nbctl --wait=sb set Logical_Router lr-frr
> options:dynamic-routing-no-learning=true
>

nit: This should be --wait=hv this applies to everything added here.


> +
> +# Verify routes do not appear in SB database.
> +OVS_WAIT_FOR_OUTPUT([ovn-sbctl list Learned_Route | grep ip_prefix |
> sort], [0], [dnl
> +])
> +
> +check ovn-nbctl --wait=sb set Logical_Router lr-frr
> options:dynamic-routing-no-learning=false
> +
> +# Verify learned route appears in SB database
> +OVS_WAIT_UNTIL([ovn-sbctl list Learned_Route | grep ip_prefix | grep -Fe
> 10.10.3.1])
> +
> +AT_CHECK([ip route del 10.10.3.1 via 20.0.0.25 vrf vrf-$vni])
> +
> +# Verify all routes removed from SB database.
> +OVS_WAIT_FOR_OUTPUT([ovn-sbctl list Learned_Route | grep ip_prefix |
> sort], [0], [dnl
> +])
> +
> +# Disable learning on router 2

+AS_BOX([$(date +%H:%M:%S.%03N) Disable dynamic-route learning 2])
> +
> +check ovn-nbctl --wait=sb set Logical_Router lr-frr
> options:dynamic-routing-no-learning=true
> +
> +# Add a route to the VRF (simulating BGP learning a route)
> +AT_CHECK([ip route add 10.10.3.1 via 20.0.0.25 vrf vrf-$vni proto zebra])
> +
> +# Verify learned route appears in SB database
>

I think this should be "doesn't appear" as the learning is disabled.

+#OVS_WAIT_UNTIL([ovn-sbctl list Learned_Route | grep ip_prefix | grep -Fe
> 10.10.3.1])
> +
> +# Verify routes do not appear in SB database.
> +OVS_WAIT_FOR_OUTPUT([ovn-sbctl list Learned_Route | grep ip_prefix |
> sort], [0], [dnl
> +])
> +
> +check ovn-nbctl --wait=sb set Logical_Router lr-frr
> options:dynamic-routing-no-learning=false
> +
> +# Verify learned route appears in SB database
> +OVS_WAIT_UNTIL([ovn-sbctl list Learned_Route | grep ip_prefix | grep -Fe
> 10.10.3.1])
> +
> +AT_CHECK([ip route del 10.10.3.1 via 20.0.0.25 vrf vrf-$vni])
> +
> +# Verify all routes removed from SB database.
> +OVS_WAIT_FOR_OUTPUT([ovn-sbctl list Learned_Route | grep ip_prefix |
> sort], [0], [dnl
> +])
> +
> +# Disable learning on router port
> +AS_BOX([$(date +%H:%M:%S.%03N) Disable dynamic-route learning on port])
> +check ovn-nbctl --wait=sb set Logical_Router_Port lrp-local-bgp-port
> options:dynamic-routing-no-learning=true
> +
> +# Add a route to the VRF (simulating BGP learning a route)
> +AT_CHECK([ip route add 10.10.3.1 via 20.0.0.25 vrf vrf-$vni proto zebra])
> +
> +# Verify routes do not appear in SB database.
> +OVS_WAIT_FOR_OUTPUT([ovn-sbctl list Learned_Route | grep ip_prefix |
> sort], [0], [dnl
> +])
> +
> +AT_CHECK([ip route del 10.10.3.1 via 20.0.0.25 vrf vrf-$vni])
> +check ovn-nbctl --wait=sb set Logical_Router_Port lrp-local-bgp-port
> options:dynamic-routing-no-learning=false
> +
>  # Add again a route to the VRF (simulating BGP learning a route)
>  AT_CHECK([ip route add 10.10.3.1 via 20.0.0.25 vrf vrf-$vni proto zebra])
>

We should also try the priority, set no-learning=true on LR but at the same
time set no-lerning=false on LRP, the routes should be there.


>
> --
> 2.52.0
>
> _______________________________________________
> dev mailing list
> [email protected]
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
>
Regards,
Ales
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to