ebb439
From e552cd40bf8abb3eb5ff80bd25c13105e427119a Mon Sep 17 00:00:00 2001
ebb439
From: Dumitru Ceara <dceara@redhat.com>
ebb439
Date: Thu, 15 Oct 2020 11:12:30 +0200
ebb439
Subject: [PATCH 7/7] ofctrl.c: Add a predictable resolution for conflicting
ebb439
 flow actions.
ebb439
ebb439
Until now, in case the ACL configuration generates openflows that have
ebb439
the same match but different actions, ovn-controller was using the
ebb439
following approach:
ebb439
1. If the flow being added contains conjunctive actions, merge its
ebb439
   actions with the already existing flow.
ebb439
2. Otherwise, if the flow is being added incrementally
ebb439
   (update_installed_flows_by_track), don't install the new flow but
ebb439
   instead keep the old one.
ebb439
3. Otherwise, (update_installed_flows_by_compare), don't install the
ebb439
   new flow but instead keep the old one.
ebb439
ebb439
Even though one can argue that having an ACL with a match that includes
ebb439
the match of another ACL is a misconfiguration, it can happen that the
ebb439
users provision OVN like this.  Depending on the order of reading and
ebb439
installing the logical flows, the above operations can yield
ebb439
unpredictable results, e.g., allow specific traffic but then after
ebb439
ovn-controller is restarted (or a recompute happens) that specific
ebb439
traffic starts being dropped.
ebb439
ebb439
A simple example of ACL configuration is:
ebb439
ovn-nbctl acl-add ls to-lport 3 '(ip4.src==10.0.0.1 ||
ebb439
ip4.src==10.0.0.2) && (ip4.dst == 10.0.0.3 || ip4.dst == 10.0.0.4)' allow
ebb439
ovn-nbctl acl-add ls to-lport 3 'ip4.src==10.0.0.1' allow
ebb439
ovn-nbctl acl-add ls to-lport 2 'arp' allow
ebb439
ovn-nbctl acl-add ls to-lport 1 'ip4' drop
ebb439
ebb439
This is a pattern used by most CMSs:
ebb439
- define a default deny policy.
ebb439
- punch holes in the default deny policy based on user specific
ebb439
  configurations.
ebb439
ebb439
Without this commit the behavior for traffic from 10.0.0.1 to 10.0.0.5
ebb439
is unpredictable.  Depending on the order of operations traffic might be
ebb439
dropped or allowed.
ebb439
ebb439
It's also quite hard to force the CMS to ensure that such match overlaps
ebb439
never occur.
ebb439
ebb439
To address this issue we now ensure that all desired flows refering the
ebb439
same installed flow are partially sorted in the following way:
ebb439
- first all flows with action "allow".
ebb439
- then all flows with action "drop".
ebb439
- then a single flow with action "conjunction" (resulting from merging
ebb439
  all flows with the same match and action conjunction).
ebb439
ebb439
This ensures that "allow" flows have precedence over "drop" flows which
ebb439
in turn have precedence over "conjunction" flows.  Essentially less
ebb439
restrictive flows are always preferred over more restrictive flows whenever a match
ebb439
conflict happens.
ebb439
ebb439
CC: Daniel Alvarez <dalvarez@redhat.com>
ebb439
Reported-at: https://bugzilla.redhat.com/1871931
ebb439
Signed-off-by: Dumitru Ceara <dceara@redhat.com>
ebb439
Acked-by: Mark Gray <mark.d.gray@redhat.com>
ebb439
Signed-off-by: Han Zhou <hzhou@ovn.org>
ebb439
(cherry picked from upstream commit 986b3d5e4ad6f05245d021ba699c957246294a22)
ebb439
ebb439
Change-Id: Ibf49b5103ea34e5f268782f81cdae9cc7c06cae0
ebb439
---
ebb439
 controller/ofctrl.c |  74 ++++++++++++++++--
ebb439
 tests/ovn.at        | 214 ++++++++++++++++++++++++++++++++++++++++++++++++++++
ebb439
 2 files changed, 283 insertions(+), 5 deletions(-)
