Merge 75b51b2468e0afbbdabc11715dd856ce20d0fc92 on remote branch

Change-Id: I799d24379341d0b4a380ffb1344f46ee9e87a0ae
diff --git a/ipacm/inc/IPACM_ConntrackListener.h b/ipacm/inc/IPACM_ConntrackListener.h
index 2977af7..217761b 100644
--- a/ipacm/inc/IPACM_ConntrackListener.h
+++ b/ipacm/inc/IPACM_ConntrackListener.h
@@ -103,6 +103,7 @@
 	void CheckSTAClient(const nat_table_entry *, bool *);
 	int CheckNatIface(ipacm_event_data_all *, bool *);
 	void HandleNonNatIPAddr(void *, bool);
+	void HandleNatTableMove(void *in_param);
 
 #ifdef CT_OPT
 	void ProcessCTV6Message(void *);
diff --git a/ipacm/inc/IPACM_Conntrack_NATApp.h b/ipacm/inc/IPACM_Conntrack_NATApp.h
index 95dcdfe..9490aa6 100644
--- a/ipacm/inc/IPACM_Conntrack_NATApp.h
+++ b/ipacm/inc/IPACM_Conntrack_NATApp.h
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2013-2020, The Linux Foundation. All rights reserved.
+Copyright (c) 2013-2021, 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
@@ -119,6 +119,7 @@
 	int AddTable(uint32_t, uint8_t mux_id);
 	uint32_t GetTableHdl(uint32_t);
 	int DeleteTable(uint32_t);
+	int MoveTable(bool to_ddr);
 
 	int AddEntry(const nat_table_entry *);
 	int DeleteEntry(const nat_table_entry *);
diff --git a/ipacm/inc/IPACM_Defs.h b/ipacm/inc/IPACM_Defs.h
index 20bef93..0d2a257 100644
--- a/ipacm/inc/IPACM_Defs.h
+++ b/ipacm/inc/IPACM_Defs.h
@@ -44,6 +44,7 @@
 #include <fcntl.h>
 #include <linux/msm_ipa.h>
 #include "IPACM_Log.h"
+#include "linux/ipa_qmi_service_v01.h"
 
 #ifdef USE_GLIB
 #include <glib.h>
@@ -146,7 +147,7 @@
 	IPA_BRIDGE_LINK_UP_EVENT,                 /* ipacm_event_data_all */
 	IPA_WAN_EMBMS_LINK_UP_EVENT,              /* ipacm_event_data_mac */
 	IPA_ADDR_ADD_EVENT,                       /* ipacm_event_data_addr */
-	IPA_ADDR_DEL_EVENT,                       /* no use */
+	IPA_ADDR_DEL_EVENT,                       /* ipacm_event_data_addr */
 	IPA_ROUTE_ADD_EVENT,                      /* ipacm_event_data_addr */
 	IPA_ROUTE_DEL_EVENT,                      /* ipacm_event_data_addr */
 	IPA_WAN_UPSTREAM_ROUTE_ADD_EVENT,         /* ipacm_event_data_fid */
@@ -213,6 +214,7 @@
 	IPA_LAN_DELETE_SELF,                      /* ipacm_event_data_fid */
 	IPA_WIGIG_CLIENT_ADD_EVENT,               /* ipacm_event_data_mac_ep */
 	IPA_WIGIG_FST_SWITCH,                     /* ipacm_event_data_fst */
+	IPA_MOVE_NAT_TBL_EVENT,                   /* ipacm_event_move_nat */
 	IPACM_EVENT_MAX
 } ipa_cm_event_id;
 
@@ -394,6 +396,10 @@
 	uint8_t xlat_mux_id;
 }ipacm_event_iface_up_tehter;
 
+typedef struct
+{
+	ipa_move_nat_type_enum_v01 nat_move_direction;
+}ipacm_event_move_nat;
 
 typedef struct _ipacm_ifacemgr_data
 {
diff --git a/ipacm/inc/IPACM_Iface.h b/ipacm/inc/IPACM_Iface.h
index 35d12db..00c410a 100644
--- a/ipacm/inc/IPACM_Iface.h
+++ b/ipacm/inc/IPACM_Iface.h
@@ -103,7 +103,7 @@
 	uint32_t dft_v6fl_rule_hdl[IPV6_DEFAULT_FILTERTING_RULES + IPV6_DEFAULT_LAN_FILTERTING_RULES];
 	/* create additional set of v6 RT-rules in Wanv6RT table*/
 	uint32_t dft_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES+2*MAX_DEFAULT_v6_ROUTE_RULES];
-
+	uint32_t dft_rt_v6_glbl_idx;
 	ipa_ioc_query_intf *iface_query;
 	ipa_ioc_query_intf_tx_props *tx_prop;
 	ipa_ioc_query_intf_rx_props *rx_prop;
diff --git a/ipacm/inc/IPACM_Wan.h b/ipacm/inc/IPACM_Wan.h
index 77954c7..e72c2be 100644
--- a/ipacm/inc/IPACM_Wan.h
+++ b/ipacm/inc/IPACM_Wan.h
@@ -60,6 +60,7 @@
 #else
 #define IPA_V2_NUM_DEFAULT_WAN_FILTER_RULE_IPV6 3
 #endif
+#define MAX_DEFAULT_SEC_v6_ROUTE_RULES  1
 
 #define NETWORK_STATS "%s %llu %llu %llu %llu"
 #define IPA_NETWORK_STATS_FILE_NAME "/data/misc/ipa/network_stats"
@@ -351,6 +352,8 @@
 	uint32_t ODU_fl_hdl[IPA_NUM_DEFAULT_WAN_FILTER_RULES];
 	int num_firewall_v4,num_firewall_v6;
 	uint32_t wan_v4_addr;
+	uint32_t sec_wan_v4_addr;
+	bool sec_wan_v4_addr_set;
 	uint32_t wan_v4_addr_gw;
 	uint32_t wan_v6_addr_gw[4];
 	bool wan_v4_addr_set;
@@ -364,11 +367,19 @@
 	bool header_partial_default_wan_v6;
 	uint8_t ext_router_mac_addr[IPA_MAC_ADDR_SIZE];
 	uint8_t netdev_mac[IPA_MAC_ADDR_SIZE];
+	/* IPACM interface secondary v6 ip-addresses */
+	uint32_t sec_ipv6_addr[MAX_DEFAULT_SEC_v6_ROUTE_RULES][4];
 	/* create additional set of v4 Coalesce RT-rules: tcp udp */
 	uint32_t dft_coalesce_rt_rule_hdl[2*MAX_DEFAULT_v4_ROUTE_RULES+ 2*MAX_DEFAULT_v6_ROUTE_RULES];
-	/* create additional set of v4 low_lat RT-rules: tcp udp */
+	/* create additional set of v4 Coalesce RT-rules for secondary addresses: tcp udp */
+	uint32_t sec_dft_coalesce_rt_rule_hdl[2*MAX_DEFAULT_v4_ROUTE_RULES+ 2*MAX_DEFAULT_SEC_v6_ROUTE_RULES];
+
+	/* create additional set of v4/v6 low_lat RT-rules: tcp udp */
 	uint32_t dft_low_lat_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES+ MAX_DEFAULT_v6_ROUTE_RULES];
 
+	/* create additional set of v4/v6 low_lat RT-rules for seconday addresses: */
+	uint32_t sec_dft_low_lat_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES+ MAX_DEFAULT_SEC_v6_ROUTE_RULES];
+
 	static int num_ipv4_modem_pdn;
 
 	static int num_ipv6_modem_pdn;
@@ -417,6 +428,12 @@
 	uint16_t mtu_v6;
 	bool mtu_v6_set;
 
+	/* IPACM number of default route rules for secondary ipv6 address*/
+	uint32_t sec_num_dft_rt_v6;
+
+	/* create additional set of v6 RT-rules for secondary addresses in Wanv6RT table*/
+	uint32_t sec_dft_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES + 2*MAX_DEFAULT_SEC_v6_ROUTE_RULES];
+
 	inline ipa_wan_client* get_client_memptr(ipa_wan_client *param, int cnt)
 	{
 	    char *ret = ((char *)param) + (wan_client_len * cnt);
@@ -605,6 +622,9 @@
 	/* handle new_address event */
 	int handle_addr_evt(ipacm_event_data_addr *data);
 
+	/* handle del_address event */
+	int handle_addr_del_evt(ipacm_event_data_addr *data);
+
 	/* handle new_address event for q6_mhi */
 	int handle_addr_evt_mhi_q6(ipacm_event_data_addr *data);
 
diff --git a/ipacm/src/IPACM_Config.cpp b/ipacm/src/IPACM_Config.cpp
index 369c7d4..41b920d 100644
--- a/ipacm/src/IPACM_Config.cpp
+++ b/ipacm/src/IPACM_Config.cpp
@@ -125,7 +125,8 @@
 	__stringify(IPA_LAN_DELETE_SELF),                      /* ipacm_event_data_fid */
 	__stringify(IPA_WIGIG_CLIENT_ADD_EVENT),               /* ipacm_event_data_mac_ep */
 	__stringify(IPA_WIGIG_FST_SWITCH),                     /* ipacm_event_data_fst */
-	__stringify(IPACM_EVENT_MAX),
+	__stringify(IPA_MOVE_NAT_TBL_EVENT),                   /* ipacm_event_move_nat */
+	__stringify(IPACM_EVENT_MAX)
 };
 
 IPACM_Config::IPACM_Config()
diff --git a/ipacm/src/IPACM_ConntrackListener.cpp b/ipacm/src/IPACM_ConntrackListener.cpp
index b991324..f0abb84 100644
--- a/ipacm/src/IPACM_ConntrackListener.cpp
+++ b/ipacm/src/IPACM_ConntrackListener.cpp
@@ -65,6 +65,7 @@
 	 IPACM_EvtDispatcher::registr(IPA_HANDLE_LAN_UP, this);
 	 IPACM_EvtDispatcher::registr(IPA_NEIGH_CLIENT_IP_ADDR_ADD_EVENT, this);
 	 IPACM_EvtDispatcher::registr(IPA_NEIGH_CLIENT_IP_ADDR_DEL_EVENT, this);
+	 IPACM_EvtDispatcher::registr(IPA_MOVE_NAT_TBL_EVENT, this);
 
 #ifdef CT_OPT
 	 p_lan2lan = IPACM_LanToLan::getLan2LanInstance();
@@ -144,7 +145,10 @@
 		 IPACMDBG("Received IPA_NEIGH_CLIENT_IP_ADDR_DEL_EVENT event\n");
 		 HandleNonNatIPAddr(data, false);
 		 break;
-
+	 case IPA_MOVE_NAT_TBL_EVENT:
+		 IPACMDBG_H("Received IPA_MOVE_NAT_TBL_EVENT event\n");
+		 HandleNatTableMove(data);
+		 break;
 	 default:
 			IPACMDBG("Ignore cmd %d\n", evt);
 			break;
@@ -1501,3 +1505,37 @@
 	IPACMDBG("Exit:\n");
 }
 
+void IPACM_ConntrackListener::HandleNatTableMove(void *in_param)
+{
+	int ret;
+	int fd_wwan_ioctl;
+	ipacm_event_move_nat *data_nat = (ipacm_event_move_nat *)in_param;
+
+	IPACMDBG_H("handling nat table move request\n");
+
+	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;
+	}
+
+	if(data_nat->nat_move_direction == QMI_IPA_MOVE_NAT_TO_DDR_V01) {
+		ret = nat_inst->MoveTable(true);
+	}
+	else {
+		ret = nat_inst->MoveTable(false);
+	}
+
+	IPACMDBG_H("sending indication to Q6 about transition %s\n",
+		ret ? "failure" : "success");
+
+	ret = ioctl(fd_wwan_ioctl, WAN_IOC_NOTIFY_NAT_MOVE_RES, ret);
+	if(ret != 0)
+	{
+		IPACMERR("Failed sending NAT TABLR MOVE indication with ret %d\n ", ret);
+	}
+
+	close(fd_wwan_ioctl);
+}
+
diff --git a/ipacm/src/IPACM_Conntrack_NATApp.cpp b/ipacm/src/IPACM_Conntrack_NATApp.cpp
index 111733a..5b72cba 100644
--- a/ipacm/src/IPACM_Conntrack_NATApp.cpp
+++ b/ipacm/src/IPACM_Conntrack_NATApp.cpp
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2013-2020, The Linux Foundation. All rights reserved.
+Copyright (c) 2013-2021, 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
@@ -318,6 +318,21 @@
 	return 0;
 }
 
