Merge "IPACM: Add support for Ethernet bridging in MCC Mode"
diff --git a/ipacm/inc/IPACM_Conntrack_NATApp.h b/ipacm/inc/IPACM_Conntrack_NATApp.h
index 7c97225..1fb12bf 100644
--- a/ipacm/inc/IPACM_Conntrack_NATApp.h
+++ b/ipacm/inc/IPACM_Conntrack_NATApp.h
@@ -52,6 +52,7 @@
 	uint32_t target_ip;
 	uint16_t target_port;
 
+	uint16_t public_ip;
 	uint16_t public_port;
 
 	u_int8_t  protocol;
@@ -74,6 +75,7 @@
 	nat_table_entry *cache;
 	nat_table_entry temp[MAX_TEMP_ENTRIES];
 	uint32_t pub_ip_addr;
+	uint32_t pub_ip_addr_pre;
 	uint32_t nat_table_hdl;
 
 	int curCnt, max_entries;
@@ -118,6 +120,7 @@
 	void UpdateTcpUdpTo(uint32_t, int proto);
 
 	void AddTempEntry(const nat_table_entry *);
+	void CacheEntry(const nat_table_entry *);
 	void DeleteTempEntry(const nat_table_entry *);
 	void FlushTempEntries(uint32_t, bool);
 };
diff --git a/ipacm/inc/IPACM_Defs.h b/ipacm/inc/IPACM_Defs.h
index 45ea6c0..72c348c 100644
--- a/ipacm/inc/IPACM_Defs.h
+++ b/ipacm/inc/IPACM_Defs.h
@@ -101,7 +101,7 @@
 #define IPA_LAN_TO_LAN_WLAN_HDR_NAME_V4 "Lan2Lan_Wlan_v4"
 #define IPA_LAN_TO_LAN_WLAN_HDR_NAME_V6 "Lan2Lan_Wlan_v6"
 #define IPA_LAN_TO_LAN_MAX_WLAN_CLIENT 32
-#define IPA_LAN_TO_LAN_MAX_USB_CLIENT 1
+#define IPA_LAN_TO_LAN_MAX_USB_CLIENT 15
 #define TCP_FIN_SHIFT 16
 #define TCP_SYN_SHIFT 17
 #define TCP_RST_SHIFT 18
@@ -179,6 +179,7 @@
 	IPA_ETH_BRIDGE_HDR_PROC_CTX_UNSET_EVENT,  /* 49 ipacm_event_data_fid */
 	IPA_WLAN_SWITCH_TO_SCC,                   /* 50 No Data */
 	IPA_WLAN_SWITCH_TO_MCC,                   /* 51 No Data */
+	IPA_CRADLE_WAN_MODE_SWITCH,				  /* 52 ipacm_event_cradle_wan_mode */
 	IPACM_EVENT_MAX
 } ipa_cm_event_id;
 
@@ -200,6 +201,12 @@
 	UNKNOWN_IF
 } ipacm_iface_type;
 
