msm: ipa4: add multi PDN support

IPAv4 presents multi PDN support. This change provides backward
compatibility towards user space and allows setting NAT rules
for several PDNs. Metadata replacement is also supported as part
of this feature.

CRs-Fixed: 2040674
Change-Id: If0ee3cb1da5b40a4d0887aaec276e5b78e947337
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 31e530e..7d94118 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c
@@ -610,6 +610,7 @@
 	struct ipa_ioc_nat_alloc_mem nat_mem;
 	struct ipa_ioc_v4_nat_init nat_init;
 	struct ipa_ioc_v4_nat_del nat_del;
+	struct ipa_ioc_nat_pdn_entry mdfy_pdn;
 	struct ipa_ioc_rm_dependency rm_depend;
 	size_t sz;
 	int pre_entry;
@@ -708,6 +709,18 @@
 		}
 		break;
 
+	case IPA_IOC_NAT_MODIFY_PDN:
+		if (copy_from_user((u8 *)&mdfy_pdn, (const void __user *)arg,
+			sizeof(struct ipa_ioc_nat_pdn_entry))) {
+			retval = -EFAULT;
+			break;
+		}
+		if (ipa4_nat_mdfy_pdn(&mdfy_pdn)) {
+			retval = -EFAULT;
+			break;
+		}
+		break;
+
 	case IPA_IOC_ADD_HDR:
 		if (copy_from_user(header, (u8 *)arg,
 					sizeof(struct ipa_ioc_add_hdr))) {
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c
index 1ee8ec8..1634b1c 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c
@@ -906,6 +906,10 @@
 			pr_err("hashable:%u rule_id:%u max_prio:%u prio:%u ",
 				entry->rule.hashable, entry->rule_id,
 				entry->rule.max_prio, entry->prio);
+			if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0)
+				pr_err("pdn index %d, set metadata %d ",
+					entry->rule.pdn_idx,
+					entry->rule.set_metadata);
 			if (eq)
 				ipa3_attrib_dump_eq(
 					&entry->rule.eq_attrib);
@@ -968,6 +972,10 @@
 				bitmap, rules[rl].rule.retain_hdr);
 			pr_err("rule_id:%u prio:%u ",
 				rules[rl].id, rules[rl].priority);
+			if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0)
+				pr_err("pdn: %u, set_metadata: %u ",
+					rules[rl].rule.pdn_idx,
+					rules[rl].rule.set_metadata);
 			ipa3_attrib_dump_eq(&rules[rl].rule.eq_attrib);
 		}
 
@@ -992,6 +1000,10 @@
 				bitmap, rules[rl].rule.retain_hdr);
 			pr_err("rule_id:%u  prio:%u ",
 				rules[rl].id, rules[rl].priority);
+			if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0)
+				pr_err("pdn: %u, set_metadata: %u ",
+					rules[rl].rule.pdn_idx,
+					rules[rl].rule.set_metadata);
 			ipa3_attrib_dump_eq(&rules[rl].rule.eq_attrib);
 		}
 		pr_err("\n");
@@ -1502,6 +1514,7 @@
 	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;
 
 	mutex_lock(&ipa3_ctx->nat_mem.lock);
 	value = ipa3_ctx->nat_mem.public_ip_addr;
@@ -1512,6 +1525,15 @@
 				((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);
 
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c
index d0ed782..bfcaa2b 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c
@@ -755,6 +755,23 @@
 		}
 	}
 
