Merge "[qca-nss-clients] Avoid fragmentation for wan to lan direction"
diff --git a/netlink/Makefile b/netlink/Makefile
index 3072316..1a73c1c 100644
--- a/netlink/Makefile
+++ b/netlink/Makefile
@@ -20,8 +20,23 @@
 qca-nss-netlink-objs += nss_nlipv6.o
 qca-nss-netlink-objs += nss_nlipsec.o
 qca-nss-netlink-objs += nss_nloam.o
-obj-m += qca-nss-netlink.o
 
+CAPWAP_ENABLED:=CONFIG_PACKAGE_kmod-qca-nss-drv-capwapmgr=y
+CAPWAP_CONFIG:=$(shell  grep $(CAPWAP_ENABLED) $(TOPDIR)/.config)
+ifeq ($(CAPWAP_CONFIG),$(CAPWAP_ENABLED))
+ccflags-y += -DCONFIG_NSS_NLCAPWAP=1
+qca-nss-netlink-objs += nss_nlcapwap.o
+else
+ccflags-y += -DCONFIG_NSS_NLCAPWAP=0
+endif
+DTLS_ENABLED:=CONFIG_PACKAGE_kmod-qca-nss-drv-dtlsmgr=y
+DTLS_CONFIG:=$(shell  grep $(DTLS_ENABLED) $(TOPDIR)/.config)
+ifeq ($(DTLS_CONFIG),$(DTLS_ENABLED))
+ccflags-y += -DCONFIG_NSS_NLDTLS=1
+qca-nss-netlink-objs += nss_nldtls.o
+else
+ccflags-y += -DCONFIG_NSS_NLDTLS=0
+endif
 ifeq ($(SoC),$(filter $(SoC),ipq807x ipq807x_64 ipq60xx ipq60xx_64))
 ccflags-y += -DCONFIG_NSS_NLCRYPTOV2=1
 qca-nss-netlink-objs += nss_nlcryptov2.o
@@ -29,3 +44,4 @@
 ccflags-y += -DCONFIG_NSS_NLCRYPTO=1
 qca-nss-netlink-objs += nss_nlcrypto.o
 endif
+obj-m += qca-nss-netlink.o
diff --git a/netlink/include/nss_nlcapwap_if.h b/netlink/include/nss_nlcapwap_if.h
new file mode 100644
index 0000000..fddd664
--- /dev/null
+++ b/netlink/include/nss_nlcapwap_if.h
@@ -0,0 +1,182 @@
+/*
+ **************************************************************************
+ * Copyright (c) 2015,2018-2020 The Linux Foundation. All rights reserved.
+ * Permission to use, copy, modify, and/or distribute this software for
+ * any purpose with or without fee is hereby granted, provided that the
+ * above copyright notice and this permission notice appear in all copies.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ **************************************************************************
+ */
+
+/*
+ * @file nss_nlcapwap_if.h
+ *	NSS Netlink CAPWAP headers
+ */
+#ifndef __NSS_NLCAPWAP_IF_H
+#define __NSS_NLCAPWAP_IF_H
+
+#include <nss_nlcmn_if.h>
+
+/**
+ * Capwap family definitions
+ */
+#define NSS_NLCAPWAP_META_HEADER_SZ 32
+#define NSS_NLCAPWAP_FAMILY "nss_nlcapwap"
+#define NSS_NLCAPWAP_MCAST_GRP "nss_nlcapwap_mc"
+#define NSS_NLCAPWAP_MODE_MAX_SZ 16
+#define NSS_NLCAPWAP_PROTO_MAX_SZ 16
+#define NSS_NLCAPWAP_KEY_SZ 32
+
+/**
+ * @brief Enumeration for capwap mode
+ */
+enum nss_nlcapwap_mode {
+	NSS_NLCAPWAP_MODE_UNKNOWN,		/**< Unknown mode for capwap */
+	NSS_NLCAPWAP_MODE_RX,			/**< Capwap tunnel operating in rx mode */
+	NSS_NLCAPWAP_MODE_TX,			/**< Capwap tunnel operating in tx mode */
+	NSS_NLCAPWAP_MODE_MAX			/**< Max number of capwap mode */
+};
+
+/**
+ * @brief Enumeration for all command types.
+ */
+enum nss_nlcapwap_cmd_type {
+	NSS_NLCAPWAP_CMD_TYPE_UNKNOWN,			/**< Unknown command type */
+	NSS_NLCAPWAP_CMD_TYPE_CREATE_TUN,		/**< Creates a tunnel. */
+	NSS_NLCAPWAP_CMD_TYPE_DESTROY_TUN,		/**< Destroys the tunnel created. */
+	NSS_NLCAPWAP_CMD_TYPE_UPDATE_MTU,		/**< Updates the mtu of the path. */
+	NSS_NLCAPWAP_CMD_TYPE_PERF,			/**< Enables or disables performance for capwap. */
+	NSS_NLCAPWAP_CMD_TYPE_DTLS,			/**< Enables or disables dtls for capwap tunnel. */
+	NSS_NLCAPWAP_CMD_TYPE_TX_PACKETS,		/**< Helps in configuring parameters for sending traffic. */
+	NSS_NLCAPWAP_CMD_TYPE_META_HEADER,		/**< Creates a meta header for capwap. */
+	NSS_NLCAPWAP_CMD_TYPE_IP_FLOW,			/**< To add or delete an ip flow for capwap. */
+	NSS_NLCAPWAP_CMD_TYPE_MAX			/**< Max number of commands type. */
+};
+
+/**
+ * @brief Enumeration for ip flow type.
+ */
+enum nss_nlcapwap_ip_flow_mode {
+	NSS_NLCAPWAP_IP_FLOW_MODE_UNKNOWN,		/**< To add ip flow rule */
+	NSS_NLCAPWAP_IP_FLOW_MODE_ADD,			/**< To add ip flow rule */
+	NSS_NLCAPWAP_IP_FLOW_MODE_DEL,			/**< To delelte ip flow rule */
+	NSS_NLCAPWAP_IP_FLOW_MODE_MAX			/**< To delelte ip flow rule */
+};
+
+/**
+ * @brief Parameters for creating tunnel
+ */
+struct nss_nlcapwap_create_tun {
+	enum nss_nlcapwap_mode mode;		/**< Tx or rx mode */
+	struct nss_capwap_rule_msg rule;	/**< Rule to add capwap tunnel */
+	char gmac_ifname[IFNAMSIZ];		/**< WAN interface name */
+	bool inner_trustsec_en;			/**< Inner trustsec is enabled */
+	bool outer_trustsec_en;			/**< Outer trustsec is enabled */
+	bool wireless_qos_en;			/**< Wireless qos is enabled */
+	uint8_t gmac_ifmac[ETH_ALEN];		/**< Mac address of the wan interface */
+	uint8_t bssid[ETH_ALEN];		/**< BSSID of the AP */
+	uint8_t vlan_config;			/**< Vlan is configured */
+	uint8_t pppoe_config;			/**< PPPOE is configured */
+	uint8_t csum_enable;			/**< Udp lite is configured */
+};
+
+/**
+ * @brief parameters useful for destroying the tunnel
+ */
+struct nss_nlcapwap_destroy_tun {
+	uint8_t tun_id;			/**< Tunnel associated with the netdev */
+};
+
+/**
+ * @brief parameters useful for updating the mtu of tunnel
+ */
+struct nss_nlcapwap_update_mtu {
+	struct nss_capwap_path_mtu_msg mtu;	/** Update mtu params */
+	uint8_t tun_id;				/**< Tunnel associated with the netdev */
+};
+
+/**
+ * @brief parameters useful for enabling or disabling dtls
+ */
+struct nss_nlcapwap_dtls {
+	uint32_t flags;				/**< DTLS header flags. */
+	void *app_data;				/**< Opaque data returned in callback. */
+	uint16_t tun_id;			/**< Tunnel for which dtls to be enabled. */
+	bool enable_dtls;			/**< Enables or disables dtls for capwap tunnel. */
+	uint8_t ip_version;			/**< Version of IP address */
+	struct nss_dtlsmgr_encap_config encap;	/**< Encap data. */
+	struct nss_dtlsmgr_decap_config decap;	/**< Decap data. */
+};
+
+/**
+ * @brief parameters useful for enabling or disabling performance
+ */
+struct nss_nlcapwap_perf {
+	bool perf_en;			/**< Enables or disables performance for capwap tunnel */
+};
+
+/**
+ * @brief parameters to send traffic.
+ */
+struct nss_nlcapwap_tx_packets {
+	uint32_t pkt_size;				/**< Packet size */
+	uint32_t num_of_packets;			/**< Number of packets to be transmitted */
+};
+
+/**
+ * @brief parameters used to add or delete IP flow
+ */
+struct nss_nlcapwap_ip_flow {
+	enum nss_nlcapwap_ip_flow_mode ip_flow_mode;	/**< Add or delete flow */
+	struct nss_capwap_flow_rule_msg flow;		/**< IP flow rule */
+	uint16_t tun_id;				/**< Tunnel for which the flow is added */
+};
+
+/**
+ * @brief parameters used to create meta header
+ */
+struct nss_nlcapwap_meta_header {
+	uint8_t meta_header_blob[NSS_NLCAPWAP_META_HEADER_SZ];	/**< Binary blob of meta header. */
+	uint16_t type;						/**< Type of meta header. */
+};
+
+/**
+ * @brief capwap message
+ */
+struct nss_nlcapwap_rule {
+	struct nss_nlcmn cm;		/**< Common message header */
+
+	/**
+	 * @brief payload of an CAPWAP netlink msg
+	 */
+	union {
+		struct nss_nlcapwap_create_tun create;		/**< Create tunnel */
+		struct nss_nlcapwap_destroy_tun destroy;	/**< Destroy tunnel */
+		struct nss_nlcapwap_dtls dtls;			/**< Enables/creates or disables/destroys dtls tunnel. */
+		struct nss_nlcapwap_perf perf;			/**< Enable or disables performance. */
+		struct nss_nlcapwap_tx_packets tx_packets;	/**< Send traffic from host_to_host or end_to_end*/
+		struct nss_nlcapwap_meta_header meta_header;	/**< Creates meta header */
+		struct nss_nlcapwap_ip_flow ip_flow;		/**< Add or delete ip flow rules */
+		struct nss_nlcapwap_update_mtu update_mtu;	/**< Update mtu of the path */
+	} msg;
+};
+
+/**
+ * @brief NETLINK capwap message init
+ * @param rule[IN] NSS NETLINK capwap rule
+ * @param type[IN] capwap cmd type
+ */
+static inline void nss_nlcapwap_rule_init(struct nss_nlcapwap_rule *rule, enum nss_nlcapwap_cmd_type type)
+{
+	nss_nlcmn_set_ver(&rule->cm, NSS_NL_VER);
+	nss_nlcmn_init_cmd(&rule->cm, sizeof(struct nss_nlcapwap_rule), type);
+}
+
+/**@}*/
+#endif /* __NSS_NLCAPWAP_IF_H */
diff --git a/netlink/include/nss_nldtls_if.h b/netlink/include/nss_nldtls_if.h
new file mode 100644
index 0000000..aedb137
--- /dev/null
+++ b/netlink/include/nss_nldtls_if.h
@@ -0,0 +1,127 @@
+/*
+ **************************************************************************
+ * Copyright (c) 2015,2018-2020 The Linux Foundation. All rights reserved.
+ * Permission to use, copy, modify, and/or distribute this software for
+ * any purpose with or without fee is hereby granted, provided that the
+ * above copyright notice and this permission notice appear in all copies.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ **************************************************************************
+ */
+
+/*
+ * @file nss_nldtls_if.h
+ *	NSS Netlink Dtls headers
+ */
+#ifndef __NSS_NLDTLS_IF_H
+#define __NSS_NLDTLS_IF_H
+
+#include <nss_nlcmn_if.h>
+
+/**
+ * @brief Dtls definitions
+ */
+#define NSS_NLDTLS_FAMILY "nss_nldtls"
+#define NSS_NLDTLS_MCAST_GRP "nss_nldtls_mc"
+#define NSS_NLDTLS_MODE_MAX_SZ 16
+#define NSS_NLDTLS_PROTO_MAX_SZ 16
+#define NSS_NLDTLS_ENCAP_SIDE 0
+#define NSS_NLDTLS_DECAP_SIDE 1
+#define NSS_NLDTLS_TX_PKTS_MODE_END_TO_END 0
+#define NSS_NLDTLS_TX_PKTS_MODE_HOST_TO_HOST 1
+#define NSS_NLDTLS_CKD_MAX 32
+#define NSS_NLDTLS_AKD_MAX 32
+#define NSS_NLDTLS_ND_MAX 20
+
+/**
+ * @brief Enumeration for all command types.
+ */
+enum nss_nldtls_cmd_type {
+	NSS_NLDTLS_CMD_TYPE_UNKNOWN,			/**< Unknown command type */
+	NSS_NLDTLS_CMD_TYPE_CREATE_TUN,			/**< Creates a tunnel. */
+	NSS_NLDTLS_CMD_TYPE_DESTROY_TUN,		/**< Destroys the tunnel created. */
+	NSS_NLDTLS_CMD_TYPE_UPDATE_CONFIG,		/**< Updates the configuration of the dtls tunnel. */
+	NSS_NLDTLS_CMD_TYPE_TX_PKTS,			/**< Helps in configuring parameters for sending traffic. */
+	NSS_NLDTLS_CMD_TYPE_MAX				/**< Max number of commands type. */
+};
+
+/**
+ * @brief Parameters to create a tunnel.
+ */
+struct nss_nldtls_create_tun {
+	struct nss_dtlsmgr_encap_config encap;	/**< Encap data. */
+	struct nss_dtlsmgr_decap_config decap;	/**< Decap data. */
+	uint32_t flags;				/**< DTLS header flags. */
+	uint32_t from_mtu;			/**< Mtu of incoming interface. */
+	uint32_t to_mtu;			/**< Mtu of outgoing interface. */
+	uint8_t ip_version;			/**< Version of IP address */
+	char gmac_ifname[IFNAMSIZ];		/**< WAN interface name */
+	uint8_t gmac_ifmac[ETH_ALEN];		/**< Mac address of the wan interface */
+	uint8_t dir;				/**< Encap or decap side. */
+};
+
+/**
+ * @brief parameters useful for destroying the tunnel
+ */
+struct nss_nldtls_destroy_tun {
+	char dev_name[IFNAMSIZ];	/**< Device name associated with tunnel */
+};
+
+/**
+ * @brief parameters useful for updating the tunnel configuration
+ */
+struct nss_nldtls_update_config {
+	struct nss_dtlsmgr_config_update config_update;		/**< Update config params */
+	uint16_t epoch;						/**< Dtls encap epoch. */
+	uint16_t window_sz;					/**< Dtls window size parameter. */
+	char dev_name[IFNAMSIZ];				/**< Device whose config to be updated. */
+	uint8_t dir;						/**< Encap or decap side. */
+};
+
+/**
+ * @brief parameters to send traffic.
+ */
+struct nss_nldtls_tx_pkts {
+	uint32_t num_pkts;		/**< Number of packets to be transmitted */
+	uint16_t pkt_sz;		/**< Size of packet to be transmitted */
+	char dev_name[IFNAMSIZ];	/**< Device used for transmission */
+	uint8_t ip_version;		/**< Ip version [4 or 6] */
+	uint8_t mode;			/**< Can be end_to_end or host_to_host*/
+	bool log_en;			/**< Enable or disable wireless info */
+};
+
+/**
+ * @brief dtls message
+ */
+struct nss_nldtls_rule {
+	struct nss_nlcmn cm;		/**< Common message header */
+
+	/**
+	 * @brief payload of an DTLS netlink msg
+	 */
+	union {
+		struct nss_nldtls_create_tun create;			/**< Create tunnel */
+		struct nss_nldtls_destroy_tun destroy;			/**< Destroy tunnel */
+		struct nss_nldtls_tx_pkts tx_pkts;			/**< Send traffic*/
+		struct nss_nldtls_update_config update_config;		/**< Update configuration*/
+	} msg;
+};
+
+/**
+ * @brief NETLINK dtls rule init
+ * @param rule[IN] NSS NETLINK dtls rule
+ * @param type[IN] dtls command type
+ */
+static inline void nss_nldtls_rule_init(struct nss_nldtls_rule *rule, enum nss_nldtls_cmd_type type)
+{
+	nss_nlcmn_set_ver(&rule->cm, NSS_NL_VER);
+	nss_nlcmn_init_cmd(&rule->cm, sizeof(struct nss_nldtls_rule), type);
+}
+
+/**@}*/
+#endif /* __NSS_NLDTLS_IF_H */
diff --git a/netlink/nss_nl.c b/netlink/nss_nl.c
index 6e697ca..d67e563 100644
--- a/netlink/nss_nl.c
+++ b/netlink/nss_nl.c
@@ -1,6 +1,6 @@
 /*
  **************************************************************************
- * Copyright (c) 2015-2016,2018-2019 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2016,2018-2020 The Linux Foundation. All rights reserved.
  * Permission to use, copy, modify, and/or distribute this software for
  * any purpose with or without fee is hereby granted, provided that the
  * above copyright notice and this permission notice appear in all copies.
@@ -14,39 +14,45 @@
  **************************************************************************
  */
 
