diff mbox series

[ovs-dev] ovn-northd: Optimize logical flow generation for reject ACLs.

Message ID 20200924164955.2733564-1-numans@ovn.org
State Changes Requested
Headers show
Series [ovs-dev] ovn-northd: Optimize logical flow generation for reject ACLs. | expand

Commit Message

Numan Siddique Sept. 24, 2020, 4:49 p.m. UTC
From: Numan Siddique <numans@ovn.org>

ovn-northd adds below lflows for a reject ACL with a match - M

match = (ip4 && tcp && 'M') action = tcp_reject{}
match = (ip6 && tcp && 'M') action = tcp_reject{}
match = (ip4 && 'M') action = icmp4{}
match = (ip6 && 'M') action = icmp6{}

This approach has a couple of problems:
   - ovn-controller can reject the lflows if there are invalid matches.
     Eg. If match 'M' is - 'ip4 && udp'.

   - In a large scale deployment, this could result in lot of invalid
     logical flows and increase the size of the SB DB.

This patch addresses this problem by providing an option to the CMS
to specify the l3 protocol and l4 protocol as hints. A new column
'options' is added to the ACL table with the options -
options:l3-protocol and options:l4-protocol.

ovn-northd will now generate only the required lflows for the reject
ACL. If no options are set, then it falls back to the default scenario.

Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=1876990
Reported-by: Ilya Maximets <i.maximets@ovn.org>
Signed-off-by: Numan Siddique <numans@ovn.org>
---
 northd/ovn-northd.c | 194 ++++++++++++++++++++----------
 ovn-nb.ovsschema    |   7 +-
 ovn-nb.xml          |  43 +++++++
 tests/ovn-northd.at | 285 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 461 insertions(+), 68 deletions(-)

Comments

Han Zhou Sept. 28, 2020, 12:26 a.m. UTC | #1
Thanks Numan. I have some comments on the solution.

On Thu, Sep 24, 2020 at 9:50 AM <numans@ovn.org> wrote:
>
> From: Numan Siddique <numans@ovn.org>
>
> ovn-northd adds below lflows for a reject ACL with a match - M
>
> match = (ip4 && tcp && 'M') action = tcp_reject{}
> match = (ip6 && tcp && 'M') action = tcp_reject{}
> match = (ip4 && 'M') action = icmp4{}
> match = (ip6 && 'M') action = icmp6{}
>
> This approach has a couple of problems:
>    - ovn-controller can reject the lflows if there are invalid matches.
>      Eg. If match 'M' is - 'ip4 && udp'.
>
>    - In a large scale deployment, this could result in lot of invalid
>      logical flows and increase the size of the SB DB.
>
> This patch addresses this problem by providing an option to the CMS
> to specify the l3 protocol and l4 protocol as hints. A new column
> 'options' is added to the ACL table with the options -
> options:l3-protocol and options:l4-protocol.
>
> ovn-northd will now generate only the required lflows for the reject
> ACL. If no options are set, then it falls back to the default scenario.
>

Thanks Numan. I believe the solution should work. However, it seems a
little confusing for the end user about how to use the hint field. I think
maybe we should try to avoid the extra field if possible. Here is my
proposal:

1. For the tcp v.s. udp check, since we don't want northd to parse the
match condition, we will have to rely on users input. However, instead of a
hint, I think it is better to let the user explicitly specify in the action
field. E.g. "reject" means sending icmp, and "tcp_reject" means sending tcp
reset if the traffic is tcp and otherwise sending icmp. So, if the user
adds an ACL with match include "udp", they will naturally use "reject"
instead of "tcp_reject" because they know it is not possible for a TCP
packet to hit this ACL. If they add an ACL that tcp traffic is expected to
match (either TCP only, or general IP traffic), they can use "tcp_reject".
Would this be more straightforward than an extra "hint" field?

2. For ip4 v.s. ip6, the idea is to support generic ovn logical flow
actions "tcp_reject" and "icmp" in ovn-controller, which can handle both
ipv4 and ipv6. If this can be supported, the logical flows only needs to be:
match = (tcp && 'M') action = tcp_reject{}
match = ('M') action = icmp{}

I haven't thought through all the details but it seems doable. Did you have
any thoughts on this? (If it proves to be unreasonable I am ok with current
"hint" approach)

In addition, I found the existing TCP reject handling has a bug which
messes up the user defined ACL priorities. I just posted a patch:
https://patchwork.ozlabs.org/project/ovn/patch/20200928000904.858685-1-hzhou@ovn.org/
It is not directly related to this patch but it would be great if you could
take a look.

Thanks,
Han


> Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=1876990
> Reported-by: Ilya Maximets <i.maximets@ovn.org>
> Signed-off-by: Numan Siddique <numans@ovn.org>
> ---
>  northd/ovn-northd.c | 194 ++++++++++++++++++++----------
>  ovn-nb.ovsschema    |   7 +-
>  ovn-nb.xml          |  43 +++++++
>  tests/ovn-northd.at | 285 ++++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 461 insertions(+), 68 deletions(-)
>
> diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
> index 3324c9e81d..d167137e07 100644
> --- a/northd/ovn-northd.c
> +++ b/northd/ovn-northd.c
> @@ -5372,73 +5372,135 @@ build_reject_acl_rules(struct ovn_datapath *od,
struct hmap *lflows,
>      struct ds actions = DS_EMPTY_INITIALIZER;
>      bool ingress = (stage == S_SWITCH_IN_ACL);
>
> -    /* TCP */
> -    build_acl_log(&actions, acl);
> -    if (extra_match->length > 0) {
> -        ds_put_format(&match, "(%s) && ", extra_match->string);
> -    }
> -    ds_put_format(&match, "ip4 && tcp && (%s)", acl->match);
> -    ds_put_format(&actions, "reg0 = 0; "
> -                  "eth.dst <-> eth.src; ip4.dst <-> ip4.src; "
> -                  "tcp_reset { outport <-> inport; %s };",
> -                  ingress ? "next(pipeline=egress,table=5);"
> -                          : "next(pipeline=ingress,table=20);");
> -    ovn_lflow_add_with_hint(lflows, od, stage,
> -                            acl->priority + OVN_ACL_PRI_OFFSET + 10,
> -                            ds_cstr(&match), ds_cstr(&actions),
stage_hint);
> -    ds_clear(&match);
> -    ds_clear(&actions);
> -    build_acl_log(&actions, acl);
> -    if (extra_match->length > 0) {
> -        ds_put_format(&match, "(%s) && ", extra_match->string);
> -    }
> -    ds_put_format(&match, "ip6 && tcp && (%s)", acl->match);
> -    ds_put_format(&actions, "reg0 = 0; "
> -                  "eth.dst <-> eth.src; ip6.dst <-> ip6.src; "
> -                  "tcp_reset { outport <-> inport; %s };",
> -                  ingress ? "next(pipeline=egress,table=5);"
> -                          : "next(pipeline=ingress,table=20);");
> -    ovn_lflow_add_with_hint(lflows, od, stage,
> -                            acl->priority + OVN_ACL_PRI_OFFSET + 10,
> -                            ds_cstr(&match), ds_cstr(&actions),
stage_hint);
> +    bool is_ip4 = true;
> +    bool is_ip6 = true;
> +    bool tcp_reset = true;
> +    bool icmp_reset = true;
> +    bool is_udp = false;
> +
> +    const char *l3_protocol = smap_get(&acl->options, "l3-protocol");
> +    if (l3_protocol) {
> +        if (!strcasecmp(l3_protocol, "ip")) {
> +            is_ip4 = true;
> +            is_ip6 = true;
> +        } else if (!strcasecmp(l3_protocol, "ip4")) {
> +            is_ip4 = true;
> +            is_ip6 = false;
> +        } else if (!strcasecmp(l3_protocol, "ip6")) {
> +            is_ip6 = true;
> +            is_ip4 = false;
> +        }
> +    }
> +
> +    const char *l4_protocol = smap_get(&acl->options, "l4-protocol");
> +    if (l4_protocol) {
> +        if (!strcasecmp(l4_protocol, "tcp")) {
> +            tcp_reset = true;
> +            icmp_reset = false;
> +        } else if (!strcasecmp(l4_protocol, "udp")) {
> +            tcp_reset = false;
> +            is_udp = true;
> +        } else {
> +            tcp_reset = false;
> +        }
> +    }
>
> -    /* IP traffic */
> -    ds_clear(&match);
> -    ds_clear(&actions);
> -    build_acl_log(&actions, acl);
> -    if (extra_match->length > 0) {
> -        ds_put_format(&match, "(%s) && ", extra_match->string);
> -    }
> -    ds_put_format(&match, "ip4 && (%s)", acl->match);
> -    if (extra_actions->length > 0) {
> -        ds_put_format(&actions, "%s ", extra_actions->string);
> -    }
> -    ds_put_format(&actions, "reg0 = 0; "
> -                  "icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src; "
> -                  "outport <-> inport; %s };",
> -                  ingress ? "next(pipeline=egress,table=5);"
> -                          : "next(pipeline=ingress,table=20);");
> -    ovn_lflow_add_with_hint(lflows, od, stage,
> -                            acl->priority + OVN_ACL_PRI_OFFSET,
> -                            ds_cstr(&match), ds_cstr(&actions),
stage_hint);
> -    ds_clear(&match);
> -    ds_clear(&actions);
> -    build_acl_log(&actions, acl);
> -    if (extra_match->length > 0) {
> -        ds_put_format(&match, "(%s) && ", extra_match->string);
> -    }
> -    ds_put_format(&match, "ip6 && (%s)", acl->match);
> -    if (extra_actions->length > 0) {
> -        ds_put_format(&actions, "%s ", extra_actions->string);
> -    }
> -    ds_put_format(&actions, "reg0 = 0; icmp6 { "
> -                  "eth.dst <-> eth.src; ip6.dst <-> ip6.src; "
> -                  "outport <-> inport; %s };",
> -                  ingress ? "next(pipeline=egress,table=5);"
> -                          : "next(pipeline=ingress,table=20);");
> -    ovn_lflow_add_with_hint(lflows, od, stage,
> -                            acl->priority + OVN_ACL_PRI_OFFSET,
> -                            ds_cstr(&match), ds_cstr(&actions),
stage_hint);
> +    if (is_ip4) {
> +        if (tcp_reset) {
> +            build_acl_log(&actions, acl);
> +            if (extra_match->length > 0) {
> +                ds_put_format(&match, "(%s) && ", extra_match->string);
> +            }
> +            ds_put_format(&match, "ip4 && tcp && (%s)", acl->match);
> +            if (extra_actions->length > 0) {
> +                ds_put_format(&actions, "%s ", extra_actions->string);
> +            }
> +            ds_put_format(&actions, "reg0 = 0; "
> +                          "eth.dst <-> eth.src; ip4.dst <-> ip4.src; "
> +                          "tcp_reset { outport <-> inport; %s };",
> +                          ingress ? "next(pipeline=egress,table=5);"
> +                                  : "next(pipeline=ingress,table=20);");
> +            ovn_lflow_add_with_hint(lflows, od, stage,
> +                                    acl->priority + OVN_ACL_PRI_OFFSET +
10,
> +                                    ds_cstr(&match), ds_cstr(&actions),
> +                                    stage_hint);
> +        }
> +
> +        if (icmp_reset) {
> +            ds_clear(&match);
> +            ds_clear(&actions);
> +            build_acl_log(&actions, acl);
> +            if (extra_match->length > 0) {
> +                ds_put_format(&match, "(%s) && ", extra_match->string);
> +            }
> +            ds_put_format(&match, "ip4 && (%s)", acl->match);
> +            if (is_udp) {
> +                ds_put_cstr(&match, " && udp");
> +            }
> +            if (extra_actions->length > 0) {
> +                ds_put_format(&actions, "%s ", extra_actions->string);
> +            }
> +            ds_put_format(&actions, "reg0 = 0; "
> +                          "icmp4 { eth.dst <-> eth.src; ip4.dst <->
ip4.src; "
> +                          "outport <-> inport; %s };",
> +                          ingress ? "next(pipeline=egress,table=5);"
> +                                  : "next(pipeline=ingress,table=20);");
> +            ovn_lflow_add_with_hint(lflows, od, stage,
> +                                    acl->priority + OVN_ACL_PRI_OFFSET,
> +                                    ds_cstr(&match), ds_cstr(&actions),
> +                                    stage_hint);
> +        }
> +    }
> +
> +    if (is_ip6) {
> +        if (tcp_reset) {
> +            ds_clear(&match);
> +            ds_clear(&actions);
> +            build_acl_log(&actions, acl);
> +            if (extra_match->length > 0) {
> +                ds_put_format(&match, "(%s) && ", extra_match->string);
> +            }
> +            ds_put_format(&match, "ip6 && tcp && (%s)", acl->match);
> +            if (extra_actions->length > 0) {
> +                ds_put_format(&actions, "%s ", extra_actions->string);
> +            }
> +            ds_put_format(&actions, "reg0 = 0; "
> +                          "eth.dst <-> eth.src; ip6.dst <-> ip6.src; "
> +                          "tcp_reset { outport <-> inport; %s };",
> +                          ingress ? "next(pipeline=egress,table=5);"
> +                                  : "next(pipeline=ingress,table=20);");
> +            ovn_lflow_add_with_hint(lflows, od, stage,
> +                                    acl->priority + OVN_ACL_PRI_OFFSET +
10,
> +                                    ds_cstr(&match), ds_cstr(&actions),
> +                                    stage_hint);
> +        }
> +
> +        if (icmp_reset) {
> +            ds_clear(&match);
> +            ds_clear(&actions);
> +            build_acl_log(&actions, acl);
> +            if (extra_match->length > 0) {
> +                ds_put_format(&match, "(%s) && ", extra_match->string);
> +            }
> +            ds_put_format(&match, "ip6 && (%s)", acl->match);
> +            if (is_udp) {
> +                ds_put_cstr(&match, " && udp");
> +            }
> +
> +            if (extra_actions->length > 0) {
> +                ds_put_format(&actions, "%s ", extra_actions->string);
> +            }
> +            ds_put_format(&actions, "reg0 = 0; icmp6 { "
> +                          "eth.dst <-> eth.src; ip6.dst <-> ip6.src; "
> +                          "outport <-> inport; %s };",
> +                          ingress ? "next(pipeline=egress,table=5);"
> +                                : "next(pipeline=ingress,table=20);");
> +            ovn_lflow_add_with_hint(lflows, od, stage,
> +                                    acl->priority + OVN_ACL_PRI_OFFSET,
> +                                    ds_cstr(&match), ds_cstr(&actions),
> +                                    stage_hint);
> +        }
> +    }
>
>      ds_destroy(&match);
>      ds_destroy(&actions);
> diff --git a/ovn-nb.ovsschema b/ovn-nb.ovsschema
> index 092322ab2c..00f1c7cd4b 100644
> --- a/ovn-nb.ovsschema
> +++ b/ovn-nb.ovsschema
> @@ -1,7 +1,7 @@
>  {
>      "name": "OVN_Northbound",
> -    "version": "5.27.0",
> -    "cksum": "3507518247 26773",
> +    "version": "5.28.0",
> +    "cksum": "699859908 26928",
>      "tables": {
>          "NB_Global": {
>              "columns": {
> @@ -225,6 +225,9 @@
>                                                          "debug"]]},
>                                        "min": 0, "max": 1}},
>                  "meter": {"type": {"key": "string", "min": 0, "max": 1}},
> +                "options": {
> +                    "type": {"key": "string", "value": "string",
> +                             "min": 0, "max": "unlimited"}},
>                  "external_ids": {
>                      "type": {"key": "string", "value": "string",
>                               "min": 0, "max": "unlimited"}}},
> diff --git a/ovn-nb.xml b/ovn-nb.xml
> index 86195af341..2c3497e2ae 100644
> --- a/ovn-nb.xml
> +++ b/ovn-nb.xml
> @@ -1721,6 +1721,49 @@
>        </ul>
>      </column>
>
> +    <group title="Common options">
> +      <column name="options">
> +        This column provides general key/value settings. The supported
> +        options are described individually below.
> +      </column>
> +
> +      <group title="Options for providing protocol hints for reject
ACL.">
> +        <p>
> +          The ACL match specified in the
> +          <ref column="match" table="ACL" db="OVN_Northbound"/> column is
> +          opaque to <code>OVN</code> and <code>ovn-northd</code> doesn't
> +          look into the match fields.
> +          These options can be specified by CMS to provide hints to
> +          <code>OVN</code> about the L3 and L4 protocol matches for the
reject
> +          ACL. <code>ovn-northd</code> uses these options if set to use
> +          appropriate actions when generating logical flows.
> +        </p>
> +
> +        <p>
> +          If these options are not set, then <code>ovn-northd</code>
assumes
> +          the reject ACL applies to IPv4, IPv6 and TCP packets.
> +        </p>
> +
> +        <column name="options" key="l3-protocol">
> +          The possible values are <code>ip</code>, <code>ip4</code> and
> +          <code>ip6</code>. If the value is <code>ip</code>, it means the
> +          reject ACL action applies to both IPv4 and IPv6 packets. If the
> +          value is <code>ip4</code>, it means the reject ACL action
applies to
> +          IPv4 packets and the value <code>ip6</code> applies to IPv6
packets.
> +        </column>
> +
> +        <column name="options" key="l4-protocol">
> +          <p>
> +            The possible values are <code>tcp</code> and
<code>udp</code>.
> +            If the value is <code>tcp</code>, it means the
> +            reject ACL action applies to TCP packets. If the
> +            value is <code>udp</code>, it means the reject ACL action
applies
> +            to UDP packets.
> +          </p>
> +        </column>
> +      </group>
> +    </group>
> +
>      <group title="Logging">
>        <p>
>          These columns control whether and how OVN logs packets that
match an
> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> index 99a9204f1f..4a30f8ed07 100644
> --- a/tests/ovn-northd.at
> +++ b/tests/ovn-northd.at
> @@ -2010,3 +2010,288 @@ ovn-nbctl --wait=sb set NB_Global .
options:ignore_lsp_down=true
>  AT_CHECK([ovn-sbctl lflow-list | grep arp | grep 10\.0\.0\.1], [0],
[ignore])
>
>  AT_CLEANUP
> +
> +AT_SETUP([ovn-northd -- ACL reject flows])
> +ovn_start
> +
> +ovn-nbctl ls-add sw0
> +ovn-nbctl lsp-add sw0 sw0-p1
> +ovn-nbctl lsp-add sw0 sw0-p2
> +
> +ovn-nbctl pg-add pg0 sw0-p1 sw0-p2
> +ovn-nbctl acl-add pg0 to-lport 1002 "inport == @pg0 && ip" reject
> +
> +ovn-nbctl --wait=sb sync
> +
> +# If there is a reject ACL wihtout any protocol hints, then ovn-northd
should
> +# generate lflows with ip4 tcp_reset, icmp4, ip6 tcp_reset and icmp6
actions.
> +
> +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
sort], [0], [dnl
> +  table=5 (ls_out_acl         ), priority=2012 , dnl
> +match=(ip4 && tcp && (inport == @pg0 && ip)), dnl
> +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src; tcp_reset {
outport <-> inport; next(pipeline=ingress,table=20); };)
> +  table=5 (ls_out_acl         ), priority=2012 , dnl
> +match=(ip6 && tcp && (inport == @pg0 && ip)), dnl
> +action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src; tcp_reset {
outport <-> inport; next(pipeline=ingress,table=20); };)
> +])
> +
> +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
sort], [0], [dnl
> +  table=5 (ls_out_acl         ), priority=2002 , dnl
> +match=(ip4 && (inport == @pg0 && ip)), dnl
> +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src;
outport <-> inport; next(pipeline=ingress,table=20); };)
> +  table=5 (ls_out_acl         ), priority=2002 , dnl
> +match=(ip6 && (inport == @pg0 && ip)), dnl
> +action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <-> ip6.src;
outport <-> inport; next(pipeline=ingress,table=20); };)
> +])
> +
> +# Set l3-protocol=ip4 for the reject ACL.
> +ovn-nbctl --wait=sb set ACL . options:l3-protocol=ip4
> +
> +# ovn-northd should generate  2 lflows with ip4 tcp_reset and icmp4
actions.
> +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
sort], [0], [dnl
> +  table=5 (ls_out_acl         ), priority=2012 , dnl
> +match=(ip4 && tcp && (inport == @pg0 && ip)), dnl
> +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src; tcp_reset {
outport <-> inport; next(pipeline=ingress,table=20); };)
> +])
> +
> +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
sort], [0], [dnl
> +  table=5 (ls_out_acl         ), priority=2002 , dnl
> +match=(ip4 && (inport == @pg0 && ip)), dnl
> +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src;
outport <-> inport; next(pipeline=ingress,table=20); };)
> +])
> +
> +# Set l4-protocol=tcp for the reject ACL.
> +ovn-nbctl --wait=sb set ACL . options:l4-protocol=tcp
> +
> +# ovn-northd should generate 1 lflow with ip4 tcp_reset action.
> +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
sort], [0], [dnl
> +  table=5 (ls_out_acl         ), priority=2012 , dnl
> +match=(ip4 && tcp && (inport == @pg0 && ip)), dnl
> +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src; tcp_reset {
outport <-> inport; next(pipeline=ingress,table=20); };)
> +])
> +
> +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
sort], [0], [dnl
> +])
> +
> +# Set l4-protocol=udp for the reject ACL.
> +ovn-nbctl --wait=sb set ACL . options:l4-protocol=udp
> +
> +# ovn-northd should generate 1 lflow with udp match and icmp4 action.
> +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
sort], [0], [dnl
> +])
> +
> +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
sort], [0], [dnl
> +  table=5 (ls_out_acl         ), priority=2002 , dnl
> +match=(ip4 && (inport == @pg0 && ip) && udp), dnl
> +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src;
outport <-> inport; next(pipeline=ingress,table=20); };)
> +])
> +
> +# Remove l4-protocol from the reject ACL.
> +ovn-nbctl --wait=sb remove ACL . options l4-protocol
> +
> +# ovn-northd should generate 2 lflow with tcp_reset and icmp4 action.
> +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
sort], [0], [dnl
> +  table=5 (ls_out_acl         ), priority=2012 , dnl
> +match=(ip4 && tcp && (inport == @pg0 && ip)), dnl
> +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src; tcp_reset {
outport <-> inport; next(pipeline=ingress,table=20); };)
> +])
> +
> +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
sort], [0], [dnl
> +  table=5 (ls_out_acl         ), priority=2002 , dnl
> +match=(ip4 && (inport == @pg0 && ip)), dnl
> +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src;
outport <-> inport; next(pipeline=ingress,table=20); };)
> +])
> +
> +# Set l3-protocol to ip.
> +ovn-nbctl --wait=sb set ACL . options:l3-protocol=ip
> +
> +# ovn-northd should generate 4 lflow with ip4 tcp_reset, ip6 tcp_reset,
icmp4 and icmp6 actions.
> +
> +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
sort], [0], [dnl
> +  table=5 (ls_out_acl         ), priority=2012 , dnl
> +match=(ip4 && tcp && (inport == @pg0 && ip)), dnl
> +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src; tcp_reset {
outport <-> inport; next(pipeline=ingress,table=20); };)
> +  table=5 (ls_out_acl         ), priority=2012 , dnl
> +match=(ip6 && tcp && (inport == @pg0 && ip)), dnl
> +action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src; tcp_reset {
outport <-> inport; next(pipeline=ingress,table=20); };)
> +])
> +
> +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
sort], [0], [dnl
> +  table=5 (ls_out_acl         ), priority=2002 , dnl
> +match=(ip4 && (inport == @pg0 && ip)), dnl
> +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src;
outport <-> inport; next(pipeline=ingress,table=20); };)
> +  table=5 (ls_out_acl         ), priority=2002 , dnl
> +match=(ip6 && (inport == @pg0 && ip)), dnl
> +action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <-> ip6.src;
outport <-> inport; next(pipeline=ingress,table=20); };)
> +])
> +
> +# Set l3-protocol=ip6 for the reject ACL.
> +ovn-nbctl --wait=sb set ACL . options:l3-protocol=ip6
> +
> +# ovn-northd should generate  2 lflows with ip6 tcp_reset and icmp6
actions.
> +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
sort], [0], [dnl
> +  table=5 (ls_out_acl         ), priority=2012 , dnl
> +match=(ip6 && tcp && (inport == @pg0 && ip)), dnl
> +action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src; tcp_reset {
outport <-> inport; next(pipeline=ingress,table=20); };)
> +])
> +
> +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
sort], [0], [dnl
> +  table=5 (ls_out_acl         ), priority=2002 , dnl
> +match=(ip6 && (inport == @pg0 && ip)), dnl
> +action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <-> ip6.src;
outport <-> inport; next(pipeline=ingress,table=20); };)
> +])
> +
> +# Set l4-protocol=tcp for the reject ACL.
> +ovn-nbctl --wait=sb set ACL . options:l4-protocol=tcp
> +
> +# ovn-northd should generate 1 lflow with ip6 tcp_reset action.
> +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
sort], [0], [dnl
> +  table=5 (ls_out_acl         ), priority=2012 , dnl
> +match=(ip6 && tcp && (inport == @pg0 && ip)), dnl
> +action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src; tcp_reset {
outport <-> inport; next(pipeline=ingress,table=20); };)
> +])
> +
> +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
sort], [0], [dnl
> +])
> +
> +# Set l4-protocol=udp for the reject ACL.
> +ovn-nbctl --wait=sb set ACL . options:l4-protocol=udp
> +
> +# ovn-northd should generate 1 lflow with udp match and icmp6 action.
> +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
sort], [0], [dnl
> +])
> +
> +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
sort], [0], [dnl
> +  table=5 (ls_out_acl         ), priority=2002 , dnl
> +match=(ip6 && (inport == @pg0 && ip) && udp), dnl
> +action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <-> ip6.src;
outport <-> inport; next(pipeline=ingress,table=20); };)
> +])
> +
> +# Set l3-protocol to ip and l4-protocol=tcp for the reject ACL.
> +ovn-nbctl --wait=sb set ACL . options:l3-protocol=ip
> +ovn-nbctl --wait=sb set ACL . options:l4-protocol=tcp
> +
> +# ovn-northd should generate 2 lflow with ip4 tcp_reset and ip6
tcp_reset actions.
> +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
sort], [0], [dnl
> +  table=5 (ls_out_acl         ), priority=2012 , dnl
> +match=(ip4 && tcp && (inport == @pg0 && ip)), dnl
> +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src; tcp_reset {
outport <-> inport; next(pipeline=ingress,table=20); };)
> +  table=5 (ls_out_acl         ), priority=2012 , dnl
> +match=(ip6 && tcp && (inport == @pg0 && ip)), dnl
> +action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src; tcp_reset {
outport <-> inport; next(pipeline=ingress,table=20); };)
> +])
> +
> +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
sort], [0], [dnl
> +])
> +
> +# Set l3-protocol to ip and l4-protocol=udp for the reject ACL.
> +ovn-nbctl --wait=sb set ACL . options:l3-protocol=ip
> +ovn-nbctl --wait=sb set ACL . options:l4-protocol=udp
> +
> +# ovn-northd should generate 2 lflows (udp match) with icmp4 and icmp6
actions.
> +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
sort], [0], [dnl
> +])
> +
> +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
sort], [0], [dnl
> +  table=5 (ls_out_acl         ), priority=2002 , dnl
> +match=(ip4 && (inport == @pg0 && ip) && udp), dnl
> +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src;
outport <-> inport; next(pipeline=ingress,table=20); };)
> +  table=5 (ls_out_acl         ), priority=2002 , dnl
> +match=(ip6 && (inport == @pg0 && ip) && udp), dnl
> +action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <-> ip6.src;
outport <-> inport; next(pipeline=ingress,table=20); };)
> +])
> +
> +# Add an ACL with allow-related
> +ovn-nbctl --wait=sb acl-add pg0 to-lport 1000 "ip" allow-related
> +
> +# ovn-northd should generate 4 lflow with 2 icmp4 and 2 icmp6 actions.
> +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
sort], [0], [dnl
> +])
> +
> +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
sort], [0], [dnl
> +  table=5 (ls_out_acl         ), priority=2002 , dnl
> +match=((reg0[[10]] == 1) && ip4 && (inport == @pg0 && ip) && udp), dnl\
> +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; icmp4 { eth.dst
<-> eth.src; ip4.dst <-> ip4.src; outport <-> inport;
next(pipeline=ingress,table=20); };)
> +  table=5 (ls_out_acl         ), priority=2002 , dnl
> +match=((reg0[[10]] == 1) && ip6 && (inport == @pg0 && ip) && udp), dnl
> +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; icmp6 { eth.dst
<-> eth.src; ip6.dst <-> ip6.src; outport <-> inport;
next(pipeline=ingress,table=20); };)
> +  table=5 (ls_out_acl         ), priority=2002 , dnl
> +match=((reg0[[9]] == 1) && ip4 && (inport == @pg0 && ip) && udp), dnl
> +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src;
outport <-> inport; next(pipeline=ingress,table=20); };)
> +  table=5 (ls_out_acl         ), priority=2002 , dnl
> +match=((reg0[[9]] == 1) && ip6 && (inport == @pg0 && ip) && udp), dnl
> +action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <-> ip6.src;
outport <-> inport; next(pipeline=ingress,table=20); };)
> +])
> +
> +# Set l3-protocol to ip and l4-protocol=tcp for the reject ACL.
> +rej_acl=$(ovn-nbctl --bare --columns _uuid  find ACL action=reject)
> +ovn-nbctl --wait=sb set ACL $rej_acl options:l3-protocol=ip
> +ovn-nbctl --wait=sb set ACL $rej_acl options:l4-protocol=tcp
> +
> +# ovn-northd should generate 4 lflow with 2 ip4 tcp_reset and 2 ip6
tcp_reset actions.
> +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
sort], [0], [dnl
> +  table=5 (ls_out_acl         ), priority=2012 , dnl
> +match=((reg0[[10]] == 1) && ip4 && tcp && (inport == @pg0 && ip)), dnl
> +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; eth.dst <->
eth.src; ip4.dst <-> ip4.src; tcp_reset { outport <-> inport;
next(pipeline=ingress,table=20); };)
> +  table=5 (ls_out_acl         ), priority=2012 , dnl
> +match=((reg0[[10]] == 1) && ip6 && tcp && (inport == @pg0 && ip)), dnl
> +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; eth.dst <->
eth.src; ip6.dst <-> ip6.src; tcp_reset { outport <-> inport;
next(pipeline=ingress,table=20); };)
> +  table=5 (ls_out_acl         ), priority=2012 , dnl
> +match=((reg0[[9]] == 1) && ip4 && tcp && (inport == @pg0 && ip)), dnl
> +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src; tcp_reset {
outport <-> inport; next(pipeline=ingress,table=20); };)
> +  table=5 (ls_out_acl         ), priority=2012 , dnl
> +match=((reg0[[9]] == 1) && ip6 && tcp && (inport == @pg0 && ip)), dnl
> +action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src; tcp_reset {
outport <-> inport; next(pipeline=ingress,table=20); };)
> +])
> +
> +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
sort], [0], [dnl
> +])
> +
> +
> +# Clear l3-protocol and set l4-protocol to udp
> +ovn-nbctl --wait=sb remove ACL $rej_acl options l3-protocol
> +ovn-nbctl --wait=sb set ACL $rej_acl options:l4-protocol=udp
> +
> +# ovn-northd should generate 4 lflows (with udp match) with 2 icmp4 and
2 icmp6 actions.
> +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
sort], [0], [dnl
> +])
> +
> +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
sort], [0], [dnl
> +  table=5 (ls_out_acl         ), priority=2002 , dnl
> +match=((reg0[[10]] == 1) && ip4 && (inport == @pg0 && ip) && udp), dnl\
> +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; icmp4 { eth.dst
<-> eth.src; ip4.dst <-> ip4.src; outport <-> inport;
next(pipeline=ingress,table=20); };)
> +  table=5 (ls_out_acl         ), priority=2002 , dnl
> +match=((reg0[[10]] == 1) && ip6 && (inport == @pg0 && ip) && udp), dnl
> +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; icmp6 { eth.dst
<-> eth.src; ip6.dst <-> ip6.src; outport <-> inport;
next(pipeline=ingress,table=20); };)
> +  table=5 (ls_out_acl         ), priority=2002 , dnl
> +match=((reg0[[9]] == 1) && ip4 && (inport == @pg0 && ip) && udp), dnl
> +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src;
outport <-> inport; next(pipeline=ingress,table=20); };)
> +  table=5 (ls_out_acl         ), priority=2002 , dnl
> +match=((reg0[[9]] == 1) && ip6 && (inport == @pg0 && ip) && udp), dnl
> +action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <-> ip6.src;
outport <-> inport; next(pipeline=ingress,table=20); };)
> +])
> +
> +# Clear l3-protocol and set l4-protocol to tcp
> +ovn-nbctl --wait=sb remove ACL $rej_acl options l3-protocol
> +ovn-nbctl --wait=sb set ACL $rej_acl options:l4-protocol=tcp
> +
> +# ovn-northd should generate 4 lflow with 2 ip4 tcp_reset and 2 ip6
tcp_reset actions.
> +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
sort], [0], [dnl
> +  table=5 (ls_out_acl         ), priority=2012 , dnl
> +match=((reg0[[10]] == 1) && ip4 && tcp && (inport == @pg0 && ip)), dnl
> +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; eth.dst <->
eth.src; ip4.dst <-> ip4.src; tcp_reset { outport <-> inport;
next(pipeline=ingress,table=20); };)
> +  table=5 (ls_out_acl         ), priority=2012 , dnl
> +match=((reg0[[10]] == 1) && ip6 && tcp && (inport == @pg0 && ip)), dnl
> +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; eth.dst <->
eth.src; ip6.dst <-> ip6.src; tcp_reset { outport <-> inport;
next(pipeline=ingress,table=20); };)
> +  table=5 (ls_out_acl         ), priority=2012 , dnl
> +match=((reg0[[9]] == 1) && ip4 && tcp && (inport == @pg0 && ip)), dnl
> +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src; tcp_reset {
outport <-> inport; next(pipeline=ingress,table=20); };)
> +  table=5 (ls_out_acl         ), priority=2012 , dnl
> +match=((reg0[[9]] == 1) && ip6 && tcp && (inport == @pg0 && ip)), dnl
> +action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src; tcp_reset {
outport <-> inport; next(pipeline=ingress,table=20); };)
> +])
> +
> +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
sort], [0], [dnl
> +])
> +
> +AT_CLEANUP
> --
> 2.26.2
>
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
Numan Siddique Sept. 28, 2020, 8:44 a.m. UTC | #2
On Mon, Sep 28, 2020 at 5:57 AM Han Zhou <hzhou@ovn.org> wrote:
>
> Thanks Numan. I have some comments on the solution.
>
> On Thu, Sep 24, 2020 at 9:50 AM <numans@ovn.org> wrote:
> >
> > From: Numan Siddique <numans@ovn.org>
> >
> > ovn-northd adds below lflows for a reject ACL with a match - M
> >
> > match = (ip4 && tcp && 'M') action = tcp_reject{}
> > match = (ip6 && tcp && 'M') action = tcp_reject{}
> > match = (ip4 && 'M') action = icmp4{}
> > match = (ip6 && 'M') action = icmp6{}
> >
> > This approach has a couple of problems:
> >    - ovn-controller can reject the lflows if there are invalid matches.
> >      Eg. If match 'M' is - 'ip4 && udp'.
> >
> >    - In a large scale deployment, this could result in lot of invalid
> >      logical flows and increase the size of the SB DB.
> >
> > This patch addresses this problem by providing an option to the CMS
> > to specify the l3 protocol and l4 protocol as hints. A new column
> > 'options' is added to the ACL table with the options -
> > options:l3-protocol and options:l4-protocol.
> >
> > ovn-northd will now generate only the required lflows for the reject
> > ACL. If no options are set, then it falls back to the default scenario.
> >
>
> Thanks Numan. I believe the solution should work. However, it seems a
> little confusing for the end user about how to use the hint field. I think
> maybe we should try to avoid the extra field if possible. Here is my
> proposal:
>

Thanks Han for the comments. I do agree if we can avoid the extra option.

> 1. For the tcp v.s. udp check, since we don't want northd to parse the
> match condition, we will have to rely on users input. However, instead of a
> hint, I think it is better to let the user explicitly specify in the action
> field. E.g. "reject" means sending icmp, and "tcp_reject" means sending tcp
> reset if the traffic is tcp and otherwise sending icmp. So, if the user
> adds an ACL with match include "udp", they will naturally use "reject"
> instead of "tcp_reject" because they know it is not possible for a TCP
> packet to hit this ACL. If they add an ACL that tcp traffic is expected to
> match (either TCP only, or general IP traffic), they can use "tcp_reject".
> Would this be more straightforward than an extra "hint" field?
>

I have few comments here.
- If we consider "reject" = "icmp", then we will be breaking the
backward compatibility.
    Right now, for a reject action, ovn-northd would send tcp_reset
for tcp traffic and icmp reply or other traffic.
    After your proposed approach, the  behaviour would change ? Is
that OK ? Because existing test cases would fail.
    The proposed patch handles the backward compatibility. Although
I'm not sure if it is really required in this case.

-  For  a tcp_reject action, if the match has "tcp", then we will end
up adding a logical flow with the icmp action, which is
    really not required. Or do you mean, pinctrl would take the
decision while handling the "tcp_reject" action to either
    send tcp reset packet or icmp packet ? If so, we can probably just
have one action - "reject". Please see below.

> 2. For ip4 v.s. ip6, the idea is to support generic ovn logical flow
> actions "tcp_reject" and "icmp" in ovn-controller, which can handle both
> ipv4 and ipv6. If this can be supported, the logical flows only needs to be:
> match = (tcp && 'M') action = tcp_reject{}
> match = ('M') action = icmp{}
>

