[qca-nss-clients] Adding match to support pkg/match.
Change-Id: I48c2709036d5b5360f6dd97d0603681ee596ccd2
Signed-off-by: Suruchi Suman <surusuma@codeaurora.org>
diff --git a/Makefile b/Makefile
index a7b50ec..be58e54 100644
--- a/Makefile
+++ b/Makefile
@@ -33,6 +33,7 @@
obj-$(ovpn-mgr)+= openvpn/
obj-$(eogremgr)+= eogremgr/
obj-$(clmapmgr)+= clmapmgr/
+obj-$(match)+= match/
#NSS NETLINK
obj-$(netlink)+= netlink/
diff --git a/exports/nss_match_user.h b/exports/nss_match_user.h
new file mode 100644
index 0000000..e59f3a5
--- /dev/null
+++ b/exports/nss_match_user.h
@@ -0,0 +1,115 @@
+/*
+ **************************************************************************
+ * 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_match_get_ifnum_by_table_index
+ * Returns match interface number given table index.
+ *
+ * @param[in] table_id Table ID of match instance.
+ *
+ * @return
+ * Interface number of match instance, return -1 on failure.
+ */
+extern int nss_match_get_ifnum_by_table_id(uint32_t table_id);
+
+/**
+ * nss_match_vow_rule_add
+ * Add the VoW rule to the configured match instance.
+ *
+ * @datatype
+ * nss_ctx_instance \n
+ * nss_match_rule_vow_msg
+ *
+ * @param[in] nss_ctx Pointer to NSS core context.
+ * @param[in] rule_msg Pointer to VoW rule message.
+ * @param[in] table_id Table ID of match instance.
+ *
+ * @return
+ * Rule ID of rule added, returns -1 on failure.
+ */
+extern int nss_match_vow_rule_add(struct nss_ctx_instance *nss_ctx, struct nss_match_rule_vow_msg *rule_msg, uint32_t table_id);
+
+/**
+ * nss_match_l2_rule_add
+ * Add the l2 rule to the configured match instance.
+ *
+ * @datatype
+ * nss_ctx_instance \n
+ * nss_match_rule_l2_msg
+ *
+ * @param[in] nss_ctx Pointer to NSS core context.
+ * @param[in] rule_msg Pointer to L2 rule message.
+ * @param[in] table_id Table ID of match instance.
+ *
+ * @return
+ * Rule ID of rule added, returns -1 on failure.
+ */
+extern int nss_match_l2_rule_add(struct nss_ctx_instance *nss_ctx, struct nss_match_rule_l2_msg *rule_msg, uint32_t table_id);
+
+/**
+ * nss_match_rule_delete
+ * Deletes the rule from match instance.
+ *
+ * @datatype
+ * nss_ctx_instance
+ *
+ * @param[in] nss_ctx Pointer to NSS core context.
+ * @param[in] rule_id Rule ID of match rule.
+ * @param[in] table_id Table ID of match instance.
+ *
+ * @return
+ * Error status of rule deletion.
+ */
+extern nss_match_status_t nss_match_rule_delete(struct nss_ctx_instance *nss_ctx, uint32_t rule_id, uint32_t table_id);
+
+/**
+ * nss_match_profile_configure
+ * Configures the match instance.
+ *
+ * @datatype
+ * nss_ctx_instance \n
+ * nss_match_profile_configure_msg
+ *
+ * @param[in] nss_ctx Pointer to NSS core context.
+ * @param[in] config_msg Match message for configuration.
+ * @param[in] table_id Table ID of match instance.
+ *
+ * @return
+ * Error status of configuration.
+ */
+extern nss_match_status_t nss_match_profile_configure(struct nss_ctx_instance *nss_ctx, struct nss_match_profile_configure_msg *config_msg, uint32_t table_id);
+
+/**
+ * nss_match_instance_create
+ * Creates the match instance.
+ *
+ * @return
+ * Table ID of match instance created, returns -1 on failure.
+ */
+extern int32_t nss_match_instance_create(void);
+
+/**
+ * nss_match_instance_destroy
+ * Deletes the match instance.
+ *
+ * @param[in] table_id Table ID of match instance.
+ *
+ * @return
+ * Error status of deletion.
+ */
+extern nss_match_status_t nss_match_instance_destroy(uint32_t table_id);
diff --git a/match/Makefile b/match/Makefile
new file mode 100644
index 0000000..5cb9b63
--- /dev/null
+++ b/match/Makefile
@@ -0,0 +1,11 @@
+# Makefile for match
+ccflags-y := -I$(obj)/../exports -I$(obj)/.. \
+ -DNSS_CLIENT_BUILD_ID="$(BUILD_ID)" -DNSS_MATCH_DEBUG_LEVEL=2 -Wall -Werror
+
+obj-m += qca-nss-match.o
+qca-nss-match-objs := nss_match.o
+qca-nss-match-objs += nss_match_cmd.o
+qca-nss-match-objs += nss_match_db.o
+qca-nss-match-objs += nss_match_l2.o
+qca-nss-match-objs += nss_match_stats.o
+qca-nss-match-objs += nss_match_vow.o
diff --git a/match/nss_match.c b/match/nss_match.c
new file mode 100644
index 0000000..493b088
--- /dev/null
+++ b/match/nss_match.c
@@ -0,0 +1,465 @@
+/*
+ ***************************************************************************
+ * 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_match.c
+ */
+
+#include "nss_match_cmd.h"
+#include "nss_match_db.h"
+#include "nss_match_vow.h"
+#include "nss_match_l2.h"
+#include "nss_match_priv.h"
+#include <linux/types.h>
+#include <nss_api_if.h>
+#include <linux/debugfs.h>
+
+/*
+ * nss_match_verify_config_msg()
+ * Verify configuration message.
+ */
+static nss_match_status_t nss_match_verify_config_msg(struct nss_match_profile_configure_msg *config_msg)
+{
+ int mask, valid_mask_word;
+ int index, invalid_word_count = 0;
+
+ switch (config_msg->profile_type) {
+ case NSS_MATCH_PROFILE_TYPE_VOW:
+ valid_mask_word = 1;
+ break;
+ case NSS_MATCH_PROFILE_TYPE_L2:
+ valid_mask_word = 4;
+ break;
+ default:
+ nss_match_warn("Invalid profile type: %d.\n", config_msg->profile_type);
+ return NSS_MATCH_ERROR_PROFILE_CONFIG_INVALID;
+ }
+
+ for (mask = 0; mask < NSS_MATCH_MASK_MAX; mask++) {
+ if ((config_msg->valid_mask_flag) & (1 << mask)) {
+ for (index = 0; index < valid_mask_word; index++) {
+ if (!config_msg->maskset[mask][index]) {
+ invalid_word_count++;
+ }
+ }
+
+ /*
+ * If all words of mask is null.
+ */
+ if (invalid_word_count == valid_mask_word) {
+ nss_match_warn("Invalid mask for valid_mask_flag: %d\n", config_msg->valid_mask_flag);
+ return NSS_MATCH_ERROR_PROFILE_CONFIG_INVALID;
+ }
+ }
+ }
+
+ return NSS_MATCH_SUCCESS;
+}
+
+/*
+ * nss_match_sync_callback()
+ * Sync callback for syncing stats.
+ */
+static void nss_match_sync_callback(void *app_data, struct nss_match_msg *nmm)
+{
+ struct nss_ctx_instance *nss_ctx = nss_match_get_context();
+
+ switch (nmm->cm.type) {
+ case NSS_MATCH_STATS_SYNC:
+ nss_match_stats_table_sync(nss_ctx, &nmm->msg.stats, nmm->cm.interface);
+ return;
+ default:
+ nss_match_warn("%p: Unknown Event from NSS", nmm);
+ return;
+ }
+}
+
+/*
+ * nss_match_rule_delete()
+ * User API to delete the match rule.
+ */
+nss_match_status_t nss_match_rule_delete(struct nss_ctx_instance *nss_ctx, uint32_t rule_id, uint32_t table_id)
+{
+ struct nss_match_msg matchm;
+ uint32_t profile_type, len;
+ int if_num;
+ enum nss_match_msg_types type;
+ nss_tx_status_t nss_tx_status;
+
+ if ((rule_id == 0) || (rule_id > NSS_MATCH_INSTANCE_RULE_MAX)) {
+ nss_match_warn("%p: rule_id doesnot exist, rule_id = %d", nss_ctx, rule_id);
+ return NSS_MATCH_ERROR_RULE_ID_OUTOFBOUND;
+ }
+
+ if (!nss_match_db_table_validate(table_id)) {
+ nss_match_warn("%p: Invalid table_id %d", nss_ctx, table_id);
+ return NSS_MATCH_ERROR_TABLE_ID_OUTOFBOUND;
+ }
+
+ if_num = nss_match_get_ifnum_by_table_id(table_id);
+ if (if_num < 0) {
+ nss_match_warn("%p: Invalid table_id %d", nss_ctx, table_id);
+ return NSS_MATCH_ERROR_TABLE_ID_OUTOFBOUND;
+ }
+
+ nss_match_db_get_profile_type(table_id, &profile_type);
+
+ switch (profile_type) {
+ case NSS_MATCH_PROFILE_TYPE_VOW:
+ type = NSS_MATCH_DELETE_VOW_RULE_MSG;
+ len = sizeof(struct nss_match_rule_vow_msg);
+ break;
+ case NSS_MATCH_PROFILE_TYPE_L2:
+ type = NSS_MATCH_DELETE_L2_RULE_MSG;
+ len = sizeof(struct nss_match_rule_l2_msg);
+ break;
+ default:
+ nss_match_warn("%p: Unknown profile type: %d", nss_ctx, profile_type);
+ return NSS_MATCH_ERROR_UNKNOWN_MSG;
+ }
+
+ /*
+ * Read the rule information
+ */
+ if (!nss_match_db_rule_read(&matchm, table_id, rule_id)) {
+ nss_match_warn("%p: rule_id doesnot exist, rule_id = %d", nss_ctx, rule_id);
+ return NSS_MATCH_ERROR_RULE_ID_OUTOFBOUND;
+ }
+
+ nss_match_msg_init(&matchm, if_num, type, len, NULL, NULL);
+
+ nss_tx_status = nss_match_msg_tx_sync(nss_ctx, &matchm);
+ if (nss_tx_status != NSS_TX_SUCCESS) {
+ nss_match_warn("%p: Sending delete rule failed, rule_id = %d, status = %d", nss_ctx, rule_id, nss_tx_status);
+ return NSS_MATCH_ERROR_RULE_DELETE;
+ }
+
+ nss_match_db_rule_delete(table_id, rule_id);
+ return NSS_MATCH_SUCCESS;
+}
+EXPORT_SYMBOL(nss_match_rule_delete);
+
+/*
+ * nss_match_vow_rule_add()
+ * User API to add VoW rule.
+ */
+int nss_match_vow_rule_add(struct nss_ctx_instance *nss_ctx, struct nss_match_rule_vow_msg *rule_msg, uint32_t table_id)
+{
+ int rule_id = -1, if_num;
+ struct nss_match_msg nmm;
+ enum nss_match_msg_types type = NSS_MATCH_ADD_VOW_RULE_MSG;
+ nss_tx_status_t nss_tx_status;
+
+ if (!nss_match_db_table_validate(table_id)) {
+ nss_match_warn("%p: Invalid table_id %d, table is not configured.\n", nss_ctx, table_id);
+ return rule_id;
+ }
+
+ if_num = nss_match_get_ifnum_by_table_id(table_id);
+ if (if_num < 0) {
+ nss_match_warn("%p: Cannot add the rule, table doesnot exist", nss_ctx);
+ return rule_id;
+ }
+
+ if (nss_match_db_rule_find(rule_msg, table_id)) {
+ nss_match_warn("%p: Rule exists already. \n", nss_ctx);
+ return -1;
+ }
+
+ rule_id = nss_match_db_generate_rule_id(table_id);
+ if (rule_id <= 0 ) {
+ nss_match_warn("%p: Reached limit, New rule can't be added. \n", nss_ctx);
+ return -1;
+ }
+
+ rule_msg->rule_id = rule_id;
+ nss_match_db_rule_add(rule_msg, table_id);
+
+ nss_match_msg_init(&nmm, if_num, type, sizeof(struct nss_match_rule_vow_msg), NULL, NULL);
+
+ nmm.msg.vow_rule = *rule_msg;
+ nmm.cm.type = type;
+
+ /*
+ * Send the message to FW to add the VoW rule into the rule table.
+ */
+ nss_tx_status = nss_match_msg_tx_sync(nss_ctx, &nmm);
+ if (nss_tx_status != NSS_TX_SUCCESS) {
+ nss_match_warn("%p:add rule failed from NSS, rule_id = %d, nss_tx_status = %d\n",
+ nss_ctx, rule_id, nss_tx_status);
+ nss_match_db_rule_delete(table_id, rule_id);
+ return -1;
+ }
+
+ return rule_id;
+}
+EXPORT_SYMBOL(nss_match_vow_rule_add);
+
+/*
+ * nss_match_l2_rule_add()
+ * User API to add rule for L2 profile.
+ */
+int nss_match_l2_rule_add(struct nss_ctx_instance *nss_ctx, struct nss_match_rule_l2_msg *rule_msg, uint32_t table_id)
+{
+ int rule_id = -1, if_num;
+ enum nss_match_msg_types type = NSS_MATCH_ADD_L2_RULE_MSG;
+ struct nss_match_msg nmm;
+ nss_tx_status_t nss_tx_status;
+
+ if (!nss_match_db_table_validate(table_id)) {
+ nss_match_warn("%p: Cannot insert rule, table: %d is not configured. \n", nss_ctx, table_id);
+ return -1;
+ }
+
+ if_num = nss_match_get_ifnum_by_table_id(table_id);
+ if (if_num < 0) {
+ nss_match_warn("%p: Cannot add the rule, invalid table ID: %d", nss_ctx, table_id);
+ return rule_id;
+ }
+
+ if (nss_match_db_rule_find(rule_msg, table_id)) {
+ nss_match_warn("%p: Rule exists already \n", nss_ctx);
+ return -1;
+ }
+
+ rule_id = nss_match_db_generate_rule_id(table_id);
+ if (rule_id <= 0) {
+ nss_match_warn("%p: New rule can't be added, Reached limit. \n", nss_ctx);
+ return -1;
+ }
+
+ rule_msg->rule_id = rule_id;
+ nss_match_db_rule_add(rule_msg, table_id);
+
+ nss_match_msg_init(&nmm, if_num, type, sizeof(struct nss_match_rule_l2_msg), NULL, NULL);
+
+ nmm.msg.l2_rule = *rule_msg;
+ nmm.cm.type = type;
+
+ /*
+ * Send the message to FW to add the L2 rule into the rule table.
+ */
+ nss_tx_status = nss_match_msg_tx_sync(nss_ctx, &nmm);
+ if (nss_tx_status != NSS_TX_SUCCESS) {
+ nss_match_warn("%p:add rule failed from NSS, rule_id = %d, nss_tx_status = %d\n",
+ nss_ctx, rule_id, nss_tx_status);
+ nss_match_db_rule_delete(table_id, rule_id);
+ return -1;
+ }
+
+ return rule_id;
+}
+EXPORT_SYMBOL(nss_match_l2_rule_add);
+
+/*
+ * nss_match_profile_configure()
+ * User API to configure match profile.
+ */
+nss_match_status_t nss_match_profile_configure(struct nss_ctx_instance *nss_ctx, struct nss_match_profile_configure_msg *config_msg, uint32_t table_id)
+{
+ struct nss_match_msg matchm;
+ int32_t if_num;
+ nss_tx_status_t nss_tx_status;
+ nss_match_status_t status;
+
+ status = nss_match_verify_config_msg(config_msg);
+ if (status != NSS_MATCH_SUCCESS) {
+ nss_match_warn("%p: Invalid config message.", nss_ctx);
+ return status;
+ }
+
+ if ((table_id == 0) || (table_id > NSS_MATCH_INSTANCE_MAX)) {
+ nss_match_warn("%p: Cannot configure table, table_id %d is not valid.\n", nss_ctx, table_id);
+ return NSS_MATCH_ERROR_TABLE_ID_OUTOFBOUND;
+ }
+
+ if (nss_match_db_table_validate(table_id)) {
+ nss_match_warn("Table %d is already configured.\n", table_id);
+ return NSS_MATCH_ERROR_TABLE_ID_OUTOFBOUND;
+ }
+
+ if_num = nss_match_get_ifnum_by_table_id(table_id);
+ if (if_num < 0) {
+ nss_match_warn("%p: Invalid table ID: %d, if_num %d", nss_ctx, table_id, if_num);
+ return NSS_MATCH_ERROR_TABLE_ID_OUTOFBOUND;
+ }
+
+ /*
+ * Configuring the profile will enable sync from NSS. So add the instance into the DB first.
+ */
+ nss_match_db_profile_type_add(config_msg->profile_type, table_id);
+ nss_match_db_mask_add(config_msg, table_id);
+ nss_match_db_instance_enable(table_id);
+
+ nss_match_msg_init(&matchm, if_num, NSS_MATCH_TABLE_CONFIGURE_MSG,
+ sizeof(struct nss_match_profile_configure_msg), NULL, NULL);
+ matchm.msg.configure_msg = *config_msg;
+
+ nss_tx_status = nss_match_msg_tx_sync(nss_ctx, &matchm);
+ if (nss_tx_status != NSS_TX_SUCCESS) {
+ nss_match_warn("%p: Profile configuration failed for table_id = %d\n, nss_tx_status = %d\n",
+ nss_ctx, if_num, nss_tx_status);
+ nss_match_db_instance_disable(table_id);
+ return NSS_MATCH_ERROR_INSTANCE_CONFIGURED;
+ }
+
+ return NSS_MATCH_SUCCESS;
+}
+EXPORT_SYMBOL(nss_match_profile_configure);
+
+/*
+ * nss_match_instance_destroy()
+ * User API to destroy the match instance.
+ */
+nss_match_status_t nss_match_instance_destroy(uint32_t table_id)
+{
+ nss_tx_status_t status;
+ int if_num;
+
+ if (table_id == 0 || table_id > NSS_MATCH_INSTANCE_MAX) {
+ nss_match_warn("Cannot delete the rule, table = %d, does not exist", table_id);
+ return NSS_MATCH_ERROR_TABLE_ID_OUTOFBOUND;
+ }
+
+ if_num = nss_match_get_ifnum_by_table_id(table_id);
+ if (if_num < 0) {
+ nss_match_warn("Cannot delete the rule, if_num = %d, table = %d, does not exist", if_num, table_id);
+ return NSS_MATCH_ERROR_TABLE_ID_OUTOFBOUND;
+ }
+
+ if (!nss_match_unregister_instance(if_num)) {
+ nss_match_warn("Cannot unregister the instance: %d\n", table_id);
+ return NSS_MATCH_ERROR_TABLE_ID_OUTOFBOUND;
+ }
+
+ /*
+ * Dealloc the interface first, so that stats syncs are disabled.
+ */
+ status = nss_dynamic_interface_dealloc_node(if_num, NSS_DYNAMIC_INTERFACE_TYPE_MATCH);
+ if (status != NSS_TX_SUCCESS) {
+ nss_match_warn("Table dealloc node failure for if_num =%d\n", if_num);
+ return NSS_MATCH_ERROR_TABLE_DELETE;
+ }
+
+ nss_match_db_table_destroy(table_id);
+ return NSS_MATCH_SUCCESS;
+}
+EXPORT_SYMBOL(nss_match_instance_destroy);
+
+/*
+ * nss_match_instance_create()
+ * User API to create the match instance.
+ */
+int32_t nss_match_instance_create(void)
+{
+ int32_t if_num, table_id;
+ struct nss_ctx_instance *nss_ctx;
+
+ if (nss_match_db_instance_count_get() == NSS_MATCH_INSTANCE_MAX) {
+ nss_match_warn("Match instance limit exceeded.\n");
+ return -1;
+ }
+
+ if_num = nss_dynamic_interface_alloc_node(NSS_DYNAMIC_INTERFACE_TYPE_MATCH);
+ if (if_num < 0) {
+ nss_match_warn("Failed to allocate a node.\n");
+ return if_num;
+ }
+
+ nss_ctx = nss_match_register_instance(if_num, nss_match_sync_callback);
+ if (!nss_ctx) {
+ nss_match_warn("%p, Failed to register node : %d.\n", nss_ctx, if_num);
+ goto dealloc_node;
+ }
+
+ /*
+ * Create the table entry in the DB.
+ */
+ table_id = nss_match_db_table_create(if_num);
+ if (table_id < 0) {
+ nss_match_warn("%p: Memory allocation failed for match DB array\n", nss_ctx);
+ goto unregister_node;
+ }
+
+ return table_id;
+
+unregister_node:
+ nss_match_unregister_instance(if_num);
+
+dealloc_node:
+ nss_dynamic_interface_dealloc_node(if_num, NSS_DYNAMIC_INTERFACE_TYPE_MATCH);
+
+ return -1;
+
+}
+EXPORT_SYMBOL(nss_match_instance_create);
+
+/*
+ * nss_match_init_module()
+ */
+int __init nss_match_init_module(void)
+{
+ struct dentry *match_config;
+
+#ifdef CONFIG_OF
+ /*
+ * If the node is not compatible, don't do anything.
+ */
+ if (!of_find_node_by_name(NULL, "nss-common")) {
+ return 0;
+ }
+#endif
+ nss_match_db_init();
+
+ /*
+ * Register VoW profile ops
+ */
+ nss_match_vow_init();
+
+ /*
+ * Register L2 profile ops
+ */
+ nss_match_l2_init();
+
+ match_config = debugfs_create_dir("match", NULL);
+
+ if (!match_config) {
+ nss_match_warn("Cannot create MATCH directory");
+ return -1;
+ }
+
+ /*
+ * Register command line interface for match
+ */
+ if (!nss_match_cmd_debugfs_create(match_config)) {
+ nss_match_warn("Cannot create MATCH node cmd dentry file");
+ debugfs_remove_recursive(match_config);
+ return -1;
+ }
+
+ if (!nss_match_stats_debugfs_create(match_config)) {
+ nss_match_warn("Cannot create MATCH node stats dentry file");
+ debugfs_remove_recursive(match_config);
+ return -1;
+ }
+
+ nss_match_info("NSS match client initialized\n");
+ return 0;
+}
+module_init(nss_match_init_module);
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/match/nss_match_cmd.c b/match/nss_match_cmd.c
new file mode 100644
index 0000000..9dd5ea7
--- /dev/null
+++ b/match/nss_match_cmd.c
@@ -0,0 +1,612 @@
+/*
+ ***************************************************************************
+ * 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_match_cmd.c
+ */
+
+#include <linux/sysctl.h>
+#include <linux/inetdevice.h>
+#include <linux/netdevice.h>
+#include <linux/ctype.h>
+#include "nss_match_db.h"
+#include "nss_match_cmd.h"
+#include <nss_api_if.h>
+#include <linux/debugfs.h>
+#include "nss_match_priv.h"
+
+/*
+ * nss_match_cmd_instance_config_tx_sync()
+ * Sends configuration message to NSS
+ */
+static nss_tx_status_t nss_match_cmd_instance_config_tx_sync(struct nss_ctx_instance *nss_ctx,
+ uint32_t if_num, struct nss_match_profile_configure_msg *config_msg)
+{
+ struct nss_match_msg matchm;
+
+ nss_match_msg_init(&matchm, if_num, NSS_MATCH_TABLE_CONFIGURE_MSG,
+ sizeof(struct nss_match_profile_configure_msg), NULL, NULL);
+ matchm.msg.configure_msg = *config_msg;
+
+ return nss_match_msg_tx_sync(nss_ctx, &matchm);
+}
+
+static int nss_match_cmd_enable_instance(struct nss_match_profile_configure_msg *config_msg, int if_num, uint32_t table_id) {
+
+ nss_tx_status_t nss_tx_status;
+ struct nss_ctx_instance *nss_ctx = nss_match_get_context();
+
+ nss_tx_status = nss_match_cmd_instance_config_tx_sync(nss_ctx, if_num, config_msg);
+ if (nss_tx_status == NSS_TX_SUCCESS) {
+ if (!nss_match_db_instance_enable(table_id)) {
+ nss_match_warn("Failed to enable instance for table_id=%u\n", table_id);
+ return -1;
+ }
+ return 0;
+ }
+ nss_match_warn("%p: Cannot configure/enable the new instance\n", nss_ctx);
+
+ return -1;
+}
+
+/*
+ * nss_match_cmd_parse()
+ * Returns command type.
+ */
+static nss_match_cmd_t nss_match_cmd_parse(char *cmd)
+{
+ if (cmd == NULL)
+ return NSS_MATCH_UNKNOWN;
+ if (!(strncasecmp(cmd, "createtable", strlen("createtable"))))
+ return NSS_MATCH_CREATE_TABLE;
+ if (!(strncasecmp(cmd, "addmask", strlen("addmask"))))
+ return NSS_MATCH_ADD_MASK;
+ if (!(strncasecmp(cmd, "enable", strlen("enable"))))
+ return NSS_MATCH_ENABLE;
+ if (!(strncasecmp(cmd, "addrule", strlen("addrule"))))
+ return NSS_MATCH_ADD_RULE;
+ if (!(strncasecmp(cmd, "delrule", strlen("delrule"))))
+ return NSS_MATCH_DELETE_RULE;
+ if (!(strncasecmp(cmd, "deltable", strlen("deltable"))))
+ return NSS_MATCH_DESTROY_TABLE;
+
+ return NSS_MATCH_UNKNOWN;
+}
+
+/*
+ * nss_match_cmd_get_profile_type()
+ * Parse message to create an instance.
+ */
+static enum nss_match_profile_type nss_match_cmd_get_profile_type(char *input_msg)
+{
+ char *token, *param;
+
+ token = strsep(&input_msg, " ");
+ if (!token) {
+ return NSS_MATCH_PROFILE_TYPE_NONE;
+ }
+
+ param = strsep(&token, "=");
+ if (!param || !token) {
+ return NSS_MATCH_PROFILE_TYPE_NONE;
+ }
+
+ if (!(strncasecmp(param, "profile_type", strlen("profile_type")))) {
+ if (!(strncasecmp(token, "vow", strlen("vow")))) {
+ return NSS_MATCH_PROFILE_TYPE_VOW;
+ }
+
+ if (!(strncasecmp(token, "l2", strlen("l2")))) {
+ return NSS_MATCH_PROFILE_TYPE_L2;
+ }
+ }
+
+ return NSS_MATCH_PROFILE_TYPE_NONE;
+}
+
+
+/*
+ * nss_match_cmd_debugfs_write_handler()
+ * Handles command input by user to create and configure match instance.
+ */
+static ssize_t nss_match_cmd_debugfs_write_handler(struct file *fp, const char __user *buf, size_t count, loff_t *ppos)
+{
+ int ret;
+ char *command_str, *token, *param, *value;
+ char *input_msg, *input_msg_orig;
+ nss_match_cmd_t command;
+ struct nss_ctx_instance *nss_ctx = nss_match_get_context();
+
+ input_msg = (char *)kzalloc(count + 1, GFP_KERNEL);
+ if (!input_msg) {
+ nss_match_warn("%p: Dynamic allocation falied while writing input message from file", fp);
+ return -ENOMEM;
+ }
+
+ input_msg_orig = input_msg;
+ if (copy_from_user(input_msg, buf, count)) {
+ kfree(input_msg);
+ nss_match_warn("%p: Cannot copy user's entry to kernel memory\n", fp);
+ return -EFAULT;
+ }
+
+ command_str = strsep(&input_msg, " ");
+ command = nss_match_cmd_parse(command_str);
+
+ switch (command) {
+ case NSS_MATCH_CREATE_TABLE:
+ {
+ int table_id = -1, profile_type = 0;
+
+ profile_type = nss_match_cmd_get_profile_type(input_msg);
+ if (profile_type == NSS_MATCH_PROFILE_TYPE_NONE) {
+ pr_warn("%p: Please provide a valid profile type\n", fp);
+ kfree(input_msg_orig);
+ return -EINVAL;
+ }
+
+ table_id = nss_match_instance_create();
+ if (table_id <= 0) {
+ pr_warn("%p: Cannot create a new match instance\n", fp);
+ kfree(input_msg_orig);
+ return -EINVAL;
+ }
+
+ nss_match_db_profile_type_add(profile_type, table_id);
+ pr_warn("New match instance created, table_id = %d\n", table_id);
+ kfree(input_msg_orig);
+ return count;
+ }
+
+ case NSS_MATCH_ADD_MASK:
+ {
+ uint32_t table_id = 0;
+ struct nss_match_msg input_mask_param = {0};
+
+ token = strsep(&input_msg, " ");
+ param = strsep(&token, "=");
+ value = token;
+ if (!value || !param) {
+ goto fail;
+ }
+
+ if (!strncasecmp(param, "table_id", strlen("table_id"))) {
+ ret = sscanf(value, "%u", &table_id);
+ if (!ret) {
+ pr_warn("%p: Cannot convert to integer. Wrong input!!", fp);
+ kfree(input_msg_orig);
+ return -EINVAL;
+ }
+ }
+
+ if (table_id == 0 || table_id > NSS_MATCH_INSTANCE_MAX) {
+ pr_warn("%p: Invalid table_id %d", fp, table_id);
+ kfree(input_msg_orig);
+ return -EINVAL;
+ }
+
+ if (nss_match_db_table_validate(table_id)) {
+ pr_warn("%p: Table is already configured, %d", fp, table_id);
+ kfree(input_msg_orig);
+ return -EINVAL;
+ }
+
+ if (nss_match_db_parse_cmd(table_id, input_msg, &input_mask_param, NSS_MATCH_ADD_MASK)) {
+ kfree(input_msg_orig);
+ return -EINVAL;
+ }
+
+ nss_match_db_mask_add(&input_mask_param.msg.configure_msg, table_id);
+ pr_warn("%p: Mask added to instance successfully. %d", fp, table_id);
+
+ kfree(input_msg_orig);
+ return count;
+ }
+
+ case NSS_MATCH_ENABLE:
+ {
+ uint32_t table_id = 0;
+ struct nss_match_profile_configure_msg config_msg = {0};
+ int if_num = -1;
+
+ token = strsep(&input_msg, " ");
+ param = strsep(&token, "=");
+ value = token;
+ if (!param || !value) {
+ goto fail;
+ }
+
+ if (!strncasecmp(param, "table_id", strlen("table_id"))) {
+ ret = sscanf(value, "%u", &table_id);
+ if (!ret) {
+ pr_warn("%p: Cannot convert to integer. Wrong input!!", fp);
+ kfree(input_msg_orig);
+ return -EINVAL;
+ }
+ }
+
+ if ((table_id == 0) || (table_id > NSS_MATCH_INSTANCE_MAX)) {
+ pr_warn("%p: Invalid table_id %d", fp, table_id);
+ kfree(input_msg_orig);
+ return -EINVAL;
+ }
+
+ if (nss_match_db_table_validate(table_id)) {
+ pr_warn("%p: Table is already configured, %d", fp, table_id);
+ kfree(input_msg_orig);
+ return -EINVAL;
+ }
+
+ if (!nss_match_db_instance_config_get(&config_msg, &if_num, table_id)) {
+ pr_warn("%p: Unable to fetch stored configuration %d", fp, table_id);
+ kfree(input_msg_orig);
+ return -EINVAL;
+ }
+
+ if (if_num < 0) {
+ nss_match_warn("%p: Incorrect interface number: %d\n", fp, if_num);
+ kfree(input_msg_orig);
+ return -EINVAL;
+ }
+
+ if (nss_match_cmd_enable_instance(&config_msg, if_num, table_id)) {
+ pr_warn("Failed to enable table %d\n", table_id);
+ kfree(input_msg_orig);
+ return -EINVAL;
+ }
+
+ pr_warn("Table %d enabled successfully\n", table_id);
+ kfree(input_msg_orig);
+ return count;
+ }
+
+ case NSS_MATCH_ADD_RULE:
+ {
+ uint32_t profile_type = 0;
+ uint32_t table_id = 0;
+ int rule_id = -1;
+ struct nss_match_msg input_rule_param = {0};
+
+ token = strsep(&input_msg, " ");
+ param = strsep(&token, "=");
+ value = token;
+ if (!param || !value) {
+ goto fail;
+ }
+
+ if (!strncasecmp(param, "table_id", strlen("table_id"))) {
+ ret = sscanf(value, "%u", &table_id);
+ if (!ret) {
+ pr_warn("%p: Cannot convert to integer. Wrong input!!", fp);
+ kfree(input_msg_orig);
+ return -EINVAL;
+ }
+ }
+
+ if (table_id == 0 || table_id > NSS_MATCH_INSTANCE_MAX) {
+ pr_warn("%p: Invalid table_id: %d", fp, table_id);
+ kfree(input_msg_orig);
+ return -EINVAL;
+ }
+
+ nss_match_db_get_profile_type(table_id, &profile_type);
+
+ if (nss_match_db_parse_cmd(table_id, input_msg, &input_rule_param, NSS_MATCH_ADD_RULE)) {
+ pr_warn("%p: Wrong input", fp);
+ kfree(input_msg_orig);
+ return -EINVAL;
+ }
+
+ if (profile_type == NSS_MATCH_PROFILE_TYPE_VOW) {
+ rule_id = nss_match_vow_rule_add(nss_ctx, &input_rule_param.msg.vow_rule, table_id);
+ } else if (profile_type == NSS_MATCH_PROFILE_TYPE_L2) {
+ rule_id = nss_match_l2_rule_add(nss_ctx, &input_rule_param.msg.l2_rule, table_id);
+ }
+
+ if (rule_id < 0) {
+ pr_warn("Failed to add vow rule to table %d.\n", table_id);
+ kfree(input_msg_orig);
+ return -EINVAL;
+ }
+
+ pr_warn("Rule added to table %d successfully with rule_id: %d\n", table_id, rule_id);
+ kfree(input_msg_orig);
+ return count;
+ }
+
+ case NSS_MATCH_DELETE_RULE:
+ {
+ uint32_t table_id = 0;
+ uint16_t rule_id = 0;
+
+ while (input_msg != NULL) {
+ token = strsep(&input_msg, " ");
+ param = strsep(&token, "=");
+ if (!param || !token) {
+ goto fail;
+ }
+
+ /*
+ * Parsing rule_id and table_id value from the message.
+ */
+ if (!(strncasecmp(param, "rule_id", strlen("rule_id")))) {
+ if (!sscanf(token, "%hu", &rule_id)) {
+ pr_warn("%p: Cannot convert to integer. Wrong input\n", fp);
+ kfree(input_msg_orig);
+ return -EINVAL;
+ }
+ continue;
+ }
+
+ if (!strncasecmp(param, "table_id", strlen("table_id"))) {
+ if (!sscanf(token, "%u", &table_id)) {
+ pr_warn("%p: Cannot convert to integer. Wrong input!!", fp);
+ kfree(input_msg_orig);
+ return -EINVAL;
+ }
+ continue;
+ }
+
+ kfree(input_msg_orig);
+ return -EINVAL;
+ }
+
+ if (table_id == 0 || table_id > NSS_MATCH_INSTANCE_MAX) {
+ pr_warn("%p: Invalid table_id: %d", fp, table_id);
+ kfree(input_msg_orig);
+ return -EINVAL;
+ }
+
+ if (rule_id == 0 || rule_id > NSS_MATCH_INSTANCE_RULE_MAX) {
+ pr_warn("%p: Invalid rule_id: %d", fp, rule_id);
+ kfree(input_msg_orig);
+ return -EINVAL;
+ }
+
+ if (nss_match_rule_delete(nss_ctx, rule_id, table_id)) {
+ pr_warn("Failed to delete rule from table %d.\n", table_id);
+ kfree(input_msg_orig);
+ return -EINVAL;
+ }
+
+ pr_warn("Rule deleted from table %d successfully\n", table_id);
+ kfree(input_msg_orig);
+ return count;
+ }
+
+ case NSS_MATCH_DESTROY_TABLE:
+ {
+ uint32_t table_id = 0;
+ char *token, *param;
+ int ret = 0;
+
+ token = strsep(&input_msg, " ");
+ param = strsep(&token, "=");
+ if (!token || !param) {
+ goto fail;
+ }
+
+ if (!(strncasecmp(param, "table_id", strlen("table_id")))) {
+ ret = sscanf(token, "%u", &table_id);
+ if (!ret) {
+ pr_warn("%p: Cannot convert to integer. Wrong input!!", input_msg);
+ kfree(input_msg_orig);
+ return -EINVAL;
+ }
+ }
+
+ if (table_id == 0 || table_id > NSS_MATCH_INSTANCE_MAX) {
+ pr_warn("%p: Invalid table_id: %d", fp, table_id);
+ kfree(input_msg_orig);
+ return -EINVAL;
+ }
+
+ if (nss_match_instance_destroy(table_id)) {
+ pr_warn("Failed to destroy table %d\n", table_id);
+ kfree(input_msg_orig);
+ return -EINVAL;
+ }
+
+ pr_warn("Table %d destroyed successfully.\n", table_id);
+ kfree(input_msg_orig);
+ return count;
+ }
+
+ default:
+ {
+ pr_warn("%p: Input command is not as per syntax, Please enter a valid command", fp);
+ kfree(input_msg_orig);
+ return -EINVAL;
+ }
+ }
+
+fail:
+ pr_warn("wrong input.Check help. (cat /sys/kernel/debug/match/help)");
+ kfree(input_msg_orig);
+ return count;
+
+}
+
+/*
+ * nss_match_cmd_debugfs_set_if_nexthop
+ * Set next hop of an interface to a match instance.
+ */
+ssize_t nss_match_cmd_debugfs_set_if_nexthop(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
+{
+ struct net_device *dev;
+ uint32_t if_num;
+ uint32_t nh_if_num;
+ int table_id;
+ struct nss_ctx_instance *nss_ctx = nss_match_get_context();
+ char *dev_name, *nexthop_msg;
+ char *cmd_buf = NULL;
+ nss_tx_status_t nss_tx_status;
+
+ cmd_buf = (char *)kzalloc(count + 1, GFP_KERNEL);
+ nexthop_msg = cmd_buf;
+ if (!cmd_buf) {
+ pr_warn("%p: Cannot allocate buffer to read input", nss_ctx);
+ return -ENOMEM;
+ }
+
+ if (copy_from_user(cmd_buf, buf, count)) {
+ kfree(nexthop_msg);
+ pr_warn("%p: Cannot copy user's entry to kernel memory\n", nss_ctx);
+ return -EFAULT;
+ }
+
+ dev_name = strsep(&cmd_buf, " ");
+ dev = dev_get_by_name(&init_net, dev_name);
+ if (!dev) {
+ pr_warn("%p: Cannot find the net device\n", nss_ctx);
+ kfree(nexthop_msg);
+ return -ENODEV;
+ }
+
+ if_num = nss_cmn_get_interface_number_by_dev(dev);
+ if (if_num < 0) {
+ pr_warn("%p: Invalid interface number:%d\n", nss_ctx, if_num);
+ kfree(nexthop_msg);
+ dev_put(dev);
+ return -ENODEV;
+ }
+
+ if (isdigit(cmd_buf[0])) {
+ if (!sscanf(cmd_buf, "%u", &nh_if_num)) {
+ pr_warn("%p, Failed to write the nexthop if_num token to integer\n", nss_ctx);
+ kfree(nexthop_msg);
+ dev_put(dev);
+ return -EFAULT;
+ }
+ } else {
+ pr_warn("%p: Invalid nexthop interface number.\n", nss_ctx);
+ kfree(nexthop_msg);
+ dev_put(dev);
+ return -ENODEV;
+ }
+
+ if (nh_if_num < 0) {
+ pr_warn("%p: Invalid nexthop interface number:%d\n", nss_ctx, if_num);
+ kfree(nexthop_msg);
+ dev_put(dev);
+ return -ENODEV;
+ }
+
+ table_id = nss_match_get_table_id_by_ifnum(nh_if_num);
+ if (table_id <= 0 || table_id > NSS_MATCH_INSTANCE_MAX) {
+ pr_warn("Invalid match interface. Failed to set %d as nexthop.\n", nh_if_num);
+ kfree(nexthop_msg);
+ dev_put(dev);
+ return -EFAULT;
+ }
+
+ nss_tx_status = nss_phys_if_set_nexthop(nss_ctx, if_num, nh_if_num);
+ if (nss_tx_status != NSS_TX_SUCCESS) {
+ pr_warn("%p: Sending message failed, cannot change nexthop\n", nss_ctx);
+ }
+
+ kfree(nexthop_msg);
+ dev_put(dev);
+ return count;
+}
+
+/*
+ * nss_match_cmd_help()
+ * Display help for commands.
+ */
+static ssize_t nss_match_cmd_help(struct file *fp, char __user *ubuf, size_t sz, loff_t *ppos)
+{
+ size_t size_wr;
+ size_t size_al = NSS_STATS_MAX_STR_LENGTH * 24;
+ ssize_t bytes_read = 0;
+
+ char *lbuf = kzalloc(size_al, GFP_KERNEL);
+ if (unlikely(lbuf == NULL)) {
+ nss_match_warn("Could not allocate memory for local statistics buffer\n");
+ return 0;
+ }
+
+ size_wr += scnprintf(lbuf + size_wr, size_al - size_wr, "\nHelp: (/sys/kernel/debug/match/) \n\
+ 1. To create match isntance:\n\
+ echo createtable profile_type=<vow/l2> > config \n\
+ 2. To addmask: \n\
+ a. VoW profile \n\
+ echo addmask table_id=<1..4> mask=<1/2> ifname=<1..ffff> dscp=<1..3f> 802.1p_outer=<1..7> 802.1p_inner=<1..7> > config\n\
+ b. L2 profile \n\
+ echo addmask table_id=<1..4> mask=<1/2> ifname=<1..ffff> smac=<1..ffffffffffff>\
+ dmac=<1..ffffffffffff> ethertype=<1..ffff> > config\n\
+ 3. To enable match instance \n\
+ echo enable table_id=1 > config \n\
+ 5. Actions:\n\
+ a. action=1 priority=<pri_value>\n\
+ b. action=2 nexthop=<nexthop_ifnum>\n\
+ c. action=3 priority=<pri_value> nexthop=<nexthop_ifnum>\n\
+ d. action=4\n\
+ 4. To set nexthop: \n\
+ echo <phy_ifname/VAP name> <match_ifnum> > set_nexthop\n");
+
+ bytes_read = simple_read_from_buffer(ubuf, sz, ppos, lbuf, size_wr);
+ kfree(lbuf);
+ return bytes_read;
+}
+
+/*
+ * nss_match_help
+ */
+static const struct file_operations nss_match_help = {
+ .read = nss_match_cmd_help,
+};
+
+/*
+ * File ops for match configuration.
+ */
+static const struct file_operations match_fops = {
+ .write = nss_match_cmd_debugfs_write_handler,
+};
+
+/*
+ * File ops for setting nexthop of an interface to match.
+ */
+static const struct file_operations nexthop_fops = {
+ .write = nss_match_cmd_debugfs_set_if_nexthop,
+};
+
+/*
+ * nss_match_cmd_debugfs_create()
+ */
+bool nss_match_cmd_debugfs_create(struct dentry *match_config)
+{
+ if (!debugfs_create_file("config", 0777, match_config, NULL, &match_fops)) {
+ nss_match_warn("Cannot create config dentry file");
+ debugfs_remove_recursive(match_config);
+ return false;
+ }
+
+ if (!debugfs_create_file("set_nexthop", 0777, match_config, NULL, &nexthop_fops)) {
+ nss_match_warn("Cannot create set nexthop dentry file");
+ debugfs_remove_recursive(match_config);
+ return false;
+ }
+
+ if (!debugfs_create_file("help", 0400, match_config, NULL, &nss_match_help)) {
+ nss_match_warn("Cannot create MATCH dentry file");
+ return false;
+ }
+
+ return true;
+}
diff --git a/match/nss_match_cmd.h b/match/nss_match_cmd.h
new file mode 100644
index 0000000..8dcfdd3
--- /dev/null
+++ b/match/nss_match_cmd.h
@@ -0,0 +1,41 @@
+/*
+ *******************************************************************************
+ * 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.
+ ********************************************************************************
+ **/
+
+#ifndef __NSS_MATCH_CMD_H
+#define __NSS_MATCH_CMD_H
+
+#include <linux/types.h>
+#include <linux/sysctl.h>
+#include <linux/debugfs.h>
+
+/*
+ * nss_match_parse_command_type
+ * Match parse command types.
+ */
+typedef enum nss_match_parse_command_type {
+ NSS_MATCH_UNKNOWN,
+ NSS_MATCH_CREATE_TABLE,
+ NSS_MATCH_ADD_MASK,
+ NSS_MATCH_ENABLE,
+ NSS_MATCH_ADD_RULE,
+ NSS_MATCH_DELETE_RULE,
+ NSS_MATCH_DESTROY_TABLE,
+} nss_match_cmd_t;
+
+bool nss_match_cmd_debugfs_create(struct dentry *match_config);
+#endif /* __NSS_MATCH_CMD_H */
diff --git a/match/nss_match_db.c b/match/nss_match_db.c
new file mode 100644
index 0000000..84aeaff
--- /dev/null
+++ b/match/nss_match_db.c
@@ -0,0 +1,664 @@
+/*
+ *******************************************************************************
+ * 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 "nss_match_db.h"
+#include "nss_match_priv.h"
+#include "nss_match_user.h"
+
+struct nss_match_db match_db; /* Match DB */
+struct match_profile_ops *profile_ops[NSS_MATCH_PROFILE_TYPE_MAX];
+
+/*
+ * nss_match_db_get_instance_by_table_id()
+ * Get match instance.
+ */
+static struct nss_match_instance *nss_match_db_get_instance_by_table_id(uint32_t table_id)
+{
+ assert_spin_locked(&match_db.db_lock);
+
+ /*
+ * This assumes table_id is 0 based actual array index.
+ */
+ if ((table_id >= NSS_MATCH_INSTANCE_MAX) || (!match_db.instance[table_id])) {
+ nss_match_warn("Invalid table index: %d\n", table_id+1);
+ return NULL;
+ }
+
+ return match_db.instance[table_id];
+}
+
+int nss_match_table_count_get(void)
+{
+ int index, count = 0;
+
+ spin_lock_bh(&match_db.db_lock);
+
+ for (index = 0; index < NSS_MATCH_INSTANCE_MAX; index++) {
+ if ((match_db.instance[index]) && (match_db.instance[index]->is_configured)) {
+ count++;
+ }
+ }
+
+ spin_unlock_bh(&match_db.db_lock);
+ return count;
+}
+
+/*
+ * nss_match_db_stats_get()
+ * Get stats value to print.
+ */
+bool nss_match_db_stats_get(uint32_t table_id, struct nss_match_stats *match_stats)
+{
+ struct nss_match_instance *db_instance;
+
+ spin_lock_bh(&match_db.db_lock);
+ db_instance = nss_match_db_get_instance_by_table_id(table_id - 1);
+ if (!db_instance) {
+ nss_match_warn("Invalid table index, failed to get DB instance: %d\n", table_id);
+ spin_unlock_bh(&match_db.db_lock);
+ return false;
+ }
+
+ memcpy(match_stats, &db_instance->stats, sizeof(struct nss_match_stats));
+ spin_unlock_bh(&match_db.db_lock);
+ return true;
+}
+
+/*
+ * nss_match_db_table_read()
+ * Read match table information, including profile information and rules data.
+ */
+size_t nss_match_db_table_read(uint32_t table_id, size_t buflen, char *bufp)
+{
+ struct nss_match_instance *db_instance;
+ size_t wr_len = 0;
+
+ spin_lock_bh(&match_db.db_lock);
+
+ db_instance = nss_match_db_get_instance_by_table_id(table_id - 1);
+ if (!db_instance) {
+ nss_match_warn("Invalid table index, failed to get DB instance: %d\n", table_id);
+ spin_unlock_bh(&match_db.db_lock);
+ return wr_len;
+ }
+
+ wr_len = db_instance->ops->nss_match_table_read(db_instance, buflen, bufp);
+ spin_unlock_bh(&match_db.db_lock);
+ return wr_len;
+}
+
+/*
+ * nss_match_db_generate_rule_id()
+ * Generates rule id for match rule.
+ */
+int nss_match_db_generate_rule_id(uint32_t table_id)
+{
+ int rule_id;
+ struct nss_match_instance *db_instance;
+
+ spin_lock_bh(&match_db.db_lock);
+ db_instance = nss_match_db_get_instance_by_table_id(table_id - 1);
+ if (!db_instance) {
+ nss_match_warn("Invalid table index, failed to get DB instance: %d\n", table_id);
+ spin_unlock_bh(&match_db.db_lock);
+ return -1;
+ }
+
+ rule_id = db_instance->ops->nss_match_rule_id_generate(db_instance);
+ spin_unlock_bh(&match_db.db_lock);
+ return rule_id;
+}
+
+/*
+ * nss_match_db_rule_find()
+ * Finds if rule exists in db.
+ */
+bool nss_match_db_rule_find(void *rule, uint32_t table_id)
+{
+ bool result;
+ struct nss_match_instance *db_instance;
+
+ spin_lock_bh(&match_db.db_lock);
+ db_instance = nss_match_db_get_instance_by_table_id(table_id - 1);
+ if (!db_instance) {
+ nss_match_warn("Invalid table index, failed to get DB instance: %d\n", table_id);
+ spin_unlock_bh(&match_db.db_lock);
+ return true;
+ }
+
+ result = db_instance->ops->nss_match_rule_find(db_instance, rule);
+ spin_unlock_bh(&match_db.db_lock);
+ return result;
+}
+
+/*
+ * nss_match_db_rule_add()
+ * Adds the match rule in match db.
+ */
+bool nss_match_db_rule_add(void *rule, uint8_t table_id)
+{
+ struct nss_match_instance *db_instance;
+
+ spin_lock_bh(&match_db.db_lock);
+
+ db_instance = nss_match_db_get_instance_by_table_id(table_id - 1);
+ if (!db_instance) {
+ nss_match_warn("Invalid table index, failed to get DB instance: %d\n", table_id);
+ spin_unlock_bh(&match_db.db_lock);
+ return false;
+ }
+
+ if (!db_instance->ops->nss_match_rule_add(db_instance, rule)) {
+ nss_match_warn("Unable to add rule to match database.\n");
+ spin_unlock_bh(&match_db.db_lock);
+ return false;
+ }
+
+ spin_unlock_bh(&match_db.db_lock);
+ return true;
+}
+
+/*
+ * nss_match_db_rule_delete()
+ * Deletes the match rule from match db.
+ */
+bool nss_match_db_rule_delete(uint32_t table_id, uint32_t rule_id)
+{
+ struct nss_match_instance *db_instance;
+
+ spin_lock_bh(&match_db.db_lock);
+
+ db_instance = nss_match_db_get_instance_by_table_id(table_id - 1);
+ if (!db_instance) {
+ nss_match_warn("Invalid table index, failed to get DB instance: %d\n", table_id);
+ spin_unlock_bh(&match_db.db_lock);
+ return false;
+ }
+
+ if (!db_instance->ops->nss_match_rule_delete(db_instance, rule_id)) {
+ nss_match_warn("Unable to delete rule from match database.\n");
+ spin_unlock_bh(&match_db.db_lock);
+ return false;
+ }
+
+ spin_unlock_bh(&match_db.db_lock);
+ return true;
+}
+
+/*
+ * nss_match_db_rule_read()
+ * Gets the match rule from match db.
+ */
+bool nss_match_db_rule_read(struct nss_match_msg *rule, uint32_t table_id, uint16_t rule_id)
+{
+ bool res;
+ struct nss_match_instance *db_instance;
+
+ spin_lock_bh(&match_db.db_lock);
+
+ db_instance = nss_match_db_get_instance_by_table_id(table_id - 1);
+ if (!db_instance) {
+ nss_match_warn("Invalid table index, failed to get DB instance: %d\n", table_id);
+ spin_unlock_bh(&match_db.db_lock);
+ return false;
+ }
+
+ res = db_instance->ops->nss_match_rule_read(db_instance, rule, rule_id);
+ spin_unlock_bh(&match_db.db_lock);
+ return res;
+}
+
+/*
+ * nss_match_db_parse_cmd()
+ * Parses match commands and fills 'rule_msg' accordingly with the information.
+ */
+int nss_match_db_parse_cmd(uint32_t table_id, char *input_msg, struct nss_match_msg *rule_msg, nss_match_cmd_t type)
+{
+ int res;
+ struct nss_match_instance *db_instance;
+
+ spin_lock_bh(&match_db.db_lock);
+
+ db_instance = nss_match_db_get_instance_by_table_id(table_id - 1);
+ if (!db_instance) {
+ nss_match_warn("Invalid table index, failed to get DB instance: %d\n", table_id);
+ spin_unlock_bh(&match_db.db_lock);
+ return -1;
+ }
+
+ spin_unlock_bh(&match_db.db_lock);
+
+ res = db_instance->ops->nss_match_cmd_parse(input_msg, rule_msg, type);
+ return res;
+}
+
+
+/*
+ * nss_match_db_table_validate()
+ * Check if table is configured.
+ */
+bool nss_match_db_table_validate(int table_id)
+{
+ struct nss_match_instance *db_instance;
+
+ spin_lock_bh(&match_db.db_lock);
+
+ db_instance = nss_match_db_get_instance_by_table_id(table_id - 1);
+ if (!db_instance) {
+ nss_match_warn("Invalid table index, failed to get DB instance: %d\n", table_id);
+ spin_unlock_bh(&match_db.db_lock);
+ return false;
+ }
+
+ if (!db_instance->is_configured) {
+ nss_match_warn("Table is not configured, table_id %d\n", table_id);
+ spin_unlock_bh(&match_db.db_lock);
+ return false;
+ }
+
+ spin_unlock_bh(&match_db.db_lock);
+ return true;
+}
+
+/*
+ * nss_match_db_get_profile_type()
+ * Returns the table index and updates profile type.
+ */
+bool nss_match_db_get_profile_type(uint32_t table_id, uint32_t *profile_type)
+{
+ struct nss_match_instance *db_instance;
+
+ spin_lock_bh(&match_db.db_lock);
+
+ db_instance = nss_match_db_get_instance_by_table_id(table_id - 1);
+ if (!db_instance) {
+ nss_match_warn("Invalid table index, failed to get DB instance: %d\n", table_id);
+ spin_unlock_bh(&match_db.db_lock);
+ return false;
+ }
+
+ *profile_type = db_instance->profile_type;
+
+ spin_unlock_bh(&match_db.db_lock);
+ return true;
+}
+
+/*
+ * nss_match_get_ifnum_by_table_id()
+ * Returns interface number using table ID.
+ */
+int nss_match_get_ifnum_by_table_id(uint32_t table_id)
+{
+ int if_num;
+ struct nss_match_instance *db_instance;
+
+ spin_lock_bh(&match_db.db_lock);
+
+ db_instance = nss_match_db_get_instance_by_table_id(table_id - 1);
+ if (!db_instance) {
+ nss_match_warn("Invalid table index, failed to get DB instance: %d\n", table_id);
+ spin_unlock_bh(&match_db.db_lock);
+ return -1;
+ }
+
+ if_num = db_instance->if_num;
+ spin_unlock_bh(&match_db.db_lock);
+ return if_num;
+}
+EXPORT_SYMBOL(nss_match_get_ifnum_by_table_id);
+
+/*
+ * nss_match_get_table_id_by_ifnum()
+ * Returns table index using interface number.
+ *
+ */
+int nss_match_get_table_id_by_ifnum(int if_num)
+{
+ int index;
+
+ spin_lock_bh(&match_db.db_lock);
+
+ for (index = 0; index < NSS_MATCH_INSTANCE_MAX; index++) {
+ if (!match_db.instance[index] || match_db.instance[index]->if_num != if_num) {
+ continue;
+ }
+
+ spin_unlock_bh(&match_db.db_lock);
+ return index + 1;
+ }
+
+ spin_unlock_bh(&match_db.db_lock);
+ return -1;
+}
+
+/*
+ * nss_match_db_table_destroy()
+ * Destroys table information from DB.
+ */
+bool nss_match_db_table_destroy(int table_id)
+{
+ /*
+ * TODO: Add clear table API.
+ */
+ struct nss_match_instance *table_info;
+
+ if (table_id <= 0 || table_id > NSS_MATCH_INSTANCE_MAX) {
+ nss_match_warn("Invalid table index, failed to get DB instance: %d\n", table_id);
+ return false;
+ }
+
+ spin_lock_bh(&match_db.db_lock);
+
+ table_info = match_db.instance[table_id - 1];
+ if (!table_info) {
+ nss_match_warn("Invalid table index: %d, table doesn't exist.\n", table_id);
+ spin_unlock_bh(&match_db.db_lock);
+ return false;
+ }
+
+ match_db.instance_count--;
+ match_db.instance[table_id - 1] = NULL;
+
+ spin_unlock_bh(&match_db.db_lock);
+ kfree(table_info);
+ return true;
+}
+
+/*
+ * nss_match_db_table_create()
+ * Create an instance in the DB for the new table.
+ */
+int nss_match_db_table_create(int if_num)
+{
+ int index, table_id = -1;
+ struct nss_match_instance *mi;
+
+ mi = (struct nss_match_instance *) kzalloc(sizeof(struct nss_match_instance), GFP_KERNEL);
+ if (!mi) {
+ nss_match_warn("Unable to allocate memory for new table.\n");
+ return -1;
+ }
+
+ spin_lock_bh(&match_db.db_lock);
+ for (index = 0; index < NSS_MATCH_INSTANCE_MAX; index++) {
+ if (match_db.instance[index]) {
+ continue;
+ }
+
+ match_db.instance[index] = mi;
+ match_db.instance_count++;
+ match_db.instance[index]->profile_type = 0;
+ match_db.instance[index]->maskset[0][0] = 0;
+ match_db.instance[index]->maskset[1][0] = 0;
+ match_db.instance[index]->if_num = if_num;
+ match_db.instance[index]->is_configured = false;
+ match_db.instance[index]->valid_mask_flag = 0;
+ table_id = index + 1;
+ break;
+ }
+
+ spin_unlock_bh(&match_db.db_lock);
+ return table_id;
+}
+
+/*
+ * nss_match_db_profile_type_add()
+ * Add profile type to match instance.
+ */
+bool nss_match_db_profile_type_add(uint32_t profile_type, uint8_t table_id)
+{
+ struct nss_match_instance *db_instance;
+
+ spin_lock_bh(&match_db.db_lock);
+
+ db_instance = nss_match_db_get_instance_by_table_id(table_id - 1);
+ if (!db_instance) {
+ nss_match_warn("Invalid table index, failed to get DB instance: %d\n", table_id);
+ spin_unlock_bh(&match_db.db_lock);
+ return false;
+ }
+
+ db_instance->profile_type = profile_type;
+ db_instance->ops = profile_ops[profile_type];
+ nss_match_info("Added ops %p for profile type: %d", db_instance->ops, profile_type);
+ spin_unlock_bh(&match_db.db_lock);
+ return true;
+}
+
+
+/*
+ * nss_match_db_mask_add()
+ * Add a mask to the profile.
+ */
+bool nss_match_db_mask_add(struct nss_match_profile_configure_msg *config_msg, uint8_t table_id)
+{
+ int valid, index;
+ struct nss_match_instance *db_instance;
+
+ spin_lock_bh(&match_db.db_lock);
+
+ db_instance = nss_match_db_get_instance_by_table_id(table_id - 1);
+ if (!db_instance) {
+ nss_match_warn("Invalid table index, failed to get DB instance: %d\n", table_id);
+ spin_unlock_bh(&match_db.db_lock);
+ return false;
+ }
+
+ for (valid = 0; valid < NSS_MATCH_MASK_MAX; valid++) {
+ if (config_msg->valid_mask_flag & (1 << valid)) {
+ for (index = 0; index < NSS_MATCH_MASK_WORDS_MAX; index++) {
+ db_instance->maskset[valid][index] = config_msg->maskset[valid][index];
+ }
+ }
+ }
+ db_instance->valid_mask_flag |= config_msg->valid_mask_flag;
+ spin_unlock_bh(&match_db.db_lock);
+ return true;
+}
+
+/*
+ * nss_match_db_instance_config_get()
+ * Get the profile config given the table ID.
+ */
+bool nss_match_db_instance_config_get(struct nss_match_profile_configure_msg *config_msg, int *if_num, uint8_t table_id)
+{
+ int valid, index;
+ struct nss_match_instance *db_instance;
+
+ spin_lock_bh(&match_db.db_lock);
+
+ db_instance = nss_match_db_get_instance_by_table_id(table_id - 1);
+ if (!db_instance) {
+ nss_match_warn("Invalid table index, failed to get DB instance: %d\n", table_id);
+ spin_unlock_bh(&match_db.db_lock);
+ return false;
+ }
+
+ config_msg->profile_type = db_instance->profile_type;
+ if (!(db_instance->valid_mask_flag)) {
+ spin_unlock_bh(&match_db.db_lock);
+ return false;
+ }
+
+ config_msg->valid_mask_flag = db_instance->valid_mask_flag;
+
+ for (valid = 0; valid < NSS_MATCH_MASK_MAX; valid++) {
+ if (db_instance->valid_mask_flag & (1 << valid)) {
+ for (index = 0; index < NSS_MATCH_MASK_WORDS_MAX; index++) {
+ config_msg->maskset[valid][index] = db_instance->maskset[valid][index];
+ }
+ }
+ }
+
+ *if_num = db_instance->if_num;
+
+ spin_unlock_bh(&match_db.db_lock);
+
+ return true;
+}
+
+/*
+ * nss_match_db_instance_count_get()
+ * Get total number of existing match instance.
+ */
+int nss_match_db_instance_count_get(void)
+{
+ int count;
+
+ spin_lock_bh(&match_db.db_lock);
+ count = match_db.instance_count;
+ spin_unlock_bh(&match_db.db_lock);
+
+ return count;
+}
+
+/*
+ * nss_match_db_rule_count_get()
+ * Get total number of rule pr instance.
+ */
+int nss_match_db_rule_count_get(uint32_t table_id)
+{
+ int count;
+ struct nss_match_instance *db_instance;
+
+ spin_lock_bh(&match_db.db_lock);
+
+ db_instance = nss_match_db_get_instance_by_table_id(table_id - 1);
+ if (!db_instance) {
+ nss_match_warn("Invalid table index, failed to get DB instance: %d\n", table_id);
+ spin_unlock_bh(&match_db.db_lock);
+ return -1;
+ }
+
+ count = db_instance->rule_count;
+ spin_unlock_bh(&match_db.db_lock);
+ return count;
+}
+
+/*
+ * nss_match_stats_table_sync()
+ * Debug stats sync for match.
+ */
+void nss_match_stats_table_sync(struct nss_ctx_instance *nss_ctx, struct nss_match_stats_sync *stats_msg, uint16_t if_num)
+{
+ int index, table_id;
+ struct nss_match_instance *db_instance;
+
+ table_id = nss_match_get_table_id_by_ifnum(if_num);
+ if (table_id <= 0 || table_id > NSS_MATCH_INSTANCE_MAX) {
+ nss_match_warn("Invalid table id: %d\n", table_id);
+ return;
+ }
+
+ spin_lock_bh(&match_db.db_lock);
+
+ db_instance = nss_match_db_get_instance_by_table_id(table_id - 1);
+ if (!db_instance) {
+ nss_match_warn("Invalid if_num: %d, table index: %d, failed to get DB instance. \n", if_num, table_id);
+ spin_unlock_bh(&match_db.db_lock);
+ return;
+ }
+
+ db_instance->stats.pstats.rx_packets += stats_msg->p_stats.rx_packets;
+ db_instance->stats.pstats.rx_bytes += stats_msg->p_stats.rx_bytes;
+ db_instance->stats.pstats.tx_packets += stats_msg->p_stats.tx_packets;
+ db_instance->stats.pstats.tx_bytes += stats_msg->p_stats.tx_bytes;
+
+ for (index = 0; index < NSS_MAX_NUM_PRI; index++) {
+ db_instance->stats.pstats.rx_dropped[index] += stats_msg->p_stats.rx_dropped[index];
+ }
+
+ for (index = 0; index < NSS_MATCH_INSTANCE_RULE_MAX; index++) {
+ db_instance->stats.hit_count[index] += stats_msg->hit_count[index];
+ }
+
+ spin_unlock_bh(&match_db.db_lock);
+}
+
+/*
+ * nss_match_db_instance_disable
+ * Disable the match instance.
+ */
+bool nss_match_db_instance_disable(uint32_t table_id)
+{
+ struct nss_match_instance *db_instance;
+
+ spin_lock_bh(&match_db.db_lock);
+
+ db_instance = nss_match_db_get_instance_by_table_id(table_id - 1);
+ if (!db_instance) {
+ nss_match_warn("Invalid table index, failed to get DB instance: %d\n", table_id);
+ spin_unlock_bh(&match_db.db_lock);
+ return false;
+ }
+
+ db_instance->is_configured = false;
+ spin_unlock_bh(&match_db.db_lock);
+
+ return true;
+}
+
+/*
+ * nss_match_db_instance_enable()
+ * Enable the match instance.
+ */
+bool nss_match_db_instance_enable(uint32_t table_id)
+{
+ struct nss_match_instance *db_instance;
+
+ spin_lock_bh(&match_db.db_lock);
+
+ db_instance = nss_match_db_get_instance_by_table_id(table_id - 1);
+ if (!db_instance) {
+ nss_match_warn("Invalid table index, failed to get DB instance: %d\n", table_id);
+ spin_unlock_bh(&match_db.db_lock);
+ return false;
+ }
+
+ db_instance->is_configured = true;
+ spin_unlock_bh(&match_db.db_lock);
+
+ return true;
+}
+
+/*
+ * nss_match_profile_ops_register()
+ * Registers match ops according to profile type.
+ */
+bool nss_match_profile_ops_register(uint32_t type, struct match_profile_ops *mops)
+{
+ if (type >= NSS_MATCH_PROFILE_TYPE_MAX) {
+ nss_match_warn("Invalid profile type: %d", type);
+ return false;
+ }
+
+ profile_ops[type] = mops;
+ nss_match_info("Match ops added for profile type: %d", type);
+
+ return true;
+}
+
+/*
+ * nss_match_db_init()
+ * Initializes DB.
+ */
+void nss_match_db_init(void)
+{
+ match_db.instance_count = 0;
+ spin_lock_init(&match_db.db_lock);
+ nss_match_info("db init successful.\n");
+}
diff --git a/match/nss_match_db.h b/match/nss_match_db.h
new file mode 100644
index 0000000..7a02b05
--- /dev/null
+++ b/match/nss_match_db.h
@@ -0,0 +1,126 @@
+/*
+ *******************************************************************************
+ * 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.
+ ********************************************************************************
+ **/
+
+#ifndef __NSS_MATCH_DB_H
+#define __NSS_MATCH_DB_H
+
+#include "nss_match_stats.h"
+#include "nss_match_cmd.h"
+
+/*
+ * nss_match_vow_rule_info
+ * VoW profile rule to store.
+ */
+struct nss_match_vow_rule_info {
+ struct nss_match_rule_vow_msg rule;
+ bool valid_rule;
+};
+
+/*
+ * nss_match_l2_rule_info
+ * L2 profile rule to store.
+ */
+struct nss_match_l2_rule_info {
+ struct nss_match_rule_l2_msg rule;
+ bool valid_rule;
+};
+
+/*
+ * nss_match_instance
+ * Match instance information.
+ */
+struct nss_match_instance {
+ union {
+ struct nss_match_vow_rule_info vow[NSS_MATCH_INSTANCE_RULE_MAX];
+ struct nss_match_l2_rule_info l2[NSS_MATCH_INSTANCE_RULE_MAX];
+ } rules;
+
+ struct nss_match_stats stats;
+ struct match_profile_ops *ops;
+ uint32_t valid_mask_flag;
+ uint32_t maskset[NSS_MATCH_MASK_MAX][NSS_MATCH_MASK_WORDS_MAX]; /* Maskset. */
+ uint32_t profile_type;
+ uint32_t if_num;
+ uint16_t rule_count;
+ bool is_configured;
+};
+
+/*
+ * nss_match_db
+ * Structure to store all match instance information.
+ */
+struct nss_match_db {
+ struct nss_match_instance *instance[NSS_MATCH_INSTANCE_MAX]; /* Pointer to each match instance database. */
+ spinlock_t db_lock; /* Spin lock to protect database. */
+ int8_t instance_count; /* Match instance count. */
+};
+
+/*
+ * match_profile_ops
+ * Operations to perform on match db.
+ */
+struct match_profile_ops {
+
+ /* Generate rule id for adding the rule. */
+ int (*nss_match_rule_id_generate)(struct nss_match_instance *db_instance);
+
+ /* Check if rule exists already in database. */
+ bool (*nss_match_rule_find)(struct nss_match_instance *db_instance, void *rule);
+
+ /* Add match rule into database. */
+ bool (*nss_match_rule_add)(struct nss_match_instance *db_instance, void *rule);
+
+ /* Delete match rule from database. */
+ bool (*nss_match_rule_delete)(struct nss_match_instance *db_instance, uint32_t rule_id);
+
+ /* Get match rules details from database. */
+ bool (*nss_match_rule_read)(struct nss_match_instance *db_instance, struct nss_match_msg *delete_rule, uint16_t rule_id);
+
+ /* Read table details. */
+ size_t (*nss_match_table_read)(struct nss_match_instance *db_instance, size_t buflen, char *bufp);
+
+ /* Parse command line arguements. */
+ int (*nss_match_cmd_parse)(char *input_msg, struct nss_match_msg *rule_msg, nss_match_cmd_t type);
+};
+
+int nss_match_table_count_get(void);
+int nss_match_db_rule_count_get(uint32_t table_id);
+int nss_match_get_table_id_by_ifnum(int if_num);
+bool nss_match_db_table_validate(int table_id);
+bool nss_match_db_get_profile_type(uint32_t table_id, uint32_t *profile_type);
+bool nss_match_db_table_destroy(int table_id);
+int nss_match_db_table_create(int if_num);
+bool nss_match_db_mask_add(struct nss_match_profile_configure_msg *config_msg, uint8_t table_id);
+bool nss_match_db_instance_config_get(struct nss_match_profile_configure_msg *config_msg, int *if_num, uint8_t table_id);
+bool nss_match_db_instance_enable(uint32_t table_id);
+bool nss_match_db_instance_disable(uint32_t table_id);
+int nss_match_db_instance_count_get(void);
+bool nss_match_db_profile_type_add(uint32_t profile_type, uint8_t table_id);
+int nss_match_db_generate_rule_id(uint32_t table_id);
+bool nss_match_db_rule_find(void *rule, uint32_t table_id);
+bool nss_match_db_rule_add(void *rule, uint8_t table_id);
+bool nss_match_db_rule_delete(uint32_t table_id, uint32_t rule_id);
+bool nss_match_db_rule_read(struct nss_match_msg *delete_rule, uint32_t table_id, uint16_t rule_id);
+int nss_match_db_parse_cmd(uint32_t table_id, char *input_msg, struct nss_match_msg *rule_msg, nss_match_cmd_t type);
+void nss_match_stats_table_sync(struct nss_ctx_instance *nss_ctx, struct nss_match_stats_sync *stats_msg, uint16_t if_num);
+size_t nss_match_db_table_read(uint32_t table_id, size_t buflen, char *bufp);
+bool nss_match_db_stats_get(uint32_t table_id, struct nss_match_stats *match_stats);
+void nss_match_db_init(void);
+bool nss_match_profile_ops_register(uint32_t type, struct match_profile_ops *mops);
+
+#endif /* __NSS_MATCH_DB_H */
diff --git a/match/nss_match_l2.c b/match/nss_match_l2.c
new file mode 100644
index 0000000..c1d71c2
--- /dev/null
+++ b/match/nss_match_l2.c
@@ -0,0 +1,463 @@
+/*
+ *******************************************************************************
+ * 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 "nss_match_db.h"
+#include "nss_match_priv.h"
+
+#define MATCH_L2_KEY0_IFNUM_SHIFT 0
+#define MATCH_L2_KEY0_DMAC_HW0_SHIFT 16
+#define MATCH_L2_KEY1_DMAC_HW1_SHIFT 0
+#define MATCH_L2_KEY1_DMAC_HW2_SHIFT 16
+#define MATCH_L2_KEY2_SMAC_HW0_SHIFT 0
+#define MATCH_L2_KEY2_SMAC_HW1_SHIFT 16
+#define MATCH_L2_KEY3_SMAC_HW2_SHIFT 0
+#define MATCH_L2_KEY3_ETHERTYPE_SHIFT 16
+
+/*
+ * nss_match_l2_rule_id_generate()
+ * Check if any slot is available for new entry.
+ */
+static int nss_match_l2_rule_id_generate(struct nss_match_instance *db_instance)
+{
+ uint16_t index;
+
+ for (index = 0; index < NSS_MATCH_INSTANCE_RULE_MAX; index++) {
+ if (db_instance->rules.l2[index].valid_rule) {
+ continue;
+ }
+
+ return (index + 1);
+ }
+
+ nss_match_warn("Rule table full with NSS_MATCH_INSTANCE_RULE_MAX:%d entries.\n",
+ NSS_MATCH_INSTANCE_RULE_MAX);
+ return -1;
+}
+
+/*
+ * nss_match_l2_rule_find()
+ * Check if any slot is available for new entry.
+ */
+static bool nss_match_l2_rule_find(struct nss_match_instance *db_instance, void *rule)
+{
+ uint16_t index;
+ struct nss_match_rule_l2_msg *l2_rule = (struct nss_match_rule_l2_msg *)rule;
+
+ /*
+ * Check if entry is duplicate.
+ */
+ for (index = 0; index < NSS_MATCH_INSTANCE_RULE_MAX; index++) {
+ struct nss_match_l2_rule_info rule_info = db_instance->rules.l2[index];
+
+ if (rule_info.valid_rule &&
+ rule_info.rule.if_num == l2_rule->if_num &&
+ rule_info.rule.ethertype == l2_rule->ethertype &&
+ rule_info.rule.smac[0] == l2_rule->smac[0] &&
+ rule_info.rule.smac[1] == l2_rule->smac[1] &&
+ rule_info.rule.smac[2] == l2_rule->smac[2] &&
+ rule_info.rule.dmac[0] == l2_rule->dmac[0] &&
+ rule_info.rule.dmac[1] == l2_rule->dmac[1] &&
+ rule_info.rule.dmac[2] == l2_rule->dmac[2] &&
+ rule_info.rule.mask_id == l2_rule->mask_id) {
+ nss_match_info("Rule matched\n");
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/*
+ * nss_match_l2_db_rule_add()
+ * Store L2 rule information.
+ */
+static bool nss_match_l2_db_rule_add(struct nss_match_instance *db_instance, void *rule)
+{
+ struct nss_match_rule_l2_msg *l2_rule = (struct nss_match_rule_l2_msg *)rule;
+ uint8_t rule_id = l2_rule->rule_id;
+
+ if (rule_id == 0 || rule_id > NSS_MATCH_INSTANCE_RULE_MAX) {
+ nss_match_warn("Invalid rule id: %d\n", rule_id);
+ return false;
+ }
+
+ if (db_instance->rules.l2[rule_id - 1].valid_rule) {
+ nss_match_warn("Rule exists for rule id: %d\n", rule_id);
+ return false;
+ }
+
+ db_instance->rules.l2[rule_id - 1].rule.if_num = l2_rule->if_num;
+ db_instance->rules.l2[rule_id - 1].rule.smac[0] = l2_rule->smac[0];
+ db_instance->rules.l2[rule_id - 1].rule.smac[1] = l2_rule->smac[1];
+ db_instance->rules.l2[rule_id - 1].rule.smac[2] = l2_rule->smac[2];
+ db_instance->rules.l2[rule_id - 1].rule.dmac[0] = l2_rule->dmac[0];
+ db_instance->rules.l2[rule_id - 1].rule.dmac[1] = l2_rule->dmac[1];
+ db_instance->rules.l2[rule_id - 1].rule.dmac[2] = l2_rule->dmac[2];
+ db_instance->rules.l2[rule_id - 1].rule.ethertype = l2_rule->ethertype;
+ db_instance->rules.l2[rule_id - 1].rule.mask_id = l2_rule->mask_id;
+ db_instance->rules.l2[rule_id - 1].rule.action.action_flag = l2_rule->action.action_flag;
+ db_instance->rules.l2[rule_id - 1].rule.action.setprio = l2_rule->action.setprio;
+ db_instance->rules.l2[rule_id - 1].rule.action.forward_ifnum = l2_rule->action.forward_ifnum;
+ db_instance->rules.l2[rule_id - 1].valid_rule = true;
+ db_instance->rules.l2[rule_id - 1].rule.rule_id = rule_id;
+ db_instance->rule_count++;
+
+ return true;
+}
+
+/*
+ * nss_match_l2_db_rule_delete()
+ * Clears stored rule information.
+ */
+static bool nss_match_l2_db_rule_delete(struct nss_match_instance *db_instance, uint32_t rule_id)
+{
+ if (rule_id == 0 || rule_id > NSS_MATCH_INSTANCE_RULE_MAX) {
+ nss_match_warn("Invalid rule id: %d\n", rule_id);
+ return false;
+ }
+
+ if (!(db_instance->rules.l2[rule_id - 1].valid_rule)) {
+ nss_match_warn("Rule dosn't exist for rule id: %d\n", rule_id);
+ return false;
+ }
+
+ db_instance->rule_count--;
+ db_instance->stats.hit_count[rule_id - 1] = 0;
+ memset(db_instance->rules.l2 + rule_id - 1, 0, sizeof(struct nss_match_l2_rule_info));
+
+ return true;
+}
+
+/*
+ * nss_match_l2_rule_read()
+ * Reads rule parameters by rule id.
+ */
+static bool nss_match_l2_rule_read(struct nss_match_instance *db_instance, struct nss_match_msg *rule, uint16_t rule_id)
+{
+ if (!db_instance->rules.l2[rule_id - 1].valid_rule) {
+ nss_match_warn("rule_id doesnot exist, rule_id = %d", rule_id);
+ return false;
+ }
+
+ rule->msg.l2_rule.if_num = db_instance->rules.l2[rule_id - 1].rule.if_num;
+ rule->msg.l2_rule.smac[0] = db_instance->rules.l2[rule_id - 1].rule.smac[0];
+ rule->msg.l2_rule.smac[1] = db_instance->rules.l2[rule_id - 1].rule.smac[1];
+ rule->msg.l2_rule.smac[2] = db_instance->rules.l2[rule_id - 1].rule.smac[2];
+ rule->msg.l2_rule.dmac[0] = db_instance->rules.l2[rule_id - 1].rule.dmac[0];
+ rule->msg.l2_rule.dmac[1] = db_instance->rules.l2[rule_id - 1].rule.dmac[1];
+ rule->msg.l2_rule.dmac[2] = db_instance->rules.l2[rule_id - 1].rule.dmac[2];
+ rule->msg.l2_rule.ethertype = db_instance->rules.l2[rule_id - 1].rule.ethertype;
+ rule->msg.l2_rule.mask_id = db_instance->rules.l2[rule_id - 1].rule.mask_id;
+ rule->msg.l2_rule.rule_id = db_instance->rules.l2[rule_id - 1].rule.rule_id;
+
+ return true;
+}
+
+/*
+ * nss_match_l2_cmd_parse()
+ * Adds new rules to the list
+ */
+static int nss_match_l2_cmd_parse(char *input_msg, struct nss_match_msg *rule_msg, nss_match_cmd_t type)
+{
+ char *token, *param;
+ struct nss_ctx_instance *nss_ctx = nss_match_get_context();
+ int ret = 0;
+ uint32_t mask_val[4] = {0};
+ uint32_t actions = 0, if_num = 0, setprio = 0, nexthop = 0;
+ uint16_t smac[3] = {0}, dmac[3] = {0}, mask_id = 0, ethertype = 0;
+ char tmp[4];
+
+ while (input_msg != NULL) {
+ token = strsep(&input_msg, " ");
+ param = strsep(&token, "=");
+ if (!param || !token) {
+ goto fail;
+ }
+
+ if (!(strncasecmp(param, "mask", strlen("mask")))) {
+ if (!sscanf(token, "%hu", &mask_id)) {
+ nss_match_warn("%p: Cannot convert to integer. Wrong input\n", nss_ctx);
+ return -EINVAL;
+ }
+
+ if (mask_id > NSS_MATCH_MASK_MAX) {
+ nss_match_warn("%p: Maskset num %d, exceeds max allowed value %d\n", nss_ctx, mask_id, NSS_MATCH_MASK_MAX);
+ return -EINVAL;
+ }
+
+ continue;
+ }
+
+ if (!(strncasecmp(param, "ifname", strlen("ifname")))) {
+ struct net_device *dev;
+ if (type == NSS_MATCH_ADD_MASK) {
+ if (!sscanf(token, "%x", &if_num)) {
+ nss_match_warn("%p: Cannot convert to integer. Wrong input\n", nss_ctx);
+ return -EINVAL;
+ }
+
+ continue;
+ }
+
+ if (type == NSS_MATCH_ADD_RULE) {
+ dev = dev_get_by_name(&init_net, token);
+ if (!dev) {
+ nss_match_warn("%p: Cannot find the net device\n", nss_ctx);
+ return -ENODEV;
+ }
+
+ if_num = nss_cmn_get_interface_number_by_dev(dev);
+ dev_put(dev);
+ continue;
+ }
+ }
+
+ if (!(strncasecmp(param, "smac", strlen("smac")))) {
+ tmp[0] = token[0];
+ tmp[1] = token[1];
+ tmp[2] = token[2];
+ tmp[3] = token[3];
+ sscanf(tmp, "%hx", &smac[0]);
+
+ tmp[0] = token[4];
+ tmp[1] = token[5];
+ tmp[2] = token[6];
+ tmp[3] = token[7];
+ sscanf(tmp, "%hx", &smac[1]);
+
+ tmp[0] = token[8];
+ tmp[1] = token[9];
+ tmp[2] = token[10];
+ tmp[3] = token[11];
+ sscanf(tmp, "%hx", &smac[2]);
+
+ nss_match_info("%p: src mac %x %x %x ", nss_ctx, smac[0], smac[1], smac[2]);
+ continue;
+ }
+
+ if (!(strncasecmp(param, "dmac", strlen("dmac")))) {
+ tmp[0] = token[0];
+ tmp[1] = token[1];
+ tmp[2] = token[2];
+ tmp[3] = token[3];
+ sscanf(tmp, "%hx", &dmac[0]);
+
+ tmp[0] = token[4];
+ tmp[1] = token[5];
+ tmp[2] = token[6];
+ tmp[3] = token[7];
+ sscanf(tmp, "%hx", &dmac[1]);
+
+ tmp[0] = token[8];
+ tmp[1] = token[9];
+ tmp[2] = token[10];
+ tmp[3] = token[11];
+ sscanf(tmp, "%hx", &dmac[2]);
+
+ nss_match_info("%p: dest mac %x %x %x ", nss_ctx, dmac[0], dmac[1], dmac[2]);
+ continue;
+ }
+
+ if (!(strncasecmp(param, "ethertype", strlen("ethertype")))) {
+ if (type == NSS_MATCH_ADD_RULE) {
+ ret = sscanf(token, "%hu", ðertype);
+ } else if (type == NSS_MATCH_ADD_MASK) {
+ ret = sscanf(token, "%hx", ðertype);
+ }
+
+ if (!ret) {
+ nss_match_warn("%p: Cannot convert to integer. Wrong input\n", nss_ctx);
+ return -EINVAL;
+ }
+
+ continue;
+ }
+
+ /*
+ * Parsing action from the message provided by user.
+ */
+ if (!(strncasecmp(param, "action", strlen("action")))) {
+ if (!sscanf(token, "%u", &actions)) {
+ nss_match_warn("%p: Cannot convert to integer. Wrong input\n", nss_ctx);
+ return -EINVAL;
+ }
+
+ if (actions >= NSS_MATCH_ACTION_MAX ) {
+ nss_match_warn("Invalid action type: %d", actions);
+ }
+
+ if (actions == 1 || actions == 3) {
+ token = strsep(&input_msg, " ");
+ param = strsep(&token, "=");
+ if (!token || !param) {
+ goto fail;
+ }
+
+ if (!(strncasecmp(param, "priority", strlen("priority")))) {
+ if (!sscanf(token, "%u", &setprio)) {
+ nss_match_warn("%p: Cannot convert to integer. Wrong input\n", nss_ctx);
+ return -EINVAL;
+ }
+
+ if (setprio >= NSS_MAX_NUM_PRI) {
+ nss_match_warn("Invalid priority: %d", setprio);
+ }
+
+ }
+ }
+
+ if (actions == 2 || actions == 3) {
+ token = strsep(&input_msg, " ");
+ param = strsep(&token, "=");
+ if (!token || !param) {
+ goto fail;
+ }
+
+ if (!(strncasecmp(param, "nexthop", strlen("nexthop")))) {
+ if (!sscanf(token, "%u", &nexthop)) {
+ nss_match_warn("%p: Cannot convert to integer. Wrong input\n", nss_ctx);
+ return -EINVAL;
+ }
+ }
+ }
+
+ continue;
+ }
+
+ nss_match_warn("%p: Not a valid input\n", nss_ctx);
+ return -EINVAL;
+ }
+
+ switch (type) {
+ case NSS_MATCH_ADD_RULE:
+ if (!mask_id || !actions) {
+ goto fail;
+ }
+
+ rule_msg->msg.l2_rule.if_num = if_num;
+ rule_msg->msg.l2_rule.smac[0] = smac[0];
+ rule_msg->msg.l2_rule.smac[1] = smac[1];
+ rule_msg->msg.l2_rule.smac[2] = smac[2];
+ rule_msg->msg.l2_rule.dmac[0] = dmac[0];
+ rule_msg->msg.l2_rule.dmac[1] = dmac[1];
+ rule_msg->msg.l2_rule.dmac[2] = dmac[2];
+ rule_msg->msg.l2_rule.ethertype = ethertype;
+ rule_msg->msg.l2_rule.mask_id = mask_id;
+ rule_msg->msg.l2_rule.action.setprio = setprio;
+ rule_msg->msg.l2_rule.action.action_flag = actions;
+ rule_msg->msg.l2_rule.action.forward_ifnum = nexthop;
+ break;
+ case NSS_MATCH_ADD_MASK:
+ mask_val[0] = (if_num << MATCH_L2_KEY0_IFNUM_SHIFT);
+ mask_val[0] |= (dmac[0] << MATCH_L2_KEY0_DMAC_HW0_SHIFT);
+ mask_val[1] = (dmac[1] << MATCH_L2_KEY1_DMAC_HW1_SHIFT);
+ mask_val[1] |= (dmac[2] << MATCH_L2_KEY1_DMAC_HW2_SHIFT);
+ mask_val[2] = (smac[0] << MATCH_L2_KEY2_SMAC_HW0_SHIFT);
+ mask_val[2] |= (smac[1] << MATCH_L2_KEY2_SMAC_HW1_SHIFT);
+ mask_val[3] = (smac[2] << MATCH_L2_KEY3_SMAC_HW2_SHIFT);
+ mask_val[3] |= (ethertype << MATCH_L2_KEY3_ETHERTYPE_SHIFT);
+
+ rule_msg->msg.configure_msg.maskset[mask_id-1][0] = mask_val[0];
+ rule_msg->msg.configure_msg.maskset[mask_id-1][1] = mask_val[1];
+ rule_msg->msg.configure_msg.maskset[mask_id-1][2] = mask_val[2];
+ rule_msg->msg.configure_msg.maskset[mask_id-1][3] = mask_val[3];
+ rule_msg->msg.configure_msg.valid_mask_flag = mask_id;
+ break;
+ default:
+ nss_match_warn("Invalid parse type: %d", type);
+ return -EINVAL;
+ }
+
+ return 0;
+
+fail:
+ pr_warn("Invalid input, Check help: cat /sys/kernel/debug/match/help");
+ return -EINVAL;
+}
+
+/*
+ * nss_match_l2_table_read()
+ * Reads stats for l2 profile.
+ */
+static size_t nss_match_l2_table_read(struct nss_match_instance *db_instance, size_t buflen, char *bufp) {
+ int i, j;
+ struct nss_ctx_instance *nss_ctx = nss_match_get_context();
+ struct net_device *net_dev;
+ size_t size_wr = 0;
+ char *dev_name;
+
+ size_wr += scnprintf(bufp + size_wr, buflen - size_wr, "\nMatch if_num = %d\n", db_instance->if_num);
+ size_wr += scnprintf(bufp + size_wr, buflen - size_wr, "\nProfile Type = %d\n", db_instance->profile_type);
+
+ for (i = 0; i < NSS_MATCH_MASK_MAX; i++) {
+ if (!(db_instance->valid_mask_flag & (1 << i))) {
+ size_wr += scnprintf(bufp + size_wr, buflen - size_wr, "Mask %d = Invalid\n\n", i+1);
+ continue;
+ }
+
+ size_wr += scnprintf(bufp + size_wr, buflen - size_wr, "Mask %d =%x %x %x %x \n\n", i+1, db_instance->maskset[i][3],
+ db_instance->maskset[i][2], db_instance->maskset[i][1], db_instance->maskset[i][0]);
+ }
+
+ size_wr += scnprintf(bufp + size_wr, buflen - size_wr, "rule_id\t\t hit_count\t\t mask_id\t\t if_name\t\t DMAC \t\t\t SMAC \t\t\t ethertype\t\t action\t\t priority\t\t nexthop\n");
+ for (j = 0; j < NSS_MATCH_INSTANCE_RULE_MAX; j++) {
+ if (!db_instance->rules.l2[j].valid_rule)
+ continue;
+
+ dev_name = "N/A";
+ net_dev = nss_cmn_get_interface_dev(nss_ctx, db_instance->rules.l2[j].rule.if_num);
+ if (net_dev) {
+ dev_name = net_dev->name;
+ }
+
+ size_wr += scnprintf(bufp + size_wr, buflen - size_wr, "%d\t\t %llu\t\t %d\t\t %s\t\t%pM %pM\t\t %d\t\t %u\t\t %d\t\t %d\n",
+ j + 1,
+ db_instance->stats.hit_count[j],
+ db_instance->rules.l2[j].rule.mask_id,
+ dev_name,
+ &db_instance->rules.l2[j].rule.dmac,
+ &db_instance->rules.l2[j].rule.smac,
+ db_instance->rules.l2[j].rule.ethertype,
+ db_instance->rules.l2[j].rule.action.action_flag,
+ db_instance->rules.l2[j].rule.action.setprio,
+ db_instance->rules.l2[j].rule.action.forward_ifnum);
+ }
+
+ return size_wr;
+}
+
+/*
+ * Match ops for L2 profile.
+ */
+static struct match_profile_ops match_profile_ops_l2 = {
+ nss_match_l2_rule_id_generate,
+ nss_match_l2_rule_find,
+ nss_match_l2_db_rule_add,
+ nss_match_l2_db_rule_delete,
+ nss_match_l2_rule_read,
+ nss_match_l2_table_read,
+ nss_match_l2_cmd_parse,
+};
+
+
+void nss_match_l2_init(void) {
+ /*
+ * Register the L2 profile ops.
+ */
+ nss_match_profile_ops_register(NSS_MATCH_PROFILE_TYPE_L2, &match_profile_ops_l2);
+ nss_match_info("L2 profile initialization done\n");
+}
diff --git a/match/nss_match_l2.h b/match/nss_match_l2.h
new file mode 100644
index 0000000..40ef8f8
--- /dev/null
+++ b/match/nss_match_l2.h
@@ -0,0 +1,24 @@
+/*
+ *******************************************************************************
+ * 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.
+ ********************************************************************************
+ **/
+
+#ifndef __NSS_MATCH_L2_H
+#define __NSS_MATCH_L2_H
+
+void nss_match_l2_init(void);
+
+#endif /* __NSS_MATCH_L2_H */
diff --git a/match/nss_match_priv.h b/match/nss_match_priv.h
new file mode 100644
index 0000000..4386696
--- /dev/null
+++ b/match/nss_match_priv.h
@@ -0,0 +1,51 @@
+/*
+ **************************************************************************
+ * 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 "nss_match_user.h"
+
+/*
+ * Compile messages for dynamic enable/disable
+ */
+#if defined(CONFIG_DYNAMIC_DEBUG)
+#define nss_match_warn(s, ...) pr_debug("%s[%d]:" s, __FUNCTION__, __LINE__, ##__VA_ARGS__)
+#define nss_match_info(s, ...) pr_debug("%s[%d]:" s, __FUNCTION__, __LINE__, ##__VA_ARGS__)
+#define nss_match_trace(s, ...) pr_debug("%s[%d]:" s, __FUNCTION__, __LINE__, ##__VA_ARGS__)
+#else /* CONFIG_DYNAMIC_DEBUG */
+/*
+ * Statically compile messages at different levels
+ */
+#if (NSS_match_DEBUG_LEVEL < 2)
+#define nss_match_warn(s, ...)
+#else
+#define nss_match_warn(s, ...) pr_warn("%s[%d]:" s, __FUNCTION__, __LINE__, ##__VA_ARGS__)
+#endif
+
+#if (NSS_match_DEBUG_LEVEL < 3)
+#define nss_match_info(s, ...)
+#else
+#define nss_match_info(s, ...) pr_notice("%s[%d]:" s, __FUNCTION__, __LINE__, ##__VA_ARGS__)
+#endif
+
+#if (NSS_match_DEBUG_LEVEL < 4)
+#define nss_match_trace(s, ...)
+#else
+#define nss_match_trace(s, ...) pr_info("%s[%d]:" s, __FUNCTION__, __LINE__, ##__VA_ARGS__)
+#endif
+#endif /* CONFIG_DYNAMIC_DEBUG */
+
+
diff --git a/match/nss_match_stats.c b/match/nss_match_stats.c
new file mode 100644
index 0000000..1f3657c
--- /dev/null
+++ b/match/nss_match_stats.c
@@ -0,0 +1,183 @@
+/*
+ **************************************************************************
+ * 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/types.h>
+#include <nss_api_if.h>
+#include <linux/debugfs.h>
+#include "nss_match_db.h"
+#include "nss_match_priv.h"
+
+/*
+ * nss_match_stats_str
+ * MATCH statistics strings for nss MATCH stats
+ */
+static int8_t *nss_match_stats_str[NSS_MATCH_STATS_INSTANCE_MAX] = {
+ "rx_packets",
+ "rx_bytes",
+ "tx_packets",
+ "tx_bytes",
+ "rx_queue_0_drop",
+ "rx_queue_1_drop",
+ "rx_queue_2_drop",
+ "rx_queue_3_drop",
+};
+
+/*
+ * nss_match_stats_read()
+ * Read MATCH statistics
+ */
+static ssize_t nss_match_stats_read(struct file *fp, char __user *ubuf, size_t sz, loff_t *ppos)
+{
+ int instance_count = nss_match_table_count_get();
+ uint32_t max_output_lines = 2 /* header & footer for instance stats */
+ + instance_count * (NSS_MATCH_STATS_INSTANCE_MAX + 2) /*instance stats */
+ + 2;
+ size_t size_al = NSS_STATS_MAX_STR_LENGTH * max_output_lines;
+ size_t size_wr = 0;
+ int id, i;
+ ssize_t bytes_read = 0;
+ struct nss_match_stats match_stats = {0};
+ char *lbuf = kzalloc(size_al, GFP_KERNEL);
+ if (unlikely(lbuf == NULL)) {
+ nss_match_warn("Could not allocate memory for local statistics buffer\n");
+ return 0;
+ }
+
+ /*
+ * Session stats
+ */
+ size_wr += scnprintf(lbuf + size_wr, size_al - size_wr, "\nmatch instance stats start:\n\n");
+ for (id = 1; id <= NSS_MATCH_INSTANCE_MAX; id++) {
+ if (!nss_match_db_table_validate(id)) {
+ continue;
+ }
+
+ if (!nss_match_db_stats_get(id, &match_stats)) {
+ nss_match_warn("Could not read stats for table_id = %d\n", id);
+ continue;
+ }
+
+ size_wr += scnprintf(lbuf + size_wr, size_al - size_wr, "Table_id= %d\n\n", id);
+
+ size_wr += scnprintf(lbuf + size_wr, size_al - size_wr, "if_num of match instance = %d\n\n", nss_match_get_ifnum_by_table_id(id));
+
+ size_wr += scnprintf(lbuf + size_wr, size_al - size_wr, "%s = %llu\n", nss_match_stats_str[NSS_MATCH_STATS_RX_PACKETS],
+ match_stats.pstats.rx_packets);
+ size_wr += scnprintf(lbuf + size_wr, size_al - size_wr, "%s = %llu\n", nss_match_stats_str[NSS_MATCH_STATS_RX_BYTES],
+ match_stats.pstats.rx_bytes);
+ size_wr += scnprintf(lbuf + size_wr, size_al - size_wr, "%s = %llu\n", nss_match_stats_str[NSS_MATCH_STATS_TX_PACKETS],
+ match_stats.pstats.tx_packets);
+ size_wr += scnprintf(lbuf + size_wr, size_al - size_wr, "%s = %llu\n", nss_match_stats_str[NSS_MATCH_STATS_TX_BYTES],
+ match_stats.pstats.tx_bytes);
+ for (i = 0; i < NSS_MAX_NUM_PRI ; ++i) {
+ size_wr += scnprintf(lbuf + size_wr, size_al - size_wr, "%s = %llu\n",
+ nss_match_stats_str[i + NSS_MATCH_STATS_RX_QUEUE_0_DROP],
+ match_stats.pstats.rx_dropped[i]);
+ }
+
+ for (i = 0; i < NSS_MATCH_INSTANCE_RULE_MAX; ++i) {
+ if (match_stats.hit_count[i] <= 0) {
+ continue;
+ }
+ size_wr += scnprintf(lbuf + size_wr, size_al - size_wr,
+ "Hit count of rule ID %d = %llu\n", i+1,
+ match_stats.hit_count[i]);
+ }
+
+ size_wr += scnprintf(lbuf + size_wr, size_al - size_wr, "\n");
+ }
+
+ size_wr += scnprintf(lbuf + size_wr, size_al - size_wr, "\nMatch instance stats end\n");
+ bytes_read = simple_read_from_buffer(ubuf, sz, ppos, lbuf, size_wr);
+
+ kfree(lbuf);
+ return bytes_read;
+}
+
+/*
+ * nss_match_stats_table_read()
+ * Read match table entry
+ */
+static ssize_t nss_match_stats_table_read(struct file *fp, char __user *ubuf, size_t sz, loff_t *ppos)
+{
+ /*
+ * TODO: Optimize buffer size needed for table information.
+ */
+ uint32_t max_output_lines = 2 + NSS_MATCH_INSTANCE_MAX * 2 /* for 2 maskset */
+ + NSS_MATCH_INSTANCE_MAX * NSS_MATCH_INSTANCE_RULE_MAX *(9) /* for: 4 rule fields + 1 rule_id + 3 action fields + 1 hit count*/
+ + 2;
+ int i;
+ size_t size_wr = 0, buflen = 0;
+ size_t size_al = NSS_STATS_MAX_STR_LENGTH * max_output_lines;
+ ssize_t bytes_read = 0;
+ char *lbuf = kzalloc(size_al, GFP_KERNEL);
+ char *bufp;
+ if (unlikely(lbuf == NULL)) {
+ nss_match_warn("%p: Could not allocate memory for local statistics buffer", fp);
+ return 0;
+ }
+
+ for (i = 1; i <= NSS_MATCH_INSTANCE_MAX; ++i) {
+ if (!nss_match_db_table_validate(i)) {
+ continue;
+ }
+ size_wr += scnprintf(lbuf + size_wr, size_al - size_wr, "\nTable_id =%d\n", i);
+ bufp = lbuf + size_wr;
+ buflen = size_al - size_wr;
+ size_wr += nss_match_db_table_read(i, buflen, bufp);
+
+ }
+
+ bytes_read = simple_read_from_buffer(ubuf, sz, ppos, lbuf, size_wr);
+ kfree(lbuf);
+ return bytes_read;
+}
+
+/*
+ * nss_match_stats_show_table_ops
+ */
+static const struct file_operations nss_match_stats_show_table_ops = {
+ .read = nss_match_stats_table_read,
+};
+
+/*
+ * nss_match_stats_ops
+ */
+static const struct file_operations nss_match_stats_ops = {
+ .read = nss_match_stats_read,
+};
+
+/*
+ * nss_match_stats_debugfs_create()
+ * Create MATCH node statistics debug entry.
+ */
+bool nss_match_stats_debugfs_create(struct dentry *match_config)
+{
+ if (!debugfs_create_file("showtable", 0400, match_config, NULL, &nss_match_stats_show_table_ops)) {
+ nss_match_warn("Cannot create match display dentry file");
+ debugfs_remove_recursive(match_config);
+ return false;
+ }
+
+ if (!debugfs_create_file("stats", 0400, match_config, NULL, &nss_match_stats_ops)) {
+ nss_match_warn("Cannot create MATCH dentry file");
+ return false;
+ }
+
+ return true;
+}
diff --git a/match/nss_match_stats.h b/match/nss_match_stats.h
new file mode 100644
index 0000000..ce4ed46
--- /dev/null
+++ b/match/nss_match_stats.h
@@ -0,0 +1,72 @@
+/*
+ *******************************************************************************
+ * 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.
+ ********************************************************************************
+ **/
+
+#ifndef __NSS_MATCH_STATS_H
+#define __NSS_MATCH_STATS_H
+
+#include <nss_api_if.h>
+#include <linux/module.h>
+#include <linux/ctype.h>
+#include <linux/debugfs.h>
+
+#define NSS_STATS_MAX_STR_LENGTH 96
+#define NSS_MATCH_STATS_INSTANCE_MAX NSS_MATCH_STATS_MAX + NSS_MATCH_INSTANCE_RULE_MAX
+
+/*
+ * nss_match_stats_type
+ * Match stats types.
+ */
+enum nss_match_stats_type {
+ NSS_MATCH_STATS_RX_PACKETS,
+ NSS_MATCH_STATS_RX_BYTES,
+ NSS_MATCH_STATS_TX_PACKETS,
+ NSS_MATCH_STATS_TX_BYTES,
+ NSS_MATCH_STATS_RX_QUEUE_0_DROP,
+ NSS_MATCH_STATS_RX_QUEUE_1_DROP,
+ NSS_MATCH_STATS_RX_QUEUE_2_DROP,
+ NSS_MATCH_STATS_RX_QUEUE_3_DROP,
+ NSS_MATCH_STATS_MAX
+};
+
+/*
+ * nss_match_db_node_stats
+ * Common node stats for match.
+ */
+struct nss_match_db_node_stats {
+ uint64_t rx_packets; /* Number of packets received. */
+ uint64_t rx_bytes; /* Number of bytes received. */
+ uint64_t tx_packets; /* Number of packets transmitted. */
+ uint64_t tx_bytes; /* Number of bytes transmitted. */
+ uint64_t rx_dropped[NSS_MAX_NUM_PRI]; /* Packets dropped on receive due to queue full. */
+};
+
+/*
+ * nss_match_stats
+ * Match rule stats.
+ */
+struct nss_match_stats {
+ struct nss_match_db_node_stats pstats;
+ uint64_t hit_count[NSS_MATCH_INSTANCE_RULE_MAX];
+};
+
+/*
+ * NSS match statistics APIs
+ */
+extern bool nss_match_stats_debugfs_create(struct dentry *match_config);
+
+#endif /* __NSS_MATCH_STATS_H */
diff --git a/match/nss_match_vow.c b/match/nss_match_vow.c
new file mode 100644
index 0000000..123415b
--- /dev/null
+++ b/match/nss_match_vow.c
@@ -0,0 +1,469 @@
+/*
+ *******************************************************************************
+ * 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 "nss_match_db.h"
+#include "nss_match_priv.h"
+
+#define MAX_DSCP 63
+#define MAX_PRIORITY 7
+#define NSS_MATCH_VOW_KEY_IFNUM_SHIFT 0
+#define NSS_MATCH_VOW_KEY_DSCP_SHIFT 16
+#define NSS_MATCH_VOW_KEY_INNER_8021P_SHIFT 22
+#define NSS_MATCH_VOW_KEY_OUTER_8021P_SHIFT 25
+
+/*
+ * nss_match_vow_rule_id_generate()
+ * Check if any slot is available for new entry.
+ */
+static int nss_match_vow_rule_id_generate(struct nss_match_instance *db_instance)
+{
+ uint16_t index;
+
+ for (index = 0; index < NSS_MATCH_INSTANCE_RULE_MAX; index++) {
+ if (db_instance->rules.vow[index].valid_rule) {
+ continue;
+ }
+
+ return (index + 1);
+ }
+
+ nss_match_warn("Rule table full with NSS_MATCH_INSTANCE_RULE_MAX:%d entries.\n",
+ NSS_MATCH_INSTANCE_RULE_MAX);
+ return -1;
+}
+
+/*
+ * nss_match_vow_rule_find()
+ * Check if rule exists already.
+ */
+static bool nss_match_vow_rule_find(struct nss_match_instance *db_instance, void *rule)
+{
+ uint16_t index;
+ struct nss_match_rule_vow_msg *vow_rule = (struct nss_match_rule_vow_msg *)rule;
+
+ /*
+ * Check if entry is present already.
+ */
+ for (index = 0; index < NSS_MATCH_INSTANCE_RULE_MAX; index++) {
+ struct nss_match_vow_rule_info rule_info = db_instance->rules.vow[index];
+
+ if (rule_info.valid_rule &&
+ rule_info.rule.if_num == vow_rule->if_num &&
+ rule_info.rule.dscp == vow_rule->dscp &&
+ rule_info.rule.inner_8021p == vow_rule->inner_8021p &&
+ rule_info.rule.outer_8021p == vow_rule->outer_8021p &&
+ rule_info.rule.mask_id == vow_rule->mask_id) {
+ nss_match_info("Rule matched.\n");
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/*
+ * nss_match_vow_db_rule_add()
+ * Store VoW rule information.
+ */
+static bool nss_match_vow_db_rule_add(struct nss_match_instance *db_instance, void *rule)
+{
+ struct nss_match_rule_vow_msg *vow_rule = (struct nss_match_rule_vow_msg *)rule;
+ uint8_t rule_id = vow_rule->rule_id;
+
+ if (rule_id == 0 || rule_id > NSS_MATCH_INSTANCE_RULE_MAX) {
+ nss_match_warn("Invalid rule id: %d\n", rule_id);
+ return false;
+ }
+
+ if (db_instance->rules.vow[rule_id - 1].valid_rule) {
+ nss_match_warn("Rule exists for rule id: %d\n", rule_id);
+ return false;
+ }
+
+ db_instance->rules.vow[rule_id - 1].rule.if_num = vow_rule->if_num;
+ db_instance->rules.vow[rule_id - 1].rule.dscp = vow_rule->dscp;
+ db_instance->rules.vow[rule_id - 1].rule.inner_8021p = vow_rule->inner_8021p;
+ db_instance->rules.vow[rule_id - 1].rule.outer_8021p = vow_rule->outer_8021p;
+ db_instance->rules.vow[rule_id - 1].rule.mask_id = vow_rule->mask_id;
+ db_instance->rules.vow[rule_id - 1].rule.action.action_flag = vow_rule->action.action_flag;
+ db_instance->rules.vow[rule_id - 1].rule.action.setprio = vow_rule->action.setprio;
+ db_instance->rules.vow[rule_id - 1].rule.action.forward_ifnum = vow_rule->action.forward_ifnum;
+ db_instance->rules.vow[rule_id - 1].valid_rule = true;
+ db_instance->rules.vow[rule_id - 1].rule.rule_id = rule_id;
+ db_instance->rule_count++;
+
+ return true;
+}
+
+/*
+ * nss_match_vow_db_rule_delete()
+ * Clears stored rule information.
+ */
+static bool nss_match_vow_db_rule_delete(struct nss_match_instance *db_instance, uint32_t rule_id)
+{
+ if (rule_id == 0 || rule_id > NSS_MATCH_INSTANCE_RULE_MAX) {
+ nss_match_warn("Invalid rule id: %d\n", rule_id);
+ return false;
+ }
+
+ if (!(db_instance->rules.vow[rule_id - 1].valid_rule)) {
+ nss_match_warn("Rule does not exist for rule id: %d\n", rule_id);
+ return false;
+ }
+
+ db_instance->rule_count--;
+ db_instance->stats.hit_count[rule_id - 1] = 0;
+ memset(db_instance->rules.vow + rule_id - 1, 0, sizeof(struct nss_match_vow_rule_info));
+
+ return true;
+}
+
+/*
+ * nss_match_vow_rule_read()
+ * Reads rule parameters by rule id.
+ */
+static bool nss_match_vow_rule_read(struct nss_match_instance *db_instance, struct nss_match_msg *rule, uint16_t rule_id)
+{
+ if (rule_id == 0 || rule_id > NSS_MATCH_INSTANCE_RULE_MAX) {
+ nss_match_warn("Invalid rule id: %d\n", rule_id);
+ return false;
+ }
+
+ if (!db_instance->rules.vow[rule_id - 1].valid_rule) {
+ nss_match_warn("rule_id doesnot exist, rule_id = %d", rule_id);
+ return false;
+ }
+
+ rule->msg.vow_rule.if_num = db_instance->rules.vow[rule_id - 1].rule.if_num;
+ rule->msg.vow_rule.dscp = db_instance->rules.vow[rule_id - 1].rule.dscp;
+ rule->msg.vow_rule.outer_8021p = db_instance->rules.vow[rule_id - 1].rule.outer_8021p;
+ rule->msg.vow_rule.inner_8021p = db_instance->rules.vow[rule_id - 1].rule.inner_8021p;
+ rule->msg.vow_rule.mask_id = db_instance->rules.vow[rule_id - 1].rule.mask_id;
+ rule->msg.vow_rule.rule_id = db_instance->rules.vow[rule_id - 1].rule.rule_id;
+ return true;
+}
+
+/*
+ * nss_match_vow_cmd_parse()
+ * Adds new rules to the list
+ */
+static int nss_match_vow_cmd_parse(char *input_msg, struct nss_match_msg *rule_msg, nss_match_cmd_t type)
+{
+ char *token, *param, *value;
+ struct nss_ctx_instance *nss_ctx = nss_match_get_context();
+ int ret = 0;
+ uint32_t actions = 0, if_num = 0, dscp = 0, outer_prio = 0, inner_prio = 0, setprio = 0, nexthop = 0;
+ uint16_t mask_id = 0;
+ uint32_t mask_val = 0;
+
+ while (input_msg != NULL) {
+ token = strsep(&input_msg, " ");
+ param = strsep(&token, "=");
+ value = token;
+ if (!param || !value) {
+ goto fail;
+ }
+
+ if (!(strncasecmp(param, "mask", strlen("mask")))) {
+ if (!sscanf(value, "%hu", &mask_id)) {
+ nss_match_warn("%p: Cannot convert to integer. Wrong input\n", nss_ctx);
+ return -EINVAL;
+ }
+
+ if (mask_id > NSS_MATCH_MASK_MAX) {
+ nss_match_warn("%p: Maskset num exceeds allowed value: %d\n", nss_ctx, mask_id);
+ return -EINVAL;
+ }
+
+ continue;
+ }
+
+ if (!(strncasecmp(param, "ifname", strlen("ifname")))) {
+ struct net_device *dev;
+
+ if (type == NSS_MATCH_ADD_MASK) {
+ if (!sscanf(value, "%x", &if_num)) {
+ nss_match_warn("%p: Cannot convert to integer. Wrong input\n", nss_ctx);
+ return -EINVAL;
+ }
+ continue;
+ }
+
+ if (type == NSS_MATCH_ADD_RULE) {
+ dev = dev_get_by_name(&init_net, value);
+ if (!dev) {
+ nss_match_warn("%p: Cannot find the net device\n", nss_ctx);
+ return -ENODEV;
+ }
+
+ if_num = nss_cmn_get_interface_number_by_dev(dev);
+ dev_put(dev);
+ continue;
+ }
+ }
+
+ /*
+ * Parsing Dscp value from the message.
+ */
+ if (!(strncasecmp(param, "dscp", strlen("dscp")))) {
+
+ if (type == NSS_MATCH_ADD_RULE) {
+ ret = sscanf(value, "%u", &dscp);
+ } else if (type == NSS_MATCH_ADD_MASK) {
+ ret = sscanf(value, "%x", &dscp);
+ }
+
+ if (!ret) {
+ nss_match_warn("%p: Cannot convert to integer. Wrong input\n", nss_ctx);
+ return -EINVAL;
+ }
+
+ if (dscp > MAX_DSCP) {
+ nss_match_warn("%p: Dscp value %d cannot go beyong %d\n", nss_ctx, dscp, MAX_DSCP);
+ return -EINVAL;
+ }
+
+ continue;
+ }
+
+ /*
+ * Parsing 8021.p from the message given by host.
+ */
+ if (!(strncasecmp(param, "802.1p_inner", strlen("802.1p_inner")))) {
+ if (type == NSS_MATCH_ADD_RULE) {
+ ret = sscanf(value, "%u", &inner_prio);
+ } else if (type == NSS_MATCH_ADD_MASK) {
+ ret = sscanf(value, "%x", &inner_prio);
+ }
+
+ if (!ret) {
+ nss_match_warn("%p: Cannot convert to integer. Wrong input!!\n", nss_ctx);
+ return -EINVAL;
+ }
+
+ if (inner_prio > MAX_PRIORITY) {
+ nss_match_warn("%p: Priority %d value cannot go beyong 7\n", nss_ctx, inner_prio);
+ return -EINVAL;
+ }
+
+ continue;
+ }
+
+ if (!(strncasecmp(param, "802.1p_outer", strlen("802.1p_outer")))) {
+
+ if (type == NSS_MATCH_ADD_RULE) {
+ ret = sscanf(value, "%u", &outer_prio);
+ } else if (type == NSS_MATCH_ADD_MASK) {
+ ret = sscanf(value, "%x", &outer_prio);
+ }
+
+ if (!ret) {
+ nss_match_warn("%p: Cannot convert to integer. Wrong input\n", nss_ctx);
+ return -EINVAL;
+ }
+
+ if (outer_prio > MAX_PRIORITY) {
+ nss_match_warn("%p: vlan_priority = %d, priority value cannot go beyong 8"
+ " 1 extra for wildcard\n", nss_ctx, outer_prio);
+ return -EINVAL;
+ }
+
+ continue;
+ }
+
+ /*
+ * Parsing action from the message provided by user.
+ */
+ if (!(strncasecmp(param, "action", strlen("action")))) {
+ if (!sscanf(value, "%u", &actions)) {
+ nss_match_warn("%p: Cannot convert to integer. Wrong input\n", nss_ctx);
+ return -EINVAL;
+ }
+
+ if (actions >= NSS_MATCH_ACTION_MAX ) {
+ nss_match_warn("Inavlid action type: %d, action type < %d is correct.",
+ actions, NSS_MATCH_ACTION_MAX);
+ }
+
+ if (actions == 1 || actions == 3) {
+ token = strsep(&input_msg, " ");
+ param = strsep(&token, "=");
+ value = token;
+ if (!param || !value) {
+ goto fail;
+ }
+
+ if (!(strncasecmp(param, "priority", strlen("priority")))) {
+ if (!sscanf(value, "%u", &setprio)) {
+ nss_match_warn("%p: Cannot convert to integer. Wrong input\n", nss_ctx);
+ return -EINVAL;
+ }
+
+ if (setprio >= NSS_MAX_NUM_PRI) {
+ nss_match_warn("Invalid priority: %d", setprio);
+ }
+ }
+ }
+
+ if (actions == 2 || actions == 3) {
+ token = strsep(&input_msg, " ");
+ param = strsep(&token, "=");
+ value = token;
+ if (!param || !value) {
+ goto fail;
+ }
+
+ if (!(strncasecmp(param, "nexthop", strlen("nexthop")))) {
+ if (!sscanf(value, "%u", &nexthop)) {
+ nss_match_warn("%p: Cannot convert to integer. Wrong input\n", nss_ctx);
+ return -EINVAL;
+ }
+ }
+ }
+
+ continue;
+ }
+
+ nss_match_warn("%p: Not a valid input\n", nss_ctx);
+ return -EINVAL;
+ }
+
+ /*
+ * Verify 802.1 outer priority exists with inner priority.
+ */
+ switch (type) {
+ case NSS_MATCH_ADD_RULE:
+ if (!mask_id || !actions) {
+ goto fail;
+ }
+
+ if (!outer_prio && inner_prio) {
+ nss_match_warn("802p_inner priority = %u can not exist without 802p_outer priority = %u", inner_prio, outer_prio);
+ return -EINVAL;
+ }
+
+ rule_msg->msg.vow_rule.if_num = if_num;
+ rule_msg->msg.vow_rule.dscp = dscp;
+ rule_msg->msg.vow_rule.outer_8021p = outer_prio;
+ rule_msg->msg.vow_rule.inner_8021p = inner_prio;
+ rule_msg->msg.vow_rule.mask_id = mask_id;
+ rule_msg->msg.vow_rule.action.setprio = setprio;
+ rule_msg->msg.vow_rule.action.action_flag = actions;
+ rule_msg->msg.vow_rule.action.forward_ifnum = nexthop;
+ break;
+ case NSS_MATCH_ADD_MASK:
+ if (!mask_id) {
+ goto fail;
+ }
+
+ mask_val = (if_num << NSS_MATCH_VOW_KEY_IFNUM_SHIFT);
+ mask_val |= (dscp << NSS_MATCH_VOW_KEY_DSCP_SHIFT);
+ mask_val |= (inner_prio << NSS_MATCH_VOW_KEY_INNER_8021P_SHIFT);
+ mask_val |= (outer_prio << NSS_MATCH_VOW_KEY_OUTER_8021P_SHIFT);
+
+ rule_msg->msg.configure_msg.profile_type = NSS_MATCH_PROFILE_TYPE_VOW;
+ rule_msg->msg.configure_msg.maskset[mask_id-1][0] = mask_val;
+ rule_msg->msg.configure_msg.valid_mask_flag = mask_id;
+ break;
+ default:
+ nss_match_warn("Invalid parse type: %d", type);
+ return -EINVAL;
+ }
+
+ return 0;
+
+fail:
+ pr_warn("Invalid input, Check help.(cat /sys/kernel/debug/match/help)");
+ return -EINVAL;
+}
+
+/*
+ * nss_match_vow_table_read()
+ * Reads stats for VoW profile.
+ */
+static size_t nss_match_vow_table_read(struct nss_match_instance *db_instance, size_t buflen, char *bufp)
+{
+ int i, j;
+ struct nss_ctx_instance *nss_ctx = nss_match_get_context();
+ struct net_device *net_dev;
+ size_t size_wr = 0;
+ char *dev_name;
+
+ size_wr += scnprintf(bufp + size_wr, buflen - size_wr, "Match if_num = %d\n\n", db_instance->if_num);
+ size_wr += scnprintf(bufp + size_wr, buflen - size_wr, "Profile Type = %d\n\n", db_instance->profile_type);
+
+ for (i = 0; i < NSS_MATCH_MASK_MAX; i++) {
+ if (!(db_instance->valid_mask_flag & (1 << i))) {
+ size_wr += scnprintf(bufp + size_wr, buflen - size_wr, "Mask %d = Invalid\n", i+1);
+ continue;
+ }
+
+ size_wr += scnprintf(bufp + size_wr, buflen - size_wr, "Mask %d = %x\n", i+1, db_instance->maskset[i][0]);
+ }
+
+ size_wr += scnprintf(bufp + size_wr, buflen - size_wr, "rule_id\t hit_count\t mask_id\t if_name\t dscp\t outer 802.1p\t inner 802.1p\t action\t priority\t nexthop\n\n");
+ for (j = 0; j < NSS_MATCH_INSTANCE_RULE_MAX; j++) {
+ if (!db_instance->rules.vow[j].valid_rule)
+ continue;
+
+ dev_name = "N/A";
+ net_dev = nss_cmn_get_interface_dev(nss_ctx, db_instance->rules.vow[j].rule.if_num);
+ if (net_dev) {
+ dev_name = net_dev->name;
+ }
+
+ size_wr += scnprintf(bufp + size_wr, buflen - size_wr, "%d\t\t %llu\t\t %d\t\t %s\t\t %d\t\t %d\t\t %d\t\t %u\t\t %d\t\t %d\n",
+ j + 1,
+ db_instance->stats.hit_count[j],
+ db_instance->rules.vow[j].rule.mask_id,
+ dev_name,
+ db_instance->rules.vow[j].rule.dscp,
+ db_instance->rules.vow[j].rule.outer_8021p,
+ db_instance->rules.vow[j].rule.inner_8021p,
+ db_instance->rules.vow[j].rule.action.action_flag,
+ db_instance->rules.vow[j].rule.action.setprio,
+ db_instance->rules.vow[j].rule.action.forward_ifnum);
+ }
+
+ return size_wr;
+}
+
+/*
+ * Match ops for VoW profile.
+ */
+static struct match_profile_ops match_profile_vow_ops = {
+ nss_match_vow_rule_id_generate,
+ nss_match_vow_rule_find,
+ nss_match_vow_db_rule_add,
+ nss_match_vow_db_rule_delete,
+ nss_match_vow_rule_read,
+ nss_match_vow_table_read,
+ nss_match_vow_cmd_parse,
+};
+
+/*
+ * nss_match_vow_init()
+ * Initializes the VoW profile.
+ */
+void nss_match_vow_init(void) {
+
+ /*
+ * Register the VoW profile ops.
+ */
+ nss_match_profile_ops_register(NSS_MATCH_PROFILE_TYPE_VOW, &match_profile_vow_ops);
+ nss_match_info("VoW profile initialization done\n");
+}
diff --git a/match/nss_match_vow.h b/match/nss_match_vow.h
new file mode 100644
index 0000000..30f3b4e
--- /dev/null
+++ b/match/nss_match_vow.h
@@ -0,0 +1,23 @@
+/*
+ *******************************************************************************
+ * 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.
+ ********************************************************************************
+ **/
+
+#ifndef __NSS_MATCH_VOW_H
+#define __NSS_MATCH_VOW_H
+
+void nss_match_vow_init(void);
+#endif /* __NSS_MATCH_VOW_H */