msm: ipa4: add IPv6CT support

IPAv4 HW presents new IPv6 connection tracking functionality.
This change adds SW support of this feature.

Change-Id: I7545af08f090405db80bbd8c10223ca7521080d9
Acked-by: Dmitry Kogan <dmitryk@qti.qualcomm.com>
Signed-off-by: Amir Levy <alevy@codeaurora.org>
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c
index 9d55032..545e949 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c
@@ -147,15 +147,33 @@
 #define IPA_IOC_ALLOC_NAT_MEM32 _IOWR(IPA_IOC_MAGIC, \
 				IPA_IOCTL_ALLOC_NAT_MEM, \
 				compat_uptr_t)
+#define IPA_IOC_ALLOC_NAT_TABLE32 _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_ALLOC_NAT_TABLE, \
+				compat_uptr_t)
+#define IPA_IOC_ALLOC_IPV6CT_TABLE32 _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_ALLOC_IPV6CT_TABLE, \
+				compat_uptr_t)
 #define IPA_IOC_V4_INIT_NAT32 _IOWR(IPA_IOC_MAGIC, \
 				IPA_IOCTL_V4_INIT_NAT, \
 				compat_uptr_t)
-#define IPA_IOC_NAT_DMA32 _IOWR(IPA_IOC_MAGIC, \
-				IPA_IOCTL_NAT_DMA, \
+#define IPA_IOC_INIT_IPV6CT_TABLE32 _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_INIT_IPV6CT_TABLE, \
+				compat_uptr_t)
+#define IPA_IOC_TABLE_DMA_CMD32 _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_TABLE_DMA_CMD, \
 				compat_uptr_t)
 #define IPA_IOC_V4_DEL_NAT32 _IOWR(IPA_IOC_MAGIC, \
 				IPA_IOCTL_V4_DEL_NAT, \
 				compat_uptr_t)
+#define IPA_IOC_DEL_NAT_TABLE32 _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_DEL_NAT_TABLE, \
+				compat_uptr_t)
+#define IPA_IOC_DEL_IPV6CT_TABLE32 _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_DEL_IPV6CT_TABLE, \
+				compat_uptr_t)
+#define IPA_IOC_NAT_MODIFY_PDN32 _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_NAT_MODIFY_PDN, \
+				compat_uptr_t)
 #define IPA_IOC_GET_NAT_OFFSET32 _IOWR(IPA_IOC_MAGIC, \
 				IPA_IOCTL_GET_NAT_OFFSET, \
 				compat_uptr_t)
@@ -211,6 +229,18 @@
 	compat_size_t size;
 	compat_off_t offset;
 };
+
+/**
+ * struct ipa_ioc_nat_ipv6ct_table_alloc32 - table memory allocation
+ * properties
+ * @size: input parameter, size of table in bytes
+ * @offset: output parameter, offset into page in case of system memory
+ */
+struct ipa_ioc_nat_ipv6ct_table_alloc32 {
+	compat_size_t size;
+	compat_off_t offset;
+};
+
 #endif
 
 #define IPA_TZ_UNLOCK_ATTRIBUTE 0x0C0311
@@ -545,7 +575,7 @@
 		return -ENOMEM;
 	}
 
