Merge "msm: ipa: Add NAT and IPv6 CT suppression tests"
diff --git a/kernel-tests/IPv6CTTest.cpp b/kernel-tests/IPv6CTTest.cpp
index 207b03f..e1bbfdf 100644
--- a/kernel-tests/IPv6CTTest.cpp
+++ b/kernel-tests/IPv6CTTest.cpp
@@ -114,7 +114,7 @@
 		m_testSuiteName.push_back("IPv6CT");
 	}
 
-	static int SetupKernelModule(bool en_status = false)
+	static int SetupKernelModule(bool en_status = false, bool ct_suppress = false)
 	{
 		printf("Entering %s, %s()\n", __FUNCTION__, __FILE__);
 		int retval;
@@ -157,6 +157,7 @@
 
 		/* To ipa configurations - 1 pipes */
 		memset(&to_ipa_cfg[0], 0, sizeof(to_ipa_cfg[0]));
+		to_ipa_cfg[0].nat.nat_exc_suppress = ct_suppress;
 		prepare_channel_struct(&to_ipa_channels[0],
 			header.to_ipa_channels_num++,
 			IPA_CLIENT_TEST_PROD,
@@ -172,11 +173,11 @@
 		return retval;
 	}
 
-	bool Setup()
+	bool Setup(bool en_status = false, bool ct_suppress = false)
 	{
 		bool bRetVal = true;
 
-		if (SetupKernelModule() != true)
+		if (SetupKernelModule(en_status,ct_suppress) != true)
 			return bRetVal;
 
 		m_producer.Open(INTERFACE0_TO_IPA_DATA_PATH, INTERFACE0_FROM_IPA_DATA_PATH);
@@ -1725,6 +1726,652 @@
 	}
 };
 
