Merge c40430c299f4694692553132086854b03eba3e52 on remote branch

Change-Id: Idb0831563ea664d9ef923e094ac9115a47e6d8d9
diff --git a/ipacm/inc/IPACM_ConntrackListener.h b/ipacm/inc/IPACM_ConntrackListener.h
index 7831498..2977af7 100644
--- a/ipacm/inc/IPACM_ConntrackListener.h
+++ b/ipacm/inc/IPACM_ConntrackListener.h
@@ -61,6 +61,13 @@
 
 }nat_entry_bundle;
 
+typedef struct _ct_entry
+{
+	struct nf_conntrack *ct;
+	u_int8_t  protocol;
+	enum nf_conntrack_msg_type type;
+}ct_entry;
+
 class IPACM_ConntrackListener : public IPACM_Listener
 {
 
@@ -78,13 +85,14 @@
 	uint32_t nonnat_iface_ipv4_addr[MAX_IFACE_ADDRESS];
 	uint32_t sta_clnt_ipv4_addr[MAX_STA_CLNT_IFACES];
 	IPACM_Config *pConfig;
-	struct nf_conntrack **ct_entries;
+	ct_entry *ct_entries;
+	ct_entry ct_cache[MAX_CONNTRACK_ENTRIES];
 #ifdef CT_OPT
 	IPACM_LanToLan *p_lan2lan;
 #endif
 
 	void ProcessCTMessage(void *);
-	void ProcessTCPorUDPMsg(struct nf_conntrack *,
+	bool ProcessTCPorUDPMsg(struct nf_conntrack *,
 	enum nf_conntrack_msg_type, u_int8_t);
 	void TriggerWANUp(void *);
 	void TriggerWANDown(uint32_t);
@@ -121,6 +129,9 @@
 	int  CreateConnTrackThreads(void);
 	void readConntrack(int fd);
 	void processConntrack(void);
+	void CacheORDeleteConntrack(struct nf_conntrack *ct,
+		enum nf_conntrack_msg_type type, u_int8_t protocol);
+	void processCacheConntrack(void);
 };
 
 extern IPACM_ConntrackListener *CtList;
diff --git a/ipacm/src/IPACM_ConntrackClient.cpp b/ipacm/src/IPACM_ConntrackClient.cpp
index f6bc9a6..29afd50 100644
--- a/ipacm/src/IPACM_ConntrackClient.cpp
+++ b/ipacm/src/IPACM_ConntrackClient.cpp
@@ -574,7 +574,8 @@
 	/* Block to catch events from net filter connection track */
 ctcatch:
 	ret = nfct_catch(pClient->udp_hdl);
-	if((ret == -1) && (errno != ENOMSG))
+	/* Due to conntrack dump, sequence number might mismatch for initial events. */
+	if((ret == -1) && (errno != ENOMSG) && (errno != EILSEQ))
 	{
 		IPACMDBG("(%d)(%d)(%s)\n", ret, errno, strerror(errno));
 		return NULL;
diff --git a/ipacm/src/IPACM_ConntrackListener.cpp b/ipacm/src/IPACM_ConntrackListener.cpp
index 9d06442..b991324 100644
--- a/ipacm/src/IPACM_ConntrackListener.cpp
+++ b/ipacm/src/IPACM_ConntrackListener.cpp
@@ -40,7 +40,6 @@
 IPACM_ConntrackListener::IPACM_ConntrackListener()
 {
 	 IPACMDBG("\n");
-
 	 isNatThreadStart = false;
 	 isCTReg = false;
 	 WanUp = false;
@@ -70,6 +69,9 @@
 #ifdef CT_OPT
 	 p_lan2lan = IPACM_LanToLan::getLan2LanInstance();
 #endif
+
+	 /* Initialize the CT cache. */
+	 memset(ct_cache, 0, sizeof(ct_cache));
 }
 
 void IPACM_ConntrackListener::event_callback(ipa_cm_event_id evt,
@@ -105,6 +107,8 @@
 			{
 				processConntrack();
 			}
+			/* Process the cached entries. */
+			processCacheConntrack();
 			break;
 
 	 case IPA_HANDLE_WAN_DOWN:
@@ -675,6 +679,7 @@
 {
 	 ipacm_ct_evt_data *evt_data = (ipacm_ct_evt_data *)param;
 	 u_int8_t l4proto = 0;
+	 bool cache_ct = false;
 
 #ifdef IPACM_DEBUG
 	 char buf[1024];
@@ -696,11 +701,14 @@
 	 }
 	 else
 	 {
-			ProcessTCPorUDPMsg(evt_data->ct, evt_data->type, l4proto);
+			cache_ct = ProcessTCPorUDPMsg(evt_data->ct, evt_data->type, l4proto);
 	 }
 
 	 /* Cleanup item that was allocated during the original CT callback */
-	 nfct_destroy(evt_data->ct);
+	 if (!cache_ct)
+		nfct_destroy(evt_data->ct);
+	 else
+	 	CacheORDeleteConntrack(evt_data->ct, evt_data->type, l4proto);
 	 return;
 }
 
@@ -847,7 +855,7 @@
 	}
 	else if (IPPROTO_UDP == input->rule->protocol)
 	{
-		if (NFCT_T_NEW == input->type)
+		if (NFCT_T_NEW == input->type || NFCT_T_UPDATE == input->type)
 		{
 			IPACMDBG("New UDP connection at time %ld\n", time(NULL));
 			if (!CtList->isWanUp())
@@ -1037,7 +1045,7 @@
 }
 
 /* conntrack send in host order and ipa expects in host order */
-void IPACM_ConntrackListener::ProcessTCPorUDPMsg(
+bool IPACM_ConntrackListener::ProcessTCPorUDPMsg(
 	 struct nf_conntrack *ct,
 	 enum nf_conntrack_msg_type type,
 	 u_int8_t l4proto)
@@ -1046,6 +1054,7 @@
 	 uint32_t status = 0;
 	 uint32_t orig_src_ip, orig_dst_ip;
 	 bool isAdd = false;
+	 bool cache_ct = false;
 
 	 nat_entry_bundle nat_entry;
 	 nat_entry.isTempEntry = false;
@@ -1075,7 +1084,7 @@
 		 if(orig_src_ip == 0)
 		 {
 			 IPACMERR("unable to retrieve orig src ip address\n");
-			 return;
+			 return cache_ct;
 		 }
 
 		 orig_dst_ip = nfct_get_attr_u32(ct, ATTR_ORIG_IPV4_DST);
@@ -1083,7 +1092,7 @@
 		 if(orig_dst_ip == 0)
 		 {
 			 IPACMERR("unable to retrieve orig dst ip address\n");
-			 return;
+			 return cache_ct;
 		 }
 
 		if(orig_src_ip == wan_ipaddr)
@@ -1104,8 +1113,11 @@
 #ifdef CT_OPT
 			HandleLan2Lan(ct, type, &rule);
 #endif
-		 	IPACMDBG("Neither source Nor destination nat\n");
-		 	goto IGNORE;
+		 	IPACMDBG("Neither source Nor destination nat.\n");
+			/* If WAN is not up, cache the event. */
+			if(!CtList->isWanUp())
+				cache_ct = true;
+			goto IGNORE;
 		}
 	}
 
@@ -1137,7 +1149,7 @@
 	CheckSTAClient(&rule, &nat_entry.isTempEntry);
 	nat_entry.rule = &rule;
 	AddORDeleteNatEntry(&nat_entry);
-	return;
+	return cache_ct;
 
 IGNORE:
 	IPACMDBG_H("ignoring below Nat Entry\n");
@@ -1147,7 +1159,7 @@
 	IPACMDBG("private port or src port: 0x%x, Decimal:%d\n", rule.private_port, rule.private_port);
 	IPACMDBG("public port or reply dst port: 0x%x, Decimal:%d\n", rule.public_port, rule.public_port);
 	IPACMDBG("Protocol: %d, destination nat flag: %d\n", rule.protocol, rule.dst_nat);
-	return;
+	return cache_ct;
 }
 
 void IPACM_ConntrackListener::HandleSTAClientAddEvt(uint32_t clnt_ip_addr)
