Merge Android 12

Bug: 202323961
Merged-In: Iec734ba978828f123e524782bf4bce971ccfaede
Change-Id: Ib51c598964cb304fab3a1b94210cb4e90c002334
diff --git a/hal/inc/HAL.h b/hal/inc/HAL.h
index 894438f..92ec135 100644
--- a/hal/inc/HAL.h
+++ b/hal/inc/HAL.h
@@ -123,7 +123,7 @@
      * Yet, a major version update, would not be backwards compatible.  This means that a 2.x HAL
      * could not linked into the same IPACM code base as a 1.x HAL.
      */
-    static HAL* makeIPAHAL(int /* version */, IOffloadManager* /* mgr */);
+    static Return<::android::sp<HAL>> makeIPAHAL(int /* version */, IOffloadManager* /* mgr */);
 
     /* IOffloadConfig */
     Return<void> setHandles(
diff --git a/hal/src/HAL.cpp b/hal/src/HAL.cpp
index 3f1a41f..f18767a 100644
--- a/hal/src/HAL.cpp
+++ b/hal/src/HAL.cpp
@@ -62,7 +62,7 @@
 
 
 /* ------------------------------ PUBLIC ------------------------------------ */
-HAL* HAL::makeIPAHAL(int version, IOffloadManager* mgr) {
+Return<::android::sp<HAL>> HAL::makeIPAHAL(int version, IOffloadManager* mgr) {
     android::hardware::ProcessState::initWithMmapSize((size_t)(2 * KERNEL_PAGE));
 
     if (DBG)
@@ -70,7 +70,7 @@
                 (mgr != nullptr) ? "provided" : "null");
     if (nullptr == mgr) return NULL;
     else if (version != 1) return NULL;
-    HAL* ret = new HAL(mgr);
+    ::android::sp<HAL> ret = new HAL(mgr);
     if (nullptr == ret) return NULL;
     configureRpcThreadpool(1, false);
     ret->registerAsSystemService("ipacm");
diff --git a/ipacm/Android.bp b/ipacm/Android.bp
index 5afc37f..6662fce 100644
--- a/ipacm/Android.bp
+++ b/ipacm/Android.bp
@@ -16,6 +16,7 @@
     header_libs: ["device_kernel_headers"],
     cflags: ["-DFEATURE_IPA_ANDROID"] + ["-DFEATURE_IPACM_RESTART"] + [
         "-DFEATURE_IPACM_HAL",
+	"-DDEBUG",
         "-Wall",
         "-Werror",
         "-Wno-error=macro-redefined",
diff --git a/ipacm/inc/IPACM_ConntrackListener.h b/ipacm/inc/IPACM_ConntrackListener.h
index 24a2c72..2977af7 100644
--- a/ipacm/inc/IPACM_ConntrackListener.h
+++ b/ipacm/inc/IPACM_ConntrackListener.h
@@ -61,6 +61,13 @@
 
 }nat_entry_bundle;
 
+typedef struct _ct_entry
+{
+	struct nf_conntrack *ct;
+	u_int8_t  protocol;
+	enum nf_conntrack_msg_type type;
+}ct_entry;
+
 class IPACM_ConntrackListener : public IPACM_Listener
 {
 
@@ -68,6 +75,7 @@
 	bool isCTReg;
 	bool isNatThreadStart;
 	bool WanUp;
+	bool isProcessCTDone;
 	NatApp *nat_inst;
 
 	int NatIfaceCnt;
@@ -77,12 +85,14 @@
 	uint32_t nonnat_iface_ipv4_addr[MAX_IFACE_ADDRESS];
 	uint32_t sta_clnt_ipv4_addr[MAX_STA_CLNT_IFACES];
 	IPACM_Config *pConfig;
+	ct_entry *ct_entries;
+	ct_entry ct_cache[MAX_CONNTRACK_ENTRIES];
 #ifdef CT_OPT
 	IPACM_LanToLan *p_lan2lan;
 #endif
 
 	void ProcessCTMessage(void *);
-	void ProcessTCPorUDPMsg(struct nf_conntrack *,
+	bool ProcessTCPorUDPMsg(struct nf_conntrack *,
 	enum nf_conntrack_msg_type, u_int8_t);
 	void TriggerWANUp(void *);
 	void TriggerWANDown(uint32_t);
@@ -104,6 +114,7 @@
 	char wan_ifname[IPA_IFACE_NAME_LEN];
 	uint32_t wan_ipaddr;
 	ipacm_wan_iface_type backhaul_mode;
+	bool isReadCTDone;
 	IPACM_ConntrackListener();
 	void event_callback(ipa_cm_event_id, void *data);
 	inline bool isWanUp()
@@ -116,6 +127,11 @@
 	void HandleSTAClientAddEvt(uint32_t);
 	void HandleSTAClientDelEvt(uint32_t);
 	int  CreateConnTrackThreads(void);
+	void readConntrack(int fd);
+	void processConntrack(void);
+	void CacheORDeleteConntrack(struct nf_conntrack *ct,
+		enum nf_conntrack_msg_type type, u_int8_t protocol);
+	void processCacheConntrack(void);
 };
 
 extern IPACM_ConntrackListener *CtList;
diff --git a/ipacm/inc/IPACM_Defs.h b/ipacm/inc/IPACM_Defs.h
index a3cbba1..82e9c6b 100644
--- a/ipacm/inc/IPACM_Defs.h
+++ b/ipacm/inc/IPACM_Defs.h
@@ -105,6 +105,11 @@
 #define NUM_IPV6_PREFIX_FLT_RULE 1
 #define NUM_IPV6_PREFIX_MTU_RULE 1
 
+#define MAX_CONNTRACK_ENTRIES 100
+#define CT_ENTRIES_BUFFER_SIZE 8096
+#define LOOPBACK_MASK 0xFF000000
+#define LOOPBACK_ADDR 0x7F000000
+
 /*---------------------------------------------------------------------------
 										Return values indicating error status
 ---------------------------------------------------------------------------*/
@@ -188,6 +193,10 @@
 	IPA_ETH_BRIDGE_WLAN_SCC_MCC_SWITCH,       /* ipacm_event_eth_bridge*/
 	IPA_SSR_NOTICE,                           /* NULL*/
 	IPA_COALESCE_NOTICE,                      /* NULL*/
+#ifdef IPA_MTU_EVENT_MAX
+	IPA_MTU_SET,                              /* ipa_mtu_info */
+	IPA_MTU_UPDATE,                           /* ipacm_event_mtu_info */
+#endif
 #ifdef FEATURE_L2TP
 	IPA_ADD_VLAN_IFACE,                       /* ipa_ioc_vlan_iface_info */
 	IPA_DEL_VLAN_IFACE,                       /* ipa_ioc_vlan_iface_info */
@@ -402,4 +411,11 @@
 	_ipacm_offload_prefix prefix;
 } ipacm_event_ipahal_stream;
 
+#ifdef IPA_MTU_EVENT_MAX
+typedef struct _ipacm_event_mtu_info
+{
+	int if_index;
+	ipa_mtu_info mtu_info;
+} ipacm_event_mtu_info;
+#endif
 #endif /* IPA_CM_DEFS_H */
diff --git a/ipacm/inc/IPACM_Filtering.h b/ipacm/inc/IPACM_Filtering.h
index 428c21a..6667235 100644
--- a/ipacm/inc/IPACM_Filtering.h
+++ b/ipacm/inc/IPACM_Filtering.h
@@ -48,6 +48,7 @@
 #include <linux/rmnet_ipa_fd_ioctl.h>
 
 #define IPA_PCIE_MODEM_RULE_ID_START 69
+#define IPA_PCIE_MODEM_RULE_ID_MAX 1000
 
 class IPACM_Filtering
 {
@@ -80,6 +81,7 @@
 	int fd; /* File descriptor of the IPA device node /dev/ipa */
 	int total_num_offload_rules;
 	int pcie_modem_rule_id;
+	bool pcie_modem_rule_id_in_use[IPA_PCIE_MODEM_RULE_ID_MAX];
 };
 
 #endif //IPACM_FILTERING_H
diff --git a/ipacm/inc/IPACM_Iface.h b/ipacm/inc/IPACM_Iface.h
index 55d9e99..35d12db 100644
--- a/ipacm/inc/IPACM_Iface.h
+++ b/ipacm/inc/IPACM_Iface.h
@@ -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
@@ -59,7 +59,7 @@
 #define IPV4_DEFAULT_FILTERTING_RULES 3
 
 #ifdef FEATURE_IPA_ANDROID
-#define IPV6_DEFAULT_FILTERTING_RULES 8
+#define IPV6_DEFAULT_FILTERTING_RULES 5
 #else
 #define IPV6_DEFAULT_FILTERTING_RULES 4
 #endif
diff --git a/ipacm/inc/IPACM_Lan.h b/ipacm/inc/IPACM_Lan.h
index 605edb5..de64be1 100644
--- a/ipacm/inc/IPACM_Lan.h
+++ b/ipacm/inc/IPACM_Lan.h
@@ -230,6 +230,8 @@
 
 	int reset_to_dummy_flt_rule(ipa_ip_type iptype, uint32_t rule_hdl);
 
+	virtual int modify_ipv6_prefix_flt_rule(uint32_t* prefix);
+
 	virtual int install_ipv6_prefix_flt_rule(uint32_t* prefix);
 
 	virtual void delete_ipv6_prefix_flt_rule();
diff --git a/ipacm/inc/IPACM_Wan.h b/ipacm/inc/IPACM_Wan.h
index 31949c5..9460938 100644
--- a/ipacm/inc/IPACM_Wan.h
+++ b/ipacm/inc/IPACM_Wan.h
@@ -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
@@ -53,7 +53,7 @@
 #define IPA_V2_NUM_DEFAULT_WAN_FILTER_RULE_IPV4 2
 
 #ifdef FEATURE_IPA_ANDROID
-#define IPA_V2_NUM_DEFAULT_WAN_FILTER_RULE_IPV6 7
+#define IPA_V2_NUM_DEFAULT_WAN_FILTER_RULE_IPV6 4
 #define IPA_V2_NUM_TCP_WAN_FILTER_RULE_IPV6 3
 #define IPA_V2_NUM_MULTICAST_WAN_FILTER_RULE_IPV6 3
 #define IPA_V2_NUM_FRAG_WAN_FILTER_RULE_IPV6 1
@@ -63,6 +63,7 @@
 
 #define NETWORK_STATS "%s %llu %llu %llu %llu"
 #define IPA_NETWORK_STATS_FILE_NAME "/data/misc/ipa/network_stats"
+#define IPA_OFFLOAD_TETHER_STATE_FILE_NAME "/data/vendor/ipa/offload_state"
 
 typedef struct _wan_client_rt_hdl
 {
@@ -105,8 +106,10 @@
 	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;
+
+	static uint16_t mtu_default_wan_v4;
+	static uint16_t mtu_default_wan_v6;
+
 	/* IPACM interface name */
 	static char wan_up_dev_name[IF_NAME_LEN];
 	static uint32_t curr_wan_ip;
@@ -145,15 +148,14 @@
 		{
 			if (isWanUP(ipa_if_num_tether))
 			{
-				return mtu_default_wan;
+				return mtu_default_wan_v4;
 			}
 		}
 		else if (iptype == IPA_IP_v6)
 		{
 			if (isWanUP_V6(ipa_if_num_tether))
 			{
-				return mtu_default_wan;
-
+				return mtu_default_wan_v6;
 			}
 		}
 		return DEFAULT_MTU_SIZE;
@@ -405,6 +407,14 @@
 	/* handle for TCP RST rule */
 	uint32_t tcp_rst_hdl;
 
+	/* V4 MTU value. */
+	uint16_t mtu_v4;
+	bool mtu_v4_set;
+
+	/* V6 MTU value. */
+	uint16_t mtu_v6;
+	bool mtu_v6_set;
+
 	inline ipa_wan_client* get_client_memptr(ipa_wan_client *param, int cnt)
 	{
 	    char *ret = ((char *)param) + (wan_client_len * cnt);
@@ -583,10 +593,13 @@
 		return IPACM_SUCCESS;
 	}
 
-	int handle_wan_hdr_init(uint8_t *mac_addr);
+	int handle_wan_hdr_init(uint8_t *mac_addr, bool replaced = false, int entry = 0);
 	int handle_wan_client_ipaddr(ipacm_event_data_all *data);
 	int handle_wan_client_route_rule(uint8_t *mac_addr, ipa_ip_type iptype);
 
+	/* handle_gw_mac_renew, index_client valiud is success */
+	int handle_gw_mac_renew(ipacm_event_data_all *data, int index_client);
+
 	/* handle new_address event */
 	int handle_addr_evt(ipacm_event_data_addr *data);
 
@@ -594,10 +607,10 @@
 	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);
+	int handle_route_add_evt(ipa_ip_type iptype, bool add_only = false);
 
 	/* construct complete STA ethernet header */
-	int handle_sta_header_add_evt();
+	int handle_sta_header_add_evt(bool renew = false);
 
 	bool check_dft_firewall_rules_attr_mask(IPACM_firewall_conf_t *firewall_config);
 
@@ -612,7 +625,7 @@
 	/* configure the initial firewall filter rules */
 	int config_dft_embms_rules(ipa_ioc_add_flt_rule *pFilteringTable_v4, ipa_ioc_add_flt_rule *pFilteringTable_v6);
 
-	int handle_route_del_evt(ipa_ip_type iptype);
+	int handle_route_del_evt(ipa_ip_type iptype, bool delete_only = false);
 
 	int del_dft_firewall_rules(ipa_ip_type iptype);
 
@@ -645,8 +658,6 @@
 
 	int add_dft_filtering_rule(struct ipa_flt_rule_add* rules, int rule_offset, ipa_ip_type iptype);
 
-	int add_tcpv6_filtering_rule(struct ipa_flt_rule_add* rules, int rule_offset);
-
 	int install_wan_filtering_rule(bool is_sw_routing);
 
 	void handle_wlan_SCC_MCC_switch(bool, ipa_ip_type);
@@ -676,7 +687,7 @@
 
 	int delete_tcp_fin_rst_exception_rule();
 
-	/* Query mtu size */
+	/* MTU helper functions */
 	int query_mtu_size();
 };
 
diff --git a/ipacm/src/IPACM_Config.cpp b/ipacm/src/IPACM_Config.cpp
index c396c6c..d0c33e9 100644
--- a/ipacm/src/IPACM_Config.cpp
+++ b/ipacm/src/IPACM_Config.cpp
@@ -106,7 +106,12 @@
 	__stringify(IPA_ETH_BRIDGE_CLIENT_ADD),                /* ipacm_event_eth_bridge*/
 	__stringify(IPA_ETH_BRIDGE_CLIENT_DEL),                /* ipacm_event_eth_bridge*/
 	__stringify(IPA_ETH_BRIDGE_WLAN_SCC_MCC_SWITCH),       /* ipacm_event_eth_bridge*/
-	__stringify(IPA_SSR_NOTICE)			       /* NULL*/
+	__stringify(IPA_SSR_NOTICE),                           /* NULL*/
+	__stringify(IPA_COALESCE_NOTICE),                      /* NULL*/
+#ifdef IPA_MTU_EVENT_MAX
+	__stringify(IPA_MTU_SET),                              /* ipa_mtu_info */
+	__stringify(IPA_MTU_UPDATE),                           /* ipacm_event_mtu_info */
+#endif
 #ifdef FEATURE_L2TP
 	__stringify(IPA_ADD_VLAN_IFACE),                       /* ipa_ioc_vlan_iface_info */
 	__stringify(IPA_DEL_VLAN_IFACE),                       /* ipa_ioc_vlan_iface_info */
diff --git a/ipacm/src/IPACM_ConntrackClient.cpp b/ipacm/src/IPACM_ConntrackClient.cpp
index 8a2499c..29afd50 100644
--- a/ipacm/src/IPACM_ConntrackClient.cpp
+++ b/ipacm/src/IPACM_ConntrackClient.cpp
@@ -482,12 +482,18 @@
 			 blocks waiting for events. */
 	IPACMDBG("Waiting for events\n");
 
+ctcatch:
 	ret = nfct_catch(pClient->tcp_hdl);
-	if(ret == -1)
+	if((ret == -1) && (errno != ENOMSG))
 	{
-		IPACMERR("(%d)(%s)\n", ret, strerror(errno));
+		IPACMERR("(%d)(%d)(%s)\n", ret, errno, strerror(errno));
 		return NULL;
 	}
+	else
+	{
+		IPACMDBG("ctcatch ret:%d, errno:%d\n", ret, errno);
+		goto ctcatch;
+	}
 
 	IPACMDBG("Exit from tcp thread\n");
 
@@ -568,14 +574,15 @@
 	/* Block to catch events from net filter connection track */
 ctcatch:
 	ret = nfct_catch(pClient->udp_hdl);
-	if(ret == -1)
+	/* Due to conntrack dump, sequence number might mismatch for initial events. */
+	if((ret == -1) && (errno != ENOMSG) && (errno != EILSEQ))
 	{
-		IPACMDBG("(%d)(%s)\n", ret, strerror(errno));
+		IPACMDBG("(%d)(%d)(%s)\n", ret, errno, strerror(errno));
 		return NULL;
 	}
 	else
 	{
-		IPACMDBG("ctcatch ret:%d\n", ret);
+		IPACMDBG("ctcatch ret:%d, errno:%d\n", ret, errno);
 		goto ctcatch;
 	}
 
diff --git a/ipacm/src/IPACM_ConntrackListener.cpp b/ipacm/src/IPACM_ConntrackListener.cpp
index e006393..fd1566d 100644
--- a/ipacm/src/IPACM_ConntrackListener.cpp
+++ b/ipacm/src/IPACM_ConntrackListener.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
@@ -35,19 +35,22 @@
 #include "IPACM_EvtDispatcher.h"
 #include "IPACM_Iface.h"
 #include "IPACM_Wan.h"
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
 
 IPACM_ConntrackListener::IPACM_ConntrackListener()
 {
 	 IPACMDBG("\n");
-
 	 isNatThreadStart = false;
 	 isCTReg = false;
 	 WanUp = false;
+	 isReadCTDone = false;
+	 isProcessCTDone = false;
 	 nat_inst = NatApp::GetInstance();
 
 	 NatIfaceCnt = 0;
 	 StaClntCnt = 0;
 	 pNatIfaces = NULL;
+	 ct_entries = NULL;
 	 pConfig = IPACM_Config::GetInstance();;
 
 	 memset(nat_iface_ipv4_addr, 0, sizeof(nat_iface_ipv4_addr));
@@ -66,6 +69,9 @@
 #ifdef CT_OPT
 	 p_lan2lan = IPACM_LanToLan::getLan2LanInstance();
 #endif
+
+	 /* Initialize the CT cache. */
+	 memset(ct_cache, 0, sizeof(ct_cache));
 }
 
 void IPACM_ConntrackListener::event_callback(ipa_cm_event_id evt,
@@ -97,6 +103,12 @@
 			IPACMDBG_H("Received IPA_HANDLE_WAN_UP event\n");
 			CreateConnTrackThreads();
 			TriggerWANUp(data);
+			if(isReadCTDone && !isProcessCTDone)
+			{
+				processConntrack();
+			}
+			/* Process the cached entries. */
+			processCacheConntrack();
 			break;
 
 	 case IPA_HANDLE_WAN_DOWN:
@@ -667,6 +679,7 @@
 {
 	 ipacm_ct_evt_data *evt_data = (ipacm_ct_evt_data *)param;
 	 u_int8_t l4proto = 0;
+	 bool cache_ct = false;
 
 #ifdef IPACM_DEBUG
 	 char buf[1024];
@@ -688,11 +701,14 @@
 	 }
 	 else
 	 {
-			ProcessTCPorUDPMsg(evt_data->ct, evt_data->type, l4proto);
+			cache_ct = ProcessTCPorUDPMsg(evt_data->ct, evt_data->type, l4proto);
 	 }
 
 	 /* Cleanup item that was allocated during the original CT callback */
-	 nfct_destroy(evt_data->ct);
+	 if (!cache_ct)
+		nfct_destroy(evt_data->ct);
+	 else
+	 	CacheORDeleteConntrack(evt_data->ct, evt_data->type, l4proto);
 	 return;
 }
 
@@ -839,7 +855,7 @@
 	}
 	else if (IPPROTO_UDP == input->rule->protocol)
 	{
-		if (NFCT_T_NEW == input->type)
+		if (NFCT_T_NEW == input->type || NFCT_T_UPDATE == input->type)
 		{
 			IPACMDBG("New UDP connection at time %ld\n", time(NULL));
 			if (!CtList->isWanUp())
@@ -872,6 +888,8 @@
 	 uint32_t status,
 	 nat_table_entry *rule)
 {
+	uint32_t repl_dst_ip;
+
 	if (IPS_DST_NAT == status)
 	{
 		IPACMDBG("Destination NAT\n");
@@ -956,6 +974,15 @@
 		{
 			IPACMDBG("unable to retrieve private port\n");
 		}
+
+		/* If Reply destination IP is not Public IP, install dummy NAT rule. */
+		repl_dst_ip = nfct_get_attr_u32(ct, ATTR_REPL_IPV4_DST);
+		repl_dst_ip = ntohl(repl_dst_ip);
+		if(repl_dst_ip != rule->public_ip)
+		{
+			IPACMDBG_H("Reply dst IP:0x%x not equal to wan ip:0x%x\n",repl_dst_ip, rule->public_ip);
+			rule->private_ip = rule->public_ip;
+		}
 	}
 
 	return;
@@ -1029,7 +1056,7 @@
 }
 
 /* conntrack send in host order and ipa expects in host order */
-void IPACM_ConntrackListener::ProcessTCPorUDPMsg(
+bool IPACM_ConntrackListener::ProcessTCPorUDPMsg(
 	 struct nf_conntrack *ct,
 	 enum nf_conntrack_msg_type type,
 	 u_int8_t l4proto)
@@ -1038,6 +1065,7 @@
 	 uint32_t status = 0;
 	 uint32_t orig_src_ip, orig_dst_ip;
 	 bool isAdd = false;
+	 bool cache_ct = false;
 
 	 nat_entry_bundle nat_entry;
 	 nat_entry.isTempEntry = false;
@@ -1067,7 +1095,7 @@
 		 if(orig_src_ip == 0)
 		 {
 			 IPACMERR("unable to retrieve orig src ip address\n");
-			 return;
+			 return cache_ct;
 		 }
 
 		 orig_dst_ip = nfct_get_attr_u32(ct, ATTR_ORIG_IPV4_DST);
@@ -1075,7 +1103,7 @@
 		 if(orig_dst_ip == 0)
 		 {
 			 IPACMERR("unable to retrieve orig dst ip address\n");
-			 return;
+			 return cache_ct;
 		 }
 
 		if(orig_src_ip == wan_ipaddr)
@@ -1096,13 +1124,16 @@
 #ifdef CT_OPT
 			HandleLan2Lan(ct, type, &rule);
 #endif
-		 	IPACMDBG("Neither source Nor destination nat\n");
-		 	goto IGNORE;
+		 	IPACMDBG("Neither source Nor destination nat.\n");
+			/* If WAN is not up, cache the event. */
+			if(!CtList->isWanUp())
+				cache_ct = true;
+			goto IGNORE;
 		}
 	}
 