+typedef enum
+{
+	ROUTER = 0,
+	BRIDGE
+} ipacm_iface_mode;
+
 typedef struct
 {
 	struct nf_conntrack *ct;
@@ -210,6 +217,7 @@
 {
 	char iface_name[IPA_IFACE_NAME_LEN];
 	ipacm_iface_type if_cat;
+	ipacm_iface_mode if_mode;
 	int netlink_interface_index;
 } ipa_ifi_dev_name_t;
 
@@ -233,6 +241,11 @@
 
 typedef struct
 {
+	ipacm_iface_mode cradle_wan_mode;
+} ipacm_event_cradle_wan_mode;
+
+typedef struct
+{
 	enum ipa_ip_type iptype;
 	uint32_t ipv4_addr;
 	uint32_t ipv6_addr[4];
diff --git a/ipacm/inc/IPACM_Lan.h b/ipacm/inc/IPACM_Lan.h
index 505f426..bcfd898 100644
--- a/ipacm/inc/IPACM_Lan.h
+++ b/ipacm/inc/IPACM_Lan.h
@@ -185,6 +185,7 @@
 
 	int del_lan2lan_hdr(ipa_ip_type iptype, uint32_t hdr_hdl);
 
+	int handle_cradle_wan_mode_switch(bool is_wan_bridge_mode);
 
 
 	static ipa_hdr_l2_type usb_hdr_type;
diff --git a/ipacm/inc/IPACM_Wan.h b/ipacm/inc/IPACM_Wan.h
index 07036f0..96bb0e6 100644
--- a/ipacm/inc/IPACM_Wan.h
+++ b/ipacm/inc/IPACM_Wan.h
@@ -116,6 +116,7 @@
 	static uint32_t backhaul_ipv6_prefix[2];
 
 	static bool embms_is_on;
+	static bool cradle_backhaul_is_wan_bridge;
 
 private:
 
diff --git a/ipacm/inc/IPACM_Xml.h b/ipacm/inc/IPACM_Xml.h
index 90e0289..924e16d 100644
--- a/ipacm/inc/IPACM_Xml.h
+++ b/ipacm/inc/IPACM_Xml.h
@@ -84,6 +84,7 @@
 #define IFACE_TAG                            "Iface"
 #define NAME_TAG                             "Name"
 #define CATEGORY_TAG                         "Category"
+#define MODE_TAG                             "Mode"
 #define IPACMPRIVATESUBNETCFG_TAG            "IPACMPrivateSubnet"
 #define SUBNET_TAG                           "Subnet"
 #define SUBNETADDRESS_TAG                    "SubnetAddress"
@@ -96,6 +97,8 @@
 #define ODUIF_TAG                            "ODU"
 #define EMBMSIF_TAG                          "EMBMS"
 #define ETHIF_TAG                            "ETH"
+#define IFACE_ROUTER_MODE_TAG                "ROUTER"
+#define IFACE_BRIDGE_MODE_TAG                "BRIDGE"
 #define IPACMALG_TAG                         "IPACMALG"
 #define ALG_TAG                              "ALG"
 #define Protocol_TAG                         "Protocol"
diff --git a/ipacm/src/IPACM_Config.cpp b/ipacm/src/IPACM_Config.cpp
index 1a11012..5626367 100644
--- a/ipacm/src/IPACM_Config.cpp
+++ b/ipacm/src/IPACM_Config.cpp
@@ -145,7 +145,9 @@
 	{
 		strncpy(iface_table[i].iface_name, cfg->iface_config.iface_entries[i].iface_name, sizeof(iface_table[i].iface_name));
 		iface_table[i].if_cat = cfg->iface_config.iface_entries[i].if_cat;
-		IPACMDBG_H("IPACM_Config::iface_table[%d] = %s, cat=%d\n", i, iface_table[i].iface_name, iface_table[i].if_cat);
+		iface_table[i].if_mode = cfg->iface_config.iface_entries[i].if_mode;
+		IPACMDBG_H("IPACM_Config::iface_table[%d] = %s, cat=%d, mode=%d\n", i, iface_table[i].iface_name,
+				iface_table[i].if_cat, iface_table[i].if_mode);
 		/* copy bridge interface name to ipacmcfg */
 		if( iface_table[i].if_cat == VIRTUAL_IF)
 		{
diff --git a/ipacm/src/IPACM_ConntrackClient.cpp b/ipacm/src/IPACM_ConntrackClient.cpp
index d8fe726..9de5911 100644
--- a/ipacm/src/IPACM_ConntrackClient.cpp
+++ b/ipacm/src/IPACM_ConntrackClient.cpp
@@ -113,19 +113,6 @@
 		goto IGNORE;
 	}
 
-	if(!CtList->isWanUp())
-	{
-#ifdef IPACM_DEBUG
-		IPACMDBG("Wan is not up, ignoring below connections\n");
-		char buf[1024];
-		nfct_snprintf(buf, sizeof(buf), ct,
-									type, NFCT_O_PLAIN, NFCT_OF_TIME);
-		IPACMDBG("%s\n", buf);
-		IPACMDBG("\n");
-		ParseCTMessage(ct);
-#endif
-		goto IGNORE;
-	}
 #endif
 
 	ct_data = (ipacm_ct_evt_data *)malloc(sizeof(ipacm_ct_evt_data));
diff --git a/ipacm/src/IPACM_ConntrackListener.cpp b/ipacm/src/IPACM_ConntrackListener.cpp
index e00f921..9e66a00 100644
--- a/ipacm/src/IPACM_ConntrackListener.cpp
+++ b/ipacm/src/IPACM_ConntrackListener.cpp
@@ -213,6 +213,7 @@
 				if(nat_iface_ipv4_addr[j] == 0)
 				{
 					nat_iface_ipv4_addr[j] = data->ipv4_addr;
+					nat_inst->ResetPwrSaveIf(data->ipv4_addr);
 					nat_inst->FlushTempEntries(data->ipv4_addr, true);
 					break;
 				}
@@ -702,11 +703,13 @@
 		 {
 			 IPACMDBG("orig src ip:0x%x equal to wan ip\n",orig_src_ip);
 			 status = IPS_SRC_NAT;
+			 rule.public_ip = wan_ipaddr;
 		 }
 		 else if(orig_dst_ip == wan_ipaddr)
 		 {
 			 IPACMDBG("orig Dst IP:0x%x equal to wan ip\n",orig_dst_ip);
 			 status = IPS_DST_NAT;
+	 	 	 rule.public_ip = wan_ipaddr;
 		 }
 		 else
 		 {
@@ -933,7 +936,12 @@
 			if(TCP_CONNTRACK_ESTABLISHED == tcp_state)
 			{
 				 IPACMDBG("TCP state TCP_CONNTRACK_ESTABLISHED(%d)\n", tcp_state);
-				 if(isTempEntry)
+				 if(!CtList->isWanUp())
+				 {
+				 	 IPACMDBG("Wan is not up, cache connections\n");
+					 nat_inst->CacheEntry(&rule);
+				 }
+				 else if(isTempEntry)
 				 {
 					 nat_inst->AddTempEntry(&rule);
 				 }
@@ -973,7 +981,12 @@
 			if(NFCT_T_NEW == type)
 			{
 				 IPACMDBG("New UDP connection at time %ld\n", time(NULL));
-				 if(isTempEntry)
+				 if(!CtList->isWanUp())
+				 {
+				 	 IPACMDBG("Wan is not up, cache connections\n");
+					 nat_inst->CacheEntry(&rule);
+				 }
+				 else if(isTempEntry)
 				 {
 					 nat_inst->AddTempEntry(&rule);
 				 }
diff --git a/ipacm/src/IPACM_Conntrack_NATApp.cpp b/ipacm/src/IPACM_Conntrack_NATApp.cpp
index c4a7e52..0915fda 100644
--- a/ipacm/src/IPACM_Conntrack_NATApp.cpp
+++ b/ipacm/src/IPACM_Conntrack_NATApp.cpp
@@ -129,8 +129,19 @@
 int NatApp::AddTable(uint32_t pub_ip)
 {
 	int ret;
+	int cnt = 0;
+	ipa_nat_ipv4_rule nat_rule;
 	IPACMDBG("%s() %d\n", __FUNCTION__, __LINE__);
 
+	/* Not reset the cache wait it timeout by destroy event */
+#if 0
+	if (pub_ip != pub_ip_addr_pre)
+	{
+		IPACMDBG("Reset the cache because NAT-ipv4 different\n");
+		memset(cache, 0, sizeof(nat_table_entry) * max_entries);
+		curCnt = 0;
+	}
+#endif
 	ret = ipa_nat_add_ipv4_tbl(pub_ip, max_entries, &nat_table_hdl);
 	if(ret)
 	{
@@ -138,17 +149,56 @@
 		return ret;
 	}
 
+	/* Add back the cashed NAT-entry */
+	if (pub_ip == pub_ip_addr_pre)
+	{
+		IPACMDBG("Restore the cache to ipa NAT-table\n");
+		for(cnt = 0; cnt < max_entries; cnt++)
+		{
+			if(cache[cnt].private_ip !=0)
+			{
+				memset(&nat_rule, 0 , sizeof(nat_rule));
+				nat_rule.private_ip = cache[cnt].private_ip;
+				nat_rule.target_ip = cache[cnt].target_ip;
+				nat_rule.target_port = cache[cnt].target_port;
+				nat_rule.private_port = cache[cnt].private_port;
+				nat_rule.public_port = cache[cnt].public_port;
+				nat_rule.protocol = cache[cnt].protocol;
+
+				if(ipa_nat_add_ipv4_rule(nat_table_hdl, &nat_rule, &cache[cnt].rule_hdl) < 0)
+				{
+					IPACMERR("unable to add the rule delete from cache\n");
+					memset(&cache[cnt], 0, sizeof(cache[cnt]));
+					curCnt--;
+					continue;
+				}
+				cache[cnt].enabled = true;
+
+				IPACMDBG("On wan-iface reset added below rule successfully\n");
+				iptodot("Private IP", nat_rule.private_ip);
+				iptodot("Target IP", nat_rule.target_ip);
+				IPACMDBG("Private Port:%d \t Target Port: %d\t", nat_rule.private_port, nat_rule.target_port);
+				IPACMDBG("Public Port:%d\n", nat_rule.public_port);
+				IPACMDBG("protocol: %d\n", nat_rule.protocol);
+			}
+		}
+	}
+
 	pub_ip_addr = pub_ip;
 	return 0;
 }
 
 void NatApp::Reset()
 {
-	memset(cache, 0, sizeof(nat_table_entry) * max_entries);
+	int cnt = 0;
 
 	nat_table_hdl = 0;
 	pub_ip_addr = 0;
-	curCnt = 0;
+	/* NAT tbl deleted, reset enabled bit */
+	for(cnt = 0; cnt < max_entries; cnt++)
+	{
+		cache[cnt].enabled ==false;
+	}
 }
 
 int NatApp::DeleteTable(uint32_t pub_ip)
@@ -172,6 +222,7 @@
 		return ret;
 	}
 
+	pub_ip_addr_pre = pub_ip_addr;
 	Reset();
 	return 0;
 }
@@ -208,9 +259,7 @@
 	int cnt = 0;
 	IPACMDBG("%s() %d\n", __FUNCTION__, __LINE__);
 
-	CHK_TBL_HDL();
-
-  IPACMDBG("Received below nat entry for deletion\n");
+	IPACMDBG("Received below nat entry for deletion\n");
 	iptodot("Private IP", rule->private_ip);
 	iptodot("Target IP", rule->target_ip);
 	IPACMDBG("Private Port: %d\t Target Port: %d\t", rule->private_port, rule->target_port);
@@ -229,8 +278,7 @@
 			{
 				if(ipa_nat_del_ipv4_rule(nat_table_hdl, cache[cnt].rule_hdl) < 0)
 				{
-					IPACMERR("%s() %d\n", __FUNCTION__, __LINE__);
-					return -1;
+					IPACMERR("%s() %d deletion failed\n", __FUNCTION__, __LINE__);
 				}
 
 				IPACMDBG("Deleted Nat entry(%d) Successfully\n", cnt);
@@ -525,7 +573,6 @@
 		}
 	}
 
-	CHK_TBL_HDL();
 
 	for(cnt = 0; cnt < IPA_MAX_NUM_WIFI_CLIENTS; cnt++)
 	{
@@ -579,6 +626,8 @@
 
 	for(cnt = 0; cnt < max_entries; cnt++)
 	{
+		IPACMDBG("cache (%d): enable %d, ip 0x%x\n", cnt, cache[cnt].enabled, cache[cnt].private_ip);
+
 		if(cache[cnt].private_ip == client_lan_ip &&
 			 cache[cnt].enabled == false)
 		{
@@ -704,11 +753,14 @@
 		{
 			if(isAdd)
 			{
-				ret = AddEntry(&temp[cnt]);
-				if(ret)
+				if(temp[cnt].public_ip == pub_ip_addr)
 				{
-					IPACMERR("unable to add temp entry: %d\n", ret);
-					continue;
+					ret = AddEntry(&temp[cnt]);
+					if(ret)
+					{
+						IPACMERR("unable to add temp entry: %d\n", ret);
+						continue;
+					}
 				}
 			}
 			memset(&temp[cnt], 0, sizeof(nat_table_entry));
@@ -729,7 +781,6 @@
 		return -1;
 	}
 
-	CHK_TBL_HDL();
 
   for(cnt = 0; cnt < IPA_MAX_NUM_WIFI_CLIENTS; cnt++)
   {
@@ -741,27 +792,28 @@
     }
   }
 
-	for(cnt = 0; cnt < max_entries; cnt++)
+  for(cnt = 0; cnt < max_entries; cnt++)
+  {
+	if(cache[cnt].private_ip == ip_addr)
 	{
-		if(cache[cnt].private_ip == ip_addr)
-		{
-
-      if(cache[cnt].enabled == true)
-      {
+		if(cache[cnt].enabled == true)
+      		{
 			if(ipa_nat_del_ipv4_rule(nat_table_hdl, cache[cnt].rule_hdl) < 0)
 			{
 				IPACMERR("unable to delete the rule\n");
 				continue;
 			}
-      }
-
-			memset(&cache[cnt], 0, sizeof(cache[cnt]));
-      curCnt--;
-		}
+			else
+			{
+				IPACMDBG("won't delete the rule\n");
+				cache[cnt].enabled = false;
+		        }
+	        }
+		IPACMDBG("won't delete the rule for entry %d, enabled %d\n",cnt, cache[cnt].enabled);
 	}
-
+  }
   IPACMDBG("Deleted %d entries\n", (tmp - curCnt));
-	return 0;
+  return 0;
 }
 
 int NatApp::DelEntriesOnSTAClntDiscon(uint32_t ip_addr)
@@ -775,7 +827,6 @@
 		return -1;
 	}
 
-	CHK_TBL_HDL();
 
 	for(cnt = 0; cnt < max_entries; cnt++)
 	{
@@ -798,3 +849,61 @@
 	IPACMDBG("Deleted %d entries\n", (tmp - curCnt));
 	return 0;
 }
+
+void NatApp::CacheEntry(const nat_table_entry *rule)
+{
+	int cnt;
+	if(rule->private_ip == 0 ||
+		 rule->target_ip == 0 ||
+		 rule->private_port == 0  ||
+		 rule->target_port == 0 ||
+		 rule->protocol == 0)
+	{
+		IPACMERR("Invalid Connection, ignoring it\n");
+		return;
+	}
+
+	if(!ChkForDup(rule))
+	{
+		for(; cnt < max_entries; cnt++)
+		{
+			if(cache[cnt].private_ip == 0 &&
+				 cache[cnt].target_ip == 0 &&
+				 cache[cnt].private_port == 0  &&
+				 cache[cnt].target_port == 0 &&
+				 cache[cnt].protocol == 0)
+			{
+				break;
+			}
+		}
+
+		if(max_entries == cnt)
+		{
+			IPACMERR("Error: Unable to add, reached maximum rules\n");
+			return;
+		}
+		else
+		{
+			cache[cnt].enabled = false;
+			cache[cnt].rule_hdl = 0;
+			cache[cnt].private_ip = rule->private_ip;
+			cache[cnt].target_ip = rule->target_ip;
+			cache[cnt].target_port = rule->target_port;
+			cache[cnt].private_port = rule->private_port;
+			cache[cnt].protocol = rule->protocol;
+			cache[cnt].timestamp = 0;
+			cache[cnt].public_port = rule->public_port;
+			cache[cnt].dst_nat = rule->dst_nat;
+			curCnt++;
+		}
+
+	}
+	else
+	{
+		IPACMERR("Duplicate rule. Ignore it\n");
+		return;
+	}
+
+	IPACMDBG("Cached rule(%d) successfully\n", cnt);
+	return;
+}
diff --git a/ipacm/src/IPACM_IfaceManager.cpp b/ipacm/src/IPACM_IfaceManager.cpp
index 573d7c6..80f7210 100644
--- a/ipacm/src/IPACM_IfaceManager.cpp
+++ b/ipacm/src/IPACM_IfaceManager.cpp
@@ -225,6 +225,7 @@
 				IPACM_EvtDispatcher::registr(IPA_ETH_BRIDGE_HDR_PROC_CTX_SET_EVENT, lan);
 				IPACM_EvtDispatcher::registr(IPA_ETH_BRIDGE_HDR_PROC_CTX_UNSET_EVENT, lan);
 #endif
+				IPACM_EvtDispatcher::registr(IPA_CRADLE_WAN_MODE_SWITCH, lan);
 				IPACM_EvtDispatcher::registr(IPA_LINK_DOWN_EVENT, lan);
 				IPACM_EvtDispatcher::registr(IPA_LAN_DELETE_SELF, lan);
 				IPACMDBG_H("ipa_LAN (%s):ipa_index (%d) instance open/registr ok\n", lan->dev_name, lan->ipa_if_num);
@@ -246,6 +247,7 @@
 				IPACM_EvtDispatcher::registr(IPA_HANDLE_WAN_UP_V6, ETH);
 				IPACM_EvtDispatcher::registr(IPA_HANDLE_WAN_DOWN, ETH);
 				IPACM_EvtDispatcher::registr(IPA_HANDLE_WAN_DOWN_V6, ETH);
+				IPACM_EvtDispatcher::registr(IPA_CRADLE_WAN_MODE_SWITCH, ETH);
 				IPACM_EvtDispatcher::registr(IPA_LINK_DOWN_EVENT, ETH);
 				IPACM_EvtDispatcher::registr(IPA_LAN_DELETE_SELF, ETH);
 				IPACMDBG_H("ipa_LAN (%s):ipa_index (%d) instance open/registr ok\n", ETH->dev_name, ETH->ipa_if_num);
@@ -269,6 +271,7 @@
 					IPACM_EvtDispatcher::registr(IPA_HANDLE_WAN_UP_V6, odu);
 					IPACM_EvtDispatcher::registr(IPA_HANDLE_WAN_DOWN, odu);
 					IPACM_EvtDispatcher::registr(IPA_HANDLE_WAN_DOWN_V6, odu);
+					IPACM_EvtDispatcher::registr(IPA_CRADLE_WAN_MODE_SWITCH, odu);
 					IPACM_EvtDispatcher::registr(IPA_LINK_DOWN_EVENT, odu);
 					IPACM_EvtDispatcher::registr(IPA_LAN_DELETE_SELF, odu);
 					IPACMDBG("ipa_LAN (%s):ipa_index (%d) instance open/registr ok\n", odu->dev_name, odu->ipa_if_num);
@@ -319,6 +322,7 @@
 				IPACM_EvtDispatcher::registr(IPA_ETH_BRIDGE_HDR_PROC_CTX_SET_EVENT, wl);
 				IPACM_EvtDispatcher::registr(IPA_ETH_BRIDGE_HDR_PROC_CTX_UNSET_EVENT, wl);
 #endif
+				IPACM_EvtDispatcher::registr(IPA_CRADLE_WAN_MODE_SWITCH, wl);
 				IPACM_EvtDispatcher::registr(IPA_WLAN_LINK_DOWN_EVENT, wl);
 				IPACM_EvtDispatcher::registr(IPA_LAN_DELETE_SELF, wl);
 #ifndef FEATURE_IPA_ANDROID
diff --git a/ipacm/src/IPACM_Lan.cpp b/ipacm/src/IPACM_Lan.cpp
index a5b5eae..2b757a6 100644
--- a/ipacm/src/IPACM_Lan.cpp
+++ b/ipacm/src/IPACM_Lan.cpp
@@ -732,6 +732,27 @@
 	}
 	break;
 
+	case IPA_CRADLE_WAN_MODE_SWITCH:
+	{
+		IPACMDBG_H("Received IPA_CRADLE_WAN_MODE_SWITCH event.\n");
+		ipacm_event_cradle_wan_mode* wan_mode = (ipacm_event_cradle_wan_mode*)param;
+		if(wan_mode == NULL)
+		{
+			IPACMERR("Event data is empty.\n");
+			return;
+		}
+
+		if(wan_mode->cradle_wan_mode == BRIDGE)
+		{
+			handle_cradle_wan_mode_switch(true);
+		}
+		else
+		{
+			handle_cradle_wan_mode_switch(false);
+		}
+	}
+	break;
+
 	default:
 		break;
 	}
@@ -1148,7 +1169,14 @@
 		flt_rule_entry.at_rear = true;
 		flt_rule_entry.flt_rule_hdl = -1;
 		flt_rule_entry.status = -1;
-		flt_rule_entry.rule.action = IPA_PASS_TO_SRC_NAT; //IPA_PASS_TO_ROUTING
+		if(IPACM_Wan::cradle_backhaul_is_wan_bridge == true)
+		{
+			flt_rule_entry.rule.action = IPA_PASS_TO_ROUTING;
+		}
+		else
+		{
+			flt_rule_entry.rule.action = IPA_PASS_TO_SRC_NAT; //IPA_PASS_TO_ROUTING
+		}
 		flt_rule_entry.rule.rt_tbl_hdl = IPACM_Iface::ipacmcfg->rt_tbl_wan_v4.hdl;
 
 		memcpy(&flt_rule_entry.rule.attrib,
@@ -2372,12 +2400,6 @@
 			goto fail;
 		}
 #endif
-		if(m_filtering.DeleteFilteringHdls(ipv6_icmp_flt_rule_hdl, IPA_IP_v6, NUM_IPV6_ICMP_FLT_RULE) == false)
-		{
-			IPACMERR("Error Deleting ICMPv6 Filtering Rule, aborting...\n");
-			res = IPACM_FAILURE;
-			goto fail;
-		}
 #ifndef FEATURE_ETH_BRIDGE_LE
 #ifdef CT_OPT
 		if (m_filtering.DeleteFilteringHdls(tcp_ctl_flt_rule_hdl_v4, IPA_IP_v4, NUM_TCP_CTL_FLT_RULE) == false)
@@ -2427,6 +2449,13 @@
 
 	if (ip_type != IPA_IP_v4 && rx_prop != NULL)
 	{
+		if(m_filtering.DeleteFilteringHdls(ipv6_icmp_flt_rule_hdl, IPA_IP_v6, NUM_IPV6_ICMP_FLT_RULE) == false)
+		{
+			IPACMERR("Error Deleting ICMPv6 Filtering Rule, aborting...\n");
+			res = IPACM_FAILURE;
+			goto fail;
+		}
+
 		if (m_filtering.DeleteFilteringHdls(dft_v6fl_rule_hdl,
 																				IPA_IP_v6,
 																				(IPV6_DEFAULT_FILTERTING_RULES + IPV6_DEFAULT_LAN_FILTERTING_RULES)) == false)
@@ -5425,5 +5454,85 @@
 	return IPACM_SUCCESS;
 }
 
+int IPACM_Lan::handle_cradle_wan_mode_switch(bool is_wan_bridge_mode)
+{
+	struct ipa_flt_rule_mdfy flt_rule_entry;
+	int len = 0;
+	ipa_ioc_mdfy_flt_rule *m_pFilteringTable;
+
+	IPACMDBG_H("Handle wan mode swtich: is wan bridge mode?%d\n", is_wan_bridge_mode);
+
+	if (rx_prop == NULL)
+	{
+		IPACMDBG_H("No rx properties registered for iface %s\n", dev_name);
+		return IPACM_SUCCESS;
+	}
+
+	len = sizeof(struct ipa_ioc_mdfy_flt_rule) + (1 * sizeof(struct ipa_flt_rule_mdfy));
+	m_pFilteringTable = (struct ipa_ioc_mdfy_flt_rule *)calloc(1, len);
+	if (m_pFilteringTable == NULL)
+	{
+		PERROR("Error Locate ipa_ioc_mdfy_flt_rule memory...\n");
+		return IPACM_FAILURE;
+	}
+
+	m_pFilteringTable->commit = 1;
+	m_pFilteringTable->ip = IPA_IP_v4;
+	m_pFilteringTable->num_rules = (uint8_t)1;
+
+	IPACMDBG_H("Retrieving routing hanle for table: %s\n",
+					 IPACM_Iface::ipacmcfg->rt_tbl_wan_v4.name);
+	if (false == m_routing.GetRoutingTable(&IPACM_Iface::ipacmcfg->rt_tbl_wan_v4))
+	{
+		IPACMERR("m_routing.GetRoutingTable(&IPACM_Iface::ipacmcfg->rt_tbl_wan_v4=0x%p) Failed.\n",
+						 &IPACM_Iface::ipacmcfg->rt_tbl_wan_v4);
+		free(m_pFilteringTable);
+		return IPACM_FAILURE;
+	}
+	IPACMDBG_H("Routing handle for table: %d\n", IPACM_Iface::ipacmcfg->rt_tbl_wan_v4.hdl);
+
+
+	memset(&flt_rule_entry, 0, sizeof(struct ipa_flt_rule_mdfy)); // Zero All Fields
+	flt_rule_entry.status = -1;
+	flt_rule_entry.rule_hdl = lan_wan_fl_rule_hdl[0];
+
+	flt_rule_entry.rule.retain_hdr = 0;
+	flt_rule_entry.rule.to_uc = 0;
+	flt_rule_entry.rule.eq_attrib_type = 0;
+	if(is_wan_bridge_mode)
+	{
+		flt_rule_entry.rule.action = IPA_PASS_TO_ROUTING;
+	}
+	else
+	{
+		flt_rule_entry.rule.action = IPA_PASS_TO_SRC_NAT;
+	}
+	flt_rule_entry.rule.rt_tbl_hdl = IPACM_Iface::ipacmcfg->rt_tbl_wan_v4.hdl;
+
+	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.v4.dst_addr_mask = 0x0;
+	flt_rule_entry.rule.attrib.u.v4.dst_addr = 0x0;
+
+	memcpy(&m_pFilteringTable->rules[0], &flt_rule_entry, sizeof(flt_rule_entry));
+	if (false == m_filtering.ModifyFilteringRule(m_pFilteringTable))
+	{
+		IPACMERR("Error Modifying RuleTable(0) to Filtering, aborting...\n");
+		free(m_pFilteringTable);
+		return IPACM_FAILURE;
+	}
+	else
+	{
+		IPACMDBG_H("flt rule hdl = %d, status = %d\n",
+						 m_pFilteringTable->rules[0].rule_hdl,
+						 m_pFilteringTable->rules[0].status);
+	}
+	free(m_pFilteringTable);
+	return IPACM_SUCCESS;
+}
+
 
 
diff --git a/ipacm/src/IPACM_Wan.cpp b/ipacm/src/IPACM_Wan.cpp
index 92b4a5e..ddb7915 100644
--- a/ipacm/src/IPACM_Wan.cpp
+++ b/ipacm/src/IPACM_Wan.cpp
@@ -66,6 +66,7 @@
 int IPACM_Wan::num_ipv6_modem_pdn = 0;
 
 bool IPACM_Wan::embms_is_on = false;
+bool IPACM_Wan::cradle_backhaul_is_wan_bridge = false;
 
 uint32_t IPACM_Wan::backhaul_ipv6_prefix[2];
 
@@ -501,7 +502,66 @@
 
 	case IPA_CFG_CHANGE_EVENT:
 		{
-			if ( (IPACM_Iface::ipacmcfg->iface_table[ipa_if_num].if_cat != ipa_if_cate) &&
+			if ( (IPACM_Iface::ipacmcfg->iface_table[ipa_if_num].if_cat == ipa_if_cate) &&
+					(m_is_sta_mode ==ECM_WAN))
+			{
+				IPACMDBG_H("Received IPA_CFG_CHANGE_EVENT and category did not change(wan_mode:%d)\n", m_is_sta_mode);
+				IPACMDBG_H("Now the cradle wan mode is %d.\n", IPACM_Iface::ipacmcfg->iface_table[ipa_if_num].if_mode);
+				if(is_default_gateway == true)
+				{
+					if(cradle_backhaul_is_wan_bridge == false && IPACM_Iface::ipacmcfg->iface_table[ipa_if_num].if_mode == BRIDGE)
+					{
+						IPACMDBG_H("Cradle wan mode switch to bridge mode.\n");
+						cradle_backhaul_is_wan_bridge = true;
+					}
+					else if(cradle_backhaul_is_wan_bridge == true && IPACM_Iface::ipacmcfg->iface_table[ipa_if_num].if_mode == ROUTER)
+					{
+						IPACMDBG_H("Cradle wan mode switch to router mode.\n");
+						cradle_backhaul_is_wan_bridge = false;
+					}
+					else
+					{
+						IPACMDBG_H("No cradle mode switch, return.\n");
+						return;
+					}
+					/* post wan mode change event to LAN/WLAN */
+					if(IPACM_Wan::wan_up == true)
+					{
+						IPACMDBG_H("This interface is default GW.\n");
+						ipacm_cmd_q_data evt_data;
+						memset(&evt_data, 0, sizeof(evt_data));
+
+						ipacm_event_cradle_wan_mode *data_wan_mode = NULL;
+						data_wan_mode = (ipacm_event_cradle_wan_mode *)malloc(sizeof(ipacm_event_cradle_wan_mode));
+						if(data_wan_mode == NULL)
+						{
+							IPACMERR("unable to allocate memory.\n");
+							return;
+						}
+						data_wan_mode->cradle_wan_mode = IPACM_Iface::ipacmcfg->iface_table[ipa_if_num].if_mode;
+						evt_data.event = IPA_CRADLE_WAN_MODE_SWITCH;
+						evt_data.evt_data = data_wan_mode;
+						IPACMDBG_H("Posting IPA_CRADLE_WAN_MODE_SWITCH event.\n");
+						IPACM_EvtDispatcher::PostEvt(&evt_data);
+					}
+					/* update the firewall flt rule actions */
+					if(active_v4)
+					{
+						del_dft_firewall_rules(IPA_IP_v4);
+						config_dft_firewall_rules(IPA_IP_v4);
+					}
+					if(active_v6)
+					{
+						del_dft_firewall_rules(IPA_IP_v6);
+						config_dft_firewall_rules(IPA_IP_v6);
+					}
+				}
+				else
+				{
+					IPACMDBG_H("This interface is not default GW, ignore.\n");
+				}
+			}
+			else if ( (IPACM_Iface::ipacmcfg->iface_table[ipa_if_num].if_cat != ipa_if_cate) &&
 					(m_is_sta_mode ==ECM_WAN))
 			{
 				IPACMDBG_H("Received IPA_CFG_CHANGE_EVENT and category changed(wan_mode:%d)\n", m_is_sta_mode);
@@ -1027,6 +1087,14 @@
 	if (m_is_sta_mode !=Q6_WAN)
 	{
 		IPACM_Wan::backhaul_is_sta_mode	= true;
+		if(IPACM_Iface::ipacmcfg->iface_table[ipa_if_num].if_mode == BRIDGE)
+		{
+			IPACM_Wan::cradle_backhaul_is_wan_bridge = true;
+		}
+		else
+		{
+			IPACM_Wan::cradle_backhaul_is_wan_bridge = false;
+		}
 		if((iptype==IPA_IP_v4) && (header_set_v4 != true))
 		{
 			header_partial_default_wan_v4 = true;
@@ -1566,13 +1634,13 @@
 	        }
 	}
 
-    /* see if default routes are setup before constructing full header */
-    if(header_partial_default_wan_v4 == true)
+	/* see if default routes are setup before constructing full header */
+	if(header_partial_default_wan_v4 == true)
 	{
 	   handle_route_add_evt(IPA_IP_v4);
 	}
 
-    if(header_partial_default_wan_v6 == true)
+	if(header_partial_default_wan_v6 == true)
 	{
 	   handle_route_add_evt(IPA_IP_v6);
 	}
@@ -1697,25 +1765,39 @@
 			flt_rule_entry.status = -1;
 
 			/* firewall disable, all traffic are allowed */
-                        if(firewall_config.firewall_enable == true)
+			if(firewall_config.firewall_enable == true)
 			{
-			    flt_rule_entry.at_rear = true;
+				flt_rule_entry.at_rear = true;
 
-			     /* default action for v4 is go DST_NAT unless user set to exception*/
-                             if(firewall_config.rule_action_accept == true)
-			    {
-			        flt_rule_entry.rule.action = IPA_PASS_TO_EXCEPTION;
-			    }
-			    else
-			    {
-			        flt_rule_entry.rule.action = IPA_PASS_TO_DST_NAT;
-                            }
-		        }
+				/* default action for v4 is go DST_NAT unless user set to exception*/
+				if(firewall_config.rule_action_accept == true)
+				{
+					flt_rule_entry.rule.action = IPA_PASS_TO_EXCEPTION;
+				}
+				else
+				{
+					if(IPACM_Iface::ipacmcfg->iface_table[ipa_if_num].if_mode == ROUTER)
+					{
+						flt_rule_entry.rule.action = IPA_PASS_TO_DST_NAT;
+					}
+					else
+					{
+						flt_rule_entry.rule.action = IPA_PASS_TO_ROUTING;
+					}
+				}
+			}
 			else
 			{
-			  flt_rule_entry.at_rear = true;
-			flt_rule_entry.rule.action = IPA_PASS_TO_DST_NAT;
-                        }
+				flt_rule_entry.at_rear = true;
+				if(IPACM_Iface::ipacmcfg->iface_table[ipa_if_num].if_mode == ROUTER)
+				{
+					flt_rule_entry.rule.action = IPA_PASS_TO_DST_NAT;
+				}
+				else
+				{
+					flt_rule_entry.rule.action = IPA_PASS_TO_ROUTING;
+				}
+            }
 
 			flt_rule_entry.rule.rt_tbl_hdl = IPACM_Iface::ipacmcfg->rt_tbl_lan_v4.hdl;
 			memcpy(&flt_rule_entry.rule.attrib,
@@ -1776,14 +1858,21 @@
 					flt_rule_entry.rule.rt_tbl_hdl = IPACM_Iface::ipacmcfg->rt_tbl_lan_v4.hdl;
 
 					/* Accept v4 matched rules*/
-                                        if(firewall_config.rule_action_accept == true)
-			                {
-			                     flt_rule_entry.rule.action = IPA_PASS_TO_DST_NAT;
-			                }
-			                else
-			                {
-			                     flt_rule_entry.rule.action = IPA_PASS_TO_EXCEPTION;
-                                        }
+                    if(firewall_config.rule_action_accept == true)
+			        {
+						if(IPACM_Iface::ipacmcfg->iface_table[ipa_if_num].if_mode == ROUTER)
+						{
+							flt_rule_entry.rule.action = IPA_PASS_TO_DST_NAT;
+						}
+						else
+						{
+							flt_rule_entry.rule.action = IPA_PASS_TO_ROUTING;
+						}
+			        }
+			        else
+			        {
+			            flt_rule_entry.rule.action = IPA_PASS_TO_EXCEPTION;
+                    }
 
 					memcpy(&flt_rule_entry.rule.attrib,
 								 &firewall_config.extd_firewall_entries[i].attrib,
@@ -1877,7 +1966,7 @@
 			flt_rule_entry.status = -1;
 
 			/* firewall disable, all traffic are allowed */
-                        if(firewall_config.firewall_enable == true)
+            if(firewall_config.firewall_enable == true)
 			{
 			     flt_rule_entry.at_rear = true;
 
@@ -1888,14 +1977,28 @@
 			     }
 			     else
 			     {
-			       flt_rule_entry.rule.action = IPA_PASS_TO_DST_NAT;
-                             }
-		        }
+					if(IPACM_Iface::ipacmcfg->iface_table[ipa_if_num].if_mode == ROUTER)
+					{
+						flt_rule_entry.rule.action = IPA_PASS_TO_DST_NAT;
+					}
+					else
+					{
+						flt_rule_entry.rule.action = IPA_PASS_TO_ROUTING;
+					}
+				}
+		    }
 			else
 			{
 			    flt_rule_entry.at_rear = true;
-			flt_rule_entry.rule.action = IPA_PASS_TO_DST_NAT;
-                        }
+				if(IPACM_Iface::ipacmcfg->iface_table[ipa_if_num].if_mode == ROUTER)
+				{
+					flt_rule_entry.rule.action = IPA_PASS_TO_DST_NAT;
+				}
+				else
+				{
+					flt_rule_entry.rule.action = IPA_PASS_TO_ROUTING;
+				}
+            }
 
 			flt_rule_entry.rule.rt_tbl_hdl = IPACM_Iface::ipacmcfg->rt_tbl_lan_v4.hdl;
 			memcpy(&flt_rule_entry.rule.attrib,
diff --git a/ipacm/src/IPACM_Wlan.cpp b/ipacm/src/IPACM_Wlan.cpp
index e0f9a82..fd957a4 100644
--- a/ipacm/src/IPACM_Wlan.cpp
+++ b/ipacm/src/IPACM_Wlan.cpp
@@ -763,6 +763,27 @@
 		}
 		break;
 
