IPACM: support ODU project in LE

Enable odu router/bridge mode in ipa-HW for
eMBMS traffic to home router

Change-Id: Icdad38ad8cc8d32a96a05b5512453e539358a5f7
diff --git a/ipacm/inc/IPACM_Config.h b/ipacm/inc/IPACM_Config.h
index c6bb06c..92d4f72 100644
--- a/ipacm/inc/IPACM_Config.h
+++ b/ipacm/inc/IPACM_Config.h
@@ -109,6 +109,10 @@
 
 	int ipa_nat_max_entries;
 
+	bool ipacm_odu_router_mode;
+
+	bool ipacm_odu_enable;
+
 	int ipa_nat_iface_entries;
 
 	/* Store SW-enable or not */
@@ -119,6 +123,8 @@
 	struct ipa_ioc_get_rt_tbl rt_tbl_wan_dl;
 	struct ipa_ioc_get_rt_tbl rt_tbl_lan2lan_v4, rt_tbl_lan2lan_v6;
 
+	struct ipa_ioc_get_rt_tbl rt_tbl_odu_v4, rt_tbl_odu_v6;
+
 	/* To return the instance */
 	static IPACM_Config* GetInstance();
 
@@ -257,6 +263,8 @@
 	}
 #endif /* defined(FEATURE_IPA_ANDROID)*/
 
+	static const char *DEVICE_NAME_ODU;
+
 private:
 	static IPACM_Config *pInstance;
 	static const char *DEVICE_NAME;
diff --git a/ipacm/inc/IPACM_Defs.h b/ipacm/inc/IPACM_Defs.h
index d225f4c..20cde8d 100644
--- a/ipacm/inc/IPACM_Defs.h
+++ b/ipacm/inc/IPACM_Defs.h
@@ -58,12 +58,16 @@
 
 #define IPA_WLAN_PARTIAL_HDR_OFFSET  0 // dst mac first then src mac
 //#define IPA_ETH_PARTIAL_HDR_OFFSET  8 // dst mac first then src mac
+#define IPA_ODU_PARTIAL_HDR_OFFSET  8 // dst mac first then src mac
 #define IPA_WLAN_PARTIAL_HDR_NAME_v4  "IEEE802_3_v4"
 #define IPA_WLAN_PARTIAL_HDR_NAME_v6  "IEEE802_3_v6"
 #define IPA_WAN_PARTIAL_HDR_NAME_v4  "IEEE802_3_STA_v4"
 #define IPA_WAN_PARTIAL_HDR_NAME_v6  "IEEE802_3_STA_v6"
 #define IPA_ETH_HDR_NAME_v4  "IPACM_ETH_v4"
 #define IPA_ETH_HDR_NAME_v6  "IPACM_ETH_v6"
+#define IPA_ODU_HDR_NAME_v4  "IPACM_ODU_v4"
+#define IPA_ODU_HDR_NAME_v6  "IPACM_ODU_v6"
+
 
 #define IPA_MAX_IFACE_ENTRIES 15
 #define IPA_MAX_PRIVATE_SUBNET_ENTRIES 3
@@ -78,6 +82,8 @@
 #define V6_WAN_ROUTE_TABLE_NAME  "WANRTBLv6"
 #define V4_LAN_TO_LAN_ROUTE_TABLE_NAME "LANTOLANRTBLv4"
 #define V6_LAN_TO_LAN_ROUTE_TABLE_NAME "LANTOLANRTBLv6"
+#define V4_ODU_ROUTE_TABLE_NAME  "ODURTBLv4"
+#define V6_ODU_ROUTE_TABLE_NAME  "ODURTBLv6"
 
 #define WWAN_QMI_IOCTL_DEVICE_NAME "/dev/wwan_ioctl"
 #define IPA_DEVICE_NAME "/dev/ipa"
@@ -157,8 +163,9 @@
 	IPA_USB_LINK_UP_EVENT,                    /* 38 ipacm_event_data_fid */
 	IPA_PROCESS_CT_MESSAGE_V6,				  /* 39 ipacm_ct_evt_data */
 	IPA_PRIVATE_SUBNET_CHANGE_EVENT,		  /* 40 ipacm_event_data_fid */
-	IPA_WAN_UPSTREAM_ROUTE_ADD_EVENT,          /* 41 ipacm_event_data_fid */
-	IPA_WAN_UPSTREAM_ROUTE_DEL_EVENT,          /* 42 ipacm_event_data_fid */
+	IPA_WAN_UPSTREAM_ROUTE_ADD_EVENT,         /* 41 ipacm_event_data_fid */
+	IPA_WAN_UPSTREAM_ROUTE_DEL_EVENT,         /* 42 ipacm_event_data_fid */
+	IPA_WAN_EMBMS_LINK_UP_EVENT,              /* 43 ipacm_event_data_mac */
 	IPACM_EVENT_MAX
 } ipa_cm_event_id;
 
@@ -175,6 +182,8 @@
 	WAN_IF,
 	VIRTUAL_IF,
 	ETH_IF,
+	EMBMS_IF,
+	ODU_IF,
 	UNKNOWN_IF
 } ipacm_iface_type;
 
diff --git a/ipacm/inc/IPACM_Lan.h b/ipacm/inc/IPACM_Lan.h
index 64ae9e9..32b135c 100644
--- a/ipacm/inc/IPACM_Lan.h
+++ b/ipacm/inc/IPACM_Lan.h
@@ -52,6 +52,7 @@
 
 #define IPA_WAN_DEFAULT_FILTER_RULE_HANDLES  1
 #define IPA_PRIV_SUBNET_FILTER_RULE_HANDLES  3
+#define IPA_NUM_ODU_ROUTE_RULES 2
 #define MAX_WAN_UL_FILTER_RULES 20
 #define NUM_IPV6_PREFIX_FLT_RULE 1
 
@@ -136,6 +137,10 @@
 	/* handle new_address event*/
 	int handle_addr_evt(ipacm_event_data_addr *data);
 
+	int handle_addr_evt_odu_bridge(ipacm_event_data_addr* data);
+
+	static bool odu_up;
+
 	/* install UL filter rule from Q6 */
 	virtual int handle_uplink_filter_rule(ipacm_ext_prop* prop, ipa_ip_type iptype);
 
@@ -214,6 +219,18 @@
 
 	NatApp *Nat_App;
 
+    int ipv6_set;
+
+	uint32_t ODU_hdr_hdl_v4, ODU_hdr_hdl_v6;
+
+	uint32_t *odu_route_rule_v4_hdl;
+
+	uint32_t *odu_route_rule_v6_hdl;
+
+	bool ipv4_header_set;
+
+	bool ipv6_header_set;
+
 	inline ipa_eth_client* get_client_memptr(ipa_eth_client *param, int cnt)
 	{
 	    char *ret = ((char *)param) + (eth_client_len * cnt);
@@ -326,6 +343,15 @@
 	/*handle eth client del mode*/
 	int handle_eth_client_down_evt(uint8_t *mac_addr);
 
+	/* handle odu client initial, construct full headers (tx property) */
+	int handle_odu_hdr_init(uint8_t *mac_addr);
+
+	/* handle odu default route rule configuration */
+	int handle_odu_route_add();
+
+	/* handle odu default route rule deletion */
+	int handle_odu_route_del();
+
 	/*handle lan iface down event*/
 	int handle_down_evt();
 
diff --git a/ipacm/inc/IPACM_Wan.h b/ipacm/inc/IPACM_Wan.h
index 7923316..7888791 100644
--- a/ipacm/inc/IPACM_Wan.h
+++ b/ipacm/inc/IPACM_Wan.h
@@ -115,6 +115,8 @@
 	static bool is_ext_prop_set;
 	static uint32_t backhaul_ipv6_prefix[2];
 
+	static bool embms_is_on;
+
 private:
 	uint32_t *wan_route_rule_v4_hdl;
 	uint32_t *wan_route_rule_v6_hdl;
@@ -126,6 +128,7 @@
 	uint32_t dft_wan_fl_hdl[IPA_NUM_DEFAULT_WAN_FILTER_RULES];
 	uint32_t ipv6_dest_flt_rule_hdl[MAX_DEFAULT_v6_ROUTE_RULES];
 	int num_ipv6_dest_flt_rule;
+	uint32_t ODU_fl_hdl[IPA_NUM_DEFAULT_WAN_FILTER_RULES];
 	int num_firewall_v4,num_firewall_v6;
 	uint32_t wan_v4_addr;
 	bool active_v4;
@@ -274,6 +277,9 @@
 
 	int config_dft_firewall_rules(ipa_ip_type iptype);
 
+	/* configure the initial firewall filter rules */
+	int config_dft_embms_rules(ipa_ioc_add_flt_rule *pFilteringTable_v4, ipa_ioc_add_flt_rule *pFilteringTable_v6);
+
 	int handle_route_del_evt(ipa_ip_type iptype);
 
 	int del_dft_firewall_rules(ipa_ip_type iptype);
diff --git a/ipacm/inc/IPACM_Xml.h b/ipacm/inc/IPACM_Xml.h
index dfeebbb..90e0289 100644
--- a/ipacm/inc/IPACM_Xml.h
+++ b/ipacm/inc/IPACM_Xml.h
@@ -75,6 +75,10 @@
   
 /* IPA Config Entries */
 #define system_TAG                           "system"
+#define ODU_TAG                              "ODUCFG"
+#define ODUMODE_TAG                          "Mode"
+#define ODU_ROUTER_TAG                       "router"
+#define ODU_BRIDGE_TAG                       "bridge"
 #define IPACMCFG_TAG                         "IPACM"
 #define IPACMIFACECFG_TAG                    "IPACMIface"
 #define IFACE_TAG                            "Iface"
@@ -89,6 +93,8 @@
 #define WLANIF_TAG                           "WLAN"
 #define VIRTUALIF_TAG                        "VIRTUAL"
 #define UNKNOWNIF_TAG                        "UNKNOWN"
+#define ODUIF_TAG                            "ODU"
+#define EMBMSIF_TAG                          "EMBMS"
 #define ETHIF_TAG                            "ETH"
 #define IPACMALG_TAG                         "IPACMALG"
 #define ALG_TAG                              "ALG"
@@ -259,6 +265,8 @@
   ipacm_private_subnet_conf_t private_subnet_config;
   ipacm_alg_conf_t alg_config;
 	int nat_max_entries;
+  bool odu_enable;
+  bool router_mode_enable;
 } IPACM_conf_t;  
 
 /* This function read IPACM XML configuration*/
