Add the "options:dynamic-routing-advertise-prefixes" key to the
Logical_Router_Port table. If it is set, only the listed prefixes are
synced to the SB Advertised_Route table for that port and every route
selected by "dynamic-routing-redistribute" (connected, static, nat, lb,
hub-spoke) is skipped on it. This lets the CMS announce an aggregated
prefix instead of many individual NAT or LB addresses.

Signed-off-by: Alexandra Rukomoinikova <[email protected]>
---
 NEWS                              |   4 ++
 northd/en-advertised-route-sync.c |  69 ++++++++++++++++++-
 ovn-nb.xml                        |  30 +++++++++
 tests/ovn-northd.at               | 108 ++++++++++++++++++++++++++++++
 4 files changed, 210 insertions(+), 1 deletion(-)

diff --git a/NEWS b/NEWS
index 40a1b9867..78af38f9f 100644
--- a/NEWS
+++ b/NEWS
@@ -120,6 +120,10 @@ OVN v26.09.0 - xxx xx xxxx
      filter the routes learned through the port by route tag.  This supersedes
      "ic-route-filter-tag", which is now deprecated and is ignored when
      "ic-route-learn-tag-rules" is set.
+   - Logical_Router_Port: Added a new
+     "options:dynamic-routing-advertise-prefixes" key that contains an
+     explicit list of prefixes to be advertised via this port.  If it is
+     set, only these prefixes will be announced.
 
 OVN v26.03.0 - xxx xx xxxx
 --------------------------
diff --git a/northd/en-advertised-route-sync.c 
b/northd/en-advertised-route-sync.c
index eb8a24a39..c623d9ba0 100644
--- a/northd/en-advertised-route-sync.c
+++ b/northd/en-advertised-route-sync.c
@@ -29,6 +29,8 @@
 
 VLOG_DEFINE_THIS_MODULE(en_advertised_route_sync);
 
+#define DYNAMIC_ROUTING_ADVERTISE_PREFIXES "dynamic-routing-advertise-prefixes"
+
 struct ar_entry {
     struct hmap_node hmap_node;
 
@@ -50,6 +52,8 @@ struct ar_entry {
      * unmonitored listener must remain reachable regardless. */
     bool has_ungated_lb;
     struct sset health_checks;
+
+    bool advertise_prefix;
 };
 
 /* Add a new entries to the to-be-advertised routes.
@@ -871,6 +875,61 @@ build_connected_as_host_routes(const struct ovn_datapath 
*od,
     }
 }
 
+static const char *
+lrp_advertise_prefixes(const struct ovn_port *op)
+{
+    if (!op || !op->nbrp) {
+        return NULL;
+    }
+
+    return smap_get(&op->nbrp->options, DYNAMIC_ROUTING_ADVERTISE_PREFIXES);
+}
+
+static void
+build_advertise_prefix_routes(const struct ovn_datapath *od,
+                              struct hmap *routes)
+{
+    const struct ovn_port *op;
+    HMAP_FOR_EACH (op, dp_node, &od->ports) {
+        const char *prefixes = lrp_advertise_prefixes(op);
+        if (!prefixes || !op->sb) {
+            continue;
+        }
+
+        char *save_ptr = NULL;
+        char *tokstr = xstrdup(prefixes);
+        for (char *token = strtok_r(tokstr, ",", &save_ptr);
+             token != NULL;
+             token = strtok_r(NULL, ",", &save_ptr)) {
+            struct in6_addr prefix;
+            unsigned int plen;
+
+            if (!ip46_parse_cidr(token, &prefix, &plen)) {
+                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
+                VLOG_WARN_RL(&rl, "bad prefix '%s' in option %s of %s",
+                             token, DYNAMIC_ROUTING_ADVERTISE_PREFIXES,
+                             op->nbrp->name);
+                continue;
+            }
+
+            char *ip_prefix = normalize_v46_prefix(&prefix, plen);
+            struct ar_entry *dup = ar_entry_find(routes, od->sdp->sb_dp,
+                                                 op->sb, ip_prefix, NULL);
+            if (dup && dup->advertise_prefix) {
+                /* The same prefix is listed twice in the option. */
+                free(ip_prefix);
+                continue;
+            }
+
+            struct ar_entry *route_e =
+                ar_entry_add_nocopy(routes, od, op, ip_prefix, NULL,
+                                    ROUTE_SOURCE_STATIC);
+            route_e->advertise_prefix = true;
+        }
+        free(tokstr);
+    }
+}
+
 void *
 en_dynamic_routes_init(struct engine_node *node OVS_UNUSED,
                        struct engine_arg *arg OVS_UNUSED)
@@ -989,6 +1048,8 @@ en_dynamic_routes_run(struct engine_node *node, void *data)
         build_connected_as_host_routes(od, &northd_data->ls_ports,
                                        dynamic_routes_data);
 
