On Thu, Sep 11, 2025 at 09:09:53AM +0000, Indrajitt Valsaraj wrote:
> This commit adds the ability for ovn-ic to deny filter routes
> learnt/advertised between AZs. This commit also fixes a documentation
> error for the ic-route-filter-adv option.
> 
> Signed-off-by: Indrajitt Valsaraj <indrajitt.valsa...@nutanix.com>
> 
> ---
> v1:
>  - Address review comments from Mark
> ---
>  NEWS            |   3 +
>  ic/ovn-ic.c     |  53 +++++++++----
>  ovn-nb.xml      |  66 +++++++++++++---
>  tests/ovn-ic.at | 194 ++++++++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 291 insertions(+), 25 deletions(-)
> 
> diff --git a/NEWS b/NEWS
> index 0cce1790d..44ec47b5e 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -41,6 +41,9 @@ Post v25.03.0
>     - Added support for running tests from the 'check-kernel' system test 
> target
>       under retis by setting OVS_TEST_WITH_RETIS=yes.  See the 'Testing' 
> section
>       of the documentation for more details.
> +   - Added "ic-route-deny-adv" and "ic-route-deny-learn" options to
> +     the Logical_Router/Logical_Router_Port tables to allow users to
> +     deny filter advertised/learned IC routes.
> 
>  OVN v25.03.0 - 07 Mar 2025
>  --------------------------
> diff --git a/ic/ovn-ic.c b/ic/ovn-ic.c
> index caffa6fe0..71174ae01 100644
> --- a/ic/ovn-ic.c
> +++ b/ic/ovn-ic.c
> @@ -1109,26 +1109,50 @@ prefix_is_filtered(struct in6_addr *prefix,
>  }
> 
>  static bool
> -prefix_is_deny_listed(const struct smap *nb_options,
> -                      struct in6_addr *prefix,
> -                      unsigned int plen)
> -{
> -    const char *filter_name = "ic-route-denylist";
> -    const char *denylist = smap_get(nb_options, filter_name);
> -    if (!denylist || !denylist[0]) {
> -        denylist = smap_get(nb_options, "ic-route-blacklist");
> -        if (!denylist || !denylist[0]) {
> -            return false;
> +prefix_is_deny_filtered(struct in6_addr *prefix,
> +                        unsigned int plen,
> +                        const struct smap *nb_options,
> +                        const struct nbrec_logical_router *nb_lr,
> +                        const struct nbrec_logical_router_port *ts_lrp,
> +                        bool is_advertisement)
> +{
> +    struct ds deny_list = DS_EMPTY_INITIALIZER;
> +    const char *deny_key = is_advertisement ? "ic-route-deny-adv" :
> +                                              "ic-route-deny-learn";
> +
> +    if (ts_lrp) {
> +        const char *lrp_deny_filter = smap_get(&ts_lrp->options, deny_key);
> +        if (lrp_deny_filter) {
> +            ds_put_format(&deny_list, "%s,", lrp_deny_filter);
> +        }
> +    }
> +
> +    if (nb_lr) {
> +        const char *lr_deny_filter = smap_get(&nb_lr->options, deny_key);
> +        if (lr_deny_filter) {
> +            ds_put_format(&deny_list, "%s,", lr_deny_filter);
> +        }
> +    }
> +
> +    if (nb_options) {
> +        const char *global_deny = smap_get(nb_options, "ic-route-denylist");
> +        if (!global_deny || !global_deny[0]) {
> +            global_deny = smap_get(nb_options, "ic-route-blacklist");
> +        }
> +        if (global_deny && global_deny[0]) {
> +            ds_put_format(&deny_list, "%s,", global_deny);
>          }
>      }
> 
>      struct sset prefix_set = SSET_INITIALIZER(&prefix_set);
> -    sset_from_delimited_string(&prefix_set, denylist, ",");
> +    sset_from_delimited_string(&prefix_set, ds_cstr(&deny_list), ",");
> 
>      bool denied = false;
>      if (!sset_is_empty(&prefix_set)) {
> -        denied = find_prefix_in_set(prefix, plen, &prefix_set, filter_name);
> +        denied = find_prefix_in_set(prefix, plen, &prefix_set, deny_key);
>      }
> +
> +    ds_destroy(&deny_list);
>      sset_destroy(&prefix_set);
>      return denied;
>  }
> @@ -1158,7 +1182,8 @@ route_need_advertise(const char *policy,
>          return false;
>      }
> 
> -    if (prefix_is_deny_listed(nb_options, prefix, plen)) {
> +    if (prefix_is_deny_filtered(prefix, plen, nb_options,
> +                                nb_lr, ts_lrp, true)) {
>          return false;
>      }
> 
> @@ -1511,7 +1536,7 @@ route_need_learn(const struct nbrec_logical_router *lr,
>          return false;
>      }
> 
> -    if (prefix_is_deny_listed(nb_options, prefix, plen)) {
> +    if (prefix_is_deny_filtered(prefix, plen, nb_options, lr, ts_lrp, 
> false)) {
>          return false;
>      }
> 
> diff --git a/ovn-nb.xml b/ovn-nb.xml
> index 4a7581807..54f4fd2bd 100644
> --- a/ovn-nb.xml
> +++ b/ovn-nb.xml
> @@ -3231,7 +3231,7 @@ or
>        <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
> +          in the Logical Router. 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
> @@ -3240,6 +3240,28 @@ or
>        </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 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="ic-route-deny-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-deny-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
> @@ -4236,18 +4258,40 @@ or
>             This allows to filter CIDR prefixes in the process of advertising
>             routes in <code>ovn-ic</code> daemon.
>           </p>
> -       </column>
> +      </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.
> +      <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>
> +          This allows to filter CIDR prefixes in the process of learning
> +          routes in <code>ovn-ic</code> daemon.
> +        </p>
> +      </column>
> +
> +      <column name="options" key="ic-route-deny-adv">
> +        <p>
> +          This option expects list of CIDRs delimited by "," that's present
> +          in the Logical Router Port. 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-deny-learn">
> +        <p>
> +          This option expects list of CIDRs delimited by "," that's present 
> in
> +          the Logical Router Port. 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>
>      </group>
> 
>      <group title="Attachment">
> diff --git a/tests/ovn-ic.at b/tests/ovn-ic.at
> index 49a409015..b866f9b8b 100644
> --- a/tests/ovn-ic.at
> +++ b/tests/ovn-ic.at
> @@ -3640,6 +3640,200 @@ OVS_WAIT_FOR_OUTPUT([ovn_as az1 ovn-nbctl 
> lr-route-list lr11 | grep 2001 |
>  2001:db12::/64 fe80:10::2
>  ])
> 
> +OVN_CLEANUP_IC([az1], [az2])
> +AT_CLEANUP
> +])
> +
> +OVN_FOR_EACH_NORTHD([
> +AT_SETUP([ovn-ic -- prefix filter -- deny route adv])
> +ovn_init_ic_db
> +ovn-ic-nbctl ts-add ts1
> +for i in 1 2; do
> +    ovn_start az$i
> +    ovn_as az$i
> +    check ovn-nbctl set nb_global . options:ic-route-learn=true
> +    check ovn-nbctl set nb_global . options:ic-route-adv=true
> +done
> +
> +# Create routers and connect to transit switch
> +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
> +    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
> +
> +# Add directly connected routes to lr12 (first two test prefixes reused)
> +ovn_as az2 check ovn-nbctl lrp-add lr12 lrp-lr12-1 aa:aa:aa:aa:cc:01 
> "192.168.100.1/24" "2001:db12::1/64"
> +ovn_as az2 check ovn-nbctl lrp-add lr12 lrp-lr12-2 aa:aa:aa:aa:cc:02 
> "192.168.200.1/24" "2001:db22::1/64"
> +
> +# Sync IC DB
> +check ovn-ic-nbctl --wait=sb sync
> +
> +# Validate lr11 learns both IPv4 and IPv6 routes from lr12
> +OVS_WAIT_FOR_OUTPUT([ovn_as az1 ovn-nbctl lr-route-list lr11 | grep -E 
> '192.168|2001' | grep learned | awk '{print $1, $2}' | sort], [0], [dnl
> +192.168.100.0/24 169.254.101.2
> +192.168.200.0/24 169.254.101.2
> +2001:db12::/64 fe80:10::2
> +2001:db22::/64 fe80:10::2
> +])
> +
> +# Set deny-adv on lrp-lr12-ts1 for 192.168.100.0/24
> +ovn_as az2 check ovn-nbctl set logical_router_port lrp-lr12-ts1 
> options:ic-route-deny-adv=192.168.100.0/24
> +
> +# Only 192.168.200.0/24 should now be learned
> +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.200.0/24 169.254.101.2
> +])
> +
> +# Add IPv6 deny prefix too
> +ovn_as az2 check ovn-nbctl set logical_router_port lrp-lr12-ts1 
> options:ic-route-deny-adv="192.168.100.0/24,2001:db12::/64"
> +
> +# Only 2001:db22::/64 should be learned now
> +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
> +])
> +
> +# Remove deny-adv and validate all are learned again
> +ovn_as az2 check ovn-nbctl remove logical_router_port lrp-lr12-ts1 options 
> ic-route-deny-adv
> +
> +OVS_WAIT_FOR_OUTPUT([ovn_as az1 ovn-nbctl lr-route-list lr11 | grep learned 
> | awk '{print $1, $2}' | sort], [0], [dnl
> +192.168.100.0/24 169.254.101.2
> +192.168.200.0/24 169.254.101.2
> +2001:db12::/64 fe80:10::2
> +2001:db22::/64 fe80:10::2
> +])
> +
> +# --- Test ic-route-deny-learn ---
> +
> +# Set deny-learn on lrp-lr11-ts1 for 192.168.200.0/24 and 2001:db22::/64
> +ovn_as az1 check ovn-nbctl set logical_router_port lrp-lr11-ts1 
> options:ic-route-deny-learn="192.168.200.0/24,2001:db22::/64"
> +
> +# Now lr11 should only learn 192.168.100.0/24 and 2001:db12::/64
> +OVS_WAIT_FOR_OUTPUT([ovn_as az1 ovn-nbctl lr-route-list lr11 | grep -E 
> '192.168|2001' | grep learned | awk '{print $1, $2}' | sort], [0], [dnl
> +192.168.100.0/24 169.254.101.2
> +2001:db12::/64 fe80:10::2
> +])
> +
> +# Remove deny-learn and confirm full route learning resumes
> +ovn_as az1 check ovn-nbctl remove logical_router_port lrp-lr11-ts1 options 
> ic-route-deny-learn
> +
> +OVS_WAIT_FOR_OUTPUT([ovn_as az1 ovn-nbctl lr-route-list lr11 | grep -E 
> '192.168|2001' | grep learned | awk '{print $1, $2}' | sort], [0], [dnl
> +192.168.100.0/24 169.254.101.2
> +192.168.200.0/24 169.254.101.2
> +2001:db12::/64 fe80:10::2
> +2001:db22::/64 fe80:10::2
> +])
> +
> +# --- Test setting deny options on logical router ---
> +
> +# Set deny-adv on lr12 for 192.168.100.0/24
> +ovn_as az2 check ovn-nbctl set logical_router lr12 
> options:ic-route-deny-adv="192.168.100.0/24"
> +
> +# Sync again after router option is applied
> +check ovn-ic-nbctl --wait=sb sync
> +
> +# Only 192.168.200.0/24 should now be learned
> +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.200.0/24 169.254.101.2
> +])
> +
> +# Set deny-learn on lr11 for 2001:db22::/64
> +ovn_as az1 check ovn-nbctl set logical_router lr11 
> options:ic-route-deny-learn="2001:db22::/64"
> +
> +# Only 2001:db12::/64 should now be learned
> +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
> +])
> +
> +# Clean up router-level options
> +ovn_as az2 check ovn-nbctl remove logical_router lr12 options 
> ic-route-deny-adv
> +ovn_as az1 check ovn-nbctl remove logical_router lr11 options 
> ic-route-deny-learn
> +
> +# Confirm all routes are back
> +OVS_WAIT_FOR_OUTPUT([ovn_as az1 ovn-nbctl lr-route-list lr11 | grep learned 
> | awk '{print $1, $2}' | sort], [0], [dnl
> +192.168.100.0/24 169.254.101.2
> +192.168.200.0/24 169.254.101.2
> +2001:db12::/64 fe80:10::2
> +2001:db22::/64 fe80:10::2
> +])
> +
> +# --- Test ic-route-denylist (global option) ---
> +
> +# Set global denylist to block 192.168.100.0/24 and 2001:db12::/64
> +ovn_as az2 check ovn-nbctl set nb_global . 
> options:ic-route-denylist="192.168.100.0/24,2001:db12::/64"
> +
> +# Now lr11 should only learn 192.168.200.0/24 and 2001:db22::/64
> +OVS_WAIT_FOR_OUTPUT([ovn_as az1 ovn-nbctl lr-route-list lr11 | grep -E 
> '192.168|2001' | grep learned | awk '{print $1, $2}' | sort], [0], [dnl
> +192.168.200.0/24 169.254.101.2
> +2001:db22::/64 fe80:10::2
> +])
> +
> +# Remove global denylist
> +ovn_as az2 check ovn-nbctl remove nb_global . options ic-route-denylist
> +
> +# Confirm all routes are learned again
> +OVS_WAIT_FOR_OUTPUT([ovn_as az1 ovn-nbctl lr-route-list lr11 | grep -E 
> '192.168|2001' | grep learned | awk '{print $1, $2}' | sort], [0], [dnl
> +192.168.100.0/24 169.254.101.2
> +192.168.200.0/24 169.254.101.2
> +2001:db12::/64 fe80:10::2
> +2001:db22::/64 fe80:10::2
> +])
> +
> +# --- Test combination of deny options ---
> +
> +# Set all deny filters
> +ovn_as az2 check ovn-nbctl set logical_router_port lrp-lr12-ts1 
> options:ic-route-deny-adv="192.168.100.0/24"
> +ovn_as az1 check ovn-nbctl set logical_router_port lrp-lr11-ts1 
> options:ic-route-deny-learn="192.168.200.0/24"
> +ovn_as az2 check ovn-nbctl set nb_global . 
> options:ic-route-denylist="2001:db12::/64"
> +
> +# Validate only 2001:db22::/64 is learned
> +OVS_WAIT_FOR_OUTPUT([ovn_as az1 ovn-nbctl lr-route-list lr11 | grep -E 
> '192.168|2001' | grep learned | awk '{print $1, $2}' | sort], [0], [dnl
> +2001:db22::/64 fe80:10::2
> +])
> +
> +# Clean up for interaction tests
> +ovn_as az2 check ovn-nbctl remove logical_router_port lrp-lr12-ts1 options 
> ic-route-deny-adv
> +ovn_as az1 check ovn-nbctl remove logical_router_port lrp-lr11-ts1 options 
> ic-route-deny-learn
> +ovn_as az2 check ovn-nbctl remove nb_global . options ic-route-denylist
> +
> +# --- Test interactions between different filter options ---
> +
> +# Test ic-route-filter-adv vs ic-route-deny-adv precedence
> +# Set both filter-adv (allow) and deny-adv (deny) for same CIDR - deny 
> should take precedence
> +ovn_as az2 check ovn-nbctl set logical_router_port lrp-lr12-ts1 
> options:ic-route-filter-adv="192.168.100.0/24,192.168.200.0/24"
> +ovn_as az2 check ovn-nbctl set logical_router_port lrp-lr12-ts1 
> options:ic-route-deny-adv="192.168.100.0/24"
> +
> +# Only 192.168.200.0/24 should be learned (192.168.100.0/24 denied despite 
> being in filter-adv)
> +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.200.0/24 169.254.101.2
> +])
> +
> +# Clean up
> +ovn_as az2 check ovn-nbctl remove logical_router_port lrp-lr12-ts1 options 
> ic-route-filter-adv
> +ovn_as az2 check ovn-nbctl remove logical_router_port lrp-lr12-ts1 options 
> ic-route-deny-adv
> +
> +# Test cross-router interaction: filter-adv on lr12 vs deny-learn on lr11
> +# lr12 allows advertising 192.168.100.0/24, but lr11 denies learning it
> +ovn_as az2 check ovn-nbctl set logical_router_port lrp-lr12-ts1 
> options:ic-route-filter-adv="192.168.100.0/24"
> +ovn_as az1 check ovn-nbctl set logical_router_port lrp-lr11-ts1 
> options:ic-route-deny-learn="192.168.100.0/24"
> +
> +# 192.168.100.0/24 should not be learned (deny-learn takes precedence over 
> filter-adv)
> +# No routes should be learned since only 192.168.100.0/24 is allowed to be 
> advertised but it's denied for learning
> +OVS_WAIT_FOR_OUTPUT([ovn_as az1 ovn-nbctl lr-route-list lr11 | grep 192.168 
> | grep learned | wc -l], [0], [0
> +])
> +
> +# Clean up interaction tests
> +ovn_as az2 check ovn-nbctl remove logical_router_port lrp-lr12-ts1 options 
> ic-route-filter-adv
> +ovn_as az1 check ovn-nbctl remove logical_router_port lrp-lr11-ts1 options 
> ic-route-deny-learn
> +
> +
>  OVN_CLEANUP_IC([az1], [az2])
>  AT_CLEANUP
>  ])
> --
> 2.39.3
> _______________________________________________
> dev mailing list
> d...@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> 

Patch looks good to me

Acked-by: Mairtin O'Loingsigh <moloi...@redhat.com>

_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to