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.
---
 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;
+            }
+
+        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);
+    }
+
+    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
+])
-- 
2.51.1

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

Reply via email to