[qca-nss-clients] Added multitunnel support

Added multitunnel support in ipsecmgr module.

Change-Id: I9b7d32500196eab72dfc33a90f336482914a4abc
Signed-off-by: mandrw <mandrw@codeaurora.org>
diff --git a/ipsecmgr/nss_ipsecmgr.c b/ipsecmgr/nss_ipsecmgr.c
index 365bac3..21f758c 100644
--- a/ipsecmgr/nss_ipsecmgr.c
+++ b/ipsecmgr/nss_ipsecmgr.c
@@ -30,6 +30,8 @@
 #include <asm/atomic.h>
 #include <linux/vmalloc.h>
 #include <linux/debugfs.h>
+#include <net/route.h>
+#include <net/ip6_route.h>
 
 #include <nss_api_if.h>
 #include <nss_ipsec.h>
@@ -40,7 +42,7 @@
 
 extern bool nss_cmn_get_nss_enabled(void);
 
-static struct nss_ipsecmgr_drv ipsecmgr_ctx = {0};
+struct nss_ipsecmgr_drv *ipsecmgr_ctx;
 
 /*
  **********************
@@ -229,7 +231,7 @@
 	 * Check if skb is non-linear
 	 */
 	if (skb_is_nonlinear(skb)) {
-		nss_ipsecmgr_error("%p: NSS IPSEC does not support fragments %p\n", priv->nss_ctx, skb);
+		nss_ipsecmgr_error("%s: NSS IPSEC does not support fragments %p\n", dev->name, skb);
 		goto fail;
 	}
 
@@ -237,7 +239,7 @@
 	 * Check if skb is shared
 	 */
 	if (unlikely(skb_shared(skb))) {
-		nss_ipsecmgr_error("%p: Shared skb is not supported: %p\n", priv->nss_ctx, skb);
+		nss_ipsecmgr_error("%s: Shared skb is not supported: %p\n", dev->name, skb);
 		goto fail;
 	}
 
@@ -245,7 +247,7 @@
 	 * Check if packet is given starting from network header
 	 */
 	if (skb->data != skb_network_header(skb)) {
-		nss_ipsecmgr_error("%p: 'Skb data is not starting from IP header", priv->nss_ctx);
+		nss_ipsecmgr_error("%s: 'Skb data is not starting from IP header\n", dev->name);
 		goto fail;
 	}
 
@@ -260,7 +262,7 @@
 	}
 
 	if (expand_skb && pskb_expand_head(skb, nhead, ntail, GFP_KERNEL)) {
-		nss_ipsecmgr_error("%p: unable to expand buffer\n", priv->nss_ctx);
+		nss_ipsecmgr_error("%s: unable to expand buffer\n", dev->name);
 		goto fail;
 	}
 
@@ -362,25 +364,77 @@
 }
 
 /*
+ * nss_ipsecmgr_get_dev()
+ *	Get the net_device associated with the packet.
+ */
+static struct net_device *nss_ipsecmgr_get_dev(struct sk_buff *skb)
+{
+	struct dst_entry *dst;
+	struct net_device *dev;
+	uint32_t ip_addr;
+	struct rtable *rt;
+	struct flowi6 fl6;
+
+	skb_reset_network_header(skb);
+
+	switch (ip_hdr(skb)->version) {
+	case IPVERSION:
+		ip_addr = ip_hdr(skb)->saddr;
+
+		rt = ip_route_output(&init_net, ip_addr, 0, 0, 0);
+		if (IS_ERR(rt)) {
+			return NULL;
+		}
+
+		dst = (struct dst_entry *)rt;
+		break;
+
+	case 6:
+		memset(&fl6, 0, sizeof(fl6));
+		memcpy(&fl6.daddr, &ipv6_hdr(skb)->saddr, sizeof(fl6.daddr));
+
+		dst = ip6_route_output(&init_net, NULL, &fl6);
+		if (IS_ERR(dst)) {
+			return NULL;
+		}
+		break;
+
+	default:
+		nss_ipsecmgr_warn("%p:could not get dev for the flow\n", skb);
+		return NULL;
+	}
+
+	dev = dst->dev;
+	dst_release(dst);
+
+	return dev;
+}
+
+/*
  * nss_ipsecmgr_tunnel_rx()
  *	receive NSS exception packets
  */
