ipacm: add dynamic support for IPA new MTU events

Handle new MTU events to replace modem PDN MTU for WANv4/v6
and replace the MTU rules if they are already installed.

Change-Id: I06c16db2300ccd572a99ab316d413fcbf9197986
Signed-off-by: Michael Adisumarta <madisuma@codeaurora.org>
diff --git a/ipacm/inc/IPACM_Defs.h b/ipacm/inc/IPACM_Defs.h
index e60516e..82e9c6b 100644
--- a/ipacm/inc/IPACM_Defs.h
+++ b/ipacm/inc/IPACM_Defs.h
@@ -193,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 */
@@ -407,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_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 ad62783..eecf039 100644
--- a/ipacm/inc/IPACM_Wan.h
+++ b/ipacm/inc/IPACM_Wan.h
@@ -105,8 +105,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 +147,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 +406,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);
@@ -674,7 +683,7 @@
 
 	int delete_tcp_fin_rst_exception_rule();
 
-	/* Query mtu size */
+	/* MTU helper functions */
 	int query_mtu_size();
 };
 
diff --git a/ipacm/src/IPACM_Iface.cpp b/ipacm/src/IPACM_Iface.cpp
index 8282915..55cf71d 100644
--- a/ipacm/src/IPACM_Iface.cpp
+++ b/ipacm/src/IPACM_Iface.cpp
@@ -1009,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 7ba5a80..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);
@@ -464,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 7e7e875..a6b2b90 100644
--- a/ipacm/src/IPACM_Lan.cpp
+++ b/ipacm/src/IPACM_Lan.cpp
@@ -1110,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;
 	}
@@ -1641,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);
 			}
 		}
 
@@ -1912,9 +1942,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);
 			}
 		}
 
@@ -4293,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");
@@ -4302,12 +4337,114 @@
 	}
 	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)
 	{
@@ -4383,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)
 		{
diff --git a/ipacm/src/IPACM_Main.cpp b/ipacm/src/IPACM_Main.cpp
index ca75a4a..6bd117e 100644
--- a/ipacm/src/IPACM_Main.cpp
+++ b/ipacm/src/IPACM_Main.cpp
@@ -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);
@@ -874,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_Wan.cpp b/ipacm/src/IPACM_Wan.cpp
index 397c820..ad174c4 100644
--- a/ipacm/src/IPACM_Wan.cpp
+++ b/ipacm/src/IPACM_Wan.cpp
@@ -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);
@@ -1112,6 +1164,7 @@
 			}
 		}
 		break;
+
 	case IPA_LINK_DOWN_EVENT:
 		{
 			ipacm_event_data_fid *data = (ipacm_event_data_fid *)param;
@@ -1721,54 +1774,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;
 	}
@@ -1779,7 +1895,6 @@
 /* wan default route/filter rule configuration */
 int IPACM_Wan::handle_route_add_evt(ipa_ip_type iptype)
 {
-
 	/* add default WAN route */
 	struct ipa_ioc_add_rt_rule *rt_rule = NULL;
 	struct ipa_rt_rule_add *rt_rule_entry;
@@ -1817,8 +1932,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,11 +2225,6 @@
 				wan_route_rule_v6_hdl_a5[0], 0, iptype);
 	}
 
-	/* set mtu_default_wan to current default wan instance */
-	mtu_default_wan = mtu_size;
-
-	IPACMDBG_H("replace the mtu_default_wan to %d\n", mtu_default_wan);
-
 	ipacm_event_iface_up *wanup_data;
 	wanup_data = (ipacm_event_iface_up *)malloc(sizeof(ipacm_event_iface_up));
 	if (wanup_data == NULL)
@@ -2122,6 +2238,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 +2301,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]);
 
@@ -5453,8 +5577,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)
 	{
@@ -7828,10 +7957,12 @@
 		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;
diff --git a/ipacm/src/IPACM_Wlan.cpp b/ipacm/src/IPACM_Wlan.cpp
index 689cc78..460b6c0 100644
--- a/ipacm/src/IPACM_Wlan.cpp
+++ b/ipacm/src/IPACM_Wlan.cpp
@@ -651,6 +651,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");