diff --git a/ipacm/src/IPACM_Config.cpp b/ipacm/src/IPACM_Config.cpp
index a934e97..997f64e 100644
--- a/ipacm/src/IPACM_Config.cpp
+++ b/ipacm/src/IPACM_Config.cpp
@@ -45,6 +45,7 @@
 
 IPACM_Config *IPACM_Config::pInstance = NULL;
 const char *IPACM_Config::DEVICE_NAME = "/dev/ipa";
+const char *IPACM_Config::DEVICE_NAME_ODU = "/dev/odu_ipa_bridge";
 
 IPACM_Config::IPACM_Config()
 {
@@ -52,7 +53,9 @@
 	alg_table = NULL;
 	memset(&ipa_client_rm_map_tbl, 0, sizeof(ipa_client_rm_map_tbl));
 	memset(&ipa_rm_tbl, 0, sizeof(ipa_rm_tbl));
-	ipa_rm_a2_check=0;
+    ipa_rm_a2_check=0;
+	ipacm_odu_enable = false;
+	ipacm_odu_router_mode = false;
 
 	ipa_num_ipa_interfaces = 0;
 	ipa_num_private_subnet = 0;
@@ -67,6 +70,8 @@
 	memset(&rt_tbl_v6, 0, sizeof(rt_tbl_v6));
 	memset(&rt_tbl_wan_v6, 0, sizeof(rt_tbl_wan_v6));
 	memset(&rt_tbl_wan_dl, 0, sizeof(rt_tbl_wan_dl));
+	memset(&rt_tbl_odu_v4, 0, sizeof(rt_tbl_odu_v4));
+	memset(&rt_tbl_odu_v6, 0, sizeof(rt_tbl_odu_v6));
 
 	memset(&ext_prop_v4, 0, sizeof(ext_prop_v4));
 	memset(&ext_prop_v6, 0, sizeof(ext_prop_v6));
@@ -194,6 +199,12 @@
 	ipa_nat_max_entries = cfg->nat_max_entries;
 	IPACMDBG_H("Nat Maximum Entries %d\n", ipa_nat_max_entries);
 
+	/* Find ODU is either router mode or bridge mode*/
+	ipacm_odu_enable = cfg->odu_enable;
+	ipacm_odu_router_mode = cfg->router_mode_enable;
+	IPACMDBG_H("ipacm_odu_enable %d\n", ipacm_odu_enable);
+	IPACMDBG_H("ipacm_odu_mode %d\n", ipacm_odu_router_mode);
+
 	/* Allocate more non-nat entries if the monitored iface dun have Tx/Rx properties */
 	if (pNatIfaces != NULL)
 	{
@@ -227,6 +238,12 @@
 	rt_tbl_wan_v6.ip = IPA_IP_v6;
 	strncpy(rt_tbl_wan_v6.name, V6_WAN_ROUTE_TABLE_NAME, sizeof(rt_tbl_wan_v6.name));
 
+	rt_tbl_odu_v4.ip = IPA_IP_v4;
+	strncpy(rt_tbl_odu_v4.name, V4_ODU_ROUTE_TABLE_NAME, sizeof(rt_tbl_odu_v4.name));
+
+	rt_tbl_odu_v6.ip = IPA_IP_v6;
+	strncpy(rt_tbl_odu_v6.name, V6_ODU_ROUTE_TABLE_NAME, sizeof(rt_tbl_odu_v6.name));
+
 	rt_tbl_wan_dl.ip = IPA_IP_MAX;
 	strncpy(rt_tbl_wan_dl.name, WAN_DL_ROUTE_TABLE_NAME, sizeof(rt_tbl_wan_dl.name));
 
diff --git a/ipacm/src/IPACM_IfaceManager.cpp b/ipacm/src/IPACM_IfaceManager.cpp
index af9811c..d38a58e 100644
--- a/ipacm/src/IPACM_IfaceManager.cpp
+++ b/ipacm/src/IPACM_IfaceManager.cpp
@@ -60,6 +60,7 @@
 	IPACM_EvtDispatcher::registr(IPA_WLAN_STA_LINK_UP_EVENT, this); // register for wlan STA-iface
 #endif /* not defined(FEATURE_IPA_ANDROID)*/
 	IPACM_EvtDispatcher::registr(IPA_USB_LINK_UP_EVENT, this); // register for wlan STA-iface
+	IPACM_EvtDispatcher::registr(IPA_WAN_EMBMS_LINK_UP_EVENT, this);  // register for wan eMBMS-iface
 	return;
 }
 
@@ -77,10 +78,14 @@
 			IPACMDBG_H("link up %d: \n", evt_data->if_index);
 			ipa_interface_index = IPACM_Iface::iface_ipa_index_query(evt_data->if_index);
 			/* LTE-backhaul */
-			if(IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].if_cat == WAN_IF)
+			if(IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].if_cat == EMBMS_IF)
+			{
+				IPACMDBG("WAN-EMBMS (%s) link already up, iface: %d: \n", IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].iface_name,evt_data->if_index);
+			}
+			else if(IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].if_cat == WAN_IF)
 			{
 				IPACMDBG_H("WAN-LTE (%s) link up, iface: %d: \n", IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].iface_name,evt_data->if_index);
-			create_iface_instance(evt_data->if_index, Q6_WAN);
+				create_iface_instance(evt_data->if_index, Q6_WAN);
 			}
 			break;
 
@@ -90,7 +95,7 @@
 			if(IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].if_cat == WAN_IF)
 			{
 				/* usb-backhaul using sta_mode ECM_WAN*/
-			IPACMDBG_H("WAN-usb (%s) link up, iface: %d: \n", IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].iface_name,evt_data->if_index);
+				IPACMDBG_H("WAN-usb (%s) link up, iface: %d: \n", IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].iface_name,evt_data->if_index);
 				create_iface_instance(evt_data->if_index, ECM_WAN);
 			}
 			else
@@ -104,13 +109,13 @@
 			/* change iface category from unknown to WLAN_IF */
 			if(IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].if_cat == UNKNOWN_IF)
 			{
-			IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].if_cat=WLAN_IF;
-			IPACMDBG_H("WLAN AP (%s) link up, iface: %d: \n", IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].iface_name,evt_data->if_index);
-			create_iface_instance(evt_data->if_index, Q6_WAN);
+				IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].if_cat=WLAN_IF;
+				IPACMDBG_H("WLAN AP (%s) link up, iface: %d: \n", IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].iface_name,evt_data->if_index);
+				create_iface_instance(evt_data->if_index, Q6_WAN);
 			}
 			else
 			{
-			IPACMDBG_H("iface %s already up and act as %d mode: \n",IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].iface_name,IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].if_cat);
+				IPACMDBG_H("iface %s already up and act as %d mode: \n",IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].iface_name,IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].if_cat);
 			}
 			break;
 
@@ -120,13 +125,33 @@
 			if(IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].if_cat==UNKNOWN_IF)
 			{
 				/* wlan-backhaul using sta_mode WLAN_WAN */
-			IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].if_cat=WAN_IF;
-			IPACMDBG_H("WLAN STA (%s) link up, iface: %d: \n", IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].iface_name,evt_data->if_index);
+				IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].if_cat=WAN_IF;
+				IPACMDBG_H("WLAN STA (%s) link up, iface: %d: \n", IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].iface_name,evt_data->if_index);
 				create_iface_instance(evt_data->if_index, WLAN_WAN);
 			}
 			else
 			{
-			IPACMDBG_H("iface %s already up and act as %d mode: \n",IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].iface_name,IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].if_cat);
+				IPACMDBG_H("iface %s already up and act as %d mode: \n",IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].iface_name,IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].if_cat);
+			}
+			break;
+
+		/* Add new instance open for eMBMS iface and wan iface */
+		case IPA_WAN_EMBMS_LINK_UP_EVENT:
+			ipa_interface_index = IPACM_Iface::iface_ipa_index_query(evt_data->if_index);
+			/* change iface category from unknown to EMBMS_IF */
+			if (IPACM_Iface::ipacmcfg->ipacm_odu_enable == true)
+			{
+				IPACMDBG(" ODU-mode enable or not (%d) \n",IPACM_Iface::ipacmcfg->ipacm_odu_enable);
+			if(IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].if_cat == WAN_IF)
+			{
+				IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].if_cat=EMBMS_IF;
+				IPACMDBG("WAN eMBMS (%s) link up, iface: %d: \n", IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].iface_name,evt_data->if_index);
+				create_iface_instance(evt_data->if_index, Q6_WAN);
+			}
+			else
+			{
+				IPACMDBG("iface %s already up and act as %d mode: \n",IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].iface_name,IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].if_cat);
+			}
 			}
 			break;
 
@@ -186,9 +211,14 @@
 					IPACM_Lan *ETH = new IPACM_Lan(ipa_interface_index);
 					IPACM_EvtDispatcher::registr(IPA_ADDR_ADD_EVENT, ETH);
 					IPACM_EvtDispatcher::registr(IPA_NEIGH_CLIENT_IP_ADDR_ADD_EVENT, ETH);
+					IPACM_EvtDispatcher::registr(IPA_SW_ROUTING_ENABLE, ETH);
+					IPACM_EvtDispatcher::registr(IPA_SW_ROUTING_DISABLE, ETH);
 					IPACM_EvtDispatcher::registr(IPA_HANDLE_WAN_UP, ETH);