ebb439
ebb439
diff --git a/controller/ofctrl.c b/controller/ofctrl.c
ebb439
index f444cae..79529d1 100644
ebb439
--- a/controller/ofctrl.c
ebb439
+++ b/controller/ofctrl.c
ebb439
@@ -188,6 +188,14 @@ struct sb_flow_ref {
ebb439
  * relationship is 1 to N. A link is added when a flow addition is processed.
ebb439
  * A link is removed when a flow deletion is processed, the desired flow
ebb439
  * table is cleared, or the installed flow table is cleared.
ebb439
+ *
ebb439
+ * To ensure predictable behavior, the list of desired flows is maintained
ebb439
+ * partially sorted in the following way (from least restrictive to most
ebb439
+ * restrictive wrt. match):
ebb439
+ * - allow flows without action conjunction.
ebb439
+ * - drop flows without action conjunction.
ebb439
+ * - a single flow with action conjunction.
ebb439
+ *
ebb439
  * The first desired_flow in the list is the active one, the one that is
ebb439
  * actually installed.
ebb439
  */
ebb439
@@ -796,6 +804,12 @@ ofctrl_recv(const struct ofp_header *oh, enum ofptype type)
ebb439
 }
ebb439
 
ebb439
 static bool
ebb439
+flow_action_has_drop(const struct ovn_flow *f)
ebb439
+{
ebb439
+    return f->ofpacts_len == 0;
ebb439
+}
ebb439
+
ebb439
+static bool
ebb439
 flow_action_has_conj(const struct ovn_flow *f)
ebb439
 {
ebb439
     const struct ofpact *a = NULL;
ebb439
@@ -808,6 +822,33 @@ flow_action_has_conj(const struct ovn_flow *f)
ebb439
     return false;
ebb439
 }
ebb439
 
ebb439
+static bool
ebb439
+flow_action_has_allow(const struct ovn_flow *f)
ebb439
+{
ebb439
+    return !flow_action_has_drop(f) && !flow_action_has_conj(f);
ebb439
+}
ebb439
+
ebb439
+/* Returns true if flow 'a' is preferred over flow 'b'. */
ebb439
+static bool
ebb439
+flow_is_preferred(const struct ovn_flow *a, const struct ovn_flow *b)
ebb439
+{
ebb439
+    if (flow_action_has_allow(b)) {
ebb439
+        return false;
ebb439
+    }
ebb439
+    if (flow_action_has_allow(a)) {
ebb439
+        return true;
ebb439
+    }
ebb439
+    if (flow_action_has_drop(b)) {
ebb439
+        return false;
ebb439
+    }
ebb439
+    if (flow_action_has_drop(a)) {
ebb439
+        return true;
ebb439
+    }
ebb439
+
ebb439
+    /* Flows 'a' and 'b' should never both have action conjunction. */
ebb439
+    OVS_NOT_REACHED();
ebb439
+}
ebb439
+
ebb439
 /* Adds the desired flow to the list of desired flows that have same match
ebb439
  * conditions as the installed flow.
ebb439
  *
ebb439
@@ -820,8 +861,18 @@ flow_action_has_conj(const struct ovn_flow *f)
ebb439
 static bool
ebb439
 link_installed_to_desired(struct installed_flow *i, struct desired_flow *d)
ebb439
 {
ebb439
+    struct desired_flow *f;
ebb439
+
ebb439
+    /* Find first 'f' such that 'd' is preferred over 'f'.  If no such desired
ebb439
+     * flow exists then 'f' will point after the last element of the list.
ebb439
+     */
ebb439
+    LIST_FOR_EACH (f, installed_ref_list_node, &i->desired_refs) {
ebb439
+        if (flow_is_preferred(&d->flow, &f->flow)) {
ebb439
+            break;
ebb439
+        }
ebb439
+    }
ebb439
+    ovs_list_insert(&f->installed_ref_list_node, &d->installed_ref_list_node);
ebb439
     d->installed_flow = i;
ebb439
-    ovs_list_push_back(&i->desired_refs, &d->installed_ref_list_node);
ebb439
     return installed_flow_get_active(i) == d;
ebb439
 }
ebb439
 
