[qca-nss-clients] Client module for gre_redir family

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

Signed-off-by: Himanshu Joshi <himajosh@codeaurora.org>
Change-Id: I55728266da7b486ddd7187e7ce5d80acfb77310b
diff --git a/netlink/Makefile b/netlink/Makefile
index 2912bac..ecdba8c 100644
--- a/netlink/Makefile
+++ b/netlink/Makefile
@@ -8,8 +8,11 @@
 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
 
 qca-nss-netlink-objs := nss_nl.o
+qca-nss-netlink-objs += nss_nlgre_redir.o
+qca-nss-netlink-objs += nss_nlgre_redir_mgr.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_nlcmn_if.h b/netlink/include/nss_nlcmn_if.h
index 0313390..97d9869 100644
--- a/netlink/include/nss_nlcmn_if.h
+++ b/netlink/include/nss_nlcmn_if.h
@@ -1,6 +1,6 @@
 /*
  **************************************************************************
- * Copyright (c) 2015,2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015,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.
@@ -28,14 +28,11 @@
  */
 struct nss_nlcmn {
 	uint32_t version;			/**< message version */
-
 	uint32_t pid;				/**< process ID for the message */
 	nss_ptr_t sock_data;			/**< socket specific info, used by kernel */
-
 	uint16_t cmd_len;			/**< command len */
 	uint8_t cmd_type;			/**< command type */
 	uint8_t res;				/**< reserve for future use */
-
 	int32_t cb_owner;			/**< CB identifier */
 	uint8_t cb_data[NSS_NLCMN_CB_MAX_SZ]; 	/**< user context buffer */
 };
diff --git a/netlink/include/nss_nlgre_redir_if.h b/netlink/include/nss_nlgre_redir_if.h
new file mode 100644
index 0000000..1b8a376
--- /dev/null
+++ b/netlink/include/nss_nlgre_redir_if.h
@@ -0,0 +1,146 @@
+/*
+ **************************************************************************
+ * Copyright (c) 2015,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.
+ **************************************************************************
+ */
+
+#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.
+ */
+enum nss_nlgre_redir_cmd_type {
+	NSS_NLGRE_REDIR_CMD_TYPE_UNKNOWN,		/**< Unknown command type */
+	NSS_NLGRE_REDIR_CMD_TYPE_CREATE_TUN,		/**< Create tunnel */
+	NSS_NLGRE_REDIR_CMD_TYPE_DESTROY_TUN,		/**< Destroy tunnel */
+	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_MAX,			/**< Max number of commands */
+};
+
+/**
+ * @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 */
+};
+
+/**
+ * @brief parameters useful for destroying the tunnel
+ */
+struct nss_nlgre_redir_destroy_tun {
+	char netdev[IFNAMSIZ];				/**< Dev to be destroyed */
+};
+
+/**
+ * @brief parameters to create interface map message.
+ */
+struct nss_nlgre_redir_map {
+	uint32_t ipsec_sa_pattern;				/**< Ipsec association parameters */
+	uint16_t rid;						/**< Radio id */
+	uint16_t vid;						/**< Vap id */
+	char vap_nss_if[IFNAMSIZ];				/**< Vap interface name */
+	char tun_type[NSS_NLGRE_REDIR_TUN_TYPE_MAX_SZ];		/**< Tunnel type{tun, dtun, split} */
+};
+
+/**
+ * @brief parameters used to unmap the device
+ */
+struct nss_nlgre_redir_unmap {
+	char vap_nss_if[IFNAMSIZ];			/**< Dev name to be unmapped */
+	uint16_t rid;					/**< Radio id */
+	uint16_t vid;					/**< Vap id */
+	uint8_t res[2];					/**< Reserve for padding purpose */
+};
+
+/**
+ * @brief parameters used to set the next hop
+ */
+struct nss_nlgre_redir_set_next {
+	char dev_name[IFNAMSIZ];			/**< Dev whose next hop to be set */
+	char next_dev_name[IFNAMSIZ];			/**< Dev set as the set next */
+	char mode[NSS_NLGRE_REDIR_MODE_MAX_SZ];		/**< Split or wifi */
+	uint8_t res[2];					/**< Reserve for padding purpose */
+};
+
+/**
+ * @brief gre_redir message
+ */
+struct nss_nlgre_redir_rule {
+	struct nss_nlcmn cm;				/**< Common message header */
+
+	/**
+	 * @brief payload of an GRE_REDIR netlink msg
+	 */
+	union {
+		struct nss_nlgre_redir_create_tun create;	/**< Create tunnel */
+		struct nss_nlgre_redir_destroy_tun destroy;	/**< Destroy tunnel */
+		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 */
+	} msg;
+};
+
+/**
+ * @brief NETLINK gre_redir message init
+ * @param rule[IN] NSS NETLINK gre_redir message
+ * @param type[IN] gre_redir message type
+ */
+static inline void nss_nlgre_redir_rule_init(struct nss_nlgre_redir_rule *rule, enum nss_nlgre_redir_cmd_type type)
+{
+	nss_nlcmn_set_ver(&rule->cm, NSS_NL_VER);
+	nss_nlcmn_init_cmd(&rule->cm, sizeof(struct nss_nlgre_redir_rule), type);
+}
+
+/**@}*/
+#endif /* __NSS_NLGRE_REDIR_IF_H */
diff --git a/netlink/nss_nl.c b/netlink/nss_nl.c
index 45c06ee..f8e8e6d 100644
--- a/netlink/nss_nl.c
+++ b/netlink/nss_nl.c
@@ -1,6 +1,6 @@
 /*
  **************************************************************************
- * Copyright (c) 2015-2016,2018 The Linux Foundation. All rights reserved.
+ * 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.
@@ -13,6 +13,7 @@
  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  **************************************************************************
  */