+					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_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);
 					registr(ipa_interface_index, ETH);
 					/* solve the new_addr comes earlier issue */
@@ -196,6 +226,45 @@
 			}
 			break;
 
+		case ODU_IF:
+			{
+				if(IPACM_Iface::ipacmcfg->ipacm_odu_router_mode == true)
+				{
+					IPACMDBG("Creating ODU interface in router mode\n");
+					IPACM_Lan *odu = new IPACM_Lan(ipa_interface_index);
+					IPACM_EvtDispatcher::registr(IPA_ADDR_ADD_EVENT, odu);
+					IPACM_EvtDispatcher::registr(IPA_NEIGH_CLIENT_IP_ADDR_ADD_EVENT, odu);
+					IPACM_EvtDispatcher::registr(IPA_SW_ROUTING_ENABLE, odu);
+					IPACM_EvtDispatcher::registr(IPA_SW_ROUTING_DISABLE, odu);
+					IPACM_EvtDispatcher::registr(IPA_HANDLE_WAN_UP, odu);
+					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_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);
+					registr(ipa_interface_index, odu);
+					/* solve the new_addr comes earlier issue */
+					IPACM_Iface::iface_addr_query(if_index);
+				}
+				else
+				{
+					IPACMDBG("Creating ODU interface in bridge mode\n");
+					IPACM_Lan *odu = new IPACM_Lan(ipa_interface_index);
+					IPACM_EvtDispatcher::registr(IPA_ADDR_ADD_EVENT, odu);
+					IPACM_EvtDispatcher::registr(IPA_NEIGH_CLIENT_IP_ADDR_ADD_EVENT, odu);
+					IPACM_EvtDispatcher::registr(IPA_SW_ROUTING_ENABLE, odu);
+					IPACM_EvtDispatcher::registr(IPA_SW_ROUTING_DISABLE, 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);
+					registr(ipa_interface_index, odu);
+					/* solve the new_addr comes earlier issue */
+					IPACM_Iface::iface_addr_query(if_index);
+				}
+			}
+			break;
+
 		case WLAN_IF:
 			{
 				IPACMDBG_H("Creating WLan interface\n");
@@ -226,32 +295,46 @@
 
 		case WAN_IF:
 			{
-				IPACMDBG_H("Creating Wan interface\n");
-				IPACM_Wan *w = new IPACM_Wan(ipa_interface_index, is_sta_mode);
-				IPACM_EvtDispatcher::registr(IPA_ADDR_ADD_EVENT, w);
+				if((IPACM_Iface::ipacmcfg->ipacm_odu_enable == false) || (IPACM_Iface::ipacmcfg->ipacm_odu_router_mode == true))
+				{
+					IPACMDBG_H("Creating Wan interface\n");
+					IPACM_Wan *w = new IPACM_Wan(ipa_interface_index, is_sta_mode);
+					IPACM_EvtDispatcher::registr(IPA_ADDR_ADD_EVENT, w);
 #ifdef FEATURE_IPA_ANDROID
 				IPACM_EvtDispatcher::registr(IPA_WAN_UPSTREAM_ROUTE_ADD_EVENT, w);
 #else/* defined(FEATURE_IPA_ANDROID) */
 				IPACM_EvtDispatcher::registr(IPA_ROUTE_ADD_EVENT, w);
 				IPACM_EvtDispatcher::registr(IPA_ROUTE_DEL_EVENT, w);
 #endif /* not defined(FEATURE_IPA_ANDROID)*/
-				IPACM_EvtDispatcher::registr(IPA_FIREWALL_CHANGE_EVENT, w);
-				IPACM_EvtDispatcher::registr(IPA_NEIGH_CLIENT_IP_ADDR_ADD_EVENT, w);
-				IPACM_EvtDispatcher::registr(IPA_SW_ROUTING_ENABLE, w);
-				IPACM_EvtDispatcher::registr(IPA_SW_ROUTING_DISABLE, w);
-				IPACM_EvtDispatcher::registr(IPA_CFG_CHANGE_EVENT, w); 		// register for IPA_CFG_CHANGE event
-				if(is_sta_mode == WLAN_WAN)
-				{
-					IPACM_EvtDispatcher::registr(IPA_WLAN_LINK_DOWN_EVENT, w); // for STA mode
+					IPACM_EvtDispatcher::registr(IPA_FIREWALL_CHANGE_EVENT, w);
+					IPACM_EvtDispatcher::registr(IPA_NEIGH_CLIENT_IP_ADDR_ADD_EVENT, w);
+					IPACM_EvtDispatcher::registr(IPA_SW_ROUTING_ENABLE, w);
+					IPACM_EvtDispatcher::registr(IPA_SW_ROUTING_DISABLE, w);
+					IPACM_EvtDispatcher::registr(IPA_CFG_CHANGE_EVENT, w); 		// register for IPA_CFG_CHANGE event
+					if(is_sta_mode == WLAN_WAN)
+					{
+						IPACM_EvtDispatcher::registr(IPA_WLAN_LINK_DOWN_EVENT, w); // for STA mode
+					}
+					else
+					{
+						IPACM_EvtDispatcher::registr(IPA_LINK_DOWN_EVENT, w);
+					}
+					IPACMDBG_H("ipa_WAN (%s):ipa_index (%d) instance open/registr ok\n", w->dev_name, w->ipa_if_num);
+					registr(ipa_interface_index, w);
+					/* solve the new_addr comes earlier issue */
+									IPACM_Iface::iface_addr_query(if_index);
 				}
-				else
-				{
-					IPACM_EvtDispatcher::registr(IPA_LINK_DOWN_EVENT, w);
-				}
-				IPACMDBG_H("ipa_WAN (%s):ipa_index (%d) instance open/registr ok\n", w->dev_name, w->ipa_if_num);
-				registr(ipa_interface_index, w);
-				/* solve the new_addr comes earlier issue */
-                                IPACM_Iface::iface_addr_query(if_index);
+			}
+			break;
+
+	    /* WAN-eMBMS instance */
+		case EMBMS_IF:
+			{
+				IPACMDBG("Creating Wan-eMBSM interface\n");
+				IPACM_Wan *embms = new IPACM_Wan(ipa_interface_index, is_sta_mode);
+				IPACM_EvtDispatcher::registr(IPA_LINK_DOWN_EVENT, embms);
+				IPACMDBG("ipa_WAN (%s):ipa_index (%d) instance open/registr ok\n", embms->dev_name, embms->ipa_if_num);
+				registr(ipa_interface_index, embms);
 			}
 			break;
 
diff --git a/ipacm/src/IPACM_Lan.cpp b/ipacm/src/IPACM_Lan.cpp
index 5e1c36a..fc1b4f6 100644
--- a/ipacm/src/IPACM_Lan.cpp
+++ b/ipacm/src/IPACM_Lan.cpp
@@ -50,12 +50,19 @@
 #include "linux/ipa_qmi_service_v01.h"
 #include "linux/msm_ipa.h"
 #include "IPACM_ConntrackListener.h"
+#include <sys/ioctl.h>
+#include <fcntl.h>
 
+bool IPACM_Lan::odu_up = false;
 
 IPACM_Lan::IPACM_Lan(int iface_index) : IPACM_Iface(iface_index)
 {
 	num_eth_client = 0;
 	header_name_count = 0;
+	ipv6_set = 0;
+	ipv4_header_set = false;
+	ipv6_header_set = false;
+	int m_fd_odu, ret = IPACM_SUCCESS;
 
 	Nat_App = NatApp::GetInstance();
 	if (Nat_App == NULL)
@@ -102,6 +109,44 @@
 	if_ipv4_subnet =0;
 	memset(private_fl_rule_hdl, 0, IPA_MAX_PRIVATE_SUBNET_ENTRIES * sizeof(uint32_t));
 	memset(ipv6_prefix_flt_rule_hdl, 0, NUM_IPV6_PREFIX_FLT_RULE * sizeof(uint32_t));
+
+	/* ODU routing table initilization */
+	if(IPACM_Iface::ipacmcfg->iface_table[ipa_if_num].if_cat == ODU_IF)
+	{
+		odu_route_rule_v4_hdl = (uint32_t *)calloc(iface_query->num_tx_props, sizeof(uint32_t));
+		odu_route_rule_v6_hdl = (uint32_t *)calloc(iface_query->num_tx_props, sizeof(uint32_t));
+
+		/* only do one time ioctl to odu-driver to infrom in router or bridge mode*/
+		if (IPACM_Lan::odu_up != true)
+		{
+				m_fd_odu = open(IPACM_Iface::ipacmcfg->DEVICE_NAME_ODU, O_RDWR);
+				if (0 == m_fd_odu)
+				{
+					IPACMERR("Failed opening %s.\n", IPACM_Iface::ipacmcfg->DEVICE_NAME_ODU);
+					return ;
+				}
+
+				if(IPACM_Iface::ipacmcfg->ipacm_odu_router_mode == true)
+				{
+					ret = ioctl(m_fd_odu, ODU_BRIDGE_IOC_SET_MODE, ODU_BRIDGE_MODE_ROUTER);
+					IPACM_Iface::ipacmcfg->ipacm_odu_enable = true;
+				}
+				else
+				{
+					ret = ioctl(m_fd_odu, ODU_BRIDGE_IOC_SET_MODE, ODU_BRIDGE_MODE_BRIDGE);
+					IPACM_Iface::ipacmcfg->ipacm_odu_enable = true;
+				}
+
+				if (ret)
+				{
+					IPACMERR("Failed tell odu-driver the mode\n");
+				}
+				IPACMDBG("Tell odu-driver in router-mode(%d)\n", IPACM_Iface::ipacmcfg->ipacm_odu_router_mode);
+				close(m_fd_odu);
+				IPACM_Lan::odu_up = true;
+		}
+	}
+
 	return;
 }
 
@@ -231,14 +276,13 @@
 			{
 				IPACMDBG_H("Received IPA_ADDR_ADD_EVENT\n");
 
-				/* check v4 not setup before, v6 can have 2 iface ip */
-				if( ((data->iptype != ip_type) && (ip_type != IPA_IP_MAX))
-				    || ((data->iptype==IPA_IP_v6) && (num_dft_rt_v6!=MAX_DEFAULT_v6_ROUTE_RULES)))
+				/* only call ioctl for ODU iface with bridge mode */
+				if((IPACM_Iface::ipacmcfg->ipacm_odu_enable == true) && (IPACM_Iface::ipacmcfg->ipacm_odu_router_mode == false)
+						&& (IPACM_Iface::ipacmcfg->iface_table[ipa_if_num].if_cat == ODU_IF))
 				{
-				  IPACMDBG_H("Got IPA_ADDR_ADD_EVENT ip-family:%d, v6 num %d: \n",data->iptype,num_dft_rt_v6);
-					if(handle_addr_evt(data) == IPACM_FAILURE)
+					if((data->iptype == IPA_IP_v6) && (num_dft_rt_v6 == 0))
 					{
-						return;
+						handle_addr_evt_odu_bridge(data);
 					}
 #ifdef FEATURE_IPA_ANDROID
 					add_dummy_private_subnet_flt_rule(data->iptype);
@@ -246,67 +290,84 @@
 #else
 					handle_private_subnet(data->iptype);
 #endif
+				}
+				else
+				{
 
-					if (IPACM_Wan::isWanUP())
+					/* check v4 not setup before, v6 can have 2 iface ip */
+					if( ((data->iptype != ip_type) && (ip_type != IPA_IP_MAX))
+						|| ((data->iptype==IPA_IP_v6) && (num_dft_rt_v6!=MAX_DEFAULT_v6_ROUTE_RULES)))
 					{
-						if(data->iptype == IPA_IP_v4 || data->iptype == IPA_IP_MAX)
+					IPACMDBG_H("Got IPA_ADDR_ADD_EVENT ip-family:%d, v6 num %d: \n",data->iptype,num_dft_rt_v6);
+						if(handle_addr_evt(data) == IPACM_FAILURE)
 						{
-						if(IPACM_Wan::backhaul_is_sta_mode == false)
-						{
-								ext_prop = IPACM_Iface::ipacmcfg->GetExtProp(IPA_IP_v4);
-								handle_wan_up_ex(ext_prop, IPA_IP_v4);
-							}
-						else
-						{
-							handle_wan_up(IPA_IP_v4);
-						}
-					}
-					}
-
-					if(IPACM_Wan::isWanUP_V6())
-					{
-						if((data->iptype == IPA_IP_v6 || data->iptype == IPA_IP_MAX) && num_dft_rt_v6 == 1)
-						{
-							install_ipv6_prefix_flt_rule(IPACM_Wan::backhaul_ipv6_prefix);
-						if(IPACM_Wan::backhaul_is_sta_mode == false)
-						{
-								ext_prop = IPACM_Iface::ipacmcfg->GetExtProp(IPA_IP_v6);
-								handle_wan_up_ex(ext_prop, IPA_IP_v6);
-							}
-						else
-						{
-							handle_wan_up(IPA_IP_v6);
-						}
-					}
-					}
-
-					/* Post event to NAT */
-					if (data->iptype == IPA_IP_v4)
-					{
-						ipacm_cmd_q_data evt_data;
-						ipacm_event_iface_up *info;
-
-						info = (ipacm_event_iface_up *)
-							 malloc(sizeof(ipacm_event_iface_up));
-						if (info == NULL)
-						{
-							IPACMERR("Unable to allocate memory\n");
 							return;
 						}
+						handle_private_subnet(data->iptype);
 
-						memcpy(info->ifname, dev_name, IF_NAME_LEN);
-						info->ipv4_addr = data->ipv4_addr;
-						info->addr_mask = IPACM_Iface::ipacmcfg->private_subnet_table[0].subnet_mask;
+						if (IPACM_Wan::isWanUP())
+						{
+							if(data->iptype == IPA_IP_v4 || data->iptype == IPA_IP_MAX)
+							{
+							if(IPACM_Wan::backhaul_is_sta_mode == false)
+							{
+									ext_prop = IPACM_Iface::ipacmcfg->GetExtProp(IPA_IP_v4);
+									handle_wan_up_ex(ext_prop, IPA_IP_v4);
+								}
+							else
+							{
+								handle_wan_up(IPA_IP_v4);
+							}
+						}
+						}
 
-						evt_data.event = IPA_HANDLE_LAN_UP;
-						evt_data.evt_data = (void *)info;
+						if(IPACM_Wan::isWanUP_V6())
+						{
+							if((data->iptype == IPA_IP_v6 || data->iptype == IPA_IP_MAX) && num_dft_rt_v6 == 1)
+							{
+								install_ipv6_prefix_flt_rule(IPACM_Wan::backhaul_ipv6_prefix);
+							if(IPACM_Wan::backhaul_is_sta_mode == false)
+							{
+									ext_prop = IPACM_Iface::ipacmcfg->GetExtProp(IPA_IP_v6);
+									handle_wan_up_ex(ext_prop, IPA_IP_v6);
+								}
+							else
+							{
+								handle_wan_up(IPA_IP_v6);
+							}
+						}
+						}
 
-						/* Insert IPA_HANDLE_LAN_UP to command queue */
-						IPACMDBG_H("posting IPA_HANDLE_LAN_UP for IPv4 with below information\n");
-						IPACMDBG_H("IPv4 address:0x%x, IPv4 address mask:0x%x\n",
-										 info->ipv4_addr, info->addr_mask);
-						IPACM_EvtDispatcher::PostEvt(&evt_data);
+						/* Post event to NAT */
+						if (data->iptype == IPA_IP_v4)
+						{
+							ipacm_cmd_q_data evt_data;
+							ipacm_event_iface_up *info;
+
+							info = (ipacm_event_iface_up *)
+								malloc(sizeof(ipacm_event_iface_up));
+							if (info == NULL)
+							{
+								IPACMERR("Unable to allocate memory\n");
+								return;
+							}
+
+							memcpy(info->ifname, dev_name, IF_NAME_LEN);
+							info->ipv4_addr = data->ipv4_addr;
+							info->addr_mask = IPACM_Iface::ipacmcfg->private_subnet_table[0].subnet_mask;
+
+							evt_data.event = IPA_HANDLE_LAN_UP;
+							evt_data.evt_data = (void *)info;
+
+							/* Insert IPA_HANDLE_LAN_UP to command queue */
+							IPACMDBG_H("posting IPA_HANDLE_LAN_UP for IPv4 with below information\n");
+							IPACMDBG_H("IPv4 address:0x%x, IPv4 address mask:0x%x\n",
+											info->ipv4_addr, info->addr_mask);
+							IPACM_EvtDispatcher::PostEvt(&evt_data);
+						}
+						IPACMDBG_H("Finish handling IPA_ADDR_ADD_EVENT for ip-family(%d)\n", data->iptype);
 					}
+
 					IPACMDBG_H("Finish handling IPA_ADDR_ADD_EVENT for ip-family(%d)\n", data->iptype);
 					/* checking if SW-RT_enable */
 					if (IPACM_Iface::ipacmcfg->ipa_sw_rt_enable == true)
@@ -315,6 +376,7 @@
 						IPACMDBG_H("IPA_SW_ROUTING_ENABLE for iface: %s \n",IPACM_Iface::ipacmcfg->iface_table[ipa_if_num].iface_name);
 						handle_software_routing_enable();
 					}
+
 				}
 			}
 		}
