Promotion of data.lnx.3.0-00087.

CRs      Change ID                                   Subject
--------------------------------------------------------------------------------------------------------------
2069634   Ic01e10eaf35195da8b55e25f779a13669d0d1dae   IPACM: fix L2TP issues
2066430   I296aee0fe857a7db4fc29f87c833224d7c0ffd9d   IPACM: add support for L2TP
2064317   Ifbeacfb9bc5148d399dace97dabfe63d2939b066   IPACM: add filtering rule for unique local address

Change-Id: I1ee00dd218eae7d742d69ba5cd25ef825c0fd07c
CRs-Fixed: 2066430, 2064317, 2069634
diff --git a/ipacm/inc/IPACM_Defs.h b/ipacm/inc/IPACM_Defs.h
index 74ed3bf..31d8543 100644
--- a/ipacm/inc/IPACM_Defs.h
+++ b/ipacm/inc/IPACM_Defs.h
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
@@ -187,6 +187,12 @@
 	IPA_LAN_DELETE_SELF,                      /* ipacm_event_data_fid */
 	IPA_DOWNSTREAM_ADD,                       /* ipacm_event_ipahal_stream */
 	IPA_DOWNSTREAM_DEL,                       /* ipacm_event_ipahal_stream */
+	IPA_ADD_VLAN_IFACE,                       /* ipa_ioc_vlan_iface_info */
+	IPA_DEL_VLAN_IFACE,                       /* ipa_ioc_vlan_iface_info */
+	IPA_ADD_L2TP_VLAN_MAPPING,                /* ipa_ioc_l2tp_vlan_mapping_info */
+	IPA_DEL_L2TP_VLAN_MAPPING,                /* ipa_ioc_l2tp_vlan_mapping_info */
+	IPA_HANDLE_VLAN_CLIENT_INFO,              /* ipacm_event_data_all */
+	IPA_HANDLE_VLAN_IFACE_INFO,               /* ipacm_event_data_all */
 	IPACM_EVENT_MAX
 } ipa_cm_event_id;
 
@@ -249,6 +255,7 @@
 	uint32_t  ipv4_addr;
 	uint32_t  ipv6_addr[4];
 	uint8_t mac_addr[IPA_MAC_ADDR_SIZE];
+	char iface_name[IPA_IFACE_NAME_LEN];
 } ipacm_event_data_all;
 
 class IPACM_Lan;
@@ -263,6 +270,7 @@
 	IPACM_Lan *p_iface;
 	ipa_ip_type iptype;
 	uint8_t mac_addr[6];
+	char iface_name[IPA_IFACE_NAME_LEN];
 } ipacm_event_eth_bridge;
 
 typedef struct
@@ -299,6 +307,7 @@
 typedef struct _ipacm_event_data_addr
 {
 	enum ipa_ip_type iptype;
+	char iface_name[IPA_IFACE_NAME_LEN];
 	int if_index;
 	uint32_t  ipv4_addr_gw;
 	uint32_t  ipv4_addr;
diff --git a/ipacm/inc/IPACM_Iface.h b/ipacm/inc/IPACM_Iface.h
index 43b0da6..0469699 100644
--- a/ipacm/inc/IPACM_Iface.h
+++ b/ipacm/inc/IPACM_Iface.h
@@ -59,9 +59,9 @@
 #define IPV4_DEFAULT_FILTERTING_RULES 3
 
 #ifdef FEATURE_IPA_ANDROID
-#define IPV6_DEFAULT_FILTERTING_RULES 6
+#define IPV6_DEFAULT_FILTERTING_RULES 7
 #else
-#define IPV6_DEFAULT_FILTERTING_RULES 3
+#define IPV6_DEFAULT_FILTERTING_RULES 4
 #endif
 
 #define IPV6_DEFAULT_LAN_FILTERTING_RULES 1
diff --git a/ipacm/inc/IPACM_Lan.h b/ipacm/inc/IPACM_Lan.h
index 2e5f21b..280c830 100644
--- a/ipacm/inc/IPACM_Lan.h
+++ b/ipacm/inc/IPACM_Lan.h
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
@@ -169,7 +169,35 @@
 	/* delete header processing context */
 	int eth_bridge_del_hdr_proc_ctx(uint32_t hdr_proc_ctx_hdl);
 
+	/* add l2tp rt rule for l2tp client */
+	int add_l2tp_rt_rule(ipa_ip_type iptype, uint8_t *dst_mac, ipa_hdr_l2_type peer_l2_hdr_type,
+		uint32_t l2tp_session_id, uint32_t vlan_id, uint8_t *vlan_client_mac, uint32_t *vlan_iface_ipv6_addr,
+		uint32_t *vlan_client_ipv6_addr, uint32_t *first_pass_hdr_hdl, uint32_t *first_pass_hdr_proc_ctx_hdl,
+		uint32_t *second_pass_hdr_hdl, int *num_rt_hdl, uint32_t *first_pass_rt_rule_hdl, uint32_t *second_pass_rt_rule_hdl);
 
+	/* delete l2tp rt rule for l2tp client */
+	int del_l2tp_rt_rule(ipa_ip_type iptype, uint32_t first_pass_hdr_hdl, uint32_t first_pass_hdr_proc_ctx_hdl,
+		uint32_t second_pass_hdr_hdl, int num_rt_hdl, uint32_t *first_pass_rt_rule_hdl, uint32_t *second_pass_rt_rule_hdl);
+
+	/* add l2tp rt rule for non l2tp client */
+	int add_l2tp_rt_rule(ipa_ip_type iptype, uint8_t *dst_mac, uint32_t *hdr_proc_ctx_hdl,
+		int *num_rt_hdl, uint32_t *rt_rule_hdl);
+
+	/* delete l2tp rt rule for non l2tp client */
+	int del_l2tp_rt_rule(ipa_ip_type iptype, int num_rt_hdl, uint32_t *rt_rule_hdl);
+
+	/* add l2tp flt rule on l2tp interface */
+	int add_l2tp_flt_rule(uint8_t *dst_mac, uint32_t *flt_rule_hdl);
+
+	/* delete l2tp flt rule on l2tp interface */
+	int del_l2tp_flt_rule(uint32_t flt_rule_hdl);
+
+	/* add l2tp flt rule on non l2tp interface */
+	int add_l2tp_flt_rule(ipa_ip_type iptype, uint8_t *dst_mac, uint32_t *vlan_client_ipv6_addr,
+		uint32_t *first_pass_flt_rule_hdl, uint32_t *second_pass_flt_rule_hdl);
+
+	/* delete l2tp flt rule on non l2tp interface */
+	int del_l2tp_flt_rule(ipa_ip_type iptype, uint32_t first_pass_flt_rule_hdl, uint32_t second_pass_flt_rule_hdl);
 
 protected:
 
@@ -178,9 +206,17 @@
 	uint32_t eth_bridge_flt_rule_offset[IPA_IP_MAX];
 
 	/* mac address has to be provided for client related events */
-	void eth_bridge_post_event(ipa_cm_event_id evt, ipa_ip_type iptype, uint8_t *mac);
+	void eth_bridge_post_event(ipa_cm_event_id evt, ipa_ip_type iptype, uint8_t *mac,
+		uint32_t *ipv6_addr, char *iface_name);
 
+	/* check if the event is associated with vlan interface */
+	bool is_vlan_event(char *event_iface_name);
 
+	/* check if the event is associated with l2tp interface */
+	bool is_l2tp_event(char *event_iface_name);
+
+	/* check if the IPv6 address is unique local address */
+	bool is_unique_local_ipv6_addr(uint32_t *ipv6_addr);
 
 	virtual int add_dummy_private_subnet_flt_rule(ipa_ip_type iptype);
 
diff --git a/ipacm/inc/IPACM_LanToLan.h b/ipacm/inc/IPACM_LanToLan.h
index a28631e..b055cdd 100644
--- a/ipacm/inc/IPACM_LanToLan.h
+++ b/ipacm/inc/IPACM_LanToLan.h
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2014, The Linux Foundation. All rights reserved.
+Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
@@ -53,23 +53,63 @@
 #define MAX_NUM_IFACE 10
 #define MAX_NUM_CLIENT 16
 
+struct vlan_iface_info
+{
+	char vlan_iface_name[IPA_RESOURCE_NAME_MAX];
+	uint8_t vlan_id;
+	uint32_t vlan_iface_ipv6_addr[4];
+	uint8_t vlan_client_mac[6];
+	uint32_t vlan_client_ipv6_addr[4];
+};
+
+struct l2tp_vlan_mapping_info
+{
+	/* the following are l2tp iface info (name, session id) */
+	char l2tp_iface_name[IPA_RESOURCE_NAME_MAX];
+	uint8_t l2tp_session_id;
+	/* the following are mdm vlan iface info (name, vlan id, ipv6 addr) */
+	char vlan_iface_name[IPA_RESOURCE_NAME_MAX];
+	uint8_t vlan_id;
+	uint32_t vlan_iface_ipv6_addr[4];
+	/* the following are MIB3 vlan client info (mac, ipv6 addr) */
+	uint8_t vlan_client_mac[6];
+	uint32_t vlan_client_ipv6_addr[4];
+	/* the following is MIB3 l2tp client info (mac) */
+	uint8_t l2tp_client_mac[6];
+};
+
 struct rt_rule_info
 {
 	int num_hdl[IPA_IP_MAX];	/* one client may need more than one routing rules on the same routing table depending on tx_prop */
 	uint32_t  rule_hdl[IPA_IP_MAX][MAX_NUM_PROP];
 };
 
+struct l2tp_rt_rule_info
+{
+	uint32_t first_pass_hdr_hdl;	/* first pass hdr template (IPv4 and IPv6 use the same hdr template) */
+	uint32_t first_pass_hdr_proc_ctx_hdl[IPA_IP_MAX]; /* first pass hdr proc ctx */
+	uint32_t second_pass_hdr_hdl;	/* second pass hdr template (IPv4 and IPv6 use the same hdr template) */
+	int num_rt_hdl[IPA_IP_MAX];		/* number of TX properties for IPv4 and IPv6 respectively */
+	uint32_t  first_pass_rt_rule_hdl[IPA_IP_MAX][MAX_NUM_PROP];	/* first pass routing rule */
+	uint32_t  second_pass_rt_rule_hdl[MAX_NUM_PROP];	/*second pass routing rule (only ipv6 rt rule is needed) */
+};
+
 struct client_info
 {
 	uint8_t mac_addr[6];
 	rt_rule_info inter_iface_rt_rule_hdl[IPA_HDR_L2_MAX];	/* routing rule handles of inter interface communication based on source l2 header type */
 	rt_rule_info intra_iface_rt_rule_hdl;	/* routing rule handles of inter interface communication */
+	bool is_l2tp_client;
+	l2tp_vlan_mapping_info *mapping_info;
+	l2tp_rt_rule_info l2tp_rt_rule_hdl[IPA_HDR_L2_MAX];
 };
 
 struct flt_rule_info
 {
 	client_info *p_client;
 	uint32_t flt_rule_hdl[IPA_IP_MAX];
+	uint32_t l2tp_first_pass_flt_rule_hdl[IPA_IP_MAX];	/* L2TP filtering rules are destination MAC based */
+	uint32_t l2tp_second_pass_flt_rule_hdl;
 };
 
 struct peer_iface_info
@@ -101,7 +141,7 @@
 	void handle_new_iface_up(char rt_tbl_name_for_flt[][IPA_RESOURCE_NAME_MAX], char rt_tbl_name_for_rt[][IPA_RESOURCE_NAME_MAX],
 		IPACM_LanToLan_Iface *peer_iface);
 
-	void handle_client_add(uint8_t *mac);
+	void handle_client_add(uint8_t *mac, bool is_l2tp_client, l2tp_vlan_mapping_info *mapping_info);
 
 	void handle_client_del(uint8_t *mac);
 
@@ -121,16 +161,28 @@
 
 	void decrement_ref_cnt_peer_l2_hdr_type(ipa_hdr_l2_type peer_l2_type);
 
+	void switch_to_l2tp_iface();
+
+	bool set_l2tp_iface(char *vlan_iface_name);
+
+	bool is_l2tp_iface();
+
+	void handle_l2tp_enable();
+
+	void handle_l2tp_disable();
+
 private:
 
 	IPACM_Lan *m_p_iface;
 	bool m_is_ip_addr_assigned[IPA_IP_MAX];
 	bool m_support_inter_iface_offload;
 	bool m_support_intra_iface_offload;
+	bool m_is_l2tp_iface;
 
 	int ref_cnt_peer_l2_hdr_type[IPA_HDR_L2_MAX];	/* reference count of l2 header type of peer interfaces */
 	uint32_t hdr_proc_ctx_for_inter_interface[IPA_HDR_L2_MAX];
 	uint32_t hdr_proc_ctx_for_intra_interface;
+	uint32_t hdr_proc_ctx_for_l2tp;		/* uc needs to remove 62 bytes IPv6 + L2TP + inner Ethernet header */
 
 	list<client_info> m_client_info;	/* client list */
 	list<peer_iface_info> m_peer_iface_info;	/* peer information list */
@@ -150,6 +202,8 @@
 
 	void del_client_rt_rule(peer_iface_info *peer, client_info *client);
 
+	void add_l2tp_client_rt_rule(peer_iface_info *peer, client_info *client);
+
 	void clear_all_flt_rule_for_one_peer_iface(peer_iface_info *peer);
 
 	void clear_all_rt_rule_for_one_peer_iface(peer_iface_info *peer);
@@ -167,16 +221,26 @@
 
 public:
 
-	IPACM_LanToLan();
+	static IPACM_LanToLan* p_instance;
+	static IPACM_LanToLan* get_instance();
+	bool has_l2tp_iface();
 
 private:
 
+	IPACM_LanToLan();
+
 	~IPACM_LanToLan();
 
+	bool m_has_l2tp_iface;
+
 	list<class IPACM_LanToLan_Iface> m_iface;
 
 	list<ipacm_event_eth_bridge> m_cached_client_add_event;
 
+	list<vlan_iface_info> m_vlan_iface;
+
+	list<l2tp_vlan_mapping_info> m_l2tp_vlan_mapping;
+
 	void handle_iface_up(ipacm_event_eth_bridge *data);
 
 	void handle_iface_down(ipacm_event_eth_bridge *data);
@@ -187,6 +251,18 @@
 
 	void handle_wlan_scc_mcc_switch(ipacm_event_eth_bridge *data);
 
+	void handle_add_vlan_iface(ipa_ioc_vlan_iface_info *data);
+
+	void handle_del_vlan_iface(ipa_ioc_vlan_iface_info *data);
+
+	void handle_add_l2tp_vlan_mapping(ipa_ioc_l2tp_vlan_mapping_info *data);
+
+	void handle_del_l2tp_vlan_mapping(ipa_ioc_l2tp_vlan_mapping_info *data);
+
+	void handle_vlan_client_info(ipacm_event_data_all *data);
+
+	void handle_vlan_iface_info(ipacm_event_data_all *data);
+
 	void handle_new_iface_up(IPACM_LanToLan_Iface *new_iface, IPACM_LanToLan_Iface *exist_iface);
 
 	void event_callback(ipa_cm_event_id event, void* param);
diff --git a/ipacm/inc/IPACM_Neighbor.h b/ipacm/inc/IPACM_Neighbor.h
index 14e86e5..745b145 100644
--- a/ipacm/inc/IPACM_Neighbor.h
+++ b/ipacm/inc/IPACM_Neighbor.h
@@ -1,5 +1,5 @@
-/* 
-Copyright (c) 2013, The Linux Foundation. All rights reserved.
+/*
+Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
@@ -56,6 +56,8 @@
 	int iface_index;
 	uint32_t v4_addr;
 	int ipa_if_num;
+	/* add support for handling L2TP clients which associated with eth0 vlan interface */
+	char iface_name[IPA_IFACE_NAME_LEN];
 };
 
 class IPACM_Neighbor : public IPACM_Listener
diff --git a/ipacm/src/IPACM_Config.cpp b/ipacm/src/IPACM_Config.cpp
index a355883..52cc985 100644
--- a/ipacm/src/IPACM_Config.cpp
+++ b/ipacm/src/IPACM_Config.cpp
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
@@ -105,6 +105,12 @@
 	__stringify(IPA_ETH_BRIDGE_CLIENT_DEL),                /* ipacm_event_eth_bridge*/
 	__stringify(IPA_ETH_BRIDGE_WLAN_SCC_MCC_SWITCH),       /* ipacm_event_eth_bridge*/
 	__stringify(IPA_LAN_DELETE_SELF),                      /* ipacm_event_data_fid */
+	__stringify(IPA_ADD_VLAN_IFACE),                       /* ipa_ioc_vlan_iface_info */
+	__stringify(IPA_DEL_VLAN_IFACE),                       /* ipa_ioc_vlan_iface_info */
+	__stringify(IPA_ADD_L2TP_VLAN_MAPPING),                /* ipa_ioc_l2tp_vlan_mapping_info */
+	__stringify(IPA_DEL_L2TP_VLAN_MAPPING),                /* ipa_ioc_l2tp_vlan_mapping_info */
+	__stringify(IPA_VLAN_CLIENT_INFO),                     /* ipacm_event_data_all */
+	__stringify(IPA_VLAN_IFACE_INFO),                      /* ipacm_event_data_all */
 	__stringify(IPACM_EVENT_MAX),
 };
 
diff --git a/ipacm/src/IPACM_Iface.cpp b/ipacm/src/IPACM_Iface.cpp
index 8c37d80..717f6aa 100644
--- a/ipacm/src/IPACM_Iface.cpp
+++ b/ipacm/src/IPACM_Iface.cpp
@@ -450,12 +450,15 @@
 						freeifaddrs(myaddrs);
 						return ;
 					}
+					memset(data_addr, 0, sizeof(ipacm_event_data_addr));
 					data_addr->iptype = IPA_IP_v4;
 					data_addr->if_index = interface_index;
 					data_addr->ipv4_addr = 	iface_ipv4.s_addr;
 					data_addr->ipv4_addr = ntohl(data_addr->ipv4_addr);
-					IPACMDBG_H("Posting IPA_ADDR_ADD_EVENT with if index:%d, ipv4 addr:0x%x\n",
+					strlcpy(data_addr->iface_name, ifr.ifr_name, sizeof(data_addr->iface_name));
+					IPACMDBG_H("Posting IPA_ADDR_ADD_EVENT with if index:%d, if name:%s, ipv4 addr:0x%x\n",
 						data_addr->if_index,
+						data_addr->iface_name,
 						data_addr->ipv4_addr);
 
 					evt_data.event = IPA_ADDR_ADD_EVENT;
@@ -475,6 +478,7 @@
 						freeifaddrs(myaddrs);
 						return ;
 					}