+
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
@@ -31,6 +32,8 @@
 #include "nss_nlipv4_if.h"
 #include "nss_nlipv6_if.h"
 #include "nss_nlipsec_if.h"
+#include "nss_nlgre_redir_if.h"
+#include "nss_nlgre_redir.h"
 #include "nss_nloam_if.h"
 #if defined (CONFIG_NSS_NLCRYPTO)
 #include "nss_nlcrypto_if.h"
@@ -121,6 +124,15 @@
 		.exit = NSS_NLIPV6_EXIT,		/* exit */
 		.valid = CONFIG_NSS_NLIPV6		/* 1 or 0 */
 	},
+	{
+		/*
+		 * 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 */
+	},
 
 };
 
diff --git a/netlink/nss_nlgre_redir.c b/netlink/nss_nlgre_redir.c
new file mode 100644
index 0000000..5f3e296
--- /dev/null
+++ b/netlink/nss_nlgre_redir.c
@@ -0,0 +1,371 @@
+/*
+ **************************************************************************
+ * 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_mgr.h"
+
+/*
+ * Get max size of the array
+ */
+#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)
+{
+	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_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;
+	}
+
+	/*
+	 * 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;
+	}
+
+	nss_nl_info("Successfully destroyed gretun = %s tunnel\n", nl_rule->msg.destroy.netdev);
+fail:
+	return ret;
+}
+
+/*
+ * nss_nlgre_redir_ops_map()
+ * 	Handler for map command
+ */
+static int nss_nlgre_redir_ops_map(struct sk_buff *skb, struct genl_info *info)
+{
+	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;
+	}
+
+	/*
+	 * 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);
+	if (!dev) {
+		nss_nl_error("Invalid parameter: vap_nss_if\n");
+		ret = -ENODEV;
+		goto fail;
+	}
+
+	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_nlgre_redir_ops_unmap()
+ * 	Handler for unmap command
+ */
+static int nss_nlgre_redir_ops_unmap(struct sk_buff *skb, struct genl_info *info)
+{
+	struct nss_nlgre_redir_rule *nl_rule;
+	struct nss_nlcmn *nl_cm;
+	struct net_device *dev;
+	int32_t vap_nss_if;
+	int ret = 0;
+
+	/*
+	 * 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;
+	}
+
+	/*
+	 * 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.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);
+		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
+	 */
+	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;
+	}
+
+	return true;
+}
+
+/*
+ * nss_nlgre_redir_exit()
+ *	handler exit
+ */
+bool nss_nlgre_redir_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_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.h b/netlink/nss_nlgre_redir.h
new file mode 100644
index 0000000..ac42019
--- /dev/null
+++ b/netlink/nss_nlgre_redir.h
@@ -0,0 +1,50 @@
+/*
+ **************************************************************************
+ * 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.h
+ *	NSS Netlink gre_redir API definitions
+ */
+#ifndef __NSS_NLGRE_REDIR_H
+#define __NSS_NLGRE_REDIR_H
+
+/*
+ * 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);
+
+/*
+ * nss_nlgre_redir_init()
+ * 	To initialize the gre_redir module
+ */
+bool nss_nlgre_redir_init(void);
+
+/*
+ * nss_nlgre_redir_exit()
+ * 	Exit the gre_redir module
+ */
+bool nss_nlgre_redir_exit(void);
+
+#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 */
+
+#endif /* __NSS_NLGRE_REDIR_H */
diff --git a/netlink/nss_nlgre_redir_mgr.c b/netlink/nss_nlgre_redir_mgr.c
new file mode 100644
index 0000000..72ccd7d
--- /dev/null
+++ b/netlink/nss_nlgre_redir_mgr.c
@@ -0,0 +1,728 @@
+/*
+ ***************************************************************************
+ * 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
new file mode 100644
index 0000000..076998b
--- /dev/null
+++ b/netlink/nss_nlgre_redir_mgr.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_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 d1cf730..02819a7 100644
--- a/netlink/nss_nlipv4.c
+++ b/netlink/nss_nlipv4.c
@@ -36,25 +36,25 @@
 #include <linux/semaphore.h>
 #include <linux/in.h>
 
-#include <net/genetlink.h>
-#include <net/route.h>
 #include <net/arp.h>
-#include <net/neighbour.h>
 #include <net/genetlink.h>
+#include <net/neighbour.h>
 #include <net/net_namespace.h>
+#include <net/route.h>
 #include <net/sock.h>
 
 #include <nss_api_if.h>
 #include <nss_cmn.h>
 #include <nss_nl_if.h>
-#include "nss_nl.h"
-#include "nss_nlcmn_if.h"
-#include "nss_nlipv4_if.h"
-#include "nss_nlipsec.h"
 #include "nss_ipsec.h"
 #include "nss_ipsec_cmn.h"
 #include "nss_ipsecmgr.h"
+#include "nss_nl.h"
+#include "nss_nlcmn_if.h"
+#include "nss_nlgre_redir.h"
 #include "nss_nlipsec_if.h"
+#include "nss_nlipsec.h"
+#include "nss_nlipv4_if.h"
 
 /*
  * NSS NETLINK IPv4 context
@@ -322,6 +322,21 @@
 							tuple->return_ident, tuple->flow_ident);
 		break;
 
+	case NSS_NL_IFTYPE_TUNNEL_GRE:
+		/*
+		 * Currently this implementation is only for gre_redir
+		 */
+		conn->flow_interface_num = nss_nlgre_redir_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);
+			return -EINVAL;
+		}
+
+		conn->flow_mtu = NSS_NLIPV4_MAX_MTU;
+		break;
+
 	case NSS_NL_IFTYPE_VLAN:
 		conn->flow_interface_num = nss_cmn_get_interface_number_by_dev(vlan_dev_real_dev(flow_dev));
 		if (conn->flow_interface_num < 0 ) {
@@ -368,6 +383,18 @@
 							tuple->return_ident, tuple->flow_ident);
 		break;
 