+/*---------------------------------------------------------------------------------------------*/
+/* Test019: IPv6CT send outbound packet, suppression test  */
+/*---------------------------------------------------------------------------------------------*/
+class IpaIPV6CTBlockTest019 : public IpaIPv6CTBlockTestFixture
+{
+public:
+
+	IpaIPV6CTBlockTest019()
+	{
+		m_name = "IpaIPV6CTBlockTest019";
+		m_description =
+			"IPv6CT block test 019 - IPv6CT passes successfully one packet in outbound direction\n"
+			"1. Generate and commit three routing tables.\n"
+			"   Each table contains a single \"bypass\" rule (all data goes to output pipe 0, 1 and 2 (accordingly))\n"
+			"2. Generate and commit one outbound filtering rule: Destination IP Exactly Match.\n"
+			"3. Add IPv6CT rule for the packet which doesn't match\n"
+			"4. Expect NAT supporession to kick in and packet is routed correctly\n";
+		Register(*this);
+	}
+
+	virtual bool Setup()
+	{
+		/* we want statuses on this test */
+		return IpaIPv6CTBlockTestFixture::Setup(false, true);
+	}
+
+	virtual bool AddRoutingFilteringRules(enum ipa_flt_action flt_action, uint64_t dst_addr_msb, uint64_t dst_addr_lsb)
+	{
+		printf("Entering %s, %s()\n", __FUNCTION__, __FILE__);
+		const char bypass0[20] = "Bypass0";
+		const char bypass1[20] = "Bypass1";
+		const char bypass2[20] = "Bypass2";
+
+		if (!CreateThreeIPv6BypassRoutingTables(bypass0, bypass1, bypass2))
+		{
+			printf("CreateThreeBypassRoutingTables Failed\n");
+			return false;
+		}
+		printf("CreateThreeBypassRoutingTables completed successfully\n");
+
+		ipa_ioc_get_rt_tbl routing_table0;
+		routing_table0.ip = IPA_IP_v6;
+		strlcpy(routing_table0.name, bypass0, sizeof(routing_table0.name));
+		if (!m_routing.GetRoutingTable(&routing_table0))
+		{
+			printf("m_routing.GetRoutingTable(&routing_table0=0x%pK) Failed.\n", &routing_table0);
+			return false;
+		}
+
+		/* Setup conntrack exception routing table. */
+		if (!m_routing.SetNatConntrackExcRoutingTable(routing_table0.hdl, false))
+		{
+			LOG_MSG_ERROR("m_routing.SetNatConntrackExcRoutingTable(routing_table0 hdl=%d) Failed.\n",
+				routing_table0.hdl);
+			return false;
+		}
+
+		ipa_ioc_get_rt_tbl routing_table1;
+		routing_table1.ip = IPA_IP_v6;
+		strlcpy(routing_table1.name, bypass1, sizeof(routing_table1.name));
+		if (!m_routing.GetRoutingTable(&routing_table1))
+		{
+			printf("m_routing.GetRoutingTable(&routing_table1=0x%pK) Failed.\n", &routing_table1);
+			return false;
+		}
+
+		IPAFilteringTable FilterTable0;
+		ipa_flt_rule_add flt_rule_entry;
+		FilterTable0.Init(IPA_IP_v6, IPA_CLIENT_TEST_PROD, false, 1);
+
+		// Configuring Filtering Rule No.0
+		FilterTable0.GeneratePresetRule(1, flt_rule_entry);
+		flt_rule_entry.at_rear = true;
+		flt_rule_entry.flt_rule_hdl = -1; // return Value
+		flt_rule_entry.status = -1; // return value
+		flt_rule_entry.rule.action = flt_action;
+		flt_rule_entry.rule.rt_tbl_hdl = routing_table0.hdl; //put here the handle corresponding to Routing Rule 1
+		flt_rule_entry.rule.attrib.attrib_mask = IPA_FLT_DST_ADDR;
+
+		flt_rule_entry.rule.attrib.u.v6.dst_addr_mask[0] = 0xFFFFFFFF;// Exact Match
+		flt_rule_entry.rule.attrib.u.v6.dst_addr_mask[1] = 0xFFFFFFFF;// Exact Match
+		flt_rule_entry.rule.attrib.u.v6.dst_addr_mask[2] = 0xFFFFFFFF;// Exact Match
+		flt_rule_entry.rule.attrib.u.v6.dst_addr_mask[3] = 0xFFFFFFFF;// Exact Match
+		flt_rule_entry.rule.attrib.u.v6.dst_addr[0] = GetHigh32(dst_addr_msb); // Filter DST_IP
+		flt_rule_entry.rule.attrib.u.v6.dst_addr[1] = GetLow32(dst_addr_msb);
+		flt_rule_entry.rule.attrib.u.v6.dst_addr[2] = GetHigh32(dst_addr_lsb);
+		flt_rule_entry.rule.attrib.u.v6.dst_addr[3] = GetLow32(dst_addr_lsb);
+
+		printf("flt_rule_entry was set successfully, preparing for insertion....\n");
+
+		if (((uint8_t)-1 == FilterTable0.AddRuleToTable(flt_rule_entry)) ||
+			!m_filtering.AddFilteringRule(FilterTable0.GetFilteringTable()))
+		{
+			printf("%s::Error Adding Rule to Filter Table, aborting...\n", __FUNCTION__);
+			return false;
+		}
+		else
+		{
+			printf("flt rule hdl0=0x%x, status=0x%x\n",
+				FilterTable0.ReadRuleFromTable(0)->flt_rule_hdl, FilterTable0.ReadRuleFromTable(0)->status);
+		}
+
+		printf("Leaving %s, %s()\n", __FUNCTION__, __FILE__);
+		return true;
+	}// AddRoutingFilteringRules()
+
+	virtual bool AddRoutingFilteringRules()
+	{
+		printf("Entering %s, %s()\n", __FUNCTION__, __FILE__);
+		bool result = AddRoutingFilteringRules(IPA_PASS_TO_SRC_NAT,
+			m_outbound_dst_addr_msb, m_outbound_dst_addr_lsb);
+		printf("Leaving %s, %s()\n", __FUNCTION__, __FILE__);
+		return result;
+	}// AddRoutingFilteringRules()
+
+	virtual bool ModifyPackets()
+	{
+		printf("Entering %s, %s()\n", __FUNCTION__, __FILE__);
+		IpaIPv6CTBlockTestFixture::ModifyPackets(m_outbound_dst_addr_lsb, m_outbound_dst_addr_msb, m_outbound_dst_port,
+			m_outbound_src_addr_lsb, m_outbound_src_addr_msb, m_outbound_src_port+1);
+		printf("Leaving %s, %s()\n", __FUNCTION__, __FILE__);
+		return true;
+	}// ModifyPackets()
+
+	virtual bool ReceivePacketsAndCompare()
+	{
+		printf("Entering %s, %s()\n", __FUNCTION__, __FILE__);
+		bool result = IpaIPv6CTBlockTestFixture::ReceivePacketsAndCompare(true);
+		printf("Leaving %s, %s()\n", __FUNCTION__, __FILE__);
+		return result;
+	}
+};
+
+/*---------------------------------------------------------------------------------------------*/
+/* Test020: IPv6CT send inbound packet for NAT suppression test */
+/*---------------------------------------------------------------------------------------------*/
+class IpaIPV6CTBlockTest020 : public IpaIPv6CTBlockTestFixture
+{
+public:
+
+	IpaIPV6CTBlockTest020()
+	{
+		m_name = "IpaIPV6CTBlockTest020";
+		m_description =
+			"IPv6CT block test 020 - IPv6CT passes successfully one packet in inbound direction on NAT suppression\n"
+			"1. Generate and commit three routing tables.\n"
+			"   Each table contains a single \"bypass\" rule (all data goes to output pipe 0, 1 and 2 (accordingly))\n"
+			"2. Generate and commit one inbound filtering rule: Destination IP Exactly Match.\n"
+			"3. Add IPv6CT rule for the packet\n";
+		Register(*this);
+	}
+
+	virtual bool Setup()
+	{
+		/* we want statuses on this test */
+		return IpaIPv6CTBlockTestFixture::Setup(false, true);
+	}
+
+	virtual bool AddRoutingFilteringRules(enum ipa_flt_action flt_action, uint64_t dst_addr_msb, uint64_t dst_addr_lsb)
+	{
+		printf("Entering %s, %s()\n", __FUNCTION__, __FILE__);
+		const char bypass0[20] = "Bypass0";
+		const char bypass1[20] = "Bypass1";
+		const char bypass2[20] = "Bypass2";
+
+		if (!CreateThreeIPv6BypassRoutingTables(bypass0, bypass1, bypass2))
+		{
+			printf("CreateThreeBypassRoutingTables Failed\n");
+			return false;
+		}
+		printf("CreateThreeBypassRoutingTables completed successfully\n");
+
+		ipa_ioc_get_rt_tbl routing_table0;
+		routing_table0.ip = IPA_IP_v6;
+		strlcpy(routing_table0.name, bypass0, sizeof(routing_table0.name));
+		if (!m_routing.GetRoutingTable(&routing_table0))
+		{
+			printf("m_routing.GetRoutingTable(&routing_table0=0x%pK) Failed.\n", &routing_table0);
+			return false;
+		}
+
+		/* Setup conntrack exception routing table. */
+		if (!m_routing.SetNatConntrackExcRoutingTable(routing_table0.hdl, false))
+		{
+			LOG_MSG_ERROR("m_routing.SetNatConntrackExcRoutingTable(routing_table0 hdl=%d) Failed.\n",
+				routing_table0.hdl);
+			return false;
+		}
+
+		ipa_ioc_get_rt_tbl routing_table1;
+		routing_table1.ip = IPA_IP_v6;
+		strlcpy(routing_table1.name, bypass1, sizeof(routing_table1.name));
+		if (!m_routing.GetRoutingTable(&routing_table1))
+		{
+			printf("m_routing.GetRoutingTable(&routing_table1=0x%pK) Failed.\n", &routing_table1);
+			return false;
+		}
+
+		IPAFilteringTable FilterTable0;
+		ipa_flt_rule_add flt_rule_entry;
+		FilterTable0.Init(IPA_IP_v6, IPA_CLIENT_TEST_PROD, false, 1);
+
+		// Configuring Filtering Rule No.0
+		FilterTable0.GeneratePresetRule(1, flt_rule_entry);
+		flt_rule_entry.at_rear = true;
+		flt_rule_entry.flt_rule_hdl = -1; // return Value
+		flt_rule_entry.status = -1; // return value
+		flt_rule_entry.rule.action = flt_action;
+		flt_rule_entry.rule.rt_tbl_hdl = routing_table0.hdl; //put here the handle corresponding to Routing Rule 1
+		flt_rule_entry.rule.attrib.attrib_mask = IPA_FLT_DST_ADDR;
+
+		flt_rule_entry.rule.attrib.u.v6.dst_addr_mask[0] = 0xFFFFFFFF;// Exact Match
+		flt_rule_entry.rule.attrib.u.v6.dst_addr_mask[1] = 0xFFFFFFFF;// Exact Match
+		flt_rule_entry.rule.attrib.u.v6.dst_addr_mask[2] = 0xFFFFFFFF;// Exact Match
+		flt_rule_entry.rule.attrib.u.v6.dst_addr_mask[3] = 0xFFFFFFFF;// Exact Match
+		flt_rule_entry.rule.attrib.u.v6.dst_addr[0] = GetHigh32(dst_addr_msb); // Filter DST_IP
+		flt_rule_entry.rule.attrib.u.v6.dst_addr[1] = GetLow32(dst_addr_msb);
+		flt_rule_entry.rule.attrib.u.v6.dst_addr[2] = GetHigh32(dst_addr_lsb);
+		flt_rule_entry.rule.attrib.u.v6.dst_addr[3] = GetLow32(dst_addr_lsb);
+
+		printf("flt_rule_entry was set successfully, preparing for insertion....\n");
+
+		if (((uint8_t)-1 == FilterTable0.AddRuleToTable(flt_rule_entry)) ||
+			!m_filtering.AddFilteringRule(FilterTable0.GetFilteringTable()))
+		{
+			printf("%s::Error Adding Rule to Filter Table, aborting...\n", __FUNCTION__);
+			return false;
+		}
+		else
+		{
+			printf("flt rule hdl0=0x%x, status=0x%x\n",
+				FilterTable0.ReadRuleFromTable(0)->flt_rule_hdl, FilterTable0.ReadRuleFromTable(0)->status);
+		}
+
+		printf("Leaving %s, %s()\n", __FUNCTION__, __FILE__);
+		return true;
+	}// AddRoutingFilteringRules()
+
+	virtual bool AddRoutingFilteringRules()
+	{
+		printf("Entering %s, %s()\n", __FUNCTION__, __FILE__);
+		bool result = AddRoutingFilteringRules(IPA_PASS_TO_DST_NAT,
+			m_outbound_src_addr_msb, m_outbound_src_addr_lsb);
+		printf("Leaving %s, %s()\n", __FUNCTION__, __FILE__);
+		return result;
+	}// AddRoutingFilteringRules()
+
+	virtual bool ModifyPackets()
+	{
+		printf("Entering %s, %s()\n", __FUNCTION__, __FILE__);
+		IpaIPv6CTBlockTestFixture::ModifyPackets(m_outbound_src_addr_lsb, m_outbound_src_addr_msb, m_outbound_src_port,
+			m_outbound_dst_addr_lsb, m_outbound_dst_addr_msb, m_outbound_dst_port+1);
+		printf("Leaving %s, %s()\n", __FUNCTION__, __FILE__);
+		return true;
+	}// ModifyPackets()
+
+	virtual bool ReceivePacketsAndCompare()
+	{
+		printf("Entering %s, %s()\n", __FUNCTION__, __FILE__);
+		bool result = IpaIPv6CTBlockTestFixture::ReceivePacketsAndCompare(true);
+		printf("Leaving %s, %s()\n", __FUNCTION__, __FILE__);
+		return result;
+	}
+};
+
+/*---------------------------------------------------------------------------------------------*/
+/* Test021: IPv6CT send outbound packet, suppression test with status enabled  */
+/*---------------------------------------------------------------------------------------------*/
+class IpaIPV6CTBlockTest021 : public IpaIPv6CTBlockTestFixture
+{
+public:
+
+	IpaIPV6CTBlockTest021()
+	{
+		m_name = "IpaIPV6CTBlockTest021";
+		m_description =
+			"IPv6CT block test 021 - IPv6CT passes successfully one packet in outbound direction\n"
+			"1. Generate and commit three routing tables.\n"
+			"   Each table contains a single \"bypass\" rule (all data goes to output pipe 0, 1 and 2 (accordingly))\n"
+			"2. Generate and commit one outbound filtering rule: Destination IP Exactly Match.\n"
+			"3. Add IPv6CT rule for the packet which doesn't match\n"
+			"4. Expect NAT suppression to kick in and packet is routed correctly\n"
+			"5. Compare status and check if NAT suppression kicked in.\n";
+		Register(*this);
+	}
+
+	virtual bool Setup()
+	{
+		/* we want statuses on this test */
+		return IpaIPv6CTBlockTestFixture::Setup(true, true);
+	}
+
+	virtual bool AddRoutingFilteringRules(enum ipa_flt_action flt_action, uint64_t dst_addr_msb, uint64_t dst_addr_lsb)
+	{
+		printf("Entering %s, %s()\n", __FUNCTION__, __FILE__);
+		const char bypass0[20] = "Bypass0";
+		const char bypass1[20] = "Bypass1";
+		const char bypass2[20] = "Bypass2";
+
+		if (!CreateThreeIPv6BypassRoutingTables(bypass0, bypass1, bypass2))
+		{
+			printf("CreateThreeBypassRoutingTables Failed\n");
+			return false;
+		}
+		printf("CreateThreeBypassRoutingTables completed successfully\n");
+
+		ipa_ioc_get_rt_tbl routing_table0;
+		routing_table0.ip = IPA_IP_v6;
+		strlcpy(routing_table0.name, bypass0, sizeof(routing_table0.name));
+		if (!m_routing.GetRoutingTable(&routing_table0))
+		{
+			printf("m_routing.GetRoutingTable(&routing_table0=0x%pK) Failed.\n", &routing_table0);
+			return false;
+		}
+
+		/* Setup conntrack exception routing table. */
+		if (!m_routing.SetNatConntrackExcRoutingTable(routing_table0.hdl, false))
+		{
+			LOG_MSG_ERROR("m_routing.SetNatConntrackExcRoutingTable(routing_table0 hdl=%d) Failed.\n",
+				routing_table0.hdl);
+			return false;
+		}
+
+		ipa_ioc_get_rt_tbl routing_table1;
+		routing_table1.ip = IPA_IP_v6;
+		strlcpy(routing_table1.name, bypass1, sizeof(routing_table1.name));
+		if (!m_routing.GetRoutingTable(&routing_table1))
+		{
+			printf("m_routing.GetRoutingTable(&routing_table1=0x%pK) Failed.\n", &routing_table1);
+			return false;
+		}
+
+		IPAFilteringTable FilterTable0;
+		ipa_flt_rule_add flt_rule_entry;
+		FilterTable0.Init(IPA_IP_v6, IPA_CLIENT_TEST_PROD, false, 1);
+
+		// Configuring Filtering Rule No.0
+		FilterTable0.GeneratePresetRule(1, flt_rule_entry);
+		flt_rule_entry.at_rear = true;
+		flt_rule_entry.flt_rule_hdl = -1; // return Value
+		flt_rule_entry.status = -1; // return value
+		flt_rule_entry.rule.action = flt_action;
+		flt_rule_entry.rule.rt_tbl_hdl = routing_table0.hdl; //put here the handle corresponding to Routing Rule 1
+		flt_rule_entry.rule.attrib.attrib_mask = IPA_FLT_DST_ADDR;
+
+		flt_rule_entry.rule.attrib.u.v6.dst_addr_mask[0] = 0xFFFFFFFF;// Exact Match
+		flt_rule_entry.rule.attrib.u.v6.dst_addr_mask[1] = 0xFFFFFFFF;// Exact Match
+		flt_rule_entry.rule.attrib.u.v6.dst_addr_mask[2] = 0xFFFFFFFF;// Exact Match
+		flt_rule_entry.rule.attrib.u.v6.dst_addr_mask[3] = 0xFFFFFFFF;// Exact Match
+		flt_rule_entry.rule.attrib.u.v6.dst_addr[0] = GetHigh32(dst_addr_msb); // Filter DST_IP
+		flt_rule_entry.rule.attrib.u.v6.dst_addr[1] = GetLow32(dst_addr_msb);
+		flt_rule_entry.rule.attrib.u.v6.dst_addr[2] = GetHigh32(dst_addr_lsb);
+		flt_rule_entry.rule.attrib.u.v6.dst_addr[3] = GetLow32(dst_addr_lsb);
+
+		printf("flt_rule_entry was set successfully, preparing for insertion....\n");
+
+		if (((uint8_t)-1 == FilterTable0.AddRuleToTable(flt_rule_entry)) ||
+			!m_filtering.AddFilteringRule(FilterTable0.GetFilteringTable()))
+		{
+			printf("%s::Error Adding Rule to Filter Table, aborting...\n", __FUNCTION__);
+			return false;
+		}
+		else
+		{
+			printf("flt rule hdl0=0x%x, status=0x%x\n",
+				FilterTable0.ReadRuleFromTable(0)->flt_rule_hdl, FilterTable0.ReadRuleFromTable(0)->status);
+		}
+
+		printf("Leaving %s, %s()\n", __FUNCTION__, __FILE__);
+		return true;
+	}// AddRoutingFilteringRules()
+
+	virtual bool AddRoutingFilteringRules()
+	{
+		printf("Entering %s, %s()\n", __FUNCTION__, __FILE__);
+		bool result = AddRoutingFilteringRules(IPA_PASS_TO_SRC_NAT,
+			m_outbound_dst_addr_msb, m_outbound_dst_addr_lsb);
+		printf("Leaving %s, %s()\n", __FUNCTION__, __FILE__);
+		return result;
+	}// AddRoutingFilteringRules()
+
+	virtual bool ModifyPackets()
+	{
+		printf("Entering %s, %s()\n", __FUNCTION__, __FILE__);
+		IpaIPv6CTBlockTestFixture::ModifyPackets(m_outbound_dst_addr_lsb, m_outbound_dst_addr_msb, m_outbound_dst_port,
+			m_outbound_src_addr_lsb, m_outbound_src_addr_msb, m_outbound_src_port+1);
+		printf("Leaving %s, %s()\n", __FUNCTION__, __FILE__);
+		return true;
+	}// ModifyPackets()
+
+	virtual bool ReceivePacketsAndCompare(bool packetPassExpected)
+	{
+		printf("Entering %s, %s()\n", __FUNCTION__, __FILE__);
+
+		// Receive results		
+		struct ipa3_hw_pkt_status_hw_v5_5 *status = NULL;
+		Byte rxBuff1[0x400];
+		size_t receivedSize = m_consumer.ReceiveData(rxBuff1, 0x400);
+		printf("Received %zu bytes on %s.\n", receivedSize, m_consumer.m_fromChannelName.c_str());
+
+		bool isSuccess = true;
+		if (packetPassExpected)
+		{
+			// Compare results
+			if (!CompareResultVsGolden_w_Status(m_sendBuffer, m_sendSize, rxBuff1, receivedSize))
+			{
+				printf("Comparison of Buffer0 Failed!\n");
+				isSuccess = false;
+			}
+		}
+		else
+		{
+			if (receivedSize)
+			{
+				isSuccess = false;
+				printf("got data while expected packet to be blocked, failing\n");
+			}
+		}
+
+		status = (struct ipa3_hw_pkt_status_hw_v5_5 *)rxBuff1;
+		if (!status->nat_exc_suppress)
+		{
+			printf("NAT Suppression not hit!\n");
+			isSuccess = false;
+		}
+
+		char recievedBuffer[256] = {0};
+		char SentBuffer[256] = {0};
+		size_t j;
+
+		for (j = 0; j < m_sendSize; j++)
+		{
+			snprintf(&SentBuffer[3 * j], sizeof(SentBuffer)-(3 * j + 1), " %02X", m_sendBuffer[j]);
+		}
+
+		for (j = 0; j < receivedSize; j++)
+		{
+			snprintf(&recievedBuffer[3 * j], sizeof(recievedBuffer)-(3 * j + 1), " %02X", rxBuff1[j]);
+		}
+		printf("Expected Value1 (%zu)\n%s\n, Received Value1(%zu)\n%s\n",
+			m_sendSize, SentBuffer, receivedSize, recievedBuffer);
+
+		printf("Leaving %s, %s()\n", __FUNCTION__, __FILE__);
+		return isSuccess;
+	}
+
+	virtual bool ReceivePacketsAndCompare()
+	{
+		printf("Entering %s, %s()\n", __FUNCTION__, __FILE__);
+		bool result = ReceivePacketsAndCompare(true);
+		printf("Leaving %s, %s()\n", __FUNCTION__, __FILE__);
+		return result;
+	}
+};
+
+/*---------------------------------------------------------------------------------------------*/
+/* Test022: IPv6CT send inbound packet for NAT suppression test with status enabled */
+/*---------------------------------------------------------------------------------------------*/
+class IpaIPV6CTBlockTest022 : public IpaIPv6CTBlockTestFixture
+{
+public:
+
+	IpaIPV6CTBlockTest022()
+	{
+		m_name = "IpaIPV6CTBlockTest022";
+		m_description =
+			"IPv6CT block test 022 - IPv6CT passes successfully one packet in inbound direction on NAT suppression\n"
+			"1. Generate and commit three routing tables.\n"
+			"   Each table contains a single \"bypass\" rule (all data goes to output pipe 0, 1 and 2 (accordingly))\n"
+			"2. Generate and commit one inbound filtering rule: Destination IP Exactly Match.\n"
+			"3. Add IPv6CT rule for the packet\n"
+			"4. Send packet which doesn't match CT and expect NAT suppression to kick in.\n"
+			"5. Compare status and check if NAT suppression kicked in.\n";
+		Register(*this);
+	}
+
+	virtual bool Setup()
+	{
+		/* we want statuses on this test */
+		return IpaIPv6CTBlockTestFixture::Setup(true, true);
+	}
+
+	virtual bool AddRoutingFilteringRules(enum ipa_flt_action flt_action, uint64_t dst_addr_msb, uint64_t dst_addr_lsb)
+	{
+		printf("Entering %s, %s()\n", __FUNCTION__, __FILE__);
+		const char bypass0[20] = "Bypass0";
+		const char bypass1[20] = "Bypass1";
+		const char bypass2[20] = "Bypass2";
+
+		if (!CreateThreeIPv6BypassRoutingTables(bypass0, bypass1, bypass2))
+		{
+			printf("CreateThreeBypassRoutingTables Failed\n");
+			return false;
+		}
+		printf("CreateThreeBypassRoutingTables completed successfully\n");
+
+		ipa_ioc_get_rt_tbl routing_table0;
+		routing_table0.ip = IPA_IP_v6;
+		strlcpy(routing_table0.name, bypass0, sizeof(routing_table0.name));
+		if (!m_routing.GetRoutingTable(&routing_table0))
+		{
+			printf("m_routing.GetRoutingTable(&routing_table0=0x%pK) Failed.\n", &routing_table0);
+			return false;
+		}
+
+		/* Setup conntrack exception routing table. */
+		if (!m_routing.SetNatConntrackExcRoutingTable(routing_table0.hdl, false))
+		{
+			LOG_MSG_ERROR("m_routing.SetNatConntrackExcRoutingTable(routing_table0 hdl=%d) Failed.\n",
+				routing_table0.hdl);
+			return false;
+		}
+
+		ipa_ioc_get_rt_tbl routing_table1;
+		routing_table1.ip = IPA_IP_v6;
+		strlcpy(routing_table1.name, bypass1, sizeof(routing_table1.name));
+		if (!m_routing.GetRoutingTable(&routing_table1))
+		{
+			printf("m_routing.GetRoutingTable(&routing_table1=0x%pK) Failed.\n", &routing_table1);
+			return false;
+		}
+
+		IPAFilteringTable FilterTable0;
+		ipa_flt_rule_add flt_rule_entry;
+		FilterTable0.Init(IPA_IP_v6, IPA_CLIENT_TEST_PROD, false, 1);
+
+		// Configuring Filtering Rule No.0
+		FilterTable0.GeneratePresetRule(1, flt_rule_entry);
+		flt_rule_entry.at_rear = true;
+		flt_rule_entry.flt_rule_hdl = -1; // return Value
+		flt_rule_entry.status = -1; // return value
+		flt_rule_entry.rule.action = flt_action;
+		flt_rule_entry.rule.rt_tbl_hdl = routing_table0.hdl; //put here the handle corresponding to Routing Rule 1
+		flt_rule_entry.rule.attrib.attrib_mask = IPA_FLT_DST_ADDR;
+
+		flt_rule_entry.rule.attrib.u.v6.dst_addr_mask[0] = 0xFFFFFFFF;// Exact Match
+		flt_rule_entry.rule.attrib.u.v6.dst_addr_mask[1] = 0xFFFFFFFF;// Exact Match
+		flt_rule_entry.rule.attrib.u.v6.dst_addr_mask[2] = 0xFFFFFFFF;// Exact Match
+		flt_rule_entry.rule.attrib.u.v6.dst_addr_mask[3] = 0xFFFFFFFF;// Exact Match
+		flt_rule_entry.rule.attrib.u.v6.dst_addr[0] = GetHigh32(dst_addr_msb); // Filter DST_IP
+		flt_rule_entry.rule.attrib.u.v6.dst_addr[1] = GetLow32(dst_addr_msb);
+		flt_rule_entry.rule.attrib.u.v6.dst_addr[2] = GetHigh32(dst_addr_lsb);
+		flt_rule_entry.rule.attrib.u.v6.dst_addr[3] = GetLow32(dst_addr_lsb);
+
+		printf("flt_rule_entry was set successfully, preparing for insertion....\n");
+
+		if (((uint8_t)-1 == FilterTable0.AddRuleToTable(flt_rule_entry)) ||
+			!m_filtering.AddFilteringRule(FilterTable0.GetFilteringTable()))
+		{
+			printf("%s::Error Adding Rule to Filter Table, aborting...\n", __FUNCTION__);
+			return false;
+		}
+		else
+		{
+			printf("flt rule hdl0=0x%x, status=0x%x\n",
+				FilterTable0.ReadRuleFromTable(0)->flt_rule_hdl, FilterTable0.ReadRuleFromTable(0)->status);
+		}
+
+		printf("Leaving %s, %s()\n", __FUNCTION__, __FILE__);
+		return true;
+	}// AddRoutingFilteringRules()
+
+	virtual bool AddRoutingFilteringRules()
+	{
+		printf("Entering %s, %s()\n", __FUNCTION__, __FILE__);
+		bool result = IpaIPv6CTBlockTestFixture::AddRoutingFilteringRules(IPA_PASS_TO_DST_NAT,
+			m_outbound_src_addr_msb, m_outbound_src_addr_lsb);
+		printf("Leaving %s, %s()\n", __FUNCTION__, __FILE__);
+		return result;
+	}// AddRoutingFilteringRules()
+
+	virtual bool ModifyPackets()
+	{
+		printf("Entering %s, %s()\n", __FUNCTION__, __FILE__);
+		IpaIPv6CTBlockTestFixture::ModifyPackets(m_outbound_src_addr_lsb, m_outbound_src_addr_msb, m_outbound_src_port,
+			m_outbound_dst_addr_lsb, m_outbound_dst_addr_msb, m_outbound_dst_port+1);
+		printf("Leaving %s, %s()\n", __FUNCTION__, __FILE__);
+		return true;
+	}// ModifyPackets()
+
+	virtual bool ReceivePacketsAndCompare(bool packetPassExpected)
+	{
+		printf("Entering %s, %s()\n", __FUNCTION__, __FILE__);
+
+		// Receive results		
+		struct ipa3_hw_pkt_status_hw_v5_5 *status = NULL;
+		Byte rxBuff1[0x400];
+		size_t receivedSize = m_consumer.ReceiveData(rxBuff1, 0x400);
+		printf("Received %zu bytes on %s.\n", receivedSize, m_consumer.m_fromChannelName.c_str());
+
+		bool isSuccess = true;
+		if (packetPassExpected)
+		{
+			// Compare results
+			if (!CompareResultVsGolden_w_Status(m_sendBuffer, m_sendSize, rxBuff1, receivedSize))
+			{
+				printf("Comparison of Buffer0 Failed!\n");
+				isSuccess = false;
+			}
+		}
+		else
+		{
+			if (receivedSize)
+			{
+				isSuccess = false;
+				printf("got data while expected packet to be blocked, failing\n");
+			}
+		}
+
+		status = (struct ipa3_hw_pkt_status_hw_v5_5 *)rxBuff1;
+		if (!status->nat_exc_suppress)
+		{
+			printf("NAT Suppression not hit!\n");
+			isSuccess = false;
+		}
+
+		char recievedBuffer[256] = {0};
+		char SentBuffer[256] = {0};
+		size_t j;
+
+		for (j = 0; j < m_sendSize; j++)
+		{
+			snprintf(&SentBuffer[3 * j], sizeof(SentBuffer)-(3 * j + 1), " %02X", m_sendBuffer[j]);
+		}
+
+		for (j = 0; j < receivedSize; j++)
+		{
+			snprintf(&recievedBuffer[3 * j], sizeof(recievedBuffer)-(3 * j + 1), " %02X", rxBuff1[j]);
+		}
+		printf("Expected Value1 (%zu)\n%s\n, Received Value1(%zu)\n%s\n",
+			m_sendSize, SentBuffer, receivedSize, recievedBuffer);
+
+		printf("Leaving %s, %s()\n", __FUNCTION__, __FILE__);
+		return isSuccess;
+	}
+
+	virtual bool ReceivePacketsAndCompare()
+	{
+		printf("Entering %s, %s()\n", __FUNCTION__, __FILE__);
+		bool result = ReceivePacketsAndCompare(true);
+		printf("Leaving %s, %s()\n", __FUNCTION__, __FILE__);
+		return result;
+	}
+};
+
+
 // IPv6CT outbound packet test
 static class IpaIPV6CTBlockTest001 IpaIPV6CTBlockTest001;
 
