/*
// Copyright (c) 2017 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>

#include <app.h>
#include <rte_common.h>
#include <rte_malloc.h>
#include <rte_mbuf.h>
#include <rte_ip.h>
#include <rte_byteorder.h>
#include <rte_table_lpm.h>
#include <rte_table_hash.h>
#include <rte_pipeline.h>
#include <rte_arp.h>
#include <rte_icmp.h>
#include <rte_hash.h>
#include <rte_jhash.h>
#include <rte_cycles.h>
#include "pipeline_cgnapt_common.h"
#include "pipeline_actions_common.h"
#include "pipeline_cgnapt_be.h"
#include "hash_func.h"
#include "lib_ftp_alg.h"
#include "vnf_common.h"
#include "pipeline_common_be.h"
#include "rte_ct_tcp.h"
#include "rte_cnxn_tracking.h"
#define ALG_DEBUG 1

#if 1
extern uint8_t
rte_ct_create_cnxn_hashkey(
	uint32_t *src_addr,
	uint32_t *dst_addr,
	uint16_t src_port,
	uint16_t dst_port,
	uint8_t proto,
	uint32_t *key,
	uint8_t type);
#endif

struct rte_mbuf *lib_alg_pkt;
enum {PRIVATE, PUBLIC};
struct rte_hash_parameters ftp_alg_hash_params = {
	.name = "FTP ALG",
	.entries = 1024,
	.reserved = 0,
	.key_len = sizeof(struct ftp_alg_key),
	.hash_func = rte_jhash,
	.hash_func_init_val = 0,
};

struct rte_hash *ftp_alg_hash_handle;

/**
 * ftp_alg Init function
 */
void lib_ftp_alg_init(void)
{
printf("NAT FTP ALG initialization ...\n");

	/* FTP ALG hash table initialization */

	ftp_alg_hash_handle = rte_hash_create(&ftp_alg_hash_params);

	#ifdef ALGDBG
	if (ftp_alg_hash_handle == NULL)
		printf("FTP ALG rte_hash_create failed ...\n");
	else
		printf("ftp_alg_hash_table %p\n\n",
			(void *)ftp_alg_hash_handle);

	#endif
}

/*
 * ftp_alg table retreive function
 * Input - alg key
 * Output - Entry
 */

struct ftp_alg_table_entry *retrieve_ftp_alg_entry(struct ftp_alg_key alg_key)
{
	struct ftp_alg_table_entry *ret_alg_data = NULL;
	alg_key.filler1 = 0;
	alg_key.filler2 = 0;

	int ret = rte_hash_lookup_data(ftp_alg_hash_handle, &alg_key,
							 (void **)&ret_alg_data);
	if (ret < 0) {
		#ifdef ALGDBG
		printf("alg-hash lookup failed ret %d, EINVAL %d, ENOENT %d\n",
					 ret, EINVAL, ENOENT);
		#endif
	} else {
		return ret_alg_data;
	}

	return NULL;
}

/*
 * ftp_alg table entry delete
 * Input - ipaddress, portid
 * Output - sucess or failure
 */
static int remove_ftp_alg_entry(uint32_t ipaddr, uint8_t portid)
{

	/* need to lock here if multi-threaded... */
	/* rte_hash_del_key is not thread safe */
	struct ftp_alg_key alg_key;
	alg_key.l4port = rte_bswap16(portid);
	alg_key.ip_address = rte_bswap32(ipaddr);
	alg_key.filler1 = 0;
	alg_key.filler2 = 0;

	#ifdef ALGDBG
		printf("remove_alg_entry ip %x, port %d\n", alg_key.ip_address,
					 alg_key.l4port);
	#endif
	return rte_hash_del_key(ftp_alg_hash_handle, &alg_key);
}
/*
 * ftp_alg table entry add
 * Input - ipaddress, portid
 * Output - sucess or failure
 */