+					memset(data_addr, 0, sizeof(ipacm_event_data_addr));
 					data_addr->iptype = IPA_IP_v6;
 					data_addr->if_index = interface_index;
 					memcpy(data_addr->ipv6_addr,
@@ -484,8 +488,10 @@
 					data_addr->ipv6_addr[1] = ntohl(data_addr->ipv6_addr[1]);
 					data_addr->ipv6_addr[2] = ntohl(data_addr->ipv6_addr[2]);
 					data_addr->ipv6_addr[3] = ntohl(data_addr->ipv6_addr[3]);
-					IPACMDBG_H("Posting IPA_ADDR_ADD_EVENT with if index:%d, ipv6 addr:0x%x:%x:%x:%x\n",
+					strlcpy(data_addr->iface_name, ifr.ifr_name, sizeof(data_addr->iface_name));
+					IPACMDBG_H("Posting IPA_ADDR_ADD_EVENT with if index:%d, if name:%s, ipv6 addr:0x%x:%x:%x:%x\n",
 							data_addr->if_index,
+							data_addr->iface_name,
 							data_addr->ipv6_addr[0], data_addr->ipv6_addr[1], data_addr->ipv6_addr[2], data_addr->ipv6_addr[3]);
 
 					evt_data.event = IPA_ADDR_ADD_EVENT;
@@ -837,6 +843,21 @@
 #endif
 		memcpy(&(m_pFilteringTable->rules[2]), &flt_rule_entry, sizeof(struct ipa_flt_rule_add));
 
+		/* Configuring fd00::/8 Unique Local Ipv6 Address */
+		flt_rule_entry.rule.attrib.u.v6.dst_addr_mask[0] = 0xFF000000;
+		flt_rule_entry.rule.attrib.u.v6.dst_addr_mask[1] = 0x00000000;
+		flt_rule_entry.rule.attrib.u.v6.dst_addr_mask[2] = 0x00000000;
+		flt_rule_entry.rule.attrib.u.v6.dst_addr_mask[3] = 0x00000000;
+		flt_rule_entry.rule.attrib.u.v6.dst_addr[0] = 0xFD000000;
+		flt_rule_entry.rule.attrib.u.v6.dst_addr[1] = 0x00000000;
+		flt_rule_entry.rule.attrib.u.v6.dst_addr[2] = 0x00000000;
+		flt_rule_entry.rule.attrib.u.v6.dst_addr[3] = 0X00000000;
+#ifdef FEATURE_IPA_V3
+		flt_rule_entry.at_rear = true;
+		flt_rule_entry.rule.hashable = true;
+#endif
+		memcpy(&(m_pFilteringTable->rules[3]), &flt_rule_entry, sizeof(struct ipa_flt_rule_add));
+
 #ifdef FEATURE_IPA_ANDROID
 		IPACMDBG_H("Add TCP ctrl rules: total num %d\n", IPV6_DEFAULT_FILTERTING_RULES);
 		memset(&flt_rule_entry, 0, sizeof(struct ipa_flt_rule_add));
@@ -871,17 +892,17 @@
 		/* add TCP FIN rule*/
 		flt_rule_entry.rule.eq_attrib.ihl_offset_meq_32[0].value = (((uint32_t)1)<<TCP_FIN_SHIFT);
 		flt_rule_entry.rule.eq_attrib.ihl_offset_meq_32[0].mask = (((uint32_t)1)<<TCP_FIN_SHIFT);
-		memcpy(&(m_pFilteringTable->rules[3]), &flt_rule_entry, sizeof(struct ipa_flt_rule_add));
+		memcpy(&(m_pFilteringTable->rules[4]), &flt_rule_entry, sizeof(struct ipa_flt_rule_add));
 
 		/* add TCP SYN rule*/
 		flt_rule_entry.rule.eq_attrib.ihl_offset_meq_32[0].value = (((uint32_t)1)<<TCP_SYN_SHIFT);
 		flt_rule_entry.rule.eq_attrib.ihl_offset_meq_32[0].mask = (((uint32_t)1)<<TCP_SYN_SHIFT);
-		memcpy(&(m_pFilteringTable->rules[4]), &flt_rule_entry, sizeof(struct ipa_flt_rule_add));
+		memcpy(&(m_pFilteringTable->rules[5]), &flt_rule_entry, sizeof(struct ipa_flt_rule_add));
 
 		/* add TCP RST rule*/
 		flt_rule_entry.rule.eq_attrib.ihl_offset_meq_32[0].value = (((uint32_t)1)<<TCP_RST_SHIFT);
 		flt_rule_entry.rule.eq_attrib.ihl_offset_meq_32[0].mask = (((uint32_t)1)<<TCP_RST_SHIFT);
-		memcpy(&(m_pFilteringTable->rules[5]), &flt_rule_entry, sizeof(struct ipa_flt_rule_add));
+		memcpy(&(m_pFilteringTable->rules[6]), &flt_rule_entry, sizeof(struct ipa_flt_rule_add));
 #endif
 		if (m_filtering.AddFilteringRule(m_pFilteringTable) == false)
 		{
diff --git a/ipacm/src/IPACM_Lan.cpp b/ipacm/src/IPACM_Lan.cpp
index a561eef..2f561cc 100644
--- a/ipacm/src/IPACM_Lan.cpp
+++ b/ipacm/src/IPACM_Lan.cpp
@@ -331,8 +331,14 @@
 				IPACMDBG_H("Invalid address, ignore IPA_ADDR_ADD_EVENT event\n");
 				return;
 			}
-
-
+#ifdef FEATURE_L2TP
+			if(data->iptype == IPA_IP_v6 && is_vlan_event(data->iface_name) && is_unique_local_ipv6_addr(data->ipv6_addr))
+			{
+				IPACMDBG_H("Got IPv6 new addr event for a vlan iface %s.\n", data->iface_name);
+				eth_bridge_post_event(IPA_HANDLE_VLAN_IFACE_INFO, data->iptype, NULL,
+					data->ipv6_addr, data->iface_name);
+			}
+#endif
 			if (ipa_interface_index == ipa_if_num)
 			{
 				IPACMDBG_H("Received IPA_ADDR_ADD_EVENT\n");
@@ -826,24 +832,46 @@
 				}
 			}
 
-			if (ipa_interface_index == ipa_if_num)
+			if (ipa_interface_index == ipa_if_num || is_vlan_event(data->iface_name)
+				|| (is_l2tp_event(data->iface_name) && ipa_if_cate == ODU_IF))
 			{
 				IPACMDBG_H("ETH iface got client \n");
-				/* first construc ETH full header */
-				handle_eth_hdr_init(data->mac_addr);
-				IPACMDBG_H("construct ETH header and route rules \n");
-				/* Associate with IP and construct RT-rule */
-				if (handle_eth_client_ipaddr(data) == IPACM_FAILURE)
+				if(ipa_interface_index == ipa_if_num)
 				{
-					return;
+					/* first construc ETH full header */
+					handle_eth_hdr_init(data->mac_addr);
+					IPACMDBG_H("construct ETH header and route rules \n");
+					/* Associate with IP and construct RT-rule */
+					if (handle_eth_client_ipaddr(data) == IPACM_FAILURE)
+					{
+						return;
+					}
+					handle_eth_client_route_rule(data->mac_addr, data->iptype);
+					if (data->iptype == IPA_IP_v4)
+					{
+						/* Add NAT rules after ipv4 RT rules are set */
+						CtList->HandleNeighIpAddrAddEvt(data);
+					}
+					eth_bridge_post_event(IPA_ETH_BRIDGE_CLIENT_ADD, IPA_IP_MAX, data->mac_addr, NULL, data->iface_name);
 				}
-				handle_eth_client_route_rule(data->mac_addr, data->iptype);
-				if (data->iptype == IPA_IP_v4)
+#ifdef FEATURE_L2TP
+				else if(is_l2tp_event(data->iface_name) && ipa_if_cate == ODU_IF)
 				{
-					/* Add NAT rules after ipv4 RT rules are set */
-					CtList->HandleNeighIpAddrAddEvt(data);
+					if(tx_prop != NULL)
+					{
+						IPACMDBG_H("add rm dependency for L2TP interface.\n");
+						IPACM_Iface::ipacmcfg->AddRmDepend(IPACM_Iface::ipacmcfg->ipa_client_rm_map_tbl[tx_prop->tx[0].dst_pipe],false);
+					}
+					eth_bridge_post_event(IPA_ETH_BRIDGE_CLIENT_ADD, IPA_IP_MAX, data->mac_addr, NULL, data->iface_name);
 				}
-				eth_bridge_post_event(IPA_ETH_BRIDGE_CLIENT_ADD, IPA_IP_MAX, data->mac_addr);
+				else
+				{
+					if(data->iptype == IPA_IP_v6 && is_unique_local_ipv6_addr(data->ipv6_addr))
+					{
+						eth_bridge_post_event(IPA_HANDLE_VLAN_CLIENT_INFO, IPA_IP_MAX, data->mac_addr, data->ipv6_addr, data->iface_name);
+					}
+				}
+#endif
 				return;
 			}
 		}
@@ -863,18 +891,23 @@
 				return;
 			}
 
-			if (ipa_interface_index == ipa_if_num)
+			if (ipa_interface_index == ipa_if_num
+				|| (is_l2tp_event(data->iface_name) && ipa_if_cate == ODU_IF))
 			{
-				if (data->iptype == IPA_IP_v6)
+				if(ipa_interface_index == ipa_if_num)
 				{
-					handle_del_ipv6_addr(data);
-					return;
+					if (data->iptype == IPA_IP_v6)
+					{
+						handle_del_ipv6_addr(data);
+						return;
+					}
+					IPACMDBG_H("LAN iface delete client \n");
+					handle_eth_client_down_evt(data->mac_addr);
 				}
-
-				eth_bridge_post_event(IPA_ETH_BRIDGE_CLIENT_DEL, IPA_IP_MAX, data->mac_addr);
-
-				IPACMDBG_H("LAN iface delete client \n");
-				handle_eth_client_down_evt(data->mac_addr);
+				else
+				{
+					eth_bridge_post_event(IPA_ETH_BRIDGE_CLIENT_DEL, IPA_IP_MAX, data->mac_addr, NULL, data->iface_name);
+				}
 				return;
 			}
 		}
@@ -1166,7 +1199,7 @@
 
 		/* populate the flt rule offset for eth bridge */
 		eth_bridge_flt_rule_offset[data->iptype] = ipv4_icmp_flt_rule_hdl[0];
-		eth_bridge_post_event(IPA_ETH_BRIDGE_IFACE_UP, IPA_IP_v4, NULL);
+		eth_bridge_post_event(IPA_ETH_BRIDGE_IFACE_UP, IPA_IP_v4, NULL, NULL, NULL);
 	}
 	else
 	{
@@ -1257,7 +1290,7 @@
 
 			/* populate the flt rule offset for eth bridge */
 			eth_bridge_flt_rule_offset[data->iptype] = ipv6_icmp_flt_rule_hdl[0];
-			eth_bridge_post_event(IPA_ETH_BRIDGE_IFACE_UP, IPA_IP_v6, NULL);
+			eth_bridge_post_event(IPA_ETH_BRIDGE_IFACE_UP, IPA_IP_v6, NULL, NULL, NULL);
 
 			init_fl_rule(data->iptype);
 		}
@@ -2809,7 +2842,7 @@
 		IPACM_Iface::ipacmcfg->DelRmDepend(IPACM_Iface::ipacmcfg->ipa_client_rm_map_tbl[tx_prop->tx[0].dst_pipe]);
 	}
 
-	eth_bridge_post_event(IPA_ETH_BRIDGE_IFACE_DOWN, IPA_IP_MAX, NULL);
+	eth_bridge_post_event(IPA_ETH_BRIDGE_IFACE_DOWN, IPA_IP_MAX, NULL, NULL, NULL);
 
 /* Delete private subnet*/
 #ifdef FEATURE_IPA_ANDROID
@@ -4126,32 +4159,71 @@
 }
 
 /* mac address has to be provided for client related events */