@@ -1779,3 +2426,14 @@
 // IPv6CT send inbound packet with rule in expansion table while the rule in the middle of the list was deleted
 static class IpaIPV6CTBlockTest018 IpaIPV6CTBlockTest018;
 
+// IPv6CT suppression test Outbound traffic
+static class IpaIPV6CTBlockTest019 IpaIPV6CTBlockTest019;
+
+// IPv6CT suppression test Inbound traffic
+static class IpaIPV6CTBlockTest020 IpaIPV6CTBlockTest020;
+
+// IPv6CT suppression test Outbound traffic with status
+static class IpaIPV6CTBlockTest021 IpaIPV6CTBlockTest021;
+
+// IPv6CT suppression test Inbound traffic with status
+static class IpaIPV6CTBlockTest022 IpaIPV6CTBlockTest022;
diff --git a/kernel-tests/NatTest.cpp b/kernel-tests/NatTest.cpp
index 71d491a..b863dca 100644
--- a/kernel-tests/NatTest.cpp
+++ b/kernel-tests/NatTest.cpp
@@ -84,7 +84,7 @@
 		m_testSuiteName.push_back("Nat");
 	}
 
-	static int SetupKernelModule(bool en_status = 0)
+	static int SetupKernelModule(bool en_status = 0, bool nat_suppress = 0)
 	{
 		int retval;
 		struct ipa_channel_config from_ipa_channels[3];
@@ -126,6 +126,7 @@
 
 		/* To ipa configurations - 2 pipes */
 		memset(&to_ipa_cfg[0], 0, sizeof(to_ipa_cfg[0]));
+		to_ipa_cfg[0].nat.nat_exc_suppress = nat_suppress; 
 		prepare_channel_struct(&to_ipa_channels[0],
 			header.to_ipa_channels_num++,
 			IPA_CLIENT_TEST_PROD,
@@ -135,6 +136,7 @@
 
 		/* header removal for Ethernet header + 8021Q header */
 		memset(&to_ipa_cfg[1], 0, sizeof(to_ipa_cfg[1]));
+		to_ipa_cfg[1].nat.nat_exc_suppress = nat_suppress; 
 		to_ipa_cfg[1].hdr.hdr_len = ETH8021Q_HEADER_LEN;
 		to_ipa_cfg[1].hdr.hdr_ofst_metadata_valid = 1;
 		to_ipa_cfg[1].hdr.hdr_ofst_metadata =
@@ -153,6 +155,7 @@
 		return retval;
 	}
 
+
 	bool Setup()
 	{
 		bool bRetVal = true;
@@ -183,11 +186,11 @@
 		return true;
 	} // Setup()
 
-	bool Setup(bool en_status = false)
+	bool Setup(bool en_status = false, bool nat_suppress = false)
 	{
 		bool bRetVal = true;
 
-		if (SetupKernelModule(en_status) != true)
+		if (SetupKernelModule(en_status, nat_suppress) != true)
 			return bRetVal;
 
 		m_producer.Open(INTERFACE0_TO_IPA_DATA_PATH, INTERFACE0_FROM_IPA_DATA_PATH);
@@ -212,6 +215,7 @@
 		return true;
 	} // Setup()
 
+
 	bool Teardown()
 	{
 		ipa_nat_dump_ipv4_table(m_tbl_hdl);
@@ -4267,6 +4271,865 @@
 	}
 };
 
