IPACM: enable LAN2LAN traffic using HW-path

Current Lan2Lan traffic (usb->wlan, wlan->usb)
is taking the SW-path which cannot fit the
throughput requirement from customers, the change
is to add client2client specific header/filter,
routing rules in IPA to make Lan2Lan traffic
going through ipa-HW.

Change-Id: I80767339c28f69ca3cb08d97c531ceabd8879a47
diff --git a/ipacm/src/IPACM_Config.cpp b/ipacm/src/IPACM_Config.cpp
index ba0417d..e493e94 100644
--- a/ipacm/src/IPACM_Config.cpp
+++ b/ipacm/src/IPACM_Config.cpp
@@ -185,6 +185,11 @@
 	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));
 
+	rt_tbl_lan2lan_v4.ip = IPA_IP_v4;
+	strncpy(rt_tbl_lan2lan_v4.name, V4_LAN_TO_LAN_ROUTE_TABLE_NAME, sizeof(rt_tbl_lan2lan_v4.name));
+
+	rt_tbl_lan2lan_v6.ip = IPA_IP_v6;
+	strncpy(rt_tbl_lan2lan_v6.name, V6_LAN_TO_LAN_ROUTE_TABLE_NAME, sizeof(rt_tbl_lan2lan_v6.name));
 
 	/* Construct IPACM ipa_client map to rm_resource table */
 	ipa_client_rm_map_tbl[IPA_CLIENT_WLAN1_PROD]= IPA_RM_RESOURCE_WLAN_PROD;
diff --git a/ipacm/src/IPACM_Filtering.cpp b/ipacm/src/IPACM_Filtering.cpp
index 8eeb07b..ef988a8 100644
--- a/ipacm/src/IPACM_Filtering.cpp
+++ b/ipacm/src/IPACM_Filtering.cpp
@@ -364,3 +364,34 @@
 	}
 }
 
+bool IPACM_Filtering::ModifyFilteringRule(struct ipa_ioc_mdfy_flt_rule* ruleTable)
+{
+	int i, ret = 0;
+
+	IPACMDBG("Printing filtering add attributes\n");
+	IPACMDBG("IP type: %d Number of rules: %d commit value: %d\n", ruleTable->ip, ruleTable->num_rules, ruleTable->commit);
+
+	for (i=0; i<ruleTable->num_rules; i++)
+	{
+		IPACMDBG("Filter rule:%d attrib mask: 0x%x\n", i, ruleTable->rules[i].rule.attrib.attrib_mask);
+	}
+
+	ret = ioctl(fd, IPA_IOC_MDFY_FLT_RULE, ruleTable);
+	if (ret != 0)
+	{
+		IPACMERR("Failed modifying filtering rule %p\n", ruleTable);
+
+		for (i = 0; i < ruleTable->num_rules; i++)
+		{
+			if (ruleTable->rules[i].status != 0)
+			{
+				IPACMERR("Modifying filter rule %d failed\n", i);
+			}
+		}
+		return false;
+	}
+
+	IPACMDBG("Modified filtering rule %p\n", ruleTable);
+	return true;
+}
+
diff --git a/ipacm/src/IPACM_IfaceManager.cpp b/ipacm/src/IPACM_IfaceManager.cpp
index 6050783..373e516 100644
--- a/ipacm/src/IPACM_IfaceManager.cpp
+++ b/ipacm/src/IPACM_IfaceManager.cpp
@@ -141,6 +141,7 @@
 				IPACM_EvtDispatcher::registr(IPA_HANDLE_WAN_DOWN, lan);
 				IPACM_EvtDispatcher::registr(IPA_HANDLE_WAN_DOWN_V6, lan);
 				IPACM_EvtDispatcher::registr(IPA_LINK_DOWN_EVENT, lan);
+				IPACM_EvtDispatcher::registr(IPA_LAN_DELETE_SELF, lan);
 				IPACMDBG("ipa_LAN (%s):ipa_index (%d) instance open/registr ok\n", lan->dev_name, lan->ipa_if_num);
 				registr(ipa_interface_index, lan);
 				/* solve the new_addr comes earlier issue */
@@ -183,6 +184,7 @@
 				IPACM_EvtDispatcher::registr(IPA_HANDLE_WAN_DOWN, wl);
 				IPACM_EvtDispatcher::registr(IPA_HANDLE_WAN_DOWN_V6, wl);
 				IPACM_EvtDispatcher::registr(IPA_LINK_DOWN_EVENT, wl);
+				IPACM_EvtDispatcher::registr(IPA_LAN_DELETE_SELF, wl);
 				IPACMDBG("ipa_WLAN (%s):ipa_index (%d) instance open/registr ok\n", wl->dev_name, wl->ipa_if_num);
 				registr(ipa_interface_index, wl);
 				/* solve the new_addr comes earlier issue */
diff --git a/ipacm/src/IPACM_Lan.cpp b/ipacm/src/IPACM_Lan.cpp
index a125a13..d713553 100644
--- a/ipacm/src/IPACM_Lan.cpp
+++ b/ipacm/src/IPACM_Lan.cpp
@@ -1,17 +1,17 @@
-/* 
+/*
 Copyright (c) 2013, 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. 
- 
+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. 
- 
+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.
@@ -42,10 +42,11 @@
 #include <string.h>
 #include <fcntl.h>
 #include <sys/ioctl.h>
-#include <IPACM_Netlink.h>
-#include <IPACM_Lan.h>
-#include <IPACM_Wan.h>
-#include <IPACM_IfaceManager.h>
+#include <assert.h>
+#include "IPACM_Netlink.h"
+#include "IPACM_Lan.h"
+#include "IPACM_Wan.h"
+#include "IPACM_IfaceManager.h"
 #include "linux/rmnet_ipa_fd_ioctl.h"
 #include "linux/ipa_qmi_service_v01.h"
 #include "linux/msm_ipa.h"
@@ -62,7 +63,7 @@
 		IPACMERR("unable to get Nat App instance \n");
 		return;
 	}
-	
+
 	/* support eth multiple clients */
 	eth_client_len = (sizeof(ipa_eth_client)) + (iface_query->num_tx_props * sizeof(eth_client_rt_hdl));
 	eth_client = (ipa_eth_client *)calloc(IPA_MAX_NUM_ETH_CLIENTS, eth_client_len);
@@ -80,7 +81,18 @@
 
 	memset(wan_ul_fl_rule_hdl_v4, 0, MAX_WAN_UL_FILTER_RULES * sizeof(uint32_t));
 	memset(wan_ul_fl_rule_hdl_v6, 0, MAX_WAN_UL_FILTER_RULES * sizeof(uint32_t));
-	
+
+	memset(lan2lan_flt_rule_hdl_v4, 0, MAX_OFFLOAD_PAIR * sizeof(lan2lan_flt_rule_hdl));
+	num_lan2lan_flt_rule_v4 = 0;
+
+	memset(lan2lan_flt_rule_hdl_v6, 0, MAX_OFFLOAD_PAIR * sizeof(lan2lan_flt_rule_hdl));
+	num_lan2lan_flt_rule_v6 = 0;
+
+	memset(lan2lan_hdr_hdl_v4, 0, MAX_OFFLOAD_PAIR*sizeof(lan2lan_hdr_hdl));
+	memset(lan2lan_hdr_hdl_v6, 0, MAX_OFFLOAD_PAIR*sizeof(lan2lan_hdr_hdl));
+
+	is_active = true;
+
 	return;
 }
 
@@ -95,6 +107,12 @@
 /* LAN-iface's callback function */
 void IPACM_Lan::event_callback(ipa_cm_event_id event, void *param)
 {
+	if(is_active == false && event != IPA_LAN_DELETE_SELF)
+	{
+		IPACMDBG("The interface is no longer active, return.\n");
+		return;
+	}
+
 	int ipa_interface_index;
 	ipacm_ext_prop* ext_prop;
 
@@ -108,22 +126,32 @@
 			{
 				IPACMDBG("Received IPA_LINK_DOWN_EVENT\n");
 				handle_down_evt();
-				IPACMDBG("ipa_LAN (%s):ipa_index (%d) instance close \n", IPACM_Iface::ipacmcfg->iface_table[ipa_if_num].iface_name, ipa_if_num);
 				IPACM_Iface::ipacmcfg->DelNatIfaces(dev_name); // delete NAT-iface
-				delete this;
 				return;
 			}
 		}
 		break;
 
+	case IPA_LAN_DELETE_SELF:
+	{
+		ipacm_event_data_fid *data = (ipacm_event_data_fid *)param;
+		if(data->if_index == ipa_if_num)
+		{
+			IPACMDBG("Received IPA_LAN_DELETE_SELF event.\n");
+			IPACMDBG("ipa_LAN (%s):ipa_index (%d) instance close \n", IPACM_Iface::ipacmcfg->iface_table[ipa_if_num].iface_name, ipa_if_num);
+			delete this;
+		}
+		break;
+	}
+
 	case IPA_ADDR_ADD_EVENT:
 		{
 			ipacm_event_data_addr *data = (ipacm_event_data_addr *)param;
 			ipa_interface_index = iface_ipa_index_query(data->if_index);
 
 			if ( (data->iptype == IPA_IP_v4 && data->ipv4_addr == 0) ||
-					 (data->iptype == IPA_IP_v6 && 
-						data->ipv6_addr[0] == 0 && data->ipv6_addr[1] == 0 && 
+					 (data->iptype == IPA_IP_v6 &&
+						data->ipv6_addr[0] == 0 && data->ipv6_addr[1] == 0 &&
 					  data->ipv6_addr[2] == 0 && data->ipv6_addr[3] == 0) )
 			{
 				IPACMDBG("Invalid address, ignore IPA_ADDR_ADD_EVENT event\n");
@@ -136,8 +164,8 @@
 				IPACMDBG("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))) 
+				if( ((data->iptype != ip_type) && (ip_type != IPA_IP_MAX))
+				    || ((data->iptype==IPA_IP_v6) && (num_dft_rt_v6!=MAX_DEFAULT_v6_ROUTE_RULES)))
 				{
 				  IPACMDBG("Got IPA_ADDR_ADD_EVENT ip-family:%d, v6 num %d: \n",data->iptype,num_dft_rt_v6);
 					handle_addr_evt(data);
@@ -151,7 +179,7 @@
 							{
 								ext_prop = IPACM_Iface::ipacmcfg->GetExtProp(IPA_IP_v4);
 								handle_wan_up_ex(ext_prop, IPA_IP_v4);
-							}			
+							}
 						}
 						else
 						{
@@ -209,7 +237,7 @@
 
 	case IPA_HANDLE_WAN_UP:
 		IPACMDBG("Received IPA_HANDLE_WAN_UP event\n");
-	
+
 		if(IPACM_Wan::backhaul_is_sta_mode == false)
 		{
 			if(ip_type == IPA_IP_v4 || ip_type == IPA_IP_MAX)
@@ -226,7 +254,7 @@
 
 	case IPA_HANDLE_WAN_UP_V6:
 		IPACMDBG("Received IPA_HANDLE_WAN_UP_V6 event\n");
-		
+
 		if(IPACM_Wan::backhaul_is_sta_mode == false)
 		{
 			if(ip_type == IPA_IP_v6 || ip_type == IPA_IP_MAX)
@@ -255,7 +283,6 @@
 		{
 			ipacm_event_data_all *data = (ipacm_event_data_all *)param;
 			ipa_interface_index = iface_ipa_index_query(data->if_index);
-
 			IPACMDBG("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)
 			{
@@ -269,6 +296,7 @@
 					return;
 				}
 				handle_eth_client_route_rule(data->mac_addr, data->iptype);
+				handle_lan2lan_client_active(data, IPA_LAN_CLIENT_ACTIVE);
 				return;
 			}
 		}
@@ -278,14 +306,15 @@
 		{
 			ipacm_event_data_all *data = (ipacm_event_data_all *)param;
 			ipa_interface_index = iface_ipa_index_query(data->if_index);
-			
+
 			IPACMDBG("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)
 			{
 				IPACMDBG("ETH iface delete client \n");
 				handle_eth_client_down_evt(data->mac_addr);
+				handle_lan2lan_client_active(data, IPA_LAN_CLIENT_INACTIVE);
 				return;
-			}	
+			}
 		}
 		break;
 
@@ -321,11 +350,11 @@
 		return IPACM_FAILURE;
 	}
 
-	flt_rule_count_v4 = IPV4_DEFAULT_FILTERTING_RULES + IPACM_Iface::ipacmcfg->ipa_num_private_subnet;
-	
+	flt_rule_count_v4 = IPV4_DEFAULT_FILTERTING_RULES + MAX_OFFLOAD_PAIR + IPACM_Iface::ipacmcfg->ipa_num_private_subnet;
+
 	if(is_sta_mode == false)
 	{
-		if (m_filtering.DeleteFilteringHdls(wan_ul_fl_rule_hdl_v4, 
+		if (m_filtering.DeleteFilteringHdls(wan_ul_fl_rule_hdl_v4,
 			IPA_IP_v4, num_wan_ul_fl_rule_v4) == false)
 		{
 			IPACMERR("Error Deleting RuleTable(1) to Filtering, aborting...\n");
@@ -361,9 +390,9 @@
 			IPACMERR("Error Adding RuleTable(1) to Filtering, aborting...\n");
 			close(fd);
 			return IPACM_FAILURE;
-		}		
+		}
 	}
-	
+
 	close(fd);
 	return IPACM_SUCCESS;
 }
@@ -373,35 +402,31 @@
 {
 	struct ipa_ioc_add_rt_rule *rt_rule;
 	struct ipa_rt_rule_add *rt_rule_entry;
-	struct ipa_flt_rule_add flt_rule_entry;
 	const int NUM_RULES = 1;
 	int num_ipv6_addr;
 	int res = IPACM_SUCCESS;
 
-	/* construct ipa_ioc_add_flt_rule with v6 rules */
-	ipa_ioc_add_flt_rule *m_pFilteringTable;
-
 	IPACMDBG("set route/filter rule ip-type: %d \n", data->iptype);
 
 	if (data->iptype == IPA_IP_v4)
 	{
-	rt_rule = (struct ipa_ioc_add_rt_rule *)
-		 calloc(1, sizeof(struct ipa_ioc_add_rt_rule) +
-						NUM_RULES * sizeof(struct ipa_rt_rule_add));
+		rt_rule = (struct ipa_ioc_add_rt_rule *)
+			 calloc(1, sizeof(struct ipa_ioc_add_rt_rule) +
+							NUM_RULES * sizeof(struct ipa_rt_rule_add));
 
 		if (!rt_rule)
-	{
-		IPACMERR("Error Locate ipa_ioc_add_rt_rule memory...\n");
-		return IPACM_FAILURE;
-	}
+		{
+			IPACMERR("Error Locate ipa_ioc_add_rt_rule memory...\n");
+			return IPACM_FAILURE;
+		}
 
-	rt_rule->commit = 1;
-	rt_rule->num_rules = NUM_RULES;
-	rt_rule->ip = data->iptype;
-	rt_rule_entry = &rt_rule->rules[0];
-	rt_rule_entry->at_rear = false;
-	rt_rule_entry->rule.dst = iface_query->excp_pipe;  //go to A5
-	rt_rule_entry->rule.attrib.attrib_mask = IPA_FLT_DST_ADDR;
+		rt_rule->commit = 1;
+		rt_rule->num_rules = NUM_RULES;
+		rt_rule->ip = data->iptype;
+		rt_rule_entry = &rt_rule->rules[0];
+		rt_rule_entry->at_rear = false;
+		rt_rule_entry->rule.dst = iface_query->excp_pipe;  //go to A5
+		rt_rule_entry->rule.attrib.attrib_mask = IPA_FLT_DST_ADDR;
    		strcpy(rt_rule->rt_tbl_name, IPACM_Iface::ipacmcfg->rt_tbl_lan_v4.name);
 		rt_rule_entry->rule.attrib.u.v4.dst_addr      = data->ipv4_addr;
 		rt_rule_entry->rule.attrib.u.v4.dst_addr_mask = 0xFFFFFFFF;
@@ -418,22 +443,23 @@
 	    	goto fail;
 	    }
 		dft_rt_rule_hdl[0] = rt_rule_entry->rt_rule_hdl;
-        IPACMDBG("ipv4 iface rt-rule hdl1=0x%x\n", dft_rt_rule_hdl[0]);		
+        IPACMDBG("ipv4 iface rt-rule hdl1=0x%x\n", dft_rt_rule_hdl[0]);
 		/* initial multicast/broadcast/fragment filter rule */
 		IPACM_Iface::init_fl_rule(data->iptype);
+		add_dummy_lan2lan_flt_rule(data->iptype);
 	}
 	else
 	{
 	    /* check if see that v6-addr already or not*/
 	    for(num_ipv6_addr=0;num_ipv6_addr<num_dft_rt_v6;num_ipv6_addr++)
 	    {
-                if((ipv6_addr[num_ipv6_addr][0] == data->ipv6_addr[0]) &&	
-	           (ipv6_addr[num_ipv6_addr][1] == data->ipv6_addr[1]) &&	
-	            (ipv6_addr[num_ipv6_addr][2] == data->ipv6_addr[2]) &&	
-	                (ipv6_addr[num_ipv6_addr][3] == data->ipv6_addr[3]))
-                {
-	           return IPACM_SUCCESS;
-	           break; 
+            if((ipv6_addr[num_ipv6_addr][0] == data->ipv6_addr[0]) &&
+	           (ipv6_addr[num_ipv6_addr][1] == data->ipv6_addr[1]) &&
+	           (ipv6_addr[num_ipv6_addr][2] == data->ipv6_addr[2]) &&
+	           (ipv6_addr[num_ipv6_addr][3] == data->ipv6_addr[3]))
+            {
+				return IPACM_SUCCESS;
+				break;
 	        }
 	    }
 
@@ -443,7 +469,7 @@
 
 		if (!rt_rule)
 		{
-		IPACMERR("Error Locate ipa_ioc_add_rt_rule memory...\n");
+			IPACMERR("Error Locate ipa_ioc_add_rt_rule memory...\n");
 			return IPACM_FAILURE;
 		}
 
@@ -455,7 +481,7 @@
 	    rt_rule_entry = &rt_rule->rules[0];
 	    rt_rule_entry->at_rear = false;
 	    rt_rule_entry->rule.dst = iface_query->excp_pipe;  //go to A5
-	    rt_rule_entry->rule.attrib.attrib_mask = IPA_FLT_DST_ADDR;	
+	    rt_rule_entry->rule.attrib.attrib_mask = IPA_FLT_DST_ADDR;
 		rt_rule_entry->rule.attrib.u.v6.dst_addr[0] = data->ipv6_addr[0];
 		rt_rule_entry->rule.attrib.u.v6.dst_addr[1] = data->ipv6_addr[1];
 		rt_rule_entry->rule.attrib.u.v6.dst_addr[2] = data->ipv6_addr[2];
@@ -468,37 +494,38 @@
 		ipv6_addr[num_dft_rt_v6][1] = data->ipv6_addr[1];
 		ipv6_addr[num_dft_rt_v6][2] = data->ipv6_addr[2];
 		ipv6_addr[num_dft_rt_v6][3] = data->ipv6_addr[3];
-	if (false == m_routing.AddRoutingRule(rt_rule))
-	{
-		IPACMERR("Routing rule addition failed!\n");
-		res = IPACM_FAILURE;
-		goto fail;
-	}
-	else if (rt_rule_entry->status)
-	{
-		IPACMERR("rt rule adding failed. Result=%d\n", rt_rule_entry->status);
-		res = rt_rule_entry->status;
-		goto fail;
-	}
+
+		if (false == m_routing.AddRoutingRule(rt_rule))
+		{
+			IPACMERR("Routing rule addition failed!\n");
+			res = IPACM_FAILURE;
+			goto fail;
+		}
+		else if (rt_rule_entry->status)
+		{
+			IPACMERR("rt rule adding failed. Result=%d\n", rt_rule_entry->status);
+			res = rt_rule_entry->status;
+			goto fail;
+		}
 		dft_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES + 2*num_dft_rt_v6] = rt_rule_entry->rt_rule_hdl;
 
-        /* setup same rule for v6_wan table*/		
+        /* setup same rule for v6_wan table*/
 		strcpy(rt_rule->rt_tbl_name, IPACM_Iface::ipacmcfg->rt_tbl_wan_v6.name);
 	    if (false == m_routing.AddRoutingRule(rt_rule))
-	{
+		{
 	    	IPACMERR("Routing rule addition failed!\n");
 	    	res = IPACM_FAILURE;
 	    	goto fail;
-	}
+		}
 	    else if (rt_rule_entry->status)
-	{
+		{
 	    	IPACMERR("rt rule adding failed. Result=%d\n", rt_rule_entry->status);
 	    	res = rt_rule_entry->status;
 	    	goto fail;
-	    }		
+	    }
 		dft_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES + 2*num_dft_rt_v6+1] = rt_rule_entry->rt_rule_hdl;
 
-		IPACMDBG("ipv6 wan iface rt-rule hdl=0x%x hdl=0x%x, num_dft_rt_v6: %d \n", 
+		IPACMDBG("ipv6 wan iface rt-rule hdl=0x%x hdl=0x%x, num_dft_rt_v6: %d \n",
 		          dft_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES + 2*num_dft_rt_v6],
 		          dft_rt_rule_hdl[MAX_DEFAULT_v4_ROUTE_RULES + 2*num_dft_rt_v6+1],num_dft_rt_v6);
 
@@ -506,6 +533,7 @@
 		{
 			/* initial multicast/broadcast/fragment filter rule */
 			IPACM_Iface::init_fl_rule(data->iptype);
+			add_dummy_lan2lan_flt_rule(data->iptype);
 		}
 		num_dft_rt_v6++;
 	}
@@ -514,7 +542,6 @@
 
 fail:
 	free(rt_rule);
-
 	return res;
 }
 
@@ -567,7 +594,7 @@
 			free(m_pFilteringTable);
 			return IPACM_FAILURE;
 		}
-		
+
 		for (i = 0; i < (IPACM_Iface::ipacmcfg->ipa_num_private_subnet); i++)
 		{
 			memset(&flt_rule_entry, 0, sizeof(struct ipa_flt_rule_add));
@@ -599,7 +626,7 @@
 		}
 
 		flt_rule_count_v4 += IPACM_Iface::ipacmcfg->ipa_num_private_subnet;
-		
+
 		/* copy filter rule hdls */
 		for (i = 0; i < IPACM_Iface::ipacmcfg->ipa_num_private_subnet; i++)
 		{
@@ -721,7 +748,7 @@
 		flt_rule_entry.status = -1;
 		flt_rule_entry.rule.action = IPA_PASS_TO_ROUTING;
 			flt_rule_entry.rule.rt_tbl_hdl = IPACM_Iface::ipacmcfg->rt_tbl_v6.hdl;
-             
+
 		memcpy(&flt_rule_entry.rule.attrib,
 					 &rx_prop->rx[0].attrib,
 					 sizeof(flt_rule_entry.rule.attrib));
@@ -750,7 +777,7 @@
 
 		/* copy filter hdls */
 		dft_v6fl_rule_hdl[IPV6_DEFAULT_FILTERTING_RULES] = m_pFilteringTable->rules[0].flt_rule_hdl;
-		free(m_pFilteringTable);	
+		free(m_pFilteringTable);
 	}
 
 	return IPACM_SUCCESS;
@@ -759,27 +786,27 @@
 int IPACM_Lan::handle_wan_up_ex(ipacm_ext_prop* ext_prop, ipa_ip_type iptype)
 {
 	int fd, ret = IPACM_SUCCESS, cnt;
-	IPACM_Config* ipacm_config = IPACM_Iface::ipacmcfg;	
-	struct ipa_ioc_write_qmapid mux;						
+	IPACM_Config* ipacm_config = IPACM_Iface::ipacmcfg;
+	struct ipa_ioc_write_qmapid mux;
 
 	if(rx_prop != NULL)
 	{
 		/* give mud ID to IPA-driver for WLAN/LAN pkts */
-		fd = open(IPA_DEVICE_NAME, O_RDWR);	
-		if (0 == fd) 
-		{		
-			IPACMDBG("Failed opening %s.\n", IPA_DEVICE_NAME);		
-			return IPACM_FAILURE;						
+		fd = open(IPA_DEVICE_NAME, O_RDWR);
+		if (0 == fd)
+		{
+			IPACMDBG("Failed opening %s.\n", IPA_DEVICE_NAME);
+			return IPACM_FAILURE;
 		}
 
-		mux.qmap_id = ipacm_config->GetQmapId();	
+		mux.qmap_id = ipacm_config->GetQmapId();
 		for(cnt=0; cnt<rx_prop->num_rx_props; cnt++)
 		{
-			mux.client = rx_prop->rx[cnt].src_pipe;	
-			ret = ioctl(fd, IPA_IOC_WRITE_QMAPID, &mux);	
-			if (ret) 
-			{	
-				IPACMERR("Failed to write mux id %d\n", mux.qmap_id);		
+			mux.client = rx_prop->rx[cnt].src_pipe;
+			ret = ioctl(fd, IPA_IOC_WRITE_QMAPID, &mux);
+			if (ret)
+			{
+				IPACMERR("Failed to write mux id %d\n", mux.qmap_id);
 				close(fd);
 				return IPACM_FAILURE;
 			}
@@ -803,14 +830,14 @@
 	struct ipa_ioc_add_hdr *pHeaderDescriptor = NULL;
     uint32_t cnt;
 	int clnt_indx;
-	
+
 	clnt_indx = get_eth_client_index(mac_addr);
-	
+
 	if (clnt_indx != IPACM_INVALID_INDEX)
 	{
 		IPACMERR("eth client is found/attached already with index %d \n", clnt_indx);
 		return IPACM_FAILURE;
-	}	
+	}
 
 	/* add header to IPA */
 	if (num_eth_client >= IPA_MAX_NUM_ETH_CLIENTS)
@@ -819,13 +846,13 @@
 		return IPACM_FAILURE;
 	}
 
-	IPACMDBG("ETH client number: %d\n", num_eth_client);	
-	
+	IPACMDBG("ETH client number: %d\n", num_eth_client);
+
 	memcpy(get_client_memptr(eth_client, num_eth_client)->mac,
 				 mac_addr,
 				 sizeof(get_client_memptr(eth_client, num_eth_client)->mac));
-	
-	
+
+
 	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]);
@@ -838,7 +865,7 @@
 					 get_client_memptr(eth_client, num_eth_client)->mac[4],
 					 get_client_memptr(eth_client, num_eth_client)->mac[5]);
 
-	/* add header to IPA */ 
+	/* add header to IPA */
 	if(tx_prop != NULL)
 	{
 		len = sizeof(struct ipa_ioc_add_hdr) + (1 * sizeof(struct ipa_hdr_add));
@@ -879,7 +906,7 @@
 								else
 								{
 									memcpy(pHeaderDescriptor->hdr[0].hdr,
-												 sCopyHeader.hdr, 
+												 sCopyHeader.hdr,
 												 sCopyHeader.hdr_len);
 								}
 
@@ -888,7 +915,7 @@
 								{
 									memcpy(&pHeaderDescriptor->hdr[0].hdr[sCopyHeader.eth2_ofst],
 											 mac_addr,
-											 IPA_MAC_ADDR_SIZE);								
+											 IPA_MAC_ADDR_SIZE);
 								}
 
 								pHeaderDescriptor->commit = true;
@@ -899,7 +926,7 @@
 
 								sprintf(index, "%d", ipa_if_num);
 								strncpy(pHeaderDescriptor->hdr[0].name, index, sizeof(index));
-								
+
 								strncat(pHeaderDescriptor->hdr[0].name,
 												IPA_ETH_HDR_NAME_v4,
 												sizeof(IPA_ETH_HDR_NAME_v4));
@@ -925,9 +952,9 @@
 												 num_eth_client,
 												 pHeaderDescriptor->hdr[0].name,
 												 get_client_memptr(eth_client, num_eth_client)->hdr_hdl_v4);
-									get_client_memptr(eth_client, num_eth_client)->ipv4_header_set=true;	
+									get_client_memptr(eth_client, num_eth_client)->ipv4_header_set=true;
 
-					break;  
+					break;
 				 }
 		}
 