+int NatApp::MoveTable(bool to_ddr)
+{
+	int ret;
+
+	if (to_ddr) {
+		IPACMDBG_H("direction TO_DDR - move and lock table at DDR\n");
+		ret = ipa_nat_switch_to(IPA_NAT_MEM_IN_DDR, true);
+	} else {
+		IPACMDBG_H("direction TO_SRAM - allow table transition to SRAM\n");
+		ret = ipa_nat_switch_to(IPA_NAT_MEM_IN_DDR, false);
+	}
+
+	return ret;
+}
+
 /* Check for duplicate entries */
 bool NatApp::ChkForDup(const nat_table_entry *rule)
 {
diff --git a/ipacm/src/IPACM_IfaceManager.cpp b/ipacm/src/IPACM_IfaceManager.cpp
index a2e9b66..0be4695 100644
--- a/ipacm/src/IPACM_IfaceManager.cpp
+++ b/ipacm/src/IPACM_IfaceManager.cpp
@@ -470,6 +470,7 @@
 					IPACM_EvtDispatcher::registr(IPA_WAN_UPSTREAM_ROUTE_DEL_EVENT, w);
 					if(is_sta_mode == Q6_WAN)
 					{
+						IPACM_EvtDispatcher::registr(IPA_ADDR_DEL_EVENT, w);
 						IPACM_EvtDispatcher::registr(IPA_NETWORK_STATS_UPDATE_EVENT, w);
 #ifdef IPA_MTU_EVENT_MAX
 						IPACM_EvtDispatcher::registr(IPA_MTU_SET, w);
diff --git a/ipacm/src/IPACM_Main.cpp b/ipacm/src/IPACM_Main.cpp
index 8166329..2e41389 100644
--- a/ipacm/src/IPACM_Main.cpp
+++ b/ipacm/src/IPACM_Main.cpp
@@ -305,6 +305,9 @@
 #endif
 
 	param = NULL;
+	struct ipa_move_nat_req_msg_v01 *move_nat;
+	ipacm_event_move_nat *move_nat_data;
+
 	fd = open(IPA_DRIVER, O_RDWR);
 	if (fd < 0)
 	{
@@ -945,6 +948,23 @@
 			break;
 #endif
 
+		case IPA_MOVE_NAT_TABLE:
+			move_nat = (struct ipa_move_nat_req_msg_v01 *)(buffer + sizeof(struct ipa_msg_meta));
+			IPACMDBG_H("received IPA_MOVE_NAT_TABLE direction %s\n",
+				move_nat->nat_move_direction == QMI_IPA_MOVE_NAT_TO_DDR_V01 ? "TO_DDR" : "TO_SRAM");
+			move_nat_data = (ipacm_event_move_nat *)malloc(sizeof(ipacm_event_move_nat));
+			if(move_nat_data == NULL)
+			{
+				IPACMERR("unable to allocate memory for move_nat_tbl_evnt\n");
+				return NULL;
+			}
+
+			move_nat_data->nat_move_direction = move_nat->nat_move_direction;
+
+			evt_data.event = IPA_MOVE_NAT_TBL_EVENT;
+			evt_data.evt_data = move_nat_data;
+			break;
+
 		default:
 			IPACMDBG_H("Unhandled message type: %d\n", event_hdr.msg_type);
 			continue;
diff --git a/ipacm/src/IPACM_Netlink.cpp b/ipacm/src/IPACM_Netlink.cpp
index fa4b8b7..a32dd7f 100644
--- a/ipacm/src/IPACM_Netlink.cpp
+++ b/ipacm/src/IPACM_Netlink.cpp
@@ -878,6 +878,71 @@
 			}
 			break;
 
+		case RTM_DELADDR:
+			IPACMDBG("\n GOT RTM_DELADDR event\n");
+			if(IPACM_SUCCESS != ipa_nl_decode_rtm_addr(buffer, buflen, &(msg_ptr->nl_addr_info)))
+			{
+				IPACMERR("Failed to decode rtm addr message\n");
+				return IPACM_FAILURE;
+			}
+			else
+			{
+				ret_val = ipa_get_if_name(dev_name, msg_ptr->nl_addr_info.metainfo.ifa_index);
+				if(ret_val != IPACM_SUCCESS)
+				{
+					IPACMERR("Error while getting interface name\n");
+				}
+				IPACMDBG("Interface %s \n", dev_name);
+
+				data_addr = (ipacm_event_data_addr *)malloc(sizeof(ipacm_event_data_addr));
+				if(data_addr == NULL)
+				{
+					IPACMERR("unable to allocate memory for event data_addr\n");
+					return IPACM_FAILURE;
+				}
+
+				if(AF_INET6 == msg_ptr->nl_addr_info.attr_info.prefix_addr.ss_family)
+				{
+					data_addr->iptype = IPA_IP_v6;
+					IPACM_NL_REPORT_ADDR( "IFA_ADDRESS:", msg_ptr->nl_addr_info.attr_info.prefix_addr );
+					IPACM_EVENT_COPY_ADDR_v6( data_addr->ipv6_addr, msg_ptr->nl_addr_info.attr_info.prefix_addr);
+					data_addr->ipv6_addr[0] = ntohl(data_addr->ipv6_addr[0]);
+					data_addr->ipv6_addr[1] = ntohl(data_addr->ipv6_addr[1]);
+					data_addr->ipv6_addr[2] = ntohl(data_addr->ipv6_addr[2]);
+					data_addr->ipv6_addr[3] = ntohl(data_addr->ipv6_addr[3]);
+				}
+				else
+				{
+					data_addr->iptype = IPA_IP_v4;
+					IPACM_NL_REPORT_ADDR( "IFA_ADDRESS:", msg_ptr->nl_addr_info.attr_info.prefix_addr );
+					IPACM_EVENT_COPY_ADDR_v4( data_addr->ipv4_addr, msg_ptr->nl_addr_info.attr_info.prefix_addr);
+					data_addr->ipv4_addr = ntohl(data_addr->ipv4_addr);
+
+				}
+
+				evt_data.event = IPA_ADDR_DEL_EVENT;
+				data_addr->if_index = msg_ptr->nl_addr_info.metainfo.ifa_index;
+				strlcpy(data_addr->iface_name, dev_name, sizeof(data_addr->iface_name));
+				if(AF_INET6 == msg_ptr->nl_addr_info.attr_info.prefix_addr.ss_family)
+				{
+				    IPACMDBG("Posting IPA_ADDR_DEL_EVENT with if index:%d, ipv6 addr:0x%x:%x:%x:%x\n",
+								 data_addr->if_index,
+								 data_addr->ipv6_addr[0],
+								 data_addr->ipv6_addr[1],
+								 data_addr->ipv6_addr[2],
+								 data_addr->ipv6_addr[3]);
+                }
+				else
+				{
+				IPACMDBG("Posting IPA_ADDR_DEL_EVENT with if index:%d, ipv4 addr:0x%x\n",
+								 data_addr->if_index,
+								 data_addr->ipv4_addr);
+				}
+				evt_data.evt_data = data_addr;
+				IPACM_EvtDispatcher::PostEvt(&evt_data);
+			}
+			break;
+
 		case RTM_NEWROUTE:
 
 			if(IPACM_SUCCESS != ipa_nl_decode_rtm_route(buffer, buflen, &(msg_ptr->nl_route_info)))
diff --git a/ipacm/src/IPACM_Wan.cpp b/ipacm/src/IPACM_Wan.cpp
index f0aef3c..1e01e5e 100644
--- a/ipacm/src/IPACM_Wan.cpp
+++ b/ipacm/src/IPACM_Wan.cpp
@@ -293,6 +293,7 @@
 #ifdef FEATURE_IPACM_HAL
 	IPACM_OffloadManager* OffloadMng;
 #endif
+	bool sec_addr = false;
 
 	memset(&hdr, 0, sizeof(hdr));
 	if(tx_prop == NULL || rx_prop == NULL)
@@ -306,18 +307,50 @@
 
 	if (data->iptype == IPA_IP_v6)
 	{
-		for(num_ipv6_addr=0;num_ipv6_addr<num_dft_rt_v6;num_ipv6_addr++)
+		/* Check if Global address changed for WWAN backhaul. */
+		if ((m_is_sta_mode == Q6_WAN) && is_global_ipv6_addr(data->ipv6_addr) &&
+			(data->ipv6_addr[0] || data->ipv6_addr[1]) &&
+			(ipv6_prefix[0] || ipv6_prefix[1]) &&
+			!((ipv6_prefix[0] == data->ipv6_addr[0]) &&
+				   (ipv6_prefix[1] == data->ipv6_addr[1])))
 		{
-			if((ipv6_addr[num_ipv6_addr][0] == data->ipv6_addr[0]) &&
-			   (ipv6_addr[num_ipv6_addr][1] == data->ipv6_addr[1]) &&
-			   (ipv6_addr[num_ipv6_addr][2] == data->ipv6_addr[2]) &&
-			   (ipv6_addr[num_ipv6_addr][3] == data->ipv6_addr[3]))
+			sec_addr = true;
+		}
+
+		if (sec_addr)
+		{
+			if (sec_num_dft_rt_v6 == MAX_DEFAULT_SEC_v6_ROUTE_RULES)
 			{
-				IPACMDBG_H("find matched ipv6 address, index:%d \n", num_ipv6_addr);
+				IPACMDBG_H("Secondary v6 addresses overflow:%d \n", sec_num_dft_rt_v6);
 				return IPACM_SUCCESS;
-				break;
+			}
+			for(num_ipv6_addr=0;num_ipv6_addr<sec_num_dft_rt_v6;num_ipv6_addr++)
+			{
+				if((sec_ipv6_addr[num_ipv6_addr][0] == data->ipv6_addr[0]) &&
+				   (sec_ipv6_addr[num_ipv6_addr][1] == data->ipv6_addr[1]) &&
+				   (sec_ipv6_addr[num_ipv6_addr][2] == data->ipv6_addr[2]) &&
+				   (sec_ipv6_addr[num_ipv6_addr][3] == data->ipv6_addr[3]))
+				{
+					IPACMDBG_H("find matched ipv6 address, index:%d \n", num_ipv6_addr);
+					return IPACM_SUCCESS;
+				}
 			}
 		}
+		else
+		{
+			for(num_ipv6_addr=0;num_ipv6_addr<num_dft_rt_v6;num_ipv6_addr++)
+			{
+				if((ipv6_addr[num_ipv6_addr][0] == data->ipv6_addr[0]) &&
+				   (ipv6_addr[num_ipv6_addr][1] == data->ipv6_addr[1]) &&
+				   (ipv6_addr[num_ipv6_addr][2] == data->ipv6_addr[2]) &&
+				   (ipv6_addr[num_ipv6_addr][3] == data->ipv6_addr[3]))
+				{
+					IPACMDBG_H("find matched ipv6 address, index:%d \n", num_ipv6_addr);
+					return IPACM_SUCCESS;
+				}
+			}
+		}
+
 		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));
@@ -345,10 +378,21 @@
 		rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[1] = 0xFFFFFFFF;
 		rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[2] = 0xFFFFFFFF;
 		rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[3] = 0xFFFFFFFF;
-		ipv6_addr[num_dft_rt_v6][0] = data->ipv6_addr[0];
-		ipv6_addr[num_dft_rt_v6][1] = data->ipv6_addr[1];
-		ipv6_addr[num_dft_rt_v6][2] = data->ipv6_addr[2];
-		ipv6_addr[num_dft_rt_v6][3] = data->ipv6_addr[3];
+		if (sec_addr)
+		{
+			sec_ipv6_addr[sec_num_dft_rt_v6][0] = data->ipv6_addr[0];
+			sec_ipv6_addr[sec_num_dft_rt_v6][1] = data->ipv6_addr[1];
+			sec_ipv6_addr[sec_num_dft_rt_v6][2] = data->ipv6_addr[2];
+			sec_ipv6_addr[sec_num_dft_rt_v6][3] = data->ipv6_addr[3];
+		}
+		else
+		{
+			ipv6_addr[num_dft_rt_v6][0] = data->ipv6_addr[0];
+			ipv6_addr[num_dft_rt_v6][1] = data->ipv6_addr[1];
+			ipv6_addr[num_dft_rt_v6][2] = data->ipv6_addr[2];
+			ipv6_addr[num_dft_rt_v6][3] = data->ipv6_addr[3];
+
+		}
 		if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
 			rt_rule_entry->rule.hashable = false;
 		if(m_is_sta_mode == Q6_WAN)
@@ -379,7 +423,10 @@
 				res = rt_rule_entry->status;
 				goto fail;
 			}
