On 1/29/25 12:15 PM, Felix Huettner via dev wrote:
> Previously we just assumed that if a LR had multiple LRPs bound to the
> local chassis that all routes where valid for all of these LRPs.
> This commit handles a previous option in ovn-nb that allowes the user to
> specify the interface name that a route needs to use to be acceptable
> for a given LRP.
> The users can then have a 1:1 relationship between LRPs and interfaces
> to allow ovn-controller to determine which route belongs to which LRP.
> 
> This can e.g. be used if a chassis has multiple uplinks. Each of these
> uplinks could be assigned to a different LRP of the same LR connected to
> the same localnet.
> The option allows then to map e.g. a learned default route to each
> individual LRP. If one of the links would go down just one of the
> learned routes would be removed, causing ECMP across multiple chassis to
> be correctly weighted.
> 
> Signed-off-by: Felix Huettner <felix.huettner@stackit.cloud>
> ---

Hi Felix,

I only have two minor comments below.  If addressing those is the only
diff in v6, feel free to also add my ack:

Acked-by: Dumitru Ceara <dce...@redhat.com>

> v3->v4:
>   - addressed review comments.
>   - updated commit message to be more descriptive.
> 
>  controller/route-exchange-netlink.c |  1 +
>  controller/route-exchange-netlink.h |  3 +++
>  controller/route-exchange.c         | 20 ++++++++++++++------
>  controller/route.c                  | 14 +++++++++-----
>  controller/route.h                  |  6 ++++--
>  tests/system-ovn.at                 | 23 ++++++++++++++++++++++-
>  6 files changed, 53 insertions(+), 14 deletions(-)
> 
> diff --git a/controller/route-exchange-netlink.c 
> b/controller/route-exchange-netlink.c
> index 74741a3fd..c5a77efe4 100644
> --- a/controller/route-exchange-netlink.c
> +++ b/controller/route-exchange-netlink.c
> @@ -237,6 +237,7 @@ handle_route_msg(const struct route_table_msg *msg, void 
> *data)
>              rr->addr = rd->rta_dst;
>              rr->plen = rd->rtm_dst_len;
>              rr->nexthop = nexthop->addr;
> +            memcpy(rr->ifname, nexthop->ifname, IFNAMSIZ);

As mentioned on patch 6/11 I think I'd just set rr->ifname[IFNAMSIZ] = 0
and use xmalloc() instead of xzalloc().