@@ -969,7 +996,7 @@
 
 				/* 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);
@@ -989,7 +1016,7 @@
 
 				sprintf(index, "%d", header_name_count);
 				strncat(pHeaderDescriptor->hdr[0].name, index, sizeof(index));
-						
+
 				pHeaderDescriptor->hdr[0].hdr_len = sCopyHeader.hdr_len;
 				pHeaderDescriptor->hdr[0].hdr_hdl = -1;
 				pHeaderDescriptor->hdr[0].is_partial = 0;
@@ -1008,10 +1035,10 @@
 						 num_eth_client,
 						 pHeaderDescriptor->hdr[0].name,
 									 get_client_memptr(eth_client, num_eth_client)->hdr_hdl_v6);
-						 
-									get_client_memptr(eth_client, num_eth_client)->ipv6_header_set=true;			   
 
-				break;  
+									get_client_memptr(eth_client, num_eth_client)->ipv6_header_set=true;
+
+				break;
 
 			}
 		}
@@ -1095,22 +1122,22 @@
 				(data->ipv6_addr[2] != 0) || (data->ipv6_addr[3] || 0)) /* check if all 0 not valid ipv6 address */
 		{
 		   IPACMDBG("ipv6 address: 0x%x:%x:%x:%x\n", data->ipv6_addr[0], data->ipv6_addr[1], data->ipv6_addr[2], data->ipv6_addr[3]);
-                   if(get_client_memptr(eth_client, clnt_indx)->ipv6_set < IPV6_NUM_ADDR)		
+                   if(get_client_memptr(eth_client, clnt_indx)->ipv6_set < IPV6_NUM_ADDR)
 		   {
-		   
+
 		       for(v6_num=0;v6_num < get_client_memptr(eth_client, clnt_indx)->ipv6_set;v6_num++)
 	               {
-			      if( data->ipv6_addr[0] == get_client_memptr(eth_client, clnt_indx)->v6_addr[v6_num][0] && 
+			      if( data->ipv6_addr[0] == get_client_memptr(eth_client, clnt_indx)->v6_addr[v6_num][0] &&
 			           data->ipv6_addr[1] == get_client_memptr(eth_client, clnt_indx)->v6_addr[v6_num][1] &&
-			  	        data->ipv6_addr[2]== get_client_memptr(eth_client, clnt_indx)->v6_addr[v6_num][2] && 
+			  	        data->ipv6_addr[2]== get_client_memptr(eth_client, clnt_indx)->v6_addr[v6_num][2] &&
 			  	         data->ipv6_addr[3] == get_client_memptr(eth_client, clnt_indx)->v6_addr[v6_num][3])
 			      {
 			  	    IPACMDBG("Already see this ipv6 addr for client:%d\n", clnt_indx);
 			  	    return IPACM_FAILURE; /* not setup the RT rules*/
 			  		break;
-			      }  
-		       }	   
-		   
+			      }
+		       }
+
 		       /* not see this ipv6 before for wifi client*/
 			   get_client_memptr(eth_client, clnt_indx)->v6_addr[get_client_memptr(eth_client, clnt_indx)->ipv6_set][0] = data->ipv6_addr[0];
 			   get_client_memptr(eth_client, clnt_indx)->v6_addr[get_client_memptr(eth_client, clnt_indx)->ipv6_set][1] = data->ipv6_addr[1];
@@ -1164,7 +1191,7 @@
 					 get_client_memptr(eth_client, eth_index)->ipv6_set,
 					 get_client_memptr(eth_client, eth_index)->route_rule_set_v6);
 	}
-	
+
 	/* Add default routing rules if not set yet */
 	if ((iptype == IPA_IP_v4
 			 && get_client_memptr(eth_client, eth_index)->route_rule_set_v4 == false
@@ -1173,10 +1200,10 @@
 		            && get_client_memptr(eth_client, eth_index)->route_rule_set_v6 < get_client_memptr(eth_client, eth_index)->ipv6_set
 					))
 	{
-	
+
         /* 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);	
-	
+        IPACM_Iface::ipacmcfg->AddRmDepend(IPACM_Iface::ipacmcfg->ipa_client_rm_map_tbl[tx_prop->tx[0].dst_pipe],false);
+
 		rt_rule = (struct ipa_ioc_add_rt_rule *)
 			 calloc(1, sizeof(struct ipa_ioc_add_rt_rule) +
 						NUM * sizeof(struct ipa_rt_rule_add));
@@ -1195,26 +1222,26 @@
 		{
 			if(iptype != tx_prop->tx[tx_index].ip)
 		    {
-				IPACMDBG("Tx:%d, ip-type: %d conflict ip-type: %d no RT-rule added\n", 
-						tx_index, tx_prop->tx[tx_index].ip,iptype);		
+				IPACMDBG("Tx:%d, ip-type: %d conflict ip-type: %d no RT-rule added\n",
+						tx_index, tx_prop->tx[tx_index].ip,iptype);
 		   	        continue;
-		    }	   
-			
+		    }
+
   	   	    rt_rule_entry = &rt_rule->rules[0];
 			rt_rule_entry->at_rear = 0;
 
 			if (iptype == IPA_IP_v4)
 			{
-		        IPACMDBG("client index(%d):ipv4 address: 0x%x\n", eth_index, 
+		        IPACMDBG("client index(%d):ipv4 address: 0x%x\n", eth_index,
 		  		        get_client_memptr(eth_client, eth_index)->v4_addr);
-          
+
                 IPACMDBG("client(%d): v4 header handle:(0x%x)\n",
 		  				 eth_index,
 		  				 get_client_memptr(eth_client, eth_index)->hdr_hdl_v4);
 				strncpy(rt_rule->rt_tbl_name,
 								IPACM_Iface::ipacmcfg->rt_tbl_lan_v4.name,
 								sizeof(rt_rule->rt_tbl_name));
-	   
+
 
 			    rt_rule_entry->rule.dst = tx_prop->tx[tx_index].dst_pipe;
 			    memcpy(&rt_rule_entry->rule.attrib,
@@ -1231,26 +1258,26 @@
   	          	            free(rt_rule);
   	          	            return IPACM_FAILURE;
 			    }
-			  
+
 			    /* copy ipv4 RT hdl */
 		        get_client_memptr(eth_client, eth_index)->eth_rt_hdl[tx_index].eth_rt_rule_hdl_v4 =
   	   	        rt_rule->rules[0].rt_rule_hdl;
-		        IPACMDBG("tx:%d, rt rule hdl=%x ip-type: %d\n", tx_index, 
+		        IPACMDBG("tx:%d, rt rule hdl=%x ip-type: %d\n", tx_index,
 		      	get_client_memptr(eth_client, eth_index)->eth_rt_hdl[tx_index].eth_rt_rule_hdl_v4, iptype);
-		  
+
   	   	    } else {
-		   
+
 		        for(v6_num = get_client_memptr(eth_client, eth_index)->route_rule_set_v6;v6_num < get_client_memptr(eth_client, eth_index)->ipv6_set;v6_num++)
 			    {
                     IPACMDBG("client(%d): v6 header handle:(0x%x)\n",
 		  	    			 eth_index,
 		  	    			 get_client_memptr(eth_client, eth_index)->hdr_hdl_v6);
-	            
+
 		            /* v6 LAN_RT_TBL */
 			    	strncpy(rt_rule->rt_tbl_name,
 			    					IPACM_Iface::ipacmcfg->rt_tbl_v6.name,
 			    					sizeof(rt_rule->rt_tbl_name));
-					
+
 		            /* 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));
@@ -1264,7 +1291,7 @@
 					rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[1] = 0xFFFFFFFF;
 					rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[2] = 0xFFFFFFFF;
 					rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[3] = 0xFFFFFFFF;
-                    
+
    	                if (false == m_routing.AddRoutingRule(rt_rule))
   	                {
   	                	    IPACMERR("Routing rule addition failed!\n");
@@ -1273,14 +1300,14 @@
 			        }
 
 		            get_client_memptr(eth_client, eth_index)->eth_rt_hdl[tx_index].eth_rt_rule_hdl_v6[v6_num] = rt_rule->rules[0].rt_rule_hdl;
-		            IPACMDBG("tx:%d, rt rule hdl=%x ip-type: %d\n", tx_index, 
+		            IPACMDBG("tx:%d, rt rule hdl=%x ip-type: %d\n", tx_index,
 		            				 get_client_memptr(eth_client, eth_index)->eth_rt_hdl[tx_index].eth_rt_rule_hdl_v6[v6_num], iptype);
-			        
+
 			        /*Copy same rule to v6 WAN RT TBL*/
   	                strncpy(rt_rule->rt_tbl_name,
   	                 					IPACM_Iface::ipacmcfg->rt_tbl_wan_v6.name,
   	                 					sizeof(rt_rule->rt_tbl_name));
-   	                
+
                     /* Downlink traffic from Wan iface, directly through IPA */
 					rt_rule_entry->rule.dst = tx_prop->tx[tx_index].dst_pipe;
 			        memcpy(&rt_rule_entry->rule.attrib,
@@ -1296,7 +1323,7 @@
 					rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[1] = 0xFFFFFFFF;
 					rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[2] = 0xFFFFFFFF;
 					rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[3] = 0xFFFFFFFF;
-   	                
+
 		            if (false == m_routing.AddRoutingRule(rt_rule))
 		            {
 							IPACMERR("Routing rule addition failed!\n");
@@ -1305,11 +1332,11 @@
 		            }
 
 		            get_client_memptr(eth_client, eth_index)->eth_rt_hdl[tx_index].eth_rt_rule_hdl_v6_wan[v6_num] = rt_rule->rules[0].rt_rule_hdl;
-					IPACMDBG("tx:%d, rt rule hdl=%x ip-type: %d\n", tx_index, 
-		            				 get_client_memptr(eth_client, eth_index)->eth_rt_hdl[tx_index].eth_rt_rule_hdl_v6_wan[v6_num], iptype);			  
+					IPACMDBG("tx:%d, rt rule hdl=%x ip-type: %d\n", tx_index,
+		            				 get_client_memptr(eth_client, eth_index)->eth_rt_hdl[tx_index].eth_rt_rule_hdl_v6_wan[v6_num], iptype);
 			    }
 			}
-		   
+
   	    } /* end of for loop */
 
 		free(rt_rule);
@@ -1349,8 +1376,8 @@
 	{
 	        IPACMDBG("Deleting Nat Rules\n");
 	        Nat_App->UpdatePwrSaveIf(get_client_memptr(eth_client, clt_indx)->v4_addr);
- 	}	
-	
+ 	}
+
 	if (delete_eth_rtrules(clt_indx, IPA_IP_v4))
 	{
 		IPACMERR("unbale to delete ecm-client v4 route rules\n");
@@ -1410,12 +1437,12 @@
 		get_client_memptr(eth_client, clt_indx)->route_rule_set_v4 = get_client_memptr(eth_client, (clt_indx + 1))->route_rule_set_v4;
 		get_client_memptr(eth_client, clt_indx)->route_rule_set_v6 = get_client_memptr(eth_client, (clt_indx + 1))->route_rule_set_v6;
 
-        for (num_v6=0;num_v6< get_client_memptr(eth_client, clt_indx)->ipv6_set;num_v6++)	 
-	    {					
+        for (num_v6=0;num_v6< get_client_memptr(eth_client, clt_indx)->ipv6_set;num_v6++)
+	    {
 		    get_client_memptr(eth_client, clt_indx)->v6_addr[num_v6][0] = get_client_memptr(eth_client, (clt_indx + 1))->v6_addr[num_v6][0];
 		    get_client_memptr(eth_client, clt_indx)->v6_addr[num_v6][1] = get_client_memptr(eth_client, (clt_indx + 1))->v6_addr[num_v6][1];
 		    get_client_memptr(eth_client, clt_indx)->v6_addr[num_v6][2] = get_client_memptr(eth_client, (clt_indx + 1))->v6_addr[num_v6][2];
-		    get_client_memptr(eth_client, clt_indx)->v6_addr[num_v6][3] = get_client_memptr(eth_client, (clt_indx + 1))->v6_addr[num_v6][3]; 
+		    get_client_memptr(eth_client, clt_indx)->v6_addr[num_v6][3] = get_client_memptr(eth_client, (clt_indx + 1))->v6_addr[num_v6][3];
         }
 
 		for (tx_index = 0; tx_index < iface_query->num_tx_props; tx_index++)
@@ -1423,7 +1450,7 @@
 			get_client_memptr(eth_client, clt_indx)->eth_rt_hdl[tx_index].eth_rt_rule_hdl_v4 =
 				 get_client_memptr(eth_client, (clt_indx + 1))->eth_rt_hdl[tx_index].eth_rt_rule_hdl_v4;
 
-			for(num_v6=0;num_v6< get_client_memptr(eth_client, clt_indx)->route_rule_set_v6;num_v6++)	 
+			for(num_v6=0;num_v6< get_client_memptr(eth_client, clt_indx)->route_rule_set_v6;num_v6++)
 			{
 			  get_client_memptr(eth_client, clt_indx)->eth_rt_hdl[tx_index].eth_rt_rule_hdl_v6[num_v6] =
 			   	 get_client_memptr(eth_client, (clt_indx + 1))->eth_rt_hdl[tx_index].eth_rt_rule_hdl_v6[num_v6];
@@ -1440,10 +1467,10 @@
 	/* Del RM dependency */
 	if(num_eth_client == 0)
 	{
-	   /* Delete corresponding ipa_rm_resource_name of TX-endpoint after delete all IPV4V6 RT-rule*/ 
+	   /* Delete corresponding ipa_rm_resource_name of TX-endpoint after delete all IPV4V6 RT-rule*/
 	   IPACM_Iface::ipacmcfg->DelRmDepend(IPACM_Iface::ipacmcfg->ipa_client_rm_map_tbl[tx_prop->tx[0].dst_pipe]);
 	}
-				
+
 	return IPACM_SUCCESS;
 }
 
@@ -1473,8 +1500,8 @@
 		}
 	}
 
-        IPACMDBG("Finished delete default iface ipv4 rules \n ");	
-	
+        IPACMDBG("Finished delete default iface ipv4 rules \n ");
+
 	/* delete default v6 routing rule */
 	if (ip_type != IPA_IP_v4)
 	{
@@ -1491,27 +1518,28 @@
 		}
 	}
 
-	IPACMDBG("Finished delete default iface ipv6 rules \n ");	
+
+	IPACMDBG("Finished delete default iface ipv6 rules \n ");
 	/* clean eth-client header, routing rules */
 	IPACMDBG("left %d eth clients need to be deleted \n ", num_eth_client);
 	for (i = 0; i < num_eth_client; i++)
 	{
 			delete_eth_rtrules(i, IPA_IP_v4);
 			delete_eth_rtrules(i, IPA_IP_v6);
-	
+
 			IPACMDBG("Delete %d client header\n", num_eth_client);
-	
-	
+
+
 			if(get_client_memptr(eth_client, i)->ipv4_header_set == true)
 			{
-			if (m_header.DeleteHeaderHdl(get_client_memptr(eth_client, i)->hdr_hdl_v4)
+				if (m_header.DeleteHeaderHdl(get_client_memptr(eth_client, i)->hdr_hdl_v4)
 					== false)
 				{
 					res = IPACM_FAILURE;
 					goto fail;
 				}
 			}
-	
+
 			if(get_client_memptr(eth_client, i)->ipv6_header_set == true)
 			{
 			if (m_header.DeleteHeaderHdl(get_client_memptr(eth_client, i)->hdr_hdl_v6)
@@ -1520,14 +1548,15 @@
 				res = IPACM_FAILURE;
 				goto fail;
 			}
-			}		
+			}
 	} /* end of for loop */
-	
+
 	/* free the edm clients cache */
 	IPACMDBG("Free ecm clients cache\n");
 
-	/* Delete corresponding ipa_rm_resource_name of TX-endpoint after delete all IPV4V6 RT-rule */ 
-	IPACM_Iface::ipacmcfg->DelRmDepend(IPACM_Iface::ipacmcfg->ipa_client_rm_map_tbl[tx_prop->tx[0].dst_pipe]);				
+	/* Delete corresponding ipa_rm_resource_name of TX-endpoint after delete all IPV4V6 RT-rule */
+	IPACM_Iface::ipacmcfg->DelRmDepend(IPACM_Iface::ipacmcfg->ipa_client_rm_map_tbl[tx_prop->tx[0].dst_pipe]);
+
 
 	/* check software routing fl rule hdl */
 	if (softwarerouting_act == true && rx_prop != NULL)
@@ -1548,6 +1577,17 @@
 			goto fail;
 		}
 
+		for(i=0; i<MAX_OFFLOAD_PAIR; i++)
+		{
+			if(m_filtering.DeleteFilteringHdls(&(lan2lan_flt_rule_hdl_v4[i].rule_hdl), IPA_IP_v4, 1) == false)
+			{
+				IPACMERR("Error deleting lan2lan IPv4 flt rules.\n");
+				res = IPACM_FAILURE;
+				goto fail;
+			}
+		}
+		IPACMDBG("Deleted lan2lan IPv4 flt rules.\n");
+
 		/* free private-subnet ipv4 filter rules */
 		if (m_filtering.DeleteFilteringHdls(
 					private_fl_rule_hdl,
@@ -1560,7 +1600,7 @@
 		}
 	}
 
-        IPACMDBG("Finished delete default iface ipv4 filtering rules \n ");
+    IPACMDBG("Finished delete default iface ipv4 filtering rules \n ");
 
 	if (ip_type != IPA_IP_v4 && rx_prop != NULL)
 	{
@@ -1572,10 +1612,21 @@
 			res = IPACM_FAILURE;
 			goto fail;
 		}
+
+		for(i=0; i<MAX_OFFLOAD_PAIR; i++)
+		{
+			if(m_filtering.DeleteFilteringHdls(&(lan2lan_flt_rule_hdl_v6[i].rule_hdl), IPA_IP_v6, 1) == false)
+			{
+				IPACMERR("Error deleting lan2lan IPv4 flt rules.\n");
+				res = IPACM_FAILURE;
+				goto fail;
+			}
+		}
+		IPACMDBG("Deleted lan2lan IPv6 flt rules.\n");
 	}
 
         IPACMDBG("Finished delete default iface ipv6 filtering rules \n ");
-	
+
 	/* delete wan filter rule */
 	if (IPACM_Wan::isWanUP() && rx_prop != NULL)
 	{
@@ -1588,17 +1639,23 @@
 		IPACMDBG("LAN IF goes down, backhaul type %d\n", IPACM_Wan::backhaul_is_sta_mode);
 		handle_wan_down_v6(IPACM_Wan::backhaul_is_sta_mode);
 	}
-	
+
+	/* posting ip to lan2lan module to delete RT/FILTER rules*/
+	post_lan2lan_client_disconnect_msg();
+
 fail:
-	/* Delete corresponding ipa_rm_resource_name of RX-endpoint after delete all IPV4V6 FT-rule */ 
-	IPACM_Iface::ipacmcfg->DelRmDepend(IPACM_Iface::ipacmcfg->ipa_client_rm_map_tbl[rx_prop->rx[0].src_pipe]);	
+	/* Delete corresponding ipa_rm_resource_name of RX-endpoint after delete all IPV4V6 FT-rule */
+	IPACM_Iface::ipacmcfg->DelRmDepend(IPACM_Iface::ipacmcfg->ipa_client_rm_map_tbl[rx_prop->rx[0].src_pipe]);
 	IPACMDBG("Finished delete dependency \n ");
 
-	free(eth_client);
+	if (eth_client != NULL)
+	{
+		free(eth_client);
+	}
 
 	if (tx_prop != NULL)
 	{
-		free(tx_prop); 
+		free(tx_prop);
 	}
 	if (rx_prop != NULL)
 	{
@@ -1608,7 +1665,10 @@
 	{
 		free(iface_query);
 	}
-	
+
+	is_active = false;
+	post_del_self_evt();
+
 	return res;
 }
 
@@ -1655,7 +1715,7 @@
 
 	IPACMDBG("flt_index: src pipe: %d, num of rules: %d, ebd pipe: %d, mux id: %d\n", flt_index.source_pipe_index,
 				flt_index.filter_index_list_len, flt_index.embedded_pipe_index, flt_index.embedded_call_mux_id);
-	
+
 	len = sizeof(struct ipa_ioc_add_flt_rule) + prop->num_ext_props * sizeof(struct ipa_flt_rule_add);
 	pFilteringTable = (struct ipa_ioc_add_flt_rule*)malloc(len);
 	if (pFilteringTable == NULL)
@@ -1769,11 +1829,11 @@
 		return IPACM_FAILURE;
 	}
 
