[qca-nss-clients] NETLINK I/F for ipsec

Added the ipsec netlink support in the common framework.
Added support for asynchronous status and sending statistics
to user space

Change-Id: I1c5b39809c274c43259efb5ad699e824b3b3975a
Signed-off-by: Samarjeet Banerjee <banerjee@codeaurora.org>
Signed-off-by: mandrw <mandrw@codeaurora.org>
diff --git a/netlink/include/nss_nlipsec_if.h b/netlink/include/nss_nlipsec_if.h
new file mode 100644
index 0000000..12dec65
--- /dev/null
+++ b/netlink/include/nss_nlipsec_if.h
@@ -0,0 +1,74 @@
+/*
+ **************************************************************************
+ * Copyright (c) 2015, 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_NLIPSEC_IF_H
+#define __NSS_NLIPSEC_IF_H
+
+/**
+ * @addtogroup nss_netlink
+ * @{
+ */
+
+/*
+ * @file nss_nlipsec_if.h
+ *	NSS Netlink IPsec headers
+ */
+
+#define NSS_NLIPSEC_FAMILY "nss_nlipsec"	/**< IPsec family */
+
+/**
+ * @brief ipsec commands types
+ */
+enum nss_nlipsec_cmd {
+	NSS_NLIPSEC_CMD_UNSPEC = 0,		/**< unspecified cmd */
+	NSS_NLIPSEC_CMD_CREATE_TUNNEL = 1,	/**< create tunnel */
+	NSS_NLIPSEC_CMD_DESTROY_TUNNEL = 2,	/**< destroy tunnel */
+	NSS_NLIPSEC_CMD_CREATE_RULE = 3,	/**< create rule */
+	NSS_NLIPSEC_CMD_DESTROY_RULE = 4,	/**< destroy rule */
+
+	NSS_NLIPSEC_CMD_MAX
+};
+
+/**
+ * @brief IPsec message
+ */
+struct nss_nlipsec_rule {
+	struct nss_nlcmn cm;				/**< common message header */
+
+	uint8_t ifname[IFNAMSIZ];			/**< IPsec tunnel interface name */
+
+	struct {
+		enum nss_ipsecmgr_rule_type type; 	/**< IPsec rule type */
+		union nss_ipsecmgr_rule data;		/**< IPsec rule data */
+	} msg;
+
+	struct nss_ipsecmgr_event event;		/**< IPsec event */
+};
+
+/**
+ * @brief NETLINK IPsec message init
+ *
+ * @param rule[IN] NSS NETLINK IPsec message
+ * @param type[IN] IPsec message type
+ */
+static inline void nss_nlipsec_rule_init(struct nss_nlipsec_rule *rule, enum nss_nlipsec_cmd type)
+{
+	nss_nlcmn_set_ver(&rule->cm, NSS_NL_VER);
+	nss_nlcmn_init_cmd(&rule->cm, type, sizeof(struct nss_nlipsec_rule));
+}
+
+/**@}*/
+#endif /* __NSS_NLIPSEC_IF_H */
diff --git a/netlink/nss_nlipsec.c b/netlink/nss_nlipsec.c
new file mode 100644
index 0000000..33f11cf
--- /dev/null
+++ b/netlink/nss_nlipsec.c
@@ -0,0 +1,728 @@
+
+/*
+ **************************************************************************
+ * Copyright (c) 2015, 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_nlipsec.c
+ *	NSS Netlink IPsec Handler
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/if.h>
+#include <linux/netlink.h>
+#include <linux/mutex.h>
+
+#include <net/genetlink.h>
+#include <net/sock.h>
+
+#include <nss_api_if.h>
+#include <nss_cmn.h>
+#include <nss_ipsec.h>
+#include <nss_ipsecmgr.h>
+#include <nss_crypto.h>
+#include "nss_crypto_if.h"
+#include <nss_nl_if.h>
+#include "nss_nl.h"
+#include "nss_nlipsec_if.h"
+
+
+/* remove the "%d" and the NULL terminator */
+#define NSS_NLIPSEC_TUNNEL_NAME_SZ (sizeof(NSS_IPSECMGR_TUN_NAME) - 3)
+
+/**
+ * Local protos.
+ */
+static int nss_nlipsec_op_create_tunnel(struct sk_buff *skb_msg, struct genl_info *info);
+static int nss_nlipsec_op_destroy_tunnel(struct sk_buff *skb_msg, struct genl_info *info);
+static int nss_nlipsec_op_create_rule(struct sk_buff *skb, struct genl_info *info);
+static int nss_nlipsec_op_destroy_rule(struct sk_buff *skb, struct genl_info *info);
+
+/*
+ * IPsec family definition
+ */
+static struct genl_family nss_nlipsec_family = {
+	.id = GENL_ID_GENERATE,				/* Auto generate ID */
+	.name = NSS_NLIPSEC_FAMILY,			/* family name string */
+	.hdrsize = sizeof(struct nss_nlipsec_rule),	/* NSS NETLINK IPsec rule */
+	.version = NSS_NL_VER,				/* Set it to NSS_NL version */
+	.maxattr = NSS_NLIPSEC_CMD_MAX,			/* maximum commands supported */
+	.netnsok = true,
+	.pre_doit = NULL,
+	.post_doit = NULL,
+};
+
+/*
+ * multicast group for sending message status & events
+ */
+static struct genl_multicast_group nss_nlipsec_mcgrp = {
+	.name = NSS_NLIPSEC_FAMILY,
+};
+
+/*
+ * operation table called by the generic netlink layer based on the command
+ */
+static struct genl_ops nss_nlipsec_ops[] = {
+	{.cmd = NSS_NLIPSEC_CMD_CREATE_TUNNEL, .doit = nss_nlipsec_op_create_tunnel,},		/* create tunnel */
+	{.cmd = NSS_NLIPSEC_CMD_DESTROY_TUNNEL, .doit = nss_nlipsec_op_destroy_tunnel,},	/* destroy tunnel */
+	{.cmd = NSS_NLIPSEC_CMD_CREATE_RULE, .doit = nss_nlipsec_op_create_rule,},		/* create rule */
+	{.cmd = NSS_NLIPSEC_CMD_DESTROY_RULE, .doit = nss_nlipsec_op_destroy_rule,},		/* destroy rule */
+};
+
+/*
+ * Hold netdevice references
+ */
+struct nss_nlipsec_ref {
+	struct mutex lock;	/* mutex for field access */
+	int ifindex;		/* device interface index */
+	bool valid;		/* reference is valid or invalid */
+};
+
+/*
+ * Local context for the NSS_NLIPSEC
+ */
+struct nss_nlipsec_ctx {
+	atomic_t tunnels;	/* number tunnels allocated */
+	/*
+	 * This table stores device reference associated
+	 * to the IPsec tunnel that it has created through NETLINK
+	 * thus prohibiting any spurious attempts to delete
+	 * random net_devices from the Linux kernel
+	 */
+	struct nss_nlipsec_ref ref_tbl[NSS_IPSECMGR_MAX_TUNNELS];
+};
+
+/*
+ * global context
+ */
+static struct nss_nlipsec_ctx gbl_ctx;
+
+/*
+ * nss_nlipsec_add_ref()
+ *	add reference to the netdevice object
+ */
+static inline void nss_nlipsec_add_ref(struct nss_nlipsec_ref *ref_tbl, struct net_device *dev)
+{
+	struct nss_nlipsec_ref *ref;
+	int i;
+
+	for (i = 0, ref = &ref_tbl[0]; i < NSS_IPSECMGR_MAX_TUNNELS; i++, ref++) {
+		mutex_lock(&ref->lock); /* lock_ref */
+
+		if (!ref->valid) {
+			ref->ifindex = dev->ifindex;
+			ref->valid = true;
+
+			mutex_unlock(&ref->lock); /* unlock_ref */
+			return;
+		}
+
+		mutex_unlock(&ref->lock); /* unlock_ref */
+	}
+
+	BUG_ON(i == NSS_IPSECMGR_MAX_TUNNELS);
+}
+
+/*
+ * nss_nlipsec_del_ref()
+ * 	delete netdevice reference
+ */
+static inline void nss_nlipsec_del_ref(struct nss_nlipsec_ref *ref)
+{
+	mutex_lock(&ref->lock);	/* lock_ref */
+
+	ref->ifindex = -1;
+	ref->valid = false;
+
+	mutex_unlock(&ref->lock); /* unlock_ref */
+}
+
+/*
+ * nss_nlipsec_find_ref()
+ * 	find refernce node for the given netdevice
+ */
+struct nss_nlipsec_ref *nss_nlipsec_find_ref(struct nss_nlipsec_ref *ref_tbl, struct net_device *dev)
+{
+	struct nss_nlipsec_ref *ref = NULL;
+	int i;
+
+	for (i = 0, ref = &ref_tbl[0]; i < NSS_IPSECMGR_MAX_TUNNELS; i++, ref++) {
+
+		mutex_lock(&ref->lock); /* lock_ref */
+
+		if (ref->valid && (dev->ifindex == ref->ifindex)) {
+			mutex_unlock(&ref->lock); /* unlock_ref */
+			return ref;
+		}
+
+		mutex_unlock(&ref->lock); /* unlock_ref */
+	}
+
+	return NULL;
+}
+
+/*
+ * nss_nlipsec_verify_create_encap()
+ * 	verify create encap rule
+ */
+static int nss_nlipsec_verify_create_encap(struct nss_ipsecmgr_encap_add *encap)
+{
+	const uint32_t encap_req_type = (NSS_CRYPTO_REQ_TYPE_AUTH | NSS_CRYPTO_REQ_TYPE_ENCRYPT);
+	uint32_t req_type;
+
+	/*
+	 * inner header checks
+	 */
+	if (!encap->inner_ipv4_src || !encap->inner_ipv4_dst || !encap->inner_ipv4_proto) {
+		return -1;
+	}
+
+	/*
+	 * outer header checks
+	 */
+	if (!encap->outer_ipv4_src || !encap->outer_ipv4_dst || !encap->esp_spi) {
+		return -2;
+	}
+
+	/*
+	 * check cipher
+	 */
+	if (encap->cipher_algo != nss_crypto_get_cipher(encap->crypto_index)) {
+		return -3;
+	}
+
+	/*
+	 * check auth
+	 */
+	if (encap->auth_algo != nss_crypto_get_auth(encap->crypto_index)) {
+		return -4;
+	}
+
+	/*
+	 * match request type for crypto with encap direction
+	 */
+	req_type = nss_crypto_get_reqtype(encap->crypto_index);
+	if (req_type != encap_req_type) {
+		return -5;
+	}
+
+	return 0;
+}
+
+/*
+ * nss_nlipsec_verify_create_decap()
+ * 	verify create decap rule
+ */
+static int nss_nlipsec_verify_create_decap(struct nss_ipsecmgr_decap_add *decap)
+{
+	const uint32_t decap_req_type = (NSS_CRYPTO_REQ_TYPE_AUTH | NSS_CRYPTO_REQ_TYPE_DECRYPT);
+	uint32_t req_type;
+
+	/*
+	 * outer header checks
+	 */
+	if (!decap->outer_ipv4_src || !decap->outer_ipv4_dst || !decap->esp_spi) {
+		return -1;
+	}
+
+	/*
+	 * check cipher
+	 */
+	if (decap->cipher_algo != nss_crypto_get_cipher(decap->crypto_index)) {
+		return -2;
+	}
+
+	/*
+	 * check auth
+	 */
+	if (decap->auth_algo != nss_crypto_get_auth(decap->crypto_index)) {
+		return -3;
+	}
+
+	/*
+	 * match request type for crypto with encap direction
+	 */
+	req_type = nss_crypto_get_reqtype(decap->crypto_index);
+	if (req_type != decap_req_type) {
+		return -4;
+	}
+
+	return 0;
+}
+
+/*
+ * nss_nlipsec_verify_destroy_encap()
+ * 	verify destroy encap rule
+ */
+static int nss_nlipsec_verify_destroy_encap(struct nss_ipsecmgr_encap_del *encap)
+{
+	/*
+	 * inner header checks
+	 */
+	if (!encap->inner_ipv4_src || !encap->inner_ipv4_dst || !encap->inner_ipv4_proto) {
+		return -1;
+	}
+
+	return 0;
+}
+
+/*
+ * nss_nlipsec_verify_destroy_decap()
+ * 	verify destroy decap rule
+ */
+static int nss_nlipsec_verify_destroy_decap(struct nss_ipsecmgr_decap_del *decap)
+{
+	/*
+	 * outer header checks
+	 */
+	if (!decap->outer_ipv4_src || !decap->outer_ipv4_dst || !decap->esp_spi) {
+		return -1;
+	}
+
+	return 0;
+}
+
+/*
+ * nss_nlipsec_process_event()
+ * 	process events from NSS IPsec manager and MCAST it to user
+ */
+static void nss_nlipsec_process_event(void *ctx, struct nss_ipsecmgr_event *ev)
+{
+	struct nss_nlipsec_rule *nl_rule;
+	struct sk_buff *skb;
+
+	/*
+	 * Allocate a new event message
+	 */
+	skb = nss_nl_new_msg(&nss_nlipsec_family, ev->type);
+	if (!skb) {
+		nss_nl_error("unable to allocate NSS_NLIPV4 event\n");
+		return;
+	}
+
+	nl_rule = nss_nl_get_data(skb);
+
+	/*
+	 * initialize the NETLINK common header
+	 */
+	nss_nlipsec_rule_init(nl_rule, ev->type);
+
+	/*
+	 * copy the contents of the sync message into the NETLINK message
+	 */
+	memcpy(&nl_rule->event, ev, sizeof(struct nss_ipsecmgr_event));
+
+	nss_nl_mcast_event(&nss_nlipsec_mcgrp, skb);
+}
+
+/*
+ * ***********
+ * OP handlers
+ * ***********
+ */
+
+/*
+ * nss_nlipsec_op_create_rule()
+ * 	create a SA rule (encap or decap) type to the IPsec tunnel
+ */
+static int nss_nlipsec_op_create_rule(struct sk_buff *skb, struct genl_info *info)
+{
+	struct nss_nlipsec_rule *nl_rule;
+	enum nss_ipsecmgr_rule_type type;
+	union nss_ipsecmgr_rule *rule;
+	struct nss_nlipsec_ref *ref;
+	struct nss_nlcmn *nl_cm;
+	struct net_device *dev;
+	uint32_t pid;
+	int error;
+
+	/*
+	 * extract the message payload
+	 */
+	nl_cm = nss_nl_get_msg(&nss_nlipsec_family, info, NSS_NLIPSEC_CMD_CREATE_RULE);
+	if (!nl_cm) {
+		nss_nl_error("unable to extract create rule data\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Message validation required before accepting the configuration
+	 */
+	nl_rule = container_of(nl_cm, struct nss_nlipsec_rule, cm);
+	pid = nl_cm->pid;
+
+	/*
+	 * search/get the the linux netdevice object
+	 */
+	dev = dev_get_by_name(&init_net, nl_rule->ifname);
+	if (!dev) {
+		nss_nl_error("%d:unable to find netdevice (%s)\n", pid, nl_rule->ifname);
+		return -EINVAL;
+	}
+
+	/*
+	 * find if we have the local reference
+	 */
+	ref = nss_nlipsec_find_ref(gbl_ctx.ref_tbl, dev);
+	if (!ref) {
+		nss_nl_error("%d:interface(%s) was not created through NL_IPSEC\n", pid, dev->name);
+		error =  -EINVAL;
+		goto done;
+	}
+
+	rule = &nl_rule->msg.data;
+	type = nl_rule->msg.type;
+
+	/*
+	 * verify create rule
+	 */
+	switch (type) {
+	case NSS_IPSECMGR_RULE_TYPE_ENCAP:
+		error = nss_nlipsec_verify_create_encap(&rule->encap_add);
+		break;
+
+	case NSS_IPSECMGR_RULE_TYPE_DECAP:
+		error = nss_nlipsec_verify_create_decap(&rule->decap_add);
+		break;
+
+	default:
+		error = -EINVAL;
+		break;
+	}
+	if (error < 0) {
+		nss_nl_error("%d:invalid encap rule type(%d), error(%d)\n", pid, type, error);
+		goto done;
+	}
+
+	/*
+	 * Add the SA rule to the IPsec tunnel
+	 */
+	if (nss_ipsecmgr_sa_add(dev, rule, type) == false) {
+		nss_nl_error("%d:unable to add(%d) rule to tunnel(%s)\n", pid, type, dev->name);
+		error =  -EINVAL;
+		goto done;
+	}
+
+done:
+	dev_put(dev);
+	return error;
+}
+
+/*
+ * nss_nlipsec_op_destroy_rule()
+ * 	destroy a SA rule (encap or decap) type from the IPsec tunnel
+ */
+static int nss_nlipsec_op_destroy_rule(struct sk_buff *skb, struct genl_info *info)
+{
+	enum nss_ipsecmgr_rule_type type;
+	struct nss_nlipsec_rule *nl_rule;
+	union nss_ipsecmgr_rule *rule;
+	struct nss_nlipsec_ref *ref;
+	struct nss_nlcmn *nl_cm;
+	struct net_device *dev;
+	uint32_t pid;
+	int error;
+
+	/*
+	 * extract the message payload
+	 */
+	nl_cm = nss_nl_get_msg(&nss_nlipsec_family, info, NSS_NLIPSEC_CMD_DESTROY_RULE);
+	if (!nl_cm) {
+		nss_nl_error("unable to extract destroy rule data\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Message validation required before accepting the configuration
+	 */
+	nl_rule = container_of(nl_cm, struct nss_nlipsec_rule, cm);
+	pid = nl_cm->pid;
+
+	/*
+	 * search/get the the linux netdevice object
+	 */
+	dev = dev_get_by_name(&init_net, nl_rule->ifname);
+	if (!dev) {
+		nss_nl_error("%d:unable to find netdevice (%s)\n", pid, nl_rule->ifname);
+		return -EINVAL;
+	}
+
+	/*
+	 * find if we have the local reference
+	 */
+	ref = nss_nlipsec_find_ref(gbl_ctx.ref_tbl, dev);
+	if (!ref) {
+		nss_nl_error("%d:interface(%s) was not created through NL_IPSEC\n", pid, dev->name);
+		error =  -EINVAL;
+		goto done;
+	}
+
+	rule = &nl_rule->msg.data;
+	type = nl_rule->msg.type;
+
+	/*
+	 * verify destroy rule
+	 */
+	switch (type) {
+	case NSS_IPSECMGR_RULE_TYPE_ENCAP:
+		error = nss_nlipsec_verify_destroy_encap(&rule->encap_del);
+		break;
+
+	case NSS_IPSECMGR_RULE_TYPE_DECAP:
+		error = nss_nlipsec_verify_destroy_decap(&rule->decap_del);
+		break;
+
+	default:
+		error = -EINVAL;
+		break;
+	}
+	if (error < 0) {
+		nss_nl_error("%d:invalid destroy rule type(%d), error(%d)\n", pid, type, error);
+		goto done;
+	}
+
+	/*
+	 * Delete the SA rule from the IPsec tunnel
+	 */
+	if (nss_ipsecmgr_sa_del(dev, rule, type) == false) {
+		nss_nl_error("%d:unable to delete(%d) rule from tunnel(%s)\n", pid, type, dev->name);
+		error =  -EINVAL;
+		goto done;
+	}
+
+done:
+	dev_put(dev);
+	return error;
+}
+
+/*
+ * nss_nlipsec_op_create_tunnel()
+ * 	Add IPsec tunnel
+ */
+static int nss_nlipsec_op_create_tunnel(struct sk_buff *skb, struct genl_info *info)
+{
+	struct nss_nlipsec_rule *nl_rule;
+	struct nss_nlcmn *nl_cm;
+	struct net_device *dev;
+	struct sk_buff *copy;
+	uint32_t pid;
+
+	/*
+	 * extract the message payload
+	 */
+	nl_cm = nss_nl_get_msg(&nss_nlipsec_family, info, NSS_NLIPSEC_CMD_CREATE_TUNNEL);
+	if (!nl_cm) {
+		nss_nl_error("unable to extract create tunnel data\n");
+		return -EINVAL;
+	}
+
+	pid = nl_cm->pid;
+
+	if (atomic_read(&gbl_ctx.tunnels) == NSS_IPSECMGR_MAX_TUNNELS) {
+		nss_nl_error("%d:max allowed tunnel reached (%d)\n", pid, NSS_IPSECMGR_MAX_TUNNELS);
+		return -EINVAL;
+	}
+
+	/*
+	 * create a IPsec tunnel device
+	 */
+	dev = nss_ipsecmgr_tunnel_add(&gbl_ctx, NULL, nss_nlipsec_process_event);
+	if (!dev) {
+		nss_nl_error("%d:unable to add IPsec tunnel\n", pid);
+		return -ENOMEM;
+	}
+
+	/*
+	 * add an internal reference to the tunnel dev
+	 */
+	atomic_inc(&gbl_ctx.tunnels);
+	nss_nlipsec_add_ref(gbl_ctx.ref_tbl, dev);
+
+	/*
+	 * Response message
+	 */
+	copy  = nss_nl_copy_msg(skb);
+	if (!copy) {
+		nss_nl_error("unable to copy incoming message\n");
+		goto free_dev;
+	}
+
+	/*
+	 * We need to send the  name to the user; copy
+	 * the tunnel I/F name into the same rule and send it
+	 * as part of the response for the create operation
+	 */
+	nl_rule = container_of(nl_cm, struct nss_nlipsec_rule, cm);
+	strncpy(nl_rule->ifname, dev->name, IFNAMSIZ);
+
+	/*
+	 * Send to userspace
+	 */
+	nss_nl_ucast_resp(copy);
+
+	return 0;
+
+free_dev:
+
+	nss_ipsecmgr_tunnel_del(dev);
+	return -ENOMEM;
+}
+
+/*
+ * nss_nlipsec_op_tunnel_del()
+ * 	delete an IPsec tunnel
+ */
+static int nss_nlipsec_op_destroy_tunnel(struct sk_buff *skb, struct genl_info *info)
+{
+	struct nss_nlipsec_rule *nl_rule;
+	struct nss_nlipsec_ref *ref;
+	struct nss_nlcmn *nl_cm;
+	struct net_device *dev;
+	uint32_t pid;
+
+	/*
+	 * extract the message payload
+	 */
+	nl_cm = nss_nl_get_msg(&nss_nlipsec_family, info, NSS_NLIPSEC_CMD_DESTROY_TUNNEL);
+	if (!nl_cm) {
+		nss_nl_error("unable to extract destroy tunnel data\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Message validation required before accepting the configuration
+	 */
+	nl_rule = container_of(nl_cm, struct nss_nlipsec_rule, cm);
+	pid = nl_cm->pid;
+
+	if (atomic_read(&gbl_ctx.tunnels) == 0) {
+		nss_nl_error("%d:no tunnels available for deletion\n", pid);
+		return -EINVAL;
+	}
+	/*
+	 * search/get the the linux netdevice object
+	 */
+	dev = dev_get_by_name(&init_net, nl_rule->ifname);
+	if (!dev) {
+		nss_nl_error("%d:unable to find netdevice (%s)\n", pid, nl_rule->ifname);
+		return -EINVAL;
+	}
+
+	/*
+	 * find if we have the local reference
+	 */
+	ref = nss_nlipsec_find_ref(gbl_ctx.ref_tbl, dev);
+	if (!ref) {
+		nss_nl_error("%d:interface(%s) was not created through NL_IPSEC\n", pid, dev->name);
+		dev_put(dev);
+		return -EINVAL;
+	}
+
+	nss_nlipsec_del_ref(ref);
+	atomic_dec(&gbl_ctx.tunnels);
+
+	/*
+	 * down the ref_cnt held by nss_nlipsec_destroy_tunnel
+	 */
+	dev_put(dev);
+
+	/*
+	 * delete the tunnel device
+	 */
+	nss_ipsecmgr_tunnel_del(dev);
+
+	return 0;
+}
+
+#define NSS_NLIPSEC_OPS_SZ ARRAY_SIZE(nss_nlipsec_ops)
+
+/*
+ * nss_nlipsec_init()
+ * 	handler init
+ */
+bool nss_nlipsec_init(void)
+{
+	struct nss_nlipsec_ref *ref;
+	int error;
+	int i;
+
+	nss_nl_info_always("Init NSS netlink IPsec handler\n");
+
+	/*
+	 * initialize reference table
+	 */
+	for (i = 0, ref = gbl_ctx.ref_tbl; i < NSS_IPSECMGR_MAX_TUNNELS; i++, ref++) {
+		mutex_init(&ref->lock);
+		ref->valid = false;
+		ref->ifindex = -1;
+	}
+
+	/*
+	 * register with the family
+	 */
+	error = genl_register_family_with_ops(&nss_nlipsec_family,
+			nss_nlipsec_ops, NSS_NLIPSEC_OPS_SZ);
+	if (error != 0) {
+		nss_nl_info_always("Error: unable to register IPsec family\n");
+		return false;
+	}
+
+	/*
+	 * register NETLINK MCAST group for notifications
+	 */
+	error = genl_register_mc_group(&nss_nlipsec_family, &nss_nlipsec_mcgrp);
+	if (error != 0) {
+		nss_nl_info_always("Error: unable to register IPsec Netlink"
+				"multicast group\n");
+		goto unreg_family;
+	}
+
+
+	return true;
+
+unreg_family:
+
+	/*
+	 * undo all registeration
+	 */
+	genl_unregister_family(&nss_nlipsec_family);
+
+	return false;
+}
+
+/*
+ * nss_nlipsec_exit()
+ * 	handler exit
+ */
+bool nss_nlipsec_exit(void)
+{
+	int error;
+
+	nss_nl_info_always("Exit NSS netlink IPsec handler\n");
+
+	/*
+	 * unregister the ops family
+	 */
+	error = genl_unregister_family(&nss_nlipsec_family);
+	if (error != 0) {
+		nss_nl_info_always("unable to unregister IPsec NETLINK family\n");
+		return false;
+	}
+
+	return true;
+}