Ok. In this case we cannot support inner actions unless they are
independent of the ip version.
i.e we can't support - tcp_reject { eth.dst <-> eth.src; ip4.dst <->
ip4.src; } or
tcp_reject {eth.dst <-> eth.src; ip6.dst <-> ip6.src;} in one logical
flow. ovn-northd has to generate
2 logical flows.
  - ip4 && 'M', action = tcp_reject { eth.dst <-> eth.src; ip4.dst <->
ip4.src; }
  - ip6 && 'M', action = tcp_reject { eth.dst <-> eth.src; ip6.dst <->
ip6.src; }


Right now for one reject ACL we add the below lflows
   - ip4 && tcp && 'M', action = tcp_reset { eth.dst <-> eth.src;
ip4.dst <-> ip4.src; outport <-> inport;}
   - ip6 && tcp && 'M', action = tcp_reset { eth.dst <-> eth.src;
ip6.dst <-> ip6.src; outport <-> inport;}
   - ip4 && 'M'            , action = icmp4 {eth.dst <-> eth.src;
ip4.dst <-> ip4.src; outport <-> inport;}
   - ip6 && 'M'            , action = icmp6 {eth.dst <-> eth.src;
ip6.dst <-> ip6.src; outport <-> inport;}

I have another idea based on your suggestions here: How about we have
a new action - reject{} which will be used
for all reject ACLs and this will
    - send tcp reset packet if the packet is tcp (pinctrl would take
the decision)
    - send icmp packet otherwise.

We can add the logical flow for a reject ACL in 2 ways

1. match = M, action = reject { eth.dst <-> eth.src, outport <-> inport;}

OR

2. match = M, action = reject { eth.dst <-> eth.src, ip.src <->
ip.dst; outport <-> inport;}

In (1), pinctrl while handling the reject action, will swap the ip src
with ip dst (i.e ip4.dst <-> ip4.src or ip6.dst <-> ip6.src }.

In (2), we will add 2 new OVN fields "ip.src" and "ip.dst" (just like
we have icmp4.frag_mtu now) and the action - ip.src <-> ip.dst
will result in another controller action which will swap the ip src
and ip dst fields (just like how "icmp4.frag_mtu = 1400" would result
into a controller action).

Even though (1) will work, it's not transparent. I would personally
prefer (2) and I think it's ok if the reject ACL would result in 2 OF
controller actions.

With this we will have just one logical flow (instead of 4 ) and there
will be no changes on the CMS side.

What do you think ?

Thanks
Numan


> I haven't thought through all the details but it seems doable. Did you have
> any thoughts on this? (If it proves to be unreasonable I am ok with current
> "hint" approach)
>
> In addition, I found the existing TCP reject handling has a bug which
> messes up the user defined ACL priorities. I just posted a patch:
> https://patchwork.ozlabs.org/project/ovn/patch/20200928000904.858685-1-hzhou@ovn.org/
> It is not directly related to this patch but it would be great if you could
> take a look.
>
> Thanks,
> Han
>
>
> > Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=1876990
> > Reported-by: Ilya Maximets <i.maximets@ovn.org>
> > Signed-off-by: Numan Siddique <numans@ovn.org>
> > ---
> >  northd/ovn-northd.c | 194 ++++++++++++++++++++----------
> >  ovn-nb.ovsschema    |   7 +-
> >  ovn-nb.xml          |  43 +++++++
> >  tests/ovn-northd.at | 285 ++++++++++++++++++++++++++++++++++++++++++++
> >  4 files changed, 461 insertions(+), 68 deletions(-)
> >
> > diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
> > index 3324c9e81d..d167137e07 100644
> > --- a/northd/ovn-northd.c
> > +++ b/northd/ovn-northd.c
> > @@ -5372,73 +5372,135 @@ build_reject_acl_rules(struct ovn_datapath *od,
> struct hmap *lflows,
> >      struct ds actions = DS_EMPTY_INITIALIZER;
> >      bool ingress = (stage == S_SWITCH_IN_ACL);
> >
> > -    /* TCP */
> > -    build_acl_log(&actions, acl);
> > -    if (extra_match->length > 0) {
> > -        ds_put_format(&match, "(%s) && ", extra_match->string);
> > -    }
> > -    ds_put_format(&match, "ip4 && tcp && (%s)", acl->match);
> > -    ds_put_format(&actions, "reg0 = 0; "
> > -                  "eth.dst <-> eth.src; ip4.dst <-> ip4.src; "
> > -                  "tcp_reset { outport <-> inport; %s };",
> > -                  ingress ? "next(pipeline=egress,table=5);"
> > -                          : "next(pipeline=ingress,table=20);");
> > -    ovn_lflow_add_with_hint(lflows, od, stage,
> > -                            acl->priority + OVN_ACL_PRI_OFFSET + 10,
> > -                            ds_cstr(&match), ds_cstr(&actions),
> stage_hint);
> > -    ds_clear(&match);
> > -    ds_clear(&actions);
> > -    build_acl_log(&actions, acl);
> > -    if (extra_match->length > 0) {
> > -        ds_put_format(&match, "(%s) && ", extra_match->string);
> > -    }
> > -    ds_put_format(&match, "ip6 && tcp && (%s)", acl->match);
> > -    ds_put_format(&actions, "reg0 = 0; "
> > -                  "eth.dst <-> eth.src; ip6.dst <-> ip6.src; "
> > -                  "tcp_reset { outport <-> inport; %s };",
> > -                  ingress ? "next(pipeline=egress,table=5);"
> > -                          : "next(pipeline=ingress,table=20);");
> > -    ovn_lflow_add_with_hint(lflows, od, stage,
> > -                            acl->priority + OVN_ACL_PRI_OFFSET + 10,
> > -                            ds_cstr(&match), ds_cstr(&actions),
> stage_hint);
> > +    bool is_ip4 = true;
> > +    bool is_ip6 = true;
> > +    bool tcp_reset = true;
> > +    bool icmp_reset = true;
> > +    bool is_udp = false;
> > +
> > +    const char *l3_protocol = smap_get(&acl->options, "l3-protocol");
> > +    if (l3_protocol) {
> > +        if (!strcasecmp(l3_protocol, "ip")) {
> > +            is_ip4 = true;
> > +            is_ip6 = true;
> > +        } else if (!strcasecmp(l3_protocol, "ip4")) {
> > +            is_ip4 = true;
> > +            is_ip6 = false;
> > +        } else if (!strcasecmp(l3_protocol, "ip6")) {
> > +            is_ip6 = true;
> > +            is_ip4 = false;
> > +        }
> > +    }
> > +
> > +    const char *l4_protocol = smap_get(&acl->options, "l4-protocol");
> > +    if (l4_protocol) {
> > +        if (!strcasecmp(l4_protocol, "tcp")) {
> > +            tcp_reset = true;
> > +            icmp_reset = false;
> > +        } else if (!strcasecmp(l4_protocol, "udp")) {
> > +            tcp_reset = false;
> > +            is_udp = true;
> > +        } else {
> > +            tcp_reset = false;
> > +        }
> > +    }
> >
> > -    /* IP traffic */
> > -    ds_clear(&match);
> > -    ds_clear(&actions);
> > -    build_acl_log(&actions, acl);
> > -    if (extra_match->length > 0) {
> > -        ds_put_format(&match, "(%s) && ", extra_match->string);
> > -    }
> > -    ds_put_format(&match, "ip4 && (%s)", acl->match);
> > -    if (extra_actions->length > 0) {
> > -        ds_put_format(&actions, "%s ", extra_actions->string);
> > -    }
> > -    ds_put_format(&actions, "reg0 = 0; "
> > -                  "icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src; "
> > -                  "outport <-> inport; %s };",
> > -                  ingress ? "next(pipeline=egress,table=5);"
> > -                          : "next(pipeline=ingress,table=20);");
> > -    ovn_lflow_add_with_hint(lflows, od, stage,
> > -                            acl->priority + OVN_ACL_PRI_OFFSET,
> > -                            ds_cstr(&match), ds_cstr(&actions),
> stage_hint);
> > -    ds_clear(&match);
> > -    ds_clear(&actions);
> > -    build_acl_log(&actions, acl);
> > -    if (extra_match->length > 0) {
> > -        ds_put_format(&match, "(%s) && ", extra_match->string);
> > -    }
> > -    ds_put_format(&match, "ip6 && (%s)", acl->match);
> > -    if (extra_actions->length > 0) {
> > -        ds_put_format(&actions, "%s ", extra_actions->string);
> > -    }
> > -    ds_put_format(&actions, "reg0 = 0; icmp6 { "
> > -                  "eth.dst <-> eth.src; ip6.dst <-> ip6.src; "
> > -                  "outport <-> inport; %s };",
> > -                  ingress ? "next(pipeline=egress,table=5);"
> > -                          : "next(pipeline=ingress,table=20);");
> > -    ovn_lflow_add_with_hint(lflows, od, stage,
> > -                            acl->priority + OVN_ACL_PRI_OFFSET,
> > -                            ds_cstr(&match), ds_cstr(&actions),
> stage_hint);
> > +    if (is_ip4) {
> > +        if (tcp_reset) {
> > +            build_acl_log(&actions, acl);
> > +            if (extra_match->length > 0) {
> > +                ds_put_format(&match, "(%s) && ", extra_match->string);
> > +            }
> > +            ds_put_format(&match, "ip4 && tcp && (%s)", acl->match);
> > +            if (extra_actions->length > 0) {
> > +                ds_put_format(&actions, "%s ", extra_actions->string);
> > +            }
> > +            ds_put_format(&actions, "reg0 = 0; "
> > +                          "eth.dst <-> eth.src; ip4.dst <-> ip4.src; "
> > +                          "tcp_reset { outport <-> inport; %s };",
> > +                          ingress ? "next(pipeline=egress,table=5);"
> > +                                  : "next(pipeline=ingress,table=20);");
> > +            ovn_lflow_add_with_hint(lflows, od, stage,
> > +                                    acl->priority + OVN_ACL_PRI_OFFSET +
> 10,
> > +                                    ds_cstr(&match), ds_cstr(&actions),
> > +                                    stage_hint);
> > +        }
> > +
> > +        if (icmp_reset) {
> > +            ds_clear(&match);
> > +            ds_clear(&actions);
> > +            build_acl_log(&actions, acl);
> > +            if (extra_match->length > 0) {
> > +                ds_put_format(&match, "(%s) && ", extra_match->string);
> > +            }
> > +            ds_put_format(&match, "ip4 && (%s)", acl->match);
> > +            if (is_udp) {
> > +                ds_put_cstr(&match, " && udp");
> > +            }
> > +            if (extra_actions->length > 0) {
> > +                ds_put_format(&actions, "%s ", extra_actions->string);
> > +            }
> > +            ds_put_format(&actions, "reg0 = 0; "
> > +                          "icmp4 { eth.dst <-> eth.src; ip4.dst <->
> ip4.src; "
> > +                          "outport <-> inport; %s };",
> > +                          ingress ? "next(pipeline=egress,table=5);"
> > +                                  : "next(pipeline=ingress,table=20);");
> > +            ovn_lflow_add_with_hint(lflows, od, stage,
> > +                                    acl->priority + OVN_ACL_PRI_OFFSET,
> > +                                    ds_cstr(&match), ds_cstr(&actions),
> > +                                    stage_hint);
> > +        }
> > +    }
> > +
> > +    if (is_ip6) {
> > +        if (tcp_reset) {
> > +            ds_clear(&match);
> > +            ds_clear(&actions);
> > +            build_acl_log(&actions, acl);
> > +            if (extra_match->length > 0) {
> > +                ds_put_format(&match, "(%s) && ", extra_match->string);
> > +            }
> > +            ds_put_format(&match, "ip6 && tcp && (%s)", acl->match);
> > +            if (extra_actions->length > 0) {
> > +                ds_put_format(&actions, "%s ", extra_actions->string);
> > +            }
> > +            ds_put_format(&actions, "reg0 = 0; "
> > +                          "eth.dst <-> eth.src; ip6.dst <-> ip6.src; "
> > +                          "tcp_reset { outport <-> inport; %s };",
> > +                          ingress ? "next(pipeline=egress,table=5);"
> > +                                  : "next(pipeline=ingress,table=20);");
> > +            ovn_lflow_add_with_hint(lflows, od, stage,
> > +                                    acl->priority + OVN_ACL_PRI_OFFSET +
> 10,
> > +                                    ds_cstr(&match), ds_cstr(&actions),
> > +                                    stage_hint);
> > +        }
> > +
> > +        if (icmp_reset) {
> > +            ds_clear(&match);
> > +            ds_clear(&actions);
> > +            build_acl_log(&actions, acl);
> > +            if (extra_match->length > 0) {
> > +                ds_put_format(&match, "(%s) && ", extra_match->string);
> > +            }
> > +            ds_put_format(&match, "ip6 && (%s)", acl->match);
> > +            if (is_udp) {
> > +                ds_put_cstr(&match, " && udp");
> > +            }
> > +
> > +            if (extra_actions->length > 0) {
> > +                ds_put_format(&actions, "%s ", extra_actions->string);
> > +            }
> > +            ds_put_format(&actions, "reg0 = 0; icmp6 { "
> > +                          "eth.dst <-> eth.src; ip6.dst <-> ip6.src; "
> > +                          "outport <-> inport; %s };",
> > +                          ingress ? "next(pipeline=egress,table=5);"
> > +                                : "next(pipeline=ingress,table=20);");
> > +            ovn_lflow_add_with_hint(lflows, od, stage,
> > +                                    acl->priority + OVN_ACL_PRI_OFFSET,
> > +                                    ds_cstr(&match), ds_cstr(&actions),
> > +                                    stage_hint);
> > +        }
> > +    }
> >
> >      ds_destroy(&match);
> >      ds_destroy(&actions);
> > diff --git a/ovn-nb.ovsschema b/ovn-nb.ovsschema
> > index 092322ab2c..00f1c7cd4b 100644
> > --- a/ovn-nb.ovsschema
> > +++ b/ovn-nb.ovsschema
> > @@ -1,7 +1,7 @@
> >  {
> >      "name": "OVN_Northbound",
> > -    "version": "5.27.0",
> > -    "cksum": "3507518247 26773",
> > +    "version": "5.28.0",
> > +    "cksum": "699859908 26928",
> >      "tables": {
> >          "NB_Global": {
> >              "columns": {
> > @@ -225,6 +225,9 @@
> >                                                          "debug"]]},
> >                                        "min": 0, "max": 1}},
> >                  "meter": {"type": {"key": "string", "min": 0, "max": 1}},
> > +                "options": {
> > +                    "type": {"key": "string", "value": "string",
> > +                             "min": 0, "max": "unlimited"}},
> >                  "external_ids": {
> >                      "type": {"key": "string", "value": "string",
> >                               "min": 0, "max": "unlimited"}}},
> > diff --git a/ovn-nb.xml b/ovn-nb.xml
> > index 86195af341..2c3497e2ae 100644
> > --- a/ovn-nb.xml
> > +++ b/ovn-nb.xml
> > @@ -1721,6 +1721,49 @@
> >        </ul>
> >      </column>
> >
> > +    <group title="Common options">
> > +      <column name="options">
> > +        This column provides general key/value settings. The supported
> > +        options are described individually below.
> > +      </column>
> > +
> > +      <group title="Options for providing protocol hints for reject
> ACL.">
> > +        <p>
> > +          The ACL match specified in the
> > +          <ref column="match" table="ACL" db="OVN_Northbound"/> column is
> > +          opaque to <code>OVN</code> and <code>ovn-northd</code> doesn't
> > +          look into the match fields.
> > +          These options can be specified by CMS to provide hints to
> > +          <code>OVN</code> about the L3 and L4 protocol matches for the
> reject
> > +          ACL. <code>ovn-northd</code> uses these options if set to use
> > +          appropriate actions when generating logical flows.
> > +        </p>
> > +
> > +        <p>
> > +          If these options are not set, then <code>ovn-northd</code>
> assumes
> > +          the reject ACL applies to IPv4, IPv6 and TCP packets.
> > +        </p>
> > +
> > +        <column name="options" key="l3-protocol">
> > +          The possible values are <code>ip</code>, <code>ip4</code> and
> > +          <code>ip6</code>. If the value is <code>ip</code>, it means the
> > +          reject ACL action applies to both IPv4 and IPv6 packets. If the
> > +          value is <code>ip4</code>, it means the reject ACL action
> applies to
> > +          IPv4 packets and the value <code>ip6</code> applies to IPv6
> packets.
> > +        </column>
> > +
> > +        <column name="options" key="l4-protocol">
> > +          <p>
> > +            The possible values are <code>tcp</code> and
> <code>udp</code>.
> > +            If the value is <code>tcp</code>, it means the
> > +            reject ACL action applies to TCP packets. If the
> > +            value is <code>udp</code>, it means the reject ACL action
> applies
> > +            to UDP packets.
> > +          </p>
> > +        </column>
> > +      </group>
> > +    </group>
> > +
> >      <group title="Logging">
> >        <p>
> >          These columns control whether and how OVN logs packets that
> match an
> > diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> > index 99a9204f1f..4a30f8ed07 100644
> > --- a/tests/ovn-northd.at
> > +++ b/tests/ovn-northd.at
> > @@ -2010,3 +2010,288 @@ ovn-nbctl --wait=sb set NB_Global .
> options:ignore_lsp_down=true
> >  AT_CHECK([ovn-sbctl lflow-list | grep arp | grep 10\.0\.0\.1], [0],
> [ignore])
> >
> >  AT_CLEANUP
> > +
> > +AT_SETUP([ovn-northd -- ACL reject flows])
> > +ovn_start
> > +
> > +ovn-nbctl ls-add sw0
> > +ovn-nbctl lsp-add sw0 sw0-p1
> > +ovn-nbctl lsp-add sw0 sw0-p2
> > +
> > +ovn-nbctl pg-add pg0 sw0-p1 sw0-p2
> > +ovn-nbctl acl-add pg0 to-lport 1002 "inport == @pg0 && ip" reject
> > +
> > +ovn-nbctl --wait=sb sync
> > +
> > +# If there is a reject ACL wihtout any protocol hints, then ovn-northd
> should
> > +# generate lflows with ip4 tcp_reset, icmp4, ip6 tcp_reset and icmp6
> actions.
> > +
> > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> sort], [0], [dnl
> > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > +match=(ip4 && tcp && (inport == @pg0 && ip)), dnl
> > +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src; tcp_reset {
> outport <-> inport; next(pipeline=ingress,table=20); };)
> > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > +match=(ip6 && tcp && (inport == @pg0 && ip)), dnl
> > +action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src; tcp_reset {
> outport <-> inport; next(pipeline=ingress,table=20); };)
> > +])
> > +
> > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> sort], [0], [dnl
> > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > +match=(ip4 && (inport == @pg0 && ip)), dnl
> > +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> outport <-> inport; next(pipeline=ingress,table=20); };)
> > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > +match=(ip6 && (inport == @pg0 && ip)), dnl
> > +action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <-> ip6.src;
> outport <-> inport; next(pipeline=ingress,table=20); };)
> > +])
> > +
> > +# Set l3-protocol=ip4 for the reject ACL.
> > +ovn-nbctl --wait=sb set ACL . options:l3-protocol=ip4
> > +
> > +# ovn-northd should generate  2 lflows with ip4 tcp_reset and icmp4
> actions.
> > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> sort], [0], [dnl
> > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > +match=(ip4 && tcp && (inport == @pg0 && ip)), dnl
> > +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src; tcp_reset {
> outport <-> inport; next(pipeline=ingress,table=20); };)
> > +])
> > +
> > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> sort], [0], [dnl
> > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > +match=(ip4 && (inport == @pg0 && ip)), dnl
> > +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> outport <-> inport; next(pipeline=ingress,table=20); };)
> > +])
> > +
> > +# Set l4-protocol=tcp for the reject ACL.
> > +ovn-nbctl --wait=sb set ACL . options:l4-protocol=tcp
> > +
> > +# ovn-northd should generate 1 lflow with ip4 tcp_reset action.
> > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> sort], [0], [dnl
> > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > +match=(ip4 && tcp && (inport == @pg0 && ip)), dnl
> > +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src; tcp_reset {
> outport <-> inport; next(pipeline=ingress,table=20); };)
> > +])
> > +
> > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> sort], [0], [dnl
> > +])
> > +
> > +# Set l4-protocol=udp for the reject ACL.
> > +ovn-nbctl --wait=sb set ACL . options:l4-protocol=udp
> > +
> > +# ovn-northd should generate 1 lflow with udp match and icmp4 action.
> > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> sort], [0], [dnl
> > +])
> > +
> > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> sort], [0], [dnl
> > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > +match=(ip4 && (inport == @pg0 && ip) && udp), dnl
> > +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> outport <-> inport; next(pipeline=ingress,table=20); };)
> > +])
> > +
> > +# Remove l4-protocol from the reject ACL.
> > +ovn-nbctl --wait=sb remove ACL . options l4-protocol
> > +
> > +# ovn-northd should generate 2 lflow with tcp_reset and icmp4 action.
> > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> sort], [0], [dnl
> > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > +match=(ip4 && tcp && (inport == @pg0 && ip)), dnl
> > +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src; tcp_reset {
> outport <-> inport; next(pipeline=ingress,table=20); };)
> > +])
> > +
> > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> sort], [0], [dnl
> > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > +match=(ip4 && (inport == @pg0 && ip)), dnl
> > +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> outport <-> inport; next(pipeline=ingress,table=20); };)
> > +])
> > +
> > +# Set l3-protocol to ip.
> > +ovn-nbctl --wait=sb set ACL . options:l3-protocol=ip
> > +
> > +# ovn-northd should generate 4 lflow with ip4 tcp_reset, ip6 tcp_reset,
> icmp4 and icmp6 actions.
> > +
> > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> sort], [0], [dnl
> > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > +match=(ip4 && tcp && (inport == @pg0 && ip)), dnl
> > +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src; tcp_reset {
> outport <-> inport; next(pipeline=ingress,table=20); };)
> > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > +match=(ip6 && tcp && (inport == @pg0 && ip)), dnl
> > +action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src; tcp_reset {
> outport <-> inport; next(pipeline=ingress,table=20); };)
> > +])
> > +
> > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> sort], [0], [dnl
> > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > +match=(ip4 && (inport == @pg0 && ip)), dnl
> > +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> outport <-> inport; next(pipeline=ingress,table=20); };)
> > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > +match=(ip6 && (inport == @pg0 && ip)), dnl
> > +action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <-> ip6.src;
> outport <-> inport; next(pipeline=ingress,table=20); };)
> > +])
> > +
> > +# Set l3-protocol=ip6 for the reject ACL.
> > +ovn-nbctl --wait=sb set ACL . options:l3-protocol=ip6
> > +
> > +# ovn-northd should generate  2 lflows with ip6 tcp_reset and icmp6
> actions.
> > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> sort], [0], [dnl
> > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > +match=(ip6 && tcp && (inport == @pg0 && ip)), dnl
> > +action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src; tcp_reset {
> outport <-> inport; next(pipeline=ingress,table=20); };)
> > +])
> > +
> > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> sort], [0], [dnl
> > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > +match=(ip6 && (inport == @pg0 && ip)), dnl
> > +action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <-> ip6.src;
> outport <-> inport; next(pipeline=ingress,table=20); };)
> > +])
> > +
> > +# Set l4-protocol=tcp for the reject ACL.
> > +ovn-nbctl --wait=sb set ACL . options:l4-protocol=tcp
> > +
> > +# ovn-northd should generate 1 lflow with ip6 tcp_reset action.
> > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> sort], [0], [dnl
> > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > +match=(ip6 && tcp && (inport == @pg0 && ip)), dnl
> > +action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src; tcp_reset {
> outport <-> inport; next(pipeline=ingress,table=20); };)
> > +])
> > +
> > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> sort], [0], [dnl
> > +])
> > +
> > +# Set l4-protocol=udp for the reject ACL.
> > +ovn-nbctl --wait=sb set ACL . options:l4-protocol=udp
> > +
> > +# ovn-northd should generate 1 lflow with udp match and icmp6 action.
> > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> sort], [0], [dnl
> > +])
> > +
> > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> sort], [0], [dnl
> > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > +match=(ip6 && (inport == @pg0 && ip) && udp), dnl
> > +action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <-> ip6.src;
> outport <-> inport; next(pipeline=ingress,table=20); };)
> > +])
> > +
> > +# Set l3-protocol to ip and l4-protocol=tcp for the reject ACL.
> > +ovn-nbctl --wait=sb set ACL . options:l3-protocol=ip
> > +ovn-nbctl --wait=sb set ACL . options:l4-protocol=tcp
> > +
> > +# ovn-northd should generate 2 lflow with ip4 tcp_reset and ip6
> tcp_reset actions.
> > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> sort], [0], [dnl
> > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > +match=(ip4 && tcp && (inport == @pg0 && ip)), dnl
> > +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src; tcp_reset {
> outport <-> inport; next(pipeline=ingress,table=20); };)
> > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > +match=(ip6 && tcp && (inport == @pg0 && ip)), dnl
> > +action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src; tcp_reset {
> outport <-> inport; next(pipeline=ingress,table=20); };)
> > +])
> > +
> > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> sort], [0], [dnl
> > +])
> > +
> > +# Set l3-protocol to ip and l4-protocol=udp for the reject ACL.
> > +ovn-nbctl --wait=sb set ACL . options:l3-protocol=ip
> > +ovn-nbctl --wait=sb set ACL . options:l4-protocol=udp
> > +
> > +# ovn-northd should generate 2 lflows (udp match) with icmp4 and icmp6
> actions.
> > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> sort], [0], [dnl
> > +])
> > +
> > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> sort], [0], [dnl
> > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > +match=(ip4 && (inport == @pg0 && ip) && udp), dnl
> > +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> outport <-> inport; next(pipeline=ingress,table=20); };)
> > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > +match=(ip6 && (inport == @pg0 && ip) && udp), dnl
> > +action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <-> ip6.src;
> outport <-> inport; next(pipeline=ingress,table=20); };)
> > +])
> > +
> > +# Add an ACL with allow-related
> > +ovn-nbctl --wait=sb acl-add pg0 to-lport 1000 "ip" allow-related
> > +
> > +# ovn-northd should generate 4 lflow with 2 icmp4 and 2 icmp6 actions.
> > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> sort], [0], [dnl
> > +])
> > +
> > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> sort], [0], [dnl
> > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > +match=((reg0[[10]] == 1) && ip4 && (inport == @pg0 && ip) && udp), dnl\
> > +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; icmp4 { eth.dst
> <-> eth.src; ip4.dst <-> ip4.src; outport <-> inport;
> next(pipeline=ingress,table=20); };)
> > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > +match=((reg0[[10]] == 1) && ip6 && (inport == @pg0 && ip) && udp), dnl
> > +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; icmp6 { eth.dst
> <-> eth.src; ip6.dst <-> ip6.src; outport <-> inport;
> next(pipeline=ingress,table=20); };)
> > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > +match=((reg0[[9]] == 1) && ip4 && (inport == @pg0 && ip) && udp), dnl
> > +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> outport <-> inport; next(pipeline=ingress,table=20); };)
> > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > +match=((reg0[[9]] == 1) && ip6 && (inport == @pg0 && ip) && udp), dnl
> > +action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <-> ip6.src;
> outport <-> inport; next(pipeline=ingress,table=20); };)
> > +])
> > +
> > +# Set l3-protocol to ip and l4-protocol=tcp for the reject ACL.
> > +rej_acl=$(ovn-nbctl --bare --columns _uuid  find ACL action=reject)
> > +ovn-nbctl --wait=sb set ACL $rej_acl options:l3-protocol=ip
> > +ovn-nbctl --wait=sb set ACL $rej_acl options:l4-protocol=tcp
> > +
> > +# ovn-northd should generate 4 lflow with 2 ip4 tcp_reset and 2 ip6
> tcp_reset actions.
> > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> sort], [0], [dnl
> > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > +match=((reg0[[10]] == 1) && ip4 && tcp && (inport == @pg0 && ip)), dnl
> > +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; eth.dst <->
> eth.src; ip4.dst <-> ip4.src; tcp_reset { outport <-> inport;
> next(pipeline=ingress,table=20); };)
> > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > +match=((reg0[[10]] == 1) && ip6 && tcp && (inport == @pg0 && ip)), dnl
> > +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; eth.dst <->
> eth.src; ip6.dst <-> ip6.src; tcp_reset { outport <-> inport;
> next(pipeline=ingress,table=20); };)
> > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > +match=((reg0[[9]] == 1) && ip4 && tcp && (inport == @pg0 && ip)), dnl
> > +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src; tcp_reset {
> outport <-> inport; next(pipeline=ingress,table=20); };)
> > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > +match=((reg0[[9]] == 1) && ip6 && tcp && (inport == @pg0 && ip)), dnl
> > +action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src; tcp_reset {
> outport <-> inport; next(pipeline=ingress,table=20); };)
> > +])
> > +
> > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> sort], [0], [dnl
> > +])
> > +
> > +
> > +# Clear l3-protocol and set l4-protocol to udp
> > +ovn-nbctl --wait=sb remove ACL $rej_acl options l3-protocol
> > +ovn-nbctl --wait=sb set ACL $rej_acl options:l4-protocol=udp
> > +
> > +# ovn-northd should generate 4 lflows (with udp match) with 2 icmp4 and
> 2 icmp6 actions.
> > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> sort], [0], [dnl
> > +])
> > +
> > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> sort], [0], [dnl
> > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > +match=((reg0[[10]] == 1) && ip4 && (inport == @pg0 && ip) && udp), dnl\
> > +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; icmp4 { eth.dst
> <-> eth.src; ip4.dst <-> ip4.src; outport <-> inport;
> next(pipeline=ingress,table=20); };)
> > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > +match=((reg0[[10]] == 1) && ip6 && (inport == @pg0 && ip) && udp), dnl
> > +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; icmp6 { eth.dst
> <-> eth.src; ip6.dst <-> ip6.src; outport <-> inport;
> next(pipeline=ingress,table=20); };)
> > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > +match=((reg0[[9]] == 1) && ip4 && (inport == @pg0 && ip) && udp), dnl
> > +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> outport <-> inport; next(pipeline=ingress,table=20); };)
> > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > +match=((reg0[[9]] == 1) && ip6 && (inport == @pg0 && ip) && udp), dnl
> > +action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <-> ip6.src;
> outport <-> inport; next(pipeline=ingress,table=20); };)
> > +])
> > +
> > +# Clear l3-protocol and set l4-protocol to tcp
> > +ovn-nbctl --wait=sb remove ACL $rej_acl options l3-protocol
> > +ovn-nbctl --wait=sb set ACL $rej_acl options:l4-protocol=tcp
> > +
> > +# ovn-northd should generate 4 lflow with 2 ip4 tcp_reset and 2 ip6
> tcp_reset actions.
> > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> sort], [0], [dnl
> > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > +match=((reg0[[10]] == 1) && ip4 && tcp && (inport == @pg0 && ip)), dnl
> > +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; eth.dst <->
> eth.src; ip4.dst <-> ip4.src; tcp_reset { outport <-> inport;
> next(pipeline=ingress,table=20); };)
> > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > +match=((reg0[[10]] == 1) && ip6 && tcp && (inport == @pg0 && ip)), dnl
> > +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; eth.dst <->
> eth.src; ip6.dst <-> ip6.src; tcp_reset { outport <-> inport;
> next(pipeline=ingress,table=20); };)
> > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > +match=((reg0[[9]] == 1) && ip4 && tcp && (inport == @pg0 && ip)), dnl
> > +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src; tcp_reset {
> outport <-> inport; next(pipeline=ingress,table=20); };)
> > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > +match=((reg0[[9]] == 1) && ip6 && tcp && (inport == @pg0 && ip)), dnl
> > +action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src; tcp_reset {
> outport <-> inport; next(pipeline=ingress,table=20); };)
> > +])
> > +
> > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> sort], [0], [dnl
> > +])
> > +
> > +AT_CLEANUP
> > --
> > 2.26.2
> >
> > _______________________________________________
> > dev mailing list
> > dev@openvswitch.org
> > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
Numan Siddique Sept. 28, 2020, 9:21 a.m. UTC | #3
On Mon, Sep 28, 2020 at 2:14 PM Numan Siddique <numans@ovn.org> wrote:
>
> On Mon, Sep 28, 2020 at 5:57 AM Han Zhou <hzhou@ovn.org> wrote:
> >
> > Thanks Numan. I have some comments on the solution.
> >
> > On Thu, Sep 24, 2020 at 9:50 AM <numans@ovn.org> wrote:
> > >
> > > From: Numan Siddique <numans@ovn.org>
> > >
> > > ovn-northd adds below lflows for a reject ACL with a match - M
> > >
> > > match = (ip4 && tcp && 'M') action = tcp_reject{}
> > > match = (ip6 && tcp && 'M') action = tcp_reject{}
> > > match = (ip4 && 'M') action = icmp4{}
> > > match = (ip6 && 'M') action = icmp6{}
> > >
> > > This approach has a couple of problems:
> > >    - ovn-controller can reject the lflows if there are invalid matches.
> > >      Eg. If match 'M' is - 'ip4 && udp'.
> > >
> > >    - In a large scale deployment, this could result in lot of invalid
> > >      logical flows and increase the size of the SB DB.
> > >
> > > This patch addresses this problem by providing an option to the CMS
> > > to specify the l3 protocol and l4 protocol as hints. A new column
> > > 'options' is added to the ACL table with the options -
> > > options:l3-protocol and options:l4-protocol.
> > >
> > > ovn-northd will now generate only the required lflows for the reject
> > > ACL. If no options are set, then it falls back to the default scenario.
> > >
> >
> > Thanks Numan. I believe the solution should work. However, it seems a
> > little confusing for the end user about how to use the hint field. I think
> > maybe we should try to avoid the extra field if possible. Here is my
> > proposal:
> >
>
> Thanks Han for the comments. I do agree if we can avoid the extra option.
>
> > 1. For the tcp v.s. udp check, since we don't want northd to parse the
> > match condition, we will have to rely on users input. However, instead of a
> > hint, I think it is better to let the user explicitly specify in the action
> > field. E.g. "reject" means sending icmp, and "tcp_reject" means sending tcp
> > reset if the traffic is tcp and otherwise sending icmp. So, if the user
> > adds an ACL with match include "udp", they will naturally use "reject"
> > instead of "tcp_reject" because they know it is not possible for a TCP
> > packet to hit this ACL. If they add an ACL that tcp traffic is expected to
> > match (either TCP only, or general IP traffic), they can use "tcp_reject".
> > Would this be more straightforward than an extra "hint" field?
> >
>
> I have few comments here.
> - If we consider "reject" = "icmp", then we will be breaking the
> backward compatibility.
>     Right now, for a reject action, ovn-northd would send tcp_reset
> for tcp traffic and icmp reply or other traffic.
>     After your proposed approach, the  behaviour would change ? Is
> that OK ? Because existing test cases would fail.
>     The proposed patch handles the backward compatibility. Although
> I'm not sure if it is really required in this case.
>
> -  For  a tcp_reject action, if the match has "tcp", then we will end
> up adding a logical flow with the icmp action, which is
>     really not required. Or do you mean, pinctrl would take the
> decision while handling the "tcp_reject" action to either
>     send tcp reset packet or icmp packet ? If so, we can probably just
> have one action - "reject". Please see below.
>
> > 2. For ip4 v.s. ip6, the idea is to support generic ovn logical flow
> > actions "tcp_reject" and "icmp" in ovn-controller, which can handle both
> > ipv4 and ipv6. If this can be supported, the logical flows only needs to be:
> > match = (tcp && 'M') action = tcp_reject{}
> > match = ('M') action = icmp{}
> >
>
> Ok. In this case we cannot support inner actions unless they are
> independent of the ip version.
> i.e we can't support - tcp_reject { eth.dst <-> eth.src; ip4.dst <->
> ip4.src; } or
> tcp_reject {eth.dst <-> eth.src; ip6.dst <-> ip6.src;} in one logical
> flow. ovn-northd has to generate
> 2 logical flows.
>   - ip4 && 'M', action = tcp_reject { eth.dst <-> eth.src; ip4.dst <->
> ip4.src; }
>   - ip6 && 'M', action = tcp_reject { eth.dst <-> eth.src; ip6.dst <->
> ip6.src; }
>
>
> Right now for one reject ACL we add the below lflows
>    - ip4 && tcp && 'M', action = tcp_reset { eth.dst <-> eth.src;
> ip4.dst <-> ip4.src; outport <-> inport;}
>    - ip6 && tcp && 'M', action = tcp_reset { eth.dst <-> eth.src;
> ip6.dst <-> ip6.src; outport <-> inport;}
>    - ip4 && 'M'            , action = icmp4 {eth.dst <-> eth.src;
> ip4.dst <-> ip4.src; outport <-> inport;}
>    - ip6 && 'M'            , action = icmp6 {eth.dst <-> eth.src;
> ip6.dst <-> ip6.src; outport <-> inport;}
>
> I have another idea based on your suggestions here: How about we have
> a new action - reject{} which will be used
> for all reject ACLs and this will
>     - send tcp reset packet if the packet is tcp (pinctrl would take
> the decision)
>     - send icmp packet otherwise.
>
> We can add the logical flow for a reject ACL in 2 ways
>
> 1. match = M, action = reject { eth.dst <-> eth.src, outport <-> inport;}
>
> OR
>
> 2. match = M, action = reject { eth.dst <-> eth.src, ip.src <->
> ip.dst; outport <-> inport;}
>
> In (1), pinctrl while handling the reject action, will swap the ip src
> with ip dst (i.e ip4.dst <-> ip4.src or ip6.dst <-> ip6.src }.
>
> In (2), we will add 2 new OVN fields "ip.src" and "ip.dst" (just like
> we have icmp4.frag_mtu now) and the action - ip.src <-> ip.dst
> will result in another controller action which will swap the ip src
> and ip dst fields (just like how "icmp4.frag_mtu = 1400" would result
> into a controller action).
>
> Even though (1) will work, it's not transparent. I would personally
> prefer (2) and I think it's ok if the reject ACL would result in 2 OF
> controller actions.
>
> With this we will have just one logical flow (instead of 4 ) and there
> will be no changes on the CMS side.
>
> What do you think ?
>
> Thanks
> Numan
>
>
> > I haven't thought through all the details but it seems doable. Did you have
> > any thoughts on this? (If it proves to be unreasonable I am ok with current
> > "hint" approach)
> >
> > In addition, I found the existing TCP reject handling has a bug which
> > messes up the user defined ACL priorities. I just posted a patch:
> > https://patchwork.ozlabs.org/project/ovn/patch/20200928000904.858685-1-hzhou@ovn.org/
> > It is not directly related to this patch but it would be great if you could
> > take a look.

