From patchwork Tue Apr 25 11:05:28 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Alvarez Sanchez X-Patchwork-Id: 754748 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3wC0jc1lydz9s80 for ; Tue, 25 Apr 2017 21:05:08 +1000 (AEST) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 16F88B44; Tue, 25 Apr 2017 11:05:03 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id E7B39B30 for ; Tue, 25 Apr 2017 11:05:01 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id 35F0779 for ; Tue, 25 Apr 2017 11:05:00 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 7D2C76408A for ; Tue, 25 Apr 2017 11:04:59 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com 7D2C76408A Authentication-Results: ext-mx09.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx09.extmail.prod.ext.phx2.redhat.com; spf=pass smtp.mailfrom=dalvarez@redhat.com DKIM-Filter: OpenDKIM Filter v2.11.0 mx1.redhat.com 7D2C76408A Received: from devstack.novalocal (unknown [10.16.19.47]) by smtp.corp.redhat.com (Postfix) with ESMTP id 12B397FE93; Tue, 25 Apr 2017 11:04:58 +0000 (UTC) From: Daniel Alvarez To: dev@openvswitch.org Date: Tue, 25 Apr 2017 11:05:28 +0000 Message-Id: <1493118328-21311-1-git-send-email-dalvarez@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.15 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.38]); Tue, 25 Apr 2017 11:04:59 +0000 (UTC) X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Subject: [ovs-dev] [PATCH] OVN localport type support X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org This patch introduces a new type of OVN ports called "localport". These ports will be present in every hypervisor and may have the same IP/MAC addresses. They are not bound to any chassis and traffic to these ports will never go through a tunnel. Its main use case is the OpenStack metadata API support which relies on a local agent running on every hypervisor and serving metadata to VM's locally. This service is described in detail at [0]. Signed-off-by: Daniel Alvarez --- ovn/controller/binding.c | 87 ++++++++++++++++++++---- ovn/controller/ovn-controller.8.xml | 15 +++++ ovn/controller/physical.c | 85 ++++++++++++++++++------ ovn/northd/ovn-northd.8.xml | 8 +-- ovn/northd/ovn-northd.c | 6 +- ovn/ovn-architecture.7.xml | 20 ++++-- ovn/ovn-nb.xml | 9 +++ ovn/ovn-sb.xml | 16 ++++- tests/ovn.at | 129 ++++++++++++++++++++++++++++++++++++ 9 files changed, 329 insertions(+), 46 deletions(-) diff --git a/ovn/controller/binding.c b/ovn/controller/binding.c index 95e9deb..a0fb778 100644 --- a/ovn/controller/binding.c +++ b/ovn/controller/binding.c @@ -352,6 +352,43 @@ setup_qos(const char *egress_iface, struct hmap *queue_map) hmap_destroy(&consistent_queues); netdev_close(netdev_phy); } +static bool +set_ovsport_external_ids(struct controller_ctx *ctx, + const struct ovsrec_bridge *bridge, + const struct ovsrec_interface *iface_rec, + const char *key, + const char *value) +{ + if (!ctx->ovs_idl_txn || !bridge || !iface_rec || !key) { + return false; + } + + /* Find the port with this interface and add the key/value pair to its + * external_ids. Assume that an interface corresponds only to one port. */ + int i; + for (i = 0; i < bridge->n_ports; i++) { + const struct ovsrec_port *port_rec = bridge->ports[i]; + int j; + + if (!strcmp(port_rec->name, bridge->name)) { + continue; + } + + for (j = 0; j < port_rec->n_interfaces; j++) { + if (port_rec->interfaces[j] == iface_rec) { + struct smap new_ids; + smap_clone(&new_ids, &port_rec->external_ids); + smap_replace(&new_ids, key, value); + ovsrec_port_verify_external_ids(port_rec); + ovsrec_port_set_external_ids(port_rec, &new_ids); + smap_destroy(&new_ids); + return true; + } + } + } + return false; +} + static void consider_local_datapath(struct controller_ctx *ctx, @@ -359,6 +396,7 @@ consider_local_datapath(struct controller_ctx *ctx, const struct lport_index *lports, const struct sbrec_chassis *chassis_rec, const struct sbrec_port_binding *binding_rec, + const struct ovsrec_bridge *bridge, struct hmap *qos_map, struct hmap *local_datapaths, struct shash *lport_to_iface, @@ -368,19 +406,40 @@ consider_local_datapath(struct controller_ctx *ctx, = shash_find_data(lport_to_iface, binding_rec->logical_port); bool our_chassis = false; - if (iface_rec - || (binding_rec->parent_port && binding_rec->parent_port[0] && - sset_contains(local_lports, binding_rec->parent_port))) { - if (binding_rec->parent_port && binding_rec->parent_port[0]) { - /* Add child logical port to the set of all local ports. */ - sset_add(local_lports, binding_rec->logical_port); - } - add_local_datapath(ldatapaths, lports, binding_rec->datapath, - false, local_datapaths); - if (iface_rec && qos_map && ctx->ovs_idl_txn) { - get_qos_params(binding_rec, qos_map); + + if (iface_rec && !strcmp(binding_rec->type, "localport")) { + /* Make sure localport external_id is present, otherwise set it + * for both interface and port. This should only happen the first + * time. We set the value on both the interface and port because it's + * most convenient to check for the value here on the interface. Later, + * we need to read this value in physical.c, and expect to read it from + * the port there.*/ + if (!smap_get(&iface_rec->external_ids, "ovn-localport-port")) { + if (set_ovsport_external_ids(ctx, bridge, iface_rec, + "ovn-localport-port", + binding_rec->logical_port)) { + struct smap new_ids; + smap_clone(&new_ids, &iface_rec->external_ids); + smap_replace(&new_ids, "ovn-localport-port", + binding_rec->logical_port); + ovsrec_interface_verify_external_ids(iface_rec); + ovsrec_interface_set_external_ids(iface_rec, &new_ids); + smap_destroy(&new_ids); + } } - our_chassis = true; + } else if (iface_rec + || (binding_rec->parent_port && binding_rec->parent_port[0] && + sset_contains(local_lports, binding_rec->parent_port))) { + if (binding_rec->parent_port && binding_rec->parent_port[0]) { + /* Add child logical port to the set of all local ports. */ + sset_add(local_lports, binding_rec->logical_port); + } + add_local_datapath(ldatapaths, lports, binding_rec->datapath, + false, local_datapaths); + if (iface_rec && qos_map && ctx->ovs_idl_txn) { + get_qos_params(binding_rec, qos_map); + } + our_chassis = true; } else if (!strcmp(binding_rec->type, "l2gateway")) { const char *chassis_id = smap_get(&binding_rec->options, "l2gateway-chassis"); @@ -468,8 +527,8 @@ binding_run(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int, consider_local_datapath(ctx, ldatapaths, lports, chassis_rec, binding_rec, sset_is_empty(&egress_ifaces) ? NULL : - &qos_map, local_datapaths, &lport_to_iface, - local_lports); + br_int, &qos_map, local_datapaths, + &lport_to_iface, local_lports); } diff --git a/ovn/controller/ovn-controller.8.xml b/ovn/controller/ovn-controller.8.xml index f9cbbfe..e6d5cd5 100644 --- a/ovn/controller/ovn-controller.8.xml +++ b/ovn/controller/ovn-controller.8.xml @@ -305,6 +305,21 @@ logical patch port that it implements.

