[qca-nss-ecm] Added support for PPTP packet acceleration

Change-Id: I834130ce635bb2e21363b9d1fc3912d022442fa2
Signed-off-by: Shyam Sunder <ssunde@codeaurora.org>
diff --git a/Makefile b/Makefile
index f017c40..8a499db 100755
--- a/Makefile
+++ b/Makefile
@@ -80,15 +80,26 @@
 ccflags-$(ECM_INTERFACE_L2TPV2_ENABLE) += -DECM_INTERFACE_L2TPV2_ENABLE
 
 # #############################################################################
-# if pppoe, l2tpv2 acceleration is enabled, ppp should
+# Define ECM_INTERFACE_PPTP_ENABLE=y in order
+# to enable support for pptp acceleration.
+# #############################################################################
+ifneq ($(findstring 3.4, $(KERNELVERSION)),)
+ECM_INTERFACE_PPTP_ENABLE=y
+endif
+ccflags-$(ECM_INTERFACE_PPTP_ENABLE) += -DECM_INTERFACE_PPTP_ENABLE
+
+# #############################################################################
+# if pppoe, l2tpv2, pptp acceleration is enabled, ppp should
 # be enabled automatically
 # #############################################################################
 ECM_INTERFACE_PPP_ENABLE=y
 ifeq "$(ECM_INTERFACE_PPPOE_ENABLE)" "n"
 ifeq "$(ECM_INTERFACE_L2TPV2_ENABLE)" "n"
+ifeq "$(ECM_INTERFACE_PPTP_ENABLE)" "n"
 ECM_INTERFACE_PPP_ENABLE=n
 endif
 endif
+endif
 ccflags-$(ECM_INTERFACE_PPP_ENABLE) += -DECM_INTERFACE_PPP_ENABLE
 
 # #############################################################################
diff --git a/ecm_db.c b/ecm_db.c
index 047ee5f..b2d3550 100644
--- a/ecm_db.c
+++ b/ecm_db.c
@@ -256,6 +256,9 @@
 #ifdef ECM_INTERFACE_L2TPV2_ENABLE
 		struct ecm_db_interface_info_pppol2tpv2 pppol2tpv2;	/* type == ECM_DB_IFACE_TYPE_PPPOL2TPV2 */
 #endif
+#ifdef ECM_INTERFACE_PPTP_ENABLE
+		struct ecm_db_interface_info_pptp pptp;			/* type == ECM_DB_IFACE_TYPE_PPTP */
+#endif
 		struct ecm_db_interface_info_unknown unknown;		/* type == ECM_DB_IFACE_TYPE_UNKNOWN */
 		struct ecm_db_interface_info_loopback loopback;		/* type == ECM_DB_IFACE_TYPE_LOOPBACK */
 #ifdef ECM_INTERFACE_IPSEC_ENABLE
@@ -882,7 +885,8 @@
 	"UNKNOWN",
 	"SIT",
 	"TUNIPIP6",