+	if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0) {
+		if (rule->pdn_idx) {
+			if (rule->action == IPA_PASS_TO_EXCEPTION ||
+				rule->action == IPA_PASS_TO_ROUTING) {
+				IPAERR(
+					"PDN index should be 0 when action is not pass to NAT\n");
+				goto error;
+			} else {
+				if (rule->pdn_idx >= IPA_MAX_PDN_NUM) {
+					IPAERR("PDN index %d is too large\n",
+						rule->pdn_idx);
+					goto error;
+				}
+			}
+		}
+	}
+
 	if (rule->rule_id) {
 		if (!(rule->rule_id & ipahal_get_rule_id_hi_bit())) {
 			IPAERR("invalid rule_id provided 0x%x\n"
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
index 86442b1..7df6887 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
@@ -739,6 +739,19 @@
 };
 
 /**
+* struct ipa_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
+*/
+struct ipa_pdn_entry {
+	u32 public_ip;
+	u32 src_metadata;
+	u32 dst_metadata;
+	u32 resrvd;
+};
+/**
  * struct ipa3_nat_mem - IPA NAT memory description
  * @class: pointer to the struct class
  * @dev: the dev_t of the device
@@ -759,6 +772,7 @@
  * @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
  */
 struct ipa3_nat_mem {
 	struct class *class;
@@ -784,6 +798,7 @@
 	void *tmp_vaddr;
 	dma_addr_t tmp_dma_handle;
 	bool is_tmp_mem;
+	struct ipa_mem_buffer pdn_mem;
 };
 
 /**
@@ -1580,6 +1595,8 @@
 
 int ipa3_nat_del_cmd(struct ipa_ioc_v4_nat_del *del);
 
+int ipa4_nat_mdfy_pdn(struct ipa_ioc_nat_pdn_entry *mdfy_pdn);
+
 /*
  * Messaging
  */
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_nat.c b/drivers/platform/msm/ipa/ipa_v3/ipa_nat.c
index e1177ca..6acc4d8 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_nat.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_nat.c
@@ -34,7 +34,6 @@
 #define NAT_TABLE_ENTRY_SIZE_BYTE 32
 #define NAT_INTEX_TABLE_ENTRY_SIZE_BYTE 4
 
-
 static int ipa3_nat_vma_fault_remap(
 	 struct vm_area_struct *vma, struct vm_fault *vmf)
 {
@@ -247,7 +246,7 @@
 int ipa3_allocate_nat_device(struct ipa_ioc_nat_alloc_mem *mem)
 {
 	struct ipa3_nat_mem *nat_ctx = &(ipa3_ctx->nat_mem);
-	int gfp_flags = GFP_KERNEL | __GFP_ZERO;
+	gfp_t gfp_flags = GFP_KERNEL | __GFP_ZERO;
 	int result;
 
 	IPADBG("passed memory size %zu\n", mem->size);
@@ -295,11 +294,44 @@
 		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;
+
+		pdn_mem->size = sizeof(struct ipa_pdn_entry) * IPA_MAX_PDN_NUM;
+		if (IPA_MEM_PART(pdn_config_size) < pdn_mem->size) {
+			IPAERR(
+				"number of PDN entries exceeds SRAM available space\n");
+			result = -ENOMEM;
+			goto fail_alloc_pdn;
+		}
+
+		pdn_mem->base = dma_alloc_coherent(ipa3_ctx->pdev,
+			pdn_mem->size,
+			&pdn_mem->phys_base,
+			gfp_flags);
+		if (!pdn_mem->base) {
+			IPAERR("fail to allocate PDN memory\n");
+			result = -ENOMEM;
+			goto fail_alloc_pdn;
+		}
+		pdn_entries = pdn_mem->base;
+		memset(pdn_entries, 0, pdn_mem->size);
+		IPADBG("IPA NAT dev allocated PDN memory successfully\n");
+	}
 
 	nat_ctx->is_dev_init = true;
 	IPADBG("IPA NAT dev init successfully\n");
-	result = 0;
+	mutex_unlock(&nat_ctx->lock);
 
+	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;
+	}
 bail:
 	mutex_unlock(&nat_ctx->lock);
 
@@ -320,11 +352,13 @@
 #define TBL_ENTRY_SIZE 32
 #define INDX_TBL_ENTRY_SIZE 4
 
-	struct ipahal_imm_cmd_pyld *nop_cmd_pyld = NULL;
-	struct ipa3_desc desc[2];
+	struct ipa3_desc desc[3];
 	struct ipahal_imm_cmd_ip_v4_nat_init cmd;
-	struct ipahal_imm_cmd_pyld *cmd_pyld = NULL;
-	int result;
+	int num_cmd = 0;
+	int i = 0;
+	struct ipahal_imm_cmd_pyld *cmd_pyld[3];
+	struct ipahal_imm_cmd_dma_shared_mem mem_cmd = { 0 };
+	int result = 0;
 	u32 offset = 0;
 	size_t tmp;
 
@@ -412,21 +446,22 @@
 
 	memset(&desc, 0, sizeof(desc));
 	/* NO-OP IC for ensuring that IPA pipeline is empty */
-	nop_cmd_pyld =
+	cmd_pyld[num_cmd] =
 		ipahal_construct_nop_imm_cmd(false, IPAHAL_HPS_CLEAR, false);
-	if (!nop_cmd_pyld) {
+	if (!cmd_pyld[num_cmd]) {
 		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;
+	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++;
 
 	if (ipa3_ctx->nat_mem.vaddr) {
 		IPADBG("using system memory for nat table\n");
@@ -453,7 +488,7 @@
 			IPAERR("index_expn_offset: 0x%x\n",
 				init->index_expn_offset);
 			result = -EPERM;
-			goto free_nop;
+			goto destroy_imm_cmd;
 		}
 		cmd.ipv4_rules_addr =
 			ipa3_ctx->nat_mem.dma_handle + init->ipv4_rules_offset;
@@ -495,25 +530,75 @@
 	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);
-	cmd.public_ip_addr = init->ip_addr;
-	IPADBG("Public ip address:0x%x\n", cmd.public_ip_addr);
-	cmd_pyld = ipahal_construct_imm_cmd(
+	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);
+	}
+	cmd_pyld[num_cmd] = ipahal_construct_imm_cmd(
 		IPA_IMM_CMD_IP_V4_NAT_INIT, &cmd, false);
-	if (!cmd_pyld) {
+	if (!cmd_pyld[num_cmd]) {
 		IPAERR("Fail to construct ip_v4_nat_init imm cmd\n");
 		result = -EPERM;
-		goto free_nop;
+		goto destroy_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;
+	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++;
+
+	if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0) {
+		struct ipa_pdn_entry *pdn_entries;
+
+		/* store ip in pdn entries cache array */
+		pdn_entries = ipa3_ctx->nat_mem.pdn_mem.base;
+		pdn_entries[0].public_ip = init->ip_addr;
+		pdn_entries[0].dst_metadata = 0;
+		pdn_entries[0].src_metadata = 0;
+		pdn_entries[0].resrvd = 0;
+
+		IPADBG("Public ip address:0x%x\n", init->ip_addr);
+
+		/* Copy the PDN config table to SRAM */
+		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 = 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[num_cmd] = ipahal_construct_imm_cmd(
+			IPA_IMM_CMD_DMA_SHARED_MEM, &mem_cmd, false);
+		if (!cmd_pyld[num_cmd]) {
+			IPAERR(
+			"fail construct dma_shared_mem cmd: for pdn table");
+			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++;
+		IPADBG("added PDN table copy cmd\n");
+	}
+
 	IPADBG("posting v4 init command\n");
-	if (ipa3_send_cmd(2, desc)) {
+	if (ipa3_send_cmd(num_cmd, desc)) {
 		IPAERR("Fail to send immediate command\n");
 		result = -EPERM;
 		goto destroy_imm_cmd;
@@ -550,16 +635,97 @@
 	ipa3_ctx->nat_mem.size_expansion_tables = init->expn_table_entries;
 
 	IPADBG("return\n");
-	result = 0;
 destroy_imm_cmd:
-	ipahal_destroy_imm_cmd(cmd_pyld);
-free_nop:
-	ipahal_destroy_imm_cmd(nop_cmd_pyld);
+	for (i = 0; i < num_cmd; i++)
+		ipahal_destroy_imm_cmd(cmd_pyld[i]);
 bail:
 	return result;
 }
 
 /**
+* ipa4_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 ipa4_nat_mdfy_pdn(struct ipa_ioc_nat_pdn_entry *mdfy_pdn)
+{
+	struct ipahal_imm_cmd_dma_shared_mem mem_cmd = { 0 };
+	struct ipa3_desc desc;
+	struct ipahal_imm_cmd_pyld *cmd_pyld;
+	int result = 0;
+	struct ipa3_nat_mem *nat_ctx = &(ipa3_ctx->nat_mem);
+	struct ipa_pdn_entry *pdn_entries = nat_ctx->pdn_mem.base;
+
+	if (ipa3_ctx->ipa_hw_type < IPA_HW_v4_0) {
+		IPAERR("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");
+		return -EPERM;
+	}
+
+	if (mdfy_pdn->pdn_index > (IPA_MAX_PDN_NUM - 1)) {
+		IPAERR("pdn index out of range %d\n", mdfy_pdn->pdn_index);
+		return -EPERM;
+	}
+
+	mutex_lock(&nat_ctx->lock);
+
+	/* store ip in pdn entries cache array */
+	pdn_entries[mdfy_pdn->pdn_index].public_ip =
+		mdfy_pdn->public_ip;
+	pdn_entries[mdfy_pdn->pdn_index].dst_metadata =
+		mdfy_pdn->dst_metadata;
+	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));
+
+	/* Copy the PDN config table to SRAM */
+	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.local_addr = ipa3_ctx->smem_restricted_bytes +
+		IPA_MEM_PART(pdn_config_ofst);
+	cmd_pyld = ipahal_construct_imm_cmd(
+		IPA_IMM_CMD_DMA_SHARED_MEM, &mem_cmd, false);
+	if (!cmd_pyld) {
+		IPAERR(
+			"fail construct dma_shared_mem cmd: for pdn table");
+		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;
+
+	IPADBG("sending PDN table copy cmd");
+	if (ipa3_send_cmd(1, &desc)) {
+		IPAERR("Fail to send immediate command\n");
+		result = -EPERM;
+	}
+
+	ipahal_destroy_imm_cmd(cmd_pyld);
+bail:
+	mutex_unlock(&nat_ctx->lock);
+	return result;
+}
+/**
  * ipa3_nat_dma_cmd() - Post NAT_DMA command to IPA HW
  * @dma:	[in] initialization command attributes
  *
@@ -573,6 +739,7 @@
 
 	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;
@@ -675,13 +842,16 @@
 	desc[0].pyld = nop_cmd_pyld->data;
 	desc[0].len = nop_cmd_pyld->len;
 
+	/* 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 = ipahal_construct_imm_cmd(
-			IPA_IMM_CMD_NAT_DMA, &cmd, false);
+		cmd_pyld = ipahal_construct_imm_cmd(cmd_name, &cmd, false);
 		if (!cmd_pyld) {
 			IPAERR("Fail to construct nat_dma imm cmd\n");
 			continue;
@@ -718,6 +888,10 @@
  */
 void ipa3_nat_free_mem_and_device(struct ipa3_nat_mem *nat_ctx)
 {
+	struct ipahal_imm_cmd_dma_shared_mem mem_cmd = { 0 };
+	struct ipa3_desc desc;
+	struct ipahal_imm_cmd_pyld *cmd_pyld;
+
 	IPADBG("\n");
 	mutex_lock(&nat_ctx->lock);
 
@@ -729,6 +903,47 @@
 		nat_ctx->size = 0;
 		nat_ctx->vaddr = NULL;
 	}
+
+	if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0) {
+		struct ipa_pdn_entry *pdn_entries =
+			nat_ctx->pdn_mem.base;
+
+		/* 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);
+		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.local_addr = ipa3_ctx->smem_restricted_bytes +
+			IPA_MEM_PART(pdn_config_ofst);
+		cmd_pyld = ipahal_construct_imm_cmd(
+			IPA_IMM_CMD_DMA_SHARED_MEM, &mem_cmd, false);
+		if (!cmd_pyld) {
+			IPAERR(
+				"fail construct dma_shared_mem cmd: for pdn table");
+			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;
+
+		IPADBG("sending PDN table copy cmd\n");
+		if (ipa3_send_cmd(1, &desc))
+			IPAERR("Fail to send immediate command\n");
+
+		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);
+	}
 	nat_ctx->is_mapped = false;
 	nat_ctx->is_sys_mem = false;
 	nat_ctx->is_dev_init = false;
@@ -762,7 +977,8 @@
 		base_addr = ipa3_ctx->nat_mem.tmp_dma_handle;
 	}
 
-	if (del->public_ip_addr == 0) {
+	if ((ipa3_ctx->ipa_hw_type < IPA_HW_v4_0) &&
+		(del->public_ip_addr == 0)) {
 		IPADBG("Bad Parameter\n");
 		result = -EPERM;
 		goto bail;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.c b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.c
index d35b8a7..6f46ebf 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.c
@@ -32,6 +32,7 @@
 	__stringify(IPA_IMM_CMD_DMA_SHARED_MEM),
 	__stringify(IPA_IMM_CMD_IP_PACKET_TAG_STATUS),
 	__stringify(IPA_IMM_CMD_DMA_TASK_32B_ADDR),
+	__stringify(IPA_IMM_CMD_TABLE_DMA),
 };
 
 static const char *ipahal_pkt_status_exception_to_str
@@ -371,6 +372,31 @@
 	return pyld;
 }
 
+static struct ipahal_imm_cmd_pyld *ipa_imm_cmd_construct_table_dma_ipav4(
+	enum ipahal_imm_cmd_name cmd, const void *params, bool is_atomic_ctx)
+{
+	struct ipahal_imm_cmd_pyld *pyld;
+	struct ipa_imm_cmd_hw_table_dma_ipav4 *data;
+	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)) {
+		IPAHAL_ERR("kzalloc err\n");
+		return pyld;
+	}
+	pyld->opcode = ipahal_imm_cmd_get_opcode(cmd);
+	pyld->len = sizeof(*data);
+	data = (struct ipa_imm_cmd_hw_table_dma_ipav4 *)pyld->data;
+
+	data->table_index = nat_params->table_index;
+	data->base_addr = nat_params->base_addr;
+	data->offset = nat_params->offset;
+	data->data = nat_params->data;
+
+	return pyld;
+}
+
 static struct ipahal_imm_cmd_pyld *ipa_imm_cmd_construct_hdr_init_system(
 	enum ipahal_imm_cmd_name cmd, const void *params, bool is_atomic_ctx)
 {
@@ -640,6 +666,13 @@
 	[IPA_HW_v4_0][IPA_IMM_CMD_REGISTER_WRITE] = {
 		ipa_imm_cmd_construct_register_write_v_4_0,
 		12},
+	/* NAT_DMA was renamed to TABLE_DMA for IPAv4 */
+	[IPA_HW_v4_0][IPA_IMM_CMD_NAT_DMA] = {
+		NULL,
+		-1 },
+	[IPA_HW_v4_0][IPA_IMM_CMD_TABLE_DMA] = {
+		ipa_imm_cmd_construct_table_dma_ipav4,
+		14},
 	[IPA_HW_v4_0][IPA_IMM_CMD_DMA_SHARED_MEM] = {
 		ipa_imm_cmd_construct_dma_shared_mem_v_4_0,
 		19},
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.h b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.h
index e71a48b..f8bdc2c 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.h
@@ -36,6 +36,7 @@
 	IPA_IMM_CMD_DMA_SHARED_MEM,
 	IPA_IMM_CMD_IP_PACKET_TAG_STATUS,
 	IPA_IMM_CMD_DMA_TASK_32B_ADDR,
+	IPA_IMM_CMD_TABLE_DMA,
 	IPA_IMM_CMD_MAX,
 };
 
@@ -204,6 +205,23 @@
 };
 
 /*
+ * 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
+ * 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
+ */
+struct ipahal_imm_cmd_table_dma {
+	u8 table_index;
+	u8 base_addr;
+	u32 offset;
+	u16 data;
+};
+
+/*
  * struct ipahal_imm_cmd_ip_packet_init - IP_PACKET_INIT cmd payload
  * Configuration for specific IP pkt. Shall be called prior to an IP pkt
  *  data. Pkt will not go through IP pkt processing.
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt.c b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt.c
index 053a581..4f20e0f 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt.c
@@ -162,6 +162,8 @@
 		struct ipahal_rt_rule_entry *rule);
 static int ipa_flt_parse_hw_rule(u8 *addr,
 		struct ipahal_flt_rule_entry *rule);
+static int ipa_flt_parse_hw_rule_ipav4(u8 *addr,
+		struct ipahal_flt_rule_entry *rule);
 
 #define IPA_IS_RAN_OUT_OF_EQ(__eq_array, __eq_index) \
 	(ARRAY_SIZE(__eq_array) <= (__eq_index))
@@ -349,6 +351,93 @@
 	return 0;
 }
 
+static int ipa_flt_gen_hw_rule_ipav4(struct ipahal_flt_rule_gen_params *params,
+	u32 *hw_len, u8 *buf)
+{
+	struct ipa4_0_flt_rule_hw_hdr *rule_hdr;
+	u8 *start;
+	u16 en_rule = 0;
+
+	start = buf;
+	rule_hdr = (struct ipa4_0_flt_rule_hw_hdr *)buf;
+
+	switch (params->rule->action) {
+	case IPA_PASS_TO_ROUTING:
+		rule_hdr->u.hdr.action = 0x0;
+		break;
+	case IPA_PASS_TO_SRC_NAT:
+		rule_hdr->u.hdr.action = 0x1;
+		break;
+	case IPA_PASS_TO_DST_NAT:
+		rule_hdr->u.hdr.action = 0x2;
+		break;
+	case IPA_PASS_TO_EXCEPTION:
+		rule_hdr->u.hdr.action = 0x3;
+		break;
+	default:
+		IPAHAL_ERR("Invalid Rule Action %d\n", params->rule->action);
+		WARN_ON(1);
+		return -EINVAL;
+	}
+
+	ipa_assert_on(params->rt_tbl_idx & ~0x1F);
+	rule_hdr->u.hdr.rt_tbl_idx = params->rt_tbl_idx;
+	rule_hdr->u.hdr.retain_hdr = params->rule->retain_hdr ? 0x1 : 0x0;
+
+	ipa_assert_on(params->rule->pdn_idx & ~0xF);
+	rule_hdr->u.hdr.pdn_idx = params->rule->pdn_idx;
+	rule_hdr->u.hdr.set_metadata = params->rule->set_metadata;
+	rule_hdr->u.hdr.rsvd2 = 0;
+	rule_hdr->u.hdr.rsvd3 = 0;
+
+	ipa_assert_on(params->priority & ~0x3FF);
+	rule_hdr->u.hdr.priority = params->priority;
+	ipa_assert_on(params->id & ~((1 << IPA3_0_RULE_ID_BIT_LEN) - 1));
+	ipa_assert_on(params->id == ((1 << IPA3_0_RULE_ID_BIT_LEN) - 1));
+	rule_hdr->u.hdr.rule_id = params->id;
+
+	buf += sizeof(struct ipa4_0_flt_rule_hw_hdr);
+
+	if (params->rule->eq_attrib_type) {
+		if (ipa_fltrt_generate_hw_rule_bdy_from_eq(
+			&params->rule->eq_attrib, &buf)) {
+			IPAHAL_ERR("fail to generate hw rule from eq\n");
+			return -EPERM;
+		}
+		en_rule = params->rule->eq_attrib.rule_eq_bitmap;
+	} else {
+		if (ipa_fltrt_generate_hw_rule_bdy(params->ipt,
+			&params->rule->attrib, &buf, &en_rule)) {
+			IPAHAL_ERR("fail to generate hw rule\n");
+			return -EPERM;
+		}
+	}
+	rule_hdr->u.hdr.en_rule = en_rule;
+
+	IPAHAL_DBG_LOW("en_rule=0x%x, action=%d, rt_idx=%d, retain_hdr=%d\n",
+		en_rule,
+		rule_hdr->u.hdr.action,
+		rule_hdr->u.hdr.rt_tbl_idx,
+		rule_hdr->u.hdr.retain_hdr);
+	IPAHAL_DBG_LOW("priority=%d, rule_id=%d, pdn=%d, set_metadata=%d\n",
+		rule_hdr->u.hdr.priority,
+		rule_hdr->u.hdr.rule_id,
+		rule_hdr->u.hdr.pdn_idx,
+		rule_hdr->u.hdr.set_metadata);
+
+	ipa_write_64(rule_hdr->u.word, (u8 *)rule_hdr);
+
+	if (*hw_len == 0) {
+		*hw_len = buf - start;
+	} else if (*hw_len != (buf - start)) {
+		IPAHAL_ERR("hw_len differs b/w passed=0x%x calc=%td\n",
+			*hw_len, (buf - start));
+		return -EPERM;
+	}
+
+	return 0;
+}
+
 /*
  * This array contains the FLT/RT info for IPAv3 and later.
  * All the information on IPAv3 are statically defined below.
@@ -401,6 +490,50 @@
 			[IPA_IS_FRAG]			= 15,
 		},
 	},
+
+	/* IPAv4 */
+	[IPA_HW_v4_0] = {
+		true,
+		IPA3_0_HW_TBL_WIDTH,
+		IPA3_0_HW_TBL_SYSADDR_ALIGNMENT,
+		IPA3_0_HW_TBL_LCLADDR_ALIGNMENT,
+		IPA3_0_HW_TBL_BLK_SIZE_ALIGNMENT,
+		IPA3_0_HW_RULE_START_ALIGNMENT,
+		IPA3_0_HW_TBL_HDR_WIDTH,
+		IPA3_0_HW_TBL_ADDR_MASK,
+		IPA3_0_RULE_MAX_PRIORITY,
+		IPA3_0_RULE_MIN_PRIORITY,
+		IPA3_0_LOW_RULE_ID,
+		IPA3_0_RULE_ID_BIT_LEN,
+		IPA3_0_HW_RULE_BUF_SIZE,
+		ipa_write_64,
+		ipa_fltrt_create_flt_bitmap,
+		ipa_fltrt_create_tbl_addr,
+		ipa_fltrt_parse_tbl_addr,
+		ipa_rt_gen_hw_rule,
+		ipa_flt_gen_hw_rule_ipav4,
+		ipa_flt_generate_eq,
+		ipa_rt_parse_hw_rule,
+		ipa_flt_parse_hw_rule_ipav4,
+		{
+			[IPA_TOS_EQ] = 0,
+			[IPA_PROTOCOL_EQ] = 1,
+			[IPA_TC_EQ] = 2,
+			[IPA_OFFSET_MEQ128_0] = 3,
+			[IPA_OFFSET_MEQ128_1] = 4,
+			[IPA_OFFSET_MEQ32_0] = 5,
+			[IPA_OFFSET_MEQ32_1] = 6,
+			[IPA_IHL_OFFSET_MEQ32_0] = 7,
+			[IPA_IHL_OFFSET_MEQ32_1] = 8,
+			[IPA_METADATA_COMPARE] = 9,
+			[IPA_IHL_OFFSET_RANGE16_0] = 10,
+			[IPA_IHL_OFFSET_RANGE16_1] = 11,
+			[IPA_IHL_OFFSET_EQ_32] = 12,
+			[IPA_IHL_OFFSET_EQ_16] = 13,
+			[IPA_FL_EQ] = 14,
+			[IPA_IS_FRAG] = 15,
+		},
+	},
 };
 
 static int ipa_flt_generate_eq(enum ipa_ip_type ipt,
@@ -2276,6 +2409,55 @@
 		atrb, &rule->rule_size);
 }
 
