diff mbox series

[ovs-dev] northd: Add option to enable conntrack for router port

Message ID 20240110164010.1810381-1-numans@ovn.org
State Accepted
Headers show
Series [ovs-dev] northd: Add option to enable conntrack for router port | expand

Checks

Context Check Description
ovsrobot/apply-robot warning apply and check: warning
ovsrobot/github-robot-_Build_and_Test success github build: passed
ovsrobot/github-robot-_ovn-kubernetes success github build: passed

Commit Message

Numan Siddique Jan. 10, 2024, 4:40 p.m. UTC
From: shylou <liuxie_11@163.com>

By default, OVN skips the conntrack process for router type
LSP within a LS. It seems unnecessary for the LSP whose peer
is l3dgw_port.

Therefore, we introduce an option named 'enable_router_port_acl',
which defaults to false and can be set to true to enable
conntrack for the LSP whose peer is l3dgw_port.

And then we can implement a gateway stateful firewall by
dgw with stateful ACL. For example:

 prelude: R1-S1 is a l3dgw_port
 ovn-nbctl pg-add pg_dgw
 ovn-nbctl pg-set-ports pg_dgw S1-R1
 ovn-nbctl acl-add pg_dgw from-lport 1002 "inport == @pg_dgw && ip4" allow-related
 ovn-nbctl acl-add pg_dgw to-lport 1003 "outport == @pg_dgw && ip4" allow-related
 ovn-nbctl lsp-set-options S1-R1 router-port=R1-S1 enable_router_port_acl=true

NOTE: this option only works for the LSP whose peer is l3dgw_port.

Submitted-at: https://github.com/ovn-org/ovn/pull/226
Signed-off-by: Xie Liu <liushyshy@gmail.com>
Acked-by: Dumitru Ceara <dceara@redhat.com>
Signed-off-by: Numan Siddique <numans@ovn.org>
---
 NEWS                    |   2 +
 northd/northd.c         |  15 ++++-
 northd/ovn-northd.8.xml |   7 ++-
 ovn-nb.xml              |   8 +++
 tests/ovn-northd.at     | 124 ++++++++++++++++++++++++++++++++++++++++
 5 files changed, 154 insertions(+), 2 deletions(-)

Comments

Numan Siddique Jan. 10, 2024, 8:02 p.m. UTC | #1
On Wed, Jan 10, 2024 at 11:41 AM <numans@ovn.org> wrote:
>
> From: shylou <liuxie_11@163.com>
>
> By default, OVN skips the conntrack process for router type
> LSP within a LS. It seems unnecessary for the LSP whose peer
> is l3dgw_port.
>
> Therefore, we introduce an option named 'enable_router_port_acl',
> which defaults to false and can be set to true to enable
> conntrack for the LSP whose peer is l3dgw_port.
>
> And then we can implement a gateway stateful firewall by
> dgw with stateful ACL. For example:
>
>  prelude: R1-S1 is a l3dgw_port
>  ovn-nbctl pg-add pg_dgw
>  ovn-nbctl pg-set-ports pg_dgw S1-R1
>  ovn-nbctl acl-add pg_dgw from-lport 1002 "inport == @pg_dgw && ip4" allow-related
>  ovn-nbctl acl-add pg_dgw to-lport 1003 "outport == @pg_dgw && ip4" allow-related
>  ovn-nbctl lsp-set-options S1-R1 router-port=R1-S1 enable_router_port_acl=true
>
> NOTE: this option only works for the LSP whose peer is l3dgw_port.
>
> Submitted-at: https://github.com/ovn-org/ovn/pull/226
> Signed-off-by: Xie Liu <liushyshy@gmail.com>
> Acked-by: Dumitru Ceara <dceara@redhat.com>
> Signed-off-by: Numan Siddique <numans@ovn.org>

Thanks.  Applied the patch to the main branch.

Numan