-	"PPPoL2TPV2"
+	"PPPoL2TPV2",
+	"PPTP"
 };
 
 /*
@@ -3958,6 +3962,18 @@
 
 #endif
 
+#ifdef ECM_INTERFACE_PPTP_ENABLE
+/*
+ * ecm_db_iface_generate_hash_index_pptp()
+ *	Calculate the hash index.
+ */
+static inline ecm_db_iface_hash_t ecm_db_iface_generate_hash_index_pptp(uint16_t pptp_src_call_id, uint16_t pptp_dst_call_id)
+{
+	uint32_t hash_val;
+	hash_val = (uint32_t)jhash_2words(pptp_src_call_id, pptp_dst_call_id, ecm_db_jhash_rnd);
+	return (ecm_db_iface_hash_t)(hash_val & (ECM_DB_IFACE_HASH_SLOTS - 1));
+}
+#endif
 /*
  * ecm_db_iface_generate_hash_index_unknown()
  * 	Calculate the hash index.
@@ -4538,6 +4554,65 @@
 
 #endif
 
+#ifdef ECM_INTERFACE_PPTP_ENABLE
+/*
+ * ecm_db_iface_pptp_session_info_get
+ *	get pptp specific info
+ */
+void ecm_db_iface_pptp_session_info_get(struct ecm_db_iface_instance *ii, struct ecm_db_interface_info_pptp *pptp_info)
+{
+	DEBUG_CHECK_MAGIC(ii, ECM_DB_IFACE_INSTANCE_MAGIC, "%p: magic failed", ii);
+	DEBUG_ASSERT(ii->type == ECM_DB_IFACE_TYPE_PPTP, "%p: Bad type, expected pptp, actual: %d\n", ii, ii->type);
+	spin_lock_bh(&ecm_db_lock);
+	memcpy(pptp_info, &ii->type_info.pptp, sizeof(struct ecm_db_interface_info_pptp));
+	spin_unlock_bh(&ecm_db_lock);
+}
+EXPORT_SYMBOL(ecm_db_iface_pptp_session_info_get);
+
+/*
+ * ecm_db_iface_find_and_ref_pptp()
+ *	Lookup and return a iface reference if any
+ */
+struct ecm_db_iface_instance *ecm_db_iface_find_and_ref_pptp(uint32_t pptp_src_call_id, uint32_t pptp_dst_call_id)
+{
+	ecm_db_iface_hash_t hash_index;
+	struct ecm_db_iface_instance *ii;
+
+	/*
+	 * Compute the hash chain index and prepare to walk the chain
+	 */
+	hash_index = ecm_db_iface_generate_hash_index_pptp(pptp_src_call_id, pptp_dst_call_id);
+
+	DEBUG_TRACE("Lookup pptp iface with local_call_id = %d, remote_call_id = %d, hash = 0x%x\n", pptp_src_call_id,
+									pptp_dst_call_id, hash_index);
+
+	/*
+	 * Iterate the chain looking for a host with matching details
+	 */
+	spin_lock_bh(&ecm_db_lock);
+	ii = ecm_db_iface_table[hash_index];
+
+	while (ii) {
+		if ((ii->type != ECM_DB_IFACE_TYPE_PPTP)
+				|| (ii->type_info.pptp.src_call_id != pptp_src_call_id)
+				|| (ii->type_info.pptp.dst_call_id != pptp_dst_call_id)) {
+			ii = ii->hash_next;
+			continue;
+		}
+
+		_ecm_db_iface_ref(ii);
+		spin_unlock_bh(&ecm_db_lock);
+		DEBUG_TRACE("iface found %p\n", ii);
+		return ii;
+	}
+	spin_unlock_bh(&ecm_db_lock);
+
+	DEBUG_TRACE("Iface not found\n");
+	return NULL;
+}
+EXPORT_SYMBOL(ecm_db_iface_find_and_ref_pptp);
+#endif
+
 /*
  * ecm_db_iface_find_and_ref_unknown()
  *	Lookup and return a iface reference if any
@@ -7710,6 +7785,45 @@
 
 #endif
 
+#ifdef ECM_INTERFACE_PPTP_ENABLE
+/*
+ * ecm_db_iface_pptp_state_get()
+ *	Return interface type specific state
+ */
+static int ecm_db_iface_pptp_state_get(struct ecm_db_iface_instance *ii, struct ecm_state_file_instance *sfi)
+{
+	int result;
+	struct ecm_db_interface_info_pptp type_info;
+
+	DEBUG_CHECK_MAGIC(ii, ECM_DB_IFACE_INSTANCE_MAGIC, "%p: magic failed\n", ii);
+	spin_lock_bh(&ecm_db_lock);
+	memcpy(&type_info, &ii->type_info, sizeof(struct ecm_db_interface_info_pptp));
+	spin_unlock_bh(&ecm_db_lock);
+
+	result = ecm_state_prefix_add(sfi, "pptp");
+	if (result) {
+		return result;
+	}
+
+	result = ecm_db_iface_state_get_base(ii, sfi);
+	if (result) {
+		return result;
+	}
+
+	result = ecm_state_write(sfi, "local_call_id", "%u", type_info.src_call_id);
+	if (result) {
+		return result;
+	}
+
+	result = ecm_state_write(sfi, "peer_call_id", "%u", type_info.dst_call_id);
+	if (result) {
+		return result;
+	}
+
+	return ecm_state_prefix_remove(sfi);
+}
+#endif
+
 /*
  * ecm_db_iface_unknown_state_get()
  * 	Return interface type specific state
@@ -9547,6 +9661,123 @@
 
 #endif
 
+#ifdef ECM_INTERFACE_PPTP_ENABLE
+/*
+ * ecm_db_iface_add_pptp()
+ *	Add a iface instance into the database
+ */
+void ecm_db_iface_add_pptp(struct ecm_db_iface_instance *ii, struct ecm_db_interface_info_pptp *pptp_info,
+					char *name, int32_t mtu, int32_t interface_identifier,
+					int32_t ae_interface_identifier, ecm_db_iface_final_callback_t final,
+					void *arg)
+{
+	ecm_db_iface_hash_t hash_index;
+	ecm_db_iface_id_hash_t iface_id_hash_index;
+	struct ecm_db_listener_instance *li;
+	struct ecm_db_interface_info_pptp *type_info;
+
+	DEBUG_CHECK_MAGIC(ii, ECM_DB_IFACE_INSTANCE_MAGIC, "%p: magic failed\n", ii);
+	spin_lock_bh(&ecm_db_lock);
+#ifdef ECM_DB_XREF_ENABLE
+	DEBUG_ASSERT((ii->nodes == NULL) && (ii->node_count == 0), "%p: nodes not null\n", ii);
+#endif
+	DEBUG_ASSERT(!(ii->flags & ECM_DB_IFACE_FLAGS_INSERTED), "%p: inserted\n", ii);
+	DEBUG_ASSERT(name, "%p: no name given\n", ii);
+	spin_unlock_bh(&ecm_db_lock);
+
+	/*
+	 * Record general info
+	 */
+	ii->type = ECM_DB_IFACE_TYPE_PPTP;
+#ifdef ECM_STATE_OUTPUT_ENABLE
+	ii->state_get = ecm_db_iface_pptp_state_get;
+#endif
+	ii->arg = arg;
+	ii->final = final;
+	strlcpy(ii->name, name, IFNAMSIZ);
+	ii->mtu = mtu;
+	ii->interface_identifier = interface_identifier;
+	ii->ae_interface_identifier = ae_interface_identifier;
+
+	/*
+	 * Type specific info
+	 */
+	type_info = &ii->type_info.pptp;
+	memcpy(type_info, pptp_info, sizeof(struct ecm_db_interface_info_pptp));
+
+	/*
+	 * Compute hash chain for insertion
+	 */
+	hash_index = ecm_db_iface_generate_hash_index_pptp(type_info->src_call_id,
+							  type_info->dst_call_id);
+	ii->hash_index = hash_index;
+
+	iface_id_hash_index = ecm_db_iface_id_generate_hash_index(interface_identifier);
+	ii->iface_id_hash_index = iface_id_hash_index;
+	/*
+	 * Add into the global list
+	 */
+	spin_lock_bh(&ecm_db_lock);
+	ii->flags |= ECM_DB_IFACE_FLAGS_INSERTED;
+	ii->prev = NULL;
+	ii->next = ecm_db_interfaces;
+	if (ecm_db_interfaces) {
+		ecm_db_interfaces->prev = ii;
+	}
+	ecm_db_interfaces = ii;
+
+	/*
+	 * Insert into chain
+	 */
+	ii->hash_next = ecm_db_iface_table[hash_index];
+	if (ecm_db_iface_table[hash_index]) {
+		ecm_db_iface_table[hash_index]->hash_prev = ii;
+	}
+	ecm_db_iface_table[hash_index] = ii;
+	ecm_db_iface_table_lengths[hash_index]++;
+	DEBUG_ASSERT(ecm_db_iface_table_lengths[hash_index] > 0, "%p: invalid table len %d\n", ii, ecm_db_iface_table_lengths[hash_index]);
+
+	DEBUG_INFO("%p: interface inserted at hash index %u, hash prev is %p, type: %d\n", ii, ii->hash_index, ii->hash_prev, ii->type);
+
+	/*
+	 * Insert into interface identifier chain
+	 */
+	ii->iface_id_hash_next = ecm_db_iface_id_table[iface_id_hash_index];
+	if (ecm_db_iface_id_table[iface_id_hash_index]) {
+		ecm_db_iface_id_table[iface_id_hash_index]->iface_id_hash_prev = ii;
+	}
+	ecm_db_iface_id_table[iface_id_hash_index] = ii;
+	ecm_db_iface_id_table_lengths[iface_id_hash_index]++;
+	DEBUG_ASSERT(ecm_db_iface_id_table_lengths[iface_id_hash_index] > 0, "%p: invalid iface id table len %d\n", ii, ecm_db_iface_id_table_lengths[iface_id_hash_index]);
+
+	/*
+	 * Set time of addition
+	 */
+	ii->time_added = ecm_db_time;
+	spin_unlock_bh(&ecm_db_lock);
+
+	/*
+	 * Throw add event to the listeners
+	 */
+	DEBUG_TRACE("%p: Throw iface added event\n", ii);
+	li = ecm_db_listeners_get_and_ref_first();
+	while (li) {
+		struct ecm_db_listener_instance *lin;
+		if (li->iface_added) {
+			li->iface_added(li->arg, ii);
+		}
+
+		/*
+		 * Get next listener
+		 */
+		lin = ecm_db_listener_get_and_ref_next(li);
+		ecm_db_listener_deref(li);
+		li = lin;
+	}
+}
+EXPORT_SYMBOL(ecm_db_iface_add_pptp);
+#endif
+
 /*
  * ecm_db_iface_add_unknown()
  *	Add a iface instance into the database
diff --git a/ecm_db.h b/ecm_db.h
index 68871dc..c4f5e84 100644
--- a/ecm_db.h
+++ b/ecm_db.h
@@ -154,6 +154,10 @@
 void ecm_db_iface_pppol2tpv2_session_info_get(struct ecm_db_iface_instance *ii, struct ecm_db_interface_info_pppol2tpv2 *pppol2tpv2_info);
 struct ecm_db_iface_instance *ecm_db_iface_find_and_ref_pppol2tpv2(uint32_t pppol2tpv2_tunnel_id, uint32_t pppol2tpv2_session_id);
 #endif
+#ifdef ECM_INTERFACE_PPTP_ENABLE
+struct ecm_db_iface_instance *ecm_db_iface_find_and_ref_pptp(uint32_t pptp_src_call_id, uint32_t pptp_dst_call_id);
+void ecm_db_iface_pptp_session_info_get(struct ecm_db_iface_instance *ii, struct ecm_db_interface_info_pptp *pptp_info);
+#endif
 struct ecm_db_iface_instance *ecm_db_iface_find_and_ref_loopback(uint32_t os_specific_ident);
 #ifdef ECM_INTERFACE_IPSEC_ENABLE
 struct ecm_db_iface_instance *ecm_db_iface_find_and_ref_ipsec_tunnel(uint32_t os_specific_ident);
@@ -278,6 +282,11 @@
 #ifdef ECM_INTERFACE_L2TPV2_ENABLE
 void ecm_db_iface_add_pppol2tpv2(struct ecm_db_iface_instance *ii, struct ecm_db_interface_info_pppol2tpv2 *pppol2tpv2_info, char *name, int32_t mtu, int32_t interface_identifier, int32_t ae_interface_identifier, ecm_db_iface_final_callback_t final, void *arg);
 #endif
+
+#ifdef ECM_INTERFACE_PPTP_ENABLE
+void ecm_db_iface_add_pptp(struct ecm_db_iface_instance *ii, struct ecm_db_interface_info_pptp *pptp_info, char *name, int32_t mtu, int32_t interface_identifier, int32_t ae_interface_identifier, ecm_db_iface_final_callback_t final, void *arg);
+#endif
+
 void ecm_db_iface_add_unknown(struct ecm_db_iface_instance *ii, uint32_t os_specific_ident, char *name, int32_t mtu, int32_t interface_identifier, int32_t ae_interface_identifier, ecm_db_iface_final_callback_t final, void *arg);
 void ecm_db_iface_add_loopback(struct ecm_db_iface_instance *ii, uint32_t os_specific_ident, char *name, int32_t mtu, int32_t interface_identifier, int32_t ae_interface_identifier, ecm_db_iface_final_callback_t final, void *arg);
 #ifdef ECM_INTERFACE_IPSEC_ENABLE
diff --git a/ecm_db_types.h b/ecm_db_types.h
index 69e526d..cc8cd05 100644
--- a/ecm_db_types.h
+++ b/ecm_db_types.h
@@ -242,6 +242,7 @@
 	ECM_DB_IFACE_TYPE_SIT,				/* IPv6 in IPv4 tunnel (SIT) interface */
 	ECM_DB_IFACE_TYPE_TUNIPIP6,			/* IPIP6 Tunnel (TUNNEL6) interface */
 	ECM_DB_IFACE_TYPE_PPPOL2TPV2,			/* Interface is a PPPoL2TPV2 interface (a specific form of PPP that we recognise in the ECM) */