-	flt_rule_count_v6 = IPV6_DEFAULT_FILTERTING_RULES;
-	
+	flt_rule_count_v6 = IPV6_DEFAULT_FILTERTING_RULES + MAX_OFFLOAD_PAIR;
+
 	if(is_sta_mode == false)
 	{
-		if (m_filtering.DeleteFilteringHdls(wan_ul_fl_rule_hdl_v6, 
+		if (m_filtering.DeleteFilteringHdls(wan_ul_fl_rule_hdl_v6,
 			IPA_IP_v6, num_wan_ul_fl_rule_v6) == false)
 		{
 			IPACMERR("Error Deleting RuleTable(1) to Filtering, aborting...\n");
@@ -1816,3 +1876,1074 @@
 	close(fd);
 	return IPACM_SUCCESS;
 }
+
+
+/*handle lan2lan client active*/
+int IPACM_Lan::handle_lan2lan_client_active(ipacm_event_data_all *data, ipa_cm_event_id event)
+{
+	if(data == NULL)
+	{
+		IPACMERR("Event data is empty.\n");
+		return IPACM_FAILURE;
+	}
+
+	if(data->iptype == IPA_IP_v4 && ip_type != IPA_IP_v4 && ip_type != IPA_IP_MAX)
+	{
+		IPACMERR("Client has IPv4 addr but iface does not have IPv4 up.\n");
+		return IPACM_FAILURE;
+	}
+	if(data->iptype == IPA_IP_v6 && ip_type != IPA_IP_v6 && ip_type != IPA_IP_MAX)
+	{
+		IPACMERR("Client has IPv6 addr but iface does not have IPv6 up.\n");
+		return IPACM_FAILURE;
+	}
+
+	ipacm_cmd_q_data evt_data;
+	memset(&evt_data, 0, sizeof(evt_data));
+
+	ipacm_event_lan_client* lan_client;
+	lan_client = (ipacm_event_lan_client*)malloc(sizeof(ipacm_event_lan_client));
+	if(lan_client == NULL)
+	{
+		IPACMERR("Unable to allocate memory.\n");
+		return IPACM_FAILURE;
+	}
+	memset(lan_client, 0, sizeof(ipacm_event_lan_client));
+	lan_client->iptype = data->iptype;
+	lan_client->ipv4_addr = data->ipv4_addr;
+	memcpy(lan_client->ipv6_addr, data->ipv6_addr, 4 * sizeof(uint32_t));
+	memcpy(lan_client->mac_addr, data->mac_addr, 6 * sizeof(uint8_t));
+	lan_client->p_iface = this;
+
+	evt_data.event = event;
+	evt_data.evt_data = (void*)lan_client;
+
+	IPACMDBG("Posting event: %d\n", event);
+	IPACM_EvtDispatcher::PostEvt(&evt_data);
+	return IPACM_SUCCESS;
+}
+
+int IPACM_Lan::add_lan2lan_flt_rule(ipa_ip_type iptype, uint32_t src_v4_addr, uint32_t dst_v4_addr, uint32_t* src_v6_addr, uint32_t* dst_v6_addr, uint32_t* rule_hdl)
+{
+	if(rx_prop == NULL)
+	{
+		IPACMERR("There is no rx_prop for iface %s, not able to add lan2lan filtering rule.\n", dev_name);
+		return IPACM_FAILURE;
+	}
+	if(rule_hdl == NULL)
+	{
+		IPACMERR("Filteing rule handle is empty.\n");
+		return IPACM_FAILURE;
+	}
+
+	IPACMDBG("Got a new lan2lan flt rule with IP type: %d\n", iptype);
+
+	int i, len, res = IPACM_SUCCESS;
+	struct ipa_flt_rule_mdfy flt_rule;
+	struct ipa_ioc_mdfy_flt_rule* pFilteringTable;
+
+	len = sizeof(struct ipa_ioc_mdfy_flt_rule) + sizeof(struct ipa_flt_rule_mdfy);
+
+	pFilteringTable = (struct ipa_ioc_mdfy_flt_rule*)malloc(len);
+
+	if (pFilteringTable == NULL)
+	{
+		IPACMERR("Error allocate lan2lan flt rule memory...\n");
+		return IPACM_FAILURE;
+	}
+	memset(pFilteringTable, 0, len);
+
+	pFilteringTable->commit = 1;
+	pFilteringTable->ip = iptype;
+	pFilteringTable->num_rules = 1;
+
+	memset(&flt_rule, 0, sizeof(struct ipa_flt_rule_mdfy));
+
+	if(iptype == IPA_IP_v4)
+	{
+		IPACMDBG("src_v4_addr: %d dst_v4_addr: %d\n", src_v4_addr, dst_v4_addr);
+
+		if(num_lan2lan_flt_rule_v4 >= MAX_OFFLOAD_PAIR)
+		{
+			IPACMERR("Lan2lan flt rule table is full, not able to add.\n");
+			res = IPACM_FAILURE;
+			goto fail;
+		}
+
+		if(false == m_routing.GetRoutingTable(&IPACM_Iface::ipacmcfg->rt_tbl_lan2lan_v4))
+		{
+			IPACMERR("Failed to get routing table %s handle.\n", IPACM_Iface::ipacmcfg->rt_tbl_lan2lan_v4.name);
+			res = IPACM_FAILURE;
+			goto fail;
+		}
+		IPACMDBG("Routing handle for table %s: %d\n", IPACM_Iface::ipacmcfg->rt_tbl_lan2lan_v4.name,
+				IPACM_Iface::ipacmcfg->rt_tbl_lan2lan_v4.hdl);
+
+		flt_rule.status = -1;
+		for(i=0; i<MAX_OFFLOAD_PAIR; i++)
+		{
+			if(lan2lan_flt_rule_hdl_v4[i].valid == false)
+			{
+				flt_rule.rule_hdl = lan2lan_flt_rule_hdl_v4[i].rule_hdl;
+				break;
+			}
+		}
+		if(i == MAX_OFFLOAD_PAIR)
+		{
+			IPACMERR("Failed to find a filtering rule.\n");
+			res = IPACM_FAILURE;
+			goto fail;
+		}
+
+		flt_rule.rule.retain_hdr = 0;
+		flt_rule.rule.to_uc = 0;
+		flt_rule.rule.eq_attrib_type = 0;
+		flt_rule.rule.action = IPA_PASS_TO_ROUTING;
+		flt_rule.rule.rt_tbl_hdl = IPACM_Iface::ipacmcfg->rt_tbl_lan2lan_v4.hdl;
+
+		memcpy(&flt_rule.rule.attrib, &rx_prop->rx[0].attrib,
+					 sizeof(flt_rule.rule.attrib));
+		IPACMDBG("Rx property attrib mask:0x%x\n", rx_prop->rx[0].attrib.attrib_mask);
+
+		flt_rule.rule.attrib.attrib_mask |= IPA_FLT_SRC_ADDR;
+		flt_rule.rule.attrib.u.v4.src_addr = src_v4_addr;
+		flt_rule.rule.attrib.u.v4.src_addr_mask = 0xFFFFFFFF;
+
+		flt_rule.rule.attrib.attrib_mask |= IPA_FLT_DST_ADDR;
+		flt_rule.rule.attrib.u.v4.dst_addr = dst_v4_addr;
+		flt_rule.rule.attrib.u.v4.dst_addr_mask = 0xFFFFFFFF;
+
+		memcpy(&(pFilteringTable->rules[0]), &flt_rule, sizeof(struct ipa_flt_rule_mdfy));
+		if (false == m_filtering.ModifyFilteringRule(pFilteringTable))
+		{
+			IPACMERR("Error modifying filtering rule.\n");
+			res = IPACM_FAILURE;
+			goto fail;
+		}
+		else
+		{
+			lan2lan_flt_rule_hdl_v4[i].valid = true;
+			*rule_hdl = lan2lan_flt_rule_hdl_v4[i].rule_hdl;
+			num_lan2lan_flt_rule_v4++;
+			IPACMDBG("Flt rule modified, hdl: 0x%x, status: %d\n", pFilteringTable->rules[0].rule_hdl,
+						pFilteringTable->rules[0].status);
+		}
+	}
+	else if(iptype == IPA_IP_v6)
+	{
+		if(num_lan2lan_flt_rule_v6 >= MAX_OFFLOAD_PAIR)
+		{
+			IPACMERR("Lan2lan flt rule table is full, not able to add.\n");
+			res = IPACM_FAILURE;
+			goto fail;
+		}
+		if(src_v6_addr == NULL || dst_v6_addr == NULL)
+		{
+			IPACMERR("Got IPv6 flt rule but without IPv6 src/dst addr.\n");
+			res = IPACM_FAILURE;
+			goto fail;
+		}
+		IPACMDBG("src_v6_addr: 0x%08x%08x%08x%08x, dst_v6_addr: 0x%08x%08x%08x%08x\n", src_v6_addr[0], src_v6_addr[1],
+				src_v6_addr[2], src_v6_addr[3], dst_v6_addr[0], dst_v6_addr[1], dst_v6_addr[2], dst_v6_addr[3]);
+
+		if(false == m_routing.GetRoutingTable(&IPACM_Iface::ipacmcfg->rt_tbl_lan2lan_v6))
+		{
+			IPACMERR("Failed to get routing table %s handle.\n", IPACM_Iface::ipacmcfg->rt_tbl_lan2lan_v6.name);
+			res = IPACM_FAILURE;
+			goto fail;
+		}
+		IPACMDBG("Routing handle for table %s: %d\n", IPACM_Iface::ipacmcfg->rt_tbl_lan2lan_v6.name,
+				IPACM_Iface::ipacmcfg->rt_tbl_lan2lan_v6.hdl);
+
+		flt_rule.status = -1;
+		for(i=0; i<MAX_OFFLOAD_PAIR; i++)
+		{
+			if(lan2lan_flt_rule_hdl_v6[i].valid == false)
+			{
+				flt_rule.rule_hdl = lan2lan_flt_rule_hdl_v6[i].rule_hdl;
+				break;
+			}
+		}
+		if(i == MAX_OFFLOAD_PAIR)
+		{
+			IPACMERR("Failed to find a filtering rule handle.\n");
+			res = IPACM_FAILURE;
+			goto fail;
+		}
+
+		flt_rule.rule.retain_hdr = 0;
+		flt_rule.rule.to_uc = 0;
+		flt_rule.rule.eq_attrib_type = 0;
+		flt_rule.rule.action = IPA_PASS_TO_ROUTING;
+		flt_rule.rule.rt_tbl_hdl = IPACM_Iface::ipacmcfg->rt_tbl_lan2lan_v6.hdl;
+
+		memcpy(&flt_rule.rule.attrib, &rx_prop->rx[0].attrib,
+					 sizeof(flt_rule.rule.attrib));
+		IPACMDBG("Rx property attrib mask:0x%x\n", rx_prop->rx[0].attrib.attrib_mask);
+
+		flt_rule.rule.attrib.attrib_mask |= IPA_FLT_SRC_ADDR;
+		flt_rule.rule.attrib.u.v6.src_addr[0] = src_v6_addr[0];
+		flt_rule.rule.attrib.u.v6.src_addr[1] = src_v6_addr[1];
+		flt_rule.rule.attrib.u.v6.src_addr[2] = src_v6_addr[2];
+		flt_rule.rule.attrib.u.v6.src_addr[3] = src_v6_addr[3];
+		flt_rule.rule.attrib.u.v6.src_addr_mask[0] = 0xFFFFFFFF;
+		flt_rule.rule.attrib.u.v6.src_addr_mask[1] = 0xFFFFFFFF;
+		flt_rule.rule.attrib.u.v6.src_addr_mask[2] = 0xFFFFFFFF;
+		flt_rule.rule.attrib.u.v6.src_addr_mask[3] = 0xFFFFFFFF;
+
+
+		flt_rule.rule.attrib.attrib_mask |= IPA_FLT_DST_ADDR;
+		flt_rule.rule.attrib.u.v6.dst_addr[0] = dst_v6_addr[0];
+		flt_rule.rule.attrib.u.v6.dst_addr[1] = dst_v6_addr[1];
+		flt_rule.rule.attrib.u.v6.dst_addr[2] = dst_v6_addr[2];
+		flt_rule.rule.attrib.u.v6.dst_addr[3] = dst_v6_addr[3];
+		flt_rule.rule.attrib.u.v6.dst_addr_mask[0] = 0xFFFFFFFF;
+		flt_rule.rule.attrib.u.v6.dst_addr_mask[1] = 0xFFFFFFFF;
+		flt_rule.rule.attrib.u.v6.dst_addr_mask[2] = 0xFFFFFFFF;
+		flt_rule.rule.attrib.u.v6.dst_addr_mask[3] = 0xFFFFFFFF;
+
+		memcpy(&(pFilteringTable->rules[0]), &flt_rule, sizeof(struct ipa_flt_rule_mdfy));
+		if (false == m_filtering.ModifyFilteringRule(pFilteringTable))
+		{
+			IPACMERR("Error modifying filtering rule.\n");
+			res = IPACM_FAILURE;
+			goto fail;
+		}
+		else
+		{
+			lan2lan_flt_rule_hdl_v6[i].valid = true;
+			*rule_hdl = lan2lan_flt_rule_hdl_v6[i].rule_hdl;
+			num_lan2lan_flt_rule_v6++;
+			IPACMDBG("Flt rule modified, hdl: 0x%x, status: %d\n", pFilteringTable->rules[0].rule_hdl,
+						pFilteringTable->rules[0].status);
+		}
+	}
+	else
+	{
+		IPACMERR("IP type is not expected.\n");
+		res = IPACM_FAILURE;
+		goto fail;
+	}
+
+fail:
+	free(pFilteringTable);
+	return res;
+}
+
+int IPACM_Lan::add_dummy_lan2lan_flt_rule(ipa_ip_type iptype)
+{
+	if(rx_prop == NULL)
+	{
+		IPACMDBG("There is no rx_prop for iface %s, not able to add dummy lan2lan filtering rule.\n", dev_name);
+		return 0;
+	}
+
+	int i, len, res = IPACM_SUCCESS;
+	struct ipa_flt_rule_add flt_rule;
+	ipa_ioc_add_flt_rule* pFilteringTable;
+
+	len = sizeof(struct ipa_ioc_add_flt_rule) +	MAX_OFFLOAD_PAIR * sizeof(struct ipa_flt_rule_add);
+
+	pFilteringTable = (struct ipa_ioc_add_flt_rule *)malloc(len);
+	if (pFilteringTable == NULL)
+	{
+		IPACMERR("Error allocate flt table memory...\n");
+		return IPACM_FAILURE;
+	}
+	memset(pFilteringTable, 0, len);
+
+	pFilteringTable->commit = 1;
+	pFilteringTable->ep = rx_prop->rx[0].src_pipe;
+	pFilteringTable->global = false;
+	pFilteringTable->ip = iptype;
+	pFilteringTable->num_rules = MAX_OFFLOAD_PAIR;
+
+	memset(&flt_rule, 0, sizeof(struct ipa_flt_rule_add));
+
+	flt_rule.rule.retain_hdr = 0;
+	flt_rule.at_rear = true;
+	flt_rule.flt_rule_hdl = -1;
+	flt_rule.status = -1;
+	flt_rule.rule.action = IPA_PASS_TO_EXCEPTION;
+
+	memcpy(&flt_rule.rule.attrib, &rx_prop->rx[0].attrib,
+			sizeof(flt_rule.rule.attrib));
+
+	if(iptype == IPA_IP_v4)
+	{
+		flt_rule.rule.attrib.attrib_mask = IPA_FLT_SRC_ADDR | IPA_FLT_DST_ADDR;
+		flt_rule.rule.attrib.u.v4.src_addr_mask = ~0;
+		flt_rule.rule.attrib.u.v4.src_addr = ~0;
+		flt_rule.rule.attrib.u.v4.dst_addr_mask = ~0;
+		flt_rule.rule.attrib.u.v4.dst_addr = ~0;
+
+		for(i=0; i<MAX_OFFLOAD_PAIR; i++)
+		{
+			memcpy(&(pFilteringTable->rules[i]), &flt_rule, sizeof(struct ipa_flt_rule_add));
+		}
+
+		if (false == m_filtering.AddFilteringRule(pFilteringTable))
+		{
+			IPACMERR("Error adding dummy lan2lan v4 flt rule\n");
+			res = IPACM_FAILURE;
+			goto fail;
+		}
+		else
+		{
+			flt_rule_count_v4 += MAX_OFFLOAD_PAIR;
+			/* copy filter rule hdls */
+			for (int i = 0; i < MAX_OFFLOAD_PAIR; i++)
+			{
+				if (pFilteringTable->rules[i].status == 0)
+				{
+					lan2lan_flt_rule_hdl_v4[i].rule_hdl = pFilteringTable->rules[i].flt_rule_hdl;
+					IPACMDBG("Lan2lan v4 flt rule %d hdl:0x%x\n", i, lan2lan_flt_rule_hdl_v4[i].rule_hdl);
+				}
+				else
+				{
+					IPACMERR("Failed adding lan2lan v4 flt rule %d\n", i);
+					res = IPACM_FAILURE;
+					goto fail;
+				}
+			}
+		}
+	}
+	else if(iptype == IPA_IP_v6)
+	{
+		flt_rule.rule.attrib.attrib_mask = IPA_FLT_SRC_ADDR | IPA_FLT_DST_ADDR;
+		flt_rule.rule.attrib.u.v6.src_addr_mask[0] = ~0;
+		flt_rule.rule.attrib.u.v6.src_addr_mask[1] = ~0;
+		flt_rule.rule.attrib.u.v6.src_addr_mask[2] = ~0;
+		flt_rule.rule.attrib.u.v6.src_addr_mask[3] = ~0;
+		flt_rule.rule.attrib.u.v6.src_addr[0] = ~0;
+		flt_rule.rule.attrib.u.v6.src_addr[1] = ~0;
+		flt_rule.rule.attrib.u.v6.src_addr[2] = ~0;
+		flt_rule.rule.attrib.u.v6.src_addr[3] = ~0;
+		flt_rule.rule.attrib.u.v6.dst_addr_mask[0] = ~0;
+		flt_rule.rule.attrib.u.v6.dst_addr_mask[1] = ~0;
+		flt_rule.rule.attrib.u.v6.dst_addr_mask[2] = ~0;
+		flt_rule.rule.attrib.u.v6.dst_addr_mask[3] = ~0;
+		flt_rule.rule.attrib.u.v6.dst_addr[0] = ~0;
+		flt_rule.rule.attrib.u.v6.dst_addr[1] = ~0;
+		flt_rule.rule.attrib.u.v6.dst_addr[2] = ~0;
+		flt_rule.rule.attrib.u.v6.dst_addr[3] = ~0;
+
+		for(i=0; i<MAX_OFFLOAD_PAIR; i++)
+		{
+			memcpy(&(pFilteringTable->rules[i]), &flt_rule, sizeof(struct ipa_flt_rule_add));
+		}
+
+		if (false == m_filtering.AddFilteringRule(pFilteringTable))
+		{
+			IPACMERR("Error adding dummy lan2lan v6 flt rule\n");
+			res = IPACM_FAILURE;
+			goto fail;
+		}
+		else
+		{
+			flt_rule_count_v6 += MAX_OFFLOAD_PAIR;
+			/* copy filter rule hdls */
+			for (int i = 0; i < MAX_OFFLOAD_PAIR; i++)
+			{
+				if (pFilteringTable->rules[i].status == 0)
+				{
+					lan2lan_flt_rule_hdl_v6[i].rule_hdl = pFilteringTable->rules[i].flt_rule_hdl;
+					IPACMDBG("Lan2lan v6 flt rule %d hdl:0x%x\n", i, lan2lan_flt_rule_hdl_v6[i].rule_hdl);
+				}
+				else
+				{
+					IPACMERR("Failed adding lan2lan v6 flt rule %d\n", i);
+					res = IPACM_FAILURE;
+					goto fail;
+				}
+			}
+		}
+	}
+	else
+	{
+		IPACMERR("IP type is not expected.\n");
+		goto fail;
+	}
+
+fail:
+	free(pFilteringTable);
+	return res;
+}
+
+int IPACM_Lan::del_lan2lan_flt_rule(ipa_ip_type iptype, uint32_t rule_hdl)
+{
+	int i;
+
+	IPACMDBG("Del lan2lan flt rule with IP type: %d hdl: %d\n", iptype, rule_hdl);
+	if(iptype == IPA_IP_v4)
+	{
+		for(i=0; i<MAX_OFFLOAD_PAIR; i++)
+		{
+			if(lan2lan_flt_rule_hdl_v4[i].rule_hdl == rule_hdl)
+			{
+				assert(lan2lan_flt_rule_hdl_v4[i].valid == true);
+				if(reset_lan2lan_dummy_flt_rule(IPA_IP_v4, rule_hdl) == IPACM_FAILURE)
+				{
+					IPACMERR("Failed to delete lan2lan v4 flt rule %d\n", rule_hdl);
+					return IPACM_FAILURE;
+				}
+				IPACMDBG("Deleted lan2lan v4 flt rule %d\n", rule_hdl);
+				lan2lan_flt_rule_hdl_v4[i].valid = false;
+				num_lan2lan_flt_rule_v4--;
+				break;
+			}
+		}
+
+		if(i == MAX_OFFLOAD_PAIR) //not finding the rule
+		{
+			IPACMERR("The rule is not found.\n");
+			return IPACM_FAILURE;
+		}
+	}
+	else if(iptype == IPA_IP_v6)
+	{
+		for(i=0; i<MAX_OFFLOAD_PAIR; i++)
+		{
+			if(lan2lan_flt_rule_hdl_v6[i].rule_hdl == rule_hdl)
+			{
+				assert(lan2lan_flt_rule_hdl_v6[i].valid == true);
+				if(reset_lan2lan_dummy_flt_rule(IPA_IP_v6, rule_hdl) == IPACM_FAILURE)
+				{
+					IPACMERR("Failed to delete lan2lan v6 flt rule %d\n", rule_hdl);
+					return IPACM_FAILURE;
+				}
+				IPACMDBG("Deleted lan2lan v6 flt rule %d\n", rule_hdl);
+				lan2lan_flt_rule_hdl_v6[i].valid = false;
+				num_lan2lan_flt_rule_v6--;
+				break;
+			}
+		}
+
+		if(i == MAX_OFFLOAD_PAIR) //not finding the rule
+		{
+			IPACMERR("The rule is not found.\n");
+			return IPACM_FAILURE;
+		}
+	}
+	else
+	{
+		IPACMERR("IP type is not expected.\n");
+		return IPACM_FAILURE;
+	}
+
+	return IPACM_SUCCESS;
+}
+
+int IPACM_Lan::reset_lan2lan_dummy_flt_rule(ipa_ip_type iptype, uint32_t rule_hdl)
+{
+	int len, res = IPACM_SUCCESS;
+	struct ipa_flt_rule_mdfy flt_rule;
+	struct ipa_ioc_mdfy_flt_rule* pFilteringTable;
+
+	IPACMDBG("Reset flt rule to dummy, IP type: %d, hdl: %d\n", iptype, rule_hdl);
+	len = sizeof(struct ipa_ioc_mdfy_flt_rule) + sizeof(struct ipa_flt_rule_mdfy);
+	pFilteringTable = (struct ipa_ioc_mdfy_flt_rule*)malloc(len);
+
+	if (pFilteringTable == NULL)
+	{
+		IPACMERR("Error allocate lan2lan flt rule memory...\n");
+		return IPACM_FAILURE;
+	}
+	memset(pFilteringTable, 0, len);
+
+	pFilteringTable->commit = 1;
+	pFilteringTable->ip = iptype;
+	pFilteringTable->num_rules = 1;
+
+	memset(&flt_rule, 0, sizeof(struct ipa_flt_rule_mdfy));
+	flt_rule.status = -1;
+	flt_rule.rule_hdl = rule_hdl;
+
+	flt_rule.rule.retain_hdr = 0;
+	flt_rule.rule.action = IPA_PASS_TO_EXCEPTION;
+
+	if(iptype == IPA_IP_v4)
+	{
+		IPACMDBG("Reset IPv4 lan2lan flt rule to dummy\n");
+
+		flt_rule.rule.attrib.attrib_mask = IPA_FLT_SRC_ADDR | IPA_FLT_DST_ADDR;
+		flt_rule.rule.attrib.u.v4.dst_addr = ~0;
+		flt_rule.rule.attrib.u.v4.dst_addr_mask = ~0;
+		flt_rule.rule.attrib.u.v4.src_addr = ~0;
+		flt_rule.rule.attrib.u.v4.src_addr_mask = ~0;
+
+		memcpy(&(pFilteringTable->rules[0]), &flt_rule, sizeof(struct ipa_flt_rule_mdfy));
+		if (false == m_filtering.ModifyFilteringRule(pFilteringTable))
+		{
+			IPACMERR("Error modifying filtering rule.\n");
+			res = IPACM_FAILURE;
+			goto fail;
+		}
+		else
+		{
+			IPACMDBG("Flt rule reset to dummy, hdl: 0x%x, status: %d\n", pFilteringTable->rules[0].rule_hdl,
+						pFilteringTable->rules[0].status);
+		}
+	}
+	else if(iptype == IPA_IP_v6)
+	{
+		IPACMDBG("Reset IPv6 lan2lan flt rule to dummy\n");
+
+		flt_rule.rule.attrib.attrib_mask = IPA_FLT_SRC_ADDR | IPA_FLT_DST_ADDR;
+		flt_rule.rule.attrib.u.v6.src_addr[0] = ~0;
+		flt_rule.rule.attrib.u.v6.src_addr[1] = ~0;
+		flt_rule.rule.attrib.u.v6.src_addr[2] = ~0;
+		flt_rule.rule.attrib.u.v6.src_addr[3] = ~0;
+		flt_rule.rule.attrib.u.v6.src_addr_mask[0] = ~0;
+		flt_rule.rule.attrib.u.v6.src_addr_mask[1] = ~0;
+		flt_rule.rule.attrib.u.v6.src_addr_mask[2] = ~0;
+		flt_rule.rule.attrib.u.v6.src_addr_mask[3] = ~0;
+		flt_rule.rule.attrib.u.v6.dst_addr[0] = ~0;
+		flt_rule.rule.attrib.u.v6.dst_addr[1] = ~0;
+		flt_rule.rule.attrib.u.v6.dst_addr[2] = ~0;
+		flt_rule.rule.attrib.u.v6.dst_addr[3] = ~0;
+		flt_rule.rule.attrib.u.v6.dst_addr_mask[0] = ~0;
+		flt_rule.rule.attrib.u.v6.dst_addr_mask[1] = ~0;
+		flt_rule.rule.attrib.u.v6.dst_addr_mask[2] = ~0;
+		flt_rule.rule.attrib.u.v6.dst_addr_mask[3] = ~0;
+
+
+		memcpy(&(pFilteringTable->rules[0]), &flt_rule, sizeof(struct ipa_flt_rule_mdfy));
+		if (false == m_filtering.ModifyFilteringRule(pFilteringTable))
+		{
+			IPACMERR("Error modifying filtering rule.\n");
+			res = IPACM_FAILURE;
+			goto fail;
+		}
+		else
+		{
+			IPACMDBG("Flt rule reset to dummy, hdl: 0x%x, status: %d\n", pFilteringTable->rules[0].rule_hdl,
+						pFilteringTable->rules[0].status);
+		}
+	}
+	else
+	{
+		IPACMERR("IP type is not expected.\n");
+		res = IPACM_FAILURE;
+		goto fail;
+	}
+
+fail:
+	free(pFilteringTable);
+	return res;
+}
+
+int IPACM_Lan::add_lan2lan_hdr(ipa_ip_type iptype, uint8_t* src_mac, uint8_t* dst_mac, uint32_t* hdr_hdl)
+{
+	if(tx_prop == NULL)
+	{
+		IPACMERR("There is no tx_prop, cannot add header.\n");
+		return IPACM_FAILURE;
+	}
+	if(src_mac == NULL || dst_mac == NULL)
+	{
+		IPACMERR("Either src_mac or dst_mac is null, cannot add header.\n");
+		return IPACM_FAILURE;
+	}
+	if(hdr_hdl == NULL)
+	{
+		IPACMERR("Header handle is empty.\n");
+		return IPACM_FAILURE;
+	}
+
+	int i, j, len;
+	int res = IPACM_SUCCESS;
+	char index[4];
+	struct ipa_ioc_copy_hdr sCopyHeader;
+	struct ipa_ioc_add_hdr *pHeader;
+
+	IPACMDBG("Get lan2lan header request, src_mac: 0x%02x%02x%02x%02x%02x%02x dst_mac: 0x%02x%02x%02x%02x%02x%02x\n",
+			src_mac[0], src_mac[1], src_mac[2], src_mac[3], src_mac[4], src_mac[5], dst_mac[0], dst_mac[1],
+			dst_mac[2], dst_mac[3], dst_mac[4], dst_mac[5]);
+
+	len = sizeof(struct ipa_ioc_add_hdr) + sizeof(struct ipa_hdr_add);
+	pHeader = (struct ipa_ioc_add_hdr *)malloc(len);
+	if (pHeader == NULL)
+	{
+		IPACMERR("Failed to allocate header\n");
+		return IPACM_FAILURE;
+	}
+	memset(pHeader, 0, len);
+
+	if(iptype == IPA_IP_v4)
+	{		/* copy partial header for v4*/
+		for(i=0; i<tx_prop->num_tx_props; i++)
+		{
+			if(tx_prop->tx[i].ip == IPA_IP_v4)
+			{
+				IPACMDBG("Got v4-header name from %d tx props\n", i);
+				memset(&sCopyHeader, 0, sizeof(sCopyHeader));
+				memcpy(sCopyHeader.name, tx_prop->tx[i].hdr_name, sizeof(sCopyHeader.name));
+
+				IPACMDBG("Header name: %s\n", sCopyHeader.name);
+				if(m_header.CopyHeader(&sCopyHeader) == false)
+				{
+					IPACMERR("Copy header failed\n");
+					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(pHeader->hdr[0].hdr, sCopyHeader.hdr, sCopyHeader.hdr_len);
+				}
+
+				if(sCopyHeader.is_eth2_ofst_valid)
+				{
+					memcpy(&pHeader->hdr[0].hdr[sCopyHeader.eth2_ofst], dst_mac, IPA_MAC_ADDR_SIZE);
+					memcpy(&pHeader->hdr[0].hdr[sCopyHeader.eth2_ofst+IPA_MAC_ADDR_SIZE], src_mac, IPA_MAC_ADDR_SIZE);
+				}
+				else
+				{
+					IPACMERR("Ethernet 2 header offset is invalid.\n");
+				}
+
+				pHeader->commit = true;
+				pHeader->num_hdrs = 1;
+
+				memset(pHeader->hdr[0].name, 0, sizeof(pHeader->hdr[0].name));
+				strncpy(pHeader->hdr[0].name, IPA_LAN_TO_LAN_USB_HDR_NAME_V4, sizeof(IPA_LAN_TO_LAN_USB_HDR_NAME_V4));
+
+				for(j=0; j<MAX_OFFLOAD_PAIR; j++)
+				{
+					if( lan2lan_hdr_hdl_v4[j].valid == false)
+					{
+						IPACMDBG("Construct lan2lan hdr with index %d.\n", j);
+						break;
+					}
+				}
+				if(j == MAX_OFFLOAD_PAIR)
+				{
+					IPACMERR("Failed to find an available hdr index.\n");
+					res = IPACM_FAILURE;
+					goto fail;
+				}
+				lan2lan_hdr_hdl_v4[j].valid = true;
+				sprintf(index, "%d", j);
+				strncat(pHeader->hdr[0].name, index, sizeof(index));
+
+				pHeader->hdr[0].hdr_len = sCopyHeader.hdr_len;
+				pHeader->hdr[0].is_partial = 0;
+				pHeader->hdr[0].hdr_hdl = -1;
+				pHeader->hdr[0].status = -1;
+
+				if (m_header.AddHeader(pHeader) == false || pHeader->hdr[0].status != 0)
+				{
+					IPACMERR("Ioctl IPA_IOC_ADD_HDR failed with status: %d\n", pHeader->hdr[0].status);
+					res = IPACM_FAILURE;
+					goto fail;
+				}
+				IPACMDBG("Installed v4 full header %s header handle 0x%08x\n", pHeader->hdr[0].name,
+							pHeader->hdr[0].hdr_hdl);
+				*hdr_hdl = pHeader->hdr[0].hdr_hdl;
+				lan2lan_hdr_hdl_v4[j].hdr_hdl = pHeader->hdr[0].hdr_hdl;
+				break;
+			}
+		}
+	}
+	else if(iptype == IPA_IP_v6)
+	{		/* copy partial header for v6*/
+		for(i=0; i<tx_prop->num_tx_props; i++)
+		{
+			if(tx_prop->tx[i].ip == IPA_IP_v6)
+			{
+				IPACMDBG("Got v6-header name from %d tx props\n", i);
+				memset(&sCopyHeader, 0, sizeof(sCopyHeader));
+				memcpy(sCopyHeader.name, tx_prop->tx[i].hdr_name, sizeof(sCopyHeader.name));
+
+				IPACMDBG("Header name: %s\n", sCopyHeader.name);
+				if(m_header.CopyHeader(&sCopyHeader) == false)
+				{
+					IPACMERR("Copy header failed\n");
+					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(pHeader->hdr[0].hdr, sCopyHeader.hdr, sCopyHeader.hdr_len);
+				}
+				if(sCopyHeader.is_eth2_ofst_valid)
+				{
+					memcpy(&pHeader->hdr[0].hdr[sCopyHeader.eth2_ofst], dst_mac, IPA_MAC_ADDR_SIZE);
+					memcpy(&pHeader->hdr[0].hdr[sCopyHeader.eth2_ofst+IPA_MAC_ADDR_SIZE], src_mac, IPA_MAC_ADDR_SIZE);
+				}
+				else
+				{
+					IPACMERR("Ethernet 2 header offset is invalid.\n");
+				}
+
+				pHeader->commit = true;
+				pHeader->num_hdrs = 1;
+
+				memset(pHeader->hdr[0].name, 0, sizeof(pHeader->hdr[0].name));
+				strncpy(pHeader->hdr[0].name, IPA_LAN_TO_LAN_USB_HDR_NAME_V6, sizeof(IPA_LAN_TO_LAN_USB_HDR_NAME_V6));
+
+				for(j=0; j<MAX_OFFLOAD_PAIR; j++)
+				{
+					if( lan2lan_hdr_hdl_v6[j].valid == false)
+					{
+						IPACMDBG("Construct lan2lan hdr with index %d.\n", j);
+						break;
+					}
+				}
+				if(j == MAX_OFFLOAD_PAIR)
+				{
+					IPACMERR("Failed to find an available hdr index.\n");
+					res = IPACM_FAILURE;
+					goto fail;
+				}
+				lan2lan_hdr_hdl_v6[j].valid = true;
+				sprintf(index, "%d", j);
+				strncat(pHeader->hdr[0].name, index, sizeof(index));
+
+				pHeader->hdr[0].hdr_len = sCopyHeader.hdr_len;
+				pHeader->hdr[0].is_partial = 0;
+				pHeader->hdr[0].hdr_hdl = -1;
+				pHeader->hdr[0].status = -1;
+
+				if (m_header.AddHeader(pHeader) == false || pHeader->hdr[0].status != 0)
+				{
+					IPACMERR("Ioctl IPA_IOC_ADD_HDR failed with status: %d\n", pHeader->hdr[0].status);
+					res = IPACM_FAILURE;
+					goto fail;
+				}
+				IPACMDBG("Installed v6 full header %s header handle 0x%08x\n", pHeader->hdr[0].name,
+							pHeader->hdr[0].hdr_hdl);
+				*hdr_hdl = pHeader->hdr[0].hdr_hdl;
+				lan2lan_hdr_hdl_v6[j].hdr_hdl = pHeader->hdr[0].hdr_hdl;
+				break;
+			}
+		}
+	}
+	else
+	{
+		IPACMERR("IP type is not expected.\n");
+	}
+
+fail:
+	free(pHeader);
+	return res;
+}
+
+int IPACM_Lan::del_lan2lan_hdr(ipa_ip_type iptype, uint32_t hdr_hdl)
+{
+	int i;
+	if (m_header.DeleteHeaderHdl(hdr_hdl) == false)
+	{
+		IPACMERR("Failed to delete header %d\n", hdr_hdl);
+		return IPACM_FAILURE;
+	}
+	IPACMDBG("Deleted header %d\n", hdr_hdl);
+
+	if(iptype == IPA_IP_v4)
+	{
+		for(i=0; i<MAX_OFFLOAD_PAIR; i++)
+		{
+			if(lan2lan_hdr_hdl_v4[i].hdr_hdl == hdr_hdl)
+			{
+				lan2lan_hdr_hdl_v4[i].valid = false;
+				break;
+			}
+		}
+		if(i == MAX_OFFLOAD_PAIR)
+		{
+			IPACMERR("Failed to find corresponding hdr hdl.\n");
+			return IPACM_FAILURE;
+		}
+	}
+	else if(iptype == IPA_IP_v6)
+	{
+		for(i=0; i<MAX_OFFLOAD_PAIR; i++)
+		{
+			if(lan2lan_hdr_hdl_v6[i].hdr_hdl == hdr_hdl)
+			{
+				lan2lan_hdr_hdl_v6[i].valid = false;
+				break;
+			}
+		}
+		if(i == MAX_OFFLOAD_PAIR)
+		{
+			IPACMERR("Failed to find corresponding hdr hdl.\n");
+			return IPACM_FAILURE;
+		}
+	}
+	else
+	{
+		IPACMERR("IP type is not expected.\n");
+		return IPACM_FAILURE;
+	}
+	return IPACM_SUCCESS;
+}
+
+int IPACM_Lan::add_lan2lan_rt_rule(ipa_ip_type iptype, uint32_t src_v4_addr, uint32_t dst_v4_addr,
+			uint32_t* src_v6_addr, uint32_t* dst_v6_addr, uint32_t hdr_hdl, lan_to_lan_rt_rule_hdl* rule_hdl)
+{
+	struct ipa_ioc_add_rt_rule *rt_rule;
+	struct ipa_rt_rule_add *rt_rule_entry;
+	uint32_t tx_index;
+	int len;
+	int res = IPACM_SUCCESS;
+
+	IPACMDBG("Got a new lan2lan rt rule with IP type: %d\n", iptype);
+
+	if(rule_hdl == NULL)
+	{
+		IPACMERR("Rule hdl is empty.\n");
+		return IPACM_FAILURE;
+	}
+	memset(rule_hdl, 0, sizeof(lan_to_lan_rt_rule_hdl));
+
+	if(tx_prop == NULL)
+	{
+		IPACMDBG("There is no tx_prop for iface %s, not able to add lan2lan routing rule.\n", dev_name);
+		return IPACM_FAILURE;
+	}
+
+	len = sizeof(struct ipa_ioc_add_rt_rule) + sizeof(struct ipa_rt_rule_add);
+	rt_rule = (struct ipa_ioc_add_rt_rule *)malloc(len);
+	if (!rt_rule)
+	{
+		IPACMERR("Failed to allocate memory for rt rule\n");
+		return IPACM_FAILURE;
+	}
+	memset(rt_rule, 0, len);
+
+	rt_rule->commit = 1;
+	rt_rule->num_rules = 1;
+	rt_rule->ip = iptype;
+
+	if(iptype == IPA_IP_v4)
+	{
+		IPACMDBG("src_v4_addr: 0x%08x dst_v4_addr: 0x%08x\n", src_v4_addr, dst_v4_addr);
+
+		strcpy(rt_rule->rt_tbl_name, IPACM_Iface::ipacmcfg->rt_tbl_lan2lan_v4.name);
+		rt_rule_entry = &rt_rule->rules[0];
+		rt_rule_entry->at_rear = false;
+		rt_rule_entry->rt_rule_hdl = 0;
+		rt_rule_entry->status = -1;
+
+		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, iptype: %d conflict ip-type: %d bypass\n",
+		    				tx_index, tx_prop->tx[tx_index].ip, IPA_IP_v4);
+		    	continue;
+		    }
+
+			rt_rule_entry->rule.hdr_hdl = hdr_hdl;
+			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_SRC_ADDR;
+			rt_rule_entry->rule.attrib.u.v4.src_addr      = src_v4_addr;
+			rt_rule_entry->rule.attrib.u.v4.src_addr_mask = 0xFFFFFFFF;
+
+			rt_rule_entry->rule.attrib.attrib_mask |= IPA_FLT_DST_ADDR;
+			rt_rule_entry->rule.attrib.u.v4.dst_addr      = dst_v4_addr;
+			rt_rule_entry->rule.attrib.u.v4.dst_addr_mask = 0xFFFFFFFF;
+
+			if(m_routing.AddRoutingRule(rt_rule) == false)
+			{
+				IPACMERR("Routing rule addition failed\n");
+				res = IPACM_FAILURE;
+				goto fail;
+			}
+			IPACMDBG("Added rt rule hdl: 0x%08x\n", rt_rule_entry->rt_rule_hdl);
+			rule_hdl->rule_hdl[rule_hdl->num_rule] = rt_rule_entry->rt_rule_hdl;
+			rule_hdl->num_rule++;
+		}
+	}
+	else if(iptype == IPA_IP_v6)
+	{
+		if(src_v6_addr == NULL || dst_v6_addr == NULL)
+		{
+			IPACMERR("Got IPv6 rt rule but without IPv6 src/dst addr.\n");
+			res = IPACM_FAILURE;
+			goto fail;
+		}
+		IPACMDBG("src_v6_addr: 0x%08x%08x%08x%08x, dst_v6_addr: 0x%08x%08x%08x%08x\n", src_v6_addr[0], src_v6_addr[1],
+				src_v6_addr[2], src_v6_addr[3], dst_v6_addr[0], dst_v6_addr[1], dst_v6_addr[2], dst_v6_addr[3]);
+
+		strcpy(rt_rule->rt_tbl_name, IPACM_Iface::ipacmcfg->rt_tbl_lan2lan_v6.name);
+		rt_rule_entry = &rt_rule->rules[0];
+		rt_rule_entry->at_rear = false;
+		rt_rule_entry->rt_rule_hdl = 0;
+		rt_rule_entry->status = -1;
+
+		for (tx_index = 0; tx_index<iface_query->num_tx_props; tx_index++)
+		{
+		    if(tx_prop->tx[tx_index].ip != IPA_IP_v6)
+		    {
+		    	IPACMDBG("Tx:%d, iptype: %d conflict ip-type: %d bypass\n",
+		    				tx_index, tx_prop->tx[tx_index].ip, IPA_IP_v6);
+		    	continue;
+		    }
+
+			rt_rule_entry->rule.hdr_hdl = hdr_hdl;
+			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_SRC_ADDR;
+			rt_rule_entry->rule.attrib.u.v6.src_addr[0] = src_v6_addr[0];
+			rt_rule_entry->rule.attrib.u.v6.src_addr[1] = src_v6_addr[1];
+			rt_rule_entry->rule.attrib.u.v6.src_addr[2] = src_v6_addr[2];
+			rt_rule_entry->rule.attrib.u.v6.src_addr[3] = src_v6_addr[3];
+			rt_rule_entry->rule.attrib.u.v6.src_addr_mask[0] = 0xFFFFFFFF;
+			rt_rule_entry->rule.attrib.u.v6.src_addr_mask[1] = 0xFFFFFFFF;
+			rt_rule_entry->rule.attrib.u.v6.src_addr_mask[2] = 0xFFFFFFFF;
+			rt_rule_entry->rule.attrib.u.v6.src_addr_mask[3] = 0xFFFFFFFF;
+
+			rt_rule_entry->rule.attrib.attrib_mask |= IPA_FLT_DST_ADDR;
+			rt_rule_entry->rule.attrib.u.v6.dst_addr[0] = dst_v6_addr[0];
+			rt_rule_entry->rule.attrib.u.v6.dst_addr[1] = dst_v6_addr[1];
+			rt_rule_entry->rule.attrib.u.v6.dst_addr[2] = dst_v6_addr[2];
+			rt_rule_entry->rule.attrib.u.v6.dst_addr[3] = dst_v6_addr[3];
+			rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[0] = 0xFFFFFFFF;
+			rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[1] = 0xFFFFFFFF;
+			rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[2] = 0xFFFFFFFF;
+			rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[3] = 0xFFFFFFFF;
+
+			if(m_routing.AddRoutingRule(rt_rule) == false)
+			{
+				IPACMERR("Routing rule addition failed\n");
+				res = IPACM_FAILURE;
+				goto fail;
+			}
+			IPACMDBG("Added rt rule hdl: 0x%08x\n", rt_rule_entry->rt_rule_hdl);
+			rule_hdl->rule_hdl[rule_hdl->num_rule] = rt_rule_entry->rt_rule_hdl;
+			rule_hdl->num_rule++;
+		}
+	}
+	else
+	{
+		IPACMERR("IP type is not expected.\n");
+	}
+
+fail:
+	free(rt_rule);
+	return res;
+}
+
+int IPACM_Lan::del_lan2lan_rt_rule(ipa_ip_type iptype, lan_to_lan_rt_rule_hdl rule_hdl)
+{
+	if(rule_hdl.num_rule <= 0 || rule_hdl.num_rule > MAX_NUM_PROP)
+	{
+		IPACMERR("The number of rule handles are not correct.\n");
+		return IPACM_FAILURE;
+	}
+
+	int i, res = IPACM_SUCCESS;
+
+	IPACMDBG("Get %d rule handles with IP type %d\n", rule_hdl.num_rule, iptype);
+	for(i=0; i<rule_hdl.num_rule; i++)
+	{
+		if(m_routing.DeleteRoutingHdl(rule_hdl.rule_hdl[i], iptype) == false)
+		{
+			IPACMERR("Failed to delete routing rule hdl %d.\n", rule_hdl.rule_hdl[i]);
+			res = IPACM_FAILURE;
+		}
+		IPACMDBG("Deleted routing rule handle %d\n",rule_hdl.rule_hdl[i]);
+	}
+	return res;
+}
+
+void IPACM_Lan::post_del_self_evt()
+{
+	ipacm_cmd_q_data evt;
+	ipacm_event_data_fid* fid;
+	fid = (ipacm_event_data_fid*)malloc(sizeof(ipacm_event_data_fid));
+	if(fid == NULL)
+	{
+		IPACMERR("Failed to allocate fid memory.\n");
+		return;
+	}
+	memset(fid, 0, sizeof(ipacm_event_data_fid));
+	memset(&evt, 0, sizeof(ipacm_cmd_q_data));
+
+	fid->if_index = ipa_if_num;
+
+	evt.evt_data = (void*)fid;
+	evt.event = IPA_LAN_DELETE_SELF;
+
+	IPACMDBG("Posting event IPA_LAN_DELETE_SELF\n");
+	IPACM_EvtDispatcher::PostEvt(&evt);
+}
+
+void IPACM_Lan::post_lan2lan_client_disconnect_msg()
+{
+	int i, j;
+	ipacm_cmd_q_data evt_data;
+	ipacm_event_lan_client* lan_client;
+
+	for (i = 0; i < num_eth_client; i++)
+	{
+			if(get_client_memptr(eth_client, i)->ipv4_set == true)
+			{
+				lan_client = (ipacm_event_lan_client*)malloc(sizeof(ipacm_event_lan_client));
+				if(lan_client == NULL)
+				{
+					IPACMERR("Failed to allocate memory.\n");
+					return;
+				}
+				memset(lan_client, 0, sizeof(ipacm_event_lan_client));
+				lan_client->iptype = IPA_IP_v4;
+				lan_client->ipv4_addr = get_client_memptr(eth_client, i)->v4_addr;
+				lan_client->p_iface = this;
+
+				memset(&evt_data, 0, sizeof(ipacm_cmd_q_data));
+				evt_data.evt_data = (void*)lan_client;
+				evt_data.event = IPA_LAN_CLIENT_DISCONNECT;
+
+				IPACMDBG("Posting event IPA_LAN_CLIENT_DISCONNECT\n");
+				IPACM_EvtDispatcher::PostEvt(&evt_data);
+			}
+
+			for (j = 0; j < get_client_memptr(eth_client, i)->ipv6_set; j++)
+			{
+				lan_client = (ipacm_event_lan_client*)malloc(sizeof(ipacm_event_lan_client));
+				if(lan_client == NULL)
+				{
+					IPACMERR("Failed to allocate memory.\n");
+					return;
+				}
+				memset(lan_client, 0, sizeof(ipacm_event_lan_client));
+				lan_client->iptype = IPA_IP_v6;
+				lan_client->ipv6_addr[0] = get_client_memptr(eth_client, i)->v6_addr[j][0];
+				lan_client->ipv6_addr[0] = get_client_memptr(eth_client, i)->v6_addr[j][0];
+				lan_client->ipv6_addr[0] = get_client_memptr(eth_client, i)->v6_addr[j][0];
+				lan_client->ipv6_addr[0] = get_client_memptr(eth_client, i)->v6_addr[j][0];
+				lan_client->p_iface = this;
+
+				memset(&evt_data, 0, sizeof(ipacm_cmd_q_data));
+				evt_data.evt_data = (void*)lan_client;
+				evt_data.event = IPA_LAN_CLIENT_DISCONNECT;
+
+				IPACMDBG("Posting event IPA_LAN_CLIENT_DISCONNECT\n");
+				IPACM_EvtDispatcher::PostEvt(&evt_data);
+			}
+	} /* end of for loop */
+	return;
+}
\ No newline at end of file
diff --git a/ipacm/src/IPACM_LanToLan.cpp b/ipacm/src/IPACM_LanToLan.cpp
new file mode 100644
index 0000000..ed9a671
--- /dev/null
+++ b/ipacm/src/IPACM_LanToLan.cpp
@@ -0,0 +1,1286 @@
+/*
+Copyright (c) 2014, 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.
+*/
+/*!
+	@file
+	IPACM_LanToLan.cpp
+
+	@brief
+	This file implements the functionality of offloading LAN to LAN traffic.
+
+	@Author
+	Shihuan Liu
+
+*/
+
+#include <stdlib.h>
+#include <assert.h>
+#include "IPACM_LanToLan.h"
+#include "IPACM_Wlan.h"
+
+IPACM_LanToLan::IPACM_LanToLan()
+{
+	num_offload_pair_v4_ = 0;
+	num_offload_pair_v6_ = 0;
+	client_info_v4_.reserve(IPA_LAN_TO_LAN_MAX_WLAN_CLIENT + IPA_LAN_TO_LAN_MAX_USB_CLIENT);
+	client_info_v6_.reserve(3*(IPA_LAN_TO_LAN_MAX_WLAN_CLIENT + IPA_LAN_TO_LAN_MAX_USB_CLIENT));
+
+	IPACM_EvtDispatcher::registr(IPA_LAN_CLIENT_ACTIVE, this);
+	IPACM_EvtDispatcher::registr(IPA_LAN_CLIENT_INACTIVE, this);
+	IPACM_EvtDispatcher::registr(IPA_LAN_CLIENT_DISCONNECT, this);
+	IPACM_EvtDispatcher::registr(IPA_LAN_CLIENT_POWER_SAVE, this);
+	IPACM_EvtDispatcher::registr(IPA_LAN_CLIENT_POWER_RECOVER, this);
+	IPACM_EvtDispatcher::registr(IPA_LAN_TO_LAN_NEW_CONNECTION, this);
+	IPACM_EvtDispatcher::registr(IPA_LAN_TO_LAN_DEL_CONNECTION, this);
+	return;
+}
+
+IPACM_LanToLan::~IPACM_LanToLan()
+{
+	client_table_v4::iterator it_v4;
+	for(it_v4 = client_info_v4_.begin(); it_v4 != client_info_v4_.end(); it_v4++)
+	{
+		turnoff_offload_links(IPA_IP_v4, &(it_v4->second));
+		clear_peer_list(&(it_v4->second));
+	}
+	client_info_v4_.clear();
+	IPACMDBG("Clear IPv4 hash table in Lan2Lan distructor.\n");
+
+	client_table_v6::iterator it_v6;
+	for(it_v6 = client_info_v6_.begin(); it_v6 != client_info_v6_.end(); it_v6++)
+	{
+		turnoff_offload_links(IPA_IP_v6, &(it_v6->second));
+		clear_peer_list(&(it_v6->second));
+	}
+	client_info_v6_.clear();
+	IPACMDBG("Clear IPv4 hash table in Lan2Lan distructor.\n");
+
+	return;
+}
+
+void IPACM_LanToLan::event_callback(ipa_cm_event_id event, void* param)
+{
+	switch(event)
+	{
+		case IPA_LAN_CLIENT_ACTIVE:
+		{
+			IPACMDBG("Get IPA_LAN_CLIENT_ACTIVE event.\n");
+			ipacm_event_lan_client* data = (ipacm_event_lan_client*)param;
+			handle_client_active(data);
+			break;
+		}
+
+		case IPA_LAN_CLIENT_INACTIVE:
+		{
+			IPACMDBG("Get IPA_LAN_CLIENT_INACTIVE event.\n");
+			ipacm_event_lan_client* data = (ipacm_event_lan_client*)param;
+			handle_client_inactive(data);
+			break;
+		}
+
+		case IPA_LAN_CLIENT_DISCONNECT:
+		{
+			IPACMDBG("Get IPA_LAN_CLIENT_DISCONNECT event.\n");
+			ipacm_event_lan_client* data = (ipacm_event_lan_client*)param;
+			handle_client_disconnect(data);
+			break;
+		}
+
+		case IPA_LAN_TO_LAN_NEW_CONNECTION:
+		{
+			IPACMDBG("Get IPA_LAN_TO_LAN_NEW_CONNECTION event.\n");
+			ipacm_event_connection* data = (ipacm_event_connection*)param;
+			handle_new_connection(data);
+			break;
+		}
+
+		case IPA_LAN_TO_LAN_DEL_CONNECTION:
+		{
+			IPACMDBG("Get IPA_LAN_TO_LAN_DEL_CONNECTION event.\n");
+			ipacm_event_connection* data = (ipacm_event_connection*)param;
+			handle_del_connection(data);
+			break;
+		}
+		case IPA_LAN_CLIENT_POWER_SAVE:
+		{
+			IPACMDBG("Get IPA_LAN_CLIENT_POWER_SAVE event.\n");
+			ipacm_event_lan_client* data = (ipacm_event_lan_client*)param;
+			handle_client_power_save(data);
+			break;
+		}
+		case IPA_LAN_CLIENT_POWER_RECOVER:
+		{
+			IPACMDBG("Get IPA_LAN_CLIENT_POWER_RECOVER event.\n");
+			ipacm_event_lan_client* data = (ipacm_event_lan_client*)param;
+			handle_client_power_recover(data);
+			break;
+		}
+		default:
+			break;
+	}
+	return;
+}
+
+void IPACM_LanToLan::handle_client_active(ipacm_event_lan_client* data)
+{
+	if(data == NULL)
+	{
+		IPACMERR("No client info is found.\n");
+		return;
+	}
+	if(data->mac_addr == NULL || data->ipv6_addr == NULL || data->p_iface == NULL)
+	{
+		IPACMERR("Event data is not populated properly.\n");
+		return;
+	}
+	if(data->iptype != IPA_IP_v4 && data->iptype != IPA_IP_v6)
+	{
+		IPACMERR("IP type is not expected.\n");
+		return;
+	}
+
+	IPACMDBG("New client info: iface %s, iptype: %d, mac: 0x%02x%02x%02x%02x%02x%02x, v4_addr: 0x%08x, v6_addr: 0x%08x%08x%08x%08x \n",
+		data->p_iface->dev_name, data->iptype,
+		data->mac_addr[0], data->mac_addr[1], data->mac_addr[2], data->mac_addr[3], data->mac_addr[4], data->mac_addr[5],
+		data->ipv4_addr, data->ipv6_addr[0], data->ipv6_addr[1], data->ipv6_addr[2], data->ipv6_addr[3]);
+
+	bool client_not_found;
+	client_info* client_ptr;
+
+	if(data->iptype == IPA_IP_v4)
+	{
+		client_not_found = (client_info_v4_.count(data->ipv4_addr) == 0);
+		IPACMDBG("Is the client not found? %d\n", client_not_found);
+		client_info& client = client_info_v4_[data->ipv4_addr];
+		client_ptr = &client;
+	}
+	else
+	{
+		uint64_t v6_addr;
+		memcpy(&v6_addr, &(data->ipv6_addr[2]), sizeof(uint64_t));
+
+		client_not_found = (client_info_v6_.count(v6_addr) == 0);
+		IPACMDBG("Is the client not found? %d\n", client_not_found);
+		client_info& client = client_info_v6_[v6_addr];
+		client_ptr = &client;
+	}
+
+	if(client_not_found == true)
+	{
+		if(data->iptype == IPA_IP_v4)
+		{
+			client_ptr->ip.ipv4_addr = data->ipv4_addr;
+		}
+		else
+		{
+			memcpy(client_ptr->ip.ipv6_addr, data->ipv6_addr, sizeof(client_ptr->ip.ipv6_addr));
+		}
+		memcpy(client_ptr->mac_addr, data->mac_addr, sizeof(client_ptr->mac_addr));
+		client_ptr->is_active =  true;
+		client_ptr->is_powersave = false;
+		client_ptr->p_iface = data->p_iface;
+
+		generate_new_connection(data->iptype, client_ptr); //code review: remember of remove this once listening to real connection evt
+	}
+	else	//the client is found
+	{
+		if(client_ptr->is_active == true)	//the client is active
+		{
+			IPACMDBG("The client is active.\n");
+			if(memcmp(client_ptr->mac_addr, data->mac_addr, sizeof(client_ptr->mac_addr)) == 0)
+			{
+				IPACMDBG("Mac addr is the same, do nothing.\n");
+			}
+			else
+			{
+				IPACMERR("The new client has same IP but differenc mac.\n");
+				turnoff_offload_links(data->iptype, client_ptr);
+				clear_peer_list(client_ptr);
+
+				if(data->iptype == IPA_IP_v4)
+				{
+					client_ptr->ip.ipv4_addr = data->ipv4_addr;
+				}
+				else
+				{
+					memcpy(client_ptr->ip.ipv6_addr, data->ipv6_addr, sizeof(client_ptr->ip.ipv6_addr));
+				}
+				memcpy(client_ptr->mac_addr, data->mac_addr, sizeof(client_ptr->mac_addr));
+				client_ptr->is_active =  true;
+				client_ptr->is_powersave = false;
+				client_ptr->p_iface = data->p_iface;
+
+				generate_new_connection(data->iptype, client_ptr);
+			}
+		}
+		else 	//the client is inactive
+		{
+			IPACMDBG("The client is inactive.\n");
+			if(data->iptype == IPA_IP_v4)
+			{
+				client_ptr->ip.ipv4_addr = data->ipv4_addr;
+			}
+			else
+			{
+				memcpy(client_ptr->ip.ipv6_addr, data->ipv6_addr, sizeof(client_ptr->ip.ipv6_addr));
+			}
+			memcpy(client_ptr->mac_addr, data->mac_addr, sizeof(client_ptr->mac_addr));
+			client_ptr->is_active =  true;
+			client_ptr->is_powersave = false;
+			client_ptr->p_iface = data->p_iface;
+
+			check_potential_link(data->iptype, client_ptr);
+			generate_new_connection(data->iptype, client_ptr);
+		}
+	}
+	IPACMDBG("There are %d clients in v4 table and %d clients in v6 table.\n", client_info_v4_.size(), client_info_v6_.size());
+	return;
+}
+
+void IPACM_LanToLan::check_potential_link(ipa_ip_type iptype, client_info* client)
+{
+	if(client == NULL)
+	{
+		IPACMERR("Client is NULL.\n");
+		return;
+	}
+
+	IPACMDBG("Check client's peer list.\n");
+	IPACMDBG("Client: IP type: %d, IPv4 addr: 0x%08x, IPv6 addr: 0x%08x%08x%08x%08x\n", iptype, client->ip.ipv4_addr,
+				client->ip.ipv6_addr[0], client->ip.ipv6_addr[1], client->ip.ipv6_addr[2], client->ip.ipv6_addr[3]);
+
+	peer_info_list::iterator peer_it;
+	int res, num = 0;
+
+	for(peer_it = client->peer.begin(); peer_it != client->peer.end(); peer_it++)
+	{
+		if(peer_it->peer_pointer->is_active == true && peer_it->num_connection > 0)
+		{
+			res = IPACM_SUCCESS;
+			res = add_offload_link(iptype, client, peer_it->peer_pointer);
+			res = add_offload_link(iptype, peer_it->peer_pointer, client);
+			if(res == IPACM_SUCCESS)
+			{
+				if(iptype == IPA_IP_v4)
+				{
+					num_offload_pair_v4_ ++;
+					IPACMDBG("Now the number of v4 offload links is %d.\n", num_offload_pair_v4_);
+				}
+				else
+				{
+					num_offload_pair_v6_ ++;
+					IPACMDBG("Now the number of v6 offload links is %d.\n", num_offload_pair_v6_);
+				}
+				num++;
+			}
+		}
+	}
+	IPACMDBG("Added %d offload links in total.\n", num);
+	return;
+}
+
+int IPACM_LanToLan::add_offload_link(ipa_ip_type iptype, client_info* client, client_info* peer)
+{
+	if( (iptype == IPA_IP_v4 && num_offload_pair_v4_ >= MAX_OFFLOAD_PAIR)
+		|| (iptype == IPA_IP_v6 && num_offload_pair_v6_ >= MAX_OFFLOAD_PAIR) )
+	{
+		IPACMDBG("The number of offload pairs already reaches maximum.\n");
+		return IPACM_FAILURE;
+	}
+	if(client == NULL || peer == NULL)
+	{
+		IPACMERR("Either client or peer is NULL.\n");
+		return IPACM_FAILURE;
+	}
+
+	uint32_t hdr_hdl, flt_hdl;
+	lan_to_lan_rt_rule_hdl rt_rule_hdl;
+
+	offload_link_info link_info;
+
+
+	if(iptype == IPA_IP_v4)
+	{
+		IPACMDBG("Add offload link for IPv4, client IP: 0x%08x, peer IP: 0x%08x \n", client->ip.ipv4_addr, peer->ip.ipv4_addr);
+	}
+	else if(iptype == IPA_IP_v6)
+	{
+		IPACMDBG("Add offload link for IPv6, client IP: 0x%08x%08x%08x%08x, peer IP: 0x%08x%08x%08x%08x \n",
+				client->ip.ipv6_addr[0], client->ip.ipv6_addr[1], client->ip.ipv6_addr[2], client->ip.ipv6_addr[3],
+				peer->ip.ipv6_addr[0], peer->ip.ipv6_addr[1], peer->ip.ipv6_addr[2], peer->ip.ipv6_addr[3]);
+	}
+	else
+	{
+		IPACMERR("IP type is not expected.\n");
+		return IPACM_FAILURE;
+	}
+
+	//add lan2lan header
+	if(peer->p_iface->add_lan2lan_hdr(iptype, client->mac_addr, peer->mac_addr, &hdr_hdl) == IPACM_FAILURE)
+	{
+		IPACMERR("Failed to create lan2lan header.\n");
+		return IPACM_FAILURE;
+	}
+	IPACMDBG("Created lan2lan hdr with hdl %d.\n", hdr_hdl);
+
+	//add lan2lan routing/filtering rules
+	if(peer->p_iface->add_lan2lan_rt_rule(iptype, client->ip.ipv4_addr, peer->ip.ipv4_addr,
+					client->ip.ipv6_addr, peer->ip.ipv6_addr, hdr_hdl, &rt_rule_hdl) == IPACM_FAILURE)
+	{
+		IPACMERR("Failed to create lan2lan rt rule.\n");
+		goto rt_fail;
+	}
+	IPACMDBG("Created %d lan2lan rt rules.\n", rt_rule_hdl.num_rule);
+	IPACMDBG("Created lan2lan rt rules with hdl: %d.\n", rt_rule_hdl.rule_hdl[0]);
+
+	if(client->p_iface->add_lan2lan_flt_rule(iptype, client->ip.ipv4_addr, peer->ip.ipv4_addr,
+					client->ip.ipv6_addr, peer->ip.ipv6_addr, &flt_hdl) == IPACM_FAILURE)
+	{
+		IPACMERR("Failed to create lan2lan flt rule.\n");
+		goto flt_fail;
+	}
+	IPACMDBG("Created lan2lan flt rule with hdl %d.\n", flt_hdl);
+
+	link_info.peer_pointer = peer;
+	link_info.flt_rule_hdl = flt_hdl;
+	link_info.hdr_hdl = hdr_hdl;
+	memcpy(&link_info.rt_rule_hdl, &rt_rule_hdl, sizeof(lan_to_lan_rt_rule_hdl));
+
+	client->link.push_back(link_info);
+
+	return IPACM_SUCCESS;
+
+flt_fail:
+	peer->p_iface->del_lan2lan_rt_rule(iptype, rt_rule_hdl);
+
+rt_fail:
+	peer->p_iface->del_lan2lan_hdr(iptype, hdr_hdl);
+
+	return IPACM_FAILURE;
+}
+
+void IPACM_LanToLan::handle_client_inactive(ipacm_event_lan_client* data)
+{
+	if(data == NULL)
+	{
+		IPACMERR("No client info is found.\n");
+		return;
+	}
+	if(data->mac_addr == NULL || data->ipv6_addr == NULL || data->p_iface == NULL)
+	{
+		IPACMERR("Event data is not populated properly.\n");
+		return;
+	}
+	if(data->iptype != IPA_IP_v4 && data->iptype != IPA_IP_v6)
+	{
+		IPACMERR("IP type is not expected: %d.\n", data->iptype);
+		return;
+	}
+
+	IPACMDBG("Del client info: iface %s, iptype: %d, mac: 0x%02x%02x%02x%02x%02x%02x, v4_addr: 0x%08x, v6_addr: 0x%08x%08x%08x%08x \n",
+		data->p_iface->dev_name, data->iptype,
+		data->mac_addr[0], data->mac_addr[1], data->mac_addr[2], data->mac_addr[3], data->mac_addr[4], data->mac_addr[5],
+		data->ipv4_addr, data->ipv6_addr[0], data->ipv6_addr[1], data->ipv6_addr[2], data->ipv6_addr[3]);
+
+	client_info* client_ptr;
+	uint64_t v6_addr;
+
+	if(data->iptype == IPA_IP_v4)
+	{
+		if(client_info_v4_.count(data->ipv4_addr) == 0)//if not found the client, return
+		{
+			IPACMERR("The client is not found the client, return.\n");
+			return;
+		}
+		IPACMDBG("The client is found.\n");
+		client_info& client = client_info_v4_[data->ipv4_addr];
+		client_ptr = &client;
+	}
+	else
+	{
+		memcpy(&v6_addr, &(data->ipv6_addr[2]), sizeof(uint64_t));
+		if(client_info_v6_.count(v6_addr) == 0)	//if not found the client, insert it in table
+		{
+			IPACMERR("The client is not found the client, return.\n");
+			return;
+		}
+		IPACMDBG("The client is found.\n");
+		client_info& client = client_info_v6_[v6_addr];
+		client_ptr = &client;
+	}
+
+	turnoff_offload_links(data->iptype, client_ptr);
+	client_ptr->is_active = false;
+	if(client_ptr->peer.size() == 0)
+	{
+		IPACMDBG("Peer list is empty, remove client entry.\n");
+		if(data->iptype == IPA_IP_v4)
+		{
+			client_info_v4_.erase(data->ipv4_addr);
+		}
+		else
+		{
+			client_info_v6_.erase(v6_addr);
+		}
+	}
+	return;
+}
+
+int IPACM_LanToLan::turnoff_offload_links(ipa_ip_type iptype, client_info* client)
+{
+	if(client == NULL)
+	{
+		IPACMERR("Client is NULL.\n");
+		return IPACM_FAILURE;
+	}
+
+	bool err_flag;
+	offload_link_info_list::iterator client_it;
+	offload_link_info_list::iterator peer_it;
+	client_info* peer;
+
+	for(client_it = client->link.begin(); client_it != client->link.end(); client_it++)
+	{
+		peer = client_it->peer_pointer;
+		if(del_offload_link(iptype, client->p_iface, peer->p_iface, &(*client_it)) == IPACM_FAILURE)
+		{
+			IPACMERR("Failed to delete client's offload link.\n");
+			return IPACM_FAILURE;
+		}
+
+		err_flag = true;
+		for(peer_it = peer->link.begin(); peer_it != peer->link.end(); peer_it++)
+		{
+			if(peer_it->peer_pointer == client)
+			{
+				if(del_offload_link(iptype, peer->p_iface, client->p_iface, &(*peer_it)) == IPACM_FAILURE)
+				{
+					IPACMERR("Failed to delete peer's offload link.\n");
+					return IPACM_FAILURE;
+				}
+				peer->link.erase(peer_it);
+				err_flag = false;
+				break;
+			}
+		}
+		if(err_flag)
+		{
+			IPACMERR("Unable to find corresponding offload link in peer's entry.\n");
+			return IPACM_FAILURE;
+		}
+		if(iptype == IPA_IP_v4)
+		{
+			num_offload_pair_v4_ --;
+			IPACMDBG("Now the number of v4 offload pair is %d\n", num_offload_pair_v4_);
+		}
+		else
+		{
+			num_offload_pair_v6_ --;
+			IPACMDBG("Now the number of v6 offload pair is %d\n", num_offload_pair_v6_);
+		}
+	}
+
+	client->link.clear();
+	return IPACM_SUCCESS;
+}
+
+int IPACM_LanToLan::del_offload_link(ipa_ip_type iptype, IPACM_Lan* client, IPACM_Lan* peer, offload_link_info* link)
+{
+	if(client == NULL || peer == NULL || link == NULL)
+	{
+		IPACMERR("Either iface or link is NULL.\n");
+		return IPACM_FAILURE;
+	}
+
+	IPACMDBG("Delete an offload link for IP type: %d\n", iptype);
+
+	int res = IPACM_SUCCESS;
+
+	if(client->del_lan2lan_flt_rule(iptype, link->flt_rule_hdl) == IPACM_FAILURE)
+	{
+		IPACMERR("Failed to delete flt rule.\n");
+		res = IPACM_FAILURE;
+	}
+
+	if(peer->del_lan2lan_rt_rule(iptype, link->rt_rule_hdl) == IPACM_FAILURE)
+	{
+		IPACMERR("Failed to delete rt rules.\n");
+		res = IPACM_FAILURE;
+	}
+
+	if(peer->del_lan2lan_hdr(iptype, link->hdr_hdl) == IPACM_FAILURE)
+	{
+		IPACMERR("Failed to delete header.\n");
+		res = IPACM_FAILURE;
+	}
+
+	return res;
+}
+
+void IPACM_LanToLan::handle_client_disconnect(ipacm_event_lan_client* data)
+{
+	if(data == NULL)
+	{
+		IPACMERR("No client info is found.\n");
+		return;
+	}
+	if(data->mac_addr == NULL || data->ipv6_addr == NULL || data->p_iface == NULL)
+	{
+		IPACMERR("Event data is not populated properly.\n");
+		return;
+	}
+	if(data->iptype != IPA_IP_v4 && data->iptype != IPA_IP_v6)
+	{
+		IPACMERR("IP type is not expected: %d.\n", data->iptype);
+		return;
+	}
+
+	IPACMDBG("Del client info: iface %s, iptype: %d, mac: 0x%02x%02x%02x%02x%02x%02x, v4_addr: 0x%08x, v6_addr: 0x%08x%08x%08x%08x \n",
+		data->p_iface->dev_name, data->iptype,
+		data->mac_addr[0], data->mac_addr[1], data->mac_addr[2], data->mac_addr[3], data->mac_addr[4], data->mac_addr[5],
+		data->ipv4_addr, data->ipv6_addr[0], data->ipv6_addr[1], data->ipv6_addr[2], data->ipv6_addr[3]);
+
+	client_info* client_ptr;
+	uint64_t v6_addr;
+	if(data->iptype == IPA_IP_v4)
+	{
+		if(client_info_v4_.count(data->ipv4_addr) == 0)	//if not found the client, return
+		{
+			IPACMERR("The client is not found the client, return.\n");
+			return;
+		}
+		IPACMDBG("The client is found.\n");
+		client_info& client = client_info_v4_[data->ipv4_addr];
+		client_ptr = &client;
+	}
+	else
+	{
+		memcpy(&v6_addr, &(data->ipv6_addr[2]), sizeof(uint64_t));
+		if(client_info_v6_.count(v6_addr) == 0)	//if not found the client, insert it in table
+		{
+			IPACMERR("The client is not found the client, return.\n");
+			return;
+		}
+		IPACMDBG("The client is found.\n");
+		client_info& client = client_info_v6_[v6_addr];
+		client_ptr = &client;
+	}
+
+	turnoff_offload_links(data->iptype, client_ptr);
+	clear_peer_list(client_ptr);
+	if(data->iptype == IPA_IP_v4)
+	{
+		client_info_v4_.erase(data->ipv4_addr);
+	}
+	else
+	{
+		client_info_v6_.erase(v6_addr);
+	}
+	return;
+}
+
+int IPACM_LanToLan::clear_peer_list(client_info* client)
+{
+	if(client == NULL)
+	{
+		IPACMERR("Client is NULL.\n");
+		return IPACM_FAILURE;
+	}
+
+	bool err_flag;
+	peer_info_list::iterator client_it;
+	peer_info_list::iterator peer_it;
+	client_info* peer;
+
+	for(client_it = client->peer.begin(); client_it != client->peer.end(); client_it++)
+	{
+		err_flag = true;
+		peer = client_it->peer_pointer;
+		for(peer_it = peer->peer.begin(); peer_it != peer->peer.end(); peer_it++)
+		{
+			if(peer_it->peer_pointer == client)
+			{
+				peer->peer.erase(peer_it);
+				err_flag = false;
+				break;
+			}
+		}
+		if(err_flag == true)
+		{
+			IPACMERR("Failed to find peer info.\n");
+			return IPACM_FAILURE;
+		}
+	}
+	client->peer.clear();
+	return IPACM_SUCCESS;
+}
+
+void IPACM_LanToLan::handle_client_power_save(ipacm_event_lan_client* data)
+{
+	if(data == NULL)
+	{
+		IPACMERR("No client info is found.\n");
+		return;
+	}
+	if(data->mac_addr == NULL || data->ipv6_addr == NULL || data->p_iface == NULL)
+	{
+		IPACMERR("Event data is not populated properly.\n");
+		return;
+	}
+	if(data->iptype != IPA_IP_v4 && data->iptype != IPA_IP_v6)
+	{
+		IPACMERR("IP type is not expected: %d.\n", data->iptype);
+		return;
+	}
+
+	IPACMDBG("Client power save info: iface %s, iptype: %d, mac: 0x%02x%02x%02x%02x%02x%02x, v4_addr: 0x%08x, v6_addr: 0x%08x%08x%08x%08x \n",
+		data->p_iface->dev_name, data->iptype,
+		data->mac_addr[0], data->mac_addr[1], data->mac_addr[2], data->mac_addr[3], data->mac_addr[4], data->mac_addr[5],
+		data->ipv4_addr, data->ipv6_addr[0], data->ipv6_addr[1], data->ipv6_addr[2], data->ipv6_addr[3]);
+
+	client_info* client_ptr;
+	uint64_t v6_addr;
+
+	if(data->iptype == IPA_IP_v4)
+	{
+		if(client_info_v4_.count(data->ipv4_addr) == 0)//if not found the client, return
+		{
+			IPACMERR("The client is not found the client, return.\n");
+			return;
+		}
+		IPACMDBG("The client is found.\n");
+		client_info& client = client_info_v4_[data->ipv4_addr];
+		client_ptr = &client;
+	}
+	else
+	{
+		memcpy(&v6_addr, &(data->ipv6_addr[2]), sizeof(uint64_t));
+		if(client_info_v6_.count(v6_addr) == 0)	//if not found the client, insert it in table
+		{
+			IPACMERR("The client is not found the client, return.\n");
+			return;
+		}
+		IPACMDBG("The client is found.\n");
+		client_info& client = client_info_v6_[v6_addr];
+		client_ptr = &client;
+	}
+
+	if(remove_flt_rules(data->iptype, client_ptr) == IPACM_FAILURE)
+	{
+		IPACMERR("Failed to remove flt rules when power save.\n");
+		return;
+	}
+	client_ptr->is_active = false;
+	client_ptr->is_powersave = true;
+	return;
+}
+
+void IPACM_LanToLan::handle_new_connection(ipacm_event_connection* data)
+{
+	if(data == NULL)
+	{
+		IPACMERR("No connection info is found.\n");
+		return;
+	}
+	if(data->iptype != IPA_IP_v4 && data->iptype != IPA_IP_v6)
+	{
+		IPACMERR("IP type is not expected: %d.\n", data->iptype);
+		return;
+	}
+
+	IPACMDBG("New connection info: IP type: %d, src_v4_addr: 0x%08x, dst_v4_addr: 0x%08x\n", data->iptype, data->src_ipv4_addr, data->dst_ipv4_addr);
+	IPACMDBG("src_v6_addr: 0x%08x%08x%08x%08x, dst_v6_addr: 0x%08x%08x%08x%08x", data->src_ipv6_addr[0], data->src_ipv6_addr[1], data->src_ipv6_addr[2],
+				data->src_ipv6_addr[3], data->dst_ipv6_addr[0], data->dst_ipv6_addr[1], data->dst_ipv6_addr[2], data->dst_ipv6_addr[3]);
+
+	client_info* src_client_ptr;
+	client_info* dst_client_ptr;
+
+	if(data->iptype == IPA_IP_v4)
+	{
+		if(client_info_v4_.count(data->src_ipv4_addr) == 0 || client_info_v4_.count(data->dst_ipv4_addr) == 0)
+		{
+			IPACMERR("Either source or destination is not in table.\n");
+			return;
+		}
+		client_info& src_client = client_info_v4_[data->src_ipv4_addr];
+		src_client_ptr = &src_client;
+		client_info& dst_client = client_info_v4_[data->dst_ipv4_addr];
+		dst_client_ptr = &dst_client;
+	}
+	else //ipv6 case
+	{
+		uint64_t src_ipv6_addr;
+		uint64_t dst_ipv6_addr;
+		memcpy(&src_ipv6_addr, &(data->src_ipv6_addr[2]), sizeof(uint64_t));
+		memcpy(&dst_ipv6_addr, &(data->dst_ipv6_addr[2]), sizeof(uint64_t));
+
+		if(client_info_v6_.count(src_ipv6_addr) == 0 || client_info_v6_.count(dst_ipv6_addr) == 0)
+		{
+			IPACMERR("Either source or destination is not in table.\n");
+			return;
+		}
+		client_info& src_client = client_info_v6_[src_ipv6_addr];
+		src_client_ptr = &src_client;
+		client_info& dst_client = client_info_v6_[dst_ipv6_addr];
+		dst_client_ptr = &dst_client;
+	}
+
+	IPACMDBG("Both src and dst are already in table.\n");
+	bool is_first_connection;
+	is_first_connection = add_connection(src_client_ptr, dst_client_ptr);
+
+	if(is_first_connection && src_client_ptr->is_active && dst_client_ptr->is_active)
+	{
+		IPACMDBG("This is first connection, add offload links.\n");
+
+		if(add_offload_link(data->iptype, src_client_ptr, dst_client_ptr) == IPACM_FAILURE)
+		{
+			IPACMERR("Failed to add offload link for src->dst direction.\n");
+			return;
+		}
+		if(add_offload_link(data->iptype, dst_client_ptr, src_client_ptr) == IPACM_FAILURE)
+		{
+			IPACMERR("Failed to add offload link for dst->src direction.\n");
+			return;
+		}
+
+		if(data->iptype == IPA_IP_v4)
+		{
+			num_offload_pair_v4_ ++;
+			IPACMDBG("Added offload links, now num_offload_pair_v4_: %d\n", num_offload_pair_v4_);
+		}
+		else
+		{
+			num_offload_pair_v6_ ++;
+			IPACMDBG("Added offload links, now num_offload_pair_v6_: %d\n", num_offload_pair_v6_);
+		}
+	}
+	return;
+}
+
+//If need to insert an entry in peer list, return true, otherwise return false
+bool IPACM_LanToLan::add_connection(client_info* src_client, client_info* dst_client)
+{
+	if(src_client == NULL || dst_client == NULL)
+	{
+		IPACMERR("Either source or dest client is NULL.\n");
+		return false;
+	}
+
+	peer_info_list::iterator it;
+	peer_info new_peer;
+	bool ret = false;
+
+	for(it = src_client->peer.begin(); it != src_client->peer.end(); it++)
+	{
+		if(it->peer_pointer == dst_client)
+		{
+			it->num_connection++;
+			IPACMDBG("Find dst client entry in peer list, connection count: %d\n", it->num_connection);
+			break;
+		}
+	}
+	if(it == src_client->peer.end())
+	{
+		IPACMDBG("Not finding dst client entry, insert a new one in peer list.\n");
+		new_peer.peer_pointer = dst_client;
+		new_peer.num_connection = 1;
+		src_client->peer.push_back(new_peer);
+		ret = true;
+	}
+
+	for(it = dst_client->peer.begin(); it != dst_client->peer.end(); it++)
+	{
+		if(it->peer_pointer == src_client)
+		{
+			it->num_connection++;
+			IPACMDBG("Find dst client entry in peer list, connection count: %d\n", it->num_connection);
+			break;
+		}
+	}
+	if(it == dst_client->peer.end())
+	{
+		IPACMDBG("Not finding src client entry, insert a new one in peer list.\n");
+		new_peer.peer_pointer = src_client;
+		new_peer.num_connection = 1;
+		dst_client->peer.push_back(new_peer);
+		ret = true;
+	}
+	return ret;
+}
+
+void IPACM_LanToLan::handle_del_connection(ipacm_event_connection* data)
+{
+	if(data == NULL)
+	{
+		IPACMERR("No connection info is found.\n");
+		return;
+	}
+	if(data->iptype != IPA_IP_v4 && data->iptype != IPA_IP_v6)
+	{
+		IPACMERR("IP type is not expected: %d.\n", data->iptype)
+		return;
+	}
+
+	IPACMDBG("Del connection info: IP type: %d, src_v4_addr: 0x%08x, dst_v4_addr: 0x%08x\n", data->iptype, data->src_ipv4_addr, data->dst_ipv4_addr);
+	IPACMDBG("src_v6_addr: 0x%08x%08x%08x%08x, dst_v6_addr: 0x%08x%08x%08x%08x", data->src_ipv6_addr[0], data->src_ipv6_addr[1], data->src_ipv6_addr[2],
+				data->src_ipv6_addr[3], data->dst_ipv6_addr[0], data->dst_ipv6_addr[1], data->dst_ipv6_addr[2], data->dst_ipv6_addr[3]);
+
+	bool res;
+	uint64_t src_ipv6_addr, dst_ipv6_addr;
+	client_info* src_client_ptr;
+	client_info* dst_client_ptr;
+
+	if(data->iptype == IPA_IP_v4)
+	{
+		if(client_info_v4_.count(data->src_ipv4_addr) == 0
+			|| client_info_v4_.count(data->dst_ipv4_addr) == 0)	//if not found the client
+		{
+			IPACMDBG("Not found either source or dest client, return.\n");
+			return;
+		}
+		client_info& src_client = client_info_v4_[data->src_ipv4_addr];
+		client_info& dst_client = client_info_v4_[data->dst_ipv4_addr];
+		src_client_ptr = &src_client;
+		dst_client_ptr = &dst_client;
+	}
+	else
+	{
+		memcpy(&src_ipv6_addr, &(data->src_ipv6_addr[2]), sizeof(uint64_t));
+		memcpy(&dst_ipv6_addr, &(data->dst_ipv6_addr[2]), sizeof(uint64_t));
+		if(client_info_v6_.count(src_ipv6_addr) == 0
+			|| client_info_v6_.count(dst_ipv6_addr) == 0)//if not found the client
+		{
+			IPACMDBG("Not found either source or dest client, return.\n");
+			return;
+		}
+		client_info& src_client = client_info_v6_[src_ipv6_addr];
+		client_info& dst_client = client_info_v6_[dst_ipv6_addr];
+		src_client_ptr = &src_client;
+		dst_client_ptr = &dst_client;
+	}
+
+	res = remove_connection(src_client_ptr, dst_client_ptr);
+
+	if(res && src_client_ptr->is_active && dst_client_ptr->is_active)
+	{
+		IPACMDBG("Erase link info for both src and dst entries.\n");
+		erase_offload_link(data->iptype, src_client_ptr, dst_client_ptr);
+	}
+	else
+	{
+		if(res && src_client_ptr->is_powersave && (dst_client_ptr->is_active || dst_client_ptr->is_powersave))
+		{
+			IPACMDBG("Erase link info for both src and dst entries due to src powersave.\n");
+			erase_offload_link(data->iptype, src_client_ptr, dst_client_ptr);
+		}
+		if(res && dst_client_ptr->is_powersave && (src_client_ptr->is_active || src_client_ptr->is_powersave))
+		{
+			IPACMDBG("Erase link info for both src and dst entries due to dst powersave.\n");
+			erase_offload_link(data->iptype, dst_client_ptr, src_client_ptr);
+		}
+	}
+
+	//if the src client is not active and not powersave mode, if peer list is empty, remove client entry
+	if(res && src_client_ptr->is_active == false && src_client_ptr->is_powersave == false && src_client_ptr->peer.size() == 0)
+	{
+		IPACMDBG("Peer list of src is empty, remove src entry.\n");
+		if(data->iptype == IPA_IP_v4)
+		{
+			client_info_v4_.erase(data->src_ipv4_addr);
+		}
+		else
+		{
+			client_info_v6_.erase(src_ipv6_addr);
+		}
+	}
+
+	//if the dst client is not active and not powersave mode, if peer list is empty, remove client entry
+	if(res && dst_client_ptr->is_active == false && dst_client_ptr->is_powersave == false && dst_client_ptr->peer.size() == 0)
+	{
+		IPACMDBG("Peer list of dst is empty, remove dst entry.\n");
+		if(data->iptype == IPA_IP_v4)
+		{
+			client_info_v4_.erase(data->dst_ipv4_addr);
+		}
+		else
+		{
+			client_info_v6_.erase(dst_ipv6_addr);
+		}
+	}
+	return;
+}
+
+//If need to remove an entry in peer list, return true, otherwise return false
+bool IPACM_LanToLan::remove_connection(client_info* src_client, client_info* dst_client)
+{
+	if(src_client == NULL || dst_client == NULL)
+	{
+		IPACMERR("Either source or dest client is NULL.\n");
+		return false;
+	}
+
+	peer_info_list::iterator it;
+	bool ret = false;
+
+	for(it = src_client->peer.begin(); it != src_client->peer.end(); it++)
+	{
+		if(it->peer_pointer == dst_client)
+		{
+			it->num_connection--;
+			IPACMDBG("Find dst client entry in src peer list, connection count: %d\n", it->num_connection);
+			if(it->num_connection == 0)
+			{
+				IPACMDBG("Need to remove dst entry in src peer list");
+				ret = true;
+			}
+			break;
+		}
+	}
+	if(ret == true)
+	{
+		src_client->peer.erase(it);
+	}
+
+	ret = false;
+	for(it = dst_client->peer.begin(); it != dst_client->peer.end(); it++)
+	{
+		if(it->peer_pointer == src_client)
+		{
+			it->num_connection--;
+			IPACMDBG("Find src client entry in dst peer list, connection count: %d\n", it->num_connection);
+			if(it->num_connection == 0)
+			{
+				IPACMDBG("Need to remove src entry in dst peer list");
+				ret = true;
+			}
+			break;
+		}
+	}
+	if(ret == true)
+	{
+		dst_client->peer.erase(it);
+	}
+	return ret;
+}
+
+void IPACM_LanToLan::erase_offload_link(ipa_ip_type iptype, client_info* src_client, client_info* dst_client)
+{
+	if(src_client == NULL || dst_client == NULL)
+	{
+		IPACMERR("Either source or dest client is NULL.\n");
+		return;
+	}
+
+	offload_link_info_list::iterator it;
+	int res_src = IPACM_FAILURE, res_dst = IPACM_FAILURE;
+
+	for(it = src_client->link.begin(); it != src_client->link.end(); it++)
+	{
+		if(it->peer_pointer == dst_client)
+		{
+			res_src = IPACM_SUCCESS;
+			IPACMDBG("Find dst client entry in src link list\n");
+			res_src = del_offload_link(iptype, src_client->p_iface, dst_client->p_iface, &(*it));
+			src_client->link.erase(it);
+			break;
+		}
+	}
+
+	for(it = dst_client->link.begin(); it != dst_client->link.end(); it++)
+	{
+		if(it->peer_pointer == src_client)
+		{
+			res_dst = IPACM_SUCCESS;
+			IPACMDBG("Find src client entry in dst link list\n");
+			res_dst = del_offload_link(iptype, dst_client->p_iface, src_client->p_iface, &(*it));
+			dst_client->link.erase(it);
+			break;
+		}
+	}
+
+	if(res_src == IPACM_SUCCESS && res_dst == IPACM_SUCCESS)
+	{
+		if(iptype == IPA_IP_v4)
+		{
+			num_offload_pair_v4_ --;
+			IPACMDBG("Decrease num of v4 offload pairs to %d\n", num_offload_pair_v4_);
+		}
+		else
+		{
+			num_offload_pair_v6_ --;
+			IPACMDBG("Decrease num of v6 offload pairs to %d\n", num_offload_pair_v6_);
+		}
+	}
+	return;
+}
+
+void IPACM_LanToLan::generate_new_connection(ipa_ip_type iptype, client_info* client)
+{
+	if(client == NULL)
+	{
+		IPACMERR("Client is NULL.\n");
+		return;
+	}
+
+	IPACMDBG("Generate new connection events for IP type %d\n", iptype);
+
+	int num = 0;
+	ipacm_cmd_q_data evt;
+	ipacm_event_connection* new_conn;
+	ipacm_iface_type client_type, peer_type;
+	client_type = IPACM_Iface::ipacmcfg->iface_table[client->p_iface->ipa_if_num].if_cat;
+
+	IPACMDBG("Client's iface type is %d.\n", client_type);
+
+	if(iptype == IPA_IP_v4)
+	{
+		client_table_v4::iterator it;
+		for(it = client_info_v4_.begin(); it != client_info_v4_.end(); it++)
+		{
+			peer_type = IPACM_Iface::ipacmcfg->iface_table[it->second.p_iface->ipa_if_num].if_cat;
+			if(peer_type != client_type && it->second.is_active == true)
+			{
+				IPACMDBG("Find a qualified peer to generate new_conn event.\n");
+				IPACMDBG("Peer's iface type is %d.\n", peer_type);
+				new_conn = (ipacm_event_connection*)malloc(sizeof(ipacm_event_connection));
+				if(new_conn == NULL)
+				{
+					IPACMERR("Failed to allocate memory for new_connection event.\n");
+					return;
+				}
+				memset(new_conn, 0, sizeof(ipacm_event_connection));
+				new_conn->iptype = IPA_IP_v4;
+				new_conn->src_ipv4_addr = client->ip.ipv4_addr;
+				new_conn->dst_ipv4_addr = it->second.ip.ipv4_addr;
+
+				memset(&evt, 0, sizeof(evt));
+				evt.event = IPA_LAN_TO_LAN_NEW_CONNECTION;
+				evt.evt_data = (void*)new_conn;
+				IPACM_EvtDispatcher::PostEvt(&evt);
+				num++;
+			}
+		}
+	}
+	else if(iptype == IPA_IP_v6)
+	{
+		client_table_v6::iterator it;
+		for(it = client_info_v6_.begin(); it != client_info_v6_.end(); it++)
+		{
+			peer_type = IPACM_Iface::ipacmcfg->iface_table[it->second.p_iface->ipa_if_num].if_cat;
+			if(peer_type != client_type && it->second.is_active == true)
+			{
+				IPACMDBG("Find a qualified peer to generate new_conn event.\n");
+				IPACMDBG("Peer's iface type is %d.\n", peer_type);
+				new_conn = (ipacm_event_connection*)malloc(sizeof(ipacm_event_connection));
+				if(new_conn == NULL)
+				{
+					IPACMERR("Failed to allocate memory for new_connection event.\n");
+					return;
+				}
+				memset(new_conn, 0, sizeof(ipacm_event_connection));
+				new_conn->iptype = IPA_IP_v6;
+				memcpy(new_conn->src_ipv6_addr, client->ip.ipv6_addr, sizeof(client->ip.ipv6_addr));
+				memcpy(new_conn->dst_ipv6_addr, it->second.ip.ipv6_addr, sizeof(it->second.ip.ipv6_addr));
+
+				memset(&evt, 0, sizeof(evt));
+				evt.event = IPA_LAN_TO_LAN_NEW_CONNECTION;
+				evt.evt_data = (void*)new_conn;
+				IPACM_EvtDispatcher::PostEvt(&evt);
+				num++;
+			}
+		}
+	}
+	else
+	{
+		IPACMERR("IP type is not expected.\n");
+	}
+	IPACMDBG("Generate %d new connection events in total.\n", num);
+	return;
+}
+
+void IPACM_LanToLan::handle_client_power_recover(ipacm_event_lan_client* data)
+{
+	if(data == NULL)
+	{
+		IPACMERR("No client info is found.\n");
+		return;
+	}
+	if(data->mac_addr == NULL || data->ipv6_addr == NULL || data->p_iface == NULL)
+	{
+		IPACMERR("Event data is not populated properly.\n");
+		return;
+	}
+	if(data->iptype != IPA_IP_v4 && data->iptype != IPA_IP_v6)
+	{
+		IPACMERR("IP type is not expected: %d\n", data->iptype);
+		return;
+	}
+
+	IPACMDBG("New client info: iface %s, iptype: %d, mac: 0x%02x%02x%02x%02x%02x%02x, v4_addr: 0x%08x, v6_addr: 0x%08x%08x%08x%08x \n",
+		data->p_iface->dev_name, data->iptype,
+		data->mac_addr[0], data->mac_addr[1], data->mac_addr[2], data->mac_addr[3], data->mac_addr[4], data->mac_addr[5],
+		data->ipv4_addr, data->ipv6_addr[0], data->ipv6_addr[1], data->ipv6_addr[2], data->ipv6_addr[3]);
+
+	client_info* client_ptr;
+	if(data->iptype == IPA_IP_v4)
+	{
+		if(client_info_v4_.count(data->ipv4_addr) == 0)
+		{
+			IPACMERR("Client is not found.\n");
+			return;
+		}
+		client_info& client = client_info_v4_[data->ipv4_addr];
+		client_ptr = &client;
+	}
+	else
+	{
+		uint64_t v6_addr;
+		memcpy(&v6_addr, &(data->ipv6_addr[2]), sizeof(uint64_t));
+		if(client_info_v6_.count(v6_addr) == 0)
+		{
+			IPACMERR("Client is not found.\n");
+			return;
+		}
+		client_info& client = client_info_v6_[v6_addr];
+		client_ptr = &client;
+	}
+
+	if(client_ptr->is_active == true || client_ptr->is_powersave != true)	//the client is in wrong state
+	{
+		IPACMERR("The client is in wrong state: active %d, powersave %d.\n", client_ptr->is_active, client_ptr->is_powersave);
+		return;
+	}
+	else
+	{
+		if(add_flt_rules(data->iptype, client_ptr) == IPACM_FAILURE)
+		{
+			IPACMERR("Failed to add back flt rules when power recovery.\n");
+			return;
+		}
+		client_ptr->is_active =  true;
+		client_ptr->is_powersave = false;
+
+		check_potential_link(data->iptype, client_ptr);
+		generate_new_connection(data->iptype, client_ptr);
+	}
+	IPACMDBG("There are %d clients in v4 table and %d clients in v6 table.\n", client_info_v4_.size(), client_info_v6_.size());
+	return;
+}
+
+//This function is called when power save: remove filtering rules only
+int IPACM_LanToLan::remove_flt_rules(ipa_ip_type iptype, client_info* client)
+{
+	if(client == NULL)
+	{
+		IPACMERR("No client info is found.\n");
+		return IPACM_FAILURE;
+	}
+
+	bool err_flag;
+	offload_link_info_list::iterator client_it;
+	offload_link_info_list::iterator peer_it;
+	client_info* peer;
+
+	for(client_it = client->link.begin(); client_it != client->link.end(); client_it++)
+	{
+		if(client->p_iface->del_lan2lan_flt_rule(iptype, client_it->flt_rule_hdl) == IPACM_FAILURE)
+		{
+			IPACMERR("Failed to delete client's filtering rule.\n");
+		}
+
+		err_flag = true;
+		peer = client_it->peer_pointer;
+		for(peer_it = peer->link.begin(); peer_it != peer->link.end(); peer_it++)
+		{
+			if(peer_it->peer_pointer == client)
+			{
+				if(peer->p_iface->del_lan2lan_flt_rule(iptype, peer_it->flt_rule_hdl) == IPACM_FAILURE)
+				{
+					IPACMERR("Failed to delete peer's offload link.\n");
+				}
+				err_flag = false;
+				break;
+			}
+		}
+		if(err_flag)
+		{
+			IPACMERR("Unable to find corresponding offload link in peer's entry.\n");
+			return IPACM_FAILURE;
+		}
+	}
+	return IPACM_SUCCESS;
+}
+
+int IPACM_LanToLan::add_flt_rules(ipa_ip_type iptype, client_info* client)
+{
+	if(client == NULL)
+	{
+		IPACMERR("No client info is found.\n");
+		return IPACM_FAILURE;
+	}
+
+	bool err_flag;
+	offload_link_info_list::iterator client_it;
+	offload_link_info_list::iterator peer_it;
+	client_info* peer;
+
+	for(client_it = client->link.begin(); client_it != client->link.end(); client_it++)
+	{
+		peer = client_it->peer_pointer;
+		if(client->p_iface->add_lan2lan_flt_rule(iptype, client->ip.ipv4_addr, peer->ip.ipv4_addr,
+			client->ip.ipv6_addr, peer->ip.ipv6_addr, &(client_it->flt_rule_hdl)) == IPACM_FAILURE)
+		{
+			IPACMERR("Failed to add client's filtering rule.\n");
+			return IPACM_FAILURE;
+		}
+
+		err_flag = true;
+		for(peer_it = peer->link.begin(); peer_it != peer->link.end(); peer_it++)
+		{
+			if(peer_it->peer_pointer == client)
+			{
+				if(peer->p_iface->add_lan2lan_flt_rule(iptype, peer->ip.ipv4_addr, client->ip.ipv4_addr,
+					peer->ip.ipv6_addr, client->ip.ipv6_addr, &(peer_it->flt_rule_hdl)) == IPACM_FAILURE)
+				{
+					IPACMERR("Failed to delete peer's offload link.\n");
+					return IPACM_FAILURE;
+				}
+				err_flag = false;
+				break;
+			}
+		}
+		if(err_flag)
+		{
+			IPACMERR("Unable to find corresponding offload link in peer's entry.\n");
+			return IPACM_FAILURE;
+		}
+	}
+	return IPACM_SUCCESS;
+}
+
diff --git a/ipacm/src/IPACM_Main.cpp b/ipacm/src/IPACM_Main.cpp
index d13816f..7ec4d6e 100644
--- a/ipacm/src/IPACM_Main.cpp
+++ b/ipacm/src/IPACM_Main.cpp
@@ -68,6 +68,8 @@
 
 #include "IPACM_ConntrackListener.h"
 
