On 12/11/25 4:24 PM, Ales Musil wrote:
> Up until now all advertised routes wouldn't have a nexthop and would
> be installed purely as blackhole.
> Add the "dynamic-routing-v4-prefix-nexthop" and
> "dynamic-routing-v6-prefix-nexthop" option. That allows to
> define the nexthop for IPv4 and IPv6 advertised routes on given
> Logical Router. This option also allows to install IPv4 routes with
> IPv6 nexthop.
> 
> Reported-at: https://issues.redhat.com/browse/FDP-2735
> Acked-by: Dumitru Ceara <[email protected]>
> Signed-off-by: Ales Musil <[email protected]>
> ---
> v3: Add Dumitru's ack.
> ---

Hi Ales,

I spotted a few more minor things, please see below.

With those addressed, my ack stands.

Regards,
Dumitru

>  NEWS                                |   4 +
>  controller/ovn-controller.c         |  36 +++++++
>  controller/route.c                  |  38 ++++++-
>  controller/route.h                  |   3 +
>  northd/en-datapath-logical-router.c |  12 +++
>  ovn-nb.xml                          |  28 ++++++
>  tests/ovn-inc-proc-graph-dump.at    |   1 +
>  tests/system-ovn.at                 | 149 ++++++++++++++++++++++++++++
>  8 files changed, 267 insertions(+), 4 deletions(-)
> 
> diff --git a/NEWS b/NEWS
> index 22a9141e0..54ef04269 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -25,6 +25,10 @@ Post v25.09.0
>         EVPN domain.
>       * Add the option "dynamic-routing-vrf-id" to Logical Routers which 
> allows
>         CMS to specify the Linux routing table id for a given vrf.
> +     * Add the option "dynamic-routing-v4-prefix-nexthop" to Logical Routers
> +       which allows CMS to specify nexthop for IPv4 Advertised routes.
> +     * Add the option "dynamic-routing-v6-prefix-nexthop" to Logical Routers
> +       which allows CMS to specify nexthop for IPv6 Advertised routes.
>     - 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/controller/ovn-controller.c b/controller/ovn-controller.c
> index d5f2d98ad..4f73c0d1e 100644
> --- a/controller/ovn-controller.c
> +++ b/controller/ovn-controller.c
> @@ -5427,6 +5427,40 @@ route_sb_advertised_route_data_handler(struct 
> engine_node *node, void *data)
>      return EN_HANDLED_UNCHANGED;
>  }
>  
> +static enum engine_input_handler_result
> +route_sb_datapath_binding_handler(struct engine_node *node,
> +                                  void *data OVS_UNUSED)
> +{
> +    const struct sbrec_datapath_binding_table *dp_table =
> +        EN_OVSDB_GET(engine_get_input("SB_datapath_binding", node));
> +    struct ed_type_runtime_data *rt_data =
> +        engine_get_input_data("runtime_data", node);
> +
> +    const struct sbrec_datapath_binding *dp;
> +    SBREC_DATAPATH_BINDING_TABLE_FOR_EACH_TRACKED (dp, dp_table) {
> +        if (sbrec_datapath_binding_is_new(dp) ||
> +            sbrec_datapath_binding_is_deleted(dp)) {
> +            /* We are reflecting only datapaths that are becoming or are
> +             * removed from being local, that is taken care of by 
> runtime_data
> +             * handler. */
> +            return EN_HANDLED_UNCHANGED;
> +            }

Nit: indentation

> +
> +        struct local_datapath *ld =
> +            get_local_datapath(&rt_data->local_datapaths, dp->tunnel_key);
> +        if (!ld || ld->is_switch) {
> +            continue;
> +        }
> +
> +        if (sbrec_datapath_binding_is_updated(
> +                dp, SBREC_DATAPATH_BINDING_COL_EXTERNAL_IDS)) {
> +            return EN_UNHANDLED;
> +        }
> +    }
> +
> +    return EN_HANDLED_UNCHANGED;
> +}
> +
>  struct ed_type_route_exchange {
>      /* We need the idl to check if the Learned_Route table exists. */
>      struct ovsdb_idl *sb_idl;
> @@ -6642,6 +6676,8 @@ inc_proc_ovn_controller_init(
>                       route_runtime_data_handler);
>      engine_add_input(&en_route, &en_sb_advertised_route,
>                       route_sb_advertised_route_data_handler);
> +    engine_add_input(&en_route, &en_sb_datapath_binding,
> +                     route_sb_datapath_binding_handler);
>  
>      engine_add_input(&en_route_exchange, &en_route, NULL);
>      engine_add_input(&en_route_exchange, &en_sb_learned_route,
> diff --git a/controller/route.c b/controller/route.c
> index c0ed2af48..5028ef258 100644
> --- a/controller/route.c
> +++ b/controller/route.c
> @@ -185,6 +185,37 @@ advertise_datapath_find(const struct hmap *datapaths,
>      return NULL;
>  }
>  
> +static struct advertise_datapath_entry *
> +advertised_datapath_alloc(const struct sbrec_datapath_binding *datapath)
> +{
> +    struct advertise_datapath_entry *ad = xmalloc(sizeof *ad);
> +    *ad = (struct advertise_datapath_entry) {
> +        .db = datapath,
> +        .routes = HMAP_INITIALIZER(&ad->routes),
> +        .bound_ports = SMAP_INITIALIZER(&ad->bound_ports),
> +    };
> +
> +    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 10);
> +
> +    const char *nh4 =
> +        smap_get(&datapath->external_ids, 
> "dynamic-routing-v4-prefix-nexthop");
> +    if (nh4 && !ip46_parse(nh4, &ad->ipv4_nexthop)) {
> +        memset(&ad->ipv4_nexthop, 0, sizeof ad->ipv4_nexthop);
> +        VLOG_WARN_RL(&rl, "Couldn't parse IPv4 prefix nexthop %s, "
> +                    "routes will be installed as blackhole.", nh4);

Nit: indentation.

> +    }
> +
> +    const char *nh6 =
> +        smap_get(&datapath->external_ids, 
> "dynamic-routing-v6-prefix-nexthop");
> +    if (nh6 && !ipv6_parse(nh6, &ad->ipv6_nexthop)) {
> +        memset(&ad->ipv6_nexthop, 0, sizeof ad->ipv6_nexthop);
> +        VLOG_WARN_RL(&rl, "Couldn't parse IPv6 prefix nexthop %s, "
> +                     "routes will be installed as blackhole.", nh6);
> +    }
> +
> +    return ad;
> +}
> +
>  void
>  route_run(struct route_ctx_in *r_ctx_in,
>            struct route_ctx_out *r_ctx_out)
> @@ -220,10 +251,7 @@ route_run(struct route_ctx_in *r_ctx_in,
>              }
>  
>              if (!ad) {
> -                ad = xzalloc(sizeof(*ad));
> -                ad->db = ld->datapath;
> -                hmap_init(&ad->routes);
> -                smap_init(&ad->bound_ports);
> +                ad = advertised_datapath_alloc(ld->datapath);
>              }
>  
>              ad->maintain_vrf |=
> @@ -345,6 +373,8 @@ route_run(struct route_ctx_in *r_ctx_in,
>              .addr = prefix,
>              .plen = plen,
>              .priority = priority,
> +            .nexthop = IN6_IS_ADDR_V4MAPPED(&prefix)
> +                       ? ad->ipv4_nexthop : ad->ipv6_nexthop,
>          };
>          hmap_insert(&ad->routes, &ar->node,
>                      advertise_route_hash(&ar->addr, &ar->nexthop, plen));
> diff --git a/controller/route.h b/controller/route.h
> index 38564c945..de77f64fc 100644
> --- a/controller/route.h
> +++ b/controller/route.h
> @@ -66,6 +66,9 @@ struct advertise_datapath_entry {
>      const struct sbrec_datapath_binding *db;
>      bool maintain_vrf;
>      char vrf_name[IFNAMSIZ + 1];
> +    struct in6_addr ipv4_nexthop;
> +    struct in6_addr ipv6_nexthop;
> +
>      struct hmap routes;
>  
>      /* The name of the port bindings locally bound for this datapath and
> diff --git a/northd/en-datapath-logical-router.c 
> b/northd/en-datapath-logical-router.c
> index 56785ebfb..a4b5e2383 100644
> --- a/northd/en-datapath-logical-router.c
> +++ b/northd/en-datapath-logical-router.c
> @@ -95,6 +95,18 @@ gather_external_ids(const struct nbrec_logical_router *nbr,
>                          vrf_id);
>      }
>  
> +    const char *nh4 =
> +        smap_get(&nbr->options, "dynamic-routing-v4-prefix-nexthop");
> +    if (nh4 && nh4[0]) {
> +        smap_add(external_ids, "dynamic-routing-v4-prefix-nexthop", nh4);
> +    }
> +
> +    const char *nh6 =
> +        smap_get(&nbr->options, "dynamic-routing-v6-prefix-nexthop");
> +    if (nh6 && nh6[0]) {
> +        smap_add(external_ids, "dynamic-routing-v6-prefix-nexthop", nh6);
> +    }
> +
>      /* For backwards-compatibility, also store the NB UUID in
>       * external-ids:logical-router. This is useful if ovn-controller
>       * has not updated and expects this to be where to find the
> diff --git a/ovn-nb.xml b/ovn-nb.xml
> index b5fe44e53..177c7dd9c 100644
> --- a/ovn-nb.xml
> +++ b/ovn-nb.xml
> @@ -3421,6 +3421,34 @@ or
>          </p>
>        </column>
>  
> +      <column name="options" key="dynamic-routing-v4-prefix-nexthop"
> +              type='{"type": "string"}'>
> +        <p>
> +          Only relevant if <ref column="options" key="dynamic-routing"/>
> +          is set to <code>true</code>.
> +        </p>
> +
> +        <p>
> +          This defines nexthop used by IPv4 advertised routes instead of the
> +          routes being advertised as blackhole. This can be either valid IPv4
> +          or IPv6 address.
> +        </p>
> +      </column>
> +
> +      <column name="options" key="dynamic-routing-v6-prefix-nexthop"
> +              type='{"type": "string"}'>
> +        <p>
> +          Only relevant if <ref column="options" key="dynamic-routing"/>
> +          is set to <code>true</code>.
> +        </p>
> +
> +        <p>
> +          This defines nexthop used by IPv6 advertised routes instead of the
> +          routes being advertised as blackhole. This can be only valid IPv6
> +          address.
> +        </p>
> +      </column>
> +
>        <column name="options" key="ct-commit-all" type='{"type": "boolean"}'>
>          When enabled the LR will commit traffic in a zone that is stateful.
>          The traffic is not commited to both zones but it is selective based
> diff --git a/tests/ovn-inc-proc-graph-dump.at 
> b/tests/ovn-inc-proc-graph-dump.at
> index 2d7780a13..eb00ccc7d 100644
> --- a/tests/ovn-inc-proc-graph-dump.at
> +++ b/tests/ovn-inc-proc-graph-dump.at
> @@ -447,6 +447,7 @@ digraph "Incremental-Processing-Engine" {
>       SB_port_binding -> route [[label="route_sb_port_binding_data_handler"]];
>       runtime_data -> route [[label="route_runtime_data_handler"]];
>       SB_advertised_route -> route 
> [[label="route_sb_advertised_route_data_handler"]];
> +     SB_datapath_binding -> route 
> [[label="route_sb_datapath_binding_handler"]];
>       SB_learned_route [[style=filled, shape=box, fillcolor=white, 
> label="SB_learned_route"]];
>       route_table_notify [[style=filled, shape=box, fillcolor=white, 
> label="route_table_notify"]];
>       route_exchange_status [[style=filled, shape=box, fillcolor=white, 
> label="route_exchange_status"]];
> diff --git a/tests/system-ovn.at b/tests/system-ovn.at
> index d8be7befc..1b2505f90 100644
> --- a/tests/system-ovn.at
> +++ b/tests/system-ovn.at
> @@ -18824,3 +18824,152 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port 
> patch-.*/d
>  AT_CLEANUP
>  ])
>  ])
> +
> +OVN_FOR_EACH_NORTHD([
> +AT_SETUP([dynamic-routing - Advertised route nexthop])
> +AT_KEYWORDS([dynamic-routing])
> +
> +CHECK_VRF()
> +CHECK_CONNTRACK()
> +CHECK_CONNTRACK_NAT()
> +
> +vrf=1002
> +VRF_RESERVE([$vrf])
> +ovn_start
> +OVS_TRAFFIC_VSWITCHD_START()
> +ADD_BR([br-int])
> +ADD_BR([br-ext], [set Bridge br-ext fail-mode=standalone])
> +
> +# Set external-ids in br-int needed for ovn-controller.
> +check ovs-vsctl \
> +    -- set Open_vSwitch . external-ids:system-id=hv1 \
> +    -- set Open_vSwitch . 
> external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
> +    -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
> +    -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \
> +    -- set Open_vSwitch . external-ids:ovn-bridge-mappings=phynet:br-ext \
> +    -- set bridge br-int fail-mode=secure other-config:disable-in-band=true
> +
> +# Start ovn-controller.
> +start_daemon ovn-controller
> +
> +OVS_WAIT_WHILE([ip link | grep -q ovnvrf$vrf:.*UP])
> +
> +check ovn-nbctl                                                         \
> +    -- lr-add R1                                                        \
> +    -- set Logical_Router R1                                            \
> +            options:dynamic-routing-vrf-id=$vrf                         \
> +            options:dynamic-routing=true                                \
> +    -- ls-add sw0                                                       \
> +    -- ls-add public                                                    \
> +    -- lrp-add R1 rp-sw0 00:00:01:01:02:03 192.168.1.1/24               \
> +                         2001:db8:100::1/64                             \
> +    -- lrp-add R1 rp-public 00:00:02:01:02:03 172.16.1.1/24             \
> +                         2001:db8:1003::1/64                            \
> +    -- lrp-set-options rp-public                                        \
> +           dynamic-routing-maintain-vrf=true                            \
> +           dynamic-routing-redistribute=connected-as-host,nat,lb        \
> +    -- set logical_router R1 options:chassis=hv1                        \
> +    -- lsp-add sw0 sw0-rp                                               \
> +    -- set Logical_Switch_Port sw0-rp type=router                       \
> +            options:router-port=rp-sw0                                  \
> +    -- lsp-set-addresses sw0-rp router                                  \
> +    -- lsp-add public public-rp                                         \
> +    -- set Logical_Switch_Port public-rp type=router                    \
> +            options:router-port=rp-public                               \
> +    -- lsp-set-addresses public-rp router                               \
> +    -- lsp-add-localnet-port public public1 phynet                      \
> +    -- lr-nat-add R1 dnat_and_snat 172.16.1.10 192.168.1.10             \
> +    -- lr-nat-add R1 dnat 172.16.1.11 192.168.1.11                      \
> +    -- lr-nat-add R1 dnat_and_snat 2001:db8:1003::150 2001:db8:100::100 \
> +    -- lr-nat-add R1 dnat 2001:db8:1003::151 2001:db8:100::100          \
> +    -- lb-add lb1 172.16.1.20 192.168.1.10,192.168.1.20                 \
> +    -- lb-add lb2 2001:db8:1003::20 2001:db8:100::10,2001:db8:100::20   \
> +    -- lr-lb-add R1 lb1                                                 \
> +    -- lr-lb-add R1 lb2
> +
> +OVN_POPULATE_ARP
> +check ovn-nbctl --wait=hv sync
> +wait_for_ports_up
> +
> +AT_CHECK([test `ip route show table $vrf | wc -l` -eq 2], [1])
> +AT_CHECK([ip link | grep -q ovnvrf$vrf:.*UP])
> +
> +check ip link add lo-test type dummy
> +on_exit 'ip link del lo-test'
> +check ip link set lo-test master ovnvrf$vrf
> +check ip link set lo-test address 00:00:00:00:00:10
> +check ip addr add 20.0.0.10/24 dev lo-test
> +check ip addr add 2000:db8:1000::10/96 dev lo-test nodad
> +check ip link set up lo-test
> +
> +OVN_ROUTE_EQUAL([ovnvrf$vrf], [dnl
> +20.0.0.0/24 dev lo-test proto kernel scope link src 20.0.0.10
> +blackhole 172.16.1.1 proto ovn metric 100
> +blackhole 172.16.1.10 proto ovn metric 1000
> +blackhole 172.16.1.11 proto ovn metric 1000
> +blackhole 172.16.1.20 proto ovn metric 1000])
> +
> +OVN_ROUTE_V6_EQUAL([ovnvrf$vrf], [dnl
> +2000:db8:1000::/96 dev lo-test proto kernel metric 256 pref medium
> +blackhole 2001:db8:1003::1 dev lo proto ovn metric 100 pref medium
> +blackhole 2001:db8:1003::20 dev lo proto ovn metric 1000 pref medium
> +blackhole 2001:db8:1003::150 dev lo proto ovn metric 1000 pref medium
> +blackhole 2001:db8:1003::151 dev lo proto ovn metric 1000 pref medium
> +fe80::/64 dev lo-test proto kernel metric 256 pref medium
> +multicast ff00::/8 dev lo-test proto kernel metric 256 pref medium])
> +
> +check ovn-nbctl --wait=hv set logical_router R1 \
> +    options:dynamic-routing-v4-prefix-nexthop="20.0.0.1"
> +OVN_ROUTE_EQUAL([ovnvrf$vrf], [dnl
> +20.0.0.0/24 dev lo-test proto kernel scope link src 20.0.0.10
> +172.16.1.1 via 20.0.0.1 dev lo-test proto ovn metric 100
> +172.16.1.10 via 20.0.0.1 dev lo-test proto ovn metric 1000
> +172.16.1.11 via 20.0.0.1 dev lo-test proto ovn metric 1000
> +172.16.1.20 via 20.0.0.1 dev lo-test proto ovn metric 1000])
> +
> +check ovn-nbctl --wait=hv set logical_router R1 \
> +    options:dynamic-routing-v4-prefix-nexthop="2000:db8:1000::1"
> +OVN_ROUTE_EQUAL([ovnvrf$vrf], [dnl
> +20.0.0.0/24 dev lo-test proto kernel scope link src 20.0.0.10
> +172.16.1.1 via inet6 2000:db8:1000::1 dev lo-test proto ovn metric 100
> +172.16.1.10 via inet6 2000:db8:1000::1 dev lo-test proto ovn metric 1000
> +172.16.1.11 via inet6 2000:db8:1000::1 dev lo-test proto ovn metric 1000
> +172.16.1.20 via inet6 2000:db8:1000::1 dev lo-test proto ovn metric 1000])
> +
> +check ovn-nbctl --wait=hv set logical_router R1 \
> +    options:dynamic-routing-v6-prefix-nexthop="20.0.0.1"
> +OVN_ROUTE_V6_EQUAL([ovnvrf$vrf], [dnl
> +2000:db8:1000::/96 dev lo-test proto kernel metric 256 pref medium
> +blackhole 2001:db8:1003::1 dev lo proto ovn metric 100 pref medium
> +blackhole 2001:db8:1003::20 dev lo proto ovn metric 1000 pref medium
> +blackhole 2001:db8:1003::150 dev lo proto ovn metric 1000 pref medium
> +blackhole 2001:db8:1003::151 dev lo proto ovn metric 1000 pref medium
> +fe80::/64 dev lo-test proto kernel metric 256 pref medium
> +multicast ff00::/8 dev lo-test proto kernel metric 256 pref medium])
> +
> +check ovn-nbctl --wait=hv set logical_router R1 \
> +    options:dynamic-routing-v6-prefix-nexthop="2000:db8:1000::1"
> +OVN_ROUTE_V6_EQUAL([ovnvrf$vrf], [dnl
> +2000:db8:1000::/96 dev lo-test proto kernel metric 256 pref medium
> +2001:db8:1003::1 via 2000:db8:1000::1 dev lo-test proto ovn metric 100 pref 
> medium
> +2001:db8:1003::20 via 2000:db8:1000::1 dev lo-test proto ovn metric 1000 
> pref medium
> +2001:db8:1003::150 via 2000:db8:1000::1 dev lo-test proto ovn metric 1000 
> pref medium
> +2001:db8:1003::151 via 2000:db8:1000::1 dev lo-test proto ovn metric 1000 
> pref medium
> +fe80::/64 dev lo-test proto kernel metric 256 pref medium
> +multicast ff00::/8 dev lo-test proto kernel metric 256 pref medium])
> +
> +OVN_CLEANUP_CONTROLLER([hv1])
> +
> +# Ensure system resources are cleaned up.
> +AT_CHECK([ip link | grep -q ovnvrf$vrf:.*UP], [1])
> +AT_CHECK([test `ip route show table $vrf | wc -l` -eq 1], [1])
> +
> +OVN_CLEANUP_NORTHD
> +
> +as
> +OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
> +/Failed to acquire.*/d
> +/connection dropped.*/d
> +/Couldn't parse IPv6 prefix nexthop.*/d"])
> +AT_CLEANUP
> +])

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

Reply via email to