+	ECM_DB_IFACE_TYPE_PPTP,				/* Interface is a PPTP interface */
 	ECM_DB_IFACE_TYPE_COUNT,			/* Number of interface types */
 };
 typedef enum ecm_db_iface_types ecm_db_iface_type_t;
@@ -301,6 +302,15 @@
 
 #endif
 
+#ifdef ECM_INTERFACE_PPTP_ENABLE
+struct ecm_db_interface_info_pptp {
+	uint32_t src_ip;
+	uint32_t dst_ip;
+	uint16_t src_call_id;
+	uint16_t dst_call_id;
+};
+#endif
+
 struct ecm_db_interface_info_unknown {			/* type == ECM_DB_IFACE_TYPE_UNKNOWN */
 	uint32_t os_specific_ident;			/* Operating system specific identifier (known only by front end) */
 };
diff --git a/ecm_interface.c b/ecm_interface.c
index c94bb6b..ca84ebf 100644
--- a/ecm_interface.c
+++ b/ecm_interface.c
@@ -74,6 +74,9 @@
 #ifdef ECM_INTERFACE_L2TPV2_ENABLE
 #include <linux/if_pppol2tp.h>
 #endif
+#ifdef ECM_INTERFACE_PPTP_ENABLE
+#include <linux/netfilter/nf_conntrack_proto_gre.h>
+#endif
 #endif
 
 /*
@@ -785,15 +788,14 @@
 #endif
 
 #ifdef ECM_INTERFACE_PPP_ENABLE
-#ifdef ECM_INTERFACE_L2TPV2_ENABLE
 /*
- * ecm_interface_skip_l2tpv3_pptp()
+ * ecm_interface_skip_pptp()
  *	skip pptp tunnel encapsulated traffic
  *
- * ECM does not handle  PPTP and l2tpv3,
+ * ECM does not handle PPTP,
  * this function detects packets of that type so they can be skipped over to improve their throughput.
  */
-bool ecm_interface_skip_l2tpv3_pptp(struct sk_buff *skb, const struct net_device *out)
+bool ecm_interface_skip_pptp(struct sk_buff *skb, const struct net_device *out)
 {
 	struct ppp_channel *ppp_chan[1];
 	int px_proto;
@@ -814,25 +816,6 @@
 			return true;
 		}
 
