Hi Ales, I have a response below.

On 4/13/23 06:56, Ales Musil wrote:


On Mon, Apr 10, 2023 at 7:26 PM Mark Michelson <[email protected] <mailto:[email protected]>> wrote:

    With this commit, ACLs can now be arranged in hierarchical tiers. A tier
    number can be assigned to an ACL. When evaluating ACLs, we first will
    consider ACLs at tier 0. If no matching ACL is found, then we move to
    tier 1. This continues until a matching ACL is found, or we reach the
    maximum tier. If no match is found, then the default acl action is
    applied.

    Signed-off-by: Mark Michelson <[email protected]
    <mailto:[email protected]>>


Hi Mark,
I have some small comments below.

    ---
      northd/northd.c     |  96 +++++++++++++++++++-------
      northd/northd.h     |   1 +
      ovn-nb.ovsschema    |   5 +-
      ovn-nb.xml          |  20 ++++++
      tests/ovn-northd.at <http://ovn-northd.at> | 162
    +++++++++++++++++++++++++++++++++++++++-----
      tests/system-ovn.at <http://system-ovn.at> | 107
    +++++++++++++++++++++++++++++
      6 files changed, 347 insertions(+), 44 deletions(-)

    diff --git a/northd/northd.c b/northd/northd.c
    index 39de54e5b..36ca94ebb 100644
    --- a/northd/northd.c
    +++ b/northd/northd.c
    @@ -242,6 +242,7 @@ enum ovn_stage {
      #define REGBIT_ACL_VERDICT_ALLOW "reg8[16]"
      #define REGBIT_ACL_VERDICT_DROP "reg8[17]"
      #define REGBIT_ACL_VERDICT_REJECT "reg8[18]"
    +#define REG_ACL_TIER "reg8[30..31]"

      /* Indicate that this packet has been recirculated using egress
       * loopback.  This allows certain checks to be bypassed, such as a
    @@ -5704,36 +5705,51 @@ ovn_ls_port_group_destroy(struct hmap *nb_pgs)
          hmap_destroy(nb_pgs);
      }

    +static bool
    +od_set_acl_flags(struct ovn_datapath *od, struct nbrec_acl **acls,
    +                 size_t n_acls)
    +{
    +    /* A true return indicates that there are no possible ACL flags
    +     * left to set on od. A false return indicates that further ACLs
    +     * should be explored in case more flags need to be set on od
    +     */
    +    if (!n_acls) {
    +        return false;
    +    }
    +
    +    od->has_acls = true;
    +    for (size_t i = 0; i < n_acls; i++) {
    +        const struct nbrec_acl *acl = acls[i];
    +        if (acl->tier > od->max_acl_tier) {
    +            od->max_acl_tier = acl->tier;
    +        }
    +        if (!od->has_stateful_acl && !strcmp(acl->action,
    "allow-related")) {
    +            od->has_stateful_acl = true;
    +        }
    +        if (od->has_stateful_acl &&
    +            od->max_acl_tier ==
    nbrec_acl_col_tier.type.value.integer.max) {
    +            return true;
    +        }
    +    }
    +
    +    return false;
    +}
    +
      static void
      ls_get_acl_flags(struct ovn_datapath *od)
      {
          od->has_acls = false;
          od->has_stateful_acl = false;
    +    od->max_acl_tier = 0;

    -    if (od->nbs->n_acls) {
    -        od->has_acls = true;
    -
    -        for (size_t i = 0; i < od->nbs->n_acls; i++) {
    -            struct nbrec_acl *acl = od->nbs->acls[i];
    -            if (!strcmp(acl->action, "allow-related")) {
    -                od->has_stateful_acl = true;
    -                return;
    -            }
    -        }
    +    if (od_set_acl_flags(od, od->nbs->acls, od->nbs->n_acls)) {
    +        return;
          }

          struct ovn_ls_port_group *ls_pg;
          HMAP_FOR_EACH (ls_pg, key_node, &od->nb_pgs) {
    -        if (ls_pg->nb_pg->n_acls) {
    -            od->has_acls = true;
    -
    -            for (size_t i = 0; i < ls_pg->nb_pg->n_acls; i++) {
    -                struct nbrec_acl *acl = ls_pg->nb_pg->acls[i];
    -                if (!strcmp(acl->action, "allow-related")) {
    -                    od->has_stateful_acl = true;
    -                    return;
    -                }
    -            }
    +        if (od_set_acl_flags(od, ls_pg->nb_pg->acls,
    ls_pg->nb_pg->n_acls)) {
    +            return;
              }
          }
      }
    @@ -6448,10 +6464,19 @@ consider_acl(struct hmap *lflows, struct
    ovn_datapath *od,
          size_t log_verdict_len = actions->length;
          uint16_t priority = acl->priority + OVN_ACL_PRI_OFFSET;

    +    /* All ACLS will start by matching on their respective tier. */
    +    size_t match_tier_len = 0;
    +    ds_clear(match);
    +    if (od->max_acl_tier) {
    +        ds_put_format(match, REG_ACL_TIER " == %"PRId64" && ",
    acl->tier);
    +        match_tier_len = match->length;
    +    }
    +
          if (!has_stateful || !strcmp(acl->action, "allow-stateless")) {
              ds_put_cstr(actions, "next;");
    +        ds_put_format(match, "(%s)", acl->match);
              ovn_lflow_add_with_hint(lflows, od, stage, priority,
    -                                acl->match, ds_cstr(actions),
    +                                ds_cstr(match), ds_cstr(actions),
                                      &acl->header_);
              return;
          }
    @@ -6476,7 +6501,7 @@ consider_acl(struct hmap *lflows, struct
    ovn_datapath *od,
               * by ct_commit in the "stateful" stage) to indicate that the
               * connection should be allowed to resume.
               */
    -        ds_clear(match);
    +        ds_truncate(match, match_tier_len);
              ds_put_format(match, REGBIT_ACL_HINT_ALLOW_NEW " == 1 &&
    (%s)",
                            acl->match);

    @@ -6499,7 +6524,7 @@ consider_acl(struct hmap *lflows, struct
    ovn_datapath *od,
               * Commit the connection only if the ACL has a label. This
    is done
               * to update the connection tracking entry label in case
    the ACL
               * allowing the connection changes. */
    -        ds_clear(match);
    +        ds_truncate(match, match_tier_len);
              ds_truncate(actions, log_verdict_len);
              ds_put_format(match, REGBIT_ACL_HINT_ALLOW " == 1 && (%s)",
                            acl->match);
    @@ -6520,7 +6545,7 @@ consider_acl(struct hmap *lflows, struct
    ovn_datapath *od,
               * to the connection tracker with ct_commit. */
              /* If the packet is not tracked or not part of an established
               * connection, then we can simply reject/drop it. */
    -        ds_clear(match);
    +        ds_truncate(match, match_tier_len);
              ds_put_cstr(match, REGBIT_ACL_HINT_DROP " == 1");
              ds_put_format(match, " && (%s)", acl->match);

    @@ -6540,7 +6565,7 @@ consider_acl(struct hmap *lflows, struct
    ovn_datapath *od,
               * ct_commit() to the "stateful" stage, but since we're
               * rejecting/dropping the packet, we go ahead and do it here.
               */
    -        ds_clear(match);
    +        ds_truncate(match, match_tier_len);
              ds_put_cstr(match, REGBIT_ACL_HINT_BLOCK " == 1");
              ds_put_format(match, " && (%s)", acl->match);

    @@ -6692,6 +6717,7 @@ static void
      build_acl_action_lflows(struct ovn_datapath *od, struct hmap *lflows,
                              const char *default_acl_action,
                              const struct shash *meter_groups,
    +                        struct ds *match,
                              struct ds *actions)
      {
          enum ovn_stage stages [] = {
    @@ -6704,6 +6730,10 @@ build_acl_action_lflows(struct ovn_datapath
    *od, struct hmap *lflows,
          ds_put_cstr(actions, REGBIT_ACL_VERDICT_ALLOW " = 0; "
                              REGBIT_ACL_VERDICT_DROP " = 0; "
                              REGBIT_ACL_VERDICT_REJECT " = 0; ");
    +    if (od->max_acl_tier) {
    +        ds_put_cstr(actions, REG_ACL_TIER " = 0; ");
    +    }
    +
          size_t verdict_len = actions->length;

          for (size_t i = 0; i < ARRAY_SIZE(stages); i++) {
    @@ -6744,6 +6774,20 @@ build_acl_action_lflows(struct ovn_datapath
    *od, struct hmap *lflows,
              ds_truncate(actions, verdict_len);
              ds_put_cstr(actions, default_acl_action);
              ovn_lflow_add(lflows, od, stage, 0, "1", ds_cstr(actions));
    +
    +        struct ds tier_actions = DS_EMPTY_INITIALIZER;


I don't think we actually need to allocate another ds.
The "actions" variable is not used at this point anymore, it can be used instead.

I'm about to post v3 of this series, and this comment is the only one that I am not addressing as part of v3.

The loop below is an inner loop that runs within an outer loop. The outer loop expects to be able to ds_truncate() the "actions" variable so that it will contain the subset of actions to reset the verdict and tier bits to 0. The tier-related actions need an empty string to manipulate. If I re-use the actions string here, then I have to re-populate it with the verdict-resetting string after the inner loop completes so that the outer loop will function correctly. I could do that, but I think it makes the code harder to follow since the "actions" variable is being used inconsistently, and it could potentially result in unnecessary string manipulation if there are no ACL tiers configured.


    +        for (size_t j = 0; j < od->max_acl_tier; j++) {
    +            ds_clear(match);
    +            ds_put_format(match, REG_ACL_TIER " == %"PRIuSIZE, j);
    +            ds_clear(&tier_actions);
    +            ds_put_format(&tier_actions, REG_ACL_TIER " =
    %"PRIuSIZE"; "
    +                                   "next(pipeline=%s,table=%"PRIu8");",
    +                          j + 1, ingress ? "ingress" : "egress",
    +                          ovn_stage_get_table(stage) - 1);


nit: This formatting is not uniform.

    +            ovn_lflow_add(lflows, od, stage, 500, ds_cstr(match),
    +                         ds_cstr(&tier_actions));
    +        }
    +        ds_destroy(&tier_actions);
          }
      }

    @@ -7107,7 +7151,7 @@ build_acls(struct ovn_datapath *od, const
    struct chassis_features *features,
          }

          build_acl_action_lflows(od, lflows, default_acl_action,
    meter_groups,
    -                            &actions);
    +                            &match, &actions);

          ds_destroy(&match);
          ds_destroy(&actions);
    diff --git a/northd/northd.h b/northd/northd.h
    index a503f4a66..ad6ccef5e 100644
    --- a/northd/northd.h
    +++ b/northd/northd.h
    @@ -230,6 +230,7 @@ struct ovn_datapath {
          bool has_lb_vip;
          bool has_unknown;
          bool has_acls;
    +    uint64_t max_acl_tier;
          bool has_vtep_lports;
          bool has_arp_proxy_port;

    diff --git a/ovn-nb.ovsschema b/ovn-nb.ovsschema
    index 4836a219f..f12d39542 100644
    --- a/ovn-nb.ovsschema
    +++ b/ovn-nb.ovsschema
    @@ -1,7 +1,7 @@
      {
          "name": "OVN_Northbound",
          "version": "7.0.0",
    -    "cksum": "94023179 33468",
    +    "cksum": "3195094080 33650",
          "tables": {
              "NB_Global": {
                  "columns": {
    @@ -272,6 +272,9 @@
                      "label": {"type": {"key": {"type": "integer",
                                                 "minInteger": 0,
                                                 "maxInteger":
    4294967295}}},
    +                "tier": {"type": {"key": {"type": "integer",
    +                                          "minInteger": 0,
    +                                          "maxInteger": 3}}},
                      "options": {
                           "type": {"key": "string",
                                    "value": "string",
    diff --git a/ovn-nb.xml b/ovn-nb.xml
    index d6694778f..19cbd191d 100644
    --- a/ovn-nb.xml
    +++ b/ovn-nb.xml
    @@ -2257,6 +2257,26 @@ or
            </ul>
          </column>

    +    <column name="tier">
    +      <p>The hierarchical tier that this ACL belongs to.</p>
    +
    +      <p>
    +        ACLs can be assigned to numerical tiers. When evaluating
    ACLs, an
    +        internal counter is used to determine which tier of ACLs
    should be
    +        evaluated. Tier 0 ACLs are evaluated first. If no verdict
    can be
    +        determined, then tier 1 ACLs are evaluated next. This continues
    +        until the maximum tier value is reached. If all tiers of
    ACLs are
    +        evaluated and no verdict is reached, then the <ref
    column="options"
    +        key="default_acl_drop" table="NB_Global" /> option from table
    +        <ref table="NB_Global" /> is used to determine how to proceed.
    +      </p>
    +
    +      <p>
    +        In this version of OVN, the maximum tier value for ACLs is 3,
    +        meaning there are 4 tiers of ACLs allowed (0-3).
    +      </p>
    +    </column>
    +
          <group title="options">
            <p>
              ACLs options.
    diff --git a/tests/ovn-northd.at <http://ovn-northd.at>
    b/tests/ovn-northd.at <http://ovn-northd.at>
    index 19a0e630f..507db7e7a 100644
    --- a/tests/ovn-northd.at <http://ovn-northd.at>
    +++ b/tests/ovn-northd.at <http://ovn-northd.at>
    @@ -2165,10 +2165,10 @@ AT_CAPTURE_FILE([sw1flows])

      AT_CHECK(
        [grep -E 'ls_(in|out)_acl' sw0flows sw1flows | grep pg0 | sort],
    [0], [dnl
    -sw0flows:  table=4 (ls_out_acl_eval    ), priority=2003 ,
    match=(outport == @pg0 && ip6 && udp), action=(reg8[[18]] = 1; next;)
    -sw0flows:  table=8 (ls_in_acl_eval     ), priority=2002 ,
    match=(inport == @pg0 && ip4 && tcp && tcp.dst == 80),
    action=(reg8[[18]] = 1; next;)
    -sw1flows:  table=4 (ls_out_acl_eval    ), priority=2003 ,
    match=(outport == @pg0 && ip6 && udp), action=(reg8[[18]] = 1; next;)
    -sw1flows:  table=8 (ls_in_acl_eval     ), priority=2002 ,
    match=(inport == @pg0 && ip4 && tcp && tcp.dst == 80),
    action=(reg8[[18]] = 1; next;)
    +sw0flows:  table=4 (ls_out_acl_eval    ), priority=2003 ,
    match=((outport == @pg0 && ip6 && udp)), action=(reg8[[18]] = 1; next;)
    +sw0flows:  table=8 (ls_in_acl_eval     ), priority=2002 ,
    match=((inport == @pg0 && ip4 && tcp && tcp.dst == 80)),
    action=(reg8[[18]] = 1; next;)
    +sw1flows:  table=4 (ls_out_acl_eval    ), priority=2003 ,
    match=((outport == @pg0 && ip6 && udp)), action=(reg8[[18]] = 1; next;)
    +sw1flows:  table=8 (ls_in_acl_eval     ), priority=2002 ,
    match=((inport == @pg0 && ip4 && tcp && tcp.dst == 80)),
    action=(reg8[[18]] = 1; next;)
      ])

      AS_BOX([2])
    @@ -2181,10 +2181,10 @@ ovn-sbctl dump-flows sw1 > sw1flows2
      AT_CAPTURE_FILE([sw1flows2])

      AT_CHECK([grep "ls_out_acl" sw0flows2 sw1flows2 | grep pg0 |
    sort], [0], [dnl
    -sw0flows2:  table=4 (ls_out_acl_eval    ), priority=2002 ,
    match=(outport == @pg0 && ip4 && udp), action=(reg8[[18]] = 1; next;)
    -sw0flows2:  table=4 (ls_out_acl_eval    ), priority=2003 ,
    match=(outport == @pg0 && ip6 && udp), action=(reg8[[18]] = 1; next;)
    -sw1flows2:  table=4 (ls_out_acl_eval    ), priority=2002 ,
    match=(outport == @pg0 && ip4 && udp), action=(reg8[[18]] = 1; next;)
    -sw1flows2:  table=4 (ls_out_acl_eval    ), priority=2003 ,
    match=(outport == @pg0 && ip6 && udp), action=(reg8[[18]] = 1; next;)
    +sw0flows2:  table=4 (ls_out_acl_eval    ), priority=2002 ,
    match=((outport == @pg0 && ip4 && udp)), action=(reg8[[18]] = 1; next;)
    +sw0flows2:  table=4 (ls_out_acl_eval    ), priority=2003 ,
    match=((outport == @pg0 && ip6 && udp)), action=(reg8[[18]] = 1; next;)
    +sw1flows2:  table=4 (ls_out_acl_eval    ), priority=2002 ,
    match=((outport == @pg0 && ip4 && udp)), action=(reg8[[18]] = 1; next;)
    +sw1flows2:  table=4 (ls_out_acl_eval    ), priority=2003 ,
    match=((outport == @pg0 && ip6 && udp)), action=(reg8[[18]] = 1; next;)
      ])

      AS_BOX([3])
    @@ -7324,7 +7324,7 @@ AT_CHECK([ovn-sbctl dump-flows | grep -E
    "ls_.*_acl" | sed 's/table=../table=??/
        table=??(ls_in_acl_after_lb_action), priority=1000 ,
    match=(reg8[[18]] == 1), action=(reg8[[16]] = 0; reg8[[17]] = 0;
    reg8[[18]] = 0; reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst
    <-> ip.src; is implicit. */ outport <-> inport;
    next(pipeline=egress,table=6); };)
        table=??(ls_in_acl_after_lb_eval), priority=0    , match=(1),
    action=(next;)
        table=??(ls_in_acl_eval     ), priority=0    , match=(1),
    action=(next;)
    -  table=??(ls_in_acl_eval     ), priority=1001 , match=(ip4 &&
    tcp), action=(reg8[[16]] = 1; next;)
    +  table=??(ls_in_acl_eval     ), priority=1001 , match=((ip4 &&
    tcp)), action=(reg8[[16]] = 1; next;)
        table=??(ls_in_acl_eval     ), priority=34000, match=(eth.dst ==
    $svc_monitor_mac), action=(reg8[[16]] = 1; next;)
        table=??(ls_in_acl_hint     ), priority=0    , match=(1),
    action=(next;)
        table=??(ls_in_pre_acl      ), priority=0    , match=(1),
    action=(next;)
    @@ -7358,7 +7358,7 @@ AT_CHECK([ovn-sbctl dump-flows | grep -E
    "ls_.*_acl" | sed 's/table=../table=??/
        table=??(ls_in_acl_after_lb_action), priority=1000 ,
    match=(reg8[[18]] == 1), action=(reg8[[16]] = 0; reg8[[17]] = 0;
    reg8[[18]] = 0; reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst
    <-> ip.src; is implicit. */ outport <-> inport;
    next(pipeline=egress,table=6); };)
        table=??(ls_in_acl_after_lb_eval), priority=0    , match=(1),
    action=(next;)
        table=??(ls_in_acl_eval     ), priority=0    , match=(1),
    action=(next;)
    -  table=??(ls_in_acl_eval     ), priority=1001 , match=(ip4 &&
    tcp), action=(reg8[[16]] = 1; next;)
    +  table=??(ls_in_acl_eval     ), priority=1001 , match=((ip4 &&
    tcp)), action=(reg8[[16]] = 1; next;)
        table=??(ls_in_acl_eval     ), priority=34000, match=(eth.dst ==
    $svc_monitor_mac), action=(reg8[[16]] = 1; next;)
        table=??(ls_in_acl_hint     ), priority=0    , match=(1),
    action=(next;)
        table=??(ls_in_pre_acl      ), priority=0    , match=(1),
    action=(next;)
    @@ -7392,7 +7392,7 @@ AT_CHECK([ovn-sbctl dump-flows | grep -E
    "ls_.*_acl" | sed 's/table=../table=??/
        table=??(ls_in_acl_after_lb_action), priority=1000 ,
    match=(reg8[[18]] == 1), action=(reg8[[16]] = 0; reg8[[17]] = 0;
    reg8[[18]] = 0; reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst
    <-> ip.src; is implicit. */ outport <-> inport;
    next(pipeline=egress,table=6); };)
        table=??(ls_in_acl_after_lb_eval), priority=0    , match=(1),
    action=(next;)
        table=??(ls_in_acl_eval     ), priority=0    , match=(1),
    action=(next;)
    -  table=??(ls_in_acl_eval     ), priority=1001 , match=(ip4 &&
    tcp), action=(reg8[[16]] = 1; next;)
    +  table=??(ls_in_acl_eval     ), priority=1001 , match=((ip4 &&
    tcp)), action=(reg8[[16]] = 1; next;)
        table=??(ls_in_acl_eval     ), priority=34000, match=(eth.dst ==
    $svc_monitor_mac), action=(reg8[[16]] = 1; next;)
        table=??(ls_in_acl_hint     ), priority=0    , match=(1),
    action=(next;)
        table=??(ls_in_pre_acl      ), priority=0    , match=(1),
    action=(next;)
    @@ -7497,7 +7497,7 @@ AT_CHECK([ovn-sbctl dump-flows | grep -E
    "ls_.*_acl" | sed 's/table=../table=??/
        table=??(ls_in_acl_after_lb_action), priority=1000 ,
    match=(reg8[[17]] == 1), action=(reg8[[16]] = 0; reg8[[17]] = 0;
    reg8[[18]] = 0; /* drop */)
        table=??(ls_in_acl_after_lb_action), priority=1000 ,
    match=(reg8[[18]] == 1), action=(reg8[[16]] = 0; reg8[[17]] = 0;
    reg8[[18]] = 0; reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst
    <-> ip.src; is implicit. */ outport <-> inport;
    next(pipeline=egress,table=6); };)
        table=??(ls_in_acl_after_lb_eval), priority=0    , match=(1),
    action=(next;)
    -  table=??(ls_in_acl_after_lb_eval), priority=1001 , match=(ip4 &&
    tcp), action=(reg8[[16]] = 1; next;)
    +  table=??(ls_in_acl_after_lb_eval), priority=1001 , match=((ip4 &&
    tcp)), action=(reg8[[16]] = 1; next;)
        table=??(ls_in_acl_eval     ), priority=0    , match=(1),
    action=(next;)
        table=??(ls_in_acl_eval     ), priority=34000, match=(eth.dst ==
    $svc_monitor_mac), action=(reg8[[16]] = 1; next;)
        table=??(ls_in_acl_hint     ), priority=0    , match=(1),
    action=(next;)
    @@ -7531,7 +7531,7 @@ AT_CHECK([ovn-sbctl dump-flows | grep -E
    "ls_.*_acl" | sed 's/table=../table=??/
        table=??(ls_in_acl_after_lb_action), priority=1000 ,
    match=(reg8[[17]] == 1), action=(reg8[[16]] = 0; reg8[[17]] = 0;
    reg8[[18]] = 0; /* drop */)
        table=??(ls_in_acl_after_lb_action), priority=1000 ,
    match=(reg8[[18]] == 1), action=(reg8[[16]] = 0; reg8[[17]] = 0;
    reg8[[18]] = 0; reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst
    <-> ip.src; is implicit. */ outport <-> inport;
    next(pipeline=egress,table=6); };)
        table=??(ls_in_acl_after_lb_eval), priority=0    , match=(1),
    action=(next;)
    -  table=??(ls_in_acl_after_lb_eval), priority=1001 , match=(ip4 &&
    tcp), action=(reg8[[16]] = 1; next;)
    +  table=??(ls_in_acl_after_lb_eval), priority=1001 , match=((ip4 &&
    tcp)), action=(reg8[[16]] = 1; next;)
        table=??(ls_in_acl_eval     ), priority=0    , match=(1),
    action=(next;)
        table=??(ls_in_acl_eval     ), priority=34000, match=(eth.dst ==
    $svc_monitor_mac), action=(reg8[[16]] = 1; next;)
        table=??(ls_in_acl_hint     ), priority=0    , match=(1),
    action=(next;)
    @@ -7565,7 +7565,7 @@ AT_CHECK([ovn-sbctl dump-flows | grep -E
    "ls_.*_acl" | sed 's/table=../table=??/
        table=??(ls_in_acl_after_lb_action), priority=1000 ,
    match=(reg8[[17]] == 1), action=(reg8[[16]] = 0; reg8[[17]] = 0;
    reg8[[18]] = 0; /* drop */)
        table=??(ls_in_acl_after_lb_action), priority=1000 ,
    match=(reg8[[18]] == 1), action=(reg8[[16]] = 0; reg8[[17]] = 0;
    reg8[[18]] = 0; reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst
    <-> ip.src; is implicit. */ outport <-> inport;
    next(pipeline=egress,table=6); };)
        table=??(ls_in_acl_after_lb_eval), priority=0    , match=(1),
    action=(next;)
    -  table=??(ls_in_acl_after_lb_eval), priority=1001 , match=(ip4 &&
    tcp), action=(reg8[[16]] = 1; next;)
    +  table=??(ls_in_acl_after_lb_eval), priority=1001 , match=((ip4 &&
    tcp)), action=(reg8[[16]] = 1; next;)
        table=??(ls_in_acl_eval     ), priority=0    , match=(1),
    action=(next;)
        table=??(ls_in_acl_eval     ), priority=34000, match=(eth.dst ==
    $svc_monitor_mac), action=(reg8[[16]] = 1; next;)
        table=??(ls_in_acl_hint     ), priority=0    , match=(1),
    action=(next;)
    @@ -7681,7 +7681,7 @@ AT_CHECK([ovn-sbctl dump-flows | grep -E
    "ls_.*_acl" | sed 's/table=../table=??/
        table=??(ls_out_acl_action  ), priority=1000 , match=(reg8[[17]]
    == 1), action=(reg8[[16]] = 0; reg8[[17]] = 0; reg8[[18]] = 0; /*
    drop */)
        table=??(ls_out_acl_action  ), priority=1000 , match=(reg8[[18]]
    == 1), action=(reg8[[16]] = 0; reg8[[17]] = 0; reg8[[18]] = 0; reg0
    = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is
    implicit. */ outport <-> inport; next(pipeline=ingress,table=27); };)
        table=??(ls_out_acl_eval    ), priority=0    , match=(1),
    action=(next;)
    -  table=??(ls_out_acl_eval    ), priority=1001 , match=(ip4 &&
    tcp), action=(reg8[[16]] = 1; next;)
    +  table=??(ls_out_acl_eval    ), priority=1001 , match=((ip4 &&
    tcp)), action=(reg8[[16]] = 1; next;)
        table=??(ls_out_acl_eval    ), priority=34000, match=(eth.src ==
    $svc_monitor_mac), action=(reg8[[16]] = 1; next;)
        table=??(ls_out_acl_hint    ), priority=0    , match=(1),
    action=(next;)
        table=??(ls_out_pre_acl     ), priority=0    , match=(1),
    action=(next;)
    @@ -7715,7 +7715,7 @@ AT_CHECK([ovn-sbctl dump-flows | grep -E
    "ls_.*_acl" | sed 's/table=../table=??/
        table=??(ls_out_acl_action  ), priority=1000 , match=(reg8[[17]]
    == 1), action=(reg8[[16]] = 0; reg8[[17]] = 0; reg8[[18]] = 0; /*
    drop */)
        table=??(ls_out_acl_action  ), priority=1000 , match=(reg8[[18]]
    == 1), action=(reg8[[16]] = 0; reg8[[17]] = 0; reg8[[18]] = 0; reg0
    = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is
    implicit. */ outport <-> inport; next(pipeline=ingress,table=27); };)
        table=??(ls_out_acl_eval    ), priority=0    , match=(1),
    action=(next;)
    -  table=??(ls_out_acl_eval    ), priority=1001 , match=(ip4 &&
    tcp), action=(reg8[[16]] = 1; next;)
    +  table=??(ls_out_acl_eval    ), priority=1001 , match=((ip4 &&
    tcp)), action=(reg8[[16]] = 1; next;)
        table=??(ls_out_acl_eval    ), priority=34000, match=(eth.src ==
    $svc_monitor_mac), action=(reg8[[16]] = 1; next;)
        table=??(ls_out_acl_hint    ), priority=0    , match=(1),
    action=(next;)
        table=??(ls_out_pre_acl     ), priority=0    , match=(1),
    action=(next;)
    @@ -7749,7 +7749,7 @@ AT_CHECK([ovn-sbctl dump-flows | grep -E
    "ls_.*_acl" | sed 's/table=../table=??/
        table=??(ls_out_acl_action  ), priority=1000 , match=(reg8[[17]]
    == 1), action=(reg8[[16]] = 0; reg8[[17]] = 0; reg8[[18]] = 0; /*
    drop */)
        table=??(ls_out_acl_action  ), priority=1000 , match=(reg8[[18]]
    == 1), action=(reg8[[16]] = 0; reg8[[17]] = 0; reg8[[18]] = 0; reg0
    = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is
    implicit. */ outport <-> inport; next(pipeline=ingress,table=27); };)
        table=??(ls_out_acl_eval    ), priority=0    , match=(1),
    action=(next;)
    -  table=??(ls_out_acl_eval    ), priority=1001 , match=(ip4 &&
    tcp), action=(reg8[[16]] = 1; next;)
    +  table=??(ls_out_acl_eval    ), priority=1001 , match=((ip4 &&
    tcp)), action=(reg8[[16]] = 1; next;)
        table=??(ls_out_acl_eval    ), priority=34000, match=(eth.src ==
    $svc_monitor_mac), action=(reg8[[16]] = 1; next;)
        table=??(ls_out_acl_hint    ), priority=0    , match=(1),
    action=(next;)
        table=??(ls_out_pre_acl     ), priority=0    , match=(1),
    action=(next;)
    @@ -8998,3 +8998,131 @@ mac_binding_timestamp: true

      AT_CLEANUP
      ])
    +
    +OVN_FOR_EACH_NORTHD_NO_HV([
    +AT_SETUP([Tiered ACL logical flows])
    +AT_KEYWORDS([acl])
    +
    +ovn_start
    +
    +check ovn-nbctl ls-add ls
    +check ovn-nbctl lsp-add ls lsp
    +check ovn-nbctl pg-add pg lsp
    +
    +m4_define([ACL_FLOWS], [grep -w $1 lflows | grep "$2" | sed
    's/table=../table=??/' | sed "s/\($1[[^)]]*\)/$1/" | sort])
    +
    +acl_test() {
    +    direction=$1
    +    options=$2
    +    thing=$3
    +    eval_stage=$4
    +    action_stage=$5
    +    eval_stage_table=$6
    +
    +    if test "$direction" = "from-lport" ; then
    +        pipeline=ingress
    +    else
    +        pipeline=egress
    +    fi
    +
    +    # Baseline test. Ensure that no ACL evaluation or tier-related
    flows are
    +    # installed.
    +    ovn-sbctl lflow-list ls > lflows
    +    AT_CHECK([ACL_FLOWS([$eval_stage], [priority=2000])], [0], [])
    +
    +    AT_CHECK([ACL_FLOWS([$action_stage], [priority=500])], [0], [])
    +
    +    # Add an untiered ACL. Ensure that the ACL appears in the eval
    stage, and
    +    # that no tier-related flows appear in the action stage.
    +    check ovn-nbctl --wait=sb $options acl-add $thing $direction
    1000 "ip4.addr == 80.111.111.112" drop
    +    acl1_uuid=$(ovn-nbctl --bare --columns _uuid find ACL
    priority=1000)
    +
    +    ovn-sbctl lflow-list ls > lflows
    +    AT_CAPTURE_FILE([lflows])
    +    AT_CHECK_UNQUOTED([ACL_FLOWS([$eval_stage], [priority=2000])],
    [0], [dnl
    +  table=??($eval_stage), priority=2000 , match=((ip4.addr ==
    80.111.111.112)), action=(reg8[[17]] = 1; next;)
    +])
    +
    +    AT_CHECK([ACL_FLOWS([$action_stage], [priority=500])], [0], [])
    +
    +    # Explicitly name the tier on the ACL to be tier 0. This should
    have no
    +    # effect on the logical flows.
    +    check ovn-nbctl --wait=sb set ACL $acl1_uuid tier=0
    +    ovn-sbctl lflow-list ls > lflows
    +    AT_CHECK_UNQUOTED([ACL_FLOWS([$eval_stage], [priority=2000])],
    [0], [dnl
    +  table=??($eval_stage), priority=2000 , match=((ip4.addr ==
    80.111.111.112)), action=(reg8[[17]] = 1; next;)
    +])
    +    AT_CHECK([ACL_FLOWS([$action_stage], [priority=500])], [0], [])
    +
    +    # Change the ACL to tier 1. Now we should see the tier as part
    of the ACL
    +    # match, and we should see a flow in the action stage to bump
    the tier
    +    # to 1 if there was no match on tier 0.
    +    check ovn-nbctl --wait=sb set ACL $acl1_uuid tier=1
    +    ovn-sbctl lflow-list ls > lflows
    +    AT_CHECK_UNQUOTED([ACL_FLOWS([$eval_stage], [priority=2000])],
    [0], [dnl
    +  table=??($eval_stage), priority=2000 , match=(reg8[[30..31]] == 1
    && (ip4.addr == 80.111.111.112)), action=(reg8[[17]] = 1; next;)
    +])
    +
    +    AT_CHECK_UNQUOTED([ACL_FLOWS([$action_stage], [priority=500])],
    [0], [dnl
    +  table=??($action_stage), priority=500  , match=(reg8[[30..31]] ==
    0), action=(reg8[[30..31]] = 1;
    next(pipeline=$pipeline,table=$eval_stage_table);)
    +])
    +
    +    # Change the ACL to tier 3. Ensure the tier match on the ACL
    has been
    +    # updated, and ensure we see three flows present for
    incrementing the
    +    # tier value in the action stage.
    +    check ovn-nbctl --wait=sb set ACL $acl1_uuid tier=3
    +    ovn-sbctl lflow-list ls > lflows
    +    AT_CHECK_UNQUOTED([ACL_FLOWS([$eval_stage], [priority=2000])],
    [0], [dnl
    +  table=??($eval_stage), priority=2000 , match=(reg8[[30..31]] == 3
    && (ip4.addr == 80.111.111.112)), action=(reg8[[17]] = 1; next;)
    +])
    +
    +    AT_CHECK_UNQUOTED([ACL_FLOWS([$action_stage], [priority=500])],
    [0], [dnl
    +  table=??($action_stage), priority=500  , match=(reg8[[30..31]] ==
    0), action=(reg8[[30..31]] = 1;
    next(pipeline=$pipeline,table=$eval_stage_table);)
    +  table=??($action_stage), priority=500  , match=(reg8[[30..31]] ==
    1), action=(reg8[[30..31]] = 2;
    next(pipeline=$pipeline,table=$eval_stage_table);)
    +  table=??($action_stage), priority=500  , match=(reg8[[30..31]] ==
    2), action=(reg8[[30..31]] = 3;
    next(pipeline=$pipeline,table=$eval_stage_table);)
    +])
    +
    +    # Add an untiered ACL. Ensure that it matches on tier 0, but
    otherwise,
    +    # nothing else should have changed in the logical flows.
    +    check ovn-nbctl --wait=sb $options acl-add $thing $direction
    1000 "ip4.addr == 83.104.105.116" allow
    +    ovn-sbctl lflow-list ls > lflows
    +    AT_CHECK_UNQUOTED([ACL_FLOWS([$eval_stage], [priority=2000])],
    [0], [dnl
    +  table=??($eval_stage), priority=2000 , match=(reg8[[30..31]] == 0
    && (ip4.addr == 83.104.105.116)), action=(reg8[[16]] = 1; next;)
    +  table=??($eval_stage), priority=2000 , match=(reg8[[30..31]] == 3
    && (ip4.addr == 80.111.111.112)), action=(reg8[[17]] = 1; next;)
    +])
    +
    +    AT_CHECK_UNQUOTED([ACL_FLOWS([$action_stage], [priority=500])],
    [0], [dnl
    +  table=??($action_stage), priority=500  , match=(reg8[[30..31]] ==
    0), action=(reg8[[30..31]] = 1;
    next(pipeline=$pipeline,table=$eval_stage_table);)
    +  table=??($action_stage), priority=500  , match=(reg8[[30..31]] ==
    1), action=(reg8[[30..31]] = 2;
    next(pipeline=$pipeline,table=$eval_stage_table);)
    +  table=??($action_stage), priority=500  , match=(reg8[[30..31]] ==
    2), action=(reg8[[30..31]] = 3;
    next(pipeline=$pipeline,table=$eval_stage_table);)
    +])
    +
    +    # Remove the tier 3 ACL. The remaining ACL is untiered, and
    there are no
    +    # other tiered ACLs. So we should go back to not checking the tier
    +    # number in the ACL match, and there should be no tier-related
    flows
    +    # in the action stage.
    +    check ovn-nbctl --wait=sb acl-del $thing $direction 1000
    "ip4.addr == 80.111.111.112"
    +    ovn-sbctl lflow-list ls > lflows
    +    AT_CHECK_UNQUOTED([ACL_FLOWS([$eval_stage], [priority=2000])],
    [0], [dnl
    +  table=??($eval_stage), priority=2000 , match=((ip4.addr ==
    83.104.105.116)), action=(reg8[[16]] = 1; next;)
    +])
    +
    +    AT_CHECK([ACL_FLOWS([$action_stage], [priority=500])], [0], [])
    +
    +    check ovn-nbctl --wait=sb acl-del $thing
    +    ovn-sbctl lflow-list ls > lflows
    +
    +    AT_CHECK([ACL_FLOWS([$eval_stage], [priority=2000])], [0], [])
    +
    +    AT_CHECK([ACL_FLOWS([$action_stage], [priority=500])], [0], [])
    +}
    +
    +acl_test from-lport "" ls ls_in_acl_eval ls_in_acl_action 8
    +acl_test from-lport "--apply-after-lb" ls ls_in_acl_after_lb_eval
    ls_in_acl_after_lb_action 18
    +acl_test to-lport "" ls ls_out_acl_eval ls_out_acl_action 4
    +acl_test from-lport "" pg ls_in_acl_eval ls_in_acl_action 8
    +acl_test from-lport "--apply-after-lb" pg ls_in_acl_after_lb_eval
    ls_in_acl_after_lb_action 18
    +acl_test to-lport "" pg ls_out_acl_eval ls_out_acl_action 4
    +
    +AT_CLEANUP
    +])
    diff --git a/tests/system-ovn.at <http://system-ovn.at>
    b/tests/system-ovn.at <http://system-ovn.at>
    index 6d1ce9577..620e29cf6 100644
    --- a/tests/system-ovn.at <http://system-ovn.at>
    +++ b/tests/system-ovn.at <http://system-ovn.at>
    @@ -10784,3 +10784,110 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/failed to
    query port patch-.*/d
      /connection dropped.*/d"])
      AT_CLEANUP
      ])
    +
    +OVN_FOR_EACH_NORTHD([
    +AT_SETUP([Tiered ACLs])
    +AT_KEYWORDS([acl])
    +
    +ovn_start
    +OVS_TRAFFIC_VSWITCHD_START()
    +ADD_BR([br-int])
    +
    +# Set external-ids in br-int needed for ovn-controller
    +check ovs-vsctl \
    +        -- set Open_vSwitch . external-ids:system-id=hv1 \
    +        -- set Open_vSwitch .
    external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
    +        -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
    +        -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \
    +        -- set bridge br-int fail-mode=secure
    other-config:disable-in-band=true
    +
    +# Start ovn-controller
    +start_daemon ovn-controller
    +
    +check ovn-nbctl ls-add ls
    +check ovn-nbctl lsp-add ls lsp1 -- lsp-set-addresses lsp1
    "00:00:00:00:00:01 10.0.0.1"
    +check ovn-nbctl lsp-add ls lsp2 -- lsp-set-addresses lsp2
    "00:00:00:00:00:02 10.0.0.2"
    +
    +check ovn-nbctl pg-add pg lsp1 lsp2
    +
    +ADD_NAMESPACES(lsp1)
    +ADD_VETH(lsp1, lsp1, br-int, "10.0.0.1/24 <http://10.0.0.1/24>",
    "00:00:00:00:00:01")
    +ADD_NAMESPACES(lsp2)
    +ADD_VETH(lsp2, lsp2, br-int, "10.0.0.2/24 <http://10.0.0.2/24>",
    "00:00:00:00:00:02")
    +
    +m4_define([PING_PCT], [grep -o "[[0-9]]\{1,3\}% packet loss"])
    +
    +acl_test() {
    +    direction=$1
    +    options=$2
    +    thing=$3
    +
    +    # First a baseline. If traffic isn't being allowed, then
    something is
    +    # very wrong.
    +    NS_CHECK_EXEC([lsp1], [ping -q -c 3 -i 0.3 -w 2 10.0.0.2 |
    PING_PCT], \
    +[0], [dnl
    +0% packet loss
    +])
    +    # Add an untiered drop ACL. This should cause pings to fail.
    +    check ovn-nbctl --wait=sb $options acl-add $thing $direction
    1000 "ip4.dst == 10.0.0.2" drop
    +    acl1_uuid=$(ovn-nbctl --bare --columns _uuid find ACL
    priority=1000)
    +
    +    NS_CHECK_EXEC([lsp1], [ping -q -c 3 -i 0.3 -w 2 10.0.0.2 |
    PING_PCT], \
    +[0], [dnl
    +100% packet loss
    +])
    +
    +    # Change the tier to 3. Despite there being "holes" in tiers 0,
    1, and 2,
    +    # the ACL should still apply, and pings should fail.
    +    check ovn-nbctl --wait=sb set ACL $acl1_uuid tier=3
    +    NS_CHECK_EXEC([lsp1], [ping -q -c 3 -i 0.3 -w 2 10.0.0.2 |
    PING_PCT], \
    +[0], [dnl
    +100% packet loss
    +])
    +
    +    # Add a tier-0 ACL that allows the traffic. The priority is
    only 4, but
    +    # since it is a higher tier, the traffic should be allowed.
    +    check ovn-nbctl --wait=sb $options acl-add $thing $direction 4
    "ip4.dst == 10.0.0.2" allow
    +    NS_CHECK_EXEC([lsp1], [ping -q -c 3 -i 0.3 -w 2 10.0.0.2 |
    PING_PCT], \
    +[0], [dnl
    +0% packet loss
    +])
    +
    +    # Removing the 0-tier ACL should make traffic go back to being
    dropped.
    +    check ovn-nbctl --wait=sb acl-del $thing $direction 4 "ip4.dst
    == 10.0.0.2"
    +    NS_CHECK_EXEC([lsp1], [ping -q -c 3 -i 0.3 -w 2 10.0.0.2 |
    PING_PCT], \
    +[0], [dnl
    +100% packet loss
    +])
    +
    +    # Removing all ACLs should make traffic go back to passing.
    +    check ovn-nbctl --wait=sb acl-del $thing
    +    NS_CHECK_EXEC([lsp1], [ping -q -c 3 -i 0.3 -w 2 10.0.0.2 |
    PING_PCT], \
    +[0], [dnl
    +0% packet loss
    +])
    +}
    +
    +acl_test from-lport "" ls
    +acl_test from-lport "--apply-after-lb" ls
    +acl_test to-lport "" ls
    +acl_test from-lport "" pg
    +acl_test from-lport "--apply-after-lb" pg
    +acl_test to-lport "" pg
    +
    +OVS_APP_EXIT_AND_WAIT([ovn-controller])
    +
    +as ovn-sb
    +OVS_APP_EXIT_AND_WAIT([ovsdb-server])
    +
    +as ovn-nb
    +OVS_APP_EXIT_AND_WAIT([ovsdb-server])
    +
    +as northd
    +OVS_APP_EXIT_AND_WAIT([NORTHD_TYPE])
    +
    +as
    +OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
    +/connection dropped.*/d"])
    +AT_CLEANUP
    +])
-- 2.38.1

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


Other than that and the error that causes the CI failure it looks good.

Thanks,
Ales

--

Ales Musil

Senior Software Engineer - OVN Core

Red Hat EMEA <https://www.redhat.com>

[email protected] <mailto:[email protected]> IM: amusil

<https://red.ht/sig>



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

Reply via email to