On 2/3/25 5:06 PM, [email protected] wrote:
> On Mon, 2025-02-03 at 16:47 +0100, Felix Huettner wrote:
>> On Mon, Feb 03, 2025 at 04:23:00PM +0100, Dumitru Ceara wrote:
>>> On 2/3/25 4:12 PM, [email protected] wrote:
>>>> On Fri, 2025-01-31 at 12:49 +0100, Dumitru Ceara wrote:
>>>>> On 1/30/25 10:51 PM, Dumitru Ceara wrote:
>>>>>> On 1/21/25 4:47 PM, Felix Huettner via dev wrote:
>>>>>>> in order to exchange routes between OVN and the network
>>>>>>> fabric we
>>>>>>> use the new Advertised_Route sb table. Northd here
>>>>>>> advertises all
>>>>>>> routes
>>>>>>> where the user explicitly opted-in.
>>>>>>>
>>>>>>> ovn-controller will later use this table to share these
>>>>>>> routes to
>>>>>>> the
>>>>>>> outside.
>>>>>>>
>>>>>>> Signed-off-by: Felix Huettner
>>>>>>> <[email protected]>
>>>>>>> ---
>>>>>>
>>>>>> Hi Felix,
>>>>>>
>>>>>> I have a few small comments below.  Aside from those this
>>>>>> patch
>>>>>> looks in
>>>>>> good shape to me.
>>>>>>
>>>>>>> v3->v4:
>>>>>>>   * fix use after free
>>>>>>> v2->v3:
>>>>>>>   * A lot of minor review comments.
>>>>>>>   * Sync logic reworked to no longer need a "stale" field.
>>>>>>>   * Stop watching Advertised_Route table for changes.
>>>>>>>
>>>>>>>  NEWS                              |   4 +
>>>>>>>  ic/ovn-ic.c                       |  21 ----
>>>>>>>  lib/ovn-util.c                    |  22 ++++
>>>>>>>  lib/ovn-util.h                    |   2 +
>>>>>>>  lib/stopwatch-names.h             |   1 +
>>>>>>>  northd/automake.mk                |   2 +
>>>>>>>  northd/en-advertised-route-sync.c | 186
>>>>>>> ++++++++++++++++++++++++++++++
>>>>>>>  northd/en-advertised-route-sync.h |  29 +++++
>>>>>>>  northd/en-northd-output.c         |   8 ++
>>>>>>>  northd/en-northd-output.h         |   2 +
>>>>>>>  northd/inc-proc-northd.c          |  11 +-
>>>>>>>  northd/northd.c                   |  29 +++--
>>>>>>>  northd/northd.h                   |   6 +-
>>>>>>>  northd/ovn-northd.c               |   5 +
>>>>>>>  ovn-nb.xml                        |  13 +++
>>>>>>>  tests/ovn-northd.at               | 137
>>>>>>> ++++++++++++++++++++++
>>>>>>>  16 files changed, 443 insertions(+), 35 deletions(-)
>>>>>>>  create mode 100644 northd/en-advertised-route-sync.c
>>>>>>>  create mode 100644 northd/en-advertised-route-sync.h
>>>>>>>
>>>>>>> diff --git a/NEWS b/NEWS
>>>>>>> index 72c5a6339..4ff158014 100644
>>>>>>> --- a/NEWS
>>>>>>> +++ b/NEWS
>>>>>>> @@ -28,6 +28,10 @@ Post v24.09.0
>>>>>>>         this option was not set).  TLS ciphersuites for
>>>>>>> TLSv1.3
>>>>>>> and later can
>>>>>>>         be configured via --ssl-ciphersuites (--ssl-ciphers
>>>>>>> only
>>>>>>> applies to
>>>>>>>         TLSv1.2 and earlier).
>>>>>>> +   - Dynamic Routing:
>>>>>>> +     * Add the option "dynamic-routing" to Logical
>>>>>>> Routers. If
>>>>>>> set to true all
>>>>>>> +       static and connected routes attached to the router
>>>>>>> are
>>>>>>> shared to the
>>>>>>> +       southbound "Route" table for sharing outside of
>>>>>>> OVN.
>>>>>>>  
>>>>>>>  OVN v24.09.0 - 13 Sep 2024
>>>>>>>  --------------------------
>>>>>>> diff --git a/ic/ovn-ic.c b/ic/ovn-ic.c
>>>>>>> index 8320cbea5..948c17f9b 100644
>>>>>>> --- a/ic/ovn-ic.c
>>>>>>> +++ b/ic/ovn-ic.c
>>>>>>> @@ -1008,27 +1008,6 @@
>>>>>>> get_nexthop_from_lport_addresses(bool
>>>>>>> is_v4,
>>>>>>>      return true;
>>>>>>>  }
>>>>>>>  
>>>>>>> -static bool
>>>>>>> -prefix_is_link_local(struct in6_addr *prefix, unsigned int
>>>>>>> plen)
>>>>>>> -{
>>>>>>> -    if (IN6_IS_ADDR_V4MAPPED(prefix)) {
>>>>>>> -        /* Link local range is "169.254.0.0/16". */
>>>>>>> -        if (plen < 16) {
>>>>>>> -            return false;
>>>>>>> -        }
>>>>>>> -        ovs_be32 lla;
>>>>>>> -        inet_pton(AF_INET, "169.254.0.0", &lla);
>>>>>>> -        return ((in6_addr_get_mapped_ipv4(prefix) &
>>>>>>> htonl(0xffff0000)) == lla);
>>>>>>> -    }
>>>>>>> -
>>>>>>> -    /* ipv6, link local range is "fe80::/10". */
>>>>>>> -    if (plen < 10) {
>>>>>>> -        return false;
>>>>>>> -    }
>>>>>>> -    return (((prefix->s6_addr[0] & 0xff) == 0xfe) &&
>>>>>>> -            ((prefix->s6_addr[1] & 0xc0) == 0x80));
>>>>>>> -}
>>>>>>> -
>>>>>>>  static bool
>>>>>>>  prefix_is_deny_listed(const struct smap *nb_options,
>>>>>>>                        struct in6_addr *prefix,
>>>>>>> diff --git a/lib/ovn-util.c b/lib/ovn-util.c
>>>>>>> index b78bdbfa1..ed847517a 100644
>>>>>>> --- a/lib/ovn-util.c
>>>>>>> +++ b/lib/ovn-util.c
>>>>>>> @@ -1351,3 +1351,25 @@ ovn_update_swconn_at(struct rconn
>>>>>>> *swconn,
>>>>>>> const char *target,
>>>>>>>  
>>>>>>>      return notify;
>>>>>>>  }
>>>>>>> +
>>>>>>> +bool
>>>>>>> +prefix_is_link_local(const struct in6_addr *prefix,
>>>>>>> unsigned int
>>>>>>> plen)
>>>>>>> +{
>>>>>>> +    if (IN6_IS_ADDR_V4MAPPED(prefix)) {
>>>>>>> +        /* Link local range is "169.254.0.0/16". */
>>>>>>> +        if (plen < 16) {
>>>>>>> +            return false;
>>>>>>> +        }
>>>>>>> +        ovs_be32 lla;
>>>>>>> +        inet_pton(AF_INET, "169.254.0.0", &lla);
>>>>>>> +        return ((in6_addr_get_mapped_ipv4(prefix) &
>>>>>>> htonl(0xffff0000)) == lla);
>>>>>>> +    }
>>>>>>> +
>>>>>>> +    /* ipv6, link local range is "fe80::/10". */
>>>>>>> +    if (plen < 10) {
>>>>>>> +        return false;
>>>>>>> +    }
>>>>>>> +    return (((prefix->s6_addr[0] & 0xff) == 0xfe) &&
>>>>>>> +            ((prefix->s6_addr[1] & 0xc0) == 0x80));
>>>>>>> +}
>>>>>>> +
>>>>>>
>>>>>> Nit: one newline too many.
>>>>>>
>>>>>>> diff --git a/lib/ovn-util.h b/lib/ovn-util.h
>>>>>>> index 899bd9d12..31c2c68df 100644
>>>>>>> --- a/lib/ovn-util.h
>>>>>>> +++ b/lib/ovn-util.h
>>>>>>> @@ -487,4 +487,6 @@ void ovn_exit_args_finish(struct
>>>>>>> ovn_exit_args *exit_args);
>>>>>>>  bool ovn_update_swconn_at(struct rconn *swconn, const char
>>>>>>> *target,
>>>>>>>                            int probe_interval, const char
>>>>>>> *where);
>>>>>>>  
>>>>>>> +bool prefix_is_link_local(const struct in6_addr *prefix,
>>>>>>> unsigned int plen);
>>>>>>> +
>>>>>>>  #endif /* OVN_UTIL_H */
>>>>>>> diff --git a/lib/stopwatch-names.h b/lib/stopwatch-names.h
>>>>>>> index 660c653fb..dc4129ee5 100644
>>>>>>> --- a/lib/stopwatch-names.h
>>>>>>> +++ b/lib/stopwatch-names.h
>>>>>>> @@ -34,5 +34,6 @@
>>>>>>>  #define LR_NAT_RUN_STOPWATCH_NAME "lr_nat_run"
>>>>>>>  #define LR_STATEFUL_RUN_STOPWATCH_NAME "lr_stateful"
>>>>>>>  #define LS_STATEFUL_RUN_STOPWATCH_NAME "ls_stateful"
>>>>>>> +#define ADVERTISED_ROUTE_SYNC_RUN_STOPWATCH_NAME
>>>>>>> "advertised_route_sync"
>>>>>>>  
>>>>>>>  #endif
>>>>>>> diff --git a/northd/automake.mk b/northd/automake.mk
>>>>>>> index 6566ad299..a2797237a 100644
>>>>>>> --- a/northd/automake.mk
>>>>>>> +++ b/northd/automake.mk
>>>>>>> @@ -34,6 +34,8 @@ northd_ovn_northd_SOURCES = \
>>>>>>>         northd/en-ls-stateful.h \
>>>>>>>         northd/en-sampling-app.c \
>>>>>>>         northd/en-sampling-app.h \
>>>>>>> +       northd/en-advertised-route-sync.c \
>>>>>>> +       northd/en-advertised-route-sync.h \
>>>>>>>         northd/inc-proc-northd.c \
>>>>>>>         northd/inc-proc-northd.h \
>>>>>>>         northd/ipam.c \
>>>>>>> diff --git a/northd/en-advertised-route-sync.c b/northd/en-
>>>>>>> advertised-route-sync.c
>>>>>>> new file mode 100644
>>>>>>> index 000000000..6ddd15512
>>>>>>> --- /dev/null
>>>>>>> +++ b/northd/en-advertised-route-sync.c
>>>>>>> @@ -0,0 +1,186 @@
>>>>>>> +/*
>>>>>>> + * Copyright (c) 2024, STACKIT GmbH & Co. KG
>>>>>>
>>>>>> Nit: 2025
>>>>>>
>>>>>>> + *
>>>>>>> + * Licensed under the Apache License, Version 2.0 (the
>>>>>>> "License");
>>>>>>> + * you may not use this file except in compliance with the
>>>>>>> License.
>>>>>>> + * You may obtain a copy of the License at:
>>>>>>> + *
>>>>>>> + *     http://www.apache.org/licenses/LICENSE-2.0
>>>>>>> + *
>>>>>>> + * Unless required by applicable law or agreed to in
>>>>>>> writing,
>>>>>>> software
>>>>>>> + * distributed under the License is distributed on an "AS
>>>>>>> IS"
>>>>>>> BASIS,
>>>>>>> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
>>>>>>> express
>>>>>>> or implied.
>>>>>>> + * See the License for the specific language governing
>>>>>>> permissions and
>>>>>>> + * limitations under the License.
>>>>>>> + */
>>>>>>> +
>>>>>>> +#include <config.h>
>>>>>>> +
>>>>>>> +#include "stopwatch.h"
>>>>>>> +#include "northd.h"
>>>>>>> +
>>>>>>> +#include "en-advertised-route-sync.h"
>>>>>>> +#include "lib/stopwatch-names.h"
>>>>>>> +#include "openvswitch/hmap.h"
>>>>>>> +#include "ovn-util.h"
>>>>>>> +
>>>>>>> +static void
>>>>>>> +advertised_route_table_sync(
>>>>>>> +    struct ovsdb_idl_txn *ovnsb_txn,
>>>>>>> +    const struct sbrec_advertised_route_table
>>>>>>> *sbrec_advertised_route_table,
>>>>>>> +    const struct hmap *parsed_routes);
>>>>>>> +
>>>>>>> +void
>>>>>>> +*en_advertised_route_sync_init(struct engine_node *node
>>>>>>> OVS_UNUSED,
>>>>>>> +                     struct engine_arg *arg OVS_UNUSED)
>>>>>>> +{
>>>>>>> +    return NULL;
>>>>>>> +}
>>>>>>> +
>>>>>>> +void
>>>>>>> +en_advertised_route_sync_cleanup(void *data OVS_UNUSED)
>>>>>>> +{
>>>>>>> +}
>>>>>>> +
>>>>>>> +void
>>>>>>> +en_advertised_route_sync_run(struct engine_node *node,
>>>>>>> void
>>>>>>> *data OVS_UNUSED)
>>>>>>> +{
>>>>>>> +    struct routes_data *routes_data
>>>>>>> +        = engine_get_input_data("routes", node);
>>>>>>> +    const struct engine_context *eng_ctx =
>>>>>>> engine_get_context();
>>>>>>> +    const struct sbrec_advertised_route_table
>>>>>>> *sbrec_advertised_route_table =
>>>>>>> +       
>>>>>>> EN_OVSDB_GET(engine_get_input("SB_advertised_route",
>>>>>>> node));
>>>>>>> +
>>>>>>> +   
>>>>>>> stopwatch_start(ADVERTISED_ROUTE_SYNC_RUN_STOPWATCH_NAME,
>>>>>>> time_msec());
>>>>>>> +
>>>>>>> +    advertised_route_table_sync(eng_ctx->ovnsb_idl_txn,
>>>>>>> +                      sbrec_advertised_route_table,
>>>>>>> +                      &routes_data->parsed_routes);
>>>>>>
>>>>>> Nit: indent under the parenthesis, i.e.:
>>>>>>
>>>>>>     advertised_route_table_sync(eng_ctx->ovnsb_idl_txn,
>>>>>>                                 sbrec_advertised_route_table,
>>>>>>                                 &routes_data->parsed_routes);
>>>>>>
>>>>>>> +
>>>>>>> +   
>>>>>>> stopwatch_stop(ADVERTISED_ROUTE_SYNC_RUN_STOPWATCH_NAME,
>>>>>>> time_msec());
>>>>>>> +    engine_set_node_state(node, EN_UPDATED);
>>>>>>> +}
>>>>>>> +
>>>>>>> +struct ar_entry {
>>>>>>> +    struct hmap_node hmap_node;
>>>>>>> +
>>>>>>> +    const struct sbrec_datapath_binding *sb_db;
>>>>>>> +
>>>>>>> +    const struct sbrec_port_binding *logical_port;
>>>>>>> +    char *ip_prefix;
>>>>>>> +};
>>>>>>> +
>>>>>>> +static struct ar_entry *
>>>>>>> +ar_alloc_entry(struct hmap *routes,
>>>>>>> +               const struct sbrec_datapath_binding *sb_db,
>>>>>>> +               const struct sbrec_port_binding
>>>>>>> *logical_port,
>>>>>>> +               char *ip_prefix)
>>>>>>> +{
>>>>>>> +    struct ar_entry *route_e = xzalloc(sizeof *route_e);
>>>>>>> +
>>>>>>> +    route_e->sb_db = sb_db;
>>>>>>> +    route_e->logical_port = logical_port;
>>>>>>> +    route_e->ip_prefix = ip_prefix;
>>>>>>> +    uint32_t hash = uuid_hash(&sb_db->header_.uuid);
>>>>>>> +    hash = hash_string(logical_port->logical_port, hash);
>>>>>>> +    hash = hash_string(ip_prefix, hash);
>>>>>>> +    hmap_insert(routes, &route_e->hmap_node, hash);
>>>>>>> +
>>>>>
>>>>> This does more than just allocating the entry.  Let's call the
>>>>> function
>>>>> ar_add_entry() or similar.  What do you think?
>>>>
>>>> Hi Felix, Dumitru,
>>>
>>> Hi Martin,
>>>
>>>> I have one more question/comment for ar_alloc_entry.
>>>> If you 'free' memory at pointer passed as the '*ip_prefix' after
>>>> calling this function, northd will crash some time later when
>>>> trying to
>>>> serialize request to SB. Example of code that would cause the
>>>> crash:
>>>>
>>>>     char *ip_prefix = normalize_v46_prefix(&route->prefix,
>>>>                                            route->plen);
>>>>     ar_alloc_entry(route_map,
>>>>                   route->od->sb,
>>>>                   route->out_port->sb,
>>>>                   ip_prefix,
>>>>                   tracked_pb);
>>>>     free(ip_prefix);
>>>>
>>>> This caught me quite off guard. Could the function copy the value
>>>> from
>>>> `*ip_prefix` instead? Or alternatively add a comment that
>>>> outlines this
>>>> behavior.
>>>>
>>>
>>> It kind of depends what semantics we want.  In the current form
>>> ar_alloc_entry() takes ownership of the ip_prefix you pass it.  So
>>> freeing ip_prefix outside afterwards would break the semantics.
>>
>> Hi Martin, Hi Dumitru,
>>
>> i wanted to take the ownership for ar_alloc_entry (or ar_add_entry)
>> because we generally need to convert something to a string first.
>> As we don't need this string afterwards i found that duplicating it
>> seems quite unnecessary.
>>
>> Thanks a lot,
>> Felix
> 
> Thanks for the explanation,
> I don't have strong opinion on the ownership (whether to take it or
> not), but i think that if it stays this way, a comment would go a long
> way to avoid issues for future travelers.

That's a fair ask, +1.

>>
>>>
>>>> I also noticed that in later patches in this series, in function
>>>> `publish_host_routes`, there's code like this:
>>>>
>>>>     struct ovn_port_routable_addresses addrs = get_op_addresses(
>>>>         port->peer, lr_stateful_rec, false);
>>>>     for (size_t i = 0; i < addrs.n_addrs; i++) {
>>>>         publish_lport_addresses(sync_routes, route->od->sb,
>>>>                                 route->out_port,
>>>>                                 &addrs.laddrs[i],
>>>>                                 port->peer);
>>>>     }
>>>>     destroy_routable_addresses(&addrs);
>>>>
>>>> Here, the `&addrs.laddrs[i]` are passed to
>>>> `publish_lport_addresses`
>>>> which eventually passes `&addrs.laddrs[i]->ipv4_addrs[]` into
>>>> `ar_alloc_entry` and then it destroys the `&addrs`. I'm bit
>>>> usnsure if
>>>> it's the way that `destroy_routable_addresses` destroys `&addrs`,
>>>> or if
>>>> it's just accidental, but in this case the north is not crashing.
>>>>
>>>
>>> The difference in this case is that the call is:
>>>
>>>         const struct ipv4_netaddr *addr = &addresses-
>>>> ipv4_addrs[i];
>>>         char *addr_s = xasprintf("%s/32", addr->addr_s);
>>>         ar_alloc_entry(sync_routes, sb_db, logical_port->sb,
>>>                        addr_s, tracking_port->sb);
>>>
>>> So ar_alloc_entry() is getting again full ownership of the addr_s
>>> we
>>> built (and allocated) with the xasprintf() call.
> 
> Oh right, thanks for pointing that out, I missed the effect of
> xasprintf.
> 
> 
>>>
>>> Hope this helps.
>>>
> It did, thanks ;)
> 
> Martin.
> 
>>> Dumitru
>>>
>>>> Martin.
>>>>
>>>>>
>>>>>>> +    return route_e;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static struct ar_entry *
>>>>>>> +ar_find(struct hmap *route_map,
>>>>>>> +                    const struct sbrec_datapath_binding
>>>>>>> *sb_db,
>>>>>>> +                    const struct sbrec_port_binding
>>>>>>> *logical_port,
>>>>>>> +                    const char *ip_prefix)
>>>>>>
>>>>>> Nit: indentation
>>>>>>
>>>>>>> +{
>>>>>>> +    struct ar_entry *route_e;
>>>>>>> +    uint32_t hash;
>>>>>>> +
>>>>>>> +    hash = uuid_hash(&sb_db->header_.uuid);
>>>>>>> +    hash = hash_string(logical_port->logical_port, hash);
>>>>>>> +    hash = hash_string(ip_prefix, hash);
>>>>>>> +    HMAP_FOR_EACH_WITH_HASH (route_e, hmap_node, hash,
>>>>>>> route_map) {
>>>>>>> +        if (!uuid_equals(&sb_db->header_.uuid,
>>>>>>> +                         &route_e->sb_db->header_.uuid)) {
>>>>>>> +            continue;
>>>>>>> +        }
>>>>>>> +
>>>>>>> +        if (!uuid_equals(&logical_port->header_.uuid,
>>>>>>> +                         &route_e->logical_port-
>>>>>>>> header_.uuid))
>>>>>>> {
>>>>>>> +            continue;
>>>>>>> +        }
>>>>>>> +
>>>>>>> +        if (strcmp(ip_prefix, route_e->ip_prefix)) {
>>>>>>> +            continue;
>>>>>>> +        }
>>>>>>> +
>>>>>>> +        return route_e;
>>>>>>> +    }
>>>>>>> +
>>>>>>> +    return NULL;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static void
>>>>>>> +ar_entry_free(struct ar_entry *route_e)
>>>>>>> +{
>>>>>>> +    free(route_e->ip_prefix);
>>>>>>> +    free(route_e);
>>>>>>> +}
>>>>>>> +
>>>>>>> +static void
>>>>>>> +advertised_route_table_sync(
>>>>>>> +    struct ovsdb_idl_txn *ovnsb_txn,
>>>>>>> +    const struct sbrec_advertised_route_table
>>>>>>> *sbrec_advertised_route_table,
>>>>>>> +    const struct hmap *parsed_routes)
>>>>>>> +{
>>>>>>> +    struct hmap sync_routes =
>>>>>>> HMAP_INITIALIZER(&sync_routes);
>>>>>>> +
>>>>>>
>>>>>> Nit: no need for an empty line IMO.
>>>>>>
>>>>>>> +    const struct parsed_route *route;
>>>>>>> +
>>>>>>> +    struct ar_entry *route_e;
>>>>>>> +    const struct sbrec_advertised_route *sb_route;
>>>>>>> +    HMAP_FOR_EACH (route, key_node, parsed_routes) {
>>>>>>> +        if (route->is_discard_route) {
>>>>>>> +            continue;
>>>>>>> +        }
>>>>>>> +        if (prefix_is_link_local(&route->prefix, route-
>>>>>>>> plen)) {
>>>>>>> +            continue;
>>>>>>> +        }
>>>>>>> +        if (!smap_get_bool(&route->od->nbr->options,
>>>>>>> "dynamic-
>>>>>>> routing",
>>>>>>> +                           false)) {
>>>>>>> +            continue;
>>>>>>> +        }
>>>>>>
>>>>>> This should be:
>>>>>>
>>>>>>         if (!route->od->dynamic_routing) {
>>>>>>             continue;
>>>>>>         }
>>>>>>
>>>>>> instead.
>>>>>>
>>>>>>> +
>>>>>>> +        char *ip_prefix = normalize_v46_prefix(&route-
>>>>>>>> prefix,
>>>>>>> +                                               route-
>>>>>>>> plen);
>>>>>>> +        route_e = ar_alloc_entry(&sync_routes, route->od-
>>>>>>>> sb,
>>>>>>> +                                 route->out_port->sb,
>>>>>>> ip_prefix);
>>>>>>> +    }
>>>>>>> +
>>>>>>> +    SBREC_ADVERTISED_ROUTE_TABLE_FOR_EACH_SAFE (sb_route,
>>>>>>> +                                               
>>>>>>> sbrec_advertised_route_table) {
>>>>>>> +        route_e = ar_find(&sync_routes, sb_route-
>>>>>>>> datapath,
>>>>>>> +                          sb_route->logical_port,
>>>>>>> +                          sb_route->ip_prefix);
>>>>>>> +        if (route_e) {
>>>>>>> +          hmap_remove(&sync_routes, &route_e->hmap_node);
>>>>>>> +          ar_entry_free(route_e);
>>>>>>> +        } else {
>>>>>>> +          sbrec_advertised_route_delete(sb_route);
>>>>>>> +        }
>>>>>>> +    }
>>>>>>> +
>>>>>>> +    HMAP_FOR_EACH_POP (route_e, hmap_node, &sync_routes) {
>>>>>>> +        const struct sbrec_advertised_route *sr =
>>>>>>> +            sbrec_advertised_route_insert(ovnsb_txn);
>>>>>>> +        sbrec_advertised_route_set_datapath(sr, route_e-
>>>>>>>> sb_db);
>>>>>>> +        sbrec_advertised_route_set_logical_port(sr,
>>>>>>> route_e-
>>>>>>>> logical_port);
>>>>>>> +        sbrec_advertised_route_set_ip_prefix(sr, route_e-
>>>>>>>> ip_prefix);
>>>>>>> +        ar_entry_free(route_e);
>>>>>>> +    }
>>>>>>> +
>>>>>>> +    hmap_destroy(&sync_routes);
>>>>>>> +}
>>>>>>> +
>>>>>>> diff --git a/northd/en-advertised-route-sync.h b/northd/en-
>>>>>>> advertised-route-sync.h
>>>>>>> new file mode 100644
>>>>>>> index 000000000..bd27ecefa
>>>>>>> --- /dev/null
>>>>>>> +++ b/northd/en-advertised-route-sync.h
>>>>>>> @@ -0,0 +1,29 @@
>>>>>>> +/*
>>>>>>> + * Copyright (c) 2024, STACKIT GmbH & Co. KG
>>>>>>
>>>>>> Nit: 2025
>>>>>>
>>>>>>> + *
>>>>>>> + * Licensed under the Apache License, Version 2.0 (the
>>>>>>> "License");
>>>>>>> + * you may not use this file except in compliance with the
>>>>>>> License.
>>>>>>> + * You may obtain a copy of the License at:
>>>>>>> + *
>>>>>>> + *     http://www.apache.org/licenses/LICENSE-2.0
>>>>>>> + *
>>>>>>> + * Unless required by applicable law or agreed to in
>>>>>>> writing,
>>>>>>> software
>>>>>>> + * distributed under the License is distributed on an "AS
>>>>>>> IS"
>>>>>>> BASIS,
>>>>>>> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
>>>>>>> express
>>>>>>> or implied.
>>>>>>> + * See the License for the specific language governing
>>>>>>> permissions and
>>>>>>> + * limitations under the License.
>>>>>>> + */
>>>>>>> +#ifndef EN_ADVERTISED_ROUTE_SYNC_H
>>>>>>> +#define EN_ADVERTISED_ROUTE_SYNC_H 1
>>>>>>> +
>>>>>>> +#include "lib/inc-proc-eng.h"
>>>>>>> +
>>>>>>> +struct advertised_route_sync_data {
>>>>>>> +};
>>>>>>> +
>>>>>>> +void *en_advertised_route_sync_init(struct engine_node *,
>>>>>>> struct
>>>>>>> engine_arg *);
>>>>>>> +void en_advertised_route_sync_cleanup(void *data);
>>>>>>> +void en_advertised_route_sync_run(struct engine_node *,
>>>>>>> void
>>>>>>> *data);
>>>>>>> +
>>>>>>> +
>>>>>>
>>>>>> Nit: one empty line too many.
>>>>>>
>>>>>>> +#endif /* EN_ADVERTISED_ROUTE_SYNC_H */
>>>>>>> diff --git a/northd/en-northd-output.c b/northd/en-northd-
>>>>>>> output.c
>>>>>>> index 98098d974..555ed2b9e 100644
>>>>>>> --- a/northd/en-northd-output.c
>>>>>>> +++ b/northd/en-northd-output.c
>>>>>>> @@ -72,3 +72,11 @@ northd_output_fdb_aging_handler(struct
>>>>>>> engine_node *node,
>>>>>>>      engine_set_node_state(node, EN_UPDATED);
>>>>>>>      return true;
>>>>>>>  }
>>>>>>> +
>>>>>>> +bool
>>>>>>> +northd_output_advertised_route_sync_handler(struct
>>>>>>> engine_node
>>>>>>> *node,
>>>>>>> +                                            void *data
>>>>>>> OVS_UNUSED)
>>>>>>> +{
>>>>>>> +    engine_set_node_state(node, EN_UPDATED);
>>>>>>> +    return true;
>>>>>>> +}
>>>>>>> diff --git a/northd/en-northd-output.h b/northd/en-northd-
>>>>>>> output.h
>>>>>>> index 5f577b89c..00391ace3 100644
>>>>>>> --- a/northd/en-northd-output.h
>>>>>>> +++ b/northd/en-northd-output.h
>>>>>>> @@ -17,5 +17,7 @@ bool
>>>>>>> northd_output_mac_binding_aging_handler(struct engine_node
>>>>>>> *node,
>>>>>>>                                               void *data
>>>>>>> OVS_UNUSED);
>>>>>>>  bool northd_output_fdb_aging_handler(struct engine_node
>>>>>>> *node,
>>>>>>>                                       void *data
>>>>>>> OVS_UNUSED);
>>>>>>> +bool northd_output_advertised_route_sync_handler(struct
>>>>>>> engine_node *node,
>>>>>>> +                                                 void
>>>>>>> *data
>>>>>>> OVS_UNUSED);
>>>>>>>  
>>>>>>>  #endif
>>>>>>> diff --git a/northd/inc-proc-northd.c b/northd/inc-proc-
>>>>>>> northd.c
>>>>>>> index 6e0aa04c4..12956a5de 100644
>>>>>>> --- a/northd/inc-proc-northd.c
>>>>>>> +++ b/northd/inc-proc-northd.c
>>>>>>> @@ -41,6 +41,7 @@
>>>>>>>  #include "en-sampling-app.h"
>>>>>>>  #include "en-sync-sb.h"
>>>>>>>  #include "en-sync-from-sb.h"
>>>>>>> +#include "en-advertised-route-sync.h"
>>>>>>>  #include "unixctl.h"
>>>>>>>  #include "util.h"
>>>>>>>  
>>>>>>> @@ -102,7 +103,8 @@ static unixctl_cb_func
>>>>>>> chassis_features_list;
>>>>>>>      SB_NODE(fdb, "fdb") \
>>>>>>>      SB_NODE(static_mac_binding, "static_mac_binding") \
>>>>>>>      SB_NODE(chassis_template_var, "chassis_template_var")
>>>>>>> \
>>>>>>> -    SB_NODE(logical_dp_group, "logical_dp_group")
>>>>>>> +    SB_NODE(logical_dp_group, "logical_dp_group") \
>>>>>>> +    SB_NODE(advertised_route, "advertised_route")
>>>>>>>  
>>>>>>>  enum sb_engine_node {
>>>>>>>  #define SB_NODE(NAME, NAME_STR) SB_##NAME,
>>>>>>> @@ -161,6 +163,7 @@ static ENGINE_NODE(route_policies,
>>>>>>> "route_policies");
>>>>>>>  static ENGINE_NODE(routes, "routes");
>>>>>>>  static ENGINE_NODE(bfd, "bfd");
>>>>>>>  static ENGINE_NODE(bfd_sync, "bfd_sync");
>>>>>>> +static ENGINE_NODE(advertised_route_sync,
>>>>>>> "advertised_route_sync");
>>>>>>>  
>>>>>>>  void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
>>>>>>>                            struct ovsdb_idl_loop *sb)
>>>>>>> @@ -263,6 +266,10 @@ void inc_proc_northd_init(struct
>>>>>>> ovsdb_idl_loop *nb,
>>>>>>>      engine_add_input(&en_bfd_sync, &en_route_policies,
>>>>>>> NULL);
>>>>>>>      engine_add_input(&en_bfd_sync, &en_northd,
>>>>>>> bfd_sync_northd_change_handler);
>>>>>>>  
>>>>>>> +    engine_add_input(&en_advertised_route_sync,
>>>>>>> &en_routes,
>>>>>>> NULL);
>>>>>>> +    engine_add_input(&en_advertised_route_sync,
>>>>>>> &en_sb_advertised_route,
>>>>>>> +                     NULL);
>>>>>>> +
>>>>>>>      engine_add_input(&en_sync_meters, &en_nb_acl, NULL);
>>>>>>>      engine_add_input(&en_sync_meters, &en_nb_meter, NULL);
>>>>>>>      engine_add_input(&en_sync_meters, &en_sb_meter, NULL);
>>>>>>> @@ -344,6 +351,8 @@ void inc_proc_northd_init(struct
>>>>>>> ovsdb_idl_loop *nb,
>>>>>>>                      
>>>>>>> northd_output_mac_binding_aging_handler);
>>>>>>>      engine_add_input(&en_northd_output, &en_fdb_aging,
>>>>>>>                       northd_output_fdb_aging_handler);
>>>>>>> +    engine_add_input(&en_northd_output,
>>>>>>> &en_advertised_route_sync,
>>>>>>> +                    
>>>>>>> northd_output_advertised_route_sync_handler);
>>>>>>>  
>>>>>>>      struct engine_arg engine_arg = {
>>>>>>>          .nb_idl = nb->idl,
>>>>>>> diff --git a/northd/northd.c b/northd/northd.c
>>>>>>> index 74014de32..1c32ef8f8 100644
>>>>>>> --- a/northd/northd.c
>>>>>>> +++ b/northd/northd.c
>>>>>>> @@ -912,6 +912,8 @@ join_datapaths(const struct
>>>>>>> nbrec_logical_switch_table *nbrec_ls_table,
>>>>>>>          if (smap_get(&od->nbr->options, "chassis")) {
>>>>>>>              od->is_gw_router = true;
>>>>>>>          }
>>>>>>> +        od->dynamic_routing = smap_get_bool(&od->nbr-
>>>>>>>> options,
>>>>>>> +                                            "dynamic-
>>>>>>> routing",
>>>>>>> false);
>>>>>>>          ovs_list_push_back(lr_list, &od->lr_list);
>>>>>>>      }
>>>>>>>  }
>>>>>>> @@ -11127,7 +11129,8 @@ route_hash(struct parsed_route
>>>>>>> *route)
>>>>>>>  }
>>>>>>>  
>>>>>>>  static bool
>>>>>>> -find_static_route_outport(struct ovn_datapath *od, const
>>>>>>> struct
>>>>>>> hmap *lr_ports,
>>>>>>> +find_static_route_outport(const struct ovn_datapath *od,
>>>>>>> +    const struct hmap *lr_ports,
>>>>>>>      const struct nbrec_logical_router_static_route *route,
>>>>>>> bool
>>>>>>> is_ipv4,
>>>>>>>      const char **p_lrp_addr_s, struct ovn_port
>>>>>>> **p_out_port);
>>>>>>>  
>>>>>>> @@ -11229,7 +11232,7 @@ parsed_route_add(const struct
>>>>>>> ovn_datapath *od,
>>>>>>>      new_pr->route_table_id = route_table_id;
>>>>>>>      new_pr->is_src_route = is_src_route;
>>>>>>>      new_pr->hash = route_hash(new_pr);
>>>>>>> -    new_pr->nbr = od->nbr;
>>>>>>> +    new_pr->od = od;
>>>>>>>      new_pr->ecmp_symmetric_reply = ecmp_symmetric_reply;
>>>>>>>      new_pr->is_discard_route = is_discard_route;
>>>>>>>      if (!is_discard_route) {
>>>>>>> @@ -11255,11 +11258,12 @@ parsed_route_add(const struct
>>>>>>> ovn_datapath *od,
>>>>>>>  }
>>>>>>>  
>>>>>>>  static void
>>>>>>> -parsed_routes_add_static(struct ovn_datapath *od, const
>>>>>>> struct
>>>>>>> hmap *lr_ports,
>>>>>>> -                  const struct
>>>>>>> nbrec_logical_router_static_route
>>>>>>> *route,
>>>>>>> -                  const struct hmap *bfd_connections,
>>>>>>> -                  struct hmap *routes, struct simap
>>>>>>> *route_tables,
>>>>>>> -                  struct hmap *bfd_active_connections)
>>>>>>> +parsed_routes_add_static(const struct ovn_datapath *od,
>>>>>>> +                         const struct hmap *lr_ports,
>>>>>>> +                         const struct
>>>>>>> nbrec_logical_router_static_route *route,
>>>>>>> +                         const struct hmap
>>>>>>> *bfd_connections,
>>>>>>> +                         struct hmap *routes, struct simap
>>>>>>> *route_tables,
>>>>>>> +                         struct hmap
>>>>>>> *bfd_active_connections)
>>>>>>>  {
>>>>>>>      /* Verify that the next hop is an IP address with an
>>>>>>> all-
>>>>>>> ones mask. */
>>>>>>>      struct in6_addr *nexthop = NULL;
>>>>>>> @@ -11381,7 +11385,8 @@ parsed_routes_add_static(struct
>>>>>>> ovn_datapath *od, const struct hmap *lr_ports,
>>>>>>>  }
>>>>>>>  
>>>>>>>  static void
>>>>>>> -parsed_routes_add_connected(struct ovn_datapath *od, const
>>>>>>> struct ovn_port *op,
>>>>>>> +parsed_routes_add_connected(const struct ovn_datapath *od,
>>>>>>> +                            const struct ovn_port *op,
>>>>>>>                              struct hmap *routes)
>>>>>>>  {
>>>>>>>      for (size_t i = 0; i < op->lrp_networks.n_ipv4_addrs;
>>>>>>> i++) {
>>>>>>> @@ -11410,14 +11415,14 @@
>>>>>>> parsed_routes_add_connected(struct
>>>>>>> ovn_datapath *od, const struct ovn_port *op,
>>>>>>>  }
>>>>>>>  
>>>>>>>  void
>>>>>>> -build_parsed_routes(struct ovn_datapath *od, const struct
>>>>>>> hmap
>>>>>>> *lr_ports,
>>>>>>> +build_parsed_routes(const struct ovn_datapath *od, const
>>>>>>> struct
>>>>>>> hmap *lr_ports,
>>>>>>>                      const struct hmap *bfd_connections,
>>>>>>> struct
>>>>>>> hmap *routes,
>>>>>>>                      struct simap *route_tables,
>>>>>>>                      struct hmap *bfd_active_connections)
>>>>>>>  {
>>>>>>>      struct parsed_route *pr;
>>>>>>>      HMAP_FOR_EACH (pr, key_node, routes) {
>>>>>>> -        if (pr->nbr == od->nbr) {
>>>>>>> +        if (pr->od == od) {
>>>>>>>              pr->stale = true;
>>>>>>>          }
>>>>>>>      }
>>>>>>> @@ -11641,13 +11646,15 @@ build_route_match(const struct
>>>>>>> ovn_port
>>>>>>> *op_inport, uint32_t rtb_id,
>>>>>>>  
>>>>>>>  /* Output: p_lrp_addr_s and p_out_port. */
>>>>>>>  static bool
>>>>>>> -find_static_route_outport(struct ovn_datapath *od, const
>>>>>>> struct
>>>>>>> hmap *lr_ports,
>>>>>>> +find_static_route_outport(const struct ovn_datapath *od,
>>>>>>> +    const struct hmap *lr_ports,
>>>>>>>      const struct nbrec_logical_router_static_route *route,
>>>>>>> bool
>>>>>>> is_ipv4,
>>>>>>>      const char **p_lrp_addr_s, struct ovn_port
>>>>>>> **p_out_port)
>>>>>>>  {
>>>>>>>      const char *lrp_addr_s = NULL;
>>>>>>>      struct ovn_port *out_port = NULL;
>>>>>>>      if (route->output_port) {
>>>>>>> +        /* XXX: we should be able to use &od->ports
>>>>>>> instead of
>>>>>>> lr_ports. */
>>>>>>>          out_port = ovn_port_find(lr_ports, route-
>>>>>>>> output_port);
>>>>>>>          if (!out_port) {
>>>>>>>              static struct vlog_rate_limit rl =
>>>>>>> VLOG_RATE_LIMIT_INIT(5, 1);
>>>>>>> diff --git a/northd/northd.h b/northd/northd.h
>>>>>>> index 9457a7be6..98a17d733 100644
>>>>>>> --- a/northd/northd.h
>>>>>>> +++ b/northd/northd.h
>>>>>>> @@ -367,6 +367,8 @@ struct ovn_datapath {
>>>>>>>  
>>>>>>>      /* router datapath has a logical port with redirect-
>>>>>>> type set
>>>>>>> to bridged. */
>>>>>>>      bool redirect_bridged;
>>>>>>> +    /* nbr has the option "dynamic-routing" set to true.
>>>>>>> */
>>>>>>> +    bool dynamic_routing;
>>>>>>>  
>>>>>>>      struct ovn_port **localnet_ports;
>>>>>>>      size_t n_localnet_ports;
>>>>>>> @@ -714,7 +716,7 @@ struct parsed_route {
>>>>>>>      const struct nbrec_logical_router_static_route *route;
>>>>>>>      bool ecmp_symmetric_reply;
>>>>>>>      bool is_discard_route;
>>>>>>> -    const struct nbrec_logical_router *nbr;
>>>>>>> +    const struct ovn_datapath *od;
>>>>>>>      bool stale;
>>>>>>>      struct sset ecmp_selection_fields;
>>>>>>>      enum route_source source;
>>>>>>> @@ -745,7 +747,7 @@ void northd_indices_create(struct
>>>>>>> northd_data
>>>>>>> *data,
>>>>>>>  
>>>>>>>  void route_policies_init(struct route_policies_data *);
>>>>>>>  void route_policies_destroy(struct route_policies_data *);
>>>>>>> -void build_parsed_routes(struct ovn_datapath *, const
>>>>>>> struct
>>>>>>> hmap *,
>>>>>>> +void build_parsed_routes(const struct ovn_datapath *,
>>>>>>> const
>>>>>>> struct hmap *,
>>>>>>>                           const struct hmap *, struct hmap
>>>>>>> *,
>>>>>>> struct simap *,
>>>>>>>                           struct hmap *);
>>>>>>>  uint32_t get_route_table_id(struct simap *, const char *);
>>>>>>> diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
>>>>>>> index aa4c57663..17d6145be 100644
>>>>>>> --- a/northd/ovn-northd.c
>>>>>>> +++ b/northd/ovn-northd.c
>>>>>>> @@ -941,6 +941,10 @@ main(int argc, char *argv[])
>>>>>>>          ovsdb_idl_omit_alert(ovnsb_idl_loop.idl,
>>>>>>>                              
>>>>>>> &sbrec_logical_dp_group_columns[i]);
>>>>>>>      }
>>>>>>> +    for (size_t i = 0; i <
>>>>>>> SBREC_ADVERTISED_ROUTE_N_COLUMNS;
>>>>>>> i++) {
>>>>>>> +        ovsdb_idl_omit_alert(ovnsb_idl_loop.idl,
>>>>>>> +  ��                         
>>>>>>> &sbrec_advertised_route_columns[i]);
>>>>>>> +    }
>>>>>>>  
>>>>>>>      unixctl_command_register("sb-connection-status", "",
>>>>>>> 0, 0,
>>>>>>>                               ovn_conn_show,
>>>>>>> ovnsb_idl_loop.idl);
>>>>>>> @@ -968,6 +972,7 @@ main(int argc, char *argv[])
>>>>>>>      stopwatch_create(LR_NAT_RUN_STOPWATCH_NAME, SW_MS);
>>>>>>>      stopwatch_create(LR_STATEFUL_RUN_STOPWATCH_NAME,
>>>>>>> SW_MS);
>>>>>>>      stopwatch_create(LS_STATEFUL_RUN_STOPWATCH_NAME,
>>>>>>> SW_MS);
>>>>>>> +   
>>>>>>> stopwatch_create(ADVERTISED_ROUTE_SYNC_RUN_STOPWATCH_NAME,
>>>>>>> SW_MS);
>>>>>>>  
>>>>>>>      /* Initialize incremental processing engine for ovn-
>>>>>>> northd
>>>>>>> */
>>>>>>>      inc_proc_northd_init(&ovnnb_idl_loop,
>>>>>>> &ovnsb_idl_loop);
>>>>>>> diff --git a/ovn-nb.xml b/ovn-nb.xml
>>>>>>> index 24ef12f3b..718d0126e 100644
>>>>>>> --- a/ovn-nb.xml
>>>>>>> +++ b/ovn-nb.xml
>>>>>>> @@ -2946,6 +2946,19 @@ or
>>>>>>>          option is not present the limit is not set and the
>>>>>>> zone
>>>>>>> limit is
>>>>>>>          derived from OvS default datapath limit.
>>>>>>>        </column>
>>>>>>> +
>>>>>>> +      <column name="options" key="dynamic-routing"
>>>>>>> type='{"type": "boolean"}'>
>>>>>>> +        If set to <code>true</code> then this <ref
>>>>>>> table="Logical_Router"/>
>>>>>>> +        can participate in dynamic routing with components
>>>>>>> outside of OVN.
>>>>>>> +
>>>>>>> +        It will synchronize all routes to the soutbound
>>>>>>> +        <ref table="Route" db="OVN_SB"/> table that are
>>>>>>> relevant
>>>>>>> for the
>>>>>>> +        router. This includes:
>>>>>>> +        * all "connected" routes implicitly created by
>>>>>>> networks
>>>>>>> associated with
>>>>>>> +          this Logical Router
>>>>>>> +        * all <ref table="Logical_Router_Static_Route"/>
>>>>>>> that
>>>>>>> are applied to
>>>>>>> +          this Logical Router
>>>>>>
>>>>>> This doesn't render that nicely.  The resulting man page is
>>>>>> content
>>>>>> is
>>>>>> something like:
>>>>>>
>>>>>>               includes:  * all "connected" routes implicitly
>>>>>> created by
>>>>>>               networks associated with this Logical Router *
>>>>>> all
>>>>>>               Logical_Router_Static_Route that are applied to
>>>>>> this
>>>>>>               Logical Router
>>>>>>
>>>>>> If you change this block to:
>>>>>>
>>>>>>         <ul>
>>>>>>           <li>
>>>>>>             all "connected" routes implicitly created by
>>>>>> networks
>>>>>>             associated with this Logical Router
>>>>>>           </li>
>>>>>>           <li>
>>>>>>             all <ref table="Logical_Router_Static_Route"/>
>>>>>> that are
>>>>>>             applied to this Logical Router
>>>>>>           </li>
>>>>>>         </ul>
>>>>>>
>>>>>> The generated man page looks slightly better IMO:
>>>>>>
>>>>>>               table that are relevant for the router. This
>>>>>> includes:
>>>>>>
>>>>>>               •      all  "connected" routes implicitly
>>>>>> created by
>>>>>>                      networks associated with this Logical
>>>>>> Router
>>>>>>
>>>>>>               •      all Logical_Router_Static_Route that are
>>>>>> applied
>>>>>>                      to this Logical Router
>>>>>>
>>>>>>> +      </column>
>>>>>>>      </group>
>>>>>>>  
>>>>>>>      <group title="Common Columns">
>>>>>>> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
>>>>>>> index edfd5764b..30ea23502 100644
>>>>>>> --- a/tests/ovn-northd.at
>>>>>>> +++ b/tests/ovn-northd.at
>>>>>>> @@ -14419,3 +14419,140 @@ AT_CHECK([ovn-sbctl lflow-list S1
>>>>>>> |
>>>>>>> grep ls_out_acl_action | grep priority=500 |
>>>>>>>  
>>>>>>>  AT_CLEANUP
>>>>>>>  ])
>>>>>>> +
>>>>>>> +OVN_FOR_EACH_NORTHD_NO_HV([
>>>>>>> +AT_SETUP([dynamic-routing - sync to sb])
>>>>>>> +AT_KEYWORDS([dynamic-routing])
>>>>>>> +ovn_start
>>>>>>> +
>>>>>>> +# adding a router - still nothing here
>>>>>>
>>>>>> Nit: comments should be sentences.
>>>>>>
>>>>>>> +check ovn-nbctl lr-add lr0
>>>>>>> +check ovn-nbctl --wait=sb set Logical_Router lr0
>>>>>>> option:dynamic-
>>>>>>> routing=true
>>>>>>> +check_row_count Advertised_Route 0
>>>>>>> +datapath=$(fetch_column datapath_binding _uuid
>>>>>>> external_ids:name=lr0)
>>>>>>> +
>>>>>>> +# adding a LRP adds a route entry for the associated
>>>>>>> network
>>>>>>> +check ovn-nbctl --wait=sb lrp-add lr0 lr0-sw0
>>>>>>> 00:00:00:00:ff:01
>>>>>>> 10.0.0.1/24
>>>>>>> +pb=$(fetch_column port_binding _uuid logical_port=lr0-sw0)
>>>>>>> +check_row_count Advertised_Route 1
>>>>>>> +check_column 10.0.0.0/24 Advertised_Route ip_prefix
>>>>>>> datapath=$datapath logical_port=$pb
>>>>>>> +
>>>>>>> +# adding a second LRP adds an additional route entry
>>>>>>> +check ovn-nbctl --wait=sb lrp-add lr0 lr0-sw1
>>>>>>> 00:00:00:00:ff:02
>>>>>>> 10.0.1.1/24
>>>>>>> +pb2=$(fetch_column port_binding _uuid logical_port=lr0-
>>>>>>> sw1)
>>>>>>> +check_row_count Advertised_Route 2
>>>>>>> +check_column 10.0.0.0/24 Advertised_Route ip_prefix
>>>>>>> datapath=$datapath logical_port=$pb
>>>>>>> +check_column 10.0.1.0/24 Advertised_Route ip_prefix
>>>>>>> datapath=$datapath logical_port=$pb2
>>>>>>> +
>>>>>>> +# adding a static route adds an additional entry
>>>>>>> +check ovn-nbctl --wait=sb lr-route-add lr0 192.168.0.0/24
>>>>>>> 10.0.0.10
>>>>>>> +check_row_count Advertised_Route 3
>>>>>>> +check_row_count Advertised_Route 2 logical_port=$pb
>>>>>>> +check_row_count Advertised_Route 1 logical_port=$pb
>>>>>>> ip_prefix=192.168.0.0/24
>>>>>>> +
>>>>>>> +# adding an ipv6 LRP adds an addition route entry
>>>>>>> +check ovn-nbctl --wait=sb lrp-add lr0 lr0-sw2
>>>>>>> 00:00:00:00:ff:03
>>>>>>> 2001:db8::1/64 fe80::1/64
>>>>>>> +pb3=$(fetch_column port_binding _uuid logical_port=lr0-
>>>>>>> sw2)
>>>>>>> +check_row_count Advertised_Route 4
>>>>>>> +check_row_count Advertised_Route 2 logical_port=$pb
>>>>>>> +check_row_count Advertised_Route 1 logical_port=$pb
>>>>>>> ip_prefix=192.168.0.0/24
>>>>>>> +check_column 10.0.1.0/24 Advertised_Route ip_prefix
>>>>>>> datapath=$datapath logical_port=$pb2
>>>>>>> +check_column 2001:db8::/64 Advertised_Route ip_prefix
>>>>>>> datapath=$datapath logical_port=$pb3
>>>>>>> +
>>>>>>> +# removing the option:dynamic-routing removes all routes
>>>>>>> +check ovn-nbctl --wait=sb remove Logical_Router lr0 option
>>>>>>> dynamic-routing
>>>>>>> +check_row_count Advertised_Route 0
>>>>>>> +
>>>>>>> +# and setting it again adds them again
>>>>>>> +check ovn-nbctl --wait=sb set Logical_Router lr0
>>>>>>> option:dynamic-
>>>>>>> routing=true
>>>>>>> +check_row_count Advertised_Route 4
>>>>>>> +
>>>>>>> +# removing the lrp used for the static route removes both
>>>>>>> route
>>>>>>> entries
>>>>>>> +check ovn-nbctl --wait=sb lrp-del lr0-sw0
>>>>>>> +check_row_count Advertised_Route 2
>>>>>>> +check_row_count Advertised_Route 1 logical_port=$pb2
>>>>>>> +check_row_count Advertised_Route 1 logical_port=$pb3
>>>>>>> +
>>>>>>> +# removing the lr will remove all routes
>>>>>>> +check ovn-nbctl --wait=sb lr-del lr0
>>>>>>> +check_row_count Advertised_Route 0
>>>>>>> +
>>>>>>> +AT_CLEANUP
>>>>>>> +])
>>>>>>> +
>>>>>>> +OVN_FOR_EACH_NORTHD_NO_HV([
>>>>>>> +AT_SETUP([dynamic-routing incremental processing])
>>>>>>> +AT_KEYWORDS([dynamic-routing])
>>>>>>> +ovn_start
>>>>>>> +
>>>>>>> +# Test I-P for dynamic-routing.
>>>>>>> +# Presently ovn-northd has no I-P for Advertised_Route.
>>>>>>> +# Wait for sb to be connected before clearing stats.
>>>>>>> +check ovn-nbctl --wait=sb sync
>>>>>>> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-
>>>>>>> stats
>>>>>>> +check ovn-nbctl lr-add lr0
>>>>>>> +check ovn-nbctl --wait=sb set Logical_Router lr0
>>>>>>> option:dynamic-
>>>>>>> routing=true
>>>>>>> +
>>>>>>> +check_engine_stats northd recompute nocompute
>>>>>>> +check_engine_stats routes recompute nocompute
>>>>>>> +check_engine_stats advertised_route_sync recompute
>>>>>>> nocompute
>>>>>>> +CHECK_NO_CHANGE_AFTER_RECOMPUTE(1)
>>>>>>
>>>>>> CHECK_NO_CHANGE_AFTER_RECOMPUTE(1) also waits for logical
>>>>>> ports to
>>>>>> be
>>>>>> up.  We have no ports in the configuration at this point. 
>>>>>> This can
>>>>>> be a
>>>>>> plain:
>>>>>>
>>>>>> CHECK_NO_CHANGE_AFTER_RECOMPUTE
>>>>>>
>>>>>>> +
>>>>>>> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-
>>>>>>> stats
>>>>>>> +check ovn-nbctl --wait=sb lrp-add lr0 lr0-sw0
>>>>>>> 00:00:00:00:ff:01
>>>>>>> 10.0.0.1/24
>>>>>>> +check_engine_stats northd recompute compute
>>>>>>> +check_engine_stats routes recompute nocompute
>>>>>>> +check_engine_stats advertised_route_sync recompute
>>>>>>> nocompute
>>>>>>> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
>>>>>>> +
>>>>>>> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-
>>>>>>> stats
>>>>>>> +check ovn-nbctl --wait=sb lrp-add lr0 lr0-sw1
>>>>>>> 00:00:00:00:ff:02
>>>>>>> 10.0.1.1/24
>>>>>>> +check_engine_stats northd recompute compute
>>>>>>> +check_engine_stats routes recompute nocompute
>>>>>>> +check_engine_stats advertised_route_sync recompute
>>>>>>> nocompute
>>>>>>> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
>>>>>>> +
>>>>>>> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-
>>>>>>> stats
>>>>>>> +check ovn-nbctl --wait=sb lr-route-add lr0 192.168.0.0/24
>>>>>>> 10.0.0.10
>>>>>>> +check_engine_stats northd recompute nocompute
>>>>>>> +check_engine_stats routes recompute nocompute
>>>>>>> +check_engine_stats advertised_route_sync recompute
>>>>>>> nocompute
>>>>>>> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
>>>>>>> +
>>>>>>> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-
>>>>>>> stats
>>>>>>> +check ovn-nbctl --wait=sb lrp-add lr0 lr0-sw2
>>>>>>> 00:00:00:00:ff:03
>>>>>>> 2001:db8::1/64 fe80::1/64
>>>>>>> +check_engine_stats northd recompute compute
>>>>>>> +check_engine_stats routes recompute nocompute
>>>>>>> +check_engine_stats advertised_route_sync recompute
>>>>>>> nocompute
>>>>>>> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
>>>>>>> +
>>>>>>> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-
>>>>>>> stats
>>>>>>> +check ovn-nbctl --wait=sb remove Logical_Router lr0 option
>>>>>>> dynamic-routing
>>>>>>> +check_engine_stats northd recompute nocompute
>>>>>>> +check_engine_stats routes recompute nocompute
>>>>>>> +check_engine_stats advertised_route_sync recompute
>>>>>>> nocompute
>>>>>>> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
>>>>>>> +
>>>>>>> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-
>>>>>>> stats
>>>>>>> +check ovn-nbctl --wait=sb set Logical_Router lr0
>>>>>>> option:dynamic-
>>>>>>> routing=true
>>>>>>> +check_engine_stats northd recompute nocompute
>>>>>>> +check_engine_stats routes recompute nocompute
>>>>>>> +check_engine_stats advertised_route_sync recompute
>>>>>>> nocompute
>>>>>>> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
>>>>>>> +
>>>>>>> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-
>>>>>>> stats
>>>>>>> +check ovn-nbctl --wait=sb lrp-del lr0-sw0
>>>>>>> +check_engine_stats northd recompute compute
>>>>>>> +check_engine_stats routes recompute nocompute
>>>>>>> +check_engine_stats advertised_route_sync recompute
>>>>>>> nocompute
>>>>>>> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
>>>>>>> +
>>>>>>> +check as northd ovn-appctl -t ovn-northd inc-engine/clear-
>>>>>>> stats
>>>>>>> +check ovn-nbctl --wait=sb lr-del lr0
>>>>>>> +check_engine_stats northd recompute nocompute
>>>>>>> +check_engine_stats routes recompute nocompute
>>>>>>> +check_engine_stats advertised_route_sync recompute
>>>>>>> nocompute
>>>>>>> +CHECK_NO_CHANGE_AFTER_RECOMPUTE
>>>>>>> +
>>>>>>> +AT_CLEANUP
>>>>>>> +])
>>>>>>
>>>>>> Regards,
>>>>>> Dumitru
>>>>>>
>>>>>
>>>>
>>>
> 

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to