Merge tag 'LA.UM.8.1.r1-09500-sm8150.0' of https://source.codeaurora.org/quic/la/platform/vendor/qcom-opensource/data-ipa-cfg-mgr into lineage-17.0

"LA.UM.8.1.r1-09500-sm8150.0"

Change-Id: Icac11a6a986ac0a99bb97b2a735b7c8c74842b4b
diff --git a/ipacm/inc/IPACM_Config.h b/ipacm/inc/IPACM_Config.h
index af1b616..9230f7d 100644
--- a/ipacm/inc/IPACM_Config.h
+++ b/ipacm/inc/IPACM_Config.h
@@ -147,6 +147,11 @@
 
 	bool isMCC_Mode;
 
+	/* IPA_HW_FNR_STATS */
+	bool hw_fnr_stats_support;
+	int hw_counter_offset;
+	int sw_counter_offset;
+
 	/* To return the instance */
 	static IPACM_Config* GetInstance();
 
diff --git a/ipacm/inc/IPACM_Filtering.h b/ipacm/inc/IPACM_Filtering.h
index 5ceab75..428c21a 100644
--- a/ipacm/inc/IPACM_Filtering.h
+++ b/ipacm/inc/IPACM_Filtering.h
@@ -56,6 +56,10 @@
 	~IPACM_Filtering();
 	bool AddFilteringRule(struct ipa_ioc_add_flt_rule const *ruleTable);
 	bool AddFilteringRuleAfter(struct ipa_ioc_add_flt_rule_after const *ruleTable);
+#ifdef IPA_IOCTL_SET_FNR_COUNTER_INFO
+	bool AddFilteringRule_hw_index(struct ipa_ioc_add_flt_rule *ruleTable, int hw_counter_index);
+	bool AddFilteringRuleAfter_hw_index(struct ipa_ioc_add_flt_rule_after *ruleTable, int hw_counter_index);
+#endif //IPA_IOCTL_SET_FNR_COUNTER_INFO
 	bool DeleteFilteringRule(struct ipa_ioc_del_flt_rule *ruleTable);
 	bool Commit(enum ipa_ip_type ip);
 	bool Reset(enum ipa_ip_type ip);
diff --git a/ipacm/inc/IPACM_Iface.h b/ipacm/inc/IPACM_Iface.h
index df192c9..55d9e99 100644
--- a/ipacm/inc/IPACM_Iface.h
+++ b/ipacm/inc/IPACM_Iface.h
@@ -142,10 +142,10 @@
 	void change_to_network_order(ipa_ip_type iptype, ipa_rule_attrib* attrib);
 
 	/* software routing enable */
-	virtual int handle_software_routing_enable(void);
+	virtual int handle_software_routing_enable(bool mhip);
 
 	/* software routing disable */
-	virtual int handle_software_routing_disable(void);
+	virtual int handle_software_routing_disable(bool mhip);
 	void delete_iface(void);
 
 	bool is_global_ipv6_addr(uint32_t* ipv6_addr);
diff --git a/ipacm/inc/IPACM_Routing.h b/ipacm/inc/IPACM_Routing.h
index b5ffabc..663076b 100644
--- a/ipacm/inc/IPACM_Routing.h
+++ b/ipacm/inc/IPACM_Routing.h
@@ -55,6 +55,10 @@
 	~IPACM_Routing();
 
 	bool AddRoutingRule(struct ipa_ioc_add_rt_rule *ruleTable);
+
+#ifdef IPA_IOCTL_SET_FNR_COUNTER_INFO
+	bool AddRoutingRule_hw_index(struct ipa_ioc_add_rt_rule *ruleTable, int hw_counter_index);
+#endif //IPA_IOCTL_SET_FNR_COUNTER_INFO
 	bool DeleteRoutingRule(struct ipa_ioc_del_rt_rule *ruleTable);
 
 	bool Commit(enum ipa_ip_type ip);
diff --git a/ipacm/inc/IPACM_Wan.h b/ipacm/inc/IPACM_Wan.h
index 9acf175..9f1001a 100644
--- a/ipacm/inc/IPACM_Wan.h
+++ b/ipacm/inc/IPACM_Wan.h
@@ -370,6 +370,12 @@
 	/* handle for icmpv6 exception rule */
 	uint32_t icmpv6_exception_hdl;
 
+	/* handle for TCP FIN rule */
+	uint32_t tcp_fin_hdl;
+
+	/* handle for TCP RST rule */
+	uint32_t tcp_rst_hdl;
+
 	inline ipa_wan_client* get_client_memptr(ipa_wan_client *param, int cnt)
 	{
 	    char *ret = ((char *)param) + (wan_client_len * cnt);
@@ -636,6 +642,10 @@
 	int add_icmpv6_exception_rule();
 
 	int delete_icmpv6_exception_rule();
+
+	int add_tcp_fin_rst_exception_rule();
+
+	int delete_tcp_fin_rst_exception_rule();
 };
 
 #endif /* IPACM_WAN_H */
diff --git a/ipacm/src/Android.mk b/ipacm/src/Android.mk
index 7c9f1b2..d8a5104 100644
--- a/ipacm/src/Android.mk
+++ b/ipacm/src/Android.mk
@@ -1,3 +1,13 @@
+TARGET_DISABLE_IPACM := false
+
+ifeq ($(TARGET_USES_QMAA),true)
+ifneq ($(TARGET_USES_QMAA_OVERRIDE_DATA),true)
+	TARGET_DISABLE_IPACM := true
+endif #TARGET_USES_QMAA_OVERRIDE_DATA
+endif #TARGET_USES_QMAA
+
+
+ifneq ($(TARGET_DISABLE_IPACM),true)
 ifneq ($(TARGET_HAS_LOW_RAM),true)
 BOARD_PLATFORM_LIST := msm8909
 BOARD_PLATFORM_LIST += msm8916
@@ -127,3 +137,4 @@
 endif
 endif
 endif
+endif
\ No newline at end of file
diff --git a/ipacm/src/IPACM_Config.cpp b/ipacm/src/IPACM_Config.cpp
index bfacd7c..781f1cb 100644
--- a/ipacm/src/IPACM_Config.cpp
+++ b/ipacm/src/IPACM_Config.cpp
@@ -143,6 +143,10 @@
 	ipa_bridge_enable = false;
 	isMCC_Mode = false;
 	ipa_max_valid_rm_entry = 0;
+	/* IPA_HW_FNR_STATS */
+	hw_fnr_stats_support = false;
+	hw_counter_offset = 0;
+	sw_counter_offset = 0;
 
 	memset(&rt_tbl_default_v4, 0, sizeof(rt_tbl_default_v4));
 	memset(&rt_tbl_lan_v4, 0, sizeof(rt_tbl_lan_v4));
diff --git a/ipacm/src/IPACM_Filtering.cpp b/ipacm/src/IPACM_Filtering.cpp
index a5c2bf5..a158d74 100644
--- a/ipacm/src/IPACM_Filtering.cpp
+++ b/ipacm/src/IPACM_Filtering.cpp
@@ -116,6 +116,242 @@
 	return true;
 }
 