@@ -409,6 +471,25 @@
 			ipacm_event_data_all *data = (ipacm_event_data_all *)param;
 			ipa_interface_index = iface_ipa_index_query(data->if_index);
 			IPACMDBG_H("check iface %s category: %d\n",IPACM_Iface::ipacmcfg->iface_table[ipa_if_num].iface_name, IPACM_Iface::ipacmcfg->iface_table[ipa_if_num].if_cat);
+
+			if ((ipa_interface_index == ipa_if_num) && (IPACM_Iface::ipacmcfg->iface_table[ipa_if_num].if_cat == ODU_IF))
+			{
+				IPACMDBG("ODU iface got v4-ip \n");
+				/* first construc ODU full header */
+				if ((ipv4_header_set == false) && (ipv6_header_set == false))
+				{
+					handle_odu_hdr_init(data->mac_addr);
+					handle_odu_route_add(); /* construct ODU RT tbl*/
+					IPACMDBG("construct ODU header and route rules \n");
+				}
+				/* if ODU in bridge mode, directly return */
+				if(IPACM_Iface::ipacmcfg->ipacm_odu_router_mode == false)
+				{
+				  return;
+				}
+
+			}
+
 			if (ipa_interface_index == ipa_if_num)
 			{
 				IPACMDBG_H("ETH iface got client \n");
@@ -1449,6 +1530,10 @@
 				rt_rule_entry->rule.attrib.u.v4.dst_addr = get_client_memptr(eth_client, eth_index)->v4_addr;
 				rt_rule_entry->rule.attrib.u.v4.dst_addr_mask = 0xFFFFFFFF;
 
+				/* Replace the v4 header in ODU interface */
+				if (IPACM_Iface::ipacmcfg->iface_table[ipa_if_num].if_cat == ODU_IF)
+				rt_rule_entry->rule.hdr_hdl = ODU_hdr_hdl_v4;
+
 			    if (false == m_routing.AddRoutingRule(rt_rule))
   	            {
   	          	            IPACMERR("Routing rule addition failed!\n");
@@ -1475,6 +1560,10 @@
 			    					IPACM_Iface::ipacmcfg->rt_tbl_v6.name,
 			    					sizeof(rt_rule->rt_tbl_name));
 
+				   /* Replace v6 header in ODU interface */
+				   if (IPACM_Iface::ipacmcfg->iface_table[ipa_if_num].if_cat == ODU_IF)
+						rt_rule_entry->rule.hdr_hdl = ODU_hdr_hdl_v6;
+
 		            /* Support QCMAP LAN traffic feature, send to A5 */
 					rt_rule_entry->rule.dst = iface_query->excp_pipe;
 			        memset(&rt_rule_entry->rule.attrib, 0, sizeof(rt_rule_entry->rule.attrib));
@@ -1551,6 +1640,325 @@
 	return IPACM_SUCCESS;
 }
 