-static void nss_ipsecmgr_tunnel_rx(struct net_device *dev, struct sk_buff *skb, __attribute((unused)) struct napi_struct *napi)
+static void nss_ipsecmgr_tunnel_rx(struct net_device *dummy, struct sk_buff *skb, __attribute((unused)) struct napi_struct *napi)
 {
 	struct nss_ipsecmgr_priv *priv;
 	nss_ipsecmgr_data_cb_t cb_fn;
+	struct net_device *dev;
 	uint16_t next_protocol;
 	void *cb_ctx;
 
-	BUG_ON(dev == NULL);
+	BUG_ON(dummy == NULL);
 	BUG_ON(skb == NULL);
 
+	dev = nss_ipsecmgr_get_dev(skb);
+	if (unlikely(!dev)) {
+		nss_ipsecmgr_error("didn't find an tunnel dev\n");
+		dev_kfree_skb_any(skb);
+		return;
+	}
+
 	/* hold the device till we process it */
 	dev_hold(dev);
 
-	/*
-	 * XXX:need to ensure that the dev being accessed is not deleted
-	 */
 	priv = netdev_priv(dev);
 
 	skb->dev = dev;
@@ -392,7 +446,7 @@
 	 * if tunnel creator gave a callback then send the packet without
 	 * any modifications to him
 	 */
-	if (cb_fn && cb_ctx) {
+	if (cb_fn) {
 		cb_fn(cb_ctx, skb);
 		goto done;
 	}
@@ -411,7 +465,7 @@
 		break;
 
 	default:
-		nss_ipsecmgr_error("%p: Unsupported IP Version\n",  priv->nss_ctx);
+		nss_ipsecmgr_error("%s: Unsupported IP Version\n",  dev->name);
 		dev_kfree_skb_any(skb);
 		goto done;
 	}
@@ -443,9 +497,8 @@
  * nss_ipsecmgr_tunnel_notify()
  * 	asynchronous event reception
  */
-static void nss_ipsecmgr_tunnel_notify(void *app_data, struct nss_ipsec_msg *nim)
+static void nss_ipsecmgr_tunnel_notify(__attribute((unused))void *app_data, struct nss_ipsec_msg *nim)
 {
-	struct net_device *tun_dev = (struct net_device *)app_data;
 	struct nss_ipsecmgr_sa_stats *sa_stats;
 	struct nss_ipsec_node_stats *drv_stats;
 	struct nss_ipsecmgr_event stats_event;
@@ -456,7 +509,6 @@
 	struct nss_ipsecmgr_key key;
 	struct net_device *dev;
 
-	BUG_ON(tun_dev == NULL);
 	BUG_ON(nim == NULL);
 
 	/*
@@ -478,12 +530,12 @@
 		 */
 		nss_ipsecmgr_sa_sel2key(&nim->msg.sa_stats.sel, &key);
 
-		write_lock(&priv->lock);
+		write_lock(&ipsecmgr_ctx->lock);
 
-		ref = nss_ipsecmgr_sa_lookup(priv, &key);
+		ref = nss_ipsecmgr_sa_lookup(&key);
 		if (!ref) {
-			write_unlock(&priv->lock);
-			nss_ipsecmgr_info("event received on deallocated SA tunnel:(%d)\n", nim->tunnel_id);
+			write_unlock(&ipsecmgr_ctx->lock);
+			nss_ipsecmgr_error("event received on deallocated SA tunnel:(%d)\n", nim->tunnel_id);
 			goto done;
 		}
 
@@ -498,7 +550,7 @@
 		memcpy(&sa_stats->sa, &sa->sa_info, sizeof(struct nss_ipsecmgr_sa));
 		sa_stats->crypto_index = sa->nim.msg.push.data.crypto_index;
 
-		write_unlock(&priv->lock);
+		write_unlock(&ipsecmgr_ctx->lock);
 
 		/*
 		 * if event callback is available then post the statistics using the callback function
@@ -521,9 +573,9 @@
 
 	case NSS_IPSEC_MSG_TYPE_SYNC_NODE_STATS:
 
-		drv_stats = &ipsecmgr_ctx.enc_stats;
+		drv_stats = &ipsecmgr_ctx->enc_stats;
 		if (unlikely(nim->cm.interface == NSS_IPSEC_DECAP_IF_NUMBER)) {
-			drv_stats = &ipsecmgr_ctx.dec_stats;
+			drv_stats = &ipsecmgr_ctx->dec_stats;
 		}
 
 		memcpy(drv_stats, &nim->msg.node_stats, sizeof(struct nss_ipsec_node_stats));
@@ -542,22 +594,24 @@
  */
 static ssize_t nss_ipsecmgr_node_stats_read(struct file *fp, char __user *ubuf, size_t sz, loff_t *ppos)
 {
-	char *local;
+	struct nss_ipsec_node_stats *enc_stats = &ipsecmgr_ctx->enc_stats;
+	struct nss_ipsec_node_stats *dec_stats = &ipsecmgr_ctx->dec_stats;
 	ssize_t ret = 0;
+	char *local;
 	int len;
 
 	local = vmalloc(NSS_IPSECMGR_MAX_BUF_SZ);
 
 	len = 0;
-	len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "\tencap_enqueued: %d\n", ipsecmgr_ctx.enc_stats.enqueued);
-	len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "\tencap_completed: %d\n", ipsecmgr_ctx.enc_stats.completed);
-	len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "\tencap_exceptioned: %d\n", ipsecmgr_ctx.enc_stats.exceptioned);
-	len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "\tencap_enqueue_failed: %d\n", ipsecmgr_ctx.enc_stats.fail_enqueue);
+	len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "\tencap_enqueued: %d\n", enc_stats->enqueued);
+	len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "\tencap_completed: %d\n", enc_stats->completed);
+	len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "\tencap_exceptioned: %d\n", enc_stats->exceptioned);
+	len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "\tencap_enqueue_failed: %d\n", enc_stats->fail_enqueue);
 
-	len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "\tdecap_enqueued: %d\n", ipsecmgr_ctx.dec_stats.enqueued);
-	len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "\tdecap_completed: %d\n", ipsecmgr_ctx.dec_stats.completed);
-	len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "\tdecap_exceptioned: %d\n", ipsecmgr_ctx.dec_stats.exceptioned);
-	len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "\tdecap_enqueue_failed: %d\n", ipsecmgr_ctx.dec_stats.fail_enqueue);
+	len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "\tdecap_enqueued: %d\n", dec_stats->enqueued);
+	len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "\tdecap_completed: %d\n", dec_stats->completed);
+	len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "\tdecap_exceptioned: %d\n", dec_stats->exceptioned);
+	len += snprintf(local + len, NSS_IPSECMGR_MAX_BUF_SZ - len, "\tdecap_enqueue_failed: %d\n", dec_stats->fail_enqueue);
 
 	ret = simple_read_from_buffer(ubuf, sz, ppos, local, len + 1);
 
