The "ic-route-filter-tag" option on a Logical_Router_Port acts as a
blocklist: routes whose "ic-route-tag" matches the configured tag are
not learned. There was no way to express the opposite - "learn only
routes carrying one of these tags" - which is useful when a router port
should import routes from a known set of VPCs and drop everything else.

Add an "ic-route-allow-tag" option that takes a comma-separated list
of route tags. When set, only IC routes whose "ic-route-tag" matches
one of the listed tags are learned; every other route, including
untagged ones, is filtered out. The existing "ic-route-filter-tag"
blocklist still takes precedence, so a route whose tag is filtered is
skipped even if the same tag is allowlisted.

Assisted-by: Claude Opus 4.8, Claude Code
Signed-off-by: Lucas Vargas Dias <[email protected]>
---
 NEWS            |  4 ++++
 ic/ovn-ic.c     | 26 +++++++++++++++++++++++++-
 ovn-nb.xml      | 24 ++++++++++++++++++++++++
 tests/ovn-ic.at | 35 +++++++++++++++++++++++++++++++++++
 4 files changed, 88 insertions(+), 1 deletion(-)

diff --git a/NEWS b/NEWS
index 44f117807..9f4fb2ad9 100644
--- a/NEWS
+++ b/NEWS
@@ -94,6 +94,10 @@ Post v26.03.0
    - Added a new "ovn-debug lflow-pipeline-oftable-start-list" command that
      prints the starting OpenFlow table number of the logical ingress and
      egress pipelines.
+   - Added "ic-route-allow-tag" option to the Logical_Router_Port
+     table, it accepts a comma-separated list of route tags and
+     only IC routes whose "ic-route-tag" matches one of them are learned.
+     "ic-route-filter-tag" takes precedence over it.
 
 OVN v26.03.0 - xxx xx xxxx
 --------------------------