+/* handle odu client initial, construct full headers (tx property) */
+int IPACM_Lan::handle_odu_hdr_init(uint8_t *mac_addr)
+{
+	int res = IPACM_SUCCESS, len = 0;
+	struct ipa_ioc_copy_hdr sCopyHeader;
+	struct ipa_ioc_add_hdr *pHeaderDescriptor = NULL;
+    uint32_t cnt;
+
+	IPACMDBG("Received Client MAC %02x:%02x:%02x:%02x:%02x:%02x\n",
+					 mac_addr[0], mac_addr[1], mac_addr[2],
+					 mac_addr[3], mac_addr[4], mac_addr[5]);
+
+	/* add header to IPA */
+	if(tx_prop != NULL)
+	{
+		len = sizeof(struct ipa_ioc_add_hdr) + (1 * sizeof(struct ipa_hdr_add));
+		pHeaderDescriptor = (struct ipa_ioc_add_hdr *)calloc(1, len);
+		if (pHeaderDescriptor == NULL)
+		{
+			IPACMERR("calloc failed to allocate pHeaderDescriptor\n");
+			return IPACM_FAILURE;
+		}
+
+		/* copy partial header for v4*/
+		for (cnt=0; cnt<tx_prop->num_tx_props; cnt++)
+		{
+				 if(tx_prop->tx[cnt].ip==IPA_IP_v4)
+				 {
+								IPACMDBG("Got partial v4-header name from %d tx props\n", cnt);
+								memset(&sCopyHeader, 0, sizeof(sCopyHeader));
+								memcpy(sCopyHeader.name,
+											tx_prop->tx[cnt].hdr_name,
+											 sizeof(sCopyHeader.name));
+								IPACMDBG("header name: %s in tx:%d\n", sCopyHeader.name,cnt);
+								if (m_header.CopyHeader(&sCopyHeader) == false)
+								{
+									PERROR("ioctl copy header failed");
+									res = IPACM_FAILURE;
+									goto fail;
+								}
+								IPACMDBG("header length: %d, paritial: %d\n", sCopyHeader.hdr_len, sCopyHeader.is_partial);
+								if (sCopyHeader.hdr_len > IPA_HDR_MAX_SIZE)
+								{
+									IPACMERR("header oversize\n");
+									res = IPACM_FAILURE;
+									goto fail;
+								}
+								else
+								{
+									memcpy(pHeaderDescriptor->hdr[0].hdr,
+												 sCopyHeader.hdr,
+												 sCopyHeader.hdr_len);
+								}
+								/* copy client mac_addr to partial header */
+								if (sCopyHeader.is_eth2_ofst_valid)
+								{
+									memcpy(&pHeaderDescriptor->hdr[0].hdr[sCopyHeader.eth2_ofst],
+											 mac_addr,
+											 IPA_MAC_ADDR_SIZE);
+								}
+
+
+								pHeaderDescriptor->commit = true;
+								pHeaderDescriptor->num_hdrs = 1;
+
+								memset(pHeaderDescriptor->hdr[0].name, 0,
+											 sizeof(pHeaderDescriptor->hdr[0].name));
+
+											 strncat(pHeaderDescriptor->hdr[0].name,
+												IPA_ODU_HDR_NAME_v4,
+												sizeof(IPA_ODU_HDR_NAME_v4));
+
+								pHeaderDescriptor->hdr[0].hdr_len = sCopyHeader.hdr_len;
+								pHeaderDescriptor->hdr[0].hdr_hdl = -1;
+								pHeaderDescriptor->hdr[0].is_partial = 0;
+								pHeaderDescriptor->hdr[0].status = -1;
+
+					 if (m_header.AddHeader(pHeaderDescriptor) == false ||
+							pHeaderDescriptor->hdr[0].status != 0)
+					 {
+						IPACMERR("ioctl IPA_IOC_ADD_HDR failed: %d\n", pHeaderDescriptor->hdr[0].status);
+						res = IPACM_FAILURE;
+						goto fail;
+					 }
+
+					ODU_hdr_hdl_v4 = pHeaderDescriptor->hdr[0].hdr_hdl;
+					ipv4_header_set = true ;
+					IPACMDBG(" ODU v4 full header name:%s header handle:(0x%x)\n",
+										 pHeaderDescriptor->hdr[0].name,
+												 ODU_hdr_hdl_v4);
+					break;
+				 }
+		}
+
+
+		/* copy partial header for v6*/
+		for (cnt=0; cnt<tx_prop->num_tx_props; cnt++)
+		{
+			if(tx_prop->tx[cnt].ip==IPA_IP_v6)
+			{
+
+				IPACMDBG("Got partial v6-header name from %d tx props\n", cnt);
+				memset(&sCopyHeader, 0, sizeof(sCopyHeader));
+				memcpy(sCopyHeader.name,
+						tx_prop->tx[cnt].hdr_name,
+							sizeof(sCopyHeader.name));
+
+				IPACMDBG("header name: %s in tx:%d\n", sCopyHeader.name,cnt);
+				if (m_header.CopyHeader(&sCopyHeader) == false)
+				{
+					PERROR("ioctl copy header failed");
+					res = IPACM_FAILURE;
+					goto fail;
+				}
+
+				IPACMDBG("header length: %d, paritial: %d\n", sCopyHeader.hdr_len, sCopyHeader.is_partial);
+				if (sCopyHeader.hdr_len > IPA_HDR_MAX_SIZE)
+				{
+					IPACMERR("header oversize\n");
+					res = IPACM_FAILURE;
+					goto fail;
+				}
+				else
+				{
+					memcpy(pHeaderDescriptor->hdr[0].hdr,
+							sCopyHeader.hdr,
+								sCopyHeader.hdr_len);
+				}
+
+				/* copy client mac_addr to partial header */
+				if (sCopyHeader.is_eth2_ofst_valid)
+				{
+					memcpy(&pHeaderDescriptor->hdr[0].hdr[sCopyHeader.eth2_ofst],
+					 mac_addr,
+					 IPA_MAC_ADDR_SIZE);
+				}
+
+				pHeaderDescriptor->commit = true;
+				pHeaderDescriptor->num_hdrs = 1;
+
+				memset(pHeaderDescriptor->hdr[0].name, 0,
+					 sizeof(pHeaderDescriptor->hdr[0].name));
+
+				strncat(pHeaderDescriptor->hdr[0].name,
+						IPA_ODU_HDR_NAME_v6,
+						sizeof(IPA_ODU_HDR_NAME_v6));
+
+				pHeaderDescriptor->hdr[0].hdr_len = sCopyHeader.hdr_len;
+				pHeaderDescriptor->hdr[0].hdr_hdl = -1;
+				pHeaderDescriptor->hdr[0].is_partial = 0;
+				pHeaderDescriptor->hdr[0].status = -1;
+
+				if (m_header.AddHeader(pHeaderDescriptor) == false ||
+						pHeaderDescriptor->hdr[0].status != 0)
+				{
+					IPACMERR("ioctl IPA_IOC_ADD_HDR failed: %d\n", pHeaderDescriptor->hdr[0].status);
+					res = IPACM_FAILURE;
+					goto fail;
+				}
+
+				ODU_hdr_hdl_v6 = pHeaderDescriptor->hdr[0].hdr_hdl;
+				ipv6_header_set = true ;
+				IPACMDBG(" ODU v4 full header name:%s header handle:(0x%x)\n",
+									 pHeaderDescriptor->hdr[0].name,
+											 ODU_hdr_hdl_v6);
+				break;
+			}
+		}
+	}
+fail:
+	free(pHeaderDescriptor);
+
+	return res;
+}
+
+
+/* handle odu default route rule configuration */
+int IPACM_Lan::handle_odu_route_add()
+{
+	/* add default WAN route */
+	struct ipa_ioc_add_rt_rule *rt_rule;
+	struct ipa_rt_rule_add *rt_rule_entry;
+	uint32_t tx_index;
+	const int NUM = 1;
+
+	if(tx_prop == NULL)
+	{
+	  IPACMDBG("No tx properties, ignore default route setting\n");
+	  return IPACM_SUCCESS;
+	}
+
+	rt_rule = (struct ipa_ioc_add_rt_rule *)
+		 calloc(1, sizeof(struct ipa_ioc_add_rt_rule) +
+						NUM * 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 = (uint8_t)NUM;
+
+
+	IPACMDBG(" WAN table created %s \n", rt_rule->rt_tbl_name);
+	rt_rule_entry = &rt_rule->rules[0];
+	rt_rule_entry->at_rear = true;
+
+	for (tx_index = 0; tx_index < iface_query->num_tx_props; tx_index++)
+	{
+
+	    if (IPA_IP_v4 == tx_prop->tx[tx_index].ip)
+	    {
+	    	strcpy(rt_rule->rt_tbl_name, IPACM_Iface::ipacmcfg->rt_tbl_odu_v4.name);
+			rt_rule_entry->rule.hdr_hdl = ODU_hdr_hdl_v4;
+			rt_rule->ip = IPA_IP_v4;
+	    }
+	    else
+	    {
+	    	strcpy(rt_rule->rt_tbl_name, IPACM_Iface::ipacmcfg->rt_tbl_odu_v6.name);
+			rt_rule_entry->rule.hdr_hdl = ODU_hdr_hdl_v6;
+			rt_rule->ip = IPA_IP_v6;
+	    }
+
+		rt_rule_entry->rule.dst = tx_prop->tx[tx_index].dst_pipe;
+		memcpy(&rt_rule_entry->rule.attrib,
+					 &tx_prop->tx[tx_index].attrib,
+					 sizeof(rt_rule_entry->rule.attrib));
+
+		rt_rule_entry->rule.attrib.attrib_mask |= IPA_FLT_DST_ADDR;
+		if (IPA_IP_v4 == tx_prop->tx[tx_index].ip)
+		{
+			rt_rule_entry->rule.attrib.u.v4.dst_addr      = 0;
+			rt_rule_entry->rule.attrib.u.v4.dst_addr_mask = 0;
+
+			if (false == m_routing.AddRoutingRule(rt_rule))
+		    {
+		    	IPACMERR("Routing rule addition failed!\n");
+		    	free(rt_rule);
+		    	return IPACM_FAILURE;
+		    }
+			odu_route_rule_v4_hdl[tx_index] = rt_rule_entry->rt_rule_hdl;
+		    IPACMDBG("Got ipv4 ODU-route rule hdl:0x%x,tx:%d,ip-type: %d \n",
+						 odu_route_rule_v4_hdl[tx_index],
+						 tx_index,
+						 IPA_IP_v4);
+		}
+		else
+		{
+			rt_rule_entry->rule.attrib.u.v6.dst_addr[0] = 0;
+			rt_rule_entry->rule.attrib.u.v6.dst_addr[1] = 0;
+			rt_rule_entry->rule.attrib.u.v6.dst_addr[2] = 0;
+			rt_rule_entry->rule.attrib.u.v6.dst_addr[3] = 0;
+			rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[0] = 0;
+			rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[1] = 0;
+			rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[2] = 0;
+			rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[3] = 0;
+
+			if (false == m_routing.AddRoutingRule(rt_rule))
+		    {
+		    	IPACMERR("Routing rule addition failed!\n");
+		    	free(rt_rule);
+		    	return IPACM_FAILURE;
+			}
+			odu_route_rule_v6_hdl[tx_index] = rt_rule_entry->rt_rule_hdl;
+			IPACMDBG("Set ipv6 ODU-route rule hdl for v6_lan_table:0x%x,tx:%d,ip-type: %d \n",
+		                 odu_route_rule_v6_hdl[tx_index],
+		                 tx_index,
+		                 IPA_IP_v6);
+		}
+
+	}
+	free(rt_rule);
+	return IPACM_SUCCESS;
+}
+
+/* handle odu default route rule deletion */
+int IPACM_Lan::handle_odu_route_del()
+{
+	uint32_t tx_index;
+
+	if(tx_prop == NULL)
+	{
+	  IPACMDBG("No tx properties, ignore delete default route setting\n");
+	  return IPACM_SUCCESS;
+	}
+
+		for (tx_index = 0; tx_index < iface_query->num_tx_props; tx_index++)
+		{
+			if (tx_prop->tx[tx_index].ip == IPA_IP_v4)
+			{
+		    	IPACMDBG("Tx:%d, ip-type: %d match ip-type: %d, RT-rule deleted\n",
+		    					    tx_index, tx_prop->tx[tx_index].ip,IPA_IP_v4);
+
+				if (m_routing.DeleteRoutingHdl(odu_route_rule_v4_hdl[tx_index], IPA_IP_v4)
+						== false)
+				{
+					IPACMDBG("IP-family:%d, Routing rule(hdl:0x%x) deletion failed with tx_index %d!\n", IPA_IP_v4, odu_route_rule_v4_hdl[tx_index], tx_index);
+					return IPACM_FAILURE;
+				}
+			}
+			else
+			{
+		    	IPACMDBG("Tx:%d, ip-type: %d match ip-type: %d, RT-rule deleted\n",
+		    					    tx_index, tx_prop->tx[tx_index].ip,IPA_IP_v6);
+
+				if (m_routing.DeleteRoutingHdl(odu_route_rule_v6_hdl[tx_index], IPA_IP_v6)
+						== false)
+				{
+					IPACMDBG("IP-family:%d, Routing rule(hdl:0x%x) deletion failed with tx_index %d!\n", IPA_IP_v6, odu_route_rule_v6_hdl[tx_index], tx_index);
+					return IPACM_FAILURE;
+				}
+			}
+		}
+
+	return IPACM_SUCCESS;
+}
+
 /*handle eth client del mode*/
 int IPACM_Lan::handle_eth_client_down_evt(uint8_t *mac_addr)
 {
@@ -1677,6 +2085,37 @@
 	int i;
 	int res = IPACM_SUCCESS;
 
+	if (IPACM_Iface::ipacmcfg->iface_table[ipa_if_num].if_cat == ODU_IF)
+	{
+		/* delete ODU default RT rules */
+		handle_odu_route_del();
+
+		/* delete full header */
+		if (ipv4_header_set)
+		{
+			if (m_header.DeleteHeaderHdl(ODU_hdr_hdl_v4)
+					== false)
+			{
+					IPACMDBG("ODU ipv4 header delete fail\n");
+					res = IPACM_FAILURE;
+					goto fail;
+			}
+			IPACMDBG("ODU ipv4 header delete success\n");
+		}
+
+		if (ipv6_header_set)
+		{
+			if (m_header.DeleteHeaderHdl(ODU_hdr_hdl_v6)
+					== false)
+			{
+				IPACMDBG("ODU ipv6 header delete fail\n");
+				res = IPACM_FAILURE;
+				goto fail;
+			}
+			IPACMDBG("ODU ipv6 header delete success\n");
+		}
+	}
+
 	/* no iface address up, directly close iface*/
 	if (ip_type == IPACM_IP_NULL)
 	{
@@ -1889,6 +2328,14 @@
 }
 #endif /* defined(FEATURE_IPA_ANDROID)*/
 fail:
+	if (odu_route_rule_v4_hdl != NULL)
+	{
+		free(odu_route_rule_v4_hdl);
+	}
+	if (odu_route_rule_v6_hdl != NULL)
+	{
+		free(odu_route_rule_v6_hdl);
+	}
 	/* Delete corresponding ipa_rm_resource_name of RX-endpoint after delete all IPV4V6 FT-rule */
 	if (rx_prop != NULL)
 	{
@@ -3603,3 +4050,35 @@
 	return IPACM_SUCCESS;
 }
 
+int IPACM_Lan::handle_addr_evt_odu_bridge(ipacm_event_data_addr* data)
+{
+	int fd, res = IPACM_SUCCESS;
+	struct in6_addr ipv6_addr;
+	if(data == NULL)
+	{
+		IPACMERR("Failed to get interface IP address.\n");
+		return IPACM_FAILURE;
+	}
+
+	if(data->iptype == IPA_IP_v6)
+	{
+		fd = open(IPACM_Iface::ipacmcfg->DEVICE_NAME_ODU, O_RDWR);
+		if(fd == 0)
+		{
+			IPACMERR("Failed to open %s.\n", IPACM_Iface::ipacmcfg->DEVICE_NAME_ODU);
+			return IPACM_FAILURE;
+		}
+
+		memcpy(&ipv6_addr, data->ipv6_addr, sizeof(struct in6_addr));
+
+		if( ioctl(fd, ODU_BRIDGE_IOC_SET_LLV6_ADDR, &ipv6_addr) )
+		{
+			IPACMERR("Failed to write IPv6 address to odu driver.\n");
+			res = IPACM_FAILURE;
+		}
+		num_dft_rt_v6++;
+		close(fd);
+	}
+
+	return res;
+}
diff --git a/ipacm/src/IPACM_Main.cpp b/ipacm/src/IPACM_Main.cpp
index 201334e..9341ec7 100644
--- a/ipacm/src/IPACM_Main.cpp
+++ b/ipacm/src/IPACM_Main.cpp
@@ -571,6 +571,21 @@
 			break;
         /* End of adding for 8994 Android case */
 
+        /* Add for embms case */
+		case WAN_EMBMS_CONNECT:
+			memcpy(&event_wan, buffer + sizeof(struct ipa_msg_meta), sizeof(struct ipa_wan_msg));
+			IPACMDBG("Received WAN_EMBMS_CONNECT name: %s\n",event_wan.upstream_ifname);
+			data_fid = (ipacm_event_data_fid *)malloc(sizeof(ipacm_event_data_fid));
+			if(data_fid == NULL)
+			{
+				IPACMERR("unable to allocate memory for event data_fid\n");
+				return NULL;
+			}
+			ipa_get_if_index(event_wan.upstream_ifname, &(data_fid->if_index));
+			evt_data.event = IPA_WAN_EMBMS_LINK_UP_EVENT;
+			evt_data.evt_data = data_fid;
+			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 a82a904..9db578c 100644
--- a/ipacm/src/IPACM_Netlink.cpp
+++ b/ipacm/src/IPACM_Netlink.cpp
@@ -680,10 +680,8 @@
 						IPACMDBG_H("Posting IPA_LINK_DOWN_EVENT with if index: %d\n",
 										 data_fid->if_index);
 					}
-
 					evt_data.evt_data = data_fid;
 					IPACM_EvtDispatcher::PostEvt(&evt_data);
-
 				}
 
 				/* Add IPACM support for ECM plug-in/plug_out */