@@ -597,31 +651,13 @@
 	priv->cb_ctx = cb->ctx;
 	priv->data_cb = cb->data_fn;
 	priv->event_cb = cb->event_fn;
-	priv->nss_ctx = nss_ipsec_get_context();
-	priv->nss_ifnum = nss_ipsec_get_interface(priv->nss_ctx);
-	if (priv->nss_ifnum < 0) {
-		nss_ipsecmgr_error("Invalid nss interface :%d\n", priv->nss_ifnum);
-		goto fail;
-	}
-
-	rwlock_init(&priv->lock);
-	nss_ipsecmgr_init_sa_db(&priv->sa_db);
-	nss_ipsecmgr_init_netmask_db(&priv->net_db);
-	nss_ipsecmgr_init_flow_db(&priv->flow_db);
 
 	status = rtnl_is_locked() ? register_netdevice(dev) : register_netdev(dev);
 	if (status < 0) {
-		nss_ipsecmgr_error("register net dev failed :%d\n", priv->nss_ifnum);
+		nss_ipsecmgr_error("register net dev failed :%s\n", dev->name);
 		goto fail;
 	}
 
-	nss_ipsec_data_register(priv->nss_ifnum, nss_ipsecmgr_tunnel_rx, dev, 0);
-	nss_ipsec_notify_register(NSS_IPSEC_ENCAP_IF_NUMBER, nss_ipsecmgr_tunnel_notify, dev);
-	nss_ipsec_notify_register(NSS_IPSEC_DECAP_IF_NUMBER, nss_ipsecmgr_tunnel_notify, dev);
-
-	priv->dentry = debugfs_create_dir(dev->name, ipsecmgr_ctx.dentry);
-	init_completion(&priv->complete);
-
 	return dev;
 fail:
 	free_netdev(dev);