+#ifdef IPA_IOCTL_SET_FNR_COUNTER_INFO
+bool IPACM_Filtering::AddFilteringRule_hw_index(struct ipa_ioc_add_flt_rule *ruleTable, int hw_counter_index)
+{
+	int retval=0, cnt = 0, len = 0;
+	struct ipa_ioc_add_flt_rule_v2 *ruleTable_v2;
+	struct ipa_flt_rule_add_v2 flt_rule_entry;
+	bool ret = true;
+
+	IPACMDBG("Printing filter add attributes\n");
+	IPACMDBG("ip type: %d\n", ruleTable->ip);
+	IPACMDBG("Number of rules: %d\n", ruleTable->num_rules);
+	IPACMDBG("End point: %d and global value: %d\n", ruleTable->ep, ruleTable->global);
+	IPACMDBG("commit value: %d\n", ruleTable->commit);
+
+	/* change to v2 format*/
+	len = sizeof(struct ipa_ioc_add_flt_rule_v2);
+	ruleTable_v2 = (struct ipa_ioc_add_flt_rule_v2*)malloc(len);
+	if (ruleTable_v2 == NULL)
+	{
+		IPACMERR("Error Locate ipa_ioc_add_flt_rule_v2 memory...\n");
+		return false;
+	}
+	memset(ruleTable_v2, 0, len);
+	ruleTable_v2->rules = (uint64_t)calloc(ruleTable->num_rules, sizeof(struct ipa_flt_rule_add_v2));
+	if (!ruleTable_v2->rules) {
+		IPACMERR("Failed to allocate memory for filtering rules\n");
+		ret = false;
+		goto fail_tbl;
+	}
+
+	ruleTable_v2->commit = ruleTable->commit;
+	ruleTable_v2->ep = ruleTable->ep;
+	ruleTable_v2->global = ruleTable->global;
+	ruleTable_v2->ip = ruleTable->ip;
+	ruleTable_v2->num_rules = ruleTable->num_rules;
+	ruleTable_v2->flt_rule_size = sizeof(struct ipa_flt_rule_add_v2);
+
+	for (cnt=0; cnt < ruleTable->num_rules; cnt++)
+	{
+		memset(&flt_rule_entry, 0, sizeof(struct ipa_flt_rule_add_v2));
+		flt_rule_entry.at_rear = ruleTable->rules[cnt].at_rear;
+		flt_rule_entry.rule.retain_hdr = ruleTable->rules[cnt].rule.retain_hdr;
+		flt_rule_entry.rule.to_uc = ruleTable->rules[cnt].rule.to_uc;
+		flt_rule_entry.rule.action = ruleTable->rules[cnt].rule.action;
+		flt_rule_entry.rule.rt_tbl_hdl = ruleTable->rules[cnt].rule.rt_tbl_hdl;
+		flt_rule_entry.rule.rt_tbl_idx = ruleTable->rules[cnt].rule.rt_tbl_idx;
+		flt_rule_entry.rule.eq_attrib_type = ruleTable->rules[cnt].rule.eq_attrib_type;
+		flt_rule_entry.rule.max_prio = ruleTable->rules[cnt].rule.max_prio;
+		flt_rule_entry.rule.hashable = ruleTable->rules[cnt].rule.hashable;
+		flt_rule_entry.rule.rule_id = ruleTable->rules[cnt].rule.rule_id;
+		flt_rule_entry.rule.set_metadata = ruleTable->rules[cnt].rule.set_metadata;
+		flt_rule_entry.rule.pdn_idx = ruleTable->rules[cnt].rule.pdn_idx;
+		memcpy(&flt_rule_entry.rule.eq_attrib,
+					 &ruleTable->rules[cnt].rule.eq_attrib,
+					 sizeof(flt_rule_entry.rule.eq_attrib));
+		memcpy(&flt_rule_entry.rule.attrib,
+					 &ruleTable->rules[cnt].rule.attrib,
+					 sizeof(flt_rule_entry.rule.attrib));
+		IPACMDBG("Filter rule:%d attrib mask: 0x%x\n", cnt,
+				ruleTable->rules[cnt].rule.attrib.attrib_mask);
+		/* 0 means disable hw-counter-sats */
+		if (hw_counter_index != 0)
+		{
+			flt_rule_entry.rule.enable_stats = 1;
+			flt_rule_entry.rule.cnt_idx = hw_counter_index;
+		}
+
+		/* copy to v2 table*/
+		memcpy((void *)(ruleTable_v2->rules + (cnt * sizeof(struct ipa_flt_rule_add_v2))),
+			&flt_rule_entry, sizeof(flt_rule_entry));
+	}
+
+	retval = ioctl(fd, IPA_IOC_ADD_FLT_RULE_V2, ruleTable_v2);
+	if (retval != 0)
+	{
+		IPACMERR("Failed adding Filtering rule %pK\n", ruleTable_v2);
+		PERROR("unable to add filter rule:");
+
+		for (int cnt = 0; cnt < ruleTable_v2->num_rules; cnt++)
+		{
+			if (((struct ipa_flt_rule_add_v2 *)ruleTable_v2->rules)[cnt].status != 0)
+			{
+				IPACMERR("Adding Filter rule:%d failed with status:%d\n",
+								 cnt, ((struct ipa_flt_rule_add_v2 *)ruleTable_v2->rules)[cnt].status);
+			}
+		}
+		ret = false;
+		goto fail_rule;
+	}
+
+	/* copy results from v2 to v1 format */
+	for (int cnt = 0; cnt < ruleTable->num_rules; cnt++)
+	{
+		/* copy status to v1 format */
+		ruleTable->rules[cnt].status = ((struct ipa_flt_rule_add_v2 *)ruleTable_v2->rules)[cnt].status;
+		ruleTable->rules[cnt].flt_rule_hdl = ((struct ipa_flt_rule_add_v2 *)ruleTable_v2->rules)[cnt].flt_rule_hdl;
+
+		if(((struct ipa_flt_rule_add_v2 *)ruleTable_v2->rules)[cnt].status != 0)
+		{
+			IPACMERR("Adding Filter rule:%d failed with status:%d\n",
+							 cnt, ((struct ipa_flt_rule_add_v2 *) ruleTable_v2->rules)[cnt].status);
+		}
+	}
+
+	IPACMDBG("Added Filtering rule %pK\n", ruleTable_v2);
+
+fail_rule:
+	if((void *)ruleTable_v2->rules != NULL)
+		free((void *)ruleTable_v2->rules);
+fail_tbl:
+	if (ruleTable_v2 != NULL)
+		free(ruleTable_v2);
+	return ret;
+}
+
+bool IPACM_Filtering::AddFilteringRuleAfter_hw_index(struct ipa_ioc_add_flt_rule_after *ruleTable, int hw_counter_index)
+{
+	bool ret = true;
+#ifdef FEATURE_IPA_V3
+	int retval=0, cnt = 0, len = 0;
+	struct ipa_ioc_add_flt_rule_after_v2 *ruleTable_v2;
+	struct ipa_flt_rule_add_v2 flt_rule_entry;
+
+	IPACMDBG("Printing filter add attributes\n");
+	IPACMDBG("ep: %d\n", ruleTable->ep);
+	IPACMDBG("ip type: %d\n", ruleTable->ip);
+	IPACMDBG("Number of rules: %d\n", ruleTable->num_rules);
+	IPACMDBG("add_after_hdl: %d\n", ruleTable->add_after_hdl);
+	IPACMDBG("commit value: %d\n", ruleTable->commit);
+
+	/* change to v2 format*/
+	len = sizeof(struct ipa_ioc_add_flt_rule_after_v2);
+	ruleTable_v2 = (struct ipa_ioc_add_flt_rule_after_v2*)malloc(len);
+	if (ruleTable_v2 == NULL)
+	{
+		IPACMERR("Error Locate ipa_ioc_add_flt_rule_after_v2 memory...\n");
+		return false;
+	}
+	memset(ruleTable_v2, 0, len);
+	ruleTable_v2->rules = (uint64_t)calloc(ruleTable->num_rules, sizeof(struct ipa_flt_rule_add_v2));
+	if (!ruleTable_v2->rules) {
+		IPACMERR("Failed to allocate memory for filtering rules\n");
+		ret = false;
+		goto fail_tbl;
+	}
+
+	ruleTable_v2->commit = ruleTable->commit;
+	ruleTable_v2->ep = ruleTable->ep;
+	ruleTable_v2->ip = ruleTable->ip;
+	ruleTable_v2->num_rules = ruleTable->num_rules;
+	ruleTable_v2->add_after_hdl = ruleTable->add_after_hdl;
+	ruleTable_v2->flt_rule_size = sizeof(struct ipa_flt_rule_add_v2);
+
+	for (cnt=0; cnt < ruleTable->num_rules; cnt++)
+	{
+		memset(&flt_rule_entry, 0, sizeof(struct ipa_flt_rule_add_v2));
+		flt_rule_entry.at_rear = ruleTable->rules[cnt].at_rear;
+		flt_rule_entry.rule.retain_hdr = ruleTable->rules[cnt].rule.retain_hdr;
+		flt_rule_entry.rule.to_uc = ruleTable->rules[cnt].rule.to_uc;
+		flt_rule_entry.rule.action = ruleTable->rules[cnt].rule.action;
+		flt_rule_entry.rule.rt_tbl_hdl = ruleTable->rules[cnt].rule.rt_tbl_hdl;
+		flt_rule_entry.rule.rt_tbl_idx = ruleTable->rules[cnt].rule.rt_tbl_idx;
+		flt_rule_entry.rule.eq_attrib_type = ruleTable->rules[cnt].rule.eq_attrib_type;
+		flt_rule_entry.rule.max_prio = ruleTable->rules[cnt].rule.max_prio;
+		flt_rule_entry.rule.hashable = ruleTable->rules[cnt].rule.hashable;
+		flt_rule_entry.rule.rule_id = ruleTable->rules[cnt].rule.rule_id;
+		flt_rule_entry.rule.set_metadata = ruleTable->rules[cnt].rule.set_metadata;
+		flt_rule_entry.rule.pdn_idx = ruleTable->rules[cnt].rule.pdn_idx;
+		memcpy(&flt_rule_entry.rule.eq_attrib,
+					 &ruleTable->rules[cnt].rule.eq_attrib,
+					 sizeof(flt_rule_entry.rule.eq_attrib));
+		memcpy(&flt_rule_entry.rule.attrib,
+					 &ruleTable->rules[cnt].rule.attrib,
+					 sizeof(flt_rule_entry.rule.attrib));
+		IPACMDBG("Filter rule:%d attrib mask: 0x%x\n", cnt,
+				ruleTable->rules[cnt].rule.attrib.attrib_mask);
+		/* 0 means disable hw-counter-sats */
+		if (hw_counter_index != 0)
+		{
+			flt_rule_entry.rule.enable_stats = 1;
+			flt_rule_entry.rule.cnt_idx = hw_counter_index;
+		}
+
+		/* copy to v2 table*/
+		memcpy((void *)(ruleTable_v2->rules + (cnt * sizeof(struct ipa_flt_rule_add_v2))),
+			&flt_rule_entry, sizeof(flt_rule_entry));
+	}
+
+	retval = ioctl(fd, IPA_IOC_ADD_FLT_RULE_AFTER_V2, ruleTable_v2);
+	if (retval != 0)
+	{
+		IPACMERR("Failed adding Filtering rule %pK\n", ruleTable_v2);
+		PERROR("unable to add filter rule:");
+
+		for (int cnt = 0; cnt < ruleTable_v2->num_rules; cnt++)
+		{
+			if (((struct ipa_flt_rule_add_v2 *)ruleTable_v2->rules)[cnt].status != 0)
+			{
+				IPACMERR("Adding Filter rule:%d failed with status:%d\n",
+								 cnt, ((struct ipa_flt_rule_add_v2 *)ruleTable_v2->rules)[cnt].status);
+			}
+		}
+		ret = false;
+		goto fail_rule;
+	}
+
+	/* copy results from v2 to v1 format */
+	for (int cnt = 0; cnt < ruleTable->num_rules; cnt++)
+	{
+		/* copy status to v1 format */
+		ruleTable->rules[cnt].status = ((struct ipa_flt_rule_add_v2 *)ruleTable_v2->rules)[cnt].status;
+		ruleTable->rules[cnt].flt_rule_hdl = ((struct ipa_flt_rule_add_v2 *)ruleTable_v2->rules)[cnt].flt_rule_hdl;
+
+		if(((struct ipa_flt_rule_add_v2 *)ruleTable_v2->rules)[cnt].status != 0)
+		{
+			IPACMERR("Adding Filter rule:%d failed with status:%d\n",
+							 cnt, ((struct ipa_flt_rule_add_v2 *) ruleTable_v2->rules)[cnt].status);
+		}
+	}
+
+	IPACMDBG("Added Filtering rule %pK\n", ruleTable_v2);
+
+fail_rule:
+	if((void *)ruleTable_v2->rules != NULL)
+		free((void *)ruleTable_v2->rules);
+fail_tbl:
+	if (ruleTable_v2 != NULL)
+		free(ruleTable_v2);
+#else
+	if (ruleTable)
+	IPACMERR("Not support adding Filtering rule %pK\n", ruleTable);
+#endif
+	return ret;
+}
+#endif //IPA_IOCTL_SET_FNR_COUNTER_INFO
+
 bool IPACM_Filtering::AddFilteringRuleAfter(struct ipa_ioc_add_flt_rule_after const *ruleTable)
 {
 #ifdef FEATURE_IPA_V3
diff --git a/ipacm/src/IPACM_Iface.cpp b/ipacm/src/IPACM_Iface.cpp
index 55e19ba..ec4a9ea 100644
--- a/ipacm/src/IPACM_Iface.cpp
+++ b/ipacm/src/IPACM_Iface.cpp
@@ -88,12 +88,14 @@
 }
 
 /* software routing enable */
-int IPACM_Iface::handle_software_routing_enable(void)
+int IPACM_Iface::handle_software_routing_enable(bool mhip)
 {
-
+	int fd =0;
 	int res = IPACM_SUCCESS;
 	struct ipa_flt_rule_add flt_rule_entry;
 	ipa_ioc_add_flt_rule *m_pFilteringTable;
+	/* contruct filter rules to pcie modem */
+	ipa_ioc_generate_flt_eq flt_eq;
 
 	IPACMDBG("\n");
 	if (softwarerouting_act == true)
@@ -121,11 +123,9 @@
 	}
 
 	m_pFilteringTable->commit = 1;
-	m_pFilteringTable->ep = rx_prop->rx[0].src_pipe;
 	m_pFilteringTable->global = false;
 	m_pFilteringTable->num_rules = (uint8_t)1;
 
-
 	/* Configuring Software-Routing Filtering Rule */
 	memset(&flt_rule_entry, 0, sizeof(struct ipa_flt_rule_add));
 
@@ -139,72 +139,52 @@
 	memcpy(&flt_rule_entry.rule.attrib,
 				 &rx_prop->rx[0].attrib,
 				 sizeof(flt_rule_entry.rule.attrib));
-	/* disble meta-data filtering skylar */
-	if (IPACM_Wan::backhaul_mode == Q6_MHI_WAN)
-	{
-		flt_rule_entry.rule.attrib.attrib_mask &= ~((uint32_t)IPA_FLT_META_DATA);
-		IPACMDBG_H("disable meta-data filtering 0x%x\n", flt_rule_entry.rule.attrib.attrib_mask);
-	}
 
 	memcpy(&(m_pFilteringTable->rules[0]), &flt_rule_entry, sizeof(struct ipa_flt_rule_add));
 
 	/* check iface is v4 or v6 or both*/
-//	if (ip_type == IPA_IP_MAX)
-//	{
+
 		/* handle v4 */
 		m_pFilteringTable->ip = IPA_IP_v4;
-		if (false == m_filtering.AddFilteringRule(m_pFilteringTable))
+
+	fd = open(IPA_DEVICE_NAME, O_RDWR);
+	if (fd < 0)
+	{
+		IPACMERR("Failed opening %s.\n", IPA_DEVICE_NAME);
+		free(m_pFilteringTable);
+		return IPACM_FAILURE;
+	}
+
+	if (mhip)
+	{
+		/* generate eq */
+		memset(&flt_eq, 0, sizeof(flt_eq));
+		memcpy(&flt_eq.attrib, &flt_rule_entry.rule.attrib, sizeof(flt_eq.attrib));
+		flt_eq.ip = IPA_IP_v4;
+
+
+		if(0 != ioctl(fd, IPA_IOC_GENERATE_FLT_EQ, &flt_eq)) //define and cpy attribute to this struct
 		{
-			IPACMERR("Error Adding Filtering rule, aborting...\n");
+			IPACMERR("Failed to get eq_attrib\n");
 			res = IPACM_FAILURE;
 			goto fail;
 		}
-		else if (m_pFilteringTable->rules[0].status)
+		memcpy(&flt_rule_entry.rule.eq_attrib,
+			&flt_eq.eq_attrib,
+			sizeof(flt_rule_entry.rule.eq_attrib));
+		memcpy(&(m_pFilteringTable->rules[0]), &flt_rule_entry, sizeof(struct ipa_flt_rule_add));
+
+		/* add rule */
+		if(false == m_filtering.AddOffloadFilteringRule(m_pFilteringTable, IPACM_Iface::ipacmcfg->GetQmapId(), 1))
 		{
-			IPACMERR("adding flt rule failed status=0x%x\n", m_pFilteringTable->rules[0].status);
+			IPACMERR("Failed to install WAN DL filtering table.\n");
 			res = IPACM_FAILURE;
 			goto fail;
 		}
-
-		IPACM_Iface::ipacmcfg->increaseFltRuleCount(rx_prop->rx[0].src_pipe, IPA_IP_v4, 1);
-		IPACMDBG("soft-routing flt rule hdl0=0x%x\n", m_pFilteringTable->rules[0].flt_rule_hdl);
-		/* copy filter hdls */
-		software_routing_fl_rule_hdl[0] = m_pFilteringTable->rules[0].flt_rule_hdl;
-
-
-		/* handle v6*/
-		m_pFilteringTable->ip = IPA_IP_v6;
-		if (false == m_filtering.AddFilteringRule(m_pFilteringTable))
-		{
-			IPACMERR("Error Adding Filtering rule, aborting...\n");
-			res = IPACM_FAILURE;
-			goto fail;
-		}
-		else if (m_pFilteringTable->rules[0].status)
-		{
-			IPACMDBG("adding flt rule failed status=0x%x\n", m_pFilteringTable->rules[0].status);
-			res = IPACM_FAILURE;
-			goto fail;
-		}
-
-		IPACM_Iface::ipacmcfg->increaseFltRuleCount(rx_prop->rx[0].src_pipe, IPA_IP_v6, 1);
-		IPACMDBG("soft-routing flt rule hdl0=0x%x\n", m_pFilteringTable->rules[0].flt_rule_hdl);
-		/* copy filter hdls */
-		software_routing_fl_rule_hdl[1] = m_pFilteringTable->rules[0].flt_rule_hdl;
-		softwarerouting_act = true;
-#if 0
 	}
 	else
 	{
-		if (ip_type == IPA_IP_v4)
-		{
-			m_pFilteringTable->ip = IPA_IP_v4;
-		}
-		else
-		{
-			m_pFilteringTable->ip = IPA_IP_v6;
-		}
-
+		m_pFilteringTable->ep = rx_prop->rx[0].src_pipe;
 		if (false == m_filtering.AddFilteringRule(m_pFilteringTable))
 		{
 			IPACMERR("Error Adding Filtering rule, aborting...\n");
@@ -217,32 +197,79 @@
 			res = IPACM_FAILURE;
 			goto fail;
 		}
-
-		IPACM_Iface::ipacmcfg->increaseFltRuleCount(rx_prop->rx[0].src_pipe, ip_type, 1);
-		IPACMDBG("soft-routing flt rule hdl0=0x%x\n", m_pFilteringTable->rules[0].flt_rule_hdl);
-		/* copy filter hdls */
-		if (ip_type == IPA_IP_v4)
-		{
-			software_routing_fl_rule_hdl[0] = m_pFilteringTable->rules[0].flt_rule_hdl;
-		}
-		else
-		{
-			software_routing_fl_rule_hdl[1] = m_pFilteringTable->rules[0].flt_rule_hdl;
-		}
-		softwarerouting_act = true;
+		IPACM_Iface::ipacmcfg->increaseFltRuleCount(rx_prop->rx[0].src_pipe, IPA_IP_v4, 1);
 	}