+/*---------------------------------------------------------------------------*/
+/* Test019: NAT Suppression test					     */
+/* NOTE: other classes are derived from this class - change carefully        */
+/*---------------------------------------------------------------------------*/
+class IpaNatBlockTest019 : public IpaNatBlockTestFixture
+{
+public:
+	IpaNatBlockTest019()
+	{
+		m_name = "IpaNatBlockTest019";
+		m_description =
+			"NAT block test 019 - single PDN src NAT test\
+		1. Generate and commit three routing tables (only one is used). \
+			Each table contains a single \"bypass\" rule (all data goes to output pipe 0, 1  and 2 (accordingly)) \
+		2. Generate and commit one filtering rule: (DST & Mask Match). \
+			action go to src NAT \
+			All DST_IP == (193.23.22.1 & 0.255.255.255)traffic goes to NAT block \
+		3. generate and commit one NAT rule:\
+			private ip 194.23.22.1 --> public ip 192.23.22.1";
+		m_private_ip = 0xC2171601; /* 194.23.22.1 */
+		m_private_port = 5678;
+		m_public_ip = 0xC0171601;   /* "192.23.22.1" */
+		m_public_port = 9050;
+		m_target_ip = 0xC1171601; /* 193.23.22.1 */
+		m_target_port = 1234;
+		Register(*this);
+	}
+
+	virtual bool Setup()
+	{
+		return IpaNatBlockTestFixture::Setup(false, true);
+	}
+
+	virtual bool AddRules()
+	{
+		LOG_MSG_DEBUG("Entering\n");
+
+		const char bypass0[20] = "Bypass0";
+		const char bypass1[20] = "Bypass1";
+		const char bypass2[20] = "Bypass2";
+		struct ipa_ioc_get_rt_tbl routing_table0;
+
+		if (!CreateThreeIPv4BypassRoutingTables(bypass0, bypass1, bypass2))
+		{
+			LOG_MSG_ERROR("CreateThreeBypassRoutingTables Failed\n");
+			return false;
+		}
+
+		LOG_MSG_DEBUG("CreateThreeBypassRoutingTables completed successfully\n");
+		routing_table0.ip = IPA_IP_v4;
+		strlcpy(routing_table0.name, bypass0, sizeof(routing_table0.name));
+		if (!m_routing.GetRoutingTable(&routing_table0))
+		{
+			LOG_MSG_ERROR("m_routing.GetRoutingTable(&routing_table0=0x%p) Failed.\n", &routing_table0);
+			return false;
+		}
+		LOG_MSG_DEBUG("%s route table handle = %u\n", bypass0, routing_table0.hdl);
+
+		/* Setup NAT Exception routing table. */
+		if (!m_routing.SetNatConntrackExcRoutingTable(routing_table0.hdl, true))
+		{
+			LOG_MSG_ERROR("m_routing.SetNatConntrackExcRoutingTable(routing_table0 hdl=%d) Failed.\n",
+				routing_table0.hdl);
+			return false;
+		}
+
+		IPAFilteringTable FilterTable0;
+		struct ipa_flt_rule_add flt_rule_entry;
+		FilterTable0.Init(IPA_IP_v4, IPA_CLIENT_TEST_PROD, false, 1);
+		LOG_MSG_DEBUG("FilterTable*.Init Completed Successfully..\n");
+
+		// Configuring Filtering Rule No.0
+		FilterTable0.GeneratePresetRule(1, flt_rule_entry);
+		flt_rule_entry.at_rear = true;
+		flt_rule_entry.flt_rule_hdl = -1; // return Value
+		flt_rule_entry.status = -1; // return value
+		flt_rule_entry.rule.action = IPA_PASS_TO_SRC_NAT;
+		flt_rule_entry.rule.rt_tbl_hdl = routing_table0.hdl; //put here the handle corresponding to Routing Rule 1
+		flt_rule_entry.rule.attrib.attrib_mask = IPA_FLT_DST_ADDR;
+		flt_rule_entry.rule.attrib.u.v4.dst_addr_mask = 0xFFFFFF00; // Mask
+		flt_rule_entry.rule.attrib.u.v4.dst_addr = m_target_ip; // Filter DST_IP == 193.23.22.1
+		flt_rule_entry.rule.pdn_idx = 0;
+		flt_rule_entry.rule.set_metadata = 0;
+		if (
+			((uint8_t)-1 == FilterTable0.AddRuleToTable(flt_rule_entry)) ||
+			!m_filtering.AddFilteringRule(FilterTable0.GetFilteringTable())
+			)
+		{
+			LOG_MSG_ERROR("Error Adding Rule to Filter Table, aborting...\n");
+			return false;
+		}
+		else
+		{
+			LOG_MSG_DEBUG("flt rule hdl0=0x%x, status=0x%x\n", FilterTable0.ReadRuleFromTable(0)->flt_rule_hdl, FilterTable0.ReadRuleFromTable(0)->status);
+		}
+
+		//NAT table and rules creation
+		int total_entries = 20;
+		int ret;
+		ipa_nat_ipv4_rule ipv4_rule;
+
+		ret = ipa_nat_add_ipv4_tbl(m_public_ip, m_mem_type, total_entries, &m_tbl_hdl);
+		if (ret) {
+			LOG_MSG_DEBUG("failed creating NAT table\n");
+			return false;
+		}
+		LOG_MSG_DEBUG("nat table added, hdl %d, public ip 0x%X\n", m_tbl_hdl,
+			m_public_ip);
+
+		ipv4_rule.target_ip = m_target_ip;
+		ipv4_rule.target_port = m_target_port;
+		ipv4_rule.private_ip = m_private_ip;
+		ipv4_rule.private_port = m_private_port;
+		ipv4_rule.protocol = IPPROTO_TCP;
+		ipv4_rule.public_port = m_public_port;
+		ipv4_rule.pdn_index = 0;
+
+		ret = ipa_nat_add_ipv4_rule(m_tbl_hdl, &ipv4_rule, &m_nat_rule_hdl1);
+		if (ret) {
+			LOG_MSG_ERROR("failed adding NAT rule 0\n");
+			return false;
+		}
+		LOG_MSG_DEBUG("NAT rule added, hdl %d, data: 0x%X, %d, 0x%X, %d, %d, %d\n",
+			m_nat_rule_hdl1, ipv4_rule.target_ip, ipv4_rule.target_port,
+			ipv4_rule.private_ip, ipv4_rule.private_port,
+			ipv4_rule.protocol, ipv4_rule.public_port);
+
+		LOG_MSG_DEBUG("Leaving");
+		return true;
+	}// AddRules()
+
+	virtual bool ModifyPackets()
+	{
+		uint32_t address;
+		uint16_t port;
+		char flags = 0x18;
+
+		address = htonl(m_target_ip);//193.23.22.1
+		memcpy(&m_sendBuffer[IPV4_DST_ADDR_OFFSET], &address, sizeof(address));
+		port = htons(m_target_port);
+		memcpy(&m_sendBuffer[IPV4_DST_PORT_OFFSET], &port, sizeof(port));
+
+		address = htonl(m_private_ip);/* 194.23.22.1 */
+		memcpy(&m_sendBuffer[IPV4_SRC_ADDR_OFFSET], &address, sizeof(address));
+		port = htons(m_private_port+1);
+		memcpy(&m_sendBuffer[IPV4_SRC_PORT_OFFSET], &port, sizeof(port));
+
+		//make sure the FIN flag is not set, otherwise we will get a NAT miss
+		memcpy(&m_sendBuffer[IPV4_TCP_FLAGS_OFFSET],&flags , sizeof(flags));
+		return true;
+	}// ModifyPacktes ()
+
+	virtual bool SendPackets()
+	{
+		bool isSuccess = false;
+
+		// Send first packet
+		isSuccess = m_producer.SendData(m_sendBuffer, m_sendSize);
+		if (false == isSuccess)
+		{
+			LOG_MSG_ERROR("SendData failure.\n");
+			return false;
+		}
+
+		LOG_MSG_DEBUG("sent successfully one packet\n");
+		return true;
+	}
+
+	virtual bool ReceivePacketsAndCompare()
+	{
+		size_t receivedSize = 0;
+		bool isSuccess = true;
+
+		// Receive results
+		Byte *rxBuff1 = new Byte[0x400];
+
+		if (NULL == rxBuff1)
+		{
+			LOG_MSG_ERROR("Memory allocation error.\n");
+			return false;
+		}
+
+		receivedSize = m_consumer.ReceiveData(rxBuff1, 0x400);
+		LOG_MSG_DEBUG("Received %zu bytes on %s.\n", receivedSize, m_consumer.m_fromChannelName.c_str());
+
+		// Compare results
+		if (!CompareResultVsGolden(m_sendBuffer, m_sendSize, rxBuff1, receivedSize))
+		{
+			printf("Comparison of Buffer0 Failed!\n");
+			isSuccess = false;
+		}
+
+		char recievedBuffer[256] = { 0 };
+		char SentBuffer[256] = { 0 };
+		size_t j;
+
+		for (j = 0; j < m_sendSize; j++)
+			snprintf(&SentBuffer[3 * j], sizeof(SentBuffer) - (3 * j + 1), " %02X", m_sendBuffer[j]);
+		for (j = 0; j < receivedSize; j++)
+			snprintf(&recievedBuffer[3 * j], sizeof(recievedBuffer) - (3 * j + 1), " %02X", rxBuff1[j]);
+		LOG_MSG_STACK("sent Value1 (%zu)\n%s\n, Received Value1(%zu)\n%s\n", m_sendSize, SentBuffer, receivedSize, recievedBuffer);
+
+		delete[] rxBuff1;
+
+		return isSuccess;
+	}
+};
+
+/*---------------------------------------------------------------------------*/
+/* Test020: NAT Suppression test with Status					     */
+/* NOTE: other classes are derived from this class - change carefully        */
+/*---------------------------------------------------------------------------*/
+class IpaNatBlockTest020 : public IpaNatBlockTestFixture
+{
+public:
+	IpaNatBlockTest020()
+	{
+		m_name = "IpaNatBlockTest020";
+		m_description =
+			"NAT block test 020 - single PDN src NAT test with status enabled\
+		1. Generate and commit three routing tables (only one is used). \
+			Each table contains a single \"bypass\" rule (all data goes to output pipe 0, 1  and 2 (accordingly)) \
+		2. Generate and commit one filtering rule: (DST & Mask Match). \
+			action go to src NAT \
+			All DST_IP == (193.23.22.1 & 0.255.255.255)traffic goes to NAT block \
+		3. generate and commit one NAT rule:\
+			private ip 194.23.22.1 --> public ip 192.23.22.1";
+		m_private_ip = 0xC2171601; /* 194.23.22.1 */
+		m_private_port = 5678;
+		m_public_ip = 0xC0171601;   /* "192.23.22.1" */
+		m_public_port = 9050;
+		m_target_ip = 0xC1171601; /* 193.23.22.1 */
+		m_target_port = 1234;
+		Register(*this);
+	}
+
+	virtual bool Setup()
+	{
+		return IpaNatBlockTestFixture::Setup(true, true);
+	}
+
+	virtual bool AddRules()
+	{
+		LOG_MSG_DEBUG("Entering\n");
+
+		const char bypass0[20] = "Bypass0";
+		const char bypass1[20] = "Bypass1";
+		const char bypass2[20] = "Bypass2";
+		struct ipa_ioc_get_rt_tbl routing_table0;
+
+		if (!CreateThreeIPv4BypassRoutingTables(bypass0, bypass1, bypass2))
+		{
+			LOG_MSG_ERROR("CreateThreeBypassRoutingTables Failed\n");
+			return false;
+		}
+
+		LOG_MSG_DEBUG("CreateThreeBypassRoutingTables completed successfully\n");
+		routing_table0.ip = IPA_IP_v4;
+		strlcpy(routing_table0.name, bypass0, sizeof(routing_table0.name));
+		if (!m_routing.GetRoutingTable(&routing_table0))
+		{
+			LOG_MSG_ERROR("m_routing.GetRoutingTable(&routing_table0=0x%p) Failed.\n", &routing_table0);
+			return false;
+		}
+		LOG_MSG_DEBUG("%s route table handle = %u\n", bypass0, routing_table0.hdl);
+
+		/* Setup NAT Exception routing table. */
+		if (!m_routing.SetNatConntrackExcRoutingTable(routing_table0.hdl, true))
+		{
+			LOG_MSG_ERROR("m_routing.SetNatConntrackExcRoutingTable(routing_table0 hdl=%d) Failed.\n",
+				routing_table0.hdl);
+			return false;
+		}
+
+		IPAFilteringTable FilterTable0;
+		struct ipa_flt_rule_add flt_rule_entry;
+		FilterTable0.Init(IPA_IP_v4, IPA_CLIENT_TEST_PROD, false, 1);
+		LOG_MSG_DEBUG("FilterTable*.Init Completed Successfully..\n");
+
+		// Configuring Filtering Rule No.0
+		FilterTable0.GeneratePresetRule(1, flt_rule_entry);
+		flt_rule_entry.at_rear = true;
+		flt_rule_entry.flt_rule_hdl = -1; // return Value
+		flt_rule_entry.status = -1; // return value
+		flt_rule_entry.rule.action = IPA_PASS_TO_SRC_NAT;
+		flt_rule_entry.rule.rt_tbl_hdl = routing_table0.hdl; //put here the handle corresponding to Routing Rule 1
+		flt_rule_entry.rule.attrib.attrib_mask = IPA_FLT_DST_ADDR;
+		flt_rule_entry.rule.attrib.u.v4.dst_addr_mask = 0xFFFFFF00; // Mask
+		flt_rule_entry.rule.attrib.u.v4.dst_addr = m_target_ip; // Filter DST_IP == 193.23.22.1
+		flt_rule_entry.rule.pdn_idx = 0;
+		flt_rule_entry.rule.set_metadata = 0;
+		if (
+			((uint8_t)-1 == FilterTable0.AddRuleToTable(flt_rule_entry)) ||
+			!m_filtering.AddFilteringRule(FilterTable0.GetFilteringTable())
+			)
+		{
+			LOG_MSG_ERROR("Error Adding Rule to Filter Table, aborting...\n");
+			return false;
+		}
+		else
+		{
+			LOG_MSG_DEBUG("flt rule hdl0=0x%x, status=0x%x\n", FilterTable0.ReadRuleFromTable(0)->flt_rule_hdl, FilterTable0.ReadRuleFromTable(0)->status);
+		}
+
+		//NAT table and rules creation
+		int total_entries = 20;
+		int ret;
+		ipa_nat_ipv4_rule ipv4_rule;
+
+		ret = ipa_nat_add_ipv4_tbl(m_public_ip, m_mem_type, total_entries, &m_tbl_hdl);
+		if (ret) {
+			LOG_MSG_DEBUG("failed creating NAT table\n");
+			return false;
+		}
+		LOG_MSG_DEBUG("nat table added, hdl %d, public ip 0x%X\n", m_tbl_hdl,
+			m_public_ip);
+
+		ipv4_rule.target_ip = m_target_ip;
+		ipv4_rule.target_port = m_target_port;
+		ipv4_rule.private_ip = m_private_ip;
+		ipv4_rule.private_port = m_private_port;
+		ipv4_rule.protocol = IPPROTO_TCP;
+		ipv4_rule.public_port = m_public_port;
+		ipv4_rule.pdn_index = 0;
+
+		ret = ipa_nat_add_ipv4_rule(m_tbl_hdl, &ipv4_rule, &m_nat_rule_hdl1);
+		if (ret) {
+			LOG_MSG_ERROR("failed adding NAT rule 0\n");
+			return false;
+		}
+		LOG_MSG_DEBUG("NAT rule added, hdl %d, data: 0x%X, %d, 0x%X, %d, %d, %d\n",
+			m_nat_rule_hdl1, ipv4_rule.target_ip, ipv4_rule.target_port,
+			ipv4_rule.private_ip, ipv4_rule.private_port,
+			ipv4_rule.protocol, ipv4_rule.public_port);
+
+		LOG_MSG_DEBUG("Leaving");
+		return true;
+	}// AddRules()
+
+	virtual bool ModifyPackets()
+	{
+		uint32_t address;
+		uint16_t port;
+		char flags = 0x18;
+
+		address = htonl(m_target_ip);//193.23.22.1
+		memcpy(&m_sendBuffer[IPV4_DST_ADDR_OFFSET], &address, sizeof(address));
+		port = htons(m_target_port);
+		memcpy(&m_sendBuffer[IPV4_DST_PORT_OFFSET], &port, sizeof(port));
+
+		address = htonl(m_private_ip);/* 194.23.22.1 */
+		memcpy(&m_sendBuffer[IPV4_SRC_ADDR_OFFSET], &address, sizeof(address));
+		port = htons(m_private_port+1);
+		memcpy(&m_sendBuffer[IPV4_SRC_PORT_OFFSET], &port, sizeof(port));
+
+		//make sure the FIN flag is not set, otherwise we will get a NAT miss
+		memcpy(&m_sendBuffer[IPV4_TCP_FLAGS_OFFSET],&flags , sizeof(flags));
+		return true;
+	}// ModifyPacktes ()
+
+	virtual bool SendPackets()
+	{
+		bool isSuccess = false;
+
+		// Send first packet
+		isSuccess = m_producer.SendData(m_sendBuffer, m_sendSize);
+		if (false == isSuccess)
+		{
+			LOG_MSG_ERROR("SendData failure.\n");
+			return false;
+		}
+
+		LOG_MSG_DEBUG("sent successfully one packet\n");
+		return true;
+	}
+
+	virtual bool ReceivePacketsAndCompare()
+	{
+		size_t receivedSize = 0;
+		bool isSuccess = true;
+		struct ipa3_hw_pkt_status_hw_v5_5 *status = NULL;
+
+		// Receive results
+		Byte *rxBuff1 = new Byte[0x400];
+
+		if (NULL == rxBuff1)
+		{
+			LOG_MSG_ERROR("Memory allocation error.\n");
+			return false;
+		}
+
+		receivedSize = m_consumer.ReceiveData(rxBuff1, 0x400);
+		LOG_MSG_DEBUG("Received %zu bytes on %s.\n", receivedSize, m_consumer.m_fromChannelName.c_str());
+
+		// Compare results
+		if (!CompareResultVsGolden_w_Status(m_sendBuffer, m_sendSize, rxBuff1, receivedSize))
+		{
+			printf("Comparison of Buffer0 Failed!\n");
+			isSuccess = false;
+		}
+
+		status = (struct ipa3_hw_pkt_status_hw_v5_5 *)rxBuff1;
+		if (!status->nat_exc_suppress)
+		{
+			printf("NAT Suppression not hit!\n");
+			isSuccess = false;
+		}
+
+		char recievedBuffer[256] = { 0 };
+		char SentBuffer[256] = { 0 };
+		size_t j;
+
+		for (j = 0; j < m_sendSize; j++)
+			snprintf(&SentBuffer[3 * j], sizeof(SentBuffer) - (3 * j + 1), " %02X", m_sendBuffer[j]);
+		for (j = 0; j < receivedSize; j++)
+			snprintf(&recievedBuffer[3 * j], sizeof(recievedBuffer) - (3 * j + 1), " %02X", rxBuff1[j]);
+		LOG_MSG_STACK("sent Value1 (%zu)\n%s\n, Received Value1(%zu)\n%s\n", m_sendSize, SentBuffer, receivedSize, recievedBuffer);
+
+		delete[] rxBuff1;
+
+		return isSuccess;
+	}
+};
+
+/*---------------------------------------------------------------------------*/
+/* Test021: Single PDN dst NAT test with NAT suppresssion					     */
+/* NOTE: other classes are derived from this class - change carefully        */
+/*---------------------------------------------------------------------------*/
+class IpaNatBlockTest021 : public IpaNatBlockTestFixture
+{
+public:
+	IpaNatBlockTest021()
+	{
+		m_name = "IpaNatBlockTest021";
+		m_description =
+			"NAT block test 002 - single PDN dst NAT test with NAT suppression \
+		1. Generate and commit three routing tables (only one is used). \
+			Each table contains a single \"bypass\" rule (all data goes to output pipe 0, 1  and 2 (accordingly)) \
+		2. Generate and commit one filtering rule: (DST & Mask Match). \
+			action go to dst NAT \
+			All DST_IP == (192.23.22.1 & 0.255.255.255)traffic goes to NAT block (public IP filtering) \
+		3. generate and commit one NAT rule: \
+			public ip 192.23.22.1 --> private ip 194.23.22.1. \
+		4. Send packet not matching with NAT entry.\n";
+		m_private_ip = 0xC2171601; /* 194.23.22.1 */
+		m_private_port = 5678;
+		m_public_ip = 0xC0171601;   /* "192.23.22.1" */
+		m_public_port = 9050;
+		m_target_ip = 0xC1171601; /* 193.23.22.1 */
+		m_target_port = 1234;
+		Register(*this);
+	}
+
+	virtual bool Setup()
+	{
+		return IpaNatBlockTestFixture::Setup(false, true);
+	}
+
+	virtual bool AddRules()
+	{
+		LOG_MSG_DEBUG("Entering\n");
+
+		const char bypass0[20] = "Bypass0";
+		const char bypass1[20] = "Bypass1";
+		const char bypass2[20] = "Bypass2";
+		struct ipa_ioc_get_rt_tbl routing_table0;
+
+		if (!CreateThreeIPv4BypassRoutingTables(bypass0, bypass1, bypass2))
+		{
+			LOG_MSG_ERROR("CreateThreeBypassRoutingTables Failed\n");
+			return false;
+		}
+
+		LOG_MSG_DEBUG("CreateThreeBypassRoutingTables completed successfully\n");
+		routing_table0.ip = IPA_IP_v4;
+		strlcpy(routing_table0.name, bypass0, sizeof(routing_table0.name));
+		if (!m_routing.GetRoutingTable(&routing_table0))
+		{
+			LOG_MSG_ERROR("m_routing.GetRoutingTable(&routing_table0=0x%p) Failed.\n", &routing_table0);
+			return false;
+		}
+		LOG_MSG_DEBUG("%s route table handle = %u\n", bypass0, routing_table0.hdl);
+
+		/* Setup NAT Exception routing table. */
+		if (!m_routing.SetNatConntrackExcRoutingTable(routing_table0.hdl, true))
+		{
+			LOG_MSG_ERROR("m_routing.SetNatConntrackExcRoutingTable(routing_table0 hdl=%d) Failed.\n",
+				routing_table0.hdl);
+			return false;
+		}
+
+		IPAFilteringTable FilterTable0;
+		struct ipa_flt_rule_add flt_rule_entry;
+		FilterTable0.Init(IPA_IP_v4, IPA_CLIENT_TEST_PROD, false, 3);
+		LOG_MSG_DEBUG("FilterTable*.Init Completed Successfully..\n");
+
+		// Configuring Filtering Rule No.0
+		FilterTable0.GeneratePresetRule(1, flt_rule_entry);
+		flt_rule_entry.at_rear = true;
+		flt_rule_entry.flt_rule_hdl = -1; // return Value
+		flt_rule_entry.status = -1; // return value
+		flt_rule_entry.rule.action = IPA_PASS_TO_DST_NAT;
+		flt_rule_entry.rule.rt_tbl_hdl = routing_table0.hdl; //put here the handle corresponding to Routing Rule 1
+		flt_rule_entry.rule.attrib.attrib_mask = IPA_FLT_DST_ADDR;
+		flt_rule_entry.rule.attrib.u.v4.dst_addr_mask = 0x00FFFFFF; // Mask
+		flt_rule_entry.rule.attrib.u.v4.dst_addr = m_public_ip; // Filter DST_IP == 192.23.22.1
+		flt_rule_entry.rule.pdn_idx = 0;
+		flt_rule_entry.rule.set_metadata = 0;
+		if (
+			((uint8_t)-1 == FilterTable0.AddRuleToTable(flt_rule_entry)) ||
+			!m_filtering.AddFilteringRule(FilterTable0.GetFilteringTable())
+			)
+		{
+			LOG_MSG_ERROR("Error Adding Rule to Filter Table, aborting...\n");
+			return false;
+		}
+		else
+		{
+			LOG_MSG_DEBUG("flt rule hdl0=0x%x, status=0x%x\n", FilterTable0.ReadRuleFromTable(0)->flt_rule_hdl, FilterTable0.ReadRuleFromTable(0)->status);
+		}
+
+		//NAT table and rules creation
+		int total_entries = 20;
+		int ret;
+		ipa_nat_ipv4_rule ipv4_rule;
+		uint32_t pub_ip_add = m_public_ip;
+
+		ret = ipa_nat_add_ipv4_tbl(pub_ip_add, m_mem_type, total_entries, &m_tbl_hdl);
+		if (ret) {
+			LOG_MSG_ERROR("Leaving, failed creating NAT table\n");
+			return false;
+		}
+
+		LOG_MSG_DEBUG("nat table added, hdl %d, public ip 0x%X\n", m_tbl_hdl,
+			pub_ip_add);
+
+		ipv4_rule.target_ip = m_target_ip;
+		ipv4_rule.target_port = m_target_port;
+		ipv4_rule.private_ip = m_private_ip;
+		ipv4_rule.private_port = m_private_port;
+		ipv4_rule.protocol = IPPROTO_TCP;
+		ipv4_rule.public_port = m_public_port;
+		ipv4_rule.pdn_index = 0;
+
+		ret = ipa_nat_add_ipv4_rule(m_tbl_hdl, &ipv4_rule, &m_nat_rule_hdl1);
+		if (ret) {
+			LOG_MSG_ERROR("Leaving, failed adding NAT rule 0\n");
+			return false;
+		}
+
+		LOG_MSG_DEBUG("NAT rule added, hdl %d, data: 0x%X, %d, 0x%X, %d, %d, %d\n",
+			m_nat_rule_hdl1, ipv4_rule.target_ip, ipv4_rule.target_port,
+			ipv4_rule.private_ip, ipv4_rule.private_port,
+			ipv4_rule.protocol, ipv4_rule.public_port);
+
+		LOG_MSG_DEBUG("Leaving\n");
+		return true;
+	}// AddRules()
+
+	virtual bool ModifyPackets()
+	{
+		uint32_t address;
+		uint16_t port;
+		char flags = 0x18;
+
+		address = htonl(m_public_ip);//192.23.22.1
+		memcpy(&m_sendBuffer[IPV4_DST_ADDR_OFFSET], &address, sizeof(address));
+		port = htons(m_public_port);
+		memcpy(&m_sendBuffer[IPV4_DST_PORT_OFFSET], &port, sizeof(port));
+
+		address = htonl(m_target_ip);/* 193.23.22.1 */
+		memcpy(&m_sendBuffer[IPV4_SRC_ADDR_OFFSET], &address, sizeof(address));
+		port = htons(m_target_port+1);
+		memcpy(&m_sendBuffer[IPV4_SRC_PORT_OFFSET], &port, sizeof(port));
+
+		//make sure the FIN flag is not set, otherwise we will get a NAT miss
+		memcpy(&m_sendBuffer[IPV4_TCP_FLAGS_OFFSET], &flags, sizeof(flags));
+
+		return true;
+	}// ModifyPacktes ()
+
+	virtual bool SendPackets()
+	{
+		bool isSuccess = false;
+
+		// Send first packet
+		isSuccess = m_producer.SendData(m_sendBuffer, m_sendSize);
+		if (false == isSuccess)
+		{
+			LOG_MSG_ERROR("SendData failure.\n");
+			return false;
+		}
+
+		LOG_MSG_DEBUG("sent successfully one packet\n");
+		return true;
+	}
+
+	virtual bool ReceivePacketsAndCompare()
+	{
+		size_t receivedSize = 0;
+		bool isSuccess = true;
+
+		// Receive results
+		Byte *rxBuff1 = new Byte[0x400];
+
+		if (NULL == rxBuff1)
+		{
+			LOG_MSG_ERROR("Memory allocation error.\n");
+			return false;
+		}
+
+		receivedSize = m_consumer.ReceiveData(rxBuff1, 0x400);
+		LOG_MSG_DEBUG("Received %zu bytes on %s.\n", receivedSize, m_consumer.m_fromChannelName.c_str());
+
+		// Compare results
+		if (!CompareResultVsGolden(m_sendBuffer, m_sendSize, rxBuff1, receivedSize))
+		{
+			printf("Comparison of Buffer0 Failed!\n");
+			isSuccess = false;
+		}
+
+		char recievedBuffer[256] = { 0 };
+		char SentBuffer[256] = { 0 };
+		size_t j;
+
+		for (j = 0; j < m_sendSize; j++)
+			snprintf(&SentBuffer[3 * j], sizeof(SentBuffer) - (3 * j + 1), " %02X", m_sendBuffer[j]);
+		for (j = 0; j < receivedSize; j++)
+			snprintf(&recievedBuffer[3 * j], sizeof(recievedBuffer) - (3 * j + 1), " %02X", rxBuff1[j]);
+		LOG_MSG_STACK("sent Value1 (%zu)\n%s\n, Received Value1(%zu)\n%s\n", m_sendSize, SentBuffer, receivedSize, recievedBuffer);
+
+		delete[] rxBuff1;
+
+		return isSuccess;
+	}
+};
+
+/*---------------------------------------------------------------------------*/
+/* Test022: Single PDN dst NAT test with NAT suppresssion and status enabled */
+/* NOTE: other classes are derived from this class - change carefully        */
+/*---------------------------------------------------------------------------*/
+class IpaNatBlockTest022 : public IpaNatBlockTestFixture
+{
+public:
+	IpaNatBlockTest022()
+	{
+		m_name = "IpaNatBlockTest022";
+		m_description =
+			"NAT block test 022 - single PDN dst NAT test with NAT suppression and status enabled \
+		1. Generate and commit three routing tables (only one is used). \
+			Each table contains a single \"bypass\" rule (all data goes to output pipe 0, 1  and 2 (accordingly)) \
+		2. Generate and commit one filtering rule: (DST & Mask Match). \
+			action go to dst NAT \
+			All DST_IP == (192.23.22.1 & 0.255.255.255)traffic goes to NAT block (public IP filtering) \
+		3. generate and commit one NAT rule:\
+			public ip 192.23.22.1 --> private ip 194.23.22.1. \
+		4. Send packet not matching with NAT entry.\n";
+		m_private_ip = 0xC2171601; /* 194.23.22.1 */
+		m_private_port = 5678;
+		m_public_ip = 0xC0171601;   /* "192.23.22.1" */
+		m_public_port = 9050;
+		m_target_ip = 0xC1171601; /* 193.23.22.1 */
+		m_target_port = 1234;
+		Register(*this);
+	}
+
+	virtual bool Setup()
+	{
+		return IpaNatBlockTestFixture::Setup(true, true);
+	}
+
+	virtual bool AddRules()
+	{
+		LOG_MSG_DEBUG("Entering\n");
+
+		const char bypass0[20] = "Bypass0";
+		const char bypass1[20] = "Bypass1";
+		const char bypass2[20] = "Bypass2";
+		struct ipa_ioc_get_rt_tbl routing_table0;
+
+		if (!CreateThreeIPv4BypassRoutingTables(bypass0, bypass1, bypass2))
+		{
+			LOG_MSG_ERROR("CreateThreeBypassRoutingTables Failed\n");
+			return false;
+		}
+
+		LOG_MSG_DEBUG("CreateThreeBypassRoutingTables completed successfully\n");
+		routing_table0.ip = IPA_IP_v4;
+		strlcpy(routing_table0.name, bypass0, sizeof(routing_table0.name));
+		if (!m_routing.GetRoutingTable(&routing_table0))
+		{
+			LOG_MSG_ERROR("m_routing.GetRoutingTable(&routing_table0=0x%p) Failed.\n", &routing_table0);
+			return false;
+		}
+		LOG_MSG_DEBUG("%s route table handle = %u\n", bypass0, routing_table0.hdl);
+
+		/* Setup NAT Exception routing table. */
+		if (!m_routing.SetNatConntrackExcRoutingTable(routing_table0.hdl, true))
+		{
+			LOG_MSG_ERROR("m_routing.SetNatConntrackExcRoutingTable(routing_table0 hdl=%d) Failed.\n",
+				routing_table0.hdl);
+			return false;
+		}
+
+		IPAFilteringTable FilterTable0;
+		struct ipa_flt_rule_add flt_rule_entry;
+		FilterTable0.Init(IPA_IP_v4, IPA_CLIENT_TEST_PROD, false, 3);
+		LOG_MSG_DEBUG("FilterTable*.Init Completed Successfully..\n");
+
+		// Configuring Filtering Rule No.0
+		FilterTable0.GeneratePresetRule(1, flt_rule_entry);
+		flt_rule_entry.at_rear = true;
+		flt_rule_entry.flt_rule_hdl = -1; // return Value
+		flt_rule_entry.status = -1; // return value
+		flt_rule_entry.rule.action = IPA_PASS_TO_DST_NAT;
+		flt_rule_entry.rule.rt_tbl_hdl = routing_table0.hdl; //put here the handle corresponding to Routing Rule 1
+		flt_rule_entry.rule.attrib.attrib_mask = IPA_FLT_DST_ADDR;
+		flt_rule_entry.rule.attrib.u.v4.dst_addr_mask = 0x00FFFFFF; // Mask
+		flt_rule_entry.rule.attrib.u.v4.dst_addr = m_public_ip; // Filter DST_IP == 192.23.22.1
+		flt_rule_entry.rule.pdn_idx = 0;
+		flt_rule_entry.rule.set_metadata = 0;
+		if (
+			((uint8_t)-1 == FilterTable0.AddRuleToTable(flt_rule_entry)) ||
+			!m_filtering.AddFilteringRule(FilterTable0.GetFilteringTable())
+			)
+		{
+			LOG_MSG_ERROR("Error Adding Rule to Filter Table, aborting...\n");
+			return false;
+		}
+		else
+		{
+			LOG_MSG_DEBUG("flt rule hdl0=0x%x, status=0x%x\n", FilterTable0.ReadRuleFromTable(0)->flt_rule_hdl, FilterTable0.ReadRuleFromTable(0)->status);
+		}
+
+		//NAT table and rules creation
+		int total_entries = 20;
+		int ret;
+		ipa_nat_ipv4_rule ipv4_rule;
+		uint32_t pub_ip_add = m_public_ip;
+
+		ret = ipa_nat_add_ipv4_tbl(pub_ip_add, m_mem_type, total_entries, &m_tbl_hdl);
+		if (ret) {
+			LOG_MSG_ERROR("Leaving, failed creating NAT table\n");
+			return false;
+		}
+
+		LOG_MSG_DEBUG("nat table added, hdl %d, public ip 0x%X\n", m_tbl_hdl,
+			pub_ip_add);
+
+		ipv4_rule.target_ip = m_target_ip;
+		ipv4_rule.target_port = m_target_port;
+		ipv4_rule.private_ip = m_private_ip;
+		ipv4_rule.private_port = m_private_port;
+		ipv4_rule.protocol = IPPROTO_TCP;
+		ipv4_rule.public_port = m_public_port;
+		ipv4_rule.pdn_index = 0;
+
+		ret = ipa_nat_add_ipv4_rule(m_tbl_hdl, &ipv4_rule, &m_nat_rule_hdl1);
+		if (ret) {
+			LOG_MSG_ERROR("Leaving, failed adding NAT rule 0\n");
+			return false;
+		}
+
+		LOG_MSG_DEBUG("NAT rule added, hdl %d, data: 0x%X, %d, 0x%X, %d, %d, %d\n",
+			m_nat_rule_hdl1, ipv4_rule.target_ip, ipv4_rule.target_port,
+			ipv4_rule.private_ip, ipv4_rule.private_port,
+			ipv4_rule.protocol, ipv4_rule.public_port);
+
+		LOG_MSG_DEBUG("Leaving\n");
+		return true;
+	}// AddRules()
+
+	virtual bool ModifyPackets()
+	{
+		uint32_t address;
+		uint16_t port;
+		char flags = 0x18;
+
+		address = htonl(m_public_ip);//192.23.22.1
+		memcpy(&m_sendBuffer[IPV4_DST_ADDR_OFFSET], &address, sizeof(address));
+		port = htons(m_public_port);
+		memcpy(&m_sendBuffer[IPV4_DST_PORT_OFFSET], &port, sizeof(port));
+
+		address = htonl(m_target_ip);/* 193.23.22.1 */
+		memcpy(&m_sendBuffer[IPV4_SRC_ADDR_OFFSET], &address, sizeof(address));
+		port = htons(m_target_port+1);
+		memcpy(&m_sendBuffer[IPV4_SRC_PORT_OFFSET], &port, sizeof(port));
+
+		//make sure the FIN flag is not set, otherwise we will get a NAT miss
+		memcpy(&m_sendBuffer[IPV4_TCP_FLAGS_OFFSET], &flags, sizeof(flags));
+
+		return true;
+	}// ModifyPacktes ()
+
+	virtual bool SendPackets()
+	{
+		bool isSuccess = false;
+
+		// Send first packet
+		isSuccess = m_producer.SendData(m_sendBuffer, m_sendSize);
+		if (false == isSuccess)
+		{
+			LOG_MSG_ERROR("SendData failure.\n");
+			return false;
+		}
+
+		LOG_MSG_DEBUG("sent successfully one packet\n");
+		return true;
+	}
+
+	virtual bool ReceivePacketsAndCompare()
+	{
+		size_t receivedSize = 0;
+		bool isSuccess = true;
+		struct ipa3_hw_pkt_status_hw_v5_5 *status = NULL;
+
+		// Receive results
+		Byte *rxBuff1 = new Byte[0x400];
+
+		if (NULL == rxBuff1)
+		{
+			LOG_MSG_ERROR("Memory allocation error.\n");
+			return false;
+		}
+
+		receivedSize = m_consumer.ReceiveData(rxBuff1, 0x400);
+		LOG_MSG_DEBUG("Received %zu bytes on %s.\n", receivedSize, m_consumer.m_fromChannelName.c_str());
+
+		// Compare results
+		if (!CompareResultVsGolden_w_Status(m_sendBuffer, m_sendSize, rxBuff1, receivedSize))
+		{
+			printf("Comparison of Buffer0 Failed!\n");
+			isSuccess = false;
+		}
+
+		status = (struct ipa3_hw_pkt_status_hw_v5_5 *)rxBuff1;
+		if (!status->nat_exc_suppress)
+		{
+			printf("NAT Suppression not hit!\n");
+			isSuccess = false;
+		}
+
+		char recievedBuffer[256] = { 0 };
+		char SentBuffer[256] = { 0 };
+		size_t j;
+
+		for (j = 0; j < m_sendSize; j++)
+			snprintf(&SentBuffer[3 * j], sizeof(SentBuffer) - (3 * j + 1), " %02X", m_sendBuffer[j]);
+		for (j = 0; j < receivedSize; j++)
+			snprintf(&recievedBuffer[3 * j], sizeof(recievedBuffer) - (3 * j + 1), " %02X", rxBuff1[j]);
+		LOG_MSG_STACK("sent Value1 (%zu)\n%s\n, Received Value1(%zu)\n%s\n", m_sendSize, SentBuffer, receivedSize, recievedBuffer);
+
+		delete[] rxBuff1;
+
+		return isSuccess;
+	}
+};
+
+
 static class IpaNatBlockTest001 IpaNatBlockTest001;//single PDN src NAT test
 static class IpaNatBlockTest002 IpaNatBlockTest002;//single PDN dst NAT test
 static class IpaNatBlockTest003 IpaNatBlockTest003;//multi PDN (tuple) src NAT test