void
populate_ftp_alg_entry(uint32_t ipaddr, uint8_t portid)
{
	/* need to lock here if multi-threaded */
	/* rte_hash_add_key_data is not thread safe */
	struct ftp_alg_key alg_key;
	alg_key.l4port = rte_bswap16(portid);
	//arp_key.ip = rte_bswap32(ipaddr);
	alg_key.ip_address = rte_bswap32(ipaddr);
	alg_key.filler1 = 0;
	alg_key.filler2 = 0;


	//lib_arp_populate_called++;

	#ifdef ALGDBG
	printf("populate_ftp_alg_entry ip %x, port %d\n", alg_key.ip_address,
					 alg_key.l4port);
	#endif

	struct ftp_alg_table_entry *new_alg_data =
		retrieve_ftp_alg_entry(alg_key);
	if (new_alg_data) {
		#ifdef ALGDBG
		printf("alg_entry exists ip%x, port %d\n", alg_key.ip_address,
				alg_key.l4port);
		#endif
		//lib_arp_duplicate_found++;
		return;
	}
	new_alg_data = (struct ftp_alg_table_entry *)
			malloc(sizeof(new_alg_data));
	//new_alg_data->status = INCOMPLETE;
	new_alg_data->l4port = rte_bswap16(portid);
	new_alg_data->ip_address = rte_bswap32(ipaddr);
	rte_hash_add_key_data(ftp_alg_hash_handle, &alg_key, new_alg_data);

	#ifdef ALGDBG
		// print entire hash table
		printf
				("\tALG: table update - ip=%d.%d.%d.%d  on port=%d\n",
				 (alg_key.ip_address >> 24),
				 ((alg_key.ip_address & 0x00ff0000) >> 16),
				 ((alg_key.ip_address & 0x0000ff00) >> 8),
				 ((alg_key.ip_address & 0x000000ff)), portid);
		/* print_arp_table(); */
		puts("");
	#endif
}

/*
 * ftp_alg payload modification for PORT and PASV command
 * Input - cgnapt table entry - for taking the public /translated ip/port ,
 * incoming PORT/PASV string, Session type - PORT or PASV
 * Output - Translated string
 */
int ftp_alg_modify_payload(
	struct cgnapt_table_entry *egress_entry,
	char *port_string,
	char *port_string_translated, int ftp_session_type)
{
	uint32_t transport_ip;
	uint16_t transport_port;
	uint16_t tmp1, tmp2, tmp3, tmp4, tmp5, tmp6;
	uint16_t new_port_string_length;

	uint8_t *bptr_public_address;

	transport_ip = egress_entry->data.pub_ip;
	transport_port = egress_entry->data.pub_port;
	tmp5 = (uint16_t) (transport_port/0x100);
	tmp6 = (uint16_t) (transport_port % 0x100);

	transport_ip = rte_bswap32(transport_ip);

	bptr_public_address = (uint8_t *) &transport_ip;

	tmp4 = bptr_public_address[3];
	tmp3 = bptr_public_address[2];
	tmp2 = bptr_public_address[1];
	tmp1 = bptr_public_address[0];

	if (ftp_session_type == 1)
		sprintf(port_string_translated, FTP_PASV_PARAMETER_STRING,
			FTP_PASV_RETURN_CODE, tmp1, tmp2, tmp3, tmp4,
			tmp5, tmp6);
	else
		sprintf(port_string_translated, FTP_PORT_PARAMETER_STRING,
			tmp1, tmp2, tmp3, tmp4, tmp5, tmp6);
	#ifdef ALGDBG
	printf("FTP ALG: FTP new string: Len:%d %s\n",
			(uint16_t) strlen(port_string_translated)-2,
			port_string_translated);

	printf("FTP non translated PASV string: Len:%d, %s\n",
		(uint16_t)strlen(port_string)-2, port_string);
		printf("old strlen:%d  new strlen:%d\n",
		(int)strlen(port_string),
		(int)strlen(port_string_translated));
	#endif

	return(new_port_string_length =
		(uint16_t) strlen(port_string_translated));
}

/*
 * ftp_alg modify packet len (due to change in len of FTP payload )
 * Input - pkt
 * Output - Length append /Trimmed Pkt
**/
static inline void ftp_alg_modify_pkt_len(struct rte_mbuf *pkt)
{
	uint16_t pkt_length = 0;
	int ip_hdr_size_bytes = rte_ct_get_IP_hdr_size(pkt);
	void *iphdr = RTE_MBUF_METADATA_UINT32_PTR(pkt, IP_START);

	if (ip_hdr_size_bytes == IPv4_HEADER_SIZE) {
		struct ipv4_hdr *ihdr4 = (struct ipv4_hdr *)iphdr;
		pkt_length = rte_bswap16(ihdr4->total_length) + ETH_HDR_SIZE;
	} else if (ip_hdr_size_bytes == IPv6_HEADER_SIZE) {
		struct ipv6_hdr *ihdr6 = (struct ipv6_hdr *)iphdr;
		pkt_length = rte_bswap16(ihdr6->payload_len) +
			IPv6_HEADER_SIZE + ETH_HDR_SIZE;
	}

	uint16_t mbuf_pkt_length = rte_pktmbuf_pkt_len(pkt);

	if (pkt_length == mbuf_pkt_length)
		return;

	if (pkt_length < mbuf_pkt_length) {
		rte_pktmbuf_trim(pkt, mbuf_pkt_length - pkt_length);
		return;
	}

	/* pkt_length > mbuf_pkt_length */
	rte_pktmbuf_append(pkt, pkt_length - mbuf_pkt_length);
}