-void IPACM_Lan::eth_bridge_post_event(ipa_cm_event_id evt, ipa_ip_type iptype, uint8_t *mac)
+void IPACM_Lan::eth_bridge_post_event(ipa_cm_event_id evt, ipa_ip_type iptype, uint8_t *mac, uint32_t *ipv6_addr, char *iface_name)
 {
 	ipacm_cmd_q_data eth_bridge_evt;
-	ipacm_event_eth_bridge *evt_data;
-
-	evt_data = (ipacm_event_eth_bridge*)malloc(sizeof(ipacm_event_eth_bridge));
-	if(evt_data == NULL)
-	{
-		IPACMERR("Failed to allocate memory.\n");
-		return;
-	}
-	memset(evt_data, 0, sizeof(ipacm_event_eth_bridge));
-
-	evt_data->p_iface = this;
-	evt_data->iptype = iptype;
-	if(mac)
-	{
-		IPACMDBG_H("Client mac: 0x%02x%02x%02x%02x%02x%02x \n",
-			mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
-		memcpy(evt_data->mac_addr, mac, sizeof(evt_data->mac_addr));
-	}
+	ipacm_event_eth_bridge *evt_data_eth_bridge;
+	ipacm_event_data_all *evt_data_all;
 
 	memset(&eth_bridge_evt, 0, sizeof(ipacm_cmd_q_data));
-	eth_bridge_evt.evt_data = (void*)evt_data;
 	eth_bridge_evt.event = evt;
 
+	if(evt == IPA_HANDLE_VLAN_CLIENT_INFO || evt == IPA_HANDLE_VLAN_IFACE_INFO)
+	{
+		evt_data_all = (ipacm_event_data_all*)malloc(sizeof(*evt_data_all));
+		if(evt_data_all == NULL)
+		{
+			IPACMERR("Failed to allocate memory.\n");
+			return;
+		}
+		memset(evt_data_all, 0, sizeof(*evt_data_all));
+
+		if(ipv6_addr)
+		{
+			IPACMDBG_H("IPv6 addr: %08x:%08x:%08x:%08x \n", ipv6_addr[0],
+				ipv6_addr[1], ipv6_addr[2], ipv6_addr[3]);
+			memcpy(evt_data_all->ipv6_addr, ipv6_addr, sizeof(evt_data_all->ipv6_addr));
+		}
+		if(mac)
+		{
+			IPACMDBG_H("Mac: 0x%02x%02x%02x%02x%02x%02x \n",
+				mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+			memcpy(evt_data_all->mac_addr, mac, sizeof(evt_data_all->mac_addr));
+		}
+		if(iface_name)
+		{
+			IPACMDBG_H("Iface: %s\n", iface_name);
+			memcpy(evt_data_all->iface_name, iface_name, sizeof(evt_data_all->iface_name));
+		}
+		eth_bridge_evt.evt_data = (void*)evt_data_all;
+	}
+	else
+	{
+		evt_data_eth_bridge = (ipacm_event_eth_bridge*)malloc(sizeof(*evt_data_eth_bridge));
+		if(evt_data_eth_bridge == NULL)
+		{
+			IPACMERR("Failed to allocate memory.\n");
+			return;
+		}
+		memset(evt_data_eth_bridge, 0, sizeof(*evt_data_eth_bridge));
+
+		evt_data_eth_bridge->p_iface = this;
+		evt_data_eth_bridge->iptype = iptype;
+		if(mac)
+		{
+			IPACMDBG_H("Mac: 0x%02x%02x%02x%02x%02x%02x \n",
+				mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+			memcpy(evt_data_eth_bridge->mac_addr, mac, sizeof(evt_data_eth_bridge->mac_addr));
+		}
+		if(iface_name)
+		{
+			IPACMDBG_H("Iface: %s\n", iface_name);
+			memcpy(evt_data_eth_bridge->iface_name, iface_name,
+				sizeof(evt_data_eth_bridge->iface_name));
+		}
+		eth_bridge_evt.evt_data = (void*)evt_data_eth_bridge;
+	}
+
 	IPACMDBG_H("Posting event %s\n",
 		IPACM_Iface::ipacmcfg->getEventName(evt));
 	IPACM_EvtDispatcher::PostEvt(&eth_bridge_evt);
@@ -4494,3 +4566,817 @@
 	}
 	return IPACM_SUCCESS;
 }
+
+/* check if the event is associated with vlan interface */
+bool IPACM_Lan::is_vlan_event(char *event_iface_name)
+{
+	int self_name_len, event_iface_name_len;
+	if(event_iface_name == NULL)
+	{
+		IPACMERR("Invalid input\n");
+		return false;
+	}
+
+	IPACMDBG_H("Self iface %s, event iface %s\n", dev_name, event_iface_name);
+	self_name_len = strlen(dev_name);
+	event_iface_name_len = strlen(event_iface_name);
+
+	if(event_iface_name_len > self_name_len && strncmp(dev_name, event_iface_name, self_name_len) == 0)
+	{
+		IPACMDBG_H("This is vlan event.\n");
+		return true;
+	}
+	return false;
+}
+
+/* check if the event is associated with l2tp interface */
+bool IPACM_Lan::is_l2tp_event(char *event_iface_name)
+{
+	if(event_iface_name == NULL)
+	{
+		IPACMERR("Invalid input\n");
+		return false;
+	}
+
+	IPACMDBG_H("Self iface %s, event iface %s\n", dev_name, event_iface_name);
+	if(strncmp(event_iface_name, "l2tp", 4) == 0)
+	{
+		IPACMDBG_H("This is l2tp event.\n");
+		return true;
+	}
+	return false;
+}
+
+/* add l2tp rt rule for l2tp client */
+int IPACM_Lan::add_l2tp_rt_rule(ipa_ip_type iptype, uint8_t *dst_mac, ipa_hdr_l2_type peer_l2_hdr_type,
+	uint32_t l2tp_session_id, uint32_t vlan_id, uint8_t *vlan_client_mac, uint32_t *vlan_iface_ipv6_addr,
+	uint32_t *vlan_client_ipv6_addr, uint32_t *first_pass_hdr_hdl, uint32_t *first_pass_hdr_proc_ctx_hdl,
+	uint32_t *second_pass_hdr_hdl, int *num_rt_hdl, uint32_t *first_pass_rt_rule_hdl, uint32_t *second_pass_rt_rule_hdl)
+{
+	int i, size, position;
+	uint32_t vlan_iface_ipv6_addr_network[4], vlan_client_ipv6_addr_network[4];
+	ipa_ioc_add_hdr *hdr_table;
+	ipa_hdr_add *hdr;
+	ipa_ioc_add_hdr_proc_ctx *hdr_proc_ctx_table;
+	ipa_hdr_proc_ctx_add *hdr_proc_ctx;
+	ipa_ioc_add_rt_rule* rt_rule_table;
+	ipa_rt_rule_add *rt_rule;
+	ipa_ioc_copy_hdr copy_hdr;
+
+	if(tx_prop == NULL)
+	{
+		IPACMERR("No tx prop.\n");
+		return IPACM_FAILURE;
+	}
+
+	/* =========== install first pass hdr template (IPv6 + L2TP + inner ETH header = 62 bytes) ============= */
+	if(*first_pass_hdr_hdl != 0)
+	{
+		IPACMDBG_H("First pass hdr template was added before.\n");
+	}
+	else
+	{
+		size = sizeof(ipa_ioc_add_hdr) + sizeof(ipa_hdr_add);
+		hdr_table = (ipa_ioc_add_hdr*)malloc(size);
+		if(hdr_table == NULL)
+		{
+			IPACMERR("Failed to allocate memory.\n");
+			return IPACM_FAILURE;
+		}
+		memset(hdr_table, 0, size);
+
+		hdr_table->commit = 1;
+		hdr_table->num_hdrs = 1;
+		hdr = &hdr_table->hdr[0];
+
+		if(iptype == IPA_IP_v4)
+		{
+			snprintf(hdr->name, sizeof(hdr->name), "vlan_%d_l2tp_%d_v4", vlan_id, l2tp_session_id);
+		}
+		else
+		{
+			snprintf(hdr->name, sizeof(hdr->name), "vlan_%d_l2tp_%d_v6", vlan_id, l2tp_session_id);
+		}
+		hdr->hdr_len = 62;
+		hdr->type = IPA_HDR_L2_ETHERNET_II;
+		hdr->is_partial = 0;
+
+		hdr->hdr[0] = 0x60;	/* version */
+		hdr->hdr[6] = 0x73; /* next header = L2TP */
+		hdr->hdr[7] = 0x40; /* hop limit = 64 */
+		for(i = 0; i < 4; i++)
+		{
+			vlan_iface_ipv6_addr_network[i] = htonl(vlan_iface_ipv6_addr[i]);
+			vlan_client_ipv6_addr_network[i] = htonl(vlan_client_ipv6_addr[i]);
+		}
+		memcpy(hdr->hdr + 8, vlan_iface_ipv6_addr_network, 16); /* source IPv6 addr */
+		memcpy(hdr->hdr + 24, vlan_client_ipv6_addr_network, 16); /* dest IPv6 addr */
+		hdr->hdr[43] = (uint8_t)(l2tp_session_id & 0xFF); /* l2tp header */
+		hdr->hdr[42] = (uint8_t)(l2tp_session_id >> 8 & 0xFF);
+		hdr->hdr[41] = (uint8_t)(l2tp_session_id >> 16 & 0xFF);
+		hdr->hdr[40] = (uint8_t)(l2tp_session_id >> 24 & 0xFF);
+
+		if(m_header.AddHeader(hdr_table) == false)
+		{
+			IPACMERR("Failed to add hdr with status: %d\n", hdr_table->hdr[0].status);
+			free(hdr_table);
+			return IPACM_FAILURE;
+		}
+		*first_pass_hdr_hdl = hdr_table->hdr[0].hdr_hdl;
+		IPACMDBG_H("Installed first pass hdr: hdl %d\n", *first_pass_hdr_hdl);
+		free(hdr_table);
+	}
+
+	/* =========== install first pass hdr proc ctx (populate src/dst MAC and Ether type) ============= */
+	size = sizeof(ipa_ioc_add_hdr_proc_ctx) + sizeof(ipa_hdr_proc_ctx_add);
+	hdr_proc_ctx_table = (ipa_ioc_add_hdr_proc_ctx*)malloc(size);
+	if(hdr_proc_ctx_table == NULL)
+	{
+		IPACMERR("Failed to allocate memory.\n");
+		return IPACM_FAILURE;
+	}
+	memset(hdr_proc_ctx_table, 0, size);
+
+	hdr_proc_ctx_table->commit = 1;
+	hdr_proc_ctx_table->num_proc_ctxs = 1;
+	hdr_proc_ctx = &hdr_proc_ctx_table->proc_ctx[0];
+
+	hdr_proc_ctx->type = IPA_HDR_PROC_L2TP_HEADER_ADD;
+	hdr_proc_ctx->hdr_hdl = *first_pass_hdr_hdl;
+	hdr_proc_ctx->l2tp_params.hdr_add_param.eth_hdr_retained = 1;
+	hdr_proc_ctx->l2tp_params.hdr_add_param.input_ip_version = iptype;
+	hdr_proc_ctx->l2tp_params.hdr_add_param.output_ip_version = IPA_IP_v6;
+	if(m_header.AddHeaderProcCtx(hdr_proc_ctx_table) == false)
+	{
+		IPACMERR("Failed to add hdr proc ctx with status: %d\n", hdr_proc_ctx_table->proc_ctx[0].status);
+		free(hdr_proc_ctx_table);
+		return IPACM_FAILURE;
+	}
+	*first_pass_hdr_proc_ctx_hdl = hdr_proc_ctx_table->proc_ctx[0].proc_ctx_hdl;
+	IPACMDBG_H("Installed first pass hdr proc ctx: hdl %d\n", *first_pass_hdr_proc_ctx_hdl);
+	free(hdr_proc_ctx_table);
+
+	/* =========== install first pass rt rules (match dst MAC then doing UCP) ============= */
+	*num_rt_hdl = each_client_rt_rule_count[iptype];
+	size = sizeof(ipa_ioc_add_rt_rule) + (*num_rt_hdl) * sizeof(ipa_rt_rule_add);
+	rt_rule_table = (ipa_ioc_add_rt_rule*)malloc(size);
+	if (rt_rule_table == NULL)
+	{
+		IPACMERR("Failed to allocate memory.\n");
+		return IPACM_FAILURE;
+	}
+	memset(rt_rule_table, 0, size);
+
+	rt_rule_table->commit = 1;
+	rt_rule_table->ip = iptype;
+	rt_rule_table->num_rules = *num_rt_hdl;
+	snprintf(rt_rule_table->rt_tbl_name, sizeof(rt_rule_table->rt_tbl_name), "l2tp");
+	rt_rule_table->rt_tbl_name[IPA_RESOURCE_NAME_MAX-1] = 0;
+
+	position = 0;
+	for(i = 0; i < iface_query->num_tx_props; i++)
+	{
+		if(tx_prop->tx[i].ip == iptype)
+		{
+			if(position >= *num_rt_hdl || position >= MAX_NUM_PROP)
+			{
+				IPACMERR("Number of routing rules already exceeds limit.\n");
+				free(rt_rule_table);
+				return IPACM_FAILURE;
+			}
+
+			rt_rule = &rt_rule_table->rules[position];
+			rt_rule->at_rear = false;
+			rt_rule->status = -1;
+			rt_rule->rt_rule_hdl = -1;
+			rt_rule->rule.hashable = false;	//WLAN->ETH direction rules are set to non-hashable to keep consistent with the other direction
+			rt_rule->rule.hdr_hdl = 0;
+			rt_rule->rule.hdr_proc_ctx_hdl = *first_pass_hdr_proc_ctx_hdl;
+			rt_rule->rule.dst = IPA_CLIENT_DUMMY_CONS;
+
+			memcpy(&rt_rule->rule.attrib, &tx_prop->tx[i].attrib, sizeof(rt_rule->rule.attrib));
+			if(peer_l2_hdr_type == IPA_HDR_L2_ETHERNET_II)
+				rt_rule->rule.attrib.attrib_mask |= IPA_FLT_MAC_DST_ADDR_ETHER_II;
+			else
+				rt_rule->rule.attrib.attrib_mask |= IPA_FLT_MAC_DST_ADDR_802_3;
+			memcpy(rt_rule->rule.attrib.dst_mac_addr, dst_mac, sizeof(rt_rule->rule.attrib.dst_mac_addr));
+			memset(rt_rule->rule.attrib.dst_mac_addr_mask, 0xFF, sizeof(rt_rule->rule.attrib.dst_mac_addr_mask));
+			position++;
+		}
+	}
+	if(m_routing.AddRoutingRule(rt_rule_table) == false)
+	{
+		IPACMERR("Failed to add first pass rt rules.\n");
+		free(rt_rule_table);
+		return IPACM_FAILURE;
+	}
+	for(i = 0; i < position; i++)
+	{
+		first_pass_rt_rule_hdl[i] = rt_rule_table->rules[i].rt_rule_hdl;
+	}
+	free(rt_rule_table);
+
+	/* =========== install second pass hdr (Ethernet header with L2TP tag = 18 bytes) ============= */
+	if(*second_pass_hdr_hdl != 0)
+	{
+		IPACMDBG_H("Second pass hdr was added before.\n");
+	}
+	else
+	{
+		size = sizeof(ipa_ioc_add_hdr) + sizeof(ipa_hdr_add);
+		hdr_table = (ipa_ioc_add_hdr*)malloc(size);
+		if(hdr_table == NULL)
+		{
+			IPACMERR("Failed to allocate memory.\n");
+			return IPACM_FAILURE;
+		}
+		memset(hdr_table, 0, size);
+
+		hdr_table->commit = 1;
+		hdr_table->num_hdrs = 1;
+		hdr = &hdr_table->hdr[0];
+
+		if(iptype == IPA_IP_v4)
+		{
+			snprintf(hdr->name, sizeof(hdr->name), "vlan_%d_v4", vlan_id);
+		}
+		else
+		{
+			snprintf(hdr->name, sizeof(hdr->name), "vlan_%d_v6", vlan_id);
+		}
+		hdr->type = IPA_HDR_L2_ETHERNET_II;
+		hdr->is_partial = 0;
+		for(i = 0; i < tx_prop->num_tx_props; i++)
+		{
+			if(tx_prop->tx[i].ip == IPA_IP_v6)
+			{
+				memset(&copy_hdr, 0, sizeof(copy_hdr));
+				strlcpy(copy_hdr.name, tx_prop->tx[i].hdr_name,
+					sizeof(copy_hdr.name));
+				IPACMDBG_H("Header name: %s in tx:%d\n", copy_hdr.name, i);
+				if(m_header.CopyHeader(&copy_hdr) == false)
+				{
+					IPACMERR("Failed to get partial header.\n");
+					free(hdr_table);
+					return IPACM_FAILURE;
+				}
+				IPACMDBG_H("Header length: %d\n", copy_hdr.hdr_len);
+				hdr->hdr_len = copy_hdr.hdr_len;
+				memcpy(hdr->hdr, copy_hdr.hdr, hdr->hdr_len);
+				break;
+			}
+		}
+		/* copy vlan client mac */
+		memcpy(hdr->hdr + hdr->hdr_len - 18, vlan_client_mac, 6);
+		hdr->hdr[hdr->hdr_len - 3] = (uint8_t)vlan_id & 0xFF;
+		hdr->hdr[hdr->hdr_len - 4] = (uint8_t)(vlan_id >> 8) & 0xFF;
+
+		if(m_header.AddHeader(hdr_table) == false)
+		{
+			IPACMERR("Failed to add hdr with status: %d\n", hdr->status);
+			free(hdr_table);
+			return IPACM_FAILURE;
+		}
+		*second_pass_hdr_hdl = hdr->hdr_hdl;
+		IPACMDBG_H("Installed second pass hdr: hdl %d\n", *second_pass_hdr_hdl);
+		free(hdr_table);
+	}
+
+	/* =========== install second pass rt rules (match VLAN interface IPv6 address at dst client side) ============= */
+	if(second_pass_rt_rule_hdl[0] != 0)
+	{
+		IPACMDBG_H("Second pass rt rule was added before, return.\n");
+		return IPACM_SUCCESS;
+	}
+
+	*num_rt_hdl = each_client_rt_rule_count[IPA_IP_v6];
+	size = sizeof(ipa_ioc_add_rt_rule) + (*num_rt_hdl) * sizeof(ipa_rt_rule_add);
+	rt_rule_table = (ipa_ioc_add_rt_rule*)malloc(size);
+	if (rt_rule_table == NULL)
+	{
+		IPACMERR("Failed to allocate memory.\n");
+		return IPACM_FAILURE;
+	}
+	memset(rt_rule_table, 0, size);
+
+	rt_rule_table->commit = 1;
+	rt_rule_table->ip = IPA_IP_v6;
+	rt_rule_table->num_rules = *num_rt_hdl;
+	snprintf(rt_rule_table->rt_tbl_name, sizeof(rt_rule_table->rt_tbl_name), "l2tp");
+	rt_rule_table->rt_tbl_name[IPA_RESOURCE_NAME_MAX-1] = 0;
+
+	position = 0;
+	for(i = 0; i < iface_query->num_tx_props; i++)
+	{
+		if(tx_prop->tx[i].ip == IPA_IP_v6)
+		{
+			if(position >= *num_rt_hdl || position >= MAX_NUM_PROP)
+			{
+				IPACMERR("Number of routing rules already exceeds limit.\n");
+				free(rt_rule_table);
+				return IPACM_FAILURE;
+			}
+
+			rt_rule = &rt_rule_table->rules[position];
+			rt_rule->at_rear = false;
+			rt_rule->status = -1;
+			rt_rule->rt_rule_hdl = -1;
+			rt_rule->rule.hashable = false;	//WLAN->ETH direction rules are set to non-hashable to keep consistent with the other direction
+			rt_rule->rule.hdr_hdl = *second_pass_hdr_hdl;
+			rt_rule->rule.hdr_proc_ctx_hdl = 0;
+			rt_rule->rule.dst = tx_prop->tx[i].dst_pipe;
+
+			memcpy(&rt_rule->rule.attrib, &tx_prop->tx[i].attrib, sizeof(rt_rule->rule.attrib));
+			rt_rule->rule.attrib.attrib_mask |= IPA_FLT_DST_ADDR;
+			memcpy(rt_rule->rule.attrib.u.v6.dst_addr, vlan_client_ipv6_addr,
+				sizeof(rt_rule->rule.attrib.u.v6.dst_addr));
+			memset(rt_rule->rule.attrib.u.v6.dst_addr_mask, 0xFF, sizeof(rt_rule->rule.attrib.u.v6.dst_addr_mask));
+			position++;
+		}
+	}
+	if(m_routing.AddRoutingRule(rt_rule_table) == false)
+	{
+		IPACMERR("Failed to add second pass rt rules.\n");
+		free(rt_rule_table);
+		return IPACM_FAILURE;
+	}
+	for(i = 0; i < position; i++)
+	{
+		second_pass_rt_rule_hdl[i] = rt_rule_table->rules[i].rt_rule_hdl;
+	}
+	free(rt_rule_table);
+
+	return IPACM_SUCCESS;
+}
+
+/* delete l2tp rt rule for l2tp client */
+int IPACM_Lan::del_l2tp_rt_rule(ipa_ip_type iptype, uint32_t first_pass_hdr_hdl, uint32_t first_pass_hdr_proc_ctx_hdl,
+	uint32_t second_pass_hdr_hdl, int num_rt_hdl, uint32_t *first_pass_rt_rule_hdl, uint32_t *second_pass_rt_rule_hdl)
+{
+	int i;
+
+	if(num_rt_hdl < 0)
+	{
+		IPACMERR("Invalid num rt rule: %d\n", num_rt_hdl);
+		return IPACM_FAILURE;
+	}
+
+	for(i = 0; i < num_rt_hdl; i++)
+	{
+		if(first_pass_rt_rule_hdl != NULL)
+		{
+			if(m_routing.DeleteRoutingHdl(first_pass_rt_rule_hdl[i], iptype) == false)
+			{
+				return IPACM_FAILURE;
+			}
+		}
+		if(second_pass_rt_rule_hdl != NULL)
+		{
+			if(m_routing.DeleteRoutingHdl(second_pass_rt_rule_hdl[i], IPA_IP_v6) == false)
+			{
+				return IPACM_FAILURE;
+			}
+		}
+	}
+
+	if(first_pass_hdr_proc_ctx_hdl != 0)
+	{
+		if(m_header.DeleteHeaderProcCtx(first_pass_hdr_proc_ctx_hdl) == false)
+		{
+			return IPACM_FAILURE;
+		}
+	}
+
+	if(first_pass_hdr_hdl != 0)
+	{
+		if(m_header.DeleteHeaderHdl(first_pass_hdr_hdl) == false)
+		{
+			return IPACM_FAILURE;
+		}
+	}
+	if(second_pass_hdr_hdl != 0)
+	{
+		if(m_header.DeleteHeaderHdl(second_pass_hdr_hdl) == false)
+		{
+			return IPACM_FAILURE;
+		}
+	}
+
+	return IPACM_SUCCESS;
+}
+
+/* add l2tp rt rule for non l2tp client */
+int IPACM_Lan::add_l2tp_rt_rule(ipa_ip_type iptype, uint8_t *dst_mac, uint32_t *hdr_proc_ctx_hdl,
+	int *num_rt_hdl, uint32_t *rt_rule_hdl)
+{
+	int i, size, position;
+	ipa_ioc_add_hdr_proc_ctx *hdr_proc_ctx_table;
+	ipa_hdr_proc_ctx_add *hdr_proc_ctx;
+	ipa_ioc_add_rt_rule* rt_rule_table;
+	ipa_rt_rule_add *rt_rule;
+	ipa_ioc_get_hdr hdr;
+
+	if(tx_prop == NULL)
+	{
+		IPACMERR("No tx prop.\n");
+		return IPACM_FAILURE;
+	}
+
+	memset(&hdr, 0, sizeof(hdr));
+	for(i = 0; i < tx_prop->num_tx_props; i++)
+	{
+		if(tx_prop->tx[i].ip == iptype)
+		{
+			strlcpy(hdr.name, tx_prop->tx[i].hdr_name,
+				sizeof(hdr.name));
+			break;
+		}
+	}
+	if(m_header.GetHeaderHandle(&hdr) == false)
+	{
+		IPACMERR("Failed to get template hdr hdl.\n");
+		return IPACM_FAILURE;
+	}
+
+	/* =========== install hdr proc ctx (uc needs to remove IPv6 + L2TP + inner ETH header = 62 bytes) ============= */
+	if(*hdr_proc_ctx_hdl != 0)
+	{
+		IPACMDBG_H("Hdr proc ctx was added before.\n");
+	}
+	else
+	{
+		size = sizeof(ipa_ioc_add_hdr_proc_ctx) + sizeof(ipa_hdr_proc_ctx_add);
+		hdr_proc_ctx_table = (ipa_ioc_add_hdr_proc_ctx*)malloc(size);
+		if(hdr_proc_ctx_table == NULL)
+		{
+			IPACMERR("Failed to allocate memory.\n");
+			return IPACM_FAILURE;
+		}
+		memset(hdr_proc_ctx_table, 0, size);
+
+		hdr_proc_ctx_table->commit = 1;
+		hdr_proc_ctx_table->num_proc_ctxs = 1;
+		hdr_proc_ctx = &hdr_proc_ctx_table->proc_ctx[0];
+
+		hdr_proc_ctx->type = IPA_HDR_PROC_L2TP_HEADER_REMOVE;
+		hdr_proc_ctx->hdr_hdl = hdr.hdl;
+		hdr_proc_ctx->l2tp_params.hdr_remove_param.hdr_len_remove = 62;
+		hdr_proc_ctx->l2tp_params.hdr_remove_param.eth_hdr_retained = 1;
+		if(m_header.AddHeaderProcCtx(hdr_proc_ctx_table) == false)
+		{
+			IPACMERR("Failed to add hdr proc ctx with status: %d\n", hdr_proc_ctx_table->proc_ctx[0].status);
+			free(hdr_proc_ctx_table);
+			return IPACM_FAILURE;
+		}
+		*hdr_proc_ctx_hdl = hdr_proc_ctx_table->proc_ctx[0].proc_ctx_hdl;
+		IPACMDBG_H("Installed hdr proc ctx: hdl %d\n", *hdr_proc_ctx_hdl);
+		free(hdr_proc_ctx_table);
+	}
+
+	/* =========== install rt rules (match dst MAC within 62 bytes header) ============= */
+	*num_rt_hdl = each_client_rt_rule_count[iptype];
+	size = sizeof(ipa_ioc_add_rt_rule) + (*num_rt_hdl) * sizeof(ipa_rt_rule_add);
+	rt_rule_table = (ipa_ioc_add_rt_rule*)malloc(size);
+	if (rt_rule_table == NULL)
+	{
+		IPACMERR("Failed to allocate memory.\n");
+		return IPACM_FAILURE;
+	}
+	memset(rt_rule_table, 0, size);
+
+	rt_rule_table->commit = 1;
+	rt_rule_table->ip = iptype;
+	rt_rule_table->num_rules = *num_rt_hdl;
+	snprintf(rt_rule_table->rt_tbl_name, sizeof(rt_rule_table->rt_tbl_name), "l2tp");
+	rt_rule_table->rt_tbl_name[IPA_RESOURCE_NAME_MAX-1] = 0;
+
+	position = 0;
+	for(i = 0; i < iface_query->num_tx_props; i++)
+	{
+		if(tx_prop->tx[i].ip == iptype)
+		{
+			if(position >= *num_rt_hdl || position >= MAX_NUM_PROP)
+			{
+				IPACMERR("Number of routing rules already exceeds limit.\n");
+				free(rt_rule_table);
+				return IPACM_FAILURE;
+			}
+
+			rt_rule = &rt_rule_table->rules[position];
+			rt_rule->at_rear = false;
+			rt_rule->status = -1;
+			rt_rule->rt_rule_hdl = -1;
+			rt_rule->rule.hashable = false;	//ETH->WLAN direction rules need to be non-hashable due to encapsulation
+
+			rt_rule->rule.hdr_hdl = 0;
+			rt_rule->rule.hdr_proc_ctx_hdl = *hdr_proc_ctx_hdl;
+			rt_rule->rule.dst = tx_prop->tx[i].dst_pipe;
+
+			memcpy(&rt_rule->rule.attrib, &tx_prop->tx[i].attrib, sizeof(rt_rule->rule.attrib));
+
+			rt_rule->rule.attrib.attrib_mask |= IPA_FLT_MAC_DST_ADDR_L2TP;
+			memset(rt_rule->rule.attrib.dst_mac_addr_mask, 0xFF, sizeof(rt_rule->rule.attrib.dst_mac_addr_mask));
+			memcpy(rt_rule->rule.attrib.dst_mac_addr, dst_mac, sizeof(rt_rule->rule.attrib.dst_mac_addr));
+
+			position++;
+		}
+	}
+	if(m_routing.AddRoutingRule(rt_rule_table) == false)
+	{
+		IPACMERR("Failed to add first pass rt rules.\n");
+		free(rt_rule_table);
+		return IPACM_FAILURE;
+	}
+	for(i = 0; i < position; i++)
+		rt_rule_hdl[i] = rt_rule_table->rules[i].rt_rule_hdl;
+
+	free(rt_rule_table);
+	return IPACM_SUCCESS;
+}
+
+int IPACM_Lan::del_l2tp_rt_rule(ipa_ip_type iptype, int num_rt_hdl, uint32_t *rt_rule_hdl)
+{
+	int i;
+
+	if(num_rt_hdl < 0)
+	{
+		IPACMERR("Invalid num rt rule: %d\n", num_rt_hdl);
+		return IPACM_FAILURE;
+	}
+
+	for(i = 0; i < num_rt_hdl; i++)
+	{
+		if(m_routing.DeleteRoutingHdl(rt_rule_hdl[i], iptype) == false)
+		{
+			return IPACM_FAILURE;
+		}
+	}
+
+	return IPACM_SUCCESS;
+}
+
+/* add l2tp flt rule on l2tp interface */
+int IPACM_Lan::add_l2tp_flt_rule(uint8_t *dst_mac, uint32_t *flt_rule_hdl)
+{
+	int len;
+	int fd_ipa;
+	struct ipa_flt_rule_add flt_rule_entry;
+	struct ipa_ioc_add_flt_rule_after *pFilteringTable = NULL;
+	ipa_ioc_get_rt_tbl rt_tbl;
+
+#ifdef FEATURE_IPA_V3
+	if (rx_prop == NULL || tx_prop == NULL)
+	{
+		IPACMDBG_H("No rx or tx properties registered for iface %s\n", dev_name);
+		return IPACM_FAILURE;
+	}
+
+	len = sizeof(struct ipa_ioc_add_flt_rule_after) + sizeof(struct ipa_flt_rule_add);
+	pFilteringTable = (struct ipa_ioc_add_flt_rule_after*)malloc(len);
+	if (!pFilteringTable)
+	{
+		IPACMERR("Failed to allocate ipa_ioc_add_flt_rule_after memory...\n");
+		return IPACM_FAILURE;
+	}
+	memset(pFilteringTable, 0, len);
+
+	pFilteringTable->commit = 1;
+	pFilteringTable->ep = rx_prop->rx[0].src_pipe;
+	pFilteringTable->ip = IPA_IP_v6;
+	pFilteringTable->num_rules = 1;
+	pFilteringTable->add_after_hdl = eth_bridge_flt_rule_offset[IPA_IP_v6];
+
+	fd_ipa = open(IPA_DEVICE_NAME, O_RDWR);
+	if(fd_ipa == 0)
+	{
+		IPACMERR("Failed to open %s\n",IPA_DEVICE_NAME);
+		free(pFilteringTable);
+		return IPACM_FAILURE;
+	}
+
+	rt_tbl.ip = IPA_IP_v6;
+	snprintf(rt_tbl.name, sizeof(rt_tbl.name), "l2tp");
+	rt_tbl.name[IPA_RESOURCE_NAME_MAX-1] = '\0';
+	IPACMDBG_H("This flt rule points to rt tbl %s.\n", rt_tbl.name);
+	if(m_routing.GetRoutingTable(&rt_tbl) == false)
+	{
+		IPACMERR("Failed to get routing table from name\n");
+		free(pFilteringTable);
+		close(fd_ipa);
+		return IPACM_FAILURE;
+	}
+
+	memset(&flt_rule_entry, 0, sizeof(flt_rule_entry));
+	flt_rule_entry.at_rear = 1;
+
+	flt_rule_entry.rule.retain_hdr = 0;
+	flt_rule_entry.rule.to_uc = 0;
+	flt_rule_entry.rule.action = IPA_PASS_TO_ROUTING;
+	flt_rule_entry.rule.eq_attrib_type = 0;
+	flt_rule_entry.rule.rt_tbl_hdl = rt_tbl.hdl;
+	flt_rule_entry.rule.hashable = false;	//ETH->WLAN direction rules need to be non-hashable due to encapsulation
+
+	memcpy(&flt_rule_entry.rule.attrib, &rx_prop->rx[0].attrib, sizeof(flt_rule_entry.rule.attrib));
+
+	/* flt rule is matching dst MAC within 62 bytes header */
+	flt_rule_entry.rule.attrib.attrib_mask |= IPA_FLT_MAC_DST_ADDR_L2TP;
+	memset(flt_rule_entry.rule.attrib.dst_mac_addr_mask, 0xFF, sizeof(flt_rule_entry.rule.attrib.dst_mac_addr_mask));
+	memcpy(flt_rule_entry.rule.attrib.dst_mac_addr, dst_mac, sizeof(flt_rule_entry.rule.attrib.dst_mac_addr));
+
+	memcpy(&(pFilteringTable->rules[0]), &flt_rule_entry, sizeof(flt_rule_entry));
+	if(m_filtering.AddFilteringRuleAfter(pFilteringTable) == false)
+	{
+		IPACMERR("Failed to add client filtering rules.\n");
+		free(pFilteringTable);
+		close(fd_ipa);
+		return IPACM_FAILURE;
+	}
+	*flt_rule_hdl = pFilteringTable->rules[0].flt_rule_hdl;
+
+	free(pFilteringTable);
+	close(fd_ipa);
+#endif
+	return IPACM_SUCCESS;
+}
+
+/* delete l2tp flt rule on l2tp interface */
+int IPACM_Lan::del_l2tp_flt_rule(uint32_t flt_rule_hdl)
+{
+	if(m_filtering.DeleteFilteringHdls(&flt_rule_hdl, IPA_IP_v6, 1) == false)
+	{
+		return IPACM_FAILURE;
+	}
+
+	return IPACM_SUCCESS;
+}
+
+/* add l2tp flt rule on non l2tp interface */
+int IPACM_Lan::add_l2tp_flt_rule(ipa_ip_type iptype, uint8_t *dst_mac, uint32_t *vlan_client_ipv6_addr,
+	uint32_t *first_pass_flt_rule_hdl, uint32_t *second_pass_flt_rule_hdl)
+{
+	int len;
+	struct ipa_flt_rule_add flt_rule_entry;
+	struct ipa_ioc_add_flt_rule_after *pFilteringTable = NULL;
+	ipa_ioc_get_rt_tbl rt_tbl;
+
+#ifdef FEATURE_IPA_V3
+	if (rx_prop == NULL || tx_prop == NULL)
+	{
+		IPACMDBG_H("No rx or tx properties registered for iface %s\n", dev_name);
+		return IPACM_FAILURE;
+	}
+
+	IPACMDBG_H("Dst client MAC 0x%02x%02x%02x%02x%02x%02x.\n", dst_mac[0], dst_mac[1],
+		dst_mac[2], dst_mac[3], dst_mac[4], dst_mac[5]);
+
+	len = sizeof(struct ipa_ioc_add_flt_rule_after) + sizeof(struct ipa_flt_rule_add);
+	pFilteringTable = (struct ipa_ioc_add_flt_rule_after*)malloc(len);
+	if (!pFilteringTable)
+	{
+		IPACMERR("Failed to allocate ipa_ioc_add_flt_rule_after memory...\n");
+		return IPACM_FAILURE;
+	}
+	memset(pFilteringTable, 0, len);
+
+	pFilteringTable->commit = 1;
+	pFilteringTable->ep = rx_prop->rx[0].src_pipe;
+	pFilteringTable->ip = iptype;
+	pFilteringTable->num_rules = 1;
+	pFilteringTable->add_after_hdl = eth_bridge_flt_rule_offset[iptype];
+
+	/* =========== add first pass flt rule (match dst MAC) ============= */
+	rt_tbl.ip = iptype;
+	snprintf(rt_tbl.name, sizeof(rt_tbl.name), "l2tp");
+	IPACMDBG_H("This flt rule points to rt tbl %s.\n", rt_tbl.name);
+
+	if(m_routing.GetRoutingTable(&rt_tbl) == false)
+	{
+		IPACMERR("Failed to get routing table.\n");
+		return IPACM_FAILURE;
+	}
+
+	memset(&flt_rule_entry, 0, sizeof(flt_rule_entry));
+	flt_rule_entry.at_rear = 1;
+
+	flt_rule_entry.rule.retain_hdr = 0;
+	flt_rule_entry.rule.to_uc = 0;
+	flt_rule_entry.rule.action = IPA_PASS_TO_ROUTING;
+	flt_rule_entry.rule.eq_attrib_type = 0;
+	flt_rule_entry.rule.rt_tbl_hdl = rt_tbl.hdl;
+	flt_rule_entry.rule.hashable = false;	//WLAN->ETH direction rules are set to non-hashable to keep consistent with the other direction
+
+	memcpy(&flt_rule_entry.rule.attrib, &rx_prop->rx[0].attrib, sizeof(flt_rule_entry.rule.attrib));
+	if(tx_prop->tx[0].hdr_l2_type == IPA_HDR_L2_ETHERNET_II)
+	{
+		flt_rule_entry.rule.attrib.attrib_mask |= IPA_FLT_MAC_DST_ADDR_ETHER_II;
+	}
+	else
+	{
+		flt_rule_entry.rule.attrib.attrib_mask |= IPA_FLT_MAC_DST_ADDR_802_3;
+	}
+
+	memcpy(flt_rule_entry.rule.attrib.dst_mac_addr, dst_mac, sizeof(flt_rule_entry.rule.attrib.dst_mac_addr));
+	memset(flt_rule_entry.rule.attrib.dst_mac_addr_mask, 0xFF, sizeof(flt_rule_entry.rule.attrib.dst_mac_addr_mask));
+
+	memcpy(&(pFilteringTable->rules[0]), &flt_rule_entry, sizeof(flt_rule_entry));
+	if (false == m_filtering.AddFilteringRuleAfter(pFilteringTable))
+	{
+		IPACMERR("Failed to add first pass filtering rules.\n");
+		free(pFilteringTable);
+		return IPACM_FAILURE;
+	}
+	*first_pass_flt_rule_hdl = pFilteringTable->rules[0].flt_rule_hdl;
+
+	/* =========== add second pass flt rule (match VLAN interface IPv6 address at client side) ============= */
+	if(*second_pass_flt_rule_hdl != 0)
+	{
+		IPACMDBG_H("Second pass flt rule was added before, return.\n");
+		free(pFilteringTable);
+		return IPACM_SUCCESS;
+	}
+
+	rt_tbl.ip = IPA_IP_v6;
+	snprintf(rt_tbl.name, sizeof(rt_tbl.name), "l2tp");
+	IPACMDBG_H("This flt rule points to rt tbl %s.\n", rt_tbl.name);
+
+	if(m_routing.GetRoutingTable(&rt_tbl) == false)
+	{
+		IPACMERR("Failed to get routing table.\n");
+		return IPACM_FAILURE;
+	}
+
+	pFilteringTable->ip = IPA_IP_v6;
+	pFilteringTable->add_after_hdl = eth_bridge_flt_rule_offset[IPA_IP_v6];
+
+	memset(&flt_rule_entry, 0, sizeof(flt_rule_entry));
+	flt_rule_entry.at_rear = 1;
+
+	flt_rule_entry.rule.retain_hdr = 0;
+	flt_rule_entry.rule.to_uc = 0;
+	flt_rule_entry.rule.action = IPA_PASS_TO_ROUTING;
+	flt_rule_entry.rule.eq_attrib_type = 0;
+	flt_rule_entry.rule.rt_tbl_hdl = rt_tbl.hdl;
+	flt_rule_entry.rule.hashable = false;	//WLAN->ETH direction rules are set to non-hashable to keep consistent with the other direction
+
+	memcpy(&flt_rule_entry.rule.attrib, &rx_prop->rx[0].attrib, sizeof(flt_rule_entry.rule.attrib));
+	flt_rule_entry.rule.attrib.attrib_mask = IPA_FLT_DST_ADDR;
+
+	memcpy(flt_rule_entry.rule.attrib.u.v6.dst_addr, vlan_client_ipv6_addr, sizeof(flt_rule_entry.rule.attrib.u.v6.dst_addr));
+	memset(flt_rule_entry.rule.attrib.u.v6.dst_addr_mask, 0xFF, sizeof(flt_rule_entry.rule.attrib.u.v6.dst_addr_mask));
+
+	memcpy(&(pFilteringTable->rules[0]), &flt_rule_entry, sizeof(flt_rule_entry));
+	if (false == m_filtering.AddFilteringRuleAfter(pFilteringTable))
+	{
+		IPACMERR("Failed to add client filtering rules.\n");
+		free(pFilteringTable);
+		return IPACM_FAILURE;
+	}
+	*second_pass_flt_rule_hdl = pFilteringTable->rules[0].flt_rule_hdl;
+
+	free(pFilteringTable);
+#endif
+	return IPACM_SUCCESS;
+}
+
+/* delete l2tp flt rule on non l2tp interface */
+int IPACM_Lan::del_l2tp_flt_rule(ipa_ip_type iptype, uint32_t first_pass_flt_rule_hdl, uint32_t second_pass_flt_rule_hdl)
+{
+	if(first_pass_flt_rule_hdl != 0)
+	{
+		if(m_filtering.DeleteFilteringHdls(&first_pass_flt_rule_hdl, iptype, 1) == false)
+		{
+			return IPACM_FAILURE;
+		}
+	}
+
+	if(second_pass_flt_rule_hdl != 0)
+	{
+		if(m_filtering.DeleteFilteringHdls(&second_pass_flt_rule_hdl, iptype, 1) == false)
+		{
+			return IPACM_FAILURE;
+		}
+	}
+
+	return IPACM_SUCCESS;
+}
+
+bool IPACM_Lan::is_unique_local_ipv6_addr(uint32_t* ipv6_addr)
+{
+	uint32_t ipv6_unique_local_prefix, ipv6_unique_local_prefix_mask;
+
+	if(ipv6_addr == NULL)
+	{
+		IPACMERR("IPv6 address is empty.\n");
+		return false;
+	}
+	IPACMDBG_H("Get ipv6 address with first word 0x%08x.\n", ipv6_addr[0]);
+
+	ipv6_unique_local_prefix = 0xFD000000;
+	ipv6_unique_local_prefix_mask = 0xFF000000;
+	if((ipv6_addr[0] & ipv6_unique_local_prefix_mask) == (ipv6_unique_local_prefix & ipv6_unique_local_prefix_mask))
+	{
+		IPACMDBG_H("This IPv6 address is unique local IPv6 address.\n");
+		return true;
+	}
+	return false;
+}
diff --git a/ipacm/src/IPACM_LanToLan.cpp b/ipacm/src/IPACM_LanToLan.cpp
index d77f389..799dfee 100644
--- a/ipacm/src/IPACM_LanToLan.cpp
+++ b/ipacm/src/IPACM_LanToLan.cpp
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2014, The Linux Foundation. All rights reserved.
+Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
@@ -51,6 +51,8 @@
 	__stringify(L2_MAX)
 };
 
+IPACM_LanToLan* IPACM_LanToLan::p_instance;
+
 IPACM_LanToLan_Iface::IPACM_LanToLan_Iface(IPACM_Lan *p_iface)
 {
 	int i;
@@ -59,12 +61,14 @@
 	memset(m_is_ip_addr_assigned, 0, sizeof(m_is_ip_addr_assigned));
 	m_support_inter_iface_offload = true;
 	m_support_intra_iface_offload = false;
+	m_is_l2tp_iface = false;
 	for(i = 0; i < IPA_HDR_L2_MAX; i++)
 	{
 		ref_cnt_peer_l2_hdr_type[i] = 0;
 		hdr_proc_ctx_for_inter_interface[i] = 0;
 	}
 	hdr_proc_ctx_for_intra_interface = 0;
+	hdr_proc_ctx_for_l2tp = 0;
 
 	if(p_iface->ipa_if_cate == WLAN_IF)
 	{
@@ -90,6 +94,14 @@
 	IPACM_EvtDispatcher::registr(IPA_ETH_BRIDGE_CLIENT_ADD, this);
 	IPACM_EvtDispatcher::registr(IPA_ETH_BRIDGE_CLIENT_DEL, this);
 	IPACM_EvtDispatcher::registr(IPA_ETH_BRIDGE_WLAN_SCC_MCC_SWITCH, this);
+	IPACM_EvtDispatcher::registr(IPA_ADD_VLAN_IFACE, this);
+	IPACM_EvtDispatcher::registr(IPA_DEL_VLAN_IFACE, this);
+	IPACM_EvtDispatcher::registr(IPA_ADD_L2TP_VLAN_MAPPING, this);
+	IPACM_EvtDispatcher::registr(IPA_DEL_L2TP_VLAN_MAPPING, this);
+	IPACM_EvtDispatcher::registr(IPA_HANDLE_VLAN_CLIENT_INFO, this);
+	IPACM_EvtDispatcher::registr(IPA_HANDLE_VLAN_IFACE_INFO, this);
+
+	m_has_l2tp_iface = false;
 
 	return;
 }
@@ -100,40 +112,115 @@
 	return;
 }
 
+IPACM_LanToLan* IPACM_LanToLan::get_instance()
+{
+	if(p_instance == NULL)
+	{
+		p_instance = new IPACM_LanToLan();
+		IPACMDBG_H("Created LanToLan instance.\n");
+	}
+	return p_instance;
+}
+
+bool IPACM_LanToLan::has_l2tp_iface()
+{
+	list<IPACM_LanToLan_Iface>::iterator it;
+	bool has_l2tp_iface = false;
+
+	for(it = m_iface.begin(); it != m_iface.end(); it++)
+	{
+		if(it->is_l2tp_iface() == true)
+		{
+			has_l2tp_iface = true;
+			break;
+		}
+	}
+	return has_l2tp_iface;
+}
+
 void IPACM_LanToLan::event_callback(ipa_cm_event_id event, void* param)
 {
-	ipacm_event_eth_bridge *data = (ipacm_event_eth_bridge*)param;
+	ipacm_event_eth_bridge *eth_bridge_data;
+	ipa_ioc_vlan_iface_info *vlan_iface_data;
+	ipa_ioc_l2tp_vlan_mapping_info *l2tp_vlan_mapping_data;
+	ipacm_event_data_all *vlan_data;
+
 	IPACMDBG_H("Get %s event.\n", IPACM_Iface::ipacmcfg->getEventName(event));
 
 	switch(event)
 	{
 		case IPA_ETH_BRIDGE_IFACE_UP:
 		{
-			handle_iface_up(data);
+			eth_bridge_data = (ipacm_event_eth_bridge*)param;
+			handle_iface_up(eth_bridge_data);
 			break;
 		}
 
 		case IPA_ETH_BRIDGE_IFACE_DOWN:
 		{
-			handle_iface_down(data);
+			eth_bridge_data = (ipacm_event_eth_bridge*)param;
+			handle_iface_down(eth_bridge_data);
 			break;
 		}
 
 		case IPA_ETH_BRIDGE_CLIENT_ADD:
 		{
-			handle_client_add(data);
+			eth_bridge_data = (ipacm_event_eth_bridge*)param;
+			handle_client_add(eth_bridge_data);
 			break;
 		}
 
 		case IPA_ETH_BRIDGE_CLIENT_DEL:
 		{
-			handle_client_del(data);
+			eth_bridge_data = (ipacm_event_eth_bridge*)param;
+			handle_client_del(eth_bridge_data);
 			break;
 		}
 
 		case IPA_ETH_BRIDGE_WLAN_SCC_MCC_SWITCH:
 		{
-			handle_wlan_scc_mcc_switch(data);
+			eth_bridge_data = (ipacm_event_eth_bridge*)param;
+			handle_wlan_scc_mcc_switch(eth_bridge_data);
+			break;
+		}
+
+		case IPA_ADD_VLAN_IFACE:
+		{
+			vlan_iface_data = (ipa_ioc_vlan_iface_info*)param;
+			handle_add_vlan_iface(vlan_iface_data);
+			break;
+		}
+
+		case IPA_DEL_VLAN_IFACE:
+		{
+			vlan_iface_data = (ipa_ioc_vlan_iface_info*)param;
+			handle_del_vlan_iface(vlan_iface_data);
+			break;
+		}
+
+		case IPA_ADD_L2TP_VLAN_MAPPING:
+		{
+			l2tp_vlan_mapping_data = (ipa_ioc_l2tp_vlan_mapping_info*)param;
+			handle_add_l2tp_vlan_mapping(l2tp_vlan_mapping_data);
+			break;
+		}
+
+		case IPA_DEL_L2TP_VLAN_MAPPING:
+		{
+			l2tp_vlan_mapping_data = (ipa_ioc_l2tp_vlan_mapping_info*)param;
+			handle_del_l2tp_vlan_mapping(l2tp_vlan_mapping_data);
+			break;
+		}
+		case IPA_HANDLE_VLAN_CLIENT_INFO:
+		{
+			vlan_data = (ipacm_event_data_all*)param;
+			handle_vlan_client_info(vlan_data);
+			break;
+		}
+		case IPA_HANDLE_VLAN_IFACE_INFO:
+		{
+			vlan_data = (ipacm_event_data_all*)param;
+			handle_vlan_iface_info(vlan_data);
 			break;
 		}
 		default:
@@ -147,6 +234,8 @@
 void IPACM_LanToLan::handle_iface_up(ipacm_event_eth_bridge *data)
 {
 	list<IPACM_LanToLan_Iface>::iterator it;
+	list<l2tp_vlan_mapping_info>::iterator it_mapping;
+	bool has_l2tp_iface = false;
 
 	IPACMDBG_H("Interface name: %s IP type: %d\n", data->p_iface->dev_name, data->iptype);
 	for(it = m_iface.begin(); it != m_iface.end(); it++)
@@ -199,7 +288,28 @@
 		IPACMDBG_H("Now the total number of interfaces is %d.\n", m_iface.size());
 
 		IPACM_LanToLan_Iface &front_iface = m_iface.front();
+#ifdef FEATURE_L2TP
+		for(it_mapping = m_l2tp_vlan_mapping.begin(); it_mapping != m_l2tp_vlan_mapping.end(); it_mapping++)
+		{
+			if(front_iface.set_l2tp_iface(it_mapping->vlan_iface_name) == true)
+			{
+				has_l2tp_iface = true;
+			}
+		}
 
+		if(m_has_l2tp_iface == false && has_l2tp_iface == true)
+		{
+			IPACMDBG_H("There is l2tp iface, add rt rules for l2tp iface.\n");
+			m_has_l2tp_iface = true;
+			for(it = ++m_iface.begin(); it != m_iface.end(); it++)
+			{
+				if(it->is_l2tp_iface() == false)
+				{
+					it->handle_l2tp_enable();
+				}
+			}
+		}
+#endif
 		/* install inter-interface rules */
 		if(front_iface.get_m_support_inter_iface_offload())
 		{
@@ -235,6 +345,7 @@
 void IPACM_LanToLan::handle_iface_down(ipacm_event_eth_bridge *data)
 {
 	list<IPACM_LanToLan_Iface>::iterator it_target_iface;
+	bool has_l2tp_iface = false;
 
 	IPACMDBG_H("Interface name: %s\n", data->p_iface->dev_name);
 
@@ -257,7 +368,29 @@
 
 	it_target_iface->handle_down_event();
 	m_iface.erase(it_target_iface);
+#ifdef FEATURE_L2TP
+	for(it_target_iface = m_iface.begin(); it_target_iface != m_iface.end(); it_target_iface++)
+	{
+		if(it_target_iface->is_l2tp_iface() == true)
+		{
+			has_l2tp_iface = true;
+			break;
+		}
+	}
 
+	if(m_has_l2tp_iface == true && has_l2tp_iface == false)
+	{
+		IPACMDBG_H("There is no l2tp iface now, delete rt rules for l2tp iface.\n");
+		m_has_l2tp_iface = false;
+		for(it_target_iface = m_iface.begin(); it_target_iface != m_iface.end(); it_target_iface++)
+		{
+			if(it_target_iface->is_l2tp_iface() == false)
+			{
+				it_target_iface->handle_l2tp_disable();
+			}
+		}
+	}
+#endif
 	return;
 }
 
@@ -301,16 +434,35 @@
 void IPACM_LanToLan::handle_client_add(ipacm_event_eth_bridge *data)
 {
 	list<IPACM_LanToLan_Iface>::iterator it_iface;
+	list<l2tp_vlan_mapping_info>::iterator it_mapping;
+	l2tp_vlan_mapping_info *mapping_info = NULL;
+	bool is_l2tp_client = false;
 
 	IPACMDBG_H("Incoming client MAC: 0x%02x%02x%02x%02x%02x%02x, interface: %s\n", 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->p_iface->dev_name);
-
+#ifdef FEATURE_L2TP
+	for(it_mapping = m_l2tp_vlan_mapping.begin(); it_mapping != m_l2tp_vlan_mapping.end(); it_mapping++)
+	{
+		if(strncmp(it_mapping->l2tp_iface_name, data->iface_name,
+			sizeof(it_mapping->l2tp_iface_name)) == 0)
+		{
+			IPACMDBG_H("Found l2tp iface %s with l2tp client MAC 0x%02x%02x%02x%02x%02x%02x\n",
+				it_mapping->l2tp_iface_name, it_mapping->l2tp_client_mac[0], it_mapping->l2tp_client_mac[1],
+				it_mapping->l2tp_client_mac[2], it_mapping->l2tp_client_mac[3], it_mapping->l2tp_client_mac[4],
+				it_mapping->l2tp_client_mac[5]);
+			memcpy(it_mapping->l2tp_client_mac, data->mac_addr, sizeof(it_mapping->l2tp_client_mac));
+			mapping_info = &(*it_mapping);
+			is_l2tp_client = true;
+			break;
+		}
+	}
+#endif
 	for(it_iface = m_iface.begin(); it_iface != m_iface.end(); it_iface++)
 	{
 		if(it_iface->get_iface_pointer() == data->p_iface)	//find the interface
 		{
 			IPACMDBG_H("Found the interface.\n");
-			it_iface->handle_client_add(data->mac_addr);
+			it_iface->handle_client_add(data->mac_addr, is_l2tp_client, mapping_info);
 			break;
 		}
 	}
@@ -373,6 +525,241 @@
 	return;
 }
 
+void IPACM_LanToLan::handle_add_vlan_iface(ipa_ioc_vlan_iface_info *data)
+{
+	list<vlan_iface_info>::iterator it_vlan;
+	list<l2tp_vlan_mapping_info>::iterator it_mapping;
+	vlan_iface_info new_vlan_info;
+
+	IPACMDBG_H("Vlan iface: %s vlan id: %d\n", data->name, data->vlan_id);
+	for(it_vlan = m_vlan_iface.begin(); it_vlan != m_vlan_iface.end(); it_vlan++)
+	{
+		if(strncmp(it_vlan->vlan_iface_name, data->name, sizeof(it_vlan->vlan_iface_name)) == 0)
+		{
+			IPACMERR("The vlan iface was added before with id %d\n", it_vlan->vlan_id);
+			return;
+		}
+	}
+
+	for(it_mapping = m_l2tp_vlan_mapping.begin(); it_mapping != m_l2tp_vlan_mapping.end(); it_mapping++)
+	{
+		if(strncmp(data->name, it_mapping->vlan_iface_name, sizeof(data->name)) == 0)
+		{
+			IPACMDBG_H("Found a mapping: l2tp iface %s.\n", it_mapping->l2tp_iface_name);
+			it_mapping->vlan_id = data->vlan_id;
+		}
+	}
+
+	memset(&new_vlan_info, 0 , sizeof(new_vlan_info));
+	strlcpy(new_vlan_info.vlan_iface_name, data->name, sizeof(new_vlan_info.vlan_iface_name));
+	new_vlan_info.vlan_id = data->vlan_id;
+	m_vlan_iface.push_front(new_vlan_info);
+	return;
+}
+
+void IPACM_LanToLan::handle_del_vlan_iface(ipa_ioc_vlan_iface_info *data)
+{
+	list<vlan_iface_info>::iterator it_vlan;
+	list<l2tp_vlan_mapping_info>::iterator it_mapping;
+
+	IPACMDBG_H("Vlan iface: %s vlan id: %d\n", data->name, data->vlan_id);
+	for(it_vlan = m_vlan_iface.begin(); it_vlan != m_vlan_iface.end(); it_vlan++)
+	{
+		if(strncmp(it_vlan->vlan_iface_name, data->name, sizeof(it_vlan->vlan_iface_name)) == 0)
+		{
+			IPACMDBG_H("Found the vlan interface\n");
+			m_vlan_iface.erase(it_vlan);
+			break;
+		}
+	}
+
+	it_mapping = m_l2tp_vlan_mapping.begin();
+	while(it_mapping != m_l2tp_vlan_mapping.end())
+	{
+		if(strncmp(data->name, it_mapping->vlan_iface_name, sizeof(data->name)) == 0)
+		{
+			IPACMDBG_H("Delete mapping with l2tp iface %s\n", it_mapping->l2tp_iface_name);
+			it_mapping = m_l2tp_vlan_mapping.erase(it_mapping);
+		}
+		else
+		{
+			it_mapping++;
+		}
+	}
+	return;
+}
+
+void IPACM_LanToLan::handle_add_l2tp_vlan_mapping(ipa_ioc_l2tp_vlan_mapping_info *data)
+{
+	list<l2tp_vlan_mapping_info>::iterator it_mapping;
+	list<vlan_iface_info>::iterator it_vlan;
+	list<IPACM_LanToLan_Iface>::iterator it_iface;
+	l2tp_vlan_mapping_info new_mapping;
+	bool has_l2tp_iface = false;
+
+	IPACMDBG_H("L2tp iface: %s session id: %d vlan iface: %s \n",
+		data->l2tp_iface_name, data->l2tp_session_id, data->vlan_iface_name);
+	for(it_mapping = m_l2tp_vlan_mapping.begin(); it_mapping != m_l2tp_vlan_mapping.end(); it_mapping++)
+	{
+		if(strncmp(data->l2tp_iface_name, it_mapping->l2tp_iface_name,
+			sizeof(data->l2tp_iface_name)) == 0)
+		{
+			IPACMERR("L2tp mapping was added before mapped to vlan %s.\n", it_mapping->vlan_iface_name);
+			return;
+		}
+	}
+	memset(&new_mapping, 0, sizeof(new_mapping));
+	strlcpy(new_mapping.l2tp_iface_name, data->l2tp_iface_name,
+		sizeof(new_mapping.l2tp_iface_name));
+	strlcpy(new_mapping.vlan_iface_name, data->vlan_iface_name,
+		sizeof(new_mapping.vlan_iface_name));
+	new_mapping.l2tp_session_id = data->l2tp_session_id;
+
+	for(it_vlan = m_vlan_iface.begin(); it_vlan != m_vlan_iface.end(); it_vlan++)
+	{
+		if(strncmp(it_vlan->vlan_iface_name, data->vlan_iface_name, sizeof(it_vlan->vlan_iface_name)) == 0)
+		{
+			IPACMDBG_H("Found vlan iface with id %d\n", it_vlan->vlan_id);
+			new_mapping.vlan_id = it_vlan->vlan_id;
+			memcpy(new_mapping.vlan_iface_ipv6_addr, it_vlan->vlan_iface_ipv6_addr,
+				sizeof(new_mapping.vlan_iface_ipv6_addr));
+			memcpy(new_mapping.vlan_client_mac, it_vlan->vlan_client_mac,
+				sizeof(new_mapping.vlan_client_mac));
+			memcpy(new_mapping.vlan_client_ipv6_addr, it_vlan->vlan_client_ipv6_addr,
+				sizeof(new_mapping.vlan_client_ipv6_addr));
+			break;
+		}
+	}
+	m_l2tp_vlan_mapping.push_front(new_mapping);
+
+	for(it_iface = m_iface.begin(); it_iface != m_iface.end(); it_iface++)
+	{
+		if(it_iface->set_l2tp_iface(data->vlan_iface_name) == true)
+		{
+			has_l2tp_iface = true;
+		}
+	}
+
+	if(m_has_l2tp_iface == false && has_l2tp_iface == true)
+	{
+		IPACMDBG_H("There is l2tp iface, add rt rules for l2tp iface.\n");
+		m_has_l2tp_iface = true;
+		for(it_iface = m_iface.begin(); it_iface != m_iface.end(); it_iface++)
+		{
+			if(it_iface->is_l2tp_iface() == false)
+			{
+				it_iface->handle_l2tp_enable();
+			}
+		}
+	}
+	return;
+}
+
+void IPACM_LanToLan::handle_del_l2tp_vlan_mapping(ipa_ioc_l2tp_vlan_mapping_info *data)
+{
+	list<l2tp_vlan_mapping_info>::iterator it;
+	list<IPACM_LanToLan_Iface>::iterator it_iface;
+
+	IPACMDBG_H("L2tp iface: %s session id: %d vlan iface: %s \n",
+		data->l2tp_iface_name, data->l2tp_session_id, data->vlan_iface_name);
+	for(it = m_l2tp_vlan_mapping.begin(); it != m_l2tp_vlan_mapping.end(); it++)
+	{
+		if(strncmp(data->l2tp_iface_name, it->l2tp_iface_name,
+			sizeof(data->l2tp_iface_name)) == 0)
+		{
+			IPACMDBG_H("Found l2tp iface mapped to vlan %s.\n", it->vlan_iface_name);
+			if(strncmp(data->vlan_iface_name, it->vlan_iface_name,
+				sizeof(data->vlan_iface_name)) == 0)
+			{
+				m_l2tp_vlan_mapping.erase(it);
+			}
+			else
+			{
+				IPACMERR("Incoming mapping is incorrect.\n");
+			}
+			break;
+		}
+	}
+	return;
+}
+
+void IPACM_LanToLan::handle_vlan_client_info(ipacm_event_data_all *data)
+{
+	list<l2tp_vlan_mapping_info>::iterator it_mapping;
+	list<vlan_iface_info>::iterator it_vlan;
+
+	IPACMDBG_H("Incoming vlan client iface: %s IPv6 address: 0x%08x%08x%08x%08x\n", data->iface_name,
+		data->ipv6_addr[0], data->ipv6_addr[1], data->ipv6_addr[2], data->ipv6_addr[3]);
+	IPACMDBG_H("MAC address: 0x%02x%02x%02x%02x%02x%02x\n", data->mac_addr[0], data->mac_addr[1],
+		data->mac_addr[2], data->mac_addr[3], data->mac_addr[4], data->mac_addr[5]);
+
+	for(it_vlan = m_vlan_iface.begin(); it_vlan != m_vlan_iface.end(); it_vlan++)
+	{
+		if(strncmp(it_vlan->vlan_iface_name, data->iface_name, sizeof(it_vlan->vlan_iface_name)) == 0)
+		{
+			IPACMDBG_H("Found vlan iface in vlan list: %s\n", it_vlan->vlan_iface_name);
+			if(it_vlan->vlan_client_ipv6_addr[0] > 0 || it_vlan->vlan_client_ipv6_addr[1] > 0 ||
+				it_vlan->vlan_client_ipv6_addr[2] > 0 || it_vlan->vlan_client_ipv6_addr[3] > 0)
+			{
+				IPACMDBG_H("Vlan client info has been populated before, return.\n");
+				return;
+			}
+			memcpy(it_vlan->vlan_client_mac, data->mac_addr, sizeof(it_vlan->vlan_client_mac));
+			memcpy(it_vlan->vlan_client_ipv6_addr, data->ipv6_addr, sizeof(it_vlan->vlan_client_ipv6_addr));
+		}
+	}
+
+	for(it_mapping = m_l2tp_vlan_mapping.begin(); it_mapping != m_l2tp_vlan_mapping.end(); it_mapping++)
+	{
+		if(strncmp(it_mapping->vlan_iface_name, data->iface_name, sizeof(it_mapping->vlan_iface_name)) == 0)
+		{
+			IPACMDBG_H("Found vlan iface in l2tp mapping list: %s, l2tp iface: %s\n", it_mapping->vlan_iface_name,
+				it_mapping->l2tp_iface_name);
+			memcpy(it_mapping->vlan_client_mac, data->mac_addr, sizeof(it_mapping->vlan_client_mac));
+			memcpy(it_mapping->vlan_client_ipv6_addr, data->ipv6_addr, sizeof(it_mapping->vlan_client_ipv6_addr));
+		}
+	}
+	return;
+}
+
+void IPACM_LanToLan::handle_vlan_iface_info(ipacm_event_data_all *data)
+{
+	list<vlan_iface_info>::iterator it_vlan;
+	list<l2tp_vlan_mapping_info>::iterator it_mapping;
+
+	IPACMDBG_H("Incoming vlan iface: %s IPv6 address: 0x%08x%08x%08x%08x\n", data->iface_name,
+		data->ipv6_addr[0], data->ipv6_addr[1], data->ipv6_addr[2], data->ipv6_addr[3]);
+
+	for(it_vlan = m_vlan_iface.begin(); it_vlan != m_vlan_iface.end(); it_vlan++)
+	{
+		if(strncmp(it_vlan->vlan_iface_name, data->iface_name,
+			sizeof(it_vlan->vlan_iface_name)) == 0)
+		{
+			IPACMDBG_H("Found vlan iface: %s\n", it_vlan->vlan_iface_name);
+			memcpy(it_vlan->vlan_iface_ipv6_addr, data->ipv6_addr,
+				sizeof(it_vlan->vlan_iface_ipv6_addr));
+
+			for(it_mapping = m_l2tp_vlan_mapping.begin(); it_mapping != m_l2tp_vlan_mapping.end(); it_mapping++)
+			{
+				if(strncmp(it_mapping->vlan_iface_name, it_vlan->vlan_iface_name,
+					sizeof(it_mapping->vlan_iface_name)) == 0)
+				{
+					IPACMDBG_H("Found the l2tp-vlan mapping: l2tp %s\n", it_mapping->l2tp_iface_name);
+					memcpy(it_mapping->vlan_iface_ipv6_addr, data->ipv6_addr,
+						sizeof(it_mapping->vlan_iface_ipv6_addr));
+				}
+			}
+			break;
+		}
+	}
+
+	if(it_vlan == m_vlan_iface.end())
+	{
+		IPACMDBG_H("Failed to find the vlan iface: %s\n", data->iface_name);
+	}
+	return;
+}
+
 void IPACM_LanToLan::handle_cached_client_add_event(IPACM_Lan *p_iface)
 {
 	list<ipacm_event_eth_bridge>::iterator it;
@@ -420,10 +807,42 @@
 {
 	list<IPACM_LanToLan_Iface>::iterator it;
 	list<ipacm_event_eth_bridge>::iterator it_event;
+	list<vlan_iface_info>::iterator it_vlan;
+	list<l2tp_vlan_mapping_info>::iterator it_mapping;
 	int i;
 
-	IPACMDBG_H("There are %d interfaces in total.\n", m_iface.size());
+	IPACMDBG_H("Is there l2tp interface? %d\n", m_has_l2tp_iface);
 
+	IPACMDBG_H("There are %d vlan interfaces.\n", m_vlan_iface.size());
+	for(it_vlan = m_vlan_iface.begin(); it_vlan != m_vlan_iface.end(); it_vlan++)
+	{
+		IPACMDBG_H("Vlan iface: %s, id: %d, ipv6 addr: 0x%08x%08x%08x%08x\n", it_vlan->vlan_iface_name,
+			it_vlan->vlan_id, it_vlan->vlan_iface_ipv6_addr[0], it_vlan->vlan_iface_ipv6_addr[1],
+			it_vlan->vlan_iface_ipv6_addr[2], it_vlan->vlan_iface_ipv6_addr[3]);
+		IPACMDBG_H("Vlan client mac: 0x%02x%02x%02x%02x%02x%02x, ipv6 addr: 0x%08x%08x%08x%08x\n",
+			it_vlan->vlan_client_mac[0], it_vlan->vlan_client_mac[1], it_vlan->vlan_client_mac[2],
+			it_vlan->vlan_client_mac[3], it_vlan->vlan_client_mac[4], it_vlan->vlan_client_mac[5],
+			it_vlan->vlan_client_ipv6_addr[0], it_vlan->vlan_client_ipv6_addr[1], it_vlan->vlan_client_ipv6_addr[2],
+			it_vlan->vlan_client_ipv6_addr[3]);
+	}
+
+	IPACMDBG_H("There are %d vlan-l2tp mapping.\n", m_l2tp_vlan_mapping.size());
+	for(it_mapping = m_l2tp_vlan_mapping.begin(); it_mapping != m_l2tp_vlan_mapping.end(); it_mapping++)
+	{
+		IPACMDBG_H("L2tp iface: %s, session id: %d\n", it_mapping->l2tp_iface_name, it_mapping->l2tp_session_id);
+		IPACMDBG_H("Vlan iface: %s, id: %d, ipv6 addr: 0x%08x%08x%08x%08x\n", it_mapping->vlan_iface_name,
+			it_mapping->vlan_id, it_mapping->vlan_iface_ipv6_addr[0], it_mapping->vlan_iface_ipv6_addr[1],
+			it_mapping->vlan_iface_ipv6_addr[2], it_mapping->vlan_iface_ipv6_addr[3]);
+		IPACMDBG_H("Vlan client mac: 0x%02x%02x%02x%02x%02x%02x, ipv6 addr: 0x%08x%08x%08x%08x\n",
+			it_mapping->vlan_client_mac[0], it_mapping->vlan_client_mac[1], it_mapping->vlan_client_mac[2],
+			it_mapping->vlan_client_mac[3], it_mapping->vlan_client_mac[4], it_mapping->vlan_client_mac[5],
+			it_mapping->vlan_client_ipv6_addr[0], it_mapping->vlan_client_ipv6_addr[1], it_mapping->vlan_client_ipv6_addr[2],
+			it_mapping->vlan_client_ipv6_addr[3]);
+		IPACMDBG_H("L2tp client mac: 0x%02x%02x%02x%02x%02x%02x\n", it_mapping->l2tp_client_mac[0], it_mapping->l2tp_client_mac[1],
+			it_mapping->l2tp_client_mac[2], it_mapping->l2tp_client_mac[3], it_mapping->l2tp_client_mac[4], it_mapping->l2tp_client_mac[5]);
+	}
+
+	IPACMDBG_H("There are %d interfaces in total.\n", m_iface.size());
 	for(it = m_iface.begin(); it != m_iface.end(); it++)
 	{
 		it->print_data_structure_info();
@@ -453,7 +872,16 @@
 	{
 		for(it = m_client_info.begin(); it != m_client_info.end(); it++)
 		{
+#ifdef FEATURE_L2TP
+			if(it->is_l2tp_client == false)
+			{
+				add_client_rt_rule(&peer, &(*it));
+			}
+			/* add l2tp rt rules */
+			add_l2tp_client_rt_rule(&peer, &(*it));
+#else
 			add_client_rt_rule(&peer, &(*it));
+#endif
 		}
 	}
 
@@ -524,6 +952,40 @@
 	return;
 }
 
+void IPACM_LanToLan_Iface::add_l2tp_client_rt_rule(peer_iface_info *peer, client_info *client)
+{
+	ipa_hdr_l2_type peer_l2_hdr_type;
+	l2tp_vlan_mapping_info *mapping_info;
+
+	peer_l2_hdr_type = peer->peer->get_iface_pointer()->tx_prop->tx[0].hdr_l2_type;
+	mapping_info = client->mapping_info;
+	if(client->is_l2tp_client)
+	{
+		m_p_iface->add_l2tp_rt_rule(IPA_IP_v4, client->mac_addr, peer_l2_hdr_type, mapping_info->l2tp_session_id,
+			mapping_info->vlan_id, mapping_info->vlan_client_mac, mapping_info->vlan_iface_ipv6_addr,
+			mapping_info->vlan_client_ipv6_addr, &client->l2tp_rt_rule_hdl[peer_l2_hdr_type].first_pass_hdr_hdl,
+			&client->l2tp_rt_rule_hdl[peer_l2_hdr_type].first_pass_hdr_proc_ctx_hdl[IPA_IP_v4], &client->l2tp_rt_rule_hdl[peer_l2_hdr_type].second_pass_hdr_hdl,
+			&client->l2tp_rt_rule_hdl[peer_l2_hdr_type].num_rt_hdl[IPA_IP_v4], client->l2tp_rt_rule_hdl[peer_l2_hdr_type].first_pass_rt_rule_hdl[IPA_IP_v4],
+			client->l2tp_rt_rule_hdl[peer_l2_hdr_type].second_pass_rt_rule_hdl);
+
+		m_p_iface->add_l2tp_rt_rule(IPA_IP_v6, client->mac_addr, peer_l2_hdr_type, mapping_info->l2tp_session_id,
+			mapping_info->vlan_id, mapping_info->vlan_client_mac, mapping_info->vlan_iface_ipv6_addr,
+			mapping_info->vlan_client_ipv6_addr, &client->l2tp_rt_rule_hdl[peer_l2_hdr_type].first_pass_hdr_hdl,
+			&client->l2tp_rt_rule_hdl[peer_l2_hdr_type].first_pass_hdr_proc_ctx_hdl[IPA_IP_v6], &client->l2tp_rt_rule_hdl[peer_l2_hdr_type].second_pass_hdr_hdl,
+			&client->l2tp_rt_rule_hdl[peer_l2_hdr_type].num_rt_hdl[IPA_IP_v6], client->l2tp_rt_rule_hdl[peer_l2_hdr_type].first_pass_rt_rule_hdl[IPA_IP_v6],
+			client->l2tp_rt_rule_hdl[peer_l2_hdr_type].second_pass_rt_rule_hdl);
+	}
+	else
+	{
+		if(IPACM_LanToLan::get_instance()->has_l2tp_iface() == true)
+		{
+			m_p_iface->add_l2tp_rt_rule(IPA_IP_v6, client->mac_addr, &hdr_proc_ctx_for_l2tp, &client->l2tp_rt_rule_hdl[peer_l2_hdr_type].num_rt_hdl[IPA_IP_v6],
+				client->l2tp_rt_rule_hdl[peer_l2_hdr_type].first_pass_rt_rule_hdl[IPA_IP_v6]);
+		}
+	}
+	return;
+}
+
 void IPACM_LanToLan_Iface::add_all_inter_interface_client_flt_rule(ipa_ip_type iptype)
 {
 	list<peer_iface_info>::iterator it_iface;
@@ -581,39 +1043,74 @@
 {
 	list<flt_rule_info>::iterator it_flt;
 	uint32_t flt_rule_hdl;
+	uint32_t l2tp_first_pass_flt_rule_hdl = 0, l2tp_second_pass_flt_rule_hdl = 0;
 	flt_rule_info new_flt_info;
 	ipa_ioc_get_rt_tbl rt_tbl;
 
-	rt_tbl.ip = iptype;
-	memcpy(rt_tbl.name, peer->rt_tbl_name_for_flt[iptype], sizeof(rt_tbl.name));
-	IPACMDBG_H("This flt rule points to rt tbl %s.\n", rt_tbl.name);
-
-	if(IPACM_Iface::m_routing.GetRoutingTable(&rt_tbl) == false)
+	if(m_is_l2tp_iface && iptype == IPA_IP_v4)
 	{
-		IPACMERR("Failed to get routing table.\n");
+		IPACMDBG_H("No need to install IPv4 flt rule on l2tp interface.\n");
 		return;
 	}
 
-	m_p_iface->eth_bridge_add_flt_rule(client->mac_addr, rt_tbl.hdl,
-		iptype, &flt_rule_hdl);
-	IPACMDBG_H("Installed flt rule for IP type %d: handle %d\n", iptype, flt_rule_hdl);
-
 	for(it_flt = peer->flt_rule.begin(); it_flt != peer->flt_rule.end(); it_flt++)
 	{
 		if(it_flt->p_client == client)	//the client is already in the flt info list
 		{
 			IPACMDBG_H("The client is found in flt info list.\n");
-			it_flt->flt_rule_hdl[iptype] = flt_rule_hdl;
 			break;
 		}
 	}
 
-	if(it_flt == peer->flt_rule.end())	//the client is not in the flt info list
+	if(it_flt != peer->flt_rule.end())
+	{
+		l2tp_first_pass_flt_rule_hdl = it_flt->l2tp_first_pass_flt_rule_hdl[iptype];
+		l2tp_second_pass_flt_rule_hdl = it_flt->l2tp_second_pass_flt_rule_hdl;
+	}
+
+	if(m_is_l2tp_iface)
+	{
+		m_p_iface->add_l2tp_flt_rule(client->mac_addr, &l2tp_first_pass_flt_rule_hdl);
+	}
+	else
+	{
+		if(client->is_l2tp_client)
+		{
+			m_p_iface->add_l2tp_flt_rule(iptype, client->mac_addr, client->mapping_info->vlan_client_ipv6_addr,
+				&l2tp_first_pass_flt_rule_hdl, &l2tp_second_pass_flt_rule_hdl);
+		}
+		else
+		{
+			rt_tbl.ip = iptype;
+			memcpy(rt_tbl.name, peer->rt_tbl_name_for_flt[iptype], sizeof(rt_tbl.name));
+			IPACMDBG_H("This flt rule points to rt tbl %s.\n", rt_tbl.name);
+
+			if(IPACM_Iface::m_routing.GetRoutingTable(&rt_tbl) == false)
+			{
+				IPACMERR("Failed to get routing table.\n");
+				return;
+			}
+
+			m_p_iface->eth_bridge_add_flt_rule(client->mac_addr, rt_tbl.hdl,
+				iptype, &flt_rule_hdl);
+			IPACMDBG_H("Installed flt rule for IP type %d: handle %d\n", iptype, flt_rule_hdl);
+		}
+	}
+
+	if(it_flt != peer->flt_rule.end())
+	{
+		it_flt->flt_rule_hdl[iptype] = flt_rule_hdl;
+		it_flt->l2tp_first_pass_flt_rule_hdl[iptype] = l2tp_first_pass_flt_rule_hdl;
+		it_flt->l2tp_second_pass_flt_rule_hdl = l2tp_second_pass_flt_rule_hdl;
+	}
+	else
 	{
 		IPACMDBG_H("The client is not found in flt info list, insert a new one.\n");
 		memset(&new_flt_info, 0, sizeof(new_flt_info));
 		new_flt_info.p_client = client;
 		new_flt_info.flt_rule_hdl[iptype] = flt_rule_hdl;
+		new_flt_info.l2tp_first_pass_flt_rule_hdl[iptype] = l2tp_first_pass_flt_rule_hdl;
+		new_flt_info.l2tp_second_pass_flt_rule_hdl = l2tp_second_pass_flt_rule_hdl;
 
 		peer->flt_rule.push_front(new_flt_info);
 	}
@@ -648,15 +1145,50 @@
 			IPACMDBG_H("Found the client in flt info list.\n");
 			if(m_is_ip_addr_assigned[IPA_IP_v4])
 			{
-				m_p_iface->eth_bridge_del_flt_rule(it_flt->flt_rule_hdl[IPA_IP_v4], IPA_IP_v4);
-				IPACMDBG_H("IPv4 flt rule %d is deleted.\n", it_flt->flt_rule_hdl[IPA_IP_v4]);
+				if(m_is_l2tp_iface)
+				{
+					IPACMDBG_H("No IPv4 client flt rule on l2tp iface.\n");
+				}
+				else
+				{
+					if(client->is_l2tp_client)
+					{
+						m_p_iface->del_l2tp_flt_rule(IPA_IP_v4, it_flt->l2tp_first_pass_flt_rule_hdl[IPA_IP_v4],
+							it_flt->l2tp_second_pass_flt_rule_hdl);
+						it_flt->l2tp_second_pass_flt_rule_hdl = 0;
+						IPACMDBG_H("Deleted IPv4 first pass flt rule %d and second pass flt rule %d.\n",
+							it_flt->l2tp_first_pass_flt_rule_hdl[IPA_IP_v4], it_flt->l2tp_second_pass_flt_rule_hdl);
+					}
+					else
+					{
+						m_p_iface->eth_bridge_del_flt_rule(it_flt->flt_rule_hdl[IPA_IP_v4], IPA_IP_v4);
+						IPACMDBG_H("Deleted IPv4 flt rule %d.\n", it_flt->flt_rule_hdl[IPA_IP_v4]);
+					}
+				}
 			}
 			if(m_is_ip_addr_assigned[IPA_IP_v6])
 			{
-				m_p_iface->eth_bridge_del_flt_rule(it_flt->flt_rule_hdl[IPA_IP_v6], IPA_IP_v6);
-				IPACMDBG_H("IPv6 flt rule %d is deleted.\n", it_flt->flt_rule_hdl[IPA_IP_v6]);
+				if(m_is_l2tp_iface)
+				{
+					m_p_iface->del_l2tp_flt_rule(it_flt->l2tp_first_pass_flt_rule_hdl[IPA_IP_v6]);
+					IPACMDBG_H("Deleted IPv6 flt rule %d.\n", it_flt->l2tp_first_pass_flt_rule_hdl[IPA_IP_v6]);
+				}
+				else
+				{
+					if(client->is_l2tp_client)
+					{
+						m_p_iface->del_l2tp_flt_rule(IPA_IP_v6, it_flt->l2tp_first_pass_flt_rule_hdl[IPA_IP_v6],
+							it_flt->l2tp_second_pass_flt_rule_hdl);
+						IPACMDBG_H("Deleted IPv6 first pass flt rule %d and second pass flt rule %d.\n",
+							it_flt->l2tp_first_pass_flt_rule_hdl[IPA_IP_v6], it_flt->l2tp_second_pass_flt_rule_hdl);
+					}
+					else
+					{
+						m_p_iface->eth_bridge_del_flt_rule(it_flt->flt_rule_hdl[IPA_IP_v6], IPA_IP_v6);
+						IPACMDBG_H("Deleted IPv6 flt rule %d.\n", it_flt->flt_rule_hdl[IPA_IP_v6]);
+					}
+				}
 			}
-
 			peer->flt_rule.erase(it_flt);
 			break;
 		}
@@ -675,21 +1207,41 @@
 	{
 		IPACMDBG_H("Delete routing rules for inter interface communication.\n");
 
-		num_rules = client->inter_iface_rt_rule_hdl[peer_l2_hdr_type].num_hdl[IPA_IP_v4];
-		for(i = 0; i < num_rules; i++)
+		if(client->is_l2tp_client == false)
 		{
-			m_p_iface->eth_bridge_del_rt_rule(client->inter_iface_rt_rule_hdl[peer_l2_hdr_type].rule_hdl[IPA_IP_v4][i], IPA_IP_v4);
-			IPACMDBG_H("IPv4 rt rule %d is deleted.\n", client->inter_iface_rt_rule_hdl[peer_l2_hdr_type].rule_hdl[IPA_IP_v4][i]);
-		}
-		client->inter_iface_rt_rule_hdl[peer_l2_hdr_type].num_hdl[IPA_IP_v4] = 0;
+			num_rules = client->inter_iface_rt_rule_hdl[peer_l2_hdr_type].num_hdl[IPA_IP_v4];
+			for(i = 0; i < num_rules; i++)
+			{
+				m_p_iface->eth_bridge_del_rt_rule(client->inter_iface_rt_rule_hdl[peer_l2_hdr_type].rule_hdl[IPA_IP_v4][i], IPA_IP_v4);
+				IPACMDBG_H("IPv4 rt rule %d is deleted.\n", client->inter_iface_rt_rule_hdl[peer_l2_hdr_type].rule_hdl[IPA_IP_v4][i]);
+			}
+			client->inter_iface_rt_rule_hdl[peer_l2_hdr_type].num_hdl[IPA_IP_v4] = 0;
 
-		num_rules = client->inter_iface_rt_rule_hdl[peer_l2_hdr_type].num_hdl[IPA_IP_v6];
-		for(i = 0; i < num_rules; i++)
-		{
-			m_p_iface->eth_bridge_del_rt_rule(client->inter_iface_rt_rule_hdl[peer_l2_hdr_type].rule_hdl[IPA_IP_v6][i], IPA_IP_v6);
-			IPACMDBG_H("IPv6 rt rule %d is deleted.\n", client->inter_iface_rt_rule_hdl[peer_l2_hdr_type].rule_hdl[IPA_IP_v6][i]);
+			num_rules = client->inter_iface_rt_rule_hdl[peer_l2_hdr_type].num_hdl[IPA_IP_v6];
+			for(i = 0; i < num_rules; i++)
+			{
+				m_p_iface->eth_bridge_del_rt_rule(client->inter_iface_rt_rule_hdl[peer_l2_hdr_type].rule_hdl[IPA_IP_v6][i], IPA_IP_v6);
+				IPACMDBG_H("IPv6 rt rule %d is deleted.\n", client->inter_iface_rt_rule_hdl[peer_l2_hdr_type].rule_hdl[IPA_IP_v6][i]);
+			}
+			client->inter_iface_rt_rule_hdl[peer_l2_hdr_type].num_hdl[IPA_IP_v6] = 0;
+
+			if(IPACM_LanToLan::get_instance()->has_l2tp_iface() == true)
+			{
+				m_p_iface->del_l2tp_rt_rule(IPA_IP_v6, client->l2tp_rt_rule_hdl[peer_l2_hdr_type].num_rt_hdl[IPA_IP_v6],
+					client->l2tp_rt_rule_hdl[peer_l2_hdr_type].first_pass_rt_rule_hdl[IPA_IP_v6]);
+			}
 		}
-		client->inter_iface_rt_rule_hdl[peer_l2_hdr_type].num_hdl[IPA_IP_v6] = 0;
+		else
+		{
+			m_p_iface->del_l2tp_rt_rule(IPA_IP_v4, client->l2tp_rt_rule_hdl[peer_l2_hdr_type].first_pass_hdr_hdl,
+				client->l2tp_rt_rule_hdl[peer_l2_hdr_type].first_pass_hdr_proc_ctx_hdl[IPA_IP_v4], client->l2tp_rt_rule_hdl[peer_l2_hdr_type].second_pass_hdr_hdl,
+				client->l2tp_rt_rule_hdl[peer_l2_hdr_type].num_rt_hdl[IPA_IP_v4], client->l2tp_rt_rule_hdl[peer_l2_hdr_type].first_pass_rt_rule_hdl[IPA_IP_v4],
+				client->l2tp_rt_rule_hdl[peer_l2_hdr_type].second_pass_rt_rule_hdl);
+
+			m_p_iface->del_l2tp_rt_rule(IPA_IP_v6, 0, client->l2tp_rt_rule_hdl[peer_l2_hdr_type].first_pass_hdr_proc_ctx_hdl[IPA_IP_v6],
+				0, client->l2tp_rt_rule_hdl[peer_l2_hdr_type].num_rt_hdl[IPA_IP_v6], client->l2tp_rt_rule_hdl[peer_l2_hdr_type].first_pass_rt_rule_hdl[IPA_IP_v6],
+				NULL);
+		}
 	}
 	else
 	{
@@ -786,13 +1338,49 @@
 	{
 		if(m_is_ip_addr_assigned[IPA_IP_v4])
 		{
-			m_p_iface->eth_bridge_del_flt_rule(it->flt_rule_hdl[IPA_IP_v4], IPA_IP_v4);
-			IPACMDBG_H("IPv4 flt rule %d is deleted.\n", it->flt_rule_hdl[IPA_IP_v4]);
+			if(m_is_l2tp_iface)
+			{
+				IPACMDBG_H("No IPv4 client flt rule on l2tp iface.\n");
+			}
+			else
+			{
+				if(it->p_client->is_l2tp_client)
+				{
+					m_p_iface->del_l2tp_flt_rule(IPA_IP_v4, it->l2tp_first_pass_flt_rule_hdl[IPA_IP_v4],
+						it->l2tp_second_pass_flt_rule_hdl);
+					it->l2tp_second_pass_flt_rule_hdl = 0;
+					IPACMDBG_H("Deleted IPv4 first pass flt rule %d and second pass flt rule %d.\n",
+						it->l2tp_first_pass_flt_rule_hdl[IPA_IP_v4], it->l2tp_second_pass_flt_rule_hdl);
+				}
+				else
+				{
+					m_p_iface->eth_bridge_del_flt_rule(it->flt_rule_hdl[IPA_IP_v4], IPA_IP_v4);
+					IPACMDBG_H("Deleted IPv4 flt rule %d.\n", it->flt_rule_hdl[IPA_IP_v4]);
+				}
+			}
 		}
 		if(m_is_ip_addr_assigned[IPA_IP_v6])
 		{
-			m_p_iface->eth_bridge_del_flt_rule(it->flt_rule_hdl[IPA_IP_v6], IPA_IP_v6);
-			IPACMDBG_H("IPv6 flt rule %d is deleted.\n", it->flt_rule_hdl[IPA_IP_v6]);
+			if(m_is_l2tp_iface)
+			{
+				m_p_iface->del_l2tp_flt_rule(it->l2tp_first_pass_flt_rule_hdl[IPA_IP_v6]);
+				IPACMDBG_H("Deleted IPv6 flt rule %d.\n", it->l2tp_first_pass_flt_rule_hdl[IPA_IP_v6]);
+			}
+			else
+			{
+				if(it->p_client->is_l2tp_client)
+				{
+					m_p_iface->del_l2tp_flt_rule(IPA_IP_v6, it->l2tp_first_pass_flt_rule_hdl[IPA_IP_v6],
+						it->l2tp_second_pass_flt_rule_hdl);
+					IPACMDBG_H("Deleted IPv6 first pass flt rule %d and second pass flt rule %d.\n",
+						it->l2tp_first_pass_flt_rule_hdl[IPA_IP_v6], it->l2tp_second_pass_flt_rule_hdl);
+				}
+				else
+				{
+					m_p_iface->eth_bridge_del_flt_rule(it->flt_rule_hdl[IPA_IP_v6], IPA_IP_v6);
+					IPACMDBG_H("Deleted IPv6 flt rule %d.\n", it->flt_rule_hdl[IPA_IP_v6]);
+				}
+			}
 		}
 	}
 	peer->flt_rule.clear();
@@ -811,6 +1399,11 @@
 		{
 			del_client_rt_rule(peer, &(*it));
 		}
+		if(IPACM_LanToLan::get_instance()->has_l2tp_iface() == true)
+		{
+			m_p_iface->eth_bridge_del_hdr_proc_ctx(hdr_proc_ctx_for_l2tp);
+			hdr_proc_ctx_for_l2tp = 0;
+		}
 	}
 
 	return;
@@ -944,7 +1537,7 @@
 	return;
 }
 
-void IPACM_LanToLan_Iface::handle_client_add(uint8_t *mac)
+void IPACM_LanToLan_Iface::handle_client_add(uint8_t *mac, bool is_l2tp_client, l2tp_vlan_mapping_info *mapping_info)
 {
 	list<client_info>::iterator it_client;
 	list<peer_iface_info>::iterator it_peer_info;
@@ -966,7 +1559,11 @@
 		return;
 	}
 
+	IPACMDBG_H("is_l2tp_client: %d, mapping_info: %p\n", is_l2tp_client, mapping_info);
+	memset(&new_client, 0, sizeof(new_client));
 	memcpy(new_client.mac_addr, mac, sizeof(new_client.mac_addr));
+	new_client.is_l2tp_client = is_l2tp_client;
+	new_client.mapping_info = mapping_info;
 	m_client_info.push_front(new_client);
 
 	client_info &front_client = m_client_info.front();
@@ -981,7 +1578,14 @@
 			if(flag[it_peer_info->peer->get_iface_pointer()->tx_prop->tx[0].hdr_l2_type] == false)
 			{
 				/* add client routing rule for each peer interface */
-				add_client_rt_rule(&(*it_peer_info), &front_client);
+				if(front_client.is_l2tp_client == false)
+				{
+					add_client_rt_rule(&(*it_peer_info), &front_client);
+				}
+#ifdef FEATURE_L2TP
+				/* add l2tp rt rules */
+				add_l2tp_client_rt_rule(&(*it_peer_info), &front_client);
+#endif
 				flag[it_peer_info->peer->get_iface_pointer()->tx_prop->tx[0].hdr_l2_type] = true;
 			}
 
@@ -1042,6 +1646,12 @@
 				{
 					IPACMDBG_H("Delete client routing rule for peer interface.\n");
 					del_client_rt_rule(&(*it_peer_info), &(*it_client));
+					if(it_client->is_l2tp_client == false && IPACM_LanToLan::get_instance()->has_l2tp_iface() == true
+						&& m_client_info.size() == 1)
+					{
+						m_p_iface->eth_bridge_del_hdr_proc_ctx(hdr_proc_ctx_for_l2tp);
+						hdr_proc_ctx_for_l2tp = 0;
+					}
 					flag[it_peer_info->peer->get_iface_pointer()->tx_prop->tx[0].hdr_l2_type] = true;
 				}
 			}
@@ -1105,6 +1715,7 @@
 	IPACMDBG_H("Is IPv6 addr assigned? %d\n", m_is_ip_addr_assigned[IPA_IP_v6]);
 	IPACMDBG_H("Support inter interface offload? %d\n", m_support_inter_iface_offload);
 	IPACMDBG_H("Support intra interface offload? %d\n", m_support_intra_iface_offload);
+	IPACMDBG_H("Is l2tp interface? %d\n", m_is_l2tp_iface);
 
 	if(m_support_inter_iface_offload)
 	{
@@ -1123,12 +1734,19 @@
 		IPACMDBG_H("Hdr proc ctx for intra-interface: %d\n", hdr_proc_ctx_for_intra_interface);
 	}
 
+	IPACMDBG_H("Hdr proc ctx for l2tp: %d\n", hdr_proc_ctx_for_l2tp);
+
 	i = 1;
 	IPACMDBG_H("There are %d clients in total.\n", m_client_info.size());
 	for(it_client = m_client_info.begin(); it_client != m_client_info.end(); it_client++)
 	{
 		IPACMDBG_H("Client %d MAC: 0x%02x%02x%02x%02x%02x%02x Pointer: 0x%08x\n", i, it_client->mac_addr[0], it_client->mac_addr[1],
 			it_client->mac_addr[2], it_client->mac_addr[3], it_client->mac_addr[4], it_client->mac_addr[5], &(*it_client));
+		IPACMDBG_H("Is l2tp client? %d\n", it_client->is_l2tp_client);
+		if(it_client->is_l2tp_client && it_client->mapping_info)
+		{
+			IPACMDBG_H("Vlan iface associated with this client: %s\n", it_client->mapping_info->vlan_iface_name);
+		}
 
 		if(m_support_inter_iface_offload)
 		{
@@ -1149,6 +1767,54 @@
 					{
 						IPACMDBG_H("%d\n", it_client->inter_iface_rt_rule_hdl[j].rule_hdl[IPA_IP_v6][k]);
 					}
+
+					if(it_client->is_l2tp_client)
+					{
+						IPACMDBG_H("Printing l2tp hdr info for l2tp client.\n");
+						IPACMDBG_H("First pass hdr hdl: %d, IPv4 hdr proc ctx hdl: IPv6 hdr proc ctx hdl: %d\n",
+							it_client->l2tp_rt_rule_hdl[j].first_pass_hdr_hdl, it_client->l2tp_rt_rule_hdl[j].first_pass_hdr_proc_ctx_hdl[IPA_IP_v4],
+							it_client->l2tp_rt_rule_hdl[j].first_pass_hdr_proc_ctx_hdl[IPA_IP_v6]);
+						IPACMDBG_H("Second pass hdr hdl: %d\n", it_client->l2tp_rt_rule_hdl[j].second_pass_hdr_hdl);
+
+						IPACMDBG_H("Printing l2tp routing rule info for l2tp client.\n");
+						IPACMDBG_H("Number of IPv4 routing rules is %d, first pass handles:\n", it_client->l2tp_rt_rule_hdl[j].num_rt_hdl[IPA_IP_v4]);
+						for(k = 0; k < it_client->l2tp_rt_rule_hdl[j].num_rt_hdl[IPA_IP_v4]; k++)
+						{
+							IPACMDBG_H("%d\n", it_client->l2tp_rt_rule_hdl[j].first_pass_rt_rule_hdl[IPA_IP_v4][k]);
+						}
+						IPACMDBG_H("Number of IPv6 routing rules is %d, first pass handles:\n", it_client->l2tp_rt_rule_hdl[j].num_rt_hdl[IPA_IP_v6]);
+						for(k = 0; k < it_client->l2tp_rt_rule_hdl[j].num_rt_hdl[IPA_IP_v6]; k++)
+						{
+							IPACMDBG_H("%d\n", it_client->l2tp_rt_rule_hdl[j].first_pass_rt_rule_hdl[IPA_IP_v6][k]);
+						}
+						IPACMDBG_H("Second pass handles:\n");
+						for(k = 0; k < it_client->l2tp_rt_rule_hdl[j].num_rt_hdl[IPA_IP_v6]; k++)
+						{
+							IPACMDBG_H("%d\n", it_client->l2tp_rt_rule_hdl[j].second_pass_rt_rule_hdl[k]);
+						}
+					}
+					else
+					{
+						if(IPACM_LanToLan::get_instance()->has_l2tp_iface())
+						{
+							IPACMDBG_H("Printing l2tp hdr info for non l2tp client.\n");
+							IPACMDBG_H("Hdr hdl: %d, IPv4 hdr proc ctx hdl: IPv6 hdr proc ctx hdl: %d\n",
+								it_client->l2tp_rt_rule_hdl[j].first_pass_hdr_hdl, it_client->l2tp_rt_rule_hdl[j].first_pass_hdr_proc_ctx_hdl[IPA_IP_v4],
+								it_client->l2tp_rt_rule_hdl[j].first_pass_hdr_proc_ctx_hdl[IPA_IP_v6]);
+
+							IPACMDBG_H("Printing l2tp routing rule info for non l2tp client.\n");
+							IPACMDBG_H("Number of IPv4 routing rules is %d, handles:\n", it_client->l2tp_rt_rule_hdl[j].num_rt_hdl[IPA_IP_v4]);
+							for(k = 0; k < it_client->l2tp_rt_rule_hdl[j].num_rt_hdl[IPA_IP_v4]; k++)
+							{
+								IPACMDBG_H("%d\n", it_client->l2tp_rt_rule_hdl[j].first_pass_rt_rule_hdl[IPA_IP_v4][k]);
+							}
+							IPACMDBG_H("Number of IPv6 routing rules is %d, handles:\n", it_client->l2tp_rt_rule_hdl[j].num_rt_hdl[IPA_IP_v6]);
+							for(k = 0; k < it_client->l2tp_rt_rule_hdl[j].num_rt_hdl[IPA_IP_v6]; k++)
+							{
+								IPACMDBG_H("%d\n", it_client->l2tp_rt_rule_hdl[j].first_pass_rt_rule_hdl[IPA_IP_v6][k]);
+							}
+						}
+					}
 				}
 			}
 		}
@@ -1200,11 +1866,14 @@
 		if(m_is_ip_addr_assigned[IPA_IP_v4])
 		{
 			IPACMDBG_H("IPv4 %d\n", it_flt->flt_rule_hdl[IPA_IP_v4]);
+			IPACMDBG_H("IPv4 l2tp first pass flt rule: %d\n", it_flt->l2tp_first_pass_flt_rule_hdl[IPA_IP_v4]);
 		}
 		if(m_is_ip_addr_assigned[IPA_IP_v6])
 		{
 			IPACMDBG_H("IPv6 %d\n", it_flt->flt_rule_hdl[IPA_IP_v6]);
+			IPACMDBG_H("IPv6 l2tp first pass flt rule: %d\n", it_flt->l2tp_first_pass_flt_rule_hdl[IPA_IP_v6]);
 		}
+		IPACMDBG_H("L2tp second pass flt rule: %d\n", it_flt->l2tp_second_pass_flt_rule_hdl);
 	}
 
 	return;
@@ -1260,3 +1929,120 @@
 
 	return;
 }
+
+bool IPACM_LanToLan_Iface::set_l2tp_iface(char *vlan_iface_name)
+{
+	IPACMDBG_H("Self iface %s, vlan iface %s\n", m_p_iface->dev_name,
+		vlan_iface_name);
+
+	if(m_is_l2tp_iface == false)
+	{
+		if(strncmp(m_p_iface->dev_name, vlan_iface_name, strlen(m_p_iface->dev_name)) == 0)
+		{
+			IPACMDBG_H("This interface is l2tp interface.\n");
+			m_is_l2tp_iface = true;
+			switch_to_l2tp_iface();
+		}
+	}
+	return m_is_l2tp_iface;
+}
+
+bool IPACM_LanToLan_Iface::is_l2tp_iface()
+{
+	return m_is_l2tp_iface;
+}
+
+void IPACM_LanToLan_Iface::switch_to_l2tp_iface()
+{
+	list<peer_iface_info>::iterator it_peer;
+	list<flt_rule_info>::iterator it_flt;
+
+	for(it_peer = m_peer_iface_info.begin(); it_peer != m_peer_iface_info.end(); it_peer++)
+	{
+		for(it_flt = it_peer->flt_rule.begin(); it_flt != it_peer->flt_rule.end(); it_flt++)
+		{
+			if(m_is_ip_addr_assigned[IPA_IP_v4])
+			{
+				m_p_iface->eth_bridge_del_flt_rule(it_flt->flt_rule_hdl[IPA_IP_v4], IPA_IP_v4);
+				IPACMDBG_H("Deleted IPv4 flt rule %d.\n", it_flt->flt_rule_hdl[IPA_IP_v4]);
+			}
+			if(m_is_ip_addr_assigned[IPA_IP_v6])
+			{
+				m_p_iface->eth_bridge_del_flt_rule(it_flt->flt_rule_hdl[IPA_IP_v6], IPA_IP_v6);
+				m_p_iface->add_l2tp_flt_rule(it_flt->p_client->mac_addr, &it_flt->l2tp_first_pass_flt_rule_hdl[IPA_IP_v6]);
+				IPACMDBG_H("Deleted IPv6 flt rule %d.\n", it_flt->flt_rule_hdl[IPA_IP_v6]);
+			}
+		}
+	}
+	return;
+}
+
+void IPACM_LanToLan_Iface::handle_l2tp_enable()
+{
+	int i;
+	ipa_hdr_l2_type peer_l2_hdr_type;
+	list<peer_iface_info>::iterator it_peer_info;
+	list<client_info>::iterator it_client;
+	bool flag[IPA_HDR_L2_MAX];
+
+	if(m_support_inter_iface_offload)
+	{
+		memset(flag, 0, sizeof(flag));
+		for(it_peer_info = m_peer_iface_info.begin(); it_peer_info != m_peer_iface_info.end(); it_peer_info++)
+		{
+			if(it_peer_info->peer->is_l2tp_iface())
+			{
+				peer_l2_hdr_type = it_peer_info->peer->get_iface_pointer()->tx_prop->tx[0].hdr_l2_type;
+				flag[peer_l2_hdr_type] = true;
+			}
+		}
+
+		for(i = 0; i < IPA_HDR_L2_MAX; i++)
+		{
+			if(flag[i] == true)
+			{
+				IPACMDBG_H("Add rt rule for peer l2 type %s\n", ipa_l2_hdr_type[i]);
+				for(it_client = m_client_info.begin(); it_client != m_client_info.end(); it_client++)
+				{
+					m_p_iface->add_l2tp_rt_rule(IPA_IP_v6, it_client->mac_addr, &hdr_proc_ctx_for_l2tp,
+						&it_client->l2tp_rt_rule_hdl[i].num_rt_hdl[IPA_IP_v6],
+						it_client->l2tp_rt_rule_hdl[i].first_pass_rt_rule_hdl[IPA_IP_v6]);
+				}
+			}
+		}
+	}
+	return;
+}
+
+void IPACM_LanToLan_Iface::handle_l2tp_disable()
+{
+	int i;
+	ipa_hdr_l2_type peer_l2_hdr_type;
+	list<peer_iface_info>::iterator it_peer_info;
+	list<client_info>::iterator it_client;
+	bool flag[IPA_HDR_L2_MAX];
+
+	if(m_support_inter_iface_offload)
+	{
+		memset(flag, 0, sizeof(flag));
+		for(it_peer_info = m_peer_iface_info.begin(); it_peer_info != m_peer_iface_info.end(); it_peer_info++)
+		{
+			peer_l2_hdr_type = it_peer_info->peer->get_iface_pointer()->tx_prop->tx[0].hdr_l2_type;
+			flag[peer_l2_hdr_type] = true;
+		}
+
+		for(i = 0; i < IPA_HDR_L2_MAX; i++)
+		{
+			if(flag[i] == true)
+			{
+				IPACMDBG_H("Delete rt rule for peer l2 type %s\n", ipa_l2_hdr_type[i]);
+				for(it_client = m_client_info.begin(); it_client != m_client_info.end(); it_client++)
+				{
+					m_p_iface->del_l2tp_rt_rule(IPA_IP_v6, it_client->l2tp_rt_rule_hdl[i].num_rt_hdl[IPA_IP_v6],
+						it_client->l2tp_rt_rule_hdl[i].first_pass_rt_rule_hdl[IPA_IP_v6]);
+				}
+			}
+		}
+	}
+	return;
+}
diff --git a/ipacm/src/IPACM_Main.cpp b/ipacm/src/IPACM_Main.cpp
index 16ec73a..6bd0cbd 100644
--- a/ipacm/src/IPACM_Main.cpp
+++ b/ipacm/src/IPACM_Main.cpp
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
@@ -232,6 +232,8 @@
 	ipacm_event_data_wlan_ex *data_ex;
 	ipa_get_data_stats_resp_msg_v01 *data_tethering_stats = NULL;
 	ipa_get_apn_data_stats_resp_msg_v01 *data_network_stats = NULL;
+	ipa_ioc_vlan_iface_info *vlan_info = NULL;
+	ipa_ioc_l2tp_vlan_mapping_info *mapping = NULL;
 
 	ipacm_cmd_q_data new_neigh_evt;
 	ipacm_event_data_all* new_neigh_data;
@@ -677,6 +679,7 @@
 			evt_data.event = IPA_NETWORK_STATS_UPDATE_EVENT;
 			evt_data.evt_data = data_network_stats;
 			break;
+
 #ifdef FEATURE_IPACM_HAL
 		case IPA_QUOTA_REACH:
 			IPACMDBG_H("Received IPA_QUOTA_REACH\n");
@@ -706,6 +709,55 @@
 			}
 			break;
 #endif
+#ifdef FEATURE_L2TP
+		case ADD_VLAN_IFACE:
+			vlan_info = (ipa_ioc_vlan_iface_info *)malloc(sizeof(*vlan_info));
+			if(vlan_info == NULL)
+			{
+				IPACMERR("Failed to allocate memory.\n");
+				return NULL;
+			}
+			memcpy(vlan_info, buffer + sizeof(struct ipa_msg_meta), sizeof(*vlan_info));
+			evt_data.event = IPA_ADD_VLAN_IFACE;
+			evt_data.evt_data = vlan_info;
+			break;
+
+		case DEL_VLAN_IFACE:
+			vlan_info = (ipa_ioc_vlan_iface_info *)malloc(sizeof(*vlan_info));
+			if(vlan_info == NULL)
+			{
+				IPACMERR("Failed to allocate memory.\n");
+				return NULL;
+			}
+			memcpy(vlan_info, buffer + sizeof(struct ipa_msg_meta), sizeof(*vlan_info));
+			evt_data.event = IPA_DEL_VLAN_IFACE;
+			evt_data.evt_data = vlan_info;
+			break;
+
+		case ADD_L2TP_VLAN_MAPPING:
+			mapping = (ipa_ioc_l2tp_vlan_mapping_info *)malloc(sizeof(*mapping));
+			if(mapping == NULL)
+			{
+				IPACMERR("Failed to allocate memory.\n");
+				return NULL;
+			}
+			memcpy(mapping, buffer + sizeof(struct ipa_msg_meta), sizeof(*mapping));
+			evt_data.event = IPA_ADD_L2TP_VLAN_MAPPING;
+			evt_data.evt_data = mapping;
+			break;
+
+		case DEL_L2TP_VLAN_MAPPING:
+			mapping = (ipa_ioc_l2tp_vlan_mapping_info *)malloc(sizeof(*mapping));
+			if(mapping == NULL)
+			{
+				IPACMERR("Failed to allocate memory.\n");
+				return NULL;
+			}
+			memcpy(mapping, buffer + sizeof(struct ipa_msg_meta), sizeof(*mapping));
+			evt_data.event = IPA_DEL_L2TP_VLAN_MAPPING;
+			evt_data.evt_data = mapping;
+			break;
+#endif
 		default:
 			IPACMDBG_H("Unhandled message type: %d\n", event_hdr.msg_type);
 			continue;
@@ -782,7 +834,7 @@
 #endif
 
 #ifdef FEATURE_ETH_BRIDGE_LE
-	IPACM_LanToLan* lan2lan = new IPACM_LanToLan();
+	IPACM_LanToLan* lan2lan = IPACM_LanToLan::get_instance();
 #endif
 
 	CtList = new IPACM_ConntrackListener();
diff --git a/ipacm/src/IPACM_Neighbor.cpp b/ipacm/src/IPACM_Neighbor.cpp
index 7f73bbd..8d5a16f 100644
--- a/ipacm/src/IPACM_Neighbor.cpp
+++ b/ipacm/src/IPACM_Neighbor.cpp
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2013, The Linux Foundation. All rights reserved.
+Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
@@ -133,6 +133,10 @@
 							memcpy(data_all->mac_addr,
 									neighbor_client[i].mac_addr,
 												sizeof(data_all->mac_addr));
+#ifdef FEATURE_L2TP
+							memcpy(data_all->iface_name, neighbor_client[i].iface_name,
+								sizeof(data_all->iface_name));
+#endif
 							evt_data.evt_data = (void *)data_all;
 							IPACM_EvtDispatcher::PostEvt(&evt_data);
 							/* ask for replaced iface name*/
@@ -143,7 +147,7 @@
 							} else {
 								IPACMDBG_H("Posted event %d, with %s for ipv4 client re-connect\n",
 									evt_data.event,
-									IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].iface_name);
+									data_all->iface_name);
 							}
 						}
 					}
