Promotion of data.lnx.3.0-00082.

CRs      Change ID                                   Subject
--------------------------------------------------------------------------------------------------------------
2057519   Icc47d0199a0a30a2b0173ef2d4be905d000ecdaf   ipacm: Set flag to handle IPA_HANDLE_WAN_UP_TETHER event
2068359   I25ff6a52261fad60ecea4be044b0e15ba55821f5   ipanat: Add support for Multi PDN

Change-Id: I4dc10980f9dd136bda3ea96f2dc5d1f8874e9116
CRs-Fixed: 2068359, 2057519
diff --git a/configure.ac b/configure.ac
index 33164c0..6662ef6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -24,6 +24,7 @@
       AS_HELP_STRING([--with-sanitized-headers=DIR],
          [Specify the location of the sanitized Linux headers]),
       [CPPFLAGS="$CPPFLAGS -idirafter $withval"])
+AM_CONDITIONAL(KERNELMODULES, [test -n -eq 0])
 
 AC_ARG_WITH([glib],
       AC_HELP_STRING([--with-glib],
diff --git a/ipacm/inc/IPACM_Lan.h b/ipacm/inc/IPACM_Lan.h
index 85ea927..2e5f21b 100644
--- a/ipacm/inc/IPACM_Lan.h
+++ b/ipacm/inc/IPACM_Lan.h
@@ -220,6 +220,9 @@
 	bool modem_ul_v4_set;
 	bool modem_ul_v6_set;
 
+	bool sta_ul_v4_set;
+	bool sta_ul_v6_set;
+
 	uint32_t if_ipv4_subnet;
 
 	uint32_t ipv6_prefix[2];
diff --git a/ipacm/src/IPACM_Conntrack_NATApp.cpp b/ipacm/src/IPACM_Conntrack_NATApp.cpp
index 2c06642..077cab5 100644
--- a/ipacm/src/IPACM_Conntrack_NATApp.cpp
+++ b/ipacm/src/IPACM_Conntrack_NATApp.cpp
@@ -344,6 +344,7 @@
 		}
 		else
 		{
+			memset(&nat_rule, 0, sizeof(nat_rule));
 			nat_rule.private_ip = rule->private_ip;
 			nat_rule.target_ip = rule->target_ip;
 			nat_rule.target_port = rule->target_port;
diff --git a/ipacm/src/IPACM_Lan.cpp b/ipacm/src/IPACM_Lan.cpp
index 8d853d2..a561eef 100644
--- a/ipacm/src/IPACM_Lan.cpp
+++ b/ipacm/src/IPACM_Lan.cpp
@@ -79,6 +79,10 @@
 	is_active = true;
 	modem_ul_v4_set = false;
 	modem_ul_v6_set = false;
+
+	sta_ul_v4_set = false;
+	sta_ul_v6_set = false;
+
 	is_mode_switch = false;
 	if_ipv4_subnet =0;
 	each_client_rt_rule_count[IPA_IP_v4] = 0;
@@ -1081,6 +1085,7 @@
 			return IPACM_FAILURE;
 		}
 		IPACM_Iface::ipacmcfg->decreaseFltRuleCount(rx_prop->rx[0].src_pipe, IPA_IP_v4, 1);
+		sta_ul_v4_set = false;
 	}
 
 	close(fd);
@@ -1375,6 +1380,11 @@
 
 	if(ip_type == IPA_IP_v4)
 	{
+		if(sta_ul_v4_set == true)
+		{
+			IPACMDBG_H("Filetring rule for IPV4 of STA mode is already configured, sta_ul_v4_set: %d\n",sta_ul_v4_set);
+			return IPACM_FAILURE;
+		}
 		len = sizeof(struct ipa_ioc_add_flt_rule) + (1 * sizeof(struct ipa_flt_rule_add));
 		m_pFilteringTable = (struct ipa_ioc_add_flt_rule *)calloc(1, len);
 		if (m_pFilteringTable == NULL)
@@ -1447,13 +1457,18 @@
 							 m_pFilteringTable->rules[0].status);
 		}
 