Sure.

Thanks
Numan

> >
> > Thanks,
> > Han
> >
> >
> > > Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=1876990
> > > Reported-by: Ilya Maximets <i.maximets@ovn.org>
> > > Signed-off-by: Numan Siddique <numans@ovn.org>
> > > ---
> > >  northd/ovn-northd.c | 194 ++++++++++++++++++++----------
> > >  ovn-nb.ovsschema    |   7 +-
> > >  ovn-nb.xml          |  43 +++++++
> > >  tests/ovn-northd.at | 285 ++++++++++++++++++++++++++++++++++++++++++++
> > >  4 files changed, 461 insertions(+), 68 deletions(-)
> > >
> > > diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
> > > index 3324c9e81d..d167137e07 100644
> > > --- a/northd/ovn-northd.c
> > > +++ b/northd/ovn-northd.c
> > > @@ -5372,73 +5372,135 @@ build_reject_acl_rules(struct ovn_datapath *od,
> > struct hmap *lflows,
> > >      struct ds actions = DS_EMPTY_INITIALIZER;
> > >      bool ingress = (stage == S_SWITCH_IN_ACL);
> > >
> > > -    /* TCP */
> > > -    build_acl_log(&actions, acl);
> > > -    if (extra_match->length > 0) {
> > > -        ds_put_format(&match, "(%s) && ", extra_match->string);
> > > -    }
> > > -    ds_put_format(&match, "ip4 && tcp && (%s)", acl->match);
> > > -    ds_put_format(&actions, "reg0 = 0; "
> > > -                  "eth.dst <-> eth.src; ip4.dst <-> ip4.src; "
> > > -                  "tcp_reset { outport <-> inport; %s };",
> > > -                  ingress ? "next(pipeline=egress,table=5);"
> > > -                          : "next(pipeline=ingress,table=20);");
> > > -    ovn_lflow_add_with_hint(lflows, od, stage,
> > > -                            acl->priority + OVN_ACL_PRI_OFFSET + 10,
> > > -                            ds_cstr(&match), ds_cstr(&actions),
> > stage_hint);
> > > -    ds_clear(&match);
> > > -    ds_clear(&actions);
> > > -    build_acl_log(&actions, acl);
> > > -    if (extra_match->length > 0) {
> > > -        ds_put_format(&match, "(%s) && ", extra_match->string);
> > > -    }
> > > -    ds_put_format(&match, "ip6 && tcp && (%s)", acl->match);
> > > -    ds_put_format(&actions, "reg0 = 0; "
> > > -                  "eth.dst <-> eth.src; ip6.dst <-> ip6.src; "
> > > -                  "tcp_reset { outport <-> inport; %s };",
> > > -                  ingress ? "next(pipeline=egress,table=5);"
> > > -                          : "next(pipeline=ingress,table=20);");
> > > -    ovn_lflow_add_with_hint(lflows, od, stage,
> > > -                            acl->priority + OVN_ACL_PRI_OFFSET + 10,
> > > -                            ds_cstr(&match), ds_cstr(&actions),
> > stage_hint);
> > > +    bool is_ip4 = true;
> > > +    bool is_ip6 = true;
> > > +    bool tcp_reset = true;
> > > +    bool icmp_reset = true;
> > > +    bool is_udp = false;
> > > +
> > > +    const char *l3_protocol = smap_get(&acl->options, "l3-protocol");
> > > +    if (l3_protocol) {
> > > +        if (!strcasecmp(l3_protocol, "ip")) {
> > > +            is_ip4 = true;
> > > +            is_ip6 = true;
> > > +        } else if (!strcasecmp(l3_protocol, "ip4")) {
> > > +            is_ip4 = true;
> > > +            is_ip6 = false;
> > > +        } else if (!strcasecmp(l3_protocol, "ip6")) {
> > > +            is_ip6 = true;
> > > +            is_ip4 = false;
> > > +        }
> > > +    }
> > > +
> > > +    const char *l4_protocol = smap_get(&acl->options, "l4-protocol");
> > > +    if (l4_protocol) {
> > > +        if (!strcasecmp(l4_protocol, "tcp")) {
> > > +            tcp_reset = true;
> > > +            icmp_reset = false;
> > > +        } else if (!strcasecmp(l4_protocol, "udp")) {
> > > +            tcp_reset = false;
> > > +            is_udp = true;
> > > +        } else {
> > > +            tcp_reset = false;
> > > +        }
> > > +    }
> > >
> > > -    /* IP traffic */
> > > -    ds_clear(&match);
> > > -    ds_clear(&actions);
> > > -    build_acl_log(&actions, acl);
> > > -    if (extra_match->length > 0) {
> > > -        ds_put_format(&match, "(%s) && ", extra_match->string);
> > > -    }
> > > -    ds_put_format(&match, "ip4 && (%s)", acl->match);
> > > -    if (extra_actions->length > 0) {
> > > -        ds_put_format(&actions, "%s ", extra_actions->string);
> > > -    }
> > > -    ds_put_format(&actions, "reg0 = 0; "
> > > -                  "icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src; "
> > > -                  "outport <-> inport; %s };",
> > > -                  ingress ? "next(pipeline=egress,table=5);"
> > > -                          : "next(pipeline=ingress,table=20);");
> > > -    ovn_lflow_add_with_hint(lflows, od, stage,
> > > -                            acl->priority + OVN_ACL_PRI_OFFSET,
> > > -                            ds_cstr(&match), ds_cstr(&actions),
> > stage_hint);
> > > -    ds_clear(&match);
> > > -    ds_clear(&actions);
> > > -    build_acl_log(&actions, acl);
> > > -    if (extra_match->length > 0) {
> > > -        ds_put_format(&match, "(%s) && ", extra_match->string);
> > > -    }
> > > -    ds_put_format(&match, "ip6 && (%s)", acl->match);
> > > -    if (extra_actions->length > 0) {
> > > -        ds_put_format(&actions, "%s ", extra_actions->string);
> > > -    }
> > > -    ds_put_format(&actions, "reg0 = 0; icmp6 { "
> > > -                  "eth.dst <-> eth.src; ip6.dst <-> ip6.src; "
> > > -                  "outport <-> inport; %s };",
> > > -                  ingress ? "next(pipeline=egress,table=5);"
> > > -                          : "next(pipeline=ingress,table=20);");
> > > -    ovn_lflow_add_with_hint(lflows, od, stage,
> > > -                            acl->priority + OVN_ACL_PRI_OFFSET,
> > > -                            ds_cstr(&match), ds_cstr(&actions),
> > stage_hint);
> > > +    if (is_ip4) {
> > > +        if (tcp_reset) {
> > > +            build_acl_log(&actions, acl);
> > > +            if (extra_match->length > 0) {
> > > +                ds_put_format(&match, "(%s) && ", extra_match->string);
> > > +            }
> > > +            ds_put_format(&match, "ip4 && tcp && (%s)", acl->match);
> > > +            if (extra_actions->length > 0) {
> > > +                ds_put_format(&actions, "%s ", extra_actions->string);
> > > +            }
> > > +            ds_put_format(&actions, "reg0 = 0; "
> > > +                          "eth.dst <-> eth.src; ip4.dst <-> ip4.src; "
> > > +                          "tcp_reset { outport <-> inport; %s };",
> > > +                          ingress ? "next(pipeline=egress,table=5);"
> > > +                                  : "next(pipeline=ingress,table=20);");
> > > +            ovn_lflow_add_with_hint(lflows, od, stage,
> > > +                                    acl->priority + OVN_ACL_PRI_OFFSET +
> > 10,
> > > +                                    ds_cstr(&match), ds_cstr(&actions),
> > > +                                    stage_hint);
> > > +        }
> > > +
> > > +        if (icmp_reset) {
> > > +            ds_clear(&match);
> > > +            ds_clear(&actions);
> > > +            build_acl_log(&actions, acl);
> > > +            if (extra_match->length > 0) {
> > > +                ds_put_format(&match, "(%s) && ", extra_match->string);
> > > +            }
> > > +            ds_put_format(&match, "ip4 && (%s)", acl->match);
> > > +            if (is_udp) {
> > > +                ds_put_cstr(&match, " && udp");
> > > +            }
> > > +            if (extra_actions->length > 0) {
> > > +                ds_put_format(&actions, "%s ", extra_actions->string);
> > > +            }
> > > +            ds_put_format(&actions, "reg0 = 0; "
> > > +                          "icmp4 { eth.dst <-> eth.src; ip4.dst <->
> > ip4.src; "
> > > +                          "outport <-> inport; %s };",
> > > +                          ingress ? "next(pipeline=egress,table=5);"
> > > +                                  : "next(pipeline=ingress,table=20);");
> > > +            ovn_lflow_add_with_hint(lflows, od, stage,
> > > +                                    acl->priority + OVN_ACL_PRI_OFFSET,
> > > +                                    ds_cstr(&match), ds_cstr(&actions),
> > > +                                    stage_hint);
> > > +        }
> > > +    }
> > > +
> > > +    if (is_ip6) {
> > > +        if (tcp_reset) {
> > > +            ds_clear(&match);
> > > +            ds_clear(&actions);
> > > +            build_acl_log(&actions, acl);
> > > +            if (extra_match->length > 0) {
> > > +                ds_put_format(&match, "(%s) && ", extra_match->string);
> > > +            }
> > > +            ds_put_format(&match, "ip6 && tcp && (%s)", acl->match);
> > > +            if (extra_actions->length > 0) {
> > > +                ds_put_format(&actions, "%s ", extra_actions->string);
> > > +            }
> > > +            ds_put_format(&actions, "reg0 = 0; "
> > > +                          "eth.dst <-> eth.src; ip6.dst <-> ip6.src; "
> > > +                          "tcp_reset { outport <-> inport; %s };",
> > > +                          ingress ? "next(pipeline=egress,table=5);"
> > > +                                  : "next(pipeline=ingress,table=20);");
> > > +            ovn_lflow_add_with_hint(lflows, od, stage,
> > > +                                    acl->priority + OVN_ACL_PRI_OFFSET +
> > 10,
> > > +                                    ds_cstr(&match), ds_cstr(&actions),
> > > +                                    stage_hint);
> > > +        }
> > > +
> > > +        if (icmp_reset) {
> > > +            ds_clear(&match);
> > > +            ds_clear(&actions);
> > > +            build_acl_log(&actions, acl);
> > > +            if (extra_match->length > 0) {
> > > +                ds_put_format(&match, "(%s) && ", extra_match->string);
> > > +            }
> > > +            ds_put_format(&match, "ip6 && (%s)", acl->match);
> > > +            if (is_udp) {
> > > +                ds_put_cstr(&match, " && udp");
> > > +            }
> > > +
> > > +            if (extra_actions->length > 0) {
> > > +                ds_put_format(&actions, "%s ", extra_actions->string);
> > > +            }
> > > +            ds_put_format(&actions, "reg0 = 0; icmp6 { "
> > > +                          "eth.dst <-> eth.src; ip6.dst <-> ip6.src; "
> > > +                          "outport <-> inport; %s };",
> > > +                          ingress ? "next(pipeline=egress,table=5);"
> > > +                                : "next(pipeline=ingress,table=20);");
> > > +            ovn_lflow_add_with_hint(lflows, od, stage,
> > > +                                    acl->priority + OVN_ACL_PRI_OFFSET,
> > > +                                    ds_cstr(&match), ds_cstr(&actions),
> > > +                                    stage_hint);
> > > +        }
> > > +    }
> > >
> > >      ds_destroy(&match);
> > >      ds_destroy(&actions);
> > > diff --git a/ovn-nb.ovsschema b/ovn-nb.ovsschema
> > > index 092322ab2c..00f1c7cd4b 100644
> > > --- a/ovn-nb.ovsschema
> > > +++ b/ovn-nb.ovsschema
> > > @@ -1,7 +1,7 @@
> > >  {
> > >      "name": "OVN_Northbound",
> > > -    "version": "5.27.0",
> > > -    "cksum": "3507518247 26773",
> > > +    "version": "5.28.0",
> > > +    "cksum": "699859908 26928",
> > >      "tables": {
> > >          "NB_Global": {
> > >              "columns": {
> > > @@ -225,6 +225,9 @@
> > >                                                          "debug"]]},
> > >                                        "min": 0, "max": 1}},
> > >                  "meter": {"type": {"key": "string", "min": 0, "max": 1}},
> > > +                "options": {
> > > +                    "type": {"key": "string", "value": "string",
> > > +                             "min": 0, "max": "unlimited"}},
> > >                  "external_ids": {
> > >                      "type": {"key": "string", "value": "string",
> > >                               "min": 0, "max": "unlimited"}}},
> > > diff --git a/ovn-nb.xml b/ovn-nb.xml
> > > index 86195af341..2c3497e2ae 100644
> > > --- a/ovn-nb.xml
> > > +++ b/ovn-nb.xml
> > > @@ -1721,6 +1721,49 @@
> > >        </ul>
> > >      </column>
> > >
> > > +    <group title="Common options">
> > > +      <column name="options">
> > > +        This column provides general key/value settings. The supported
> > > +        options are described individually below.
> > > +      </column>
> > > +
> > > +      <group title="Options for providing protocol hints for reject
> > ACL.">
> > > +        <p>
> > > +          The ACL match specified in the
> > > +          <ref column="match" table="ACL" db="OVN_Northbound"/> column is
> > > +          opaque to <code>OVN</code> and <code>ovn-northd</code> doesn't
> > > +          look into the match fields.
> > > +          These options can be specified by CMS to provide hints to
> > > +          <code>OVN</code> about the L3 and L4 protocol matches for the
> > reject
> > > +          ACL. <code>ovn-northd</code> uses these options if set to use
> > > +          appropriate actions when generating logical flows.
> > > +        </p>
> > > +
> > > +        <p>
> > > +          If these options are not set, then <code>ovn-northd</code>
> > assumes
> > > +          the reject ACL applies to IPv4, IPv6 and TCP packets.
> > > +        </p>
> > > +
> > > +        <column name="options" key="l3-protocol">
> > > +          The possible values are <code>ip</code>, <code>ip4</code> and
> > > +          <code>ip6</code>. If the value is <code>ip</code>, it means the
> > > +          reject ACL action applies to both IPv4 and IPv6 packets. If the
> > > +          value is <code>ip4</code>, it means the reject ACL action
> > applies to
> > > +          IPv4 packets and the value <code>ip6</code> applies to IPv6
> > packets.
> > > +        </column>
> > > +
> > > +        <column name="options" key="l4-protocol">
> > > +          <p>
> > > +            The possible values are <code>tcp</code> and
> > <code>udp</code>.
> > > +            If the value is <code>tcp</code>, it means the
> > > +            reject ACL action applies to TCP packets. If the
> > > +            value is <code>udp</code>, it means the reject ACL action
> > applies
> > > +            to UDP packets.
> > > +          </p>
> > > +        </column>
> > > +      </group>
> > > +    </group>
> > > +
> > >      <group title="Logging">
> > >        <p>
> > >          These columns control whether and how OVN logs packets that
> > match an
> > > diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> > > index 99a9204f1f..4a30f8ed07 100644
> > > --- a/tests/ovn-northd.at
> > > +++ b/tests/ovn-northd.at
> > > @@ -2010,3 +2010,288 @@ ovn-nbctl --wait=sb set NB_Global .
> > options:ignore_lsp_down=true
> > >  AT_CHECK([ovn-sbctl lflow-list | grep arp | grep 10\.0\.0\.1], [0],
> > [ignore])
> > >
> > >  AT_CLEANUP
> > > +
> > > +AT_SETUP([ovn-northd -- ACL reject flows])
> > > +ovn_start
> > > +
> > > +ovn-nbctl ls-add sw0
> > > +ovn-nbctl lsp-add sw0 sw0-p1
> > > +ovn-nbctl lsp-add sw0 sw0-p2
> > > +
> > > +ovn-nbctl pg-add pg0 sw0-p1 sw0-p2
> > > +ovn-nbctl acl-add pg0 to-lport 1002 "inport == @pg0 && ip" reject
> > > +
> > > +ovn-nbctl --wait=sb sync
> > > +
> > > +# If there is a reject ACL wihtout any protocol hints, then ovn-northd
> > should
> > > +# generate lflows with ip4 tcp_reset, icmp4, ip6 tcp_reset and icmp6
> > actions.
> > > +
> > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> > sort], [0], [dnl
> > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > +match=(ip4 && tcp && (inport == @pg0 && ip)), dnl
> > > +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src; tcp_reset {
> > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > +match=(ip6 && tcp && (inport == @pg0 && ip)), dnl
> > > +action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src; tcp_reset {
> > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > +])
> > > +
> > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> > sort], [0], [dnl
> > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > +match=(ip4 && (inport == @pg0 && ip)), dnl
> > > +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > +match=(ip6 && (inport == @pg0 && ip)), dnl
> > > +action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <-> ip6.src;
> > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > +])
> > > +
> > > +# Set l3-protocol=ip4 for the reject ACL.
> > > +ovn-nbctl --wait=sb set ACL . options:l3-protocol=ip4
> > > +
> > > +# ovn-northd should generate  2 lflows with ip4 tcp_reset and icmp4
> > actions.
> > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> > sort], [0], [dnl
> > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > +match=(ip4 && tcp && (inport == @pg0 && ip)), dnl
> > > +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src; tcp_reset {
> > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > +])
> > > +
> > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> > sort], [0], [dnl
> > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > +match=(ip4 && (inport == @pg0 && ip)), dnl
> > > +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > +])
> > > +
> > > +# Set l4-protocol=tcp for the reject ACL.
> > > +ovn-nbctl --wait=sb set ACL . options:l4-protocol=tcp
> > > +
> > > +# ovn-northd should generate 1 lflow with ip4 tcp_reset action.
> > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> > sort], [0], [dnl
> > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > +match=(ip4 && tcp && (inport == @pg0 && ip)), dnl
> > > +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src; tcp_reset {
> > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > +])
> > > +
> > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> > sort], [0], [dnl
> > > +])
> > > +
> > > +# Set l4-protocol=udp for the reject ACL.
> > > +ovn-nbctl --wait=sb set ACL . options:l4-protocol=udp
> > > +
> > > +# ovn-northd should generate 1 lflow with udp match and icmp4 action.
> > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> > sort], [0], [dnl
> > > +])
> > > +
> > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> > sort], [0], [dnl
> > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > +match=(ip4 && (inport == @pg0 && ip) && udp), dnl
> > > +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > +])
> > > +
> > > +# Remove l4-protocol from the reject ACL.
> > > +ovn-nbctl --wait=sb remove ACL . options l4-protocol
> > > +
> > > +# ovn-northd should generate 2 lflow with tcp_reset and icmp4 action.
> > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> > sort], [0], [dnl
> > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > +match=(ip4 && tcp && (inport == @pg0 && ip)), dnl
> > > +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src; tcp_reset {
> > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > +])
> > > +
> > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> > sort], [0], [dnl
> > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > +match=(ip4 && (inport == @pg0 && ip)), dnl
> > > +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > +])
> > > +
> > > +# Set l3-protocol to ip.
> > > +ovn-nbctl --wait=sb set ACL . options:l3-protocol=ip
> > > +
> > > +# ovn-northd should generate 4 lflow with ip4 tcp_reset, ip6 tcp_reset,
> > icmp4 and icmp6 actions.
> > > +
> > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> > sort], [0], [dnl
> > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > +match=(ip4 && tcp && (inport == @pg0 && ip)), dnl
> > > +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src; tcp_reset {
> > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > +match=(ip6 && tcp && (inport == @pg0 && ip)), dnl
> > > +action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src; tcp_reset {
> > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > +])
> > > +
> > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> > sort], [0], [dnl
> > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > +match=(ip4 && (inport == @pg0 && ip)), dnl
> > > +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > +match=(ip6 && (inport == @pg0 && ip)), dnl
> > > +action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <-> ip6.src;
> > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > +])
> > > +
> > > +# Set l3-protocol=ip6 for the reject ACL.
> > > +ovn-nbctl --wait=sb set ACL . options:l3-protocol=ip6
> > > +
> > > +# ovn-northd should generate  2 lflows with ip6 tcp_reset and icmp6
> > actions.
> > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> > sort], [0], [dnl
> > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > +match=(ip6 && tcp && (inport == @pg0 && ip)), dnl
> > > +action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src; tcp_reset {
> > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > +])
> > > +
> > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> > sort], [0], [dnl
> > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > +match=(ip6 && (inport == @pg0 && ip)), dnl
> > > +action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <-> ip6.src;
> > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > +])
> > > +
> > > +# Set l4-protocol=tcp for the reject ACL.
> > > +ovn-nbctl --wait=sb set ACL . options:l4-protocol=tcp
> > > +
> > > +# ovn-northd should generate 1 lflow with ip6 tcp_reset action.
> > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> > sort], [0], [dnl
> > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > +match=(ip6 && tcp && (inport == @pg0 && ip)), dnl
> > > +action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src; tcp_reset {
> > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > +])
> > > +
> > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> > sort], [0], [dnl
> > > +])
> > > +
> > > +# Set l4-protocol=udp for the reject ACL.
> > > +ovn-nbctl --wait=sb set ACL . options:l4-protocol=udp
> > > +
> > > +# ovn-northd should generate 1 lflow with udp match and icmp6 action.
> > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> > sort], [0], [dnl
> > > +])
> > > +
> > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> > sort], [0], [dnl
> > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > +match=(ip6 && (inport == @pg0 && ip) && udp), dnl
> > > +action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <-> ip6.src;
> > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > +])
> > > +
> > > +# Set l3-protocol to ip and l4-protocol=tcp for the reject ACL.
> > > +ovn-nbctl --wait=sb set ACL . options:l3-protocol=ip
> > > +ovn-nbctl --wait=sb set ACL . options:l4-protocol=tcp
> > > +
> > > +# ovn-northd should generate 2 lflow with ip4 tcp_reset and ip6
> > tcp_reset actions.
> > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> > sort], [0], [dnl
> > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > +match=(ip4 && tcp && (inport == @pg0 && ip)), dnl
> > > +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src; tcp_reset {
> > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > +match=(ip6 && tcp && (inport == @pg0 && ip)), dnl
> > > +action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src; tcp_reset {
> > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > +])
> > > +
> > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> > sort], [0], [dnl
> > > +])
> > > +
> > > +# Set l3-protocol to ip and l4-protocol=udp for the reject ACL.
> > > +ovn-nbctl --wait=sb set ACL . options:l3-protocol=ip
> > > +ovn-nbctl --wait=sb set ACL . options:l4-protocol=udp
> > > +
> > > +# ovn-northd should generate 2 lflows (udp match) with icmp4 and icmp6
> > actions.
> > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> > sort], [0], [dnl
> > > +])
> > > +
> > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> > sort], [0], [dnl
> > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > +match=(ip4 && (inport == @pg0 && ip) && udp), dnl
> > > +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > +match=(ip6 && (inport == @pg0 && ip) && udp), dnl
> > > +action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <-> ip6.src;
> > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > +])
> > > +
> > > +# Add an ACL with allow-related
> > > +ovn-nbctl --wait=sb acl-add pg0 to-lport 1000 "ip" allow-related
> > > +
> > > +# ovn-northd should generate 4 lflow with 2 icmp4 and 2 icmp6 actions.
> > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> > sort], [0], [dnl
> > > +])
> > > +
> > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> > sort], [0], [dnl
> > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > +match=((reg0[[10]] == 1) && ip4 && (inport == @pg0 && ip) && udp), dnl\
> > > +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; icmp4 { eth.dst
> > <-> eth.src; ip4.dst <-> ip4.src; outport <-> inport;
> > next(pipeline=ingress,table=20); };)
> > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > +match=((reg0[[10]] == 1) && ip6 && (inport == @pg0 && ip) && udp), dnl
> > > +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; icmp6 { eth.dst
> > <-> eth.src; ip6.dst <-> ip6.src; outport <-> inport;
> > next(pipeline=ingress,table=20); };)
> > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > +match=((reg0[[9]] == 1) && ip4 && (inport == @pg0 && ip) && udp), dnl
> > > +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > +match=((reg0[[9]] == 1) && ip6 && (inport == @pg0 && ip) && udp), dnl
> > > +action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <-> ip6.src;
> > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > +])
> > > +
> > > +# Set l3-protocol to ip and l4-protocol=tcp for the reject ACL.
> > > +rej_acl=$(ovn-nbctl --bare --columns _uuid  find ACL action=reject)
> > > +ovn-nbctl --wait=sb set ACL $rej_acl options:l3-protocol=ip
> > > +ovn-nbctl --wait=sb set ACL $rej_acl options:l4-protocol=tcp
> > > +
> > > +# ovn-northd should generate 4 lflow with 2 ip4 tcp_reset and 2 ip6
> > tcp_reset actions.
> > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> > sort], [0], [dnl
> > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > +match=((reg0[[10]] == 1) && ip4 && tcp && (inport == @pg0 && ip)), dnl
> > > +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; eth.dst <->
> > eth.src; ip4.dst <-> ip4.src; tcp_reset { outport <-> inport;
> > next(pipeline=ingress,table=20); };)
> > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > +match=((reg0[[10]] == 1) && ip6 && tcp && (inport == @pg0 && ip)), dnl
> > > +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; eth.dst <->
> > eth.src; ip6.dst <-> ip6.src; tcp_reset { outport <-> inport;
> > next(pipeline=ingress,table=20); };)
> > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > +match=((reg0[[9]] == 1) && ip4 && tcp && (inport == @pg0 && ip)), dnl
> > > +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src; tcp_reset {
> > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > +match=((reg0[[9]] == 1) && ip6 && tcp && (inport == @pg0 && ip)), dnl
> > > +action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src; tcp_reset {
> > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > +])
> > > +
> > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> > sort], [0], [dnl
> > > +])
> > > +
> > > +
> > > +# Clear l3-protocol and set l4-protocol to udp
> > > +ovn-nbctl --wait=sb remove ACL $rej_acl options l3-protocol
> > > +ovn-nbctl --wait=sb set ACL $rej_acl options:l4-protocol=udp
> > > +
> > > +# ovn-northd should generate 4 lflows (with udp match) with 2 icmp4 and
> > 2 icmp6 actions.
> > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> > sort], [0], [dnl
> > > +])
> > > +
> > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> > sort], [0], [dnl
> > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > +match=((reg0[[10]] == 1) && ip4 && (inport == @pg0 && ip) && udp), dnl\
> > > +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; icmp4 { eth.dst
> > <-> eth.src; ip4.dst <-> ip4.src; outport <-> inport;
> > next(pipeline=ingress,table=20); };)
> > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > +match=((reg0[[10]] == 1) && ip6 && (inport == @pg0 && ip) && udp), dnl
> > > +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; icmp6 { eth.dst
> > <-> eth.src; ip6.dst <-> ip6.src; outport <-> inport;
> > next(pipeline=ingress,table=20); };)
> > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > +match=((reg0[[9]] == 1) && ip4 && (inport == @pg0 && ip) && udp), dnl
> > > +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > +match=((reg0[[9]] == 1) && ip6 && (inport == @pg0 && ip) && udp), dnl
> > > +action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <-> ip6.src;
> > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > +])
> > > +
> > > +# Clear l3-protocol and set l4-protocol to tcp
> > > +ovn-nbctl --wait=sb remove ACL $rej_acl options l3-protocol
> > > +ovn-nbctl --wait=sb set ACL $rej_acl options:l4-protocol=tcp
> > > +
> > > +# ovn-northd should generate 4 lflow with 2 ip4 tcp_reset and 2 ip6
> > tcp_reset actions.
> > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> > sort], [0], [dnl
> > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > +match=((reg0[[10]] == 1) && ip4 && tcp && (inport == @pg0 && ip)), dnl
> > > +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; eth.dst <->
> > eth.src; ip4.dst <-> ip4.src; tcp_reset { outport <-> inport;
> > next(pipeline=ingress,table=20); };)
> > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > +match=((reg0[[10]] == 1) && ip6 && tcp && (inport == @pg0 && ip)), dnl
> > > +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; eth.dst <->
> > eth.src; ip6.dst <-> ip6.src; tcp_reset { outport <-> inport;
> > next(pipeline=ingress,table=20); };)
> > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > +match=((reg0[[9]] == 1) && ip4 && tcp && (inport == @pg0 && ip)), dnl
> > > +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src; tcp_reset {
> > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > +match=((reg0[[9]] == 1) && ip6 && tcp && (inport == @pg0 && ip)), dnl
> > > +action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src; tcp_reset {
> > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > +])
> > > +
> > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> > sort], [0], [dnl
> > > +])
> > > +
> > > +AT_CLEANUP
> > > --
> > > 2.26.2
> > >
> > > _______________________________________________
> > > dev mailing list
> > > dev@openvswitch.org
> > > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> > _______________________________________________
> > dev mailing list
> > dev@openvswitch.org
> > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> >
Han Zhou Sept. 28, 2020, 4:10 p.m. UTC | #4
On Mon, Sep 28, 2020 at 2:21 AM Numan Siddique <numans@ovn.org> wrote:
>
> On Mon, Sep 28, 2020 at 2:14 PM Numan Siddique <numans@ovn.org> wrote:
> >
> > On Mon, Sep 28, 2020 at 5:57 AM Han Zhou <hzhou@ovn.org> wrote:
> > >
> > > Thanks Numan. I have some comments on the solution.
> > >
> > > On Thu, Sep 24, 2020 at 9:50 AM <numans@ovn.org> wrote:
> > > >
> > > > From: Numan Siddique <numans@ovn.org>
> > > >
> > > > ovn-northd adds below lflows for a reject ACL with a match - M
> > > >
> > > > match = (ip4 && tcp && 'M') action = tcp_reject{}
> > > > match = (ip6 && tcp && 'M') action = tcp_reject{}
> > > > match = (ip4 && 'M') action = icmp4{}
> > > > match = (ip6 && 'M') action = icmp6{}
> > > >
> > > > This approach has a couple of problems:
> > > >    - ovn-controller can reject the lflows if there are invalid
matches.
> > > >      Eg. If match 'M' is - 'ip4 && udp'.
> > > >
> > > >    - In a large scale deployment, this could result in lot of
invalid
> > > >      logical flows and increase the size of the SB DB.
> > > >
> > > > This patch addresses this problem by providing an option to the CMS
> > > > to specify the l3 protocol and l4 protocol as hints. A new column
> > > > 'options' is added to the ACL table with the options -
> > > > options:l3-protocol and options:l4-protocol.
> > > >
> > > > ovn-northd will now generate only the required lflows for the reject
> > > > ACL. If no options are set, then it falls back to the default
scenario.
> > > >
> > >
> > > Thanks Numan. I believe the solution should work. However, it seems a
> > > little confusing for the end user about how to use the hint field. I
think
> > > maybe we should try to avoid the extra field if possible. Here is my
> > > proposal:
> > >
> >
> > Thanks Han for the comments. I do agree if we can avoid the extra
option.
> >
> > > 1. For the tcp v.s. udp check, since we don't want northd to parse the
> > > match condition, we will have to rely on users input. However,
instead of a
> > > hint, I think it is better to let the user explicitly specify in the
action
> > > field. E.g. "reject" means sending icmp, and "tcp_reject" means
sending tcp
> > > reset if the traffic is tcp and otherwise sending icmp. So, if the
user
> > > adds an ACL with match include "udp", they will naturally use "reject"
> > > instead of "tcp_reject" because they know it is not possible for a TCP
> > > packet to hit this ACL. If they add an ACL that tcp traffic is
expected to
> > > match (either TCP only, or general IP traffic), they can use
"tcp_reject".
> > > Would this be more straightforward than an extra "hint" field?
> > >
> >
> > I have few comments here.
> > - If we consider "reject" = "icmp", then we will be breaking the
> > backward compatibility.
> >     Right now, for a reject action, ovn-northd would send tcp_reset
> > for tcp traffic and icmp reply or other traffic.
> >     After your proposed approach, the  behaviour would change ? Is
> > that OK ? Because existing test cases would fail.
> >     The proposed patch handles the backward compatibility. Although
> > I'm not sure if it is really required in this case.
> >
> > -  For  a tcp_reject action, if the match has "tcp", then we will end
> > up adding a logical flow with the icmp action, which is
> >     really not required. Or do you mean, pinctrl would take the
> > decision while handling the "tcp_reject" action to either
> >     send tcp reset packet or icmp packet ? If so, we can probably just
> > have one action - "reject". Please see below.
> >
> > > 2. For ip4 v.s. ip6, the idea is to support generic ovn logical flow
> > > actions "tcp_reject" and "icmp" in ovn-controller, which can handle
both
> > > ipv4 and ipv6. If this can be supported, the logical flows only needs
to be:
> > > match = (tcp && 'M') action = tcp_reject{}
> > > match = ('M') action = icmp{}
> > >
> >
> > Ok. In this case we cannot support inner actions unless they are
> > independent of the ip version.
> > i.e we can't support - tcp_reject { eth.dst <-> eth.src; ip4.dst <->
> > ip4.src; } or
> > tcp_reject {eth.dst <-> eth.src; ip6.dst <-> ip6.src;} in one logical
> > flow. ovn-northd has to generate
> > 2 logical flows.
> >   - ip4 && 'M', action = tcp_reject { eth.dst <-> eth.src; ip4.dst <->
> > ip4.src; }
> >   - ip6 && 'M', action = tcp_reject { eth.dst <-> eth.src; ip6.dst <->
> > ip6.src; }
> >
> >
> > Right now for one reject ACL we add the below lflows
> >    - ip4 && tcp && 'M', action = tcp_reset { eth.dst <-> eth.src;
> > ip4.dst <-> ip4.src; outport <-> inport;}
> >    - ip6 && tcp && 'M', action = tcp_reset { eth.dst <-> eth.src;
> > ip6.dst <-> ip6.src; outport <-> inport;}
> >    - ip4 && 'M'            , action = icmp4 {eth.dst <-> eth.src;
> > ip4.dst <-> ip4.src; outport <-> inport;}
> >    - ip6 && 'M'            , action = icmp6 {eth.dst <-> eth.src;
> > ip6.dst <-> ip6.src; outport <-> inport;}
> >
> > I have another idea based on your suggestions here: How about we have
> > a new action - reject{} which will be used
> > for all reject ACLs and this will
> >     - send tcp reset packet if the packet is tcp (pinctrl would take
> > the decision)
> >     - send icmp packet otherwise.
> >
> > We can add the logical flow for a reject ACL in 2 ways
> >
> > 1. match = M, action = reject { eth.dst <-> eth.src, outport <->
inport;}
> >
> > OR
> >
> > 2. match = M, action = reject { eth.dst <-> eth.src, ip.src <->
> > ip.dst; outport <-> inport;}
> >
> > In (1), pinctrl while handling the reject action, will swap the ip src
> > with ip dst (i.e ip4.dst <-> ip4.src or ip6.dst <-> ip6.src }.
> >
> > In (2), we will add 2 new OVN fields "ip.src" and "ip.dst" (just like
> > we have icmp4.frag_mtu now) and the action - ip.src <-> ip.dst
> > will result in another controller action which will swap the ip src
> > and ip dst fields (just like how "icmp4.frag_mtu = 1400" would result
> > into a controller action).
> >
> > Even though (1) will work, it's not transparent. I would personally
> > prefer (2) and I think it's ok if the reject ACL would result in 2 OF
> > controller actions.
> >
> > With this we will have just one logical flow (instead of 4 ) and there
> > will be no changes on the CMS side.
> >

Yes, this would be even better.

> > What do you think ?
> >
> > Thanks
> > Numan
> >
> >
> > > I haven't thought through all the details but it seems doable. Did
you have
> > > any thoughts on this? (If it proves to be unreasonable I am ok with
current
> > > "hint" approach)
> > >
> > > In addition, I found the existing TCP reject handling has a bug which
> > > messes up the user defined ACL priorities. I just posted a patch:
> > >
https://patchwork.ozlabs.org/project/ovn/patch/20200928000904.858685-1-hzhou@ovn.org/
> > > It is not directly related to this patch but it would be great if you
could
> > > take a look.
>
> Sure.
>
With only one lflow, my above fix is not needed. (I am not sure about
backporting to old branches.)