@@ -166,11 +170,13 @@
 
 			ipacm_event_data_all *data = (ipacm_event_data_all *)param;
 			ipa_interface_index = IPACM_Iface::iface_ipa_index_query(data->if_index);
+#ifndef FEATURE_L2TP
 			/* check for failure return */
 			if (IPACM_FAILURE == ipa_interface_index) {
 				IPACMERR("not supported iface id: %d\n", data->if_index);
 				break;
 			}
+#endif
 			if (data->iptype == IPA_IP_v4)
 			{
 				if (data->ipv4_addr != 0) /* not 0.0.0.0 */
@@ -183,7 +189,7 @@
 						return;
 					}
 					/* check if iface is bridge interface*/
-					if (strcmp(IPACM_Iface::ipacmcfg->ipa_virtual_iface_name, IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].iface_name) == 0)
+					if (strcmp(IPACM_Iface::ipacmcfg->ipa_virtual_iface_name, data->iface_name) == 0)
 					{
 						/* searh if seen this client or not*/
 						for (i = 0; i < num_neighbor_client_temp; i++)
@@ -191,6 +197,7 @@
 							if (memcmp(neighbor_client[i].mac_addr, data->mac_addr, sizeof(neighbor_client[i].mac_addr)) == 0)
 							{
 								data->if_index = neighbor_client[i].iface_index;
+								strlcpy(data->iface_name, neighbor_client[i].iface_name, sizeof(data->iface_name));
 								neighbor_client[i].v4_addr = data->ipv4_addr; // cache client's previous ipv4 address
 								/* construct IPA_NEIGH_CLIENT_IP_ADDR_ADD_EVENT command and insert to command-queue */
 								if (event == IPA_NEW_NEIGH_EVENT)
@@ -217,7 +224,7 @@
 									IPACMDBG_H("Posted event %d,\
 										with %s for ipv4\n",
 										evt_data.event,
-										IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].iface_name);
+										data->iface_name);
 								}
 								break;
 							}