ebb439
@@ -1789,8 +1840,14 @@ update_installed_flows_by_compare(struct ovn_desired_flow_table *flow_table,
ebb439
             link_installed_to_desired(i, d);
ebb439
         } else if (!d->installed_flow) {
ebb439
             /* This is a desired_flow that conflicts with one installed
ebb439
-             * previously but not linked yet. */
ebb439
-            link_installed_to_desired(i, d);
ebb439
+             * previously but not linked yet.  However, if this flow becomes
ebb439
+             * active, e.g., it is less restrictive than the previous active
ebb439
+             * flow then modify the installed flow.
ebb439
+             */
ebb439
+            if (link_installed_to_desired(i, d)) {
ebb439
+                installed_flow_mod(&i->flow, &d->flow, msgs);
ebb439
+                ovn_flow_log(&i->flow, "updating installed (conflict)");
ebb439
+            }
ebb439
         }
ebb439
     }
ebb439
 }
ebb439
@@ -1919,8 +1976,15 @@ update_installed_flows_by_track(struct ovn_desired_flow_table *flow_table,
ebb439
                 ovn_flow_log(&i->flow, "updating installed (tracked)");
ebb439
             } else {
ebb439
                 /* Adding a new flow that conflicts with an existing installed
ebb439
-                 * flow, so just add it to the link. */
ebb439
-                link_installed_to_desired(i, f);
ebb439
+                 * flow, so add it to the link.  If this flow becomes active,
ebb439
+                 * e.g., it is less restrictive than the previous active flow
ebb439
+                 * then modify the installed flow.
ebb439
+                 */
ebb439
+                if (link_installed_to_desired(i, f)) {
ebb439
+                    installed_flow_mod(&i->flow, &f->flow, msgs);
ebb439
+                    ovn_flow_log(&i->flow,
ebb439
+                                 "updating installed (tracked conflict)");
ebb439
+                }
ebb439
             }
ebb439
             /* The track_list_node emptyness is used to check if the node is
ebb439
              * already added to track list, so initialize it again here. */
