On Thu, Dec 11, 2025 at 8:22 PM Dumitru Ceara <[email protected]> wrote:
> 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 > > +]) > > Thank you Dumitru, I went ahead, addressed the nits and merged this into main. Regards, Ales _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