@@ -240,10 +247,9 @@
 									neighbor_client[i].iface_index = data->if_index;
 									neighbor_client[i].ipa_if_num = ipa_interface_index;
 									neighbor_client[i].v4_addr = data->ipv4_addr; // cache client's previous ipv4 address
+									strlcpy(neighbor_client[i].iface_name, data->iface_name, sizeof(neighbor_client[i].iface_name));
 									IPACMDBG_H("update cache %d-entry, with %s iface, ipv4 address: 0x%x\n",
-													i,
-													IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].iface_name,
-													data->ipv4_addr);
+										i, data->iface_name, data->ipv4_addr);
 									break;
 								}
 							}
@@ -259,6 +265,8 @@
 									/* cache the network interface client associated */
 									neighbor_client[num_neighbor_client_temp].ipa_if_num = ipa_interface_index;
 									neighbor_client[num_neighbor_client_temp].v4_addr = data->ipv4_addr;
+									strlcpy(neighbor_client[num_neighbor_client_temp].iface_name,
+										data->iface_name, sizeof(neighbor_client[num_neighbor_client_temp].iface_name));
 									num_neighbor_client++;
 									IPACMDBG_H("Cache client MAC %02x:%02x:%02x:%02x:%02x:%02x\n, total client: %d\n",
 												neighbor_client[num_neighbor_client_temp].mac_addr[0],
@@ -280,6 +288,8 @@
 									/* cache the network interface client associated */
 									neighbor_client[circular_index].ipa_if_num = ipa_interface_index;
 									neighbor_client[circular_index].v4_addr = 0;
+									strlcpy(neighbor_client[circular_index].iface_name,
+										data->iface_name, sizeof(neighbor_client[circular_index].iface_name));
 									IPACMDBG_H("Copy wlan-iface client MAC %02x:%02x:%02x:%02x:%02x:%02x\n, total client: %d, circular %d\n",
 													neighbor_client[circular_index].mac_addr[0],
 													neighbor_client[circular_index].mac_addr[1],
@@ -316,6 +326,7 @@
 									neighbor_client[i].iface_index = 0;
 									neighbor_client[i].v4_addr = 0;
 									neighbor_client[i].ipa_if_num = 0;
+									memset(neighbor_client[i].iface_name, 0, sizeof(neighbor_client[i].iface_name));
 									for (; i < num_neighbor_client_temp - 1; i++)
 									{
 										memcpy(neighbor_client[i].mac_addr,
@@ -324,6 +335,8 @@
 										neighbor_client[i].iface_index = neighbor_client[i+1].iface_index;
 										neighbor_client[i].v4_addr = neighbor_client[i+1].v4_addr;
 										neighbor_client[i].ipa_if_num = neighbor_client[i+1].ipa_if_num;
+										strlcpy(neighbor_client[i].iface_name, neighbor_client[i+1].iface_name,
+											sizeof(neighbor_client[i].iface_name));
 									}
 									num_neighbor_client--;
 									IPACMDBG_H(" total number of left cased clients: %d\n", num_neighbor_client);
@@ -343,8 +356,7 @@
 						evt_data.evt_data = (void *)data_all;
 						IPACM_EvtDispatcher::PostEvt(&evt_data);
 						IPACMDBG_H("Posted event %d with %s for ipv4\n",
-									evt_data.event,
-									IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].iface_name);
+							evt_data.event, data->iface_name);
 					}
 				}
 			}