+	case NSS_NL_IFTYPE_TUNNEL_GRE:
+		conn->return_interface_num = nss_nlgre_redir_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",
+							return_dev, return_dev->name, return_iftype);
+			return -EINVAL;
+		}
+
+		conn->return_mtu = NSS_NLIPV4_MAX_MTU;
+		break;
+
 	case NSS_NL_IFTYPE_VLAN:
 		conn->return_interface_num = nss_cmn_get_interface_number_by_dev(vlan_dev_real_dev(return_dev));
 		if (conn->return_interface_num < 0 ) {
diff --git a/netlink/nss_nlipv6.c b/netlink/nss_nlipv6.c
index 1d49bce..0fe5a97 100644
--- a/netlink/nss_nlipv6.c
+++ b/netlink/nss_nlipv6.c
@@ -57,6 +57,7 @@
 #include "nss_nlipv6_if.h"
 #include "nss_ipsecmgr.h"
 #include "nss_nlipsec_if.h"
+#include "nss_nlgre_redir.h"
 
 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0))
 #define DST_NEIGH_LOOKUP(dst, ip_addr) dst_neigh_lookup(dst, ip_addr)
@@ -335,6 +336,17 @@
 							tuple->return_ident, tuple->flow_ident);
 		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);
+		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);
+			return -EINVAL;
+		}
+
+		conn->flow_mtu = NSS_NLIPV6_MAX_MTU;
+		break;
+
 	case NSS_NL_IFTYPE_VLAN:
 		conn->flow_interface_num = nss_cmn_get_interface_number_by_dev(vlan_dev_real_dev(flow_dev));
 		if (conn->flow_interface_num < 0 ) {
@@ -382,6 +394,18 @@
 							tuple->return_ident, tuple->flow_ident);
 		break;
 
+	case NSS_NL_IFTYPE_TUNNEL_GRE:
+		conn->return_interface_num = nss_nlgre_redir_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",
+			return_dev, return_dev->name, return_iftype);
+			return -EINVAL;
+		}
+
+		conn->return_mtu = NSS_NLIPV6_MAX_MTU;
+		break;
+
 	case NSS_NL_IFTYPE_VLAN:
 		conn->return_interface_num = nss_cmn_get_interface_number_by_dev(vlan_dev_real_dev(return_dev));
 		if (conn->return_interface_num < 0 ) {