@@ -746,8 +744,8 @@
 					IPACM_EvtDispatcher::PostEvt(&evt_data);
 					IPACMDBG_H("Posting usb IPA_LINK_DOWN_EVENT with if index: %d\n",
 										 data_fid->if_index);
-                                }
 
+                                }
 			}
 			break;
 
diff --git a/ipacm/src/IPACM_Wan.cpp b/ipacm/src/IPACM_Wan.cpp
index 1791cee..c410702 100644
--- a/ipacm/src/IPACM_Wan.cpp
+++ b/ipacm/src/IPACM_Wan.cpp
@@ -65,6 +65,8 @@
 int IPACM_Wan::num_ipv4_modem_pdn = 0;
 int IPACM_Wan::num_ipv6_modem_pdn = 0;
 
+bool IPACM_Wan::embms_is_on = false;
+
 uint32_t IPACM_Wan::backhaul_ipv6_prefix[2];
 
 IPACM_Wan::IPACM_Wan(int iface_index, ipacm_wan_iface_type is_sta_mode) : IPACM_Iface(iface_index)
@@ -126,6 +128,20 @@
 	{
 		IPACMERR("Failed to open %s\n",IPA_DEVICE_NAME);
 	}
+
+	if(IPACM_Iface::ipacmcfg->iface_table[ipa_if_num].if_cat == EMBMS_IF)
+	{
+		IPACMDBG(" IPACM->IPACM_Wan_eMBMS(%d) constructor: Tx:%d\n", ipa_if_num, iface_query->num_tx_props);
+		embms_is_on = true;
+		install_wan_filtering_rule(false);
+		/* Add corresponding ipa_rm_resource_name of TX-endpoint up before IPV6 RT-rule set */
+        IPACM_Iface::ipacmcfg->AddRmDepend(IPACM_Iface::ipacmcfg->ipa_client_rm_map_tbl[tx_prop->tx[0].dst_pipe],false);
+	}
+	else
+	{
+		IPACMDBG(" IPACM->IPACM_Wan(%d) constructor: Tx:%d\n", ipa_if_num, iface_query->num_tx_props);
+	}
+
 	return;
 }
 