-		/*
-		 * Check for L2TPv3 packets
-		 */
-		if (px_proto == PX_PROTO_OL2TP) {
-			struct pppol2tp_common_addr info;
-			if (pppol2tp_channel_addressing_get(ppp_chan[0], &info)) {
-				ppp_release_channels(ppp_chan, 1);
-				return true;
-			}
-
-			if (info.tunnel_version == 2) {
-				ppp_release_channels(ppp_chan, 1);
-				return false;
-			}
-			if (info.tunnel_version == 3) {
-				ppp_release_channels(ppp_chan, 1);
-				return true;
-			}
-		}
 		ppp_release_channels(ppp_chan, 1);
 	}
 
@@ -858,6 +841,64 @@
 			return true;
 		}
 
+		ppp_release_channels(ppp_chan, 1);
+	}
+
+	dev_put(in);
+	return false;
+}
+
+/*
+ * ecm_interface_skip_l2tp_packet_by_version()
+ *	Check version of l2tp tunnel encapsulated traffic
+ *
+ * ECM does not handle l2tp,
+ * this function detects packets of that type so they can be skipped over to improve their throughput.
+ */
+bool ecm_interface_skip_l2tp_packet_by_version(struct sk_buff *skb, const struct net_device *out, int ver)
+{
+	struct ppp_channel *ppp_chan[1];
+	int px_proto;
+	struct net_device *in;
+
+	if (out->type == ARPHRD_PPP) {
+		if (ppp_hold_channels((struct net_device *)out, ppp_chan, 1) != 1) {
+			return true;
+		}
+
+		px_proto = ppp_channel_get_protocol(ppp_chan[0]);
+
+		/*
+		 * Check for L2TPv3 packets
+		 */
+		if (px_proto == PX_PROTO_OL2TP) {
+			struct pppol2tp_common_addr info;
+			if (pppol2tp_channel_addressing_get(ppp_chan[0], &info)) {
+				ppp_release_channels(ppp_chan, 1);
+				return true;
+			}
+
+			if (info.tunnel_version == ver) {
+				ppp_release_channels(ppp_chan, 1);
+				return true;
+			}
+		}
+		ppp_release_channels(ppp_chan, 1);
+	}
+
+	in = dev_get_by_index(&init_net, skb->skb_iif);
+	if (!in) {
+		return true;
+	}
+
+	if (in->type == ARPHRD_PPP) {
+		if (__ppp_hold_channels((struct net_device *)in, ppp_chan, 1) != 1) {
+			dev_put(in);
+			return true;
+		}
+
+		px_proto = ppp_channel_get_protocol(ppp_chan[0]);
+
 		/*
 		 * Check for L2TPv3 pkts
 		 */
@@ -868,12 +909,8 @@
 				dev_put(in);
 				return true;
 			}
-			if (info.tunnel_version == 2) {
-				ppp_release_channels(ppp_chan, 1);
-				dev_put(in);
-				return false;
-			}
-			if (info.tunnel_version == 3) {
+
+			if (info.tunnel_version == ver) {
 				ppp_release_channels(ppp_chan, 1);
 				dev_put(in);
 				return true;
@@ -885,7 +922,6 @@
 	dev_put(in);
 	return false;
 }
-#endif
 
 /*
  * ecm_interface_skip_l2tp_pptp()
@@ -1240,6 +1276,59 @@
 
 #endif
 
+#ifdef ECM_INTERFACE_PPTP_ENABLE
+/*
+ * ecm_interface_pptp_interface_establish()
+ *	Returns a reference to a iface of the PPTP type, possibly creating one if necessary.
+ *	Returns NULL on failure or a reference to interface.
+ */
+static struct ecm_db_iface_instance *ecm_interface_pptp_interface_establish(struct ecm_db_interface_info_pptp *type_info,
+							char *dev_name, int32_t dev_interface_num, int32_t ae_interface_num, int32_t mtu)
+{
+	struct ecm_db_iface_instance *nii;
+	struct ecm_db_iface_instance *ii;
+
+	DEBUG_INFO("Establish PPTP iface: %s with local call id %u peer call id %u\n", dev_name, type_info->src_call_id,
+				type_info->dst_call_id);
+	/*
+	 * Locate the iface
+	 */
+	ii = ecm_db_iface_find_and_ref_pptp(type_info->src_call_id, type_info->dst_call_id);
+	if (ii) {
+		DEBUG_TRACE("%p: iface established\n", ii);
+		ecm_db_iface_update_ae_interface_identifier(ii, ae_interface_num);
+		return ii;
+	}
+
+	/*
+	 * No iface - create one
+	 */
+	nii = ecm_db_iface_alloc();
+	if (!nii) {
+		DEBUG_WARN("Failed to establish iface\n");
+		return NULL;
+	}
+
+	/*
+	 * Add iface into the database, atomically to avoid races creating the same thing
+	 */
+	spin_lock_bh(&ecm_interface_lock);
+	ii = ecm_db_iface_find_and_ref_pptp(type_info->src_call_id, type_info->dst_call_id);
+	if (ii) {
+		spin_unlock_bh(&ecm_interface_lock);
+		ecm_db_iface_deref(nii);
+		ecm_db_iface_update_ae_interface_identifier(ii, ae_interface_num);
+		return ii;
+	}
+
+	ecm_db_iface_add_pptp(nii, type_info, dev_name, mtu, dev_interface_num, ae_interface_num, NULL, nii);
+	spin_unlock_bh(&ecm_interface_lock);
+
+	DEBUG_TRACE("%p: pptp iface established\n", nii);
+	return nii;
+}
+#endif
+
 /*
  * ecm_interface_unknown_interface_establish()
  *	Returns a reference to a iface of the UNKNOWN type, possibly creating one if necessary.
@@ -1530,6 +1619,9 @@
 #ifdef ECM_INTERFACE_L2TPV2_ENABLE
 		struct ecm_db_interface_info_pppol2tpv2 pppol2tpv2;		/* type == ECM_DB_IFACE_TYPE_PPPOL2TPV2 */
 #endif
+#ifdef ECM_INTERFACE_PPTP_ENABLE
+		struct ecm_db_interface_info_pptp pptp;			/* type == ECM_DB_IFACE_TYPE_PPTP */
+#endif
 		struct ecm_db_interface_info_unknown unknown;		/* type == ECM_DB_IFACE_TYPE_UNKNOWN */
 		struct ecm_db_interface_info_loopback loopback;		/* type == ECM_DB_IFACE_TYPE_LOOPBACK */
 #ifdef ECM_INTERFACE_IPSEC_ENABLE
@@ -1552,8 +1644,16 @@
 #ifdef ECM_INTERFACE_PPPOE_ENABLE
 	struct pppoe_opt addressing;
 #endif
+#ifdef ECM_INTERFACE_PPTP_ENABLE
+	int protocol = IPPROTO_IP;
+	struct pptp_opt opt;
+	struct iphdr *v4_hdr = NULL;
+	if (skb) {
+		v4_hdr = ip_hdr(skb);
+		protocol = v4_hdr->protocol;
+	}
 #endif
-
+#endif
 	/*
 	 * Get basic information about the given device
 	 */
@@ -1852,6 +1952,53 @@
 	}
 #endif
 
+#ifdef ECM_INTERFACE_PPTP_ENABLE
+	if ((protocol == IPPROTO_GRE) && skb && v4_hdr) {
+		struct gre_hdr_pptp *gre_hdr;
+		uint16_t proto;
+		int ret;
+
+		skb_pull(skb, sizeof(struct iphdr));
+		gre_hdr = (struct gre_hdr_pptp *)(skb->data);
+		proto = ntohs(gre_hdr->protocol);
+		if ((gre_hdr->version == GRE_VERSION_PPTP) && (proto == GRE_PROTOCOL_PPTP)) {
+			ret = pptp_session_find(&opt, gre_hdr->call_id, v4_hdr->daddr);
+			if (ret < 0) {
+				skb_push(skb, sizeof(struct iphdr));
+				DEBUG_WARN("PPTP session info not found\n");
+				return NULL;
+			}
+
+			/*
+			 * Get PPTP session info
+			 */
+			type_info.pptp.src_call_id = ntohs(opt.src_addr.call_id);
+			type_info.pptp.dst_call_id = ntohs(opt.dst_addr.call_id);
+			type_info.pptp.src_ip = ntohl(opt.src_addr.sin_addr.s_addr);
+			type_info.pptp.dst_ip = ntohl(opt.dst_addr.sin_addr.s_addr);
+
+			skb_push(skb, sizeof(struct iphdr));
+
+			/*
+			 * Establish this type of interface
+			 */
+			ii = ecm_interface_pptp_interface_establish(&type_info.pptp, dev_name, dev_interface_num, ae_interface_num, dev_mtu);
+			return ii;
+		}
+
+		skb_push(skb, sizeof(struct iphdr));
+
+		DEBUG_TRACE("Unknown GRE protocol \n");
+		type_info.unknown.os_specific_ident = dev_interface_num;
+
+		/*
+		 * Establish this type of interface
+		 */
+		ii = ecm_interface_unknown_interface_establish(&type_info.unknown, dev_name, dev_interface_num, ae_interface_num, dev_mtu);
+		return ii;
+	}
+#endif
+
 	/*
 	 * PPP - but what is the channel type?
 	 * First: If this is multi-link then we do not support it
@@ -1955,6 +2102,29 @@
 		return ii;
 	}
 #endif
+
+#ifdef ECM_INTERFACE_PPTP_ENABLE
+	if (channel_protocol == PX_PROTO_PPTP) {
+		pptp_channel_addressing_get(&opt, ppp_chan[0]);
+
+		/*
+		 * Get PPTP session info
+		 */
+		type_info.pptp.src_call_id = ntohs(opt.src_addr.call_id);
+		type_info.pptp.dst_call_id = ntohs(opt.dst_addr.call_id);
+		type_info.pptp.src_ip = ntohl(opt.src_addr.sin_addr.s_addr);
+		type_info.pptp.dst_ip = ntohl(opt.dst_addr.sin_addr.s_addr);
+
+		DEBUG_TRACE("Net device: %p PPTP source call id: %d,n", dev, type_info.pptp.src_call_id);
+		ppp_release_channels(ppp_chan, 1);
+
+		/*
+		 * Establish this type of interface
+		 */
+		ii = ecm_interface_pptp_interface_establish(&type_info.pptp, dev_name, dev_interface_num, ae_interface_num, dev_mtu);
+		return ii;
+	}
+#endif
 	DEBUG_TRACE("Net device: %p PPP channel protocol: %d - Unknown to the ECM\n", dev, channel_protocol);
 	type_info.unknown.os_specific_ident = dev_interface_num;
 
