This commit adds the ability for ovn-ic to filter routes between VPCs.
In use cases where the user is using vpc-peering(route tag) handling
multiple points of redistribution, when we connect more than one TS in
the same Logical Router it is essential that we have control over which
routes will be filtered or advertised by transit switch.

Signed-off-by: Paulo Guilherme da Silva <guilherme.pa...@luizalabs.com>
Co-authored-by: Lucas Vargas Dias <lucas.vd...@luizalabs.com>
---
 ic/ovn-ic.c     | 119 +++++++++++++++++++++++++++++++++++++++++-----
 ovn-nb.xml      |  44 +++++++++++++++++
 tests/ovn-ic.at | 122 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 274 insertions(+), 11 deletions(-)

diff --git a/ic/ovn-ic.c b/ic/ovn-ic.c
index ea68b1c12..f0df8bf8d 100644
--- a/ic/ovn-ic.c
+++ b/ic/ovn-ic.c
@@ -1069,6 +1069,86 @@ get_nexthop_from_lport_addresses(bool is_v4,
     return true;
 }
 
+static bool
+prefix_is_filtered(struct in6_addr *prefix,
+                   unsigned int plen,
+                   const struct nbrec_logical_router *nb_lr,
+                   const struct nbrec_logical_router_port *ts_lrp,
+                   bool is_advertisement)
+{
+    struct ds filter_list = DS_EMPTY_INITIALIZER;
+    const char *filter_direction = is_advertisement ? "ic-route-filter-adv" :\
+        "ic-route-filter-learn";
+    if (ts_lrp) {
+        const char *lrp_route_filter = smap_get(&ts_lrp->options,
+                                         filter_direction);
+        if (lrp_route_filter) {
+            ds_put_format(&filter_list, "%s,", lrp_route_filter);
+        }
+    }
+    const char *lr_route_filter = smap_get(&nb_lr->options,
+                                           filter_direction);
+    if (lr_route_filter) {
+        ds_put_format(&filter_list, "%s,", lr_route_filter);
+    }
+
+    if (!filter_list.length) {
+        ds_destroy(&filter_list);
+        return true;
+    }
+
+    struct in6_addr bl_prefix;
+    unsigned int bl_plen;
+    char *cur, *next, *start;
+    next = start = xstrdup(ds_cstr(&filter_list));
+    bool matched = false;
+    while ((cur = strsep(&next, ",")) && *cur) {
+        if (!ip46_parse_cidr(cur, &bl_prefix, &bl_plen)) {
+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
+            VLOG_WARN_RL(&rl, "Bad format in logical router/logical router"
+                         "port options: %s: %s. CIDR expected.",
+                         filter_direction, cur);
+            matched = true;
+            break;
+        }
+
+        if (IN6_IS_ADDR_V4MAPPED(&bl_prefix) != IN6_IS_ADDR_V4MAPPED(prefix)) {
+            continue;
+        }
+
+        /* 192.168.0.0/16 does not belong to 192.168.0.0/17 */
+        if (plen < bl_plen) {
+            continue;
+        }
+
+        if (IN6_IS_ADDR_V4MAPPED(prefix)) {
+            ovs_be32 bl_prefix_v4 = in6_addr_get_mapped_ipv4(&bl_prefix);
+            ovs_be32 prefix_v4 = in6_addr_get_mapped_ipv4(prefix);
+            ovs_be32 mask = be32_prefix_mask(bl_plen);
+
+            if ((prefix_v4 & mask) != (bl_prefix_v4 & mask)) {
+                continue;
+            }
+        } else {
+            struct in6_addr mask = ipv6_create_mask(plen);
+            /* First calculate the difference between bl_prefix and prefix, so
+             * use the bl mask to ensure prefixes are correctly validated.
+             * e.g.: 2005:1734:5678::/50 is a subnet of 2005:1234::/21 */
+            struct in6_addr m_prefixes = ipv6_addr_bitand(prefix, &bl_prefix);
+            struct in6_addr m_prefix = ipv6_addr_bitand(&m_prefixes, &mask);
+            struct in6_addr m_bl_prefix = ipv6_addr_bitand(&bl_prefix, &mask);
+            if (!ipv6_addr_equals(&m_prefix, &m_bl_prefix)) {
+                continue;
+            }
+        }
+        matched = true;
+        break;
+    }
+    free(start);
+    ds_destroy(&filter_list);
+    return matched;
+}
+
 static bool
 prefix_is_deny_listed(const struct smap *nb_options,
                       struct in6_addr *prefix,
@@ -1134,7 +1214,9 @@ static bool
 route_need_advertise(const char *policy,
                      struct in6_addr *prefix,
                      unsigned int plen,
-                     const struct smap *nb_options)
+                     const struct smap *nb_options,
+                     const struct nbrec_logical_router *nb_lr,
+                     const struct nbrec_logical_router_port *ts_lrp)
 {
     if (!smap_get_bool(nb_options, "ic-route-adv", false)) {
         return false;
@@ -1156,6 +1238,11 @@ route_need_advertise(const char *policy,
     if (prefix_is_deny_listed(nb_options, prefix, plen)) {
         return false;
     }
+
+    if (!prefix_is_filtered(prefix, plen, nb_lr, ts_lrp, true)) {
+        return false;
+    }
+
     return true;
 }
 
@@ -1210,7 +1297,8 @@ add_static_to_routes_ad(
     const struct nbrec_logical_router *nb_lr,
     const struct lport_addresses *nexthop_addresses,
     const struct smap *nb_options,
-    const char *route_tag)
+    const char *route_tag,
+    const struct nbrec_logical_router_port *ts_lrp)
 {
     struct in6_addr prefix, nexthop;
     unsigned int plen;
@@ -1219,7 +1307,8 @@ add_static_to_routes_ad(
         return;
     }
 
-    if (!route_need_advertise(nb_route->policy, &prefix, plen, nb_options)) {
+    if (!route_need_advertise(nb_route->policy, &prefix, plen, nb_options,
+                              nb_lr, ts_lrp)) {
         return;
     }
 
@@ -1260,7 +1349,8 @@ add_network_to_routes_ad(struct hmap *routes_ad, const 
char *network,
                          const struct lport_addresses *nexthop_addresses,
                          const struct smap *nb_options,
                          const struct nbrec_logical_router *nb_lr,
-                         const char *route_tag)
+                         const char *route_tag,
+                         const struct nbrec_logical_router_port *ts_lrp)
 {
     struct in6_addr prefix, nexthop;
     unsigned int plen;
@@ -1268,7 +1358,8 @@ add_network_to_routes_ad(struct hmap *routes_ad, const 
char *network,
         return;
     }
 
-    if (!route_need_advertise(NULL, &prefix, plen, nb_options)) {
+    if (!route_need_advertise(NULL, &prefix, plen, nb_options,
+                              nb_lr, ts_lrp)) {
         VLOG_DBG("Route ad: skip network %s of lrp %s.",
                  network, nb_lrp->name);
         return;
@@ -1322,7 +1413,8 @@ static bool
 route_need_learn(const struct nbrec_logical_router *lr,
                  const struct icsbrec_route *isb_route,
                  struct in6_addr *prefix, unsigned int plen,
-                 const struct smap *nb_options)
+                 const struct smap *nb_options,
+                 const struct nbrec_logical_router_port *ts_lrp)
 {
     if (!smap_get_bool(nb_options, "ic-route-learn", false)) {
         return false;
@@ -1341,6 +1433,10 @@ route_need_learn(const struct nbrec_logical_router *lr,
         return false;
     }
 
+    if (!prefix_is_filtered(prefix, plen, lr, ts_lrp, false)) {
+        return false;
+    }
+
     if (route_has_local_gw(lr, isb_route->route_table, isb_route->ip_prefix)) {
         VLOG_DBG("Skip learning %s (rtb:%s) route, as we've got one with "
                  "local GW", isb_route->ip_prefix, isb_route->route_table);
@@ -1470,7 +1566,7 @@ sync_learned_routes(struct ic_context *ctx,
                 continue;
             }
             if (!route_need_learn(ic_lr->lr, isb_route, &prefix, plen,
-                                  &nb_global->options)) {
+                                  &nb_global->options, lrp)) {
                 continue;
             }
 
@@ -1656,7 +1752,8 @@ build_ts_routes_to_adv(struct ic_context *ctx,
                        struct lport_addresses *ts_port_addrs,
                        const struct nbrec_nb_global *nb_global,
                        const char *ts_route_table,
-                       const char *route_tag)
+                       const char *route_tag,
+                       const struct nbrec_logical_router_port *ts_lrp)
 {
     const struct nbrec_logical_router *lr = ic_lr->lr;
 
@@ -1679,7 +1776,7 @@ build_ts_routes_to_adv(struct ic_context *ctx,
         } else if (!strcmp(ts_route_table, nb_route->route_table)) {
             /* It may be a route to be advertised */
             add_static_to_routes_ad(routes_ad, nb_route, lr, ts_port_addrs,
-                                    &nb_global->options, route_tag);
+                                    &nb_global->options, route_tag, ts_lrp);
         }
     }
 
@@ -1691,7 +1788,7 @@ build_ts_routes_to_adv(struct ic_context *ctx,
                 add_network_to_routes_ad(routes_ad, lrp->networks[j], lrp,
                                          ts_port_addrs,
                                          &nb_global->options,
-                                         lr, route_tag);
+                                         lr, route_tag, ts_lrp);
             }
         } else {
             /* The router port of the TS port is ignored. */
@@ -1755,7 +1852,7 @@ collect_lr_routes(struct ic_context *ctx,
             route_tag = "";
         }
         build_ts_routes_to_adv(ctx, ic_lr, routes_ad, &ts_port_addrs,
-                               nb_global, route_table, route_tag);
+                               nb_global, route_table, route_tag, lrp);
         destroy_lport_addresses(&ts_port_addrs);
     }
 }
