Acked-by: Mark Michelson <[email protected]>

On 6/2/22 11:22, Dumitru Ceara wrote:
Commit a075230e4a0f ("Use ct_mark for masked access to make flows
HW-offloading friendly.") started using the ct_mark.blocked and
ct_mark.ecmp_reply_port ct_label.  In usual scenarios this new feature would
be picked up when the next stable release becomes available (i.e., 22.06.0).
However, the commit was also backported to stable branches (branch-22.03 and
branch-21.12).

While the supported upgrade scenario for OVN when moving to a new
stable release is to ensure that ovn-controllers are upgraded first,
it's not really clear that this restriction applies to "z-stream"
upgrades too (e.g., from v21.12.1 to v21.12.2).

Some CMSs, like RHEV (Red Hat Virtualization), expect ovn-controllers
running older code from a stable branch to be able to interpret all
Southbound contents generated by ovn-northd instances built from the
same or newer versions of the stable branch.

Ensure that ct_mark.blocked and ecmp_reply_port are used only when all
chassis registered in the Southbound support it.

Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=2091565
Signed-off-by: Dumitru Ceara <[email protected]>
---
  controller/chassis.c   |    6 ++
  include/ovn/features.h |    5 +-
  northd/northd.c        |  156 ++++++++++++++++++++++++++++++++----------------
  northd/northd.h        |    1
  tests/ovn-northd.at    |   92 ++++++++++++++++++++++++++++
  5 files changed, 206 insertions(+), 54 deletions(-)

diff --git a/controller/chassis.c b/controller/chassis.c
index 239658461..656f4c331 100644
--- a/controller/chassis.c
+++ b/controller/chassis.c
@@ -351,6 +351,7 @@ chassis_build_other_config(const struct ovs_chassis_cfg 
*ovs_cfg,
                   ovs_cfg->is_interconn ? "true" : "false");
      smap_replace(config, OVN_FEATURE_PORT_UP_NOTIF, "true");
      smap_replace(config, OVN_FEATURE_CT_LB_MARK, "true");
+    smap_replace(config, OVN_FEATURE_CT_MASKED_MARK, "true");
  }