-	PopulateTCPorUDPEntry(ct, status, &rule);
 	rule.public_ip = wan_ipaddr;
+	PopulateTCPorUDPEntry(ct, status, &rule);
 
 	if (rule.private_ip != wan_ipaddr)
 	{
@@ -1129,7 +1160,7 @@
 	CheckSTAClient(&rule, &nat_entry.isTempEntry);
 	nat_entry.rule = &rule;
 	AddORDeleteNatEntry(&nat_entry);
-	return;
+	return cache_ct;
 
 IGNORE:
 	IPACMDBG_H("ignoring below Nat Entry\n");
@@ -1139,7 +1170,7 @@
 	IPACMDBG("private port or src port: 0x%x, Decimal:%d\n", rule.private_port, rule.private_port);
 	IPACMDBG("public port or reply dst port: 0x%x, Decimal:%d\n", rule.public_port, rule.public_port);
 	IPACMDBG("Protocol: %d, destination nat flag: %d\n", rule.protocol, rule.dst_nat);
-	return;
+	return cache_ct;
 }
 
 void IPACM_ConntrackListener::HandleSTAClientAddEvt(uint32_t clnt_ip_addr)
@@ -1201,3 +1232,283 @@
 	 nat_inst->FlushTempEntries(clnt_ip_addr, false);
    return;
 }