+#include "IPACM_LanToLan.h"
+
 const char *ipacm_event_name[] = {
 	__stringify(IPA_LINK_UP_EVENT),
 	__stringify(IPA_LINK_DOWN_EVENT),
@@ -492,7 +494,6 @@
 void IPACM_Sig_Handler(int sig)
 {
 	int cnt = 0;
-	char evt_name[30] = {0};
 
 	printf("Received Signal: %d\n", sig);
 
@@ -539,6 +540,7 @@
 
 	IPACM_Neighbor *neigh = new IPACM_Neighbor();
 	IPACM_IfaceManager *ifacemgr = new IPACM_IfaceManager();
+	IPACM_LanToLan* lan2lan = new IPACM_LanToLan();
 
 	IPACMDBG("Staring IPA main\n");
 	IPACMDBG("ipa_cmdq_successful\n");
diff --git a/ipacm/src/IPACM_Wlan.cpp b/ipacm/src/IPACM_Wlan.cpp
index 4670974..30b71e6 100644
--- a/ipacm/src/IPACM_Wlan.cpp
+++ b/ipacm/src/IPACM_Wlan.cpp
@@ -1,4 +1,4 @@
-/* 
+/*
 Copyright (c) 2013, The Linux Foundation. All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
@@ -88,10 +88,15 @@
 
 void IPACM_Wlan::event_callback(ipa_cm_event_id event, void *param)
 {
+	if(is_active == false && event != IPA_LAN_DELETE_SELF)
+	{
+		IPACMDBG("The interface is no longer active, return.\n");
+		return;
+	}
+
 	int ipa_interface_index;
 	int wlan_index;
 	ipacm_ext_prop* ext_prop;
-	ipacm_event_iface_up* data;
 
 	switch (event)
 	{
@@ -104,36 +109,45 @@
 			{
 				IPACMDBG("Received IPA_LINK_DOWN_EVENT\n");
 				handle_down_evt();
-				IPACMDBG("ipa_WLAN (%s):ipa_index (%d) instance close \n",
-				IPACM_Iface::ipacmcfg->iface_table[ipa_if_num].iface_name, ipa_if_num);
-				/* reset the AP-iface category to unknown */				 
+				/* reset the AP-iface category to unknown */
 				IPACM_Iface::ipacmcfg->iface_table[ipa_if_num].if_cat=UNKNOWN_IF;
 				IPACM_Iface::ipacmcfg->DelNatIfaces(dev_name); // delete NAT-iface
-				delete this;
 				return;
 			}
 		}
 		break;
 