/*
 * ftp_alg IP HDR size calculation
 * Input - pkt
 * Output - Length of IP HDR
 */

/* same as rte_ct_get_IP_hdr_size()*/
uint16_t ftp_alg_get_IP_hdr_size(struct rte_mbuf *pkt)
{
	/* NOTE: Only supporting IP headers with no options at this time
	* so header is fixed size
	*/

	uint8_t hdr_chk = RTE_MBUF_METADATA_UINT8(pkt, IP_START);
	hdr_chk = hdr_chk >> 4;

	if (hdr_chk == IP_VERSION_4)
		return IPv4_HEADER_SIZE;
	else if (hdr_chk == IP_VERSION_6)
		return IPv6_HEADER_SIZE;
	else            /* Not IPv4 header with no options, return negative. */
		return -1;

}

/*
 * ftp_alg checksum re-computing due to change in payload , uses rte function,
 * if HW Checksum is supported s/w checksum will be disabled
 * Input - IP HDR and TCP HDR
 * Output - Length of IP HDR
 */
static void ftp_alg_compute_checksums(
	void *i_hdr,
	struct tcp_hdr *t_hdr)
/* same as rte_synproxy_compute_checksums*/
{
	/*
	* calculate IP and TCP checksums.
	* Note that both checksum routines require
	* checksum fields to be set to zero, and the the checksum is in the
	* correct byte order, so no rte_bswap16 is required.
	*/

	int8_t hdr_chk = rte_ct_ipversion(i_hdr);
	t_hdr->cksum = 0;

	if (hdr_chk == IP_VERSION_4) {
		struct ipv4_hdr *i4_hdr = (struct ipv4_hdr *)i_hdr;
		i4_hdr->hdr_checksum = 0;
		t_hdr->cksum = 0;
		t_hdr->cksum = rte_ipv4_udptcp_cksum(i4_hdr, t_hdr);

		#ifdef ALGDBG
		printf("cksum %x\n", rte_bswap32(t_hdr->cksum));
		#endif

		i4_hdr->hdr_checksum = rte_ipv4_cksum(i4_hdr);
	} else if (hdr_chk == IP_VERSION_6) {
		struct ipv6_hdr *i6_hdr = (struct ipv6_hdr *)i_hdr;
		t_hdr->cksum = 0;
		t_hdr->cksum = rte_ipv6_udptcp_cksum(i6_hdr, t_hdr);
	}
}

/*
 * ftp_alg adjusting ACK from other end ;
 * ACK field of return packet to be adjusted
 * to the same value of length modified in the payload
 * Input - pkt, ack diff - delta
 * Output - None(void)
 */
static void  ftp_alg_adjust_tcp_ack(struct rte_mbuf *pkt, int16_t ackSeqdiff)
{
	/*Since v6 is not supported now*/
	uint16_t ip_hdr_size_bytes = IPv4_HEADER_SIZE;
	struct ipv4_hdr *iphdr = (struct ipv4_hdr *)
		RTE_MBUF_METADATA_UINT32_PTR(pkt, IP_START);
	struct tcp_hdr *thdr = (struct tcp_hdr *)
		RTE_MBUF_METADATA_UINT32_PTR(pkt,
			IP_START + ip_hdr_size_bytes);
	/*
	* recv_ack and total length first to be chnaged to host byte order
	* and then do the addition and then set back to network byte order
	*/
	uint32_t temp;
	temp = rte_bswap32(thdr->recv_ack);
	//printf("%s: ackSeqdiff :%d %u\n", __FUNCTION__, ackSeqdiff, temp);
	if (ackSeqdiff < 0)
		temp += abs(ackSeqdiff);
	else
		temp -= abs(ackSeqdiff);

	thdr->recv_ack = rte_bswap32(temp);
}
/*
 * ftp_alg adjusting SEQ from other end ; SEQ field of onward/egress  packet
 * to be adjusted to the same value of length modified in the payload
 * Input - pkt, ack diff - delta
 * Output - None(void)
 */

