[qca-nss-clients] Clients module for gre_redir_lag family.

1. Provide netlink support for gre_redir_lag family.
2. Command supported: create, destroy, map, unmap, set_next, add_hash, del_hash.

Change-Id: I9dac36e0637ee45a00433ad7cec0e2be0d15af8f
Signed-off-by: Himanshu Joshi <himajosh@codeaurora.org>
diff --git a/netlink/Makefile b/netlink/Makefile
index ecdba8c..3072316 100644
--- a/netlink/Makefile
+++ b/netlink/Makefile
@@ -8,11 +8,14 @@
 ccflags-y += -DCONFIG_NSS_NLIPV6=1
 ccflags-y += -DCONFIG_NSS_NLIPSEC=1
 ccflags-y += -DCONFIG_NSS_NLOAM=1
-ccflags-y += -DCONFIG_NSS_NLGRE_REDIR=1
+ccflags-y += -DCONFIG_NSS_NLGRE_REDIR_FAMILY=1
 
 qca-nss-netlink-objs := nss_nl.o
+qca-nss-netlink-objs += nss_nlgre_redir_family.o
+qca-nss-netlink-objs += nss_nlgre_redir_cmd.o
+qca-nss-netlink-objs += nss_nlgre_redir_cmn.o
 qca-nss-netlink-objs += nss_nlgre_redir.o
-qca-nss-netlink-objs += nss_nlgre_redir_mgr.o
+qca-nss-netlink-objs += nss_nlgre_redir_lag.o
 qca-nss-netlink-objs += nss_nlipv4.o
 qca-nss-netlink-objs += nss_nlipv6.o
 qca-nss-netlink-objs += nss_nlipsec.o
diff --git a/netlink/include/nss_nlgre_redir_if.h b/netlink/include/nss_nlgre_redir_if.h
index 1b8a376..95ddc5c 100644
--- a/netlink/include/nss_nlgre_redir_if.h
+++ b/netlink/include/nss_nlgre_redir_if.h
@@ -17,43 +17,13 @@
 #ifndef __NSS_NLGRE_REDIR_IF_H
 #define __NSS_NLGRE_REDIR_IF_H
 
-/**
- * @file nss_nlgre_redir_if.h
- *	NSS Netlink gre_redir headers
- */
 #define NSS_NLGRE_REDIR_TUN_TYPE_MAX_SZ 16		/**< Maximum length of tunnel type */
 #define NSS_NLGRE_REDIR_MODE_MAX_SZ 16			/**< Maximum length of mode */
-#define NSS_NLGRE_REDIR_NEEDED_HEADROOM 192		/**< Maximum headroom available */
-#define NSS_NLGRE_REDIR_RADIO_ID_MAX 2			/**< Radio Id max size */
-#define NSS_NLGRE_REDIR_RADIO_ID_MIN 0			/**< Radio Id min size */
-#define NSS_NLGRE_REDIR_VAP_ID_MAX 16			/**< Vap Id max size */
-#define NSS_NLGRE_REDIR_VAP_ID_MIN 0			/**< Vap Id min size */
-#define NSS_NLGRE_REDIR_MAX_TUNNELS 24			/**< Maximum number of tunnels allowed */
 #define NSS_NLGRE_REDIR_FAMILY "nss_nlgre_redir"	/**< Family */
 #define NSS_NLGRE_REDIR_MCAST_GRP "nss_nlgrerdr_mc"	/**< Multicast group */
 
 /**
- * @brief netdevice private data
- */
-struct nss_gre_redir_ndev_priv {
-	uint32_t gre_seq;				/**< Sequence number */
-};
-
-/**
- * @brief context need to be maintained globally for GRE redirect tunnel.
- */
-struct nss_nlgre_redir_pvt_data {
-	struct net_device *dev;				/**< Net device */
-	int32_t host_inner_ifnum;			/**< Interface no. of pnode host inner */
-	int32_t wifi_offl_inner_ifnum;			/**< Interface no. of pnode wifi offld inner */
-	int32_t sjack_inner_ifnum;			/**< Interface no. of pnode sjack inner */
-	int32_t outer_ifnum;				/**< Interface no. of pnode outer */
-	int32_t vap_nss_if;				/**< Interface no. of vap */
-	bool enable;					/**< Device is enabled or not */
-};
-
-/**
- * @brief enumeration for all command types.
+ * @brief Enumeration for all command types.
  */
 enum nss_nlgre_redir_cmd_type {
 	NSS_NLGRE_REDIR_CMD_TYPE_UNKNOWN,		/**< Unknown command type */
@@ -62,17 +32,24 @@
 	NSS_NLGRE_REDIR_CMD_TYPE_MAP,			/**< Map the vap interface to tunnel id */
 	NSS_NLGRE_REDIR_CMD_TYPE_UNMAP,			/**< Unmap vap and tunnel id */
 	NSS_NLGRE_REDIR_CMD_TYPE_SET_NEXT_HOP,		/**< Set the next hop of inner interface */
+	NSS_NLGRE_REDIR_CMD_TYPE_ADD_HASH,		/**< Add a hash entry*/
+	NSS_NLGRE_REDIR_CMD_TYPE_DEL_HASH,		/**< Delete a hash entry */
 	NSS_NLGRE_REDIR_CMD_TYPE_MAX,			/**< Max number of commands */
 };
 
 /**
- * @brief parameters to create a tunnel.
+ * @brief Parameters to create a tunnel.
  */
 struct nss_nlgre_redir_create_tun {
-	uint32_t sip[4];				/**< Src IP address */
-	uint32_t dip[4];				/**< Dest IP address */
-	uint16_t iptype;				/**< Ip version */
-	uint8_t res[2];					/**< Padding to make size multiple of 4 */
+	uint32_t sip[4];				/**< Src address of tunnel */
+	uint32_t dip[4];				/**< Dest address of tunnel */
+	uint32_t ssip[4];				/**< Src address of second tunnel */
+	uint32_t sdip[4];				/**< Dest address of second tunnel */
+	uint8_t hash_mode;				/**< Indicates how the traffic should be mapped */
+	uint8_t iptype;					/**< IPv4 = 1 and IPV6 = 2 */
+	char mode[NSS_NLGRE_REDIR_MODE_MAX_SZ];		/**< Mode can be sjack or wifi */
+	bool lag_enable;				/**< Indicates whether lag is enabled or not */
+	uint8_t res;					/**< Padding to make size multiple of 4 */
 };
 
 /**
@@ -114,6 +91,18 @@
 };
 
 /**
+ * @brief parameters to perform hash operations.
+ */
+struct nss_nlgre_redir_hash_ops {
+	uint16_t slave;					/**< Tunnel to which the traffic should be mapped */
+	uint8_t smac[6];				/**< Source mac address */
+	uint8_t dmac[6];				/**< Destination mac address */
+	char mode[NSS_NLGRE_REDIR_MODE_MAX_SZ];    	/**< Sjack or wifi */
+};
+
+/**@}*/
+
+/**
  * @brief gre_redir message
  */
 struct nss_nlgre_redir_rule {
@@ -128,6 +117,7 @@
 		struct nss_nlgre_redir_map map;			/**< Map the interface */
 		struct nss_nlgre_redir_unmap unmap;		/**< Unmap the interface */
 		struct nss_nlgre_redir_set_next snext;		/**< Set next hop */
+		struct nss_nlgre_redir_hash_ops hash_ops;	/**< Add and del hash value(s) */
 	} msg;
 };
 
diff --git a/netlink/nss_nl.c b/netlink/nss_nl.c
index f8e8e6d..6e697ca 100644
--- a/netlink/nss_nl.c
+++ b/netlink/nss_nl.c
@@ -33,7 +33,7 @@
 #include "nss_nlipv6_if.h"
 #include "nss_nlipsec_if.h"
 #include "nss_nlgre_redir_if.h"
-#include "nss_nlgre_redir.h"
+#include "nss_nlgre_redir_family.h"
 #include "nss_nloam_if.h"
 #if defined (CONFIG_NSS_NLCRYPTO)
 #include "nss_nlcrypto_if.h"
@@ -129,9 +129,9 @@
 		 * NSS_NLGRE_REDIR
 		 */
 		.name = NSS_NLGRE_REDIR_FAMILY,		/* gre_redir */
-		.entry = NSS_NLGRE_REDIR_INIT,		/* init */
-		.exit = NSS_NLGRE_REDIR_EXIT,		/* exit */
-		.valid = CONFIG_NSS_NLGRE_REDIR		/* 1 or 0 */
+		.entry = NSS_NLGRE_REDIR_FAMILY_INIT,	/* init */
+		.exit = NSS_NLGRE_REDIR_FAMILY_EXIT,	/* exit */
+		.valid = CONFIG_NSS_NLGRE_REDIR_FAMILY	/* 1 or 0 */
 	},
 
 };
diff --git a/netlink/nss_nlgre_redir.c b/netlink/nss_nlgre_redir.c
index 5f3e296..4fccc26 100644
--- a/netlink/nss_nlgre_redir.c
+++ b/netlink/nss_nlgre_redir.c
@@ -1,5 +1,5 @@
 /*
- **************************************************************************
+ ***************************************************************************
  * Copyright (c) 2015-2016,2018-2019, 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
@@ -11,361 +11,164 @@
  * 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_nlgre_redir.c
- * 	NSS Netlink gre_redir Handler
- */
 #include <linux/version.h>
+#include <linux/etherdevice.h>
+#include <linux/if_ether.h>
 #include <net/genetlink.h>
 #include <nss_api_if.h>
-#include <nss_nlcmn_if.h>
 #include <nss_nl_if.h>
-#include <nss_nlgre_redir_if.h>
+#include "nss_nlcmn_if.h"
 #include "nss_nl.h"
+#include "nss_nlgre_redir_if.h"
+#include "nss_nlgre_redir_cmn.h"
 #include "nss_nlgre_redir.h"
-#include "nss_nlgre_redir_mgr.h"
 
 /*
- * Get max size of the array
+ * nss_nlgre_redir_destroy_tun()
+ * 	Destroys the gre_redir tunnel
  */