+	case IPA_LAN_DELETE_SELF:
+	{
+		ipacm_event_data_fid *data = (ipacm_event_data_fid *)param;
+		if(data->if_index == ipa_if_num)
+		{
+			IPACMDBG("Received IPA_LAN_DELETE_SELF event.\n");
+			IPACMDBG("ipa_WLAN (%s):ipa_index (%d) instance close \n", IPACM_Iface::ipacmcfg->iface_table[ipa_if_num].iface_name, ipa_if_num);
+			delete this;
+		}
+		break;
+	}
+
 	case IPA_ADDR_ADD_EVENT:
 		{
 			ipacm_event_data_addr *data = (ipacm_event_data_addr *)param;
 			ipa_interface_index = iface_ipa_index_query(data->if_index);
 
 			if ( (data->iptype == IPA_IP_v4 && data->ipv4_addr == 0) ||
-					 (data->iptype == IPA_IP_v6 && 
-						data->ipv6_addr[0] == 0 && data->ipv6_addr[1] == 0 && 
+					 (data->iptype == IPA_IP_v6 &&
+						data->ipv6_addr[0] == 0 && data->ipv6_addr[1] == 0 &&
 					  data->ipv6_addr[2] == 0 && data->ipv6_addr[3] == 0) )
 			{
 				IPACMDBG("Invalid address, ignore IPA_ADDR_ADD_EVENT event\n");
 				return;
 			}
-			
+
 			if (ipa_interface_index == ipa_if_num)
 			{
 				/* 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 != ip_type) && (ip_type != IPA_IP_MAX))
+				    || ((data->iptype==IPA_IP_v6) && (num_dft_rt_v6!=MAX_DEFAULT_v6_ROUTE_RULES)))
 				{
 				  IPACMDBG("Got IPA_ADDR_ADD_EVENT ip-family:%d, v6 num %d: \n",data->iptype,num_dft_rt_v6);
 					/* Post event to NAT */
@@ -208,7 +222,7 @@
 	case IPA_HANDLE_WAN_UP:
 	{
 		IPACMDBG("Received IPA_HANDLE_WAN_UP event\n");
-		
+
 		if(IPACM_Wan::backhaul_is_sta_mode == false)
 		{
 			if(ip_type == IPA_IP_v4 || ip_type == IPA_IP_MAX)
@@ -226,7 +240,7 @@
 
 	case IPA_HANDLE_WAN_UP_V6:
 		IPACMDBG("Received IPA_HANDLE_WAN_UP_V6 event\n");
-		
+
 		if(IPACM_Wan::backhaul_is_sta_mode == false)
 		{
 			if(ip_type == IPA_IP_v6 || ip_type == IPA_IP_MAX)
@@ -247,7 +261,7 @@
 		{
 			IPACM_Lan::handle_wan_down(IPACM_Wan::backhaul_is_sta_mode);
 		}
-		
+
 		break;
 
 	case IPA_HANDLE_WAN_DOWN_V6:
@@ -274,6 +288,8 @@
 			if (ipa_interface_index == ipa_if_num)
 			{
 				IPACMDBG("Received IPA_WLAN_CLIENT_DEL_EVENT\n");
+				/* support lan2lan ipa-HW feature*/
+				handle_lan2lan_msg_post(data->mac_addr, IPA_LAN_CLIENT_DISCONNECT);
 				handle_wlan_client_down_evt(data->mac_addr);
 			}
 		}
@@ -286,6 +302,8 @@
 			if (ipa_interface_index == ipa_if_num)
 			{
 				IPACMDBG("Received IPA_WLAN_CLIENT_POWER_SAVE_EVENT\n");
+				/* support lan2lan ipa-HW feature*/
+				handle_lan2lan_msg_post(data->mac_addr, IPA_LAN_CLIENT_POWER_SAVE);
 				handle_wlan_client_pwrsave(data->mac_addr);
 			}
 		}
@@ -298,15 +316,17 @@
 			if (ipa_interface_index == ipa_if_num)
 			{
 				IPACMDBG("Received IPA_WLAN_CLIENT_RECOVER_EVENT\n");
-			  wlan_index = get_wlan_client_index(data->mac_addr);
+				/* support lan2lan ipa-HW feature*/
+				handle_lan2lan_msg_post(data->mac_addr, IPA_LAN_CLIENT_POWER_RECOVER);
 
+				wlan_index = get_wlan_client_index(data->mac_addr);
 				if ((wlan_index != IPACM_INVALID_INDEX) &&
 						(get_client_memptr(wlan_client, wlan_index)->power_save_set == true))
 				{
 
 					IPACMDBG("change wlan client out of  power safe mode \n");
 					get_client_memptr(wlan_client, wlan_index)->power_save_set = false;
-				
+
 					/* First add route rules and then nat rules */
                     if(get_client_memptr(wlan_client, wlan_index)->ipv4_set == true) /* for ipv4 */
 				    {
@@ -319,7 +339,7 @@
 
 						IPACMDBG("Adding Nat Rules\n");
 						Nat_App->ResetPwrSaveIf(get_client_memptr(wlan_client, wlan_index)->v4_addr);
-				    }				
+				    }
 
 				    if(get_client_memptr(wlan_client, wlan_index)->ipv6_set != 0) /* for ipv6 */
 				    {
@@ -341,6 +361,9 @@
 				{
 					return;
 				}
+				/* support lan2lan ipa-hw feature */
+				handle_lan2lan_client_active(data, IPA_LAN_CLIENT_ACTIVE);
+
 				handle_wlan_client_route_rule(data->mac_addr, data->iptype);
 				if (data->iptype == IPA_IP_v4)
 				{
@@ -374,7 +397,7 @@
 
 #define WLAN_IFACE_INDEX_LEN 2
 
-	int res = IPACM_SUCCESS, len = 0, i;
+	int res = IPACM_SUCCESS, len = 0, i, evt_size;
 	char index[WLAN_IFACE_INDEX_LEN];
 	struct ipa_ioc_copy_hdr sCopyHeader;
 	struct ipa_ioc_add_hdr *pHeaderDescriptor = NULL;
@@ -401,110 +424,112 @@
 			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);
-								}
-	
-								for(i = 0; i < data->num_of_attribs; i++)
-								{
-									if(data->attribs[i].attrib_type == WLAN_HDR_ATTRIB_MAC_ADDR)
-									{
-										memcpy(get_client_memptr(wlan_client, num_wifi_client)->mac,
-												data->attribs[i].u.mac_addr,
-												sizeof(get_client_memptr(wlan_client, num_wifi_client)->mac));
+		evt_size = sizeof(ipacm_event_data_wlan_ex) + data->num_of_attribs * sizeof(struct ipa_wlan_hdr_attrib_val);
+		get_client_memptr(wlan_client, num_wifi_client)->p_hdr_info = (ipacm_event_data_wlan_ex*)malloc(evt_size);
+		memcpy(get_client_memptr(wlan_client, num_wifi_client)->p_hdr_info, data, evt_size);
 
-										/* copy client mac_addr to partial header */
-										memcpy(&pHeaderDescriptor->hdr[0].hdr[data->attribs[i].offset],
-													 get_client_memptr(wlan_client, num_wifi_client)->mac,
-													 IPA_MAC_ADDR_SIZE);
-									}
-									else if(data->attribs[i].attrib_type == WLAN_HDR_ATTRIB_STA_ID)
-									{
-										/* copy client id to header */
-										memcpy(&pHeaderDescriptor->hdr[0].hdr[data->attribs[i].offset],
-													&data->attribs[i].u.sta_id, sizeof(data->attribs[i].u.sta_id));
-									}
-									else
-									{
-										IPACMDBG("The attribute type is not expected!\n");
-									}
-								}
-												 
-								pHeaderDescriptor->commit = true;
-								pHeaderDescriptor->num_hdrs = 1;
-												 
-								memset(pHeaderDescriptor->hdr[0].name, 0,
-											 sizeof(pHeaderDescriptor->hdr[0].name));
-												 
-								sprintf(index, "%d", ipa_if_num);
-								strncpy(pHeaderDescriptor->hdr[0].name, index, sizeof(index));
-												 
-								strncat(pHeaderDescriptor->hdr[0].name,
-												IPA_WLAN_PARTIAL_HDR_NAME_v4,
-												sizeof(IPA_WLAN_PARTIAL_HDR_NAME_v4));
-												 
-								sprintf(index, "%d", header_name_count);
+		/* 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);
+				}
+
+				for(i = 0; i < data->num_of_attribs; i++)
+				{
+					if(data->attribs[i].attrib_type == WLAN_HDR_ATTRIB_MAC_ADDR)
+					{
+						memcpy(get_client_memptr(wlan_client, num_wifi_client)->mac,
+								data->attribs[i].u.mac_addr,
+								sizeof(get_client_memptr(wlan_client, num_wifi_client)->mac));
+
+						/* copy client mac_addr to partial header */
+						memcpy(&pHeaderDescriptor->hdr[0].hdr[data->attribs[i].offset],
+									 get_client_memptr(wlan_client, num_wifi_client)->mac,
+									 IPA_MAC_ADDR_SIZE);
+					}
+					else if(data->attribs[i].attrib_type == WLAN_HDR_ATTRIB_STA_ID)
+					{
+						/* copy client id to header */
+						memcpy(&pHeaderDescriptor->hdr[0].hdr[data->attribs[i].offset],
+									&data->attribs[i].u.sta_id, sizeof(data->attribs[i].u.sta_id));
+					}
+					else
+					{
+						IPACMDBG("The attribute type is not expected!\n");
+					}
+				}
+
+				pHeaderDescriptor->commit = true;
+				pHeaderDescriptor->num_hdrs = 1;
+
+				memset(pHeaderDescriptor->hdr[0].name, 0,
+							 sizeof(pHeaderDescriptor->hdr[0].name));
+
+				sprintf(index, "%d", ipa_if_num);
+				strncpy(pHeaderDescriptor->hdr[0].name, index, sizeof(index));
+
+				strncat(pHeaderDescriptor->hdr[0].name,
+								IPA_WLAN_PARTIAL_HDR_NAME_v4,
+								sizeof(IPA_WLAN_PARTIAL_HDR_NAME_v4));
+
+				sprintf(index, "%d", header_name_count);
 								strncat(pHeaderDescriptor->hdr[0].name, index, sizeof(index));
-												 
-								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;
-					 }
-						 
-								get_client_memptr(wlan_client, num_wifi_client)->hdr_hdl_v4 = pHeaderDescriptor->hdr[0].hdr_hdl;
-								IPACMDBG("client(%d) v4 full header name:%s header handle:(0x%x)\n",
-												 num_wifi_client,
-												 pHeaderDescriptor->hdr[0].name,
-												 get_client_memptr(wlan_client, num_wifi_client)->hdr_hdl_v4);
-									get_client_memptr(wlan_client, num_wifi_client)->ipv4_header_set=true;	
-							break;  
-				 }
-		}	
-		
-					 
+
+				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;
+				}
+
+				get_client_memptr(wlan_client, num_wifi_client)->hdr_hdl_v4 = pHeaderDescriptor->hdr[0].hdr_hdl;
+				IPACMDBG("client(%d) v4 full header name:%s header handle:(0x%x)\n",
+								 num_wifi_client,
+								 pHeaderDescriptor->hdr[0].name,
+								 get_client_memptr(wlan_client, num_wifi_client)->hdr_hdl_v4);
+				get_client_memptr(wlan_client, num_wifi_client)->ipv4_header_set=true;
+				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,
@@ -548,7 +573,7 @@
 					}
 					else if (data->attribs[i].attrib_type == WLAN_HDR_ATTRIB_STA_ID)
 					{
-											/* copy client id to header */
+						/* copy client id to header */
 						memcpy(&pHeaderDescriptor->hdr[0].hdr[data->attribs[i].offset],
 								&data->attribs[i].u.sta_id, sizeof(data->attribs[i].u.sta_id));
 					}
