IPACM: Add support on IPACM-HAL for Android-O

Add API for IPACM_HAL to start tethered-traffic
offload and get stats from modem and set quota.

Change-Id: I7d07c4ce4e7b12c7e088dec94a3d54c7da3a4a8b
diff --git a/ipacm/inc/IPACM_ConntrackClient.h b/ipacm/inc/IPACM_ConntrackClient.h
index a6076cf..16d5b9c 100644
--- a/ipacm/inc/IPACM_ConntrackClient.h
+++ b/ipacm/inc/IPACM_ConntrackClient.h
@@ -91,6 +91,13 @@
 
    static IPACM_ConntrackClient* GetInstance();
 
+   static void UNRegisterWithConnTrack(void);
+   int fd_tcp;
+   int fd_udp;
+
+   unsigned int subscrips_tcp;
+   unsigned int subscrips_udp;
+
 #ifdef IPACM_DEBUG
 #define iptodot(X,Y) \
 		 IPACMLOG(" %s(0x%x): %d.%d.%d.%d\n", X, Y, ((Y>>24) & 0xFF), ((Y>>16) & 0xFF), ((Y>>8) & 0xFF), (Y & 0xFF));
diff --git a/ipacm/inc/IPACM_ConntrackListener.h b/ipacm/inc/IPACM_ConntrackListener.h
index cdf3ef5..d965cf7 100644
--- a/ipacm/inc/IPACM_ConntrackListener.h
+++ b/ipacm/inc/IPACM_ConntrackListener.h
@@ -87,7 +87,6 @@
 	void TriggerWANUp(void *);
 	void TriggerWANDown(uint32_t);
 	int  CreateNatThreads(void);
-	int  CreateConnTrackThreads(void);
 	bool AddIface(nat_table_entry *, bool *);
 	void AddORDeleteNatEntry(const nat_entry_bundle *);
 	void PopulateTCPorUDPEntry(struct nf_conntrack *, uint32_t, nat_table_entry *);
@@ -116,6 +115,7 @@
 	void HandleNeighIpAddrDelEvt(uint32_t);
 	void HandleSTAClientAddEvt(uint32_t);
 	void HandleSTAClientDelEvt(uint32_t);
+	int  CreateConnTrackThreads(void);
 };
 
 extern IPACM_ConntrackListener *CtList;
diff --git a/ipacm/inc/IPACM_Defs.h b/ipacm/inc/IPACM_Defs.h
index d2cc362..74ed3bf 100644
--- a/ipacm/inc/IPACM_Defs.h
+++ b/ipacm/inc/IPACM_Defs.h
@@ -185,6 +185,8 @@
 	IPA_ETH_BRIDGE_CLIENT_DEL,                /* ipacm_event_eth_bridge*/
 	IPA_ETH_BRIDGE_WLAN_SCC_MCC_SWITCH,       /* ipacm_event_eth_bridge*/
 	IPA_LAN_DELETE_SELF,                      /* ipacm_event_data_fid */
+	IPA_DOWNSTREAM_ADD,                       /* ipacm_event_ipahal_stream */
+	IPA_DOWNSTREAM_DEL,                       /* ipacm_event_ipahal_stream */
 	IPACM_EVENT_MAX
 } ipa_cm_event_id;
 
@@ -351,4 +353,17 @@
 	uint8_t mac_addr[IPA_MAC_ADDR_SIZE];
 }ipacm_ifacemgr_data;
 
+typedef struct _ipacm_offload_prefix {
+	enum ipa_ip_type iptype;
+	uint32_t v4Addr;
+	uint32_t v4Mask;
+	uint32_t v6Addr[4];
+	uint32_t v6Mask[4];
+} ipacm_offload_prefix;
+
+typedef struct {
+	int if_index;
+	_ipacm_offload_prefix prefix;
+} ipacm_event_ipahal_stream;
+
 #endif /* IPA_CM_DEFS_H */
diff --git a/ipacm/inc/IPACM_Lan.h b/ipacm/inc/IPACM_Lan.h
index 6c54f5e..85ea927 100644
--- a/ipacm/inc/IPACM_Lan.h
+++ b/ipacm/inc/IPACM_Lan.h
@@ -224,6 +224,10 @@
 
 	uint32_t ipv6_prefix[2];
 
+	bool is_upstream_set[IPA_IP_MAX];
+	bool is_downstream_set[IPA_IP_MAX];
+	_ipacm_offload_prefix prefix[IPA_IP_MAX];
+
 private:
 
 	/* get hdr proc ctx type given source and destination l2 hdr type */
diff --git a/ipacm/inc/IPACM_OffloadManager.h b/ipacm/inc/IPACM_OffloadManager.h
new file mode 100644
index 0000000..85a2063
--- /dev/null
+++ b/ipacm/inc/IPACM_OffloadManager.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2017, 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
+ * met:
+ *    * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    * Redistributions in binary form must reproduce the above
+ *      copyright notice, this list of conditions and the following
+ *      disclaimer in the documentation and/or other materials provided
+ *      with the distribution.
+ *    * Neither the name of The Linux Foundation nor the names of its
+ *      contributors may be used to endorse or promote products derived
+ *      from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef _IPACM_OFFLOAD_MANAGER_H_
+#define _IPACM_OFFLOAD_MANAGER_H_
+
+
+#include <stdint.h>
+#include <IOffloadManager.h>
+#include "IPACM_Defs.h"
+
+using RET = ::IOffloadManager::RET;
+using Prefix = ::IOffloadManager::Prefix;
+using IP_FAM = ::IOffloadManager::IP_FAM;
+using L4Protocol = ::IOffloadManager::ConntrackTimeoutUpdater::L4Protocol;
+using natTimeoutUpdate_t = ::IOffloadManager::ConntrackTimeoutUpdater::natTimeoutUpdate_t;
+//using ipAddrPortPair_t = ::IOffloadManager::ConntrackTimeoutUpdater::ipAddrPortPair_t;
+//using UDP = ::IOffloadManager::ConntrackTimeoutUpdater::UDP;
+//using TCP = ::IOffloadManager::ConntrackTimeoutUpdater::TCP;
+
+
+
+class IPACM_OffloadManager : public IOffloadManager
+{
+
+public:
+
+	IPACM_OffloadManager();
+	static IPACM_OffloadManager* GetInstance();
+
+	virtual RET registerEventListener(IpaEventListener* /* listener */);
+	virtual RET unregisterEventListener(IpaEventListener* /* listener */);
+	virtual RET registerCtTimeoutUpdater(ConntrackTimeoutUpdater* /* cb */);
+	virtual RET unregisterCtTimeoutUpdater(ConntrackTimeoutUpdater* /* cb */);
+
+	virtual RET provideFd(int /* fd */, unsigned int /* group */);
+	virtual RET clearAllFds();
+	virtual bool isStaApSupported();
+
+    /* ---------------------------- ROUTE ------------------------------- */
+    virtual RET setLocalPrefixes(std::vector<Prefix> &/* prefixes */);
+    virtual RET addDownstream(const char * /* downstream */,
+            const Prefix & /* prefix */);
+    virtual RET removeDownstream(const char * /* downstream */,
+            const Prefix &/* prefix */);
+	virtual RET setUpstream(const char* /* iface */, const Prefix& /* v4Gw */, const Prefix& /* v6Gw */);
+    virtual RET stopAllOffload();
+
+    /* ------------------------- STATS/POLICY --------------------------- */
+    virtual RET setQuota(const char * /* upstream */, uint64_t /* limit */);
+    virtual RET getStats(const char * /* upstream */, bool /* reset */,
+		OffloadStatistics& /* ret */);
+
+	static IPACM_OffloadManager *pInstance; //sky
+
+	IpaEventListener *elrInstance;
+
+	ConntrackTimeoutUpdater *touInstance;
+
+private:
+
+	bool upstream_v4_up;
+
+	bool upstream_v6_up;
+
+	int default_gw_index;
+
+	int post_route_evt(enum ipa_ip_type iptype, int index, ipa_cm_event_id event, const Prefix &gw_addr);
+
+	int ipa_get_if_index(const char *if_name, int *if_index);
+
+	int resetTetherStats(const char *upstream_name);
+
+	static const char *DEVICE_NAME;
+
+}; /* IPACM_OffloadManager */
+
+#endif /* _IPACM_OFFLOAD_MANAGER_H_ */
diff --git a/ipacm/inc/IPACM_Wan.h b/ipacm/inc/IPACM_Wan.h
index fe6d35e..3ed58fe 100644
--- a/ipacm/inc/IPACM_Wan.h
+++ b/ipacm/inc/IPACM_Wan.h
@@ -104,6 +104,10 @@
 	static bool isWanUP(int ipa_if_num_tether)
 	{
 #ifdef FEATURE_IPA_ANDROID
+#ifdef FEATURE_IPACM_HAL
+		return wan_up;
+#else
+
 		int i;
 		for (i=0; i < ipa_if_num_tether_v4_total;i++)
 		{
@@ -116,6 +120,7 @@
 			}
 		}
 		return false;
+#endif
 #else
 		return wan_up;
 #endif
diff --git a/ipacm/src/Android.mk b/ipacm/src/Android.mk
index 99bc3ae..bcb8332 100644
--- a/ipacm/src/Android.mk
+++ b/ipacm/src/Android.mk
@@ -12,12 +12,13 @@
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/../src
 LOCAL_C_INCLUDES += $(LOCAL_PATH)/../inc
 LOCAL_C_INCLUDES += $(LOCAL_PATH)/../../ipanat/inc
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/../../hal/inc
 ifeq ($(call is-platform-sdk-version-at-least,20),true)
 LOCAL_C_INCLUDES += external/icu/icu4c/source/common
 else
 LOCAL_C_INCLUDES += external/icu4c/common
 endif
-LOCAL_C_INCLUDES += external/dhcpcd
+#LOCAL_C_INCLUDES += external/dhcpcd
 LOCAL_C_INCLUDES += external/libxml2/include
 LOCAL_C_INCLUDES += external/libnetfilter_conntrack/include
 LOCAL_C_INCLUDES += external/libnfnetlink/include
@@ -25,9 +26,9 @@
 LOCAL_C_INCLUDES += $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr/include
 LOCAL_ADDITIONAL_DEPENDENCIES := $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr
 
-
 LOCAL_CFLAGS := -v
 LOCAL_CFLAGS += -DFEATURE_IPA_ANDROID
+LOCAL_CFLAGS += -DFEATURE_IPACM_HAL
 ifneq (,$(filter userdebug eng, $(TARGET_BUILD_VARIANT)))
 LOCAL_CFLAGS += -DDEBUG
 endif
@@ -59,17 +60,33 @@
 		IPACM_Conntrack_NATApp.cpp\
 		IPACM_ConntrackClient.cpp \
 		IPACM_ConntrackListener.cpp \
-                IPACM_Log.cpp
+		IPACM_Log.cpp \
+		IPACM_OffloadManager.cpp
 
 LOCAL_MODULE := ipacm
 LOCAL_CLANG := false
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_SHARED_LIBRARIES := libipanat
+LOCAL_SHARED_LIBRARIES := liboffloadhal
+LOCAL_SHARED_LIBRARIES += libipanat
 LOCAL_SHARED_LIBRARIES += libxml2
 LOCAL_SHARED_LIBRARIES += libnfnetlink
 LOCAL_SHARED_LIBRARIES += libnetfilter_conntrack
-LOCAL_SHARED_LIBRARIES += libdhcpcd
+LOCAL_SHARED_LIBRARIES += libhwbinder \
+                libhidlbase \
+                libhidltransport \
+                liblog \
+                libcutils \
+                libdl \
+                libbase \
+                libutils \
+                libhardware_legacy \
+                libhardware \
+                android.hardware.tetheroffload.config@1.0 \
+                android.hardware.tetheroffload.control@1.0
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_EXECUTABLES)
+
 LOCAL_CLANG := true
 include $(BUILD_EXECUTABLE)
 
diff --git a/ipacm/src/IPACM_ConntrackClient.cpp b/ipacm/src/IPACM_ConntrackClient.cpp
index 23724fd..f8f143b 100644
--- a/ipacm/src/IPACM_ConntrackClient.cpp
+++ b/ipacm/src/IPACM_ConntrackClient.cpp
@@ -60,6 +60,10 @@
 	udp_hdl = NULL;
 	tcp_filter = NULL;
 	udp_filter = NULL;
+	fd_tcp = -1;
+	fd_udp = -1;
+	subscrips_tcp = NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY;
+	subscrips_udp = NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY;
 }
 
 IPACM_ConntrackClient* IPACM_ConntrackClient::GetInstance()
@@ -429,10 +433,20 @@
 	subscrips |= NF_NETLINK_CONNTRACK_NEW;
 #endif
 
+#ifdef FEATURE_IPACM_HAL
+	if (pClient->fd_tcp < 0) {
+		IPACMERR("unable to get conntrack TCP handle due to fd_tcp is invalid \n");
+		return NULL;
+	} else {
+		pClient->tcp_hdl = nfct_open2(CONNTRACK, subscrips, pClient->fd_tcp);
+	}
+#else
 	pClient->tcp_hdl = nfct_open(CONNTRACK, subscrips);