-			dft_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES + 2*num_dft_rt_v6] = rt_rule_entry->rt_rule_hdl;
+			if (sec_addr)
+				sec_dft_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES + 2*sec_num_dft_rt_v6] = rt_rule_entry->rt_rule_hdl;
+			else
+				dft_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES + 2*num_dft_rt_v6] = rt_rule_entry->rt_rule_hdl;
 
 			/* setup same rule for v6_wan table*/
 			strlcpy(rt_rule->rt_tbl_name, IPACM_Iface::ipacmcfg->rt_tbl_wan_v6.name, sizeof(rt_rule->rt_tbl_name));
@@ -395,13 +442,24 @@
 				res = rt_rule_entry->status;
 				goto fail;
 		}
+		if (sec_addr)
+		{
+			sec_dft_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES + 2*sec_num_dft_rt_v6+1] = rt_rule_entry->rt_rule_hdl;
+			IPACMDBG_H("Secondary ipv6 wan iface rt-rule hdl=0x%x hdl=0x%x, entry: %d %d\n",
+					sec_dft_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES + 2*sec_num_dft_rt_v6],
+					sec_dft_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES + 2*sec_num_dft_rt_v6+1],
+					MAX_DEFAULT_v4_ROUTE_RULES + 2*sec_num_dft_rt_v6,
+					MAX_DEFAULT_v4_ROUTE_RULES + 2*sec_num_dft_rt_v6+1);
+		}
+		else
+		{
 			dft_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES + 2*num_dft_rt_v6+1] = rt_rule_entry->rt_rule_hdl;
-
 			IPACMDBG_H("ipv6 wan iface rt-rule hdl=0x%x hdl=0x%x, entry: %d %d\n",
 					dft_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES + 2*num_dft_rt_v6],
 					dft_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES + 2*num_dft_rt_v6+1],
 					MAX_DEFAULT_v4_ROUTE_RULES + 2*num_dft_rt_v6,
 					MAX_DEFAULT_v4_ROUTE_RULES + 2*num_dft_rt_v6+1);
+		}
 			/* RSC TCP rule*/
 			rt_rule_entry->rule.attrib.attrib_mask |= IPA_FLT_NEXT_HDR;
 			rt_rule_entry->rule.attrib.u.v6.next_hdr = (uint8_t)IPACM_FIREWALL_IPPROTO_TCP;
@@ -423,10 +481,20 @@
 				res = rt_rule_entry->status;
 				goto fail;
 		}
+		if (sec_addr)
+		{
+			sec_dft_coalesce_rt_rule_hdl[2*MAX_DEFAULT_v4_ROUTE_RULES + 2*sec_num_dft_rt_v6] = rt_rule_entry->rt_rule_hdl;
+			IPACMDBG_H("Secondary ipv6 wan iface rsc tcp rt-rule hdll=0x%x\n enable(%d), entry %d", sec_dft_coalesce_rt_rule_hdl[2*MAX_DEFAULT_v4_ROUTE_RULES + 2*sec_num_dft_rt_v6],
+				IPACM_Wan::coalesce_enable_info[ext_prop->ext[0].mux_id].coalesce_tcp_enable,
+				2*MAX_DEFAULT_v4_ROUTE_RULES + 2*sec_num_dft_rt_v6);
+		}
+		else
+		{
 			dft_coalesce_rt_rule_hdl[2*MAX_DEFAULT_v4_ROUTE_RULES + 2*num_dft_rt_v6] = rt_rule_entry->rt_rule_hdl;
 			IPACMDBG_H("ipv6 wan iface rsc tcp rt-rule hdll=0x%x\n enable(%d), entry %d", dft_coalesce_rt_rule_hdl[2*MAX_DEFAULT_v4_ROUTE_RULES + 2*num_dft_rt_v6],
 				IPACM_Wan::coalesce_enable_info[ext_prop->ext[0].mux_id].coalesce_tcp_enable,
 				2*MAX_DEFAULT_v4_ROUTE_RULES + 2*num_dft_rt_v6);
+		}
 			/* RSB UDP rule*/
 			rt_rule_entry->rule.attrib.u.v6.next_hdr = (uint8_t)IPACM_FIREWALL_IPPROTO_UDP;
 #ifdef IPA_RT_SUPPORT_COAL
@@ -446,11 +514,21 @@
 				IPACMERR("rsb udp rt rule adding failed. Result=%d\n", rt_rule_entry->status);
 				res = rt_rule_entry->status;
 				goto fail;
-			}
+		}
+		if (sec_addr)
+		{
+			sec_dft_coalesce_rt_rule_hdl[2*MAX_DEFAULT_v4_ROUTE_RULES + 2*sec_num_dft_rt_v6+1] = rt_rule_entry->rt_rule_hdl;
+			IPACMDBG_H("Secondary ipv6 wan iface rsb udp rt-rule hdll=0x%x\n enable(%d) entry %d", sec_dft_coalesce_rt_rule_hdl[2*MAX_DEFAULT_v4_ROUTE_RULES + 2*sec_num_dft_rt_v6+1],
+				IPACM_Wan::coalesce_enable_info[ext_prop->ext[0].mux_id].coalesce_udp_enable,
+				2*MAX_DEFAULT_v4_ROUTE_RULES + 2*num_dft_rt_v6+1);
+		}
+		else
+		{
 			dft_coalesce_rt_rule_hdl[2*MAX_DEFAULT_v4_ROUTE_RULES + 2*num_dft_rt_v6+1] = rt_rule_entry->rt_rule_hdl;
 			IPACMDBG_H("ipv6 wan iface rsb udp rt-rule hdll=0x%x\n enable(%d) entry %d", dft_coalesce_rt_rule_hdl[2*MAX_DEFAULT_v4_ROUTE_RULES + 2*num_dft_rt_v6+1],
 				IPACM_Wan::coalesce_enable_info[ext_prop->ext[0].mux_id].coalesce_udp_enable,
 				2*MAX_DEFAULT_v4_ROUTE_RULES + 2*num_dft_rt_v6+1);
+		}
 
 			/* Low latency v6 rule*/
 			fd_wwan_ioctl = open(IPA_DEVICE_NAME, O_RDWR);
@@ -481,9 +559,20 @@
 						res = rt_rule_entry->status;
 						goto fail;
 					}
+
+				if (sec_addr)
+				{
+					sec_dft_low_lat_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES] = rt_rule_entry->rt_rule_hdl;
+					IPACMDBG_H("secondary ipv6 wan iface low lat udp rt-rule hdll=0x%x\n entry %d", sec_dft_low_lat_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES],
+						MAX_DEFAULT_v4_ROUTE_RULES + sec_num_dft_rt_v6);
+				}
+				else
+				{
 					dft_low_lat_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES + num_dft_rt_v6] = rt_rule_entry->rt_rule_hdl;
 					IPACMDBG_H("ipv6 wan iface low lat udp rt-rule hdll=0x%x\n entry %d", dft_low_lat_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES + num_dft_rt_v6],
 						MAX_DEFAULT_v4_ROUTE_RULES + num_dft_rt_v6);
+				}
+
 			}
 		}
 		else
@@ -528,7 +617,7 @@
 		}
 
 		/* add default filtering rules when wan-iface get global v6-prefix */
-		if (num_dft_rt_v6 == 1)
+		if (!sec_addr && num_dft_rt_v6 == 1)
 		{
 			if(m_is_sta_mode == Q6_WAN)
 			{
@@ -619,12 +708,21 @@
 				}
 			}
 		}
-		/* store ipv6 prefix if the ipv6 address is not link local */
-		if(is_global_ipv6_addr(data->ipv6_addr))
+
+		if (sec_addr)
 		{
-			memcpy(ipv6_prefix, data->ipv6_addr, sizeof(ipv6_prefix));
+	    	sec_num_dft_rt_v6++;
 		}
-	    num_dft_rt_v6++;
+		else
+		{
+			/* store ipv6 prefix if the ipv6 address is not link local */
+			if (is_global_ipv6_addr(data->ipv6_addr))
+			{
+				memcpy(ipv6_prefix, data->ipv6_addr, sizeof(ipv6_prefix));
+				dft_rt_v6_glbl_idx = num_dft_rt_v6;
+			}
+			num_dft_rt_v6++;
+		}
     }
 	else
 	{
@@ -639,28 +737,19 @@
 			else
 			{
 				IPACMDBG_H(" device (%s) ipv4 addr is changed\n", dev_name);
-				/* Delete default Coalese v4 RT rule */
 				if (m_is_sta_mode == Q6_WAN) {
-					if (m_routing.DeleteRoutingHdl(dft_coalesce_rt_rule_hdl[0], IPA_IP_v4) == false)
-					{
-						IPACMERR("Routing old RSC TCP RT rule deletion failed!\n");
-						res = IPACM_FAILURE;
-						goto fail;
-					}
-					if (m_routing.DeleteRoutingHdl(dft_coalesce_rt_rule_hdl[1], IPA_IP_v4) == false)
-					{
-						IPACMERR("Routing old RSB UDP RT rule deletion failed!\n");
-						res = IPACM_FAILURE;
-						goto fail;
-					}
+					sec_addr = true;
 				}
-				/* 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)
+				else
 				{
-					IPACMERR("Routing old RT rule deletion failed!\n");
-					res = IPACM_FAILURE;
-					goto fail;
+					/* Delete default v4 RT rule */
+					IPACMDBG_H("Delete default v4 routing rules\n");
+					if (m_routing.DeleteRoutingHdl(dft_rt_rule_hdl[0], IPA_IP_v4) == false)
+					{
+						IPACMERR("Routing old RT rule deletion failed!\n");
+						res = IPACM_FAILURE;
+						goto fail;
+					}
 				}
 			}
 		}
@@ -716,9 +805,18 @@
 				IPACMERR("rt rule adding failed. Result=%d\n", rt_rule_entry->status);
 				res = rt_rule_entry->status;
 				goto fail;
-		}
-			dft_rt_rule_hdl[0] = rt_rule_entry->rt_rule_hdl;
-			IPACMDBG_H("ipv4 wan iface rt-rule hdll=0x%x\n", dft_rt_rule_hdl[0]);
+			}
+			if (sec_addr)
+			{
+				sec_dft_rt_rule_hdl[0] = rt_rule_entry->rt_rule_hdl;
+				IPACMDBG_H("Secondary ipv4 wan iface rt-rule hdll=0x%x\n",
+					sec_dft_rt_rule_hdl[0]);
+			}
+			else
+			{
+				dft_rt_rule_hdl[0] = rt_rule_entry->rt_rule_hdl;
+				IPACMDBG_H("ipv4 wan iface rt-rule hdll=0x%x\n", dft_rt_rule_hdl[0]);
+			}
 			/* RSC TCP rule*/
 			rt_rule_entry->rule.attrib.attrib_mask |= IPA_FLT_PROTOCOL;
 			rt_rule_entry->rule.attrib.u.v4.protocol = (uint8_t)IPACM_FIREWALL_IPPROTO_TCP;
@@ -740,10 +838,19 @@
 				IPACMERR("rsc tcp rt rule adding failed. Result=%d\n", rt_rule_entry->status);
 				res = rt_rule_entry->status;
 				goto fail;
-		}
-			dft_coalesce_rt_rule_hdl[0] = rt_rule_entry->rt_rule_hdl;
-			IPACMDBG_H("ipv4 wan iface rsc tcp rt-rule hdll=0x%x\n enable(%d)", dft_coalesce_rt_rule_hdl[0],
-				IPACM_Wan::coalesce_enable_info[ext_prop->ext[0].mux_id].coalesce_tcp_enable);
+			}
+			if (sec_addr)
+			{
+				sec_dft_coalesce_rt_rule_hdl[0] = rt_rule_entry->rt_rule_hdl;
+				IPACMDBG_H("Secondary ipv4 wan iface rsc tcp rt-rule hdll=0x%x\n enable(%d)", sec_dft_coalesce_rt_rule_hdl[0],
+					IPACM_Wan::coalesce_enable_info[ext_prop->ext[0].mux_id].coalesce_tcp_enable);
+			}
+			else
+			{
+				dft_coalesce_rt_rule_hdl[0] = rt_rule_entry->rt_rule_hdl;
+				IPACMDBG_H("ipv4 wan iface rsc tcp rt-rule hdll=0x%x\n enable(%d)", dft_coalesce_rt_rule_hdl[0],
+					IPACM_Wan::coalesce_enable_info[ext_prop->ext[0].mux_id].coalesce_tcp_enable);
+			}
 
 			/* RSB UDP rule*/
 			rt_rule_entry->rule.attrib.u.v4.protocol = (uint8_t)IPACM_FIREWALL_IPPROTO_UDP;