@@ -3407,6 +3423,126 @@
 	return IPACM_SUCCESS;
 }
 
+/* configure the initial embms filter rules */
+int IPACM_Wan::config_dft_embms_rules(ipa_ioc_add_flt_rule *pFilteringTable_v4, ipa_ioc_add_flt_rule *pFilteringTable_v6)
+{
+	struct ipa_flt_rule_add flt_rule_entry;
+	struct ipa_ioc_get_rt_tbl_indx rt_tbl_idx;
+	struct ipa_ioc_generate_flt_eq flt_eq;
+
+	if (rx_prop == NULL)
+	{
+		IPACMDBG("No rx properties registered for iface %s\n", dev_name);
+		return IPACM_SUCCESS;
+	}
+
+	if(pFilteringTable_v4 == NULL || pFilteringTable_v6 == NULL)
+	{
+		IPACMERR("Either v4 or v6 filtering table is empty.\n");
+		return IPACM_FAILURE;
+	}
+
+	/* set up ipv4 odu rule*/
+	memset(&flt_rule_entry, 0, sizeof(struct ipa_flt_rule_add));
+
+	/* get eMBMS ODU tbl index*/
+	memset(&rt_tbl_idx, 0, sizeof(rt_tbl_idx));
+	strncpy(rt_tbl_idx.name, IPACM_Iface::ipacmcfg->rt_tbl_odu_v4.name, IPA_RESOURCE_NAME_MAX);
+	rt_tbl_idx.ip = IPA_IP_v4;
+	if(0 != ioctl(m_fd_ipa, IPA_IOC_QUERY_RT_TBL_INDEX, &rt_tbl_idx))
+	{
+		IPACMERR("Failed to get routing table index from name\n");
+		return IPACM_FAILURE;
+	}
+	IPACMDBG_H("Odu routing table %s has index %d\n", rt_tbl_idx.name, rt_tbl_idx.idx);
+
+	memset(&flt_rule_entry, 0, sizeof(struct ipa_flt_rule_add));
+	flt_rule_entry.flt_rule_hdl = -1;
+	flt_rule_entry.status = -1;
+	flt_rule_entry.at_rear = false;
+
+	flt_rule_entry.rule.retain_hdr = 0;
+	flt_rule_entry.rule.to_uc = 0;
+	flt_rule_entry.rule.eq_attrib_type = 1;
+	flt_rule_entry.rule.action = IPA_PASS_TO_ROUTING;
+	flt_rule_entry.rule.rt_tbl_idx = rt_tbl_idx.idx;
+
+	memcpy(&flt_rule_entry.rule.attrib,
+				 &rx_prop->rx[0].attrib,
+				 sizeof(struct ipa_rule_attrib));
+	flt_rule_entry.rule.attrib.attrib_mask |= IPA_FLT_DST_ADDR;
+	flt_rule_entry.rule.attrib.u.v4.dst_addr_mask = 0x00000000;
+	flt_rule_entry.rule.attrib.u.v4.dst_addr = 0x00000000;
+
+	memset(&flt_eq, 0, sizeof(flt_eq));
+	memcpy(&flt_eq.attrib, &flt_rule_entry.rule.attrib, sizeof(flt_eq.attrib));
+	flt_eq.ip = IPA_IP_v4;
+	if(0 != ioctl(m_fd_ipa, IPA_IOC_GENERATE_FLT_EQ, &flt_eq))
+	{
+		IPACMERR("Failed to get eq_attrib\n");
+		return IPACM_FAILURE;
+	}
+	memcpy(&flt_rule_entry.rule.eq_attrib,
+				 &flt_eq.eq_attrib,
+				 sizeof(flt_rule_entry.rule.eq_attrib));
+
+	memcpy(&(pFilteringTable_v4->rules[0]), &flt_rule_entry, sizeof(struct ipa_flt_rule_add));
+
+	/* construc v6 rule */
+	memset(&flt_rule_entry, 0, sizeof(struct ipa_flt_rule_add));
+	/* get eMBMS ODU tbl*/
+	memset(&rt_tbl_idx, 0, sizeof(rt_tbl_idx));
+	strncpy(rt_tbl_idx.name, IPACM_Iface::ipacmcfg->rt_tbl_odu_v6.name, IPA_RESOURCE_NAME_MAX);
+	rt_tbl_idx.ip = IPA_IP_v6;
+	if(0 != ioctl(m_fd_ipa, IPA_IOC_QUERY_RT_TBL_INDEX, &rt_tbl_idx))
+	{
+		IPACMERR("Failed to get routing table index from name\n");
+		return IPACM_FAILURE;
+	}
+	IPACMDBG_H("Odu routing table %s has index %d\n", rt_tbl_idx.name, rt_tbl_idx.idx);
+
+	memset(&flt_rule_entry, 0, sizeof(struct ipa_flt_rule_add));
+	flt_rule_entry.flt_rule_hdl = -1;
+	flt_rule_entry.status = -1;
+	flt_rule_entry.at_rear = false;
+
+	flt_rule_entry.rule.retain_hdr = 0;
+	flt_rule_entry.rule.to_uc = 0;
+	flt_rule_entry.rule.eq_attrib_type = 1;
+	flt_rule_entry.rule.action = IPA_PASS_TO_ROUTING;
+	flt_rule_entry.rule.rt_tbl_idx = rt_tbl_idx.idx;
+
+	memcpy(&flt_rule_entry.rule.attrib,
+				 &rx_prop->rx[0].attrib,
+				 sizeof(struct ipa_rule_attrib));
+	flt_rule_entry.rule.attrib.attrib_mask |= IPA_FLT_DST_ADDR;
+	flt_rule_entry.rule.attrib.u.v6.dst_addr_mask[0] = 0x00000000;
+	flt_rule_entry.rule.attrib.u.v6.dst_addr_mask[1] = 0x00000000;
+	flt_rule_entry.rule.attrib.u.v6.dst_addr_mask[2] = 0x00000000;
+	flt_rule_entry.rule.attrib.u.v6.dst_addr_mask[3] = 0x00000000;
+	flt_rule_entry.rule.attrib.u.v6.dst_addr[0] = 0X00000000;
+	flt_rule_entry.rule.attrib.u.v6.dst_addr[1] = 0x00000000;
+	flt_rule_entry.rule.attrib.u.v6.dst_addr[2] = 0x00000000;
+	flt_rule_entry.rule.attrib.u.v6.dst_addr[3] = 0X00000000;
+
+	memset(&flt_eq, 0, sizeof(flt_eq));
+	memcpy(&flt_eq.attrib, &flt_rule_entry.rule.attrib, sizeof(flt_eq.attrib));
+	flt_eq.ip = IPA_IP_v6;
+	if(0 != ioctl(m_fd_ipa, IPA_IOC_GENERATE_FLT_EQ, &flt_eq))
+	{
+		IPACMERR("Failed to get eq_attrib\n");
+		return IPACM_FAILURE;
+	}
+	memcpy(&flt_rule_entry.rule.eq_attrib,
+				 &flt_eq.eq_attrib,
+				 sizeof(flt_rule_entry.rule.eq_attrib));
+
+	memcpy(&(pFilteringTable_v6->rules[0]), &flt_rule_entry, sizeof(struct ipa_flt_rule_add));
+
+	return IPACM_SUCCESS;
+}
+
+
 /*for STA mode: handle wan-iface down event */
 int IPACM_Wan::handle_down_evt()
 {
@@ -3634,6 +3770,21 @@
 		goto fail;
 	}
 