> ---
>  NEWS                    |   2 +
>  northd/northd.c         |  15 ++++-
>  northd/ovn-northd.8.xml |   7 ++-
>  ovn-nb.xml              |   8 +++
>  tests/ovn-northd.at     | 124 ++++++++++++++++++++++++++++++++++++++++
>  5 files changed, 154 insertions(+), 2 deletions(-)
>
> diff --git a/NEWS b/NEWS
> index 20df92cb76..5f267b4c64 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -12,6 +12,8 @@ Post v23.09.0
>    - Support CIDR based MAC binding aging threshold. See ovn-nb(5) for
>      'mac_binding_age_threshold' for more details.
>    - ovn-northd-ddlog has been removed.
> +  - A new LSP option "enable_router_port_acl" has been added to enable
> +    conntrack for the router port whose peer is l3dgw_port if set it true.
>
>  OVN v23.09.0 - 15 Sep 2023
>  --------------------------
> diff --git a/northd/northd.c b/northd/northd.c
> index db3cd272e1..952f8200d4 100644
> --- a/northd/northd.c
> +++ b/northd/northd.c
> @@ -1612,6 +1612,9 @@ struct ovn_port {
>       * access it from any other nodes.
>       */
>      struct ovs_list lflows;
> +
> +    /* Only used for the router type LSP whose peer is l3dgw_port */
> +    bool enable_router_port_acl;
>  };
>
>  static bool lsp_can_be_inc_processed(const struct nbrec_logical_switch_port *);
> @@ -2826,6 +2829,12 @@ join_logical_ports(const struct sbrec_port_binding_table *sbrec_pb_table,
>                          arp_proxy, op->nbsp->name);
>                  }
>              }
> +
> +            /* Only used for the router type LSP whose peer is l3dgw_port */
> +            if (op->peer && is_l3dgw_port(op->peer)) {
> +                op->enable_router_port_acl = smap_get_bool(
> +                    &op->nbsp->options, "enable_router_port_acl", false);
> +            }
>          } else if (op->nbrp && op->nbrp->peer && !op->l3dgw_port) {
>              struct ovn_port *peer = ovn_port_find(ports, op->nbrp->peer);
>              if (peer) {
> @@ -7207,7 +7216,11 @@ build_pre_acls(struct ovn_datapath *od,
>       * which handles defragmentation, in order to match L4 headers. */
>      if (od->has_stateful_acl) {
>          for (size_t i = 0; i < od->n_router_ports; i++) {
> -            skip_port_from_conntrack(od, od->router_ports[i],
> +            struct ovn_port *op = od->router_ports[i];
> +            if (op->enable_router_port_acl) {
> +                continue;
> +            }
> +            skip_port_from_conntrack(od, op,
>                                       S_SWITCH_IN_PRE_ACL, S_SWITCH_OUT_PRE_ACL,
>                                       110, lflows);
>          }
> diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml
> index 98cf7adb43..f1eb9ecb1b 100644
> --- a/northd/ovn-northd.8.xml
> +++ b/northd/ovn-northd.8.xml
> @@ -438,7 +438,12 @@
>        <code>Pre-stateful</code> to send IP packets to the connection tracker
>        before eventually advancing to ingress table <code>ACLs</code>. If
>        special ports such as route ports or localnet ports can't use ct(), a
> -      priority-110 flow is added to skip over stateful ACLs. Multicast, IPv6
> +      priority-110 flow is added to skip over stateful ACLs.  This
> +      priority-110 flow is not addd for router ports if the option
> +      enable_router_port_acl is set to true in
> +      <ref column="options:enable_router_port_acl"
> +      table="Logical_Switch_Port" db="OVN_Northbound"/> column of
> +      <ref table="Logical_Switch_Port" db="OVN_Northbound"/>.  Multicast, IPv6
>        Neighbor Discovery and MLD traffic also skips stateful ACLs. For
>        "allow-stateless" ACLs, a flow is added to bypass setting the hint for
>        connection tracker processing when there are stateful ACLs or LB rules;
> diff --git a/ovn-nb.xml b/ovn-nb.xml
> index b2913bdd7a..765ffcf2e6 100644
> --- a/ovn-nb.xml
> +++ b/ovn-nb.xml
> @@ -1107,6 +1107,14 @@
>            should have a route to forward packets sent to configured proxy ARP
>            MAC/IPs to an appropriate destination.
>          </column>
> +
> +        <column name="options" key="enable_router_port_acl"
> +                type='{"type": "boolean"}'>
> +          Optional. Enable conntrack for the router port whose peer is
> +          l3dgw_port if set to <code>true</code>. The default value is
> +          <code>false</code>.
> +        </column>
> +
>        </group>
>
>        <group title="Options for localnet ports">
> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> index 34bd25de7b..9a0d418e46 100644
> --- a/tests/ovn-northd.at
> +++ b/tests/ovn-northd.at
> @@ -10970,5 +10970,129 @@ AT_CHECK([as northd ovn-appctl -t ovn-northd status], [0], [dnl
>  Status: active
>  ])
>
> +AT_CLEANUP
> +])
> +
> +OVN_FOR_EACH_NORTHD_NO_HV([
> +AT_SETUP([Distributed gw port enable conntrack option])
> +ovn_start
> +
> +check ovn-sbctl chassis-add gw1 geneve 127.0.0.1
> +
> +# Add a distributed router
> +check ovn-nbctl lr-add R1
> +check ovn-nbctl lrp-add R1 R1-S1 02:ac:10:01:00:01 172.16.1.1/24
> +check ovn-nbctl lrp-set-gateway-chassis R1-S1 gw1
> +
> +# Add a external network connected to R1
> +check ovn-nbctl ls-add S1
> +check ovn-nbctl lsp-add S1 S1-R1
> +check ovn-nbctl lsp-set-type S1-R1 router
> +check ovn-nbctl lsp-set-addresses S1-R1 router
> +check ovn-nbctl --wait=sb lsp-set-options S1-R1 router-port=R1-S1
> +AT_CHECK([test x`ovn-nbctl lsp-get-up S1-R1` = xup])
> +
> +# Add a external network vif
> +check ovn-nbctl lsp-add S1 S1-VIF
> +check ovn-nbctl lsp-set-addresses S1-VIF "02:ac:10:01:00:02 172.16.1.11"
> +
> +# Add the router gw port and vif to one port_group which has stateful acls
> +check ovn-nbctl --wait=sb pg-add pg_dgw S1-R1 S1-VIF
> +check ovn-nbctl acl-add pg_dgw from-lport 1002 "inport == @pg_dgw && ip4" allow-related
> +check ovn-nbctl acl-add pg_dgw to-lport 1003 "outport == @pg_dgw && ip4" allow-related
> +
> +# Check skip conntrack option with 'enable_router_port_acl' default (false)
> +AT_CHECK([ovn-sbctl dump-flows S1 | grep pre_acl | sed 's/table=./table=?/'], [0], [dnl
> +  table=? (ls_in_pre_acl      ), priority=110  , match=(eth.dst == $svc_monitor_mac), action=(next;)
> +  table=? (ls_in_pre_acl      ), priority=110  , match=(eth.mcast), action=(next;)
> +  table=? (ls_in_pre_acl      ), priority=110  , match=(ip && inport == "S1-R1"), action=(next;)
> +  table=? (ls_in_pre_acl      ), priority=110  , match=(nd || nd_rs || nd_ra || mldv1 || mldv2 || (udp && udp.src == 546 && udp.dst == 547)), action=(next;)
> +  table=? (ls_in_pre_acl      ), priority=100  , match=(ip), action=(reg0[[0]] = 1; next;)
> +  table=? (ls_in_pre_acl      ), priority=0    , match=(1), action=(next;)
> +  table=? (ls_out_pre_acl     ), priority=110  , match=(eth.mcast), action=(next;)
> +  table=? (ls_out_pre_acl     ), priority=110  , match=(eth.src == $svc_monitor_mac), action=(next;)
> +  table=? (ls_out_pre_acl     ), priority=110  , match=(ip && outport == "S1-R1"), action=(next;)
> +  table=? (ls_out_pre_acl     ), priority=110  , match=(nd || nd_rs || nd_ra || mldv1 || mldv2 || (udp && udp.src == 546 && udp.dst == 547)), action=(next;)
> +  table=? (ls_out_pre_acl     ), priority=100  , match=(ip), action=(reg0[[0]] = 1; next;)
> +  table=? (ls_out_pre_acl     ), priority=0    , match=(1), action=(next;)
> +])
> +
> +# Enable 'enable_router_port_acl' and check the flows
> +check ovn-nbctl --wait=sb lsp-set-options S1-R1 router-port=R1-S1 enable_router_port_acl=true
> +AT_CHECK([ovn-sbctl dump-flows S1 | grep pre_acl | sed 's/table=./table=?/'], [0], [dnl
> +  table=? (ls_in_pre_acl      ), priority=110  , match=(eth.dst == $svc_monitor_mac), action=(next;)
> +  table=? (ls_in_pre_acl      ), priority=110  , match=(eth.mcast), action=(next;)
> +  table=? (ls_in_pre_acl      ), priority=110  , match=(nd || nd_rs || nd_ra || mldv1 || mldv2 || (udp && udp.src == 546 && udp.dst == 547)), action=(next;)
> +  table=? (ls_in_pre_acl      ), priority=100  , match=(ip), action=(reg0[[0]] = 1; next;)
> +  table=? (ls_in_pre_acl      ), priority=0    , match=(1), action=(next;)
> +  table=? (ls_out_pre_acl     ), priority=110  , match=(eth.mcast), action=(next;)
> +  table=? (ls_out_pre_acl     ), priority=110  , match=(eth.src == $svc_monitor_mac), action=(next;)
> +  table=? (ls_out_pre_acl     ), priority=110  , match=(nd || nd_rs || nd_ra || mldv1 || mldv2 || (udp && udp.src == 546 && udp.dst == 547)), action=(next;)
> +  table=? (ls_out_pre_acl     ), priority=100  , match=(ip), action=(reg0[[0]] = 1; next;)
> +  table=? (ls_out_pre_acl     ), priority=0    , match=(1), action=(next;)
> +])
> +
> +# ICMP packets from router port to external network should go to conntrack
> +flow_eth_in='eth.src == 02:ac:10:01:00:01 && eth.dst == 02:ac:10:01:00:02'
> +flow_ip_in='ip.ttl==64 && ip4.src == 172.16.10.1 && ip4.dst == 172.16.10.11'
> +flow_icmp='icmp4.type == 8'
> +flow_in="inport == \"S1-R1\" && ${flow_eth_in} && ${flow_ip_in} && ${flow_icmp}"
> +AT_CHECK_UNQUOTED([ovn_trace --ct est --ct est --minimal S1 "${flow_in}"], [0], [dnl
> +ct_next(ct_state=est|trk) {
> +    ct_next(ct_state=est|trk) {
> +        output("S1-VIF");
> +    };
> +};
> +])
> +
> +# Disable 'enable_router_port_acl' and check the flows
> +check ovn-nbctl --wait=sb lsp-set-options S1-R1 router-port=R1-S1 enable_router_port_acl=false
> +AT_CHECK([ovn-sbctl dump-flows S1 | grep pre_acl | sed 's/table=./table=?/'], [0], [dnl
> +  table=? (ls_in_pre_acl      ), priority=110  , match=(eth.dst == $svc_monitor_mac), action=(next;)
> +  table=? (ls_in_pre_acl      ), priority=110  , match=(eth.mcast), action=(next;)
> +  table=? (ls_in_pre_acl      ), priority=110  , match=(ip && inport == "S1-R1"), action=(next;)
> +  table=? (ls_in_pre_acl      ), priority=110  , match=(nd || nd_rs || nd_ra || mldv1 || mldv2 || (udp && udp.src == 546 && udp.dst == 547)), action=(next;)
> +  table=? (ls_in_pre_acl      ), priority=100  , match=(ip), action=(reg0[[0]] = 1; next;)
> +  table=? (ls_in_pre_acl      ), priority=0    , match=(1), action=(next;)
> +  table=? (ls_out_pre_acl     ), priority=110  , match=(eth.mcast), action=(next;)
> +  table=? (ls_out_pre_acl     ), priority=110  , match=(eth.src == $svc_monitor_mac), action=(next;)
> +  table=? (ls_out_pre_acl     ), priority=110  , match=(ip && outport == "S1-R1"), action=(next;)
> +  table=? (ls_out_pre_acl     ), priority=110  , match=(nd || nd_rs || nd_ra || mldv1 || mldv2 || (udp && udp.src == 546 && udp.dst == 547)), action=(next;)
> +  table=? (ls_out_pre_acl     ), priority=100  , match=(ip), action=(reg0[[0]] = 1; next;)
> +  table=? (ls_out_pre_acl     ), priority=0    , match=(1), action=(next;)
> +])
> +
> +# Clear the option 'enable_router_port_acl' and check the flows.  Before that enable the option.
> +check ovn-nbctl --wait=sb lsp-set-options S1-R1 router-port=R1-S1 enable_router_port_acl=true
> +AT_CHECK([ovn-sbctl dump-flows S1 | grep pre_acl | sed 's/table=./table=?/'], [0], [dnl
> +  table=? (ls_in_pre_acl      ), priority=110  , match=(eth.dst == $svc_monitor_mac), action=(next;)
> +  table=? (ls_in_pre_acl      ), priority=110  , match=(eth.mcast), action=(next;)
> +  table=? (ls_in_pre_acl      ), priority=110  , match=(nd || nd_rs || nd_ra || mldv1 || mldv2 || (udp && udp.src == 546 && udp.dst == 547)), action=(next;)
> +  table=? (ls_in_pre_acl      ), priority=100  , match=(ip), action=(reg0[[0]] = 1; next;)
> +  table=? (ls_in_pre_acl      ), priority=0    , match=(1), action=(next;)
> +  table=? (ls_out_pre_acl     ), priority=110  , match=(eth.mcast), action=(next;)
> +  table=? (ls_out_pre_acl     ), priority=110  , match=(eth.src == $svc_monitor_mac), action=(next;)
> +  table=? (ls_out_pre_acl     ), priority=110  , match=(nd || nd_rs || nd_ra || mldv1 || mldv2 || (udp && udp.src == 546 && udp.dst == 547)), action=(next;)
> +  table=? (ls_out_pre_acl     ), priority=100  , match=(ip), action=(reg0[[0]] = 1; next;)
> +  table=? (ls_out_pre_acl     ), priority=0    , match=(1), action=(next;)
> +])
> +
> +check ovn-nbctl --wait=sb lsp-set-options S1-R1 router-port=R1-S1
> +AT_CHECK([ovn-sbctl dump-flows S1 | grep pre_acl | sed 's/table=./table=?/'], [0], [dnl
> +  table=? (ls_in_pre_acl      ), priority=110  , match=(eth.dst == $svc_monitor_mac), action=(next;)
> +  table=? (ls_in_pre_acl      ), priority=110  , match=(eth.mcast), action=(next;)
> +  table=? (ls_in_pre_acl      ), priority=110  , match=(ip && inport == "S1-R1"), action=(next;)
> +  table=? (ls_in_pre_acl      ), priority=110  , match=(nd || nd_rs || nd_ra || mldv1 || mldv2 || (udp && udp.src == 546 && udp.dst == 547)), action=(next;)
> +  table=? (ls_in_pre_acl      ), priority=100  , match=(ip), action=(reg0[[0]] = 1; next;)
> +  table=? (ls_in_pre_acl      ), priority=0    , match=(1), action=(next;)
> +  table=? (ls_out_pre_acl     ), priority=110  , match=(eth.mcast), action=(next;)
> +  table=? (ls_out_pre_acl     ), priority=110  , match=(eth.src == $svc_monitor_mac), action=(next;)
> +  table=? (ls_out_pre_acl     ), priority=110  , match=(ip && outport == "S1-R1"), action=(next;)
> +  table=? (ls_out_pre_acl     ), priority=110  , match=(nd || nd_rs || nd_ra || mldv1 || mldv2 || (udp && udp.src == 546 && udp.dst == 547)), action=(next;)
> +  table=? (ls_out_pre_acl     ), priority=100  , match=(ip), action=(reg0[[0]] = 1; next;)
> +  table=? (ls_out_pre_acl     ), priority=0    , match=(1), action=(next;)
> +])
> +
> +
>  AT_CLEANUP
>  ])
> --
> 2.43.0
>
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
diff mbox series

Patch

diff --git a/NEWS b/NEWS
index 20df92cb76..5f267b4c64 100644
--- a/NEWS
+++ b/NEWS
@@ -12,6 +12,8 @@  Post v23.09.0
   - Support CIDR based MAC binding aging threshold. See ovn-nb(5) for
     'mac_binding_age_threshold' for more details.
   - ovn-northd-ddlog has been removed.
+  - A new LSP option "enable_router_port_acl" has been added to enable
+    conntrack for the router port whose peer is l3dgw_port if set it true.
 
 OVN v23.09.0 - 15 Sep 2023
 --------------------------
diff --git a/northd/northd.c b/northd/northd.c
index db3cd272e1..952f8200d4 100644
--- a/northd/northd.c
+++ b/northd/northd.c
@@ -1612,6 +1612,9 @@  struct ovn_port {
      * access it from any other nodes.
      */
     struct ovs_list lflows;
+
+    /* Only used for the router type LSP whose peer is l3dgw_port */
+    bool enable_router_port_acl;
 };
 
 static bool lsp_can_be_inc_processed(const struct nbrec_logical_switch_port *);
@@ -2826,6 +2829,12 @@  join_logical_ports(const struct sbrec_port_binding_table *sbrec_pb_table,
                         arp_proxy, op->nbsp->name);
                 }
             }
+
+            /* Only used for the router type LSP whose peer is l3dgw_port */
+            if (op->peer && is_l3dgw_port(op->peer)) {
+                op->enable_router_port_acl = smap_get_bool(
+                    &op->nbsp->options, "enable_router_port_acl", false);
+            }
         } else if (op->nbrp && op->nbrp->peer && !op->l3dgw_port) {
             struct ovn_port *peer = ovn_port_find(ports, op->nbrp->peer);
             if (peer) {
@@ -7207,7 +7216,11 @@  build_pre_acls(struct ovn_datapath *od,
      * which handles defragmentation, in order to match L4 headers. */
     if (od->has_stateful_acl) {
         for (size_t i = 0; i < od->n_router_ports; i++) {
-            skip_port_from_conntrack(od, od->router_ports[i],
+            struct ovn_port *op = od->router_ports[i];
+            if (op->enable_router_port_acl) {
+                continue;
+            }
+            skip_port_from_conntrack(od, op,
                                      S_SWITCH_IN_PRE_ACL, S_SWITCH_OUT_PRE_ACL,
                                      110, lflows);
         }
diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml
index 98cf7adb43..f1eb9ecb1b 100644
--- a/northd/ovn-northd.8.xml
+++ b/northd/ovn-northd.8.xml
@@ -438,7 +438,12 @@ 
       <code>Pre-stateful</code> to send IP packets to the connection tracker
       before eventually advancing to ingress table <code>ACLs</code>. If
       special ports such as route ports or localnet ports can't use ct(), a
-      priority-110 flow is added to skip over stateful ACLs. Multicast, IPv6
+      priority-110 flow is added to skip over stateful ACLs.  This
+      priority-110 flow is not addd for router ports if the option
+      enable_router_port_acl is set to true in
+      <ref column="options:enable_router_port_acl"
+      table="Logical_Switch_Port" db="OVN_Northbound"/> column of
+      <ref table="Logical_Switch_Port" db="OVN_Northbound"/>.  Multicast, IPv6
       Neighbor Discovery and MLD traffic also skips stateful ACLs. For
       "allow-stateless" ACLs, a flow is added to bypass setting the hint for
       connection tracker processing when there are stateful ACLs or LB rules;
diff --git a/ovn-nb.xml b/ovn-nb.xml
index b2913bdd7a..765ffcf2e6 100644
--- a/ovn-nb.xml
+++ b/ovn-nb.xml
@@ -1107,6 +1107,14 @@ 
           should have a route to forward packets sent to configured proxy ARP
           MAC/IPs to an appropriate destination.
         </column>
+
+        <column name="options" key="enable_router_port_acl"
+                type='{"type": "boolean"}'>
+          Optional. Enable conntrack for the router port whose peer is
+          l3dgw_port if set to <code>true</code>. The default value is
+          <code>false</code>.
+        </column>
+
       </group>
 
       <group title="Options for localnet ports">
diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
index 34bd25de7b..9a0d418e46 100644
--- a/tests/ovn-northd.at
+++ b/tests/ovn-northd.at
@@ -10970,5 +10970,129 @@  AT_CHECK([as northd ovn-appctl -t ovn-northd status], [0], [dnl
 Status: active
 ])
 
+AT_CLEANUP
+])
+
+OVN_FOR_EACH_NORTHD_NO_HV([
+AT_SETUP([Distributed gw port enable conntrack option])
+ovn_start
+
+check ovn-sbctl chassis-add gw1 geneve 127.0.0.1
+
+# Add a distributed router
+check ovn-nbctl lr-add R1
+check ovn-nbctl lrp-add R1 R1-S1 02:ac:10:01:00:01 172.16.1.1/24
+check ovn-nbctl lrp-set-gateway-chassis R1-S1 gw1
+
+# Add a external network connected to R1
+check ovn-nbctl ls-add S1
+check ovn-nbctl lsp-add S1 S1-R1
+check ovn-nbctl lsp-set-type S1-R1 router
+check ovn-nbctl lsp-set-addresses S1-R1 router
+check ovn-nbctl --wait=sb lsp-set-options S1-R1 router-port=R1-S1
+AT_CHECK([test x`ovn-nbctl lsp-get-up S1-R1` = xup])
+
+# Add a external network vif
+check ovn-nbctl lsp-add S1 S1-VIF
+check ovn-nbctl lsp-set-addresses S1-VIF "02:ac:10:01:00:02 172.16.1.11"
+
+# Add the router gw port and vif to one port_group which has stateful acls
+check ovn-nbctl --wait=sb pg-add pg_dgw S1-R1 S1-VIF
+check ovn-nbctl acl-add pg_dgw from-lport 1002 "inport == @pg_dgw && ip4" allow-related
+check ovn-nbctl acl-add pg_dgw to-lport 1003 "outport == @pg_dgw && ip4" allow-related
+
+# Check skip conntrack option with 'enable_router_port_acl' default (false)
+AT_CHECK([ovn-sbctl dump-flows S1 | grep pre_acl | sed 's/table=./table=?/'], [0], [dnl
+  table=? (ls_in_pre_acl      ), priority=110  , match=(eth.dst == $svc_monitor_mac), action=(next;)
+  table=? (ls_in_pre_acl      ), priority=110  , match=(eth.mcast), action=(next;)
+  table=? (ls_in_pre_acl      ), priority=110  , match=(ip && inport == "S1-R1"), action=(next;)
+  table=? (ls_in_pre_acl      ), priority=110  , match=(nd || nd_rs || nd_ra || mldv1 || mldv2 || (udp && udp.src == 546 && udp.dst == 547)), action=(next;)
+  table=? (ls_in_pre_acl      ), priority=100  , match=(ip), action=(reg0[[0]] = 1; next;)
+  table=? (ls_in_pre_acl      ), priority=0    , match=(1), action=(next;)
+  table=? (ls_out_pre_acl     ), priority=110  , match=(eth.mcast), action=(next;)
+  table=? (ls_out_pre_acl     ), priority=110  , match=(eth.src == $svc_monitor_mac), action=(next;)
+  table=? (ls_out_pre_acl     ), priority=110  , match=(ip && outport == "S1-R1"), action=(next;)
+  table=? (ls_out_pre_acl     ), priority=110  , match=(nd || nd_rs || nd_ra || mldv1 || mldv2 || (udp && udp.src == 546 && udp.dst == 547)), action=(next;)
+  table=? (ls_out_pre_acl     ), priority=100  , match=(ip), action=(reg0[[0]] = 1; next;)
+  table=? (ls_out_pre_acl     ), priority=0    , match=(1), action=(next;)
+])
+
+# Enable 'enable_router_port_acl' and check the flows
+check ovn-nbctl --wait=sb lsp-set-options S1-R1 router-port=R1-S1 enable_router_port_acl=true
+AT_CHECK([ovn-sbctl dump-flows S1 | grep pre_acl | sed 's/table=./table=?/'], [0], [dnl
+  table=? (ls_in_pre_acl      ), priority=110  , match=(eth.dst == $svc_monitor_mac), action=(next;)
+  table=? (ls_in_pre_acl      ), priority=110  , match=(eth.mcast), action=(next;)
+  table=? (ls_in_pre_acl      ), priority=110  , match=(nd || nd_rs || nd_ra || mldv1 || mldv2 || (udp && udp.src == 546 && udp.dst == 547)), action=(next;)
+  table=? (ls_in_pre_acl      ), priority=100  , match=(ip), action=(reg0[[0]] = 1; next;)
+  table=? (ls_in_pre_acl      ), priority=0    , match=(1), action=(next;)
+  table=? (ls_out_pre_acl     ), priority=110  , match=(eth.mcast), action=(next;)
+  table=? (ls_out_pre_acl     ), priority=110  , match=(eth.src == $svc_monitor_mac), action=(next;)
+  table=? (ls_out_pre_acl     ), priority=110  , match=(nd || nd_rs || nd_ra || mldv1 || mldv2 || (udp && udp.src == 546 && udp.dst == 547)), action=(next;)
+  table=? (ls_out_pre_acl     ), priority=100  , match=(ip), action=(reg0[[0]] = 1; next;)
+  table=? (ls_out_pre_acl     ), priority=0    , match=(1), action=(next;)
+])
+
+# ICMP packets from router port to external network should go to conntrack
+flow_eth_in='eth.src == 02:ac:10:01:00:01 && eth.dst == 02:ac:10:01:00:02'
+flow_ip_in='ip.ttl==64 && ip4.src == 172.16.10.1 && ip4.dst == 172.16.10.11'
+flow_icmp='icmp4.type == 8'
+flow_in="inport == \"S1-R1\" && ${flow_eth_in} && ${flow_ip_in} && ${flow_icmp}"
+AT_CHECK_UNQUOTED([ovn_trace --ct est --ct est --minimal S1 "${flow_in}"], [0], [dnl
+ct_next(ct_state=est|trk) {
+    ct_next(ct_state=est|trk) {
+        output("S1-VIF");
+    };
+};
+])
+
+# Disable 'enable_router_port_acl' and check the flows
+check ovn-nbctl --wait=sb lsp-set-options S1-R1 router-port=R1-S1 enable_router_port_acl=false
+AT_CHECK([ovn-sbctl dump-flows S1 | grep pre_acl | sed 's/table=./table=?/'], [0], [dnl
+  table=? (ls_in_pre_acl      ), priority=110  , match=(eth.dst == $svc_monitor_mac), action=(next;)
+  table=? (ls_in_pre_acl      ), priority=110  , match=(eth.mcast), action=(next;)
+  table=? (ls_in_pre_acl      ), priority=110  , match=(ip && inport == "S1-R1"), action=(next;)
+  table=? (ls_in_pre_acl      ), priority=110  , match=(nd || nd_rs || nd_ra || mldv1 || mldv2 || (udp && udp.src == 546 && udp.dst == 547)), action=(next;)
+  table=? (ls_in_pre_acl      ), priority=100  , match=(ip), action=(reg0[[0]] = 1; next;)
+  table=? (ls_in_pre_acl      ), priority=0    , match=(1), action=(next;)
+  table=? (ls_out_pre_acl     ), priority=110  , match=(eth.mcast), action=(next;)
+  table=? (ls_out_pre_acl     ), priority=110  , match=(eth.src == $svc_monitor_mac), action=(next;)
+  table=? (ls_out_pre_acl     ), priority=110  , match=(ip && outport == "S1-R1"), action=(next;)
+  table=? (ls_out_pre_acl     ), priority=110  , match=(nd || nd_rs || nd_ra || mldv1 || mldv2 || (udp && udp.src == 546 && udp.dst == 547)), action=(next;)
+  table=? (ls_out_pre_acl     ), priority=100  , match=(ip), action=(reg0[[0]] = 1; next;)
+  table=? (ls_out_pre_acl     ), priority=0    , match=(1), action=(next;)
+])
+
+# Clear the option 'enable_router_port_acl' and check the flows.  Before that enable the option.
+check ovn-nbctl --wait=sb lsp-set-options S1-R1 router-port=R1-S1 enable_router_port_acl=true
+AT_CHECK([ovn-sbctl dump-flows S1 | grep pre_acl | sed 's/table=./table=?/'], [0], [dnl
+  table=? (ls_in_pre_acl      ), priority=110  , match=(eth.dst == $svc_monitor_mac), action=(next;)
+  table=? (ls_in_pre_acl      ), priority=110  , match=(eth.mcast), action=(next;)
+  table=? (ls_in_pre_acl      ), priority=110  , match=(nd || nd_rs || nd_ra || mldv1 || mldv2 || (udp && udp.src == 546 && udp.dst == 547)), action=(next;)
+  table=? (ls_in_pre_acl      ), priority=100  , match=(ip), action=(reg0[[0]] = 1; next;)
+  table=? (ls_in_pre_acl      ), priority=0    , match=(1), action=(next;)
+  table=? (ls_out_pre_acl     ), priority=110  , match=(eth.mcast), action=(next;)
+  table=? (ls_out_pre_acl     ), priority=110  , match=(eth.src == $svc_monitor_mac), action=(next;)
+  table=? (ls_out_pre_acl     ), priority=110  , match=(nd || nd_rs || nd_ra || mldv1 || mldv2 || (udp && udp.src == 546 && udp.dst == 547)), action=(next;)
+  table=? (ls_out_pre_acl     ), priority=100  , match=(ip), action=(reg0[[0]] = 1; next;)
+  table=? (ls_out_pre_acl     ), priority=0    , match=(1), action=(next;)
+])
+
+check ovn-nbctl --wait=sb lsp-set-options S1-R1 router-port=R1-S1
+AT_CHECK([ovn-sbctl dump-flows S1 | grep pre_acl | sed 's/table=./table=?/'], [0], [dnl
+  table=? (ls_in_pre_acl      ), priority=110  , match=(eth.dst == $svc_monitor_mac), action=(next;)
+  table=? (ls_in_pre_acl      ), priority=110  , match=(eth.mcast), action=(next;)
+  table=? (ls_in_pre_acl      ), priority=110  , match=(ip && inport == "S1-R1"), action=(next;)
+  table=? (ls_in_pre_acl      ), priority=110  , match=(nd || nd_rs || nd_ra || mldv1 || mldv2 || (udp && udp.src == 546 && udp.dst == 547)), action=(next;)
+  table=? (ls_in_pre_acl      ), priority=100  , match=(ip), action=(reg0[[0]] = 1; next;)
+  table=? (ls_in_pre_acl      ), priority=0    , match=(1), action=(next;)
+  table=? (ls_out_pre_acl     ), priority=110  , match=(eth.mcast), action=(next;)
+  table=? (ls_out_pre_acl     ), priority=110  , match=(eth.src == $svc_monitor_mac), action=(next;)
+  table=? (ls_out_pre_acl     ), priority=110  , match=(ip && outport == "S1-R1"), action=(next;)
+  table=? (ls_out_pre_acl     ), priority=110  , match=(nd || nd_rs || nd_ra || mldv1 || mldv2 || (udp && udp.src == 546 && udp.dst == 547)), action=(next;)
+  table=? (ls_out_pre_acl     ), priority=100  , match=(ip), action=(reg0[[0]] = 1; next;)
+  table=? (ls_out_pre_acl     ), priority=0    , match=(1), action=(next;)
+])
+
+
 AT_CLEANUP
 ])