+#include <linux/if.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/kernel.h>
+#include <linux/netlink.h>
 #include <linux/of.h>
 #include <linux/types.h>
 #include <linux/version.h>
-#include <linux/if.h>
-#include <linux/netlink.h>
 #include <net/genetlink.h>
 
 #include <nss_api_if.h>
-#include <nss_nl_if.h>
+#include <nss_capwap.h>
+#include <nss_capwapmgr.h>
+#include <nss_cmn.h>
 #include <nss_ipsecmgr.h>
-#include "nss_nlcmn_if.h"
+#include <nss_nl_if.h>
 #include "nss_crypto_defines.h"
-#include "nss_nlipv4_if.h"
-#include "nss_nlipv6_if.h"
-#include "nss_nlipsec_if.h"
+#include "nss_nl.h"
+#include "nss_nlcapwap.h"
+#include "nss_nlcapwap_if.h"
+#include "nss_nlcmn_if.h"
+#include "nss_nlcrypto.h"
+#include "nss_nlcryptov2.h"
+#include "nss_nldtls.h"
+#include "nss_nldtls_if.h"
 #include "nss_nlgre_redir_if.h"
 #include "nss_nlgre_redir_family.h"
+#include "nss_nlipsec.h"
+#include "nss_nlipsec_if.h"
+#include "nss_nlipv4.h"
+#include "nss_nlipv4_if.h"
+#include "nss_nlipv6.h"
+#include "nss_nlipv6_if.h"
+#include "nss_nloam.h"
 #include "nss_nloam_if.h"
 #if defined (CONFIG_NSS_NLCRYPTO)
 #include "nss_nlcrypto_if.h"
 #else
 #include "nss_nlcryptov2_if.h"
 #endif
-#include "nss_nl.h"
-#include "nss_nlipv4.h"
-#include "nss_nlipv6.h"
-#include "nss_nlcrypto.h"
-#include "nss_nlcryptov2.h"
-#include "nss_nlipsec.h"
-#include "nss_nloam.h"
 
 /*
  * nss_nl.c
@@ -133,14 +139,31 @@
 		.exit = NSS_NLGRE_REDIR_FAMILY_EXIT,	/* exit */
 		.valid = CONFIG_NSS_NLGRE_REDIR_FAMILY	/* 1 or 0 */
 	},
-
+	{
+		/*
+		 * NSS_NLCAPWAP
+		 */
+		.name = NSS_NLCAPWAP_FAMILY,		/* capwap */
+		.entry = NSS_NLCAPWAP_INIT,		/* init */
+		.exit = NSS_NLCAPWAP_EXIT,		/* exit */
+		.valid = CONFIG_NSS_NLCAPWAP		/* 1 or 0 */
+	},
+	{
+		/*
+		 * NSS_NLDTLS
+		 */
+		.name = NSS_NLDTLS_FAMILY,		/* dtls */
+		.entry = NSS_NLDTLS_INIT,		/* init */
+		.exit = NSS_NLDTLS_EXIT,		/* exit */
+		.valid = CONFIG_NSS_NLDTLS		/* 1 or 0 */
+	},
 };
 
 #define NSS_NL_FAMILY_HANDLER_SZ ARRAY_SIZE(family_handlers)
 
 /*
  * nss_nl_alloc_msg()
- * 	allocate NETLINK message
+ *	allocate NETLINK message
  *
  * NOTE: this returns the SKB/message
  */
@@ -184,7 +207,7 @@
 
 /*
  * nss_nl_copy_msg()
- * 	copy a existing NETLINK message into a new one
+ *	copy a existing NETLINK message into a new one
  *
  * NOTE: this returns the new SKB/message
  */