@@ -1240,9 +1252,9 @@
 		.msg_flags	= 0,
 	};
 
-	len = MAX_CONNTRACK_ENTRIES * sizeof(struct nf_conntrack **);
+	len = MAX_CONNTRACK_ENTRIES * sizeof(ct_entry);
 
-	ct_entries = (struct nf_conntrack **) malloc(len);
+	ct_entries = (ct_entry *) malloc(len);
 	if(ct_entries == NULL)
 	{
 		IPACMERR("unable to allocate ct_entries memory \n");
@@ -1264,7 +1276,7 @@
 		recv_bytes = recvmsg(fd, &msg, 0);
 		if(recv_bytes < 0)
 		{
-			IPACMDBG_H("error in receiving conntrack entries %d%s",errno, strerror(errno));
+			IPACMDBG_H("error in receiving conntrack entries %d%s\n",errno, strerror(errno));
 			break;
 		}
 		else
@@ -1283,18 +1295,20 @@
 				if (ct != NULL)
 				{
 					int parseResult =  nfct_parse_conntrack((nf_conntrack_msg_type) NFCT_T_ALL,nl_header, ct);
-					if(parseResult != NFCT_T_ERROR)
+					if(parseResult != NFCT_T_ERROR && parseResult != 0)
 					{
-						ct_entries[index++] = ct;
+						ct_entries[index].ct = ct;
+						ct_entries[index++].type = (nf_conntrack_msg_type)parseResult;
 					}
 					else
 					{
 						IPACMDBG_H("error in parsing  %d%s \n", errno, strerror(errno));
+						nfct_destroy(ct);
 					}
 				}
 				else
 				{
-					IPACMDBG_H("ct allocation failed");
+					IPACMDBG_H("ct allocation failed\n");
 				}
 				if (nl_header->nlmsg_type == NLMSG_DONE)
 				{
@@ -1322,15 +1336,14 @@
 	uint8_t ip_type;
 	int index = 0;
 	ipacm_ct_evt_data *ct_data;
-	enum nf_conntrack_msg_type type = NFCT_T_ALL;
 	IPACMDBG_H("process conntrack started \n");
 	if(ct_entries != NULL)
 	{
-		while(ct_entries[index] != NULL)
+		while(ct_entries[index].ct != NULL)
 		{
-			ip_type = nfct_get_attr_u8(ct_entries[index], ATTR_REPL_L3PROTO);
-			if((AF_INET == ip_type) && isLocalHostAddr(nfct_get_attr_u32(ct_entries[index], ATTR_ORIG_IPV4_SRC),
-					nfct_get_attr_u32(ct_entries[index], ATTR_ORIG_IPV4_DST)))
+			ip_type = nfct_get_attr_u8(ct_entries[index].ct, ATTR_REPL_L3PROTO);
+			if((AF_INET == ip_type) && isLocalHostAddr(nfct_get_attr_u32(ct_entries[index].ct, ATTR_ORIG_IPV4_SRC),
+					nfct_get_attr_u32(ct_entries[index].ct, ATTR_ORIG_IPV4_DST)))
 			{
 				IPACMDBG_H(" loopback entry \n");
 				goto IGNORE;
@@ -1351,8 +1364,8 @@
 				goto IGNORE;
 			}
 
-			ct_data->ct = ct_entries[index];
-			ct_data->type = type;
+			ct_data->ct = ct_entries[index].ct;
+			ct_data->type = ct_entries[index].type;
 
 #ifdef CT_OPT
 			if(AF_INET6 == ip_type)
@@ -1366,7 +1379,7 @@
 		free(ct_data);
 		continue;
 IGNORE:
-		nfct_destroy(ct_entries[index]);
+		nfct_destroy(ct_entries[index].ct);
 		index++;
 		}
 	}
@@ -1377,6 +1390,114 @@
 	}
 	isProcessCTDone = true;
 	free(ct_entries);
+	ct_entries = NULL;
 	IPACMDBG_H("process conntrack ended. Number of entries:%d \n", index);
 	return;
 }