@@ -4285,4 +5148,7 @@
 static class IpaNatBlockTest016 IpaNatBlockTest016;//single PDN dst NAT test - send two packets that will hit the same rule
 static class IpaNatBlockTest017 IpaNatBlockTest017;//multi PDN (tuple) src NAT test - identical private IPs different ports
 static class IpaNatBlockTest018 IpaNatBlockTest018;//multi PDN (tuple) dst NAT test - identical private IPs different ports
-
+static class IpaNatBlockTest019 IpaNatBlockTest019;//Single PDN SRC NAT suppression test
+static class IpaNatBlockTest020 IpaNatBlockTest020;//Single PDN SRC NAT suppression test with status
+static class IpaNatBlockTest021 IpaNatBlockTest021;//Single PDN DST NAT suppression test
+static class IpaNatBlockTest022 IpaNatBlockTest022;//Single PDN DST NAT suppression test with status
diff --git a/kernel-tests/RoutingDriverWrapper.cpp b/kernel-tests/RoutingDriverWrapper.cpp
index 6fbb0f4..30fcdf2 100644
--- a/kernel-tests/RoutingDriverWrapper.cpp
+++ b/kernel-tests/RoutingDriverWrapper.cpp
@@ -189,3 +189,25 @@
 	return true;
 }
 