+static int ipa_flt_parse_hw_rule_ipav4(u8 *addr,
+	struct ipahal_flt_rule_entry *rule)
+{
+	struct ipa4_0_flt_rule_hw_hdr *rule_hdr;
+	struct ipa_ipfltri_rule_eq *atrb;
+
+	IPAHAL_DBG_LOW("Entry\n");
+
+	rule_hdr = (struct ipa4_0_flt_rule_hw_hdr *)addr;
+	atrb = &rule->rule.eq_attrib;
+
+	if (rule_hdr->u.word == 0) {
+		/* table termintator - empty table */
+		rule->rule_size = 0;
+		return 0;
+	}
+
+	switch (rule_hdr->u.hdr.action) {
+	case 0x0:
+		rule->rule.action = IPA_PASS_TO_ROUTING;
+		break;
+	case 0x1:
+		rule->rule.action = IPA_PASS_TO_SRC_NAT;
+		break;
+	case 0x2:
+		rule->rule.action = IPA_PASS_TO_DST_NAT;
+		break;
+	case 0x3:
+		rule->rule.action = IPA_PASS_TO_EXCEPTION;
+		break;
+	default:
+		IPAHAL_ERR("Invalid Rule Action %d\n", rule_hdr->u.hdr.action);
+		WARN_ON(1);
+		rule->rule.action = rule_hdr->u.hdr.action;
+	}
+
+	rule->rule.rt_tbl_idx = rule_hdr->u.hdr.rt_tbl_idx;
+	rule->rule.retain_hdr = rule_hdr->u.hdr.retain_hdr;
+	rule->priority = rule_hdr->u.hdr.priority;
+	rule->id = rule_hdr->u.hdr.rule_id;
+	rule->rule.pdn_idx = rule_hdr->u.hdr.pdn_idx;
+	rule->rule.set_metadata = rule_hdr->u.hdr.set_metadata;
+
+	atrb->rule_eq_bitmap = rule_hdr->u.hdr.en_rule;
+	rule->rule.eq_attrib_type = 1;
+	return ipa_fltrt_parse_hw_rule_eq(addr, sizeof(*rule_hdr),
+		atrb, &rule->rule_size);
+}
+
 /*
  * ipahal_fltrt_init() - Build the FLT/RT information table
  *  See ipahal_fltrt_objs[] comments
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt_i.h b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt_i.h
index 0c0637d..645383a 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt_i.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt_i.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-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
@@ -137,6 +137,43 @@
 	} u;
 };
 
+/**
+ * struct ipa4_0_flt_rule_hw_hdr - HW header of IPA filter rule
+ * @word: filtering rule properties
+ * @en_rule: enable rule
+ * @action: post filtering action
+ * @rt_tbl_idx: index in routing table
+ * @retain_hdr: added to add back to the packet the header removed
+ *  as part of header removal. This will be done as part of
+ *  header insertion block.
+ * @pdn_idx: in case of go to src nat action possible to input the pdn index to
+ *  the NAT block
+ * @set_metadata: enable metadata replacement in the NAT block
+ * @priority: Rule priority. Added to distinguish rules order
+ *  at the integrated table consisting from hashable and
+ *  non-hashable parts
+ * @rsvd2: reserved bits
+ * @rule_id: rule ID that will be returned in the packet status
+ * @rsvd3: reserved bits
+ */
+struct ipa4_0_flt_rule_hw_hdr {
+	union {
+		u64 word;
+		struct {
+			u64 en_rule : 16;
+			u64 action : 5;
+			u64 rt_tbl_idx : 5;
+			u64 retain_hdr : 1;
+			u64 pdn_idx : 4;
+			u64 set_metadata : 1;
+			u64 priority : 10;
+			u64 rsvd2 : 6;
+			u64 rule_id : 10;
+			u64 rsvd3 : 6;
+		} hdr;
+	} u;
+};
+
 int ipahal_fltrt_init(enum ipa_hw_type ipa_hw_type);
 void ipahal_fltrt_destroy(void);
 
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 804c554..c023082 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_i.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_i.h
@@ -143,7 +143,8 @@
  * @size_expansion_tables: Num of entries in NAT expantion tbl and expantion
  *  idx tbl (each)
  * @rsvd2: reserved