@@ -765,9 +872,18 @@
 				res = rt_rule_entry->status;
 				goto fail;
 			}
-			dft_coalesce_rt_rule_hdl[1] = rt_rule_entry->rt_rule_hdl;
-			IPACMDBG_H("ipv4 wan iface rsb udp rt-rule hdll=0x%x\n enable(%d)", dft_coalesce_rt_rule_hdl[1],
-				IPACM_Wan::coalesce_enable_info[ext_prop->ext[0].mux_id].coalesce_udp_enable);
+			if (sec_addr)
+			{
+				sec_dft_coalesce_rt_rule_hdl[1] = rt_rule_entry->rt_rule_hdl;
+				IPACMDBG_H("Secondary ipv4 wan iface rsb udp rt-rule hdll=0x%x\n enable(%d)", sec_dft_coalesce_rt_rule_hdl[1],
+					IPACM_Wan::coalesce_enable_info[ext_prop->ext[0].mux_id].coalesce_udp_enable);
+			}
+			else
+			{
+				dft_coalesce_rt_rule_hdl[1] = rt_rule_entry->rt_rule_hdl;
+				IPACMDBG_H("ipv4 wan iface rsb udp rt-rule hdll=0x%x\n enable(%d)", dft_coalesce_rt_rule_hdl[1],
+					IPACM_Wan::coalesce_enable_info[ext_prop->ext[0].mux_id].coalesce_udp_enable);
+			}
 
 			/* low latency v4 rule*/
 			fd_wwan_ioctl = open(IPA_DEVICE_NAME, O_RDWR);
@@ -798,9 +914,17 @@
 					res = rt_rule_entry->status;
 					goto fail;
 				}
+			if (sec_addr)
+			{
+				sec_dft_low_lat_rt_rule_hdl[0] = rt_rule_entry->rt_rule_hdl;
+				IPACMDBG_H("secondary ipv4 low lat udp rt-rule hdll=0x%x\n", sec_dft_low_lat_rt_rule_hdl[0]);
+			}
+			else
+			{
 				dft_low_lat_rt_rule_hdl[0] = rt_rule_entry->rt_rule_hdl;
 				IPACMDBG_H("ipv4 low lat udp rt-rule hdll=0x%x\n", dft_low_lat_rt_rule_hdl[0]);
 			}
+			}
 		}
 		else
 		{
@@ -845,13 +969,21 @@
 			}
 		}
 
-		wan_v4_addr = data->ipv4_addr;
-		wan_v4_addr_set = true;
+			if (m_is_sta_mode == Q6_WAN && sec_addr)
+			{
+				sec_wan_v4_addr = data->ipv4_addr;
+				sec_wan_v4_addr_set = true;
+			}
+			else
+			{
+				wan_v4_addr = data->ipv4_addr;
+				wan_v4_addr_set = true;
+			}
 
-		if (m_is_sta_mode == Q6_WAN)
+			if (m_is_sta_mode == Q6_WAN && !sec_addr)
 			curr_wan_ip = data->ipv4_addr;
 
-		IPACMDBG_H("Receved wan ipv4-addr:0x%x\n",wan_v4_addr);
+			IPACMDBG_H("Receved wan ipv4-addr:0x%x, sec_addr:%d\n",data->ipv4_addr, sec_addr);
 	}
 
 #ifdef FEATURE_IPACM_HAL
@@ -874,6 +1006,280 @@
 	return res;
 }
 
+	/* handle del_address event */
+	int IPACM_Wan::handle_addr_del_evt(ipacm_event_data_addr *data)
+	{
+		bool result;
+
+		const int NUM_RULES = 1;
+		uint32_t num_ipv6_addr, rt_idx = 0;
+		int res = IPACM_SUCCESS,len;
+#ifdef FEATURE_IPACM_HAL
+		IPACM_OffloadManager* OffloadMng;
+#endif
+		bool sec_addr = false, pri_addr = false;
+		int i = 0;
+		bool wan_active = false;
+
+		if(tx_prop == NULL || rx_prop == NULL)
+		{
+			IPACMDBG_H("Either tx or rx property is NULL, return.\n");
+			return IPACM_SUCCESS;
+		}
+
+		if (data->iptype == IPA_IP_v6)
+		{
+			/* Check if the address deleted is primary or secondary address. */
+
+			for(num_ipv6_addr=0;num_ipv6_addr<sec_num_dft_rt_v6;num_ipv6_addr++)
+			{
+				if((sec_ipv6_addr[num_ipv6_addr][0] == data->ipv6_addr[0]) &&
+					(sec_ipv6_addr[num_ipv6_addr][1] == data->ipv6_addr[1]) &&
+					(sec_ipv6_addr[num_ipv6_addr][2] == data->ipv6_addr[2]) &&
+					(sec_ipv6_addr[num_ipv6_addr][3] == data->ipv6_addr[3]))
+				{
+						IPACMDBG_H("find matched ipv6 address, index:%d \n", num_ipv6_addr);
+						sec_addr = true;
+				}
+			}
+
+			for(num_ipv6_addr=0;num_ipv6_addr<num_dft_rt_v6;num_ipv6_addr++)
+			{
+				if((ipv6_addr[num_ipv6_addr][0] == data->ipv6_addr[0]) &&
+					(ipv6_addr[num_ipv6_addr][1] == data->ipv6_addr[1]) &&
+					(ipv6_addr[num_ipv6_addr][2] == data->ipv6_addr[2]) &&
+					(ipv6_addr[num_ipv6_addr][3] == data->ipv6_addr[3]))
+				{
+					IPACMDBG_H("find matched ipv6 address, index:%d \n", num_ipv6_addr);
+					pri_addr = true;
+					rt_idx = num_ipv6_addr;
+				}
+			}
+
+			if (!sec_addr && !pri_addr)
+			{
+				IPACMDBG_H("Not matching Either Primary or Secondary addresses, return.\n");
+				return IPACM_SUCCESS;
+			}
+
+			if (sec_addr)
+			{
+				/* Clean up the rules. */
+				IPACMDBG_H("Delete Secondary v6 routing rules\n");
+				for (i = 0; i < 2; i++)
+				{
+					/* delete v6 colasce rules */
+					if (m_routing.DeleteRoutingHdl(sec_dft_coalesce_rt_rule_hdl[2*MAX_DEFAULT_v4_ROUTE_RULES+i], IPA_IP_v6) == false)
+					{
+						IPACMERR("Colasce Routing rule deletion failed!\n");
+						res = IPACM_FAILURE;
+						goto fail;
+					}
+					if (m_routing.DeleteRoutingHdl(sec_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;
+					}
+				}
+				if (m_routing.DeleteRoutingHdl(sec_dft_low_lat_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES], IPA_IP_v6) == false)
+				{
+					IPACMERR("Routing rule deletion failed!\n");
+					res = IPACM_FAILURE;
+					goto fail;
+				}
+				memset(sec_ipv6_addr, 0, sizeof(sec_ipv6_addr));
+				sec_num_dft_rt_v6 = 0;
+				return IPACM_SUCCESS;
+			}
+
+			if (pri_addr)
+			{
+				/* delete v6 colasce rules and copy secondary rules into Primary if applicable. */
+				IPACMDBG_H("Delete Primary v6 routing rules\n");
+				for (i = 0; i < MAX_DEFAULT_v6_ROUTE_RULES; i++)
+				{
+					if (m_routing.DeleteRoutingHdl(dft_coalesce_rt_rule_hdl[2*MAX_DEFAULT_v4_ROUTE_RULES + 2*rt_idx+i], IPA_IP_v6) == false)
+					{
+						IPACMERR("Colasce Routing rule deletion failed!\n");
+						res = IPACM_FAILURE;
+						goto fail;
+					}
+					if (is_global_ipv6_addr(data->ipv6_addr) && sec_num_dft_rt_v6 > 0)
+					{
+						dft_coalesce_rt_rule_hdl[2*MAX_DEFAULT_v4_ROUTE_RULES + 2*rt_idx+i] =
+							sec_dft_coalesce_rt_rule_hdl[2*MAX_DEFAULT_v4_ROUTE_RULES+i];
+						sec_dft_coalesce_rt_rule_hdl[2*MAX_DEFAULT_v4_ROUTE_RULES+i] = 0;
+						IPACMDBG_H("Coalescing routing rule update, index: %d/%d, hdl:%d\n",
+							rt_idx, 2*rt_idx+i,
+							dft_coalesce_rt_rule_hdl[2*MAX_DEFAULT_v4_ROUTE_RULES + 2*rt_idx+i]);
+					}
+					if (m_routing.DeleteRoutingHdl(dft_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES+2*rt_idx+i], IPA_IP_v6) == false)
+					{
+						IPACMERR("Routing rule deletion failed!\n");
+						res = IPACM_FAILURE;
+						goto fail;
+					}
+					if (is_global_ipv6_addr(data->ipv6_addr) && sec_num_dft_rt_v6 > 0)
+					{
+						dft_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES+2*rt_idx+i] =
+							sec_dft_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES + i];
+						sec_dft_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES + i] = 0;
+						IPACMDBG_H("Default routing rule update, index: %d/%d, hdl:%d\n",
+							rt_idx, 2*rt_idx+i,
+							dft_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES+2*rt_idx+i]);
+					}
+				}
+
+				/* Delete low lat rules. */
+				if (m_routing.DeleteRoutingHdl(dft_low_lat_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES+rt_idx], IPA_IP_v6) == false)
+				{
+					IPACMERR("Routing rule deletion failed!\n");
+					res = IPACM_FAILURE;
+					goto fail;
+				}
+				/* Copy secondary rules into Primary. */
+				if (is_global_ipv6_addr(data->ipv6_addr) && sec_num_dft_rt_v6 > 0)
+				{
+					dft_low_lat_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES+rt_idx] =
+						sec_dft_low_lat_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES];
+				}
+
+				if (is_global_ipv6_addr(data->ipv6_addr) && sec_num_dft_rt_v6 > 0)
+				{
+					if (active_v6)
+					{
+						post_wan_down_tether_evt(IPA_IP_v6, 0);
+						del_wan_firewall_rule(IPA_IP_v6);
+						handle_route_del_evt_ex(IPA_IP_v6);
+						wan_active = true;
+					}
+					/* Copy Secondary Address onto Primary. */
+					ipv6_addr[rt_idx][0] = sec_ipv6_addr[sec_num_dft_rt_v6-1][0];
+					ipv6_addr[rt_idx][1] = sec_ipv6_addr[sec_num_dft_rt_v6-1][1];
+					ipv6_addr[rt_idx][2] = sec_ipv6_addr[sec_num_dft_rt_v6-1][2];
+					ipv6_addr[rt_idx][3] = sec_ipv6_addr[sec_num_dft_rt_v6-1][3];
+					memcpy(ipv6_prefix, sec_ipv6_addr[sec_num_dft_rt_v6-1], sizeof(ipv6_prefix));
+					memset(sec_ipv6_addr[sec_num_dft_rt_v6-1], 0, sizeof(sec_ipv6_addr[sec_num_dft_rt_v6-1]));
+					sec_num_dft_rt_v6--;
+					IPACMDBG_H("Secondary v6 num %d: \n",sec_num_dft_rt_v6);
+					if (wan_active)
+					{
+						handle_route_add_evt(IPA_IP_v6);
+					}
+				}
+				else
+				{
+					num_dft_rt_v6--;
+					IPACMDBG_H("v6 num %d: \n",num_dft_rt_v6);
+				}
+			}
+		}
+		else
+		{
+			if (data->ipv4_addr == sec_wan_v4_addr)
+			{
+				IPACMDBG_H("Delete Secondary coalescing v4 routing rules\n");
+				if (m_routing.DeleteRoutingHdl(sec_dft_coalesce_rt_rule_hdl[0], IPA_IP_v4) == false)
+				{
+					IPACMERR("Routing old RSC TCP RT rule deletion failed!\n");
+					res = IPACM_FAILURE;
+					goto fail;
+				}
+				sec_dft_coalesce_rt_rule_hdl[0] = 0;
+				if (m_routing.DeleteRoutingHdl(sec_dft_coalesce_rt_rule_hdl[1], IPA_IP_v4) == false)
+				{
+					IPACMERR("Routing old RSB UDP RT rule deletion failed!\n");
+					res = IPACM_FAILURE;
+					goto fail;
+				}
+				sec_dft_coalesce_rt_rule_hdl[1] = 0;
+				IPACMDBG_H("Delete Secondary v4 default routing rules\n");
+				if (m_routing.DeleteRoutingHdl(sec_dft_rt_rule_hdl[0], IPA_IP_v4) == false)
+				{
+					IPACMERR("Routing old RT rule deletion failed!\n");
+					res = IPACM_FAILURE;
+					goto fail;
+				}
+				sec_dft_rt_rule_hdl[0] = 0;
+				if (m_routing.DeleteRoutingHdl(sec_dft_low_lat_rt_rule_hdl[0], IPA_IP_v4) == false)
+				{
+					IPACMERR("Secondary Routing rule low lat deletion failed!\n");
+					res = IPACM_FAILURE;
+					goto fail;
+				}
+				sec_dft_low_lat_rt_rule_hdl[0] = 0;
+				sec_wan_v4_addr_set = false;
+				sec_wan_v4_addr = 0;
+				return IPACM_SUCCESS;
+			}
+			else if (data->ipv4_addr == wan_v4_addr)
+			{
+
+				IPACMDBG_H("Delete default coalescing v4 routing rules\n");
+				if (m_routing.DeleteRoutingHdl(dft_coalesce_rt_rule_hdl[0], IPA_IP_v4) == false)
+				{
+					IPACMERR("Routing old RSC TCP RT rule deletion failed!\n");
+					res = IPACM_FAILURE;
+					goto fail;
+				}
+				dft_coalesce_rt_rule_hdl[0] = sec_dft_coalesce_rt_rule_hdl[0];
+				sec_dft_coalesce_rt_rule_hdl[0] = 0;
+				if (m_routing.DeleteRoutingHdl(dft_coalesce_rt_rule_hdl[1], IPA_IP_v4) == false)
+				{
+					IPACMERR("Routing old RSB UDP RT rule deletion failed!\n");
+					res = IPACM_FAILURE;
+					goto fail;
+				}
+				dft_coalesce_rt_rule_hdl[1] = sec_dft_coalesce_rt_rule_hdl[1];
+				sec_dft_coalesce_rt_rule_hdl[1] = 0;
+				IPACMDBG_H("New coalescing routing rule hdls:%d:%d\n",
+					dft_coalesce_rt_rule_hdl[0],dft_coalesce_rt_rule_hdl[1]);
+				IPACMDBG_H("Delete default v4 routing rules\n");
+				if (m_routing.DeleteRoutingHdl(dft_rt_rule_hdl[0], IPA_IP_v4) == false)
+				{
+					IPACMERR("Routing old RT rule deletion failed!\n");
+					res = IPACM_FAILURE;
+					goto fail;
+				}
+				dft_rt_rule_hdl[0] = sec_dft_rt_rule_hdl[0];
+				IPACMDBG_H("New default rule routing rule hdl:%d\n",
+					dft_rt_rule_hdl[0]);
+				if (m_routing.DeleteRoutingHdl(dft_low_lat_rt_rule_hdl[0], IPA_IP_v4) == false)
+				{
+					IPACMERR("Routing old RT rule deletion failed!\n");
+					res = IPACM_FAILURE;
+					goto fail;
+				}
+				dft_low_lat_rt_rule_hdl[0] = sec_dft_low_lat_rt_rule_hdl[0];
+				IPACMDBG_H("New default rule low lat routing rule hdl:%d\n",
+					sec_dft_low_lat_rt_rule_hdl[0]);
+
+				/* Post WAN_DOWN with old ip address. */
+				if (active_v4)
+				{
+					post_wan_down_tether_evt(IPA_IP_v4, 0);
+					del_wan_firewall_rule(IPA_IP_v4);
+					handle_route_del_evt_ex(IPA_IP_v4);
+					wan_active = true;
+				}
+				wan_v4_addr = sec_wan_v4_addr;
+				IPACMDBG_H("WAN IPv4 address:0x%x\n",
+					wan_v4_addr);
+				sec_wan_v4_addr_set = false;
+				sec_wan_v4_addr = 0;
+				/* Post WAN_UP with new ip address. */
+				if (wan_active)
+				{
+					handle_route_add_evt(IPA_IP_v4);
+				}
+			}
+		}
+fail:
+		return res;
+	}
+
+
 /* handle new_address event */
 int IPACM_Wan::handle_addr_evt_mhi_q6(ipacm_event_data_addr *data)
 {
@@ -1305,9 +1711,11 @@
 			if (ipa_interface_index == ipa_if_num)
 			{
 				IPACMDBG_H("Get IPA_ADDR_ADD_EVENT: IF ip type %d, incoming ip type %d\n", ip_type, data->iptype);
-				/* check v4 not setup before, v6 can have 2 iface ip */
+					/* check v4 not setup before, v6 can have 2 iface ip or secondary ipv6 address. */
 				if( (data->iptype == IPA_IP_v4)
-				    || ((data->iptype==IPA_IP_v6) && (num_dft_rt_v6!=MAX_DEFAULT_v6_ROUTE_RULES)))
+					    || ((data->iptype==IPA_IP_v6) && (num_dft_rt_v6!=MAX_DEFAULT_v6_ROUTE_RULES))
+					    || ((data->iptype==IPA_IP_v6) && is_global_ipv6_addr(data->ipv6_addr)
+					    && (m_is_sta_mode == Q6_WAN) && sec_num_dft_rt_v6 != MAX_DEFAULT_SEC_v6_ROUTE_RULES) )
 				{
 					if (m_is_sta_mode == Q6_MHI_WAN)
 					{
@@ -1335,9 +1743,31 @@
 							handle_software_routing_enable(false);
 						}
 					}
-
+					}
 				}
 			}