+	case IPA_CRADLE_WAN_MODE_SWITCH:
+	{
+		IPACMDBG_H("Received IPA_CRADLE_WAN_MODE_SWITCH event.\n");
+		ipacm_event_cradle_wan_mode* wan_mode = (ipacm_event_cradle_wan_mode*)param;
+		if(wan_mode == NULL)
+		{
+			IPACMERR("Event data is empty.\n");
+			return;
+		}
+
+		if(wan_mode->cradle_wan_mode == BRIDGE)
+		{
+			handle_cradle_wan_mode_switch(true);
+		}
+		else
+		{
+			handle_cradle_wan_mode_switch(false);
+		}
+	}
+	break;
+
 	default:
 		break;
 	}
@@ -2212,17 +2233,6 @@
 		}
 		IPACMDBG_H("Deleted guest ap v4 filter rules successfully.\n");
 #endif
-
-		/* delete icmp filter rules */
-		if(wlan_ap_index == 0)
-		{
-			if(m_filtering.DeleteFilteringHdls(ipv6_icmp_flt_rule_hdl, IPA_IP_v6, NUM_IPV6_ICMP_FLT_RULE) == false)
-			{
-				IPACMERR("Error Deleting ICMPv6 Filtering Rule, aborting...\n");
-				res = IPACM_FAILURE;
-				goto fail;
-			}
-		}
 #ifndef FEATURE_ETH_BRIDGE_LE
 #ifdef CT_OPT
 		IPACMDBG_H("Delete tcp control flt rules.\n");
