From patchwork Sun Nov 27 20:14:07 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Abhiram RN X-Patchwork-Id: 1709442 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=140.211.166.136; helo=smtp3.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: legolas.ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20210112 header.b=LVavkwrt; dkim-atps=neutral Received: from smtp3.osuosl.org (smtp3.osuosl.org [140.211.166.136]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-384) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4NL0Fn3JSjz23nT for ; Mon, 28 Nov 2022 07:15:09 +1100 (AEDT) Received: from localhost (localhost [127.0.0.1]) by smtp3.osuosl.org (Postfix) with ESMTP id 2A4EF60E72; Sun, 27 Nov 2022 20:15:07 +0000 (UTC) DKIM-Filter: OpenDKIM Filter v2.11.0 smtp3.osuosl.org 2A4EF60E72 Authentication-Results: smtp3.osuosl.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20210112 header.b=LVavkwrt X-Virus-Scanned: amavisd-new at osuosl.org Received: from smtp3.osuosl.org ([127.0.0.1]) by localhost (smtp3.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id ZdkNp1g1-HO1; Sun, 27 Nov 2022 20:15:04 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by smtp3.osuosl.org (Postfix) with ESMTPS id BA33F60E8D; Sun, 27 Nov 2022 20:15:03 +0000 (UTC) DKIM-Filter: OpenDKIM Filter v2.11.0 smtp3.osuosl.org BA33F60E8D Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 88774C0032; Sun, 27 Nov 2022 20:15:03 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp2.osuosl.org (smtp2.osuosl.org [IPv6:2605:bc80:3010::133]) by lists.linuxfoundation.org (Postfix) with ESMTP id AD5A2C002D for ; Sun, 27 Nov 2022 20:15:02 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp2.osuosl.org (Postfix) with ESMTP id DEDEE408BF for ; Sun, 27 Nov 2022 20:14:53 +0000 (UTC) DKIM-Filter: OpenDKIM Filter v2.11.0 smtp2.osuosl.org DEDEE408BF Authentication-Results: smtp2.osuosl.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20210112 header.b=LVavkwrt X-Virus-Scanned: amavisd-new at osuosl.org Received: from smtp2.osuosl.org ([127.0.0.1]) by localhost (smtp2.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id VJv9-2b3q37g for ; Sun, 27 Nov 2022 20:14:51 +0000 (UTC) X-Greylist: whitelisted by SQLgrey-1.8.0 DKIM-Filter: OpenDKIM Filter v2.11.0 smtp2.osuosl.org 54662408B2 Received: from mail-pl1-x636.google.com (mail-pl1-x636.google.com [IPv6:2607:f8b0:4864:20::636]) by smtp2.osuosl.org (Postfix) with ESMTPS id 54662408B2 for ; Sun, 27 Nov 2022 20:14:51 +0000 (UTC) Received: by mail-pl1-x636.google.com with SMTP id 4so8363884pli.0 for ; Sun, 27 Nov 2022 12:14:51 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=7X+6p5NOxTSXB1e8YQxOeKKXGdk1Jdw+pHBRpF65zzU=; b=LVavkwrtFfl8DlBC0lZSgiZyPZ08nKIPpbEt7YJLuRKT/JeDyO8yuZ/zf5fLHyURcj smFv3+GLGGhJLUYSBlbMM5XANad+ncBiDDljx7sfwvsTT3v7jejCZUirQ11aJIRGYNoy tN4JBlp4qzhH1mwJ43ftl17Tt5Ajk2PSATfMkncHI8n6ruae+HUeb9Eo27jCqn0V9I5Q BqgGYzFmFsZQfLL9nca2SD+DB7eD2S78PgqV2CIJ9uW8lpSHy/5nkDXZvKj4w7gH1f31 ikTHgiOAwue83olJEBz7wQSFxYmX4vYs2Cq195eELr46UYh0EYQsfit/Uyw9o0U4sems LE0g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=7X+6p5NOxTSXB1e8YQxOeKKXGdk1Jdw+pHBRpF65zzU=; b=K3tvjYmKGUiW4oImYkyN4GgX4RQEwOBzhqydKZfdSHVrK86serxasFbFFdcY3MbxgX sfy8W0JSvybS/OmOEpqcf/KCZYluIZzFiF74oyqXJen4J+6dMARAtClvV+NH4XTezXaH pwkDYn9/lb7ueXls6SldiUqI4qHr6dTR2RTIHy4ZK1v0qu89lYl47zrODh2rIn44moCx ho8l/U+lMdsaO6tBoUb8dXs0sd5mjHHkIkJOd4+UXo8onxjCzlFt9+Z6AxwiZZepiW+j X7UP5xymgiOmirAyqzvkL15j9YNdIwAE6xp9YrSv8dXrJNpzSNRVwIyciRmbqou0yYKe Fwvg== X-Gm-Message-State: ANoB5pk92lW7QOLvvh3Z4ixXrK0TEyVrkV3+3+qEJ3Zm9YSje9hv4PPy KEO6Hzh5LuC1f8Xm2koSTFuDyqb9ivqYhw== X-Google-Smtp-Source: AA0mqf5VQBWdJvoKpm0ImxkajCmec+Ua/cmvqmMKADXq5pQ2mGYaTMiqOtC23sBLfm/xng7F1bFytA== X-Received: by 2002:a17:902:f80b:b0:188:4e75:7365 with SMTP id ix11-20020a170902f80b00b001884e757365mr30676119plb.102.1669580089948; Sun, 27 Nov 2022 12:14:49 -0800 (PST) Received: from arn.com ([49.205.133.150]) by smtp.googlemail.com with ESMTPSA id z14-20020a170903018e00b00186a2dd3ffdsm7255658plg.15.2022.11.27.12.14.46 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 27 Nov 2022 12:14:49 -0800 (PST) From: Abhiram R N To: dev@openvswitch.org Date: Mon, 28 Nov 2022 01:44:07 +0530 Message-Id: <20221127201407.1579603-3-abhiramrn@gmail.com> X-Mailer: git-send-email 2.31.1 In-Reply-To: <20221127201407.1579603-1-abhiramrn@gmail.com> References: <20221125200219.1312140-3-abhiramrn@gmail.com> <20221127201407.1579603-1-abhiramrn@gmail.com> MIME-Version: 1.0 Cc: vbarrenk@redhat.com, vedabarrenkala@gmail.com, arn@redhat.com Subject: [ovs-dev] [OVN v17 3/3] OVN Remote Port Mirroring: controller changes to create ovs mirrors 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: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" Mirror creation just creates the mirror. The lsp-attach-mirror triggers the sequence to create Mirror in OVS DB on compute node. OVS already supports Port Mirroring. Further added test cases in ovn.at to verify end to end the functioning of Port Mirror and also verify bulk updates to mirrors. Note: This is targeted to mirror to destinations anywhere outside the cluster where the analyser resides and it need not be an OVN node. Example commands are as below: Mirror creation ovn-nbctl mirror-add mirror1 gre 0 from-lport 10.10.10.2 Attach a logical port to the mirror. ovn-nbctl lsp-attach-mirror sw0-port1 mirror1 Detach a source from Mirror ovn-nbctl lsp-detach-mirror sw0-port1 mirror1 Mirror deletion ovn-nbctl mirror-del mirror1 Co-authored-by: Veda Barrenkala Signed-off-by: Veda Barrenkala Signed-off-by: Abhiram R N --- v16-->v17: Fixed mem leak issue in mirror.c while running Mirror bulk updates test. NEWS | 1 + controller/automake.mk | 4 +- controller/mirror.c | 416 +++++++++++++++++++++++++++++ controller/mirror.h | 33 +++ controller/ovn-controller.c | 54 ++-- tests/ovn.at | 514 ++++++++++++++++++++++++++++++++++++ 6 files changed, 999 insertions(+), 23 deletions(-) create mode 100644 controller/mirror.c create mode 100644 controller/mirror.h diff --git a/NEWS b/NEWS index 6c4573b50..dbffcac0f 100644 --- a/NEWS +++ b/NEWS @@ -27,6 +27,7 @@ OVN v22.09.0 - 16 Sep 2022 any of LR's LRP IP, there is no need to create SNAT entry. Now such traffic destined to LRP IP is not dropped. - Bump python version required for building OVN to 3.6. + - Added Support for Remote Port Mirroring. OVN v22.06.0 - 03 Jun 2022 -------------------------- diff --git a/controller/automake.mk b/controller/automake.mk index c2ab1bbe6..334672b4d 100644 --- a/controller/automake.mk +++ b/controller/automake.mk @@ -41,7 +41,9 @@ controller_ovn_controller_SOURCES = \ controller/ovsport.h \ controller/ovsport.c \ controller/vif-plug.h \ - controller/vif-plug.c + controller/vif-plug.c \ + controller/mirror.h \ + controller/mirror.c controller_ovn_controller_LDADD = lib/libovn.la $(OVS_LIBDIR)/libopenvswitch.la man_MANS += controller/ovn-controller.8 diff --git a/controller/mirror.c b/controller/mirror.c new file mode 100644 index 000000000..ac9d3eea5 --- /dev/null +++ b/controller/mirror.c @@ -0,0 +1,416 @@ +/* Copyright (c) 2022 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +/* library headers */ +#include "lib/sset.h" +#include "lib/util.h" + +/* OVS includes. */ +#include "lib/vswitch-idl.h" +#include "include/openvswitch/shash.h" +#include "openvswitch/vlog.h" + +/* OVN includes. */ +#include "binding.h" +#include "lib/ovn-sb-idl.h" +#include "mirror.h" + +VLOG_DEFINE_THIS_MODULE(port_mirror); + +struct ovn_mirror { + char *name; + const struct sbrec_mirror *sb_mirror; + const struct ovsrec_mirror *ovs_mirror; + struct ovs_list mirror_src_lports; + struct ovs_list mirror_dst_lports; +}; + +struct mirror_lport { + struct ovs_list list_node; + + struct local_binding *lbinding; +}; + +static struct ovn_mirror *ovn_mirror_create(char *mirror_name); +static void ovn_mirror_add(struct shash *ovn_mirrors, + struct ovn_mirror *); +static struct ovn_mirror *ovn_mirror_find(struct shash *ovn_mirrors, + const char *mirror_name); +static void ovn_mirror_delete(struct ovn_mirror *); +static void ovn_mirror_add_lport(struct ovn_mirror *, struct local_binding *); +static void sync_ovn_mirror(struct ovn_mirror *, struct ovsdb_idl_txn *, + const struct ovsrec_bridge *); + +static void create_ovs_mirror(struct ovn_mirror *, struct ovsdb_idl_txn *, + const struct ovsrec_bridge *); +static void sync_ovs_mirror_ports(struct ovn_mirror *, + const struct ovsrec_bridge *); +static void delete_ovs_mirror(struct ovn_mirror *, + const struct ovsrec_bridge *); +static bool should_delete_ovs_mirror(struct ovn_mirror *); + +static const struct ovsrec_port *get_iface_port( + const struct ovsrec_interface *, const struct ovsrec_bridge *); + + +void +mirror_register_ovs_idl(struct ovsdb_idl *ovs_idl) +{ + ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_mirrors); + + ovsdb_idl_add_table(ovs_idl, &ovsrec_table_mirror); + ovsdb_idl_add_column(ovs_idl, &ovsrec_mirror_col_name); + ovsdb_idl_add_column(ovs_idl, &ovsrec_mirror_col_output_port); + ovsdb_idl_add_column(ovs_idl, &ovsrec_mirror_col_select_dst_port); + ovsdb_idl_add_column(ovs_idl, &ovsrec_mirror_col_select_src_port); + ovsdb_idl_add_column(ovs_idl, &ovsrec_mirror_col_external_ids); +} + +void +mirror_init(void) +{ +} + +void +mirror_destroy(void) +{ +} + +void +mirror_run(struct ovsdb_idl_txn *ovs_idl_txn, + const struct ovsrec_mirror_table *ovs_mirror_table, + const struct sbrec_mirror_table *sb_mirror_table, + const struct ovsrec_bridge *br_int, + struct shash *local_bindings) +{ + if (!ovs_idl_txn) { + return; + } + + struct shash ovn_mirrors = SHASH_INITIALIZER(&ovn_mirrors); + struct shash tmp_mirrors = SHASH_INITIALIZER(&tmp_mirrors); + + /* Iterate through sb mirrors and build the 'ovn_mirrors'. */ + const struct sbrec_mirror *sb_mirror; + SBREC_MIRROR_TABLE_FOR_EACH (sb_mirror, sb_mirror_table) { + struct ovn_mirror *m = ovn_mirror_create(sb_mirror->name); + m->sb_mirror = sb_mirror; + ovn_mirror_add(&ovn_mirrors, m); + } + + /* Iterate through ovs mirrors and add to the 'ovn_mirrors'. */ + const struct ovsrec_mirror *ovs_mirror; + OVSREC_MIRROR_TABLE_FOR_EACH (ovs_mirror, ovs_mirror_table) { + bool ovn_owned_mirror = smap_get_bool(&ovs_mirror->external_ids, + "ovn-owned", false); + if (!ovn_owned_mirror) { + continue; + } + + struct ovn_mirror *m = ovn_mirror_find(&ovn_mirrors, ovs_mirror->name); + if (!m) { + m = ovn_mirror_create(ovs_mirror->name); + shash_add(&tmp_mirrors, ovs_mirror->name, m); + } + m->ovs_mirror = ovs_mirror; + } + + if (shash_is_empty(&ovn_mirrors)) { + shash_destroy(&ovn_mirrors); + if (shash_is_empty(&tmp_mirrors)) { + shash_destroy(&tmp_mirrors); + return; + } else { + goto cleanup; + } + } + + /* Iterate through the local bindings and if the local binding's 'pb' has + * mirrors associated, add it to the ovn_mirror. */ + struct shash_node *node; + SHASH_FOR_EACH (node, local_bindings) { + struct local_binding *lbinding = node->data; + const struct sbrec_port_binding *pb = + local_binding_get_primary_pb(local_bindings, lbinding->name); + if (!pb || !pb->n_mirror_rules) { + continue; + } + + for (size_t i = 0; i < pb->n_mirror_rules; i++) { + struct ovn_mirror *m = ovn_mirror_find(&ovn_mirrors, + pb->mirror_rules[i]->name); + ovs_assert(m); + ovn_mirror_add_lport(m, lbinding); + } + } + + /* Iterate through the built 'ovn_mirrors' and + * sync with the local ovsdb i.e. + * create/update or delete the ovsrec mirror(s). */ + SHASH_FOR_EACH (node, &ovn_mirrors) { + struct ovn_mirror *m = node->data; + sync_ovn_mirror(m, ovs_idl_txn, br_int); + } + + SHASH_FOR_EACH_SAFE (node, &ovn_mirrors) { + ovn_mirror_delete(node->data); + shash_delete(&ovn_mirrors, node); + } + + shash_destroy(&ovn_mirrors); + +cleanup: + SHASH_FOR_EACH_SAFE (node, &tmp_mirrors) { + ovn_mirror_delete(node->data); + shash_delete(&tmp_mirrors, node); + } + + shash_destroy(&tmp_mirrors); +} + +/* Static functions. */ +static struct ovn_mirror * +ovn_mirror_create(char *mirror_name) +{ + struct ovn_mirror *m = xzalloc(sizeof *m); + m->name = xstrdup(mirror_name); + ovs_list_init(&m->mirror_src_lports); + ovs_list_init(&m->mirror_dst_lports); + return m; +} + +static void +ovn_mirror_add(struct shash *ovn_mirrors, struct ovn_mirror *m) +{ + shash_add(ovn_mirrors, m->sb_mirror->name, m); +} + +static struct ovn_mirror * +ovn_mirror_find(struct shash *ovn_mirrors, const char *mirror_name) +{ + return shash_find_data(ovn_mirrors, mirror_name); +} + +static void +ovn_mirror_delete(struct ovn_mirror *m) +{ + free(m->name); + struct mirror_lport *m_lport; + LIST_FOR_EACH_POP (m_lport, list_node, &m->mirror_src_lports) { + free(m_lport); + } + + LIST_FOR_EACH_POP (m_lport, list_node, &m->mirror_dst_lports) { + free(m_lport); + } + + free(m); +} + +static void +ovn_mirror_add_lport(struct ovn_mirror *m, struct local_binding *lbinding) +{ + struct mirror_lport *m_lport = xzalloc(sizeof *m_lport); + m_lport->lbinding = lbinding; + if (!strcmp(m->sb_mirror->filter, "from-lport")) { + ovs_list_push_back(&m->mirror_src_lports, &m_lport->list_node); + } else if (!strcmp(m->sb_mirror->filter, "to-lport")) { + ovs_list_push_back(&m->mirror_dst_lports, &m_lport->list_node); + } else { + ovs_list_push_back(&m->mirror_src_lports, &m_lport->list_node); + m_lport = xzalloc(sizeof *m_lport); + m_lport->lbinding = lbinding; + ovs_list_push_back(&m->mirror_dst_lports, &m_lport->list_node); + } +} + +static void +create_and_set_options(struct ovsrec_interface *iface, + const struct sbrec_mirror *sb_mirror) +{ + struct smap options = SMAP_INITIALIZER(&options); + char *key; + + key = xasprintf("%ld", (long int) sb_mirror->index); + smap_add(&options, "remote_ip", sb_mirror->sink); + smap_add(&options, "key", key); + if (!strcmp(sb_mirror->type, "erspan")) { + /* Set the ERSPAN index */ + smap_add(&options, "erspan_idx", key); + smap_add(&options, "erspan_ver", "1"); + } + ovsrec_interface_set_options(iface, &options); + + free(key); + smap_destroy(&options); +} + +static void +check_and_update_interface_table(const struct sbrec_mirror *sb_mirror, + const struct ovsrec_mirror *ovs_mirror) +{ + char *type; + struct ovsrec_interface *iface = + ovs_mirror->output_port->interfaces[0]; + struct smap *opts = &iface->options; + const char *erspan_ver = smap_get(opts, "erspan_ver"); + if (erspan_ver) { + type = "erspan"; + } else { + type = "gre"; + } + if (strcmp(type, sb_mirror->type)) { + ovsrec_interface_set_type(iface, sb_mirror->type); + } + create_and_set_options(iface, sb_mirror); +} + +static void +sync_ovn_mirror(struct ovn_mirror *m, struct ovsdb_idl_txn *ovs_idl_txn, + const struct ovsrec_bridge *br_int) +{ + if (should_delete_ovs_mirror(m)) { + /* Delete the ovsrec mirror. */ + delete_ovs_mirror(m, br_int); + return; + } + + if (ovs_list_is_empty(&m->mirror_src_lports) && + ovs_list_is_empty(&m->mirror_dst_lports)) { + /* Nothing to do. */ + return; + } + + if (m->sb_mirror && !m->ovs_mirror) { + create_ovs_mirror(m, ovs_idl_txn, br_int); + } else { + check_and_update_interface_table(m->sb_mirror, m->ovs_mirror); + } + + sync_ovs_mirror_ports(m, br_int); +} + +static bool +should_delete_ovs_mirror(struct ovn_mirror *m) +{ + if (!m->ovs_mirror) { + return false; + } + + if (m->ovs_mirror && !m->sb_mirror) { + return true; + } + + return (ovs_list_is_empty(&m->mirror_src_lports) && + ovs_list_is_empty(&m->mirror_dst_lports)); +} + +static const struct ovsrec_port * +get_iface_port(const struct ovsrec_interface *iface, + const struct ovsrec_bridge *br_int) +{ + for (size_t i = 0; i < br_int->n_ports; i++) { + const struct ovsrec_port *p = br_int->ports[i]; + for (size_t j = 0; j < p->n_interfaces; j++) { + if (!strcmp(iface->name, p->interfaces[j]->name)) { + return p; + } + } + } + return NULL; +} + +static void +create_ovs_mirror(struct ovn_mirror *m, struct ovsdb_idl_txn *ovs_idl_txn, + const struct ovsrec_bridge *br_int) +{ + struct ovsrec_interface *iface = ovsrec_interface_insert(ovs_idl_txn); + char *port_name = xasprintf("ovn-%s", m->name); + + ovsrec_interface_set_name(iface, port_name); + ovsrec_interface_set_type(iface, m->sb_mirror->type); + create_and_set_options(iface, m->sb_mirror); + + struct ovsrec_port *port = ovsrec_port_insert(ovs_idl_txn); + ovsrec_port_set_name(port, port_name); + ovsrec_port_set_interfaces(port, &iface, 1); + ovsrec_bridge_update_ports_addvalue(br_int, port); + + free(port_name); + + m->ovs_mirror = ovsrec_mirror_insert(ovs_idl_txn); + ovsrec_mirror_set_name(m->ovs_mirror, m->name); + ovsrec_mirror_set_output_port(m->ovs_mirror, port); + + const struct smap external_ids = + SMAP_CONST1(&external_ids, "ovn-owned", "true"); + ovsrec_mirror_set_external_ids(m->ovs_mirror, &external_ids); + + ovsrec_bridge_update_mirrors_addvalue(br_int, m->ovs_mirror); +} + +static void +sync_ovs_mirror_ports(struct ovn_mirror *m, const struct ovsrec_bridge *br_int) +{ + struct mirror_lport *m_lport; + + if (ovs_list_is_empty(&m->mirror_src_lports)) { + ovsrec_mirror_set_select_src_port(m->ovs_mirror, NULL, 0); + } else { + size_t n_lports = ovs_list_size(&m->mirror_src_lports); + struct ovsrec_port **ovs_ports = xmalloc(sizeof *ovs_ports * n_lports); + + size_t i = 0; + LIST_FOR_EACH (m_lport, list_node, &m->mirror_src_lports) { + const struct ovsrec_port *p = + get_iface_port(m_lport->lbinding->iface, br_int); + ovs_assert(p); + ovs_ports[i++] = (struct ovsrec_port *) p; + } + + ovsrec_mirror_set_select_src_port(m->ovs_mirror, ovs_ports, n_lports); + free(ovs_ports); + } + + if (ovs_list_is_empty(&m->mirror_dst_lports)) { + ovsrec_mirror_set_select_dst_port(m->ovs_mirror, NULL, 0); + } else { + size_t n_lports = ovs_list_size(&m->mirror_dst_lports); + struct ovsrec_port **ovs_ports = xmalloc(sizeof *ovs_ports * n_lports); + + size_t i = 0; + LIST_FOR_EACH (m_lport, list_node, &m->mirror_dst_lports) { + const struct ovsrec_port *p = + get_iface_port(m_lport->lbinding->iface, br_int); + ovs_assert(p); + ovs_ports[i++] = (struct ovsrec_port *) p; + } + + ovsrec_mirror_set_select_dst_port(m->ovs_mirror, ovs_ports, n_lports); + free(ovs_ports); + } +} + +static void +delete_ovs_mirror(struct ovn_mirror *m, const struct ovsrec_bridge *br_int) +{ + ovsrec_bridge_update_ports_delvalue(br_int, m->ovs_mirror->output_port); + ovsrec_bridge_update_mirrors_delvalue(br_int, m->ovs_mirror); + ovsrec_port_delete(m->ovs_mirror->output_port); + ovsrec_mirror_delete(m->ovs_mirror); +} diff --git a/controller/mirror.h b/controller/mirror.h new file mode 100644 index 000000000..a79de109d --- /dev/null +++ b/controller/mirror.h @@ -0,0 +1,33 @@ +/* Copyright (c) 2022 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OVN_MIRROR_H +#define OVN_MIRROR_H 1 + +struct ovsdb_idl_txn; +struct ovsrec_mirror_table; +struct sbrec_mirror_table; +struct ovsrec_bridge; +struct shash; + +void mirror_register_ovs_idl(struct ovsdb_idl *); +void mirror_init(void); +void mirror_destroy(void); +void mirror_run(struct ovsdb_idl_txn *ovs_idl_txn, + const struct ovsrec_mirror_table *, + const struct sbrec_mirror_table *, + const struct ovsrec_bridge *, + struct shash *local_bindings); +#endif diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c index 0752a71ad..0e1d31dbd 100644 --- a/controller/ovn-controller.c +++ b/controller/ovn-controller.c @@ -78,6 +78,7 @@ #include "lib/inc-proc-eng.h" #include "lib/ovn-l7.h" #include "hmapx.h" +#include "mirror.h" VLOG_DEFINE_THIS_MODULE(main); @@ -994,6 +995,7 @@ ctrl_register_ovs_idl(struct ovsdb_idl *ovs_idl) ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_name); ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_interfaces); ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_external_ids); + mirror_register_ovs_idl(ovs_idl); } #define SB_NODES \ @@ -3619,6 +3621,7 @@ main(int argc, char *argv[]) patch_init(); pinctrl_init(); lflow_init(); + mirror_init(); vif_plug_provider_initialize(); /* Connect to OVS OVSDB instance. */ @@ -4195,34 +4198,35 @@ main(int argc, char *argv[]) stopwatch_start(CONTROLLER_LOOP_STOPWATCH_NAME, time_msec()); - if (ovnsb_idl_txn) { - if (ofctrl_has_backlog()) { - /* When there are in-flight messages pending to - * ovs-vswitchd, we should hold on recomputing so - * that the previous flow installations won't be - * delayed. However, we still want to try if - * recompute is not needed and we can quickly - * incrementally process the new changes, to avoid - * unnecessarily forced recomputes later on. This - * is because the OVSDB change tracker cannot - * preserve tracked changes across iterations. If - * change tracking is improved, we can simply skip - * this round of engine_run and continue processing - * acculated changes incrementally later when - * ofctrl_has_backlog() returns false. */ - engine_run(false); - } else { - engine_run(true); - } - } else { - /* Even if there's no SB DB transaction available, + + bool allow_engine_recompute = true; + + if (!ovnsb_idl_txn || ofctrl_has_backlog()) { + /* When there are in-flight messages pending to + * ovs-vswitchd, we should hold on recomputing so + * that the previous flow installations won't be + * delayed. However, we still want to try if + * recompute is not needed and we can quickly + * incrementally process the new changes, to avoid + * unnecessarily forced recomputes later on. This + * is because the OVSDB change tracker cannot + * preserve tracked changes across iterations. If + * change tracking is improved, we can simply skip + * this round of engine_run and continue processing + * acculated changes incrementally later when + * ofctrl_has_backlog() returns false. */ + + /* Even if there's no SB/OVS DB transaction available, * try to run the engine so that we can handle any * incremental changes that don't require a recompute. * If a recompute is required, the engine will abort, * triggerring a full run in the next iteration. */ - engine_run(false); + allow_engine_recompute = false; } + + engine_run(allow_engine_recompute); + stopwatch_stop(CONTROLLER_LOOP_STOPWATCH_NAME, time_msec()); if (engine_has_updated()) { @@ -4312,6 +4316,11 @@ main(int argc, char *argv[]) &runtime_data->local_active_ports_ras); stopwatch_stop(PINCTRL_RUN_STOPWATCH_NAME, time_msec()); + mirror_run(ovs_idl_txn, + ovsrec_mirror_table_get(ovs_idl_loop.idl), + sbrec_mirror_table_get(ovnsb_idl_loop.idl), + br_int, + &runtime_data->lbinding_data.bindings); /* Updating monitor conditions if runtime data or * logical datapath goups changed. */ if (engine_node_changed(&en_runtime_data) @@ -4555,6 +4564,7 @@ loop_done: pinctrl_destroy(); binding_destroy(); patch_destroy(); + mirror_destroy(); if_status_mgr_destroy(if_mgr); shash_destroy(&vif_plug_deleted_iface_ids); shash_destroy(&vif_plug_changed_iface_ids); diff --git a/tests/ovn.at b/tests/ovn.at index 0ef536509..1769f4906 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -16237,6 +16237,520 @@ OVN_CLEANUP([hv1], [hv2]) AT_CLEANUP ]) +OVN_FOR_EACH_NORTHD([ +AT_SETUP([Mirror]) +AT_KEYWORDS([Mirror]) +ovn_start + +# Logical network: +# One LR - R1 has switch ls1 (191.168.1.0/24) connected to it, +# and has switch ls2 (172.16.1.0/24) connected to it. + +ovn-nbctl lr-add R1 + +ovn-nbctl ls-add ls1 +ovn-nbctl ls-add ls2 + +# Connect ls1 to R1 +ovn-nbctl lrp-add R1 ls1 00:00:00:01:02:f1 192.168.1.1/24 +ovn-nbctl lsp-add ls1 rp-ls1 -- set Logical_Switch_Port rp-ls1 \ + type=router options:router-port=ls1 addresses=\"00:00:00:01:02:f1\" + +# Connect ls2 to R1 +ovn-nbctl lrp-add R1 ls2 00:00:00:01:02:f2 172.16.1.1/24 +ovn-nbctl lsp-add ls2 rp-ls2 -- set Logical_Switch_Port rp-ls2 \ + type=router options:router-port=ls2 addresses=\"00:00:00:01:02:f2\" + +# Create logical port ls1-lp1 in ls1 +ovn-nbctl lsp-add ls1 ls1-lp1 \ +-- lsp-set-addresses ls1-lp1 "00:00:00:01:02:03 192.168.1.2" + +# Create logical port ls2-lp1 in ls2 +ovn-nbctl lsp-add ls2 ls2-lp1 \ +-- lsp-set-addresses ls2-lp1 "00:00:00:01:02:04 172.16.1.2" + +ovn-nbctl lsp-add ls1 ln-public +ovn-nbctl lsp-set-type ln-public localnet +ovn-nbctl lsp-set-addresses ln-public unknown +ovn-nbctl lsp-set-options ln-public network_name=public + +# Create one hypervisor and create OVS ports corresponding to logical ports. +net_add n1 + +sim_add hv1 +as hv1 +ovs-vsctl add-br br-phys -- set bridge br-phys other-config:hwaddr=\"00:00:00:01:02:00\" +ovn_attach n1 br-phys 192.168.1.11 + +ovs-vsctl -- add-port br-int vif1 -- \ + set interface 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 vif2 -- \ + set interface vif2 external-ids:iface-id=ls2-lp1 \ + options:tx_pcap=hv1/vif2-tx.pcap \ + options:rxq_pcap=hv1/vif2-rx.pcap \ + ofport-request=1 + +ovs-vsctl set open . external-ids:ovn-bridge-mappings=public:br-phys + +# Allow some time for ovn-northd and ovn-controller to catch up. +wait_for_ports_up +check ovn-nbctl --wait=hv sync +ovn-nbctl dump-flows > sbflows +AT_CAPTURE_FILE([sbflows]) + +for i in 1 2; do + : > vif$i.expected +done + +net_add n2 + +sim_add hv2 +as hv2 +ovs-vsctl add-br br-phys -- set bridge br-phys other-config:hwaddr=\"00:00:00:02:02:00\" +ovn_attach n2 br-phys 192.168.1.12 + +OVN_POPULATE_ARP + +as hv1 + +# test_ipv4_icmp_request INPORT ETH_SRC ETH_DST IPV4_SRC IPV4_DST IP_CHKSUM ICMP_CHKSUM [EXP_IP_CHKSUM EXP_ICMP_CHKSUM] ENCAP_TYPE FILTER +# +# Causes a packet to be received on INPORT. The packet is an ICMPv4 +# request with ETH_SRC, ETH_DST, IPV4_SRC, IPV4_DST, IP_CHSUM and +# ICMP_CHKSUM as specified. If EXP_IP_CHKSUM and EXP_ICMP_CHKSUM are +# provided, then it should be the ip and icmp checksums of the packet +# responded; otherwise, no reply is expected. +# In the absence of an ip checksum calculation helpers, this relies +# on the caller to provide the checksums for the ip and icmp headers. +# XXX This should be more systematic. +# +# INPORT is an lport number, e.g. 11 for vif11. +# ETH_SRC and ETH_DST are each 12 hex digits. +# IPV4_SRC and IPV4_DST are each 8 hex digits. +# IP_CHSUM and ICMP_CHKSUM are each 4 hex digits. +# EXP_IP_CHSUM and EXP_ICMP_CHKSUM are each 4 hex digits. +# ENCAP_TYPE - gre or erspan +# FILTER - Mirror Filter - to-lport / from-lport +test_ipv4_icmp_request() { + local inport=$1 eth_src=$2 eth_dst=$3 ipv4_src=$4 ipv4_dst=$5 ip_chksum=$6 icmp_chksum=$7 + local exp_ip_chksum=$8 exp_icmp_chksum=$9 mirror_encap_type=${10} mirror_filter=${11} + shift; shift; shift; shift; shift; shift; shift + shift; shift; shift; shift; + + # Use ttl to exercise section 4.2.2.9 of RFC1812 + local ip_ttl=02 + local icmp_id=5fbf + local icmp_seq=0001 + local icmp_data=$(seq 1 56 | xargs printf "%02x") + local icmp_type_code_request=0800 + local icmp_payload=${icmp_type_code_request}${icmp_chksum}${icmp_id}${icmp_seq}${icmp_data} + local packet=${eth_dst}${eth_src}08004500005400004000${ip_ttl}01${ip_chksum}${ipv4_src}${ipv4_dst}${icmp_payload} + + as hv1 ovs-appctl netdev-dummy/receive vif$inport $packet + + # Expect to receive the reply, if any. In same port where packet was sent. + # Note: src and dst fields are expected to be reversed. + local icmp_type_code_response=0000 + local reply_icmp_ttl=fe + local reply_icmp_payload=${icmp_type_code_response}${exp_icmp_chksum}${icmp_id}${icmp_seq}${icmp_data} + local reply=${eth_src}${eth_dst}08004500005400004000${reply_icmp_ttl}01${exp_ip_chksum}${ipv4_dst}${ipv4_src}${reply_icmp_payload} + echo $reply >> vif$inport.expected + local remote_mac=000000020200 + local local_mac=000000010200 + if test ${mirror_encap_type} = "gre" ; then + local ipv4_gre=4500007e00004000402fb6e9c0a8010bc0a8010c2000655800000000 + if test ${mirror_filter} = "to-lport" ; then + local mirror=${remote_mac}${local_mac}0800${ipv4_gre}${reply} + elif test ${mirror_filter} = "from-lport" ; then + local mirror=${remote_mac}${local_mac}0800${ipv4_gre}${packet} + fi + elif test ${mirror_encap_type} = "erspan" ; then + local ipv4_erspan=4500008600004000402fb6e1c0a8010bc0a8010c + local erspan_seq0=100088be000000001000000000000000 + local erspan_seq1=100088be000000011000000000000000 + if test ${mirror_filter} = "to-lport" ; then + local mirror=${remote_mac}${local_mac}0800${ipv4_erspan}${erspan_seq0}${reply} + elif test ${mirror_filter} = "from-lport" ; then + local mirror=${remote_mac}${local_mac}0800${ipv4_erspan}${erspan_seq1}${packet} + fi + fi + echo $mirror >> br-phys_n1.expected + +} + +# Set IPs +rtr_l2_ip=$(ip_to_hex 172 16 1 1) +l1_ip=$(ip_to_hex 192 168 1 2) + +check ovn-nbctl mirror-add mirror0 gre 0 to-lport 192.168.1.12 +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror0 + +# Send ping packet and check for mirrored packet of the reply +test_ipv4_icmp_request 1 000000010203 0000000102f1 $l1_ip $rtr_l2_ip 0000 8510 03ff 8d10 "gre" "to-lport" + +OVN_CHECK_PACKETS_CONTAIN([hv1/br-phys_n1-tx.pcap], [br-phys_n1.expected]) +OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected]) + +as hv1 reset_pcap_file vif1 hv1/vif1 +as hv1 reset_pcap_file br-phys_n1 hv1/br-phys_n1 +rm -f br-phys_n1.expected +rm -f vif1.expected + +check ovn-nbctl set mirror . type=erspan + +# Send ping packet and check for mirrored packet of the reply +test_ipv4_icmp_request 1 000000010203 0000000102f1 $l1_ip $rtr_l2_ip 0000 8510 03ff 8d10 "erspan" "to-lport" + +OVN_CHECK_PACKETS_CONTAIN([hv1/br-phys_n1-tx.pcap], [br-phys_n1.expected]) +OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected]) + +as hv1 reset_pcap_file vif1 hv1/vif1 +as hv1 reset_pcap_file br-phys_n1 hv1/br-phys_n1 +rm -f br-phys_n1.expected +rm -f vif1.expected + +check ovn-nbctl set mirror . filter=from-lport + +# Send ping packet and check for mirrored packet of the request +test_ipv4_icmp_request 1 000000010203 0000000102f1 $l1_ip $rtr_l2_ip 0000 8510 03ff 8d10 "erspan" "from-lport" + +OVN_CHECK_PACKETS_CONTAIN([hv1/br-phys_n1-tx.pcap], [br-phys_n1.expected]) +OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected]) + +as hv1 reset_pcap_file vif1 hv1/vif1 +as hv1 reset_pcap_file br-phys_n1 hv1/br-phys_n1 +rm -f br-phys_n1.expected +rm -f vif1.expected + +check ovn-nbctl set mirror . type=gre + +# Send ping packet and check for mirrored packet of the request +test_ipv4_icmp_request 1 000000010203 0000000102f1 $l1_ip $rtr_l2_ip 0000 8510 03ff 8d10 "gre" "from-lport" + +OVN_CHECK_PACKETS_CONTAIN([hv1/br-phys_n1-tx.pcap], [br-phys_n1.expected]) +OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected]) + +echo "---------OVN NB Mirror-----" +ovn-nbctl mirror-list + +echo "---------OVS Mirror----" +ovs-vsctl list Mirror + +echo "-----------------------" + +echo "Verifying Mirror deletion in OVS" +# Set vif1 iface-id such that OVN releases port binding +check ovs-vsctl set interface vif1 external_ids:iface-id=foo +check ovn-nbctl --wait=hv sync + +AT_CHECK([as hv1 ovs-vsctl list Mirror], [0], [dnl +]) + +# Set vif1 iface-id back to ls1-lp1 +check ovs-vsctl set interface vif1 external_ids:iface-id=ls1-lp1 +check ovn-nbctl --wait=hv sync + +OVS_WAIT_UNTIL([test $(as hv1 ovs-vsctl get Mirror mirror0 name) = "mirror0"]) + +# Delete vif1 so that OVN releases port binding +check ovs-vsctl del-port br-int vif1 +check ovn-nbctl --wait=hv sync + +OVS_WAIT_UNTIL([test 0 = $(as hv1 ovs-vsctl list Mirror | wc -l)]) + +OVN_CLEANUP([hv1], [hv2]) +AT_CLEANUP +]) + +OVN_FOR_EACH_NORTHD([ +AT_SETUP([Mirror test bulk updates]) +AT_KEYWORDS([Mirror test bulk updates]) +ovn_start + +# Logical network: +# One LR - R1 has switch ls1 (191.168.1.0/24) connected to it, +# and has switch ls2 (172.16.1.0/24) connected to it. + +ovn-nbctl lr-add R1 + +ovn-nbctl ls-add ls1 +ovn-nbctl ls-add ls2 + +# Connect ls1 to R1 +ovn-nbctl lrp-add R1 ls1 00:00:00:01:02:f1 192.168.1.1/24 +ovn-nbctl lsp-add ls1 rp-ls1 -- set Logical_Switch_Port rp-ls1 \ + type=router options:router-port=ls1 addresses=\"00:00:00:01:02:f1\" + +# Connect ls2 to R1 +ovn-nbctl lrp-add R1 ls2 00:00:00:01:02:f2 172.16.1.1/24 +ovn-nbctl lsp-add ls2 rp-ls2 -- set Logical_Switch_Port rp-ls2 \ + type=router options:router-port=ls2 addresses=\"00:00:00:01:02:f2\" + +# Create logical port ls1-lp1 in ls1 +ovn-nbctl lsp-add ls1 ls1-lp1 \ +-- lsp-set-addresses ls1-lp1 "00:00:00:01:02:03 192.168.1.2" + +# Create logical port ls1-lp2 in ls1 +ovn-nbctl lsp-add ls1 ls1-lp2 \ +-- lsp-set-addresses ls1-lp2 "00:00:00:01:03:03 192.168.1.3" + +# Create logical port ls2-lp1 in ls2 +ovn-nbctl lsp-add ls2 ls2-lp1 \ +-- lsp-set-addresses ls2-lp1 "00:00:00:01:02:04 172.16.1.2" + +# Create logical port ls2-lp2 in ls2 +ovn-nbctl lsp-add ls2 ls2-lp2 \ +-- lsp-set-addresses ls2-lp2 "00:00:00:01:03:04 172.16.1.3" + +ovn-nbctl lsp-add ls1 ln-public +ovn-nbctl lsp-set-type ln-public localnet +ovn-nbctl lsp-set-addresses ln-public unknown +ovn-nbctl lsp-set-options ln-public network_name=public + +# Create 2 hypervisors and create OVS ports corresponding to logical ports for hv1. +net_add n1 + +sim_add hv1 +as hv1 +ovs-vsctl add-br br-phys -- set bridge br-phys other-config:hwaddr=\"00:00:00:01:02:00\" +ovn_attach n1 br-phys 192.168.1.11 + +net_add n2 + +sim_add hv2 +as hv2 +ovs-vsctl add-br br-phys -- set bridge br-phys other-config:hwaddr=\"00:00:00:02:02:00\" +ovn_attach n2 br-phys 192.168.1.12 + +OVN_POPULATE_ARP + +as hv1 + +ovs-vsctl -- add-port br-int vif1 -- \ + set interface vif1 external-ids:iface-id=ls1-lp1 + +ovs-vsctl -- add-port br-int vif2 -- \ + set interface vif2 external-ids:iface-id=ls2-lp1 + +ovs-vsctl -- add-port br-int vif3 -- \ + set interface vif3 external-ids:iface-id=ls1-lp2 + +ovs-vsctl -- add-port br-int vif4 -- \ + set interface vif4 external-ids:iface-id=ls2-lp2 + +ovs-vsctl set open . external-ids:ovn-bridge-mappings=public:br-phys + +# Allow some time for ovn-northd and ovn-controller to catch up. +wait_for_ports_up +check ovn-nbctl --wait=hv sync +ovn-nbctl dump-flows > sbflows +AT_CAPTURE_FILE([sbflows]) + +check ovn-nbctl mirror-add mirror0 gre 0 to-lport 192.168.1.12 +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror0 +check ovn-nbctl --wait=hv sync +origA=$(ovs-vsctl get Mirror mirror0 select_dst_port) +check ovn-nbctl lsp-attach-mirror ls2-lp1 mirror0 + +check ovn-nbctl mirror-add mirror1 gre 1 to-lport 192.168.1.12 +check ovn-nbctl lsp-attach-mirror ls1-lp2 mirror1 +check ovn-nbctl --wait=hv sync +origB=$(ovs-vsctl get Mirror mirror1 select_dst_port) +check ovn-nbctl lsp-attach-mirror ls2-lp2 mirror1 + +check ovn-nbctl --wait=hv sync + +as hv1 +orig1=$(ovs-vsctl get Mirror mirror0 select_dst_port) +orig2=$(ovs-vsctl get Mirror mirror1 select_dst_port) + +check ovn-nbctl mirror-del +check ovn-nbctl --wait=hv sync + +check ovn-nbctl mirror-add mirror0 gre 0 to-lport 192.168.1.12 +check ovn-nbctl mirror-add mirror1 gre 1 to-lport 192.168.1.12 + +# Attaches multiple mirrors + +check as hv1 ovn-appctl -t ovn-controller debug/pause +check ovn-nbctl lsp-attach-mirror ls2-lp1 mirror0 +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror0 +check ovn-nbctl lsp-attach-mirror ls2-lp2 mirror1 +check ovn-nbctl lsp-attach-mirror ls1-lp2 mirror1 +check as hv1 ovn-appctl -t ovn-controller debug/resume + +check ovn-nbctl --wait=hv sync + +as hv1 +new1=$(ovs-vsctl get Mirror mirror0 select_dst_port) +new2=$(ovs-vsctl get Mirror mirror1 select_dst_port) + +AT_CHECK([test "$orig1" = "$new1"], [0], []) + +AT_CHECK([test "$orig2" = "$new2"], [0], []) + +# Equal detaches and attaches +check as hv1 ovn-appctl -t ovn-controller debug/pause +check ovn-nbctl lsp-detach-mirror ls1-lp1 mirror0 +check ovn-nbctl lsp-detach-mirror ls2-lp1 mirror0 +check ovn-nbctl lsp-attach-mirror ls1-lp2 mirror0 +check ovn-nbctl lsp-attach-mirror ls2-lp2 mirror0 + +check ovn-nbctl lsp-detach-mirror ls1-lp2 mirror1 +check ovn-nbctl lsp-detach-mirror ls2-lp2 mirror1 +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror1 +check ovn-nbctl lsp-attach-mirror ls2-lp1 mirror1 +check as hv1 ovn-appctl -t ovn-controller debug/resume + +check ovn-nbctl --wait=hv sync + +# Make sure that ovn-controller has not asserted. +AT_CHECK([kill -0 $(cat hv1/ovn-controller.pid)]) + +as hv1 +new1=$(ovs-vsctl get Mirror mirror0 select_dst_port) +new2=$(ovs-vsctl get Mirror mirror1 select_dst_port) + +AT_CHECK([test "$orig1" = "$new2"], [0], []) + +AT_CHECK([test "$orig2" = "$new1"], [0], []) + +# Detaches more than attaches +check as hv1 ovn-appctl -t ovn-controller debug/pause +check ovn-nbctl lsp-detach-mirror ls1-lp1 mirror1 +check ovn-nbctl lsp-detach-mirror ls2-lp1 mirror1 +check ovn-nbctl lsp-detach-mirror ls1-lp2 mirror0 +check ovn-nbctl lsp-detach-mirror ls2-lp2 mirror0 +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror0 +check ovn-nbctl lsp-attach-mirror ls1-lp2 mirror1 +check as hv1 ovn-appctl -t ovn-controller debug/resume + +check ovn-nbctl --wait=hv sync +# Make sure that ovn-controller has not asserted. +AT_CHECK([kill -0 $(cat hv1/ovn-controller.pid)]) + +as hv1 +new1=$(ovs-vsctl get Mirror mirror0 select_dst_port) +new2=$(ovs-vsctl get Mirror mirror1 select_dst_port) + +AT_CHECK([test "$origA" = "$new1"], [0], []) + +AT_CHECK([test "$origB" = "$new2"], [0], []) + +# Attaches more than detaches +check as hv1 ovn-appctl -t ovn-controller debug/pause +check ovn-nbctl lsp-detach-mirror ls1-lp1 mirror0 +check ovn-nbctl lsp-detach-mirror ls1-lp2 mirror1 +check ovn-nbctl lsp-attach-mirror ls1-lp2 mirror0 +check ovn-nbctl lsp-attach-mirror ls2-lp2 mirror0 +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror1 +check ovn-nbctl lsp-attach-mirror ls2-lp1 mirror1 +check as hv1 ovn-appctl -t ovn-controller debug/resume + +check ovn-nbctl --wait=hv sync +# Make sure that ovn-controller has not asserted. +AT_CHECK([kill -0 $(cat hv1/ovn-controller.pid)]) + +as hv1 +new1=$(ovs-vsctl get Mirror mirror0 select_dst_port) +new2=$(ovs-vsctl get Mirror mirror1 select_dst_port) + +AT_CHECK([test "$orig1" = "$new2"], [0], []) + +AT_CHECK([test "$orig2" = "$new1"], [0], []) + +# Detaches all +check as hv1 ovn-appctl -t ovn-controller debug/pause +check ovn-nbctl lsp-detach-mirror ls1-lp2 mirror0 +check ovn-nbctl lsp-detach-mirror ls2-lp2 mirror0 +check ovn-nbctl lsp-detach-mirror ls1-lp1 mirror1 +check ovn-nbctl lsp-detach-mirror ls2-lp1 mirror1 +check as hv1 ovn-appctl -t ovn-controller debug/resume +check ovn-nbctl --wait=hv sync + +OVS_WAIT_UNTIL([test 0 = $(as hv1 ovs-vsctl list Mirror | wc -l)]) + +check ovn-nbctl mirror-add mirror2 gre 2 to-lport 192.168.1.12 +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror2 +check ovn-nbctl --wait=hv sync + +# Attaches SAME port to multiple mirrors +# and detaches it from existing mirror. +# More attach than detach + +check as hv1 ovn-appctl -t ovn-controller debug/pause +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror0 +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror1 +check ovn-nbctl lsp-detach-mirror ls1-lp1 mirror2 +check as hv1 ovn-appctl -t ovn-controller debug/resume + +check ovn-nbctl --wait=hv sync + +as hv1 +new1=$(ovs-vsctl get Mirror mirror0 select_dst_port) +new2=$(ovs-vsctl get Mirror mirror1 select_dst_port) + +AT_CHECK([test "$origA" = "$new1"], [0], []) +AT_CHECK([test "$origA" = "$new2"], [0], []) +OVS_WAIT_UNTIL([test 0 = $(as hv1 ovs-vsctl list Mirror mirror2 | wc -l)]) + +check ovn-nbctl mirror-add mirror3 gre 3 to-lport 192.168.1.12 +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror3 +check ovn-nbctl --wait=hv sync + +# Detaches SAME port from multiple mirrors +# and attaches it to existing mirror. +# More detach than attach + +check as hv1 ovn-appctl -t ovn-controller debug/pause +check ovn-nbctl lsp-detach-mirror ls1-lp1 mirror0 +check ovn-nbctl lsp-detach-mirror ls1-lp1 mirror3 +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror2 +check as hv1 ovn-appctl -t ovn-controller debug/resume + +check ovn-nbctl --wait=hv sync + +as hv1 +new1=$(ovs-vsctl get Mirror mirror1 select_dst_port) +new2=$(ovs-vsctl get Mirror mirror2 select_dst_port) + +AT_CHECK([test "$origA" = "$new1"], [0], []) +AT_CHECK([test "$origA" = "$new2"], [0], []) +OVS_WAIT_UNTIL([test 0 = $(as hv1 ovs-vsctl list Mirror mirror0 | wc -l)]) +OVS_WAIT_UNTIL([test 0 = $(as hv1 ovs-vsctl list Mirror mirror3 | wc -l)]) + +check ovn-nbctl mirror-del +check ovn-nbctl --wait=hv sync +check ovn-nbctl mirror-add mirror0 erspan 0 to-lport 192.168.1.12 +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror0 +check ovn-nbctl --wait=hv sync + +# Make sure different fields of mirror resource set from OVN +# propagates to OVS correctly + +check as hv1 ovn-appctl -t ovn-controller debug/pause +check ovn-nbctl set mirror . filter=from-lport +check ovn-nbctl set mirror . type=gre +check ovn-nbctl set mirror . index=123 +check as hv1 ovn-appctl -t ovn-controller debug/resume + +check ovn-nbctl --wait=hv sync + +as hv1 +new1=$(ovs-vsctl get Mirror mirror0 select_src_port) +AT_CHECK([test "$origA" = "$new1"], [0], []) +type=$(ovs-vsctl get Interface ovn-mirror0 type) +OVS_WAIT_UNTIL([test "gre" = "$type"]) +index=$(ovs-vsctl get Interface ovn-mirror0 options:key | grep 123 | wc -l) +OVS_WAIT_UNTIL([test "1" = "$index"]) + +OVN_CLEANUP([hv1],[hv2]) +AT_CLEANUP +]) OVN_FOR_EACH_NORTHD([ AT_SETUP([Port Groups])