@@ -638,24 +674,12 @@
 {
 	struct nss_ipsecmgr_priv *priv = netdev_priv(dev);
 
-	/*
-	 * Unregister the callbacks from the HLOS as we are no longer
-	 * interested in exception data & async messages
-	 */
-	nss_ipsec_data_unregister(priv->nss_ctx, priv->nss_ifnum);
-
-	nss_ipsec_notify_unregister(priv->nss_ctx, NSS_IPSEC_ENCAP_IF_NUMBER);
-	nss_ipsec_notify_unregister(priv->nss_ctx, NSS_IPSEC_DECAP_IF_NUMBER);
 
 	priv->data_cb = NULL;
 	priv->event_cb = NULL;
 
 	nss_ipsecmgr_sa_flush_all(priv);
 
-	if (priv->dentry)  {
-		debugfs_remove_recursive(priv->dentry);
-	}
-
 	/*
 	 * The unregister should start here but the expectation is that the free would
 	 * happen when the reference count goes down to '0'
@@ -666,25 +690,97 @@
 }
 EXPORT_SYMBOL(nss_ipsecmgr_tunnel_del);
 
+static const struct net_device_ops nss_ipsecmgr_ipsec_ndev_ops;
+
+/*
+ * nss_ipsecmgr_dummy_netdevice_setup()
+ *	Setup function for dummy netdevice.
+ */
+static void nss_ipsecmgr_dummy_netdevice_setup(struct net_device *dev)
+{
+}
+
 /*
  * nss_ipsecmgr_init()
  *	module init
  */
 static int __init nss_ipsecmgr_init(void)
 {
+	int status;
+
 	if (!nss_cmn_get_nss_enabled()) {
 		nss_ipsecmgr_info_always("NSS is not enabled in this platform\n");
 		return 0;
 	}
 
+	ipsecmgr_ctx = vmalloc(sizeof(struct nss_ipsecmgr_drv));
+	if (!ipsecmgr_ctx) {
+		nss_ipsecmgr_info_always("Allocating ipsecmgr context failed\n");
+		return 0;
+	}
+
+	ipsecmgr_ctx->nss_ctx = nss_ipsec_get_context();
+	if (!ipsecmgr_ctx->nss_ctx) {
+		nss_ipsecmgr_info_always("Getting NSS Context failed\n");
+		goto free;
+	}
+
+	ipsecmgr_ctx->nss_ifnum = nss_ipsec_get_interface(ipsecmgr_ctx->nss_ctx);
+	if (ipsecmgr_ctx->nss_ifnum < 0) {
+		nss_ipsecmgr_info_always("%p: Invalid interface number  %d\n", ipsecmgr_ctx->nss_ctx, ipsecmgr_ctx->nss_ifnum);
+		goto free;
+	}
+
+	ipsecmgr_ctx->ndev = alloc_netdev(0, NSS_IPSECMGR_TUN_NAME, nss_ipsecmgr_dummy_netdevice_setup);
+	if (!ipsecmgr_ctx->ndev) {
+		nss_ipsecmgr_info_always("Ipsec: Could not allocate ipsec net_device\n");
+		goto free;
+	}
+
+	ipsecmgr_ctx->ndev->netdev_ops = &nss_ipsecmgr_ipsec_ndev_ops;
+
+	status = rtnl_is_locked() ? register_netdevice(ipsecmgr_ctx->ndev) : register_netdev(ipsecmgr_ctx->ndev);
+	if (status) {
+		nss_ipsecmgr_info_always("IPsec: Could not register ipsec net_device\n");
+		goto netdev_free;
+	}
+
+	rwlock_init(&ipsecmgr_ctx->lock);
+	nss_ipsecmgr_init_sa_db(&ipsecmgr_ctx->sa_db);
+	nss_ipsecmgr_init_netmask_db(&ipsecmgr_ctx->net_db);
+	nss_ipsecmgr_init_flow_db(&ipsecmgr_ctx->flow_db);
+
+
+	nss_ipsec_data_register(ipsecmgr_ctx->nss_ifnum, nss_ipsecmgr_tunnel_rx, ipsecmgr_ctx->ndev, 0);
+	nss_ipsec_notify_register(NSS_IPSEC_ENCAP_IF_NUMBER, nss_ipsecmgr_tunnel_notify, NULL);
+	nss_ipsec_notify_register(NSS_IPSEC_DECAP_IF_NUMBER, nss_ipsecmgr_tunnel_notify, NULL);
+
 	/*
 	 * initialize debugfs.
 	 */
-	ipsecmgr_ctx.dentry = debugfs_create_dir("qca-nss-ipsecmgr", NULL);
-	debugfs_create_file("stats", S_IRUGO, ipsecmgr_ctx.dentry, NULL, &node_stats_op);
+	ipsecmgr_ctx->dentry = debugfs_create_dir("qca-nss-ipsecmgr", NULL);
+	if (!ipsecmgr_ctx->dentry) {
+		nss_ipsecmgr_info_always("Creating debug directory failed\n");
+		goto unregister_dev;
+
+	}
+
+	debugfs_create_file("stats", S_IRUGO, ipsecmgr_ctx->dentry, NULL, &node_stats_op);
 
 	nss_ipsecmgr_info_always("NSS IPsec manager loaded: %s\n", NSS_CLIENT_BUILD_ID);
 	return 0;
+
+unregister_dev:
+	rtnl_is_locked() ? unregister_netdevice(ipsecmgr_ctx->ndev) : unregister_netdev(ipsecmgr_ctx->ndev);
+
+netdev_free:
+	free_netdev(ipsecmgr_ctx->ndev);
+
+free:
+	vfree(ipsecmgr_ctx);
+	ipsecmgr_ctx = NULL;
+
+	return 0;
 }
 
 /*
@@ -693,15 +789,46 @@
  */
 static void __exit nss_ipsecmgr_exit(void)
 {
-	nss_ipsecmgr_info_always("NSS IPsec manager unloaded\n");
+	if (!ipsecmgr_ctx) {
+		nss_ipsecmgr_info_always("Invalid ipsecmgr Context\n");
+		return;
+	}
+
+	if (!ipsecmgr_ctx->nss_ctx) {
+		nss_ipsecmgr_info_always("Invalid NSS Context\n");
+		vfree(ipsecmgr_ctx);
+		ipsecmgr_ctx = NULL;
+		return;
+	}
 
 	/*
-	 * cleanup debugfs.
+	 * Unregister the callbacks from the HLOS as we are no longer
+	 * interested in exception data & async messages
 	 */
-	if (ipsecmgr_ctx.dentry) {
-		debugfs_remove_recursive(ipsecmgr_ctx.dentry);
+	nss_ipsec_data_unregister(ipsecmgr_ctx->nss_ctx, ipsecmgr_ctx->nss_ifnum);
+
+	nss_ipsec_notify_unregister(ipsecmgr_ctx->nss_ctx, NSS_IPSEC_ENCAP_IF_NUMBER);
+	nss_ipsec_notify_unregister(ipsecmgr_ctx->nss_ctx, NSS_IPSEC_DECAP_IF_NUMBER);
+
+	if (ipsecmgr_ctx->ndev) {
+		rtnl_is_locked() ? unregister_netdevice(ipsecmgr_ctx->ndev) : unregister_netdev(ipsecmgr_ctx->ndev);
 	}
 
+	/*
+	 * Remove debugfs directory and entries below that.
+	 */
+	if (ipsecmgr_ctx->dentry) {
+		debugfs_remove_recursive(ipsecmgr_ctx->dentry);
+	}
+
+	/*
+	 * Free the ipsecmgr ctx
+	 */
+	vfree(ipsecmgr_ctx);
+	ipsecmgr_ctx = NULL;
+
+	nss_ipsecmgr_info_always("NSS IPsec manager unloaded\n");
+
 }
 
 MODULE_LICENSE("Dual BSD/GPL");
diff --git a/ipsecmgr/nss_ipsecmgr_flow.c b/ipsecmgr/nss_ipsecmgr_flow.c
index 6d01acd..a6f8686 100644
--- a/ipsecmgr/nss_ipsecmgr_flow.c
+++ b/ipsecmgr/nss_ipsecmgr_flow.c
@@ -33,6 +33,8 @@
 
 #include "nss_ipsecmgr_priv.h"
 
+extern struct nss_ipsecmgr_drv *ipsecmgr_ctx;
+
 /*
  *
  * nss_ipsecmgr_flow_resp()
@@ -80,7 +82,7 @@
 	 */
 	nss_ipsecmgr_copy_nim(&flow->nim, &nss_nim);
 
-	if (nss_ipsec_tx_msg(priv->nss_ctx, &nss_nim) != NSS_TX_SUCCESS) {
+	if (nss_ipsec_tx_msg(ipsecmgr_ctx->nss_ctx, &nss_nim) != NSS_TX_SUCCESS) {
 		/*
 		 * XXX: Stop the TX queue and add this "entry"
 		 * to pending queue
@@ -109,7 +111,7 @@
 	 */
 	nss_ipsecmgr_copy_nim(&flow->nim, &nss_nim);
 
-	if (nss_ipsec_tx_msg(priv->nss_ctx, &nss_nim) != NSS_TX_SUCCESS) {
+	if (nss_ipsec_tx_msg(ipsecmgr_ctx->nss_ctx, &nss_nim) != NSS_TX_SUCCESS) {
 		/*
 		 * XXX: add this "entry" to pending queue
 		 */
@@ -378,7 +380,7 @@
  */
 struct nss_ipsecmgr_ref *nss_ipsecmgr_flow_lookup(struct nss_ipsecmgr_priv *priv, struct nss_ipsecmgr_key *key)
 {
-	struct nss_ipsecmgr_flow_db *db = &priv->flow_db;
+	struct nss_ipsecmgr_flow_db *db = &ipsecmgr_ctx->flow_db;
 	struct nss_ipsecmgr_flow_entry *entry;
 	struct list_head *head;
 	int idx;
@@ -426,7 +428,7 @@
 	/*
 	 * add flow to the database
 	 */
-	db = &priv->flow_db;
+	db = &ipsecmgr_ctx->flow_db;
 	INIT_LIST_HEAD(&flow->node);
 
 	/*
@@ -468,9 +470,9 @@
 		/*
 		 * flow lookup is done with read lock
 		 */
-		read_lock_bh(&priv->lock);
+		read_lock_bh(&ipsecmgr_ctx->lock);
 		flow_ref = nss_ipsecmgr_flow_lookup(priv, &flow_key);
-		read_unlock_bh(&priv->lock);
+		read_unlock_bh(&ipsecmgr_ctx->lock);
 
 		/*
 		 * if flow is found then proceed with the TX
@@ -488,11 +490,11 @@
 		/*
 		 * write lock as it can update the flow database
 		 */
-		write_lock_bh(&priv->lock);
+		write_lock_bh(&ipsecmgr_ctx->lock);
 
 		subnet_ref = nss_ipsecmgr_v4_subnet_match(priv, &subnet_key);
 		if (!subnet_ref) {
-			write_unlock_bh(&priv->lock);
+			write_unlock_bh(&ipsecmgr_ctx->lock);
 			return false;
 		}
 
@@ -509,7 +511,7 @@
 
 		flow_ref = nss_ipsecmgr_flow_alloc(priv, &flow_key);
 		if (!flow_ref) {
-			write_unlock_bh(&priv->lock);
+			write_unlock_bh(&ipsecmgr_ctx->lock);
 			return false;
 		}
 
@@ -519,7 +521,7 @@
 		nss_ipsecmgr_ref_add(flow_ref, subnet_ref);
 		nss_ipsecmgr_ref_update(priv, flow_ref, &nim);
 
-		write_unlock_bh(&priv->lock);
+		write_unlock_bh(&ipsecmgr_ctx->lock);
 
 		break;
 
@@ -532,9 +534,9 @@
 		/*
 		 * flow lookup is done with read lock
 		 */
-		read_lock_bh(&priv->lock);
+		read_lock_bh(&ipsecmgr_ctx->lock);
 		flow_ref = nss_ipsecmgr_flow_lookup(priv, &flow_key);
-		read_unlock_bh(&priv->lock);
+		read_unlock_bh(&ipsecmgr_ctx->lock);
 
 		/*
 		 * if flow is found then proceed with the TX
@@ -553,11 +555,11 @@
 		/*
 		 * write lock as it can update the flow database
 		 */
-		write_lock(&priv->lock);
+		write_lock(&ipsecmgr_ctx->lock);
 
 		subnet_ref = nss_ipsecmgr_v6_subnet_match(priv, &subnet_key);
 		if (!subnet_ref) {
-			write_unlock(&priv->lock);
+			write_unlock(&ipsecmgr_ctx->lock);
 			return false;
 		}
 
@@ -573,7 +575,7 @@
 		 */
 		flow_ref = nss_ipsecmgr_flow_alloc(priv, &flow_key);
 		if (!flow_ref) {
-			write_unlock(&priv->lock);
+			write_unlock(&ipsecmgr_ctx->lock);
 			return false;
 		}
 
@@ -583,7 +585,7 @@
 		nss_ipsecmgr_ref_add(flow_ref, subnet_ref);
 		nss_ipsecmgr_ref_update(priv, flow_ref, &nim);
 
-		write_unlock(&priv->lock);
+		write_unlock(&ipsecmgr_ctx->lock);
 		break;
 
 	default:
diff --git a/ipsecmgr/nss_ipsecmgr_priv.h b/ipsecmgr/nss_ipsecmgr_priv.h
index 122382d..81d6e82 100644
--- a/ipsecmgr/nss_ipsecmgr_priv.h
+++ b/ipsecmgr/nss_ipsecmgr_priv.h
@@ -284,21 +284,11 @@
  */
 struct nss_ipsecmgr_priv {
 	struct net_device *dev;			/* back pointer to tunnel device */
-	rwlock_t lock;				/* lock for all DB operations */
-
-	struct nss_ipsecmgr_sa_db sa_db;	/* SA database */
-	struct nss_ipsecmgr_netmask_db net_db;	/* Subnet mask database */
-	struct nss_ipsecmgr_flow_db flow_db;	/* flow database */
 
 	void *cb_ctx;				/* callback context */
 	nss_ipsecmgr_data_cb_t data_cb;		/* data callback function */
 	nss_ipsecmgr_event_cb_t event_cb;	/* event callback function */
 
-	uint32_t nss_ifnum;			/* NSS interface for sending data */
-	struct nss_ctx_instance *nss_ctx;	/* NSS context */
-
-	struct dentry *dentry;			/* Tunnel device debugfs entry */
-	struct completion complete;		/* completion for flow stats nss msg */
 };
 
 /*
@@ -306,6 +296,17 @@
  */
 struct nss_ipsecmgr_drv {
 	struct dentry *dentry;			/* Debugfs entry per ipsecmgr module */
+	struct net_device *ndev;		/* IPsec dummy net device */
+
+	rwlock_t lock;				/* lock for all DB operations */
+
+	struct nss_ipsecmgr_sa_db sa_db;	/* SA database */
+	struct nss_ipsecmgr_netmask_db net_db;	/* Subnet mask database */
+	struct nss_ipsecmgr_flow_db flow_db;	/* flow database */
+
+	uint32_t nss_ifnum;			/* NSS interface for sending data */
+
+	struct nss_ctx_instance *nss_ctx;	/* NSS context */
 
 	struct nss_ipsec_node_stats enc_stats;	/* Encap node stats */
 	struct nss_ipsec_node_stats dec_stats;	/* Decap node stats */
@@ -1072,7 +1073,7 @@
  * SA alloc/lookup/flush API(s)
  */
 struct nss_ipsecmgr_ref *nss_ipsecmgr_sa_alloc(struct nss_ipsecmgr_priv *priv, struct nss_ipsecmgr_key *key);
-struct nss_ipsecmgr_ref *nss_ipsecmgr_sa_lookup(struct nss_ipsecmgr_priv *priv, struct nss_ipsecmgr_key *key);
+struct nss_ipsecmgr_ref *nss_ipsecmgr_sa_lookup(struct nss_ipsecmgr_key *key);
 void nss_ipsecmgr_sa_flush_all(struct nss_ipsecmgr_priv *priv);
 
 #endif /* __NSS_IPSECMGR_PRIV_H */
diff --git a/ipsecmgr/nss_ipsecmgr_sa.c b/ipsecmgr/nss_ipsecmgr_sa.c
index 063ef12..f6d678e 100644
--- a/ipsecmgr/nss_ipsecmgr_sa.c
+++ b/ipsecmgr/nss_ipsecmgr_sa.c
@@ -32,6 +32,8 @@
 
 #include "nss_ipsecmgr_priv.h"
 
+extern struct nss_ipsecmgr_drv *ipsecmgr_ctx;
+
 /*
  * SA operation info
  */
@@ -76,7 +78,7 @@
 	/*
 	 * lock database
 	 */
-	write_lock_bh(&priv->lock);
+	write_lock_bh(&ipsecmgr_ctx->lock);
 
 	/*
 	 * search the flow for deletion
@@ -86,7 +88,7 @@
 		/*
 		 * unlock device
 		 */
-		write_unlock_bh(&priv->lock);
+		write_unlock_bh(&ipsecmgr_ctx->lock);
 
 		nss_ipsecmgr_warn("%p:failed to lookup child_entry\n", priv->dev);
 		nss_ipsecmgr_trace("%p:child_lookup(%p)\n", priv, info->child_lookup);
@@ -96,9 +98,9 @@
 	/*
 	 * search the SA in sa_db
 	 */
-	sa_ref = nss_ipsecmgr_sa_lookup(priv, &info->sa_key);
+	sa_ref = nss_ipsecmgr_sa_lookup(&info->sa_key);
 	if (!sa_ref) {
-		write_unlock_bh(&priv->lock);
+		write_unlock_bh(&ipsecmgr_ctx->lock);
 
 		nss_ipsecmgr_warn("%p:failed to lookup sa_entry\n", priv->dev);
 		return false;
@@ -116,7 +118,7 @@
 	 */
 	nss_ipsecmgr_sa_free(priv, sa_ref);
 
-	write_unlock_bh(&priv->lock);
+	write_unlock_bh(&ipsecmgr_ctx->lock);
 	return true;
 }
 
@@ -135,7 +137,7 @@
 	/*
 	 * lock database
 	 */
-	write_lock_bh(&priv->lock);
+	write_lock_bh(&ipsecmgr_ctx->lock);
 
 	/*
 	 * allocate a flow, this returns either a new flow or an existing
@@ -146,7 +148,7 @@
 		/*
 		 * unlock device
 		 */
-		write_unlock_bh(&priv->lock);
+		write_unlock_bh(&ipsecmgr_ctx->lock);
 
 		nss_ipsecmgr_warn("%p:failed to alloc child_entry\n", priv->dev);
 		nss_ipsecmgr_trace("%p:child_alloc(%p)\n", priv, info->child_alloc);
@@ -163,7 +165,7 @@
 		 * release the flow and unlock device
 		 */
 		nss_ipsecmgr_ref_free(priv, child_ref);
-		write_unlock_bh(&priv->lock);
+		write_unlock_bh(&ipsecmgr_ctx->lock);
 
 		nss_ipsecmgr_warn("%p:failed to alloc sa_entry\n", priv->dev);
 		return false;
@@ -195,7 +197,7 @@
 	 */
 	nss_ipsecmgr_ref_update(priv, child_ref, &info->nim);
 
-	write_unlock_bh(&priv->lock);
+	write_unlock_bh(&ipsecmgr_ctx->lock);
 	return true;
 }
 
@@ -213,7 +215,7 @@
 	/*
 	 * Search the object in the database first
 	 */
-	ref = nss_ipsecmgr_sa_lookup(priv, key);
+	ref = nss_ipsecmgr_sa_lookup(key);
 	if (ref) {
 		return ref;
 	}
@@ -236,7 +238,7 @@
 	 * initialize sa list node
 	 */
 	ref = &sa->ref;
-	db = &priv->sa_db;
+	db = &ipsecmgr_ctx->sa_db;
 	INIT_LIST_HEAD(&sa->node);
 
 	/*
@@ -425,9 +427,9 @@
  * nss_ipsecmgr_sa_lookup()
  * 	lookup the SA in the sa_db
  */
-struct nss_ipsecmgr_ref *nss_ipsecmgr_sa_lookup(struct nss_ipsecmgr_priv *priv, struct nss_ipsecmgr_key *key)
+struct nss_ipsecmgr_ref *nss_ipsecmgr_sa_lookup(struct nss_ipsecmgr_key *key)
 {
-	struct nss_ipsecmgr_sa_db *db = &priv->sa_db;
+	struct nss_ipsecmgr_sa_db *db = &ipsecmgr_ctx->sa_db;
 	struct nss_ipsecmgr_sa_entry *entry;
 	struct list_head *head;
 	int idx;
@@ -450,8 +452,9 @@
  */
 struct rtnl_link_stats64 *nss_ipsecmgr_sa_stats_all(struct nss_ipsecmgr_priv *priv, struct rtnl_link_stats64 *stats)
 {
-	struct nss_ipsecmgr_sa_db *sa_db = &priv->sa_db;
+	struct nss_ipsecmgr_sa_db *sa_db = &ipsecmgr_ctx->sa_db;
 	struct nss_ipsecmgr_sa_entry *sa;
+	int ifindex = priv->dev->ifindex;
 	struct list_head *head;
 	int i;
 
@@ -460,13 +463,18 @@
 	/*
 	 * trigger a stats update chain
 	 */
-	read_lock_bh(&priv->lock);
+	read_lock_bh(&ipsecmgr_ctx->lock);
 
 	/*
 	 * walk the SA database for each entry and get stats for attached SA
 	 */
 	for (i = 0, head = sa_db->entries; i < NSS_IPSECMGR_MAX_SA; i++, head++) {
 		list_for_each_entry(sa, head, node) {
+
+			if (sa->nim.tunnel_id != ifindex) {
+				continue;
+			}
+
 			/*
 			 * Check the SA type (ENCAP or DECAP)
 			 */
@@ -499,7 +507,7 @@
 		}
 	}
 
-	read_unlock_bh(&priv->lock);
+	read_unlock_bh(&ipsecmgr_ctx->lock);
 
 	return stats;
 }
@@ -510,30 +518,33 @@
  */
 void nss_ipsecmgr_sa_flush_all(struct nss_ipsecmgr_priv *priv)
 {
-	struct nss_ipsecmgr_sa_db *sa_db = &priv->sa_db;
+	struct nss_ipsecmgr_sa_db *sa_db = &ipsecmgr_ctx->sa_db;
 	struct nss_ipsecmgr_sa_entry *entry;
+	int ifindex  = priv->dev->ifindex;
 	struct list_head *head;
 	int i;
 
 	/*
 	 * lock database
 	 */
-	write_lock_bh(&priv->lock);
+	write_lock_bh(&ipsecmgr_ctx->lock);
 
 	/*
-	 * walk the SA database for each entry and delete the attached SA
+	 * walk the SA database for each entry and delete the attached SA.
+	 * Assumption is that single SA cannot be associated to multiple ipsectunX interfaces.
 	 */
 	for (i = 0, head = sa_db->entries; i < NSS_IPSECMGR_MAX_SA; i++, head++) {
-		while (!list_empty(head)) {
-			entry = list_first_entry(head, struct nss_ipsecmgr_sa_entry, node);
-			nss_ipsecmgr_ref_free(priv, &entry->ref);
+		list_for_each_entry(entry, head, node) {
+			if (entry->nim.tunnel_id == ifindex) {
+				nss_ipsecmgr_ref_free(priv, &entry->ref);
+			}
 		}
 	}
 
 	/*
 	 * unlock database
 	 */
-	write_unlock_bh(&priv->lock);
+	write_unlock_bh(&ipsecmgr_ctx->lock);
 }
 
 /*
@@ -803,14 +814,14 @@
 	/*
 	 * lock database
 	 */
-	write_lock_bh(&priv->lock);
+	write_lock_bh(&ipsecmgr_ctx->lock);
 
 	/*
 	 * search the SA in sa_db
 	 */
-	sa_ref = nss_ipsecmgr_sa_lookup(priv, &sa_key);
+	sa_ref = nss_ipsecmgr_sa_lookup(&sa_key);
 	if (!sa_ref) {
-		write_unlock_bh(&priv->lock);
+		write_unlock_bh(&ipsecmgr_ctx->lock);
 		nss_ipsecmgr_warn("%p:failed to lookup SA\n", priv);
 		return false;
 	}
@@ -823,7 +834,7 @@
 	/*
 	 * unlock database
 	 */
-	write_unlock_bh(&priv->lock);
+	write_unlock_bh(&ipsecmgr_ctx->lock);
 
 	return true;
 }
diff --git a/ipsecmgr/nss_ipsecmgr_subnet.c b/ipsecmgr/nss_ipsecmgr_subnet.c
index a0d474a..4362b72 100644
--- a/ipsecmgr/nss_ipsecmgr_subnet.c
+++ b/ipsecmgr/nss_ipsecmgr_subnet.c
@@ -35,6 +35,8 @@
 
 #include "nss_ipsecmgr_priv.h"
 
+extern struct nss_ipsecmgr_drv *ipsecmgr_ctx;
+
 /*
  * nss_ipsecmgr_subnet_key_data2idx()
  * 	subnet specific api for converting word stream to index
@@ -185,7 +187,7 @@
 	 */
 	list_del(&subnet->node);
 
-	nss_ipsecmgr_netmask_free(&priv->net_db, &subnet->key);
+	nss_ipsecmgr_netmask_free(&ipsecmgr_ctx->net_db, &subnet->key);
 	kfree(subnet);
 }
 
@@ -195,7 +197,7 @@
  */
 static inline struct nss_ipsecmgr_netmask_entry *nss_ipsecmgr_netmask_lookup(struct nss_ipsecmgr_priv *priv, struct nss_ipsecmgr_key *key)
 {
-	struct nss_ipsecmgr_netmask_db *db = &priv->net_db;
+	struct nss_ipsecmgr_netmask_db *db = &ipsecmgr_ctx->net_db;
 	uint32_t idx;
 
 	if (nss_ipsecmgr_netmask_is_default(key)) {
@@ -212,7 +214,7 @@
  */
 static struct nss_ipsecmgr_netmask_entry *nss_ipsecmgr_netmask_alloc(struct nss_ipsecmgr_priv *priv, struct nss_ipsecmgr_key *key)
 {
-	struct nss_ipsecmgr_netmask_db *db = &priv->net_db;
+	struct nss_ipsecmgr_netmask_db *db = &ipsecmgr_ctx->net_db;
 	struct nss_ipsecmgr_netmask_entry *entry;
 	int idx;
 
@@ -356,7 +358,7 @@
  */
 struct nss_ipsecmgr_ref *nss_ipsecmgr_v4_subnet_match(struct nss_ipsecmgr_priv *priv, struct nss_ipsecmgr_key *key)
 {
-	struct nss_ipsecmgr_netmask_db *db = &priv->net_db;
+	struct nss_ipsecmgr_netmask_db *db = &ipsecmgr_ctx->net_db;
 	struct nss_ipsecmgr_key tmp_key;
 	struct nss_ipsecmgr_ref *ref;
 	int i;
@@ -404,7 +406,7 @@
  */
 struct nss_ipsecmgr_ref *nss_ipsecmgr_v6_subnet_match(struct nss_ipsecmgr_priv *priv, struct nss_ipsecmgr_key *key)
 {
-	struct nss_ipsecmgr_netmask_db *db = &priv->net_db;
+	struct nss_ipsecmgr_netmask_db *db = &ipsecmgr_ctx->net_db;
 	struct nss_ipsecmgr_key tmp_key;
 	struct nss_ipsecmgr_ref *ref;
 	int i;
@@ -502,7 +504,7 @@
 	 */
 	subnet = kzalloc(sizeof(struct nss_ipsecmgr_subnet_entry), GFP_ATOMIC);
 	if (!subnet) {
-		nss_ipsecmgr_netmask_free(&priv->net_db, key);
+		nss_ipsecmgr_netmask_free(&ipsecmgr_ctx->net_db, key);
 		return NULL;
 	}
 
diff --git a/netlink/nss_nlipv4.c b/netlink/nss_nlipv4.c
index e30c508..dd2a268 100755
--- a/netlink/nss_nlipv4.c
+++ b/netlink/nss_nlipv4.c
@@ -301,10 +301,19 @@
 	}
 
 	/*
-	 * update flow and return interface numbers
+	 * update flow and return interface numbers. Handle Ipsec interfaces seperately.
 	 */
-	conn->flow_interface_num = nss_cmn_get_interface_number_by_dev(flow_dev);
-	conn->return_interface_num = nss_cmn_get_interface_number_by_dev(return_dev);
+	if (flow_dev->type == NSS_IPSEC_ARPHRD_IPSEC) {
+		conn->flow_interface_num = nss_ipsec_get_interface(nss_ipsec_get_context());
+	} else {
+		conn->flow_interface_num = nss_cmn_get_interface_number_by_dev(flow_dev);
+	}
+
+	if (return_dev->type == NSS_IPSEC_ARPHRD_IPSEC) {
+		conn->flow_interface_num = nss_ipsec_get_interface(nss_ipsec_get_context());
+	} else {
+		conn->return_interface_num = nss_cmn_get_interface_number_by_dev(return_dev);
+	}
 
 	/*
 	 * update the flow & return MTU(s)
diff --git a/netlink/nss_nlipv6.c b/netlink/nss_nlipv6.c
index 42a90d5..d59b2f9 100644
--- a/netlink/nss_nlipv6.c
+++ b/netlink/nss_nlipv6.c
@@ -302,10 +302,19 @@
 	}
 
 	/*
-	 * update flow and return interface numbers
+	 * update flow and return interface numbers. Handle Ipsec interfaces seperately.
 	 */
-	conn->flow_interface_num = nss_cmn_get_interface_number_by_dev(flow_dev);
-	conn->return_interface_num = nss_cmn_get_interface_number_by_dev(return_dev);
+	if (flow_dev->type == NSS_IPSEC_ARPHRD_IPSEC) {
+		conn->flow_interface_num = nss_ipsec_get_interface(nss_ipsec_get_context());
+	} else {
+		conn->flow_interface_num = nss_cmn_get_interface_number_by_dev(flow_dev);
+	}
+
+	if (return_dev->type == NSS_IPSEC_ARPHRD_IPSEC) {
+		conn->flow_interface_num = nss_ipsec_get_interface(nss_ipsec_get_context());
+	} else {
+		conn->return_interface_num = nss_cmn_get_interface_number_by_dev(return_dev);
+	}
 
 	/*
 	 * update the flow & return MTU(s)