-
+		sta_ul_v4_set = true;
 		/* copy filter hdls  */
 		lan_wan_fl_rule_hdl[0] = m_pFilteringTable->rules[0].flt_rule_hdl;
 		free(m_pFilteringTable);
 	}
 	else if(ip_type == IPA_IP_v6)
 	{
+		if(sta_ul_v6_set == true)
+		{
+			IPACMDBG_H("Filetring rule for IPV6 of STA mode is already configured, sta_ul_v6_set: %d\n",sta_ul_v6_set);
+			return IPACM_FAILURE;
+		}
 		/* add default v6 filter rule */
 		m_pFilteringTable = (struct ipa_ioc_add_flt_rule *)
 			 calloc(1, sizeof(struct ipa_ioc_add_flt_rule) +
@@ -1529,6 +1544,7 @@
 			IPACMDBG_H("flt rule hdl0=0x%x, status=0x%x\n", m_pFilteringTable->rules[0].flt_rule_hdl, m_pFilteringTable->rules[0].status);
 		}
 
+		sta_ul_v6_set = true;
 		/* copy filter hdls */
 		dft_v6fl_rule_hdl[IPV6_DEFAULT_FILTERTING_RULES] = m_pFilteringTable->rules[0].flt_rule_hdl;
 		free(m_pFilteringTable);
@@ -3248,6 +3264,7 @@
 			return IPACM_FAILURE;
 		}
 		IPACM_Iface::ipacmcfg->decreaseFltRuleCount(rx_prop->rx[0].src_pipe, IPA_IP_v6, 1);
+		sta_ul_v6_set = false;
 	}
 	close(fd);
 	return IPACM_SUCCESS;
diff --git a/ipanat/inc/ipa_nat_drv.h b/ipanat/inc/ipa_nat_drv.h
index 04e3af9..4ef8779 100644
--- a/ipanat/inc/ipa_nat_drv.h
+++ b/ipanat/inc/ipa_nat_drv.h
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2013, The Linux Foundation. All rights reserved.
+Copyright (c) 2013 - 2017, The Linux Foundation. All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
@@ -38,6 +38,7 @@
  * @target_port: destination port
  * @private_port: private port
  * @protocol: protocol of rule (tcp/udp)
+ * @pdn_index: PDN index in the PDN config table
  */
 typedef struct {
 	uint32_t target_ip;
@@ -46,9 +47,22 @@
 	uint16_t private_port;
 	uint16_t public_port;
 	uint8_t  protocol;
+	uint8_t  pdn_index;
 } ipa_nat_ipv4_rule;
 
 /**
+* struct ipa_nat_pdn_entry - holds a PDN entry data
+* @public_ip: PDN's public ip address
+* @src_metadata: metadata to be used for source NAT metadata replacement
+* @dst_metadata: metadata to be used for destination NAT metadata replacement
+*/
+typedef struct {
+	uint32_t public_ip;
+	uint32_t src_metadata;
+	uint32_t dst_metadata;
+} ipa_nat_pdn_entry;
+
+/**
  * ipa_nat_add_ipv4_tbl() - create ipv4 nat table
  * @public_ip_addr: [in] public ipv4 address
  * @number_of_entries: [in]  number of nat entries
@@ -114,3 +128,17 @@
 				uint32_t  rule_handle,
 				uint32_t  *time_stamp);
 
+
+/**
+* ipa_nat_modify_pdn() - modify single PDN entry in the PDN config table
+* @table_handle: [in] handle of ipv4 nat table
+* @pdn_index : [in] the index of the entry to be modified
+* @pdn_info : [in] values for the PDN entry to be changed
+*
+* Modify a PDN entry
+*
+* Returns:	0  On Success, negative on failure
+*/
+int ipa_nat_modify_pdn(uint32_t  tbl_hdl,
+	uint8_t pdn_index,
+	ipa_nat_pdn_entry *pdn_info);
diff --git a/ipanat/inc/ipa_nat_drvi.h b/ipanat/inc/ipa_nat_drvi.h
index 6f9b1bd..1896a3e 100644
--- a/ipanat/inc/ipa_nat_drvi.h
+++ b/ipanat/inc/ipa_nat_drvi.h
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2013, The Linux Foundation. All rights reserved.
+Copyright (c) 2013 - 2017, The Linux Foundation. All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
@@ -215,8 +215,8 @@
   | Proto   |      TimeStamp(3B)        |       Flags(2B)     | IP check sum Diff(2B)|
   | (1B)    |                           |EN|FIN|Resv |        |                      |
   -----------------------------------------------------------------------------------