+	/* free ODU filter rule handlers */
+	if(IPACM_Iface::ipacmcfg->iface_table[ipa_if_num].if_cat == EMBMS_IF)
+	{
+		embms_is_on = false;
+		/* Delete corresponding ipa_rm_resource_name of TX-endpoint after delete IPV4/V6 RT-rule */
+		IPACM_Iface::ipacmcfg->DelRmDepend(IPACM_Iface::ipacmcfg->ipa_client_rm_map_tbl[tx_prop->tx[0].dst_pipe]);
+
+		if (rx_prop != NULL)
+		{
+			install_wan_filtering_rule(false);
+			IPACMDBG("finished delete embms filtering rule\n ");
+		}
+		goto fail;
+	}
+
 	if(ip_type == IPA_IP_v4)
 	{
 		num_ipv4_modem_pdn--;
@@ -3956,13 +4107,57 @@
 	}
 	else
 	{
-		if(IPACM_Wan::num_v4_flt_rule > 0)
+		if(embms_is_on == false)
 		{
-			len = sizeof(struct ipa_ioc_add_flt_rule) + IPACM_Wan::num_v4_flt_rule * sizeof(struct ipa_flt_rule_add);
+			if(IPACM_Wan::num_v4_flt_rule > 0)
+			{
+				len = sizeof(struct ipa_ioc_add_flt_rule) + IPACM_Wan::num_v4_flt_rule * sizeof(struct ipa_flt_rule_add);
+				pFilteringTable_v4 = (struct ipa_ioc_add_flt_rule*)malloc(len);
+
+				IPACMDBG_H("Total number of WAN DL filtering rule for IPv4 is %d\n", IPACM_Wan::num_v4_flt_rule);
+
+				if (pFilteringTable_v4 == NULL)
+				{
+					IPACMERR("Error Locate ipa_flt_rule_add memory...\n");
+					return IPACM_FAILURE;
+				}
+				pFilteringTable_v4->commit = 1;
+				pFilteringTable_v4->ep = rx_prop->rx[0].src_pipe;
+				pFilteringTable_v4->global = false;
+				pFilteringTable_v4->ip = IPA_IP_v4;
+				pFilteringTable_v4->num_rules = (uint8_t)IPACM_Wan::num_v4_flt_rule;
+
+				memcpy(pFilteringTable_v4->rules, IPACM_Wan::flt_rule_v4, IPACM_Wan::num_v4_flt_rule * sizeof(ipa_flt_rule_add));
+			}
+
+			if(IPACM_Wan::num_v6_flt_rule > 0)
+			{
+				len = sizeof(struct ipa_ioc_add_flt_rule) + IPACM_Wan::num_v6_flt_rule * sizeof(struct ipa_flt_rule_add);
+				pFilteringTable_v6 = (struct ipa_ioc_add_flt_rule*)malloc(len);
+
+				IPACMDBG_H("Total number of WAN DL filtering rule for IPv6 is %d\n", IPACM_Wan::num_v6_flt_rule);
+
+				if (pFilteringTable_v6 == NULL)
+				{
+					IPACMERR("Error Locate ipa_flt_rule_add memory...\n");
+					free(pFilteringTable_v4);
+					return IPACM_FAILURE;
+				}
+				pFilteringTable_v6->commit = 1;
+				pFilteringTable_v6->ep = rx_prop->rx[0].src_pipe;
+				pFilteringTable_v6->global = false;
+				pFilteringTable_v6->ip = IPA_IP_v6;
+				pFilteringTable_v6->num_rules = (uint8_t)IPACM_Wan::num_v6_flt_rule;
+
+				memcpy(pFilteringTable_v6->rules, IPACM_Wan::flt_rule_v6, IPACM_Wan::num_v6_flt_rule * sizeof(ipa_flt_rule_add));
+			}
+		}
+		else	//embms is on, always add 1 embms rule on top of WAN DL flt table
+		{
+			/* allocate ipv4 filtering table */
+			len = sizeof(struct ipa_ioc_add_flt_rule) + (1 + IPACM_Wan::num_v4_flt_rule) * sizeof(struct ipa_flt_rule_add);
 			pFilteringTable_v4 = (struct ipa_ioc_add_flt_rule*)malloc(len);
-
-			IPACMDBG_H("Total number of WAN DL filtering rule for IPv4 is %d\n", IPACM_Wan::num_v4_flt_rule);
-
+			IPACMDBG_H("Total number of WAN DL filtering rule for IPv4 is %d\n", IPACM_Wan::num_v4_flt_rule + 1);
 			if (pFilteringTable_v4 == NULL)
 			{
 				IPACMERR("Error Locate ipa_flt_rule_add memory...\n");
@@ -3972,18 +4167,12 @@
 			pFilteringTable_v4->ep = rx_prop->rx[0].src_pipe;
 			pFilteringTable_v4->global = false;
 			pFilteringTable_v4->ip = IPA_IP_v4;
-			pFilteringTable_v4->num_rules = (uint8_t)IPACM_Wan::num_v4_flt_rule;
+			pFilteringTable_v4->num_rules = (uint8_t)IPACM_Wan::num_v4_flt_rule + 1;
 
-			memcpy(pFilteringTable_v4->rules, IPACM_Wan::flt_rule_v4, IPACM_Wan::num_v4_flt_rule * sizeof(ipa_flt_rule_add));
-		}
-
-		if(IPACM_Wan::num_v6_flt_rule > 0)
-		{
-			len = sizeof(struct ipa_ioc_add_flt_rule) + IPACM_Wan::num_v6_flt_rule * sizeof(struct ipa_flt_rule_add);
+			/* allocate ipv6 filtering table */
+			len = sizeof(struct ipa_ioc_add_flt_rule) + (1 + IPACM_Wan::num_v6_flt_rule) * sizeof(struct ipa_flt_rule_add);
 			pFilteringTable_v6 = (struct ipa_ioc_add_flt_rule*)malloc(len);
-
-			IPACMDBG_H("Total number of WAN DL filtering rule for IPv6 is %d\n", IPACM_Wan::num_v6_flt_rule);
-
+			IPACMDBG_H("Total number of WAN DL filtering rule for IPv6 is %d\n", IPACM_Wan::num_v6_flt_rule + 1);
 			if (pFilteringTable_v6 == NULL)
 			{
 				IPACMERR("Error Locate ipa_flt_rule_add memory...\n");
@@ -3994,9 +4183,18 @@
 			pFilteringTable_v6->ep = rx_prop->rx[0].src_pipe;
 			pFilteringTable_v6->global = false;
 			pFilteringTable_v6->ip = IPA_IP_v6;
-			pFilteringTable_v6->num_rules = (uint8_t)IPACM_Wan::num_v6_flt_rule;
+			pFilteringTable_v6->num_rules = (uint8_t)IPACM_Wan::num_v6_flt_rule + 1;
 
-			memcpy(pFilteringTable_v6->rules, IPACM_Wan::flt_rule_v6, IPACM_Wan::num_v6_flt_rule * sizeof(ipa_flt_rule_add));
+			config_dft_embms_rules(pFilteringTable_v4, pFilteringTable_v6);
+			if(IPACM_Wan::num_v4_flt_rule > 0)
+			{
+				memcpy(&(pFilteringTable_v4->rules[1]), IPACM_Wan::flt_rule_v4, IPACM_Wan::num_v4_flt_rule * sizeof(ipa_flt_rule_add));
+			}
+
+			if(IPACM_Wan::num_v6_flt_rule > 0)
+			{
+				memcpy(&(pFilteringTable_v6->rules[1]), IPACM_Wan::flt_rule_v6, IPACM_Wan::num_v6_flt_rule * sizeof(ipa_flt_rule_add));
+			}
 		}
 	}
 
diff --git a/ipacm/src/IPACM_Xml.cpp b/ipacm/src/IPACM_Xml.cpp
index 0a1d156..6ef4cc0 100644
--- a/ipacm/src/IPACM_Xml.cpp
+++ b/ipacm/src/IPACM_Xml.cpp
@@ -169,6 +169,8 @@
 				if (IPACM_util_icmp_string((char*)xml_node->name,
 																	 system_TAG) == 0 ||
 						IPACM_util_icmp_string((char*)xml_node->name,
+																	 ODU_TAG) == 0 ||
+						IPACM_util_icmp_string((char*)xml_node->name,
 																	 IPACMCFG_TAG) == 0 ||
 						IPACM_util_icmp_string((char*)xml_node->name,
 																	 IPACMIFACECFG_TAG) == 0 ||
@@ -210,6 +212,28 @@
 																						 config);
 				}
 				else if (IPACM_util_icmp_string((char*)xml_node->name,
+																	ODUMODE_TAG) == 0)
+				{
+					IPACMDBG("inside ODU-XML\n");
+					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, ODU_ROUTER_TAG, str_size))
+						{
+							config->router_mode_enable = true;
+							IPACMDBG("router-mode enable %d\n", config->router_mode_enable);
+						}
+						else if (0 == strncasecmp(content_buf, ODU_BRIDGE_TAG, str_size))
+						{
+							config->router_mode_enable = false;
+							IPACMDBG("router-mode enable %d\n", config->router_mode_enable);
+						}
+					}
+				}
+				else if (IPACM_util_icmp_string((char*)xml_node->name,
 																				NAME_TAG) == 0)
 				{
 					content = IPACM_read_content_element(xml_node);
@@ -261,6 +285,11 @@
 							config->iface_config.iface_entries[config->iface_config.num_iface_entries - 1].if_cat = ETH_IF;
 							IPACMDBG_H("Category %d\n", config->iface_config.iface_entries[config->iface_config.num_iface_entries - 1].if_cat);
 						}
+						else  if (0 == strncasecmp(content_buf, ODUIF_TAG, str_size))
+						{
+							config->iface_config.iface_entries[config->iface_config.num_iface_entries - 1].if_cat = ODU_IF;
+							IPACMDBG("Category %d\n", config->iface_config.iface_entries[config->iface_config.num_iface_entries - 1].if_cat);
+						}
 					}
 				}
 				else if (IPACM_util_icmp_string((char*)xml_node->name,
diff --git a/ipacm/src/IPACM_cfg.xml b/ipacm/src/IPACM_cfg.xml
index 7a7de58..95a7796 100644
--- a/ipacm/src/IPACM_cfg.xml
+++ b/ipacm/src/IPACM_cfg.xml
@@ -1,5 +1,8 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <system xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ipacm_cfg.xsd">
+	<ODUCFG>
+		<Mode>router</Mode>
+	</ODUCFG>
 	<IPACM>
 		<IPACMIface>
 			<Iface>
@@ -49,10 +52,10 @@
 			<Iface>
 			   <Name>wlan1</Name>
 			   <Category>UNKNOWN</Category>
-			</Iface>			
+			</Iface>
 			<Iface>
 			   <Name>eth0</Name>
-			   <Category>ETH</Category>
+			   <Category>ODU</Category>
 			</Iface>
 			<Iface>
 			   <Name>bridge0</Name>