@@ -592,19 +617,19 @@
 								 num_wifi_client,
 								 pHeaderDescriptor->hdr[0].name,
 											 get_client_memptr(wlan_client, num_wifi_client)->hdr_hdl_v6);
-						 
-				get_client_memptr(wlan_client, num_wifi_client)->ipv6_header_set=true;			   
-				break;  	
+
+				get_client_memptr(wlan_client, num_wifi_client)->ipv6_header_set=true;
+				break;
 			}
-		}	
+		}
 	}
-	
+
 	/* initialize wifi client*/
 	get_client_memptr(wlan_client, num_wifi_client)->route_rule_set_v4 = false;
-        get_client_memptr(wlan_client, num_wifi_client)->route_rule_set_v6 = 0;	
+    get_client_memptr(wlan_client, num_wifi_client)->route_rule_set_v6 = 0;
 	get_client_memptr(wlan_client, num_wifi_client)->ipv4_set = false;
-        get_client_memptr(wlan_client, num_wifi_client)->ipv6_set = 0;	
-        get_client_memptr(wlan_client, num_wifi_client)->power_save_set=false;
+    get_client_memptr(wlan_client, num_wifi_client)->ipv6_set = 0;
+    get_client_memptr(wlan_client, num_wifi_client)->power_save_set=false;
 	num_wifi_client++;
 	header_name_count++; //keep increasing header_name_count
 	IPACM_Wlan::total_num_wifi_clients++;
