On 2/11/25 9:36 AM, Felix Huettner via dev wrote:
> This engine node determines the routes that the ovn-controller should
> export.
> 
> Acked-by: Dumitru Ceara <dce...@redhat.com>
> Co-Authored-By: Frode Nordahl <fnord...@ubuntu.com>
> Signed-off-by: Frode Nordahl <fnord...@ubuntu.com>
> Signed-off-by: Felix Huettner <felix.huettner@stackit.cloud>
> ---

Hi Felix, Frode,

I applied this patch to main with the following minor style changes:

diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
index 4f5da8b653..30872cd3bc 100644
--- a/controller/ovn-controller.c
+++ b/controller/ovn-controller.c
@@ -4947,12 +4947,14 @@ pflow_lflow_output_sb_chassis_handler(struct 
engine_node *node,
 }
 
 struct ed_type_route {
-    struct ovsdb_idl *ovnsb_idl;
     /* Contains struct tracked_datapath entries for local datapaths subject to
      * route exchange. */
     struct hmap tracked_route_datapaths;
+
     /* Contains struct advertise_datapath_entry */
     struct hmap announce_routes;
+
+    struct ovsdb_idl *ovnsb_idl;
 };
 
 static void
@@ -5088,6 +5090,7 @@ route_sb_port_binding_data_handler(struct engine_node 
*node, void *data)
         }
 
     }
+
     return true;
 }
 