+			break;
+
+		case IPA_ADDR_DEL_EVENT:
+			{
+				ipacm_event_data_addr *data = (ipacm_event_data_addr *)param;
+				ipa_interface_index = iface_ipa_index_query(data->if_index);
+
+				if ( (data->iptype == IPA_IP_v4 && data->ipv4_addr == 0) ||
+						 (data->iptype == IPA_IP_v6 &&
+							data->ipv6_addr[0] == 0 && data->ipv6_addr[1] == 0 &&
+						  data->ipv6_addr[2] == 0 && data->ipv6_addr[3] == 0) )
+				{
+					IPACMDBG_H("Invalid address, ignore IPA_ADDR_DEL_EVENT event\n");
+					return;
+				}
+
+				if (ipa_interface_index == ipa_if_num)
+				{
+					IPACMDBG_H("Get IPA_ADDR_DEL_EVENT: IF ip type %d, incoming ip type %d\n", ip_type, data->iptype);
+					IPACMDBG_H("v6 num %d: \n",num_dft_rt_v6);
+					handle_addr_del_evt(data);
+			}
 		}
 		break;
 
@@ -5891,6 +6321,41 @@
 			res = IPACM_FAILURE;
 			goto fail;
 		}
+
+		if (sec_wan_v4_addr_set)
+		{
+			IPACMDBG_H("Delete Secondary v4 coalesce routing rules\n");
+			if (m_routing.DeleteRoutingHdl(sec_dft_coalesce_rt_rule_hdl[0], IPA_IP_v4) == false)
+			{
+				IPACMERR("Secondary Routing rule RSC TCP deletion failed!\n");
+				res = IPACM_FAILURE;
+				goto fail;
+			}
+
+			if (m_routing.DeleteRoutingHdl(sec_dft_coalesce_rt_rule_hdl[1], IPA_IP_v4) == false)
+			{
+				IPACMERR("Secondary Routing rule RSB UDP deletion failed!\n");
+				res = IPACM_FAILURE;
+				goto fail;
+			}
+
+			if (m_routing.DeleteRoutingHdl(sec_dft_rt_rule_hdl[0], IPA_IP_v4) == false)
+			{
+				IPACMERR("Secondary Routing rule deletion failed!\n");
+				res = IPACM_FAILURE;
+				goto fail;
+			}
+
+			if (m_routing.DeleteRoutingHdl(sec_dft_low_lat_rt_rule_hdl[0], IPA_IP_v4) == false)
+			{
+				IPACMERR("Secondary Routing rule low lat deletion failed!\n");
+				res = IPACM_FAILURE;
+				goto fail;
+			}
+
+			sec_wan_v4_addr_set = false;
+			sec_wan_v4_addr = 0;
+		}
 	}
 	else if(ip_type == IPA_IP_v6)
 	{
@@ -5957,6 +6422,7 @@
 				goto fail;
 			}
 		}
+
 		/* Delete v6 low lat rules */
 		for (i = 0; i < num_dft_rt_v6; i++)
 		{
@@ -5967,6 +6433,36 @@
 				goto fail;
 			}
 		}
+
+		if (sec_num_dft_rt_v6)
+		{
+			/* Clean up the rules. */
+			IPACMDBG_H("Delete Secondary v6 routing rules\n");
+			for (i = 0; i < 2; i++)
+			{
+				/* delete v6 colasce rules */
+				if (m_routing.DeleteRoutingHdl(sec_dft_coalesce_rt_rule_hdl[2*MAX_DEFAULT_v4_ROUTE_RULES+i], IPA_IP_v6) == false)
+				{
+					IPACMERR("Colasce Routing rule deletion failed!\n");
+					res = IPACM_FAILURE;
+					goto fail;
+				}
+				if (m_routing.DeleteRoutingHdl(sec_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;
+				}
+			}
+			if (m_routing.DeleteRoutingHdl(sec_dft_low_lat_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES], IPA_IP_v6) == false)
+			{
+				IPACMERR("Routing rule deletion failed!\n");
+				res = IPACM_FAILURE;
+				goto fail;
+			}
+			memset(sec_ipv6_addr, 0, sizeof(sec_ipv6_addr));
+			sec_num_dft_rt_v6 = 0;
+		}
 	}
 	else
 	{
@@ -6073,6 +6569,41 @@
 			goto fail;
 		}
 
+		if (sec_wan_v4_addr_set)
+		{
+			IPACMDBG_H("Delete Secondary v4 coalesce routing rules\n");
+			if (m_routing.DeleteRoutingHdl(sec_dft_coalesce_rt_rule_hdl[0], IPA_IP_v4) == false)
+			{
+				IPACMERR("Secondary Routing rule RSC TCP deletion failed!\n");
+				res = IPACM_FAILURE;
+				goto fail;
+			}
+
+			if (m_routing.DeleteRoutingHdl(sec_dft_coalesce_rt_rule_hdl[1], IPA_IP_v4) == false)
+			{
+				IPACMERR("Secondary Routing rule RSB UDP deletion failed!\n");
+				res = IPACM_FAILURE;
+				goto fail;
+			}
+
+			if (m_routing.DeleteRoutingHdl(sec_dft_rt_rule_hdl[0], IPA_IP_v4) == false)
+			{
+				IPACMERR("Secondary Routing rule deletion failed!\n");
+				res = IPACM_FAILURE;
+				goto fail;
+			}
+
+			if (m_routing.DeleteRoutingHdl(sec_dft_low_lat_rt_rule_hdl[0], IPA_IP_v4) == false)
+			{
+				IPACMERR("Routing rule deletion failed!\n");
+				res = IPACM_FAILURE;
+				goto fail;
+			}
+
+			sec_wan_v4_addr_set = false;
+			sec_wan_v4_addr = 0;
+		}
+
 		for (i = 0; i < 2*num_dft_rt_v6; i++)
 		{
 			/* delete v6 colasce rules */
@@ -6089,6 +6620,7 @@
 				goto fail;
 			}
 		}
+
 		for (i = 0; i < num_dft_rt_v6; i++)
 		{
 			if (m_routing.DeleteRoutingHdl(dft_low_lat_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES+i], IPA_IP_v6) == false)
@@ -6098,6 +6630,36 @@
 				goto fail;
 			}
 		}