@@ -680,22 +705,22 @@
 				(data->ipv6_addr[2] != 0) || (data->ipv6_addr[3] || 0)) /* check if all 0 not valid ipv6 address */
 		{
 		   IPACMDBG("ipv6 address: 0x%x:%x:%x:%x\n", data->ipv6_addr[0], data->ipv6_addr[1], data->ipv6_addr[2], data->ipv6_addr[3]);
-                   if(get_client_memptr(wlan_client, clnt_indx)->ipv6_set < IPV6_NUM_ADDR)		
+                   if(get_client_memptr(wlan_client, clnt_indx)->ipv6_set < IPV6_NUM_ADDR)
 		   {
-		   
+
 		       for(v6_num=0;v6_num < get_client_memptr(wlan_client, clnt_indx)->ipv6_set;v6_num++)
 	               {
-			      if( data->ipv6_addr[0] == get_client_memptr(wlan_client, clnt_indx)->v6_addr[v6_num][0] && 
+			      if( data->ipv6_addr[0] == get_client_memptr(wlan_client, clnt_indx)->v6_addr[v6_num][0] &&
 			           data->ipv6_addr[1] == get_client_memptr(wlan_client, clnt_indx)->v6_addr[v6_num][1] &&
-			  	        data->ipv6_addr[2]== get_client_memptr(wlan_client, clnt_indx)->v6_addr[v6_num][2] && 
+			  	        data->ipv6_addr[2]== get_client_memptr(wlan_client, clnt_indx)->v6_addr[v6_num][2] &&
 			  	         data->ipv6_addr[3] == get_client_memptr(wlan_client, clnt_indx)->v6_addr[v6_num][3])
 			      {
 			  	    IPACMDBG("Already see this ipv6 addr for client:%d\n", clnt_indx);
 			  	    return IPACM_FAILURE; /* not setup the RT rules*/
 			  		break;
-			      }  
-		       }	   
-		   
+			      }
+		       }
+
 		       /* not see this ipv6 before for wifi client*/
 			   get_client_memptr(wlan_client, clnt_indx)->v6_addr[get_client_memptr(wlan_client, clnt_indx)->ipv6_set][0] = data->ipv6_addr[0];
 			   get_client_memptr(wlan_client, clnt_indx)->v6_addr[get_client_memptr(wlan_client, clnt_indx)->ipv6_set][1] = data->ipv6_addr[1];
@@ -740,7 +765,7 @@
 		return IPACM_SUCCESS;
 	}
 
-	/* during power_save mode, even receive IP_ADDR_ADD, not setting RT rules*/ 
+	/* during power_save mode, even receive IP_ADDR_ADD, not setting RT rules*/
 	if (get_client_memptr(wlan_client, wlan_index)->power_save_set == true)
 	{
 		IPACMDBG("wlan client is in power safe mode \n");
@@ -760,7 +785,7 @@
 					 get_client_memptr(wlan_client, wlan_index)->route_rule_set_v6);
 	}
 
