On Mon, Apr 13, 2026 at 6:37 PM Lucas Vargas Dias via dev <
[email protected]> wrote:

> Consider the scenario where there are 2 AZs using ovn-ic, each AZ has
> one LR and its LS. The LRs are interconnected by a transit switch.
> On each AZ, we can configure the dynamic routing and exchange routes
> via BGP with external BGP speakers.
>
> AZ1
> LS1 - LR1 - BGP Speaker1 - Local subnet1
>        \
>    -----------------------
>     transit switch
>    -----------------------
> AZ2    /
> LS2 - LR2 - BGP Speaker2 - Local subnet2
>
> The LR2 will learn the subnet1 prefix via ovn-ic but this prefix will
> not be redistributed to BGP Speaker2 and it also happens with LR1.
> This scenario uses the network's hub-and-spoke terminology where we can
> enable the hub-spoke on the LR for such redistribution.
> subnet1 and subnet2 are configured as ic-source-dynamic=true in ovn-ic.
> Also, consider the same scenario, but LR1 and LR2 learn the same subnet
> for redundancy, hub-spoke option is disabled to not adv the subnet learned
> from BGP and advertised in ovn-ic in BGP speakers.
>
> Signed-off-by: Lucas Vargas Dias <[email protected]>
> ---
>

Hi Lucas,

thank you for the v4. I have some small comments down below.


>  NEWS                              |   2 +
>  ic/ovn-ic.c                       |  20 ++++--
>  lib/ovn-util.h                    |   1 +
>  northd/en-advertised-route-sync.c |   2 +
>  northd/northd.c                   |   8 +++
>  northd/northd.h                   |   5 +-
>  northd/ovn-northd.c               |   1 -
>  ovn-ic-sb.ovsschema               |   7 ++-
>  ovn-ic-sb.xml                     |  14 +++--
>  ovn-nb.xml                        |  14 +++++
>  tests/ovn-ic.at                   | 101 ++++++++++++++++++++++++++++++
>  11 files changed, 160 insertions(+), 15 deletions(-)
>
> diff --git a/NEWS b/NEWS
> index 888946b54..5710716aa 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -52,6 +52,8 @@ OVN v26.03.0 - xxx xx xxxx
>         learned routes. This option has priority over its router
> counterpart.
>       * The EVPN support is now considered stable.  Its "experimental" tag
> has
>         been removed.
> +     * Add support for hub-and-spoke propagation via the "hub-spoke"
> option
> +       in dynamic-routing-redistribute settings.
>

nit: This should be in the post 26.03.0 section.

    - Add support for Network Function insertion in OVN with stateful