+
+bool isLocalHostAddr(uint32_t src_ip_addr, uint32_t dst_ip_addr) {
+
+	src_ip_addr = ntohl(src_ip_addr);
+	dst_ip_addr = ntohl(dst_ip_addr);
+	if ((src_ip_addr & LOOPBACK_MASK) == LOOPBACK_ADDR || (dst_ip_addr & LOOPBACK_MASK) == LOOPBACK_ADDR) /* (loopback) */
+		return true;
+	return false;
+}
+
+void IPACM_ConntrackListener::readConntrack(int fd) {
+
+	int recv_bytes = -1, index = 0, len =0;
+	char buffer[CT_ENTRIES_BUFFER_SIZE];
+	struct nf_conntrack *ct;
+	struct nlmsghdr *nl_header;
+   	struct iovec iov = {
+		.iov_base	= buffer,
+		.iov_len	= CT_ENTRIES_BUFFER_SIZE,
+	};
+	struct sockaddr_nl addr;
+	struct msghdr msg = {
+		.msg_name	= &addr,
+		.msg_namelen	= sizeof(struct sockaddr_nl),
+		.msg_iov	= &iov,
+		.msg_iovlen	= 1,
+		.msg_control	= NULL,
+		.msg_controllen	= 0,
+		.msg_flags	= 0,
+	};
+
+	len = MAX_CONNTRACK_ENTRIES * sizeof(ct_entry);
+
+	ct_entries = (ct_entry *) malloc(len);
+	if(ct_entries == NULL)
+	{
+		IPACMERR("unable to allocate ct_entries memory \n");
+		return;
+	}
+	memset(ct_entries, 0, len);
+
+	if( fd < 0)
+	{
+		IPACMDBG_H("Invalid fd %d \n",fd);
+		free(ct_entries);
+		return;
+	}
+	IPACMDBG_H("receiving conntrack entries started.\n");
+	len = CT_ENTRIES_BUFFER_SIZE;
+	while (len > 0)
+	{
+		memset(buffer, 0, CT_ENTRIES_BUFFER_SIZE);
+		recv_bytes = recvmsg(fd, &msg, 0);
+		if(recv_bytes < 0)
+		{
+			IPACMDBG_H("error in receiving conntrack entries %d%s\n",errno, strerror(errno));
+			break;
+		}
+		else
+		{
+			len -= recv_bytes;
+			nl_header = (struct nlmsghdr *)buffer;
+			IPACMDBG_H("Number of bytes:%d to parse\n", recv_bytes);
+			while(NLMSG_OK(nl_header, recv_bytes) && (index < MAX_CONNTRACK_ENTRIES))
+			{
+				if (nl_header->nlmsg_type == NLMSG_ERROR)
+				{
+					IPACMDBG_H("Error, recv_bytes is %d\n",recv_bytes);
+					break;
+				}
+				ct = nfct_new();
+				if (ct != NULL)
+				{
+					int parseResult =  nfct_parse_conntrack((nf_conntrack_msg_type) NFCT_T_ALL,nl_header, ct);
+					if(parseResult != NFCT_T_ERROR && parseResult != 0)
+					{
+						ct_entries[index].ct = ct;
+						ct_entries[index++].type = (nf_conntrack_msg_type)parseResult;
+					}
+					else
+					{
+						IPACMDBG_H("error in parsing  %d%s \n", errno, strerror(errno));
+						nfct_destroy(ct);
+					}
+				}
+				else
+				{
+					IPACMDBG_H("ct allocation failed\n");
+				}
+				if (nl_header->nlmsg_type == NLMSG_DONE)
+				{
+					IPACMDBG_H("Message is done.\n");
+					break;
+				}
+				nl_header = NLMSG_NEXT(nl_header, recv_bytes);
+			}
+		}
+	}
+
+	isReadCTDone = true;
+	IPACMDBG_H("receiving conntrack entries ended. No of entries: %d\n", index);
+	if(isWanUp() && !isProcessCTDone)
+	{
+		IPACMDBG_H("wan is up, process ct entries \n");
+		processConntrack();
+	}
+
+	return ;
+}
+
+void IPACM_ConntrackListener::processConntrack() {
+
+	uint8_t ip_type;
+	int index = 0;
+	ipacm_ct_evt_data *ct_data;
+	IPACMDBG_H("process conntrack started \n");
+	if(ct_entries != NULL)
+	{
+		while(ct_entries[index].ct != NULL)
+		{
+			ip_type = nfct_get_attr_u8(ct_entries[index].ct, ATTR_REPL_L3PROTO);
+			if((AF_INET == ip_type) && isLocalHostAddr(nfct_get_attr_u32(ct_entries[index].ct, ATTR_ORIG_IPV4_SRC),
+					nfct_get_attr_u32(ct_entries[index].ct, ATTR_ORIG_IPV4_DST)))
+			{
+				IPACMDBG_H(" loopback entry \n");
+				goto IGNORE;
+			}
+
+#ifndef CT_OPT
+			if(AF_INET6 == ip_type)
+			{
+				IPACMDBG("Ignoring ipv6(%d) connections\n", ip_type);
+				goto IGNORE;
+			}
+#endif
+
+			ct_data = (ipacm_ct_evt_data *)malloc(sizeof(ipacm_ct_evt_data));
+			if(ct_data == NULL)
+			{
+				IPACMERR("unable to allocate memory \n");
+				goto IGNORE;
+			}
+
+			ct_data->ct = ct_entries[index].ct;
+			ct_data->type = ct_entries[index].type;
+
+#ifdef CT_OPT
+			if(AF_INET6 == ip_type)
+			{
+				ProcessCTV6Message(ct_data);
+			}
+#else
+				ProcessCTMessage(ct_data);
+#endif
+		index++;
+		free(ct_data);
+		continue;
+IGNORE:
+		nfct_destroy(ct_entries[index].ct);
+		index++;
+		}
+	}
+	else
+	{
+		IPACMDBG_H("ct entry is null\n");
+		return ;
+	}
+	isProcessCTDone = true;
+	free(ct_entries);
+	ct_entries = NULL;
+	IPACMDBG_H("process conntrack ended. Number of entries:%d \n", index);
+	return;
+}
+
+void IPACM_ConntrackListener::CacheORDeleteConntrack
+(
+	struct nf_conntrack *ct,
+	enum nf_conntrack_msg_type type,
+	u_int8_t protocol
+)
+{
+	u_int8_t tcp_state;
+	int i = 0, free_idx = -1;
+
+	IPACMDBG("CT entry, type (%d), protocol(%d)\n", type, protocol);
+	/* Check for duplicate entry and in parallel find first free index. */
+	for(; i < MAX_CONNTRACK_ENTRIES; i++)
+	{
+		if (ct_cache[i].ct != NULL)
+		{
+			if (nfct_cmp(ct_cache[i].ct, ct, NFCT_CMP_ORIG | NFCT_CMP_REPL))
+			{
+				/* Duplicate entry. */
+				IPACMDBG("Duplicate CT entry, type (%d), protocol(%d)\n",
+					type, protocol);
+				break;
+			}
+		}
+		else if ((ct_cache[i].ct == NULL) && (free_idx == -1))
+		{
+			/* Cache the first free index. */
+			free_idx = i;
+		}
+	}
+
+	/* Duplicate entry handling. */
+	if (i < MAX_CONNTRACK_ENTRIES)
+	{
+		if (IPPROTO_TCP == protocol)
+		{
+			tcp_state = nfct_get_attr_u8(ct, ATTR_TCP_STATE);
+			if (TCP_CONNTRACK_FIN_WAIT == tcp_state || type == NFCT_T_DESTROY)
+			{
+				IPACMDBG("TCP state TCP_CONNTRACK_FIN_WAIT(%d) "
+							 "or type NFCT_T_DESTROY\n", tcp_state);
+				nfct_destroy(ct_cache[i].ct);
+				nfct_destroy(ct);
+				memset(&ct_cache[i], 0, sizeof(ct_cache[i]));
+				return ;
+			}
+		}
+		if ((IPPROTO_UDP == protocol) && (type == NFCT_T_DESTROY))
+		{
+			IPACMDBG("UDP type NFCT_T_DESTROY\n");
+			nfct_destroy(ct_cache[i].ct);
+			nfct_destroy(ct);
+			memset(&ct_cache[i], 0, sizeof(ct_cache[i]));
+			return;
+		}
+	}
+	else if ((i == MAX_CONNTRACK_ENTRIES) &&
+		(type != NFCT_T_DESTROY) && (free_idx != -1))
+	{
+		if (IPPROTO_TCP == protocol)
+		{
+			tcp_state = nfct_get_attr_u8(ct, ATTR_TCP_STATE);
+			if (TCP_CONNTRACK_ESTABLISHED == tcp_state)
+			{
+				IPACMDBG("TCP state TCP_CONNTRACK_ESTABLISHED\n");
+				/* Cache the entry. */
+				ct_cache[free_idx].ct = ct;
+				ct_cache[free_idx].protocol = protocol;
+				ct_cache[free_idx].type = type;
+				return;
+			}
+		}
+		if (IPPROTO_UDP == protocol)
+		{
+			if (NFCT_T_NEW  == type)
+			{
+				IPACMDBG("New UDP connection\n");
+				/* Cache the entry. */
+				ct_cache[free_idx].ct = ct;
+				ct_cache[free_idx].protocol = protocol;
+				ct_cache[free_idx].type = type;
+				return;
+			}
+		}
+	}
+	/* In all other cases, free the conntracy entry. */
+	nfct_destroy(ct);
+	return ;
+}
+void IPACM_ConntrackListener::processCacheConntrack(void)
+{
+	int i = 0;
+
+	IPACMDBG("Entry:\n");
+	for(; i < MAX_CONNTRACK_ENTRIES; i++)
+	{
+		if (ct_cache[i].ct != NULL)
+		{
+			ProcessTCPorUDPMsg(ct_cache[i].ct, ct_cache[i].type, ct_cache[i].protocol);
+			nfct_destroy(ct_cache[i].ct);
+			memset(&ct_cache[i], 0, sizeof(ct_cache[i]));
+		}
+	}
+	IPACMDBG("Exit:\n");
+}
+
diff --git a/ipacm/src/IPACM_Filtering.cpp b/ipacm/src/IPACM_Filtering.cpp
index 8aa25a6..875a794 100644
--- a/ipacm/src/IPACM_Filtering.cpp
+++ b/ipacm/src/IPACM_Filtering.cpp
@@ -60,6 +60,7 @@
 	}
 	total_num_offload_rules = 0;
 	pcie_modem_rule_id = 0;
+	memset(pcie_modem_rule_id_in_use, 0, sizeof(pcie_modem_rule_id_in_use));
 }
 
 IPACM_Filtering::~IPACM_Filtering()
@@ -710,7 +711,7 @@
 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;
+	int ret = 0, cnt, pos = 0, i;
 	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)
@@ -807,7 +808,23 @@
 						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;
+					pcie_modem_rule_id_in_use[pcie_modem_rule_id] = true;
+					for(i = 0; i < IPA_PCIE_MODEM_RULE_ID_MAX; i++)
+					{
+						pcie_modem_rule_id = (pcie_modem_rule_id + 1)%IPA_PCIE_MODEM_RULE_ID_MAX;
+						if(!pcie_modem_rule_id_in_use[pcie_modem_rule_id])
+							break;
+					}
+
+					if(i == IPA_PCIE_MODEM_RULE_ID_MAX)
+					{
+						IPACMERR("all handles are in use, max = %d\n", i);
+						return false;
+					}
+					else
+					{
+						IPACMDBG("next free pcie_modem_rule_id: %d\n", pcie_modem_rule_id);
+					}
 				}
 				else
 				{
@@ -892,6 +909,12 @@
 					/* passing rule-id to wan-driver */
 					qmi_del_msg.filter_handle_list[pos].filter_spec_identifier = flt_rule_tbl->hdl[cnt].hdl;
 					pos++;
+
+					/* set in use to false for future rule additions (need to subtract offset and mod max index) */
+					pcie_modem_rule_id_in_use[(IPA_PCIE_MODEM_RULE_ID_MAX + flt_rule_tbl->hdl[cnt].hdl - IPA_PCIE_MODEM_RULE_ID_START)
+						% IPA_PCIE_MODEM_RULE_ID_MAX] = false;
+					IPACMDBG("freeing pcie_modem_rule_id: %d\n", (IPA_PCIE_MODEM_RULE_ID_MAX + flt_rule_tbl->hdl[cnt].hdl -IPA_PCIE_MODEM_RULE_ID_START)
+						% IPA_PCIE_MODEM_RULE_ID_MAX);
 				}
 				else
 				{
@@ -983,17 +1006,18 @@
 	}
 
 	ret = ioctl(fd, IPA_IOC_MDFY_FLT_RULE, ruleTable);
+
+	for (i = 0; i < ruleTable->num_rules; i++)
+	{
+		if (ruleTable->rules[i].status != 0)
+		{
+			IPACMERR("Modifying filter rule %d failed\n", i);
+		}
+	}
+
 	if (ret != 0)
 	{
-		IPACMERR("Failed modifying filtering rule %pK\n", ruleTable);
-
-		for (i = 0; i < ruleTable->num_rules; i++)
-		{
-			if (ruleTable->rules[i].status != 0)
-			{
-				IPACMERR("Modifying filter rule %d failed\n", i);
-			}
-		}
+		IPACMERR("Failed modifying filtering rule IOCTL for %pK\n", ruleTable);
 		return false;
 	}
 
diff --git a/ipacm/src/IPACM_Iface.cpp b/ipacm/src/IPACM_Iface.cpp
index 0d4f54f..55cf71d 100644
--- a/ipacm/src/IPACM_Iface.cpp
+++ b/ipacm/src/IPACM_Iface.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
@@ -955,57 +955,6 @@
 		memcpy(&(m_pFilteringTable->rules[4]), &flt_rule_entry,
 			sizeof(struct ipa_flt_rule_add));
 
-		IPACMDBG_H("Add TCP ctrl rules: total num %d\n", IPV6_DEFAULT_FILTERTING_RULES);
-		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.action = IPA_PASS_TO_EXCEPTION;
-		flt_rule_entry.rule.eq_attrib_type = 1;
-		flt_rule_entry.rule.eq_attrib.rule_eq_bitmap = 0;
-
-		if(rx_prop->rx[0].attrib.attrib_mask & IPA_FLT_META_DATA)
-		{
-			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;
-			flt_rule_entry.rule.eq_attrib.metadata_meq32.mask = rx_prop->rx[0].attrib.meta_data_mask;
-		}
-
-		flt_rule_entry.rule.eq_attrib.rule_eq_bitmap |= (1<<1);
-		flt_rule_entry.rule.eq_attrib.protocol_eq_present = 1;
-		flt_rule_entry.rule.eq_attrib.protocol_eq = IPACM_FIREWALL_IPPROTO_TCP;
-
-		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;
-
-		/* 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(&(m_pFilteringTable->rules[5]), &flt_rule_entry, sizeof(struct ipa_flt_rule_add));
-
-		/* add TCP SYN rule*/
-		flt_rule_entry.rule.eq_attrib.ihl_offset_meq_32[0].value = (((uint32_t)1)<<TCP_SYN_SHIFT);
-		flt_rule_entry.rule.eq_attrib.ihl_offset_meq_32[0].mask = (((uint32_t)1)<<TCP_SYN_SHIFT);
-		memcpy(&(m_pFilteringTable->rules[6]), &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(&(m_pFilteringTable->rules[7]), &flt_rule_entry, sizeof(struct ipa_flt_rule_add));
-
 #endif
 #ifdef IPA_IOCTL_SET_FNR_COUNTER_INFO
 		/* use index hw-counter */
@@ -1060,7 +1009,7 @@
 	return res;
 }
 