-	
+
 	/* Add default  Qos routing rules if not set yet */
 	if ((iptype == IPA_IP_v4
 			 && get_client_memptr(wlan_client, wlan_index)->route_rule_set_v4 == false
@@ -789,26 +814,26 @@
 
 		        if(iptype != tx_prop->tx[tx_index].ip)
 		        {
-		   	        IPACMDBG("Tx:%d, ip-type: %d conflict ip-type: %d no RT-rule added\n", 
-		   	  				    tx_index, tx_prop->tx[tx_index].ip,iptype);		
+		   	        IPACMDBG("Tx:%d, ip-type: %d conflict ip-type: %d no RT-rule added\n",
+		   	  				    tx_index, tx_prop->tx[tx_index].ip,iptype);
 		   	        continue;
-		        }	   
-			
+		        }
+
   	   	        rt_rule_entry = &rt_rule->rules[0];
 			rt_rule_entry->at_rear = 0;
 
 			if (iptype == IPA_IP_v4)
 			{
-		                IPACMDBG("client index(%d):ipv4 address: 0x%x\n", wlan_index, 
+		                IPACMDBG("client index(%d):ipv4 address: 0x%x\n", wlan_index,
 		  				        get_client_memptr(wlan_client, wlan_index)->v4_addr);
-          
+
                                 IPACMDBG("client(%d): v4 header handle:(0x%x)\n",
 		  				 wlan_index,
 		  				 get_client_memptr(wlan_client, wlan_index)->hdr_hdl_v4);
 				strncpy(rt_rule->rt_tbl_name,
 								IPACM_Iface::ipacmcfg->rt_tbl_lan_v4.name,
 								sizeof(rt_rule->rt_tbl_name));
-	   
+
 
 			        rt_rule_entry->rule.dst = tx_prop->tx[tx_index].dst_pipe;
 			        memcpy(&rt_rule_entry->rule.attrib,
@@ -825,28 +850,26 @@
   	          	            free(rt_rule);
   	          	            return IPACM_FAILURE;
 			        }
-			  
+
 			        /* copy ipv4 RT hdl */
 		                get_client_memptr(wlan_client, wlan_index)->wifi_rt_hdl[tx_index].wifi_rt_rule_hdl_v4 =
   	   	                rt_rule->rules[0].rt_rule_hdl;
-		                IPACMDBG("tx:%d, rt rule hdl=%x ip-type: %d\n", tx_index, 
+		                IPACMDBG("tx:%d, rt rule hdl=%x ip-type: %d\n", tx_index,
 		      	        get_client_memptr(wlan_client, wlan_index)->wifi_rt_hdl[tx_index].wifi_rt_rule_hdl_v4, iptype);
-		  
   	   	        }
   	   	        else
   	   	        {
-		   
 		            for(v6_num = get_client_memptr(wlan_client, wlan_index)->route_rule_set_v6;v6_num < get_client_memptr(wlan_client, wlan_index)->ipv6_set;v6_num++)
 			    {
                                 IPACMDBG("client(%d): v6 header handle:(0x%x)\n",
 		  	    			 wlan_index,
 		  	    			 get_client_memptr(wlan_client, wlan_index)->hdr_hdl_v6);
-	            
+
 		                /* v6 LAN_RT_TBL */
 			    	strncpy(rt_rule->rt_tbl_name,
 			    					IPACM_Iface::ipacmcfg->rt_tbl_v6.name,
 			    					sizeof(rt_rule->rt_tbl_name));
-					
+
 		                /* 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));
@@ -860,7 +883,7 @@
 				rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[1] = 0xFFFFFFFF;
 				rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[2] = 0xFFFFFFFF;
 				rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[3] = 0xFFFFFFFF;
-                    
+
    	                        if (false == m_routing.AddRoutingRule(rt_rule))
   	                        {
   	                	    IPACMERR("Routing rule addition failed!\n");
@@ -869,14 +892,14 @@
 			        }
 
 		                get_client_memptr(wlan_client, wlan_index)->wifi_rt_hdl[tx_index].wifi_rt_rule_hdl_v6[v6_num] = rt_rule->rules[0].rt_rule_hdl;
-		                IPACMDBG("tx:%d, rt rule hdl=%x ip-type: %d\n", tx_index, 
+		                IPACMDBG("tx:%d, rt rule hdl=%x ip-type: %d\n", tx_index,
 		            				 get_client_memptr(wlan_client, wlan_index)->wifi_rt_hdl[tx_index].wifi_rt_rule_hdl_v6[v6_num], iptype);
-		
+
 			        /*Copy same rule to v6 WAN RT TBL*/
   	                        strncpy(rt_rule->rt_tbl_name,
   	                 					IPACM_Iface::ipacmcfg->rt_tbl_wan_v6.name,
   	                 					sizeof(rt_rule->rt_tbl_name));
-   	                
+
                                 /* Downlink traffic from Wan iface, directly through IPA */
 				rt_rule_entry->rule.dst = tx_prop->tx[tx_index].dst_pipe;
 			        memcpy(&rt_rule_entry->rule.attrib,
@@ -892,7 +915,7 @@
 				rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[1] = 0xFFFFFFFF;
 				rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[2] = 0xFFFFFFFF;
 				rt_rule_entry->rule.attrib.u.v6.dst_addr_mask[3] = 0xFFFFFFFF;
-   	                
+
 		               if (false == m_routing.AddRoutingRule(rt_rule))
 		               {
 			           IPACMERR("Routing rule addition failed!\n");
@@ -902,11 +925,11 @@
 
 		                get_client_memptr(wlan_client, wlan_index)->wifi_rt_hdl[tx_index].wifi_rt_rule_hdl_v6_wan[v6_num] = rt_rule->rules[0].rt_rule_hdl;
 
-				IPACMDBG("tx:%d, rt rule hdl=%x ip-type: %d\n", tx_index, 
-		            				 get_client_memptr(wlan_client, wlan_index)->wifi_rt_hdl[tx_index].wifi_rt_rule_hdl_v6_wan[v6_num], iptype);			  
+				IPACMDBG("tx:%d, rt rule hdl=%x ip-type: %d\n", tx_index,
+		            				 get_client_memptr(wlan_client, wlan_index)->wifi_rt_hdl[tx_index].wifi_rt_rule_hdl_v6_wan[v6_num], iptype);
 			    }
 			}
-		   
+
   	        } /* end of for loop */
 
 		free(rt_rule);
@@ -945,7 +968,7 @@
 			IPACMDBG("Deleting Nat Rules\n");
 			Nat_App->UpdatePwrSaveIf(get_client_memptr(wlan_client, clt_indx)->v4_addr);
  	     }
- 	  
+
 		IPACMDBG("Deleting default qos Route Rules\n");
 		delete_default_qos_rtrules(clt_indx, IPA_IP_v4);
 		delete_default_qos_rtrules(clt_indx, IPA_IP_v6);
@@ -980,8 +1003,8 @@
 	{
 	        IPACMDBG("Deleting Nat Rules\n");
 	        Nat_App->UpdatePwrSaveIf(get_client_memptr(wlan_client, clt_indx)->v4_addr);
- 	}	
-	
+ 	}
+
 	if (delete_default_qos_rtrules(clt_indx, IPA_IP_v4))
 	{
 		IPACMERR("unbale to delete v4 default qos route rules\n");
@@ -1022,9 +1045,12 @@
 	get_client_memptr(wlan_client, clt_indx)->ipv6_header_set = false;
 	get_client_memptr(wlan_client, clt_indx)->route_rule_set_v4 = false;
 	get_client_memptr(wlan_client, clt_indx)->route_rule_set_v6 = 0;
+	free(get_client_memptr(wlan_client, clt_indx)->p_hdr_info);
 
 	for (; clt_indx < num_wifi_client_tmp - 1; clt_indx++)
 	{
+		get_client_memptr(wlan_client, clt_indx)->p_hdr_info = get_client_memptr(wlan_client, (clt_indx + 1))->p_hdr_info;
+
 		memcpy(get_client_memptr(wlan_client, clt_indx)->mac,
 					 get_client_memptr(wlan_client, (clt_indx + 1))->mac,
 					 sizeof(get_client_memptr(wlan_client, clt_indx)->mac));
@@ -1041,12 +1067,12 @@
 		get_client_memptr(wlan_client, clt_indx)->route_rule_set_v4 = get_client_memptr(wlan_client, (clt_indx + 1))->route_rule_set_v4;
 		get_client_memptr(wlan_client, clt_indx)->route_rule_set_v6 = get_client_memptr(wlan_client, (clt_indx + 1))->route_rule_set_v6;
 
-                for(num_v6=0;num_v6< get_client_memptr(wlan_client, clt_indx)->ipv6_set;num_v6++)	 
-	        {					
+                for(num_v6=0;num_v6< get_client_memptr(wlan_client, clt_indx)->ipv6_set;num_v6++)
+	        {
 		    get_client_memptr(wlan_client, clt_indx)->v6_addr[num_v6][0] = get_client_memptr(wlan_client, (clt_indx + 1))->v6_addr[num_v6][0];
 		    get_client_memptr(wlan_client, clt_indx)->v6_addr[num_v6][1] = get_client_memptr(wlan_client, (clt_indx + 1))->v6_addr[num_v6][1];
 		    get_client_memptr(wlan_client, clt_indx)->v6_addr[num_v6][2] = get_client_memptr(wlan_client, (clt_indx + 1))->v6_addr[num_v6][2];
-		    get_client_memptr(wlan_client, clt_indx)->v6_addr[num_v6][3] = get_client_memptr(wlan_client, (clt_indx + 1))->v6_addr[num_v6][3]; 
+		    get_client_memptr(wlan_client, clt_indx)->v6_addr[num_v6][3] = get_client_memptr(wlan_client, (clt_indx + 1))->v6_addr[num_v6][3];
                 }
 
 		for (tx_index = 0; tx_index < iface_query->num_tx_props; tx_index++)
@@ -1054,7 +1080,7 @@
 			get_client_memptr(wlan_client, clt_indx)->wifi_rt_hdl[tx_index].wifi_rt_rule_hdl_v4 =
 				 get_client_memptr(wlan_client, (clt_indx + 1))->wifi_rt_hdl[tx_index].wifi_rt_rule_hdl_v4;
 
-			for(num_v6=0;num_v6< get_client_memptr(wlan_client, clt_indx)->route_rule_set_v6;num_v6++)	 
+			for(num_v6=0;num_v6< get_client_memptr(wlan_client, clt_indx)->route_rule_set_v6;num_v6++)
 			{
 			  get_client_memptr(wlan_client, clt_indx)->wifi_rt_hdl[tx_index].wifi_rt_rule_hdl_v6[num_v6] =
 			   	 get_client_memptr(wlan_client, (clt_indx + 1))->wifi_rt_hdl[tx_index].wifi_rt_rule_hdl_v6[num_v6];
@@ -1108,6 +1134,16 @@
 			res = IPACM_FAILURE;
 			goto fail;
 		}
+
+		for(i=0; i<MAX_OFFLOAD_PAIR; i++)
+		{
+			if(m_filtering.DeleteFilteringHdls(&(lan2lan_flt_rule_hdl_v4[i].rule_hdl), IPA_IP_v4, 1) == false)
+			{
+				IPACMERR("Error deleting lan2lan IPv4 flt rules.\n");
+				res = IPACM_FAILURE;
+				goto fail;
+			}
+		}
 	}
 
 	/* Delete v4 filtering rules */
@@ -1118,11 +1154,20 @@
 		if (m_filtering.DeleteFilteringHdls(dft_v6fl_rule_hdl,
 																				IPA_IP_v6,
 																				(IPV6_DEFAULT_FILTERTING_RULES + IPV6_DEFAULT_LAN_FILTERTING_RULES)) == false)
-																				
+
 		{
 			res = IPACM_FAILURE;
 			goto fail;
 		}
+		for(i=0; i<MAX_OFFLOAD_PAIR; i++)
+		{
+			if(m_filtering.DeleteFilteringHdls(&(lan2lan_flt_rule_hdl_v6[i].rule_hdl), IPA_IP_v6, 1) == false)
+			{
+				IPACMERR("Error deleting lan2lan IPv4 flt rules.\n");
+				res = IPACM_FAILURE;
+				goto fail;
+			}
+		}
 	}
 	IPACMDBG("finished delte default filtering rules\n ");
 
@@ -1186,6 +1231,7 @@
 	/* clean wifi-client header, routing rules */
 	/* clean wifi client rule*/
 	IPACMDBG("left %d wifi clients need to be deleted \n ", num_wifi_client);
+
 	for (i = 0; i < num_wifi_client; i++)
 	{
 		delete_default_qos_rtrules(i, IPA_IP_v4);
@@ -1193,10 +1239,11 @@
 
 		IPACMDBG("Delete %d client header\n", num_wifi_client);
 
+		handle_lan2lan_msg_post(get_client_memptr(wlan_client, i)->mac, IPA_LAN_CLIENT_DISCONNECT);
 
         if(get_client_memptr(wlan_client, i)->ipv4_header_set == true)
         {
-		if (m_header.DeleteHeaderHdl(get_client_memptr(wlan_client, i)->hdr_hdl_v4)
+			if (m_header.DeleteHeaderHdl(get_client_memptr(wlan_client, i)->hdr_hdl_v4)
 				== false)
 			{
 				res = IPACM_FAILURE;
@@ -1206,22 +1253,26 @@
 
         if(get_client_memptr(wlan_client, i)->ipv6_header_set == true)
         {
-		if (m_header.DeleteHeaderHdl(get_client_memptr(wlan_client, i)->hdr_hdl_v6)
-				== false)
-		{
-			res = IPACM_FAILURE;
-			goto fail;
+			if (m_header.DeleteHeaderHdl(get_client_memptr(wlan_client, i)->hdr_hdl_v6)
+					== false)
+			{
+				res = IPACM_FAILURE;
+				goto fail;
+			}
 		}
-		}		
 	} /* end of for loop */
 
 	/* free the wlan clients cache */
 	IPACMDBG("Free wlan clients cache\n");
 
 fail:
-	/* Delete corresponding ipa_rm_resource_name of RX-endpoint after delete all IPV4V6 FT-rule */ 
+	/* Delete corresponding ipa_rm_resource_name of RX-endpoint after delete all IPV4V6 FT-rule */
 	IPACM_Iface::ipacmcfg->DelRmDepend(IPACM_Iface::ipacmcfg->ipa_client_rm_map_tbl[rx_prop->rx[0].src_pipe]);
 
+	for (i = 0; i < num_wifi_client; i++)
+	{
+		free(get_client_memptr(wlan_client, i)->p_hdr_info);
+	}
 	free(wlan_client);
 	if (tx_prop != NULL)
 	{
@@ -1236,5 +1287,352 @@
 		free(iface_query);
 	}
 
+	is_active = false;
+	post_del_self_evt();
+
+	return res;
+}
+
+/*handle lan2lan internal mesg posting*/
+int IPACM_Wlan::handle_lan2lan_msg_post(uint8_t *mac_addr, ipa_cm_event_id event)
+{
+	int client_index;
+	client_index = get_wlan_client_index(mac_addr);
+	if (client_index == IPACM_INVALID_INDEX)
+	{
+		IPACMDBG("wlan client not attached\n");
+		return IPACM_SUCCESS;
+	}
+
+	ipacm_event_lan_client* lan_client;
+	ipacm_cmd_q_data evt_data;
+	if(get_client_memptr(wlan_client, client_index)->ipv4_set == true) /* handle ipv4 case*/
+	{
+		if(ip_type != IPA_IP_v4 && ip_type != IPA_IP_MAX)
+		{
+			IPACMERR("Client has IPv4 addr but iface does not have IPv4 up.\n");
+			return IPACM_FAILURE;
+		}
+
+		lan_client = (ipacm_event_lan_client*)malloc(sizeof(ipacm_event_lan_client));
+		if(lan_client == NULL)
+		{
+			IPACMERR("Unable to allocate memory.\n");
+			return IPACM_FAILURE;
+		}
+		memset(lan_client, 0, sizeof(ipacm_event_lan_client));
+
+		lan_client->iptype = IPA_IP_v4;
+		lan_client->ipv4_addr = get_client_memptr(wlan_client, client_index)->v4_addr;
+		lan_client->p_iface = this;
+
+		memset(&evt_data, 0, sizeof(evt_data));
+		evt_data.event = event;
+		evt_data.evt_data = (void*)lan_client;
+
+		IPACMDBG("Posting event: %d\n",event);
+		IPACM_EvtDispatcher::PostEvt(&evt_data);
+	}
+
+	if(get_client_memptr(wlan_client, client_index)->ipv6_set > 0) /* handle v6 case: may be multiple v6 addr */
+	{
+		if(ip_type != IPA_IP_v6 && ip_type != IPA_IP_MAX)
+		{
+			IPACMERR("Client has IPv6 addr but iface does not have IPv6 up.\n");
+			return IPACM_FAILURE;
+		}
+		int i;
+
+		for(i=0; i<get_client_memptr(wlan_client, client_index)->ipv6_set; i++)
+		{
+			lan_client = (ipacm_event_lan_client*)malloc(sizeof(ipacm_event_lan_client));
+			if(lan_client == NULL)
+			{
+				IPACMERR("Unable to allocate memory.\n");
+				return IPACM_FAILURE;
+			}
+			memset(lan_client, 0, sizeof(ipacm_event_lan_client));
+
+			lan_client->iptype = IPA_IP_v6;
+			memcpy(lan_client->ipv6_addr, get_client_memptr(wlan_client, client_index)->v6_addr[i], 4*sizeof(uint32_t));
+			lan_client->p_iface = this;
+
+			memset(&evt_data, 0, sizeof(evt_data));
+			evt_data.event = event;
+			evt_data.evt_data = (void*)lan_client;
+
+			IPACMDBG("Posting event: %d\n",event);
+			IPACM_EvtDispatcher::PostEvt(&evt_data);
+		}
+	}
+	return IPACM_SUCCESS;
+}
+
+int IPACM_Wlan::add_lan2lan_hdr(ipa_ip_type iptype, uint8_t* src_mac, uint8_t* dst_mac, uint32_t* hdr_hdl)
+{
+	if(tx_prop == NULL)
+	{
+		IPACMERR("There is no tx_prop, cannot add header.\n");
+		return IPACM_FAILURE;
+	}
+	if(src_mac == NULL || dst_mac == NULL)
+	{
+		IPACMERR("Either src_mac or dst_mac is null, cannot add header.\n");
+		return IPACM_FAILURE;
+	}
+	if(hdr_hdl == NULL)
+	{
+		IPACMERR("Header handle is empty.\n");
+		return IPACM_FAILURE;
+	}
+
+	int i, j, k, len;
+	int res = IPACM_SUCCESS;
+	char index[4];
+	struct ipa_ioc_copy_hdr sCopyHeader;
+	struct ipa_ioc_add_hdr *pHeader;
+
+	IPACMDBG("Get lan2lan header request, src_mac: 0x%02x%02x%02x%02x%02x%02x dst_mac: 0x%02x%02x%02x%02x%02x%02x\n",
+			src_mac[0], src_mac[1], src_mac[2], src_mac[3], src_mac[4], src_mac[5], dst_mac[0], dst_mac[1],
+			dst_mac[2], dst_mac[3], dst_mac[4], dst_mac[5]);
+
+	len = sizeof(struct ipa_ioc_add_hdr) + sizeof(struct ipa_hdr_add);
+	pHeader = (struct ipa_ioc_add_hdr *)malloc(len);
+	if (pHeader == NULL)
+	{
+		IPACMERR("Failed to allocate header\n");
+		return IPACM_FAILURE;
+	}
+	memset(pHeader, 0, len);
+
+	if(iptype == IPA_IP_v4)
+	{		/* copy partial header for v4*/
+		for(i=0; i<tx_prop->num_tx_props; i++)
+		{
+			if(tx_prop->tx[i].ip == IPA_IP_v4)
+			{
+				IPACMDBG("Got v4-header name from %d tx props\n", i);
+				memset(&sCopyHeader, 0, sizeof(sCopyHeader));
+				memcpy(sCopyHeader.name, tx_prop->tx[i].hdr_name, sizeof(sCopyHeader.name));
+
+				IPACMDBG("Header name: %s\n", sCopyHeader.name);
+				if(m_header.CopyHeader(&sCopyHeader) == false)
+				{
+					IPACMERR("Copy header failed\n");
+					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(pHeader->hdr[0].hdr, sCopyHeader.hdr, sCopyHeader.hdr_len);
+				}
+
+				for(j=0; j<num_wifi_client; j++)	//Add src/dst mac to the header
+				{
+					if(memcmp(dst_mac, get_client_memptr(wlan_client, j)->mac, IPA_MAC_ADDR_SIZE) == 0)
+					{
+						break;
+					}
+				}
+				if(j == num_wifi_client)
+				{
+					IPACMERR("Not able to find the wifi client from mac addr.\n");
+					res = IPACM_FAILURE;
+					goto fail;
+				}
+				else
+				{
+					IPACMDBG("Find wifi client at position %d\n", j);
+					for(k = 0; k < get_client_memptr(wlan_client, j)->p_hdr_info->num_of_attribs; k++)
+					{
+						if(get_client_memptr(wlan_client, j)->p_hdr_info->attribs[k].attrib_type == WLAN_HDR_ATTRIB_MAC_ADDR)
+						{
+							memcpy(&pHeader->hdr[0].hdr[get_client_memptr(wlan_client, j)->p_hdr_info->attribs[k].offset],
+									dst_mac, IPA_MAC_ADDR_SIZE);
+							memcpy(&pHeader->hdr[0].hdr[get_client_memptr(wlan_client, j)->p_hdr_info->attribs[k].offset + IPA_MAC_ADDR_SIZE],
+									src_mac, IPA_MAC_ADDR_SIZE);
+						}
+						else if(get_client_memptr(wlan_client, j)->p_hdr_info->attribs[k].attrib_type == WLAN_HDR_ATTRIB_STA_ID)
+						{
+							memcpy(&pHeader->hdr[0].hdr[get_client_memptr(wlan_client, j)->p_hdr_info->attribs[k].offset],
+									&get_client_memptr(wlan_client, j)->p_hdr_info->attribs[k].u.sta_id,
+									sizeof(get_client_memptr(wlan_client, j)->p_hdr_info->attribs[k].u.sta_id));
+						}
+						else
+						{
+							IPACMDBG("The attribute type is not expected!\n");
+						}
+					}
+				}
+
+				pHeader->commit = true;
+				pHeader->num_hdrs = 1;
+
+				memset(pHeader->hdr[0].name, 0, sizeof(pHeader->hdr[0].name));
+				strncpy(pHeader->hdr[0].name, IPA_LAN_TO_LAN_WLAN_HDR_NAME_V4, sizeof(IPA_LAN_TO_LAN_WLAN_HDR_NAME_V4));
+
+				for(j=0; j<MAX_OFFLOAD_PAIR; j++)
+				{
+					if( lan2lan_hdr_hdl_v4[j].valid == false)
+					{
+						IPACMDBG("Construct lan2lan hdr with index %d.\n", j);
+						break;
+					}
+				}
+				if(j == MAX_OFFLOAD_PAIR)
+				{
+					IPACMERR("Failed to find an available hdr index.\n");
+					res = IPACM_FAILURE;
+					goto fail;
+				}
+				lan2lan_hdr_hdl_v4[j].valid = true;
+				sprintf(index, "%d", j);
+				strncat(pHeader->hdr[0].name, index, sizeof(index));
+
+				pHeader->hdr[0].hdr_len = sCopyHeader.hdr_len;
+				pHeader->hdr[0].is_partial = 0;
+				pHeader->hdr[0].hdr_hdl = -1;
+				pHeader->hdr[0].status = -1;
+
+				if (m_header.AddHeader(pHeader) == false || pHeader->hdr[0].status != 0)
+				{
+					IPACMERR("Ioctl IPA_IOC_ADD_HDR failed with status: %d\n", pHeader->hdr[0].status);
+					res = IPACM_FAILURE;
+					goto fail;
+				}
+				IPACMDBG("Installed v4 full header %s header handle 0x%08x\n", pHeader->hdr[0].name,
+							pHeader->hdr[0].hdr_hdl);
+				*hdr_hdl = pHeader->hdr[0].hdr_hdl;
+				lan2lan_hdr_hdl_v4[j].hdr_hdl = pHeader->hdr[0].hdr_hdl;
+				break;
+			}
+		}
+	}
+	else if(iptype == IPA_IP_v6)
+	{		/* copy partial header for v6*/
+		for(i=0; i<tx_prop->num_tx_props; i++)
+		{
+			if(tx_prop->tx[i].ip == IPA_IP_v6)
+			{
+				IPACMDBG("Got v6-header name from %d tx props\n", i);
+				memset(&sCopyHeader, 0, sizeof(sCopyHeader));
+				memcpy(sCopyHeader.name, tx_prop->tx[i].hdr_name, sizeof(sCopyHeader.name));
+
+				IPACMDBG("Header name: %s\n", sCopyHeader.name);
+				if(m_header.CopyHeader(&sCopyHeader) == false)
+				{
+					IPACMERR("Copy header failed\n");
+					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(pHeader->hdr[0].hdr, sCopyHeader.hdr, sCopyHeader.hdr_len);
+				}
+
+				for(j=0; j<num_wifi_client; j++)	//Add src/dst mac to the header
+				{
+					if(memcmp(dst_mac, get_client_memptr(wlan_client, j)->mac, IPA_MAC_ADDR_SIZE) == 0)
+					{
+						break;
+					}
+				}
+				if(j == num_wifi_client)
+				{
+					IPACMERR("Not able to find the wifi client from mac addr.\n");
+					res = IPACM_FAILURE;
+					goto fail;
+				}
+				else
+				{
+					IPACMDBG("Find wifi client at position %d\n", j);
+					for(k = 0; k < get_client_memptr(wlan_client, j)->p_hdr_info->num_of_attribs; k++)
+					{
+						if(get_client_memptr(wlan_client, j)->p_hdr_info->attribs[k].attrib_type == WLAN_HDR_ATTRIB_MAC_ADDR)
+						{
+							memcpy(&pHeader->hdr[0].hdr[get_client_memptr(wlan_client, j)->p_hdr_info->attribs[k].offset],
+									dst_mac, IPA_MAC_ADDR_SIZE);
+							memcpy(&pHeader->hdr[0].hdr[get_client_memptr(wlan_client, j)->p_hdr_info->attribs[k].offset + IPA_MAC_ADDR_SIZE],
+									src_mac, IPA_MAC_ADDR_SIZE);
+						}
+						else if(get_client_memptr(wlan_client, j)->p_hdr_info->attribs[k].attrib_type == WLAN_HDR_ATTRIB_STA_ID)
+						{
+							memcpy(&pHeader->hdr[0].hdr[get_client_memptr(wlan_client, j)->p_hdr_info->attribs[k].offset],
+									&get_client_memptr(wlan_client, j)->p_hdr_info->attribs[k].u.sta_id,
+									sizeof(get_client_memptr(wlan_client, j)->p_hdr_info->attribs[k].u.sta_id));
+						}
+						else
+						{
+							IPACMDBG("The attribute type is not expected!\n");
+						}
+					}
+				}
+
+				pHeader->commit = true;
+				pHeader->num_hdrs = 1;
+
+				memset(pHeader->hdr[0].name, 0, sizeof(pHeader->hdr[0].name));
+				strncpy(pHeader->hdr[0].name, IPA_LAN_TO_LAN_WLAN_HDR_NAME_V6, sizeof(IPA_LAN_TO_LAN_WLAN_HDR_NAME_V6));
+
+				for(j=0; j<MAX_OFFLOAD_PAIR; j++)
+				{
+					if( lan2lan_hdr_hdl_v6[j].valid == false)
+					{
+						IPACMDBG("Construct lan2lan hdr with index %d.\n", j);
+						break;
+					}
+				}
+				if(j == MAX_OFFLOAD_PAIR)
+				{
+					IPACMERR("Failed to find an available hdr index.\n");
+					res = IPACM_FAILURE;
+					goto fail;
+				}
+				lan2lan_hdr_hdl_v6[j].valid = true;
+				sprintf(index, "%d", j);
+				strncat(pHeader->hdr[0].name, index, sizeof(index));
+
+				pHeader->hdr[0].hdr_len = sCopyHeader.hdr_len;
+				pHeader->hdr[0].is_partial = 0;
+				pHeader->hdr[0].hdr_hdl = -1;
+				pHeader->hdr[0].status = -1;
+
+				if (m_header.AddHeader(pHeader) == false || pHeader->hdr[0].status != 0)
+				{
+					IPACMERR("Ioctl IPA_IOC_ADD_HDR failed with status: %d\n", pHeader->hdr[0].status);
+					res = IPACM_FAILURE;
+					goto fail;
+				}
+				IPACMDBG("Installed v6 full header %s header handle 0x%08x\n", pHeader->hdr[0].name,
+							pHeader->hdr[0].hdr_hdl);
+				*hdr_hdl = pHeader->hdr[0].hdr_hdl;
+				lan2lan_hdr_hdl_v6[j].hdr_hdl = pHeader->hdr[0].hdr_hdl;
+				break;
+			}
+		}
+	}
+	else
+	{
+		IPACMERR("IP type is not expected.\n");
+	}
+
+fail:
+	free(pHeader);
 	return res;
 }
diff --git a/ipacm/src/IPACM_cfg.xml b/ipacm/src/IPACM_cfg.xml
index dcb4c92..5105097 100644
--- a/ipacm/src/IPACM_cfg.xml
+++ b/ipacm/src/IPACM_cfg.xml
@@ -71,4 +71,4 @@
  	        <MaxNatEntries>100</MaxNatEntries>
 		</IPACMNAT>
 		</IPACM>
-</system>	
+</system>
\ No newline at end of file
diff --git a/ipacm/src/Makefile.am b/ipacm/src/Makefile.am
index d395a7f..82cc3dc 100644
--- a/ipacm/src/Makefile.am
+++ b/ipacm/src/Makefile.am
@@ -22,15 +22,18 @@
 		IPACM_IfaceManager.cpp \
 		IPACM_Neighbor.cpp \
 		IPACM_Netlink.cpp \
-		IPACM_Xml.cpp		
-		
+		IPACM_Xml.cpp \
+		IPACM_LanToLan.cpp
+
 bin_PROGRAMS  =  ipacm
 
 requiredlibs =  ${LIBXML_LIB} -lxml2 -lpthread -lnetfilter_conntrack -lnfnetlink\
                ../../ipanat/src/libipanat.la
-			   
+
 ipacm_LDADD =  $(requiredlibs)
-			 
+
+AM_CPPFLAGS += "-std=c++0x"
+
 LOCAL_MODULE := libipanat
 LOCAL_PRELINK_MODULE := false
 include $(BUILD_SHARED_LIBRARY)