From patchwork Wed Apr 6 20:19:09 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Abhiram RN X-Patchwork-Id: 1614128 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: bilbo.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=iohSFeH2; dkim-atps=neutral Authentication-Results: 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=) 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 RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by bilbo.ozlabs.org (Postfix) with ESMTPS id 4KYbTj53Wfz9sFv for ; Thu, 7 Apr 2022 06:19:53 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp3.osuosl.org (Postfix) with ESMTP id 80C1861113; Wed, 6 Apr 2022 20:19:51 +0000 (UTC) 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 DWy45XtwuivR; Wed, 6 Apr 2022 20:19:50 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by smtp3.osuosl.org (Postfix) with ESMTPS id 169D5605AC; Wed, 6 Apr 2022 20:19:49 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id DB03CC002C; Wed, 6 Apr 2022 20:19:48 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp4.osuosl.org (smtp4.osuosl.org [IPv6:2605:bc80:3010::137]) by lists.linuxfoundation.org (Postfix) with ESMTP id 108BEC0012 for ; Wed, 6 Apr 2022 20:19:48 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp4.osuosl.org (Postfix) with ESMTP id E3A3040280 for ; Wed, 6 Apr 2022 20:19:47 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Authentication-Results: smtp4.osuosl.org (amavisd-new); dkim=pass (2048-bit key) header.d=gmail.com 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 i86Qdk7Riq3d for ; Wed, 6 Apr 2022 20:19:46 +0000 (UTC) X-Greylist: whitelisted by SQLgrey-1.8.0 Received: from mail-pl1-x631.google.com (mail-pl1-x631.google.com [IPv6:2607:f8b0:4864:20::631]) by smtp4.osuosl.org (Postfix) with ESMTPS id 0F4114023B for ; Wed, 6 Apr 2022 20:19:45 +0000 (UTC) Received: by mail-pl1-x631.google.com with SMTP id m16so2977324plx.3 for ; Wed, 06 Apr 2022 13:19:45 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=aN7jhAFszcrOb7yyyPWHWh65n6lj/tiVx50ze+VloCM=; b=iohSFeH2ktW6lm+hyoW/utDz8sVVTcHsDXN5HBwFOK7DICoyMSuGqrcDcMj7C6Lou3 VqPmnjhqfhrBl8ZVHjlRA5JfHYQ3+wo9ORpVcUkNvGKuDBXHPS9iv6aLq30VpEGkNQzt 1qGrmMzRCRrAXoqpQoevY1Azii2GUfoAbCwkfK0VECCLM1HN/hGpbhvBPoy1F5eOXTgf SCR6Khng2DgeM98QAw7UWZJNgLsJcFt1s7sXklu/RgOzMR3bg0Vir5topiWaHHmc1QS/ qDN7HJNE27kdUYFEpt3sYuN1Uz4LDDifFvfZCwOiHyNnrXfXl/tDL0a1Xg02ehwip44v GNsw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=aN7jhAFszcrOb7yyyPWHWh65n6lj/tiVx50ze+VloCM=; b=dWUwESZ3YIwN/GScB2T1DjktHEfx//m4l4LRwEyw2iBTaSsIOLsBNBU/gpz8G/SEB+ XGZrKUVW0n7G3/2F3A8ITNtLeNqi0Poyf5uW6EdW8/3KGFt+UnOmUuzlkNnxQDiE3az+ s8NGy+lQCheQP2e9cK1ROCwfMGegVVm2ThavbG2JQpxWXxEQxekc1EniFIbXI5agie40 wggcBaxsswNBskr6+F+tPTFPOlPTXA805AnLN0ecxYygCYg3bOj8yaYJUQQDoczMRia+ wsAtkIP8sCm/PykYT9du/2I7GHf17kP6rMKj7cDIB1mrCsVAuXmwaB3zfnluKPJJ8bbB C0YQ== X-Gm-Message-State: AOAM533IDxKmQBzaB56UyTNoODmTloEvy4bBPycRnFQkWjEWbP8Uo7F8 KNU7ki7i7swF6cNHtL/9357IiN6d9sRphyqb X-Google-Smtp-Source: ABdhPJweUHsFV8294X4NJoLKj6U7UQgmqCtEHPocS8BK7tcsbS8IkJJ4n93CmiGe74xODAfc6L3Zuw== X-Received: by 2002:a17:902:9345:b0:153:4d7a:53d9 with SMTP id g5-20020a170902934500b001534d7a53d9mr10612672plp.116.1649276383414; Wed, 06 Apr 2022 13:19:43 -0700 (PDT) Received: from arn.com ([49.205.133.255]) by smtp.googlemail.com with ESMTPSA id p34-20020a056a000a2200b004cd49fc15e5sm21424998pfh.59.2022.04.06.13.19.40 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 06 Apr 2022 13:19:43 -0700 (PDT) From: Abhiram R N To: dev@openvswitch.org Date: Thu, 7 Apr 2022 01:49:09 +0530 Message-Id: <20220406201909.133011-1-abhiramrn@gmail.com> X-Mailer: git-send-email 2.27.0 MIME-Version: 1.0 Cc: vbarrenk@redhat.com, arn@redhat.com, vedabarrenkala@gmail.com Subject: [ovs-dev] [Patch ovn] ovn-nbctl: Add support for Port Mirroring 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 done in ovn-nbctl to support creation of mirror and attach a source port for the same. Example commands: ovn-nbctl mirror-add mirror1 gre 0 to-lport sw0-port2 ovn-nbctl lsp-attach-mirror sw0-port1 mirror1 Changes are needed in ovn-sbctl and ovn-controller as well to make it functional end to end. That needs to be done in future commits as incremental changes. Co-authored-by: Veda Barrenkala Signed-off-by: Abhiram R N --- ovn-nb.ovsschema | 27 ++- ovn-nb.xml | 53 ++++++ tests/ovn-nbctl.at | 78 +++++++++ utilities/ovn-nbctl.c | 397 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 553 insertions(+), 2 deletions(-) diff --git a/ovn-nb.ovsschema b/ovn-nb.ovsschema index 6c69732e9..892807081 100644 --- a/ovn-nb.ovsschema +++ b/ovn-nb.ovsschema @@ -1,7 +1,7 @@ { "name": "OVN_Northbound", - "version": "6.2.0", - "cksum": "2862883635 31533", + "version": "6.3.0", + "cksum": "1451927243 32797", "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,24 @@ "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"}}}, + "isRoot": true}, "Meter": { "columns": { "name": {"type": "string"}, diff --git a/ovn-nb.xml b/ovn-nb.xml index 0e3ad2b9b..3163dc2bf 100644 --- a/ovn-nb.xml +++ b/ovn-nb.xml @@ -1503,6 +1503,10 @@ + + Mirror rules that apply to logical switch port which is the source + + References a row in the OVN Northbound database's table. @@ -2424,6 +2428,55 @@ + +