+ +
+ external_ids:ovn-localport-port in the Port + and Interface tables +
+ +
+

+ The presence of this key identifies a port as a + localport so that ovn-controller can + properly set the right flows to allow only local traffic and + drop any packets directed to an external chassis. +

+
+

Runtime Management Commands

diff --git a/ovn/controller/physical.c b/ovn/controller/physical.c index 0f1aa63..6a745f8 100644 --- a/ovn/controller/physical.c +++ b/ovn/controller/physical.c @@ -59,6 +59,8 @@ physical_register_ovs_idl(struct ovsdb_idl *ovs_idl) static struct simap localvif_to_ofport = SIMAP_INITIALIZER(&localvif_to_ofport); static struct hmap tunnels = HMAP_INITIALIZER(&tunnels); +static struct simap localport_to_ofport = + SIMAP_INITIALIZER(&localport_to_ofport); /* Maps from a chassis to the OpenFlow port number of the tunnel that can be * used to reach that chassis. */ @@ -601,6 +603,27 @@ consider_port_binding(enum mf_field_id mff_ovn_geneve, } else { /* Remote port connected by tunnel */ + /* Table 32, priority 150. + * ======================= + * + * Drop traffic originated from a localport to a remote destination. + */ + struct simap_node *localport; + SIMAP_FOR_EACH (localport, &localport_to_ofport) { + int inport; + if ((inport = simap_get(&localport_to_ofport, localport->name))) { + match_init_catchall(&match); + ofpbuf_clear(ofpacts_p); + /* Match localport in_port. */ + match_set_in_port(&match, inport); + /* Match MFF_LOG_DATAPATH, MFF_LOG_OUTPORT. */ + match_set_metadata(&match, htonll(dp_key)); + match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key); + ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 150, 0, + &match, ofpacts_p); + } + } + /* Table 32, priority 100. * ======================= * @@ -753,6 +776,34 @@ consider_mc_group(enum mf_field_id mff_ovn_geneve, sset_destroy(&remote_chassis); } +static bool +simap_sync(struct simap *old, const struct simap *new) +{ + struct simap_node *vif_name, *vif_name_next; + bool has_changed = false; + + SIMAP_FOR_EACH_SAFE (vif_name, vif_name_next, old) { + int newport; + if ((newport = simap_get(new, vif_name->name))) { + if (newport != simap_get(old, vif_name->name)) { + simap_put(old, vif_name->name, newport); + has_changed = true; + } + } else { + simap_find_and_delete(old, vif_name->name); + has_changed = true; + } + } + SIMAP_FOR_EACH (vif_name, new) { + if (!simap_get(old, vif_name->name)) { + simap_put(old, vif_name->name, + simap_get(new, vif_name->name)); + has_changed = true; + } + } + return has_changed; +} + void physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve, const struct ovsrec_bridge *br_int, @@ -768,6 +819,9 @@ physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve, SIMAP_INITIALIZER(&new_localvif_to_ofport); struct simap new_tunnel_to_ofport = SIMAP_INITIALIZER(&new_tunnel_to_ofport); + struct simap new_localport_to_ofport = + SIMAP_INITIALIZER(&new_localport_to_ofport); + for (int i = 0; i < br_int->n_ports; i++) { const struct ovsrec_port *port_rec = br_int->ports[i]; if (!strcmp(port_rec->name, br_int->name)) { @@ -784,6 +838,8 @@ physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve, "ovn-localnet-port"); const char *l2gateway = smap_get(&port_rec->external_ids, "ovn-l2gateway-port"); + const char *localport = smap_get(&port_rec->external_ids, + "ovn-localport-port"); for (int j = 0; j < port_rec->n_interfaces; j++) { const struct ovsrec_interface *iface_rec = port_rec->interfaces[j]; @@ -848,6 +904,9 @@ physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve, "iface-id"); if (iface_id) { simap_put(&new_localvif_to_ofport, iface_id, ofport); + if (localport) { + simap_put(&new_localport_to_ofport, localport, ofport); + } } } } @@ -864,26 +923,11 @@ physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve, } /* Capture changed or removed openflow ports. */ - struct simap_node *vif_name, *vif_name_next; - SIMAP_FOR_EACH_SAFE (vif_name, vif_name_next, &localvif_to_ofport) { - int newport; - if ((newport = simap_get(&new_localvif_to_ofport, vif_name->name))) { - if (newport != simap_get(&localvif_to_ofport, vif_name->name)) { - simap_put(&localvif_to_ofport, vif_name->name, newport); - physical_map_changed = true; - } - } else { - simap_find_and_delete(&localvif_to_ofport, vif_name->name); - physical_map_changed = true; - } - } - SIMAP_FOR_EACH (vif_name, &new_localvif_to_ofport) { - if (!simap_get(&localvif_to_ofport, vif_name->name)) { - simap_put(&localvif_to_ofport, vif_name->name, - simap_get(&new_localvif_to_ofport, vif_name->name)); - physical_map_changed = true; - } - } + physical_map_changed = simap_sync(&localvif_to_ofport, + &new_localvif_to_ofport); + /* Do the same for all localports. */ + physical_map_changed |= simap_sync(&localport_to_ofport, + &new_localport_to_ofport); if (physical_map_changed) { /* Reprocess logical flow table immediately. */ poll_immediate_wake(); @@ -1043,4 +1087,5 @@ physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve, simap_destroy(&new_localvif_to_ofport); simap_destroy(&new_tunnel_to_ofport); + simap_destroy(&new_localport_to_ofport); } diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml index ab8fd88..c4552c6 100644 --- a/ovn/northd/ovn-northd.8.xml +++ b/ovn/northd/ovn-northd.8.xml @@ -521,8 +521,8 @@ output;