diff --git a/ovn-nb.xml b/ovn-nb.xml
index 20d30dd58..550495cea 100644
--- a/ovn-nb.xml
+++ b/ovn-nb.xml
@@ -3024,6 +3024,28 @@ or
         derived from OvS default datapath limit.
       </column>
 
+      <column name="options" key="ic-route-filter-adv">
+        <p>
+          This option expects list of CIDRs delimited by "," that's present
+          in the Logical Router. A route will not be advertised if the
+          route's prefix belongs to any of the CIDRs listed.
+
+          This allows to filter CIDR prefixes in the process of advertising
+          routes in <code>ovn-ic</code> daemon.
+        </p>
+      </column>
+
+      <column name="options" key="ic-route-filter-learn">
+        <p>
+          This option expects list of CIDRs delimited by "," that's present
+          in the Logical Router. A route will not be learned if the
+          route's prefix belongs to any of the CIDRs listed.
+
+          This allows to filter CIDR prefixes in the process of learning
+          routes in <code>ovn-ic</code> daemon.
+        </p>
+      </column>
+
       <column name="options" key="dynamic-routing" type='{"type": "boolean"}'>
         If set to <code>true</code> then this <ref table="Logical_Router"/>
         can participate in dynamic routing with components outside of OVN.
@@ -3884,6 +3906,28 @@ or
         </p>
       </column>
 