diff --git a/ic/ovn-ic.c b/ic/ovn-ic.c
index 8b0cf9e15..09e91cb38 100644
--- a/ic/ovn-ic.c
+++ b/ic/ovn-ic.c
@@ -2537,7 +2537,8 @@ sync_learned_routes(struct ic_context *ctx,
         nbrec_nb_global_first(ctx->ovnnb_idl);
     ovs_assert(nb_global);
 
-    const char *lrp_name, *ts_route_table, *route_tag_filter;
+    const char *lrp_name, *ts_route_table, *route_tag_filter,
+               *route_tag_allow;
     const struct icsbrec_port_binding *isb_pb;
     const struct nbrec_logical_router_port *lrp;
     VECTOR_FOR_EACH (&ic_lr->isb_pbs, isb_pb) {
@@ -2550,15 +2551,25 @@ sync_learned_routes(struct ic_context *ctx,
             ts_route_table = smap_get_def(&lrp->options, "route_table", "");
             route_tag_filter = smap_get_def(&lrp->options,
                                             "ic-route-filter-tag", "");
+            route_tag_allow = smap_get_def(&lrp->options,
+                                            "ic-route-allow-tag", "");
         } else {
             ts_route_table = "";
             route_tag_filter = "";
+            route_tag_allow = "";
         }
 
         /* The filter tag option accepts a comma-separated list of tags. */
         struct sset filter_tags = SSET_INITIALIZER(&filter_tags);
         sset_from_delimited_string(&filter_tags, route_tag_filter, ",");
 
+
+        /* Allowlist of route tags to learn. When non-empty, only routes
+         * whose "ic-route-tag" is in this set are learned; every other
+         * route (including untagged ones) is filtered out. */
+        struct sset allow_tag_set = SSET_INITIALIZER(&allow_tag_set);
+        sset_from_delimited_string(&allow_tag_set, route_tag_allow, ",");
+
         isb_route_key = icsbrec_route_index_init_row(ctx->icsbrec_route_by_ts);
         icsbrec_route_index_set_transit_switch(isb_route_key,
                                                isb_pb->transit_switch);
@@ -2590,6 +2601,18 @@ sync_learned_routes(struct ic_context *ctx,
                 continue;
             }
 
+            if (!sset_is_empty(&allow_tag_set)) {
+                if (!isb_route_tag ||
+                    !sset_contains(&allow_tag_set, isb_route_tag)) {
+                    VLOG_DBG("Skip learning route %s -> %s as its route tag "
+                             "[%s] is not in the allow tag list [%s] of TS"
+                             "LRP ", isb_route->ip_prefix, isb_route->nexthop,
+                             isb_route_tag ? isb_route_tag : "(none)",
+                            route_tag_allow);
+                    continue;
+                }
+            }
+
             if (isb_route->route_table[0] &&
                 strcmp(isb_route->route_table, ts_route_table)) {
                 if (VLOG_IS_DBG_ENABLED()) {
@@ -2655,6 +2678,7 @@ sync_learned_routes(struct ic_context *ctx,
         }
         icsbrec_route_index_destroy_row(isb_route_key);
         sset_destroy(&filter_tags);
+        sset_destroy(&allow_tag_set);
     }
 
     /* Delete extra learned routes. */
diff --git a/ovn-nb.xml b/ovn-nb.xml
index 23d15bb45..32ab99b5d 100644
--- a/ovn-nb.xml
+++ b/ovn-nb.xml
@@ -4700,6 +4700,30 @@ or
         </p>
       </column>
 
+      <column name="options" key="ic-route-allow-tag"
+              type='{"type": "string"}'>
+        <p>
+          This option expects a comma-separated list of allowed route-tags,
+          for example <code>vpc1,vpc2</code>. When set on the Logical Router
+          Port, it acts as an allowlist for the route learning process: only
+          routes whose <code>ic-route-tag</code> (present in the
+          <code>external_ids</code> register of the advertised route entry in
+          the <ref table="Route" db="OVN_IC_Southbound"/> table of the
+          <ref db="OVN_IC_Southbound"/> database) matches one of the listed
+          tags are learned by the <code>ovn-ic</code> daemon. Every other
+          route, including routes that carry no <code>ic-route-tag</code> at
+          all, is filtered and not learned.
+        </p>
+
+        <p>
+          When both this option and <ref column="options"
+          key="ic-route-filter-tag"/> are set, the blocklist behaviour of
+          <ref column="options" key="ic-route-filter-tag"/> takes precedence:
+          a route whose tag matches the filter tag is skipped even if the
+          same tag is present in this allowlist.
+        </p>
+      </column>
+
       <column name="options" key="requested-chassis">
         <p>
           If set, identifies a specific chassis (by name or hostname) that
diff --git a/tests/ovn-ic.at b/tests/ovn-ic.at
index 178bd22ee..b78c98487 100644
--- a/tests/ovn-ic.at
+++ b/tests/ovn-ic.at
@@ -3734,6 +3734,41 @@ OVS_WAIT_FOR_OUTPUT([ovn_as az1 ovn-nbctl lr-route-list 
lr11 | grep 192.168 |
 192.168.1.0/24 169.254.103.22
 ])
 
+ovn_as az1 ovn-nbctl remove logical_router_port lrp-lr11-tspeer options 
ic-route-filter-tag
+# Allowlist: only learn tspeer routes tagged vpc1 on lrp-lr11-tspeer.
+# The tspeer route from lr22 (169.254.103.22) carries no tag and is thus
+# filtered by the strict allowlist. Routes learned through ts11/ts12 are
+# not affected as the option is set on the tspeer LRP only.
+ovn_as az1 ovn-nbctl set logical_router_port lrp-lr11-tspeer 
options:ic-route-allow-tag=vpc1
+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
+192.168.0.0/24 169.254.102.2
+192.168.0.0/24 169.254.103.12
+])
+
+# Tag lr22's advertised route with vpc2 and allow a list of tags [vpc1,vpc2].
+# The previously untagged route is now tagged vpc2 and learned again.
+ovn_as az2 ovn-nbctl set logical_router_port lrp-lr22-tspeer 
options:ic-route-tag=vpc2
+ovn_as az1 ovn-nbctl set logical_router_port lrp-lr11-tspeer 
options:ic-route-allow-tag=vpc1,vpc2
+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
+192.168.0.0/24 169.254.102.2
+192.168.0.0/24 169.254.103.12
+192.168.1.0/24 169.254.103.22
+])
+
+# Blocklist takes precedence over the allowlist: filtering vpc1 drops the
+# vpc1 tspeer route (169.254.103.12) even though vpc1 is in the allow list.
+ovn_as az1 ovn-nbctl set logical_router_port lrp-lr11-tspeer 
options:ic-route-filter-tag=vpc1
+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
+192.168.0.0/24 169.254.102.2
+192.168.1.0/24 169.254.103.22
+])
+
 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

Reply via email to