-  | TCP/UDP checksum |  Reserved(2B)    |    SW Specific Parameters(4B)              |
-  |    diff (2B)                        |                                            |
+  | TCP/UDP checksum |PDN info| Reserved|    SW Specific Parameters(4B)              |
+  |    diff (2B)     |  (1B)  |   (1B)  |                                            |
   -----------------------------------------------------------------------------------
 
   Dont change below structure definition.
@@ -240,11 +240,23 @@
    ------------------------------------
    |  index table    |  prev index    |
    |     entry       |                |
-	 ------------------------------------
+   ------------------------------------
   --------------------------------------------------*/
 	uint64_t sw_spec_params:32;
 
-	uint64_t rsvd2:16;
+	uint64_t rsvd2:8;
+
+  /*-----------------------------------------
+   8 bit PDN info is interpreted as following
+   ------------------------------------
+   |     4 bits      |     4 bits     |
+   ------------------------------------
+   |  PDN index      |    reserved    |
+   |                 |                |
+   ------------------------------------
+  -------------------------------------------*/
+	uint64_t rsvd3:4;
+	uint64_t pdn_index:4;
 	uint64_t tcp_udp_chksum:16;
 };
 
@@ -275,7 +287,18 @@
   --------------------------------------------------*/
 	uint64_t prev_index:16;
 	uint64_t indx_tbl_entry:16;
-	uint64_t rsvd2:16;
+	uint64_t rsvd2 :8;
+  /*-----------------------------------------
+   8 bit PDN info is interpreted as following
+   ------------------------------------
+   |     4 bits      |     4 bits     |
+   ------------------------------------
+   |  PDN index      |    reserved    |
+   |                 |                |
+   ------------------------------------
+  -------------------------------------------*/
+	uint64_t rsvd3 :4;
+	uint64_t pdn_index :4;
 	uint64_t tcp_udp_chksum:16;
 };
 #define IPA_NAT_TABLE_ENTRY_SIZE        32
@@ -326,6 +349,7 @@
 	struct ipa_nat_ip4_table_cache ip4_tbl[IPA_NAT_MAX_IP4_TBLS];
 	int ipa_fd;
 	uint8_t table_cnt;
+	enum ipa_hw_type ver;
 };
 
 struct ipa_nat_indx_tbl_sw_rule {
@@ -402,6 +426,8 @@
 				uint32_t  rule_hdl,
 				uint32_t  *time_stamp);
 
+int ipa_nati_modify_pdn(struct ipa_ioc_nat_pdn_entry *entry);
+
 int ipa_nati_add_ipv4_rule(uint32_t tbl_hdl,
 				const ipa_nat_ipv4_rule *clnt_rule,
 				uint32_t *rule_hdl);
diff --git a/ipanat/src/Makefile.am b/ipanat/src/Makefile.am
index 8bdb9b8..8e2d005 100644
--- a/ipanat/src/Makefile.am
+++ b/ipanat/src/Makefile.am
@@ -1,21 +1,38 @@
 AM_CFLAGS = -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs
+if KERNELMODULES
+AM_CFLAGS += -I./../inc $(KERNEL_DIR)/include
+else
 AM_CFLAGS += -I./../inc
