Merge 68c5d9a38a6a8bd833321768fe862630fdbc482f on remote branch

Change-Id: I1c1d52b92216fb78f172ebb91d0e41763897b8e8
diff --git a/ipacm/Android.bp b/ipacm/Android.bp
index c6ed74b..fbb69e3 100644
--- a/ipacm/Android.bp
+++ b/ipacm/Android.bp
@@ -70,3 +70,12 @@
     src: "src/IPACM_cfg.xml",
 
 }
+
+prebuilt_etc {
+    name: "IPACM_Filter_cfg.xml",
+
+    vendor: true,
+    owner: "ipacm",
+    src: "src/IPACM_Filter_cfg.xml",
+
+}
diff --git a/ipacm/inc/IPACM_Config.h b/ipacm/inc/IPACM_Config.h
index cc89c83..f0f1cf5 100644
--- a/ipacm/inc/IPACM_Config.h
+++ b/ipacm/inc/IPACM_Config.h
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2013-2020, The Linux Foundation. All rights reserved.
+Copyright (c) 2013-2021, 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
@@ -96,6 +96,9 @@
 	/* Store private subnet configuration from XML file */
 	ipa_private_subnet private_subnet_table[IPA_MAX_PRIVATE_SUBNET_ENTRIES + IPA_MAX_MTU_ENTRIES];
 
+	/* Store Filter configuration. */
+	IPACM_filter_conf_t filter_config;
+
 	/* Store the non nat iface names */
 	NatIfaces *pNatIfaces;
 
diff --git a/ipacm/inc/IPACM_Defs.h b/ipacm/inc/IPACM_Defs.h
index 82e9c6b..20bef93 100644
--- a/ipacm/inc/IPACM_Defs.h
+++ b/ipacm/inc/IPACM_Defs.h
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
+Copyright (c) 2013-2021, 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
@@ -78,6 +78,7 @@
 #define IPA_MAX_IFACE_ENTRIES 20
 #define IPA_MAX_PRIVATE_SUBNET_ENTRIES 3
 #define IPA_MAX_MTU_ENTRIES 3
+#define IPA_MAX_FILTER_CFG_ENTRIES 2
 #define IPA_MAX_ALG_ENTRIES 20
 #define IPA_MAX_RM_ENTRY 6
 
@@ -110,6 +111,8 @@
 #define LOOPBACK_MASK 0xFF000000
 #define LOOPBACK_ADDR 0x7F000000
 
+#define IPACM_FILTER_CFG_FILE "/data/vendor/ipa/IPACM_Filter_cfg.xml"
+
 /*---------------------------------------------------------------------------
 										Return values indicating error status
 ---------------------------------------------------------------------------*/
@@ -136,6 +139,7 @@
 	IPA_CFG_CHANGE_EVENT,                 /* NULL */
 	IPA_PRIVATE_SUBNET_CHANGE_EVENT,          /* ipacm_event_data_fid */
 	IPA_FIREWALL_CHANGE_EVENT,                /* NULL */
+	IPA_FILTER_CFG_CHANGE_EVENT,              /* NULL */
 	IPA_LINK_UP_EVENT,                        /* ipacm_event_data_fid */
 	IPA_LINK_DOWN_EVENT,                      /* ipacm_event_data_fid */
 	IPA_USB_LINK_UP_EVENT,                    /* ipacm_event_data_fid */
diff --git a/ipacm/inc/IPACM_Lan.h b/ipacm/inc/IPACM_Lan.h
index de64be1..735ce6b 100644
--- a/ipacm/inc/IPACM_Lan.h
+++ b/ipacm/inc/IPACM_Lan.h
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
+Copyright (c) 2013-2021, 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
@@ -63,6 +63,20 @@
 
 #define PIPE_STATS "%s %s %llu %llu %llu %llu"
 #define IPA_PIPE_STATS_FILE_NAME "/data/misc/ipa/tether_stats"
+#define IPA_DOWNSTREAM_TETHER_STATE_FILE_NAME "/data/vendor/ipa/downstream_state"
+/* Max tether interfaces: rndis0/wlan0/wlan1 */
+#define IPA_MAX_TETHER_IFACE_ENTRIES 3
+
+/* Down Stream information. */
+struct ipa_lan_downstream_info
+{
+	/* IPACM interface name */
+	char dev_name[IF_NAME_LEN];
+	/* Down Stream state */
+	bool downstream_state;
+	/* Bool indicating entry in use. */
+	bool entry_in_use;
+};
 
 /* store each lan-iface unicast routing rule and its handler*/
 struct ipa_lan_rt_rule
@@ -115,9 +129,17 @@
 	/* store private-subnet filter rule handlers */
 	uint32_t private_fl_rule_hdl[IPA_MAX_PRIVATE_SUBNET_ENTRIES + IPA_MAX_MTU_ENTRIES];
 
+	/* store filter cfg rule handles */
+	uint32_t filter_cfg_rule_hdl[IPA_MAX_FILTER_CFG_ENTRIES];
+
+	/* store downstream information */
+	static struct ipa_lan_downstream_info downstream_info[IPA_MAX_TETHER_IFACE_ENTRIES];
+
 	/* LAN-iface's callback function */
 	void event_callback(ipa_cm_event_id event, void *data);
 
+	virtual void store_downstream_state(bool up, enum ipa_ip_type iptype);
+
 	virtual int handle_wan_up(ipa_ip_type ip_type);
 
 	/* configure filter rule for wan_up event*/
@@ -230,6 +252,10 @@
 
 	int reset_to_dummy_flt_rule(ipa_ip_type iptype, uint32_t rule_hdl);
 
+	int handle_filter_cfg_update(ipa_ip_type iptype);
+
+	int add_dummy_filter_cfg_rules(ipa_ip_type iptype);
+
 	virtual int modify_ipv6_prefix_flt_rule(uint32_t* prefix);
 
 	virtual int install_ipv6_prefix_flt_rule(uint32_t* prefix);
diff --git a/ipacm/inc/IPACM_Wan.h b/ipacm/inc/IPACM_Wan.h
index a73ec98..d762430 100644
--- a/ipacm/inc/IPACM_Wan.h
+++ b/ipacm/inc/IPACM_Wan.h
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2013-2020, The Linux Foundation. All rights reserved.
+Copyright (c) 2013-2021, 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
@@ -63,6 +63,7 @@
 
 #define NETWORK_STATS "%s %llu %llu %llu %llu"
 #define IPA_NETWORK_STATS_FILE_NAME "/data/misc/ipa/network_stats"
+#define IPA_OFFLOAD_TETHER_STATE_FILE_NAME "/data/vendor/ipa/offload_state"
 
 typedef struct _wan_client_rt_hdl
 {
@@ -640,6 +641,10 @@
 	int config_dft_firewall_rules_ex(struct ipa_flt_rule_add* rules, int rule_offset,
 		ipa_ip_type iptype);
 
+	/* configure dl ack rule if enabled. */
+	int config_filter_dl_ack_rule_ex(struct ipa_flt_rule_add *rules, int rule_offset,
+		ipa_ip_type iptype);
+
 	/* init filtering rule in wan dl filtering table */
 	int init_fl_rule_ex(ipa_ip_type iptype);
 
diff --git a/ipacm/inc/IPACM_Xml.h b/ipacm/inc/IPACM_Xml.h
index c78eefd..77059d3 100644
--- a/ipacm/inc/IPACM_Xml.h
+++ b/ipacm/inc/IPACM_Xml.h
@@ -1,5 +1,5 @@
 /* 
-Copyright (c) 2013-2020, The Linux Foundation. All rights reserved.
+Copyright (c) 2013-2021, 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
@@ -65,6 +65,7 @@
 /* Max allowed size of the XML file (2 MB) */
 #define IPACM_XML_MAX_FILESIZE               (2 << 20)
 #define IPACM_MAX_FIREWALL_ENTRIES            50
+#define IPACM_MAX_FILTER_CFG_ENTRIES          10
 #define IPACM_IPV6_ADDR_LEN                   16
 
 /* Defines for clipping space or space & quotes (single, double) */
@@ -113,6 +114,13 @@
 #define SRAM_TABLETYPE_TAG                   "SRAM"
 #define HYBRID_TABLETYPE_TAG                 "HYBRID"
 
+/* Filter Config Entries */
+#define FilterCfg_TAG                        "IPACMFilterCfg"
+#define FilterEnabled_TAG                    "FilteringEnabled"
+#define FilterDLAck_TAG                      "FilterDLAck"
+#define FilterEntry_TAG                      "FilterEntry"
+
+
 /* FIREWALL Config Entries */
 #define Firewall_TAG                         "Firewall"
 #define MobileAPFirewallCfg_TAG              "MobileAPFirewallCfg"
@@ -216,7 +224,7 @@
 {
 	IP_V4 = 4,
 	IP_V6 = 6
-} firewall_ip_version_enum;
+} ip_version_enum;
   
 /*---------------------------------------------------------------------------
            Extended FireWall Entry Configuration.
@@ -224,7 +232,7 @@
 typedef struct
 {
 	struct ipa_rule_attrib attrib;
-	firewall_ip_version_enum  ip_vsn;
+	ip_version_enum  ip_vsn;
 } IPACM_extd_firewall_entry_conf_t;
 
 
@@ -245,8 +253,23 @@
 	bool rule_action_accept;
 	bool firewall_enable;
 } IPACM_firewall_conf_t;
-  
 
+/*---------------------------------------------------------------------------
+           Filter Configuration.
+---------------------------------------------------------------------------*/
+typedef struct
+{
+	struct ipa_rule_attrib attrib;
+	ip_version_enum  ip_vsn;
+} IPACM_filter_conf_entry_t;
+
+typedef struct
+{
+	bool filter_enable;
+	bool dl_ack_filter_enable;
+	uint8_t  num_filter_cfg_entries;
+	IPACM_filter_conf_entry_t filter_cfg_entries[IPACM_MAX_FILTER_CFG_ENTRIES];
+} IPACM_filter_conf_t;
 
 typedef struct
 {
@@ -301,6 +324,14 @@
 	IPACM_firewall_conf_t *config                   /* Mobile AP config data */
 );
 