-/*  get ipa interface name */
+/*  get ipa interface index from name */
 int IPACM_Iface::ipa_get_if_index
 (
   char * if_name,
diff --git a/ipacm/src/IPACM_IfaceManager.cpp b/ipacm/src/IPACM_IfaceManager.cpp
index 2857f39..1392a1a 100644
--- a/ipacm/src/IPACM_IfaceManager.cpp
+++ b/ipacm/src/IPACM_IfaceManager.cpp
@@ -291,6 +291,9 @@
 				IPACM_EvtDispatcher::registr(IPA_TETHERING_STATS_UPDATE_EVENT, lan);
 #endif
 				IPACM_EvtDispatcher::registr(IPA_CRADLE_WAN_MODE_SWITCH, lan);
+#ifdef IPA_MTU_EVENT_MAX
+				IPACM_EvtDispatcher::registr(IPA_MTU_UPDATE, lan);
+#endif
 				IPACM_EvtDispatcher::registr(IPA_LINK_DOWN_EVENT, lan);
 				/* IPA_LAN_DELETE_SELF should be always last */
 				IPACM_EvtDispatcher::registr(IPA_LAN_DELETE_SELF, lan);
@@ -420,6 +423,9 @@
 				IPACM_EvtDispatcher::registr(IPA_WLAN_FWR_SSR_BEFORE_SHUTDOWN_NOTICE, wl);
 #endif
 				IPACM_EvtDispatcher::registr(IPA_WIGIG_CLIENT_ADD_EVENT, wl);
+#ifdef IPA_MTU_EVENT_MAX
+				IPACM_EvtDispatcher::registr(IPA_MTU_UPDATE, wl);
+#endif
 				/* IPA_LAN_DELETE_SELF should be always last */
 				IPACM_EvtDispatcher::registr(IPA_LAN_DELETE_SELF, wl);
 				IPACMDBG_H("ipa_WLAN (%s):ipa_index (%d) instance open/registr ok\n", wl->dev_name, wl->ipa_if_num);
@@ -450,6 +456,12 @@
 					else
 					{
 						w = new IPACM_Wan(ipa_interface_index, is_sta_mode, NULL);
+						if (w->rx_prop == NULL && w->tx_prop == NULL)
+						{
+							/* close the netdev instance if IPA not support*/
+							w->delete_iface();
+							return IPACM_FAILURE;
+						}
 					}
 					IPACM_EvtDispatcher::registr(IPA_ADDR_ADD_EVENT, w);
 #ifdef FEATURE_IPA_ANDROID
@@ -458,6 +470,9 @@
 					if(is_sta_mode == Q6_WAN)
 					{
 						IPACM_EvtDispatcher::registr(IPA_NETWORK_STATS_UPDATE_EVENT, w);
+#ifdef IPA_MTU_EVENT_MAX
+						IPACM_EvtDispatcher::registr(IPA_MTU_SET, w);
+#endif
 					};
 #else/* defined(FEATURE_IPA_ANDROID) */
 					IPACM_EvtDispatcher::registr(IPA_ROUTE_ADD_EVENT, w);
diff --git a/ipacm/src/IPACM_Lan.cpp b/ipacm/src/IPACM_Lan.cpp
index 0a532fd..c83a729 100644
--- a/ipacm/src/IPACM_Lan.cpp
+++ b/ipacm/src/IPACM_Lan.cpp
@@ -751,25 +751,7 @@
 		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);
@@ -1128,6 +1110,36 @@
 	}
 	break;
 
+#ifdef IPA_MTU_EVENT_MAX
+	case IPA_MTU_UPDATE:
+	{
+		IPACMDBG_H("Received IPA_MTU_UPDATE");
+		ipacm_event_mtu_info *evt_data = (ipacm_event_mtu_info *)param;
+		ipa_mtu_info *data = &(evt_data->mtu_info);
+
+		/* IPA_IP_MAX means both ipv4 and ipv6 */
+		if ((data->ip_type == IPA_IP_v4 || data->ip_type == IPA_IP_MAX) && IPACM_Wan::isWanUP(ipa_if_num))
+		{
+			handle_private_subnet_android(IPA_IP_v4);
+		}
+
+		/* IPA_IP_MAX means both ipv4 and ipv6 */
+		if ((data->ip_type == IPA_IP_v6 || data->ip_type == IPA_IP_MAX) && IPACM_Wan::isWanUP_V6(ipa_if_num))
+		{
+			/* check if the prefix + MTU rules are installed */
+			if (ipv6_prefix_flt_rule_hdl[0] && ipv6_prefix_flt_rule_hdl[1])
+			{
+				modify_ipv6_prefix_flt_rule(IPACM_Wan::backhaul_ipv6_prefix);
+			}
+			else
+			{
+				IPACMERR("Failed to update prefix MTU rules, no prefix rules set");
+			}
+		}
+	}
+	break;
+#endif
+
 	default:
 		break;
 	}
@@ -1659,9 +1671,9 @@
 		/* 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 (ipv6_prefix_flt_rule_hdl[0] && ipv6_prefix_flt_rule_hdl[1])
+			{
+				modify_ipv6_prefix_flt_rule(IPACM_Wan::backhaul_ipv6_prefix);
 			}
 		}
 
@@ -1905,15 +1917,34 @@
 		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) {
+#ifdef FEATURE_IPA_ANDROID
+				if (IPACM_Wan::isXlat())
+				{
+					/* 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 IPACM_FAILURE;
+					}
+				}
+
+				IPACMDBG_H(" support downstream iface %s, cur %d\n", dev_name,
+					IPACM_OffloadManager::num_offload_v4_tethered_iface);
+#endif
 		/* 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 (ipv6_prefix_flt_rule_hdl[0] && ipv6_prefix_flt_rule_hdl[1])
+			{
+				modify_ipv6_prefix_flt_rule(IPACM_Wan::backhaul_ipv6_prefix);
 			}
 		}
 
@@ -4205,9 +4236,14 @@
 			mtu[i] = IPACM_Wan::queryMTU(ipa_if_num, IPA_IP_v4);
 
 			if (mtu[i] > 0)
+			{
 				mtu_rule_cnt++;
+				IPACMDBG_H("MTU[%d] is %d\n", i, mtu[i]);
+			}
 			else
-				IPACMERR("MTU is zero\n");
+			{
+				IPACMDBG_H("MTU is zero\n");
+			}
 		}
 		IPACMDBG_H("total %d MTU rules are needed\n", mtu_rule_cnt);
 
@@ -4237,16 +4273,16 @@
 
 		flt_rule.rule.retain_hdr = 1;
 		flt_rule.rule.to_uc = 0;
-		flt_rule.rule.action = IPA_PASS_TO_ROUTING;
-		flt_rule.rule.eq_attrib_type = 0;
-		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);
 
 		for (i = 0; i < (IPACM_Iface::ipacmcfg->ipa_num_private_subnet); i++)
 		{
 			/* add private subnet rule for ipv4 */
+			/* these 3 properties below will be reset during construct_mtu_rule */
 			flt_rule.rule.action = IPA_PASS_TO_ROUTING;
 			flt_rule.rule.eq_attrib_type = 0;
+			flt_rule.rule.rt_tbl_hdl = IPACM_Iface::ipacmcfg->rt_tbl_default_v4.hdl;
+
 			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;
@@ -4264,7 +4300,9 @@
 				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);
 			}
@@ -4285,8 +4323,13 @@
 	return res;
 }
 
-int IPACM_Lan::install_ipv6_prefix_flt_rule(uint32_t* prefix)
+int IPACM_Lan::modify_ipv6_prefix_flt_rule(uint32_t* prefix)
 {
+	int len, res = IPACM_SUCCESS;
+	struct ipa_flt_rule_mdfy flt_rule_entry;
+	struct ipa_ioc_mdfy_flt_rule* flt_rule;
+	int rule_cnt = 1;
+
 	if(prefix == NULL)
 	{
 		IPACMERR("IPv6 prefix is empty.\n");
@@ -4294,15 +4337,124 @@
 	}
 	IPACMDBG_H("Receive IPv6 prefix: 0x%08x%08x.\n", prefix[0], prefix[1]);
 
+	uint16_t mtu = IPACM_Wan::queryMTU(ipa_if_num, IPA_IP_v6);
+	if (mtu > 0)
+	{
+		IPACMDBG_H("MTU is %d\n", mtu);
+		rule_cnt ++;
+	}
+	else
+	{
+		IPACMERR("MTU is 0");
+	}
+
+
+	if(rx_prop == NULL)
+	{
+		IPACMERR("no rx props\n");
+		return IPACM_FAILURE;
+	}
+
+	len = sizeof(struct ipa_ioc_mdfy_flt_rule) + rule_cnt * sizeof(struct ipa_flt_rule_mdfy);
+	flt_rule = (struct ipa_ioc_mdfy_flt_rule*)malloc(len);
+	if(!flt_rule)
+	{
+		IPACMERR("Failed to allocate ipa_ioc_mdfy_flt_rule memory...\n");
+		return IPACM_FAILURE;
+	}
+	memset(flt_rule, 0, len);
+
+	flt_rule->commit = 1;
+	flt_rule->ip = IPA_IP_v6;
+	flt_rule->num_rules = rule_cnt;
+
+	memset(&flt_rule_entry, 0, sizeof(struct ipa_flt_rule_mdfy));
+	flt_rule_entry.status = -1;
+	flt_rule_entry.rule.retain_hdr = 1;
+	flt_rule_entry.rule.to_uc = 0;
+	flt_rule_entry.rule.action = IPA_PASS_TO_EXCEPTION;
+	flt_rule_entry.rule.eq_attrib_type = 0;
+	flt_rule_entry.rule_hdl = ipv6_prefix_flt_rule_hdl[0];
+
+	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];
+	flt_rule_entry.rule.attrib.u.v6.dst_addr[1] = prefix[1];
+	flt_rule_entry.rule.attrib.u.v6.dst_addr[2] = 0x0;
+	flt_rule_entry.rule.attrib.u.v6.dst_addr[3] = 0x0;
+	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] = 0x0;
+	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_mdfy));
+
+
+	flt_rule_entry.rule_hdl = ipv6_prefix_flt_rule_hdl[1];
+	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_mdfy));
+		}
+	}
+
+
+	if(false == m_filtering.ModifyFilteringRule(flt_rule))
+	{
+		IPACMERR("Failed to modify prefix filtering rules.\n");
+		res = IPACM_FAILURE;
+		goto fail;
+	}
+
+fail:
+	if(flt_rule != NULL)
+	{
+		free(flt_rule);
+	}
+	return res;
+}
+
+int IPACM_Lan::install_ipv6_prefix_flt_rule(uint32_t* prefix)
+{
 	int len;
 	struct ipa_ioc_add_flt_rule* flt_rule;
 	struct ipa_flt_rule_add flt_rule_entry;
 	bool result;
 	int rule_cnt = 1;
 
+	if(prefix == NULL)
+	{
+		IPACMERR("IPv6 prefix is empty.\n");
+		return IPACM_FAILURE;
+	}
+	IPACMDBG_H("Receive IPv6 prefix: 0x%08x%08x.\n", prefix[0], prefix[1]);
+
 	uint16_t mtu = IPACM_Wan::queryMTU(ipa_if_num, IPA_IP_v6);
 	if (mtu > 0)
+	{
+		IPACMDBG_H("MTU is %d\n", mtu);
 		rule_cnt ++;
+	}
+	else
+	{
+		IPACMERR("MTU is 0");
+	}
 
 	if(rx_prop != NULL)
 	{
@@ -4368,18 +4520,7 @@
 			}
 		}
 
-#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)
 		{
@@ -6467,6 +6608,7 @@
 	rule->eq_attrib_type = 1;
 	rule->eq_attrib.rule_eq_bitmap = 0;
 	rule->action = IPA_PASS_TO_EXCEPTION;
+	rule->rt_tbl_hdl = -1;
 
 	/* generate eq */
 	memset(&flt_eq, 0, sizeof(flt_eq));
@@ -6490,7 +6632,11 @@
 		&flt_eq.eq_attrib, sizeof(rule->eq_attrib));
 
 	//add IHL offsets
-	rule->eq_attrib.rule_eq_bitmap |= (1<<10);
+#ifdef FEATURE_IPA_V3
+		rule->eq_attrib.rule_eq_bitmap |= (1<<10);
+#else
+		rule->eq_attrib.rule_eq_bitmap |= (1<<4);
+#endif
 	rule->eq_attrib.num_ihl_offset_range_16 = 1;
 	if (iptype == IPA_IP_v4)
 		rule->eq_attrib.ihl_offset_range_16[0].offset = 0x82;
