ipacm : Add change to update NAT entries for embedded/tethered connections

Add change to read and process conntrack entries only when WAN is
up and add NAT entries accordingly when switch to tethered connections.

Change-Id: Iaa31a44684082802c03402cdd1faa393a7bc85f8
diff --git a/ipacm/inc/IPACM_ConntrackListener.h b/ipacm/inc/IPACM_ConntrackListener.h
index 24a2c72..6cc4188 100644
--- a/ipacm/inc/IPACM_ConntrackListener.h
+++ b/ipacm/inc/IPACM_ConntrackListener.h
@@ -68,6 +68,7 @@
 	bool isCTReg;
 	bool isNatThreadStart;
 	bool WanUp;
+	bool isProcessCTDone;
 	NatApp *nat_inst;
 
 	int NatIfaceCnt;
@@ -77,6 +78,7 @@
 	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;
 #ifdef CT_OPT
 	IPACM_LanToLan *p_lan2lan;
 #endif
@@ -104,6 +106,7 @@
 	char wan_ifname[IPA_IFACE_NAME_LEN];
 	uint32_t wan_ipaddr;
 	ipacm_wan_iface_type backhaul_mode;
+	bool isReadCTDone;
 	IPACM_ConntrackListener();
 	void event_callback(ipa_cm_event_id, void *data);
 	inline bool isWanUp()
@@ -116,6 +119,8 @@
 	void HandleSTAClientAddEvt(uint32_t);
 	void HandleSTAClientDelEvt(uint32_t);
 	int  CreateConnTrackThreads(void);
+	void readConntrack(void);
+	void processConntrack(void);
 };
 
 extern IPACM_ConntrackListener *CtList;
diff --git a/ipacm/inc/IPACM_Defs.h b/ipacm/inc/IPACM_Defs.h
index a3cbba1..e60516e 100644
--- a/ipacm/inc/IPACM_Defs.h
+++ b/ipacm/inc/IPACM_Defs.h
@@ -105,6 +105,11 @@
 #define NUM_IPV6_PREFIX_FLT_RULE 1
 #define NUM_IPV6_PREFIX_MTU_RULE 1
 
+#define MAX_CONNTRACK_ENTRIES 100
+#define CT_ENTRIES_BUFFER_SIZE 8096
+#define LOOPBACK_MASK 0xFF000000
+#define LOOPBACK_ADDR 0x7F000000
+
 /*---------------------------------------------------------------------------
 										Return values indicating error status
 ---------------------------------------------------------------------------*/
diff --git a/ipacm/src/IPACM_ConntrackClient.cpp b/ipacm/src/IPACM_ConntrackClient.cpp
index 8a2499c..24f95a3 100644
--- a/ipacm/src/IPACM_ConntrackClient.cpp
+++ b/ipacm/src/IPACM_ConntrackClient.cpp
@@ -421,6 +421,17 @@
 	unsigned subscrips = 0;
 
 	IPACMDBG("\n");
+	/* In Android we get conntrack handles once after tethering is enabled but we
+	   loose connections info for embedded traffic if running before. So no NAT
+	   entries are added for embedded traffic due to which we see NAT exception and
+	   data takes S/W path which results in less throughput. Hence for embedded
+	   traffic info, framework sends conntrack dump before providing handles. Here
+	   reading ct entries before creating filter on Fd in order to have NAT entries
+	   for both TCP/UDP embedded traffic. */
+	if(CtList != NULL && !CtList->isReadCTDone)
+	{
+		CtList->readConntrack();
+	}
 
 	pClient = IPACM_ConntrackClient::GetInstance();
 	if(pClient == NULL)
diff --git a/ipacm/src/IPACM_ConntrackListener.cpp b/ipacm/src/IPACM_ConntrackListener.cpp
index e006393..fff6bfc 100644
--- a/ipacm/src/IPACM_ConntrackListener.cpp
+++ b/ipacm/src/IPACM_ConntrackListener.cpp
@@ -35,6 +35,7 @@
 #include "IPACM_EvtDispatcher.h"
 #include "IPACM_Iface.h"
 #include "IPACM_Wan.h"
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
 
 IPACM_ConntrackListener::IPACM_ConntrackListener()
 {
@@ -43,11 +44,14 @@
 	 isNatThreadStart = false;
 	 isCTReg = false;
 	 WanUp = false;
+	 isReadCTDone = false;
+	 isProcessCTDone = false;
 	 nat_inst = NatApp::GetInstance();
 
 	 NatIfaceCnt = 0;
 	 StaClntCnt = 0;
 	 pNatIfaces = NULL;
+	 ct_entries = NULL;
 	 pConfig = IPACM_Config::GetInstance();;
 
 	 memset(nat_iface_ipv4_addr, 0, sizeof(nat_iface_ipv4_addr));
@@ -97,6 +101,10 @@
 			IPACMDBG_H("Received IPA_HANDLE_WAN_UP event\n");
 			CreateConnTrackThreads();
 			TriggerWANUp(data);
+			if(isReadCTDone && !isProcessCTDone)
+			{
+				processConntrack();
+			}
 			break;
 
 	 case IPA_HANDLE_WAN_DOWN:
@@ -1201,3 +1209,157 @@
 	 nat_inst->FlushTempEntries(clnt_ip_addr, false);
    return;
 }