> Thanks
> Numan
>
> > >
> > > Thanks,
> > > Han
> > >
> > >
> > > > Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=1876990
> > > > Reported-by: Ilya Maximets <i.maximets@ovn.org>
> > > > Signed-off-by: Numan Siddique <numans@ovn.org>
> > > > ---
> > > >  northd/ovn-northd.c | 194 ++++++++++++++++++++----------
> > > >  ovn-nb.ovsschema    |   7 +-
> > > >  ovn-nb.xml          |  43 +++++++
> > > >  tests/ovn-northd.at | 285
++++++++++++++++++++++++++++++++++++++++++++
> > > >  4 files changed, 461 insertions(+), 68 deletions(-)
> > > >
> > > > diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
> > > > index 3324c9e81d..d167137e07 100644
> > > > --- a/northd/ovn-northd.c
> > > > +++ b/northd/ovn-northd.c
> > > > @@ -5372,73 +5372,135 @@ build_reject_acl_rules(struct ovn_datapath
*od,
> > > struct hmap *lflows,
> > > >      struct ds actions = DS_EMPTY_INITIALIZER;
> > > >      bool ingress = (stage == S_SWITCH_IN_ACL);
> > > >
> > > > -    /* TCP */
> > > > -    build_acl_log(&actions, acl);
> > > > -    if (extra_match->length > 0) {
> > > > -        ds_put_format(&match, "(%s) && ", extra_match->string);
> > > > -    }
> > > > -    ds_put_format(&match, "ip4 && tcp && (%s)", acl->match);
> > > > -    ds_put_format(&actions, "reg0 = 0; "
> > > > -                  "eth.dst <-> eth.src; ip4.dst <-> ip4.src; "
> > > > -                  "tcp_reset { outport <-> inport; %s };",
> > > > -                  ingress ? "next(pipeline=egress,table=5);"
> > > > -                          : "next(pipeline=ingress,table=20);");
> > > > -    ovn_lflow_add_with_hint(lflows, od, stage,
> > > > -                            acl->priority + OVN_ACL_PRI_OFFSET +
10,
> > > > -                            ds_cstr(&match), ds_cstr(&actions),
> > > stage_hint);
> > > > -    ds_clear(&match);
> > > > -    ds_clear(&actions);
> > > > -    build_acl_log(&actions, acl);
> > > > -    if (extra_match->length > 0) {
> > > > -        ds_put_format(&match, "(%s) && ", extra_match->string);
> > > > -    }
> > > > -    ds_put_format(&match, "ip6 && tcp && (%s)", acl->match);
> > > > -    ds_put_format(&actions, "reg0 = 0; "
> > > > -                  "eth.dst <-> eth.src; ip6.dst <-> ip6.src; "
> > > > -                  "tcp_reset { outport <-> inport; %s };",
> > > > -                  ingress ? "next(pipeline=egress,table=5);"
> > > > -                          : "next(pipeline=ingress,table=20);");
> > > > -    ovn_lflow_add_with_hint(lflows, od, stage,
> > > > -                            acl->priority + OVN_ACL_PRI_OFFSET +
10,
> > > > -                            ds_cstr(&match), ds_cstr(&actions),
> > > stage_hint);
> > > > +    bool is_ip4 = true;
> > > > +    bool is_ip6 = true;
> > > > +    bool tcp_reset = true;
> > > > +    bool icmp_reset = true;
> > > > +    bool is_udp = false;
> > > > +
> > > > +    const char *l3_protocol = smap_get(&acl->options,
"l3-protocol");
> > > > +    if (l3_protocol) {
> > > > +        if (!strcasecmp(l3_protocol, "ip")) {
> > > > +            is_ip4 = true;
> > > > +            is_ip6 = true;
> > > > +        } else if (!strcasecmp(l3_protocol, "ip4")) {
> > > > +            is_ip4 = true;
> > > > +            is_ip6 = false;
> > > > +        } else if (!strcasecmp(l3_protocol, "ip6")) {
> > > > +            is_ip6 = true;
> > > > +            is_ip4 = false;
> > > > +        }
> > > > +    }
> > > > +
> > > > +    const char *l4_protocol = smap_get(&acl->options,
"l4-protocol");
> > > > +    if (l4_protocol) {
> > > > +        if (!strcasecmp(l4_protocol, "tcp")) {
> > > > +            tcp_reset = true;
> > > > +            icmp_reset = false;
> > > > +        } else if (!strcasecmp(l4_protocol, "udp")) {
> > > > +            tcp_reset = false;
> > > > +            is_udp = true;
> > > > +        } else {
> > > > +            tcp_reset = false;
> > > > +        }
> > > > +    }
> > > >
> > > > -    /* IP traffic */
> > > > -    ds_clear(&match);
> > > > -    ds_clear(&actions);
> > > > -    build_acl_log(&actions, acl);
> > > > -    if (extra_match->length > 0) {
> > > > -        ds_put_format(&match, "(%s) && ", extra_match->string);
> > > > -    }
> > > > -    ds_put_format(&match, "ip4 && (%s)", acl->match);
> > > > -    if (extra_actions->length > 0) {
> > > > -        ds_put_format(&actions, "%s ", extra_actions->string);
> > > > -    }
> > > > -    ds_put_format(&actions, "reg0 = 0; "
> > > > -                  "icmp4 { eth.dst <-> eth.src; ip4.dst <->
ip4.src; "
> > > > -                  "outport <-> inport; %s };",
> > > > -                  ingress ? "next(pipeline=egress,table=5);"
> > > > -                          : "next(pipeline=ingress,table=20);");
> > > > -    ovn_lflow_add_with_hint(lflows, od, stage,
> > > > -                            acl->priority + OVN_ACL_PRI_OFFSET,
> > > > -                            ds_cstr(&match), ds_cstr(&actions),
> > > stage_hint);
> > > > -    ds_clear(&match);
> > > > -    ds_clear(&actions);
> > > > -    build_acl_log(&actions, acl);
> > > > -    if (extra_match->length > 0) {
> > > > -        ds_put_format(&match, "(%s) && ", extra_match->string);
> > > > -    }
> > > > -    ds_put_format(&match, "ip6 && (%s)", acl->match);
> > > > -    if (extra_actions->length > 0) {
> > > > -        ds_put_format(&actions, "%s ", extra_actions->string);
> > > > -    }
> > > > -    ds_put_format(&actions, "reg0 = 0; icmp6 { "
> > > > -                  "eth.dst <-> eth.src; ip6.dst <-> ip6.src; "
> > > > -                  "outport <-> inport; %s };",
> > > > -                  ingress ? "next(pipeline=egress,table=5);"
> > > > -                          : "next(pipeline=ingress,table=20);");
> > > > -    ovn_lflow_add_with_hint(lflows, od, stage,
> > > > -                            acl->priority + OVN_ACL_PRI_OFFSET,
> > > > -                            ds_cstr(&match), ds_cstr(&actions),
> > > stage_hint);
> > > > +    if (is_ip4) {
> > > > +        if (tcp_reset) {
> > > > +            build_acl_log(&actions, acl);
> > > > +            if (extra_match->length > 0) {
> > > > +                ds_put_format(&match, "(%s) && ",
extra_match->string);
> > > > +            }
> > > > +            ds_put_format(&match, "ip4 && tcp && (%s)",
acl->match);
> > > > +            if (extra_actions->length > 0) {
> > > > +                ds_put_format(&actions, "%s ",
extra_actions->string);
> > > > +            }
> > > > +            ds_put_format(&actions, "reg0 = 0; "
> > > > +                          "eth.dst <-> eth.src; ip4.dst <->
ip4.src; "
> > > > +                          "tcp_reset { outport <-> inport; %s };",
> > > > +                          ingress ?
"next(pipeline=egress,table=5);"
> > > > +                                  :
"next(pipeline=ingress,table=20);");
> > > > +            ovn_lflow_add_with_hint(lflows, od, stage,
> > > > +                                    acl->priority +
OVN_ACL_PRI_OFFSET +
> > > 10,
> > > > +                                    ds_cstr(&match),
ds_cstr(&actions),
> > > > +                                    stage_hint);
> > > > +        }
> > > > +
> > > > +        if (icmp_reset) {
> > > > +            ds_clear(&match);
> > > > +            ds_clear(&actions);
> > > > +            build_acl_log(&actions, acl);
> > > > +            if (extra_match->length > 0) {
> > > > +                ds_put_format(&match, "(%s) && ",
extra_match->string);
> > > > +            }
> > > > +            ds_put_format(&match, "ip4 && (%s)", acl->match);
> > > > +            if (is_udp) {
> > > > +                ds_put_cstr(&match, " && udp");
> > > > +            }
> > > > +            if (extra_actions->length > 0) {
> > > > +                ds_put_format(&actions, "%s ",
extra_actions->string);
> > > > +            }
> > > > +            ds_put_format(&actions, "reg0 = 0; "
> > > > +                          "icmp4 { eth.dst <-> eth.src; ip4.dst <->
> > > ip4.src; "
> > > > +                          "outport <-> inport; %s };",
> > > > +                          ingress ?
"next(pipeline=egress,table=5);"
> > > > +                                  :
"next(pipeline=ingress,table=20);");
> > > > +            ovn_lflow_add_with_hint(lflows, od, stage,
> > > > +                                    acl->priority +
OVN_ACL_PRI_OFFSET,
> > > > +                                    ds_cstr(&match),
ds_cstr(&actions),
> > > > +                                    stage_hint);
> > > > +        }
> > > > +    }
> > > > +
> > > > +    if (is_ip6) {
> > > > +        if (tcp_reset) {
> > > > +            ds_clear(&match);
> > > > +            ds_clear(&actions);
> > > > +            build_acl_log(&actions, acl);
> > > > +            if (extra_match->length > 0) {
> > > > +                ds_put_format(&match, "(%s) && ",
extra_match->string);
> > > > +            }
> > > > +            ds_put_format(&match, "ip6 && tcp && (%s)",
acl->match);
> > > > +            if (extra_actions->length > 0) {
> > > > +                ds_put_format(&actions, "%s ",
extra_actions->string);
> > > > +            }
> > > > +            ds_put_format(&actions, "reg0 = 0; "
> > > > +                          "eth.dst <-> eth.src; ip6.dst <->
ip6.src; "
> > > > +                          "tcp_reset { outport <-> inport; %s };",
> > > > +                          ingress ?
"next(pipeline=egress,table=5);"
> > > > +                                  :
"next(pipeline=ingress,table=20);");
> > > > +            ovn_lflow_add_with_hint(lflows, od, stage,
> > > > +                                    acl->priority +
OVN_ACL_PRI_OFFSET +
> > > 10,
> > > > +                                    ds_cstr(&match),
ds_cstr(&actions),
> > > > +                                    stage_hint);
> > > > +        }
> > > > +
> > > > +        if (icmp_reset) {
> > > > +            ds_clear(&match);
> > > > +            ds_clear(&actions);
> > > > +            build_acl_log(&actions, acl);
> > > > +            if (extra_match->length > 0) {
> > > > +                ds_put_format(&match, "(%s) && ",
extra_match->string);
> > > > +            }
> > > > +            ds_put_format(&match, "ip6 && (%s)", acl->match);
> > > > +            if (is_udp) {
> > > > +                ds_put_cstr(&match, " && udp");
> > > > +            }
> > > > +
> > > > +            if (extra_actions->length > 0) {
> > > > +                ds_put_format(&actions, "%s ",
extra_actions->string);
> > > > +            }
> > > > +            ds_put_format(&actions, "reg0 = 0; icmp6 { "
> > > > +                          "eth.dst <-> eth.src; ip6.dst <->
ip6.src; "
> > > > +                          "outport <-> inport; %s };",
> > > > +                          ingress ?
"next(pipeline=egress,table=5);"
> > > > +                                :
"next(pipeline=ingress,table=20);");
> > > > +            ovn_lflow_add_with_hint(lflows, od, stage,
> > > > +                                    acl->priority +
OVN_ACL_PRI_OFFSET,
> > > > +                                    ds_cstr(&match),
ds_cstr(&actions),
> > > > +                                    stage_hint);
> > > > +        }
> > > > +    }
> > > >
> > > >      ds_destroy(&match);
> > > >      ds_destroy(&actions);
> > > > diff --git a/ovn-nb.ovsschema b/ovn-nb.ovsschema
> > > > index 092322ab2c..00f1c7cd4b 100644
> > > > --- a/ovn-nb.ovsschema
> > > > +++ b/ovn-nb.ovsschema
> > > > @@ -1,7 +1,7 @@
> > > >  {
> > > >      "name": "OVN_Northbound",
> > > > -    "version": "5.27.0",
> > > > -    "cksum": "3507518247 26773",
> > > > +    "version": "5.28.0",
> > > > +    "cksum": "699859908 26928",
> > > >      "tables": {
> > > >          "NB_Global": {
> > > >              "columns": {
> > > > @@ -225,6 +225,9 @@
> > > >                                                          "debug"]]},
> > > >                                        "min": 0, "max": 1}},
> > > >                  "meter": {"type": {"key": "string", "min": 0,
"max": 1}},
> > > > +                "options": {
> > > > +                    "type": {"key": "string", "value": "string",
> > > > +                             "min": 0, "max": "unlimited"}},
> > > >                  "external_ids": {
> > > >                      "type": {"key": "string", "value": "string",
> > > >                               "min": 0, "max": "unlimited"}}},
> > > > diff --git a/ovn-nb.xml b/ovn-nb.xml
> > > > index 86195af341..2c3497e2ae 100644
> > > > --- a/ovn-nb.xml
> > > > +++ b/ovn-nb.xml
> > > > @@ -1721,6 +1721,49 @@
> > > >        </ul>
> > > >      </column>
> > > >
> > > > +    <group title="Common options">
> > > > +      <column name="options">
> > > > +        This column provides general key/value settings. The
supported
> > > > +        options are described individually below.
> > > > +      </column>
> > > > +
> > > > +      <group title="Options for providing protocol hints for reject
> > > ACL.">
> > > > +        <p>
> > > > +          The ACL match specified in the
> > > > +          <ref column="match" table="ACL" db="OVN_Northbound"/>
column is
> > > > +          opaque to <code>OVN</code> and <code>ovn-northd</code>
doesn't
> > > > +          look into the match fields.
> > > > +          These options can be specified by CMS to provide hints to
> > > > +          <code>OVN</code> about the L3 and L4 protocol matches
for the
> > > reject
> > > > +          ACL. <code>ovn-northd</code> uses these options if set
to use
> > > > +          appropriate actions when generating logical flows.
> > > > +        </p>
> > > > +
> > > > +        <p>
> > > > +          If these options are not set, then
<code>ovn-northd</code>
> > > assumes
> > > > +          the reject ACL applies to IPv4, IPv6 and TCP packets.
> > > > +        </p>
> > > > +
> > > > +        <column name="options" key="l3-protocol">
> > > > +          The possible values are <code>ip</code>,
<code>ip4</code> and
> > > > +          <code>ip6</code>. If the value is <code>ip</code>, it
means the
> > > > +          reject ACL action applies to both IPv4 and IPv6 packets.
If the
> > > > +          value is <code>ip4</code>, it means the reject ACL action
> > > applies to
> > > > +          IPv4 packets and the value <code>ip6</code> applies to
IPv6
> > > packets.
> > > > +        </column>
> > > > +
> > > > +        <column name="options" key="l4-protocol">
> > > > +          <p>
> > > > +            The possible values are <code>tcp</code> and
> > > <code>udp</code>.
> > > > +            If the value is <code>tcp</code>, it means the
> > > > +            reject ACL action applies to TCP packets. If the
> > > > +            value is <code>udp</code>, it means the reject ACL
action
> > > applies
> > > > +            to UDP packets.
> > > > +          </p>
> > > > +        </column>
> > > > +      </group>
> > > > +    </group>
> > > > +
> > > >      <group title="Logging">
> > > >        <p>
> > > >          These columns control whether and how OVN logs packets that
> > > match an
> > > > diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> > > > index 99a9204f1f..4a30f8ed07 100644
> > > > --- a/tests/ovn-northd.at
> > > > +++ b/tests/ovn-northd.at
> > > > @@ -2010,3 +2010,288 @@ ovn-nbctl --wait=sb set NB_Global .
> > > options:ignore_lsp_down=true
> > > >  AT_CHECK([ovn-sbctl lflow-list | grep arp | grep 10\.0\.0\.1], [0],
> > > [ignore])
> > > >
> > > >  AT_CLEANUP
> > > > +
> > > > +AT_SETUP([ovn-northd -- ACL reject flows])
> > > > +ovn_start
> > > > +
> > > > +ovn-nbctl ls-add sw0
> > > > +ovn-nbctl lsp-add sw0 sw0-p1
> > > > +ovn-nbctl lsp-add sw0 sw0-p2
> > > > +
> > > > +ovn-nbctl pg-add pg0 sw0-p1 sw0-p2
> > > > +ovn-nbctl acl-add pg0 to-lport 1002 "inport == @pg0 && ip" reject
> > > > +
> > > > +ovn-nbctl --wait=sb sync
> > > > +
> > > > +# If there is a reject ACL wihtout any protocol hints, then
ovn-northd
> > > should
> > > > +# generate lflows with ip4 tcp_reset, icmp4, ip6 tcp_reset and
icmp6
> > > actions.
> > > > +
> > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> > > sort], [0], [dnl
> > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > +match=(ip4 && tcp && (inport == @pg0 && ip)), dnl
> > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src;
tcp_reset {
> > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > +match=(ip6 && tcp && (inport == @pg0 && ip)), dnl
> > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src;
tcp_reset {
> > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > +])
> > > > +
> > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> > > sort], [0], [dnl
> > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > +match=(ip4 && (inport == @pg0 && ip)), dnl
> > > > +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > +match=(ip6 && (inport == @pg0 && ip)), dnl
> > > > +action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <-> ip6.src;
> > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > +])
> > > > +
> > > > +# Set l3-protocol=ip4 for the reject ACL.
> > > > +ovn-nbctl --wait=sb set ACL . options:l3-protocol=ip4
> > > > +
> > > > +# ovn-northd should generate  2 lflows with ip4 tcp_reset and icmp4
> > > actions.
> > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> > > sort], [0], [dnl
> > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > +match=(ip4 && tcp && (inport == @pg0 && ip)), dnl
> > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src;
tcp_reset {
> > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > +])
> > > > +
> > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> > > sort], [0], [dnl
> > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > +match=(ip4 && (inport == @pg0 && ip)), dnl
> > > > +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > +])
> > > > +
> > > > +# Set l4-protocol=tcp for the reject ACL.
> > > > +ovn-nbctl --wait=sb set ACL . options:l4-protocol=tcp
> > > > +
> > > > +# ovn-northd should generate 1 lflow with ip4 tcp_reset action.
> > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> > > sort], [0], [dnl
> > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > +match=(ip4 && tcp && (inport == @pg0 && ip)), dnl
> > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src;
tcp_reset {
> > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > +])
> > > > +
> > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> > > sort], [0], [dnl
> > > > +])
> > > > +
> > > > +# Set l4-protocol=udp for the reject ACL.
> > > > +ovn-nbctl --wait=sb set ACL . options:l4-protocol=udp
> > > > +
> > > > +# ovn-northd should generate 1 lflow with udp match and icmp4
action.
> > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> > > sort], [0], [dnl
> > > > +])
> > > > +
> > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> > > sort], [0], [dnl
> > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > +match=(ip4 && (inport == @pg0 && ip) && udp), dnl
> > > > +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > +])
> > > > +
> > > > +# Remove l4-protocol from the reject ACL.
> > > > +ovn-nbctl --wait=sb remove ACL . options l4-protocol
> > > > +
> > > > +# ovn-northd should generate 2 lflow with tcp_reset and icmp4
action.
> > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> > > sort], [0], [dnl
> > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > +match=(ip4 && tcp && (inport == @pg0 && ip)), dnl
> > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src;
tcp_reset {
> > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > +])
> > > > +
> > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> > > sort], [0], [dnl
> > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > +match=(ip4 && (inport == @pg0 && ip)), dnl
> > > > +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > +])
> > > > +
> > > > +# Set l3-protocol to ip.
> > > > +ovn-nbctl --wait=sb set ACL . options:l3-protocol=ip
> > > > +
> > > > +# ovn-northd should generate 4 lflow with ip4 tcp_reset, ip6
tcp_reset,
> > > icmp4 and icmp6 actions.
> > > > +
> > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> > > sort], [0], [dnl
> > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > +match=(ip4 && tcp && (inport == @pg0 && ip)), dnl
> > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src;
tcp_reset {
> > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > +match=(ip6 && tcp && (inport == @pg0 && ip)), dnl
> > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src;
tcp_reset {
> > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > +])
> > > > +
> > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> > > sort], [0], [dnl
> > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > +match=(ip4 && (inport == @pg0 && ip)), dnl
> > > > +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > +match=(ip6 && (inport == @pg0 && ip)), dnl
> > > > +action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <-> ip6.src;
> > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > +])
> > > > +
> > > > +# Set l3-protocol=ip6 for the reject ACL.
> > > > +ovn-nbctl --wait=sb set ACL . options:l3-protocol=ip6
> > > > +
> > > > +# ovn-northd should generate  2 lflows with ip6 tcp_reset and icmp6
> > > actions.
> > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> > > sort], [0], [dnl
> > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > +match=(ip6 && tcp && (inport == @pg0 && ip)), dnl
> > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src;
tcp_reset {
> > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > +])
> > > > +
> > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> > > sort], [0], [dnl
> > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > +match=(ip6 && (inport == @pg0 && ip)), dnl
> > > > +action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <-> ip6.src;
> > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > +])
> > > > +
> > > > +# Set l4-protocol=tcp for the reject ACL.
> > > > +ovn-nbctl --wait=sb set ACL . options:l4-protocol=tcp
> > > > +
> > > > +# ovn-northd should generate 1 lflow with ip6 tcp_reset action.
> > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> > > sort], [0], [dnl
> > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > +match=(ip6 && tcp && (inport == @pg0 && ip)), dnl
> > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src;
tcp_reset {
> > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > +])
> > > > +
> > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> > > sort], [0], [dnl
> > > > +])
> > > > +
> > > > +# Set l4-protocol=udp for the reject ACL.
> > > > +ovn-nbctl --wait=sb set ACL . options:l4-protocol=udp
> > > > +
> > > > +# ovn-northd should generate 1 lflow with udp match and icmp6
action.
> > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> > > sort], [0], [dnl
> > > > +])
> > > > +
> > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> > > sort], [0], [dnl
> > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > +match=(ip6 && (inport == @pg0 && ip) && udp), dnl
> > > > +action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <-> ip6.src;
> > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > +])
> > > > +
> > > > +# Set l3-protocol to ip and l4-protocol=tcp for the reject ACL.
> > > > +ovn-nbctl --wait=sb set ACL . options:l3-protocol=ip
> > > > +ovn-nbctl --wait=sb set ACL . options:l4-protocol=tcp
> > > > +
> > > > +# ovn-northd should generate 2 lflow with ip4 tcp_reset and ip6
> > > tcp_reset actions.
> > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> > > sort], [0], [dnl
> > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > +match=(ip4 && tcp && (inport == @pg0 && ip)), dnl
> > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src;
tcp_reset {
> > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > +match=(ip6 && tcp && (inport == @pg0 && ip)), dnl
> > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src;
tcp_reset {
> > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > +])
> > > > +
> > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> > > sort], [0], [dnl
> > > > +])
> > > > +
> > > > +# Set l3-protocol to ip and l4-protocol=udp for the reject ACL.
> > > > +ovn-nbctl --wait=sb set ACL . options:l3-protocol=ip
> > > > +ovn-nbctl --wait=sb set ACL . options:l4-protocol=udp
> > > > +
> > > > +# ovn-northd should generate 2 lflows (udp match) with icmp4 and
icmp6
> > > actions.
> > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> > > sort], [0], [dnl
> > > > +])
> > > > +
> > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> > > sort], [0], [dnl
> > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > +match=(ip4 && (inport == @pg0 && ip) && udp), dnl
> > > > +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > +match=(ip6 && (inport == @pg0 && ip) && udp), dnl
> > > > +action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <-> ip6.src;
> > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > +])
> > > > +
> > > > +# Add an ACL with allow-related
> > > > +ovn-nbctl --wait=sb acl-add pg0 to-lport 1000 "ip" allow-related
> > > > +
> > > > +# ovn-northd should generate 4 lflow with 2 icmp4 and 2 icmp6
actions.
> > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> > > sort], [0], [dnl
> > > > +])
> > > > +
> > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> > > sort], [0], [dnl
> > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > +match=((reg0[[10]] == 1) && ip4 && (inport == @pg0 && ip) && udp),
dnl\
> > > > +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; icmp4 {
eth.dst
> > > <-> eth.src; ip4.dst <-> ip4.src; outport <-> inport;
> > > next(pipeline=ingress,table=20); };)
> > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > +match=((reg0[[10]] == 1) && ip6 && (inport == @pg0 && ip) && udp),
dnl
> > > > +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; icmp6 {
eth.dst
> > > <-> eth.src; ip6.dst <-> ip6.src; outport <-> inport;
> > > next(pipeline=ingress,table=20); };)
> > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > +match=((reg0[[9]] == 1) && ip4 && (inport == @pg0 && ip) && udp),
dnl
> > > > +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > +match=((reg0[[9]] == 1) && ip6 && (inport == @pg0 && ip) && udp),
dnl
> > > > +action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <-> ip6.src;
> > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > +])
> > > > +
> > > > +# Set l3-protocol to ip and l4-protocol=tcp for the reject ACL.
> > > > +rej_acl=$(ovn-nbctl --bare --columns _uuid  find ACL action=reject)
> > > > +ovn-nbctl --wait=sb set ACL $rej_acl options:l3-protocol=ip
> > > > +ovn-nbctl --wait=sb set ACL $rej_acl options:l4-protocol=tcp
> > > > +
> > > > +# ovn-northd should generate 4 lflow with 2 ip4 tcp_reset and 2 ip6
> > > tcp_reset actions.
> > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> > > sort], [0], [dnl
> > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > +match=((reg0[[10]] == 1) && ip4 && tcp && (inport == @pg0 && ip)),
dnl
> > > > +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; eth.dst <->
> > > eth.src; ip4.dst <-> ip4.src; tcp_reset { outport <-> inport;
> > > next(pipeline=ingress,table=20); };)
> > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > +match=((reg0[[10]] == 1) && ip6 && tcp && (inport == @pg0 && ip)),
dnl
> > > > +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; eth.dst <->
> > > eth.src; ip6.dst <-> ip6.src; tcp_reset { outport <-> inport;
> > > next(pipeline=ingress,table=20); };)
> > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > +match=((reg0[[9]] == 1) && ip4 && tcp && (inport == @pg0 && ip)),
dnl
> > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src;
tcp_reset {
> > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > +match=((reg0[[9]] == 1) && ip6 && tcp && (inport == @pg0 && ip)),
dnl
> > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src;
tcp_reset {
> > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > +])
> > > > +
> > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> > > sort], [0], [dnl
> > > > +])
> > > > +
> > > > +
> > > > +# Clear l3-protocol and set l4-protocol to udp
> > > > +ovn-nbctl --wait=sb remove ACL $rej_acl options l3-protocol
> > > > +ovn-nbctl --wait=sb set ACL $rej_acl options:l4-protocol=udp
> > > > +
> > > > +# ovn-northd should generate 4 lflows (with udp match) with 2
icmp4 and
> > > 2 icmp6 actions.
> > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> > > sort], [0], [dnl
> > > > +])
> > > > +
> > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> > > sort], [0], [dnl
> > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > +match=((reg0[[10]] == 1) && ip4 && (inport == @pg0 && ip) && udp),
dnl\
> > > > +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; icmp4 {
eth.dst
> > > <-> eth.src; ip4.dst <-> ip4.src; outport <-> inport;
> > > next(pipeline=ingress,table=20); };)
> > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > +match=((reg0[[10]] == 1) && ip6 && (inport == @pg0 && ip) && udp),
dnl
> > > > +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; icmp6 {
eth.dst
> > > <-> eth.src; ip6.dst <-> ip6.src; outport <-> inport;
> > > next(pipeline=ingress,table=20); };)
> > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > +match=((reg0[[9]] == 1) && ip4 && (inport == @pg0 && ip) && udp),
dnl
> > > > +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > +match=((reg0[[9]] == 1) && ip6 && (inport == @pg0 && ip) && udp),
dnl
> > > > +action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <-> ip6.src;
> > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > +])
> > > > +
> > > > +# Clear l3-protocol and set l4-protocol to tcp
> > > > +ovn-nbctl --wait=sb remove ACL $rej_acl options l3-protocol
> > > > +ovn-nbctl --wait=sb set ACL $rej_acl options:l4-protocol=tcp
> > > > +
> > > > +# ovn-northd should generate 4 lflow with 2 ip4 tcp_reset and 2 ip6
> > > tcp_reset actions.
> > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> > > sort], [0], [dnl
> > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > +match=((reg0[[10]] == 1) && ip4 && tcp && (inport == @pg0 && ip)),
dnl
> > > > +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; eth.dst <->
> > > eth.src; ip4.dst <-> ip4.src; tcp_reset { outport <-> inport;
> > > next(pipeline=ingress,table=20); };)
> > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > +match=((reg0[[10]] == 1) && ip6 && tcp && (inport == @pg0 && ip)),
dnl
> > > > +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; eth.dst <->
> > > eth.src; ip6.dst <-> ip6.src; tcp_reset { outport <-> inport;
> > > next(pipeline=ingress,table=20); };)
> > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > +match=((reg0[[9]] == 1) && ip4 && tcp && (inport == @pg0 && ip)),
dnl
> > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src;
tcp_reset {
> > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > +match=((reg0[[9]] == 1) && ip6 && tcp && (inport == @pg0 && ip)),
dnl
> > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src;
tcp_reset {
> > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > +])
> > > > +
> > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> > > sort], [0], [dnl
> > > > +])
> > > > +
> > > > +AT_CLEANUP
> > > > --
> > > > 2.26.2
> > > >
> > > > _______________________________________________
> > > > dev mailing list
> > > > dev@openvswitch.org
> > > > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> > > _______________________________________________
> > > dev mailing list
> > > dev@openvswitch.org
> > > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> > >
Numan Siddique Sept. 28, 2020, 5:03 p.m. UTC | #5
On Mon, Sep 28, 2020 at 9:41 PM Han Zhou <hzhou@ovn.org> wrote:
>
> On Mon, Sep 28, 2020 at 2:21 AM Numan Siddique <numans@ovn.org> wrote:
> >
> > On Mon, Sep 28, 2020 at 2:14 PM Numan Siddique <numans@ovn.org> wrote:
> > >
> > > On Mon, Sep 28, 2020 at 5:57 AM Han Zhou <hzhou@ovn.org> wrote:
> > > >
> > > > Thanks Numan. I have some comments on the solution.
> > > >
> > > > On Thu, Sep 24, 2020 at 9:50 AM <numans@ovn.org> wrote:
> > > > >
> > > > > From: Numan Siddique <numans@ovn.org>
> > > > >
> > > > > ovn-northd adds below lflows for a reject ACL with a match - M
> > > > >
> > > > > match = (ip4 && tcp && 'M') action = tcp_reject{}
> > > > > match = (ip6 && tcp && 'M') action = tcp_reject{}
> > > > > match = (ip4 && 'M') action = icmp4{}
> > > > > match = (ip6 && 'M') action = icmp6{}
> > > > >
> > > > > This approach has a couple of problems:
> > > > >    - ovn-controller can reject the lflows if there are invalid
> matches.
> > > > >      Eg. If match 'M' is - 'ip4 && udp'.
> > > > >
> > > > >    - In a large scale deployment, this could result in lot of
> invalid
> > > > >      logical flows and increase the size of the SB DB.
> > > > >
> > > > > This patch addresses this problem by providing an option to the CMS
> > > > > to specify the l3 protocol and l4 protocol as hints. A new column
> > > > > 'options' is added to the ACL table with the options -
> > > > > options:l3-protocol and options:l4-protocol.
> > > > >
> > > > > ovn-northd will now generate only the required lflows for the reject
> > > > > ACL. If no options are set, then it falls back to the default
> scenario.
> > > > >
> > > >
> > > > Thanks Numan. I believe the solution should work. However, it seems a
> > > > little confusing for the end user about how to use the hint field. I
> think
> > > > maybe we should try to avoid the extra field if possible. Here is my
> > > > proposal:
> > > >
> > >
> > > Thanks Han for the comments. I do agree if we can avoid the extra
> option.
> > >
> > > > 1. For the tcp v.s. udp check, since we don't want northd to parse the
> > > > match condition, we will have to rely on users input. However,
> instead of a
> > > > hint, I think it is better to let the user explicitly specify in the
> action
> > > > field. E.g. "reject" means sending icmp, and "tcp_reject" means
> sending tcp
> > > > reset if the traffic is tcp and otherwise sending icmp. So, if the
> user
> > > > adds an ACL with match include "udp", they will naturally use "reject"
> > > > instead of "tcp_reject" because they know it is not possible for a TCP
> > > > packet to hit this ACL. If they add an ACL that tcp traffic is
> expected to
> > > > match (either TCP only, or general IP traffic), they can use
> "tcp_reject".
> > > > Would this be more straightforward than an extra "hint" field?
> > > >
> > >
> > > I have few comments here.
> > > - If we consider "reject" = "icmp", then we will be breaking the
> > > backward compatibility.
> > >     Right now, for a reject action, ovn-northd would send tcp_reset
> > > for tcp traffic and icmp reply or other traffic.
> > >     After your proposed approach, the  behaviour would change ? Is
> > > that OK ? Because existing test cases would fail.
> > >     The proposed patch handles the backward compatibility. Although
> > > I'm not sure if it is really required in this case.
> > >
> > > -  For  a tcp_reject action, if the match has "tcp", then we will end
> > > up adding a logical flow with the icmp action, which is
> > >     really not required. Or do you mean, pinctrl would take the
> > > decision while handling the "tcp_reject" action to either
> > >     send tcp reset packet or icmp packet ? If so, we can probably just
> > > have one action - "reject". Please see below.
> > >
> > > > 2. For ip4 v.s. ip6, the idea is to support generic ovn logical flow
> > > > actions "tcp_reject" and "icmp" in ovn-controller, which can handle
> both
> > > > ipv4 and ipv6. If this can be supported, the logical flows only needs
> to be:
> > > > match = (tcp && 'M') action = tcp_reject{}
> > > > match = ('M') action = icmp{}
> > > >
> > >
> > > Ok. In this case we cannot support inner actions unless they are
> > > independent of the ip version.
> > > i.e we can't support - tcp_reject { eth.dst <-> eth.src; ip4.dst <->
> > > ip4.src; } or
> > > tcp_reject {eth.dst <-> eth.src; ip6.dst <-> ip6.src;} in one logical
> > > flow. ovn-northd has to generate
> > > 2 logical flows.
> > >   - ip4 && 'M', action = tcp_reject { eth.dst <-> eth.src; ip4.dst <->
> > > ip4.src; }
> > >   - ip6 && 'M', action = tcp_reject { eth.dst <-> eth.src; ip6.dst <->
> > > ip6.src; }
> > >
> > >
> > > Right now for one reject ACL we add the below lflows
> > >    - ip4 && tcp && 'M', action = tcp_reset { eth.dst <-> eth.src;
> > > ip4.dst <-> ip4.src; outport <-> inport;}
> > >    - ip6 && tcp && 'M', action = tcp_reset { eth.dst <-> eth.src;
> > > ip6.dst <-> ip6.src; outport <-> inport;}
> > >    - ip4 && 'M'            , action = icmp4 {eth.dst <-> eth.src;
> > > ip4.dst <-> ip4.src; outport <-> inport;}
> > >    - ip6 && 'M'            , action = icmp6 {eth.dst <-> eth.src;
> > > ip6.dst <-> ip6.src; outport <-> inport;}
> > >
> > > I have another idea based on your suggestions here: How about we have
> > > a new action - reject{} which will be used
> > > for all reject ACLs and this will
> > >     - send tcp reset packet if the packet is tcp (pinctrl would take
> > > the decision)
> > >     - send icmp packet otherwise.
> > >
> > > We can add the logical flow for a reject ACL in 2 ways
> > >
> > > 1. match = M, action = reject { eth.dst <-> eth.src, outport <->
> inport;}
> > >
> > > OR
> > >
> > > 2. match = M, action = reject { eth.dst <-> eth.src, ip.src <->
> > > ip.dst; outport <-> inport;}
> > >
> > > In (1), pinctrl while handling the reject action, will swap the ip src
> > > with ip dst (i.e ip4.dst <-> ip4.src or ip6.dst <-> ip6.src }.
> > >
> > > In (2), we will add 2 new OVN fields "ip.src" and "ip.dst" (just like
> > > we have icmp4.frag_mtu now) and the action - ip.src <-> ip.dst
> > > will result in another controller action which will swap the ip src
> > > and ip dst fields (just like how "icmp4.frag_mtu = 1400" would result
> > > into a controller action).
> > >
> > > Even though (1) will work, it's not transparent. I would personally
> > > prefer (2) and I think it's ok if the reject ACL would result in 2 OF
> > > controller actions.
> > >
> > > With this we will have just one logical flow (instead of 4 ) and there
> > > will be no changes on the CMS side.
> > >
>
> Yes, this would be even better.

Thanks. I will work on it.


>
> > > What do you think ?
> > >
> > > Thanks
> > > Numan
> > >
> > >
> > > > I haven't thought through all the details but it seems doable. Did
> you have
> > > > any thoughts on this? (If it proves to be unreasonable I am ok with
> current
> > > > "hint" approach)
> > > >
> > > > In addition, I found the existing TCP reject handling has a bug which
> > > > messes up the user defined ACL priorities. I just posted a patch:
> > > >
> https://patchwork.ozlabs.org/project/ovn/patch/20200928000904.858685-1-hzhou@ovn.org/
> > > > It is not directly related to this patch but it would be great if you
> could
> > > > take a look.
> >
> > Sure.
> >
> With only one lflow, my above fix is not needed. (I am not sure about
> backporting to old branches.)

I thought about it. Since this issue has been there for a long time
and no one reported it. Probably its ok to not
address this for the branches ?

We could also take the approach of accepting your patch and backport
to the branches. And we can revert it when I submit the patch
with the proposed new approach here.

Thanks
Numan