-#endif
+
+	IPACMDBG("soft-routing ipv4 flt rule hdl0=0x%x\n", m_pFilteringTable->rules[0].flt_rule_hdl);
+		/* copy filter hdls */
+		software_routing_fl_rule_hdl[0] = m_pFilteringTable->rules[0].flt_rule_hdl;
+
+		/* handle v6*/
+		m_pFilteringTable->ip = IPA_IP_v6;
+	if (mhip)
+	{
+		/* generate eq */
+		memset(&flt_eq, 0, sizeof(flt_eq));
+		memcpy(&flt_eq.attrib, &flt_rule_entry.rule.attrib, sizeof(flt_eq.attrib));
+		flt_eq.ip = IPA_IP_v6;
+
+		if(0 != ioctl(fd, IPA_IOC_GENERATE_FLT_EQ, &flt_eq)) //define and cpy attribute to this struct
+		{
+			IPACMERR("Failed to get eq_attrib\n");
+			res = IPACM_FAILURE;
+			goto fail;
+		}
+		memcpy(&flt_rule_entry.rule.eq_attrib,
+			&flt_eq.eq_attrib,
+			sizeof(flt_rule_entry.rule.eq_attrib));
+		memcpy(&(m_pFilteringTable->rules[0]), &flt_rule_entry, sizeof(struct ipa_flt_rule_add));
+
+		/* add rule */
+		if(false == m_filtering.AddOffloadFilteringRule(m_pFilteringTable, IPACM_Iface::ipacmcfg->GetQmapId(), 1))
+		{
+			IPACMERR("Failed to install WAN DL filtering table.\n");
+			res = IPACM_FAILURE;
+			goto fail;
+		}
+	}
+	else
+	{
+		m_pFilteringTable->ep = rx_prop->rx[0].src_pipe;
+		if (false == m_filtering.AddFilteringRule(m_pFilteringTable))
+		{
+			IPACMERR("Error Adding Filtering rule, aborting...\n");
+			res = IPACM_FAILURE;
+			goto fail;
+		}
+		else if (m_pFilteringTable->rules[0].status)
+		{
+			IPACMERR("adding flt rule failed status=0x%x\n", m_pFilteringTable->rules[0].status);
+			res = IPACM_FAILURE;
+			goto fail;
+		}
+		IPACM_Iface::ipacmcfg->increaseFltRuleCount(rx_prop->rx[0].src_pipe, IPA_IP_v6, 1);
+	}
+
+	IPACMDBG("soft-routing ipv6 flt rule hdl0=0x%x\n", m_pFilteringTable->rules[0].flt_rule_hdl);
+		/* copy filter hdls */
+			software_routing_fl_rule_hdl[1] = m_pFilteringTable->rules[0].flt_rule_hdl;
+		softwarerouting_act = true;
 
 fail:
+	close(fd);
+	if(m_pFilteringTable != NULL)
+	{
 	free(m_pFilteringTable);
-
+	}
 	return res;
 }
 
 /* software routing disable */
-int IPACM_Iface::handle_software_routing_disable(void)
+int IPACM_Iface::handle_software_routing_disable(bool mhip)
 {
-	int res = IPACM_SUCCESS;
+	int len, res = IPACM_SUCCESS;
+	ipa_ioc_del_flt_rule *pFilteringTable = NULL;
+	struct ipa_flt_rule_del flt_rule_entry;
 
 	if (rx_prop == NULL)
 	{
@@ -256,8 +283,43 @@
 		return IPACM_SUCCESS;
 	}
 
-//	if (ip_type == IPA_IP_MAX)
-//	{
+	if(mhip)
+	{
+		len = sizeof(struct ipa_ioc_del_flt_rule) + sizeof(struct ipa_flt_rule_del);
+		pFilteringTable = (struct ipa_ioc_del_flt_rule*)malloc(len);
+		if (pFilteringTable == NULL)
+		{
+			IPACMERR("Error Locate ipa_ioc_del_flt_rule memory...\n");
+			return IPACM_FAILURE;
+		}
+		memset(pFilteringTable, 0, len);
+
+		pFilteringTable->commit = 1;
+		pFilteringTable->ip = IPA_IP_v4;
+		pFilteringTable->num_hdls = (uint8_t)1;
+
+		if (software_routing_fl_rule_hdl[0] == 0)
+		{
+			IPACMERR("invalid ipv4_exception_hdl.\n");
+			res = IPACM_FAILURE;
+			goto fail;
+		}
+
+		memset(&flt_rule_entry, 0, sizeof(struct ipa_flt_rule_del));
+		flt_rule_entry.hdl = software_routing_fl_rule_hdl[0];
+
+		memcpy(&(pFilteringTable->hdl[0]), &flt_rule_entry, sizeof(struct ipa_flt_rule_del));
+
+		if(false == m_filtering.DelOffloadFilteringRule(pFilteringTable))
+		{
+			IPACMERR("Failed to delete MHI ipv4 exception rule.\n");
+			res = IPACM_FAILURE;
+			goto fail;
+		}
+		software_routing_fl_rule_hdl[0] = 0;
+	}
+	else
+	{
 		/* ipv4 case */
 		if (m_filtering.DeleteFilteringHdls(&software_routing_fl_rule_hdl[0],
 																				IPA_IP_v4, 1) == false)
@@ -266,8 +328,34 @@
 			res = IPACM_FAILURE;
 			goto fail;
 		}
-		IPACM_Iface::ipacmcfg->decreaseFltRuleCount(rx_prop->rx[0].src_pipe, IPA_IP_v4, 1);
+		}
+	IPACM_Iface::ipacmcfg->decreaseFltRuleCount(rx_prop->rx[0].src_pipe, IPA_IP_v4, 1);
 
+	if(mhip)
+	{
+		pFilteringTable->ip = IPA_IP_v6;
+		if (software_routing_fl_rule_hdl[1] == 0)
+		{
+			IPACMERR("invalid ipv6_exception_hdl.\n");
+			res = IPACM_FAILURE;
+			goto fail;
+		}
+
+		memset(&flt_rule_entry, 0, sizeof(struct ipa_flt_rule_del));
+		flt_rule_entry.hdl = software_routing_fl_rule_hdl[1];
+
+		memcpy(&(pFilteringTable->hdl[0]), &flt_rule_entry, sizeof(struct ipa_flt_rule_del));
+
+		if(false == m_filtering.DelOffloadFilteringRule(pFilteringTable))
+		{
+			IPACMERR("Failed to delete MHI ipv6 exception rule.\n");
+			res = IPACM_FAILURE;
+			goto fail;
+		}
+		software_routing_fl_rule_hdl[1] = 0;
+		}
+		else
+		{
 		/* ipv6 case */
 		if (m_filtering.DeleteFilteringHdls(&software_routing_fl_rule_hdl[1],
 																				IPA_IP_v6, 1) == false)
@@ -276,43 +364,15 @@
 			res = IPACM_FAILURE;
 			goto fail;
 		}
-		IPACM_Iface::ipacmcfg->decreaseFltRuleCount(rx_prop->rx[0].src_pipe, IPA_IP_v6, 1);
-		softwarerouting_act = false;
-#if 0
 	}
-	else
-	{
-		if (ip_type == IPA_IP_v4)
-		{
-			ip = IPA_IP_v4;
-		}
-		else
-		{
-			ip = IPA_IP_v6;
-		}
-
-
-		if (ip_type == IPA_IP_v4)
-		{
-			flt_hdl = software_routing_fl_rule_hdl[0];
-		}
-		else
-		{
-			flt_hdl = software_routing_fl_rule_hdl[1];
-		}
-
-		if (m_filtering.DeleteFilteringHdls(&flt_hdl, ip, 1) == false)
-		{
-			IPACMERR("Error Adding Filtering rule, aborting...\n");
-			res = IPACM_FAILURE;
-			goto fail;
-		}
-		IPACM_Iface::ipacmcfg->decreaseFltRuleCount(rx_prop->rx[0].src_pipe, ip, 1);
+	IPACM_Iface::ipacmcfg->decreaseFltRuleCount(rx_prop->rx[0].src_pipe, IPA_IP_v6, 1);
 		softwarerouting_act = false;
-	}
-#endif
 
 fail:
+	if(pFilteringTable != NULL)
+	{
+		free(pFilteringTable);
+	}
 	return res;
 }
 
@@ -639,6 +699,7 @@
 	int res = IPACM_SUCCESS, len = 0;
 	struct ipa_flt_rule_add flt_rule_entry;
 	ipa_ioc_add_flt_rule *m_pFilteringTable;
+	bool result;
 
   /* Adding this hack because WLAN may not registered for Rx-endpoint, other ifaces will always have*/
 	const char *dev_wlan0="wlan0";
@@ -745,7 +806,25 @@
 #endif
 		memcpy(&(m_pFilteringTable->rules[2]), &flt_rule_entry, sizeof(struct ipa_flt_rule_add));
 