diff --git a/ipacm/src/IPACM_LanToLan.cpp b/ipacm/src/IPACM_LanToLan.cpp
index ea016ef..0075418 100644
--- a/ipacm/src/IPACM_LanToLan.cpp
+++ b/ipacm/src/IPACM_LanToLan.cpp
@@ -1308,8 +1308,12 @@
 			it_own_peer_info++)
 		{
 			/* decrement reference count of peer l2 header type on both interfaces*/
-			decrement_ref_cnt_peer_l2_hdr_type(it_own_peer_info->peer->get_iface_pointer()->tx_prop->tx[0].hdr_l2_type);
-			it_own_peer_info->peer->decrement_ref_cnt_peer_l2_hdr_type(m_p_iface->tx_prop->tx[0].hdr_l2_type);
+			if (it_own_peer_info->peer &&
+				it_own_peer_info->peer->get_iface_pointer() &&
+				it_own_peer_info->peer->get_iface_pointer()->tx_prop)
+				decrement_ref_cnt_peer_l2_hdr_type(it_own_peer_info->peer->get_iface_pointer()->tx_prop->tx[0].hdr_l2_type);
+			if (it_own_peer_info->peer && m_p_iface && m_p_iface->tx_prop)
+				it_own_peer_info->peer->decrement_ref_cnt_peer_l2_hdr_type(m_p_iface->tx_prop->tx[0].hdr_l2_type);
 
 			/* first clear all flt rule on target interface */
 			IPACMDBG_H("Clear all flt rule on target interface.\n");
@@ -1330,7 +1334,8 @@
 					other_iface->clear_all_rt_rule_for_one_peer_iface(&(*it_other_iface_peer_info));
 					/* remove the peer info from the list */
 					other_iface->m_peer_iface_info.erase(it_other_iface_peer_info);
-					other_iface->del_hdr_proc_ctx(m_p_iface->tx_prop->tx[0].hdr_l2_type);
+					if (m_p_iface && m_p_iface->tx_prop)
+						other_iface->del_hdr_proc_ctx(m_p_iface->tx_prop->tx[0].hdr_l2_type);
 					break;
 				}
 			}
@@ -1338,6 +1343,9 @@
 			/* then clear rt rule and hdr proc ctx and release rt table on target interface */
 			IPACMDBG_H("Clear rt rules and hdr proc ctx and release rt table on target interface.\n");
 			clear_all_rt_rule_for_one_peer_iface(&(*it_own_peer_info));
+			if (it_own_peer_info->peer &&
+				it_own_peer_info->peer->get_iface_pointer() &&
+				it_own_peer_info->peer->get_iface_pointer()->tx_prop)
 			del_hdr_proc_ctx(it_own_peer_info->peer->get_iface_pointer()->tx_prop->tx[0].hdr_l2_type);
 		}
 		m_peer_iface_info.clear();
diff --git a/ipacm/src/IPACM_Main.cpp b/ipacm/src/IPACM_Main.cpp
index 696f518..6bd117e 100644
--- a/ipacm/src/IPACM_Main.cpp
+++ b/ipacm/src/IPACM_Main.cpp
@@ -121,7 +121,7 @@
 
 #ifdef FEATURE_IPACM_HAL
 	IPACM_OffloadManager* OffloadMng;
-	HAL *hal;
+	::android::sp<HAL> hal;
 #endif
 
 /* start netlink socket monitor*/
@@ -270,6 +270,10 @@
 #endif
 	ipacm_cmd_q_data new_neigh_evt;
 	ipacm_event_data_all* new_neigh_data;
+#ifdef IPA_MTU_EVENT_MAX
+	ipacm_event_mtu_info *mtu_event = NULL;
+	ipa_mtu_info *mtu_info;
+#endif
 
 	param = NULL;
 	fd = open(IPA_DRIVER, O_RDWR);
@@ -760,10 +764,19 @@
 				IPACMERR("calling OffloadMng->elrInstance->onOffloadStopped \n");
 				OffloadMng->elrInstance->onOffloadStopped(IpaEventRelay::ERROR);
 			}
-			/* WA to clean up wlan instances during SSR */
-			evt_data.event = IPA_SSR_NOTICE;
-			evt_data.evt_data = NULL;
-			break;
+			/* Starting from Hastings, WLAN is not restarted as part of Modem SSR.
+			 * No need to reset NAT Iface.
+			 */
+#ifdef IPA_HW_v4_9
+                        if (IPACM_Iface::ipacmcfg->GetIPAVer() != IPA_HW_v4_9)
+#endif
+			{
+                                /* WA to clean up wlan instances during SSR */
+                                evt_data.event = IPA_SSR_NOTICE;
+                                evt_data.evt_data = NULL;
+                                break;
+                        }
+                        continue;
 		case IPA_SSR_AFTER_POWERUP:
 			IPACMDBG_H("Received IPA_SSR_AFTER_POWERUP\n");
 			OffloadMng = IPACM_OffloadManager::GetInstance();
@@ -865,6 +878,32 @@
 			break;
 #endif
 
+#ifdef IPA_MTU_EVENT_MAX
+		case IPA_SET_MTU:
+			mtu_event = (ipacm_event_mtu_info *)malloc(sizeof(*mtu_event));
+			if(mtu_event == NULL)
+			{
+				IPACMERR("Failed to allocate memory.\n");
+				return NULL;
+			}
+			mtu_info = &(mtu_event->mtu_info);
+			memcpy(mtu_info, buffer + sizeof(struct ipa_msg_meta), sizeof(struct ipa_mtu_info));
+			IPACMDBG_H("Received IPA_SET_MTU if_name %s ip_type %d mtu_v4 %d mtu_v6 %d\n",
+				mtu_info->if_name, mtu_info->ip_type, mtu_info->mtu_v4, mtu_info->mtu_v6);
+			if (mtu_info->ip_type > IPA_IP_MAX)
+			{
+				IPACMERR("ip_type (%d) beyond the Max range (%d), abort\n",
+				mtu_info->ip_type, IPA_IP_MAX);
+				return NULL;
+			}
+
+			ipa_get_if_index(mtu_info->if_name, &(mtu_event->if_index));
+
+			evt_data.event = IPA_MTU_SET;
+			evt_data.evt_data = mtu_event;
+			break;
+#endif
+
 		default:
 			IPACMDBG_H("Unhandled message type: %d\n", event_hdr.msg_type);
 			continue;
diff --git a/ipacm/src/IPACM_OffloadManager.cpp b/ipacm/src/IPACM_OffloadManager.cpp
index 7919413..708725b 100644
--- a/ipacm/src/IPACM_OffloadManager.cpp
+++ b/ipacm/src/IPACM_OffloadManager.cpp
@@ -120,8 +120,9 @@
 
 RET IPACM_OffloadManager::provideFd(int fd, unsigned int groups)
 {
+	struct timeval tv;
 	IPACM_ConntrackClient *cc;
-	int on = 1, rel;
+	int on = 1, rel = 0;
 	struct sockaddr_nl	local;
 	unsigned int addr_len;
 
@@ -155,13 +156,39 @@
 			IPACMERR( "setsockopt returned error code %d ( %s )", errno, strerror( errno ) );
 		}
 	} else if (groups == cc->subscrips_udp) {
+		/* Set receive timeout to 1s on the FD which is used to read conntrack dump. */
+		memset(&tv,0, sizeof(struct timeval));
+		tv.tv_sec = 1; /* 1s timeout */
+		rel = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO,(struct timeval *)&tv,sizeof(struct timeval));
+		if (rel == -1)
+		{
+			IPACMERR("setsockopt returned error code %d ( %s )\n", errno, strerror(errno));
+		}
+
+		/* In Android we get conntrack handles once after tethering is enabled but we
+		   loose connections info for embedded traffic if running before. So no NAT
+		   entries are added for embedded traffic due to which we see NAT exception and
+		   data takes S/W path which results in less throughput. Hence for embedded
+		   traffic info, framework sends conntrack dump before providing handles. Here
+		   reading ct entries before creating filter on Fd in order to have NAT entries
+		   for both TCP/UDP embedded traffic.
+		*/
+		CtList->readConntrack(fd);
+		/* Reset receive timeout on the FD which is used to read conntrack dump. */
+		memset(&tv,0, sizeof(struct timeval));
+		rel = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO,(struct timeval *)&tv,sizeof(struct timeval));
+		if (rel == -1)
+		{
+			IPACMERR("setsockopt returned error code %d ( %s )\n", errno, strerror(errno));
+		}
+
 		cc->fd_udp = dup(fd);
 		IPACMDBG_H("Received fd %d with groups %d.\n", fd, groups);
 		/* set netlink buf */
-		rel = setsockopt(cc->fd_tcp, SOL_NETLINK, NETLINK_NO_ENOBUFS, &on, sizeof(int) );
+		rel = setsockopt(cc->fd_udp, SOL_NETLINK, NETLINK_NO_ENOBUFS, &on, sizeof(int) );
 		if (rel == -1)
 		{
-			IPACMERR( "setsockopt returned error code %d ( %s )", errno, strerror( errno ) );
+			IPACMERR("setsockopt returned error code %d ( %s )\n", errno, strerror(errno));
 		}
 	} else {
 		IPACMERR("Received unexpected fd with groups %d.\n", groups);
diff --git a/ipacm/src/IPACM_Wan.cpp b/ipacm/src/IPACM_Wan.cpp
index 847e08d..f17eb03 100644
--- a/ipacm/src/IPACM_Wan.cpp
+++ b/ipacm/src/IPACM_Wan.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
@@ -92,7 +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;
+uint16_t IPACM_Wan::mtu_default_wan_v4 = DEFAULT_MTU_SIZE;
+uint16_t IPACM_Wan::mtu_default_wan_v6 = DEFAULT_MTU_SIZE;
 
 IPACM_Wan::IPACM_Wan(int iface_index,
 	ipacm_wan_iface_type is_sta_mode,
@@ -132,7 +133,11 @@
 	ext_prop = NULL;
 	is_ipv6_frag_firewall_flt_rule_installed = false;
 	ipv6_frag_firewall_flt_rule_hdl = 0;
-	mtu_size = DEFAULT_MTU_SIZE;
+
+	mtu_v4 = DEFAULT_MTU_SIZE;
+	mtu_v4_set = false;
+	mtu_v6 = DEFAULT_MTU_SIZE;
+	mtu_v6_set = false;
 
 	num_wan_client = 0;
 	header_name_count = 0;
@@ -146,6 +151,53 @@
 	wan_client_len = 0;
 	m_is_sta_mode = is_sta_mode;
 
+#ifdef IPA_MTU_EVENT_MAX
+	/* Query WAN MTU to handle IPACM restart scenarios. */
+	if(is_sta_mode == Q6_WAN)
+	{
+		int fd_wwan_ioctl;
+		ipa_mtu_info *mtu_info = (ipa_mtu_info *)malloc(sizeof(ipa_mtu_info));
+		if (mtu_info)
+		{
+			memset(mtu_info, 0, sizeof(ipa_mtu_info));
+			memcpy(mtu_info->if_name, dev_name, IPA_IFACE_NAME_LEN);
+			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);
+			}
+			else
+			{
+				IPACMDBG_H("send WAN_IOC_GET_WAN_MTU for %s\n", mtu_info->if_name);
+				if(ioctl(fd_wwan_ioctl, WAN_IOC_GET_WAN_MTU, mtu_info))
+				{
+					IPACMERR("Failed to send WAN_IOC_GET_WAN_MTU\n ");
+				}
+				else
+				{
+					/* Updated MTU values.*/
+					if (mtu_info->mtu_v4)
+					{
+						mtu_v4 = mtu_info->mtu_v4;
+						mtu_v4_set = true;
+						IPACMDBG_H("Updated v4 mtu=[%d] for (%s)\n",
+							mtu_v4, mtu_info->if_name);
+					}
+					if (mtu_info->mtu_v6)
+					{
+						mtu_v6 = mtu_info->mtu_v6;
+						mtu_v6_set = true;
+						IPACMDBG_H("Updated v6 mtu=[%d] for (%s)\n",
+							mtu_v6, mtu_info->if_name);
+					}
+				}
+				close(fd_wwan_ioctl);
+			}
+			free(mtu_info);
+		}
+	}
+#endif
+
 	if(iface_query != NULL)
 	{
 		IPACMDBG_H("index:%d constructor: Tx properties:%d\n", iface_index, iface_query->num_tx_props);
@@ -810,7 +862,7 @@
 				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 */
+			/* 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));
@@ -1112,6 +1164,7 @@
 			}
 		}
 		break;
+
 	case IPA_LINK_DOWN_EVENT:
 		{
 			ipacm_event_data_fid *data = (ipacm_event_data_fid *)param;
@@ -1575,6 +1628,8 @@
 		{
 			ipacm_event_data_all *data = (ipacm_event_data_all *)param;
 			ipa_interface_index = iface_ipa_index_query(data->if_index);
+			int index = 0;
+			bool renew = false;
 
 			if (ipa_interface_index == ipa_if_num)
 			{
@@ -1606,6 +1661,7 @@
 				}
 
 				IPACMDBG_H("wan-iface got client \n");
+
 				/* first construc WAN-client full header */
 				if(memcmp(data->mac_addr,
 						invalid_mac,
@@ -1617,7 +1673,20 @@
 					return;
 				}
 
-				handle_wan_hdr_init(data->mac_addr);
+				/* check if same as GW_ip need replacing */
+				if( handle_gw_mac_renew(data, index) == IPACM_SUCCESS)
+				{
+					renew = true;
+					IPACMDBG_H("Renew is happening with client-index (%d)\n", index);
+					/* clinet renew procedure */
+					handle_wan_hdr_init(data->mac_addr, true, index);
+				}
+				else
+				{
+					IPACMDBG_H("Renew is no need!\n");
+					handle_wan_hdr_init(data->mac_addr);
+				}
+
 				IPACMDBG_H("construct wan-client header and route rules \n");
 				/* Associate with IP and construct RT-rule */
 				if (handle_wan_client_ipaddr(data) == IPACM_FAILURE)
@@ -1626,7 +1695,7 @@
 				}
 				handle_wan_client_route_rule(data->mac_addr, data->iptype);
 				/* Check & construct STA header */
-				handle_sta_header_add_evt();
+				handle_sta_header_add_evt(renew);
 				return;
 			}
 		}