@@ -206,7 +229,7 @@
 
 /*
  * nss_nl_get_data()
- * 	Returns start of payload data
+ *	Returns start of payload data
  */
 void  *nss_nl_get_data(struct sk_buff *skb)
 {
@@ -215,7 +238,7 @@
 
 /*
  * nss_nl_mcast_event()
- * 	mcast the event to the user listening on the MCAST group ID
+ *	mcast the event to the user listening on the MCAST group ID
  *
  * Note: It will free the message buffer if there is no space left to end
  */
@@ -253,7 +276,7 @@
 
 /*
  * nss_nl_ucast_resp()
- * 	send the response to the user (PID)
+ *	send the response to the user (PID)
  *
  * NOTE: this assumes the socket to be available for reception
  */
@@ -286,7 +309,7 @@
 
 /*
  * nss_nl_get_msg()
- * 	verifies and returns the message pointer
+ *	verifies and returns the message pointer
  */
 struct nss_nlcmn *nss_nl_get_msg(struct genl_family *family, struct genl_info *info, uint16_t cmd)
 {
@@ -323,7 +346,7 @@
 
 /*
  * nss_nl_init()
- * 	init module
+ *	init module
  */
 static int __init nss_nl_init(void)
 {
@@ -366,7 +389,7 @@
 
 /*
  * nss_nl_exit()
- * 	deinit module
+ *	deinit module
  */
 static void __exit nss_nl_exit(void)
 {
diff --git a/netlink/nss_nlcapwap.c b/netlink/nss_nlcapwap.c
new file mode 100644
index 0000000..7c5420b
--- /dev/null
+++ b/netlink/nss_nlcapwap.c
@@ -0,0 +1,1112 @@
+/*
+ **************************************************************************
+ * Copyright (c) 2015-2016,2018-2020 The Linux Foundation. All rights reserved.
+ * Permission to use, copy, modify, and/or distribute this software for
+ * any purpose with or without fee is hereby granted, provided that the
+ * above copyright notice and this permission notice appear in all copies.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ **************************************************************************
+ */
+
+#include <linux/if.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/netlink.h>
+#include <linux/types.h>
+#include <linux/version.h>
+
+#include <net/genetlink.h>
+#include <net/sock.h>
+
+#include <nss_api_if.h>
+#include <nss_capwap.h>
+#include <nss_capwapmgr.h>
+#include <nss_capwap_user.h>
+#include <nss_cmn.h>
+#include <nss_dtlsmgr.h>
+#include <nss_dtls_cmn.h>
+#include <nss_nlcmn_if.h>
+#include <nss_nl_if.h>
+#include "nss_crypto_defines.h"
+#include "nss_nl.h"
+#include "nss_nlcapwap.h"
+#include "nss_nlcapwap_if.h"
+#include "nss_nlcmn_if.h"
+
+/*
+ * Lock variable for synchronization
+ */
+static DEFINE_SPINLOCK(lock);
+
+/*
+ * Bitmap to keep track of tunnel state
+ */
+static DECLARE_BITMAP(tun_data, NSS_CAPWAPMGR_MAX_TUNNELS);
+
+/*
+ * Global capwap context variable
+ */
+static struct nss_nlcapwap_global_ctx capwap_gbl_ctx;
+
+/*
+ * Atomic variable to keep track of peformance
+ */
+static atomic_t enable_perf = ATOMIC_INIT(0);
+
+/*
+ * nss_nlcapwap_family_mcgrp
+ *	Multicast group for sending message status & events
+ */
+static const struct genl_multicast_group nss_nlcapwap_family_mcgrp[] = {
+	{.name = NSS_NLCAPWAP_MCAST_GRP},
+};
+
+/*
+ * nss_nlcapwap_family
+ *	Capwap family definition
+ */
+struct genl_family nss_nlcapwap_family = {
+	.id = GENL_ID_GENERATE,				/* Auto generate ID */
+	.name = NSS_NLCAPWAP_FAMILY,			/* family name string */
+	.hdrsize = sizeof(struct nss_nlcapwap_rule),	/* NSS NETLINK capwap rule */
+	.version = NSS_NL_VER,				/* Set it to NSS_NL_VER version */
+	.maxattr = NSS_NLCAPWAP_CMD_TYPE_MAX,		/* maximum commands supported */
+	.netnsok = true,
+	.pre_doit = NULL,
+	.post_doit = NULL,
+};
+
+/*
+ * nss_nlcapwap_update_tun_status()
+ *	Sets tun status to true or false based on parameter passed
+ *	0: The tunnel id is unused
+ *	1: The tunnel id is already taken and is in use
+ */
+static inline bool nss_nlcapwap_update_tun_status(int index, int status)
+{
+	if ((index < 0) || (index >= NSS_CAPWAPMGR_MAX_TUNNELS)) {
+		nss_nl_error("index value out of bound: %d\n", index);
+		return false;
+	}
+
+	/*
+	 * Test if tunnel is not set to status already
+	 */
+	spin_lock(&lock);
+	if (test_bit(index, tun_data) == status) {
+		spin_unlock(&lock);
+		nss_nl_error("Tunnel %d status is already set to: %d\n", index, status);
+		return false;
+	}
+
+	spin_unlock(&lock);
+	change_bit(index, tun_data);
+	return true;
+}
+
+/*
+ * nss_nlcapwap_get_meta_header()
+ *	Returns meta header
+ */
+static inline struct nss_nlcapwap_meta_header nss_nlcapwap_get_meta_header(void)
+{
+	struct nss_nlcapwap_meta_header meta_hdr;
+	spin_lock(&lock);
+	meta_hdr = capwap_gbl_ctx.meta_header;
+	spin_unlock(&lock);
+	return meta_hdr;
+}
+
+/*
+ * nss_nlcapwap_set_meta_header()
+ *	Sets the value of meta header
+ */
+static inline void nss_nlcapwap_set_meta_header(struct nss_nlcapwap_meta_header meta_hdr)
+{
+	spin_lock(&lock);
+	capwap_gbl_ctx.meta_header = meta_hdr;
+	spin_unlock(&lock);
+}
+
+/*
+ * nss_nlcapwap_rx_handler()
+ *	Capwap netdev rx handler
+ */
+static rx_handler_result_t nss_nlcapwapmgr_rx_handler(struct sk_buff **pskb)
+{
+	struct nss_capwap_metaheader *pre;
+	uint32_t pattern, no_pattern, i;
+	struct sk_buff *skb = *pskb;
+	uint8_t *data;
+
+	if (atomic_read(&enable_perf)) {
+		goto done;
+	}
+
+	data = skb->data;
+	pattern = no_pattern = 0;
+	for (i = 0; i < skb->len; i++) {
+		(data[i] == NSS_NLCAPWAP_DATA) ? pattern++ : no_pattern++;
+	}
+
+done:
+	pre = (struct nss_capwap_metaheader *)skb->data;
+	nss_nl_info("tunnel %d: NETDEV RX: len:%d (%d,%d)\n", pre->tunnel_id,
+	      skb->len, pattern, no_pattern);
+	dev_kfree_skb_any(skb);
+	return RX_HANDLER_CONSUMED;
+}
+
+/*
+ * nss_nlcapwap_register_netdev_handler()
+ *	Handler to register capwap netdev rx handler
+ */
+static void nss_nlcapwap_register_netdev_handler(void)
+{
+	struct net_device *capwap_ndev = nss_capwapmgr_get_netdev();
+
+	/*
+	 * Register a netdevice rx handler
+	 */
+	rtnl_lock();
+	if (netdev_rx_handler_register(capwap_ndev, nss_nlcapwapmgr_rx_handler, NULL)) {
+		nss_nl_error("Couldn't register CAPWAP RX handler\n");
+	}
+
+	rtnl_unlock();
+}
+
+/*
+ * nss_nlcapwap_data_cb()
+ *	Data callback for capwap
+ */
+static void nss_nlcapwap_data_cb(void *app_data, struct sk_buff *skb)
+{
+	nss_nl_error("%p: RX DTLS exception packet len:%d\n", skb, skb->len);
+}
+
+/*
+ * nss_nlcapwap_dtls_configure()
+ *	Common handler for v4 and v6 capwap-dtls configuration
+ */
+static void nss_nlcapwap_dtls_configure(struct nss_dtlsmgr_config *dcfg,
+		struct nss_nlcapwap_rule *nl_rule)
+{
+	uint8_t algo = nl_rule->msg.dtls.encap.crypto.algo;
+
+	dcfg->flags |= NSS_DTLSMGR_HDR_CAPWAP;
+	if (algo == NSS_DTLSMGR_ALGO_AES_GCM) {
+		dcfg->flags |= NSS_DTLSMGR_CIPHER_MODE_GCM;
+	}
+
+	/*
+	 * Encap configuration
+	 */
+	dcfg->app_data = NULL;
+	dcfg->notify = NULL;
+	dcfg->data = nss_nlcapwap_data_cb;
+	memcpy((void *)&dcfg->encap, (void *)&nl_rule->msg.dtls.encap, sizeof(struct nss_dtlsmgr_encap_config));
+	/*
+	 * Decap configuration
+	 */
+	memcpy((void *)&dcfg->decap, (void *)&nl_rule->msg.dtls.decap, sizeof(struct nss_dtlsmgr_decap_config));
+}
+
+/*
+ * nss_nlcapwap_create_tun_ipv4_config()
+ *	Configures the common rule for rx and tx.
+ */
+static void nss_nlcapwap_create_tun_ipv4_config(struct nss_nlcapwap_rule *nl_rule,
+		struct nss_capwap_rule_msg *capwap_rule, struct nss_dtlsmgr_config *dtls_ipv4,
+		struct nss_ipv4_create *ipv4)
+{
+	struct net_device *wan_ndev;
+	uint32_t features = 0;
+	uint32_t if_num;
+
+	/*
+	 * Initialize the msgs
+	 */
+	memset(ipv4, 0, sizeof(*ipv4));
+	memset(capwap_rule, 0, sizeof(*capwap_rule));
+
+	/*
+	 * Configure CAPWAP rule
+	 */
+	capwap_rule->encap.path_mtu = htonl(nl_rule->msg.create.rule.encap.path_mtu);
+	capwap_rule->decap.reassembly_timeout = htonl(nl_rule->msg.create.rule.decap.reassembly_timeout);
+	capwap_rule->decap.max_fragments = htonl(nl_rule->msg.create.rule.decap.max_fragments);
+	capwap_rule->decap.max_buffer_size = htonl(nl_rule->msg.create.rule.decap.max_buffer_size);
+	capwap_rule->stats_timer = htonl(nl_rule->msg.create.rule.stats_timer);
+	capwap_rule->l3_proto = NSS_CAPWAP_TUNNEL_IPV4;
+	if (nl_rule->msg.create.rule.which_udp == IPPROTO_UDP) {
+		capwap_rule->which_udp = NSS_CAPWAP_TUNNEL_UDP;
+		ipv4->protocol = IPPROTO_UDP;
+	} else {
+		capwap_rule->which_udp = NSS_CAPWAP_TUNNEL_UDPLite;
+		ipv4->protocol = IPPROTO_UDPLITE;
+	}
+
+	if (nl_rule->msg.create.inner_trustsec_en) {
+		nss_nl_info("Enabling INNER TRUSTSEC in rule\n");
+		features |= NSS_CAPWAPMGR_FEATURE_INNER_TRUSTSEC_ENABLED;
+	}
+
+	if (nl_rule->msg.create.outer_trustsec_en) {
+		nss_nl_info("Enabling OUTER TRUSTSEC in rule\n");
+		features |= NSS_CAPWAPMGR_FEATURE_OUTER_TRUSTSEC_ENABLED;
+	}
+
+	if (nl_rule->msg.create.wireless_qos_en) {
+		nss_nl_info("Enabling WIRELESS QOS\n");
+		features |= NSS_CAPWAPMGR_FEATURE_WIRELESS_QOS_ENABLED;
+	}
+
+	capwap_rule->enabled_features = features;
+
+	/*
+	 * Configure IPv4 rule
+	 */
+	wan_ndev = dev_get_by_name(&init_net, &nl_rule->msg.create.gmac_ifname[0]);
+	if (!wan_ndev) {
+		nss_nl_info("Can't find %s netdev\n", nl_rule->msg.create.gmac_ifname);
+		return;
+	}
+
+	if_num = nss_cmn_get_interface_number_by_dev(wan_ndev);
+	nss_nl_info("CAPWAP on %s, if_num is %d\n", nl_rule->msg.create.gmac_ifname, if_num);
+	ipv4->src_interface_num = if_num;
+	ipv4->dest_interface_num = NSS_NLCAPWAP_WAN_IFNUM;
+	ipv4->from_mtu = wan_ndev->mtu;
+	ipv4->to_mtu = wan_ndev->mtu;
+
+	ipv4->src_ip = nl_rule->msg.create.rule.encap.src_ip.ip.ipv4;
+	ipv4->src_port = nl_rule->msg.create.rule.encap.src_port;
+	ipv4->src_ip_xlate = nl_rule->msg.create.rule.encap.src_ip.ip.ipv4;
+	ipv4->src_port_xlate = nl_rule->msg.create.rule.encap.src_port;
+
+	ipv4->dest_ip = nl_rule->msg.create.rule.encap.dest_ip.ip.ipv4;
+	ipv4->dest_port = nl_rule->msg.create.rule.encap.dest_port;
+	ipv4->dest_ip_xlate = nl_rule->msg.create.rule.encap.dest_ip.ip.ipv4;
+	ipv4->dest_port_xlate = nl_rule->msg.create.rule.encap.dest_port;
+
+	memcpy(ipv4->src_mac, nl_rule->msg.create.gmac_ifmac, sizeof(ipv4->src_mac));
+
+	ipv4->in_vlan_tag[0] = NSS_NLCAPWAP_VLAN_TAG_INVALID;
+	ipv4->in_vlan_tag[1] = NSS_NLCAPWAP_VLAN_TAG_INVALID;
+	ipv4->out_vlan_tag[0] = NSS_NLCAPWAP_VLAN_TAG_INVALID;
+	ipv4->out_vlan_tag[1] = NSS_NLCAPWAP_VLAN_TAG_INVALID;
+	dev_put(wan_ndev);
+}
+
+/*
+ * nss_nlcapwap_create_tun_ipv6_config()
+ *	Configures the common rule for rx and tx.
+ */
+static void nss_nlcapwap_create_tun_ipv6_config(struct nss_nlcapwap_rule *nl_rule,
+		struct nss_capwap_rule_msg *capwap_rule, struct nss_dtlsmgr_config *dtls_ipv6,
+		struct nss_ipv6_create *ipv6)
+{
+	struct net_device *wan_ndev;
+	uint32_t features = 0;
+	uint32_t if_num;
+
+	/*
+	 * Initialize the msgs
+	 */
+	memset(ipv6, 0, sizeof (struct nss_ipv6_create));
+	memset(capwap_rule, 0, sizeof(struct nss_capwap_rule_msg));
+
+	/*
+	 * Configuring capwap rule
+	 */
+	capwap_rule->encap.path_mtu = htonl(nl_rule->msg.create.rule.encap.path_mtu);
+	capwap_rule->decap.reassembly_timeout = htonl(nl_rule->msg.create.rule.decap.reassembly_timeout);
+	capwap_rule->decap.max_fragments = htonl(nl_rule->msg.create.rule.decap.max_fragments);
+	capwap_rule->decap.max_buffer_size = htonl(nl_rule->msg.create.rule.decap.max_buffer_size);
+	capwap_rule->stats_timer = htonl(nl_rule->msg.create.rule.stats_timer);
+	capwap_rule->l3_proto = NSS_CAPWAP_TUNNEL_IPV6;
+
+	if (nl_rule->msg.create.rule.which_udp == IPPROTO_UDP) {
+		capwap_rule->which_udp = NSS_CAPWAP_TUNNEL_UDP;
+		ipv6->protocol = IPPROTO_UDP;
+	} else {
+		capwap_rule->which_udp = NSS_CAPWAP_TUNNEL_UDPLite;
+		ipv6->protocol = IPPROTO_UDPLITE;
+	}
+
+	if (nl_rule->msg.create.inner_trustsec_en) {
+		nss_nl_info("Enabling INNER TRUSTSEC in rule\n");
+		features |= NSS_CAPWAPMGR_FEATURE_INNER_TRUSTSEC_ENABLED;
+	}
+
+	if (nl_rule->msg.create.outer_trustsec_en) {
+		nss_nl_info("Enabling OUTER TRUSTSEC in rule\n");
+		features |= NSS_CAPWAPMGR_FEATURE_OUTER_TRUSTSEC_ENABLED;
+	}
+
+	if (nl_rule->msg.create.wireless_qos_en) {
+		nss_nl_info("Enabling WIRELESS QOS\n");
+		features |= NSS_CAPWAPMGR_FEATURE_WIRELESS_QOS_ENABLED;
+	}
+
+	capwap_rule->enabled_features = features;
+	capwap_rule->outer_sgt_value = nl_rule->msg.create.rule.outer_sgt_value;
+
+	/*
+	 * Configure IPv6 rule
+	 */
+	wan_ndev = dev_get_by_name(&init_net, &nl_rule->msg.create.gmac_ifname[0]);
+	if (!wan_ndev) {
+		nss_nl_info("Can't find %s netdev\n", nl_rule->msg.create.gmac_ifname);
+		return;
+	}
+
+	if_num = nss_cmn_get_interface_number_by_dev(wan_ndev);
+	nss_nl_info("CAPWAP on %s, if_num is %d\n", nl_rule->msg.create.gmac_ifname, if_num);
+	ipv6->src_interface_num = if_num;
+	ipv6->dest_interface_num = NSS_NLCAPWAP_WAN_IFNUM;
+	ipv6->from_mtu = wan_ndev->mtu;
+	ipv6->to_mtu = wan_ndev->mtu;
+	memcpy(ipv6->src_ip, nl_rule->msg.create.rule.encap.src_ip.ip.ipv6, sizeof(ipv6->src_ip));
+	memcpy(ipv6->dest_ip, nl_rule->msg.create.rule.encap.dest_ip.ip.ipv6, sizeof(ipv6->dest_ip));
+	memcpy(&ipv6->src_mac[0], &nl_rule->msg.create.gmac_ifmac[0], sizeof(ipv6->src_mac));
+	ipv6->src_port = nl_rule->msg.create.rule.encap.src_port;
+	ipv6->dest_port = nl_rule->msg.create.rule.encap.dest_port;
+	ipv6->in_vlan_tag[0] = NSS_NLCAPWAP_VLAN_TAG_INVALID;
+	ipv6->in_vlan_tag[1] = NSS_NLCAPWAP_VLAN_TAG_INVALID;
+	ipv6->out_vlan_tag[0] = NSS_NLCAPWAP_VLAN_TAG_INVALID;
+	ipv6->out_vlan_tag[1] = NSS_NLCAPWAP_VLAN_TAG_INVALID;
+	dev_put(wan_ndev);
+}
+
+/*
+ * nss_nlcapwap_ops_create_tun()
+ *	Handler for creating tunnel
+ */
+static int nss_nlcapwap_ops_create_tun(struct sk_buff *skb, struct genl_info *info)
+{
+	struct nss_capwap_rule_msg capwap_rule;
+	struct nss_dtlsmgr_config dtls_ipv4;
+	struct nss_dtlsmgr_config dtls_ipv6;
+	struct nss_nlcapwap_rule *nl_rule;
+	struct nss_ipv4_create ipv4_rule;
+	struct nss_ipv6_create ipv6_rule;
+	struct net_device *capwap_ndev;
+	struct nss_nlcmn *nl_cm;
+	int tun_id;
+	int ret = 0;
+
+	/*
+	 * Get the tunnel_id. We only create a new tunnel if max limit is not exceeded
+	 */
+	spin_lock(&lock);
+	tun_id = find_first_zero_bit(tun_data, NSS_CAPWAPMGR_MAX_TUNNELS);
+	spin_unlock(&lock);
+	if (tun_id >= NSS_CAPWAPMGR_MAX_TUNNELS) {
+		nss_nl_error("Max number of tunnels limit exceeded\n");
+		return -EPERM;
+	}
+
+	/*
+	 * Get the capwap netdev reference
+	 */
+	capwap_ndev = nss_capwapmgr_get_netdev();
+
+	/*
+	 * Extract the message payload
+	 */
+	nl_cm = nss_nl_get_msg(&nss_nlcapwap_family, info, NSS_NLCAPWAP_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_nlcapwap_rule, cm);
+
+	/*
+	 * Needed for 802.3 to 802.11 conversion
+	 */
+	memcpy(capwap_rule.bssid, nl_rule->msg.create.rule.bssid, sizeof(capwap_rule.bssid));
+	capwap_rule.outer_sgt_value = nl_rule->msg.create.rule.outer_sgt_value;
+
+	spin_lock(&lock);
+	if (nl_rule->msg.create.wireless_qos_en) {
+		capwap_gbl_ctx.nss_nlcapwap_80211e = NSS_NLCAPWAP_80211E_ONE;
+	} else {
+		capwap_gbl_ctx.nss_nlcapwap_80211e = NSS_NLCAPWAP_80211E_ZERO;
+	}
+
+	spin_unlock(&lock);
+
+	/*
+	 * Create tunnel based on ip version
+	 */
+	if (nl_rule->msg.create.rule.l3_proto == NSS_NLCAPWAP_IP_VERS_4) {
+		nss_nlcapwap_create_tun_ipv4_config(nl_rule, &capwap_rule, &dtls_ipv4, &ipv4_rule);
+		/*
+		 * Create CAPWAP IPv4 tunnel
+		 */
+		ret = nss_capwapmgr_ipv4_tunnel_create(capwap_ndev, tun_id, &ipv4_rule,
+				&capwap_rule, &dtls_ipv4);
+		if (ret != 0) {
+			nss_nl_error("Unable to create tunnel: %d\n", tun_id);
+			return -EAGAIN;
+		}
+
+		nss_nl_info("Created IPv4 tunnel: src:%pI4h(%d) dst:%pI4h(%d) p:%d\n\n",
+		      &ipv4_rule.src_ip, ipv4_rule.src_port, &ipv4_rule.dest_ip,
+		      ipv4_rule.dest_port, ipv4_rule.protocol);
+	} else {
+		nss_nlcapwap_create_tun_ipv6_config(nl_rule, &capwap_rule, &dtls_ipv6, &ipv6_rule);
+		/*
+		 * Create CAPWAP IPv6 tunnel
+		 */
+		ret = nss_capwapmgr_ipv6_tunnel_create(capwap_ndev, tun_id, &ipv6_rule,
+				&capwap_rule, &dtls_ipv6);
+		if (ret != 0) {
+			nss_nl_error("Unable to create tunnel: %d\n", tun_id);
+			return -EAGAIN;
+		}
+
+		nss_nl_info("Created IPv6 tunnel + proto:%d\n", ipv6_rule.protocol);
+		nss_nl_info("src:%pI6(%d)\n", ipv6_rule.src_ip, ipv6_rule.src_port);
+		nss_nl_info("dst:%pI6(%d)\n\n", ipv6_rule.dest_ip, ipv6_rule.dest_port);
+	}
+
+	nss_capwapmgr_change_version(capwap_ndev, tun_id, NSS_CAPWAP_VERSION_V2);
+	nss_capwapmgr_enable_tunnel(capwap_ndev, tun_id);
+
+	/*
+	 * Increment the tunnel_id
+	 */
+	if (!nss_nlcapwap_update_tun_status(tun_id, 1)) {
+		nss_nl_error("Unable to set the tunnel status: %d\n", tun_id);
+		return -EAGAIN;
+	}
+
+	nss_nl_info("Successfully created tunnel %d\n", tun_id + 1);
+	return 0;
+}
+
+/*
+ * nss_nlcapwap_destroy_tun()
+ *	Destroys tunnel based on tun_id
+ */
+static bool nss_nlcapwap_destroy_tun(int tun_id)
+{
+	struct net_device *capwap_ndev;
+	nss_capwapmgr_status_t status;
+
+	/*
+	 * Get the capwap netdev reference
+	 */
+	capwap_ndev = nss_capwapmgr_get_netdev();
+
+	/*
+	 * Disable the tunnel and then destroy
+	 */
+	spin_lock(&lock);
+	if (!test_bit(tun_id, tun_data)) {
+		nss_nl_error("Tunnel is already disabled: %d\n", tun_id);
+		spin_unlock(&lock);
+		return false;
+	}
+
+	spin_unlock(&lock);
+	status = nss_capwapmgr_disable_tunnel(capwap_ndev, tun_id);
+	if (status != NSS_CAPWAPMGR_SUCCESS) {
+		nss_nl_error("Unable to disable the tunnel: %d\n", tun_id);
+		return false;
+	}
+
+	status = nss_capwapmgr_tunnel_destroy(capwap_ndev, tun_id);
+	if (status != NSS_CAPWAPMGR_SUCCESS) {
+		nss_nl_error("Unable to destroy the tunnel: %d\n", tun_id);
+		nss_capwapmgr_enable_tunnel(capwap_ndev, tun_id);
+		return false;
+	}
+
+	/*
+	 * Update the tunnel status in global array
+	 */
+	if (!nss_nlcapwap_update_tun_status(tun_id, 0)) {
+		nss_nl_error("Unable to set the tunnel status to 0: %d\n", tun_id);
+		return false;
+	}
+
+	return true;
+}
+
+/*
+ * nss_nlcapwap_ops_destroy_tun()
+ *	Handler to destroy tunnel
+ */
+static int nss_nlcapwap_ops_destroy_tun(struct sk_buff *skb, struct genl_info *info)
+{
+	struct nss_nlcapwap_rule *nl_rule;
+	struct nss_nlcmn *nl_cm;
+	int tun_id, ret;
+
+	/*
+	 * Extract the message payload
+	 */
+	nl_cm = nss_nl_get_msg(&nss_nlcapwap_family, info, NSS_NLCAPWAP_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_nlcapwap_rule, cm);
+
+	/*
+	 * Get the tunnel id
+	 */
+	tun_id = nl_rule->msg.destroy.tun_id;
+	if (tun_id < 0 || (tun_id >= NSS_CAPWAPMGR_MAX_TUNNELS)) {
+		nss_nl_error("Not a valid tunnel_id: %d\n", tun_id);
+		return -EINVAL;
+	}
+
+	ret = nss_nlcapwap_destroy_tun(tun_id);
+	if (!ret) {
+		nss_nl_error("%p: Unable to destroy tunnel: %d\n", skb, tun_id);
+		return -EINVAL;
+	}
+
+	nss_nl_info("Successfully destroyed %d tunnel\n", nl_rule->msg.destroy.tun_id);
+	return 0;
+}
+
+/*
+ * nss_nlcapwap_ops_update_mtu()
+ *	Handler for updating mtu
+ */
+static int nss_nlcapwap_ops_update_mtu(struct sk_buff *skb, struct genl_info *info)
+{
+	struct nss_nlcapwap_rule *nl_rule;
+	struct net_device *capwap_ndev;
+	nss_capwapmgr_status_t status;
+	struct nss_nlcmn *nl_cm;
+	int tun_id;
+
+	/*
+	 * Get the capwap netdev reference
+	 */
+	capwap_ndev = nss_capwapmgr_get_netdev();
+
+	/*
+	 * extract the message payload
+	 */
+	nl_cm = nss_nl_get_msg(&nss_nlcapwap_family, info, NSS_NLCAPWAP_CMD_TYPE_UPDATE_MTU);
+	if (!nl_cm) {
+		nss_nl_error("Unable to extract update mtu data\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Message validation required before accepting the configuration
+	 */
+	nl_rule = container_of(nl_cm, struct nss_nlcapwap_rule, cm);
+
+	/*
+	 * Update the path_mtu of the corresponding tunnel
+	 */
+	tun_id = nl_rule->msg.update_mtu.tun_id;
+	if (tun_id < 0 || (tun_id >= NSS_CAPWAPMGR_MAX_TUNNELS)) {
+		nss_nl_error("Not a valid tunnel_id: %d\n", tun_id);
+		return -EINVAL;
+	}
+
+	status = nss_capwapmgr_update_path_mtu(capwap_ndev, tun_id,
+			nl_rule->msg.update_mtu.mtu.path_mtu);
+	if (status != NSS_CAPWAPMGR_SUCCESS) {
+		nss_nl_error("Unable to update the mtu of the %d tunnel: %d\n", tun_id,
+				nl_rule->msg.update_mtu.mtu.path_mtu);
+		return -EAGAIN;
+	}
+
+	nss_nl_info("Successfully updated the mtu of the %d tunnel.\n", tun_id);
+	return 0;
+}
+
+/*
+ * nss_nlcapwap_ops_dtls()
+ *	Handler for dtls enable or disable command
+ */
+static int nss_nlcapwap_ops_dtls(struct sk_buff *skb, struct genl_info *info)
+{
+	struct nss_dtlsmgr_config dtls_config;
+	struct nss_nlcapwap_rule *nl_rule;
+	struct net_device *capwap_ndev;
+	nss_capwapmgr_status_t status;
+	struct nss_nlcmn *nl_cm;
+
+	/*
+	 * Get the capwap netdev reference
+	 */
+	capwap_ndev = nss_capwapmgr_get_netdev();
+
+	/*
+	 * extract the message payload
+	 */
+	nl_cm = nss_nl_get_msg(&nss_nlcapwap_family, info, NSS_NLCAPWAP_CMD_TYPE_DTLS);
+	if (!nl_cm) {
+		nss_nl_error("Unable to extract dtls data\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Message validation required before accepting the configuration
+	 */
+	nl_rule = container_of(nl_cm, struct nss_nlcapwap_rule, cm);
+
+	/*
+	 * Disabling dtls for capwap
+	 */
+	if (!nl_rule->msg.dtls.enable_dtls) {
+		status = nss_capwapmgr_disable_tunnel(capwap_ndev, nl_rule->msg.dtls.tun_id);
+		if (status != NSS_CAPWAPMGR_SUCCESS) {
+			nss_nl_error("Not able to disable tunnel %d\n",
+					nl_rule->msg.dtls.tun_id);
+			return -EAGAIN;
+		}
+
+		status = nss_capwapmgr_configure_dtls(capwap_ndev, nl_rule->msg.dtls.tun_id, 0, NULL);
+		if (status != NSS_CAPWAPMGR_SUCCESS) {
+			nss_nl_error("Not able to disable dtls for tunnel %d\n",
+					nl_rule->msg.dtls.tun_id);
+			nss_capwapmgr_enable_tunnel(capwap_ndev, nl_rule->msg.dtls.tun_id);
+			return -EAGAIN;
+		}
+
+		status = nss_capwapmgr_enable_tunnel(capwap_ndev, nl_rule->msg.dtls.tun_id);
+		if (status != NSS_CAPWAPMGR_SUCCESS) {
+			nss_nl_error("Not able to enable tunnel %d\n",
+					nl_rule->msg.dtls.tun_id);
+			return -EAGAIN;
+		}
+
+		nss_nl_info("Succesfully disabled dtls for capwap tunnel %d\n",
+				nl_rule->msg.dtls.tun_id);
+		return 0;
+	}
+
+	nss_nl_info("Enabling DTLS for tunnel %d\n", nl_rule->msg.dtls.tun_id);
+
+	/*
+	 * Initializing the dtls message
+	 */
+	dtls_config.flags = nl_rule->msg.dtls.flags;
+
+	/*
+	 * Fill rest of DTLS data
+	 */
+	nss_nlcapwap_dtls_configure(&dtls_config, nl_rule);
+
+	/*
+	 * Enabling dtls for capwap tunnel
+	 */
+	status = nss_capwapmgr_disable_tunnel(capwap_ndev, nl_rule->msg.dtls.tun_id);
+	if (status != NSS_CAPWAPMGR_SUCCESS) {
+		nss_nl_error("Not able to disable tunnel %d\n",
+				nl_rule->msg.dtls.tun_id);
+		return -EAGAIN;
+	}
+
+	status = nss_capwapmgr_configure_dtls(capwap_ndev, nl_rule->msg.dtls.tun_id,
+			1, &dtls_config);
+	if (status != NSS_CAPWAPMGR_SUCCESS) {
+		nss_nl_error("Not able to enable dtls for tunnel %d\n",
+				nl_rule->msg.dtls.tun_id);
+		nss_capwapmgr_enable_tunnel(capwap_ndev, nl_rule->msg.dtls.tun_id);
+		return -EAGAIN;
+	}
+
+	status = nss_capwapmgr_enable_tunnel(capwap_ndev, nl_rule->msg.dtls.tun_id);
+	if (status != NSS_CAPWAPMGR_SUCCESS) {
+		nss_nl_error("Not able to enable tunnel %d\n",
+				nl_rule->msg.dtls.tun_id);
+		return -EAGAIN;
+	}
+
+	nss_nl_info("Successfully enabled dtls for the capwap tunnel: %d\n",
+			nl_rule->msg.dtls.tun_id);
+	return 0;
+}
+
+/*
+ * nss_nlcapwap_ops_perf()
+ *	Handler for enabling or disabling perf
+ */
+static int nss_nlcapwap_ops_perf(struct sk_buff *skb, struct genl_info *info)
+{
+	struct nss_nlcapwap_rule *nl_rule;
+	struct nss_nlcmn *nl_cm;
+
+	/*
+	 * extract the message payload
+	 */
+	nl_cm = nss_nl_get_msg(&nss_nlcapwap_family, info, NSS_NLCAPWAP_CMD_TYPE_PERF);
+	if (!nl_cm) {
+		nss_nl_error("Unable to extract perf parameter.\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Message validation required before accepting the configuration
+	 */
+	nl_rule = container_of(nl_cm, struct nss_nlcapwap_rule, cm);
+
+	if (nl_rule->msg.perf.perf_en) {
+		atomic_set(&enable_perf, 1);
+		nss_nl_info("Successfully enabled performance.\n");
+		return 0;
+	}
+
+	atomic_set(&enable_perf, 0);
+	nss_nl_info("Successfully disabled performance.\n");
+
+	return 0;
+}
+
+/*
+ * nss_nlcapwap_ops_ip_flow()
+ *	Handler for adding or deleting ip flow rule
+ */
+static int nss_nlcapwap_ops_ip_flow(struct sk_buff *skb, struct genl_info *info)
+{
+	nss_capwapmgr_status_t status;
+	struct nss_nlcapwap_rule *nl_rule;
+	struct net_device *capwap_ndev;
+	struct nss_nlcmn *nl_cm;
+
+	/*
+	 * Get the capwap netdev reference
+	 */
+	capwap_ndev = nss_capwapmgr_get_netdev();
+
+	/*
+	 * extract the message payload
+	 */
+	nl_cm = nss_nl_get_msg(&nss_nlcapwap_family, info, NSS_NLCAPWAP_CMD_TYPE_IP_FLOW);
+	if (!nl_cm) {
+		nss_nl_error("Unable to extract ip flow rule.\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Message validation required before accepting the configuration
+	 */
+	nl_rule = container_of(nl_cm, struct nss_nlcapwap_rule, cm);
+
+	/*
+	 * Add flow rule
+	 */
+	if (nl_rule->msg.ip_flow.ip_flow_mode == NSS_NLCAPWAP_IP_FLOW_MODE_ADD) {
+		status = nss_capwapmgr_add_flow_rule(capwap_ndev, nl_rule->msg.ip_flow.tun_id,
+				nl_rule->msg.ip_flow.flow.ip_version, nl_rule->msg.ip_flow.flow.protocol,
+				nl_rule->msg.ip_flow.flow.src_ip, nl_rule->msg.ip_flow.flow.dst_ip,
+				nl_rule->msg.ip_flow.flow.src_port, nl_rule->msg.ip_flow.flow.dst_port,
+				nl_rule->msg.ip_flow.flow.flow_id);
+		if (status != NSS_CAPWAPMGR_SUCCESS) {
+			nss_nl_error("Unable to add flow rule\n");
+			return -EAGAIN;
+		}
+
+		nss_nl_info("Succesfully added flow rule for tunnel %d\n",
+				nl_rule->msg.ip_flow.tun_id);
+		return 0;
+	}
+
+	/*
+	 * Delete existing flow rule
+	 */
+	status = nss_capwapmgr_del_flow_rule(capwap_ndev, nl_rule->msg.ip_flow.tun_id,
+			nl_rule->msg.ip_flow.flow.ip_version, nl_rule->msg.ip_flow.flow.protocol,
+			nl_rule->msg.ip_flow.flow.src_ip, nl_rule->msg.ip_flow.flow.dst_ip,
+			nl_rule->msg.ip_flow.flow.src_port, nl_rule->msg.ip_flow.flow.dst_port);
+	if (status != NSS_CAPWAPMGR_SUCCESS) {
+		nss_nl_error("Unable to del flow rule\n");
+		return -EAGAIN;
+	}
+
+	nss_nl_info("Succesfully deleted flow rule for tunnel %d\n", nl_rule->msg.ip_flow.tun_id);
+	return 0;
+}
+
+/*
+ * nss_nlcapwap_tx_packets_host_to_host()
+ *	Handler for sending traffic from one DUT to other
+ */
+static int nss_nlcapwap_tx_packets_host_to_host(struct nss_nlcapwap_rule *nl_rule)
+{
+	struct nss_capwap_metaheader *pre;
+	struct net_device *capwap_ndev;
+	uint32_t pattern, no_pattern;
+	struct sk_buff *skb;
+	uint8_t *data;
+	int i, len;
+
+	/*
+	 * Get the capwap netdev reference
+	 */
+	capwap_ndev = nss_capwapmgr_get_netdev();
+	skb = dev_alloc_skb(nl_rule->msg.tx_packets.pkt_size + NSS_NLCAPWAP_SKB_TAILROOM);
+	if (!skb) {
+		nss_nl_error("Could not allocate a sk_buff\n");
+		return -1;
+	}
+
+	/*
+	 * Reserve 2 bytes extra if meta header type is 802.3
+	 */
+	spin_lock(&lock);
+	if (capwap_gbl_ctx.meta_header.type == NSS_CAPWAP_PKT_TYPE_802_3) {
+		skb_reserve(skb, NSS_NLCAPWAP_SKB_RESERVE_SZ_HUNDRED);
+	} else {
+		skb_reserve(skb, NSS_NLCAPWAP_SKB_RESERVE_SZ_NINTY_EIGHT +
+				NSS_NLCAPWAP_SKB_RESERVE_SZ_TWO * capwap_gbl_ctx.nss_nlcapwap_80211e);
+	}
+
+	skb_put(skb, nl_rule->msg.tx_packets.pkt_size + sizeof(struct nss_capwap_metaheader));
+
+	/*
+	 * Fill pattern to check for packet integrity
+	 */
+	if (!atomic_read(&enable_perf)) {
+		memset(&skb->data[sizeof(struct nss_capwap_metaheader)], NSS_NLCAPWAP_DATA,
+			nl_rule->msg.tx_packets.pkt_size);
+	}
+
+	if (capwap_gbl_ctx.meta_header.type == NSS_NLCAPWAP_META_HEADER_TYPE_ZERO) {
+		/*
+		 * TYPE ipv4. Fill the iptype and ipheader info.
+		 */
+		skb->data[sizeof(struct nss_capwap_metaheader) + 12] = 0x8;
+		skb->data[sizeof(struct nss_capwap_metaheader) + 13] = 0;
+		skb->data[sizeof(struct nss_capwap_metaheader) + 14] = 0x45;
+		skb->data[sizeof(struct nss_capwap_metaheader) + 15] = 0;
+	} else if (capwap_gbl_ctx.meta_header.type == NSS_NLCAPWAP_META_HEADER_TYPE_ONE) {
+		/*
+		 * Type EAPOL. Fill the iptype for ethernet header.
+		 */
+		skb->data[sizeof(struct nss_capwap_metaheader) + 12] = 0x88;
+		skb->data[sizeof(struct nss_capwap_metaheader) + 13] = 0x8e;
+	} else {
+		/*
+		 * Type management. Recognized by 0x0004 after metadata.
+		 */
+		skb->data[sizeof(struct nss_capwap_metaheader) + 0] = 0x00;
+		skb->data[sizeof(struct nss_capwap_metaheader) + 1] = 0x04;
+	}
+
+	pre = (struct nss_capwap_metaheader *)skb->data;
+	memcpy(pre, &capwap_gbl_ctx.meta_header, sizeof(struct nss_capwap_metaheader));
+	spin_unlock(&lock);
+
+	if (atomic_read(&enable_perf)) {
+		goto process;
+	}
+
+	pattern = no_pattern = 0;
+	data = skb->data;
+	len = skb->len;
+	for (i = 0; i < len; i++) {
+		(data[i] == NSS_NLCAPWAP_DATA) ? pattern++ : no_pattern++;
+	}
+
+process:
+	capwap_ndev->netdev_ops->ndo_start_xmit(skb, capwap_ndev);
+	nss_nl_info("tunnel %d: TX: len:%d (%d,%d)\n", pre->tunnel_id,
+	      len, pattern, no_pattern);
+
+	return 0;
+}
+
+/*
+ * nss_nlcapwap_ops_tx_packets()
+ *	Handler for sending traffic
+ */
+static int nss_nlcapwap_ops_tx_packets(struct sk_buff *skb, struct genl_info *info)
+{
+	struct nss_nlcapwap_rule *nl_rule;
+	struct nss_nlcmn *nl_cm;
+	int ret, i;
+
+	/*
+	 * extract the message payload
+	 */
+	nl_cm = nss_nl_get_msg(&nss_nlcapwap_family, info, NSS_NLCAPWAP_CMD_TYPE_TX_PACKETS);
+	if (!nl_cm) {
+		nss_nl_error("Unable to extract tx_packets data\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Message validation required before accepting the configuration
+	 */
+	nl_rule = container_of(nl_cm, struct nss_nlcapwap_rule, cm);
+
+	/*
+	 * Send traffic from host to host
+	 */
+	for (i = 0; i < nl_rule->msg.tx_packets.num_of_packets; i++) {
+		ret = nss_nlcapwap_tx_packets_host_to_host(nl_rule);
+		if (ret < 0) {
+			nss_nl_error("Error in transmission of skb\n");
+			return ret;
+		}
+	}
+
+	nss_nl_info("Traffic transmission successful\n");
+	return 0;
+}
+
+/*
+ * nss_nlcapwap_ops_meta_header()
+ *	Handler for creating meta header
+ */
+static int nss_nlcapwap_ops_meta_header(struct sk_buff *skb, struct genl_info *info)
+{
+	struct nss_nlcapwap_meta_header meta_hdr;
+	struct nss_nlcapwap_rule *nl_rule;
+	struct nss_nlcmn *nl_cm;
+
+	/*
+	 * extract the message payload
+	 */
+	nl_cm = nss_nl_get_msg(&nss_nlcapwap_family, info, NSS_NLCAPWAP_CMD_TYPE_META_HEADER);
+	if (!nl_cm) {
+		nss_nl_error("Unable to extract meta header values.\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Message validation required before accepting the configuration
+	 */
+	nl_rule = container_of(nl_cm, struct nss_nlcapwap_rule, cm);
+
+	/*
+	 * Set meta header values
+	 */
+	meta_hdr = nl_rule->msg.meta_header;
+	nss_nlcapwap_set_meta_header(meta_hdr);
+	nss_nl_info("Successfully created meta header.\n");
+	return 0;
+}
+
+/*
+ * nss_nlcapwap_cmd_ops
+ *	Operation table called by the generic netlink layer based on the command
+ */
+struct genl_ops nss_nlcapwap_cmd_ops[] = {
+	{.cmd = NSS_NLCAPWAP_CMD_TYPE_CREATE_TUN, .doit = nss_nlcapwap_ops_create_tun,},
+	{.cmd = NSS_NLCAPWAP_CMD_TYPE_DESTROY_TUN, .doit = nss_nlcapwap_ops_destroy_tun,},
+	{.cmd = NSS_NLCAPWAP_CMD_TYPE_UPDATE_MTU, .doit = nss_nlcapwap_ops_update_mtu,},
+	{.cmd = NSS_NLCAPWAP_CMD_TYPE_DTLS, .doit = nss_nlcapwap_ops_dtls,},
+	{.cmd = NSS_NLCAPWAP_CMD_TYPE_PERF, .doit = nss_nlcapwap_ops_perf,},
+	{.cmd = NSS_NLCAPWAP_CMD_TYPE_TX_PACKETS, .doit = nss_nlcapwap_ops_tx_packets,},
+	{.cmd = NSS_NLCAPWAP_CMD_TYPE_META_HEADER, .doit = nss_nlcapwap_ops_meta_header,},
+	{.cmd = NSS_NLCAPWAP_CMD_TYPE_IP_FLOW, .doit = nss_nlcapwap_ops_ip_flow,},
+};
+
+/*
+ * nss_nlcapwap_get_ifnum()
+ *	Get the interface number corresponding to netdev
+ */
+int nss_nlcapwap_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 -ENODEV;
+	}
+
+	return ifnum;
+}
+
+/*
+ * nss_nlcapwap_init()
+ *	Init handler for capwap
+ */
+bool nss_nlcapwap_init(void)
+{
+	int err;
+
+	nss_nl_info_always("Init NSS netlink capwap handler\n");
+
+	/*
+	 * register NETLINK ops with the family
+	 */
+	err = genl_register_family_with_ops_groups(&nss_nlcapwap_family, nss_nlcapwap_cmd_ops,
+			nss_nlcapwap_family_mcgrp);
+	if (err) {
+		nss_nl_info_always("Error: %d unable to register capwap family\n", err);
+		genl_unregister_family(&nss_nlcapwap_family);
+		return false;
+	}
+
+	/*
+	 * Register the capwap netdev rx handler
+	 */
+	nss_nlcapwap_register_netdev_handler();
+	return true;
+}
+
+/*
+ * nss_nlcapwap_exit()
+ *	Exit handler for capwap
+ */
+bool nss_nlcapwap_exit(void)
+{
+	struct net_device *capwap_ndev;
+	int err, i;
+
+	nss_nl_info_always("Exit NSS netlink capwap handler\n");
+
+	/*
+	 * Unregister the capwap netdev rx handler
+	 */
+	capwap_ndev = nss_capwapmgr_get_netdev();
+	rtnl_lock();
+	netdev_rx_handler_unregister(capwap_ndev);
+	rtnl_unlock();
+
+	/*
+	 * Destroy all the active tunnels
+	 */
+	for (i = 0; i < NSS_CAPWAPMGR_MAX_TUNNELS; i++) {
+		nss_nlcapwap_destroy_tun(i);
+	}
+
+	/*
+	 * unregister the ops family
+	 */
+	err = genl_unregister_family(&nss_nlcapwap_family);
+	if (err) {
+		nss_nl_info_always("Error: %d unable to unregister capwap NETLINK family\n", err);
+		return false;
+	}
+
+	return true;
+}
diff --git a/netlink/nss_nlcapwap.h b/netlink/nss_nlcapwap.h
new file mode 100644
index 0000000..59cb4a8
--- /dev/null
+++ b/netlink/nss_nlcapwap.h
@@ -0,0 +1,77 @@
+/*
+ **************************************************************************
+ * Copyright (c) 2014-2015,2018-2020 The Linux Foundation. All rights reserved.
+ * Permission to use, copy, modify, and/or distribute this software for
+ * any purpose with or without fee is hereby granted, provided that the
+ * above copyright notice and this permission notice appear in all copies.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ **************************************************************************
+ */
+
+/*
+ * nss_nlcapwap.h
+ *	NSS Netlink Capwap API definitions
+ */
+#ifndef __NSS_NLCAPWAP_H
+#define __NSS_NLCAPWAP_H
+
+#include "nss_nlcapwap_if.h"
+
+#define NSS_NLCAPWAP_SKB_TAILROOM 192			/**< Tailroom for skb */
+#define NSS_NLCAPWAP_IP_VERS_4 4			/**< Ip version 4 */
+#define NSS_NLCAPWAP_IP_VERS_6 6			/**< Ip version 6 */
+#define NSS_NLCAPWAP_VLAN_TAG_INVALID 0xFFF		/**< Invalid vlan tag */
+#define NSS_NLCAPWAP_WAN_IFNUM 0			/**< WAN interface number */
+#define NSS_NLCAPWAP_SKB_RESERVE_SZ_TWO 2		/**< skb reserve size */
+#define NSS_NLCAPWAP_SKB_RESERVE_SZ_NINTY_EIGHT 98	/**< skb reserve size */
+#define NSS_NLCAPWAP_SKB_RESERVE_SZ_HUNDRED 100		/**< skb reserve size */
+#define NSS_NLCAPWAP_80211E_ZERO 0			/**< 80211e type */
+#define NSS_NLCAPWAP_80211E_ONE 1			/**< 80211e type */
+#define NSS_NLCAPWAP_DATA 0xcc				/**< Dummy data */
+
+/*
+ * nss_nlcapwap_meta_header_type
+ *	Capwap meta header type
+ */
+enum nss_nlcapwap_meta_header_type {
+	NSS_NLCAPWAP_META_HEADER_TYPE_UNKNOWN = -1,	/**< Unknown meta header type */
+	NSS_NLCAPWAP_META_HEADER_TYPE_ZERO,		/**< capwap meta header type 0 */
+	NSS_NLCAPWAP_META_HEADER_TYPE_ONE,		/**< capwap meta header type 1 */
+	NSS_NLCAPWAP_META_HEADER_TYPE_MAX		/**< Max meta header type */
+};
+
+/*
+ * nss_nlcapwap_global_ctx
+ *	Global context for capwap
+ */
+struct nss_nlcapwap_global_ctx {
+	struct nss_nlcapwap_meta_header meta_header;	/**< Stores meta header of tunnel */
+	int nss_nlcapwap_80211e;			/**< Enable or disable wireless qos */
+};
+
+/*
+ * nss_nlcapwap_ndev_priv
+ *	Netdevice private data
+ */
+struct nss_nlcapwap_ndev_priv {
+	uint32_t capwap_seq;				/**< Sequence number */
+};
+
+bool nss_nlcapwap_init(void);
+bool nss_nlcapwap_exit(void);
+
+#if (CONFIG_NSS_NLCAPWAP == 1)
+#define NSS_NLCAPWAP_INIT nss_nlcapwap_init
+#define NSS_NLCAPWAP_EXIT nss_nlcapwap_exit
+#else
+#define NSS_NLCAPWAP_INIT 0
+#define NSS_NLCAPWAP_EXIT 0
+#endif /* !CONFIG_NSS_NLCAPWAP */
+
+#endif /* __NSS_NLCAPWAP_H */
diff --git a/netlink/nss_nldtls.c b/netlink/nss_nldtls.c
new file mode 100644
index 0000000..0aea037
--- /dev/null
+++ b/netlink/nss_nldtls.c
@@ -0,0 +1,1158 @@
+/*
+ **************************************************************************
+ * Copyright (c) 2015-2016,2018-2020 The Linux Foundation. All rights reserved.
+ * Permission to use, copy, modify, and/or distribute this software for
+ * any purpose with or without fee is hereby granted, provided that the
+ * above copyright notice and this permission notice appear in all copies.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ **************************************************************************
+ */
+
+#include <crypto/internal/skcipher.h>
+#include <linux/etherdevice.h>
+#include <linux/icmp.h>
+#include <linux/inet.h>
+#include <linux/udp.h>
+#include <linux/version.h>
+#include <net/genetlink.h>
+#include <net/ip6_checksum.h>
+#include <net/udp.h>
+#include <nss_api_if.h>
+#include <nss_dtls.h>
+#include <nss_dtlsmgr.h>
+#include <nss_dtls_cmn.h>
+#include <nss_nlcmn_if.h>
+#include <nss_nl_if.h>
+#include "nss_nl.h"
+#include "nss_nldtls.h"
+#include "nss_nldtls_if.h"
+
+/*
+ * Initializing the global variables
+ */
+static struct nss_nldtls_gbl_ctx gbl_ctx = {
+	.lock = __SPIN_LOCK_UNLOCKED(lock),
+	.num_tun = ATOMIC_INIT(0),
+	.dtls_list_head = LIST_HEAD_INIT(gbl_ctx.dtls_list_head),
+	.log_en = false
+};
+
+/*
+ * nss_nldtls_family_mcgrp
+ *	Multicast group for sending message status & events
+ */
+static const struct genl_multicast_group nss_nldtls_family_mcgrp[] = {
+	{.name = NSS_NLDTLS_MCAST_GRP},
+};
+
+/*
+ * nss_nldtls_family
+ *	Dtls family definition
+ */
+struct genl_family nss_nldtls_family = {
+	.id = GENL_ID_GENERATE,				/* Auto generate ID */
+	.name = NSS_NLDTLS_FAMILY,			/* family name string */
+	.hdrsize = sizeof(struct nss_nldtls_rule),	/* NSS NETLINK dtls rule */
+	.version = NSS_NL_VER,				/* Set it to NSS_NLDTLS version */
+	.maxattr = NSS_NLDTLS_CMD_TYPE_MAX,		/* maximum commands supported */
+	.netnsok = true,
+	.pre_doit = NULL,
+	.post_doit = NULL,
+};
+
+/*
+ * nss_nldtls_find_dtls_tun_gbl_ctx()
+ *	Returns the global context object of a tunnel
+ */
+static struct nss_nldtls_tun_ctx *nss_nldtls_find_dtls_tun_gbl_ctx(struct net_device *dev)
+{
+	struct nss_nldtls_tun_ctx *entry;
+
+	spin_lock(&gbl_ctx.lock);
+	list_for_each_entry(entry, &gbl_ctx.dtls_list_head, list) {
+		if (!strncmp(entry->dev_name, dev->name, IFNAMSIZ)) {
+			spin_unlock(&gbl_ctx.lock);
+			return entry;
+		}
+	}
+
+	spin_unlock(&gbl_ctx.lock);
+	return NULL;
+}
+
+/*
+ * nss_nldtls_data_cb()
+ *	Data callback function for dtls
+ */
+static void __maybe_unused nss_nldtls_data_cb(void *app_data __maybe_unused, struct sk_buff *skb __maybe_unused)
+{
+	static bool first_pkt;
+	unsigned long long duration;
+	ktime_t delta;
+
+	if (unlikely(!first_pkt)) {
+		gbl_ctx.first_rx_pkt_time = ktime_get();
+		first_pkt = true;
+	}
+
+	/*
+	 * Remove meta header
+	 */
+	skb_pull(skb, sizeof(struct nss_dtlsmgr_metadata));
+	gbl_ctx.last_rx_pkt_time = ktime_get();
+
+	if (unlikely(gbl_ctx.log_en)) {
+		struct net_device *dev;
+
+		delta = ktime_sub(gbl_ctx.last_rx_pkt_time, gbl_ctx.first_rx_pkt_time);
+		duration = (unsigned long long) ktime_to_ns(delta) >> 10;
+		print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, skb->data, 32);
+		dev = dev_get_by_index(&init_net, skb->skb_iif);
+		if (dev) {
+			nss_nl_error("In dev = %s, out_dev = %s\n", dev->name, skb->dev->name);
+			dev_put(dev);
+		}
+
+		nss_nl_info("%p: DTLS RX (%s) pkt len = %d udp_csum = %s rx_time: %llu\n", skb,
+				skb->dev->name, skb->len, udp_lib_checksum_complete(skb) ?
+				"invalid" : "valid", duration);
+	}
+
+	dev_kfree_skb_any(skb);
+}
+
+/*
+ * nss_nldtls_dev_rx_handler()
+ *	Common rx handler for all dtls dev
+ */
+static rx_handler_result_t nss_nldtls_dev_rx_handler(struct sk_buff **pskb)
+{
+	static bool first_pkt;
+	unsigned long long duration;
+	struct sk_buff *skb = *pskb;
+	ktime_t delta;
+
+	if (unlikely(!first_pkt)) {
+		gbl_ctx.first_rx_pkt_time = ktime_get();
+		first_pkt = true;
+	}
+
+	gbl_ctx.last_rx_pkt_time = ktime_get();
+	if (unlikely(gbl_ctx.log_en)) {
+		struct net_device *dev;
+
+		delta = ktime_sub(gbl_ctx.last_rx_pkt_time, gbl_ctx.first_rx_pkt_time);
+		duration = (unsigned long long) ktime_to_ns(delta) >> 10;
+		print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, skb->data, 64);
+		dev = dev_get_by_index(&init_net, skb->skb_iif);
+		if (dev) {
+			nss_nl_info("%p: in_dev = %s, out_dev = %s\n", skb, dev->name, skb->dev->name);
+			dev_put(dev);
+		}
+
+		nss_nl_info("%p: DTLS RX (%s) pkt len = %d udp_csum = %s rx_time: %llu\n", skb,
+				skb->dev->name, skb->len, udp_lib_checksum_complete(skb) ?
+				"invalid" : "valid", duration);
+	}
+
+	dev_kfree_skb_any(skb);
+	return RX_HANDLER_CONSUMED;
+}
+
+/*
+ * nss_nldtls_create_ipv4_rule()
+ *	Create a nss entry to accelerate the given IPv4 connection
+ */
+static int nss_nldtls_create_ipv4_rule(struct nss_ipv4_create *unic, uint16_t rule_flags)
+{
+	struct nss_ipv4_rule_create_msg *nircm;
+	struct nss_ctx_instance *nss_ctx;
+	struct nss_ipv4_msg nim;
+	nss_tx_status_t status;
+
+	nss_ctx = nss_ipv4_get_mgr();
+	if (!nss_ctx) {
+		nss_nl_info("%p: Couldn't get IPv4 ctx\n", unic);
+		return -1;
+	}
+
+	nss_nl_info("%p: IPv4 rule: src:%pI4h:%d dst:%pI4h:%d p:%d\n",
+	      unic, &unic->src_ip, unic->src_port,
+	      &unic->dest_ip, unic->dest_port, unic->protocol);
+
+	memset(&nim, 0, sizeof (struct nss_ipv4_msg));
+	nss_ipv4_msg_init(&nim, NSS_IPV4_RX_INTERFACE,
+			  NSS_IPV4_TX_CREATE_RULE_MSG,
+			  sizeof(struct nss_ipv4_rule_create_msg), NULL, NULL);
+
+	nircm = &nim.msg.rule_create;
+	nircm->valid_flags = 0;
+	nircm->rule_flags = 0;
+
+	/*
+	 * Copy over the 5 tuple details.
+	 */
+	nircm->tuple.protocol = (uint8_t)unic->protocol;
+	nircm->tuple.flow_ip = unic->src_ip;
+	nircm->tuple.flow_ident = (uint32_t)unic->src_port;
+	nircm->tuple.return_ip = unic->dest_ip;
+	nircm->tuple.return_ident = (uint32_t)unic->dest_port;
+
+	/*
+	 * Copy over the connection rules and set the CONN_VALID flag
+	 */
+	nircm->conn_rule.flow_interface_num = unic->src_interface_num;
+	nircm->conn_rule.flow_mtu = unic->from_mtu;
+	nircm->conn_rule.flow_ip_xlate = unic->src_ip_xlate;
+	nircm->conn_rule.flow_ident_xlate = (uint32_t)unic->src_port_xlate;
+	memcpy(nircm->conn_rule.flow_mac, unic->src_mac, 6);
+	nircm->conn_rule.return_interface_num = unic->dest_interface_num;
+	nircm->conn_rule.return_mtu = unic->to_mtu;
+	nircm->conn_rule.return_ip_xlate = unic->dest_ip_xlate;
+	nircm->conn_rule.return_ident_xlate = (uint32_t)unic->dest_port_xlate;
+	if ((nircm->tuple.return_ip != nircm->conn_rule.return_ip_xlate) ||
+	    (nircm->tuple.return_ident != nircm->conn_rule.return_ident_xlate))
+		memcpy(nircm->conn_rule.return_mac, unic->dest_mac_xlate, 6);
+	else
+		memcpy(nircm->conn_rule.return_mac, unic->dest_mac, 6);
+
+	/*
+	 * Copy over the DSCP rule parameters
+	 */
+	if (unic->flags & NSS_IPV4_CREATE_FLAG_DSCP_MARKING) {
+		nircm->dscp_rule.flow_dscp = unic->flow_dscp;
+		nircm->dscp_rule.return_dscp = unic->return_dscp;
+		nircm->rule_flags |= NSS_IPV4_RULE_CREATE_FLAG_DSCP_MARKING;
+		nircm->valid_flags |= NSS_IPV4_RULE_CREATE_DSCP_MARKING_VALID;
+	}
+
+	nircm->valid_flags |= NSS_IPV4_RULE_CREATE_CONN_VALID;
+
+	/*
+	 * Copy over the pppoe rules and set the PPPOE_VALID flag.
+	 */
+	nircm->pppoe_rule.flow_if_exist = unic->flow_pppoe_if_exist;
+	nircm->pppoe_rule.flow_if_num = unic->flow_pppoe_if_num;
+	nircm->pppoe_rule.return_if_exist = unic->return_pppoe_if_exist;
+	nircm->pppoe_rule.return_if_num = unic->return_pppoe_if_num;
+	nircm->valid_flags |= NSS_IPV4_RULE_CREATE_PPPOE_VALID;
+
+	/*
+	 * Copy over the vlan rules and set the VLAN_VALID flag
+	 */
+	nircm->vlan_primary_rule.ingress_vlan_tag = unic->in_vlan_tag[0];
+	nircm->vlan_primary_rule.egress_vlan_tag = unic->out_vlan_tag[0];
+	nircm->vlan_secondary_rule.ingress_vlan_tag = unic->in_vlan_tag[1];
+	nircm->vlan_secondary_rule.egress_vlan_tag = unic->out_vlan_tag[1];
+	nircm->valid_flags |= NSS_IPV4_RULE_CREATE_VLAN_VALID;
+
+	/*
+	 * Copy over the qos rules and set the QOS_VALID flag
+	 */
+	nircm->qos_rule.flow_qos_tag = unic->flow_qos_tag;
+	nircm->qos_rule.return_qos_tag = unic->return_qos_tag;
+	nircm->valid_flags |= NSS_IPV4_RULE_CREATE_QOS_VALID;
+
+	if (unic->flags & NSS_IPV4_CREATE_FLAG_NO_SEQ_CHECK)
+		nircm->rule_flags |= NSS_IPV4_RULE_CREATE_FLAG_NO_SEQ_CHECK;
+
+	if (unic->flags & NSS_IPV4_CREATE_FLAG_BRIDGE_FLOW)
+		nircm->rule_flags |= NSS_IPV4_RULE_CREATE_FLAG_BRIDGE_FLOW;
+
+	if (unic->flags & NSS_IPV4_CREATE_FLAG_ROUTED)
+		nircm->rule_flags |= NSS_IPV4_RULE_CREATE_FLAG_ROUTED;
+
+	/*
+	 * Set the flag NSS_IPV4_RULE_CREATE_FLAG_ICMP_NO_CME_FLUSH so that
+	 * rule is not flushed when NSS FW receives ICMP errors/packets.
+	 */
+	nircm->rule_flags |= NSS_IPV4_RULE_CREATE_FLAG_ICMP_NO_CME_FLUSH;
+
+	/*
+	 * Add any other additional flags which caller has requested.
+	 * For example: update MTU
+	 */
+	nircm->rule_flags |= rule_flags;
+
+	status = nss_ipv4_tx(nss_ctx, &nim);
+	if (status != NSS_TX_SUCCESS) {
+		nss_nl_info("%p: Create IPv4 message failed %d\n", nss_ctx, status);
+	}
+
+	return 0;
+}
+
+/*
+ * nss_nldtls_create_ipv6_rule()
+ *	Create a nss entry to accelerate the given IPV6 connection
+ */
+static int nss_nldtls_create_ipv6_rule(struct nss_ipv6_create *unic, uint16_t rule_flags)
+{
+	struct nss_ipv6_rule_create_msg *nircm;
+	struct nss_ctx_instance *nss_ctx;
+	struct nss_ipv6_msg nim;
+	nss_tx_status_t status;
+
+	nss_ctx = nss_ipv6_get_mgr();
+	if (!nss_ctx) {
+		nss_nl_info("%p: Couldn't get IPv6 ctx\n", unic);
+		return -1;
+	}
+
+	nss_nl_info("%p: Create IPv6 rule: %pI6:%d %pI6:%d p:%d\n",
+	      unic, unic->src_ip, unic->src_port, unic->dest_ip,
+	      unic->dest_port, unic->protocol);
+
+	memset(&nim, 0, sizeof (struct nss_ipv6_msg));
+	nss_ipv6_msg_init(&nim, NSS_IPV6_RX_INTERFACE,
+			  NSS_IPV6_TX_CREATE_RULE_MSG,
+			  sizeof(struct nss_ipv6_rule_create_msg), NULL, NULL);
+
+	nircm = &nim.msg.rule_create;
+	nircm->rule_flags = 0;
+	nircm->valid_flags = 0;
+
+	/*
+	 * Copy over the 5 tuple information.
+	 */
+	nircm->tuple.protocol = (uint8_t)unic->protocol;
+	memcpy(nircm->tuple.flow_ip, unic->src_ip, sizeof(nircm->tuple.flow_ip));
+	memcpy(nircm->tuple.return_ip, unic->dest_ip, sizeof(nircm->tuple.return_ip));
+	nircm->tuple.flow_ident = (uint32_t)unic->src_port;
+	nircm->tuple.return_ident = (uint32_t)unic->dest_port;
+
+	/*
+	 * Copy over the connection rules and set CONN_VALID flag
+	 */
+	nircm->conn_rule.flow_interface_num = unic->src_interface_num;
+	nircm->conn_rule.flow_mtu = unic->from_mtu;
+	nircm->conn_rule.return_interface_num = unic->dest_interface_num;
+	nircm->conn_rule.return_mtu = unic->to_mtu;
+	memcpy(nircm->conn_rule.flow_mac, unic->src_mac, 6);
+	memcpy(nircm->conn_rule.return_mac, unic->dest_mac, 6);
+	nircm->valid_flags |= NSS_IPV6_RULE_CREATE_CONN_VALID;
+
+	/*
+	 * Copy over the DSCP rule parameters
+	 */
+	if (unic->flags & NSS_IPV6_CREATE_FLAG_DSCP_MARKING) {
+		nircm->dscp_rule.flow_dscp = unic->flow_dscp;
+		nircm->dscp_rule.return_dscp = unic->return_dscp;
+		nircm->rule_flags |= NSS_IPV6_RULE_CREATE_FLAG_DSCP_MARKING;
+		nircm->valid_flags |= NSS_IPV6_RULE_CREATE_DSCP_MARKING_VALID;
+	}
+
+	/*
+	 * Copy over the pppoe rules and set PPPOE_VALID flag
+	 */
+	nircm->pppoe_rule.flow_if_exist = unic->flow_pppoe_if_exist;
+	nircm->pppoe_rule.flow_if_num = unic->flow_pppoe_if_num;
+	nircm->pppoe_rule.return_if_exist = unic->return_pppoe_if_exist;
+	nircm->pppoe_rule.return_if_num = unic->return_pppoe_if_num;
+	nircm->valid_flags |= NSS_IPV6_RULE_CREATE_PPPOE_VALID;
+
+	/*
+	 * Copy over the tcp rules and set TCP_VALID flag
+	 */
+	nircm->tcp_rule.flow_window_scale = unic->flow_window_scale;
+	nircm->tcp_rule.flow_max_window = unic->flow_max_window;
+	nircm->tcp_rule.flow_end = unic->flow_end;
+	nircm->tcp_rule.flow_max_end = unic->flow_max_end;
+	nircm->tcp_rule.return_window_scale = unic->return_window_scale;
+	nircm->tcp_rule.return_max_window = unic->return_max_window;
+	nircm->tcp_rule.return_end = unic->return_end;
+	nircm->tcp_rule.return_max_end = unic->return_max_end;
+	nircm->valid_flags |= NSS_IPV6_RULE_CREATE_TCP_VALID;
+
+	/*
+	 * Copy over the vlan rules and set the VLAN_VALID flag
+	 */
+	nircm->vlan_primary_rule.egress_vlan_tag = unic->out_vlan_tag[0];
+	nircm->vlan_primary_rule.ingress_vlan_tag = unic->in_vlan_tag[0];
+	nircm->vlan_secondary_rule.egress_vlan_tag = unic->out_vlan_tag[1];
+	nircm->vlan_secondary_rule.ingress_vlan_tag = unic->in_vlan_tag[1];
+	nircm->valid_flags |= NSS_IPV6_RULE_CREATE_VLAN_VALID;
+
+	/*
+	 * Copy over the qos rules and set the QOS_VALID flag
+	 */
+	nircm->qos_rule.flow_qos_tag = unic->flow_qos_tag;
+	nircm->qos_rule.return_qos_tag = unic->return_qos_tag;
+	nircm->valid_flags |= NSS_IPV6_RULE_CREATE_QOS_VALID;
+
+	if (unic->flags & NSS_IPV6_CREATE_FLAG_NO_SEQ_CHECK)
+		nircm->rule_flags |= NSS_IPV6_RULE_CREATE_FLAG_NO_SEQ_CHECK;
+
+	if (unic->flags & NSS_IPV6_CREATE_FLAG_BRIDGE_FLOW)
+		nircm->rule_flags |= NSS_IPV6_RULE_CREATE_FLAG_BRIDGE_FLOW;
+
+	if (unic->flags & NSS_IPV6_CREATE_FLAG_ROUTED)
+		nircm->rule_flags |= NSS_IPV6_RULE_CREATE_FLAG_ROUTED;
+
+	/*
+	 * Set the flag NSS_IPV4_RULE_CREATE_FLAG_ICMP_NO_CME_FLUSH so that
+	 * rule is not flushed when NSS FW receives ICMP errors/packets.
+	 */
+	nircm->rule_flags |= NSS_IPV4_RULE_CREATE_FLAG_ICMP_NO_CME_FLUSH;
+
+	/*
+	 * Add any other additional flags which caller has requested.
+	 * For example: update MTU
+	 */
+	nircm->rule_flags |= rule_flags;
+
+	status = nss_ipv6_tx(nss_ctx, &nim);
+	if (status != NSS_TX_SUCCESS) {
+		nss_nl_info("%p: Create IPv4 message failed %d\n", nss_ctx, status);
+	}
+
+	return 0;
+}
+
+/*
+ * nss_nldtls_create_session()
+ *	Create a DTLS session through dtlsmgr driver API.
+ */
+static struct net_device *nss_nldtls_create_session(struct nss_nldtls_rule *nl_rule, uint32_t flags)
+{
+	struct nss_nldtls_tun_ctx *dtls_tun_data;
+	struct nss_dtlsmgr_config dcfg;
+	struct nss_dtlsmgr_ctx *ctx;
+	struct net_device *ndev;
+	uint8_t algo;
+	int err;
+
+	if (atomic_read(&gbl_ctx.num_tun) >= NSS_NLDTLS_MAX_TUNNELS) {
+		nss_nl_error("Max number of tunnels exhausted: 32\n");
+		return NULL;
+	}
+
+	memset(&dcfg, 0, sizeof(struct nss_dtlsmgr_config));
+	algo = nl_rule->msg.create.encap.crypto.algo;
+	dcfg.flags = flags;
+	if (algo == NSS_DTLSMGR_ALGO_AES_GCM)
+		dcfg.flags |= NSS_DTLSMGR_CIPHER_MODE_GCM;
+
+	dcfg.app_data = NULL;
+	dcfg.notify = NULL;
+	dcfg.data = NULL;
+
+	/*
+	 * Encap configuration
+	 */
+	memcpy((void *)&dcfg.encap, (void *)&nl_rule->msg.create.encap, sizeof(struct nss_dtlsmgr_encap_config));
+
+	/*
+	 * Decap configuration
+	 */
+	memcpy((void *)&dcfg.decap, (void *)&nl_rule->msg.create.decap, sizeof(struct nss_dtlsmgr_decap_config));
+	dcfg.decap.nexthop_ifnum = NSS_N2H_INTERFACE;
+
+	/*
+	 * Create a dtls session
+	 */
+	ndev = nss_dtlsmgr_session_create(&dcfg);
+	if (!ndev) {
+		nss_nl_error("Failed to create DTLS session\n");
+		return NULL;
+	}
+
+	ctx = netdev_priv(ndev);
+	/*
+	 * Register rx handler for dtls netdev
+	 */
+	rtnl_lock();
+	err = netdev_rx_handler_register(ndev, nss_nldtls_dev_rx_handler, 0);
+	if (err) {
+		rtnl_unlock();
+		nss_dtlsmgr_session_destroy(ndev);
+		nss_nl_error("%p: Failed to register rx handler\n", ctx);
+		return NULL;
+	}
+
+	rtnl_unlock();
+
+	/*
+	 * Prepare data for current tunnel
+	 */
+	dtls_tun_data = (struct nss_nldtls_tun_ctx *)kmalloc(sizeof(*dtls_tun_data), GFP_KERNEL);
+	dtls_tun_data->nl_rule = nl_rule;
+	memcpy(dtls_tun_data->dev_name, ndev->name, IFNAMSIZ);
+
+	/*
+	 * Adding tunnel to global list of tunnels
+	 */
+	spin_lock(&gbl_ctx.lock);
+	list_add_tail(&dtls_tun_data->list, &gbl_ctx.dtls_list_head);
+	spin_unlock(&gbl_ctx.lock);
+
+	nss_nl_info("%p: Succesfully created dtls session.\n", ctx);
+	return ndev;
+}
+
+/*
+ * nss_nldtls_create_ipv4_rule_entry()
+ *	Handler for adding ipv4 rule entry for dtls session
+ */
+static int nss_nldtls_create_ipv4_rule_entry(struct net_device *dtls_dev, struct nss_nldtls_rule *nl_rule)
+{
+	struct nss_ipv4_create ipv4;
+	struct net_device *ndev;
+	uint32_t if_num;
+
+	/*
+	 * Configure IPv4 rule
+	 */
+	memset(&ipv4, 0, sizeof(struct nss_ipv4_create));
+
+	ipv4.from_mtu = nl_rule->msg.create.from_mtu;
+	ipv4.to_mtu = nl_rule->msg.create.to_mtu;
+
+	ndev = dev_get_by_name(&init_net, &nl_rule->msg.create.gmac_ifname[0]);
+	if (ndev == NULL) {
+		nss_nl_error("Can't find %s netdev\n", nl_rule->msg.create.gmac_ifname);
+		return -EINVAL;
+	}
+
+	if_num = nss_cmn_get_interface_number_by_dev(ndev);
+	ipv4.src_interface_num = if_num;
+	ipv4.dest_interface_num = nss_dtlsmgr_get_interface(dtls_dev, NSS_DTLSMGR_INTERFACE_TYPE_OUTER);
+
+	ipv4.src_port = nl_rule->msg.create.encap.dport;
+	ipv4.src_port_xlate = nl_rule->msg.create.encap.dport;
+	ipv4.src_ip = nl_rule->msg.create.encap.dip[0];
+	ipv4.src_ip_xlate = nl_rule->msg.create.encap.dip[0];
+
+	ipv4.dest_ip = nl_rule->msg.create.encap.sip[0];
+	ipv4.dest_ip_xlate = nl_rule->msg.create.encap.sip[0];
+	ipv4.dest_port = nl_rule->msg.create.encap.sport;
+	ipv4.dest_port_xlate = nl_rule->msg.create.encap.sport;
+
+	ipv4.protocol = IPPROTO_UDP;
+	ipv4.in_vlan_tag[0] = NSS_NLDTLS_VLAN_INVALID;
+	ipv4.out_vlan_tag[0] = NSS_NLDTLS_VLAN_INVALID;
+	ipv4.in_vlan_tag[1] = NSS_NLDTLS_VLAN_INVALID;
+	ipv4.out_vlan_tag[1] = NSS_NLDTLS_VLAN_INVALID;
+
+	memcpy(&ipv4.src_mac[0], &nl_rule->msg.create.gmac_ifmac[0], sizeof(ipv4.src_mac));
+
+	/*
+	 * Create an ipv4 rule entry
+	 */
+	return nss_nldtls_create_ipv4_rule(&ipv4, 0);
+}
+
+/*
+ * nss_nldtls_create_ipv6_rule_entry()
+ *	Handler to add an ipv6 rule entry for dtls session
+ */
+static int nss_nldtls_create_ipv6_rule_entry(struct net_device *dtls_dev, struct nss_nldtls_rule *nl_rule)
+{
+	struct nss_ipv6_create ipv6;
+	struct net_device *ndev;
+	uint32_t if_num;
+
+	/*
+	 * Configure IPv6 rule
+	 */
+	memset(&ipv6, 0, sizeof(struct nss_ipv6_create));
+
+	ipv6.from_mtu = nl_rule->msg.create.from_mtu;
+	ipv6.to_mtu = nl_rule->msg.create.to_mtu;
+
+	ndev = dev_get_by_name(&init_net, &nl_rule->msg.create.gmac_ifname[0]);
+	if (ndev == NULL) {
+		nss_nl_error("Can't find %s netdev\n", nl_rule->msg.create.gmac_ifname);
+		return -EINVAL;
+	}
+
+	if_num = nss_cmn_get_interface_number_by_dev(ndev);
+	ipv6.src_interface_num = if_num;
+	ipv6.dest_interface_num = nss_dtlsmgr_get_interface(dtls_dev, NSS_DTLSMGR_INTERFACE_TYPE_OUTER);
+	ipv6.src_port = nl_rule->msg.create.encap.dport;
+	ipv6.dest_port = nl_rule->msg.create.encap.sport;
+
+	/*
+	 * Configure IPv6 rule
+	 */
+	memcpy(ipv6.src_ip, nl_rule->msg.create.encap.dip, sizeof(ipv6.src_ip));
+	memcpy(ipv6.dest_ip, nl_rule->msg.create.encap.sip, sizeof(ipv6.dest_ip));
+	ipv6.protocol = IPPROTO_UDP;
+
+	ipv6.in_vlan_tag[0] = NSS_NLDTLS_VLAN_INVALID;
+	ipv6.in_vlan_tag[1] = NSS_NLDTLS_VLAN_INVALID;
+	ipv6.out_vlan_tag[1] = NSS_NLDTLS_VLAN_INVALID;
+	ipv6.out_vlan_tag[0] = NSS_NLDTLS_VLAN_INVALID;
+
+	memcpy(&ipv6.src_mac[0], &nl_rule->msg.create.gmac_ifmac[0], sizeof(ipv6.src_mac));
+
+	/*
+	 * Create an ipv6 rule entry
+	 */
+	return nss_nldtls_create_ipv6_rule(&ipv6, 0);
+}
+
+/*
+ * nss_nldtls_destroy_tun()
+ *	Common handler for tunnel destroy
+ */
+static int nss_nldtls_destroy_tun(struct net_device *dtls_ndev)
+{
+	struct nss_nldtls_tun_ctx *dtls_tun_data;
+
+	dtls_tun_data = nss_nldtls_find_dtls_tun_gbl_ctx(dtls_ndev);
+	if (!dtls_tun_data) {
+		nss_nl_error("Unable to find context of the tunnel: %s\n", dtls_ndev->name);
+		dev_put(dtls_ndev);
+		return -EAGAIN;
+	}
+
+	/*
+	 * Delete tunnel node from the list
+	 */
+	list_del_init(&dtls_tun_data->list);
+	kfree(dtls_tun_data);
+	dev_put(dtls_ndev);
+
+	/*
+	 * Destroy the dtls session
+	 */
+	if (nss_dtlsmgr_session_destroy(dtls_ndev)) {
+		nss_nl_error("Unable to destroy the tunnel: %s\n", dtls_ndev->name);
+		return -EAGAIN;
+	}
+
+	return 0;
+}
+
+/*
+ * nss_nldtls_ops_create_tun()
+ *	Handler for creating tunnel
+ */
+static int nss_nldtls_ops_create_tun(struct sk_buff *skb, struct genl_info *info)
+{
+	struct nss_nldtls_rule *nl_rule;
+	struct net_device *dtls_dev;
+	struct nss_nlcmn *nl_cm;
+	int ret = 0;
+
+	/*
+	 * Extract the message payload
+	 */
+	nl_cm = nss_nl_get_msg(&nss_nldtls_family, info, NSS_NLDTLS_CMD_TYPE_CREATE_TUN);
+	if (!nl_cm) {
+		nss_nl_error("%p: Unable to extract create tunnel data\n", skb);
+		return -EINVAL;
+	}
+
+	/*
+	 * Message validation required before accepting the configuration
+	 */
+	nl_rule = container_of(nl_cm, struct nss_nldtls_rule, cm);
+
+	/*
+	 * Create tunnel based on ip version
+	 */
+	if (nl_rule->msg.create.ip_version == NSS_NLDTLS_IP_VERS_4) {
+		dtls_dev = nss_nldtls_create_session(nl_rule, NSS_NLDTLS_IPV4_SESSION);
+		if (!dtls_dev) {
+			nss_nl_error("%p: Unable to create dtls session for v4\n", skb);
+			return -EINVAL;
+		}
+
+		/*
+		 * Create IPv4 rule entry
+		 */
+		ret = nss_nldtls_create_ipv4_rule_entry(dtls_dev, nl_rule);
+		if (ret < 0) {
+			nss_nldtls_destroy_tun(dtls_dev);
+			nss_nl_error("%p Unable to add a rule entry for ipv4.\n", skb);
+			return -EAGAIN;
+		}
+
+		atomic_inc(&gbl_ctx.num_tun);
+		nss_nl_info("%p: Successfully created ipv4 dtls tunnel\n", skb);
+	} else {
+		dtls_dev = nss_nldtls_create_session(nl_rule, NSS_DTLSMGR_HDR_IPV6);
+		if (!dtls_dev) {
+			nss_nl_error("%p: Unable to create dtls session for v6\n", skb);
+			return -EINVAL;
+		}
+
+		/*
+		 * Create IPv6 rule entry
+		 */
+		ret = nss_nldtls_create_ipv6_rule_entry(dtls_dev, nl_rule);
+		if (ret < 0) {
+			nss_nldtls_destroy_tun(dtls_dev);
+			nss_nl_error("%p: Unable to add a rule entry for ipv6.\n", skb);
+			return -EAGAIN;
+		}
+
+		atomic_inc(&gbl_ctx.num_tun);
+		nss_nl_info("%p: Successfully created ipv6 dtls tunnel\n", skb);
+	}
+
+	return 0;
+}
+
+/*
+ * nss_nldtls_ops_destroy_tun()
+ *	Handler to destroy tunnel
+ */
+static int nss_nldtls_ops_destroy_tun(struct sk_buff *skb, struct genl_info *info)
+{
+	struct nss_nldtls_rule *nl_rule;
+	struct net_device *dtls_ndev;
+	struct nss_nlcmn *nl_cm;
+	int ret;
+
+	/*
+	 * Extract the message payload
+	 */
+	nl_cm = nss_nl_get_msg(&nss_nldtls_family, info, NSS_NLDTLS_CMD_TYPE_DESTROY_TUN);
+	if (!nl_cm) {
+		nss_nl_error("%p: Unable to extract destroy tunnel data\n", skb);
+		return -EINVAL;
+	}
+
+	/*
+	 * Message validation required before accepting the configuration
+	 */
+	nl_rule = container_of(nl_cm, struct nss_nldtls_rule, cm);
+
+	dtls_ndev = dev_get_by_name(&init_net, nl_rule->msg.destroy.dev_name);
+	if (!dtls_ndev) {
+		nss_nl_error("%p: Unable to find dev: %s\n", skb, nl_rule->msg.destroy.dev_name);
+		return -EINVAL;
+	}
+
+	/*
+	 * Common dtls handler for tunnel destroy
+	 */
+	ret = nss_nldtls_destroy_tun(dtls_ndev);
+	if (ret < 0) {
+		nss_nl_error("%p: Unable to destroy tunnel: %s\n", skb, dtls_ndev->name);
+		return -EAGAIN;
+	}
+
+	atomic_dec(&gbl_ctx.num_tun);
+	nss_nl_info("%p: Successfully destroyed dtls session: %s\n", skb, nl_rule->msg.destroy.dev_name);
+	return 0;
+}
+
+/*
+ * nss_nldtls_ops_update_config()
+ *	Handler for updating configuration command
+ */
+static int nss_nldtls_ops_update_config(struct sk_buff *skb, struct genl_info *info)
+{
+	struct nss_nldtls_tun_ctx *dtls_tun_data;
+	struct nss_dtlsmgr_config_update dcfg;
+	struct nss_nldtls_rule *nl_rule;
+	struct net_device *dtls_ndev;
+	struct nss_dtlsmgr_ctx *ctx;
+	nss_dtlsmgr_status_t status;
+	struct nss_nlcmn *nl_cm;
+
+	/*
+	 * extract the message payload
+	 */
+	nl_cm = nss_nl_get_msg(&nss_nldtls_family, info, NSS_NLDTLS_CMD_TYPE_UPDATE_CONFIG);
+	if (!nl_cm) {
+		nss_nl_error("%p: Unable to extract update_config data.\n", skb);
+		return -EINVAL;
+	}
+
+	/*
+	 * Message validation required before accepting the configuration
+	 */
+	nl_rule = container_of(nl_cm, struct nss_nldtls_rule, cm);
+
+	dtls_ndev = dev_get_by_name(&init_net, nl_rule->msg.update_config.dev_name);
+	if (!dtls_ndev) {
+		nss_nl_error("%p: Unable to find dev: %s\n", skb, nl_rule->msg.update_config.dev_name);
+		return -EINVAL;
+	}
+
+	ctx = netdev_priv(dtls_ndev);
+	dtls_tun_data = nss_nldtls_find_dtls_tun_gbl_ctx(dtls_ndev);
+	if (!dtls_tun_data) {
+		nss_nl_error("%p: Unable to find context of the tunnel: %s\n", ctx, dtls_ndev->name);
+		dev_put(dtls_ndev);
+		return -EAGAIN;
+	}
+
+	/*
+	 * Configure the dtls configuration
+	 */
+	dcfg.crypto.algo = nl_rule->msg.update_config.config_update.crypto.algo;
+	dcfg.crypto.cipher_key.data = nl_rule->msg.update_config.config_update.crypto.cipher_key.data;
+	dcfg.crypto.cipher_key.len = nl_rule->msg.update_config.config_update.crypto.cipher_key.len;
+	dcfg.crypto.auth_key.data = nl_rule->msg.update_config.config_update.crypto.auth_key.data;
+	dcfg.crypto.auth_key.len = nl_rule->msg.update_config.config_update.crypto.auth_key.len;
+	dcfg.crypto.nonce.data = nl_rule->msg.update_config.config_update.crypto.nonce.data;
+	dcfg.crypto.nonce.len = nl_rule->msg.update_config.config_update.crypto.nonce.len;
+	dcfg.epoch = nl_rule->msg.update_config.config_update.epoch;
+	dcfg.window_size = nl_rule->msg.update_config.config_update.window_size;
+	if (!nl_rule->msg.update_config.dir) {
+		status = nss_dtlsmgr_session_update_encap(dtls_ndev, &dcfg);
+		if (status != NSS_DTLSMGR_OK) {
+			nss_nl_error("%p: Unable to update encap configuration\n", ctx);
+			dev_put(dtls_ndev);
+			return -EINVAL;
+		}
+
+		nss_nl_info("%p: Successfully update the encap configuration\n", ctx);
+	} else {
+		status = nss_dtlsmgr_session_update_decap(dtls_ndev, &dcfg);
+		if (status != NSS_DTLSMGR_OK) {
+			nss_nl_error("%p: Unable to update decap configuration\n", ctx);
+			dev_put(dtls_ndev);
+			return -EINVAL;
+		}
+
+		nss_nl_info("%p: Successfully update the decap configuration\n", ctx);
+	}
+
+	/*
+	 * Update the tun data configuration
+	 */
+	dtls_tun_data->nl_rule = nl_rule;
+	return 0;
+}
+
+/*
+ * nss_nldtls_construct_ipv4_udp_header()
+ *	Creates an ipv4 + udp packet
+ */
+static struct sk_buff *nss_nldtls_construct_ipv4_udp_header(struct net_device *dev, struct nss_nldtls_rule *nl_rule)
+{
+	struct nss_nldtls_tun_ctx *tun_data;
+	struct nss_nldtls_rule *dtls_rule;
+	uint16_t hroom, troom;
+	struct sk_buff *skb;
+	struct udphdr *uh;
+	struct iphdr *iph;
+
+	/*
+	 * Get the tun data
+	 */
+	tun_data = nss_nldtls_find_dtls_tun_gbl_ctx(dev);
+	dtls_rule = tun_data->nl_rule;
+	hroom = dev->needed_headroom;
+	troom = dev->needed_tailroom;
+	skb = dev_alloc_skb(nl_rule->msg.tx_pkts.pkt_sz + hroom + troom);
+	if (!skb) {
+		nss_nl_info("Failed to allocate skb\n");
+		return NULL;
+	}
+
+	skb_reserve(skb, sizeof(struct udphdr) + sizeof(struct iphdr));
+	skb_put(skb, nl_rule->msg.tx_pkts.pkt_sz);
+
+	/*
+	 * Fill the packet with dummy data
+	 */
+	memset(skb->data, NSS_NLDTLS_DUMMY_DATA, skb->len);
+
+	/*
+	 * Fill udp header fields
+	 */
+	skb_push(skb, sizeof(struct udphdr));
+	uh = (struct udphdr *)skb->data;
+	uh->source = htons(dtls_rule->msg.create.encap.sport);
+	uh->dest = htons(dtls_rule->msg.create.encap.dport);
+	uh->len = htons(skb->len);
+	uh->check = 0;
+
+	/*
+	 * Fill IP header fields
+	 */
+	skb_push(skb, sizeof(struct iphdr));
+	iph = (struct iphdr *)skb->data;
+	iph->ihl = 5;
+	iph->version = 4;
+	iph->tot_len = (nl_rule->msg.tx_pkts.pkt_sz + sizeof(struct udphdr) + sizeof(struct iphdr));
+	iph->ttl = dtls_rule->msg.create.encap.ip_ttl;
+	iph->protocol = IPPROTO_UDP;
+	iph->saddr = dtls_rule->msg.create.encap.sip[0];
+	iph->daddr = dtls_rule->msg.create.encap.dip[0];
+
+	/*
+	 * UDP checksum
+	 */
+	uh->check = udp_csum(skb);
+	uh->check = csum_tcpudp_magic(iph->saddr, iph->daddr, skb->len,
+				      IPPROTO_UDP, uh->check);
+
+	if (nl_rule->msg.tx_pkts.log_en) {
+		nss_nl_info("%p: DTLS TX pkt len:%d udp_csum:0x%x\n", skb, skb->len, uh->check);
+	}
+
+	return skb;
+}
+
+/*
+ * nss_nldtls_construct_ipv6_udp_header()
+ *	Creates an ipv6 + udp packet
+ */
+static struct sk_buff *nss_nldtls_construct_ipv6_udp_header(struct net_device *dev, struct nss_nldtls_rule *nl_rule)
+{
+	struct nss_nldtls_tun_ctx *tun_data;
+	struct nss_nldtls_rule *dtls_rule;
+	uint16_t hroom, troom;
+	struct sk_buff *skb;
+	struct udphdr *uh;
+	struct ipv6hdr *ip6h;
+
+	/*
+	 * Get the tun data
+	 */
+	tun_data = nss_nldtls_find_dtls_tun_gbl_ctx(dev);
+	dtls_rule = tun_data->nl_rule;
+	hroom = dev->needed_headroom;
+	troom = dev->needed_tailroom;
+	skb = dev_alloc_skb(nl_rule->msg.tx_pkts.pkt_sz + hroom + troom);
+	if (!skb) {
+		nss_nl_info("Failed to allocate skb\n");
+		return NULL;
+	}
+
+	skb_reserve(skb, sizeof(struct udphdr) + sizeof(struct iphdr));
+	skb_put(skb, nl_rule->msg.tx_pkts.pkt_sz);
+
+	/*
+	 * Fill the packet with dummy data
+	 */
+	memset(skb->data, NSS_NLDTLS_DUMMY_DATA, skb->len);
+
+	/*
+	 * Fill udp header fields
+	 */
+	skb_push(skb, sizeof(struct udphdr));
+	uh = (struct udphdr *)skb->data;
+	uh->source = htons(dtls_rule->msg.create.encap.sport);
+	uh->dest = htons(dtls_rule->msg.create.encap.dport);
+	uh->len = htons(skb->len);
+	uh->check = 0;
+
+	/*
+	 * Fill IP header fields
+	 */
+	skb_push(skb, sizeof(struct ipv6hdr));
+	ip6h = (struct ipv6hdr *)skb->data;
+	ip6h->version = 6;
+	ip6h->payload_len = htons(nl_rule->msg.tx_pkts.pkt_sz + sizeof(struct udphdr));
+	ip6h->hop_limit = 64;
+	ip6h->nexthdr = IPPROTO_UDP;
+	ip6h->saddr.in6_u.u6_addr32[0] = htonl(dtls_rule->msg.create.encap.sip[0]);
+	ip6h->saddr.in6_u.u6_addr32[1] = htonl(dtls_rule->msg.create.encap.sip[1]);
+	ip6h->saddr.in6_u.u6_addr32[2] = htonl(dtls_rule->msg.create.encap.sip[2]);
+	ip6h->saddr.in6_u.u6_addr32[3] = htonl(dtls_rule->msg.create.encap.sip[3]);
+
+	ip6h->saddr.in6_u.u6_addr32[0] = htonl(dtls_rule->msg.create.encap.dip[0]);
+	ip6h->saddr.in6_u.u6_addr32[1] = htonl(dtls_rule->msg.create.encap.dip[1]);
+	ip6h->saddr.in6_u.u6_addr32[2] = htonl(dtls_rule->msg.create.encap.dip[2]);
+	ip6h->saddr.in6_u.u6_addr32[3] = htonl(dtls_rule->msg.create.encap.dip[3]);
+
+	skb_set_transport_header(skb, sizeof(struct ipv6hdr));
+	/*
+	 * UDP checksum
+	 */
+	udp6_set_csum(false, skb, &ip6h->saddr, &ip6h->daddr, nl_rule->msg.tx_pkts.pkt_sz + sizeof(struct udphdr));
+
+	if (nl_rule->msg.tx_pkts.log_en) {
+		nss_nl_info("%p: DTLS TX pkt len:%d udp_csum:0x%x\n", skb, skb->len, uh->check);
+	}
+
+	return skb;
+}
+
+/*
+ * nss_nldtls_tx_ipv4_pkts_host_to_host()
+ *	Handler for sending ipv4 traffic from one host to other
+ */
+static bool nss_nldtls_tx_ipv4_pkts_host_to_host(struct nss_nldtls_rule *nl_rule, struct net_device *dtls_dev)
+{
+	int i;
+	for (i = 0; i < nl_rule->msg.tx_pkts.num_pkts; i++) {
+		struct sk_buff *skb;
+
+		skb = nss_nldtls_construct_ipv4_udp_header(dtls_dev, nl_rule);
+		if (!skb) {
+			nss_nl_error("%p: Unable to create ipv4 + udp packet\n", dtls_dev);
+			return false;
+		}
+
+		dtls_dev->netdev_ops->ndo_start_xmit(skb, dtls_dev);
+	}
+
+	return true;
+}
+
+/*
+ * nss_nldtls_tx_ipv6_pkts_host_to_host()
+ *	Handler for sending ipv6 traffic from one host to other
+ */
+static bool nss_nldtls_tx_ipv6_pkts_host_to_host(struct nss_nldtls_rule *nl_rule, struct net_device *dtls_dev)
+{
+	int i;
+	for (i = 0; i < nl_rule->msg.tx_pkts.num_pkts; i++) {
+		struct sk_buff *skb;
+
+		skb = nss_nldtls_construct_ipv6_udp_header(dtls_dev, nl_rule);
+		if (!skb) {
+			nss_nl_error("%p: Unable to create ipv4 + udp packet\n", dtls_dev);
+			return false;
+		}
+
+		dtls_dev->netdev_ops->ndo_start_xmit(skb, dtls_dev);
+	}
+
+	return true;
+}
+
+/*
+ * nss_nldtls_ops_tx_pkts()
+ *	Handler for sending traffic
+ */
+static int nss_nldtls_ops_tx_pkts(struct sk_buff *skb, struct genl_info *info)
+{
+	struct nss_nldtls_tun_ctx *dtls_tun_data;
+	struct nss_nldtls_rule *nl_rule;
+	struct net_device *dtls_ndev;
+	unsigned long long duration;
+	struct nss_nlcmn *nl_cm;
+	ktime_t delta;
+
+	/*
+	 * extract the message payload
+	 */
+	nl_cm = nss_nl_get_msg(&nss_nldtls_family, info, NSS_NLDTLS_CMD_TYPE_TX_PKTS);
+	if (!nl_cm) {
+		nss_nl_error("%p: Unable to extract tx_pkts data\n", skb);
+		return -EINVAL;
+	}
+
+	/*
+	 * Message validation required before accepting the configuration
+	 */
+	nl_rule = container_of(nl_cm, struct nss_nldtls_rule, cm);
+
+	dtls_ndev = dev_get_by_name(&init_net, nl_rule->msg.tx_pkts.dev_name);
+	if (!dtls_ndev) {
+		nss_nl_error("%p: Unable to find dev: %s\n", skb, nl_rule->msg.tx_pkts.dev_name);
+		return -EINVAL;
+	}
+
+	dtls_tun_data = nss_nldtls_find_dtls_tun_gbl_ctx(dtls_ndev);
+	if (!dtls_tun_data) {
+		nss_nl_error("%p: Unable to find context of the tunnel: %s\n", skb, dtls_ndev->name);
+		dev_put(dtls_ndev);
+		return -EAGAIN;
+	}
+
+	spin_lock(&gbl_ctx.lock);
+	gbl_ctx.log_en = nl_rule->msg.tx_pkts.log_en;
+	spin_unlock(&gbl_ctx.lock);
+
+	/*
+	 * Send traffic from host to host
+	 */
+	gbl_ctx.first_tx_pkt_time = ktime_get();
+	if (nl_rule->msg.tx_pkts.ip_version == NSS_NLDTLS_IP_VERS_4) {
+		if (!nss_nldtls_tx_ipv4_pkts_host_to_host(nl_rule, dtls_ndev)) {
+			nss_nl_error("%p: Error in transmission\n", skb);
+			return -EAGAIN;
+		}
+	} else {
+		if (!nss_nldtls_tx_ipv6_pkts_host_to_host(nl_rule, dtls_ndev)) {
+			nss_nl_error("%p: Error in transmission\n", skb);
+			return -EAGAIN;
+		}
+	}
+
+	gbl_ctx.last_tx_pkt_time = ktime_get();
+	delta = ktime_sub(gbl_ctx.last_tx_pkt_time, gbl_ctx.first_tx_pkt_time);
+	duration = (unsigned long long) ktime_to_ns(delta) >> 10;
+	nss_nl_info("%p: Packets sent in %llu usecs", dtls_ndev, duration);
+	nss_nl_info("%p: Traffic transmission successful\n", skb);
+	return 0;
+}
+
+/*
+ * nss_nldtls_cmd_ops
+ *	Operation table called by the generic netlink layer based on the command
+ */
+struct genl_ops nss_nldtls_cmd_ops[] = {
+	{.cmd = NSS_NLDTLS_CMD_TYPE_CREATE_TUN, .doit = nss_nldtls_ops_create_tun,},
+	{.cmd = NSS_NLDTLS_CMD_TYPE_DESTROY_TUN, .doit = nss_nldtls_ops_destroy_tun,},
+	{.cmd = NSS_NLDTLS_CMD_TYPE_UPDATE_CONFIG, .doit = nss_nldtls_ops_update_config,},
+	{.cmd = NSS_NLDTLS_CMD_TYPE_TX_PKTS, .doit = nss_nldtls_ops_tx_pkts,},
+};
+
+/*
+ * nss_nldtls_init()
+ *	Init handler for dtls
+ */
+bool nss_nldtls_init(void)
+{
+	int err;
+
+	nss_nl_info_always("Init NSS netlink dtls handler\n");
+
+	/*
+	 * register NETLINK ops with the family
+	 */
+	err = genl_register_family_with_ops_groups(&nss_nldtls_family, nss_nldtls_cmd_ops, nss_nldtls_family_mcgrp);
+	if (err) {
+		nss_nl_info_always("Error: %d unable to register gre_redir family\n", err);
+		genl_unregister_family(&nss_nldtls_family);
+		return false;
+	}
+
+	return true;
+}
+
+/*
+ * nss_nldtls_exit()
+ *	Exit handler for dtls
+ */
+bool nss_nldtls_exit(void)
+{
+	struct nss_nldtls_tun_ctx *entry, *tmp;
+	struct net_device *dtls_ndev;
+	int err;
+
+	nss_nl_info_always("Exit NSS netlink dtls handler\n");
+
+	/*
+	 * Destroy all active tunnel before exiting
+	 */
+	list_for_each_entry_safe(entry, tmp, &gbl_ctx.dtls_list_head, list) {
+		dtls_ndev = dev_get_by_name(&init_net, entry->dev_name);
+		if (dtls_ndev) {
+			nss_nldtls_destroy_tun(dtls_ndev);
+		}
+	}
+
+	nss_nl_info_always("All active tunnels destroyed\n");
+
+	/*
+	 * unregister the ops family
+	 */
+	err = genl_unregister_family(&nss_nldtls_family);
+	if (err) {
+		nss_nl_info_always("Error: %d unable to unregister dtls NETLINK family\n", err);
+		return false;
+	}
+
+	return true;
+}
diff --git a/netlink/nss_nldtls.h b/netlink/nss_nldtls.h
new file mode 100644
index 0000000..9175465
--- /dev/null
+++ b/netlink/nss_nldtls.h
@@ -0,0 +1,66 @@
+/*
+ **************************************************************************
+ * Copyright (c) 2014-2015,2018-2020 The Linux Foundation. All rights reserved.
+ * Permission to use, copy, modify, and/or distribute this software for
+ * any purpose with or without fee is hereby granted, provided that the
+ * above copyright notice and this permission notice appear in all copies.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ **************************************************************************
+ */
+
+/*
+ * nss_nldtls.h
+ *	NSS Netlink Dtls API definitions
+ */
+#ifndef __NSS_NLDTLS_H
+#define __NSS_NLDTLS_H
+
+#define NSS_NLDTLS_SKB_TAILROOM 192
+#define NSS_NLDTLS_IPV4_SESSION 0
+#define NSS_NLDTLS_MAX_TUNNELS 32
+#define NSS_NLDTLS_VLAN_INVALID 0xFFF
+#define NSS_NLDTLS_IP_VERS_4 4
+#define NSS_NLDTLS_DUMMY_DATA 0xcc
+
+/*
+ * nss_nldtls_tun_ctx
+ *	Per dtls tunnel context
+ */
+struct nss_nldtls_tun_ctx {
+	struct list_head list;			/**< List for holding different tunnel info */
+	struct nss_nldtls_rule *nl_rule;	/**< Dtls rule structure */
+	char dev_name[IFNAMSIZ];		/**< Dtls session netdev */
+};
+
+/*
+ * nss_nldtls_gbl_ctx
+ *	Global context for dtls
+ */
+struct nss_nldtls_gbl_ctx {
+	spinlock_t lock;
+	atomic_t num_tun;
+	struct list_head dtls_list_head;
+	bool log_en;
+	ktime_t first_rx_pkt_time;
+	ktime_t first_tx_pkt_time;
+	ktime_t last_rx_pkt_time;
+	ktime_t last_tx_pkt_time;
+};
+
+bool nss_nldtls_init(void);
+bool nss_nldtls_exit(void);
+
+#if (CONFIG_NSS_NLDTLS == 1)
+#define NSS_NLDTLS_INIT nss_nldtls_init
+#define NSS_NLDTLS_EXIT nss_nldtls_exit
+#else
+#define NSS_NLDTLS_INIT 0
+#define NSS_NLDTLS_EXIT 0
+#endif /* !CONFIG_NSS_NLDTLS */
+#endif /* __NSS_NLDTLS_H */