[qca-nss-clients] Mirror client module.
Change-Id: I61e3b6452db7096abe66a8dc6e2b079397331372
Signed-off-by: Manish Verma <maniverm@codeaurora.org>
diff --git a/Makefile b/Makefile
index e52480d..8b16c6c 100644
--- a/Makefile
+++ b/Makefile
@@ -35,6 +35,7 @@
obj-$(clmapmgr)+= clmapmgr/
obj-$(match)+= match/
obj-$(tlsmgr)+= tls/
+obj-$(mirror)+= mirror/
#NSS NETLINK
obj-$(netlink)+= netlink/
diff --git a/exports/nss_mirror_public.h b/exports/nss_mirror_public.h
new file mode 100644
index 0000000..de3e227
--- /dev/null
+++ b/exports/nss_mirror_public.h
@@ -0,0 +1,131 @@
+/*
+ ***************************************************************************
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ ***************************************************************************
+ */
+
+/**
+ * nss_mirror_public.h
+ * NSS Mirror interface definitions.
+ */
+#ifndef __NSS_MIRROR_H
+#define __NSS_MIRROR_H
+
+#include <nss_api_if.h>
+
+/**
+ * nss_mirror_disable
+ * API to send disable message to mirror interface in NSS firmware.
+ *
+ * @datatypes
+ * struct net_device \n
+ *
+ * @param[in] mirror_dev Mirror netdevice pointer.
+ *
+ * @return
+ * Return 0 on success, -1 on failure.
+ */
+extern int nss_mirror_disable(struct net_device *mirror_dev);
+
+/**
+ * nss_mirror_enable
+ * API to send enable message to mirror interface in NSS firmware.
+ *
+ * @datatypes
+ * struct net_device \n
+ *
+ * @param[in] mirror_dev Mirror netdevice pointer.
+ *
+ * @return
+ * Return 0 on success, -1 on failure.
+ */
+extern int nss_mirror_enable(struct net_device *mirror_dev);
+
+/**
+ * nss_mirror_set_nexthop
+ * API to send set nexthop message to mirror interface in NSS firmware.
+ *
+ * @datatypes
+ * struct net_device \n
+ *
+ * @param[in] mirror_dev Mirror netdevice pointer.
+ * @param[in] mirror_next_hop Nexthop interface number.
+ *
+ * @return
+ * Return 0 on success, -1 on failure.
+ */
+extern int nss_mirror_set_nexthop(struct net_device *mirror_dev, int32_t mirror_next_hop);
+
+/**
+ * nss_mirror_reset_nexthop
+ * API to send reset nexthop message to mirror interface in NSS firmware.
+ *
+ * @datatypes
+ * struct net_device \n
+ *
+ * @param[in] mirror_dev Mirror netdevice pointer.
+ *
+ * @return
+ * Return 0 on success, -1 on failure.
+ */
+extern int nss_mirror_reset_nexthop(struct net_device *mirror_dev);
+
+/**
+ * nss_mirror_configure
+ * API to send configure message to mirror interface in NSS firmware.
+ *
+ * @datatypes
+ * struct net_device \n
+ * nss_mirror_pkt_clone_point \n
+ *
+ * @param[in] mirror_dev Mirror netdevice pointer.
+ * @param[in] pkt_clone_point Point in the packet to copy from.
+ * @param[in] pkt_clone_size Number of bytes to copy.
+ * @param[in] pkt_clone_offset Copy offset.
+ *
+ * @return
+ * Return 0 on success, -1 on failure.
+ */
+extern int nss_mirror_configure(struct net_device *mirror_dev, enum nss_mirror_pkt_clone_point pkt_clone_point,
+ uint16_t pkt_clone_size, uint16_t pkt_clone_offset);
+
+/**
+ * nss_mirror_destroy
+ * API to de-register and delete mirror interface.
+ *
+ * @datatypes
+ * struct net_device \n
+ *
+ * @param[in] mirror_dev Mirror netdevice pointer.
+ *
+ * @return
+ * Return 0 on success, -1 on failure.
+ */
+extern int nss_mirror_destroy(struct net_device *mirror_dev);
+
+/**
+ * nss_mirror_create
+ * API to create and register mirror interface.
+ *
+ * @datatypes
+ * struct net_device \n
+ *
+ * @return
+ * Return pointer of the newly created device.
+ */
+
+extern struct net_device *nss_mirror_create(void);
+
+#endif /* __NSS_MIRROR_H */
diff --git a/mirror/Makefile b/mirror/Makefile
new file mode 100644
index 0000000..213f0b9
--- /dev/null
+++ b/mirror/Makefile
@@ -0,0 +1,11 @@
+# Makefile for NSS MIRROR
+
+ccflags-y += $(NSS_CCFLAGS) -I$(obj)/../../exports
+ccflags-y += -DNSS_MIRROR_DEBUG_LEVEL=2
+ccflags-y += -Werror
+
+obj-m += qca-nss-mirror.o
+qca-nss-mirror-objs := \
+ nss_mirror.o \
+ nss_mirror_ctl.o \
+ nss_mirror_init.o
diff --git a/mirror/nss_mirror.c b/mirror/nss_mirror.c
new file mode 100644
index 0000000..b976a33
--- /dev/null
+++ b/mirror/nss_mirror.c
@@ -0,0 +1,885 @@
+/*
+ ***************************************************************************
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ ***************************************************************************
+ */
+
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+
+#include <nss_api_if.h>
+#include <nss_cmn.h>
+
+#include "nss_mirror.h"
+
+#define NSS_MIRROR_NETDEV_NAME "mirror."
+
+/*
+ * Mirror netdevice count for naming convension.
+ */
+static u8 nss_mirror_netdev_count = 0;
+
+/*
+ * Mirror device statistics lock.
+ */
+static DEFINE_SPINLOCK(nss_mirror_stats_lock);
+
+/*
+ * Mirror interface array lock.
+ */
+static DEFINE_SPINLOCK(nss_mirror_arr_lock);
+
+/*
+ * Array of mirror interfaces.
+ */
+static struct net_device *nss_mirror_arr[NSS_MAX_MIRROR_DYNAMIC_INTERFACES];
+
+/*
+ * nss_mirror_arr_del()
+ * API to delete mirror netdev from mirror array.
+ */
+static void nss_mirror_arr_del(struct net_device *mirror_dev)
+{
+ struct nss_mirror_instance_priv *mirror_priv = netdev_priv(mirror_dev);
+
+ spin_lock(&nss_mirror_arr_lock);
+ nss_mirror_arr[mirror_priv->mirror_arr_index] = NULL;
+ spin_unlock(&nss_mirror_arr_lock);
+}
+
+/*
+ * nss_mirror_arr_add()
+ * API to add mirror netdev in mirror array.
+ */
+static int nss_mirror_arr_add(struct net_device *dev)
+{
+ uint8_t i;
+
+ spin_lock(&nss_mirror_arr_lock);
+ for (i = 0; i < NSS_MAX_MIRROR_DYNAMIC_INTERFACES; i++) {
+ struct nss_mirror_instance_priv *mirror_priv;
+
+ if (nss_mirror_arr[i] != NULL) {
+ continue;
+ }
+
+ /*
+ * Add interface in mirror array.
+ */
+ nss_mirror_arr[i] = dev;
+ mirror_priv = netdev_priv(dev);
+ mirror_priv->mirror_arr_index = i;
+ spin_unlock(&nss_mirror_arr_lock);
+ return 0;
+ }
+ spin_unlock(&nss_mirror_arr_lock);
+ return -1;
+}
+
+/*
+ * nss_mirror_display_info()
+ * API to display configure information for the given mirror device.
+ */
+void nss_mirror_display_info(struct net_device *mirror_dev)
+{
+ struct nss_mirror_instance_priv *mirror_priv = netdev_priv(mirror_dev);
+
+ pr_info("Mirror interface:%s info:\n", mirror_dev->name);
+
+ pr_info("mirror interface number: %d\n"
+ "mirror configure: mirror size: %d, mirror point: %d, mirror offset: %d\n"
+ "rule config mode: %d\n",
+ mirror_priv->mirror_instance_if_num, mirror_priv->mirror_size,
+ mirror_priv->mirror_point, mirror_priv->mirror_offset,
+ mirror_priv->rule_config_mode);
+}
+
+/*
+ * nss_mirror_display_all_info()
+ * API to display configure information for all mirror devices.
+ */
+void nss_mirror_display_all_info(void)
+{
+ int i;
+ struct net_device *mirror_dev;
+
+ spin_lock(&nss_mirror_arr_lock);
+ for (i = 0; i < NSS_MAX_MIRROR_DYNAMIC_INTERFACES; i++) {
+
+ if (nss_mirror_arr[i] == NULL) {
+ continue;
+ }
+
+ mirror_dev = nss_mirror_arr[i];
+ nss_mirror_display_info(mirror_dev);
+ }
+ spin_unlock(&nss_mirror_arr_lock);
+}
+
+/*
+ * nss_mirror_netdev_up()
+ * API to make the net device up.
+ */
+static int nss_mirror_netdev_up(struct net_device *dev)
+{
+ netif_start_queue(dev);
+ return 0;
+}
+
+/*
+ * nss_mirror_netdev_down()
+ * API to make the net device down.
+ */
+static int nss_mirror_netdev_down(struct net_device *dev)
+{
+ netif_stop_queue(dev);
+ return 0;
+}
+
+/*
+ * nss_mirror_get_stats()
+ * API to get statistics for mirror interface.
+ */
+static struct rtnl_link_stats64 *nss_mirror_get_stats(struct net_device *dev, struct rtnl_link_stats64 *stats)
+{
+ struct nss_mirror_instance_priv *mirror_priv;
+
+ if (!stats) {
+ nss_mirror_warn("%p: Invalid stats parameter.\n", dev);
+ return stats;
+ }
+
+ memset(stats, 0, sizeof(struct rtnl_link_stats64));
+
+ spin_lock_bh(&nss_mirror_stats_lock);
+ if (!dev) {
+ spin_unlock_bh(&nss_mirror_stats_lock);
+ nss_mirror_warn("Invalid netdev.\n");
+ return stats;
+ }
+ dev_hold(dev);
+ mirror_priv = netdev_priv(dev);
+ memcpy(stats, &mirror_priv->stats, sizeof(struct rtnl_link_stats64));
+ dev_put(dev);
+ spin_unlock_bh(&nss_mirror_stats_lock);
+
+ return stats;
+}
+
+/*
+ * nss_mirror_netdev_ops
+ * Mirror net device operations.
+ */
+static const struct net_device_ops nss_mirror_netdev_ops = {
+ .ndo_open = nss_mirror_netdev_up,
+ .ndo_stop = nss_mirror_netdev_down,
+ .ndo_get_stats64 = nss_mirror_get_stats,
+};
+
+/*
+ * nss_mirror_netdev_setup()
+ * Setup the mirror net device.
+ */
+static void nss_mirror_netdev_setup(struct net_device *dev)
+{
+ dev->addr_len = 0;
+ dev->flags = IFF_NOARP;
+ dev->features = NETIF_F_FRAGLIST;
+ dev->netdev_ops = &nss_mirror_netdev_ops;
+}
+
+/*
+ * nss_mirror_stats_update()
+ * API to update mirror interface statistics.
+ */
+static void nss_mirror_stats_update(struct net_device *mirror_dev, struct nss_mirror_stats_sync_msg *stats)
+{
+ struct rtnl_link_stats64 *netdev_stats;
+ struct nss_mirror_instance_priv *mirror_priv;
+ uint64_t dropped = 0;
+
+ spin_lock_bh(&nss_mirror_stats_lock);
+ if (!mirror_dev) {
+ spin_unlock_bh(&nss_mirror_stats_lock);
+ nss_mirror_warn("Invalid mirror interface\n");
+ return;
+ }
+
+ dev_hold(mirror_dev);
+ mirror_priv = netdev_priv(mirror_dev);
+ netdev_stats = &mirror_priv->stats;
+
+ dropped += nss_cmn_rx_dropped_sum(&stats->node_stats);
+
+ /*
+ * TODO: Currently mirror stat tx_send fail is used by NSS firmware
+ * even after sending the packet to the host.
+ * So, at present, this stat cannot be used as drop stats by this API.
+ * In future, if we need to add this stat too in mirror interface
+ * drop stats, then NSS firmware should not use this stat after sending
+ * the packets to the host and should use some new stat for post mirrored
+ * errors (errors which may occur after sending the packet to the host).
+ */
+ dropped += stats->mirror_stats.mem_alloc_fail;
+ dropped += stats->mirror_stats.copy_fail;
+
+ /*
+ * There will not be any tx stats for mirror interface, as mirror interface
+ * is intented to receive mirrored packets only.
+ */
+ netdev_stats->rx_packets += stats->mirror_stats.mirror_pkts;
+ netdev_stats->rx_bytes += stats->mirror_stats.mirror_bytes;
+ netdev_stats->rx_dropped += dropped;
+ dev_put(mirror_dev);
+ spin_unlock_bh(&nss_mirror_stats_lock);
+}
+
+/*
+ * nss_mirror_event_cb()
+ * Event callback.
+ */
+static void nss_mirror_event_cb(void *if_ctx, struct nss_cmn_msg *ncm)
+{
+ struct net_device *netdev = if_ctx;
+ struct nss_mirror_msg *nim = (struct nss_mirror_msg *)ncm;
+
+ switch (ncm->type) {
+ case NSS_MIRROR_MSG_SYNC_STATS:
+ nss_mirror_stats_update(netdev, &nim->msg.stats);
+ break;
+
+ default:
+ nss_mirror_warn("%p: Unknown Event from NSS\n", netdev);
+ break;
+ }
+}
+
+/*
+ * nss_mirror_data_cb()
+ * Data callback.
+ */
+static void nss_mirror_data_cb(struct net_device *netdev, struct sk_buff *skb, struct napi_struct *napi)
+{
+ if (skb && skb->data) {
+ nss_mirror_info("Printing 64 bytes of data\n");
+ print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 16, 1,
+ skb->data, 64, 0);
+ }
+
+ dev_hold(netdev);
+
+ /*
+ * Free the packet if mirror device is not up.
+ */
+ if (!(netdev->flags & IFF_UP)) {
+ kfree_skb(skb);
+ dev_put(netdev);
+ return;
+ }
+
+ skb->dev = netdev;
+ skb->pkt_type = PACKET_HOST;
+ skb->skb_iif = netdev->ifindex;
+ skb_reset_mac_header(skb);
+ netif_receive_skb(skb);
+
+ dev_put(netdev);
+}
+
+/*
+ * nss_mirror_disable()
+ * API to send disable message to mirror interface in NSS firmware.
+ */
+int nss_mirror_disable(struct net_device *mirror_dev)
+{
+ struct nss_mirror_msg nmm = {0};
+ struct nss_ctx_instance *nss_ctx;
+ const struct net_device_ops *ops;
+ nss_tx_status_t status;
+ int32_t if_num, ret;
+
+ if (!mirror_dev) {
+ nss_mirror_warn("Invalid input mirror interface\n");
+ return -1;
+ }
+
+ /*
+ * Fetch the mirror netdevice.
+ */
+ if ((if_num = nss_cmn_get_interface_number_by_dev_and_type(mirror_dev, NSS_DYNAMIC_INTERFACE_TYPE_MIRROR)) < 0) {
+ nss_mirror_warn("No valid NSS FW interface for %s device\n", mirror_dev->name);
+ return -1;
+ }
+
+ nss_ctx = nss_mirror_get_context();
+ if (!nss_ctx) {
+ nss_mirror_warn("Invalid NSS context\n");
+ return -1;
+ }
+
+ nss_cmn_msg_init(&nmm.cm, if_num, NSS_MIRROR_MSG_DISABLE, 0, NULL, NULL);
+
+ status = nss_mirror_tx_msg_sync(nss_ctx, &nmm);
+ if (status != NSS_TX_SUCCESS) {
+ nss_mirror_warn("Sending disable config to %d mirror interface failed\n", if_num);
+ return -1;
+ }
+
+ /*
+ * Bring down the mirror netdev.
+ */
+ ops = mirror_dev->netdev_ops;
+ if (ops->ndo_stop) {
+ ret = ops->ndo_stop(mirror_dev);
+ if (ret) {
+ nss_mirror_warn("%s device stop function failed: %d\n", mirror_dev->name, ret);
+ return -1;
+ }
+ mirror_dev->flags &= ~IFF_UP;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(nss_mirror_disable);
+
+/*
+ * nss_mirror_enable()
+ * API to send enable message to mirror interface in NSS firmware.
+ */
+int nss_mirror_enable(struct net_device *mirror_dev)
+{
+ struct nss_mirror_msg nmm = {0};
+ struct nss_ctx_instance *nss_ctx;
+ const struct net_device_ops *ops;
+ nss_tx_status_t status;
+ int32_t if_num, ret;
+
+ if (!mirror_dev) {
+ nss_mirror_warn("Invalid input mirror interface\n");
+ return -1;
+ }
+
+ /*
+ * Verify the mirror netdevice.
+ */
+ if ((if_num = nss_cmn_get_interface_number_by_dev_and_type(mirror_dev, NSS_DYNAMIC_INTERFACE_TYPE_MIRROR)) < 0) {
+ nss_mirror_warn("No valid NSS FW interface for %s device\n", mirror_dev->name);
+ return -1;
+ }
+
+ nss_ctx = nss_mirror_get_context();
+ if (!nss_ctx) {
+ nss_mirror_warn("Invalid NSS context\n");
+ return -1;
+ }
+
+ nss_cmn_msg_init(&nmm.cm, if_num, NSS_MIRROR_MSG_ENABLE, 0, NULL, NULL);
+
+ status = nss_mirror_tx_msg_sync(nss_ctx, &nmm);
+ if (status != NSS_TX_SUCCESS) {
+ nss_mirror_warn("Sending enable config to %d mirror interface failed\n", if_num);
+ return -1;
+ }
+
+ /*
+ * Bring up the mirror netdev.
+ */
+ ops = mirror_dev->netdev_ops;
+ if (ops->ndo_open) {
+ ret = ops->ndo_open(mirror_dev);
+ if (ret) {
+ nss_mirror_warn("%s device open function failed: %d\n", mirror_dev->name, ret);
+ return -1;
+ }
+ mirror_dev->flags |= IFF_UP;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(nss_mirror_enable);
+
+/*
+ * nss_mirror_set_nexthop()
+ * API to send set nexthop message to mirror interface in NSS firmware.
+ */
+int nss_mirror_set_nexthop(struct net_device *mirror_dev, int32_t mirror_next_hop)
+{
+ struct nss_mirror_msg nmm = {0};
+ struct nss_ctx_instance *nss_ctx;
+ struct nss_mirror_instance_priv *mirror_priv;
+ int32_t mirror_if_num;
+ nss_tx_status_t status;
+
+ if (!mirror_dev) {
+ nss_mirror_warn("Invalid input mirror interface\n");
+ return -1;
+ }
+
+ /*
+ * Validate the nexthop argument.
+ */
+ if (mirror_next_hop >= NSS_MAX_NET_INTERFACES) {
+ nss_mirror_warn("Invalid mirror next hop interface:%d\n", mirror_next_hop);
+ return -1;
+ }
+
+ /*
+ * Fetch the mirror netdevice.
+ */
+ if ((mirror_if_num = nss_cmn_get_interface_number_by_dev_and_type(mirror_dev, NSS_DYNAMIC_INTERFACE_TYPE_MIRROR)) < 0) {
+ nss_mirror_warn("No valid NSS FW interface for %s device\n", mirror_dev->name);
+ return -1;
+ }
+
+ /*
+ * Return if the mirror interface is in open state.
+ */
+ if ((mirror_dev->flags & IFF_UP)) {
+ nss_mirror_warn("%s mirror interface is in open state\n", mirror_dev->name);
+ return -1;
+ }
+
+ mirror_priv = netdev_priv(mirror_dev);
+ if (mirror_priv->rule_config_mode == NSS_MIRROR_MODE_INGRESS_PMC) {
+ nss_mirror_warn("Nexthop already configured for %s mirror device\n", mirror_dev->name);
+ return -1;
+ }
+
+ nss_ctx = nss_mirror_get_context();
+ if (!nss_ctx) {
+ nss_mirror_warn("Invalid NSS context\n");
+ return -1;
+ }
+
+ nss_cmn_msg_init(&nmm.cm, mirror_if_num, NSS_MIRROR_MSG_SET_NEXTHOP, 0, NULL, NULL);
+ nmm.msg.nexthop.if_num = mirror_next_hop;
+
+ status = nss_mirror_tx_msg_sync(nss_ctx, &nmm);
+ if (status != NSS_TX_SUCCESS) {
+ nss_mirror_warn("Sending set nexthop config to %d mirror interface failed"
+ " Nexthop: %d\n", mirror_if_num, mirror_next_hop);
+ return -1;
+ }
+
+ /*
+ * Update the configure mode in mirror netdev.
+ */
+ mirror_priv->rule_config_mode = NSS_MIRROR_MODE_INGRESS_PMC;
+
+ return 0;
+}
+EXPORT_SYMBOL(nss_mirror_set_nexthop);
+
+/*
+ * nss_mirror_reset_nexthop()
+ * API to send reset nexthop message to mirror interface in NSS firmware.
+ */
+int nss_mirror_reset_nexthop(struct net_device *mirror_dev)
+{
+ struct nss_mirror_msg nmm = {0};
+ struct nss_ctx_instance *nss_ctx;
+ struct nss_mirror_instance_priv *mirror_priv;
+ nss_tx_status_t status;
+ int32_t if_num;
+
+ if (!mirror_dev) {
+ nss_mirror_warn("Invalid input mirror interface\n");
+ return -1;
+ }
+
+ /*
+ * Verify the mirror netdevice.
+ */
+ if ((if_num = nss_cmn_get_interface_number_by_dev_and_type(mirror_dev, NSS_DYNAMIC_INTERFACE_TYPE_MIRROR)) < 0) {
+ nss_mirror_warn("No valid NSS FW interface for %s device\n", mirror_dev->name);
+ return -1;
+ }
+
+ mirror_priv = netdev_priv(mirror_dev);
+ if (mirror_priv->rule_config_mode != NSS_MIRROR_MODE_INGRESS_PMC) {
+ nss_mirror_warn("Invalid request for %s mirror device\n", mirror_dev->name);
+ return -1;
+ }
+
+ nss_ctx = nss_mirror_get_context();
+ if (!nss_ctx) {
+ nss_mirror_warn("Invalid NSS context\n");
+ return -1;
+ }
+
+ nss_cmn_msg_init(&nmm.cm, if_num, NSS_MIRROR_MSG_RESET_NEXTHOP, 0, NULL, NULL);
+
+ status = nss_mirror_tx_msg_sync(nss_ctx, &nmm);
+ if (status != NSS_TX_SUCCESS) {
+ nss_mirror_warn("Sending reset nexthop config to %d mirror interface failed\n", if_num);
+ return -1;
+ }
+
+ /*
+ * Reset the mirror configuration.
+ */
+ mirror_priv->rule_config_mode = NSS_MIRROR_MODE_NONE;
+
+ return 0;
+}
+EXPORT_SYMBOL(nss_mirror_reset_nexthop);
+
+/*
+ * nss_mirror_configure()
+ * API to send configure message to mirror interface in NSS firmware.
+ */
+int nss_mirror_configure(struct net_device *mirror_dev, enum nss_mirror_pkt_clone_point pkt_clone_point,
+ uint16_t pkt_clone_size, uint16_t pkt_clone_offset)
+{
+ struct nss_mirror_msg nmm = {0};
+ struct nss_ctx_instance *nss_ctx;
+ struct nss_mirror_instance_priv *mirror_priv;
+ nss_tx_status_t status;
+ int32_t if_num;
+
+ if (!mirror_dev) {
+ nss_mirror_warn("Invalid input mirror interface\n");
+ return -1;
+ }
+
+ /*
+ * Validate clone point parameter.
+ */
+ if (!pkt_clone_point || (pkt_clone_point >= NSS_MIRROR_PKT_CLONE_POINT_MAX)) {
+ nss_mirror_warn("Invalid clone point value: %d for device %s\n", pkt_clone_point, mirror_dev->name);
+ return -1;
+ }
+
+ /*
+ * Fetch the mirror netdevice.
+ */
+ if ((if_num = nss_cmn_get_interface_number_by_dev_and_type(mirror_dev, NSS_DYNAMIC_INTERFACE_TYPE_MIRROR)) < 0) {
+ nss_mirror_warn("No valid NSS FW interface for %s device\n", mirror_dev->name);
+ return -1;
+ }
+
+ nss_ctx = nss_mirror_get_context();
+ if (!nss_ctx) {
+ nss_mirror_warn("Invalid NSS context\n");
+ return -1;
+ }
+
+ nss_cmn_msg_init(&nmm.cm, if_num, NSS_MIRROR_MSG_CONFIGURE,
+ sizeof(struct nss_mirror_configure_msg), NULL, NULL);
+ nmm.msg.config.pkt_clone_size = pkt_clone_size;
+ nmm.msg.config.pkt_clone_point = pkt_clone_point;
+ nmm.msg.config.pkt_clone_offset = pkt_clone_offset;
+
+ nss_mirror_trace("pkt_clone_size : %d pkt_clone_point %d pkt_clone_offset %d\n", nmm.msg.config.pkt_clone_size, nmm.msg.config.pkt_clone_point, nmm.msg.config.pkt_clone_offset);
+
+ status = nss_mirror_tx_msg_sync(nss_ctx, &nmm);
+ if (status != NSS_TX_SUCCESS) {
+ nss_mirror_warn("Sending create config to %d mirror interface failed\n", if_num);
+ return -1;
+ }
+
+ /*
+ * Save the mirror configurations in mirror netdev.
+ */
+ mirror_priv = netdev_priv(mirror_dev);
+ mirror_priv->mirror_point = pkt_clone_point;
+ mirror_priv->mirror_offset = pkt_clone_offset;
+ mirror_priv->mirror_size = pkt_clone_size;
+
+ return 0;
+}
+EXPORT_SYMBOL(nss_mirror_configure);
+
+/*
+ * nss_mirror_reset_if_nexthop()
+ * API to send reset nexthop command to NSS firmware.
+ * TODO: Remove this API and its usage and use NSS driver API, once
+ * similar API is added into NSS driver.
+ */
+nss_tx_status_t nss_mirror_reset_if_nexthop(uint32_t if_num)
+{
+ struct nss_if_msg nim;
+ struct nss_ctx_instance *nss_ctx;
+ nss_tx_status_t status;
+
+ /*
+ * TODO: Use context of the input interface instead of mirror context.
+ */
+ nss_ctx = nss_mirror_get_context();
+ if (!nss_ctx) {
+ nss_mirror_warn("Invalid NSS context\n");
+ return -1;
+ }
+
+ nss_cmn_msg_init(&nim.cm, if_num, NSS_IF_RESET_NEXTHOP, 0, NULL, NULL);
+
+ status = nss_if_tx_msg(nss_ctx, &nim);
+ if (status != NSS_TX_SUCCESS) {
+ nss_mirror_warn("%p: Failed to send reset nexthop message to %d interface\n", nss_ctx, if_num);
+ return status;
+ }
+
+ nss_mirror_info("%p: Reset nexthop message is sent successfully\n", nss_ctx);
+ return status;
+}
+
+/*
+ * nss_mirror_destroy_if()
+ * API to un-register and destroy the mirror instance in NSS firmware.
+ */
+static int nss_mirror_destroy_if(struct net_device *mirror_dev)
+{
+ int32_t if_num;
+
+ /*
+ * Fetch the mirror netdevice.
+ */
+ if ((if_num = nss_cmn_get_interface_number_by_dev_and_type(mirror_dev, NSS_DYNAMIC_INTERFACE_TYPE_MIRROR)) < 0) {
+ nss_mirror_warn("No valid NSS FW interface for %s device\n", mirror_dev->name);
+ return -1;
+ }
+
+ nss_mirror_unregister_if(if_num);
+ if (nss_dynamic_interface_dealloc_node(if_num, NSS_DYNAMIC_INTERFACE_TYPE_MIRROR)
+ != NSS_TX_SUCCESS) {
+ nss_mirror_warn("Failed to dealloc mirror interface instance\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * nss_mirror_netdev_destroy()
+ * API to un-register and free mirror net device.
+ */
+static void nss_mirror_netdev_destroy(struct net_device *mirror_dev)
+{
+ if (!mirror_dev) {
+ nss_mirror_warn("Invalid mirror device pointer\n");
+ return;
+ }
+
+ unregister_netdev(mirror_dev);
+
+ spin_lock_bh(&nss_mirror_stats_lock);
+ free_netdev(mirror_dev);
+ spin_unlock_bh(&nss_mirror_stats_lock);
+}
+
+/*
+ * nss_mirror_destroy()
+ * API to de-register and delete mirror interface.
+ */
+int nss_mirror_destroy(struct net_device *mirror_dev)
+{
+ if (!mirror_dev) {
+ nss_mirror_warn("Invalid input mirror interface\n");
+ return -1;
+ }
+
+ /*
+ * Destroy mirror instance in NSS firmware.
+ */
+ if (nss_mirror_destroy_if(mirror_dev)) {
+ nss_mirror_warn("Error in destroying %s mirror interface from NSS"
+ " firmware\n", mirror_dev->name);
+ return -1;
+ }
+
+ /*
+ * Remove mirror netdev from mirror array.
+ */
+ nss_mirror_arr_del(mirror_dev);
+
+ /*
+ * Destroy the mirror device.
+ */
+ nss_mirror_netdev_destroy(mirror_dev);
+ return 0;
+}
+EXPORT_SYMBOL(nss_mirror_destroy);
+
+/*
+ * nss_mirror_create_if()
+ * Create and register mirror instance in NSS.
+ */
+static int32_t nss_mirror_create_if(struct net_device *dev, nss_mirror_data_callback_t data_callback)
+{
+ struct nss_ctx_instance *nss_ctx;
+ struct nss_mirror_instance_priv *mirror_priv;
+ int32_t if_num, ret;
+ uint32_t features = 0;
+
+ if_num = nss_dynamic_interface_alloc_node(NSS_DYNAMIC_INTERFACE_TYPE_MIRROR);
+ if (if_num < 0) {
+ nss_mirror_warn("Mirror interface creation failed - alloc node failed\n");
+ return -1;
+ }
+
+ nss_ctx = nss_mirror_register_if(if_num,
+ data_callback,
+ nss_mirror_event_cb,
+ dev,
+ features);
+ if (!nss_ctx) {
+ nss_mirror_warn("%d interface registration failed\n", if_num);
+ ret = nss_dynamic_interface_dealloc_node(if_num, NSS_DYNAMIC_INTERFACE_TYPE_MIRROR);
+ if (ret != NSS_TX_SUCCESS) {
+ nss_mirror_warn("%d interface dealloc failed\n", if_num);
+ }
+ return -1;
+ }
+
+ nss_mirror_trace("Alloc status on %d interface\n", if_num);
+
+ /*
+ * Save NSS mirror interface number in mirror netdev.
+ */
+ mirror_priv = netdev_priv(dev);
+ mirror_priv->mirror_instance_if_num = if_num;
+
+ return if_num;
+}
+
+/*
+ * nss_mirror_create()
+ * API to create and register mirror interface.
+ */
+struct net_device *nss_mirror_create(void)
+{
+ struct net_device *dev;
+ char *devname;
+ int32_t mirror_if_num;
+ int ret;
+
+ devname = vzalloc(strlen(NSS_MIRROR_NETDEV_NAME)+3+1);
+ if (!devname) {
+ nss_mirror_warn("memory allocation failed\n");
+ return NULL;
+ }
+
+ snprintf(devname, strlen(NSS_MIRROR_NETDEV_NAME)+3, "%s%d", NSS_MIRROR_NETDEV_NAME, nss_mirror_netdev_count);
+
+ dev = alloc_netdev(sizeof(struct nss_mirror_instance_priv), devname, NET_NAME_UNKNOWN, nss_mirror_netdev_setup);
+ if (!dev) {
+ nss_mirror_warn("netdev allocation failed\n");
+ vfree(devname);
+ return NULL;
+ }
+
+ mirror_if_num = nss_mirror_create_if(dev, nss_mirror_data_cb);
+ if (mirror_if_num < 0) {
+ nss_mirror_warn("mirror interface instance creation failed\n");
+ free_netdev(dev);
+ vfree(devname);
+ return NULL;
+ }
+
+ ret = register_netdev(dev);
+ if (ret) {
+ nss_mirror_warn("netdev registration failed\n");
+ if (nss_mirror_destroy_if(dev)) {
+ nss_mirror_warn("Error in destroying %s mirror device from"
+ " NSS firmware\n", dev->name);
+ }
+ free_netdev(dev);
+ vfree(devname);
+ return NULL;
+ }
+
+ /*
+ * Add the mirror netdev to mirror array.
+ */
+ if (nss_mirror_arr_add(dev)) {
+ nss_mirror_warn("Error in adding %s mirror device in mirror array\n", dev->name);
+ if (nss_mirror_destroy_if(dev)) {
+ nss_mirror_warn("Error in destroying %s mirror device from"
+ " NSS firmware\n", dev->name);
+ }
+ nss_mirror_netdev_destroy(dev);
+ vfree(devname);
+ return NULL;
+ }
+
+ nss_mirror_netdev_count++;
+ return dev;
+}
+EXPORT_SYMBOL(nss_mirror_create);
+
+/*
+ * nss_mirror_deconfigure_mirror()
+ * API to deconfigure mirror device.
+ */
+int nss_mirror_deconfigure_mirror(struct net_device *mirror_dev)
+{
+ struct nss_mirror_instance_priv *mirror_priv = netdev_priv(mirror_dev);
+
+ if (mirror_priv->rule_config_mode == NSS_MIRROR_MODE_NONE) {
+ return 0;
+ }
+
+ if (mirror_priv->rule_config_mode == NSS_MIRROR_MODE_INGRESS_PMC) {
+ if (nss_mirror_reset_nexthop(mirror_dev)) {
+ nss_mirror_warn("Error in sending reset nexthop config to mirror interface: %d\n",
+ mirror_priv->mirror_instance_if_num);
+ return -1;
+ }
+ }
+
+ /*
+ * Reset the mirror mode.
+ */
+ mirror_priv->rule_config_mode = NSS_MIRROR_MODE_NONE;
+ return 0;
+}
+
+/*
+ * nss_mirror_destroy_all()
+ * API to destroy all the configured mirror devices.
+ */
+int nss_mirror_destroy_all(void)
+{
+ int i;
+
+ for (i = 0; i < NSS_MAX_MIRROR_DYNAMIC_INTERFACES; i++) {
+ struct net_device *mirror_dev;
+
+ spin_lock(&nss_mirror_arr_lock);
+ if (nss_mirror_arr[i] == NULL) {
+ spin_unlock(&nss_mirror_arr_lock);
+ continue;
+ }
+
+ mirror_dev = nss_mirror_arr[i];
+ spin_unlock(&nss_mirror_arr_lock);
+
+ /*
+ * Deconfigure mirror interface.
+ */
+ if (nss_mirror_deconfigure_mirror(mirror_dev) < 0) {
+ nss_mirror_warn("Error in deconfiguring mirror interface: %s\n", mirror_dev->name);
+ return -1;
+ }
+
+ if (nss_mirror_destroy(mirror_dev)) {
+ nss_mirror_warn("Error in sending delete config to mirror interface: %s\n", mirror_dev->name);
+ return -1;
+ }
+ }
+ return 0;
+}
diff --git a/mirror/nss_mirror.h b/mirror/nss_mirror.h
new file mode 100644
index 0000000..8f9dc0b
--- /dev/null
+++ b/mirror/nss_mirror.h
@@ -0,0 +1,110 @@
+/*
+ ***************************************************************************
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ ***************************************************************************
+ */
+
+#include <linux/etherdevice.h>
+
+#include <nss_api_if.h>
+#include <nss_cmn.h>
+
+#if (NSS_MIRROR_DEBUG_LEVEL < 1)
+#define nss_mirror_assert(fmt, args...)
+#else
+#define nss_mirror_assert(c) if (!(c)) { BUG_ON(!(c)); }
+#endif /* NSS_MIRROR_DEBUG_LEVEL */
+
+/*
+ * Compile messages for dynamic enable/disable
+ */
+#if defined(CONFIG_DYNAMIC_DEBUG)
+#define nss_mirror_warn(s, ...) pr_debug("%s[%d]:" s, __FUNCTION__, __LINE__, ##__VA_ARGS__)
+#define nss_mirror_info(s, ...) pr_debug("%s[%d]:" s, __FUNCTION__, __LINE__, ##__VA_ARGS__)
+#define nss_mirror_trace(s, ...) pr_debug("%s[%d]:" s, __FUNCTION__, __LINE__, ##__VA_ARGS__)
+#else /* CONFIG_DYNAMIC_DEBUG */
+/*
+ * Statically compile messages at different levels
+ */
+#if (NSS_MIRROR_DEBUG_LEVEL < 2)
+#define nss_mirror_warn(s, ...)
+#else
+#define nss_mirror_warn(s, ...) pr_warn("%s[%d]:" s, __FUNCTION__, __LINE__, ##__VA_ARGS__)
+#endif
+
+#if (NSS_MIRROR_DEBUG_LEVEL < 3)
+#define nss_mirror_info(s, ...)
+#else
+#define nss_mirror_info(s, ...) pr_notice("%s[%d]:" s, __FUNCTION__, __LINE__, ##__VA_ARGS__)
+#endif
+
+#if (NSS_MIRROR_DEBUG_LEVEL < 4)
+#define nss_mirror_trace(s, ...)
+#else
+#define nss_mirror_trace(s, ...) pr_info("%s[%d]:" s, __FUNCTION__, __LINE__, ##__VA_ARGS__)
+#endif
+#endif /* CONFIG_DYNAMIC_DEBUG */
+
+/*
+ * Mirror configure modes.
+ */
+#define NSS_MIRROR_MODE_NONE 0x0
+#define NSS_MIRROR_MODE_INGRESS_PMC 0x1
+
+/*
+ * nss_mirror_instance_priv
+ * Private structure of mirror net device.
+ */
+struct nss_mirror_instance_priv {
+ struct rtnl_link_stats64 stats; /* Netdev stats. */
+ uint32_t mirror_instance_if_num; /* NSS interface number for this mirror device. */
+ enum nss_mirror_pkt_clone_point mirror_point; /* Point in the packet to copy from. */
+ uint16_t mirror_size; /* Number of bytes to copy. */
+ uint16_t mirror_offset; /* Copy offset. */
+ uint8_t rule_config_mode; /* Configure mode. */
+ uint8_t mirror_arr_index; /* Netdev index in mirror array. */
+};
+
+/*
+ * nss_mirror_display_info()
+ * API to display configure information for the given mirror device.
+ */
+extern void nss_mirror_display_info(struct net_device *mirror_dev);
+
+/*
+ * nss_mirror_display_all_info()
+ * API to display configure information for all mirror devices.
+ */
+extern void nss_mirror_display_all_info(void);
+
+/*
+ * nss_mirror_reset_if_nexthop()
+ * API to send reset nexthop command to NSS firmware.
+ * TODO: Remove this API and its usage and use NSS driver API, once
+ * similar API is added into NSS driver.
+ */
+extern nss_tx_status_t nss_mirror_reset_if_nexthop(uint32_t if_num);
+
+/*
+ * nss_mirror_deconfigure_mirror()
+ * API to deconfigure mirror device.
+ */
+extern int nss_mirror_deconfigure_mirror(struct net_device *mirror_dev);
+
+/*
+ * nss_mirror_destroy_all()
+ * API to destroy all the configured mirror devices.
+ */
+extern int nss_mirror_destroy_all(void);
diff --git a/mirror/nss_mirror_ctl.c b/mirror/nss_mirror_ctl.c
new file mode 100644
index 0000000..9adb250
--- /dev/null
+++ b/mirror/nss_mirror_ctl.c
@@ -0,0 +1,959 @@
+/*
+ ***************************************************************************
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ ***************************************************************************
+ */
+
+#include <linux/sysctl.h>
+#include <linux/string.h>
+
+#include "nss_mirror_public.h"
+#include "nss_mirror.h"
+
+
+#define NSS_MIRROR_CONFIG_PARAM_NUM 3
+#define NSS_MIRROR_SET_NEXTHOP_PARAM_NUM 2
+#define NSS_MIRROR_ENABLE_INGRESS_PMC_NUM 2
+
+/*
+ * nss_mirror_ctl_cmd_type
+ * Mirror command types.
+ */
+enum nss_mirror_ctl_cmd_type {
+ NSS_MIRROR_CMD_UNKNOWN, /* Unknown command. */
+ NSS_MIRROR_CMD_CREATE, /* Create command. */
+ NSS_MIRROR_CMD_CONFIGURE, /* Configure command. */
+ NSS_MIRROR_CMD_SET_NEXTHOP, /* Set nexthop command. */
+ NSS_MIRROR_CMD_RESET_NEXTHOP, /* Reset nexthop command. */
+ NSS_MIRROR_CMD_INGRESS_PMC_ENABLE, /* Ingress promiscuous enable command. */
+ NSS_MIRROR_CMD_INGRESS_PMC_DISABLE, /* Ingress promiscuous disable command. */
+ NSS_MIRROR_CMD_ENABLE, /* Enable command. */
+ NSS_MIRROR_CMD_DISABLE, /* Disable command. */
+ NSS_MIRROR_CMD_DISPLAY, /* Display command. */
+ NSS_MIRROR_CMD_DESTROY, /* Destroy command. */
+ NSS_MIRROR_CMD_HELP, /* Help command. */
+};
+
+/*
+ * Sysctl table header for mirror.
+ */
+static struct ctl_table_header *nss_mirror_ctl_header;
+
+/*
+ * Mirror config data.
+ */
+static unsigned char nss_mirror_config_data[128] __read_mostly;
+
+/*
+ * nss_mirror_ctl_read_nextarg()
+ * API to read the next argument in the command.
+ */
+static char *nss_mirror_ctl_read_nextarg(char **buf_ptr)
+{
+ if (!buf_ptr || !(*buf_ptr)) {
+ nss_mirror_warn("Read Buf is NULL\n");
+ return NULL;
+ }
+
+ return strsep(buf_ptr, " ");
+}
+
+/*
+ * nss_mirror_ctl_read_value()
+ * API to read a value of the param in the command.
+ */
+static char *nss_mirror_ctl_read_value(char **buf_ptr, char **value_ptr, char *delim)
+{
+ *value_ptr = nss_mirror_ctl_read_nextarg(buf_ptr);
+
+ if (!(*value_ptr)) {
+ return NULL;
+ }
+
+ return strsep(value_ptr, delim);
+}
+
+/*
+ * nss_mirror_ctl_convert_char_to_u32()
+ * API to convert character to u32.
+ */
+static int nss_mirror_ctl_convert_char_to_u32(char *buf, uint32_t *arg)
+{
+ int ret;
+
+ /*
+ * Write the tokens to unsigned integer.
+ */
+ ret = sscanf(buf, "%u", arg);
+ if (ret != 1) {
+ nss_mirror_warn("Failed to write the %s token to u32\n", buf);
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * nss_mirror_ctl_convert_char_to_u16()
+ * API to convert character to u16.
+ */
+static int nss_mirror_ctl_convert_char_to_u16(char *buf, uint16_t *arg)
+{
+ int ret;
+
+ /*
+ * Write the tokens to unsigned short integer.
+ */
+ ret = sscanf(buf, "%hu", arg);
+ if (ret != 1) {
+ nss_mirror_warn("Failed to write the %s token to u16\n", buf);
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * nss_mirror_ctl_get_netdev_by_name()
+ * API to get netdev from name.
+ *
+ * Note: Caller is expected to release the hold on the dev.
+ */
+static int nss_mirror_ctl_get_netdev_by_name(char *name, struct net_device **dev)
+{
+ char dev_name[IFNAMSIZ] = {0};
+
+ strlcpy(dev_name, name, IFNAMSIZ);
+ if (dev_name[strlen(dev_name) - 1] == '\n') {
+ dev_name[strlen(dev_name) - 1] = '\0';
+ }
+ nss_mirror_info("Device name: %s\n", dev_name);
+ *dev = dev_get_by_name(&init_net, dev_name);
+ if (!*dev) {
+ nss_mirror_warn("Cannot find %s netdevice\n", dev_name);
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * nss_mirror_ctl_display_help()
+ * API to display help.
+ */
+static void nss_mirror_ctl_display_help(void)
+{
+ pr_info("Usage: \n"
+ "echo create > <REDIRECT PATH>\n"
+ "echo configure mirrordev=<mirror dev name> [mirror_size=<MIRROR SIZE>]"
+ " [mirror_point=<MIRROR POINT>] [mirror_offset=<MIRROR OFFSET>] > <REDIRECT PATH>\n"
+ "echo enable mirrordev=<mirror dev name> > <REDIRECT PATH>\n"
+ "echo disable mirrordev=<mirror dev name> > <REDIRECT PATH>\n"
+ "echo set_nexthop mirrordev=<mirror dev name> mirror_next_hop=n2h/eth_rx\n > <REDIRECT PATH>"
+ "echo reset_nexthop mirrordev=<mirror dev name> > <REDIRECT PATH>\n"
+ "echo ingress_pmc_enable devname=<eth/ath dev name> mirrordev=<mirror dev name> > <REDIRECT PATH>\n"
+ "echo ingress_pmc_disable devname=<eth/ath dev name> > <REDIRECT PATH>\n"
+ "echo display [mirrordev=<dev name> / all] > <REDIRECT PATH>\n"
+ "echo destroy mirrordev=<mirror dev name> > <REDIRECT PATH>\n"
+ "Where,\n"
+ "\t<REDIRECT PATH> = /proc/sys/dev/nss/mirror/config\n"
+ );
+}
+
+/*
+ * nss_mirror_ctl_parse_reset_nexthop_cmd()
+ * API to parse reset nexthop command.
+ */
+static int nss_mirror_ctl_parse_reset_nexthop_cmd(char *buffer)
+{
+ struct net_device *mirror_dev;
+ char *param, *value;
+
+ param = nss_mirror_ctl_read_value(&buffer, &value, "=");
+ if (!param || !value) {
+ return -1;
+ }
+
+ if (strcmp(param, "mirrordev")) {
+ nss_mirror_warn("Invalid param: %s in promiscuous enable cmd\n", value);
+ return -1;
+ }
+
+ if (nss_mirror_ctl_get_netdev_by_name(value, &mirror_dev) < 0) {
+ nss_mirror_warn("Error in getting mirror net device.\n");
+ return -1;
+ }
+
+ if (nss_mirror_reset_nexthop(mirror_dev)) {
+ nss_mirror_warn("Error in sending reset nexthop config to mirror interface: %s\n", mirror_dev->name);
+ dev_put(mirror_dev);
+ return -1;
+ }
+
+ dev_put(mirror_dev);
+ return 0;
+}
+
+/*
+ * nss_mirror_ctl_parse_set_nexthop_cmd()
+ * API to parse set nexthop command.
+ */
+static int nss_mirror_ctl_parse_set_nexthop_cmd(char *buffer)
+{
+ struct net_device *mirror_dev = NULL;
+ char *param, *value;
+ uint32_t nexthop_if_num;
+ uint8_t param_num = NSS_MIRROR_SET_NEXTHOP_PARAM_NUM;
+
+ do {
+ param = nss_mirror_ctl_read_value(&buffer, &value, "=");
+ if (!param || !value) {
+ return -1;
+ }
+
+ if (!strcmp(param, "mirrordev")) {
+ if (nss_mirror_ctl_get_netdev_by_name(value, &mirror_dev) < 0) {
+ nss_mirror_warn("Error in getting mirror net device.\n");
+ return -1;
+ }
+ } else if (!strcmp(param, "mirror_next_hop")) {
+ if (value[strlen(value) - 1] == '\n') {
+ value[strlen(value) - 1] = '\0';
+ }
+ if (!strcmp(value, "eth_rx")) {
+ nexthop_if_num = NSS_ETH_RX_INTERFACE;
+ } else if (!strcmp(value, "n2h")) {
+ nexthop_if_num = NSS_N2H_INTERFACE;
+ } else {
+ if (mirror_dev) {
+ dev_put(mirror_dev);
+ }
+ nss_mirror_warn("Invalid mirror next hop value %s\n", value);
+ return -1;
+ }
+ } else {
+ if (mirror_dev) {
+ dev_put(mirror_dev);
+ }
+ nss_mirror_warn("Invalid param: %s in promiscuous enable cmd\n", value);
+ return -1;
+ }
+ } while (--param_num);
+
+ if (nss_mirror_set_nexthop(mirror_dev, nexthop_if_num)) {
+ nss_mirror_warn("Error in sending set nexthop config to mirror interface: %s\n", mirror_dev->name);
+ dev_put(mirror_dev);
+ return -1;
+ }
+
+ dev_put(mirror_dev);
+ return 0;
+}
+
+/*
+ * nss_mirror_ctl_parse_disable_cmd()
+ * API to parse disable command.
+ */
+static int nss_mirror_ctl_parse_disable_cmd(char *buffer)
+{
+ struct net_device *mirror_dev;
+ char *param, *value;
+
+ param = nss_mirror_ctl_read_value(&buffer, &value, "=");
+ if (!param || !value) {
+ return -1;
+ }
+
+ if (strcmp(param, "mirrordev")) {
+ nss_mirror_warn("Invalid parameter %s in disable cmd\n", param);
+ return -1;
+ }
+
+ if (nss_mirror_ctl_get_netdev_by_name(value, &mirror_dev) < 0) {
+ nss_mirror_warn("Error in getting mirror net device.\n");
+ return -1;
+ }
+
+ if (nss_mirror_disable(mirror_dev)) {
+ nss_mirror_warn("Error in sending disable config to mirror interface: %s\n", mirror_dev->name);
+ dev_put(mirror_dev);
+ return -1;
+ }
+
+ dev_put(mirror_dev);
+ return 0;
+}
+
+/*
+ * nss_mirror_ctl_parse_enable_cmd()
+ * API to parse enable command.
+ */
+static int nss_mirror_ctl_parse_enable_cmd(char *buffer)
+{
+ struct net_device *mirror_dev;
+ char *param, *value;
+
+ param = nss_mirror_ctl_read_value(&buffer, &value, "=");
+ if (!param || !value) {
+ return -1;
+ }
+
+ if (strcmp(param, "mirrordev")) {
+ nss_mirror_warn("Invalid parameter %s in enable cmd\n", param);
+ return -1;
+ }
+
+ if (nss_mirror_ctl_get_netdev_by_name(value, &mirror_dev) < 0) {
+ nss_mirror_warn("Error in getting mirror net device.\n");
+ return -1;
+ }
+
+ if (nss_mirror_enable(mirror_dev)) {
+ nss_mirror_warn("Error in sending enable config to mirror interface: %s\n", mirror_dev->name);
+ dev_put(mirror_dev);
+ return -1;
+ }
+
+ dev_put(mirror_dev);
+ return 0;
+}
+
+/*
+ * nss_mirror_ctl_parse_display_cmd()
+ * API to parse display command.
+ */
+static int nss_mirror_ctl_parse_display_cmd(char *buffer)
+{
+ char *param, *value;
+ char dev_name[IFNAMSIZ] = {0};
+ struct net_device *mirror_dev;
+
+ param = nss_mirror_ctl_read_value(&buffer, &value, "=");
+ if (!param || !value) {
+ return -1;
+ }
+
+ if (strcmp(param, "mirrordev")) {
+ nss_mirror_warn("Invalid param %s in mirror display cmd\n", param);
+ return -1;
+ }
+
+ strlcpy(dev_name, value, IFNAMSIZ);
+ if (dev_name[strlen(dev_name) - 1] == '\n') {
+ dev_name[strlen(dev_name) - 1] = '\0';
+ }
+ nss_mirror_info("dev name: %s\n", dev_name);
+
+ if (!strcmp(dev_name, "all")) {
+ nss_mirror_display_all_info();
+ return 0;
+ }
+
+ mirror_dev = dev_get_by_name(&init_net, dev_name);
+ if (!mirror_dev) {
+ nss_mirror_warn("Cannot find the %s netdevice\n", dev_name);
+ return -1;
+ }
+
+ /*
+ * Verify the mirror netdevice.
+ */
+ if (nss_cmn_get_interface_number_by_dev_and_type(mirror_dev, NSS_DYNAMIC_INTERFACE_TYPE_MIRROR) < 0) {
+ dev_put(mirror_dev);
+ nss_mirror_warn("No valid NSS FW interface for %s device\n", dev_name);
+ return -1;
+ }
+
+ nss_mirror_display_info(mirror_dev);
+
+ dev_put(mirror_dev);
+ return 0;
+}
+
+/*
+ * nss_mirror_ctl_parse_enable_ingress_pmc_cmd()
+ * API to parse ingress promiscuous enable command.
+ */
+static int nss_mirror_ctl_parse_enable_ingress_pmc_cmd(char *buffer)
+{
+ char *param, *value;
+ struct nss_ctx_instance *nss_ctx;
+ struct net_device *dev, *mirror_dev = NULL;
+ nss_tx_status_t status;
+ int32_t if_num, type = 0, mirror_if_num;
+ uint8_t param_num = NSS_MIRROR_ENABLE_INGRESS_PMC_NUM;
+
+ do {
+ param = nss_mirror_ctl_read_value(&buffer, &value, "=");
+ if (!param || !value) {
+ return -1;
+ }
+
+ if (!strcmp(param, "devname")) {
+ if (nss_mirror_ctl_get_netdev_by_name(value, &dev) < 0) {
+ nss_mirror_warn("Error in getting devname net device.\n");
+ if (mirror_dev) {
+ dev_put(mirror_dev);
+ }
+ return -1;
+ }
+
+ if ((if_num = nss_cmn_get_interface_number_by_dev(dev)) < 0) {
+ nss_mirror_warn("No valid NSS FW interface for %s device\n", dev->name);
+ if (mirror_dev) {
+ dev_put(mirror_dev);
+ }
+ dev_put(dev);
+ return -1;
+ }
+ dev_put(dev);
+ } else if (!strcmp(param, "mirrordev")) {
+ if (nss_mirror_ctl_get_netdev_by_name(value, &mirror_dev) < 0) {
+ nss_mirror_warn("Error in getting mirror net device.\n");
+ return -1;
+ }
+
+ /*
+ * Verify the mirror netdevice.
+ */
+ if ((mirror_if_num = nss_cmn_get_interface_number_by_dev_and_type(mirror_dev, NSS_DYNAMIC_INTERFACE_TYPE_MIRROR)) < 0) {
+ nss_mirror_warn("No valid NSS FW interface for %s device\n", mirror_dev->name);
+ dev_put(mirror_dev);
+ return -1;
+ }
+ } else {
+ if (mirror_dev) {
+ dev_put(mirror_dev);
+ }
+ nss_mirror_warn("Invalid param: %s in promiscuous enable cmd\n", value);
+ return -1;
+ }
+ } while (--param_num);
+
+ /*
+ * Return if the mirror interface is in open state.
+ */
+ if (mirror_dev->flags & IFF_UP) {
+ nss_mirror_warn("%s mirror interface is in open state\n", mirror_dev->name);
+ dev_put(mirror_dev);
+ return -1;
+ }
+
+ dev_put(mirror_dev);
+
+ nss_ctx = nss_mirror_get_context();
+ if (!nss_ctx) {
+ nss_mirror_warn("Invalid NSS context\n");
+ return -1;
+ }
+
+ type = nss_dynamic_interface_get_type(nss_ctx, if_num);
+
+ if (type == NSS_DYNAMIC_INTERFACE_TYPE_VAP) {
+ status = nss_wifi_vdev_set_next_hop(nss_wifi_get_context(), if_num, mirror_if_num);
+ } else if (if_num < NSS_MAX_PHYSICAL_INTERFACES) {
+ status = nss_phys_if_set_nexthop(nss_ctx, if_num, mirror_if_num);
+ } else {
+ nss_mirror_warn("Invalid nexthop interface:%d type:%d\n", if_num, type);
+ return -1;
+ }
+
+ if (status != NSS_TX_SUCCESS) {
+ nss_mirror_warn("Setting nexthop failed. if_num: %u, nexthop: %u\n", if_num, mirror_if_num);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * nss_mirror_ctl_parse_disable_ingress_pmc_cmd()
+ * API to parse ingress promiscuous disable command.
+ */
+static int nss_mirror_ctl_parse_disable_ingress_pmc_cmd(char *buffer)
+{
+ char *param, *value;
+ struct nss_ctx_instance *nss_ctx;
+ struct net_device *dev;
+ nss_tx_status_t status;
+ int32_t if_num, type = 0;
+
+ param = nss_mirror_ctl_read_value(&buffer, &value, "=");
+ if (!param || !value) {
+ return -1;
+ }
+
+ if (strcmp(param, "devname")) {
+ nss_mirror_warn("Invalid param: %s in promiscuous enable cmd\n", value);
+ return -1;
+ }
+
+ if (nss_mirror_ctl_get_netdev_by_name(value, &dev) < 0) {
+ nss_mirror_warn("Error in getting devname net device.\n");
+ return -1;
+ }
+
+ if ((if_num = nss_cmn_get_interface_number_by_dev(dev)) < 0) {
+ nss_mirror_warn("No valid NSS FW interface for %s device\n", dev->name);
+ dev_put(dev);
+ return -1;
+ }
+
+ dev_put(dev);
+
+ nss_ctx = nss_mirror_get_context();
+ if (!nss_ctx) {
+ nss_mirror_warn("Invalid NSS context\n");
+ return -1;
+ }
+
+ type = nss_dynamic_interface_get_type(nss_ctx, if_num);
+
+ if (type == NSS_DYNAMIC_INTERFACE_TYPE_VAP) {
+ status = nss_mirror_reset_if_nexthop(if_num);
+ } else if (if_num < NSS_MAX_PHYSICAL_INTERFACES) {
+ status = nss_phys_if_reset_nexthop(nss_ctx, if_num);
+ } else {
+ nss_mirror_warn("Reset nexthop failed for %d interface, type:%d\n", if_num, type);
+ return -1;
+ }
+
+ if (status != NSS_TX_SUCCESS) {
+ nss_mirror_warn("Re-setting nexthop failed for if_num: %u\n", if_num);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * nss_mirror_ctl_parse_destroy_cmd()
+ * API to parse destroy command.
+ */
+static int nss_mirror_ctl_parse_destroy_cmd(char *buffer)
+{
+ struct net_device *mirror_dev;
+ char *param, *value;
+
+ param = nss_mirror_ctl_read_value(&buffer, &value, "=");
+ if (!param || !value) {
+ return -1;
+ }
+
+ if (strcmp(param, "mirrordev")) {
+ nss_mirror_warn("Invalid param %s in mirror delete command\n", param);
+ return -1;
+ }
+
+ if (nss_mirror_ctl_get_netdev_by_name(value, &mirror_dev) < 0) {
+ nss_mirror_warn("Error in getting mirror net device.\n");
+ return -1;
+ }
+
+ /*
+ * Deconfigure mirror interface.
+ */
+ if (nss_mirror_deconfigure_mirror(mirror_dev) < 0) {
+ nss_mirror_warn("Error in deconfiguring mirror interface: %s\n", mirror_dev->name);
+ dev_put(mirror_dev);
+ return -1;
+ }
+
+ dev_put(mirror_dev);
+
+ if (nss_mirror_destroy(mirror_dev)) {
+ nss_mirror_warn("Error in sending delete config to mirror interface: %s\n", mirror_dev->name);
+ dev_put(mirror_dev);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * nss_mirror_ctl_parse_config_param()
+ * API to parse configure parameters.
+ */
+static int nss_mirror_ctl_parse_config_param(char *buffer, uint16_t *size_ptr,
+ enum nss_mirror_pkt_clone_point *pt_ptr, uint16_t *offset_ptr)
+{
+ uint8_t param_num = NSS_MIRROR_CONFIG_PARAM_NUM;
+ char *param, *value;
+
+ do {
+ param = nss_mirror_ctl_read_value(&buffer, &value, "=");
+
+ /*
+ * No configure parameter is given. Use the default configure values.
+ */
+ if (!param || !value) {
+ return 0;
+ }
+
+ if (!strcmp(param, "mirror_size")) {
+ if (nss_mirror_ctl_convert_char_to_u16(value, size_ptr)) {
+ nss_mirror_warn("Not able to parse %s to u16\n", value);
+ return -1;
+ }
+ } else if (!strcmp(param, "mirror_point")) {
+ if (nss_mirror_ctl_convert_char_to_u32(value, pt_ptr)) {
+ nss_mirror_warn("Not able to parse %s to u32\n", value);
+ return -1;
+ }
+ } else if (!strcmp(param, "mirror_offset")) {
+ if (nss_mirror_ctl_convert_char_to_u16(value, offset_ptr)) {
+ nss_mirror_warn("Not able to parse %s to u16\n", value);
+ return -1;
+ }
+ } else {
+ nss_mirror_warn("Invalid param:%s in create cmd\n", param);
+ return -1;
+ }
+ } while (--param_num);
+
+ return 0;
+}
+
+/*
+ * nss_mirror_ctl_parse_configure_cmd()
+ * API to parse configure command.
+ */
+static int nss_mirror_ctl_parse_configure_cmd(char *buffer)
+{
+ struct net_device *mirror_dev;
+ char *param, *value;
+ enum nss_mirror_pkt_clone_point mirror_point = NSS_MIRROR_PKT_CLONE_POINT_DEFAULT;
+ uint16_t mirror_size = 0;
+ uint16_t mirror_offset = 0;
+
+ param = nss_mirror_ctl_read_value(&buffer, &value, "=");
+ if (!param || !value) {
+ return -1;
+ }
+
+ if (strcmp(param, "mirrordev")) {
+ nss_mirror_warn("Invalid param %s in mirror configure command\n", param);
+ return -1;
+ }
+
+ if (nss_mirror_ctl_get_netdev_by_name(value, &mirror_dev) < 0) {
+ nss_mirror_warn("Error in getting mirror net device.\n");
+ return -1;
+ }
+
+ if (nss_mirror_ctl_parse_config_param(buffer, &mirror_size,
+ &mirror_point, &mirror_offset)) {
+ dev_put(mirror_dev);
+ nss_mirror_warn("Error in parsing mirror configure command\n");
+ return -1;
+ }
+
+ nss_mirror_info("Mirror size: %d, mirror point: %d, mirror offset: %d\n",
+ mirror_size, mirror_point, mirror_offset);
+
+ if (nss_mirror_configure(mirror_dev, mirror_point,
+ mirror_size, mirror_offset)) {
+ nss_mirror_warn("Error in sending config message to %s mirror interface\n", mirror_dev->name);
+ dev_put(mirror_dev);
+ return -1;
+ }
+
+ dev_put(mirror_dev);
+ return 0;
+}
+
+/*
+ * nss_mirror_ctl_parse_cmd()
+ * API to parse mirror commands.
+ */
+static int32_t nss_mirror_ctl_parse_cmd(char *cmd)
+{
+ if (cmd == NULL) {
+ return NSS_MIRROR_CMD_UNKNOWN;
+ }
+
+ if (!strcmp(cmd, "create")) {
+ return NSS_MIRROR_CMD_CREATE;
+ }
+
+ if (!strcmp(cmd, "configure")) {
+ return NSS_MIRROR_CMD_CONFIGURE;
+ }
+
+ if (!strcmp(cmd, "enable")) {
+ return NSS_MIRROR_CMD_ENABLE;
+ }
+
+ if (!strcmp(cmd, "disable")) {
+ return NSS_MIRROR_CMD_DISABLE;
+ }
+
+ if (!strcmp(cmd, "display")) {
+ return NSS_MIRROR_CMD_DISPLAY;
+ }
+
+ if (!strcmp(cmd, "destroy")) {
+ return NSS_MIRROR_CMD_DESTROY;
+ }
+
+ if (!strcmp(cmd, "set_nexthop")) {
+ return NSS_MIRROR_CMD_SET_NEXTHOP;
+ }
+
+ if (!strcmp(cmd, "reset_nexthop")) {
+ return NSS_MIRROR_CMD_RESET_NEXTHOP;
+ }
+
+ if (!strcmp(cmd, "ingress_pmc_enable")) {
+ return NSS_MIRROR_CMD_INGRESS_PMC_ENABLE;
+ }
+
+ if (!strcmp(cmd, "ingress_pmc_disable")) {
+ return NSS_MIRROR_CMD_INGRESS_PMC_DISABLE;
+ }
+
+ if (!strcmp(cmd, "help")) {
+ return NSS_MIRROR_CMD_HELP;
+ }
+
+ nss_mirror_warn("Invalid string:%s in command\n", cmd);
+ return NSS_MIRROR_CMD_UNKNOWN;
+}
+
+/*
+ * nss_mirror_ctl_config_handler()
+ * Mirror sysctl config handler.
+ */
+static int nss_mirror_ctl_config_handler(struct ctl_table *ctl, int write,
+ void __user *buf, size_t *lenp, loff_t *ppos)
+{
+ char *buffer, *pfree;
+ char * nextarg;
+ int command, ret;
+ size_t count = *lenp;
+
+ /*
+ * Find the string, return an error if not found
+ */
+ ret = proc_dostring(ctl, write, buf, lenp, ppos);
+ if (ret || !write) {
+ return ret;
+ }
+
+ buffer = vzalloc(count + 1);
+ if (!buffer) {
+ nss_mirror_warn("%p: Dynamic allocation failed for input buffer\n", ctl);
+ return -ENOMEM;
+ }
+
+ pfree = buffer;
+
+ if (copy_from_user(buffer, buf, count)) {
+ vfree(pfree);
+ return -EFAULT;
+ }
+
+ nextarg = nss_mirror_ctl_read_nextarg(&buffer);
+ if (!nextarg) {
+ goto err;
+ }
+
+ if (nextarg[strlen(nextarg) - 1] == '\n') {
+ nextarg[strlen(nextarg) - 1] = '\0';
+ }
+
+ command = nss_mirror_ctl_parse_cmd(nextarg);
+ switch (command) {
+
+ case NSS_MIRROR_CMD_CREATE:
+ {
+ struct net_device *mirror_dev;
+ if (!(mirror_dev = nss_mirror_create())) {
+ nss_mirror_warn("%s Error in create cmd\n", __func__);
+ goto err;
+ }
+ break;
+ }
+
+ case NSS_MIRROR_CMD_CONFIGURE:
+ {
+ if (nss_mirror_ctl_parse_configure_cmd(buffer)) {
+ nss_mirror_warn("Error in parsing configure cmd\n");
+ goto err;
+ }
+ break;
+ }
+
+ case NSS_MIRROR_CMD_SET_NEXTHOP:
+ {
+ if (nss_mirror_ctl_parse_set_nexthop_cmd(buffer)) {
+ nss_mirror_warn("Error in parsing promiscuous rx enable cmd\n");
+ goto err;
+ }
+ break;
+ }
+
+ case NSS_MIRROR_CMD_RESET_NEXTHOP:
+ {
+ if (nss_mirror_ctl_parse_reset_nexthop_cmd(buffer)) {
+ nss_mirror_warn("Error in parsing promiscuous rx disable cmd\n");
+ goto err;
+ }
+ break;
+ }
+
+ case NSS_MIRROR_CMD_INGRESS_PMC_ENABLE:
+ {
+ if (nss_mirror_ctl_parse_enable_ingress_pmc_cmd(buffer)) {
+ nss_mirror_warn("Error in parsing set nexthop cmd\n");
+ goto err;
+ }
+ break;
+ }
+
+ case NSS_MIRROR_CMD_INGRESS_PMC_DISABLE:
+ {
+ if (nss_mirror_ctl_parse_disable_ingress_pmc_cmd(buffer)) {
+ nss_mirror_warn("Error in parsing reset nexthop cmd\n");
+ goto err;
+ }
+ break;
+ }
+
+ case NSS_MIRROR_CMD_DISABLE:
+ {
+ if (nss_mirror_ctl_parse_disable_cmd(buffer)) {
+ nss_mirror_warn("Error in parsing disable cmd\n");
+ goto err;
+ }
+ break;
+ }
+
+ case NSS_MIRROR_CMD_ENABLE:
+ {
+ if (nss_mirror_ctl_parse_enable_cmd(buffer)) {
+ nss_mirror_warn("Error in parsing enable cmd\n");
+ goto err;
+ }
+ break;
+ }
+
+ case NSS_MIRROR_CMD_DISPLAY:
+ {
+ if (nss_mirror_ctl_parse_display_cmd(buffer)) {
+ nss_mirror_warn("Error in parsing display cmd\n");
+ goto err;
+ }
+ break;
+ }
+
+ case NSS_MIRROR_CMD_DESTROY:
+ {
+ if (nss_mirror_ctl_parse_destroy_cmd(buffer)) {
+ nss_mirror_warn("Error in parsing destroy cmd\n");
+ goto err;
+ }
+ break;
+ }
+
+ case NSS_MIRROR_CMD_HELP:
+ {
+ nss_mirror_ctl_display_help();
+ break;
+ }
+
+ default:
+ nss_mirror_warn("Invalid input in command.\n");
+ goto err;
+ }
+
+ vfree(pfree);
+ return ret;
+err:
+ vfree(pfree);
+ return -EINVAL;
+}
+
+/*
+ * nss mirror sysctl table
+ */
+static struct ctl_table nss_mirror_table[] = {
+ {
+ .procname = "config",
+ .data = &nss_mirror_config_data,
+ .maxlen = sizeof(nss_mirror_config_data),
+ .mode = 0644,
+ .proc_handler = &nss_mirror_ctl_config_handler,
+ },
+ { }
+};
+
+/*
+ * nss mirror dir
+ */
+static struct ctl_table nss_mirror_root_dir[] = {
+ {
+ .procname = "mirror",
+ .mode = 0555,
+ .child = nss_mirror_table,
+ },
+ { }
+};
+
+/*
+ * nss mirror sysctl nss root dir
+ */
+static struct ctl_table nss_mirror_nss_root_dir[] = {
+ {
+ .procname = "nss",
+ .mode = 0555,
+ .child = nss_mirror_root_dir,
+ },
+ { }
+};
+
+/*
+ * nss mirror sysctl root dir
+ */
+static struct ctl_table nss_mirror_root[] = {
+ {
+ .procname = "dev",
+ .mode = 0555,
+ .child = nss_mirror_nss_root_dir,
+ },
+ { }
+};
+
+/*
+ * nss_mirror_ctl_register()
+ * Register command line interface for mirror.
+ */
+int nss_mirror_ctl_register(void)
+{
+ nss_mirror_ctl_header = register_sysctl_table(nss_mirror_root);
+ if (!nss_mirror_ctl_header) {
+ nss_mirror_warn("Creating sysctl directory table header for mirror failed\n");
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * nss_mirror_ctl_unregister()
+ * Un-register command line interface for mirror.
+ */
+void nss_mirror_ctl_unregister(void)
+{
+ if (nss_mirror_ctl_header) {
+ unregister_sysctl_table(nss_mirror_ctl_header);
+ }
+}
diff --git a/mirror/nss_mirror_ctl.h b/mirror/nss_mirror_ctl.h
new file mode 100644
index 0000000..0b0c2ec
--- /dev/null
+++ b/mirror/nss_mirror_ctl.h
@@ -0,0 +1,29 @@
+/*
+ ***************************************************************************
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ ***************************************************************************
+ */
+
+/*
+ * nss_mirror_ctl_register()
+ * Register command line interface for mirror.
+ */
+extern int nss_mirror_ctl_register(void);
+
+/*
+ * nss_mirror_ctl_unregister()
+ * Un-register command line interface for mirror.
+ */
+extern void nss_mirror_ctl_unregister(void);
diff --git a/mirror/nss_mirror_init.c b/mirror/nss_mirror_init.c
new file mode 100644
index 0000000..b918fb7
--- /dev/null
+++ b/mirror/nss_mirror_init.c
@@ -0,0 +1,61 @@
+/*
+ ***************************************************************************
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ ***************************************************************************
+ */
+
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/module.h>
+
+#include "nss_mirror_ctl.h"
+#include "nss_mirror.h"
+
+/*
+ * nss_mirror_module_init()
+ * Initialize mirror module.
+ */
+static int __init nss_mirror_module_init(void)
+{
+ int ret = 0;
+
+ ret = nss_mirror_ctl_register();
+ if (ret) {
+ nss_mirror_warn("Mirror ctl registration failed\n");
+ return ret;
+ }
+
+ nss_mirror_info("Mirror module initialized\n");
+ return 0;
+}
+
+/*
+ * nss_mirror_module_exit()
+ * De-initialize mirror module.
+ */
+static void __exit nss_mirror_module_exit(void)
+{
+ if (nss_mirror_destroy_all() < 0) {
+ nss_mirror_warn("Error in destroying all the configured mirror devices\n");
+ }
+ nss_mirror_ctl_unregister();
+ nss_mirror_info("Mirror module unloaded.\n");
+}
+
+module_init(nss_mirror_module_init);
+module_exit(nss_mirror_module_exit);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("NSS MIRROR Client");