+/* This function reads Filter Cfg XML and store in IPACM Filter Cfg stucture */
+int IPACM_read_filter_cfg_xml
+(
+	char *xml_file,                                 /* Filename and path */
+	IPACM_filter_conf_t *config                     /* Filter config data */
+);
+
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/ipacm/src/IPACM_Config.cpp b/ipacm/src/IPACM_Config.cpp
index 55b6f43..369c7d4 100644
--- a/ipacm/src/IPACM_Config.cpp
+++ b/ipacm/src/IPACM_Config.cpp
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2013-2020, The Linux Foundation. All rights reserved.
+Copyright (c) 2013-2021, 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,6 +53,7 @@
 	__stringify(IPA_CFG_CHANGE_EVENT),                     /* NULL */
 	__stringify(IPA_PRIVATE_SUBNET_CHANGE_EVENT),          /* ipacm_event_data_fid */
 	__stringify(IPA_FIREWALL_CHANGE_EVENT),                /* NULL */
+	__stringify(IPA_FILTER_CFG_CHANGE_EVENT),              /* NULL */
 	__stringify(IPA_LINK_UP_EVENT),                        /* ipacm_event_data_fid */
 	__stringify(IPA_LINK_DOWN_EVENT),                      /* ipacm_event_data_fid */
 	__stringify(IPA_USB_LINK_UP_EVENT),                    /* ipacm_event_data_fid */
@@ -349,6 +350,18 @@
 		goto fail;
 	}
 
+	/* default fillter config is disable. */
+	memset(&filter_config, 0, sizeof(filter_config));
+	strlcpy(IPACM_config_file, IPACM_FILTER_CFG_FILE, sizeof(IPACM_config_file));
+	if (IPACM_SUCCESS == IPACM_read_filter_cfg_xml(IPACM_config_file, &filter_config))
+	{
+		IPACMDBG_H("Filter XML read OK \n");
+	}
+	else
+	{
+		IPACMERR("Filter Config XML read failed, use default configuration \n");
+	}
+
 	/* Construct the routing table ictol name in iface static member*/
 	rt_tbl_default_v4.ip = IPA_IP_v4;
 	strlcpy(rt_tbl_default_v4.name, V4_DEFAULT_ROUTE_TABLE_NAME, sizeof(rt_tbl_default_v4.name));
diff --git a/ipacm/src/IPACM_Filter_cfg.xml b/ipacm/src/IPACM_Filter_cfg.xml
new file mode 100644
index 0000000..39bbd14
--- /dev/null
+++ b/ipacm/src/IPACM_Filter_cfg.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (c) 2021 The Linux Foundation. All rights reserved. -->
+<system xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ipacm_filter_cfg.xsd">
+	<IPACMFilterCfg>
+		<FilteringEnabled>0</FilteringEnabled>
+		<FilterDLAck>0</FilterDLAck>
+		<FilterEntry>
+			<IPFamily>4</IPFamily>
+			<IPV4NextHeaderProtocol>17</IPV4NextHeaderProtocol>
+		</FilterEntry>
+		<FilterEntry>
+			<IPFamily>4</IPFamily>
+			<IPV4NextHeaderProtocol>6</IPV4NextHeaderProtocol>
+		</FilterEntry>
+	</IPACMFilterCfg>
+</system>
diff --git a/ipacm/src/IPACM_IfaceManager.cpp b/ipacm/src/IPACM_IfaceManager.cpp
index 1392a1a..a2e9b66 100644
--- a/ipacm/src/IPACM_IfaceManager.cpp
+++ b/ipacm/src/IPACM_IfaceManager.cpp
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
+Copyright (c) 2013-2021, 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
@@ -286,6 +286,7 @@
 				IPACM_EvtDispatcher::registr(IPA_HANDLE_WAN_DOWN_V6, lan);
 #endif
 				IPACM_EvtDispatcher::registr(IPA_CFG_CHANGE_EVENT, lan); 				// register for IPA_CFG_CHANGE event
+				IPACM_EvtDispatcher::registr(IPA_FILTER_CFG_CHANGE_EVENT, lan); 		// register for IPA_FILTER_CFG_CHANGE_EVENT event
 				IPACM_EvtDispatcher::registr(IPA_PRIVATE_SUBNET_CHANGE_EVENT, lan); 	// register for IPA_PRIVATE_SUBNET_CHANGE_EVENT event
 #ifdef FEATURE_IPA_ANDROID
 				IPACM_EvtDispatcher::registr(IPA_TETHERING_STATS_UPDATE_EVENT, lan);
@@ -483,6 +484,7 @@
 					IPACM_EvtDispatcher::registr(IPA_SW_ROUTING_ENABLE, w);
 					IPACM_EvtDispatcher::registr(IPA_SW_ROUTING_DISABLE, w);
 					IPACM_EvtDispatcher::registr(IPA_CFG_CHANGE_EVENT, w); 		// register for IPA_CFG_CHANGE event
