From patchwork Wed Sep 30 07:38:47 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dumitru Ceara X-Patchwork-Id: 1374177 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=140.211.166.137; helo=fraxinus.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=WLCw9+RW; dkim-atps=neutral Received: from fraxinus.osuosl.org (smtp4.osuosl.org [140.211.166.137]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4C1Sn83Hqxz9sSG for ; Wed, 30 Sep 2020 17:39:08 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by fraxinus.osuosl.org (Postfix) with ESMTP id 122A985F4B; Wed, 30 Sep 2020 07:39:07 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from fraxinus.osuosl.org ([127.0.0.1]) by localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 21u4XSGbUs4d; Wed, 30 Sep 2020 07:39:04 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by fraxinus.osuosl.org (Postfix) with ESMTP id 6B8D485E25; Wed, 30 Sep 2020 07:39:04 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 487CDC016F; Wed, 30 Sep 2020 07:39:04 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from silver.osuosl.org (smtp3.osuosl.org [140.211.166.136]) by lists.linuxfoundation.org (Postfix) with ESMTP id E6182C0051 for ; Wed, 30 Sep 2020 07:39:02 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by silver.osuosl.org (Postfix) with ESMTP id CBEFF203C7 for ; Wed, 30 Sep 2020 07:39:02 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from silver.osuosl.org ([127.0.0.1]) by localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id yWHdxQfUKaBf for ; Wed, 30 Sep 2020 07:39:00 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [216.205.24.124]) by silver.osuosl.org (Postfix) with ESMTPS id 6A81220396 for ; Wed, 30 Sep 2020 07:39:00 +0000 (UTC) Dkim-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1601451539; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:content-type:content-type; bh=S3GHPddtgyGCsZcN1sMwbgvFc08rzBhZp3c62zKafOw=; b=WLCw9+RWbPccoC/GJh5kbtKxPP6iYBbqkVA3DWy18+BH1ZvxUfjg9IA8cDgaxsjNYiNc4m /KTHDFMpgRBx20wkp7BPy0VGkqZPdDg5A337UcQ4kzq0jOWO218hTEckXX1//4WGdfbPsN k+Og1+c8qU9fIaa5E+9rfqVwtgQErOY= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-169-OL2pcKOeNcaSUatKo4yvRQ-1; Wed, 30 Sep 2020 03:38:57 -0400 X-MC-Unique: OL2pcKOeNcaSUatKo4yvRQ-1 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 759D38030C3; Wed, 30 Sep 2020 07:38:55 +0000 (UTC) Received: from dceara.remote.csb (ovpn-113-132.ams2.redhat.com [10.36.113.132]) by smtp.corp.redhat.com (Postfix) with ESMTP id F120260C05; Wed, 30 Sep 2020 07:38:53 +0000 (UTC) From: Dumitru Ceara To: dev@openvswitch.org Date: Wed, 30 Sep 2020 09:38:47 +0200 Message-Id: <1601451527-19334-1-git-send-email-dceara@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=dceara@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Cc: Han Zhou Subject: [ovs-dev] [PATCH ovn v4] ofctrl: Add a predictable resolution for conflicting flow actions. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" Until now, in case the ACL configuration generates openflows that have the same match but different actions, ovn-controller was using the following approach: 1. If the flow being added contains conjunctive actions, merge its actions with the already existing flow. 2. Otherwise, if the flow is being added incrementally (update_installed_flows_by_track), don't install the new flow but instead keep the old one. 3. Otherwise, (update_installed_flows_by_compare), don't install the new flow but instead keep the old one. Even though one can argue that having an ACL with a match that includes the match of another ACL is a misconfiguration, it can happen that the users provision OVN like this. Depending on the order of reading and installing the logical flows, the above operations can yield unpredictable results, e.g., allow specific traffic but then after ovn-controller is restarted (or a recompute happens) that specific traffic starts being dropped. A simple example of ACL configuration is: ovn-nbctl acl-add ls 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 ovn-nbctl acl-add ls to-lport 3 'ip4.src==10.0.0.1' allow ovn-nbctl acl-add ls to-lport 2 'arp' allow ovn-nbctl acl-add ls to-lport 1 'ip4' drop This is a pattern used by most CMSs: - define a default deny policy. - punch holes in the default deny policy based on user specific configurations. Without this commit the behavior for traffic from 10.0.0.1 to 10.0.0.5 is unpredictable. Depending on the order of operations traffic might be dropped or allowed. It's also quite hard to force the CMS to ensure that such match overlaps never occur. To address this issue we now resolve conflicts between flows with the same match and different actions by giving precedence to less restrictive flows. This means that if the installed flow has action "conjunction" and the desired flow doesn't then we prefer the desired flow. Similarly, if the desired flow has action "conjunction" and the installed flow doesn't then we prefer the already installed flow. CC: Daniel Alvarez CC: Han Zhou Reported-at: https://bugzilla.redhat.com/1871931 Acked-by: Mark Michelson Signed-off-by: Dumitru Ceara Acked-by: Mark Michelson --- v4: - Address Han's comments: - make sure only flows with action conjunction are combined. v3: - Add Mark's ack. - Add last missing pcap check in the test. v2: - Address Han's comments: - Do not delete desired flow that cannot be merged, it might be installed later. - Fix typos in the commit log. - Update the test to check the OVS flows. --- controller/ofctrl.c | 163 ++++++++++++++++++++++++++++++++++++------- tests/ovn.at | 195 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 332 insertions(+), 26 deletions(-) diff --git a/controller/ofctrl.c b/controller/ofctrl.c index 81a00c8..4577413 100644 --- a/controller/ofctrl.c +++ b/controller/ofctrl.c @@ -206,6 +206,9 @@ struct installed_flow { struct desired_flow *desired_flow; }; +typedef bool +(*desired_flow_match_cb)(const struct desired_flow *candidate, + const void *arg); static struct desired_flow *desired_flow_alloc( uint8_t table_id, uint16_t priority, @@ -214,8 +217,14 @@ static struct desired_flow *desired_flow_alloc( const struct ofpbuf *actions); static struct desired_flow *desired_flow_lookup( struct ovn_desired_flow_table *, + const struct ovn_flow *target); +static struct desired_flow *desired_flow_lookup_by_uuid( + struct ovn_desired_flow_table *, const struct ovn_flow *target, - const struct uuid *sb_uuid); + const struct uuid *); +static struct desired_flow *desired_flow_lookup_conjunctive( + struct ovn_desired_flow_table *, + const struct ovn_flow *target); static void desired_flow_destroy(struct desired_flow *); static struct installed_flow *installed_flow_lookup( @@ -916,6 +925,33 @@ link_flow_to_sb(struct ovn_desired_flow_table *flow_table, ovs_list_insert(&stf->flows, &sfr->flow_list); } +static bool +flow_action_has_conj(const struct ovn_flow *f) +{ + const struct ofpact *a = NULL; + + OFPACT_FOR_EACH (a, f->ofpacts, f->ofpacts_len) { + if (a->type == OFPACT_CONJUNCTION) { + return true; + } + } + return false; +} + +static void +flow_log_actions_conflict(const char *msg, const struct ovn_flow *f1, + const struct ovn_flow *f2) +{ + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); + char *f1_s = ovn_flow_to_string(f1); + char *f2_s = ovn_flow_to_string(f2); + + VLOG_WARN_RL(&rl, "Flow actions conflict: %s, flow-1: %s, flow-2: %s", + msg, f1_s, f2_s); + free(f1_s); + free(f2_s); +} + /* Flow table interfaces to the rest of ovn-controller. */ /* Adds a flow to 'desired_flows' with the specified 'match' and 'actions' to @@ -939,7 +975,7 @@ ofctrl_check_and_add_flow(struct ovn_desired_flow_table *flow_table, struct desired_flow *f = desired_flow_alloc(table_id, priority, cookie, match, actions); - if (desired_flow_lookup(flow_table, &f->flow, sb_uuid)) { + if (desired_flow_lookup_by_uuid(flow_table, &f->flow, sb_uuid)) { if (log_duplicate_flow) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5); if (!VLOG_DROP_DBG(&rl)) { @@ -979,14 +1015,15 @@ ofctrl_add_or_append_flow(struct ovn_desired_flow_table *desired_flows, const struct ofpbuf *actions, const struct uuid *sb_uuid) { - struct desired_flow *f = desired_flow_alloc(table_id, priority, cookie, - match, actions); - struct desired_flow *existing; - existing = desired_flow_lookup(desired_flows, &f->flow, NULL); + struct desired_flow *f; + + f = desired_flow_alloc(table_id, priority, cookie, match, actions); + existing = desired_flow_lookup_conjunctive(desired_flows, &f->flow); if (existing) { - /* There's already a flow with this particular match. Append the - * action to that flow rather than adding a new flow + /* There's already a flow with this particular match and action + * 'conjunction'. Append the action to that flow rather than + * adding a new flow. */ uint64_t compound_stub[64 / 8]; struct ofpbuf compound; @@ -1225,15 +1262,11 @@ installed_flow_dup(struct desired_flow *src) return dst; } -/* Finds and returns a desired_flow in 'flow_table' whose key is identical to - * 'target''s key, or NULL if there is none. - * - * If sb_uuid is not NULL, the function will also check if the found flow is - * referenced by the sb_uuid. */ static struct desired_flow * -desired_flow_lookup(struct ovn_desired_flow_table *flow_table, - const struct ovn_flow *target, - const struct uuid *sb_uuid) +desired_flow_lookup__(struct ovn_desired_flow_table *flow_table, + const struct ovn_flow *target, + desired_flow_match_cb match_cb, + const void *arg) { struct desired_flow *d; HMAP_FOR_EACH_WITH_HASH (d, match_hmap_node, target->hash, @@ -1242,20 +1275,75 @@ desired_flow_lookup(struct ovn_desired_flow_table *flow_table, if (f->table_id == target->table_id && f->priority == target->priority && minimatch_equal(&f->match, &target->match)) { - if (!sb_uuid) { + + if (!match_cb || match_cb(d, arg)) { return d; } - struct sb_flow_ref *sfr; - LIST_FOR_EACH (sfr, sb_list, &d->references) { - if (uuid_equals(sb_uuid, &sfr->sb_uuid)) { - return d; - } - } } } return NULL; } +/* Finds and returns a desired_flow in 'flow_table' whose key is identical to + * 'target''s key, or NULL if there is none. + */ +static struct desired_flow * +desired_flow_lookup(struct ovn_desired_flow_table *flow_table, + const struct ovn_flow *target) +{ + return desired_flow_lookup__(flow_table, target, NULL, NULL); +} + +static bool +flow_lookup_match_uuid(const struct desired_flow *candidate, const void *arg) +{ + const struct uuid *sb_uuid = arg; + struct sb_flow_ref *sfr; + + LIST_FOR_EACH (sfr, sb_list, &candidate->references) { + if (uuid_equals(sb_uuid, &sfr->sb_uuid)) { + return true; + } + } + return false; +} + +/* Finds and returns a desired_flow in 'flow_table' whose key is identical to + * 'target''s key, or NULL if there is none. + * + * The function will also check if the found flow is referenced by the + * 'sb_uuid'. + */ +static struct desired_flow * +desired_flow_lookup_by_uuid(struct ovn_desired_flow_table *flow_table, + const struct ovn_flow *target, + const struct uuid *sb_uuid) +{ + return desired_flow_lookup__(flow_table, target, flow_lookup_match_uuid, + sb_uuid); +} + +static bool +flow_lookup_match_conj(const struct desired_flow *candidate, + const void *arg OVS_UNUSED) +{ + return flow_action_has_conj(&candidate->flow); +} + +/* Finds and returns a desired_flow in 'flow_table' whose key is identical to + * 'target''s key, or NULL if there is none. + * + * The function will only return a matching flow if it contains action + * 'conjunction'. + */ +static struct desired_flow * +desired_flow_lookup_conjunctive(struct ovn_desired_flow_table *flow_table, + const struct ovn_flow *target) +{ + return desired_flow_lookup__(flow_table, target, flow_lookup_match_conj, + NULL); +} + /* Finds and returns an installed_flow in installed_flows whose key is * identical to 'target''s key, or NULL if there is none. */ static struct installed_flow * @@ -1653,8 +1741,7 @@ update_installed_flows_by_compare(struct ovn_desired_flow_table *flow_table, struct installed_flow *i, *next; HMAP_FOR_EACH_SAFE (i, next, match_hmap_node, &installed_flows) { unlink_all_refs_for_installed_flow(i); - struct desired_flow *d = - desired_flow_lookup(flow_table, &i->flow, NULL); + struct desired_flow *d = desired_flow_lookup(flow_table, &i->flow); if (!d) { /* Installed flow is no longer desirable. Delete it from the * switch and from installed_flows. */ @@ -1687,6 +1774,18 @@ update_installed_flows_by_compare(struct ovn_desired_flow_table *flow_table, /* Copy 'd' from 'flow_table' to installed_flows. */ i = installed_flow_dup(d); hmap_insert(&installed_flows, &i->match_hmap_node, i->flow.hash); + } else if (i->desired_flow != d) { + /* If a matching installed flow was found but its actions are + * conflicting with the desired flow actions, we should chose + * to install the least restrictive one (i.e., no conjunctive + * action). + */ + if (flow_action_has_conj(&i->flow) && + !flow_action_has_conj(&d->flow)) { + flow_log_actions_conflict("Use new flow", &i->flow, &d->flow); + installed_flow_mod(&i->flow, &d->flow, msgs); + ovn_flow_log(&i->flow, "updating installed"); + } } link_installed_to_desired(i, d); } @@ -1817,7 +1916,19 @@ update_installed_flows_by_track(struct ovn_desired_flow_table *flow_table, installed_flow_mod(&i->flow, &f->flow, msgs); } else { /* Adding a new flow that conflicts with an existing installed - * flow, so just add it to the link. */ + * flow, so just add it to the link. + * + * However, if the existing installed flow is less restrictive + * (i.e., no conjunctive action), we should chose it over the + * existing installed flow. + */ + if (flow_action_has_conj(&i->flow) && + !flow_action_has_conj(&f->flow)) { + flow_log_actions_conflict("Use new flow", + &i->flow, &f->flow); + ovn_flow_log(&i->flow, "updating installed (tracked)"); + installed_flow_mod(&i->flow, &f->flow, msgs); + } link_installed_to_desired(i, f); } /* The track_list_node emptyness is used to check if the node is diff --git a/tests/ovn.at b/tests/ovn.at index 7769b69..6b77057 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -13709,6 +13709,201 @@ grep conjunction.*conjunction.*conjunction | wc -l`]) OVN_CLEANUP([hv1]) AT_CLEANUP +AT_SETUP([ovn -- Superseeding ACLs with conjunction]) +ovn_start + +ovn-nbctl ls-add ls1 + +ovn-nbctl lsp-add ls1 ls1-lp1 \ +-- lsp-set-addresses ls1-lp1 "f0:00:00:00:00:01" + +ovn-nbctl lsp-add ls1 ls1-lp2 \ +-- lsp-set-addresses ls1-lp2 "f0:00:00:00:00:02" + +net_add n1 +sim_add hv1 + +as hv1 +ovs-vsctl add-br br-phys +ovn_attach n1 br-phys 192.168.0.1 +ovs-vsctl -- add-port br-int hv1-vif1 -- \ + set interface hv1-vif1 external-ids:iface-id=ls1-lp1 \ + options:tx_pcap=hv1/vif1-tx.pcap \ + options:rxq_pcap=hv1/vif1-rx.pcap \ + ofport-request=1 + +ovs-vsctl -- add-port br-int hv1-vif2 -- \ + set interface hv1-vif2 external-ids:iface-id=ls1-lp2 \ + options:tx_pcap=hv1/vif2-tx.pcap \ + options:rxq_pcap=hv1/vif2-rx.pcap \ + ofport-request=2 + +# test_ip INPORT SRC_MAC DST_MAC SRC_IP DST_IP OUTPORT... +# +# This shell function causes an ip packet to be received on INPORT. +# The packet's content has Ethernet destination DST and source SRC +# (each exactly 12 hex digits) and Ethernet type ETHTYPE (4 hex digits). +# The OUTPORTs (zero or more) list the VIFs on which the packet should +# be received. INPORT and the OUTPORTs are specified as logical switch +# port numbers, e.g. 11 for vif11. +test_ip() { + # This packet has bad checksums but logical L3 routing doesn't check. + local inport=$1 src_mac=$2 dst_mac=$3 src_ip=$4 dst_ip=$5 + local packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}\ +${dst_ip}0035111100080000 + shift; shift; shift; shift; shift + as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet + for outport; do + echo $packet >> $outport.expected + done +} + +ip_to_hex() { + printf "%02x%02x%02x%02x" "$@" +} + +reset_pcap_file() { + local iface=$1 + local pcap_file=$2 + ovs-vsctl -- set Interface $iface options:tx_pcap=dummy-tx.pcap \ +options:rxq_pcap=dummy-rx.pcap + rm -f ${pcap_file}*.pcap + ovs-vsctl -- set Interface $iface options:tx_pcap=${pcap_file}-tx.pcap \ +options:rxq_pcap=${pcap_file}-rx.pcap +} + +# Add a default deny ACL and an allow ACL for specific IP traffic. +ovn-nbctl acl-add ls1 to-lport 2 'arp' allow +ovn-nbctl acl-add ls1 to-lport 1 'ip4' drop +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 +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 +ovn-nbctl --wait=hv sync + +# Traffic 10.0.0.1, 10.0.0.2 -> 10.0.0.3, 10.0.0.4 should be allowed. +for src in `seq 1 2`; do + for dst in `seq 3 4`; do + sip=`ip_to_hex 10 0 0 $src` + dip=`ip_to_hex 10 0 0 $dst` + + test_ip 1 f00000000001 f00000000002 $sip $dip 2 + done +done + +# Traffic 10.0.0.1, 10.0.0.2 -> 10.0.0.5 should be dropped. +dip=`ip_to_hex 10 0 0 5` +for src in `seq 1 2`; do + sip=`ip_to_hex 10 0 0 $src` + + test_ip 1 f00000000001 f00000000002 $sip $dip +done + +cat 2.expected > expout +$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets +AT_CHECK([cat 2.packets], [0], [expout]) +reset_pcap_file hv1-vif2 hv1/vif2 +rm -f 2.packets +> 2.expected + +# Add a less restrictive allow ACL for src IP 10.0.0.1 +ovn-nbctl acl-add ls1 to-lport 3 'ip4.src==10.0.0.1' allow +ovn-nbctl --wait=hv sync + +# Check OVS flows, the less restrictive flows should have been installed. +AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=45 | \ + grep "priority=1003" | awk '{print $7 " " $8}' | sort], [0], [dnl +priority=1003,conj_id=2,ip,metadata=0x1 actions=resubmit(,46) +priority=1003,conj_id=3,ip,metadata=0x1 actions=resubmit(,46) +priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(2,1/2),conjunction(3,1/2) +priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(2,1/2),conjunction(3,1/2) +priority=1003,ip,metadata=0x1,nw_src=10.0.0.1 actions=resubmit(,46) +priority=1003,ip,metadata=0x1,nw_src=10.0.0.2 actions=conjunction(2,2/2) +priority=1003,ip,metadata=0x1,nw_src=10.0.0.42 actions=conjunction(3,2/2) +]) + +# Traffic 10.0.0.1, 10.0.0.2 -> 10.0.0.3, 10.0.0.4 should be allowed. +for src in `seq 1 2`; do + for dst in `seq 3 4`; do + sip=`ip_to_hex 10 0 0 $src` + dip=`ip_to_hex 10 0 0 $dst` + + test_ip 1 f00000000001 f00000000002 $sip $dip 2 + done +done + +# Traffic 10.0.0.2 -> 10.0.0.5 should be dropped. +sip=`ip_to_hex 10 0 0 2` +dip=`ip_to_hex 10 0 0 5` +test_ip 1 f00000000001 f00000000002 $sip $dip + +# Traffic 10.0.0.1 -> 10.0.0.5 should be allowed. +sip=`ip_to_hex 10 0 0 1` +dip=`ip_to_hex 10 0 0 5` +test_ip 1 f00000000001 f00000000002 $sip $dip 2 + +cat 2.expected > expout +$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets +AT_CHECK([cat 2.packets], [0], [expout]) +reset_pcap_file hv1-vif2 hv1/vif2 +rm -f 2.packets +> 2.expected + +# Remove the less restrictive allow ACL. +ovn-nbctl acl-del ls1 to-lport 3 'ip4.src==10.0.0.1' +ovn-nbctl --wait=hv sync + +# Check OVS flows, the 10.0.0.1 conjunction should have been reinstalled. +AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=45 | \ + grep "priority=1003" | awk '{print $7 " " $8}' | sort], [0], [dnl +priority=1003,conj_id=2,ip,metadata=0x1 actions=resubmit(,46) +priority=1003,conj_id=3,ip,metadata=0x1 actions=resubmit(,46) +priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(2,1/2),conjunction(3,1/2) +priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(2,1/2),conjunction(3,1/2) +priority=1003,ip,metadata=0x1,nw_src=10.0.0.1 actions=conjunction(2,2/2),conjunction(3,2/2) +priority=1003,ip,metadata=0x1,nw_src=10.0.0.2 actions=conjunction(2,2/2) +priority=1003,ip,metadata=0x1,nw_src=10.0.0.42 actions=conjunction(3,2/2) +]) + +# Traffic 10.0.0.1, 10.0.0.2 -> 10.0.0.3, 10.0.0.4 should be allowed. +for src in `seq 1 2`; do + for dst in `seq 3 4`; do + sip=`ip_to_hex 10 0 0 $src` + dip=`ip_to_hex 10 0 0 $dst` + + test_ip 1 f00000000001 f00000000002 $sip $dip 2 + done +done + +# Traffic 10.0.0.1, 10.0.0.2 -> 10.0.0.5 should be dropped. +dip=`ip_to_hex 10 0 0 5` +for src in `seq 1 2`; do + sip=`ip_to_hex 10 0 0 $src` + + test_ip 1 f00000000001 f00000000002 $sip $dip +done + +cat 2.expected > expout +$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets +AT_CHECK([cat 2.packets], [0], [expout]) + +# Re-add the less restrictive allow ACL for src IP 10.0.0.1 +ovn-nbctl acl-add ls1 to-lport 3 'ip4.src==10.0.0.1' allow +ovn-nbctl --wait=hv sync + +# Check OVS flows, the less restrictive flows should have been installed. +AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=45 | \ + grep "priority=1003" | awk '{print $7 " " $8}' | sort], [0], [dnl +priority=1003,conj_id=2,ip,metadata=0x1 actions=resubmit(,46) +priority=1003,conj_id=3,ip,metadata=0x1 actions=resubmit(,46) +priority=1003,ip,metadata=0x1,nw_dst=10.0.0.3 actions=conjunction(2,1/2),conjunction(3,1/2) +priority=1003,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(2,1/2),conjunction(3,1/2) +priority=1003,ip,metadata=0x1,nw_src=10.0.0.1 actions=resubmit(,46) +priority=1003,ip,metadata=0x1,nw_src=10.0.0.2 actions=conjunction(2,2/2) +priority=1003,ip,metadata=0x1,nw_src=10.0.0.42 actions=conjunction(3,2/2) +]) + +OVN_CLEANUP([hv1]) +AT_CLEANUP + # 3 hypervisors, one logical switch, 3 logical ports per hypervisor AT_SETUP([ovn -- L2 Drop and Allow ACL w/ Stateful ACL]) ovn_start