ebb439
diff --git a/tests/ovn.at b/tests/ovn.at
ebb439
index 6f1ab59..53f5d4d 100644
ebb439
--- a/tests/ovn.at
ebb439
+++ b/tests/ovn.at
ebb439
@@ -13727,6 +13727,220 @@ grep conjunction.*conjunction.*conjunction | wc -l`])
ebb439
 OVN_CLEANUP([hv1])
ebb439
 AT_CLEANUP
ebb439
 
ebb439
+AT_SETUP([ovn -- Superseeding ACLs with conjunction])
ebb439
+ovn_start
ebb439
+
ebb439
+ovn-nbctl ls-add ls1
ebb439
+
ebb439
+ovn-nbctl lsp-add ls1 ls1-lp1 \
ebb439
+-- lsp-set-addresses ls1-lp1 "f0:00:00:00:00:01"
ebb439
+
ebb439
+ovn-nbctl lsp-add ls1 ls1-lp2 \
ebb439
+-- lsp-set-addresses ls1-lp2 "f0:00:00:00:00:02"
ebb439
+
ebb439
+net_add n1
ebb439
+sim_add hv1
ebb439
+
ebb439
+as hv1
ebb439
+ovs-vsctl add-br br-phys
ebb439
+ovn_attach n1 br-phys 192.168.0.1
ebb439
+ovs-vsctl -- add-port br-int hv1-vif1 -- \
ebb439
+    set interface hv1-vif1 external-ids:iface-id=ls1-lp1 \
ebb439
+    options:tx_pcap=hv1/vif1-tx.pcap \
ebb439
+    options:rxq_pcap=hv1/vif1-rx.pcap \
ebb439
+    ofport-request=1
ebb439
+
ebb439
+ovs-vsctl -- add-port br-int hv1-vif2 -- \
ebb439
+    set interface hv1-vif2 external-ids:iface-id=ls1-lp2 \
ebb439
+    options:tx_pcap=hv1/vif2-tx.pcap \
ebb439
+    options:rxq_pcap=hv1/vif2-rx.pcap \
ebb439
+    ofport-request=2
ebb439
+
ebb439
+# test_ip INPORT SRC_MAC DST_MAC SRC_IP DST_IP OUTPORT...
ebb439
+#
ebb439
+# This shell function causes an ip packet to be received on INPORT.
ebb439
+# The packet's content has Ethernet destination DST and source SRC
ebb439
+# (each exactly 12 hex digits) and Ethernet type ETHTYPE (4 hex digits).
ebb439
+# The OUTPORTs (zero or more) list the VIFs on which the packet should
ebb439
+# be received.  INPORT and the OUTPORTs are specified as logical switch
ebb439
+# port numbers, e.g. 11 for vif11.
ebb439
+test_ip() {
ebb439
+    # This packet has bad checksums but logical L3 routing doesn't check.
ebb439
+    local inport=$1 src_mac=$2 dst_mac=$3 src_ip=$4 dst_ip=$5
ebb439
+    local packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}\
ebb439
+${dst_ip}0035111100080000
ebb439
+    shift; shift; shift; shift; shift
ebb439
+    as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet
ebb439
+    for outport; do
ebb439
+        echo $packet >> $outport.expected
ebb439
+    done
ebb439
+}
ebb439
+
ebb439
+ip_to_hex() {
ebb439
+    printf "%02x%02x%02x%02x" "$@"
ebb439
+}
ebb439
+
ebb439
+reset_pcap_file() {
ebb439
+    local iface=$1
ebb439
+    local pcap_file=$2
ebb439
+    ovs-vsctl -- set Interface $iface options:tx_pcap=dummy-tx.pcap \
ebb439
+options:rxq_pcap=dummy-rx.pcap
ebb439
+    rm -f ${pcap_file}*.pcap
ebb439
+    ovs-vsctl -- set Interface $iface options:tx_pcap=${pcap_file}-tx.pcap \
ebb439
+options:rxq_pcap=${pcap_file}-rx.pcap
ebb439
+}
ebb439
+
ebb439
+# Add a default deny ACL and an allow ACL for specific IP traffic.
ebb439
+ovn-nbctl acl-add ls1 to-lport 2 'arp' allow
ebb439
+ovn-nbctl acl-add ls1 to-lport 1 'ip4' drop
ebb439
+ovn-nbctl acl-add ls1 to-lport 3 '(ip4.src==10.0.0.1 || ip4.src==10.0.0.2) && (ip4.dst == 10.0.0.3 || ip4.dst == 10.0.0.4)' allow
ebb439
+ovn-nbctl acl-add ls1 to-lport 3 '(ip4.src==10.0.0.1 || ip4.src==10.0.0.42) && (ip4.dst == 10.0.0.3 || ip4.dst == 10.0.0.4)' allow
ebb439
+ovn-nbctl --wait=hv sync
ebb439
+
ebb439
+# Traffic 10.0.0.1, 10.0.0.2 -> 10.0.0.3, 10.0.0.4 should be allowed.
ebb439
+for src in `seq 1 2`; do
ebb439
+    for dst in `seq 3 4`; do
ebb439
+        sip=`ip_to_hex 10 0 0 $src`
ebb439
+        dip=`ip_to_hex 10 0 0 $dst`
ebb439
+
ebb439
+        test_ip 1 f00000000001 f00000000002 $sip $dip 2
ebb439
+    done
ebb439
+done
ebb439
+
ebb439
+# Traffic 10.0.0.1, 10.0.0.2 -> 10.0.0.5 should be dropped.
ebb439
+dip=`ip_to_hex 10 0 0 5`
ebb439
+for src in `seq 1 2`; do
ebb439
+    sip=`ip_to_hex 10 0 0 $src`
ebb439
+
ebb439
+    test_ip 1 f00000000001 f00000000002 $sip $dip
ebb439
+done
ebb439
+
ebb439
+cat 2.expected > expout
ebb439
+$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
ebb439
+AT_CHECK([cat 2.packets], [0], [expout])
ebb439
+reset_pcap_file hv1-vif2 hv1/vif2
ebb439
+rm -f 2.packets
ebb439
+> 2.expected
ebb439
+
ebb439
+# Add two less restrictive allow ACLs for src IP 10.0.0.1.
ebb439
+ovn-nbctl acl-add ls1 to-lport 3 'ip4.src==10.0.0.1 || ip4.src==10.0.0.1' allow
ebb439
+ovn-nbctl acl-add ls1 to-lport 3 'ip4.src==10.0.0.1' allow
ebb439
+ovn-nbctl --wait=hv sync
ebb439
+
ebb439
+# Check OVS flows, the less restrictive flows should have been installed.
ebb439
+AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=45 | \
ebb439
+    grep "priority=1003" | awk '{print $7 " " $8}' | sort], [0], [dnl
ebb439
+priority=1003,conj_id=2,ip,metadata=0x1 actions=resubmit(,46)
ebb439
+priority=1003,conj_id=3,ip,metadata=0x1 actions=resubmit(,46)
ebb439
+priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(2,1/2),conjunction(3,1/2)
ebb439
+priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(2,1/2),conjunction(3,1/2)
ebb439
+priority=1003,ip,metadata=0x1,nw_src=10.0.0.1 actions=resubmit(,46)
ebb439
+priority=1003,ip,metadata=0x1,nw_src=10.0.0.2 actions=conjunction(2,2/2)
ebb439
+priority=1003,ip,metadata=0x1,nw_src=10.0.0.42 actions=conjunction(3,2/2)
ebb439
+])
ebb439
+
ebb439
+# Traffic 10.0.0.1, 10.0.0.2 -> 10.0.0.3, 10.0.0.4 should be allowed.
ebb439
+for src in `seq 1 2`; do
ebb439
+    for dst in `seq 3 4`; do
ebb439
+        sip=`ip_to_hex 10 0 0 $src`
ebb439
+        dip=`ip_to_hex 10 0 0 $dst`
ebb439
+
ebb439
+        test_ip 1 f00000000001 f00000000002 $sip $dip 2
ebb439
+    done
ebb439
+done
ebb439
+
ebb439
+# Traffic 10.0.0.2 -> 10.0.0.5 should be dropped.
ebb439
+sip=`ip_to_hex 10 0 0 2`
ebb439
+dip=`ip_to_hex 10 0 0 5`
ebb439
+test_ip 1 f00000000001 f00000000002 $sip $dip
ebb439
+
ebb439
+# Traffic 10.0.0.1 -> 10.0.0.5 should be allowed.
ebb439
+sip=`ip_to_hex 10 0 0 1`
ebb439
+dip=`ip_to_hex 10 0 0 5`
ebb439
+test_ip 1 f00000000001 f00000000002 $sip $dip 2
ebb439
+
ebb439
+cat 2.expected > expout
ebb439
+$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
ebb439
+AT_CHECK([cat 2.packets], [0], [expout])
ebb439
+reset_pcap_file hv1-vif2 hv1/vif2
ebb439
+rm -f 2.packets
ebb439
+> 2.expected
ebb439
+
ebb439
+#sleep infinity
ebb439
+
ebb439
+# Remove the first less restrictive allow ACL.
ebb439
+ovn-nbctl acl-del ls1 to-lport 3 'ip4.src==10.0.0.1 || ip4.src==10.0.0.1'
ebb439
+ovn-nbctl --wait=hv sync
ebb439
+
ebb439
+# Check OVS flows, the second less restrictive allow ACL should have been installed.
ebb439
+AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=45 | \
ebb439
+    grep "priority=1003" | awk '{print $7 " " $8}' | sort], [0], [dnl
ebb439
+priority=1003,conj_id=2,ip,metadata=0x1 actions=resubmit(,46)
ebb439
+priority=1003,conj_id=3,ip,metadata=0x1 actions=resubmit(,46)
ebb439
+priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(2,1/2),conjunction(3,1/2)
ebb439
+priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(2,1/2),conjunction(3,1/2)
ebb439
+priority=1003,ip,metadata=0x1,nw_src=10.0.0.1 actions=resubmit(,46)
ebb439
+priority=1003,ip,metadata=0x1,nw_src=10.0.0.2 actions=conjunction(2,2/2)
ebb439
+priority=1003,ip,metadata=0x1,nw_src=10.0.0.42 actions=conjunction(3,2/2)
ebb439
+])
ebb439
+
ebb439
+# Remove the less restrictive allow ACL.
ebb439
+ovn-nbctl acl-del ls1 to-lport 3 'ip4.src==10.0.0.1'
ebb439
+ovn-nbctl --wait=hv sync
ebb439
+
ebb439
+# Check OVS flows, the 10.0.0.1 conjunction should have been reinstalled.
ebb439
+AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=45 | \
ebb439
+    grep "priority=1003" | awk '{print $7 " " $8}' | sort], [0], [dnl
ebb439
+priority=1003,conj_id=2,ip,metadata=0x1 actions=resubmit(,46)
ebb439
+priority=1003,conj_id=3,ip,metadata=0x1 actions=resubmit(,46)
ebb439
+priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(2,1/2),conjunction(3,1/2)
ebb439
+priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(2,1/2),conjunction(3,1/2)
ebb439
+priority=1003,ip,metadata=0x1,nw_src=10.0.0.1 actions=conjunction(2,2/2),conjunction(3,2/2)
ebb439
+priority=1003,ip,metadata=0x1,nw_src=10.0.0.2 actions=conjunction(2,2/2)
ebb439
+priority=1003,ip,metadata=0x1,nw_src=10.0.0.42 actions=conjunction(3,2/2)
ebb439
+])
ebb439
+
ebb439
+# Traffic 10.0.0.1, 10.0.0.2 -> 10.0.0.3, 10.0.0.4 should be allowed.
ebb439
+for src in `seq 1 2`; do
ebb439
+    for dst in `seq 3 4`; do
ebb439
+        sip=`ip_to_hex 10 0 0 $src`
ebb439
+        dip=`ip_to_hex 10 0 0 $dst`
ebb439
+
ebb439
+        test_ip 1 f00000000001 f00000000002 $sip $dip 2
ebb439
+    done
ebb439
+done
ebb439
+
ebb439
+# Traffic 10.0.0.1, 10.0.0.2 -> 10.0.0.5 should be dropped.
ebb439
+dip=`ip_to_hex 10 0 0 5`
ebb439
+for src in `seq 1 2`; do
ebb439
+    sip=`ip_to_hex 10 0 0 $src`
ebb439
+
ebb439
+    test_ip 1 f00000000001 f00000000002 $sip $dip
ebb439
+done
ebb439
+
ebb439
+cat 2.expected > expout
ebb439
+$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
ebb439
+AT_CHECK([cat 2.packets], [0], [expout])
ebb439
+
ebb439
+# Re-add the less restrictive allow ACL for src IP 10.0.0.1
ebb439
+ovn-nbctl acl-add ls1 to-lport 3 'ip4.src==10.0.0.1' allow
ebb439
+ovn-nbctl --wait=hv sync
ebb439
+
ebb439
+# Check OVS flows, the less restrictive flows should have been installed.
ebb439
+AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=45 | \
ebb439
+   grep "priority=1003" | awk '{print $7 " " $8}' | sort], [0], [dnl
ebb439
+priority=1003,conj_id=2,ip,metadata=0x1 actions=resubmit(,46)
ebb439
+priority=1003,conj_id=3,ip,metadata=0x1 actions=resubmit(,46)
ebb439
+priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(2,1/2),conjunction(3,1/2)
ebb439
+priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(2,1/2),conjunction(3,1/2)
ebb439
+priority=1003,ip,metadata=0x1,nw_src=10.0.0.1 actions=resubmit(,46)
ebb439
+priority=1003,ip,metadata=0x1,nw_src=10.0.0.2 actions=conjunction(2,2/2)
ebb439
+priority=1003,ip,metadata=0x1,nw_src=10.0.0.42 actions=conjunction(3,2/2)
ebb439
+])
ebb439
+
ebb439
+OVN_CLEANUP([hv1])
ebb439
+AT_CLEANUP
ebb439
+
ebb439
 # 3 hypervisors, one logical switch, 3 logical ports per hypervisor
ebb439
 AT_SETUP([ovn -- L2 Drop and Allow ACL w/ Stateful ACL])
ebb439
 ovn_start
ebb439
-- 
ebb439
1.8.3.1
ebb439