>
> > Thanks
> > Numan
> >
> > > >
> > > > Thanks,
> > > > Han
> > > >
> > > >
> > > > > Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=1876990
> > > > > Reported-by: Ilya Maximets <i.maximets@ovn.org>
> > > > > Signed-off-by: Numan Siddique <numans@ovn.org>
> > > > > ---
> > > > >  northd/ovn-northd.c | 194 ++++++++++++++++++++----------
> > > > >  ovn-nb.ovsschema    |   7 +-
> > > > >  ovn-nb.xml          |  43 +++++++
> > > > >  tests/ovn-northd.at | 285
> ++++++++++++++++++++++++++++++++++++++++++++
> > > > >  4 files changed, 461 insertions(+), 68 deletions(-)
> > > > >
> > > > > diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
> > > > > index 3324c9e81d..d167137e07 100644
> > > > > --- a/northd/ovn-northd.c
> > > > > +++ b/northd/ovn-northd.c
> > > > > @@ -5372,73 +5372,135 @@ build_reject_acl_rules(struct ovn_datapath
> *od,
> > > > struct hmap *lflows,
> > > > >      struct ds actions = DS_EMPTY_INITIALIZER;
> > > > >      bool ingress = (stage == S_SWITCH_IN_ACL);
> > > > >
> > > > > -    /* TCP */
> > > > > -    build_acl_log(&actions, acl);
> > > > > -    if (extra_match->length > 0) {
> > > > > -        ds_put_format(&match, "(%s) && ", extra_match->string);
> > > > > -    }
> > > > > -    ds_put_format(&match, "ip4 && tcp && (%s)", acl->match);
> > > > > -    ds_put_format(&actions, "reg0 = 0; "
> > > > > -                  "eth.dst <-> eth.src; ip4.dst <-> ip4.src; "
> > > > > -                  "tcp_reset { outport <-> inport; %s };",
> > > > > -                  ingress ? "next(pipeline=egress,table=5);"
> > > > > -                          : "next(pipeline=ingress,table=20);");
> > > > > -    ovn_lflow_add_with_hint(lflows, od, stage,
> > > > > -                            acl->priority + OVN_ACL_PRI_OFFSET +
> 10,
> > > > > -                            ds_cstr(&match), ds_cstr(&actions),
> > > > stage_hint);
> > > > > -    ds_clear(&match);
> > > > > -    ds_clear(&actions);
> > > > > -    build_acl_log(&actions, acl);
> > > > > -    if (extra_match->length > 0) {
> > > > > -        ds_put_format(&match, "(%s) && ", extra_match->string);
> > > > > -    }
> > > > > -    ds_put_format(&match, "ip6 && tcp && (%s)", acl->match);
> > > > > -    ds_put_format(&actions, "reg0 = 0; "
> > > > > -                  "eth.dst <-> eth.src; ip6.dst <-> ip6.src; "
> > > > > -                  "tcp_reset { outport <-> inport; %s };",
> > > > > -                  ingress ? "next(pipeline=egress,table=5);"
> > > > > -                          : "next(pipeline=ingress,table=20);");
> > > > > -    ovn_lflow_add_with_hint(lflows, od, stage,
> > > > > -                            acl->priority + OVN_ACL_PRI_OFFSET +
> 10,
> > > > > -                            ds_cstr(&match), ds_cstr(&actions),
> > > > stage_hint);
> > > > > +    bool is_ip4 = true;
> > > > > +    bool is_ip6 = true;
> > > > > +    bool tcp_reset = true;
> > > > > +    bool icmp_reset = true;
> > > > > +    bool is_udp = false;
> > > > > +
> > > > > +    const char *l3_protocol = smap_get(&acl->options,
> "l3-protocol");
> > > > > +    if (l3_protocol) {
> > > > > +        if (!strcasecmp(l3_protocol, "ip")) {
> > > > > +            is_ip4 = true;
> > > > > +            is_ip6 = true;
> > > > > +        } else if (!strcasecmp(l3_protocol, "ip4")) {
> > > > > +            is_ip4 = true;
> > > > > +            is_ip6 = false;
> > > > > +        } else if (!strcasecmp(l3_protocol, "ip6")) {
> > > > > +            is_ip6 = true;
> > > > > +            is_ip4 = false;
> > > > > +        }
> > > > > +    }
> > > > > +
> > > > > +    const char *l4_protocol = smap_get(&acl->options,
> "l4-protocol");
> > > > > +    if (l4_protocol) {
> > > > > +        if (!strcasecmp(l4_protocol, "tcp")) {
> > > > > +            tcp_reset = true;
> > > > > +            icmp_reset = false;
> > > > > +        } else if (!strcasecmp(l4_protocol, "udp")) {
> > > > > +            tcp_reset = false;
> > > > > +            is_udp = true;
> > > > > +        } else {
> > > > > +            tcp_reset = false;
> > > > > +        }
> > > > > +    }
> > > > >
> > > > > -    /* IP traffic */
> > > > > -    ds_clear(&match);
> > > > > -    ds_clear(&actions);
> > > > > -    build_acl_log(&actions, acl);
> > > > > -    if (extra_match->length > 0) {
> > > > > -        ds_put_format(&match, "(%s) && ", extra_match->string);
> > > > > -    }
> > > > > -    ds_put_format(&match, "ip4 && (%s)", acl->match);
> > > > > -    if (extra_actions->length > 0) {
> > > > > -        ds_put_format(&actions, "%s ", extra_actions->string);
> > > > > -    }
> > > > > -    ds_put_format(&actions, "reg0 = 0; "
> > > > > -                  "icmp4 { eth.dst <-> eth.src; ip4.dst <->
> ip4.src; "
> > > > > -                  "outport <-> inport; %s };",
> > > > > -                  ingress ? "next(pipeline=egress,table=5);"
> > > > > -                          : "next(pipeline=ingress,table=20);");
> > > > > -    ovn_lflow_add_with_hint(lflows, od, stage,
> > > > > -                            acl->priority + OVN_ACL_PRI_OFFSET,
> > > > > -                            ds_cstr(&match), ds_cstr(&actions),
> > > > stage_hint);
> > > > > -    ds_clear(&match);
> > > > > -    ds_clear(&actions);
> > > > > -    build_acl_log(&actions, acl);
> > > > > -    if (extra_match->length > 0) {
> > > > > -        ds_put_format(&match, "(%s) && ", extra_match->string);
> > > > > -    }
> > > > > -    ds_put_format(&match, "ip6 && (%s)", acl->match);
> > > > > -    if (extra_actions->length > 0) {
> > > > > -        ds_put_format(&actions, "%s ", extra_actions->string);
> > > > > -    }
> > > > > -    ds_put_format(&actions, "reg0 = 0; icmp6 { "
> > > > > -                  "eth.dst <-> eth.src; ip6.dst <-> ip6.src; "
> > > > > -                  "outport <-> inport; %s };",
> > > > > -                  ingress ? "next(pipeline=egress,table=5);"
> > > > > -                          : "next(pipeline=ingress,table=20);");
> > > > > -    ovn_lflow_add_with_hint(lflows, od, stage,
> > > > > -                            acl->priority + OVN_ACL_PRI_OFFSET,
> > > > > -                            ds_cstr(&match), ds_cstr(&actions),
> > > > stage_hint);
> > > > > +    if (is_ip4) {
> > > > > +        if (tcp_reset) {
> > > > > +            build_acl_log(&actions, acl);
> > > > > +            if (extra_match->length > 0) {
> > > > > +                ds_put_format(&match, "(%s) && ",
> extra_match->string);
> > > > > +            }
> > > > > +            ds_put_format(&match, "ip4 && tcp && (%s)",
> acl->match);
> > > > > +            if (extra_actions->length > 0) {
> > > > > +                ds_put_format(&actions, "%s ",
> extra_actions->string);
> > > > > +            }
> > > > > +            ds_put_format(&actions, "reg0 = 0; "
> > > > > +                          "eth.dst <-> eth.src; ip4.dst <->
> ip4.src; "
> > > > > +                          "tcp_reset { outport <-> inport; %s };",
> > > > > +                          ingress ?
> "next(pipeline=egress,table=5);"
> > > > > +                                  :
> "next(pipeline=ingress,table=20);");
> > > > > +            ovn_lflow_add_with_hint(lflows, od, stage,
> > > > > +                                    acl->priority +
> OVN_ACL_PRI_OFFSET +
> > > > 10,
> > > > > +                                    ds_cstr(&match),
> ds_cstr(&actions),
> > > > > +                                    stage_hint);
> > > > > +        }
> > > > > +
> > > > > +        if (icmp_reset) {
> > > > > +            ds_clear(&match);
> > > > > +            ds_clear(&actions);
> > > > > +            build_acl_log(&actions, acl);
> > > > > +            if (extra_match->length > 0) {
> > > > > +                ds_put_format(&match, "(%s) && ",
> extra_match->string);
> > > > > +            }
> > > > > +            ds_put_format(&match, "ip4 && (%s)", acl->match);
> > > > > +            if (is_udp) {
> > > > > +                ds_put_cstr(&match, " && udp");
> > > > > +            }
> > > > > +            if (extra_actions->length > 0) {
> > > > > +                ds_put_format(&actions, "%s ",
> extra_actions->string);
> > > > > +            }
> > > > > +            ds_put_format(&actions, "reg0 = 0; "
> > > > > +                          "icmp4 { eth.dst <-> eth.src; ip4.dst <->
> > > > ip4.src; "
> > > > > +                          "outport <-> inport; %s };",
> > > > > +                          ingress ?
> "next(pipeline=egress,table=5);"
> > > > > +                                  :
> "next(pipeline=ingress,table=20);");
> > > > > +            ovn_lflow_add_with_hint(lflows, od, stage,
> > > > > +                                    acl->priority +
> OVN_ACL_PRI_OFFSET,
> > > > > +                                    ds_cstr(&match),
> ds_cstr(&actions),
> > > > > +                                    stage_hint);
> > > > > +        }
> > > > > +    }
> > > > > +
> > > > > +    if (is_ip6) {
> > > > > +        if (tcp_reset) {
> > > > > +            ds_clear(&match);
> > > > > +            ds_clear(&actions);
> > > > > +            build_acl_log(&actions, acl);
> > > > > +            if (extra_match->length > 0) {
> > > > > +                ds_put_format(&match, "(%s) && ",
> extra_match->string);
> > > > > +            }
> > > > > +            ds_put_format(&match, "ip6 && tcp && (%s)",
> acl->match);
> > > > > +            if (extra_actions->length > 0) {
> > > > > +                ds_put_format(&actions, "%s ",
> extra_actions->string);
> > > > > +            }
> > > > > +            ds_put_format(&actions, "reg0 = 0; "
> > > > > +                          "eth.dst <-> eth.src; ip6.dst <->
> ip6.src; "
> > > > > +                          "tcp_reset { outport <-> inport; %s };",
> > > > > +                          ingress ?
> "next(pipeline=egress,table=5);"
> > > > > +                                  :
> "next(pipeline=ingress,table=20);");
> > > > > +            ovn_lflow_add_with_hint(lflows, od, stage,
> > > > > +                                    acl->priority +
> OVN_ACL_PRI_OFFSET +
> > > > 10,
> > > > > +                                    ds_cstr(&match),
> ds_cstr(&actions),
> > > > > +                                    stage_hint);
> > > > > +        }
> > > > > +
> > > > > +        if (icmp_reset) {
> > > > > +            ds_clear(&match);
> > > > > +            ds_clear(&actions);
> > > > > +            build_acl_log(&actions, acl);
> > > > > +            if (extra_match->length > 0) {
> > > > > +                ds_put_format(&match, "(%s) && ",
> extra_match->string);
> > > > > +            }
> > > > > +            ds_put_format(&match, "ip6 && (%s)", acl->match);
> > > > > +            if (is_udp) {
> > > > > +                ds_put_cstr(&match, " && udp");
> > > > > +            }
> > > > > +
> > > > > +            if (extra_actions->length > 0) {
> > > > > +                ds_put_format(&actions, "%s ",
> extra_actions->string);
> > > > > +            }
> > > > > +            ds_put_format(&actions, "reg0 = 0; icmp6 { "
> > > > > +                          "eth.dst <-> eth.src; ip6.dst <->
> ip6.src; "
> > > > > +                          "outport <-> inport; %s };",
> > > > > +                          ingress ?
> "next(pipeline=egress,table=5);"
> > > > > +                                :
> "next(pipeline=ingress,table=20);");
> > > > > +            ovn_lflow_add_with_hint(lflows, od, stage,
> > > > > +                                    acl->priority +
> OVN_ACL_PRI_OFFSET,
> > > > > +                                    ds_cstr(&match),
> ds_cstr(&actions),
> > > > > +                                    stage_hint);
> > > > > +        }
> > > > > +    }
> > > > >
> > > > >      ds_destroy(&match);
> > > > >      ds_destroy(&actions);
> > > > > diff --git a/ovn-nb.ovsschema b/ovn-nb.ovsschema
> > > > > index 092322ab2c..00f1c7cd4b 100644
> > > > > --- a/ovn-nb.ovsschema
> > > > > +++ b/ovn-nb.ovsschema
> > > > > @@ -1,7 +1,7 @@
> > > > >  {
> > > > >      "name": "OVN_Northbound",
> > > > > -    "version": "5.27.0",
> > > > > -    "cksum": "3507518247 26773",
> > > > > +    "version": "5.28.0",
> > > > > +    "cksum": "699859908 26928",
> > > > >      "tables": {
> > > > >          "NB_Global": {
> > > > >              "columns": {
> > > > > @@ -225,6 +225,9 @@
> > > > >                                                          "debug"]]},
> > > > >                                        "min": 0, "max": 1}},
> > > > >                  "meter": {"type": {"key": "string", "min": 0,
> "max": 1}},
> > > > > +                "options": {
> > > > > +                    "type": {"key": "string", "value": "string",
> > > > > +                             "min": 0, "max": "unlimited"}},
> > > > >                  "external_ids": {
> > > > >                      "type": {"key": "string", "value": "string",
> > > > >                               "min": 0, "max": "unlimited"}}},
> > > > > diff --git a/ovn-nb.xml b/ovn-nb.xml
> > > > > index 86195af341..2c3497e2ae 100644
> > > > > --- a/ovn-nb.xml
> > > > > +++ b/ovn-nb.xml
> > > > > @@ -1721,6 +1721,49 @@
> > > > >        </ul>
> > > > >      </column>
> > > > >
> > > > > +    <group title="Common options">
> > > > > +      <column name="options">
> > > > > +        This column provides general key/value settings. The
> supported
> > > > > +        options are described individually below.
> > > > > +      </column>
> > > > > +
> > > > > +      <group title="Options for providing protocol hints for reject
> > > > ACL.">
> > > > > +        <p>
> > > > > +          The ACL match specified in the
> > > > > +          <ref column="match" table="ACL" db="OVN_Northbound"/>
> column is
> > > > > +          opaque to <code>OVN</code> and <code>ovn-northd</code>
> doesn't
> > > > > +          look into the match fields.
> > > > > +          These options can be specified by CMS to provide hints to
> > > > > +          <code>OVN</code> about the L3 and L4 protocol matches
> for the
> > > > reject
> > > > > +          ACL. <code>ovn-northd</code> uses these options if set
> to use
> > > > > +          appropriate actions when generating logical flows.
> > > > > +        </p>
> > > > > +
> > > > > +        <p>
> > > > > +          If these options are not set, then
> <code>ovn-northd</code>
> > > > assumes
> > > > > +          the reject ACL applies to IPv4, IPv6 and TCP packets.
> > > > > +        </p>
> > > > > +
> > > > > +        <column name="options" key="l3-protocol">
> > > > > +          The possible values are <code>ip</code>,
> <code>ip4</code> and
> > > > > +          <code>ip6</code>. If the value is <code>ip</code>, it
> means the
> > > > > +          reject ACL action applies to both IPv4 and IPv6 packets.
> If the
> > > > > +          value is <code>ip4</code>, it means the reject ACL action
> > > > applies to
> > > > > +          IPv4 packets and the value <code>ip6</code> applies to
> IPv6
> > > > packets.
> > > > > +        </column>
> > > > > +
> > > > > +        <column name="options" key="l4-protocol">
> > > > > +          <p>
> > > > > +            The possible values are <code>tcp</code> and
> > > > <code>udp</code>.
> > > > > +            If the value is <code>tcp</code>, it means the
> > > > > +            reject ACL action applies to TCP packets. If the
> > > > > +            value is <code>udp</code>, it means the reject ACL
> action
> > > > applies
> > > > > +            to UDP packets.
> > > > > +          </p>
> > > > > +        </column>
> > > > > +      </group>
> > > > > +    </group>
> > > > > +
> > > > >      <group title="Logging">
> > > > >        <p>
> > > > >          These columns control whether and how OVN logs packets that
> > > > match an
> > > > > diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> > > > > index 99a9204f1f..4a30f8ed07 100644
> > > > > --- a/tests/ovn-northd.at
> > > > > +++ b/tests/ovn-northd.at
> > > > > @@ -2010,3 +2010,288 @@ ovn-nbctl --wait=sb set NB_Global .
> > > > options:ignore_lsp_down=true
> > > > >  AT_CHECK([ovn-sbctl lflow-list | grep arp | grep 10\.0\.0\.1], [0],
> > > > [ignore])
> > > > >
> > > > >  AT_CLEANUP
> > > > > +
> > > > > +AT_SETUP([ovn-northd -- ACL reject flows])
> > > > > +ovn_start
> > > > > +
> > > > > +ovn-nbctl ls-add sw0
> > > > > +ovn-nbctl lsp-add sw0 sw0-p1
> > > > > +ovn-nbctl lsp-add sw0 sw0-p2
> > > > > +
> > > > > +ovn-nbctl pg-add pg0 sw0-p1 sw0-p2
> > > > > +ovn-nbctl acl-add pg0 to-lport 1002 "inport == @pg0 && ip" reject
> > > > > +
> > > > > +ovn-nbctl --wait=sb sync
> > > > > +
> > > > > +# If there is a reject ACL wihtout any protocol hints, then
> ovn-northd
> > > > should
> > > > > +# generate lflows with ip4 tcp_reset, icmp4, ip6 tcp_reset and
> icmp6
> > > > actions.
> > > > > +
> > > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> > > > sort], [0], [dnl
> > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > +match=(ip4 && tcp && (inport == @pg0 && ip)), dnl
> > > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> tcp_reset {
> > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > +match=(ip6 && tcp && (inport == @pg0 && ip)), dnl
> > > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src;
> tcp_reset {
> > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > +])
> > > > > +
> > > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> > > > sort], [0], [dnl
> > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > +match=(ip4 && (inport == @pg0 && ip)), dnl
> > > > > +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > +match=(ip6 && (inport == @pg0 && ip)), dnl
> > > > > +action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <-> ip6.src;
> > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > +])
> > > > > +
> > > > > +# Set l3-protocol=ip4 for the reject ACL.
> > > > > +ovn-nbctl --wait=sb set ACL . options:l3-protocol=ip4
> > > > > +
> > > > > +# ovn-northd should generate  2 lflows with ip4 tcp_reset and icmp4
> > > > actions.
> > > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> > > > sort], [0], [dnl
> > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > +match=(ip4 && tcp && (inport == @pg0 && ip)), dnl
> > > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> tcp_reset {
> > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > +])
> > > > > +
> > > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> > > > sort], [0], [dnl
> > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > +match=(ip4 && (inport == @pg0 && ip)), dnl
> > > > > +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > +])
> > > > > +
> > > > > +# Set l4-protocol=tcp for the reject ACL.
> > > > > +ovn-nbctl --wait=sb set ACL . options:l4-protocol=tcp
> > > > > +
> > > > > +# ovn-northd should generate 1 lflow with ip4 tcp_reset action.
> > > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> > > > sort], [0], [dnl
> > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > +match=(ip4 && tcp && (inport == @pg0 && ip)), dnl
> > > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> tcp_reset {
> > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > +])
> > > > > +
> > > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> > > > sort], [0], [dnl
> > > > > +])
> > > > > +
> > > > > +# Set l4-protocol=udp for the reject ACL.
> > > > > +ovn-nbctl --wait=sb set ACL . options:l4-protocol=udp
> > > > > +
> > > > > +# ovn-northd should generate 1 lflow with udp match and icmp4
> action.
> > > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> > > > sort], [0], [dnl
> > > > > +])
> > > > > +
> > > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> > > > sort], [0], [dnl
> > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > +match=(ip4 && (inport == @pg0 && ip) && udp), dnl
> > > > > +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > +])
> > > > > +
> > > > > +# Remove l4-protocol from the reject ACL.
> > > > > +ovn-nbctl --wait=sb remove ACL . options l4-protocol
> > > > > +
> > > > > +# ovn-northd should generate 2 lflow with tcp_reset and icmp4
> action.
> > > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> > > > sort], [0], [dnl
> > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > +match=(ip4 && tcp && (inport == @pg0 && ip)), dnl
> > > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> tcp_reset {
> > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > +])
> > > > > +
> > > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> > > > sort], [0], [dnl
> > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > +match=(ip4 && (inport == @pg0 && ip)), dnl
> > > > > +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > +])
> > > > > +
> > > > > +# Set l3-protocol to ip.
> > > > > +ovn-nbctl --wait=sb set ACL . options:l3-protocol=ip
> > > > > +
> > > > > +# ovn-northd should generate 4 lflow with ip4 tcp_reset, ip6
> tcp_reset,
> > > > icmp4 and icmp6 actions.
> > > > > +
> > > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> > > > sort], [0], [dnl
> > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > +match=(ip4 && tcp && (inport == @pg0 && ip)), dnl
> > > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> tcp_reset {
> > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > +match=(ip6 && tcp && (inport == @pg0 && ip)), dnl
> > > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src;
> tcp_reset {
> > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > +])
> > > > > +
> > > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> > > > sort], [0], [dnl
> > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > +match=(ip4 && (inport == @pg0 && ip)), dnl
> > > > > +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > +match=(ip6 && (inport == @pg0 && ip)), dnl
> > > > > +action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <-> ip6.src;
> > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > +])
> > > > > +
> > > > > +# Set l3-protocol=ip6 for the reject ACL.
> > > > > +ovn-nbctl --wait=sb set ACL . options:l3-protocol=ip6
> > > > > +
> > > > > +# ovn-northd should generate  2 lflows with ip6 tcp_reset and icmp6
> > > > actions.
> > > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> > > > sort], [0], [dnl
> > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > +match=(ip6 && tcp && (inport == @pg0 && ip)), dnl
> > > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src;
> tcp_reset {
> > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > +])
> > > > > +
> > > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> > > > sort], [0], [dnl
> > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > +match=(ip6 && (inport == @pg0 && ip)), dnl
> > > > > +action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <-> ip6.src;
> > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > +])
> > > > > +
> > > > > +# Set l4-protocol=tcp for the reject ACL.
> > > > > +ovn-nbctl --wait=sb set ACL . options:l4-protocol=tcp
> > > > > +
> > > > > +# ovn-northd should generate 1 lflow with ip6 tcp_reset action.
> > > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> > > > sort], [0], [dnl
> > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > +match=(ip6 && tcp && (inport == @pg0 && ip)), dnl
> > > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src;
> tcp_reset {
> > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > +])
> > > > > +
> > > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> > > > sort], [0], [dnl
> > > > > +])
> > > > > +
> > > > > +# Set l4-protocol=udp for the reject ACL.
> > > > > +ovn-nbctl --wait=sb set ACL . options:l4-protocol=udp
> > > > > +
> > > > > +# ovn-northd should generate 1 lflow with udp match and icmp6
> action.
> > > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> > > > sort], [0], [dnl
> > > > > +])
> > > > > +
> > > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> > > > sort], [0], [dnl
> > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > +match=(ip6 && (inport == @pg0 && ip) && udp), dnl
> > > > > +action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <-> ip6.src;
> > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > +])
> > > > > +
> > > > > +# Set l3-protocol to ip and l4-protocol=tcp for the reject ACL.
> > > > > +ovn-nbctl --wait=sb set ACL . options:l3-protocol=ip
> > > > > +ovn-nbctl --wait=sb set ACL . options:l4-protocol=tcp
> > > > > +
> > > > > +# ovn-northd should generate 2 lflow with ip4 tcp_reset and ip6
> > > > tcp_reset actions.
> > > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> > > > sort], [0], [dnl
> > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > +match=(ip4 && tcp && (inport == @pg0 && ip)), dnl
> > > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> tcp_reset {
> > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > +match=(ip6 && tcp && (inport == @pg0 && ip)), dnl
> > > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src;
> tcp_reset {
> > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > +])
> > > > > +
> > > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> > > > sort], [0], [dnl
> > > > > +])
> > > > > +
> > > > > +# Set l3-protocol to ip and l4-protocol=udp for the reject ACL.
> > > > > +ovn-nbctl --wait=sb set ACL . options:l3-protocol=ip
> > > > > +ovn-nbctl --wait=sb set ACL . options:l4-protocol=udp
> > > > > +
> > > > > +# ovn-northd should generate 2 lflows (udp match) with icmp4 and
> icmp6
> > > > actions.
> > > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> > > > sort], [0], [dnl
> > > > > +])
> > > > > +
> > > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> > > > sort], [0], [dnl
> > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > +match=(ip4 && (inport == @pg0 && ip) && udp), dnl
> > > > > +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > +match=(ip6 && (inport == @pg0 && ip) && udp), dnl
> > > > > +action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <-> ip6.src;
> > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > +])
> > > > > +
> > > > > +# Add an ACL with allow-related
> > > > > +ovn-nbctl --wait=sb acl-add pg0 to-lport 1000 "ip" allow-related
> > > > > +
> > > > > +# ovn-northd should generate 4 lflow with 2 icmp4 and 2 icmp6
> actions.
> > > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> > > > sort], [0], [dnl
> > > > > +])
> > > > > +
> > > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> > > > sort], [0], [dnl
> > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > +match=((reg0[[10]] == 1) && ip4 && (inport == @pg0 && ip) && udp),
> dnl\
> > > > > +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; icmp4 {
> eth.dst
> > > > <-> eth.src; ip4.dst <-> ip4.src; outport <-> inport;
> > > > next(pipeline=ingress,table=20); };)
> > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > +match=((reg0[[10]] == 1) && ip6 && (inport == @pg0 && ip) && udp),
> dnl
> > > > > +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; icmp6 {
> eth.dst
> > > > <-> eth.src; ip6.dst <-> ip6.src; outport <-> inport;
> > > > next(pipeline=ingress,table=20); };)
> > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > +match=((reg0[[9]] == 1) && ip4 && (inport == @pg0 && ip) && udp),
> dnl
> > > > > +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > +match=((reg0[[9]] == 1) && ip6 && (inport == @pg0 && ip) && udp),
> dnl
> > > > > +action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <-> ip6.src;
> > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > +])
> > > > > +
> > > > > +# Set l3-protocol to ip and l4-protocol=tcp for the reject ACL.
> > > > > +rej_acl=$(ovn-nbctl --bare --columns _uuid  find ACL action=reject)
> > > > > +ovn-nbctl --wait=sb set ACL $rej_acl options:l3-protocol=ip
> > > > > +ovn-nbctl --wait=sb set ACL $rej_acl options:l4-protocol=tcp
> > > > > +
> > > > > +# ovn-northd should generate 4 lflow with 2 ip4 tcp_reset and 2 ip6
> > > > tcp_reset actions.
> > > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> > > > sort], [0], [dnl
> > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > +match=((reg0[[10]] == 1) && ip4 && tcp && (inport == @pg0 && ip)),
> dnl
> > > > > +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; eth.dst <->
> > > > eth.src; ip4.dst <-> ip4.src; tcp_reset { outport <-> inport;
> > > > next(pipeline=ingress,table=20); };)
> > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > +match=((reg0[[10]] == 1) && ip6 && tcp && (inport == @pg0 && ip)),
> dnl
> > > > > +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; eth.dst <->
> > > > eth.src; ip6.dst <-> ip6.src; tcp_reset { outport <-> inport;
> > > > next(pipeline=ingress,table=20); };)
> > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > +match=((reg0[[9]] == 1) && ip4 && tcp && (inport == @pg0 && ip)),
> dnl
> > > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> tcp_reset {
> > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > +match=((reg0[[9]] == 1) && ip6 && tcp && (inport == @pg0 && ip)),
> dnl
> > > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src;
> tcp_reset {
> > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > +])
> > > > > +
> > > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> > > > sort], [0], [dnl
> > > > > +])
> > > > > +
> > > > > +
> > > > > +# Clear l3-protocol and set l4-protocol to udp
> > > > > +ovn-nbctl --wait=sb remove ACL $rej_acl options l3-protocol
> > > > > +ovn-nbctl --wait=sb set ACL $rej_acl options:l4-protocol=udp
> > > > > +
> > > > > +# ovn-northd should generate 4 lflows (with udp match) with 2
> icmp4 and
> > > > 2 icmp6 actions.
> > > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> > > > sort], [0], [dnl
> > > > > +])
> > > > > +
> > > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> > > > sort], [0], [dnl
> > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > +match=((reg0[[10]] == 1) && ip4 && (inport == @pg0 && ip) && udp),
> dnl\
> > > > > +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; icmp4 {
> eth.dst
> > > > <-> eth.src; ip4.dst <-> ip4.src; outport <-> inport;
> > > > next(pipeline=ingress,table=20); };)
> > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > +match=((reg0[[10]] == 1) && ip6 && (inport == @pg0 && ip) && udp),
> dnl
> > > > > +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; icmp6 {
> eth.dst
> > > > <-> eth.src; ip6.dst <-> ip6.src; outport <-> inport;
> > > > next(pipeline=ingress,table=20); };)
> > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > +match=((reg0[[9]] == 1) && ip4 && (inport == @pg0 && ip) && udp),
> dnl
> > > > > +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > +match=((reg0[[9]] == 1) && ip6 && (inport == @pg0 && ip) && udp),
> dnl
> > > > > +action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <-> ip6.src;
> > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > +])
> > > > > +
> > > > > +# Clear l3-protocol and set l4-protocol to tcp
> > > > > +ovn-nbctl --wait=sb remove ACL $rej_acl options l3-protocol
> > > > > +ovn-nbctl --wait=sb set ACL $rej_acl options:l4-protocol=tcp
> > > > > +
> > > > > +# ovn-northd should generate 4 lflow with 2 ip4 tcp_reset and 2 ip6
> > > > tcp_reset actions.
> > > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" |
> > > > sort], [0], [dnl
> > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > +match=((reg0[[10]] == 1) && ip4 && tcp && (inport == @pg0 && ip)),
> dnl
> > > > > +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; eth.dst <->
> > > > eth.src; ip4.dst <-> ip4.src; tcp_reset { outport <-> inport;
> > > > next(pipeline=ingress,table=20); };)
> > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > +match=((reg0[[10]] == 1) && ip6 && tcp && (inport == @pg0 && ip)),
> dnl
> > > > > +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; eth.dst <->
> > > > eth.src; ip6.dst <-> ip6.src; tcp_reset { outport <-> inport;
> > > > next(pipeline=ingress,table=20); };)
> > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > +match=((reg0[[9]] == 1) && ip4 && tcp && (inport == @pg0 && ip)),
> dnl
> > > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> tcp_reset {
> > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > +match=((reg0[[9]] == 1) && ip6 && tcp && (inport == @pg0 && ip)),
> dnl
> > > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src;
> tcp_reset {
> > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > +])
> > > > > +
> > > > > +AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" |
> > > > sort], [0], [dnl
> > > > > +])
> > > > > +
> > > > > +AT_CLEANUP
> > > > > --
> > > > > 2.26.2
> > > > >
> > > > > _______________________________________________
> > > > > dev mailing list
> > > > > dev@openvswitch.org
> > > > > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> > > > _______________________________________________
> > > > dev mailing list
> > > > dev@openvswitch.org
> > > > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> > > >
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
Han Zhou Sept. 28, 2020, 5:42 p.m. UTC | #6
On Mon, Sep 28, 2020 at 10:03 AM Numan Siddique <numans@ovn.org> wrote:
>
> On Mon, Sep 28, 2020 at 9:41 PM Han Zhou <hzhou@ovn.org> wrote:
> >
> > On Mon, Sep 28, 2020 at 2:21 AM Numan Siddique <numans@ovn.org> wrote:
> > >
> > > On Mon, Sep 28, 2020 at 2:14 PM Numan Siddique <numans@ovn.org> wrote:
> > > >
> > > > On Mon, Sep 28, 2020 at 5:57 AM Han Zhou <hzhou@ovn.org> wrote:
> > > > >
> > > > > Thanks Numan. I have some comments on the solution.
> > > > >
> > > > > On Thu, Sep 24, 2020 at 9:50 AM <numans@ovn.org> wrote:
> > > > > >
> > > > > > From: Numan Siddique <numans@ovn.org>
> > > > > >
> > > > > > ovn-northd adds below lflows for a reject ACL with a match - M
> > > > > >
> > > > > > match = (ip4 && tcp && 'M') action = tcp_reject{}
> > > > > > match = (ip6 && tcp && 'M') action = tcp_reject{}
> > > > > > match = (ip4 && 'M') action = icmp4{}
> > > > > > match = (ip6 && 'M') action = icmp6{}
> > > > > >
> > > > > > This approach has a couple of problems:
> > > > > >    - ovn-controller can reject the lflows if there are invalid
> > matches.
> > > > > >      Eg. If match 'M' is - 'ip4 && udp'.
> > > > > >
> > > > > >    - In a large scale deployment, this could result in lot of
> > invalid
> > > > > >      logical flows and increase the size of the SB DB.
> > > > > >
> > > > > > This patch addresses this problem by providing an option to the
CMS
> > > > > > to specify the l3 protocol and l4 protocol as hints. A new
column
> > > > > > 'options' is added to the ACL table with the options -
> > > > > > options:l3-protocol and options:l4-protocol.
> > > > > >
> > > > > > ovn-northd will now generate only the required lflows for the
reject
> > > > > > ACL. If no options are set, then it falls back to the default
> > scenario.
> > > > > >
> > > > >
> > > > > Thanks Numan. I believe the solution should work. However, it
seems a
> > > > > little confusing for the end user about how to use the hint
field. I
> > think
> > > > > maybe we should try to avoid the extra field if possible. Here is
my
> > > > > proposal:
> > > > >
> > > >
> > > > Thanks Han for the comments. I do agree if we can avoid the extra
> > option.
> > > >
> > > > > 1. For the tcp v.s. udp check, since we don't want northd to
parse the
> > > > > match condition, we will have to rely on users input. However,
> > instead of a
> > > > > hint, I think it is better to let the user explicitly specify in
the
> > action
> > > > > field. E.g. "reject" means sending icmp, and "tcp_reject" means
> > sending tcp
> > > > > reset if the traffic is tcp and otherwise sending icmp. So, if the
> > user
> > > > > adds an ACL with match include "udp", they will naturally use
"reject"
> > > > > instead of "tcp_reject" because they know it is not possible for
a TCP
> > > > > packet to hit this ACL. If they add an ACL that tcp traffic is
> > expected to
> > > > > match (either TCP only, or general IP traffic), they can use
> > "tcp_reject".
> > > > > Would this be more straightforward than an extra "hint" field?
> > > > >
> > > >
> > > > I have few comments here.
> > > > - If we consider "reject" = "icmp", then we will be breaking the
> > > > backward compatibility.
> > > >     Right now, for a reject action, ovn-northd would send tcp_reset
> > > > for tcp traffic and icmp reply or other traffic.
> > > >     After your proposed approach, the  behaviour would change ? Is
> > > > that OK ? Because existing test cases would fail.
> > > >     The proposed patch handles the backward compatibility. Although
> > > > I'm not sure if it is really required in this case.
> > > >
> > > > -  For  a tcp_reject action, if the match has "tcp", then we will
end
> > > > up adding a logical flow with the icmp action, which is
> > > >     really not required. Or do you mean, pinctrl would take the
> > > > decision while handling the "tcp_reject" action to either
> > > >     send tcp reset packet or icmp packet ? If so, we can probably
just
> > > > have one action - "reject". Please see below.
> > > >
> > > > > 2. For ip4 v.s. ip6, the idea is to support generic ovn logical
flow
> > > > > actions "tcp_reject" and "icmp" in ovn-controller, which can
handle
> > both
> > > > > ipv4 and ipv6. If this can be supported, the logical flows only
needs
> > to be:
> > > > > match = (tcp && 'M') action = tcp_reject{}
> > > > > match = ('M') action = icmp{}
> > > > >
> > > >
> > > > Ok. In this case we cannot support inner actions unless they are
> > > > independent of the ip version.
> > > > i.e we can't support - tcp_reject { eth.dst <-> eth.src; ip4.dst <->
> > > > ip4.src; } or
> > > > tcp_reject {eth.dst <-> eth.src; ip6.dst <-> ip6.src;} in one
logical
> > > > flow. ovn-northd has to generate
> > > > 2 logical flows.
> > > >   - ip4 && 'M', action = tcp_reject { eth.dst <-> eth.src; ip4.dst
<->
> > > > ip4.src; }
> > > >   - ip6 && 'M', action = tcp_reject { eth.dst <-> eth.src; ip6.dst
<->
> > > > ip6.src; }
> > > >
> > > >
> > > > Right now for one reject ACL we add the below lflows
> > > >    - ip4 && tcp && 'M', action = tcp_reset { eth.dst <-> eth.src;
> > > > ip4.dst <-> ip4.src; outport <-> inport;}
> > > >    - ip6 && tcp && 'M', action = tcp_reset { eth.dst <-> eth.src;
> > > > ip6.dst <-> ip6.src; outport <-> inport;}
> > > >    - ip4 && 'M'            , action = icmp4 {eth.dst <-> eth.src;
> > > > ip4.dst <-> ip4.src; outport <-> inport;}
> > > >    - ip6 && 'M'            , action = icmp6 {eth.dst <-> eth.src;
> > > > ip6.dst <-> ip6.src; outport <-> inport;}
> > > >
> > > > I have another idea based on your suggestions here: How about we
have
> > > > a new action - reject{} which will be used
> > > > for all reject ACLs and this will
> > > >     - send tcp reset packet if the packet is tcp (pinctrl would take
> > > > the decision)
> > > >     - send icmp packet otherwise.
> > > >
> > > > We can add the logical flow for a reject ACL in 2 ways
> > > >
> > > > 1. match = M, action = reject { eth.dst <-> eth.src, outport <->
> > inport;}
> > > >
> > > > OR
> > > >
> > > > 2. match = M, action = reject { eth.dst <-> eth.src, ip.src <->
> > > > ip.dst; outport <-> inport;}
> > > >
> > > > In (1), pinctrl while handling the reject action, will swap the ip
src
> > > > with ip dst (i.e ip4.dst <-> ip4.src or ip6.dst <-> ip6.src }.
> > > >
> > > > In (2), we will add 2 new OVN fields "ip.src" and "ip.dst" (just
like
> > > > we have icmp4.frag_mtu now) and the action - ip.src <-> ip.dst
> > > > will result in another controller action which will swap the ip src
> > > > and ip dst fields (just like how "icmp4.frag_mtu = 1400" would
result
> > > > into a controller action).
> > > >
> > > > Even though (1) will work, it's not transparent. I would personally
> > > > prefer (2) and I think it's ok if the reject ACL would result in 2
OF
> > > > controller actions.
> > > >
> > > > With this we will have just one logical flow (instead of 4 ) and
there
> > > > will be no changes on the CMS side.
> > > >
> >
> > Yes, this would be even better.
>
> Thanks. I will work on it.
>
>
> >
> > > > What do you think ?
> > > >
> > > > Thanks
> > > > Numan
> > > >
> > > >
> > > > > I haven't thought through all the details but it seems doable. Did
> > you have
> > > > > any thoughts on this? (If it proves to be unreasonable I am ok
with
> > current
> > > > > "hint" approach)
> > > > >
> > > > > In addition, I found the existing TCP reject handling has a bug
which
> > > > > messes up the user defined ACL priorities. I just posted a patch:
> > > > >
> >
https://patchwork.ozlabs.org/project/ovn/patch/20200928000904.858685-1-hzhou@ovn.org/
> > > > > It is not directly related to this patch but it would be great if
you
> > could
> > > > > take a look.
> > >
> > > Sure.
> > >
> > With only one lflow, my above fix is not needed. (I am not sure about
> > backporting to old branches.)
>
> I thought about it. Since this issue has been there for a long time
> and no one reported it. Probably its ok to not
> address this for the branches ?
>
Maybe. It seems this feature is not heavily used. Otherwise such issues
should have been reported. I reported another issue related to "reject" but
didn't have time to debug:
https://mail.openvswitch.org/pipermail/ovs-discuss/2020-September/050711.html