@@ -1968,7 +2138,6 @@
 	 */
 	ii = ecm_interface_unknown_interface_establish(&type_info.unknown, dev_name, dev_interface_num, ae_interface_num, dev_mtu);
 	return ii;
-
 #endif
 }
 EXPORT_SYMBOL(ecm_interface_establish_and_ref);
@@ -2817,6 +2986,20 @@
 	}
 #endif
 
+#ifdef ECM_INTERFACE_PPTP_ENABLE
+	/*
+	 * if the address is a local address and indev=PPTP.
+	 */
+	if (protocol == IPPROTO_GRE && given_dest_dev && given_dest_dev->type == ARPHRD_PPP) {
+		dev_put(dest_dev);
+		dest_dev = given_dest_dev;
+		if (dest_dev) {
+			dev_hold(dest_dev);
+			DEBUG_TRACE("PPTP packet tunnel packet with dest_addr: " ECM_IP_ADDR_OCTAL_FMT " uses dev: %p(%s)\n", ECM_IP_ADDR_TO_OCTAL(dest_addr), dest_dev, dest_dev->name);
+		}
+	}
+#endif
+
 	if (!dest_dev) {
 		DEBUG_WARN("dest_addr: " ECM_IP_ADDR_OCTAL_FMT " - cannot locate device\n", ECM_IP_ADDR_TO_OCTAL(dest_addr));
 		return ECM_DB_IFACE_HEIRARCHY_MAX;
@@ -2936,7 +3119,6 @@
 	while (current_interface_index > 0) {
 		struct ecm_db_iface_instance *ii;
 		struct net_device *next_dev;
-
 		/*
 		 * Get the ecm db interface instance for the device at hand
 		 */
@@ -3260,6 +3442,13 @@
 				}
 			}
 #endif