>          }
>          return;
>      }
> diff --git a/controller/route-exchange-netlink.h 
> b/controller/route-exchange-netlink.h
> index bc77504ae..e4fe81977 100644
> --- a/controller/route-exchange-netlink.h
> +++ b/controller/route-exchange-netlink.h
> @@ -21,6 +21,7 @@
>  #include <stdint.h>
>  #include "openvswitch/list.h"
>  #include <netinet/in.h>
> +#include <net/if.h>
>  
>  /* This value is arbitrary but currently unused.
>   * See https://github.com/iproute2/iproute2/blob/main/etc/iproute2/rt_protos 
> */
> @@ -35,6 +36,8 @@ struct re_nl_received_route_node {
>      struct in6_addr addr;
>      unsigned int plen;
>      struct in6_addr nexthop;
> +    /* Adding 1 to this to be sure we actually have a terminating '\0' */
> +    char ifname[IFNAMSIZ + 1];
>  };
>  
>  int re_nl_create_vrf(const char *ifname, uint32_t table_id);
> diff --git a/controller/route-exchange.c b/controller/route-exchange.c
> index a163968a7..dc3e5657c 100644
> --- a/controller/route-exchange.c
> +++ b/controller/route-exchange.c
> @@ -97,7 +97,7 @@ route_lookup(struct hmap *route_map,
>  static void
>  sb_sync_learned_routes(const struct ovs_list *learned_routes,
>                         const struct sbrec_datapath_binding *datapath,
> -                       const struct sset *bound_ports,
> +                       const struct smap *bound_ports,
>                         struct ovsdb_idl_txn *ovnsb_idl_txn,
>                         struct ovsdb_idl_index *sbrec_port_binding_by_name,
>                         struct ovsdb_idl_index 
> *sbrec_learned_route_by_datapath)
> @@ -112,8 +112,9 @@ sb_sync_learned_routes(const struct ovs_list 
> *learned_routes,
>      SBREC_LEARNED_ROUTE_FOR_EACH_EQUAL (sb_route, filter,
>                                          sbrec_learned_route_by_datapath) {
>          /* If the port is not local we don't care about it.
> -         * Some other ovn-controller will handle it. */
> -        if (!sset_contains(bound_ports,
> +         * Some other ovn-controller will handle it.
> +         * We may not use smap_get since the value might be validly NULL. */
> +        if (!smap_get_node(bound_ports,
>                             sb_route->logical_port->logical_port)) {
>              continue;
>          }
> @@ -127,11 +128,18 @@ sb_sync_learned_routes(const struct ovs_list 
> *learned_routes,
>                                                 learned_route->plen);
>          char *nexthop = normalize_v46(&learned_route->nexthop);
>  
> -        const char *logical_port_name;
> -        SSET_FOR_EACH (logical_port_name, bound_ports) {
> +        struct smap_node *port_node;
> +        SMAP_FOR_EACH (port_node, bound_ports) {
> +            /* The user specified an ifname, but we learned it on a different
> +             * port. */
> +            if (port_node->value && strcmp(port_node->value,
> +                                           learned_route->ifname)) {
> +                continue;
> +            }
> +
>              const struct sbrec_port_binding *logical_port =
>                  lport_lookup_by_name(sbrec_port_binding_by_name,
> -                                     logical_port_name);
> +                                     port_node->key);
>              if (!logical_port) {
>                  continue;
>              }
> diff --git a/controller/route.c b/controller/route.c
> index 49e76ee73..22f38976b 100644
> --- a/controller/route.c
> +++ b/controller/route.c
> @@ -87,7 +87,7 @@ advertise_datapath_cleanup(struct advertise_datapath_entry 
> *ad)
>          free(ar);
>      }
>      hmap_destroy(&ad->routes);
> -    sset_destroy(&ad->bound_ports);
> +    smap_destroy(&ad->bound_ports);
>      free(ad);
>  }
>  
> @@ -108,8 +108,8 @@ 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;
> +    struct advertise_datapath_entry *ad;

I actually think it looks better in the other order (reverse xmas tree). :)

>  
>      HMAP_FOR_EACH (ld, hmap_node, r_ctx_in->local_datapaths) {
>          if (!ld->n_peer_ports || ld->is_switch) {
> @@ -119,7 +119,7 @@ route_run(struct route_ctx_in *r_ctx_in,
>          ad = xzalloc(sizeof(*ad));
>          ad->db = ld->datapath;
>          hmap_init(&ad->routes);
> -        sset_init(&ad->bound_ports);
> +        smap_init(&ad->bound_ports);
>  
>          /* This is a LR datapath, find LRPs with route exchange options
>           * that are bound locally. */
> @@ -137,10 +137,14 @@ route_run(struct route_ctx_in *r_ctx_in,
>  
>              ad->maintain_vrf |= smap_get_bool(
>                  &repb->options, "dynamic-routing-maintain-vrf", false);
> -            sset_add(&ad->bound_ports, local_peer->logical_port);
> +            char *ifname = nullable_xstrdup(
> +                                    smap_get(&repb->options,
> +                                             "dynamic-routing-ifname"));
> +            smap_add_nocopy(&ad->bound_ports,
> +                            xstrdup(local_peer->logical_port), ifname);
>          }
>  
> -        if (sset_is_empty(&ad->bound_ports)) {
> +        if (smap_is_empty(&ad->bound_ports)) {
>              advertise_datapath_cleanup(ad);
>              continue;
>          }
> diff --git a/controller/route.h b/controller/route.h
> index 6fc5963a5..f23115abb 100644
> --- a/controller/route.h
> +++ b/controller/route.h
> @@ -22,6 +22,7 @@
>  #include <netinet/in.h>
>  #include "openvswitch/hmap.h"
>  #include "sset.h"
> +#include "smap.h"
>  
>  struct hmap;
>  struct ovsdb_idl_index;
> @@ -50,8 +51,9 @@ struct advertise_datapath_entry {
>      struct hmap routes;
>  
>      /* The name of the port bindings locally bound for this datapath and
> -     * running route exchange logic. */
> -    struct sset bound_ports;
> +     * running route exchange logic.
> +     * The key is the port name and the value is the ifname if set. */
> +    struct smap bound_ports;
>  };
>  
>  struct advertise_route_entry {
> diff --git a/tests/system-ovn.at b/tests/system-ovn.at
> index dc99d4c57..e17d9534e 100644
> --- a/tests/system-ovn.at
> +++ b/tests/system-ovn.at
> @@ -15058,6 +15058,17 @@ check_row_count Learned_Route 1
>  lp=$(fetch_column port_binding _uuid logical_port=internet-phys)
>  check_row_count Learned_Route 1 logical_port=$lp ip_prefix=233.252.0.0/24 
> nexthop=192.168.10.10
>  
> +# By setting a learning interface filter we will now forget about this route.
> +check ovn-nbctl --wait=hv set Logical_Router_Port internet-phys \
> +      options:dynamic-routing-ifname=thisdoesnotexist
> +check_row_count Learned_Route 0
> +
> +# Changing it to "lo" will allow us to learn the route again.
> +check ovn-nbctl --wait=hv set Logical_Router_Port internet-phys \
> +      options:dynamic-routing-ifname=lo
> +check_row_count Learned_Route 1
> +
> +
>  OVS_APP_EXIT_AND_WAIT([ovn-controller])
>  
>  as ovn-sb
> @@ -15268,10 +15279,20 @@ check ip route add 233.252.0.0/24 via 192.168.10.10 
> dev lo onlink vrf ovnvrf1337
>  # For now we trigger a recompute as route watching is not yet implemented.
>  check ovn-appctl -t ovn-controller inc-engine/recompute
>  check ovn-nbctl --wait=hv sync
> -check_row_count Learned_Route 2
> +check_row_count Learned_Route 1
>  lp=$(fetch_column port_binding _uuid logical_port=internet-phys)
>  check_row_count Learned_Route 1 logical_port=$lp ip_prefix=233.252.0.0/24 
> nexthop=192.168.10.10
>  
> +# By setting a learning interface filter we will now forget about this route.
> +check ovn-nbctl --wait=hv set Logical_Router_Port internet-phys \
> +      options:dynamic-routing-ifname=thisdoesnotexist
> +check_row_count Learned_Route 0
> +
> +# Changing it to "lo" will allow us to learn the route again.
> +check ovn-nbctl --wait=hv set Logical_Router_Port internet-phys \
> +      options:dynamic-routing-ifname=lo
> +check_row_count Learned_Route 1
> +
>  as ovn-sb
>  OVS_APP_EXIT_AND_WAIT([ovsdb-server])
>  

Thanks,
Dumitru

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

Reply via email to