> We could also take the approach of accepting your patch and backport
> to the branches. And we can revert it when I submit the patch
> with the proposed new approach here.
>
I think we can wait for your patch, unless someone complains and requires
an immediate fix. My patch reduces the ACL priority range, which is a risk
of backward compatibility, so I'd rather not apply it if you could work out
the proposed fix here.

> Thanks
> Numan
>
> >
> > > Thanks
> > > Numan
> > >
> > > > >
> > > > > Thanks,
> > > > > Han
> > > > >
> > > > >
> > > > > > Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=1876990
> > > > > > Reported-by: Ilya Maximets <i.maximets@ovn.org>
> > > > > > Signed-off-by: Numan Siddique <numans@ovn.org>
> > > > > > ---
> > > > > >  northd/ovn-northd.c | 194 ++++++++++++++++++++----------
> > > > > >  ovn-nb.ovsschema    |   7 +-
> > > > > >  ovn-nb.xml          |  43 +++++++
> > > > > >  tests/ovn-northd.at | 285
> > ++++++++++++++++++++++++++++++++++++++++++++
> > > > > >  4 files changed, 461 insertions(+), 68 deletions(-)
> > > > > >
> > > > > > diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
> > > > > > index 3324c9e81d..d167137e07 100644
> > > > > > --- a/northd/ovn-northd.c
> > > > > > +++ b/northd/ovn-northd.c
> > > > > > @@ -5372,73 +5372,135 @@ build_reject_acl_rules(struct
ovn_datapath
> > *od,
> > > > > struct hmap *lflows,
> > > > > >      struct ds actions = DS_EMPTY_INITIALIZER;
> > > > > >      bool ingress = (stage == S_SWITCH_IN_ACL);
> > > > > >
> > > > > > -    /* TCP */
> > > > > > -    build_acl_log(&actions, acl);
> > > > > > -    if (extra_match->length > 0) {
> > > > > > -        ds_put_format(&match, "(%s) && ", extra_match->string);
> > > > > > -    }
> > > > > > -    ds_put_format(&match, "ip4 && tcp && (%s)", acl->match);
> > > > > > -    ds_put_format(&actions, "reg0 = 0; "
> > > > > > -                  "eth.dst <-> eth.src; ip4.dst <-> ip4.src; "
> > > > > > -                  "tcp_reset { outport <-> inport; %s };",
> > > > > > -                  ingress ? "next(pipeline=egress,table=5);"
> > > > > > -                          :
"next(pipeline=ingress,table=20);");
> > > > > > -    ovn_lflow_add_with_hint(lflows, od, stage,
> > > > > > -                            acl->priority + OVN_ACL_PRI_OFFSET
+
> > 10,
> > > > > > -                            ds_cstr(&match), ds_cstr(&actions),
> > > > > stage_hint);
> > > > > > -    ds_clear(&match);
> > > > > > -    ds_clear(&actions);
> > > > > > -    build_acl_log(&actions, acl);
> > > > > > -    if (extra_match->length > 0) {
> > > > > > -        ds_put_format(&match, "(%s) && ", extra_match->string);
> > > > > > -    }
> > > > > > -    ds_put_format(&match, "ip6 && tcp && (%s)", acl->match);
> > > > > > -    ds_put_format(&actions, "reg0 = 0; "
> > > > > > -                  "eth.dst <-> eth.src; ip6.dst <-> ip6.src; "
> > > > > > -                  "tcp_reset { outport <-> inport; %s };",
> > > > > > -                  ingress ? "next(pipeline=egress,table=5);"
> > > > > > -                          :
"next(pipeline=ingress,table=20);");
> > > > > > -    ovn_lflow_add_with_hint(lflows, od, stage,
> > > > > > -                            acl->priority + OVN_ACL_PRI_OFFSET
+
> > 10,
> > > > > > -                            ds_cstr(&match), ds_cstr(&actions),
> > > > > stage_hint);
> > > > > > +    bool is_ip4 = true;
> > > > > > +    bool is_ip6 = true;
> > > > > > +    bool tcp_reset = true;
> > > > > > +    bool icmp_reset = true;
> > > > > > +    bool is_udp = false;
> > > > > > +
> > > > > > +    const char *l3_protocol = smap_get(&acl->options,
> > "l3-protocol");
> > > > > > +    if (l3_protocol) {
> > > > > > +        if (!strcasecmp(l3_protocol, "ip")) {
> > > > > > +            is_ip4 = true;
> > > > > > +            is_ip6 = true;
> > > > > > +        } else if (!strcasecmp(l3_protocol, "ip4")) {
> > > > > > +            is_ip4 = true;
> > > > > > +            is_ip6 = false;
> > > > > > +        } else if (!strcasecmp(l3_protocol, "ip6")) {
> > > > > > +            is_ip6 = true;
> > > > > > +            is_ip4 = false;
> > > > > > +        }
> > > > > > +    }
> > > > > > +
> > > > > > +    const char *l4_protocol = smap_get(&acl->options,
> > "l4-protocol");
> > > > > > +    if (l4_protocol) {
> > > > > > +        if (!strcasecmp(l4_protocol, "tcp")) {
> > > > > > +            tcp_reset = true;
> > > > > > +            icmp_reset = false;
> > > > > > +        } else if (!strcasecmp(l4_protocol, "udp")) {
> > > > > > +            tcp_reset = false;
> > > > > > +            is_udp = true;
> > > > > > +        } else {
> > > > > > +            tcp_reset = false;
> > > > > > +        }
> > > > > > +    }
> > > > > >
> > > > > > -    /* IP traffic */
> > > > > > -    ds_clear(&match);
> > > > > > -    ds_clear(&actions);
> > > > > > -    build_acl_log(&actions, acl);
> > > > > > -    if (extra_match->length > 0) {
> > > > > > -        ds_put_format(&match, "(%s) && ", extra_match->string);
> > > > > > -    }
> > > > > > -    ds_put_format(&match, "ip4 && (%s)", acl->match);
> > > > > > -    if (extra_actions->length > 0) {
> > > > > > -        ds_put_format(&actions, "%s ", extra_actions->string);
> > > > > > -    }
> > > > > > -    ds_put_format(&actions, "reg0 = 0; "
> > > > > > -                  "icmp4 { eth.dst <-> eth.src; ip4.dst <->
> > ip4.src; "
> > > > > > -                  "outport <-> inport; %s };",
> > > > > > -                  ingress ? "next(pipeline=egress,table=5);"
> > > > > > -                          :
"next(pipeline=ingress,table=20);");
> > > > > > -    ovn_lflow_add_with_hint(lflows, od, stage,
> > > > > > -                            acl->priority + OVN_ACL_PRI_OFFSET,
> > > > > > -                            ds_cstr(&match), ds_cstr(&actions),
> > > > > stage_hint);
> > > > > > -    ds_clear(&match);
> > > > > > -    ds_clear(&actions);
> > > > > > -    build_acl_log(&actions, acl);
> > > > > > -    if (extra_match->length > 0) {
> > > > > > -        ds_put_format(&match, "(%s) && ", extra_match->string);
> > > > > > -    }
> > > > > > -    ds_put_format(&match, "ip6 && (%s)", acl->match);
> > > > > > -    if (extra_actions->length > 0) {
> > > > > > -        ds_put_format(&actions, "%s ", extra_actions->string);
> > > > > > -    }
> > > > > > -    ds_put_format(&actions, "reg0 = 0; icmp6 { "
> > > > > > -                  "eth.dst <-> eth.src; ip6.dst <-> ip6.src; "
> > > > > > -                  "outport <-> inport; %s };",
> > > > > > -                  ingress ? "next(pipeline=egress,table=5);"
> > > > > > -                          :
"next(pipeline=ingress,table=20);");
> > > > > > -    ovn_lflow_add_with_hint(lflows, od, stage,
> > > > > > -                            acl->priority + OVN_ACL_PRI_OFFSET,
> > > > > > -                            ds_cstr(&match), ds_cstr(&actions),
> > > > > stage_hint);
> > > > > > +    if (is_ip4) {
> > > > > > +        if (tcp_reset) {
> > > > > > +            build_acl_log(&actions, acl);
> > > > > > +            if (extra_match->length > 0) {
> > > > > > +                ds_put_format(&match, "(%s) && ",
> > extra_match->string);
> > > > > > +            }
> > > > > > +            ds_put_format(&match, "ip4 && tcp && (%s)",
> > acl->match);
> > > > > > +            if (extra_actions->length > 0) {
> > > > > > +                ds_put_format(&actions, "%s ",
> > extra_actions->string);
> > > > > > +            }
> > > > > > +            ds_put_format(&actions, "reg0 = 0; "
> > > > > > +                          "eth.dst <-> eth.src; ip4.dst <->
> > ip4.src; "
> > > > > > +                          "tcp_reset { outport <-> inport; %s
};",
> > > > > > +                          ingress ?
> > "next(pipeline=egress,table=5);"
> > > > > > +                                  :
> > "next(pipeline=ingress,table=20);");
> > > > > > +            ovn_lflow_add_with_hint(lflows, od, stage,
> > > > > > +                                    acl->priority +
> > OVN_ACL_PRI_OFFSET +
> > > > > 10,
> > > > > > +                                    ds_cstr(&match),
> > ds_cstr(&actions),
> > > > > > +                                    stage_hint);
> > > > > > +        }
> > > > > > +
> > > > > > +        if (icmp_reset) {
> > > > > > +            ds_clear(&match);
> > > > > > +            ds_clear(&actions);
> > > > > > +            build_acl_log(&actions, acl);
> > > > > > +            if (extra_match->length > 0) {
> > > > > > +                ds_put_format(&match, "(%s) && ",
> > extra_match->string);
> > > > > > +            }
> > > > > > +            ds_put_format(&match, "ip4 && (%s)", acl->match);
> > > > > > +            if (is_udp) {
> > > > > > +                ds_put_cstr(&match, " && udp");
> > > > > > +            }
> > > > > > +            if (extra_actions->length > 0) {
> > > > > > +                ds_put_format(&actions, "%s ",
> > extra_actions->string);
> > > > > > +            }
> > > > > > +            ds_put_format(&actions, "reg0 = 0; "
> > > > > > +                          "icmp4 { eth.dst <-> eth.src;
ip4.dst <->
> > > > > ip4.src; "
> > > > > > +                          "outport <-> inport; %s };",
> > > > > > +                          ingress ?
> > "next(pipeline=egress,table=5);"
> > > > > > +                                  :
> > "next(pipeline=ingress,table=20);");
> > > > > > +            ovn_lflow_add_with_hint(lflows, od, stage,
> > > > > > +                                    acl->priority +
> > OVN_ACL_PRI_OFFSET,
> > > > > > +                                    ds_cstr(&match),
> > ds_cstr(&actions),
> > > > > > +                                    stage_hint);
> > > > > > +        }
> > > > > > +    }
> > > > > > +
> > > > > > +    if (is_ip6) {
> > > > > > +        if (tcp_reset) {
> > > > > > +            ds_clear(&match);
> > > > > > +            ds_clear(&actions);
> > > > > > +            build_acl_log(&actions, acl);
> > > > > > +            if (extra_match->length > 0) {
> > > > > > +                ds_put_format(&match, "(%s) && ",
> > extra_match->string);
> > > > > > +            }
> > > > > > +            ds_put_format(&match, "ip6 && tcp && (%s)",
> > acl->match);
> > > > > > +            if (extra_actions->length > 0) {
> > > > > > +                ds_put_format(&actions, "%s ",
> > extra_actions->string);
> > > > > > +            }
> > > > > > +            ds_put_format(&actions, "reg0 = 0; "
> > > > > > +                          "eth.dst <-> eth.src; ip6.dst <->
> > ip6.src; "
> > > > > > +                          "tcp_reset { outport <-> inport; %s
};",
> > > > > > +                          ingress ?
> > "next(pipeline=egress,table=5);"
> > > > > > +                                  :
> > "next(pipeline=ingress,table=20);");
> > > > > > +            ovn_lflow_add_with_hint(lflows, od, stage,
> > > > > > +                                    acl->priority +
> > OVN_ACL_PRI_OFFSET +
> > > > > 10,
> > > > > > +                                    ds_cstr(&match),
> > ds_cstr(&actions),
> > > > > > +                                    stage_hint);
> > > > > > +        }
> > > > > > +
> > > > > > +        if (icmp_reset) {
> > > > > > +            ds_clear(&match);
> > > > > > +            ds_clear(&actions);
> > > > > > +            build_acl_log(&actions, acl);
> > > > > > +            if (extra_match->length > 0) {
> > > > > > +                ds_put_format(&match, "(%s) && ",
> > extra_match->string);
> > > > > > +            }
> > > > > > +            ds_put_format(&match, "ip6 && (%s)", acl->match);
> > > > > > +            if (is_udp) {
> > > > > > +                ds_put_cstr(&match, " && udp");
> > > > > > +            }
> > > > > > +
> > > > > > +            if (extra_actions->length > 0) {
> > > > > > +                ds_put_format(&actions, "%s ",
> > extra_actions->string);
> > > > > > +            }
> > > > > > +            ds_put_format(&actions, "reg0 = 0; icmp6 { "
> > > > > > +                          "eth.dst <-> eth.src; ip6.dst <->
> > ip6.src; "
> > > > > > +                          "outport <-> inport; %s };",
> > > > > > +                          ingress ?
> > "next(pipeline=egress,table=5);"
> > > > > > +                                :
> > "next(pipeline=ingress,table=20);");
> > > > > > +            ovn_lflow_add_with_hint(lflows, od, stage,
> > > > > > +                                    acl->priority +
> > OVN_ACL_PRI_OFFSET,
> > > > > > +                                    ds_cstr(&match),
> > ds_cstr(&actions),
> > > > > > +                                    stage_hint);
> > > > > > +        }
> > > > > > +    }
> > > > > >
> > > > > >      ds_destroy(&match);
> > > > > >      ds_destroy(&actions);
> > > > > > diff --git a/ovn-nb.ovsschema b/ovn-nb.ovsschema
> > > > > > index 092322ab2c..00f1c7cd4b 100644
> > > > > > --- a/ovn-nb.ovsschema
> > > > > > +++ b/ovn-nb.ovsschema
> > > > > > @@ -1,7 +1,7 @@
> > > > > >  {
> > > > > >      "name": "OVN_Northbound",
> > > > > > -    "version": "5.27.0",
> > > > > > -    "cksum": "3507518247 26773",
> > > > > > +    "version": "5.28.0",
> > > > > > +    "cksum": "699859908 26928",
> > > > > >      "tables": {
> > > > > >          "NB_Global": {
> > > > > >              "columns": {
> > > > > > @@ -225,6 +225,9 @@
> > > > > >
 "debug"]]},
> > > > > >                                        "min": 0, "max": 1}},
> > > > > >                  "meter": {"type": {"key": "string", "min": 0,
> > "max": 1}},
> > > > > > +                "options": {
> > > > > > +                    "type": {"key": "string", "value":
"string",
> > > > > > +                             "min": 0, "max": "unlimited"}},
> > > > > >                  "external_ids": {
> > > > > >                      "type": {"key": "string", "value":
"string",
> > > > > >                               "min": 0, "max": "unlimited"}}},
> > > > > > diff --git a/ovn-nb.xml b/ovn-nb.xml
> > > > > > index 86195af341..2c3497e2ae 100644
> > > > > > --- a/ovn-nb.xml
> > > > > > +++ b/ovn-nb.xml
> > > > > > @@ -1721,6 +1721,49 @@
> > > > > >        </ul>
> > > > > >      </column>
> > > > > >
> > > > > > +    <group title="Common options">
> > > > > > +      <column name="options">
> > > > > > +        This column provides general key/value settings. The
> > supported
> > > > > > +        options are described individually below.
> > > > > > +      </column>
> > > > > > +
> > > > > > +      <group title="Options for providing protocol hints for
reject
> > > > > ACL.">
> > > > > > +        <p>
> > > > > > +          The ACL match specified in the
> > > > > > +          <ref column="match" table="ACL" db="OVN_Northbound"/>
> > column is
> > > > > > +          opaque to <code>OVN</code> and
<code>ovn-northd</code>
> > doesn't
> > > > > > +          look into the match fields.
> > > > > > +          These options can be specified by CMS to provide
hints to
> > > > > > +          <code>OVN</code> about the L3 and L4 protocol matches
> > for the
> > > > > reject
> > > > > > +          ACL. <code>ovn-northd</code> uses these options if
set
> > to use
> > > > > > +          appropriate actions when generating logical flows.
> > > > > > +        </p>
> > > > > > +
> > > > > > +        <p>
> > > > > > +          If these options are not set, then
> > <code>ovn-northd</code>
> > > > > assumes
> > > > > > +          the reject ACL applies to IPv4, IPv6 and TCP packets.
> > > > > > +        </p>
> > > > > > +
> > > > > > +        <column name="options" key="l3-protocol">
> > > > > > +          The possible values are <code>ip</code>,
> > <code>ip4</code> and
> > > > > > +          <code>ip6</code>. If the value is <code>ip</code>, it
> > means the
> > > > > > +          reject ACL action applies to both IPv4 and IPv6
packets.
> > If the
> > > > > > +          value is <code>ip4</code>, it means the reject ACL
action
> > > > > applies to
> > > > > > +          IPv4 packets and the value <code>ip6</code> applies
to
> > IPv6
> > > > > packets.
> > > > > > +        </column>
> > > > > > +
> > > > > > +        <column name="options" key="l4-protocol">
> > > > > > +          <p>
> > > > > > +            The possible values are <code>tcp</code> and
> > > > > <code>udp</code>.
> > > > > > +            If the value is <code>tcp</code>, it means the
> > > > > > +            reject ACL action applies to TCP packets. If the
> > > > > > +            value is <code>udp</code>, it means the reject ACL
> > action
> > > > > applies
> > > > > > +            to UDP packets.
> > > > > > +          </p>
> > > > > > +        </column>
> > > > > > +      </group>
> > > > > > +    </group>
> > > > > > +
> > > > > >      <group title="Logging">
> > > > > >        <p>
> > > > > >          These columns control whether and how OVN logs packets
that
> > > > > match an
> > > > > > diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> > > > > > index 99a9204f1f..4a30f8ed07 100644
> > > > > > --- a/tests/ovn-northd.at
> > > > > > +++ b/tests/ovn-northd.at
> > > > > > @@ -2010,3 +2010,288 @@ ovn-nbctl --wait=sb set NB_Global .
> > > > > options:ignore_lsp_down=true
> > > > > >  AT_CHECK([ovn-sbctl lflow-list | grep arp | grep 10\.0\.0\.1],
[0],
> > > > > [ignore])
> > > > > >
> > > > > >  AT_CLEANUP
> > > > > > +
> > > > > > +AT_SETUP([ovn-northd -- ACL reject flows])
> > > > > > +ovn_start
> > > > > > +
> > > > > > +ovn-nbctl ls-add sw0
> > > > > > +ovn-nbctl lsp-add sw0 sw0-p1
> > > > > > +ovn-nbctl lsp-add sw0 sw0-p2
> > > > > > +
> > > > > > +ovn-nbctl pg-add pg0 sw0-p1 sw0-p2
> > > > > > +ovn-nbctl acl-add pg0 to-lport 1002 "inport == @pg0 && ip"
reject
> > > > > > +
> > > > > > +ovn-nbctl --wait=sb sync
> > > > > > +
> > > > > > +# If there is a reject ACL wihtout any protocol hints, then
> > ovn-northd
> > > > > should
> > > > > > +# generate lflows with ip4 tcp_reset, icmp4, ip6 tcp_reset and
> > icmp6
> > > > > actions.
> > > > > > +
> > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
"ls_out_acl.*priority=2012" |
> > > > > sort], [0], [dnl
> > > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > > +match=(ip4 && tcp && (inport == @pg0 && ip)), dnl
> > > > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> > tcp_reset {
> > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > > +match=(ip6 && tcp && (inport == @pg0 && ip)), dnl
> > > > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src;
> > tcp_reset {
> > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > +])
> > > > > > +
> > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
"ls_out_acl.*priority=2002" |
> > > > > sort], [0], [dnl
> > > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > > +match=(ip4 && (inport == @pg0 && ip)), dnl
> > > > > > +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <->
ip4.src;
> > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > > +match=(ip6 && (inport == @pg0 && ip)), dnl
> > > > > > +action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <->
ip6.src;
> > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > +])
> > > > > > +
> > > > > > +# Set l3-protocol=ip4 for the reject ACL.
> > > > > > +ovn-nbctl --wait=sb set ACL . options:l3-protocol=ip4
> > > > > > +
> > > > > > +# ovn-northd should generate  2 lflows with ip4 tcp_reset and
icmp4
> > > > > actions.
> > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
"ls_out_acl.*priority=2012" |
> > > > > sort], [0], [dnl
> > > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > > +match=(ip4 && tcp && (inport == @pg0 && ip)), dnl
> > > > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> > tcp_reset {
> > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > +])
> > > > > > +
> > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
"ls_out_acl.*priority=2002" |
> > > > > sort], [0], [dnl
> > > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > > +match=(ip4 && (inport == @pg0 && ip)), dnl
> > > > > > +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <->
ip4.src;
> > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > +])
> > > > > > +
> > > > > > +# Set l4-protocol=tcp for the reject ACL.
> > > > > > +ovn-nbctl --wait=sb set ACL . options:l4-protocol=tcp
> > > > > > +
> > > > > > +# ovn-northd should generate 1 lflow with ip4 tcp_reset action.
> > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
"ls_out_acl.*priority=2012" |
> > > > > sort], [0], [dnl
> > > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > > +match=(ip4 && tcp && (inport == @pg0 && ip)), dnl
> > > > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> > tcp_reset {
> > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > +])
> > > > > > +
> > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
"ls_out_acl.*priority=2002" |
> > > > > sort], [0], [dnl
> > > > > > +])
> > > > > > +
> > > > > > +# Set l4-protocol=udp for the reject ACL.
> > > > > > +ovn-nbctl --wait=sb set ACL . options:l4-protocol=udp
> > > > > > +
> > > > > > +# ovn-northd should generate 1 lflow with udp match and icmp4
> > action.
> > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
"ls_out_acl.*priority=2012" |
> > > > > sort], [0], [dnl
> > > > > > +])
> > > > > > +
> > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
"ls_out_acl.*priority=2002" |
> > > > > sort], [0], [dnl
> > > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > > +match=(ip4 && (inport == @pg0 && ip) && udp), dnl
> > > > > > +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <->
ip4.src;
> > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > +])
> > > > > > +
> > > > > > +# Remove l4-protocol from the reject ACL.
> > > > > > +ovn-nbctl --wait=sb remove ACL . options l4-protocol
> > > > > > +
> > > > > > +# ovn-northd should generate 2 lflow with tcp_reset and icmp4
> > action.
> > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
"ls_out_acl.*priority=2012" |
> > > > > sort], [0], [dnl
> > > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > > +match=(ip4 && tcp && (inport == @pg0 && ip)), dnl
> > > > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> > tcp_reset {
> > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > +])
> > > > > > +
> > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
"ls_out_acl.*priority=2002" |
> > > > > sort], [0], [dnl
> > > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > > +match=(ip4 && (inport == @pg0 && ip)), dnl
> > > > > > +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <->
ip4.src;
> > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > +])
> > > > > > +
> > > > > > +# Set l3-protocol to ip.
> > > > > > +ovn-nbctl --wait=sb set ACL . options:l3-protocol=ip
> > > > > > +
> > > > > > +# ovn-northd should generate 4 lflow with ip4 tcp_reset, ip6
> > tcp_reset,
> > > > > icmp4 and icmp6 actions.
> > > > > > +
> > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
"ls_out_acl.*priority=2012" |
> > > > > sort], [0], [dnl
> > > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > > +match=(ip4 && tcp && (inport == @pg0 && ip)), dnl
> > > > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> > tcp_reset {
> > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > > +match=(ip6 && tcp && (inport == @pg0 && ip)), dnl
> > > > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src;
> > tcp_reset {
> > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > +])
> > > > > > +
> > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
"ls_out_acl.*priority=2002" |
> > > > > sort], [0], [dnl
> > > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > > +match=(ip4 && (inport == @pg0 && ip)), dnl
> > > > > > +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <->
ip4.src;
> > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > > +match=(ip6 && (inport == @pg0 && ip)), dnl
> > > > > > +action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <->
ip6.src;
> > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > +])
> > > > > > +
> > > > > > +# Set l3-protocol=ip6 for the reject ACL.
> > > > > > +ovn-nbctl --wait=sb set ACL . options:l3-protocol=ip6
> > > > > > +
> > > > > > +# ovn-northd should generate  2 lflows with ip6 tcp_reset and
icmp6
> > > > > actions.
> > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
"ls_out_acl.*priority=2012" |
> > > > > sort], [0], [dnl
> > > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > > +match=(ip6 && tcp && (inport == @pg0 && ip)), dnl
> > > > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src;
> > tcp_reset {
> > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > +])
> > > > > > +
> > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
"ls_out_acl.*priority=2002" |
> > > > > sort], [0], [dnl
> > > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > > +match=(ip6 && (inport == @pg0 && ip)), dnl
> > > > > > +action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <->
ip6.src;
> > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > +])
> > > > > > +
> > > > > > +# Set l4-protocol=tcp for the reject ACL.
> > > > > > +ovn-nbctl --wait=sb set ACL . options:l4-protocol=tcp
> > > > > > +
> > > > > > +# ovn-northd should generate 1 lflow with ip6 tcp_reset action.
> > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
"ls_out_acl.*priority=2012" |
> > > > > sort], [0], [dnl
> > > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > > +match=(ip6 && tcp && (inport == @pg0 && ip)), dnl
> > > > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src;
> > tcp_reset {
> > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > +])
> > > > > > +
> > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
"ls_out_acl.*priority=2002" |
> > > > > sort], [0], [dnl
> > > > > > +])
> > > > > > +
> > > > > > +# Set l4-protocol=udp for the reject ACL.
> > > > > > +ovn-nbctl --wait=sb set ACL . options:l4-protocol=udp
> > > > > > +
> > > > > > +# ovn-northd should generate 1 lflow with udp match and icmp6
> > action.
> > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
"ls_out_acl.*priority=2012" |
> > > > > sort], [0], [dnl
> > > > > > +])
> > > > > > +
> > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
"ls_out_acl.*priority=2002" |
> > > > > sort], [0], [dnl
> > > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > > +match=(ip6 && (inport == @pg0 && ip) && udp), dnl
> > > > > > +action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <->
ip6.src;
> > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > +])
> > > > > > +
> > > > > > +# Set l3-protocol to ip and l4-protocol=tcp for the reject ACL.
> > > > > > +ovn-nbctl --wait=sb set ACL . options:l3-protocol=ip
> > > > > > +ovn-nbctl --wait=sb set ACL . options:l4-protocol=tcp
> > > > > > +
> > > > > > +# ovn-northd should generate 2 lflow with ip4 tcp_reset and ip6
> > > > > tcp_reset actions.
> > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
"ls_out_acl.*priority=2012" |
> > > > > sort], [0], [dnl
> > > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > > +match=(ip4 && tcp && (inport == @pg0 && ip)), dnl
> > > > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> > tcp_reset {
> > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > > +match=(ip6 && tcp && (inport == @pg0 && ip)), dnl
> > > > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src;
> > tcp_reset {
> > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > +])
> > > > > > +
> > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
"ls_out_acl.*priority=2002" |
> > > > > sort], [0], [dnl
> > > > > > +])
> > > > > > +
> > > > > > +# Set l3-protocol to ip and l4-protocol=udp for the reject ACL.
> > > > > > +ovn-nbctl --wait=sb set ACL . options:l3-protocol=ip
> > > > > > +ovn-nbctl --wait=sb set ACL . options:l4-protocol=udp
> > > > > > +
> > > > > > +# ovn-northd should generate 2 lflows (udp match) with icmp4
and
> > icmp6
> > > > > actions.
> > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
"ls_out_acl.*priority=2012" |
> > > > > sort], [0], [dnl
> > > > > > +])
> > > > > > +
> > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
"ls_out_acl.*priority=2002" |
> > > > > sort], [0], [dnl
> > > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > > +match=(ip4 && (inport == @pg0 && ip) && udp), dnl
> > > > > > +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <->
ip4.src;
> > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > > +match=(ip6 && (inport == @pg0 && ip) && udp), dnl
> > > > > > +action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <->
ip6.src;
> > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > +])
> > > > > > +
> > > > > > +# Add an ACL with allow-related
> > > > > > +ovn-nbctl --wait=sb acl-add pg0 to-lport 1000 "ip"
allow-related
> > > > > > +
> > > > > > +# ovn-northd should generate 4 lflow with 2 icmp4 and 2 icmp6
> > actions.
> > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
"ls_out_acl.*priority=2012" |
> > > > > sort], [0], [dnl
> > > > > > +])
> > > > > > +
> > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
"ls_out_acl.*priority=2002" |
> > > > > sort], [0], [dnl
> > > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > > +match=((reg0[[10]] == 1) && ip4 && (inport == @pg0 && ip) &&
udp),
> > dnl\
> > > > > > +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; icmp4 {
> > eth.dst
> > > > > <-> eth.src; ip4.dst <-> ip4.src; outport <-> inport;
> > > > > next(pipeline=ingress,table=20); };)
> > > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > > +match=((reg0[[10]] == 1) && ip6 && (inport == @pg0 && ip) &&
udp),
> > dnl
> > > > > > +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; icmp6 {
> > eth.dst
> > > > > <-> eth.src; ip6.dst <-> ip6.src; outport <-> inport;
> > > > > next(pipeline=ingress,table=20); };)
> > > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > > +match=((reg0[[9]] == 1) && ip4 && (inport == @pg0 && ip) &&
udp),
> > dnl
> > > > > > +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <->
ip4.src;
> > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > > +match=((reg0[[9]] == 1) && ip6 && (inport == @pg0 && ip) &&
udp),
> > dnl
> > > > > > +action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <->
ip6.src;
> > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > +])
> > > > > > +
> > > > > > +# Set l3-protocol to ip and l4-protocol=tcp for the reject ACL.
> > > > > > +rej_acl=$(ovn-nbctl --bare --columns _uuid  find ACL
action=reject)
> > > > > > +ovn-nbctl --wait=sb set ACL $rej_acl options:l3-protocol=ip
> > > > > > +ovn-nbctl --wait=sb set ACL $rej_acl options:l4-protocol=tcp
> > > > > > +
> > > > > > +# ovn-northd should generate 4 lflow with 2 ip4 tcp_reset and
2 ip6
> > > > > tcp_reset actions.
> > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
"ls_out_acl.*priority=2012" |
> > > > > sort], [0], [dnl
> > > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > > +match=((reg0[[10]] == 1) && ip4 && tcp && (inport == @pg0 &&
ip)),
> > dnl
> > > > > > +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0;
eth.dst <->
> > > > > eth.src; ip4.dst <-> ip4.src; tcp_reset { outport <-> inport;
> > > > > next(pipeline=ingress,table=20); };)
> > > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > > +match=((reg0[[10]] == 1) && ip6 && tcp && (inport == @pg0 &&
ip)),
> > dnl
> > > > > > +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0;
eth.dst <->
> > > > > eth.src; ip6.dst <-> ip6.src; tcp_reset { outport <-> inport;
> > > > > next(pipeline=ingress,table=20); };)
> > > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > > +match=((reg0[[9]] == 1) && ip4 && tcp && (inport == @pg0 &&
ip)),
> > dnl
> > > > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> > tcp_reset {
> > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > > +match=((reg0[[9]] == 1) && ip6 && tcp && (inport == @pg0 &&
ip)),
> > dnl
> > > > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src;
> > tcp_reset {
> > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > +])
> > > > > > +
> > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
"ls_out_acl.*priority=2002" |
> > > > > sort], [0], [dnl
> > > > > > +])
> > > > > > +
> > > > > > +
> > > > > > +# Clear l3-protocol and set l4-protocol to udp
> > > > > > +ovn-nbctl --wait=sb remove ACL $rej_acl options l3-protocol
> > > > > > +ovn-nbctl --wait=sb set ACL $rej_acl options:l4-protocol=udp
> > > > > > +
> > > > > > +# ovn-northd should generate 4 lflows (with udp match) with 2
> > icmp4 and
> > > > > 2 icmp6 actions.
> > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
"ls_out_acl.*priority=2012" |
> > > > > sort], [0], [dnl
> > > > > > +])
> > > > > > +
> > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
"ls_out_acl.*priority=2002" |
> > > > > sort], [0], [dnl
> > > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > > +match=((reg0[[10]] == 1) && ip4 && (inport == @pg0 && ip) &&
udp),
> > dnl\
> > > > > > +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; icmp4 {
> > eth.dst
> > > > > <-> eth.src; ip4.dst <-> ip4.src; outport <-> inport;
> > > > > next(pipeline=ingress,table=20); };)
> > > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > > +match=((reg0[[10]] == 1) && ip6 && (inport == @pg0 && ip) &&
udp),
> > dnl
> > > > > > +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; icmp6 {
> > eth.dst
> > > > > <-> eth.src; ip6.dst <-> ip6.src; outport <-> inport;
> > > > > next(pipeline=ingress,table=20); };)
> > > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > > +match=((reg0[[9]] == 1) && ip4 && (inport == @pg0 && ip) &&
udp),
> > dnl
> > > > > > +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <->
ip4.src;
> > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > > +match=((reg0[[9]] == 1) && ip6 && (inport == @pg0 && ip) &&
udp),
> > dnl
> > > > > > +action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <->
ip6.src;
> > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > +])
> > > > > > +
> > > > > > +# Clear l3-protocol and set l4-protocol to tcp
> > > > > > +ovn-nbctl --wait=sb remove ACL $rej_acl options l3-protocol
> > > > > > +ovn-nbctl --wait=sb set ACL $rej_acl options:l4-protocol=tcp
> > > > > > +
> > > > > > +# ovn-northd should generate 4 lflow with 2 ip4 tcp_reset and
2 ip6
> > > > > tcp_reset actions.
> > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
"ls_out_acl.*priority=2012" |
> > > > > sort], [0], [dnl
> > > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > > +match=((reg0[[10]] == 1) && ip4 && tcp && (inport == @pg0 &&
ip)),
> > dnl
> > > > > > +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0;
eth.dst <->
> > > > > eth.src; ip4.dst <-> ip4.src; tcp_reset { outport <-> inport;
> > > > > next(pipeline=ingress,table=20); };)
> > > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > > +match=((reg0[[10]] == 1) && ip6 && tcp && (inport == @pg0 &&
ip)),
> > dnl
> > > > > > +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0;
eth.dst <->
> > > > > eth.src; ip6.dst <-> ip6.src; tcp_reset { outport <-> inport;
> > > > > next(pipeline=ingress,table=20); };)
> > > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > > +match=((reg0[[9]] == 1) && ip4 && tcp && (inport == @pg0 &&
ip)),
> > dnl
> > > > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> > tcp_reset {
> > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > > +match=((reg0[[9]] == 1) && ip6 && tcp && (inport == @pg0 &&
ip)),
> > dnl
> > > > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src;
> > tcp_reset {
> > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > +])
> > > > > > +
> > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
"ls_out_acl.*priority=2002" |
> > > > > sort], [0], [dnl
> > > > > > +])
> > > > > > +
> > > > > > +AT_CLEANUP
> > > > > > --
> > > > > > 2.26.2
> > > > > >
> > > > > > _______________________________________________
> > > > > > dev mailing list
> > > > > > dev@openvswitch.org
> > > > > > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> > > > > _______________________________________________
> > > > > dev mailing list
> > > > > dev@openvswitch.org
> > > > > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> > > > >
> > _______________________________________________
> > dev mailing list
> > dev@openvswitch.org
> > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> >
Numan Siddique Sept. 28, 2020, 6:34 p.m. UTC | #7
On Mon, Sep 28, 2020 at 11:13 PM Han Zhou <hzhou@ovn.org> wrote:
>
> On Mon, Sep 28, 2020 at 10:03 AM Numan Siddique <numans@ovn.org> wrote:
> >
> > On Mon, Sep 28, 2020 at 9:41 PM Han Zhou <hzhou@ovn.org> wrote:
> > >
> > > On Mon, Sep 28, 2020 at 2:21 AM Numan Siddique <numans@ovn.org> wrote:
> > > >
> > > > On Mon, Sep 28, 2020 at 2:14 PM Numan Siddique <numans@ovn.org> wrote:
> > > > >
> > > > > On Mon, Sep 28, 2020 at 5:57 AM Han Zhou <hzhou@ovn.org> wrote:
> > > > > >
> > > > > > Thanks Numan. I have some comments on the solution.
> > > > > >
> > > > > > On Thu, Sep 24, 2020 at 9:50 AM <numans@ovn.org> wrote:
> > > > > > >
> > > > > > > From: Numan Siddique <numans@ovn.org>
> > > > > > >
> > > > > > > ovn-northd adds below lflows for a reject ACL with a match - M
> > > > > > >
> > > > > > > match = (ip4 && tcp && 'M') action = tcp_reject{}
> > > > > > > match = (ip6 && tcp && 'M') action = tcp_reject{}
> > > > > > > match = (ip4 && 'M') action = icmp4{}
> > > > > > > match = (ip6 && 'M') action = icmp6{}
> > > > > > >
> > > > > > > This approach has a couple of problems:
> > > > > > >    - ovn-controller can reject the lflows if there are invalid
> > > matches.
> > > > > > >      Eg. If match 'M' is - 'ip4 && udp'.
> > > > > > >
> > > > > > >    - In a large scale deployment, this could result in lot of
> > > invalid
> > > > > > >      logical flows and increase the size of the SB DB.
> > > > > > >
> > > > > > > This patch addresses this problem by providing an option to the
> CMS
> > > > > > > to specify the l3 protocol and l4 protocol as hints. A new
> column
> > > > > > > 'options' is added to the ACL table with the options -
> > > > > > > options:l3-protocol and options:l4-protocol.
> > > > > > >
> > > > > > > ovn-northd will now generate only the required lflows for the
> reject
> > > > > > > ACL. If no options are set, then it falls back to the default
> > > scenario.
> > > > > > >
> > > > > >
> > > > > > Thanks Numan. I believe the solution should work. However, it
> seems a
> > > > > > little confusing for the end user about how to use the hint
> field. I
> > > think
> > > > > > maybe we should try to avoid the extra field if possible. Here is
> my
> > > > > > proposal:
> > > > > >
> > > > >
> > > > > Thanks Han for the comments. I do agree if we can avoid the extra
> > > option.
> > > > >
> > > > > > 1. For the tcp v.s. udp check, since we don't want northd to
> parse the
> > > > > > match condition, we will have to rely on users input. However,
> > > instead of a
> > > > > > hint, I think it is better to let the user explicitly specify in
> the
> > > action
> > > > > > field. E.g. "reject" means sending icmp, and "tcp_reject" means
> > > sending tcp
> > > > > > reset if the traffic is tcp and otherwise sending icmp. So, if the
> > > user
> > > > > > adds an ACL with match include "udp", they will naturally use
> "reject"
> > > > > > instead of "tcp_reject" because they know it is not possible for
> a TCP
> > > > > > packet to hit this ACL. If they add an ACL that tcp traffic is
> > > expected to
> > > > > > match (either TCP only, or general IP traffic), they can use
> > > "tcp_reject".
> > > > > > Would this be more straightforward than an extra "hint" field?
> > > > > >
> > > > >
> > > > > I have few comments here.
> > > > > - If we consider "reject" = "icmp", then we will be breaking the
> > > > > backward compatibility.
> > > > >     Right now, for a reject action, ovn-northd would send tcp_reset
> > > > > for tcp traffic and icmp reply or other traffic.
> > > > >     After your proposed approach, the  behaviour would change ? Is
> > > > > that OK ? Because existing test cases would fail.
> > > > >     The proposed patch handles the backward compatibility. Although
> > > > > I'm not sure if it is really required in this case.
> > > > >
> > > > > -  For  a tcp_reject action, if the match has "tcp", then we will
> end
> > > > > up adding a logical flow with the icmp action, which is
> > > > >     really not required. Or do you mean, pinctrl would take the
> > > > > decision while handling the "tcp_reject" action to either
> > > > >     send tcp reset packet or icmp packet ? If so, we can probably
> just
> > > > > have one action - "reject". Please see below.
> > > > >
> > > > > > 2. For ip4 v.s. ip6, the idea is to support generic ovn logical
> flow
> > > > > > actions "tcp_reject" and "icmp" in ovn-controller, which can
> handle
> > > both
> > > > > > ipv4 and ipv6. If this can be supported, the logical flows only
> needs
> > > to be:
> > > > > > match = (tcp && 'M') action = tcp_reject{}
> > > > > > match = ('M') action = icmp{}
> > > > > >
> > > > >
> > > > > Ok. In this case we cannot support inner actions unless they are
> > > > > independent of the ip version.
> > > > > i.e we can't support - tcp_reject { eth.dst <-> eth.src; ip4.dst <->
> > > > > ip4.src; } or
> > > > > tcp_reject {eth.dst <-> eth.src; ip6.dst <-> ip6.src;} in one
> logical
> > > > > flow. ovn-northd has to generate
> > > > > 2 logical flows.
> > > > >   - ip4 && 'M', action = tcp_reject { eth.dst <-> eth.src; ip4.dst
> <->
> > > > > ip4.src; }
> > > > >   - ip6 && 'M', action = tcp_reject { eth.dst <-> eth.src; ip6.dst
> <->
> > > > > ip6.src; }
> > > > >
> > > > >
> > > > > Right now for one reject ACL we add the below lflows
> > > > >    - ip4 && tcp && 'M', action = tcp_reset { eth.dst <-> eth.src;
> > > > > ip4.dst <-> ip4.src; outport <-> inport;}
> > > > >    - ip6 && tcp && 'M', action = tcp_reset { eth.dst <-> eth.src;
> > > > > ip6.dst <-> ip6.src; outport <-> inport;}
> > > > >    - ip4 && 'M'            , action = icmp4 {eth.dst <-> eth.src;
> > > > > ip4.dst <-> ip4.src; outport <-> inport;}
> > > > >    - ip6 && 'M'            , action = icmp6 {eth.dst <-> eth.src;
> > > > > ip6.dst <-> ip6.src; outport <-> inport;}
> > > > >
> > > > > I have another idea based on your suggestions here: How about we
> have
> > > > > a new action - reject{} which will be used
> > > > > for all reject ACLs and this will
> > > > >     - send tcp reset packet if the packet is tcp (pinctrl would take
> > > > > the decision)
> > > > >     - send icmp packet otherwise.
> > > > >
> > > > > We can add the logical flow for a reject ACL in 2 ways
> > > > >
> > > > > 1. match = M, action = reject { eth.dst <-> eth.src, outport <->
> > > inport;}
> > > > >
> > > > > OR
> > > > >
> > > > > 2. match = M, action = reject { eth.dst <-> eth.src, ip.src <->
> > > > > ip.dst; outport <-> inport;}
> > > > >
> > > > > In (1), pinctrl while handling the reject action, will swap the ip
> src
> > > > > with ip dst (i.e ip4.dst <-> ip4.src or ip6.dst <-> ip6.src }.
> > > > >
> > > > > In (2), we will add 2 new OVN fields "ip.src" and "ip.dst" (just
> like
> > > > > we have icmp4.frag_mtu now) and the action - ip.src <-> ip.dst
> > > > > will result in another controller action which will swap the ip src
> > > > > and ip dst fields (just like how "icmp4.frag_mtu = 1400" would
> result
> > > > > into a controller action).
> > > > >
> > > > > Even though (1) will work, it's not transparent. I would personally
> > > > > prefer (2) and I think it's ok if the reject ACL would result in 2
> OF
> > > > > controller actions.
> > > > >
> > > > > With this we will have just one logical flow (instead of 4 ) and
> there
> > > > > will be no changes on the CMS side.
> > > > >
> > >
> > > Yes, this would be even better.
> >
> > Thanks. I will work on it.
> >
> >
> > >
> > > > > What do you think ?
> > > > >
> > > > > Thanks
> > > > > Numan
> > > > >
> > > > >
> > > > > > I haven't thought through all the details but it seems doable. Did
> > > you have
> > > > > > any thoughts on this? (If it proves to be unreasonable I am ok
> with
> > > current
> > > > > > "hint" approach)
> > > > > >
> > > > > > In addition, I found the existing TCP reject handling has a bug
> which
> > > > > > messes up the user defined ACL priorities. I just posted a patch:
> > > > > >
> > >
> https://patchwork.ozlabs.org/project/ovn/patch/20200928000904.858685-1-hzhou@ovn.org/
> > > > > > It is not directly related to this patch but it would be great if
> you
> > > could
> > > > > > take a look.
> > > >
> > > > Sure.
> > > >
> > > With only one lflow, my above fix is not needed. (I am not sure about
> > > backporting to old branches.)
> >
> > I thought about it. Since this issue has been there for a long time
> > and no one reported it. Probably its ok to not
> > address this for the branches ?
> >
> Maybe. It seems this feature is not heavily used. Otherwise such issues
> should have been reported. I reported another issue related to "reject" but
> didn't have time to debug:
> https://mail.openvswitch.org/pipermail/ovs-discuss/2020-September/050711.html