+
+#ifdef ECM_INTERFACE_PPTP_ENABLE
+			if (protocol == IPPROTO_GRE && dest_dev && dest_dev->type == ARPHRD_PPP) {
+				DEBUG_TRACE("Net device: %p PPP channel is PPTP\n", dest_dev);
+				break;
+			}
+#endif
 			/*
 			 * PPP - but what is the channel type?
 			 * First: If this is multi-link then we do not support it
diff --git a/ecm_interface.h b/ecm_interface.h
index 78b05ed..adc903d 100644
--- a/ecm_interface.h
+++ b/ecm_interface.h
@@ -51,7 +51,8 @@
 #ifdef ECM_IPV6_ENABLE
 struct neighbour *ecm_interface_ipv6_neigh_get(ip_addr_t addr);
 #endif
-bool ecm_interface_skip_l2tpv3_pptp(struct sk_buff *skb, const struct net_device *out);
+bool ecm_interface_skip_pptp(struct sk_buff *skb, const struct net_device *out);
+bool ecm_interface_skip_l2tp_packet_by_version(struct sk_buff *skb, const struct net_device *out, int ver);
 bool ecm_interface_skip_l2tp_pptp(struct sk_buff *skb, const struct net_device *out);
 struct ecm_db_iface_instance *ecm_interface_establish_and_ref(struct ecm_front_end_connection_instance *feci, struct net_device *dev, struct sk_buff *skb);
 
diff --git a/frontends/nss/ecm_nss_ipv4.c b/frontends/nss/ecm_nss_ipv4.c
index f34f2e6..9f9037f 100644
--- a/frontends/nss/ecm_nss_ipv4.c
+++ b/frontends/nss/ecm_nss_ipv4.c
@@ -188,7 +188,7 @@
 	int i;
 	bool done;
 	uint8_t node_addr[ETH_ALEN];
-#ifdef ECM_INTERFACE_L2TPV2_ENABLE
+#if defined(ECM_INTERFACE_L2TPV2_ENABLE) || defined(ECM_INTERFACE_PPTP_ENABLE)
 	ip_addr_t local_ip, remote_ip;
 #endif
 
@@ -222,6 +222,9 @@
 #ifdef ECM_INTERFACE_L2TPV2_ENABLE
 		struct ecm_db_interface_info_pppol2tpv2 pppol2tpv2_info;
 #endif
+#ifdef ECM_INTERFACE_PPTP_ENABLE
+		struct ecm_db_interface_info_pptp pptp_info;
+#endif
 		type = ecm_db_connection_iface_type_get(interface_list[i]);
 		DEBUG_INFO("Lookup node address, interface @ %d is type: %d\n", i, type);
 
@@ -271,6 +274,30 @@
 			return NULL;
 #endif
 
+		case ECM_DB_IFACE_TYPE_PPTP:
+#ifdef ECM_INTERFACE_PPTP_ENABLE
+			ecm_db_iface_pptp_session_info_get(interface_list[i], &pptp_info);
+			ECM_HIN4_ADDR_TO_IP_ADDR(local_ip, pptp_info.src_ip);
+			ECM_HIN4_ADDR_TO_IP_ADDR(remote_ip, pptp_info.dst_ip);
+			if (ECM_IP_ADDR_MATCH(local_ip, addr)) {
+				if (unlikely(!ecm_interface_mac_addr_get(local_ip, node_addr, &on_link, gw_addr))) {
+					DEBUG_TRACE("failed to obtain node address for " ECM_IP_ADDR_DOT_FMT "\n", ECM_IP_ADDR_TO_DOT(local_ip));
+					return NULL;
+				}
+
+			} else {
+				if (unlikely(!ecm_interface_mac_addr_get(remote_ip, node_addr, &on_link, gw_addr))) {
+					DEBUG_TRACE("failed to obtain node address for host " ECM_IP_ADDR_DOT_FMT "\n", ECM_IP_ADDR_TO_DOT(remote_ip));
+					return NULL;
+				}
+			}
+
+			done = true;
+			break;
+#else
+			DEBUG_TRACE("PPTP interface unsupported\n");
+			return NULL;
+#endif
 		case ECM_DB_IFACE_TYPE_VLAN:
 #ifdef ECM_INTERFACE_VLAN_ENABLE
 			/*
@@ -1349,18 +1376,27 @@
 #endif
 
 #ifdef ECM_INTERFACE_PPP_ENABLE
-#ifdef ECM_INTERFACE_L2TPV2_ENABLE
+#ifndef ECM_INTERFACE_PPTP_ENABLE
 	/*
-	 * skip l2tpv3/pptp because we don't accelerate them
+	 * skip pptp because we don't accelerate them
 	 */