+					IPACM_EvtDispatcher::registr(IPA_FILTER_CFG_CHANGE_EVENT, w); // register for IPA_FILTER_CFG_CHANGE_EVENT event
 					IPACM_EvtDispatcher::registr(IPA_WAN_XLAT_CONNECT_EVENT, w);
 					if(is_sta_mode == WLAN_WAN)
 					{
diff --git a/ipacm/src/IPACM_Lan.cpp b/ipacm/src/IPACM_Lan.cpp
index bffcd8e..e2beea6 100644
--- a/ipacm/src/IPACM_Lan.cpp
+++ b/ipacm/src/IPACM_Lan.cpp
@@ -57,6 +57,8 @@
 #endif
 bool IPACM_Lan::odu_up = false;
 
+struct ipa_lan_downstream_info IPACM_Lan::downstream_info[IPA_MAX_TETHER_IFACE_ENTRIES];
+
 IPACM_Lan::IPACM_Lan(int iface_index) : IPACM_Iface(iface_index)
 {
 	num_eth_client = 0;
@@ -133,6 +135,7 @@
 	memset(ipv6_prefix_flt_rule_hdl, 0, (NUM_IPV6_PREFIX_FLT_RULE + NUM_IPV6_PREFIX_MTU_RULE) * sizeof(uint32_t));
 	memset(ipv6_icmp_flt_rule_hdl, 0, NUM_IPV6_ICMP_FLT_RULE * sizeof(uint32_t));
 	memset(ipv6_prefix, 0, sizeof(ipv6_prefix));
+	memset(filter_cfg_rule_hdl, 0, sizeof(filter_cfg_rule_hdl));
 
 	/* ODU routing table initilization */
 	if(ipa_if_cate == ODU_IF)
@@ -275,6 +278,20 @@
 		}
 		break;
 
+	case IPA_FILTER_CFG_CHANGE_EVENT:
+	{
+		IPACMDBG_H("Received IPA_FILTER_CFG_CHANGE_EVENT");
+
+		/* IPA_IP_MAX means both ipv4 and ipv6 */
+		if ((ip_type == IPA_IP_v4 || ip_type == IPA_IP_MAX)
+			&& IPACM_Wan::isWanUP(ipa_if_num))
+		{
+			handle_filter_cfg_update(IPA_IP_v4);
+		}
+
+	}
+	break;
+
 	case IPA_PRIVATE_SUBNET_CHANGE_EVENT:
 		{
 			ipacm_event_data_fid *data = (ipacm_event_data_fid *)param;
@@ -398,6 +415,8 @@
 #ifdef FEATURE_IPA_ANDROID
 					add_dummy_private_subnet_flt_rule(data->iptype);
 					handle_private_subnet_android(data->iptype);
+					add_dummy_filter_cfg_rules(data->iptype);
+					handle_filter_cfg_update(data->iptype);
 #else
 					handle_private_subnet(data->iptype);
 #endif // FEATURE_IPA_ANDROID end
@@ -418,6 +437,8 @@
 #ifdef FEATURE_IPA_ANDROID
 						add_dummy_private_subnet_flt_rule(data->iptype);
 						handle_private_subnet_android(data->iptype);
+						add_dummy_filter_cfg_rules(data->iptype);
+						handle_filter_cfg_update(data->iptype);
 #else
 						handle_private_subnet(data->iptype);
 #endif // FEATURE_IPA_ANDROID end
@@ -755,7 +776,9 @@
 			if (data->prefix.iptype < IPA_IP_MAX && is_downstream_set[data->prefix.iptype] == false)
 			{
 				IPACMDBG_H("Add downstream for IP iptype %d\n", data->prefix.iptype);
+
 				is_downstream_set[data->prefix.iptype] = true;
+				store_downstream_state(true, data->prefix.iptype);
 				memcpy(&prefix[data->prefix.iptype], &data->prefix,
 					sizeof(prefix[data->prefix.iptype]));
 
@@ -808,7 +831,7 @@
 			{
 				IPACMDBG_H("Del downstream for IP iptype %d.\n", data->prefix.iptype);
 				is_downstream_set[data->prefix.iptype] = false;
-
+				store_downstream_state(false, data->prefix.iptype);
 				if (is_upstream_set[data->prefix.iptype] == true)
 				{
 					IPACMDBG_H("Upstream was set before, deleting UL rules.\n");
@@ -1147,6 +1170,71 @@
 	return;
 }
 
+void IPACM_Lan::store_downstream_state(bool up, enum ipa_ip_type iptype)
+{
+	/* Store the downstream state. */
+	FILE *fp = NULL;
+	bool state_update = false;
+	int free_index = -1;
+
+	/* Return if other iptype is active. */
+	if ((iptype == IPA_IP_v4 && is_downstream_set[IPA_IP_v6]) ||
+		(iptype == IPA_IP_v6 && is_downstream_set[IPA_IP_v4]))
+		return;
+
+	/* Update the downstream state info. */
+	for (int i=0; i < IPA_MAX_TETHER_IFACE_ENTRIES; i++)
+	{
+		if (IPACM_Lan::downstream_info[i].entry_in_use &&
+			strncmp(dev_name, IPACM_Lan::downstream_info[i].dev_name, IF_NAME_LEN) == 0)
+		{
+			/* Update state. */
+			IPACM_Lan::downstream_info[i].downstream_state = up;
+			state_update = true;
+			IPACMDBG_H("Updating info for tether iface :%s, State: %s\n", dev_name,
+				up ? "UP" : "DOWN");
+			break;
+		}
+
+		if (!IPACM_Lan::downstream_info[i].entry_in_use && free_index == -1)
+			free_index = i;
+	}
+
+	/* if state is up, check if entry is already present, if not add. */
+	if (up && !state_update && free_index != -1)
+	{
+		IPACM_Lan::downstream_info[free_index].entry_in_use = true;
+		IPACM_Lan::downstream_info[free_index].downstream_state = up;
+		strlcpy(IPACM_Lan::downstream_info[free_index].dev_name,
+			dev_name, IF_NAME_LEN);
+		IPACMDBG_H("Adding new tether iface :%s, State: UP\n", dev_name);
+	}
+	else if (!state_update && free_index == -1)
+	{
+		IPACMERR("Exceeded max tether ifaces: not storing info for %s\n", dev_name);
+		return;
+	}
+
+	fp = fopen(IPA_DOWNSTREAM_TETHER_STATE_FILE_NAME, "w");
+	if (fp == NULL)
+	{
+		IPACMERR("Failed to write downstream state to %s, error is %d - %s\n",
+			IPA_DOWNSTREAM_TETHER_STATE_FILE_NAME,
+			errno, strerror(errno));
+	}
+	else
+	{
+		for (int i=0; i < IPA_MAX_TETHER_IFACE_ENTRIES; i++)
+		{
+			if (IPACM_Lan::downstream_info[i].entry_in_use)
+			{
+				fprintf(fp, "DOWNSTREAM=%s,STATE=%s;", IPACM_Lan::downstream_info[i].dev_name,
+					IPACM_Lan::downstream_info[i].downstream_state ? "UP" : "DOWN");
+			}
+		}
+		fclose(fp);
+	}
+}
 
 int IPACM_Lan::handle_del_ipv6_addr(ipacm_event_data_all *data)
 {
@@ -1938,6 +2026,9 @@
 #endif
 		/* add MTU rules for ipv4 */
 		handle_private_subnet_android(IPA_IP_v4);
+		/* Add filter config rules for ipv4 */
+		handle_filter_cfg_update(IPA_IP_v4);
+
 
 		/* Update ipv6 MTU here if WAN_v6 is up and filter rules were installed */
 		if (IPACM_Wan::isWanUP_V6(ipa_if_num))
@@ -3085,6 +3176,18 @@
 #endif
 	}
 
+	if (is_downstream_set[IPA_IP_v4])
+	{
+		is_downstream_set[IPA_IP_v4] = false;
+		store_downstream_state(false, IPA_IP_v4);
+	}
+
+	if (is_downstream_set[IPA_IP_v6])
+	{
+		is_downstream_set[IPA_IP_v6] = false;
+		store_downstream_state(false, IPA_IP_v6);
+	}
+
 	/* delete default filter rules */
 	if (ip_type != IPA_IP_v6 && rx_prop != NULL)
 	{
@@ -3134,6 +3237,14 @@
 		IPACM_Iface::ipacmcfg->decreaseFltRuleCount(rx_prop->rx[0].src_pipe, IPA_IP_v4, IPACM_Iface::ipacmcfg->ipa_num_private_subnet);
 #endif
 		IPACMDBG_H("Deleted private subnet v4 filter rules successfully.\n");
+
+		if(m_filtering.DeleteFilteringHdls(filter_cfg_rule_hdl, IPA_IP_v4, IPA_MAX_FILTER_CFG_ENTRIES) == false)
+		{
+			IPACMERR("Error deleting filter cfg rules.\n");
+			res = IPACM_FAILURE;
+			goto fail;
+		}
+		IPACM_Iface::ipacmcfg->decreaseFltRuleCount(rx_prop->rx[0].src_pipe, IPA_IP_v4, IPA_MAX_FILTER_CFG_ENTRIES);
 	}
 	IPACMDBG_H("Finished delete default iface ipv4 filtering rules \n ");
 
@@ -4213,6 +4324,194 @@
 	return res;
 }
 
+int IPACM_Lan::add_dummy_filter_cfg_rules(ipa_ip_type iptype)
+{
+	int i, len, res = IPACM_SUCCESS;
+	struct ipa_flt_rule_add flt_rule;
+	ipa_ioc_add_flt_rule* pFilteringTable;
+	bool result;
+
+	if(rx_prop == NULL)
+	{
+		IPACMDBG_H("There is no rx_prop for iface %s, not able to add dummy filter cfg rules.\n", dev_name);
+		return 0;
+	}
+
+	if(iptype == IPA_IP_v6)
+	{
+		IPACMDBG_H("There is no ipv6 dummy filter config rules needed for iface %s\n", dev_name);
+		return 0;
+	}
+
+	len = sizeof(struct ipa_ioc_add_flt_rule) + IPA_MAX_FILTER_CFG_ENTRIES * sizeof(struct ipa_flt_rule_add);
+
+	pFilteringTable = (struct ipa_ioc_add_flt_rule *)malloc(len);
+	if (pFilteringTable == NULL)
+	{
+		IPACMERR("Error allocate flt table memory...\n");
+		return IPACM_FAILURE;
+	}
+	memset(pFilteringTable, 0, len);
+
+	pFilteringTable->commit = 1;
+	pFilteringTable->ep = rx_prop->rx[0].src_pipe;
+	pFilteringTable->global = false;
+	pFilteringTable->ip = iptype;
+	pFilteringTable->num_rules = IPA_MAX_FILTER_CFG_ENTRIES;
+
+	memset(&flt_rule, 0, sizeof(struct ipa_flt_rule_add));
+
+	flt_rule.rule.retain_hdr = 0;
+	flt_rule.at_rear = true;
+	flt_rule.flt_rule_hdl = -1;
+	flt_rule.status = -1;
+	flt_rule.rule.action = IPA_PASS_TO_EXCEPTION;
+	if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+		flt_rule.rule.hashable = true;
+	memcpy(&flt_rule.rule.attrib, &rx_prop->rx[0].attrib,
+			sizeof(flt_rule.rule.attrib));
+
+	if(iptype == IPA_IP_v4)
+	{
+		flt_rule.rule.attrib.attrib_mask = IPA_FLT_SRC_ADDR | IPA_FLT_DST_ADDR;
+		flt_rule.rule.attrib.u.v4.src_addr_mask = ~0;
+		flt_rule.rule.attrib.u.v4.src_addr = ~0;
+		flt_rule.rule.attrib.u.v4.dst_addr_mask = ~0;
+		flt_rule.rule.attrib.u.v4.dst_addr = ~0;
+
+		for(i=0; i<IPA_MAX_FILTER_CFG_ENTRIES; i++)
+		{
+			memcpy(&(pFilteringTable->rules[i]), &flt_rule, sizeof(struct ipa_flt_rule_add));
+		}
+
+#ifdef IPA_IOCTL_SET_FNR_COUNTER_INFO
+		/* use index hw-counter */
+		if(ipa_if_cate == WLAN_IF && IPACM_Iface::ipacmcfg->hw_fnr_stats_support)
+		{
+			IPACMDBG_H("hw-index-enable %d, counter %d\n", IPACM_Iface::ipacmcfg->hw_fnr_stats_support, IPACM_Iface::ipacmcfg->hw_counter_offset + UL_ALL);
+			result = m_filtering.AddFilteringRule_hw_index(pFilteringTable, IPACM_Iface::ipacmcfg->hw_counter_offset + UL_ALL);
+		} else {
+			result = m_filtering.AddFilteringRule(pFilteringTable);
+		}
+#else
+		result = m_filtering.AddFilteringRule(pFilteringTable);
+#endif
+
+		if (result == false)
+		{
+			IPACMERR("Error adding dummy filter cfg rules\n");
+			res = IPACM_FAILURE;
+			goto fail;
+		}
+		else
+		{
+			IPACM_Iface::ipacmcfg->increaseFltRuleCount(rx_prop->rx[0].src_pipe, IPA_IP_v4, IPA_MAX_FILTER_CFG_ENTRIES);
+			/* copy filter rule hdls */
+			for (int i = 0; i < IPA_MAX_FILTER_CFG_ENTRIES; i++)
+			{
+				if (pFilteringTable->rules[i].status == 0)
+				{
+					filter_cfg_rule_hdl[i] = pFilteringTable->rules[i].flt_rule_hdl;
+					IPACMDBG_H("Filter cfg rule %d hdl:0x%x\n", i, private_fl_rule_hdl[i]);
+				}
+				else
+				{
+					IPACMERR("Failed adding filter cfg rule %d\n", i);
+					res = IPACM_FAILURE;
+					goto fail;
+				}
+			}
+		}
+	}
+fail:
+	free(pFilteringTable);
+	return res;
+}
+
+
+int IPACM_Lan::handle_filter_cfg_update(ipa_ip_type iptype)
+{
+	int i, len, res = IPACM_SUCCESS;
+	struct ipa_flt_rule_mdfy flt_rule;
+	struct ipa_ioc_mdfy_flt_rule* pFilteringTable = NULL;
+
+	if (rx_prop == NULL)
+	{
+		IPACMDBG_H("No rx properties registered for iface %s\n", dev_name);
+		return IPACM_SUCCESS;
+	}
+
+	if(iptype == IPA_IP_v6)
+	{
+		IPACMDBG_H("Ipv6 Filter config is not supported for iface %s\n", dev_name);
+		return 0;
+	}
+
+	for(i=0; i<IPA_MAX_FILTER_CFG_ENTRIES; i++)
+	{
+		reset_to_dummy_flt_rule(IPA_IP_v4, filter_cfg_rule_hdl[i]);
+	}
+
+	if(IPACM_Iface::ipacmcfg->filter_config.filter_enable == true)
+	{
+		if ((IPACM_Iface::ipacmcfg->filter_config.num_filter_cfg_entries == 0) ||
+			(IPACM_Iface::ipacmcfg->filter_config.num_filter_cfg_entries > IPA_MAX_FILTER_CFG_ENTRIES))
+		{
+			IPACMERR("Invalid filter Config num rules %d\n",
+				IPACM_Iface::ipacmcfg->filter_config.num_filter_cfg_entries);
+			return IPACM_FAILURE;
+		}
+
+		IPACMDBG_H("total %d filter cfg rules are needed\n", IPACM_Iface::ipacmcfg->filter_config.num_filter_cfg_entries);
+
+		len = sizeof(struct ipa_ioc_mdfy_flt_rule) + IPACM_Iface::ipacmcfg->filter_config.num_filter_cfg_entries * sizeof(struct ipa_flt_rule_mdfy);
+		pFilteringTable = (struct ipa_ioc_mdfy_flt_rule*)malloc(len);
+		if (!pFilteringTable)
+		{
+			IPACMERR("Failed to allocate ipa_ioc_mdfy_flt_rule memory...\n");
+			return IPACM_FAILURE;
+		}
+		memset(pFilteringTable, 0, len);
+
+		pFilteringTable->commit = 1;
+		pFilteringTable->ip = iptype;
+		pFilteringTable->num_rules = (uint8_t)IPACM_Iface::ipacmcfg->filter_config.num_filter_cfg_entries;
+
+		for (i = 0; i < IPACM_Iface::ipacmcfg->filter_config.num_filter_cfg_entries; i++)
+		{
+			/* add filter cfg for ipv4 */
+			memset(&flt_rule, 0, sizeof(struct ipa_flt_rule_mdfy));
+			flt_rule.status = -1;
+			flt_rule.rule.retain_hdr = 1;
+			flt_rule.rule.to_uc = 0;
+			flt_rule.rule.action = IPA_PASS_TO_EXCEPTION;
+			flt_rule.rule.eq_attrib_type = 0;
+			if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+				flt_rule.rule.hashable = true;
+			flt_rule.rule_hdl = filter_cfg_rule_hdl[i];
+			memcpy(&flt_rule.rule.attrib,
+				&IPACM_Iface::ipacmcfg->filter_config.filter_cfg_entries[i].attrib,
+				sizeof(struct ipa_rule_attrib));
+			flt_rule.rule.attrib.attrib_mask |= rx_prop->rx[0].attrib.attrib_mask;
+			memcpy(&(pFilteringTable->rules[i]), &flt_rule, sizeof(struct ipa_flt_rule_mdfy));
+		}
+
+		if (false == m_filtering.ModifyFilteringRule(pFilteringTable))
+		{
+			IPACMERR("Failed to modify filter config rules.\n");
+			res = IPACM_FAILURE;
+			goto fail;
+		}
+	}
+fail:
+	if(pFilteringTable != NULL)
+	{
+		free(pFilteringTable);
+	}
+	return res;
+}
+
+
 int IPACM_Lan::handle_private_subnet_android(ipa_ip_type iptype)
 {
 	int i, len, res = IPACM_SUCCESS;
diff --git a/ipacm/src/IPACM_Main.cpp b/ipacm/src/IPACM_Main.cpp
index 6bd117e..e29289d 100644
--- a/ipacm/src/IPACM_Main.cpp
+++ b/ipacm/src/IPACM_Main.cpp
@@ -80,9 +80,10 @@
 
 #define IPACM_FIREWALL_FILE_NAME    "mobileap_firewall.xml"
 #define IPACM_CFG_FILE_NAME    "IPACM_cfg.xml"
+#define IPACM_FILTER_CFG_FILE_NAME    "IPACM_Filter_cfg.xml"
 #ifdef FEATURE_IPA_ANDROID
 #define IPACM_PID_FILE "/data/vendor/ipa/ipacm.pid"
-#define IPACM_DIR_NAME     "/data"
+#define IPACM_DIR_NAME     "/data/vendor/ipa/"
 #else/* defined(FEATURE_IPA_ANDROID) */
 #define IPACM_PID_FILE "/etc/ipacm.pid"
 #define IPACM_DIR_NAME     "/etc"
@@ -146,8 +147,8 @@
 	return NULL;
 }
 
-/* start firewall-rule monitor*/
-void* firewall_monitor(void *param)
+/* start Config change monitor*/
+void* cfg_change_monitor(void *param)
 {
 	int length;
 	int wd;
@@ -196,6 +197,7 @@
 				{
 					IPACMDBG_H("The directory %s was 0x%x\n", event->name, event->mask);
 				}
+#ifndef FEATURE_IPA_ANDROID
 				else if (!strncmp(event->name, IPACM_FIREWALL_FILE_NAME, event->len)) // firewall_rule change
 				{
 					IPACMDBG_H("File \"%s\" was 0x%x\n", event->name, event->mask);
@@ -218,6 +220,33 @@
 					/* Insert IPA_FIREWALL_CHANGE_EVENT to command queue */
 					IPACM_EvtDispatcher::PostEvt(&evt_data);
 				}
+#endif
+				else if (!strncmp(event->name, IPACM_FILTER_CFG_FILE_NAME, event->len)) // IPACM Filter Config change
+				{
+					char IPACM_config_file[IPA_MAX_FILE_LEN];
+					IPACMDBG_H("File \"%s\" was 0x%x\n", event->name, event->mask);
+					IPACMDBG_H("The interested file %s .\n", IPACM_FILTER_CFG_FILE_NAME);
+
+					/* default fillter config is disable. */
+					memset(&IPACM_Iface::ipacmcfg->filter_config, 0,
+						sizeof(IPACM_Iface::ipacmcfg->filter_config));
+					strlcpy(IPACM_config_file, IPACM_FILTER_CFG_FILE, sizeof(IPACM_config_file));
+					if (IPACM_SUCCESS == IPACM_read_filter_cfg_xml(IPACM_config_file,
+						&IPACM_Iface::ipacmcfg->filter_config))
+					{
+						IPACMDBG_H("Filter XML read OK \n");
+					}
+					else
+					{
+						IPACMERR("Filter Config XML read failed, use default configuration \n");
+					}
+
+					evt_data.event = IPA_FILTER_CFG_CHANGE_EVENT;
+					evt_data.evt_data = NULL;
+
+					/* Insert IPA_FILTER_CFG_CHANGE_EVENT to command queue */
+					IPACM_EvtDispatcher::PostEvt(&evt_data);
+				}
 			}
 			IPACMDBG_H("Received monitoring event %s.\n", event->name);
 		}
@@ -1035,23 +1064,20 @@
 		}
 	}
 