+
+void IPACM_ConntrackListener::CacheORDeleteConntrack
+(
+	struct nf_conntrack *ct,
+	enum nf_conntrack_msg_type type,
+	u_int8_t protocol
+)
+{
+	u_int8_t tcp_state;
+	int i = 0, free_idx = -1;
+
+	IPACMDBG("CT entry, type (%d), protocol(%d)\n", type, protocol);
+	/* Check for duplicate entry and in parallel find first free index. */
+	for(; i < MAX_CONNTRACK_ENTRIES; i++)
+	{
+		if (ct_cache[i].ct != NULL)
+		{
+			if (nfct_cmp(ct_cache[i].ct, ct, NFCT_CMP_ORIG | NFCT_CMP_REPL))
+			{
+				/* Duplicate entry. */
+				IPACMDBG("Duplicate CT entry, type (%d), protocol(%d)\n",
+					type, protocol);
+				break;
+			}
+		}
+		else if ((ct_cache[i].ct == NULL) && (free_idx == -1))
+		{
+			/* Cache the first free index. */
+			free_idx = i;
+		}
+	}
+
+	/* Duplicate entry handling. */
+	if (i < MAX_CONNTRACK_ENTRIES)
+	{
+		if (IPPROTO_TCP == protocol)
+		{
+			tcp_state = nfct_get_attr_u8(ct, ATTR_TCP_STATE);
+			if (TCP_CONNTRACK_FIN_WAIT == tcp_state || type == NFCT_T_DESTROY)
+			{
+				IPACMDBG("TCP state TCP_CONNTRACK_FIN_WAIT(%d) "
+							 "or type NFCT_T_DESTROY\n", tcp_state);
+				nfct_destroy(ct_cache[i].ct);
+				nfct_destroy(ct);
+				memset(&ct_cache[i], 0, sizeof(ct_cache[i]));
+				return ;
+			}
+		}
+		if ((IPPROTO_UDP == protocol) && (type == NFCT_T_DESTROY))
+		{
+			IPACMDBG("UDP type NFCT_T_DESTROY\n");
+			nfct_destroy(ct_cache[i].ct);
+			nfct_destroy(ct);
+			memset(&ct_cache[i], 0, sizeof(ct_cache[i]));
+			return;
+		}
+	}
+	else if ((i == MAX_CONNTRACK_ENTRIES) &&
+		(type != NFCT_T_DESTROY) && (free_idx != -1))
+	{
+		if (IPPROTO_TCP == protocol)
+		{
+			tcp_state = nfct_get_attr_u8(ct, ATTR_TCP_STATE);
+			if (TCP_CONNTRACK_ESTABLISHED == tcp_state)
+			{
+				IPACMDBG("TCP state TCP_CONNTRACK_ESTABLISHED\n");
+				/* Cache the entry. */
+				ct_cache[free_idx].ct = ct;
+				ct_cache[free_idx].protocol = protocol;
+				ct_cache[free_idx].type = type;
+				return;
+			}
+		}
+		if (IPPROTO_UDP == protocol)
+		{
+			if (NFCT_T_NEW  == type)
+			{
+				IPACMDBG("New UDP connection\n");
+				/* Cache the entry. */
+				ct_cache[free_idx].ct = ct;
+				ct_cache[free_idx].protocol = protocol;
+				ct_cache[free_idx].type = type;
+				return;
+			}
+		}
+	}
+	/* In all other cases, free the conntracy entry. */
+	nfct_destroy(ct);
+	return ;
+}
+void IPACM_ConntrackListener::processCacheConntrack(void)
+{
+	int i = 0;
+
+	IPACMDBG("Entry:\n");
+	for(; i < MAX_CONNTRACK_ENTRIES; i++)
+	{
+		if (ct_cache[i].ct != NULL)
+		{
+			ProcessTCPorUDPMsg(ct_cache[i].ct, ct_cache[i].type, ct_cache[i].protocol);
+			nfct_destroy(ct_cache[i].ct);
+			memset(&ct_cache[i], 0, sizeof(ct_cache[i]));
+		}
+	}
+	IPACMDBG("Exit:\n");
+}
+
diff --git a/ipacm/src/IPACM_Lan.cpp b/ipacm/src/IPACM_Lan.cpp
index 65bd3ca..7e7e875 100644
--- a/ipacm/src/IPACM_Lan.cpp
+++ b/ipacm/src/IPACM_Lan.cpp
@@ -751,25 +751,7 @@
 		if (ipa_interface_index == ipa_if_num)
 		{
 			IPACMDBG_H("Received IPA_DOWNSTREAM_ADD event.\n");
-#ifdef FEATURE_IPA_ANDROID
-			if (IPACM_Wan::isXlat() && (data->prefix.iptype == IPA_IP_v4))
-			{
-				/* indicate v4-offload */
-				IPACM_OffloadManager::num_offload_v4_tethered_iface++;
-				IPACMDBG_H("in xlat: update num_offload_v4_tethered_iface %d\n", IPACM_OffloadManager::num_offload_v4_tethered_iface);
 
-				/* xlat not support for 2st tethered iface */
-				if (IPACM_OffloadManager::num_offload_v4_tethered_iface > 1)
-				{
-					IPACMDBG_H("Not support 2st downstream iface %s for xlat, cur: %d\n", dev_name,
-						IPACM_OffloadManager::num_offload_v4_tethered_iface);
-					return;
-				}
-			}
-
-			IPACMDBG_H(" support downstream iface %s, cur %d\n", dev_name,
-				IPACM_OffloadManager::num_offload_v4_tethered_iface);
-#endif
 			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);