@@ -355,7 +367,7 @@
 				{
 					IPACMDBG("Got New_Neighbor event with ipv6 address \n");
 					/* check if iface is bridge interface*/
-					if (strcmp(IPACM_Iface::ipacmcfg->ipa_virtual_iface_name, IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].iface_name) == 0)
+					if (strcmp(IPACM_Iface::ipacmcfg->ipa_virtual_iface_name, data->iface_name) == 0)
 					{
 						/* searh if seen this client or not*/
 						for (i = 0; i < num_neighbor_client_temp; i++)
@@ -363,6 +375,7 @@
 							if (memcmp(neighbor_client[i].mac_addr, data->mac_addr, sizeof(neighbor_client[i].mac_addr)) == 0)
 							{
 								data->if_index = neighbor_client[i].iface_index;
+								strlcpy(data->iface_name, neighbor_client[i].iface_name, sizeof(data->iface_name));
 								/* construct IPA_NEIGH_CLIENT_IP_ADDR_ADD_EVENT command and insert to command-queue */
 								if (event == IPA_NEW_NEIGH_EVENT) evt_data.event = IPA_NEIGH_CLIENT_IP_ADDR_ADD_EVENT;
 								else evt_data.event = IPA_NEIGH_CLIENT_IP_ADDR_DEL_EVENT;
@@ -384,7 +397,7 @@
 									IPACMDBG_H("Posted event %d,\
 										with %s for ipv6\n",
 										evt_data.event,
-										IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].iface_name);
+										data->iface_name);
 								}
 								break;
 							};