-#define NSS_NLGRE_REDIR_OPS_SZ ARRAY_SIZE(nss_nlgre_redir_ops)
-
-/*
- * nss_nlgre_redir_family
- * 	Gre_redir family definition
- */
-static struct genl_family nss_nlgre_redir_family = {
-	.id = GENL_ID_GENERATE,				/* Auto generate ID */
-	.name = NSS_NLGRE_REDIR_FAMILY,			/* family name string */
-	.hdrsize = sizeof(struct nss_nlgre_redir_rule),	/* NSS NETLINK gre_redir rule */
-	.version = NSS_NL_VER,				/* Set it to NSS_NLGRE_REDIR version */
-	.maxattr = NSS_NLGRE_REDIR_CMD_TYPE_MAX,	/* maximum commands supported */
-	.netnsok = true,
-	.pre_doit = NULL,
-	.post_doit = NULL,
-};
-
-/*
- * nss_nlgre_redir_mcgrp
- * 	Multicast group for sending message status & events
- */
-static const struct genl_multicast_group nss_nlgre_redir_mcgrp[] = {
-	{.name = NSS_NLGRE_REDIR_MCAST_GRP},
-};
-
-/*
- * nss_nlgre_redir_ops_create_tun()
- * 	Handler for tunnel create
- */
-static int nss_nlgre_redir_ops_create_tun(struct sk_buff *skb, struct genl_info *info)
+int nss_nlgre_redir_destroy_tun(struct net_device *dev)
 {
-	struct nss_nlgre_redir_rule *nl_rule;
-	struct nss_nlcmn *nl_cm;
-	int ret = 0;
+	int ret;
 
-	/*
-	 * Extract the message payload
-	 */
-	nl_cm = nss_nl_get_msg(&nss_nlgre_redir_family, info, NSS_NLGRE_REDIR_CMD_TYPE_CREATE_TUN);
-	if (!nl_cm) {
-		nss_nl_error("Unable to extract create tunnel data\n");
-		ret = -EINVAL;
-		goto fail;
-	}
-
-	/*
-	 * Message validation required before accepting the configuration
-	 */
-	nl_rule = container_of(nl_cm, struct nss_nlgre_redir_rule, cm);
-
-	/*
-	 * Create a gre tunnel
-	 */
-	ret = nss_nlgre_redir_mgr_create_tun(&nl_rule->msg.create);
-	if(ret == -1) {
-		nss_nl_error("Unable to create tunnel\n");
-		ret = -EAGAIN;
-		goto fail;
-	}
-
-	nss_nl_info("Successfully created gretun%d tunnel\n", ret);
-fail:
-	return ret;
-}
-
-/*
- * nss_nlgre_redir_ops_destroy_tun()
- * 	Handler destroy tunnel
- */
-static int nss_nlgre_redir_ops_destroy_tun(struct sk_buff *skb, struct genl_info *info)
-{
-	struct nss_nlgre_redir_rule *nl_rule;
-	struct net_device *dev;
-	struct nss_nlcmn *nl_cm;
-	int ret = 0;
-
-	/*
-	 * Extract the message payload
-	 */
-	nl_cm = nss_nl_get_msg(&nss_nlgre_redir_family, info, NSS_NLGRE_REDIR_CMD_TYPE_DESTROY_TUN);
-	if (!nl_cm) {
-		nss_nl_error("Unable to extract destroy tunnel data\n");
-		ret = -EINVAL;
-		goto fail;
-	}
-
-	/*
-	 * Message validation required before accepting the configuration
-	 */
-	nl_rule = container_of(nl_cm, struct nss_nlgre_redir_rule, cm);
-
-	/*
-	 * Get the dev reference
-	 */
-	dev = dev_get_by_name(&init_net, nl_rule->msg.destroy.netdev);
 	if (!dev) {
-		nss_nl_error("Invalid parameter: %s\n", nl_rule->msg.destroy.netdev);
-		ret = -ENODEV;
-		goto fail;
+		nss_nl_error("Dev is NULL\n");
+		return -EINVAL;
 	}
 
-	/*
-	 * Destroy the tunnel
-	 */
-	ret = nss_nlgre_redir_mgr_destroy_tun(dev);
-	if (ret == -1) {
-		nss_nl_error("Unable to destroy tunnel: %s\n", nl_rule->msg.destroy.netdev);
-		ret = -EAGAIN;
-		dev_put(dev);
-		goto fail;
+	ret = nss_nlgre_redir_cmn_destroy_tun(dev);
+	if (ret < 0) {
+		nss_nl_error("Could not destroy the tunnel\n");
+		return -EINVAL;
 	}
 
-	nss_nl_info("Successfully destroyed gretun = %s tunnel\n", nl_rule->msg.destroy.netdev);
-fail:
-	return ret;
+	nss_nl_info("Successfully destroyed the tunnel\n");
+	return 0;
 }
 
 /*
- * nss_nlgre_redir_ops_map()
- * 	Handler for map command
+ * nss_nlgre_redir_create_tun()
+ * 	Creates a gre_redir tunnel
  */
-static int nss_nlgre_redir_ops_map(struct sk_buff *skb, struct genl_info *info)
+int nss_nlgre_redir_create_tun(struct nss_nlgre_redir_create_tun *create_params)
 {
-	struct nss_nlgre_redir_rule *nl_rule;
-	struct nss_nlcmn *nl_cm;
-	uint32_t vap_nss_if, tun_type;
 	struct net_device *dev;
-	int ret = 0;
 
-	/*
-	 * extract the message payload
-	 */
-	nl_cm = nss_nl_get_msg(&nss_nlgre_redir_family, info, NSS_NLGRE_REDIR_CMD_TYPE_MAP);
-	if (!nl_cm) {
-		nss_nl_error("Unable to extract map interface data\n");
-		ret = -EINVAL;
-		goto fail;
+	if (!create_params) {
+		nss_nl_error("create_params is NULL\n");
+		return -EINVAL;
 	}
 
-	/*
-	 * Message validation required before accepting the configuration
-	 */
-	nl_rule = container_of(nl_cm, struct nss_nlgre_redir_rule, cm);
-
-	/*
-	 * Get the dev reference
-	 */
-	dev = dev_get_by_name(&init_net, nl_rule->msg.map.vap_nss_if);
+	dev = nss_nlgre_redir_cmn_create_tun(create_params->sip, create_params->dip, create_params->iptype);
 	if (!dev) {
-		nss_nl_error("Invalid parameter: vap_nss_if\n");
-		ret = -ENODEV;
-		goto fail;
+		nss_nl_error("Could not create tunnel\n");
+		return -EINVAL;
 	}
 
-	vap_nss_if = nss_cmn_get_interface_number_by_dev(dev);
-	dev_put(dev);
-	tun_type = nss_nlgre_redir_mgr_tunnel_type(nl_rule->msg.map.tun_type);
-
-	/*
-	 * map the interface
-	 */
-	ret = nss_nlgre_redir_mgr_interface_map(vap_nss_if, tun_type, &nl_rule->msg.map);
-	if(!ret) {
-		nss_nl_error("Unable to map nss interface\n");
-		ret = -EAGAIN;
-		goto fail;
-	}
-
-	nss_nl_info("Successfully mapped nss interface.\n");
-fail:
-	return ret;
+	nss_nl_info("Successfully created the tunnel = %s\n", dev->name);
+	return 0;
 }
 
 /*
- * nss_nlgre_redir_ops_unmap()
- * 	Handler for unmap command
+ * nss_nlgre_redir_map_interface()
+ * 	Maps the nss interface to the tunnel ID
  */
-static int nss_nlgre_redir_ops_unmap(struct sk_buff *skb, struct genl_info *info)
+int nss_nlgre_redir_map_interface(struct nss_nlgre_redir_map *map_params)
 {
-	struct nss_nlgre_redir_rule *nl_rule;
-	struct nss_nlcmn *nl_cm;
-	struct net_device *dev;
-	int32_t vap_nss_if;
-	int ret = 0;
+	struct nss_ctx_instance *nss_ctx;
+	uint32_t nexthop_nssif, vap_nss_if;
+	uint8_t tun_type;
+	int ret;
 
-	/*
-	 * extract the message payload
-	 */
-	nl_cm = nss_nl_get_msg(&nss_nlgre_redir_family, info, NSS_NLGRE_REDIR_CMD_TYPE_UNMAP);
-	if (!nl_cm) {
-		nss_nl_error("Unable to extract unmap data\n");
-		ret = -EINVAL;
-		goto fail;
+	if (!map_params) {
+		nss_nl_error("map params is NULL\n");
+		return -EINVAL;
 	}
 
-	/*
-	 * Message validation required before accepting the configuration
-	 */
-	nl_rule = container_of(nl_cm, struct nss_nlgre_redir_rule, cm);
+	nss_ctx = nss_gre_redir_get_context();
+	vap_nss_if = nss_nlgre_redir_cmn_get_dev_ifnum(map_params->vap_nss_if);
 
 	/*
-	 * Get the dev reference
+	 * Get tunnel type from tun_type string
 	 */
-	dev = dev_get_by_name(&init_net, nl_rule->msg.unmap.vap_nss_if);
-	if (!dev) {
-		nss_nl_error("Invalid parameter: dev_name\n");
-		ret = -ENODEV;
-		goto fail;
-	}
-
-	vap_nss_if = nss_cmn_get_interface_number_by_dev(dev);
-	dev_put(dev);
-
-	/*
-	 * Unmap the interface
-	 */
-	ret = nss_nlgre_redir_mgr_interface_unmap(vap_nss_if, &nl_rule->msg.unmap);
-	if(!ret) {
-		nss_nl_error("Unable to unmap nss interface\n");
-		ret = -EAGAIN;
-		goto fail;
-	}
-
-	nss_nl_info("Successfully unmapped the nss interface.\n");
-fail:
-	return ret;
-}
-
-/*
- * nss_nlgre_redir_ops_set_next()
- * 	Handler for set_next command
- */
-static int nss_nlgre_redir_ops_set_next(struct sk_buff *skb, struct genl_info *info)
-{
-	struct nss_nlgre_redir_rule *nl_rule;
-	struct nss_nlcmn *nl_cm;
-	int ret = 0;
-
-	/*
-	 * extract the message payload
-	 */
-	nl_cm = nss_nl_get_msg(&nss_nlgre_redir_family, info, NSS_NLGRE_REDIR_CMD_TYPE_SET_NEXT_HOP);
-	if (!nl_cm) {
-		nss_nl_error("Unable to extract set_next_hop data\n");
-		ret = -EINVAL;
-		goto fail;
-	}
-
-	/*
-	 * Message validation required before accepting the configuration
-	 */
-	nl_rule = container_of(nl_cm, struct nss_nlgre_redir_rule, cm);
-
-	ret = nss_nlgre_redir_mgr_set_next_hop(&nl_rule->msg.snext);
-	if (!ret) {
-		nss_nl_error("Unable to set next hop\n");
-		ret = -EAGAIN;
-		goto fail;
-	}
-
-	nss_nl_info("Successfully set the next hop\n");
-fail:
-	return ret;
-}
-
-/*
- * nss_nlgre_redir_get_ifnum()
- * 	Get the interface number corresponding to netdev
- */
-int nss_nlgre_redir_get_ifnum(struct net_device* dev, enum nss_dynamic_interface_type type)
-{
-	int ifnum;
-
-	/*
-	 * Get the interface number depending upon the dev and type
-	 */
-	ifnum = nss_cmn_get_interface_number_by_dev_and_type(dev, type);
-	if (ifnum < 0) {
-		nss_nl_error("%p: Failed to find interface number (dev:%s, type:%d)\n", dev, dev->name, type);
+	tun_type = nss_nlgre_redir_cmn_get_tun_type(map_params->tun_type);
+	switch(tun_type) {
+	case NSS_NLGRE_REDIR_TUN_TYPE_DTUN:
+	case NSS_NLGRE_REDIR_TUN_TYPE_TUN:
+		nexthop_nssif = vap_nss_if;
+		break;
+	case NSS_NLGRE_REDIR_TUN_TYPE_SPLIT:
+		nexthop_nssif = NSS_ETH_RX_INTERFACE;
+		break;
+	default:
+		nss_nl_error("%p: not a valid tunnel_type\n", nss_ctx);
 		return -1;
 	}
 
-	return ifnum;
-}
-
-/*
- * nss_nlgre_redir_ops
- * 	Operation table called by the generic netlink layer based on the command
- */
-static struct genl_ops nss_nlgre_redir_ops[] = {
-	{.cmd = NSS_NLGRE_REDIR_CMD_TYPE_CREATE_TUN, .doit = nss_nlgre_redir_ops_create_tun,},
-	{.cmd = NSS_NLGRE_REDIR_CMD_TYPE_DESTROY_TUN, .doit = nss_nlgre_redir_ops_destroy_tun,},
-	{.cmd = NSS_NLGRE_REDIR_CMD_TYPE_MAP, .doit = nss_nlgre_redir_ops_map,},
-	{.cmd = NSS_NLGRE_REDIR_CMD_TYPE_UNMAP, .doit = nss_nlgre_redir_ops_unmap,},
-	{.cmd = NSS_NLGRE_REDIR_CMD_TYPE_SET_NEXT_HOP, .doit = nss_nlgre_redir_ops_set_next,},
-};
-
-/*
- * nss_nlgre_redir_init()
- * 	handler init
- */
-bool nss_nlgre_redir_init(void)
-{
-	int err;
-	nss_nl_info_always("Init NSS netlink gre_redir handler\n");
-
 	/*
-	 * register NETLINK ops with the family
+	 * Map the nss interface
 	 */
-	err = genl_register_family_with_ops_groups(&nss_nlgre_redir_family, nss_nlgre_redir_ops, nss_nlgre_redir_mcgrp);
-	if (err) {
-		nss_nl_info_always("Error: %d unable to register gre_redir family\n", err);
-		return false;
+	ret = nss_nlgre_redir_cmn_map_interface(nexthop_nssif, 0, map_params);
+	if (ret == -1) {
+		nss_nl_error("%p: Unable to map nss interface\n", nss_ctx);
+		return -1;
 	}
 
-	return true;
+	nss_nl_info("Successfully mapped the nss interface to tunnel ID\n");
+	return 0;
 }
 
 /*
- * nss_nlgre_redir_exit()
- *	handler exit
+ * nss_nlgre_redir_set_next_hop()
+ * 	Sets the next hop of vap as wifi_offld_inner interface of gre_redir node
  */
-bool nss_nlgre_redir_exit(void)
+int nss_nlgre_redir_set_next_hop(struct nss_nlgre_redir_set_next *set_next_params)
 {
-	int err;
-	nss_nl_info_always("Exit NSS netlink gre_redir handler\n");
+	enum nss_nlgre_redir_cmn_mode_type mode;
+	struct nss_ctx_instance *nss_ctx;
+	struct net_device *next_dev;
+	uint32_t nexthop_ifnum;
+	int ret;
 
-	/*
-	 * unregister the ops family
-	 */
-	err = genl_unregister_family(&nss_nlgre_redir_family);
-	if (err) {
-		nss_nl_info_always("Error: %d unable to unregister gre_redir NETLINK family\n", err);
-		return false;
+	if (!set_next_params) {
+		nss_nl_error("set next params is NULL\n");
+		return -EINVAL;
 	}
 
-	return true;
+	nss_ctx = nss_gre_redir_get_context();
+	next_dev = dev_get_by_name(&init_net, set_next_params->next_dev_name);
+	if (!next_dev) {
+		nss_nl_error("%p: Unable to get the reference to dev %s\n", nss_ctx, set_next_params->next_dev_name);
+		return -1;
+	}
+
+	dev_put(next_dev);
+	mode = nss_nlgre_redir_cmn_mode_str_to_enum(set_next_params->mode);
+	switch(mode) {
+	case NSS_NLGRE_REDIR_CMN_MODE_TYPE_WIFI:
+		/*
+		 * Gets the wifi_offl_inner_ifnum interface number of gretun[index]
+		 */
+		nexthop_ifnum = nss_nlgre_redir_cmn_get_tun_ifnum(NSS_NLGRE_REDIR_CMN_MODE_TYPE_WIFI, next_dev);
+		break;
+	case NSS_NLGRE_REDIR_CMN_MODE_TYPE_SPLIT:
+		nexthop_ifnum = NSS_ETH_RX_INTERFACE;
+		break;
+	default:
+		nss_nl_error("%p: Unknown set next mode\n", nss_ctx);
+		return -1;
+	}
+
+	ret = nss_nlgre_redir_cmn_set_next_hop(nexthop_ifnum, set_next_params);
+	if (ret == -1) {
+		nss_nl_error("%p: Unable to set the next hop\n", nss_ctx);
+		return -1;
+	}
+
+	nss_nl_info("Successfully set the next hop\n");
+	return 0;
 }
+
diff --git a/netlink/nss_nlgre_redir.h b/netlink/nss_nlgre_redir.h
index ac42019..16ab737 100644
--- a/netlink/nss_nlgre_redir.h
+++ b/netlink/nss_nlgre_redir.h
@@ -1,5 +1,5 @@
 /*
- **************************************************************************
+ ***************************************************************************
  * Copyright (c) 2014-2015,2019, 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
@@ -14,37 +14,41 @@
  **************************************************************************
  */
 
-/*
- * nss_nlgre_redir.h
- *	NSS Netlink gre_redir API definitions
- */
-#ifndef __NSS_NLGRE_REDIR_H
-#define __NSS_NLGRE_REDIR_H
+#define NSS_NLGRE_REDIR_MODE_MAX_SZ 16		/**< Max size of mode value */
 
 /*
- * nss_nlgre_redir_get_ifnum()
- * 	Get the interface number corresponding to netdev
+ * nss_nlgre_redir_tun_type
+ * 	Different tunnel types supported in gre_redir
  */
-int nss_nlgre_redir_get_ifnum(struct net_device* dev, enum nss_dynamic_interface_type type);
+enum nss_nlgre_redir_tun_type {
+	NSS_NLGRE_REDIR_TUN_TYPE_UNKNOWN,		/**< Unknown tunnel type */
+	NSS_NLGRE_REDIR_TUN_TYPE_TUN,			/**< Raw mode 802.11 frames traffic*/
+	NSS_NLGRE_REDIR_TUN_TYPE_DTUN,			/**< For 802.3 frames traffic */
+	NSS_NLGRE_REDIR_TUN_TYPE_SPLIT,			/**< For split mode */
+	NSS_NLGRE_REDIR_TUN_TYPE_MAX			/**< Max number of tun type supported */
+};
 
 /*
- * nss_nlgre_redir_init()
- * 	To initialize the gre_redir module
+ * nss_nlgre_redir_map_interface()
+ * 	Interface map message.
  */
-bool nss_nlgre_redir_init(void);
+int nss_nlgre_redir_map_interface(struct nss_nlgre_redir_map *map_params);
 
 /*
- * nss_nlgre_redir_exit()
- * 	Exit the gre_redir module
+ * nss_nlgre_redir_set_next_hop()
+ * 	Sets next hop as gre-redir for wifi.
  */
-bool nss_nlgre_redir_exit(void);
+int nss_nlgre_redir_set_next_hop(struct nss_nlgre_redir_set_next *setnext_params);
 
-#if defined(CONFIG_NSS_NLGRE_REDIR)
-#define NSS_NLGRE_REDIR_INIT nss_nlgre_redir_init
-#define NSS_NLGRE_REDIR_EXIT nss_nlgre_redir_exit
-#else
-#define NSS_NLGRE_REDIR_INIT 0
-#define NSS_NLGRE_REDIR_EXIT 0
-#endif /* !CONFIG_NSS_NLGRE_REDIR */
+/*
+ * nss_nlgre_redir_create_tun()
+ * 	Unregisters and deallocs dynamic interfaces.
+ */
+int nss_nlgre_redir_create_tun(struct nss_nlgre_redir_create_tun *create_params);
 
-#endif /* __NSS_NLGRE_REDIR_H */
+/*
+ * nss_nlgre_redir_destroy_tun()
+ * 	Destroy tunnel in tunnel mode.
+ */
+int nss_nlgre_redir_destroy_tun(struct net_device *dev);
+
diff --git a/netlink/nss_nlgre_redir_cmd.c b/netlink/nss_nlgre_redir_cmd.c
new file mode 100644
index 0000000..3653bea
--- /dev/null
+++ b/netlink/nss_nlgre_redir_cmd.c
@@ -0,0 +1,443 @@
+/*
+ **************************************************************************
+ * Copyright (c) 2015-2016,2018-2019, 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_nlgre_redir.c
+ * 	NSS Netlink gre_redir Handler
+ */
+#include <linux/version.h>
+#include <net/genetlink.h>
+#include <nss_api_if.h>
+#include <nss_nlcmn_if.h>
+#include <nss_nl_if.h>
+#include <nss_nlgre_redir_if.h>
+#include "nss_nl.h"
+#include "nss_nlgre_redir.h"
+#include "nss_nlgre_redir_cmd.h"
+#include "nss_nlgre_redir_cmn.h"
+#include "nss_nlgre_redir_lag.h"
+
+/*
+ * To get lock on deploy_mode
+ */
+static DEFINE_SPINLOCK(lock);
+
+/*
+ * Variable to keep track of mode we are operating
+ */
+static enum nss_nlgre_redir_cmd_deploy_mode deploy_mode;
+
+/*
+ * nss_nlgre_redir_cmd_family
+ * 	Gre_redir family definition
+ */
+struct genl_family nss_nlgre_redir_cmd_family = {
+	.id = GENL_ID_GENERATE,				/* Auto generate ID */
+	.name = NSS_NLGRE_REDIR_FAMILY,			/* family name string */
+	.hdrsize = sizeof(struct nss_nlgre_redir_rule),	/* NSS NETLINK gre_redir rule */
+	.version = NSS_NL_VER,				/* Set it to NSS_NLGRE_REDIR version */
+	.maxattr = NSS_NLGRE_REDIR_CMD_TYPE_MAX,	/* maximum commands supported */
+	.netnsok = true,
+	.pre_doit = NULL,
+	.post_doit = NULL,
+};
+
+/*
+ * nss_nlgre_redir_cmd_get_deploy_mode()
+ * 	Returns deploy_mode value
+ */
+static inline enum nss_nlgre_redir_cmd_deploy_mode nss_nlgre_redir_cmd_get_deploy_mode(void)
+{
+	enum nss_nlgre_redir_cmd_deploy_mode ret_deploy_mode;
+	spin_lock(&lock);
+	ret_deploy_mode = deploy_mode;
+	spin_unlock(&lock);
+	return ret_deploy_mode;
+}
+
+/*
+ * nss_nlgre_redir_cmd_set_deploy_mode()
+ * 	Sets the value of deploy_mode to parameter passed
+ */
+static inline void nss_nlgre_redir_cmd_set_deploy_mode(enum nss_nlgre_redir_cmd_deploy_mode param_deploy_mode)
+{
+	spin_lock(&lock);
+	deploy_mode = param_deploy_mode;
+	spin_unlock(&lock);
+}
+
+/*
+ * nss_nlgre_redir_cmd_ops_tun_create()
+ * 	Handler for tunnel create
+ */
+static int nss_nlgre_redir_cmd_ops_tun_create(struct sk_buff *skb, struct genl_info *info)
+{
+	struct nss_nlgre_redir_rule *nl_rule;
+	struct nss_nlcmn *nl_cm;
+	int ret = 0;
+
+	/*
+	 * Extract the message payload
+	 */
+	nl_cm = nss_nl_get_msg(&nss_nlgre_redir_cmd_family, info, NSS_NLGRE_REDIR_CMD_TYPE_CREATE_TUN);
+	if (!nl_cm) {
+		nss_nl_error("Unable to extract create tunnel data\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Message validation required before accepting the configuration
+	 */
+	nl_rule = container_of(nl_cm, struct nss_nlgre_redir_rule, cm);
+
+	/*
+	 * Create tunnel based on value of lag_enable
+	 */
+	if (!nl_rule->msg.create.lag_enable) {
+		nss_nlgre_redir_cmd_set_deploy_mode(NSS_NLGRE_REDIR_CMD_DEPLOY_MODE_NON_LAG);
+		ret = nss_nlgre_redir_create_tun(&nl_rule->msg.create);
+		if (ret < 0) {
+			nss_nl_error("Unable to create tunnel\n");
+			return -EAGAIN;
+		}
+
+		goto done;
+	}
+
+	/*
+	 * Create a lag tunnel
+	 */
+	nss_nlgre_redir_cmd_set_deploy_mode(NSS_NLGRE_REDIR_CMD_DEPLOY_MODE_LAG);
+	ret = nss_nlgre_redir_lag_create_tun(&nl_rule->msg.create);
+	if (ret < 0) {
+		nss_nl_error("Unable to create lag tunnel\n");
+		return -EAGAIN;
+	}
+done:
+	nss_nl_info("Successfully created tunnel\n");
+	return 0;
+}
+
+/*
+ * nss_nlgre_redir_cmd_ops_destroy_tun()
+ * 	Handler to destroy tunnel
+ */
+static int nss_nlgre_redir_cmd_ops_tun_destroy(struct sk_buff *skb, struct genl_info *info)
+{
+	enum nss_nlgre_redir_cmd_deploy_mode deploy_mode;
+	struct nss_nlgre_redir_rule *nl_rule;
+	struct nss_nlcmn *nl_cm;
+	struct net_device *dev;
+	int ret = 0;
+
+	/*
+	 * Extract the message payload
+	 */
+	nl_cm = nss_nl_get_msg(&nss_nlgre_redir_cmd_family, info, NSS_NLGRE_REDIR_CMD_TYPE_DESTROY_TUN);
+	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_nlgre_redir_rule, cm);
+
+	/*
+	 * Get the dev reference
+	 */
+	dev = dev_get_by_name(&init_net, nl_rule->msg.destroy.netdev);
+	if (!dev) {
+		nss_nl_error("Invalid parameters: %s\n", nl_rule->msg.destroy.netdev);
+		return -ENODEV;
+	}
+
+	dev_put(dev);
+
+	/*
+	 * Destroy the non-lag tunnel
+	 */
+	deploy_mode = nss_nlgre_redir_cmd_get_deploy_mode();
+	if (deploy_mode != NSS_NLGRE_REDIR_CMD_DEPLOY_MODE_LAG) {
+		ret = nss_nlgre_redir_destroy_tun(dev);
+		if (ret < 0) {
+			nss_nl_error("Unable to destroy tunnel: %s\n", nl_rule->msg.destroy.netdev);
+			dev_put(dev);
+			return -EAGAIN;
+		}
+
+		goto done;
+	}
+
+	/*
+	 * Destroy the lag tunnel
+	 */
+	ret = nss_nlgre_redir_lag_destroy_tun(dev);
+	if (ret < 0) {
+		nss_nl_error("Unable to destroy tunnel: %s\n", nl_rule->msg.destroy.netdev);
+		dev_put(dev);
+		return -EAGAIN;
+	}
+
+done:
+	nss_nl_info("Successfully destroyed gretun = %s tunnel\n", nl_rule->msg.destroy.netdev);
+	return 0;
+}
+
+/*
+ * nss_nlgre_redir_cmd_ops_map()
+ * 	Handler for map command
+ */
+static int nss_nlgre_redir_cmd_ops_map(struct sk_buff *skb, struct genl_info *info)
+{
+	enum nss_nlgre_redir_cmd_deploy_mode deploy_mode;
+	struct nss_nlgre_redir_rule *nl_rule;
+	struct nss_nlcmn *nl_cm;
+	int ret = 0;
+
+	/*
+	 * extract the message payload
+	 */
+	nl_cm = nss_nl_get_msg(&nss_nlgre_redir_cmd_family, info, NSS_NLGRE_REDIR_CMD_TYPE_MAP);
+	if (!nl_cm) {
+		nss_nl_error("Unable to extract map interface data\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Message validation required before accepting the configuration
+	 */
+	nl_rule = container_of(nl_cm, struct nss_nlgre_redir_rule, cm);
+
+	/*
+	 * Map the interface
+	 */
+	deploy_mode = nss_nlgre_redir_cmd_get_deploy_mode();
+	if (deploy_mode != NSS_NLGRE_REDIR_CMD_DEPLOY_MODE_LAG) {
+		ret = nss_nlgre_redir_map_interface(&nl_rule->msg.map);
+		if(ret < 0) {
+			nss_nl_error("Unable to map nss interface\n");
+			return -EAGAIN;
+		}
+
+		goto done;
+	}
+
+	ret = nss_nlgre_redir_lag_map_interface(&nl_rule->msg.map);
+	if (ret < 0) {
+		nss_nl_error("Unable to map nss interface\n");
+		return -EAGAIN;
+	}
+done:
+	nss_nl_info("Successfully mapped nss interface.\n");
+	return 0;
+}
+
+/*
+ * nss_nlgre_redir_cmd_ops_unmap()
+ * 	Handler for unmap command
+ */
+static int nss_nlgre_redir_cmd_ops_unmap(struct sk_buff *skb, struct genl_info *info)
+{
+	struct nss_nlgre_redir_rule *nl_rule;
+	struct nss_nlcmn *nl_cm;
+	int ret = 0;
+
+	/*
+	 * extract the message payload
+	 */
+	nl_cm = nss_nl_get_msg(&nss_nlgre_redir_cmd_family, info, NSS_NLGRE_REDIR_CMD_TYPE_UNMAP);
+	if (!nl_cm) {
+		nss_nl_error("Unable to extract unmap data\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Message validation required before accepting the configuration
+	 */
+	nl_rule = container_of(nl_cm, struct nss_nlgre_redir_rule, cm);
+
+	/*
+	 * Unmap the interface
+	 */
+	ret = nss_nlgre_redir_cmn_unmap_interface(&nl_rule->msg.unmap);
+	if(ret < 0) {
+		nss_nl_error("Unable to unmap nss interface\n");
+		return -EAGAIN;
+	}
+
+	nss_nl_info("Successfully unmapped the nss interface.\n");
+	return 0;
+}
+
+/*
+ * nss_nlgre_redir_cmd_set_next()
+ * 	Handler for set_next command
+ */
+static int nss_nlgre_redir_cmd_ops_set_next(struct sk_buff *skb, struct genl_info *info)
+{
+	enum nss_nlgre_redir_cmd_deploy_mode deploy_mode;
+	struct nss_nlgre_redir_rule *nl_rule;
+	struct nss_nlcmn *nl_cm;
+	int ret = 0;
+
+	/*
+	 * extract the message payload
+	 */
+	nl_cm = nss_nl_get_msg(&nss_nlgre_redir_cmd_family, info, NSS_NLGRE_REDIR_CMD_TYPE_SET_NEXT_HOP);
+	if (!nl_cm) {
+		nss_nl_error("Unable to extract set_next_hop data\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Message validation required before accepting the configuration
+	 */
+	nl_rule = container_of(nl_cm, struct nss_nlgre_redir_rule, cm);
+
+	/*
+	 * Set the next hop of ath0 as wifi_offld_inner of gre_redir node
+	 */
+	deploy_mode = nss_nlgre_redir_cmd_get_deploy_mode();
+	if (deploy_mode != NSS_NLGRE_REDIR_CMD_DEPLOY_MODE_LAG) {
+		ret = nss_nlgre_redir_set_next_hop(&nl_rule->msg.snext);
+		if (ret < 0) {
+			nss_nl_error("Unable to set next hop\n");
+			return -EAGAIN;
+		}
+
+		goto done;
+	}
+
+	/*
+	 * Set the next hop of ath0 as lag US node's inner interface
+	 */
+	ret = nss_nlgre_redir_lag_set_next_hop(&nl_rule->msg.snext);
+	if (ret < 0) {
+		nss_nl_error("Unable to set the next hop\n");
+		return -EAGAIN;
+	}
+
+done:
+	nss_nl_info("Successfully set the next hop\n");
+	return 0;
+}
+
+/*
+ * nss_nlgre_redir_cmd_add_hash()
+ * 	Handler for adding hash a value
+ */
+static int nss_nlgre_redir_cmd_ops_add_hash(struct sk_buff *skb, struct genl_info *info)
+{
+	struct nss_nlgre_redir_rule *nl_rule;
+	struct nss_nlcmn *nl_cm;
+	int ret;
+
+	/*
+	 * extract the message payload
+	 */
+	nl_cm = nss_nl_get_msg(&nss_nlgre_redir_cmd_family, info, NSS_NLGRE_REDIR_CMD_TYPE_ADD_HASH);
+	if (!nl_cm) {
+		nss_nl_error("Unable to add a new hash value.\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Message validation required before accepting the configuration
+	 */
+	nl_rule = container_of(nl_cm, struct nss_nlgre_redir_rule, cm);
+	ret = nss_nlgre_redir_lag_add_hash(&nl_rule->msg.hash_ops);
+	if(ret < 0) {
+		nss_nl_error("Unable to add hash value.\n");
+		return -EINVAL;
+	}
+
+	nss_nl_info("Successfully added a hash value.\n");
+	return 0;
+}
+
+/*
+ * nss_nlgre_redir_cmd_del_hash()
+ * 	Handler for deleting a hash value
+ */
+static int nss_nlgre_redir_cmd_ops_del_hash(struct sk_buff *skb, struct genl_info *info)
+{
+	struct nss_nlgre_redir_rule *nl_rule;
+	struct nss_nlcmn *nl_cm;
+	int ret;
+
+	/*
+	 * extract the message payload
+	 */
+	nl_cm = nss_nl_get_msg(&nss_nlgre_redir_cmd_family, info, NSS_NLGRE_REDIR_CMD_TYPE_DEL_HASH);
+	if (!nl_cm) {
+		nss_nl_error("Unable to delete the hash value.\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Message validation required before accepting the configuration
+	 */
+	nl_rule = container_of(nl_cm, struct nss_nlgre_redir_rule, cm);
+
+	/*
+	 * Delete hash value corresponding to smac and dmac
+	 */
+	ret = nss_nlgre_redir_lag_del_hash(&nl_rule->msg.hash_ops);
+	if(ret < 0) {
+		nss_nl_error("Unable to delete hash value.\n");
+		return -EINVAL;
+	}
+
+	nss_nl_info("Successfully deleted the hash entry.\n");
+	return 0;
+}
+
+/*
+ * nss_nlgre_redir_cmd_ops
+ * 	Operation table called by the generic netlink layer based on the command
+ */
+struct genl_ops nss_nlgre_redir_cmd_ops[] = {
+	{.cmd = NSS_NLGRE_REDIR_CMD_TYPE_CREATE_TUN, .doit = nss_nlgre_redir_cmd_ops_tun_create,},
+	{.cmd = NSS_NLGRE_REDIR_CMD_TYPE_DESTROY_TUN, .doit = nss_nlgre_redir_cmd_ops_tun_destroy,},
+	{.cmd = NSS_NLGRE_REDIR_CMD_TYPE_MAP, .doit = nss_nlgre_redir_cmd_ops_map,},
+	{.cmd = NSS_NLGRE_REDIR_CMD_TYPE_UNMAP, .doit = nss_nlgre_redir_cmd_ops_unmap,},
+	{.cmd = NSS_NLGRE_REDIR_CMD_TYPE_SET_NEXT_HOP, .doit = nss_nlgre_redir_cmd_ops_set_next,},
+	{.cmd = NSS_NLGRE_REDIR_CMD_TYPE_ADD_HASH, .doit = nss_nlgre_redir_cmd_ops_add_hash,},
+	{.cmd = NSS_NLGRE_REDIR_CMD_TYPE_DEL_HASH, .doit = nss_nlgre_redir_cmd_ops_del_hash,},
+};
+
+/*
+ * nss_nlgre_redir_cmd_get_ifnum()
+ * 	Get the interface number corresponding to netdev
+ */
+int nss_nlgre_redir_cmd_get_ifnum(struct net_device* dev, enum nss_dynamic_interface_type type)
+{
+	int ifnum;
+
+	/*
+	 * Get the interface number depending upon the dev and type
+	 */
+	ifnum = nss_cmn_get_interface_number_by_dev_and_type(dev, type);
+	if (ifnum < 0) {
+		nss_nl_error("%p: Failed to find interface number (dev:%s, type:%d)\n", dev, dev->name, type);
+		return -1;
+	}
+
+	return ifnum;
+}
+
diff --git a/netlink/nss_nlgre_redir_cmd.h b/netlink/nss_nlgre_redir_cmd.h
new file mode 100644
index 0000000..858f82a
--- /dev/null
+++ b/netlink/nss_nlgre_redir_cmd.h
@@ -0,0 +1,51 @@
+/*
+ **************************************************************************
+ * Copyright (c) 2014-2015,2019, 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_nlgre_redir_cmd.h
+ *	NSS Netlink gre_redir API definitions
+ */
+#ifndef __NSS_NLGRE_REDIR_CMD_H
+#define __NSS_NLGRE_REDIR_CMD_H
+#define NSS_NLGRE_REDIR_CMD_MAX 7
+/*
+ * nss_nlgre_redir_cmd_deploy_mode
+ * 	Gre_redir deployment mode types
+ */
+enum nss_nlgre_redir_cmd_deploy_mode {
+	NSS_NLGRE_REDIR_CMD_DEPLOY_MODE_UNKNOWN,			/**< Invalid mode */
+	NSS_NLGRE_REDIR_CMD_DEPLOY_MODE_NON_LAG,			/**< Basic tunnel mode */
+	NSS_NLGRE_REDIR_CMD_DEPLOY_MODE_LAG,				/**< Lag mode */
+	NSS_NLGRE_REDIR_CMD_DEPLOY_MODE_MAX				/**< Maximum deploy mode */
+};
+
+/*
+ * To keep track of family operations
+ */
+extern struct genl_family nss_nlgre_redir_cmd_family;
+
+/*
+ * Gre_redir generic netlink operations
+ */
+extern struct genl_ops nss_nlgre_redir_cmd_ops[NSS_NLGRE_REDIR_CMD_MAX];
+
+/*
+ * nss_nlgre_redir_cmd_get_ifnum()
+ * 	Get the interface number corresponding to netdev
+ */
+int nss_nlgre_redir_cmd_get_ifnum(struct net_device* dev, enum nss_dynamic_interface_type type);
+
+#endif /* __NSS_NLGRE_REDIR_CMD_H */
diff --git a/netlink/nss_nlgre_redir_cmn.c b/netlink/nss_nlgre_redir_cmn.c
new file mode 100644
index 0000000..f927a9d
--- /dev/null
+++ b/netlink/nss_nlgre_redir_cmn.c
@@ -0,0 +1,902 @@
+/*
+ ***************************************************************************
+ * Copyright (c) 2015-2016,2018-2019, The Linux Foundation. All rights reserved.
+ * Permission to use, copy, modify, and/or distribute this software for
+ * any purpose with or without fee is hereby granted, provided that the
+ * above copyright notice and this permission notice appear in all copies.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ ***************************************************************************
+ */
+
+#include <linux/version.h>
+#include <linux/etherdevice.h>
+#include <linux/if_ether.h>
+#include <net/genetlink.h>
+#include <nss_api_if.h>
+#include <nss_nl_if.h>
+#include "nss_nlcmn_if.h"
+#include "nss_nl.h"
+#include "nss_nlgre_redir_if.h"
+#include "nss_nlgre_redir_cmn.h"
+
+static struct nss_nlgre_redir_cmn_tun_data tun_data[NSS_NLGRE_REDIR_CMN_MAX_TUNNELS];
+static const struct net_device_ops gre_redir_netdev_ops;
+static DEFINE_SPINLOCK(lock);
+
+/*
+ * nss_nlgre_redir_cmn_get_tun_data()
+ * 	Returns the tun_data after checking for lock
+ */
+static struct nss_nlgre_redir_cmn_tun_data nss_nlgre_redir_cmn_get_tun_data(struct net_device *dev)
+{
+	struct nss_nlgre_redir_cmn_tun_data dummy_tun_data = {0};
+	int index;
+
+	spin_lock(&lock);
+	for (index = 0; index < NSS_NLGRE_REDIR_CMN_MAX_TUNNELS; index++) {
+		if (dev != tun_data[index].dev) {
+			continue;
+		}
+
+		spin_unlock(&lock);
+		return tun_data[index];
+	}
+
+	spin_unlock(&lock);
+	return dummy_tun_data;
+}
+
+/*
+ * nss_nlgre_redir_cmn_get_next_free_tun()
+ * 	Returns the next free tunnel available
+ */
+static int nss_nlgre_redir_cmn_get_next_free_tun(void)
+{
+	int index;
+
+	spin_lock(&lock);
+	for (index = 0; index < NSS_NLGRE_REDIR_CMN_MAX_TUNNELS ; index++) {
+		if (!tun_data[index].enable) {
+			spin_unlock(&lock);
+			return index;
+		}
+	}
+
+	spin_unlock(&lock);
+	nss_nl_error("Max tunnel count exceeded: %d\n", index);
+	return -1;
+}
+
+/*
+ * nss_nlgre_redir_cmn_set_tun_data()
+ * 	Set the tun_data value to value passed
+ */
+static bool nss_nlgre_redir_cmn_set_tun_data(struct nss_nlgre_redir_cmn_tun_data *data, int index)
+{
+	if (!data) {
+		nss_nl_error("data is NULL\n");
+		return false;
+	}
+
+	spin_lock(&lock);
+	tun_data[index] = *data;
+	spin_unlock(&lock);
+	return true;
+}
+
+/*
+ * nss_nlgre_redir_cmn_init_tun_data()
+ * 	Initializes the tun_data
+ */
+static void nss_nlgre_redir_cmn_init_tun_data(struct nss_nlgre_redir_cmn_tun_data *tun_data)
+{
+	tun_data->dev = NULL;
+	tun_data->enable = false;
+	tun_data->host_inner_ifnum = -1;
+	tun_data->wifi_offl_inner_ifnum = -1;
+	tun_data->sjack_inner_ifnum = -1;
+	tun_data->outer_ifnum = -1;
+}
+
+/*
+ * nss_nlgre_redir_cmn_deinit_tun_data()
+ *	Deinitialize private data for the given index.
+ */
+static bool nss_nlgre_redir_cmn_deinit_tun_data(struct nss_nlgre_redir_cmn_tun_data *tun_data, int index)
+{
+	struct nss_ctx_instance *nss_ctx;
+
+	nss_ctx = nss_gre_redir_get_context();
+	tun_data->dev = NULL;
+	tun_data->enable = false;
+	tun_data->host_inner_ifnum = -1;
+	tun_data->wifi_offl_inner_ifnum = -1;
+	tun_data->sjack_inner_ifnum = -1;
+	tun_data->outer_ifnum = -1;
+
+	if (!nss_nlgre_redir_cmn_set_tun_data(tun_data, index)) {
+		nss_nl_error("%p: Unable to set tun_data\n", nss_ctx);
+		return false;
+	}
+
+	return true;
+}
+
+/*
+ * nss_nlgre_redir_cmn_host_data_cb()
+ *	Data callback for host offload inner node.
+ */
+static void nss_nlgre_redir_cmn_host_data_cb(struct net_device *netdev, struct sk_buff *skb, struct napi_struct *napi)
+{
+	struct nss_ctx_instance *nss_ctx = nss_gre_redir_get_context();
+	struct nss_gre_redir_decap_per_pkt_metadata *meta_data_decap = NULL;
+
+	if (!skb) {
+		nss_nl_trace("%p: SKB is NULL\n", nss_ctx);
+		return;
+	}
+
+	meta_data_decap = (struct nss_gre_redir_decap_per_pkt_metadata *)(skb->data - NSS_GRE_REDIR_PER_PACKET_METADATA_OFFSET);
+	skb->protocol = eth_type_trans(skb, netdev);
+	netif_receive_skb(skb);
+}
+
+/*
+ * nss_nlgre_redir_cmn_wifi_offl_data_cb()
+ *	Data callback for wifi offload inner node.
+ */
+static void nss_nlgre_redir_cmn_wifi_offl_data_cb(struct net_device *netdev, struct sk_buff *skb, struct napi_struct *napi)
+{
+	nss_nl_trace("%p: Exception packet on wifi offld inner:\n", skb);
+	nss_nlgre_redir_cmn_print_skb(skb);
+	dev_kfree_skb(skb);
+}
+
+/*
+ * nss_nlgre_redir_cmn_sjack_data_cb
+ *	Data callback for sjack inner node.
+ */
+static void nss_nlgre_redir_cmn_sjack_data_cb(struct net_device *netdev, struct sk_buff *skb, struct napi_struct *napi)
+{
+	nss_nl_trace("%p: Exception packet on sjack inner node:\n", skb);
+	nss_nlgre_redir_cmn_print_skb(skb);
+	dev_kfree_skb(skb);
+}
+
+/*
+ * nss_nlgre_redir_cmn_outer_data_cb()
+ *	Data callback for outer node.
+ */
+static void nss_nlgre_redir_cmn_outer_data_cb(struct net_device *netdev, struct sk_buff *skb, struct napi_struct *napi)
+{
+	nss_nl_trace("%p: Exception packet on outer node:\n", skb);
+	nss_nlgre_redir_cmn_print_skb(skb);
+	dev_kfree_skb(skb);
+}
+
+/*
+ * nss_nlgre_redir_cmn_map_unmap_msg_cb()
+ *	HLOS->NSS message completion callback.
+ */
+static void nss_nlgre_redir_cmn_map_unmap_msg_cb(void *app_data, struct nss_cmn_msg *cmnmsg)
+{
+	struct nss_ctx_instance *nss_ctx = nss_gre_redir_get_context();
+	nss_nl_info("%p: callback gre_redir tunnel msg from NSS\n", nss_ctx);
+}
+
+/*
+ * nss_nlgre_redir_cmn_interface_alloc_and_register()
+ * 	Allocates nodes and registers callbacks
+ */
+static int nss_nlgre_redir_cmn_interface_alloc_and_register(struct nss_nlgre_redir_cmn_tun_data *tun_data, struct net_device *dev)
+{
+	struct nss_ctx_instance *nss_ctx = nss_gre_redir_get_context();
+
+	tun_data->host_inner_ifnum = nss_gre_redir_alloc_and_register_node(dev,
+			nss_nlgre_redir_cmn_host_data_cb,
+			NULL, NSS_DYNAMIC_INTERFACE_TYPE_GRE_REDIR_WIFI_HOST_INNER, dev);
+	nss_nl_info("%p: host_inner = %d\n", nss_ctx, tun_data->host_inner_ifnum);
+	if (tun_data->host_inner_ifnum == -1) {
+		nss_nl_error("%p: Unable to allocate and register wifi host inner interface\n", nss_ctx);
+		return -1;
+	}
+
+	tun_data->wifi_offl_inner_ifnum = nss_gre_redir_alloc_and_register_node(dev,
+			nss_nlgre_redir_cmn_wifi_offl_data_cb,
+			NULL, NSS_DYNAMIC_INTERFACE_TYPE_GRE_REDIR_WIFI_OFFL_INNER, dev);
+	nss_nl_info("%p: wifi_inner = %d\n", nss_ctx, tun_data->wifi_offl_inner_ifnum);
+	if (tun_data->wifi_offl_inner_ifnum == -1) {
+		nss_nl_error("%p: Unable to allocate and register wifi offload inner interface\n", nss_ctx);
+		return -1;
+	}
+
+	tun_data->sjack_inner_ifnum = nss_gre_redir_alloc_and_register_node(dev,
+			nss_nlgre_redir_cmn_sjack_data_cb,
+			NULL, NSS_DYNAMIC_INTERFACE_TYPE_GRE_REDIR_SJACK_INNER, dev);
+	nss_nl_info("%p: sjack_inner = %d\n", nss_ctx, tun_data->sjack_inner_ifnum);
+	if (tun_data->sjack_inner_ifnum == -1) {
+		nss_nl_error("%p: Unable to allocate and register sjack inner interface\n", nss_ctx);
+		return -1;
+	}
+
+	tun_data->outer_ifnum = nss_gre_redir_alloc_and_register_node(dev,
+			nss_nlgre_redir_cmn_outer_data_cb,
+			NULL, NSS_DYNAMIC_INTERFACE_TYPE_GRE_REDIR_OUTER, dev);
+	nss_nl_info("%p: outer = %d\n", nss_ctx, tun_data->outer_ifnum);
+	if (tun_data->outer_ifnum == -1) {
+		nss_nl_error("%p: Unable to allocate and register outer interface\n", nss_ctx);
+		return -1;
+	}
+
+	return 0;
+
+}
+
+/*
+ * nss_nlgre_redir_cmn_open_interface()
+ *	Used when the interface is opened for use.
+ */
+static int nss_nlgre_redir_cmn_open_interface(struct net_device *dev)
+{
+	struct nss_gre_redir_cmn_ndev_priv *priv;
+	priv = netdev_priv(dev);
+	priv->gre_seq = 0;
+	netif_start_queue(dev);
+	netif_carrier_on(dev);
+	return 0;
+}
+
+/*
+ * nss_nlgre_redir_cmn_close_interace()
+ *	Used when the interface is closed.
+ */
+static int nss_nlgre_redir_cmn_close_interface(struct net_device *dev)
+{
+	netif_stop_queue (dev);
+	netif_carrier_off(dev);
+	return 0;
+}
+
+/*
+ * nss_nlgre_redir_cmn_xmit_data()
+ *	Used when the interface is used for transmit data.
+ */
+static netdev_tx_t nss_nlgre_redir_cmn_xmit_data(struct sk_buff *skb, struct net_device *dev)
+{
+	struct nss_gre_redir_encap_per_pkt_metadata *meta_data_encap = NULL;
+	struct nss_gre_redir_cmn_ndev_priv *priv;
+	uint32_t ifnum, ret = 0;
+	struct nss_ctx_instance *nss_ctx;
+
+	nss_ctx = nss_gre_redir_get_context();
+	priv = netdev_priv(dev);
+	ifnum = nss_cmn_get_interface_number_by_dev_and_type(dev, NSS_DYNAMIC_INTERFACE_TYPE_GRE_REDIR_WIFI_HOST_INNER);
+
+	/*
+	 * Initializing the start of skb with offset of metadata
+	 */
+	*(skb->head) = NSS_GRE_REDIR_PER_PACKET_METADATA_OFFSET;
+
+	/*
+	 * Configuring gre_redir meta data.
+	 */
+	meta_data_encap = (struct nss_gre_redir_encap_per_pkt_metadata *)(skb->data - NSS_GRE_REDIR_PER_PACKET_METADATA_OFFSET);
+	memset(meta_data_encap, 0, sizeof(struct nss_gre_redir_encap_per_pkt_metadata));
+	meta_data_encap->gre_flags = 0;
+	meta_data_encap->gre_prio = 0;
+	meta_data_encap->gre_seq = ++priv->gre_seq;
+	meta_data_encap->gre_tunnel_id = 10;
+	meta_data_encap->ip_dscp = 0;
+	meta_data_encap->ip_df_override = 0;
+	meta_data_encap->ipsec_pattern = 0;
+
+	nss_ctx = nss_gre_redir_get_context();
+	ret = nss_gre_redir_tx_buf(nss_ctx, skb, ifnum);
+	if (ret != NSS_TX_SUCCESS) {
+		nss_nl_error("%p: Transmit failed and returned with %d\n", nss_ctx, ret);
+		dev_kfree_skb_any(skb);
+	}
+
+	return ret;
+}
+
+/*
+ * nss_nlgre_redir_cmn_stats64_get()
+ *	Used to get link statistics.
+ */
+static struct rtnl_link_stats64 *nss_nlgre_redir_cmn_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
+{
+	struct nss_gre_redir_tunnel_stats get_stats;
+	bool found = false;
+	int i;
+
+	for (i = 0; i < NSS_GRE_REDIR_MAX_INTERFACES; i++) {
+		if (!nss_gre_redir_get_stats(i, &get_stats)) {
+			continue;
+		}
+
+		if (get_stats.dev != dev) {
+			continue;
+		}
+
+		found = true;
+		break;
+	}
+
+	if (found == false)
+		return NULL;
+
+	stats->tx_bytes   = get_stats.node_stats.tx_bytes;
+	stats->tx_packets = get_stats.node_stats.tx_packets;
+	stats->rx_bytes   = get_stats.node_stats.rx_bytes;
+	stats->rx_packets = get_stats.node_stats.rx_packets;
+	for (i = 0;i < ARRAY_SIZE(get_stats.node_stats.rx_dropped); i++) {
+		stats->rx_dropped += get_stats.node_stats.rx_dropped[i];
+	}
+
+	stats->tx_dropped = get_stats.tx_dropped;
+
+	return stats;
+}
+
+/*
+ * nss_nlgre_redir_cmn_set_mac_address()
+ * 	Sets the mac address of netdev
+ */
+static int nss_nlgre_redir_cmn_set_mac_address(struct net_device *dev, void *p)
+{
+	struct sockaddr *addr = (struct sockaddr *)p;
+
+	if (!is_valid_ether_addr(addr->sa_data)) {
+		nss_nl_error("%pM: MAC address validation failed\n", addr->sa_data);
+		return -EINVAL;
+	}
+
+	memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
+	return 0;
+}
+
+/*
+ * nss_nlgre_redir_cmn_netdev_destructor()
+ * 	Unregisters and free the net device
+ */
+static void nss_nlgre_redir_cmn_netdev_destructor(struct net_device *dev)
+{
+	nss_nl_info("Gre_redir tunnel device freed %s\n", dev->name);
+	free_netdev(dev);
+}
+
+/*
+ * nss_nlgre_redir_cmn_dev_setup()
+ * 	To setup the netdevice
+ */
+static void nss_nlgre_redir_cmn_dev_setup(struct net_device *dev)
+{
+	ether_setup(dev);
+	dev->needed_headroom = NSS_NLGRE_REDIR_CMN_NEEDED_HEADROOM;
+	dev->netdev_ops = &gre_redir_netdev_ops;
+	dev->destructor = nss_nlgre_redir_cmn_netdev_destructor;
+	eth_hw_addr_random(dev);
+}
+
+/*
+ * net_device_ops
+ *	Netdevice operations.
+ */
+static const struct net_device_ops gre_redir_netdev_ops = {
+	.ndo_open = nss_nlgre_redir_cmn_open_interface,
+	.ndo_stop = nss_nlgre_redir_cmn_close_interface,
+	.ndo_start_xmit = nss_nlgre_redir_cmn_xmit_data,
+	.ndo_get_stats64 = nss_nlgre_redir_cmn_get_stats64,
+	.ndo_set_mac_address = nss_nlgre_redir_cmn_set_mac_address,
+};
+
+/*
+ * nss_nlgre_redir_cmn_mode_str_to_enum()
+ * 	Returns the type of mode
+ */
+enum nss_nlgre_redir_cmn_mode_type nss_nlgre_redir_cmn_mode_str_to_enum(char *mode)
+{
+	if (!mode)
+		return NSS_NLGRE_REDIR_CMN_MODE_TYPE_UNKNOWN;
+	if (!strncmp(mode, "wifi", NSS_NLGRE_REDIR_MODE_MAX_SZ))
+		return NSS_NLGRE_REDIR_CMN_MODE_TYPE_WIFI;
+	if (!strncmp(mode, "split", NSS_NLGRE_REDIR_MODE_MAX_SZ))
+		return NSS_NLGRE_REDIR_CMN_MODE_TYPE_SPLIT;
+
+	return NSS_NLGRE_REDIR_CMN_MODE_TYPE_UNKNOWN;
+}
+
+/*
+ * nss_nlgre_redir_cmn_get_tun_ifnum()
+ * 	Returns the interface number of the net device
+ */
+int32_t nss_nlgre_redir_cmn_get_tun_ifnum(enum nss_nlgre_redir_cmn_mode_type type, struct net_device *dev)
+{
+	struct nss_nlgre_redir_cmn_tun_data tun_data;
+
+	if (!dev) {
+		nss_nl_error("net_dev is NULL\n");
+		return -1;
+	}
+
+	tun_data = nss_nlgre_redir_cmn_get_tun_data(dev);
+	if (!tun_data.dev) {
+		nss_nl_error("Invalid tun_data: %p\n", tun_data.dev);
+		return -1;
+	}
+
+	switch(type) {
+	case NSS_NLGRE_REDIR_CMN_MODE_TYPE_WIFI:
+		return tun_data.wifi_offl_inner_ifnum;
+	case NSS_NLGRE_REDIR_CMN_MODE_TYPE_SPLIT:
+		return NSS_ETH_RX_INTERFACE;
+	default:
+		nss_nl_error("Wrong mode type: %d\n", type);
+		return -1;
+	}
+
+	return -1;
+}
+
+/*
+ * nss_nlgre_redir_cmn_get_tun_type()
+ * 	Returns the type of tunnel we'll operate in
+ */
+enum nss_nlgre_redir_cmn_tun_type nss_nlgre_redir_cmn_get_tun_type(char *tun_type)
+{
+	if (!tun_type)
+		return NSS_NLGRE_REDIR_CMN_TUN_TYPE_UNKNOWN;
+	if (!strncmp(tun_type, "tun", NSS_NLGRE_REDIR_CMN_TUN_TYPE_MAX_SZ))
+		return NSS_NLGRE_REDIR_CMN_TUN_TYPE_TUN;
+	if (!strncmp(tun_type, "dtun", NSS_NLGRE_REDIR_CMN_TUN_TYPE_MAX_SZ))
+		return NSS_NLGRE_REDIR_CMN_TUN_TYPE_DTUN;
+	if (!strncmp(tun_type, "split", NSS_NLGRE_REDIR_CMN_TUN_TYPE_MAX_SZ))
+		return NSS_NLGRE_REDIR_CMN_TUN_TYPE_SPLIT;
+
+	return NSS_NLGRE_REDIR_CMN_TUN_TYPE_UNKNOWN;
+}
+
+/*
+ * nss_nlgre_redir_cmn_get_tun_data_index()
+ *	Returns index in array of private data.
+ */
+int nss_nlgre_redir_cmn_get_tun_data_index(struct net_device *dev)
+{
+	struct nss_ctx_instance *nss_ctx;
+	uint32_t iter;
+
+	nss_ctx = nss_gre_redir_get_context();
+	if (!dev) {
+		nss_nl_error("%p: Dev is NULL\n", nss_ctx);
+		return -1;
+	}
+
+	spin_lock(&lock);
+	for (iter = 0; iter < NSS_NLGRE_REDIR_CMN_MAX_TUNNELS; iter++) {
+		if (tun_data[iter].dev != dev) {
+			continue;
+		}
+
+		spin_unlock(&lock);
+		return iter;
+	}
+
+	spin_unlock(&lock);
+	return -1;
+}
+
+/*
+ * nss_nlgre_redir_cmn_print_skb()
+ * 	Prints the skb data
+ */
+void nss_nlgre_redir_cmn_print_skb(struct sk_buff *skb)
+{
+	int length;
+	int iter;
+
+	/*
+	 * Check if length is less than 16 bytes
+	 * Else bring down to minimum multiple of 16 bytes.
+	 */
+	if (skb->len < 16) {
+		nss_nl_trace("%p: Skb too small to print min size: 16 bytes\n", skb);
+		return;
+	}
+
+	length = (skb->len / 16);
+	if (length > NSS_NLGRE_REDIR_CMN_MAX_SKB_PRINT_LEN)
+		length = NSS_NLGRE_REDIR_CMN_MAX_SKB_PRINT_LEN;
+
+	/*
+	 * Print first 48 bytes of sk_buff
+	 */
+	for (iter = 0; iter < length; iter++) {
+		nss_nl_trace("%04xx: %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x\n", iter,
+			skb->data[iter * 8], skb->data[iter * 8 + 1], skb->data[iter * 8 + 2], skb->data[iter * 8 + 3],
+			skb->data[iter * 8 + 4], skb->data[iter * 8 + 5], skb->data[iter * 8 + 6], skb->data[iter * 8 + 7],
+			skb->data[iter * 8 + 8], skb->data[iter * 8 + 9], skb->data[iter * 8 + 10], skb->data[iter * 8 + 11],
+			skb->data[iter * 8 + 10], skb->data[iter * 8 + 13], skb->data[iter * 8 + 14], skb->data[iter * 8 + 15]);
+	}
+}
+
+/*
+ * nss_gre_redir_unregister_and_deallocate()
+ *	Unregisters and deallocates corresponding dev and node.
+ */
+bool nss_nlgre_redir_cmn_unregister_and_deallocate(struct net_device *dev, uint32_t type)
+{
+	nss_tx_status_t status;
+	int ifnum;
+	bool ret;
+
+	ifnum = nss_cmn_get_interface_number_by_dev_and_type(dev, type);
+	if (ifnum == -1) {
+		nss_nl_error("%p: unable to get NSS interface for net device %s of type %d\n", dev, dev->name, type);
+		return false;
+	}
+
+	ret = nss_gre_redir_unregister_if(ifnum);
+	if (!ret) {
+		nss_nl_error("%p: Unable to unregister interface %d\n", dev, ret);
+		return false;
+	}
+
+	status = nss_dynamic_interface_dealloc_node(ifnum, type);
+	if (status != NSS_TX_SUCCESS) {
+		nss_nl_error("%p: Unable to deallocate node %d\n", dev, status);
+		return false;
+	}
+
+	nss_nl_trace("%s: Sucessfully unregistered and deallocated %d\n", dev->name, ifnum);
+	return true;
+}
+
+/*
+ * nss_nlgre_redir_cmn_interfaces_unregister_and_dealloc
+ * 	Find out the interfaces to be deallocated
+ */
+void nss_nlgre_redir_cmn_interfaces_unregister_and_dealloc(struct nss_nlgre_redir_cmn_tun_data *tun_data)
+{
+	struct nss_ctx_instance *nss_ctx;
+
+	nss_ctx = nss_gre_redir_get_context();
+	if (tun_data->sjack_inner_ifnum != -1) {
+		if(!nss_nlgre_redir_cmn_unregister_and_deallocate(tun_data->dev, NSS_DYNAMIC_INTERFACE_TYPE_GRE_REDIR_WIFI_HOST_INNER)) {
+			nss_nl_error("%p: Unable to unregister and deallocate node of type %d\n", nss_ctx,
+					NSS_DYNAMIC_INTERFACE_TYPE_GRE_REDIR_WIFI_HOST_INNER);
+		}
+	}
+
+	if (tun_data->wifi_offl_inner_ifnum != -1) {
+		if (!nss_nlgre_redir_cmn_unregister_and_deallocate(tun_data->dev, NSS_DYNAMIC_INTERFACE_TYPE_GRE_REDIR_WIFI_OFFL_INNER)) {
+			nss_nl_error("%p: Unable to unregister and deallocate node of type %d\n", nss_ctx,
+					NSS_DYNAMIC_INTERFACE_TYPE_GRE_REDIR_WIFI_OFFL_INNER);
+		}
+	}
+
+	if (tun_data->host_inner_ifnum != -1) {
+		if (!nss_nlgre_redir_cmn_unregister_and_deallocate(tun_data->dev, NSS_DYNAMIC_INTERFACE_TYPE_GRE_REDIR_SJACK_INNER)) {
+			nss_nl_error("%p: Unable to unregister and deallocate node of type %d\n", nss_ctx,
+					NSS_DYNAMIC_INTERFACE_TYPE_GRE_REDIR_SJACK_INNER);
+		}
+	}
+
+	if (tun_data->outer_ifnum != -1) {
+		if (!nss_nlgre_redir_cmn_unregister_and_deallocate(tun_data->dev, NSS_DYNAMIC_INTERFACE_TYPE_GRE_REDIR_OUTER)) {
+			nss_nl_error("%p: Unable to unregister and deallocate node of type %d\n", nss_ctx,
+					NSS_DYNAMIC_INTERFACE_TYPE_GRE_REDIR_OUTER);
+		}
+	}
+
+	nss_nl_trace("%s: Sucessfully unregistered and deallocated\n", tun_data->dev->name);
+}
+
+/*
+ * nss_nlgre_redir_cmn_destroy_tun()
+ *	Unregisters and deallocs dynamic interfaces.
+ */
+int nss_nlgre_redir_cmn_destroy_tun(struct net_device *dev)
+{
+	struct nss_ctx_instance *nss_ctx = nss_gre_redir_get_context();
+	struct nss_nlgre_redir_cmn_tun_data tun_data;
+	int index;
+
+	dev_hold(dev);
+	tun_data = nss_nlgre_redir_cmn_get_tun_data(dev);
+	if (!tun_data.dev) {
+		nss_nl_error("%p: Invalid tun data\n", nss_ctx);
+		return -1;
+	}
+
+	index = nss_nlgre_redir_cmn_get_tun_data_index(tun_data.dev);
+	if (index < NSS_NLGRE_REDIR_CMN_MIN_TUNNELS || index >= NSS_NLGRE_REDIR_CMN_MAX_TUNNELS) {
+		nss_nl_error("%p: index out of bound %d\n", nss_ctx, index);
+		return -1;
+	}
+
+	nss_nlgre_redir_cmn_interfaces_unregister_and_dealloc(&tun_data);
+	nss_nlgre_redir_cmn_deinit_tun_data(&tun_data, index);
+	dev_put(dev);
+	unregister_netdev(dev);
+	nss_nl_info("%p: Successfully destroyed gretun = gretun%d tunnel\n", dev, index);
+	return index;
+}
+
+/*
+ * nss_nlgre_redir_cmn_create_tun()
+ *	Allocates netdevice and configures tunnel.
+ */
+struct net_device *nss_nlgre_redir_cmn_create_tun(uint32_t sip[4], uint32_t dip[4], uint8_t iptype)
+{
+	struct nss_ctx_instance *nss_ctx = nss_gre_redir_get_context();
+	struct nss_gre_redir_outer_configure_msg ngrocm = {0};
+	struct nss_gre_redir_inner_configure_msg ngrm = {0};
+	struct nss_nlgre_redir_cmn_tun_data tun_data;
+	struct nss_gre_redir_cmn_ndev_priv *priv;
+	struct net_device *dev;
+	nss_tx_status_t status;
+	int tun_idx = -1, ret;
+
+	tun_idx = nss_nlgre_redir_cmn_get_next_free_tun();
+	if (tun_idx == -1) {
+		nss_nl_error("Unable to allocate any tunnel\n");
+		return NULL;
+	}
+
+	/*
+	 * Initializes the tun_data
+	 */
+	nss_nlgre_redir_cmn_init_tun_data(&tun_data);
+	dev = alloc_netdev(sizeof(*priv), "gretun%d", NET_NAME_UNKNOWN, nss_nlgre_redir_cmn_dev_setup);
+	if (!dev) {
+		nss_nl_error("Unable to allocate netdev\n");
+		return NULL;
+	}
+
+	if (register_netdev(dev)) {
+		nss_nl_warn("Unable to register netdev %s\n", dev->name);
+		free_netdev(dev);
+		goto fail;
+	}
+
+	/*
+	 * Dynamic interface allocation.
+	 */
+	ret = nss_nlgre_redir_cmn_interface_alloc_and_register(&tun_data, dev);
+	if (ret == -1) {
+		nss_nl_error("%p: Unable to allocate and register gre_redir nodes\n", nss_ctx);
+		unregister_netdev(dev);
+		goto fail;
+	}
+
+	memcpy(ngrm.ip_src_addr, sip, sizeof(ngrm.ip_src_addr));
+	memcpy(ngrm.ip_dest_addr, dip, sizeof(ngrm.ip_dest_addr));
+
+	/*
+	 * TODO: Dynamic assignment of values from userspace
+	 * ip_df_policy value currently hard coded. This needs to be supplied from userspace.
+	 */
+	ngrm.ip_hdr_type = iptype;
+	ngrm.ip_df_policy = 0;
+	ngrm.gre_version = 0;
+	ngrm.ip_ttl = NSS_NLGRE_REDIR_CMN_IP_TTL;
+	ngrm.except_outerif = tun_data.outer_ifnum;
+
+	/*
+	 * TODO: Dynamic assignment of values from userspace
+	 * rps_hint value currently hard coded. This needs to be supplied from userspace.
+	 */
+	ngrocm.ip_hdr_type = iptype;
+	ngrocm.rps_hint = 0;
+	ngrocm.except_hostif = tun_data.host_inner_ifnum;
+	ngrocm.except_offlif = tun_data.wifi_offl_inner_ifnum;
+	ngrocm.except_sjackif = tun_data.sjack_inner_ifnum;
+
+	status = nss_gre_redir_configure_inner_node(tun_data.host_inner_ifnum, &ngrm);
+	if (status != NSS_TX_SUCCESS) {
+		nss_nl_warn("%p: unable to configure host inner node %d\n", nss_ctx, tun_data.host_inner_ifnum);
+		unregister_netdev(dev);
+		goto fail;
+	}
+
+	status = nss_gre_redir_configure_inner_node(tun_data.wifi_offl_inner_ifnum, &ngrm);
+	if (status != NSS_TX_SUCCESS) {
+		nss_nl_warn("%p: unable to configure wifi offload inner node %d\n", nss_ctx, tun_data.host_inner_ifnum);
+		unregister_netdev(dev);
+		goto fail;
+	}
+
+	status = nss_gre_redir_configure_inner_node(tun_data.sjack_inner_ifnum, &ngrm);
+	if (status != NSS_TX_SUCCESS) {
+		nss_nl_warn("%p: unable to configure sjack inner node %d\n", nss_ctx, tun_data.sjack_inner_ifnum);
+		unregister_netdev(dev);
+		goto fail;
+	}
+
+	status = nss_gre_redir_configure_outer_node(tun_data.outer_ifnum, &ngrocm);
+	if (status != NSS_TX_SUCCESS) {
+		nss_nl_warn("%p: unable to configure outer node %d\n", nss_ctx, tun_data.host_inner_ifnum);
+		unregister_netdev(dev);
+		goto fail;
+	}
+
+	tun_data.enable = true;
+	tun_data.dev = dev;
+	if (!nss_nlgre_redir_cmn_set_tun_data(&tun_data, tun_idx)) {
+		nss_nl_error("%p: Unable to set tun data\n", nss_ctx);
+		unregister_netdev(dev);
+		goto fail;
+	}
+
+	return dev;
+fail:
+	nss_nlgre_redir_cmn_interfaces_unregister_and_dealloc(&tun_data);
+	nss_nlgre_redir_cmn_deinit_tun_data(&tun_data, tun_idx);
+	return NULL;
+}
+
+/*
+ * nss_nlgre_redir_cmn_get_dev_ifnum()
+ * 	Returns the interface number by dev and type
+ */
+int32_t nss_nlgre_redir_cmn_get_dev_ifnum(char *dev_name)
+{
+	struct net_device *dev;
+	uint32_t ifnum;
+
+	if (!dev_name) {
+		nss_nl_error("dev_name is NULL\n");
+		return -1;
+	}
+
+	/*
+	 * Get the dev reference
+	 */
+	dev = dev_get_by_name(&init_net, dev_name);
+	if (!dev) {
+		nss_nl_error("Invalid parameter: %s\n", dev_name);
+		return -ENODEV;
+	}
+
+	ifnum = nss_cmn_get_interface_number_by_dev(dev);
+	dev_put(dev);
+	return ifnum;
+}
+
+/*
+ * nss_nlgre_redir_cmn_map_interface()
+ *	Map nss interface to tunnel ID.
+ */
+int nss_nlgre_redir_cmn_map_interface(uint32_t nexthop_nssif, uint16_t lag_en, struct nss_nlgre_redir_map *map_params)
+{
+	struct nss_gre_redir_msg ngrm;
+	struct nss_ctx_instance *nss_ctx;
+	nss_tx_status_t ret;
+	uint32_t vap_nss_if;
+	uint8_t tun_type;
+	uint32_t len;
+
+	len = sizeof(struct nss_gre_redir_msg) - sizeof(struct nss_cmn_msg);
+	nss_ctx = nss_gre_redir_get_context();
+	tun_type = nss_nlgre_redir_cmn_get_tun_type(map_params->tun_type);
+	vap_nss_if = nss_nlgre_redir_cmn_get_dev_ifnum(map_params->vap_nss_if);
+	if ((vap_nss_if >= NSS_DYNAMIC_IF_START+NSS_MAX_DYNAMIC_INTERFACES) || (vap_nss_if < NSS_DYNAMIC_IF_START)) {
+		nss_nl_error("%p: vap_nss_if is out of valid range for vap: %d\n", nss_ctx, vap_nss_if);
+		return -1;
+	}
+
+	if (map_params->rid >= NSS_NLGRE_REDIR_CMN_RADIO_ID_MAX) {
+		nss_nl_error("%p: radio_id is out of valid range for vap: %d\n", nss_ctx, vap_nss_if);
+		return -1;
+	}
+
+	if (map_params->vid >= NSS_NLGRE_REDIR_CMN_VAP_ID_MAX) {
+		nss_nl_error("%p: vap_id is out of valid range for vap: %d\n", nss_ctx, vap_nss_if);
+		return -1;
+	}
+
+	nss_cmn_msg_init(&ngrm.cm, NSS_GRE_REDIR_INTERFACE, NSS_GRE_REDIR_TX_INTERFACE_MAP_MSG,
+			len, nss_nlgre_redir_cmn_map_unmap_msg_cb, NULL);
+
+	ngrm.msg.interface_map.vap_nssif = vap_nss_if;
+	ngrm.msg.interface_map.radio_id = map_params->rid;
+	ngrm.msg.interface_map.vap_id = map_params->vid;
+	ngrm.msg.interface_map.tunnel_type = tun_type;
+	ngrm.msg.interface_map.ipsec_pattern = map_params->ipsec_sa_pattern;
+	ngrm.msg.interface_map.lag_en = lag_en;
+	ngrm.msg.interface_map.nexthop_nssif = nexthop_nssif;
+
+	ret = nss_gre_redir_tx_msg_sync(nss_ctx, &ngrm);
+	if (ret != NSS_TX_SUCCESS) {
+		nss_nl_error("%p: Tx to firmware failed\n", nss_ctx);
+		return -1;
+	}
+
+	nss_nl_info("%p: Successfully transmitted msg to firmware\n", nss_ctx);
+	return 0;
+}
+
+/*
+ * nss_nlgre_redir_cmn_unmap_interface()
+ *	Interface unmap message.
+ */
+int nss_nlgre_redir_cmn_unmap_interface(struct nss_nlgre_redir_unmap *unmap_params)
+{
+	struct nss_ctx_instance *nss_ctx;
+	struct nss_gre_redir_msg ngrm;
+	uint32_t vap_nss_if, len;
+	nss_tx_status_t ret;
+
+	len = sizeof(struct nss_gre_redir_msg) - sizeof(struct nss_cmn_msg);
+	vap_nss_if = nss_nlgre_redir_cmn_get_dev_ifnum(unmap_params->vap_nss_if);
+	nss_ctx = nss_gre_redir_get_context();
+
+	if ((vap_nss_if >= NSS_DYNAMIC_IF_START+NSS_MAX_DYNAMIC_INTERFACES) || (vap_nss_if < NSS_DYNAMIC_IF_START)) {
+		nss_nl_error("%p: vap_nss_if is out of valid range for vap: %d\n", nss_ctx, vap_nss_if);
+		return -1;
+	}
+
+	if (unmap_params->rid >= NSS_NLGRE_REDIR_CMN_RADIO_ID_MAX) {
+		nss_nl_error("%p: radio_id is out of valid range for vap: %d\n", nss_ctx, vap_nss_if);
+		return -1;
+	}
+
+	if (unmap_params->vid >= NSS_NLGRE_REDIR_CMN_VAP_ID_MAX) {
+		nss_nl_error("%p: vap_id is out of valid range for vap: %d\n", nss_ctx, vap_nss_if);
+		return -1;
+	}
+
+	nss_cmn_msg_init(&ngrm.cm, NSS_GRE_REDIR_INTERFACE, NSS_GRE_REDIR_TX_INTERFACE_UNMAP_MSG,
+			len, nss_nlgre_redir_cmn_map_unmap_msg_cb, NULL);
+	ngrm.msg.interface_unmap.vap_nssif = vap_nss_if;
+	ngrm.msg.interface_unmap.radio_id = unmap_params->rid;
+	ngrm.msg.interface_unmap.vap_id = unmap_params->vid;
+
+	ret = nss_gre_redir_tx_msg_sync(nss_ctx, &ngrm);
+	if (ret != NSS_TX_SUCCESS) {
+		nss_nl_error("%p: Tx to firmware failed\n", nss_ctx);
+		return -1;
+	}
+
+	nss_nl_info("%p: Successfully transmitted msg to firmware\n", nss_ctx);
+	return 0;
+}
+
+/*
+ * nss_nlgre_redir_cmn_set_next_hop()
+ *	Sets next hop as gre-redir for wifi.
+ */
+int nss_nlgre_redir_cmn_set_next_hop(uint32_t next_dev_ifnum, struct nss_nlgre_redir_set_next *setnext_params)
+{
+	struct nss_ctx_instance *nss_ctx;
+	nss_tx_status_t ret;
+	int ifnumber;
+	void *ctx;
+
+	nss_ctx = nss_gre_redir_get_context();
+	ifnumber = nss_nlgre_redir_cmn_get_dev_ifnum(setnext_params->dev_name);
+	if (ifnumber == -1) {
+		nss_nl_error("%p: Unable to find NSS interface for net device %s\n", nss_ctx, setnext_params->dev_name);
+		return -1;
+	}
+
+	nss_nl_info("%p: next hop interface number is %d\n", nss_ctx, next_dev_ifnum);
+	ctx = nss_wifi_get_context();
+
+	ret = nss_wifi_vdev_set_next_hop(ctx, ifnumber, next_dev_ifnum);
+	if (ret != NSS_TX_SUCCESS) {
+		nss_nl_error("%p: wifi drv api failed to set next hop\n", nss_ctx);
+		return -1;
+	}
+
+	nss_nl_info("%p: Successfully set the next hop\n", nss_ctx);
+	return 0;
+}
+
diff --git a/netlink/nss_nlgre_redir_cmn.h b/netlink/nss_nlgre_redir_cmn.h
new file mode 100644
index 0000000..64daaaa
--- /dev/null
+++ b/netlink/nss_nlgre_redir_cmn.h
@@ -0,0 +1,155 @@
+/*
+ ***************************************************************************
+ * Copyright (c) 2014-2015,2019, 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 Netlink gre_redir headers
+ */
+#define NSS_NLGRE_REDIR_CMN_NEEDED_HEADROOM 192		/**< Maximum headroom available */
+#define NSS_NLGRE_REDIR_CMN_RADIO_ID_MAX 2		/**< Radio Id max size */
+#define NSS_NLGRE_REDIR_CMN_RADIO_ID_MIN 0		/**< Radio Id min size */
+#define NSS_NLGRE_REDIR_CMN_VAP_ID_MAX 16		/**< Vap Id max size */
+#define NSS_NLGRE_REDIR_CMN_VAP_ID_MIN 0		/**< Vap Id min size */
+#define NSS_NLGRE_REDIR_CMN_MAX_TUNNELS 24		/**< Maximum number of tunnels allowed */
+#define NSS_NLGRE_REDIR_CMN_TUN_TYPE_MAX_SZ 16		/**< Maximum size of tunnel type */
+#define NSS_NLGRE_REDIR_CMN_MAX_SKB_PRINT_LEN 3		/**< Maximum length of skb to print */
+#define NSS_NLGRE_REDIR_CMN_MIN_TUNNELS 0		/**< Mininum number of tunnels required */
+#define NSS_NLGRE_REDIR_CMN_IP_TTL 128			/**< Time to live for IP */
+
+/*
+ * netdevice private data
+ */
+struct nss_gre_redir_cmn_ndev_priv {
+	uint32_t gre_seq;				/**< Sequence number */
+};
+
+/*
+ * Context need to be maintained globally for GRE redirect tunnel.
+ */
+struct nss_nlgre_redir_cmn_tun_data {
+	struct net_device *dev;				/**< Net device */
+	int32_t host_inner_ifnum;			/**< Interface no. of pnode host inner */
+	int32_t wifi_offl_inner_ifnum;			/**< Interface no. of pnode wifi offld inner */
+	int32_t sjack_inner_ifnum;			/**< Interface no. of pnode sjack inner */
+	int32_t outer_ifnum;				/**< Interface no. of pnode outer */
+	bool enable;					/**< Device is enabled or not */
+};
+
+/*
+ * nss_nlgre_redir_cmn_tun_type
+ * 	Different tunnel types supported in gre_redir
+ */
+enum nss_nlgre_redir_cmn_tun_type {
+	NSS_NLGRE_REDIR_CMN_TUN_TYPE_UNKNOWN,		/**< Unknown tunnel type */
+	NSS_NLGRE_REDIR_CMN_TUN_TYPE_TUN,		/**< Raw mode 802.11 frames traffic*/
+	NSS_NLGRE_REDIR_CMN_TUN_TYPE_DTUN,		/**< For 802.3 frames traffic */
+	NSS_NLGRE_REDIR_CMN_TUN_TYPE_SPLIT,		/**< For split mode */
+	NSS_NLGRE_REDIR_CMN_TUN_TYPE_MAX		/**< Max number of tun type supported */
+};
+
+/*
+ * nss_nlgre_redir_cmn_mode_type
+ * 	Modes available for setting next hop
+ */
+enum nss_nlgre_redir_cmn_mode_type {
+	NSS_NLGRE_REDIR_CMN_MODE_TYPE_UNKNOWN,	/**< Unknown mode type */
+	NSS_NLGRE_REDIR_CMN_MODE_TYPE_WIFI,	/**< ath0 ---> wifi_offld_inner */
+	NSS_NLGRE_REDIR_CMN_MODE_TYPE_SPLIT,	/**< ath0 ---> ETH_RX_INTERFACE */
+	NSS_NLGRE_REDIR_CMN_MODE_TYPE_MAX	/**< Max number of modes supported */
+};
+
+/*
+ * nss_nlgre_redir_cmn_init()
+ * 	Initializes the tun_data
+ */
+void nss_nlgre_redir_cmn_init(void);
+
+/*
+ * nss_nlgre_redir_cmn_get_tun_ifnum()
+ * 	Returns the interface number corresponding to dev
+ */
+int32_t nss_nlgre_redir_cmn_get_tun_ifnum(enum nss_nlgre_redir_cmn_mode_type type, struct net_device *dev);
+/*
+ * nss_nlgre_redir_cmn_mode_str_to_enum()
+ * 	Returns the enum converted value of the string
+ */
+enum nss_nlgre_redir_cmn_mode_type nss_nlgre_redir_cmn_mode_str_to_enum(char *mode);
+
+/*
+ * nss_nlgre_redir_cmn_get_tun_data_index()
+ * 	Returns the interface number of dev
+ */
+int nss_nlgre_redir_cmn_get_tun_data_index(struct net_device *dev);
+
+/*
+ * nss_nlgre_redir_cmn_get_dev_ifnum()
+ * 	Returns the interface number of dev
+ */
+int32_t nss_nlgre_redir_cmn_get_dev_ifnum(char *dev_name);
+
+/*
+ * nss_nlgre_redir_cmn_get_tun_type()
+ * 	Returns the tunnel type
+ */
+enum nss_nlgre_redir_cmn_tun_type nss_nlgre_redir_cmn_get_tun_type(char *tun_type);
+
+/*
+ * nss_gre_redir_cmn_unregister_and_deallocate()
+ * 	Unregisters and deallocates a node.
+ */
+bool nss_nlgre_redir_cmn_unregister_and_deallocate(struct net_device *dev, uint32_t type);
+
+/*
+ * nss_nlgre_redir_cmn_print_skb()
+ * 	Prints the first 48 bytes of skb
+ */
+void nss_nlgre_redir_cmn_print_skb(struct sk_buff *skb);
+
+/*
+ * nss_nlgre_redir_cmn_create_tun()
+ * 	Creates a gre_redir tunnel
+ */
+struct net_device *nss_nlgre_redir_cmn_create_tun(uint32_t sip[4], uint32_t dip[4], uint8_t iptype);
+
+/*
+ * nss_nlgre_redir_cmn_destroy_tun()
+ * 	Destroys the gre_redir tunnel
+ */
+int nss_nlgre_redir_cmn_destroy_tun(struct net_device *dev);
+
+/*
+ * nss_nlgre_redir_cmn_unmap_interface()
+ * 	Unmaps the nss interface
+ */
+int nss_nlgre_redir_cmn_unmap_interface(struct nss_nlgre_redir_unmap *unmap_params);
+
+/*
+ * nss_nlgre_redir_cmn_map_interface()
+ * 	Unmaps the nss interface
+ */
+int nss_nlgre_redir_cmn_map_interface(uint32_t nexthop_nssif, uint16_t lag_en, struct nss_nlgre_redir_map *map_params);
+
+/*
+ * nss_nlgre_redir_cmn_set_next_hop()
+ * 	Sets the next hop of the nss ath0 interface
+ */
+int nss_nlgre_redir_cmn_set_next_hop(uint32_t next_dev_ifnum, struct nss_nlgre_redir_set_next *set_next_params);
+
+/*
+ * nss_nlgre_redir_cmn_init()
+ *	Initializes tun_data and lock variable.
+ */
+void nss_nlgre_redir_cmn_init(void);
+
diff --git a/netlink/nss_nlgre_redir_family.c b/netlink/nss_nlgre_redir_family.c
new file mode 100644
index 0000000..43a8f3d
--- /dev/null
+++ b/netlink/nss_nlgre_redir_family.c
@@ -0,0 +1,78 @@
+/*
+ **************************************************************************
+ * Copyright (c) 2015-2016,2018-2019, 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_nlgre_redir.c
+ * 	NSS Netlink gre_redir Handler
+ */
+#include <linux/version.h>
+#include <net/genetlink.h>
+#include <nss_api_if.h>
+#include <nss_nlcmn_if.h>
+#include <nss_nl_if.h>
+#include <nss_nlgre_redir_if.h>
+#include "nss_nl.h"
+#include "nss_nlgre_redir_cmd.h"
+
+/*
+ * nss_nlgre_redir_cmd_mcgrp
+ * 	Multicast group for sending message status & events
+ */
+static const struct genl_multicast_group nss_nlgre_redir_family_mcgrp[] = {
+	{.name = NSS_NLGRE_REDIR_MCAST_GRP},
+};
+
+/*
+ * nss_nlgre_redir_family_init()
+ * 	handler init
+ */
+bool nss_nlgre_redir_family_init(void)
+{
+	int err;
+	nss_nl_info_always("Init NSS netlink gre_redir handler\n");
+
+	/*
+	 * register NETLINK ops with the family
+	 */
+	err = genl_register_family_with_ops_groups(&nss_nlgre_redir_cmd_family, nss_nlgre_redir_cmd_ops, nss_nlgre_redir_family_mcgrp);
+	if (err) {
+		nss_nl_info_always("Error: %d unable to register gre_redir family\n", err);
+		return false;
+	}
+
+	return true;
+}
+
+/*
+ * nss_nlgre_redir_family_exit()
+ *	handler exit
+ */
+bool nss_nlgre_redir_family_exit(void)
+{
+	int err;
+	nss_nl_info_always("Exit NSS netlink gre_redir handler\n");
+
+	/*
+	 * unregister the ops family
+	 */
+	err = genl_unregister_family(&nss_nlgre_redir_cmd_family);
+	if (err) {
+		nss_nl_info_always("Error: %d unable to unregister gre_redir NETLINK family\n", err);
+		return false;
+	}
+
+	return true;
+}
diff --git a/netlink/nss_nlgre_redir_family.h b/netlink/nss_nlgre_redir_family.h
new file mode 100644
index 0000000..dbad4e1
--- /dev/null
+++ b/netlink/nss_nlgre_redir_family.h
@@ -0,0 +1,44 @@
+/*
+ **************************************************************************
+ * Copyright (c) 2014-2015,2019, 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_nlgre_redir_family.h
+ *	NSS Netlink gre_redir API definitions
+ */
+#ifndef __NSS_NLGRE_REDIR_FAMILY_H
+#define __NSS_NLGRE_REDIR_FAMILY_H
+
+/*
+ * nss_nlgre_redir_family_init()
+ * 	To initialize the gre_redir module
+ */
+bool nss_nlgre_redir_family_init(void);
+
+/*
+ * nss_nlgre_redir_family_exit()
+ * 	Exit the gre_redir module
+ */
+bool nss_nlgre_redir_family_exit(void);
+
+#if defined(CONFIG_NSS_NLGRE_REDIR_FAMILY)
+#define NSS_NLGRE_REDIR_FAMILY_INIT nss_nlgre_redir_family_init
+#define NSS_NLGRE_REDIR_FAMILY_EXIT nss_nlgre_redir_family_exit
+#else
+#define NSS_NLGRE_REDIR_FAMILY_INIT 0
+#define NSS_NLGRE_REDIR_FAMILY_EXIT 0
+#endif /* !CONFIG_NSS_NLGRE_REDIR_FAMILY */
+
+#endif /* __NSS_NLGRE_REDIR_FAMILY_H */
diff --git a/netlink/nss_nlgre_redir_lag.c b/netlink/nss_nlgre_redir_lag.c
new file mode 100644
index 0000000..7b76bcc
--- /dev/null
+++ b/netlink/nss_nlgre_redir_lag.c
@@ -0,0 +1,439 @@
+/*
+ ***************************************************************************
+ * Copyright (c) 2015-2016,2018-2019, The Linux Foundation. All rights reserved.
+ * Permission to use, copy, modify, and/or distribute this software for
+ * any purpose with or without fee is hereby granted, provided that the
+ * above copyright notice and this permission notice appear in all copies.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ ***************************************************************************
+ */
+
+#include <linux/version.h>
+#include <linux/etherdevice.h>
+#include <linux/if_ether.h>
+#include <net/genetlink.h>
+#include <nss_api_if.h>
+#include <nss_nl_if.h>
+#include "nss_nlcmn_if.h"
+#include "nss_nl.h"
+#include "nss_nlgre_redir_if.h"
+#include "nss_nlgre_redir_cmn.h"
+#include "nss_nlgre_redir_lag.h"
+
+/*
+ * Spin_lock for lag_pvt_data
+ */
+static DEFINE_SPINLOCK(lock);
+
+static struct nss_nlgre_redir_lag_pvt_data lag_pvt_data;
+static const struct net_device_ops dummy_netdev_ops;
+
+/*
+ * nss_nlgre_redir_lag_msg_completion_cb()
+ * 	HLOS->NSS message completion callback.
+ */
+static void nss_nlgre_redir_lag_msg_completion_cb(void *app_data, struct nss_cmn_msg *cmnmsg)
+{
+	struct nss_ctx_instance *nss_ctx = nss_gre_redir_get_context();
+	nss_nl_info("%p: callback gre_redir tunnel msg from NSS\n", nss_ctx);
+}
+
+/*
+ * nss_nlgre_redir_lag_us_data_cb()
+ *	Exception handler for LAG_US node.
+ */
+static void nss_nlgre_redir_lag_us_data_cb(struct net_device *netdev, struct sk_buff *skb, struct napi_struct *napi)
+{
+	nss_nl_trace("Exception packet on lag_us node:\n");
+	nss_nlgre_redir_cmn_print_skb(skb);
+	dev_kfree_skb_any(skb);
+}
+
+/*
+ * nss_nlgre_redir_lag_ds_data_cb()
+ *	Exception handler for LAG_DS node.
+ */
+static void nss_nlgre_redir_lag_ds_data_cb(struct net_device *netdev, struct sk_buff *skb, struct napi_struct *napi)
+{
+	nss_nl_trace("Exception packet on lag_ds node:\n");
+	nss_nlgre_redir_cmn_print_skb(skb);
+	dev_kfree_skb_any(skb);
+}
+
+/*
+ * nss_nlgre_redir_lag_us_msg_cb()
+ *	GRE LAG upstream  NSS->HLOS message callback.
+ */
+static void nss_nlgre_redir_lag_us_msg_cb(void *app_data, struct nss_cmn_msg *cmnmsg)
+{
+	struct nss_gre_redir_lag_us_msg *tunmsg = (struct nss_gre_redir_lag_us_msg *)cmnmsg;
+	int i;
+
+	if (tunmsg->cm.type != NSS_GRE_REDIR_LAG_US_DB_HASH_NODE_MSG) {
+		return;
+	}
+
+	nss_nl_trace("%p: callback_stats: count = %u index = %u\n", tunmsg,
+			tunmsg->msg.hash_stats.count, tunmsg->msg.hash_stats.db_entry_idx);
+	for (i = 0; i < tunmsg->msg.hash_stats.count; i++) {
+		nss_nl_trace("%p: hits = %llu smac = %pM dmac = %pM\n", tunmsg,
+				tunmsg->msg.hash_stats.hstats[i].hits,
+				&(tunmsg->msg.hash_stats.hstats[i].src_mac),
+				&(tunmsg->msg.hash_stats.hstats[i].dest_mac));
+	}
+}
+
+/*
+ * nss_nlgre_redir_lag_ds_msg_cb()
+ *	GRE LAG upstream  NSS->HLOS message callback.
+ */
+static void nss_nlgre_redir_lag_ds_msg_cb(void *app_data, struct nss_cmn_msg *cmnmsg)
+{
+	struct nss_gre_redir_lag_ds_msg *tunmsg = (struct nss_gre_redir_lag_ds_msg *)cmnmsg;
+
+	if (tunmsg->cm.type != NSS_GRE_REDIR_LAG_DS_STATS_SYNC_MSG) {
+		return;
+	}
+
+
+	nss_nl_trace("%p: callback_stats: invalid_dest: %u, exception_cnt: %u\n", tunmsg,
+			tunmsg->msg.ds_sync_stats.ds_stats.dst_invalid,
+			tunmsg->msg.ds_sync_stats.ds_stats.exception_cnt);
+
+
+}
+
+/*
+ * nss_nlgre_redir_lag_get_lag_pvt_data()
+ * 	Return lag_pvt_data after lock checking
+ */
+static struct nss_nlgre_redir_lag_pvt_data nss_nlgre_redir_lag_get_lag_pvt_data(void)
+{
+	struct nss_nlgre_redir_lag_pvt_data ret_lag_pvt_data;
+
+	spin_lock(&lock);
+	ret_lag_pvt_data = lag_pvt_data;
+	spin_unlock(&lock);
+	return ret_lag_pvt_data;
+}
+
+/*
+ * nss_nlgre_redir_lag_set_lag_pvt_data()
+ * 	Return lag_pvt_data after lock checking
+ */
+static void nss_nlgre_redir_lag_set_lag_pvt_data(struct nss_nlgre_redir_lag_pvt_data *data)
+{
+	spin_lock(&lock);
+	lag_pvt_data = *data;
+	spin_unlock(&lock);
+}
+
+/*
+ * nss_nlgre_redir_lag_destroy_tun()
+ *	Destroy tunnel in LAG mode.
+ */
+int nss_nlgre_redir_lag_destroy_tun(struct net_device *dev)
+{
+	struct nss_ctx_instance *nss_ctx = nss_gre_redir_get_context();
+	struct nss_nlgre_redir_lag_pvt_data lag_pvt_data;
+	uint8_t tun_idx;
+	int ret;
+
+	if (!dev) {
+		nss_nl_error("%p: Dev is null\n", nss_ctx);
+		return -1;
+	}
+
+	dev_hold(dev);
+
+	/*
+	 * Unregister and deallocate the lag US and DS interfaces
+	 */
+	lag_pvt_data = nss_nlgre_redir_lag_get_lag_pvt_data();
+	ret = nss_gre_redir_lag_us_unregister_and_dealloc(lag_pvt_data.inner_ifnum);
+	if (ret) {
+		nss_nl_error("%p: Unable to deallocate node %d\n", dev, lag_pvt_data.inner_ifnum);
+	}
+
+	ret = nss_gre_redir_lag_ds_unregister_and_dealloc(lag_pvt_data.outer_ifnum);
+	if (ret) {
+		nss_nl_error("%p: Unable to deallocate node %d\n", dev, lag_pvt_data.outer_ifnum);
+	}
+
+	for (tun_idx = 0; tun_idx < NSS_NLGRE_REDIR_LAG_SLAVES; tun_idx++) {
+		if (!lag_pvt_data.slaves[tun_idx]) {
+			nss_nl_error("%p: Slave tunnel index out of range\n", nss_ctx);
+			ret = -1;
+			goto done;
+		}
+
+		ret = nss_nlgre_redir_cmn_destroy_tun(lag_pvt_data.slaves[tun_idx]);
+		if (ret == -1) {
+			nss_nl_error("%p: Unable to destroy tunnel associated with slave %d\n", nss_ctx, tun_idx+1);
+			goto done;
+		}
+
+		nss_nl_info("%p: Successfully destroyed slave tunnel %d\n", nss_ctx, tun_idx+1);
+	}
+
+done:
+	/*
+	 * Free the lag dummy dev resources
+	 */
+	dev_put(dev);
+	unregister_netdev(dev);
+	free_netdev(dev);
+	lag_pvt_data.dev = NULL;
+	return ret;
+}
+
+/*
+ * nss_nlgre_redir_lag_create_tun()
+ * 	Cretate GRE redir tunnel in LAG mode.
+ */
+int nss_nlgre_redir_lag_create_tun(struct nss_nlgre_redir_create_tun *create_params)
+{
+	struct nss_nlgre_redir_lag_pvt_data lag_pvt_data;
+	struct nss_gre_redir_lag_us_config_msg config;
+	struct nss_gre_redir_cmn_ndev_priv *gr;
+	struct nss_ctx_instance *nss_ctx;
+	struct net_device *dummy_dev;
+	bool status;
+	int iter;
+
+	nss_ctx = nss_gre_redir_get_context();
+	lag_pvt_data = nss_nlgre_redir_lag_get_lag_pvt_data();
+	lag_pvt_data.slaves[0] = nss_nlgre_redir_cmn_create_tun(create_params->sip, create_params->dip, create_params->iptype);
+	if (!lag_pvt_data.slaves[0]) {
+		nss_nl_error("%p: Unable to create tunnel for %dst slave\n", nss_ctx, 1);
+		goto fail0;
+	}
+
+	lag_pvt_data.slaves[1] = nss_nlgre_redir_cmn_create_tun(create_params->ssip, create_params->sdip, create_params->iptype);
+	if (!lag_pvt_data.slaves[1]) {
+		nss_nl_error("%p: Unable to create tunnel for %dnd slave\n", nss_ctx, 2);
+		goto fail0;
+	}
+
+	dummy_dev = alloc_netdev(sizeof(*gr), "grelag%d", NET_NAME_UNKNOWN, ether_setup);
+	if (!dummy_dev) {
+		nss_nl_error("%p: Unable to allocate net_dev for dummy_dev\n", nss_ctx);
+		goto fail0;
+	}
+
+	dummy_dev->needed_headroom = NSS_NLGRE_REDIR_CMN_NEEDED_HEADROOM;
+	dummy_dev->netdev_ops = &dummy_netdev_ops;
+	lag_pvt_data.inner_ifnum = nss_gre_redir_lag_us_alloc_and_register_node(dummy_dev,
+			nss_nlgre_redir_lag_us_data_cb, nss_nlgre_redir_lag_us_msg_cb, dummy_dev);
+
+	if (lag_pvt_data.inner_ifnum == -1) {
+		nss_nl_error("%p: Unable to allocate or register LAG US dynamic node.\n", nss_ctx);
+		goto fail1;
+	}
+
+	nss_nl_info("%p: LAG US interface number = %d\n", nss_ctx, lag_pvt_data.inner_ifnum);
+	lag_pvt_data.outer_ifnum = nss_gre_redir_lag_ds_alloc_and_register_node(dummy_dev,
+			nss_nlgre_redir_lag_ds_data_cb, nss_nlgre_redir_lag_ds_msg_cb, dummy_dev);
+
+	if (lag_pvt_data.outer_ifnum == -1) {
+		nss_nl_error("%p: Unable to allocate or register LAG DS dynamic node.\n", nss_ctx);
+		goto fail2;
+	}
+
+	nss_nl_info("%p: LAG DS interface number = %d\n", nss_ctx, lag_pvt_data.outer_ifnum);
+
+	config.hash_mode = create_params->hash_mode;
+	config.num_slaves = NSS_NLGRE_REDIR_LAG_SLAVES;
+
+	for (iter = 0; iter < NSS_NLGRE_REDIR_LAG_SLAVES; iter++) {
+		config.if_num[iter] = nss_nlgre_redir_cmn_get_tun_ifnum(NSS_NLGRE_REDIR_CMN_MODE_TYPE_WIFI, lag_pvt_data.slaves[iter]);
+	}
+
+	status = nss_gre_redir_lag_us_configure_node(lag_pvt_data.inner_ifnum, &config);
+	if (!status) {
+		nss_nl_info("%p: Unable to configure LAG US node.\n", nss_ctx);
+		goto fail3;
+	}
+
+	if (register_netdev(dummy_dev)) {
+		nss_nl_error("%p: Unable to register dummy_dev\n", nss_ctx);
+		goto fail3;
+	}
+
+	lag_pvt_data.dev = dummy_dev;
+	nss_nlgre_redir_lag_set_lag_pvt_data(&lag_pvt_data);
+	return 0;
+
+fail3:
+	nss_nlgre_redir_cmn_unregister_and_deallocate(dummy_dev, NSS_DYNAMIC_INTERFACE_TYPE_GRE_REDIR_LAG_DS);
+fail2:
+	nss_nlgre_redir_cmn_unregister_and_deallocate(dummy_dev, NSS_DYNAMIC_INTERFACE_TYPE_GRE_REDIR_LAG_US);
+fail1:
+	free_netdev(dummy_dev);
+fail0:
+	for (iter = 0; iter < NSS_NLGRE_REDIR_LAG_SLAVES; iter++) {
+		if (!lag_pvt_data.slaves[iter]) {
+			nss_nlgre_redir_cmn_destroy_tun(lag_pvt_data.slaves[iter]);
+			continue;
+		}
+
+		break;
+	}
+
+	return -1;
+}
+
+/*
+ * nss_nlgre_redir_lag_map_interface()
+ * 	Maps the nss interface to tunnel ID
+ */
+int nss_nlgre_redir_lag_map_interface(struct nss_nlgre_redir_map *map_params)
+{
+	struct nss_nlgre_redir_lag_pvt_data lag_pvt_data;
+	struct nss_ctx_instance *nss_ctx;
+	uint32_t nexthop_nssif;
+	int ret;
+
+	nss_ctx = nss_gre_redir_get_context();
+	lag_pvt_data = nss_nlgre_redir_lag_get_lag_pvt_data();
+	nexthop_nssif = lag_pvt_data.outer_ifnum;
+	ret = nss_nlgre_redir_cmn_map_interface(nexthop_nssif, 1, map_params);
+	if (ret == -1) {
+		nss_nl_error("%p: Unable to map nss interface\n", nss_ctx);
+		return -1;
+	}
+
+	nss_nl_info("Successfully mapped the nss interface to tunnel ID\n");
+	return 0;
+}
+
+/*
+ * nss_nlgre_redir_lag_set_next_hop()
+ * 	Sets the next hop of ath0 interface as lag US node's interface
+ */
+int nss_nlgre_redir_lag_set_next_hop(struct nss_nlgre_redir_set_next *set_next_params)
+{
+	struct nss_nlgre_redir_lag_pvt_data lag_pvt_data;
+	struct nss_ctx_instance *nss_ctx;
+	uint32_t nexthop_ifnum;
+	int ret;
+
+	nss_ctx = nss_gre_redir_get_context();
+	lag_pvt_data = nss_nlgre_redir_lag_get_lag_pvt_data();
+	nexthop_ifnum = lag_pvt_data.inner_ifnum;
+	ret = nss_nlgre_redir_cmn_set_next_hop(nexthop_ifnum, set_next_params);
+	if (ret == -1) {
+		nss_nl_error("%p: Unable to set the next hop as lag US node's interface\n", nss_ctx);
+		return -1;
+	}
+
+	nss_nl_info("Successfully set the next hop as lag US node's interface\n");
+	return 0;
+}
+
+/*
+ * nss_nlgre_redir_lag_add_hash()
+ *	Add hash entry.
+ */
+int nss_nlgre_redir_lag_add_hash(struct nss_nlgre_redir_hash_ops *hash_ops)
+{
+	struct nss_nlgre_redir_lag_pvt_data lag_pvt_data;
+	struct nss_gre_redir_lag_us_msg *nglm;
+	struct nss_ctx_instance *nss_ctx;
+	nss_tx_status_t status;
+	uint32_t len;
+	int i;
+
+	lag_pvt_data = nss_nlgre_redir_lag_get_lag_pvt_data();
+	nss_ctx = nss_gre_redir_lag_us_get_context();
+	if (!nss_ctx) {
+		nss_nl_error("Unable to get nss context\n");
+		return -1;
+	}
+
+	nglm = kmalloc(sizeof(struct nss_gre_redir_lag_us_msg), GFP_KERNEL);
+	if (!nglm) {
+		nss_nl_error("%p: Unable to allocate memory to send add hash msg.\n", nss_ctx);
+		return -1;
+	}
+
+	len = sizeof(struct nss_gre_redir_lag_us_msg) - sizeof(struct nss_cmn_msg);
+	nss_cmn_msg_init(&nglm->cm, lag_pvt_data.inner_ifnum, NSS_GRE_REDIR_LAG_US_ADD_HASH_NODE_MSG, len,
+			nss_nlgre_redir_lag_msg_completion_cb, NULL);
+	memcpy((void *)(nglm->msg.add_hash.src_mac), (void *)hash_ops->smac, sizeof(nglm->msg.add_hash.src_mac));
+	memcpy((void *)(nglm->msg.add_hash.dest_mac), (void *)hash_ops->dmac, sizeof(nglm->msg.add_hash.dest_mac));
+	for (i = 0; i < NSS_NLGRE_REDIR_LAG_SLAVES; i++) {
+		if (hash_ops->slave != i) {
+			continue;
+		}
+
+		nglm->msg.add_hash.if_num = nss_nlgre_redir_cmn_get_tun_ifnum(NSS_NLGRE_REDIR_CMN_MODE_TYPE_WIFI,
+				lag_pvt_data.slaves[i]);
+		break;
+	}
+
+	if (i == NSS_NLGRE_REDIR_LAG_SLAVES || nglm->msg.add_hash.if_num == -1) {
+		nss_nl_error("%p: Invalid value for index, valid slaves: [%s, %s]\n", nss_ctx,
+				lag_pvt_data.slaves[0]->name, lag_pvt_data.slaves[1]->name);
+		return -1;
+	}
+
+	nss_nl_info("smac = %pM dmac = %pM ifnum = %d\n", &(nglm->msg.add_hash.src_mac),
+			&(nglm->msg.add_hash.dest_mac), nglm->msg.add_hash.if_num);
+	status = nss_gre_redir_lag_us_tx_msg_sync(nss_ctx, nglm);
+	kfree(nglm);
+	if (status != NSS_TX_SUCCESS) {
+		nss_nl_error("%p: Unable to add hash entry.\n", nss_ctx);
+		return -1;
+	}
+
+	return 0;
+}
+
+/*
+ * nss_nlgre_redir_lag_del_hash()
+ *	Delete hash entry.
+ */
+int nss_nlgre_redir_lag_del_hash(struct nss_nlgre_redir_hash_ops *hash_ops)
+{
+	struct nss_nlgre_redir_lag_pvt_data lag_pvt_data;
+	struct nss_gre_redir_lag_us_msg *nglm;
+	struct nss_ctx_instance *nss_ctx;
+	nss_tx_status_t status;
+	uint32_t len;
+
+	lag_pvt_data = nss_nlgre_redir_lag_get_lag_pvt_data();
+	nss_ctx = nss_gre_redir_lag_us_get_context();
+	if (!nss_ctx) {
+		nss_nl_error("Unable to get nss context\n");
+		return -1;
+	}
+
+	nglm = kmalloc(sizeof(struct nss_gre_redir_lag_us_msg), GFP_KERNEL);
+	if (!nglm) {
+		nss_nl_error("%p: Unable to allocate memory to send del hash msg.\n", nss_ctx);
+		return -1;
+	}
+
+	len = sizeof(struct nss_gre_redir_lag_us_msg) - sizeof(struct nss_cmn_msg);
+	nss_cmn_msg_init(&nglm->cm, lag_pvt_data.inner_ifnum, NSS_GRE_REDIR_LAG_US_DEL_HASH_NODE_MSG, len,
+			nss_nlgre_redir_lag_msg_completion_cb, NULL);
+	memcpy((void *)(nglm->msg.del_hash.src_mac), (void *)hash_ops->smac, sizeof(nglm->msg.del_hash.src_mac));
+	memcpy((void *)(nglm->msg.del_hash.dest_mac), (void *)hash_ops->dmac, sizeof(nglm->msg.del_hash.dest_mac));
+	nss_nl_info("smac = %pM dmac = %pM\n", &(nglm->msg.del_hash.src_mac), &(nglm->msg.del_hash.dest_mac));
+	status = nss_gre_redir_lag_us_tx_msg_sync(nss_ctx, nglm);
+	kfree(nglm);
+	if (status != NSS_TX_SUCCESS) {
+		nss_nl_error("%p: Unable to delete hash entry.\n", nss_ctx);
+		return -1;
+	}
+
+	return 0;
+}
+
diff --git a/netlink/nss_nlgre_redir_lag.h b/netlink/nss_nlgre_redir_lag.h
new file mode 100644
index 0000000..b0b7ee1
--- /dev/null
+++ b/netlink/nss_nlgre_redir_lag.h
@@ -0,0 +1,65 @@
+/*
+ ***************************************************************************
+ * Copyright (c) 2014-2015,2019, 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.
+ **************************************************************************
+ */
+
+#define NSS_NLGRE_REDIR_LAG_SLAVES 2		/**< Maximum number of slaves for lag tunnel */
+
+/*
+ * nss_nlgre_redir_lag_pvt_data
+ * 	Context need to be maintained for LAG deployment.
+ */
+struct nss_nlgre_redir_lag_pvt_data {
+	uint32_t inner_ifnum;			/**< Inner ifnum of lag netdev */
+	uint32_t outer_ifnum;			/**< Outer ifnum of lag netdev */
+	struct net_device *slaves[2];		/**< Slave's dev reference*/
+	struct net_device *dev;			/**< Pointer to netdev for lag */
+};
+
+/*
+ * nss_nlgre_redir_lag_create_tun()
+ *	Creates a lag node and configures LAG US/DS dynamic nodes.
+ */
+int nss_nlgre_redir_lag_create_tun(struct nss_nlgre_redir_create_tun *create_params);
+
+/*
+ * nss_nlgre_redir_lag_destroy_tun()
+ * 	Destroys the lag tunnel.
+ */
+int nss_nlgre_redir_lag_destroy_tun(struct net_device *dev);
+
+/*
+ * nss_nlgre_redir_lag_set_next_hop()
+ * 	Sets the next hop as lag US node
+ */
+int nss_nlgre_redir_lag_set_next_hop(struct nss_nlgre_redir_set_next *set_next_params);
+
+/*
+ * nss_nlgre_redir_lag_map_interface()
+ * 	Maps the vap interface to tunnel ID
+ */
+int nss_nlgre_redir_lag_map_interface(struct nss_nlgre_redir_map *map_params);
+
+/*
+ * nss_nlgre_redir_lag_add_hash()
+ * 	Add hash entry.
+ */
+int nss_nlgre_redir_lag_add_hash(struct nss_nlgre_redir_hash_ops *hash_ops);
+
+/*
+ * nss_nlgre_redir_lag_del_hash()
+ * 	Delete hash entry.
+ */
+int nss_nlgre_redir_lag_del_hash(struct nss_nlgre_redir_hash_ops *hash_ops);
+
diff --git a/netlink/nss_nlgre_redir_mgr.c b/netlink/nss_nlgre_redir_mgr.c
deleted file mode 100644
index 72ccd7d..0000000
--- a/netlink/nss_nlgre_redir_mgr.c
+++ /dev/null
@@ -1,728 +0,0 @@
-/*
- ***************************************************************************
- * Copyright (c) 2015-2016,2018-2019, The Linux Foundation. All rights reserved.
- * Permission to use, copy, modify, and/or distribute this software for
- * any purpose with or without fee is hereby granted, provided that the
- * above copyright notice and this permission notice appear in all copies.
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
- * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- ***************************************************************************
- */
-
-#include <linux/version.h>
-#include <linux/etherdevice.h>
-#include <linux/if_ether.h>
-#include <net/genetlink.h>
-#include <nss_api_if.h>
-#include <nss_nl_if.h>
-#include "nss_nlcmn_if.h"
-#include "nss_nl.h"
-#include "nss_nlgre_redir_if.h"
-
-#define DRIVER_NAME "nss_gre_redir"
-
-static struct nss_nlgre_redir_pvt_data pvt_data[NSS_NLGRE_REDIR_MAX_TUNNELS];
-static const struct net_device_ops gre_netdev_ops;
-
-/*
- * nss_nlgre_redir_mgr_get_pvt_data_index()
- *	Returns Index in array of private data.
- */
-static bool nss_nlgre_redir_mgr_get_pvt_data_index(struct net_device *dev, uint32_t *index)
-{
-	uint32_t i;
-	struct nss_ctx_instance *nss_ctx = nss_gre_redir_get_context();
-
-	*index = -1;
-	if (!dev) {
-		nss_nl_error("%p: Dev is NULL\n", nss_ctx);
-		return false;
-	}
-
-	for (i = 0; i < NSS_NLGRE_REDIR_MAX_TUNNELS; i++) {
-		if (pvt_data[i].dev != dev) {
-			continue;
-		}
-
-		*index = i;
-		return true;
-	}
-
-	return false;
-}
-
-/*
- * nss_nlgre_redir_mgr_xmit()
- *	Used when the interface is used for transmit data.
- */
-static netdev_tx_t nss_nlgre_redir_mgr_xmit(struct sk_buff *skb, struct net_device *dev)
-{
-	struct nss_gre_redir_ndev_priv *priv;
-	struct nss_gre_redir_encap_per_pkt_metadata *gre_encap = NULL;
-	uint32_t ifnum, ret = 0;
-	uint32_t *mdata_ptr = (uint32_t *)&skb->head[0];
-	struct nss_ctx_instance *nss_ctx = nss_gre_redir_get_context();
-
-	priv = netdev_priv(dev);
-	ifnum = nss_cmn_get_interface_number_by_dev_and_type(dev, NSS_DYNAMIC_INTERFACE_TYPE_GRE_REDIR_WIFI_HOST_INNER);
-	*mdata_ptr = NSS_GRE_REDIR_PER_PACKET_METADATA_OFFSET;
-
-	/*
-	 * configuring gre meta data
-	 */
-	gre_encap =  (struct nss_gre_redir_encap_per_pkt_metadata *)(skb->head + NSS_GRE_REDIR_PER_PACKET_METADATA_OFFSET);
-	if (gre_encap == NULL) {
-		nss_nl_error("%p: Not a valid skb to transmit\n", nss_ctx);
-		dev_kfree_skb_any(skb);
-		return -EINVAL;
-	}
-
-	memset(gre_encap, 0, sizeof(struct nss_gre_redir_encap_per_pkt_metadata));
-	gre_encap->gre_flags = 0;
-	gre_encap->gre_prio = 0;
-	gre_encap->gre_seq = ++priv->gre_seq;
-	gre_encap->gre_tunnel_id = 10;
-	gre_encap->ip_dscp = 0;
-	gre_encap->ip_df_override = 0;
-	gre_encap->ipsec_pattern = 0;
-
-	nss_ctx = nss_gre_redir_get_context();
-	ret = nss_gre_redir_tx_buf(nss_ctx, skb, ifnum);
-	if (ret != NSS_TX_SUCCESS) {
-		nss_nl_error("%p: Transmit failed and returned with %d\n", nss_ctx, ret);
-		dev_kfree_skb_any(skb);
-	}
-
-	return ret;
-}
-
-/*
- * nss_nlgre_redir_mgr_host_data_cb()
- *	Data callback for host offload inner node.
- */
-static void nss_nlgre_redir_mgr_host_data_cb(struct net_device *netdev, struct sk_buff *skb, struct napi_struct *napi)
-{
-	struct nss_gre_redir_decap_per_pkt_metadata *gre_decap = NULL;
-	struct nss_ctx_instance *nss_ctx = nss_gre_redir_get_context();
-
-	if (!skb) {
-		nss_nl_error("%p: SKB in NULL\n", nss_ctx);
-		return;
-	}
-
-	gre_decap = (struct nss_gre_redir_decap_per_pkt_metadata *)(skb->head +  NSS_GRE_REDIR_PER_PACKET_METADATA_OFFSET);
-	if (!gre_decap) {
-		nss_nl_error("%p: Not a valid skb\n", nss_ctx);
-		dev_kfree_skb_any(skb);
-		return;
-	}
-
-	skb->protocol = eth_type_trans(skb, netdev);
-	netif_receive_skb(skb);
-}
-
-/*
- * nss_nlgre_redir_mgr_print_skb()
- * 	Prints the skb data
- */
-static void nss_nlgre_redir_mgr_print_skb(struct sk_buff *skb)
-{
-	int length;
-	int iter;
-
-	/*
-	 * Check if length is less than 12 bytes
-	 * Else bring down to minimum multiple of 12 bytes.
-	 */
-	if (skb->len < 16)
-		nss_nl_trace("Skb too small to print min size: 16 bytes\n");
-	else
-		length = (skb->len / 16);
-	/*
-	 * Print first 48 bytes of sk_buff
-	 */
-	for (iter = 0; iter < length && length <= 3; iter++) {
-		nss_nl_trace("%04xx: %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x\n", iter,
-				skb->data[iter*8], skb->data[iter*8 + 1], skb->data[iter*8 + 2], skb->data[iter*8 + 3],
-				skb->data[iter*8 + 4], skb->data[iter*8 + 5], skb->data[iter*8 + 6], skb->data[iter*8 + 7],
-				skb->data[iter*8 + 8], skb->data[iter*8 + 9], skb->data[iter*8 + 10], skb->data[iter*8 + 11],
-				skb->data[iter*8 + 10], skb->data[iter*8 + 13], skb->data[iter*8 + 14], skb->data[iter*8 + 15]);
-	}
-}
-
-/*
- * nss_nlgre_redir_mgr_wifi_offl_data_cb()
- *	Data callback for wifi offload inner node.
- */
-static void nss_nlgre_redir_mgr_wifi_offl_data_cb(struct net_device *netdev, struct sk_buff *skb, struct napi_struct *napi)
-{
-	nss_nl_trace("Exception packet on wifi offld inner printing skb:\n");
-	nss_nlgre_redir_mgr_print_skb(skb);
-	dev_kfree_skb(skb);
-}
-
-/*
- * nss_nlgre_redir_mgr_sjack_data_cb
- *	Data callback for sjack inner node.
- */
-static void nss_nlgre_redir_mgr_sjack_data_cb(struct net_device *netdev, struct sk_buff *skb, struct napi_struct *napi)
-{
-	nss_nl_trace("Exception packet on sjack inner node printing skb:\n");
-	nss_nlgre_redir_mgr_print_skb(skb);
-	dev_kfree_skb(skb);
-}
-
-/*
- * nss_nlgre_redir_mgr_outer_data_cb()
- *	Data callback for outer node.
- */
-static void nss_nlgre_redir_mgr_outer_data_cb(struct net_device *netdev, struct sk_buff *skb, struct napi_struct *napi)
-{
-	nss_nl_trace("Exception packet on outer node printing skb:\n");
-	nss_nlgre_redir_mgr_print_skb(skb);
-	dev_kfree_skb(skb);
-}
-
-/*
- * nss_nlgre_redir_mgr_cb_gre_msg()
- *	HLOS->NSS message completion callback.
- */
-static void nss_nlgre_redir_mgr_cb_gre_msg(void)
-{
-	struct nss_ctx_instance *nss_ctx = nss_gre_redir_get_context();
-	nss_nl_info("%p: callback gre tunnel msg from NSS\n", nss_ctx);
-}
-
-/*
- * nss_nlgre_redir_mgr_deinit_pvt_data()
- *	Deinitialize private data for the given index.
- */
-static bool nss_nlgre_redir_mgr_deinit_pvt_data(uint32_t index)
-{
-	struct nss_ctx_instance *nss_ctx = nss_gre_redir_get_context();
-
-	if (index == NSS_NLGRE_REDIR_MAX_TUNNELS) {
-		nss_nl_error("%p: Index is out of range\n", nss_ctx);
-		return false;
-	}
-
-	pvt_data[index].dev = NULL;
-	pvt_data[index].enable = false;
-	pvt_data[index].host_inner_ifnum = -1;
-	pvt_data[index].wifi_offl_inner_ifnum = -1;
-	pvt_data[index].sjack_inner_ifnum = -1;
-	pvt_data[index].outer_ifnum = -1;
-	return true;
-}
-
-/*
- * nss_nlgre_redir_mgr_tunnel_type
- * 	Returns tunnel type
- */
-enum nss_gre_redir_tunnel_types nss_nlgre_redir_mgr_tunnel_type(char *tun_type)
-{
-	if (tun_type == NULL)
-		return NSS_GRE_REDIR_TUNNEL_TYPE_UNKNOWN;
-	if (!strncmp(tun_type, "tun", NSS_NLGRE_REDIR_TUN_TYPE_MAX_SZ))
-		return NSS_GRE_REDIR_TUNNEL_TYPE_TUN;
-	if (!strncmp(tun_type, "dtun", NSS_NLGRE_REDIR_TUN_TYPE_MAX_SZ))
-		return NSS_GRE_REDIR_TUNNEL_TYPE_DTUN;
-	if (!strncmp(tun_type, "split", NSS_NLGRE_REDIR_TUN_TYPE_MAX_SZ))
-		return NSS_GRE_REDIR_TUNNEL_TYPE_SPLIT;
-
-	return NSS_GRE_REDIR_TUNNEL_TYPE_UNKNOWN;
-}
-
-/*
- * nss_gre_redir_unregister_and_deallocate()
- *	Unregisters and deallocates a node.
- */
-bool nss_nlgre_redir_mgr_unregister_and_deallocate(struct net_device *dev, uint32_t type)
-{
-	int ifnum;
-	bool ret;
-	nss_tx_status_t status;
-
-	ifnum = nss_cmn_get_interface_number_by_dev_and_type(dev, type);
-	if (ifnum == -1) {
-		nss_nl_error("%p: unable to get NSS interface for net device %s of type %d\n", dev, dev->name, type);
-		return false;
-	}
-
-	ret = nss_gre_redir_unregister_if(ifnum);
-	if (!ret) {
-		nss_nl_error("%p: Unable to unregister interface %d\n", dev, ifnum);
-		return false;
-	}
-
-	status = nss_dynamic_interface_dealloc_node(ifnum, type);
-	if (status != NSS_TX_SUCCESS) {
-		nss_nl_error("%p: Unable to deallocate node %d\n", dev, ifnum);
-		return false;
-	}
-
-	return true;
-}
-
-/*
- * nss_nlgre_redir_mgr_interfaces_unregister_and_dealloc
- * 	Find out the interfaces to be deallocated
- */
-void nss_nlgre_redir_mgr_interfaces_unregister_and_dealloc(struct net_device *dev, int tun)
-{
-	struct nss_ctx_instance *nss_ctx = nss_gre_redir_get_context();
-	if (pvt_data[tun].sjack_inner_ifnum != -1) {
-		if(!nss_nlgre_redir_mgr_unregister_and_deallocate(dev, NSS_DYNAMIC_INTERFACE_TYPE_GRE_REDIR_WIFI_HOST_INNER)) {
-		nss_nl_error("%p: Unable to unregister and deallocate node of type %d\n", nss_ctx,
-				NSS_DYNAMIC_INTERFACE_TYPE_GRE_REDIR_WIFI_HOST_INNER);
-		}
-	}
-
-	if (pvt_data[tun].wifi_offl_inner_ifnum != -1) {
-		if (!nss_nlgre_redir_mgr_unregister_and_deallocate(dev, NSS_DYNAMIC_INTERFACE_TYPE_GRE_REDIR_WIFI_OFFL_INNER)) {
-			nss_nl_error("%p: Unable to unregister and deallocate node of type %d\n", nss_ctx,
-				NSS_DYNAMIC_INTERFACE_TYPE_GRE_REDIR_WIFI_OFFL_INNER);
-		}
-	}
-
-	if (pvt_data[tun].host_inner_ifnum != -1) {
-		if (!nss_nlgre_redir_mgr_unregister_and_deallocate(dev, NSS_DYNAMIC_INTERFACE_TYPE_GRE_REDIR_SJACK_INNER)) {
-			nss_nl_error("%p: Unable to unregister and deallocate node of type %d\n", nss_ctx,
-					NSS_DYNAMIC_INTERFACE_TYPE_GRE_REDIR_SJACK_INNER);
-		}
-	}
-
-	if (pvt_data[tun].outer_ifnum != -1) {
-		if (!nss_nlgre_redir_mgr_unregister_and_deallocate(dev, NSS_DYNAMIC_INTERFACE_TYPE_GRE_REDIR_OUTER)) {
-			nss_nl_error("%p: Unable to unregister and deallocate node of type %d\n", nss_ctx,
-					NSS_DYNAMIC_INTERFACE_TYPE_GRE_REDIR_OUTER);
-		}
-	}
-}
-
-/*
- * nss_nlgre_redir_mgr_ether_setup()
- * 	Used to setup the ethernet functionality
- */
-void nss_nlgre_redir_mgr_ether_setup(struct net_device *dev)
-{
-	eth_hw_addr_random(dev);
-}
-
-/*
- * nss_nlgre_redir_mgr_destroy_tun()
- *	Unregisters and deallocs dynamic interfaces.
- */
-int nss_nlgre_redir_mgr_destroy_tun(struct net_device *dev)
-{
-	int index;
-	struct nss_ctx_instance *nss_ctx = nss_gre_redir_get_context();
-
-	if (!dev) {
-		nss_nl_error("%p: Dev is NULL\n", nss_ctx);
-		return -1;
-	}
-
-	if (!nss_nlgre_redir_mgr_get_pvt_data_index(dev, &index)) {
-		nss_nl_error("%p: Unable to find tunnel associated with net dev\n", dev);
-		return -1;
-	}
-
-	nss_nlgre_redir_mgr_interfaces_unregister_and_dealloc(dev, index);
-	nss_nlgre_redir_mgr_deinit_pvt_data(index);
-	dev_put(dev);
-	unregister_netdev(dev);
-	free_netdev(dev);
-
-	return index;
-}
-
-/*
- * nss_nlgre_redir_mgr_interface_map()
- *	Interface map message.
- */
-bool nss_nlgre_redir_mgr_interface_map(uint32_t vap_nss_if, uint32_t tun_type, struct nss_nlgre_redir_map *if_map_params)
-{
-	struct nss_gre_redir_msg ngrm;
-	struct nss_ctx_instance *nss_ctx;
-	nss_tx_status_t ret;
-	uint32_t len = sizeof(struct nss_gre_redir_msg) - sizeof(struct nss_cmn_msg);
-	nss_ctx = nss_gre_redir_get_context();
-
-	if ((vap_nss_if >= NSS_DYNAMIC_IF_START+NSS_MAX_DYNAMIC_INTERFACES) || (vap_nss_if < NSS_DYNAMIC_IF_START)) {
-		nss_nl_error("%p: vap_nss_if is out of valid range for vap: %d\n", nss_ctx, vap_nss_if);
-		return false;
-	}
-
-	if (if_map_params->rid >= NSS_NLGRE_REDIR_RADIO_ID_MAX) {
-		nss_nl_error("%p: radio_id is out of valid range for vap: %d\n", nss_ctx, vap_nss_if);
-		return false;
-	}
-
-	if (if_map_params->vid >= NSS_NLGRE_REDIR_VAP_ID_MAX) {
-		nss_nl_error("%p: vap_id is out of valid range for vap: %d\n", nss_ctx, vap_nss_if);
-		return false;
-	}
-
-	if ((tun_type <= NSS_GRE_REDIR_TUNNEL_TYPE_UNKNOWN) || (tun_type >= NSS_GRE_REDIR_TUNNEL_TYPE_MAX)) {
-		nss_nl_error("%p: not a valid tunnel_type\n", nss_ctx);
-		return false;
-	}
-
-	nss_cmn_msg_init(&ngrm.cm, NSS_GRE_REDIR_INTERFACE, NSS_GRE_REDIR_TX_INTERFACE_MAP_MSG,
-			len, nss_nlgre_redir_mgr_cb_gre_msg, NULL);
-
-	ngrm.msg.interface_map.vap_nssif = vap_nss_if;
-	ngrm.msg.interface_map.radio_id = if_map_params->rid;
-	ngrm.msg.interface_map.vap_id = if_map_params->vid;
-	ngrm.msg.interface_map.tunnel_type = tun_type;
-	ngrm.msg.interface_map.ipsec_pattern = if_map_params->ipsec_sa_pattern;
-
-	if (tun_type == NSS_GRE_REDIR_TUNNEL_TYPE_SPLIT) {
-		ngrm.msg.interface_map.nexthop_nssif = NSS_ETH_RX_INTERFACE;
-	} else {
-		ngrm.msg.interface_map.nexthop_nssif = vap_nss_if;
-	}
-
-	ngrm.msg.interface_map.lag_en = 0;
-	ret = nss_gre_redir_tx_msg_sync(nss_ctx, &ngrm);
-	if (ret != NSS_TX_SUCCESS) {
-		nss_nl_error("%p: Tx to firmware failed\n", nss_ctx);
-		return false;
-	}
-
-	nss_nl_info("%p: Successfully transmitted msg to firmware\n", nss_ctx);
-	return true;
-}
-
-/*
- * nss_nlgre_redir_mgr_interface_unmap()
- *	Interface unmap message.
- */
-bool nss_nlgre_redir_mgr_interface_unmap(uint32_t vap_nss_if, struct nss_nlgre_redir_unmap *if_unmap_params)
-{
-	struct nss_gre_redir_msg ngrm;
-	struct nss_ctx_instance *nss_ctx;
-	nss_tx_status_t ret;
-	uint32_t len = sizeof(struct nss_gre_redir_msg) - sizeof(struct nss_cmn_msg);
-	nss_ctx = nss_gre_redir_get_context();
-
-	if ((vap_nss_if >= NSS_DYNAMIC_IF_START+NSS_MAX_DYNAMIC_INTERFACES) || (vap_nss_if < NSS_DYNAMIC_IF_START)) {
-		nss_nl_error("%p: vap_nss_if is out of valid range for vap: %d\n", nss_ctx, vap_nss_if);
-		return false;
-	}
-
-	if (if_unmap_params->rid >= NSS_NLGRE_REDIR_RADIO_ID_MAX) {
-		nss_nl_error("%p: radio_id is out of valid range for vap: %d\n", nss_ctx, vap_nss_if);
-		return false;
-	}
-
-	if (if_unmap_params->vid >= NSS_NLGRE_REDIR_VAP_ID_MAX) {
-		nss_nl_error("%p: vap_id is out of valid range for vap: %d\n", nss_ctx, vap_nss_if);
-		return false;
-	}
-
-	nss_cmn_msg_init(&ngrm.cm, NSS_GRE_REDIR_INTERFACE, NSS_GRE_REDIR_TX_INTERFACE_UNMAP_MSG,
-			len, nss_nlgre_redir_mgr_cb_gre_msg, NULL);
-	ngrm.msg.interface_unmap.vap_nssif = vap_nss_if;
-	ngrm.msg.interface_unmap.radio_id = if_unmap_params->rid;
-	ngrm.msg.interface_unmap.vap_id = if_unmap_params->vid;
-
-	ret = nss_gre_redir_tx_msg_sync(nss_ctx, &ngrm);
-	if (ret != NSS_TX_SUCCESS) {
-		nss_nl_error("%p: Tx to firmware failed\n", nss_ctx);
-		return false;
-	}
-
-	nss_nl_info("%p: Successfully transmitted msg to firmware\n", nss_ctx);
-	return true;
-}
-
-/*
- * nss_nlgre_redir_mgr_set_next_hop()
- *	Sets next hop as gre-redir for wifi.
- */
-bool nss_nlgre_redir_mgr_set_next_hop(struct nss_nlgre_redir_set_next *setnext_params)
-{
-	void *ctx;
-	int ifnumber, next_dev_ifnum, index;
-	struct net_device *dev, *tundev;
-	nss_tx_status_t ret;
-	struct nss_ctx_instance *nss_ctx = nss_gre_redir_get_context();
-
-	dev = dev_get_by_name(&init_net, setnext_params->dev_name);
-	if (!dev) {
-		nss_nl_error("%p: Unable to find net device corresponding to device %s\n", nss_ctx, setnext_params->dev_name);
-		return false;
-	}
-
-	ifnumber = nss_cmn_get_interface_number_by_dev(dev);
-	dev_put(dev);
-	if (ifnumber == -1) {
-		nss_nl_error("%p: Unable to find NSS interface for net device %s\n", nss_ctx, setnext_params->dev_name);
-		return false;
-	}
-
-	if (!strncmp(setnext_params->mode, "split", NSS_NLGRE_REDIR_TUN_TYPE_MAX_SZ)) {
-		next_dev_ifnum = NSS_ETH_RX_INTERFACE;
-	} else {
-		tundev = dev_get_by_name(&init_net, setnext_params->next_dev_name);
-		if (!tundev) {
-			nss_nl_error("%p: Unable to find net device corresponding to device %s\n", nss_ctx, setnext_params->next_dev_name);
-			return false;
-		}
-
-		if (!nss_nlgre_redir_mgr_get_pvt_data_index(tundev, &index)) {
-			nss_nl_error("%p: Unable to find tunnel associated with device\n", tundev);
-			dev_put(tundev);
-			return false;
-		}
-
-		dev_put(tundev);
-		next_dev_ifnum = pvt_data[index].wifi_offl_inner_ifnum;
-	}
-
-	nss_nl_info("%p: next hop interface number is %d\n", nss_ctx, next_dev_ifnum);
-	ctx = nss_wifi_get_context();
-	ret = nss_wifi_vdev_set_next_hop(ctx, ifnumber, next_dev_ifnum);
-
-	if (ret != NSS_TX_SUCCESS) {
-		nss_nl_error("%p: wifi drv api failed to set next hop\n", nss_ctx);
-		return false;
-	}
-
-	return true;
-}
-
-/*
- * nss_nlgre_redir_mgr_create_tun()
- *	Allocates net_dev and configures tunnel.
- */
-int nss_nlgre_redir_mgr_create_tun(struct nss_nlgre_redir_create_tun *create_params)
-{
-	int i, tun_idx = -1;
-	struct net_device *dev;
-	struct nss_gre_redir_ndev_priv *priv;
-	struct nss_gre_redir_inner_configure_msg ngrm;
-	struct nss_gre_redir_outer_configure_msg ngrocm;
-	nss_tx_status_t status;
-	struct nss_ctx_instance *nss_ctx = nss_gre_redir_get_context();
-
-	dev = alloc_netdev(sizeof(*priv), "gretun%d", NET_NAME_UNKNOWN, ether_setup);
-	if (!dev) {
-		nss_nl_error("Unable to allocate netdev\n");
-		return tun_idx;
-	}
-
-	dev->needed_headroom = NSS_NLGRE_REDIR_NEEDED_HEADROOM;
-	dev->netdev_ops = &gre_netdev_ops;
-	for (i = 0; i < NSS_NLGRE_REDIR_MAX_TUNNELS ; i++) {
-		if (!pvt_data[i].enable) {
-			tun_idx = i;
-			break;
-		}
-	}
-
-	if (tun_idx == -1) {
-		nss_nl_error("Unable to allocate any tunnel\n");
-		return tun_idx;
-	}
-
-	/*
-	 * Dynamic interface allocation.
-	 */
-	pvt_data[tun_idx].host_inner_ifnum = nss_gre_redir_alloc_and_register_node(dev,
-			nss_nlgre_redir_mgr_host_data_cb,
-			NULL, NSS_DYNAMIC_INTERFACE_TYPE_GRE_REDIR_WIFI_HOST_INNER, dev);
-	nss_nl_info("%p: host_inner = %d\n", nss_ctx, pvt_data[tun_idx].host_inner_ifnum);
-	if (pvt_data[tun_idx].host_inner_ifnum == -1) {
-		nss_nl_error("%p: Unable to allocate and register wifi host inner interface\n", nss_ctx);
-		goto fail;
-	}
-
-	pvt_data[tun_idx].wifi_offl_inner_ifnum = nss_gre_redir_alloc_and_register_node(dev,
-			nss_nlgre_redir_mgr_wifi_offl_data_cb,
-			NULL, NSS_DYNAMIC_INTERFACE_TYPE_GRE_REDIR_WIFI_OFFL_INNER, dev);
-	nss_nl_info("%p: wifi_inner = %d\n", nss_ctx, pvt_data[tun_idx].wifi_offl_inner_ifnum);
-	if (pvt_data[tun_idx].wifi_offl_inner_ifnum == -1) {
-		nss_nl_error("%p: Unable to allocate and register wifi offload inner interface\n", nss_ctx);
-		goto fail;
-	}
-
-	pvt_data[tun_idx].sjack_inner_ifnum = nss_gre_redir_alloc_and_register_node(dev,
-			nss_nlgre_redir_mgr_sjack_data_cb,
-			NULL, NSS_DYNAMIC_INTERFACE_TYPE_GRE_REDIR_SJACK_INNER, dev);
-	nss_nl_info("%p: sjack_inner = %d\n", nss_ctx, pvt_data[tun_idx].sjack_inner_ifnum);
-	if (pvt_data[tun_idx].sjack_inner_ifnum == -1) {
-		nss_nl_error("%p: Unable to allocate and register sjack inner interface\n", nss_ctx);
-		goto fail;
-	}
-
-	pvt_data[tun_idx].outer_ifnum = nss_gre_redir_alloc_and_register_node(dev,
-			nss_nlgre_redir_mgr_outer_data_cb,
-			NULL, NSS_DYNAMIC_INTERFACE_TYPE_GRE_REDIR_OUTER, dev);
-	nss_nl_info("%p: outer = %d\n", nss_ctx, pvt_data[tun_idx].outer_ifnum);
-	if (pvt_data[tun_idx].outer_ifnum == -1) {
-		nss_nl_error("%p: Unable to allocate and register outer interface\n", nss_ctx);
-		goto fail;
-	}
-
-	memset(&ngrm, 0, sizeof(struct nss_gre_redir_inner_configure_msg));
-	memset(&ngrocm, 0, sizeof(struct nss_gre_redir_outer_configure_msg));
-	ngrm.ip_hdr_type = create_params->iptype;
-	ngrocm.ip_hdr_type = create_params->iptype;
-
-	memcpy(ngrm.ip_src_addr, create_params->sip, sizeof(ngrm.ip_src_addr));
-	memcpy(ngrm.ip_dest_addr, create_params->dip, sizeof(ngrm.ip_dest_addr));
-
-	/*
-	 * TODO: Dynamic assignment of values from userspace
-	 * ip_df_policy value currently hard coded. This needs to be supplied from userspace.
-	 */
-	ngrm.ip_df_policy = 0;
-	ngrm.gre_version = 0;
-	ngrm.ip_ttl = 128;
-	ngrm.except_outerif = pvt_data[tun_idx].outer_ifnum;
-
-	/*
-	 * TODO: Dynamic assignment of values from userspace
-	 * rps_hint value currently hard coded. This needs to be supplied from userspace.
-	 */
-	ngrocm.rps_hint = 0;
-	ngrocm.except_hostif = pvt_data[tun_idx].host_inner_ifnum;
-	ngrocm.except_offlif = pvt_data[tun_idx].wifi_offl_inner_ifnum;
-	ngrocm.except_sjackif = pvt_data[tun_idx].sjack_inner_ifnum;
-
-	status = nss_gre_redir_configure_inner_node(pvt_data[tun_idx].host_inner_ifnum, &ngrm);
-	if (status != NSS_TX_SUCCESS) {
-		nss_nl_warn("%p: unable to configure host inner node %d\n", nss_ctx, pvt_data[tun_idx].host_inner_ifnum);
-		goto fail;
-	}
-
-	status = nss_gre_redir_configure_inner_node(pvt_data[tun_idx].wifi_offl_inner_ifnum, &ngrm);
-	if (status != NSS_TX_SUCCESS) {
-		nss_nl_warn("%p: unable to configure wifi offload inner node %d\n", nss_ctx, pvt_data[tun_idx].host_inner_ifnum);
-		goto fail;
-	}
-
-	status = nss_gre_redir_configure_inner_node(pvt_data[tun_idx].sjack_inner_ifnum, &ngrm);
-	if (status != NSS_TX_SUCCESS) {
-		nss_nl_warn("%p: unable to configure sjack inner node %d\n", nss_ctx, pvt_data[tun_idx].sjack_inner_ifnum);
-		goto fail;
-	}
-
-	status = nss_gre_redir_configure_outer_node(pvt_data[tun_idx].outer_ifnum, &ngrocm);
-	if (status != NSS_TX_SUCCESS) {
-		nss_nl_warn("%p: unable to configure outer node %d\n", nss_ctx, pvt_data[tun_idx].host_inner_ifnum);
-		goto fail;
-	}
-
-	nss_nlgre_redir_mgr_ether_setup(dev);
-	if (register_netdev(dev)) {
-		nss_nl_warn("%p: Unable to register netdev\n", nss_ctx);
-		nss_nlgre_redir_mgr_destroy_tun(dev);
-	}
-
-	pvt_data[tun_idx].enable = true;
-	pvt_data[tun_idx].dev = dev;
-
-	return tun_idx;
-fail:
-	nss_nlgre_redir_mgr_interfaces_unregister_and_dealloc(dev, tun_idx);
-	nss_nlgre_redir_mgr_deinit_pvt_data(tun_idx);
-	free_netdev(dev);
-	return -1;
-}
-
-/*
- * nss_nlgre_redir_mgr_open()
- *	Used when the interface is opened for use.
- */
-static int nss_nlgre_redir_mgr_open(struct net_device *dev)
-{
-	struct nss_gre_redir_ndev_priv *priv;
-	priv = netdev_priv(dev);
-	priv->gre_seq = 0;
-	netif_start_queue(dev);
-	netif_carrier_on(dev);
-	return 0;
-}
-
-/*
- * nss_nlgre_redir_mgr_close()
- *	Used when the interface is closed.
- */
-static int nss_nlgre_redir_mgr_close(struct net_device *dev)
-{
-	netif_stop_queue (dev);
-	netif_carrier_off(dev);
-	return 0;
-}
-
-/*
- * nss_nlgre_redir_mgr_get_stats64()
- *	Used when to get link statistics.
- */
-static struct rtnl_link_stats64 *nss_nlgre_redir_mgr_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
-{
-	bool ret = false;
-	int i;
-	struct nss_gre_redir_tunnel_stats get_stats;
-
-	for (i = 0; i < NSS_GRE_REDIR_MAX_INTERFACES; i++) {
-		ret = nss_gre_redir_get_stats(i, &get_stats);
-		if (ret == true && get_stats.dev == dev) {
-			break;
-		}
-	}
-	if (ret == false)
-		return stats;
-
-	stats->tx_bytes   = get_stats.node_stats.tx_bytes;
-	stats->tx_packets = get_stats.node_stats.tx_packets;
-	stats->rx_bytes   = get_stats.node_stats.rx_bytes;
-	stats->rx_packets = get_stats.node_stats.rx_packets;
-	for (i = 0;i < ARRAY_SIZE(get_stats.node_stats.rx_dropped); i++) {
-		stats->rx_dropped += get_stats.node_stats.rx_dropped[i];
-	}
-
-	stats->tx_dropped = get_stats.tx_dropped;
-
-	return stats;
-}
-
-/*
- * nss_nlgre_redir_mgr_set_mac_address()
- *	Sets the MAC address.
- */
-static int nss_nlgre_redir_mgr_set_mac_address(struct net_device *dev, void *p)
-{
-	struct sockaddr *addr = (struct sockaddr *)p;
-
-	if (!is_valid_ether_addr(addr->sa_data)) {
-		nss_nl_error("MAC address validation failed \n");
-		return -EINVAL;
-	}
-
-	memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
-	return 0;
-}
-
-/*
- * net_device_ops
- *	Netdevice operations.
- */
-static const struct net_device_ops gre_netdev_ops = {
-	.ndo_open = nss_nlgre_redir_mgr_open,
-	.ndo_stop = nss_nlgre_redir_mgr_close,
-	.ndo_start_xmit = nss_nlgre_redir_mgr_xmit,
-	.ndo_get_stats64 = nss_nlgre_redir_mgr_get_stats64,
-	.ndo_set_mac_address = nss_nlgre_redir_mgr_set_mac_address,
-};
-
-MODULE_DESCRIPTION("NSS GRE_REDIR module");
diff --git a/netlink/nss_nlgre_redir_mgr.h b/netlink/nss_nlgre_redir_mgr.h
deleted file mode 100644
index 076998b..0000000
--- a/netlink/nss_nlgre_redir_mgr.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- ***************************************************************************
- * Copyright (c) 2014-2015,2019, 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_nlgre_redir_mgr_interface_map()
- * 	Interface map message.
- */
-bool nss_nlgre_redir_mgr_interface_map(uint32_t vap_nssif, uint32_t tun_type, struct nss_nlgre_redir_map *interface_map_params);
-
-/*
- * nss_nlgre_redir_mgr_interface_unmap()
- * 	Interface unmap message.
- */
-bool nss_nlgre_redir_mgr_interface_unmap(uint32_t vap_nssif, struct nss_nlgre_redir_unmap *interface_unmap_params);
-
-/*
- * nss_nlgre_redir_mgr_set_next_hop()
- * 	Sets next hop as gre-redir for wifi.
- */
-bool nss_nlgre_redir_mgr_set_next_hop(struct nss_nlgre_redir_set_next *interface_setnext_params);
-
-/*
- * nss_nlgre_redir_mgr_create_tun()
- * 	Allocates net_dev and configures tunnel.
- */
-int nss_nlgre_redir_mgr_create_tun(struct nss_nlgre_redir_create_tun *create_params);
-
-/*
- * nss_nlgre_redir_mgr_destroy_tun()
- * 	Unregisters and deallocs dynamic interfaces.
- */
-bool nss_nlgre_redir_mgr_destroy_tun(struct net_device *dev);
-
-/*
- * nss_gre_redir_tunnel_types nss_nlgre_redir_mgr_tunnel_type()
- * 	Returns the tunnel type
- */
-enum nss_gre_redir_tunnel_types nss_nlgre_redir_mgr_tunnel_type(char *tun_type);
diff --git a/netlink/nss_nlipv4.c b/netlink/nss_nlipv4.c
index 02819a7..e102a15 100644
--- a/netlink/nss_nlipv4.c
+++ b/netlink/nss_nlipv4.c
@@ -51,7 +51,7 @@
 #include "nss_ipsecmgr.h"
 #include "nss_nl.h"
 #include "nss_nlcmn_if.h"
-#include "nss_nlgre_redir.h"
+#include "nss_nlgre_redir_cmd.h"
 #include "nss_nlipsec_if.h"
 #include "nss_nlipsec.h"
 #include "nss_nlipv4_if.h"
@@ -326,7 +326,7 @@
 		/*
 		 * Currently this implementation is only for gre_redir
 		 */
-		conn->flow_interface_num = nss_nlgre_redir_get_ifnum(flow_dev,
+		conn->flow_interface_num = nss_nlgre_redir_cmd_get_ifnum(flow_dev,
 					NSS_DYNAMIC_INTERFACE_TYPE_GRE_REDIR_OUTER);
 		if (conn->flow_interface_num < 0 ) {
 			nss_nl_error("%p: Failed to get flow interface number (dev:%s, type:%d)\n",
@@ -384,7 +384,7 @@
 		break;
 
 	case NSS_NL_IFTYPE_TUNNEL_GRE:
-		conn->return_interface_num = nss_nlgre_redir_get_ifnum(return_dev,
+		conn->return_interface_num = nss_nlgre_redir_cmd_get_ifnum(return_dev,
 				NSS_DYNAMIC_INTERFACE_TYPE_GRE_REDIR_WIFI_OFFL_INNER);
 		if (conn->return_interface_num < 0 ) {
 			nss_nl_error("%p: Failed to get return interface number (dev:%s, type:%d)\n",
diff --git a/netlink/nss_nlipv6.c b/netlink/nss_nlipv6.c
index 0fe5a97..c673832 100644
--- a/netlink/nss_nlipv6.c
+++ b/netlink/nss_nlipv6.c
@@ -57,7 +57,7 @@
 #include "nss_nlipv6_if.h"
 #include "nss_ipsecmgr.h"
 #include "nss_nlipsec_if.h"
-#include "nss_nlgre_redir.h"
+#include "nss_nlgre_redir_cmd.h"
 
 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0))
 #define DST_NEIGH_LOOKUP(dst, ip_addr) dst_neigh_lookup(dst, ip_addr)
@@ -337,7 +337,7 @@
 		break;
 
 	case NSS_NL_IFTYPE_TUNNEL_GRE:
-		conn->flow_interface_num = nss_nlgre_redir_get_ifnum(flow_dev, NSS_DYNAMIC_INTERFACE_TYPE_GRE_REDIR_OUTER);
+		conn->flow_interface_num = nss_nlgre_redir_cmd_get_ifnum(flow_dev, NSS_DYNAMIC_INTERFACE_TYPE_GRE_REDIR_OUTER);
 		if (conn->flow_interface_num < 0 ) {
 			nss_nl_error("%p: Failed to get flow interface number (dev:%s, type:%d)\n",
 			flow_dev, flow_dev->name, flow_iftype);
@@ -395,7 +395,7 @@
 		break;
 
 	case NSS_NL_IFTYPE_TUNNEL_GRE:
-		conn->return_interface_num = nss_nlgre_redir_get_ifnum(return_dev,
+		conn->return_interface_num = nss_nlgre_redir_cmd_get_ifnum(return_dev,
 		NSS_DYNAMIC_INTERFACE_TYPE_GRE_REDIR_WIFI_OFFL_INNER);
 		if (conn->return_interface_num < 0 ) {
 			nss_nl_error("%p: Failed to get return interface number (dev:%s, type:%d)\n",