-	if (copy_from_user((u8 *)wan_msg, (u8 *)usr_param,
+	if (copy_from_user(wan_msg, (const void __user *)usr_param,
 		sizeof(struct ipa_wan_msg))) {
 		kfree(wan_msg);
 		return -EFAULT;
@@ -677,10 +707,14 @@
 	u8 header[128] = { 0 };
 	u8 *param = NULL;
 	struct ipa_ioc_nat_alloc_mem nat_mem;
+	struct ipa_ioc_nat_ipv6ct_table_alloc table_alloc;
 	struct ipa_ioc_v4_nat_init nat_init;
+	struct ipa_ioc_ipv6ct_init ipv6ct_init;
 	struct ipa_ioc_v4_nat_del nat_del;
+	struct ipa_ioc_nat_ipv6ct_table_del table_del;
 	struct ipa_ioc_nat_pdn_entry mdfy_pdn;
 	struct ipa_ioc_rm_dependency rm_depend;
+	struct ipa_ioc_nat_dma_cmd *table_dma_cmd;
 	size_t sz;
 	int pre_entry;
 
@@ -688,8 +722,6 @@
 
 	if (_IOC_TYPE(cmd) != IPA_IOC_MAGIC)
 		return -ENOTTY;
-	if (_IOC_NR(cmd) >= IPA_IOCTL_MAX)
-		return -ENOTTY;
 
 	if (!ipa3_is_ready()) {
 		IPAERR("IPA not ready, waiting for init completion\n");
@@ -700,8 +732,8 @@
 
 	switch (cmd) {
 	case IPA_IOC_ALLOC_NAT_MEM:
-		if (copy_from_user((u8 *)&nat_mem, (u8 *)arg,
-					sizeof(struct ipa_ioc_nat_alloc_mem))) {
+		if (copy_from_user(&nat_mem, (const void __user *)arg,
+			sizeof(struct ipa_ioc_nat_alloc_mem))) {
 			retval = -EFAULT;
 			break;
 		}
@@ -712,15 +744,53 @@
 			retval = -EFAULT;
 			break;
 		}
-		if (copy_to_user((u8 *)arg, (u8 *)&nat_mem,
-					sizeof(struct ipa_ioc_nat_alloc_mem))) {
+		if (copy_to_user((void __user *)arg, &nat_mem,
+			sizeof(struct ipa_ioc_nat_alloc_mem))) {
 			retval = -EFAULT;
 			break;
 		}
 		break;
+	case IPA_IOC_ALLOC_NAT_TABLE:
+		if (copy_from_user(&table_alloc, (const void __user *)arg,
+			sizeof(struct ipa_ioc_nat_ipv6ct_table_alloc))) {
+			retval = -EFAULT;
+			break;
+		}
+
+		if (ipa3_allocate_nat_table(&table_alloc)) {
+			retval = -EFAULT;
+			break;
+		}
+		if (table_alloc.offset &&
+			copy_to_user((void __user *)arg, &table_alloc, sizeof(
+				struct ipa_ioc_nat_ipv6ct_table_alloc))) {
+			retval = -EFAULT;
+			break;
+		}
+		break;
+
+	case IPA_IOC_ALLOC_IPV6CT_TABLE:
+		if (copy_from_user(&table_alloc, (const void __user *)arg,
+			sizeof(struct ipa_ioc_nat_ipv6ct_table_alloc))) {
+			retval = -EFAULT;
+			break;
+		}
+
+		if (ipa3_allocate_ipv6ct_table(&table_alloc)) {
+			retval = -EFAULT;
+			break;
+		}
+		if (table_alloc.offset &&
+			copy_to_user((void __user *)arg, &table_alloc, sizeof(
+				struct ipa_ioc_nat_ipv6ct_table_alloc))) {
+			retval = -EFAULT;
+			break;
+		}
+		break;
+
 	case IPA_IOC_V4_INIT_NAT:
-		if (copy_from_user((u8 *)&nat_init, (u8 *)arg,
-					sizeof(struct ipa_ioc_v4_nat_init))) {
+		if (copy_from_user(&nat_init, (const void __user *)arg,
+			sizeof(struct ipa_ioc_v4_nat_init))) {
 			retval = -EFAULT;
 			break;
 		}
@@ -730,45 +800,56 @@
 		}
 		break;
 
-	case IPA_IOC_NAT_DMA:
-		if (copy_from_user(header, (u8 *)arg,
-					sizeof(struct ipa_ioc_nat_dma_cmd))) {
+	case IPA_IOC_INIT_IPV6CT_TABLE:
+		if (copy_from_user(&ipv6ct_init, (const void __user *)arg,
+			sizeof(struct ipa_ioc_ipv6ct_init))) {
 			retval = -EFAULT;
 			break;
 		}
-		pre_entry =
-			((struct ipa_ioc_nat_dma_cmd *)header)->entries;
-		pyld_sz =
-		   sizeof(struct ipa_ioc_nat_dma_cmd) +
-		   pre_entry * sizeof(struct ipa_ioc_nat_dma_one);
+		if (ipa3_ipv6ct_init_cmd(&ipv6ct_init)) {
+			retval = -EFAULT;
+			break;
+		}
+		break;
+
+	case IPA_IOC_TABLE_DMA_CMD:
+		table_dma_cmd = (struct ipa_ioc_nat_dma_cmd *)header;
+		if (copy_from_user(header, (const void __user *)arg,
+			sizeof(struct ipa_ioc_nat_dma_cmd))) {
+			retval = -EFAULT;
+			break;
+		}
+		pre_entry = table_dma_cmd->entries;
+		pyld_sz = sizeof(struct ipa_ioc_nat_dma_cmd) +
+			pre_entry * sizeof(struct ipa_ioc_nat_dma_one);
 		param = kzalloc(pyld_sz, GFP_KERNEL);
 		if (!param) {
 			retval = -ENOMEM;
 			break;
 		}
 
-		if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+		if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
+		table_dma_cmd = (struct ipa_ioc_nat_dma_cmd *)param;
+
 		/* add check in case user-space module compromised */
-		if (unlikely(((struct ipa_ioc_nat_dma_cmd *)param)->entries
-			!= pre_entry)) {
+		if (unlikely(table_dma_cmd->entries != pre_entry)) {
 			IPAERR_RL("current %d pre %d\n",
-				((struct ipa_ioc_nat_dma_cmd *)param)->entries,
-				pre_entry);
+				table_dma_cmd->entries, pre_entry);
 			retval = -EFAULT;
 			break;
 		}
-		if (ipa3_nat_dma_cmd((struct ipa_ioc_nat_dma_cmd *)param)) {
+		if (ipa3_table_dma_cmd(table_dma_cmd)) {
 			retval = -EFAULT;
 			break;
 		}
 		break;
 
 	case IPA_IOC_V4_DEL_NAT:
-		if (copy_from_user((u8 *)&nat_del, (u8 *)arg,
-					sizeof(struct ipa_ioc_v4_nat_del))) {
+		if (copy_from_user(&nat_del, (const void __user *)arg,
+			sizeof(struct ipa_ioc_v4_nat_del))) {
 			retval = -EFAULT;
 			break;
 		}
@@ -778,8 +859,32 @@
 		}
 		break;
 
+	case IPA_IOC_DEL_NAT_TABLE:
+		if (copy_from_user(&table_del, (const void __user *)arg,
+			sizeof(struct ipa_ioc_nat_ipv6ct_table_del))) {
+			retval = -EFAULT;
+			break;
+		}
+		if (ipa3_del_nat_table(&table_del)) {
+			retval = -EFAULT;
+			break;
+		}
+		break;
+
+	case IPA_IOC_DEL_IPV6CT_TABLE:
+		if (copy_from_user(&table_del, (const void __user *)arg,
+			sizeof(struct ipa_ioc_nat_ipv6ct_table_del))) {
+			retval = -EFAULT;
+			break;
+		}
+		if (ipa3_del_ipv6ct_table(&table_del)) {
+			retval = -EFAULT;
+			break;
+		}
+		break;
+
 	case IPA_IOC_NAT_MODIFY_PDN:
-		if (copy_from_user((u8 *)&mdfy_pdn, (const void __user *)arg,
+		if (copy_from_user(&mdfy_pdn, (const void __user *)arg,
 			sizeof(struct ipa_ioc_nat_pdn_entry))) {
 			retval = -EFAULT;
 			break;
@@ -791,8 +896,8 @@
 		break;
 
 	case IPA_IOC_ADD_HDR:
-		if (copy_from_user(header, (u8 *)arg,
-					sizeof(struct ipa_ioc_add_hdr))) {
+		if (copy_from_user(header, (const void __user *)arg,
+			sizeof(struct ipa_ioc_add_hdr))) {
 			retval = -EFAULT;
 			break;
 		}
@@ -806,7 +911,7 @@
 			retval = -ENOMEM;
 			break;
 		}
-		if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+		if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
@@ -823,15 +928,15 @@
 			retval = -EFAULT;
 			break;
 		}
-		if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+		if (copy_to_user((void __user *)arg, param, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
 		break;
 
 	case IPA_IOC_DEL_HDR:
-		if (copy_from_user(header, (u8 *)arg,
-					sizeof(struct ipa_ioc_del_hdr))) {
+		if (copy_from_user(header, (const void __user *)arg,
+			sizeof(struct ipa_ioc_del_hdr))) {
 			retval = -EFAULT;
 			break;
 		}
@@ -845,7 +950,7 @@
 			retval = -ENOMEM;
 			break;
 		}
-		if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+		if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
@@ -863,15 +968,15 @@
 			retval = -EFAULT;
 			break;
 		}
-		if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+		if (copy_to_user((void __user *)arg, param, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
 		break;
 
 	case IPA_IOC_ADD_RT_RULE:
-		if (copy_from_user(header, (u8 *)arg,
-					sizeof(struct ipa_ioc_add_rt_rule))) {
+		if (copy_from_user(header, (const void __user *)arg,
+			sizeof(struct ipa_ioc_add_rt_rule))) {
 			retval = -EFAULT;
 			break;
 		}
@@ -885,7 +990,7 @@
 			retval = -ENOMEM;
 			break;
 		}
-		if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+		if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
@@ -903,13 +1008,13 @@
 			retval = -EFAULT;
 			break;
 		}
-		if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+		if (copy_to_user((void __user *)arg, param, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
 		break;
 	case IPA_IOC_ADD_RT_RULE_AFTER:
-		if (copy_from_user(header, (u8 *)arg,
+		if (copy_from_user(header, (const void __user *)arg,
 			sizeof(struct ipa_ioc_add_rt_rule_after))) {
 
 			retval = -EFAULT;
@@ -925,7 +1030,7 @@
 			retval = -ENOMEM;
 			break;
 		}
-		if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+		if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
@@ -945,15 +1050,15 @@
 			retval = -EFAULT;
 			break;
 		}
-		if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+		if (copy_to_user((void __user *)arg, param, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
 		break;
 
 	case IPA_IOC_MDFY_RT_RULE:
-		if (copy_from_user(header, (u8 *)arg,
-					sizeof(struct ipa_ioc_mdfy_rt_rule))) {
+		if (copy_from_user(header, (const void __user *)arg,
+			sizeof(struct ipa_ioc_mdfy_rt_rule))) {
 			retval = -EFAULT;
 			break;
 		}
@@ -967,7 +1072,7 @@
 			retval = -ENOMEM;
 			break;
 		}
-		if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+		if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
@@ -985,15 +1090,15 @@
 			retval = -EFAULT;
 			break;
 		}
-		if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+		if (copy_to_user((void __user *)arg, param, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
 		break;
 
 	case IPA_IOC_DEL_RT_RULE:
-		if (copy_from_user(header, (u8 *)arg,
-					sizeof(struct ipa_ioc_del_rt_rule))) {
+		if (copy_from_user(header, (const void __user *)arg,
+			sizeof(struct ipa_ioc_del_rt_rule))) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1007,7 +1112,7 @@
 			retval = -ENOMEM;
 			break;
 		}
-		if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+		if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1024,15 +1129,15 @@
 			retval = -EFAULT;
 			break;
 		}
-		if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+		if (copy_to_user((void __user *)arg, param, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
 		break;
 
 	case IPA_IOC_ADD_FLT_RULE:
-		if (copy_from_user(header, (u8 *)arg,
-					sizeof(struct ipa_ioc_add_flt_rule))) {
+		if (copy_from_user(header, (const void __user *)arg,
+			sizeof(struct ipa_ioc_add_flt_rule))) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1046,7 +1151,7 @@
 			retval = -ENOMEM;
 			break;
 		}
-		if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+		if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1064,15 +1169,15 @@
 			retval = -EFAULT;
 			break;
 		}
-		if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+		if (copy_to_user((void __user *)arg, param, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
 		break;
 
 	case IPA_IOC_ADD_FLT_RULE_AFTER:
-		if (copy_from_user(header, (u8 *)arg,
-				sizeof(struct ipa_ioc_add_flt_rule_after))) {
+		if (copy_from_user(header, (const void __user *)arg,
+			sizeof(struct ipa_ioc_add_flt_rule_after))) {
 
 			retval = -EFAULT;
 			break;
@@ -1088,7 +1193,7 @@
 			retval = -ENOMEM;
 			break;
 		}
-		if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+		if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1107,15 +1212,15 @@
 			retval = -EFAULT;
 			break;
 		}
-		if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+		if (copy_to_user((void __user *)arg, param, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
 		break;
 
 	case IPA_IOC_DEL_FLT_RULE:
-		if (copy_from_user(header, (u8 *)arg,
-					sizeof(struct ipa_ioc_del_flt_rule))) {
+		if (copy_from_user(header, (const void __user *)arg,
+			sizeof(struct ipa_ioc_del_flt_rule))) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1129,7 +1234,7 @@
 			retval = -ENOMEM;
 			break;
 		}
-		if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+		if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1147,15 +1252,15 @@
 			retval = -EFAULT;
 			break;
 		}
-		if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+		if (copy_to_user((void __user *)arg, param, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
 		break;
 
 	case IPA_IOC_MDFY_FLT_RULE:
-		if (copy_from_user(header, (u8 *)arg,
-					sizeof(struct ipa_ioc_mdfy_flt_rule))) {
+		if (copy_from_user(header, (const void __user *)arg,
+			sizeof(struct ipa_ioc_mdfy_flt_rule))) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1169,7 +1274,7 @@
 			retval = -ENOMEM;
 			break;
 		}
-		if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+		if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1187,7 +1292,7 @@
 			retval = -EFAULT;
 			break;
 		}
-		if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+		if (copy_to_user((void __user *)arg, param, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1212,8 +1317,8 @@
 		retval = ipa3_reset_flt(arg);
 		break;
 	case IPA_IOC_GET_RT_TBL:
-		if (copy_from_user(header, (u8 *)arg,
-					sizeof(struct ipa_ioc_get_rt_tbl))) {
+		if (copy_from_user(header, (const void __user *)arg,
+			sizeof(struct ipa_ioc_get_rt_tbl))) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1221,7 +1326,7 @@
 			retval = -EFAULT;
 			break;
 		}
-		if (copy_to_user((u8 *)arg, header,
+		if (copy_to_user((void __user *)arg, header,
 					sizeof(struct ipa_ioc_get_rt_tbl))) {
 			retval = -EFAULT;
 			break;
@@ -1231,8 +1336,8 @@
 		retval = ipa3_put_rt_tbl(arg);
 		break;
 	case IPA_IOC_GET_HDR:
-		if (copy_from_user(header, (u8 *)arg,
-					sizeof(struct ipa_ioc_get_hdr))) {
+		if (copy_from_user(header, (const void __user *)arg,
+			sizeof(struct ipa_ioc_get_hdr))) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1240,8 +1345,8 @@
 			retval = -EFAULT;
 			break;
 		}
-		if (copy_to_user((u8 *)arg, header,
-					sizeof(struct ipa_ioc_get_hdr))) {
+		if (copy_to_user((void __user *)arg, header,
+			sizeof(struct ipa_ioc_get_hdr))) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1253,8 +1358,8 @@
 		retval = ipa3_cfg_filter(arg);
 		break;
 	case IPA_IOC_COPY_HDR:
-		if (copy_from_user(header, (u8 *)arg,
-					sizeof(struct ipa_ioc_copy_hdr))) {
+		if (copy_from_user(header, (const void __user *)arg,
+			sizeof(struct ipa_ioc_copy_hdr))) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1262,15 +1367,15 @@
 			retval = -EFAULT;
 			break;
 		}
-		if (copy_to_user((u8 *)arg, header,
-					sizeof(struct ipa_ioc_copy_hdr))) {
+		if (copy_to_user((void __user *)arg, header,
+			sizeof(struct ipa_ioc_copy_hdr))) {
 			retval = -EFAULT;
 			break;
 		}
 		break;
 	case IPA_IOC_QUERY_INTF:
-		if (copy_from_user(header, (u8 *)arg,
-					sizeof(struct ipa_ioc_query_intf))) {
+		if (copy_from_user(header, (const void __user *)arg,
+			sizeof(struct ipa_ioc_query_intf))) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1278,21 +1383,21 @@
 			retval = -1;
 			break;
 		}
-		if (copy_to_user((u8 *)arg, header,
-					sizeof(struct ipa_ioc_query_intf))) {
+		if (copy_to_user((void __user *)arg, header,
+			sizeof(struct ipa_ioc_query_intf))) {
 			retval = -EFAULT;
 			break;
 		}
 		break;
 	case IPA_IOC_QUERY_INTF_TX_PROPS:
 		sz = sizeof(struct ipa_ioc_query_intf_tx_props);
-		if (copy_from_user(header, (u8 *)arg, sz)) {
+		if (copy_from_user(header, (const void __user *)arg, sz)) {
 			retval = -EFAULT;
 			break;
 		}
 
 		if (((struct ipa_ioc_query_intf_tx_props *)header)->num_tx_props
-				> IPA_NUM_PROPS_MAX) {
+			> IPA_NUM_PROPS_MAX) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1306,7 +1411,7 @@
 			retval = -ENOMEM;
 			break;
 		}
-		if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+		if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1321,24 +1426,24 @@
 			break;
 		}
 		if (ipa3_query_intf_tx_props(
-				(struct ipa_ioc_query_intf_tx_props *)param)) {
+			(struct ipa_ioc_query_intf_tx_props *)param)) {
 			retval = -1;
 			break;
 		}
-		if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+		if (copy_to_user((void __user *)arg, param, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
 		break;
 	case IPA_IOC_QUERY_INTF_RX_PROPS:
 		sz = sizeof(struct ipa_ioc_query_intf_rx_props);
-		if (copy_from_user(header, (u8 *)arg, sz)) {
+		if (copy_from_user(header, (const void __user *)arg, sz)) {
 			retval = -EFAULT;
 			break;
 		}
 
 		if (((struct ipa_ioc_query_intf_rx_props *)header)->num_rx_props
-				> IPA_NUM_PROPS_MAX) {
+			> IPA_NUM_PROPS_MAX) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1352,7 +1457,7 @@
 			retval = -ENOMEM;
 			break;
 		}
-		if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+		if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1366,24 +1471,24 @@
 			break;
 		}
 		if (ipa3_query_intf_rx_props(
-				(struct ipa_ioc_query_intf_rx_props *)param)) {
+			(struct ipa_ioc_query_intf_rx_props *)param)) {
 			retval = -1;
 			break;
 		}
-		if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+		if (copy_to_user((void __user *)arg, param, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
 		break;
 	case IPA_IOC_QUERY_INTF_EXT_PROPS:
 		sz = sizeof(struct ipa_ioc_query_intf_ext_props);
-		if (copy_from_user(header, (u8 *)arg, sz)) {
+		if (copy_from_user(header, (const void __user *)arg, sz)) {
 			retval = -EFAULT;
 			break;
 		}
 
 		if (((struct ipa_ioc_query_intf_ext_props *)
-				header)->num_ext_props > IPA_NUM_PROPS_MAX) {
+			header)->num_ext_props > IPA_NUM_PROPS_MAX) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1397,7 +1502,7 @@
 			retval = -ENOMEM;
 			break;
 		}
-		if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+		if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1411,18 +1516,18 @@
 			break;
 		}
 		if (ipa3_query_intf_ext_props(
-				(struct ipa_ioc_query_intf_ext_props *)param)) {
+			(struct ipa_ioc_query_intf_ext_props *)param)) {
 			retval = -1;
 			break;
 		}
-		if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+		if (copy_to_user((void __user *)arg, param, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
 		break;
 	case IPA_IOC_PULL_MSG:
-		if (copy_from_user(header, (u8 *)arg,
-					sizeof(struct ipa_msg_meta))) {
+		if (copy_from_user(header, (const void __user *)arg,
+			sizeof(struct ipa_msg_meta))) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1435,7 +1540,7 @@
 			retval = -ENOMEM;
 			break;
 		}
-		if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+		if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1449,13 +1554,13 @@
 			break;
 		}
 		if (ipa3_pull_msg((struct ipa_msg_meta *)param,
-				 (char *)param + sizeof(struct ipa_msg_meta),
-				 ((struct ipa_msg_meta *)param)->msg_len) !=
-		       ((struct ipa_msg_meta *)param)->msg_len) {
+			(char *)param + sizeof(struct ipa_msg_meta),
+			((struct ipa_msg_meta *)param)->msg_len) !=
+			((struct ipa_msg_meta *)param)->msg_len) {
 			retval = -1;
 			break;
 		}
-		if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+		if (copy_to_user((void __user *)arg, param, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1465,8 +1570,8 @@
 		if (ipa3_ctx->use_ipa_pm)
 			return 0;
 
-		if (copy_from_user((u8 *)&rm_depend, (u8 *)arg,
-				sizeof(struct ipa_ioc_rm_dependency))) {
+		if (copy_from_user(&rm_depend, (const void __user *)arg,
+			sizeof(struct ipa_ioc_rm_dependency))) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1478,8 +1583,8 @@
 		if (ipa3_ctx->use_ipa_pm)
 			return 0;
 
-		if (copy_from_user((u8 *)&rm_depend, (u8 *)arg,
-				sizeof(struct ipa_ioc_rm_dependency))) {
+		if (copy_from_user(&rm_depend, (const void __user *)arg,
+			sizeof(struct ipa_ioc_rm_dependency))) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1490,7 +1595,7 @@
 		{
 			struct ipa_ioc_generate_flt_eq flt_eq;
 
-			if (copy_from_user(&flt_eq, (u8 *)arg,
+			if (copy_from_user(&flt_eq, (const void __user *)arg,
 				sizeof(struct ipa_ioc_generate_flt_eq))) {
 				retval = -EFAULT;
 				break;
@@ -1500,7 +1605,7 @@
 				retval = -EFAULT;
 				break;
 			}
-			if (copy_to_user((u8 *)arg, &flt_eq,
+			if (copy_to_user((void __user *)arg, &flt_eq,
 				sizeof(struct ipa_ioc_generate_flt_eq))) {
 				retval = -EFAULT;
 				break;
@@ -1513,25 +1618,25 @@
 			break;
 		}
 	case IPA_IOC_QUERY_RT_TBL_INDEX:
-		if (copy_from_user(header, (u8 *)arg,
-				sizeof(struct ipa_ioc_get_rt_tbl_indx))) {
+		if (copy_from_user(header, (const void __user *)arg,
+			sizeof(struct ipa_ioc_get_rt_tbl_indx))) {
 			retval = -EFAULT;
 			break;
 		}
 		if (ipa3_query_rt_index(
-			 (struct ipa_ioc_get_rt_tbl_indx *)header)) {
+			(struct ipa_ioc_get_rt_tbl_indx *)header)) {
 			retval = -EFAULT;
 			break;
 		}
-		if (copy_to_user((u8 *)arg, header,
-				sizeof(struct ipa_ioc_get_rt_tbl_indx))) {
+		if (copy_to_user((void __user *)arg, header,
+			sizeof(struct ipa_ioc_get_rt_tbl_indx))) {
 			retval = -EFAULT;
 			break;
 		}
 		break;
 	case IPA_IOC_WRITE_QMAPID:
-		if (copy_from_user(header, (u8 *)arg,
-					sizeof(struct ipa_ioc_write_qmapid))) {
+		if (copy_from_user(header, (const void __user *)arg,
+			sizeof(struct ipa_ioc_write_qmapid))) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1539,8 +1644,8 @@
 			retval = -EFAULT;
 			break;
 		}
-		if (copy_to_user((u8 *)arg, header,
-					sizeof(struct ipa_ioc_write_qmapid))) {
+		if (copy_to_user((void __user *)arg, header,
+			sizeof(struct ipa_ioc_write_qmapid))) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1567,7 +1672,7 @@
 		}
 		break;
 	case IPA_IOC_ADD_HDR_PROC_CTX:
-		if (copy_from_user(header, (u8 *)arg,
+		if (copy_from_user(header, (const void __user *)arg,
 			sizeof(struct ipa_ioc_add_hdr_proc_ctx))) {
 			retval = -EFAULT;
 			break;
@@ -1583,7 +1688,7 @@
 			retval = -ENOMEM;
 			break;
 		}
-		if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+		if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1601,13 +1706,13 @@
 			retval = -EFAULT;
 			break;
 		}
-		if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+		if (copy_to_user((void __user *)arg, param, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
 		break;
 	case IPA_IOC_DEL_HDR_PROC_CTX:
-		if (copy_from_user(header, (u8 *)arg,
+		if (copy_from_user(header, (const void __user *)arg,
 			sizeof(struct ipa_ioc_del_hdr_proc_ctx))) {
 			retval = -EFAULT;
 			break;
@@ -1622,7 +1727,7 @@
 			retval = -ENOMEM;
 			break;
 		}
-		if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+		if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1641,7 +1746,7 @@
 			retval = -EFAULT;
 			break;
 		}
-		if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+		if (copy_to_user((void __user *)arg, param, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1655,7 +1760,7 @@
 			break;
 		}
 		memcpy(param, &ipa3_ctx->ipa_hw_type, pyld_sz);
-		if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+		if (copy_to_user((void __user *)arg, param, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1689,7 +1794,7 @@
 		}
 		break;
 
-	default:        /* redundant, as cmd was checked against MAXNR */
+	default:
 		IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
 		return -ENOTTY;
 	}
@@ -1713,8 +1818,8 @@
 	struct ipa_rt_rule_add *rt_rule_entry;
 
 	rt_rule =
-	   kzalloc(sizeof(struct ipa_ioc_add_rt_rule) + 1 *
-			   sizeof(struct ipa_rt_rule_add), GFP_KERNEL);
+		kzalloc(sizeof(struct ipa_ioc_add_rt_rule) + 1 *
+			sizeof(struct ipa_rt_rule_add), GFP_KERNEL);
 	if (!rt_rule) {
 		IPAERR("fail to alloc mem\n");
 		return -ENOMEM;
@@ -1724,7 +1829,7 @@
 	rt_rule->commit = 1;
 	rt_rule->ip = IPA_IP_v4;
 	strlcpy(rt_rule->rt_tbl_name, IPA_DFLT_RT_TBL_NAME,
-			IPA_RESOURCE_NAME_MAX);
+		IPA_RESOURCE_NAME_MAX);
 
 	rt_rule_entry = &rt_rule->rules[0];
 	rt_rule_entry->at_rear = 1;
@@ -1853,10 +1958,7 @@
 		IPAERR("failed to construct dma_shared_mem imm cmd\n");
 		return -ENOMEM;
 	}
-	desc.opcode = cmd_pyld->opcode;
-	desc.pyld = cmd_pyld->data;
-	desc.len = cmd_pyld->len;
-	desc.type = IPA_IMM_CMD_DESC;
+	ipa3_init_imm_cmd_desc(&desc, cmd_pyld);
 
 	rc = ipa3_send_cmd(1, &desc);
 	if (rc) {
@@ -2117,6 +2219,12 @@
 		if (!ipa3_ctx->ep[pipe_idx].valid ||
 		    ipa3_ctx->ep[pipe_idx].skip_ep_cfg) {
 
+			if (num_cmds >= ipa3_ctx->ep_flt_num) {
+				IPAERR("number of commands is out of range\n");
+				retval = -ENOBUFS;
+				goto free_empty_img;
+			}
+
 			cmd.is_read = false;
 			cmd.skip_pipeline_clear = false;
 			cmd.pipeline_clear_options = IPAHAL_HPS_CLEAR;
@@ -2134,14 +2242,12 @@
 				retval = -ENOMEM;
 				goto free_empty_img;
 			}
-			desc[num_cmds].opcode = cmd_pyld[num_cmds]->opcode;
-			desc[num_cmds].pyld = cmd_pyld[num_cmds]->data;
-			desc[num_cmds].len = cmd_pyld[num_cmds]->len;
-			desc[num_cmds].type = IPA_IMM_CMD_DESC;
-			num_cmds++;
+			ipa3_init_imm_cmd_desc(&desc[num_cmds],
+				cmd_pyld[num_cmds]);
+			++num_cmds;
 		}
 
-		flt_idx++;
+		++flt_idx;
 	}
 
 	IPADBG("Sending %d descriptors for flt tbl clearing\n", num_cmds);
@@ -2233,10 +2339,7 @@
 		retval = -ENOMEM;
 		goto free_desc;
 	}
-	desc->opcode = cmd_pyld->opcode;
-	desc->pyld = cmd_pyld->data;
-	desc->len = cmd_pyld->len;
-	desc->type = IPA_IMM_CMD_DESC;
+	ipa3_init_imm_cmd_desc(desc, cmd_pyld);
 
 	IPADBG("Sending 1 descriptor for rt tbl clearing\n");
 	retval = ipa3_send_cmd(1, desc);
@@ -2323,10 +2426,7 @@
 		retval = -EFAULT;
 		goto bail_desc;
 	}
-	desc->opcode = cmd_pyld->opcode;
-	desc->pyld = cmd_pyld->data;
-	desc->len = cmd_pyld->len;
-	desc->type = IPA_IMM_CMD_DESC;
+	ipa3_init_imm_cmd_desc(desc, cmd_pyld);
 
 	IPADBG("Sending 1 descriptor for tbls flush\n");
 	retval = ipa3_send_cmd(1, desc);
@@ -2389,13 +2489,10 @@
 				return -EFAULT;
 			}
 
-			desc[num_descs].opcode = cmd_pyld->opcode;
-			desc[num_descs].type = IPA_IMM_CMD_DESC;
+			ipa3_init_imm_cmd_desc(&desc[num_descs], cmd_pyld);
 			desc[num_descs].callback = ipa3_destroy_imm;
 			desc[num_descs].user1 = cmd_pyld;
-			desc[num_descs].pyld = cmd_pyld->data;
-			desc[num_descs].len = cmd_pyld->len;
-			num_descs++;
+			++num_descs;
 		}
 	}
 
@@ -2565,7 +2662,7 @@
  */
 int _ipa_init_hdr_v3_0(void)
 {
-	struct ipa3_desc desc = { 0 };
+	struct ipa3_desc desc;
 	struct ipa_mem_buffer mem;
 	struct ipahal_imm_cmd_hdr_init_local cmd = {0};
 	struct ipahal_imm_cmd_pyld *cmd_pyld;
@@ -2593,10 +2690,7 @@
 			mem.phys_base);
 		return -EFAULT;
 	}
-	desc.opcode = cmd_pyld->opcode;
-	desc.type = IPA_IMM_CMD_DESC;
-	desc.pyld = cmd_pyld->data;
-	desc.len = cmd_pyld->len;
+	ipa3_init_imm_cmd_desc(&desc, cmd_pyld);
 	IPA_DUMP_BUFF(mem.base, mem.phys_base, mem.size);
 
 	if (ipa3_send_cmd(1, &desc)) {
@@ -2620,7 +2714,6 @@
 		return -ENOMEM;
 	}
 	memset(mem.base, 0, mem.size);
-	memset(&desc, 0, sizeof(desc));
 
 	dma_cmd.is_read = false;
 	dma_cmd.skip_pipeline_clear = false;
@@ -2638,10 +2731,7 @@
 			mem.phys_base);
 		return -EFAULT;
 	}
-	desc.opcode = cmd_pyld->opcode;
-	desc.pyld = cmd_pyld->data;
-	desc.len = cmd_pyld->len;
-	desc.type = IPA_IMM_CMD_DESC;
+	ipa3_init_imm_cmd_desc(&desc, cmd_pyld);
 	IPA_DUMP_BUFF(mem.base, mem.phys_base, mem.size);
 
 	if (ipa3_send_cmd(1, &desc)) {
@@ -2669,7 +2759,7 @@
  */
 int _ipa_init_rt4_v3(void)
 {
-	struct ipa3_desc desc = { 0 };
+	struct ipa3_desc desc;
 	struct ipa_mem_buffer mem;
 	struct ipahal_imm_cmd_ip_v4_routing_init v4_cmd;
 	struct ipahal_imm_cmd_pyld *cmd_pyld;
@@ -2710,10 +2800,7 @@
 		goto free_mem;
 	}
 
-	desc.opcode = cmd_pyld->opcode;
-	desc.type = IPA_IMM_CMD_DESC;
-	desc.pyld = cmd_pyld->data;
-	desc.len = cmd_pyld->len;
+	ipa3_init_imm_cmd_desc(&desc, cmd_pyld);
 	IPA_DUMP_BUFF(mem.base, mem.phys_base, mem.size);
 
 	if (ipa3_send_cmd(1, &desc)) {
@@ -2735,7 +2822,7 @@
  */
 int _ipa_init_rt6_v3(void)
 {
-	struct ipa3_desc desc = { 0 };
+	struct ipa3_desc desc;
 	struct ipa_mem_buffer mem;
 	struct ipahal_imm_cmd_ip_v6_routing_init v6_cmd;
 	struct ipahal_imm_cmd_pyld *cmd_pyld;
@@ -2776,10 +2863,7 @@
 		goto free_mem;
 	}
 
-	desc.opcode = cmd_pyld->opcode;
-	desc.type = IPA_IMM_CMD_DESC;
-	desc.pyld = cmd_pyld->data;
-	desc.len = cmd_pyld->len;
+	ipa3_init_imm_cmd_desc(&desc, cmd_pyld);
 	IPA_DUMP_BUFF(mem.base, mem.phys_base, mem.size);
 
 	if (ipa3_send_cmd(1, &desc)) {
@@ -2801,7 +2885,7 @@
  */
 int _ipa_init_flt4_v3(void)
 {
-	struct ipa3_desc desc = { 0 };
+	struct ipa3_desc desc;
 	struct ipa_mem_buffer mem;
 	struct ipahal_imm_cmd_ip_v4_filter_init v4_cmd;
 	struct ipahal_imm_cmd_pyld *cmd_pyld;
@@ -2836,10 +2920,7 @@
 		goto free_mem;
 	}
 
-	desc.opcode = cmd_pyld->opcode;
-	desc.type = IPA_IMM_CMD_DESC;
-	desc.pyld = cmd_pyld->data;
-	desc.len = cmd_pyld->len;
+	ipa3_init_imm_cmd_desc(&desc, cmd_pyld);
 	IPA_DUMP_BUFF(mem.base, mem.phys_base, mem.size);
 
 	if (ipa3_send_cmd(1, &desc)) {
@@ -2861,7 +2942,7 @@
  */
 int _ipa_init_flt6_v3(void)
 {
-	struct ipa3_desc desc = { 0 };
+	struct ipa3_desc desc;
 	struct ipa_mem_buffer mem;
 	struct ipahal_imm_cmd_ip_v6_filter_init v6_cmd;
 	struct ipahal_imm_cmd_pyld *cmd_pyld;
@@ -2897,10 +2978,7 @@
 		goto free_mem;
 	}
 
-	desc.opcode = cmd_pyld->opcode;
-	desc.type = IPA_IMM_CMD_DESC;
-	desc.pyld = cmd_pyld->data;
-	desc.len = cmd_pyld->len;
+	ipa3_init_imm_cmd_desc(&desc, cmd_pyld);
 	IPA_DUMP_BUFF(mem.base, mem.phys_base, mem.size);
 
 	if (ipa3_send_cmd(1, &desc)) {
@@ -3118,9 +3196,38 @@
 }
 
 #ifdef CONFIG_COMPAT
+
+static long compat_ipa3_nat_ipv6ct_alloc_table(unsigned long arg,
+	int (alloc_func)(struct ipa_ioc_nat_ipv6ct_table_alloc *))
+{
+	long retval;
+	struct ipa_ioc_nat_ipv6ct_table_alloc32 table_alloc32;
+	struct ipa_ioc_nat_ipv6ct_table_alloc table_alloc;
+
+	retval = copy_from_user(&table_alloc32, (const void __user *)arg,
+		sizeof(struct ipa_ioc_nat_ipv6ct_table_alloc32));
+	if (retval)
+		return retval;
+
+	table_alloc.size = (size_t)table_alloc32.size;
+	table_alloc.offset = (off_t)table_alloc32.offset;
+
+	retval = alloc_func(&table_alloc);
+	if (retval)
+		return retval;
+
+	if (table_alloc.offset) {
+		table_alloc32.offset = (compat_off_t)table_alloc.offset;
+		retval = copy_to_user((void __user *)arg, &table_alloc32,
+			sizeof(struct ipa_ioc_nat_ipv6ct_table_alloc32));
+	}
+
+	return retval;
+}
+
 long compat_ipa3_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
-	int retval = 0;
+	long retval = 0;
 	struct ipa3_ioc_nat_alloc_mem32 nat_mem32;
 	struct ipa_ioc_nat_alloc_mem nat_mem;
 
@@ -3165,11 +3272,10 @@
 		cmd = IPA_IOC_GET_HDR;
 		break;
 	case IPA_IOC_ALLOC_NAT_MEM32:
-		if (copy_from_user((u8 *)&nat_mem32, (u8 *)arg,
-			sizeof(struct ipa3_ioc_nat_alloc_mem32))) {
-			retval = -EFAULT;
-			goto ret;
-		}
+		retval = copy_from_user(&nat_mem32, (const void __user *)arg,
+			sizeof(struct ipa3_ioc_nat_alloc_mem32));
+		if (retval)
+			return retval;
 		memcpy(nat_mem.dev_name, nat_mem32.dev_name,
 				IPA_RESOURCE_NAME_MAX);
 		nat_mem.size = (size_t)nat_mem32.size;
@@ -3178,26 +3284,40 @@
 		/* null terminate the string */
 		nat_mem.dev_name[IPA_RESOURCE_NAME_MAX - 1] = '\0';
 
-		if (ipa3_allocate_nat_device(&nat_mem)) {
-			retval = -EFAULT;
-			goto ret;
-		}
+		retval = ipa3_allocate_nat_device(&nat_mem);
+		if (retval)
+			return retval;
 		nat_mem32.offset = (compat_off_t)nat_mem.offset;
-		if (copy_to_user((u8 *)arg, (u8 *)&nat_mem32,
-			sizeof(struct ipa3_ioc_nat_alloc_mem32))) {
-			retval = -EFAULT;
-		}
-ret:
+		retval = copy_to_user((void __user *)arg, &nat_mem32,
+			sizeof(struct ipa3_ioc_nat_alloc_mem32));
 		return retval;
+	case IPA_IOC_ALLOC_NAT_TABLE32:
+		return compat_ipa3_nat_ipv6ct_alloc_table(arg,
+			ipa3_allocate_nat_table);
+	case IPA_IOC_ALLOC_IPV6CT_TABLE32:
+		return compat_ipa3_nat_ipv6ct_alloc_table(arg,
+			ipa3_allocate_ipv6ct_table);
 	case IPA_IOC_V4_INIT_NAT32:
 		cmd = IPA_IOC_V4_INIT_NAT;
 		break;
-	case IPA_IOC_NAT_DMA32:
-		cmd = IPA_IOC_NAT_DMA;
+	case IPA_IOC_INIT_IPV6CT_TABLE32:
+		cmd = IPA_IOC_INIT_IPV6CT_TABLE;
+		break;
+	case IPA_IOC_TABLE_DMA_CMD32:
+		cmd = IPA_IOC_TABLE_DMA_CMD;
 		break;
 	case IPA_IOC_V4_DEL_NAT32:
 		cmd = IPA_IOC_V4_DEL_NAT;
 		break;
+	case IPA_IOC_DEL_NAT_TABLE32:
+		cmd = IPA_IOC_DEL_NAT_TABLE;
+		break;
+	case IPA_IOC_DEL_IPV6CT_TABLE32:
+		cmd = IPA_IOC_DEL_IPV6CT_TABLE;
+		break;
+	case IPA_IOC_NAT_MODIFY_PDN32:
+		cmd = IPA_IOC_NAT_MODIFY_PDN;
+		break;
 	case IPA_IOC_GET_NAT_OFFSET32:
 		cmd = IPA_IOC_GET_NAT_OFFSET;
 		break;
@@ -4962,7 +5082,6 @@
 	mutex_init(&ipa3_ctx->msg_lock);
 
 	mutex_init(&ipa3_ctx->lock);
-	mutex_init(&ipa3_ctx->nat_mem.lock);
 	mutex_init(&ipa3_ctx->q6_proxy_clk_vote_mutex);
 	mutex_init(&ipa3_ctx->ipa_cne_evt_lock);
 
@@ -4992,10 +5111,10 @@
 		goto fail_device_create;
 	}
 
-	if (ipa3_create_nat_device()) {
-		IPAERR("unable to create nat device\n");
+	if (ipa3_nat_ipv6ct_init_devices()) {
+		IPAERR("unable to init NAT and IPv6CT devices\n");
 		result = -ENODEV;
-		goto fail_nat_dev_add;
+		goto fail_nat_ipv6ct_init_dev;
 	}
 
 	/* Create a wakeup source. */
@@ -5094,7 +5213,8 @@
 	if (!ipa3_ctx->use_ipa_pm)
 		ipa_rm_exit();
 fail_ipa_rm_init:
-fail_nat_dev_add:
+	ipa3_nat_ipv6ct_destroy_devices();
+fail_nat_ipv6ct_init_dev:
 	device_destroy(ipa3_ctx->class, ipa3_ctx->dev_num);
 fail_device_create:
 	unregister_chrdev_region(ipa3_ctx->dev_num, 1);
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
index a859ff7..59fe07f 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
@@ -995,6 +995,7 @@
 		msecs_to_jiffies(IPA_CHANNEL_STOP_IN_PROC_TO_MSEC);
 	int res;
 
+	IPADBG("entry\n");
 	if (clnt_hdl >= ipa3_ctx->ipa_num_pipes ||
 		ipa3_ctx->ep[clnt_hdl].valid == 0 ||
 		!stop_in_proc) {
@@ -1041,6 +1042,7 @@
 	bool stop_in_proc;
 	struct ipa3_ep_context *ep;
 	struct ipa_ep_cfg_ctrl ep_cfg_ctrl;
+	IPADBG("entry\n");
 
 	if (clnt_hdl >= ipa3_ctx->ipa_num_pipes ||
 		ipa3_ctx->ep[clnt_hdl].valid == 0) {
@@ -1157,6 +1159,7 @@
 			ep->ep_delay_set = false;
 		}
 	}
+	IPADBG("exit\n");
 	return result;
 }
 
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c
index f72f41c..c740660 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c
@@ -17,7 +17,9 @@
 #include <linux/stringify.h>
 #include "ipa_i.h"
 #include "../ipa_rm_i.h"
+#include "ipahal/ipahal_nat.h"
 
+#define IPA_MAX_ENTRY_STRING_LEN 500
 #define IPA_MAX_MSG_LEN 4096
 #define IPA_DBG_MAX_RULE_IN_TBL 128
 #define IPA_DBG_ACTIVE_CLIENT_BUF_SIZE ((IPA3_ACTIVE_CLIENTS_LOG_LINE_LEN \
@@ -26,6 +28,17 @@
 #define IPA_DUMP_STATUS_FIELD(f) \
 	pr_err(#f "=0x%x\n", status->f)
 
+#define IPA_READ_ONLY_MODE  0444
+#define IPA_READ_WRITE_MODE 0664
+#define IPA_WRITE_ONLY_MODE 0220
+
+struct ipa3_debugfs_file {
+	const char *name;
+	umode_t mode;
+	void *data;
+	const struct file_operations fops;
+};
+
 const char *ipa3_excp_name[] = {
 	__stringify_1(IPA_A5_MUX_HDR_EXCP_RSVD0),
 	__stringify_1(IPA_A5_MUX_HDR_EXCP_RSVD1),
@@ -87,30 +100,6 @@
 };
 
 static struct dentry *dent;
-static struct dentry *dfile_gen_reg;
-static struct dentry *dfile_ep_reg;
-static struct dentry *dfile_keep_awake;
-static struct dentry *dfile_ep_holb;
-static struct dentry *dfile_hdr;
-static struct dentry *dfile_proc_ctx;
-static struct dentry *dfile_ip4_rt;
-static struct dentry *dfile_ip4_rt_hw;
-static struct dentry *dfile_ip6_rt;
-static struct dentry *dfile_ip6_rt_hw;
-static struct dentry *dfile_ip4_flt;
-static struct dentry *dfile_ip4_flt_hw;
-static struct dentry *dfile_ip6_flt;
-static struct dentry *dfile_ip6_flt_hw;
-static struct dentry *dfile_stats;
-static struct dentry *dfile_wstats;
-static struct dentry *dfile_wdi_stats;
-static struct dentry *dfile_ntn_stats;
-static struct dentry *dfile_dbg_cnt;
-static struct dentry *dfile_msg;
-static struct dentry *dfile_ip4_nat;
-static struct dentry *dfile_rm_stats;
-static struct dentry *dfile_status_stats;
-static struct dentry *dfile_active_clients;
 static char dbg_buff[IPA_MAX_MSG_LEN];
 static char *active_clients_buf;
 
@@ -1516,250 +1505,367 @@
 	return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, cnt);
 }
 
+static int ipa3_read_table(
+	char *table_addr, u32 table_size,
+	char *buff, u32 buff_size,
+	u32 *total_num_entries,
+	u32 *rule_id,
+	enum ipahal_nat_type nat_type)
+{
+	int result;
+	char *entry;
+	size_t entry_size;
+	bool entry_zeroed;
+	u32 i, num_entries = 0, id = *rule_id, pos = 0;
+
+	IPADBG("\n");
+
+	if (table_addr == NULL)
+		return 0;
+
+	result = ipahal_nat_entry_size(nat_type, &entry_size);
+	if (result) {
+		IPAERR("Failed to retrieve size of %s entry\n",
+			ipahal_nat_type_str(nat_type));
+		return 0;
+	}
+
+	for (i = 0, entry = table_addr;
+		i < table_size;
+		++i, ++id, entry += entry_size) {
+		result = ipahal_nat_is_entry_zeroed(nat_type, entry,
+			&entry_zeroed);
+		if (result) {
+			IPAERR(
+				"Failed to determine whether the %s entry is definitely zero",
+				ipahal_nat_type_str(nat_type));
+			goto bail;
+		}
+		if (entry_zeroed)
+			continue;
+
+		pos += scnprintf(buff + pos, buff_size - pos,
+			"\tEntry_Index=%d\n", id);
+
+		pos += ipahal_nat_stringify_entry(nat_type, entry,
+			buff + pos, buff_size - pos);
+
+		++num_entries;
+	}
+
+	if (num_entries)
+		pos += scnprintf(buff + pos, buff_size - pos, "\n");
+	else
+		pos += scnprintf(buff + pos, buff_size - pos, "\tEmpty\n\n");
+
+	IPADBG("return\n");
+bail:
+	*rule_id = id;
+	*total_num_entries += num_entries;
+	return pos;
+}
+
+static int ipa3_start_read_memory_device(
+	struct ipa3_nat_ipv6ct_common_mem *dev,
+	char *buff, u32 buff_size,
+	enum ipahal_nat_type nat_type,
+	u32 *num_entries)
+{
+	u32 rule_id = 0, pos = 0;
+
+	IPADBG("\n");
+
+	pos += scnprintf(buff + pos, buff_size - pos, "%s_Table_Size=%d\n",
+		dev->name, dev->table_entries + 1);
+
+	pos += scnprintf(buff + pos, buff_size - pos,
+		"%s_Expansion_Table_Size=%d\n",
+		dev->name, dev->expn_table_entries);
+
+	if (!dev->is_sys_mem)
+		pos += scnprintf(buff + pos, buff_size - pos,
+			"Not supported for local(shared) memory\n");
+
+	pos += scnprintf(buff + pos, buff_size - pos,
+		"\n%s Base Table:\n", dev->name);
+	pos += ipa3_read_table(dev->base_table_addr, dev->table_entries + 1,
+		buff + pos, buff_size - pos, num_entries, &rule_id, nat_type);
+
+	pos += scnprintf(buff + pos, buff_size - pos,
+		"%s Expansion Table:\n", dev->name);
+	pos += ipa3_read_table(
+		dev->expansion_table_addr, dev->expn_table_entries,
+		buff + pos, buff_size - pos,
+		num_entries,
+		&rule_id,
+		nat_type);
+
+	IPADBG("return\n");
+	return pos;
+}
+
+static int ipa3_finish_read_memory_device(
+	struct ipa3_nat_ipv6ct_common_mem *dev,
+	char *buff, u32 buff_size,
+	u32 curr_pos,
+	u32 num_entries)
+{
+	u32 pos = 0;
+
+	IPADBG("\n");
+
+	/*
+	 * A real buffer and buff size, so need to use the
+	 * real current position
+	 */
+	pos += scnprintf(buff + curr_pos, buff_size - curr_pos,
+		"Overall number %s entries: %d\n\n", dev->name, num_entries);
+
+	if (curr_pos + pos >= buff_size - 1)
+		IPAERR(
+			"The %s debug information is larger than the internal buffer, so the read information might be incomplete",
+			dev->name);
+
+	IPADBG("return\n");
+	return pos;
+}
+
+static int ipa3_read_pdn_table(char *buff, u32 buff_size)
+{
+	int i, result;
+	char *pdn_entry;
+	size_t pdn_entry_size;
+	bool entry_zeroed;
+	u32 pos = 0;
+
+	IPADBG("\n");
+
+	result = ipahal_nat_entry_size(IPAHAL_NAT_IPV4_PDN, &pdn_entry_size);
+	if (result) {
+		IPAERR("Failed to retrieve size of PDN entry");
+		return 0;
+	}
+
+	for (i = 0, pdn_entry = ipa3_ctx->nat_mem.pdn_mem.base;
+		i < IPA_MAX_PDN_NUM;
+		++i, pdn_entry += pdn_entry_size) {
+		result = ipahal_nat_is_entry_zeroed(IPAHAL_NAT_IPV4_PDN,
+			pdn_entry, &entry_zeroed);
+		if (result) {
+			IPAERR(
+				"Failed to determine whether the PDN entry is definitely zero");
+			goto bail;
+		}
+		if (entry_zeroed)
+			continue;
+
+		pos += scnprintf(buff + pos, buff_size - pos, "PDN %d: ", i);
+
+		pos += ipahal_nat_stringify_entry(IPAHAL_NAT_IPV4_PDN,
+			pdn_entry, buff + pos, buff_size - pos);
+	}
+	pos += scnprintf(buff + pos, buff_size - pos, "\n");
+
+	IPADBG("return\n");
+bail:
+	return pos;
+}
+
 static ssize_t ipa3_read_nat4(struct file *file,
 		char __user *ubuf, size_t count,
-		loff_t *ppos) {
+		loff_t *ppos)
+{
+	ssize_t ret;
+	char *buff;
+	u32 rule_id = 0, pos = 0, num_entries = 0, index_num_entries = 0;
+	const u32 buff_size = IPA_MAX_MSG_LEN + 2 * IPA_MAX_ENTRY_STRING_LEN * (
+		ipa3_ctx->nat_mem.dev.table_entries + 1 +
+		ipa3_ctx->nat_mem.dev.expn_table_entries);
 
-#define ENTRY_U32_FIELDS 8
-#define NAT_ENTRY_ENABLE 0x8000
-#define NAT_ENTRY_RST_FIN_BIT 0x4000
-#define BASE_TABLE 0
-#define EXPANSION_TABLE 1
+	IPADBG("\n");
 
-	u32 *base_tbl, *indx_tbl;
-	u32 tbl_size, *tmp;
-	u32 value, i, j, rule_id;
-	u16 enable, tbl_entry, flag;
-	u32 no_entrys = 0;
-	struct ipa_pdn_entry *pdn_table = ipa3_ctx->nat_mem.pdn_mem.base;
+	buff = kzalloc(buff_size, GFP_KERNEL);
+	if (buff == NULL)
+		return 0;
 
-	mutex_lock(&ipa3_ctx->nat_mem.lock);
-	value = ipa3_ctx->nat_mem.public_ip_addr;
-	pr_err(
-				"Table IP Address:%d.%d.%d.%d\n",
-				((value & 0xFF000000) >> 24),
-				((value & 0x00FF0000) >> 16),
-				((value & 0x0000FF00) >> 8),
-				((value & 0x000000FF)));
-
-	if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0)
-		for (i = 0; i < IPA_MAX_PDN_NUM; i++) {
-			pr_err(
-				"PDN %d: ip 0x%X, src_metadata 0x%X, dst_metadata 0x%X\n",
-				i, pdn_table[i].public_ip,
-				pdn_table[i].src_metadata,
-				pdn_table[i].dst_metadata);
-		}
-
-	pr_err("Table Size:%d\n",
-				ipa3_ctx->nat_mem.size_base_tables);
-
-	pr_err("Expansion Table Size:%d\n",
-				ipa3_ctx->nat_mem.size_expansion_tables-1);
-
-	if (!ipa3_ctx->nat_mem.is_sys_mem)
-		pr_err("Not supported for local(shared) memory\n");
-
-	/* Print Base tables */
-	rule_id = 0;
-	for (j = 0; j < 2; j++) {
-		if (j == BASE_TABLE) {
-			tbl_size = ipa3_ctx->nat_mem.size_base_tables;
-			base_tbl = (u32 *)ipa3_ctx->nat_mem.ipv4_rules_addr;
-
-			pr_err("\nBase Table:\n");
-		} else {
-			tbl_size = ipa3_ctx->nat_mem.size_expansion_tables-1;
-			base_tbl =
-			 (u32 *)ipa3_ctx->nat_mem.ipv4_expansion_rules_addr;
-
-			pr_err("\nExpansion Base Table:\n");
-		}
-
-		if (base_tbl != NULL) {
-			for (i = 0; i <= tbl_size; i++, rule_id++) {
-				tmp = base_tbl;
-				value = tmp[4];
-				enable = ((value & 0xFFFF0000) >> 16);
-
-				if (enable & NAT_ENTRY_ENABLE) {
-					no_entrys++;
-					pr_err("Rule:%d ", rule_id);
-
-					value = *tmp;
-					pr_err(
-						"Private_IP:%d.%d.%d.%d ",
-						((value & 0xFF000000) >> 24),
-						((value & 0x00FF0000) >> 16),
-						((value & 0x0000FF00) >> 8),
-						((value & 0x000000FF)));
-					tmp++;
-
-					value = *tmp;
-					pr_err(
-						"Target_IP:%d.%d.%d.%d ",
-						((value & 0xFF000000) >> 24),
-						((value & 0x00FF0000) >> 16),
-						((value & 0x0000FF00) >> 8),
-						((value & 0x000000FF)));
-					tmp++;
-
-					value = *tmp;
-					pr_err(
-						"Next_Index:%d  Public_Port:%d ",
-						(value & 0x0000FFFF),
-						((value & 0xFFFF0000) >> 16));
-					tmp++;
-
-					value = *tmp;
-					pr_err(
-						"Private_Port:%d  Target_Port:%d ",
-						(value & 0x0000FFFF),
-						((value & 0xFFFF0000) >> 16));
-					tmp++;
-
-					value = *tmp;
-					flag = ((value & 0xFFFF0000) >> 16);
-					if (flag & NAT_ENTRY_RST_FIN_BIT) {
-						pr_err(
-								"IP_CKSM_delta:0x%x  Flags:%s ",
-							  (value & 0x0000FFFF),
-								"Direct_To_A5");
-					} else {
-						pr_err(
-							"IP_CKSM_delta:0x%x  Flags:%s ",
-							(value & 0x0000FFFF),
-							"Fwd_to_route");
-					}
-					tmp++;
-
-					value = *tmp;
-					pr_err(
-						"Time_stamp:0x%x Proto:%d ",
-						(value & 0x00FFFFFF),
-						((value & 0xFF000000) >> 24));
-					tmp++;
-
-					value = *tmp;
-					pr_err(
-						"Prev_Index:%d  Indx_tbl_entry:%d ",
-						(value & 0x0000FFFF),
-						((value & 0xFFFF0000) >> 16));
-					tmp++;
-
-					value = *tmp;
-					pr_err(
-						"TCP_UDP_cksum_delta:0x%x\n",
-						((value & 0xFFFF0000) >> 16));
-				}
-
-				base_tbl += ENTRY_U32_FIELDS;
-
-			}
-		}
+	if (!ipa3_ctx->nat_mem.dev.is_dev_init) {
+		pos += scnprintf(buff + pos, buff_size - pos,
+			"NAT hasn't been initialized or not supported\n");
+		goto ret;
 	}
 
+	mutex_lock(&ipa3_ctx->nat_mem.dev.lock);
+
+	if (!ipa3_ctx->nat_mem.dev.is_hw_init) {
+		pos += scnprintf(buff + pos, buff_size - pos,
+			"NAT H/W hasn't been initialized\n");
+		goto bail;
+	}
+
+	pos += scnprintf(buff + pos, buff_size - pos, "\n");
+
+	if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0) {
+		pos += ipa3_read_pdn_table(buff + pos, buff_size - pos);
+	} else {
+		pos += scnprintf(buff + pos, buff_size - pos,
+			"NAT Table IP Address=%pI4h\n\n",
+			&ipa3_ctx->nat_mem.public_ip_addr);
+	}
+
+	pos += ipa3_start_read_memory_device(&ipa3_ctx->nat_mem.dev,
+		buff + pos, buff_size - pos, IPAHAL_NAT_IPV4, &num_entries);
+
 	/* Print Index tables */
-	rule_id = 0;
-	for (j = 0; j < 2; j++) {
-		if (j == BASE_TABLE) {
-			tbl_size = ipa3_ctx->nat_mem.size_base_tables;
-			indx_tbl = (u32 *)ipa3_ctx->nat_mem.index_table_addr;
+	pos += scnprintf(buff + pos, buff_size - pos,
+		"ipaNatTable Index Table:\n");
+	pos += ipa3_read_table(
+		ipa3_ctx->nat_mem.index_table_addr,
+		ipa3_ctx->nat_mem.dev.table_entries + 1,
+		buff + pos, buff_size - pos,
+		&index_num_entries,
+		&rule_id,
+		IPAHAL_NAT_IPV4_INDEX);
 
-			pr_err("\nIndex Table:\n");
-		} else {
-			tbl_size = ipa3_ctx->nat_mem.size_expansion_tables-1;
-			indx_tbl =
-			 (u32 *)ipa3_ctx->nat_mem.index_table_expansion_addr;
+	pos += scnprintf(buff + pos, buff_size - pos,
+		"ipaNatTable Expansion Index Table:\n");
+	pos += ipa3_read_table(
+		ipa3_ctx->nat_mem.index_table_expansion_addr,
+		ipa3_ctx->nat_mem.dev.expn_table_entries,
+		buff + pos, buff_size - pos,
+		&index_num_entries,
+		&rule_id,
+		IPAHAL_NAT_IPV4_INDEX);
 
-			pr_err("\nExpansion Index Table:\n");
-		}
+	if (num_entries != index_num_entries)
+		IPAERR(
+			"The NAT table number of entries %d is different from index table number of entries %d\n",
+			num_entries, index_num_entries);
 
-		if (indx_tbl != NULL) {
-			for (i = 0; i <= tbl_size; i++, rule_id++) {
-				tmp = indx_tbl;
-				value = *tmp;
-				tbl_entry = (value & 0x0000FFFF);
+	pos += ipa3_finish_read_memory_device(&ipa3_ctx->nat_mem.dev,
+		buff, buff_size, pos, num_entries);
 
-				if (tbl_entry) {
-					pr_err("Rule:%d ", rule_id);
+	IPADBG("return\n");
+bail:
+	mutex_unlock(&ipa3_ctx->nat_mem.dev.lock);
+ret:
+	ret = simple_read_from_buffer(ubuf, count, ppos, buff, pos);
+	kfree(buff);
+	return ret;
+}
 
-					value = *tmp;
-					pr_err(
-						"Table_Entry:%d  Next_Index:%d\n",
-						tbl_entry,
-						((value & 0xFFFF0000) >> 16));
-				}
+static ssize_t ipa3_read_ipv6ct(struct file *file,
+	char __user *ubuf, size_t count,
+	loff_t *ppos) {
+	ssize_t ret;
+	char *buff;
+	u32 pos = 0, num_entries = 0;
+	const u32 buff_size = IPA_MAX_MSG_LEN + IPA_MAX_ENTRY_STRING_LEN * (
+		ipa3_ctx->nat_mem.dev.table_entries + 1 +
+		ipa3_ctx->nat_mem.dev.expn_table_entries);
 
-				indx_tbl++;
-			}
-		}
+	IPADBG("\n");
+
+	buff = kzalloc(buff_size, GFP_KERNEL);
+	if (buff == NULL)
+		return 0;
+
+	pos += scnprintf(buff + pos, buff_size - pos, "\n");
+
+	if (!ipa3_ctx->ipv6ct_mem.dev.is_dev_init) {
+		pos += scnprintf(buff + pos, buff_size - pos,
+			"IPv6 connection tracking hasn't been initialized or not supported\n");
+		goto ret;
 	}
-	pr_err("Current No. Nat Entries: %d\n", no_entrys);
-	mutex_unlock(&ipa3_ctx->nat_mem.lock);
 
-	return 0;
+	mutex_lock(&ipa3_ctx->ipv6ct_mem.dev.lock);
+
+	if (!ipa3_ctx->ipv6ct_mem.dev.is_hw_init) {
+		pos += scnprintf(buff + pos, buff_size - pos,
+			"IPv6 connection tracking H/W hasn't been initialized\n");
+		goto bail;
+	}
+
+	pos += ipa3_start_read_memory_device(&ipa3_ctx->ipv6ct_mem.dev,
+		buff + pos, buff_size - pos, IPAHAL_NAT_IPV6CT, &num_entries);
+	pos += ipa3_finish_read_memory_device(&ipa3_ctx->ipv6ct_mem.dev,
+		buff, buff_size, pos, num_entries);
+
+	IPADBG("return\n");
+bail:
+	mutex_unlock(&ipa3_ctx->ipv6ct_mem.dev.lock);
+ret:
+	ret = simple_read_from_buffer(ubuf, count, ppos, buff, pos);
+	kfree(buff);
+	return ret;
 }
 
 static ssize_t ipa3_rm_read_stats(struct file *file, char __user *ubuf,
 		size_t count, loff_t *ppos)
 {
-	int result, nbytes, cnt = 0;
+	int result, cnt = 0;
 
 	/* deprecate if IPA PM is used */
-	if (ipa3_ctx->use_ipa_pm)
-		return 0;
+	if (ipa3_ctx->use_ipa_pm) {
+		cnt += scnprintf(dbg_buff + cnt, IPA_MAX_MSG_LEN - cnt,
+			"IPA RM is disabled\n");
+		goto ret;
+	}
 
 	result = ipa_rm_stat(dbg_buff, IPA_MAX_MSG_LEN);
 	if (result < 0) {
-		nbytes = scnprintf(dbg_buff, IPA_MAX_MSG_LEN,
+		cnt += scnprintf(dbg_buff + cnt, IPA_MAX_MSG_LEN - cnt,
 				"Error in printing RM stat %d\n", result);
-		cnt += nbytes;
-	} else
-		cnt += result;
-
+		goto ret;
+	}
+	cnt += result;
+ret:
 	return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, cnt);
 }
 
 static ssize_t ipa3_pm_read_stats(struct file *file, char __user *ubuf,
 		size_t count, loff_t *ppos)
 {
-	int result, nbytes, cnt = 0;
+	int result, cnt = 0;
 
-	if (!ipa3_ctx->use_ipa_pm)
-		return 0;
+	if (!ipa3_ctx->use_ipa_pm) {
+		cnt += scnprintf(dbg_buff + cnt, IPA_MAX_MSG_LEN - cnt,
+			"IPA PM is disabled\n");
+		goto ret;
+	}
 
 	result = ipa_pm_stat(dbg_buff, IPA_MAX_MSG_LEN);
 	if (result < 0) {
-		nbytes = scnprintf(dbg_buff, IPA_MAX_MSG_LEN,
+		cnt += scnprintf(dbg_buff + cnt, IPA_MAX_MSG_LEN - cnt,
 				"Error in printing PM stat %d\n", result);
-		cnt += nbytes;
-	} else
-		cnt += result;
-
+		goto ret;
+	}
+	cnt += result;
+ret:
 	return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, cnt);
 }
 
 static ssize_t ipa3_pm_ex_read_stats(struct file *file, char __user *ubuf,
 		size_t count, loff_t *ppos)
 {
-	int result, nbytes, cnt = 0;
+	int result, cnt = 0;
 
-	if (!ipa3_ctx->use_ipa_pm)
-		return 0;
+	if (!ipa3_ctx->use_ipa_pm) {
+		cnt += scnprintf(dbg_buff + cnt, IPA_MAX_MSG_LEN - cnt,
+			"IPA PM is disabled\n");
+		goto ret;
+	}
 
 	result = ipa_pm_exceptions_stat(dbg_buff, IPA_MAX_MSG_LEN);
 	if (result < 0) {
-		nbytes = scnprintf(dbg_buff, IPA_MAX_MSG_LEN,
+		cnt += scnprintf(dbg_buff + cnt, IPA_MAX_MSG_LEN - cnt,
 				"Error in printing PM stat %d\n", result);
-		cnt += nbytes;
-	} else
-		cnt += result;
-
+		goto ret;
+	}
+	cnt += result;
+ret:
 	return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, cnt);
 }
 
-
 static void ipa_dump_status(struct ipahal_pkt_status *status)
 {
 	IPA_DUMP_STATUS_FIELD(status_opcode);
@@ -1901,113 +2007,139 @@
 	return count;
 }
 
-const struct file_operations ipa3_gen_reg_ops = {
-	.read = ipa3_read_gen_reg,
-};
-
-const struct file_operations ipa3_ep_reg_ops = {
-	.read = ipa3_read_ep_reg,
-	.write = ipa3_write_ep_reg,
-};
-
-const struct file_operations ipa3_keep_awake_ops = {
-	.read = ipa3_read_keep_awake,
-	.write = ipa3_write_keep_awake,
-};
-
-const struct file_operations ipa3_ep_holb_ops = {
-	.write = ipa3_write_ep_holb,
-};
-
-const struct file_operations ipa3_hdr_ops = {
-	.read = ipa3_read_hdr,
-};
-
-const struct file_operations ipa3_rt_ops = {
-	.read = ipa3_read_rt,
-	.open = ipa3_open_dbg,
-};
-
-const struct file_operations ipa3_rt_hw_ops = {
-	.read = ipa3_read_rt_hw,
-	.open = ipa3_open_dbg,
-};
-
-const struct file_operations ipa3_proc_ctx_ops = {
-	.read = ipa3_read_proc_ctx,
-};
-
-const struct file_operations ipa3_flt_ops = {
-	.read = ipa3_read_flt,
-	.open = ipa3_open_dbg,
-};
-
-const struct file_operations ipa3_flt_hw_ops = {
-	.read = ipa3_read_flt_hw,
-	.open = ipa3_open_dbg,
-};
-
-const struct file_operations ipa3_stats_ops = {
-	.read = ipa3_read_stats,
-};
-
-const struct file_operations ipa3_wstats_ops = {
-	.read = ipa3_read_wstats,
-};
-
-const struct file_operations ipa3_wdi_ops = {
-	.read = ipa3_read_wdi,
-};
-
-const struct file_operations ipa3_ntn_ops = {
-	.read = ipa3_read_ntn,
-};
-
-const struct file_operations ipa3_msg_ops = {
-	.read = ipa3_read_msg,
-};
-
-const struct file_operations ipa3_dbg_cnt_ops = {
-	.read = ipa3_read_dbg_cnt,
-	.write = ipa3_write_dbg_cnt,
-};
-
-const struct file_operations ipa3_status_stats_ops = {
-	.read = ipa_status_stats_read,
-};
-
-const struct file_operations ipa3_nat4_ops = {
-	.read = ipa3_read_nat4,
-};
-
-const struct file_operations ipa3_rm_stats = {
-	.read = ipa3_rm_read_stats,
-};
-
-static const struct file_operations ipa3_pm_stats = {
-	.read = ipa3_pm_read_stats,
-};
-
-
-static const struct file_operations ipa3_pm_ex_stats = {
-	.read = ipa3_pm_ex_read_stats,
-};
-
-const struct file_operations ipa3_active_clients = {
-	.read = ipa3_print_active_clients_log,
-	.write = ipa3_clear_active_clients_log,
-};
-
-const struct file_operations ipa3_ipc_low_ops = {
-	.write = ipa3_enable_ipc_low,
+static const struct ipa3_debugfs_file debugfs_files[] = {
+	{
+		"gen_reg", IPA_READ_ONLY_MODE, NULL, {
+			.read = ipa3_read_gen_reg
+		}
+	}, {
+		"active_clients", IPA_READ_WRITE_MODE, NULL, {
+			.read = ipa3_print_active_clients_log,
+			.write = ipa3_clear_active_clients_log
+		}
+	}, {
+		"ep_reg", IPA_READ_WRITE_MODE, NULL, {
+			.read = ipa3_read_ep_reg,
+			.write = ipa3_write_ep_reg,
+		}
+	}, {
+		"keep_awake", IPA_READ_WRITE_MODE, NULL, {
+			.read = ipa3_read_keep_awake,
+			.write = ipa3_write_keep_awake,
+		}
+	}, {
+		"holb", IPA_WRITE_ONLY_MODE, NULL, {
+			.write = ipa3_write_ep_holb,
+		}
+	}, {
+		"hdr", IPA_READ_ONLY_MODE, NULL, {
+			.read = ipa3_read_hdr,
+		}
+	}, {
+		"proc_ctx", IPA_READ_ONLY_MODE, NULL, {
+			.read = ipa3_read_proc_ctx,
+		}
+	}, {
+		"ip4_rt", IPA_READ_ONLY_MODE, (void *)IPA_IP_v4, {
+			.read = ipa3_read_rt,
+			.open = ipa3_open_dbg,
+		}
+	}, {
+		"ip4_rt_hw", IPA_READ_ONLY_MODE, (void *)IPA_IP_v4, {
+			.read = ipa3_read_rt_hw,
+			.open = ipa3_open_dbg,
+		}
+	}, {
+		"ip6_rt", IPA_READ_ONLY_MODE, (void *)IPA_IP_v6, {
+			.read = ipa3_read_rt,
+			.open = ipa3_open_dbg,
+		}
+	}, {
+		"ip6_rt_hw", IPA_READ_ONLY_MODE, (void *)IPA_IP_v6, {
+			.read = ipa3_read_rt_hw,
+			.open = ipa3_open_dbg,
+		}
+	}, {
+		"ip4_flt", IPA_READ_ONLY_MODE, (void *)IPA_IP_v4, {
+			.read = ipa3_read_flt,
+			.open = ipa3_open_dbg,
+		}
+	}, {
+		"ip4_flt_hw", IPA_READ_ONLY_MODE, (void *)IPA_IP_v4, {
+			.read = ipa3_read_flt_hw,
+			.open = ipa3_open_dbg,
+		}
+	}, {
+		"ip6_flt", IPA_READ_ONLY_MODE, (void *)IPA_IP_v6, {
+			.read = ipa3_read_flt,
+			.open = ipa3_open_dbg,
+		}
+	}, {
+		"ip6_flt_hw", IPA_READ_ONLY_MODE, (void *)IPA_IP_v6, {
+			.read = ipa3_read_flt_hw,
+			.open = ipa3_open_dbg,
+		}
+	}, {
+		"stats", IPA_READ_ONLY_MODE, NULL, {
+			.read = ipa3_read_stats,
+		}
+	}, {
+		"wstats", IPA_READ_ONLY_MODE, NULL, {
+			.read = ipa3_read_wstats,
+		}
+	}, {
+		"wdi", IPA_READ_ONLY_MODE, NULL, {
+			.read = ipa3_read_wdi,
+		}
+	}, {
+		"ntn", IPA_READ_ONLY_MODE, NULL, {
+			.read = ipa3_read_ntn,
+		}
+	}, {
+		"dbg_cnt", IPA_READ_WRITE_MODE, NULL, {
+			.read = ipa3_read_dbg_cnt,
+			.write = ipa3_write_dbg_cnt,
+		}
+	}, {
+		"msg", IPA_READ_ONLY_MODE, NULL, {
+			.read = ipa3_read_msg,
+		}
+	}, {
+		"ip4_nat", IPA_READ_ONLY_MODE, NULL, {
+			.read = ipa3_read_nat4,
+		}
+	}, {
+		"ipv6ct", IPA_READ_ONLY_MODE, NULL, {
+			.read = ipa3_read_ipv6ct,
+		}
+	}, {
+		"rm_stats", IPA_READ_ONLY_MODE, NULL, {
+			.read = ipa3_rm_read_stats,
+		}
+	}, {
+		"pm_stats", IPA_READ_ONLY_MODE, NULL, {
+			.read = ipa3_pm_read_stats,
+		}
+	}, {
+		"pm_ex_stats", IPA_READ_ONLY_MODE, NULL, {
+			.read = ipa3_pm_ex_read_stats,
+		}
+	}, {
+		"status_stats", IPA_READ_ONLY_MODE, NULL, {
+			.read = ipa_status_stats_read,
+		}
+	}, {
+		"enable_low_prio_print", IPA_WRITE_ONLY_MODE, NULL, {
+			.write = ipa3_enable_ipc_low,
+		}
+	}
 };
 
 void ipa3_debugfs_init(void)
 {
-	const mode_t read_only_mode = S_IRUSR | S_IRGRP | S_IROTH;
-	const mode_t read_write_mode = S_IRUSR | S_IRGRP | S_IROTH |
-			S_IWUSR | S_IWGRP;
-	const mode_t write_only_mode = S_IWUSR | S_IWGRP;
+	const size_t debugfs_files_num =
+		sizeof(debugfs_files) / sizeof(struct ipa3_debugfs_file);
+	size_t i;
 	struct dentry *file;
 
 	dent = debugfs_create_dir("ipa", 0);
@@ -2016,26 +2148,24 @@
 		return;
 	}
 
-	file = debugfs_create_u32("hw_type", read_only_mode,
-			dent, &ipa3_ctx->ipa_hw_type);
+	file = debugfs_create_u32("hw_type", IPA_READ_ONLY_MODE,
+		dent, &ipa3_ctx->ipa_hw_type);
 	if (!file) {
 		IPAERR("could not create hw_type file\n");
 		goto fail;
 	}
 
 
-	dfile_gen_reg = debugfs_create_file("gen_reg", read_only_mode, dent, 0,
-			&ipa3_gen_reg_ops);
-	if (!dfile_gen_reg || IS_ERR(dfile_gen_reg)) {
-		IPAERR("fail to create file for debug_fs gen_reg\n");
-		goto fail;
-	}
+	for (i = 0; i < debugfs_files_num; ++i) {
+		const struct ipa3_debugfs_file *curr = &debugfs_files[i];
 
-	dfile_active_clients = debugfs_create_file("active_clients",
-			read_write_mode, dent, 0, &ipa3_active_clients);
-	if (!dfile_active_clients || IS_ERR(dfile_active_clients)) {
-		IPAERR("fail to create file for debug_fs active_clients\n");
-		goto fail;
+		file = debugfs_create_file(curr->name, curr->mode, dent,
+			curr->data, &curr->fops);
+		if (!file || IS_ERR(file)) {
+			IPAERR("fail to create file for debug_fs %s\n",
+				curr->name);
+			goto fail;
+		}
 	}
 
 	active_clients_buf = NULL;
@@ -2044,177 +2174,7 @@
 	if (active_clients_buf == NULL)
 		IPAERR("fail to allocate active clients memory buffer");
 
-	dfile_ep_reg = debugfs_create_file("ep_reg", read_write_mode, dent, 0,
-			&ipa3_ep_reg_ops);
-	if (!dfile_ep_reg || IS_ERR(dfile_ep_reg)) {
-		IPAERR("fail to create file for debug_fs ep_reg\n");
-		goto fail;
-	}
-
-	dfile_keep_awake = debugfs_create_file("keep_awake", read_write_mode,
-			dent, 0, &ipa3_keep_awake_ops);
-	if (!dfile_keep_awake || IS_ERR(dfile_keep_awake)) {
-		IPAERR("fail to create file for debug_fs dfile_keep_awake\n");
-		goto fail;
-	}
-
-	dfile_ep_holb = debugfs_create_file("holb", write_only_mode, dent,
-			0, &ipa3_ep_holb_ops);
-	if (!dfile_ep_holb || IS_ERR(dfile_ep_holb)) {
-		IPAERR("fail to create file for debug_fs dfile_ep_hol_en\n");
-		goto fail;
-	}
-
-	dfile_hdr = debugfs_create_file("hdr", read_only_mode, dent, 0,
-			&ipa3_hdr_ops);
-	if (!dfile_hdr || IS_ERR(dfile_hdr)) {
-		IPAERR("fail to create file for debug_fs hdr\n");
-		goto fail;
-	}
-
-	dfile_proc_ctx = debugfs_create_file("proc_ctx", read_only_mode, dent,
-		0, &ipa3_proc_ctx_ops);
-	if (!dfile_hdr || IS_ERR(dfile_hdr)) {
-		IPAERR("fail to create file for debug_fs proc_ctx\n");
-		goto fail;
-	}
-
-	dfile_ip4_rt = debugfs_create_file("ip4_rt", read_only_mode, dent,
-			(void *)IPA_IP_v4, &ipa3_rt_ops);
-	if (!dfile_ip4_rt || IS_ERR(dfile_ip4_rt)) {
-		IPAERR("fail to create file for debug_fs ip4 rt\n");
-		goto fail;
-	}
-
-	dfile_ip4_rt_hw = debugfs_create_file("ip4_rt_hw", read_only_mode, dent,
-		(void *)IPA_IP_v4, &ipa3_rt_hw_ops);
-	if (!dfile_ip4_rt_hw || IS_ERR(dfile_ip4_rt_hw)) {
-		IPAERR("fail to create file for debug_fs ip4 rt hw\n");
-		goto fail;
-	}
-
-	dfile_ip6_rt = debugfs_create_file("ip6_rt", read_only_mode, dent,
-			(void *)IPA_IP_v6, &ipa3_rt_ops);
-	if (!dfile_ip6_rt || IS_ERR(dfile_ip6_rt)) {
-		IPAERR("fail to create file for debug_fs ip6:w rt\n");
-		goto fail;
-	}
-
-	dfile_ip6_rt_hw = debugfs_create_file("ip6_rt_hw", read_only_mode, dent,
-		(void *)IPA_IP_v6, &ipa3_rt_hw_ops);
-	if (!dfile_ip6_rt_hw || IS_ERR(dfile_ip6_rt_hw)) {
-		IPAERR("fail to create file for debug_fs ip6 rt hw\n");
-		goto fail;
-	}
-
-	dfile_ip4_flt = debugfs_create_file("ip4_flt", read_only_mode, dent,
-			(void *)IPA_IP_v4, &ipa3_flt_ops);
-	if (!dfile_ip4_flt || IS_ERR(dfile_ip4_flt)) {
-		IPAERR("fail to create file for debug_fs ip4 flt\n");
-		goto fail;
-	}
-
-	dfile_ip4_flt_hw = debugfs_create_file("ip4_flt_hw", read_only_mode,
-			dent, (void *)IPA_IP_v4, &ipa3_flt_hw_ops);
-	if (!dfile_ip4_flt_hw || IS_ERR(dfile_ip4_flt_hw)) {
-		IPAERR("fail to create file for debug_fs ip4 flt\n");
-		goto fail;
-	}
-
-	dfile_ip6_flt = debugfs_create_file("ip6_flt", read_only_mode, dent,
-			(void *)IPA_IP_v6, &ipa3_flt_ops);
-	if (!dfile_ip6_flt || IS_ERR(dfile_ip6_flt)) {
-		IPAERR("fail to create file for debug_fs ip6 flt\n");
-		goto fail;
-	}
-
-	dfile_ip6_flt_hw = debugfs_create_file("ip6_flt_hw", read_only_mode,
-			dent, (void *)IPA_IP_v6, &ipa3_flt_hw_ops);
-	if (!dfile_ip6_flt_hw || IS_ERR(dfile_ip6_flt_hw)) {
-		IPAERR("fail to create file for debug_fs ip6 flt\n");
-		goto fail;
-	}
-
-	dfile_stats = debugfs_create_file("stats", read_only_mode, dent, 0,
-			&ipa3_stats_ops);
-	if (!dfile_stats || IS_ERR(dfile_stats)) {
-		IPAERR("fail to create file for debug_fs stats\n");
-		goto fail;
-	}
-
-	dfile_wstats = debugfs_create_file("wstats", read_only_mode,
-			dent, 0, &ipa3_wstats_ops);
-	if (!dfile_wstats || IS_ERR(dfile_wstats)) {
-		IPAERR("fail to create file for debug_fs wstats\n");
-		goto fail;
-	}
-
-	dfile_wdi_stats = debugfs_create_file("wdi", read_only_mode, dent, 0,
-			&ipa3_wdi_ops);
-	if (!dfile_wdi_stats || IS_ERR(dfile_wdi_stats)) {
-		IPAERR("fail to create file for debug_fs wdi stats\n");
-		goto fail;
-	}
-
-	dfile_ntn_stats = debugfs_create_file("ntn", read_only_mode, dent, 0,
-			&ipa3_ntn_ops);
-	if (!dfile_ntn_stats || IS_ERR(dfile_ntn_stats)) {
-		IPAERR("fail to create file for debug_fs ntn stats\n");
-		goto fail;
-	}
-
-	dfile_dbg_cnt = debugfs_create_file("dbg_cnt", read_write_mode, dent, 0,
-			&ipa3_dbg_cnt_ops);
-	if (!dfile_dbg_cnt || IS_ERR(dfile_dbg_cnt)) {
-		IPAERR("fail to create file for debug_fs dbg_cnt\n");
-		goto fail;
-	}
-
-	dfile_msg = debugfs_create_file("msg", read_only_mode, dent, 0,
-			&ipa3_msg_ops);
-	if (!dfile_msg || IS_ERR(dfile_msg)) {
-		IPAERR("fail to create file for debug_fs msg\n");
-		goto fail;
-	}
-
-	dfile_ip4_nat = debugfs_create_file("ip4_nat", read_only_mode, dent,
-			0, &ipa3_nat4_ops);
-	if (!dfile_ip4_nat || IS_ERR(dfile_ip4_nat)) {
-		IPAERR("fail to create file for debug_fs ip4 nat\n");
-		goto fail;
-	}
-
-	if (ipa3_ctx->use_ipa_pm) {
-		file = dfile_rm_stats = debugfs_create_file("pm_stats",
-			read_only_mode, dent, NULL, &ipa3_pm_stats);
-		if (!file || IS_ERR(file)) {
-			IPAERR("fail to create file for debug_fs pm_stats\n");
-			goto fail;
-		}
-
-		file = dfile_rm_stats = debugfs_create_file("pm_ex_stats",
-			read_only_mode, dent, NULL, &ipa3_pm_ex_stats);
-		if (!file || IS_ERR(file)) {
-			IPAERR("fail to create file for debugfs pm_ex_stats\n");
-			goto fail;
-		}
-	} else {
-		dfile_rm_stats = debugfs_create_file("rm_stats",
-				read_only_mode, dent, NULL, &ipa3_rm_stats);
-		if (!dfile_rm_stats || IS_ERR(dfile_rm_stats)) {
-			IPAERR("fail to create file for debug_fs rm_stats\n");
-			goto fail;
-		}
-	}
-
-	dfile_status_stats = debugfs_create_file("status_stats",
-			read_only_mode, dent, 0, &ipa3_status_stats_ops);
-	if (!dfile_status_stats || IS_ERR(dfile_status_stats)) {
-		IPAERR("fail to create file for debug_fs status_stats\n");
-		goto fail;
-	}
-
-	file = debugfs_create_u32("enable_clock_scaling", read_write_mode,
+	file = debugfs_create_u32("enable_clock_scaling", IPA_READ_WRITE_MODE,
 		dent, &ipa3_ctx->enable_clock_scaling);
 	if (!file) {
 		IPAERR("could not create enable_clock_scaling file\n");
@@ -2222,7 +2182,7 @@
 	}
 
 	file = debugfs_create_u32("clock_scaling_bw_threshold_nominal_mbps",
-		read_write_mode, dent,
+		IPA_READ_WRITE_MODE, dent,
 		&ipa3_ctx->ctrl->clock_scaling_bw_threshold_nominal);
 	if (!file) {
 		IPAERR("could not create bw_threshold_nominal_mbps\n");
@@ -2230,20 +2190,13 @@
 	}
 
 	file = debugfs_create_u32("clock_scaling_bw_threshold_turbo_mbps",
-		read_write_mode, dent,
-		&ipa3_ctx->ctrl->clock_scaling_bw_threshold_turbo);
+			IPA_READ_WRITE_MODE, dent,
+			&ipa3_ctx->ctrl->clock_scaling_bw_threshold_turbo);
 	if (!file) {
 		IPAERR("could not create bw_threshold_turbo_mbps\n");
 		goto fail;
 	}
 
-	file = debugfs_create_file("enable_low_prio_print", write_only_mode,
-		dent, 0, &ipa3_ipc_low_ops);
-	if (!file) {
-		IPAERR("could not create enable_low_prio_print file\n");
-		goto fail;
-	}
-
 	ipa_debugfs_init_stats(dent);
 
 	return;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c
index 5d1bbe7..6a89f49 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c
@@ -385,19 +385,15 @@
  *  payload pointers buffers for headers and bodies of flt structure
  *  as well as place for flush imm.
  * @ipt: the ip address family type
+ * @entries: the number of entries
  * @desc: [OUT] descriptor buffer
  * @cmd: [OUT] imm commands payload pointers buffer
  *
  * Return: 0 on success, negative on failure
  */
-static int ipa_flt_alloc_cmd_buffers(enum ipa_ip_type ip,
+static int ipa_flt_alloc_cmd_buffers(enum ipa_ip_type ip, u16 entries,
 	struct ipa3_desc **desc, struct ipahal_imm_cmd_pyld ***cmd_pyld)
 {
-	u16 entries;
-
-	/* +3: 2 for bodies (hashable and non-hashable) and 1 for flushing */
-	entries = (ipa3_ctx->ep_flt_num) * 2 + 3;
-
 	*desc = kcalloc(entries, sizeof(**desc), GFP_ATOMIC);
 	if (*desc == NULL) {
 		IPAERR("fail to alloc desc blob ip %d\n", ip);
@@ -473,6 +469,7 @@
 	struct ipahal_reg_valmask valmask;
 	u32 tbl_hdr_width;
 	struct ipa3_flt_tbl *tbl;
+	u16 entries;
 
 	tbl_hdr_width = ipahal_get_hw_tbl_hdr_width();
 	memset(&alloc_params, 0, sizeof(alloc_params));
@@ -549,7 +546,10 @@
 		goto fail_size_valid;
 	}
 
-	if (ipa_flt_alloc_cmd_buffers(ip, &desc, &cmd_pyld)) {
+	/* +3: 2 for bodies (hashable and non-hashable) and 1 for flushing */
+	entries = (ipa3_ctx->ep_flt_num) * 2 + 3;
+
+	if (ipa_flt_alloc_cmd_buffers(ip, entries, &desc, &cmd_pyld)) {
 		rc = -ENOMEM;
 		goto fail_size_valid;
 	}
@@ -573,11 +573,8 @@
 		rc = -EFAULT;
 		goto fail_reg_write_construct;
 	}
-	desc[0].opcode = cmd_pyld[0]->opcode;
-	desc[0].pyld = cmd_pyld[0]->data;
-	desc[0].len = cmd_pyld[0]->len;
-	desc[0].type = IPA_IMM_CMD_DESC;
-	num_cmd++;
+	ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
+	++num_cmd;
 
 	hdr_idx = 0;
 	for (i = 0; i < ipa3_ctx->ipa_num_pipes; i++) {
@@ -591,6 +588,13 @@
 			continue;
 		}
 
+		if (num_cmd + 1 >= entries) {
+			IPAERR("number of commands is out of range: IP = %d\n",
+				ip);
+			rc = -ENOBUFS;
+			goto fail_imm_cmd_construct;
+		}
+
 		IPADBG_LOW("Prepare imm cmd for hdr at index %d for pipe %d\n",
 			hdr_idx, i);
 
@@ -607,12 +611,11 @@
 		if (!cmd_pyld[num_cmd]) {
 			IPAERR("fail construct dma_shared_mem cmd: IP = %d\n",
 				ip);
+			rc = -ENOMEM;
 			goto fail_imm_cmd_construct;
 		}
-		desc[num_cmd].opcode = cmd_pyld[num_cmd]->opcode;
-		desc[num_cmd].pyld = cmd_pyld[num_cmd]->data;
-		desc[num_cmd].len = cmd_pyld[num_cmd]->len;
-		desc[num_cmd++].type = IPA_IMM_CMD_DESC;
+		ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
+		++num_cmd;
 
 		mem_cmd.is_read = false;
 		mem_cmd.skip_pipeline_clear = false;
@@ -627,17 +630,23 @@
 		if (!cmd_pyld[num_cmd]) {
 			IPAERR("fail construct dma_shared_mem cmd: IP = %d\n",
 				ip);
+			rc = -ENOMEM;
 			goto fail_imm_cmd_construct;
 		}
-		desc[num_cmd].opcode = cmd_pyld[num_cmd]->opcode;
-		desc[num_cmd].pyld = cmd_pyld[num_cmd]->data;
-		desc[num_cmd].len = cmd_pyld[num_cmd]->len;
-		desc[num_cmd++].type = IPA_IMM_CMD_DESC;
+		ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
+		++num_cmd;
 
-		hdr_idx++;
+		++hdr_idx;
 	}
 
 	if (lcl_nhash) {
+		if (num_cmd >= entries) {
+			IPAERR("number of commands is out of range: IP = %d\n",
+				ip);
+			rc = -ENOBUFS;
+			goto fail_imm_cmd_construct;
+		}
+
 		mem_cmd.is_read = false;
 		mem_cmd.skip_pipeline_clear = false;
 		mem_cmd.pipeline_clear_options = IPAHAL_HPS_CLEAR;
@@ -649,14 +658,20 @@
 		if (!cmd_pyld[num_cmd]) {
 			IPAERR("fail construct dma_shared_mem cmd: IP = %d\n",
 				ip);
+			rc = -ENOMEM;
 			goto fail_imm_cmd_construct;
 		}
-		desc[num_cmd].opcode = cmd_pyld[num_cmd]->opcode;
-		desc[num_cmd].pyld = cmd_pyld[num_cmd]->data;
-		desc[num_cmd].len = cmd_pyld[num_cmd]->len;
-		desc[num_cmd++].type = IPA_IMM_CMD_DESC;
+		ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
+		++num_cmd;
 	}
 	if (lcl_hash) {
+		if (num_cmd >= entries) {
+			IPAERR("number of commands is out of range: IP = %d\n",
+				ip);
+			rc = -ENOBUFS;
+			goto fail_imm_cmd_construct;
+		}
+
 		mem_cmd.is_read = false;
 		mem_cmd.skip_pipeline_clear = false;
 		mem_cmd.pipeline_clear_options = IPAHAL_HPS_CLEAR;
@@ -668,12 +683,11 @@
 		if (!cmd_pyld[num_cmd]) {
 			IPAERR("fail construct dma_shared_mem cmd: IP = %d\n",
 				ip);
+			rc = -ENOMEM;
 			goto fail_imm_cmd_construct;
 		}
-		desc[num_cmd].opcode = cmd_pyld[num_cmd]->opcode;
-		desc[num_cmd].pyld = cmd_pyld[num_cmd]->data;
-		desc[num_cmd].len = cmd_pyld[num_cmd]->len;
-		desc[num_cmd++].type = IPA_IMM_CMD_DESC;
+		ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
+		++num_cmd;
 	}
 
 	if (ipa3_send_cmd(num_cmd, desc)) {
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c b/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c
index 34624c0..a89bd78 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c
@@ -199,9 +199,6 @@
 				IPAERR("fail construct dma_shared_mem cmd\n");
 				goto end;
 			}
-			desc[0].opcode = hdr_cmd_pyld->opcode;
-			desc[0].pyld = hdr_cmd_pyld->data;
-			desc[0].len = hdr_cmd_pyld->len;
 		}
 	} else {
 		if (hdr_mem.size > IPA_MEM_PART(apps_hdr_size_ddr)) {
@@ -217,12 +214,9 @@
 				IPAERR("fail construct hdr_init_system cmd\n");
 				goto end;
 			}
-			desc[0].opcode = hdr_cmd_pyld->opcode;
-			desc[0].pyld = hdr_cmd_pyld->data;
-			desc[0].len = hdr_cmd_pyld->len;
 		}
 	}
-	desc[0].type = IPA_IMM_CMD_DESC;
+	ipa3_init_imm_cmd_desc(&desc[0], hdr_cmd_pyld);
 	IPA_DUMP_BUFF(hdr_mem.base, hdr_mem.phys_base, hdr_mem.size);
 
 	proc_ctx_size = IPA_MEM_PART(apps_hdr_proc_ctx_size);
@@ -249,9 +243,6 @@
 				IPAERR("fail construct dma_shared_mem cmd\n");
 				goto end;
 			}
-			desc[1].opcode = ctx_cmd_pyld->opcode;
-			desc[1].pyld = ctx_cmd_pyld->data;
-			desc[1].len = ctx_cmd_pyld->len;
 		}
 	} else {
 		proc_ctx_size_ddr = IPA_MEM_PART(apps_hdr_proc_ctx_size_ddr);
@@ -277,12 +268,9 @@
 				IPAERR("fail construct register_write cmd\n");
 				goto end;
 			}
-			desc[1].opcode = ctx_cmd_pyld->opcode;
-			desc[1].pyld = ctx_cmd_pyld->data;
-			desc[1].len = ctx_cmd_pyld->len;
 		}
 	}
-	desc[1].type = IPA_IMM_CMD_DESC;
+	ipa3_init_imm_cmd_desc(&desc[1], ctx_cmd_pyld);
 	IPA_DUMP_BUFF(ctx_mem.base, ctx_mem.phys_base, ctx_mem.size);
 
 	if (ipa3_send_cmd(2, desc))
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
index cc69eee..2bd65a0 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
@@ -38,8 +38,9 @@
 #include "ipa_uc_offload_i.h"
 #include "ipa_pm.h"
 
+#define IPA_DEV_NAME_MAX_LEN 15
 #define DRV_NAME "ipa"
-#define NAT_DEV_NAME "ipaNatTable"
+
 #define IPA_COOKIE 0x57831603
 #define IPA_RT_RULE_COOKIE 0x57831604
 #define IPA_RT_TBL_COOKIE 0x57831605
@@ -125,6 +126,8 @@
 
 #define IPA_RAM_NAT_OFST    0
 #define IPA_RAM_NAT_SIZE    0
+#define IPA_RAM_IPV6CT_OFST 0
+#define IPA_RAM_IPV6CT_SIZE 0
 #define IPA_MEM_CANARY_VAL 0xdeadbeef
 
 #define IPA_STATS
@@ -780,57 +783,101 @@
 	u32 dst_metadata;
 	u32 resrvd;
 };
+
 /**
- * struct ipa3_nat_mem - IPA NAT memory description
+ * struct ipa3_nat_ipv6ct_tmp_mem - NAT/IPv6CT temporary memory
+ *
+ * In case NAT/IPv6CT table are destroyed the HW is provided with the
+ * temporary memory
+ *
+ * @vaddr: the address of the temporary memory
+ * @dma_handle: the handle of the temporary memory
+ */
+struct ipa3_nat_ipv6ct_tmp_mem {
+	void *vaddr;
+	dma_addr_t dma_handle;
+};
+
+/**
+ * struct ipa3_nat_ipv6ct_common_mem - IPA NAT/IPv6CT memory device
  * @class: pointer to the struct class
  * @dev: the dev_t of the device
  * @cdev: cdev of the device
  * @dev_num: device number
- * @vaddr: virtual address
- * @dma_handle: DMA handle
- * @size: NAT memory size
- * @is_mapped: flag indicating if NAT memory is mapped
- * @is_sys_mem: flag indicating if NAT memory is sys memory
- * @is_dev_init: flag indicating if NAT device is initialized
- * @lock: NAT memory mutex
- * @nat_base_address: nat table virutal address
- * @ipv4_rules_addr: base nat table address
- * @ipv4_expansion_rules_addr: expansion table address
- * @index_table_addr: index table address
- * @index_table_expansion_addr: index expansion table address
- * @size_base_tables: base table size
- * @size_expansion_tables: expansion table size
- * @public_ip_addr: ip address of nat table
- * @pdn_mem: pdn config table SW cache memory structure
+ * @vaddr: the virtual address in the system memory
+ * @dma_handle: the system memory DMA handle
+ * @phys_mem_size: the physical size in the shared memory
+ * @smem_offset: the offset in the shared memory
+ * @size: memory size
+ * @is_mapped: flag indicating if memory is mapped
+ * @is_sys_mem: flag indicating if memory is sys memory
+ * @is_mem_allocated: flag indicating if the memory is allocated
+ * @is_hw_init: flag indicating if the corresponding HW is initialized
+ * @is_dev_init: flag indicating if device is initialized
+ * @lock: memory mutex
+ * @base_address: table virtual address
+ * @base_table_addr: base table address
+ * @expansion_table_addr: expansion table address
+ * @table_entries: num of entries in the base table
+ * @expn_table_entries: num of entries in the expansion table
+ * @tmp_mem: temporary memory used to always provide HW with a legal memory
+ * @name: the device name
  */
-struct ipa3_nat_mem {
+struct ipa3_nat_ipv6ct_common_mem {
 	struct class *class;
 	struct device *dev;
 	struct cdev cdev;
 	dev_t dev_num;
+
+	/* system memory */
 	void *vaddr;
 	dma_addr_t dma_handle;
+
+	/* shared memory */
+	u32 phys_mem_size;
+	u32 smem_offset;
+
 	size_t size;
 	bool is_mapped;
 	bool is_sys_mem;
+	bool is_mem_allocated;
+	bool is_hw_init;
 	bool is_dev_init;
-	bool is_dev;
 	struct mutex lock;
-	void *nat_base_address;
-	char *ipv4_rules_addr;
-	char *ipv4_expansion_rules_addr;
+	void *base_address;
+	char *base_table_addr;
+	char *expansion_table_addr;
+	u32 table_entries;
+	u32 expn_table_entries;
+	struct ipa3_nat_ipv6ct_tmp_mem *tmp_mem;
+	char name[IPA_DEV_NAME_MAX_LEN];
+};
+
+/**
+ * struct ipa3_nat_mem - IPA NAT memory description
+ * @dev: the memory device structure
+ * @index_table_addr: index table address
+ * @index_table_expansion_addr: index expansion table address
+ * @public_ip_addr: ip address of nat table
+ * @pdn_mem: pdn config table SW cache memory structure
+ */
+struct ipa3_nat_mem {
+	struct ipa3_nat_ipv6ct_common_mem dev;
 	char *index_table_addr;
 	char *index_table_expansion_addr;
-	u32 size_base_tables;
-	u32 size_expansion_tables;
 	u32 public_ip_addr;
-	void *tmp_vaddr;
-	dma_addr_t tmp_dma_handle;
-	bool is_tmp_mem;
 	struct ipa_mem_buffer pdn_mem;
 };
 
 /**
+* struct ipa3_ipv6ct_mem - IPA IPv6 connection tracking memory description
+* @dev: the memory device structure
+*/
+struct ipa3_ipv6ct_mem {
+	struct ipa3_nat_ipv6ct_common_mem dev;
+};
+
+/**
  * enum ipa3_hw_mode - IPA hardware mode
  * @IPA_HW_Normal: Regular IPA hardware
  * @IPA_HW_Virtual: IPA hardware supporting virtual memory allocation
@@ -1137,6 +1184,7 @@
  *  from non-restricted bytes
  * @smem_restricted_bytes: the bytes that SW should not use in the shared mem
  * @nat_mem: NAT memory
+ * @ipv6ct_mem: IPv6CT memory
  * @excp_hdr_hdl: exception header handle
  * @dflt_v4_rt_rule_hdl: default v4 routing rule handle
  * @dflt_v6_rt_rule_hdl: default v6 routing rule handle
@@ -1223,6 +1271,7 @@
 	u16 smem_restricted_bytes;
 	u16 smem_reqd_sz;
 	struct ipa3_nat_mem nat_mem;
+	struct ipa3_ipv6ct_mem ipv6ct_mem;
 	u32 excp_hdr_hdl;
 	u32 dflt_v4_rt_rule_hdl;
 	u32 dflt_v6_rt_rule_hdl;
@@ -1743,13 +1792,24 @@
 /*
  * NAT
  */
+int ipa3_nat_ipv6ct_init_devices(void);
+void ipa3_nat_ipv6ct_destroy_devices(void);
+
 int ipa3_allocate_nat_device(struct ipa_ioc_nat_alloc_mem *mem);
+int ipa3_allocate_nat_table(
+	struct ipa_ioc_nat_ipv6ct_table_alloc *table_alloc);
+int ipa3_allocate_ipv6ct_table(
+	struct ipa_ioc_nat_ipv6ct_table_alloc *table_alloc);
 
 int ipa3_nat_init_cmd(struct ipa_ioc_v4_nat_init *init);
+int ipa3_ipv6ct_init_cmd(struct ipa_ioc_ipv6ct_init *init);
 
+int ipa3_table_dma_cmd(struct ipa_ioc_nat_dma_cmd *dma);
 int ipa3_nat_dma_cmd(struct ipa_ioc_nat_dma_cmd *dma);
 
 int ipa3_nat_del_cmd(struct ipa_ioc_v4_nat_del *del);
+int ipa3_del_nat_table(struct ipa_ioc_nat_ipv6ct_table_del *del);
+int ipa3_del_ipv6ct_table(struct ipa_ioc_nat_ipv6ct_table_del *del);
 
 int ipa3_nat_mdfy_pdn(struct ipa_ioc_nat_pdn_entry *mdfy_pdn);
 
@@ -2100,7 +2160,6 @@
 		    bool polling_mode, unsigned long timeout_jiffies);
 void ipa3_uc_register_handlers(enum ipa3_hw_features feature,
 			      struct ipa3_uc_hdlrs *hdlrs);
-int ipa3_create_nat_device(void);
 int ipa3_uc_notify_clk_state(bool enabled);
 int ipa3_dma_setup(void);
 void ipa3_dma_shutdown(void);
@@ -2245,4 +2304,6 @@
 int ipa3_set_clock_plan_from_pm(int idx);
 void __ipa_gsi_irq_rx_scedule_poll(struct ipa3_sys_context *sys);
 int ipa3_tz_unlock_reg(struct ipa_tz_unlock_reg_info *reg_info, u16 num_regs);
+void ipa3_init_imm_cmd_desc(struct ipa3_desc *desc,
+	struct ipahal_imm_cmd_pyld *cmd_pyld);
 #endif /* _IPA3_I_H_ */
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_nat.c b/drivers/platform/msm/ipa/ipa_v3/ipa_nat.c
index 958fc6c..9f27c4f 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_nat.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_nat.c
@@ -18,23 +18,30 @@
 #include <linux/uaccess.h>
 #include "ipa_i.h"
 #include "ipahal/ipahal.h"
+#include "ipahal/ipahal_nat.h"
 
 #define IPA_NAT_PHYS_MEM_OFFSET  0
+#define IPA_IPV6CT_PHYS_MEM_OFFSET  0
 #define IPA_NAT_PHYS_MEM_SIZE  IPA_RAM_NAT_SIZE
+#define IPA_IPV6CT_PHYS_MEM_SIZE  IPA_RAM_IPV6CT_SIZE
 
-#define IPA_NAT_TEMP_MEM_SIZE 128
+#define IPA_NAT_IPV6CT_TEMP_MEM_SIZE 128
 
-enum nat_table_type {
+#define IPA_NAT_MAX_NUM_OF_INIT_CMD_DESC 3
+#define IPA_IPV6CT_MAX_NUM_OF_INIT_CMD_DESC 2
+#define IPA_MAX_NUM_OF_TABLE_DMA_CMD_DESC 4
+#define IPA_MAX_NUM_OF_DEL_TABLE_CMD_DESC 2
+
+enum ipa_nat_ipv6ct_table_type {
 	IPA_NAT_BASE_TBL = 0,
 	IPA_NAT_EXPN_TBL = 1,
 	IPA_NAT_INDX_TBL = 2,
 	IPA_NAT_INDEX_EXPN_TBL = 3,
+	IPA_IPV6CT_BASE_TBL = 4,
+	IPA_IPV6CT_EXPN_TBL = 5
 };
 
-#define NAT_TABLE_ENTRY_SIZE_BYTE 32
-#define NAT_INTEX_TABLE_ENTRY_SIZE_BYTE 4
-
-static int ipa3_nat_vma_fault_remap(
+static int ipa3_nat_ipv6ct_vma_fault_remap(
 	 struct vm_area_struct *vma, struct vm_fault *vmf)
 {
 	IPADBG("\n");
@@ -44,193 +51,340 @@
 }
 
 /* VMA related file operations functions */
-static struct vm_operations_struct ipa3_nat_remap_vm_ops = {
-	.fault = ipa3_nat_vma_fault_remap,
+static const struct vm_operations_struct ipa3_nat_ipv6ct_remap_vm_ops = {
+	.fault = ipa3_nat_ipv6ct_vma_fault_remap,
 };
 
-static int ipa3_nat_open(struct inode *inode, struct file *filp)
+static int ipa3_nat_ipv6ct_open(struct inode *inode, struct file *filp)
 {
-	struct ipa3_nat_mem *nat_ctx;
+	struct ipa3_nat_ipv6ct_common_mem *dev;
 
 	IPADBG("\n");
-	nat_ctx = container_of(inode->i_cdev, struct ipa3_nat_mem, cdev);
-	filp->private_data = nat_ctx;
+	dev = container_of(inode->i_cdev,
+		struct ipa3_nat_ipv6ct_common_mem, cdev);
+	filp->private_data = dev;
 	IPADBG("return\n");
 
 	return 0;
 }
 
-static int ipa3_nat_mmap(struct file *filp, struct vm_area_struct *vma)
+static int ipa3_nat_ipv6ct_mmap(struct file *filp, struct vm_area_struct *vma)
 {
+	struct ipa3_nat_ipv6ct_common_mem *dev =
+		(struct ipa3_nat_ipv6ct_common_mem *)filp->private_data;
 	unsigned long vsize = vma->vm_end - vma->vm_start;
-	struct ipa3_nat_mem *nat_ctx =
-		(struct ipa3_nat_mem *)filp->private_data;
 	unsigned long phys_addr;
-	int result;
+	int result = 0;
 
-	mutex_lock(&nat_ctx->lock);
-	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
-	if (nat_ctx->is_sys_mem) {
-		IPADBG("Mapping system memory\n");
-		if (nat_ctx->is_mapped) {
-			IPAERR("mapping already exists, only 1 supported\n");
+	IPADBG("\n");
+
+	if (!dev->is_dev_init) {
+		IPAERR("attempt to mmap %s before dev init\n", dev->name);
+		return -EPERM;
+	}
+
+	mutex_lock(&dev->lock);
+	if (!dev->is_mem_allocated) {
+		IPAERR_RL("attempt to mmap %s before the memory allocation\n",
+			dev->name);
+		result = -EPERM;
+		goto bail;
+	}
+
+	if (dev->is_sys_mem) {
+		if (dev->is_mapped) {
+			IPAERR("%s already mapped, only 1 mapping supported\n",
+				dev->name);
 			result = -EINVAL;
 			goto bail;
 		}
-		IPADBG("map sz=0x%zx\n", nat_ctx->size);
+	} else {
+		if ((dev->phys_mem_size == 0) || (vsize > dev->phys_mem_size)) {
+			IPAERR_RL("wrong parameters to %s mapping\n",
+				dev->name);
+			result = -EINVAL;
+			goto bail;
+		}
+	}
+
+	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+	if (dev->is_sys_mem) {
+		IPADBG("Mapping system memory\n");
+		IPADBG("map sz=0x%zx\n", dev->size);
 		result =
 			dma_mmap_coherent(
-				 ipa3_ctx->pdev, vma,
-				 nat_ctx->vaddr, nat_ctx->dma_handle,
-				 nat_ctx->size);
-
+				ipa3_ctx->pdev, vma,
+				dev->vaddr, dev->dma_handle,
+				dev->size);
 		if (result) {
 			IPAERR("unable to map memory. Err:%d\n", result);
 			goto bail;
 		}
-		ipa3_ctx->nat_mem.nat_base_address = nat_ctx->vaddr;
+		dev->base_address = dev->vaddr;
 	} else {
 		IPADBG("Mapping shared(local) memory\n");
 		IPADBG("map sz=0x%lx\n", vsize);
 
-		if ((IPA_NAT_PHYS_MEM_SIZE == 0) ||
-				(vsize > IPA_NAT_PHYS_MEM_SIZE)) {
-			result = -EINVAL;
-			goto bail;
-		}
 		phys_addr = ipa3_ctx->ipa_wrapper_base +
 			ipa3_ctx->ctrl->ipa_reg_base_ofst +
 			ipahal_get_reg_n_ofst(IPA_SRAM_DIRECT_ACCESS_n,
-			IPA_NAT_PHYS_MEM_OFFSET);
+				dev->smem_offset);
 
 		if (remap_pfn_range(
-			 vma, vma->vm_start,
-			 phys_addr >> PAGE_SHIFT, vsize, vma->vm_page_prot)) {
+			vma, vma->vm_start,
+			phys_addr >> PAGE_SHIFT, vsize, vma->vm_page_prot)) {
 			IPAERR("remap failed\n");
 			result = -EAGAIN;
 			goto bail;
 		}
-		ipa3_ctx->nat_mem.nat_base_address = (void *)vma->vm_start;
+		dev->base_address = (void *)vma->vm_start;
 	}
-	nat_ctx->is_mapped = true;
-	vma->vm_ops = &ipa3_nat_remap_vm_ops;
-	IPADBG("return\n");
 	result = 0;
+	vma->vm_ops = &ipa3_nat_ipv6ct_remap_vm_ops;
+	dev->is_mapped = true;
+	IPADBG("return\n");
 bail:
-	mutex_unlock(&nat_ctx->lock);
+	mutex_unlock(&dev->lock);
 	return result;
 }
 
-static const struct file_operations ipa3_nat_fops = {
+static const struct file_operations ipa3_nat_ipv6ct_fops = {
 	.owner = THIS_MODULE,
-	.open = ipa3_nat_open,
-	.mmap = ipa3_nat_mmap
+	.open = ipa3_nat_ipv6ct_open,
+	.mmap = ipa3_nat_ipv6ct_mmap
 };
 
 /**
- * ipa3_allocate_temp_nat_memory() - Allocates temp nat memory
- *
- * Called during nat table delete
+ * ipa3_allocate_nat_ipv6ct_tmp_memory() - Allocates the NAT\IPv6CT temp memory
  */
-void ipa3_allocate_temp_nat_memory(void)
+static struct ipa3_nat_ipv6ct_tmp_mem *ipa3_nat_ipv6ct_allocate_tmp_memory(void)
 {
-	struct ipa3_nat_mem *nat_ctx = &(ipa3_ctx->nat_mem);
-	int gfp_flags = GFP_KERNEL | __GFP_ZERO;
-
-	nat_ctx->tmp_vaddr =
-		dma_alloc_coherent(ipa3_ctx->pdev, IPA_NAT_TEMP_MEM_SIZE,
-				&nat_ctx->tmp_dma_handle, gfp_flags);
-
-	if (nat_ctx->tmp_vaddr == NULL) {
-		IPAERR("Temp Memory alloc failed\n");
-		nat_ctx->is_tmp_mem = false;
-		return;
-	}
-
-	nat_ctx->is_tmp_mem = true;
-	IPADBG("IPA NAT allocated temp memory successfully\n");
-}
-
-/**
- * ipa3_create_nat_device() - Create the NAT device
- *
- * Called during ipa init to create nat device
- *
- * Returns:	0 on success, negative on failure
- */
-int ipa3_create_nat_device(void)
-{
-	struct ipa3_nat_mem *nat_ctx = &(ipa3_ctx->nat_mem);
-	int result;
+	struct ipa3_nat_ipv6ct_tmp_mem *tmp_mem;
+	gfp_t gfp_flags = GFP_KERNEL | __GFP_ZERO;
 
 	IPADBG("\n");
 
-	mutex_lock(&nat_ctx->lock);
-	nat_ctx->class = class_create(THIS_MODULE, NAT_DEV_NAME);
-	if (IS_ERR(nat_ctx->class)) {
-		IPAERR("unable to create the class\n");
-		result = -ENODEV;
-		goto vaddr_alloc_fail;
+	tmp_mem = kzalloc(sizeof(*tmp_mem), GFP_KERNEL);
+	if (tmp_mem == NULL)
+		return NULL;
+
+	tmp_mem->vaddr =
+		dma_alloc_coherent(ipa3_ctx->pdev, IPA_NAT_IPV6CT_TEMP_MEM_SIZE,
+			&tmp_mem->dma_handle, gfp_flags);
+	if (tmp_mem->vaddr == NULL)
+		goto bail_tmp_mem;
+
+	IPADBG("IPA successfully allocated temp memory\n");
+	return tmp_mem;
+
+bail_tmp_mem:
+	kfree(tmp_mem);
+	return NULL;
+}
+
+static int ipa3_nat_ipv6ct_init_device(
+	struct ipa3_nat_ipv6ct_common_mem *dev,
+	const char *name,
+	u32 phys_mem_size,
+	u32 smem_offset,
+	struct ipa3_nat_ipv6ct_tmp_mem *tmp_mem)
+{
+	int result;
+
+	IPADBG("Init %s\n", name);
+
+	if (strnlen(name, IPA_DEV_NAME_MAX_LEN) == IPA_DEV_NAME_MAX_LEN) {
+		IPAERR("device name is too long\n");
+		return -ENODEV;
 	}
-	result = alloc_chrdev_region(&nat_ctx->dev_num,
-					0,
-					1,
-					NAT_DEV_NAME);
+
+	strlcpy(dev->name, name, IPA_DEV_NAME_MAX_LEN);
+
+	dev->class = class_create(THIS_MODULE, name);
+	if (IS_ERR(dev->class)) {
+		IPAERR("unable to create the class for %s\n", name);
+		return -ENODEV;
+	}
+	result = alloc_chrdev_region(&dev->dev_num, 0, 1, name);
 	if (result) {
-		IPAERR("alloc_chrdev_region err.\n");
+		IPAERR("alloc_chrdev_region err. for %s\n", name);
 		result = -ENODEV;
 		goto alloc_chrdev_region_fail;
 	}
 
-	nat_ctx->dev =
-	   device_create(nat_ctx->class, NULL, nat_ctx->dev_num, nat_ctx,
-			"%s", NAT_DEV_NAME);
+	dev->dev = device_create(dev->class, NULL, dev->dev_num, NULL, name);
 
-	if (IS_ERR(nat_ctx->dev)) {
-		IPAERR("device_create err:%ld\n", PTR_ERR(nat_ctx->dev));
+	if (IS_ERR(dev->dev)) {
+		IPAERR("device_create err:%ld\n", PTR_ERR(dev->dev));
 		result = -ENODEV;
 		goto device_create_fail;
 	}
 
-	cdev_init(&nat_ctx->cdev, &ipa3_nat_fops);
-	nat_ctx->cdev.owner = THIS_MODULE;
-	nat_ctx->cdev.ops = &ipa3_nat_fops;
+	cdev_init(&dev->cdev, &ipa3_nat_ipv6ct_fops);
+	dev->cdev.owner = THIS_MODULE;
 
-	result = cdev_add(&nat_ctx->cdev, nat_ctx->dev_num, 1);
+	mutex_init(&dev->lock);
+	mutex_lock(&dev->lock);
+
+	result = cdev_add(&dev->cdev, dev->dev_num, 1);
 	if (result) {
 		IPAERR("cdev_add err=%d\n", -result);
 		goto cdev_add_fail;
 	}
-	IPADBG("ipa nat dev added successful. major:%d minor:%d\n",
-			MAJOR(nat_ctx->dev_num),
-			MINOR(nat_ctx->dev_num));
 
-	nat_ctx->is_dev = true;
-	ipa3_allocate_temp_nat_memory();
-	IPADBG("IPA NAT device created successfully\n");
-	result = 0;
-	goto bail;
+	dev->phys_mem_size = phys_mem_size;
+	dev->smem_offset = smem_offset;
+
+	dev->is_dev_init = true;
+	mutex_unlock(&dev->lock);
+
+	IPADBG("ipa dev %s added successful. major:%d minor:%d\n", name,
+		MAJOR(dev->dev_num), MINOR(dev->dev_num));
+	return 0;
 
 cdev_add_fail:
-	device_destroy(nat_ctx->class, nat_ctx->dev_num);
+	mutex_unlock(&dev->lock);
+	device_destroy(dev->class, dev->dev_num);
 device_create_fail:
-	unregister_chrdev_region(nat_ctx->dev_num, 1);
+	unregister_chrdev_region(dev->dev_num, 1);
 alloc_chrdev_region_fail:
-	class_destroy(nat_ctx->class);
-vaddr_alloc_fail:
-	if (nat_ctx->vaddr) {
-		IPADBG("Releasing system memory\n");
-		dma_free_coherent(
-			 ipa3_ctx->pdev, nat_ctx->size,
-			 nat_ctx->vaddr, nat_ctx->dma_handle);
-		nat_ctx->vaddr = NULL;
-		nat_ctx->dma_handle = 0;
-		nat_ctx->size = 0;
+	class_destroy(dev->class);
+	return result;
+}
+
+static void ipa3_nat_ipv6ct_destroy_device(
+	struct ipa3_nat_ipv6ct_common_mem *dev)
+{
+	IPADBG("\n");
+
+	mutex_lock(&dev->lock);
+
+	device_destroy(dev->class, dev->dev_num);
+	unregister_chrdev_region(dev->dev_num, 1);
+	class_destroy(dev->class);
+	dev->is_dev_init = false;
+
+	mutex_unlock(&dev->lock);
+
+	IPADBG("return\n");
+}
+
+/**
+ * ipa3_nat_ipv6ct_init_devices() - Initialize the NAT and IPv6CT devices
+ *
+ * Called during IPA init to create memory device
+ *
+ * Returns:	0 on success, negative on failure
+ */
+int ipa3_nat_ipv6ct_init_devices(void)
+{
+	struct ipa3_nat_ipv6ct_tmp_mem *tmp_mem;
+	int result;
+
+	IPADBG("\n");
+
+	/*
+	 * Allocate NAT/IPv6CT temporary memory. The memory is never deleted,
+	 * because provided to HW once NAT or IPv6CT table is deleted.
+	 * NULL is a legal value
+	 */
+	tmp_mem = ipa3_nat_ipv6ct_allocate_tmp_memory();
+
+	if (ipa3_nat_ipv6ct_init_device(
+		&ipa3_ctx->nat_mem.dev,
+		IPA_NAT_DEV_NAME,
+		IPA_NAT_PHYS_MEM_SIZE,
+		IPA_NAT_PHYS_MEM_OFFSET,
+		tmp_mem)) {
+		IPAERR("unable to create nat device\n");
+		result = -ENODEV;
+		goto fail_init_nat_dev;
 	}
 
-bail:
-	mutex_unlock(&nat_ctx->lock);
+	if ((ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0) &&
+		ipa3_nat_ipv6ct_init_device(
+			&ipa3_ctx->ipv6ct_mem.dev,
+			IPA_IPV6CT_DEV_NAME,
+			IPA_IPV6CT_PHYS_MEM_SIZE,
+			IPA_IPV6CT_PHYS_MEM_OFFSET,
+			tmp_mem)) {
+		IPAERR("unable to create IPv6CT device\n");
+		result = -ENODEV;
+		goto fail_init_ipv6ct_dev;
+	}
 
+	return 0;
+
+fail_init_ipv6ct_dev:
+	ipa3_nat_ipv6ct_destroy_device(&ipa3_ctx->nat_mem.dev);
+fail_init_nat_dev:
+	if (tmp_mem != NULL) {
+		dma_free_coherent(ipa3_ctx->pdev, IPA_NAT_IPV6CT_TEMP_MEM_SIZE,
+			tmp_mem->vaddr, tmp_mem->dma_handle);
+		kfree(tmp_mem);
+	}
+	return result;
+}
+
+/**
+ * ipa3_nat_ipv6ct_destroy_devices() - destroy the NAT and IPv6CT devices
+ *
+ * Called during IPA init to destroy nat device
+ */
+void ipa3_nat_ipv6ct_destroy_devices(void)
+{
+	ipa3_nat_ipv6ct_destroy_device(&ipa3_ctx->nat_mem.dev);
+	if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0)
+		ipa3_nat_ipv6ct_destroy_device(&ipa3_ctx->ipv6ct_mem.dev);
+}
+
+static int ipa3_nat_ipv6ct_allocate_mem(struct ipa3_nat_ipv6ct_common_mem *dev,
+	struct ipa_ioc_nat_ipv6ct_table_alloc *table_alloc)
+{
+	gfp_t gfp_flags = GFP_KERNEL | __GFP_ZERO;
+	int result = 0;
+
+	IPADBG("passed memory size %zu for %s\n",
+		table_alloc->size, dev->name);
+
+	if (!dev->is_dev_init) {
+		IPAERR("%s hasn't been initialized\n", dev->name);
+		result = -EPERM;
+		goto bail;
+	}
+
+	if (dev->is_mem_allocated) {
+		IPAERR("Memory already allocated\n");
+		result = 0;
+		goto bail;
+	}
+
+	if (!table_alloc->size) {
+		IPAERR_RL("Invalid Parameters\n");
+		result = -EPERM;
+		goto bail;
+	}
+
+	if (table_alloc->size > IPA_NAT_PHYS_MEM_SIZE) {
+		IPADBG("Allocating system memory\n");
+		dev->is_sys_mem = true;
+		dev->vaddr =
+		   dma_alloc_coherent(ipa3_ctx->pdev, table_alloc->size,
+			   &dev->dma_handle, gfp_flags);
+		if (dev->vaddr == NULL) {
+			IPAERR("memory alloc failed\n");
+			result = -ENOMEM;
+			goto bail;
+		}
+		dev->size = table_alloc->size;
+	} else {
+		IPADBG("using shared(local) memory\n");
+		dev->is_sys_mem = false;
+	}
+
+	IPADBG("return\n");
+
+bail:
 	return result;
 }
 
@@ -245,60 +399,51 @@
  */
 int ipa3_allocate_nat_device(struct ipa_ioc_nat_alloc_mem *mem)
 {
+	int result;
+	struct ipa_ioc_nat_ipv6ct_table_alloc tmp;
+
+	tmp.size = mem->size;
+	tmp.offset = 0;
+
+	result = ipa3_allocate_nat_table(&tmp);
+	if (result)
+		goto bail;
+
+	mem->offset = tmp.offset;
+
+bail:
+	return result;
+}
+
+/**
+ * ipa3_allocate_nat_table() - Allocates memory for the NAT table
+ * @table_alloc: [in/out] memory parameters
+ *
+ * Called by NAT client to allocate memory for the table entries.
+ * Based on the request size either shared or system memory will be used.
+ *
+ * Returns:	0 on success, negative on failure
+ */
+int ipa3_allocate_nat_table(struct ipa_ioc_nat_ipv6ct_table_alloc *table_alloc)
+{
 	struct ipa3_nat_mem *nat_ctx = &(ipa3_ctx->nat_mem);
 	gfp_t gfp_flags = GFP_KERNEL | __GFP_ZERO;
 	int result;
 
-	IPADBG("passed memory size %zu\n", mem->size);
+	IPADBG("\n");
 
-	mutex_lock(&nat_ctx->lock);
-	if (strcmp(mem->dev_name, NAT_DEV_NAME)) {
-		IPAERR_RL("Nat device name mismatch\n");
-		IPAERR_RL("Expect: %s Recv: %s\n", NAT_DEV_NAME, mem->dev_name);
-		result = -EPERM;
+	mutex_lock(&nat_ctx->dev.lock);
+
+	result = ipa3_nat_ipv6ct_allocate_mem(&nat_ctx->dev, table_alloc);
+	if (result)
 		goto bail;
-	}
 
-	if (nat_ctx->is_dev != true) {
-		IPAERR("Nat device not created successfully during boot up\n");
-		result = -EPERM;
-		goto bail;
-	}
-
-	if (nat_ctx->is_dev_init == true) {
-		IPAERR("Device already init\n");
-		result = 0;
-		goto bail;
-	}
-
-	if (mem->size <= 0 ||
-			nat_ctx->is_dev_init == true) {
-		IPAERR_RL("Invalid Parameters or device is already init\n");
-		result = -EPERM;
-		goto bail;
-	}
-
-	if (mem->size > IPA_NAT_PHYS_MEM_SIZE) {
-		IPADBG("Allocating system memory\n");
-		nat_ctx->is_sys_mem = true;
-		nat_ctx->vaddr =
-		   dma_alloc_coherent(ipa3_ctx->pdev, mem->size,
-				   &nat_ctx->dma_handle, gfp_flags);
-		if (nat_ctx->vaddr == NULL) {
-			IPAERR("memory alloc failed\n");
-			result = -ENOMEM;
-			goto bail;
-		}
-		nat_ctx->size = mem->size;
-	} else {
-		IPADBG("using shared(local) memory\n");
-		nat_ctx->is_sys_mem = false;
-	}
 	if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0) {
-		struct ipa_pdn_entry *pdn_entries;
-		struct ipa_mem_buffer *pdn_mem = &ipa3_ctx->nat_mem.pdn_mem;
+		size_t pdn_entry_size;
+		struct ipa_mem_buffer *pdn_mem = &nat_ctx->pdn_mem;
 
-		pdn_mem->size = sizeof(struct ipa_pdn_entry) * IPA_MAX_PDN_NUM;
+		ipahal_nat_entry_size(IPAHAL_NAT_IPV4_PDN, &pdn_entry_size);
+		pdn_mem->size = pdn_entry_size * IPA_MAX_PDN_NUM;
 		if (IPA_MEM_PART(pdn_config_size) < pdn_mem->size) {
 			IPAERR(
 				"number of PDN entries exceeds SRAM available space\n");
@@ -315,29 +460,203 @@
 			result = -ENOMEM;
 			goto fail_alloc_pdn;
 		}
-		pdn_entries = pdn_mem->base;
-		memset(pdn_entries, 0, pdn_mem->size);
+		memset(pdn_mem->base, 0, pdn_mem->size);
 		IPADBG("IPA NAT dev allocated PDN memory successfully\n");
 	}
 
-	nat_ctx->is_dev_init = true;
+	nat_ctx->dev.is_mem_allocated = true;
 	IPADBG("IPA NAT dev init successfully\n");
-	mutex_unlock(&nat_ctx->lock);
+	mutex_unlock(&nat_ctx->dev.lock);
+
+	IPADBG("return\n");
 
 	return 0;
 
 fail_alloc_pdn:
-	if (nat_ctx->vaddr) {
-		dma_free_coherent(ipa3_ctx->pdev, mem->size, nat_ctx->vaddr,
-			nat_ctx->dma_handle);
-		nat_ctx->vaddr = NULL;
+	if (nat_ctx->dev.vaddr) {
+		dma_free_coherent(ipa3_ctx->pdev, table_alloc->size,
+			nat_ctx->dev.vaddr, nat_ctx->dev.dma_handle);
+		nat_ctx->dev.vaddr = NULL;
 	}
 bail:
-	mutex_unlock(&nat_ctx->lock);
+	mutex_unlock(&nat_ctx->dev.lock);
 
 	return result;
 }
 
+/**
+ * ipa3_allocate_ipv6ct_table() - Allocates memory for the IPv6CT table
+ * @table_alloc: [in/out] memory parameters
+ *
+ * Called by IPv6CT client to allocate memory for the table entries.
+ * Based on the request size either shared or system memory will be used.
+ *
+ * Returns:	0 on success, negative on failure
+ */
+int ipa3_allocate_ipv6ct_table(
+	struct ipa_ioc_nat_ipv6ct_table_alloc *table_alloc)
+{
+	int result;
+
+	IPADBG("\n");
+
+	if (ipa3_ctx->ipa_hw_type < IPA_HW_v4_0) {
+		IPAERR_RL("IPv6 connection tracking isn't supported\n");
+		return -EPERM;
+	}
+
+	mutex_lock(&ipa3_ctx->ipv6ct_mem.dev.lock);
+
+	result = ipa3_nat_ipv6ct_allocate_mem(
+		&ipa3_ctx->ipv6ct_mem.dev, table_alloc);
+	if (result)
+		goto bail;
+
+	ipa3_ctx->ipv6ct_mem.dev.is_mem_allocated = true;
+	IPADBG("IPA IPv6CT dev init successfully\n");
+
+bail:
+	mutex_unlock(&ipa3_ctx->ipv6ct_mem.dev.lock);
+	return result;
+}
+
+static int ipa3_nat_ipv6ct_check_table_params(
+	struct ipa3_nat_ipv6ct_common_mem *dev,
+	uint32_t offset, uint16_t entries_num,
+	enum ipahal_nat_type nat_type)
+{
+	int result;
+	size_t entry_size, table_size;
+
+	result = ipahal_nat_entry_size(nat_type, &entry_size);
+	if (result) {
+		IPAERR("Failed to retrieve size of entry for %s\n",
+			ipahal_nat_type_str(nat_type));
+		return result;
+	}
+	table_size = entry_size * entries_num;
+
+	/* check for integer overflow */
+	if (offset > UINT_MAX - table_size) {
+		IPAERR_RL("Detected overflow\n");
+		return -EPERM;
+	}
+
+	/* Check offset is not beyond allocated size */
+	if (dev->size < offset + table_size) {
+		IPAERR_RL("Table offset not valid\n");
+		IPAERR_RL("offset:%d entries:%d table_size:%zu mem_size:%zu\n",
+			offset, entries_num, table_size, dev->size);
+		return -EPERM;
+	}
+
+	if (dev->is_sys_mem && offset > UINT_MAX - dev->dma_handle) {
+		IPAERR_RL("Failed due to integer overflow\n");
+		IPAERR_RL("%s dma_handle: 0x%pa offset: 0x%x\n",
+			dev->name, &dev->dma_handle, offset);
+		return -EPERM;
+	}
+
+	return 0;
+}
+
+static inline void ipa3_nat_ipv6ct_create_init_cmd(
+	struct ipahal_imm_cmd_nat_ipv6ct_init_common *table_init_cmd,
+	bool is_shared,
+	dma_addr_t base_addr,
+	uint8_t tbl_index,
+	uint32_t base_table_offset,
+	uint32_t expn_table_offset,
+	uint16_t table_entries,
+	uint16_t expn_table_entries,
+	const char *table_name)
+{
+	table_init_cmd->base_table_addr_shared = is_shared;
+	table_init_cmd->expansion_table_addr_shared = is_shared;
+
+	table_init_cmd->base_table_addr = base_addr + base_table_offset;
+	IPADBG("%s base table offset:0x%x\n", table_name, base_table_offset);
+
+	table_init_cmd->expansion_table_addr = base_addr + expn_table_offset;
+	IPADBG("%s expn table offset:0x%x\n", table_name, expn_table_offset);
+
+	table_init_cmd->table_index = tbl_index;
+	IPADBG("%s table index:0x%x\n", table_name, tbl_index);
+
+	table_init_cmd->size_base_table = table_entries;
+	IPADBG("%s base table size:0x%x\n", table_name, table_entries);
+
+	table_init_cmd->size_expansion_table = expn_table_entries;
+	IPADBG("%s expansion table size:0x%x\n",
+		table_name, expn_table_entries);
+}
+
+static inline void ipa3_nat_ipv6ct_init_device_structure(
+	struct ipa3_nat_ipv6ct_common_mem *dev,
+	uint32_t base_table_offset,
+	uint32_t expn_table_offset,
+	uint16_t table_entries,
+	uint16_t expn_table_entries)
+{
+	dev->base_table_addr = (char *)dev->base_address + base_table_offset;
+	IPADBG("%s base_table_addr: 0x%p\n", dev->name, dev->base_table_addr);
+
+	dev->expansion_table_addr =
+		(char *)dev->base_address + expn_table_offset;
+	IPADBG("%s expansion_table_addr: 0x%p\n",
+		dev->name, dev->expansion_table_addr);
+
+	IPADBG("%s table_entries: %d\n", dev->name, table_entries);
+	dev->table_entries = table_entries;
+
+	IPADBG("%s expn_table_entries: %d\n", dev->name, expn_table_entries);
+	dev->expn_table_entries = expn_table_entries;
+}
+
+static void ipa3_nat_create_init_cmd(
+	struct ipa_ioc_v4_nat_init *init,
+	bool is_shared,
+	dma_addr_t base_addr,
+	struct ipahal_imm_cmd_ip_v4_nat_init *cmd)
+{
+	IPADBG("\n");
+
+	ipa3_nat_ipv6ct_create_init_cmd(
+		&cmd->table_init,
+		is_shared,
+		base_addr,
+		init->tbl_index,
+		init->ipv4_rules_offset,
+		init->expn_rules_offset,
+		init->table_entries,
+		init->expn_table_entries,
+		ipa3_ctx->nat_mem.dev.name);
+
+	cmd->index_table_addr_shared = is_shared;
+	cmd->index_table_expansion_addr_shared = is_shared;
+
+	cmd->index_table_addr =
+		base_addr + init->index_offset;
+	IPADBG("index_offset:0x%x\n", init->index_offset);
+
+	cmd->index_table_expansion_addr =
+		base_addr + init->index_expn_offset;
+	IPADBG("index_expn_offset:0x%x\n", init->index_expn_offset);
+	if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0) {
+		/*
+		 * starting IPAv4.0 public ip field changed to store the
+		 * PDN config table offset in SMEM
+		 */
+		cmd->public_addr_info = IPA_MEM_PART(pdn_config_ofst);
+		IPADBG("pdn config base:0x%x\n", cmd->public_addr_info);
+	} else {
+		cmd->public_addr_info = init->ip_addr;
+		IPADBG("Public IP address:%pI4h\n", &cmd->public_addr_info);
+	}
+
+	IPADBG("return\n");
+}
+
 /* IOCTL function handlers */
 /**
  * ipa3_nat_init_cmd() - Post IP_V4_NAT_INIT command to IPA HW
@@ -349,102 +668,71 @@
  */
 int ipa3_nat_init_cmd(struct ipa_ioc_v4_nat_init *init)
 {
-#define TBL_ENTRY_SIZE 32
-#define INDX_TBL_ENTRY_SIZE 4
-
-	struct ipa3_desc desc[3];
+	struct ipa3_desc desc[IPA_NAT_MAX_NUM_OF_INIT_CMD_DESC];
 	struct ipahal_imm_cmd_ip_v4_nat_init cmd;
-	int num_cmd = 0;
-	int i = 0;
-	struct ipahal_imm_cmd_pyld *cmd_pyld[3];
+	int i, num_cmd = 0;
+	struct ipahal_imm_cmd_pyld *cmd_pyld[IPA_NAT_MAX_NUM_OF_INIT_CMD_DESC];
 	struct ipahal_imm_cmd_dma_shared_mem mem_cmd = { 0 };
-	int result = 0;
-	u32 offset = 0;
-	size_t tmp;
+	int result;
 
 	IPADBG("\n");
+
+	if (!ipa3_ctx->nat_mem.dev.is_mapped) {
+		IPAERR_RL("attempt to init %s before mmap\n",
+			ipa3_ctx->nat_mem.dev.name);
+		return -EPERM;
+	}
+
+	if (init->tbl_index >= 1) {
+		IPAERR_RL("Unsupported table index %d\n", init->tbl_index);
+		return -EPERM;
+	}
+
 	if (init->table_entries == 0) {
-		IPADBG("Table entries is zero\n");
+		IPAERR_RL("Table entries is zero\n");
 		return -EPERM;
 	}
 
-	/* check for integer overflow */
-	if (init->ipv4_rules_offset >
-		UINT_MAX - (TBL_ENTRY_SIZE * (init->table_entries + 1))) {
-		IPAERR_RL("Detected overflow\n");
-		return -EPERM;
-	}
-	/* Check Table Entry offset is not
-	 * beyond allocated size
-	 */
-	tmp = init->ipv4_rules_offset +
-		(TBL_ENTRY_SIZE * (init->table_entries + 1));
-	if (tmp > ipa3_ctx->nat_mem.size) {
-		IPAERR_RL("Table rules offset not valid\n");
-		IPAERR_RL("offset:%d entrys:%d size:%zu mem_size:%zu\n",
-			init->ipv4_rules_offset, (init->table_entries + 1),
-			tmp, ipa3_ctx->nat_mem.size);
-		return -EPERM;
+	result = ipa3_nat_ipv6ct_check_table_params(
+		&ipa3_ctx->nat_mem.dev,
+		init->ipv4_rules_offset,
+		init->table_entries + 1,
+		IPAHAL_NAT_IPV4);
+	if (result) {
+		IPAERR_RL("Bad params for NAT base table\n");
+		return result;
 	}
 
-	/* check for integer overflow */
-	if (init->expn_rules_offset >
-		(UINT_MAX - (TBL_ENTRY_SIZE * init->expn_table_entries))) {
-		IPAERR_RL("Detected overflow\n");
-		return -EPERM;
-	}
-	/* Check Expn Table Entry offset is not
-	 * beyond allocated size
-	 */
-	tmp = init->expn_rules_offset +
-		(TBL_ENTRY_SIZE * init->expn_table_entries);
-	if (tmp > ipa3_ctx->nat_mem.size) {
-		IPAERR_RL("Expn Table rules offset not valid\n");
-		IPAERR_RL("offset:%d entrys:%d size:%zu mem_size:%zu\n",
-			init->expn_rules_offset, init->expn_table_entries,
-			tmp, ipa3_ctx->nat_mem.size);
-		return -EPERM;
+	result = ipa3_nat_ipv6ct_check_table_params(
+		&ipa3_ctx->nat_mem.dev,
+		init->expn_rules_offset,
+		init->expn_table_entries,
+		IPAHAL_NAT_IPV4);
+	if (result) {
+		IPAERR_RL("Bad params for NAT expansion table\n");
+		return result;
 	}
 
-	/* check for integer overflow */
-	if (init->index_offset >
-		UINT_MAX - (INDX_TBL_ENTRY_SIZE * (init->table_entries + 1))) {
-		IPAERR_RL("Detected overflow\n");
-		return -EPERM;
-	}
-	/* Check Indx Table Entry offset is not
-	 * beyond allocated size
-	 */
-	tmp = init->index_offset +
-		(INDX_TBL_ENTRY_SIZE * (init->table_entries + 1));
-	if (tmp > ipa3_ctx->nat_mem.size) {
-		IPAERR_RL("Indx Table rules offset not valid\n");
-		IPAERR_RL("offset:%d entrys:%d size:%zu mem_size:%zu\n",
-			init->index_offset, (init->table_entries + 1),
-			tmp, ipa3_ctx->nat_mem.size);
-		return -EPERM;
+	result = ipa3_nat_ipv6ct_check_table_params(
+		&ipa3_ctx->nat_mem.dev,
+		init->index_offset,
+		init->table_entries + 1,
+		IPAHAL_NAT_IPV4_INDEX);
+	if (result) {
+		IPAERR_RL("Bad params for index table\n");
+		return result;
 	}
 
-	/* check for integer overflow */
-	if (init->index_expn_offset >
-		UINT_MAX - (INDX_TBL_ENTRY_SIZE * init->expn_table_entries)) {
-		IPAERR_RL("Detected overflow\n");
-		return -EPERM;
-	}
-	/* Check Expn Table entry offset is not
-	 * beyond allocated size
-	 */
-	tmp = init->index_expn_offset +
-		(INDX_TBL_ENTRY_SIZE * init->expn_table_entries);
-	if (tmp > ipa3_ctx->nat_mem.size) {
-		IPAERR_RL("Indx Expn Table rules offset not valid\n");
-		IPAERR_RL("offset:%d entrys:%d size:%zu mem_size:%zu\n",
-			init->index_expn_offset, init->expn_table_entries,
-			tmp, ipa3_ctx->nat_mem.size);
-		return -EPERM;
+	result = ipa3_nat_ipv6ct_check_table_params(
+		&ipa3_ctx->nat_mem.dev,
+		init->index_expn_offset,
+		init->expn_table_entries,
+		IPAHAL_NAT_IPV4_INDEX);
+	if (result) {
+		IPAERR_RL("Bad params for index expansion table\n");
+		return result;
 	}
 
-	memset(&desc, 0, sizeof(desc));
 	/* NO-OP IC for ensuring that IPA pipeline is empty */
 	cmd_pyld[num_cmd] =
 		ipahal_construct_nop_imm_cmd(false, IPAHAL_HPS_CLEAR, false);
@@ -454,92 +742,20 @@
 		goto bail;
 	}
 
-	desc[num_cmd].opcode = cmd_pyld[num_cmd]->opcode;
-	desc[num_cmd].type = IPA_IMM_CMD_DESC;
-	desc[num_cmd].callback = NULL;
-	desc[num_cmd].user1 = NULL;
-	desc[num_cmd].user2 = 0;
-	desc[num_cmd].pyld = cmd_pyld[num_cmd]->data;
-	desc[num_cmd].len = cmd_pyld[num_cmd]->len;
-	num_cmd++;
+	ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
+	++num_cmd;
 
-	if (ipa3_ctx->nat_mem.vaddr) {
+	if (ipa3_ctx->nat_mem.dev.is_sys_mem) {
 		IPADBG("using system memory for nat table\n");
-		cmd.ipv4_rules_addr_shared = false;
-		cmd.ipv4_expansion_rules_addr_shared = false;
-		cmd.index_table_addr_shared = false;
-		cmd.index_table_expansion_addr_shared = false;
-
-		offset = UINT_MAX - ipa3_ctx->nat_mem.dma_handle;
-
-		if ((init->ipv4_rules_offset > offset) ||
-				(init->expn_rules_offset > offset) ||
-				(init->index_offset > offset) ||
-				(init->index_expn_offset > offset)) {
-			IPAERR_RL("Failed due to integer overflow\n");
-			IPAERR_RL("nat.mem.dma_handle: 0x%pa\n",
-				&ipa3_ctx->nat_mem.dma_handle);
-			IPAERR_RL("ipv4_rules_offset: 0x%x\n",
-				init->ipv4_rules_offset);
-			IPAERR_RL("expn_rules_offset: 0x%x\n",
-				init->expn_rules_offset);
-			IPAERR_RL("index_offset: 0x%x\n",
-				init->index_offset);
-			IPAERR_RL("index_expn_offset: 0x%x\n",
-				init->index_expn_offset);
-			result = -EPERM;
-			goto destroy_imm_cmd;
-		}
-		cmd.ipv4_rules_addr =
-			ipa3_ctx->nat_mem.dma_handle + init->ipv4_rules_offset;
-		IPADBG("ipv4_rules_offset:0x%x\n", init->ipv4_rules_offset);
-
-		cmd.ipv4_expansion_rules_addr =
-		   ipa3_ctx->nat_mem.dma_handle + init->expn_rules_offset;
-		IPADBG("expn_rules_offset:0x%x\n", init->expn_rules_offset);
-
-		cmd.index_table_addr =
-			ipa3_ctx->nat_mem.dma_handle + init->index_offset;
-		IPADBG("index_offset:0x%x\n", init->index_offset);
-
-		cmd.index_table_expansion_addr =
-		   ipa3_ctx->nat_mem.dma_handle + init->index_expn_offset;
-		IPADBG("index_expn_offset:0x%x\n", init->index_expn_offset);
+		/*
+		 * Safe to process, since integer overflow was
+		 * checked in ipa3_nat_ipv6ct_check_table_params
+		 */
+		ipa3_nat_create_init_cmd(init, false,
+			ipa3_ctx->nat_mem.dev.dma_handle, &cmd);
 	} else {
 		IPADBG("using shared(local) memory for nat table\n");
-		cmd.ipv4_rules_addr_shared = true;
-		cmd.ipv4_expansion_rules_addr_shared = true;
-		cmd.index_table_addr_shared = true;
-		cmd.index_table_expansion_addr_shared = true;
-
-		cmd.ipv4_rules_addr = init->ipv4_rules_offset +
-				IPA_RAM_NAT_OFST;
-
-		cmd.ipv4_expansion_rules_addr = init->expn_rules_offset +
-				IPA_RAM_NAT_OFST;
-
-		cmd.index_table_addr = init->index_offset  +
-				IPA_RAM_NAT_OFST;
-
-		cmd.index_table_expansion_addr = init->index_expn_offset +
-				IPA_RAM_NAT_OFST;
-	}
-	cmd.table_index = init->tbl_index;
-	IPADBG("Table index:0x%x\n", cmd.table_index);
-	cmd.size_base_tables = init->table_entries;
-	IPADBG("Base Table size:0x%x\n", cmd.size_base_tables);
-	cmd.size_expansion_tables = init->expn_table_entries;
-	IPADBG("Expansion Table size:0x%x\n", cmd.size_expansion_tables);
-	if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0) {
-		/*
-		 * public ip field changed to store the PDN config base
-		 * address in IPAv4
-		 */
-		cmd.public_ip_addr = IPA_MEM_PART(pdn_config_ofst);
-		IPADBG("pdn config base:0x%x\n", cmd.public_ip_addr);
-	} else {
-		cmd.public_ip_addr = init->ip_addr;
-		IPADBG("Public ip address:0x%x\n", cmd.public_ip_addr);
+		ipa3_nat_create_init_cmd(init, true, IPA_RAM_NAT_OFST, &cmd);
 	}
 	cmd_pyld[num_cmd] = ipahal_construct_imm_cmd(
 		IPA_IMM_CMD_IP_V4_NAT_INIT, &cmd, false);
@@ -549,18 +765,18 @@
 		goto destroy_imm_cmd;
 	}
 
-	desc[num_cmd].opcode = cmd_pyld[num_cmd]->opcode;
-	desc[num_cmd].type = IPA_IMM_CMD_DESC;
-	desc[num_cmd].callback = NULL;
-	desc[num_cmd].user1 = NULL;
-	desc[num_cmd].user2 = 0;
-	desc[num_cmd].pyld = cmd_pyld[num_cmd]->data;
-	desc[num_cmd].len = cmd_pyld[num_cmd]->len;
-	num_cmd++;
+	ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
+	++num_cmd;
 
 	if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0) {
 		struct ipa_pdn_entry *pdn_entries;
 
+		if (num_cmd >= IPA_NAT_MAX_NUM_OF_INIT_CMD_DESC) {
+			IPAERR("number of commands is out of range\n");
+			result = -ENOBUFS;
+			goto destroy_imm_cmd;
+		}
+
 		/* store ip in pdn entries cache array */
 		pdn_entries = ipa3_ctx->nat_mem.pdn_mem.base;
 		pdn_entries[0].public_ip = init->ip_addr;
@@ -586,14 +802,8 @@
 			result = -ENOMEM;
 			goto destroy_imm_cmd;
 		}
-		desc[num_cmd].opcode = cmd_pyld[num_cmd]->opcode;
-		desc[num_cmd].type = IPA_IMM_CMD_DESC;
-		desc[num_cmd].callback = NULL;
-		desc[num_cmd].user1 = NULL;
-		desc[num_cmd].user2 = 0;
-		desc[num_cmd].pyld = cmd_pyld[num_cmd]->data;
-		desc[num_cmd].len = cmd_pyld[num_cmd]->len;
-		num_cmd++;
+		ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
+		++num_cmd;
 		IPADBG("added PDN table copy cmd\n");
 	}
 
@@ -604,52 +814,186 @@
 		goto destroy_imm_cmd;
 	}
 
+	ipa3_nat_ipv6ct_init_device_structure(
+		&ipa3_ctx->nat_mem.dev,
+		init->ipv4_rules_offset,
+		init->expn_rules_offset,
+		init->table_entries,
+		init->expn_table_entries);
+
 	ipa3_ctx->nat_mem.public_ip_addr = init->ip_addr;
-	IPADBG("Table ip address:0x%x", ipa3_ctx->nat_mem.public_ip_addr);
-
-	ipa3_ctx->nat_mem.ipv4_rules_addr =
-	 (char *)ipa3_ctx->nat_mem.nat_base_address + init->ipv4_rules_offset;
-	IPADBG("ipv4_rules_addr: 0x%p\n",
-				 ipa3_ctx->nat_mem.ipv4_rules_addr);
-
-	ipa3_ctx->nat_mem.ipv4_expansion_rules_addr =
-	 (char *)ipa3_ctx->nat_mem.nat_base_address + init->expn_rules_offset;
-	IPADBG("ipv4_expansion_rules_addr: 0x%p\n",
-				 ipa3_ctx->nat_mem.ipv4_expansion_rules_addr);
+	IPADBG("Public IP address:%pI4h\n", &ipa3_ctx->nat_mem.public_ip_addr);
 
 	ipa3_ctx->nat_mem.index_table_addr =
-		 (char *)ipa3_ctx->nat_mem.nat_base_address +
+		 (char *)ipa3_ctx->nat_mem.dev.base_address +
 		 init->index_offset;
 	IPADBG("index_table_addr: 0x%p\n",
 				 ipa3_ctx->nat_mem.index_table_addr);
 
 	ipa3_ctx->nat_mem.index_table_expansion_addr =
-	 (char *)ipa3_ctx->nat_mem.nat_base_address + init->index_expn_offset;
+	 (char *)ipa3_ctx->nat_mem.dev.base_address + init->index_expn_offset;
 	IPADBG("index_table_expansion_addr: 0x%p\n",
 				 ipa3_ctx->nat_mem.index_table_expansion_addr);
 
-	IPADBG("size_base_tables: %d\n", init->table_entries);
-	ipa3_ctx->nat_mem.size_base_tables  = init->table_entries;
-
-	IPADBG("size_expansion_tables: %d\n", init->expn_table_entries);
-	ipa3_ctx->nat_mem.size_expansion_tables = init->expn_table_entries;
-
+	ipa3_ctx->nat_mem.dev.is_hw_init = true;
 	IPADBG("return\n");
 destroy_imm_cmd:
-	for (i = 0; i < num_cmd; i++)
+	for (i = 0; i < num_cmd; ++i)
 		ipahal_destroy_imm_cmd(cmd_pyld[i]);
 bail:
 	return result;
 }
 
 /**
-* ipa3_nat_mdfy_pdn() - Modify a PDN entry in PDN config table in IPA SRAM
-* @mdfy_pdn:	[in] PDN info to be written to SRAM
-*
-* Called by NAT client driver to modify an entry in the PDN config table
-*
-* Returns:	0 on success, negative on failure
-*/
+ * ipa3_ipv6ct_init_cmd() - Post IP_V6_CONN_TRACK_INIT command to IPA HW
+ * @init:	[in] initialization command attributes
+ *
+ * Called by NAT client driver to post IP_V6_CONN_TRACK_INIT command to IPA HW
+ *
+ * Returns:	0 on success, negative on failure
+ */
+int ipa3_ipv6ct_init_cmd(struct ipa_ioc_ipv6ct_init *init)
+{
+	struct ipa3_desc desc[IPA_IPV6CT_MAX_NUM_OF_INIT_CMD_DESC];
+	struct ipahal_imm_cmd_ip_v6_ct_init cmd;
+	int i, num_cmd = 0;
+	struct ipahal_imm_cmd_pyld
+		*cmd_pyld[IPA_IPV6CT_MAX_NUM_OF_INIT_CMD_DESC];
+	int result;
+
+	IPADBG("\n");
+
+	if (ipa3_ctx->ipa_hw_type < IPA_HW_v4_0) {
+		IPAERR_RL("IPv6 connection tracking isn't supported\n");
+		return -EPERM;
+	}
+
+	if (!ipa3_ctx->ipv6ct_mem.dev.is_mapped) {
+		IPAERR_RL("attempt to init %s before mmap\n",
+			ipa3_ctx->ipv6ct_mem.dev.name);
+		return -EPERM;
+	}
+
+	if (init->tbl_index >= 1) {
+		IPAERR_RL("Unsupported table index %d\n", init->tbl_index);
+		return -EPERM;
+	}
+
+	if (init->table_entries == 0) {
+		IPAERR_RL("Table entries is zero\n");
+		return -EPERM;
+	}
+
+	result = ipa3_nat_ipv6ct_check_table_params(
+		&ipa3_ctx->ipv6ct_mem.dev,
+		init->base_table_offset,
+		init->table_entries + 1,
+		IPAHAL_NAT_IPV6CT);
+	if (result) {
+		IPAERR_RL("Bad params for IPv6CT base table\n");
+		return result;
+	}
+
+	result = ipa3_nat_ipv6ct_check_table_params(
+		&ipa3_ctx->ipv6ct_mem.dev,
+		init->expn_table_offset,
+		init->expn_table_entries,
+		IPAHAL_NAT_IPV6CT);
+	if (result) {
+		IPAERR_RL("Bad params for IPv6CT expansion table\n");
+		return result;
+	}
+
+	/* NO-OP IC for ensuring that IPA pipeline is empty */
+	cmd_pyld[num_cmd] =
+		ipahal_construct_nop_imm_cmd(false, IPAHAL_HPS_CLEAR, false);
+	if (!cmd_pyld[num_cmd]) {
+		IPAERR("failed to construct NOP imm cmd\n");
+		result = -ENOMEM;
+		goto bail;
+	}
+
+	ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
+	++num_cmd;
+
+	if (ipa3_ctx->ipv6ct_mem.dev.is_sys_mem) {
+		IPADBG("using system memory for nat table\n");
+		/*
+		 * Safe to process, since integer overflow was
+		 * checked in ipa3_nat_ipv6ct_check_table_params
+		 */
+		ipa3_nat_ipv6ct_create_init_cmd(
+			&cmd.table_init,
+			false,
+			ipa3_ctx->ipv6ct_mem.dev.dma_handle,
+			init->tbl_index,
+			init->base_table_offset,
+			init->expn_table_offset,
+			init->table_entries,
+			init->expn_table_entries,
+			ipa3_ctx->ipv6ct_mem.dev.name);
+	} else {
+		IPADBG("using shared(local) memory for nat table\n");
+		ipa3_nat_ipv6ct_create_init_cmd(
+			&cmd.table_init,
+			true,
+			IPA_RAM_IPV6CT_OFST,
+			init->tbl_index,
+			init->base_table_offset,
+			init->expn_table_offset,
+			init->table_entries,
+			init->expn_table_entries,
+			ipa3_ctx->ipv6ct_mem.dev.name);
+	}
+
+	if (num_cmd >= IPA_IPV6CT_MAX_NUM_OF_INIT_CMD_DESC) {
+		IPAERR("number of commands is out of range\n");
+		result = -ENOBUFS;
+		goto destroy_imm_cmd;
+	}
+
+	cmd_pyld[num_cmd] = ipahal_construct_imm_cmd(
+		IPA_IMM_CMD_IP_V6_CT_INIT, &cmd, false);
+	if (!cmd_pyld[num_cmd]) {
+		IPAERR_RL("Fail to construct ip_v6_ct_init imm cmd\n");
+		result = -EPERM;
+		goto destroy_imm_cmd;
+	}
+
+	ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
+	++num_cmd;
+
+	IPADBG("posting ip_v6_ct_init imm command\n");
+	if (ipa3_send_cmd(num_cmd, desc)) {
+		IPAERR("Fail to send immediate command\n");
+		result = -EPERM;
+		goto destroy_imm_cmd;
+	}
+
+	ipa3_nat_ipv6ct_init_device_structure(
+		&ipa3_ctx->ipv6ct_mem.dev,
+		init->base_table_offset,
+		init->expn_table_offset,
+		init->table_entries,
+		init->expn_table_entries);
+
+	ipa3_ctx->ipv6ct_mem.dev.is_hw_init = true;
+	IPADBG("return\n");
+destroy_imm_cmd:
+	for (i = 0; i < num_cmd; ++i)
+		ipahal_destroy_imm_cmd(cmd_pyld[i]);
+bail:
+	return result;
+}
+
+/**
+ * ipa3_nat_mdfy_pdn() - Modify a PDN entry in PDN config table in IPA SRAM
+ * @mdfy_pdn:	[in] PDN info to be written to SRAM
+ *
+ * Called by NAT client driver to modify an entry in the PDN config table
+ *
+ * Returns:	0 on success, negative on failure
+ */
 int ipa3_nat_mdfy_pdn(struct ipa_ioc_nat_pdn_entry *mdfy_pdn)
 {
 	struct ipahal_imm_cmd_dma_shared_mem mem_cmd = { 0 };
@@ -659,21 +1003,24 @@
 	struct ipa3_nat_mem *nat_ctx = &(ipa3_ctx->nat_mem);
 	struct ipa_pdn_entry *pdn_entries = nat_ctx->pdn_mem.base;
 
+	IPADBG("\n");
+
 	if (ipa3_ctx->ipa_hw_type < IPA_HW_v4_0) {
-		IPAERR("IPA HW does not support multi PDN\n");
+		IPAERR_RL("IPA HW does not support multi PDN\n");
 		return -EPERM;
 	}
-	if (!nat_ctx->is_dev_init) {
-		IPAERR("attempt to modify a PDN entry before dev init\n");
+	if (!nat_ctx->dev.is_mem_allocated) {
+		IPAERR_RL(
+			"attempt to modify a PDN entry before the PDN table memory allocation\n");
 		return -EPERM;
 	}
 
 	if (mdfy_pdn->pdn_index > (IPA_MAX_PDN_NUM - 1)) {
-		IPAERR("pdn index out of range %d\n", mdfy_pdn->pdn_index);
+		IPAERR_RL("pdn index out of range %d\n", mdfy_pdn->pdn_index);
 		return -EPERM;
 	}
 
-	mutex_lock(&nat_ctx->lock);
+	mutex_lock(&nat_ctx->dev.lock);
 
 	/* store ip in pdn entries cache array */
 	pdn_entries[mdfy_pdn->pdn_index].public_ip =
@@ -683,12 +1030,10 @@
 	pdn_entries[mdfy_pdn->pdn_index].src_metadata =
 		mdfy_pdn->src_metadata;
 
-	IPADBG("Modify PDN in index %d: ", mdfy_pdn->pdn_index);
-	IPADBG("Public ip address:0x%x, ", mdfy_pdn->public_ip);
-	IPADBG("dst metadata:0x%x, ", mdfy_pdn->dst_metadata);
-	IPADBG("src metadata:0x%x\n", mdfy_pdn->src_metadata);
-
-	memset(&desc, 0, sizeof(desc));
+	IPADBG("Modify PDN in index: %d Public ip address:%pI4h\n",
+		mdfy_pdn->pdn_index, &mdfy_pdn->public_ip);
+	IPADBG("Modify PDN dst metadata: 0x%x src metadata: 0x%x\n",
+		mdfy_pdn->dst_metadata, mdfy_pdn->src_metadata);
 
 	/* Copy the PDN config table to SRAM */
 	mem_cmd.is_read = false;
@@ -706,25 +1051,200 @@
 		result = -ENOMEM;
 		goto bail;
 	}
-	desc.opcode = cmd_pyld->opcode;
-	desc.type = IPA_IMM_CMD_DESC;
-	desc.callback = NULL;
-	desc.user1 = NULL;
-	desc.user2 = 0;
-	desc.pyld = cmd_pyld->data;
-	desc.len = cmd_pyld->len;
+	ipa3_init_imm_cmd_desc(&desc, cmd_pyld);
 
-	IPADBG("sending PDN table copy cmd");
+	IPADBG("sending PDN table copy cmd\n");
 	if (ipa3_send_cmd(1, &desc)) {
 		IPAERR("Fail to send immediate command\n");
 		result = -EPERM;
 	}
 
 	ipahal_destroy_imm_cmd(cmd_pyld);
+
+	IPADBG("return\n");
+
 bail:
-	mutex_unlock(&nat_ctx->lock);
+	mutex_unlock(&nat_ctx->dev.lock);
 	return result;
 }
+
+static uint32_t ipa3_nat_ipv6ct_calculate_table_size(uint8_t base_addr)
+{
+	size_t entry_size;
+	u32 entries_num;
+	enum ipahal_nat_type nat_type;
+
+	switch (base_addr) {
+	case IPA_NAT_BASE_TBL:
+		entries_num = ipa3_ctx->nat_mem.dev.table_entries + 1;
+		nat_type = IPAHAL_NAT_IPV4;
+		break;
+	case IPA_NAT_EXPN_TBL:
+		entries_num = ipa3_ctx->nat_mem.dev.expn_table_entries;
+		nat_type = IPAHAL_NAT_IPV4;
+		break;
+	case IPA_NAT_INDX_TBL:
+		entries_num = ipa3_ctx->nat_mem.dev.table_entries + 1;
+		nat_type = IPAHAL_NAT_IPV4_INDEX;
+		break;
+	case IPA_NAT_INDEX_EXPN_TBL:
+		entries_num = ipa3_ctx->nat_mem.dev.expn_table_entries;
+		nat_type = IPAHAL_NAT_IPV4_INDEX;
+		break;
+	case IPA_IPV6CT_BASE_TBL:
+		entries_num = ipa3_ctx->ipv6ct_mem.dev.table_entries + 1;
+		nat_type = IPAHAL_NAT_IPV6CT;
+		break;
+	case IPA_IPV6CT_EXPN_TBL:
+		entries_num = ipa3_ctx->ipv6ct_mem.dev.expn_table_entries;
+		nat_type = IPAHAL_NAT_IPV6CT;
+		break;
+	default:
+		IPAERR_RL("Invalid base_addr %d for table DMA command\n",
+			base_addr);
+		return 0;
+	}
+
+	ipahal_nat_entry_size(nat_type, &entry_size);
+	return entry_size * entries_num;
+}
+
+static int ipa3_table_validate_table_dma_one(struct ipa_ioc_nat_dma_one *param)
+{
+	uint32_t table_size;
+
+	if (param->table_index >= 1) {
+		IPAERR_RL("Unsupported table index %d\n", param->table_index);
+		return -EPERM;
+	}
+
+	switch (param->base_addr) {
+	case IPA_NAT_BASE_TBL:
+	case IPA_NAT_EXPN_TBL:
+	case IPA_NAT_INDX_TBL:
+	case IPA_NAT_INDEX_EXPN_TBL:
+		if (!ipa3_ctx->nat_mem.dev.is_hw_init) {
+			IPAERR_RL("attempt to write to %s before HW int\n",
+				ipa3_ctx->nat_mem.dev.name);
+			return -EPERM;
+		}
+		break;
+	case IPA_IPV6CT_BASE_TBL:
+	case IPA_IPV6CT_EXPN_TBL:
+		if (ipa3_ctx->ipa_hw_type < IPA_HW_v4_0) {
+			IPAERR_RL("IPv6 connection tracking isn't supported\n");
+			return -EPERM;
+		}
+
+		if (!ipa3_ctx->ipv6ct_mem.dev.is_hw_init) {
+			IPAERR_RL("attempt to write to %s before HW int\n",
+				ipa3_ctx->ipv6ct_mem.dev.name);
+			return -EPERM;
+		}
+		break;
+	default:
+		IPAERR_RL("Invalid base_addr %d for table DMA command\n",
+			param->base_addr);
+		return -EPERM;
+	}
+
+	table_size = ipa3_nat_ipv6ct_calculate_table_size(param->base_addr);
+	if (!table_size) {
+		IPAERR_RL("Failed to calculate table size for base_addr %d\n",
+			param->base_addr);
+		return -EPERM;
+	}
+
+	if (param->offset >= table_size) {
+		IPAERR_RL("Invalid offset %d for table DMA command\n",
+			param->offset);
+		IPAERR_RL("table_index %d base addr %d size %d\n",
+			param->table_index, param->base_addr, table_size);
+		return -EPERM;
+	}
+
+	return 0;
+}
+
+
+/**
+ * ipa3_table_dma_cmd() - Post TABLE_DMA command to IPA HW
+ * @dma:	[in] initialization command attributes
+ *
+ * Called by NAT/IPv6CT clients to post TABLE_DMA command to IPA HW
+ *
+ * Returns:	0 on success, negative on failure
+ */
+int ipa3_table_dma_cmd(struct ipa_ioc_nat_dma_cmd *dma)
+{
+	struct ipahal_imm_cmd_table_dma cmd;
+	enum ipahal_imm_cmd_name cmd_name = IPA_IMM_CMD_NAT_DMA;
+	struct ipahal_imm_cmd_pyld *cmd_pyld[IPA_MAX_NUM_OF_TABLE_DMA_CMD_DESC];
+	struct ipa3_desc desc[IPA_MAX_NUM_OF_TABLE_DMA_CMD_DESC];
+	uint8_t cnt, num_cmd = 0;
+	int result = 0;
+
+	IPADBG("\n");
+	if (!dma->entries ||
+		dma->entries >= IPA_MAX_NUM_OF_TABLE_DMA_CMD_DESC) {
+		IPAERR_RL("Invalid number of entries %d\n",
+			dma->entries);
+		result = -EPERM;
+		goto bail;
+	}
+
+	for (cnt = 0; cnt < dma->entries; ++cnt) {
+		result = ipa3_table_validate_table_dma_one(&dma->dma[cnt]);
+		if (result) {
+			IPAERR_RL("Table DMA command parameter %d is invalid\n",
+				cnt);
+			goto bail;
+		}
+	}
+
+	/* NO-OP IC for ensuring that IPA pipeline is empty */
+	cmd_pyld[num_cmd] =
+		ipahal_construct_nop_imm_cmd(false, IPAHAL_HPS_CLEAR, false);
+	if (!cmd_pyld[num_cmd]) {
+		IPAERR("Failed to construct NOP imm cmd\n");
+		result = -ENOMEM;
+		goto destroy_imm_cmd;
+	}
+	ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
+	++num_cmd;
+
+	/* NAT_DMA was renamed to TABLE_DMA starting from IPAv4 */
+	if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0)
+		cmd_name = IPA_IMM_CMD_TABLE_DMA;
+
+	for (cnt = 0; cnt < dma->entries; ++cnt) {
+		cmd.table_index = dma->dma[cnt].table_index;
+		cmd.base_addr = dma->dma[cnt].base_addr;
+		cmd.offset = dma->dma[cnt].offset;
+		cmd.data = dma->dma[cnt].data;
+		cmd_pyld[num_cmd] =
+			ipahal_construct_imm_cmd(cmd_name, &cmd, false);
+		if (!cmd_pyld[num_cmd]) {
+			IPAERR_RL("Fail to construct table_dma imm cmd\n");
+			result = -ENOMEM;
+			goto destroy_imm_cmd;
+		}
+		ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
+		++num_cmd;
+	}
+	result = ipa3_send_cmd(num_cmd, desc);
+	if (result == -EPERM)
+		IPAERR("Fail to send table_dma immediate command\n");
+
+	IPADBG("return\n");
+
+destroy_imm_cmd:
+	for (cnt = 0; cnt < num_cmd; ++cnt)
+		ipahal_destroy_imm_cmd(cmd_pyld[cnt]);
+bail:
+	return result;
+}
+
 /**
  * ipa3_nat_dma_cmd() - Post NAT_DMA command to IPA HW
  * @dma:	[in] initialization command attributes
@@ -735,188 +1255,74 @@
  */
 int ipa3_nat_dma_cmd(struct ipa_ioc_nat_dma_cmd *dma)
 {
-#define NUM_OF_DESC 2
+	return ipa3_table_dma_cmd(dma);
+}
 
-	struct ipahal_imm_cmd_pyld *nop_cmd_pyld = NULL;
-	struct ipahal_imm_cmd_nat_dma cmd;
-	enum ipahal_imm_cmd_name cmd_name = IPA_IMM_CMD_NAT_DMA;
-	struct ipahal_imm_cmd_pyld *cmd_pyld = NULL;
-	struct ipa3_desc *desc = NULL;
-	u16 size = 0, cnt = 0;
-	int ret = 0;
-
+static void ipa3_nat_ipv6ct_free_mem(struct ipa3_nat_ipv6ct_common_mem *dev)
+{
 	IPADBG("\n");
-	if (dma->entries <= 0) {
-		IPAERR_RL("Invalid number of commands %d\n",
-			dma->entries);
-		ret = -EPERM;
-		goto bail;
+	if (!dev->is_mem_allocated) {
+		IPADBG("attempt to delete %s before memory allocation\n",
+			dev->name);
+		/* Deletion of partly initialized table is not an error */
+		goto clear;
 	}
 
-	for (cnt = 0; cnt < dma->entries; cnt++) {
-		if (dma->dma[cnt].table_index >= 1) {
-			IPAERR_RL("Invalid table index %d\n",
-				dma->dma[cnt].table_index);
-			ret = -EPERM;
-			goto bail;
-		}
-
-		switch (dma->dma[cnt].base_addr) {
-		case IPA_NAT_BASE_TBL:
-			if (dma->dma[cnt].offset >=
-				(ipa3_ctx->nat_mem.size_base_tables + 1) *
-				NAT_TABLE_ENTRY_SIZE_BYTE) {
-				IPAERR_RL("Invalid offset %d\n",
-					dma->dma[cnt].offset);
-				ret = -EPERM;
-				goto bail;
-			}
-
-			break;
-
-		case IPA_NAT_EXPN_TBL:
-			if (dma->dma[cnt].offset >=
-				ipa3_ctx->nat_mem.size_expansion_tables *
-				NAT_TABLE_ENTRY_SIZE_BYTE) {
-				IPAERR_RL("Invalid offset %d\n",
-					dma->dma[cnt].offset);
-				ret = -EPERM;
-				goto bail;
-			}
-
-			break;
-
-		case IPA_NAT_INDX_TBL:
-			if (dma->dma[cnt].offset >=
-				(ipa3_ctx->nat_mem.size_base_tables + 1) *
-				NAT_INTEX_TABLE_ENTRY_SIZE_BYTE) {
-				IPAERR_RL("Invalid offset %d\n",
-					dma->dma[cnt].offset);
-				ret = -EPERM;
-				goto bail;
-			}
-
-			break;
-
-		case IPA_NAT_INDEX_EXPN_TBL:
-			if (dma->dma[cnt].offset >=
-				ipa3_ctx->nat_mem.size_expansion_tables *
-				NAT_INTEX_TABLE_ENTRY_SIZE_BYTE) {
-				IPAERR_RL("Invalid offset %d\n",
-					dma->dma[cnt].offset);
-				ret = -EPERM;
-				goto bail;
-			}
-
-			break;
-
-		default:
-			IPAERR_RL("Invalid base_addr %d\n",
-				dma->dma[cnt].base_addr);
-			ret = -EPERM;
-			goto bail;
-		}
+	if (dev->is_sys_mem) {
+		IPADBG("freeing the dma memory for %s\n", dev->name);
+		dma_free_coherent(
+			ipa3_ctx->pdev, dev->size,
+			dev->vaddr, dev->dma_handle);
+		dev->size = 0;
+		dev->vaddr = NULL;
 	}
 
-	size = sizeof(struct ipa3_desc) * NUM_OF_DESC;
-	desc = kzalloc(size, GFP_KERNEL);
-	if (desc == NULL) {
-		IPAERR("Failed to alloc memory\n");
-		ret = -ENOMEM;
-		goto bail;
-	}
+	dev->is_mem_allocated = false;
 
-	/* NO-OP IC for ensuring that IPA pipeline is empty */
-	nop_cmd_pyld =
-		ipahal_construct_nop_imm_cmd(false, IPAHAL_HPS_CLEAR, false);
-	if (!nop_cmd_pyld) {
-		IPAERR("Failed to construct NOP imm cmd\n");
-		ret = -ENOMEM;
-		goto bail;
-	}
-	desc[0].type = IPA_IMM_CMD_DESC;
-	desc[0].opcode = nop_cmd_pyld->opcode;
-	desc[0].callback = NULL;
-	desc[0].user1 = NULL;
-	desc[0].user2 = 0;
-	desc[0].pyld = nop_cmd_pyld->data;
-	desc[0].len = nop_cmd_pyld->len;
+clear:
+	dev->table_entries = 0;
+	dev->expn_table_entries = 0;
+	dev->base_table_addr = NULL;
+	dev->expansion_table_addr = NULL;
 
-	/* NAT_DMA was renamed to TABLE_DMA starting from IPAv4 */
-	if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0)
-		cmd_name = IPA_IMM_CMD_TABLE_DMA;
+	dev->is_hw_init = false;
+	dev->is_mapped = false;
+	dev->is_sys_mem = false;
 
-	for (cnt = 0; cnt < dma->entries; cnt++) {
-		cmd.table_index = dma->dma[cnt].table_index;
-		cmd.base_addr = dma->dma[cnt].base_addr;
-		cmd.offset = dma->dma[cnt].offset;
-		cmd.data = dma->dma[cnt].data;
-		cmd_pyld = ipahal_construct_imm_cmd(cmd_name, &cmd, false);
-		if (!cmd_pyld) {
-			IPAERR_RL("Fail to construct nat_dma imm cmd\n");
-			continue;
-		}
-		desc[1].type = IPA_IMM_CMD_DESC;
-		desc[1].opcode = cmd_pyld->opcode;
-		desc[1].callback = NULL;
-		desc[1].user1 = NULL;
-		desc[1].user2 = 0;
-		desc[1].pyld = cmd_pyld->data;
-		desc[1].len = cmd_pyld->len;
-
-		ret = ipa3_send_cmd(NUM_OF_DESC, desc);
-		if (ret == -EPERM)
-			IPAERR("Fail to send immediate command %d\n", cnt);
-		ipahal_destroy_imm_cmd(cmd_pyld);
-	}
-
-bail:
-	if (desc != NULL)
-		kfree(desc);
-
-	if (nop_cmd_pyld != NULL)
-		ipahal_destroy_imm_cmd(nop_cmd_pyld);
-
-	return ret;
+	IPADBG("return\n");
 }
 
 /**
- * ipa3_nat_free_mem_and_device() - free the NAT memory and remove the device
- * @nat_ctx:	[in] the IPA NAT memory to free
+ * ipa3_nat_free_mem() - free the NAT memory
  *
- * Called by NAT client driver to free the NAT memory and remove the device
+ * Called by NAT client driver to free the NAT memory
  */
-void ipa3_nat_free_mem_and_device(struct ipa3_nat_mem *nat_ctx)
+static int ipa3_nat_free_mem(void)
 {
 	struct ipahal_imm_cmd_dma_shared_mem mem_cmd = { 0 };
 	struct ipa3_desc desc;
 	struct ipahal_imm_cmd_pyld *cmd_pyld;
+	int result = 0;
 
 	IPADBG("\n");
-	mutex_lock(&nat_ctx->lock);
+	mutex_lock(&ipa3_ctx->nat_mem.dev.lock);
 
-	if (nat_ctx->is_sys_mem) {
-		IPADBG("freeing the dma memory\n");
-		dma_free_coherent(
-			 ipa3_ctx->pdev, nat_ctx->size,
-			 nat_ctx->vaddr, nat_ctx->dma_handle);
-		nat_ctx->size = 0;
-		nat_ctx->vaddr = NULL;
-	}
+	ipa3_nat_ipv6ct_free_mem(&ipa3_ctx->nat_mem.dev);
 
 	if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0) {
-		struct ipa_pdn_entry *pdn_entries =
-			nat_ctx->pdn_mem.base;
+		size_t pdn_entry_size;
+
+		ipahal_nat_entry_size(IPAHAL_NAT_IPV4_PDN, &pdn_entry_size);
 
 		/* zero the PDN table and copy the PDN config table to SRAM */
 		IPADBG("zeroing the PDN config table\n");
-		memset(pdn_entries, 0, sizeof(struct ipa_pdn_entry) *
-			IPA_MAX_PDN_NUM);
+		memset(ipa3_ctx->nat_mem.pdn_mem.base, 0,
+			pdn_entry_size * IPA_MAX_PDN_NUM);
 		mem_cmd.is_read = false;
 		mem_cmd.skip_pipeline_clear = false;
 		mem_cmd.pipeline_clear_options = IPAHAL_HPS_CLEAR;
-		mem_cmd.size = sizeof(struct ipa_pdn_entry) * IPA_MAX_PDN_NUM;
-		mem_cmd.system_addr = nat_ctx->pdn_mem.phys_base;
+		mem_cmd.size = pdn_entry_size * IPA_MAX_PDN_NUM;
+		mem_cmd.system_addr = ipa3_ctx->nat_mem.pdn_mem.phys_base;
 		mem_cmd.local_addr = ipa3_ctx->smem_restricted_bytes +
 			IPA_MEM_PART(pdn_config_ofst);
 		cmd_pyld = ipahal_construct_imm_cmd(
@@ -924,32 +1330,97 @@
 		if (!cmd_pyld) {
 			IPAERR(
 				"fail construct dma_shared_mem cmd: for pdn table");
+			result = -ENOMEM;
 			goto lbl_free_pdn;
 		}
-		memset(&desc, 0, sizeof(desc));
-		desc.opcode = cmd_pyld->opcode;
-		desc.pyld = cmd_pyld->data;
-		desc.len = cmd_pyld->len;
-		desc.type = IPA_IMM_CMD_DESC;
+		ipa3_init_imm_cmd_desc(&desc, cmd_pyld);
 
 		IPADBG("sending PDN table copy cmd\n");
-		if (ipa3_send_cmd(1, &desc))
+		if (ipa3_send_cmd(1, &desc)) {
 			IPAERR("Fail to send immediate command\n");
+			result = -ENOMEM;
+		}
 
 		ipahal_destroy_imm_cmd(cmd_pyld);
 lbl_free_pdn:
 		IPADBG("freeing the PDN memory\n");
 		dma_free_coherent(ipa3_ctx->pdev,
-			nat_ctx->pdn_mem.size,
-			nat_ctx->pdn_mem.base,
-			nat_ctx->pdn_mem.phys_base);
+			ipa3_ctx->nat_mem.pdn_mem.size,
+			ipa3_ctx->nat_mem.pdn_mem.base,
+			ipa3_ctx->nat_mem.pdn_mem.phys_base);
 	}
-	nat_ctx->is_mapped = false;
-	nat_ctx->is_sys_mem = false;
-	nat_ctx->is_dev_init = false;
 
-	mutex_unlock(&nat_ctx->lock);
+	mutex_unlock(&ipa3_ctx->nat_mem.dev.lock);
 	IPADBG("return\n");
+	return result;
+}
+
+static int ipa3_nat_ipv6ct_send_del_table_cmd(
+	uint8_t tbl_index,
+	u32 base_addr,
+	bool mem_type_shared,
+	struct ipa3_nat_ipv6ct_common_mem *dev,
+	enum ipahal_imm_cmd_name cmd_name,
+	struct ipahal_imm_cmd_nat_ipv6ct_init_common *table_init_cmd,
+	const void *cmd)
+{
+	struct ipahal_imm_cmd_pyld *nop_cmd_pyld = NULL, *cmd_pyld = NULL;
+	struct ipa3_desc desc[IPA_MAX_NUM_OF_DEL_TABLE_CMD_DESC];
+	int result = 0;
+
+	IPADBG("\n");
+
+	if (!dev->is_hw_init) {
+		IPADBG("attempt to delete %s before HW int\n", dev->name);
+		/* Deletion of partly initialized table is not an error */
+		return 0;
+	}
+
+	if (tbl_index >= 1) {
+		IPAERR_RL("Unsupported table index %d\n", tbl_index);
+		return -EPERM;
+	}
+
+	/* NO-OP IC for ensuring that IPA pipeline is empty */
+	nop_cmd_pyld =
+		ipahal_construct_nop_imm_cmd(false, IPAHAL_HPS_CLEAR, false);
+	if (!nop_cmd_pyld) {
+		IPAERR("Failed to construct NOP imm cmd\n");
+		result = -ENOMEM;
+		goto bail;
+	}
+	ipa3_init_imm_cmd_desc(&desc[0], nop_cmd_pyld);
+
+	table_init_cmd->table_index = tbl_index;
+	table_init_cmd->base_table_addr = base_addr;
+	table_init_cmd->base_table_addr_shared = mem_type_shared;
+	table_init_cmd->expansion_table_addr = base_addr;
+	table_init_cmd->expansion_table_addr_shared = mem_type_shared;
+	table_init_cmd->size_base_table = 0;
+	table_init_cmd->size_expansion_table = 0;
+	cmd_pyld = ipahal_construct_imm_cmd(cmd_name, &cmd, false);
+	if (!cmd_pyld) {
+		IPAERR_RL("Fail to construct table init imm cmd for %s\n",
+			dev->name);
+		result = -EPERM;
+		goto destroy_nop_imm_cmd;
+	}
+	ipa3_init_imm_cmd_desc(&desc[1], cmd_pyld);
+
+	if (ipa3_send_cmd(IPA_MAX_NUM_OF_DEL_TABLE_CMD_DESC, desc)) {
+		IPAERR("Fail to send immediate command\n");
+		result = -EPERM;
+		goto destroy_imm_cmd;
+	}
+
+	IPADBG("return\n");
+
+destroy_imm_cmd:
+	ipahal_destroy_imm_cmd(cmd_pyld);
+destroy_nop_imm_cmd:
+	ipahal_destroy_imm_cmd(nop_cmd_pyld);
+bail:
+	return result;
 }
 
 /**
@@ -962,94 +1433,122 @@
  */
 int ipa3_nat_del_cmd(struct ipa_ioc_v4_nat_del *del)
 {
-	struct ipahal_imm_cmd_pyld *nop_cmd_pyld = NULL;
-	struct ipa3_desc desc[2];
+	struct ipa_ioc_nat_ipv6ct_table_del tmp;
+
+	if ((ipa3_ctx->ipa_hw_type < IPA_HW_v4_0) &&
+		(del->public_ip_addr == 0)) {
+		IPAERR_RL("Bad Parameter public IP address\n");
+		return -EPERM;
+	}
+
+	tmp.table_index = del->table_index;
+
+	return ipa3_del_nat_table(&tmp);
+}
+
+/**
+ * ipa3_del_nat_table() - Delete the NAT table
+ * @del:	[in] delete table parameters
+ *
+ * Called by NAT client to delete the table
+ *
+ * Returns:	0 on success, negative on failure
+ */
+int ipa3_del_nat_table(struct ipa_ioc_nat_ipv6ct_table_del *del)
+{
 	struct ipahal_imm_cmd_ip_v4_nat_init cmd;
-	struct ipahal_imm_cmd_pyld *cmd_pyld;
 	bool mem_type_shared = true;
 	u32 base_addr = IPA_NAT_PHYS_MEM_OFFSET;
 	int result;
 
 	IPADBG("\n");
-	if (ipa3_ctx->nat_mem.is_tmp_mem) {
-		IPAERR("using temp memory during nat del\n");
+	if (!ipa3_ctx->nat_mem.dev.is_dev_init) {
+		IPAERR("NAT hasn't been initialized\n");
+		return -EPERM;
+	}
+
+	if (ipa3_ctx->nat_mem.dev.tmp_mem != NULL) {
+		IPADBG("using temp memory during nat del\n");
 		mem_type_shared = false;
-		base_addr = ipa3_ctx->nat_mem.tmp_dma_handle;
+		base_addr = ipa3_ctx->nat_mem.dev.tmp_mem->dma_handle;
 	}
 
-	if ((ipa3_ctx->ipa_hw_type < IPA_HW_v4_0) &&
-		(del->public_ip_addr == 0)) {
-		IPADBG("Bad Parameter\n");
-		result = -EPERM;
-		goto bail;
-	}
-
-	memset(&desc, 0, sizeof(desc));
-	/* NO-OP IC for ensuring that IPA pipeline is empty */
-	nop_cmd_pyld =
-		ipahal_construct_nop_imm_cmd(false, IPAHAL_HPS_CLEAR, false);
-	if (!nop_cmd_pyld) {
-		IPAERR("Failed to construct NOP imm cmd\n");
-		result = -ENOMEM;
-		goto bail;
-	}
-	desc[0].opcode = nop_cmd_pyld->opcode;
-	desc[0].type = IPA_IMM_CMD_DESC;
-	desc[0].callback = NULL;
-	desc[0].user1 = NULL;
-	desc[0].user2 = 0;
-	desc[0].pyld = nop_cmd_pyld->data;
-	desc[0].len = nop_cmd_pyld->len;
-
-	cmd.table_index = del->table_index;
-	cmd.ipv4_rules_addr = base_addr;
-	cmd.ipv4_rules_addr_shared = mem_type_shared;
-	cmd.ipv4_expansion_rules_addr = base_addr;
-	cmd.ipv4_expansion_rules_addr_shared = mem_type_shared;
 	cmd.index_table_addr = base_addr;
 	cmd.index_table_addr_shared = mem_type_shared;
 	cmd.index_table_expansion_addr = base_addr;
 	cmd.index_table_expansion_addr_shared = mem_type_shared;
-	cmd.size_base_tables = 0;
-	cmd.size_expansion_tables = 0;
-	cmd.public_ip_addr = 0;
-	cmd_pyld = ipahal_construct_imm_cmd(
-		IPA_IMM_CMD_IP_V4_NAT_INIT, &cmd, false);
-	if (!cmd_pyld) {
-		IPAERR_RL("Fail to construct ip_v4_nat_init imm cmd\n");
-		result = -EPERM;
-		goto destroy_regwrt_imm_cmd;
-	}
-	desc[1].opcode = cmd_pyld->opcode;
-	desc[1].type = IPA_IMM_CMD_DESC;
-	desc[1].callback = NULL;
-	desc[1].user1 = NULL;
-	desc[1].user2 = 0;
-	desc[1].pyld = cmd_pyld->data;
-	desc[1].len = cmd_pyld->len;
+	cmd.public_addr_info = 0;
 
-	if (ipa3_send_cmd(2, desc)) {
-		IPAERR("Fail to send immediate command\n");
-		result = -EPERM;
-		goto destroy_imm_cmd;
-	}
+	result = ipa3_nat_ipv6ct_send_del_table_cmd(
+		del->table_index,
+		base_addr,
+		mem_type_shared,
+		&ipa3_ctx->nat_mem.dev,
+		IPA_IMM_CMD_IP_V4_NAT_INIT,
+		&cmd.table_init,
+		&cmd);
+	if (result)
+		goto bail;
 
-	ipa3_ctx->nat_mem.size_base_tables = 0;
-	ipa3_ctx->nat_mem.size_expansion_tables = 0;
 	ipa3_ctx->nat_mem.public_ip_addr = 0;
-	ipa3_ctx->nat_mem.ipv4_rules_addr = 0;
-	ipa3_ctx->nat_mem.ipv4_expansion_rules_addr = 0;
 	ipa3_ctx->nat_mem.index_table_addr = 0;
 	ipa3_ctx->nat_mem.index_table_expansion_addr = 0;
 
-	ipa3_nat_free_mem_and_device(&ipa3_ctx->nat_mem);
+	result = ipa3_nat_free_mem();
 	IPADBG("return\n");
-	result = 0;
-
-destroy_imm_cmd:
-	ipahal_destroy_imm_cmd(cmd_pyld);
-destroy_regwrt_imm_cmd:
-	ipahal_destroy_imm_cmd(nop_cmd_pyld);
 bail:
 	return result;
 }
+
+/**
+ * ipa3_del_ipv6ct_table() - Delete the IPv6CT table
+ * @del:	[in] delete table parameters
+ *
+ * Called by IPv6CT client to delete the table
+ *
+ * Returns:	0 on success, negative on failure
+ */
+int ipa3_del_ipv6ct_table(struct ipa_ioc_nat_ipv6ct_table_del *del)
+{
+	struct ipahal_imm_cmd_ip_v6_ct_init cmd;
+	bool mem_type_shared = true;
+	u32 base_addr = IPA_IPV6CT_PHYS_MEM_OFFSET;
+	int result;
+
+	IPADBG("\n");
+
+	if (ipa3_ctx->ipa_hw_type < IPA_HW_v4_0) {
+		IPAERR_RL("IPv6 connection tracking isn't supported\n");
+		return -EPERM;
+	}
+
+	if (!ipa3_ctx->ipv6ct_mem.dev.is_dev_init) {
+		IPAERR("IPv6 connection tracking hasn't been initialized\n");
+		return -EPERM;
+	}
+
+	if (ipa3_ctx->ipv6ct_mem.dev.tmp_mem != NULL) {
+		IPADBG("using temp memory during IPv6CT del\n");
+		mem_type_shared = false;
+		base_addr = ipa3_ctx->ipv6ct_mem.dev.tmp_mem->dma_handle;
+	}
+
+	result = ipa3_nat_ipv6ct_send_del_table_cmd(
+		del->table_index,
+		base_addr,
+		mem_type_shared,
+		&ipa3_ctx->ipv6ct_mem.dev,
+		IPA_IMM_CMD_IP_V6_CT_INIT,
+		&cmd.table_init,
+		&cmd);
+	if (result)
+		goto bail;
+
+	mutex_lock(&ipa3_ctx->ipv6ct_mem.dev.lock);
+	ipa3_nat_ipv6ct_free_mem(&ipa3_ctx->ipv6ct_mem.dev);
+	mutex_unlock(&ipa3_ctx->ipv6ct_mem.dev.lock);
+	IPADBG("return\n");
+bail:
+	return result;
+}
+
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c
index 8d7b107..edba283 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c
@@ -21,6 +21,8 @@
 #define IPA_RT_STATUS_OF_DEL_FAILED	(-1)
 #define IPA_RT_STATUS_OF_MDFY_FAILED (-1)
 
+#define IPA_RT_MAX_NUM_OF_COMMIT_TABLES_CMD_DESC 5
+
 #define IPA_RT_GET_RULE_TYPE(__entry) \
 	( \
 	((__entry)->rule.hashable) ? \
@@ -432,10 +434,11 @@
  */
 int __ipa_commit_rt_v3(enum ipa_ip_type ip)
 {
-	struct ipa3_desc desc[5];
+	struct ipa3_desc desc[IPA_RT_MAX_NUM_OF_COMMIT_TABLES_CMD_DESC];
 	struct ipahal_imm_cmd_register_write reg_write_cmd = {0};
 	struct ipahal_imm_cmd_dma_shared_mem  mem_cmd = {0};
-	struct ipahal_imm_cmd_pyld *cmd_pyld[5];
+	struct ipahal_imm_cmd_pyld
+		*cmd_pyld[IPA_RT_MAX_NUM_OF_COMMIT_TABLES_CMD_DESC];
 	int num_cmd = 0;
 	struct ipahal_fltrt_alloc_imgs_params alloc_params;
 	u32 num_modem_rt_index;
@@ -557,10 +560,7 @@
 		IPAERR("fail construct register_write imm cmd. IP %d\n", ip);
 		goto fail_size_valid;
 	}
-	desc[num_cmd].opcode = cmd_pyld[num_cmd]->opcode;
-	desc[num_cmd].pyld = cmd_pyld[num_cmd]->data;
-	desc[num_cmd].len = cmd_pyld[num_cmd]->len;
-	desc[num_cmd].type = IPA_IMM_CMD_DESC;
+	ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
 	num_cmd++;
 
 	mem_cmd.is_read = false;
@@ -575,10 +575,7 @@
 		IPAERR("fail construct dma_shared_mem imm cmd. IP %d\n", ip);
 		goto fail_imm_cmd_construct;
 	}
-	desc[num_cmd].opcode = cmd_pyld[num_cmd]->opcode;
-	desc[num_cmd].pyld = cmd_pyld[num_cmd]->data;
-	desc[num_cmd].len = cmd_pyld[num_cmd]->len;
-	desc[num_cmd].type = IPA_IMM_CMD_DESC;
+	ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
 	num_cmd++;
 
 	mem_cmd.is_read = false;
@@ -593,13 +590,17 @@
 		IPAERR("fail construct dma_shared_mem imm cmd. IP %d\n", ip);
 		goto fail_imm_cmd_construct;
 	}
-	desc[num_cmd].opcode = cmd_pyld[num_cmd]->opcode;
-	desc[num_cmd].pyld = cmd_pyld[num_cmd]->data;
-	desc[num_cmd].len = cmd_pyld[num_cmd]->len;
-	desc[num_cmd].type = IPA_IMM_CMD_DESC;
+	ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
 	num_cmd++;
 
 	if (lcl_nhash) {
+		if (num_cmd >= IPA_RT_MAX_NUM_OF_COMMIT_TABLES_CMD_DESC) {
+			IPAERR("number of commands is out of range: IP = %d\n",
+				ip);
+			rc = -ENOBUFS;
+			goto fail_imm_cmd_construct;
+		}
+
 		mem_cmd.is_read = false;
 		mem_cmd.skip_pipeline_clear = false;
 		mem_cmd.pipeline_clear_options = IPAHAL_HPS_CLEAR;
@@ -613,13 +614,17 @@
 				ip);
 			goto fail_imm_cmd_construct;
 		}
-		desc[num_cmd].opcode = cmd_pyld[num_cmd]->opcode;
-		desc[num_cmd].pyld = cmd_pyld[num_cmd]->data;
-		desc[num_cmd].len = cmd_pyld[num_cmd]->len;
-		desc[num_cmd].type = IPA_IMM_CMD_DESC;
+		ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
 		num_cmd++;
 	}
 	if (lcl_hash) {
+		if (num_cmd >= IPA_RT_MAX_NUM_OF_COMMIT_TABLES_CMD_DESC) {
+			IPAERR("number of commands is out of range: IP = %d\n",
+				ip);
+			rc = -ENOBUFS;
+			goto fail_imm_cmd_construct;
+		}
+
 		mem_cmd.is_read = false;
 		mem_cmd.skip_pipeline_clear = false;
 		mem_cmd.pipeline_clear_options = IPAHAL_HPS_CLEAR;
@@ -633,10 +638,7 @@
 				ip);
 			goto fail_imm_cmd_construct;
 		}
-		desc[num_cmd].opcode = cmd_pyld[num_cmd]->opcode;
-		desc[num_cmd].pyld = cmd_pyld[num_cmd]->data;
-		desc[num_cmd].len = cmd_pyld[num_cmd]->len;
-		desc[num_cmd].type = IPA_IMM_CMD_DESC;
+		ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
 		num_cmd++;
 	}
 
@@ -1137,8 +1139,8 @@
 		goto bail;
 	}
 
-	if (tbl->rule_cnt <= 0) {
-		IPAERR_RL("tbl->rule_cnt <= 0");
+	if (!tbl->rule_cnt) {
+		IPAERR_RL("tbl->rule_cnt == 0");
 		ret = -EINVAL;
 		goto bail;
 	}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
index fd38a2d..979369a 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
@@ -2787,7 +2787,7 @@
 		ep_hdr->hdr_ofst_pkt_size_valid,
 		ep_hdr->hdr_additional_const_len);
 
-	IPADBG("ofst_metadata=0x%x, ofst_metadata_valid=%d, len=0x%x",
+	IPADBG("ofst_metadata=0x%x, ofst_metadata_valid=%d, len=0x%x\n",
 		ep_hdr->hdr_ofst_metadata,
 		ep_hdr->hdr_ofst_metadata_valid,
 		ep_hdr->hdr_len);
@@ -2946,7 +2946,7 @@
 	if (!IPA_CLIENT_IS_CONS(ep_mode->dst))
 		ep = ipa3_get_ep_mapping(IPA_CLIENT_APPS_LAN_CONS);
 
-	IPADBG("pipe=%d mode=%d(%s), dst_client_number=%d",
+	IPADBG("pipe=%d mode=%d(%s), dst_client_number=%d\n",
 			clnt_hdl,
 			ep_mode->mode,
 			ipa3_get_mode_type_str(ep_mode->mode),
@@ -3908,13 +3908,10 @@
 		res = -ENOMEM;
 		goto fail_free_tag_desc;
 	}
-	tag_desc[desc_idx].opcode = cmd_pyld->opcode;
-	tag_desc[desc_idx].pyld = cmd_pyld->data;
-	tag_desc[desc_idx].len = cmd_pyld->len;
-	tag_desc[desc_idx].type = IPA_IMM_CMD_DESC;
+	ipa3_init_imm_cmd_desc(&tag_desc[desc_idx], cmd_pyld);
 	tag_desc[desc_idx].callback = ipa3_tag_destroy_imm;
 	tag_desc[desc_idx].user1 = cmd_pyld;
-	desc_idx++;
+	++desc_idx;
 
 	/* IP_PACKET_INIT IC for tag status to be sent to apps */
 	pktinit_cmd.destination_pipe_index =
@@ -3926,13 +3923,10 @@
 		res = -ENOMEM;
 		goto fail_free_desc;
 	}
-	tag_desc[desc_idx].opcode = cmd_pyld->opcode;
-	tag_desc[desc_idx].pyld = cmd_pyld->data;
-	tag_desc[desc_idx].len = cmd_pyld->len;
-	tag_desc[desc_idx].type = IPA_IMM_CMD_DESC;
+	ipa3_init_imm_cmd_desc(&tag_desc[desc_idx], cmd_pyld);
 	tag_desc[desc_idx].callback = ipa3_tag_destroy_imm;
 	tag_desc[desc_idx].user1 = cmd_pyld;
-	desc_idx++;
+	++desc_idx;
 
 	/* status IC */
 	status.tag = IPA_COOKIE;
@@ -3943,13 +3937,10 @@
 		res = -ENOMEM;
 		goto fail_free_desc;
 	}
-	tag_desc[desc_idx].opcode = cmd_pyld->opcode;
-	tag_desc[desc_idx].pyld = cmd_pyld->data;
-	tag_desc[desc_idx].len = cmd_pyld->len;
-	tag_desc[desc_idx].type = IPA_IMM_CMD_DESC;
+	ipa3_init_imm_cmd_desc(&tag_desc[desc_idx], cmd_pyld);
 	tag_desc[desc_idx].callback = ipa3_tag_destroy_imm;
 	tag_desc[desc_idx].user1 = cmd_pyld;
-	desc_idx++;
+	++desc_idx;
 
 	comp = kzalloc(sizeof(*comp), GFP_KERNEL);
 	if (!comp) {
@@ -3972,6 +3963,12 @@
 
 	memcpy(skb_put(dummy_skb, sizeof(comp)), &comp, sizeof(comp));
 
+	if (desc_idx >= IPA_TAG_MAX_DESC) {
+		IPAERR("number of commands is out of range\n");
+		res = -ENOBUFS;
+		goto fail_free_skb;
+	}
+
 	tag_desc[desc_idx].pyld = dummy_skb->data;
 	tag_desc[desc_idx].len = dummy_skb->len;
 	tag_desc[desc_idx].type = IPA_DATA_DESC_SKB;
@@ -3984,7 +3981,7 @@
 	if (res) {
 		IPAERR("failed to send TAG packets %d\n", res);
 		res = -ENOMEM;
-		goto fail_free_comp;
+		goto fail_free_skb;
 	}
 	kfree(tag_desc);
 	tag_desc = NULL;
@@ -4012,6 +4009,8 @@
 
 	return 0;
 
+fail_free_skb:
+	kfree_skb(dummy_skb);
 fail_free_comp:
 	kfree(comp);
 fail_free_desc:
@@ -4082,19 +4081,16 @@
 			goto fail_alloc_reg_write_agg_close;
 		}
 
-		desc[desc_idx].opcode = cmd_pyld->opcode;
-		desc[desc_idx].pyld = cmd_pyld->data;
-		desc[desc_idx].len = cmd_pyld->len;
-		desc[desc_idx].type = IPA_IMM_CMD_DESC;
+		ipa3_init_imm_cmd_desc(&desc[desc_idx], cmd_pyld);
 		desc[desc_idx].callback = ipa3_tag_destroy_imm;
 		desc[desc_idx].user1 = cmd_pyld;
-		desc_idx++;
+		++desc_idx;
 	}
 
 	return desc_idx;
 
 fail_alloc_reg_write_agg_close:
-	for (i = 0; i < desc_idx; i++)
+	for (i = 0; i < desc_idx; ++i)
 		if (desc[desc_idx].callback)
 			desc[desc_idx].callback(desc[desc_idx].user1,
 				desc[desc_idx].user2);
@@ -4377,10 +4373,17 @@
 	api_ctrl->ipa_mdfy_flt_rule = ipa3_mdfy_flt_rule;
 	api_ctrl->ipa_commit_flt = ipa3_commit_flt;
 	api_ctrl->ipa_reset_flt = ipa3_reset_flt;
-	api_ctrl->allocate_nat_device = ipa3_allocate_nat_device;
+	api_ctrl->ipa_allocate_nat_device = ipa3_allocate_nat_device;
+	api_ctrl->ipa_allocate_nat_table = ipa3_allocate_nat_table;
+	api_ctrl->ipa_allocate_ipv6ct_table = ipa3_allocate_ipv6ct_table;
 	api_ctrl->ipa_nat_init_cmd = ipa3_nat_init_cmd;
+	api_ctrl->ipa_ipv6ct_init_cmd = ipa3_ipv6ct_init_cmd;
 	api_ctrl->ipa_nat_dma_cmd = ipa3_nat_dma_cmd;
+	api_ctrl->ipa_table_dma_cmd = ipa3_table_dma_cmd;
 	api_ctrl->ipa_nat_del_cmd = ipa3_nat_del_cmd;
+	api_ctrl->ipa_del_nat_table = ipa3_del_nat_table;
+	api_ctrl->ipa_del_ipv6ct_table = ipa3_del_ipv6ct_table;
+	api_ctrl->ipa_nat_mdfy_pdn = ipa3_nat_mdfy_pdn;
 	api_ctrl->ipa_send_msg = ipa3_send_msg;
 	api_ctrl->ipa_register_pull_msg = ipa3_register_pull_msg;
 	api_ctrl->ipa_deregister_pull_msg = ipa3_deregister_pull_msg;
@@ -4997,12 +5000,9 @@
  */
 int ipa3_inject_dma_task_for_gsi(void)
 {
-	struct ipa3_desc desc = {0};
+	struct ipa3_desc desc;
 
-	desc.opcode = ipa3_ctx->dma_task_info.cmd_pyld->opcode;
-	desc.pyld = ipa3_ctx->dma_task_info.cmd_pyld->data;
-	desc.len = ipa3_ctx->dma_task_info.cmd_pyld->len;
-	desc.type = IPA_IMM_CMD_DESC;
+	ipa3_init_imm_cmd_desc(&desc, ipa3_ctx->dma_task_info.cmd_pyld);
 
 	IPADBG("sending 1B packet to IPA\n");
 	if (ipa3_send_cmd_timeout(1, &desc,
@@ -5325,3 +5325,14 @@
 	ipahal_write_reg_fields(IPA_IDLE_INDICATION_CFG,
 			&idle_indication_cfg);
 }
+
+void ipa3_init_imm_cmd_desc(struct ipa3_desc *desc,
+	struct ipahal_imm_cmd_pyld *cmd_pyld)
+{
+	memset(desc, 0, sizeof(*desc));
+	desc->opcode = cmd_pyld->opcode;
+	desc->pyld = cmd_pyld->data;
+	desc->len = cmd_pyld->len;
+	desc->type = IPA_IMM_CMD_DESC;
+}
+
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/Makefile b/drivers/platform/msm/ipa/ipa_v3/ipahal/Makefile
index 67e491b..869ee7e 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/Makefile
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/Makefile
@@ -1,3 +1,3 @@
 obj-$(CONFIG_IPA3) += ipa_hal.o
 
-ipa_hal-y := ipahal.o ipahal_reg.o ipahal_fltrt.o ipahal_hw_stats.o
+ipa_hal-y := ipahal.o ipahal_reg.o ipahal_fltrt.o ipahal_hw_stats.o ipahal_nat.o
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.c b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.c
index a8d5342..d015b22 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.c
@@ -16,7 +16,7 @@
 #include "ipahal_reg_i.h"
 #include "ipahal_fltrt_i.h"
 #include "ipahal_hw_stats_i.h"
-
+#include "ipahal_nat_i.h"
 
 struct ipahal_context *ipahal_ctx;
 
@@ -35,6 +35,7 @@
 	__stringify(IPA_IMM_CMD_IP_PACKET_TAG_STATUS),
 	__stringify(IPA_IMM_CMD_DMA_TASK_32B_ADDR),
 	__stringify(IPA_IMM_CMD_TABLE_DMA),
+	__stringify(IPA_IMM_CMD_IP_V6_CT_INIT)
 };
 
 static const char *ipahal_pkt_status_exception_to_str
@@ -352,8 +353,8 @@
 {
 	struct ipahal_imm_cmd_pyld *pyld;
 	struct ipa_imm_cmd_hw_nat_dma *data;
-	struct ipahal_imm_cmd_nat_dma *nat_params =
-		(struct ipahal_imm_cmd_nat_dma *)params;
+	struct ipahal_imm_cmd_table_dma *nat_params =
+		(struct ipahal_imm_cmd_table_dma *)params;
 
 	pyld = IPAHAL_MEM_ALLOC(sizeof(*pyld) + sizeof(*data), is_atomic_ctx);
 	if (unlikely(!pyld)) {
@@ -519,24 +520,55 @@
 	pyld->len = sizeof(*data);
 	data = (struct ipa_imm_cmd_hw_ip_v4_nat_init *)pyld->data;
 
-	data->ipv4_rules_addr = nat4_params->ipv4_rules_addr;
+	data->ipv4_rules_addr = nat4_params->table_init.base_table_addr;
 	data->ipv4_expansion_rules_addr =
-		nat4_params->ipv4_expansion_rules_addr;
+		nat4_params->table_init.expansion_table_addr;
 	data->index_table_addr = nat4_params->index_table_addr;
 	data->index_table_expansion_addr =
 		nat4_params->index_table_expansion_addr;
-	data->table_index = nat4_params->table_index;
+	data->table_index = nat4_params->table_init.table_index;
 	data->ipv4_rules_addr_type =
-		nat4_params->ipv4_rules_addr_shared ? 1 : 0;
+		nat4_params->table_init.base_table_addr_shared ? 1 : 0;
 	data->ipv4_expansion_rules_addr_type =
-		nat4_params->ipv4_expansion_rules_addr_shared ? 1 : 0;
+		nat4_params->table_init.expansion_table_addr_shared ? 1 : 0;
 	data->index_table_addr_type =
 		nat4_params->index_table_addr_shared ? 1 : 0;
 	data->index_table_expansion_addr_type =
 		nat4_params->index_table_expansion_addr_shared ? 1 : 0;
-	data->size_base_tables = nat4_params->size_base_tables;
-	data->size_expansion_tables = nat4_params->size_expansion_tables;
-	data->public_ip_addr = nat4_params->public_ip_addr;
+	data->size_base_tables = nat4_params->table_init.size_base_table;
+	data->size_expansion_tables =
+		nat4_params->table_init.size_expansion_table;
+	data->public_addr_info = nat4_params->public_addr_info;
+
+	return pyld;
+}
+
+static struct ipahal_imm_cmd_pyld *ipa_imm_cmd_construct_ip_v6_ct_init(
+	enum ipahal_imm_cmd_name cmd, const void *params, bool is_atomic_ctx)
+{
+	struct ipahal_imm_cmd_pyld *pyld;
+	struct ipa_imm_cmd_hw_ip_v6_ct_init *data;
+	struct ipahal_imm_cmd_ip_v6_ct_init *ipv6ct_params =
+		(struct ipahal_imm_cmd_ip_v6_ct_init *)params;
+
+	pyld = IPAHAL_MEM_ALLOC(sizeof(*pyld) + sizeof(*data), is_atomic_ctx);
+	if (unlikely(!pyld))
+		return pyld;
+	pyld->opcode = ipahal_imm_cmd_get_opcode(cmd);
+	pyld->len = sizeof(*data);
+	data = (struct ipa_imm_cmd_hw_ip_v6_ct_init *)pyld->data;
+
+	data->table_addr = ipv6ct_params->table_init.base_table_addr;
+	data->expansion_table_addr =
+		ipv6ct_params->table_init.expansion_table_addr;
+	data->table_index = ipv6ct_params->table_init.table_index;
+	data->table_addr_type =
+		ipv6ct_params->table_init.base_table_addr_shared ? 1 : 0;
+	data->expansion_table_addr_type =
+		ipv6ct_params->table_init.expansion_table_addr_shared ? 1 : 0;
+	data->size_base_table = ipv6ct_params->table_init.size_base_table;
+	data->size_expansion_table =
+		ipv6ct_params->table_init.size_expansion_table;
 
 	return pyld;
 }
@@ -685,6 +717,9 @@
 	[IPA_HW_v4_0][IPA_IMM_CMD_DMA_SHARED_MEM] = {
 		ipa_imm_cmd_construct_dma_shared_mem_v_4_0,
 		19},
+	[IPA_HW_v4_0][IPA_IMM_CMD_IP_V6_CT_INIT] = {
+		ipa_imm_cmd_construct_ip_v6_ct_init,
+		23}
 };
 
 /*
@@ -1526,13 +1561,21 @@
 	if (ipahal_hw_stats_init(ipa_hw_type)) {
 		IPAHAL_ERR("failed to init ipahal hw stats\n");
 		result = -EFAULT;
-		goto bail_free_ctx;
+		goto bail_free_fltrt;
+	}
+
+	if (ipahal_nat_init(ipa_hw_type)) {
+		IPAHAL_ERR("failed to init ipahal NAT\n");
+		result = -EFAULT;
+		goto bail_free_fltrt;
 	}
 
 	ipahal_debugfs_init();
 
 	return 0;
 
+bail_free_fltrt:
+	ipahal_fltrt_destroy();
 bail_free_ctx:
 	kfree(ipahal_ctx);
 	ipahal_ctx = NULL;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.h b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.h
index 56b884b..0c2697c 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.h
@@ -37,6 +37,7 @@
 	IPA_IMM_CMD_IP_PACKET_TAG_STATUS,
 	IPA_IMM_CMD_DMA_TASK_32B_ADDR,
 	IPA_IMM_CMD_TABLE_DMA,
+	IPA_IMM_CMD_IP_V6_CT_INIT,
 	IPA_IMM_CMD_MAX,
 };
 
@@ -46,19 +47,19 @@
  * struct ipahal_imm_cmd_ip_v4_filter_init - IP_V4_FILTER_INIT cmd payload
  * Inits IPv4 filter block.
  * @hash_rules_addr: Addr in sys mem where ipv4 hashable flt tbl starts
+ * @nhash_rules_addr: Addr in sys mem where ipv4 non-hashable flt tbl starts
  * @hash_rules_size: Size in bytes of the hashable tbl to cpy to local mem
  * @hash_local_addr: Addr in shared mem where ipv4 hashable flt tbl should
  *  be copied to
- * @nhash_rules_addr: Addr in sys mem where ipv4 non-hashable flt tbl starts
  * @nhash_rules_size: Size in bytes of the non-hashable tbl to cpy to local mem
  * @nhash_local_addr: Addr in shared mem where ipv4 non-hashable flt tbl should
  *  be copied to
  */
 struct ipahal_imm_cmd_ip_v4_filter_init {
 	u64 hash_rules_addr;
+	u64 nhash_rules_addr;
 	u32 hash_rules_size;
 	u32 hash_local_addr;
-	u64 nhash_rules_addr;
 	u32 nhash_rules_size;
 	u32 nhash_local_addr;
 };
@@ -67,79 +68,98 @@
  * struct ipahal_imm_cmd_ip_v6_filter_init - IP_V6_FILTER_INIT cmd payload
  * Inits IPv6 filter block.
  * @hash_rules_addr: Addr in sys mem where ipv6 hashable flt tbl starts
+ * @nhash_rules_addr: Addr in sys mem where ipv6 non-hashable flt tbl starts
  * @hash_rules_size: Size in bytes of the hashable tbl to cpy to local mem
  * @hash_local_addr: Addr in shared mem where ipv6 hashable flt tbl should
  *  be copied to
- * @nhash_rules_addr: Addr in sys mem where ipv6 non-hashable flt tbl starts
  * @nhash_rules_size: Size in bytes of the non-hashable tbl to cpy to local mem
  * @nhash_local_addr: Addr in shared mem where ipv6 non-hashable flt tbl should
  *  be copied to
  */
 struct ipahal_imm_cmd_ip_v6_filter_init {
 	u64 hash_rules_addr;
+	u64 nhash_rules_addr;
 	u32 hash_rules_size;
 	u32 hash_local_addr;
-	u64 nhash_rules_addr;
 	u32 nhash_rules_size;
 	u32 nhash_local_addr;
 };
 
 /*
+ * struct ipahal_imm_cmd_nat_ipv6ct_init_common - NAT/IPv6CT table init command
+ *                                                common part
+ * @base_table_addr: Address in sys/shared mem where base table start
+ * @expansion_table_addr: Address in sys/shared mem where expansion table
+ *  starts. Entries that result in hash collision are located in this table.
+ * @base_table_addr_shared: base_table_addr in shared mem (if not, then sys)
+ * @expansion_table_addr_shared: expansion_rules_addr in
+ *  shared mem (if not, then sys)
+ * @size_base_table: Num of entries in the base table
+ * @size_expansion_table: Num of entries in the expansion table
+ * @table_index: For future support of multiple tables
+ */
+struct ipahal_imm_cmd_nat_ipv6ct_init_common {
+	u64 base_table_addr;
+	u64 expansion_table_addr;
+	bool base_table_addr_shared;
+	bool expansion_table_addr_shared;
+	u16 size_base_table;
+	u16 size_expansion_table;
+	u8 table_index;
+};
+
+/*
  * struct ipahal_imm_cmd_ip_v4_nat_init - IP_V4_NAT_INIT cmd payload
  * Inits IPv4 NAT block. Initiate NAT table with it dimensions, location
- *  cache address abd itger related parameters.
- * @table_index: For future support of multiple NAT tables
- * @ipv4_rules_addr: Addr in sys/shared mem where ipv4 NAT rules start
- * @ipv4_rules_addr_shared: ipv4_rules_addr in shared mem (if not, then sys)
- * @ipv4_expansion_rules_addr: Addr in sys/shared mem where expantion NAT
- *  table starts. IPv4 NAT rules that result in NAT collision are located
- *  in this table.
- * @ipv4_expansion_rules_addr_shared: ipv4_expansion_rules_addr in
- *  shared mem (if not, then sys)
+ *  cache address and other related parameters.
+ * @table_init: table initialization parameters
  * @index_table_addr: Addr in sys/shared mem where index table, which points
  *  to NAT table starts
- * @index_table_addr_shared: index_table_addr in shared mem (if not, then sys)
  * @index_table_expansion_addr: Addr in sys/shared mem where expansion index
  *  table starts
+ * @index_table_addr_shared: index_table_addr in shared mem (if not, then sys)
  * @index_table_expansion_addr_shared: index_table_expansion_addr in
  *  shared mem (if not, then sys)
- * @size_base_tables: Num of entries in NAT tbl and idx tbl (each)
- * @size_expansion_tables: Num of entries in NAT expantion tbl and expantion
- *  idx tbl (each)
- * @public_ip_addr: public IP address
+ * @public_addr_info: Public IP addresses info suitable to the IPA H/W version
+ *                    IPA H/W >= 4.0 - PDN config table offset in SMEM
+ *                    IPA H/W < 4.0  - The public IP address
  */
 struct ipahal_imm_cmd_ip_v4_nat_init {
-	u8 table_index;
-	u64 ipv4_rules_addr;
-	bool ipv4_rules_addr_shared;
-	u64 ipv4_expansion_rules_addr;
-	bool ipv4_expansion_rules_addr_shared;
+	struct ipahal_imm_cmd_nat_ipv6ct_init_common table_init;
 	u64 index_table_addr;
-	bool index_table_addr_shared;
 	u64 index_table_expansion_addr;
+	bool index_table_addr_shared;
 	bool index_table_expansion_addr_shared;
-	u16 size_base_tables;
-	u16 size_expansion_tables;
-	u32 public_ip_addr;
+	u32 public_addr_info;
+};
+
+/*
+ * struct ipahal_imm_cmd_ip_v6_ct_init - IP_V6_CONN_TRACK_INIT cmd payload
+ * Inits IPv6CT block. Initiate IPv6CT table with it dimensions, location
+ *  cache address and other related parameters.
+ * @table_init: table initialization parameters
+ */
+struct ipahal_imm_cmd_ip_v6_ct_init {
+	struct ipahal_imm_cmd_nat_ipv6ct_init_common table_init;
 };
 
 /*
  * struct ipahal_imm_cmd_ip_v4_routing_init - IP_V4_ROUTING_INIT cmd payload
  * Inits IPv4 routing table/structure - with the rules and other related params
  * @hash_rules_addr: Addr in sys mem where ipv4 hashable rt tbl starts
+ * @nhash_rules_addr: Addr in sys mem where ipv4 non-hashable rt tbl starts
  * @hash_rules_size: Size in bytes of the hashable tbl to cpy to local mem
  * @hash_local_addr: Addr in shared mem where ipv4 hashable rt tbl should
  *  be copied to
- * @nhash_rules_addr: Addr in sys mem where ipv4 non-hashable rt tbl starts
  * @nhash_rules_size: Size in bytes of the non-hashable tbl to cpy to local mem
  * @nhash_local_addr: Addr in shared mem where ipv4 non-hashable rt tbl should
  *  be copied to
  */
 struct ipahal_imm_cmd_ip_v4_routing_init {
 	u64 hash_rules_addr;
+	u64 nhash_rules_addr;
 	u32 hash_rules_size;
 	u32 hash_local_addr;
-	u64 nhash_rules_addr;
 	u32 nhash_rules_size;
 	u32 nhash_local_addr;
 };
@@ -148,19 +168,19 @@
  * struct ipahal_imm_cmd_ip_v6_routing_init - IP_V6_ROUTING_INIT cmd payload
  * Inits IPv6 routing table/structure - with the rules and other related params
  * @hash_rules_addr: Addr in sys mem where ipv6 hashable rt tbl starts
+ * @nhash_rules_addr: Addr in sys mem where ipv6 non-hashable rt tbl starts
  * @hash_rules_size: Size in bytes of the hashable tbl to cpy to local mem
  * @hash_local_addr: Addr in shared mem where ipv6 hashable rt tbl should
  *  be copied to
- * @nhash_rules_addr: Addr in sys mem where ipv6 non-hashable rt tbl starts
  * @nhash_rules_size: Size in bytes of the non-hashable tbl to cpy to local mem
  * @nhash_local_addr: Addr in shared mem where ipv6 non-hashable rt tbl should
  *  be copied to
  */
 struct ipahal_imm_cmd_ip_v6_routing_init {
 	u64 hash_rules_addr;
+	u64 nhash_rules_addr;
 	u32 hash_rules_size;
 	u32 hash_local_addr;
-	u64 nhash_rules_addr;
 	u32 nhash_rules_size;
 	u32 nhash_local_addr;
 };
@@ -189,36 +209,20 @@
 };
 
 /*
- * struct ipahal_imm_cmd_nat_dma - NAT_DMA cmd payload
- * Perform DMA operation on NAT related mem addressess. Copy data into
- *  different locations within NAT associated tbls. (For add/remove NAT rules)
- * @table_index: NAT tbl index. Defines the NAT tbl on which to perform DMA op.
- * @base_addr: Base addr to which the DMA operation should be performed.
- * @offset: offset in bytes from base addr to write 'data' to
- * @data: data to be written
- */
-struct ipahal_imm_cmd_nat_dma {
-	u8 table_index;
-	u8 base_addr;
-	u32 offset;
-	u16 data;
-};
-
-/*
  * struct ipahal_imm_cmd_table_dma - TABLE_DMA cmd payload
  * Perform DMA operation on NAT and IPV6 connection tracking related mem
- * addresses. Copy data into different locations within IPV6CT and NAT
+ * addresses. Copy data into different locations within IPv6CT and NAT
  * associated tbls. (For add/remove NAT rules)
- * @table_index: NAT tbl index. Defines the tbl on which to perform DMA op.
- * @base_addr: Base addr to which the DMA operation should be performed.
  * @offset: offset in bytes from base addr to write 'data' to
  * @data: data to be written
+ * @table_index: NAT tbl index. Defines the tbl on which to perform DMA op.
+ * @base_addr: Base addr to which the DMA operation should be performed.
  */
 struct ipahal_imm_cmd_table_dma {
-	u8 table_index;
-	u8 base_addr;
 	u32 offset;
 	u16 data;
+	u8 table_index;
+	u8 base_addr;
 };
 
 /*
@@ -275,6 +279,7 @@
 /*
  * struct ipahal_imm_cmd_dma_shared_mem - DMA_SHARED_MEM cmd payload
  * Perform mem copy into or out of the SW area of IPA local mem
+ * @system_addr: Address in system memory
  * @size: Size in bytes of data to copy. Expected size is up to 2K bytes
  * @local_addr: Address in IPA local memory
  * @clear_after_read: Clear local memory at the end of a read operation allows
@@ -282,16 +287,15 @@
  * @is_read: Read operation from local memory? If not, then write.
  * @skip_pipeline_clear: if to skip pipeline clear waiting (don't wait)
  * @pipeline_clear_option: options for pipeline clear waiting
- * @system_addr: Address in system memory
  */
 struct ipahal_imm_cmd_dma_shared_mem {
+	u64 system_addr;
 	u32 size;
 	u32 local_addr;
 	bool clear_after_read;
 	bool is_read;
 	bool skip_pipeline_clear;
 	enum ipahal_pipeline_clear_option pipeline_clear_options;
-	u64 system_addr;
 };
 
 /*
@@ -515,6 +519,7 @@
  *   following statuses: IPA_STATUS_PACKET, IPA_STATUS_DROPPED_PACKET,
  *   IPA_STATUS_SUSPENDED_PACKET.
  *  Other statuses types has different status packet structure.
+ * @tag_info: S/W defined value provided via immediate command
  * @status_opcode: The Type of the status (Opcode).
  * @exception: The first exception that took place.
  *  In case of exception, src endp and pkt len are always valid.
@@ -522,9 +527,6 @@
  *  and processing it may passed at IPA. See enum ipahal_pkt_status_mask
  * @pkt_len: Pkt pyld len including hdr and retained hdr if used. Does
  *  not include padding or checksum trailer len.
- * @endp_src_idx: Source end point index.
- * @endp_dest_idx: Destination end point index.
- *  Not valid in case of exception
  * @metadata: meta data value used by packet
  * @flt_local: Filter table location flag: Does matching flt rule belongs to
  *  flt tbl that resides in lcl memory? (if not, then system mem)
@@ -535,57 +537,59 @@
  *  specifies to retain header?
  * @flt_miss: Filtering miss flag: Was their a filtering rule miss?
  *   In case of miss, all flt info to be ignored
- * @flt_rule_id: The ID of the matching filter rule (if no miss).
- *  This info can be combined with endp_src_idx to locate the exact rule.
  * @rt_local: Route table location flag: Does matching rt rule belongs to
  *  rt tbl that resides in lcl memory? (if not, then system mem)
  * @rt_hash: Route hash hit flag: Does matching rt rule was in hash tbl?
  * @ucp: UC Processing flag
- * @rt_tbl_idx: Index of rt tbl that contains the rule on which was a match
  * @rt_miss: Routing miss flag: Was their a routing rule miss?
- * @rt_rule_id: The ID of the matching rt rule. (if no miss). This info
- *  can be combined with rt_tbl_idx to locate the exact rule.
  * @nat_hit: NAT hit flag: Was their NAT hit?
- * @nat_entry_idx: Index of the NAT entry used of NAT processing
  * @nat_type: Defines the type of the NAT operation:
- * @tag_info: S/W defined value provided via immediate command
- * @seq_num: Per source endp unique packet sequence number
  * @time_of_day_ctr: running counter from IPA clock
  * @hdr_local: Header table location flag: In header insertion, was the header
  *  taken from the table resides in local memory? (If no, then system mem)
- * @hdr_offset: Offset of used header in the header table
  * @frag_hit: Frag hit flag: Was their frag rule hit in H/W frag table?
+ * @flt_rule_id: The ID of the matching filter rule (if no miss).
+ *  This info can be combined with endp_src_idx to locate the exact rule.
+ * @rt_rule_id: The ID of the matching rt rule. (if no miss). This info
+ *  can be combined with rt_tbl_idx to locate the exact rule.
+ * @nat_entry_idx: Index of the NAT entry used of NAT processing
+ * @hdr_offset: Offset of used header in the header table
+ * @endp_src_idx: Source end point index.
+ * @endp_dest_idx: Destination end point index.
+ *  Not valid in case of exception
+ * @rt_tbl_idx: Index of rt tbl that contains the rule on which was a match
+ * @seq_num: Per source endp unique packet sequence number
  * @frag_rule: Frag rule index in H/W frag table in case of frag hit
  */
 struct ipahal_pkt_status {
+	u64 tag_info;
 	enum ipahal_pkt_status_opcode status_opcode;
 	enum ipahal_pkt_status_exception exception;
 	u32 status_mask;
 	u32 pkt_len;
-	u8 endp_src_idx;
-	u8 endp_dest_idx;
 	u32 metadata;
 	bool flt_local;
 	bool flt_hash;
 	bool flt_global;
 	bool flt_ret_hdr;
 	bool flt_miss;
-	u16 flt_rule_id;
 	bool rt_local;
 	bool rt_hash;
 	bool ucp;
-	u8 rt_tbl_idx;
 	bool rt_miss;
-	u16 rt_rule_id;
 	bool nat_hit;
-	u16 nat_entry_idx;
 	enum ipahal_pkt_status_nat_type nat_type;
-	u64 tag_info;
-	u8 seq_num;
 	u32 time_of_day_ctr;
 	bool hdr_local;
-	u16 hdr_offset;
 	bool frag_hit;
+	u16 flt_rule_id;
+	u16 rt_rule_id;
+	u16 nat_entry_idx;
+	u16 hdr_offset;
+	u8 endp_src_idx;
+	u8 endp_dest_idx;
+	u8 rt_tbl_idx;
+	u8 seq_num;
 	u8 frag_rule;
 };
 
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_i.h b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_i.h
index 5eb1aef..4ccb7e0 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_i.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_i.h
@@ -125,10 +125,10 @@
  * struct ipa_imm_cmd_hw_ip_v4_nat_init - IP_V4_NAT_INIT command payload
  *  in H/W format.
  * Inits IPv4 NAT block. Initiate NAT table with it dimensions, location
- *  cache address abd itger related parameters.
+ *  cache address and other related parameters.
  * @ipv4_rules_addr: Addr in sys/shared mem where ipv4 NAT rules start
- * @ipv4_expansion_rules_addr: Addr in sys/shared mem where expantion NAT
- *  table starts. IPv4 NAT rules that result in NAT collision are located
+ * @ipv4_expansion_rules_addr: Addr in sys/shared mem where expansion NAT
+ *  table starts. IPv4 NAT rules that result in hash collision are located
  *  in this table.
  * @index_table_addr: Addr in sys/shared mem where index table, which points
  *  to NAT table starts
@@ -143,11 +143,12 @@
  * @index_table_expansion_addr_type: index_table_expansion_addr in
  *  sys or shared mem
  * @size_base_tables: Num of entries in NAT tbl and idx tbl (each)
- * @size_expansion_tables: Num of entries in NAT expantion tbl and expantion
+ * @size_expansion_tables: Num of entries in NAT expansion tbl and expansion
  *  idx tbl (each)
  * @rsvd2: reserved
- * @public_ip_addr: public IP address. for IPAv4 this is the PDN config table
- *  offset in SMEM
+ * @public_addr_info: Public IP addresses info suitable to the IPA H/W version
+ *                    IPA H/W >= 4.0 - PDN config table offset in SMEM
+ *                    IPA H/W < 4.0  - The public IP address
  */
 struct ipa_imm_cmd_hw_ip_v4_nat_init {
 	u64 ipv4_rules_addr:64;
@@ -163,7 +164,38 @@
 	u64 size_base_tables:12;
 	u64 size_expansion_tables:10;
 	u64 rsvd2:2;
-	u64 public_ip_addr:32;
+	u64 public_addr_info:32;
+};
+
+/*
+ * struct ipa_imm_cmd_hw_ip_v6_ct_init - IP_V6_CONN_TRACK_INIT command payload
+ *  in H/W format.
+ * Inits IPv6CT block. Initiate IPv6CT table with it dimensions, location
+ *  cache address and other related parameters.
+ * @table_addr: Address in sys/shared mem where IPv6CT rules start
+ * @expansion_table_addr: Address in sys/shared mem where IPv6CT expansion
+ *  table starts. IPv6CT rules that result in hash collision are located
+ *  in this table.
+ * @table_index: For future support of multiple IPv6CT tables
+ * @rsvd1: reserved
+ * @table_addr_type: table_addr in sys or shared mem
+ * @expansion_table_addr_type: expansion_table_addr in sys or shared mem
+ * @rsvd2: reserved
+ * @size_base_tables: Number of entries in IPv6CT table
+ * @size_expansion_tables: Number of entries in IPv6CT expansion table
+ * @rsvd3: reserved
+ */
+struct ipa_imm_cmd_hw_ip_v6_ct_init {
+	u64 table_addr:64;
+	u64 expansion_table_addr:64;
+	u64 table_index:3;
+	u64 rsvd1:1;
+	u64 table_addr_type:1;
+	u64 expansion_table_addr_type:1;
+	u64 rsvd2:2;
+	u64 size_base_table:12;
+	u64 size_expansion_table:10;
+	u64 rsvd3:34;
 };
 
 /*
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_nat.c b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_nat.c
new file mode 100644
index 0000000..d335ba6
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_nat.c
@@ -0,0 +1,360 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/debugfs.h>
+#include "ipahal_nat.h"
+#include "ipahal_nat_i.h"
+#include "ipahal_i.h"
+
+#define IPA_64_LOW_32_MASK (0xFFFFFFFF)
+#define IPA_64_HIGH_32_MASK (0xFFFFFFFF00000000ULL)
+
+static const char *ipahal_nat_type_to_str[IPA_NAT_MAX] = {
+	__stringify(IPAHAL_NAT_IPV4),
+	__stringify(IPAHAL_NAT_IPV4_INDEX),
+	__stringify(IPAHAL_NAT_IPV4_PDN),
+	__stringify(IPAHAL_NAT_IPV6CT)
+};
+
+static size_t ipa_nat_ipv4_entry_size_v_3_0(void)
+{
+	return sizeof(struct ipa_nat_hw_ipv4_entry);
+}
+
+static size_t ipa_nat_ipv4_index_entry_size_v_3_0(void)
+{
+	return sizeof(struct ipa_nat_hw_indx_entry);
+}
+
+static size_t ipa_nat_ipv4_pdn_entry_size_v_4_0(void)
+{
+	return sizeof(struct ipa_nat_hw_pdn_entry);
+}
+
+static size_t ipa_nat_ipv6ct_entry_size_v_4_0(void)
+{
+	return sizeof(struct ipa_nat_hw_ipv6ct_entry);
+}
+
+static bool ipa_nat_ipv4_is_entry_zeroed_v_3_0(const void *entry)
+{
+	struct ipa_nat_hw_ipv4_entry zero_entry = { 0 };
+
+	return (memcmp(&zero_entry, entry, sizeof(zero_entry))) ? false : true;
+}
+
+static bool ipa_nat_ipv4_is_index_entry_zeroed_v_3_0(const void *entry)
+{
+	struct ipa_nat_hw_indx_entry zero_entry = { 0 };
+
+	return (memcmp(&zero_entry, entry, sizeof(zero_entry))) ? false : true;
+}
+
+static bool ipa_nat_ipv4_is_pdn_entry_zeroed_v_4_0(const void *entry)
+{
+	struct ipa_nat_hw_pdn_entry zero_entry = { 0 };
+
+	return (memcmp(&zero_entry, entry, sizeof(zero_entry))) ? false : true;
+}
+
+static bool ipa_nat_ipv6ct_is_entry_zeroed_v_4_0(const void *entry)
+{
+	struct ipa_nat_hw_ipv6ct_entry zero_entry = { 0 };
+
+	return (memcmp(&zero_entry, entry, sizeof(zero_entry))) ? false : true;
+}
+
+static int ipa_nat_ipv4_stringify_entry_v_3_0(const void *entry,
+	char *buff, size_t buff_size)
+{
+	const struct ipa_nat_hw_ipv4_entry *nat_entry =
+		(const struct ipa_nat_hw_ipv4_entry *)entry;
+
+	return scnprintf(buff, buff_size,
+		"\t\tPrivate_IP=%pI4h  Target_IP=%pI4h\n"
+		"\t\tNext_Index=%d  Public_Port=%d\n"
+		"\t\tPrivate_Port=%d  Target_Port=%d\n"
+		"\t\tIP_CKSM_delta=0x%x  Enable=%s  Redirect=%s\n"
+		"\t\tTime_stamp=0x%x Proto=%d\n"
+		"\t\tPrev_Index=%d  Indx_tbl_entry=%d\n"
+		"\t\tTCP_UDP_cksum_delta=0x%x\n",
+		&nat_entry->private_ip, &nat_entry->target_ip,
+		nat_entry->next_index, nat_entry->public_port,
+		nat_entry->private_port, nat_entry->target_port,
+		nat_entry->ip_chksum,
+		(nat_entry->enable) ? "true" : "false",
+		(nat_entry->redirect) ? "Direct_To_APPS" : "Fwd_to_route",
+		nat_entry->time_stamp, nat_entry->protocol,
+		nat_entry->prev_index, nat_entry->indx_tbl_entry,
+		nat_entry->tcp_udp_chksum);
+}
+
+static int ipa_nat_ipv4_stringify_entry_v_4_0(const void *entry,
+	char *buff, size_t buff_size)
+{
+	int length;
+	const struct ipa_nat_hw_ipv4_entry *nat_entry =
+		(const struct ipa_nat_hw_ipv4_entry *)entry;
+
+	length = ipa_nat_ipv4_stringify_entry_v_3_0(entry, buff, buff_size);
+
+	length += scnprintf(buff + length, buff_size - length,
+		"\t\tPDN_Index=%d\n", nat_entry->pdn_index);
+
+	return length;
+}
+
+static int ipa_nat_ipv4_index_stringify_entry_v_3_0(const void *entry,
+	char *buff, size_t buff_size)
+{
+	const struct ipa_nat_hw_indx_entry *index_entry =
+		(const struct ipa_nat_hw_indx_entry *)entry;
+
+	return scnprintf(buff, buff_size,
+		"\t\tTable_Entry=%d  Next_Index=%d\n",
+		index_entry->tbl_entry, index_entry->next_index);
+}
+
+static int ipa_nat_ipv4_pdn_stringify_entry_v_4_0(const void *entry,
+	char *buff, size_t buff_size)
+{
+	const struct ipa_nat_hw_pdn_entry *pdn_entry =
+		(const struct ipa_nat_hw_pdn_entry *)entry;
+
+	return scnprintf(buff, buff_size,
+		"ip=%pI4h src_metadata=0x%X, dst_metadata=0x%X\n",
+		&pdn_entry->public_ip,
+		pdn_entry->src_metadata, pdn_entry->dst_metadata);
+}
+
+static inline int ipa_nat_ipv6_stringify_addr(char *buff, size_t buff_size,
+	const char *msg, u64 lsb, u64 msb)
+{
+	struct in6_addr addr;
+
+	addr.s6_addr32[0] = cpu_to_be32((msb & IPA_64_HIGH_32_MASK) >> 32);
+	addr.s6_addr32[1] = cpu_to_be32(msb & IPA_64_LOW_32_MASK);
+	addr.s6_addr32[2] = cpu_to_be32((lsb & IPA_64_HIGH_32_MASK) >> 32);
+	addr.s6_addr32[3] = cpu_to_be32(lsb & IPA_64_LOW_32_MASK);
+
+	return scnprintf(buff, buff_size,
+		"\t\t%s_IPv6_Addr=%pI6c\n", msg, &addr);
+}
+
+static int ipa_nat_ipv6ct_stringify_entry_v_4_0(const void *entry,
+	char *buff, size_t buff_size)
+{
+	int length = 0;
+	const struct ipa_nat_hw_ipv6ct_entry *ipv6ct_entry =
+		(const struct ipa_nat_hw_ipv6ct_entry *)entry;
+
+	length += ipa_nat_ipv6_stringify_addr(
+		buff + length,
+		buff_size - length,
+		"Src",
+		ipv6ct_entry->src_ipv6_lsb,
+		ipv6ct_entry->src_ipv6_msb);
+
+	length += ipa_nat_ipv6_stringify_addr(
+		buff + length,
+		buff_size - length,
+		"Dest",
+		ipv6ct_entry->dest_ipv6_lsb,
+		ipv6ct_entry->dest_ipv6_msb);
+
+	length += scnprintf(buff + length, buff_size - length,
+		"\t\tEnable=%s Redirect=%s Time_Stamp=0x%x Proto=%d\n"
+		"\t\tNext_Index=%d Dest_Port=%d Src_Port=%d\n"
+		"\t\tDirection Settings: Out=%s In=%s\n"
+		"\t\tPrev_Index=%d\n",
+		(ipv6ct_entry->enable) ? "true" : "false",
+		(ipv6ct_entry->redirect) ? "Direct_To_APPS" : "Fwd_to_route",
+		ipv6ct_entry->time_stamp,
+		ipv6ct_entry->protocol,
+		ipv6ct_entry->next_index,
+		ipv6ct_entry->dest_port,
+		ipv6ct_entry->src_port,
+		(ipv6ct_entry->out_allowed) ? "Allow" : "Deny",
+		(ipv6ct_entry->in_allowed) ? "Allow" : "Deny",
+		ipv6ct_entry->prev_index);
+
+	return length;
+}
+
+/*
+ * struct ipahal_nat_obj - H/W information for specific IPA version
+ * @entry_size - CB to get the size of the entry
+ * @is_entry_zeroed - CB to determine whether an entry is definitely zero
+ * @stringify_entry - CB to create string that represents an entry
+ */
+struct ipahal_nat_obj {
+	size_t (*entry_size)(void);
+	bool (*is_entry_zeroed)(const void *entry);
+	int (*stringify_entry)(const void *entry, char *buff, size_t buff_size);
+};
+
+/*
+ * This table contains the info regard each NAT type for IPAv3 and later.
+ * Information like: get entry size and stringify entry functions.
+ * All the information on all the NAT types on IPAv3 are statically
+ * defined below. If information is missing regard some NAT type on some
+ * IPA version, the init function will fill it with the information from the
+ * previous IPA version.
+ * Information is considered missing if all of the fields are 0
+ */
+static struct ipahal_nat_obj ipahal_nat_objs[IPA_HW_MAX][IPA_NAT_MAX] = {
+	/* IPAv3 */
+	[IPA_HW_v3_0][IPAHAL_NAT_IPV4] = {
+			ipa_nat_ipv4_entry_size_v_3_0,
+			ipa_nat_ipv4_is_entry_zeroed_v_3_0,
+			ipa_nat_ipv4_stringify_entry_v_3_0
+		},
+	[IPA_HW_v3_0][IPAHAL_NAT_IPV4_INDEX] = {
+			ipa_nat_ipv4_index_entry_size_v_3_0,
+			ipa_nat_ipv4_is_index_entry_zeroed_v_3_0,
+			ipa_nat_ipv4_index_stringify_entry_v_3_0
+		},
+
+	/* IPAv4 */
+	[IPA_HW_v4_0][IPAHAL_NAT_IPV4] = {
+			ipa_nat_ipv4_entry_size_v_3_0,
+			ipa_nat_ipv4_is_entry_zeroed_v_3_0,
+			ipa_nat_ipv4_stringify_entry_v_4_0
+		},
+	[IPA_HW_v4_0][IPAHAL_NAT_IPV4_PDN] = {
+			ipa_nat_ipv4_pdn_entry_size_v_4_0,
+			ipa_nat_ipv4_is_pdn_entry_zeroed_v_4_0,
+			ipa_nat_ipv4_pdn_stringify_entry_v_4_0
+		},
+	[IPA_HW_v4_0][IPAHAL_NAT_IPV6CT] = {
+			ipa_nat_ipv6ct_entry_size_v_4_0,
+			ipa_nat_ipv6ct_is_entry_zeroed_v_4_0,
+			ipa_nat_ipv6ct_stringify_entry_v_4_0
+		}
+};
+
+static void ipahal_nat_check_obj(struct ipahal_nat_obj *obj,
+	int nat_type, int ver)
+{
+	WARN(obj->entry_size == NULL, "%s missing entry_size for version %d\n",
+		ipahal_nat_type_str(nat_type), ver);
+	WARN(obj->is_entry_zeroed == NULL,
+		"%s missing is_entry_zeroed for version %d\n",
+		ipahal_nat_type_str(nat_type), ver);
+	WARN(obj->stringify_entry == NULL,
+		"%s missing stringify_entry for version %d\n",
+		ipahal_nat_type_str(nat_type), ver);
+}
+
+/*
+ * ipahal_nat_init() - Build the NAT information table
+ *  See ipahal_nat_objs[][] comments
+ */
+int ipahal_nat_init(enum ipa_hw_type ipa_hw_type)
+{
+	int i;
+	int j;
+	struct ipahal_nat_obj zero_obj, *next_obj;
+
+	IPAHAL_DBG("Entry - HW_TYPE=%d\n", ipa_hw_type);
+
+	memset(&zero_obj, 0, sizeof(zero_obj));
+
+	if ((ipa_hw_type < 0) || (ipa_hw_type >= IPA_HW_MAX)) {
+		IPAHAL_ERR("invalid IPA HW type (%d)\n", ipa_hw_type);
+		return -EINVAL;
+	}
+
+	for (i = IPA_HW_v3_0 ; i < ipa_hw_type ; ++i) {
+		for (j = 0; j < IPA_NAT_MAX; ++j) {
+			next_obj = &ipahal_nat_objs[i + 1][j];
+			if (!memcmp(next_obj, &zero_obj, sizeof(*next_obj))) {
+				memcpy(next_obj, &ipahal_nat_objs[i][j],
+					sizeof(*next_obj));
+			} else {
+				ipahal_nat_check_obj(next_obj, j, i + 1);
+			}
+		}
+	}
+
+	return 0;
+}
+
+const char *ipahal_nat_type_str(enum ipahal_nat_type nat_type)
+{
+	if (nat_type < 0 || nat_type >= IPA_NAT_MAX) {
+		IPAHAL_ERR("requested NAT type %d is invalid\n", nat_type);
+		return "Invalid NAT type";
+	}
+
+	return ipahal_nat_type_to_str[nat_type];
+}
+
+int ipahal_nat_entry_size(enum ipahal_nat_type nat_type, size_t *entry_size)
+{
+	if (WARN(entry_size == NULL, "entry_size is NULL\n"))
+		return -EINVAL;
+	if (WARN(nat_type < 0 || nat_type >= IPA_NAT_MAX,
+		"requested NAT type %d is invalid\n", nat_type))
+		return -EINVAL;
+
+	IPAHAL_DBG("Get the entry size for NAT type=%s\n",
+		ipahal_nat_type_str(nat_type));
+	*entry_size = ipahal_nat_objs[ipahal_ctx->hw_type][nat_type].
+		entry_size();
+	IPAHAL_DBG("The entry size is %zu\n", *entry_size);
+
+	return 0;
+}
+
+int ipahal_nat_is_entry_zeroed(enum ipahal_nat_type nat_type, void *entry,
+	bool *entry_zeroed)
+{
+	if (WARN(entry == NULL || entry_zeroed == NULL,
+		"NULL pointer received\n"))
+		return -EINVAL;
+	if (WARN(nat_type < 0 || nat_type >= IPA_NAT_MAX,
+		"requested NAT type %d is invalid\n", nat_type))
+		return -EINVAL;
+
+	IPAHAL_DBG("Determine whether the entry is zeroed for NAT type=%s\n",
+		ipahal_nat_type_str(nat_type));
+	*entry_zeroed = ipahal_nat_objs[ipahal_ctx->hw_type][nat_type].
+		is_entry_zeroed(entry);
+	IPAHAL_DBG("The entry is %szeroed\n", (*entry_zeroed) ? "" : "not ");
+
+	return 0;
+}
+
+int ipahal_nat_stringify_entry(enum ipahal_nat_type nat_type, void *entry,
+	char *buff, size_t buff_size)
+{
+	int result;
+
+	if (WARN(entry == NULL || buff == NULL, "NULL pointer received\n"))
+		return -EINVAL;
+	if (WARN(!buff_size, "The output buff size is zero\n"))
+		return -EINVAL;
+	if (WARN(nat_type < 0 || nat_type >= IPA_NAT_MAX,
+		"requested NAT type %d is invalid\n", nat_type))
+		return -EINVAL;
+
+	IPAHAL_DBG("Create the string for the entry of NAT type=%s\n",
+		ipahal_nat_type_str(nat_type));
+	result = ipahal_nat_objs[ipahal_ctx->hw_type][nat_type].
+		stringify_entry(entry, buff, buff_size);
+	IPAHAL_DBG("The string successfully created with length %d\n",
+		result);
+
+	return result;
+}
+
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_nat.h b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_nat.h
new file mode 100644
index 0000000..f99c1a0
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_nat.h
@@ -0,0 +1,67 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _IPAHAL_NAT_H_
+#define _IPAHAL_NAT_H_
+
+/*
+ * NAT types
+ *
+ * NOTE:: Any change to this enum, need to change to ipahal_nat_to_str
+ *	  array as well.
+ */
+enum ipahal_nat_type {
+	IPAHAL_NAT_IPV4,
+	IPAHAL_NAT_IPV4_INDEX,
+	IPAHAL_NAT_IPV4_PDN,
+	IPAHAL_NAT_IPV6CT,
+	IPA_NAT_MAX
+};
+
+/* NAT Function APIs */
+
+/*
+ * ipahal_nat_type_str() - returns string that represent the NAT type
+ * @nat_type: [in] NAT type
+ */
+const char *ipahal_nat_type_str(enum ipahal_nat_type nat_type);
+
+/*
+ * ipahal_nat_entry_size() - Gets the size of HW NAT entry
+ * @nat_type: [in] The type of the NAT entry
+ * @entry_size: [out] The size of the HW NAT entry
+ */
+int ipahal_nat_entry_size(enum ipahal_nat_type nat_type, size_t *entry_size);
+
+/*
+ * ipahal_nat_is_entry_zeroed() - Determines whether HW NAT entry is
+ *                                definitely zero
+ * @nat_type: [in] The type of the NAT entry
+ * @entry: [in] The NAT entry
+ * @entry_zeroed: [out] True if the received entry is definitely zero
+ */
+int ipahal_nat_is_entry_zeroed(enum ipahal_nat_type nat_type, void *entry,
+	bool *entry_zeroed);
+
+/*
+ * ipahal_nat_stringify_entry() - Creates a string for HW NAT entry
+ * @nat_type: [in] The type of the NAT entry
+ * @entry: [in] The NAT entry
+ * @buff: [out] Output buffer for the result string
+ * @buff_size: [in] The size of the output buffer
+ * @return the number of characters written into buff not including
+ *         the trailing '\0'
+ */
+int ipahal_nat_stringify_entry(enum ipahal_nat_type nat_type, void *entry,
+	char *buff, size_t buff_size);
+
+#endif /* _IPAHAL_NAT_H_ */
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_nat_i.h b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_nat_i.h
new file mode 100644
index 0000000..83bd0f5
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_nat_i.h
@@ -0,0 +1,153 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _IPAHAL_NAT_I_H_
+#define _IPAHAL_NAT_I_H_
+
+#include <linux/msm_ipa.h>
+
+/* ----------------------- IPv4 NAT Table Entry  -------------------------
+ *
+ * -----------------------------------------------------------------------
+ * |  7  |     6    |   5    |   4   |    3        | 2 |    1    |    0  |
+ * -----------------------------------------------------------------------
+ * |           Target IP(4B)         |             Private IP(4B)        |
+ * -----------------------------------------------------------------------
+ * |Target Port(2B) |Private Port(2B)| Public Port(2B) | Next Index(2B)  |
+ * -----------------------------------------------------------------------
+ * |Proto|      TimeStamp(3B)        |     Flags(2B)   |IP check sum Diff|
+ * |(1B) |                           |EN|Redirect|Resv |        (2B)     |
+ * -----------------------------------------------------------------------
+ * |TCP/UDP checksum|  PDN info(2B)  |    SW Specific Parameters(4B)     |
+ * |    diff (2B)   |Info|Resv       |index table entry|  prev index     |
+ * -----------------------------------------------------------------------
+ */
+struct ipa_nat_hw_ipv4_entry {
+	/* An IP address can't be bit-field, because its address is used */
+	u32 private_ip;
+	u32 target_ip;
+
+	u32 next_index : 16;
+	u32 public_port : 16;
+	u32 private_port : 16;
+	u32 target_port : 16;
+	u32 ip_chksum : 16;
+
+	u32 rsvd1 : 14;
+	u32 redirect : 1;
+	u32 enable : 1;
+
+	u32 time_stamp : 24;
+	u32 protocol : 8;
+
+	u32 prev_index : 16;
+	u32 indx_tbl_entry : 16;
+
+	u32 rsvd2 : 12;
+	u32 pdn_index : 4; /* IPA 4.0 and greater */
+
+	u32 tcp_udp_chksum : 16;
+};
+
+/*--- IPV4 NAT Index Table Entry --
+ *---------------------------------
+ *|   3   |   2   |   1   |   0   |
+ *---------------------------------
+ *|next index(2B) |table entry(2B)|
+ *---------------------------------
+ */
+struct ipa_nat_hw_indx_entry {
+	u16 tbl_entry;
+	u16 next_index;
+};
+
+/**
+ * struct ipa_nat_hw_pdn_entry - IPA PDN config table entry
+ * @public_ip: the PDN's public ip
+ * @src_metadata: the PDN's metadata to be replaced for source NAT
+ * @dst_metadata: the PDN's metadata to be replaced for destination NAT
+ * @resrvd: reserved field
+ * ---------------------------------
+ * |   3   |   2   |   1   |   0   |
+ * ---------------------------------
+ * |        public_ip (4B)         |
+ * ---------------------------------
+ * |      src_metadata (4B)        |
+ * ---------------------------------
+ * |      dst_metadata (4B)        |
+ * ---------------------------------
+ * |         resrvd (4B)           |
+ * ---------------------------------
+ */
+struct ipa_nat_hw_pdn_entry {
+	u32 public_ip;
+	u32 src_metadata;
+	u32 dst_metadata;
+	u32 resrvd;
+};
+
+/*-------------------------  IPV6CT Table Entry  ------------------------------
+ *-----------------------------------------------------------------------------
+ *|   7    |      6      |  5  |  4   |        3         |  2  |   1  |   0   |
+ *-----------------------------------------------------------------------------
+ *|                   Outbound Src IPv6 Address (8 LSB Bytes)                 |
+ *-----------------------------------------------------------------------------
+ *|                   Outbound Src IPv6 Address (8 MSB Bytes)                 |
+ *-----------------------------------------------------------------------------
+ *|                   Outbound Dest IPv6 Address (8 LSB Bytes)                |
+ *-----------------------------------------------------------------------------
+ *|                   Outbound Dest IPv6 Address (8 MSB Bytes)                |
+ *-----------------------------------------------------------------------------
+ *|Protocol|      TimeStamp (3B)      |       Flags (2B)       |Reserved (2B) |
+ *|  (1B)  |                          |Enable|Redirect|Resv    |              |
+ *-----------------------------------------------------------------------------
+ *|Reserved|Direction(1B)|Src Port(2B)|     Dest Port (2B)     |Next Index(2B)|
+ *|  (1B)  |IN|OUT|Resv  |            |                        |              |
+ *-----------------------------------------------------------------------------
+ *|    SW Specific Parameters(4B)     |                Reserved (4B)          |
+ *|    Prev Index (2B)   |Reserved(2B)|                                       |
+ *-----------------------------------------------------------------------------
+ *|                            Reserved (8B)                                  |
+ *-----------------------------------------------------------------------------
+ */
+struct ipa_nat_hw_ipv6ct_entry {
+	/* An IP address can't be bit-field, because its address is used */
+	u64 src_ipv6_lsb;
+	u64 src_ipv6_msb;
+	u64 dest_ipv6_lsb;
+	u64 dest_ipv6_msb;
+
+	u64 rsvd1 : 30;
+	u64 redirect : 1;
+	u64 enable : 1;
+
+	u64 time_stamp : 24;
+	u64 protocol : 8;
+
+	u64 next_index : 16;
+	u64 dest_port : 16;
+	u64 src_port : 16;
+	u64 rsvd2 : 6;
+	u64 out_allowed : 1;
+	u64 in_allowed : 1;
+	u64 rsvd3 : 8;
+
+	u64 rsvd4 : 48;
+	u64 prev_index : 16;
+
+	u64 rsvd5 : 64;
+};
+
+int ipahal_nat_init(enum ipa_hw_type ipa_hw_type);
+
+#endif /* _IPAHAL_NAT_I_H_ */
+