+endif
 #AM_CFLAGS += -DDEBUG -g
 
 common_CFLAGS =  -DUSE_GLIB @GLIB_CFLAGS@
+if !KERNELMODULES
 common_LDFLAGS = -lrt @GLIB_LIBS@
+endif
+
+if KERNELMODULES
+library_includedir = ../inc $(KERNEL_DIR)/include $(pkgincludedir)
+else
+library_includedir = $(pkgincludedir)
+endif
 
 c_sources   = ipa_nat_drv.c \
               ipa_nat_drvi.c \
               ipa_nat_logi.c
 
-library_includedir = $(pkgincludedir)
 library_include_HEADERS = ./../inc/ipa_nat_drvi.h \
                           ./../inc/ipa_nat_drv.h \
                           ./../inc/ipa_nat_logi.h
 
+if KERNELMODULES
+noinst_LIBRARIES = libipanat.a
+libipanat_a_C = @C@
+libipanat_a_SOURCES = $(c_sources)
+else
 lib_LTLIBRARIES = libipanat.la
 libipanat_la_C = @C@
 libipanat_la_SOURCES = $(c_sources)
 libipanat_la_CFLAGS = $(AM_CFLAGS) $(common_CFLAGS)
 libipanat_la_LDFLAGS = -shared $(common_LDFLAGS) -version-info 1:0:0
+endif
diff --git a/ipanat/src/ipa_nat_drv.c b/ipanat/src/ipa_nat_drv.c
index 66504e1..d01a6c9 100644
--- a/ipanat/src/ipa_nat_drv.c
+++ b/ipanat/src/ipa_nat_drv.c
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2013, The Linux Foundation. All rights reserved.
+Copyright (c) 2013 - 2017, The Linux Foundation. All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
@@ -173,3 +173,43 @@
 }
 
 
+/**
+* ipa_nat_modify_pdn() - modify single PDN entry in the PDN config table
+* @table_handle: [in] handle of ipv4 nat table
+* @pdn_index : [in] the index of the entry to be modified
+* @pdn_info : [in] values for the PDN entry to be changed
+*
+* Modify a PDN entry
+*
+* Returns:	0  On Success, negative on failure
+*/
+int ipa_nat_modify_pdn(uint32_t  tbl_hdl,
+	uint8_t pdn_index,
+	ipa_nat_pdn_entry *pdn_info)
+{
+	struct ipa_ioc_nat_pdn_entry pdn_data;
+
+	if (0 == tbl_hdl || tbl_hdl > IPA_NAT_MAX_IP4_TBLS) {
+		IPAERR("invalid parameters passed \n");
+		return -EINVAL;
+	}
+
+	if (!pdn_info) {
+		IPAERR("pdn_info is NULL \n");
+		return -EINVAL;
+	}
+
+	if (pdn_index > IPA_MAX_PDN_NUM) {
+		IPAERR("PDN index is out of range %d", pdn_index);
+		return -EINVAL;
+	}
+
+	pdn_data.pdn_index = pdn_index;
+	pdn_data.public_ip = pdn_info->public_ip;
+	pdn_data.src_metadata = pdn_info->src_metadata;
+	pdn_data.dst_metadata = pdn_info->dst_metadata;
+
+	return ipa_nati_modify_pdn(&pdn_data);
+}
+
+
diff --git a/ipanat/src/ipa_nat_drvi.c b/ipanat/src/ipa_nat_drvi.c
index 9b96c44..d309bb3 100644
--- a/ipanat/src/ipa_nat_drvi.c
+++ b/ipanat/src/ipa_nat_drvi.c
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2013, The Linux Foundation. All rights reserved.
+Copyright (c) 2013 - 2017, The Linux Foundation. All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
@@ -33,11 +33,21 @@
 #ifdef USE_GLIB
 #include <glib.h>
 #define strlcpy g_strlcpy