+
+		if (sec_num_dft_rt_v6)
+		{
+			/* Clean up the rules. */
+			IPACMDBG_H("Delete Secondary v6 routing rules\n");
+			for (i = 0; i < 2; i++)
+			{
+				/* delete v6 colasce rules */
+				if (m_routing.DeleteRoutingHdl(sec_dft_coalesce_rt_rule_hdl[2*MAX_DEFAULT_v4_ROUTE_RULES+i], IPA_IP_v6) == false)
+				{
+					IPACMERR("Colasce Routing rule deletion failed!\n");
+					res = IPACM_FAILURE;
+					goto fail;
+				}
+				if (m_routing.DeleteRoutingHdl(sec_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;
+				}
+			}
+			if (m_routing.DeleteRoutingHdl(sec_dft_low_lat_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES], IPA_IP_v6) == false)
+			{
+				IPACMERR("Routing rule deletion failed!\n");
+				res = IPACM_FAILURE;
+				goto fail;
+			}
+			memset(sec_ipv6_addr, 0, sizeof(sec_ipv6_addr));
+			sec_num_dft_rt_v6 = 0;
+		}
 	}
 
 //	/* check software routing fl rule hdl */
@@ -7642,6 +8204,136 @@
 fail:
 	free(rt_rule);
 	}
+
+	if(sec_wan_v4_addr_set)
+	{
+		/* Delete secondary default RSC v4 RT rule */
+		if (m_routing.DeleteRoutingHdl(sec_dft_coalesce_rt_rule_hdl[0], IPA_IP_v4) == false)
+		{
+			IPACMERR("Sec Routing old RSC TCP RT rule deletion failed!\n");
+			return	IPACM_FAILURE;
+		}
+		if (m_routing.DeleteRoutingHdl(sec_dft_coalesce_rt_rule_hdl[1], IPA_IP_v4) == false)
+		{
+			IPACMERR("Sec Routing old RSB UDP RT rule deletion failed!\n");
+			return	IPACM_FAILURE;
+		}
+		/* Delete secondary default v4 RT rule */
+		IPACMDBG_H("Delete Secondary default v4 routing rules\n");
+		if (m_routing.DeleteRoutingHdl(sec_dft_rt_rule_hdl[0], IPA_IP_v4) == false)
+		{
+			IPACMERR("Routing old RT rule deletion failed!\n");
+			return	IPACM_FAILURE;
+		}
+
+		/* apply the new coalesce configuration for secondary address. */
+		rt_rule = (struct ipa_ioc_add_rt_rule *)
+			 calloc(1, sizeof(struct ipa_ioc_add_rt_rule) +
+							NUM_RULES * sizeof(struct ipa_rt_rule_add));
+		if (!rt_rule)
+		{
+			IPACMERR("Error Locate ipa_ioc_add_rt_rule memory...\n");
+			return IPACM_FAILURE;
+		}
+		rt_rule->commit = 1;
+		rt_rule->num_rules = NUM_RULES;
+		rt_rule->ip = IPA_IP_v4;
+		rt_rule_entry = &rt_rule->rules[0];
+		rt_rule_entry->at_rear = true;
+		rt_rule_entry->rule.attrib.attrib_mask = IPA_FLT_DST_ADDR;
+		/* still need setup v4 default routing rule to APPs*/
+		strlcpy(rt_rule->rt_tbl_name, IPACM_Iface::ipacmcfg->rt_tbl_lan_v4.name, sizeof(rt_rule->rt_tbl_name));
+		rt_rule_entry->rule.attrib.u.v4.dst_addr = sec_wan_v4_addr;
+		rt_rule_entry->rule.attrib.u.v4.dst_addr_mask = 0xFFFFFFFF;
+		if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+			rt_rule_entry->rule.hashable = false;
+		/* query qmap header*/
+		memset(&hdr, 0, sizeof(hdr));
+		strlcpy(hdr.name, tx_prop->tx[0].hdr_name, sizeof(hdr.name));
+		hdr.name[IPA_RESOURCE_NAME_MAX-1] = '\0';
+		if(m_header.GetHeaderHandle(&hdr) == false)
+		{
+			IPACMERR("Failed to get QMAP header.\n");
+			res = IPACM_FAILURE;
+			goto fail1;
+		}
+		rt_rule_entry->rule.hdr_hdl = hdr.hdl;
+		rt_rule_entry->rule.dst = IPA_CLIENT_APPS_WAN_CONS;
+
+		/* RSB UDP rule*/
+		rt_rule_entry->rule.attrib.attrib_mask |= IPA_FLT_PROTOCOL;
+		rt_rule_entry->rule.attrib.u.v4.protocol = (uint8_t)IPACM_FIREWALL_IPPROTO_UDP;
+#ifdef IPA_RT_SUPPORT_COAL
+		if (IPACM_Wan::coalesce_enable_info[ext_prop->ext[0].mux_id].coalesce_tcp_enable)
+			rt_rule_entry->rule.coalesce = true;
+		else
+			rt_rule_entry->rule.coalesce = false;
+#endif
+		if (false == m_routing.AddRoutingRule(rt_rule))
+		{
+			IPACMERR("Routing rule addition failed!\n");
+			res = IPACM_FAILURE;
+			goto fail1;
+		}
+		else if (rt_rule_entry->status)
+		{
+			IPACMERR("secondary rsb udp rt rule adding failed. Result=%d\n", rt_rule_entry->status);
+			res = rt_rule_entry->status;
+			goto fail1;
+		}
+		sec_dft_coalesce_rt_rule_hdl[1] = rt_rule_entry->rt_rule_hdl;
+		IPACMDBG_H("secondary ipv4 wan iface rsb udp rt-rule hdll=0x%x enable(%d)\n", sec_dft_coalesce_rt_rule_hdl[1],
+			IPACM_Wan::coalesce_enable_info[ext_prop->ext[0].mux_id].coalesce_udp_enable);
+
+		/* RSC TCP rule*/
+		rt_rule_entry->rule.attrib.u.v4.protocol = (uint8_t)IPACM_FIREWALL_IPPROTO_TCP;
+#ifdef IPA_RT_SUPPORT_COAL
+		if (IPACM_Wan::coalesce_enable_info[ext_prop->ext[0].mux_id].coalesce_tcp_enable)
+			rt_rule_entry->rule.coalesce = true;
+		else
+			rt_rule_entry->rule.coalesce = false;
+#endif
+		if (false == m_routing.AddRoutingRule(rt_rule))
+		{
+			IPACMERR("Routing rule addition failed!\n");
+			res = IPACM_FAILURE;
+			goto fail1;
+		}
+		else if (rt_rule_entry->status)
+		{
+			IPACMERR("secondary rsc tcp rt rule adding failed. Result=%d\n", rt_rule_entry->status);
+			res = rt_rule_entry->status;
+			goto fail1;
+		}
+		sec_dft_coalesce_rt_rule_hdl[0] = rt_rule_entry->rt_rule_hdl;
+		IPACMDBG_H("secondary ipv4 wan iface rsc tcp rt-rule hdll=0x%x\n enable(%d)", sec_dft_coalesce_rt_rule_hdl[0],
+			IPACM_Wan::coalesce_enable_info[ext_prop->ext[0].mux_id].coalesce_tcp_enable);
+
+		/*	secondary default v4 rt-rule */
+		rt_rule_entry->rule.attrib.attrib_mask &= ~IPA_FLT_PROTOCOL;
+#ifdef IPA_RT_SUPPORT_COAL
+			rt_rule_entry->rule.coalesce = false;
+#endif
+		/* Secondary default v4 rt-rule */
+		if (false == m_routing.AddRoutingRule(rt_rule))
+		{
+			IPACMERR("Routing rule addition failed!\n");
+			res = IPACM_FAILURE;
+			goto fail1;
+		}
+		else if (rt_rule_entry->status)
+		{
+			IPACMERR("secondary rt rule adding failed. Result=%d\n", rt_rule_entry->status);
+			res = rt_rule_entry->status;
+			goto fail1;
+		}
+		sec_dft_rt_rule_hdl[0] = rt_rule_entry->rt_rule_hdl;
+		IPACMDBG_H("secondary ipv4 wan iface rt-rule hdll=0x%x\n", sec_dft_rt_rule_hdl[0]);
+
+fail1:
+	free(rt_rule);
+	}
+
 	/* v6 */
 	if (num_dft_rt_v6 !=0)
 	{
@@ -7793,6 +8485,159 @@
 fail2:
 	free(rt_rule);
 	}
+
+	/* Secondary v6 */
+	if (sec_num_dft_rt_v6 !=0)
+	{
+		for (i = 0; i < 2*sec_num_dft_rt_v6; i++)
+		{
+			/* delete secondary v6 colasce rules */
+			if (m_routing.DeleteRoutingHdl(sec_dft_coalesce_rt_rule_hdl[2*MAX_DEFAULT_v4_ROUTE_RULES+i], IPA_IP_v6) == false)
+			{
+				IPACMERR("Colasce Routing rule deletion failed!\n");
+				return	IPACM_FAILURE;
+			}
+			/* delete secondary v6 default rules */
+			if (m_routing.DeleteRoutingHdl(sec_dft_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES+i], IPA_IP_v6) == false)
+			{
+				IPACMERR("Routing rule deletion failed!\n");
+				return	IPACM_FAILURE;
+			}
+		}
+
+		rt_rule = (struct ipa_ioc_add_rt_rule *)
+			calloc(1, sizeof(struct ipa_ioc_add_rt_rule) +
+				NUM_RULES * sizeof(struct ipa_rt_rule_add));
+		if (!rt_rule)
+		{
+			IPACMERR("Error Locate ipa_ioc_add_rt_rule memory...\n");
+			return IPACM_FAILURE;
+		}
+		rt_rule->commit = 1;
+		rt_rule->num_rules = NUM_RULES;
+		rt_rule->ip = IPA_IP_v6;
+
+		for (i = 0; i < sec_num_dft_rt_v6; i++)
+		{
+			/* setup same rule for v6_wan table */
+			strlcpy(rt_rule->rt_tbl_name, IPACM_Iface::ipacmcfg->rt_tbl_v6.name, sizeof(rt_rule->rt_tbl_name));
+			rt_rule_entry = &rt_rule->rules[0];
+			rt_rule_entry->at_rear = true;
+			rt_rule_entry->rule.attrib.attrib_mask = IPA_FLT_DST_ADDR;
+			rt_rule_entry->rule.attrib.u.v6.dst_addr[0] = sec_ipv6_addr[i][0];
+			rt_rule_entry->rule.attrib.u.v6.dst_addr[1] = sec_ipv6_addr[i][1];
+			rt_rule_entry->rule.attrib.u.v6.dst_addr[2] = sec_ipv6_addr[i][2];
+			rt_rule_entry->rule.attrib.u.v6.dst_addr[3] = sec_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;
+			rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[3] = 0xFFFFFFFF;
+			if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+				rt_rule_entry->rule.hashable = false;
+			strlcpy(hdr.name, tx_prop->tx[0].hdr_name, sizeof(hdr.name));
+			hdr.name[IPA_RESOURCE_NAME_MAX-1] = '\0';
+			if(m_header.GetHeaderHandle(&hdr) == false)
+			{
+				IPACMERR("Failed to get QMAP header.\n");
+				return IPACM_FAILURE;
+			}
+			rt_rule_entry->rule.hdr_hdl = hdr.hdl;
+			rt_rule_entry->rule.dst = IPA_CLIENT_APPS_WAN_CONS;
+
+			/* Secondary RSB UDP rule*/
+			rt_rule_entry->rule.attrib.attrib_mask |= IPA_FLT_NEXT_HDR;
+			rt_rule_entry->rule.attrib.u.v6.next_hdr = (uint8_t)IPACM_FIREWALL_IPPROTO_UDP;
+#ifdef IPA_RT_SUPPORT_COAL
+			if (IPACM_Wan::coalesce_enable_info[ext_prop->ext[0].mux_id].coalesce_udp_enable)
+				rt_rule_entry->rule.coalesce = true;
+			else
+				rt_rule_entry->rule.coalesce = false;
+#endif
+			if (false == m_routing.AddRoutingRule(rt_rule))
+			{
+				IPACMERR("Routing rule addition failed!\n");
+				res = IPACM_FAILURE;
+				goto fail3;
+			}
+			else if (rt_rule_entry->status)
+			{
+				IPACMERR("secondary rsb udp rt rule adding failed. Result=%d\n", rt_rule_entry->status);
+				res = rt_rule_entry->status;
+				goto fail3;
+			}
+			sec_dft_coalesce_rt_rule_hdl[2*MAX_DEFAULT_v4_ROUTE_RULES + 2*i+1] = rt_rule_entry->rt_rule_hdl;
+			IPACMDBG_H("secondary ipv6 wan iface rsb udp rt-rule hdll=0x%x\n enable(%d)", sec_dft_coalesce_rt_rule_hdl[2*MAX_DEFAULT_v4_ROUTE_RULES + 2*i+1],
+				IPACM_Wan::coalesce_enable_info[ext_prop->ext[0].mux_id].coalesce_udp_enable);
+
+			/* Secondary RSC TCP rule*/
+			rt_rule_entry->rule.attrib.u.v6.next_hdr = (uint8_t)IPACM_FIREWALL_IPPROTO_TCP;
+#ifdef IPA_RT_SUPPORT_COAL
+			if (IPACM_Wan::coalesce_enable_info[ext_prop->ext[0].mux_id].coalesce_tcp_enable)
+				rt_rule_entry->rule.coalesce = true;
+			else
+				rt_rule_entry->rule.coalesce = false;
+#endif
+			if (false == m_routing.AddRoutingRule(rt_rule))
+			{
+				IPACMERR("Routing rule addition failed!\n");
+				res = IPACM_FAILURE;
+				goto fail3;
+			}
+			else if (rt_rule_entry->status)
+			{
+				IPACMERR("secondary rsc tcp rt rule adding failed. Result=%d\n", rt_rule_entry->status);
+				res = rt_rule_entry->status;
+				goto fail3;
+			}
+			sec_dft_coalesce_rt_rule_hdl[2*MAX_DEFAULT_v4_ROUTE_RULES + 2*i] = rt_rule_entry->rt_rule_hdl;
+			IPACMDBG_H("secondary ipv6 wan iface rsc tcp rt-rule hdll=0x%x\n enable(%d)", sec_dft_coalesce_rt_rule_hdl[2*MAX_DEFAULT_v4_ROUTE_RULES + 2*i],
+				IPACM_Wan::coalesce_enable_info[ext_prop->ext[0].mux_id].coalesce_tcp_enable);
+
+			/* legacy default secondary v6 rt-rule */
+			rt_rule_entry->rule.attrib.attrib_mask &= ~IPA_FLT_NEXT_HDR;
+#ifdef IPA_RT_SUPPORT_COAL
+			rt_rule_entry->rule.coalesce = false;
+#endif
+			/* legacy default secondary v6 rt-rule */
+			if (false == m_routing.AddRoutingRule(rt_rule))
+			{
+				IPACMERR("Routing rule addition failed!\n");
+				res = IPACM_FAILURE;
+				goto fail3;
+			}
+			else if (rt_rule_entry->status)
+			{
+				IPACMERR("secondary rt rule adding failed. Result=%d\n", rt_rule_entry->status);
+				res = rt_rule_entry->status;
+				goto fail3;
+			}
+			sec_dft_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES + 2*i] = rt_rule_entry->rt_rule_hdl;
+
+			/* setup same rule for v6_lan table*/
+			strlcpy(rt_rule->rt_tbl_name, IPACM_Iface::ipacmcfg->rt_tbl_wan_v6.name, sizeof(rt_rule->rt_tbl_name));
+			if (false == m_routing.AddRoutingRule(rt_rule))
+			{
+				IPACMERR("Routing rule addition failed!\n");
+				res = IPACM_FAILURE;
+				goto fail3;
+			}
+			else if (rt_rule_entry->status)
+			{
+				IPACMERR("rt rule adding failed. Result=%d\n", rt_rule_entry->status);
+				res = rt_rule_entry->status;
+				goto fail3;
+			}
+			sec_dft_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES + 2*i+1] = rt_rule_entry->rt_rule_hdl;
+			IPACMDBG_H("secondary ipv6 wan iface rt-rule hdl=0x%x hdl=0x%x, entry: %d %d\n",
+				sec_dft_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES + 2*i],
+				sec_dft_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES + 2*i+1],
+				MAX_DEFAULT_v4_ROUTE_RULES + 2*i,
+				MAX_DEFAULT_v4_ROUTE_RULES + 2*i+1);
+		}
+fail3:
+	free(rt_rule);
+	}
+
 	return res;
 }
 