/*
@@ -461,6 +462,11 @@ chassis_other_config_changed(const struct ovs_chassis_cfg 
*ovs_cfg,
          return true;
      }
+ if (!smap_get_bool(&chassis_rec->other_config, OVN_FEATURE_CT_MASKED_MARK,
+                       false)) {
+        return true;
+    }
+
      return false;
  }
diff --git a/include/ovn/features.h b/include/ovn/features.h
index 09f002287..9aad22eda 100644
--- a/include/ovn/features.h
+++ b/include/ovn/features.h
@@ -21,8 +21,9 @@
  #include "smap.h"
/* ovn-controller supported feature names. */
-#define OVN_FEATURE_PORT_UP_NOTIF "port-up-notif"
-#define OVN_FEATURE_CT_LB_MARK    "ct-lb-mark"
+#define OVN_FEATURE_PORT_UP_NOTIF  "port-up-notif"
+#define OVN_FEATURE_CT_LB_MARK     "ct-lb-mark"
+#define OVN_FEATURE_CT_MASKED_MARK "ct-masked-mark"
/* OVS datapath supported features. Based on availability OVN might generate
   * different types of openflows.
diff --git a/northd/northd.c b/northd/northd.c
index 6e2f2a880..09b19d300 100644
--- a/northd/northd.c
+++ b/northd/northd.c
@@ -404,14 +404,20 @@ build_chassis_features(const struct northd_input 
*input_data,
  {
      const struct sbrec_chassis *chassis;
+ chassis_features->ct_lb_mark = true;
+    chassis_features->ct_masked_mark = true;
      SBREC_CHASSIS_TABLE_FOR_EACH (chassis, input_data->sbrec_chassis) {
-        if (!smap_get_bool(&chassis->other_config, OVN_FEATURE_CT_LB_MARK,
-                           false)) {
+        if (chassis_features->ct_lb_mark
+            && !smap_get_bool(&chassis->other_config,
+                              OVN_FEATURE_CT_LB_MARK, false)) {
              chassis_features->ct_lb_mark = false;
-            return;
+        }
+        if (chassis_features->ct_masked_mark
+            && !smap_get_bool(&chassis->other_config,
+                              OVN_FEATURE_CT_MASKED_MARK, false)) {
+            chassis_features->ct_masked_mark = false;
          }
      }
-    chassis_features->ct_lb_mark = true;
  }
  
  struct ovn_chassis_qdisc_queues {
@@ -5868,7 +5874,9 @@ build_pre_stateful(struct ovn_datapath *od,
  }
static void
-build_acl_hints(struct ovn_datapath *od, struct hmap *lflows)
+build_acl_hints(struct ovn_datapath *od,
+                const struct chassis_features *features,
+                struct hmap *lflows)
  {
      /* This stage builds hints for the IN/OUT_ACL stage. Based on various
       * combinations of ct flags packets may hit only a subset of the logical
@@ -5890,6 +5898,7 @@ build_acl_hints(struct ovn_datapath *od, struct hmap 
*lflows)
for (size_t i = 0; i < ARRAY_SIZE(stages); i++) {
          enum ovn_stage stage = stages[i];
+        const char *match;
/* In any case, advance to the next stage. */
          if (!od->has_acls && !od->has_lb_vip) {
@@ -5919,8 +5928,10 @@ build_acl_hints(struct ovn_datapath *od, struct hmap 
*lflows)
           *   REGBIT_ACL_HINT_ALLOW_NEW.
           * - drop ACLs.
           */
-        ovn_lflow_add(lflows, od, stage, 6,
-                      "!ct.new && ct.est && !ct.rpl && ct_mark.blocked == 1",
+        match = features->ct_masked_mark
+                ? "!ct.new && ct.est && !ct.rpl && ct_mark.blocked == 1"
+                : "!ct.new && ct.est && !ct.rpl && ct_label.blocked == 1";
+        ovn_lflow_add(lflows, od, stage, 6, match,
                        REGBIT_ACL_HINT_ALLOW_NEW " = 1; "
                        REGBIT_ACL_HINT_DROP " = 1; "
                        "next;");
@@ -5939,8 +5950,10 @@ build_acl_hints(struct ovn_datapath *od, struct hmap 
*lflows)
           *   connection must be committed with ct_mark.blocked set so we set
           *   REGBIT_ACL_HINT_BLOCK.
           */
-        ovn_lflow_add(lflows, od, stage, 4,
-                      "!ct.new && ct.est && !ct.rpl && ct_mark.blocked == 0",
+        match = features->ct_masked_mark
+                ? "!ct.new && ct.est && !ct.rpl && ct_mark.blocked == 0"
+                : "!ct.new && ct.est && !ct.rpl && ct_label.blocked == 0";
+        ovn_lflow_add(lflows, od, stage, 4, match,
                        REGBIT_ACL_HINT_ALLOW " = 1; "
                        REGBIT_ACL_HINT_BLOCK " = 1; "
                        "next;");
@@ -5951,7 +5964,10 @@ build_acl_hints(struct ovn_datapath *od, struct hmap 
*lflows)
          ovn_lflow_add(lflows, od, stage, 3, "!ct.est",
                        REGBIT_ACL_HINT_DROP " = 1; "
                        "next;");
-        ovn_lflow_add(lflows, od, stage, 2, "ct.est && ct_mark.blocked == 1",
+        match = features->ct_masked_mark
+                ? "ct.est && ct_mark.blocked == 1"
+                : "ct.est && ct_label.blocked == 1";
+        ovn_lflow_add(lflows, od, stage, 2, match,
                        REGBIT_ACL_HINT_DROP " = 1; "
                        "next;");
@@ -5959,7 +5975,10 @@ build_acl_hints(struct ovn_datapath *od, struct hmap *lflows)
           * drop ACLs in which case the connection must be committed with
           * ct_mark.blocked set.
           */
-        ovn_lflow_add(lflows, od, stage, 1, "ct.est && ct_mark.blocked == 0",
+        match = features->ct_masked_mark
+                ? "ct.est && ct_mark.blocked == 0"
+                : "ct.est && ct_label.blocked == 0";
+        ovn_lflow_add(lflows, od, stage, 1, match,
                        REGBIT_ACL_HINT_BLOCK " = 1; "
                        "next;");
      }
@@ -6085,10 +6104,13 @@ build_reject_acl_rules(struct ovn_datapath *od, struct 
hmap *lflows,
static void
  consider_acl(struct hmap *lflows, struct ovn_datapath *od,
-             struct nbrec_acl *acl, bool has_stateful,
+             struct nbrec_acl *acl, bool has_stateful, bool ct_masked_mark,
               const struct shash *meter_groups, struct ds *match,
               struct ds *actions)
  {
+    const char *ct_blocked_match = ct_masked_mark
+                                   ? "ct_mark.blocked"
+                                   : "ct_label.blocked";
      bool ingress = !strcmp(acl->direction, "from-lport") ? true :false;
      enum ovn_stage stage;
@@ -6203,10 +6225,10 @@ consider_acl(struct hmap *lflows, struct ovn_datapath *od,
                  ds_clear(actions);
ds_put_format(match, "ct.est && !ct.rel && !ct.new%s && "
-                              "ct.rpl && ct_mark.blocked == 0 && "
+                              "ct.rpl && %s == 0 && "
                                "ct_label.label == %" PRId64,
                                use_ct_inv_match ? " && !ct.inv" : "",
-                              acl->label);
+                              ct_blocked_match, acl->label);
                  build_acl_log(actions, acl, meter_groups);
                  ds_put_cstr(actions, "next;");
                  ovn_lflow_add_with_hint(lflows, od, log_related_stage,
@@ -6216,10 +6238,10 @@ consider_acl(struct hmap *lflows, struct ovn_datapath 
*od,
ds_clear(match);
                  ds_put_format(match, "!ct.est && ct.rel && !ct.new%s && "
-                                     "ct_mark.blocked == 0 && "
+                                     "%s == 0 && "
                                       "ct_label.label == %" PRId64,
                                       use_ct_inv_match ? " && !ct.inv" : "",
-                                     acl->label);
+                                     ct_blocked_match, acl->label);
                  ovn_lflow_add_with_hint(lflows, od, log_related_stage,
                                          UINT16_MAX - 2,
                                          ds_cstr(match), ds_cstr(actions),
@@ -6265,7 +6287,8 @@ consider_acl(struct hmap *lflows, struct ovn_datapath *od,
              ds_clear(match);
              ds_clear(actions);
              ds_put_cstr(match, REGBIT_ACL_HINT_BLOCK " == 1");
-            ds_put_cstr(actions, "ct_commit { ct_mark.blocked = 1; }; ");
+            ds_put_format(actions, "ct_commit { %s = 1; }; ",
+                          ct_blocked_match);
              if (!strcmp(acl->action, "reject")) {
                  build_reject_acl_rules(od, lflows, stage, acl, match,
                                         actions, &acl->header_, meter_groups);
@@ -6433,11 +6456,15 @@ build_port_group_lswitches(struct northd_input 
*input_data,
  }
static void
-build_acls(struct ovn_datapath *od, struct hmap *lflows,
-           const struct hmap *port_groups, const struct shash *meter_groups)
+build_acls(struct ovn_datapath *od, const struct chassis_features *features,
+           struct hmap *lflows, const struct hmap *port_groups,
+           const struct shash *meter_groups)
  {
      const char *default_acl_action = default_acl_drop ? "drop;" : "next;";
      bool has_stateful = od->has_stateful_acl || od->has_lb_vip;
+    const char *ct_blocked_match = features->ct_masked_mark
+                                   ? "ct_mark.blocked"
+                                   : "ct_label.blocked";
      struct ds match   = DS_EMPTY_INITIALIZER;
      struct ds actions = DS_EMPTY_INITIALIZER;
@@ -6492,12 +6519,14 @@ build_acls(struct ovn_datapath *od, struct hmap *lflows,
           * which will be done by ct_commit() in the "stateful" stage.
           * Subsequent packets will hit the flow at priority 0 that just
           * uses "next;". */
+        ds_clear(&match);
+        ds_put_format(&match, "ip && ct.est && %s == 1", ct_blocked_match);
          ovn_lflow_add(lflows, od, S_SWITCH_IN_ACL, 1,
-                      "ip && ct.est && ct_mark.blocked == 1",
-                       REGBIT_CONNTRACK_COMMIT" = 1; next;");
+                      ds_cstr(&match),
+                      REGBIT_CONNTRACK_COMMIT" = 1; next;");
          ovn_lflow_add(lflows, od, S_SWITCH_OUT_ACL, 1,
-                      "ip && ct.est && ct_mark.blocked == 1",
-                       REGBIT_CONNTRACK_COMMIT" = 1; next;");
+                      ds_cstr(&match),
+                      REGBIT_CONNTRACK_COMMIT" = 1; next;");
default_acl_action = default_acl_drop
                               ? "drop;"
@@ -6515,8 +6544,9 @@ build_acls(struct ovn_datapath *od, struct hmap *lflows,
           *
           * This is enforced at a higher priority than ACLs can be defined. */
          ds_clear(&match);
-        ds_put_format(&match, "%s(ct.est && ct.rpl && ct_mark.blocked == 1)",
-                      use_ct_inv_match ? "ct.inv || " : "");
+        ds_put_format(&match, "%s(ct.est && ct.rpl && %s == 1)",
+                      use_ct_inv_match ? "ct.inv || " : "",
+                      ct_blocked_match);
          ovn_lflow_add(lflows, od, S_SWITCH_IN_ACL, UINT16_MAX - 3,
                        ds_cstr(&match), "drop;");
          ovn_lflow_add(lflows, od, S_SWITCH_OUT_ACL, UINT16_MAX - 3,
@@ -6533,8 +6563,9 @@ build_acls(struct ovn_datapath *od, struct hmap *lflows,
           * This is enforced at a higher priority than ACLs can be defined. */
          ds_clear(&match);
          ds_put_format(&match, "ct.est && !ct.rel && !ct.new%s && "
-                      "ct.rpl && ct_mark.blocked == 0",
-                      use_ct_inv_match ? " && !ct.inv" : "");
+                      "ct.rpl && %s == 0",
+                      use_ct_inv_match ? " && !ct.inv" : "",
+                      ct_blocked_match);
          ovn_lflow_add(lflows, od, S_SWITCH_IN_ACL, UINT16_MAX - 3,
                        ds_cstr(&match), REGBIT_ACL_HINT_DROP" = 0; "
                        REGBIT_ACL_HINT_BLOCK" = 0; next;");
@@ -6553,9 +6584,9 @@ build_acls(struct ovn_datapath *od, struct hmap *lflows,
           * related traffic such as an ICMP Port Unreachable through
           * that's generated from a non-listening UDP port.  */
          ds_clear(&match);
-        ds_put_format(&match, "!ct.est && ct.rel && !ct.new%s && "
-                      "ct_mark.blocked == 0",
-                      use_ct_inv_match ? " && !ct.inv" : "");
+        ds_put_format(&match, "!ct.est && ct.rel && !ct.new%s && %s == 0",
+                      use_ct_inv_match ? " && !ct.inv" : "",
+                      ct_blocked_match);
          ovn_lflow_add(lflows, od, S_SWITCH_IN_ACL, UINT16_MAX - 3,
                        ds_cstr(&match), "next;");
          ovn_lflow_add(lflows, od, S_SWITCH_OUT_ACL, UINT16_MAX - 3,
@@ -6573,14 +6604,15 @@ build_acls(struct ovn_datapath *od, struct hmap *lflows,
      /* Ingress or Egress ACL Table (Various priorities). */
      for (size_t i = 0; i < od->nbs->n_acls; i++) {
          struct nbrec_acl *acl = od->nbs->acls[i];
-        consider_acl(lflows, od, acl, has_stateful, meter_groups, &match,
-                     &actions);
+        consider_acl(lflows, od, acl, has_stateful, features->ct_masked_mark,
+                     meter_groups, &match, &actions);
      }
      struct ovn_port_group *pg;
      HMAP_FOR_EACH (pg, key_node, port_groups) {
          if (ovn_port_group_ls_find(pg, &od->nbs->header_.uuid)) {
              for (size_t i = 0; i < pg->nb_pg->n_acls; i++) {
                  consider_acl(lflows, od, pg->nb_pg->acls[i], has_stateful,
+                             features->ct_masked_mark,
                               meter_groups, &match, &actions);
              }
          }
@@ -6819,8 +6851,15 @@ build_lb_rules(struct hmap *lflows, struct ovn_northd_lb 
*lb, bool ct_lb_mark,
  }
static void
-build_stateful(struct ovn_datapath *od, struct hmap *lflows)
+build_stateful(struct ovn_datapath *od,
+               const struct chassis_features *features,
+               struct hmap *lflows)
  {
+    const char *ct_block_action = features->ct_masked_mark
+                                  ? "ct_mark.blocked"
+                                  : "ct_label.blocked";
+    struct ds actions = DS_EMPTY_INITIALIZER;
+
      /* Ingress LB, Ingress and Egress stateful Table (Priority 0): Packets are
       * allowed by default. */
      ovn_lflow_add(lflows, od, S_SWITCH_IN_LB, 0, "1", "next;");
@@ -6833,29 +6872,33 @@ build_stateful(struct ovn_datapath *od, struct hmap 
*lflows)
       * We always set ct_mark.blocked to 0 here as
       * any packet that makes it this far is part of a connection we
       * want to allow to continue. */
+    ds_put_format(&actions, "ct_commit { %s = 0; "
+                            "ct_label.label = " REG_LABEL "; }; next;",
+                  ct_block_action);
      ovn_lflow_add(lflows, od, S_SWITCH_IN_STATEFUL, 100,
                    REGBIT_CONNTRACK_COMMIT" == 1 && "
                    REGBIT_ACL_LABEL" == 1",
-                  "ct_commit { ct_mark.blocked = 0; "
-                  "ct_label.label = " REG_LABEL "; }; next;");
+                  ds_cstr(&actions));
      ovn_lflow_add(lflows, od, S_SWITCH_OUT_STATEFUL, 100,
                    REGBIT_CONNTRACK_COMMIT" == 1 && "
                    REGBIT_ACL_LABEL" == 1",
-                  "ct_commit { ct_mark.blocked = 0; "
-                  "ct_label.label = " REG_LABEL "; }; next;");
+                  ds_cstr(&actions));
/* If REGBIT_CONNTRACK_COMMIT is set as 1, then the packets should be
       * committed to conntrack. We always set ct_mark.blocked to 0 here as
       * any packet that makes it this far is part of a connection we
       * want to allow to continue. */
+    ds_clear(&actions);
+    ds_put_format(&actions, "ct_commit { %s = 0; }; next;", ct_block_action);
      ovn_lflow_add(lflows, od, S_SWITCH_IN_STATEFUL, 100,
                    REGBIT_CONNTRACK_COMMIT" == 1 && "
                    REGBIT_ACL_LABEL" == 0",
-                  "ct_commit { ct_mark.blocked = 0; }; next;");
+                  ds_cstr(&actions));
      ovn_lflow_add(lflows, od, S_SWITCH_OUT_STATEFUL, 100,
                    REGBIT_CONNTRACK_COMMIT" == 1 && "
                    REGBIT_ACL_LABEL" == 0",
-                  "ct_commit { ct_mark.blocked = 0; }; next;");
+                  ds_cstr(&actions));
+    ds_destroy(&actions);
  }
static void
@@ -7642,10 +7685,10 @@ build_lswitch_lflows_pre_acl_and_acl(struct 
ovn_datapath *od,
          build_pre_acls(od, port_groups, lflows);
          build_pre_lb(od, meter_groups, lflows);
          build_pre_stateful(od, features, lflows);
-        build_acl_hints(od, lflows);
-        build_acls(od, lflows, port_groups, meter_groups);
+        build_acl_hints(od, features, lflows);
+        build_acls(od, features, lflows, port_groups, meter_groups);
          build_qos(od, lflows);
-        build_stateful(od, lflows);
+        build_stateful(od, features, lflows);
          build_lb_hairpin(od, lflows);
          build_vtep_hairpin(od, lflows);
      }
@@ -9343,6 +9386,7 @@ find_static_route_outport(struct ovn_datapath *od, const 
struct hmap *ports,
  static void
  add_ecmp_symmetric_reply_flows(struct hmap *lflows,
                                 struct ovn_datapath *od,
+                               bool ct_masked_mark,
                                 const char *port_ip,
                                 struct ovn_port *out_port,
                                 const struct parsed_route *route,
@@ -9353,6 +9397,9 @@ add_ecmp_symmetric_reply_flows(struct hmap *lflows,
      struct ds actions = DS_EMPTY_INITIALIZER;
      struct ds ecmp_reply = DS_EMPTY_INITIALIZER;
      char *cidr = normalize_v46_prefix(&route->prefix, route->plen);
+    const char *ct_ecmp_reply_port_match = ct_masked_mark
+                                           ? "ct_mark.ecmp_reply_port"
+                                           : "ct_label.ecmp_reply_port";
/* If symmetric ECMP replies are enabled, then packets that arrive over
       * an ECMP route need to go through conntrack.
@@ -9381,8 +9428,8 @@ add_ecmp_symmetric_reply_flows(struct hmap *lflows,
      ds_put_cstr(&match, " && (ct.new && !ct.est)");
ds_put_format(&actions, "ct_commit { ct_label.ecmp_reply_eth = eth.src;"
-                  " ct_mark.ecmp_reply_port = %" PRId64 ";}; next;",
-                  out_port->sb->tunnel_key);
+                  " %s = %" PRId64 ";}; next;",
+                  ct_ecmp_reply_port_match, out_port->sb->tunnel_key);
      ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_ECMP_STATEFUL, 100,
                              ds_cstr(&match), ds_cstr(&actions),
                              &st_route->header_);
@@ -9390,8 +9437,8 @@ add_ecmp_symmetric_reply_flows(struct hmap *lflows,
      /* Bypass ECMP selection if we already have ct_label information
       * for where to route the packet.
       */
-    ds_put_format(&ecmp_reply, "ct.rpl && ct_mark.ecmp_reply_port == %"
-                  PRId64, out_port->sb->tunnel_key);
+    ds_put_format(&ecmp_reply, "ct.rpl && %s == %"PRId64,
+                  ct_ecmp_reply_port_match, out_port->sb->tunnel_key);
      ds_clear(&match);
      ds_put_format(&match, "%s && %s", ds_cstr(&ecmp_reply),
                    ds_cstr(route_match));
@@ -9433,7 +9480,8 @@ add_ecmp_symmetric_reply_flows(struct hmap *lflows,
static void
  build_ecmp_route_flow(struct hmap *lflows, struct ovn_datapath *od,
-                      const struct hmap *ports, struct ecmp_groups_node *eg)
+                      bool ct_masked_mark, const struct hmap *ports,
+                      struct ecmp_groups_node *eg)
{
      bool is_ipv4 = IN6_IS_ADDR_V4MAPPED(&eg->prefix);
@@ -9487,7 +9535,8 @@ build_ecmp_route_flow(struct hmap *lflows, struct 
ovn_datapath *od,
          if (smap_get(&od->nbr->options, "chassis") &&
              route_->ecmp_symmetric_reply && sset_add(&visited_ports,
                                                       out_port->key)) {
-            add_ecmp_symmetric_reply_flows(lflows, od, lrp_addr_s, out_port,
+            add_ecmp_symmetric_reply_flows(lflows, od, ct_masked_mark,
+                                           lrp_addr_s, out_port,
                                             route_, &route_match);
          }
          ds_clear(&match);
@@ -11218,8 +11267,9 @@ build_ip_routing_flows_for_lrouter_port(
static void
  build_static_route_flows_for_lrouter(
-        struct ovn_datapath *od, struct hmap *lflows,
-        const struct hmap *ports, const struct hmap *bfd_connections)
+        struct ovn_datapath *od, const struct chassis_features *features,
+        struct hmap *lflows, const struct hmap *ports,
+        const struct hmap *bfd_connections)
  {
      if (od->nbr) {
          ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_ROUTING_ECMP, 150,
@@ -11262,7 +11312,8 @@ build_static_route_flows_for_lrouter(
          HMAP_FOR_EACH (group, hmap_node, &ecmp_groups) {
              /* add a flow in IP_ROUTING, and one flow for each member in
               * IP_ROUTING_ECMP. */
-            build_ecmp_route_flow(lflows, od, ports, group);
+            build_ecmp_route_flow(lflows, od, features->ct_masked_mark,
+                                  ports, group);
          }
          const struct unique_routes_node *ur;
          HMAP_FOR_EACH (ur, hmap_node, &unique_routes) {
@@ -13618,7 +13669,8 @@ build_lswitch_and_lrouter_iterate_by_od(struct 
ovn_datapath *od,
                                             &lsi->actions, lsi->meter_groups);
      build_ND_RA_flows_for_lrouter(od, lsi->lflows);
      build_ip_routing_pre_flows_for_lrouter(od, lsi->lflows);
-    build_static_route_flows_for_lrouter(od, lsi->lflows, lsi->ports,
+    build_static_route_flows_for_lrouter(od, lsi->features,
+                                         lsi->lflows, lsi->ports,
                                           lsi->bfd_connections);
      build_mcast_lookup_flows_for_lrouter(od, lsi->lflows, &lsi->match,
                                           &lsi->actions);
diff --git a/northd/northd.h b/northd/northd.h
index 07fcbcacc..e5705eb70 100644
--- a/northd/northd.h
+++ b/northd/northd.h
@@ -60,6 +60,7 @@ struct northd_input {
struct chassis_features {
      bool ct_lb_mark;
+    bool ct_masked_mark;
  };
struct northd_data {
diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
index 4bb815c7b..a9988b1e9 100644
--- a/tests/ovn-northd.at
+++ b/tests/ovn-northd.at
@@ -5725,6 +5725,19 @@ AT_CHECK([grep -e "lr_in_ip_routing_ecmp" lr0flows | sed 
's/192\.168\.0\..0/192.
    table=??(lr_in_ip_routing_ecmp), priority=100  , match=(reg8[[0..15]] == 1 && 
reg8[[16..31]] == 2), action=(reg0 = 192.168.0.??; reg1 = 192.168.0.1; eth.src = 
00:00:20:20:12:13; outport = "lr0-public"; next;)
    table=??(lr_in_ip_routing_ecmp), priority=150  , match=(reg8[[0..15]] == 
0), action=(next;)
  ])
+
+dnl The chassis was created with other_config:ct-masked-mark=false, the flows
+dnl should be using ct_label.ecmp_reply_port.
+AT_CHECK([grep -e "lr_in_arp_resolve.*ecmp" lr0flows | sed 
's/table=../table=??/'], [0], [dnl
+  table=??(lr_in_arp_resolve  ), priority=200  , match=(ct.rpl && 
ct_label.ecmp_reply_port == 1), action=(push(xxreg1); xxreg1 = ct_label; eth.dst = 
xxreg1[[32..79]]; pop(xxreg1); next;)
+])
+
+dnl Simulate an ovn-controller upgrade to a version that supports
+dnl ct-masked-mark.  ovn-northd should start using ct_mark.ecmp_reply_port.
+
+check ovn-sbctl set chassis ch1 other_config:ct-masked-mark=true
+check ovn-nbctl --wait=sb sync
+ovn-sbctl dump-flows lr0 > lr0flows
  AT_CHECK([grep -e "lr_in_arp_resolve.*ecmp" lr0flows | sed 
's/table=../table=??/'], [0], [dnl
    table=??(lr_in_arp_resolve  ), priority=200  , match=(ct.rpl && 
ct_mark.ecmp_reply_port == 1), action=(push(xxreg1); xxreg1 = ct_label; eth.dst = 
xxreg1[[32..79]]; pop(xxreg1); next;)
  ])
@@ -7485,3 +7498,82 @@ AT_CHECK([ovn-sbctl lflow-list | grep -e natted -e 
ct_lb], [0], [dnl
AT_CLEANUP
  ])
+
+OVN_FOR_EACH_NORTHD([
+AT_SETUP([ACL ct_mark.blocked backwards compatibility])
+AT_KEYWORDS([acl])
+ovn_start
+
+check ovn-nbctl                                               \
+  -- ls-add ls                                                \
+  -- acl-add ls from-lport 1 1 allow-related                  \
+  -- --apply-after-lb acl-add ls from-lport 1 1 allow-related \
+  -- acl-add ls to-lport 1 1 allow-related
+
+AS_BOX([No chassis registered - use ct_mark.blocked])
+check ovn-nbctl --wait=sb sync
+AT_CHECK([ovn-sbctl lflow-list | grep 'ls.*acl.*blocked' ], [0], [dnl
+  table=7 (ls_in_acl_hint     ), priority=6    , match=(!ct.new && ct.est && !ct.rpl 
&& ct_mark.blocked == 1), action=(reg0[[7]] = 1; reg0[[9]] = 1; next;)
+  table=7 (ls_in_acl_hint     ), priority=4    , match=(!ct.new && ct.est && !ct.rpl 
&& ct_mark.blocked == 0), action=(reg0[[8]] = 1; reg0[[10]] = 1; next;)
+  table=7 (ls_in_acl_hint     ), priority=2    , match=(ct.est && 
ct_mark.blocked == 1), action=(reg0[[9]] = 1; next;)
+  table=7 (ls_in_acl_hint     ), priority=1    , match=(ct.est && 
ct_mark.blocked == 0), action=(reg0[[10]] = 1; next;)
+  table=8 (ls_in_acl          ), priority=65532, match=(!ct.est && ct.rel && !ct.new 
&& !ct.inv && ct_mark.blocked == 0), action=(next;)
+  table=8 (ls_in_acl          ), priority=65532, match=(ct.est && !ct.rel && !ct.new && 
!ct.inv && ct.rpl && ct_mark.blocked == 0), action=(reg0[[9]] = 0; reg0[[10]] = 0; next;)
+  table=8 (ls_in_acl          ), priority=65532, match=(ct.inv || (ct.est && ct.rpl 
&& ct_mark.blocked == 1)), action=(drop;)
+  table=8 (ls_in_acl          ), priority=1    , match=(ip && ct.est && 
ct_mark.blocked == 1), action=(reg0[[1]] = 1; next;)
+  table=3 (ls_out_acl_hint    ), priority=6    , match=(!ct.new && ct.est && !ct.rpl 
&& ct_mark.blocked == 1), action=(reg0[[7]] = 1; reg0[[9]] = 1; next;)
+  table=3 (ls_out_acl_hint    ), priority=4    , match=(!ct.new && ct.est && !ct.rpl 
&& ct_mark.blocked == 0), action=(reg0[[8]] = 1; reg0[[10]] = 1; next;)
+  table=3 (ls_out_acl_hint    ), priority=2    , match=(ct.est && 
ct_mark.blocked == 1), action=(reg0[[9]] = 1; next;)
+  table=3 (ls_out_acl_hint    ), priority=1    , match=(ct.est && 
ct_mark.blocked == 0), action=(reg0[[10]] = 1; next;)
+  table=4 (ls_out_acl         ), priority=65532, match=(!ct.est && ct.rel && !ct.new 
&& !ct.inv && ct_mark.blocked == 0), action=(next;)
+  table=4 (ls_out_acl         ), priority=65532, match=(ct.est && !ct.rel && !ct.new && 
!ct.inv && ct.rpl && ct_mark.blocked == 0), action=(next;)
+  table=4 (ls_out_acl         ), priority=65532, match=(ct.inv || (ct.est && ct.rpl 
&& ct_mark.blocked == 1)), action=(drop;)
+  table=4 (ls_out_acl         ), priority=1    , match=(ip && ct.est && 
ct_mark.blocked == 1), action=(reg0[[1]] = 1; next;)
+])
+
+AS_BOX([Chassis registered that doesn't support ct_mark.blocked - use 
ct_label.blocked])
+check ovn-sbctl chassis-add hv geneve 127.0.0.1
+check ovn-nbctl --wait=sb sync
+AT_CHECK([ovn-sbctl lflow-list | grep 'ls.*acl.*blocked' ], [0], [dnl
+  table=7 (ls_in_acl_hint     ), priority=6    , match=(!ct.new && ct.est && !ct.rpl 
&& ct_label.blocked == 1), action=(reg0[[7]] = 1; reg0[[9]] = 1; next;)
+  table=7 (ls_in_acl_hint     ), priority=4    , match=(!ct.new && ct.est && !ct.rpl 
&& ct_label.blocked == 0), action=(reg0[[8]] = 1; reg0[[10]] = 1; next;)
+  table=7 (ls_in_acl_hint     ), priority=2    , match=(ct.est && 
ct_label.blocked == 1), action=(reg0[[9]] = 1; next;)
+  table=7 (ls_in_acl_hint     ), priority=1    , match=(ct.est && 
ct_label.blocked == 0), action=(reg0[[10]] = 1; next;)
+  table=8 (ls_in_acl          ), priority=65532, match=(!ct.est && ct.rel && !ct.new 
&& !ct.inv && ct_label.blocked == 0), action=(next;)
+  table=8 (ls_in_acl          ), priority=65532, match=(ct.est && !ct.rel && !ct.new && 
!ct.inv && ct.rpl && ct_label.blocked == 0), action=(reg0[[9]] = 0; reg0[[10]] = 0; next;)
+  table=8 (ls_in_acl          ), priority=65532, match=(ct.inv || (ct.est && ct.rpl 
&& ct_label.blocked == 1)), action=(drop;)
+  table=8 (ls_in_acl          ), priority=1    , match=(ip && ct.est && 
ct_label.blocked == 1), action=(reg0[[1]] = 1; next;)
+  table=3 (ls_out_acl_hint    ), priority=6    , match=(!ct.new && ct.est && !ct.rpl 
&& ct_label.blocked == 1), action=(reg0[[7]] = 1; reg0[[9]] = 1; next;)
+  table=3 (ls_out_acl_hint    ), priority=4    , match=(!ct.new && ct.est && !ct.rpl 
&& ct_label.blocked == 0), action=(reg0[[8]] = 1; reg0[[10]] = 1; next;)
+  table=3 (ls_out_acl_hint    ), priority=2    , match=(ct.est && 
ct_label.blocked == 1), action=(reg0[[9]] = 1; next;)
+  table=3 (ls_out_acl_hint    ), priority=1    , match=(ct.est && 
ct_label.blocked == 0), action=(reg0[[10]] = 1; next;)
+  table=4 (ls_out_acl         ), priority=65532, match=(!ct.est && ct.rel && !ct.new 
&& !ct.inv && ct_label.blocked == 0), action=(next;)
+  table=4 (ls_out_acl         ), priority=65532, match=(ct.est && !ct.rel && !ct.new && 
!ct.inv && ct.rpl && ct_label.blocked == 0), action=(next;)
+  table=4 (ls_out_acl         ), priority=65532, match=(ct.inv || (ct.est && ct.rpl 
&& ct_label.blocked == 1)), action=(drop;)
+  table=4 (ls_out_acl         ), priority=1    , match=(ip && ct.est && 
ct_label.blocked == 1), action=(reg0[[1]] = 1; next;)
+])
+
+AS_BOX([Chassis upgrades and supports ct_mark.blocked - use ct_mark.blocked])
+check ovn-sbctl set chassis hv other_config:ct-masked-mark=true
+check ovn-nbctl --wait=sb sync
+AT_CHECK([ovn-sbctl lflow-list | grep 'ls.*acl.*blocked' ], [0], [dnl
+  table=7 (ls_in_acl_hint     ), priority=6    , match=(!ct.new && ct.est && !ct.rpl 
&& ct_mark.blocked == 1), action=(reg0[[7]] = 1; reg0[[9]] = 1; next;)
+  table=7 (ls_in_acl_hint     ), priority=4    , match=(!ct.new && ct.est && !ct.rpl 
&& ct_mark.blocked == 0), action=(reg0[[8]] = 1; reg0[[10]] = 1; next;)
+  table=7 (ls_in_acl_hint     ), priority=2    , match=(ct.est && 
ct_mark.blocked == 1), action=(reg0[[9]] = 1; next;)
+  table=7 (ls_in_acl_hint     ), priority=1    , match=(ct.est && 
ct_mark.blocked == 0), action=(reg0[[10]] = 1; next;)
+  table=8 (ls_in_acl          ), priority=65532, match=(!ct.est && ct.rel && !ct.new 
&& !ct.inv && ct_mark.blocked == 0), action=(next;)
+  table=8 (ls_in_acl          ), priority=65532, match=(ct.est && !ct.rel && !ct.new && 
!ct.inv && ct.rpl && ct_mark.blocked == 0), action=(reg0[[9]] = 0; reg0[[10]] = 0; next;)
+  table=8 (ls_in_acl          ), priority=65532, match=(ct.inv || (ct.est && ct.rpl 
&& ct_mark.blocked == 1)), action=(drop;)
+  table=8 (ls_in_acl          ), priority=1    , match=(ip && ct.est && 
ct_mark.blocked == 1), action=(reg0[[1]] = 1; next;)
+  table=3 (ls_out_acl_hint    ), priority=6    , match=(!ct.new && ct.est && !ct.rpl 
&& ct_mark.blocked == 1), action=(reg0[[7]] = 1; reg0[[9]] = 1; next;)
+  table=3 (ls_out_acl_hint    ), priority=4    , match=(!ct.new && ct.est && !ct.rpl 
&& ct_mark.blocked == 0), action=(reg0[[8]] = 1; reg0[[10]] = 1; next;)
+  table=3 (ls_out_acl_hint    ), priority=2    , match=(ct.est && 
ct_mark.blocked == 1), action=(reg0[[9]] = 1; next;)
+  table=3 (ls_out_acl_hint    ), priority=1    , match=(ct.est && 
ct_mark.blocked == 0), action=(reg0[[10]] = 1; next;)
+  table=4 (ls_out_acl         ), priority=65532, match=(!ct.est && ct.rel && !ct.new 
&& !ct.inv && ct_mark.blocked == 0), action=(next;)
+  table=4 (ls_out_acl         ), priority=65532, match=(ct.est && !ct.rel && !ct.new && 
!ct.inv && ct.rpl && ct_mark.blocked == 0), action=(next;)
+  table=4 (ls_out_acl         ), priority=65532, match=(ct.inv || (ct.est && ct.rpl 
&& ct_mark.blocked == 1)), action=(drop;)
+  table=4 (ls_out_acl         ), priority=1    , match=(ip && ct.est && 
ct_mark.blocked == 1), action=(reg0[[1]] = 1; next;)
+])
+
+AT_CLEANUP
+])


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

Reply via email to