- These flows are omitted for logical ports (other than router ports) - that are down. + These flows are omitted for logical ports (other than router ports or + localport ports) that are down.

@@ -548,8 +548,8 @@ nd_na {

- These flows are omitted for logical ports (other than router ports) - that are down. + These flows are omitted for logical ports (other than router ports or + localport ports) that are down.

diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c index d0a5ba2..126c89f 100644 --- a/ovn/northd/ovn-northd.c +++ b/ovn/northd/ovn-northd.c @@ -3252,9 +3252,11 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports, /* * Add ARP/ND reply flows if either the * - port is up or - * - port type is router + * - port type is router or + * - port type is localport */ - if (!lsp_is_up(op->nbsp) && strcmp(op->nbsp->type, "router")) { + if (!lsp_is_up(op->nbsp) && strcmp(op->nbsp->type, "router") && + strcmp(op->nbsp->type, "localport")) { continue; } diff --git a/ovn/ovn-architecture.7.xml b/ovn/ovn-architecture.7.xml index d8114f1..2866666 100644 --- a/ovn/ovn-architecture.7.xml +++ b/ovn/ovn-architecture.7.xml @@ -409,6 +409,15 @@ logical patch ports at each such point of connectivity, one on each side. +
  • + Localport ports represent the points of local + connectivity between logical switches and VIFs. These ports are + present in every chassis (not bound to any particular one) and + traffic from them will never go through a tunnel. A + localport is expected to only generate traffic destined + for a local destination, typically in response to a request it + received. +
  • @@ -986,11 +995,12 @@ hypervisor. Each flow's actions implement sending a packet to the port it matches. For unicast logical output ports on remote hypervisors, the actions set the tunnel key to the correct value, then send the - packet on the tunnel port to the correct hypervisor. (When the remote - hypervisor receives the packet, table 0 there will recognize it as a - tunneled packet and pass it along to table 33.) For multicast logical - output ports, the actions send one copy of the packet to each remote - hypervisor, in the same way as for unicast destinations. If a + packet on the tunnel port to the correct hypervisor (unless the packet + comes from a localport, in which case it will be dropped). (When the + remote hypervisor receives the packet, table 0 there will recognize it + as a tunneled packet and pass it along to table 33.) For multicast + logical output ports, the actions send one copy of the packet to each + remote hypervisor, in the same way as for unicast destinations. If a multicast group includes a logical port or ports on the local hypervisor, then its actions also resubmit to table 33. Table 32 also includes a fallback flow that resubmits to table 33 if there is no diff --git a/ovn/ovn-nb.xml b/ovn/ovn-nb.xml index 7a1c20e..9b73692 100644 --- a/ovn/ovn-nb.xml +++ b/ovn/ovn-nb.xml @@ -259,6 +259,15 @@ to model direct connectivity to an existing network. +
    localport
    +
    + A connection to a local VIF. Traffic that arrives on a + localport is never forwarded over a tunnel to another + chassis. These ports are present on every chassis and have the same + address in all of them. This is used to model connectivity to local + services that run on every hypervisor. +
    +
    l2gateway
    A connection to a physical network. diff --git a/ovn/ovn-sb.xml b/ovn/ovn-sb.xml index 5542f7e..b4cfd71 100644 --- a/ovn/ovn-sb.xml +++ b/ovn/ovn-sb.xml @@ -311,7 +311,7 @@ transmitted and received with reasonable performance. It is a hint to senders transmitting data to this chassis that they should use checksums to protect OVN metadata. ovn-controller - populates this key with the value defined in + populates this key with the value defined in column of the Open_vSwitch database's table. Other applications should treat this key as @@ -1739,6 +1739,11 @@ tcp.flags = RST; connectivity to the corresponding physical network.
    +
    localport
    +
    + Always empty. A localport port is present on every chassis. +
    +
    l3gateway
    The physical location of the L3 gateway. To successfully identify a @@ -1819,6 +1824,15 @@ tcp.flags = RST; to model direct connectivity to an existing network.
    +
    localport
    +
    + A connection to a local VIF. Traffic that arrives on a + localport is never forwarded over a tunnel to another + chassis. These ports are present on every chassis and have the same + address in all of them. This is used to model connectivity to local + services that run on every hypervisor. +
    +
    l2gateway
    An L2 connection to a physical network. The chassis this diff --git a/tests/ovn.at b/tests/ovn.at index 088bbf6..22ba1ac 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -6994,3 +6994,132 @@ OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected]) OVN_CLEANUP([hv1],[hv2]) AT_CLEANUP + +AT_SETUP([ovn -- 2 HVs, 1 lport/HV, localport ports]) +AT_SKIP_IF([test $HAVE_PYTHON = no]) +ovn_start + +ovn-nbctl ls-add ls1 + +# Add localport to the switch +ovn-nbctl lsp-add ls1 lp01 +ovn-nbctl lsp-set-addresses lp01 f0:00:00:00:00:01 +ovn-nbctl lsp-set-type lp01 localport + +net_add n1 + +for i in 1 2; do + sim_add hv$i + as hv$i + ovs-vsctl add-br br-phys + ovn_attach n1 br-phys 192.168.0.$i + ovs-vsctl add-port br-int vif01 -- \ + set Interface vif01 external-ids:iface-id=lp01 \ + options:tx_pcap=hv${i}/vif01-tx.pcap \ + options:rxq_pcap=hv${i}/vif01-rx.pcap \ + ofport-request=${i}0 + + ovs-vsctl add-port br-int vif${i}1 -- \ + set Interface vif${i}1 external-ids:iface-id=lp${i}1 \ + options:tx_pcap=hv${i}/vif${i}1-tx.pcap \ + options:rxq_pcap=hv${i}/vif${i}1-rx.pcap \ + ofport-request=${i}1 + + ovn-nbctl lsp-add ls1 lp${i}1 + ovn-nbctl lsp-set-addresses lp${i}1 f0:00:00:00:00:${i}1 + ovn-nbctl lsp-set-port-security lp${i}1 f0:00:00:00:00:${i}1 + + ovn-sbctl list port_binding + ovn-sbctl show + ovn-sbctl list chassis + ovs-vsctl list interface + ovs-vsctl list open + ovn-nbctl lsp-get-up lp${i}1 + ps -ef |grep ovn-controller + OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up lp${i}1` = xup]) +done + +ovn-nbctl --wait=sb sync +ovn-sbctl dump-flows + +ovn_populate_arp + +# Given the name of a logical port, prints the name of the hypervisor +# on which it is located. +vif_to_hv() { + echo hv${1%?} +} +# +# test_packet INPORT DST SRC ETHTYPE EOUT LOUT DEFHV +# +# This shell function causes a 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). INPORT is specified as +# logical switch port numbers, e.g. 11 for vif11. +# +# EOUT is the end-to-end output port, that is, where the packet will end up +# after possibly bouncing through one or more localnet ports. LOUT is the +# logical output port, which might be a localnet port, as seen by ovn-trace +# (which doesn't know what localnet ports are connected to and therefore can't +# figure out the end-to-end answer). +# +# DEFHV is the default hypervisor from where the packet is going to be sent +# if the source port is a localport. +for i in 1 2; do + for j in 0 1; do + : > $i$j.expected + done +done +test_packet() { + local inport=$1 dst=$2 src=$3 eth=$4 eout=$5 lout=$6 defhv=$7 + echo "$@" + + # First try tracing the packet. + uflow="inport==\"lp$inport\" && eth.dst==$dst && eth.src==$src && eth.type==0x$eth" + if test $lout != drop; then + echo "output(\"$lout\");" + fi > expout + AT_CAPTURE_FILE([trace]) + AT_CHECK([ovn-trace --all ls1 "$uflow" | tee trace | sed '1,/Minimal trace/d'], [0], [expout]) + + # Then actually send a packet, for an end-to-end test. + local packet=$(echo $dst$src | sed 's/://g')${eth} + hv=`vif_to_hv $inport` + # If hypervisor 0 (localport) use the defhv parameter + if test $hv == hv0; then + hv=$defhv + fi + vif=vif$inport + as $hv ovs-appctl netdev-dummy/receive $vif $packet + if test $eout != drop; then + echo $packet >> ${eout#lp}.expected + fi +} + + +# lp11 and lp21 are on different hypervisors +test_packet 11 f0:00:00:00:00:21 f0:00:00:00:00:11 1121 lp21 lp21 +test_packet 21 f0:00:00:00:00:11 f0:00:00:00:00:21 2111 lp11 lp11 + +# Both VIFs should be able to reach the localport on their own HV +test_packet 11 f0:00:00:00:00:01 f0:00:00:00:00:11 1101 lp01 lp01 +test_packet 21 f0:00:00:00:00:01 f0:00:00:00:00:21 2101 lp01 lp01 + +# Packet sent from localport on same hv should reach the vif +test_packet 01 f0:00:00:00:00:11 f0:00:00:00:00:01 0111 lp11 lp11 hv1 +test_packet 01 f0:00:00:00:00:21 f0:00:00:00:00:01 0121 lp21 lp21 hv2 + +# Packet sent from localport on different hv should be dropped +test_packet 01 f0:00:00:00:00:21 f0:00:00:00:00:01 0121 drop lp21 hv1 +test_packet 01 f0:00:00:00:00:11 f0:00:00:00:00:01 0111 drop lp11 hv2 + +# Now check the packets actually received against the ones expected. +for i in 1 2; do + for j in 0 1; do + OVN_CHECK_PACKETS([hv$i/vif$i$j-tx.pcap], [$i$j.expected]) + done +done + +OVN_CLEANUP([hv1],[hv2]) + +AT_CLEANUP