+#else
+static size_t strlcpy(char * dst, const char * src, size_t size) {
+	if (size < 1)
+		return 0;
+	strncpy(dst, src, size - 1);
+	dst[size - 1] = 0;
+	return strlen(dst);
+}
 #endif
 
 struct ipa_nat_cache ipv4_nat_cache;
 pthread_mutex_t nat_mutex    = PTHREAD_MUTEX_INITIALIZER;
 
+static ipa_nat_pdn_entry pdns[IPA_MAX_PDN_NUM];
+
 /* ------------------------------------------
 		UTILITY FUNCTIONS START
 	 --------------------------------------------*/
@@ -153,6 +163,26 @@
 	}
 }
 
+/**
+* GetIPAVer(void) - store IPA HW ver in cache
+*
+*
+* Returns: 0 on success, negative on failure
+*/
+int GetIPAVer(void)
+{
+	int ret;
+
+	ret = ioctl(ipv4_nat_cache.ipa_fd, IPA_IOC_GET_HW_VERSION, &ipv4_nat_cache.ver);
+	if (ret != 0) {
+		perror("GetIPAVer(): ioctl error value");
+		IPAERR("unable to get IPA version. Error ;%d\n", ret);
+		IPADBG("ipa fd %d\n", ipv4_nat_cache.ipa_fd);
+		return -EINVAL;
+	}
+	IPADBG("IPA version is %d\n", ipv4_nat_cache.ver);
+	return 0;
+}
 
 /**
  * CreateNatDevice() - Create nat devices
@@ -248,6 +278,7 @@
 
 /**
  * dst_hash() - Find the index into ipv4 base table
+ * @public_ip: [in] public_ip
  * @trgt_ip: [in] Target IP address
  * @trgt_port: [in]  Target port
  * @public_port: [in]  Public port
@@ -261,13 +292,18 @@
  *
  * Returns: >0 index into ipv4 base table, negative on failure
  */
-static uint16_t dst_hash(uint32_t trgt_ip, uint16_t trgt_port,
-				uint16_t public_port, uint8_t proto,
-				uint16_t size)
+static uint16_t dst_hash(uint32_t public_ip, uint32_t trgt_ip,
+			uint16_t trgt_port, uint16_t public_port,
+			uint8_t proto, uint16_t size)
 {
 	uint16_t hash = ((uint16_t)(trgt_ip)) ^ ((uint16_t)(trgt_ip >> 16)) ^
 		 (trgt_port) ^ (public_port) ^ (proto);
 
+	if (ipv4_nat_cache.ver >= IPA_HW_v4_0)
+		hash ^= ((uint16_t)(public_ip)) ^
+		((uint16_t)(public_ip >> 16));
+
+	IPADBG("public ip 0x%X\n", public_ip);
 	IPADBG("trgt_ip: 0x%x trgt_port: 0x%x\n", trgt_ip, trgt_port);
 	IPADBG("public_port: 0x%x\n", public_port);
 	IPADBG("proto: 0x%x size: 0x%x\n", proto, size);
@@ -682,6 +718,12 @@
 		return -EINVAL;
 	}
 
+	/* store the initial public ip address in the cached pdn table
+		this is backward compatible for pre IPAv4 versions, we will always
+		use this ip as the single PDN address
+	*/
+	pdns[0].public_ip = public_ip_addr;
+
 	/* Return table handle */
 	ipv4_nat_cache.table_cnt++;
 	*tbl_hdl = ipv4_nat_cache.table_cnt;
@@ -734,6 +776,11 @@
 		ipv4_nat_cache.ipa_fd = fd;
 	}
 
+	if (GetIPAVer()) {
+		IPAERR("unable to get ipa ver\n");
+		return -EIO;
+	}
+
 	ret = CreateNatDevice(mem);
 	return ret;
 }
@@ -1014,6 +1061,30 @@
 	return 0;
 }
 