@@ -1721,54 +1790,117 @@
 		}
 		break;
 
-		case IPA_WLAN_SWITCH_TO_SCC:
-			if(IPACM_Wan::backhaul_mode == WLAN_WAN)
+	case IPA_WLAN_SWITCH_TO_SCC:
+		if(IPACM_Wan::backhaul_mode == WLAN_WAN)
+		{
+			IPACMDBG_H("Received IPA_WLAN_SWITCH_TO_SCC\n");
+			if(ip_type == IPA_IP_MAX)
 			{
-				IPACMDBG_H("Received IPA_WLAN_SWITCH_TO_SCC\n");
-				if(ip_type == IPA_IP_MAX)
-				{
-					handle_wlan_SCC_MCC_switch(true, IPA_IP_v4);
-					handle_wlan_SCC_MCC_switch(true, IPA_IP_v6);
-					handle_wan_client_SCC_MCC_switch(true, IPA_IP_v4);
-					handle_wan_client_SCC_MCC_switch(true, IPA_IP_v6);
-				}
-				else
-				{
-					handle_wlan_SCC_MCC_switch(true, ip_type);
-					handle_wan_client_SCC_MCC_switch(true, ip_type);
-				}
+				handle_wlan_SCC_MCC_switch(true, IPA_IP_v4);
+				handle_wlan_SCC_MCC_switch(true, IPA_IP_v6);
+				handle_wan_client_SCC_MCC_switch(true, IPA_IP_v4);
+				handle_wan_client_SCC_MCC_switch(true, IPA_IP_v6);
 			}
-			break;
-
-		case IPA_WLAN_SWITCH_TO_MCC:
-			if(IPACM_Wan::backhaul_mode == WLAN_WAN)
+			else
 			{
-				IPACMDBG_H("Received IPA_WLAN_SWITCH_TO_MCC\n");
-				if(ip_type == IPA_IP_MAX)
-				{
-					handle_wlan_SCC_MCC_switch(false, IPA_IP_v4);
-					handle_wlan_SCC_MCC_switch(false, IPA_IP_v6);
-					handle_wan_client_SCC_MCC_switch(false, IPA_IP_v4);
-					handle_wan_client_SCC_MCC_switch(false, IPA_IP_v6);
-				}
-				else
-				{
-					handle_wlan_SCC_MCC_switch(false, ip_type);
-					handle_wan_client_SCC_MCC_switch(false, ip_type);
-				}
+				handle_wlan_SCC_MCC_switch(true, ip_type);
+				handle_wan_client_SCC_MCC_switch(true, ip_type);
 			}
-			break;
-#ifdef FEATURE_IPACM_HAL
-		/* WA for WLAN to clean up NAT instance during SSR */
-		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)
-			{
-				IPACM_Iface::ipacmcfg->DelNatIfaces(dev_name); // delete NAT-iface
-			}
+		}
 		break;
+
+	case IPA_WLAN_SWITCH_TO_MCC:
+		if(IPACM_Wan::backhaul_mode == WLAN_WAN)
+		{
+			IPACMDBG_H("Received IPA_WLAN_SWITCH_TO_MCC\n");
+			if(ip_type == IPA_IP_MAX)
+			{
+				handle_wlan_SCC_MCC_switch(false, IPA_IP_v4);
+				handle_wlan_SCC_MCC_switch(false, IPA_IP_v6);
+				handle_wan_client_SCC_MCC_switch(false, IPA_IP_v4);
+				handle_wan_client_SCC_MCC_switch(false, IPA_IP_v6);
+			}
+			else
+			{
+				handle_wlan_SCC_MCC_switch(false, ip_type);
+				handle_wan_client_SCC_MCC_switch(false, ip_type);
+			}
+		}
+		break;
+#ifdef FEATURE_IPACM_HAL
+	/* WA for WLAN to clean up NAT instance during SSR */
+	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)
+		{
+			IPACM_Iface::ipacmcfg->DelNatIfaces(dev_name); // delete NAT-iface
+		}
+	}
+	break;
 #endif
+#ifdef IPA_MTU_EVENT_MAX
+	case IPA_MTU_SET:
+	{
+		ipacm_event_mtu_info *data = (ipacm_event_mtu_info *)param;
+		ipa_mtu_info *mtu_info = &(data->mtu_info);
+		ipa_interface_index = iface_ipa_index_query(data->if_index);
+
+		if (ipa_interface_index == ipa_if_num)
+		{
+			IPACMDBG_H("Received IPA_MTU_SET (Android) for interface (%d)\n",
+				ipa_interface_index);
+			if (mtu_info->ip_type == IPA_IP_v4 || mtu_info->ip_type == IPA_IP_MAX)
+			{
+				/* Update v4_mtu. */
+				mtu_v4 = mtu_info->mtu_v4;
+				mtu_v4_set = true;
+
+				if (active_v4)
+				{
+					/* upstream interface. update default MTU. */
+					mtu_default_wan_v4 = mtu_v4;
+				}
+				IPACMDBG_H("Updated v4 mtu=[%d] for (%s), upstream_mtu=[%d]\n",
+					mtu_v4, mtu_info->if_name, mtu_default_wan_v4);
+			}
+			if (mtu_info->ip_type == IPA_IP_v6 || mtu_info->ip_type == IPA_IP_MAX)
+			{
+				/* Update v4_mtu. */
+				mtu_v6 = mtu_info->mtu_v6;
+				mtu_v6_set = true;
+				if (active_v6)
+				{
+					/* upstream interface. update default MTU. */
+					mtu_default_wan_v6 = mtu_v6;
+				}
+				IPACMDBG_H("Updated v6 mtu=[%d] for (%s), upstream_mtu=[%d]\n",
+					mtu_v6, mtu_info->if_name, mtu_default_wan_v6);
+			}
+
+			if (active_v4 || active_v6)
+			{
+				ipacm_event_mtu_info *mtu_event;
+				ipacm_cmd_q_data evt_data;
+				mtu_event = (ipacm_event_mtu_info *)malloc(sizeof(*mtu_event));
+				if(mtu_event == NULL)
+				{
+					IPACMERR("Failed to allocate memory.\n");
+					return;
+				}
+				memcpy(&mtu_event->mtu_info, mtu_info, sizeof(ipa_mtu_info));
+				evt_data.event = IPA_MTU_UPDATE;
+				evt_data.evt_data = mtu_event;
+				/* finish command queue */
+				IPACMDBG_H("Posting IPA_MTU_UPDATE event\n");
+				IPACM_EvtDispatcher::PostEvt(&evt_data);
+			}
+		}
+	}
+	break;
+#endif
+
 	default:
 		break;
 	}
@@ -1777,9 +1909,8 @@
 }
 
 /* wan default route/filter rule configuration */
-int IPACM_Wan::handle_route_add_evt(ipa_ip_type iptype)
+int IPACM_Wan::handle_route_add_evt(ipa_ip_type iptype, bool add_only)
 {
-
 	/* add default WAN route */
 	struct ipa_ioc_add_rt_rule *rt_rule = NULL;
 	struct ipa_rt_rule_add *rt_rule_entry;
@@ -1817,8 +1948,14 @@
 	}
 	IPACMDBG_H("backhaul_is_wan_bridge ?: %d \n", IPACM_Wan::backhaul_is_wan_bridge);
 
-	/* query MTU size of the interface */
-	query_mtu_size();
+	/* query MTU size of the interface if MTU is not set via ioctl. */
+	if (!mtu_v4_set && !mtu_v6_set)
+	{
+		if(query_mtu_size())
+		{
+			IPACMERR("Failed to query mtu");
+		}
+	}
 
 	if (m_is_sta_mode ==Q6_WAN)
 	{
@@ -2104,10 +2241,16 @@
 				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);
+	/* support delete only, not post wan_down event */
+	if (add_only)
+	{
+		IPACMDBG_H(" Only add default WAN routing rules (%d)\n", add_only);
+		if(rt_rule != NULL)
+		{
+			free(rt_rule);
+		}
+		return IPACM_SUCCESS;
+	}
 
 	ipacm_event_iface_up *wanup_data;
 	wanup_data = (ipacm_event_iface_up *)malloc(sizeof(ipacm_event_iface_up));
@@ -2122,6 +2265,10 @@
 	/* handling filter rule construction */
 	if (iptype == IPA_IP_v4)
 	{
+		/* set mtu_default_wan to current default wan instance */
+		mtu_default_wan_v4 = mtu_v4;
+		IPACMDBG_H("replace the mtu_wan to %d\n", mtu_default_wan_v4);
+
 		IPACM_Wan::wan_up = true;
 		active_v4 = true;
 		memcpy(IPACM_Wan::wan_up_dev_name,
@@ -2181,6 +2328,10 @@
 	}
 	else
 	{
+		/* set mtu_default_wan to current default wan instance */
+		mtu_default_wan_v6 = mtu_v6;
+		IPACMDBG_H("replace the mtu_wan to %d\n", mtu_default_wan_v6);
+
 		memcpy(backhaul_ipv6_prefix, ipv6_prefix, sizeof(backhaul_ipv6_prefix));
 		IPACMDBG_H("Setup backhaul ipv6 prefix to be 0x%08x%08x.\n", backhaul_ipv6_prefix[0], backhaul_ipv6_prefix[1]);
 
@@ -2236,11 +2387,28 @@
 				}
 				IPACMDBG_H("send WAN_IOC_NOTIFY_WAN_STATE up to IPA_PM\n");
 				wan_state.up = true;
+#ifdef WAN_IOCTL_NOTIFY_WAN_INTF_NAME
+				strlcpy(wan_state.upstreamIface, dev_name, IFNAMSIZ);
+#endif
 				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);
+
+				/* Store the Offload state. */
+				FILE *fp = NULL;
+				fp = fopen(IPA_OFFLOAD_TETHER_STATE_FILE_NAME, "w");
+				if (fp == NULL)
+				{
+					IPACMERR("Failed to write offload state to %s, error is %d - %s\n",
+						IPA_OFFLOAD_TETHER_STATE_FILE_NAME, errno, strerror(errno));
+				}
+				else
+				{
+					fprintf(fp, "UPSTREAM=%s,STATE=UP", dev_name);
+					fclose(fp);
+				}
 			}
 			ipa_pm_q6_check++;
 			IPACMDBG_H("update ipa_pm_q6_check to %d\n", ipa_pm_q6_check);
@@ -2368,7 +2536,7 @@
 #endif
 
 /* construct complete ethernet header */
-int IPACM_Wan::handle_sta_header_add_evt()
+int IPACM_Wan::handle_sta_header_add_evt(bool renew)
 {
 	int res = IPACM_SUCCESS, index = IPACM_INVALID_INDEX;
 	if((header_set_v4 == true) || (header_set_v6 == true))
@@ -2425,7 +2593,13 @@
 		}
 	}
 
-	/* checking if the ipv4 same as default route */
+	/* see if v4 default routes are setup before constructing full header */
+	if(header_partial_default_wan_v4 == true)
+	{
+	   handle_route_add_evt(IPA_IP_v4, renew);
+	}
+
+	/* checking if the ipv6 same as default route */
 	if(wan_v6_addr_gw_set)
 	{
 		index = get_wan_client_index_ipv6(wan_v6_addr_gw);
@@ -2473,15 +2647,11 @@
 		}
 	}
 
-	/* see if default routes are setup before constructing full header */
-	if(header_partial_default_wan_v4 == true)
-	{
-	   handle_route_add_evt(IPA_IP_v4);
-	}
+	/* see if v6 default routes are setup before constructing full header */
 
 	if(header_partial_default_wan_v6 == true)
 	{
-	   handle_route_add_evt(IPA_IP_v6);
+	   handle_route_add_evt(IPA_IP_v6, renew);
 	}
 	return res;
 }