+ Each row in this table represents one Mirror that can be used for + port mirroring +

+ + +

+ 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 +

+
+ + +

+ The value of this field represents the source port for the mirror. +

+
+ + + 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/tests/ovn-nbctl.at b/tests/ovn-nbctl.at index db0d23099..1cb01d456 100644 --- a/tests/ovn-nbctl.at +++ b/tests/ovn-nbctl.at @@ -435,6 +435,84 @@ 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 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]) +AT_CHECK([ovn-nbctl lsp-set-addresses sw0-port2 "aa:aa:aa:aa:aa:30 10.10.10.3"]) +AT_CHECK([ovn-nbctl mirror-add mirror3 gre 2 to-lport sw0-port2]) + +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 Add mirror with invalid sink port +AT_CHECK([ovn-nbctl mirror-add mirror1 gre 0 from-lport sw0-port4], [1], [], [stderr]) +AT_CHECK([grep 'already exists' 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 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 Detach one source port from mirror +AT_CHECK([ovn-nbctl lsp-detach-mirror sw0-port3 mirror3]) + +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. +AT_CHECK([ovn-nbctl mirror-del mirror3]) + +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 all mirrors and remove switch +AT_CHECK([ovn-nbctl mirror-del]) +AT_CHECK([ovn-nbctl ls-del sw0]) +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 f8b957792..f2a28d416 100644 --- a/utilities/ovn-nbctl.c +++ b/utilities/ovn-nbctl.c @@ -271,6 +271,16 @@ 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|PORT}\n\ + add a mirror with given name\n\ + specify TYPE 'gre' or 'erspan'\n\ + specify INDEX gre key/erpsan idx\n\ + specify FILTER for mirroring selection\n\ + specify Sink/Destination IP or port\n\ + mirror-del [NAME]\n\ + remove mirrors\n\ +\n\ Meter commands:\n\ [--fair]\n\ meter-add NAME ACTION RATE UNIT [BURST]\n\ @@ -311,6 +321,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 the MIRROR\n\ + lsp-detach-mirror PORT MIRROR detach source PORT from the MIRROR\n\ \n\ Forwarding group commands:\n\ [--liveness]\n\ @@ -1678,6 +1690,129 @@ 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) { @@ -7014,6 +7149,255 @@ 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_sink(struct ctl_context *ctx, const char **sink_ip, + struct lport_addresses *laddrs, bool *used_laddrs) +{ + char *new_external_ip = NULL; + const struct nbrec_logical_switch_port *lsp=NULL; + char *error = NULL; + + error = lsp_by_name_or_uuid(ctx, ctx->argv[5], true, &lsp); + if (error) { + //check if it is a valid ip. + new_external_ip = normalize_ipv4_addr_str(ctx->argv[5]); + if (!new_external_ip) { + new_external_ip = normalize_ipv6_addr_str(ctx->argv[5]); + } + + if (new_external_ip) { + *sink_ip = ctx->argv[5]; + free(new_external_ip); + } else { + return xasprintf("Invalid sink port"); + } + } else { + /* Check if address is set to this port */ + if (lsp->n_addresses) { + /* See if its dynamic or statically set */ + char* ip_p = NULL; + /* Find occurrence of dynamic in lsp->addresses */ + ip_p = strstr(lsp->addresses[0], "dynamic"); + + if (ip_p) { + if (lsp->dynamic_addresses) { + ip_p = &lsp->dynamic_addresses[0]; + } + } else { + ip_p = lsp->addresses[0]; + } + if (!extract_lsp_addresses(ip_p, laddrs)) { + return xasprintf("IP address not set for the sink port"); + } + *used_laddrs = true; + if (laddrs->n_ipv4_addrs) { + *sink_ip = laddrs->ipv4_addrs[0].addr_s; + } else if (laddrs->n_ipv6_addrs) { + *sink_ip = laddrs->ipv6_addrs[0].addr_s; + } + } else { + return xasprintf("IP not set for sink port"); + } + } + return NULL; +} + +static char * OVS_WARN_UNUSED_RESULT +parse_type(const char *arg, const char **type_p) +{ + /* Validate type. Only require the second 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_logical_switch_port_col_name); + ovsdb_idl_add_column(ctx->idl, &nbrec_logical_switch_port_col_addresses); + ovsdb_idl_add_column(ctx->idl, &nbrec_logical_switch_port_col_dynamic_addresses); + 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; + int64_t index; + char *error = NULL; + struct lport_addresses laddrs; + const struct nbrec_mirror *mirror_check = NULL; + bool used_laddrs = false; + + /* 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 */ + error = parse_sink(ctx,&sink_ip, &laddrs, &used_laddrs); + if (error) { + ctx->error = error; + 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); + + if (used_laddrs) { + destroy_lport_addresses(&laddrs); + } + +} + +static void +nbctl_pre_mirror_del(struct ctl_context *ctx) +{ + ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_name); +} + +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", mirror->index); + ds_put_cstr(&ctx->output, " Sources :"); + if(mirror->n_src>0){ + for (size_t j = 0; j < mirror->n_src; j++) { + ds_put_format(&ctx->output, " %s", mirror->src[j]->name); + } + } 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, @@ -7107,6 +7491,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|PORT}", + 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 }, @@ -7161,6 +7554,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...",