From patchwork Fri Nov 25 20:02:17 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Abhiram RN X-Patchwork-Id: 1709161 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=2605:bc80:3010::133; helo=smtp2.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=XsFKDg7h; dkim-atps=neutral Received: from smtp2.osuosl.org (smtp2.osuosl.org [IPv6:2605:bc80:3010::133]) (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 4NJm4V71F6z23nd for ; Sat, 26 Nov 2022 07:02:49 +1100 (AEDT) Received: from localhost (localhost [127.0.0.1]) by smtp2.osuosl.org (Postfix) with ESMTP id 4580D414F7; Fri, 25 Nov 2022 20:02:46 +0000 (UTC) DKIM-Filter: OpenDKIM Filter v2.11.0 smtp2.osuosl.org 4580D414F7 Authentication-Results: smtp2.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=XsFKDg7h 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 FJ5gL8z_cmJP; Fri, 25 Nov 2022 20:02:44 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by smtp2.osuosl.org (Postfix) with ESMTPS id 69C5F400F2; Fri, 25 Nov 2022 20:02:43 +0000 (UTC) DKIM-Filter: OpenDKIM Filter v2.11.0 smtp2.osuosl.org 69C5F400F2 Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 1D2C8C0033; Fri, 25 Nov 2022 20:02:43 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp3.osuosl.org (smtp3.osuosl.org [IPv6:2605:bc80:3010::136]) by lists.linuxfoundation.org (Postfix) with ESMTP id 789FCC002D for ; Fri, 25 Nov 2022 20:02:41 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp3.osuosl.org (Postfix) with ESMTP id 58399611F3 for ; Fri, 25 Nov 2022 20:02:41 +0000 (UTC) DKIM-Filter: OpenDKIM Filter v2.11.0 smtp3.osuosl.org 58399611F3 Authentication-Results: smtp3.osuosl.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20210112 header.b=XsFKDg7h 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 Wdy-CcgtFxa8 for ; Fri, 25 Nov 2022 20:02:39 +0000 (UTC) X-Greylist: whitelisted by SQLgrey-1.8.0 DKIM-Filter: OpenDKIM Filter v2.11.0 smtp3.osuosl.org 0DC7D611E4 Received: from mail-pf1-x429.google.com (mail-pf1-x429.google.com [IPv6:2607:f8b0:4864:20::429]) by smtp3.osuosl.org (Postfix) with ESMTPS id 0DC7D611E4 for ; Fri, 25 Nov 2022 20:02:38 +0000 (UTC) Received: by mail-pf1-x429.google.com with SMTP id b4so4974722pfb.9 for ; Fri, 25 Nov 2022 12:02:38 -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=QpVWL6Sp3JNC0+0DkkwJNC40QKil6nRkNb4oWOBa9AE=; b=XsFKDg7h4W1MRvVLV71KoEm4elOQ6nJAYTFkHI8VGvPr8j630lxPPgl/iaHYR/STr1 JWvWndANfnztaYelh6KTJrwUr+GSiPhF3wq5tect1/ZyFTJ6+HZPS2gNQw2AfPJDKQjo cDu5IkOaazh0H57ptrEy/AWJnY2QsWRVxzuhSO6HRwza7iM14qN03Sg0fzbNYHRDa7bO ATXoGvFIvga4bQMFAQv1AEtk50DbyOE1wJivDGopojjSGdzikHP2bA1Hwxh5m8voaooQ OMrWB0BlA44UT1WAk94ciaLiL4IupclGMScgDKVKEvzehhVW7m4ad8qwQLR7SlkvXBRP qYRg== 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=QpVWL6Sp3JNC0+0DkkwJNC40QKil6nRkNb4oWOBa9AE=; b=PwUu+ZUELE+mxKc6eJom5GVKzaxa9ClvX8b+vvuTnTKHpGDoayFaLWw09b/B6ZFRwA ZxHp9ttFcb4u7+f4HMJAuUcUnwn4ASM/sMBgZOjgok2yIE8yVPpvWduPLiejpPNCQvuT uTxt2cIkiVU/9NVRYvOabk46umcIIZzUuKwrZyRXACeviuH4wdFwHLnWJ0r4aOeZeJjN NBjE87Ys5SAHzQSUo/61G28yIGDx382GAkUxt94lONpWMo3aNK+OzNsxHKMIeSgnQpp9 EuYFYiJOxDl8KlPdv4o2C8X/yi2hSnrcO6oH6fZ6Uo/lDHreRf3hpv0ewBZkEY6nPTIz 3A4w== X-Gm-Message-State: ANoB5pno/3Xf+xSduJTzWx4fFvwumKJ3skkFnY6P27usY7ci5ofx9YSt 6P5ngY0elrS9BbRpUPe6gQh1NUjCUFY= X-Google-Smtp-Source: AA0mqf7OMPov81Z1JAYDygQlReknNxYkzinWH4htWZBCKxX5JD1b6uPJ8ouWEEwCTN6mpXXbg7MbMw== X-Received: by 2002:a05:6a00:4104:b0:56c:e37d:ec12 with SMTP id bu4-20020a056a00410400b0056ce37dec12mr20025775pfb.80.1669406557832; Fri, 25 Nov 2022 12:02:37 -0800 (PST) Received: from arn.com ([49.205.133.150]) by smtp.googlemail.com with ESMTPSA id x80-20020a633153000000b004308422060csm2934375pgx.69.2022.11.25.12.02.34 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 25 Nov 2022 12:02:37 -0800 (PST) From: Abhiram R N To: dev@openvswitch.org Date: Sat, 26 Nov 2022 01:32:17 +0530 Message-Id: <20221125200219.1312140-1-abhiramrn@gmail.com> X-Mailer: git-send-email 2.31.1 In-Reply-To: References: MIME-Version: 1.0 Cc: vbarrenk@redhat.com, vedabarrenkala@gmail.com, arn@redhat.com Subject: [ovs-dev] [OVN v16 1/3] OVN Remote Port Mirroring: Add new Schemas in NB and SB 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" In order to support Remote Port Mirroring added the required schemas in NB and SB. Also, nbctl.c and sbctl.c changes are added. Futher added test case to test nbctl commands. Co-authored-by: Veda Barrenkala Signed-off-by: Veda Barrenkala Signed-off-by: Abhiram R N --- ovn-nb.ovsschema | 31 +++- ovn-nb.xml | 63 ++++++++ ovn-sb.ovsschema | 26 ++- ovn-sb.xml | 50 ++++++ tests/ovn-nbctl.at | 120 ++++++++++++++ utilities/ovn-nbctl.c | 357 ++++++++++++++++++++++++++++++++++++++++++ utilities/ovn-sbctl.c | 4 + 7 files changed, 647 insertions(+), 4 deletions(-) diff --git a/ovn-nb.ovsschema b/ovn-nb.ovsschema index 174364c8b..01de55222 100644 --- a/ovn-nb.ovsschema +++ b/ovn-nb.ovsschema @@ -1,7 +1,7 @@ { "name": "OVN_Northbound", - "version": "6.3.0", - "cksum": "4042813038 31869", + "version": "6.4.0", + "cksum": "589874483 33352", "tables": { "NB_Global": { "columns": { @@ -132,6 +132,11 @@ "refType": "weak"}, "min": 0, "max": 1}}, + "mirror_rules": {"type": {"key": {"type": "uuid", + "refTable": "Mirror", + "refType": "weak"}, + "min": 0, + "max": "unlimited"}}, "ha_chassis_group": { "type": {"key": {"type": "uuid", "refTable": "HA_Chassis_Group", @@ -301,6 +306,28 @@ "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}}, "isRoot": false}, + "Mirror": { + "columns": { + "name": {"type": "string"}, + "filter": {"type": {"key": {"type": "string", + "enum": ["set", ["from-lport", + "to-lport", + "both"]]}}}, + "sink":{"type": "string"}, + "type": {"type": {"key": {"type": "string", + "enum": ["set", ["gre", + "erspan"]]}}}, + "index": {"type": "integer"}, + "src": {"type": {"key": {"type": "uuid", + "refTable": "Logical_Switch_Port", + "refType": "weak"}, + "min": 0, + "max": "unlimited"}}, + "external_ids": { + "type": {"key": "string", "value": "string", + "min": 0, "max": "unlimited"}}}, + "indexes": [["name"]], + "isRoot": true}, "Meter": { "columns": { "name": {"type": "string"}, diff --git a/ovn-nb.xml b/ovn-nb.xml index 7f207a413..671692b49 100644 --- a/ovn-nb.xml +++ b/ovn-nb.xml @@ -1582,6 +1582,11 @@ + + Mirror rules that apply to logical switch port which is the source. + Please see the table. + + References a row in the OVN Northbound database's table. @@ -2527,6 +2532,64 @@ + +

+ Each row in this table represents one Mirror that can be used for + port mirroring. These Mirrors are referenced by the + column in + the table. +

+ + +

+ Represents the name of the mirror. +

+
+ + +

+ The value of this field represents selection criteria of the mirror. + Supported values for filter to-lport / from-lport / both + to-lport - to mirror packets coming into logical port + from-lport - to mirror packets going out of logical port + both - to mirror packets coming into and going out of logical port. +

+
+ + +

+ The value of this field represents the destination/sink of the mirror. + The value it takes is an IP address of the sink port. +

+
+ + +

+ The value of this field represents the type of the tunnel used for + sending the mirrored packets. Supported Tunnel types gre and erspan +

+
+ + +

+ The value of this field represents the tunnel ID. Depending on the + tunnel type configured, GRE key value if type GRE and erspan_idx value + if ERSPAN +

+
+ + +

+ The value of this field represents a list of source ports for the + mirror. Please see the table. +

+
+ + + See External IDs at the beginning of this document. + +
+

Each row in this table represents a meter that can be used for QoS or diff --git a/ovn-sb.ovsschema b/ovn-sb.ovsschema index 576ebbdeb..b83134416 100644 --- a/ovn-sb.ovsschema +++ b/ovn-sb.ovsschema @@ -1,7 +1,7 @@ { "name": "OVN_Southbound", - "version": "20.25.0", - "cksum": "53184112 28845", + "version": "20.26.0", + "cksum": "2344151793 30004", "tables": { "SB_Global": { "columns": { @@ -142,6 +142,23 @@ "indexes": [["datapath", "tunnel_key"], ["datapath", "name"]], "isRoot": true}, + "Mirror": { + "columns": { + "name": {"type": "string"}, + "filter": {"type": {"key": {"type": "string", + "enum": ["set", + ["from-lport", + "to-lport","both"]]}}}, + "sink":{"type": "string"}, + "type": {"type": {"key": {"type": "string", + "enum": ["set", + ["gre", "erspan"]]}}}, + "index": {"type": "integer"}, + "external_ids": { + "type": {"key": "string", "value": "string", + "min": 0, "max": "unlimited"}}}, + "indexes": [["name"]], + "isRoot": true}, "Meter": { "columns": { "name": {"type": "string"}, @@ -230,6 +247,11 @@ "refTable": "Encap", "refType": "weak"}, "min": 0, "max": "unlimited"}}, + "mirror_rules": {"type": {"key": {"type": "uuid", + "refTable": "Mirror", + "refType": "weak"}, + "min": 0, + "max": "unlimited"}}, "mac": {"type": {"key": "string", "min": 0, "max": "unlimited"}}, diff --git a/ovn-sb.xml b/ovn-sb.xml index 16f57b374..2dd2e304f 100644 --- a/ovn-sb.xml +++ b/ovn-sb.xml @@ -2869,6 +2869,51 @@ tcp.flags = RST;

+ +

+ Each row in this table represents one Mirror that can be used for + port mirroring. These Mirrors are referenced by the + column in + the table. +

+ + +

+ Represents the name of the mirror. +

+
+ + +

+ The value of this field represents selection criteria of the mirror. +

+
+ + +

+ The value of this field represents the destination/sink of the mirror. +

+
+ + +

+ The value of this field represents the type of the tunnel used for + sending the mirrored packets +

+
+ + +

+ The value of this field represents the key/idx depending on the + tunnel type configured +

+
+ + + See External IDs at the beginning of this document. + +
+

Each row in this table represents a meter that can be used for QoS or @@ -3371,6 +3416,11 @@ tcp.flags = RST; + + Mirror rules that apply to the port binding. + Please see the table. + +

These options apply to logical ports with of diff --git a/tests/ovn-nbctl.at b/tests/ovn-nbctl.at index 4d480e357..d79f9d929 100644 --- a/tests/ovn-nbctl.at +++ b/tests/ovn-nbctl.at @@ -435,6 +435,126 @@ AT_CHECK([ovn-nbctl meter-list], [0], [dnl dnl --------------------------------------------------------------------- +OVN_NBCTL_TEST([ovn_nbctl_mirrors], [mirrors], [ +AT_CHECK([ovn-nbctl mirror-add mirror1 gre 0 from-lport 10.10.10.1]) +AT_CHECK([ovn-nbctl mirror-add mirror2 erspan 1 both 10.10.10.2]) +AT_CHECK([ovn-nbctl mirror-add mirror3 gre 2 to-lport 10.10.10.3]) +AT_CHECK([ovn-nbctl ls-add sw0]) +AT_CHECK([ovn-nbctl lsp-add sw0 sw0-port1]) +AT_CHECK([ovn-nbctl lsp-add sw0 sw0-port2]) +AT_CHECK([ovn-nbctl lsp-add sw0 sw0-port3]) + +dnl Add duplicate mirror name +AT_CHECK([ovn-nbctl mirror-add mirror1 gre 0 from-lport 10.10.10.5], [1], [], [stderr]) +AT_CHECK([grep 'already exists' stderr], [0], [ignore]) + +dnl Attach invalid source port to mirror +AT_CHECK([ovn-nbctl lsp-attach-mirror sw0-port4 mirror3], [1], [], [stderr]) +AT_CHECK([grep 'port name not found' stderr], [0], [ignore]) + +dnl Attach source port to invalid mirror +AT_CHECK([ovn-nbctl lsp-attach-mirror sw0-port3 mirror4], [1], [], [stderr]) +AT_CHECK([grep 'mirror name not found' stderr], [0], [ignore]) + +dnl Attach source port to mirror +AT_CHECK([ovn-nbctl lsp-attach-mirror sw0-port1 mirror3]) + +dnl Attach one more source port to mirror +AT_CHECK([ovn-nbctl lsp-attach-mirror sw0-port3 mirror3]) + +dnl Verify if multiple ports are attached to the same mirror properly +AT_CHECK([ovn-nbctl mirror-list], [0], [dnl +mirror1: + Type : gre + Sink : 10.10.10.1 + Filter : from-lport + Index/Key: 0 + Sources : None attached +mirror2: + Type : erspan + Sink : 10.10.10.2 + Filter : both + Index/Key: 1 + Sources : None attached +mirror3: + Type : gre + Sink : 10.10.10.3 + Filter : to-lport + Index/Key: 2 + Sources : sw0-port1 sw0-port3 +]) + +dnl Detach one source port from mirror +AT_CHECK([ovn-nbctl lsp-detach-mirror sw0-port3 mirror3]) + +dnl Verify if detach source port from mirror happens properly +AT_CHECK([ovn-nbctl mirror-list], [0], [dnl +mirror1: + Type : gre + Sink : 10.10.10.1 + Filter : from-lport + Index/Key: 0 + Sources : None attached +mirror2: + Type : erspan + Sink : 10.10.10.2 + Filter : both + Index/Key: 1 + Sources : None attached +mirror3: + Type : gre + Sink : 10.10.10.3 + Filter : to-lport + Index/Key: 2 + Sources : sw0-port1 +]) + +dnl Delete a single mirror which has source attached. +AT_CHECK([ovn-nbctl mirror-del mirror3]) + +dnl Check if the detach happened from source properly +AT_CHECK([ovn-nbctl get Logical_Switch_Port sw0-port1 mirror_rules | cut -b 3], [0], [dnl + +]) + +dnl Check if the mirror deleted properly +AT_CHECK([ovn-nbctl mirror-list], [0], [dnl +mirror1: + Type : gre + Sink : 10.10.10.1 + Filter : from-lport + Index/Key: 0 + Sources : None attached +mirror2: + Type : erspan + Sink : 10.10.10.2 + Filter : both + Index/Key: 1 + Sources : None attached +]) + +dnl Delete another mirror +AT_CHECK([ovn-nbctl mirror-del mirror2]) + +dnl Update the Sink address +AT_CHECK([ovn-nbctl set mirror . sink=192.168.1.13]) + +AT_CHECK([ovn-nbctl mirror-list], [0], [dnl +mirror1: + Type : gre + Sink : 192.168.1.13 + Filter : from-lport + Index/Key: 0 + Sources : None attached +]) + +dnl Delete all mirrors +AT_CHECK([ovn-nbctl mirror-del]) +AT_CHECK([ovn-nbctl mirror-list], [0], [dnl +])]) + +dnl --------------------------------------------------------------------- + OVN_NBCTL_TEST([ovn_nbctl_nats], [NATs], [ AT_CHECK([ovn-nbctl lr-add lr0]) AT_CHECK([ovn-nbctl lr-nat-add lr0 snatt 30.0.0.2 192.168.1.2], [1], [], diff --git a/utilities/ovn-nbctl.c b/utilities/ovn-nbctl.c index 811468dc6..af2e61435 100644 --- a/utilities/ovn-nbctl.c +++ b/utilities/ovn-nbctl.c @@ -271,6 +271,19 @@ QoS commands:\n\ remove QoS rules from SWITCH\n\ qos-list SWITCH print QoS rules for SWITCH\n\ \n\ +Mirror commands:\n\ + mirror-add NAME TYPE INDEX FILTER IP\n\ + add a mirror with given name\n\ + specify TYPE 'gre' or 'erspan'\n\ + specify the tunnel INDEX value\n\ + (indicates key if GRE\n\ + erpsan_idx if ERSPAN)\n\ + specify FILTER for mirroring selection\n\ + 'to-lport' / 'from-lport' / 'both'\n\ + specify Sink / Destination i.e. Remote IP\n\ + mirror-del [NAME] remove mirrors\n\ + mirror-list print mirrors\n\ +\n\ Meter commands:\n\ [--fair]\n\ meter-add NAME ACTION RATE UNIT [BURST]\n\ @@ -311,6 +324,8 @@ Logical switch port commands:\n\ set dhcpv6 options for PORT\n\ lsp-get-dhcpv6-options PORT get the dhcpv6 options for PORT\n\ lsp-get-ls PORT get the logical switch which the port belongs to\n\ + lsp-attach-mirror PORT MIRROR attach source PORT to MIRROR\n\ + lsp-detach-mirror PORT MIRROR detach source PORT from MIRROR\n\ \n\ Forwarding group commands:\n\ [--liveness]\n\ @@ -1685,6 +1700,130 @@ nbctl_pre_lsp_type(struct ctl_context *ctx) ovsdb_idl_add_column(ctx->idl, &nbrec_logical_switch_port_col_type); } +static void +nbctl_pre_lsp_mirror(struct ctl_context *ctx) +{ + ovsdb_idl_add_column(ctx->idl, &nbrec_logical_switch_port_col_name); + ovsdb_idl_add_column(ctx->idl, + &nbrec_logical_switch_port_col_mirror_rules); + ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_name); + ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_src); +} + +static int +mirror_cmp(const void *mirror1_, const void *mirror2_) +{ + const struct nbrec_mirror *const *mirror_1 = mirror1_; + const struct nbrec_mirror *const *mirror_2 = mirror2_; + + const struct nbrec_mirror *mirror1 = *mirror_1; + const struct nbrec_mirror *mirror2 = *mirror_2; + + return strcmp(mirror1->name,mirror2->name); +} + +static char * OVS_WARN_UNUSED_RESULT +mirror_by_name_or_uuid(struct ctl_context *ctx, const char *id, + bool must_exist, + const struct nbrec_mirror **mirror_p) +{ + const struct nbrec_mirror *mirror = NULL; + *mirror_p = NULL; + + struct uuid mirror_uuid; + bool is_uuid = uuid_from_string(&mirror_uuid, id); + if (is_uuid) { + mirror = nbrec_mirror_get_for_uuid(ctx->idl, &mirror_uuid); + } + + if (!mirror) { + NBREC_MIRROR_FOR_EACH (mirror, ctx->idl) { + if (!strcmp(mirror->name, id)) { + break; + } + } + } + + if (!mirror && must_exist) { + return xasprintf("%s: mirror %s not found", + id, is_uuid ? "UUID" : "name"); + } + + *mirror_p = mirror; + return NULL; +} + +static void +nbctl_lsp_attach_mirror(struct ctl_context *ctx) +{ + const char *port = ctx->argv[1]; + const char *mirror_name = ctx->argv[2]; + const struct nbrec_logical_switch_port *lsp = NULL; + const struct nbrec_mirror *mirror; + + char *error; + + error = lsp_by_name_or_uuid(ctx, port, true, &lsp); + if (error) { + ctx->error = error; + return; + } + + + /*check if a mirror rule actually exists on that name or not*/ + error = mirror_by_name_or_uuid(ctx, mirror_name, true, &mirror); + if (error) { + ctx->error = error; + return; + } + + /* Check if same mirror rule already exists for the lsp */ + for (size_t i = 0; i < lsp->n_mirror_rules; i++) { + if (!mirror_cmp(&lsp->mirror_rules[i], &mirror)) { + bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL; + if (!may_exist) { + ctl_error(ctx, "Same mirror already existed on the lsp %s.", + ctx->argv[1]); + return; + } + return; + } + } + + nbrec_logical_switch_port_update_mirror_rules_addvalue(lsp, mirror); + nbrec_mirror_update_src_addvalue(mirror,lsp); + +} + +static void +nbctl_lsp_detach_mirror(struct ctl_context *ctx) +{ + const char *port = ctx->argv[1]; + const char *mirror_name = ctx->argv[2]; + const struct nbrec_logical_switch_port *lsp = NULL; + const struct nbrec_mirror *mirror; + + char *error; + + error = lsp_by_name_or_uuid(ctx, port, true, &lsp); + if (error) { + ctx->error = error; + return; + } + + + /*check if a mirror rule actually exists on that name or not*/ + error = mirror_by_name_or_uuid(ctx, mirror_name, true, &mirror); + if (error) { + ctx->error = error; + return; + } + + nbrec_logical_switch_port_update_mirror_rules_delvalue(lsp, mirror); + nbrec_mirror_update_src_delvalue(mirror,lsp); + +} + static void nbctl_lsp_set_type(struct ctl_context *ctx) { @@ -7241,6 +7380,211 @@ cmd_ha_ch_grp_set_chassis_prio(struct ctl_context *ctx) nbrec_ha_chassis_set_priority(ha_chassis, priority); } +static char * OVS_WARN_UNUSED_RESULT +parse_filter(const char *arg, const char **selection_p) +{ + /* Validate selection. Only require the first letter. */ + if (arg[0] == 't') { + *selection_p = "to-lport"; + } else if (arg[0] == 'f') { + *selection_p = "from-lport"; + } else if (arg[0] == 'b') { + *selection_p = "both"; + } else { + *selection_p = NULL; + return xasprintf("%s: selection must be \"to-lport\" or " + "\"from-lport\" or \"both\" ", arg); + } + return NULL; +} + +static char * OVS_WARN_UNUSED_RESULT +parse_type(const char *arg, const char **type_p) +{ + /* Validate type. Only require the first letter. */ + if (arg[0] == 'g') { + *type_p = "gre"; + } else if (arg[0] == 'e') { + *type_p = "erspan"; + } else { + *type_p = NULL; + return xasprintf("%s: type must be \"gre\" or " + "\"erspan\"", arg); + } + return NULL; +} + +static void +nbctl_pre_mirror_add(struct ctl_context *ctx) +{ + ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_name); + ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_filter); + ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_index); + ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_sink); + ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_type); +} + +static void +nbctl_mirror_add(struct ctl_context *ctx) +{ + const char *filter = NULL; + const char *sink_ip = NULL; + const char *type = NULL; + const char *name = NULL; + char *new_sink_ip = NULL; + int64_t index; + char *error = NULL; + const struct nbrec_mirror *mirror_check = NULL; + + /* Mirror Name */ + name = ctx->argv[1]; + NBREC_MIRROR_FOR_EACH (mirror_check, ctx->idl) { + if (!strcmp(mirror_check->name, name)) { + ctl_error(ctx, "Mirror with %s name already exists.", + name); + return; + } + } + + /* Tunnel Type - GRE/ERSPAN */ + error = parse_type(ctx->argv[2], &type); + if (error) { + ctx->error = error; + return; + } + + /* tunnel index / GRE key / ERSPAN idx */ + index = atoi(ctx->argv[3]); + + /* Filter for mirroring */ + error = parse_filter(ctx->argv[4], &filter); + if (error) { + ctx->error = error; + return; + } + + /* Destination / Sink details */ + sink_ip = ctx->argv[5]; + + /* check if it is a valid ip */ + new_sink_ip = normalize_ipv4_addr_str(sink_ip); + if (!new_sink_ip) { + new_sink_ip = normalize_ipv6_addr_str(sink_ip); + } + + if (new_sink_ip) { + free(new_sink_ip); + } else { + ctl_error(ctx, "Invalid sink ip: %s", sink_ip); + return; + } + + /* Create the mirror. */ + struct nbrec_mirror *mirror = nbrec_mirror_insert(ctx->txn); + nbrec_mirror_set_name(mirror, name); + nbrec_mirror_set_index(mirror, index); + nbrec_mirror_set_filter(mirror, filter); + nbrec_mirror_set_type(mirror, type); + nbrec_mirror_set_sink(mirror, sink_ip); + +} + +static void +nbctl_pre_mirror_del(struct ctl_context *ctx) +{ + ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_name); + ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_src); +} + +static void +nbctl_mirror_del(struct ctl_context *ctx) +{ + const struct nbrec_mirror *mirror, *next; + + /* If a name is not specified, delete all mirrors. */ + if (ctx->argc == 1) { + NBREC_MIRROR_FOR_EACH_SAFE (mirror, next, ctx->idl) { + nbrec_mirror_delete(mirror); + } + return; + } + + /* Remove the matching mirror. */ + NBREC_MIRROR_FOR_EACH (mirror, ctx->idl) { + if (strcmp(ctx->argv[1], mirror->name)) { + continue; + } + nbrec_mirror_delete(mirror); + return; + } +} + +static void +nbctl_pre_mirror_list(struct ctl_context *ctx) +{ + ovsdb_idl_add_column(ctx->idl, &nbrec_logical_switch_port_col_name); + ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_name); + ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_filter); + ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_index); + ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_sink); + ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_type); + ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_src); +} + +static void +nbctl_mirror_list(struct ctl_context *ctx) +{ + + const struct nbrec_mirror **mirrors = NULL; + const struct nbrec_mirror *mirror; + size_t n_capacity = 0; + size_t n_mirrors = 0; + + NBREC_MIRROR_FOR_EACH (mirror, ctx->idl) { + if (n_mirrors == n_capacity) { + mirrors = x2nrealloc(mirrors, &n_capacity, sizeof *mirrors); + } + + mirrors[n_mirrors] = mirror; + n_mirrors++; + } + + if (n_mirrors) { + qsort(mirrors, n_mirrors, sizeof *mirrors, mirror_cmp); + } + + for (size_t i = 0; i < n_mirrors; i++) { + mirror = mirrors[i]; + ds_put_format(&ctx->output, "%s:\n", mirror->name); + /* print all the values */ + ds_put_format(&ctx->output, " Type : %s\n", mirror->type); + ds_put_format(&ctx->output, " Sink : %s\n", mirror->sink); + ds_put_format(&ctx->output, " Filter : %s\n", mirror->filter); + ds_put_format(&ctx->output, " Index/Key: %ld\n", + (long int) mirror->index); + ds_put_cstr(&ctx->output, " Sources :"); + if (mirror->n_src > 0) { + struct svec srcs; + const char *src; + size_t j; + svec_init(&srcs); + for (j = 0; j < mirror->n_src; j++) { + svec_add(&srcs, mirror->src[j]->name); + } + svec_sort(&srcs); + SVEC_FOR_EACH (j, src, &srcs) { + ds_put_format(&ctx->output, " %s", src); + } + svec_destroy(&srcs); + } else { + ds_put_cstr(&ctx->output, " None attached"); + } + ds_put_cstr(&ctx->output, "\n"); + } + + free(mirrors); +} + static const struct ctl_table_class tables[NBREC_N_TABLES] = { [NBREC_TABLE_DHCP_OPTIONS].row_ids = {{&nbrec_logical_switch_port_col_name, NULL, @@ -7334,6 +7678,15 @@ static const struct ctl_command_syntax nbctl_commands[] = { { "qos-list", 1, 1, "SWITCH", nbctl_pre_qos_list, nbctl_qos_list, NULL, "", RO }, + /* mirror commands. */ + { "mirror-add", 5, 5, + "NAME TYPE INDEX FILTER IP", + nbctl_pre_mirror_add, nbctl_mirror_add, NULL, "--may-exist", RW }, + { "mirror-del", 0, 1, "[NAME]", + nbctl_pre_mirror_del, nbctl_mirror_del, NULL, "", RW }, + { "mirror-list", 0, 0, "", nbctl_pre_mirror_list, nbctl_mirror_list, + NULL, "", RO }, + /* meter commands. */ { "meter-add", 4, 5, "NAME ACTION RATE UNIT [BURST]", nbctl_pre_meter_add, nbctl_meter_add, NULL, "--fair,--may-exist", RW }, @@ -7388,6 +7741,10 @@ static const struct ctl_command_syntax nbctl_commands[] = { nbctl_lsp_get_dhcpv6_options, NULL, "", RO }, { "lsp-get-ls", 1, 1, "PORT", nbctl_pre_lsp_get_ls, nbctl_lsp_get_ls, NULL, "", RO }, + { "lsp-attach-mirror", 2, 2, "PORT MIRROR", nbctl_pre_lsp_mirror, + nbctl_lsp_attach_mirror, NULL, "", RW }, + { "lsp-detach-mirror", 2, 2, "PORT MIRROR", nbctl_pre_lsp_mirror, + nbctl_lsp_detach_mirror, NULL, "", RW }, /* forwarding group commands. */ { "fwd-group-add", 4, INT_MAX, "SWITCH GROUP VIP VMAC PORT...", diff --git a/utilities/ovn-sbctl.c b/utilities/ovn-sbctl.c index f60dde1b6..3d73e9e25 100644 --- a/utilities/ovn-sbctl.c +++ b/utilities/ovn-sbctl.c @@ -307,6 +307,7 @@ pre_get_info(struct ctl_context *ctx) ovsdb_idl_add_column(ctx->idl, &sbrec_port_binding_col_chassis); ovsdb_idl_add_column(ctx->idl, &sbrec_port_binding_col_datapath); ovsdb_idl_add_column(ctx->idl, &sbrec_port_binding_col_up); + ovsdb_idl_add_column(ctx->idl, &sbrec_port_binding_col_mirror_rules); ovsdb_idl_add_column(ctx->idl, &sbrec_logical_flow_col_logical_datapath); ovsdb_idl_add_column(ctx->idl, &sbrec_logical_flow_col_logical_dp_group); @@ -1431,6 +1432,9 @@ static const struct ctl_table_class tables[SBREC_N_TABLES] = { [SBREC_TABLE_HA_CHASSIS_GROUP].row_ids[0] = {&sbrec_ha_chassis_group_col_name, NULL, NULL}, + [SBREC_TABLE_MIRROR].row_ids[0] + = {&sbrec_mirror_col_name, NULL, NULL}, + [SBREC_TABLE_METER].row_ids[0] = {&sbrec_meter_col_name, NULL, NULL}, From patchwork Fri Nov 25 20:02:18 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Abhiram RN X-Patchwork-Id: 1709162 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=2605:bc80:3010::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=KWKPQiCa; dkim-atps=neutral Received: from smtp3.osuosl.org (smtp3.osuosl.org [IPv6:2605:bc80:3010::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 4NJm4r0Q6lz23nd for ; Sat, 26 Nov 2022 07:03:07 +1100 (AEDT) Received: from localhost (localhost [127.0.0.1]) by smtp3.osuosl.org (Postfix) with ESMTP id 1839261215; Fri, 25 Nov 2022 20:03:05 +0000 (UTC) DKIM-Filter: OpenDKIM Filter v2.11.0 smtp3.osuosl.org 1839261215 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=KWKPQiCa 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 miqAWxofxriw; Fri, 25 Nov 2022 20:03:02 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by smtp3.osuosl.org (Postfix) with ESMTPS id 33A466120E; Fri, 25 Nov 2022 20:03:01 +0000 (UTC) DKIM-Filter: OpenDKIM Filter v2.11.0 smtp3.osuosl.org 33A466120E Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id BEFBCC0032; Fri, 25 Nov 2022 20:03:00 +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 C2599C002D for ; Fri, 25 Nov 2022 20:02:59 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp2.osuosl.org (Postfix) with ESMTP id 3C51341594 for ; Fri, 25 Nov 2022 20:02:54 +0000 (UTC) DKIM-Filter: OpenDKIM Filter v2.11.0 smtp2.osuosl.org 3C51341594 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=KWKPQiCa 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 IlO4BRtzL83c for ; Fri, 25 Nov 2022 20:02:53 +0000 (UTC) X-Greylist: whitelisted by SQLgrey-1.8.0 DKIM-Filter: OpenDKIM Filter v2.11.0 smtp2.osuosl.org 2EA1241596 Received: from mail-pl1-x631.google.com (mail-pl1-x631.google.com [IPv6:2607:f8b0:4864:20::631]) by smtp2.osuosl.org (Postfix) with ESMTPS id 2EA1241596 for ; Fri, 25 Nov 2022 20:02:53 +0000 (UTC) Received: by mail-pl1-x631.google.com with SMTP id w23so4824646ply.12 for ; Fri, 25 Nov 2022 12:02:53 -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=oQw+YsbYIzu005uvNnYIIXnoQbP54rvGqHf+G47pbkM=; b=KWKPQiCapb87jDohCOuWFqavnWcfEjVBlrchM6R846zDgEZw1FSTYW6r/Pxm4/PqfG 1GhB8zLop5ES601OLNw/r72/HAHYBAf+9mO8Df2oZMqE8X+rJgMwwJZmiwAkHxSJKDKl 4+RNT/aWG3WogDB3Mb9EU+E3xA3S260AYz3+D6+cPJqkac+dqR2SUCbWmiKkSESaVsi3 /DCoGq6lrFg2Kc58H/1r9Fj7Ij9aeDTqWC3kA/299CtkHce/8kPaalGxf/8wgZroRxSN rSWLvr4g7bcZ1rdk3nQwFMbeLehPd+s6RBDxSzjBMdXMr6OGPYWWXSD+XW3UbnmuzU36 5DqQ== 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=oQw+YsbYIzu005uvNnYIIXnoQbP54rvGqHf+G47pbkM=; b=MBkQMAXY8r30JkgGpeWAkf8BQyV83JrsncfU2JlaxjfgzQ10QtviQldcPJQOdmGsAr /AVgMCWFGkf+uZ6E0EFKCK5E4mwy4Jhn5CpgMDVzgQqhkp69igfq1k3KxrNZIVF3mqDR MSGrZA9R2ScSlSQ3fvuflE1yKQ7rsrdMASwMUHsqW6HzX0Vn1dUoZ19t2MGW9UV2D0Rx X9eqmFpy1zvYuLrd1M9IzrTgoettKCIHT2gNDQIUETcWYFb/MHMYhi05POdx1IRXPMKs 5ReYfyhp42K4al7odUTxQVprxHJv6FR+5pTeyepKqWp2g8JWVZ+0w65BcdyZtkDmvXnw npkQ== X-Gm-Message-State: ANoB5pm9TtX3FjCEv+NYpLKle9sKyenk8ycoOflwQuyM9XD0iRTmiQY4 jue7ydWVAKJ1e1qHV557ERYajXkayWBCeA== X-Google-Smtp-Source: AA0mqf5dpxmo7Vo8egNTlb++4vHu5/KjbhaRuzT6GtoGvNfIWlXaLIuS39HZD579MJ1RDnJeNXW3Xw== X-Received: by 2002:a17:90b:30d0:b0:213:f1d8:eb6a with SMTP id hi16-20020a17090b30d000b00213f1d8eb6amr41461103pjb.219.1669406572235; Fri, 25 Nov 2022 12:02:52 -0800 (PST) Received: from arn.com ([49.205.133.150]) by smtp.googlemail.com with ESMTPSA id x80-20020a633153000000b004308422060csm2934375pgx.69.2022.11.25.12.02.49 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 25 Nov 2022 12:02:51 -0800 (PST) From: Abhiram R N To: dev@openvswitch.org Date: Sat, 26 Nov 2022 01:32:18 +0530 Message-Id: <20221125200219.1312140-2-abhiramrn@gmail.com> X-Mailer: git-send-email 2.31.1 In-Reply-To: <20221125200219.1312140-1-abhiramrn@gmail.com> References: <20221125200219.1312140-1-abhiramrn@gmail.com> MIME-Version: 1.0 Cc: vbarrenk@redhat.com, vedabarrenkala@gmail.com, arn@redhat.com Subject: [ovs-dev] [OVN v16 2/3] OVN Remote Port Mirroring: northd changes to sync NB and SB 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" Changes which syncs the NB port mirrors with SB port mirrors. Also test added to check the NB and SB sync Co-authored-by: Veda Barrenkala Signed-off-by: Veda Barrenkala Signed-off-by: Abhiram R N --- northd/en-northd.c | 4 + northd/inc-proc-northd.c | 4 + northd/northd.c | 172 +++++++++++++++++++++++++++++++++++++++ northd/northd.h | 2 + tests/ovn-northd.at | 102 +++++++++++++++++++++++ 5 files changed, 284 insertions(+) diff --git a/northd/en-northd.c b/northd/en-northd.c index 93891b0b7..66ecc6573 100644 --- a/northd/en-northd.c +++ b/northd/en-northd.c @@ -78,6 +78,8 @@ void en_northd_run(struct engine_node *node, void *data) EN_OVSDB_GET(engine_get_input("NB_acl", node)); input_data.nbrec_static_mac_binding_table = EN_OVSDB_GET(engine_get_input("NB_static_mac_binding", node)); + input_data.nbrec_mirror_table = + EN_OVSDB_GET(engine_get_input("NB_mirror", node)); input_data.sbrec_sb_global_table = EN_OVSDB_GET(engine_get_input("SB_sb_global", node)); @@ -109,6 +111,8 @@ void en_northd_run(struct engine_node *node, void *data) EN_OVSDB_GET(engine_get_input("SB_chassis_private", node)); input_data.sbrec_static_mac_binding_table = EN_OVSDB_GET(engine_get_input("SB_static_mac_binding", node)); + input_data.sbrec_mirror_table = + EN_OVSDB_GET(engine_get_input("SB_mirror", node)); northd_run(&input_data, data, eng_ctx->ovnnb_idl_txn, diff --git a/northd/inc-proc-northd.c b/northd/inc-proc-northd.c index 73f230b2c..7b7b250f3 100644 --- a/northd/inc-proc-northd.c +++ b/northd/inc-proc-northd.c @@ -52,6 +52,7 @@ VLOG_DEFINE_THIS_MODULE(inc_proc_northd); NB_NODE(acl, "acl") \ NB_NODE(logical_router, "logical_router") \ NB_NODE(qos, "qos") \ + NB_NODE(mirror, "mirror") \ NB_NODE(meter, "meter") \ NB_NODE(meter_band, "meter_band") \ NB_NODE(logical_router_port, "logical_router_port") \ @@ -94,6 +95,7 @@ VLOG_DEFINE_THIS_MODULE(inc_proc_northd); SB_NODE(logical_flow, "logical_flow") \ SB_NODE(logical_dp_group, "logical_DP_group") \ SB_NODE(multicast_group, "multicast_group") \ + SB_NODE(mirror, "mirror") \ SB_NODE(meter, "meter") \ SB_NODE(meter_band, "meter_band") \ SB_NODE(datapath_binding, "datapath_binding") \ @@ -176,6 +178,7 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb, engine_add_input(&en_northd, &en_nb_acl, NULL); engine_add_input(&en_northd, &en_nb_logical_router, NULL); engine_add_input(&en_northd, &en_nb_qos, NULL); + engine_add_input(&en_northd, &en_nb_mirror, NULL); engine_add_input(&en_northd, &en_nb_meter, NULL); engine_add_input(&en_northd, &en_nb_meter_band, NULL); engine_add_input(&en_northd, &en_nb_logical_router_port, NULL); @@ -197,6 +200,7 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb, engine_add_input(&en_northd, &en_sb_encap, NULL); engine_add_input(&en_northd, &en_sb_port_group, NULL); engine_add_input(&en_northd, &en_sb_logical_dp_group, NULL); + engine_add_input(&en_northd, &en_sb_mirror, NULL); engine_add_input(&en_northd, &en_sb_meter, NULL); engine_add_input(&en_northd, &en_sb_meter_band, NULL); engine_add_input(&en_northd, &en_sb_datapath_binding, NULL); diff --git a/northd/northd.c b/northd/northd.c index 040f46e1a..16739983c 100644 --- a/northd/northd.c +++ b/northd/northd.c @@ -3239,6 +3239,89 @@ ovn_port_update_sbrec_chassis( free(requested_chassis_sb); } +static void +do_sb_mirror_addition(struct northd_input *input_data, + const struct ovn_port *op) +{ + for (size_t i = 0; i < op->nbsp->n_mirror_rules; i++) { + const struct sbrec_mirror *sb_mirror; + SBREC_MIRROR_TABLE_FOR_EACH (sb_mirror, + input_data->sbrec_mirror_table) { + if (!strcmp(sb_mirror->name, + op->nbsp->mirror_rules[i]->name)) { + /* Add the value to SB */ + sbrec_port_binding_update_mirror_rules_addvalue(op->sb, + sb_mirror); + } + } + } +} + +static void +sbrec_port_binding_update_mirror_rules(struct northd_input *input_data, + const struct ovn_port *op) +{ + size_t i = 0; + if (op->sb->n_mirror_rules > op->nbsp->n_mirror_rules) { + /* Needs deletion in SB */ + struct shash nb_mirror_rules = SHASH_INITIALIZER(&nb_mirror_rules); + for (i = 0; i < op->nbsp->n_mirror_rules; i++) { + shash_add(&nb_mirror_rules, + op->nbsp->mirror_rules[i]->name, + op->nbsp->mirror_rules[i]); + } + + for (i = 0; i < op->sb->n_mirror_rules; i++) { + if (!shash_find(&nb_mirror_rules, + op->sb->mirror_rules[i]->name)) { + sbrec_port_binding_update_mirror_rules_delvalue(op->sb, + op->sb->mirror_rules[i]); + } + } + + struct shash_node *node, *next; + SHASH_FOR_EACH_SAFE (node, next, &nb_mirror_rules) { + shash_delete(&nb_mirror_rules, node); + } + shash_destroy(&nb_mirror_rules); + + } else if (op->sb->n_mirror_rules < op->nbsp->n_mirror_rules) { + /* Needs addition in SB */ + do_sb_mirror_addition(input_data, op); + } else if (op->sb->n_mirror_rules == op->nbsp->n_mirror_rules) { + /* + * Check if its the same mirrors on both SB and NB DBs + * If not update accordingly. + */ + bool needs_sb_addition = false; + struct shash nb_mirror_rules = SHASH_INITIALIZER(&nb_mirror_rules); + for (i = 0; i < op->nbsp->n_mirror_rules; i++) { + shash_add(&nb_mirror_rules, + op->nbsp->mirror_rules[i]->name, + op->nbsp->mirror_rules[i]); + } + + for (i = 0; i < op->sb->n_mirror_rules; i++) { + if (!shash_find(&nb_mirror_rules, + op->sb->mirror_rules[i]->name)) { + sbrec_port_binding_update_mirror_rules_delvalue(op->sb, + op->sb->mirror_rules[i]); + needs_sb_addition = true; + } + } + + struct shash_node *node, *next; + SHASH_FOR_EACH_SAFE (node, next, &nb_mirror_rules) { + shash_delete(&nb_mirror_rules, node); + } + shash_destroy(&nb_mirror_rules); + + if (needs_sb_addition) { + do_sb_mirror_addition(input_data, op); + } + } +} + static void ovn_port_update_sbrec(struct northd_input *input_data, struct ovsdb_idl_txn *ovnsb_txn, @@ -3598,6 +3681,15 @@ ovn_port_update_sbrec(struct northd_input *input_data, } sbrec_port_binding_set_external_ids(op->sb, &ids); smap_destroy(&ids); + + if (!op->nbsp->n_mirror_rules) { + /* Nothing is set. Clear mirror_rules from pb. */ + sbrec_port_binding_set_mirror_rules(op->sb, NULL, 0); + } else { + /* Check if SB DB update needed */ + sbrec_port_binding_update_mirror_rules(input_data, op); + } + } if (op->tunnel_key != op->sb->tunnel_key) { sbrec_port_binding_set_tunnel_key(op->sb, op->tunnel_key); @@ -15161,6 +15253,85 @@ sync_meters(struct northd_input *input_data, shash_destroy(&sb_meters); } +static bool +mirror_needs_update(const struct nbrec_mirror *nb_mirror, + const struct sbrec_mirror *sb_mirror) +{ + + if (nb_mirror->index != sb_mirror->index) { + return true; + } else if (strcmp(nb_mirror->sink, sb_mirror->sink)) { + return true; + } else if (strcmp(nb_mirror->type, sb_mirror->type)) { + return true; + } else if (strcmp(nb_mirror->filter, sb_mirror->filter)) { + return true; + } + + return false; +} + +static void +sync_mirrors_iterate_nb_mirror(struct ovsdb_idl_txn *ovnsb_txn, + const char *mirror_name, + const struct nbrec_mirror *nb_mirror, + struct shash *sb_mirrors, + struct sset *used_sb_mirrors) +{ + const struct sbrec_mirror *sb_mirror; + bool new_sb_mirror = false; + + sb_mirror = shash_find_data(sb_mirrors, mirror_name); + if (!sb_mirror) { + sb_mirror = sbrec_mirror_insert(ovnsb_txn); + sbrec_mirror_set_name(sb_mirror, mirror_name); + shash_add(sb_mirrors, sb_mirror->name, sb_mirror); + new_sb_mirror = true; + } + sset_add(used_sb_mirrors, mirror_name); + + if ((new_sb_mirror) || mirror_needs_update(nb_mirror, sb_mirror)) { + sbrec_mirror_set_filter(sb_mirror,nb_mirror->filter); + sbrec_mirror_set_index(sb_mirror, nb_mirror->index); + sbrec_mirror_set_sink(sb_mirror, nb_mirror->sink); + sbrec_mirror_set_type(sb_mirror, nb_mirror->type); + } +} + +static void +sync_mirrors(struct northd_input *input_data, + struct ovsdb_idl_txn *ovnsb_txn) +{ + struct shash sb_mirrors = SHASH_INITIALIZER(&sb_mirrors); + struct sset used_sb_mirrors = SSET_INITIALIZER(&used_sb_mirrors); + + const struct sbrec_mirror *sb_mirror; + SBREC_MIRROR_TABLE_FOR_EACH (sb_mirror, input_data->sbrec_mirror_table) { + shash_add(&sb_mirrors, sb_mirror->name, sb_mirror); + } + + const struct nbrec_mirror *nb_mirror; + NBREC_MIRROR_TABLE_FOR_EACH (nb_mirror, input_data->nbrec_mirror_table) { + sync_mirrors_iterate_nb_mirror(ovnsb_txn, nb_mirror->name, nb_mirror, + &sb_mirrors, &used_sb_mirrors); + } + + const char *used_mirror; + const char *used_mirror_next; + SSET_FOR_EACH_SAFE (used_mirror, used_mirror_next, &used_sb_mirrors) { + shash_find_and_delete(&sb_mirrors, used_mirror); + sset_delete(&used_sb_mirrors, SSET_NODE_FROM_NAME(used_mirror)); + } + sset_destroy(&used_sb_mirrors); + + struct shash_node *node, *next; + SHASH_FOR_EACH_SAFE (node, next, &sb_mirrors) { + sbrec_mirror_delete(node->data); + shash_delete(&sb_mirrors, node); + } + shash_destroy(&sb_mirrors); +} + /* * struct 'dns_info' is used to sync the DNS records between OVN Northbound db * and Southbound db. @@ -15794,6 +15965,7 @@ ovnnb_db_run(struct northd_input *input_data, sync_lbs(input_data, ovnsb_txn, &data->datapaths, &data->lbs); sync_port_groups(input_data, ovnsb_txn, &data->port_groups); sync_meters(input_data, ovnsb_txn, &data->meter_groups); + sync_mirrors(input_data, ovnsb_txn); sync_dns_entries(input_data, ovnsb_txn, &data->datapaths); cleanup_stale_fdb_entries(input_data, &data->datapaths); stopwatch_stop(CLEAR_LFLOWS_CTX_STOPWATCH_NAME, time_msec()); diff --git a/northd/northd.h b/northd/northd.h index ea9bd5797..1670177ed 100644 --- a/northd/northd.h +++ b/northd/northd.h @@ -35,6 +35,7 @@ struct northd_input { const struct nbrec_acl_table *nbrec_acl_table; const struct nbrec_static_mac_binding_table *nbrec_static_mac_binding_table; + const struct nbrec_mirror_table *nbrec_mirror_table; /* Southbound table references */ const struct sbrec_sb_global_table *sbrec_sb_global_table; @@ -53,6 +54,7 @@ struct northd_input { const struct sbrec_chassis_private_table *sbrec_chassis_private_table; const struct sbrec_static_mac_binding_table *sbrec_static_mac_binding_table; + const struct sbrec_mirror_table *sbrec_mirror_table; /* Indexes */ struct ovsdb_idl_index *sbrec_chassis_by_name; diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at index 02c00c062..466d16916 100644 --- a/tests/ovn-northd.at +++ b/tests/ovn-northd.at @@ -2319,6 +2319,108 @@ check_meter_by_name NOT meter_me__${acl1} meter_me__${acl2} AT_CLEANUP ]) +OVN_FOR_EACH_NORTHD_NO_HV([ +AT_SETUP([Check NB-SB mirrors sync]) +AT_KEYWORDS([mirrors]) +ovn_start + +check ovn-nbctl --wait=sb mirror-add mirror1 erspan 0 both 10.10.10.2 + +AT_CHECK([ovn-sbctl get Mirror . Sink], [0], [dnl +"10.10.10.2" +]) + +AT_CHECK([ovn-sbctl get Mirror . type], [0], [dnl +erspan +]) + +AT_CHECK([ovn-sbctl get Mirror . index], [0], [dnl +0 +]) + +AT_CHECK([ovn-sbctl get Mirror . filter], [0], [dnl +both +]) + +check ovn-nbctl --wait=sb \ + -- set mirror . sink=192.168.1.13 + +AT_CHECK([ovn-sbctl get Mirror . Sink], [0], [dnl +"192.168.1.13" +]) + +AT_CHECK([ovn-sbctl get Mirror . type], [0], [dnl +erspan +]) + +AT_CHECK([ovn-sbctl get Mirror . index], [0], [dnl +0 +]) + +AT_CHECK([ovn-sbctl get Mirror . filter], [0], [dnl +both +]) + +check ovn-nbctl --wait=sb \ + -- set mirror . type=gre + +AT_CHECK([ovn-sbctl get Mirror . type], [0], [dnl +gre +]) + +AT_CHECK([ovn-sbctl get Mirror . Sink], [0], [dnl +"192.168.1.13" +]) + +AT_CHECK([ovn-sbctl get Mirror . index], [0], [dnl +0 +]) + +AT_CHECK([ovn-sbctl get Mirror . filter], [0], [dnl +both +]) + +check ovn-nbctl --wait=sb \ + -- set mirror . index=12 + +AT_CHECK([ovn-sbctl get Mirror . index], [0], [dnl +12 +]) + +AT_CHECK([ovn-sbctl get Mirror . type], [0], [dnl +gre +]) + +AT_CHECK([ovn-sbctl get Mirror . Sink], [0], [dnl +"192.168.1.13" +]) + +AT_CHECK([ovn-sbctl get Mirror . filter], [0], [dnl +both +]) + +check ovn-nbctl --wait=sb \ + -- set mirror . filter=to-lport + +AT_CHECK([ovn-sbctl get Mirror . filter], [0], [dnl +to-lport +]) + +AT_CHECK([ovn-sbctl get Mirror . index], [0], [dnl +12 +]) + +AT_CHECK([ovn-sbctl get Mirror . type], [0], [dnl +gre +]) + +AT_CHECK([ovn-sbctl get Mirror . Sink], [0], [dnl +"192.168.1.13" +]) + +AT_CLEANUP +]) + OVN_FOR_EACH_NORTHD_NO_HV([ AT_SETUP([ACL skip hints for stateless config]) AT_KEYWORDS([acl]) From patchwork Fri Nov 25 20:02:19 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Abhiram RN X-Patchwork-Id: 1709163 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=2605:bc80:3010::137; helo=smtp4.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=DIwkjOGP; dkim-atps=neutral Received: from smtp4.osuosl.org (smtp4.osuosl.org [IPv6:2605:bc80:3010::137]) (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 4NJm5B0ztPz23nd for ; Sat, 26 Nov 2022 07:03:25 +1100 (AEDT) Received: from localhost (localhost [127.0.0.1]) by smtp4.osuosl.org (Postfix) with ESMTP id 23D4D41D9B; Fri, 25 Nov 2022 20:03:22 +0000 (UTC) DKIM-Filter: OpenDKIM Filter v2.11.0 smtp4.osuosl.org 23D4D41D9B Authentication-Results: smtp4.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=DIwkjOGP X-Virus-Scanned: amavisd-new at osuosl.org Received: from smtp4.osuosl.org ([127.0.0.1]) by localhost (smtp4.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id zX9MmlOAaSAB; Fri, 25 Nov 2022 20:03:19 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by smtp4.osuosl.org (Postfix) with ESMTPS id 1DF0B4089E; Fri, 25 Nov 2022 20:03:18 +0000 (UTC) DKIM-Filter: OpenDKIM Filter v2.11.0 smtp4.osuosl.org 1DF0B4089E Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id DC265C0032; Fri, 25 Nov 2022 20:03:17 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp3.osuosl.org (smtp3.osuosl.org [IPv6:2605:bc80:3010::136]) by lists.linuxfoundation.org (Postfix) with ESMTP id 35BF5C0032 for ; Fri, 25 Nov 2022 20:03:17 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp3.osuosl.org (Postfix) with ESMTP id B908C61227 for ; Fri, 25 Nov 2022 20:03:10 +0000 (UTC) DKIM-Filter: OpenDKIM Filter v2.11.0 smtp3.osuosl.org B908C61227 Authentication-Results: smtp3.osuosl.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20210112 header.b=DIwkjOGP 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 Ej9CgLwlHkLT for ; Fri, 25 Nov 2022 20:03:08 +0000 (UTC) X-Greylist: whitelisted by SQLgrey-1.8.0 DKIM-Filter: OpenDKIM Filter v2.11.0 smtp3.osuosl.org 1E94861220 Received: from mail-pg1-x52b.google.com (mail-pg1-x52b.google.com [IPv6:2607:f8b0:4864:20::52b]) by smtp3.osuosl.org (Postfix) with ESMTPS id 1E94861220 for ; Fri, 25 Nov 2022 20:03:08 +0000 (UTC) Received: by mail-pg1-x52b.google.com with SMTP id v3so4743482pgh.4 for ; Fri, 25 Nov 2022 12:03:07 -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=agR2AX0MBuaAjuCnnHobjn38DzWMrqiZrePEtEPwbEs=; b=DIwkjOGPcYOa1M08aeFzVZVo+9tNGmvJb/aA4ljkCObejz81Q6S41MCJzidhB2weVY EOgPi6P00x1ME7pdGEQZpBN7DJgYcvRhvarcrg9MgzPpxVyLca3KJuR9xEcaJttQHWsh pViBdWPOs7Gl7rreCxwqbX3tPPRtZx3nuSEnWrgyQ+Ohe2hyTYxDKPv30nC9mcB48PTC IKUb+GJ8hGJAl+D0w1IwNhlj38Puo8ssd7DQW7eiRX9wf4o9oYeeYMTFCh3HYo4szq8G dP4zUQfcBdT0kQDfi5h00BBtsxkU3SmMEDiWFy0GDafFc5J7efS8NQ/YjzaqoWmksUWW Kiow== 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=agR2AX0MBuaAjuCnnHobjn38DzWMrqiZrePEtEPwbEs=; b=7pBubex9OKWu5VzxQtUMGCmhc9WvJI1z9QVdY5uNCgXFCjzpHY599i2tn94YMLSMRs OGV3TiDVFIkgUY2YWgBvoU6LauCYH20Ar7wOnYWgARfcz9gxRxSosZbOXF+NGobTRC9o jKJ/ZbbJHXYslh8tCC2jwEmH/t59Jh76VnarHo0kn7xx9lSWMVjg9vHCiwRuFD5nIG+A qdeH0d+SaFlsZy/BNZW8C5pcKykbG1RFYN7YNo4JpBO4fJVIH20XjAfUbTVB+C5KwEdv Ao/I66fbSl7avHlAGKemwq7BlKh53E8WLa+V5xrswLlS9ATI06kEg6NWF2qPaEDgvYE0 lReQ== X-Gm-Message-State: ANoB5plwi1iL48dln+/I5Ie5+d12z/XxcXfG2wFyCaS6ByCqADejVSh1 aI1wgFzAmL2gCpXvYcejVri1wmHy4TCi7Q== X-Google-Smtp-Source: AA0mqf7KTqDGF8QWFlF4shypknX3Ay0foIGkuliAaSEu4yuetZIZQESPY7XQSkLdAjSEXu+JY1miOw== X-Received: by 2002:a63:42c2:0:b0:477:d8c5:3ab0 with SMTP id p185-20020a6342c2000000b00477d8c53ab0mr6259417pga.95.1669406586458; Fri, 25 Nov 2022 12:03:06 -0800 (PST) Received: from arn.com ([49.205.133.150]) by smtp.googlemail.com with ESMTPSA id x80-20020a633153000000b004308422060csm2934375pgx.69.2022.11.25.12.03.03 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 25 Nov 2022 12:03:06 -0800 (PST) From: Abhiram R N To: dev@openvswitch.org Date: Sat, 26 Nov 2022 01:32:19 +0530 Message-Id: <20221125200219.1312140-3-abhiramrn@gmail.com> X-Mailer: git-send-email 2.31.1 In-Reply-To: <20221125200219.1312140-1-abhiramrn@gmail.com> References: <20221125200219.1312140-1-abhiramrn@gmail.com> MIME-Version: 1.0 Cc: vbarrenk@redhat.com, vedabarrenkala@gmail.com, arn@redhat.com Subject: [ovs-dev] [OVN v16 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 --- NEWS | 1 + controller/automake.mk | 4 +- controller/mirror.c | 402 ++++++++++++++++++++++++++++ controller/mirror.h | 33 +++ controller/ovn-controller.c | 54 ++-- tests/ovn.at | 514 ++++++++++++++++++++++++++++++++++++ 6 files changed, 985 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..b947aeb5f --- /dev/null +++ b/controller/mirror.c @@ -0,0 +1,402 @@ +/* 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); + + /* 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); + } + m->ovs_mirror = ovs_mirror; + } + + if (shash_is_empty(&ovn_mirrors)) { + shash_destroy(&ovn_mirrors); + return; + } + + /* 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); + +} + +/* 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])