static void  ftp_alg_adjust_tcp_seq(struct rte_mbuf *pkt, int16_t ackSeqdiff)
{
	/*Since v6 is not supported now*/
	uint16_t ip_hdr_size_bytes = IPv4_HEADER_SIZE;
	struct ipv4_hdr *iphdr = (struct ipv4_hdr *)
		RTE_MBUF_METADATA_UINT32_PTR(pkt, IP_START);
	struct tcp_hdr *thdr = (struct tcp_hdr *)
		RTE_MBUF_METADATA_UINT32_PTR(pkt,
			IP_START + ip_hdr_size_bytes);
	uint32_t temp;

	temp = rte_bswap32(thdr->sent_seq);
	if (ackSeqdiff < 0)
		temp -= abs(ackSeqdiff);
	else
		temp += abs(ackSeqdiff);

	thdr->sent_seq = rte_bswap32(temp);
}
/*
 * ftp_alg adjusting SEQ from other end ; SEQ field of onward/egress  packet
 * to be adjusted to the same value of length modified in the payload;
 * This function computes the delta and calls adjust_seq for chaging the packet
 * Input - pkt,Original incoming String, Translated string and corresponding
 * lengths of the string
 * Output - Seq Diff between Original and translated string
 */

static int ftp_alg_delta_tcp_sequence(
	struct rte_mbuf *pkt,
	char *port_string,
	int16_t existing_tcpSeqdiff,
	uint16_t old_port_string_length,
	uint16_t new_port_string_length)
{
	int16_t current_sequence_number_delta=0;
	int16_t final_sequence_number_delta;
	/*Since v6 is not supported now*/
	uint16_t ip_hdr_size_bytes = IPv4_HEADER_SIZE;
	struct ipv4_hdr *iphdr = (struct ipv4_hdr *)
				RTE_MBUF_METADATA_UINT32_PTR(pkt, IP_START);
	struct tcp_hdr *thdr = (struct tcp_hdr *)
				RTE_MBUF_METADATA_UINT32_PTR(pkt,
					IP_START + ip_hdr_size_bytes);
	/*
	* recv_ack and total length first to be chnaged to host byte order
	* and then do the addition and then set back to network byte order
	*/
	current_sequence_number_delta = (int16_t) (new_port_string_length -
					old_port_string_length);
	iphdr->total_length = rte_bswap16(iphdr->total_length);

	#ifdef ALGDBG
	printf("total_length :%u\n", iphdr->total_length);
	#endif
	if(current_sequence_number_delta < 0)
		iphdr->total_length -= abs(current_sequence_number_delta);
	else
		iphdr->total_length += current_sequence_number_delta;

	iphdr->total_length = rte_bswap16(iphdr->total_length);
	if (existing_tcpSeqdiff !=0)
		ftp_alg_adjust_tcp_seq(pkt,existing_tcpSeqdiff);
	final_sequence_number_delta= current_sequence_number_delta + existing_tcpSeqdiff;
	return final_sequence_number_delta;
}


/*
 * ftp_alg dpi - this function parses the packet and does the respective
 * action based on the type PORT or PASV, based on the direction of packet
 * (Private or Public) This is called from CGNAPT
 * Input - cgnapt pipeline struct, cgnapt key, pkt, CT ,
 * position of packet assigned by CT, direction of packet
 * Output - None - as it calls respective action functions
 */
