Merge Android R

Bug: 168057903
Merged-In: Icdac6de452cfb91db7fa31eb9f5633fd2372f22a
Change-Id: I96a0d7095b05fe1b3fa4d23c99df2e5bf0b069c3
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..9515b25
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,2 @@
+soong_namespace {
+}
diff --git a/hal/Android.bp b/hal/Android.bp
new file mode 100644
index 0000000..14d3745
--- /dev/null
+++ b/hal/Android.bp
@@ -0,0 +1,26 @@
+cc_library_shared {
+    name: "liboffloadhal",
+    srcs: [
+        "src/CtUpdateAmbassador.cpp",
+        "src/HAL.cpp",
+        "src/IpaEventRelay.cpp",
+        "src/LocalLogBuffer.cpp",
+        "src/OffloadStatistics.cpp",
+        "src/PrefixParser.cpp",
+    ],
+
+    shared_libs: [
+        "libhidlbase",
+        "liblog",
+        "libcutils",
+        "libdl",
+        "libbase",
+        "libutils",
+        "libhardware_legacy",
+        "libhardware",
+        "android.hardware.tetheroffload.config@1.0",
+        "android.hardware.tetheroffload.control@1.0",
+    ],
+    export_include_dirs: ["inc"],
+    vendor: true,
+}
diff --git a/hal/Android.mk b/hal/Android.mk
deleted file mode 100644
index 6ebca46..0000000
--- a/hal/Android.mk
+++ /dev/null
@@ -1,31 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_ARM_MODE := arm
-LOCAL_SRC_FILES := src/CtUpdateAmbassador.cpp \
-                src/HAL.cpp \
-                src/IpaEventRelay.cpp \
-                src/LocalLogBuffer.cpp \
-                src/OffloadStatistics.cpp \
-                src/PrefixParser.cpp
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/inc
-LOCAL_MODULE := liboffloadhal
-
-#LOCAL_CPP_FLAGS := -Wall -Werror
-LOCAL_SHARED_LIBRARIES := libhwbinder \
-                        libhidlbase \
-                        libhidltransport \
-                        liblog \
-                        libcutils \
-                        libdl \
-                        libbase \
-                        libutils \
-                        libhardware_legacy \
-                        libhardware \
-                        android.hardware.tetheroffload.config@1.0 \
-                        android.hardware.tetheroffload.control@1.0
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/inc
-LOCAL_VENDOR_MODULE := true
-LOCAL_MODULE_PATH_32 := $(TARGET_OUT_VENDOR)/lib
-LOCAL_MODULE_PATH_64 := $(TARGET_OUT_VENDOR)/lib64
-include $(BUILD_SHARED_LIBRARY)
diff --git a/ipacm/Android.bp b/ipacm/Android.bp
new file mode 100644
index 0000000..7c2a0c5
--- /dev/null
+++ b/ipacm/Android.bp
@@ -0,0 +1,72 @@
+
+cc_binary {
+    name: "ipacm",
+
+    local_include_dirs: ["src"] + ["inc"],
+    header_libs: ["device_kernel_headers"],
+    cflags: ["-DFEATURE_IPA_ANDROID"] + ["-DFEATURE_IPACM_RESTART"] + [
+        "-DFEATURE_IPACM_HAL",
+        "-Wall",
+        "-Werror",
+        "-Wno-error=macro-redefined",
+	"-Wno-enum-compare",
+	"-Wno-error=implicit-fallthrough",
+    ],
+
+    srcs: [
+        "src/IPACM_Main.cpp",
+        "src/IPACM_EvtDispatcher.cpp",
+        "src/IPACM_Config.cpp",
+        "src/IPACM_CmdQueue.cpp",
+        "src/IPACM_Filtering.cpp",
+        "src/IPACM_Routing.cpp",
+        "src/IPACM_Header.cpp",
+        "src/IPACM_Lan.cpp",
+        "src/IPACM_Iface.cpp",
+        "src/IPACM_Wlan.cpp",
+        "src/IPACM_Wan.cpp",
+        "src/IPACM_IfaceManager.cpp",
+        "src/IPACM_Neighbor.cpp",
+        "src/IPACM_Netlink.cpp",
+        "src/IPACM_Xml.cpp",
+        "src/IPACM_Conntrack_NATApp.cpp",
+        "src/IPACM_ConntrackClient.cpp",
+        "src/IPACM_ConntrackListener.cpp",
+        "src/IPACM_Log.cpp",
+        "src/IPACM_OffloadManager.cpp",
+        "src/IPACM_LanToLan.cpp",
+    ],
+
+    init_rc: ["src/ipacm.rc"],
+    clang: true,
+    vendor: true,
+
+    shared_libs: [
+	"liboffloadhal",
+	"libipanat",
+	"libxml2",
+	"libnfnetlink",
+	"libnetfilter_conntrack",
+        "libhidlbase",
+        "liblog",
+        "libcutils",
+        "libdl",
+        "libbase",
+        "libutils",
+        "libhardware_legacy",
+        "libhardware",
+        "android.hardware.tetheroffload.config@1.0",
+        "android.hardware.tetheroffload.control@1.0",
+    ],
+}
+
+//###############################################################################
+
+prebuilt_etc {
+    name: "IPACM_cfg.xml",
+
+    vendor: true,
+    owner: "ipacm",
+    src: "src/IPACM_cfg.xml",
+
+}
diff --git a/ipacm/inc/IPACM_Config.h b/ipacm/inc/IPACM_Config.h
index af1b616..1915fbc 100644
--- a/ipacm/inc/IPACM_Config.h
+++ b/ipacm/inc/IPACM_Config.h
@@ -94,7 +94,7 @@
 	ipacm_alg *alg_table;
 
 	/* Store private subnet configuration from XML file */
-	ipa_private_subnet private_subnet_table[IPA_MAX_PRIVATE_SUBNET_ENTRIES];
+	ipa_private_subnet private_subnet_table[IPA_MAX_PRIVATE_SUBNET_ENTRIES + IPA_MAX_MTU_ENTRIES];
 
 	/* Store the non nat iface names */
 	NatIfaces *pNatIfaces;
@@ -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();
 
@@ -256,6 +261,10 @@
 
 	enum ipa_hw_type GetIPAVer(bool get = false);
 
+	bool isEthBridgingSupported();
+
+	bool isIPAv3Supported();
+
 	int Init(void);
 
 	inline bool isPrivateSubnet(uint32_t ip_addr)
diff --git a/ipacm/inc/IPACM_ConntrackListener.h b/ipacm/inc/IPACM_ConntrackListener.h
index d965cf7..24a2c72 100644
--- a/ipacm/inc/IPACM_ConntrackListener.h
+++ b/ipacm/inc/IPACM_ConntrackListener.h
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
@@ -103,7 +103,7 @@
 public:
 	char wan_ifname[IPA_IFACE_NAME_LEN];
 	uint32_t wan_ipaddr;
-	bool isStaMode;
+	ipacm_wan_iface_type backhaul_mode;
 	IPACM_ConntrackListener();
 	void event_callback(ipa_cm_event_id, void *data);
 	inline bool isWanUp()
diff --git a/ipacm/inc/IPACM_Conntrack_NATApp.h b/ipacm/inc/IPACM_Conntrack_NATApp.h
index c3749e1..b362907 100644
--- a/ipacm/inc/IPACM_Conntrack_NATApp.h
+++ b/ipacm/inc/IPACM_Conntrack_NATApp.h
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
@@ -65,6 +65,8 @@
 	bool enabled;
 	uint32_t rule_hdl;
 
+	/* used for pcie-modem */
+	uint32_t rule_id;
 }nat_table_entry;
 
 #define CHK_TBL_HDL()  if(nat_table_hdl == 0){ return -1; }
@@ -80,6 +82,8 @@
 	uint32_t pub_ip_addr;
 	uint32_t pub_ip_addr_pre;
 	uint32_t nat_table_hdl;
+	/* used for pcie-modem */
+	uint8_t pub_mux_id;
 
 	int curCnt, max_entries;
 
@@ -94,7 +98,10 @@
 	struct nf_conntrack *ct;
 	struct nfct_handle *ct_hdl;
 
+	int m_fd_ipa;
+
 	NatApp();
+	~NatApp();
 	int Init();
 
 	void UpdateCTUdpTs(nat_table_entry *, uint32_t);
@@ -114,6 +121,9 @@
 	int AddEntry(const nat_table_entry *);
 	int DeleteEntry(const nat_table_entry *);
 
+	int AddConnection(const nat_table_entry *);
+	int DelConnection(const uint32_t);
+
 	void UpdateUDPTimeStamp();
 
 	int UpdatePwrSaveIf(uint32_t);
diff --git a/ipacm/inc/IPACM_Defs.h b/ipacm/inc/IPACM_Defs.h
index d2186b6..a3cbba1 100644
--- a/ipacm/inc/IPACM_Defs.h
+++ b/ipacm/inc/IPACM_Defs.h
@@ -77,6 +77,7 @@
 
 #define IPA_MAX_IFACE_ENTRIES 20
 #define IPA_MAX_PRIVATE_SUBNET_ENTRIES 3
+#define IPA_MAX_MTU_ENTRIES 3
 #define IPA_MAX_ALG_ENTRIES 20
 #define IPA_MAX_RM_ENTRY 6
 
@@ -96,16 +97,13 @@
 #define IPA_DEVICE_NAME "/dev/ipa"
 #define MAX_NUM_PROP 2
 
-#ifndef FEATURE_IPA_V3
-#define IPA_MAX_FLT_RULE 50
-#else
 #define IPA_MAX_FLT_RULE 100
-#endif
 
 #define TCP_FIN_SHIFT 16
 #define TCP_SYN_SHIFT 17
 #define TCP_RST_SHIFT 18
 #define NUM_IPV6_PREFIX_FLT_RULE 1
+#define NUM_IPV6_PREFIX_MTU_RULE 1
 
 /*---------------------------------------------------------------------------
 										Return values indicating error status
@@ -122,7 +120,9 @@
 #define IPA_MAX_NUM_ETH_CLIENTS  15
 #define IPA_MAX_NUM_AMPDU_RULE  15
 #define IPA_MAC_ADDR_SIZE  6
+#define IPA_MAX_NUM_SW_PDNS 15
 
+#define DEFAULT_MTU_SIZE 1500
 /*===========================================================================
 										 GLOBAL DEFINITIONS AND DECLARATIONS
 ===========================================================================*/
@@ -186,7 +186,8 @@
 	IPA_ETH_BRIDGE_CLIENT_ADD,                /* ipacm_event_eth_bridge */
 	IPA_ETH_BRIDGE_CLIENT_DEL,                /* ipacm_event_eth_bridge*/
 	IPA_ETH_BRIDGE_WLAN_SCC_MCC_SWITCH,       /* ipacm_event_eth_bridge*/
-	IPA_SSR_NOTICE,						      /* NULL*/
+	IPA_SSR_NOTICE,                           /* NULL*/
+	IPA_COALESCE_NOTICE,                      /* NULL*/
 #ifdef FEATURE_L2TP
 	IPA_ADD_VLAN_IFACE,                       /* ipa_ioc_vlan_iface_info */
 	IPA_DEL_VLAN_IFACE,                       /* ipa_ioc_vlan_iface_info */
@@ -352,31 +353,34 @@
 	struct ipa_wlan_hdr_attrib_val attribs[0];
 } ipacm_event_data_wlan_ex;
 
+typedef enum
+{
+	Q6_WAN = 0,
+	WLAN_WAN,
+	ECM_WAN,
+	Q6_MHI_WAN
+} ipacm_wan_iface_type;
+
 typedef struct _ipacm_event_iface_up
 {
+	ipacm_wan_iface_type backhaul_type;
 	char ifname[IPA_IFACE_NAME_LEN];
 	uint32_t ipv4_addr;
 	uint32_t addr_mask;
 	uint32_t ipv6_prefix[2];
-	bool is_sta;
 	uint8_t xlat_mux_id;
 	uint8_t mux_id;
 }ipacm_event_iface_up;
 
 typedef struct _ipacm_event_iface_up_tether
 {
+	ipacm_wan_iface_type backhaul_type;
 	uint32_t if_index_tether;
 	uint32_t ipv6_prefix[2];
 	bool is_sta;
 	uint8_t xlat_mux_id;
 }ipacm_event_iface_up_tehter;
 
-typedef enum
-{
-	Q6_WAN = 0,
-	WLAN_WAN,
-	ECM_WAN
-} ipacm_wan_iface_type;
 
 typedef struct _ipacm_ifacemgr_data
 {
diff --git a/ipacm/inc/IPACM_Filtering.h b/ipacm/inc/IPACM_Filtering.h
index 9bb8247..428c21a 100644
--- a/ipacm/inc/IPACM_Filtering.h
+++ b/ipacm/inc/IPACM_Filtering.h
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
@@ -47,6 +47,8 @@
 #include <IPACM_Defs.h>
 #include <linux/rmnet_ipa_fd_ioctl.h>
 
+#define IPA_PCIE_MODEM_RULE_ID_START 69
+
 class IPACM_Filtering
 {
 public:
@@ -54,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);
@@ -63,6 +69,8 @@
 													 uint8_t num_rules);
 
 	bool AddWanDLFilteringRule(struct ipa_ioc_add_flt_rule const *rule_table_v4, struct ipa_ioc_add_flt_rule const * rule_table_v6, uint8_t mux_id);
+	bool AddOffloadFilteringRule(struct ipa_ioc_add_flt_rule *flt_rule_tbl, uint8_t mux_id, uint8_t default_path);
+	bool DelOffloadFilteringRule(struct ipa_ioc_del_flt_rule const *flt_rule_tbl);
 	bool SendFilteringRuleIndex(struct ipa_fltr_installed_notif_req_msg_v01* table);
 	bool ModifyFilteringRule(struct ipa_ioc_mdfy_flt_rule* ruleTable);
 	ipa_filter_action_enum_v01 GetQmiFilterAction(ipa_flt_action action);
@@ -70,6 +78,8 @@
 private:
 	static const char *DEVICE_NAME;
 	int fd; /* File descriptor of the IPA device node /dev/ipa */
+	int total_num_offload_rules;
+	int pcie_modem_rule_id;
 };
 
 #endif //IPACM_FILTERING_H
diff --git a/ipacm/inc/IPACM_Iface.h b/ipacm/inc/IPACM_Iface.h
index a562613..55d9e99 100644
--- a/ipacm/inc/IPACM_Iface.h
+++ b/ipacm/inc/IPACM_Iface.h
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
+Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
@@ -139,13 +139,17 @@
 	static IPACM_Filtering m_filtering;
 	static IPACM_Header m_header;
 
+	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);
+
 private:
 
 	static const char *DEVICE_NAME;
diff --git a/ipacm/inc/IPACM_Lan.h b/ipacm/inc/IPACM_Lan.h
index 700dfbc..605edb5 100644
--- a/ipacm/inc/IPACM_Lan.h
+++ b/ipacm/inc/IPACM_Lan.h
@@ -49,6 +49,7 @@
 #include "IPACM_Filtering.h"
 #include "IPACM_Config.h"
 #include "IPACM_Conntrack_NATApp.h"
+#include "IPACM_Wan.h"
 
 #define IPA_WAN_DEFAULT_FILTER_RULE_HANDLES  1
 #define IPA_PRIV_SUBNET_FILTER_RULE_HANDLES  3
@@ -94,6 +95,8 @@
 	int ipv6_set;
 	bool ipv4_header_set;
 	bool ipv6_header_set;
+	/* used for pcie-modem */
+	uint32_t v6_rt_rule_id[IPV6_NUM_ADDR];
 	eth_client_rt_hdl eth_rt_hdl[0]; /* depends on number of tx properties */
 }ipa_eth_client;
 
@@ -110,7 +113,7 @@
 	uint32_t lan_wan_fl_rule_hdl[IPA_WAN_DEFAULT_FILTER_RULE_HANDLES];
 
 	/* store private-subnet filter rule handlers */
-	uint32_t private_fl_rule_hdl[IPA_MAX_PRIVATE_SUBNET_ENTRIES];
+	uint32_t private_fl_rule_hdl[IPA_MAX_PRIVATE_SUBNET_ENTRIES + IPA_MAX_MTU_ENTRIES];
 
 	/* LAN-iface's callback function */
 	void event_callback(ipa_cm_event_id event, void *data);
@@ -121,10 +124,10 @@
 	virtual int handle_wan_up_ex(ipacm_ext_prop* ext_prop, ipa_ip_type iptype, uint8_t xlat_mux_id);
 
 	/* delete filter rule for wan_down event*/
-	virtual int handle_wan_down(bool is_sta_mode);
+	virtual int handle_wan_down(ipacm_wan_iface_type backhaul_mode);
 
 	/* delete filter rule for wan_down event*/
-	virtual int handle_wan_down_v6(bool is_sta_mode);
+	virtual int handle_wan_down_v6(ipacm_wan_iface_type backhaul_mode);
 
 	/* configure private subnet filter rules*/
 	virtual int handle_private_subnet(ipa_ip_type iptype);
@@ -255,7 +258,7 @@
 
 	uint32_t ipv4_icmp_flt_rule_hdl[NUM_IPV4_ICMP_FLT_RULE];
 
-	uint32_t ipv6_prefix_flt_rule_hdl[NUM_IPV6_PREFIX_FLT_RULE];
+	uint32_t ipv6_prefix_flt_rule_hdl[NUM_IPV6_PREFIX_FLT_RULE + NUM_IPV6_PREFIX_MTU_RULE];
 	uint32_t ipv6_icmp_flt_rule_hdl[NUM_IPV6_ICMP_FLT_RULE];
 
 	int num_wan_ul_fl_rule_v4;
@@ -393,6 +396,18 @@
 				{
 					for(num_v6 =0;num_v6 < get_client_memptr(eth_client, clt_indx)->route_rule_set_v6;num_v6++)
 					{
+						/* send client-v6 delete to pcie modem only with global ipv6 with tx_index = 1 one time*/
+						if(is_global_ipv6_addr(get_client_memptr(eth_client, clt_indx)->v6_addr[num_v6]) && (IPACM_Wan::backhaul_mode == Q6_MHI_WAN)
+							&& (get_client_memptr(eth_client, clt_indx)->v6_rt_rule_id[num_v6] > 0))
+						{
+							IPACMDBG_H("Delete client index %d ipv6 RT-rules for %d-st ipv6 for rule-id:%d\n", clt_indx,num_v6,
+								get_client_memptr(eth_client, clt_indx)->v6_rt_rule_id[num_v6]);
+							if (del_connection(clt_indx, num_v6))
+							{
+								IPACMERR("PCIE filter rule deletion failed! (%d-client) %d v6-entry\n",clt_indx, num_v6);
+							}
+						}
+
 						IPACMDBG_H("Delete client index %d ipv6 RT-rules for %d-st ipv6 for tx:%d\n", clt_indx,num_v6,tx_index);
 						rt_hdl = get_client_memptr(eth_client, clt_indx)->eth_rt_hdl[tx_index].eth_rt_rule_hdl_v6[num_v6];
 						if(m_routing.DeleteRoutingHdl(rt_hdl, IPA_IP_v6) == false)
@@ -445,6 +460,12 @@
 
 	/*handle reset usb-client rt-rules */
 	int handle_lan_client_reset_rt(ipa_ip_type iptype);
+
+	/* for pcie modem */
+	virtual int add_connection(int client_index, int v6_num);
+	virtual int del_connection(int client_index, int v6_num);
+
+	int construct_mtu_rule(struct ipa_flt_rule *rule, enum ipa_ip_type iptype, uint16_t mtu);
 };
 
 #endif /* IPACM_LAN_H */
diff --git a/ipacm/inc/IPACM_OffloadManager.h b/ipacm/inc/IPACM_OffloadManager.h
index 88a411b..8ac904f 100644
--- a/ipacm/inc/IPACM_OffloadManager.h
+++ b/ipacm/inc/IPACM_OffloadManager.h
@@ -86,7 +86,7 @@
     virtual RET getStats(const char * /* upstream */, bool /* reset */,
 		OffloadStatistics& /* ret */);
 
-	static IPACM_OffloadManager *pInstance; //sky
+	static IPACM_OffloadManager *pInstance;
 
 	IpaEventListener *elrInstance;
 
@@ -96,6 +96,8 @@
 
 	bool push_framework_event(const char * if_name, _ipacm_offload_prefix prefix);
 
+	static int num_offload_v4_tethered_iface;
+
 private:
 
 	std::list<std::string> valid_ifaces;
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 1b917c6..31949c5 100644
--- a/ipacm/inc/IPACM_Wan.h
+++ b/ipacm/inc/IPACM_Wan.h
@@ -89,6 +89,12 @@
 	wan_client_rt_hdl wan_rt_hdl[0]; /* depends on number of tx properties */
 }ipa_wan_client;
 
+typedef struct
+{
+	bool coalesce_tcp_enable;
+	bool coalesce_udp_enable;
+}ipacm_coalesce;
+
 /* wan iface */
 class IPACM_Wan : public IPACM_Iface
 {
@@ -99,6 +105,8 @@
 	static bool wan_up;
 	static bool wan_up_v6;
 	static uint8_t xlat_mux_id;
+	static uint16_t mtu_default_wan;
+	uint16_t mtu_size;
 	/* IPACM interface name */
 	static char wan_up_dev_name[IF_NAME_LEN];
 	static uint32_t curr_wan_ip;
@@ -131,6 +139,26 @@
 #endif
 	}
 
+	static uint16_t queryMTU(int ipa_if_num_tether, enum ipa_ip_type iptype)
+	{
+		if (iptype == IPA_IP_v4)
+		{
+			if (isWanUP(ipa_if_num_tether))
+			{
+				return mtu_default_wan;
+			}
+		}
+		else if (iptype == IPA_IP_v6)
+		{
+			if (isWanUP_V6(ipa_if_num_tether))
+			{
+				return mtu_default_wan;
+
+			}
+		}
+		return DEFAULT_MTU_SIZE;
+	}
+
 	static bool isWanUP_V6(int ipa_if_num_tether)
 	{
 #ifdef FEATURE_IPA_ANDROID
@@ -215,15 +243,48 @@
 		return IPACM_SUCCESS;
 	}
 #endif
+	static void coalesce_config(uint8_t qmap_id, bool tcp_enable, bool udp_enable)
+	{
+		if (qmap_id >= IPA_MAX_NUM_SW_PDNS)
+		{
+			IPACMERR("qmap_id (%d) beyond the Max range (%d), abort\n",
+				qmap_id, IPA_MAX_NUM_SW_PDNS);
+			return ;
+		}
+
+		IPACM_Wan::coalesce_enable_info[qmap_id].coalesce_tcp_enable = tcp_enable;
+		IPACM_Wan::coalesce_enable_info[qmap_id].coalesce_udp_enable = udp_enable;
+		IPACMDBG_H(" Updated qmap(%d) coalesce enable TCP:%d UDP:%d\n",
+				qmap_id,
+				IPACM_Wan::coalesce_enable_info[qmap_id].coalesce_tcp_enable,
+				IPACM_Wan::coalesce_enable_info[qmap_id].coalesce_udp_enable);
+		return ;
+	}
+
+	static void coalesce_config_reset()
+	{
+		int i;
+		/* reset coalesce settings on all modem interfaces */
+		for (i = 0; i < IPA_MAX_NUM_SW_PDNS; i++)
+			IPACM_Wan::coalesce_config(i, false, false);
+		return ;
+	}
 
 	static uint32_t getWANIP()
 	{
 		return curr_wan_ip;
 	}
 
-	static bool getXlat_Mux_Id()
+	static int getXlat_Mux_Id()
 	{
-		return xlat_mux_id;
+		if (is_xlat)
+		{
+			IPACMDBG_H("xlat_mux_id: %d\n", xlat_mux_id);
+			return xlat_mux_id;
+		} else {
+			IPACMDBG_H("no xlat return invalid mux-id: 0\n");
+			return 0;
+		}
 	}
 
 	static void clearExtProp()
@@ -243,12 +304,13 @@
 	static int num_v6_flt_rule;
 
 	ipacm_wan_iface_type m_is_sta_mode;
-	static bool backhaul_is_sta_mode;
+	static ipacm_wan_iface_type backhaul_mode;
 	static bool is_ext_prop_set;
 	static uint32_t backhaul_ipv6_prefix[2];
 
 	static bool embms_is_on;
 	static bool backhaul_is_wan_bridge;
+	static bool is_xlat;
 
 	static bool isWan_Bridge_Mode()
 	{
@@ -260,8 +322,16 @@
 	static int ipa_if_num_tether_v4[IPA_MAX_IFACE_ENTRIES];
 	static uint32_t ipa_if_num_tether_v6_total;
 	static int ipa_if_num_tether_v6[IPA_MAX_IFACE_ENTRIES];
+
+	static bool isXlat()
+	{
+		return is_xlat;
+	}
 #endif
 
+	/* indicate coalesce support on tcp or udp*/
+	static ipacm_coalesce coalesce_enable_info[IPA_MAX_NUM_SW_PDNS];
+
 private:
 
 	bool is_ipv6_frag_firewall_flt_rule_installed;
@@ -292,6 +362,8 @@
 	bool header_partial_default_wan_v6;
 	uint8_t ext_router_mac_addr[IPA_MAC_ADDR_SIZE];
 	uint8_t netdev_mac[IPA_MAC_ADDR_SIZE];
+	/* create additional set of v4 Coalesce RT-rules: tcp udp */
+	uint32_t dft_coalesce_rt_rule_hdl[2*MAX_DEFAULT_v4_ROUTE_RULES+ 2*MAX_DEFAULT_v6_ROUTE_RULES];
 
 	static int num_ipv4_modem_pdn;
 
@@ -314,13 +386,25 @@
 	int header_name_count;
 	uint32_t num_wan_client;
 	uint8_t invalid_mac[IPA_MAC_ADDR_SIZE];
-	bool is_xlat;
+	bool is_xlat_local;
 
 	/* update network stats for CNE */
 	int ipa_network_stats_fd;
 	uint32_t hdr_hdl_dummy_v6;
 	uint32_t hdr_proc_hdl_dummy_v6;
 
+	/* handle for UDP mhi frag rule */
+	uint32_t mhi_dl_v4_frag_hdl;
+
+	/* 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);
@@ -506,6 +590,9 @@
 	/* handle new_address event */
 	int handle_addr_evt(ipacm_event_data_addr *data);
 
+	/* handle new_address event for q6_mhi */
+	int handle_addr_evt_mhi_q6(ipacm_event_data_addr *data);
+
 	/* wan default route/filter rule configuration */
 	int handle_route_add_evt(ipa_ip_type iptype);
 
@@ -562,10 +649,6 @@
 
 	int install_wan_filtering_rule(bool is_sw_routing);
 
-	void change_to_network_order(ipa_ip_type iptype, ipa_rule_attrib* attrib);
-
-	bool is_global_ipv6_addr(uint32_t* ipv6_addr);
-
 	void handle_wlan_SCC_MCC_switch(bool, ipa_ip_type);
 
 	void handle_wan_client_SCC_MCC_switch(bool, ipa_ip_type);
@@ -578,6 +661,23 @@
 
 	/* construct dummy ethernet header */
 	int add_dummy_rx_hdr();
+
+	int handle_coalesce_evt();
+
+	int add_offload_frag_rule();
+
+	int delete_offload_frag_rule();
+
+	int add_icmpv6_exception_rule();
+
+	int delete_icmpv6_exception_rule();
+
+	int add_tcp_fin_rst_exception_rule();
+
+	int delete_tcp_fin_rst_exception_rule();
+
+	/* Query mtu size */
+	int query_mtu_size();
 };
 
 #endif /* IPACM_WAN_H */
diff --git a/ipacm/inc/IPACM_Wlan.h b/ipacm/inc/IPACM_Wlan.h
index f3c6d65..9c828ee 100644
--- a/ipacm/inc/IPACM_Wlan.h
+++ b/ipacm/inc/IPACM_Wlan.h
@@ -54,6 +54,7 @@
 	uint32_t wifi_rt_rule_hdl_v4;
 	uint32_t wifi_rt_rule_hdl_v6[IPV6_NUM_ADDR];
 	uint32_t wifi_rt_rule_hdl_v6_wan[IPV6_NUM_ADDR];
+
 }wlan_client_rt_hdl;
 
 typedef struct _ipa_wlan_client
@@ -72,6 +73,8 @@
 	bool ipv6_header_set;
 	bool power_save_set;
 	enum ipa_client_type wigig_ipa_client;
+	/* used for pcie-modem */
+	uint32_t v6_rt_rule_id[IPV6_NUM_ADDR];
 	wlan_client_rt_hdl wifi_rt_hdl[0]; /* depends on number of tx properties */
 }ipa_wlan_client;
 
@@ -185,6 +188,18 @@
 				{
 					for(num_v6 =0;num_v6 < get_client_memptr(wlan_client, clt_indx)->route_rule_set_v6;num_v6++)
 					{
+						/* send client-v6 delete to pcie modem only with global ipv6 with tx_index = 0 one time*/
+						if(is_global_ipv6_addr(get_client_memptr(wlan_client, clt_indx)->v6_addr[num_v6]) && (IPACM_Wan::backhaul_mode == Q6_MHI_WAN)
+							&& (get_client_memptr(wlan_client, clt_indx)->v6_rt_rule_id[num_v6] > 0))
+						{
+							IPACMDBG_H("Delete client index %d ipv6 RT-rules for %d-st ipv6 for rule-id:%d\n", clt_indx,num_v6,
+								get_client_memptr(wlan_client, clt_indx)->v6_rt_rule_id[num_v6]);
+							if (del_connection(clt_indx, num_v6))
+							{
+								IPACMERR("PCIE filter rule deletion failed! (%d-client) %d v6-entry\n",clt_indx, num_v6);
+							}
+						}
+
 						IPACMDBG_H("Delete client index %d ipv6 Qos rules for %d-st ipv6 for tx:%d\n", clt_indx,num_v6,tx_index);
 						rt_hdl = get_client_memptr(wlan_client, clt_indx)->wifi_rt_hdl[tx_index].wifi_rt_rule_hdl_v6[num_v6];
 						if(m_routing.DeleteRoutingHdl(rt_hdl, IPA_IP_v6) == false)
@@ -237,6 +252,11 @@
 
 	void handle_SCC_MCC_switch(ipa_ip_type);
 
+	/* for pcie modem */
+	int add_connection(int client_index, int v6_num);
+
+	int del_connection(int client_index, int v6_num);
+
 #ifdef FEATURE_IPACM_RESTART
 	/*query wlan-clients */
 	int ipa_query_wlan_client();
diff --git a/ipacm/src/Android.mk b/ipacm/src/Android.mk
deleted file mode 100644
index 78783b1..0000000
--- a/ipacm/src/Android.mk
+++ /dev/null
@@ -1,125 +0,0 @@
-BOARD_PLATFORM_LIST := msm8909
-BOARD_PLATFORM_LIST += msm8916
-BOARD_PLATFORM_LIST += msm8917
-BOARD_IPAv3_LIST := msm8998
-BOARD_IPAv3_LIST += sdm845
-BOARD_IPAv3_LIST += sdm710
-BOARD_IPAv3_LIST += msmnile
-BOARD_IPAv3_LIST += kona
-BOARD_IPAv3_LIST += $(MSMSTEPPE)
-BOARD_IPAv3_LIST += $(TRINKET)
-BOARD_IPAv3_LIST += lito
-BOARD_IPAv3_LIST += atoll
-
-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)))
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/../src
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/../inc
-
-LOCAL_C_INCLUDES += $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr/include
-LOCAL_ADDITIONAL_DEPENDENCIES := $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr
-
-LOCAL_CFLAGS := -v
-LOCAL_CFLAGS += -DFEATURE_IPA_ANDROID
-LOCAL_CFLAGS += -DFEATURE_IPACM_RESTART
-LOCAL_CFLAGS += -DFEATURE_ETH_BRIDGE_LE
-
-LOCAL_CFLAGS += -DFEATURE_IPACM_HAL -Wall -Werror -Wno-error=macro-redefined -Wno-enum-compare
-ifneq (,$(filter userdebug eng, $(TARGET_BUILD_VARIANT)))
-LOCAL_CFLAGS += -DDEBUG
-endif
-
-ifeq ($(call is-board-platform-in-list,$(BOARD_IPAv3_LIST)),true)
-LOCAL_CFLAGS += -DFEATURE_IPA_V3
-endif
-
-filetoadd = bionic/libc/kernel/arch-arm/asm/posix_types.h
-LOCAL_CFLAGS += $(shell if [ -a $(filetoadd) ] ; then echo -include $(filetoadd) ; fi ;)
-filetoadd = bionic/libc/kernel/arch-arm/asm/byteorder.h
-LOCAL_CFLAGS += $(shell if [ -a $(filetoadd) ] ; then echo -include $(filetoadd) ; fi ;)
-
-# Allow warnings in IPACM_Main.cpp until they are fixed.
-LOCAL_CFLAGS += -Wno-error=implicit-fallthrough
-
-LOCAL_SRC_FILES := IPACM_Main.cpp \
-		IPACM_EvtDispatcher.cpp \
-		IPACM_Config.cpp \
-		IPACM_CmdQueue.cpp \
-		IPACM_Filtering.cpp \
-		IPACM_Routing.cpp \
-		IPACM_Header.cpp \
-		IPACM_Lan.cpp \
-		IPACM_Iface.cpp \
-		IPACM_Wlan.cpp \
-		IPACM_Wan.cpp \
-		IPACM_IfaceManager.cpp \
-		IPACM_Neighbor.cpp \
-		IPACM_Netlink.cpp \
-		IPACM_Xml.cpp \
-		IPACM_Conntrack_NATApp.cpp\
-		IPACM_ConntrackClient.cpp \
-		IPACM_ConntrackListener.cpp \
-		IPACM_Log.cpp \
-		IPACM_OffloadManager.cpp \
-		IPACM_LanToLan.cpp
-
-LOCAL_MODULE := ipacm
-LOCAL_INIT_RC := ipacm.rc
-LOCAL_CLANG := false
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SHARED_LIBRARIES := liboffloadhal
-LOCAL_SHARED_LIBRARIES += libipanat
-LOCAL_SHARED_LIBRARIES += libxml2
-LOCAL_SHARED_LIBRARIES += libnfnetlink
-LOCAL_SHARED_LIBRARIES += libnetfilter_conntrack
-LOCAL_SHARED_LIBRARIES += libhwbinder \
-                libhidlbase \
-                libhidltransport \
-                liblog \
-                libcutils \
-                libdl \
-                libbase \
-                libutils \
-                libhardware_legacy \
-                libhardware \
-                android.hardware.tetheroffload.config@1.0 \
-                android.hardware.tetheroffload.control@1.0
-
-LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_EXECUTABLES)
-
-LOCAL_CLANG := true
-include $(BUILD_EXECUTABLE)
-
-################################################################################
-
-define ADD_TEST
-
-include $(CLEAR_VARS)
-LOCAL_MODULE       := $1
-LOCAL_SRC_FILES    := $1
-LOCAL_MODULE_CLASS := ipacm
-LOCAL_MODULE_TAGS  := optional
-LOCAL_MODULE_PATH  := $(TARGET_OUT_ETC)
-include $(BUILD_PREBUILT)
-
-endef
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := IPACM_cfg.xml
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_ETC)
-LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-LOCAL_MODULE_OWNER := ipacm
-include $(BUILD_PREBUILT)
-
-endif # $(TARGET_ARCH)
-endif
-endif
diff --git a/ipacm/src/IPACM_Config.cpp b/ipacm/src/IPACM_Config.cpp
index bfacd7c..c396c6c 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));
@@ -901,3 +905,26 @@
 	IPACMDBG_H("IPA version is %d.\n", ver);
 	return ver;
 }
+
+bool IPACM_Config::isEthBridgingSupported()
+{
+	enum ipa_hw_type hw_type;
+
+	hw_type = GetIPAVer();
+
+#ifdef IPA_HW_v4_7
+	return ((hw_type >= IPA_HW_v4_5) &&
+		(hw_type != IPA_HW_v4_7));
+#else
+	return (hw_type >= IPA_HW_v4_5);
+#endif
+}
+
+bool IPACM_Config::isIPAv3Supported()
+{
+	enum ipa_hw_type hw_type;
+
+	hw_type = GetIPAVer();
+
+	return (hw_type >= IPA_HW_v3_0);
+}
diff --git a/ipacm/src/IPACM_ConntrackListener.cpp b/ipacm/src/IPACM_ConntrackListener.cpp
index d6289e3..e006393 100644
--- a/ipacm/src/IPACM_ConntrackListener.cpp
+++ b/ipacm/src/IPACM_ConntrackListener.cpp
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
+Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
@@ -230,7 +230,7 @@
 	bool NatIface = false;
 	int cnt, ret;
 
-	if (isStaMode)
+	if (backhaul_mode != Q6_WAN)
 	{
 		IPACMDBG("In STA mode, don't add dummy rules for non nat ifaces\n");
 		return;
@@ -368,8 +368,8 @@
 	 }
 
 	 WanUp = true;
-	 isStaMode = wanup_data->is_sta;
-	 IPACMDBG("isStaMode: %d\n", isStaMode);
+	 backhaul_mode = wanup_data->backhaul_type;
+	 IPACMDBG("backhaul_mode: %d\n", backhaul_mode);
 
 	 wan_ipaddr = wanup_data->ipv4_addr;
 	 memcpy(wan_ifname, wanup_data->ifname, sizeof(wan_ifname));
@@ -730,7 +730,7 @@
 		}
 	}
 
-	if (!isStaMode)
+	if (backhaul_mode == Q6_WAN)
 	{
 		/* check whether non nat iface or not, on Non Nat iface
 		   add dummy rule by copying public ip to private ip */
@@ -999,7 +999,7 @@
 
 	/* Check whether target is in STA client list or not
       if not ignore the connection */
-	 if(!isStaMode || (StaClntCnt == 0))
+	 if((backhaul_mode == Q6_WAN) || (StaClntCnt == 0))
 	 {
 		return;
 	 }
@@ -1114,7 +1114,7 @@
 	}
 	else
 	{
-		if (isStaMode)
+		if (backhaul_mode != Q6_WAN)
 		{
 			IPACMDBG("In STA mode, ignore connections destinated to STA interface\n");
 			goto IGNORE;
diff --git a/ipacm/src/IPACM_Conntrack_NATApp.cpp b/ipacm/src/IPACM_Conntrack_NATApp.cpp
index 4004597..a9c7140 100644
--- a/ipacm/src/IPACM_Conntrack_NATApp.cpp
+++ b/ipacm/src/IPACM_Conntrack_NATApp.cpp
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
+Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
@@ -28,6 +28,7 @@
 */
 #include "IPACM_Conntrack_NATApp.h"
 #include "IPACM_ConntrackClient.h"
+#include "IPACM_ConntrackListener.h"
 #ifdef FEATURE_IPACM_HAL
 #include "IPACM_OffloadManager.h"
 #endif
@@ -47,6 +48,7 @@
 
 	nat_table_hdl = 0;
 	pub_ip_addr = 0;
+	pub_mux_id = 0;
 
 	curCnt = 0;
 
@@ -57,6 +59,18 @@
 	ct_hdl = NULL;
 
 	memset(temp, 0, sizeof(temp));
+	m_fd_ipa = open(IPA_DEVICE_NAME, O_RDWR);
+	if(m_fd_ipa < 0)
+	{
+		IPACMERR("Failed to open %s\n",IPA_DEVICE_NAME);
+	}
+}
+
+NatApp::~NatApp()
+{
+	if (m_fd_ipa) {
+		close(m_fd_ipa);
+	}
 }
 
 int NatApp::Init(void)
@@ -205,6 +219,22 @@
 					continue;
 				}
 				cache[cnt].enabled = true;
+				/* send connections info to pcie modem only with DL direction */
+				if ((CtList->backhaul_mode == Q6_MHI_WAN) && (cache[cnt].dst_nat == true || cache[cnt].protocol == IPPROTO_TCP))
+				{
+					ret = AddConnection(&cache[cnt]);
+					if(ret > 0)
+					{
+						/* save the rule id for deletion */
+						cache[cnt].rule_id = ret;
+						IPACMDBG_H("rule-id(%d)\n", cache[cnt].rule_id);
+					}
+					else
+					{
+						IPACMERR("unable to add Connection to pcie modem: error:%d\n", ret);
+						cache[cnt].rule_id = 0;
+					}
+				}
 
 				IPACMDBG("On wan-iface reset added below rule successfully\n");
 				iptodot("Private IP", nat_rule.private_ip);
@@ -217,24 +247,21 @@
 	}
 
 	pub_ip_addr = pub_ip;
+	pub_mux_id = mux_id;
+	IPACMDBG(" Set pub_mux_id: %d\t", pub_mux_id);
 	return 0;
 }
 
 void NatApp::Reset()
 {
-	int cnt = 0;
-
 	nat_table_hdl = 0;
 	pub_ip_addr = 0;
-	/* NAT tbl deleted, reset enabled bit */
-	for(cnt = 0; cnt < max_entries; cnt++)
-	{
-		cache[cnt].enabled = false;
-	}
+	pub_mux_id = 0;
 }
 
 int NatApp::DeleteTable(uint32_t pub_ip)
 {
+	int cnt = 0;
 	int ret;
 	IPACMDBG_H("%s() %d\n", __FUNCTION__, __LINE__);
 
@@ -247,6 +274,27 @@
 		return -1;
 	}
 
+	/* NAT tbl deleted, reset enabled bit */
+	for(cnt = 0; cnt < max_entries; cnt++)
+	{
+		cache[cnt].enabled = false;
+		/* send connections del info to pcie modem first */
+		if ((CtList->backhaul_mode == Q6_MHI_WAN) && (cache[cnt].dst_nat == true || cache[cnt].protocol == IPPROTO_TCP) && (cache[cnt].rule_id > 0))
+
+		{
+			ret = DelConnection(cache[cnt].rule_id);
+			if(ret)
+			{
+				IPACMERR("unable to del Connection to pcie modem: %d\n", ret);
+			}
+			else
+			{
+				/* save the rule id for deletion */
+				cache[cnt].rule_id = 0;
+			}
+		}
+	}
+
 	ret = ipa_nat_del_ipv4_tbl(nat_table_hdl);
 	if(ret)
 	{
@@ -286,6 +334,7 @@
 int NatApp::DeleteEntry(const nat_table_entry *rule)
 {
 	int cnt = 0;
+	int ret = 0;
 	IPACMDBG("%s() %d\n", __FUNCTION__, __LINE__);
 
 	log_nat(rule->protocol,rule->private_ip,rule->target_ip,rule->private_port,\
@@ -303,6 +352,21 @@
 
 			if(cache[cnt].enabled == true)
 			{
+				/* send connections del info to pcie modem first */
+				if ((CtList->backhaul_mode == Q6_MHI_WAN) && (cache[cnt].dst_nat == true || cache[cnt].protocol == IPPROTO_TCP) && (cache[cnt].rule_id > 0))
+				{
+					ret = DelConnection(cache[cnt].rule_id);
+					if(ret)
+					{
+						IPACMERR("unable to del Connection to pcie modem: %d\n", ret);
+					}
+					else
+					{
+						/* save the rule id for deletion */
+						cache[cnt].rule_id = 0;
+					}
+				}
+
 				if(ipa_nat_del_ipv4_rule(nat_table_hdl, cache[cnt].rule_hdl) < 0)
 				{
 					IPACMERR("%s() %d deletion failed\n", __FUNCTION__, __LINE__);
@@ -329,6 +393,7 @@
 {
 	int cnt = 0;
 	ipa_nat_ipv4_rule nat_rule;
+	int ret = 0;
 
 	IPACMDBG("%s() %d\n", __FUNCTION__, __LINE__);
 
@@ -398,8 +463,23 @@
 				}
 
 				cache[cnt].enabled = true;
+				/* send connections info to pcie modem only with DL direction */
+				if ((CtList->backhaul_mode == Q6_MHI_WAN) && (rule->dst_nat == true || rule->protocol == IPPROTO_TCP))
+				{
+					ret = AddConnection(rule);
+					if(ret > 0)
+					{
+						/* save the rule id for deletion */
+						cache[cnt].rule_id = ret;
+						IPACMDBG_H("rule-id(%d)\n", cache[cnt].rule_id);
+					}
+					else
+					{
+						IPACMERR("unable to add Connection to pcie modem: error:%d\n", ret);
+						cache[cnt].rule_id = 0;
+					}
+				}
 			}
-
 			cache[cnt].private_ip = rule->private_ip;
 			cache[cnt].target_ip = rule->target_ip;
 			cache[cnt].target_port = rule->target_port;
@@ -430,6 +510,136 @@
 	return 0;
 }
 
+/* Add new entry to the nat table on new connection, return rule-id */
+int NatApp::AddConnection(const nat_table_entry *rule)
+{
+	int len, res = IPACM_SUCCESS;
+	ipa_ioc_add_flt_rule *pFilteringTable = NULL;
+
+	/* contruct filter rules to pcie modem */
+	struct ipa_flt_rule_add flt_rule_entry;
+	ipa_ioc_generate_flt_eq flt_eq;
+
+	IPACMDBG("\n");
+	len = sizeof(struct ipa_ioc_add_flt_rule) + 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)1;
+
+	/* Configuring Software-Routing Filtering Rule */
+	memset(&flt_rule_entry, 0, sizeof(struct ipa_flt_rule_add));
+	flt_rule_entry.at_rear = true;
+	flt_rule_entry.flt_rule_hdl = -1;
+	flt_rule_entry.status = -1;
+
+	flt_rule_entry.rule.retain_hdr = 1;
+	flt_rule_entry.rule.to_uc = 0;
+	flt_rule_entry.rule.eq_attrib_type = 1;
+	flt_rule_entry.rule.action = IPA_PASS_TO_ROUTING;
+	if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+		flt_rule_entry.rule.hashable = true;
+	flt_rule_entry.rule.attrib.attrib_mask |= IPA_FLT_SRC_PORT;
+	flt_rule_entry.rule.attrib.src_port = rule->target_port;
+	flt_rule_entry.rule.attrib.attrib_mask |= IPA_FLT_DST_PORT;
+	flt_rule_entry.rule.attrib.dst_port = rule->public_port;
+	flt_rule_entry.rule.attrib.attrib_mask |= IPA_FLT_PROTOCOL;
+	flt_rule_entry.rule.attrib.u.v4.protocol = rule->protocol;
+	flt_rule_entry.rule.attrib.attrib_mask |= IPA_FLT_DST_ADDR;
+	flt_rule_entry.rule.attrib.u.v4.dst_addr_mask = 0xFFFFFFFF;
+	flt_rule_entry.rule.attrib.u.v4.dst_addr = rule->public_ip;
+	flt_rule_entry.rule.attrib.attrib_mask |= IPA_FLT_SRC_ADDR;
+	flt_rule_entry.rule.attrib.u.v4.src_addr_mask = 0xFFFFFFFF;
+	flt_rule_entry.rule.attrib.u.v4.src_addr = rule->target_ip;
+	IPACMDBG_H("src(0x%x) port(%d)->dst(0x%x) port(%d), protocol(%d) pub_mux_id (%d)\n",
+				rule->target_ip, rule->target_port, rule->public_ip, rule->public_port,
+				rule->protocol, pub_mux_id);
+
+	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(m_fd_ipa, IPA_IOC_GENERATE_FLT_EQ, &flt_eq))
+	{
+		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(&(pFilteringTable->rules[0]), &flt_rule_entry, sizeof(struct ipa_flt_rule_add));
+
+	if(false == IPACM_Iface::m_filtering.AddOffloadFilteringRule(pFilteringTable, pub_mux_id, 0))
+	{
+		IPACMERR("Failed to install WAN DL filtering table.\n");
+		res = IPACM_FAILURE;
+		goto fail;
+	}
+
+	/* get rule-id */
+	res = pFilteringTable->rules[0].flt_rule_hdl;
+
+fail:
+	if(pFilteringTable != NULL)
+	{
+		free(pFilteringTable);
+	}
+	return res;
+}
+
+int NatApp::DelConnection(const uint32_t rule_id)
+{
+	int len, res = IPACM_SUCCESS;
+	ipa_ioc_del_flt_rule *pFilteringTable = NULL;
+
+
+	struct ipa_flt_rule_del flt_rule_entry;
+
+	IPACMDBG("\n");
+	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;
+
+	/* Configuring Software-Routing Filtering Rule */
+	memset(&flt_rule_entry, 0, sizeof(struct ipa_flt_rule_del));
+	flt_rule_entry.hdl = rule_id;
+
+	memcpy(&(pFilteringTable->hdl[0]), &flt_rule_entry, sizeof(struct ipa_flt_rule_del));
+
+	if(false == IPACM_Iface::m_filtering.DelOffloadFilteringRule(pFilteringTable))
+	{
+		IPACMERR("Failed to install WAN DL filtering table.\n");
+		res = IPACM_FAILURE;
+		goto fail;
+	}
+
+fail:
+	if(pFilteringTable != NULL)
+	{
+		free(pFilteringTable);
+	}
+	return res;
+}
+
 void NatApp::UpdateCTUdpTs(nat_table_entry *rule, uint32_t new_ts)
 {
 #ifdef FEATURE_IPACM_HAL
@@ -629,7 +839,7 @@
 
 int NatApp::UpdatePwrSaveIf(uint32_t client_lan_ip)
 {
-	int cnt;
+	int cnt, ret;
 	IPACMDBG_H("Received IP address: 0x%x\n", client_lan_ip);
 
 	if(client_lan_ip == INVALID_IP_ADDR)
@@ -662,6 +872,21 @@
 		if(cache[cnt].private_ip == client_lan_ip &&
 			 cache[cnt].enabled == true)
 		{
+			/* send connections del info to pcie modem first */
+			if ((CtList->backhaul_mode == Q6_MHI_WAN) && (cache[cnt].dst_nat == true || cache[cnt].protocol == IPPROTO_TCP) && (cache[cnt].rule_id > 0))
+			{
+				ret = DelConnection(cache[cnt].rule_id);
+				if(ret)
+				{
+					IPACMERR("unable to del Connection to pcie modem: %d\n", ret);
+				}
+				else
+				{
+					/* save the rule id for deletion */
+					cache[cnt].rule_id = 0;
+				}
+			}
+
 			if(ipa_nat_del_ipv4_rule(nat_table_hdl, cache[cnt].rule_hdl) < 0)
 			{
 				IPACMERR("unable to delete the rule\n");
@@ -678,7 +903,7 @@
 
 int NatApp::ResetPwrSaveIf(uint32_t client_lan_ip)
 {
-	int cnt;
+	int cnt, ret;
 	ipa_nat_ipv4_rule nat_rule;
 
 	IPACMDBG_H("Received ip address: 0x%x\n", client_lan_ip);
@@ -721,6 +946,22 @@
 				continue;
 			}
 			cache[cnt].enabled = true;
+			/* send connections info to pcie modem only with DL direction */
+			if ((CtList->backhaul_mode == Q6_MHI_WAN) && (cache[cnt].dst_nat == true || cache[cnt].protocol == IPPROTO_TCP))
+			{
+				ret = AddConnection(&cache[cnt]);
+				if(ret > 0)
+				{
+					/* save the rule id for deletion */
+					cache[cnt].rule_id = ret;
+					IPACMDBG_H("rule-id(%d)\n", cache[cnt].rule_id);
+				}
+				else
+				{
+					IPACMERR("unable to add Connection to pcie modem: error:%d\n", ret);
+					cache[cnt].rule_id = 0;
+				}
+			}
 
 			IPACMDBG("On power reset added below rule successfully\n");
 			iptodot("Private IP", nat_rule.private_ip);
@@ -866,7 +1107,7 @@
 
 int NatApp::DelEntriesOnClntDiscon(uint32_t ip_addr)
 {
-	int cnt, tmp = 0;
+	int cnt, tmp = 0, ret;
 	IPACMDBG_H("Received IP address: 0x%x\n", ip_addr);
 
 	if(ip_addr == INVALID_IP_ADDR)
@@ -891,6 +1132,21 @@
 		{
 			if(cache[cnt].enabled == true)
 			{
+				/* send connections del info to pcie modem first */
+				if ((CtList->backhaul_mode == Q6_MHI_WAN) && (cache[cnt].dst_nat == true || cache[cnt].protocol == IPPROTO_TCP) && (cache[cnt].rule_id > 0))
+				{
+					ret = DelConnection(cache[cnt].rule_id);
+					if(ret)
+					{
+						IPACMERR("unable to del Connection to pcie modem: %d\n", ret);
+					}
+					else
+					{
+						/* save the rule id for deletion */
+						cache[cnt].rule_id = 0;
+					}
+				}
+
 				if(ipa_nat_del_ipv4_rule(nat_table_hdl, cache[cnt].rule_hdl) < 0)
 				{
 					IPACMERR("unable to delete the rule\n");
@@ -913,7 +1169,7 @@
 
 int NatApp::DelEntriesOnSTAClntDiscon(uint32_t ip_addr)
 {
-	int cnt, tmp = curCnt;
+	int cnt, tmp = curCnt, ret;
 	IPACMDBG_H("Received IP address: 0x%x\n", ip_addr);
 
 	if(ip_addr == INVALID_IP_ADDR)
@@ -929,6 +1185,21 @@
 		{
 			if(cache[cnt].enabled == true)
 			{
+				/* send connections del info to pcie modem first */
+				if ((CtList->backhaul_mode == Q6_MHI_WAN) && (cache[cnt].dst_nat == true || cache[cnt].protocol == IPPROTO_TCP) && (cache[cnt].rule_id > 0))
+				{
+					ret = DelConnection(cache[cnt].rule_id);
+					if(ret)
+					{
+						IPACMERR("unable to del Connection to pcie modem: %d\n", ret);
+					}
+					else
+					{
+						/* save the rule id for deletion */
+						cache[cnt].rule_id = 0;
+					}
+				}
+
 				if(ipa_nat_del_ipv4_rule(nat_table_hdl, cache[cnt].rule_hdl) < 0)
 				{
 					IPACMERR("unable to delete the rule\n");
diff --git a/ipacm/src/IPACM_Filtering.cpp b/ipacm/src/IPACM_Filtering.cpp
index 3545d81..8aa25a6 100644
--- a/ipacm/src/IPACM_Filtering.cpp
+++ b/ipacm/src/IPACM_Filtering.cpp
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
+Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
@@ -46,6 +46,7 @@
 #include "IPACM_Filtering.h"
 #include <IPACM_Log.h>
 #include "IPACM_Defs.h"
+#include "IPACM_Iface.h"
 
 
 const char *IPACM_Filtering::DEVICE_NAME = "/dev/ipa";
@@ -57,6 +58,8 @@
 	{
 		IPACMERR("Failed opening %s.\n", DEVICE_NAME);
 	}
+	total_num_offload_rules = 0;
+	pcie_modem_rule_id = 0;
 }
 
 IPACM_Filtering::~IPACM_Filtering()
@@ -114,38 +117,280 @@
 	return true;
 }
 
-bool IPACM_Filtering::AddFilteringRuleAfter(struct ipa_ioc_add_flt_rule_after const *ruleTable)
+#ifdef IPA_IOCTL_SET_FNR_COUNTER_INFO
+bool IPACM_Filtering::AddFilteringRule_hw_index(struct ipa_ioc_add_flt_rule *ruleTable, int hw_counter_index)
 {
-#ifdef FEATURE_IPA_V3
-	int retval = 0;
+	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\n", ruleTable->ep);
+	IPACMDBG("End point: %d and global value: %d\n", ruleTable->ep, ruleTable->global);
 	IPACMDBG("commit value: %d\n", ruleTable->commit);
 
-	retval = ioctl(fd, IPA_IOC_ADD_FLT_RULE_AFTER, ruleTable);
-
-	for (int cnt = 0; cnt<ruleTable->num_rules; cnt++)
+	/* 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)
 	{
-		if(ruleTable->rules[cnt].status != 0)
+		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, ruleTable->rules[cnt].status);
+							 cnt, ((struct ipa_flt_rule_add_v2 *) ruleTable_v2->rules)[cnt].status);
 		}
 	}
 
-	if (retval != 0)
+	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;
+	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;
+
+	if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
 	{
-		IPACMERR("Failed adding Filtering rule %pK\n", ruleTable);
-		return false;
+		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);
 	}
-	IPACMDBG("Added Filtering rule %pK\n", ruleTable);
-#else
-	if (ruleTable)
-	IPACMERR("Not support adding Filtering rule %pK\n", ruleTable);
-#endif
+	else
+	{
+		if (ruleTable)
+			IPACMERR("Not support adding Filtering rule %pK\n", ruleTable);
+	}
+	return ret;
+}
+#endif //IPA_IOCTL_SET_FNR_COUNTER_INFO
+
+bool IPACM_Filtering::AddFilteringRuleAfter(struct ipa_ioc_add_flt_rule_after const *ruleTable)
+{
+	int retval = 0;
+
+	if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+	{
+		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\n", ruleTable->ep);
+		IPACMDBG("commit value: %d\n", ruleTable->commit);
+
+		retval = ioctl(fd, IPA_IOC_ADD_FLT_RULE_AFTER, ruleTable);
+
+		for (int cnt = 0; cnt<ruleTable->num_rules; cnt++)
+		{
+			if(ruleTable->rules[cnt].status != 0)
+			{
+				IPACMERR("Adding Filter rule:%d failed with status:%d\n",
+								 cnt, ruleTable->rules[cnt].status);
+			}
+		}
+
+		if (retval != 0)
+		{
+			IPACMERR("Failed adding Filtering rule %pK\n", ruleTable);
+			return false;
+		}
+		IPACMDBG("Added Filtering rule %pK\n", ruleTable);
+	}
+	else
+	{
+		if (ruleTable)
+			IPACMERR("Not support adding Filtering rule %pK\n", ruleTable);
+	}
 	return true;
 }
 
@@ -263,9 +508,7 @@
 {
 	int ret = 0, cnt, num_rules = 0, pos = 0;
 	ipa_install_fltr_rule_req_msg_v01 qmi_rule_msg;
-#ifdef FEATURE_IPA_V3
 	ipa_install_fltr_rule_req_ex_msg_v01 qmi_rule_ex_msg;
-#endif
 
 	memset(&qmi_rule_msg, 0, sizeof(qmi_rule_msg));
 	int fd_wwan_ioctl = open(WWAN_QMI_IOCTL_DEVICE_NAME, O_RDWR);
@@ -287,180 +530,402 @@
 	}
 
 	/* if it is not IPA v3, use old QMI format */
-#ifndef FEATURE_IPA_V3
-	if(num_rules > QMI_IPA_MAX_FILTERS_V01)
+	if (!IPACM_Iface::ipacmcfg->isIPAv3Supported())
 	{
-		IPACMERR("The number of filtering rules exceed limit.\n");
-		close(fd_wwan_ioctl);
-		return false;
-	}
-	else
-	{
-		if (num_rules > 0)
+		if(num_rules > QMI_IPA_MAX_FILTERS_V01)
 		{
-			qmi_rule_msg.filter_spec_list_valid = true;
-		}
-		else
-		{
-			qmi_rule_msg.filter_spec_list_valid = false;
-		}
-
-		qmi_rule_msg.filter_spec_list_len = num_rules;
-		qmi_rule_msg.source_pipe_index_valid = 0;
-
-		IPACMDBG_H("Get %d WAN DL filtering rules in total.\n", num_rules);
-
-		if(rule_table_v4 != NULL)
-		{
-			for(cnt = rule_table_v4->num_rules - 1; cnt >= 0; cnt--)
-			{
-				if (pos < QMI_IPA_MAX_FILTERS_V01)
-				{
-					qmi_rule_msg.filter_spec_list[pos].filter_spec_identifier = pos;
-					qmi_rule_msg.filter_spec_list[pos].ip_type = QMI_IPA_IP_TYPE_V4_V01;
-					qmi_rule_msg.filter_spec_list[pos].filter_action = GetQmiFilterAction(rule_table_v4->rules[cnt].rule.action);
-					qmi_rule_msg.filter_spec_list[pos].is_routing_table_index_valid = 1;
-					qmi_rule_msg.filter_spec_list[pos].route_table_index = rule_table_v4->rules[cnt].rule.rt_tbl_idx;
-					qmi_rule_msg.filter_spec_list[pos].is_mux_id_valid = 1;
-					qmi_rule_msg.filter_spec_list[pos].mux_id = mux_id;
-					memcpy(&qmi_rule_msg.filter_spec_list[pos].filter_rule,
-						&rule_table_v4->rules[cnt].rule.eq_attrib,
-						sizeof(struct ipa_filter_rule_type_v01));
-					pos++;
-				}
-				else
-				{
-					IPACMERR(" QMI only support max %d rules, current (%d)\n ",QMI_IPA_MAX_FILTERS_V01, pos);
-				}
-			}
-		}
-
-		if(rule_table_v6 != NULL)
-		{
-			for(cnt = rule_table_v6->num_rules - 1; cnt >= 0; cnt--)
-			{
-				if (pos < QMI_IPA_MAX_FILTERS_V01)
-				{
-					qmi_rule_msg.filter_spec_list[pos].filter_spec_identifier = pos;
-					qmi_rule_msg.filter_spec_list[pos].ip_type = QMI_IPA_IP_TYPE_V6_V01;
-					qmi_rule_msg.filter_spec_list[pos].filter_action = GetQmiFilterAction(rule_table_v6->rules[cnt].rule.action);
-					qmi_rule_msg.filter_spec_list[pos].is_routing_table_index_valid = 1;
-					qmi_rule_msg.filter_spec_list[pos].route_table_index = rule_table_v6->rules[cnt].rule.rt_tbl_idx;
-					qmi_rule_msg.filter_spec_list[pos].is_mux_id_valid = 1;
-					qmi_rule_msg.filter_spec_list[pos].mux_id = mux_id;
-					memcpy(&qmi_rule_msg.filter_spec_list[pos].filter_rule,
-						&rule_table_v6->rules[cnt].rule.eq_attrib,
-						sizeof(struct ipa_filter_rule_type_v01));
-					pos++;
-				}
-				else
-				{
-					IPACMERR(" QMI only support max %d rules, current (%d)\n ",QMI_IPA_MAX_FILTERS_V01, pos);
-				}
-			}
-		}
-
-		ret = ioctl(fd_wwan_ioctl, WAN_IOC_ADD_FLT_RULE, &qmi_rule_msg);
-		if (ret != 0)
-		{
-			IPACMERR("Failed adding Filtering rule %p with ret %d\n ", &qmi_rule_msg, ret);
+			IPACMERR("The number of filtering rules exceed limit.\n");
 			close(fd_wwan_ioctl);
 			return false;
 		}
-	}
+		else
+		{
+			if (num_rules > 0)
+			{
+				qmi_rule_msg.filter_spec_list_valid = true;
+			}
+			else
+			{
+				qmi_rule_msg.filter_spec_list_valid = false;
+			}
+
+			qmi_rule_msg.filter_spec_list_len = num_rules;
+			qmi_rule_msg.source_pipe_index_valid = 0;
+
+			IPACMDBG_H("Get %d WAN DL filtering rules in total.\n", num_rules);
+
+			if(rule_table_v4 != NULL)
+			{
+				for(cnt = rule_table_v4->num_rules - 1; cnt >= 0; cnt--)
+				{
+					if (pos < QMI_IPA_MAX_FILTERS_V01)
+					{
+						qmi_rule_msg.filter_spec_list[pos].filter_spec_identifier = pos;
+						qmi_rule_msg.filter_spec_list[pos].ip_type = QMI_IPA_IP_TYPE_V4_V01;
+						qmi_rule_msg.filter_spec_list[pos].filter_action = GetQmiFilterAction(rule_table_v4->rules[cnt].rule.action);
+						qmi_rule_msg.filter_spec_list[pos].is_routing_table_index_valid = 1;
+						qmi_rule_msg.filter_spec_list[pos].route_table_index = rule_table_v4->rules[cnt].rule.rt_tbl_idx;
+						qmi_rule_msg.filter_spec_list[pos].is_mux_id_valid = 1;
+						qmi_rule_msg.filter_spec_list[pos].mux_id = mux_id;
+						memcpy(&qmi_rule_msg.filter_spec_list[pos].filter_rule,
+							&rule_table_v4->rules[cnt].rule.eq_attrib,
+							sizeof(struct ipa_filter_rule_type_v01));
+						pos++;
+					}
+					else
+					{
+						IPACMERR(" QMI only support max %d rules, current (%d)\n ",QMI_IPA_MAX_FILTERS_V01, pos);
+					}
+				}
+			}
+
+			if(rule_table_v6 != NULL)
+			{
+				for(cnt = rule_table_v6->num_rules - 1; cnt >= 0; cnt--)
+				{
+					if (pos < QMI_IPA_MAX_FILTERS_V01)
+					{
+						qmi_rule_msg.filter_spec_list[pos].filter_spec_identifier = pos;
+						qmi_rule_msg.filter_spec_list[pos].ip_type = QMI_IPA_IP_TYPE_V6_V01;
+						qmi_rule_msg.filter_spec_list[pos].filter_action = GetQmiFilterAction(rule_table_v6->rules[cnt].rule.action);
+						qmi_rule_msg.filter_spec_list[pos].is_routing_table_index_valid = 1;
+						qmi_rule_msg.filter_spec_list[pos].route_table_index = rule_table_v6->rules[cnt].rule.rt_tbl_idx;
+						qmi_rule_msg.filter_spec_list[pos].is_mux_id_valid = 1;
+						qmi_rule_msg.filter_spec_list[pos].mux_id = mux_id;
+						memcpy(&qmi_rule_msg.filter_spec_list[pos].filter_rule,
+							&rule_table_v6->rules[cnt].rule.eq_attrib,
+							sizeof(struct ipa_filter_rule_type_v01));
+						pos++;
+					}
+					else
+					{
+						IPACMERR(" QMI only support max %d rules, current (%d)\n ",QMI_IPA_MAX_FILTERS_V01, pos);
+					}
+				}
+			}
+
+			ret = ioctl(fd_wwan_ioctl, WAN_IOC_ADD_FLT_RULE, &qmi_rule_msg);
+			if (ret != 0)
+			{
+				IPACMERR("Failed adding Filtering rule %p with ret %d\n ", &qmi_rule_msg, ret);
+				close(fd_wwan_ioctl);
+				return false;
+			}
+		}
 	/* if it is IPA v3, use new QMI format */
-#else
-	if(num_rules > QMI_IPA_MAX_FILTERS_EX_V01)
-	{
-		IPACMERR("The number of filtering rules exceed limit.\n");
-		close(fd_wwan_ioctl);
-		return false;
 	}
 	else
 	{
-		memset(&qmi_rule_ex_msg, 0, sizeof(qmi_rule_ex_msg));
-
-		if (num_rules > 0)
+		if(num_rules > QMI_IPA_MAX_FILTERS_EX_V01)
 		{
-			qmi_rule_ex_msg.filter_spec_ex_list_valid = true;
-		}
-		else
-		{
-			qmi_rule_ex_msg.filter_spec_ex_list_valid = false;
-		}
-		qmi_rule_ex_msg.filter_spec_ex_list_len = num_rules;
-		qmi_rule_ex_msg.source_pipe_index_valid = 0;
-
-		IPACMDBG_H("Get %d WAN DL filtering rules in total.\n", num_rules);
-
-		if(rule_table_v4 != NULL)
-		{
-			for(cnt = rule_table_v4->num_rules - 1; cnt >= 0; cnt--)
-			{
-				if (pos < QMI_IPA_MAX_FILTERS_EX_V01)
-				{
-					qmi_rule_ex_msg.filter_spec_ex_list[pos].ip_type = QMI_IPA_IP_TYPE_V4_V01;
-					qmi_rule_ex_msg.filter_spec_ex_list[pos].filter_action = GetQmiFilterAction(rule_table_v4->rules[cnt].rule.action);
-					qmi_rule_ex_msg.filter_spec_ex_list[pos].is_routing_table_index_valid = 1;
-					qmi_rule_ex_msg.filter_spec_ex_list[pos].route_table_index = rule_table_v4->rules[cnt].rule.rt_tbl_idx;
-					qmi_rule_ex_msg.filter_spec_ex_list[pos].is_mux_id_valid = 1;
-					qmi_rule_ex_msg.filter_spec_ex_list[pos].mux_id = mux_id;
-					qmi_rule_ex_msg.filter_spec_ex_list[pos].rule_id = rule_table_v4->rules[cnt].rule.rule_id;
-					qmi_rule_ex_msg.filter_spec_ex_list[pos].is_rule_hashable = rule_table_v4->rules[cnt].rule.hashable;
-					memcpy(&qmi_rule_ex_msg.filter_spec_ex_list[pos].filter_rule,
-						&rule_table_v4->rules[cnt].rule.eq_attrib,
-						sizeof(struct ipa_filter_rule_type_v01));
-
-					pos++;
-				}
-				else
-				{
-					IPACMERR(" QMI only support max %d rules, current (%d)\n ",QMI_IPA_MAX_FILTERS_EX_V01, pos);
-				}
-			}
-		}
-
-		if(rule_table_v6 != NULL)
-		{
-			for(cnt = rule_table_v6->num_rules - 1; cnt >= 0; cnt--)
-			{
-				if (pos < QMI_IPA_MAX_FILTERS_EX_V01)
-				{
-					qmi_rule_ex_msg.filter_spec_ex_list[pos].ip_type = QMI_IPA_IP_TYPE_V6_V01;
-					qmi_rule_ex_msg.filter_spec_ex_list[pos].filter_action = GetQmiFilterAction(rule_table_v6->rules[cnt].rule.action);
-					qmi_rule_ex_msg.filter_spec_ex_list[pos].is_routing_table_index_valid = 1;
-					qmi_rule_ex_msg.filter_spec_ex_list[pos].route_table_index = rule_table_v6->rules[cnt].rule.rt_tbl_idx;
-					qmi_rule_ex_msg.filter_spec_ex_list[pos].is_mux_id_valid = 1;
-					qmi_rule_ex_msg.filter_spec_ex_list[pos].mux_id = mux_id;
-					qmi_rule_ex_msg.filter_spec_ex_list[pos].rule_id = rule_table_v6->rules[cnt].rule.rule_id;
-					qmi_rule_ex_msg.filter_spec_ex_list[pos].is_rule_hashable = rule_table_v6->rules[cnt].rule.hashable;
-					memcpy(&qmi_rule_ex_msg.filter_spec_ex_list[pos].filter_rule,
-						&rule_table_v6->rules[cnt].rule.eq_attrib,
-						sizeof(struct ipa_filter_rule_type_v01));
-
-					pos++;
-				}
-				else
-				{
-					IPACMERR(" QMI only support max %d rules, current (%d)\n ",QMI_IPA_MAX_FILTERS_EX_V01, pos);
-				}
-			}
-		}
-
-		ret = ioctl(fd_wwan_ioctl, WAN_IOC_ADD_FLT_RULE_EX, &qmi_rule_ex_msg);
-		if (ret != 0)
-		{
-			IPACMERR("Failed adding Filtering rule %pK with ret %d\n ", &qmi_rule_ex_msg, ret);
+			IPACMERR("The number of filtering rules exceed limit.\n");
 			close(fd_wwan_ioctl);
 			return false;
 		}
+		else
+		{
+			memset(&qmi_rule_ex_msg, 0, sizeof(qmi_rule_ex_msg));
+
+			if (num_rules > 0)
+			{
+				qmi_rule_ex_msg.filter_spec_ex_list_valid = true;
+			}
+			else
+			{
+				qmi_rule_ex_msg.filter_spec_ex_list_valid = false;
+			}
+			qmi_rule_ex_msg.filter_spec_ex_list_len = num_rules;
+			qmi_rule_ex_msg.source_pipe_index_valid = 0;
+
+			IPACMDBG_H("Get %d WAN DL filtering rules in total.\n", num_rules);
+
+			if(rule_table_v4 != NULL)
+			{
+				for(cnt = rule_table_v4->num_rules - 1; cnt >= 0; cnt--)
+				{
+					if (pos < QMI_IPA_MAX_FILTERS_EX_V01)
+					{
+						qmi_rule_ex_msg.filter_spec_ex_list[pos].ip_type = QMI_IPA_IP_TYPE_V4_V01;
+						qmi_rule_ex_msg.filter_spec_ex_list[pos].filter_action = GetQmiFilterAction(rule_table_v4->rules[cnt].rule.action);
+						qmi_rule_ex_msg.filter_spec_ex_list[pos].is_routing_table_index_valid = 1;
+						qmi_rule_ex_msg.filter_spec_ex_list[pos].route_table_index = rule_table_v4->rules[cnt].rule.rt_tbl_idx;
+						qmi_rule_ex_msg.filter_spec_ex_list[pos].is_mux_id_valid = 1;
+						qmi_rule_ex_msg.filter_spec_ex_list[pos].mux_id = mux_id;
+						qmi_rule_ex_msg.filter_spec_ex_list[pos].rule_id = rule_table_v4->rules[cnt].rule.rule_id;
+						qmi_rule_ex_msg.filter_spec_ex_list[pos].is_rule_hashable = rule_table_v4->rules[cnt].rule.hashable;
+						memcpy(&qmi_rule_ex_msg.filter_spec_ex_list[pos].filter_rule,
+							&rule_table_v4->rules[cnt].rule.eq_attrib,
+							sizeof(struct ipa_filter_rule_type_v01));
+
+						pos++;
+					}
+					else
+					{
+						IPACMERR(" QMI only support max %d rules, current (%d)\n ",QMI_IPA_MAX_FILTERS_EX_V01, pos);
+					}
+				}
+			}
+
+			if(rule_table_v6 != NULL)
+			{
+				for(cnt = rule_table_v6->num_rules - 1; cnt >= 0; cnt--)
+				{
+					if (pos < QMI_IPA_MAX_FILTERS_EX_V01)
+					{
+						qmi_rule_ex_msg.filter_spec_ex_list[pos].ip_type = QMI_IPA_IP_TYPE_V6_V01;
+						qmi_rule_ex_msg.filter_spec_ex_list[pos].filter_action = GetQmiFilterAction(rule_table_v6->rules[cnt].rule.action);
+						qmi_rule_ex_msg.filter_spec_ex_list[pos].is_routing_table_index_valid = 1;
+						qmi_rule_ex_msg.filter_spec_ex_list[pos].route_table_index = rule_table_v6->rules[cnt].rule.rt_tbl_idx;
+						qmi_rule_ex_msg.filter_spec_ex_list[pos].is_mux_id_valid = 1;
+						qmi_rule_ex_msg.filter_spec_ex_list[pos].mux_id = mux_id;
+						qmi_rule_ex_msg.filter_spec_ex_list[pos].rule_id = rule_table_v6->rules[cnt].rule.rule_id;
+						qmi_rule_ex_msg.filter_spec_ex_list[pos].is_rule_hashable = rule_table_v6->rules[cnt].rule.hashable;
+						memcpy(&qmi_rule_ex_msg.filter_spec_ex_list[pos].filter_rule,
+							&rule_table_v6->rules[cnt].rule.eq_attrib,
+							sizeof(struct ipa_filter_rule_type_v01));
+
+						pos++;
+					}
+					else
+					{
+						IPACMERR(" QMI only support max %d rules, current (%d)\n ",QMI_IPA_MAX_FILTERS_EX_V01, pos);
+					}
+				}
+			}
+
+			ret = ioctl(fd_wwan_ioctl, WAN_IOC_ADD_FLT_RULE_EX, &qmi_rule_ex_msg);
+			if (ret != 0)
+			{
+				IPACMERR("Failed adding Filtering rule %pK with ret %d\n ", &qmi_rule_ex_msg, ret);
+				close(fd_wwan_ioctl);
+				return false;
+			}
+		}
 	}
-#endif
 
 	close(fd_wwan_ioctl);
 	return true;
 }
 
+bool IPACM_Filtering::AddOffloadFilteringRule(struct ipa_ioc_add_flt_rule *flt_rule_tbl, uint8_t mux_id, uint8_t default_path)
+{
+#ifdef WAN_IOCTL_ADD_OFFLOAD_CONNECTION
+	int ret = 0, cnt, pos = 0;
+	ipa_add_offload_connection_req_msg_v01 qmi_add_msg;
+	int fd_wwan_ioctl = open(WWAN_QMI_IOCTL_DEVICE_NAME, O_RDWR);
+	if(fd_wwan_ioctl < 0)
+	{
+		IPACMERR("Failed to open %s.\n",WWAN_QMI_IOCTL_DEVICE_NAME);
+		return false;
+	}
+
+	if(flt_rule_tbl == NULL)
+	{
+		if(mux_id ==0)
+		{
+			IPACMERR("Invalid add_offload_req muxd: (%d)\n", mux_id);
+			close(fd_wwan_ioctl);
+			return false;
+		}
+#ifdef QMI_IPA_MAX_FILTERS_EX2_V01
+		/* used for sending mux_id info to modem for UL sky*/
+		IPACMDBG_H("sending mux_id info (%d) to modem for UL\n", mux_id);
+		memset(&qmi_add_msg, 0, sizeof(qmi_add_msg));
+		qmi_add_msg.embedded_call_mux_id_valid = true;
+		qmi_add_msg.embedded_call_mux_id = mux_id;
+		ret = ioctl(fd_wwan_ioctl, WAN_IOC_ADD_OFFLOAD_CONNECTION, &qmi_add_msg);
+		if (ret != 0)
+		{
+			IPACMERR("Failed sending WAN_IOC_ADD_OFFLOAD_CONNECTION with ret %d\n ", ret);
+			close(fd_wwan_ioctl);
+			return false;
+		}
+#endif
+		close(fd_wwan_ioctl);
+		return true;
+	}
+	/* check Max offload connections */
+	if (total_num_offload_rules + flt_rule_tbl->num_rules > QMI_IPA_MAX_FILTERS_V01)
+	{
+		IPACMERR("(%d) add_offload req with curent(%d), exceed max (%d).\n",
+		flt_rule_tbl->num_rules, total_num_offload_rules,
+		QMI_IPA_MAX_FILTERS_V01);
+		close(fd_wwan_ioctl);
+		return false;
+	}
+	else
+	{
+		memset(&qmi_add_msg, 0, sizeof(qmi_add_msg));
+
+		if (flt_rule_tbl->num_rules > 0)
+		{
+			qmi_add_msg.filter_spec_ex2_list_valid = true;
+		}
+		else
+		{
+			IPACMDBG_H("Get %d offload-req\n", flt_rule_tbl->num_rules);
+			close(fd_wwan_ioctl);
+			return true;
+		}
+		qmi_add_msg.filter_spec_ex2_list_len = flt_rule_tbl->num_rules;
+
+		/* check if we want to take default MHI path */
+		if (default_path)
+		{
+			qmi_add_msg.default_mhi_path_valid = true;
+			qmi_add_msg.default_mhi_path = true;
+		}
+
+		IPACMDBG_H("passing %d offload req to modem. default %d\n", flt_rule_tbl->num_rules, qmi_add_msg.default_mhi_path);
+
+		if(flt_rule_tbl != NULL)
+		{
+			for(cnt = flt_rule_tbl->num_rules - 1; cnt >= 0; cnt--)
+			{
+				if (pos < QMI_IPA_MAX_FILTERS_V01)
+				{
+					if (flt_rule_tbl->ip == IPA_IP_v4)
+					{
+						qmi_add_msg.filter_spec_ex2_list[pos].ip_type = QMI_IPA_IP_TYPE_V4_V01;
+					} else if (flt_rule_tbl->ip == IPA_IP_v6) {
+						qmi_add_msg.filter_spec_ex2_list[pos].ip_type = QMI_IPA_IP_TYPE_V6_V01;
+					} else {
+						IPACMDBG_H("invalid ip-type %d\n", flt_rule_tbl->ip);
+						close(fd_wwan_ioctl);
+						return true;
+					}
+
+					qmi_add_msg.filter_spec_ex2_list[pos].filter_action = GetQmiFilterAction(flt_rule_tbl->rules[cnt].rule.action);
+					qmi_add_msg.filter_spec_ex2_list[pos].is_mux_id_valid = 1;
+					qmi_add_msg.filter_spec_ex2_list[pos].mux_id = mux_id;
+					/* assign the rule-id */
+					flt_rule_tbl->rules[cnt].flt_rule_hdl = IPA_PCIE_MODEM_RULE_ID_START + pcie_modem_rule_id;
+					qmi_add_msg.filter_spec_ex2_list[pos].rule_id = flt_rule_tbl->rules[cnt].flt_rule_hdl;
+					qmi_add_msg.filter_spec_ex2_list[pos].is_rule_hashable = flt_rule_tbl->rules[cnt].rule.hashable;
+					memcpy(&qmi_add_msg.filter_spec_ex2_list[pos].filter_rule,
+						&flt_rule_tbl->rules[cnt].rule.eq_attrib,
+						sizeof(struct ipa_filter_rule_type_v01));
+					IPACMDBG_H("mux-id %d, hashable %d\n", qmi_add_msg.filter_spec_ex2_list[pos].mux_id, qmi_add_msg.filter_spec_ex2_list[pos].is_rule_hashable);
+					pos++;
+					pcie_modem_rule_id = (pcie_modem_rule_id + 1)%100;
+				}
+				else
+				{
+					IPACMERR(" QMI only support max %d rules, current (%d)\n ",QMI_IPA_MAX_FILTERS_V01, pos);
+				}
+			}
+		}
+
+		ret = ioctl(fd_wwan_ioctl, WAN_IOC_ADD_OFFLOAD_CONNECTION, &qmi_add_msg);
+		if (ret != 0)
+		{
+			IPACMERR("Failed sending WAN_IOC_ADD_OFFLOAD_CONNECTION with ret %d\n ", ret);
+			close(fd_wwan_ioctl);
+			return false;
+		}
+	}
+	/* update total_num_offload_rules */
+	total_num_offload_rules += flt_rule_tbl->num_rules;
+	IPACMDBG_H("total_num_offload_rules %d \n", total_num_offload_rules);
+	close(fd_wwan_ioctl);
+	return true;
+#else
+	if(flt_rule_tbl != NULL)
+	{
+		IPACMERR("Not support (%d) AddOffloadFilteringRule with mux-id (%d) and default path = %d\n", flt_rule_tbl->num_rules, mux_id, default_path);
+	}
+	return false;
+#endif
+}
+
+bool IPACM_Filtering::DelOffloadFilteringRule(struct ipa_ioc_del_flt_rule const *flt_rule_tbl)
+{
+#ifdef WAN_IOCTL_ADD_OFFLOAD_CONNECTION
+	bool result = true;
+	int ret = 0, cnt, pos = 0;
+	ipa_remove_offload_connection_req_msg_v01 qmi_del_msg;
+	int fd_wwan_ioctl = open(WWAN_QMI_IOCTL_DEVICE_NAME, O_RDWR);
+
+	if(fd_wwan_ioctl < 0)
+	{
+		IPACMERR("Failed to open %s.\n",WWAN_QMI_IOCTL_DEVICE_NAME);
+		return false;
+	}
+
+	if(flt_rule_tbl == NULL)
+	{
+		IPACMERR("Invalid add_offload_req\n");
+		result =  false;
+		goto fail;
+	}
+
+	/* check # of offload connections */
+	if (flt_rule_tbl->num_hdls > total_num_offload_rules) {
+		IPACMERR("(%d) del_offload req , exceed curent(%d)\n",
+		flt_rule_tbl->num_hdls, total_num_offload_rules);
+		result =  false;
+		goto fail;
+	}
+	else
+	{
+		memset(&qmi_del_msg, 0, sizeof(qmi_del_msg));
+
+		if (flt_rule_tbl->num_hdls > 0)
+		{
+			qmi_del_msg.filter_handle_list_valid = true;
+		}
+		else
+		{
+			IPACMERR("Get %d offload-req\n", flt_rule_tbl->num_hdls);
+			goto fail;
+		}
+		qmi_del_msg.filter_handle_list_len = flt_rule_tbl->num_hdls;
+
+		IPACMDBG_H("passing %d offload req to modem.\n", flt_rule_tbl->num_hdls);
+
+		if(flt_rule_tbl != NULL)
+		{
+			for(cnt = flt_rule_tbl->num_hdls - 1; cnt >= 0; cnt--)
+			{
+				if (pos < QMI_IPA_MAX_FILTERS_V01)
+				{
+					/* passing rule-id to wan-driver */
+					qmi_del_msg.filter_handle_list[pos].filter_spec_identifier = flt_rule_tbl->hdl[cnt].hdl;
+					pos++;
+				}
+				else
+				{
+					IPACMERR(" QMI only support max %d rules, current (%d)\n ",QMI_IPA_MAX_FILTERS_V01, pos);
+					result =  false;
+					goto fail;
+				}
+			}
+		}
+
+		ret = ioctl(fd_wwan_ioctl, WAN_IOC_RMV_OFFLOAD_CONNECTION, &qmi_del_msg);
+		if (ret != 0)
+		{
+			IPACMERR("Failed deleting Filtering rule %pK with ret %d\n ", &qmi_del_msg, ret);
+			result =  false;
+			goto fail;
+		}
+	}
+	/* update total_num_offload_rules */
+	total_num_offload_rules -= flt_rule_tbl->num_hdls;
+	IPACMDBG_H("total_num_offload_rules %d \n", total_num_offload_rules);
+
+fail:
+	close(fd_wwan_ioctl);
+	return result;
+#else
+	if(flt_rule_tbl != NULL)
+	{
+		IPACMERR("Not support (%d) DelOffloadFilteringRule\n", flt_rule_tbl->num_hdls);
+	}
+	return false;
+#endif
+}
+
 bool IPACM_Filtering::SendFilteringRuleIndex(struct ipa_fltr_installed_notif_req_msg_v01* table)
 {
 	int ret = 0;
diff --git a/ipacm/src/IPACM_Iface.cpp b/ipacm/src/IPACM_Iface.cpp
index 512846f..0d4f54f 100644
--- a/ipacm/src/IPACM_Iface.cpp
+++ b/ipacm/src/IPACM_Iface.cpp
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
+Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
@@ -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));
 
@@ -133,9 +133,8 @@
 	flt_rule_entry.flt_rule_hdl = -1;
 	flt_rule_entry.status = -1;
 	flt_rule_entry.rule.action = IPA_PASS_TO_EXCEPTION;
-#ifdef FEATURE_IPA_V3
-	flt_rule_entry.rule.hashable = true;
-#endif
+	if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+		flt_rule_entry.rule.hashable = true;
 	memcpy(&flt_rule_entry.rule.attrib,
 				 &rx_prop->rx[0].attrib,
 				 sizeof(flt_rule_entry.rule.attrib));
@@ -143,62 +142,48 @@
 	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");
@@ -211,32 +196,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)
 	{
@@ -250,8 +282,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)
@@ -260,8 +327,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)
@@ -270,43 +363,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;
 }
 
@@ -633,6 +698,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";
@@ -705,10 +771,11 @@
 		flt_rule_entry.flt_rule_hdl = -1;
 		flt_rule_entry.status = -1;
 		flt_rule_entry.rule.action = IPA_PASS_TO_EXCEPTION;
-#ifdef FEATURE_IPA_V3
-		flt_rule_entry.at_rear = false;
-		flt_rule_entry.rule.hashable = false;
-#endif
+		if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+		{
+			flt_rule_entry.at_rear = false;
+			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,
@@ -724,22 +791,42 @@
 		flt_rule_entry.rule.attrib.attrib_mask |= IPA_FLT_DST_ADDR;
 		flt_rule_entry.rule.attrib.u.v4.dst_addr_mask = 0xF0000000;
 		flt_rule_entry.rule.attrib.u.v4.dst_addr = 0xE0000000;
-#ifdef FEATURE_IPA_V3
-		flt_rule_entry.at_rear = true;
-		flt_rule_entry.rule.hashable = true;
-#endif
+		if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+		{
+			flt_rule_entry.at_rear = true;
+			flt_rule_entry.rule.hashable = true;
+		}
 		memcpy(&(m_pFilteringTable->rules[1]), &flt_rule_entry, sizeof(struct ipa_flt_rule_add));
 
 		/* Configuring Broadcast Filtering Rule */
 		flt_rule_entry.rule.attrib.u.v4.dst_addr_mask = 0xFFFFFFFF;
 		flt_rule_entry.rule.attrib.u.v4.dst_addr = 0xFFFFFFFF;
-#ifdef FEATURE_IPA_V3
-		flt_rule_entry.at_rear = true;
-		flt_rule_entry.rule.hashable = true;
-#endif
+		if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+		{
+			flt_rule_entry.at_rear = true;
+			flt_rule_entry.rule.hashable = true;
+		}
 		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;
@@ -801,10 +888,11 @@
 		flt_rule_entry.rule.attrib.u.v6.dst_addr[1] = 0x00000000;
 		flt_rule_entry.rule.attrib.u.v6.dst_addr[2] = 0x00000000;
 		flt_rule_entry.rule.attrib.u.v6.dst_addr[3] = 0X00000000;
-#ifdef FEATURE_IPA_V3
-		flt_rule_entry.at_rear = true;
-		flt_rule_entry.rule.hashable = true;
-#endif
+		if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+		{
+			flt_rule_entry.at_rear = true;
+			flt_rule_entry.rule.hashable = true;
+		}
 		memcpy(&(m_pFilteringTable->rules[0]), &flt_rule_entry, sizeof(struct ipa_flt_rule_add));
 
 		/* Configuring fe80::/10 Link-Scoped Unicast Filtering Rule */
@@ -816,10 +904,11 @@
 		flt_rule_entry.rule.attrib.u.v6.dst_addr[1] = 0x00000000;
 		flt_rule_entry.rule.attrib.u.v6.dst_addr[2] = 0x00000000;
 		flt_rule_entry.rule.attrib.u.v6.dst_addr[3] = 0X00000000;
-#ifdef FEATURE_IPA_V3
-		flt_rule_entry.at_rear = true;
-		flt_rule_entry.rule.hashable = true;
-#endif
+		if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+		{
+			flt_rule_entry.at_rear = true;
+			flt_rule_entry.rule.hashable = true;
+		}
 		memcpy(&(m_pFilteringTable->rules[1]), &flt_rule_entry, sizeof(struct ipa_flt_rule_add));
 
 		/* Configuring fec0::/10 Reserved by IETF Filtering Rule */
@@ -831,10 +920,11 @@
 		flt_rule_entry.rule.attrib.u.v6.dst_addr[1] = 0x00000000;
 		flt_rule_entry.rule.attrib.u.v6.dst_addr[2] = 0x00000000;
 		flt_rule_entry.rule.attrib.u.v6.dst_addr[3] = 0X00000000;
-#ifdef FEATURE_IPA_V3
-		flt_rule_entry.at_rear = true;
-		flt_rule_entry.rule.hashable = true;
-#endif
+		if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+		{
+			flt_rule_entry.at_rear = true;
+			flt_rule_entry.rule.hashable = true;
+		}
 		memcpy(&(m_pFilteringTable->rules[2]), &flt_rule_entry, sizeof(struct ipa_flt_rule_add));
 
 		/* Configuring fd00::/8 Unique Local Ipv6 Address */
@@ -846,22 +936,20 @@
 		flt_rule_entry.rule.attrib.u.v6.dst_addr[1] = 0x00000000;
 		flt_rule_entry.rule.attrib.u.v6.dst_addr[2] = 0x00000000;
 		flt_rule_entry.rule.attrib.u.v6.dst_addr[3] = 0X00000000;
-#ifdef FEATURE_IPA_V3
-		flt_rule_entry.at_rear = true;
-		flt_rule_entry.rule.hashable = true;
-#endif
+		if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+		{
+			flt_rule_entry.at_rear = true;
+			flt_rule_entry.rule.hashable = true;
+		}
 		memcpy(&(m_pFilteringTable->rules[3]), &flt_rule_entry, sizeof(struct ipa_flt_rule_add));
 
 #ifdef FEATURE_IPA_ANDROID
-		/* Add the ipv6 tcp fragment filtering rule. */
+		/* Add the ipv6 tcp/udp fragment filtering rule for MTU */
 
-		IPACMDBG_H("Adding IPv6 TCP fragment filter rule\n");
+		IPACMDBG_H("Adding IPv6 fragment filter rule\n");
 
 		flt_rule_entry.rule.attrib.attrib_mask &= ~((uint32_t)IPA_FLT_DST_ADDR);
 
-		flt_rule_entry.rule.attrib.attrib_mask |= IPA_FLT_NEXT_HDR;
-		flt_rule_entry.rule.attrib.u.v6.next_hdr = IPACM_FIREWALL_IPPROTO_TCP;
-
 		flt_rule_entry.rule.attrib.attrib_mask |= IPA_FLT_FRAGMENT;
 
 		memcpy(&(m_pFilteringTable->rules[4]), &flt_rule_entry,
@@ -882,11 +970,10 @@
 
 		if(rx_prop->rx[0].attrib.attrib_mask & IPA_FLT_META_DATA)
 		{
-#ifdef FEATURE_IPA_V3
-			flt_rule_entry.rule.eq_attrib.rule_eq_bitmap |= (1<<9);
-#else
-			flt_rule_entry.rule.eq_attrib.rule_eq_bitmap |= (1<<14);
-#endif
+			if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+				flt_rule_entry.rule.eq_attrib.rule_eq_bitmap |= (1<<9);
+			else
+				flt_rule_entry.rule.eq_attrib.rule_eq_bitmap |= (1<<14);
 			flt_rule_entry.rule.eq_attrib.metadata_meq32_present = 1;
 			flt_rule_entry.rule.eq_attrib.metadata_meq32.offset = 0;
 			flt_rule_entry.rule.eq_attrib.metadata_meq32.value = rx_prop->rx[0].attrib.meta_data;
@@ -897,11 +984,10 @@
 		flt_rule_entry.rule.eq_attrib.protocol_eq_present = 1;
 		flt_rule_entry.rule.eq_attrib.protocol_eq = IPACM_FIREWALL_IPPROTO_TCP;
 
-#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
+		if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+			flt_rule_entry.rule.eq_attrib.rule_eq_bitmap |= (1<<7);
+		else
+			flt_rule_entry.rule.eq_attrib.rule_eq_bitmap |= (1<<8);
 		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;
 
@@ -921,7 +1007,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;
@@ -1045,6 +1144,58 @@
 	return;
 }
 
+void IPACM_Iface::change_to_network_order(ipa_ip_type iptype, ipa_rule_attrib* attrib)
+{
+	if(attrib == NULL)
+	{
+		IPACMERR("Attribute pointer is NULL.\n");
+		return;
+	}
+
+	if(iptype == IPA_IP_v6)
+	{
+		int i;
+		for(i=0; i<4; i++)
+		{
+			attrib->u.v6.src_addr[i] = htonl(attrib->u.v6.src_addr[i]);
+			attrib->u.v6.src_addr_mask[i] = htonl(attrib->u.v6.src_addr_mask[i]);
+			attrib->u.v6.dst_addr[i] = htonl(attrib->u.v6.dst_addr[i]);
+			attrib->u.v6.dst_addr_mask[i] = htonl(attrib->u.v6.dst_addr_mask[i]);
+		}
+	}
+	else
+	{
+		IPACMDBG_H("IP type is not IPv6, do nothing: %d\n", iptype);
+	}
+
+	return;
+}
+
+bool IPACM_Iface::is_global_ipv6_addr(uint32_t* ipv6_addr)
+{
+	uint32_t ipv6_link_local_prefix, ipv6_link_local_prefix_mask;
+	ipv6_link_local_prefix = 0xFE800000;
+	ipv6_link_local_prefix_mask = 0xFFC00000;
+
+	if(ipv6_addr == NULL)
+	{
+		IPACMERR("IPv6 address is empty.\n");
+		return false;
+	}
+	IPACMDBG_H("Get ipv6 address with first word 0x%08x.\n", ipv6_addr[0]);
+
+	if((ipv6_addr[0] & ipv6_link_local_prefix_mask) == (ipv6_link_local_prefix & ipv6_link_local_prefix_mask))
+	{
+		IPACMDBG_H("This IPv6 address is link local.\n");
+		return false;
+	}
+	else
+	{
+		IPACMDBG_H("This IPv6 address is not link local.\n");
+		return true;
+	}
+}
+
 void IPACM_Iface::delete_iface(void)
 {
 	IPACMDBG_H("netdev (%s):ipa_index (%d) instance close \n",
diff --git a/ipacm/src/IPACM_IfaceManager.cpp b/ipacm/src/IPACM_IfaceManager.cpp
index 1c7dfc4..2857f39 100644
--- a/ipacm/src/IPACM_IfaceManager.cpp
+++ b/ipacm/src/IPACM_IfaceManager.cpp
@@ -406,9 +406,8 @@
 				IPACM_EvtDispatcher::registr(IPA_HANDLE_WAN_DOWN_V6, wl);
 #endif
 				IPACM_EvtDispatcher::registr(IPA_PRIVATE_SUBNET_CHANGE_EVENT, wl); 	// register for IPA_PRIVATE_SUBNET_CHANGE_EVENT event
-#ifdef FEATURE_ETH_BRIDGE_LE
-				IPACM_EvtDispatcher::registr(IPA_CFG_CHANGE_EVENT, wl);
-#endif
+				if (IPACM_Iface::ipacmcfg->isEthBridgingSupported())
+					IPACM_EvtDispatcher::registr(IPA_CFG_CHANGE_EVENT, wl);
 				IPACM_EvtDispatcher::registr(IPA_CRADLE_WAN_MODE_SWITCH, wl);
 				IPACM_EvtDispatcher::registr(IPA_WLAN_LINK_DOWN_EVENT, wl);
 #ifndef FEATURE_IPA_ANDROID
@@ -484,6 +483,7 @@
 					}
 					else
 					{
+						IPACM_EvtDispatcher::registr(IPA_COALESCE_NOTICE, w);
 						IPACM_EvtDispatcher::registr(IPA_LINK_DOWN_EVENT, w);
 					}
 
diff --git a/ipacm/src/IPACM_Lan.cpp b/ipacm/src/IPACM_Lan.cpp
index 827839b..0a532fd 100644
--- a/ipacm/src/IPACM_Lan.cpp
+++ b/ipacm/src/IPACM_Lan.cpp
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
+Copyright (c) 2013-2020, The Linux Foundation. All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
@@ -129,8 +129,8 @@
 
 	memset(ipv4_icmp_flt_rule_hdl, 0, NUM_IPV4_ICMP_FLT_RULE * sizeof(uint32_t));
 
-	memset(private_fl_rule_hdl, 0, IPA_MAX_PRIVATE_SUBNET_ENTRIES * sizeof(uint32_t));
-	memset(ipv6_prefix_flt_rule_hdl, 0, NUM_IPV6_PREFIX_FLT_RULE * sizeof(uint32_t));
+	memset(private_fl_rule_hdl, 0, (IPA_MAX_PRIVATE_SUBNET_ENTRIES + IPA_MAX_MTU_ENTRIES) * sizeof(uint32_t));
+	memset(ipv6_prefix_flt_rule_hdl, 0, (NUM_IPV6_PREFIX_FLT_RULE + NUM_IPV6_PREFIX_MTU_RULE) * sizeof(uint32_t));
 	memset(ipv6_icmp_flt_rule_hdl, 0, NUM_IPV6_ICMP_FLT_RULE * sizeof(uint32_t));
 	memset(ipv6_prefix, 0, sizeof(ipv6_prefix));
 
@@ -184,9 +184,9 @@
 		handle_tethering_client(false, IPACM_CLIENT_MAX);
 #else
 		handle_tethering_client(false, IPACM_CLIENT_USB);
-#endif
+#endif // FEATURE_IPACM_HAL end
 	}
-#endif
+#endif // FEATURE_IPA_ANDROID end
 
 	memset(is_downstream_set, 0, sizeof(is_downstream_set));
 	memset(is_upstream_set, 0, sizeof(is_upstream_set));
@@ -195,9 +195,9 @@
 #ifdef FEATURE_IPACM_HAL
 
 		/* check if Upstream was set before as WIFI with RNDIS case */
-		if(ipa_if_cate == LAN_IF && IPACM_Wan::backhaul_is_sta_mode == true)  /* LTE */
+		if(ipa_if_cate == LAN_IF && IPACM_Wan::backhaul_mode == WLAN_WAN)  /* LTE */
 		{
-			IPACMDBG_H(" Skip the Upstream falg set on LAN instance (%d) with WIFI backhaul (%d)\n", ipa_if_cate, IPACM_Wan::backhaul_is_sta_mode ); /* RNDIS+WIFI not support on msm*/
+			IPACMDBG_H(" Skip the Upstream flag set on LAN instance (%d) with WIFI backhaul (%d)\n", ipa_if_cate, IPACM_Wan::backhaul_mode ); /* RNDIS+WIFI not support on msm*/
 			return;
 		}
 
@@ -213,7 +213,7 @@
 				IPACMDBG_H("Upstream was set previously for ipv6, change is_upstream_set flag\n");
 				is_upstream_set[IPA_IP_v6] = true;
 		}
-#endif
+#endif // FEATURE_IPACM_HAL end
 	return;
 }
 
@@ -342,20 +342,21 @@
 			}
 #endif
 
-#ifdef FEATURE_ETH_BRIDGE_LE
-			if(rx_prop != NULL)
+			if (IPACM_Iface::ipacmcfg->isEthBridgingSupported())
 			{
-				free(rx_prop);
+				if(rx_prop != NULL)
+				{
+					free(rx_prop);
+				}
+				if(tx_prop != NULL)
+				{
+					free(tx_prop);
+				}
+				if(iface_query != NULL)
+				{
+					free(iface_query);
+				}
 			}
-			if(tx_prop != NULL)
-			{
-				free(tx_prop);
-			}
-			if(iface_query != NULL)
-			{
-				free(iface_query);
-			}
-#endif
 			delete this;
 		}
 		break;
@@ -399,7 +400,7 @@
 					handle_private_subnet_android(data->iptype);
 #else
 					handle_private_subnet(data->iptype);
-#endif
+#endif // FEATURE_IPA_ANDROID end
 				}
 				else
 				{
@@ -419,14 +420,14 @@
 						handle_private_subnet_android(data->iptype);
 #else
 						handle_private_subnet(data->iptype);
-#endif
+#endif // FEATURE_IPA_ANDROID end
 
 #ifndef FEATURE_IPACM_HAL
 						if (IPACM_Wan::isWanUP(ipa_if_num))
 						{
 							if(data->iptype == IPA_IP_v4 || data->iptype == IPA_IP_MAX)
 							{
-								if(IPACM_Wan::backhaul_is_sta_mode == false)
+								if(IPACM_Wan::backhaul_mode == Q6_WAN)
 								{
 									ext_prop = IPACM_Iface::ipacmcfg->GetExtProp(IPA_IP_v4);
 									handle_wan_up_ex(ext_prop, IPA_IP_v4,
@@ -448,7 +449,7 @@
 							{
 								memcpy(ipv6_prefix, IPACM_Wan::backhaul_ipv6_prefix, sizeof(ipv6_prefix));
 								install_ipv6_prefix_flt_rule(IPACM_Wan::backhaul_ipv6_prefix);
-								if(IPACM_Wan::backhaul_is_sta_mode == false)
+								if(IPACM_Wan::backhaul_mode == Q6_WAN)
 								{
 									ext_prop = IPACM_Iface::ipacmcfg->GetExtProp(IPA_IP_v6);
 									handle_wan_up_ex(ext_prop, IPA_IP_v6, 0);
@@ -474,7 +475,7 @@
 							IPACMDBG_H("Upstream was set previously for ipv6, change is_upstream_set flag\n");
 							is_upstream_set[IPA_IP_v6] = true;
 						}
-#endif
+#endif //FEATURE_IPACM_HAL end
 						/* Post event to NAT */
 						if (data->iptype == IPA_IP_v4)
 						{
@@ -511,7 +512,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);
 					}
 
 				}
@@ -528,7 +529,7 @@
 			IPACMERR("No event data is found.\n");
 			return;
 		}
-		IPACMDBG_H("Backhaul is sta mode?%d, if_index_tether:%d tether_if_name:%s xlat_mux_id: %d\n", data_wan_tether->is_sta,
+		IPACMDBG_H("Backhaul is sta mode?%d, if_index_tether:%d tether_if_name:%s, xlat %d\n", data_wan_tether->backhaul_type,
 					data_wan_tether->if_index_tether,
 					IPACM_Iface::ipacmcfg->iface_table[data_wan_tether->if_index_tether].iface_name,
 					data_wan_tether->xlat_mux_id);
@@ -539,7 +540,7 @@
 			return;
 		}
 #else /* not offload rndis on WIFI mode on MSM targets */
-		if (data_wan_tether->is_sta)
+		if (data_wan_tether->backhaul_type == WLAN_WAN)
 		{
 			IPACMERR("Not support RNDIS offload on WIFI mode, dun install UL filter rules for WIFI mode\n");
 
@@ -577,7 +578,7 @@
 			} /* end of for loop */
 			return;
 		}
-#endif
+#endif // FEATURE_IPACM_HAL end
 		if (ip_type == IPA_IP_v4 || ip_type == IPA_IP_MAX)
 		{
 #ifdef FEATURE_IPACM_HAL
@@ -588,7 +589,7 @@
 				if (is_downstream_set[IPA_IP_v4] == true)
 				{
 					IPACMDBG_H("Downstream was set before, adding UL rules.\n");
-					if (data_wan_tether->is_sta == false)
+					if (data_wan_tether->backhaul_type == Q6_WAN)
 					{
 							ext_prop = IPACM_Iface::ipacmcfg->GetExtProp(IPA_IP_v4);
 							handle_wan_up_ex(ext_prop, IPA_IP_v4,
@@ -599,14 +600,14 @@
 				}
 			}
 #else
-			if (data_wan_tether->is_sta == false)
+			if (data_wan_tether->backhaul_type == Q6_WAN)
 			{
 					ext_prop = IPACM_Iface::ipacmcfg->GetExtProp(IPA_IP_v4);
 					handle_wan_up_ex(ext_prop, IPA_IP_v4, data_wan_tether->xlat_mux_id);
 			} else {
 					handle_wan_up(IPA_IP_v4);
 			}
-#endif
+#endif // FEATURE_IPACM_HAL end
 		}
 		break;
 
@@ -619,7 +620,7 @@
 			IPACMERR("No event data is found.\n");
 			return;
 		}
-		IPACMDBG_H("Backhaul is sta mode?%d, if_index_tether:%d tether_if_name:%s\n", data_wan_tether->is_sta,
+		IPACMDBG_H("Backhaul is sta mode?%d, if_index_tether:%d tether_if_name:%s\n", data_wan_tether->backhaul_type,
 					data_wan_tether->if_index_tether,
 					IPACM_Iface::ipacmcfg->iface_table[data_wan_tether->if_index_tether].iface_name);
 #ifndef FEATURE_IPACM_HAL
@@ -628,7 +629,7 @@
 			IPACMERR("IPA_HANDLE_WAN_UP_V6_TETHER tether_if(%d), not valid (%d) ignore\n", data_wan_tether->if_index_tether, ipa_if_num);
 			return;
 		}
-#endif
+#endif // FEATURE_IPACM_HAL end
 		if (ip_type == IPA_IP_v6 || ip_type == IPA_IP_MAX)
 		{
 #ifdef FEATURE_IPACM_HAL
@@ -642,7 +643,7 @@
 					IPACMDBG_H("Downstream was set before, adding UL rules.\n");
 					memcpy(ipv6_prefix, data_wan_tether->ipv6_prefix, sizeof(ipv6_prefix));
 					install_ipv6_prefix_flt_rule(data_wan_tether->ipv6_prefix);
-					if (data_wan_tether->is_sta == false)
+					if (data_wan_tether->backhaul_type == Q6_WAN)
 					{
 						ext_prop = IPACM_Iface::ipacmcfg->GetExtProp(IPA_IP_v6);
 						handle_wan_up_ex(ext_prop, IPA_IP_v6, 0);
@@ -654,14 +655,14 @@
 				}
 			}
 #else
-			if (data_wan_tether->is_sta == false)
+			if (data_wan_tether->backhaul_type == Q6_WAN)
 			{
 				ext_prop = IPACM_Iface::ipacmcfg->GetExtProp(IPA_IP_v6);
 				handle_wan_up_ex(ext_prop, IPA_IP_v6, 0);
 			} else {
 				handle_wan_up(IPA_IP_v6);
 			}
-#endif
+#endif // FEATURE_IPACM_HAL end
 		}
 		break;
 
@@ -673,7 +674,7 @@
 			IPACMERR("No event data is found.\n");
 			return;
 		}
-		IPACMDBG_H("Backhaul is sta mode?%d, if_index_tether:%d tether_if_name:%s\n", data_wan_tether->is_sta,
+		IPACMDBG_H("Backhaul is sta mode?%d, if_index_tether:%d tether_if_name:%s\n", data_wan_tether->backhaul_type,
 					data_wan_tether->if_index_tether,
 					IPACM_Iface::ipacmcfg->iface_table[data_wan_tether->if_index_tether].iface_name);
 #ifndef FEATURE_IPACM_HAL
@@ -682,7 +683,7 @@
 			IPACMERR("IPA_HANDLE_WAN_DOWN_TETHER tether_if(%d), not valid (%d) ignore\n", data_wan_tether->if_index_tether, ipa_if_num);
 			return;
 		}
-#endif
+#endif // FEATURE_IPACM_HAL end
 		if (ip_type == IPA_IP_v4 || ip_type == IPA_IP_MAX)
 		{
 #ifdef FEATURE_IPACM_HAL
@@ -693,12 +694,12 @@
 				if(is_downstream_set[IPA_IP_v4] == true)
 				{
 					IPACMDBG_H("Downstream was set before, deleting UL rules.\n");
-					handle_wan_down(data_wan_tether->is_sta);
+					handle_wan_down(data_wan_tether->backhaul_type);
 				}
 			}
 #else
-			handle_wan_down(data_wan_tether->is_sta);
-#endif
+			handle_wan_down(data_wan_tether->backhaul_type);
+#endif // FEATURE_IPACM_HAL end
 		}
 		break;
 
@@ -710,7 +711,7 @@
 			IPACMERR("No event data is found.\n");
 			return;
 		}
-		IPACMDBG_H("Backhaul is sta mode?%d, if_index_tether:%d tether_if_name:%s\n", data_wan_tether->is_sta,
+		IPACMDBG_H("Backhaul is sta mode?%d, if_index_tether:%d tether_if_name:%s\n", data_wan_tether->backhaul_type,
 					data_wan_tether->if_index_tether,
 					IPACM_Iface::ipacmcfg->iface_table[data_wan_tether->if_index_tether].iface_name);
 #ifndef FEATURE_IPACM_HAL
@@ -719,7 +720,7 @@
 			IPACMERR("IPA_HANDLE_WAN_DOWN_V6_TETHER tether_if(%d), not valid (%d) ignore\n", data_wan_tether->if_index_tether, ipa_if_num);
 			return;
 		}
-#endif
+#endif // FEATURE_IPACM_HAL end
 		if (ip_type == IPA_IP_v6 || ip_type == IPA_IP_MAX)
 		{
 #ifdef FEATURE_IPACM_HAL
@@ -732,14 +733,14 @@
 					IPACMDBG_H("Downstream was set before, deleting UL rules.\n");
 					/* reset usb-client ipv6 rt-rules */
 					handle_lan_client_reset_rt(IPA_IP_v6);
-					handle_wan_down_v6(data_wan_tether->is_sta);
+					handle_wan_down_v6(data_wan_tether->backhaul_type);
 				}
 			}
 #else
 			/* reset usb-client ipv6 rt-rules */
 			handle_lan_client_reset_rt(IPA_IP_v6);
-			handle_wan_down_v6(data_wan_tether->is_sta);
-#endif
+			handle_wan_down_v6(data_wan_tether->backhaul_type);
+#endif // FEATURE_IPACM_HAL end
 		}
 		break;
 
@@ -750,6 +751,25 @@
 		if (ipa_interface_index == ipa_if_num)
 		{
 			IPACMDBG_H("Received IPA_DOWNSTREAM_ADD event.\n");
+#ifdef FEATURE_IPA_ANDROID
+			if (IPACM_Wan::isXlat() && (data->prefix.iptype == IPA_IP_v4))
+			{
+				/* indicate v4-offload */
+				IPACM_OffloadManager::num_offload_v4_tethered_iface++;
+				IPACMDBG_H("in xlat: update num_offload_v4_tethered_iface %d\n", IPACM_OffloadManager::num_offload_v4_tethered_iface);
+
+				/* xlat not support for 2st tethered iface */
+				if (IPACM_OffloadManager::num_offload_v4_tethered_iface > 1)
+				{
+					IPACMDBG_H("Not support 2st downstream iface %s for xlat, cur: %d\n", dev_name,
+						IPACM_OffloadManager::num_offload_v4_tethered_iface);
+					return;
+				}
+			}
+
+			IPACMDBG_H(" support downstream iface %s, cur %d\n", dev_name,
+				IPACM_OffloadManager::num_offload_v4_tethered_iface);
+#endif
 			if (data->prefix.iptype < IPA_IP_MAX && is_downstream_set[data->prefix.iptype] == false)
 			{
 				IPACMDBG_H("Add downstream for IP iptype %d\n", data->prefix.iptype);
@@ -771,7 +791,7 @@
 							install_ipv6_prefix_flt_rule(ipv6_prefix);
 						}
 
-						if (IPACM_Wan::backhaul_is_sta_mode == false) /* LTE */
+						if (IPACM_Wan::backhaul_mode == Q6_WAN) /* LTE */
 						{
 							ext_prop = IPACM_Iface::ipacmcfg->GetExtProp(data->prefix.iptype);
 							if (data->prefix.iptype == IPA_IP_v4)
@@ -812,10 +832,10 @@
 					IPACMDBG_H("Upstream was set before, deleting UL rules.\n");
 					if (data->prefix.iptype == IPA_IP_v4)
 					{
-						handle_wan_down(IPACM_Wan::backhaul_is_sta_mode); /* LTE STA */
+						handle_wan_down(IPACM_Wan::backhaul_mode); /* LTE STA */
 					} else {
 						handle_lan_client_reset_rt(IPA_IP_v6);
-						handle_wan_down_v6(IPACM_Wan::backhaul_is_sta_mode); /* LTE STA */
+						handle_wan_down_v6(IPACM_Wan::backhaul_mode); /* LTE STA */
 					}
 				}
 			}
@@ -823,7 +843,7 @@
 		break;
 	}
 
-#else
+#else // above Andorid
 	case IPA_HANDLE_WAN_UP:
 		IPACMDBG_H("Received IPA_HANDLE_WAN_UP event\n");
 
@@ -833,10 +853,10 @@
 			IPACMERR("No event data is found.\n");
 			return;
 		}
-		IPACMDBG_H("Backhaul is sta mode?%d\n", data_wan->is_sta);
+		IPACMDBG_H("Backhaul is sta mode?%d\n", data_wan->backhaul_type);
 		if (ip_type == IPA_IP_v4 || ip_type == IPA_IP_MAX)
 		{
-		if (data_wan->is_sta == false)
+		if (data_wan->backhaul_type == Q6_WAN)
 		{
 				ext_prop = IPACM_Iface::ipacmcfg->GetExtProp(IPA_IP_v4);
 				handle_wan_up_ex(ext_prop, IPA_IP_v4, data_wan->xlat_mux_id);
@@ -857,12 +877,17 @@
 			IPACMERR("No event data is found.\n");
 			return;
 		}
-		IPACMDBG_H("Backhaul is sta mode?%d\n", data_wan->is_sta);
+		IPACMDBG_H("Backhaul is sta mode?%d\n", data_wan->backhaul_type);
 		if (ip_type == IPA_IP_v6 || ip_type == IPA_IP_MAX)
 		{
 			memcpy(ipv6_prefix, data_wan->ipv6_prefix, sizeof(ipv6_prefix));
 			install_ipv6_prefix_flt_rule(data_wan->ipv6_prefix);
-			if (data_wan->is_sta == false)
+
+			/* MTU might have changed. Need to update ipv4 MTU rule if up */
+			if (IPACM_Wan::isWanUP(ipa_if_num))
+				handle_private_subnet_android(IPA_IP_v4);
+
+			if (data_wan->backhaul_type == Q6_WAN)
 			{
 				ext_prop = IPACM_Iface::ipacmcfg->GetExtProp(IPA_IP_v6);
 				handle_wan_up_ex(ext_prop, IPA_IP_v6, 0);
@@ -882,10 +907,10 @@
 			IPACMERR("No event data is found.\n");
 			return;
 		}
-		IPACMDBG_H("Backhaul is sta mode?%d\n", data_wan->is_sta);
+		IPACMDBG_H("Backhaul is sta mode?%d\n", data_wan->backhaul_type);
 		if (ip_type == IPA_IP_v4 || ip_type == IPA_IP_MAX)
 		{
-			handle_wan_down(data_wan->is_sta);
+			handle_wan_down(data_wan->backhaul_type);
 		}
 		break;
 
@@ -902,13 +927,13 @@
 		/* reset usb-client ipv6 rt-rules */
 		handle_lan_client_reset_rt(IPA_IP_v6);
 
-		IPACMDBG_H("Backhaul is sta mode?%d\n", data_wan->is_sta);
+		IPACMDBG_H("Backhaul is sta mode?%d\n", data_wan->backhaul_type);
 		if (ip_type == IPA_IP_v6 || ip_type == IPA_IP_MAX)
 		{
-			handle_wan_down_v6(data_wan->is_sta);
+			handle_wan_down_v6(data_wan->backhaul_type);
 		}
 		break;
-#endif
+#endif // FEATURE_IPA_ANDROID end
 
 	case IPA_NEIGH_CLIENT_IP_ADDR_ADD_EVENT:
 		{
@@ -919,9 +944,9 @@
 
 			/* if RNDIS under WIFI mode in MSM, dun add RT rule*/
 #ifdef FEATURE_IPACM_HAL
-			if(IPACM_Wan::backhaul_is_sta_mode == true) /* WIFI */
+			if(IPACM_Wan::backhaul_mode == WLAN_WAN) /* WIFI */
 			{
-				IPACMDBG_H(" dun construct header and RT-rules for RNDIS-PC in WIFI mode on MSM targets (STA %d) \n", IPACM_Wan::backhaul_is_sta_mode);
+				IPACMDBG_H(" dun construct header and RT-rules for RNDIS-PC in WIFI mode on MSM targets (STA %d) \n", IPACM_Wan::backhaul_mode);
 				return;
 			}
 #endif
@@ -1051,13 +1076,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:
@@ -1086,7 +1111,7 @@
 		IPACMDBG_H("Received IPA_TETHERING_STATS_UPDATE_EVENT event.\n");
 		if (IPACM_Wan::isWanUP(ipa_if_num) || IPACM_Wan::isWanUP_V6(ipa_if_num))
 		{
-			if(IPACM_Wan::backhaul_is_sta_mode == false) /* LTE */
+			if(IPACM_Wan::backhaul_mode == Q6_WAN) /* LTE */
 			{
 				ipa_get_data_stats_resp_msg_v01 *data = (ipa_get_data_stats_resp_msg_v01 *)param;
 				IPACMDBG("Received IPA_TETHERING_STATS_UPDATE_STATS ipa_stats_type: %d\n",data->ipa_stats_type);
@@ -1193,7 +1218,7 @@
 }
 
 /* delete filter rule for wan_down event for IPv4*/
-int IPACM_Lan::handle_wan_down(bool is_sta_mode)
+int IPACM_Lan::handle_wan_down(ipacm_wan_iface_type backhaul_mode)
 {
 	ipa_fltr_installed_notif_req_msg_v01 flt_index;
 	int fd;
@@ -1205,7 +1230,15 @@
 		return IPACM_FAILURE;
 	}
 
-	if(is_sta_mode == false && modem_ul_v4_set == true)
+#ifdef FEATURE_IPA_ANDROID
+		/* indicate v4-offload remove */
+		if (IPACM_Wan::isXlat() && (IPACM_OffloadManager::num_offload_v4_tethered_iface > 0)) {
+			IPACM_OffloadManager::num_offload_v4_tethered_iface--;
+			IPACMDBG_H("num_offload_v4_tethered_iface %d\n", IPACM_OffloadManager::num_offload_v4_tethered_iface);
+		}
+#endif
+
+	if(backhaul_mode == Q6_WAN && modem_ul_v4_set == true)
 	{
 		if (num_wan_ul_fl_rule_v4 > MAX_WAN_UL_FILTER_RULES)
 		{
@@ -1241,12 +1274,15 @@
 			return IPACM_FAILURE;
 		}
 		flt_index.install_status = IPA_QMI_RESULT_SUCCESS_V01;
-#ifndef FEATURE_IPA_V3
-		flt_index.filter_index_list_len = 0;
-#else /* defined (FEATURE_IPA_V3) */
-		flt_index.rule_id_valid = 1;
-		flt_index.rule_id_len = 0;
-#endif
+		if (!IPACM_Iface::ipacmcfg->isIPAv3Supported())
+		{
+			flt_index.filter_index_list_len = 0;
+		}
+		else /* IPAv3 */
+		{
+			flt_index.rule_id_valid = 1;
+			flt_index.rule_id_len = 0;
+		}
 		flt_index.embedded_pipe_index_valid = 1;
 		flt_index.embedded_pipe_index = ioctl(fd, IPA_IOC_QUERY_EP_MAPPING, IPA_CLIENT_APPS_LAN_WAN_PROD);
 		if ((int)flt_index.embedded_pipe_index == -1)
@@ -1279,6 +1315,9 @@
 		sta_ul_v4_set = false;
 	}
 
+	/* clean MTU rules if needed */
+	handle_private_subnet_android(IPA_IP_v4);
+
 	close(fd);
 	return IPACM_SUCCESS;
 }
@@ -1336,9 +1375,8 @@
 		strlcpy(rt_rule->rt_tbl_name, IPACM_Iface::ipacmcfg->rt_tbl_lan_v4.name, sizeof(rt_rule->rt_tbl_name));
 		rt_rule_entry->rule.attrib.u.v4.dst_addr      = data->ipv4_addr;
 		rt_rule_entry->rule.attrib.u.v4.dst_addr_mask = 0xFFFFFFFF;
-#ifdef FEATURE_IPA_V3
-		rt_rule_entry->rule.hashable = true;
-#endif
+		if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+			rt_rule_entry->rule.hashable = true;
 		if (false == m_routing.AddRoutingRule(rt_rule))
 		{
 			IPACMERR("Routing rule addition failed!\n");
@@ -1414,9 +1452,8 @@
 		ipv6_addr[num_dft_rt_v6][1] = data->ipv6_addr[1];
 		ipv6_addr[num_dft_rt_v6][2] = data->ipv6_addr[2];
 		ipv6_addr[num_dft_rt_v6][3] = data->ipv6_addr[3];
-#ifdef FEATURE_IPA_V3
-		rt_rule_entry->rule.hashable = true;
-#endif
+		if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+			rt_rule_entry->rule.hashable = true;
 		if (false == m_routing.AddRoutingRule(rt_rule))
 		{
 			IPACMERR("Routing rule addition failed!\n");
@@ -1499,6 +1536,7 @@
 {
 	struct ipa_flt_rule_add flt_rule_entry;
 	int i;
+	bool result;
 
 	ipa_ioc_add_flt_rule *m_pFilteringTable;
 
@@ -1545,9 +1583,8 @@
 			flt_rule_entry.flt_rule_hdl = -1;
 			flt_rule_entry.status = -1;
 			flt_rule_entry.rule.action = IPA_PASS_TO_ROUTING;
-#ifdef FEATURE_IPA_V3
-			flt_rule_entry.rule.hashable = true;
-#endif
+			if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+				flt_rule_entry.rule.hashable = true;
                         /* Support private subnet feature including guest-AP can't talk to primary AP etc */
 			flt_rule_entry.rule.rt_tbl_hdl = IPACM_Iface::ipacmcfg->rt_tbl_default_v4.hdl;
 			IPACMDBG_H(" private filter rule use table: %s\n",IPACM_Iface::ipacmcfg->rt_tbl_default_v4.name);
@@ -1562,7 +1599,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);
@@ -1591,6 +1641,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");
 
@@ -1602,6 +1653,18 @@
 
 	if(ip_type == IPA_IP_v4)
 	{
+		/* add MTU rules for ipv4 */
+		handle_private_subnet_android(IPA_IP_v4);
+
+		/* Update ipv6 MTU here if WAN_v6 is up and filter rules were installed */
+		if (IPACM_Wan::isWanUP_V6(ipa_if_num))
+		{
+			if (ipv6_prefix_flt_rule_hdl[0] && ipv6_prefix_flt_rule_hdl[1] ) {
+				delete_ipv6_prefix_flt_rule();
+				install_ipv6_prefix_flt_rule(IPACM_Wan::backhaul_ipv6_prefix);
+			}
+		}
+
 		if(sta_ul_v4_set == true)
 		{
 			IPACMDBG_H("Filetring rule for IPV4 of STA mode is already configured, sta_ul_v4_set: %d\n",sta_ul_v4_set);
@@ -1645,9 +1708,8 @@
 		{
 			flt_rule_entry.rule.action = IPA_PASS_TO_SRC_NAT; //IPA_PASS_TO_ROUTING
 		}
-#ifdef FEATURE_IPA_V3
-		flt_rule_entry.rule.hashable = true;
-#endif
+		if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+			flt_rule_entry.rule.hashable = true;
 		flt_rule_entry.rule.rt_tbl_hdl = IPACM_Iface::ipacmcfg->rt_tbl_wan_v4.hdl;
 
 		memcpy(&flt_rule_entry.rule.attrib,
@@ -1665,7 +1727,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);
@@ -1721,9 +1796,8 @@
 		flt_rule_entry.flt_rule_hdl = -1;
 		flt_rule_entry.status = -1;
 		flt_rule_entry.rule.action = IPA_PASS_TO_ROUTING;
-#ifdef FEATURE_IPA_V3
-		flt_rule_entry.rule.hashable = true;
-#endif
+		if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+			flt_rule_entry.rule.hashable = true;
 		flt_rule_entry.rule.rt_tbl_hdl = IPACM_Iface::ipacmcfg->rt_tbl_v6.hdl;
 
 		memcpy(&flt_rule_entry.rule.attrib,
@@ -1743,18 +1817,32 @@
 /* only offload UL traffic of certain clients */
 #ifdef FEATURE_IPACM_HAL
 		flt_rule_entry.rule.attrib.attrib_mask |= IPA_FLT_SRC_ADDR;
-		flt_rule_entry.rule.attrib.u.v6.src_addr_mask[0] = ntohl(prefix[IPA_IP_v6].v6Mask[0]);
-		flt_rule_entry.rule.attrib.u.v6.src_addr_mask[1] = ntohl(prefix[IPA_IP_v6].v6Mask[1]);
-		flt_rule_entry.rule.attrib.u.v6.src_addr_mask[2] = ntohl(prefix[IPA_IP_v6].v6Mask[2]);
-		flt_rule_entry.rule.attrib.u.v6.src_addr_mask[3] = ntohl(prefix[IPA_IP_v6].v6Mask[3]);
-		flt_rule_entry.rule.attrib.u.v6.src_addr[0] = ntohl(prefix[IPA_IP_v6].v6Addr[0]);
-		flt_rule_entry.rule.attrib.u.v6.src_addr[1] = ntohl(prefix[IPA_IP_v6].v6Addr[1]);
-		flt_rule_entry.rule.attrib.u.v6.src_addr[2] = ntohl(prefix[IPA_IP_v6].v6Addr[2]);
-		flt_rule_entry.rule.attrib.u.v6.src_addr[3] = ntohl(prefix[IPA_IP_v6].v6Addr[3]);
+		flt_rule_entry.rule.attrib.u.v6.src_addr_mask[0] = prefix[IPA_IP_v6].v6Mask[0];
+		flt_rule_entry.rule.attrib.u.v6.src_addr_mask[1] = prefix[IPA_IP_v6].v6Mask[1];
+		flt_rule_entry.rule.attrib.u.v6.src_addr_mask[2] = prefix[IPA_IP_v6].v6Mask[2];
+		flt_rule_entry.rule.attrib.u.v6.src_addr_mask[3] = prefix[IPA_IP_v6].v6Mask[3];
+		flt_rule_entry.rule.attrib.u.v6.src_addr[0] = prefix[IPA_IP_v6].v6Addr[0];
+		flt_rule_entry.rule.attrib.u.v6.src_addr[1] = prefix[IPA_IP_v6].v6Addr[1];
+		flt_rule_entry.rule.attrib.u.v6.src_addr[2] = prefix[IPA_IP_v6].v6Addr[2];
+		flt_rule_entry.rule.attrib.u.v6.src_addr[3] = prefix[IPA_IP_v6].v6Addr[3];
 
 #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);
@@ -1817,6 +1905,19 @@
 		ret = handle_uplink_filter_rule(ext_prop, iptype, xlat_mux_id);
 		modem_ul_v6_set = true;
 	} else if (iptype ==IPA_IP_v4 && modem_ul_v4_set == false) {
+		/* add MTU rules for ipv4 */
+		handle_private_subnet_android(IPA_IP_v4);
+
+		/* Update ipv6 MTU here if WAN_v6 is up and filter rules were installed */
+		if (IPACM_Wan::isWanUP_V6(ipa_if_num))
+		{
+			if (ipv6_prefix_flt_rule_hdl[0] && ipv6_prefix_flt_rule_hdl[1] ) {
+				delete_ipv6_prefix_flt_rule();
+				install_ipv6_prefix_flt_rule(IPACM_Wan::backhaul_ipv6_prefix);
+			}
+		}
+
+		IPACMDBG_H("check getXlat_Mux_Id:%d\n", IPACM_Wan::getXlat_Mux_Id());
 		IPACMDBG_H("IPA_IP_v4 xlat_mux_id: %d, modem_ul_v4_set %d\n", xlat_mux_id, modem_ul_v4_set);
 		ret = handle_uplink_filter_rule(ext_prop, iptype, xlat_mux_id);
 		modem_ul_v4_set = true;
@@ -2356,9 +2457,8 @@
 					rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[1] = 0xFFFFFFFF;
 					rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[2] = 0xFFFFFFFF;
 					rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[3] = 0xFFFFFFFF;
-#ifdef FEATURE_IPA_V3
-					rt_rule_entry->rule.hashable = true;
-#endif
+					if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+						rt_rule_entry->rule.hashable = true;
 			if (false == m_routing.AddRoutingRule(rt_rule))
 			{
 				IPACMERR("Routing rule addition failed!\n");
@@ -2388,9 +2488,8 @@
 					rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[1] = 0xFFFFFFFF;
 					rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[2] = 0xFFFFFFFF;
 					rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[3] = 0xFFFFFFFF;
-#ifdef FEATURE_IPA_V3
-					rt_rule_entry->rule.hashable = true;
-#endif
+					if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+						rt_rule_entry->rule.hashable = true;
 		            if (false == m_routing.AddRoutingRule(rt_rule))
 		            {
 							IPACMERR("Routing rule addition failed!\n");
@@ -2401,6 +2500,15 @@
 		            get_client_memptr(eth_client, eth_index)->eth_rt_hdl[tx_index].eth_rt_rule_hdl_v6_wan[v6_num] = rt_rule->rules[0].rt_rule_hdl;
 					IPACMDBG_H("tx:%d, rt rule hdl=%x ip-type: %d\n", tx_index,
 		            				 get_client_memptr(eth_client, eth_index)->eth_rt_hdl[tx_index].eth_rt_rule_hdl_v6_wan[v6_num], iptype);
+
+					/* send client-v6 info to pcie modem only with global ipv6 with tx_index = 1 one time*/
+					if(is_global_ipv6_addr(get_client_memptr(eth_client, eth_index)->v6_addr[v6_num]) && (IPACM_Wan::backhaul_mode == Q6_MHI_WAN))
+					{
+						if (add_connection(eth_index, v6_num))
+						{
+							IPACMERR("PCIE filter rule addition failed! (%d-client) %d v6-entry\n",eth_index, v6_num);
+						}
+					}
 			    }
 			}
 
@@ -2662,9 +2770,8 @@
 		{
 			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 (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+				rt_rule_entry->rule.hashable = true;
 			if (false == m_routing.AddRoutingRule(rt_rule))
 			{
 				IPACMERR("Routing rule addition failed!\n");
@@ -2687,9 +2794,8 @@
 			rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[1] = 0;
 			rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[2] = 0;
 			rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[3] = 0;
-#ifdef FEATURE_IPA_V3
-			rt_rule_entry->rule.hashable = true;
-#endif
+			if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+				rt_rule_entry->rule.hashable = true;
 			if (false == m_routing.AddRoutingRule(rt_rule))
 			{
 				IPACMERR("Routing rule addition failed!\n");
@@ -2928,8 +3034,8 @@
 	/* delete wan filter rule */
 	if (IPACM_Wan::isWanUP(ipa_if_num) && rx_prop != NULL)
 	{
-		IPACMDBG_H("LAN IF goes down, backhaul type %d\n", IPACM_Wan::backhaul_is_sta_mode);
-		handle_wan_down(IPACM_Wan::backhaul_is_sta_mode);
+		IPACMDBG_H("LAN IF goes down, backhaul type %d\n", IPACM_Wan::backhaul_mode);
+		handle_wan_down(IPACM_Wan::backhaul_mode);
 #ifdef FEATURE_IPA_ANDROID
 #ifndef FEATURE_IPACM_HAL
 		/* Clean-up tethered-iface list */
@@ -2940,8 +3046,8 @@
 
 	if (IPACM_Wan::isWanUP_V6(ipa_if_num) && rx_prop != NULL)
 	{
-		IPACMDBG_H("LAN IF goes down, backhaul type %d\n", IPACM_Wan::backhaul_is_sta_mode);
-		handle_wan_down_v6(IPACM_Wan::backhaul_is_sta_mode);
+		IPACMDBG_H("LAN IF goes down, backhaul type %d\n", IPACM_Wan::backhaul_mode);
+		handle_wan_down_v6(IPACM_Wan::backhaul_mode);
 #ifdef FEATURE_IPA_ANDROID
 		/* Clean-up tethered-iface list */
 		IPACM_Wan::delete_tether_iface(IPA_IP_v6, ipa_if_num);
@@ -2980,13 +3086,13 @@
 		}
 
 #ifdef FEATURE_IPA_ANDROID
-		if(m_filtering.DeleteFilteringHdls(private_fl_rule_hdl, IPA_IP_v4, IPA_MAX_PRIVATE_SUBNET_ENTRIES) == false)
+		if(m_filtering.DeleteFilteringHdls(private_fl_rule_hdl, IPA_IP_v4, IPA_MAX_PRIVATE_SUBNET_ENTRIES + IPA_MAX_MTU_ENTRIES) == false)
 		{
 			IPACMERR("Error deleting private subnet IPv4 flt rules.\n");
 			res = IPACM_FAILURE;
 			goto fail;
 		}
-		IPACM_Iface::ipacmcfg->decreaseFltRuleCount(rx_prop->rx[0].src_pipe, IPA_IP_v4, IPA_MAX_PRIVATE_SUBNET_ENTRIES);
+		IPACM_Iface::ipacmcfg->decreaseFltRuleCount(rx_prop->rx[0].src_pipe, IPA_IP_v4, IPA_MAX_PRIVATE_SUBNET_ENTRIES + IPA_MAX_MTU_ENTRIES);
 #else
 		if (m_filtering.DeleteFilteringHdls(private_fl_rule_hdl, IPA_IP_v4, IPACM_Iface::ipacmcfg->ipa_num_private_subnet) == false)
 		{
@@ -3149,7 +3255,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)
@@ -3170,25 +3276,25 @@
 			IPACM_Iface::ipacmcfg->DelRmDepend(IPACM_Iface::ipacmcfg->ipa_client_rm_map_tbl[rx_prop->rx[0].src_pipe]);
 			IPACMDBG_H("Finished delete dependency \n ");
 		}
-#ifndef FEATURE_ETH_BRIDGE_LE
-		free(rx_prop);
-#endif
+		if (!(IPACM_Iface::ipacmcfg->isEthBridgingSupported()))
+				free(rx_prop);
 	}
 
 	if (eth_client != NULL)
 	{
 		free(eth_client);
 	}
-#ifndef FEATURE_ETH_BRIDGE_LE
-	if (tx_prop != NULL)
+	if (!(IPACM_Iface::ipacmcfg->isEthBridgingSupported()))
 	{
-		free(tx_prop);
+		if (tx_prop != NULL)
+		{
+			free(tx_prop);
+		}
+		if (iface_query != NULL)
+		{
+			free(iface_query);
+		}
 	}
-	if (iface_query != NULL)
-	{
-		free(iface_query);
-	}
-#endif
 	is_active = false;
 	post_del_self_evt();
 
@@ -3205,7 +3311,8 @@
 	int fd;
 	int i, index, eq_index;
 	uint32_t value = 0;
-	uint8_t qmap_id;
+	uint8_t qmap_id, xlat_debug;
+	bool result;
 
 	IPACMDBG_H("Set modem UL flt rules\n");
 
@@ -3244,12 +3351,15 @@
 	}
 
 	flt_index.install_status = IPA_QMI_RESULT_SUCCESS_V01;
-#ifndef FEATURE_IPA_V3
-	flt_index.filter_index_list_len = prop->num_ext_props;
-#else /* defined (FEATURE_IPA_V3) */
-	flt_index.rule_id_valid = 1;
-	flt_index.rule_id_len = prop->num_ext_props;
-#endif
+	if (!IPACM_Iface::ipacmcfg->isIPAv3Supported())
+	{
+		flt_index.filter_index_list_len = prop->num_ext_props;
+	}
+	else /* IPAv3 */
+	{
+		flt_index.rule_id_valid = 1;
+		flt_index.rule_id_len = prop->num_ext_props;
+	}
 	flt_index.embedded_pipe_index_valid = 1;
 	flt_index.embedded_pipe_index = ioctl(fd, IPA_IOC_QUERY_EP_MAPPING, IPA_CLIENT_APPS_LAN_WAN_PROD);
 	if ((int)flt_index.embedded_pipe_index == -1)
@@ -3263,14 +3373,18 @@
 	flt_index.retain_header = 0;
 	flt_index.embedded_call_mux_id_valid = 1;	
 	qmap_id = IPACM_Iface::ipacmcfg->GetQmapId();
+	xlat_debug = IPACM_Wan::getXlat_Mux_Id();
 	flt_index.embedded_call_mux_id = qmap_id;
-#ifndef FEATURE_IPA_V3
-	IPACMDBG_H("flt_index: src pipe: %d, num of rules: %d, ebd pipe: %d, mux id: %d\n",
-		flt_index.source_pipe_index, flt_index.filter_index_list_len, flt_index.embedded_pipe_index, flt_index.embedded_call_mux_id);
-#else /* defined (FEATURE_IPA_V3) */
-	IPACMDBG_H("flt_index: src pipe: %d, num of rules: %d, ebd pipe: %d, mux id: %d\n",
-		flt_index.source_pipe_index, flt_index.rule_id_len, flt_index.embedded_pipe_index, flt_index.embedded_call_mux_id);
-#endif
+	if (!IPACM_Iface::ipacmcfg->isIPAv3Supported())
+	{
+		IPACMDBG_H("flt_index: src pipe: %d, num of rules: %d, ebd pipe: %d, mux id: %d, xlat_mux id: %d, wan-debug %d\n",
+			flt_index.source_pipe_index, flt_index.filter_index_list_len, flt_index.embedded_pipe_index, flt_index.embedded_call_mux_id, xlat_mux_id, xlat_debug);
+	}
+	else /* IPAv3 */
+	{
+		IPACMDBG_H("flt_index: src pipe: %d, num of rules: %d, ebd pipe: %d, mux id: %d, xlat_mux id: %d, wan-debug %d\n",
+			flt_index.source_pipe_index, flt_index.rule_id_len, flt_index.embedded_pipe_index, flt_index.embedded_call_mux_id, xlat_mux_id, xlat_debug);
+	}
 	len = sizeof(struct ipa_ioc_add_flt_rule) + prop->num_ext_props * sizeof(struct ipa_flt_rule_add);
 	pFilteringTable = (struct ipa_ioc_add_flt_rule*)malloc(len);
 	if (pFilteringTable == NULL)
@@ -3289,10 +3403,9 @@
 
 	memset(&flt_rule_entry, 0, sizeof(struct ipa_flt_rule_add)); // Zero All Fields
 	flt_rule_entry.at_rear = 1;
-#ifdef FEATURE_IPA_V3
-	if (flt_rule_entry.rule.eq_attrib.ipv4_frag_eq_present)
-		flt_rule_entry.at_rear = 0;
-#endif
+	if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+		if (flt_rule_entry.rule.eq_attrib.ipv4_frag_eq_present)
+			flt_rule_entry.at_rear = 0;
 	flt_rule_entry.flt_rule_hdl = -1;
 	flt_rule_entry.status = -1;
 
@@ -3340,6 +3453,13 @@
 		}
 
 		/* Handle XLAT configuration */
+		/* temp wa to reset xlat_mux_id to qmap_id if it's xlat call */
+		if (IPACM_Wan::isXlat() && (iptype == IPA_IP_v4))
+		{
+			IPACMDBG_H("WA to replace xlat_mux_id %d with qmap_id: %d\n", xlat_mux_id, qmap_id);
+			xlat_mux_id = qmap_id;
+		}
+
 		if ((iptype == IPA_IP_v4) && prop->prop[cnt].is_xlat_rule && (xlat_mux_id != 0))
 		{
 			/* fill the value of meta-data */
@@ -3362,25 +3482,28 @@
 		{
 			flt_rule_entry.rule.eq_attrib.num_offset_meq_32++;
 			eq_index = flt_rule_entry.rule.eq_attrib.num_offset_meq_32 - 1;
-#ifdef FEATURE_IPA_V3
-			if(eq_index == 0)
+			if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
 			{
-				flt_rule_entry.rule.eq_attrib.rule_eq_bitmap |= (1<<5);
+				if(eq_index == 0)
+				{
+					flt_rule_entry.rule.eq_attrib.rule_eq_bitmap |= (1<<5);
+				}
+				else
+				{
+					flt_rule_entry.rule.eq_attrib.rule_eq_bitmap |= (1<<6);
+				}
 			}
 			else
 			{
-				flt_rule_entry.rule.eq_attrib.rule_eq_bitmap |= (1<<6);
+				if(eq_index == 0)
+				{
+					flt_rule_entry.rule.eq_attrib.rule_eq_bitmap |= (1<<2);
+				}
+				else
+				{
+					flt_rule_entry.rule.eq_attrib.rule_eq_bitmap |= (1<<3);
+				}
 			}
-#else
-			if(eq_index == 0)
-			{
-				flt_rule_entry.rule.eq_attrib.rule_eq_bitmap |= (1<<2);
-			}
-			else
-			{
-				flt_rule_entry.rule.eq_attrib.rule_eq_bitmap |= (1<<3);
-			}
-#endif
 			flt_rule_entry.rule.eq_attrib.offset_meq_32[eq_index].offset = 12;
 			flt_rule_entry.rule.eq_attrib.offset_meq_32[eq_index].mask = prefix[IPA_IP_v4].v4Mask;
 			flt_rule_entry.rule.eq_attrib.offset_meq_32[eq_index].value = prefix[IPA_IP_v4].v4Addr;
@@ -3398,88 +3521,95 @@
 			{
 				flt_rule_entry.rule.eq_attrib.num_offset_meq_128++;
 				eq_index = flt_rule_entry.rule.eq_attrib.num_offset_meq_128 - 1;
-#ifdef FEATURE_IPA_V3
-				if(eq_index == 0)
+				if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
 				{
-					flt_rule_entry.rule.eq_attrib.rule_eq_bitmap |= (1<<3);
+					if(eq_index == 0)
+					{
+						flt_rule_entry.rule.eq_attrib.rule_eq_bitmap |= (1<<3);
+					}
+					else
+					{
+						flt_rule_entry.rule.eq_attrib.rule_eq_bitmap |= (1<<4);
+					}
+					*(uint32_t *)(flt_rule_entry.rule.eq_attrib.offset_meq_128[eq_index].mask + 0)
+							= prefix[IPA_IP_v6].v6Mask[3];
+					*(uint32_t *)(flt_rule_entry.rule.eq_attrib.offset_meq_128[eq_index].mask + 4)
+							= prefix[IPA_IP_v6].v6Mask[2];
+					*(uint32_t *)(flt_rule_entry.rule.eq_attrib.offset_meq_128[eq_index].mask + 8)
+							= prefix[IPA_IP_v6].v6Mask[1];
+					*(uint32_t *)(flt_rule_entry.rule.eq_attrib.offset_meq_128[eq_index].mask + 12)
+							= prefix[IPA_IP_v6].v6Mask[0];
+					*(uint32_t *)(flt_rule_entry.rule.eq_attrib.offset_meq_128[eq_index].value + 0)
+							= prefix[IPA_IP_v6].v6Addr[3];
+					*(uint32_t *)(flt_rule_entry.rule.eq_attrib.offset_meq_128[eq_index].value + 4)
+							= prefix[IPA_IP_v6].v6Addr[2];
+					*(uint32_t *)(flt_rule_entry.rule.eq_attrib.offset_meq_128[eq_index].value + 8)
+							= prefix[IPA_IP_v6].v6Addr[1];
+					*(uint32_t *)(flt_rule_entry.rule.eq_attrib.offset_meq_128[eq_index].value + 12)
+							= prefix[IPA_IP_v6].v6Addr[0];
 				}
 				else
 				{
-					flt_rule_entry.rule.eq_attrib.rule_eq_bitmap |= (1<<4);
+					if(eq_index == 0)
+					{
+						flt_rule_entry.rule.eq_attrib.rule_eq_bitmap |= (1<<9);
+					}
+					else
+					{
+						flt_rule_entry.rule.eq_attrib.rule_eq_bitmap |= (1<<10);
+					}
+					*(uint32_t *)(flt_rule_entry.rule.eq_attrib.offset_meq_128[eq_index].mask + 0)
+							= prefix[IPA_IP_v6].v6Mask[0];
+					*(uint32_t *)(flt_rule_entry.rule.eq_attrib.offset_meq_128[eq_index].mask + 4)
+							= prefix[IPA_IP_v6].v6Mask[1];
+					*(uint32_t *)(flt_rule_entry.rule.eq_attrib.offset_meq_128[eq_index].mask + 8)
+							= prefix[IPA_IP_v6].v6Mask[2];
+					*(uint32_t *)(flt_rule_entry.rule.eq_attrib.offset_meq_128[eq_index].mask + 12)
+							= prefix[IPA_IP_v6].v6Mask[3];
+					*(uint32_t *)(flt_rule_entry.rule.eq_attrib.offset_meq_128[eq_index].value + 0)
+							= prefix[IPA_IP_v6].v6Addr[0];
+					*(uint32_t *)(flt_rule_entry.rule.eq_attrib.offset_meq_128[eq_index].value + 4)
+							= prefix[IPA_IP_v6].v6Addr[1];
+					*(uint32_t *)(flt_rule_entry.rule.eq_attrib.offset_meq_128[eq_index].value + 8)
+							= prefix[IPA_IP_v6].v6Addr[2];
+					*(uint32_t *)(flt_rule_entry.rule.eq_attrib.offset_meq_128[eq_index].value + 12)
+							= prefix[IPA_IP_v6].v6Addr[3];
 				}
-				*(uint32_t *)(flt_rule_entry.rule.eq_attrib.offset_meq_128[eq_index].mask + 0)
-						= prefix[IPA_IP_v6].v6Mask[3];
-				*(uint32_t *)(flt_rule_entry.rule.eq_attrib.offset_meq_128[eq_index].mask + 4)
-						= prefix[IPA_IP_v6].v6Mask[2];
-				*(uint32_t *)(flt_rule_entry.rule.eq_attrib.offset_meq_128[eq_index].mask + 8)
-						= prefix[IPA_IP_v6].v6Mask[1];
-				*(uint32_t *)(flt_rule_entry.rule.eq_attrib.offset_meq_128[eq_index].mask + 12)
-						= prefix[IPA_IP_v6].v6Mask[0];
-				*(uint32_t *)(flt_rule_entry.rule.eq_attrib.offset_meq_128[eq_index].value + 0)
-						= prefix[IPA_IP_v6].v6Addr[3];
-				*(uint32_t *)(flt_rule_entry.rule.eq_attrib.offset_meq_128[eq_index].value + 4)
-						= prefix[IPA_IP_v6].v6Addr[2];
-				*(uint32_t *)(flt_rule_entry.rule.eq_attrib.offset_meq_128[eq_index].value + 8)
-						= prefix[IPA_IP_v6].v6Addr[1];
-				*(uint32_t *)(flt_rule_entry.rule.eq_attrib.offset_meq_128[eq_index].value + 12)
-						= prefix[IPA_IP_v6].v6Addr[0];
-#else
-				if(eq_index == 0)
-				{
-					flt_rule_entry.rule.eq_attrib.rule_eq_bitmap |= (1<<9);
-				}
-				else
-				{
-					flt_rule_entry.rule.eq_attrib.rule_eq_bitmap |= (1<<10);
-				}
-				*(uint32_t *)(flt_rule_entry.rule.eq_attrib.offset_meq_128[eq_index].mask + 0)
-						= prefix[IPA_IP_v6].v6Mask[0];
-				*(uint32_t *)(flt_rule_entry.rule.eq_attrib.offset_meq_128[eq_index].mask + 4)
-						= prefix[IPA_IP_v6].v6Mask[1];
-				*(uint32_t *)(flt_rule_entry.rule.eq_attrib.offset_meq_128[eq_index].mask + 8)
-						= prefix[IPA_IP_v6].v6Mask[2];
-				*(uint32_t *)(flt_rule_entry.rule.eq_attrib.offset_meq_128[eq_index].mask + 12)
-						= prefix[IPA_IP_v6].v6Mask[3];
-				*(uint32_t *)(flt_rule_entry.rule.eq_attrib.offset_meq_128[eq_index].value + 0)
-						= prefix[IPA_IP_v6].v6Addr[0];
-				*(uint32_t *)(flt_rule_entry.rule.eq_attrib.offset_meq_128[eq_index].value + 4)
-						= prefix[IPA_IP_v6].v6Addr[1];
-				*(uint32_t *)(flt_rule_entry.rule.eq_attrib.offset_meq_128[eq_index].value + 8)
-						= prefix[IPA_IP_v6].v6Addr[2];
-				*(uint32_t *)(flt_rule_entry.rule.eq_attrib.offset_meq_128[eq_index].value + 12)
-						= prefix[IPA_IP_v6].v6Addr[3];
-#endif
 				flt_rule_entry.rule.eq_attrib.offset_meq_128[eq_index].offset = 8;
 			}
 			else
 			{
 				IPACMERR("Run out of MEQ128 equation.\n");
-				flt_rule_entry.rule.eq_attrib.num_offset_meq_128--;
+					flt_rule_entry.rule.eq_attrib.num_offset_meq_128--;
 			}
 		}
-#endif
 
-#ifdef FEATURE_IPA_V3
-		flt_rule_entry.rule.hashable = prop->prop[cnt].is_rule_hashable;
-		flt_rule_entry.rule.rule_id = prop->prop[cnt].rule_id;
-		if(rx_prop->rx[0].attrib.attrib_mask & IPA_FLT_META_DATA)	//turn on meta-data equation
-		{
-			flt_rule_entry.rule.eq_attrib.rule_eq_bitmap |= (1<<9);
-			flt_rule_entry.rule.eq_attrib.metadata_meq32_present = 1;
-			flt_rule_entry.rule.eq_attrib.metadata_meq32.offset = 0;
-			flt_rule_entry.rule.eq_attrib.metadata_meq32.value |= rx_prop->rx[0].attrib.meta_data;
-			flt_rule_entry.rule.eq_attrib.metadata_meq32.mask |= rx_prop->rx[0].attrib.meta_data_mask;
-		}
 #endif
+		if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+		{
+			flt_rule_entry.rule.hashable = prop->prop[cnt].is_rule_hashable;
+			flt_rule_entry.rule.rule_id = prop->prop[cnt].rule_id;
+			if(rx_prop->rx[0].attrib.attrib_mask & IPA_FLT_META_DATA)	//turn on meta-data equation
+			{
+				flt_rule_entry.rule.eq_attrib.rule_eq_bitmap |= (1<<9);
+				flt_rule_entry.rule.eq_attrib.metadata_meq32_present = 1;
+				flt_rule_entry.rule.eq_attrib.metadata_meq32.offset = 0;
+				flt_rule_entry.rule.eq_attrib.metadata_meq32.value |= rx_prop->rx[0].attrib.meta_data;
+				flt_rule_entry.rule.eq_attrib.metadata_meq32.mask |= rx_prop->rx[0].attrib.meta_data_mask;
+			}
+		}
 		memcpy(&pFilteringTable->rules[cnt], &flt_rule_entry, sizeof(flt_rule_entry));
 
 		IPACMDBG_H("Modem UL filtering rule %d has index %d\n", cnt, index);
-#ifndef FEATURE_IPA_V3
-		flt_index.filter_index_list[cnt].filter_index = index;
-		flt_index.filter_index_list[cnt].filter_handle = prop->prop[cnt].filter_hdl;
-#else /* defined (FEATURE_IPA_V3) */
-		flt_index.rule_id[cnt] = prop->prop[cnt].rule_id;
-#endif
+		if (!IPACM_Iface::ipacmcfg->isIPAv3Supported())
+		{
+			flt_index.filter_index_list[cnt].filter_index = index;
+			flt_index.filter_index_list[cnt].filter_handle = prop->prop[cnt].filter_hdl;
+		}
+		else /* IPAv3 */
+		{
+			flt_index.rule_id[cnt] = prop->prop[cnt].rule_id;
+		}
 		index++;
 	}
 
@@ -3490,7 +3620,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;
@@ -3532,7 +3675,7 @@
 	return ret;
 }
 
-int IPACM_Lan::handle_wan_down_v6(bool is_sta_mode)
+int IPACM_Lan::handle_wan_down_v6(ipacm_wan_iface_type backhaul_mode)
 {
 	ipa_fltr_installed_notif_req_msg_v01 flt_index;
 	int fd;
@@ -3548,7 +3691,7 @@
 
 	memset(ipv6_prefix, 0, sizeof(ipv6_prefix));
 
-	if(is_sta_mode == false && modem_ul_v6_set == true)
+	if(backhaul_mode == Q6_WAN && modem_ul_v6_set == true)
 	{
 		if (num_wan_ul_fl_rule_v6 > MAX_WAN_UL_FILTER_RULES)
 		{
@@ -3584,12 +3727,15 @@
 			return IPACM_FAILURE;
 		}
 		flt_index.install_status = IPA_QMI_RESULT_SUCCESS_V01;
-#ifndef FEATURE_IPA_V3
-		flt_index.filter_index_list_len = 0;
-#else /* defined (FEATURE_IPA_V3) */
-		flt_index.rule_id_valid = 1;
-		flt_index.rule_id_len = 0;
-#endif
+		if (!IPACM_Iface::ipacmcfg->isIPAv3Supported())
+		{
+			flt_index.filter_index_list_len = 0;
+		}
+		else /* IPAv3 */
+		{
+			flt_index.rule_id_valid = 1;
+			flt_index.rule_id_len = 0;
+		}
 		flt_index.embedded_pipe_index_valid = 1;
 		flt_index.embedded_pipe_index = ioctl(fd, IPA_IOC_QUERY_EP_MAPPING, IPA_CLIENT_APPS_LAN_WAN_PROD);
 		if ((int)flt_index.embedded_pipe_index == -1)
@@ -3786,6 +3932,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)
 	{
@@ -3813,16 +3960,28 @@
 		flt_rule_entry.flt_rule_hdl = -1;
 		flt_rule_entry.status = -1;
 		flt_rule_entry.rule.action = IPA_PASS_TO_EXCEPTION;
-#ifdef FEATURE_IPA_V3
-		flt_rule_entry.rule.hashable = true;
-#endif
+		if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+			flt_rule_entry.rule.hashable = true;
 		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_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);
@@ -3845,6 +4004,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)
 	{
@@ -3872,15 +4032,27 @@
 		flt_rule_entry.flt_rule_hdl = -1;
 		flt_rule_entry.status = -1;
 		flt_rule_entry.rule.action = IPA_PASS_TO_EXCEPTION;
-#ifdef FEATURE_IPA_V3
-		flt_rule_entry.rule.hashable = false;
-#endif
+		if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+			flt_rule_entry.rule.hashable = false;
 		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_NEXT_HDR;
 		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);
@@ -3913,8 +4085,9 @@
 	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);
+	len = sizeof(struct ipa_ioc_add_flt_rule) + (IPA_MAX_PRIVATE_SUBNET_ENTRIES + IPA_MAX_MTU_ENTRIES) * sizeof(struct ipa_flt_rule_add);
 
 	pFilteringTable = (struct ipa_ioc_add_flt_rule *)malloc(len);
 	if (pFilteringTable == NULL)
@@ -3928,7 +4101,7 @@
 	pFilteringTable->ep = rx_prop->rx[0].src_pipe;
 	pFilteringTable->global = false;
 	pFilteringTable->ip = iptype;
-	pFilteringTable->num_rules = IPA_MAX_PRIVATE_SUBNET_ENTRIES;
+	pFilteringTable->num_rules = IPA_MAX_PRIVATE_SUBNET_ENTRIES + IPA_MAX_MTU_ENTRIES;
 
 	memset(&flt_rule, 0, sizeof(struct ipa_flt_rule_add));
 
@@ -3937,9 +4110,8 @@
 	flt_rule.flt_rule_hdl = -1;
 	flt_rule.status = -1;
 	flt_rule.rule.action = IPA_PASS_TO_EXCEPTION;
-#ifdef FEATURE_IPA_V3
-	flt_rule.rule.hashable = true;
-#endif
+	if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+		flt_rule.rule.hashable = true;
 	memcpy(&flt_rule.rule.attrib, &rx_prop->rx[0].attrib,
 			sizeof(flt_rule.rule.attrib));
 
@@ -3951,12 +4123,25 @@
 		flt_rule.rule.attrib.u.v4.dst_addr_mask = ~0;
 		flt_rule.rule.attrib.u.v4.dst_addr = ~0;
 
-		for(i=0; i<IPA_MAX_PRIVATE_SUBNET_ENTRIES; i++)
+		for(i=0; i<IPA_MAX_PRIVATE_SUBNET_ENTRIES + IPA_MAX_MTU_ENTRIES; i++)
 		{
 			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;
@@ -3964,9 +4149,9 @@
 		}
 		else
 		{
-			IPACM_Iface::ipacmcfg->increaseFltRuleCount(rx_prop->rx[0].src_pipe, IPA_IP_v4, IPA_MAX_PRIVATE_SUBNET_ENTRIES);
+			IPACM_Iface::ipacmcfg->increaseFltRuleCount(rx_prop->rx[0].src_pipe, IPA_IP_v4, IPA_MAX_PRIVATE_SUBNET_ENTRIES + IPA_MAX_MTU_ENTRIES);
 			/* copy filter rule hdls */
-			for (int i = 0; i < IPA_MAX_PRIVATE_SUBNET_ENTRIES; i++)
+			for (int i = 0; i < IPA_MAX_PRIVATE_SUBNET_ENTRIES + IPA_MAX_MTU_ENTRIES; i++)
 			{
 				if (pFilteringTable->rules[i].status == 0)
 				{
@@ -3992,6 +4177,9 @@
 	int i, len, res = IPACM_SUCCESS;
 	struct ipa_flt_rule_mdfy flt_rule;
 	struct ipa_ioc_mdfy_flt_rule* pFilteringTable;
+	int mtu_rule_cnt = 0;
+	uint16_t mtu[IPA_MAX_PRIVATE_SUBNET_ENTRIES + IPA_MAX_MTU_ENTRIES] = { };
+	int mtu_rule_idx = IPACM_Iface::ipacmcfg->ipa_num_private_subnet;
 
 	if (rx_prop == NULL)
 	{
@@ -4006,12 +4194,24 @@
 	}
 	else
 	{
-		for(i=0; i<IPA_MAX_PRIVATE_SUBNET_ENTRIES; i++)
+		for(i=0; i<IPA_MAX_PRIVATE_SUBNET_ENTRIES + IPA_MAX_MTU_ENTRIES; i++)
 		{
 			reset_to_dummy_flt_rule(IPA_IP_v4, private_fl_rule_hdl[i]);
 		}
 
-		len = sizeof(struct ipa_ioc_mdfy_flt_rule) + (IPACM_Iface::ipacmcfg->ipa_num_private_subnet) * sizeof(struct ipa_flt_rule_mdfy);
+		/* check how many MTU rules we need to add */
+		for(i = 0; i < IPACM_Iface::ipacmcfg->ipa_num_private_subnet; i++)
+		{
+			mtu[i] = IPACM_Wan::queryMTU(ipa_if_num, IPA_IP_v4);
+
+			if (mtu[i] > 0)
+				mtu_rule_cnt++;
+			else
+				IPACMERR("MTU is zero\n");
+		}
+		IPACMDBG_H("total %d MTU rules are needed\n", mtu_rule_cnt);
+
+		len = sizeof(struct ipa_ioc_mdfy_flt_rule) + (IPACM_Iface::ipacmcfg->ipa_num_private_subnet + mtu_rule_cnt) * sizeof(struct ipa_flt_rule_mdfy);
 		pFilteringTable = (struct ipa_ioc_mdfy_flt_rule*)malloc(len);
 		if (!pFilteringTable)
 		{
@@ -4022,7 +4222,7 @@
 
 		pFilteringTable->commit = 1;
 		pFilteringTable->ip = iptype;
-		pFilteringTable->num_rules = (uint8_t)IPACM_Iface::ipacmcfg->ipa_num_private_subnet;
+		pFilteringTable->num_rules = (uint8_t)IPACM_Iface::ipacmcfg->ipa_num_private_subnet + mtu_rule_cnt;
 
 		/* Make LAN-traffic always go A5, use default IPA-RT table */
 		if (false == m_routing.GetRoutingTable(&IPACM_Iface::ipacmcfg->rt_tbl_default_v4))
@@ -4042,16 +4242,32 @@
 		flt_rule.rule.rt_tbl_hdl = IPACM_Iface::ipacmcfg->rt_tbl_default_v4.hdl;
 		IPACMDBG_H("Private filter rule use table: %s\n",IPACM_Iface::ipacmcfg->rt_tbl_default_v4.name);
 
-		memcpy(&flt_rule.rule.attrib, &rx_prop->rx[0].attrib, sizeof(flt_rule.rule.attrib));
-		flt_rule.rule.attrib.attrib_mask |= IPA_FLT_DST_ADDR;
-
 		for (i = 0; i < (IPACM_Iface::ipacmcfg->ipa_num_private_subnet); i++)
 		{
+			/* add private subnet rule for ipv4 */
+			flt_rule.rule.action = IPA_PASS_TO_ROUTING;
+			flt_rule.rule.eq_attrib_type = 0;
 			flt_rule.rule_hdl = private_fl_rule_hdl[i];
+			memcpy(&flt_rule.rule.attrib, &rx_prop->rx[0].attrib, sizeof(flt_rule.rule.attrib));
+			flt_rule.rule.attrib.attrib_mask |= IPA_FLT_DST_ADDR;
 			flt_rule.rule.attrib.u.v4.dst_addr_mask = IPACM_Iface::ipacmcfg->private_subnet_table[i].subnet_mask;
 			flt_rule.rule.attrib.u.v4.dst_addr = IPACM_Iface::ipacmcfg->private_subnet_table[i].subnet_addr;
 			memcpy(&(pFilteringTable->rules[i]), &flt_rule, sizeof(struct ipa_flt_rule_mdfy));
 			IPACMDBG_H(" IPACM private subnet_addr as: 0x%x entry(%d)\n", flt_rule.rule.attrib.u.v4.dst_addr, i);
+
+			/* add corresponding MTU rule for ipv4 */
+			if (mtu[i] > 0)
+			{
+				flt_rule.rule_hdl = private_fl_rule_hdl[mtu_rule_idx + i];
+				memcpy(&flt_rule.rule.attrib, &rx_prop->rx[0].attrib, sizeof(flt_rule.rule.attrib));
+				flt_rule.rule.attrib.u.v4.src_addr_mask = IPACM_Iface::ipacmcfg->private_subnet_table[i].subnet_mask;
+				flt_rule.rule.attrib.u.v4.src_addr = IPACM_Iface::ipacmcfg->private_subnet_table[i].subnet_addr;
+				flt_rule.rule.attrib.attrib_mask |= IPA_FLT_SRC_ADDR;
+				if (construct_mtu_rule(&flt_rule.rule, IPA_IP_v4, mtu[i]))
+					IPACMERR("Failed to modify MTU filtering rule.\n");
+				memcpy(&(pFilteringTable->rules[mtu_rule_idx + i]), &flt_rule, sizeof(struct ipa_flt_rule_mdfy));
+				IPACMDBG_H("Adding MTU rule for private subnet 0x%x.\n", flt_rule.rule.attrib.u.v4.src_addr);
+			}
 		}
 
 		if (false == m_filtering.ModifyFilteringRule(pFilteringTable))
@@ -4081,12 +4297,18 @@
 	int len;
 	struct ipa_ioc_add_flt_rule* flt_rule;
 	struct ipa_flt_rule_add flt_rule_entry;
+	bool result;
+	int rule_cnt = 1;
+
+	uint16_t mtu = IPACM_Wan::queryMTU(ipa_if_num, IPA_IP_v6);
+	if (mtu > 0)
+		rule_cnt ++;
 
 	if(rx_prop != NULL)
 	{
-		len = sizeof(struct ipa_ioc_add_flt_rule) + sizeof(struct ipa_flt_rule_add);
+		len = sizeof(struct ipa_ioc_add_flt_rule) + rule_cnt * sizeof(struct ipa_flt_rule_add);
 
-		flt_rule = (struct ipa_ioc_add_flt_rule *)calloc(1, len);
+		flt_rule = (struct ipa_ioc_add_flt_rule *)calloc(rule_cnt, len);
 		if (!flt_rule)
 		{
 			IPACMERR("Error Locate ipa_flt_rule_add memory...\n");
@@ -4097,7 +4319,7 @@
 		flt_rule->ep = rx_prop->rx[0].src_pipe;
 		flt_rule->global = false;
 		flt_rule->ip = IPA_IP_v6;
-		flt_rule->num_rules = 1;
+		flt_rule->num_rules = rule_cnt;
 
 		memset(&flt_rule_entry, 0, sizeof(struct ipa_flt_rule_add));
 
@@ -4108,9 +4330,8 @@
 		flt_rule_entry.flt_rule_hdl = -1;
 		flt_rule_entry.status = -1;
 		flt_rule_entry.rule.action = IPA_PASS_TO_EXCEPTION;
-#ifdef FEATURE_IPA_V3
-		flt_rule_entry.rule.hashable = true;
-#endif
+		if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+			flt_rule_entry.rule.hashable = true;
 		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_DST_ADDR;
 		flt_rule_entry.rule.attrib.u.v6.dst_addr[0] = prefix[0];
@@ -4123,7 +4344,44 @@
 		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)
+		memcpy(&flt_rule_entry.rule.attrib, &rx_prop->rx[0].attrib, sizeof(flt_rule_entry.rule.attrib)); // this will remove the IPA_FLT_DST_ADDR
+		flt_rule_entry.rule.attrib.u.v6.src_addr[3] = prefix[0];
+		flt_rule_entry.rule.attrib.u.v6.src_addr[2] = prefix[1];
+		flt_rule_entry.rule.attrib.u.v6.src_addr[1] = 0x0;
+		flt_rule_entry.rule.attrib.u.v6.src_addr[0] = 0x0;
+		flt_rule_entry.rule.attrib.u.v6.src_addr_mask[3] = 0xFFFFFFFF;
+		flt_rule_entry.rule.attrib.u.v6.src_addr_mask[2] = 0xFFFFFFFF;
+		flt_rule_entry.rule.attrib.u.v6.src_addr_mask[1] = 0x0;
+		flt_rule_entry.rule.attrib.u.v6.src_addr_mask[0] = 0x0;
+		flt_rule_entry.rule.attrib.attrib_mask |= IPA_FLT_SRC_ADDR;
+
+		/* Add an MTU rule with every new private prefix */
+		if (mtu > 0)
+		{
+			if (construct_mtu_rule(&flt_rule_entry.rule, IPA_IP_v6, mtu))
+			{
+				IPACMERR("Failed to add MTU filtering rule.\n")
+			}
+			else
+			{
+				memcpy(&(flt_rule->rules[1]), &flt_rule_entry, sizeof(struct ipa_flt_rule_add));
+			}
+		}
+
+#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);
@@ -4131,9 +4389,14 @@
 		}
 		else
 		{
-			IPACM_Iface::ipacmcfg->increaseFltRuleCount(rx_prop->rx[0].src_pipe, IPA_IP_v6, 1);
+			IPACM_Iface::ipacmcfg->increaseFltRuleCount(rx_prop->rx[0].src_pipe, IPA_IP_v6, 2);
 			ipv6_prefix_flt_rule_hdl[0] = flt_rule->rules[0].flt_rule_hdl;
 			IPACMDBG_H("IPv6 prefix filter rule HDL:0x%x\n", ipv6_prefix_flt_rule_hdl[0]);
+			if (rule_cnt > 1)
+			{
+				ipv6_prefix_flt_rule_hdl[1] = flt_rule->rules[1].flt_rule_hdl;
+				IPACMDBG_H("IPv6 prefix MTU filter rule HDL:0x%x\n", ipv6_prefix_flt_rule_hdl[1]);
+			}
 			free(flt_rule);
 		}
 	}
@@ -4142,12 +4405,12 @@
 
 void IPACM_Lan::delete_ipv6_prefix_flt_rule()
 {
-	if(m_filtering.DeleteFilteringHdls(ipv6_prefix_flt_rule_hdl, IPA_IP_v6, NUM_IPV6_PREFIX_FLT_RULE) == false)
+	if(m_filtering.DeleteFilteringHdls(ipv6_prefix_flt_rule_hdl, IPA_IP_v6, NUM_IPV6_PREFIX_FLT_RULE + NUM_IPV6_PREFIX_MTU_RULE) == false)
 	{
-		IPACMERR("Failed to delete ipv6 prefix flt rule.\n");
+		IPACMERR("Failed to delete ipv6 prefix flt rules.\n");
 		return;
 	}
-	IPACM_Iface::ipacmcfg->decreaseFltRuleCount(rx_prop->rx[0].src_pipe, IPA_IP_v6, NUM_IPV6_PREFIX_FLT_RULE);
+	IPACM_Iface::ipacmcfg->decreaseFltRuleCount(rx_prop->rx[0].src_pipe, IPA_IP_v6, NUM_IPV6_PREFIX_FLT_RULE + NUM_IPV6_PREFIX_MTU_RULE);
 	return;
 }
 
@@ -4575,7 +4838,7 @@
 		}
 	}
 
-	ret = ioctl(fd_wwan_ioctl, WAN_IOC_SET_TETHER_CLIENT_PIPE, &tether_client);
+	ret = ioctl(fd_wwan_ioctl, WAN_IOC_SET_TETHER_CLIENT_PIPE, tether_client);
 	if(ret != 0)
 	{
 		IPACMERR("Failed set tether-client-pipe %p with ret %d\n ", &tether_client, ret);
@@ -4782,9 +5045,8 @@
 	rt_rule.at_rear = false;
 	rt_rule.status = -1;
 	rt_rule.rt_rule_hdl = -1;
-#ifdef FEATURE_IPA_V3
-	rt_rule.rule.hashable = true;
-#endif
+	if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+		rt_rule.rule.hashable = true;
 	rt_rule.rule.hdr_hdl = 0;
 	rt_rule.rule.hdr_proc_ctx_hdl = hdr_proc_ctx_hdl;
 
@@ -4916,9 +5178,8 @@
 
 			rt_rule_entry->rule.hdr_hdl = 0;
 			rt_rule_entry->rule.hdr_proc_ctx_hdl = hdr_proc_ctx_hdl;
-#ifdef FEATURE_IPA_V3
-			rt_rule_entry->rule.hashable = true;
-#endif
+			if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+				rt_rule_entry->rule.hashable = true;
 			memcpy(&rt_rule_entry->rule.attrib, &tx_prop->tx[index].attrib,
 					sizeof(rt_rule_entry->rule.attrib));
 			if(peer_l2_hdr_type == IPA_HDR_L2_ETHERNET_II)
@@ -4957,77 +5218,92 @@
 int IPACM_Lan::eth_bridge_add_flt_rule(uint8_t *mac, uint32_t rt_tbl_hdl, ipa_ip_type iptype, uint32_t *flt_rule_hdl)
 {
 	int res = IPACM_SUCCESS;
+	int len;
+	struct ipa_flt_rule_add flt_rule_entry;
+	struct ipa_ioc_add_flt_rule_after *pFilteringTable = NULL;
+	bool result;
 
 	IPACMDBG_H("Received client MAC 0x%02x%02x%02x%02x%02x%02x.\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
 	IPACMDBG_H("Received rt_tbl_hdl :%d iptype %d\n", rt_tbl_hdl,iptype);
 	*flt_rule_hdl = 0;
 
-#ifdef FEATURE_IPA_V3
-	int len;
-	struct ipa_flt_rule_add flt_rule_entry;
-	struct ipa_ioc_add_flt_rule_after *pFilteringTable = NULL;
-
-	if (rx_prop == NULL || tx_prop == NULL)
+	if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
 	{
-		IPACMDBG_H("No rx or tx properties registered for iface %s\n", dev_name);
-		return IPACM_FAILURE;
-	}
+		if (rx_prop == NULL || tx_prop == NULL)
+		{
+			IPACMDBG_H("No rx or tx properties registered for iface %s\n", dev_name);
+			return IPACM_FAILURE;
+		}
 
 
-	len = sizeof(struct ipa_ioc_add_flt_rule_after) + sizeof(struct ipa_flt_rule_add);
-	pFilteringTable = (struct ipa_ioc_add_flt_rule_after*)malloc(len);
-	if (!pFilteringTable)
-	{
-		IPACMERR("Failed to allocate ipa_ioc_add_flt_rule_after memory...\n");
-		return IPACM_FAILURE;
-	}
-	memset(pFilteringTable, 0, len);
+		len = sizeof(struct ipa_ioc_add_flt_rule_after) + sizeof(struct ipa_flt_rule_add);
+		pFilteringTable = (struct ipa_ioc_add_flt_rule_after*)malloc(len);
+		if (!pFilteringTable)
+		{
+			IPACMERR("Failed to allocate ipa_ioc_add_flt_rule_after memory...\n");
+			return IPACM_FAILURE;
+		}
+		memset(pFilteringTable, 0, len);
 
-	/* add mac based rule*/
-	pFilteringTable->commit = 1;
-	pFilteringTable->ep = rx_prop->rx[0].src_pipe;
-	pFilteringTable->ip = iptype;
-	pFilteringTable->num_rules = 1;
-	pFilteringTable->add_after_hdl = eth_bridge_flt_rule_offset[iptype];
+		/* add mac based rule*/
+		pFilteringTable->commit = 1;
+		pFilteringTable->ep = rx_prop->rx[0].src_pipe;
+		pFilteringTable->ip = iptype;
+		pFilteringTable->num_rules = 1;
+		pFilteringTable->add_after_hdl = eth_bridge_flt_rule_offset[iptype];
 
-	memset(&flt_rule_entry, 0, sizeof(flt_rule_entry));
-	flt_rule_entry.at_rear = 1;
+		memset(&flt_rule_entry, 0, sizeof(flt_rule_entry));
+		flt_rule_entry.at_rear = 1;
 
-	flt_rule_entry.rule.retain_hdr = 0;
-	flt_rule_entry.rule.to_uc = 0;
-	flt_rule_entry.rule.action = IPA_PASS_TO_ROUTING;
-	flt_rule_entry.rule.eq_attrib_type = 0;
-	flt_rule_entry.rule.rt_tbl_hdl = rt_tbl_hdl;
-	flt_rule_entry.rule.hashable = true;
+		flt_rule_entry.rule.retain_hdr = 0;
+		flt_rule_entry.rule.to_uc = 0;
+		flt_rule_entry.rule.action = IPA_PASS_TO_ROUTING;
+		flt_rule_entry.rule.eq_attrib_type = 0;
+		flt_rule_entry.rule.rt_tbl_hdl = rt_tbl_hdl;
+		flt_rule_entry.rule.hashable = true;
 
-	memcpy(&flt_rule_entry.rule.attrib, &rx_prop->rx[0].attrib, sizeof(flt_rule_entry.rule.attrib));
-	if(tx_prop->tx[0].hdr_l2_type == IPA_HDR_L2_ETHERNET_II)
-	{
-		flt_rule_entry.rule.attrib.attrib_mask |= IPA_FLT_MAC_DST_ADDR_ETHER_II;
+		memcpy(&flt_rule_entry.rule.attrib, &rx_prop->rx[0].attrib, sizeof(flt_rule_entry.rule.attrib));
+		if(tx_prop->tx[0].hdr_l2_type == IPA_HDR_L2_ETHERNET_II)
+		{
+			flt_rule_entry.rule.attrib.attrib_mask |= IPA_FLT_MAC_DST_ADDR_ETHER_II;
+		}
+		else
+		{
+			flt_rule_entry.rule.attrib.attrib_mask |= IPA_FLT_MAC_DST_ADDR_802_3;
+		}
+
+		memcpy(flt_rule_entry.rule.attrib.dst_mac_addr, mac, sizeof(flt_rule_entry.rule.attrib.dst_mac_addr));
+		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));
+#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;
+			goto end;
+		}
+		*flt_rule_hdl = pFilteringTable->rules[0].flt_rule_hdl;
+
+end:
+		free(pFilteringTable);
 	}
 	else
 	{
-		flt_rule_entry.rule.attrib.attrib_mask |= IPA_FLT_MAC_DST_ADDR_802_3;
+		IPACMDBG_H("Received client MAC 0x%02x%02x%02x%02x%02x%02x.\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+		IPACMDBG_H("Not support rt_tbl_hdl %d flt_rule_hdl %p ip-type %d\n", rt_tbl_hdl, flt_rule_hdl, iptype);
 	}
-
-	memcpy(flt_rule_entry.rule.attrib.dst_mac_addr, mac, sizeof(flt_rule_entry.rule.attrib.dst_mac_addr));
-	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))
-	{
-		IPACMERR("Failed to add client filtering rules.\n");
-		res = IPACM_FAILURE;
-		goto end;
-	}
-	*flt_rule_hdl = pFilteringTable->rules[0].flt_rule_hdl;
-
-end:
-	free(pFilteringTable);
-#else
-	IPACMDBG_H("Received client MAC 0x%02x%02x%02x%02x%02x%02x.\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
-	IPACMDBG_H("Not support rt_tbl_hdl %d flt_rule_hdl %p ip-type %d\n", rt_tbl_hdl, flt_rule_hdl, iptype);
-#endif
 	return res;
 }
 
@@ -5628,78 +5904,79 @@
 	struct ipa_ioc_add_flt_rule_after *pFilteringTable = NULL;
 	ipa_ioc_get_rt_tbl rt_tbl;
 
-#ifdef FEATURE_IPA_V3
-	if (rx_prop == NULL || tx_prop == NULL)
+	if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
 	{
-		IPACMDBG_H("No rx or tx properties registered for iface %s\n", dev_name);
-		return IPACM_FAILURE;
-	}
+		if (rx_prop == NULL || tx_prop == NULL)
+		{
+			IPACMDBG_H("No rx or tx properties registered for iface %s\n", dev_name);
+			return IPACM_FAILURE;
+		}
 
-	len = sizeof(struct ipa_ioc_add_flt_rule_after) + sizeof(struct ipa_flt_rule_add);
-	pFilteringTable = (struct ipa_ioc_add_flt_rule_after*)malloc(len);
-	if (!pFilteringTable)
-	{
-		IPACMERR("Failed to allocate ipa_ioc_add_flt_rule_after memory...\n");
-		return IPACM_FAILURE;
-	}
-	memset(pFilteringTable, 0, len);
+		len = sizeof(struct ipa_ioc_add_flt_rule_after) + sizeof(struct ipa_flt_rule_add);
+		pFilteringTable = (struct ipa_ioc_add_flt_rule_after*)malloc(len);
+		if (!pFilteringTable)
+		{
+			IPACMERR("Failed to allocate ipa_ioc_add_flt_rule_after memory...\n");
+			return IPACM_FAILURE;
+		}
+		memset(pFilteringTable, 0, len);
 
-	pFilteringTable->commit = 1;
-	pFilteringTable->ep = rx_prop->rx[0].src_pipe;
-	pFilteringTable->ip = IPA_IP_v6;
-	pFilteringTable->num_rules = 1;
-	pFilteringTable->add_after_hdl = eth_bridge_flt_rule_offset[IPA_IP_v6];
+		pFilteringTable->commit = 1;
+		pFilteringTable->ep = rx_prop->rx[0].src_pipe;
+		pFilteringTable->ip = IPA_IP_v6;
+		pFilteringTable->num_rules = 1;
+		pFilteringTable->add_after_hdl = eth_bridge_flt_rule_offset[IPA_IP_v6];
 
-	fd_ipa = open(IPA_DEVICE_NAME, O_RDWR);
-	if(fd_ipa == 0)
-	{
-		IPACMERR("Failed to open %s\n",IPA_DEVICE_NAME);
-		free(pFilteringTable);
-		return IPACM_FAILURE;
-	}
+		fd_ipa = open(IPA_DEVICE_NAME, O_RDWR);
+		if(fd_ipa == 0)
+		{
+			IPACMERR("Failed to open %s\n",IPA_DEVICE_NAME);
+			free(pFilteringTable);
+			return IPACM_FAILURE;
+		}
 
-	rt_tbl.ip = IPA_IP_v6;
-	snprintf(rt_tbl.name, sizeof(rt_tbl.name), "l2tp");
-	rt_tbl.name[IPA_RESOURCE_NAME_MAX-1] = '\0';
-	IPACMDBG_H("This flt rule points to rt tbl %s.\n", rt_tbl.name);
-	if(m_routing.GetRoutingTable(&rt_tbl) == false)
-	{
-		IPACMERR("Failed to get routing table from name\n");
+		rt_tbl.ip = IPA_IP_v6;
+		snprintf(rt_tbl.name, sizeof(rt_tbl.name), "l2tp");
+		rt_tbl.name[IPA_RESOURCE_NAME_MAX-1] = '\0';
+		IPACMDBG_H("This flt rule points to rt tbl %s.\n", rt_tbl.name);
+		if(m_routing.GetRoutingTable(&rt_tbl) == false)
+		{
+			IPACMERR("Failed to get routing table from name\n");
+			free(pFilteringTable);
+			close(fd_ipa);
+			return IPACM_FAILURE;
+		}
+
+		memset(&flt_rule_entry, 0, sizeof(flt_rule_entry));
+		flt_rule_entry.at_rear = 1;
+
+		flt_rule_entry.rule.retain_hdr = 0;
+		flt_rule_entry.rule.to_uc = 0;
+		flt_rule_entry.rule.action = IPA_PASS_TO_ROUTING;
+		flt_rule_entry.rule.eq_attrib_type = 0;
+		flt_rule_entry.rule.rt_tbl_hdl = rt_tbl.hdl;
+		flt_rule_entry.rule.hashable = false;	//ETH->WLAN direction rules need to be non-hashable due to encapsulation
+
+		memcpy(&flt_rule_entry.rule.attrib, &rx_prop->rx[0].attrib, sizeof(flt_rule_entry.rule.attrib));
+
+		/* flt rule is matching dst MAC within 62 bytes header */
+		flt_rule_entry.rule.attrib.attrib_mask |= IPA_FLT_MAC_DST_ADDR_L2TP;
+		memset(flt_rule_entry.rule.attrib.dst_mac_addr_mask, 0xFF, sizeof(flt_rule_entry.rule.attrib.dst_mac_addr_mask));
+		memcpy(flt_rule_entry.rule.attrib.dst_mac_addr, dst_mac, sizeof(flt_rule_entry.rule.attrib.dst_mac_addr));
+
+		memcpy(&(pFilteringTable->rules[0]), &flt_rule_entry, sizeof(flt_rule_entry));
+		if(m_filtering.AddFilteringRuleAfter(pFilteringTable) == false)
+		{
+			IPACMERR("Failed to add client filtering rules.\n");
+			free(pFilteringTable);
+			close(fd_ipa);
+			return IPACM_FAILURE;
+		}
+		*flt_rule_hdl = pFilteringTable->rules[0].flt_rule_hdl;
+
 		free(pFilteringTable);
 		close(fd_ipa);
-		return IPACM_FAILURE;
 	}
-
-	memset(&flt_rule_entry, 0, sizeof(flt_rule_entry));
-	flt_rule_entry.at_rear = 1;
-
-	flt_rule_entry.rule.retain_hdr = 0;
-	flt_rule_entry.rule.to_uc = 0;
-	flt_rule_entry.rule.action = IPA_PASS_TO_ROUTING;
-	flt_rule_entry.rule.eq_attrib_type = 0;
-	flt_rule_entry.rule.rt_tbl_hdl = rt_tbl.hdl;
-	flt_rule_entry.rule.hashable = false;	//ETH->WLAN direction rules need to be non-hashable due to encapsulation
-
-	memcpy(&flt_rule_entry.rule.attrib, &rx_prop->rx[0].attrib, sizeof(flt_rule_entry.rule.attrib));
-
-	/* flt rule is matching dst MAC within 62 bytes header */
-	flt_rule_entry.rule.attrib.attrib_mask |= IPA_FLT_MAC_DST_ADDR_L2TP;
-	memset(flt_rule_entry.rule.attrib.dst_mac_addr_mask, 0xFF, sizeof(flt_rule_entry.rule.attrib.dst_mac_addr_mask));
-	memcpy(flt_rule_entry.rule.attrib.dst_mac_addr, dst_mac, sizeof(flt_rule_entry.rule.attrib.dst_mac_addr));
-
-	memcpy(&(pFilteringTable->rules[0]), &flt_rule_entry, sizeof(flt_rule_entry));
-	if(m_filtering.AddFilteringRuleAfter(pFilteringTable) == false)
-	{
-		IPACMERR("Failed to add client filtering rules.\n");
-		free(pFilteringTable);
-		close(fd_ipa);
-		return IPACM_FAILURE;
-	}
-	*flt_rule_hdl = pFilteringTable->rules[0].flt_rule_hdl;
-
-	free(pFilteringTable);
-	close(fd_ipa);
-#endif
 	return IPACM_SUCCESS;
 }
 
@@ -5723,122 +6000,123 @@
 	struct ipa_ioc_add_flt_rule_after *pFilteringTable = NULL;
 	ipa_ioc_get_rt_tbl rt_tbl;
 
-#ifdef FEATURE_IPA_V3
-	if (rx_prop == NULL || tx_prop == NULL)
+	if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
 	{
-		IPACMDBG_H("No rx or tx properties registered for iface %s\n", dev_name);
-		return IPACM_FAILURE;
-	}
+		if (rx_prop == NULL || tx_prop == NULL)
+		{
+			IPACMDBG_H("No rx or tx properties registered for iface %s\n", dev_name);
+			return IPACM_FAILURE;
+		}
 
-	IPACMDBG_H("Dst client MAC 0x%02x%02x%02x%02x%02x%02x.\n", dst_mac[0], dst_mac[1],
-		dst_mac[2], dst_mac[3], dst_mac[4], dst_mac[5]);
+		IPACMDBG_H("Dst client MAC 0x%02x%02x%02x%02x%02x%02x.\n", dst_mac[0], dst_mac[1],
+			dst_mac[2], dst_mac[3], dst_mac[4], dst_mac[5]);
 
-	len = sizeof(struct ipa_ioc_add_flt_rule_after) + sizeof(struct ipa_flt_rule_add);
-	pFilteringTable = (struct ipa_ioc_add_flt_rule_after*)malloc(len);
-	if (!pFilteringTable)
-	{
-		IPACMERR("Failed to allocate ipa_ioc_add_flt_rule_after memory...\n");
-		return IPACM_FAILURE;
-	}
-	memset(pFilteringTable, 0, len);
+		len = sizeof(struct ipa_ioc_add_flt_rule_after) + sizeof(struct ipa_flt_rule_add);
+		pFilteringTable = (struct ipa_ioc_add_flt_rule_after*)malloc(len);
+		if (!pFilteringTable)
+		{
+			IPACMERR("Failed to allocate ipa_ioc_add_flt_rule_after memory...\n");
+			return IPACM_FAILURE;
+		}
+		memset(pFilteringTable, 0, len);
 
-	pFilteringTable->commit = 1;
-	pFilteringTable->ep = rx_prop->rx[0].src_pipe;
-	pFilteringTable->ip = iptype;
-	pFilteringTable->num_rules = 1;
-	pFilteringTable->add_after_hdl = eth_bridge_flt_rule_offset[iptype];
+		pFilteringTable->commit = 1;
+		pFilteringTable->ep = rx_prop->rx[0].src_pipe;
+		pFilteringTable->ip = iptype;
+		pFilteringTable->num_rules = 1;
+		pFilteringTable->add_after_hdl = eth_bridge_flt_rule_offset[iptype];
 
-	/* =========== add first pass flt rule (match dst MAC) ============= */
-	rt_tbl.ip = iptype;
-	snprintf(rt_tbl.name, sizeof(rt_tbl.name), "l2tp");
-	IPACMDBG_H("This flt rule points to rt tbl %s.\n", rt_tbl.name);
+		/* =========== add first pass flt rule (match dst MAC) ============= */
+		rt_tbl.ip = iptype;
+		snprintf(rt_tbl.name, sizeof(rt_tbl.name), "l2tp");
+		IPACMDBG_H("This flt rule points to rt tbl %s.\n", rt_tbl.name);
 
-	if(m_routing.GetRoutingTable(&rt_tbl) == false)
-	{
-		IPACMERR("Failed to get routing table.\n");
-		return IPACM_FAILURE;
-	}
+		if(m_routing.GetRoutingTable(&rt_tbl) == false)
+		{
+			IPACMERR("Failed to get routing table.\n");
+			return IPACM_FAILURE;
+		}
 
-	memset(&flt_rule_entry, 0, sizeof(flt_rule_entry));
-	flt_rule_entry.at_rear = 1;
+		memset(&flt_rule_entry, 0, sizeof(flt_rule_entry));
+		flt_rule_entry.at_rear = 1;
 
-	flt_rule_entry.rule.retain_hdr = 0;
-	flt_rule_entry.rule.to_uc = 0;
-	flt_rule_entry.rule.action = IPA_PASS_TO_ROUTING;
-	flt_rule_entry.rule.eq_attrib_type = 0;
-	flt_rule_entry.rule.rt_tbl_hdl = rt_tbl.hdl;
-	flt_rule_entry.rule.hashable = false;	//WLAN->ETH direction rules are set to non-hashable to keep consistent with the other direction
+		flt_rule_entry.rule.retain_hdr = 0;
+		flt_rule_entry.rule.to_uc = 0;
+		flt_rule_entry.rule.action = IPA_PASS_TO_ROUTING;
+		flt_rule_entry.rule.eq_attrib_type = 0;
+		flt_rule_entry.rule.rt_tbl_hdl = rt_tbl.hdl;
+		flt_rule_entry.rule.hashable = false;	//WLAN->ETH direction rules are set to non-hashable to keep consistent with the other direction
 
-	memcpy(&flt_rule_entry.rule.attrib, &rx_prop->rx[0].attrib, sizeof(flt_rule_entry.rule.attrib));
-	if(tx_prop->tx[0].hdr_l2_type == IPA_HDR_L2_ETHERNET_II)
-	{
-		flt_rule_entry.rule.attrib.attrib_mask |= IPA_FLT_MAC_DST_ADDR_ETHER_II;
-	}
-	else
-	{
-		flt_rule_entry.rule.attrib.attrib_mask |= IPA_FLT_MAC_DST_ADDR_802_3;
-	}
+		memcpy(&flt_rule_entry.rule.attrib, &rx_prop->rx[0].attrib, sizeof(flt_rule_entry.rule.attrib));
+		if(tx_prop->tx[0].hdr_l2_type == IPA_HDR_L2_ETHERNET_II)
+		{
+			flt_rule_entry.rule.attrib.attrib_mask |= IPA_FLT_MAC_DST_ADDR_ETHER_II;
+		}
+		else
+		{
+			flt_rule_entry.rule.attrib.attrib_mask |= IPA_FLT_MAC_DST_ADDR_802_3;
+		}
 
-	memcpy(flt_rule_entry.rule.attrib.dst_mac_addr, dst_mac, sizeof(flt_rule_entry.rule.attrib.dst_mac_addr));
-	memset(flt_rule_entry.rule.attrib.dst_mac_addr_mask, 0xFF, sizeof(flt_rule_entry.rule.attrib.dst_mac_addr_mask));
+		memcpy(flt_rule_entry.rule.attrib.dst_mac_addr, dst_mac, sizeof(flt_rule_entry.rule.attrib.dst_mac_addr));
+		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))
-	{
-		IPACMERR("Failed to add first pass filtering rules.\n");
+		memcpy(&(pFilteringTable->rules[0]), &flt_rule_entry, sizeof(flt_rule_entry));
+		if (false == m_filtering.AddFilteringRuleAfter(pFilteringTable))
+		{
+			IPACMERR("Failed to add first pass filtering rules.\n");
+			free(pFilteringTable);
+			return IPACM_FAILURE;
+		}
+		*first_pass_flt_rule_hdl = pFilteringTable->rules[0].flt_rule_hdl;
+
+		/* =========== add second pass flt rule (match VLAN interface IPv6 address at client side) ============= */
+		if(*second_pass_flt_rule_hdl != 0)
+		{
+			IPACMDBG_H("Second pass flt rule was added before, return.\n");
+			free(pFilteringTable);
+			return IPACM_SUCCESS;
+		}
+
+		rt_tbl.ip = IPA_IP_v6;
+		snprintf(rt_tbl.name, sizeof(rt_tbl.name), "l2tp");
+		IPACMDBG_H("This flt rule points to rt tbl %s.\n", rt_tbl.name);
+
+		if(m_routing.GetRoutingTable(&rt_tbl) == false)
+		{
+			IPACMERR("Failed to get routing table.\n");
+			return IPACM_FAILURE;
+		}
+
+		pFilteringTable->ip = IPA_IP_v6;
+		pFilteringTable->add_after_hdl = eth_bridge_flt_rule_offset[IPA_IP_v6];
+
+		memset(&flt_rule_entry, 0, sizeof(flt_rule_entry));
+		flt_rule_entry.at_rear = 1;
+
+		flt_rule_entry.rule.retain_hdr = 0;
+		flt_rule_entry.rule.to_uc = 0;
+		flt_rule_entry.rule.action = IPA_PASS_TO_ROUTING;
+		flt_rule_entry.rule.eq_attrib_type = 0;
+		flt_rule_entry.rule.rt_tbl_hdl = rt_tbl.hdl;
+		flt_rule_entry.rule.hashable = false;	//WLAN->ETH direction rules are set to non-hashable to keep consistent with the other direction
+
+		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_DST_ADDR;
+
+		memcpy(flt_rule_entry.rule.attrib.u.v6.dst_addr, vlan_client_ipv6_addr, sizeof(flt_rule_entry.rule.attrib.u.v6.dst_addr));
+		memset(flt_rule_entry.rule.attrib.u.v6.dst_addr_mask, 0xFF, sizeof(flt_rule_entry.rule.attrib.u.v6.dst_addr_mask));
+
+		memcpy(&(pFilteringTable->rules[0]), &flt_rule_entry, sizeof(flt_rule_entry));
+		if (false == m_filtering.AddFilteringRuleAfter(pFilteringTable))
+		{
+			IPACMERR("Failed to add client filtering rules.\n");
+			free(pFilteringTable);
+			return IPACM_FAILURE;
+		}
+		*second_pass_flt_rule_hdl = pFilteringTable->rules[0].flt_rule_hdl;
+
 		free(pFilteringTable);
-		return IPACM_FAILURE;
 	}
-	*first_pass_flt_rule_hdl = pFilteringTable->rules[0].flt_rule_hdl;
-
-	/* =========== add second pass flt rule (match VLAN interface IPv6 address at client side) ============= */
-	if(*second_pass_flt_rule_hdl != 0)
-	{
-		IPACMDBG_H("Second pass flt rule was added before, return.\n");
-		free(pFilteringTable);
-		return IPACM_SUCCESS;
-	}
-
-	rt_tbl.ip = IPA_IP_v6;
-	snprintf(rt_tbl.name, sizeof(rt_tbl.name), "l2tp");
-	IPACMDBG_H("This flt rule points to rt tbl %s.\n", rt_tbl.name);
-
-	if(m_routing.GetRoutingTable(&rt_tbl) == false)
-	{
-		IPACMERR("Failed to get routing table.\n");
-		return IPACM_FAILURE;
-	}
-
-	pFilteringTable->ip = IPA_IP_v6;
-	pFilteringTable->add_after_hdl = eth_bridge_flt_rule_offset[IPA_IP_v6];
-
-	memset(&flt_rule_entry, 0, sizeof(flt_rule_entry));
-	flt_rule_entry.at_rear = 1;
-
-	flt_rule_entry.rule.retain_hdr = 0;
-	flt_rule_entry.rule.to_uc = 0;
-	flt_rule_entry.rule.action = IPA_PASS_TO_ROUTING;
-	flt_rule_entry.rule.eq_attrib_type = 0;
-	flt_rule_entry.rule.rt_tbl_hdl = rt_tbl.hdl;
-	flt_rule_entry.rule.hashable = false;	//WLAN->ETH direction rules are set to non-hashable to keep consistent with the other direction
-
-	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_DST_ADDR;
-
-	memcpy(flt_rule_entry.rule.attrib.u.v6.dst_addr, vlan_client_ipv6_addr, sizeof(flt_rule_entry.rule.attrib.u.v6.dst_addr));
-	memset(flt_rule_entry.rule.attrib.u.v6.dst_addr_mask, 0xFF, sizeof(flt_rule_entry.rule.attrib.u.v6.dst_addr_mask));
-
-	memcpy(&(pFilteringTable->rules[0]), &flt_rule_entry, sizeof(flt_rule_entry));
-	if (false == m_filtering.AddFilteringRuleAfter(pFilteringTable))
-	{
-		IPACMERR("Failed to add client filtering rules.\n");
-		free(pFilteringTable);
-		return IPACM_FAILURE;
-	}
-	*second_pass_flt_rule_hdl = pFilteringTable->rules[0].flt_rule_hdl;
-
-	free(pFilteringTable);
-#endif
 	return IPACM_SUCCESS;
 }
 
@@ -5892,6 +6170,7 @@
 	int len;
 	struct ipa_flt_rule_add flt_rule_entry;
 	ipa_ioc_add_flt_rule *m_pFilteringTable;
+	bool result;
 
 	if(rx_prop == NULL)
 	{
@@ -5936,8 +6215,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);
@@ -5997,7 +6288,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");
@@ -6009,3 +6300,206 @@
 	free(m_pFilteringTable);
 	return IPACM_SUCCESS;
 }
+
+int IPACM_Lan::add_connection(int client_index, int v6_num)
+{
+	int len, res = IPACM_SUCCESS;
+	uint8_t mux_id;
+	ipa_ioc_add_flt_rule *pFilteringTable = NULL;
+	int fd;
+
+	mux_id = IPACM_Iface::ipacmcfg->GetQmapId();
+	/* contruct filter rules to pcie modem */
+	struct ipa_flt_rule_add flt_rule_entry;
+	ipa_ioc_generate_flt_eq flt_eq;
+
+	IPACMDBG("\n");
+	len = sizeof(struct ipa_ioc_add_flt_rule) + 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_v6;
+	pFilteringTable->num_rules = (uint8_t)1;
+
+	/* Configuring Filtering Rule */
+	memset(&flt_rule_entry, 0, sizeof(struct ipa_flt_rule_add));
+	flt_rule_entry.at_rear = true;
+	flt_rule_entry.flt_rule_hdl = -1;
+	flt_rule_entry.status = -1;
+
+	flt_rule_entry.rule.retain_hdr = 1;
+	flt_rule_entry.rule.to_uc = 0;
+	flt_rule_entry.rule.eq_attrib_type = 1;
+	flt_rule_entry.rule.action = IPA_PASS_TO_ROUTING;
+	if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+		flt_rule_entry.rule.hashable = true;
+	flt_rule_entry.rule.attrib.attrib_mask |= IPA_FLT_DST_ADDR;
+	flt_rule_entry.rule.attrib.u.v6.dst_addr[0] = get_client_memptr(eth_client, client_index)->v6_addr[v6_num][0];
+	flt_rule_entry.rule.attrib.u.v6.dst_addr[1] = get_client_memptr(eth_client, client_index)->v6_addr[v6_num][1];
+	flt_rule_entry.rule.attrib.u.v6.dst_addr[2] = get_client_memptr(eth_client, client_index)->v6_addr[v6_num][2];
+	flt_rule_entry.rule.attrib.u.v6.dst_addr[3] = get_client_memptr(eth_client, client_index)->v6_addr[v6_num][3];
+	flt_rule_entry.rule.attrib.u.v6.dst_addr_mask[0] = 0xFFFFFFFF;
+	flt_rule_entry.rule.attrib.u.v6.dst_addr_mask[1] = 0xFFFFFFFF;
+	flt_rule_entry.rule.attrib.u.v6.dst_addr_mask[2] = 0xFFFFFFFF;
+	flt_rule_entry.rule.attrib.u.v6.dst_addr_mask[3] = 0xFFFFFFFF;
+
+	IPACMDBG_H("ipv6 address got: 0x%x:%x:%x:%x\n", get_client_memptr(eth_client, client_index)->v6_addr[v6_num][0],
+		get_client_memptr(eth_client, client_index)->v6_addr[v6_num][1],
+		get_client_memptr(eth_client, client_index)->v6_addr[v6_num][2],
+		get_client_memptr(eth_client, client_index)->v6_addr[v6_num][3]);
+
+	/* change to network order for modem */
+	change_to_network_order(IPA_IP_v6, &flt_rule_entry.rule.attrib);
+
+	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;
+
+	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))
+	{
+		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(&(pFilteringTable->rules[0]), &flt_rule_entry, sizeof(struct ipa_flt_rule_add));
+
+	if(false == m_filtering.AddOffloadFilteringRule(pFilteringTable, mux_id, 0))
+	{
+		IPACMERR("Failed to install WAN DL filtering table.\n");
+		res = IPACM_FAILURE;
+		goto fail;
+	}
+
+	get_client_memptr(eth_client, client_index)->v6_rt_rule_id[v6_num] = pFilteringTable->rules[0].flt_rule_hdl;
+	IPACMDBG_H("%d-st client v6_num %d: id handle 0x%x\n", client_index, v6_num, get_client_memptr(eth_client, client_index)->v6_rt_rule_id[v6_num]);
+fail:
+	close(fd);
+	if(pFilteringTable != NULL)
+	{
+		free(pFilteringTable);
+	}
+	return res;
+}
+
+int IPACM_Lan::del_connection(int client_index, int v6_num)
+{
+	int len, res = IPACM_SUCCESS;
+	ipa_ioc_del_flt_rule *pFilteringTable = NULL;
+
+	struct ipa_flt_rule_del flt_rule_entry;
+
+	IPACMDBG("\n");
+	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_v6;
+	pFilteringTable->num_hdls = (uint8_t)1;
+
+	/* Configuring Software-Routing Filtering Rule */
+	memset(&flt_rule_entry, 0, sizeof(struct ipa_flt_rule_del));
+	flt_rule_entry.hdl = get_client_memptr(eth_client, client_index)->v6_rt_rule_id[v6_num];
+
+	memcpy(&(pFilteringTable->hdl[0]), &flt_rule_entry, sizeof(struct ipa_flt_rule_del));
+
+	if(false == m_filtering.DelOffloadFilteringRule(pFilteringTable))
+	{
+		IPACMERR("Failed to install WAN DL filtering table.\n");
+		res = IPACM_FAILURE;
+		goto fail;
+	}
+	get_client_memptr(eth_client, client_index)->v6_rt_rule_id[v6_num] = 0;
+
+fail:
+	if(pFilteringTable != NULL)
+	{
+		free(pFilteringTable);
+	}
+	return res;
+}
+
+int IPACM_Lan::construct_mtu_rule(struct ipa_flt_rule *rule, ipa_ip_type iptype, uint16_t mtu)
+{
+	int res = IPACM_SUCCESS;
+	int fd;
+	ipa_ioc_generate_flt_eq flt_eq;
+
+	if (rule == NULL)
+	{
+		IPACMERR("rule is empty");
+		return IPACM_FAILURE;
+	}
+
+	if (mtu == 0)
+	{
+		IPACMERR("mtu is uninitialized");
+		return IPACM_FAILURE;
+	}
+
+	IPACMDBG_H("Adding MTU rule for iptype = %d\n", iptype);
+
+	rule->eq_attrib_type = 1;
+	rule->eq_attrib.rule_eq_bitmap = 0;
+	rule->action = IPA_PASS_TO_EXCEPTION;
+
+	/* generate eq */
+	memset(&flt_eq, 0, sizeof(flt_eq));
+	memcpy(&flt_eq.attrib, &rule->attrib, sizeof(flt_eq.attrib));
+	flt_eq.ip = iptype;
+
+	fd = open(IPA_DEVICE_NAME, O_RDWR);
+	if (fd < 0)
+	{
+		IPACMERR("Failed opening %s.\n", IPA_DEVICE_NAME);
+		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(&rule->eq_attrib,
+		&flt_eq.eq_attrib, sizeof(rule->eq_attrib));
+
+	//add IHL offsets
+	rule->eq_attrib.rule_eq_bitmap |= (1<<10);
+	rule->eq_attrib.num_ihl_offset_range_16 = 1;
+	if (iptype == IPA_IP_v4)
+		rule->eq_attrib.ihl_offset_range_16[0].offset = 0x82;
+	else
+		rule->eq_attrib.ihl_offset_range_16[0].offset = 0x84;
+	rule->eq_attrib.ihl_offset_range_16[0].range_low = mtu + 1;
+	rule->eq_attrib.ihl_offset_range_16[0].range_high = UINT16_MAX; //0xFFFF
+
+fail:
+	close(fd);
+	return res;
+}
diff --git a/ipacm/src/IPACM_Main.cpp b/ipacm/src/IPACM_Main.cpp
index 04f8a89..696f518 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;
@@ -240,6 +246,10 @@
 #endif
 	struct ipa_get_data_stats_resp_msg_v01 event_data_stats;
 	struct ipa_get_apn_data_stats_resp_msg_v01 event_network_stats;
+#ifdef IPA_RT_SUPPORT_COAL
+	struct ipa_coalesce_info coalesce_info;
+#endif
+
 #ifdef FEATURE_IPACM_HAL
 	IPACM_OffloadManager* OffloadMng;
 #endif
@@ -821,6 +831,40 @@
 			evt_data.evt_data = mapping;
 			break;
 #endif
+#ifdef IPA_RT_SUPPORT_COAL
+		case IPA_COALESCE_ENABLE:
+			memcpy(&coalesce_info, buffer + sizeof(struct ipa_msg_meta), sizeof(struct ipa_coalesce_info));
+			IPACMDBG_H("Received IPA_COALESCE_ENABLE qmap-id:%d tcp:%d, udp%d\n",
+				coalesce_info.qmap_id, coalesce_info.tcp_enable, coalesce_info.udp_enable);
+			if (coalesce_info.qmap_id >=IPA_MAX_NUM_SW_PDNS)
+			{
+				IPACMERR("qmap_id (%d) beyond the Max range (%d), abort\n",
+				coalesce_info.qmap_id, IPA_MAX_NUM_SW_PDNS);
+				return NULL;
+			}
+			IPACM_Wan::coalesce_config(coalesce_info.qmap_id, coalesce_info.tcp_enable, coalesce_info.udp_enable);
+			/* Notify all LTE instance to do RSC configuration */
+			evt_data.event = IPA_COALESCE_NOTICE;
+			evt_data.evt_data = NULL;
+			break;
+
+		case IPA_COALESCE_DISABLE:
+			memcpy(&coalesce_info, buffer + sizeof(struct ipa_msg_meta), sizeof(struct ipa_coalesce_info));
+			IPACMDBG_H("Received IPA_COALESCE_DISABLE qmap-id:%d tcp:%d, udp%d\n",
+				coalesce_info.qmap_id, coalesce_info.tcp_enable, coalesce_info.udp_enable);
+			if (coalesce_info.qmap_id >=IPA_MAX_NUM_SW_PDNS)
+			{
+				IPACMERR("qmap_id (%d) beyond the Max range (%d), abort\n",
+				coalesce_info.qmap_id, IPA_MAX_NUM_SW_PDNS);
+				return NULL;
+			}
+			IPACM_Wan::coalesce_config(coalesce_info.qmap_id, false, false);
+			/* Notify all LTE instance to do RSC configuration */
+			evt_data.event = IPA_COALESCE_NOTICE;
+			evt_data.evt_data = NULL;
+			break;
+#endif
+
 		default:
 			IPACMDBG_H("Unhandled message type: %d\n", event_hdr.msg_type);
 			continue;
@@ -894,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
@@ -902,16 +951,18 @@
 	IPACMDBG_H(" START IPACM_OffloadManager and link to android framework\n");
 #endif
 
-#ifdef FEATURE_ETH_BRIDGE_LE
-	IPACM_LanToLan* lan2lan = IPACM_LanToLan::get_instance();
-	IPACMDBG_H("Staring IPACM_LanToLan instance %p\n", lan2lan);
-#endif
-
+	if (IPACM_Iface::ipacmcfg->isEthBridgingSupported())
+	{
+		IPACM_LanToLan* lan2lan = IPACM_LanToLan::get_instance();
+		IPACMDBG_H("Staring IPACM_LanToLan instance %p\n", lan2lan);
+	}
 	CtList = new IPACM_ConntrackListener();
 
 	IPACMDBG_H("Staring IPA main\n");
 	IPACMDBG_H("ipa_cmdq_successful\n");
 
+	/* reset coalesce settings */
+	IPACM_Wan::coalesce_config_reset();
 
 	RegisterForSignals();
 
@@ -1120,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_OffloadManager.cpp b/ipacm/src/IPACM_OffloadManager.cpp
index 1359d49..7919413 100644
--- a/ipacm/src/IPACM_OffloadManager.cpp
+++ b/ipacm/src/IPACM_OffloadManager.cpp
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
@@ -51,6 +51,7 @@
 
 /* NatApp class Implementation */
 IPACM_OffloadManager *IPACM_OffloadManager::pInstance = NULL;
+int IPACM_OffloadManager::num_offload_v4_tethered_iface = 0;
 
 IPACM_OffloadManager::IPACM_OffloadManager()
 {
@@ -237,44 +238,49 @@
 		/* copy to the cache */
 		for(int i = 0; i < MAX_EVENT_CACHE ;i++)
 		{
-			if(event_cache[latest_cache_index].valid == false)
+			if (latest_cache_index >= 0)
 			{
-				//do the copy
-				event_cache[latest_cache_index].valid = true;
-				event_cache[latest_cache_index].event = IPA_DOWNSTREAM_ADD;
-				memcpy(event_cache[latest_cache_index].dev_name, downstream_name, sizeof(event_cache[latest_cache_index].dev_name));
-				memcpy(&event_cache[latest_cache_index].prefix_cache, &prefix, sizeof(event_cache[latest_cache_index].prefix_cache));
-				if (prefix.fam == V4) {
-					IPACMDBG_H("cache event(%d) subnet info v4Addr (%x) v4Mask (%x) dev(%s) on entry (%d)\n",
-						event_cache[latest_cache_index].event,
-						event_cache[latest_cache_index].prefix_cache.v4Addr,
-						event_cache[latest_cache_index].prefix_cache.v4Mask,
-						event_cache[latest_cache_index].dev_name,
-						latest_cache_index);
-				} else {
-					IPACMDBG_H("cache event (%d) v6Addr: %08x:%08x:%08x:%08x \n",
-						event_cache[latest_cache_index].event,
-						event_cache[latest_cache_index].prefix_cache.v6Addr[0],
-						event_cache[latest_cache_index].prefix_cache.v6Addr[1],
-						event_cache[latest_cache_index].prefix_cache.v6Addr[2],
-						event_cache[latest_cache_index].prefix_cache.v6Addr[3]);
-					IPACMDBG_H("subnet v6Mask: %08x:%08x:%08x:%08x dev(%s) on entry(%d), \n",
-						event_cache[latest_cache_index].prefix_cache.v6Mask[0],
-						event_cache[latest_cache_index].prefix_cache.v6Mask[1],
-						event_cache[latest_cache_index].prefix_cache.v6Mask[2],
-						event_cache[latest_cache_index].prefix_cache.v6Mask[3],
-						event_cache[latest_cache_index].dev_name,
-						latest_cache_index);
+				if(event_cache[latest_cache_index].valid == false)
+				{
+					//do the copy
+					event_cache[latest_cache_index].valid = true;
+					event_cache[latest_cache_index].event = IPA_DOWNSTREAM_ADD;
+					memcpy(event_cache[latest_cache_index].dev_name, downstream_name,
+						sizeof(event_cache[latest_cache_index].dev_name));
+					memcpy(&event_cache[latest_cache_index].prefix_cache, &prefix,
+						sizeof(event_cache[latest_cache_index].prefix_cache));
+					if (prefix.fam == V4) {
+						IPACMDBG_H("cache event(%d) subnet info v4Addr (%x) v4Mask (%x) dev(%s) on entry (%d)\n",
+							event_cache[latest_cache_index].event,
+							event_cache[latest_cache_index].prefix_cache.v4Addr,
+							event_cache[latest_cache_index].prefix_cache.v4Mask,
+							event_cache[latest_cache_index].dev_name,
+							latest_cache_index);
+					} else {
+						IPACMDBG_H("cache event (%d) v6Addr: %08x:%08x:%08x:%08x \n",
+							event_cache[latest_cache_index].event,
+							event_cache[latest_cache_index].prefix_cache.v6Addr[0],
+							event_cache[latest_cache_index].prefix_cache.v6Addr[1],
+							event_cache[latest_cache_index].prefix_cache.v6Addr[2],
+							event_cache[latest_cache_index].prefix_cache.v6Addr[3]);
+						IPACMDBG_H("subnet v6Mask: %08x:%08x:%08x:%08x dev(%s) on entry(%d), \n",
+							event_cache[latest_cache_index].prefix_cache.v6Mask[0],
+							event_cache[latest_cache_index].prefix_cache.v6Mask[1],
+							event_cache[latest_cache_index].prefix_cache.v6Mask[2],
+							event_cache[latest_cache_index].prefix_cache.v6Mask[3],
+							event_cache[latest_cache_index].dev_name,
+							latest_cache_index);
+					}
+					latest_cache_index = (latest_cache_index + 1)% MAX_EVENT_CACHE;
+					break;
 				}
 				latest_cache_index = (latest_cache_index + 1)% MAX_EVENT_CACHE;
-				break;
 			}
-			latest_cache_index = (latest_cache_index + 1)% MAX_EVENT_CACHE;
 			if(i == MAX_EVENT_CACHE - 1)
 			{
 				IPACMDBG_H(" run out of event cache (%d)\n", i);
-		return FAIL_HARDWARE;
-	}
+				return FAIL_HARDWARE;
+			}
 		}
 
 		return SUCCESS;
@@ -412,37 +418,43 @@
 			/* copy to the cache */
 			for(int i = 0; i < MAX_EVENT_CACHE ;i++)
 			{
-				if(event_cache[latest_cache_index].valid == false)
+				if (latest_cache_index >= 0)
 				{
-					//do the copy
-					event_cache[latest_cache_index].valid = true;
-					event_cache[latest_cache_index].event = IPA_WAN_UPSTREAM_ROUTE_ADD_EVENT;
-					memcpy(event_cache[latest_cache_index].dev_name, upstream_name, sizeof(event_cache[latest_cache_index].dev_name));
-					memcpy(&event_cache[latest_cache_index].prefix_cache, &gw_addr_v4, sizeof(event_cache[latest_cache_index].prefix_cache));
-					memcpy(&event_cache[latest_cache_index].prefix_cache_v6, &gw_addr_v6, sizeof(event_cache[latest_cache_index].prefix_cache_v6));
-					if (gw_addr_v4.fam == V4) {
-						IPACMDBG_H("cache event(%d) ipv4 gateway: (%x) dev(%s) on entry (%d)\n",
-							event_cache[latest_cache_index].event,
-							event_cache[latest_cache_index].prefix_cache.v4Addr,
-							event_cache[latest_cache_index].dev_name,
-							latest_cache_index);
-		}
+					if(event_cache[latest_cache_index].valid == false)
+					{
+						//do the copy
+						event_cache[latest_cache_index].valid = true;
+						event_cache[latest_cache_index].event = IPA_WAN_UPSTREAM_ROUTE_ADD_EVENT;
+						memcpy(event_cache[latest_cache_index].dev_name, upstream_name,
+							sizeof(event_cache[latest_cache_index].dev_name));
+						memcpy(&event_cache[latest_cache_index].prefix_cache, &gw_addr_v4,
+							sizeof(event_cache[latest_cache_index].prefix_cache));
+						memcpy(&event_cache[latest_cache_index].prefix_cache_v6, &gw_addr_v6,
+							sizeof(event_cache[latest_cache_index].prefix_cache_v6));
+						if (gw_addr_v4.fam == V4) {
+							IPACMDBG_H("cache event(%d) ipv4 gateway: (%x) dev(%s) on entry (%d)\n",
+								event_cache[latest_cache_index].event,
+								event_cache[latest_cache_index].prefix_cache.v4Addr,
+								event_cache[latest_cache_index].dev_name,
+								latest_cache_index);
+						}
 
-					if (gw_addr_v6.fam == V6)
-		{
-						IPACMDBG_H("cache event (%d) ipv6 gateway: %08x:%08x:%08x:%08x dev(%s) on entry(%d)\n",
-							event_cache[latest_cache_index].event,
-							event_cache[latest_cache_index].prefix_cache_v6.v6Addr[0],
-							event_cache[latest_cache_index].prefix_cache_v6.v6Addr[1],
-							event_cache[latest_cache_index].prefix_cache_v6.v6Addr[2],
-							event_cache[latest_cache_index].prefix_cache_v6.v6Addr[3],
-							event_cache[latest_cache_index].dev_name,
-							latest_cache_index);
+						if (gw_addr_v6.fam == V6)
+						{
+							IPACMDBG_H("cache event (%d) ipv6 gateway: %08x:%08x:%08x:%08x dev(%s) on entry(%d)\n",
+								event_cache[latest_cache_index].event,
+								event_cache[latest_cache_index].prefix_cache_v6.v6Addr[0],
+								event_cache[latest_cache_index].prefix_cache_v6.v6Addr[1],
+								event_cache[latest_cache_index].prefix_cache_v6.v6Addr[2],
+								event_cache[latest_cache_index].prefix_cache_v6.v6Addr[3],
+								event_cache[latest_cache_index].dev_name,
+								latest_cache_index);
+						}
+						latest_cache_index = (latest_cache_index + 1)% MAX_EVENT_CACHE;
+						break;
 					}
 					latest_cache_index = (latest_cache_index + 1)% MAX_EVENT_CACHE;
-					break;
 				}
-				latest_cache_index = (latest_cache_index + 1)% MAX_EVENT_CACHE;
 				if(i == MAX_EVENT_CACHE - 1)
 				{
 					IPACMDBG_H(" run out of event cache (%d) \n", i);
@@ -566,6 +578,7 @@
 	memset(event_cache, 0, MAX_EVENT_CACHE*sizeof(framework_event_cache));
 	latest_cache_index = 0;
 	valid_ifaces.clear();
+	IPACM_OffloadManager::num_offload_v4_tethered_iface = 0;
 	return result;
 }
 
@@ -590,7 +603,7 @@
 		return FAIL_INPUT_CHECK;
 	}
 
-	IPACMDBG_H("SET_DATA_QUOTA %s %llu", quota.interface_name, (long long)mb);
+	IPACMDBG_H("SET_DATA_QUOTA %s %llu\n", quota.interface_name, (long long)mb);
 
 	rc = ioctl(fd, WAN_IOC_SET_DATA_QUOTA, &quota);
 
@@ -765,12 +778,14 @@
 bool IPACM_OffloadManager::search_framwork_cache(char * interface_name)
 {
 	bool rel = false;
+	bool cache_need = false;
 
 	/* IPACM needs to kee old FDs, can't clear */
 	IPACMDBG_H("check netdev(%s)\n", interface_name);
 
 	for(int i = 0; i < MAX_EVENT_CACHE ;i++)
 	{
+		cache_need = false;
 		if(event_cache[i].valid == true)
 		{
 			//do the compare
@@ -780,14 +795,40 @@
 			{
 				IPACMDBG_H("found netdev (%s) in entry (%d) with event (%d)\n", interface_name, i, event_cache[i].event);
 				/* post event again */
-				if (event_cache[i].event == IPA_DOWNSTREAM_ADD)
+				if (event_cache[i].event == IPA_DOWNSTREAM_ADD) {
+					/* check if downsteam netdev driver finished its configuration on IPA-HW for ipv4 and ipv6 */
+					if (event_cache[i].prefix_cache.fam == V4 && IPACM_Iface::ipacmcfg->CheckNatIfaces(event_cache[i].dev_name, IPA_IP_v4))
+						cache_need = true;
+					if (event_cache[i].prefix_cache.fam == V6 && IPACM_Iface::ipacmcfg->CheckNatIfaces(event_cache[i].dev_name, IPA_IP_v6))
+						cache_need = true;
+					if (cache_need) {
+						IPACMDBG_H("still need cache (%d), index (%d) ip-family (%d)\n", cache_need, i, event_cache[i].prefix_cache.fam);
+						break;
+					} else {
+						IPACMDBG_H("no need cache (%d), handling it event (%d)\n", cache_need, event_cache[i].event);
 					addDownstream(interface_name, event_cache[i].prefix_cache);
-				else if (event_cache[i].event == IPA_WAN_UPSTREAM_ROUTE_ADD_EVENT)
+					}
+				} else if (event_cache[i].event == IPA_WAN_UPSTREAM_ROUTE_ADD_EVENT) {
+					/* check if upstream netdev driver finished its configuration on IPA-HW for ipv4 and ipv6 */
+					if (event_cache[i].prefix_cache.fam == V4 && IPACM_Iface::ipacmcfg->CheckNatIfaces(event_cache[i].dev_name, IPA_IP_v4))
+						cache_need = true;
+					if (event_cache[i].prefix_cache_v6.fam == V6 && IPACM_Iface::ipacmcfg->CheckNatIfaces(event_cache[i].dev_name, IPA_IP_v6))
+						cache_need = true;
+					if (cache_need) {
+						IPACMDBG_H("still need cache (%d), index (%d)\n", cache_need, i);
+						break;
+					} else {
+						IPACMDBG_H("no need cache (%d), handling it event (%d)\n", cache_need, event_cache[i].event);
 					setUpstream(interface_name, event_cache[i].prefix_cache, event_cache[i].prefix_cache_v6);
-				else
-					IPACMERR("wrong event cached (%d)", event_cache[i].event);
+					}
+				} else {
+						IPACMERR("wrong event cached (%d) index (%d)\n", event_cache[i].event, i);
+				}
+
+				/* reset entry */
 				event_cache[i].valid = false;
 				rel = true;
+				IPACMDBG_H("reset entry (%d)", i);
 			}
 		}
 	}
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 278f9ac..847e08d 100644
--- a/ipacm/src/IPACM_Wan.cpp
+++ b/ipacm/src/IPACM_Wan.cpp
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2013, 2018 The Linux Foundation. All rights reserved.
+Copyright (c) 2013-2019 The Linux Foundation. All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
@@ -52,6 +52,7 @@
 #include "linux/ipa_qmi_service_v01.h"
 #ifdef FEATURE_IPACM_HAL
 #include "IPACM_OffloadManager.h"
+#include <IPACM_Netlink.h>
 #endif
 
 bool IPACM_Wan::wan_up = false;
@@ -69,7 +70,7 @@
 
 char IPACM_Wan::wan_up_dev_name[IF_NAME_LEN];
 
-bool IPACM_Wan::backhaul_is_sta_mode = false;
+ipacm_wan_iface_type IPACM_Wan::backhaul_mode = Q6_WAN;
 bool IPACM_Wan::is_ext_prop_set = false;
 
 int IPACM_Wan::num_ipv4_modem_pdn = 0;
@@ -77,6 +78,9 @@
 
 bool IPACM_Wan::embms_is_on = false;
 bool IPACM_Wan::backhaul_is_wan_bridge = false;
+bool IPACM_Wan::is_xlat = false;
+
+ipacm_coalesce IPACM_Wan::coalesce_enable_info[IPA_MAX_NUM_SW_PDNS];
 
 uint32_t IPACM_Wan::backhaul_ipv6_prefix[2];
 
@@ -88,6 +92,8 @@
 int	IPACM_Wan::ipa_if_num_tether_v6[IPA_MAX_IFACE_ENTRIES];
 #endif
 
+uint16_t IPACM_Wan::mtu_default_wan = DEFAULT_MTU_SIZE;
+
 IPACM_Wan::IPACM_Wan(int iface_index,
 	ipacm_wan_iface_type is_sta_mode,
 	uint8_t *mac_addr) : IPACM_Iface(iface_index)
@@ -107,7 +113,6 @@
 		wan_route_rule_v6_hdl_a5 = (uint32_t *)calloc(iface_query->num_tx_props, sizeof(uint32_t));
 		IPACMDBG_H("IPACM->IPACM_Wan(%d) constructor: Tx:%d\n", ipa_if_num, iface_query->num_tx_props);
 	}
-	m_is_sta_mode = is_sta_mode;
 
 	wan_v4_addr_set = false;
 	wan_v4_addr_gw_set = false;
@@ -127,20 +132,47 @@
 	ext_prop = NULL;
 	is_ipv6_frag_firewall_flt_rule_installed = false;
 	ipv6_frag_firewall_flt_rule_hdl = 0;
+	mtu_size = DEFAULT_MTU_SIZE;
 
 	num_wan_client = 0;
 	header_name_count = 0;
 	memset(invalid_mac, 0, sizeof(invalid_mac));
 
-	is_xlat = false;
+	is_xlat_local = false;
 	hdr_hdl_dummy_v6 = 0;
 	hdr_proc_hdl_dummy_v6 = 0;
 	is_default_gateway = false;
 	m_fd_ipa = 0;
 	wan_client_len = 0;
+	m_is_sta_mode = is_sta_mode;
 
 	if(iface_query != NULL)
 	{
+		IPACMDBG_H("index:%d constructor: Tx properties:%d\n", iface_index, iface_query->num_tx_props);
+
+		if(is_sta_mode == Q6_WAN)
+		{
+			query_ext_prop();
+
+			if ((iface_query->num_ext_props == 1) && ((ext_prop != NULL) && ext_prop->ext[0].ip == IPA_IP_MAX))
+			{
+				/* only has one ext properties with IP_MAX type, will be the mhi-modem */
+				IPACMDBG_H("One extended property for iface %s, replace %d to Q6_MHI_WAN\n", dev_name, is_sta_mode);
+				m_is_sta_mode = Q6_MHI_WAN;
+			}
+			else
+			{
+				IPACMDBG_H("The new WAN interface is modem.\n");
+				m_is_sta_mode = is_sta_mode;
+				is_default_gateway = false;
+			}
+		}
+		else
+		{
+			m_is_sta_mode = is_sta_mode;
+			IPACMDBG_H("The new WAN interface is WLAN STA.\n");
+		}
+
 		wan_client_len = (sizeof(ipa_wan_client)) + (iface_query->num_tx_props * sizeof(wan_client_rt_hdl));
 		wan_client = (ipa_wan_client *)calloc(IPA_MAX_NUM_WAN_CLIENTS, wan_client_len);
 		if (wan_client == NULL)
@@ -148,19 +180,11 @@
 			IPACMERR("unable to allocate memory\n");
 			return;
 		}
-		IPACMDBG_H("index:%d constructor: Tx properties:%d\n", iface_index, iface_query->num_tx_props);
-	}
-
-
-	if(m_is_sta_mode == Q6_WAN)
-	{
-		IPACMDBG_H("The new WAN interface is modem.\n");
-		is_default_gateway = false;
-		query_ext_prop();
 	}
 	else
 	{
-		IPACMDBG_H("The new WAN interface is WLAN STA.\n");
+		IPACMDBG_H("iface_query is empty.\n");
+		return;
 	}
 
 	m_fd_ipa = open(IPA_DEVICE_NAME, O_RDWR);
@@ -207,6 +231,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;
@@ -252,29 +277,11 @@
 		rt_rule->commit = 1;
 		rt_rule->num_rules = NUM_RULES;
 		rt_rule->ip = data->iptype;
+		/* setup RT rule for v6_lan table*/
 		strlcpy(rt_rule->rt_tbl_name, IPACM_Iface::ipacmcfg->rt_tbl_v6.name, sizeof(rt_rule->rt_tbl_name));
 
 		rt_rule_entry = &rt_rule->rules[0];
-		if(m_is_sta_mode == Q6_WAN)
-		{
-			strlcpy(hdr.name, tx_prop->tx[0].hdr_name, sizeof(hdr.name));
-			hdr.name[IPA_RESOURCE_NAME_MAX-1] = '\0';
-			if(m_header.GetHeaderHandle(&hdr) == false)
-			{
-				IPACMERR("Failed to get QMAP header.\n");
-				return IPACM_FAILURE;
-			}
-			rt_rule_entry->rule.hdr_hdl = hdr.hdl;
-		}
 		rt_rule_entry->at_rear = false;
-		if(m_is_sta_mode == Q6_WAN)
-		{
-			rt_rule_entry->rule.dst = IPA_CLIENT_APPS_WAN_CONS;
-		}
-		else
-		{
-			rt_rule_entry->rule.dst = IPA_CLIENT_APPS_LAN_CONS;
-		}
 		rt_rule_entry->rule.attrib.attrib_mask = IPA_FLT_DST_ADDR;
 		rt_rule_entry->rule.attrib.u.v6.dst_addr[0] = data->ipv6_addr[0];
 		rt_rule_entry->rule.attrib.u.v6.dst_addr[1] = data->ipv6_addr[1];
@@ -288,8 +295,91 @@
 		ipv6_addr[num_dft_rt_v6][1] = data->ipv6_addr[1];
 		ipv6_addr[num_dft_rt_v6][2] = data->ipv6_addr[2];
 		ipv6_addr[num_dft_rt_v6][3] = data->ipv6_addr[3];
-#ifdef FEATURE_IPA_V3
-		rt_rule_entry->rule.hashable = false;
+		if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+			rt_rule_entry->rule.hashable = false;
+		if(m_is_sta_mode == Q6_WAN)
+		{
+			strlcpy(hdr.name, tx_prop->tx[0].hdr_name, sizeof(hdr.name));
+			hdr.name[IPA_RESOURCE_NAME_MAX-1] = '\0';
+			if(m_header.GetHeaderHandle(&hdr) == false)
+			{
+				IPACMERR("Failed to get QMAP header.\n");
+				return IPACM_FAILURE;
+			}
+			rt_rule_entry->rule.hdr_hdl = hdr.hdl;
+			rt_rule_entry->rule.dst = IPA_CLIENT_APPS_WAN_CONS;
+			/* legacy default v4 rt-rule */
+#ifdef IPA_RT_SUPPORT_COAL
+			rt_rule_entry->rule.coalesce = false;
+#endif
+			/* legacy default v6 rt-rule */
+			if (false == m_routing.AddRoutingRule(rt_rule))
+			{
+				IPACMERR("Routing rule addition failed!\n");
+				res = IPACM_FAILURE;
+				goto fail;
+			}
+			else if (rt_rule_entry->status)
+			{
+				IPACMERR("rt rule adding failed. Result=%d\n", rt_rule_entry->status);
+				res = rt_rule_entry->status;
+				goto fail;
+			}
+			dft_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES + 2*num_dft_rt_v6] = rt_rule_entry->rt_rule_hdl;
+
+			/* setup same rule for v6_wan table*/
+			strlcpy(rt_rule->rt_tbl_name, IPACM_Iface::ipacmcfg->rt_tbl_wan_v6.name, sizeof(rt_rule->rt_tbl_name));
+			if (false == m_routing.AddRoutingRule(rt_rule))
+			{
+				IPACMERR("Routing rule addition failed!\n");
+				res = IPACM_FAILURE;
+				goto fail;
+		}
+			else if (rt_rule_entry->status)
+		{
+				IPACMERR("rt rule adding failed. Result=%d\n", rt_rule_entry->status);
+				res = rt_rule_entry->status;
+				goto fail;
+		}
+			dft_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES + 2*num_dft_rt_v6+1] = rt_rule_entry->rt_rule_hdl;
+
+			IPACMDBG_H("ipv6 wan iface rt-rule hdl=0x%x hdl=0x%x, entry: %d %d\n",
+					dft_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES + 2*num_dft_rt_v6],
+					dft_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES + 2*num_dft_rt_v6+1],
+					MAX_DEFAULT_v4_ROUTE_RULES + 2*num_dft_rt_v6,
+					MAX_DEFAULT_v4_ROUTE_RULES + 2*num_dft_rt_v6+1);
+			/* RSC TCP rule*/
+			rt_rule_entry->rule.attrib.attrib_mask |= IPA_FLT_NEXT_HDR;
+			rt_rule_entry->rule.attrib.u.v6.next_hdr = (uint8_t)IPACM_FIREWALL_IPPROTO_TCP;
+#ifdef IPA_RT_SUPPORT_COAL
+			if (IPACM_Wan::coalesce_enable_info[ext_prop->ext[0].mux_id].coalesce_tcp_enable)
+				rt_rule_entry->rule.coalesce = true;
+		else
+				rt_rule_entry->rule.coalesce = false;
+#endif
+			if (false == m_routing.AddRoutingRule(rt_rule))
+		{
+				IPACMERR("Routing rule addition failed!\n");
+				res = IPACM_FAILURE;
+				goto fail;
+			}
+			else if (rt_rule_entry->status)
+			{
+				IPACMERR("rsc tcp rt rule adding failed. Result=%d\n", rt_rule_entry->status);
+				res = rt_rule_entry->status;
+				goto fail;
+		}
+			dft_coalesce_rt_rule_hdl[2*MAX_DEFAULT_v4_ROUTE_RULES + 2*num_dft_rt_v6] = rt_rule_entry->rt_rule_hdl;
+			IPACMDBG_H("ipv6 wan iface rsc tcp rt-rule hdll=0x%x\n enable(%d), entry %d", dft_coalesce_rt_rule_hdl[2*MAX_DEFAULT_v4_ROUTE_RULES + 2*num_dft_rt_v6],
+				IPACM_Wan::coalesce_enable_info[ext_prop->ext[0].mux_id].coalesce_tcp_enable,
+				2*MAX_DEFAULT_v4_ROUTE_RULES + 2*num_dft_rt_v6);
+			/* RSB UDP rule*/
+			rt_rule_entry->rule.attrib.u.v6.next_hdr = (uint8_t)IPACM_FIREWALL_IPPROTO_UDP;
+#ifdef IPA_RT_SUPPORT_COAL
+			if (IPACM_Wan::coalesce_enable_info[ext_prop->ext[0].mux_id].coalesce_udp_enable)
+				rt_rule_entry->rule.coalesce = true;
+			else
+				rt_rule_entry->rule.coalesce = false;
 #endif
 		if (false == m_routing.AddRoutingRule(rt_rule))
 		{
@@ -299,6 +389,27 @@
 		}
 		else if (rt_rule_entry->status)
 		{
+				IPACMERR("rsb udp rt rule adding failed. Result=%d\n", rt_rule_entry->status);
+				res = rt_rule_entry->status;
+				goto fail;
+			}
+			dft_coalesce_rt_rule_hdl[2*MAX_DEFAULT_v4_ROUTE_RULES + 2*num_dft_rt_v6+1] = rt_rule_entry->rt_rule_hdl;
+			IPACMDBG_H("ipv6 wan iface rsb udp rt-rule hdll=0x%x\n enable(%d) entry %d", dft_coalesce_rt_rule_hdl[2*MAX_DEFAULT_v4_ROUTE_RULES + 2*num_dft_rt_v6+1],
+				IPACM_Wan::coalesce_enable_info[ext_prop->ext[0].mux_id].coalesce_udp_enable,
+				2*MAX_DEFAULT_v4_ROUTE_RULES + 2*num_dft_rt_v6+1);
+		}
+		else
+		{
+			rt_rule_entry->rule.dst = IPA_CLIENT_APPS_LAN_CONS;
+			/* legacy default v6 rt-rule */
+			if (false == m_routing.AddRoutingRule(rt_rule))
+			{
+				IPACMERR("Routing rule addition failed!\n");
+				res = IPACM_FAILURE;
+				goto fail;
+			}
+			else if (rt_rule_entry->status)
+			{
 			IPACMERR("rt rule adding failed. Result=%d\n", rt_rule_entry->status);
 			res = rt_rule_entry->status;
 			goto fail;
@@ -321,9 +432,12 @@
 		}
 		dft_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES + 2*num_dft_rt_v6+1] = rt_rule_entry->rt_rule_hdl;
 
-		IPACMDBG_H("ipv6 wan iface rt-rule hdl=0x%x hdl=0x%x, num_dft_rt_v6: %d \n",
+			IPACMDBG_H("ipv6 wan iface rt-rule hdl=0x%x hdl=0x%x, entry: %d  %d\n",
 				dft_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES + 2*num_dft_rt_v6],
-				dft_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES + 2*num_dft_rt_v6+1],num_dft_rt_v6);
+					dft_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES + 2*num_dft_rt_v6+1],
+					MAX_DEFAULT_v4_ROUTE_RULES + 2*num_dft_rt_v6,
+					MAX_DEFAULT_v4_ROUTE_RULES + 2*num_dft_rt_v6+1);
+		}
 
 		/* add default filtering rules when wan-iface get global v6-prefix */
 		if (num_dft_rt_v6 == 1)
@@ -335,6 +449,11 @@
 				IPACMDBG_H("Now the number of modem ipv6 pdn is %d.\n", num_ipv6_modem_pdn);
 				init_fl_rule_ex(data->iptype);
 			}
+			else if(m_is_sta_mode == Q6_MHI_WAN)
+			{
+				IPACMDBG_H(" Has rx/tx properties registered for iface %s, add for NATTING for ip-family %d \n", dev_name, IPA_IP_v6);
+				IPACM_Iface::ipacmcfg->AddNatIfaces(dev_name, IPA_IP_v6);
+			}
 			else
 			{
 				init_fl_rule(data->iptype);
@@ -371,9 +490,8 @@
 				flt_rule_entry.flt_rule_hdl = -1;
 				flt_rule_entry.status = -1;
 				flt_rule_entry.rule.action = IPA_PASS_TO_EXCEPTION;
-#ifdef FEATURE_IPA_V3
-				flt_rule_entry.rule.hashable = true;
-#endif
+				if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+					flt_rule_entry.rule.hashable = true;
 				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_DST_ADDR;
@@ -383,8 +501,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);
@@ -421,6 +551,21 @@
 			else
 			{
 				IPACMDBG_H(" device (%s) ipv4 addr is changed\n", dev_name);
+				/* Delete default Coalese v4 RT rule */
+				if (m_is_sta_mode == Q6_WAN) {
+					if (m_routing.DeleteRoutingHdl(dft_coalesce_rt_rule_hdl[0], IPA_IP_v4) == false)
+					{
+						IPACMERR("Routing old RSC TCP RT rule deletion failed!\n");
+						res = IPACM_FAILURE;
+						goto fail;
+					}
+					if (m_routing.DeleteRoutingHdl(dft_coalesce_rt_rule_hdl[1], IPA_IP_v4) == false)
+					{
+						IPACMERR("Routing old RSB UDP RT rule deletion failed!\n");
+						res = IPACM_FAILURE;
+						goto fail;
+					}
+				}
 				/* Delete default v4 RT rule */
 				IPACMDBG_H("Delete default v4 routing rules\n");
 				if (m_routing.DeleteRoutingHdl(dft_rt_rule_hdl[0], IPA_IP_v4) == false)
@@ -446,30 +591,79 @@
 		rt_rule->num_rules = NUM_RULES;
 		rt_rule->ip = data->iptype;
 		rt_rule_entry = &rt_rule->rules[0];
-		if(m_is_sta_mode == Q6_WAN)
-		{
-			strlcpy(hdr.name, tx_prop->tx[0].hdr_name, sizeof(hdr.name));
-			hdr.name[IPA_RESOURCE_NAME_MAX-1] = '\0';
-			if(m_header.GetHeaderHandle(&hdr) == false)
-			{
-				IPACMERR("Failed to get QMAP header.\n");
-				return IPACM_FAILURE;
-			}
-			rt_rule_entry->rule.hdr_hdl = hdr.hdl;
-			rt_rule_entry->rule.dst = IPA_CLIENT_APPS_WAN_CONS;
-		}
-		else
-		{
-			rt_rule_entry->rule.dst = IPA_CLIENT_APPS_LAN_CONS;
-		}
 		rt_rule_entry->at_rear = false;
 		rt_rule_entry->rule.attrib.attrib_mask = IPA_FLT_DST_ADDR;
 		/* still need setup v4 default routing rule to A5*/
 		strlcpy(rt_rule->rt_tbl_name, IPACM_Iface::ipacmcfg->rt_tbl_lan_v4.name, sizeof(rt_rule->rt_tbl_name));
 		rt_rule_entry->rule.attrib.u.v4.dst_addr      = data->ipv4_addr;
 		rt_rule_entry->rule.attrib.u.v4.dst_addr_mask = 0xFFFFFFFF;
-#ifdef FEATURE_IPA_V3
-		rt_rule_entry->rule.hashable = false;
+		if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+			rt_rule_entry->rule.hashable = false;
+		if(m_is_sta_mode == Q6_WAN)
+		{
+			/* query qmap header*/
+			strlcpy(hdr.name, tx_prop->tx[0].hdr_name, sizeof(hdr.name));
+			hdr.name[IPA_RESOURCE_NAME_MAX-1] = '\0';
+			if(m_header.GetHeaderHandle(&hdr) == false)
+			{
+				IPACMERR("Failed to get QMAP header.\n");
+				res = IPACM_FAILURE;
+				goto fail;
+			}
+			rt_rule_entry->rule.hdr_hdl = hdr.hdl;
+			rt_rule_entry->rule.dst = IPA_CLIENT_APPS_WAN_CONS;
+			/* legacy default v4 rt-rule */
+#ifdef IPA_RT_SUPPORT_COAL
+			rt_rule_entry->rule.coalesce = false;
+#endif
+			/* legacy default v4 rt-rule */
+			if (false == m_routing.AddRoutingRule(rt_rule))
+			{
+				IPACMERR("Routing rule addition failed!\n");
+				res = IPACM_FAILURE;
+				goto fail;
+			}
+			else if (rt_rule_entry->status)
+			{
+				IPACMERR("rt rule adding failed. Result=%d\n", rt_rule_entry->status);
+				res = rt_rule_entry->status;
+				goto fail;
+		}
+			dft_rt_rule_hdl[0] = rt_rule_entry->rt_rule_hdl;
+			IPACMDBG_H("ipv4 wan iface rt-rule hdll=0x%x\n", dft_rt_rule_hdl[0]);
+			/* RSC TCP rule*/
+			rt_rule_entry->rule.attrib.attrib_mask |= IPA_FLT_PROTOCOL;
+			rt_rule_entry->rule.attrib.u.v4.protocol = (uint8_t)IPACM_FIREWALL_IPPROTO_TCP;
+
+#ifdef IPA_RT_SUPPORT_COAL
+			if (IPACM_Wan::coalesce_enable_info[ext_prop->ext[0].mux_id].coalesce_tcp_enable)
+				rt_rule_entry->rule.coalesce = true;
+		else
+				rt_rule_entry->rule.coalesce = false;
+#endif
+			if (false == m_routing.AddRoutingRule(rt_rule))
+		{
+				IPACMERR("Routing rule addition failed!\n");
+				res = IPACM_FAILURE;
+				goto fail;
+			}
+			else if (rt_rule_entry->status)
+			{
+				IPACMERR("rsc tcp rt rule adding failed. Result=%d\n", rt_rule_entry->status);
+				res = rt_rule_entry->status;
+				goto fail;
+		}
+			dft_coalesce_rt_rule_hdl[0] = rt_rule_entry->rt_rule_hdl;
+			IPACMDBG_H("ipv4 wan iface rsc tcp rt-rule hdll=0x%x\n enable(%d)", dft_coalesce_rt_rule_hdl[0],
+				IPACM_Wan::coalesce_enable_info[ext_prop->ext[0].mux_id].coalesce_tcp_enable);
+
+			/* RSB UDP rule*/
+			rt_rule_entry->rule.attrib.u.v4.protocol = (uint8_t)IPACM_FIREWALL_IPPROTO_UDP;
+#ifdef IPA_RT_SUPPORT_COAL
+			if (IPACM_Wan::coalesce_enable_info[ext_prop->ext[0].mux_id].coalesce_udp_enable)
+				rt_rule_entry->rule.coalesce = true;
+			else
+				rt_rule_entry->rule.coalesce = false;
 #endif
 		if (false == m_routing.AddRoutingRule(rt_rule))
 		{
@@ -479,14 +673,35 @@
 		}
 		else if (rt_rule_entry->status)
 		{
+				IPACMERR("rsb udp rt rule adding failed. Result=%d\n", rt_rule_entry->status);
+				res = rt_rule_entry->status;
+				goto fail;
+			}
+			dft_coalesce_rt_rule_hdl[1] = rt_rule_entry->rt_rule_hdl;
+			IPACMDBG_H("ipv4 wan iface rsb udp rt-rule hdll=0x%x\n enable(%d)", dft_coalesce_rt_rule_hdl[1],
+				IPACM_Wan::coalesce_enable_info[ext_prop->ext[0].mux_id].coalesce_udp_enable);
+		}
+		else
+		{
+			rt_rule_entry->rule.dst = IPA_CLIENT_APPS_LAN_CONS;
+			/* legacy default v4 rt-rule */
+			if (false == m_routing.AddRoutingRule(rt_rule))
+			{
+				IPACMERR("Routing rule addition failed!\n");
+				res = IPACM_FAILURE;
+				goto fail;
+			}
+			else if (rt_rule_entry->status)
+			{
 			IPACMERR("rt rule adding failed. Result=%d\n", rt_rule_entry->status);
 			res = rt_rule_entry->status;
 			goto fail;
 		}
 		dft_rt_rule_hdl[0] = rt_rule_entry->rt_rule_hdl;
 		IPACMDBG_H("ipv4 wan iface rt-rule hdll=0x%x\n", dft_rt_rule_hdl[0]);
-			/* initial multicast/broadcast/fragment filter rule */
+		}
 
+			/* initial multicast/broadcast/fragment filter rule */
 		/* only do one time */
 		if(!wan_v4_addr_set)
 		{
@@ -498,6 +713,11 @@
 				IPACMDBG_H("Now the number of modem ipv4 pdn is %d.\n", num_ipv4_modem_pdn);
 				init_fl_rule_ex(data->iptype);
 			}
+			else if(m_is_sta_mode == Q6_MHI_WAN)
+			{
+				IPACMDBG_H(" Has rx/tx properties registered for iface %s, add for NATTING for ip-family %d \n", dev_name, IPA_IP_v4);
+				IPACM_Iface::ipacmcfg->AddNatIfaces(dev_name, IPA_IP_v4);
+			}
 			else
 			{
 				init_fl_rule(data->iptype);
@@ -526,11 +746,229 @@
 	IPACMDBG_H("number of default route rules %d\n", num_dft_rt_v6);
 
 fail:
-	free(rt_rule);
-
+	if (rt_rule != NULL)
+	{
+		free(rt_rule);
+	}
 	return res;
 }
 
+/* handle new_address event */
+int IPACM_Wan::handle_addr_evt_mhi_q6(ipacm_event_data_addr *data)
+{
+	uint32_t num_ipv6_addr;
+	int res = IPACM_SUCCESS;
+	struct ipa_ioc_add_rt_rule *rt_rule = NULL;
+	struct ipa_rt_rule_add *rt_rule_entry;
+	struct ipa_ioc_get_hdr hdr;
+	const int NUM_RULES = 1;
+
+#ifdef FEATURE_IPACM_HAL
+	IPACM_OffloadManager* OffloadMng;
+#endif
+
+	memset(&hdr, 0, sizeof(hdr));
+	if(tx_prop == NULL || rx_prop == NULL)
+	{
+		IPACMDBG_H("Either tx or rx property is NULL, return.\n");
+		return IPACM_SUCCESS;
+	}
+	/* Update the IP Type. */
+	config_ip_type(data->iptype);
+
+	if (data->iptype == IPA_IP_v6)
+	{
+		for(num_ipv6_addr=0;num_ipv6_addr<num_dft_rt_v6;num_ipv6_addr++)
+		{
+			if((ipv6_addr[num_ipv6_addr][0] == data->ipv6_addr[0]) &&
+			   (ipv6_addr[num_ipv6_addr][1] == data->ipv6_addr[1]) &&
+			   (ipv6_addr[num_ipv6_addr][2] == data->ipv6_addr[2]) &&
+			   (ipv6_addr[num_ipv6_addr][3] == data->ipv6_addr[3]))
+			{
+				IPACMDBG_H("find matched ipv6 address, index:%d \n", num_ipv6_addr);
+				return IPACM_SUCCESS;
+				break;
+			}
+		}
+
+		ipv6_addr[num_dft_rt_v6][0] = data->ipv6_addr[0];
+		ipv6_addr[num_dft_rt_v6][1] = data->ipv6_addr[1];
+		ipv6_addr[num_dft_rt_v6][2] = data->ipv6_addr[2];
+		ipv6_addr[num_dft_rt_v6][3] = data->ipv6_addr[3];
+
+		/* store ipv6 prefix if the ipv6 address is not link local */
+		if(is_global_ipv6_addr(data->ipv6_addr))
+		{
+			memcpy(ipv6_prefix, data->ipv6_addr, sizeof(ipv6_prefix));
+		}
+		num_dft_rt_v6++;
+		if (num_dft_rt_v6 == 1)
+		{
+			/* Add Natting iface to IPACM_Config if there is  Rx/Tx property */
+			if (rx_prop != NULL || tx_prop != NULL)
+			{
+				IPACMDBG_H(" Has rx/tx properties registered for iface %s, add for NATTING for ip-family %d \n", dev_name, IPA_IP_v6);
+				IPACM_Iface::ipacmcfg->AddNatIfaces(dev_name, IPA_IP_v6);
+			}
+			/* skylar setup v6-wan-tbl */
+			rt_rule = (struct ipa_ioc_add_rt_rule *)
+				calloc(1, sizeof(struct ipa_ioc_add_rt_rule) +
+				NUM_RULES * sizeof(struct ipa_rt_rule_add));
+			if (!rt_rule)
+			{
+				IPACMERR("Error Locate ipa_ioc_add_rt_rule memory...\n");
+				return IPACM_FAILURE;
+			}
+
+			rt_rule->commit = 1;
+			rt_rule->num_rules = NUM_RULES;
+			rt_rule->ip = data->iptype;
+			strlcpy(rt_rule->rt_tbl_name, IPACM_Iface::ipacmcfg->rt_tbl_wan_v6.name, sizeof(rt_rule->rt_tbl_name));
+			rt_rule_entry = &rt_rule->rules[0];
+			strlcpy(hdr.name, tx_prop->tx[0].hdr_name, sizeof(hdr.name));
+			hdr.name[IPA_RESOURCE_NAME_MAX-1] = '\0';
+			if(m_header.GetHeaderHandle(&hdr) == false)
+			{
+				IPACMERR("Failed to get QMAP header.\n");
+				free(rt_rule);
+				return IPACM_FAILURE;
+			}
+			rt_rule_entry->rule.hdr_hdl = hdr.hdl;
+			rt_rule_entry->rule.dst = IPA_CLIENT_APPS_LAN_CONS;
+			rt_rule_entry->at_rear = false;
+			rt_rule_entry->rule.attrib.attrib_mask = IPA_FLT_DST_ADDR;
+			/* still need setup v4 default routing rule to A5*/
+			rt_rule_entry->rule.attrib.attrib_mask = IPA_FLT_DST_ADDR;
+			rt_rule_entry->rule.attrib.u.v6.dst_addr[0] = data->ipv6_addr[0];
+			rt_rule_entry->rule.attrib.u.v6.dst_addr[1] = data->ipv6_addr[1];
+			rt_rule_entry->rule.attrib.u.v6.dst_addr[2] = data->ipv6_addr[2];
+			rt_rule_entry->rule.attrib.u.v6.dst_addr[3] = data->ipv6_addr[3];
+			rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[0] = 0xFFFFFFFF;
+			rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[1] = 0xFFFFFFFF;
+			rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[2] = 0xFFFFFFFF;
+			rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[3] = 0xFFFFFFFF;
+			ipv6_addr[0][0] = data->ipv6_addr[0];
+			ipv6_addr[0][1] = data->ipv6_addr[1];
+			ipv6_addr[0][2] = data->ipv6_addr[2];
+			ipv6_addr[0][3] = data->ipv6_addr[3];
+			if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+				rt_rule_entry->rule.hashable = false;
+			if (false == m_routing.AddRoutingRule(rt_rule))
+			{
+				IPACMERR("Routing rule addition failed!\n");
+				free(rt_rule);
+				return IPACM_FAILURE;
+			}
+			else if (rt_rule_entry->status)
+			{
+				IPACMERR("rt rule adding failed. Result=%d\n", rt_rule_entry->status);
+				free(rt_rule);
+				return rt_rule_entry->status;
+			}
+			dft_rt_rule_hdl[1] = rt_rule_entry->rt_rule_hdl;
+			IPACMDBG_H("ipv6 wan iface rt-rule hdll=0x%x\n", dft_rt_rule_hdl[1]);
+		}
+	}
+	else
+	{
+		if(wan_v4_addr_set)
+		{
+			/* check iface ipv4 same or not */
+			if(data->ipv4_addr == wan_v4_addr)
+			{
+				IPACMDBG_H("Already setup device (%s) ipv4 and it didn't change(0x%x)\n", dev_name, data->ipv4_addr);
+				return IPACM_SUCCESS;
+			}
+			else
+			{
+				IPACMDBG_H(" device (%s) ipv4 addr is changed\n", dev_name);
+				/* Delete default v4 RT rule */
+				IPACMDBG_H("Delete default v4 routing rules\n");
+				if (m_routing.DeleteRoutingHdl(dft_rt_rule_hdl[0], IPA_IP_v4) == false)
+				{
+					IPACMERR("Routing old RT rule deletion failed!\n");
+					return IPACM_FAILURE;
+				}
+			}
+		}
+		/* only do one time */
+		if(!wan_v4_addr_set)
+		{
+			/* Add Natting iface to IPACM_Config if there is  Rx/Tx property */
+			if (rx_prop != NULL || tx_prop != NULL)
+			{
+				IPACMDBG_H(" Has rx/tx properties registered for iface %s, add for NATTING for ip-family %d \n", dev_name, IPA_IP_v4);
+				IPACM_Iface::ipacmcfg->AddNatIfaces(dev_name, IPA_IP_v4);
+			}
+
+			rt_rule = (struct ipa_ioc_add_rt_rule *)
+				calloc(1, sizeof(struct ipa_ioc_add_rt_rule) +
+					NUM_RULES * sizeof(struct ipa_rt_rule_add));
+
+			if (!rt_rule)
+			{
+				IPACMERR("Error Locate ipa_ioc_add_rt_rule memory...\n");
+				return IPACM_FAILURE;
+			}
+
+			rt_rule->commit = 1;
+			rt_rule->num_rules = NUM_RULES;
+			rt_rule->ip = data->iptype;
+			rt_rule_entry = &rt_rule->rules[0];
+			strlcpy(hdr.name, tx_prop->tx[0].hdr_name, sizeof(hdr.name));
+			hdr.name[IPA_RESOURCE_NAME_MAX-1] = '\0';
+			if(m_header.GetHeaderHandle(&hdr) == false)
+			{
+				IPACMERR("Failed to get QMAP header.\n");
+				free(rt_rule);
+				return IPACM_FAILURE;
+			}
+			rt_rule_entry->rule.hdr_hdl = hdr.hdl;
+			rt_rule_entry->rule.dst = IPA_CLIENT_APPS_LAN_CONS;
+			rt_rule_entry->at_rear = false;
+			rt_rule_entry->rule.attrib.attrib_mask = IPA_FLT_DST_ADDR;
+			/* still need setup v4 default routing rule to A5*/
+			strlcpy(rt_rule->rt_tbl_name, IPACM_Iface::ipacmcfg->rt_tbl_lan_v4.name, sizeof(rt_rule->rt_tbl_name));
+			rt_rule_entry->rule.attrib.u.v4.dst_addr      = data->ipv4_addr;
+			rt_rule_entry->rule.attrib.u.v4.dst_addr_mask = 0xFFFFFFFF;
+			if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+				rt_rule_entry->rule.hashable = false;
+			if (false == m_routing.AddRoutingRule(rt_rule))
+			{
+				IPACMERR("Routing rule addition failed!\n");
+				free(rt_rule);
+				return IPACM_FAILURE;
+			}
+			else if (rt_rule_entry->status)
+			{
+				IPACMERR("rt rule adding failed. Result=%d\n", rt_rule_entry->status);
+				free(rt_rule);
+				return rt_rule_entry->status;
+			}
+			dft_rt_rule_hdl[0] = rt_rule_entry->rt_rule_hdl;
+			IPACMDBG_H("ipv4 wan iface rt-rule hdll=0x%x\n", dft_rt_rule_hdl[0]);
+		}
+
+		wan_v4_addr = data->ipv4_addr;
+		wan_v4_addr_set = true;
+		IPACMDBG_H("Receved wan ipv4-addr:0x%x\n",wan_v4_addr);
+		free(rt_rule);
+	}
+
+#ifdef FEATURE_IPACM_HAL
+	/* check if having pending set_upstream cache*/
+	OffloadMng = IPACM_OffloadManager::GetInstance();
+	if (OffloadMng == NULL) {
+		IPACMERR("failed to get IPACM_OffloadManager instance !\n");
+	} else {
+		IPACMDBG_H(" check iface %s if having set_upstream cache events\n", dev_name);
+		OffloadMng->search_framwork_cache(dev_name);
+	}
+#endif
+	IPACMDBG_H("number of default route rules %d\n", num_dft_rt_v6);
+
+	return res;
+}
 void IPACM_Wan::event_callback(ipa_cm_event_id event, void *param)
 {
 	int ipa_interface_index;
@@ -565,9 +1003,9 @@
 			ipa_interface_index = IPACM_Iface::iface_ipa_index_query(data->if_index);
 			if ((ipa_interface_index == ipa_if_num) && (m_is_sta_mode == Q6_WAN))
 			{
-				is_xlat = true;
-				IPACMDBG_H("WAN-LTE (%s) link up, iface: %d is_xlat: %d\n",
-						IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].iface_name,data->if_index, is_xlat);
+				is_xlat_local = true;
+				IPACMDBG_H("WAN-LTE (%s) link up, iface: %d is_xlat_local: %d\n",
+						IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].iface_name,data->if_index, is_xlat_local);
 			}
 			break;
 		}
@@ -665,6 +1103,15 @@
 		}
 		break;
 
+	case IPA_COALESCE_NOTICE:
+		{
+			if (m_is_sta_mode == Q6_WAN)
+			{
+				IPACMDBG_H("Received IPA_COALESCE_NOTICE (wan_mode:%d)\n", m_is_sta_mode);
+				handle_coalesce_evt();
+			}
+		}
+		break;
 	case IPA_LINK_DOWN_EVENT:
 		{
 			ipacm_event_data_fid *data = (ipacm_event_data_fid *)param;
@@ -680,7 +1127,7 @@
 						delete this;
 						return;
 				}
-				else if (m_is_sta_mode == ECM_WAN)
+				else if ((m_is_sta_mode == ECM_WAN) || (m_is_sta_mode == Q6_MHI_WAN))
 				{
 					IPACMDBG_H("Received IPA_LINK_DOWN_EVENT(wan_mode:%d)\n", m_is_sta_mode);
 					/* delete previous instance */
@@ -715,15 +1162,31 @@
 				if( (data->iptype == IPA_IP_v4)
 				    || ((data->iptype==IPA_IP_v6) && (num_dft_rt_v6!=MAX_DEFAULT_v6_ROUTE_RULES)))
 				{
-					IPACMDBG_H("Got IPA_ADDR_ADD_EVENT ip-family:%d, v6 num %d: \n",data->iptype,num_dft_rt_v6);
-					handle_addr_evt(data);
+					if (m_is_sta_mode == Q6_MHI_WAN)
+					{
+						IPACMDBG_H("Got handle_addr_evt_mhi_q6 ip-family:%d, v6 num %d: \n",data->iptype,num_dft_rt_v6);
+						handle_addr_evt_mhi_q6(data);
+					}
+					else
+					{
+						IPACMDBG_H("Got handle_addr_evt ip-family:%d, v6 num %d: \n",data->iptype,num_dft_rt_v6);
+						handle_addr_evt(data);
+					}
 					/* checking if SW-RT_enable */
 					if (IPACM_Iface::ipacmcfg->ipa_sw_rt_enable == true &&
 							m_is_sta_mode != Q6_WAN)
 					{
 						/* 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);
+						}
 					}
 
 				}
@@ -763,7 +1226,7 @@
 #endif
 						if (active_v4 == false)
 						{
-							handle_route_add_evt(data->iptype); //sky
+							handle_route_add_evt(data->iptype);
 						}
 					}
 #ifdef FEATURE_IPA_ANDROID
@@ -824,6 +1287,11 @@
 						install_wan_filtering_rule(false);
 						handle_route_del_evt_ex(IPA_IP_v4);
 					}
+					else if(m_is_sta_mode == Q6_MHI_WAN)
+					{
+						/* only need cleanup rt-rule*/
+						handle_route_del_evt(IPA_IP_v4);
+					}
 					else
 					{
 						del_dft_firewall_rules(IPA_IP_v4);
@@ -840,6 +1308,11 @@
 						install_wan_filtering_rule(false);
 						handle_route_del_evt_ex(IPA_IP_v6);
 					}
+					else if(m_is_sta_mode == Q6_MHI_WAN)
+					{
+						/* only need cleanup rt-rule*/
+						handle_route_del_evt(IPA_IP_v6);
+					}
 					else
 					{
 						del_dft_firewall_rules(IPA_IP_v6);
@@ -889,6 +1362,12 @@
 						install_wan_filtering_rule(false);
 						handle_route_del_evt_ex(IPA_IP_v4);
 					}
+					else if(m_is_sta_mode == Q6_MHI_WAN)
+					{
+						/* only need cleanup rt-rule*/
+						del_dft_firewall_rules(IPA_IP_v4);
+						handle_route_del_evt(IPA_IP_v4);
+					}
 					else
 					{
 						del_dft_firewall_rules(IPA_IP_v4);
@@ -918,6 +1397,13 @@
 						install_wan_filtering_rule(false);
 						handle_route_del_evt_ex(IPA_IP_v6);
 					}
+					else if(m_is_sta_mode == Q6_MHI_WAN)
+					{
+						/* only need cleanup rt-rule*/
+						del_dft_firewall_rules(IPA_IP_v6);
+						handle_route_del_evt(IPA_IP_v6);
+					}
+
 					else
 					{
 						del_dft_firewall_rules(IPA_IP_v6);
@@ -962,7 +1448,7 @@
 					handle_sta_header_add_evt();
 					handle_route_add_evt(data->iptype);
 					/* Add IPv6 routing table if XLAT is enabled */
-					if(is_xlat && (m_is_sta_mode == Q6_WAN) && (active_v6 == false))
+					if(is_xlat_local && (m_is_sta_mode == Q6_WAN) && (active_v6 == false))
 					{
 						IPACMDBG_H("XLAT enabled: adding IPv6 routing table dev (%s)\n", dev_name);
 						handle_route_add_evt(IPA_IP_v6);
@@ -1051,7 +1537,7 @@
 						install_wan_filtering_rule(false);
 						handle_route_del_evt_ex(IPA_IP_v4);
 
-						if(is_xlat && active_v6 == true)
+						if(is_xlat_local && active_v6 == true)
 						{
 							IPACMDBG_H("XLAT enabled: Delete IPv6 routing table dev (%s)\n", dev_name);
 							del_wan_firewall_rule(IPA_IP_v6);
@@ -1153,9 +1639,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;
 
@@ -1168,9 +1658,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;
 
@@ -1228,7 +1722,7 @@
 		break;
 
 		case IPA_WLAN_SWITCH_TO_SCC:
-			if(IPACM_Wan::backhaul_is_sta_mode == true)
+			if(IPACM_Wan::backhaul_mode == WLAN_WAN)
 			{
 				IPACMDBG_H("Received IPA_WLAN_SWITCH_TO_SCC\n");
 				if(ip_type == IPA_IP_MAX)
@@ -1247,7 +1741,7 @@
 			break;
 
 		case IPA_WLAN_SWITCH_TO_MCC:
-			if(IPACM_Wan::backhaul_is_sta_mode == true)
+			if(IPACM_Wan::backhaul_mode == WLAN_WAN)
 			{
 				IPACMDBG_H("Received IPA_WLAN_SWITCH_TO_MCC\n");
 				if(ip_type == IPA_IP_MAX)
@@ -1266,7 +1760,7 @@
 			break;
 #ifdef FEATURE_IPACM_HAL
 		/* WA for WLAN to clean up NAT instance during SSR */
-		case IPA_SSR_NOTICE: //sky
+		case IPA_SSR_NOTICE:
 		case IPA_WLAN_FWR_SSR_BEFORE_SHUTDOWN_NOTICE:
 			IPACMDBG_H("Received IPA_SSR_NOTICE event.\n");
 			if(m_is_sta_mode == WLAN_WAN)
@@ -1293,6 +1787,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;
@@ -1322,25 +1817,12 @@
 	}
 	IPACMDBG_H("backhaul_is_wan_bridge ?: %d \n", IPACM_Wan::backhaul_is_wan_bridge);
 
-	if (m_is_sta_mode !=Q6_WAN)
+	/* query MTU size of the interface */
+	query_mtu_size();
+
+	if (m_is_sta_mode ==Q6_WAN)
 	{
-		IPACM_Wan::backhaul_is_sta_mode	= true;
-		if((iptype==IPA_IP_v4) && (header_set_v4 != true))
-		{
-			header_partial_default_wan_v4 = true;
-			IPACMDBG_H("STA ipv4-header haven't constructed \n");
-			return IPACM_SUCCESS;
-		}
-		else if((iptype==IPA_IP_v6) && (header_set_v6 != true))
-		{
-			header_partial_default_wan_v6 = true;
-			IPACMDBG_H("STA ipv6-header haven't constructed \n");
-			return IPACM_SUCCESS;
-		}
-	}
-	else
-	{
-		IPACM_Wan::backhaul_is_sta_mode	= false;
+		IPACM_Wan::backhaul_mode = m_is_sta_mode;
 		IPACMDBG_H("reset backhaul to LTE \n");
 
 		if (iface_query != NULL && iface_query->num_ext_props > 0)
@@ -1362,53 +1844,64 @@
 			return IPACM_FAILURE;
 		}
 	}
-#if 0
-    for (cnt=0; cnt<tx_prop->num_tx_props; cnt++)
+	else if (m_is_sta_mode == Q6_MHI_WAN)
 	{
-		if(tx_prop->tx[cnt].ip==iptype)
-		break;
+		if (iface_query != NULL && iface_query->num_ext_props > 0)
+		{
+			/* treat Q6_MHI_WAN as STA mode also */
+			IPACMDBG_H("Q6-MHI  ipv4/v6-header already constructed \n");
+			IPACM_Wan::backhaul_mode = m_is_sta_mode;		
+			IPACMDBG_H("Setting up QMAP ID %d.\n", ext_prop->ext[0].mux_id);
+			IPACM_Iface::ipacmcfg->SetQmapId(ext_prop->ext[0].mux_id);
+			/* sending mux-id info to PCIE-modem for UL */
+			if(false == m_filtering.AddOffloadFilteringRule(NULL, ext_prop->ext[0].mux_id, 0))
+			{
+				IPACMERR("Failed to send mux id info to modem.\n");
+				return IPACM_FAILURE;
+			}
+			/* send UL UDP frag filtering rule */
+			if(iptype==IPA_IP_v4 && add_offload_frag_rule())
+			{
+				IPACMERR("Failed to send DL frag rule to modem.\n");
+				return IPACM_FAILURE;
+			}
+
+			/* send ipv6 ICMP filtering rule */
+			if(iptype==IPA_IP_v6 && add_icmpv6_exception_rule())
+			{
+				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
+		{
+			IPACMERR("iface_query is empty.\n");
+			return IPACM_FAILURE;
+		}
 	}
-
-	if(tx_prop->tx[cnt].hdr_name != NULL)
+	else
 	{
-	    memset(&sCopyHeader, 0, sizeof(sCopyHeader));
-	    memcpy(sCopyHeader.name,
-	    			 tx_prop->tx[cnt].hdr_name,
-	    			 sizeof(sCopyHeader.name));
-
-	    IPACMDBG_H("header name: %s\n", sCopyHeader.name);
-	    if (m_header.CopyHeader(&sCopyHeader) == false)
-	    {
-	    	IPACMERR("ioctl copy header failed");
-	    	return IPACM_FAILURE;
-	    }
-	    IPACMDBG_H("header length: %d, paritial: %d\n", sCopyHeader.hdr_len, sCopyHeader.is_partial);
-	    if(sCopyHeader.is_partial)
-	    {
- 	    	IPACMDBG_H("Not setup default WAN routing rules cuz the header is not complete\n");
-            if(iptype==IPA_IP_v4)
-			{
-				header_partial_default_wan_v4 = true;
-            }
-			else
-			{
-				header_partial_default_wan_v6 = true;
-			}
+		IPACM_Wan::backhaul_mode = m_is_sta_mode;
+		if((iptype==IPA_IP_v4) && (header_set_v4 != true))
+		{
+			header_partial_default_wan_v4 = true;
+			IPACMDBG_H("STA ipv4-header haven't constructed \n");
 			return IPACM_SUCCESS;
-	    }
-	    else
-	    {
-            if(iptype==IPA_IP_v4)
-			{
-				header_partial_default_wan_v4 = false;
-            }
-			else
-			{
-				header_partial_default_wan_v6 = false;
-			}
-	    }
-    }
-#endif
+		}
+		else if((iptype==IPA_IP_v6) && (header_set_v6 != true))
+		{
+			header_partial_default_wan_v6 = true;
+			IPACMDBG_H("STA ipv6-header haven't constructed \n");
+			return IPACM_SUCCESS;
+		}
+	}
 
 	rt_rule = (struct ipa_ioc_add_rt_rule *)
 		 calloc(1, sizeof(struct ipa_ioc_add_rt_rule) +
@@ -1424,8 +1917,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;
 
@@ -1453,15 +1944,34 @@
 				rt_rule_entry->rule.hdr_hdl = hdr_hdl_sta_v6;
 			}
 
-			if(IPACM_Iface::ipacmcfg->isMCC_Mode == true)
+			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)
 			{
-				IPACMDBG_H("In MCC mode, use alt dst pipe: %d\n",
-						tx_prop->tx[tx_index].alt_dst_pipe);
-				rt_rule_entry->rule.dst = tx_prop->tx[tx_index].alt_dst_pipe;
+				memset(&hdr, 0, sizeof(hdr));
+				strlcpy(hdr.name, tx_prop->tx[tx_index].hdr_name, sizeof(hdr.name));
+				hdr.name[IPA_RESOURCE_NAME_MAX-1] = '\0';
+				if(m_header.GetHeaderHandle(&hdr) == false)
+				{
+					IPACMERR("Failed to get QMAP header.\n");
+					free(rt_rule);
+					return IPACM_FAILURE;
+				}
+				rt_rule_entry->rule.hdr_hdl = hdr.hdl;
+				rt_rule_entry->rule.dst = tx_prop->tx[tx_index].dst_pipe;
 			}
 			else
 			{
-				rt_rule_entry->rule.dst = tx_prop->tx[tx_index].dst_pipe;
+				if(IPACM_Iface::ipacmcfg->isMCC_Mode == true)
+				{
+					IPACMDBG_H("In MCC mode, use alt dst pipe: %d\n",
+							tx_prop->tx[tx_index].alt_dst_pipe);
+					rt_rule_entry->rule.dst = tx_prop->tx[tx_index].alt_dst_pipe;
+				}
+				else
+				{
+					rt_rule_entry->rule.dst = tx_prop->tx[tx_index].dst_pipe;
+				}
 			}
 			memcpy(&rt_rule_entry->rule.attrib,
 						 &tx_prop->tx[tx_index].attrib,
@@ -1472,11 +1982,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;
+				if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+					rt_rule_entry->rule.hashable = true;
+#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 (false == m_routing.AddRoutingRule(rt_rule))
+				if (result == false)
 				{
 		    		IPACMERR("Routing rule addition failed!\n");
 		    		free(rt_rule);
@@ -1498,10 +2018,21 @@
 				rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[1] = 0;
 				rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[2] = 0;
 				rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[3] = 0;
-#ifdef FEATURE_IPA_V3
-				rt_rule_entry->rule.hashable = true;
+				if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+					rt_rule_entry->rule.hashable = true;
+#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 (false == m_routing.AddRoutingRule(rt_rule))
+				if (result == false)
 				{
 		    		IPACMERR("Routing rule addition failed!\n");
 		    		free(rt_rule);
@@ -1518,9 +2049,10 @@
 
 	/* add a catch-all rule in wan dl routing table */
 
-	if (iptype == IPA_IP_v6)
+	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)
@@ -1531,6 +2063,7 @@
 			if(m_header.GetHeaderHandle(&hdr) == false)
 			{
 				IPACMERR("Failed to get QMAP header.\n");
+				free(rt_rule);
 				return IPACM_FAILURE;
 			}
 			rt_rule_entry->rule.hdr_hdl = hdr.hdl;
@@ -1558,9 +2091,8 @@
 		rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[1] = 0;
 		rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[2] = 0;
 		rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[3] = 0;
-#ifdef FEATURE_IPA_V3
-		rt_rule_entry->rule.hashable = true;
-#endif
+		if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+			rt_rule_entry->rule.hashable = true;
 		if (false == m_routing.AddRoutingRule(rt_rule))
 		{
 			IPACMERR("Routing rule addition failed!\n");
@@ -1572,6 +2104,11 @@
 				wan_route_rule_v6_hdl_a5[0], 0, iptype);
 	}
 
+	/* set mtu_default_wan to current default wan instance */
+	mtu_default_wan = mtu_size;
+
+	IPACMDBG_H("replace the mtu_default_wan to %d\n", mtu_default_wan);
+
 	ipacm_event_iface_up *wanup_data;
 	wanup_data = (ipacm_event_iface_up *)malloc(sizeof(ipacm_event_iface_up));
 	if (wanup_data == NULL)
@@ -1582,6 +2119,7 @@
 	}
 	memset(wanup_data, 0, sizeof(ipacm_event_iface_up));
 
+	/* handling filter rule construction */
 	if (iptype == IPA_IP_v4)
 	{
 		IPACM_Wan::wan_up = true;
@@ -1602,34 +2140,33 @@
 
 		memcpy(wanup_data->ifname, dev_name, sizeof(wanup_data->ifname));
 		wanup_data->ipv4_addr = wan_v4_addr;
-		if (m_is_sta_mode!=Q6_WAN)
-		{
-			wanup_data->is_sta = true;
-		}
-		else
-		{
-			wanup_data->is_sta = false;
-		}
+		wanup_data->backhaul_type = m_is_sta_mode;
 		IPACMDBG_H("Posting IPA_HANDLE_WAN_UP with below information:\n");
 		IPACMDBG_H("if_name:%s, ipv4_address:0x%x, is sta mode:%d\n",
-				wanup_data->ifname, wanup_data->ipv4_addr, wanup_data->is_sta);
+				wanup_data->ifname, wanup_data->ipv4_addr, wanup_data->backhaul_type);
 		memset(&evt_data, 0, sizeof(evt_data));
 
+		/* set backhaul type as xlat */
+		IPACM_Wan::is_xlat = is_xlat_local;
+
 		/* send xlat configuration for installing uplink rules */
-		if(is_xlat && (m_is_sta_mode == Q6_WAN))
+		if(is_xlat_local && (m_is_sta_mode == Q6_WAN))
 		{
 			IPACM_Wan::xlat_mux_id = ext_prop->ext[0].mux_id;
 			wanup_data->xlat_mux_id = IPACM_Wan::xlat_mux_id;
 			IPACMDBG_H("Set xlat configuraiton with below information:\n");
-			IPACMDBG_H("xlat_enabled: %d xlat_mux_id: %d \n",
-					is_xlat, xlat_mux_id);
+			IPACMDBG_H("xlat_enabled: %d set xlat_mux_id: %d \n",
+					is_xlat_local, IPACM_Wan::xlat_mux_id);
 		}
-		else
+		else /*temp put xlat = 0 for Q6_MHI_WAN*/
 		{
 			IPACM_Wan::xlat_mux_id = 0;
 			wanup_data->xlat_mux_id = 0;
-			if(m_is_sta_mode == Q6_WAN)
+			if(m_is_sta_mode != WLAN_WAN) //both q6_wan/q6_mhi_wan
+			{
 				wanup_data->mux_id = ext_prop->ext[0].mux_id;
+				IPACMDBG_H("mux_id: %d\n", wanup_data->mux_id);
+			}
 			else
 				wanup_data->mux_id = 0;
 			IPACMDBG_H("No xlat configuration\n");
@@ -1664,17 +2201,10 @@
 		}
 
 		memcpy(wanup_data->ifname, dev_name, sizeof(wanup_data->ifname));
-		if (m_is_sta_mode!=Q6_WAN)
-		{
-			wanup_data->is_sta = true;
-		}
-		else
-		{
-			wanup_data->is_sta = false;
-		}
+		wanup_data->backhaul_type = m_is_sta_mode;
 		memcpy(wanup_data->ipv6_prefix, ipv6_prefix, sizeof(wanup_data->ipv6_prefix));
 		IPACMDBG_H("Posting IPA_HANDLE_WAN_UP_V6 with below information:\n");
-		IPACMDBG_H("if_name:%s, is sta mode: %d\n", wanup_data->ifname, wanup_data->is_sta);
+		IPACMDBG_H("if_name:%s, is sta mode: %d\n", wanup_data->ifname, wanup_data->backhaul_type);
 		IPACMDBG_H("ipv6 prefix: 0x%08x%08x.\n", ipv6_prefix[0], ipv6_prefix[1]);
 		memset(&evt_data, 0, sizeof(evt_data));
 		evt_data.event = IPA_HANDLE_WAN_UP_V6;
@@ -1685,20 +2215,23 @@
                 post_wan_up_tether_evt(IPA_IP_v6, 0);
 #endif
 	}
-		if(IPACM_Iface::ipacmcfg->GetIPAVer() >= IPA_HW_None && IPACM_Iface::ipacmcfg->GetIPAVer() < IPA_HW_v4_0)
-		{
+
+	if(IPACM_Iface::ipacmcfg->GetIPAVer() >= IPA_HW_None && IPACM_Iface::ipacmcfg->GetIPAVer() < IPA_HW_v4_0)
+	{
 			/* Add corresponding ipa_rm_resource_name of TX-endpoint up before IPV6 RT-rule set */
 			IPACMDBG_H("dev %s add producer dependency\n", dev_name);
 			IPACMDBG_H("depend Got pipe %d rm index : %d \n", tx_prop->tx[0].dst_pipe, IPACM_Iface::ipacmcfg->ipa_client_rm_map_tbl[tx_prop->tx[0].dst_pipe]);
 			IPACM_Iface::ipacmcfg->AddRmDepend(IPACM_Iface::ipacmcfg->ipa_client_rm_map_tbl[tx_prop->tx[0].dst_pipe],false);
+	}
 #ifdef WAN_IOC_NOTIFY_WAN_STATE
-			} else {
-			if (m_is_sta_mode == Q6_WAN && ipa_pm_q6_check == 0)
+	else {
+			if ((m_is_sta_mode == Q6_WAN && ipa_pm_q6_check == 0 ) || (m_is_sta_mode == Q6_MHI_WAN))
 			{
 				fd_wwan_ioctl = open(WWAN_QMI_IOCTL_DEVICE_NAME, O_RDWR);
 				if(fd_wwan_ioctl < 0)
 				{
 					IPACMERR("Failed to open %s.\n",WWAN_QMI_IOCTL_DEVICE_NAME);
+					free(rt_rule);
 					return false;
 				}
 				IPACMDBG_H("send WAN_IOC_NOTIFY_WAN_STATE up to IPA_PM\n");
@@ -1711,11 +2244,9 @@
 			}
 			ipa_pm_q6_check++;
 			IPACMDBG_H("update ipa_pm_q6_check to %d\n", ipa_pm_q6_check);
-
-		}
-#else
-}
+	}
 #endif
+
 	if(rt_rule != NULL)
 	{
 		free(rt_rule);
@@ -1739,22 +2270,16 @@
 	memset(wanup_data, 0, sizeof(ipacm_event_iface_up_tehter));
 
 	wanup_data->if_index_tether = ipa_if_num_tether;
-	if (m_is_sta_mode!=Q6_WAN)
-	{
-		wanup_data->is_sta = true;
-	}
-	else
-	{
-		wanup_data->is_sta = false;
-	}
+	wanup_data->backhaul_type = m_is_sta_mode;
 	/* xlat mux-id*/
-	if(is_xlat && (m_is_sta_mode == Q6_WAN))
+	if(is_xlat_local && (m_is_sta_mode == Q6_WAN))
 		wanup_data->xlat_mux_id = ext_prop->ext[0].mux_id;
 	else
 		wanup_data->xlat_mux_id = 0;
 	IPACMDBG_H("Posting IPA_HANDLE_WAN_UP_TETHER with below information:\n");
 	IPACMDBG_H("tether_if_name:%s, is sta mode:%d xlat_mux_id: %d\n",
-			IPACM_Iface::ipacmcfg->iface_table[ipa_if_num_tether].iface_name, wanup_data->is_sta, wanup_data->xlat_mux_id);
+			IPACM_Iface::ipacmcfg->iface_table[ipa_if_num_tether].iface_name, wanup_data->backhaul_type, wanup_data->xlat_mux_id);
+
 	memset(&evt_data, 0, sizeof(evt_data));
 
 	if (iptype == IPA_IP_v4)
@@ -1806,17 +2331,10 @@
 	memset(wandown_data, 0, sizeof(ipacm_event_iface_up_tehter));
 
 	wandown_data->if_index_tether = ipa_if_num_tether;
-	if (m_is_sta_mode!=Q6_WAN)
-	{
-		wandown_data->is_sta = true;
-	}
-	else
-	{
-		wandown_data->is_sta = false;
-	}
+	wandown_data->backhaul_type = m_is_sta_mode;
 	IPACMDBG_H("Posting IPA_HANDLE_WAN_DOWN_TETHER with below information:\n");
 	IPACMDBG_H("tether_if_name:%s, is sta mode:%d\n",
-			IPACM_Iface::ipacmcfg->iface_table[ipa_if_num_tether].iface_name, wandown_data->is_sta);
+			IPACM_Iface::ipacmcfg->iface_table[ipa_if_num_tether].iface_name, wandown_data->backhaul_type);
 	memset(&evt_data, 0, sizeof(evt_data));
 
 	if (iptype == IPA_IP_v4)
@@ -2000,6 +2518,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);
 
@@ -2013,29 +2532,35 @@
 	memset(&firewall_config, 0, sizeof(firewall_config));
 	strlcpy(firewall_config.firewall_config_file, "/etc/mobileap_firewall.xml", sizeof(firewall_config.firewall_config_file));
 
-	IPACMDBG_H("Firewall XML file is %s \n", firewall_config.firewall_config_file);
-	if (IPACM_SUCCESS == IPACM_read_firewall_xml(firewall_config.firewall_config_file, &firewall_config))
+	if(m_is_sta_mode != Q6_MHI_WAN)
 	{
-		IPACMDBG_H("QCMAP Firewall XML read OK \n");
-		/* find the number of v4/v6 firewall rules */
-		for (i = 0; i < firewall_config.num_extd_firewall_entries; i++)
+		IPACMDBG_H("Firewall XML file is %s \n", firewall_config.firewall_config_file);
+		if (IPACM_SUCCESS == IPACM_read_firewall_xml(firewall_config.firewall_config_file, &firewall_config))
 		{
-			if (firewall_config.extd_firewall_entries[i].ip_vsn == 4)
+			IPACMDBG_H("QCMAP Firewall XML read OK \n");
+			/* find the number of v4/v6 firewall rules */
+			for (i = 0; i < firewall_config.num_extd_firewall_entries; i++)
 			{
-				rule_v4++;
+				if (firewall_config.extd_firewall_entries[i].ip_vsn == 4)
+				{
+					rule_v4++;
+				}
+				else
+				{
+					rule_v6++;
+				}
 			}
-			else
-			{
-				rule_v6++;
-			}
+			IPACMDBG_H("firewall rule v4:%d v6:%d total:%d\n", rule_v4, rule_v6, firewall_config.num_extd_firewall_entries);
 		}
-		IPACMDBG_H("firewall rule v4:%d v6:%d total:%d\n", rule_v4, rule_v6, firewall_config.num_extd_firewall_entries);
+		else
+		{
+			IPACMERR("QCMAP Firewall XML read failed, no that file, use default configuration \n");
+		}
 	}
 	else
 	{
-		IPACMERR("QCMAP Firewall XML read failed, no that file, use default configuration \n");
+		IPACMDBG_H("in Q6_MHI_WAN mode, skip firewall, use default configuration \n");		
 	}
-
 	/* construct ipa_ioc_add_flt_rule with N firewall rules */
 	ipa_ioc_add_flt_rule *m_pFilteringTable = NULL;
 	len = sizeof(struct ipa_ioc_add_flt_rule) + 1 * sizeof(struct ipa_flt_rule_add);
@@ -2058,17 +2583,30 @@
 
 		memset(&flt_rule_entry, 0, sizeof(struct ipa_flt_rule_add));
 		flt_rule_entry.at_rear = true;
-#ifdef FEATURE_IPA_V3
-		flt_rule_entry.at_rear = false;
-		flt_rule_entry.rule.hashable = false;
-#endif
+		if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+		{
+			flt_rule_entry.at_rear = false;
+			flt_rule_entry.rule.hashable = false;
+		}
 		flt_rule_entry.flt_rule_hdl = -1;
 		flt_rule_entry.status = -1;
 		flt_rule_entry.rule.action = IPA_PASS_TO_EXCEPTION;
 		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);
@@ -2141,10 +2679,11 @@
 					flt_rule_entry.rule.action = IPA_PASS_TO_ROUTING;
 				}
             }
-#ifdef FEATURE_IPA_V3
-			flt_rule_entry.at_rear = true;
-			flt_rule_entry.rule.hashable = true;
-#endif
+			if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+			{
+				flt_rule_entry.at_rear = true;
+				flt_rule_entry.rule.hashable = true;
+			}
 			flt_rule_entry.rule.rt_tbl_hdl = IPACM_Iface::ipacmcfg->rt_tbl_lan_v4.hdl;
 			memcpy(&flt_rule_entry.rule.attrib,
 						 &rx_prop->rx[0].attrib,
@@ -2153,9 +2692,27 @@
 			flt_rule_entry.rule.attrib.u.v4.dst_addr_mask = 0x00000000;
 			flt_rule_entry.rule.attrib.u.v4.dst_addr = 0x00000000;
 
-			memcpy(&(m_pFilteringTable->rules[0]), &flt_rule_entry, sizeof(struct ipa_flt_rule_add));
+			/* disble meta-data filtering */
+			if(m_is_sta_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);
+			}
 
-			if (false == m_filtering.AddFilteringRule(m_pFilteringTable))
+			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 == result)
 			{
 				IPACMERR("Error Adding RuleTable(0) to Filtering, aborting...\n");
 				free(m_pFilteringTable);
@@ -2220,9 +2777,8 @@
 			        {
 			            flt_rule_entry.rule.action = IPA_PASS_TO_EXCEPTION;
                     }
-#ifdef FEATURE_IPA_V3
-					flt_rule_entry.rule.hashable = true;
-#endif
+					if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+						flt_rule_entry.rule.hashable = true;
 					memcpy(&flt_rule_entry.rule.attrib,
 								 &firewall_config.extd_firewall_entries[i].attrib,
 								 sizeof(struct ipa_rule_attrib));
@@ -2242,7 +2798,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);
@@ -2266,7 +2835,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);
@@ -2290,7 +2872,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);
@@ -2351,9 +2946,8 @@
 					flt_rule_entry.rule.action = IPA_PASS_TO_ROUTING;
 				}
             }
-#ifdef FEATURE_IPA_V3
-			flt_rule_entry.rule.hashable = true;
-#endif
+			if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+				flt_rule_entry.rule.hashable = true;
 			flt_rule_entry.rule.rt_tbl_hdl = IPACM_Iface::ipacmcfg->rt_tbl_lan_v4.hdl;
 			memcpy(&flt_rule_entry.rule.attrib,
 						 &rx_prop->rx[0].attrib,
@@ -2366,7 +2960,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);
@@ -2395,40 +3002,56 @@
 			m_pFilteringTable->ip = IPA_IP_v6;
 			m_pFilteringTable->num_rules = (uint8_t)1;
 
-			/* Construct ICMP rule */
-			memset(&flt_rule_entry, 0, sizeof(struct ipa_flt_rule_add));
-			flt_rule_entry.at_rear = true;
-			flt_rule_entry.flt_rule_hdl = -1;
-			flt_rule_entry.status = -1;
-			flt_rule_entry.rule.retain_hdr = 1;
-			flt_rule_entry.rule.eq_attrib_type = 0;
-			flt_rule_entry.rule.action = IPA_PASS_TO_EXCEPTION;
-#ifdef FEATURE_IPA_V3
-			flt_rule_entry.rule.hashable = true;
-#endif
-			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_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))
+			if(m_is_sta_mode != Q6_MHI_WAN)
 			{
-				IPACMERR("Error Adding Filtering rules, aborting...\n");
-				free(m_pFilteringTable);
-				return IPACM_FAILURE;
+				/* Construct ICMP rule */
+				memset(&flt_rule_entry, 0, sizeof(struct ipa_flt_rule_add));
+				flt_rule_entry.at_rear = true;
+				flt_rule_entry.flt_rule_hdl = -1;
+				flt_rule_entry.status = -1;
+				flt_rule_entry.rule.retain_hdr = 1;
+				flt_rule_entry.rule.eq_attrib_type = 0;
+				flt_rule_entry.rule.action = IPA_PASS_TO_EXCEPTION;
+				if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+					flt_rule_entry.rule.hashable = true;
+				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_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));
+#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);
+					return IPACM_FAILURE;
+				}
+				else
+				{
+					IPACM_Iface::ipacmcfg->increaseFltRuleCount(rx_prop->rx[0].src_pipe, IPA_IP_v6, 1);
+					IPACMDBG_H("flt rule hdl0=0x%x, status=0x%x\n", m_pFilteringTable->rules[0].flt_rule_hdl, m_pFilteringTable->rules[0].status);
+				}
+				/* copy filter hdls */
+				dft_wan_fl_hdl[2] = m_pFilteringTable->rules[0].flt_rule_hdl;
+				/* End of construct ICMP rule */
 			}
 			else
 			{
-				IPACM_Iface::ipacmcfg->increaseFltRuleCount(rx_prop->rx[0].src_pipe, IPA_IP_v6, 1);
-				IPACMDBG_H("flt rule hdl0=0x%x, status=0x%x\n", m_pFilteringTable->rules[0].flt_rule_hdl, m_pFilteringTable->rules[0].status);
+				IPACMDBG_H("in Q6_MHI_WAN mode, skip ICMPv6 flt rule \n");		
 			}
-			/* copy filter hdls */
-			dft_wan_fl_hdl[2] = m_pFilteringTable->rules[0].flt_rule_hdl;
-
-			/* End of construct ICMP rule */
-
 			/* v6 default route */
 			memset(&flt_rule_entry, 0, sizeof(struct ipa_flt_rule_add));
 			if (false == m_routing.GetRoutingTable(&IPACM_Iface::ipacmcfg->rt_tbl_wan_v6)) //rt_tbl_wan_v6 rt_tbl_v6
@@ -2443,28 +3066,26 @@
 			flt_rule_entry.rule.rt_tbl_hdl = IPACM_Iface::ipacmcfg->rt_tbl_wan_v6.hdl;
 
 			/* firewall disable, all traffic are allowed */
-                        if(firewall_config.firewall_enable == true)
+			if(firewall_config.firewall_enable == true)
 			{
-			   flt_rule_entry.at_rear = true;
-
-			   /* default action for v6 is PASS_TO_ROUTE unless user set to exception*/
-                           if(firewall_config.rule_action_accept == true)
-			   {
-			       flt_rule_entry.rule.action = IPA_PASS_TO_EXCEPTION;
-			   }
-			   else
-			   {
+				flt_rule_entry.at_rear = true;
+				/* default action for v6 is PASS_TO_ROUTE unless user set to exception*/
+				if(firewall_config.rule_action_accept == true)
+				{
+					flt_rule_entry.rule.action = IPA_PASS_TO_EXCEPTION;
+				}
+				else
+				{
 			       flt_rule_entry.rule.action = IPA_PASS_TO_ROUTING;
-                           }
-		        }
+				}
+			}
 			else
 			{
 			  flt_rule_entry.at_rear = true;
 			  flt_rule_entry.rule.action = IPA_PASS_TO_ROUTING;
-                        }
-#ifdef FEATURE_IPA_V3
-			flt_rule_entry.rule.hashable = true;
-#endif
+			}
+			if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+				flt_rule_entry.rule.hashable = true;
 			memcpy(&flt_rule_entry.rule.attrib,
 						 &rx_prop->rx[0].attrib,
 						 sizeof(struct ipa_rule_attrib));
@@ -2477,10 +3098,28 @@
 			flt_rule_entry.rule.attrib.u.v6.dst_addr[1] = 0x00000000;
 			flt_rule_entry.rule.attrib.u.v6.dst_addr[2] = 0x00000000;
 			flt_rule_entry.rule.attrib.u.v6.dst_addr[3] = 0X00000000;
+			/* disble meta-data filtering */
+			if(m_is_sta_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));
+#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);
@@ -2534,10 +3173,9 @@
 			            {
 					flt_rule_entry.rule.action = IPA_PASS_TO_EXCEPTION;
                                     }
-#ifdef FEATURE_IPA_V3
-					flt_rule_entry.rule.hashable = true;
-#endif
-		    			flt_rule_entry.rule.rt_tbl_hdl = IPACM_Iface::ipacmcfg->rt_tbl_wan_v6.hdl;
+					if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+						flt_rule_entry.rule.hashable = true;
+		    		flt_rule_entry.rule.rt_tbl_hdl = IPACM_Iface::ipacmcfg->rt_tbl_wan_v6.hdl;
 					memcpy(&flt_rule_entry.rule.attrib,
 								 &firewall_config.extd_firewall_entries[i].attrib,
 								 sizeof(struct ipa_rule_attrib));
@@ -2551,7 +3189,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);
@@ -2570,7 +3221,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);
@@ -2589,7 +3252,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);
@@ -2617,9 +3293,8 @@
 			flt_rule_entry.rule.retain_hdr = 1;
 			flt_rule_entry.rule.eq_attrib_type = 0;
 			flt_rule_entry.rule.action = IPA_PASS_TO_EXCEPTION;
-#ifdef FEATURE_IPA_V3
-			flt_rule_entry.rule.hashable = true;
-#endif
+			if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+				flt_rule_entry.rule.hashable = true;
 			memcpy(&flt_rule_entry.rule.attrib,
 					 &rx_prop->rx[0].attrib,
 					 sizeof(struct ipa_rule_attrib));
@@ -2627,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);
@@ -2669,9 +3356,8 @@
 			  flt_rule_entry.at_rear = true;
 			  flt_rule_entry.rule.action = IPA_PASS_TO_ROUTING;
                         }
-#ifdef FEATURE_IPA_V3
-			flt_rule_entry.rule.hashable = true;
-#endif
+			if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+				flt_rule_entry.rule.hashable = true;
 			memcpy(&flt_rule_entry.rule.attrib,
 						 &rx_prop->rx[0].attrib,
 						 sizeof(struct ipa_rule_attrib));
@@ -2687,7 +3373,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);
@@ -2755,9 +3454,8 @@
 	{
 		memset(&flt_rule_entry, 0, sizeof(struct ipa_flt_rule_add));
 		flt_rule_entry.at_rear = true;
-#ifdef FEATURE_IPA_V3
-		flt_rule_entry.at_rear = false;
-#endif
+		if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+			flt_rule_entry.at_rear = false;
 		flt_rule_entry.flt_rule_hdl = -1;
 		flt_rule_entry.status = -1;
 
@@ -2765,10 +3463,11 @@
 		flt_rule_entry.rule.to_uc = 0;
 		flt_rule_entry.rule.eq_attrib_type = 1;
 		flt_rule_entry.rule.action = IPA_PASS_TO_ROUTING;
-#ifdef FEATURE_IPA_V3
-		flt_rule_entry.at_rear = false;
-		flt_rule_entry.rule.hashable = false;
-#endif
+		if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+		{
+			flt_rule_entry.at_rear = false;
+			flt_rule_entry.rule.hashable = false;
+		}
 		memset(&rt_tbl_idx, 0, sizeof(rt_tbl_idx));
 		rt_tbl_idx.ip = IPA_IP_v6;
 		strlcpy(rt_tbl_idx.name, IPACM_Iface::ipacmcfg->rt_tbl_wan_dl.name, IPA_RESOURCE_NAME_MAX);
@@ -2834,9 +3533,8 @@
 					{
 						flt_rule_entry.rule.action = IPA_PASS_TO_ROUTING;
 					}
-#ifdef FEATURE_IPA_V3
-					flt_rule_entry.rule.hashable = true;
-#endif
+					if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+						flt_rule_entry.rule.hashable = true;
 					memset(&rt_tbl_idx, 0, sizeof(rt_tbl_idx));
 					rt_tbl_idx.ip = iptype;
 					if(flt_rule_entry.rule.action == IPA_PASS_TO_ROUTING)
@@ -2971,9 +3669,8 @@
 				flt_rule_entry.rule.action = IPA_PASS_TO_DST_NAT;
 			}
 		}
-#ifdef FEATURE_IPA_V3
-		flt_rule_entry.rule.hashable = true;
-#endif
+		if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+			flt_rule_entry.rule.hashable = true;
 		memset(&rt_tbl_idx, 0, sizeof(rt_tbl_idx));
 		rt_tbl_idx.ip = iptype;
 
@@ -3046,9 +3743,8 @@
 					flt_rule_entry.rule.to_uc = 0;
 					flt_rule_entry.rule.eq_attrib_type = 1;
 					flt_rule_entry.rule.action = IPA_PASS_TO_ROUTING;
-#ifdef FEATURE_IPA_V3
-					flt_rule_entry.rule.hashable = true;
-#endif
+					if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+						flt_rule_entry.rule.hashable = true;
 					memset(&rt_tbl_idx, 0, sizeof(rt_tbl_idx));
 					rt_tbl_idx.ip = iptype;
 
@@ -3159,9 +3855,8 @@
 		flt_rule_entry.rule.to_uc = 0;
 		flt_rule_entry.rule.eq_attrib_type = 1;
 		flt_rule_entry.rule.action = IPA_PASS_TO_ROUTING;
-#ifdef FEATURE_IPA_V3
-		flt_rule_entry.rule.hashable = true;
-#endif
+		if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+			flt_rule_entry.rule.hashable = true;
 		memset(&rt_tbl_idx, 0, sizeof(rt_tbl_idx));
 		rt_tbl_idx.ip = iptype;
 		/* firewall disable, all traffic are allowed */
@@ -3313,9 +4008,8 @@
 		flt_rule_entry.rule.to_uc = 0;
 		flt_rule_entry.rule.eq_attrib_type = 1;
 		flt_rule_entry.rule.action = IPA_PASS_TO_ROUTING;
-#ifdef FEATURE_IPA_V3
-		flt_rule_entry.rule.hashable = true;
-#endif
+		if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+			flt_rule_entry.rule.hashable = true;
 		flt_rule_entry.rule.rt_tbl_idx = rt_tbl_idx.idx;
 
 		/* Configuring ICMP filtering rule */
@@ -3431,9 +4125,8 @@
 		flt_rule_entry.rule.to_uc = 0;
 		flt_rule_entry.rule.eq_attrib_type = 1;
 		flt_rule_entry.rule.action = IPA_PASS_TO_ROUTING;
-#ifdef FEATURE_IPA_V3
-		flt_rule_entry.rule.hashable = true;
-#endif
+		if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+			flt_rule_entry.rule.hashable = true;
 		flt_rule_entry.rule.rt_tbl_idx = rt_tbl_idx.idx;
 
 		/* Configuring ICMP filtering rule */
@@ -3513,15 +4206,18 @@
 
 		for (cnt = 0; cnt < ext_prop->num_ext_props; cnt++)
 		{
-#ifndef FEATURE_IPA_V3
-			IPACMDBG_H("Ex(%d): ip-type: %d, mux_id: %d, flt_action: %d\n, rt_tbl_idx: %d, is_xlat_rule: %d flt_hdl: %d\n",
-				cnt, ext_prop->ext[cnt].ip, ext_prop->ext[cnt].mux_id, ext_prop->ext[cnt].action,
-				ext_prop->ext[cnt].rt_tbl_idx, ext_prop->ext[cnt].is_xlat_rule, ext_prop->ext[cnt].filter_hdl);
-#else /* defined (FEATURE_IPA_V3) */
-			IPACMDBG_H("Ex(%d): ip-type: %d, mux_id: %d, flt_action: %d\n, rt_tbl_idx: %d, is_xlat_rule: %d rule_id: %d\n",
-				cnt, ext_prop->ext[cnt].ip, ext_prop->ext[cnt].mux_id, ext_prop->ext[cnt].action,
-				ext_prop->ext[cnt].rt_tbl_idx, ext_prop->ext[cnt].is_xlat_rule, ext_prop->ext[cnt].rule_id);
-#endif
+			if (!IPACM_Iface::ipacmcfg->isIPAv3Supported())
+			{
+				IPACMDBG_H("Ex(%d): ip-type: %d, mux_id: %d, flt_action: %d\n, rt_tbl_idx: %d, is_xlat_rule: %d flt_hdl: %d\n",
+					cnt, ext_prop->ext[cnt].ip, ext_prop->ext[cnt].mux_id, ext_prop->ext[cnt].action,
+					ext_prop->ext[cnt].rt_tbl_idx, ext_prop->ext[cnt].is_xlat_rule, ext_prop->ext[cnt].filter_hdl);
+			}
+			else /* IPA_V3 */
+			{
+				IPACMDBG_H("Ex(%d): ip-type: %d, mux_id: %d, flt_action: %d\n, rt_tbl_idx: %d, is_xlat_rule: %d rule_id: %d\n",
+					cnt, ext_prop->ext[cnt].ip, ext_prop->ext[cnt].mux_id, ext_prop->ext[cnt].action,
+					ext_prop->ext[cnt].rt_tbl_idx, ext_prop->ext[cnt].is_xlat_rule, ext_prop->ext[cnt].rule_id);
+			}
 		}
 
 		if(IPACM_Wan::is_ext_prop_set == false)
@@ -3634,9 +4330,8 @@
 		flt_rule_entry.rule.to_uc = 0;
 		flt_rule_entry.rule.eq_attrib_type = 1;
 		flt_rule_entry.rule.action = IPA_PASS_TO_ROUTING;
-#ifdef FEATURE_IPA_V3
-		flt_rule_entry.rule.hashable = true;
-#endif
+		if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+			flt_rule_entry.rule.hashable = true;
 		flt_rule_entry.rule.rt_tbl_idx = rt_tbl_idx.idx;
 
 		IPACMDBG_H("rx property attrib mask:0x%x\n", rx_prop->rx[0].attrib.attrib_mask);
@@ -3717,9 +4412,8 @@
 		flt_rule_entry.rule.to_uc = 0;
 		flt_rule_entry.rule.eq_attrib_type = 1;
 		flt_rule_entry.rule.action = IPA_PASS_TO_ROUTING;
-#ifdef FEATURE_IPA_V3
-		flt_rule_entry.rule.hashable = true;
-#endif
+		if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+			flt_rule_entry.rule.hashable = true;
 		flt_rule_entry.rule.rt_tbl_idx = rt_tbl_idx.idx;
 
 		/* Configuring Multicast Filtering Rule */
@@ -3916,11 +4610,10 @@
 		flt_rule_entry.rule.eq_attrib.protocol_eq_present = 1;
 		flt_rule_entry.rule.eq_attrib.protocol_eq = IPACM_FIREWALL_IPPROTO_TCP;
 
-#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
+		if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+			flt_rule_entry.rule.eq_attrib.rule_eq_bitmap |= (1<<7);
+		else
+			flt_rule_entry.rule.eq_attrib.rule_eq_bitmap |= (1<<8);
 		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;
 
@@ -3991,7 +4684,7 @@
 		if (num_firewall_v4 != 0)
 		{
 			if (m_filtering.DeleteFilteringHdls(firewall_hdl_v4,
-																					IPA_IP_v4, num_firewall_v4) == false)
+				IPA_IP_v4, num_firewall_v4) == false)
 			{
 				IPACMERR("Error Deleting Filtering rules, aborting...\n");
 				return IPACM_FAILURE;
@@ -4004,7 +4697,7 @@
 		}
 
 		if (m_filtering.DeleteFilteringHdls(dft_wan_fl_hdl,
-																				IPA_IP_v4, 1) == false)
+				IPA_IP_v4, 1) == false)
 		{
 			IPACMERR("Error Deleting Filtering rules, aborting...\n");
 			return IPACM_FAILURE;
@@ -4025,7 +4718,7 @@
 		if (num_firewall_v6 != 0)
 		{
 			if (m_filtering.DeleteFilteringHdls(firewall_hdl_v6,
-																					IPA_IP_v6, num_firewall_v6) == false)
+				IPA_IP_v6, num_firewall_v6) == false)
 			{
 				IPACMERR("Error Deleting Filtering rules, aborting...\n");
 				return IPACM_FAILURE;
@@ -4038,20 +4731,27 @@
 		}
 
 		if (m_filtering.DeleteFilteringHdls(&dft_wan_fl_hdl[1],
-																				IPA_IP_v6, 1) == false)
+			IPA_IP_v6, 1) == false)
 		{
 			IPACMERR("Error Deleting Filtering rules, aborting...\n");
 			return IPACM_FAILURE;
 		}
-		IPACM_Iface::ipacmcfg->decreaseFltRuleCount(rx_prop->rx[0].src_pipe, IPA_IP_v6, 1);
-		if (m_filtering.DeleteFilteringHdls(&dft_wan_fl_hdl[2],
-																				IPA_IP_v6, 1) == false)
-		{
-			IPACMERR("Error Deleting Filtering rules, aborting...\n");
-			return IPACM_FAILURE;
-		}
-		IPACM_Iface::ipacmcfg->decreaseFltRuleCount(rx_prop->rx[0].src_pipe, IPA_IP_v6, 1);
 
+		if(m_is_sta_mode != Q6_MHI_WAN)
+		{
+			IPACM_Iface::ipacmcfg->decreaseFltRuleCount(rx_prop->rx[0].src_pipe, IPA_IP_v6, 1);
+			if (m_filtering.DeleteFilteringHdls(&dft_wan_fl_hdl[2],
+				IPA_IP_v6, 1) == false)
+			{
+				IPACMERR("Error Deleting Filtering rules, aborting...\n");
+				return IPACM_FAILURE;
+			}
+			IPACM_Iface::ipacmcfg->decreaseFltRuleCount(rx_prop->rx[0].src_pipe, IPA_IP_v6, 1);
+		}
+		else
+		{
+			IPACMDBG_H("in Q6_MHI_WAN mode, skip ICMPv6 flt rule deletion\n");
+		}
 		if (is_ipv6_frag_firewall_flt_rule_installed &&
 			check_dft_firewall_rules_attr_mask(&firewall_config))
 		{
@@ -4065,7 +4765,6 @@
 		}
 		num_firewall_v6 = 0;
 	}
-
 	return IPACM_SUCCESS;
 }
 
@@ -4074,6 +4773,11 @@
 {
 	uint32_t tx_index;
 	ipacm_cmd_q_data evt_data;
+#ifdef WAN_IOC_NOTIFY_WAN_STATE
+	struct wan_ioctl_notify_wan_state wan_state;
+	int fd_wwan_ioctl;
+	memset(&wan_state, 0, sizeof(wan_state));
+#endif
 
 	IPACMDBG_H("got handle_route_del_evt for STA-mode with ip-family:%d \n", iptype);
 
@@ -4096,6 +4800,33 @@
 			IPACMDBG_H("depend Got pipe %d rm index : %d \n", tx_prop->tx[0].dst_pipe, IPACM_Iface::ipacmcfg->ipa_client_rm_map_tbl[tx_prop->tx[0].dst_pipe]);
 			IPACM_Iface::ipacmcfg->DelRmDepend(IPACM_Iface::ipacmcfg->ipa_client_rm_map_tbl[tx_prop->tx[0].dst_pipe]);
 		}
+		else
+		{
+			/* change wan_state for Q6_MHI */
+#ifdef WAN_IOC_NOTIFY_WAN_STATE
+			IPACMDBG_H("ipa_pm_q6_check to %d\n", ipa_pm_q6_check);
+			if(ipa_pm_q6_check == 1 && m_is_sta_mode == Q6_MHI_WAN)
+			{
+				fd_wwan_ioctl = open(WWAN_QMI_IOCTL_DEVICE_NAME, O_RDWR);
+				if(fd_wwan_ioctl < 0)
+				{
+					IPACMERR("Failed to open %s.\n",WWAN_QMI_IOCTL_DEVICE_NAME);
+					return false;
+				}
+				IPACMDBG_H("send WAN_IOC_NOTIFY_WAN_STATE down to IPA_PM\n");
+				if(ioctl(fd_wwan_ioctl, WAN_IOC_NOTIFY_WAN_STATE, &wan_state))
+				{
+					IPACMERR("Failed to send WAN_IOC_NOTIFY_WAN_STATE as up %d\n ", wan_state.up);
+				}
+				close(fd_wwan_ioctl);
+			}
+			if (ipa_pm_q6_check > 0)
+				ipa_pm_q6_check--;
+			else
+				IPACMERR(" ipa_pm_q6_check becomes negative !!!\n");
+#endif
+		}
+
 		for (tx_index = 0; tx_index < iface_query->num_tx_props; tx_index++)
 		{
 		    if(iptype != tx_prop->tx[tx_index].ip)
@@ -4128,7 +4859,7 @@
 		}
 
 		/* Delete the default wan route*/
-		if (iptype == IPA_IP_v6)
+		if (iptype == IPA_IP_v6 && m_is_sta_mode != Q6_MHI_WAN)
 		{
 		   	IPACMDBG_H("ip-type %d: default v6 wan RT-rule deleted\n",iptype);
 			if (m_routing.DeleteRoutingHdl(wan_route_rule_v6_hdl_a5[0], IPA_IP_v6) == false)
@@ -4149,14 +4880,7 @@
 		if (iptype == IPA_IP_v4)
 		{
 			wandown_data->ipv4_addr = wan_v4_addr;
-			if (m_is_sta_mode!=Q6_WAN)
-			{
-				wandown_data->is_sta = true;
-			}
-			else
-			{
-				wandown_data->is_sta = false;
-			}
+			wandown_data->backhaul_type = m_is_sta_mode;
 			evt_data.event = IPA_HANDLE_WAN_DOWN;
 			evt_data.evt_data = (void *)wandown_data;
 			/* Insert IPA_HANDLE_WAN_DOWN to command queue */
@@ -4178,17 +4902,30 @@
 			{
 				memset(IPACM_Wan::wan_up_dev_name, 0, sizeof(IPACM_Wan::wan_up_dev_name));
 			}
+
+			/* Delete MHI frag rule */
+			if(delete_offload_frag_rule())
+			{
+				IPACMERR("Failed to delete DL frag rule \n");
+				return IPACM_FAILURE;
+			}
+			/* Delete MHI icmpv6 exception rule */
+			if(delete_icmpv6_exception_rule())
+			{
+				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
 		{
-			if (m_is_sta_mode!=Q6_WAN)
-			{
-				wandown_data->is_sta = true;
-			}
-			else
-			{
-				wandown_data->is_sta = false;
-			}
+
+			wandown_data->backhaul_type = m_is_sta_mode;
 			memcpy(wandown_data->ipv6_prefix, ipv6_prefix, sizeof(wandown_data->ipv6_prefix));
 			evt_data.event = IPA_HANDLE_WAN_DOWN_V6;
 			evt_data.evt_data = (void *)wandown_data;
@@ -4245,8 +4982,9 @@
 			IPACMDBG_H("dev %s delete producer dependency\n", dev_name);
 			IPACMDBG_H("depend Got pipe %d rm index : %d \n", tx_prop->tx[0].dst_pipe, IPACM_Iface::ipacmcfg->ipa_client_rm_map_tbl[tx_prop->tx[0].dst_pipe]);
 			IPACM_Iface::ipacmcfg->DelRmDepend(IPACM_Iface::ipacmcfg->ipa_client_rm_map_tbl[tx_prop->tx[0].dst_pipe]);
+		}
 #ifdef WAN_IOC_NOTIFY_WAN_STATE
-		} else {
+		else {
 			IPACMDBG_H("ipa_pm_q6_check to %d\n", ipa_pm_q6_check);
 			if(ipa_pm_q6_check == 1)
 			{
@@ -4268,8 +5006,6 @@
 			else
 				IPACMERR(" ipa_pm_q6_check becomes negative !!!\n");
 		}
-#else
-}
 #endif
 		/* Delete the default route*/
 		if (iptype == IPA_IP_v6)
@@ -4294,14 +5030,7 @@
 		if (iptype == IPA_IP_v4)
 		{
 			wandown_data->ipv4_addr = wan_v4_addr;
-			if (m_is_sta_mode!=Q6_WAN)
-			{
-				wandown_data->is_sta = true;
-			}
-			else
-			{
-				wandown_data->is_sta = false;
-			}
+			wandown_data->backhaul_type = m_is_sta_mode;
 			evt_data.event = IPA_HANDLE_WAN_DOWN;
 			evt_data.evt_data = (void *)wandown_data;
 			/* Insert IPA_HANDLE_WAN_DOWN to command queue */
@@ -4322,14 +5051,8 @@
 		}
 		else
 		{
-			if (m_is_sta_mode!=Q6_WAN)
-			{
-				wandown_data->is_sta = true;
-			}
-			else
-			{
-				wandown_data->is_sta = false;
-			}
+
+			wandown_data->backhaul_type = m_is_sta_mode;
 			memcpy(wandown_data->ipv6_prefix, ipv6_prefix, sizeof(wandown_data->ipv6_prefix));
 			evt_data.event = IPA_HANDLE_WAN_DOWN_V6;
 			evt_data.evt_data = (void *)wandown_data;
@@ -4400,9 +5123,8 @@
 	flt_rule_entry.rule.to_uc = 0;
 	flt_rule_entry.rule.eq_attrib_type = 1;
 	flt_rule_entry.rule.action = IPA_PASS_TO_ROUTING;
-#ifdef FEATURE_IPA_V3
-	flt_rule_entry.rule.hashable = true;
-#endif
+	if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+		flt_rule_entry.rule.hashable = true;
 	flt_rule_entry.rule.rt_tbl_idx = rt_tbl_idx.idx;
 
 	memcpy(&flt_rule_entry.rule.attrib,
@@ -4449,9 +5171,8 @@
 	flt_rule_entry.rule.to_uc = 0;
 	flt_rule_entry.rule.eq_attrib_type = 1;
 	flt_rule_entry.rule.action = IPA_PASS_TO_ROUTING;
-#ifdef FEATURE_IPA_V3
-	flt_rule_entry.rule.hashable = true;
-#endif
+	if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+		flt_rule_entry.rule.hashable = true;
 	flt_rule_entry.rule.rt_tbl_idx = rt_tbl_idx.idx;
 
 	memcpy(&flt_rule_entry.rule.attrib,
@@ -4522,6 +5243,18 @@
 		handle_route_del_evt(IPA_IP_v4);
 		IPACMDBG_H("Delete default v4 routing rules\n");
 
+		if(m_is_sta_mode == Q6_MHI_WAN)
+		{
+			/* Delete default v4 RT rule */
+			IPACMDBG_H("Delete default v4 routing rules\n");
+			if (m_routing.DeleteRoutingHdl(dft_rt_rule_hdl[0], IPA_IP_v4) == false)
+			{
+				IPACMERR("Routing v6-lan-RT rule deletion failed!\n");
+				res = IPACM_FAILURE;
+				goto fail;
+			}
+		}
+
 #ifdef FEATURE_IPA_ANDROID
 		/* posting wan_down_tether for lan clients */
 #ifdef FEATURE_IPACM_HAL
@@ -4551,6 +5284,19 @@
 		}
 		handle_route_del_evt(IPA_IP_v6);
 		IPACMDBG_H("Delete default v6 routing rules\n");
+
+		if(m_is_sta_mode == Q6_MHI_WAN)
+		{
+			/* Delete default v6 RT rule */
+			IPACMDBG_H("Delete default v6 routing rules\n");
+			if (m_routing.DeleteRoutingHdl(dft_rt_rule_hdl[1], IPA_IP_v6) == false)
+			{
+				IPACMERR("Routing v6-wan-RT rule deletion failed!\n");
+				res = IPACM_FAILURE;
+				goto fail;
+			}
+		}
+
 #ifdef FEATURE_IPA_ANDROID
 		/* posting wan_down_tether for lan clients */
 #ifdef FEATURE_IPACM_HAL
@@ -4572,26 +5318,14 @@
 #endif
 	}
 
-	/* Delete default v4 RT rule */
-	if (ip_type != IPA_IP_v6)
+	if(m_is_sta_mode != Q6_MHI_WAN)
 	{
-		IPACMDBG_H("Delete default v4 routing rules\n");
-		if (m_routing.DeleteRoutingHdl(dft_rt_rule_hdl[0], IPA_IP_v4) == false)
+		/* Delete default v4 RT rule */
+		if (ip_type != IPA_IP_v6)
 		{
-			IPACMERR("Routing rule deletion failed!\n");
-			res = IPACM_FAILURE;
-			goto fail;
-		}
-	}
-
-	/* delete default v6 RT rule */
-	if (ip_type != IPA_IP_v4)
-	{
-		IPACMDBG_H("Delete default v6 routing rules\n");
-		/* May have multiple ipv6 iface-routing rules*/
-		for (i = 0; i < 2*num_dft_rt_v6; i++)
-		{
-			if (m_routing.DeleteRoutingHdl(dft_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES+i], IPA_IP_v6) == false)
+			/* no need delete v4 RSC routing rules */
+			IPACMDBG_H("Delete default v4 routing rules\n");
+			if (m_routing.DeleteRoutingHdl(dft_rt_rule_hdl[0], IPA_IP_v4) == false)
 			{
 				IPACMERR("Routing rule deletion failed!\n");
 				res = IPACM_FAILURE;
@@ -4599,130 +5333,143 @@
 			}
 		}
 
-		IPACMDBG_H("finished delete default v6 RT rules\n ");
-	}
-
-
-	/* clean wan-client header, routing rules */
-	IPACMDBG_H("left %d wan clients need to be deleted \n ", num_wan_client);
-	for (i = 0; i < num_wan_client; i++)
-	{
-			/* Del NAT rules before ipv4 RT rules are delete */
-			if(get_client_memptr(wan_client, i)->ipv4_set == true)
+		/* delete default v6 RT rule */
+		if (ip_type != IPA_IP_v4)
+		{
+			IPACMDBG_H("Delete default v6 routing rules\n");
+			/* May have multiple ipv6 iface-routing rules*/
+			for (i = 0; i < 2*num_dft_rt_v6; i++)
 			{
-				IPACMDBG_H("Clean Nat Rules for ipv4:0x%x\n", get_client_memptr(wan_client, i)->v4_addr);
-				CtList->HandleSTAClientDelEvt(get_client_memptr(wan_client, i)->v4_addr);
-			}
-
-			if (delete_wan_rtrules(i, IPA_IP_v4))
-			{
-				IPACMERR("unbale to delete wan-client v4 route rules for index %d\n", i);
-				res = IPACM_FAILURE;
-				goto fail;
-			}
-
-			if (delete_wan_rtrules(i, IPA_IP_v6))
-			{
-				IPACMERR("unbale to delete ecm-client v6 route rules for index %d\n", i);
-				res = IPACM_FAILURE;
-				goto fail;
-			}
-
-			IPACMDBG_H("Delete %d client header\n", num_wan_client);
-
-
-			if(get_client_memptr(wan_client, i)->ipv4_header_set == true)
-			{
-				if (m_header.DeleteHeaderHdl(get_client_memptr(wan_client, i)->hdr_hdl_v4)
-					== false)
+				if (m_routing.DeleteRoutingHdl(dft_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES+i], IPA_IP_v6) == false)
 				{
+					IPACMERR("Routing rule deletion failed!\n");
 					res = IPACM_FAILURE;
 					goto fail;
 				}
 			}
+			IPACMDBG_H("finished delete default v6 RT rules\n ");
+		}
+		/* clean wan-client header, routing rules */
+		IPACMDBG_H("left %d wan clients need to be deleted \n ", num_wan_client);
+		for (i = 0; i < num_wan_client; i++)
+		{
+				/* Del NAT rules before ipv4 RT rules are delete */
+				if(get_client_memptr(wan_client, i)->ipv4_set == true)
+				{
+					IPACMDBG_H("Clean Nat Rules for ipv4:0x%x\n", get_client_memptr(wan_client, i)->v4_addr);
+					CtList->HandleSTAClientDelEvt(get_client_memptr(wan_client, i)->v4_addr);
+				}
 
-			if(get_client_memptr(wan_client, i)->ipv6_header_set == true)
+				if (delete_wan_rtrules(i, IPA_IP_v4))
+				{
+					IPACMERR("unbale to delete wan-client v4 route rules for index %d\n", i);
+					res = IPACM_FAILURE;
+					goto fail;
+				}
+
+				if (delete_wan_rtrules(i, IPA_IP_v6))
+				{
+					IPACMERR("unbale to delete ecm-client v6 route rules for index %d\n", i);
+					res = IPACM_FAILURE;
+					goto fail;
+				}
+
+				IPACMDBG_H("Delete %d client header\n", num_wan_client);
+				if(get_client_memptr(wan_client, i)->ipv4_header_set == true)
+				{
+					if (m_header.DeleteHeaderHdl(get_client_memptr(wan_client, i)->hdr_hdl_v4)
+						== false)
+					{
+						res = IPACM_FAILURE;
+						goto fail;
+					}
+				}
+				if(get_client_memptr(wan_client, i)->ipv6_header_set == true)
+				{
+					if (m_header.DeleteHeaderHdl(get_client_memptr(wan_client, i)->hdr_hdl_v6)
+						== false)
+					{
+						res = IPACM_FAILURE;
+						goto fail;
+					}
+				}
+		} /* end of for loop */
+		/* free the edm clients cache */
+		IPACMDBG_H("Free wan clients cache\n");
+
+		/* check software routing fl rule hdl */
+		if (softwarerouting_act == true)
+		{
+			if(m_is_sta_mode == Q6_MHI_WAN)
 			{
-			if (m_header.DeleteHeaderHdl(get_client_memptr(wan_client, i)->hdr_hdl_v6)
-					== false)
+				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)
+		{
+			if (dft_v4fl_rule_hdl[0] != 0)
+			{
+				if (m_filtering.DeleteFilteringHdls(dft_v4fl_rule_hdl,
+					IPA_IP_v4,
+					IPV4_DEFAULT_FILTERTING_RULES) == false)
+				{
+					IPACMERR("Error Delete Filtering rules, aborting...\n");
+					res = IPACM_FAILURE;
+					goto fail;
+				}
+				IPACM_Iface::ipacmcfg->decreaseFltRuleCount(rx_prop->rx[0].src_pipe, IPA_IP_v4, IPV4_DEFAULT_FILTERTING_RULES);
+				IPACMDBG_H("finished delete default v4 filtering rules\n ");
+			}
+		}
+		/* free dft ipv6 filter rule handlers if any */
+		if (ip_type != IPA_IP_v4 && rx_prop != NULL)
+		{
+			if (dft_v6fl_rule_hdl[0] != 0)
+			{
+				if (m_filtering.DeleteFilteringHdls(dft_v6fl_rule_hdl,
+					IPA_IP_v6,
+					IPV6_DEFAULT_FILTERTING_RULES) == false)
+				{
+					IPACMERR("ErrorDeleting Filtering rule, aborting...\n");
+					res = IPACM_FAILURE;
+					goto fail;
+				}
+				IPACM_Iface::ipacmcfg->decreaseFltRuleCount(rx_prop->rx[0].src_pipe, IPA_IP_v6, IPV6_DEFAULT_FILTERTING_RULES);
+			}
+			if(num_ipv6_dest_flt_rule > 0 && num_ipv6_dest_flt_rule <= MAX_DEFAULT_v6_ROUTE_RULES)
+			{
+				if(m_filtering.DeleteFilteringHdls(ipv6_dest_flt_rule_hdl,  IPA_IP_v6, num_ipv6_dest_flt_rule) == false)
+				{
+					IPACMERR("Failed to delete ipv6 dest flt rules.\n");
+					res = IPACM_FAILURE;
+					goto fail;
+				}
+				IPACM_Iface::ipacmcfg->decreaseFltRuleCount(rx_prop->rx[0].src_pipe, IPA_IP_v6, num_ipv6_dest_flt_rule);
+			}
+			IPACMDBG_H("finished delete default v6 filtering rules\n ");
+		}
+		if(hdr_proc_hdl_dummy_v6)
+		{
+			if(m_header.DeleteHeaderProcCtx(hdr_proc_hdl_dummy_v6) == false)
+			{
+				IPACMERR("Failed to delete hdr_proc_hdl_dummy_v6\n");
 				res = IPACM_FAILURE;
 				goto fail;
 			}
-			}
-	} /* end of for loop */
-
-	/* free the edm clients cache */
-	IPACMDBG_H("Free wan clients cache\n");
-
-	/* check software routing fl rule hdl */
-	if (softwarerouting_act == true)
-	{
-		handle_software_routing_disable();
-	}
-
-	/* free dft ipv4 filter rule handlers if any */
-	if (ip_type != IPA_IP_v6 && rx_prop != NULL)
-	{
-		if (dft_v4fl_rule_hdl[0] != 0)
+		}
+		if(hdr_hdl_dummy_v6)
 		{
-			if (m_filtering.DeleteFilteringHdls(dft_v4fl_rule_hdl,
-				IPA_IP_v4,
-				IPV4_DEFAULT_FILTERTING_RULES) == false)
+			if (m_header.DeleteHeaderHdl(hdr_hdl_dummy_v6) == false)
 			{
-				IPACMERR("Error Delete Filtering rules, aborting...\n");
+				IPACMERR("Failed to delete hdr_hdl_dummy_v6\n");
 				res = IPACM_FAILURE;
 				goto fail;
 			}
-			IPACM_Iface::ipacmcfg->decreaseFltRuleCount(rx_prop->rx[0].src_pipe, IPA_IP_v4, IPV4_DEFAULT_FILTERTING_RULES);
-			IPACMDBG_H("finished delete default v4 filtering rules\n ");
-		}
-	}
-
-	/* free dft ipv6 filter rule handlers if any */
-	if (ip_type != IPA_IP_v4 && rx_prop != NULL)
-	{
-		if (dft_v6fl_rule_hdl[0] != 0)
-		{
-			if (m_filtering.DeleteFilteringHdls(dft_v6fl_rule_hdl,
-				IPA_IP_v6,
-				IPV6_DEFAULT_FILTERTING_RULES) == false)
-			{
-				IPACMERR("ErrorDeleting Filtering rule, aborting...\n");
-				res = IPACM_FAILURE;
-				goto fail;
-			}
-			IPACM_Iface::ipacmcfg->decreaseFltRuleCount(rx_prop->rx[0].src_pipe, IPA_IP_v6, IPV6_DEFAULT_FILTERTING_RULES);
-		}
-
-		if(num_ipv6_dest_flt_rule > 0 && num_ipv6_dest_flt_rule <= MAX_DEFAULT_v6_ROUTE_RULES)
-		{
-			if(m_filtering.DeleteFilteringHdls(ipv6_dest_flt_rule_hdl,  IPA_IP_v6, num_ipv6_dest_flt_rule) == false)
-			{
-				IPACMERR("Failed to delete ipv6 dest flt rules.\n");
-				res = IPACM_FAILURE;
-				goto fail;
-			}
-			IPACM_Iface::ipacmcfg->decreaseFltRuleCount(rx_prop->rx[0].src_pipe, IPA_IP_v6, num_ipv6_dest_flt_rule);
-		}
-		IPACMDBG_H("finished delete default v6 filtering rules\n ");
-	}
-	if(hdr_proc_hdl_dummy_v6)
-	{
-		if(m_header.DeleteHeaderProcCtx(hdr_proc_hdl_dummy_v6) == false)
-		{
-			IPACMERR("Failed to delete hdr_proc_hdl_dummy_v6\n");
-			res = IPACM_FAILURE;
-			goto fail;
-		}
-	}
-	if(hdr_hdl_dummy_v6)
-	{
-		if (m_header.DeleteHeaderHdl(hdr_hdl_dummy_v6) == false)
-		{
-			IPACMERR("Failed to delete hdr_hdl_dummy_v6\n");
-			res = IPACM_FAILURE;
-			goto fail;
 		}
 	}
 fail:
@@ -4797,6 +5544,9 @@
 		goto fail;
 	}
 
+	/* reset the mtu size */
+	mtu_size = DEFAULT_MTU_SIZE;
+
 	if(ip_type == IPA_IP_v4)
 	{
 		num_ipv4_modem_pdn--;
@@ -4845,6 +5595,21 @@
 		install_wan_filtering_rule(false);
 		}
 
+		IPACMDBG_H("Delete default v4 coalesce routing rules\n");
+		if (m_routing.DeleteRoutingHdl(dft_coalesce_rt_rule_hdl[0], IPA_IP_v4) == false)
+		{
+			IPACMERR("Routing rule RSC TCP deletion failed!\n");
+			res = IPACM_FAILURE;
+			goto fail;
+		}
+
+		if (m_routing.DeleteRoutingHdl(dft_coalesce_rt_rule_hdl[1], IPA_IP_v4) == false)
+		{
+			IPACMERR("Routing rule RSB UDP deletion failed!\n");
+			res = IPACM_FAILURE;
+			goto fail;
+		}
+
 		if (m_routing.DeleteRoutingHdl(dft_rt_rule_hdl[0], IPA_IP_v4) == false)
 		{
 			IPACMERR("Routing rule deletion failed!\n");
@@ -4903,6 +5668,13 @@
 
 		for (i = 0; i < 2*num_dft_rt_v6; i++)
 		{
+			/* delete v6 colasce rules */
+			if (m_routing.DeleteRoutingHdl(dft_coalesce_rt_rule_hdl[2*MAX_DEFAULT_v4_ROUTE_RULES+i], IPA_IP_v6) == false)
+			{
+				IPACMERR("Colasce Routing rule deletion failed!\n");
+				res = IPACM_FAILURE;
+				goto fail;
+			}
 			if (m_routing.DeleteRoutingHdl(dft_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES+i], IPA_IP_v6) == false)
 			{
 				IPACMERR("Routing rule deletion failed!\n");
@@ -4987,6 +5759,21 @@
 			install_wan_filtering_rule(false);
 		}
 
+		IPACMDBG_H("Delete default v4 coalesce routing rules\n");
+		if (m_routing.DeleteRoutingHdl(dft_coalesce_rt_rule_hdl[0], IPA_IP_v4) == false)
+		{
+			IPACMERR("Routing rule RSC TCP deletion failed!\n");
+			res = IPACM_FAILURE;
+			goto fail;
+		}
+
+		if (m_routing.DeleteRoutingHdl(dft_coalesce_rt_rule_hdl[1], IPA_IP_v4) == false)
+		{
+			IPACMERR("Routing rule RSB UDP deletion failed!\n");
+			res = IPACM_FAILURE;
+			goto fail;
+		}
+
 		if (m_routing.DeleteRoutingHdl(dft_rt_rule_hdl[0], IPA_IP_v4) == false)
 		{
 			IPACMERR("Routing rule deletion failed!\n");
@@ -4996,6 +5783,13 @@
 
 		for (i = 0; i < 2*num_dft_rt_v6; i++)
 		{
+			/* delete v6 colasce rules */
+			if (m_routing.DeleteRoutingHdl(dft_coalesce_rt_rule_hdl[2*MAX_DEFAULT_v4_ROUTE_RULES+i], IPA_IP_v6) == false)
+			{
+				IPACMERR("Colasce Routing rule deletion failed!\n");
+				res = IPACM_FAILURE;
+				goto fail;
+			}
 			if (m_routing.DeleteRoutingHdl(dft_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES+i], IPA_IP_v6) == false)
 			{
 				IPACMERR("Routing rule deletion failed!\n");
@@ -5005,11 +5799,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)
@@ -5111,9 +5905,8 @@
 		flt_rule_entry.flt_rule_hdl = -1;
 		flt_rule_entry.status = -1;
 		flt_rule_entry.rule.action = IPA_PASS_TO_ROUTING;
-#ifdef FEATURE_IPA_V3
-		flt_rule_entry.rule.hashable = true;
-#endif
+		if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+			flt_rule_entry.rule.hashable = true;
 
 		flt_rule_entry.rule.rt_tbl_idx = rt_tbl_idx.idx;
 
@@ -5173,9 +5966,8 @@
 		flt_rule_entry.flt_rule_hdl = -1;
 		flt_rule_entry.status = -1;
 		flt_rule_entry.rule.action = IPA_PASS_TO_ROUTING;
-#ifdef FEATURE_IPA_V3
-		flt_rule_entry.rule.hashable = true;
-#endif
+		if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+			flt_rule_entry.rule.hashable = true;
 		flt_rule_entry.rule.rt_tbl_idx = rt_tbl_idx.idx;
 		memcpy(&flt_rule_entry.rule.attrib,
 					&rx_prop->rx[0].attrib,
@@ -5316,57 +6108,6 @@
 	return res;
 }
 
-void IPACM_Wan::change_to_network_order(ipa_ip_type iptype, ipa_rule_attrib* attrib)
-{
-	if(attrib == NULL)
-	{
-		IPACMERR("Attribute pointer is NULL.\n");
-		return;
-	}
-
-	if(iptype == IPA_IP_v6)
-	{
-		int i;
-		for(i=0; i<4; i++)
-		{
-			attrib->u.v6.src_addr[i] = htonl(attrib->u.v6.src_addr[i]);
-			attrib->u.v6.src_addr_mask[i] = htonl(attrib->u.v6.src_addr_mask[i]);
-			attrib->u.v6.dst_addr[i] = htonl(attrib->u.v6.dst_addr[i]);
-			attrib->u.v6.dst_addr_mask[i] = htonl(attrib->u.v6.dst_addr_mask[i]);
-		}
-	}
-	else
-	{
-		IPACMDBG_H("IP type is not IPv6, do nothing: %d\n", iptype);
-	}
-
-	return;
-}
-
-bool IPACM_Wan::is_global_ipv6_addr(uint32_t* ipv6_addr)
-{
-	if(ipv6_addr == NULL)
-	{
-		IPACMERR("IPv6 address is empty.\n");
-		return false;
-	}
-	IPACMDBG_H("Get ipv6 address with first word 0x%08x.\n", ipv6_addr[0]);
-
-	uint32_t ipv6_link_local_prefix, ipv6_link_local_prefix_mask;
-	ipv6_link_local_prefix = 0xFE800000;
-	ipv6_link_local_prefix_mask = 0xFFC00000;
-	if((ipv6_addr[0] & ipv6_link_local_prefix_mask) == (ipv6_link_local_prefix & ipv6_link_local_prefix_mask))
-	{
-		IPACMDBG_H("This IPv6 address is link local.\n");
-		return false;
-	}
-	else
-	{
-		IPACMDBG_H("This IPv6 address is not link local.\n");
-		return true;
-	}
-}
-
 /* handle STA WAN-client */
 /* handle WAN client initial, construct full headers (tx property) */
 int IPACM_Wan::handle_wan_hdr_init(uint8_t *mac_addr)
@@ -5854,9 +6595,8 @@
 				rt_rule_entry->rule.hdr_hdl = get_client_memptr(wan_client, wan_index)->hdr_hdl_v4;
 				rt_rule_entry->rule.attrib.u.v4.dst_addr = get_client_memptr(wan_client, wan_index)->v4_addr;
 				rt_rule_entry->rule.attrib.u.v4.dst_addr_mask = 0xFFFFFFFF;
-#ifdef FEATURE_IPA_V3
-				rt_rule_entry->rule.hashable = true;
-#endif
+				if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+					rt_rule_entry->rule.hashable = true;
 				if (false == m_routing.AddRoutingRule(rt_rule))
 				{
 					IPACMERR("Routing rule addition failed!\n");
@@ -5904,9 +6644,8 @@
 					rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[1] = 0xFFFFFFFF;
 					rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[2] = 0xFFFFFFFF;
 					rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[3] = 0xFFFFFFFF;
-#ifdef FEATURE_IPA_V3
-					rt_rule_entry->rule.hashable = true;
-#endif
+					if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+						rt_rule_entry->rule.hashable = true;
 					if (false == m_routing.AddRoutingRule(rt_rule))
 					{
 						IPACMERR("Routing rule addition failed!\n");
@@ -6430,3 +7169,761 @@
 	}
 	return IPACM_SUCCESS;
 }
+
+int IPACM_Wan::handle_coalesce_evt()
+{
+	struct ipa_ioc_add_rt_rule *rt_rule = NULL;
+	struct ipa_rt_rule_add *rt_rule_entry;
+	const int NUM_RULES = 1;
+	int res = IPACM_SUCCESS;
+	struct ipa_ioc_get_hdr hdr;
+	uint32_t i;
+
+	if(wan_v4_addr_set)
+	{
+		/* Delete default RSC v4 RT rule */
+		if (m_routing.DeleteRoutingHdl(dft_coalesce_rt_rule_hdl[0], IPA_IP_v4) == false)
+		{
+			IPACMERR("Routing old RSC TCP RT rule deletion failed!\n");
+			return  IPACM_FAILURE;
+		}
+		if (m_routing.DeleteRoutingHdl(dft_coalesce_rt_rule_hdl[1], IPA_IP_v4) == false)
+		{
+			IPACMERR("Routing old RSB UDP RT rule deletion failed!\n");
+			return  IPACM_FAILURE;
+		}
+		/* Delete default v4 RT rule */
+		IPACMDBG_H("Delete default v4 routing rules\n");
+		if (m_routing.DeleteRoutingHdl(dft_rt_rule_hdl[0], IPA_IP_v4) == false)
+		{
+			IPACMERR("Routing old RT rule deletion failed!\n");
+			return  IPACM_FAILURE;
+		}
+
+		/* apply the new coalesce configuration */
+		rt_rule = (struct ipa_ioc_add_rt_rule *)
+			 calloc(1, sizeof(struct ipa_ioc_add_rt_rule) +
+							NUM_RULES * sizeof(struct ipa_rt_rule_add));
+		if (!rt_rule)
+		{
+			IPACMERR("Error Locate ipa_ioc_add_rt_rule memory...\n");
+			return IPACM_FAILURE;
+		}
+		rt_rule->commit = 1;
+		rt_rule->num_rules = NUM_RULES;
+		rt_rule->ip = IPA_IP_v4;
+		rt_rule_entry = &rt_rule->rules[0];
+		rt_rule_entry->at_rear = false;
+		rt_rule_entry->rule.attrib.attrib_mask = IPA_FLT_DST_ADDR;
+		/* still need setup v4 default routing rule to APPs*/
+		strlcpy(rt_rule->rt_tbl_name, IPACM_Iface::ipacmcfg->rt_tbl_lan_v4.name, sizeof(rt_rule->rt_tbl_name));
+		rt_rule_entry->rule.attrib.u.v4.dst_addr = wan_v4_addr;
+		rt_rule_entry->rule.attrib.u.v4.dst_addr_mask = 0xFFFFFFFF;
+		if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+			rt_rule_entry->rule.hashable = false;
+		/* query qmap header*/
+		memset(&hdr, 0, sizeof(hdr));
+		strlcpy(hdr.name, tx_prop->tx[0].hdr_name, sizeof(hdr.name));
+		hdr.name[IPA_RESOURCE_NAME_MAX-1] = '\0';
+		if(m_header.GetHeaderHandle(&hdr) == false)
+		{
+			IPACMERR("Failed to get QMAP header.\n");
+			res = IPACM_FAILURE;
+			goto fail;
+		}
+		rt_rule_entry->rule.hdr_hdl = hdr.hdl;
+		rt_rule_entry->rule.dst = IPA_CLIENT_APPS_WAN_CONS;
+		/*  default v4 rt-rule */
+#ifdef IPA_RT_SUPPORT_COAL
+			rt_rule_entry->rule.coalesce = false;
+#endif
+		/* default v4 rt-rule */
+		if (false == m_routing.AddRoutingRule(rt_rule))
+		{
+			IPACMERR("Routing rule addition failed!\n");
+			res = IPACM_FAILURE;
+			goto fail;
+		}
+		else if (rt_rule_entry->status)
+		{
+			IPACMERR("rt rule adding failed. Result=%d\n", rt_rule_entry->status);
+			res = rt_rule_entry->status;
+			goto fail;
+		}
+		dft_rt_rule_hdl[0] = rt_rule_entry->rt_rule_hdl;
+		IPACMDBG_H("ipv4 wan iface rt-rule hdll=0x%x\n", dft_rt_rule_hdl[0]);
+
+		/* RSC TCP rule*/
+		rt_rule_entry->rule.attrib.attrib_mask |= IPA_FLT_PROTOCOL;
+		rt_rule_entry->rule.attrib.u.v4.protocol = (uint8_t)IPACM_FIREWALL_IPPROTO_TCP;
+#ifdef IPA_RT_SUPPORT_COAL
+		if (IPACM_Wan::coalesce_enable_info[ext_prop->ext[0].mux_id].coalesce_tcp_enable)
+			rt_rule_entry->rule.coalesce = true;
+		else
+			rt_rule_entry->rule.coalesce = false;
+#endif
+		if (false == m_routing.AddRoutingRule(rt_rule))
+		{
+			IPACMERR("Routing rule addition failed!\n");
+			res = IPACM_FAILURE;
+			goto fail;
+		}
+		else if (rt_rule_entry->status)
+		{
+			IPACMERR("rsc tcp rt rule adding failed. Result=%d\n", rt_rule_entry->status);
+			res = rt_rule_entry->status;
+			goto fail;
+		}
+		dft_coalesce_rt_rule_hdl[0] = rt_rule_entry->rt_rule_hdl;
+		IPACMDBG_H("ipv4 wan iface rsc tcp rt-rule hdll=0x%x\n enable(%d)", dft_coalesce_rt_rule_hdl[0],
+			IPACM_Wan::coalesce_enable_info[ext_prop->ext[0].mux_id].coalesce_tcp_enable);
+
+		/* RSB UDP rule*/
+		rt_rule_entry->rule.attrib.u.v4.protocol = (uint8_t)IPACM_FIREWALL_IPPROTO_UDP;
+#ifdef IPA_RT_SUPPORT_COAL
+		if (IPACM_Wan::coalesce_enable_info[ext_prop->ext[0].mux_id].coalesce_tcp_enable)
+			rt_rule_entry->rule.coalesce = true;
+		else
+			rt_rule_entry->rule.coalesce = false;
+#endif
+		if (false == m_routing.AddRoutingRule(rt_rule))
+		{
+			IPACMERR("Routing rule addition failed!\n");
+			res = IPACM_FAILURE;
+			goto fail;
+		}
+		else if (rt_rule_entry->status)
+		{
+			IPACMERR("rsb udp rt rule adding failed. Result=%d\n", rt_rule_entry->status);
+			res = rt_rule_entry->status;
+			goto fail;
+		}
+		dft_coalesce_rt_rule_hdl[1] = rt_rule_entry->rt_rule_hdl;
+		IPACMDBG_H("ipv4 wan iface rsb udp rt-rule hdll=0x%x enable(%d)\n", dft_coalesce_rt_rule_hdl[1],
+			IPACM_Wan::coalesce_enable_info[ext_prop->ext[0].mux_id].coalesce_udp_enable);
+fail:
+	free(rt_rule);
+	}
+	/* v6 */
+	if (num_dft_rt_v6 !=0)
+	{
+		for (i = 0; i < 2*num_dft_rt_v6; i++)
+		{
+			/* delete v6 colasce rules */
+			if (m_routing.DeleteRoutingHdl(dft_coalesce_rt_rule_hdl[2*MAX_DEFAULT_v4_ROUTE_RULES+i], IPA_IP_v6) == false)
+			{
+				IPACMERR("Colasce Routing rule deletion failed!\n");
+				return  IPACM_FAILURE;
+			}
+			/* delete v6 default rules */
+			if (m_routing.DeleteRoutingHdl(dft_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES+i], IPA_IP_v6) == false)
+			{
+				IPACMERR("Routing rule deletion failed!\n");
+				return  IPACM_FAILURE;
+			}
+		}
+
+		rt_rule = (struct ipa_ioc_add_rt_rule *)
+			calloc(1, sizeof(struct ipa_ioc_add_rt_rule) +
+				NUM_RULES * sizeof(struct ipa_rt_rule_add));
+		if (!rt_rule)
+		{
+			IPACMERR("Error Locate ipa_ioc_add_rt_rule memory...\n");
+			return IPACM_FAILURE;
+		}
+		rt_rule->commit = 1;
+		rt_rule->num_rules = NUM_RULES;
+		rt_rule->ip = IPA_IP_v6;
+
+		for (i = 0; i < num_dft_rt_v6; i++)
+		{
+			/* setup same rule for v6_wan table */
+			strlcpy(rt_rule->rt_tbl_name, IPACM_Iface::ipacmcfg->rt_tbl_v6.name, sizeof(rt_rule->rt_tbl_name));
+			rt_rule_entry = &rt_rule->rules[0];
+			rt_rule_entry->at_rear = false;
+			rt_rule_entry->rule.attrib.attrib_mask = IPA_FLT_DST_ADDR;
+			rt_rule_entry->rule.attrib.u.v6.dst_addr[0] = ipv6_addr[num_dft_rt_v6][0];
+			rt_rule_entry->rule.attrib.u.v6.dst_addr[1] = ipv6_addr[num_dft_rt_v6][1];
+			rt_rule_entry->rule.attrib.u.v6.dst_addr[2] = ipv6_addr[num_dft_rt_v6][2];
+			rt_rule_entry->rule.attrib.u.v6.dst_addr[3] = ipv6_addr[num_dft_rt_v6][3];
+			rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[0] = 0xFFFFFFFF;
+			rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[1] = 0xFFFFFFFF;
+			rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[2] = 0xFFFFFFFF;
+			rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[3] = 0xFFFFFFFF;
+			if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+				rt_rule_entry->rule.hashable = false;
+			strlcpy(hdr.name, tx_prop->tx[0].hdr_name, sizeof(hdr.name));
+			hdr.name[IPA_RESOURCE_NAME_MAX-1] = '\0';
+			if(m_header.GetHeaderHandle(&hdr) == false)
+			{
+				IPACMERR("Failed to get QMAP header.\n");
+				return IPACM_FAILURE;
+			}
+			rt_rule_entry->rule.hdr_hdl = hdr.hdl;
+			rt_rule_entry->rule.dst = IPA_CLIENT_APPS_WAN_CONS;
+			/* legacy default v4 rt-rule */
+#ifdef IPA_RT_SUPPORT_COAL
+			rt_rule_entry->rule.coalesce = false;
+#endif
+			/* legacy default v6 rt-rule */
+			if (false == m_routing.AddRoutingRule(rt_rule))
+			{
+				IPACMERR("Routing rule addition failed!\n");
+				res = IPACM_FAILURE;
+				goto fail2;
+			}
+			else if (rt_rule_entry->status)
+			{
+				IPACMERR("rt rule adding failed. Result=%d\n", rt_rule_entry->status);
+				res = rt_rule_entry->status;
+				goto fail2;
+			}
+			dft_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES + 2*i] = rt_rule_entry->rt_rule_hdl;
+
+			/* setup same rule for v6_lan table*/
+			strlcpy(rt_rule->rt_tbl_name, IPACM_Iface::ipacmcfg->rt_tbl_wan_v6.name, sizeof(rt_rule->rt_tbl_name));
+			if (false == m_routing.AddRoutingRule(rt_rule))
+			{
+				IPACMERR("Routing rule addition failed!\n");
+				res = IPACM_FAILURE;
+				goto fail2;
+			}
+			else if (rt_rule_entry->status)
+			{
+				IPACMERR("rt rule adding failed. Result=%d\n", rt_rule_entry->status);
+				res = rt_rule_entry->status;
+				goto fail2;
+			}
+			dft_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES + 2*i+1] = rt_rule_entry->rt_rule_hdl;
+			IPACMDBG_H("ipv6 wan iface rt-rule hdl=0x%x hdl=0x%x, entry: %d %d\n",
+				dft_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES + 2*i],
+				dft_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES + 2*i+1],
+				MAX_DEFAULT_v4_ROUTE_RULES + 2*i,
+				MAX_DEFAULT_v4_ROUTE_RULES + 2*i+1);
+			/* RSC TCP rule*/
+			rt_rule_entry->rule.attrib.attrib_mask |= IPA_FLT_NEXT_HDR;
+			rt_rule_entry->rule.attrib.u.v6.next_hdr = (uint8_t)IPACM_FIREWALL_IPPROTO_TCP;
+#ifdef IPA_RT_SUPPORT_COAL
+			if (IPACM_Wan::coalesce_enable_info[ext_prop->ext[0].mux_id].coalesce_tcp_enable)
+				rt_rule_entry->rule.coalesce = true;
+			else
+				rt_rule_entry->rule.coalesce = false;
+#endif
+			if (false == m_routing.AddRoutingRule(rt_rule))
+			{
+				IPACMERR("Routing rule addition failed!\n");
+				res = IPACM_FAILURE;
+				goto fail2;
+			}
+			else if (rt_rule_entry->status)
+			{
+				IPACMERR("rsc tcp rt rule adding failed. Result=%d\n", rt_rule_entry->status);
+				res = rt_rule_entry->status;
+				goto fail2;
+			}
+			dft_coalesce_rt_rule_hdl[2*MAX_DEFAULT_v4_ROUTE_RULES + 2*i] = rt_rule_entry->rt_rule_hdl;
+			IPACMDBG_H("ipv6 wan iface rsc tcp rt-rule hdll=0x%x\n enable(%d)", dft_coalesce_rt_rule_hdl[2*MAX_DEFAULT_v4_ROUTE_RULES + 2*i],
+				IPACM_Wan::coalesce_enable_info[ext_prop->ext[0].mux_id].coalesce_tcp_enable);
+			/* RSB UDP rule*/
+			rt_rule_entry->rule.attrib.u.v6.next_hdr = (uint8_t)IPACM_FIREWALL_IPPROTO_UDP;
+#ifdef IPA_RT_SUPPORT_COAL
+			if (IPACM_Wan::coalesce_enable_info[ext_prop->ext[0].mux_id].coalesce_udp_enable)
+				rt_rule_entry->rule.coalesce = true;
+			else
+				rt_rule_entry->rule.coalesce = false;
+#endif
+			if (false == m_routing.AddRoutingRule(rt_rule))
+			{
+				IPACMERR("Routing rule addition failed!\n");
+				res = IPACM_FAILURE;
+				goto fail2;
+			}
+			else if (rt_rule_entry->status)
+			{
+				IPACMERR("rsb udp rt rule adding failed. Result=%d\n", rt_rule_entry->status);
+				res = rt_rule_entry->status;
+				goto fail2;
+			}
+			dft_coalesce_rt_rule_hdl[2*MAX_DEFAULT_v4_ROUTE_RULES + 2*i+1] = rt_rule_entry->rt_rule_hdl;
+			IPACMDBG_H("ipv6 wan iface rsb udp rt-rule hdll=0x%x\n enable(%d)", dft_coalesce_rt_rule_hdl[2*MAX_DEFAULT_v4_ROUTE_RULES + 2*i+1],
+				IPACM_Wan::coalesce_enable_info[ext_prop->ext[0].mux_id].coalesce_udp_enable);
+		}
+fail2:
+	free(rt_rule);
+	}
+	return res;
+}
+
+int IPACM_Wan::add_offload_frag_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 frag rule\n");
+	len = sizeof(struct ipa_ioc_add_flt_rule) + 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)1;
+
+	/* Configuring Fragment Filtering Rule */
+	memset(&flt_rule_entry, 0, sizeof(struct ipa_flt_rule_add));
+	flt_rule_entry.at_rear = false;
+	flt_rule_entry.flt_rule_hdl = -1;
+	flt_rule_entry.status = -1;
+
+	flt_rule_entry.rule.retain_hdr = 1;
+	flt_rule_entry.rule.to_uc = 0;
+	flt_rule_entry.rule.eq_attrib_type = 1;
+	flt_rule_entry.rule.action = IPA_PASS_TO_EXCEPTION;
+	if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+		flt_rule_entry.rule.hashable = true;
+	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_FRAGMENT;
+
+	/* 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");
+		goto fail;
+	}
+	memcpy(&flt_rule_entry.rule.eq_attrib,
+		&flt_eq.eq_attrib,
+		sizeof(flt_rule_entry.rule.eq_attrib));
+	memcpy(&(pFilteringTable->rules[0]), &flt_rule_entry, sizeof(struct ipa_flt_rule_add));
+
+	/* add rule */
+	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 */
+	mhi_dl_v4_frag_hdl = pFilteringTable->rules[0].flt_rule_hdl;
+
+fail:
+	close(fd);
+	if(pFilteringTable != NULL)
+	{
+		free(pFilteringTable);
+	}
+	return res;
+}
+
+int IPACM_Wan::delete_offload_frag_rule()
+{
+	int res = IPACM_SUCCESS;
+	int len;
+	ipa_ioc_del_flt_rule *pFilteringTable = NULL;
+
+	struct ipa_flt_rule_del flt_rule_entry;
+
+	IPACMDBG_H("deleting MHI frag rule \n");
+	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 false;
+	}
+	memset(pFilteringTable, 0, len);
+
+	pFilteringTable->commit = 1;
+	pFilteringTable->ip = IPA_IP_v4;
+	pFilteringTable->num_hdls = (uint8_t)1;
+
+	if (mhi_dl_v4_frag_hdl == 0)
+	{
+		IPACMERR("invalid dl_v4_frag_hdl.\n");
+		res = false;
+		goto fail;
+	}
+
+	/* Configuring Software-Routing Filtering Rule */
+	memset(&flt_rule_entry, 0, sizeof(struct ipa_flt_rule_del));
+	flt_rule_entry.hdl = mhi_dl_v4_frag_hdl;
+
+	memcpy(&(pFilteringTable->hdl[0]), &flt_rule_entry, sizeof(struct ipa_flt_rule_del));
+
+	if(false == m_filtering.DelOffloadFilteringRule(pFilteringTable))
+	{
+		IPACMERR("Failed to delete DL offload frag rule.\n");
+		res = false;
+		goto fail;
+	}
+	mhi_dl_v4_frag_hdl = 0;
+
+fail:
+	if(pFilteringTable != NULL)
+	{
+		free(pFilteringTable);
+	}
+	return res;
+}
+
+int IPACM_Wan::add_icmpv6_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 icmpv6 rule\n");
+	len = sizeof(struct ipa_ioc_add_flt_rule) + 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_v6;
+	pFilteringTable->num_rules = (uint8_t)1;
+
+	/* Configuring ICMPv6 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;
+	if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+		flt_rule_entry.rule.hashable = true;
+	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_NEXT_HDR;
+	flt_rule_entry.rule.attrib.u.v6.next_hdr = (uint8_t)IPACM_FIREWALL_IPPROTO_ICMP6;
+
+	/* 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;
+
+	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));
+	memcpy(&(pFilteringTable->rules[0]), &flt_rule_entry, sizeof(struct ipa_flt_rule_add));
+
+	/* add rule */
+	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 */
+	icmpv6_exception_hdl = pFilteringTable->rules[0].flt_rule_hdl;
+
+fail:
+	close(fd);
+	if(pFilteringTable != NULL)
+	{
+		free(pFilteringTable);
+	}
+	return res;
+}
+
+int IPACM_Wan::delete_icmpv6_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 icmpv6 rule \n");
+	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_v6;
+	pFilteringTable->num_hdls = (uint8_t)1;
+
+	if (icmpv6_exception_hdl == 0)
+	{
+		IPACMERR("invalid icmpv6_exception_hdl.\n");
+		res = IPACM_FAILURE;
+		goto fail;
+	}
+
+	memset(&flt_rule_entry, 0, sizeof(struct ipa_flt_rule_del));
+	flt_rule_entry.hdl = icmpv6_exception_hdl;
+
+	memcpy(&(pFilteringTable->hdl[0]), &flt_rule_entry, sizeof(struct ipa_flt_rule_del));
+
+	if(false == m_filtering.DelOffloadFilteringRule(pFilteringTable))
+	{
+		IPACMERR("Failed to delete MHI icmpv6 rule.\n");
+		res = IPACM_FAILURE;
+		goto fail;
+	}
+	icmpv6_exception_hdl = 0;
+
+fail:
+	if(pFilteringTable != NULL)
+	{
+		free(pFilteringTable);
+	}
+	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 */
+		if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+			flt_rule_entry.rule.eq_attrib.rule_eq_bitmap |= (1<<7);
+		else
+			flt_rule_entry.rule.eq_attrib.rule_eq_bitmap |= (1<<8);
+
+	/* 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;
+}
+
+int IPACM_Wan::query_mtu_size()
+{
+	int fd;
+	struct ifreq if_mtu;
+
+	fd = socket(AF_INET, SOCK_DGRAM, 0);
+	if ( fd < 0 ) {
+		IPACMERR("ipacm: socket open failed [%d]\n", fd);
+		return IPACM_FAILURE;
+	}
+
+	strlcpy(if_mtu.ifr_name, dev_name, IFNAMSIZ);
+	IPACMDBG_H("device name: %s\n", dev_name);
+	if_mtu.ifr_name[IFNAMSIZ - 1] = '\0';
+
+	if ( ioctl(fd, SIOCGIFMTU, &if_mtu) < 0 ) {
+		IPACMERR("ioctl failed to get mtu\n");
+		close(fd);
+		return IPACM_FAILURE;
+	}
+	IPACMDBG_H("mtu=[%d]\n", if_mtu.ifr_mtu);
+	if (if_mtu.ifr_mtu < DEFAULT_MTU_SIZE) {
+		mtu_size = if_mtu.ifr_mtu;
+		IPACMDBG_H("replaced mtu=[%d] for (%s)\n", mtu_size, dev_name);
+	}
+
+	close(fd);
+	return IPACM_SUCCESS;
+}
diff --git a/ipacm/src/IPACM_Wlan.cpp b/ipacm/src/IPACM_Wlan.cpp
index 427aef3..689cc78 100644
--- a/ipacm/src/IPACM_Wlan.cpp
+++ b/ipacm/src/IPACM_Wlan.cpp
@@ -206,20 +206,21 @@
 
 			IPACMDBG_H("Received IPA_LAN_DELETE_SELF event.\n");
 			IPACMDBG_H("ipa_WLAN (%s):ipa_index (%d) instance close \n", IPACM_Iface::ipacmcfg->iface_table[ipa_if_num].iface_name, ipa_if_num);
-#ifdef FEATURE_ETH_BRIDGE_LE
-			if(rx_prop != NULL)
+			if (IPACM_Iface::ipacmcfg->isEthBridgingSupported())
 			{
-				free(rx_prop);
+				if(rx_prop != NULL)
+				{
+					free(rx_prop);
+				}
+				if(tx_prop != NULL)
+				{
+					free(tx_prop);
+				}
+				if(iface_query != NULL)
+				{
+					free(iface_query);
+				}
 			}
-			if(tx_prop != NULL)
-			{
-				free(tx_prop);
-			}
-			if(iface_query != NULL)
-			{
-				free(iface_query);
-			}
-#endif
 			delete this;
 		}
 		break;
@@ -295,7 +296,7 @@
 					{
 						if(data->iptype == IPA_IP_v4 || data->iptype == IPA_IP_MAX)
 						{
-							if(IPACM_Wan::backhaul_is_sta_mode == false)
+							if(IPACM_Wan::backhaul_mode == Q6_WAN)
 							{
 								ext_prop = IPACM_Iface::ipacmcfg->GetExtProp(IPA_IP_v4);
 								IPACM_Lan::handle_wan_up_ex(ext_prop, IPA_IP_v4,
@@ -318,7 +319,7 @@
 							memcpy(ipv6_prefix, IPACM_Wan::backhaul_ipv6_prefix, sizeof(ipv6_prefix));
 							install_ipv6_prefix_flt_rule(IPACM_Wan::backhaul_ipv6_prefix);
 
-							if(IPACM_Wan::backhaul_is_sta_mode == false)
+							if(IPACM_Wan::backhaul_mode == Q6_WAN)
 							{
 								ext_prop = IPACM_Iface::ipacmcfg->GetExtProp(IPA_IP_v6);
 								IPACM_Lan::handle_wan_up_ex(ext_prop, IPA_IP_v6, 0);
@@ -350,7 +351,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);
 					}
 				}
 			}
@@ -366,7 +367,7 @@
 			IPACMERR("No event data is found.\n");
 			return;
 		}
-		IPACMDBG_H("Backhaul is sta mode?%d, if_index_tether:%d tether_if_name:%s xlat_mux_id: %d\n", data_wan_tether->is_sta,
+		IPACMDBG_H("Backhaul is sta mode?%d, if_index_tether:%d tether_if_name:%s xlat_mux_id: %d\n", data_wan_tether->backhaul_type,
 					data_wan_tether->if_index_tether,
 					IPACM_Iface::ipacmcfg->iface_table[data_wan_tether->if_index_tether].iface_name,
 					data_wan_tether->xlat_mux_id);
@@ -387,7 +388,7 @@
 				if(is_downstream_set[IPA_IP_v4] == true)
 				{
 					IPACMDBG_H("Downstream was set before, adding UL rules.\n");
-					if(data_wan_tether->is_sta == false)
+					if(data_wan_tether->backhaul_type == Q6_WAN)
 					{
 						ext_prop = IPACM_Iface::ipacmcfg->GetExtProp(IPA_IP_v4);
 						handle_wan_up_ex(ext_prop, IPA_IP_v4,
@@ -398,7 +399,7 @@
 				}
 			}
 #else
-			if(data_wan_tether->is_sta == false)
+			if(data_wan_tether->backhaul_type == Q6_WAN)
 			{
 				ext_prop = IPACM_Iface::ipacmcfg->GetExtProp(IPA_IP_v4);
 				handle_wan_up_ex(ext_prop, IPA_IP_v4, 0);
@@ -418,7 +419,7 @@
 			IPACMERR("No event data is found.\n");
 			return;
 		}
-		IPACMDBG_H("Backhaul is sta mode?%d, if_index_tether:%d tether_if_name:%s\n", data_wan_tether->is_sta,
+		IPACMDBG_H("Backhaul is sta mode?%d, if_index_tether:%d tether_if_name:%s\n", data_wan_tether->backhaul_type,
 					data_wan_tether->if_index_tether,
 					IPACM_Iface::ipacmcfg->iface_table[data_wan_tether->if_index_tether].iface_name);
 #ifndef FEATURE_IPACM_HAL
@@ -441,7 +442,7 @@
 					IPACMDBG_H("Downstream was set before, adding UL rules.\n");
 					memcpy(ipv6_prefix, data_wan_tether->ipv6_prefix, sizeof(ipv6_prefix));
 					install_ipv6_prefix_flt_rule(data_wan_tether->ipv6_prefix);
-					if(data_wan_tether->is_sta == false)
+					if(data_wan_tether->backhaul_type == Q6_WAN)
 					{
 						ext_prop = IPACM_Iface::ipacmcfg->GetExtProp(IPA_IP_v6);
 						handle_wan_up_ex(ext_prop, IPA_IP_v6, 0);
@@ -453,7 +454,7 @@
 				}
 			}
 #else
-			if(data_wan_tether->is_sta == false)
+			if(data_wan_tether->backhaul_type == Q6_WAN)
 			{
 				ext_prop = IPACM_Iface::ipacmcfg->GetExtProp(IPA_IP_v6);
 				handle_wan_up_ex(ext_prop, IPA_IP_v6, 0);
@@ -479,7 +480,7 @@
 			IPACMERR("No rx prop.\n");
 			return;
 		}
-		IPACMDBG_H("Backhaul is sta mode?%d, if_index_tether:%d tether_if_name:%s\n", data_wan_tether->is_sta,
+		IPACMDBG_H("Backhaul is sta mode?%d, if_index_tether:%d tether_if_name:%s\n", data_wan_tether->backhaul_type,
 					data_wan_tether->if_index_tether,
 					IPACM_Iface::ipacmcfg->iface_table[data_wan_tether->if_index_tether].iface_name);
 #ifndef FEATURE_IPACM_HAL
@@ -499,11 +500,11 @@
 				if(is_downstream_set[IPA_IP_v4] == true)
 				{
 					IPACMDBG_H("Downstream was set before, deleting UL rules.\n");
-					handle_wan_down(data_wan_tether->is_sta);
+					handle_wan_down(data_wan_tether->backhaul_type);
 				}
 			}
 #else
-			handle_wan_down(data_wan_tether->is_sta);
+			handle_wan_down(data_wan_tether->backhaul_type);
 #endif
 		}
 		break;
@@ -521,7 +522,7 @@
 			IPACMERR("No rx prop.\n");
 			return;
 		}
-		IPACMDBG_H("Backhaul is sta mode?%d, if_index_tether:%d tether_if_name:%s\n", data_wan_tether->is_sta,
+		IPACMDBG_H("Backhaul is sta mode?%d, if_index_tether:%d tether_if_name:%s\n", data_wan_tether->backhaul_type,
 					data_wan_tether->if_index_tether,
 					IPACM_Iface::ipacmcfg->iface_table[data_wan_tether->if_index_tether].iface_name);
 #ifndef FEATURE_IPACM_HAL
@@ -543,13 +544,13 @@
 					IPACMDBG_H("Downstream was set before, deleting UL rules.\n");
 					/* reset usb-client ipv6 rt-rules */
 					handle_wlan_client_reset_rt(IPA_IP_v6);
-					handle_wan_down_v6(data_wan_tether->is_sta);
+					handle_wan_down_v6(data_wan_tether->backhaul_type);
 				}
 			}
 #else
 			/* reset usb-client ipv6 rt-rules */
 			handle_wlan_client_reset_rt(IPA_IP_v6);
-			handle_wan_down_v6(data_wan_tether->is_sta);
+			handle_wan_down_v6(data_wan_tether->backhaul_type);
 #endif
 		}
 		break;
@@ -561,6 +562,25 @@
 		if(ipa_interface_index == ipa_if_num)
 		{
 			IPACMDBG_H("Received IPA_DOWNSTREAM_ADD event.\n");
+#ifdef FEATURE_IPA_ANDROID
+			if (IPACM_Wan::isXlat() && (data->prefix.iptype == IPA_IP_v4))
+			{
+				/* indicate v4-offload */
+				IPACM_OffloadManager::num_offload_v4_tethered_iface++;
+				IPACMDBG_H("in xlat: update num_offload_v4_tethered_iface %d\n", IPACM_OffloadManager::num_offload_v4_tethered_iface);
+
+				/* xlat not support for 2st tethered iface */
+				if (IPACM_OffloadManager::num_offload_v4_tethered_iface > 1)
+				{
+					IPACMDBG_H("Not support 2st downstream iface %s for xlat, cur: %d\n", dev_name,
+						IPACM_OffloadManager::num_offload_v4_tethered_iface);
+					return;
+				}
+			}
+
+			IPACMDBG_H(" support downstream iface %s, cur %d\n", dev_name,
+				IPACM_OffloadManager::num_offload_v4_tethered_iface);
+#endif
 			if(data->prefix.iptype < IPA_IP_MAX && is_downstream_set[data->prefix.iptype] == false)
 			{
 				IPACMDBG_H("Add downstream for IP iptype %d.\n", data->prefix.iptype);
@@ -582,11 +602,12 @@
 							install_ipv6_prefix_flt_rule(ipv6_prefix);
 						}
 
-						if (IPACM_Wan::backhaul_is_sta_mode == false) /* LTE */
+						if (IPACM_Wan::backhaul_mode == Q6_WAN) /* LTE */
 						{
 							ext_prop = IPACM_Iface::ipacmcfg->GetExtProp(data->prefix.iptype);
 							if (data->prefix.iptype == IPA_IP_v4)
 							{
+								IPACMDBG_H("check getXlat_Mux_Id:%d\n", IPACM_Wan::getXlat_Mux_Id());
 								handle_wan_up_ex(ext_prop, data->prefix.iptype,
 									IPACM_Wan::getXlat_Mux_Id());
 							}
@@ -620,10 +641,10 @@
 					IPACMDBG_H("Upstream was set before, deleting UL rules.\n");
 					if (data->prefix.iptype == IPA_IP_v4)
 					{
-						handle_wan_down(IPACM_Wan::backhaul_is_sta_mode); /* LTE STA */
+						handle_wan_down(IPACM_Wan::backhaul_mode); /* LTE STA */
 					} else {
 						handle_wlan_client_reset_rt(IPA_IP_v6);
-						handle_wan_down_v6(IPACM_Wan::backhaul_is_sta_mode); /* LTE STA */
+						handle_wan_down_v6(IPACM_Wan::backhaul_mode); /* LTE STA */
 					}
 				}
 			}
@@ -640,10 +661,10 @@
 			IPACMERR("No event data is found.\n");
 			return;
 		}
-		IPACMDBG_H("Backhaul is sta mode?%d\n", data_wan->is_sta);
+		IPACMDBG_H("Backhaul is sta mode?%d\n", data_wan->backhaul_type);
 		if(ip_type == IPA_IP_v4 || ip_type == IPA_IP_MAX)
 		{
-			if(data_wan->is_sta == false)
+			if(data_wan->backhaul_type == Q6_WAN)
 			{
 				ext_prop = IPACM_Iface::ipacmcfg->GetExtProp(IPA_IP_v4);
 				IPACM_Lan::handle_wan_up_ex(ext_prop, IPA_IP_v4, data_wan->xlat_mux_id);
@@ -664,13 +685,13 @@
 			IPACMERR("No event data is found.\n");
 			return;
 		}
-		IPACMDBG_H("Backhaul is sta mode?%d\n", data_wan->is_sta);
+		IPACMDBG_H("Backhaul is sta mode?%d\n", data_wan->backhaul_type);
 		if(ip_type == IPA_IP_v6 || ip_type == IPA_IP_MAX)
 		{
 			memcpy(ipv6_prefix, data_wan->ipv6_prefix, sizeof(ipv6_prefix));
 			install_ipv6_prefix_flt_rule(data_wan->ipv6_prefix);
 
-			if(data_wan->is_sta == false)
+			if(data_wan->backhaul_type == Q6_WAN)
 			{
 				ext_prop = IPACM_Iface::ipacmcfg->GetExtProp(IPA_IP_v6);
 				IPACM_Lan::handle_wan_up_ex(ext_prop, IPA_IP_v6, 0);
@@ -690,12 +711,12 @@
 			IPACMERR("No event data is found.\n");
 			return;
 		}
-		IPACMDBG_H("Backhaul is sta mode?%d\n", data_wan->is_sta);
+		IPACMDBG_H("Backhaul is sta mode?%d\n", data_wan->backhaul_type);
 		if (rx_prop != NULL)
 		{
 			if(ip_type == IPA_IP_v4 || ip_type == IPA_IP_MAX)
 			{
-				handle_wan_down(data_wan->is_sta);
+				handle_wan_down(data_wan->backhaul_type);
 			}
 		}
 		break;
@@ -712,12 +733,12 @@
 		IPACMDBG_H("Received IPA_WAN_V6_DOWN in WLAN-instance and need clean up client IPv6 address \n");
 		/* reset wifi-client ipv6 rt-rules */
 		handle_wlan_client_reset_rt(IPA_IP_v6);
-		IPACMDBG_H("Backhaul is sta mode ? %d\n", data_wan->is_sta);
+		IPACMDBG_H("Backhaul is sta mode ? %d\n", data_wan->backhaul_type);
 		if (rx_prop != NULL)
 		{
 			if(ip_type == IPA_IP_v6 || ip_type == IPA_IP_MAX)
 			{
-				handle_wan_down_v6(data_wan->is_sta);
+				handle_wan_down_v6(data_wan->backhaul_type);
 			}
 		}
 		break;
@@ -845,13 +866,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:
@@ -939,7 +960,7 @@
 		IPACMDBG_H("Received IPA_TETHERING_STATS_UPDATE_EVENT event.\n");
 		if (IPACM_Wan::isWanUP(ipa_if_num) || IPACM_Wan::isWanUP_V6(ipa_if_num))
 		{
-			if(IPACM_Wan::backhaul_is_sta_mode == false) /* LTE */
+			if(IPACM_Wan::backhaul_mode == Q6_WAN) /* LTE */
 			{
 				ipa_get_data_stats_resp_msg_v01 *data = (ipa_get_data_stats_resp_msg_v01 *)param;
 				if (data->ipa_stats_type != QMI_IPA_STATS_TYPE_PIPE_V01)
@@ -1441,6 +1462,7 @@
 	uint32_t tx_index;
 	int wlan_index,v6_num;
 	const int NUM = 1;
+	bool result;
 
 	if(tx_prop == NULL)
 	{
@@ -1561,8 +1583,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);
@@ -1601,9 +1634,8 @@
 					rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[1] = 0xFFFFFFFF;
 					rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[2] = 0xFFFFFFFF;
 					rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[3] = 0xFFFFFFFF;
-#ifdef FEATURE_IPA_V3
-					rt_rule_entry->rule.hashable = true;
-#endif
+					if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+						rt_rule_entry->rule.hashable = true;
 					if (false == m_routing.AddRoutingRule(rt_rule))
 					{
 						IPACMERR("Routing rule addition failed!\n");
@@ -1653,10 +1685,22 @@
 					rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[1] = 0xFFFFFFFF;
 					rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[2] = 0xFFFFFFFF;
 					rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[3] = 0xFFFFFFFF;
-#ifdef FEATURE_IPA_V3
-					rt_rule_entry->rule.hashable = true;
+					if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+						rt_rule_entry->rule.hashable = true;
+#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 (false == m_routing.AddRoutingRule(rt_rule))
+
+					if (result == false)
 					{
 						IPACMERR("Routing rule addition failed!\n");
 						free(rt_rule);
@@ -1667,6 +1711,15 @@
 
 					IPACMDBG_H("tx:%d, rt rule hdl=%x ip-type: %d\n", tx_index,
 							get_client_memptr(wlan_client, wlan_index)->wifi_rt_hdl[tx_index].wifi_rt_rule_hdl_v6_wan[v6_num], iptype);
+
+					/* send client-v6 info to pcie modem only with global ipv6 with tx_index = 0 one time*/
+					if(is_global_ipv6_addr(get_client_memptr(wlan_client, wlan_index)->v6_addr[v6_num]) && (IPACM_Wan::backhaul_mode == Q6_MHI_WAN))
+					{
+						if (add_connection(wlan_index, v6_num))
+						{
+							IPACMERR("PCIE filter rule addition failed! (%d-client) %d v6-entry\n",wlan_index, v6_num);
+						}
+					}
 				}
 			}
 
@@ -1856,8 +1909,8 @@
 	/* delete wan filter rule */
 	if (IPACM_Wan::isWanUP(ipa_if_num) && rx_prop != NULL)
 	{
-		IPACMDBG_H("LAN IF goes down, backhaul type %d\n", IPACM_Wan::backhaul_is_sta_mode);
-		IPACM_Lan::handle_wan_down(IPACM_Wan::backhaul_is_sta_mode);
+		IPACMDBG_H("LAN IF goes down, backhaul type %d\n", IPACM_Wan::backhaul_mode);
+		IPACM_Lan::handle_wan_down(IPACM_Wan::backhaul_mode);
 #ifdef FEATURE_IPA_ANDROID
 #ifndef FEATURE_IPACM_HAL
 		/* Clean-up tethered-iface list */
@@ -1868,8 +1921,8 @@
 
 	if (IPACM_Wan::isWanUP_V6(ipa_if_num) && rx_prop != NULL)
 	{
-		IPACMDBG_H("LAN IF goes down, backhaul type %d\n", IPACM_Wan::backhaul_is_sta_mode);
-		handle_wan_down_v6(IPACM_Wan::backhaul_is_sta_mode);
+		IPACMDBG_H("LAN IF goes down, backhaul type %d\n", IPACM_Wan::backhaul_mode);
+		handle_wan_down_v6(IPACM_Wan::backhaul_mode);
 #ifdef FEATURE_IPA_ANDROID
 		/* Clean-up tethered-iface list */
 		IPACM_Wan::delete_tether_iface(IPA_IP_v6, ipa_if_num);
@@ -1901,16 +1954,16 @@
 		}
 		/* delete private-ipv4 filter rules */
 #ifdef FEATURE_IPA_ANDROID
-		if(m_filtering.DeleteFilteringHdls(private_fl_rule_hdl, IPA_IP_v4, IPA_MAX_PRIVATE_SUBNET_ENTRIES) == false)
+		if(m_filtering.DeleteFilteringHdls(private_fl_rule_hdl, IPA_IP_v4, IPA_MAX_PRIVATE_SUBNET_ENTRIES + IPA_MAX_MTU_ENTRIES) == false)
 		{
 			IPACMERR("Error deleting private subnet IPv4 flt rules.\n");
 			res = IPACM_FAILURE;
 			goto fail;
 		}
-		IPACM_Iface::ipacmcfg->decreaseFltRuleCount(rx_prop->rx[0].src_pipe, IPA_IP_v4, IPA_MAX_PRIVATE_SUBNET_ENTRIES);
+		IPACM_Iface::ipacmcfg->decreaseFltRuleCount(rx_prop->rx[0].src_pipe, IPA_IP_v4, IPA_MAX_PRIVATE_SUBNET_ENTRIES + IPA_MAX_MTU_ENTRIES);
 #else
-		num_private_subnet_fl_rule = IPACM_Iface::ipacmcfg->ipa_num_private_subnet > IPA_MAX_PRIVATE_SUBNET_ENTRIES?
-			IPA_MAX_PRIVATE_SUBNET_ENTRIES : IPACM_Iface::ipacmcfg->ipa_num_private_subnet;
+		num_private_subnet_fl_rule = IPACM_Iface::ipacmcfg->ipa_num_private_subnet > (IPA_MAX_PRIVATE_SUBNET_ENTRIES + IPA_MAX_MTU_ENTRIES)?
+			(IPA_MAX_PRIVATE_SUBNET_ENTRIES + IPA_MAX_MTU_ENTRIES) : IPACM_Iface::ipacmcfg->ipa_num_private_subnet;
 		if(m_filtering.DeleteFilteringHdls(private_fl_rule_hdl, IPA_IP_v4, num_private_subnet_fl_rule) == false)
 		{
 			IPACMERR("Error deleting private subnet flt rules, aborting...\n");
@@ -2070,7 +2123,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 ");
 
@@ -2083,9 +2136,8 @@
 			IPACMDBG_H("depend Got pipe %d rm index : %d \n", rx_prop->rx[0].src_pipe, IPACM_Iface::ipacmcfg->ipa_client_rm_map_tbl[rx_prop->rx[0].src_pipe]);
 			IPACM_Iface::ipacmcfg->DelRmDepend(IPACM_Iface::ipacmcfg->ipa_client_rm_map_tbl[rx_prop->rx[0].src_pipe]);
 		}
-#ifndef FEATURE_ETH_BRIDGE_LE
-		free(rx_prop);
-#endif
+		if (!(IPACM_Iface::ipacmcfg->isEthBridgingSupported()))
+			free(rx_prop);
 	}
 
 	for (i = 0; i < num_wifi_client; i++)
@@ -2099,17 +2151,18 @@
 	{
 		free(wlan_client);
 	}
-#ifndef FEATURE_ETH_BRIDGE_LE
-	if (tx_prop != NULL)
+	if (!(IPACM_Iface::ipacmcfg->isEthBridgingSupported()))
 	{
-		free(tx_prop);
-	}
+		if (tx_prop != NULL)
+		{
+			free(tx_prop);
+		}
 
-	if (iface_query != NULL)
-	{
-		free(iface_query);
+		if (iface_query != NULL)
+		{
+			free(iface_query);
+		}
 	}
-#endif
 
 	is_active = false;
 	post_del_self_evt();
@@ -2411,3 +2464,147 @@
 {
 	return m_is_guest_ap;
 }
+
+int IPACM_Wlan::add_connection(int client_index, int v6_num)
+{
+	int len, res = IPACM_SUCCESS;
+	uint8_t mux_id;
+	ipa_ioc_add_flt_rule *pFilteringTable = NULL;
+	int fd;
+
+	mux_id = IPACM_Iface::ipacmcfg->GetQmapId();
+	/* contruct filter rules to pcie modem */
+	struct ipa_flt_rule_add flt_rule_entry;
+	ipa_ioc_generate_flt_eq flt_eq;
+
+	IPACMDBG("\n");
+	len = sizeof(struct ipa_ioc_add_flt_rule) + 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_v6;
+	pFilteringTable->num_rules = (uint8_t)1;
+
+	/* Configuring Filtering Rule */
+	memset(&flt_rule_entry, 0, sizeof(struct ipa_flt_rule_add));
+	flt_rule_entry.at_rear = true;
+	flt_rule_entry.flt_rule_hdl = -1;
+	flt_rule_entry.status = -1;
+
+	flt_rule_entry.rule.retain_hdr = 1;
+	flt_rule_entry.rule.to_uc = 0;
+	flt_rule_entry.rule.eq_attrib_type = 1;
+	flt_rule_entry.rule.action = IPA_PASS_TO_ROUTING;
+	if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+		flt_rule_entry.rule.hashable = true;
+	flt_rule_entry.rule.attrib.attrib_mask |= IPA_FLT_DST_ADDR;
+	flt_rule_entry.rule.attrib.u.v6.dst_addr[0] = get_client_memptr(wlan_client, client_index)->v6_addr[v6_num][0];
+	flt_rule_entry.rule.attrib.u.v6.dst_addr[1] = get_client_memptr(wlan_client, client_index)->v6_addr[v6_num][1];
+	flt_rule_entry.rule.attrib.u.v6.dst_addr[2] = get_client_memptr(wlan_client, client_index)->v6_addr[v6_num][2];
+	flt_rule_entry.rule.attrib.u.v6.dst_addr[3] = get_client_memptr(wlan_client, client_index)->v6_addr[v6_num][3];
+	flt_rule_entry.rule.attrib.u.v6.dst_addr_mask[0] = 0xFFFFFFFF;
+	flt_rule_entry.rule.attrib.u.v6.dst_addr_mask[1] = 0xFFFFFFFF;
+	flt_rule_entry.rule.attrib.u.v6.dst_addr_mask[2] = 0xFFFFFFFF;
+	flt_rule_entry.rule.attrib.u.v6.dst_addr_mask[3] = 0xFFFFFFFF;
+
+	IPACMDBG_H("ipv6 address got: 0x%x:%x:%x:%x\n", get_client_memptr(wlan_client, client_index)->v6_addr[v6_num][0],
+		get_client_memptr(wlan_client, client_index)->v6_addr[v6_num][1],
+		get_client_memptr(wlan_client, client_index)->v6_addr[v6_num][2],
+		get_client_memptr(wlan_client, client_index)->v6_addr[v6_num][3]);
+
+	/* change to network order for modem */
+	change_to_network_order(IPA_IP_v6, &flt_rule_entry.rule.attrib);
+
+	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;
+
+	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))
+	{
+		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(&(pFilteringTable->rules[0]), &flt_rule_entry, sizeof(struct ipa_flt_rule_add));
+
+	if(false == m_filtering.AddOffloadFilteringRule(pFilteringTable, mux_id, 0))
+	{
+		IPACMERR("Failed to install WAN DL filtering table.\n");
+		res = IPACM_FAILURE;
+		goto fail;
+	}
+
+	get_client_memptr(wlan_client, client_index)->v6_rt_rule_id[v6_num] = pFilteringTable->rules[0].flt_rule_hdl;
+	IPACMDBG_H("%d-st client v6_num %d: id handle 0x%x\n", client_index, v6_num, get_client_memptr(wlan_client, client_index)->v6_rt_rule_id[v6_num]);
+
+fail:
+	close(fd);
+	if(pFilteringTable != NULL)
+	{
+		free(pFilteringTable);
+	}
+	return res;
+}
+
+int IPACM_Wlan::del_connection(int client_index, int v6_num)
+{
+	int len, res = IPACM_SUCCESS;
+	ipa_ioc_del_flt_rule *pFilteringTable = NULL;
+
+	struct ipa_flt_rule_del flt_rule_entry;
+
+	IPACMDBG("\n");
+	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_v6;
+	pFilteringTable->num_hdls = (uint8_t)1;
+
+	/* Configuring Software-Routing Filtering Rule */
+	memset(&flt_rule_entry, 0, sizeof(struct ipa_flt_rule_del));
+	flt_rule_entry.hdl = get_client_memptr(wlan_client, client_index)->v6_rt_rule_id[v6_num];
+
+	memcpy(&(pFilteringTable->hdl[0]), &flt_rule_entry, sizeof(struct ipa_flt_rule_del));
+
+	if(false == m_filtering.DelOffloadFilteringRule(pFilteringTable))
+	{
+		IPACMERR("Failed to install WAN DL filtering table.\n");
+		res = IPACM_FAILURE;
+		goto fail;
+	}
+	get_client_memptr(wlan_client, client_index)->v6_rt_rule_id[v6_num] = 0;
+
+fail:
+	if(pFilteringTable != NULL)
+	{
+		free(pFilteringTable);
+	}
+	return res;
+}
diff --git a/ipacm/src/IPACM_Xml.cpp b/ipacm/src/IPACM_Xml.cpp
index 4451906..d59bbb0 100644
--- a/ipacm/src/IPACM_Xml.cpp
+++ b/ipacm/src/IPACM_Xml.cpp
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2013, The Linux Foundation. All rights reserved.
+Copyright (c) 2013, 2019, The Linux Foundation. All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
@@ -75,6 +75,7 @@
 )
 {
 	xmlNode* child_ptr;
+	uint32_t str_len;
 
 	for (child_ptr  = element->children;
 			 child_ptr != NULL;
@@ -82,7 +83,15 @@
 	{
 		if (child_ptr->type == XML_TEXT_NODE)
 		{
-			return (char*)child_ptr->content;
+			str_len = strlen((char*)child_ptr->content);
+
+			if(str_len < MAX_XML_STR_LEN)
+				return (char*)child_ptr->content;
+			else
+			{
+				IPACMERR("Invalid string size\n");
+				break;
+			}
 		}
 	}
 	return NULL;
diff --git a/ipacm/src/ipacm.rc b/ipacm/src/ipacm.rc
index c1c876b..b2658b6 100644
--- a/ipacm/src/ipacm.rc
+++ b/ipacm/src/ipacm.rc
@@ -30,5 +30,7 @@
     user radio
     group radio inet
 
-on post-fs
+on post-fs-data
+    mkdir /data/vendor/ipa 0770 radio radio
+    chmod 0770 /data/vendor/ipa
     start vendor.ipacm
diff --git a/ipacm_vendor_product.mk b/ipacm_vendor_product.mk
index 96c6116..9fe88b1 100644
--- a/ipacm_vendor_product.mk
+++ b/ipacm_vendor_product.mk
@@ -1,6 +1,43 @@
+TARGET_DISABLE_IPACM := false
+
 #IPACM_DATA
 IPACM_DATA += IPACM_cfg.xml
 IPACM_DATA += ipacm
 IPACM_DATA += ipacm.rc
 
+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
+BOARD_PLATFORM_LIST += msm8917
+BOARD_PLATFORM_LIST += qm215
+BOARD_IPAv3_LIST := msm8998
+BOARD_IPAv3_LIST += sdm845
+BOARD_IPAv3_LIST += sdm710
+BOARD_IPAv3_LIST += msmnile
+BOARD_IPAv3_LIST += kona
+BOARD_IPAv3_LIST += $(MSMSTEPPE)
+BOARD_IPAv3_LIST += $(TRINKET)
+BOARD_IPAv3_LIST += lito
+BOARD_IPAv3_LIST += atoll
+BOARD_IPAv3_LIST += bengal
+BOARD_ETH_BRIDGE_LIST := msmnile
+BOARD_ETH_BRIDGE_LIST += kona
+
+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)))
+
 PRODUCT_PACKAGES += $(IPACM_DATA)
+
+endif # $(TARGET_ARCH)
+endif
+endif
+endif
+endif
\ No newline at end of file
diff --git a/ipanat/Android.bp b/ipanat/Android.bp
new file mode 100644
index 0000000..eefd266
--- /dev/null
+++ b/ipanat/Android.bp
@@ -0,0 +1,28 @@
+
+
+cc_library_shared {
+    name: "libipanat",
+
+     header_libs: ["device_kernel_headers"],
+
+    srcs: [
+        "src/ipa_nat_drv.c",
+        "src/ipa_nat_drvi.c",
+    ],
+
+   shared_libs:
+        ["libcutils",
+        "libdl",
+        "libbase",
+        "libutils",
+    ],
+    export_include_dirs: ["inc"],
+    vendor: true,
+    cflags: [
+        "-DDEBUG",
+        "-Wall",
+        "-Werror",
+    ] + ["-DFEATURE_IPA_ANDROID"],
+
+    clang: true,
+}
diff --git a/ipanat/inc/ipa_nat_drvi.h b/ipanat/inc/ipa_nat_drvi.h
index 8015c98..292a47b 100644
--- a/ipanat/inc/ipa_nat_drvi.h
+++ b/ipanat/inc/ipa_nat_drvi.h
@@ -30,9 +30,9 @@
 #ifndef IPA_NAT_DRVI_H
 #define IPA_NAT_DRVI_H
 
-#include <unistd.h>
 #include <stdio.h>
 #include <sys/ioctl.h>
+#include <sys/types.h>
 #include <fcntl.h>
 #include <sys/mman.h>
 #include <linux/msm_ipa.h>
@@ -40,6 +40,7 @@
 #include <sys/inotify.h>
 #include <errno.h>
 #include <pthread.h>
+#include <unistd.h>
 
 #include "ipa_nat_logi.h"
 
diff --git a/ipanat/src/Android.mk b/ipanat/src/Android.mk
deleted file mode 100644
index af12674..0000000
--- a/ipanat/src/Android.mk
+++ /dev/null
@@ -1,33 +0,0 @@
-BOARD_PLATFORM_LIST := msm8909
-BOARD_PLATFORM_LIST += msm8916
-BOARD_PLATFORM_LIST += msm8917
-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)))
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/../inc
-LOCAL_C_INCLUDES += $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr/include
-LOCAL_ADDITIONAL_DEPENDENCIES := $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr
-
-LOCAL_SRC_FILES := ipa_nat_drv.c \
-                   ipa_nat_drvi.c
-
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/../inc
-LOCAL_VENDOR_MODULE := true
-LOCAL_MODULE_PATH_64 := $(TARGET_OUT_VENDOR)/lib64
-LOCAL_MODULE_PATH_32 := $(TARGET_OUT_VENDOR)/lib
-LOCAL_CFLAGS := -DDEBUG -Wall -Werror
-LOCAL_CFLAGS += -DFEATURE_IPA_ANDROID
-LOCAL_MODULE := libipanat
-LOCAL_MODULE_TAGS := optional
-LOCAL_PRELINK_MODULE := false
-LOCAL_CLANG := true
-include $(BUILD_SHARED_LIBRARY)
-
-endif # $(TARGET_ARCH)
-endif
-endif
diff --git a/os_pickup.mk b/os_pickup.mk
index b247490..59b5051 100644
--- a/os_pickup.mk
+++ b/os_pickup.mk
@@ -1,6 +1,15 @@
-ifeq ($(PRODUCT_PLATFORM),sm8150)
+ifneq ($(filter sm8150 sm7150,$(PRODUCT_PLATFORM)),)
 ifneq ($(BUILD_WITHOUT_VENDOR), true)
 LOCAL_PATH := $(call my-dir)
-include $(call first-makefiles-under,$(LOCAL_PATH))
+subdir_makefiles=$(call first-makefiles-under,$(LOCAL_PATH))
+
+ifeq ($(PRODUCT_PLATFORM),sm7150)
+SKIP_BUILD_DIRS :=    \
+  thermal
+SKIP_MAKEFILES := $(call all-named-subdir-makefiles, $(SKIP_BUILD_DIRS))
+subdir_makefiles := $(filter-out $(SKIP_MAKEFILES), $(subdir_makefiles))
+endif
+
+$(foreach mk,$(subdir_makefiles),$(info including $(mk) ...)$(eval include $(mk)))
 endif
 endif