+#endif
+
 	if(pClient->tcp_hdl == NULL)
 	{
-		PERROR("nfct_open\n");
+		PERROR("nfct_open failed on getting tcp_hdl\n");
 		return NULL;
 	}
 
@@ -483,7 +497,11 @@
 	/* de-register the callback */
 	nfct_callback_unregister(pClient->tcp_hdl);
 	/* close the handle */
+#ifdef FEATURE_IPACM_HAL
+	nfct_close2(pClient->tcp_hdl, true);
+#else
 	nfct_close(pClient->tcp_hdl);
+#endif
 	pClient->tcp_hdl = NULL;
 
 	pthread_exit(NULL);
@@ -505,11 +523,21 @@
 		return NULL;
 	}
 
+#ifdef FEATURE_IPACM_HAL
+	if (pClient->fd_udp < 0) {
+		IPACMERR("unable to get conntrack UDP handle due to fd_udp is invalid \n");
+		return NULL;
+	} else {
+		pClient->udp_hdl = nfct_open2(CONNTRACK,
+					(NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY), pClient->fd_udp);
+	}
+#else
 	pClient->udp_hdl = nfct_open(CONNTRACK,
 					(NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY));
+#endif
 	if(pClient->udp_hdl == NULL)
 	{
-		PERROR("nfct_open\n");
+		PERROR("nfct_open failed on getting udp_hdl\n");
 		return NULL;
 	}
 
@@ -559,13 +587,51 @@
 	/* de-register the callback */
 	nfct_callback_unregister(pClient->udp_hdl);
 	/* close the handle */
+#ifdef FEATURE_IPACM_HAL
+	nfct_close2(pClient->udp_hdl, true);
+#else
 	nfct_close(pClient->udp_hdl);
+#endif
 	pClient->udp_hdl = NULL;
 
 	pthread_exit(NULL);
 	return NULL;
 }
 
+/* Thread to initialize TCP Conntrack Filters*/
+void IPACM_ConntrackClient::UNRegisterWithConnTrack(void)
+{
+	int ret;
+	IPACM_ConntrackClient *pClient = NULL;
+
+	IPACMDBG("\n");
+
+	/* destroy the TCP filter.. this will not detach the filter */
+	nfct_filter_destroy(pClient->tcp_filter);
+	pClient->tcp_filter = NULL;
+
+	/* de-register the callback */
+	nfct_callback_unregister(pClient->tcp_hdl);
+	/* close the handle */
+	nfct_close(pClient->tcp_hdl);
+	pClient->tcp_hdl = NULL;
+
+	/* destroy the filter.. this will not detach the filter */
+	nfct_filter_destroy(pClient->udp_filter);
+	pClient->udp_filter = NULL;
+
+	/* de-register the callback */
+	nfct_callback_unregister(pClient->udp_hdl);
+	/* close the handle */
+	nfct_close(pClient->udp_hdl);
+	pClient->udp_hdl = NULL;
+
+	pClient->fd_tcp = 0;
+	pClient->fd_udp = 0;
+
+	return;
+}
+
 void IPACM_ConntrackClient::UpdateUDPFilters(void *param, bool isWan)
 {
 	static bool isIgnore = false;
diff --git a/ipacm/src/IPACM_ConntrackListener.cpp b/ipacm/src/IPACM_ConntrackListener.cpp
index ad3fa1e..de0e7e7 100644
--- a/ipacm/src/IPACM_ConntrackListener.cpp
+++ b/ipacm/src/IPACM_ConntrackListener.cpp
@@ -115,9 +115,12 @@
 			IPACMDBG_H("Received event: %d with ifname: %s and address: 0x%x\n",
 							 evt, ((ipacm_event_iface_up *)data)->ifname,
 							 ((ipacm_event_iface_up *)data)->ipv4_addr);
-			CreateConnTrackThreads();
-			IPACM_ConntrackClient::UpdateUDPFilters(data, false);
-			IPACM_ConntrackClient::UpdateTCPFilters(data, false);
+			if(isWanUp())
+			{
+				CreateConnTrackThreads();
+				IPACM_ConntrackClient::UpdateUDPFilters(data, false);
+				IPACM_ConntrackClient::UpdateTCPFilters(data, false);
+			}
 			break;
 
 	 case IPA_NEIGH_CLIENT_IP_ADDR_ADD_EVENT:
diff --git a/ipacm/src/IPACM_Conntrack_NATApp.cpp b/ipacm/src/IPACM_Conntrack_NATApp.cpp
index f0bdd99..2c06642 100644
--- a/ipacm/src/IPACM_Conntrack_NATApp.cpp
+++ b/ipacm/src/IPACM_Conntrack_NATApp.cpp
@@ -28,6 +28,9 @@
 */
 #include "IPACM_Conntrack_NATApp.h"
 #include "IPACM_ConntrackClient.h"
+#ifdef FEATURE_IPACM_HAL
+#include "IPACM_OffloadManager.h"
+#endif
 
 #define INVALID_IP_ADDR 0x0
 
@@ -400,11 +403,16 @@
 void NatApp::UpdateCTUdpTs(nat_table_entry *rule, uint32_t new_ts)
 {
 	int ret;
+#ifdef FEATURE_IPACM_HAL
+	IOffloadManager::ConntrackTimeoutUpdater::natTimeoutUpdate_t entry;
+	IPACM_OffloadManager* OffloadMng;
+#endif
 
 	iptodot("Private IP:", rule->private_ip);
 	iptodot("Target IP:",  rule->target_ip);
 	IPACMDBG("Private Port: %d, Target Port: %d\n", rule->private_port, rule->target_port);
 
+#ifndef FEATURE_IPACM_HAL
 	if(!ct_hdl)
 	{
 		ct_hdl = nfct_open(CONNTRACK, 0);
@@ -477,7 +485,47 @@
 		rule->timestamp = new_ts;
 		IPACMDBG("Updated time stamp successfully\n");
 	}
+#else
+	if(rule->protocol == IPPROTO_UDP)
+	{
+		entry.proto = IOffloadManager::ConntrackTimeoutUpdater::UDP;;
+	}
+	else
+	{
+		entry.proto = IOffloadManager::ConntrackTimeoutUpdater::TCP;
+	}
 
+	if(rule->dst_nat == false)
+	{
+		entry.src.ipAddr = rule->private_ip;
+		entry.src.port = rule->private_port;
+		entry.dst.ipAddr = rule->target_ip;
+		entry.dst.port = rule->target_port;
+		IPACMDBG("dst nat is not set\n");
+	}
+	else
+	{
+		entry.src.ipAddr = rule->target_ip;
+		entry.src.port = rule->target_port;
+		entry.dst.ipAddr = pub_ip_addr;
+		entry.dst.port = rule->public_port;
+		IPACMDBG("dst nat is set\n");
+	}
+
+	iptodot("Source IP:", entry.src.ipAddr);
+	iptodot("Destination IP:",  entry.dst.ipAddr);
+	IPACMDBG("Source Port: %d, Destination Port: %d\n",
+					entry.src.port, entry.dst.port);
+
+	OffloadMng = IPACM_OffloadManager::GetInstance();
+	if (OffloadMng->touInstance == NULL) {
+		IPACMERR("OffloadMng->touInstance is NULL, can't forward to framework!\n");
+	} else {
+		OffloadMng->touInstance->updateTimeout(entry);
+		IPACMDBG("Updated time stamp successfully\n");
+		rule->timestamp = new_ts;
+	}
+#endif
 	return;
 }
 
diff --git a/ipacm/src/IPACM_IfaceManager.cpp b/ipacm/src/IPACM_IfaceManager.cpp
index a142553..eab43fd 100644
--- a/ipacm/src/IPACM_IfaceManager.cpp
+++ b/ipacm/src/IPACM_IfaceManager.cpp
@@ -267,6 +267,10 @@
 				IPACM_EvtDispatcher::registr(IPA_HANDLE_WAN_UP_V6_TETHER, lan);
 				IPACM_EvtDispatcher::registr(IPA_HANDLE_WAN_DOWN_TETHER, lan);
 				IPACM_EvtDispatcher::registr(IPA_HANDLE_WAN_DOWN_V6_TETHER, lan);
+#ifdef FEATURE_IPACM_HAL
+				IPACM_EvtDispatcher::registr(IPA_DOWNSTREAM_ADD, lan);
+				IPACM_EvtDispatcher::registr(IPA_DOWNSTREAM_DEL, lan);
+#endif
 #else
 				IPACM_EvtDispatcher::registr(IPA_HANDLE_WAN_UP, lan);
 				IPACM_EvtDispatcher::registr(IPA_HANDLE_WAN_UP_V6, lan);
@@ -374,6 +378,10 @@
 				IPACM_EvtDispatcher::registr(IPA_HANDLE_WAN_UP_V6_TETHER, wl);
 				IPACM_EvtDispatcher::registr(IPA_HANDLE_WAN_DOWN_TETHER, wl);
 				IPACM_EvtDispatcher::registr(IPA_HANDLE_WAN_DOWN_V6_TETHER, wl);
+#ifdef FEATURE_IPACM_HAL
+				IPACM_EvtDispatcher::registr(IPA_DOWNSTREAM_ADD, wl);
+				IPACM_EvtDispatcher::registr(IPA_DOWNSTREAM_DEL, wl);
+#endif
 #else
 				IPACM_EvtDispatcher::registr(IPA_HANDLE_WAN_UP, wl);
 				IPACM_EvtDispatcher::registr(IPA_HANDLE_WAN_UP_V6, wl);
diff --git a/ipacm/src/IPACM_Lan.cpp b/ipacm/src/IPACM_Lan.cpp
index 991e451..8d853d2 100644
--- a/ipacm/src/IPACM_Lan.cpp
+++ b/ipacm/src/IPACM_Lan.cpp
@@ -171,9 +171,17 @@
 	/* set the IPA-client pipe enum */
 	if(ipa_if_cate == LAN_IF)
 	{
+#ifdef FEATURE_IPACM_HAL
+		handle_tethering_client(false, IPACM_CLIENT_MAX);
+#else
 		handle_tethering_client(false, IPACM_CLIENT_USB);
+#endif
 	}
 #endif
+
+	memset(is_downstream_set, 0, sizeof(is_downstream_set));
+	memset(is_upstream_set, 0, sizeof(is_upstream_set));
+	memset(&prefix, 0, sizeof(prefix));
 	return;
 }
 
@@ -375,6 +383,9 @@
 									handle_wan_up(IPA_IP_v4);
 								}
 							}
+							IPACMDBG_H("Finished checking wan_up\n");
+						} else {
+							IPACMDBG_H("Wan_V4 haven't up yet\n");
 						}
 
 						if(IPACM_Wan::isWanUP_V6(ipa_if_num))
@@ -393,6 +404,9 @@
 									handle_wan_up(IPA_IP_v6);
 								}
 							}
+							IPACMDBG_H("Finished checking wan_up_v6\n");
+						} else {
+							IPACMDBG_H("Wan_V6 haven't up yet\n");
 						}
 
 						/* Post event to NAT */
@@ -451,20 +465,41 @@
 		IPACMDBG_H("Backhaul is sta mode?%d, if_index_tether:%d tether_if_name:%s\n", data_wan_tether->is_sta,
 					data_wan_tether->if_index_tether,
 					IPACM_Iface::ipacmcfg->iface_table[data_wan_tether->if_index_tether].iface_name);
-		if (data_wan_tether->if_index_tether == ipa_if_num)
+#ifndef FEATURE_IPACM_HAL
+		if (data_wan_tether->if_index_tether != ipa_if_num)
 		{
-			if(ip_type == IPA_IP_v4 || ip_type == IPA_IP_MAX)
+			IPACMERR("IPA_HANDLE_WAN_UP_TETHER tether_if(%d), not valid (%d) ignore\n", data_wan_tether->if_index_tether, ipa_if_num);
+			return;
+		}
+#endif
+		if (ip_type == IPA_IP_v4 || ip_type == IPA_IP_MAX)
+		{
+#ifdef FEATURE_IPACM_HAL
+			if (is_upstream_set[IPA_IP_v4] == false)
 			{
-				if(data_wan_tether->is_sta == false)
+				IPACMDBG_H("Add upstream for IPv4.\n");
+				is_upstream_set[IPA_IP_v4] = true;
+				if (is_downstream_set[IPA_IP_v4] == true)
 				{
-					ext_prop = IPACM_Iface::ipacmcfg->GetExtProp(IPA_IP_v4);
-					handle_wan_up_ex(ext_prop, IPA_IP_v4, 0);
-				}
-				else
-				{
-					handle_wan_up(IPA_IP_v4);
+					IPACMDBG_H("Downstream was set before, adding UL rules.\n");
+					if (data_wan_tether->is_sta == false)
+					{
+							ext_prop = IPACM_Iface::ipacmcfg->GetExtProp(IPA_IP_v4);
+							handle_wan_up_ex(ext_prop, IPA_IP_v4, 0);
+					} else {
+							handle_wan_up(IPA_IP_v4);
+					}
 				}
 			}
+#else
+			if (data_wan_tether->is_sta == false)
+			{
+					ext_prop = IPACM_Iface::ipacmcfg->GetExtProp(IPA_IP_v4);
+					handle_wan_up_ex(ext_prop, IPA_IP_v4, 0);
+			} else {
+					handle_wan_up(IPA_IP_v4);
+			}
+#endif
 		}
 		break;
 