I didn't notice that this issue is different from the patch you
submitted. I thought you're trying
to address this issue in your patch. I will test this scenario out and
make sure it's addressed.

Thanks
Numan

>
> > We could also take the approach of accepting your patch and backport
> > to the branches. And we can revert it when I submit the patch
> > with the proposed new approach here.
> >
> I think we can wait for your patch, unless someone complains and requires
> an immediate fix. My patch reduces the ACL priority range, which is a risk
> of backward compatibility, so I'd rather not apply it if you could work out
> the proposed fix here.
>
> > Thanks
> > Numan
> >
> > >
> > > > Thanks
> > > > Numan
> > > >
> > > > > >
> > > > > > Thanks,
> > > > > > Han
> > > > > >
> > > > > >
> > > > > > > Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=1876990
> > > > > > > Reported-by: Ilya Maximets <i.maximets@ovn.org>
> > > > > > > Signed-off-by: Numan Siddique <numans@ovn.org>
> > > > > > > ---
> > > > > > >  northd/ovn-northd.c | 194 ++++++++++++++++++++----------
> > > > > > >  ovn-nb.ovsschema    |   7 +-
> > > > > > >  ovn-nb.xml          |  43 +++++++
> > > > > > >  tests/ovn-northd.at | 285
> > > ++++++++++++++++++++++++++++++++++++++++++++
> > > > > > >  4 files changed, 461 insertions(+), 68 deletions(-)
> > > > > > >
> > > > > > > diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
> > > > > > > index 3324c9e81d..d167137e07 100644
> > > > > > > --- a/northd/ovn-northd.c
> > > > > > > +++ b/northd/ovn-northd.c
> > > > > > > @@ -5372,73 +5372,135 @@ build_reject_acl_rules(struct
> ovn_datapath
> > > *od,
> > > > > > struct hmap *lflows,
> > > > > > >      struct ds actions = DS_EMPTY_INITIALIZER;
> > > > > > >      bool ingress = (stage == S_SWITCH_IN_ACL);
> > > > > > >
> > > > > > > -    /* TCP */
> > > > > > > -    build_acl_log(&actions, acl);
> > > > > > > -    if (extra_match->length > 0) {
> > > > > > > -        ds_put_format(&match, "(%s) && ", extra_match->string);
> > > > > > > -    }
> > > > > > > -    ds_put_format(&match, "ip4 && tcp && (%s)", acl->match);
> > > > > > > -    ds_put_format(&actions, "reg0 = 0; "
> > > > > > > -                  "eth.dst <-> eth.src; ip4.dst <-> ip4.src; "
> > > > > > > -                  "tcp_reset { outport <-> inport; %s };",
> > > > > > > -                  ingress ? "next(pipeline=egress,table=5);"
> > > > > > > -                          :
> "next(pipeline=ingress,table=20);");
> > > > > > > -    ovn_lflow_add_with_hint(lflows, od, stage,
> > > > > > > -                            acl->priority + OVN_ACL_PRI_OFFSET
> +
> > > 10,
> > > > > > > -                            ds_cstr(&match), ds_cstr(&actions),
> > > > > > stage_hint);
> > > > > > > -    ds_clear(&match);
> > > > > > > -    ds_clear(&actions);
> > > > > > > -    build_acl_log(&actions, acl);
> > > > > > > -    if (extra_match->length > 0) {
> > > > > > > -        ds_put_format(&match, "(%s) && ", extra_match->string);
> > > > > > > -    }
> > > > > > > -    ds_put_format(&match, "ip6 && tcp && (%s)", acl->match);
> > > > > > > -    ds_put_format(&actions, "reg0 = 0; "
> > > > > > > -                  "eth.dst <-> eth.src; ip6.dst <-> ip6.src; "
> > > > > > > -                  "tcp_reset { outport <-> inport; %s };",
> > > > > > > -                  ingress ? "next(pipeline=egress,table=5);"
> > > > > > > -                          :
> "next(pipeline=ingress,table=20);");
> > > > > > > -    ovn_lflow_add_with_hint(lflows, od, stage,
> > > > > > > -                            acl->priority + OVN_ACL_PRI_OFFSET
> +
> > > 10,
> > > > > > > -                            ds_cstr(&match), ds_cstr(&actions),
> > > > > > stage_hint);
> > > > > > > +    bool is_ip4 = true;
> > > > > > > +    bool is_ip6 = true;
> > > > > > > +    bool tcp_reset = true;
> > > > > > > +    bool icmp_reset = true;
> > > > > > > +    bool is_udp = false;
> > > > > > > +
> > > > > > > +    const char *l3_protocol = smap_get(&acl->options,
> > > "l3-protocol");
> > > > > > > +    if (l3_protocol) {
> > > > > > > +        if (!strcasecmp(l3_protocol, "ip")) {
> > > > > > > +            is_ip4 = true;
> > > > > > > +            is_ip6 = true;
> > > > > > > +        } else if (!strcasecmp(l3_protocol, "ip4")) {
> > > > > > > +            is_ip4 = true;
> > > > > > > +            is_ip6 = false;
> > > > > > > +        } else if (!strcasecmp(l3_protocol, "ip6")) {
> > > > > > > +            is_ip6 = true;
> > > > > > > +            is_ip4 = false;
> > > > > > > +        }
> > > > > > > +    }
> > > > > > > +
> > > > > > > +    const char *l4_protocol = smap_get(&acl->options,
> > > "l4-protocol");
> > > > > > > +    if (l4_protocol) {
> > > > > > > +        if (!strcasecmp(l4_protocol, "tcp")) {
> > > > > > > +            tcp_reset = true;
> > > > > > > +            icmp_reset = false;
> > > > > > > +        } else if (!strcasecmp(l4_protocol, "udp")) {
> > > > > > > +            tcp_reset = false;
> > > > > > > +            is_udp = true;
> > > > > > > +        } else {
> > > > > > > +            tcp_reset = false;
> > > > > > > +        }
> > > > > > > +    }
> > > > > > >
> > > > > > > -    /* IP traffic */
> > > > > > > -    ds_clear(&match);
> > > > > > > -    ds_clear(&actions);
> > > > > > > -    build_acl_log(&actions, acl);
> > > > > > > -    if (extra_match->length > 0) {
> > > > > > > -        ds_put_format(&match, "(%s) && ", extra_match->string);
> > > > > > > -    }
> > > > > > > -    ds_put_format(&match, "ip4 && (%s)", acl->match);
> > > > > > > -    if (extra_actions->length > 0) {
> > > > > > > -        ds_put_format(&actions, "%s ", extra_actions->string);
> > > > > > > -    }
> > > > > > > -    ds_put_format(&actions, "reg0 = 0; "
> > > > > > > -                  "icmp4 { eth.dst <-> eth.src; ip4.dst <->
> > > ip4.src; "
> > > > > > > -                  "outport <-> inport; %s };",
> > > > > > > -                  ingress ? "next(pipeline=egress,table=5);"
> > > > > > > -                          :
> "next(pipeline=ingress,table=20);");
> > > > > > > -    ovn_lflow_add_with_hint(lflows, od, stage,
> > > > > > > -                            acl->priority + OVN_ACL_PRI_OFFSET,
> > > > > > > -                            ds_cstr(&match), ds_cstr(&actions),
> > > > > > stage_hint);
> > > > > > > -    ds_clear(&match);
> > > > > > > -    ds_clear(&actions);
> > > > > > > -    build_acl_log(&actions, acl);
> > > > > > > -    if (extra_match->length > 0) {
> > > > > > > -        ds_put_format(&match, "(%s) && ", extra_match->string);
> > > > > > > -    }
> > > > > > > -    ds_put_format(&match, "ip6 && (%s)", acl->match);
> > > > > > > -    if (extra_actions->length > 0) {
> > > > > > > -        ds_put_format(&actions, "%s ", extra_actions->string);
> > > > > > > -    }
> > > > > > > -    ds_put_format(&actions, "reg0 = 0; icmp6 { "
> > > > > > > -                  "eth.dst <-> eth.src; ip6.dst <-> ip6.src; "
> > > > > > > -                  "outport <-> inport; %s };",
> > > > > > > -                  ingress ? "next(pipeline=egress,table=5);"
> > > > > > > -                          :
> "next(pipeline=ingress,table=20);");
> > > > > > > -    ovn_lflow_add_with_hint(lflows, od, stage,
> > > > > > > -                            acl->priority + OVN_ACL_PRI_OFFSET,
> > > > > > > -                            ds_cstr(&match), ds_cstr(&actions),
> > > > > > stage_hint);
> > > > > > > +    if (is_ip4) {
> > > > > > > +        if (tcp_reset) {
> > > > > > > +            build_acl_log(&actions, acl);
> > > > > > > +            if (extra_match->length > 0) {
> > > > > > > +                ds_put_format(&match, "(%s) && ",
> > > extra_match->string);
> > > > > > > +            }
> > > > > > > +            ds_put_format(&match, "ip4 && tcp && (%s)",
> > > acl->match);
> > > > > > > +            if (extra_actions->length > 0) {
> > > > > > > +                ds_put_format(&actions, "%s ",
> > > extra_actions->string);
> > > > > > > +            }
> > > > > > > +            ds_put_format(&actions, "reg0 = 0; "
> > > > > > > +                          "eth.dst <-> eth.src; ip4.dst <->
> > > ip4.src; "
> > > > > > > +                          "tcp_reset { outport <-> inport; %s
> };",
> > > > > > > +                          ingress ?
> > > "next(pipeline=egress,table=5);"
> > > > > > > +                                  :
> > > "next(pipeline=ingress,table=20);");
> > > > > > > +            ovn_lflow_add_with_hint(lflows, od, stage,
> > > > > > > +                                    acl->priority +
> > > OVN_ACL_PRI_OFFSET +
> > > > > > 10,
> > > > > > > +                                    ds_cstr(&match),
> > > ds_cstr(&actions),
> > > > > > > +                                    stage_hint);
> > > > > > > +        }
> > > > > > > +
> > > > > > > +        if (icmp_reset) {
> > > > > > > +            ds_clear(&match);
> > > > > > > +            ds_clear(&actions);
> > > > > > > +            build_acl_log(&actions, acl);
> > > > > > > +            if (extra_match->length > 0) {
> > > > > > > +                ds_put_format(&match, "(%s) && ",
> > > extra_match->string);
> > > > > > > +            }
> > > > > > > +            ds_put_format(&match, "ip4 && (%s)", acl->match);
> > > > > > > +            if (is_udp) {
> > > > > > > +                ds_put_cstr(&match, " && udp");
> > > > > > > +            }
> > > > > > > +            if (extra_actions->length > 0) {
> > > > > > > +                ds_put_format(&actions, "%s ",
> > > extra_actions->string);
> > > > > > > +            }
> > > > > > > +            ds_put_format(&actions, "reg0 = 0; "
> > > > > > > +                          "icmp4 { eth.dst <-> eth.src;
> ip4.dst <->
> > > > > > ip4.src; "
> > > > > > > +                          "outport <-> inport; %s };",
> > > > > > > +                          ingress ?
> > > "next(pipeline=egress,table=5);"
> > > > > > > +                                  :
> > > "next(pipeline=ingress,table=20);");
> > > > > > > +            ovn_lflow_add_with_hint(lflows, od, stage,
> > > > > > > +                                    acl->priority +
> > > OVN_ACL_PRI_OFFSET,
> > > > > > > +                                    ds_cstr(&match),
> > > ds_cstr(&actions),
> > > > > > > +                                    stage_hint);
> > > > > > > +        }
> > > > > > > +    }
> > > > > > > +
> > > > > > > +    if (is_ip6) {
> > > > > > > +        if (tcp_reset) {
> > > > > > > +            ds_clear(&match);
> > > > > > > +            ds_clear(&actions);
> > > > > > > +            build_acl_log(&actions, acl);
> > > > > > > +            if (extra_match->length > 0) {
> > > > > > > +                ds_put_format(&match, "(%s) && ",
> > > extra_match->string);
> > > > > > > +            }
> > > > > > > +            ds_put_format(&match, "ip6 && tcp && (%s)",
> > > acl->match);
> > > > > > > +            if (extra_actions->length > 0) {
> > > > > > > +                ds_put_format(&actions, "%s ",
> > > extra_actions->string);
> > > > > > > +            }
> > > > > > > +            ds_put_format(&actions, "reg0 = 0; "
> > > > > > > +                          "eth.dst <-> eth.src; ip6.dst <->
> > > ip6.src; "
> > > > > > > +                          "tcp_reset { outport <-> inport; %s
> };",
> > > > > > > +                          ingress ?
> > > "next(pipeline=egress,table=5);"
> > > > > > > +                                  :
> > > "next(pipeline=ingress,table=20);");
> > > > > > > +            ovn_lflow_add_with_hint(lflows, od, stage,
> > > > > > > +                                    acl->priority +
> > > OVN_ACL_PRI_OFFSET +
> > > > > > 10,
> > > > > > > +                                    ds_cstr(&match),
> > > ds_cstr(&actions),
> > > > > > > +                                    stage_hint);
> > > > > > > +        }
> > > > > > > +
> > > > > > > +        if (icmp_reset) {
> > > > > > > +            ds_clear(&match);
> > > > > > > +            ds_clear(&actions);
> > > > > > > +            build_acl_log(&actions, acl);
> > > > > > > +            if (extra_match->length > 0) {
> > > > > > > +                ds_put_format(&match, "(%s) && ",
> > > extra_match->string);
> > > > > > > +            }
> > > > > > > +            ds_put_format(&match, "ip6 && (%s)", acl->match);
> > > > > > > +            if (is_udp) {
> > > > > > > +                ds_put_cstr(&match, " && udp");
> > > > > > > +            }
> > > > > > > +
> > > > > > > +            if (extra_actions->length > 0) {
> > > > > > > +                ds_put_format(&actions, "%s ",
> > > extra_actions->string);
> > > > > > > +            }
> > > > > > > +            ds_put_format(&actions, "reg0 = 0; icmp6 { "
> > > > > > > +                          "eth.dst <-> eth.src; ip6.dst <->
> > > ip6.src; "
> > > > > > > +                          "outport <-> inport; %s };",
> > > > > > > +                          ingress ?
> > > "next(pipeline=egress,table=5);"
> > > > > > > +                                :
> > > "next(pipeline=ingress,table=20);");
> > > > > > > +            ovn_lflow_add_with_hint(lflows, od, stage,
> > > > > > > +                                    acl->priority +
> > > OVN_ACL_PRI_OFFSET,
> > > > > > > +                                    ds_cstr(&match),
> > > ds_cstr(&actions),
> > > > > > > +                                    stage_hint);
> > > > > > > +        }
> > > > > > > +    }
> > > > > > >
> > > > > > >      ds_destroy(&match);
> > > > > > >      ds_destroy(&actions);
> > > > > > > diff --git a/ovn-nb.ovsschema b/ovn-nb.ovsschema
> > > > > > > index 092322ab2c..00f1c7cd4b 100644
> > > > > > > --- a/ovn-nb.ovsschema
> > > > > > > +++ b/ovn-nb.ovsschema
> > > > > > > @@ -1,7 +1,7 @@
> > > > > > >  {
> > > > > > >      "name": "OVN_Northbound",
> > > > > > > -    "version": "5.27.0",
> > > > > > > -    "cksum": "3507518247 26773",
> > > > > > > +    "version": "5.28.0",
> > > > > > > +    "cksum": "699859908 26928",
> > > > > > >      "tables": {
> > > > > > >          "NB_Global": {
> > > > > > >              "columns": {
> > > > > > > @@ -225,6 +225,9 @@
> > > > > > >
>  "debug"]]},
> > > > > > >                                        "min": 0, "max": 1}},
> > > > > > >                  "meter": {"type": {"key": "string", "min": 0,
> > > "max": 1}},
> > > > > > > +                "options": {
> > > > > > > +                    "type": {"key": "string", "value":
> "string",
> > > > > > > +                             "min": 0, "max": "unlimited"}},
> > > > > > >                  "external_ids": {
> > > > > > >                      "type": {"key": "string", "value":
> "string",
> > > > > > >                               "min": 0, "max": "unlimited"}}},
> > > > > > > diff --git a/ovn-nb.xml b/ovn-nb.xml
> > > > > > > index 86195af341..2c3497e2ae 100644
> > > > > > > --- a/ovn-nb.xml
> > > > > > > +++ b/ovn-nb.xml
> > > > > > > @@ -1721,6 +1721,49 @@
> > > > > > >        </ul>
> > > > > > >      </column>
> > > > > > >
> > > > > > > +    <group title="Common options">
> > > > > > > +      <column name="options">
> > > > > > > +        This column provides general key/value settings. The
> > > supported
> > > > > > > +        options are described individually below.
> > > > > > > +      </column>
> > > > > > > +
> > > > > > > +      <group title="Options for providing protocol hints for
> reject
> > > > > > ACL.">
> > > > > > > +        <p>
> > > > > > > +          The ACL match specified in the
> > > > > > > +          <ref column="match" table="ACL" db="OVN_Northbound"/>
> > > column is
> > > > > > > +          opaque to <code>OVN</code> and
> <code>ovn-northd</code>
> > > doesn't
> > > > > > > +          look into the match fields.
> > > > > > > +          These options can be specified by CMS to provide
> hints to
> > > > > > > +          <code>OVN</code> about the L3 and L4 protocol matches
> > > for the
> > > > > > reject
> > > > > > > +          ACL. <code>ovn-northd</code> uses these options if
> set
> > > to use
> > > > > > > +          appropriate actions when generating logical flows.
> > > > > > > +        </p>
> > > > > > > +
> > > > > > > +        <p>
> > > > > > > +          If these options are not set, then
> > > <code>ovn-northd</code>
> > > > > > assumes
> > > > > > > +          the reject ACL applies to IPv4, IPv6 and TCP packets.
> > > > > > > +        </p>
> > > > > > > +
> > > > > > > +        <column name="options" key="l3-protocol">
> > > > > > > +          The possible values are <code>ip</code>,
> > > <code>ip4</code> and
> > > > > > > +          <code>ip6</code>. If the value is <code>ip</code>, it
> > > means the
> > > > > > > +          reject ACL action applies to both IPv4 and IPv6
> packets.
> > > If the
> > > > > > > +          value is <code>ip4</code>, it means the reject ACL
> action
> > > > > > applies to
> > > > > > > +          IPv4 packets and the value <code>ip6</code> applies
> to
> > > IPv6
> > > > > > packets.
> > > > > > > +        </column>
> > > > > > > +
> > > > > > > +        <column name="options" key="l4-protocol">
> > > > > > > +          <p>
> > > > > > > +            The possible values are <code>tcp</code> and
> > > > > > <code>udp</code>.
> > > > > > > +            If the value is <code>tcp</code>, it means the
> > > > > > > +            reject ACL action applies to TCP packets. If the
> > > > > > > +            value is <code>udp</code>, it means the reject ACL
> > > action
> > > > > > applies
> > > > > > > +            to UDP packets.
> > > > > > > +          </p>
> > > > > > > +        </column>
> > > > > > > +      </group>
> > > > > > > +    </group>
> > > > > > > +
> > > > > > >      <group title="Logging">
> > > > > > >        <p>
> > > > > > >          These columns control whether and how OVN logs packets
> that
> > > > > > match an
> > > > > > > diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> > > > > > > index 99a9204f1f..4a30f8ed07 100644
> > > > > > > --- a/tests/ovn-northd.at
> > > > > > > +++ b/tests/ovn-northd.at
> > > > > > > @@ -2010,3 +2010,288 @@ ovn-nbctl --wait=sb set NB_Global .
> > > > > > options:ignore_lsp_down=true
> > > > > > >  AT_CHECK([ovn-sbctl lflow-list | grep arp | grep 10\.0\.0\.1],
> [0],
> > > > > > [ignore])
> > > > > > >
> > > > > > >  AT_CLEANUP
> > > > > > > +
> > > > > > > +AT_SETUP([ovn-northd -- ACL reject flows])
> > > > > > > +ovn_start
> > > > > > > +
> > > > > > > +ovn-nbctl ls-add sw0
> > > > > > > +ovn-nbctl lsp-add sw0 sw0-p1
> > > > > > > +ovn-nbctl lsp-add sw0 sw0-p2
> > > > > > > +
> > > > > > > +ovn-nbctl pg-add pg0 sw0-p1 sw0-p2
> > > > > > > +ovn-nbctl acl-add pg0 to-lport 1002 "inport == @pg0 && ip"
> reject
> > > > > > > +
> > > > > > > +ovn-nbctl --wait=sb sync
> > > > > > > +
> > > > > > > +# If there is a reject ACL wihtout any protocol hints, then
> > > ovn-northd
> > > > > > should
> > > > > > > +# generate lflows with ip4 tcp_reset, icmp4, ip6 tcp_reset and
> > > icmp6
> > > > > > actions.
> > > > > > > +
> > > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
> "ls_out_acl.*priority=2012" |
> > > > > > sort], [0], [dnl
> > > > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > > > +match=(ip4 && tcp && (inport == @pg0 && ip)), dnl
> > > > > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> > > tcp_reset {
> > > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > > > +match=(ip6 && tcp && (inport == @pg0 && ip)), dnl
> > > > > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src;
> > > tcp_reset {
> > > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > > +])
> > > > > > > +
> > > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
> "ls_out_acl.*priority=2002" |
> > > > > > sort], [0], [dnl
> > > > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > > > +match=(ip4 && (inport == @pg0 && ip)), dnl
> > > > > > > +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <->
> ip4.src;
> > > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > > > +match=(ip6 && (inport == @pg0 && ip)), dnl
> > > > > > > +action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <->
> ip6.src;
> > > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > > +])
> > > > > > > +
> > > > > > > +# Set l3-protocol=ip4 for the reject ACL.
> > > > > > > +ovn-nbctl --wait=sb set ACL . options:l3-protocol=ip4
> > > > > > > +
> > > > > > > +# ovn-northd should generate  2 lflows with ip4 tcp_reset and
> icmp4
> > > > > > actions.
> > > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
> "ls_out_acl.*priority=2012" |
> > > > > > sort], [0], [dnl
> > > > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > > > +match=(ip4 && tcp && (inport == @pg0 && ip)), dnl
> > > > > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> > > tcp_reset {
> > > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > > +])
> > > > > > > +
> > > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
> "ls_out_acl.*priority=2002" |
> > > > > > sort], [0], [dnl
> > > > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > > > +match=(ip4 && (inport == @pg0 && ip)), dnl
> > > > > > > +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <->
> ip4.src;
> > > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > > +])
> > > > > > > +
> > > > > > > +# Set l4-protocol=tcp for the reject ACL.
> > > > > > > +ovn-nbctl --wait=sb set ACL . options:l4-protocol=tcp
> > > > > > > +
> > > > > > > +# ovn-northd should generate 1 lflow with ip4 tcp_reset action.
> > > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
> "ls_out_acl.*priority=2012" |
> > > > > > sort], [0], [dnl
> > > > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > > > +match=(ip4 && tcp && (inport == @pg0 && ip)), dnl
> > > > > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> > > tcp_reset {
> > > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > > +])
> > > > > > > +
> > > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
> "ls_out_acl.*priority=2002" |
> > > > > > sort], [0], [dnl
> > > > > > > +])
> > > > > > > +
> > > > > > > +# Set l4-protocol=udp for the reject ACL.
> > > > > > > +ovn-nbctl --wait=sb set ACL . options:l4-protocol=udp
> > > > > > > +
> > > > > > > +# ovn-northd should generate 1 lflow with udp match and icmp4
> > > action.
> > > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
> "ls_out_acl.*priority=2012" |
> > > > > > sort], [0], [dnl
> > > > > > > +])
> > > > > > > +
> > > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
> "ls_out_acl.*priority=2002" |
> > > > > > sort], [0], [dnl
> > > > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > > > +match=(ip4 && (inport == @pg0 && ip) && udp), dnl
> > > > > > > +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <->
> ip4.src;
> > > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > > +])
> > > > > > > +
> > > > > > > +# Remove l4-protocol from the reject ACL.
> > > > > > > +ovn-nbctl --wait=sb remove ACL . options l4-protocol
> > > > > > > +
> > > > > > > +# ovn-northd should generate 2 lflow with tcp_reset and icmp4
> > > action.
> > > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
> "ls_out_acl.*priority=2012" |
> > > > > > sort], [0], [dnl
> > > > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > > > +match=(ip4 && tcp && (inport == @pg0 && ip)), dnl
> > > > > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> > > tcp_reset {
> > > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > > +])
> > > > > > > +
> > > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
> "ls_out_acl.*priority=2002" |
> > > > > > sort], [0], [dnl
> > > > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > > > +match=(ip4 && (inport == @pg0 && ip)), dnl
> > > > > > > +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <->
> ip4.src;
> > > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > > +])
> > > > > > > +
> > > > > > > +# Set l3-protocol to ip.
> > > > > > > +ovn-nbctl --wait=sb set ACL . options:l3-protocol=ip
> > > > > > > +
> > > > > > > +# ovn-northd should generate 4 lflow with ip4 tcp_reset, ip6
> > > tcp_reset,
> > > > > > icmp4 and icmp6 actions.
> > > > > > > +
> > > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
> "ls_out_acl.*priority=2012" |
> > > > > > sort], [0], [dnl
> > > > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > > > +match=(ip4 && tcp && (inport == @pg0 && ip)), dnl
> > > > > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> > > tcp_reset {
> > > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > > > +match=(ip6 && tcp && (inport == @pg0 && ip)), dnl
> > > > > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src;
> > > tcp_reset {
> > > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > > +])
> > > > > > > +
> > > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
> "ls_out_acl.*priority=2002" |
> > > > > > sort], [0], [dnl
> > > > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > > > +match=(ip4 && (inport == @pg0 && ip)), dnl
> > > > > > > +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <->
> ip4.src;
> > > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > > > +match=(ip6 && (inport == @pg0 && ip)), dnl
> > > > > > > +action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <->
> ip6.src;
> > > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > > +])
> > > > > > > +
> > > > > > > +# Set l3-protocol=ip6 for the reject ACL.
> > > > > > > +ovn-nbctl --wait=sb set ACL . options:l3-protocol=ip6
> > > > > > > +
> > > > > > > +# ovn-northd should generate  2 lflows with ip6 tcp_reset and
> icmp6
> > > > > > actions.
> > > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
> "ls_out_acl.*priority=2012" |
> > > > > > sort], [0], [dnl
> > > > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > > > +match=(ip6 && tcp && (inport == @pg0 && ip)), dnl
> > > > > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src;
> > > tcp_reset {
> > > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > > +])
> > > > > > > +
> > > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
> "ls_out_acl.*priority=2002" |
> > > > > > sort], [0], [dnl
> > > > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > > > +match=(ip6 && (inport == @pg0 && ip)), dnl
> > > > > > > +action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <->
> ip6.src;
> > > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > > +])
> > > > > > > +
> > > > > > > +# Set l4-protocol=tcp for the reject ACL.
> > > > > > > +ovn-nbctl --wait=sb set ACL . options:l4-protocol=tcp
> > > > > > > +
> > > > > > > +# ovn-northd should generate 1 lflow with ip6 tcp_reset action.
> > > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
> "ls_out_acl.*priority=2012" |
> > > > > > sort], [0], [dnl
> > > > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > > > +match=(ip6 && tcp && (inport == @pg0 && ip)), dnl
> > > > > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src;
> > > tcp_reset {
> > > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > > +])
> > > > > > > +
> > > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
> "ls_out_acl.*priority=2002" |
> > > > > > sort], [0], [dnl
> > > > > > > +])
> > > > > > > +
> > > > > > > +# Set l4-protocol=udp for the reject ACL.
> > > > > > > +ovn-nbctl --wait=sb set ACL . options:l4-protocol=udp
> > > > > > > +
> > > > > > > +# ovn-northd should generate 1 lflow with udp match and icmp6
> > > action.
> > > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
> "ls_out_acl.*priority=2012" |
> > > > > > sort], [0], [dnl
> > > > > > > +])
> > > > > > > +
> > > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
> "ls_out_acl.*priority=2002" |
> > > > > > sort], [0], [dnl
> > > > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > > > +match=(ip6 && (inport == @pg0 && ip) && udp), dnl
> > > > > > > +action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <->
> ip6.src;
> > > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > > +])
> > > > > > > +
> > > > > > > +# Set l3-protocol to ip and l4-protocol=tcp for the reject ACL.
> > > > > > > +ovn-nbctl --wait=sb set ACL . options:l3-protocol=ip
> > > > > > > +ovn-nbctl --wait=sb set ACL . options:l4-protocol=tcp
> > > > > > > +
> > > > > > > +# ovn-northd should generate 2 lflow with ip4 tcp_reset and ip6
> > > > > > tcp_reset actions.
> > > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
> "ls_out_acl.*priority=2012" |
> > > > > > sort], [0], [dnl
> > > > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > > > +match=(ip4 && tcp && (inport == @pg0 && ip)), dnl
> > > > > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> > > tcp_reset {
> > > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > > > +match=(ip6 && tcp && (inport == @pg0 && ip)), dnl
> > > > > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src;
> > > tcp_reset {
> > > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > > +])
> > > > > > > +
> > > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
> "ls_out_acl.*priority=2002" |
> > > > > > sort], [0], [dnl
> > > > > > > +])
> > > > > > > +
> > > > > > > +# Set l3-protocol to ip and l4-protocol=udp for the reject ACL.
> > > > > > > +ovn-nbctl --wait=sb set ACL . options:l3-protocol=ip
> > > > > > > +ovn-nbctl --wait=sb set ACL . options:l4-protocol=udp
> > > > > > > +
> > > > > > > +# ovn-northd should generate 2 lflows (udp match) with icmp4
> and
> > > icmp6
> > > > > > actions.
> > > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
> "ls_out_acl.*priority=2012" |
> > > > > > sort], [0], [dnl
> > > > > > > +])
> > > > > > > +
> > > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
> "ls_out_acl.*priority=2002" |
> > > > > > sort], [0], [dnl
> > > > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > > > +match=(ip4 && (inport == @pg0 && ip) && udp), dnl
> > > > > > > +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <->
> ip4.src;
> > > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > > > +match=(ip6 && (inport == @pg0 && ip) && udp), dnl
> > > > > > > +action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <->
> ip6.src;
> > > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > > +])
> > > > > > > +
> > > > > > > +# Add an ACL with allow-related
> > > > > > > +ovn-nbctl --wait=sb acl-add pg0 to-lport 1000 "ip"
> allow-related
> > > > > > > +
> > > > > > > +# ovn-northd should generate 4 lflow with 2 icmp4 and 2 icmp6
> > > actions.
> > > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
> "ls_out_acl.*priority=2012" |
> > > > > > sort], [0], [dnl
> > > > > > > +])
> > > > > > > +
> > > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
> "ls_out_acl.*priority=2002" |
> > > > > > sort], [0], [dnl
> > > > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > > > +match=((reg0[[10]] == 1) && ip4 && (inport == @pg0 && ip) &&
> udp),
> > > dnl\
> > > > > > > +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; icmp4 {
> > > eth.dst
> > > > > > <-> eth.src; ip4.dst <-> ip4.src; outport <-> inport;
> > > > > > next(pipeline=ingress,table=20); };)
> > > > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > > > +match=((reg0[[10]] == 1) && ip6 && (inport == @pg0 && ip) &&
> udp),
> > > dnl
> > > > > > > +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; icmp6 {
> > > eth.dst
> > > > > > <-> eth.src; ip6.dst <-> ip6.src; outport <-> inport;
> > > > > > next(pipeline=ingress,table=20); };)
> > > > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > > > +match=((reg0[[9]] == 1) && ip4 && (inport == @pg0 && ip) &&
> udp),
> > > dnl
> > > > > > > +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <->
> ip4.src;
> > > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > > > +match=((reg0[[9]] == 1) && ip6 && (inport == @pg0 && ip) &&
> udp),
> > > dnl
> > > > > > > +action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <->
> ip6.src;
> > > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > > +])
> > > > > > > +
> > > > > > > +# Set l3-protocol to ip and l4-protocol=tcp for the reject ACL.
> > > > > > > +rej_acl=$(ovn-nbctl --bare --columns _uuid  find ACL
> action=reject)
> > > > > > > +ovn-nbctl --wait=sb set ACL $rej_acl options:l3-protocol=ip
> > > > > > > +ovn-nbctl --wait=sb set ACL $rej_acl options:l4-protocol=tcp
> > > > > > > +
> > > > > > > +# ovn-northd should generate 4 lflow with 2 ip4 tcp_reset and
> 2 ip6
> > > > > > tcp_reset actions.
> > > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
> "ls_out_acl.*priority=2012" |
> > > > > > sort], [0], [dnl
> > > > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > > > +match=((reg0[[10]] == 1) && ip4 && tcp && (inport == @pg0 &&
> ip)),
> > > dnl
> > > > > > > +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0;
> eth.dst <->
> > > > > > eth.src; ip4.dst <-> ip4.src; tcp_reset { outport <-> inport;
> > > > > > next(pipeline=ingress,table=20); };)
> > > > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > > > +match=((reg0[[10]] == 1) && ip6 && tcp && (inport == @pg0 &&
> ip)),
> > > dnl
> > > > > > > +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0;
> eth.dst <->
> > > > > > eth.src; ip6.dst <-> ip6.src; tcp_reset { outport <-> inport;
> > > > > > next(pipeline=ingress,table=20); };)
> > > > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > > > +match=((reg0[[9]] == 1) && ip4 && tcp && (inport == @pg0 &&
> ip)),
> > > dnl
> > > > > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> > > tcp_reset {
> > > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > > > +match=((reg0[[9]] == 1) && ip6 && tcp && (inport == @pg0 &&
> ip)),
> > > dnl
> > > > > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src;
> > > tcp_reset {
> > > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > > +])
> > > > > > > +
> > > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
> "ls_out_acl.*priority=2002" |
> > > > > > sort], [0], [dnl
> > > > > > > +])
> > > > > > > +
> > > > > > > +
> > > > > > > +# Clear l3-protocol and set l4-protocol to udp
> > > > > > > +ovn-nbctl --wait=sb remove ACL $rej_acl options l3-protocol
> > > > > > > +ovn-nbctl --wait=sb set ACL $rej_acl options:l4-protocol=udp
> > > > > > > +
> > > > > > > +# ovn-northd should generate 4 lflows (with udp match) with 2
> > > icmp4 and
> > > > > > 2 icmp6 actions.
> > > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
> "ls_out_acl.*priority=2012" |
> > > > > > sort], [0], [dnl
> > > > > > > +])
> > > > > > > +
> > > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
> "ls_out_acl.*priority=2002" |
> > > > > > sort], [0], [dnl
> > > > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > > > +match=((reg0[[10]] == 1) && ip4 && (inport == @pg0 && ip) &&
> udp),
> > > dnl\
> > > > > > > +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; icmp4 {
> > > eth.dst
> > > > > > <-> eth.src; ip4.dst <-> ip4.src; outport <-> inport;
> > > > > > next(pipeline=ingress,table=20); };)
> > > > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > > > +match=((reg0[[10]] == 1) && ip6 && (inport == @pg0 && ip) &&
> udp),
> > > dnl
> > > > > > > +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; icmp6 {
> > > eth.dst
> > > > > > <-> eth.src; ip6.dst <-> ip6.src; outport <-> inport;
> > > > > > next(pipeline=ingress,table=20); };)
> > > > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > > > +match=((reg0[[9]] == 1) && ip4 && (inport == @pg0 && ip) &&
> udp),
> > > dnl
> > > > > > > +action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <->
> ip4.src;
> > > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > > +  table=5 (ls_out_acl         ), priority=2002 , dnl
> > > > > > > +match=((reg0[[9]] == 1) && ip6 && (inport == @pg0 && ip) &&
> udp),
> > > dnl
> > > > > > > +action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <->
> ip6.src;
> > > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > > +])
> > > > > > > +
> > > > > > > +# Clear l3-protocol and set l4-protocol to tcp
> > > > > > > +ovn-nbctl --wait=sb remove ACL $rej_acl options l3-protocol
> > > > > > > +ovn-nbctl --wait=sb set ACL $rej_acl options:l4-protocol=tcp
> > > > > > > +
> > > > > > > +# ovn-northd should generate 4 lflow with 2 ip4 tcp_reset and
> 2 ip6
> > > > > > tcp_reset actions.
> > > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
> "ls_out_acl.*priority=2012" |
> > > > > > sort], [0], [dnl
> > > > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > > > +match=((reg0[[10]] == 1) && ip4 && tcp && (inport == @pg0 &&
> ip)),
> > > dnl
> > > > > > > +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0;
> eth.dst <->
> > > > > > eth.src; ip4.dst <-> ip4.src; tcp_reset { outport <-> inport;
> > > > > > next(pipeline=ingress,table=20); };)
> > > > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > > > +match=((reg0[[10]] == 1) && ip6 && tcp && (inport == @pg0 &&
> ip)),
> > > dnl
> > > > > > > +action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0;
> eth.dst <->
> > > > > > eth.src; ip6.dst <-> ip6.src; tcp_reset { outport <-> inport;
> > > > > > next(pipeline=ingress,table=20); };)
> > > > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > > > +match=((reg0[[9]] == 1) && ip4 && tcp && (inport == @pg0 &&
> ip)),
> > > dnl
> > > > > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src;
> > > tcp_reset {
> > > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > > +  table=5 (ls_out_acl         ), priority=2012 , dnl
> > > > > > > +match=((reg0[[9]] == 1) && ip6 && tcp && (inport == @pg0 &&
> ip)),
> > > dnl
> > > > > > > +action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src;
> > > tcp_reset {
> > > > > > outport <-> inport; next(pipeline=ingress,table=20); };)
> > > > > > > +])
> > > > > > > +
> > > > > > > +AT_CHECK([ovn-sbctl lflow-list | grep
> "ls_out_acl.*priority=2002" |
> > > > > > sort], [0], [dnl
> > > > > > > +])
> > > > > > > +
> > > > > > > +AT_CLEANUP
> > > > > > > --
> > > > > > > 2.26.2
> > > > > > >
> > > > > > > _______________________________________________
> > > > > > > dev mailing list
> > > > > > > dev@openvswitch.org
> > > > > > > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> > > > > > _______________________________________________
> > > > > > dev mailing list
> > > > > > dev@openvswitch.org
> > > > > > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> > > > > >
> > > _______________________________________________
> > > dev mailing list
> > > dev@openvswitch.org
> > > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> > >
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
diff mbox series