-	/* Enable Firewall support only on MDM targets */
-#ifndef FEATURE_IPA_ANDROID
 	if (IPACM_SUCCESS == monitor_thread)
 	{
-		ret = pthread_create(&monitor_thread, NULL, firewall_monitor, NULL);
+		ret = pthread_create(&monitor_thread, NULL, cfg_change_monitor, NULL);
 		if (IPACM_SUCCESS != ret)
 		{
 			IPACMERR("unable to create monitor thread\n");
 			return ret;
 		}
-		IPACMDBG_H("created firewall monitor thread\n");
-		if(pthread_setname_np(monitor_thread, "firewall cfg process") != 0)
+		IPACMDBG_H("created config change monitor thread\n");
+		if(pthread_setname_np(monitor_thread, "config change monitor") != 0)
 		{
 			IPACMERR("unable to set thread name\n");
 		}
 	}
-#endif
 
 	if (IPACM_SUCCESS == ipa_driver_thread)
 	{
diff --git a/ipacm/src/IPACM_Wan.cpp b/ipacm/src/IPACM_Wan.cpp
index 2ee9f2c..c555481 100644
--- a/ipacm/src/IPACM_Wan.cpp
+++ b/ipacm/src/IPACM_Wan.cpp
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2013-2020 The Linux Foundation. All rights reserved.
+Copyright (c) 2013-2021 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
@@ -1155,6 +1155,31 @@
 		}
 		break;
 
+	case IPA_FILTER_CFG_CHANGE_EVENT:
+		{
+			IPACMDBG_H("Received IPA_FILTER_CFG_CHANGE_EVENT\n");
+
+			if(m_is_sta_mode == Q6_WAN)
+			{
+				if(is_default_gateway == false)
+				{
+					IPACMDBG_H("Interface %s is not default gw, return.\n", dev_name);
+					return;
+				}
+
+				if(ip_type == IPA_IP_v4 || ip_type == IPA_IP_MAX)
+				{
+					del_wan_firewall_rule(IPA_IP_v4);
+					config_wan_firewall_rule(IPA_IP_v4);
+					install_wan_filtering_rule(false);
+				}
+				else
+				{
+					IPACMERR("IP type is not expected.\n");
+				}
+			}
+		}
+		break;
 	case IPA_COALESCE_NOTICE:
 		{
 			if (m_is_sta_mode == Q6_WAN)
@@ -2395,6 +2420,20 @@
 					IPACMERR("Failed to send WAN_IOC_NOTIFY_WAN_STATE as up %d\n ", wan_state.up);
 				}
 				close(fd_wwan_ioctl);
+
+				/* Store the Offload state. */
+				FILE *fp = NULL;
+				fp = fopen(IPA_OFFLOAD_TETHER_STATE_FILE_NAME, "w");
+				if (fp == NULL)
+				{
+					IPACMERR("Failed to write offload state to %s, error is %d - %s\n",
+						IPA_OFFLOAD_TETHER_STATE_FILE_NAME, errno, strerror(errno));
+				}
+				else
+				{
+					fprintf(fp, "UPSTREAM=%s,STATE=UP", dev_name);
+					fclose(fp);
+				}
 			}
 			ipa_pm_q6_check++;
 			IPACMDBG_H("update ipa_pm_q6_check to %d\n", ipa_pm_q6_check);