@@ -2281,6 +2291,16 @@
 	/* Delete v6 filtering rules */
 	if (ip_type != IPA_IP_v4 && rx_prop != NULL)
 	{
+		/* delete icmp filter rules */
+		if(wlan_ap_index == 0)
+		{
+			if(m_filtering.DeleteFilteringHdls(ipv6_icmp_flt_rule_hdl, IPA_IP_v6, NUM_IPV6_ICMP_FLT_RULE) == false)
+			{
+				IPACMERR("Error Deleting ICMPv6 Filtering Rule, aborting...\n");
+				res = IPACM_FAILURE;
+				goto fail;
+			}
+		}
 		IPACMDBG_H("Delete default %d v6 filter rules\n", IPV6_DEFAULT_FILTERTING_RULES);
 		/* delete default filter rules */
 #ifdef FEATURE_ETH_BRIDGE_LE
diff --git a/ipacm/src/IPACM_Xml.cpp b/ipacm/src/IPACM_Xml.cpp
index 6ef4cc0..09fd2e3 100644
--- a/ipacm/src/IPACM_Xml.cpp
+++ b/ipacm/src/IPACM_Xml.cpp
@@ -292,6 +292,26 @@
 						}
 					}
 				}
+				else if (IPACM_util_icmp_string((char*)xml_node->name, MODE_TAG) == 0)
+				{
+					content = IPACM_read_content_element(xml_node);
+					if (content)
+					{
+						str_size = strlen(content);
+						memset(content_buf, 0, sizeof(content_buf));
+						memcpy(content_buf, (void *)content, str_size);
+						if (0 == strncasecmp(content_buf, IFACE_ROUTER_MODE_TAG, str_size))
+						{
+							config->iface_config.iface_entries[config->iface_config.num_iface_entries - 1].if_mode = ROUTER;
+							IPACMDBG_H("Iface mode %d\n", config->iface_config.iface_entries[config->iface_config.num_iface_entries - 1].if_mode);
+						}
+						else  if (0 == strncasecmp(content_buf, IFACE_BRIDGE_MODE_TAG, str_size))
+						{
+							config->iface_config.iface_entries[config->iface_config.num_iface_entries - 1].if_mode = BRIDGE;
+							IPACMDBG_H("Iface mode %d\n", config->iface_config.iface_entries[config->iface_config.num_iface_entries - 1].if_mode);
+						}
+					}
+				}
 				else if (IPACM_util_icmp_string((char*)xml_node->name,
 																				SUBNETADDRESS_TAG) == 0)
 				{
diff --git a/ipacm/src/IPACM_cfg.xml b/ipacm/src/IPACM_cfg.xml
index 95a7796..8f7e886 100644
--- a/ipacm/src/IPACM_cfg.xml
+++ b/ipacm/src/IPACM_cfg.xml
@@ -12,6 +12,7 @@
 			<Iface>
 			   <Name>ecm0</Name>
 			   <Category>LAN</Category>
+			   <Mode>ROUTER</Mode>
 			</Iface>
 			<Iface>
 			   <Name>rmnet_data0</Name>
@@ -144,4 +145,4 @@
  	        <MaxNatEntries>500</MaxNatEntries>
 		</IPACMNAT>
 		</IPACM>
-</system>
\ No newline at end of file
+</system>