@@ -407,8 +420,7 @@
 						evt_data.evt_data = (void *)data_all;
 						IPACM_EvtDispatcher::PostEvt(&evt_data);
 						IPACMDBG_H("Posted event %d with %s for ipv6\n",
-										evt_data.event,
-										IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].iface_name);
+							evt_data.event, data->iface_name);
 					}
 				}
 				else
@@ -430,13 +442,14 @@
 												neighbor_client[i].mac_addr[5],
 												num_neighbor_client);
 							/* check if iface is not bridge interface*/
-							if (strcmp(IPACM_Iface::ipacmcfg->ipa_virtual_iface_name, IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].iface_name) != 0)
+							if (strcmp(IPACM_Iface::ipacmcfg->ipa_virtual_iface_name, data->iface_name) != 0)
 							{
 								/* use previous ipv4 first */
 								if(data->if_index != neighbor_client[i].iface_index)
 								{
 									IPACMDBG_H("update new kernel iface index \n");
 									neighbor_client[i].iface_index = data->if_index;
+									strlcpy(neighbor_client[i].iface_name, data->iface_name, sizeof(neighbor_client[i].iface_name));
 								}
 
 								/* check if client associated with previous network interface */
@@ -461,22 +474,13 @@
 									data_all->iptype = IPA_IP_v4;
 									data_all->if_index = neighbor_client[i].iface_index;
 									data_all->ipv4_addr = neighbor_client[i].v4_addr; //use previous ipv4 address
-									memcpy(data_all->mac_addr,
-											neighbor_client[i].mac_addr,
-														sizeof(data_all->mac_addr));
+									memcpy(data_all->mac_addr, neighbor_client[i].mac_addr,
+										sizeof(data_all->mac_addr));
+									strlcpy(data_all->iface_name, neighbor_client[i].iface_name, sizeof(data_all->iface_name));
 									evt_data.evt_data = (void *)data_all;
 									IPACM_EvtDispatcher::PostEvt(&evt_data);
-									/* ask for replaced iface name*/
-									ipa_interface_index = IPACM_Iface::iface_ipa_index_query(data_all->if_index);
-									/* check for failure return */
-									if (IPACM_FAILURE == ipa_interface_index) {
-										IPACMERR("not supported iface id: %d\n", data_all->if_index);
-									} else {
-										IPACMDBG_H("Posted event %d,\
-											with %s for ipv4 client re-connect\n",
-											evt_data.event,
-											IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].iface_name);
-									}
+									IPACMDBG_H("Posted event %d with %s for ipv4\n",
+										evt_data.event, data_all->iface_name);
 								}
 							}
 							/* delete cache neighbor entry */