diff --git a/ipanat/inc/ipa_nat_drv.h b/ipanat/inc/ipa_nat_drv.h
index 9be97f3..739230a 100644
--- a/ipanat/inc/ipa_nat_drv.h
+++ b/ipanat/inc/ipa_nat_drv.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2020 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2021 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
@@ -246,8 +246,16 @@
 int ipa_nat_vote_clock(
 	enum ipa_app_clock_vote_type vote_type );
 
+/**
+ * ipa_nat_switch_to() - While in HYBRID mode only, used for switching
+ * from SRAM to DDR or the reverse.
+ * @nmi: memory type to switch to
+ * @hold_state: Will the new memory type get locked in (ie. no more
+ *              oscilation between the memory types)
+ */
 int ipa_nat_switch_to(
-	enum ipa3_nat_mem_in nmi );
+	enum ipa3_nat_mem_in nmi,
+	bool                 hold_state );
 
 #endif
 
diff --git a/ipanat/inc/ipa_nat_statemach.h b/ipanat/inc/ipa_nat_statemach.h
index a86dd87..94b86ed 100644
--- a/ipanat/inc/ipa_nat_statemach.h
+++ b/ipanat/inc/ipa_nat_statemach.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2019-2021 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
@@ -29,6 +29,8 @@
 #if !defined(_IPA_NAT_STATEMACH_H_)
 # define _IPA_NAT_STATEMACH_H_
 
+#define MAKE_AS_STR_CASE(v) case v: return #v
+
 /******************************************************************************/
 /**
  * The following enum represents the states that a nati object can be
@@ -44,6 +46,26 @@
 	NATI_STATE_LAST
 } ipa_nati_state;
 
+/* KEEP THE FOLLOWING IN SYNC WITH ABOVE. */
+static inline const char* ipa_nati_state_as_str(
+	ipa_nati_state s )
+{
+	switch ( s )
+	{
+		MAKE_AS_STR_CASE(NATI_STATE_NULL);
+		MAKE_AS_STR_CASE(NATI_STATE_DDR_ONLY);
+		MAKE_AS_STR_CASE(NATI_STATE_SRAM_ONLY);
+		MAKE_AS_STR_CASE(NATI_STATE_HYBRID);
+		MAKE_AS_STR_CASE(NATI_STATE_HYBRID_DDR);
+		MAKE_AS_STR_CASE(NATI_STATE_LAST);
+
+	default:
+		break;
+	}
+
+	return "???";
+}
+
 # undef strcasesame
 # define strcasesame(a, b) (!strcasecmp(a, b))
 