> traffic
>       redirection capability in Logical Switch datapath. The feature
> introduces
>       three new NB database tables:
> diff --git a/ic/ovn-ic.c b/ic/ovn-ic.c
> index 1aa8af9b9..9d422e25b 100644
> --- a/ic/ovn-ic.c
> +++ b/ic/ovn-ic.c
> @@ -1283,6 +1283,7 @@ struct ic_route_info {
>      struct in6_addr prefix;
>      unsigned int plen;
>      struct in6_addr nexthop;
> +    bool is_src_dynamic;
>

nit: This is no longer used.


>      const char *origin;
>      const char *route_table;
>      const char *route_tag;
> @@ -1671,7 +1672,8 @@ add_network_to_routes_ad(struct hmap *routes_ad,
> const char *network,
>                           const struct smap *nb_options,
>                           const struct nbrec_logical_router *nb_lr,
>                           const char *route_tag,
> -                         const struct nbrec_logical_router_port *ts_lrp)
> +                         const struct nbrec_logical_router_port *ts_lrp,
> +                         bool is_src_dynamic)
>  {
>      struct in6_addr prefix, nexthop;
>      unsigned int plen;
> @@ -1721,8 +1723,10 @@ add_network_to_routes_ad(struct hmap *routes_ad,
> const char *network,
>          ds_destroy(&msg);
>      }
>
> +    const char *origin = is_src_dynamic ? ROUTE_ORIGIN_CONNECTED_DYNAMIC :
> +                                          ROUTE_ORIGIN_CONNECTED;
>      /* directly-connected routes go to <main> route table */
> -    add_to_routes_ad(routes_ad, prefix, plen, nexthop,
> ROUTE_ORIGIN_CONNECTED,
> +    add_to_routes_ad(routes_ad, prefix, plen, nexthop, origin,
>                       NULL, nb_lrp, NULL, nb_lr, NULL, route_tag);
>  }
>
> @@ -2169,6 +2173,14 @@ sync_learned_routes(struct ic_context *ctx,
>                                  isb_route->route_table,
>                                  &isb_route->header_.uuid, 0);
>              if (route_learned) {
> +                if (strcmp(route_learned->origin, isb_route->origin) !=
> 0) {
> +                    VLOG_DBG("Update learned route %s -> %s with new
> origin "
> +                             "%s (old origin was %s)",
> +                             isb_route->ip_prefix, isb_route->nexthop,
> +                             isb_route->origin, route_learned->origin);
> +
> nbrec_logical_router_static_route_update_options_setkey(
> +                        route_learned->nb_route, "origin",
> isb_route->origin);
> +                }
>

I think this condition can actually never happen, because thei
ic_route_find() is also checking the origin. I should have realized
this in the previous review sorry. So we don't need this update
because if they don't match we will just re-create the NB entry.

                 hmap_remove(&ic_lr->routes_learned, &route_learned->node);
>                  free(route_learned);
>              } else {
> @@ -2374,7 +2386,7 @@ build_ts_routes_to_adv(struct ic_context *ctx,
>                  add_network_to_routes_ad(routes_ad, lrp->networks[j], lrp,
>                                           ts_port_addrs,
>                                           &nb_global->options,
> -                                         lr, route_tag, ts_lrp);
> +                                         lr, route_tag, ts_lrp, false);
>              }
>          } else {
>              /* The router port of the TS port is ignored. */
> @@ -2429,7 +2441,7 @@ build_ts_routes_to_adv(struct ic_context *ctx,
>          add_network_to_routes_ad(routes_ad, sb_route->ip_prefix, NULL,
>                                   ts_port_addrs,
>                                   &nb_global->options,
> -                                 lr, route_tag, ts_lrp);
> +                                 lr, route_tag, ts_lrp, true);
>      }
>      sbrec_learned_route_index_destroy_row(filter);
>  }
> diff --git a/lib/ovn-util.h b/lib/ovn-util.h
> index 4c6623bef..4ccf6dc2d 100644
> --- a/lib/ovn-util.h
> +++ b/lib/ovn-util.h
> @@ -33,6 +33,7 @@
>  #define ROUTE_ORIGIN_CONNECTED "connected"
>  #define ROUTE_ORIGIN_STATIC "static"
>  #define ROUTE_ORIGIN_LB "loadbalancer"
> +#define ROUTE_ORIGIN_CONNECTED_DYNAMIC "connected-dynamic"
>
>  #define ETH_CRC_LENGTH 4
>  #define ETHERNET_OVERHEAD (ETH_HEADER_LEN + ETH_CRC_LENGTH)
> diff --git a/northd/en-advertised-route-sync.c
> b/northd/en-advertised-route-sync.c
> index 8b73810c7..6ae9a9689 100644
> --- a/northd/en-advertised-route-sync.c
> +++ b/northd/en-advertised-route-sync.c
> @@ -683,6 +683,8 @@ should_advertise_route(const struct ovn_datapath
> *advertising_od,
>          return drr_mode_LB_is_set(drr);
>      case ROUTE_SOURCE_CONNECTED_AS_HOST:
>          return drr_mode_CONNECTED_AS_HOST_is_set(drr);
> +    case ROUTE_SOURCE_IC_DYNAMIC:
> +        return drr_mode_IC_DYNAMIC_is_set(drr);
>      case ROUTE_SOURCE_LEARNED:
>          OVS_NOT_REACHED();
>      default:
> diff --git a/northd/northd.c b/northd/northd.c
> index b7239f4e2..ce80639ea 100644
> --- a/northd/northd.c
> +++ b/northd/northd.c
> @@ -887,6 +887,10 @@ parse_dynamic_routing_redistribute(
>              out |= DRRM_LB;
>              continue;
>          }
> +        if (!strcmp(token, "hub-spoke")) {
> +            out |= DRRM_IC_DYNAMIC;
> +            continue;
> +        }
>          static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
>          VLOG_WARN_RL(&rl,
>                       "unknown dynamic-routing-redistribute option '%s' on
> %s",
> @@ -12342,6 +12346,9 @@ parsed_routes_add_static(const struct ovn_datapath
> *od,
>      if (!strcmp(smap_get_def(&route->options, "origin", ""),
>                  ROUTE_ORIGIN_CONNECTED)) {
>          source = ROUTE_SOURCE_CONNECTED;
> +    } else if (!strcmp(smap_get_def(&route->options, "origin", ""),
> +                ROUTE_ORIGIN_CONNECTED_DYNAMIC)) {
> +        source = ROUTE_SOURCE_IC_DYNAMIC;
>

nit: We are calling the smap_get_def multiple times.


>      } else {
>          source = ROUTE_SOURCE_STATIC;
>      }
> @@ -12436,6 +12443,7 @@ route_source_to_offset(enum route_source source)
>  {
>      switch (source) {
>      case ROUTE_SOURCE_CONNECTED:
> +    case ROUTE_SOURCE_IC_DYNAMIC:
>          return ROUTE_PRIO_OFFSET_CONNECTED;
>      case ROUTE_SOURCE_STATIC:
>          return ROUTE_PRIO_OFFSET_STATIC;
> diff --git a/northd/northd.h b/northd/northd.h
> index 139519006..e86d39f9a 100644
> --- a/northd/northd.h
> +++ b/northd/northd.h
> @@ -365,7 +365,8 @@ struct mcast_port_info {
>      DRR_MODE(CONNECTED_AS_HOST, 1) \
>      DRR_MODE(STATIC,            2) \
>      DRR_MODE(NAT,               3) \
> -    DRR_MODE(LB,                4)
> +    DRR_MODE(LB,                4) \
> +    DRR_MODE(IC_DYNAMIC,        5)
>
>  enum dynamic_routing_redistribute_mode_bits {
>  #define DRR_MODE(PROTOCOL, BIT) DRRM_##PROTOCOL##_BIT = BIT,
> @@ -832,6 +833,8 @@ enum route_source {
>      ROUTE_SOURCE_LB,
>      /* The route is derived from out_port of connected logical router. */
>      ROUTE_SOURCE_CONNECTED_AS_HOST,
> +    /* The route is derived from an ovn-controller and advertised to IC.
> */
> +    ROUTE_SOURCE_IC_DYNAMIC,
>  };
>
>  struct parsed_route {
> diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
> index 0ed2eb17a..cfaf2f74b 100644
> --- a/northd/ovn-northd.c
> +++ b/northd/ovn-northd.c
> @@ -905,7 +905,6 @@ main(int argc, char *argv[])
>          &nbrec_load_balancer_col_external_ids,
>          &nbrec_load_balancer_health_check_col_external_ids,
>          &nbrec_logical_router_policy_col_external_ids,
> -        &nbrec_logical_router_static_route_col_external_ids,
>

nit: Leftover.


>          &nbrec_meter_col_external_ids,
>          &nbrec_meter_band_col_external_ids,
>          &nbrec_mirror_col_external_ids,
> diff --git a/ovn-ic-sb.ovsschema b/ovn-ic-sb.ovsschema
> index 8981a4eef..e0e0fef5e 100644
> --- a/ovn-ic-sb.ovsschema
> +++ b/ovn-ic-sb.ovsschema
> @@ -1,7 +1,7 @@
>  {
>      "name": "OVN_IC_Southbound",
> -    "version": "2.4.0",
> -    "cksum": "859073048 9662",
> +    "version": "2.5.0",
> +    "cksum": "1892994110 9713",
>      "tables": {
>          "IC_SB_Global": {
>              "columns": {
> @@ -116,7 +116,8 @@
>                  "origin": {"type": {"key": {
>                      "type": "string",
>                      "enum": ["set",
> -                             ["connected", "static", "loadbalancer"]]}}},
> +                             ["connected", "static", "loadbalancer",
> +                              "connected-dynamic"]]}}},
>                  "external_ids": {
>                      "type": {"key": "string", "value": "string",
>                               "min": 0, "max": "unlimited"}}},
> diff --git a/ovn-ic-sb.xml b/ovn-ic-sb.xml
> index b59fe1d03..3ca115073 100644
> --- a/ovn-ic-sb.xml
> +++ b/ovn-ic-sb.xml
> @@ -378,12 +378,14 @@
>        </column>
>
>        <column name="origin">
> -        Can be one of <code>connected</code>, <code>static</code> or
> -        <code>loadbalancer</code>.  Routes to directly-connected subnets -
> -        LRP's CIDRs are inserted to OVN IC SB DB with
> <code>connected</code>
> -        value in <ref column="origin"/>.  Static routes are inserted to
> OVN IC
> -        SB DB with <code>static</code> value.  Routes for LB VIPs are
> inserted
> -        in OVN IC SB DB with <code>loadbalancer</code> value.
> +        Can be one of <code>connected</code>, <code>static</code>,
> +        <code>connected-dynamic</code> or <code>loadbalancer</code>.
> Routes
> +        to directly-connected subnets - LRP's CIDRs are inserted to OVN
> IC SB
> +        DB with <code>connected</code> value in <ref column="origin"/>.
> +          Static routes are inserted to OVN IC SB DB with
> <code>static</code>
>

nit: Stray witespace.


> +        value.  Routes for LB VIPs are inserted in OVN IC SB DB with
> +        <code>loadbalancer</code> value.  Routes learned from dynamic
> routing
> +        are inserted OVN IC SB DB with <code>connected-dynamic</code>
> value.
>

nit: Missing "in".


>          Next when route is learned to another AZ NB DB by ovn-ic, route
> origin
>          is synced to <ref table="Logical_Router_Static_Route"
> column="options"
>          key="origin"/>.
> diff --git a/ovn-nb.xml b/ovn-nb.xml
> index f1cd89509..7324e8656 100644
> --- a/ovn-nb.xml
> +++ b/ovn-nb.xml
> @@ -3533,6 +3533,13 @@ or
>            Logical Switch.
>          </p>
>
> +        <p>
> +          If <code>hub-spoke</code> is in the list then northd will
> synchronize
> +          dynamic routes learned through OVN-IC from other routers into
> the
> +          <ref table="Advertised_Route" db="OVN_Southbound"/> table,
> enabling
> +          hub-and-spoke propagation.
> +        </p>
> +
>          <p>
>            This value can be overwritten on a per LRP basis using
>            <ref column="options" key="dynamic-routing-redistribute"
> @@ -4629,6 +4636,13 @@ or
>            via shared Logical Switch.
>          </p>
>
> +        <p>
> +          If <code>hub-spoke</code> is in the list then northd will
> synchronize
> +          dynamic routes learned through OVN-IC from other routers into
> the
> +          <ref table="Advertised_Route" db="OVN_Southbound"/> table,
> enabling
> +          hub-and-spoke propagation.
> +        </p>
> +
>          <p>
>            If not set the value from <ref column="options"
>            key="dynamic-routing-redistribute" table="Logical_Router"/> on
> the
> diff --git a/tests/ovn-ic.at b/tests/ovn-ic.at
> index 8bb5a4177..50270fdda 100644
> --- a/tests/ovn-ic.at
> +++ b/tests/ovn-ic.at
> @@ -4692,3 +4692,104 @@ OVS_WAIT_FOR_OUTPUT([ovn_as az2 ovn-nbctl
> lr-route-list lr12 | grep 192.168 |
>  OVN_CLEANUP_IC([az1], [az2], [az3])
>  AT_CLEANUP
>  ])
> +
> +OVN_FOR_EACH_NORTHD([
> +AT_SETUP([ovn-ic -- Check ovn-ic adv and learn from SB Learned Route -
> hub and spoke mode])
> +
> +ovn_init_ic_db
> +
> +for i in 1 2; do
> +    ovn_start az$i
> +    ovn_as az$i
> +
> +    # Enable route learning at AZ level
> +    check ovn-nbctl set nb_global . options:ic-route-learn=true
> +    # Enable route advertising at AZ level
> +    check ovn-nbctl set nb_global . options:ic-route-adv=true
> +done
> +
> +# Create new transit switches and LRs. Test topology is next:
> +#
> +#
> +# logical router (lr11) - transit switch (ts11) - logical router (lr12)
> +#
> +#
> +
> +# Create lr11, lr12 and ts11 and connect them
> +for i in 1 2; do
> +    ovn_as az$i
> +
> +    lr=lr1$i
> +    check ovn-nbctl lr-add $lr
> +
> +    ts=ts11
> +    check ovn-ic-nbctl --wait=sb --may-exist ts-add $ts
> +
> +    lrp=lrp-$lr-$ts
> +    lsp=lsp-$ts-$lr
> +    # Create LRP and connect to TS
> +    check ovn-nbctl lrp-add $lr $lrp aa:aa:aa:aa:a1:0$i 169.254.101.$i/24
> +    check ovn-nbctl lsp-add-router-port $ts $lsp $lrp
> +done
> +
> +# Create directly-connected route in lr12
> +check ovn_as az2 ovn-nbctl lrp-add lr12 lrp-lr12 aa:aa:aa:aa:bb:01 "
> 192.168.0.1/24"
> +OVS_WAIT_FOR_OUTPUT([ovn_as az1 ovn-nbctl lr-route-list lr11 | grep
> 192.168 |
> +             grep learned | awk '{print $1, $2}' | sort ], [0], [dnl
> +192.168.0.0/24 169.254.101.2
> +])
> +
> +ovn_as az2
> +check ovn-nbctl --wait=sb set Logical_Router lr12
> option:dynamic-routing=true \
> +    option:dynamic-routing-redistribute="connected,static"
> +check ovn_as az2 ovn-nbctl --wait=sb lrp-add lr12 lr12-dr1
> 00:00:00:00:ff:01 10.0.0.1/24
> +dr1=$(fetch_column port_binding _uuid logical_port=lr12-dr1)
> +datapath=$(fetch_column datapath_binding _uuid external_ids:name=lr12)
> +
> +check_uuid ovn-sbctl create Learned_Route \
> +    datapath=$datapath                    \
> +    logical_port=$dr1                     \
> +    ip_prefix=192.168.1.0/24              \
> +    nexthop=10.0.0.20
> +
> +wait_row_count ic-sb:Route 1 ip_prefix=192.168.1.0/24
> origin=connected-dynamic
> +
> +# Check Learned_Route adv in ovn-ic
> +OVS_WAIT_FOR_OUTPUT([ovn_as az1 ovn-nbctl lr-route-list lr11 |
> +             grep learned | awk '{print $1, $2}' | sort ], [0], [dnl
> +10.0.0.0/24 169.254.101.2
> +192.168.0.0/24 169.254.101.2
> +192.168.1.0/24 169.254.101.2
> +])
> +
> +ovn_as az1
> +check ovn-nbctl --wait=sb set Logical_Router lr11
> option:dynamic-routing=true \
> +    option:dynamic-routing-redistribute="connected,static"
> +# Advertise just 10.0.0.0/24 and 192.168.0.0/24 routes
> +check_row_count Advertised_Route 1 ip_prefix=192.168.0.0/24
> +check_row_count Advertised_Route 1 ip_prefix=10.0.0.0/24
> +check_row_count Advertised_Route 0 ip_prefix=192.168.1.0/24
> +
> +
> +ovn_as az1
> +check ovn-nbctl --wait=sb set Logical_Router lr11 \
> +    option:dynamic-routing-redistribute="connected,static,hub-spoke"
> +# Advertise just 10.0.0.0/24, 192.168.0.0/24 and 192.168.1.0/24 routes
> +# Route 192.168.1.0/24 is learned by DR from other logical routes (lr12)
> +# And lr12 Advertised it in ovn-ic. Hub-spoke option enable re-routing in
> lr11
> +check_row_count Advertised_Route 1 ip_prefix=192.168.0.0/24
> +check_row_count Advertised_Route 1 ip_prefix=10.0.0.0/24
> +check_row_count Advertised_Route 1 ip_prefix=192.168.1.0/24
> +
> +ovn_as az1
> +check ovn-nbctl --wait=sb set Logical_Router lr11 \
> +    option:dynamic-routing-redistribute="connected,static"
> +
> +check_row_count Advertised_Route 1 ip_prefix=192.168.0.0/24
> +check_row_count Advertised_Route 1 ip_prefix=10.0.0.0/24
> +check_row_count Advertised_Route 0 ip_prefix=192.168.1.0/24
> +
> +OVN_CLEANUP_IC([az1], [az2])
> +
> +AT_CLEANUP
> +])
> --
> 2.43.0
>
>
> --
>
>
>
>
> _'Esta mensagem é direcionada apenas para os endereços constantes no
> cabeçalho inicial. Se você não está listado nos endereços constantes no
> cabeçalho, pedimos-lhe que desconsidere completamente o conteúdo dessa
> mensagem e cuja cópia, encaminhamento e/ou execução das ações citadas
> estão
> imediatamente anuladas e proibidas'._
>
>
> * **'Apesar do Magazine Luiza tomar
> todas as precauções razoáveis para assegurar que nenhum vírus esteja
> presente nesse e-mail, a empresa não poderá aceitar a responsabilidade por
> quaisquer perdas ou danos causados por esse e-mail ou por seus anexos'.*
>
>
>
> _______________________________________________
> dev mailing list
> [email protected]
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
>
I addressed those comments and applied it to main.

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

Reply via email to