-		if (false == m_filtering.AddFilteringRule(m_pFilteringTable))
+#ifdef IPA_IOCTL_SET_FNR_COUNTER_INFO
+		/* use index hw-counter */
+		if(ipa_if_cate == WAN_IF && IPACM_Iface::ipacmcfg->hw_fnr_stats_support)
+		{
+			IPACMDBG_H("hw-index-enable %d, counter %d\n", IPACM_Iface::ipacmcfg->hw_fnr_stats_support, IPACM_Iface::ipacmcfg->hw_counter_offset + DL_ALL);
+			result = m_filtering.AddFilteringRule_hw_index(m_pFilteringTable, IPACM_Iface::ipacmcfg->hw_counter_offset + DL_ALL);
+		} else if(ipa_if_cate == WLAN_IF && IPACM_Iface::ipacmcfg->hw_fnr_stats_support)
+		{
+			IPACMDBG_H("hw-index-enable %d, counter %d\n", IPACM_Iface::ipacmcfg->hw_fnr_stats_support, IPACM_Iface::ipacmcfg->hw_counter_offset + UL_ALL);
+			result = m_filtering.AddFilteringRule_hw_index(m_pFilteringTable, IPACM_Iface::ipacmcfg->hw_counter_offset + UL_ALL);
+		} else
+		{
+			result = m_filtering.AddFilteringRule(m_pFilteringTable);
+		}
+#else
+		result = m_filtering.AddFilteringRule(m_pFilteringTable);
+#endif
+
+		if (result == false)
 		{
 			IPACMERR("Error Adding Filtering rule, aborting...\n");
 			res = IPACM_FAILURE;
@@ -927,7 +1006,20 @@
 		memcpy(&(m_pFilteringTable->rules[7]), &flt_rule_entry, sizeof(struct ipa_flt_rule_add));
 
 #endif
-		if (m_filtering.AddFilteringRule(m_pFilteringTable) == false)
+#ifdef IPA_IOCTL_SET_FNR_COUNTER_INFO
+		/* use index hw-counter */
+		if(ipa_if_cate == WAN_IF && IPACM_Iface::ipacmcfg->hw_fnr_stats_support)
+		{
+			IPACMDBG_H("hw-index-enable %d, counter %d\n", IPACM_Iface::ipacmcfg->hw_fnr_stats_support, IPACM_Iface::ipacmcfg->hw_counter_offset + DL_ALL);
+			result = m_filtering.AddFilteringRule_hw_index(m_pFilteringTable, IPACM_Iface::ipacmcfg->hw_counter_offset+ DL_ALL);
+		} else {
+			result = m_filtering.AddFilteringRule(m_pFilteringTable);
+		}
+#else
+		result = m_filtering.AddFilteringRule(m_pFilteringTable);
+#endif
+
+		if (result == false)
 		{
 			IPACMERR("Error Adding Filtering rule, aborting...\n");
 			res = IPACM_FAILURE;
diff --git a/ipacm/src/IPACM_Lan.cpp b/ipacm/src/IPACM_Lan.cpp
index 9dfac2e..fff1783 100644
--- a/ipacm/src/IPACM_Lan.cpp
+++ b/ipacm/src/IPACM_Lan.cpp
@@ -511,7 +511,7 @@
 					{
 						/* handle software routing enable event*/
 						IPACMDBG_H("IPA_SW_ROUTING_ENABLE for iface: %s \n",IPACM_Iface::ipacmcfg->iface_table[ipa_if_num].iface_name);
-						handle_software_routing_enable();
+						handle_software_routing_enable(false);
 					}
 
 				}
@@ -1066,13 +1066,13 @@
 	case IPA_SW_ROUTING_ENABLE:
 		IPACMDBG_H("Received IPA_SW_ROUTING_ENABLE\n");
 		/* handle software routing enable event*/
-		handle_software_routing_enable();
+		handle_software_routing_enable(false);
 		break;
 
 	case IPA_SW_ROUTING_DISABLE:
 		IPACMDBG_H("Received IPA_SW_ROUTING_DISABLE\n");
 		/* handle software routing disable event*/
-		handle_software_routing_disable();
+		handle_software_routing_disable(false);
 		break;
 
 	case IPA_CRADLE_WAN_MODE_SWITCH:
@@ -1522,6 +1522,7 @@
 {
 	struct ipa_flt_rule_add flt_rule_entry;
 	int i;
+	bool result;
 
 	ipa_ioc_add_flt_rule *m_pFilteringTable;
 
@@ -1585,7 +1586,20 @@
 			IPACMDBG_H("Loop %d  5\n", i);
 		}
 
-		if (false == m_filtering.AddFilteringRule(m_pFilteringTable))
+#ifdef IPA_IOCTL_SET_FNR_COUNTER_INFO
+		/* use index hw-counter */
+		if(ipa_if_cate == WLAN_IF && IPACM_Iface::ipacmcfg->hw_fnr_stats_support)
+		{
+			IPACMDBG_H("hw-index-enable %d, counter %d\n", IPACM_Iface::ipacmcfg->hw_fnr_stats_support, IPACM_Iface::ipacmcfg->hw_counter_offset + UL_ALL);
+			result = m_filtering.AddFilteringRule_hw_index(m_pFilteringTable, IPACM_Iface::ipacmcfg->hw_counter_offset + UL_ALL);
+		} else {
+			result = m_filtering.AddFilteringRule(m_pFilteringTable);
+		}
+#else
+		result = m_filtering.AddFilteringRule(m_pFilteringTable);
+#endif
+
+		if (result == false)
 		{
 			IPACMERR("Error Adding RuleTable(0) to Filtering, aborting...\n");
 			free(m_pFilteringTable);
@@ -1614,6 +1628,7 @@
 	struct ipa_flt_rule_add flt_rule_entry;
 	int len = 0;
 	ipa_ioc_add_flt_rule *m_pFilteringTable;
+	bool result;
 
 	IPACMDBG_H("set WAN interface as default filter rule\n");
 
@@ -1688,7 +1703,20 @@
 		flt_rule_entry.rule.attrib.u.v4.src_addr = prefix[IPA_IP_v4].v4Addr;
 #endif
 		memcpy(&m_pFilteringTable->rules[0], &flt_rule_entry, sizeof(flt_rule_entry));
-		if (false == m_filtering.AddFilteringRule(m_pFilteringTable))
+
+#ifdef IPA_IOCTL_SET_FNR_COUNTER_INFO
+		/* use index hw-counter */
+		if(ipa_if_cate == WLAN_IF && IPACM_Iface::ipacmcfg->hw_fnr_stats_support)
+		{
+			IPACMDBG_H("hw-index-enable %d, counter %d\n", IPACM_Iface::ipacmcfg->hw_fnr_stats_support, IPACM_Iface::ipacmcfg->hw_counter_offset + UL_ALL);
+			result = m_filtering.AddFilteringRule_hw_index(m_pFilteringTable, IPACM_Iface::ipacmcfg->hw_counter_offset + UL_ALL);
+		} else {
+			result = m_filtering.AddFilteringRule(m_pFilteringTable);
+		}
+#else
+		result = m_filtering.AddFilteringRule(m_pFilteringTable);
+#endif
+		if (result == false)
 		{
 			IPACMERR("Error Adding RuleTable(0) to Filtering, aborting...\n");
 			free(m_pFilteringTable);
@@ -1777,7 +1805,21 @@
 
 #endif
 		memcpy(&(m_pFilteringTable->rules[0]), &flt_rule_entry, sizeof(struct ipa_flt_rule_add));
-		if (false == m_filtering.AddFilteringRule(m_pFilteringTable))
+
+#ifdef IPA_IOCTL_SET_FNR_COUNTER_INFO
+		/* use index hw-counter */
+		if(ipa_if_cate == WLAN_IF && IPACM_Iface::ipacmcfg->hw_fnr_stats_support)
+		{
+			IPACMDBG_H("hw-index-enable %d, counter %d\n", IPACM_Iface::ipacmcfg->hw_fnr_stats_support, IPACM_Iface::ipacmcfg->hw_counter_offset + UL_ALL);
+			result = m_filtering.AddFilteringRule_hw_index(m_pFilteringTable, IPACM_Iface::ipacmcfg->hw_counter_offset + UL_ALL);
+		} else {
+			result = m_filtering.AddFilteringRule(m_pFilteringTable);
+		}
+#else
+		result = m_filtering.AddFilteringRule(m_pFilteringTable);
+#endif
+
+		if (result == false)
 		{
 			IPACMERR("Error Adding Filtering rule, aborting...\n");
 			free(m_pFilteringTable);
@@ -3181,7 +3223,7 @@
 	/* check software routing fl rule hdl */
 	if (softwarerouting_act == true && rx_prop != NULL)
 	{
-		handle_software_routing_disable();
+		handle_software_routing_disable(false);
 	}
 
 	if (odu_route_rule_v4_hdl != NULL)
@@ -3238,6 +3280,7 @@
 	int i, index, eq_index;
 	uint32_t value = 0;
 	uint8_t qmap_id;
+	bool result;
 
 	IPACMDBG_H("Set modem UL flt rules\n");
 
@@ -3522,7 +3565,20 @@
 		goto fail;
 	}
 
-	if(false == m_filtering.AddFilteringRule(pFilteringTable))
+#ifdef IPA_IOCTL_SET_FNR_COUNTER_INFO
+	/* use index hw-counter */
+	if(ipa_if_cate == WLAN_IF && IPACM_Iface::ipacmcfg->hw_fnr_stats_support)
+	{
+		IPACMDBG_H("hw-index-enable %d, counter %d\n", IPACM_Iface::ipacmcfg->hw_fnr_stats_support, IPACM_Iface::ipacmcfg->hw_counter_offset + UL_ALL);
+		result = m_filtering.AddFilteringRule_hw_index(pFilteringTable, IPACM_Iface::ipacmcfg->hw_counter_offset + UL_ALL);
+	} else {
+		result = m_filtering.AddFilteringRule(pFilteringTable);
+	}
+#else
+	result = m_filtering.AddFilteringRule(pFilteringTable);
+#endif
+
+	if(result == false)
 	{
 		IPACMERR("Error Adding RuleTable to Filtering, aborting...\n");
 		ret = IPACM_FAILURE;
@@ -3818,6 +3874,7 @@
 	int len;
 	struct ipa_ioc_add_flt_rule* flt_rule;
 	struct ipa_flt_rule_add flt_rule_entry;
+	bool result;
 
 	if(rx_prop != NULL)
 	{
@@ -3854,7 +3911,20 @@
 		flt_rule_entry.rule.attrib.u.v4.protocol = (uint8_t)IPACM_FIREWALL_IPPROTO_ICMP;
 		memcpy(&(flt_rule->rules[0]), &flt_rule_entry, sizeof(struct ipa_flt_rule_add));
 
-		if (m_filtering.AddFilteringRule(flt_rule) == false)
+#ifdef IPA_IOCTL_SET_FNR_COUNTER_INFO
+		/* use index hw-counter */
+		if(ipa_if_cate == WLAN_IF && IPACM_Iface::ipacmcfg->hw_fnr_stats_support)
+		{
+			IPACMDBG_H("hw-index-enable %d, counter %d\n", IPACM_Iface::ipacmcfg->hw_fnr_stats_support, IPACM_Iface::ipacmcfg->hw_counter_offset + UL_ALL);
+			result = m_filtering.AddFilteringRule_hw_index(flt_rule, IPACM_Iface::ipacmcfg->hw_counter_offset + UL_ALL);
+		} else {
+			result = m_filtering.AddFilteringRule(flt_rule);
+		}
+#else
+		result = m_filtering.AddFilteringRule(flt_rule);
+#endif
+
+		if (result == false)
 		{
 			IPACMERR("Error Adding Filtering rule, aborting...\n");
 			free(flt_rule);
@@ -3877,6 +3947,7 @@
 	int len;
 	struct ipa_ioc_add_flt_rule* flt_rule;
 	struct ipa_flt_rule_add flt_rule_entry;
+	bool result;
 
 	if(rx_prop != NULL)
 	{
@@ -3912,7 +3983,20 @@
 		flt_rule_entry.rule.attrib.u.v6.next_hdr = (uint8_t)IPACM_FIREWALL_IPPROTO_ICMP6;
 		memcpy(&(flt_rule->rules[0]), &flt_rule_entry, sizeof(struct ipa_flt_rule_add));
 
-		if (m_filtering.AddFilteringRule(flt_rule) == false)
+#ifdef IPA_IOCTL_SET_FNR_COUNTER_INFO
+		/* use index hw-counter */
+		if(ipa_if_cate == WLAN_IF && IPACM_Iface::ipacmcfg->hw_fnr_stats_support)
+		{
+			IPACMDBG_H("hw-index-enable %d, counter %d\n", IPACM_Iface::ipacmcfg->hw_fnr_stats_support, IPACM_Iface::ipacmcfg->hw_counter_offset + UL_ALL);
+			result = m_filtering.AddFilteringRule_hw_index(flt_rule, IPACM_Iface::ipacmcfg->hw_counter_offset + UL_ALL);
+		} else {
+			result = m_filtering.AddFilteringRule(flt_rule);
+		}
+#else
+		result = m_filtering.AddFilteringRule(flt_rule);
+#endif
+
+		if (result == false)
 		{
 			IPACMERR("Error Adding Filtering rule, aborting...\n");
 			free(flt_rule);
@@ -3945,6 +4029,7 @@
 	int i, len, res = IPACM_SUCCESS;
 	struct ipa_flt_rule_add flt_rule;
 	ipa_ioc_add_flt_rule* pFilteringTable;
+	bool result;
 
 	len = sizeof(struct ipa_ioc_add_flt_rule) +	IPA_MAX_PRIVATE_SUBNET_ENTRIES * sizeof(struct ipa_flt_rule_add);
 
@@ -3988,7 +4073,20 @@
 			memcpy(&(pFilteringTable->rules[i]), &flt_rule, sizeof(struct ipa_flt_rule_add));
 		}
 
-		if (false == m_filtering.AddFilteringRule(pFilteringTable))
+#ifdef IPA_IOCTL_SET_FNR_COUNTER_INFO
+		/* use index hw-counter */
+		if(ipa_if_cate == WLAN_IF && IPACM_Iface::ipacmcfg->hw_fnr_stats_support)
+		{
+			IPACMDBG_H("hw-index-enable %d, counter %d\n", IPACM_Iface::ipacmcfg->hw_fnr_stats_support, IPACM_Iface::ipacmcfg->hw_counter_offset + UL_ALL);
+			result = m_filtering.AddFilteringRule_hw_index(pFilteringTable, IPACM_Iface::ipacmcfg->hw_counter_offset + UL_ALL);
+		} else {
+			result = m_filtering.AddFilteringRule(pFilteringTable);
+		}
+#else
+		result = m_filtering.AddFilteringRule(pFilteringTable);
+#endif
+
+		if (result == false)
 		{
 			IPACMERR("Error adding dummy private subnet v4 flt rule\n");
 			res = IPACM_FAILURE;
@@ -4113,6 +4211,7 @@
 	int len;
 	struct ipa_ioc_add_flt_rule* flt_rule;
 	struct ipa_flt_rule_add flt_rule_entry;
+	bool result;
 
 	if(rx_prop != NULL)
 	{
@@ -4155,7 +4254,20 @@
 		flt_rule_entry.rule.attrib.u.v6.dst_addr_mask[3] = 0x0;
 		memcpy(&(flt_rule->rules[0]), &flt_rule_entry, sizeof(struct ipa_flt_rule_add));
 
-		if (m_filtering.AddFilteringRule(flt_rule) == false)
+#ifdef IPA_IOCTL_SET_FNR_COUNTER_INFO
+		/* use index hw-counter */
+		if(ipa_if_cate == WLAN_IF && IPACM_Iface::ipacmcfg->hw_fnr_stats_support)
+		{
+			IPACMDBG_H("hw-index-enable %d, counter %d\n", IPACM_Iface::ipacmcfg->hw_fnr_stats_support, IPACM_Iface::ipacmcfg->hw_counter_offset + UL_ALL);
+			result = m_filtering.AddFilteringRule_hw_index(flt_rule, IPACM_Iface::ipacmcfg->hw_counter_offset + UL_ALL);
+		} else {
+			result = m_filtering.AddFilteringRule(flt_rule);
+		}
+#else
+		result = m_filtering.AddFilteringRule(flt_rule);
+#endif
+
+		if (result == false)
 		{
 			IPACMERR("Error Adding Filtering rule, aborting...\n");
 			free(flt_rule);
@@ -4998,6 +5110,7 @@
 	int len;
 	struct ipa_flt_rule_add flt_rule_entry;
 	struct ipa_ioc_add_flt_rule_after *pFilteringTable = NULL;
+	bool result;
 
 	if (rx_prop == NULL || tx_prop == NULL)
 	{
@@ -5046,7 +5159,19 @@
 	memset(flt_rule_entry.rule.attrib.dst_mac_addr_mask, 0xFF, sizeof(flt_rule_entry.rule.attrib.dst_mac_addr_mask));
 
 	memcpy(&(pFilteringTable->rules[0]), &flt_rule_entry, sizeof(flt_rule_entry));
-	if (false == m_filtering.AddFilteringRuleAfter(pFilteringTable))
+#ifdef IPA_IOCTL_SET_FNR_COUNTER_INFO
+	/* use index hw-counter */
+	if(ipa_if_cate == WLAN_IF && IPACM_Iface::ipacmcfg->hw_fnr_stats_support)
+	{
+		IPACMDBG_H("hw-index-enable %d, counter %d\n", IPACM_Iface::ipacmcfg->hw_fnr_stats_support, IPACM_Iface::ipacmcfg->hw_counter_offset + UL_ALL);
+		result = m_filtering.AddFilteringRuleAfter_hw_index(pFilteringTable, IPACM_Iface::ipacmcfg->hw_counter_offset + UL_ALL);
+	} else {
+		result = m_filtering.AddFilteringRuleAfter(pFilteringTable);
+	}
+#else
+	result = m_filtering.AddFilteringRuleAfter(pFilteringTable);
+#endif
+	if (result == false)
 	{
 		IPACMERR("Failed to add client filtering rules.\n");
 		res = IPACM_FAILURE;
@@ -5924,6 +6049,7 @@
 	int len;
 	struct ipa_flt_rule_add flt_rule_entry;
 	ipa_ioc_add_flt_rule *m_pFilteringTable;
+	bool result;
 
 	if(rx_prop == NULL)
 	{
@@ -5968,8 +6094,20 @@
 	}
 
 	memcpy(&(m_pFilteringTable->rules[0]), &flt_rule_entry, sizeof(flt_rule_entry));
+#ifdef IPA_IOCTL_SET_FNR_COUNTER_INFO
+	/* use index hw-counter */
+	if(ipa_if_cate == WLAN_IF && IPACM_Iface::ipacmcfg->hw_fnr_stats_support)
+	{
+		IPACMDBG_H("hw-index-enable %d, counter %d\n", IPACM_Iface::ipacmcfg->hw_fnr_stats_support, IPACM_Iface::ipacmcfg->hw_counter_offset + UL_ALL);
+		result = m_filtering.AddFilteringRule_hw_index(m_pFilteringTable, IPACM_Iface::ipacmcfg->hw_counter_offset + UL_ALL);
+	} else {
+		result = m_filtering.AddFilteringRule(m_pFilteringTable);
+	}
+#else
+	result = m_filtering.AddFilteringRule(m_pFilteringTable);
+#endif
 
-	if(false == m_filtering.AddFilteringRule(m_pFilteringTable))
+	if(result == false)
 	{
 		IPACMERR("Error Adding RuleTable(0) to Filtering, aborting...\n");
 		free(m_pFilteringTable);
@@ -6029,7 +6167,7 @@
 	}
 
 	memcpy(&(m_pFilteringTable->rules[0]), &flt_rule_entry, sizeof(flt_rule_entry));
-
+	/* no need for hw counters */
 	if(false == m_filtering.AddFilteringRule(m_pFilteringTable))
 	{
 		IPACMERR("Error Adding RuleTable(0) to Filtering, aborting...\n");
diff --git a/ipacm/src/IPACM_Main.cpp b/ipacm/src/IPACM_Main.cpp
index eef15d9..afdf26d 100644
--- a/ipacm/src/IPACM_Main.cpp
+++ b/ipacm/src/IPACM_Main.cpp
@@ -113,6 +113,12 @@
 int ipa_query_wlan_client();
 #endif
 
+
+/* support ipa-hw-index-counters */
+#ifdef IPA_IOCTL_SET_FNR_COUNTER_INFO
+int ipa_reset_hw_index_counter();
+#endif
+
 #ifdef FEATURE_IPACM_HAL
 	IPACM_OffloadManager* OffloadMng;
 	HAL *hal;
@@ -932,6 +938,11 @@
 	ipa_reset();
 #endif
 
+#ifdef IPA_IOCTL_SET_FNR_COUNTER_INFO
+	IPACMDBG_H("Configure IPA-HW index-counter\n");
+	ipa_reset_hw_index_counter();
+#endif
+
 	neigh = new IPACM_Neighbor();
 	ifacemgr = new IPACM_IfaceManager();
 #ifdef FEATURE_IPACM_HAL
@@ -1160,3 +1171,51 @@
 	return IPACM_SUCCESS;
 }
 #endif
+
+#ifdef IPA_IOCTL_SET_FNR_COUNTER_INFO
+int ipa_reset_hw_index_counter()
+{
+	int fd = -1;
+	struct ipa_ioc_flt_rt_counter_alloc fnr_counters;
+	struct ipa_ioc_fnr_index_info fnr_info;
+
+	if ((fd = open(IPA_DEVICE_NAME, O_RDWR)) < 0) {
+		IPACMERR("Failed opening %s.\n", IPA_DEVICE_NAME);
+		return IPACM_FAILURE;
+	}
+
+	memset(&fnr_counters, 0, sizeof(fnr_counters));
+	fnr_counters.hw_counter.num_counters = 4;
+	fnr_counters.hw_counter.allow_less = false;
+	fnr_counters.sw_counter.num_counters = 4;
+	fnr_counters.sw_counter.allow_less = false;
+	IPACMDBG_H("Allocating %d hw counters and %d sw counters\n",
+		fnr_counters.hw_counter.num_counters, fnr_counters.sw_counter.num_counters);
+
+	if (ioctl(fd, IPA_IOC_FNR_COUNTER_ALLOC, &fnr_counters) < 0) {
+		IPACMERR("IPA_IOC_FNR_COUNTER_ALLOC call failed: %s \n", strerror(errno));
+		close(fd);
+		return IPACM_FAILURE;
+	}
+
+	IPACMDBG_H("hw-counter start offset %d, sw-counter start offset %d\n",
+		fnr_counters.hw_counter.start_id, fnr_counters.sw_counter.start_id);
+	IPACM_Iface::ipacmcfg->hw_fnr_stats_support = true;
+	IPACM_Iface::ipacmcfg->hw_counter_offset = fnr_counters.hw_counter.start_id;
+	IPACM_Iface::ipacmcfg->sw_counter_offset = fnr_counters.sw_counter.start_id;
+
+	/* set FNR counter info */
+	memset(&fnr_info, 0, sizeof(fnr_info));
+	fnr_info.hw_counter_offset = fnr_counters.hw_counter.start_id;
+	fnr_info.sw_counter_offset = fnr_counters.sw_counter.start_id;
+
+	if (ioctl(fd, IPA_IOC_SET_FNR_COUNTER_INFO, &fnr_info) < 0) {
+		IPACMERR("IPA_IOC_SET_FNR_COUNTER_INFO call failed: %s \n", strerror(errno));
+		close(fd);
+		return IPACM_FAILURE;
+	}
+
+	close(fd);
+	return IPACM_SUCCESS;
+}
+#endif
diff --git a/ipacm/src/IPACM_Routing.cpp b/ipacm/src/IPACM_Routing.cpp
index 2a2555a..654a0f9 100644
--- a/ipacm/src/IPACM_Routing.cpp
+++ b/ipacm/src/IPACM_Routing.cpp
@@ -112,6 +112,115 @@
 	return true;
 }
 
+#ifdef IPA_IOCTL_SET_FNR_COUNTER_INFO
+bool IPACM_Routing::AddRoutingRule_hw_index(struct ipa_ioc_add_rt_rule *ruleTable, int hw_counter_index)
+{
+	int retval = 0, cnt = 0, len = 0;
+	struct ipa_ioc_add_rt_rule_v2 *ruleTable_v2;
+	struct ipa_rt_rule_add_v2 rt_rule_entry;
+	bool ret = true;
+
+	IPACMDBG("Printing routing add attributes\n");
+	IPACMDBG("ip type: %d\n", ruleTable->ip);
+	IPACMDBG("rt tbl type: %s\n", ruleTable->rt_tbl_name);
+	IPACMDBG("Number of rules: %d\n", ruleTable->num_rules);
+	IPACMDBG("commit value: %d\n", ruleTable->commit);
+
+	/* change to v2 format*/
+	len = sizeof(struct ipa_ioc_add_rt_rule_v2);
+	ruleTable_v2 = (struct ipa_ioc_add_rt_rule_v2*)malloc(len);
+	if (ruleTable_v2 == NULL)
+	{
+		IPACMERR("Error Locate ipa_ioc_add_rt_rule_v2 memory...\n");
+		return false;
+	}
+	memset(ruleTable_v2, 0, len);
+	ruleTable_v2->rules = (uint64_t)calloc(ruleTable->num_rules, sizeof(struct ipa_rt_rule_add_v2));
+	if (!ruleTable_v2->rules) {
+		IPACMERR("Failed to allocate memory for routing rules\n");
+		ret = false;
+		goto fail_tbl;
+	}
+
+	ruleTable_v2->commit = ruleTable->commit;
+	ruleTable_v2->ip = ruleTable->ip;
+	ruleTable_v2->num_rules = ruleTable->num_rules;
+	ruleTable_v2->rule_add_size = sizeof(struct ipa_rt_rule_add_v2);
+	memcpy(ruleTable_v2->rt_tbl_name,
+		 ruleTable->rt_tbl_name,
+		 sizeof(ruleTable_v2->rt_tbl_name));
+
+	for (cnt=0; cnt < ruleTable->num_rules; cnt++)
+	{
+		memset(&rt_rule_entry, 0, sizeof(struct ipa_rt_rule_add_v2));
+		rt_rule_entry.at_rear = ruleTable->rules[cnt].at_rear;
+		rt_rule_entry.rule.dst = ruleTable->rules[cnt].rule.dst;
+		rt_rule_entry.rule.hdr_hdl = ruleTable->rules[cnt].rule.hdr_hdl;
+		rt_rule_entry.rule.hdr_proc_ctx_hdl = ruleTable->rules[cnt].rule.hdr_proc_ctx_hdl;
+		rt_rule_entry.rule.max_prio = ruleTable->rules[cnt].rule.max_prio;
+		rt_rule_entry.rule.hashable = ruleTable->rules[cnt].rule.hashable;
+		rt_rule_entry.rule.retain_hdr = ruleTable->rules[cnt].rule.retain_hdr;
+		rt_rule_entry.rule.coalesce = ruleTable->rules[cnt].rule.coalesce;
+		memcpy(&rt_rule_entry.rule.attrib,
+					 &ruleTable->rules[cnt].rule.attrib,
+					 sizeof(rt_rule_entry.rule.attrib));
+		IPACMDBG("RT rule:%d attrib mask: 0x%x\n", cnt,
+				ruleTable->rules[cnt].rule.attrib.attrib_mask);
+		/* 0 means disable hw-counter-sats */
+		if (hw_counter_index != 0)
+		{
+			rt_rule_entry.rule.enable_stats = 1;
+			rt_rule_entry.rule.cnt_idx = hw_counter_index;
+		}
+
+		/* copy to v2 table*/
+		memcpy((void *)(ruleTable_v2->rules + (cnt * sizeof(struct ipa_rt_rule_add_v2))),
+			&rt_rule_entry, sizeof(rt_rule_entry));
+	}
+
+	retval = ioctl(m_fd, IPA_IOC_ADD_RT_RULE_V2, ruleTable_v2);
+	if (retval != 0)
+	{
+		IPACMERR("Failed adding Routing rule %pK\n", ruleTable_v2);
+		PERROR("unable to add routing rule:");
+
+		for (int cnt = 0; cnt < ruleTable_v2->num_rules; cnt++)
+		{
+			if (((struct ipa_rt_rule_add_v2 *)ruleTable_v2->rules)[cnt].status != 0)
+			{
+				IPACMERR("Adding Routing rule:%d failed with status:%d\n",
+								 cnt, ((struct ipa_rt_rule_add_v2 *)ruleTable_v2->rules)[cnt].status);
+			}
+		}
+		ret = false;
+		goto fail_rule;
+	}
+
+	/* copy results from v2 to v1 format */
+	for (int cnt = 0; cnt < ruleTable->num_rules; cnt++)
+	{
+		/* copy status to v1 format */
+		ruleTable->rules[cnt].status = ((struct ipa_rt_rule_add_v2 *)ruleTable_v2->rules)[cnt].status;
+		ruleTable->rules[cnt].rt_rule_hdl = ((struct ipa_rt_rule_add_v2 *)ruleTable_v2->rules)[cnt].rt_rule_hdl;
+
+		if(((struct ipa_rt_rule_add_v2 *)ruleTable_v2->rules)[cnt].status != 0)
+		{
+			IPACMERR("Adding Routing rule:%d failed with status:%d\n",
+							 cnt, ((struct ipa_rt_rule_add_v2 *) ruleTable_v2->rules)[cnt].status);
+		}
+	}
+	IPACMDBG("Added Routing rule %pK\n", ruleTable_v2);
+fail_rule:
+	if((void *)ruleTable_v2->rules != NULL)
+		free((void *)ruleTable_v2->rules);
+fail_tbl:
+	if (ruleTable_v2 != NULL)
+		free(ruleTable_v2);
+	return ret;
+}
+#endif //IPA_IOCTL_SET_FNR_COUNTER_INFO
+
+
 bool IPACM_Routing::DeleteRoutingRule(struct ipa_ioc_del_rt_rule *ruleTable)
 {
 	int retval = 0;
diff --git a/ipacm/src/IPACM_Wan.cpp b/ipacm/src/IPACM_Wan.cpp
index 498f3b6..464a537 100644
--- a/ipacm/src/IPACM_Wan.cpp
+++ b/ipacm/src/IPACM_Wan.cpp
@@ -227,6 +227,7 @@
 	struct ipa_ioc_add_flt_rule *flt_rule;
 	struct ipa_flt_rule_add flt_rule_entry;
 	struct ipa_ioc_get_hdr hdr;
+	bool result;
 
 	const int NUM_RULES = 1;
 	uint32_t num_ipv6_addr;
@@ -498,8 +499,20 @@
 				flt_rule_entry.rule.attrib.u.v6.dst_addr_mask[2] = 0xFFFFFFFF;
 				flt_rule_entry.rule.attrib.u.v6.dst_addr_mask[3] = 0xFFFFFFFF;
 				memcpy(&(flt_rule->rules[0]), &flt_rule_entry, sizeof(struct ipa_flt_rule_add));
+#ifdef IPA_IOCTL_SET_FNR_COUNTER_INFO
+				/* use index hw-counter */
+				if(IPACM_Iface::ipacmcfg->hw_fnr_stats_support)
+				{
+					IPACMDBG_H("hw-index-enable %d, counter %d\n", IPACM_Iface::ipacmcfg->hw_fnr_stats_support, IPACM_Iface::ipacmcfg->hw_counter_offset + DL_ALL);
+					result = m_filtering.AddFilteringRule_hw_index(flt_rule, IPACM_Iface::ipacmcfg->hw_counter_offset+ DL_ALL);
+				} else {
+					result = m_filtering.AddFilteringRule(flt_rule);
+				}
+#else
+				result = m_filtering.AddFilteringRule(flt_rule);
+#endif
 
-				if (m_filtering.AddFilteringRule(flt_rule) == false)
+				if (result == false)
 				{
 					IPACMERR("Error Adding Filtering rule, aborting...\n");
 					free(flt_rule);
@@ -1166,7 +1179,15 @@
 					{
 						/* handle software routing enable event*/
 						IPACMDBG_H("IPA_SW_ROUTING_ENABLE for iface: %s \n",IPACM_Iface::ipacmcfg->iface_table[ipa_if_num].iface_name);
-						handle_software_routing_enable();
+
+						if(m_is_sta_mode == Q6_MHI_WAN)
+						{
+							handle_software_routing_enable(true);
+						}
+						else
+						{
+							handle_software_routing_enable(false);
+						}
 					}
 
 				}
@@ -1619,9 +1640,13 @@
 		{
 			install_wan_filtering_rule(true);
 		}
+		else if(m_is_sta_mode == Q6_MHI_WAN)
+		{
+			handle_software_routing_enable(true);
+		}
 		else
 		{
-			handle_software_routing_enable();
+			handle_software_routing_enable(false);
 		}
 		break;
 
@@ -1634,9 +1659,13 @@
 			install_wan_filtering_rule(false);
 			softwarerouting_act = false;
 		}
+		else if(m_is_sta_mode == Q6_MHI_WAN)
+		{
+			handle_software_routing_disable(true);
+		}
 		else
 		{
-			handle_software_routing_disable();
+			handle_software_routing_disable(false);
 		}
 		break;
 
@@ -1759,6 +1788,7 @@
 	const int NUM = 1;
 	ipacm_cmd_q_data evt_data;
 	struct ipa_ioc_get_hdr hdr;
+	bool result;
 #ifdef	WAN_IOC_NOTIFY_WAN_STATE //resolve compile issue on 4.9 kernel
 	struct wan_ioctl_notify_wan_state wan_state;
 	int fd_wwan_ioctl;
@@ -1840,6 +1870,13 @@
 				IPACMERR("Failed to send ICMPv6 ex rule to modem.\n");
 				return IPACM_FAILURE;
 			}
+
+			/* send ipv4 TCP FIN filtering rule */
+			if(iptype==IPA_IP_v4 && add_tcp_fin_rst_exception_rule())
+			{
+				IPACMERR("Failed to send TCP FIN RST rule to modem.\n");
+				return IPACM_FAILURE;
+			}
 		}
 		else
 		{
@@ -1878,8 +1915,6 @@
 	rt_rule->num_rules = (uint8_t)NUM;
 	rt_rule->ip = iptype;
 
-
-	IPACMDBG_H(" WAN table created %s \n", rt_rule->rt_tbl_name);
 	rt_rule_entry = &rt_rule->rules[0];
 	rt_rule_entry->at_rear = true;
 
@@ -1907,6 +1942,7 @@
 				rt_rule_entry->rule.hdr_hdl = hdr_hdl_sta_v6;
 			}
 
+			IPACMDBG_H(" WAN table created %s \n", rt_rule->rt_tbl_name);
 			/* replace the hdr handle for q6_PCIE*/
 			if(m_is_sta_mode == Q6_MHI_WAN)
 			{
@@ -1945,10 +1981,21 @@
 				rt_rule_entry->rule.attrib.u.v4.dst_addr      = 0;
 				rt_rule_entry->rule.attrib.u.v4.dst_addr_mask = 0;
 #ifdef FEATURE_IPA_V3
-
 				rt_rule_entry->rule.hashable = true;
 #endif
-				if (false == m_routing.AddRoutingRule(rt_rule))
+#ifdef IPA_IOCTL_SET_FNR_COUNTER_INFO
+				/* use index hw-counter */
+				if((m_is_sta_mode == WLAN_WAN) && IPACM_Iface::ipacmcfg->hw_fnr_stats_support)
+				{
+					IPACMDBG_H("hw-index-enable %d, counter %d\n", IPACM_Iface::ipacmcfg->hw_fnr_stats_support, IPACM_Iface::ipacmcfg->hw_counter_offset + UL_HW);
+					result = m_routing.AddRoutingRule_hw_index(rt_rule, IPACM_Iface::ipacmcfg->hw_counter_offset + UL_HW);
+				} else {
+					result = m_routing.AddRoutingRule(rt_rule);			
+				}
+#else
+				result = m_routing.AddRoutingRule(rt_rule);
+#endif
+				if (result == false)
 				{
 		    		IPACMERR("Routing rule addition failed!\n");
 		    		free(rt_rule);
@@ -1973,7 +2020,19 @@
 #ifdef FEATURE_IPA_V3
 				rt_rule_entry->rule.hashable = true;
 #endif
-				if (false == m_routing.AddRoutingRule(rt_rule))
+#ifdef IPA_IOCTL_SET_FNR_COUNTER_INFO
+				/* use index hw-counter */
+				if((m_is_sta_mode == WLAN_WAN) && IPACM_Iface::ipacmcfg->hw_fnr_stats_support)
+				{
+					IPACMDBG_H("hw-index-enable %d, counter %d\n", IPACM_Iface::ipacmcfg->hw_fnr_stats_support, IPACM_Iface::ipacmcfg->hw_counter_offset + UL_HW);
+					result = m_routing.AddRoutingRule_hw_index(rt_rule, IPACM_Iface::ipacmcfg->hw_counter_offset + UL_HW);
+				} else {
+					result = m_routing.AddRoutingRule(rt_rule);
+				}
+#else
+				result = m_routing.AddRoutingRule(rt_rule);
+#endif
+				if (result == false)
 				{
 		    		IPACMERR("Routing rule addition failed!\n");
 		    		free(rt_rule);
@@ -1993,6 +2052,7 @@
 	if (iptype == IPA_IP_v6 && m_is_sta_mode != Q6_MHI_WAN)
 	{
 		strlcpy(rt_rule->rt_tbl_name, IPACM_Iface::ipacmcfg->rt_tbl_wan_v6.name, sizeof(rt_rule->rt_tbl_name));
+		IPACMDBG_H(" WAN table created %s \n", rt_rule->rt_tbl_name);
 		memset(rt_rule_entry, 0, sizeof(struct ipa_rt_rule_add));
 		rt_rule_entry->at_rear = true;
 		if(m_is_sta_mode == Q6_WAN)
@@ -2454,6 +2514,7 @@
 {
 	struct ipa_flt_rule_add flt_rule_entry;
 	int i, rule_v4 = 0, rule_v6 = 0, len;
+	bool result;
 
 	IPACMDBG_H("ip-family: %d; \n", iptype);
 
@@ -2528,7 +2589,19 @@
 		memcpy(&flt_rule_entry.rule.attrib, &rx_prop->rx[0].attrib, sizeof(struct ipa_rule_attrib));
 		flt_rule_entry.rule.attrib.attrib_mask |= IPA_FLT_FRAGMENT;
 		memcpy(&(m_pFilteringTable->rules[0]), &flt_rule_entry, sizeof(struct ipa_flt_rule_add));
-		if (false == m_filtering.AddFilteringRule(m_pFilteringTable))
+#ifdef IPA_IOCTL_SET_FNR_COUNTER_INFO
+		/* use index hw-counter */
+		if(IPACM_Iface::ipacmcfg->hw_fnr_stats_support)
+		{
+			IPACMDBG_H("hw-index-enable %d, counter %d\n", IPACM_Iface::ipacmcfg->hw_fnr_stats_support, IPACM_Iface::ipacmcfg->hw_counter_offset + DL_ALL);
+			result = m_filtering.AddFilteringRule_hw_index(m_pFilteringTable, IPACM_Iface::ipacmcfg->hw_counter_offset+ DL_ALL);
+		} else {
+			result = m_filtering.AddFilteringRule(m_pFilteringTable);
+		}
+#else
+		result = m_filtering.AddFilteringRule(m_pFilteringTable);
+#endif
+		if (false == result)
 		{
 			IPACMERR("Error Adding RuleTable(0) to Filtering, aborting...\n");
 			free(m_pFilteringTable);
@@ -2621,8 +2694,19 @@
 			}
 
 			memcpy(&(m_pFilteringTable->rules[0]), &flt_rule_entry, sizeof(struct ipa_flt_rule_add));
-
-			if (false == m_filtering.AddFilteringRule(m_pFilteringTable))
+#ifdef IPA_IOCTL_SET_FNR_COUNTER_INFO
+			/* use index hw-counter */
+			if(IPACM_Iface::ipacmcfg->hw_fnr_stats_support)
+			{
+				IPACMDBG_H("hw-index-enable %d, counter %d\n", IPACM_Iface::ipacmcfg->hw_fnr_stats_support, IPACM_Iface::ipacmcfg->hw_counter_offset + DL_ALL);
+				result = m_filtering.AddFilteringRule_hw_index(m_pFilteringTable, IPACM_Iface::ipacmcfg->hw_counter_offset+ DL_ALL);
+			} else {
+				result = m_filtering.AddFilteringRule(m_pFilteringTable);
+			}
+#else
+			result = m_filtering.AddFilteringRule(m_pFilteringTable);
+#endif
+			if (false == result)
 			{
 				IPACMERR("Error Adding RuleTable(0) to Filtering, aborting...\n");
 				free(m_pFilteringTable);
@@ -2709,7 +2793,20 @@
 
 						IPACMDBG_H("Filter rule attrib mask: 0x%x\n",
 										 m_pFilteringTable->rules[0].rule.attrib.attrib_mask);
-						if (false == m_filtering.AddFilteringRule(m_pFilteringTable))
+#ifdef IPA_IOCTL_SET_FNR_COUNTER_INFO
+						/* use index hw-counter */
+						if(IPACM_Iface::ipacmcfg->hw_fnr_stats_support)
+						{
+							IPACMDBG_H("hw-index-enable %d, counter %d\n", IPACM_Iface::ipacmcfg->hw_fnr_stats_support, IPACM_Iface::ipacmcfg->hw_counter_offset + DL_ALL);
+							result = m_filtering.AddFilteringRule_hw_index(m_pFilteringTable, IPACM_Iface::ipacmcfg->hw_counter_offset+ DL_ALL);
+						} else {
+							result = m_filtering.AddFilteringRule(m_pFilteringTable);
+						}
+#else
+						result = m_filtering.AddFilteringRule(m_pFilteringTable);
+#endif
+
+						if (false == result)
 						{
 							IPACMERR("Error Adding RuleTable(0) to Filtering, aborting...\n");
 							free(m_pFilteringTable);
@@ -2733,7 +2830,20 @@
 
 						IPACMDBG_H("Filter rule attrib mask: 0x%x\n",
 										 m_pFilteringTable->rules[0].rule.attrib.attrib_mask);
-						if (false == m_filtering.AddFilteringRule(m_pFilteringTable))
+#ifdef IPA_IOCTL_SET_FNR_COUNTER_INFO
+						/* use index hw-counter */
+						if(IPACM_Iface::ipacmcfg->hw_fnr_stats_support)
+						{
+							IPACMDBG_H("hw-index-enable %d, counter %d\n", IPACM_Iface::ipacmcfg->hw_fnr_stats_support, IPACM_Iface::ipacmcfg->hw_counter_offset + DL_ALL);
+							result = m_filtering.AddFilteringRule_hw_index(m_pFilteringTable, IPACM_Iface::ipacmcfg->hw_counter_offset+ DL_ALL);
+						} else {
+							result = m_filtering.AddFilteringRule(m_pFilteringTable);
+						}
+#else
+						result = m_filtering.AddFilteringRule(m_pFilteringTable);
+#endif
+
+						if (false == result)
 						{
 							IPACMERR("Error Adding RuleTable(0) to Filtering, aborting...\n");
 							free(m_pFilteringTable);
@@ -2757,7 +2867,20 @@
 
 						IPACMDBG_H("Filter rule attrib mask: 0x%x\n",
 										 m_pFilteringTable->rules[0].rule.attrib.attrib_mask);
-						if (false == m_filtering.AddFilteringRule(m_pFilteringTable))
+#ifdef IPA_IOCTL_SET_FNR_COUNTER_INFO
+						/* use index hw-counter */
+						if(IPACM_Iface::ipacmcfg->hw_fnr_stats_support)
+						{
+							IPACMDBG_H("hw-index-enable %d, counter %d\n", IPACM_Iface::ipacmcfg->hw_fnr_stats_support, IPACM_Iface::ipacmcfg->hw_counter_offset + DL_ALL);
+							result = m_filtering.AddFilteringRule_hw_index(m_pFilteringTable, IPACM_Iface::ipacmcfg->hw_counter_offset+ DL_ALL);
+						} else {
+							result = m_filtering.AddFilteringRule(m_pFilteringTable);
+						}
+#else
+						result = m_filtering.AddFilteringRule(m_pFilteringTable);
+#endif
+
+						if (false == result)
 						{
 							IPACMERR("Error Adding RuleTable(0) to Filtering, aborting...\n");
 							free(m_pFilteringTable);
@@ -2833,7 +2956,20 @@
 
 			IPACMDBG_H("Filter rule attrib mask: 0x%x\n",
 							 m_pFilteringTable->rules[0].rule.attrib.attrib_mask);
-			if (false == m_filtering.AddFilteringRule(m_pFilteringTable))
+#ifdef IPA_IOCTL_SET_FNR_COUNTER_INFO
+			/* use index hw-counter */
+			if(IPACM_Iface::ipacmcfg->hw_fnr_stats_support)
+			{
+				IPACMDBG_H("hw-index-enable %d, counter %d\n", IPACM_Iface::ipacmcfg->hw_fnr_stats_support, IPACM_Iface::ipacmcfg->hw_counter_offset + DL_ALL);
+				result = m_filtering.AddFilteringRule_hw_index(m_pFilteringTable, IPACM_Iface::ipacmcfg->hw_counter_offset+ DL_ALL);
+			} else {
+				result = m_filtering.AddFilteringRule(m_pFilteringTable);
+			}
+#else
+			result = m_filtering.AddFilteringRule(m_pFilteringTable);
+#endif
+
+			if (false == result)
 			{
 				IPACMERR("Error Adding RuleTable(0) to Filtering, aborting...\n");
 				free(m_pFilteringTable);
@@ -2881,7 +3017,20 @@
 				flt_rule_entry.rule.attrib.attrib_mask |= IPA_FLT_NEXT_HDR;
 				flt_rule_entry.rule.attrib.u.v6.next_hdr = (uint8_t)IPACM_FIREWALL_IPPROTO_ICMP6;
 				memcpy(&(m_pFilteringTable->rules[0]), &flt_rule_entry, sizeof(struct ipa_flt_rule_add));
-				if (false == m_filtering.AddFilteringRule(m_pFilteringTable))
+#ifdef IPA_IOCTL_SET_FNR_COUNTER_INFO
+			/* use index hw-counter */
+			if(IPACM_Iface::ipacmcfg->hw_fnr_stats_support)
+			{
+				IPACMDBG_H("hw-index-enable %d, counter %d\n", IPACM_Iface::ipacmcfg->hw_fnr_stats_support, IPACM_Iface::ipacmcfg->hw_counter_offset + DL_ALL);
+				result = m_filtering.AddFilteringRule_hw_index(m_pFilteringTable, IPACM_Iface::ipacmcfg->hw_counter_offset+ DL_ALL);
+			} else {
+				result = m_filtering.AddFilteringRule(m_pFilteringTable);
+			}
+#else
+			result = m_filtering.AddFilteringRule(m_pFilteringTable);
+#endif
+
+			if (false == result)
 				{
 					IPACMERR("Error Adding Filtering rules, aborting...\n");
 					free(m_pFilteringTable);
@@ -2955,8 +3104,20 @@
 			}
 
 			memcpy(&(m_pFilteringTable->rules[0]), &flt_rule_entry, sizeof(struct ipa_flt_rule_add));
+#ifdef IPA_IOCTL_SET_FNR_COUNTER_INFO
+			/* use index hw-counter */
+			if(IPACM_Iface::ipacmcfg->hw_fnr_stats_support)
+			{
+				IPACMDBG_H("hw-index-enable %d, counter %d\n", IPACM_Iface::ipacmcfg->hw_fnr_stats_support, IPACM_Iface::ipacmcfg->hw_counter_offset + DL_ALL);
+				result = m_filtering.AddFilteringRule_hw_index(m_pFilteringTable, IPACM_Iface::ipacmcfg->hw_counter_offset+ DL_ALL);
+			} else {
+				result = m_filtering.AddFilteringRule(m_pFilteringTable);
+			}
+#else
+			result = m_filtering.AddFilteringRule(m_pFilteringTable);
+#endif
 
-			if (false == m_filtering.AddFilteringRule(m_pFilteringTable))
+			if (false == result)
 			{
 				IPACMERR("Error Adding Filtering rules, aborting...\n");
 				free(m_pFilteringTable);
@@ -3027,7 +3188,20 @@
 						/* insert TCP rule*/
 						flt_rule_entry.rule.attrib.u.v6.next_hdr = IPACM_FIREWALL_IPPROTO_TCP;
 						memcpy(&(m_pFilteringTable->rules[0]), &flt_rule_entry, sizeof(struct ipa_flt_rule_add));
-						if (false == m_filtering.AddFilteringRule(m_pFilteringTable))
+#ifdef IPA_IOCTL_SET_FNR_COUNTER_INFO
+						/* use index hw-counter */
+						if(IPACM_Iface::ipacmcfg->hw_fnr_stats_support)
+						{
+							IPACMDBG_H("hw-index-enable %d, counter %d\n", IPACM_Iface::ipacmcfg->hw_fnr_stats_support, IPACM_Iface::ipacmcfg->hw_counter_offset + DL_ALL);
+							result = m_filtering.AddFilteringRule_hw_index(m_pFilteringTable, IPACM_Iface::ipacmcfg->hw_counter_offset+ DL_ALL);
+						} else {
+							result = m_filtering.AddFilteringRule(m_pFilteringTable);
+						}
+#else
+						result = m_filtering.AddFilteringRule(m_pFilteringTable);
+#endif
+
+						if (false == result)
 						{
 							IPACMERR("Error Adding Filtering rules, aborting...\n");
 							free(m_pFilteringTable);
@@ -3046,7 +3220,19 @@
 						/* insert UDP rule*/
 						flt_rule_entry.rule.attrib.u.v6.next_hdr = IPACM_FIREWALL_IPPROTO_UDP;
 						memcpy(&(m_pFilteringTable->rules[0]), &flt_rule_entry, sizeof(struct ipa_flt_rule_add));
-						if (false == m_filtering.AddFilteringRule(m_pFilteringTable))
+#ifdef IPA_IOCTL_SET_FNR_COUNTER_INFO
+						/* use index hw-counter */
+						if(IPACM_Iface::ipacmcfg->hw_fnr_stats_support)
+						{
+							IPACMDBG_H("hw-index-enable %d, counter %d\n", IPACM_Iface::ipacmcfg->hw_fnr_stats_support, IPACM_Iface::ipacmcfg->hw_counter_offset + DL_ALL);
+							result = m_filtering.AddFilteringRule_hw_index(m_pFilteringTable, IPACM_Iface::ipacmcfg->hw_counter_offset+ DL_ALL);
+						} else {
+							result = m_filtering.AddFilteringRule(m_pFilteringTable);
+						}
+#else
+						result = m_filtering.AddFilteringRule(m_pFilteringTable);
+#endif
+						if (false == result)
 						{
 							IPACMERR("Error Adding Filtering rules, aborting...\n");
 							free(m_pFilteringTable);
@@ -3065,7 +3251,20 @@
 					else
 					{
 						memcpy(&(m_pFilteringTable->rules[0]), &flt_rule_entry, sizeof(struct ipa_flt_rule_add));
-						if (false == m_filtering.AddFilteringRule(m_pFilteringTable))
+
+#ifdef IPA_IOCTL_SET_FNR_COUNTER_INFO
+						/* use index hw-counter */
+						if(IPACM_Iface::ipacmcfg->hw_fnr_stats_support)
+						{
+							IPACMDBG_H("hw-index-enable %d, counter %d\n", IPACM_Iface::ipacmcfg->hw_fnr_stats_support, IPACM_Iface::ipacmcfg->hw_counter_offset + DL_ALL);
+							result = m_filtering.AddFilteringRule_hw_index(m_pFilteringTable, IPACM_Iface::ipacmcfg->hw_counter_offset+ DL_ALL);
+						} else {
+							result = m_filtering.AddFilteringRule(m_pFilteringTable);
+						}
+#else
+						result = m_filtering.AddFilteringRule(m_pFilteringTable);
+#endif
+						if (false == result)
 						{
 							IPACMERR("Error Adding Filtering rules, aborting...\n");
 							free(m_pFilteringTable);
@@ -3103,7 +3302,19 @@
 			flt_rule_entry.rule.attrib.u.v6.next_hdr = (uint8_t)IPACM_FIREWALL_IPPROTO_ICMP6;
 			memcpy(&(m_pFilteringTable->rules[0]), &flt_rule_entry, sizeof(struct ipa_flt_rule_add));
 
-			if (false == m_filtering.AddFilteringRule(m_pFilteringTable))
+#ifdef IPA_IOCTL_SET_FNR_COUNTER_INFO
+			/* use index hw-counter */
+			if(IPACM_Iface::ipacmcfg->hw_fnr_stats_support)
+			{
+				IPACMDBG_H("hw-index-enable %d, counter %d\n", IPACM_Iface::ipacmcfg->hw_fnr_stats_support, IPACM_Iface::ipacmcfg->hw_counter_offset + DL_ALL);
+				result = m_filtering.AddFilteringRule_hw_index(m_pFilteringTable, IPACM_Iface::ipacmcfg->hw_counter_offset+ DL_ALL);
+			} else {
+				result = m_filtering.AddFilteringRule(m_pFilteringTable);
+			}
+#else
+			result = m_filtering.AddFilteringRule(m_pFilteringTable);
+#endif
+			if (result == false)
 			{
 				IPACMERR("Error Adding Filtering rules, aborting...\n");
 				free(m_pFilteringTable);
@@ -3163,7 +3374,20 @@
 
 			memcpy(&(m_pFilteringTable->rules[0]), &flt_rule_entry, sizeof(struct ipa_flt_rule_add));
 
-			if (false == m_filtering.AddFilteringRule(m_pFilteringTable))
+#ifdef IPA_IOCTL_SET_FNR_COUNTER_INFO
+			/* use index hw-counter */
+			if(IPACM_Iface::ipacmcfg->hw_fnr_stats_support)
+			{
+				IPACMDBG_H("hw-index-enable %d, counter %d\n", IPACM_Iface::ipacmcfg->hw_fnr_stats_support, IPACM_Iface::ipacmcfg->hw_counter_offset + DL_ALL);
+				result = m_filtering.AddFilteringRule_hw_index(m_pFilteringTable, IPACM_Iface::ipacmcfg->hw_counter_offset+ DL_ALL);
+			} else {
+				result = m_filtering.AddFilteringRule(m_pFilteringTable);
+			}
+#else
+			result = m_filtering.AddFilteringRule(m_pFilteringTable);
+#endif
+
+			if (result == false)
 			{
 				IPACMERR("Error Adding Filtering rules, aborting...\n");
 				free(m_pFilteringTable);
@@ -4698,6 +4922,12 @@
 				IPACMERR("Failed to delete icmpv6 rule \n");
 				return IPACM_FAILURE;
 			}
+			/* Delete tcp_fin_rst rule */
+			if(delete_tcp_fin_rst_exception_rule())
+			{
+				IPACMERR("Failed to delete tcp_fin_rst rule \n");
+				return IPACM_FAILURE;
+			}
 		}
 		else
 		{
@@ -5180,7 +5410,14 @@
 		/* check software routing fl rule hdl */
 		if (softwarerouting_act == true)
 		{
-			handle_software_routing_disable();
+			if(m_is_sta_mode == Q6_MHI_WAN)
+			{
+				handle_software_routing_disable(true);
+			}
+			else
+			{
+				handle_software_routing_disable(false);
+			}
 		}
 		/* free dft ipv4 filter rule handlers if any */
 		if (ip_type != IPA_IP_v6 && rx_prop != NULL)
@@ -5569,11 +5806,11 @@
 		}
 	}
 
-	/* check software routing fl rule hdl */
-	if (softwarerouting_act == true)
-	{
-		handle_software_routing_disable();
-	}
+//	/* check software routing fl rule hdl */
+//	if (softwarerouting_act == true)
+//	{
+//		handle_software_routing_disable();
+//	}
 
 fail:
 	if (tx_prop != NULL)
@@ -7405,7 +7642,7 @@
 	flt_rule_entry.rule.retain_hdr = 1;
 	flt_rule_entry.rule.to_uc = 0;
 	flt_rule_entry.rule.eq_attrib_type = 0;
-	flt_rule_entry.at_rear = true;
+	flt_rule_entry.at_rear = false;
 	flt_rule_entry.flt_rule_hdl = -1;
 	flt_rule_entry.status = -1;
 	flt_rule_entry.rule.action = IPA_PASS_TO_EXCEPTION;
@@ -7509,3 +7746,170 @@
 	}
 	return res;
 }
+
+int IPACM_Wan::add_tcp_fin_rst_exception_rule()
+{
+	int fd;
+	int len, res = IPACM_SUCCESS;
+	uint8_t mux_id;
+	ipa_ioc_add_flt_rule *pFilteringTable = NULL;
+
+	mux_id = ext_prop->ext[0].mux_id;
+	/* contruct filter rules to pcie modem */
+	struct ipa_flt_rule_add flt_rule_entry;
+	ipa_ioc_generate_flt_eq flt_eq;
+
+	/* construct rule */
+	IPACMDBG_H("adding MHI TCP FIN RST rule\n");
+	len = sizeof(struct ipa_ioc_add_flt_rule) + (2 * sizeof(struct ipa_flt_rule_add));
+	pFilteringTable = (struct ipa_ioc_add_flt_rule*)malloc(len);
+	if (pFilteringTable == NULL)
+	{
+		IPACMERR("Error Locate ipa_flt_rule_add memory...\n");
+		return IPACM_FAILURE;
+	}
+	memset(pFilteringTable, 0, len);
+
+	pFilteringTable->commit = 1;
+	pFilteringTable->global = false;
+	pFilteringTable->ip = IPA_IP_v4;
+	pFilteringTable->num_rules = (uint8_t)2;
+
+	/* Configuring TCP FIN RST Filtering Rule */
+	memset(&flt_rule_entry, 0, sizeof(struct ipa_flt_rule_add));
+	flt_rule_entry.rule.retain_hdr = 1;
+	flt_rule_entry.rule.to_uc = 0;
+	flt_rule_entry.rule.eq_attrib_type = 0;
+	flt_rule_entry.at_rear = false;
+	flt_rule_entry.flt_rule_hdl = -1;
+	flt_rule_entry.status = -1;
+	flt_rule_entry.rule.action = IPA_PASS_TO_EXCEPTION;
+	/*
+	 * need this since fin is last packet in an ongoing TCP connection
+	 * so it will always match the previous hash and take MHIP path
+	 */
+	flt_rule_entry.rule.hashable = false;
+
+	IPACMDBG_H("rx property attrib mask:0x%x\n", rx_prop->rx[0].attrib.attrib_mask);
+	memcpy(&flt_rule_entry.rule.attrib, &rx_prop->rx[0].attrib, sizeof(flt_rule_entry.rule.attrib));
+	flt_rule_entry.rule.attrib.attrib_mask |= IPA_FLT_PROTOCOL;
+	flt_rule_entry.rule.attrib.u.v4.protocol = (uint8_t)IPACM_FIREWALL_IPPROTO_TCP;
+
+	/* generate eq */
+	memset(&flt_eq, 0, sizeof(flt_eq));
+	memcpy(&flt_eq.attrib, &flt_rule_entry.rule.attrib, sizeof(flt_eq.attrib));
+	flt_eq.ip = IPA_IP_v4;
+
+	fd = open(IPA_DEVICE_NAME, O_RDWR);
+	if (fd < 0)
+	{
+		IPACMERR("Failed opening %s.\n", IPA_DEVICE_NAME);
+		free(pFilteringTable);
+		return IPACM_FAILURE;
+	}
+
+	if(0 != ioctl(fd, IPA_IOC_GENERATE_FLT_EQ, &flt_eq)) //define and cpy attribute to this struct
+	{
+		IPACMERR("Failed to get eq_attrib\n");
+		res = IPACM_FAILURE;
+		goto fail;
+	}
+	memcpy(&flt_rule_entry.rule.eq_attrib,
+		&flt_eq.eq_attrib,
+		sizeof(flt_rule_entry.rule.eq_attrib));
+
+	/* set the bit mask to use MEQ32_IHL offset */
+	#ifdef FEATURE_IPA_V3
+		flt_rule_entry.rule.eq_attrib.rule_eq_bitmap |= (1<<7);
+	#else
+		flt_rule_entry.rule.eq_attrib.rule_eq_bitmap |= (1<<8);
+	#endif
+
+	/* add offset to compare TCP flags */
+	flt_rule_entry.rule.eq_attrib.num_ihl_offset_meq_32 = 1;
+	flt_rule_entry.rule.eq_attrib.ihl_offset_meq_32[0].offset = 12;
+
+	/* add TCP FIN RULE */
+	flt_rule_entry.rule.eq_attrib.ihl_offset_meq_32[0].value = (((uint32_t)1)<<TCP_FIN_SHIFT);
+	flt_rule_entry.rule.eq_attrib.ihl_offset_meq_32[0].mask = (((uint32_t)1)<<TCP_FIN_SHIFT);
+	memcpy(&(pFilteringTable->rules[0]), &flt_rule_entry, sizeof(struct ipa_flt_rule_add));
+
+	/* add TCP RST rule*/
+	flt_rule_entry.rule.eq_attrib.ihl_offset_meq_32[0].value = (((uint32_t)1)<<TCP_RST_SHIFT);
+	flt_rule_entry.rule.eq_attrib.ihl_offset_meq_32[0].mask = (((uint32_t)1)<<TCP_RST_SHIFT);
+	memcpy(&(pFilteringTable->rules[1]), &flt_rule_entry, sizeof(struct ipa_flt_rule_add));
+
+	/* add rules */
+	if(false == m_filtering.AddOffloadFilteringRule(pFilteringTable, mux_id, 1))
+	{
+		IPACMERR("Failed to install WAN DL filtering table.\n");
+		res = IPACM_FAILURE;
+		goto fail;
+	}
+
+	/* save handle */
+	tcp_fin_hdl = pFilteringTable->rules[0].flt_rule_hdl;
+	tcp_rst_hdl = pFilteringTable->rules[1].flt_rule_hdl;
+
+fail:
+	close(fd);
+	if(pFilteringTable != NULL)
+	{
+		free(pFilteringTable);
+	}
+	return res;
+}
+
+int IPACM_Wan::delete_tcp_fin_rst_exception_rule()
+{
+	int len, res = IPACM_SUCCESS;
+	ipa_ioc_del_flt_rule *pFilteringTable = NULL;
+
+	struct ipa_flt_rule_del flt_rule_entry;
+
+	IPACMDBG_H("deleting MHI TCP FIN RST rule \n");
+	len = sizeof(struct ipa_ioc_del_flt_rule) + (2 * sizeof(struct ipa_flt_rule_del));
+	pFilteringTable = (struct ipa_ioc_del_flt_rule*)malloc(len);
+	if (pFilteringTable == NULL)
+	{
+		IPACMERR("Error Locate ipa_ioc_del_flt_rule memory...\n");
+		return IPACM_FAILURE;
+	}
+	memset(pFilteringTable, 0, len);
+
+	pFilteringTable->commit = 1;
+	pFilteringTable->ip = IPA_IP_v4;
+	pFilteringTable->num_hdls = (uint8_t)2;
+
+	if (tcp_fin_hdl == 0 || tcp_rst_hdl == 0)
+	{
+		IPACMERR("invalid tcp_fin_rst_hdl.\n");
+		res = IPACM_FAILURE;
+		goto fail;
+	}
+
+	memset(&flt_rule_entry, 0, sizeof(struct ipa_flt_rule_del));
+	flt_rule_entry.hdl = tcp_fin_hdl;
+
+	memcpy(&(pFilteringTable->hdl[0]), &flt_rule_entry, sizeof(struct ipa_flt_rule_del));
+
+	flt_rule_entry.hdl = tcp_rst_hdl;
+
+	memcpy(&(pFilteringTable->hdl[1]), &flt_rule_entry, sizeof(struct ipa_flt_rule_del));
+
+	if(false == m_filtering.DelOffloadFilteringRule(pFilteringTable))
+	{
+		IPACMERR("Failed to delete MHI TCP FIN RST rule.\n");
+		res = IPACM_FAILURE;
+		goto fail;
+	}
+	tcp_fin_hdl = 0;
+	tcp_rst_hdl = 0;
+
+fail:
+	if(pFilteringTable != NULL)
+	{
+		free(pFilteringTable);
+	}
+	return res;
+}
diff --git a/ipacm/src/IPACM_Wlan.cpp b/ipacm/src/IPACM_Wlan.cpp
index b7fb9ff..48fcc7e 100644
--- a/ipacm/src/IPACM_Wlan.cpp
+++ b/ipacm/src/IPACM_Wlan.cpp
@@ -350,7 +350,7 @@
 					{
 						/* handle software routing enable event*/
 						IPACMDBG_H("IPA_SW_ROUTING_ENABLE for iface: %s \n",IPACM_Iface::ipacmcfg->iface_table[ipa_if_num].iface_name);
-						handle_software_routing_enable();
+						handle_software_routing_enable(false);
 					}
 				}
 			}
@@ -860,13 +860,13 @@
 		/* handle software routing enable event, iface will update softwarerouting_act to true*/
 	case IPA_SW_ROUTING_ENABLE:
 		IPACMDBG_H("Received IPA_SW_ROUTING_ENABLE\n");
-		IPACM_Iface::handle_software_routing_enable();
+		IPACM_Iface::handle_software_routing_enable(false);
 		break;
 
 		/* handle software routing disable event, iface will update softwarerouting_act to false*/
 	case IPA_SW_ROUTING_DISABLE:
 		IPACMDBG_H("Received IPA_SW_ROUTING_DISABLE\n");
-		IPACM_Iface::handle_software_routing_disable();
+		IPACM_Iface::handle_software_routing_disable(false);
 		break;
 
 	case IPA_WLAN_SWITCH_TO_SCC:
@@ -1456,6 +1456,7 @@
 	uint32_t tx_index;
 	int wlan_index,v6_num;
 	const int NUM = 1;
+	bool result;
 
 	if(tx_prop == NULL)
 	{
@@ -1576,8 +1577,19 @@
 				{
 					rt_rule_entry->rule.hashable = true;
 				}
-
-				if (false == m_routing.AddRoutingRule(rt_rule))
+#ifdef IPA_IOCTL_SET_FNR_COUNTER_INFO
+				/* use index hw-counter */
+				if(IPACM_Iface::ipacmcfg->hw_fnr_stats_support)
+				{
+					IPACMDBG_H("hw-index-enable %d, counter %d\n", IPACM_Iface::ipacmcfg->hw_fnr_stats_support, IPACM_Iface::ipacmcfg->hw_counter_offset + DL_HW);
+					result = m_routing.AddRoutingRule_hw_index(rt_rule, IPACM_Iface::ipacmcfg->hw_counter_offset + DL_HW);
+				} else {
+					result = m_routing.AddRoutingRule(rt_rule);
+				}
+#else
+				result = m_routing.AddRoutingRule(rt_rule);
+#endif
+				if (result == false)
 				{
 					IPACMERR("Routing rule addition failed!\n");
 					free(rt_rule);
@@ -1671,7 +1683,20 @@
 #ifdef FEATURE_IPA_V3
 					rt_rule_entry->rule.hashable = true;
 #endif
-					if (false == m_routing.AddRoutingRule(rt_rule))
+#ifdef IPA_IOCTL_SET_FNR_COUNTER_INFO
+					/* use index hw-counter */
+					if(IPACM_Iface::ipacmcfg->hw_fnr_stats_support)
+					{
+						IPACMDBG_H("hw-index-enable %d, counter %d\n", IPACM_Iface::ipacmcfg->hw_fnr_stats_support, IPACM_Iface::ipacmcfg->hw_counter_offset + DL_HW);
+						result = m_routing.AddRoutingRule_hw_index(rt_rule, IPACM_Iface::ipacmcfg->hw_counter_offset + DL_HW);
+					} else {
+						result = m_routing.AddRoutingRule(rt_rule);
+					}
+#else
+					result = m_routing.AddRoutingRule(rt_rule);
+#endif
+
+					if (result == false)
 					{
 						IPACMERR("Routing rule addition failed!\n");
 						free(rt_rule);
@@ -2094,7 +2119,7 @@
 	if (softwarerouting_act == true && rx_prop != NULL )
 	{
 		IPACMDBG_H("Delete sw routing filtering rules\n");
-		IPACM_Iface::handle_software_routing_disable();
+		IPACM_Iface::handle_software_routing_disable(false);
 	}
 	IPACMDBG_H("finished delete software-routing filtering rules\n ");
 
diff --git a/ipanat/src/Android.mk b/ipanat/src/Android.mk
index af786eb..bb87e82 100644
--- a/ipanat/src/Android.mk
+++ b/ipanat/src/Android.mk
@@ -1,6 +1,15 @@
 BOARD_PLATFORM_LIST := msm8909
 BOARD_PLATFORM_LIST += msm8916
 BOARD_PLATFORM_LIST += msm8917
+TARGET_DISABLE_IPANAT := false
+
+ifeq ($(TARGET_USES_QMAA),true)
+ifneq ($(TARGET_USES_QMAA_OVERRIDE_DATA),true)
+	TARGET_DISABLE_IPANAT := true
+endif #TARGET_USES_QMAA_OVERRIDE_DATA
+endif #TARGET_USES_QMAA
+
+ifneq ($(TARGET_DISABLE_IPANAT),true)
 ifneq ($(call is-board-platform-in-list,$(BOARD_PLATFORM_LIST)),true)
 ifneq (,$(filter $(QCOM_BOARD_PLATFORMS),$(TARGET_BOARD_PLATFORM)))
 ifneq (, $(filter aarch64 arm arm64, $(TARGET_ARCH)))
@@ -34,3 +43,4 @@
 endif # $(TARGET_ARCH)
 endif
 endif
+endif