@@ -114,6 +136,8 @@
 {
 	ipa_nati_state prev_state;
 	ipa_nati_state curr_state;
+	bool           hold_state;
+	ipa_nati_state state_to_hold;
 	uint32_t       ddr_tbl_hdl;
 	uint32_t       sram_tbl_hdl;
 	uint32_t       tot_slots_in_sram;
@@ -144,11 +168,32 @@
 #define DDR_SUB  0
 #define SRAM_SUB 1
 
+#undef BACK2_UNSTARTED_STATE
+#define BACK2_UNSTARTED_STATE() \
+	nati_obj.prev_state = nati_obj.curr_state = NATI_STATE_NULL;
+
+#undef IN_UNSTARTED_STATE
+#define IN_UNSTARTED_STATE() \
+	( nati_obj.prev_state == NATI_STATE_NULL )
+
 #undef IN_HYBRID_STATE
 #define IN_HYBRID_STATE() \
 	( nati_obj.curr_state == NATI_STATE_HYBRID || \
 	  nati_obj.curr_state == NATI_STATE_HYBRID_DDR )
 
+#undef COMPATIBLE_NMI_4SWITCH
+#define COMPATIBLE_NMI_4SWITCH(n) \
+	( (n) == IPA_NAT_MEM_IN_SRAM && nati_obj.curr_state == NATI_STATE_HYBRID_DDR ) || \
+	( (n) == IPA_NAT_MEM_IN_DDR  && nati_obj.curr_state == NATI_STATE_HYBRID ) || \
+	( (n) == IPA_NAT_MEM_IN_DDR  && nati_obj.curr_state == NATI_STATE_DDR_ONLY ) || \
+	( (n) == IPA_NAT_MEM_IN_SRAM && nati_obj.curr_state == NATI_STATE_SRAM_ONLY )
+
+#undef GEN_HOLD_STATE
+#define GEN_HOLD_STATE() \
+	( ! IN_HYBRID_STATE() ) ? nati_obj.curr_state : \
+	(nati_obj.curr_state == NATI_STATE_HYBRID) ? NATI_STATE_SRAM_ONLY : \
+	NATI_STATE_DDR_ONLY
+
 #undef  SRAM_CURRENTLY_ACTIVE
 #define SRAM_CURRENTLY_ACTIVE() \
 	( nati_obj.curr_state == NATI_STATE_SRAM_ONLY || \
@@ -256,6 +301,7 @@
 	uint32_t    public_ip_addr;
 	uint16_t    number_of_entries;
 	uint32_t*   tbl_hdl;
+	const char* mem_type_ptr;
 } table_add_args;
 
 typedef struct
diff --git a/ipanat/src/ipa_nat_statemach.c b/ipanat/src/ipa_nat_statemach.c
index c65f88a..b6cf284 100644
--- a/ipanat/src/ipa_nat_statemach.c
+++ b/ipanat/src/ipa_nat_statemach.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2019-2021 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
@@ -111,7 +111,9 @@
  */
 static ipa_nati_obj nati_obj = {
 	.prev_state          = NATI_STATE_NULL,
-	.curr_state          = NATI_STATE_DDR_ONLY,
+	.curr_state          = NATI_STATE_NULL,
+	.hold_state          = false,
+	.state_to_hold       = NATI_STATE_NULL,
 	.ddr_tbl_hdl         = 0,
 	.sram_tbl_hdl        = 0,
 	.tot_slots_in_sram   = 0,
@@ -187,6 +189,53 @@
 }
 
 /*
+ * Function for taking/locking the mutex...
+ */
+static int take_mutex()
+{
+	int ret;
+
+	if ( nat_mutex_init )
+	{
+again:
+		ret = pthread_mutex_lock(&nat_mutex);
+	}
+	else
+	{
+		ret = mutex_init();
+
+		if ( ret == 0 )
+		{
+			goto again;
+		}
+	}
+
+	if ( ret != 0 )
+	{
+		IPAERR("Unable to lock the %s nat mutex\n",
+			   (nat_mutex_init) ? "initialized" : "uninitialized");
+	}
+
+	return ret;
+}
+
+/*
+ * Function for giving/unlocking the mutex...
+ */
+static int give_mutex()
+{
+	int ret = (nat_mutex_init) ? pthread_mutex_unlock(&nat_mutex) : -1;
+
+	if ( ret != 0 )
+	{
+		IPAERR("Unable to unlock the %s nat mutex\n",
+			   (nat_mutex_init) ? "initialized" : "uninitialized");
+	}
+
+	return ret;
+}
+
+/*
  * ****************************************************************************
  *
  * HIJACKED API FUNCTIONS START HERE
@@ -203,22 +252,13 @@
 		.public_ip_addr    = public_ip_addr,
 		.number_of_entries = number_of_entries,
 		.tbl_hdl           = tbl_hdl,
+		.mem_type_ptr      = mem_type_ptr,
 	};
 
 	int ret;
 
 	IPADBG("In\n");
 
-	/*
-	 * If first time in here, then let XML drive initial state...
-	 */
-	if (nati_obj.prev_state == NATI_STATE_NULL)
-	{
-		SET_NATIOBJ_STATE(
-			&nati_obj,
-			mem_type_str_to_ipa_nati_state(mem_type_ptr));
-	}
-
 	ret = ipa_nati_statemach(&nati_obj, NATI_TRIG_ADD_TABLE, (void*) &args);
 
 	if ( ret == 0 )
@@ -388,27 +428,142 @@
 }
 
 int ipa_nat_switch_to(
-	enum ipa3_nat_mem_in nmi )
+	enum ipa3_nat_mem_in nmi,
+	bool                 hold_state )
 {
-	int ret = 0;
+	int ret = -1;
 
-	IPADBG("In\n");
+	IPADBG("In - current state %s\n",
+		   ipa_nati_state_as_str(nati_obj.curr_state));
 
-	if ( ! IPA_VALID_NAT_MEM_IN(nmi) || ! IN_HYBRID_STATE() )
+	if ( ! IPA_VALID_NAT_MEM_IN(nmi) )
 	{
-		IPAERR("Bad nmi(%s) and/or not in hybrid state\n",
-			   ipa3_nat_mem_in_as_str(nmi));
+		IPAERR("Bad nmi(%s)\n", ipa3_nat_mem_in_as_str(nmi));
+
 		ret = -1;
+
 		goto bail;
 	}
 
-	if ( (nmi == IPA_NAT_MEM_IN_SRAM && nati_obj.curr_state == NATI_STATE_HYBRID_DDR)
-		 ||
-		 (nmi == IPA_NAT_MEM_IN_DDR  && nati_obj.curr_state == NATI_STATE_HYBRID) )
+	ret = take_mutex();
+
+	if ( ret != 0 )
 	{
-		ret = ipa_nati_statemach(&nati_obj, NATI_TRIG_TBL_SWITCH, 0);
+		goto bail;
 	}
 
+	/*
+	 * Are we here before the state machine has been started?
+	 */
+	if ( IN_UNSTARTED_STATE() )
+	{
+		nati_obj.hold_state = hold_state;
+
+		nati_obj.state_to_hold =
+			(nmi == IPA_NAT_MEM_IN_DDR) ?
+			NATI_STATE_DDR_ONLY         :
+			NATI_STATE_SRAM_ONLY;
+
+		IPADBG(
+			"Initial state will be %s before table init and it %s be held\n",
+			ipa_nati_state_as_str(nati_obj.state_to_hold),
+			(hold_state) ? "will" : "will not");
+
+		ret = 0;
+
+		goto unlock;
+	}
+
+	/*
+	 * Are we here after we've already started in hybrid state?
+	 */
+	if ( IN_HYBRID_STATE() )
+	{
+		ret = 0;
+
+		if ( COMPATIBLE_NMI_4SWITCH(nmi) )
+		{
+			ret = ipa_nati_statemach(&nati_obj, NATI_TRIG_TBL_SWITCH, 0);
+		}
+
+		if ( ret == 0 )
+		{
+			nati_obj.hold_state = hold_state;
+
+			if ( hold_state )
+			{
+				nati_obj.state_to_hold = GEN_HOLD_STATE();
+			}
+
+			IPADBG(
+				"Current state is %s and it %s be held\n",
+				ipa_nati_state_as_str(nati_obj.curr_state),
+				(hold_state) ? "will" : "will not");
+		}
+
+		goto unlock;
+	}
+
+	/*
+	 * We've gotten here because we're not in an unstarted state, nor
+	 * are we in hybrid state. This means we're either in
+	 * NATI_STATE_DDR_ONLY or NATI_STATE_SRAM_ONLY
+	 *
+	 * Let's see what's being attempted and if it's OK...
+	 */
+	if ( hold_state )
+	{
+		if ( COMPATIBLE_NMI_4SWITCH(nmi) )
+		{
+			/*
+			 * If we've gotten here, it means that the requested nmi,
+			 * the current state, and the hold are compatible...
+			 */
+			nati_obj.state_to_hold = GEN_HOLD_STATE();
+			nati_obj.hold_state    = hold_state;
+
+			IPADBG(
+				"Requesting to hold memory type %s at "
+				"current state %s will be done\n",
+				ipa3_nat_mem_in_as_str(nmi),
+				ipa_nati_state_as_str(nati_obj.curr_state));
+
+			ret = 0;
+
+			goto unlock;
+		}
+		else
+		{
+			/*
+			 * The requested nmi, the current state, and the hold are
+			 * not compatible...
+			 */
+			IPAERR(
+				"Requesting to hold memory type %s and "
+				"current state %s are incompatible\n",
+				ipa3_nat_mem_in_as_str(nmi),
+				ipa_nati_state_as_str(nati_obj.curr_state));
+
+			ret = -1;
+
+			goto unlock;
+		}
+	}
+
+	/*
+	 * If we've gotten here, it's because the holding of state is no
+	 * longer desired...
+	 */
+	nati_obj.state_to_hold = NATI_STATE_NULL;
+	nati_obj.hold_state    = hold_state;
+
+	IPADBG("Holding of state is no longer desired\n");
+
+	ret = 0;
+
+unlock:
+	ret = give_mutex();
+
 bail:
 	IPADBG("Out\n");
 
@@ -671,6 +826,69 @@
 
 	ret = ipa_NATI_del_ipv4_table(tbl_hdl);
 
+	if ( ret == 0 && ! IN_HYBRID_STATE() )
+	{
+		/*
+		 * The following will create the preferred "initial state" for
+		 * restart...
+		 */
+		BACK2_UNSTARTED_STATE();
+	}
+
+	IPADBG("Out\n");
+
+	return ret;
+}
+
+/******************************************************************************/
+/*
+ * FUNCTION: _smFirstTbl
+ *
+ * PARAMS:
+ *
+ *   nati_obj_ptr (IN) A pointer to an initialized nati object
+ *
+ *   trigger      (IN) The trigger to run through the state machine
+ *
+ *   arb_data_ptr (IN) Whatever you like
+ *
+ * DESCRIPTION:
+ *
+ *   The following will cause the creation of the very first NAT table(s)
+ *   before any others have ever been created...
+ *
+ * RETURNS:
+ *
+ *   zero on success, otherwise non-zero
+ */
+static int _smFirstTbl(
+	ipa_nati_obj*    nati_obj_ptr,
+	ipa_nati_trigger trigger,
+	void*           arb_data_ptr )
+{
+	table_add_args* args = (table_add_args*) arb_data_ptr;
+
+	uint32_t  public_ip_addr    = args->public_ip_addr;
+	uint16_t  number_of_entries = args->number_of_entries;
+	uint32_t* tbl_hdl_ptr       = args->tbl_hdl;
+	const char* mem_type_ptr    = args->mem_type_ptr;
+
+	int ret;
+
+	IPADBG("In\n");
+
+	/*
+	 * This is the first time in here.  Let the ipacm's XML config (or
+	 * state_to_hold) drive initial state...
+	 */
+	SET_NATIOBJ_STATE(
+		nati_obj_ptr,
+		(nati_obj_ptr->hold_state && nati_obj_ptr->state_to_hold) ?
+		nati_obj_ptr->state_to_hold                               :
+		mem_type_str_to_ipa_nati_state(mem_type_ptr));
+
+	ret = ipa_nati_statemach(nati_obj_ptr, NATI_TRIG_ADD_TABLE, arb_data_ptr);
+
 	IPADBG("Out\n");
 
 	return ret;
@@ -987,6 +1205,15 @@
 		ret = _smDelTbl(nati_obj_ptr, trigger, (void*) &new_args);
 	}
 
+	if ( ret == 0 )
+	{
+		/*
+		 * The following will create the preferred "initial state" for
+		 * restart...
+		 */
+		BACK2_UNSTARTED_STATE();
+	}
+
 	IPADBG("Out\n");
 
 	return ret;
@@ -1514,7 +1741,9 @@
 	}
 	else
 	{
-		if ( nati_obj_ptr->curr_state == NATI_STATE_HYBRID )
+		if ( nati_obj_ptr->curr_state == NATI_STATE_HYBRID
+			 &&
+			 ! nati_obj_ptr->hold_state )
 		{
 			/*
 			 * In hybrid mode, we always start in SRAM...hence
@@ -1645,7 +1874,9 @@
 			 */
 			uint32_t* cnt_ptr = CHOOSE_CNTR();
 
-			if ( *cnt_ptr <= nati_obj_ptr->back_to_sram_thresh )
+			if ( *cnt_ptr <= nati_obj_ptr->back_to_sram_thresh
+				 &&
+				 ! nati_obj_ptr->hold_state )
 			{
 				/*
 				 * The following will focus us on SRAM and cause the copy
@@ -1807,7 +2038,9 @@
 
 	uint64_t           start, stop;
 
-	int stats_ret, ret;
+	int                stats_ret, ret;
+
+	bool               collect_stats = (bool) arb_data_ptr;
 
 	UNUSED(cnt_ptr);
 	UNUSED(trigger);
@@ -1815,8 +2048,10 @@
 
 	IPADBG("In\n");
 
-	stats_ret = ipa_NATI_ipv4_tbl_stats(
-		nati_obj_ptr->ddr_tbl_hdl, &nat_stats, &idx_stats);
+	stats_ret = (collect_stats) ?
+		ipa_NATI_ipv4_tbl_stats(
+			nati_obj_ptr->ddr_tbl_hdl, &nat_stats, &idx_stats) :
+		-1;
 
 	currTimeAs(TimeAsNanSecs, &start);
 
@@ -1979,7 +2214,9 @@
 
 	uint64_t           start, stop;
 
-	int stats_ret, ret;
+	int                stats_ret, ret;
+
+	bool               collect_stats = (bool) arb_data_ptr;
 
 	UNUSED(cnt_ptr);
 	UNUSED(trigger);
@@ -1987,8 +2224,10 @@
 
 	IPADBG("In\n");
 
-	stats_ret = ipa_NATI_ipv4_tbl_stats(
-		nati_obj_ptr->sram_tbl_hdl, &nat_stats, &idx_stats);
+	stats_ret = (collect_stats) ?
+		ipa_NATI_ipv4_tbl_stats(
+			nati_obj_ptr->sram_tbl_hdl, &nat_stats, &idx_stats) :
+		-1;
 
 	currTimeAs(TimeAsNanSecs, &start);
 
@@ -2243,7 +2482,7 @@
 {
 	{
 		SM_ROW( NATI_STATE_NULL,       NATI_TRIG_NULL,       _smUndef ),
-		SM_ROW( NATI_STATE_NULL,       NATI_TRIG_ADD_TABLE,  _smUndef ),
+		SM_ROW( NATI_STATE_NULL,       NATI_TRIG_ADD_TABLE,  _smFirstTbl ),
 		SM_ROW( NATI_STATE_NULL,       NATI_TRIG_DEL_TABLE,  _smUndef ),
 		SM_ROW( NATI_STATE_NULL,       NATI_TRIG_CLR_TABLE,  _smUndef ),
 		SM_ROW( NATI_STATE_NULL,       NATI_TRIG_WLK_TABLE,  _smUndef ),
@@ -2259,7 +2498,7 @@
 
 	{
 		SM_ROW( NATI_STATE_DDR_ONLY,   NATI_TRIG_NULL,       _smUndef ),
-		SM_ROW( NATI_STATE_DDR_ONLY,   NATI_TRIG_ADD_TABLE,  _smAddDdrTbl),
+		SM_ROW( NATI_STATE_DDR_ONLY,   NATI_TRIG_ADD_TABLE,  _smAddDdrTbl ),
 		SM_ROW( NATI_STATE_DDR_ONLY,   NATI_TRIG_DEL_TABLE,  _smDelTbl ),
 		SM_ROW( NATI_STATE_DDR_ONLY,   NATI_TRIG_CLR_TABLE,  _smClrTbl ),
 		SM_ROW( NATI_STATE_DDR_ONLY,   NATI_TRIG_WLK_TABLE,  _smWalkTbl ),
@@ -2275,7 +2514,7 @@
 
 	{
 		SM_ROW( NATI_STATE_SRAM_ONLY,  NATI_TRIG_NULL,       _smUndef ),
-		SM_ROW( NATI_STATE_SRAM_ONLY,  NATI_TRIG_ADD_TABLE,  _smAddSramTbl),
+		SM_ROW( NATI_STATE_SRAM_ONLY,  NATI_TRIG_ADD_TABLE,  _smAddSramTbl ),
 		SM_ROW( NATI_STATE_SRAM_ONLY,  NATI_TRIG_DEL_TABLE,  _smDelTbl ),
 		SM_ROW( NATI_STATE_SRAM_ONLY,  NATI_TRIG_CLR_TABLE,  _smClrTbl ),
 		SM_ROW( NATI_STATE_SRAM_ONLY,  NATI_TRIG_WLK_TABLE,  _smWalkTbl ),
@@ -2415,20 +2654,10 @@
 
 	IPADBG("In\n");
 
-	if ( ! nat_mutex_init )
-	{
-		ret = mutex_init();
+	ret = take_mutex();
 
-		if ( ret != 0 )
-		{
-			goto bail;
-		}
-	}
-
-	if ( pthread_mutex_lock(&nat_mutex) )
+	if ( ret != 0 )
 	{
-		IPAERR("Unable to lock the nat mutex\n");
-		ret = -EINVAL;
 		goto bail;
 	}
 
@@ -2464,11 +2693,7 @@
 	}
 
 unlock:
-	if ( pthread_mutex_unlock(&nat_mutex) )
-	{
-		IPAERR("Unable to unlock the nat mutex\n");
-		ret = (ret) ? ret : -EPERM;
-	}
+	ret = give_mutex();
 
 bail:
 	IPADBG("Out\n");