@@ -4079,6 +4118,82 @@
 	return IPACM_SUCCESS;
 }
 
+/* configure the DL Ack rule if enabled */
+int IPACM_Wan::config_filter_dl_ack_rule_ex(struct ipa_flt_rule_add *rules, int rule_offset, ipa_ip_type iptype)
+{
+	struct ipa_flt_rule_add flt_rule_entry;
+	ipa_ioc_get_rt_tbl_indx rt_tbl_idx;
+	ipa_ioc_generate_flt_eq flt_eq;
+
+	IPACMDBG_H("ip-family: %d; \n", iptype);
+
+	if (rx_prop == NULL)
+	{
+		IPACMDBG_H("No rx properties registered for iface %s\n", dev_name);
+		return IPACM_SUCCESS;
+	}
+
+	if(rules == NULL || rule_offset < 0)
+	{
+		IPACMERR("No filtering table is available.\n");
+		return IPACM_FAILURE;
+	}
+
+	if (iptype == IPA_IP_v4)
+	{
+		memset(&flt_rule_entry, 0, sizeof(struct ipa_flt_rule_add));
+
+		flt_rule_entry.at_rear = true;
+		flt_rule_entry.flt_rule_hdl = -1;
+		flt_rule_entry.status = -1;
+
+		flt_rule_entry.rule.retain_hdr = 1;
+		flt_rule_entry.rule.to_uc = 0;
+		flt_rule_entry.rule.eq_attrib_type = 1;
+
+		flt_rule_entry.rule.action = IPA_PASS_TO_ROUTING;
+		if (IPACM_Iface::ipacmcfg->isIPAv3Supported())
+				flt_rule_entry.rule.hashable = false;
+		memset(&rt_tbl_idx, 0, sizeof(rt_tbl_idx));
+		rt_tbl_idx.ip = iptype;
+		strlcpy(rt_tbl_idx.name, IPACM_Iface::ipacmcfg->rt_tbl_wan_dl.name, IPA_RESOURCE_NAME_MAX);
+		rt_tbl_idx.name[IPA_RESOURCE_NAME_MAX-1] = '\0';
+		if(0 != ioctl(m_fd_ipa, IPA_IOC_QUERY_RT_TBL_INDEX, &rt_tbl_idx))
+		{
+			IPACMERR("Failed to get routing table index from name\n");
+			return IPACM_FAILURE;
+		}
+		flt_rule_entry.rule.rt_tbl_idx = rt_tbl_idx.idx;
+
+		IPACMDBG_H("Routing table %s has index %d\n", rt_tbl_idx.name, rt_tbl_idx.idx);
+		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_IS_PURE_ACK;
+
+		change_to_network_order(IPA_IP_v4, &flt_rule_entry.rule.attrib);
+
+		memset(&flt_eq, 0, sizeof(flt_eq));
+		memcpy(&flt_eq.attrib, &flt_rule_entry.rule.attrib, sizeof(flt_eq.attrib));
+		flt_eq.ip = iptype;
+		if(0 != ioctl(m_fd_ipa, IPA_IOC_GENERATE_FLT_EQ, &flt_eq))
+		{
+			IPACMERR("Failed to get eq_attrib\n");
+			return IPACM_FAILURE;
+		}
+		memcpy(&flt_rule_entry.rule.eq_attrib,
+			&flt_eq.eq_attrib,
+			sizeof(flt_rule_entry.rule.eq_attrib));
+
+		memcpy(&(rules[rule_offset]), &flt_rule_entry, sizeof(struct ipa_flt_rule_add));
+		IPACMDBG_H("Filter rule attrib mask: 0x%x\n", rules[rule_offset].rule.attrib.attrib_mask);
+		IPACM_Wan::num_v4_flt_rule++;
+		IPACMDBG_H("Constructed DL Ack rule for ip type %d\n", iptype);
+	}
+	return IPACM_SUCCESS;
+}
+
+
 int IPACM_Wan::init_fl_rule_ex(ipa_ip_type iptype)
 {
 	int res = IPACM_SUCCESS;
@@ -4403,6 +4518,19 @@
 		}
 		IPACMDBG_H("Succeded in constructing ICMP/ALG rules for ip type %d\n", iptype);
 
+		/* Install DL ACK Rule if enabled. */
+		if (IPACM_Iface::ipacmcfg->filter_config.filter_enable &&
+			IPACM_Iface::ipacmcfg->filter_config.dl_ack_filter_enable)
+		{
+			if(IPACM_FAILURE == config_filter_dl_ack_rule_ex(flt_rule_v4, IPACM_Wan::num_v4_flt_rule, IPA_IP_v4))
+			{
+				IPACMERR("Failed to add DL ack filter rules.\n");
+				res = IPACM_FAILURE;
+				goto fail;
+			}
+			IPACMDBG_H("Succeded in constructing DL ACK rule for ip type %d\n", iptype);
+		}
+
 		if(IPACM_FAILURE == config_dft_firewall_rules_ex(flt_rule_v4, IPACM_Wan::num_v4_flt_rule, IPA_IP_v4))
 		{
 			IPACMERR("Failed to add firewall filtering rules.\n");
@@ -5073,6 +5201,20 @@
 					IPACMERR("Failed to send WAN_IOC_NOTIFY_WAN_STATE as up %d\n ", wan_state.up);
 				}
 				close(fd_wwan_ioctl);
+
+				/* Store the Offload state. */
+				FILE *fp = NULL;
+				fp = fopen(IPA_OFFLOAD_TETHER_STATE_FILE_NAME, "w");
+				if (fp == NULL)
+				{
+					IPACMERR("Failed to write offload state to %s, error is %d - %s\n",
+						IPA_OFFLOAD_TETHER_STATE_FILE_NAME, errno, strerror(errno));
+				}
+				else
+				{
+					fprintf(fp, "UPSTREAM=%s,STATE=DOWN", dev_name);
+					fclose(fp);
+				}
 			}
 			if (ipa_pm_q6_check > 0)
 				ipa_pm_q6_check--;
diff --git a/ipacm/src/IPACM_Wlan.cpp b/ipacm/src/IPACM_Wlan.cpp
index eeb4b01..58bbeee 100644
--- a/ipacm/src/IPACM_Wlan.cpp
+++ b/ipacm/src/IPACM_Wlan.cpp
@@ -566,6 +566,7 @@
 			{
 				IPACMDBG_H("Add downstream for IP iptype %d.\n", data->prefix.iptype);
 				is_downstream_set[data->prefix.iptype] = true;
+				store_downstream_state(true, data->prefix.iptype);
 				memcpy(&prefix[data->prefix.iptype], &data->prefix,
 					sizeof(prefix[data->prefix.iptype]));
 
@@ -616,7 +617,7 @@
 			{
 				IPACMDBG_H("Del downstream for IP iptype %d.\n", data->prefix.iptype);
 				is_downstream_set[data->prefix.iptype] = false;
-
+				store_downstream_state(false, data->prefix.iptype);
 				if(is_upstream_set[data->prefix.iptype] == true)
 				{
 					IPACMDBG_H("Upstream was set before, deleting UL rules.\n");
@@ -1940,6 +1941,18 @@
 	}
 	IPACMDBG_H("finished deleting wan filtering rules\n ");
 
+	if (is_downstream_set[IPA_IP_v4])
+	{
+		is_downstream_set[IPA_IP_v4] = false;
+		store_downstream_state(false, IPA_IP_v4);
+	}
+
+	if (is_downstream_set[IPA_IP_v6])
+	{
+		is_downstream_set[IPA_IP_v6] = false;
+		store_downstream_state(false, IPA_IP_v6);
+	}
+
 	/* Delete v4 filtering rules */
 	if (ip_type != IPA_IP_v6 && rx_prop != NULL)
 	{
diff --git a/ipacm/src/IPACM_Xml.cpp b/ipacm/src/IPACM_Xml.cpp
index 28b7af5..7886550 100644
--- a/ipacm/src/IPACM_Xml.cpp
+++ b/ipacm/src/IPACM_Xml.cpp
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2013, 2019-2020, The Linux Foundation. All rights reserved.
+Copyright (c) 2013, 2019-2021, 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
@@ -68,6 +68,12 @@
 	 IPACM_firewall_conf_t *config
 );
 
+static int IPACM_filter_cfg_xml_parse_tree
+(
+	 xmlNode* xml_node,
+	 IPACM_filter_conf_t *config
+);
+
 /*Reads content (stored as child) of the element */
 static char* IPACM_read_content_element
 (
@@ -610,7 +616,7 @@
 						memset(content_buf, 0, sizeof(content_buf));
 						memcpy(content_buf, (void *)content, str_size);
 						config->extd_firewall_entries[config->num_extd_firewall_entries - 1].ip_vsn
-							 = (firewall_ip_version_enum)atoi(content_buf);
+							 = (ip_version_enum)atoi(content_buf);
 						IPACMDBG_H("\n IP family type is %d \n",
 								config->extd_firewall_entries[config->num_extd_firewall_entries - 1].ip_vsn);
 					}
@@ -1220,3 +1226,739 @@
 	} /* end while */
 	return ret_val;
 }