+bool RoutingDriverWrapper::SetNatConntrackExcRoutingTable(uint32_t routingTableHandle, bool nat_or_conntrack)
+{
+	int retval = 0;
+
+	if (!DeviceNodeIsOpened())
+		return false;
+
+	if (nat_or_conntrack)
+		retval = ioctl(m_fd, IPA_IOC_SET_NAT_EXC_RT_TBL_IDX, routingTableHandle);
+	else
+		retval = ioctl(m_fd, IPA_IOC_SET_CONN_TRACK_EXC_RT_TBL_IDX, routingTableHandle);
+	
+	if (retval) {
+		printf("%s(), IPA_IOC_SET_CONN_TRACK_EXC_RT_TBL_IDX ioctl failed.\n", __FUNCTION__);
+		return false;
+	}
+
+	printf("%s(), %s ioctl issued to IPA routing block.\n", __FUNCTION__,(nat_or_conntrack) ?
+	"IPA_IOC_SET_NAT_EXC_RT_TBL_IDX" : "IPA_IOC_SET_CONN_TRACK_EXC_RT_TBL_IDX");
+	return true;
+}
+
diff --git a/kernel-tests/RoutingDriverWrapper.h b/kernel-tests/RoutingDriverWrapper.h
index fb13d9a..4c1e216 100644
--- a/kernel-tests/RoutingDriverWrapper.h
+++ b/kernel-tests/RoutingDriverWrapper.h
@@ -80,6 +80,7 @@
 	bool Reset(enum ipa_ip_type ip);
 	bool GetRoutingTable(struct ipa_ioc_get_rt_tbl *routingTable);
 	bool PutRoutingTable(uint32_t routingTableHandle);
+	bool SetNatConntrackExcRoutingTable(uint32_t routingTableHandle, bool nat_or_conntrack);
 };
 
 #endif