@@ -1905,6 +1887,25 @@
 		ret = handle_uplink_filter_rule(ext_prop, iptype, xlat_mux_id);
 		modem_ul_v6_set = true;
 	} else if (iptype ==IPA_IP_v4 && modem_ul_v4_set == false) {
+#ifdef FEATURE_IPA_ANDROID
+				if (IPACM_Wan::isXlat())
+				{
+					/* indicate v4-offload */
+					IPACM_OffloadManager::num_offload_v4_tethered_iface++;
+					IPACMDBG_H("in xlat: update num_offload_v4_tethered_iface %d\n", IPACM_OffloadManager::num_offload_v4_tethered_iface);
+
+					/* xlat not support for 2st tethered iface */
+					if (IPACM_OffloadManager::num_offload_v4_tethered_iface > 1)
+					{
+						IPACMDBG_H("Not support 2st downstream iface %s for xlat, cur: %d\n", dev_name,
+							IPACM_OffloadManager::num_offload_v4_tethered_iface);
+						return IPACM_FAILURE;
+					}
+				}
+
+				IPACMDBG_H(" support downstream iface %s, cur %d\n", dev_name,
+					IPACM_OffloadManager::num_offload_v4_tethered_iface);
+#endif
 		/* add MTU rules for ipv4 */
 		handle_private_subnet_android(IPA_IP_v4);
 
diff --git a/ipacm/src/IPACM_OffloadManager.cpp b/ipacm/src/IPACM_OffloadManager.cpp
index b5afda7..708725b 100644
--- a/ipacm/src/IPACM_OffloadManager.cpp
+++ b/ipacm/src/IPACM_OffloadManager.cpp
@@ -122,7 +122,7 @@
 {
 	struct timeval tv;
 	IPACM_ConntrackClient *cc;
-	int on = 1, rel;
+	int on = 1, rel = 0;
 	struct sockaddr_nl	local;
 	unsigned int addr_len;
 
@@ -156,15 +156,6 @@
 			IPACMERR( "setsockopt returned error code %d ( %s )", errno, strerror( errno ) );
 		}
 	} else if (groups == cc->subscrips_udp) {
-		cc->fd_udp = dup(fd);
-		IPACMDBG_H("Received fd %d with groups %d.\n", fd, groups);
-		/* set netlink buf */
-		rel = setsockopt(cc->fd_udp, SOL_NETLINK, NETLINK_NO_ENOBUFS, &on, sizeof(int) );
-		if (rel == -1)
-		{
-			IPACMERR("setsockopt returned error code %d ( %s )\n", errno, strerror(errno));
-		}
-
 		/* Set receive timeout to 1s on the FD which is used to read conntrack dump. */
 		memset(&tv,0, sizeof(struct timeval));
 		tv.tv_sec = 1; /* 1s timeout */
@@ -183,6 +174,22 @@
 		   for both TCP/UDP embedded traffic.
 		*/
 		CtList->readConntrack(fd);
+		/* Reset receive timeout on the FD which is used to read conntrack dump. */
+		memset(&tv,0, sizeof(struct timeval));
+		rel = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO,(struct timeval *)&tv,sizeof(struct timeval));
+		if (rel == -1)
+		{
+			IPACMERR("setsockopt returned error code %d ( %s )\n", errno, strerror(errno));
+		}
+
+		cc->fd_udp = dup(fd);
+		IPACMDBG_H("Received fd %d with groups %d.\n", fd, groups);
+		/* set netlink buf */
+		rel = setsockopt(cc->fd_udp, SOL_NETLINK, NETLINK_NO_ENOBUFS, &on, sizeof(int) );
+		if (rel == -1)
+		{
+			IPACMERR("setsockopt returned error code %d ( %s )\n", errno, strerror(errno));
+		}
 	} else {
 		IPACMERR("Received unexpected fd with groups %d.\n", groups);
 	}