Merge "IPACM: Fix conntrack event comes earlier than new neighbor"
diff --git a/ipacm/inc/IPACM_LanToLan.h b/ipacm/inc/IPACM_LanToLan.h
index 570db14..06d5832 100644
--- a/ipacm/inc/IPACM_LanToLan.h
+++ b/ipacm/inc/IPACM_LanToLan.h
@@ -69,6 +69,7 @@
 
 typedef list<peer_info> peer_info_list;
 typedef list<offload_link_info> offload_link_info_list;
+typedef list<ipacm_event_connection> connection_list;
 
 struct client_info
 {
@@ -114,6 +115,9 @@
 		client_table_v4 client_info_v4_;
 		client_table_v6 client_info_v6_;
 
+		connection_list connection_v4_;
+		connection_list connection_v6_;
+
 		static IPACM_LanToLan* p_instance;
 
 		void event_callback(ipa_cm_event_id event, void* param);
@@ -158,6 +162,14 @@
 
 		bool is_lan2lan_connection(ipacm_event_connection* data);
 
+		bool is_potential_lan2lan_connection(ipacm_event_connection* new_conn);
+
+		void cache_new_connection(ipacm_event_connection* new_conn);
+
+		void remove_cache_connection(ipacm_event_connection* del_conn);
+
+		void check_cache_connection(ipa_ip_type iptype, client_info* client);
+
 };
 
 #endif
diff --git a/ipacm/src/IPACM_ConntrackClient.cpp b/ipacm/src/IPACM_ConntrackClient.cpp
index 448fa8f..d8fe726 100644
--- a/ipacm/src/IPACM_ConntrackClient.cpp
+++ b/ipacm/src/IPACM_ConntrackClient.cpp
@@ -196,7 +196,7 @@
 		return -1;
 	}
 	IPACMDBG("Interface (%s) address %s\n", ifr.ifr_name, inet_ntoa(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr));
-  ipv4_addr = ntohl(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr);
+	ipv4_addr = ntohl(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr);
 	close(fd);
 
 	/* ignore whatever is destined to or originates from broadcast ip address */
diff --git a/ipacm/src/IPACM_ConntrackListener.cpp b/ipacm/src/IPACM_ConntrackListener.cpp
index 239d6f4..ae11d34 100644
--- a/ipacm/src/IPACM_ConntrackListener.cpp
+++ b/ipacm/src/IPACM_ConntrackListener.cpp
@@ -60,7 +60,6 @@
 	 IPACM_EvtDispatcher::registr(IPA_NEIGH_CLIENT_IP_ADDR_DEL_EVENT, this);
 
 	IPACMDBG("creating conntrack threads\n");
-	CreateConnTrackThreads();
 
 #ifdef CT_OPT
 	 p_lan2lan = IPACM_LanToLan::getLan2LanInstance();
@@ -94,6 +93,7 @@
 
 	 case IPA_HANDLE_WAN_UP:
 			IPACMDBG("Received IPA_HANDLE_WAN_UP event\n");
+			CreateConnTrackThreads();
 			if(!isWanUp())
 			{
 				TriggerWANUp(data);
@@ -116,7 +116,8 @@
 			IPACMDBG("Received event: %d with ifname: %s and address: 0x%x\n",
 							 evt, ((ipacm_event_iface_up *)data)->ifname,
 							 ((ipacm_event_iface_up *)data)->ipv4_addr);
-   		IPACM_ConntrackClient::UpdateUDPFilters(data, false);
+			CreateConnTrackThreads();
+			IPACM_ConntrackClient::UpdateUDPFilters(data, false);
 			IPACM_ConntrackClient::UpdateTCPFilters(data, false);
 			break;
 
diff --git a/ipacm/src/IPACM_LanToLan.cpp b/ipacm/src/IPACM_LanToLan.cpp
index dcd7a08..dcb95b4 100644
--- a/ipacm/src/IPACM_LanToLan.cpp
+++ b/ipacm/src/IPACM_LanToLan.cpp
@@ -43,6 +43,11 @@
 #include "IPACM_LanToLan.h"
 #include "IPACM_Wlan.h"
 
+#define ipv6_multicast_addr 0xff000000
+#define ipv6_multicast_mask 0xff000000
+
+#define max_cache_connection 20
+
 IPACM_LanToLan* IPACM_LanToLan::p_instance = NULL;
 
 IPACM_LanToLan::IPACM_LanToLan()
@@ -209,6 +214,7 @@
 		client_ptr->p_iface = data->p_iface;
 
 		generate_new_connection(data->iptype, client_ptr);
+		check_cache_connection(data->iptype, client_ptr);
 	}
 	else	//the client is found
 	{
@@ -239,6 +245,7 @@
 				client_ptr->p_iface = data->p_iface;
 
 				generate_new_connection(data->iptype, client_ptr);
+				check_cache_connection(data->iptype, client_ptr);
 			}
 		}
 		else 	//the client is inactive