- * @public_ip_addr: public IP address
+ * @public_ip_addr: public IP address. for IPAv4 this is the PDN config table
+ *  offset in SMEM
  */
 struct ipa_imm_cmd_hw_ip_v4_nat_init {
 	u64 ipv4_rules_addr:64;
@@ -250,6 +251,30 @@
 };
 
 /*
+ * struct ipa_imm_cmd_hw_table_dma_ipav4 - TABLE_DMA command payload
+ *  in H/W format
+ * Perform DMA operation on NAT and ipv6 connection tracking related mem
+ * addresses. 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.
+ * @rsvd1: reserved
+ * @base_addr: Base addr to which the DMA operation should be performed.
+ * @rsvd2: reserved
+ * @offset: offset in bytes from base addr to write 'data' to
+ * @data: data to be written
+ * @rsvd3: reserved
+ */
+struct ipa_imm_cmd_hw_table_dma_ipav4 {
+	u64 table_index : 3;
+	u64 rsvd1 : 1;
+	u64 base_addr : 3;
+	u64 rsvd2 : 1;
+	u64 offset : 32;
+	u64 data : 16;
+	u64 rsvd3 : 8;
+};
+
+/*
  * struct ipa_imm_cmd_hw_hdr_init_system - HDR_INIT_SYSTEM command payload
  *  in H/W format.
  * Inits hdr table within sys mem with the hdrs and their length.
diff --git a/include/uapi/linux/msm_ipa.h b/include/uapi/linux/msm_ipa.h
index 57c2ca4..9773480 100644
--- a/include/uapi/linux/msm_ipa.h
+++ b/include/uapi/linux/msm_ipa.h
@@ -68,7 +68,9 @@
 #define IPA_IOCTL_ADD_RT_RULE_AFTER 43
 #define IPA_IOCTL_ADD_FLT_RULE_AFTER 44
 #define IPA_IOCTL_GET_HW_VERSION 45
-#define IPA_IOCTL_MAX 46
+#define IPA_IOCTL_ADD_RT_RULE_EXT 46
+#define IPA_IOCTL_NAT_MODIFY_PDN 47
+#define IPA_IOCTL_MAX 48
 
 /**
  * max size of the header to be inserted
@@ -127,6 +129,11 @@
 #define IPA_FLT_MAC_ETHER_TYPE		(1ul << 21)
 
 /**
+ * maximal number of NAT PDNs in the PDN config table
+ */
+#define IPA_MAX_PDN_NUM 5
+
+/**
  * enum ipa_client_type - names for the various IPA "clients"
  * these are from the perspective of the clients, for e.g.
  * HSIC1_PROD means HSIC client is the producer and IPA is the
@@ -718,6 +725,11 @@
  *  consecutive packets
  * @rule_id: rule_id to be assigned to the filter rule. In case client specifies
  *  rule_id as 0 the driver will assign a new rule_id
+ * @set_metadata: bool switch. should metadata replacement at the NAT block
+ *  take place?
+ * @pdn_idx: if action is "pass to source\destination NAT" then a comparison
+ * against the PDN index in the matching PDN entry will take place as an
+ * additional condition for NAT hit.
  */
 struct ipa_flt_rule {
 	uint8_t retain_hdr;
@@ -731,6 +743,8 @@
 	uint8_t max_prio;
 	uint8_t hashable;
 	uint16_t rule_id;
+	uint8_t set_metadata;
+	uint8_t pdn_idx;
 };
 
 /**
@@ -1407,6 +1421,20 @@
 };
 
 /**
+* struct ipa_ioc_nat_pdn_entry - PDN entry modification data
+* @pdn_index: index of the entry in the PDN config table to be changed
+* @public_ip: PDN's public ip
+* @src_metadata: PDN's source NAT metadata for metadata replacement
+* @dst_metadata: PDN's destination NAT metadata for metadata replacement
+*/
+struct ipa_ioc_nat_pdn_entry {
+	uint8_t pdn_index;
+	uint32_t public_ip;
+	uint32_t src_metadata;
+	uint32_t dst_metadata;
+};
+
+/**
  * struct ipa_msg_meta - Format of the message meta-data.
  * @msg_type: the type of the message
  * @rsvd: reserved bits for future use.
@@ -1628,6 +1656,9 @@
 #define IPA_IOC_GET_NAT_OFFSET _IOWR(IPA_IOC_MAGIC, \
 				IPA_IOCTL_GET_NAT_OFFSET, \
 				uint32_t *)
+#define IPA_IOC_NAT_MODIFY_PDN _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_NAT_MODIFY_PDN, \
+				struct ipa_ioc_nat_pdn_entry *)
 #define IPA_IOC_SET_FLT _IOW(IPA_IOC_MAGIC, \
 			IPA_IOCTL_SET_FLT, \
 			uint32_t)