Patch

diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
index 3324c9e81d..d167137e07 100644
--- a/northd/ovn-northd.c
+++ b/northd/ovn-northd.c
@@ -5372,73 +5372,135 @@  build_reject_acl_rules(struct ovn_datapath *od, struct hmap *lflows,
     struct ds actions = DS_EMPTY_INITIALIZER;
     bool ingress = (stage == S_SWITCH_IN_ACL);
 
-    /* TCP */
-    build_acl_log(&actions, acl);
-    if (extra_match->length > 0) {
-        ds_put_format(&match, "(%s) && ", extra_match->string);
-    }
-    ds_put_format(&match, "ip4 && tcp && (%s)", acl->match);
-    ds_put_format(&actions, "reg0 = 0; "
-                  "eth.dst <-> eth.src; ip4.dst <-> ip4.src; "
-                  "tcp_reset { outport <-> inport; %s };",
-                  ingress ? "next(pipeline=egress,table=5);"
-                          : "next(pipeline=ingress,table=20);");
-    ovn_lflow_add_with_hint(lflows, od, stage,
-                            acl->priority + OVN_ACL_PRI_OFFSET + 10,
-                            ds_cstr(&match), ds_cstr(&actions), stage_hint);
-    ds_clear(&match);
-    ds_clear(&actions);
-    build_acl_log(&actions, acl);
-    if (extra_match->length > 0) {
-        ds_put_format(&match, "(%s) && ", extra_match->string);
-    }
-    ds_put_format(&match, "ip6 && tcp && (%s)", acl->match);
-    ds_put_format(&actions, "reg0 = 0; "
-                  "eth.dst <-> eth.src; ip6.dst <-> ip6.src; "
-                  "tcp_reset { outport <-> inport; %s };",
-                  ingress ? "next(pipeline=egress,table=5);"
-                          : "next(pipeline=ingress,table=20);");
-    ovn_lflow_add_with_hint(lflows, od, stage,
-                            acl->priority + OVN_ACL_PRI_OFFSET + 10,
-                            ds_cstr(&match), ds_cstr(&actions), stage_hint);
+    bool is_ip4 = true;
+    bool is_ip6 = true;
+    bool tcp_reset = true;
+    bool icmp_reset = true;
+    bool is_udp = false;
+
+    const char *l3_protocol = smap_get(&acl->options, "l3-protocol");
+    if (l3_protocol) {
+        if (!strcasecmp(l3_protocol, "ip")) {
+            is_ip4 = true;
+            is_ip6 = true;
+        } else if (!strcasecmp(l3_protocol, "ip4")) {
+            is_ip4 = true;
+            is_ip6 = false;
+        } else if (!strcasecmp(l3_protocol, "ip6")) {
+            is_ip6 = true;
+            is_ip4 = false;
+        }
+    }
+
+    const char *l4_protocol = smap_get(&acl->options, "l4-protocol");
+    if (l4_protocol) {
+        if (!strcasecmp(l4_protocol, "tcp")) {
+            tcp_reset = true;
+            icmp_reset = false;
+        } else if (!strcasecmp(l4_protocol, "udp")) {
+            tcp_reset = false;
+            is_udp = true;
+        } else {
+            tcp_reset = false;
+        }
+    }
 
-    /* IP traffic */
-    ds_clear(&match);
-    ds_clear(&actions);
-    build_acl_log(&actions, acl);
-    if (extra_match->length > 0) {
-        ds_put_format(&match, "(%s) && ", extra_match->string);
-    }
-    ds_put_format(&match, "ip4 && (%s)", acl->match);
-    if (extra_actions->length > 0) {
-        ds_put_format(&actions, "%s ", extra_actions->string);
-    }
-    ds_put_format(&actions, "reg0 = 0; "
-                  "icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src; "
-                  "outport <-> inport; %s };",
-                  ingress ? "next(pipeline=egress,table=5);"
-                          : "next(pipeline=ingress,table=20);");
-    ovn_lflow_add_with_hint(lflows, od, stage,
-                            acl->priority + OVN_ACL_PRI_OFFSET,
-                            ds_cstr(&match), ds_cstr(&actions), stage_hint);
-    ds_clear(&match);
-    ds_clear(&actions);
-    build_acl_log(&actions, acl);
-    if (extra_match->length > 0) {
-        ds_put_format(&match, "(%s) && ", extra_match->string);
-    }
-    ds_put_format(&match, "ip6 && (%s)", acl->match);
-    if (extra_actions->length > 0) {
-        ds_put_format(&actions, "%s ", extra_actions->string);
-    }
-    ds_put_format(&actions, "reg0 = 0; icmp6 { "
-                  "eth.dst <-> eth.src; ip6.dst <-> ip6.src; "
-                  "outport <-> inport; %s };",
-                  ingress ? "next(pipeline=egress,table=5);"
-                          : "next(pipeline=ingress,table=20);");
-    ovn_lflow_add_with_hint(lflows, od, stage,
-                            acl->priority + OVN_ACL_PRI_OFFSET,
-                            ds_cstr(&match), ds_cstr(&actions), stage_hint);
+    if (is_ip4) {
+        if (tcp_reset) {
+            build_acl_log(&actions, acl);
+            if (extra_match->length > 0) {
+                ds_put_format(&match, "(%s) && ", extra_match->string);
+            }
+            ds_put_format(&match, "ip4 && tcp && (%s)", acl->match);
+            if (extra_actions->length > 0) {
+                ds_put_format(&actions, "%s ", extra_actions->string);
+            }
+            ds_put_format(&actions, "reg0 = 0; "
+                          "eth.dst <-> eth.src; ip4.dst <-> ip4.src; "
+                          "tcp_reset { outport <-> inport; %s };",
+                          ingress ? "next(pipeline=egress,table=5);"
+                                  : "next(pipeline=ingress,table=20);");
+            ovn_lflow_add_with_hint(lflows, od, stage,
+                                    acl->priority + OVN_ACL_PRI_OFFSET + 10,
+                                    ds_cstr(&match), ds_cstr(&actions),
+                                    stage_hint);
+        }
+
+        if (icmp_reset) {
+            ds_clear(&match);
+            ds_clear(&actions);
+            build_acl_log(&actions, acl);
+            if (extra_match->length > 0) {
+                ds_put_format(&match, "(%s) && ", extra_match->string);
+            }
+            ds_put_format(&match, "ip4 && (%s)", acl->match);
+            if (is_udp) {
+                ds_put_cstr(&match, " && udp");
+            }
+            if (extra_actions->length > 0) {
+                ds_put_format(&actions, "%s ", extra_actions->string);
+            }
+            ds_put_format(&actions, "reg0 = 0; "
+                          "icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src; "
+                          "outport <-> inport; %s };",
+                          ingress ? "next(pipeline=egress,table=5);"
+                                  : "next(pipeline=ingress,table=20);");
+            ovn_lflow_add_with_hint(lflows, od, stage,
+                                    acl->priority + OVN_ACL_PRI_OFFSET,
+                                    ds_cstr(&match), ds_cstr(&actions),
+                                    stage_hint);
+        }
+    }
+
+    if (is_ip6) {
+        if (tcp_reset) {
+            ds_clear(&match);
+            ds_clear(&actions);
+            build_acl_log(&actions, acl);
+            if (extra_match->length > 0) {
+                ds_put_format(&match, "(%s) && ", extra_match->string);
+            }
+            ds_put_format(&match, "ip6 && tcp && (%s)", acl->match);
+            if (extra_actions->length > 0) {
+                ds_put_format(&actions, "%s ", extra_actions->string);
+            }
+            ds_put_format(&actions, "reg0 = 0; "
+                          "eth.dst <-> eth.src; ip6.dst <-> ip6.src; "
+                          "tcp_reset { outport <-> inport; %s };",
+                          ingress ? "next(pipeline=egress,table=5);"
+                                  : "next(pipeline=ingress,table=20);");
+            ovn_lflow_add_with_hint(lflows, od, stage,
+                                    acl->priority + OVN_ACL_PRI_OFFSET + 10,
+                                    ds_cstr(&match), ds_cstr(&actions),
+                                    stage_hint);
+        }
+
+        if (icmp_reset) {
+            ds_clear(&match);
+            ds_clear(&actions);
+            build_acl_log(&actions, acl);
+            if (extra_match->length > 0) {
+                ds_put_format(&match, "(%s) && ", extra_match->string);
+            }
+            ds_put_format(&match, "ip6 && (%s)", acl->match);
+            if (is_udp) {
+                ds_put_cstr(&match, " && udp");
+            }
+
+            if (extra_actions->length > 0) {
+                ds_put_format(&actions, "%s ", extra_actions->string);
+            }
+            ds_put_format(&actions, "reg0 = 0; icmp6 { "
+                          "eth.dst <-> eth.src; ip6.dst <-> ip6.src; "
+                          "outport <-> inport; %s };",
+                          ingress ? "next(pipeline=egress,table=5);"
+                                : "next(pipeline=ingress,table=20);");
+            ovn_lflow_add_with_hint(lflows, od, stage,
+                                    acl->priority + OVN_ACL_PRI_OFFSET,
+                                    ds_cstr(&match), ds_cstr(&actions),
+                                    stage_hint);
+        }
+    }
 
     ds_destroy(&match);
     ds_destroy(&actions);
diff --git a/ovn-nb.ovsschema b/ovn-nb.ovsschema
index 092322ab2c..00f1c7cd4b 100644
--- a/ovn-nb.ovsschema
+++ b/ovn-nb.ovsschema
@@ -1,7 +1,7 @@ 
 {
     "name": "OVN_Northbound",
-    "version": "5.27.0",
-    "cksum": "3507518247 26773",
+    "version": "5.28.0",
+    "cksum": "699859908 26928",
     "tables": {
         "NB_Global": {
             "columns": {
@@ -225,6 +225,9 @@ 
                                                         "debug"]]},
                                       "min": 0, "max": 1}},
                 "meter": {"type": {"key": "string", "min": 0, "max": 1}},
+                "options": {
+                    "type": {"key": "string", "value": "string",
+                             "min": 0, "max": "unlimited"}},
                 "external_ids": {
                     "type": {"key": "string", "value": "string",
                              "min": 0, "max": "unlimited"}}},
diff --git a/ovn-nb.xml b/ovn-nb.xml
index 86195af341..2c3497e2ae 100644
--- a/ovn-nb.xml
+++ b/ovn-nb.xml
@@ -1721,6 +1721,49 @@ 
       </ul>
     </column>
 
+    <group title="Common options">
+      <column name="options">
+        This column provides general key/value settings. The supported
+        options are described individually below.
+      </column>
+
+      <group title="Options for providing protocol hints for reject ACL.">
+        <p>
+          The ACL match specified in the
+          <ref column="match" table="ACL" db="OVN_Northbound"/> column is
+          opaque to <code>OVN</code> and <code>ovn-northd</code> doesn't
+          look into the match fields.
+          These options can be specified by CMS to provide hints to
+          <code>OVN</code> about the L3 and L4 protocol matches for the reject
+          ACL. <code>ovn-northd</code> uses these options if set to use
+          appropriate actions when generating logical flows.
+        </p>
+
+        <p>
+          If these options are not set, then <code>ovn-northd</code> assumes
+          the reject ACL applies to IPv4, IPv6 and TCP packets.
+        </p>
+
+        <column name="options" key="l3-protocol">
+          The possible values are <code>ip</code>, <code>ip4</code> and
+          <code>ip6</code>. If the value is <code>ip</code>, it means the
+          reject ACL action applies to both IPv4 and IPv6 packets. If the
+          value is <code>ip4</code>, it means the reject ACL action applies to
+          IPv4 packets and the value <code>ip6</code> applies to IPv6 packets.
+        </column>
+
+        <column name="options" key="l4-protocol">
+          <p>
+            The possible values are <code>tcp</code> and <code>udp</code>.
+            If the value is <code>tcp</code>, it means the
+            reject ACL action applies to TCP packets. If the
+            value is <code>udp</code>, it means the reject ACL action applies
+            to UDP packets.
+          </p>
+        </column>
+      </group>
+    </group>
+
     <group title="Logging">
       <p>
         These columns control whether and how OVN logs packets that match an
diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
index 99a9204f1f..4a30f8ed07 100644
--- a/tests/ovn-northd.at
+++ b/tests/ovn-northd.at
@@ -2010,3 +2010,288 @@  ovn-nbctl --wait=sb set NB_Global . options:ignore_lsp_down=true
 AT_CHECK([ovn-sbctl lflow-list | grep arp | grep 10\.0\.0\.1], [0], [ignore])
 
 AT_CLEANUP
+
+AT_SETUP([ovn-northd -- ACL reject flows])
+ovn_start
+
+ovn-nbctl ls-add sw0
+ovn-nbctl lsp-add sw0 sw0-p1
+ovn-nbctl lsp-add sw0 sw0-p2
+
+ovn-nbctl pg-add pg0 sw0-p1 sw0-p2
+ovn-nbctl acl-add pg0 to-lport 1002 "inport == @pg0 && ip" reject
+
+ovn-nbctl --wait=sb sync
+
+# If there is a reject ACL wihtout any protocol hints, then ovn-northd should
+# generate lflows with ip4 tcp_reset, icmp4, ip6 tcp_reset and icmp6 actions.
+
+AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" | sort], [0], [dnl
+  table=5 (ls_out_acl         ), priority=2012 , dnl
+match=(ip4 && tcp && (inport == @pg0 && ip)), dnl
+action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src; tcp_reset { outport <-> inport; next(pipeline=ingress,table=20); };)
+  table=5 (ls_out_acl         ), priority=2012 , dnl
+match=(ip6 && tcp && (inport == @pg0 && ip)), dnl
+action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src; tcp_reset { outport <-> inport; next(pipeline=ingress,table=20); };)
+])
+
+AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" | sort], [0], [dnl
+  table=5 (ls_out_acl         ), priority=2002 , dnl
+match=(ip4 && (inport == @pg0 && ip)), dnl
+action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src; outport <-> inport; next(pipeline=ingress,table=20); };)
+  table=5 (ls_out_acl         ), priority=2002 , dnl
+match=(ip6 && (inport == @pg0 && ip)), dnl
+action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <-> ip6.src; outport <-> inport; next(pipeline=ingress,table=20); };)
+])
+
+# Set l3-protocol=ip4 for the reject ACL.
+ovn-nbctl --wait=sb set ACL . options:l3-protocol=ip4
+
+# ovn-northd should generate  2 lflows with ip4 tcp_reset and icmp4 actions.
+AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" | sort], [0], [dnl
+  table=5 (ls_out_acl         ), priority=2012 , dnl
+match=(ip4 && tcp && (inport == @pg0 && ip)), dnl
+action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src; tcp_reset { outport <-> inport; next(pipeline=ingress,table=20); };)
+])
+
+AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" | sort], [0], [dnl
+  table=5 (ls_out_acl         ), priority=2002 , dnl
+match=(ip4 && (inport == @pg0 && ip)), dnl
+action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src; outport <-> inport; next(pipeline=ingress,table=20); };)
+])
+
+# Set l4-protocol=tcp for the reject ACL.
+ovn-nbctl --wait=sb set ACL . options:l4-protocol=tcp
+
+# ovn-northd should generate 1 lflow with ip4 tcp_reset action.
+AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" | sort], [0], [dnl
+  table=5 (ls_out_acl         ), priority=2012 , dnl
+match=(ip4 && tcp && (inport == @pg0 && ip)), dnl
+action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src; tcp_reset { outport <-> inport; next(pipeline=ingress,table=20); };)
+])
+
+AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" | sort], [0], [dnl
+])
+
+# Set l4-protocol=udp for the reject ACL.
+ovn-nbctl --wait=sb set ACL . options:l4-protocol=udp
+
+# ovn-northd should generate 1 lflow with udp match and icmp4 action.
+AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" | sort], [0], [dnl
+])
+
+AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" | sort], [0], [dnl
+  table=5 (ls_out_acl         ), priority=2002 , dnl
+match=(ip4 && (inport == @pg0 && ip) && udp), dnl
+action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src; outport <-> inport; next(pipeline=ingress,table=20); };)
+])
+
+# Remove l4-protocol from the reject ACL.
+ovn-nbctl --wait=sb remove ACL . options l4-protocol
+
+# ovn-northd should generate 2 lflow with tcp_reset and icmp4 action.
+AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" | sort], [0], [dnl
+  table=5 (ls_out_acl         ), priority=2012 , dnl
+match=(ip4 && tcp && (inport == @pg0 && ip)), dnl
+action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src; tcp_reset { outport <-> inport; next(pipeline=ingress,table=20); };)
+])
+
+AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" | sort], [0], [dnl
+  table=5 (ls_out_acl         ), priority=2002 , dnl
+match=(ip4 && (inport == @pg0 && ip)), dnl
+action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src; outport <-> inport; next(pipeline=ingress,table=20); };)
+])
+
+# Set l3-protocol to ip.
+ovn-nbctl --wait=sb set ACL . options:l3-protocol=ip
+
+# ovn-northd should generate 4 lflow with ip4 tcp_reset, ip6 tcp_reset, icmp4 and icmp6 actions.
+
+AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" | sort], [0], [dnl
+  table=5 (ls_out_acl         ), priority=2012 , dnl
+match=(ip4 && tcp && (inport == @pg0 && ip)), dnl
+action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src; tcp_reset { outport <-> inport; next(pipeline=ingress,table=20); };)
+  table=5 (ls_out_acl         ), priority=2012 , dnl
+match=(ip6 && tcp && (inport == @pg0 && ip)), dnl
+action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src; tcp_reset { outport <-> inport; next(pipeline=ingress,table=20); };)
+])
+
+AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" | sort], [0], [dnl
+  table=5 (ls_out_acl         ), priority=2002 , dnl
+match=(ip4 && (inport == @pg0 && ip)), dnl
+action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src; outport <-> inport; next(pipeline=ingress,table=20); };)
+  table=5 (ls_out_acl         ), priority=2002 , dnl
+match=(ip6 && (inport == @pg0 && ip)), dnl
+action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <-> ip6.src; outport <-> inport; next(pipeline=ingress,table=20); };)
+])
+
+# Set l3-protocol=ip6 for the reject ACL.
+ovn-nbctl --wait=sb set ACL . options:l3-protocol=ip6
+
+# ovn-northd should generate  2 lflows with ip6 tcp_reset and icmp6 actions.
+AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" | sort], [0], [dnl
+  table=5 (ls_out_acl         ), priority=2012 , dnl
+match=(ip6 && tcp && (inport == @pg0 && ip)), dnl
+action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src; tcp_reset { outport <-> inport; next(pipeline=ingress,table=20); };)
+])
+
+AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" | sort], [0], [dnl
+  table=5 (ls_out_acl         ), priority=2002 , dnl
+match=(ip6 && (inport == @pg0 && ip)), dnl
+action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <-> ip6.src; outport <-> inport; next(pipeline=ingress,table=20); };)
+])
+
+# Set l4-protocol=tcp for the reject ACL.
+ovn-nbctl --wait=sb set ACL . options:l4-protocol=tcp
+
+# ovn-northd should generate 1 lflow with ip6 tcp_reset action.
+AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" | sort], [0], [dnl
+  table=5 (ls_out_acl         ), priority=2012 , dnl
+match=(ip6 && tcp && (inport == @pg0 && ip)), dnl
+action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src; tcp_reset { outport <-> inport; next(pipeline=ingress,table=20); };)
+])
+
+AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" | sort], [0], [dnl
+])
+
+# Set l4-protocol=udp for the reject ACL.
+ovn-nbctl --wait=sb set ACL . options:l4-protocol=udp
+
+# ovn-northd should generate 1 lflow with udp match and icmp6 action.
+AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" | sort], [0], [dnl
+])
+
+AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" | sort], [0], [dnl
+  table=5 (ls_out_acl         ), priority=2002 , dnl
+match=(ip6 && (inport == @pg0 && ip) && udp), dnl
+action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <-> ip6.src; outport <-> inport; next(pipeline=ingress,table=20); };)
+])
+
+# Set l3-protocol to ip and l4-protocol=tcp for the reject ACL.
+ovn-nbctl --wait=sb set ACL . options:l3-protocol=ip
+ovn-nbctl --wait=sb set ACL . options:l4-protocol=tcp
+
+# ovn-northd should generate 2 lflow with ip4 tcp_reset and ip6 tcp_reset actions.
+AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" | sort], [0], [dnl
+  table=5 (ls_out_acl         ), priority=2012 , dnl
+match=(ip4 && tcp && (inport == @pg0 && ip)), dnl
+action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src; tcp_reset { outport <-> inport; next(pipeline=ingress,table=20); };)
+  table=5 (ls_out_acl         ), priority=2012 , dnl
+match=(ip6 && tcp && (inport == @pg0 && ip)), dnl
+action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src; tcp_reset { outport <-> inport; next(pipeline=ingress,table=20); };)
+])
+
+AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" | sort], [0], [dnl
+])
+
+# Set l3-protocol to ip and l4-protocol=udp for the reject ACL.
+ovn-nbctl --wait=sb set ACL . options:l3-protocol=ip
+ovn-nbctl --wait=sb set ACL . options:l4-protocol=udp
+
+# ovn-northd should generate 2 lflows (udp match) with icmp4 and icmp6 actions.
+AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" | sort], [0], [dnl
+])
+
+AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" | sort], [0], [dnl
+  table=5 (ls_out_acl         ), priority=2002 , dnl
+match=(ip4 && (inport == @pg0 && ip) && udp), dnl
+action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src; outport <-> inport; next(pipeline=ingress,table=20); };)
+  table=5 (ls_out_acl         ), priority=2002 , dnl
+match=(ip6 && (inport == @pg0 && ip) && udp), dnl
+action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <-> ip6.src; outport <-> inport; next(pipeline=ingress,table=20); };)
+])
+
+# Add an ACL with allow-related
+ovn-nbctl --wait=sb acl-add pg0 to-lport 1000 "ip" allow-related
+
+# ovn-northd should generate 4 lflow with 2 icmp4 and 2 icmp6 actions.
+AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" | sort], [0], [dnl
+])
+
+AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" | sort], [0], [dnl
+  table=5 (ls_out_acl         ), priority=2002 , dnl
+match=((reg0[[10]] == 1) && ip4 && (inport == @pg0 && ip) && udp), dnl\
+action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src; outport <-> inport; next(pipeline=ingress,table=20); };)
+  table=5 (ls_out_acl         ), priority=2002 , dnl
+match=((reg0[[10]] == 1) && ip6 && (inport == @pg0 && ip) && udp), dnl
+action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <-> ip6.src; outport <-> inport; next(pipeline=ingress,table=20); };)
+  table=5 (ls_out_acl         ), priority=2002 , dnl
+match=((reg0[[9]] == 1) && ip4 && (inport == @pg0 && ip) && udp), dnl
+action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src; outport <-> inport; next(pipeline=ingress,table=20); };)
+  table=5 (ls_out_acl         ), priority=2002 , dnl
+match=((reg0[[9]] == 1) && ip6 && (inport == @pg0 && ip) && udp), dnl
+action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <-> ip6.src; outport <-> inport; next(pipeline=ingress,table=20); };)
+])
+
+# Set l3-protocol to ip and l4-protocol=tcp for the reject ACL.
+rej_acl=$(ovn-nbctl --bare --columns _uuid  find ACL action=reject)
+ovn-nbctl --wait=sb set ACL $rej_acl options:l3-protocol=ip
+ovn-nbctl --wait=sb set ACL $rej_acl options:l4-protocol=tcp
+
+# ovn-northd should generate 4 lflow with 2 ip4 tcp_reset and 2 ip6 tcp_reset actions.
+AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" | sort], [0], [dnl
+  table=5 (ls_out_acl         ), priority=2012 , dnl
+match=((reg0[[10]] == 1) && ip4 && tcp && (inport == @pg0 && ip)), dnl
+action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src; tcp_reset { outport <-> inport; next(pipeline=ingress,table=20); };)
+  table=5 (ls_out_acl         ), priority=2012 , dnl
+match=((reg0[[10]] == 1) && ip6 && tcp && (inport == @pg0 && ip)), dnl
+action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src; tcp_reset { outport <-> inport; next(pipeline=ingress,table=20); };)
+  table=5 (ls_out_acl         ), priority=2012 , dnl
+match=((reg0[[9]] == 1) && ip4 && tcp && (inport == @pg0 && ip)), dnl
+action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src; tcp_reset { outport <-> inport; next(pipeline=ingress,table=20); };)
+  table=5 (ls_out_acl         ), priority=2012 , dnl
+match=((reg0[[9]] == 1) && ip6 && tcp && (inport == @pg0 && ip)), dnl
+action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src; tcp_reset { outport <-> inport; next(pipeline=ingress,table=20); };)
+])
+
+AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" | sort], [0], [dnl
+])
+
+
+# Clear l3-protocol and set l4-protocol to udp
+ovn-nbctl --wait=sb remove ACL $rej_acl options l3-protocol
+ovn-nbctl --wait=sb set ACL $rej_acl options:l4-protocol=udp
+
+# ovn-northd should generate 4 lflows (with udp match) with 2 icmp4 and 2 icmp6 actions.
+AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" | sort], [0], [dnl
+])
+
+AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" | sort], [0], [dnl
+  table=5 (ls_out_acl         ), priority=2002 , dnl
+match=((reg0[[10]] == 1) && ip4 && (inport == @pg0 && ip) && udp), dnl\
+action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src; outport <-> inport; next(pipeline=ingress,table=20); };)
+  table=5 (ls_out_acl         ), priority=2002 , dnl
+match=((reg0[[10]] == 1) && ip6 && (inport == @pg0 && ip) && udp), dnl
+action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <-> ip6.src; outport <-> inport; next(pipeline=ingress,table=20); };)
+  table=5 (ls_out_acl         ), priority=2002 , dnl
+match=((reg0[[9]] == 1) && ip4 && (inport == @pg0 && ip) && udp), dnl
+action=(reg0 = 0; icmp4 { eth.dst <-> eth.src; ip4.dst <-> ip4.src; outport <-> inport; next(pipeline=ingress,table=20); };)
+  table=5 (ls_out_acl         ), priority=2002 , dnl
+match=((reg0[[9]] == 1) && ip6 && (inport == @pg0 && ip) && udp), dnl
+action=(reg0 = 0; icmp6 { eth.dst <-> eth.src; ip6.dst <-> ip6.src; outport <-> inport; next(pipeline=ingress,table=20); };)
+])
+
+# Clear l3-protocol and set l4-protocol to tcp
+ovn-nbctl --wait=sb remove ACL $rej_acl options l3-protocol
+ovn-nbctl --wait=sb set ACL $rej_acl options:l4-protocol=tcp
+
+# ovn-northd should generate 4 lflow with 2 ip4 tcp_reset and 2 ip6 tcp_reset actions.
+AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2012" | sort], [0], [dnl
+  table=5 (ls_out_acl         ), priority=2012 , dnl
+match=((reg0[[10]] == 1) && ip4 && tcp && (inport == @pg0 && ip)), dnl
+action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src; tcp_reset { outport <-> inport; next(pipeline=ingress,table=20); };)
+  table=5 (ls_out_acl         ), priority=2012 , dnl
+match=((reg0[[10]] == 1) && ip6 && tcp && (inport == @pg0 && ip)), dnl
+action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src; tcp_reset { outport <-> inport; next(pipeline=ingress,table=20); };)
+  table=5 (ls_out_acl         ), priority=2012 , dnl
+match=((reg0[[9]] == 1) && ip4 && tcp && (inport == @pg0 && ip)), dnl
+action=(reg0 = 0; eth.dst <-> eth.src; ip4.dst <-> ip4.src; tcp_reset { outport <-> inport; next(pipeline=ingress,table=20); };)
+  table=5 (ls_out_acl         ), priority=2012 , dnl
+match=((reg0[[9]] == 1) && ip6 && tcp && (inport == @pg0 && ip)), dnl
+action=(reg0 = 0; eth.dst <-> eth.src; ip6.dst <-> ip6.src; tcp_reset { outport <-> inport; next(pipeline=ingress,table=20); };)
+])
+
+AT_CHECK([ovn-sbctl lflow-list | grep "ls_out_acl.*priority=2002" | sort], [0], [dnl
+])
+
+AT_CLEANUP