@@ -259,6 +266,7 @@
 
 			check_potential_link(data->iptype, client_ptr);
 			generate_new_connection(data->iptype, client_ptr);
+			check_cache_connection(data->iptype, client_ptr);
 		}
 	}
 	IPACMDBG("There are %d clients in v4 table and %d clients in v6 table.\n", client_info_v4_.size(), client_info_v6_.size());
@@ -1288,6 +1296,7 @@
 	if(is_lan2lan_connection(new_conn) == false)
 	{
 		IPACMDBG("The connection is not lan2lan connection.\n");
+		cache_new_connection(new_conn);
 		return;
 	}
 
@@ -1331,6 +1340,7 @@
 	if(is_lan2lan_connection(new_conn) == false)
 	{
 		IPACMDBG("The connection is not lan2lan connection.\n");
+		remove_cache_connection(new_conn);
 		return;
 	}
 
@@ -1393,3 +1403,203 @@
 {
 	return p_instance;
 }
+
+bool IPACM_LanToLan::is_potential_lan2lan_connection(ipacm_event_connection* new_conn)
+{
+	int i, num_private_subnet;
+	bool src_is_valid = false;
+	bool dst_is_valid = false;
+
+	if(new_conn->iptype == IPA_IP_v4)
+	{
+		num_private_subnet = IPACM_Iface::ipacmcfg->ipa_num_private_subnet;
+		for(i=0; i<num_private_subnet; i++)
+		{
+			if( (new_conn->src_ipv4_addr & IPACM_Iface::ipacmcfg->private_subnet_table[i].subnet_mask)
+				== (IPACM_Iface::ipacmcfg->private_subnet_table[i].subnet_addr & IPACM_Iface::ipacmcfg->private_subnet_table[i].subnet_mask) )
+			{
+				src_is_valid = true;
+			}
+			if( (new_conn->dst_ipv4_addr & IPACM_Iface::ipacmcfg->private_subnet_table[i].subnet_mask)
+				== (IPACM_Iface::ipacmcfg->private_subnet_table[i].subnet_addr & IPACM_Iface::ipacmcfg->private_subnet_table[i].subnet_mask) )
+			{
+				dst_is_valid = true;
+			}
+		}
+
+		if(src_is_valid && dst_is_valid)
+		{
+			IPACMDBG("Both src and dst are potentially in subnet.\n");
+			return true;
+		}
+	}
+	else
+	{
+		if( (new_conn->src_ipv6_addr[0] & ipv6_multicast_mask) != (ipv6_multicast_addr & ipv6_multicast_mask) )
+		{
+			src_is_valid = true;
+		}
+		if( (new_conn->dst_ipv6_addr[0] & ipv6_multicast_mask) != (ipv6_multicast_addr & ipv6_multicast_mask) )
+		{
+			dst_is_valid = true;
+		}
+
+		if(src_is_valid && dst_is_valid)
+		{
+			IPACMDBG("Both src and dst are potentially in subnet.\n");
+			return true;
+		}
+	}
+
+	IPACMDBG("This connection is not a lan2lan connection potentially.\n");
+	return false;
+}
+
+void IPACM_LanToLan::cache_new_connection(ipacm_event_connection* new_conn)
+{
+	if(is_potential_lan2lan_connection(new_conn) == true)
+	{
+		if(new_conn->iptype == IPA_IP_v4)
+		{
+			if(connection_v4_.size() == max_cache_connection)
+			{
+				IPACMDBG("Cached ipv4 connections already reach maximum, clear up the list.\n");
+				connection_v4_.clear();
+			}
+
+			connection_v4_.push_back(*new_conn);
+			IPACMDBG("Cache an ipv4 connection, now the number of ipv4 cache connection is %d.\n", connection_v4_.size());
+		}
+		else
+		{
+			if(connection_v6_.size() == max_cache_connection)
+			{
+				IPACMDBG("Cached ipv6 connections already reach maximum, clear up the list.\n");
+				connection_v6_.clear();
+			}
+
+			connection_v6_.push_back(*new_conn);
+			IPACMDBG("Cache an ipv6 connection, now the number of ipv6 cache connection is %d.\n", connection_v6_.size());
+		}
+	}
+	return;
+}
+
+void IPACM_LanToLan::remove_cache_connection(ipacm_event_connection* del_conn)
+{
+	connection_list::iterator it;
+	if(is_potential_lan2lan_connection(del_conn) == true)
+	{
+		if(del_conn->iptype == IPA_IP_v4)
+		{
+			for(it = connection_v4_.begin(); it != connection_v4_.end(); it++)
+			{
+				if(it->src_ipv4_addr == del_conn->src_ipv4_addr && it->dst_ipv4_addr == del_conn->dst_ipv4_addr)
+				{
+					IPACMDBG("Find the cached ipv4 connection, remove it from list.\n");
+					connection_v4_.erase(it);
+					IPACMDBG("Now the number of ipv4 cache connection is %d.\n", connection_v4_.size());
+					return;
+				}
+			}
+			IPACMDBG("Do not find the cached ipv4 connection, do nothing.\n");
+		}
+		else
+		{
+			for(it = connection_v6_.begin(); it != connection_v6_.end(); it++)
+			{
+				if(memcmp(it->src_ipv6_addr, del_conn->src_ipv6_addr, 4*sizeof(uint32_t)) == 0
+					&& memcmp(it->dst_ipv6_addr, del_conn->dst_ipv6_addr, 4*sizeof(uint32_t)) == 0 )
+				{
+					IPACMDBG("Find the cached ipv6 connection, remove it from list.\n");
+					connection_v6_.erase(it);
+					IPACMDBG("Now the number of ipv6 cache connection is %d.\n", connection_v6_.size());
+					return;
+				}
+			}
+			IPACMDBG("Do not find the cached ipv6 connection, do nothing.\n");
+		}
+	}
+	return;
+}
+
+void IPACM_LanToLan::check_cache_connection(ipa_ip_type iptype, client_info* client)
+{
+#ifdef CT_OPT
+	connection_list::iterator it;
+	if(iptype == IPA_IP_v4)
+	{
+		it = connection_v4_.begin();
+		while(it != connection_v4_.end())
+		{
+			if( (it->src_ipv4_addr == client->ip.ipv4_addr && client_info_v4_.count(it->dst_ipv4_addr) > 0)
+				|| (it->dst_ipv4_addr == client->ip.ipv4_addr && client_info_v4_.count(it->src_ipv4_addr) > 0) )
+			{
+				IPACMDBG("Found a cache connection for src client 0x%08x and dst client 0x%08x.\n", it->src_ipv4_addr, it->dst_ipv4_addr);
+				ipacm_cmd_q_data evt;
+				ipacm_event_connection* conn;
+
+				conn = (ipacm_event_connection*)malloc(sizeof(ipacm_event_connection));
+				if(conn == NULL)
+				{
+					IPACMERR("Failed to allocate memory for new_connection event.\n");
+					return;
+				}
+				memcpy(conn, &(*it), sizeof(ipacm_event_connection));
+
+				memset(&evt, 0, sizeof(evt));
+				evt.event = IPA_LAN_TO_LAN_NEW_CONNECTION;
+				evt.evt_data = (void*)conn;
+				IPACM_EvtDispatcher::PostEvt(&evt);
+
+				it = connection_v4_.erase(it);
+				IPACMDBG("Now the number of cache connections is %d.\n", connection_v4_.size());
+			}
+			else
+			{
+				it++;
+			}
+		}
+	}
+	else
+	{
+		uint64_t src_v6_addr, dst_v6_addr;
+		it = connection_v6_.begin();
+		while(it != connection_v6_.end())
+		{
+			memcpy(&src_v6_addr, &(it->src_ipv6_addr[2]), sizeof(uint64_t));
+			memcpy(&dst_v6_addr, &(it->dst_ipv6_addr[2]), sizeof(uint64_t));
+			if( (memcmp(it->src_ipv6_addr, client->ip.ipv6_addr, 4*sizeof(uint32_t)) == 0 && client_info_v6_.count(dst_v6_addr) > 0)
+				|| (memcmp(it->dst_ipv6_addr, client->ip.ipv6_addr, 4*sizeof(uint32_t)) == 0 && client_info_v6_.count(src_v6_addr) > 0) )
+			{
+				IPACMDBG("Found a cache connection with src client 0x%08x%08x%08x%08x and dst client 0x%08x%08x%08x%08x.\n", it->src_ipv6_addr[0],
+							it->src_ipv6_addr[1], it->src_ipv6_addr[2], it->src_ipv6_addr[3], it->dst_ipv6_addr[0], it->dst_ipv6_addr[1],
+							it->dst_ipv6_addr[2], it->dst_ipv6_addr[3]);
+				ipacm_cmd_q_data evt;
+				ipacm_event_connection* conn;
+
+				conn = (ipacm_event_connection*)malloc(sizeof(ipacm_event_connection));
+				if(conn == NULL)
+				{
+					IPACMERR("Failed to allocate memory for new_connection event.\n");
+					return;
+				}
+				memcpy(conn, &(*it), sizeof(ipacm_event_connection));
+
+				memset(&evt, 0, sizeof(evt));
+				evt.event = IPA_LAN_TO_LAN_NEW_CONNECTION;
+				evt.evt_data = (void*)conn;
+				IPACM_EvtDispatcher::PostEvt(&evt);
+
+				it = connection_v6_.erase(it);
+				IPACMDBG("Now the number of cache connections is %d.\n", connection_v6_.size());
+			}
+			else
+			{
+				it++;
+			}
+		}
+	}
+#endif
+	return;
+}