@@ -4257,9 +4427,6 @@
 	}
 	else if(iptype == IPA_IP_v6)
 	{
-#ifdef FEATURE_IPA_ANDROID
-		add_tcpv6_filtering_rule(flt_rule_v6, IPACM_Wan::num_v6_flt_rule);
-#endif
 		IPACM_Wan::num_v6_flt_rule = IPA_V2_NUM_DEFAULT_WAN_FILTER_RULE_IPV6;
 		if(IPACM_FAILURE == add_icmp_alg_rules(flt_rule_v6, IPACM_Wan::num_v6_flt_rule, IPA_IP_v6))
 		{
@@ -4544,103 +4711,6 @@
 	return res;
 }
 
-int IPACM_Wan::add_tcpv6_filtering_rule(struct ipa_flt_rule_add *rules, int rule_offset)
-{
-	struct ipa_ioc_get_rt_tbl_indx rt_tbl_idx;
-	struct ipa_flt_rule_add flt_rule_entry;
-	struct ipa_ioc_generate_flt_eq flt_eq;
-	int res = IPACM_SUCCESS;
-
-	if(rules == NULL)
-	{
-		IPACMERR("No filtering table available.\n");
-		return IPACM_FAILURE;
-	}
-	if(rx_prop == NULL)
-	{
-		IPACMERR("No tx property.\n");
-		return IPACM_FAILURE;
-	}
-
-		memset(&rt_tbl_idx, 0, sizeof(rt_tbl_idx));
-		strlcpy(rt_tbl_idx.name, IPACM_Iface::ipacmcfg->rt_tbl_wan_dl.name, IPA_RESOURCE_NAME_MAX);
-		rt_tbl_idx.name[IPA_RESOURCE_NAME_MAX-1] = '\0';
-		rt_tbl_idx.ip = IPA_IP_v6;
-		if(0 != ioctl(m_fd_ipa, IPA_IOC_QUERY_RT_TBL_INDEX, &rt_tbl_idx))
-		{
-			IPACMERR("Failed to get routing table index from name\n");
-			res = IPACM_FAILURE;
-			goto fail;
-		}
-
-		IPACMDBG_H("Routing table %s has index %d\n", rt_tbl_idx.name, rt_tbl_idx.idx);
-		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;
-		flt_rule_entry.rule.rt_tbl_idx = rt_tbl_idx.idx;
-
-#ifdef FEATURE_IPA_ANDROID
-		IPACMDBG_H("Add TCP ctrl rules: total num %d\n", IPA_V2_NUM_TCP_WAN_FILTER_RULE_IPV6);
-#endif
-		memcpy(&flt_rule_entry.rule.attrib,
-					 &rx_prop->rx[0].attrib,
-					 sizeof(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;
-		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));
-
-		flt_rule_entry.rule.eq_attrib.rule_eq_bitmap |= (1<<1);
-		flt_rule_entry.rule.eq_attrib.protocol_eq_present = 1;
-		flt_rule_entry.rule.eq_attrib.protocol_eq = IPACM_FIREWALL_IPPROTO_TCP;
-
-		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;
-
-		/* 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(&(rules[rule_offset]), &flt_rule_entry, sizeof(struct ipa_flt_rule_add));
-
-		/* add TCP SYN rule*/
-		flt_rule_entry.rule.eq_attrib.ihl_offset_meq_32[0].value = (((uint32_t)1)<<TCP_SYN_SHIFT);
-		flt_rule_entry.rule.eq_attrib.ihl_offset_meq_32[0].mask = (((uint32_t)1)<<TCP_SYN_SHIFT);
-		memcpy(&(rules[rule_offset + 1]), &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(&(rules[rule_offset + 2]), &flt_rule_entry, sizeof(struct ipa_flt_rule_add));
-
-#ifdef FEATURE_IPA_ANDROID
-		IPACM_Wan::num_v6_flt_rule += IPA_V2_NUM_TCP_WAN_FILTER_RULE_IPV6;
-		IPACMDBG_H("Constructed %d ICMP filtering rules for ip type %d\n", IPA_V2_NUM_TCP_WAN_FILTER_RULE_IPV6, IPA_IP_v6);
-#endif
-
-fail:
-	return res;
-}
-
 int IPACM_Wan::del_wan_firewall_rule(ipa_ip_type iptype)
 {
 	if(iptype == IPA_IP_v4)
@@ -4769,7 +4839,7 @@
 }
 
 /* for STA mode: wan default route/filter rule delete */
-int IPACM_Wan::handle_route_del_evt(ipa_ip_type iptype)
+int IPACM_Wan::handle_route_del_evt(ipa_ip_type iptype, bool delete_only)
 {
 	uint32_t tx_index;
 	ipacm_cmd_q_data evt_data;
@@ -4778,6 +4848,7 @@
 	int fd_wwan_ioctl;
 	memset(&wan_state, 0, sizeof(wan_state));
 #endif
+	int ret = IPACM_SUCCESS;
 
 	IPACMDBG_H("got handle_route_del_evt for STA-mode with ip-family:%d \n", iptype);
 
@@ -4793,39 +4864,42 @@
 	if (((iptype == IPA_IP_v4) && (active_v4 == true)) ||
 			((iptype == IPA_IP_v6) && (active_v6 == true)))
 	{
-		if(IPACM_Iface::ipacmcfg->GetIPAVer() >= IPA_HW_None && IPACM_Iface::ipacmcfg->GetIPAVer() < IPA_HW_v4_0)
+		if (!delete_only)
 		{
-			/* Delete corresponding ipa_rm_resource_name of TX-endpoint after delete IPV4/V6 RT-rule */
-			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]);
-		}
-		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)
+			if(IPACM_Iface::ipacmcfg->GetIPAVer() >= IPA_HW_None && IPACM_Iface::ipacmcfg->GetIPAVer() < IPA_HW_v4_0)
 			{
-				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);
+				/* Delete corresponding ipa_rm_resource_name of TX-endpoint after delete IPV4/V6 RT-rule */
+				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]);
 			}
-			if (ipa_pm_q6_check > 0)
-				ipa_pm_q6_check--;
 			else
-				IPACMERR(" ipa_pm_q6_check becomes negative !!!\n");
+			{
+				/* 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
-		}
+			}
+		} // end of delete_only
 
 		for (tx_index = 0; tx_index < iface_query->num_tx_props; tx_index++)
 		{
@@ -4868,6 +4942,14 @@
 				return IPACM_FAILURE;
 			}
 		}
+
+		/* support delete only, not post wan_down event */
+		if(delete_only)
+		{
+			IPACMDBG_H(" Only delete default WAN routing rules (%d)\n", delete_only);
+			return IPACM_SUCCESS;
+		}
+
 		ipacm_event_iface_up *wandown_data;
 		wandown_data = (ipacm_event_iface_up *)malloc(sizeof(ipacm_event_iface_up));
 		if (wandown_data == NULL)
@@ -4907,24 +4989,19 @@
 			if(delete_offload_frag_rule())
 			{
 				IPACMERR("Failed to delete DL frag rule \n");
-				return IPACM_FAILURE;
+				ret = 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;
+				ret = IPACM_FAILURE;
 			}
+			return ret;
 		}
 		else
 		{
-
 			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;
@@ -4943,6 +5020,13 @@
 			{
 				memset(IPACM_Wan::wan_up_dev_name, 0, sizeof(IPACM_Wan::wan_up_dev_name));
 			}
+			/* Delete MHI icmpv6 exception rule */
+			if(delete_icmpv6_exception_rule())
+			{
+				IPACMERR("Failed to delete icmpv6 rule \n");
+				return IPACM_FAILURE;
+			}
+
 		}
 	}
 	else
@@ -4995,11 +5079,28 @@
 					return false;
 				}
 				IPACMDBG_H("send WAN_IOC_NOTIFY_WAN_STATE down to IPA_PM\n");
+#ifdef WAN_IOCTL_NOTIFY_WAN_INTF_NAME
+                                strlcpy(wan_state.upstreamIface, dev_name, IFNAMSIZ);
+#endif
 				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);
+
+				/* Store the Offload state. */
+				FILE *fp = NULL;
+				fp = fopen(IPA_OFFLOAD_TETHER_STATE_FILE_NAME, "w");
+				if (fp == NULL)
+				{
+					IPACMERR("Failed to write offload state to %s, error is %d - %s\n",
+						IPA_OFFLOAD_TETHER_STATE_FILE_NAME, errno, strerror(errno));
+				}
+				else
+				{
+					fprintf(fp, "UPSTREAM=%s,STATE=DOWN", dev_name);
+					fclose(fp);
+				}
 			}
 			if (ipa_pm_q6_check > 0)
 				ipa_pm_q6_check--;
@@ -5243,17 +5344,6 @@
 		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 */
@@ -5285,18 +5375,6 @@
 		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
@@ -5318,37 +5396,51 @@
 #endif
 	}
 
-	if(m_is_sta_mode != Q6_MHI_WAN)
+	/* Delete default v4 RT rule */
+	if (ip_type != IPA_IP_v6 && wan_v4_addr_set)
 	{
-		/* Delete default v4 RT rule */
-		if (ip_type != IPA_IP_v6)
+		/* 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)
 		{
-			/* 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;
+			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)
 			{
 				IPACMERR("Routing rule deletion failed!\n");
 				res = IPACM_FAILURE;
 				goto fail;
 			}
 		}
+		IPACMDBG_H("finished delete default v6 RT rules\n ");
+	}
 
-		/* delete default v6 RT rule */
-		if (ip_type != IPA_IP_v4)
+	/* check software routing fl rule hdl */
+	if (softwarerouting_act == true)
+	{
+		if(m_is_sta_mode == Q6_MHI_WAN)
 		{
-			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)
-				{
-					IPACMERR("Routing rule deletion failed!\n");
-					res = IPACM_FAILURE;
-					goto fail;
-				}
-			}
-			IPACMDBG_H("finished delete default v6 RT rules\n ");
+			handle_software_routing_disable(true);
 		}