@@ -472,7 +507,7 @@
 		IPACMDBG_H("Received IPA_HANDLE_WAN_UP_V6_TETHER event\n");
 
 		data_wan_tether = (ipacm_event_iface_up_tehter*)param;
-		if(data_wan_tether == NULL)
+		if (data_wan_tether == NULL)
 		{
 			IPACMERR("No event data is found.\n");
 			return;
@@ -480,13 +515,27 @@
 		IPACMDBG_H("Backhaul is sta mode?%d, if_index_tether:%d tether_if_name:%s\n", data_wan_tether->is_sta,
 					data_wan_tether->if_index_tether,
 					IPACM_Iface::ipacmcfg->iface_table[data_wan_tether->if_index_tether].iface_name);
-		if (data_wan_tether->if_index_tether == ipa_if_num)
+#ifndef FEATURE_IPACM_HAL
+		if (data_wan_tether->if_index_tether != ipa_if_num)
 		{
-			if(ip_type == IPA_IP_v6 || ip_type == IPA_IP_MAX)
+			IPACMERR("IPA_HANDLE_WAN_UP_V6_TETHER tether_if(%d), not valid (%d) ignore\n", data_wan_tether->if_index_tether, ipa_if_num);
+			return;
+		}
+#endif
+		if (ip_type == IPA_IP_v6 || ip_type == IPA_IP_MAX)
+		{
+#ifdef FEATURE_IPACM_HAL
+			if (is_upstream_set[IPA_IP_v6] == false)
 			{
+				IPACMDBG_H("Add upstream for IPv6.\n");
+				is_upstream_set[IPA_IP_v6] = true;
+
+				if (is_downstream_set[IPA_IP_v6] == true)
+				{
+					IPACMDBG_H("Downstream was set before, adding UL rules.\n");
 					memcpy(ipv6_prefix, data_wan_tether->ipv6_prefix, sizeof(ipv6_prefix));
 					install_ipv6_prefix_flt_rule(data_wan_tether->ipv6_prefix);
-					if(data_wan_tether->is_sta == false)
+					if (data_wan_tether->is_sta == false)
 					{
 						ext_prop = IPACM_Iface::ipacmcfg->GetExtProp(IPA_IP_v6);
 						handle_wan_up_ex(ext_prop, IPA_IP_v6, 0);
@@ -495,14 +544,24 @@
 					{
 						handle_wan_up(IPA_IP_v6);
 					}
+				}
 			}
+#else
+			if (data_wan_tether->is_sta == false)
+			{
+				ext_prop = IPACM_Iface::ipacmcfg->GetExtProp(IPA_IP_v6);
+				handle_wan_up_ex(ext_prop, IPA_IP_v6, 0);
+			} else {
+				handle_wan_up(IPA_IP_v6);
+			}
+#endif
 		}
 		break;
 
 	case IPA_HANDLE_WAN_DOWN_TETHER:
 		IPACMDBG_H("Received IPA_HANDLE_WAN_DOWN_TETHER event\n");
 		data_wan_tether = (ipacm_event_iface_up_tehter*)param;
-		if(data_wan_tether == NULL)
+		if (data_wan_tether == NULL)
 		{
 			IPACMERR("No event data is found.\n");
 			return;
@@ -510,12 +569,29 @@
 		IPACMDBG_H("Backhaul is sta mode?%d, if_index_tether:%d tether_if_name:%s\n", data_wan_tether->is_sta,
 					data_wan_tether->if_index_tether,
 					IPACM_Iface::ipacmcfg->iface_table[data_wan_tether->if_index_tether].iface_name);
-		if (data_wan_tether->if_index_tether == ipa_if_num)
+#ifndef FEATURE_IPACM_HAL
+		if (data_wan_tether->if_index_tether != ipa_if_num)
 		{
-			if(ip_type == IPA_IP_v4 || ip_type == IPA_IP_MAX)
+			IPACMERR("IPA_HANDLE_WAN_DOWN_TETHER tether_if(%d), not valid (%d) ignore\n", data_wan_tether->if_index_tether, ipa_if_num);
+			return;
+		}
+#endif
+		if (ip_type == IPA_IP_v4 || ip_type == IPA_IP_MAX)
+		{
+#ifdef FEATURE_IPACM_HAL
+			if(is_upstream_set[IPA_IP_v4] == true)
 			{
-				handle_wan_down(data_wan_tether->is_sta);
+				IPACMDBG_H("Del upstream for IPv4.\n");
+				is_upstream_set[IPA_IP_v4] = false;
+				if(is_downstream_set[IPA_IP_v4] == true)
+				{
+					IPACMDBG_H("Downstream was set before, deleting UL rules.\n");
+					handle_wan_down(data_wan_tether->is_sta);
+				}
 			}
+#else
+			handle_wan_down(data_wan_tether->is_sta);
+#endif
 		}
 		break;
 
@@ -530,33 +606,116 @@
 		IPACMDBG_H("Backhaul is sta mode?%d, if_index_tether:%d tether_if_name:%s\n", data_wan_tether->is_sta,
 					data_wan_tether->if_index_tether,
 					IPACM_Iface::ipacmcfg->iface_table[data_wan_tether->if_index_tether].iface_name);
-		if (data_wan_tether->if_index_tether == ipa_if_num)
+#ifndef FEATURE_IPACM_HAL
+		if (data_wan_tether->if_index_tether != ipa_if_num)
 		{
-			/* clean up v6 RT rules*/
-			IPACMDBG_H("Received IPA_HANDLE_WAN_DOWN_V6_TETHER in LAN-instance and need clean up client IPv6 address \n");
+			IPACMERR("IPA_HANDLE_WAN_DOWN_V6_TETHER tether_if(%d), not valid (%d) ignore\n", data_wan_tether->if_index_tether, ipa_if_num);
+			return;
+		}
+#endif
+		if (ip_type == IPA_IP_v6 || ip_type == IPA_IP_MAX)
+		{
+#ifdef FEATURE_IPACM_HAL
+			if (is_upstream_set[IPA_IP_v6] == true)
+			{
+				IPACMDBG_H("Del upstream for IPv6.\n");
+				is_upstream_set[IPA_IP_v6] = false;
+				if(is_downstream_set[IPA_IP_v6] == true)
+				{
+					IPACMDBG_H("Downstream was set before, deleting UL rules.\n");
+					/* reset usb-client ipv6 rt-rules */
+					handle_lan_client_reset_rt(IPA_IP_v6);
+					handle_wan_down_v6(data_wan_tether->is_sta);
+				}
+			}
+#else
 			/* reset usb-client ipv6 rt-rules */
 			handle_lan_client_reset_rt(IPA_IP_v6);
+			handle_wan_down_v6(data_wan_tether->is_sta);
+#endif
+		}
+		break;
 
-			if(ip_type == IPA_IP_v6 || ip_type == IPA_IP_MAX)
+	case IPA_DOWNSTREAM_ADD:
+	{
+		ipacm_event_ipahal_stream *data = (ipacm_event_ipahal_stream *)param;
+		ipa_interface_index = iface_ipa_index_query(data->if_index);
+		if (ipa_interface_index == ipa_if_num)
+		{
+			IPACMDBG_H("Received IPA_DOWNSTREAM_ADD event.\n");
+			if (is_downstream_set[data->prefix.iptype] == false)
 			{
-				handle_wan_down_v6(data_wan_tether->is_sta);
+				IPACMDBG_H("Add downstream for IP iptype %d\n", data->prefix.iptype);
+				is_downstream_set[data->prefix.iptype] = true;
+				memcpy(&prefix[data->prefix.iptype], &data->prefix,
+					sizeof(prefix[data->prefix.iptype]));
+
+				if (is_upstream_set[data->prefix.iptype] == true)
+				{
+					IPACMDBG_H("Upstream was set before, adding UL rules.\n");
+					if (ip_type == IPA_IP_MAX || ip_type == data->prefix.iptype)
+					{
+						if (data->prefix.iptype == IPA_IP_v6) /* ipv6 only */
+							install_ipv6_prefix_flt_rule(IPACM_Wan::backhaul_ipv6_prefix);
+
+						if (IPACM_Wan::backhaul_is_sta_mode == false) /* LTE */
+						{
+							ext_prop = IPACM_Iface::ipacmcfg->GetExtProp(data->prefix.iptype);
+							handle_wan_up_ex(ext_prop, data->prefix.iptype, 0);
+						} else {
+							handle_wan_up(data->prefix.iptype); /* STA */
+						}
+					}
+				}
+			} else {
+				IPACMDBG_H("downstream for IP iptype %d already set \n", data->prefix.iptype);
 			}
 		}
 		break;
+	}
+
+	case IPA_DOWNSTREAM_DEL:
+	{
+		ipacm_event_ipahal_stream *data = (ipacm_event_ipahal_stream *)param;
+		ipa_interface_index = iface_ipa_index_query(data->if_index);
+		if (ipa_interface_index == ipa_if_num)
+		{
+			IPACMDBG_H("Received IPA_DOWNSTREAM_DEL event.\n");
+			if (is_downstream_set[data->prefix.iptype] == true)
+			{
+				IPACMDBG_H("Del downstream for IP iptype %d.\n", data->prefix.iptype);
+				is_downstream_set[data->prefix.iptype] = false;
+
+				if (is_upstream_set[data->prefix.iptype] == true)
+				{
+					IPACMDBG_H("Upstream was set before, deleting UL rules.\n");
+					if (data->prefix.iptype == IPA_IP_v4)
+					{
+						handle_wan_down(IPACM_Wan::backhaul_is_sta_mode); /* LTE STA */
+					} else {
+						handle_lan_client_reset_rt(IPA_IP_v6);
+						handle_wan_down_v6(IPACM_Wan::backhaul_is_sta_mode); /* LTE STA */
+					}
+				}
+			}
+		}
+		break;
+	}
+
 #else
 	case IPA_HANDLE_WAN_UP:
 		IPACMDBG_H("Received IPA_HANDLE_WAN_UP event\n");
 
 		data_wan = (ipacm_event_iface_up*)param;
-		if(data_wan == NULL)
+		if (data_wan == NULL)
 		{
 			IPACMERR("No event data is found.\n");
 			return;
 		}
 		IPACMDBG_H("Backhaul is sta mode?%d\n", data_wan->is_sta);
-		if(ip_type == IPA_IP_v4 || ip_type == IPA_IP_MAX)
+		if (ip_type == IPA_IP_v4 || ip_type == IPA_IP_MAX)
 		{
-		if(data_wan->is_sta == false)
+		if (data_wan->is_sta == false)
 		{
 				ext_prop = IPACM_Iface::ipacmcfg->GetExtProp(IPA_IP_v4);
 				handle_wan_up_ex(ext_prop, IPA_IP_v4, data_wan->xlat_mux_id);
@@ -572,17 +731,17 @@
 		IPACMDBG_H("Received IPA_HANDLE_WAN_UP_V6 event\n");
 
 		data_wan = (ipacm_event_iface_up*)param;
-		if(data_wan == NULL)
+		if (data_wan == NULL)
 		{
 			IPACMERR("No event data is found.\n");
 			return;
 		}
 		IPACMDBG_H("Backhaul is sta mode?%d\n", data_wan->is_sta);
-		if(ip_type == IPA_IP_v6 || ip_type == IPA_IP_MAX)
+		if (ip_type == IPA_IP_v6 || ip_type == IPA_IP_MAX)
 		{
 			memcpy(ipv6_prefix, data_wan->ipv6_prefix, sizeof(ipv6_prefix));
 			install_ipv6_prefix_flt_rule(data_wan->ipv6_prefix);
-			if(data_wan->is_sta == false)
+			if (data_wan->is_sta == false)
 			{
 				ext_prop = IPACM_Iface::ipacmcfg->GetExtProp(IPA_IP_v6);
 				handle_wan_up_ex(ext_prop, IPA_IP_v6, 0);
@@ -597,13 +756,13 @@
 	case IPA_HANDLE_WAN_DOWN:
 		IPACMDBG_H("Received IPA_HANDLE_WAN_DOWN event\n");
 		data_wan = (ipacm_event_iface_up*)param;
-		if(data_wan == NULL)
+		if (data_wan == NULL)
 		{
 			IPACMERR("No event data is found.\n");
 			return;
 		}
 		IPACMDBG_H("Backhaul is sta mode?%d\n", data_wan->is_sta);
-		if(ip_type == IPA_IP_v4 || ip_type == IPA_IP_MAX)
+		if (ip_type == IPA_IP_v4 || ip_type == IPA_IP_MAX)
 		{
 			handle_wan_down(data_wan->is_sta);
 		}
@@ -612,7 +771,7 @@
 	case IPA_HANDLE_WAN_DOWN_V6:
 		IPACMDBG_H("Received IPA_HANDLE_WAN_DOWN_V6 event\n");
 		data_wan = (ipacm_event_iface_up*)param;
-		if(data_wan == NULL)
+		if (data_wan == NULL)
 		{
 			IPACMERR("No event data is found.\n");
 			return;
@@ -623,7 +782,7 @@
 		handle_lan_client_reset_rt(IPA_IP_v6);
 
 		IPACMDBG_H("Backhaul is sta mode?%d\n", data_wan->is_sta);
-		if(ip_type == IPA_IP_v6 || ip_type == IPA_IP_MAX)
+		if (ip_type == IPA_IP_v6 || ip_type == IPA_IP_MAX)
 		{
 			handle_wan_down_v6(data_wan->is_sta);
 		}
@@ -1267,6 +1426,12 @@
 		flt_rule_entry.rule.attrib.u.v4.dst_addr_mask = 0x0;
 		flt_rule_entry.rule.attrib.u.v4.dst_addr = 0x0;
 
+/* only offload UL traffic of certain clients */
+#ifdef FEATURE_IPACM_HAL
+		flt_rule_entry.rule.attrib.attrib_mask |= IPA_FLT_SRC_ADDR;
+		flt_rule_entry.rule.attrib.u.v4.dst_addr_mask = prefix[IPA_IP_v4].v4Mask;
+		flt_rule_entry.rule.attrib.u.v4.dst_addr = prefix[IPA_IP_v4].v4Addr;
+#endif
 		memcpy(&m_pFilteringTable->rules[0], &flt_rule_entry, sizeof(flt_rule_entry));
 		if (false == m_filtering.AddFilteringRule(m_pFilteringTable))
 		{
@@ -1338,6 +1503,19 @@
 		flt_rule_entry.rule.attrib.u.v6.dst_addr[2] = 0x00000000;
 		flt_rule_entry.rule.attrib.u.v6.dst_addr[3] = 0X00000000;
 
+/* only offload UL traffic of certain clients */
+#ifdef FEATURE_IPACM_HAL
+		flt_rule_entry.rule.attrib.attrib_mask |= IPA_FLT_SRC_ADDR;
+		flt_rule_entry.rule.attrib.u.v6.dst_addr_mask[0] = prefix[IPA_IP_v6].v6Mask[0];
+		flt_rule_entry.rule.attrib.u.v6.dst_addr_mask[1] = prefix[IPA_IP_v6].v6Mask[1];
+		flt_rule_entry.rule.attrib.u.v6.dst_addr_mask[2] = prefix[IPA_IP_v6].v6Mask[2];
+		flt_rule_entry.rule.attrib.u.v6.dst_addr_mask[3] = prefix[IPA_IP_v6].v6Mask[3];
+		flt_rule_entry.rule.attrib.u.v6.dst_addr[0] = prefix[IPA_IP_v6].v6Addr[0];
+		flt_rule_entry.rule.attrib.u.v6.dst_addr[1] = prefix[IPA_IP_v6].v6Addr[1];
+		flt_rule_entry.rule.attrib.u.v6.dst_addr[2] = prefix[IPA_IP_v6].v6Addr[2];
+		flt_rule_entry.rule.attrib.u.v6.dst_addr[3] = prefix[IPA_IP_v6].v6Addr[3];
+
+#endif
 		memcpy(&(m_pFilteringTable->rules[0]), &flt_rule_entry, sizeof(struct ipa_flt_rule_add));
 		if (false == m_filtering.AddFilteringRule(m_pFilteringTable))
 		{
@@ -1796,8 +1974,6 @@
 	uint32_t tx_index;
 	int eth_index,v6_num;
 	const int NUM = 1;
-	char cmd[200] = {0};
-	uint32_t ipv4_addr;
 
 	if(tx_prop == NULL)
 	{
@@ -1875,17 +2051,6 @@
                 IPACMDBG_H("client(%d): v4 header handle:(0x%x)\n",
 		  				 eth_index,
 		  				 get_client_memptr(eth_client, eth_index)->hdr_hdl_v4);
-
-				/* add static arp entry */
-				ipv4_addr = get_client_memptr(eth_client, eth_index)->v4_addr;
-				snprintf(cmd, sizeof(cmd), "ip neighbor change %d.%d.%d.%d lladdr %02x:%02x:%02x:%02x:%02x:%02x dev %s nud permanent",
-					(unsigned char)(ipv4_addr >> 24), (unsigned char)(ipv4_addr >> 16),
-					(unsigned char)(ipv4_addr >> 8), (unsigned char)ipv4_addr,
-					mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5],
-					dev_name);
-				IPACMDBG_H("%s\n", cmd);
-				system(cmd);
-
 				strlcpy(rt_rule->rt_tbl_name,
 								IPACM_Iface::ipacmcfg->rt_tbl_lan_v4.name,
 								sizeof(rt_rule->rt_tbl_name));
@@ -2340,8 +2505,6 @@
 	uint32_t tx_index;
 	int num_eth_client_tmp = num_eth_client;
 	int num_v6;
-	char cmd[200] = {0};
-	uint32_t ipv4_addr;
 
 	IPACMDBG_H("total client: %d\n", num_eth_client_tmp);
 
@@ -2400,13 +2563,6 @@
 	get_client_memptr(eth_client, clt_indx)->route_rule_set_v4 = false;
 	get_client_memptr(eth_client, clt_indx)->route_rule_set_v6 = 0;
 
-	ipv4_addr = get_client_memptr(eth_client, clt_indx)->v4_addr;
-	snprintf(cmd, sizeof(cmd), "ip neighbor del %d.%d.%d.%d dev %s",
-		(unsigned char)(ipv4_addr >> 24), (unsigned char)(ipv4_addr >> 16),
-		(unsigned char)(ipv4_addr >> 8), (unsigned char)ipv4_addr, dev_name);
-	system(cmd);
-	IPACMDBG_H("%s\n", cmd);
-
 	for (; clt_indx < num_eth_client_tmp - 1; clt_indx++)
 	{
 		memcpy(get_client_memptr(eth_client, clt_indx)->mac,
@@ -2472,8 +2628,6 @@
 {
 	int i;
 	int res = IPACM_SUCCESS;
-	char cmd[200] = {0};
-	uint32_t ipv4_addr;
 
 	IPACMDBG_H("lan handle_down_evt\n ");
 	if (ipa_if_cate == ODU_IF)
@@ -2656,7 +2810,11 @@
 	/* reset the IPA-client pipe enum */
 	if(ipa_if_cate != WAN_IF)
 	{
+#ifdef FEATURE_IPACM_HAL
+		handle_tethering_client(true, IPACM_CLIENT_MAX);
+#else
 		handle_tethering_client(true, IPACM_CLIENT_USB);
+#endif
 	}
 #endif /* defined(FEATURE_IPA_ANDROID)*/
 fail:
@@ -2664,13 +2822,6 @@
 	IPACMDBG_H("left %d eth clients need to be deleted \n ", num_eth_client);
 	for (i = 0; i < num_eth_client; i++)
 	{
-		ipv4_addr = get_client_memptr(eth_client, i)->v4_addr;
-		snprintf(cmd, sizeof(cmd), "ip neighbor del %d.%d.%d.%d dev %s",
-			(unsigned char)(ipv4_addr >> 24), (unsigned char)(ipv4_addr >> 16),
-			(unsigned char)(ipv4_addr >> 8), (unsigned char)ipv4_addr, dev_name);
-		system(cmd);
-		IPACMDBG_H("%s\n", cmd);
-
 		/* First reset nat rules and then route rules */
 		if(get_client_memptr(eth_client, i)->ipv4_set == true)
 		{
@@ -2763,7 +2914,7 @@
 	ipa_ioc_add_flt_rule *pFilteringTable;
 	ipa_fltr_installed_notif_req_msg_v01 flt_index;
 	int fd;
-	int i, index;
+	int i, index, eq_index;
 	uint32_t value = 0;
 
 	IPACMDBG_H("Set modem UL flt rules\n");
@@ -2885,6 +3036,73 @@
 			IPACMDBG_H("xlat meta-data is modified for rule: %d has index %d with xlat_mux_id: %d\n",
 					cnt, index, xlat_mux_id);
 		}
+
+#ifdef FEATURE_IPACM_HAL
+		/* add prefix equation in modem UL rules */
+		if(iptype == IPA_IP_v4)
+		{
+			flt_rule_entry.rule.eq_attrib.num_offset_meq_32++;
+			if(flt_rule_entry.rule.eq_attrib.num_offset_meq_32 <= IPA_IPFLTR_NUM_MEQ_32_EQNS)
+			{
+				eq_index = flt_rule_entry.rule.eq_attrib.num_offset_meq_32 - 1;
+				if(eq_index == 0)
+				{
+					flt_rule_entry.rule.eq_attrib.rule_eq_bitmap |= (1<<5);
+				}
+				else
+				{
+					flt_rule_entry.rule.eq_attrib.rule_eq_bitmap |= (1<<6);
+				}
+				flt_rule_entry.rule.eq_attrib.offset_meq_32[eq_index].offset = 12;
+				flt_rule_entry.rule.eq_attrib.offset_meq_32[eq_index].mask = prefix[IPA_IP_v4].v4Mask;
+				flt_rule_entry.rule.eq_attrib.offset_meq_32[eq_index].value = prefix[IPA_IP_v4].v4Addr;
+			}
+			else
+			{
+				IPACMERR("Run out of MEQ32 equation.\n");
+				flt_rule_entry.rule.eq_attrib.num_offset_meq_32--;
+			}
+		}
+		else
+		{
+			flt_rule_entry.rule.eq_attrib.num_offset_meq_128++;
+			if(flt_rule_entry.rule.eq_attrib.num_offset_meq_128 <= IPA_IPFLTR_NUM_MEQ_128_EQNS)
+			{
+				eq_index = flt_rule_entry.rule.eq_attrib.num_offset_meq_128 - 1;
+				if(eq_index == 0)
+				{
+					flt_rule_entry.rule.eq_attrib.rule_eq_bitmap |= (1<<3);
+				}
+				else
+				{
+					flt_rule_entry.rule.eq_attrib.rule_eq_bitmap |= (1<<4);
+				}
+				flt_rule_entry.rule.eq_attrib.offset_meq_128[eq_index].offset = 8;
+				*(uint32_t *)(flt_rule_entry.rule.eq_attrib.offset_meq_128[eq_index].mask + 0)
+					= prefix[IPA_IP_v6].v6Mask[0];
+				*(uint32_t *)(flt_rule_entry.rule.eq_attrib.offset_meq_128[eq_index].mask + 4)
+					= prefix[IPA_IP_v6].v6Mask[1];
+				*(uint32_t *)(flt_rule_entry.rule.eq_attrib.offset_meq_128[eq_index].mask + 8)
+					= prefix[IPA_IP_v6].v6Mask[2];
+				*(uint32_t *)(flt_rule_entry.rule.eq_attrib.offset_meq_128[eq_index].mask + 12)
+					= prefix[IPA_IP_v6].v6Mask[3];
+				*(uint32_t *)(flt_rule_entry.rule.eq_attrib.offset_meq_128[eq_index].value + 0)
+					= prefix[IPA_IP_v6].v6Addr[0];
+				*(uint32_t *)(flt_rule_entry.rule.eq_attrib.offset_meq_128[eq_index].value + 4)
+					= prefix[IPA_IP_v6].v6Addr[1];
+				*(uint32_t *)(flt_rule_entry.rule.eq_attrib.offset_meq_128[eq_index].value + 8)
+					= prefix[IPA_IP_v6].v6Addr[2];
+				*(uint32_t *)(flt_rule_entry.rule.eq_attrib.offset_meq_128[eq_index].value + 12)
+					= prefix[IPA_IP_v6].v6Addr[3];
+			}
+			else
+			{
+				IPACMERR("Run out of MEQ128 equation.\n");
+				flt_rule_entry.rule.eq_attrib.num_offset_meq_128--;
+			}
+		}
+#endif
+
 #ifdef FEATURE_IPA_V3
 		flt_rule_entry.rule.hashable = prop->prop[cnt].is_rule_hashable;
 		flt_rule_entry.rule.rule_id = prop->prop[cnt].rule_id;
diff --git a/ipacm/src/IPACM_Main.cpp b/ipacm/src/IPACM_Main.cpp
index c6ab9ee..347bb88 100644
--- a/ipacm/src/IPACM_Main.cpp
+++ b/ipacm/src/IPACM_Main.cpp
@@ -68,6 +68,11 @@
 #include "IPACM_ConntrackClient.h"
 #include "IPACM_Netlink.h"
 
+#ifdef FEATURE_IPACM_HAL
+#include "IPACM_OffloadManager.h"
+#include <HAL.h>
+#endif
+
 /* not defined(FEATURE_IPA_ANDROID)*/
 #ifndef FEATURE_IPA_ANDROID
 #include "IPACM_LanToLan.h"
@@ -78,7 +83,7 @@
 #define IPACM_FIREWALL_FILE_NAME    "mobileap_firewall.xml"
 #define IPACM_CFG_FILE_NAME    "IPACM_cfg.xml"
 #ifdef FEATURE_IPA_ANDROID
-#define IPACM_PID_FILE "/data/misc/ipa/ipacm.pid"
+#define IPACM_PID_FILE "/data/vendor/misc/ipa/ipacm.pid"
 #define IPACM_DIR_NAME     "/data"
 #else/* defined(FEATURE_IPA_ANDROID) */
 #define IPACM_PID_FILE "/etc/ipacm.pid"
@@ -218,6 +223,7 @@
 	struct ipa_wlan_msg_ex *event_ex= NULL;
 	struct ipa_get_data_stats_resp_msg_v01 event_data_stats;
 	struct ipa_get_apn_data_stats_resp_msg_v01 event_network_stats;
+	IPACM_OffloadManager* OffloadMng;
 
 	ipacm_cmd_q_data evt_data;
 	ipacm_event_data_mac *data = NULL;
@@ -671,7 +677,35 @@
 			evt_data.event = IPA_NETWORK_STATS_UPDATE_EVENT;
 			evt_data.evt_data = data_network_stats;
 			break;
-
+#ifdef FEATURE_IPACM_HAL
+		case IPA_QUOTA_REACH:
+			IPACMDBG_H("Received IPA_QUOTA_REACH\n");
+			OffloadMng = IPACM_OffloadManager::GetInstance();
+			if (OffloadMng->elrInstance == NULL) {
+				IPACMERR("OffloadMng->elrInstance is NULL, can't forward to framework!\n");
+			} else {
+				OffloadMng->elrInstance->onLimitReached();
+			}
+			break;
+		case IPA_SSR_BEFORE_SHUTDOWN:
+			IPACMDBG_H("Received IPA_SSR_BEFORE_SHUTDOWN\n");
+			OffloadMng = IPACM_OffloadManager::GetInstance();
+			if (OffloadMng->elrInstance == NULL) {
+				IPACMERR("OffloadMng->elrInstance is NULL, can't forward to framework!\n");
+			} else {
+				OffloadMng->elrInstance->onOffloadStopped(IpaEventRelay::ERROR);
+			}
+			break;
+		case IPA_SSR_AFTER_POWERUP:
+			IPACMDBG_H("Received IPA_SSR_AFTER_POWERUP\n");
+			OffloadMng = IPACM_OffloadManager::GetInstance();
+			if (OffloadMng->elrInstance == NULL) {
+				IPACMERR("OffloadMng->elrInstance is NULL, can't forward to framework!\n");
+			} else {
+				OffloadMng->elrInstance->onOffloadSupportAvailable();
+			}
+			break;
+#endif
 		default:
 			IPACMDBG_H("Unhandled message type: %d\n", event_hdr.msg_type);
 			continue;
@@ -733,6 +767,7 @@
 	int ret;
 	pthread_t netlink_thread = 0, monitor_thread = 0, ipa_driver_thread = 0;
 	pthread_t cmd_queue_thread = 0;
+	IPACM_OffloadManager* OffloadMng;
 
 	/* check if ipacm is already running or not */
 	ipa_is_ipacm_running();
@@ -740,12 +775,16 @@
 	IPACMDBG_H("In main()\n");
 	IPACM_Neighbor *neigh = new IPACM_Neighbor();
 	IPACM_IfaceManager *ifacemgr = new IPACM_IfaceManager();
+#ifdef FEATURE_IPACM_HAL
+	OffloadMng = IPACM_OffloadManager::GetInstance();
+	HAL *hal = HAL::makeIPAHAL(1, OffloadMng);
+	IPACMDBG_H(" START IPACM_OffloadManager and link to android framework\n");
+#endif
 
 #ifdef FEATURE_ETH_BRIDGE_LE
 	IPACM_LanToLan* lan2lan = new IPACM_LanToLan();
 #endif
 
-	IPACM_ConntrackClient *cc = IPACM_ConntrackClient::GetInstance();
 	CtList = new IPACM_ConntrackListener();
 
 	IPACMDBG_H("Staring IPA main\n");
diff --git a/ipacm/src/IPACM_OffloadManager.cpp b/ipacm/src/IPACM_OffloadManager.cpp
new file mode 100644
index 0000000..332abd5
--- /dev/null
+++ b/ipacm/src/IPACM_OffloadManager.cpp
@@ -0,0 +1,540 @@
+/*
+Copyright (c) 2017, 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
+met:
+* Redistributions of source code must retain the above copyright
+  notice, this list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the following
+  disclaimer in the documentation and/or other materials provided
+  with the distribution.
+* Neither the name of The Linux Foundation nor the names of its
+  contributors may be used to endorse or promote products derived
+  from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.Z
+*/
+/*!
+  @file
+  IPACM_OffloadManager.cpp
+
+  @brief
+  This file implements the basis Iface functionality.
+
+  @Author
+  Skylar Chang
+
+*/
+#include <IPACM_OffloadManager.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <string.h>
+#include "IPACM_ConntrackClient.h"
+#include "IPACM_ConntrackListener.h"
+#include "IPACM_Iface.h"
+#include "IPACM_Config.h"
+
+const char *IPACM_OffloadManager::DEVICE_NAME = "/dev/wwan_ioctl";
+
+/* NatApp class Implementation */
+IPACM_OffloadManager *IPACM_OffloadManager::pInstance = NULL;
+
+IPACM_OffloadManager::IPACM_OffloadManager()
+{
+	default_gw_index = INVALID_IFACE;
+	upstream_v4_up = false;
+	upstream_v6_up = false;
+	return ;
+}
+
+RET IPACM_OffloadManager::registerEventListener(IpaEventListener* eventlistener)
+{
+	RET result = SUCCESS;
+	if (elrInstance == NULL)
+		elrInstance = eventlistener;
+	else {
+		IPACMDBG_H("already register EventListener previously \n");
+		result = FAIL_INPUT_CHECK;
+	}
+	return SUCCESS;
+}
+
+RET IPACM_OffloadManager::unregisterEventListener(IpaEventListener* )
+{
+	RET result = SUCCESS;
+	if (elrInstance)
+		elrInstance = NULL;
+	else {
+		IPACMDBG_H("already unregisterEventListener previously \n");
+		result = SUCCESS_DUPLICATE_CONFIG;
+	}
+	return SUCCESS;
+}
+
+RET IPACM_OffloadManager::registerCtTimeoutUpdater(ConntrackTimeoutUpdater* timeoutupdater)
+{
+	RET result = SUCCESS;
+	if (touInstance == NULL)
+		touInstance = timeoutupdater;
+	else {
+		IPACMDBG_H("already register ConntrackTimeoutUpdater previously \n");
+		result = FAIL_INPUT_CHECK;
+	}
+	return SUCCESS;
+}
+
+RET IPACM_OffloadManager::unregisterCtTimeoutUpdater(ConntrackTimeoutUpdater* )
+{
+	RET result = SUCCESS;
+	if (touInstance)
+		touInstance = NULL;
+	else {
+		IPACMDBG_H("already unregisterCtTimeoutUpdater previously \n");
+		result = SUCCESS_DUPLICATE_CONFIG;
+	}
+	return SUCCESS;
+}
+
+RET IPACM_OffloadManager::provideFd(int fd, unsigned int groups)
+{
+	IPACM_ConntrackClient *cc;
+	int on = 1, rel;
+
+	cc = IPACM_ConntrackClient::GetInstance();
+
+	if(!cc)
+	{
+		IPACMERR("Init failed: cc %p\n", cc);
+		return FAIL_HARDWARE;
+	}
+
+	if (groups == cc->subscrips_tcp) {
+		cc->fd_tcp = fd;
+		IPACMDBG_H("Received fd %d with groups %d.\n", fd, groups);
+		/* set netlink buf */
+		rel = setsockopt(cc->fd_tcp, SOL_NETLINK, NETLINK_NO_ENOBUFS, &on, sizeof(int) );
+		if (rel == -1)
+		{
+			IPACMERR( "setsockopt returned error code %d ( %s )", errno, strerror( errno ) );
+		}
+	} else if (groups == cc->subscrips_udp) {
+		cc->fd_udp = fd;
+		IPACMDBG_H("Received fd %d with groups %d.\n", fd, groups);
+		/* set netlink buf */
+		rel = setsockopt(cc->fd_tcp, SOL_NETLINK, NETLINK_NO_ENOBUFS, &on, sizeof(int) );
+		if (rel == -1)
+		{
+			IPACMERR( "setsockopt returned error code %d ( %s )", errno, strerror( errno ) );
+		}
+	} else {
+		IPACMERR("Received unexpected fd with groups %d.\n", groups);
+	}
+	if(cc->fd_tcp >0 && cc->fd_udp >0) {
+		IPACMDBG_H(" Got both fds from framework, start conntrack listener thread.\n");
+		CtList->CreateConnTrackThreads();
+	}
+	return SUCCESS;
+}
+
+RET IPACM_OffloadManager::clearAllFds()
+{
+	IPACM_ConntrackClient *cc;
+
+	cc = IPACM_ConntrackClient::GetInstance();
+	if(!cc)
+	{
+		IPACMERR("Init clear: cc %p \n", cc);
+		return FAIL_HARDWARE;
+	}
+	cc->UNRegisterWithConnTrack();
+
+	return SUCCESS;
+}
+
+bool IPACM_OffloadManager::isStaApSupported()
+{
+	return true;
+}
+
+
+RET IPACM_OffloadManager::setLocalPrefixes(std::vector<Prefix> &/* prefixes */)
+{
+	return SUCCESS;
+}
+
+RET IPACM_OffloadManager::addDownstream(const char * downstream_name, const Prefix &prefix)
+{
+	int index;
+	ipacm_cmd_q_data evt;
+	ipacm_event_ipahal_stream *evt_data;
+
+	IPACMDBG_H("addDownstream name(%s), ip-family(%d) \n", downstream_name, prefix.fam);
+	if (prefix.fam == V4) {
+		IPACMDBG_H("subnet info v4Addr (%x) v4Mask (%x)\n", prefix.v4Addr, prefix.v4Mask);
+	} else {
+		IPACMDBG_H("subnet info v6Addr: %08x:%08x:%08x:%08x \n",
+							prefix.v6Addr[0], prefix.v6Addr[1], prefix.v6Addr[2], prefix.v6Addr[3]);
+		IPACMDBG_H("subnet info v6Mask: %08x:%08x:%08x:%08x \n",
+							prefix.v6Mask[0], prefix.v6Mask[1], prefix.v6Mask[2], prefix.v6Mask[3]);
+	}
+
+	if(ipa_get_if_index(downstream_name, &index))
+	{
+		IPACMERR("fail to get iface index.\n");
+		return FAIL_HARDWARE;
+	}
+
+	evt_data = (ipacm_event_ipahal_stream*)malloc(sizeof(ipacm_event_ipahal_stream));
+	if(evt_data == NULL)
+	{
+		IPACMERR("Failed to allocate memory.\n");
+		return FAIL_HARDWARE;
+	}
+	memset(evt_data, 0, sizeof(*evt_data));
+
+	evt_data->if_index = index;
+	memcpy(&evt_data->prefix, &prefix, sizeof(evt_data->prefix));
+
+	memset(&evt, 0, sizeof(ipacm_cmd_q_data));
+	evt.evt_data = (void*)evt_data;
+	evt.event = IPA_DOWNSTREAM_ADD;
+
+	IPACMDBG_H("Posting event IPA_DOWNSTREAM_ADD\n");
+	IPACM_EvtDispatcher::PostEvt(&evt);
+
+	return SUCCESS;
+}
+
+RET IPACM_OffloadManager::removeDownstream(const char * downstream_name, const Prefix &prefix)
+{
+	int index;
+	ipacm_cmd_q_data evt;
+	ipacm_event_ipahal_stream *evt_data;
+
+	IPACMDBG_H("removeDownstream name(%s), ip-family(%d) \n", downstream_name, prefix.fam);
+	if(ipa_get_if_index(downstream_name, &index))
+	{
+		IPACMERR("fail to get iface index.\n");
+		return FAIL_HARDWARE;
+	}
+
+	evt_data = (ipacm_event_ipahal_stream*)malloc(sizeof(ipacm_event_ipahal_stream));
+	if(evt_data == NULL)
+	{
+		IPACMERR("Failed to allocate memory.\n");
+		return FAIL_HARDWARE;
+	}
+	memset(evt_data, 0, sizeof(*evt_data));
+
+	evt_data->if_index = index;
+	memcpy(&evt_data->prefix, &prefix, sizeof(evt_data->prefix));
+
+	memset(&evt, 0, sizeof(ipacm_cmd_q_data));
+	evt.evt_data = (void*)evt_data;
+	evt.event = IPA_DOWNSTREAM_DEL;
+
+	IPACMDBG_H("Posting event IPA_DOWNSTREAM_DEL\n");
+	IPACM_EvtDispatcher::PostEvt(&evt);
+
+	return SUCCESS;
+}
+
+RET IPACM_OffloadManager::setUpstream(const char *upstream_name, const Prefix& gw_addr_v4 , const Prefix& gw_addr_v6)
+{
+	int index;
+	ipacm_cmd_q_data evt;
+	ipacm_event_data_addr *evt_data_addr;
+	RET result = SUCCESS;
+
+	/* if interface name is NULL, default route is removed */
+	IPACMDBG_H("setUpstream upstream_name(%s), ipv4-fam(%d) ipv6-fam(%d)\n", upstream_name, gw_addr_v4.fam, gw_addr_v6.fam);
+
+	if(upstream_name == NULL)
+	{
+		if (default_gw_index == INVALID_IFACE) {
+			IPACMERR("no previous upstream set before\n");
+			return FAIL_INPUT_CHECK;
+		}
+
+		if (gw_addr_v4.fam == V4 && upstream_v4_up == true) {
+			IPACMDBG_H("clean upstream(%s) for ipv4-fam(%d) upstream_v4_up(%d)\n", upstream_name, gw_addr_v4.fam, upstream_v4_up);
+			post_route_evt(IPA_IP_v4, default_gw_index, IPA_WAN_UPSTREAM_ROUTE_DEL_EVENT, gw_addr_v4);
+			upstream_v4_up = false;
+		}
+		if (gw_addr_v6.fam == V6 && upstream_v6_up == true) {
+			IPACMDBG_H("clean upstream(%s) for ipv6-fam(%d) upstream_v6_up(%d)\n", upstream_name, gw_addr_v6.fam, upstream_v6_up);
+			post_route_evt(IPA_IP_v6, default_gw_index, IPA_WAN_UPSTREAM_ROUTE_DEL_EVENT, gw_addr_v6);
+			upstream_v6_up = false;
+		}
+		default_gw_index = INVALID_IFACE;
+	}
+	else
+	{
+		if(ipa_get_if_index(upstream_name, &index))
+		{
+			IPACMERR("fail to get iface index.\n");
+			return FAIL_INPUT_CHECK;
+		}
+
+		/* reset the stats when switch from LTE->STA */
+		if (index != default_gw_index) {
+			IPACMDBG_H(" interface switched to %s\n", upstream_name);
+			if(memcmp(upstream_name, "wlan0", sizeof("wlan0")) == 0)
+			{
+				IPACMDBG_H("switch to STA mode, need reset wlan-fw stats\n");
+				resetTetherStats(upstream_name);
+			}
+		}
+
+		if (gw_addr_v4.fam == V4 && gw_addr_v6.fam == V6) {
+
+			if (upstream_v4_up == false) {
+				IPACMDBG_H("IPV4 gateway: 0x%x \n", gw_addr_v4.v4Addr);
+				/* posting route add event for both IPv4 and IPv6 */
+				post_route_evt(IPA_IP_v4, index, IPA_WAN_UPSTREAM_ROUTE_ADD_EVENT, gw_addr_v4);
+				upstream_v4_up = true;
+			} else {
+				IPACMDBG_H("already setupstream iface(%s) ipv4 previously\n", upstream_name);
+			}
+
+			if (upstream_v6_up == false) {
+				IPACMDBG_H("IPV6 gateway: %08x:%08x:%08x:%08x \n",
+						gw_addr_v6.v6Addr[0], gw_addr_v6.v6Addr[1], gw_addr_v6.v6Addr[2], gw_addr_v6.v6Addr[3]);
+				post_route_evt(IPA_IP_v6, index, IPA_WAN_UPSTREAM_ROUTE_ADD_EVENT, gw_addr_v6);
+				upstream_v6_up = true;
+			} else {
+				IPACMDBG_H("already setupstream iface(%s) ipv6 previously\n", upstream_name);
+			}
+		} else if (gw_addr_v4.fam == V4 ) {
+			IPACMDBG_H("check upstream_v6_up (%d) v4_up (%d), default gw index (%d)\n", upstream_v6_up, upstream_v4_up, default_gw_index);
+			if (upstream_v6_up == true && default_gw_index != INVALID_IFACE ) {
+				/* clean up previous V6 upstream event */
+				IPACMDBG_H(" Post clean-up v6 default gw on iface %d\n", default_gw_index);
+				post_route_evt(IPA_IP_v6, default_gw_index, IPA_WAN_UPSTREAM_ROUTE_DEL_EVENT, gw_addr_v6);
+				upstream_v6_up = false;
+			}
+
+			if (upstream_v4_up == false) {
+				IPACMDBG_H("IPV4 gateway: %x \n", gw_addr_v4.v4Addr);
+				/* posting route add event for both IPv4 and IPv6 */
+				post_route_evt(IPA_IP_v4, index, IPA_WAN_UPSTREAM_ROUTE_ADD_EVENT, gw_addr_v4);
+				upstream_v4_up = true;
+			} else {
+				IPACMDBG_H("already setupstream iface(%s) ipv4 previously\n", upstream_name);
+				result = SUCCESS_DUPLICATE_CONFIG;
+			}
+		} else if (gw_addr_v6.fam == V6) {
+			IPACMDBG_H("check upstream_v6_up (%d) v4_up (%d), default gw index (%d)\n", upstream_v6_up, upstream_v4_up, default_gw_index);
+			if (upstream_v4_up == true && default_gw_index != INVALID_IFACE ) {
+				/* clean up previous V4 upstream event */
+				IPACMDBG_H(" Post clean-up v4 default gw on iface %d\n", default_gw_index);
+				post_route_evt(IPA_IP_v4, default_gw_index, IPA_WAN_UPSTREAM_ROUTE_DEL_EVENT, gw_addr_v4);
+				upstream_v4_up = false;
+			}
+
+			if (upstream_v6_up == false) {
+				IPACMDBG_H("IPV6 gateway: %08x:%08x:%08x:%08x \n",
+						gw_addr_v6.v6Addr[0], gw_addr_v6.v6Addr[1], gw_addr_v6.v6Addr[2], gw_addr_v6.v6Addr[3]);
+				post_route_evt(IPA_IP_v6, index, IPA_WAN_UPSTREAM_ROUTE_ADD_EVENT, gw_addr_v6);
+				upstream_v6_up = true;
+			} else {
+				IPACMDBG_H("already setupstream iface(%s) ipv6 previously\n", upstream_name);
+				result = SUCCESS_DUPLICATE_CONFIG;
+			}
+		}
+		default_gw_index = index;
+		IPACMDBG_H("Change degault_gw netdev to (%s)\n", upstream_name);
+	}
+	return result;
+}
+
+RET IPACM_OffloadManager::stopAllOffload()
+{
+	return SUCCESS;
+}
+
+RET IPACM_OffloadManager::setQuota(const char * upstream_name /* upstream */, uint64_t mb/* limit */)
+{
+	wan_ioctl_set_data_quota quota;
+	int fd = -1;
+
+	if ((fd = open(DEVICE_NAME, O_RDWR)) < 0)
+	{
+		IPACMERR("Failed opening %s.\n", DEVICE_NAME);
+		return FAIL_HARDWARE;
+	}
+
+	quota.quota_mbytes = mb;
+	quota.set_quota = true;
+
+    memset(quota.interface_name, 0, IFNAMSIZ);
+    if (strlcpy(quota.interface_name, upstream_name, IFNAMSIZ) >= IFNAMSIZ) {
+		IPACMERR("String truncation occurred on upstream");
+		close(fd);
+		return FAIL_INPUT_CHECK;
+	}
+
+	IPACMDBG_H("SET_DATA_QUOTA %s %lld", quota.interface_name, mb);
+
+	if (ioctl(fd, WAN_IOC_SET_DATA_QUOTA, &quota) < 0) {
+        IPACMERR("IOCTL WAN_IOCTL_SET_DATA_QUOTA call failed: %s", strerror(errno));
+		close(fd);
+		return FAIL_TRY_AGAIN;
+	}
+
+	close(fd);
+	return SUCCESS;
+}
+
+RET IPACM_OffloadManager::getStats(const char * upstream_name /* upstream */,
+		bool reset /* reset */, OffloadStatistics& offload_stats/* ret */)
+{
+	int fd = -1;
+	wan_ioctl_query_tether_stats_all stats;
+
+	if ((fd = open(DEVICE_NAME, O_RDWR)) < 0) {
+        IPACMERR("Failed opening %s.\n", DEVICE_NAME);
+        return FAIL_HARDWARE;
+    }
+
+    memset(&stats, 0, sizeof(stats));
+    if (strlcpy(stats.upstreamIface, upstream_name, IFNAMSIZ) >= IFNAMSIZ) {
+		IPACMERR("String truncation occurred on upstream\n");
+		close(fd);
+		return FAIL_INPUT_CHECK;
+	}
+	stats.reset_stats = reset;
+	stats.ipa_client = IPACM_CLIENT_MAX;
+
+	if (ioctl(fd, WAN_IOC_QUERY_TETHER_STATS_ALL, &stats) < 0) {
+        IPACMERR("IOCTL WAN_IOC_QUERY_TETHER_STATS_ALL call failed: %s \n", strerror(errno));
+		close(fd);
+		return FAIL_TRY_AGAIN;
+	}
+	/* feedback to IPAHAL*/
+	offload_stats.tx = stats.tx_bytes;
+	offload_stats.rx = stats.rx_bytes;
+
+	IPACMDBG_H("send getStats tx:%lld rx:%lld \n", offload_stats.tx, offload_stats.rx);
+	return SUCCESS;
+}
+
+int IPACM_OffloadManager::post_route_evt(enum ipa_ip_type iptype, int index, ipa_cm_event_id event, const Prefix &gw_addr)
+{
+	ipacm_cmd_q_data evt;
+	ipacm_event_data_iptype *evt_data_route;
+
+	evt_data_route = (ipacm_event_data_iptype*)malloc(sizeof(ipacm_event_data_iptype));
+	if(evt_data_route == NULL)
+	{
+		IPACMERR("Failed to allocate memory.\n");
+		return -EFAULT;
+	}
+	memset(evt_data_route, 0, sizeof(*evt_data_route));
+
+	evt_data_route->if_index = index;
+	evt_data_route->if_index_tether = 0;
+	evt_data_route->iptype = iptype;
+
+#ifdef IPA_WAN_MSG_IPv6_ADDR_GW_LEN
+	evt_data_route->ipv4_addr_gw = gw_addr.v4Addr;
+	evt_data_route->ipv6_addr_gw[0] = gw_addr.v6Addr[0];
+	evt_data_route->ipv6_addr_gw[1] = gw_addr.v6Addr[1];
+	evt_data_route->ipv6_addr_gw[2] = gw_addr.v6Addr[2];
+	evt_data_route->ipv6_addr_gw[3] = gw_addr.v6Addr[3];
+	IPACMDBG_H("default gw ipv4 (%x)\n", evt_data_route->ipv4_addr_gw);
+	IPACMDBG_H("IPV6 gateway: %08x:%08x:%08x:%08x \n",
+					evt_data_route->ipv6_addr_gw[0], evt_data_route->ipv6_addr_gw[1], evt_data_route->ipv6_addr_gw[2], evt_data_route->ipv6_addr_gw[3]);
+#endif
+	IPACMDBG_H("Received WAN_UPSTREAM_ROUTE_ADD: fid(%d) tether_fid(%d) ip-type(%d)\n", evt_data_route->if_index,
+			evt_data_route->if_index_tether, evt_data_route->iptype);
+
+	memset(&evt, 0, sizeof(evt));
+	evt.evt_data = (void*)evt_data_route;
+	evt.event = event;
+
+	IPACM_EvtDispatcher::PostEvt(&evt);
+
+	return 0;
+}
+
+int IPACM_OffloadManager::ipa_get_if_index(const char * if_name, int * if_index)
+{
+	int fd;
+	struct ifreq ifr;
+
+	if((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+	{
+		IPACMERR("get interface index socket create failed \n");
+		return IPACM_FAILURE;
+	}
+
+	if(strnlen(if_name, sizeof(if_name)) >= sizeof(ifr.ifr_name)) {
+		IPACMERR("interface name overflows: len %d\n", strnlen(if_name, sizeof(if_name)));
+		close(fd);
+		return IPACM_FAILURE;
+	}
+
+	memset(&ifr, 0, sizeof(struct ifreq));
+	(void)strncpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name));
+	IPACMDBG_H("interface name (%s)\n", if_name);
+
+	if(ioctl(fd,SIOCGIFINDEX , &ifr) < 0)
+	{
+		IPACMERR("call_ioctl_on_dev: ioctl failed, interface name (%s):\n", ifr.ifr_name);
+		close(fd);
+		return IPACM_FAILURE;
+	}
+
+	*if_index = ifr.ifr_ifindex;
+	IPACMDBG_H("Interface netdev index %d\n", *if_index);
+	close(fd);
+	return IPACM_SUCCESS;
+}
+
+int IPACM_OffloadManager::resetTetherStats(const char * upstream_name /* upstream */)
+{
+	int fd = -1;
+	wan_ioctl_reset_tether_stats stats;
+
+	if ((fd = open(DEVICE_NAME, O_RDWR)) < 0) {
+        IPACMERR("Failed opening %s.\n", DEVICE_NAME);
+        return FAIL_HARDWARE;
+    }
+
+    memset(stats.upstreamIface, 0, IFNAMSIZ);
+    if (strlcpy(stats.upstreamIface, upstream_name, IFNAMSIZ) >= IFNAMSIZ) {
+		IPACMERR("String truncation occurred on upstream\n");
+		close(fd);
+		return FAIL_INPUT_CHECK;
+	}
+	stats.reset_stats = true;
+
+	if (ioctl(fd, WAN_IOC_RESET_TETHER_STATS, &stats) < 0) {
+        IPACMERR("IOCTL WAN_IOC_RESET_TETHER_STATS call failed: %s", strerror(errno));
+		close(fd);
+		return FAIL_HARDWARE;
+	}
+	IPACMDBG_H("Reset Interface %s stats\n", upstream_name);
+	return IPACM_SUCCESS;
+}
+
+IPACM_OffloadManager* IPACM_OffloadManager::GetInstance()
+{
+	if(pInstance == NULL)
+		pInstance = new IPACM_OffloadManager();
+
+	return pInstance;
+}
diff --git a/ipacm/src/IPACM_Wan.cpp b/ipacm/src/IPACM_Wan.cpp
index 43bffe3..0a52ad5 100644
--- a/ipacm/src/IPACM_Wan.cpp
+++ b/ipacm/src/IPACM_Wan.cpp
@@ -714,13 +714,14 @@
 		{
 			ipacm_event_data_iptype *data = (ipacm_event_data_iptype *)param;
 			ipa_interface_index = iface_ipa_index_query(data->if_index);
+#ifndef FEATURE_IPACM_HAL
 			/* add the check see if tether_iface is valid or not */
 			if (iface_ipa_index_query(data->if_index_tether) == INVALID_IFACE)
 			{
 				IPACMERR("UPSTREAM_ROUTE_ADD tether_if(%d), not valid ignore\n", INVALID_IFACE);
 				return;
 			}
-
+#endif
 			if (ipa_interface_index == ipa_if_num)
 			{
 				IPACMDBG_H("Received IPA_WAN_UPSTREAM_ROUTE_ADD_EVENT (Android) for ip-type (%d)\n", data->iptype);
@@ -738,12 +739,16 @@
 #else
 						IPACMDBG_H("adding routing table(upstream), dev (%s) ip-type(%d)\n", dev_name,data->iptype);
 #endif
-						handle_route_add_evt(data->iptype);
+						handle_route_add_evt(data->iptype); //sky
 					}
 #ifdef FEATURE_IPA_ANDROID
+#ifdef FEATURE_IPACM_HAL
+					post_wan_up_tether_evt(data->iptype, 0);
+#else
 					/* using ipa_if_index, not netdev_index */
 					post_wan_up_tether_evt(data->iptype, iface_ipa_index_query(data->if_index_tether));
 #endif
+#endif
 				}
 				else if ((data->iptype == IPA_IP_v6) && (ip_type == IPA_IP_v6 || ip_type == IPA_IP_MAX))
 				{
@@ -770,9 +775,13 @@
 						handle_route_add_evt(data->iptype);
 					}
 #ifdef FEATURE_IPA_ANDROID
+#ifdef FEATURE_IPACM_HAL
+					post_wan_up_tether_evt(data->iptype, 0);
+#else
 					/* using ipa_if_index, not netdev_index */
 					post_wan_up_tether_evt(data->iptype, iface_ipa_index_query(data->if_index_tether));
 #endif
+#endif
 				}
 			}
 			else /* double check if current default iface is not itself */
@@ -818,13 +827,14 @@
 		{
 			ipacm_event_data_iptype *data = (ipacm_event_data_iptype *)param;
 			ipa_interface_index = iface_ipa_index_query(data->if_index);
+#ifndef FEATURE_IPACM_HAL
 			/* add the check see if tether_iface is valid or not */
 			if (iface_ipa_index_query(data->if_index_tether) == INVALID_IFACE)
 			{
 				IPACMERR("UPSTREAM_ROUTE_DEL tether_if(%d), not valid ignore\n", INVALID_IFACE);
 				return;
 			}
-
+#endif
 			if (ipa_interface_index == ipa_if_num)
 			{
 				IPACMDBG_H("Received IPA_WAN_UPSTREAM_ROUTE_DEL_EVENT\n");
@@ -833,6 +843,9 @@
 					IPACMDBG_H("get del default v4 route (dst:0.0.0.0)\n");
 					wan_v4_addr_gw_set = false;
 #ifdef FEATURE_IPA_ANDROID
+#ifdef FEATURE_IPACM_HAL
+					post_wan_down_tether_evt(data->iptype, 0);
+#else
 					/* using ipa_if_index, not netdev_index */
 					post_wan_down_tether_evt(data->iptype, iface_ipa_index_query(data->if_index_tether));
 					/* no any ipv4 tether iface support*/
@@ -842,6 +855,7 @@
 						return;
 					}
 #endif
+#endif
 					if(m_is_sta_mode == Q6_WAN)
 					{
 						del_wan_firewall_rule(IPA_IP_v4);
@@ -857,6 +871,10 @@
 				else if ((data->iptype == IPA_IP_v6) && (active_v6 == true))
 				{
 #ifdef FEATURE_IPA_ANDROID
+#ifdef FEATURE_IPACM_HAL
+					post_wan_down_tether_evt(data->iptype, 0);
+#else
+
 					/* using ipa_if_index, not netdev_index */
 					post_wan_down_tether_evt(data->iptype, iface_ipa_index_query(data->if_index_tether));
 					/* no any ipv6 tether iface support*/
@@ -866,6 +884,7 @@
 						return;
 					}
 #endif
+#endif
 					if(m_is_sta_mode == Q6_WAN)
 					{
 						del_wan_firewall_rule(IPA_IP_v6);
@@ -4291,8 +4310,7 @@
 int IPACM_Wan::handle_down_evt()
 {
 	int res = IPACM_SUCCESS;
-	int i, tether_total;
-	int ipa_if_num_tether_tmp[IPA_MAX_IFACE_ENTRIES];
+	int i;
 
 	IPACMDBG_H(" wan handle_down_evt \n");
 
@@ -4313,50 +4331,22 @@
 	/* make sure default routing rules and firewall rules are deleted*/
 	if (active_v4)
 	{
-		if (rx_prop != NULL)
-		{
+	   	if (rx_prop != NULL)
+	    {
 			del_dft_firewall_rules(IPA_IP_v4);
 		}
 		handle_route_del_evt(IPA_IP_v4);
 		IPACMDBG_H("Delete default v4 routing rules\n");
-#ifdef IPA_WAN_MSG_IPv6_ADDR_GW_LEN
-		/* posting wan_down_tether for all lan clients */
-		for (i=0; i < IPACM_Wan::ipa_if_num_tether_v4_total; i++)
-		{
-			ipa_if_num_tether_tmp[i] = IPACM_Wan::ipa_if_num_tether_v4[i];
-		}
-		tether_total = IPACM_Wan::ipa_if_num_tether_v4_total;
-		for (i=0; i < tether_total; i++)
-		{
-			post_wan_down_tether_evt(IPA_IP_v4, ipa_if_num_tether_tmp[i]);
-			IPACMDBG_H("post_wan_down_tether_v4 iface(%d: %s)\n", i,
-				IPACM_Iface::ipacmcfg->iface_table[ipa_if_num_tether_tmp[i]].iface_name);
-		}
-#endif
 	}
 
 	if (active_v6)
 	{
-		if (rx_prop != NULL)
-		{
+	   	if (rx_prop != NULL)
+	    {
 			del_dft_firewall_rules(IPA_IP_v6);
 		}
 		handle_route_del_evt(IPA_IP_v6);
 		IPACMDBG_H("Delete default v6 routing rules\n");
-#ifdef IPA_WAN_MSG_IPv6_ADDR_GW_LEN
-		/* posting wan_down_tether for all lan clients */
-		for (i=0; i < IPACM_Wan::ipa_if_num_tether_v6_total; i++)
-		{
-			ipa_if_num_tether_tmp[i] = IPACM_Wan::ipa_if_num_tether_v6[i];
-		}
-		tether_total = IPACM_Wan::ipa_if_num_tether_v6_total;
-		for (i=0; i < tether_total; i++)
-		{
-			post_wan_down_tether_evt(IPA_IP_v6, ipa_if_num_tether_tmp[i]);
-			IPACMDBG_H("post_wan_down_tether_v6 iface(%d: %s)\n", i,
-				IPACM_Iface::ipacmcfg->iface_table[ipa_if_num_tether_tmp[i]].iface_name);
-		}
-#endif
 	}
 
 	/* Delete default v4 RT rule */
@@ -4637,7 +4627,7 @@
 			del_wan_firewall_rule(IPA_IP_v6);
 			install_wan_filtering_rule(false);
 			handle_route_del_evt_ex(IPA_IP_v6);
-#ifdef FEATURE_IPA_ANDROID //sky
+#ifdef FEATURE_IPA_ANDROID
 			/* posting wan_down_tether for all lan clients */
 			for (i=0; i < IPACM_Wan::ipa_if_num_tether_v6_total; i++)
 			{
diff --git a/ipacm/src/IPACM_Wlan.cpp b/ipacm/src/IPACM_Wlan.cpp
index 2152c4d..b47c5ef 100644
--- a/ipacm/src/IPACM_Wlan.cpp
+++ b/ipacm/src/IPACM_Wlan.cpp
@@ -118,7 +118,11 @@
 	/* set the IPA-client pipe enum */
 	if(ipa_if_cate == WLAN_IF)
 	{
+#ifdef FEATURE_IPACM_HAL
+		handle_tethering_client(false, IPACM_CLIENT_MAX);
+#else
 		handle_tethering_client(false, IPACM_CLIENT_WLAN);
+#endif
 	}
 #endif
 	return;
@@ -279,6 +283,9 @@
 								IPACM_Lan::handle_wan_up(IPA_IP_v4);
 							}
 						}
+						IPACMDBG_H("Finished checking wan_up\n");
+					} else {
+						IPACMDBG_H("Wan_V4 haven't up yet \n");
 					}
 
 					if(IPACM_Wan::isWanUP_V6(ipa_if_num))
@@ -298,9 +305,10 @@
 								IPACM_Lan::handle_wan_up(IPA_IP_v6);
 							}
 						}
+						IPACMDBG_H("Finished checking wan_up_v6\n");
+					} else {
+						IPACMDBG_H("Wan_V6 haven't up yet \n");
 					}
-
-					IPACMDBG_H("posting IPA_HANDLE_WLAN_UP:Finished checking wan_up\n");
 					/* checking if SW-RT_enable */
 					if (IPACM_Iface::ipacmcfg->ipa_sw_rt_enable == true)
 					{
@@ -325,20 +333,41 @@
 		IPACMDBG_H("Backhaul is sta mode?%d, if_index_tether:%d tether_if_name:%s\n", data_wan_tether->is_sta,
 					data_wan_tether->if_index_tether,
 					IPACM_Iface::ipacmcfg->iface_table[data_wan_tether->if_index_tether].iface_name);
-		if (data_wan_tether->if_index_tether == ipa_if_num)
+#ifndef FEATURE_IPACM_HAL
+		if (data_wan_tether->if_index_tether != ipa_if_num)
 		{
-			if(ip_type == IPA_IP_v4 || ip_type == IPA_IP_MAX)
+			IPACMERR("IPA_HANDLE_WAN_UP_TETHER tether_if(%d), not valid (%d) ignore\n", data_wan_tether->if_index_tether, ipa_if_num);
+			return;
+		}
+#endif
+		if(ip_type == IPA_IP_v4 || ip_type == IPA_IP_MAX)
+		{
+#ifdef FEATURE_IPACM_HAL
+			if(is_upstream_set[IPA_IP_v4] == false)
 			{
-				if(data_wan_tether->is_sta == false)
+				IPACMDBG_H("Add upstream for IPv4.\n");
+				is_upstream_set[IPA_IP_v4] = true;
+				if(is_downstream_set[IPA_IP_v4] == true)
 				{
-					ext_prop = IPACM_Iface::ipacmcfg->GetExtProp(IPA_IP_v4);
-					IPACM_Lan::handle_wan_up_ex(ext_prop, IPA_IP_v4, 0);
-				}
-				else
-				{
-					IPACM_Lan::handle_wan_up(IPA_IP_v4);
+					IPACMDBG_H("Downstream was set before, adding UL rules.\n");
+					if(data_wan_tether->is_sta == false)
+					{
+						ext_prop = IPACM_Iface::ipacmcfg->GetExtProp(IPA_IP_v4);
+						handle_wan_up_ex(ext_prop, IPA_IP_v4, 0);
+					} else {
+						handle_wan_up(IPA_IP_v4);
+					}
 				}
 			}
+#else
+			if(data_wan_tether->is_sta == false)
+			{
+				ext_prop = IPACM_Iface::ipacmcfg->GetExtProp(IPA_IP_v4);
+				handle_wan_up_ex(ext_prop, IPA_IP_v4, 0);
+			} else {
+				handle_wan_up(IPA_IP_v4);
+			}
+#endif
 		}
 		break;
 
@@ -354,23 +383,48 @@
 		IPACMDBG_H("Backhaul is sta mode?%d, if_index_tether:%d tether_if_name:%s\n", data_wan_tether->is_sta,
 					data_wan_tether->if_index_tether,
 					IPACM_Iface::ipacmcfg->iface_table[data_wan_tether->if_index_tether].iface_name);
-		if (data_wan_tether->if_index_tether == ipa_if_num)
+#ifndef FEATURE_IPACM_HAL
+		if (data_wan_tether->if_index_tether != ipa_if_num)
 		{
-			if(ip_type == IPA_IP_v6 || ip_type == IPA_IP_MAX)
+			IPACMERR("IPA_HANDLE_WAN_UP_V6_TETHER tether_if(%d), not valid (%d) ignore\n", data_wan_tether->if_index_tether, ipa_if_num);
+			return;
+		}
+#endif
+		if(ip_type == IPA_IP_v6 || ip_type == IPA_IP_MAX)
+		{
+#ifdef FEATURE_IPACM_HAL
+			if(is_upstream_set[IPA_IP_v6] == false)
 			{
-				memcpy(ipv6_prefix, data_wan_tether->ipv6_prefix, sizeof(ipv6_prefix));
-				install_ipv6_prefix_flt_rule(data_wan_tether->ipv6_prefix);
+				IPACMDBG_H("Add upstream for IPv6.\n");
+				is_upstream_set[IPA_IP_v6] = true;
 
-				if(data_wan_tether->is_sta == false)
+				if(is_downstream_set[IPA_IP_v6] == true)
 				{
-					ext_prop = IPACM_Iface::ipacmcfg->GetExtProp(IPA_IP_v6);
-					IPACM_Lan::handle_wan_up_ex(ext_prop, IPA_IP_v6, 0);
-				}
-				else
-				{
-					IPACM_Lan::handle_wan_up(IPA_IP_v6);
+					IPACMDBG_H("Downstream was set before, adding UL rules.\n");
+					memcpy(ipv6_prefix, data_wan_tether->ipv6_prefix, sizeof(ipv6_prefix));
+					install_ipv6_prefix_flt_rule(data_wan_tether->ipv6_prefix);
+					if(data_wan_tether->is_sta == false)
+					{
+						ext_prop = IPACM_Iface::ipacmcfg->GetExtProp(IPA_IP_v6);
+						handle_wan_up_ex(ext_prop, IPA_IP_v6, 0);
+					}
+					else
+					{
+						handle_wan_up(IPA_IP_v6);
+					}
 				}
 			}
+#else
+			if(data_wan_tether->is_sta == false)
+			{
+				ext_prop = IPACM_Iface::ipacmcfg->GetExtProp(IPA_IP_v6);
+				handle_wan_up_ex(ext_prop, IPA_IP_v6, 0);
+			}
+			else
+			{
+				handle_wan_up(IPA_IP_v6);
+			}
+#endif
 		}
 		break;
 
@@ -382,23 +436,37 @@
 			IPACMERR("No event data is found.\n");
 			return;
 		}
+		if(rx_prop == NULL)
+		{
+			IPACMERR("No rx prop.\n");
+			return;
+		}
 		IPACMDBG_H("Backhaul is sta mode?%d, if_index_tether:%d tether_if_name:%s\n", data_wan_tether->is_sta,
 					data_wan_tether->if_index_tether,
 					IPACM_Iface::ipacmcfg->iface_table[data_wan_tether->if_index_tether].iface_name);
-		if (data_wan_tether->if_index_tether == ipa_if_num)
+#ifndef FEATURE_IPACM_HAL
+		if (data_wan_tether->if_index_tether != ipa_if_num)
 		{
-			if(data_wan_tether->is_sta == false && wlan_ap_index > 0)
+			IPACMERR("IPA_HANDLE_WAN_DOWN_TETHER tether_if(%d), not valid (%d) ignore\n", data_wan_tether->if_index_tether, ipa_if_num);
+			return;
+		}
+#endif
+		if(ip_type == IPA_IP_v4 || ip_type == IPA_IP_MAX)
+		{
+#ifdef FEATURE_IPACM_HAL
+			if(is_upstream_set[IPA_IP_v4] == true)
 			{
-				IPACMDBG_H("This is not the first AP instance and not STA mode, ignore WAN_DOWN event.\n");
-				return;
-			}
-			if (rx_prop != NULL)
-			{
-				if(ip_type == IPA_IP_v4 || ip_type == IPA_IP_MAX)
+				IPACMDBG_H("Del upstream for IPv4.\n");
+				is_upstream_set[IPA_IP_v4] = false;
+				if(is_downstream_set[IPA_IP_v4] == true)
 				{
+					IPACMDBG_H("Downstream was set before, deleting UL rules.\n");
 					handle_wan_down(data_wan_tether->is_sta);
 				}
 			}
+#else
+			handle_wan_down(data_wan_tether->is_sta);
+#endif
 		}
 		break;
 
@@ -410,25 +478,107 @@
 			IPACMERR("No event data is found.\n");
 			return;
 		}
+		if(rx_prop == NULL)
+		{
+			IPACMERR("No rx prop.\n");
+			return;
+		}
 		IPACMDBG_H("Backhaul is sta mode?%d, if_index_tether:%d tether_if_name:%s\n", data_wan_tether->is_sta,
 					data_wan_tether->if_index_tether,
 					IPACM_Iface::ipacmcfg->iface_table[data_wan_tether->if_index_tether].iface_name);
-		if (data_wan_tether->if_index_tether == ipa_if_num)
+#ifndef FEATURE_IPACM_HAL
+		if (data_wan_tether->if_index_tether != ipa_if_num)
 		{
-			/* clean up v6 RT rules*/
-			IPACMDBG_H("Received IPA_WAN_V6_DOWN in WLAN-instance and need clean up client IPv6 address \n");
-			/* reset wifi-client ipv6 rt-rules */
-			handle_wlan_client_reset_rt(IPA_IP_v6);
-
-			if (rx_prop != NULL)
+			IPACMERR("IPA_HANDLE_WAN_DOWN_V6_TETHER tether_if(%d), not valid (%d) ignore\n", data_wan_tether->if_index_tether, ipa_if_num);
+			return;
+		}
+#endif
+		if(ip_type == IPA_IP_v6 || ip_type == IPA_IP_MAX)
+		{
+#ifdef FEATURE_IPACM_HAL
+			if(is_upstream_set[IPA_IP_v6] == true)
 			{
-				if(ip_type == IPA_IP_v6 || ip_type == IPA_IP_MAX)
+				IPACMDBG_H("Del upstream for IPv6.\n");
+				is_upstream_set[IPA_IP_v6] = false;
+				if(is_downstream_set[IPA_IP_v6] == true)
 				{
+					IPACMDBG_H("Downstream was set before, deleting UL rules.\n");
+					/* reset usb-client ipv6 rt-rules */
+					handle_wlan_client_reset_rt(IPA_IP_v6);
 					handle_wan_down_v6(data_wan_tether->is_sta);
 				}
 			}
+#else
+			/* reset usb-client ipv6 rt-rules */
+			handle_wlan_client_reset_rt(IPA_IP_v6);
+			handle_wan_down_v6(data_wan_tether->is_sta);
+#endif
+		}
+		break;
+
+	case IPA_DOWNSTREAM_ADD:
+	{
+		ipacm_event_ipahal_stream *data = (ipacm_event_ipahal_stream *)param;
+		ipa_interface_index = iface_ipa_index_query(data->if_index);
+		if(ipa_interface_index == ipa_if_num)
+		{
+			IPACMDBG_H("Received IPA_DOWNSTREAM_ADD event.\n");
+			if(is_downstream_set[data->prefix.iptype] == false)
+			{
+				IPACMDBG_H("Add downstream for IP iptype %d.\n", data->prefix.iptype);
+				is_downstream_set[data->prefix.iptype] = true;
+				memcpy(&prefix[data->prefix.iptype], &data->prefix,
+					sizeof(prefix[data->prefix.iptype]));
+
+				if(is_upstream_set[data->prefix.iptype] == true)
+				{
+					IPACMDBG_H("Upstream was set before, adding modem UL rules.\n");
+					if(ip_type == IPA_IP_MAX || ip_type == data->prefix.iptype)
+					{
+						if (data->prefix.iptype == IPA_IP_v6) /* ipv6 only */
+							install_ipv6_prefix_flt_rule(IPACM_Wan::backhaul_ipv6_prefix);
+
+						if (IPACM_Wan::backhaul_is_sta_mode == false) /* LTE */
+						{
+							ext_prop = IPACM_Iface::ipacmcfg->GetExtProp(data->prefix.iptype);
+							handle_wan_up_ex(ext_prop, data->prefix.iptype, 0);
+						} else {
+							handle_wan_up(data->prefix.iptype); /* STA */
+						}
+					}
+				}
+			}
 		}
 		break;
+	}
+
+	case IPA_DOWNSTREAM_DEL:
+	{
+		ipacm_event_ipahal_stream *data = (ipacm_event_ipahal_stream *)param;
+		ipa_interface_index = iface_ipa_index_query(data->if_index);
+		if(ipa_interface_index == ipa_if_num)
+		{
+			IPACMDBG_H("Received IPA_DOWNSTREAM_DEL event.\n");
+			if(is_downstream_set[data->prefix.iptype] == true)
+			{
+				IPACMDBG_H("Del downstream for IP iptype %d.\n", data->prefix.iptype);
+				is_downstream_set[data->prefix.iptype] = false;
+
+				if(is_upstream_set[data->prefix.iptype] == true)
+				{
+					IPACMDBG_H("Upstream was set before, deleting UL rules.\n");
+					if (data->prefix.iptype == IPA_IP_v4)
+					{
+						handle_wan_down(IPACM_Wan::backhaul_is_sta_mode); /* LTE STA */
+					} else {
+						handle_wlan_client_reset_rt(IPA_IP_v6);
+						handle_wan_down_v6(IPACM_Wan::backhaul_is_sta_mode); /* LTE STA */
+					}
+				}
+			}
+		}
+		break;
+	}
 #else
 	case IPA_HANDLE_WAN_UP:
 		IPACMDBG_H("Received IPA_HANDLE_WAN_UP event\n");
@@ -1671,7 +1821,11 @@
 		}
 	}
 	/* reset the IPA-client pipe enum */
+#ifdef FEATURE_IPACM_HAL
+	handle_tethering_client(true, IPACM_CLIENT_MAX);
+#else
 	handle_tethering_client(true, IPACM_CLIENT_WLAN);
+#endif
 #endif /* defined(FEATURE_IPA_ANDROID)*/
 
 fail:
diff --git a/ipacm/src/IPACM_Xml.cpp b/ipacm/src/IPACM_Xml.cpp
index 120a638..b81856a 100644
--- a/ipacm/src/IPACM_Xml.cpp
+++ b/ipacm/src/IPACM_Xml.cpp
@@ -644,14 +644,11 @@
 						memset(content_buf, 0, sizeof(content_buf));
 						memcpy(content_buf, (void *)content, str_size);
 						content_buf[MAX_XML_STR_LEN-1] = '\0';
-						if (content_buf > 0)
-						{
-							config->extd_firewall_entries[config->num_extd_firewall_entries - 1].attrib.u.v4.dst_addr_mask
-								 = ntohl(inet_addr(content_buf));
-							IPACMDBG_H("IPv4 destination subnet mask is: %s \n", content_buf);
+						config->extd_firewall_entries[config->num_extd_firewall_entries - 1].attrib.u.v4.dst_addr_mask
+							= ntohl(inet_addr(content_buf));
+						IPACMDBG_H("IPv4 destination subnet mask is: %s \n", content_buf);
 						}
 					}
-				}
 				else if (0 == IPACM_util_icmp_string((char*)xml_node->name, IPV4TypeOfService_TAG))
 				{
 					config->extd_firewall_entries[config->num_extd_firewall_entries - 1].attrib.attrib_mask |= IPA_FLT_TOS;
diff --git a/ipanat/src/Android.mk b/ipanat/src/Android.mk
index 905e901..6622e5e 100644
--- a/ipanat/src/Android.mk
+++ b/ipanat/src/Android.mk
@@ -15,6 +15,8 @@
 LOCAL_SRC_FILES := ipa_nat_drv.c \
                    ipa_nat_drvi.c
 
+
+LOCAL_MODULE_PATH_64 := $(TARGET_OUT_VENDOR)/lib64
 LOCAL_CFLAGS := -DDEBUG
 LOCAL_MODULE := libipanat
 LOCAL_MODULE_TAGS := optional