+        build_advertise_prefix_routes(od, &dynamic_routes_data->routes);
+
         const struct lr_stateful_record *lr_stateful_rec =
             lr_stateful_table_find_by_uuid(&lr_stateful_data->table, od->key);
         if (!lr_stateful_rec) {
@@ -1105,6 +1166,11 @@ should_advertise_route(const struct ovn_datapath 
*advertising_od,
         return false;
     }
 
+    if (lrp_advertise_prefixes(advertising_op)) {
+        /* This port advertises only the explicitly configured prefixes. */
+        return false;
+    }
+
     enum dynamic_routing_redistribute_mode drr =
         advertising_op->dynamic_routing_redistribute;
 
@@ -1178,7 +1244,8 @@ advertised_route_table_sync(
     /* Then add the set of dynamic routes that need sync-ing. */
     struct ar_entry *route_e;
     HMAP_FOR_EACH (route_e, hmap_node, dynamic_routes) {
-        if (!should_advertise_route(route_e->od, route_e->op,
+        if (!route_e->advertise_prefix &&
+            !should_advertise_route(route_e->od, route_e->op,
                                     route_e->source)) {
             continue;
         }
diff --git a/ovn-nb.xml b/ovn-nb.xml
index c741a3b32..c771b7e05 100644
--- a/ovn-nb.xml
+++ b/ovn-nb.xml
@@ -4850,6 +4850,36 @@ or
 
       </column>
 
+      <column name="options" key="dynamic-routing-advertise-prefixes"
+              type='{"type": "string"}'>
+        <p>
+          Only relevant if <ref column="options" key="dynamic-routing"
+          table="Logical_Router"/> on the respective Logical_Router is set
+          to <code>true</code>.
+        </p>
+
+        <p>
+          This is a list of IPv4 and/or IPv6 prefixes in CIDR notation,
+          separated by <code>,</code>, e.g.
+          <code>10.0.0.0/8,2001:db8::/32</code>.
+        </p>
+
+        <p>
+          If this option is set, northd creates entries in the <ref
+          table="Advertised_Route" db="OVN_Southbound"/> table only for the
+          prefixes listed here. Every route that would otherwise be
+          advertised via this port because of <ref column="options"
+          key="dynamic-routing-redistribute"
+          table="Logical_Router_Port"/> (or its Logical_Router counterpart),
+          i.e. <code>connected</code>, <code>connected-as-host</code>,
+          <code>static</code>, <code>nat</code>, <code>lb</code> and
+          <code>hub-spoke</code> routes, is not advertised via this port.
+          Setting the option to an empty string disables all advertisements
+          on this port.
+        </p>
+
+      </column>
+
       <column name="options" key="dynamic-routing-advertise">
         If the CMS sets <ref column="options" 
key="dynamic-routing-redistribute"
         table="Logical_Router_Port"/> to <code>connected-as-host</code>,
diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
index 6d191c1a0..45322ffcb 100644
--- a/tests/ovn-northd.at
+++ b/tests/ovn-northd.at
@@ -16525,6 +16525,114 @@ OVN_CLEANUP_NORTHD
 AT_CLEANUP
 ])
 
+OVN_FOR_EACH_NORTHD_NO_HV([
+AT_SETUP([dynamic-routing - advertise only configured prefixes])
+AT_KEYWORDS([dynamic-routing])
+ovn_start
+
+# Start with a GW router that advertises connected, static, nat and lb routes
+# via both of its LRPs.
+check ovn-nbctl lr-add lr0
+check ovn-nbctl set Logical_Router lr0                            \
+    options:dynamic-routing=true                                  \
+    options:chassis=hv1                                           \
+    options:dynamic-routing-redistribute="connected,static,nat,lb"
+check ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24
+check ovn-nbctl lrp-add lr0 lr0-sw1 00:00:00:00:ff:02 10.0.1.1/24
+check ovn-nbctl lr-route-add lr0 192.168.0.0/24 10.0.0.10
+check ovn-nbctl lr-nat-add lr0 dnat_and_snat 172.16.1.10 10.0.0.20
+check ovn-nbctl lb-add lb0 172.16.2.10:80 10.0.0.30:80
+check ovn-nbctl lr-lb-add lr0 lb0
+check ovn-nbctl --wait=sb sync
+
+datapath=$(fetch_column Datapath_Binding _uuid external_ids:name=lr0)
+sw0=$(fetch_column Port_Binding _uuid logical_port=lr0-sw0)
+sw1=$(fetch_column Port_Binding _uuid logical_port=lr0-sw1)
+
+check_row_count Advertised_Route 7
+check_row_count Advertised_Route 4 logical_port=$sw0
+check_row_count Advertised_Route 1 logical_port=$sw0 ip_prefix=10.0.0.0/24
+check_row_count Advertised_Route 1 logical_port=$sw0 ip_prefix=192.168.0.0/24
+check_row_count Advertised_Route 1 logical_port=$sw0 ip_prefix=172.16.1.10
+check_row_count Advertised_Route 1 logical_port=$sw0 ip_prefix=172.16.2.10
+check_row_count Advertised_Route 3 logical_port=$sw1
+
+# Setting dynamic-routing-advertise-prefixes on lr0-sw0 replaces every route
+# advertised via this port with the configured ones.  Both IPv4 and IPv6
+# prefixes are supported.  The other LRP is not affected.
+check ovn-nbctl --wait=sb set Logical_Router_Port lr0-sw0 \
+    options:dynamic-routing-advertise-prefixes="172.16.0.0/16,2001:db8:1::/64"
+check_row_count Advertised_Route 5
+check_row_count Advertised_Route 2 logical_port=$sw0
+check_row_count Advertised_Route 1     \
+    ip_prefix="172.16.0.0/16"          \
+    datapath=$datapath                 \
+    logical_port=$sw0                  \
+    tracked_port=[[]]
+check_row_count Advertised_Route 1     \
+    ip_prefix='"2001:db8:1::/64"'      \
+    datapath=$datapath                 \
+    logical_port=$sw0                  \
+    tracked_port=[[]]
+check_row_count Advertised_Route 3 logical_port=$sw1
+check_row_count Advertised_Route 1 logical_port=$sw1 ip_prefix=10.0.1.0/24
+check_row_count Advertised_Route 1 logical_port=$sw1 ip_prefix=172.16.1.10
+check_row_count Advertised_Route 1 logical_port=$sw1 ip_prefix=172.16.2.10
+
+# The prefixes are normalized and duplicates are advertised only once.
+check ovn-nbctl --wait=sb set Logical_Router_Port lr0-sw0 \
+    options:dynamic-routing-advertise-prefixes="10.10.10.5/24,10.10.10.0/24"
+check_row_count Advertised_Route 1 logical_port=$sw0
+check_row_count Advertised_Route 1 logical_port=$sw0 ip_prefix=10.10.10.0/24
+
+# Invalid prefixes are ignored, the valid ones are still advertised.
+check ovn-nbctl --wait=sb set Logical_Router_Port lr0-sw0 \
+    options:dynamic-routing-advertise-prefixes="not-a-prefix,10.20.0.0/16"
+check_row_count Advertised_Route 1 logical_port=$sw0
+check_row_count Advertised_Route 1 logical_port=$sw0 ip_prefix=10.20.0.0/16
+
+# An empty list blocks all advertisements on this port.
+check ovn-nbctl --wait=sb set Logical_Router_Port lr0-sw0 \
+    options:dynamic-routing-advertise-prefixes='""'
+check_row_count Advertised_Route 0 logical_port=$sw0
+check_row_count Advertised_Route 3 logical_port=$sw1
+
+# The option also overrides the redistribute modes set on the LRP itself.
+check ovn-nbctl --wait=sb set Logical_Router_Port lr0-sw0            \
+    options:dynamic-routing-redistribute="connected,static,nat,lb"   \
+    options:dynamic-routing-advertise-prefixes="10.30.0.0/16"
+check_row_count Advertised_Route 1 logical_port=$sw0
+check_row_count Advertised_Route 1 logical_port=$sw0 ip_prefix=10.30.0.0/16
+
+# Removing the option brings the redistributed routes back.
+check ovn-nbctl --wait=sb remove Logical_Router_Port lr0-sw0 \
+    options dynamic-routing-advertise-prefixes
+check_row_count Advertised_Route 4 logical_port=$sw0
+check_row_count Advertised_Route 1 logical_port=$sw0 ip_prefix=10.0.0.0/24
+check_row_count Advertised_Route 1 logical_port=$sw0 ip_prefix=192.168.0.0/24
+check_row_count Advertised_Route 1 logical_port=$sw0 ip_prefix=172.16.1.10
+check_row_count Advertised_Route 1 logical_port=$sw0 ip_prefix=172.16.2.10
+
+# The configured prefixes are advertised only while dynamic routing is
+# enabled on the Logical_Router.
+check ovn-nbctl --wait=sb set Logical_Router_Port lr0-sw0 \
+    options:dynamic-routing-advertise-prefixes="10.30.0.0/16"
+check ovn-nbctl --wait=sb remove Logical_Router lr0 options dynamic-routing
+check_row_count Advertised_Route 0
+check ovn-nbctl --wait=sb set Logical_Router lr0 options:dynamic-routing=true
+check_row_count Advertised_Route 4
+check_row_count Advertised_Route 1 logical_port=$sw0 ip_prefix=10.30.0.0/16
+check_row_count Advertised_Route 3 logical_port=$sw1
+
+# Removing the LRP removes its advertised prefixes.
+check ovn-nbctl --wait=sb lrp-del lr0-sw0
+check_row_count Advertised_Route 3
+check_row_count Advertised_Route 0 logical_port=$sw0
+
+OVN_CLEANUP_NORTHD
+AT_CLEANUP
+])
+
 OVN_FOR_EACH_NORTHD_NO_HV([
 AT_SETUP([dynamic-routing - learning routes from sb])
 AT_KEYWORDS([dynamic-routing])
-- 
2.48.1

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

Reply via email to