+
+bool isLocalHostAddr(uint32_t src_ip_addr, uint32_t dst_ip_addr) {
+
+	src_ip_addr = ntohl(src_ip_addr);
+	dst_ip_addr = ntohl(dst_ip_addr);
+	if ((src_ip_addr & LOOPBACK_MASK) == LOOPBACK_ADDR || (dst_ip_addr & LOOPBACK_MASK) == LOOPBACK_ADDR) /* (loopback) */
+		return true;
+	return false;
+}
+
+void IPACM_ConntrackListener::readConntrack() {
+
+	IPACM_ConntrackClient *pClient;
+	int len_fil = 0, recv_bytes = -1, index = 0, len =0;
+	char buffer[CT_ENTRIES_BUFFER_SIZE];
+	char *buf = &buffer[0];
+	struct nf_conntrack *ct;
+	struct nlmsghdr *nl_header;
+	static struct sockaddr_nl nlAddr = {
+		.nl_family = AF_NETLINK
+	};
+	unsigned int addr_len = sizeof(nlAddr);
+
+	pClient = IPACM_ConntrackClient::GetInstance();
+	len = MAX_CONNTRACK_ENTRIES * sizeof(struct nf_conntrack **);
+
+	ct_entries = (struct nf_conntrack **) malloc(len);
+	memset(ct_entries, 0, len);
+
+	if( pClient->fd_tcp < 0)
+	{
+		IPACMDBG_H("Invalid fd %d \n",pClient->fd_tcp);
+		return;
+	}
+
+	IPACMDBG_H("receiving conntrack entries started.\n");
+	while(len_fil < sizeof(buffer) && index <  MAX_CONNTRACK_ENTRIES)
+	{
+	 	recv_bytes = recvfrom( pClient->fd_tcp, buf, sizeof(buffer)-len_fil, 0, (struct sockaddr *)&nlAddr, (socklen_t *)&addr_len);
+		if(recv_bytes < 0)
+		{
+			IPACMDBG_H("error in receiving conntrack entries %d%s",errno, strerror(errno));
+			break;
+		}
+		else
+		{
+			nl_header = (struct nlmsghdr *)buf;
+
+			if (NLMSG_OK(nl_header, recv_bytes) == 0 || nl_header->nlmsg_type == NLMSG_ERROR)
+			{
+				IPACMDBG_H("recv_bytes is %d\n",recv_bytes);
+				break;
+			}
+			ct = nfct_new();
+			if (ct != NULL)
+			{
+				int parseResult =  nfct_parse_conntrack((nf_conntrack_msg_type) NFCT_T_ALL,nl_header, ct);
+				if(parseResult != NFCT_T_ERROR)
+				{
+					ct_entries[index++] = ct;
+				}
+				else
+				{
+					IPACMDBG_H("error in parsing  %d%s \n", errno, strerror(errno));
+				}
+			}
+			else
+			{
+				IPACMDBG_H("ct allocation failed");
+				continue;
+			}
+
+			if((nl_header->nlmsg_type & NLM_F_MULTI) == 0)
+			{
+				break;
+			}
+			len_fil += recv_bytes;
+			buf = buf + recv_bytes;
+		}
+	}
+	isReadCTDone = true;
+	IPACMDBG_H("receiving conntrack entries ended.\n");
+	if(isWanUp() && !isProcessCTDone)
+	{
+		IPACMDBG_H("wan is up, process ct entries \n");
+		processConntrack();
+	}
+
+	return ;
+}
+
+void IPACM_ConntrackListener::processConntrack() {
+
+	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)
+		{
+			ip_type = nfct_get_attr_u8(ct_entries[index], ATTR_REPL_L3PROTO);
+			if(!(AF_INET6 == 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)))
+			{
+				IPACMDBG_H(" loopback entry \n");
+				goto IGNORE;
+			}
+
+#ifndef CT_OPT
+			if(AF_INET6 == ip_type)
+			{
+				IPACMDBG("Ignoring ipv6(%d) connections\n", ip_type);
+				goto IGNORE;
+			}
+#endif
+
+			ct_data = (ipacm_ct_evt_data *)malloc(sizeof(ipacm_ct_evt_data));
+			if(ct_data == NULL)
+			{
+				IPACMERR("unable to allocate memory \n");
+				goto IGNORE;
+			}
+
+			ct_data->ct = ct_entries[index];
+			ct_data->type = type;
+
+#ifdef CT_OPT
+			if(AF_INET6 == ip_type)
+			{
+				ProcessCTV6Message(ct_data);
+			}
+#else
+				ProcessCTMessage(ct_data);
+#endif
+		index++;
+		free(ct_data);
+		continue;
+IGNORE:
+		nfct_destroy(ct_entries[index]);
+		index++;
+		}
+	}
+	else
+	{
+		IPACMDBG_H("ct entry is null\n");
+		return ;
+	}
+	isProcessCTDone = true;
+	free(ct_entries);
+	IPACMDBG_H("process conntrack ended \n");
+	return;
+}