+      <column name="options" key="ic-route-filter-adv">
+        <p>
+          This option expects list of CIDRs delimited by "," that's present
+          in the Logical Router Port. A route will be advertised if the
+          route's prefix belongs to any of the CIDRs listed.
+
+          This allows to filter CIDR prefixes in the process of advertising
+          routes in <code>ovn-ic</code> daemon.
+        </p>
+      </column>
+
+      <column name="options" key="ic-route-filter-learn">
+        <p>
+          This option expects list of CIDRs delimited by "," that's present
+          in the Logical Router Port. A route will be learned if the
+          route's prefix belongs to any of the CIDRs listed.
+
+          This allows to filter CIDR prefixes in the process of learning
+          routes in <code>ovn-ic</code> daemon.
+        </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 02191f13a..0fc955ce5 100644
--- a/tests/ovn-ic.at
+++ b/tests/ovn-ic.at
@@ -2651,6 +2651,128 @@ OVN_CLEANUP_IC([az1], [az2])
 AT_CLEANUP
 ])
 
+OVN_FOR_EACH_NORTHD([
+AT_SETUP([ovn-ic -- prefix filter -- filtering routes])
+
+ovn_init_ic_db
+ovn-ic-nbctl ts-add ts1
+
+for i in 1 2; do
+    ovn_start az$i
+    ovn_as az$i
+
+    # Enable route learning at AZ level
+    check ovn-nbctl set nb_global . options:ic-route-learn=true
+    # Enable route advertising at AZ level
+    check ovn-nbctl set nb_global . options:ic-route-adv=true
+done
+
+# Create new transit switche and LRs. Test topology is next:
+# logical router (lr11) - transit switch (ts1) - logical router (lr12)
+
+# create lr11, lr12 and connect them to ts1
+for i in 1 2; do
+    ovn_as az$i
+
+    lr=lr1$i
+    check ovn-nbctl lr-add $lr
+
+    lrp=lrp-$lr-ts1
+    lsp=lsp-ts1-$lr
+    # Create LRP and connect to TS
+    check ovn-nbctl lrp-add $lr $lrp aa:aa:aa:aa:ab:0$i 169.254.101.$i/24 
fe80:10::$i/64
+    check ovn-nbctl lsp-add ts1 $lsp \
+            -- lsp-set-addresses $lsp router \
+            -- lsp-set-type $lsp router \
+            -- lsp-set-options $lsp router-port=$lrp
+done
+
+# Create directly-connected route in lr12
+
+ovn_as az2 ovn-nbctl lrp-add lr12 lrp-lr12-1 aa:aa:aa:aa:bb:01 
"192.168.12.1/24" "2001:db12::1/64"
+ovn_as az2 ovn-nbctl lrp-add lr12 lrp-lr12-2 aa:aa:aa:aa:bb:02 
"192.168.2.1/24" "2001:db22::1/64"
+
+# Create directly-connected route in lr11
+ovn_as az1 ovn-nbctl --wait=sb lrp-add lr11 lrp-lr11 aa:aa:aa:aa:cc:01 
"192.168.11.1/24" "2001:db11::1/64"
+
+check ovn-ic-nbctl --wait=sb sync
+
+# Test direct routes from lr12 were learned to lr11
+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.12.0/24 169.254.101.2
+192.168.2.0/24 169.254.101.2
+])
+
+# Test direct routes from lr11 were learned to lr12
+OVS_WAIT_FOR_OUTPUT([ovn_as az2 ovn-nbctl lr-route-list lr12 | grep 192.168 |
+             grep learned | awk '{print $1, $2}' | sort ], [0], [dnl
+192.168.11.0/24 169.254.101.1
+])
+
+ovn_as az2 check ovn-nbctl set logical_router_port lrp-lr12-ts1 
options:ic-route-filter-adv=192.168.12.0/24
+# Test direct routes from lr12 were learned to lr11
+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.12.0/24 169.254.101.2
+])
+
+# set ic-route-filter-adv with invalid CIDR to advertise all routes
+ovn_as az2 check ovn-nbctl set logical_router_port lrp-lr12-ts1 
options:ic-route-filter-adv="192.168.12.0/24,invalid"
+
+# Test direct routes from lr12 were learned to lr11
+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.12.0/24 169.254.101.2
+192.168.2.0/24 169.254.101.2
+])
+
+ovn_as az2 check ovn-nbctl remove logical_router_port lrp-lr12-ts1 options 
ic-route-filter-adv
+ovn_as az1 check ovn-nbctl set logical_router_port lrp-lr11-ts1 
options:ic-route-filter-learn=192.168.2.0/24
+
+# Test direct routes from lr12 were learned to lr11
+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.2.0/24 169.254.101.2
+])
+
+ovn_as az1 check ovn-nbctl remove logical_router_port lrp-lr11-ts1 options 
ic-route-filter-learn
+
+# Test direct routes from lr12 were learned to lr11
+OVS_WAIT_FOR_OUTPUT([ovn_as az1 ovn-nbctl lr-route-list lr11 | grep 2001 |
+             grep learned | awk '{print $1, $2}' | sort ], [0], [dnl
+2001:db12::/64 fe80:10::2
+2001:db22::/64 fe80:10::2
+])
+
+ovn_as az2 check ovn-nbctl set logical_router_port lrp-lr12-ts1 
options:ic-route-filter-adv=2001:db22::/64
+
+# Test direct routes from lr12 were learned to lr11
+OVS_WAIT_FOR_OUTPUT([ovn_as az1 ovn-nbctl lr-route-list lr11 | grep 2001 |
+             grep learned | awk '{print $1, $2}' | sort ], [0], [dnl
+2001:db22::/64 fe80:10::2
+])
+
+ovn_as az2 check ovn-nbctl remove logical_router_port lrp-lr12-ts1 options 
ic-route-filter-adv
+# Test direct routes from lr12 were learned to lr11
+OVS_WAIT_FOR_OUTPUT([ovn_as az1 ovn-nbctl lr-route-list lr11 | grep 2001 |
+             grep learned | awk '{print $1, $2}' | sort ], [0], [dnl
+2001:db12::/64 fe80:10::2
+2001:db22::/64 fe80:10::2
+])
+
+ovn_as az1 check ovn-nbctl set logical_router_port lrp-lr11-ts1 
options:ic-route-filter-learn=2001:db12::/64
+# Test direct routes from lr12 were learned to lr11
+OVS_WAIT_FOR_OUTPUT([ovn_as az1 ovn-nbctl lr-route-list lr11 | grep 2001 |
+             grep learned | awk '{print $1, $2}' | sort ], [0], [dnl
+2001:db12::/64 fe80:10::2
+])
+
+OVN_CLEANUP_IC([az1], [az2])
+
+AT_CLEANUP
+])
+
 OVN_FOR_EACH_NORTHD([
 AT_SETUP([spine-leaf: 2 AZs, 2 HVs, 2 LSs, connected via transit spine switch])
 AT_KEYWORDS([spine leaf])
-- 
2.34.1


-- 




_'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
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to