-	if (ecm_interface_skip_l2tpv3_pptp(skb, out)) {
+	if (ecm_interface_skip_pptp(skb, out)) {
+		return NF_ACCEPT;
+	}
+#endif
+#ifndef ECM_INTERFACE_L2TPV2_ENABLE
+	/*
+	 * skip l2tp v2 and v3, because we don't accelerate them
+	 */
+	if (ecm_interface_skip_l2tp_packet_by_version(skb, out, 2) ||
+		ecm_interface_l2tp_version_check(skb, out, 3)) {
 		return NF_ACCEPT;
 	}
 #else
 	/*
-	 * skip l2tp/pptp because we don't accelerate them
+	 * skip l2tpv3 because we don't accelerate them
 	 */
-	if (ecm_interface_skip_l2tp_pptp(skb, out)) {
+	if (ecm_interface_skip_l2tp_packet_by_version(skb, out, 3)) {
 		return NF_ACCEPT;
 	}
 #endif
diff --git a/frontends/nss/ecm_nss_ipv6.c b/frontends/nss/ecm_nss_ipv6.c
index 192a052..745609a 100644
--- a/frontends/nss/ecm_nss_ipv6.c
+++ b/frontends/nss/ecm_nss_ipv6.c
@@ -1065,9 +1065,17 @@
 #ifdef ECM_INTERFACE_PPP_ENABLE
 #ifdef ECM_INTERFACE_L2TPV2_ENABLE
 	/*
-	 * skip l2tpv3/pptp because we don't accelerate them
+	 * skip l2tpv3 because we don't accelerate them
 	 */
-	if (ecm_interface_skip_l2tpv3_pptp(skb, out)) {
+	if (ecm_interface_skip_l2tp_packet_by_version(skb, out, 3)) {
+		return NF_ACCEPT;
+	}
+
+	/*
+	 * Skip PPTP beacuse we don't support acceleration for
+	 * IPv6 inner header over PPTP
+	 */
+	if (ecm_interface_skip_pptp(skb, out)) {
 		return NF_ACCEPT;
 	}
 #else
diff --git a/frontends/nss/ecm_nss_non_ported_ipv4.c b/frontends/nss/ecm_nss_non_ported_ipv4.c
index cdeb75d..59ce422 100644
--- a/frontends/nss/ecm_nss_non_ported_ipv4.c
+++ b/frontends/nss/ecm_nss_non_ported_ipv4.c
@@ -387,7 +387,10 @@
 	uint8_t from_nss_iface_address[ETH_ALEN];
 	uint8_t to_nss_iface_address[ETH_ALEN];
 	ip_addr_t addr;
-	struct nss_ipv4_msg nim;
+#if defined(ECM_INTERFACE_L2TPV2_ENABLE) ||  defined(ECM_INTERFACE_PPTP_ENABLE)
+	struct net_device *dev __attribute__((unused));
+#endif
+	struct nss_ipv4_msg *nim;
 	struct nss_ipv4_rule_create_msg *nircm;
 	struct ecm_classifier_instance *assignments[ECM_CLASSIFIER_TYPES];
 	int aci_index;
@@ -413,7 +416,7 @@
 	 * For non-ported protocols we only support IPv6 in 4 or ESP
 	 */
 	protocol = ecm_db_connection_protocol_get(feci->ci);
-	if ((protocol != IPPROTO_IPV6) && (protocol != IPPROTO_ESP)) {
+	if ((protocol != IPPROTO_IPV6) && (protocol != IPPROTO_ESP) && (protocol != IPPROTO_GRE)) {
 		spin_lock_bh(&feci->lock);
 		feci->accel_mode = ECM_FRONT_END_ACCELERATION_MODE_FAIL_RULE;
 		spin_unlock_bh(&feci->lock);
@@ -429,19 +432,24 @@
 		return;
 	}
 
+	nim = (struct nss_ipv4_msg *)kzalloc(sizeof(struct nss_ipv4_msg), GFP_ATOMIC | __GFP_NOWARN);
+	if (!nim) {
+		DEBUG_WARN("%p: no memory for nss ipv4 message structure instance: %p\n", feci, feci->ci);
+		return;
+	}
+
 	/*
 	 * Okay construct an accel command.
 	 * Initialise creation structure.
 	 * NOTE: We leverage the app_data void pointer to be our 32 bit connection serial number.
 	 * When we get it back we re-cast it to a uint32 and do a faster connection lookup.
 	 */
-	memset(&nim, 0, sizeof(struct nss_ipv4_msg));
-	nss_ipv4_msg_init(&nim, NSS_IPV4_RX_INTERFACE, NSS_IPV4_TX_CREATE_RULE_MSG,
+	nss_ipv4_msg_init(nim, NSS_IPV4_RX_INTERFACE, NSS_IPV4_TX_CREATE_RULE_MSG,
 			sizeof(struct nss_ipv4_rule_create_msg),
 			ecm_nss_non_ported_ipv4_connection_callback,
 			(void *)ecm_db_connection_serial_get(feci->ci));
 
-	nircm = &nim.msg.rule_create;
+	nircm = &nim->msg.rule_create;
 	nircm->valid_flags = 0;
 	nircm->rule_flags = 0;
 
@@ -654,6 +662,31 @@
 			DEBUG_TRACE("%p: IPSEC - unsupported\n", nnpci);
 #endif
 			break;
+		case ECM_DB_IFACE_TYPE_PPTP:
+#ifdef ECM_INTERFACE_PPTP_ENABLE
+			/*
+			 * pptp packets from lan to wan gets routed twice.
+			 *	1. eth1-->br-lan to pptp
+			 *	2. pptp ---> eth0.
+			 * we need to push nss rule for both. Requirement
+			 * mandidates multiple session per tunnel and all tunnel
+			 * packets has same 5 tuple info. So need to create a
+			 * special static pptp interface with nss to identify
+			 * packets from wan side. So pptp--->eth0 rule to be
+			 * pushed with this static interface.
+			 */
+			ecm_db_connection_from_address_get(feci->ci, addr);
+			dev = ecm_interface_dev_find_by_local_addr(addr);
+			if (likely(dev)) {
+				dev_put(dev);
+				from_nss_iface_id =  NSS_PPTP_INTERFACE;
+				nircm->conn_rule.flow_interface_num = from_nss_iface_id;
+			}
+#else
+			rule_invalid = true;
+			DEBUG_TRACE("%p: PPTP - unsupported\n", nnpci);
+#endif
+			break;
 		default:
 			DEBUG_TRACE("%p: Ignoring: %d (%s)\n", nnpci, ii_type, ii_name);
 		}
@@ -875,6 +908,20 @@
 	 * The flow_ip is where the connection established from
 	 */
 	ecm_db_connection_from_address_get(feci->ci, addr);
+
+	/*
+	 * Get MTU information
+	 */
+	nircm->conn_rule.flow_mtu = (uint32_t)ecm_db_connection_from_iface_mtu_get(feci->ci);
+#ifdef ECM_INTERFACE_PPTP_ENABLE
+	if (unlikely(from_nss_iface_id ==  NSS_PPTP_INTERFACE)) {
+		dev = ecm_interface_dev_find_by_local_addr(addr);
+		if (likely(dev)) {
+			nircm->conn_rule.flow_mtu = dev->mtu;
+			dev_put(dev);
+		}
+	}
+#endif
 	ECM_IP_ADDR_TO_HIN4_ADDR(nircm->tuple.flow_ip, addr);
 
 	/*
@@ -951,7 +998,6 @@
 	/*
 	 * Get MTU information
 	 */
-	nircm->conn_rule.flow_mtu = (uint32_t)ecm_db_connection_from_iface_mtu_get(feci->ci);
 	nircm->conn_rule.return_mtu = (uint32_t)ecm_db_connection_to_iface_mtu_get(feci->ci);
 
 	/*
@@ -1048,6 +1094,7 @@
 	if (regen_occurrances != ecm_db_connection_regeneration_occurrances_get(feci->ci)) {
 		DEBUG_INFO("%p: connection:%p regen occurred - aborting accel rule.\n", feci, feci->ci);
 		ecm_nss_ipv4_accel_pending_clear(feci, ECM_FRONT_END_ACCELERATION_MODE_DECEL);
+		kfree(nim);
 		return;
 	}
 
@@ -1069,7 +1116,7 @@
 	/*
 	 * Call the rule create function
 	 */
-	nss_tx_status = nss_ipv4_tx(ecm_nss_ipv4_nss_ipv4_mgr, &nim);
+	nss_tx_status = nss_ipv4_tx(ecm_nss_ipv4_nss_ipv4_mgr, nim);
 	if (nss_tx_status == NSS_TX_SUCCESS) {
 		/*
 		 * Reset the driver_fail count - transmission was okay here.
@@ -1077,9 +1124,12 @@
 		spin_lock_bh(&feci->lock);
 		feci->stats.driver_fail = 0;
 		spin_unlock_bh(&feci->lock);
+		kfree(nim);
 		return;
 	}
 
+	kfree(nim);
+
 	/*
 	 * Release that ref!
 	 */
@@ -1109,6 +1159,7 @@
 non_ported_accel_bad_rule:
 	;
 
+	kfree(nim);
 	/*
 	 * Jump to here when rule data is bad and an offload command cannot be constructed
 	 */
@@ -1739,7 +1790,7 @@
 	 * Look up a connection.
 	 */
 	protocol = (int)orig_tuple->dst.protonum;
-	if ((protocol == IPPROTO_IPV6) || (protocol == IPPROTO_ESP)) {
+	if ((protocol == IPPROTO_IPV6) || (protocol == IPPROTO_ESP || (protocol == IPPROTO_GRE))) {
 		src_port = 0;
 		src_port_nat = 0;
 		dest_port = 0;
diff --git a/frontends/nss/ecm_nss_ported_ipv4.c b/frontends/nss/ecm_nss_ported_ipv4.c
index 58571ca..f2fb9a2 100644
--- a/frontends/nss/ecm_nss_ported_ipv4.c
+++ b/frontends/nss/ecm_nss_ported_ipv4.c
@@ -330,10 +330,10 @@
 	uint8_t from_nss_iface_address[ETH_ALEN];
 	uint8_t to_nss_iface_address[ETH_ALEN];
 	ip_addr_t addr;
-#ifdef ECM_INTERFACE_L2TPV2_ENABLE
+#if defined(ECM_INTERFACE_L2TPV2_ENABLE) || defined(ECM_INTERFACE_PPTP_ENABLE)
 	struct net_device *dev;
 #endif
-	struct nss_ipv4_msg nim;
+	struct nss_ipv4_msg *nim;
 	struct nss_ipv4_rule_create_msg *nircm;
 	struct ecm_classifier_instance *assignments[ECM_CLASSIFIER_TYPES];
 	int aci_index;
@@ -363,19 +363,24 @@
 		return;
 	}
 
+	nim = (struct nss_ipv4_msg *)kzalloc(sizeof(struct nss_ipv4_msg), GFP_ATOMIC | __GFP_NOWARN);
+	if (!nim) {
+		DEBUG_WARN("%p: no memory for nss ipv4 message structure instance: %p\n", feci, feci->ci);
+		return;
+	}
+
 	/*
 	 * Okay construct an accel command.
 	 * Initialise creation structure.
 	 * NOTE: We leverage the app_data void pointer to be our 32 bit connection serial number.
 	 * When we get it back we re-cast it to a uint32 and do a faster connection lookup.
 	 */
-	memset(&nim, 0, sizeof(struct nss_ipv4_msg));
-	nss_ipv4_msg_init(&nim, NSS_IPV4_RX_INTERFACE, NSS_IPV4_TX_CREATE_RULE_MSG,
+	nss_ipv4_msg_init(nim, NSS_IPV4_RX_INTERFACE, NSS_IPV4_TX_CREATE_RULE_MSG,
 			sizeof(struct nss_ipv4_rule_create_msg),
 			ecm_nss_ported_ipv4_connection_callback,
 			(void *)ecm_db_connection_serial_get(feci->ci));
 
-	nircm = &nim.msg.rule_create;
+	nircm = &nim->msg.rule_create;
 	nircm->valid_flags = 0;
 	nircm->rule_flags = 0;
 
@@ -793,6 +798,31 @@
 			DEBUG_TRACE("%p: IPSEC - unsupported\n", npci);
 #endif
 			break;
+		case ECM_DB_IFACE_TYPE_PPTP:
+#ifdef ECM_INTERFACE_PPTP_ENABLE
+			/*
+			 * pptp packets from lan to wan gets routed twice.
+			 *	1. eth1-->br-lan to pptp
+			 *	2. pptp ---> eth0.
+			 * we need to push nss rule for both. Requirement
+			 * mandidates multiple session per tunnel and all tunnel
+			 * packets has same 5 tuple info. So need to create a
+			 * special static pptp interface with nss to identify
+			 * packets from wan side. So pptp--->eth0 rule to be
+			 * pushed with this static interface.
+			 */
+			ecm_db_connection_to_address_get(feci->ci, addr);
+			dev = ecm_interface_dev_find_by_local_addr(addr);
+			if (likely(dev)) {
+				dev_put(dev);
+				from_nss_iface_id =  NSS_PPTP_INTERFACE;
+				nircm->conn_rule.flow_interface_num = from_nss_iface_id;
+			}
+#else
+			rule_invalid = true;
+			DEBUG_TRACE("%p: PPTP - unsupported\n", npci);
+#endif
+			break;
 		default:
 			DEBUG_TRACE("%p: Ignoring: %d (%s)\n", npci, ii_type, ii_name);
 		}
@@ -1110,6 +1140,7 @@
 	if (regen_occurrances != ecm_db_connection_regeneration_occurrances_get(feci->ci)) {
 		DEBUG_INFO("%p: connection:%p regen occurred - aborting accel rule.\n", feci, feci->ci);
 		ecm_nss_ipv4_accel_pending_clear(feci, ECM_FRONT_END_ACCELERATION_MODE_DECEL);
+		kfree(nim);
 		return;
 	}
 
@@ -1131,7 +1162,7 @@
 	/*
 	 * Call the rule create function
 	 */
-	nss_tx_status = nss_ipv4_tx(ecm_nss_ipv4_nss_ipv4_mgr, &nim);
+	nss_tx_status = nss_ipv4_tx(ecm_nss_ipv4_nss_ipv4_mgr, nim);
 	if (nss_tx_status == NSS_TX_SUCCESS) {
 		/*
 		 * Reset the driver_fail count - transmission was okay here.
@@ -1139,9 +1170,12 @@
 		spin_lock_bh(&feci->lock);
 		feci->stats.driver_fail = 0;
 		spin_unlock_bh(&feci->lock);
+		kfree(nim);
 		return;
 	}
 
+	kfree(nim);
+
 	/*
 	 * Release that ref!
 	 */
@@ -1171,6 +1205,8 @@
 ported_accel_bad_rule:
 	;
 
+	kfree(nim);
+
 	/*
 	 * Jump to here when rule data is bad and an offload command cannot be constructed
 	 */
@@ -1305,7 +1341,6 @@
 	nss_tx_status_t nss_tx_status;
 
 	DEBUG_CHECK_MAGIC(npci, ECM_NSS_PORTED_IPV4_CONNECTION_INSTANCE_MAGIC, "%p: magic failed", npci);
-
 	/*
 	 * If decelerate is in error or already pending then ignore
 	 */