+int ipa_nati_modify_pdn(struct ipa_ioc_nat_pdn_entry *entry)
+{
+	if (entry->public_ip == 0)
+		IPADBG("PDN %d public ip will be set  to 0\n", entry->pdn_index);
+
+	if (ioctl(ipv4_nat_cache.ipa_fd, IPA_IOC_NAT_MODIFY_PDN, entry)) {
+		perror("ipa_nati_modify_pdn(): ioctl error value");
+		IPAERR("unable to call modify pdn icotl\n");
+		IPAERR("index %d, ip 0x%X, src_metdata 0x%X, dst_metadata 0x%X\n",
+			entry->pdn_index, entry->public_ip, entry->src_metadata, entry->dst_metadata);
+		IPADBG("ipa fd %d\n", ipv4_nat_cache.ipa_fd);
+		return -EIO;
+	}
+
+	pdns[entry->pdn_index].public_ip = entry->public_ip;
+	pdns[entry->pdn_index].dst_metadata = entry->dst_metadata;
+	pdns[entry->pdn_index].src_metadata = entry->src_metadata;
+
+	IPADBG("posted IPA_IOC_NAT_MODIFY_PDN to kernel successfully and stored in cache\n index %d, ip 0x%X, src_metdata 0x%X, dst_metadata 0x%X\n",
+		entry->pdn_index, entry->public_ip, entry->src_metadata, entry->dst_metadata);
+
+	return 0;
+}
+
 int ipa_nati_add_ipv4_rule(uint32_t tbl_hdl,
 				const ipa_nat_ipv4_rule *clnt_rule,
 				uint32_t *rule_hdl)
@@ -1023,6 +1094,14 @@
 	struct ipa_nat_indx_tbl_sw_rule index_sw_rule;
 	uint16_t new_entry, new_index_tbl_entry;
 
+	/* verify that the rule's PDN is valid */
+	if (clnt_rule->pdn_index >= IPA_MAX_PDN_NUM ||
+		pdns[clnt_rule->pdn_index].public_ip == 0) {
+		IPAERR("invalid parameters, pdn index %d, public ip = 0x%X\n",
+			clnt_rule->pdn_index, pdns[clnt_rule->pdn_index].public_ip);
+		return -EINVAL;
+	}
+
 	memset(&sw_rule, 0, sizeof(sw_rule));
 	memset(&index_sw_rule, 0, sizeof(index_sw_rule));
 
@@ -1114,7 +1193,7 @@
 	uint16_t prev = 0, nxt_indx = 0, new_entry;
 	struct ipa_nat_rule *tbl = NULL, *expn_tbl = NULL;
 
-	pub_ip_addr = tbl_ptr->public_addr;
+	pub_ip_addr = pdns[clnt_rule->pdn_index].public_ip;
 
 	tbl = (struct ipa_nat_rule *)tbl_ptr->ipv4_rules_addr;
 	expn_tbl = (struct ipa_nat_rule *)tbl_ptr->ipv4_expn_rules_addr;
@@ -1126,6 +1205,7 @@
 	sw_rule->public_port = clnt_rule->public_port;
 	sw_rule->target_ip = clnt_rule->target_ip;
 	sw_rule->target_port = clnt_rule->target_port;
+	sw_rule->pdn_index = clnt_rule->pdn_index;
 
 	/* consider only public and private ip fields */
 	sw_rule->ip_chksum = ipa_nati_calc_ip_cksum(pub_ip_addr,
@@ -1152,10 +1232,11 @@
 	*/
 	sw_rule->time_stamp = 0;
 	sw_rule->rsvd2 = 0;
+	sw_rule->rsvd3 = 0;
 	sw_rule->prev_index = 0;
 	sw_rule->indx_tbl_entry = 0;
 
-	new_entry = dst_hash(clnt_rule->target_ip,
+	new_entry = dst_hash(pub_ip_addr, clnt_rule->target_ip,
 											 clnt_rule->target_port,
 											 clnt_rule->public_port,
 											 clnt_rule->protocol,