+		else
+		{
+			handle_software_routing_disable(false);
+		}
+	}
+
+	if(m_is_sta_mode != Q6_MHI_WAN)
+	{
 		/* 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++)
@@ -5397,18 +5489,6 @@
 		/* 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)
-			{
-				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)
 		{
@@ -5544,8 +5624,13 @@
 		goto fail;
 	}
 
+#ifndef IPA_MTU_EVENT_MAX
 	/* reset the mtu size */
-	mtu_size = DEFAULT_MTU_SIZE;
+	mtu_v4 = DEFAULT_MTU_SIZE;
+	mtu_v4_set = false;
+	mtu_v6 = DEFAULT_MTU_SIZE;
+	mtu_v6_set = false;
+#endif
 
 	if(ip_type == IPA_IP_v4)
 	{
@@ -6110,7 +6195,7 @@
 
 /* handle STA WAN-client */
 /* handle WAN client initial, construct full headers (tx property) */
-int IPACM_Wan::handle_wan_hdr_init(uint8_t *mac_addr)
+int IPACM_Wan::handle_wan_hdr_init(uint8_t *mac_addr, bool replaced, int entry)
 {
 
 #define WAN_IFACE_INDEX_LEN 2
@@ -6122,38 +6207,41 @@
 	uint32_t cnt;
 	int clnt_indx;
 
-	clnt_indx = get_wan_client_index(mac_addr);
-
-	if (clnt_indx != IPACM_INVALID_INDEX)
-	{
-		IPACMERR("eth client is found/attached already with index %d \n", clnt_indx);
-		return IPACM_FAILURE;
-	}
-
-	/* add header to IPA */
-	if (num_wan_client >= IPA_MAX_NUM_WAN_CLIENTS)
-	{
-		IPACMERR("Reached maximum number(%d) of eth clients\n", IPA_MAX_NUM_WAN_CLIENTS);
-		return IPACM_FAILURE;
-	}
-
 	IPACMDBG_H("WAN client number: %d\n", num_wan_client);
 
-	memcpy(get_client_memptr(wan_client, num_wan_client)->mac,
+	if(!replaced)
+	{
+		clnt_indx = get_wan_client_index(mac_addr);
+
+		if (clnt_indx != IPACM_INVALID_INDEX)
+		{
+			IPACMERR("eth client is found/attached already with index %d \n", clnt_indx);
+			return IPACM_FAILURE;
+		}
+
+		/* add header to IPA */
+		if (num_wan_client >= IPA_MAX_NUM_WAN_CLIENTS)
+		{
+			IPACMERR("Reached maximum number(%d) of eth clients\n", IPA_MAX_NUM_WAN_CLIENTS);
+			return IPACM_FAILURE;
+		}
+
+		memcpy(get_client_memptr(wan_client, num_wan_client)->mac,
 				 mac_addr,
 				 sizeof(get_client_memptr(wan_client, num_wan_client)->mac));
 
-	IPACMDBG_H("Received Client MAC %02x:%02x:%02x:%02x:%02x:%02x\n",
+		IPACMDBG_H("Received Client MAC %02x:%02x:%02x:%02x:%02x:%02x\n",
 					 mac_addr[0], mac_addr[1], mac_addr[2],
 					 mac_addr[3], mac_addr[4], mac_addr[5]);
 
-	IPACMDBG_H("stored MAC %02x:%02x:%02x:%02x:%02x:%02x\n",
+		IPACMDBG_H("stored MAC %02x:%02x:%02x:%02x:%02x:%02x\n",
 					 get_client_memptr(wan_client, num_wan_client)->mac[0],
 					 get_client_memptr(wan_client, num_wan_client)->mac[1],
 					 get_client_memptr(wan_client, num_wan_client)->mac[2],
 					 get_client_memptr(wan_client, num_wan_client)->mac[3],
 					 get_client_memptr(wan_client, num_wan_client)->mac[4],
 					 get_client_memptr(wan_client, num_wan_client)->mac[5]);
+	}
 
 	/* add header to IPA */
 	if(tx_prop != NULL)
@@ -6254,13 +6342,23 @@
 						goto fail;
 					 }
 
-					get_client_memptr(wan_client, num_wan_client)->hdr_hdl_v4 = pHeaderDescriptor->hdr[0].hdr_hdl;
-					IPACMDBG_H("eth-client(%d) v4 full header name:%s header handle:(0x%x)\n",
+					if (!replaced)
+					{
+						get_client_memptr(wan_client, num_wan_client)->hdr_hdl_v4 = pHeaderDescriptor->hdr[0].hdr_hdl;
+						IPACMDBG_H("eth-client(%d) v4 full header name:%s header handle:(0x%x)\n",
 												 num_wan_client,
 												 pHeaderDescriptor->hdr[0].name,
 												 get_client_memptr(wan_client, num_wan_client)->hdr_hdl_v4);
 									get_client_memptr(wan_client, num_wan_client)->ipv4_header_set=true;
-
+					} else
+					{
+						get_client_memptr(wan_client, entry)->hdr_hdl_v4 = pHeaderDescriptor->hdr[0].hdr_hdl;
+						IPACMDBG_H("replaced eth-client(%d) v4 full header name:%s header handle:(0x%x)\n",
+												 entry,
+												 pHeaderDescriptor->hdr[0].name,
+												 get_client_memptr(wan_client, entry)->hdr_hdl_v4);
+									get_client_memptr(wan_client, entry)->ipv4_header_set=true;
+					}
 					break;
 				 }
 		}
@@ -6350,24 +6448,45 @@
 					goto fail;
 				}
 
-				get_client_memptr(wan_client, num_wan_client)->hdr_hdl_v6 = pHeaderDescriptor->hdr[0].hdr_hdl;
-				IPACMDBG_H("eth-client(%d) v6 full header name:%s header handle:(0x%x)\n",
+				if (!replaced)
+				{
+					get_client_memptr(wan_client, num_wan_client)->hdr_hdl_v6 = pHeaderDescriptor->hdr[0].hdr_hdl;
+					IPACMDBG_H("eth-client(%d) v6 full header name:%s header handle:(0x%x)\n",
 						 num_wan_client,
 						 pHeaderDescriptor->hdr[0].name,
 									 get_client_memptr(wan_client, num_wan_client)->hdr_hdl_v6);
-
 									get_client_memptr(wan_client, num_wan_client)->ipv6_header_set=true;
+				}
+				else
+				{
+					get_client_memptr(wan_client, entry)->hdr_hdl_v6 = pHeaderDescriptor->hdr[0].hdr_hdl;
+					IPACMDBG_H("replaced eth-client(%d) v6 full header name:%s header handle:(0x%x)\n",
+							entry,
+							pHeaderDescriptor->hdr[0].name,
+							get_client_memptr(wan_client, entry)->hdr_hdl_v6);
+							get_client_memptr(wan_client, entry)->ipv6_header_set=true;
+				}
 
 				break;
 
 			}
 		}
 		/* initialize wifi client*/
-		get_client_memptr(wan_client, num_wan_client)->route_rule_set_v4 = false;
-		get_client_memptr(wan_client, num_wan_client)->route_rule_set_v6 = 0;
-		get_client_memptr(wan_client, num_wan_client)->ipv4_set = false;
-		get_client_memptr(wan_client, num_wan_client)->ipv6_set = 0;
-		num_wan_client++;
+		if (!replaced)
+		{
+			get_client_memptr(wan_client, num_wan_client)->route_rule_set_v4 = false;
+			get_client_memptr(wan_client, num_wan_client)->route_rule_set_v6 = 0;
+			get_client_memptr(wan_client, num_wan_client)->ipv4_set = false;
+			get_client_memptr(wan_client, num_wan_client)->ipv6_set = 0;
+			num_wan_client++;
+		}
+		else
+		{
+			get_client_memptr(wan_client, entry)->route_rule_set_v4 = false;
+			get_client_memptr(wan_client, entry)->route_rule_set_v6 = 0;
+			get_client_memptr(wan_client, entry)->ipv4_set = false;
+			get_client_memptr(wan_client, entry)->ipv6_set = 0;
+		}
 		header_name_count++; //keep increasing header_name_count
 		res = IPACM_SUCCESS;
 		IPACMDBG_H("eth client number: %d\n", num_wan_client);
@@ -7342,10 +7461,10 @@
 			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[0] = ipv6_addr[i][0];
+			rt_rule_entry->rule.attrib.u.v6.dst_addr[1] = ipv6_addr[i][1];
+			rt_rule_entry->rule.attrib.u.v6.dst_addr[2] = ipv6_addr[i][2];
+			rt_rule_entry->rule.attrib.u.v6.dst_addr[3] = ipv6_addr[i][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;
@@ -7919,11 +8038,126 @@
 		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);
+	if (if_mtu.ifr_mtu <= DEFAULT_MTU_SIZE) {
+		mtu_v4 = mtu_v6 = if_mtu.ifr_mtu;
+	}else {
+		mtu_v4 = mtu_v6 = DEFAULT_MTU_SIZE;
 	}
+	IPACMDBG_H("Updated mtu=[%d] for (%s)\n", mtu_v4, dev_name);
 
 	close(fd);
 	return IPACM_SUCCESS;
 }
+
+/* construct complete ethernet header */
+int IPACM_Wan::handle_gw_mac_renew(ipacm_event_data_all *data, int index_client)
+{
+	int index = IPACM_INVALID_INDEX;
+
+	/* checking if client has same ipv4, v6 will put future work */
+	if (data->iptype == IPA_IP_v4)
+	{
+		index = get_wan_client_index_ipv4(data->ipv4_addr);
+		if (index != IPACM_INVALID_INDEX)
+		{
+			IPACMDBG_H("Matched client index: %d\n", index);
+			IPACMDBG_H("Client MAC in cache %02x:%02x:%02x:%02x:%02x:%02x\n",
+				get_client_memptr(wan_client, index)->mac[0],
+				get_client_memptr(wan_client, index)->mac[1],
+				get_client_memptr(wan_client, index)->mac[2],
+				get_client_memptr(wan_client, index)->mac[3],
+					get_client_memptr(wan_client, index)->mac[4],
+					get_client_memptr(wan_client, index)->mac[5]);
+
+			/* check mac same or not */
+			if ((data->mac_addr[0] == get_client_memptr(wan_client, index)->mac[0]) &&
+				(data->mac_addr[1] == get_client_memptr(wan_client, index)->mac[1]) &&
+				(data->mac_addr[2] == get_client_memptr(wan_client, index)->mac[2]) &&
+				(data->mac_addr[3] == get_client_memptr(wan_client, index)->mac[3]) &&
+				(data->mac_addr[4] == get_client_memptr(wan_client, index)->mac[4]) &&
+				(data->mac_addr[5] == get_client_memptr(wan_client, index)->mac[5]))
+			{
+				IPACMDBG_H(" No need client (%d) mac renew with IPv4 (0x%x)\n", index, data->ipv4_addr);
+				return IPACM_FAILURE;
+			}
+			else
+			{
+				IPACMDBG_H(" client %d need mac renew with IPv4 (0x%x)\n", index, data->ipv4_addr);
+
+				/* Del NAT rules before ipv4 RT rules are delete */
+				if(get_client_memptr(wan_client, index)->ipv4_set == true)
+				{
+					IPACMDBG_H("Clean Nat Rules for ipv4:0x%x\n", get_client_memptr(wan_client, index)->v4_addr);
+					CtList->HandleSTAClientDelEvt(get_client_memptr(wan_client, index)->v4_addr);
+				}
+
+				/* clean up STA header / routing rule */
+				if (data->ipv4_addr == wan_v4_addr_gw && active_v4)
+				{
+					handle_route_del_evt(IPA_IP_v4, true);
+					IPACMDBG_H("Delete default v4 routing rules\n");
+					hdr_hdl_sta_v4 = 0;
+					header_set_v4 = false;
+					header_partial_default_wan_v4 = true;
+
+					if (active_v6)
+					{
+						handle_route_del_evt(IPA_IP_v6, true);
+						IPACMDBG_H("Delete default v6 routing rules\n");
+						header_partial_default_wan_v6 = true;
+					}
+					hdr_hdl_sta_v6 = 0;
+					header_set_v6 = false;
+				}
+
+				/* clean up client header routing rule entry */
+				if(delete_wan_rtrules(index, IPA_IP_v4))
+				{
+						IPACMERR("unbale to delete wan-client v4 route rules for index %d\n", index);
+						return IPACM_FAILURE;
+				}
+
+				get_client_memptr(wan_client, index)->route_rule_set_v4 = false;
+				get_client_memptr(wan_client, index)->ipv4_set = false;
+
+				IPACMDBG_H("Delete client %d header\n", index);
+				if(get_client_memptr(wan_client, index)->ipv4_header_set == true)
+				{
+					if (m_header.DeleteHeaderHdl(get_client_memptr(wan_client, index)->hdr_hdl_v4) == false)
+					{
+						IPACMERR("unable to delete client v4 header for index %d\n", index);
+						return IPACM_FAILURE;
+					}
+					get_client_memptr(wan_client, index)->ipv4_header_set = false;
+				}
+
+				if(delete_wan_rtrules(index, IPA_IP_v6))
+				{
+					IPACMERR("unbale to delete wan-client v6 route rules for index %d\n", index);
+					return IPACM_FAILURE;
+				}
+				get_client_memptr(wan_client, index)->route_rule_set_v6 = 0;
+				get_client_memptr(wan_client, index)->ipv6_set = 0;
+				if(get_client_memptr(wan_client, index)->ipv6_header_set == true)
+				{
+					if (m_header.DeleteHeaderHdl(get_client_memptr(wan_client, index)->hdr_hdl_v6) == false)
+					{
+						IPACMERR("unable to delete client v6 header for index %d\n", index);
+						return IPACM_FAILURE;
+					}
+					get_client_memptr(wan_client, index)->ipv6_header_set = false;
+				}
+				/* replacing the old mac to new_mac on same entry */
+				get_client_memptr(wan_client, index)->mac[0] = data->mac_addr[0];
+				get_client_memptr(wan_client, index)->mac[1] = data->mac_addr[1];
+				get_client_memptr(wan_client, index)->mac[2] = data->mac_addr[2];
+				get_client_memptr(wan_client, index)->mac[3] = data->mac_addr[3];
+				get_client_memptr(wan_client, index)->mac[4] = data->mac_addr[4];
+				get_client_memptr(wan_client, index)->mac[5] = data->mac_addr[5];
+				index_client = index;
+				return IPACM_SUCCESS;
+			}
+		}
+	}
+	return IPACM_FAILURE;
+}
diff --git a/ipacm/src/IPACM_Wlan.cpp b/ipacm/src/IPACM_Wlan.cpp
index 689cc78..eeb4b01 100644
--- a/ipacm/src/IPACM_Wlan.cpp
+++ b/ipacm/src/IPACM_Wlan.cpp
@@ -562,25 +562,6 @@
 		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);
@@ -651,6 +632,35 @@
 		}
 		break;
 	}
+#ifdef IPA_MTU_EVENT_MAX
+	case IPA_MTU_UPDATE:
+	{
+		IPACMDBG_H("Received IPA_MTU_UPDATE");
+		ipacm_event_mtu_info *evt_data = (ipacm_event_mtu_info *)param;
+		ipa_mtu_info *data = &(evt_data->mtu_info);
+
+		/* IPA_IP_MAX means both ipv4 and ipv6 */
+		if ((data->ip_type == IPA_IP_v4 || data->ip_type == IPA_IP_MAX) && IPACM_Wan::isWanUP(ipa_if_num))
+		{
+			handle_private_subnet_android(IPA_IP_v4);
+		}
+
+		/* IPA_IP_MAX means both ipv4 and ipv6 */
+		if ((data->ip_type == IPA_IP_v6 || data->ip_type == IPA_IP_MAX) && IPACM_Wan::isWanUP_V6(ipa_if_num))
+		{
+			//check if the prefix + MTU rules are installed
+			if (ipv6_prefix_flt_rule_hdl[0] && ipv6_prefix_flt_rule_hdl[1]) {
+				modify_ipv6_prefix_flt_rule(IPACM_Wan::backhaul_ipv6_prefix);
+			}
+			else
+			{
+				IPACMERR("failed to update prefix MTU rules, no prefix rules set");
+			}
+		}
+	}
+	break;
+#endif
+
 #else
 	case IPA_HANDLE_WAN_UP:
 		IPACMDBG_H("Received IPA_HANDLE_WAN_UP event\n");
diff --git a/ipacm_vendor_product.mk b/ipacm_vendor_product.mk
index 9fe88b1..f225a30 100644
--- a/ipacm_vendor_product.mk
+++ b/ipacm_vendor_product.mk
@@ -11,12 +11,20 @@
 endif #TARGET_USES_QMAA_OVERRIDE_DATA
 endif #TARGET_USES_QMAA
 
+BOARD_IPA_LOW_RAM_EXCP_LIST := bengal
+
+ifeq ($(TARGET_HAS_LOW_RAM),true)
+ifneq ($(call is-board-platform-in-list,$(BOARD_IPA_LOW_RAM_EXCP_LIST)),true)
+	TARGET_DISABLE_IPACM := true
+endif
+endif
+
 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_PLATFORM_LIST += msm8937
 BOARD_IPAv3_LIST := msm8998
 BOARD_IPAv3_LIST += sdm845
 BOARD_IPAv3_LIST += sdm710
@@ -40,4 +48,3 @@
 endif
 endif
 endif
-endif
\ No newline at end of file