void ftp_alg_dpi(
	struct pipeline_cgnapt *p_nat,
	struct pipeline_cgnapt_entry_key *nat_entry_key,
	struct rte_mbuf *pkt,
	struct rte_ct_cnxn_tracker *cgnat_cnxn_tracker,
	int32_t ct_position,
	uint8_t direction)
{
	/*
	* recv_ack and total length first to be chnaged to host byte order
	* and then do the addition and then set back to network byte order
	*/

	/*entry key to be framed in cgnat and pass it over here*/
	char *port_cmd_string;
	char *port_cmd_end_string;
	char *tcp_header_end;
	char *tcp_start;


	uint16_t private_port_number;
	uint16_t public_port_number;
	uint16_t ip1, ip2, ip3, ip4, port1, port2;
	int16_t tcpSeqdiff;
	int16_t ackSeqdiff, ackAdjust;
	uint32_t private_address;
	uint32_t public_address;
	uint8_t *bptr_private_address;
	/* also for PASV string */
	char port_string[FTP_MAXIMUM_PORT_STRING_LENGTH];
	char port_string_translated[FTP_MAXIMUM_PORT_STRING_LENGTH];
	int16_t new_port_string_length;
	int16_t old_port_string_length;
	int dummy_value;
	struct cgnapt_table_entry *egress_entry, *ingress_entry;
	uint32_t ct_key[10];
	uint8_t key_direction;
	/*Since v6 is not supported now*/
	uint16_t ip_hdr_size_bytes = IPv4_HEADER_SIZE;

	struct ipv4_hdr *ip_hdr = rte_pktmbuf_mtod_offset(pkt,
			struct ipv4_hdr *, sizeof(struct ether_hdr));
	/* TCP and UDP ports at same offset,
	* just use TCP for offset calculation
	*/
	struct tcp_hdr *thdr = rte_pktmbuf_mtod_offset(pkt, struct tcp_hdr *,
			(sizeof(struct ether_hdr)+sizeof(struct ipv4_hdr)));
	uint16_t src_port = rte_bswap16(thdr->src_port);
	uint16_t dst_port = rte_bswap16(thdr->dst_port);
	uint8_t proto = ip_hdr->next_proto_id;
	uint32_t src_addr = rte_bswap32(ip_hdr->src_addr);
	uint32_t dst_addr = rte_bswap32(ip_hdr->dst_addr);
	uint32_t tmp_tcp_paylod_size;

	#if 0
	- src_port & dst_port checking to be moved from cgnat to dpi
	- For control channel
		first validation of tcpSeqdiff to be checked
		IF <  > 0
			ftp_alg_tcp_ack() to be called(this includes PORT
			response and PASV response ack as well)
			Return
		ELSE
			the port/pasv paramter checkign to be done
	- For data channel
	-retreive ALG entry
		IF found
			- remove the ALG entry
		even if not found(found cases too)
	- set the bypass flag in the CT session table

	#endif

	#ifdef ALGDBG
	{
		printf("ftp port number: %d, %d\n", src_port, dst_port);
		printf("ftp TCP seq num diff: %d\n",
			cgnat_cnxn_tracker->hash_table_entries[
					ct_position].tcpSeqdiff);
		printf("tcp data offset: %d\n",
			((thdr->data_off & 0xf0) >> 2));
		printf("ct position in dpi:%d\n", ct_position);
	}
	#endif

	if (src_port == 21 || dst_port == 21)/* Control Channel*/{
	/* Control Channel Start */
	/*
	* need to look for the port or pasv command.  Then have to look for
	* the IP address and the port address. Then must create a TCP control
	* block and spoof the port number, and change the ip address, and do
	* the sequence number setting.
	*/
	/* Handle TCP headers.*/
	tcp_start = (char *)thdr;

	/* start of TCP payload */
	port_cmd_string = (char * )(tcp_start+((thdr->data_off & 0xf0) >> 2));
	tcp_header_end = port_cmd_string;

	if (direction == PRIVATE) {

		#ifdef ALGDBG
		printf("In PRIVATE  ");
		#endif

		cgnat_cnxn_tracker->hash_table_entries[ct_position].seq_client
				= rte_bswap32(thdr->sent_seq);
		cgnat_cnxn_tracker->hash_table_entries[ct_position].ack_client
				= rte_bswap32(thdr->recv_ack);
		#ifdef ALGDBG
		printf("-->Seq_cli:%u, Ack_cli:%u, Len:%4d\n",
			rte_bswap32(thdr->sent_seq),
			rte_bswap32(thdr->recv_ack),
			(rte_bswap16(ip_hdr->total_length) -
			(((thdr->data_off & 0xf0) >> 4) * 4)) - 20);
		#endif
	} else {

		#ifdef ALGDBG
		printf("In PUBLIC  ");
		#endif
		cgnat_cnxn_tracker->hash_table_entries[ct_position].seq_server
				= rte_bswap32(thdr->sent_seq);
		cgnat_cnxn_tracker->hash_table_entries[ct_position].ack_server
				= rte_bswap32(thdr->recv_ack);
		#ifdef ALGDBG
		printf("<--Seq_cli:%4d, Ack_cli%4d, Len:%4d\n",
		rte_bswap32(thdr->sent_seq), rte_bswap32(thdr->recv_ack),
		(ip_hdr->total_length - (((thdr->data_off & 0xf0) >> 2))
			- 20));
		#endif
	}

	if (sscanf(port_cmd_string, FTP_PASV_PARAMETER_STRING, &dummy_value,
		&ip1, &ip2, &ip3, &ip4, &port1, &port2) ==
		FTP_PASV_PARAMETER_COUNT){

	sprintf (port_string, FTP_PASV_PARAMETER_STRING, FTP_PASV_RETURN_CODE,
		ip1, ip2, ip3, ip4, port1, port2);

	int i = 0;
	while (port_cmd_string[i] != '\r' && port_cmd_string[i+1] != '\n')
		i++;

	i += 2; // now it points to end of port cmd string.

	old_port_string_length = i;

	private_port_number = (uint16_t) (port1 * 0x100 + port2);
	bptr_private_address = (uint8_t *) &private_address;

	bptr_private_address[3] = (uint8_t) (ip4 & 0x00FF);
	bptr_private_address[2] = (uint8_t) (ip3 & 0x00FF);
	bptr_private_address[1] = (uint8_t) (ip2 & 0x00FF);
	bptr_private_address[0] = (uint8_t) (ip1 & 0x00FF);

	/* Not needed as got the position from CT*/

	if (direction == PUBLIC) {
	/*Client in Private, Server in Public*/
	/* Not to convert in the payload for PASV resp from Pub side*/
	/* Only Table creation and no payload modification*/
	/* DAta Channel also no need to create as it will be done by NAT
	* when initiated by Client later
	*/
	populate_ftp_alg_entry(private_address, private_port_number);
	/*
	* Bypass ALG flag to be set ,
	* seqnumber -delta either to be 0 or not needed ,
	* direction checking for all scenarios
	*/
	cgnat_cnxn_tracker->hash_table_entries[ct_position].
			server_direction = SERVER_IN_PUBLIC;
	cgnat_cnxn_tracker->hash_table_entries[ct_position].
			ftp_session_type= 1; // Passive session type
	} else if (direction == PRIVATE) {
	/*Client in Public , Server in Private*/

	struct pipeline_cgnapt_entry_key data_channel_key;
	private_address = rte_bswap32(private_address);
	data_channel_key.ip = private_address;
	data_channel_key.port = private_port_number;
	/* to be checked if it can be passed as param from NAT*/
	data_channel_key.pid = pkt->port;

	/* add_dynamic_cgnat_entry() */ /* for DAta Channel*/
	/*Will be getting Private IP and port from Server ,
	* with that NAPT entry egress and ingress can be added ,
	* for further data channel communication
	*/
        #ifdef FTP_ALG
	if (add_dynamic_cgnapt_entry_alg((struct pipeline *)p_nat,
	&data_channel_key, &egress_entry, &ingress_entry) == 0){

		#ifdef ALGDBG
		printf("Wrong FTP ALG packet\n");
		#endif
		//p_nat->invalid_packets |= pkt_mask;

		p_nat->naptDroppedPktCount++;

		#ifdef CGNAPT_DEBUGGING
		p_nat->naptDroppedPktCount4++;
		#endif
		return;
	}
	#endif

	tmp_tcp_paylod_size = rte_bswap16(ip_hdr->total_length) -
			((thdr->data_off & 0xf0) >> 2) - ip_hdr_size_bytes;
	cgnat_cnxn_tracker->hash_table_entries[ct_position].
			tcp_payload_size = tmp_tcp_paylod_size;

	/*Adding ALG entry , params to be derived from egress entry*/
	populate_ftp_alg_entry(egress_entry->data.pub_ip,
			egress_entry->data.pub_port);
	/* payload modification */
	new_port_string_length = ftp_alg_modify_payload(egress_entry,
					port_string,
					port_string_translated, 1);
	strncpy(tcp_header_end, port_string_translated,
		strlen(port_string_translated));
	tcpSeqdiff = ftp_alg_delta_tcp_sequence( pkt, port_string,
			cgnat_cnxn_tracker->hash_table_entries
			[ct_position].tcpSeqdiff,
			old_port_string_length,
			new_port_string_length);

	/* same as rte_synproxy_adjust_pkt_length() in ct */
	ftp_alg_modify_pkt_len(pkt);
	/*
	* Store sequence_number_delta in Session_data structure, also bypass
	* flag to be set as NO (expecting TCP ack from other end then set the
	* bypass accordingly , handled earlier in the function
	*/

	cgnat_cnxn_tracker->hash_table_entries[ct_position].
			alg_bypass_flag = NO_BYPASS;
	cgnat_cnxn_tracker->hash_table_entries[ct_position].
			tcpSeqdiff = tcpSeqdiff;
	cgnat_cnxn_tracker->hash_table_entries[ct_position].
			server_direction = SERVER_IN_PRIVATE;
	cgnat_cnxn_tracker->hash_table_entries[ct_position].
			ftp_session_type = 1; // Passive session type
		return;

	} /* PRIVATE dir */
	} else if (sscanf(port_cmd_string, FTP_PORT_PARAMETER_STRING,
			&ip1, &ip2, &ip3, &ip4, &port1, &port2) ==
				FTP_PORT_PARAMETER_COUNT){

		int i = 0;
		static uint8_t port_hit;
		while (port_cmd_string[i] != '\r' &&
			port_cmd_string[i+1] != '\n')
			i++;

		i += 2; // now it points to end of port cmd string.

		old_port_string_length = i;

		#ifdef ALGDBG
		printf( " Existing Seq Diff = %d", cgnat_cnxn_tracker->
			hash_table_entries[ct_position].tcpSeqdiff);
		printf("FTP ALG: FTP PORT command length: %d\n",
			old_port_string_length);
		#endif

		private_port_number = (uint16_t) (port1 * 0x100 + port2);

		#ifdef ALGDBG
		printf("FTP ALG: private port number before swap: %u\n",
				private_port_number);
		#endif

		bptr_private_address = (uint8_t *) &private_address;
		bptr_private_address[3] = (uint8_t) (ip4 & 0x00FF);
		bptr_private_address[2] = (uint8_t) (ip3 & 0x00FF);
		bptr_private_address[1] = (uint8_t) (ip2 & 0x00FF);
		bptr_private_address[0] = (uint8_t) (ip1 & 0x00FF);

		sprintf(port_string, FTP_PORT_PARAMETER_STRING, ip1, ip2,
				ip3, ip4, port1, port2);

		#ifdef ALGDBG
		printf("FTP ALG: FTP original PORT string: %d,%s\n",
				(int) strlen(port_string)-2, port_string);
		printf("prv addr: %x\n", private_address);
		#endif


		if (direction == PUBLIC) {
			/* Client in Public*/
			/* retreive_cgnat_entry()* for Data Channel*/
			/* Pub port and ip address to be used for framing key ,
			* the private phrase is a misnomer
			*/
			struct pipeline_cgnapt_entry_key data_channel_key;
			data_channel_key.ip = private_address;
			data_channel_key.port = private_port_number;
			data_channel_key.pid = 0xffff;


			cgnat_cnxn_tracker->hash_table_entries[ct_position].
				server_direction = SERVER_IN_PRIVATE;
			cgnat_cnxn_tracker->hash_table_entries[ct_position].
				ftp_session_type= 0; // Active session type

			/* No payload modificaiton*/
			#ifdef ALGDBG
			printf("<--Seq_cli:%4d, Ack_cli%4d, Len:%4d\n",
			rte_bswap32(thdr->sent_seq),
			rte_bswap32(thdr->recv_ack),
			(ip_hdr->total_length -
				(((thdr->data_off & 0xf0) >> 2)) - 20));
			#endif
			populate_ftp_alg_entry(private_address, private_port_number);
		} else if (direction == PRIVATE) {

			/* Client in Private Server in Public*/
			/* Populate_alg_entry*/
			/*add_dynamic_cgnapt_entry()*/
			/* payload modificaion*/
			struct pipeline_cgnapt_entry_key data_channel_key;
			private_address = rte_bswap32(private_address);
			data_channel_key.ip = private_address;
			data_channel_key.port = private_port_number;
			/* to be checked if it can be passed as param from NAT*/
			data_channel_key.pid = pkt->port;

			/* add_dynamic_cgnat_entry() */ /* for DAta Channel*/
			/*
			* Will be getting Private IP and port from Client ,
			* with that NAPT entry egress and ingress can be added ,
			* for further data channel communication
			*/

                        #ifdef FTP_ALG
			if (add_dynamic_cgnapt_entry_alg((struct pipeline *)
				p_nat, &data_channel_key, &egress_entry,
				&ingress_entry) == 0){

				#ifdef ALGDBG
				printf("Wrong FTP ALG packet\n");
				#endif
				//p_nat->invalid_packets |= pkt_mask;
				p_nat->naptDroppedPktCount++;

				#ifdef CGNAPT_DEBUGGING
				p_nat->naptDroppedPktCount4++;
				#endif
				return;
			}
                        #endif

		tmp_tcp_paylod_size = rte_bswap16(ip_hdr->total_length) -
					((thdr->data_off & 0xf0) >> 2) -
					ip_hdr_size_bytes;
		cgnat_cnxn_tracker->hash_table_entries[ct_position].
			tcp_payload_size = tmp_tcp_paylod_size;
		/*ALG entry add, params to be derived from egress entry*/

		populate_ftp_alg_entry(egress_entry->data.pub_ip,
			egress_entry->data.pub_port);
		/* payload modification */
		new_port_string_length = ftp_alg_modify_payload(egress_entry,
						port_string,
						port_string_translated, 0);
		strncpy(tcp_header_end, port_string_translated,
				strlen(port_string_translated));
		tcpSeqdiff = ftp_alg_delta_tcp_sequence( pkt, port_string,
				cgnat_cnxn_tracker->hash_table_entries
				[ct_position].tcpSeqdiff,
				old_port_string_length,
				new_port_string_length);
		/* same as rte_synproxy_adjust_pkt_length() in ct */
		ftp_alg_modify_pkt_len(pkt);

		/*
		* Store sequence_number_delta in Session_data structure ,
		* also bypass flag to be set as NO
		* While response from other end is received ,
		* modify the ack no using reverse sign of sequen
		*/

		cgnat_cnxn_tracker->hash_table_entries[ct_position].
			alg_bypass_flag = NO_BYPASS;
		cgnat_cnxn_tracker->hash_table_entries[ct_position].
			tcpSeqdiff = tcpSeqdiff;
		cgnat_cnxn_tracker->hash_table_entries[ct_position].
			server_direction = SERVER_IN_PUBLIC;
		cgnat_cnxn_tracker->hash_table_entries[ct_position].
			ftp_session_type = 0; // Active session type

		#ifdef ALGDBG
		printf("<--Seq_cli:%4d, Ack_cli%4d, Len:%4d\n",
			rte_bswap32(thdr->sent_seq),
			rte_bswap32(thdr->recv_ack),
			(ip_hdr->total_length -
			(((thdr->data_off & 0xf0) >> 2)) - 20));

		#endif
		return;
		} /* PRIVATE dir */
	} /* PORT cmd message */

	if ((ackAdjust=cgnat_cnxn_tracker->hash_table_entries[
			ct_position].tcpSeqdiff) != 0) {
		if (direction == PRIVATE) {
			if (
				cgnat_cnxn_tracker->hash_table_entries
				[ct_position].seq_client !=
				cgnat_cnxn_tracker->hash_table_entries
				[ct_position].ack_server) {
				static int Seqhits;
				ftp_alg_adjust_tcp_seq( pkt,ackAdjust);
				tmp_tcp_paylod_size = rte_bswap16(
					ip_hdr->total_length) -
					((thdr->data_off & 0xf0) >> 2) -
					ip_hdr_size_bytes;
				cgnat_cnxn_tracker->hash_table_entries
				[ct_position].tcp_payload_size = tmp_tcp_paylod_size;
		#ifdef ALGDBG
                printf("<--Seq_cli:%4d, Ack_cli%4d, Len:%4d\n",
			rte_bswap32(thdr->sent_seq),
			rte_bswap32(thdr->recv_ack),
			(ip_hdr->total_length -(((thdr->data_off & 0xf0) >> 2))- 20));
		#endif
			}
		} else {
			if (cgnat_cnxn_tracker->hash_table_entries
				[ct_position].ack_server !=
				(cgnat_cnxn_tracker->hash_table_entries
				[ct_position].seq_client +
				cgnat_cnxn_tracker->hash_table_entries
				[ct_position].tcp_payload_size)) {
				static int Ackhits;
				ftp_alg_adjust_tcp_ack( pkt,ackAdjust);
		#ifdef ALGDBG
                printf("<--Seq_cli:%4d, Ack_cli%4d, Len:%4d\n",
			rte_bswap32(thdr->sent_seq),
			rte_bswap32(thdr->recv_ack),
			(ip_hdr->total_length -(((thdr->data_off & 0xf0) >> 2))- 20));
		#endif
			}
		}
		return;
			} /* expected_ack and sequence number updation for PUBLIC dir TCP window */
	} /* Control Channel End */
	else {
		/*remove the ALG entry, retreival is taken care by rte function  */
		#ifdef ALGDBG
		printf("In Data Channel \n");
		#endif
		remove_ftp_alg_entry (dst_addr,dst_port);/* remove the ALG entry */
		cgnat_cnxn_tracker->hash_table_entries[ct_position].alg_bypass_flag = BYPASS;
	} /* Data Channel End */
}