@@ -496,6 +500,7 @@
 								neighbor_client[i].iface_index = 0;
 								neighbor_client[i].v4_addr = 0;
 								neighbor_client[i].ipa_if_num = 0;
+								memset(neighbor_client[i].iface_name, 0, sizeof(neighbor_client[i].iface_name));
 								for (; i < num_neighbor_client_temp - 1; i++)
 								{
 									memcpy(neighbor_client[i].mac_addr,
@@ -504,6 +509,8 @@
 									neighbor_client[i].iface_index = neighbor_client[i+1].iface_index;
 									neighbor_client[i].v4_addr = neighbor_client[i+1].v4_addr;
 									neighbor_client[i].ipa_if_num = neighbor_client[i+1].ipa_if_num;
+									strlcpy(neighbor_client[i].iface_name, neighbor_client[i+1].iface_name,
+										sizeof(neighbor_client[i].iface_name));
 								}
 								num_neighbor_client--;
 								IPACMDBG_H(" total number of left cased clients: %d\n", num_neighbor_client);
@@ -515,7 +522,7 @@
 					if ((i == num_neighbor_client_temp) && (event == IPA_NEW_NEIGH_EVENT))
 					{
 						/* check if iface is not bridge interface*/
-						if (strcmp(IPACM_Iface::ipacmcfg->ipa_virtual_iface_name, IPACM_Iface::ipacmcfg->iface_table[ipa_interface_index].iface_name) != 0)
+						if (strcmp(IPACM_Iface::ipacmcfg->ipa_virtual_iface_name, data->iface_name) != 0)
 						{
 							if (num_neighbor_client_temp < IPA_MAX_NUM_NEIGHBOR_CLIENTS)
 							{
@@ -526,6 +533,8 @@
 								/* cache the network interface client associated */
 								neighbor_client[num_neighbor_client_temp].ipa_if_num = ipa_interface_index;
 								neighbor_client[num_neighbor_client_temp].v4_addr = 0;
+								strlcpy(neighbor_client[num_neighbor_client_temp].iface_name, data->iface_name,
+									sizeof(neighbor_client[num_neighbor_client_temp].iface_name));
 								num_neighbor_client++;
 								IPACMDBG_H("Copy client MAC %02x:%02x:%02x:%02x:%02x:%02x\n, total client: %d\n",
 												neighbor_client[num_neighbor_client_temp].mac_addr[0],
@@ -547,6 +556,8 @@
 								/* cache the network interface client associated */
 								neighbor_client[circular_index].ipa_if_num = ipa_interface_index;
 								neighbor_client[circular_index].v4_addr = 0;
+								strlcpy(neighbor_client[circular_index].iface_name, data->iface_name,
+									sizeof(neighbor_client[circular_index].iface_name));
 								IPACMDBG_H("Copy wlan-iface client MAC %02x:%02x:%02x:%02x:%02x:%02x\n, total client: %d, circular %d\n",
 												neighbor_client[circular_index].mac_addr[0],
 												neighbor_client[circular_index].mac_addr[1],
diff --git a/ipacm/src/IPACM_Netlink.cpp b/ipacm/src/IPACM_Netlink.cpp
index 186e196..b5fadcb 100644
--- a/ipacm/src/IPACM_Netlink.cpp
+++ b/ipacm/src/IPACM_Netlink.cpp
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2013, The Linux Foundation. All rights reserved.
+Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
@@ -849,6 +849,9 @@
 
 				evt_data.event = IPA_ADDR_ADD_EVENT;
 				data_addr->if_index = msg_ptr->nl_addr_info.metainfo.ifa_index;
+#ifdef FEATURE_L2TP
+				strlcpy(data_addr->iface_name, dev_name, sizeof(data_addr->iface_name));
+#endif
 				if(AF_INET6 == msg_ptr->nl_addr_info.attr_info.prefix_addr.ss_family)
 				{
 				    IPACMDBG("Posting IPA_ADDR_ADD_EVENT with if index:%d, ipv6 addr:0x%x:%x:%x:%x\n",
@@ -1421,6 +1424,9 @@
 		    			 msg_ptr->nl_neigh_info.attr_info.lladdr_hwaddr.sa_data,
 		    			 sizeof(data_all->mac_addr));
 			data_all->if_index = msg_ptr->nl_neigh_info.metainfo.ndm_ifindex;
+#ifdef FEATURE_L2TP
+			strlcpy(data_all->iface_name, dev_name, sizeof(data_all->iface_name));
+#endif
 			/* Add support to replace src-mac as bridge0 mac */
 			if((msg_ptr->nl_neigh_info.metainfo.ndm_family == AF_BRIDGE) &&
 				(msg_ptr->nl_neigh_info.metainfo.ndm_state == NUD_PERMANENT))
diff --git a/ipacm/src/IPACM_Wlan.cpp b/ipacm/src/IPACM_Wlan.cpp
index b47c5ef..54a2e66 100644
--- a/ipacm/src/IPACM_Wlan.cpp
+++ b/ipacm/src/IPACM_Wlan.cpp
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
@@ -683,7 +683,7 @@
 				{
 					if(data->attribs[i].attrib_type == WLAN_HDR_ATTRIB_MAC_ADDR)
 					{
-						eth_bridge_post_event(IPA_ETH_BRIDGE_CLIENT_ADD, IPA_IP_MAX, data->attribs[i].u.mac_addr);
+						eth_bridge_post_event(IPA_ETH_BRIDGE_CLIENT_ADD, IPA_IP_MAX, data->attribs[i].u.mac_addr, NULL, NULL);
 						break;
 					}
 				}
@@ -700,7 +700,7 @@
 			if (ipa_interface_index == ipa_if_num)
 			{
 				IPACMDBG_H("Received IPA_WLAN_CLIENT_DEL_EVENT\n");
-				eth_bridge_post_event(IPA_ETH_BRIDGE_CLIENT_DEL, IPA_IP_MAX, data->mac_addr);
+				eth_bridge_post_event(IPA_ETH_BRIDGE_CLIENT_DEL, IPA_IP_MAX, data->mac_addr, NULL, NULL);
 				handle_wlan_client_down_evt(data->mac_addr);
 			}
 		}
@@ -802,7 +802,7 @@
 		{
 			handle_SCC_MCC_switch(ip_type);
 		}
-		eth_bridge_post_event(IPA_ETH_BRIDGE_WLAN_SCC_MCC_SWITCH, IPA_IP_MAX, NULL);
+		eth_bridge_post_event(IPA_ETH_BRIDGE_WLAN_SCC_MCC_SWITCH, IPA_IP_MAX, NULL, NULL, NULL);
 		break;
 
 	case IPA_WLAN_SWITCH_TO_MCC:
@@ -816,7 +816,7 @@
 		{
 			handle_SCC_MCC_switch(ip_type);
 		}
-		eth_bridge_post_event(IPA_ETH_BRIDGE_WLAN_SCC_MCC_SWITCH, IPA_IP_MAX, NULL);
+		eth_bridge_post_event(IPA_ETH_BRIDGE_WLAN_SCC_MCC_SWITCH, IPA_IP_MAX, NULL, NULL, NULL);
 		break;
 
 	case IPA_CRADLE_WAN_MODE_SWITCH:
@@ -1804,7 +1804,7 @@
 	}
 	IPACMDBG_H("finished deleting default RT rules\n ");
 
-	eth_bridge_post_event(IPA_ETH_BRIDGE_IFACE_DOWN, IPA_IP_MAX, NULL);
+	eth_bridge_post_event(IPA_ETH_BRIDGE_IFACE_DOWN, IPA_IP_MAX, NULL, NULL, NULL);
 
 	/* free the wlan clients cache */
 	IPACMDBG_H("Free wlan clients cache\n");
@@ -2163,23 +2163,23 @@
 	/* ====== post events to mimic WLAN interface goes down/up when AP mode is changing ====== */
 
 	/* first post IFACE_DOWN event */
-	eth_bridge_post_event(IPA_ETH_BRIDGE_IFACE_DOWN, IPA_IP_MAX, NULL);
+	eth_bridge_post_event(IPA_ETH_BRIDGE_IFACE_DOWN, IPA_IP_MAX, NULL, NULL, NULL);
 
 	/* then post IFACE_UP event */
 	if(ip_type == IPA_IP_v4 || ip_type == IPA_IP_MAX)
 	{
-		eth_bridge_post_event(IPA_ETH_BRIDGE_IFACE_UP, IPA_IP_v4, NULL);
+		eth_bridge_post_event(IPA_ETH_BRIDGE_IFACE_UP, IPA_IP_v4, NULL, NULL, NULL);
 	}
 	if(ip_type == IPA_IP_v6 || ip_type == IPA_IP_MAX)
 	{
-		eth_bridge_post_event(IPA_ETH_BRIDGE_IFACE_UP, IPA_IP_v6, NULL);
+		eth_bridge_post_event(IPA_ETH_BRIDGE_IFACE_UP, IPA_IP_v6, NULL, NULL, NULL);
 	}
 
 	/* at last post CLIENT_ADD event */
 	for(i = 0; i < num_wifi_client; i++)
 	{
 		eth_bridge_post_event(IPA_ETH_BRIDGE_CLIENT_ADD, IPA_IP_MAX,
-			get_client_memptr(wlan_client, i)->mac);
+			get_client_memptr(wlan_client, i)->mac, NULL, NULL);
 	}
 
 	return;
diff --git a/ipacm/src/Makefile.am b/ipacm/src/Makefile.am
index 7a62a75..92ea904 100644
--- a/ipacm/src/Makefile.am
+++ b/ipacm/src/Makefile.am
@@ -2,7 +2,7 @@
 	      -I$(top_srcdir)/ipanat/inc \
 	      ${LIBXML_CFLAGS}
 AM_CPPFLAGS += -Wall -Wundef -Wno-trigraphs
-AM_CPPFLAGS	+= -DDEBUG -g -DFEATURE_ETH_BRIDGE_LE
+AM_CPPFLAGS	+= -DDEBUG -g -DFEATURE_ETH_BRIDGE_LE -DFEATURE_L2TP
 AM_CPPFLAGS += -DFEATURE_IPA_V3
 
 ipacm_SOURCES =	IPACM_Main.cpp \