diff --git a/controller/route.c b/controller/route.c
index dccebe35f8..2a57b0137b 100644
--- a/controller/route.c
+++ b/controller/route.c
@@ -121,17 +121,19 @@ route_run(struct route_ctx_in *r_ctx_in,
         for (size_t i = 0; i < ld->n_peer_ports; i++) {
             const struct sbrec_port_binding *local_peer
                 = ld->peer_ports[i].local;
-            const struct sbrec_port_binding *repb = find_route_exchange_pb(
-                r_ctx_in->sbrec_port_binding_by_name,
-                r_ctx_in->chassis,
-                r_ctx_in->active_tunnels,
-                local_peer);
+            const struct sbrec_port_binding *repb =
+                find_route_exchange_pb(r_ctx_in->sbrec_port_binding_by_name,
+                                       r_ctx_in->chassis,
+                                       r_ctx_in->active_tunnels,
+                                       local_peer);
             if (!repb) {
                 continue;
             }
 
-            ad->maintain_vrf |= smap_get_bool(
-                &repb->options, "dynamic-routing-maintain-vrf", false);
+            ad->maintain_vrf |=
+                smap_get_bool(&repb->options,
+                              "dynamic-routing-maintain-vrf",
+                              false);
             sset_add(&ad->bound_ports, local_peer->logical_port);
         }
 
diff --git a/controller/route.h b/controller/route.h
index b38f62e5af..2a42912607 100644
--- a/controller/route.h
+++ b/controller/route.h
@@ -39,12 +39,14 @@ struct route_ctx_in {
 
 struct route_ctx_out {
     struct hmap *tracked_re_datapaths;
+
     /* Contains struct advertise_datapath_entry */
     struct hmap *announce_routes;
 };
 
 struct advertise_datapath_entry {
     struct hmap_node node;
+
     const struct sbrec_datapath_binding *db;
     bool maintain_vrf;
     struct hmap routes;
--

Regards,
Dumitru

> v6->v7:
>   * addressed review comments
> v5->v6:
>   * addressed review comments
> v3->v4:
>   - addressed review comments.
> 
>  TODO.rst                    |   6 ++
>  controller/automake.mk      |   4 +-
>  controller/local_data.c     |   7 +-
>  controller/local_data.h     |   1 +
>  controller/ovn-controller.c | 196 +++++++++++++++++++++++++++++++++++-
>  controller/route.c          | 181 +++++++++++++++++++++++++++++++++
>  controller/route.h          |  67 ++++++++++++
>  tests/automake.mk           |   1 +
>  8 files changed, 460 insertions(+), 3 deletions(-)
>  create mode 100644 controller/route.c
>  create mode 100644 controller/route.h
> 
> diff --git a/TODO.rst b/TODO.rst
> index 0d3acbe81..5f26c0017 100644
> --- a/TODO.rst
> +++ b/TODO.rst
> @@ -97,6 +97,7 @@ OVN To-do List
>  * ovn-controller Incremental processing
>  
>    * Implement I-P for datapath groups.
> +  * Implement I-P for route exchange relevant ports.
>  
>  * ovn-northd parallel logical flow processing
>  
> @@ -144,3 +145,8 @@ OVN To-do List
>  
>    * Add incremental processing for northd when the Learned_Route table 
> changes.
>      Currently en_lflow is fully recomputed whenever such a change happens.
> +
> +  * The ovn-controller currently loads all Advertised_Route entries on 
> startup.
> +    This is to prevent deleting our routes on restart. If we defer updating
> +    routes until we are sure to have loaded all necessary Advertised_Routes
> +    this could be changed.
> diff --git a/controller/automake.mk b/controller/automake.mk
> index cba5d8365..452dd0c6d 100644
> --- a/controller/automake.mk
> +++ b/controller/automake.mk
> @@ -55,7 +55,9 @@ controller_ovn_controller_SOURCES = \
>       controller/ovn-dns.c \
>       controller/ovn-dns.h \
>       controller/ecmp-next-hop-monitor.h \
> -     controller/ecmp-next-hop-monitor.c
> +     controller/ecmp-next-hop-monitor.c \
> +     controller/route.h \
> +     controller/route.c
>  
>  controller_ovn_controller_LDADD = lib/libovn.la 
> $(OVS_LIBDIR)/libopenvswitch.la
>  man_MANS += controller/ovn-controller.8
> diff --git a/controller/local_data.c b/controller/local_data.c
> index 69a1b775f..4aee39d6b 100644
> --- a/controller/local_data.c
> +++ b/controller/local_data.c
> @@ -414,14 +414,19 @@ tracked_datapath_lport_add(const struct 
> sbrec_port_binding *pb,
>  }
>  
>  void
> -tracked_datapaths_destroy(struct hmap *tracked_datapaths)
> +tracked_datapaths_clear(struct hmap *tracked_datapaths)
>  {
>      struct tracked_datapath *t_dp;
>      HMAP_FOR_EACH_POP (t_dp, node, tracked_datapaths) {
>          shash_destroy_free_data(&t_dp->lports);
>          free(t_dp);
>      }
> +}
>  
> +void
> +tracked_datapaths_destroy(struct hmap *tracked_datapaths)
> +{
> +    tracked_datapaths_clear(tracked_datapaths);
>      hmap_destroy(tracked_datapaths);
>  }
>  
> diff --git a/controller/local_data.h b/controller/local_data.h
> index ab8e789a5..1d60dada8 100644
> --- a/controller/local_data.h
> +++ b/controller/local_data.h
> @@ -131,6 +131,7 @@ struct tracked_datapath *tracked_datapath_find(
>  void tracked_datapath_lport_add(const struct sbrec_port_binding *,
>                                  enum en_tracked_resource_type,
>                                  struct hmap *tracked_datapaths);
> +void tracked_datapaths_clear(struct hmap *tracked_datapaths);
>  void tracked_datapaths_destroy(struct hmap *tracked_datapaths);
>  
>  /* Maps from a chassis to the OpenFlow port number of the tunnel that can be
> diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
> index da942abaa..4f5da8b65 100644
> --- a/controller/ovn-controller.c
> +++ b/controller/ovn-controller.c
> @@ -89,6 +89,7 @@
>  #include "ct-zone.h"
>  #include "ovn-dns.h"
>  #include "acl-ids.h"
> +#include "route.h"
>  
>  VLOG_DEFINE_THIS_MODULE(main);
>  
> @@ -234,6 +235,8 @@ update_sb_monitors(struct ovsdb_idl *ovnsb_idl,
>       *
>       * Monitor ECMP_Nexthop for local datapaths.
>       *
> +     * Monitor Advertised_Route for local datapaths.
> +     *
>       * We always monitor patch ports because they allow us to see the 
> linkages
>       * between related logical datapaths.  That way, when we know that we 
> have
>       * a VIF on a particular logical switch, we immediately know to monitor 
> all
> @@ -251,6 +254,7 @@ update_sb_monitors(struct ovsdb_idl *ovnsb_idl,
>      struct ovsdb_idl_condition chprv = OVSDB_IDL_CONDITION_INIT(&chprv);
>      struct ovsdb_idl_condition tv = OVSDB_IDL_CONDITION_INIT(&tv);
>      struct ovsdb_idl_condition nh = OVSDB_IDL_CONDITION_INIT(&nh);
> +    struct ovsdb_idl_condition ar = OVSDB_IDL_CONDITION_INIT(&ar);
>  
>      /* Always monitor all logical datapath groups. Otherwise, DPG updates may
>       * be received *after* the lflows using it are seen by ovn-controller.
> @@ -271,6 +275,7 @@ update_sb_monitors(struct ovsdb_idl *ovnsb_idl,
>          ovsdb_idl_condition_add_clause_true(&chprv);
>          ovsdb_idl_condition_add_clause_true(&tv);
>          ovsdb_idl_condition_add_clause_true(&nh);
> +        ovsdb_idl_condition_add_clause_true(&ar);
>          goto out;
>      }
>  
> @@ -324,6 +329,11 @@ update_sb_monitors(struct ovsdb_idl *ovnsb_idl,
>           */
>          sbrec_port_binding_add_clause_type(&pb, OVSDB_F_EQ, "l2gateway");
>          sbrec_port_binding_add_clause_type(&pb, OVSDB_F_EQ, "l3gateway");
> +
> +        /* Monitor all advertised routes during startup. Otherwise, once we
> +         * claim a port on startup we do not yet know the routes to advertise
> +         * and might wrongly delete already installed ones. */
> +        ovsdb_idl_condition_add_clause_true(&ar);
>      }
>  
>      if (local_ifaces) {
> @@ -360,6 +370,7 @@ update_sb_monitors(struct ovsdb_idl *ovnsb_idl,
>              sbrec_ip_multicast_add_clause_datapath(&ip_mcast, OVSDB_F_EQ,
>                                                     uuid);
>              sbrec_ecmp_nexthop_add_clause_datapath(&nh, OVSDB_F_EQ, uuid);
> +            sbrec_advertised_route_add_clause_datapath(&ar, OVSDB_F_EQ, 
> uuid);
>          }
>  
>          /* Datapath groups are immutable, which means a new group record is
> @@ -388,6 +399,7 @@ out:;
>          sb_table_set_req_mon_condition(ovnsb_idl, chassis_private, &chprv),
>          sb_table_set_opt_mon_condition(ovnsb_idl, chassis_template_var, &tv),
>          sb_table_set_opt_mon_condition(ovnsb_idl, ecmp_nexthop, &nh),
> +        sb_table_set_opt_mon_condition(ovnsb_idl, advertised_route, &ar),
>      };
>  
>      unsigned int expected_cond_seqno = 0;
> @@ -408,6 +420,7 @@ out:;
>      ovsdb_idl_condition_destroy(&chprv);
>      ovsdb_idl_condition_destroy(&tv);
>      ovsdb_idl_condition_destroy(&nh);
> +    ovsdb_idl_condition_destroy(&ar);
>      return expected_cond_seqno;
>  }
>  
> @@ -949,7 +962,8 @@ ctrl_register_ovs_idl(struct ovsdb_idl *ovs_idl)
>      SB_NODE(meter, "meter") \
>      SB_NODE(static_mac_binding, "static_mac_binding") \
>      SB_NODE(chassis_template_var, "chassis_template_var") \
> -    SB_NODE(acl_id, "acl_id")
> +    SB_NODE(acl_id, "acl_id") \
> +    SB_NODE(advertised_route, "advertised_route")
>  
>  enum sb_engine_node {
>  #define SB_NODE(NAME, NAME_STR) SB_##NAME,
> @@ -4932,6 +4946,173 @@ pflow_lflow_output_sb_chassis_handler(struct 
> engine_node *node,
>      return true;
>  }
>  
> +struct ed_type_route {
> +    struct ovsdb_idl *ovnsb_idl;
> +    /* Contains struct tracked_datapath entries for local datapaths subject 
> to
> +     * route exchange. */
> +    struct hmap tracked_route_datapaths;
> +    /* Contains struct advertise_datapath_entry */
> +    struct hmap announce_routes;
> +};
> +
> +static void
> +en_route_run(struct engine_node *node, void *data)
> +{
> +    struct ed_type_route *re_data = data;
> +
> +    const struct ovsrec_open_vswitch_table *ovs_table =
> +        EN_OVSDB_GET(engine_get_input("OVS_open_vswitch", node));
> +    const char *chassis_id = get_ovs_chassis_id(ovs_table);
> +    ovs_assert(chassis_id);
> +
> +    struct ovsdb_idl_index *sbrec_chassis_by_name =
> +        engine_ovsdb_node_get_index(
> +                engine_get_input("SB_chassis", node),
> +                "name");
> +    const struct sbrec_chassis *chassis
> +        = chassis_lookup_by_name(sbrec_chassis_by_name, chassis_id);
> +    ovs_assert(chassis);
> +
> +    struct ovsdb_idl_index *sbrec_port_binding_by_name =
> +        engine_ovsdb_node_get_index(
> +                engine_get_input("SB_port_binding", node),
> +                "name");
> +    struct ed_type_runtime_data *rt_data =
> +        engine_get_input_data("runtime_data", node);
> +
> +    const struct sbrec_advertised_route_table *advertised_route_table =
> +        EN_OVSDB_GET(engine_get_input("SB_advertised_route", node));
> +
> +    struct route_ctx_in r_ctx_in = {
> +        .advertised_route_table = advertised_route_table,
> +        .sbrec_port_binding_by_name = sbrec_port_binding_by_name,
> +        .chassis = chassis,
> +        .active_tunnels = &rt_data->active_tunnels,
> +        .local_datapaths = &rt_data->local_datapaths,
> +        .local_lports = &rt_data->local_lports,
> +    };
> +
> +    struct route_ctx_out r_ctx_out = {
> +        .tracked_re_datapaths = &re_data->tracked_route_datapaths,
> +        .announce_routes = &re_data->announce_routes,
> +    };
> +
> +    route_cleanup(&re_data->announce_routes);
> +    tracked_datapaths_clear(r_ctx_out.tracked_re_datapaths);
> +    route_run(&r_ctx_in, &r_ctx_out);
> +    engine_set_node_state(node, EN_UPDATED);
> +}
> +
> +
> +static void *
> +en_route_init(struct engine_node *node OVS_UNUSED,
> +              struct engine_arg *arg)
> +{
> +    struct ed_type_route *data = xzalloc(sizeof *data);
> +
> +    hmap_init(&data->tracked_route_datapaths);
> +    hmap_init(&data->announce_routes);
> +    data->ovnsb_idl = arg->sb_idl;
> +
> +    return data;
> +}
> +
> +static void
> +en_route_cleanup(void *data)
> +{
> +    struct ed_type_route *re_data = data;
> +
> +    tracked_datapaths_destroy(&re_data->tracked_route_datapaths);
> +    route_cleanup(&re_data->announce_routes);
> +    hmap_destroy(&re_data->announce_routes);
> +}
> +
> +static bool
> +route_runtime_data_handler(struct engine_node *node, void *data)
> +{
> +    struct ed_type_route *re_data = data;
> +    struct ed_type_runtime_data *rt_data =
> +        engine_get_input_data("runtime_data", node);
> +
> +    if (!rt_data->tracked) {
> +        return false;
> +    }
> +
> +    struct tracked_datapath *t_dp;
> +    HMAP_FOR_EACH (t_dp, node, &rt_data->tracked_dp_bindings) {
> +        struct tracked_datapath *re_t_dp =
> +            tracked_datapath_find(&re_data->tracked_route_datapaths, 
> t_dp->dp);
> +
> +        if (re_t_dp) {
> +            /* XXX: Until we get I-P support for route exchange we need to
> +             * request recompute. */
> +            return false;
> +        }
> +
> +        struct shash_node *shash_node;
> +        SHASH_FOR_EACH (shash_node, &t_dp->lports) {
> +            struct tracked_lport *lport = shash_node->data;
> +            if (route_exchange_relevant_port(lport->pb)) {
> +                /* XXX: Until we get I-P support for route exchange we need 
> to
> +                 * request recompute. */
> +                return false;
> +            }
> +        }
> +    }
> +
> +    return true;
> +}
> +
> +static bool
> +route_sb_port_binding_data_handler(struct engine_node *node, void *data)
> +{
> +    struct ed_type_route *re_data = data;
> +    const struct sbrec_port_binding_table *pb_table =
> +        EN_OVSDB_GET(engine_get_input("SB_port_binding", node));
> +
> +    const struct sbrec_port_binding *sbrec_pb;
> +    SBREC_PORT_BINDING_TABLE_FOR_EACH_TRACKED (sbrec_pb, pb_table) {
> +        struct tracked_datapath *re_t_dp =
> +            tracked_datapath_find(&re_data->tracked_route_datapaths,
> +                                  sbrec_pb->datapath);
> +        if (re_t_dp) {
> +            /* XXX: Until we get I-P support for route exchange we need to
> +             * request recompute. */
> +            return false;
> +        }
> +
> +        if (route_exchange_relevant_port(sbrec_pb)) {
> +            /* XXX: Until we get I-P support for route exchange we need to
> +             * request recompute. */
> +            return false;
> +        }
> +
> +    }
> +    return true;
> +}
> +
> +static bool
> +route_sb_advertised_route_data_handler(struct engine_node *node, void *data)
> +{
> +    struct ed_type_route *re_data = data;
> +    const struct sbrec_advertised_route_table *advertised_route_table =
> +        EN_OVSDB_GET(engine_get_input("SB_advertised_route", node));
> +
> +    const struct sbrec_advertised_route *sbrec_route;
> +    SBREC_ADVERTISED_ROUTE_TABLE_FOR_EACH_TRACKED (sbrec_route,
> +                                                   advertised_route_table) {
> +        struct tracked_datapath *re_t_dp =
> +            tracked_datapath_find(&re_data->tracked_route_datapaths,
> +                                  sbrec_route->datapath);
> +        if (re_t_dp) {
> +            /* XXX: Until we get I-P support for route exchange we need to
> +             * request recompute. */
> +            return false;
> +        }
> +    }
> +    return true;
> +}
> +
>  /* Returns false if the northd internal version stored in SB_Global
>   * and ovn-controller internal version don't match.
>   */
> @@ -5231,6 +5412,7 @@ main(int argc, char *argv[])
>      ENGINE_NODE(dns_cache, "dns_cache");
>      ENGINE_NODE(acl_id, "acl_id");
>      en_acl_id.is_valid = en_acl_id_is_valid;
> +    ENGINE_NODE(route, "route");
>  
>  #define SB_NODE(NAME, NAME_STR) ENGINE_NODE_SB(NAME, NAME_STR);
>      SB_NODES
> @@ -5253,6 +5435,15 @@ main(int argc, char *argv[])
>      engine_add_input(&en_lb_data, &en_runtime_data,
>                       lb_data_runtime_data_handler);
>  
> +    engine_add_input(&en_route, &en_ovs_open_vswitch, NULL);
> +    engine_add_input(&en_route, &en_sb_chassis, NULL);
> +    engine_add_input(&en_route, &en_sb_port_binding,
> +                     route_sb_port_binding_data_handler);
> +    engine_add_input(&en_route, &en_runtime_data,
> +                     route_runtime_data_handler);
> +    engine_add_input(&en_route, &en_sb_advertised_route,
> +                     route_sb_advertised_route_data_handler);
> +
>      engine_add_input(&en_addr_sets, &en_sb_address_set,
>                       addr_sets_sb_address_set_handler);
>      engine_add_input(&en_port_groups, &en_sb_port_group,
> @@ -5438,6 +5629,9 @@ main(int argc, char *argv[])
>                       controller_output_mac_cache_handler);
>      engine_add_input(&en_controller_output, &en_bfd_chassis,
>                       controller_output_bfd_chassis_handler);
> +    /* This is just temporary until the route output is actually used. */
> +    engine_add_input(&en_controller_output, &en_route,
> +                     controller_output_bfd_chassis_handler);
>  
>      engine_add_input(&en_acl_id, &en_sb_acl_id, NULL);
>      engine_add_input(&en_controller_output, &en_acl_id,
> diff --git a/controller/route.c b/controller/route.c
> new file mode 100644
> index 000000000..dccebe35f
> --- /dev/null
> +++ b/controller/route.c
> @@ -0,0 +1,181 @@
> +/*
> + * Copyright (c) 2025, Canonical, Ltd.
> + * Copyright (c) 2025, STACKIT GmbH & Co. KG
> + *
> + * 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 <net/if.h>
> +
> +#include "openvswitch/hmap.h"
> +#include "openvswitch/vlog.h"
> +
> +#include "lib/ovn-sb-idl.h"
> +
> +#include "binding.h"
> +#include "ha-chassis.h"
> +#include "local_data.h"
> +#include "route.h"
> +
> +VLOG_DEFINE_THIS_MODULE(exchange);
> +static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
> +
> +bool
> +route_exchange_relevant_port(const struct sbrec_port_binding *pb)
> +{
> +    return pb && smap_get_bool(&pb->options, "dynamic-routing", false);
> +}
> +
> +static uint32_t
> +advertise_route_hash(const struct in6_addr *dst, unsigned int plen)
> +{
> +    uint32_t hash = hash_bytes(dst->s6_addr, 16, 0);
> +    return hash_int(plen, hash);
> +}
> +
> +static const struct sbrec_port_binding*
> +find_route_exchange_pb(struct ovsdb_idl_index *sbrec_port_binding_by_name,
> +                       const struct sbrec_chassis *chassis,
> +                       const struct sset *active_tunnels,
> +                       const struct sbrec_port_binding *pb)
> +{
> +    if (!pb) {
> +        return NULL;
> +    }
> +    if (route_exchange_relevant_port(pb)) {
> +        return pb;
> +    }
> +    const char *crp = smap_get(&pb->options, "chassis-redirect-port");
> +    if (!crp) {
> +        return NULL;
> +    }
> +    if (!lport_is_chassis_resident(sbrec_port_binding_by_name, chassis,
> +                                   active_tunnels, crp)) {
> +        return NULL;
> +    }
> +    const struct sbrec_port_binding *crpbp = lport_lookup_by_name(
> +        sbrec_port_binding_by_name, crp);
> +    if (route_exchange_relevant_port(crpbp)) {
> +        return crpbp;
> +    }
> +    return NULL;
> +}
> +
> +static void
> +advertise_datapath_cleanup(struct advertise_datapath_entry *ad)
> +{
> +    struct advertise_route_entry *ar;
> +    HMAP_FOR_EACH_SAFE (ar, node, &ad->routes) {
> +        hmap_remove(&ad->routes, &ar->node);
> +        free(ar);
> +    }
> +    hmap_destroy(&ad->routes);
> +    sset_destroy(&ad->bound_ports);
> +    free(ad);
> +}
> +
> +static struct advertise_datapath_entry*
> +advertise_datapath_find(const struct hmap *datapaths,
> +                        const struct sbrec_datapath_binding *db)
> +{
> +    struct advertise_datapath_entry *ade;
> +    HMAP_FOR_EACH_WITH_HASH (ade, node, db->tunnel_key, datapaths) {
> +        if (ade->db == db) {
> +            return ade;
> +        }
> +    }
> +    return NULL;
> +}
> +
> +void
> +route_run(struct route_ctx_in *r_ctx_in,
> +          struct route_ctx_out *r_ctx_out)
> +{
> +    struct advertise_datapath_entry *ad;
> +    const struct local_datapath *ld;
> +
> +    HMAP_FOR_EACH (ld, hmap_node, r_ctx_in->local_datapaths) {
> +        if (!ld->n_peer_ports || ld->is_switch) {
> +            continue;
> +        }
> +
> +        ad = xzalloc(sizeof(*ad));
> +        ad->db = ld->datapath;
> +        hmap_init(&ad->routes);
> +        sset_init(&ad->bound_ports);
> +
> +        /* This is a LR datapath, find LRPs with route exchange options
> +         * that are bound locally. */
> +        for (size_t i = 0; i < ld->n_peer_ports; i++) {
> +            const struct sbrec_port_binding *local_peer
> +                = ld->peer_ports[i].local;
> +            const struct sbrec_port_binding *repb = find_route_exchange_pb(
> +                r_ctx_in->sbrec_port_binding_by_name,
> +                r_ctx_in->chassis,
> +                r_ctx_in->active_tunnels,
> +                local_peer);
> +            if (!repb) {
> +                continue;
> +            }
> +
> +            ad->maintain_vrf |= smap_get_bool(
> +                &repb->options, "dynamic-routing-maintain-vrf", false);
> +            sset_add(&ad->bound_ports, local_peer->logical_port);
> +        }
> +
> +        if (sset_is_empty(&ad->bound_ports)) {
> +            advertise_datapath_cleanup(ad);
> +            continue;
> +        }
> +        tracked_datapath_add(ld->datapath, TRACKED_RESOURCE_NEW,
> +                             r_ctx_out->tracked_re_datapaths);
> +
> +        hmap_insert(r_ctx_out->announce_routes, &ad->node, 
> ad->db->tunnel_key);
> +    }
> +
> +    const struct sbrec_advertised_route *route;
> +    SBREC_ADVERTISED_ROUTE_TABLE_FOR_EACH (route,
> +                                           r_ctx_in->advertised_route_table) 
> {
> +        ad = advertise_datapath_find(r_ctx_out->announce_routes,
> +                                     route->datapath);
> +        if (!ad) {
> +            continue;
> +        }
> +
> +        struct in6_addr prefix;
> +        unsigned int plen;
> +        if (!ip46_parse_cidr(route->ip_prefix, &prefix, &plen)) {
> +            VLOG_WARN_RL(&rl, "bad 'ip_prefix' %s in route "
> +                         UUID_FMT, route->ip_prefix,
> +                         UUID_ARGS(&route->header_.uuid));
> +            continue;
> +        }
> +
> +        struct advertise_route_entry *ar = xmalloc(sizeof *ar);
> +        ar->addr = prefix;
> +        ar->plen = plen;
> +        hmap_insert(&ad->routes, &ar->node,
> +                    advertise_route_hash(&prefix, plen));
> +    }
> +}
> +
> +void
> +route_cleanup(struct hmap *announce_routes)
> +{
> +    struct advertise_datapath_entry *ad;
> +    HMAP_FOR_EACH_POP (ad, node, announce_routes) {
> +        advertise_datapath_cleanup(ad);
> +    }
> +}
> diff --git a/controller/route.h b/controller/route.h
> new file mode 100644
> index 000000000..b38f62e5a
> --- /dev/null
> +++ b/controller/route.h
> @@ -0,0 +1,67 @@
> +/*
> + * Copyright (c) 2025, Canonical, Ltd.
> + * Copyright (c) 2025, STACKIT GmbH & Co. KG
> + *
> + * 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 ROUTE_H
> +#define ROUTE_H 1
> +
> +#include <stdbool.h>
> +#include <netinet/in.h>
> +#include "openvswitch/hmap.h"
> +#include "sset.h"
> +
> +struct hmap;
> +struct ovsdb_idl_index;
> +struct sbrec_chassis;
> +struct sbrec_port_binding;
> +
> +struct route_ctx_in {
> +    const struct sbrec_advertised_route_table *advertised_route_table;
> +    struct ovsdb_idl_index *sbrec_port_binding_by_name;
> +    const struct sbrec_chassis *chassis;
> +    const struct sset *active_tunnels;
> +    const struct hmap *local_datapaths;
> +    const struct sset *local_lports;
> +};
> +
> +struct route_ctx_out {
> +    struct hmap *tracked_re_datapaths;
> +    /* Contains struct advertise_datapath_entry */
> +    struct hmap *announce_routes;
> +};
> +
> +struct advertise_datapath_entry {
> +    struct hmap_node node;
> +    const struct sbrec_datapath_binding *db;
> +    bool maintain_vrf;
> +    struct hmap routes;
> +
> +    /* The name of the port bindings locally bound for this datapath and
> +     * running route exchange logic. */
> +    struct sset bound_ports;
> +};
> +
> +struct advertise_route_entry {
> +    struct hmap_node node;
> +    struct in6_addr addr;
> +    unsigned int plen;
> +};
> +
> +bool route_exchange_relevant_port(const struct sbrec_port_binding *);
> +void route_run(struct route_ctx_in *, struct route_ctx_out *);
> +void route_cleanup(struct hmap *announce_routes);
> +
> +#endif /* ROUTE_H */
> diff --git a/tests/automake.mk b/tests/automake.mk
> index 940f5b923..8c0040b6d 100644
> --- a/tests/automake.mk
> +++ b/tests/automake.mk
> @@ -304,6 +304,7 @@ tests_ovstest_LDADD = $(OVS_LIBDIR)/daemon.lo \
>       controller/ofctrl-seqno.$(OBJEXT) \
>       controller/ovsport.$(OBJEXT) \
>       controller/patch.$(OBJEXT) \
> +     controller/route.$(OBJEXT) \
>       controller/vif-plug.$(OBJEXT) \
>       northd/ipam.$(OBJEXT)
>  

_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to