+
+/* This function read Filter Cfg XML and populate the Filter Cfg */
+int IPACM_read_filter_cfg_xml(char *xml_file, IPACM_filter_conf_t *config)
+{
+	xmlDocPtr doc = NULL;
+	xmlNode* root = NULL;
+	int ret_val;
+
+	IPACM_ASSERT(xml_file != NULL);
+	IPACM_ASSERT(config != NULL);
+
+	/* invoke the XML parser and obtain the parse tree */
+	doc = xmlReadFile(xml_file, "UTF-8", XML_PARSE_NOBLANKS);
+	if (doc == NULL) {
+		IPACMDBG_H("IPACM_xml_parse: libxml returned parse error\n");
+		return IPACM_FAILURE;
+	}
+	/*get the root of the tree*/
+	root = xmlDocGetRootElement(doc);
+
+	/* parse the xml tree returned by libxml*/
+	ret_val = IPACM_filter_cfg_xml_parse_tree(root, config);
+
+	if (ret_val != IPACM_SUCCESS)
+	{
+		IPACMDBG_H("IPACM_xml_parse: IPACM_filter_cfg_xml_parse_tree returned parse error!\n");
+	}
+
+	/* free the tree */
+	xmlFreeDoc(doc);
+
+	return ret_val;
+}
+
+/* This function traverses the filter cfg xml tree */
+static int IPACM_filter_cfg_xml_parse_tree
+(
+	 xmlNode* xml_node,
+	 IPACM_filter_conf_t *config
+)
+{
+	int mask_value_v6, mask_index;
+	int32_t ret_val = IPACM_SUCCESS;
+	char *content;
+	int str_size;
+	char content_buf[MAX_XML_STR_LEN];
+	struct in6_addr ip6_addr;
+
+	IPACM_ASSERT(config != NULL);
+
+	if (NULL == xml_node)
+		return ret_val;
+
+	while ( xml_node != NULL )
+	{
+		switch (xml_node->type)
+		{
+
+		case XML_ELEMENT_NODE:
+			{
+				if (0 == IPACM_util_icmp_string((char*)xml_node->name, system_TAG) ||
+						0 == IPACM_util_icmp_string((char*)xml_node->name, FilterCfg_TAG) ||
+						0 == IPACM_util_icmp_string((char*)xml_node->name, FilterEntry_TAG) ||
+						0 == IPACM_util_icmp_string((char*)xml_node->name, FilterEnabled_TAG)  ||
+						0 == IPACM_util_icmp_string((char*)xml_node->name, FilterDLAck_TAG))
+				{
+					if (0 == IPACM_util_icmp_string((char*)xml_node->name, FilterEntry_TAG))
+					{
+						/* increase Filter Config entry num */
+						config->num_filter_cfg_entries++;
+					}
+
+					if (0 == IPACM_util_icmp_string((char*)xml_node->name, FilterEnabled_TAG))
+					{
+						/* setup action of matched rules */
+						content = IPACM_read_content_element(xml_node);
+						if (content)
+						{
+								str_size = strlen(content);
+								memset(content_buf, 0, sizeof(content_buf));
+								memcpy(content_buf, (void *)content, str_size);
+							if (atoi(content_buf)==1)
+							{
+								config->filter_enable = true;
+							}
+							else
+							{
+								config->filter_enable = false;
+							}
+							IPACMDBG_H(" Filter Config Enabled :%d\n",config->filter_enable);
+						}
+						}
+
+					if (0 == IPACM_util_icmp_string((char*)xml_node->name, FilterDLAck_TAG))
+					{
+						/* setup if DL Ack filtering enabled or not */
+						content = IPACM_read_content_element(xml_node);
+						if (content)
+						{
+								str_size = strlen(content);
+								memset(content_buf, 0, sizeof(content_buf));
+								memcpy(content_buf, (void *)content, str_size);
+							if (atoi(content_buf)==1)
+							{
+								config->dl_ack_filter_enable = true;
+							}
+								else
+							{
+								config->dl_ack_filter_enable = false;
+							}
+							IPACMDBG_H(" DL Ack Filtering Enabled:%d\n", config->dl_ack_filter_enable);
+							}
+					}
+					/* go to child */
+					ret_val = IPACM_filter_cfg_xml_parse_tree(xml_node->children, config);
+				}
+				else if (0 == IPACM_util_icmp_string((char*)xml_node->name, IPFamily_TAG))
+				{
+					content = IPACM_read_content_element(xml_node);
+					if (content)
+					{
+						str_size = strlen(content);
+						memset(content_buf, 0, sizeof(content_buf));
+						memcpy(content_buf, (void *)content, str_size);
+						config->filter_cfg_entries[config->num_filter_cfg_entries - 1].ip_vsn
+							 = (ip_version_enum)atoi(content_buf);
+						IPACMDBG_H("\n IP family type is %d \n",
+								config->filter_cfg_entries[config->num_filter_cfg_entries - 1].ip_vsn);
+					}
+				}
+				else if (0 == IPACM_util_icmp_string((char*)xml_node->name, IPV4SourceAddress_TAG))
+				{
+					config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.attrib_mask |= IPA_FLT_SRC_ADDR;
+					/* go to child */
+					ret_val = IPACM_filter_cfg_xml_parse_tree(xml_node->children, config);
+				}
+				else if (0 == IPACM_util_icmp_string((char*)xml_node->name, IPV4SourceIPAddress_TAG))
+				{
+					content = IPACM_read_content_element(xml_node);
+					if (content)
+					{
+						str_size = strlen(content);
+						memset(content_buf, 0, sizeof(content_buf));
+						memcpy(content_buf, (void *)content, str_size);
+						content_buf[MAX_XML_STR_LEN-1] = '\0';
+						config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.u.v4.src_addr
+							 = ntohl(inet_addr(content_buf));
+						IPACMDBG_H("IPv4 source address is: %s \n", content_buf);
+					}
+				}
+				else if (0 == IPACM_util_icmp_string((char*)xml_node->name, IPV4SourceSubnetMask_TAG))
+				{
+					content = IPACM_read_content_element(xml_node);
+					if (content)
+					{
+						str_size = strlen(content);
+						memset(content_buf, 0, sizeof(content_buf));
+						memcpy(content_buf, (void *)content, str_size);
+						content_buf[MAX_XML_STR_LEN-1] = '\0';
+						config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.u.v4.src_addr_mask
+							 = ntohl(inet_addr(content_buf));
+						IPACMDBG_H("IPv4 source subnet mask is: %s \n", content_buf);
+					}
+				}
+				else if (0 == IPACM_util_icmp_string((char*)xml_node->name, IPV4DestinationAddress_TAG))
+				{
+					config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.attrib_mask |= IPA_FLT_DST_ADDR;
+					/* go to child */
+					ret_val = IPACM_filter_cfg_xml_parse_tree(xml_node->children, config);
+				}
+				else if (0 == IPACM_util_icmp_string((char*)xml_node->name, IPV4DestinationIPAddress_TAG))
+				{
+					content = IPACM_read_content_element(xml_node);
+					if (content)
+					{
+						str_size = strlen(content);
+						memset(content_buf, 0, sizeof(content_buf));
+						memcpy(content_buf, (void *)content, str_size);
+						content_buf[MAX_XML_STR_LEN-1] = '\0';
+						config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.u.v4.dst_addr
+							 = ntohl(inet_addr(content_buf));
+						IPACMDBG_H("IPv4 destination address is: %s \n", content_buf);
+					}
+				}
+				else if (0 == IPACM_util_icmp_string((char*)xml_node->name, IPV4DestinationSubnetMask_TAG))
+				{
+					content = IPACM_read_content_element(xml_node);
+					if (content)
+					{
+						str_size = strlen(content);
+						memset(content_buf, 0, sizeof(content_buf));
+						memcpy(content_buf, (void *)content, str_size);
+						content_buf[MAX_XML_STR_LEN-1] = '\0';
+						config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.u.v4.dst_addr_mask
+							= ntohl(inet_addr(content_buf));
+						IPACMDBG_H("IPv4 destination subnet mask is: %s \n", content_buf);
+						}
+					}
+				else if (0 == IPACM_util_icmp_string((char*)xml_node->name, IPV4TypeOfService_TAG))
+				{
+					config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.attrib_mask |= IPA_FLT_TOS;
+					/* go to child */
+					ret_val = IPACM_filter_cfg_xml_parse_tree(xml_node->children, config);
+				}
+				else if (0 == IPACM_util_icmp_string((char*)xml_node->name, TOSValue_TAG))
+				{
+					content = IPACM_read_content_element(xml_node);
+					if (content)
+					{
+						str_size = strlen(content);
+						memset(content_buf, 0, sizeof(content_buf));
+						memcpy(content_buf, (void *)content, str_size);
+						config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.u.v4.tos
+							 = atoi(content_buf);
+						// Here we do not know if it is TOS with mask or not, so we put at both places
+						config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.tos_value
+							= atoi(content_buf);
+						IPACMDBG_H("\n IPV4 TOS val is %d \n",
+										 config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.u.v4.tos);
+					}
+				}
+				else if (0 == IPACM_util_icmp_string((char*)xml_node->name, TOSMask_TAG))
+				{
+					content = IPACM_read_content_element(xml_node);
+					if (content)
+					{
+						uint8_t mask;
+
+						str_size = strlen(content);
+						memset(content_buf, 0, sizeof(content_buf));
+						memcpy(content_buf, (void *)content, str_size);
+						mask = atoi(content_buf);
+						IPACMDBG_H("\n IPv4 TOS mask is %u \n", mask);
+						if (mask != 0xFF) {
+							// TOS attribute cannot be used
+							config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.u.v4.tos = 0;
+							config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.tos_mask = mask;
+
+							config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.attrib_mask |=
+								IPA_FLT_TOS_MASKED;
+							config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.attrib_mask &=
+								~IPA_FLT_TOS;
+						} else {
+							config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.tos_value = 0;
+						}
+					}
+				}
+				else if (0 == IPACM_util_icmp_string((char*)xml_node->name, IPV4NextHeaderProtocol_TAG))
+				{
+					content = IPACM_read_content_element(xml_node);
+					if (content)
+					{
+						str_size = strlen(content);
+						memset(content_buf, 0, sizeof(content_buf));
+						memcpy(content_buf, (void *)content, str_size);
+						config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.attrib_mask |= IPA_FLT_PROTOCOL;
+						config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.u.v4.protocol = atoi(content_buf);
+						IPACMDBG_H("\n IPv4 next header prot is %d \n",
+								 config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.u.v4.protocol);
+					}
+				}
+				else if (0 == IPACM_util_icmp_string((char*)xml_node->name, IPV6SourceAddress_TAG))
+				{
+					config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.attrib_mask |=
+						 IPA_FLT_SRC_ADDR;
+					/* go to child */
+					ret_val = IPACM_filter_cfg_xml_parse_tree(xml_node->children, config);
+				}
+				else if (0 == IPACM_util_icmp_string((char*)xml_node->name, IPV6SourceIPAddress_TAG))
+				{
+					content = IPACM_read_content_element(xml_node);
+					if (content)
+					{
+						str_size = strlen(content);
+						memset(content_buf, 0, sizeof(content_buf));
+						memcpy(content_buf, (void *)content, str_size);
+						inet_pton(AF_INET6, content_buf, &ip6_addr);
+						memcpy(config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.u.v6.src_addr,
+									 ip6_addr.s6_addr, IPACM_IPV6_ADDR_LEN * sizeof(uint8_t));
+						config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.u.v6.src_addr[0]=ntohl(config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.u.v6.src_addr[0]);
+						config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.u.v6.src_addr[1]=ntohl(config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.u.v6.src_addr[1]);
+						config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.u.v6.src_addr[2]=ntohl(config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.u.v6.src_addr[2]);
+						config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.u.v6.src_addr[3]=ntohl(config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.u.v6.src_addr[3]);
+
+						IPACMDBG_H("\n ipv6 source addr is %d \n ",
+								config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.u.v6.src_addr[0]);
+					}
+				}
+				else if (0 == IPACM_util_icmp_string((char*)xml_node->name, IPV6SourcePrefix_TAG))
+				{
+					content = IPACM_read_content_element(xml_node);
+					if (content)
+					{
+						str_size = strlen(content);
+						memset(content_buf, 0, sizeof(content_buf));
+						memcpy(content_buf, (void *)content, str_size);
+						mask_value_v6 = atoi(content_buf);
+						for (mask_index = 0; mask_index < 4; mask_index++)
+						{
+							if (mask_value_v6 >= 32)
+							{
+								mask_v6(32, &(config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.u.v6.src_addr_mask[mask_index]));
+								mask_value_v6 -= 32;
+							}
+							else
+							{
+								mask_v6(mask_value_v6, &(config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.u.v6.src_addr_mask[mask_index]));
+								mask_value_v6 = 0;
+							}
+						}
+						IPACMDBG_H("\n ipv6 source prefix is %d \n", atoi(content_buf));
+					}
+				}
+				else if (0 == IPACM_util_icmp_string((char*)xml_node->name, IPV6DestinationAddress_TAG))
+				{
+					config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.attrib_mask |=
+						 IPA_FLT_DST_ADDR;
+					/* go to child */
+					ret_val = IPACM_filter_cfg_xml_parse_tree(xml_node->children, config);
+				}
+				else if (0 == IPACM_util_icmp_string((char*)xml_node->name, IPV6DestinationIPAddress_TAG))
+				{
+					content = IPACM_read_content_element(xml_node);
+					if (content)
+					{
+						str_size = strlen(content);
+						memset(content_buf, 0, sizeof(content_buf));
+						memcpy(content_buf, (void *)content, str_size);
+						inet_pton(AF_INET6, content_buf, &ip6_addr);
+						memcpy(config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.u.v6.dst_addr,
+									 ip6_addr.s6_addr, IPACM_IPV6_ADDR_LEN * sizeof(uint8_t));
+						config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.u.v6.dst_addr[0]=ntohl(config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.u.v6.dst_addr[0]);
+						config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.u.v6.dst_addr[1]=ntohl(config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.u.v6.dst_addr[1]);
+						config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.u.v6.dst_addr[2]=ntohl(config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.u.v6.dst_addr[2]);
+						config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.u.v6.dst_addr[3]=ntohl(config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.u.v6.dst_addr[3]);
+						IPACMDBG_H("\n ipv6 dest addr is %d \n",
+								 config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.u.v6.dst_addr[0]);
+					}
+				}
+				else if (0 == IPACM_util_icmp_string((char*)xml_node->name, IPV6DestinationPrefix_TAG))
+				{
+					content = IPACM_read_content_element(xml_node);
+					if (content)
+					{
+						str_size = strlen(content);
+						memset(content_buf, 0, sizeof(content_buf));
+						memcpy(content_buf, (void *)content, str_size);
+						mask_value_v6 = atoi(content_buf);
+						for (mask_index = 0; mask_index < 4; mask_index++)
+						{
+							if (mask_value_v6 >= 32)
+							{
+								mask_v6(32, &(config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.u.v6.dst_addr_mask[mask_index]));
+								mask_value_v6 -= 32;
+							}
+							else
+							{
+								mask_v6(mask_value_v6, &(config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.u.v6.dst_addr_mask[mask_index]));
+								mask_value_v6 = 0;
+							}
+						}
+						IPACMDBG_H("\n ipv6 dest prefix is %d \n", atoi(content_buf));
+					}
+				}
+				else if (0 == IPACM_util_icmp_string((char*)xml_node->name, IPV6TrafficClass_TAG))
+				{
+					config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.attrib_mask |= IPA_FLT_TC;
+					/* go to child */
+					ret_val = IPACM_filter_cfg_xml_parse_tree(xml_node->children, config);
+				}
+				else if (0 == IPACM_util_icmp_string((char*)xml_node->name, TrfClsValue_TAG))
+				{
+					content = IPACM_read_content_element(xml_node);
+					if (content)
+					{
+						str_size = strlen(content);
+						memset(content_buf, 0, sizeof(content_buf));
+						memcpy(content_buf, (void *)content, str_size);
+						config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.u.v6.tc
+							 = atoi(content_buf);
+						IPACMDBG_H("\n ipv6 trf class val is %d \n",
+								 config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.u.v6.tc);
+					}
+				}
+				else if (0 == IPACM_util_icmp_string((char*)xml_node->name, TrfClsMask_TAG))
+				{
+					content = IPACM_read_content_element(xml_node);
+					if (content)
+					{
+						str_size = strlen(content);
+						memset(content_buf, 0, sizeof(content_buf));
+						memcpy(content_buf, (void *)content, str_size);
+						config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.u.v6.tc
+							 &= atoi(content_buf);
+						IPACMDBG_H("\n ipv6 trf class mask is %d \n", atoi(content_buf));
+					}
+				}
+				else if (0 == IPACM_util_icmp_string((char*)xml_node->name, IPV6NextHeaderProtocol_TAG))
+				{
+					content = IPACM_read_content_element(xml_node);
+					if (content)
+					{
+						str_size = strlen(content);
+						memset(content_buf, 0, sizeof(content_buf));
+						memcpy(content_buf, (void *)content, str_size);
+						config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.attrib_mask |= IPA_FLT_NEXT_HDR;
+						config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.u.v6.next_hdr
+							 = atoi(content_buf);
+						IPACMDBG_H("\n ipv6 next header protocol is %d \n",
+								 config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.u.v6.next_hdr);
+					}
+				}
+				else if (0 == IPACM_util_icmp_string((char*)xml_node->name, TCPSource_TAG))
+				{
+					/* go to child */
+					ret_val = IPACM_filter_cfg_xml_parse_tree(xml_node->children, config);
+				}
+				else if (0 == IPACM_util_icmp_string((char*)xml_node->name, TCPSourcePort_TAG))
+				{
+					content = IPACM_read_content_element(xml_node);
+					if (content)
+					{
+						str_size = strlen(content);
+						memset(content_buf, 0, sizeof(content_buf));
+						memcpy(content_buf, (void *)content, str_size);
+						config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.src_port
+							 = atoi(content_buf);
+					}
+				}
+				else if (0 == IPACM_util_icmp_string((char*)xml_node->name, TCPSourceRange_TAG))
+				{
+					content = IPACM_read_content_element(xml_node);
+					if (content)
+					{
+						str_size = strlen(content);
+						memset(content_buf, 0, sizeof(content_buf));
+						memcpy(content_buf, (void *)content, str_size);
+						if (atoi(content_buf) != 0)
+						{
+							config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.attrib_mask |= IPA_FLT_SRC_PORT_RANGE;
+							config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.src_port_lo
+								= config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.src_port;
+							config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.src_port_hi
+								= config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.src_port + atoi(content_buf);
+							config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.src_port = 0;
+							IPACMDBG_H("\n tcp source port from %d to %d \n",
+									config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.src_port_lo,
+									config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.src_port_hi);
+						}
+						else
+						{
+							config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.attrib_mask |= IPA_FLT_SRC_PORT;
+							IPACMDBG_H("\n tcp source port= %d \n",
+									config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.src_port);
+						}
+					}
+				}
+				else if (0 == IPACM_util_icmp_string((char*)xml_node->name, TCPDestination_TAG))
+				{
+					/* go to child */
+					ret_val = IPACM_filter_cfg_xml_parse_tree(xml_node->children, config);
+				}
+				else if (0 == IPACM_util_icmp_string((char*)xml_node->name, TCPDestinationPort_TAG))
+				{
+					content = IPACM_read_content_element(xml_node);
+					if (content)
+					{
+						str_size = strlen(content);
+						memset(content_buf, 0, sizeof(content_buf));
+						memcpy(content_buf, (void *)content, str_size);
+						config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.dst_port
+							 = atoi(content_buf);
+					}
+				}
+				else if (0 == IPACM_util_icmp_string((char*)xml_node->name, TCPDestinationRange_TAG))
+				{
+					content = IPACM_read_content_element(xml_node);
+					if (content)
+					{
+						str_size = strlen(content);
+						memset(content_buf, 0, sizeof(content_buf));
+						memcpy(content_buf, (void *)content, str_size);
+						if(atoi(content_buf)!=0)
+						{
+							config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.attrib_mask |= IPA_FLT_DST_PORT_RANGE;
+							config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.dst_port_lo
+								= config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.dst_port;
+							config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.dst_port_hi
+								= config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.dst_port + atoi(content_buf);
+							config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.dst_port = 0;
+							IPACMDBG_H("\n tcp dest port from %d to %d \n",
+									config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.dst_port_lo,
+									config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.dst_port_hi);
+						}
+						else
+						{
+							config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.attrib_mask |= IPA_FLT_DST_PORT;
+							IPACMDBG_H("\n tcp dest port= %d \n",
+									config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.dst_port);
+						}
+					}
+				}
+				else if (0 == IPACM_util_icmp_string((char*)xml_node->name, UDPSource_TAG))
+				{
+					/* go to child */
+					ret_val = IPACM_filter_cfg_xml_parse_tree(xml_node->children, config);
+				}
+				else if (0 == IPACM_util_icmp_string((char*)xml_node->name, UDPSourcePort_TAG))
+				{
+					content = IPACM_read_content_element(xml_node);
+					if (content)
+					{
+						str_size = strlen(content);
+						memset(content_buf, 0, sizeof(content_buf));
+						memcpy(content_buf, (void *)content, str_size);
+						config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.src_port
+							 = atoi(content_buf);
+					}
+				}
+				else if (0 == IPACM_util_icmp_string((char*)xml_node->name, UDPSourceRange_TAG))
+				{
+					content = IPACM_read_content_element(xml_node);
+					if (content)
+					{
+						str_size = strlen(content);
+						memset(content_buf, 0, sizeof(content_buf));
+						memcpy(content_buf, (void *)content, str_size);
+						if(atoi(content_buf)!=0)
+						{
+							config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.attrib_mask |= IPA_FLT_SRC_PORT_RANGE;
+							config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.src_port_lo
+								= config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.src_port;
+							config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.src_port_hi
+								= config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.src_port + atoi(content_buf);
+							config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.src_port = 0;
+							IPACMDBG_H("\n udp source port from %d to %d \n",
+									config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.src_port_lo,
+									config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.src_port_hi);
+						}
+						else
+						{
+							config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.attrib_mask |= IPA_FLT_SRC_PORT;
+							IPACMDBG_H("\n udp source port= %d \n",
+									config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.src_port);
+						}
+					}
+				}
+				else if (0 == IPACM_util_icmp_string((char*)xml_node->name, UDPDestination_TAG))
+				{
+					/* go to child */
+					ret_val = IPACM_filter_cfg_xml_parse_tree(xml_node->children, config);
+				}
+				else if (0 == IPACM_util_icmp_string((char*)xml_node->name, UDPDestinationPort_TAG))
+				{
+					content = IPACM_read_content_element(xml_node);
+					if (content)
+					{
+						str_size = strlen(content);
+						memset(content_buf, 0, sizeof(content_buf));
+						memcpy(content_buf, (void *)content, str_size);
+						config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.dst_port
+							 = atoi(content_buf);
+					}
+				}
+				else if (0 == IPACM_util_icmp_string((char*)xml_node->name, UDPDestinationRange_TAG))
+				{
+					content = IPACM_read_content_element(xml_node);
+					if (content)
+					{
+						str_size = strlen(content);
+						memset(content_buf, 0, sizeof(content_buf));
+						memcpy(content_buf, (void *)content, str_size);
+						if(atoi(content_buf)!=0)
+						{
+							config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.attrib_mask |= IPA_FLT_DST_PORT_RANGE;
+							config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.dst_port_lo
+								= config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.dst_port;
+							config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.dst_port_hi
+								= config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.dst_port + atoi(content_buf);
+							config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.dst_port = 0;
+							IPACMDBG_H("\n UDP dest port from %d to %d \n",
+									config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.dst_port_lo,
+									config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.dst_port_hi);
+						}
+						else
+						{
+							config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.attrib_mask |= IPA_FLT_DST_PORT;
+							IPACMDBG_H("\n UDP dest port= %d \n",
+									config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.dst_port);
+						}
+					}
+				}
+				else if (0 == IPACM_util_icmp_string((char*)xml_node->name, ICMPType_TAG))
+				{
+					content = IPACM_read_content_element(xml_node);
+					if (content)
+					{
+						str_size = strlen(content);
+						memset(content_buf, 0, sizeof(content_buf));
+						memcpy(content_buf, (void *)content, str_size);
+						config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.type = atoi(content_buf);
+						config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.attrib_mask |= IPA_FLT_TYPE;
+						IPACMDBG_H("\n icmp type is %d \n",
+								 config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.type);
+					}
+				}
+				else if (0 == IPACM_util_icmp_string((char*)xml_node->name, ICMPCode_TAG))
+				{
+					content = IPACM_read_content_element(xml_node);
+					if (content)
+					{
+						str_size = strlen(content);
+						memset(content_buf, 0, sizeof(content_buf));
+						memcpy(content_buf, (void *)content, str_size);
+						config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.code = atoi(content_buf);
+						config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.attrib_mask |= IPA_FLT_CODE;
+						IPACMDBG_H("\n icmp code is %d \n",
+								 config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.code);
+					}
+				}
+				else if (0 == IPACM_util_icmp_string((char*)xml_node->name, ESPSPI_TAG))
+				{
+					content = IPACM_read_content_element(xml_node);
+					if (content)
+					{
+						str_size = strlen(content);
+						memset(content_buf, 0, sizeof(content_buf));
+						memcpy(content_buf, (void *)content, str_size);
+						config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.spi = atoi(content_buf);
+						config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.attrib_mask |= IPA_FLT_SPI;
+						IPACMDBG_H("\n esp spi is %d \n",
+								config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.spi);
+					}
+				}
+				else if (0 == IPACM_util_icmp_string((char*)xml_node->name, TCP_UDPSource_TAG))
+				{
+					/* go to child */
+					ret_val = IPACM_filter_cfg_xml_parse_tree(xml_node->children, config);
+				}
+				else if (0 == IPACM_util_icmp_string((char*)xml_node->name, TCP_UDPSourcePort_TAG))
+				{
+					content = IPACM_read_content_element(xml_node);
+					if (content)
+					{
+						str_size = strlen(content);
+						memset(content_buf, 0, sizeof(content_buf));
+						memcpy(content_buf, (void *)content,str_size);
+						config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.src_port
+							 = atoi(content_buf);
+					}
+				}
+				else if (0 == IPACM_util_icmp_string((char*)xml_node->name, TCP_UDPSourceRange_TAG))
+				{
+					content = IPACM_read_content_element(xml_node);
+					if (content)
+					{
+						str_size = strlen(content);
+						memset(content_buf, 0, sizeof(content_buf));
+						memcpy(content_buf, (void *)content, str_size);
+						if(atoi(content_buf)!=0)
+						{
+							config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.attrib_mask |= IPA_FLT_SRC_PORT_RANGE;
+							config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.src_port_lo
+								= config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.src_port;
+							config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.src_port_hi
+								= config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.src_port + atoi(content_buf);
+							config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.src_port = 0;
+							IPACMDBG_H("\n tcp_udp source port from %d to %d \n",
+									config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.src_port_lo,
+									config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.src_port_hi);
+						}
+						else
+						{
+							config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.attrib_mask |= IPA_FLT_SRC_PORT;
+							IPACMDBG_H("\n tcp_udp source port= %d \n",
+									config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.src_port);
+
+						}
+					}
+				}
+				else if (0 == IPACM_util_icmp_string((char*)xml_node->name, TCP_UDPDestination_TAG))
+				{
+					ret_val = IPACM_filter_cfg_xml_parse_tree(xml_node->children, config);
+				}
+				else if (0 == IPACM_util_icmp_string((char*)xml_node->name, TCP_UDPDestinationPort_TAG))
+				{
+					content = IPACM_read_content_element(xml_node);
+					if (content)
+					{
+						str_size = strlen(content);
+						memset(content_buf, 0, sizeof(content_buf));
+						memcpy(content_buf, (void *)content, str_size);
+						config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.dst_port
+							 = atoi(content_buf);
+					}
+				}
+				else if (0 == IPACM_util_icmp_string((char*)xml_node->name, TCP_UDPDestinationRange_TAG))
+				{
+					content = IPACM_read_content_element(xml_node);
+					if (content)
+					{
+						str_size = strlen(content);
+						memset(content_buf, 0, sizeof(content_buf));
+						memcpy(content_buf, (void *)content, str_size);
+						if(atoi(content_buf)!=0)
+						{
+							config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.attrib_mask |= IPA_FLT_DST_PORT_RANGE;
+							config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.dst_port_lo
+								= config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.dst_port;
+							config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.dst_port_hi
+								= config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.dst_port + atoi(content_buf);
+							config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.dst_port = 0;
+							IPACMDBG_H("\n tcp_udp dest port from %d to %d \n",
+								config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.dst_port_lo,
+								config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.dst_port_hi);
+						}
+						else
+						{
+							config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.attrib_mask |= IPA_FLT_DST_PORT;
+							IPACMDBG_H("\n tcp_udp dest port= %d \n",
+									config->filter_cfg_entries[config->num_filter_cfg_entries - 1].attrib.dst_port);
+						}
+					}
+				}
+			}
+			break;
+
+		default:
+			break;
+		}
+		/* go to sibling */
+		xml_node = xml_node->next;
+	} /* end while */
+	return ret_val;
+}
+
diff --git a/ipacm/src/ipacm.rc b/ipacm/src/ipacm.rc
index c1c876b..ec20898 100644
--- a/ipacm/src/ipacm.rc
+++ b/ipacm/src/ipacm.rc
@@ -1,4 +1,4 @@
-# Copyright (c) 2019, The Linux Foundation. All rights reserved.
+# Copyright (c) 2019-2021, 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
@@ -32,3 +32,8 @@
 
 on post-fs
     start vendor.ipacm
+
+on post-fs-data
+    #create prebuilt filter cfg xml file
+    copy /vendor/etc/IPACM_Filter_cfg.xml /data/vendor/ipa/IPACM_Filter_cfg.xml
+    chown radio radio /data/vendor/ipa/IPACM_Filter_cfg.xml
diff --git a/ipacm_vendor_product.mk b/ipacm_vendor_product.mk
index 57b7dc3..699f19e 100644
--- a/ipacm_vendor_product.mk
+++ b/ipacm_vendor_product.mk
@@ -2,6 +2,7 @@
 
 #IPACM_DATA
 IPACM_DATA += IPACM_cfg.xml
+IPACM_DATA += IPACM_Filter_cfg.xml
 IPACM_DATA += ipacm
 IPACM_DATA += ipacm.rc