/* 2103, Wed 16 May 01 (PST)

   LFAPMET.C:  Meter support routines for LfapMet

   Copyright (C) 2001 by the Internet Next Generation project
                             http://ing.ctit.utwente.nl/WU5
   Author: Remco Poortinga, Telematics Institute

   NeTraMet Copyright (C) 1992-2002 by Nevil Brownlee,
   CAIDA | University of Auckland */


struct interface_info *pci[MXINTERFACES];
int n_interfaces;  /* Nbr of active interfaces */

char *opcodelist[]= {"<none>","VR","VRA","FAR","FUN","AR","ARA"};
char *cmdlist[]={	"<none>",
					"RETURN_INDICATED_FLOWS",
					"RETURN_FLOW_PREFIX",
					"CONNECTION_ACCEPTED",
					"CONNECTION_REJECTED",
					"LIST_OF_FASS",
					"DISCONNECT",
					"KEEPALIVE"
				  };
char *IElist[]={	"<none>",
					"FlowID IE",
					"Source Address IE",
					"Destination Address IE",
					"Flow Qualifier IEs",
					"Time IE",
					"Type Of Service IE",
					"Source CCE Address IE",
					"Client Data IE",
					"Command Code IE",
					"FAS IP Address IE",
					"Failure Code IE",
					"Flow Identifier Prefix IE",
					"Flow State IE",
					"Byte Count (running) IE",
					"Byte Count (delta) IE",
					"Packet Count (running) IE",
					"Packet Count (delta) IE",
					"Protocol Identifier IE",
					"Source Port (UDP) IE",
					"Source Port (TCP) IE",
					"Source Port (IPX) IE",
					"Multiple Record IE",
					"Src.AS and Dst.AS IE",
					"Ingress Port IE",
					"Egress Port IE"
				  };
char *protolist[] = { 	"HOPOPT",
						"ICMP",
						"IGMP",
						"GGP",
						"IP in IP",
						"ST",
						"TCP",
						"CBT",
						"EGP",
						"IGP",
						"BBN-RCC-MON",
						"NVP-II",
						"PUP",
						"ARGUS",
						"EMCON",
						"XNET",
						"CHAOS",
						"UDP"
					};

void dumphex(void* buf, int len)
{
	int i, j;
	unsigned char* tmp_ptr;
	
	tmp_ptr = (char*) buf;
	j = 0;
	for (i=0;i<len;i++)
	{
		printf(" %02x ",tmp_ptr[i]);
		j++;
		if (j==4)
		{
			printf("\n");
			j = 0;
		}
	}
}

void dumphexfile( FILE* out, void* buf, int len)
{
	int i, j;
	unsigned char* tmp_ptr;
	
	tmp_ptr = (char*) buf;
	j = 0;
	for (i=0;i<len;i++)
	{
		fprintf(out, " %02x ",tmp_ptr[i]);
		j++;
		if (j==4)
		{
			fprintf(out, "\n");
			j = 0;
		}
	}
	fprintf(out, "\n");
}

void dumpMessage1(FILE* out, LFAPHeader* lfap, char* header)
{
	Bit8		opcode;
	LFAP_IE_Header*	ie;
	int ielen, i, uptime;
	Bit8* ptr;
	char timestring[100];

	debug("->dumpMessage()\n",LFAPMET_DBG_FUNC);
	if (header != 0) fprintf(out,header);
	uptime = uptime_cs();
	formatDuration(timestring,uptime);
	fprintf(out,"\tFAS Uptime:\t%s\n",timestring);
	fprintf(out,"\tLFAP version:\t%i\n", getLFAPVersion(lfap));
	opcode = getLFAPOpcode(lfap);
	if (opcode > LFAP_OPCODE_MAX) opcode = 0;
	fprintf(out,"\tOpcode:\t\t%s\n", opcodelist[opcode]);
	fprintf(out,"\tMessage ID:\t%i\n", getLFAPMessageID(lfap));
	fprintf(out,"\tMessage length:\t%i\n", getLFAPMessageLengthIncl(lfap));
	ie = getFirstIE(lfap);
	if (ie!=NULL) fprintf(out,"\t=== IEs ===\n");
	while (ie != NULL)
	{
		fprintf(out,"\t- %s\n", IElist[getIEType(ie)]);
		if (getIEType(ie)==LFAP_IE_COMMAND_CODE)
		{
			fprintf(out,"\t  (%s)\n\n",cmdlist[getIEDword(ie, 0)]);
		}
		else if (getIEType(ie)==LFAP_IE_PROTOCOL_IDENTIFIER)
		{
			/* do a simple hexdump of message content */
			if ((getIEByte(ie,0)==0x10)&&
				(getIEByte(ie,7)==0x08)&&
				(getIEByte(ie,12)<=17))
			{
				Bit8 proto = getIEByte(ie,12);
				fprintf(out,"\t  %02X (%s)\n",proto, protolist[proto]);
			}
			else
			{
				ielen = getIELengthExcl(ie);
				/* now dump it! */
				ptr = ((Bit8*)ie) + LFAP_IE_HEADER_SIZE;
				fprintf(out,"\t  ");
				for (i=0;i<ielen;i++) fprintf(out,"%02X ",*ptr++);
				fprintf(out,"\n");
			}
		}
		else
		{
			/* do a simple hexdump of message content */
			ielen = getIELengthExcl(ie);
			/* now dump it! */
			ptr = ((Bit8*)ie) + LFAP_IE_HEADER_SIZE;
			fprintf(out,"\t  ");
			for (i=0;i<ielen;i++) fprintf(out,"%02X ",*ptr++);
			fprintf(out,"\n");
		}
		ie = getNextIE(lfap, ie, getLFAPMessageLengthIncl(lfap));
	}
	fprintf(out,"\n");
	debug("<-dumpMessage()\n",LFAPMET_DBG_FUNC);
}

void dumpMessage(LFAPHeader* lfap, char* header)
{
	FILE* log;

	if ((lfap_info_level & LFAP_INFLVL_MSG)!=0)
	{
		dumpMessage1(stdout,lfap, header);
	}
	if ((lfap_info_level & LFAP_INFLVL_MSG_FILE)!=0)
	{
		log = fopen("LfapMet.logmessages","a");
		if (log != NULL)
		{
			dumpMessage1(log, lfap, header);
			fclose(log);
		}
	}
}

void formatDuration(char* dst,Bit32 uptime)
{
	Bit32 utsecs, utmins, uthrs, utdays;
	
	utsecs = uptime/100;
	utmins = utsecs/60;
	uthrs  = utmins/60;
	utdays = uthrs/24;
	
	utsecs = utsecs % 60;
	utmins = utmins % 60;
	uthrs  = uthrs  % 24;

	sprintf(dst,
			"%i days, %i hrs, %i mins %i secs (%i.%i secs)",
			utdays,
			uthrs,
			utmins,
			utsecs,
			uptime/100,
			uptime % 100);
}

void dumpFlowStats1(LFAPFlowStats* fs, FILE* out)
{
	Bit32 uptime;
	char timestring[256];

	uptime = uptime_cs();
	formatDuration(timestring,uptime);
	fprintf(out,"FAS Uptime:\t%s\n",timestring);

	fprintf(out,"flow id:\t%i\n", fs->flowID);
	formatDuration(timestring,fs->firstTime);
	fprintf(out,"first time:\t%s\n",timestring);
	formatDuration(timestring,fs->lastTime);
	fprintf(out,"last time:\t%s\n",timestring);

	fprintf(out,"Flow state:");
	if (fs->flowState == LFAP_FLOW_STATE_ACTIVE) fprintf(out,"\tActive\n");
	else fprintf(out,"\tInactive\n");
	fprintf(out,"Source IP:\t%i.%i.%i.%i\n",
			fs->src_addr[0],fs->src_addr[1],
			fs->src_addr[2],fs->src_addr[3]);
	fprintf(out,"Dest.  IP:\t%i.%i.%i.%i\n",
			fs->dest_addr[0],fs->dest_addr[1],
			fs->dest_addr[2],fs->dest_addr[3]);
	if (fs->protocol <= 17)
	{
		fprintf(out,"IP protocol:\t%i (%s)\n",fs->protocol,protolist[fs->protocol]);
	}
	else
	{
		fprintf(out,"Protocol:\t%i (Unknown)\n",fs->protocol);
	}
	if ((fs->protocol == 17)||(fs->protocol == 6)) /* TCP/UDP */
	{
		fprintf(out,"src port:\t%i\n",fs->src_port);
		fprintf(out,"dst port:\t%i\n",fs->dst_port);
	}
		
	if ((fs->Packets_recvd.hi|fs->Packets_recvd.lo)!=0)
	{
		fprintf(out,"Packets recvd:\t%08X, %08X\n",
				fs->Packets_recvd.hi,
				fs->Packets_recvd.lo);
	}
	if ((fs->Packets_sent.hi|fs->Packets_sent.lo)!=0)
	{
		fprintf(out,"Packets sent:\t%08X, %08X\n",
				fs->Packets_sent.hi,
				fs->Packets_sent.lo);
	}
	if ((fs->Octets_recvd.hi|fs->Octets_recvd.lo)!=0)
	{
		fprintf(out,"Octets recvd:\t%08X, %08X\n",
				fs->Octets_recvd.hi,
				fs->Octets_recvd.lo);
	}
	if ((fs->Octets_sent.hi|fs->Octets_sent.lo)!=0)
	{
		fprintf(out,"Octets sent:\t%08X, %08X\n",
				fs->Octets_sent.hi,
				fs->Octets_sent.lo);
	}
}

void dumpFlowStats(LFAPFlowStats* fs)
{
	FILE* log;

	if ((lfap_info_level & LFAP_INFLVL_FLOW)!=0)
	{
		dumpFlowStats1(fs, stdout);
	}
	if ((lfap_info_level & LFAP_INFLVL_FLOW_FILE)!=0)
	{
		log = fopen("LfapMet.logflowstats","a");
		if (log != NULL)
		{
			dumpFlowStats1(fs,log);
			fclose(log);
		}
	}
}

/* determine if the received buffer contains a complete LFAP message */
/* return -1  if so 												 */
/* return  0 if not     									 		 */
/* return >1 (LFAP message length) if there is more than one message */
/* return -2 if this message should be discarded (in case of error)  */
int isCompleteMessage(void* buf, int len)
{
	LFAPHeader* lfap_hdr;
	Bit16 msgID;
	Bit16 msgLength;
	Bit8 opcode;
	
	debug("->isCompleteMessage()\n",LFAPMET_DBG_FUNC);
	/* first check the header */
	lfap_hdr = (LFAPHeader*) buf;
	if (len < LFAP_HEADER_SIZE)	
	{
		return 0;
	}
	/* convert the header from network to host order */
	msgID = ntohs(lfap_hdr->messageID);
	msgLength = ntohs(lfap_hdr->messageLength);

	if(lfap_hdr->version != LFAP_VERSION)
	{
		printf("isCompleteMessage():!!!!!!!!!PANIC!!!!!!!! not the correct version!\n");
		debug("<-isCompleteMessage(-2)\n",LFAPMET_DBG_FUNC);
		return -2;
	}
	opcode = lfap_hdr->opcode;

	/* apparently as least as big as LFAP header, check the header! */
	if ((msgLength + LFAP_HEADER_SIZE) == len)
	{
		debug("<-isCompleteMessage(-1)\n",LFAPMET_DBG_FUNC);
		return -1;
	}
	else if ((msgLength + LFAP_HEADER_SIZE) > len)
	{
		debug("<-isCompleteMessage(0)\n",LFAPMET_DBG_FUNC);
		return 0;
	}
	/*	More than one message! */
	debug("<-isCompleteMessage(>0)\n",LFAPMET_DBG_FUNC);
	return (msgLength + LFAP_HEADER_SIZE);
}

/* copies a complete (raw!) LFAP message to a new buffer */
LFAPHeader* copyRawLFAPMessage(LFAPHeader* lfapsrc_p)
{
	LFAPHeader* lfapdst_p;
	int size;
	
	debug("->copyRawLFAPMessage()\n",LFAPMET_DBG_FUNC);
	/* first determine size of complete message */
	size = LFAP_HEADER_SIZE + ntohs(lfapsrc_p->messageLength);
	lfapdst_p = malloc(size);
	if (lfapdst_p != NULL) memcpy(lfapdst_p, lfapsrc_p, size);
	debug("<-copyRawLFAPMessage()\n",LFAPMET_DBG_FUNC);
	return lfapdst_p;
}

/* gets the type of IE */
Bit16 getIEType(LFAP_IE_Header* ie)
{
	debug("->getIEType()\n",LFAPMET_DBG_FUNC);
	debug("<-getIEType()\n",LFAPMET_DBG_FUNC);
	return ie->type;
}

/* gets the length of an IE (including header) */
Bit16 getIELengthIncl(LFAP_IE_Header* ie)
{
	int size;
	Bit8* ptr;

	debug("->getIELengthIncl()\n",LFAPMET_DBG_FUNC);
	if (ie->type != LFAP_IE_FLOW_ID)
	{
		size = (ie->length + LFAP_IE_HEADER_SIZE);
	}
	else
	{ /* special case... */
		ptr = (Bit8*)(&(ie->length));
		size = *ptr;
		size += *(ptr+1);
		size += LFAP_IE_HEADER_SIZE;
	}
	debug("<-getIELengthIncl()\n",LFAPMET_DBG_FUNC);
	return size;
}


/* gets the length of an IE (excluding header) */
Bit16 getIELengthExcl(LFAP_IE_Header* ie)
{
	int size;
	Bit8* ptr;

	debug("->getIELengthExcl()\n",LFAPMET_DBG_FUNC);
	if (ie->type != LFAP_IE_FLOW_ID)
		size = ie->length;
	else
	{ /* special case... */
		ptr = (Bit8*)(&(ie->length));
		size = *ptr;
		size += *(ptr+1);
	}
	debug("<-getIELengthExcl()\n",LFAPMET_DBG_FUNC);
	return size;
}


/* returns the Length of a message EXCLUDING the header itself */
Bit16 getLFAPMessageLengthExcl(LFAPHeader* lfap)
{
	debug("->getLFAPMessageLengthExcl()\n",LFAPMET_DBG_FUNC);
	debug("<-getLFAPMessageLengthExcl()\n",LFAPMET_DBG_FUNC);
	return (lfap->messageLength);
}


/* returns the Length of a message INCLUDING the header itself */
Bit16 getLFAPMessageLengthIncl(LFAPHeader* lfap)
{
	debug("->getLFAPMessageLengthExcl()\n",LFAPMET_DBG_FUNC);
	debug("<-getLFAPMessageLengthExcl()\n",LFAPMET_DBG_FUNC);
	return (lfap->messageLength + LFAP_HEADER_SIZE);
}


/* copies a complete LFAP message to a new buffer			*/
/* lfap header itself should have been normalised already	*/
/* (i.e. headers fields converted from network to host order*/
LFAPHeader* copyLFAPMessage(LFAPHeader* lfapsrc_p)
{
	LFAPHeader* lfapdst_p;
	int size;
	
	debug("->copyLFAPMessage()\n",LFAPMET_DBG_FUNC);
	/* first determine size of complete message */
	size = getLFAPMessageLengthIncl(lfapsrc_p);
	lfapdst_p = malloc(size);
	if (lfapdst_p != NULL) memcpy(lfapdst_p, lfapsrc_p, size);
	debug("<-copyLFAPMessage()\n",LFAPMET_DBG_FUNC);
	return lfapdst_p;
}


/* gets a pointer to the first IE in a message */
/* return NULL if there are no IEs */
LFAP_IE_Header* getFirstIE(LFAPHeader* lfap)
{
	int length;
	
	debug("->getFirstIE()\n",LFAPMET_DBG_FUNC);
	length = lfap->messageLength;
	if (length == 0)
	{
		debug("<-getFirstIE() (NULL)\n",LFAPMET_DBG_FUNC);
		return NULL;
	}
	debug("<-getFirstIE() (OK)\n",LFAPMET_DBG_FUNC);
	return (LFAP_IE_Header*)(((void*)lfap)+LFAP_HEADER_SIZE);
}


/* gets a pointer to the next IE after 'ie' 				*/
/* returns NULL if it is the last one 						*/
/* this is determined by comparing start_ptr + max_offset	*/
/* with the 'ie_next' pointer 								*/
LFAP_IE_Header* getNextIE(void* start_ptr, LFAP_IE_Header* ie, int max_offset)
{
	LFAP_IE_Header* ie_next;
	int ie_size;
	
	debug("->getNextIE()\n",LFAPMET_DBG_FUNC);
	ie_size = getIELengthIncl(ie);
	/* determine next IE from this one */
	ie_next = (LFAP_IE_Header*) (((void*)ie)+ie_size);
	if (((void*)ie_next) >= ((start_ptr)+max_offset))
	{
		debug("<-getNextIE() (NULL)\n",LFAPMET_DBG_FUNC);
		return NULL;
	}
	debug("<-getNextIE() (OK)\n",LFAPMET_DBG_FUNC);
	return ie_next;
}


void getIEDoubleDword(LFAP_IE_Header* ie, int offset, val64* result)
{
	void* ptr;
	
	debug("->getIEDoubleDword()\n",LFAPMET_DBG_FUNC);

	ptr = (void*)ie;
	ptr = ptr + LFAP_IE_HEADER_SIZE + offset;
	result->hi = ntohl(*((Bit32*)ptr));
	ptr = ptr + sizeof(Bit32);
	result->lo = ntohl(*((Bit32*)ptr));
	debug("<-getIEDoubleDword()\n",LFAPMET_DBG_FUNC);
}


Bit32 getIEDword(LFAP_IE_Header* ie, int offset)
{
	Bit32 res, res1;
	void* ptr;
	Bit8* bptr;

	debug("->getIEDword()\n",LFAPMET_DBG_FUNC);
	ptr = (void*)ie;
	ptr = ptr + LFAP_IE_HEADER_SIZE + offset;
	/* assume ptr is even */
	/* and byte read has to be done if offset is odd */
	/* or not a multiple of a double word (32bits == 4 bytes)*/
	if (((offset & 1)!=0)||((offset % 4)!=0))
	{ /* odd or not modulo DWORD */
		/* IEs still in network order */
		/* [0] = MSB, [3] = LSB*/
		bptr = (Bit8*)ptr;
		#if (WORDS_BIGENDIAN == 1)
		res = bptr[0]<<24 | bptr[1]<<16 | bptr[2]<<8 | bptr[3];
		#else
		res = bptr[3]<<24 | bptr[2]<<16 | bptr[1]<<8 | bptr[0];
		#endif
	}
	else
	{ /* even */
		res = *((Bit32*)ptr);
	}
	res = ntohl(res);
	debug("<-getIEDword()\n",LFAPMET_DBG_FUNC);
	return res;
}


Bit16 getIEWord(LFAP_IE_Header* ie, int offset)
{
	Bit16 res;
	void* ptr;
	Bit8* bptr;
	
	debug("->getIEWord()\n",LFAPMET_DBG_FUNC);
	ptr = (void*)ie;
	ptr = ptr + LFAP_IE_HEADER_SIZE + offset;

	/* assume ptr is even */
	/* and byte read has to be done if offset is odd */
	if ((offset & 1)!=0)
	{ /* odd or not modulo DWORD */
		/* IEs still in network order */
		/* [0] = MSB, [1] = LSB*/
		bptr = (Bit8*)ptr;
		bptr = (Bit8*)ptr;
		#if (WORDS_BIGENDIAN == 1)
		res = bptr[0]<<8 | bptr[1];
		#else
		res = bptr[1]<<8 | bptr[0];
		#endif
	}
	else
	{ /* even */
		res = *((Bit16*)ptr);
	}

	res = ntohs(res);
	return res;
	debug("<-getIEWord()\n",LFAPMET_DBG_FUNC);
}

Bit8 getIEByte(LFAP_IE_Header* ie, int offset)
{
	Bit8 res;
	void* ptr;
	
	debug("->getIEByte()\n",LFAPMET_DBG_FUNC);
	ptr = (void*)ie;
	ptr = ptr + LFAP_IE_HEADER_SIZE + offset;

	res = *((Bit8*)ptr);
	debug("<-getIEByte()\n",LFAPMET_DBG_FUNC);
	return res;
}

/* expands a Multiple Record IE	in a new buffer				*/
/* freeing the old & new buffer should be done by caller	*/
/* length of the new buffer containing all expanded IEs		*/
/* is returned in 'newlen'									*/
/*
  0                   1                   2                   3
  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |           TYPE = 22           |          LENGTH               |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |  Length of fixed Information  |   Length of Record Format     |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |                                                               |
 ~                     Fixed Information                         ~
 |                                                               |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |                                                               |
 ~                       Record Format                           ~
 |                                                               |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |                                                               |
 |                    Individual Record (1)                      |
 |                    Individual Record (2)                      |
 |                    Individual Record (3)                      |
 |                             .                                 |
 ~                             .                                 ~
 |                             .                                 |
 |                    Individual Record (n)                      |
 |                                                               |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
LFAP_IE_Header* expandMultipleRecordIE(LFAP_IE_Header* mrie, int* newlen)
{
	LFAP_IE_Header* newie;
	int				newie_size;
	
	LFAP_IE_Header* fixedie;
	LFAP_IE_Header* formatie;
	void* 			recordie;
	LFAP_IE_Header* workie;
	LFAP_IE_Header* workie1;
	LFAP_IE_Header* rec_ie_start;
	void* 			ptr;

	int fixed_len;
	int rec_fmt_len;
	int total_len;
	int records_len;
	int one_record_size;
	int nr_of_entries;
	int i;
	int size;
	int offset;

	debug("->expandMultipleRecordIE()\n",LFAPMET_DBG_FUNC);
	/* first analyse the different components */
	fixed_len	= getIEWord(mrie, 0);
	rec_fmt_len	= getIEWord(mrie, 2);
	total_len	= mrie->length;
	records_len	= total_len - fixed_len - rec_fmt_len - 2*sizeof(Bit16);
/*
printf("\t====================================\n");
printf("\t======== multiple record IE ========\n");
printf("\t====================================\n");
dumphex(mrie, mrie->length + LFAP_IE_HEADER_SIZE);
printf("\ttotal length (excl. header): \t%i\n",total_len);
printf("\tfixed section length: \t\t%i\n",fixed_len);
printf("\tformat section length: \t\t%i (%i headers)\n",rec_fmt_len, 		rec_fmt_len/LFAP_IE_HEADER_SIZE);
printf("\trecords section length: \t%i\n",records_len);
*/
	if (records_len == 0)
	{
		/* what's wrong here? */
		/* just return NULL */
		/* but do a hexdump first! */
/*
debug("expandMultipleRecordIE(): warning: Invalid MRIE\n",LFAPMET_DBG_WARNINGS);
dumphex(mrie, mrie->length + LFAP_IE_HEADER_SIZE);
*/
		debug("<-expandMultipleRecordIE(NULL)\n",LFAPMET_DBG_FUNC);
		return NULL;
	}
	/* now get the fixed information IE(s) */
	fixedie  = (LFAP_IE_Header*)(((void*)mrie) + LFAP_IE_HEADER_SIZE + 2*sizeof(Bit16));
	formatie = (LFAP_IE_Header*)(((void*)mrie) + LFAP_IE_HEADER_SIZE + 2*sizeof(Bit16) + fixed_len);
	recordie = (((void*)mrie) + LFAP_IE_HEADER_SIZE + 2*sizeof(Bit16) + fixed_len + rec_fmt_len);

//printf("\t======== fixed section IEs =========\n");
	/* walk through it, normalizing it along the way */
	workie = fixedie;
	if (fixed_len > 0)
	{
		while(workie != NULL)
		{
			workie->type = ntohs(workie->type);
			workie->length = ntohs(workie->length);
//printf("\tfixed IE:\t%i (%s)\n",getIEType(workie),IElist[getIEType(workie)]);
			workie = getNextIE(fixedie, workie, fixed_len);
		}
	}
//printf("\t========== format section ==========\n");
	workie = formatie;
	/*  determine size of one record as well, starts with 
	 	size of format section which consists of headers only
	 	add the 'value' sizes for each IE in a record in the loop */
	one_record_size = rec_fmt_len;
	while(((void*)workie) < (((void*)formatie) + rec_fmt_len))
	{
		workie->type = ntohs(workie->type);
		workie->length = ntohs(workie->length);
		one_record_size += getIELengthExcl(workie);
//printf("\tformat IE:\t%i (%s)\n",getIEType(workie),IElist[getIEType(workie)]);
		workie = (LFAP_IE_Header*) (((void*)workie)+LFAP_IE_HEADER_SIZE);
	}

//printf("\t====================================\n");
//printf("\tsize of one (expanded) record:\t%i\n",one_record_size);
//printf("\tsize of one record entry:\t%i\n",one_record_size - rec_fmt_len);

	 if (records_len == 0) nr_of_entries = 0;
	 else nr_of_entries = records_len / (one_record_size - rec_fmt_len);
//printf("\tnumber of entries:\t\t%i\n", nr_of_entries);

	/* can copy this first to newie section */

	/*  now malloc the total size to accomodate the records as well
		then set a pointer to the start of the records section in
		the new list of IEs */
	newie_size = fixed_len + nr_of_entries*one_record_size;
//printf("\tnewie_size:\t\t%i\n", newie_size);
	newie = malloc(newie_size);
	if (newie == NULL) return NULL;
	/* copy the (normalized earlier) fixed IEs */
	memcpy(newie, fixedie, fixed_len);
	rec_ie_start = (LFAP_IE_Header*)(((void*)newie) + fixed_len);
	
	/* now copy only the (format) IE headers to all records */
	workie = formatie;
	offset = 0;

	while(((void*)workie) < (((void*)formatie) + rec_fmt_len))
	{
		/* offset within a record */
		for (i=0; i<nr_of_entries; i++)
		{
			/* copy to every (expanded) record */
			workie1 = (LFAP_IE_Header*)(((void*)rec_ie_start) + i*one_record_size);
			workie1 = (LFAP_IE_Header*)(((void*)workie1) + offset);
			memcpy(workie1, workie, LFAP_IE_HEADER_SIZE);
		}
		offset += LFAP_IE_HEADER_SIZE;
		offset += getIELengthExcl(workie);
		workie = (LFAP_IE_Header*) (((void*)workie)+LFAP_IE_HEADER_SIZE);
	}

	/* now fill in those records! */
	workie	= recordie; 	/* source 		*/
	workie1	= rec_ie_start; /* destination	*/

	while(workie1 != NULL)
	{
		size = getIELengthExcl(workie1);
		ptr = ((void*)workie1) + LFAP_IE_HEADER_SIZE;
		memcpy(ptr, workie, size);
		workie = (LFAP_IE_Header*)(((void*)workie)+size);
		workie1 = getNextIE(rec_ie_start, workie1, one_record_size*nr_of_entries);
	}
//printf("\t====================================\n");
	/* report back the total length */
	*newlen = newie_size;
	debug("<-expandMultipleRecordIE()\n",LFAPMET_DBG_FUNC);
	return newie;
}

/* returns the opcode of a message */
Bit8 getLFAPOpcode(LFAPHeader* lfap)
{
	debug("->getLFAPOpcode()\n",LFAPMET_DBG_FUNC);
	debug("<-getLFAPOpcode()\n",LFAPMET_DBG_FUNC);
	return (lfap->opcode);
}


/* returns the LFAP version of a message */
Bit8 getLFAPVersion(LFAPHeader* lfap)
{
	debug("->getLFAPVersion()\n",LFAPMET_DBG_FUNC);
	debug("<-getLFAPVersion()\n",LFAPMET_DBG_FUNC);
	return (lfap->version);
}


Bit8	getLFAPStatus(LFAPHeader* lfap)
{
	debug("->getLFAPStatus()\n",LFAPMET_DBG_FUNC);
	debug("<-getLFAPStatus()\n",LFAPMET_DBG_FUNC);
	return (lfap->status);
}


/* returns the message ID of a message */
Bit16 getLFAPMessageID(LFAPHeader* lfap)
{
	debug("->getLFAPMessageID()\n",LFAPMET_DBG_FUNC);
	debug("<-getLFAPMessageID()\n",LFAPMET_DBG_FUNC);
	return (lfap->messageID);
}


/* Inserts an IE in an LFAP message and adjusts the message size */
void addIE(LFAPHeader** lfap, void* ie)
{
	int newsize_incl;
	int newsize_excl;
	int oldsize_incl;
	int ie_size;
	void* insertat;
	void* lfapnew;
	
	debug("->()addIE()\n",LFAPMET_DBG_FUNC);
	ie_size = getIELengthIncl(ie);
	oldsize_incl = getLFAPMessageLengthIncl(*lfap);
	/* first determine the new size (inclusive) */
	newsize_incl = oldsize_incl + ie_size;
	/* and the new size (exclusive) */
	newsize_excl = getLFAPMessageLengthExcl(*lfap) + ie_size;

	/* alloc the space for the message */
	lfapnew = malloc(newsize_incl);
	if (lfapnew == NULL)
	{
		debug("addIE(): malloc failed!\n",LFAPMET_DBG_ERRORS);
		return;
	}
	memcpy(lfapnew, *lfap, oldsize_incl);
	/* copy the IE into place */
	insertat = lfapnew + oldsize_incl;
	memcpy(insertat, ie, ie_size);
	/* free the old memory */
	free(*lfap);
	/* set the message size correctly */
	*lfap = lfapnew;
	(*lfap)->messageLength = newsize_excl;
	debug("<-()addIE()\n",LFAPMET_DBG_FUNC);
}


/* adds a number of IEs to an LFAP message and adjusts the message size */
/* size denotes the size of the memory block that has to be added */
void addIEBlock(LFAPHeader** lfap, void* ie, int size)
{
	int newsize_incl;
	int newsize_excl;
	int oldsize_incl;
	void* insertat;
	void* lfapnew;
	
	debug("->()addIEBlock()\n",LFAPMET_DBG_FUNC);
	oldsize_incl = getLFAPMessageLengthIncl(*lfap);
	/* first determine the new size (inclusive) */
	newsize_incl = oldsize_incl + size;
	/* and the new size (exclusive) */
	newsize_excl = getLFAPMessageLengthExcl(*lfap) + size;

	/* alloc the space for the message */
	lfapnew = malloc(newsize_incl);
	if (lfapnew == NULL)
	{
		debug("addIEBlock(): malloc failed!\n",LFAPMET_DBG_ERRORS);
		return;
	}
	memcpy(lfapnew, *lfap, oldsize_incl);
	/* copy the IE into place */
	insertat = lfapnew + oldsize_incl;
	memcpy(insertat, ie, size);
	/* free the old memory */
	free(*lfap);
	/* set the message size correctly */
	*lfap = lfapnew;
	(*lfap)->messageLength = newsize_excl;
	debug("<-()addIEBlock()\n",LFAPMET_DBG_FUNC);
}


/* normalizes all the headers in the message			*/
/* i.e. convert from network to host order				*/
/* the header itself as well as the headers of the IEs	*/
/* it will also expand a multiple record IE if present	*/
void normalizeHeaders(LFAPHeader** lfap)
{
	LFAPHeader* lfapnew;
	LFAP_IE_Header*	ie;
	LFAP_IE_Header* next_ie;
	void*			ie_tmp;
	LFAP_IE_Header* mrie;
	int size;
	int newmsg_size;
	int rest_size;
	
	debug("->normalizeHeaders()\n",LFAPMET_DBG_FUNC);
	/* first normalize the LFAP header, octets not needed */
	(*lfap)->messageID = ntohs((*lfap)->messageID);
	(*lfap)->messageLength = ntohs((*lfap)->messageLength);

	/* see if we need to get some IEs as well */
	
	/* now we can get the first IE */
	ie = getFirstIE(*lfap);
	while(ie != NULL)
	{
		ie->type = ntohs(ie->type);
		ie->length = ntohs(ie->length);

		if (ie->type == LFAP_IE_MULTIPLE_RECORD)
		{
			/* expand the Multiple Record IE */
			mrie = expandMultipleRecordIE(ie, &size);
			if (mrie == NULL)
			{ /* expanding failed for some reason, dump it... */
/*				dumphex((*lfap),((*lfap)->messageLength)+LFAP_HEADER_SIZE);*/
			}
			/* make a new one... */
			lfapnew = newLFAPMessage(
									 getLFAPOpcode(*lfap),
									 getLFAPStatus(*lfap),
									 getLFAPMessageID(*lfap)
									);
			free(*lfap);
			*lfap = lfapnew;
			if (mrie != NULL)
			{
				addIEBlock(lfap,mrie,size);
				free(mrie);
			}
			ie = NULL;
		}
		else
		{
			ie = getNextIE(*lfap, ie, getLFAPMessageLengthIncl(*lfap));
		}
	}
	debug("<-normalizeHeaders()\n",LFAPMET_DBG_FUNC);
}


/* 'de'normalizes all the headers in the message		*/
/* i.e. convert from host to network order				*/
/* the header itself as well as the headers of the IEs	*/
/* it will also expand a multiple record IE if present	*/
void denormalizeHeaders(LFAPHeader* lfap)
{
	LFAP_IE_Header*	ie;
	LFAP_IE_Header* next_ie;
	int msglen;
		
	debug("->denormalizeHeaders()\n",LFAPMET_DBG_FUNC);
	
	msglen = getLFAPMessageLengthIncl(lfap);
	ie = getFirstIE(lfap);
	
	/* denormalize the LFAP header, octets not needed */
	lfap->messageID = htons(lfap->messageID);
	lfap->messageLength = htons(lfap->messageLength);
	
	while(ie != NULL)
	{
		next_ie = getNextIE(lfap, ie, msglen);
		ie->type = htons(ie->type);
		ie->length = htons(ie->length);
		ie = next_ie;
	}
	debug("<-denormalizeHeaders()\n",LFAPMET_DBG_FUNC);
}


/* process opcode functions */
int handleError()
{
	debug("->handleError()\n",LFAPMET_DBG_FUNC);
	debug("<-handleError()\n",LFAPMET_DBG_FUNC);
	return FALSE;
}

int handleVR(struct interface_info *pi)
{
	FASInfo* fasinfo;
	LFAPHeader* lfap;
	
	debug("->handleVR()\n",LFAPMET_DBG_FUNC);
	fasinfo = &(pi->fasinfo);
	lfap = fasinfo->lfap_msg;
	/* assume it is the correct version */
	lfap = newLFAPMessage(LFAP_OPCODE_VRA,
						  LFAP_STATUS_SUCCESS,
						  getLFAPMessageID(fasinfo->lfap_msg));
	sendLFAPMessage(lfap, fasinfo->conn);
	free(lfap);
	debug("<-handleVR()\n",LFAPMET_DBG_FUNC);
	return TRUE;
}


int handleFAR(struct interface_info *pi)
{
	FASInfo*		fi;
	LFAPHeader* 	lfap;
	LFAP_IE_Header*	ie;
	LFAPFlowStats*	fs;

	debug("->handleFAR()\n",LFAPMET_DBG_FUNC);
	fi = &(pi->fasinfo);
	lfap = fi->lfap_msg;
	fi->fs_pp = realloc(fi->fs_pp,(fi->fs_len+1)*sizeof(LFAPFlowStats*));
	/* now alloc the new stats structure... */
	fi->fs_len++;
	fi->fs_pp[fi->fs_len - 1] = calloc(sizeof(LFAPFlowStats),1);
	fs = fi->fs_pp[fi->fs_len - 1];

	/* set the state to active */
	fs->flowState = LFAP_FLOW_STATE_ACTIVE;
	/* fill in the time stamps (see also comment with TIME_IE) */
	fs->firstTime = uptime_cs();
	fs->lastTime  = uptime_cs();
	/* if we KNOW the difference in sysUpTime we do not have to guesstimate */

	/* now get all the IEs */
	ie = getFirstIE(lfap);
	while (ie != NULL)
	{
		Bit8*	ptr;
		int		ielen,i;
		Bit16	ietype;

		ietype = getIEType(ie);
		switch (ietype)
		{
			case LFAP_IE_TIME:
				/* calculate the difference in time between the FAS and CCE */
				if (pi->fasinfo.guesstimate)
					fs->diffTime = getIEDword(ie,0) - fs->firstTime;
				else
					fs->diffTime = pi->fasinfo.diffTime;
/*				{
char temp[256];
formatDuration(temp,getIEDword(ie,0));
printf("received time  (%lu : %s\n",
		getIEDword(ie,0), temp);
formatDuration(temp,fs->diffTime);
printf("time difference: %s\n",temp);
formatDuration(temp,fs->lastTime);
printf("corrected time : %s\n",temp);
				}
*/
				break;

			case LFAP_IE_FLOW_ID:
				if (getIELengthExcl(ie) > 4)
				{
					getIEDoubleDword(ie, 0, &(fs->fasPrefix));
					fs->flowID = getIEDword(ie,8);
				}
				else
				{
					fs->fasPrefix.hi = 0;
					fs->fasPrefix.lo = 0;
					fs->flowID = getIEDword(ie,0);
				}
				break;

			default:
				processGenericFlowIEs(fs, ie, &(fi->npackets));
				break;
		}
		ie = getNextIE(lfap, ie, getLFAPMessageLengthIncl(lfap));
	}

fs->changed_b = 1;
handleChangedFlows(pi);

	if (lfap_info_level & 5)
	{
		if (lfap_info_level & 1) printf("--------------- new Flow -----------------\n");
		dumpFlowStats(fs);
	}

	debug("<-handleFAR()\n",LFAPMET_DBG_FUNC);
	return TRUE;
}


int handleFUN(struct interface_info *pi)
{
	FASInfo*			fi;
	LFAPHeader* 		lfap;
	LFAP_IE_Header* 	ie;
	LFAP_IE_Header**	ies;
	int 				ies_len;
	LFAPFlowStats*		fs;
	int 				i;
	Bit32		flowID;

	debug("->handleFUN()\n",LFAPMET_DBG_FUNC);
	fi = &(pi->fasinfo);
	lfap = fi->lfap_msg;
	ies = NULL;
	ies_len = 0;

	/* unlike FARS, FUNs can describe multiple flows
	 	There can be first IEs (not flowIDs)
		that are ' fixed IEs' from the multiple record IE
		these can be f.e. timeIEs and relate to all flows
		mentioned after this...*/
	
	/* get all the IEs */
	ie = getFirstIE(lfap);
	if (ie!=NULL)
	{
		int leave = (getIEType(ie) == LFAP_IE_FLOW_ID);
/*		while ((getIEType(ie) != LFAP_IE_FLOW_ID) && (ie!=NULL))*/
		while (!leave)
		{
			void *oldptr = ies;
			ies_len++;
			ies = realloc(ies, ies_len*sizeof(LFAP_IE_Header*));
			if (ies==NULL)
			{
				debug("handleFUN(): realloc failed!\n",LFAPMET_DBG_ERRORS);
				ies = oldptr;
				ies_len--;
				for (i=0;i<ies_len;i++) free(ies[i]);
				if (ies!=NULL) free(ies);	
				return;
			}
			ies[ies_len-1] = malloc(getIELengthIncl(ie));
			if (ies[ies_len-1]==NULL)
			{
				debug("handleFUN(): malloc failed!\n",LFAPMET_DBG_ERRORS);
				return;
			}
			memcpy(ies[ies_len-1],ie,getIELengthIncl(ie));
			ie = getNextIE(lfap, ie, getLFAPMessageLengthIncl(lfap));
			if (ie == NULL) leave = TRUE;
			else leave = (getIEType(ie) == LFAP_IE_FLOW_ID);
		}
		if (ie==NULL)
		{	/* don't forget to free allocated memory ! */
			for (i=0;i<ies_len;i++) free(ies[i]);
			if (ies!=NULL) free(ies);
			return;
		}
		if (getIEType(ie) != LFAP_IE_FLOW_ID)
		{
			for (i=0;i<ies_len;i++) free(ies[i]);
			if (ies!=NULL) free(ies);
			return;
		}
	}

	while (ie != NULL)
	{
		Bit8*		ptr;
		int			ielen,i;
		Bit16		ietype;

		ietype = getIEType(ie);
		switch (ietype)
		{
			case LFAP_IE_FLOW_ID:
				if (getIELengthExcl(ie) > 4) flowID = getIEDword(ie,8);
				else flowID = getIEDword(ie,0);

				/* try to get fs to point to the correct FlowStats structure */
				i = 0;
				while( (i < fi->fs_len) && (flowID!=fi->fs_pp[i]->flowID) ) i++;
				if (i == fi->fs_len)
				{
					for (i=0;i<ies_len;i++) free(ies[i]);
					if (ies!=NULL) free(ies);
					return;
				}
				fs = fi->fs_pp[i];
				/* see if there's a FAS prefix, set it if so */
				if (getIELengthExcl(ie) > 4)
				{
					getIEDoubleDword(ie, 0, &(fs->fasPrefix));
				}
				/* fill in the time stamps (see also comment with TIME_IE) */
				fs->lastTime  = uptime_cs();
				/* modified later if there is a TIME_IE */
				/* processing IEs that are for all flows */
				for (i=0;i<ies_len;i++)
				{
					processGenericFlowIEs(fs, ies[i], &(fi->npackets));
				}
fs->changed_b = 1;
				break;

			default:
				processGenericFlowIEs(fs, ie, &(fi->npackets));
				break;
		}
		ie = getNextIE(lfap, ie, getLFAPMessageLengthIncl(lfap));
	}
	/* now free the ies structure */
	for (i=0;i<ies_len;i++) free(ies[i]);
	if (ies!=NULL) free(ies);

	handleChangedFlows(pi);
	debug("<-handleFUN()\n",LFAPMET_DBG_FUNC);
	return TRUE;
}


int handleAR(struct interface_info *pi)
{
	FASInfo* 		fasinfo;
	LFAPHeader* 	lfap;
	LFAP_IE_Header* ie;
	Bit16* 	word_p;
	Bit32	command;
	
	debug("->handleAR()\n",LFAPMET_DBG_FUNC);
	fasinfo = &(pi->fasinfo);
	lfap = fasinfo->lfap_msg;
	/* assume 1st one is command IE */
	ie = getFirstIE(lfap);
	if (ie != NULL)
	{
		command = getIEDword(ie, 0);
		switch (command)
		{
			case LFAP_CMD_RETURN_FLOW_PREFIX:
				handleCMD_RFP(fasinfo);
				break;
				
			case LFAP_CMD_LIST_OF_FASS:
				handleCMD_LOF(fasinfo);
				break;
				
			default:
				/* could do some warning here */
				break;
		}
	}
	else
	{
		debug("handleAR(): NO IEs present!\n",LFAPMET_DBG_ERRORS);
	}
	debug("<-handleAR()\n",LFAPMET_DBG_FUNC);
	return TRUE;
}

int handleARA(struct interface_info *pi)
{
	debug("->handleARA()\n",LFAPMET_DBG_FUNC);
	/* just ignore */
	debug("<-handleARA()\n",LFAPMET_DBG_FUNC);
	return TRUE;
}


void processGenericFlowIEs( LFAPFlowStats* fs,
							LFAP_IE_Header* ie,
							unsigned long *npkts)
{
	Bit16 ietype;
	/* handles generic IEs for FAR & FUN messages
	 	flowID and timeIEs not included (not generic)!!!! */
	debug("->processGenericFlowIEs()\n",LFAPMET_DBG_FUNC);
	ietype = getIEType(ie);
	switch (ietype)
	{
		case LFAP_IE_TIME:
			fs->lastTime = getIEDword(ie,0) - fs->diffTime;
/*
			{
char temp[256];
formatDuration(temp,getIEDword(ie,0));
printf("received time  : %s\n",temp);
formatDuration(temp,fs->diffTime);
printf("time difference: %s\n",temp);
formatDuration(temp,fs->lastTime);
printf("corrected time : %s\n",temp);
			}
*/
			break;

		case LFAP_IE_SRC_AND_DEST_AS:
			fs->src_as  = getIEWord(ie,0);
			fs->dest_as = getIEWord(ie,2);
			break;

		case LFAP_IE_SOURCE_ADDRESS:
			{
				void* ptr;
				fs->src_fam = getIEWord(ie,0);
				fs->src_len = getIEWord(ie,2);
				ptr = ((void*)ie)+LFAP_IE_HEADER_SIZE+2*sizeof(Bit16);
				memcpy(fs->src_addr, ptr, fs->src_len);
			}
			break;

		case LFAP_IE_DESTINATION_ADDRESS:
			{
				void* ptr;
				fs->dest_fam = getIEWord(ie,0);
				fs->dest_len = getIEWord(ie,2);
				ptr = ((void*)ie)+LFAP_IE_HEADER_SIZE+2*sizeof(Bit16);
				memcpy(fs->dest_addr, ptr, fs->dest_len);
			}
			break;

		case LFAP_IE_SOURCE_CCE_ADDRESS:
			{
				void* ptr;
				fs->src_cce_fam = getIEWord(ie,0);
				fs->src_cce_len = getIEWord(ie,2);
				ptr = ((void*)ie)+LFAP_IE_HEADER_SIZE+2*sizeof(Bit16);
				memcpy(fs->src_cce_addr, ptr, fs->src_cce_len);
			}
			break;

		case LFAP_IE_TYPE_OF_SERVICE:
			fs->tos = (getIEDword(ie,0))>>2;
			/* DSCodePoint is upper 6 bits of DS field */
			break;

		case LFAP_IE_SOURCE_PORT_UDP:
			fs->src_port_type = 17;
			fs->src_port = getIEDword(ie,0);
			break;

		case LFAP_IE_SOURCE_PORT_TCP:
			fs->src_port_type = 6;
			fs->src_port = getIEDword(ie,0);
			break;

		case LFAP_IE_SOURCE_PORT_IPX:
			fs->src_port_type = 111;
			fs->src_port = getIEDword(ie,0);
			break;

		case LFAP_IE_INGRESS_PORT:
			fs->ingress_port = getIEDword(ie,0); /* as in RFC2233 */
			break;

		case LFAP_IE_EGRESS_PORT:
			fs->egress_port = getIEDword(ie,0); /* as in RFC2233 */
			break;

		case LFAP_IE_BYTE_COUNT_RUNNING:
			{
				val64 Octets_recvd;
				val64 Octets_sent;
				/* get the byte counts for sent and received... */
				getIEDoubleDword(ie,0,&(Octets_recvd));
				getIEDoubleDword(ie,8,&(Octets_sent));
				/* from this calculate the delta. 				*/
				/* if the running counter is not reset to 0 !	*/
				if ((Octets_recvd.lo | Octets_recvd.hi)!=0)
				{
					sub64(&(fs->dOctets_recvd),
						   Octets_recvd,
						   fs->Octets_recvd);
				}
				else
				{
					fs->dOctets_recvd.lo = 0;
					fs->dOctets_recvd.hi = 0;
				}
				
				if ((Octets_sent.lo | Octets_sent.hi)!=0)
				{
					sub64(&(fs->dOctets_sent),
						   Octets_sent,
						   fs->Octets_sent);
				}
				else
				{
					fs->dOctets_sent.lo = 0;
					fs->dOctets_sent.hi = 0;
				}
				/* now copy to the FlowStats structure */
				memcpy(&(fs->Octets_recvd),&(Octets_recvd),sizeof(val64));
				memcpy(&(fs->Octets_sent), &(Octets_sent), sizeof(val64));
			}
			break;

		case LFAP_IE_BYTE_COUNT_DELTA:
			/* get the delta counts for sent and received... */
			getIEDoubleDword(ie,0,&(fs->dOctets_recvd));
			getIEDoubleDword(ie,8,&(fs->dOctets_sent));
			/* add them to the running total */
			add64(&(fs->Octets_recvd), fs->Octets_recvd, fs->dOctets_recvd);
			add64(&(fs->Octets_sent), fs->Octets_sent, fs->dOctets_sent);
			break;

		case LFAP_IE_PACKET_COUNT_RUNNING:
			{
				val64 Packets_recvd;
				val64 Packets_sent;
				/* get the packet counts for sent and received... */
				getIEDoubleDword(ie,0,&(Packets_recvd));
				getIEDoubleDword(ie,8,&(Packets_sent));
				/* from this calculate the delta. */
				/* if the running counter is not reset to 0 !	*/
				if ((Packets_recvd.lo | Packets_recvd.hi)!=0)
				{
					sub64(&(fs->dPackets_recvd),
						   Packets_recvd,
						   fs->Packets_recvd);
				}
				else
				{
					fs->dPackets_recvd.lo = 0;
					fs->dPackets_recvd.hi = 0;
				}
				
				if ((Packets_sent.lo | Packets_sent.hi)!=0)
				{
					sub64(&(fs->dPackets_sent),
						   Packets_sent,
						   fs->Packets_sent);
				}
				else
				{
					fs->dPackets_sent.lo = 0;
					fs->dPackets_sent.hi = 0;
				}
				/* add them to npackets */
				*npkts += fs->dPackets_recvd.lo;
				*npkts += fs->dPackets_sent.lo;
				/* now copy to the FlowStats structure */
				memcpy(&(fs->Packets_recvd),&(Packets_recvd),sizeof(val64));
				memcpy(&(fs->Packets_sent), &(Packets_sent), sizeof(val64));
			}
			break;

		case LFAP_IE_PACKET_COUNT_DELTA:
			/* get the delta counts for sent and received... */
			getIEDoubleDword(ie,0,&(fs->dPackets_recvd));
			getIEDoubleDword(ie,8,&(fs->dPackets_sent));
			/* add them to the running total */
			add64(&(fs->Packets_recvd), fs->Packets_recvd, fs->dPackets_recvd);
			add64(&(fs->Packets_sent), fs->Packets_sent, fs->dPackets_sent);
			/* add them to npackets */
			*npkts += fs->dPackets_recvd.lo;
			*npkts += fs->dPackets_sent.lo;
			break;

		case LFAP_IE_FLOW_STATE:
			fs->flowState = getIEDword(ie,0);
			break;

		case LFAP_IE_PROTOCOL_IDENTIFIER:
			/* do a simple hexdump */
			memcpy(	fs->protDirId,
					((void*)ie)+LFAP_IE_HEADER_SIZE,
					getIELengthExcl(ie));
			/* disect the protocol identifier */
			if ((getIEDword(ie,1)==1) &&
				(getIEDword(ie,5)==0x800))
			{
				fs->protocol = getIEByte(ie,12);
				if ((fs->protocol == 17)||(fs->protocol == 6))
				{
					fs->dst_port_type = fs->protocol;
					fs->dst_port = getIEWord(ie,15);
				}
			}
			break;

		default:
			break;
	}
	debug("<-processGenericFlowIEs()\n",LFAPMET_DBG_FUNC);
}


void sub64(val64* res, val64 orig, val64 min)
{
	val64 result;
	/*subtract min from org, result stored in res */
	result.lo = (orig.lo - min.lo) & 0xFFFFFFFF;
	if (result.lo > orig.lo) result.hi = orig.hi - 1 - min.hi;
	else result.hi = orig.hi - min.hi;
	memcpy(res, &result, sizeof(val64));
}


void add64(val64* res, val64 orig, val64 pls)
{
	val64 result;
	/*add pls to org, result stored in res */
	result.lo = (orig.lo + pls.lo) & 0xFFFFFFFF;
	if (result.lo < orig.lo) result.hi = orig.hi + 1 + pls.hi;
	else result.hi = orig.hi + pls.hi;
	memcpy(res, &result, sizeof(val64));
}

void sendKeepAlive(FASInfo *fasinfo)
{
	LFAPHeader* 	lfap;
	LFAP_IE_Header* ie;
	
	/* a keepalive is a command code message (AR) */
	lfap = newLFAPMessage(	LFAP_OPCODE_AR,		\
							LFAP_STATUS_SUCCESS,\
							newMessageId(fasinfo));
	if (lfap!=NULL)
	{
		ie = newIE(LFAP_IE_COMMAND_CODE, 4);
		if (ie!=NULL)
		{
			putIEDword(ie, LFAP_CMD_KEEPALIVE, 0);
			addIE(&lfap,ie);
			free(ie);
		}
		sendLFAPMessage(lfap, fasinfo->conn);
		free(lfap);
	}
}


void handleCMD_RFP(FASInfo* fasinfo)
{
	LFAPHeader* lfaprsp;
	LFAP_IE_Header* ie;
	Bit16* ptr;
	
	debug("->CMD_RFP\n",LFAPMET_DBG_FUNC);
	/*  return a flow prefix... */

	lfaprsp = newLFAPMessage(LFAP_OPCODE_ARA,
							 LFAP_STATUS_SUCCESS,
							 getLFAPMessageID(fasinfo->lfap_msg));

	if (lfaprsp!=NULL)
	{
		/* fill in flow ID prefix IE */
		ie = newIE(LFAP_IE_FLOW_ID_PREFIX,8);
		if (ie!=NULL)
		{
			/* maybe replace by own IP address ? */
			putIEDword(ie, fasPrefix.lo, 0);
			putIEDword(ie, fasPrefix.hi, 4);

			addIE(&lfaprsp, ie);
			free(ie);
			sendLFAPMessage(lfaprsp, fasinfo->conn);
		}
		free(lfaprsp);
	}
	debug("<-CMD_RFP\n",LFAPMET_DBG_FUNC);
}


void handleCMD_LOF(FASInfo* fasinfo)
{
	LFAPHeader* lfap;
	LFAP_IE_Header* ie;
	
	debug("->CMD_LOF\n",LFAPMET_DBG_FUNC);
	/* assume it is the right one ... */
	lfap = newLFAPMessage(LFAP_OPCODE_AR,
						  LFAP_STATUS_SUCCESS,
						  newMessageId(fasinfo));
	if (lfap!=NULL)
	{
		/* fill in command code IE */
		ie = newIE(LFAP_IE_COMMAND_CODE,4);
		if (ie!=NULL)
		{
			putIEDword(ie, (Bit32)LFAP_CMD_CONNECTION_ACCEPTED, 0);
			addIE(&lfap, ie);
			free(ie);
			sendLFAPMessage(lfap, fasinfo->conn);
		}
		free(lfap);
	}
	debug("<-CMD_LOF\n",LFAPMET_DBG_FUNC);
}


void sendLFAPMessage(LFAPHeader* lfap, int conn)
{
	int msglen;
	
	debug("->sendLFAPMessage()\n",LFAPMET_DBG_FUNC);
	if ((lfap_info_level & 10)!=0)
	{
		dumpMessage(lfap,"----------- Sending Message -------------\n");
	}
	msglen = getLFAPMessageLengthIncl(lfap);
	denormalizeHeaders(lfap);


   	if (write(conn, lfap, msglen)<0)
	{
		printf("write to socket failed!\n");
		debug("<-sendLFAPMessage() (FAILED)\n",LFAPMET_DBG_FUNC);
	}
	debug("<-sendLFAPMessage() (OK)\n",LFAPMET_DBG_FUNC);
}


Bit16 newMessageId(FASInfo* fasinfo)
{
	return fasinfo->msgID++;
}

LFAPHeader* newLFAPMessage(
							Bit8 opcode,
							Bit8 status,
							Bit16 messageID
						  )
{
	LFAPHeader* lfap;
	
	debug("->newLFAPMessage()\n",LFAPMET_DBG_FUNC);
	lfap = malloc(LFAP_HEADER_SIZE);
	if (lfap!=NULL)
	{
		lfap->version = LFAP_VERSION;
		lfap->opcode = opcode;
		lfap->reserved = 0;
		lfap->status = status;
		lfap->messageID = messageID;
		lfap->messageLength = 0;
	}
	debug("<-newLFAPMessage()\n",LFAPMET_DBG_FUNC);
	return lfap;
}


LFAP_IE_Header* newIE(Bit16 type, Bit16 length)
{
	LFAP_IE_Header* ie;
	
	debug("->newIE()\n",LFAPMET_DBG_FUNC);
	ie = malloc(LFAP_IE_HEADER_SIZE+length);
	if (ie!=NULL)
	{
		ie->type = type;
		ie->length = length;
	}
	debug("<-newIE()\n",LFAPMET_DBG_FUNC);
	return ie;
}


void putIEDword(LFAP_IE_Header* ie, Bit32 value, int offset)
{
	Bit32* ptr;
	
	debug("->putIEDword()\n",LFAPMET_DBG_FUNC);
	if (offset >= getIELengthExcl(ie))
	{
		debug("<-putIEDword() ERROR!\n",LFAPMET_DBG_FUNC);
		return;
	}
	ptr = (Bit32*)(((void*)ie)+LFAP_IE_HEADER_SIZE+offset);
	/* htonl already done here! 							*/
	/* (for receiving, values also stay in network order)	*/
	*ptr = htonl(value);
	debug("<-putIEDword()\n",LFAPMET_DBG_FUNC);
	return;
}


void putIEWord(LFAP_IE_Header* ie, Bit16 value, int offset)
{
	Bit16* ptr;
	
	debug("->putIEWord()\n",LFAPMET_DBG_FUNC);
	if (offset >= getIELengthExcl(ie))
	{
		debug("<-putIEWord() ERROR!\n",LFAPMET_DBG_FUNC);
		return;
	}
	ptr = (Bit16*)(((void*)ie)+LFAP_IE_HEADER_SIZE+offset);
	/* htons already done here! 							*/
	/* (for receiving, values also stay in network order)	*/
	*ptr = htons(value);
	debug("<-putIEWord()\n",LFAPMET_DBG_FUNC);
	return;
}

/* tries to get a system.sysUpTime.0 from the CCE 					*/
/* returns the sysUpTime or 0 if the get fails for some reason 		*/
/* which (quite erroneously) assumes that sysUpTime can't be zero, 	*/
/*  although it is highly unlikely....								*/
Bit32 getSysUpTime(struct interface_info *pi)
{
	int 				t;
	int 				len;
	char				peer[256];
	struct sockaddr_in	sin;
	struct hostent  	*host;
    struct snmp_session session, *ss;
    struct snmp_session sessn;
    struct snmp_pdu 	*pdu = NULL;
    struct snmp_pdu 	*response = NULL;
    oid 				anOID[]={1,3,6,1,2,1,1,3,0}; /* system.sysUpTime.0 */
    size_t  			anOID_len = 9;
    struct variable_list *vars;
    int 				status;

	len = sizeof sin;
	if (getpeername(pi->fasinfo.conn,
					(struct sockaddr *) &sin,
					&len) < 0)
	{
		perror("getpeername");
		return 0;
	}
	else
	{
		strcpy(peer, inet_ntoa(sin.sin_addr));
//		printf("remote host is %s (",peer);
	}

	session.version = SNMP_VERSION_1;
	session.community = lfap_community;
	session.community_len = strlen(lfap_community);
	session.retries = 1;
	session.timeout = 3000000; /* in uS, so this is 3 secs */
	session.peername = peer;
	session.remote_port = 161;
	session.local_port = 0; /* don't care */
	session.authenticator = NULL;

	ss = snmp_open(&session);
	if (ss == NULL)
	{
		printf("error opening SNMP session!\n");
		return 0;
	}
	else
	{
//		printf("opening SNMP session OK!\n");
		/* now get the system.sysUpTime.0	*/
		/* equals: .1.3.6.1.2.1.1.3.0 		*/
		pdu = snmp_pdu_create(GET_REQ_MSG);
		snmp_add_null_var(pdu, anOID, anOID_len);
		snmp_synch_setup(ss);
		status = snmp_synch_response(ss, pdu, &response);
		pdu = NULL;

	    /*
   		 * Process the response.
	     */
		if (status == STAT_SUCCESS && response->errstat == SNMP_ERR_NOERROR)
		{
			char temp[256];
			/*
			 * SUCCESS: Print the result variables
			 */
			vars = response->variables;
//printf("variable type=%i\n",vars->type);
			formatDuration(temp,*(vars->val.u_int));
//printf("value = %s\n",temp);
	    	/*
		     * Clean up:
	    	 */
			if (response)
				snmp_free_pdu(response);
			if (pdu)
				snmp_free_pdu(pdu);
			snmp_close(ss);
			return *(vars->val.u_int);
		}
		else
		{
			/*
			 * FAILURE: print what went wrong!
			 */
			if (status == STAT_SUCCESS)
				fprintf(stderr, "Error in packet\nReason: %s\n",          										snmp_errstring(response->errstat));
			else
			{
//				printf("ERROR!!!!!\n");
			}
	    	/*
		     * Clean up:
	    	 */
			if (response)
				snmp_free_pdu(response);
			if (pdu)
				snmp_free_pdu(pdu);
			snmp_close(ss);
			return 0;
		}
	}
}

void processMessage(struct interface_info *pi)
{
	FASInfo* fasinfo;
	LFAPHeader* lfap;
	Bit32 dword;
	Bit8 opcode;
	
	debug("->processMessage()\n",LFAPMET_DBG_FUNC);
	fasinfo = &(pi->fasinfo);
	lfap = fasinfo->lfap_msg;
	if ((lfap_info_level & 10)!=0)
	{
		dumpMessage(lfap,"----------- Received Message -------------\n");
	}
		
	opcode = getLFAPOpcode(lfap);
	switch (opcode)
	{
		case LFAP_OPCODE_FUN:
			handleFUN(pi);
			break;

		case LFAP_OPCODE_FAR:
			handleFAR(pi);
			break;

		case LFAP_OPCODE_AR:
			handleAR(pi);
			break;

		case LFAP_OPCODE_VR:
			pi->fasinfo.guesstimate = 1; /* guesstimate, unless proven otherwise */
			handleVR(pi);
			dword = getSysUpTime(pi);
			if (dword == 0)
			{
//				printf("FAILED!!!\n");
			}
			else
			{
				char temp[256];
				pi->fasinfo.diffTime 	= dword - uptime_cs();
				pi->fasinfo.guesstimate = 0; /* we know better now than to guess */
				formatDuration(temp, dword);
//printf("%s\n",temp);
				formatDuration(temp, pi->fasinfo.diffTime);
//printf("diff = %s\n",temp);
			}

			break;

		case LFAP_OPCODE_ARA:
			handleARA(pi);
			break;

		default:
			handleError(pi);
			break;
	}
	debug("<-processMessage()\n",LFAPMET_DBG_FUNC);
	return;
}


int init_interface(struct interface_info *pi)
{
	pi->fd = -1;	/* set file descriptor to -1 first (invalid) */
	pi->fasinfo.conn = -1;
	pi->fasinfo.lfap_msg = NULL;
	pi->fasinfo.fs_pp = NULL;
	pi->fasinfo.fs_len = 0;
	pi->fasinfo.msgID = 1;
	sprintf(pi->name,"lfap%i",pi->nbr - 1);
	pi->fasinfo.buf = malloc(LFAP_RECEIVE_BUF_SIZE);
	if (pi->fasinfo.buf == NULL) return 0; /* FAIL */
	pi->fasinfo.bufsize = LFAP_RECEIVE_BUF_SIZE;
	pi->fasinfo.recvptr = pi->fasinfo.buf;
	pi->fasinfo.ticks = 0;

	return 1; /* OK */
}


void interface_read(struct interface_info *pi)
{
	int n, complete, received;
	FASInfo* fi;
	LFAPHeader* lfap_hdr;
	Bit16 msgLength;
	
	debug("interface_read()\n", LFAPMET_DBG_FUNC);
	received = 0;
	fi = &(pi->fasinfo);
	n = recv(
			pi->fd,
			fi->recvptr,
			LFAP_HEADER_SIZE,
			0
			 );
	if (n >= 0)
	{
		received += n;
		/* see what's to come */
		lfap_hdr = (LFAPHeader*) fi->recvptr;
		msgLength = ntohs(lfap_hdr->messageLength);
		fi->recvptr += n;
		if (msgLength > pi->fasinfo.bufsize)
		{
			void *ptr = realloc(pi->fasinfo.buf, msgLength+100);
			if (ptr == NULL) return; /* FAIL */
			pi->fasinfo.buf = ptr;
			fi->recvptr = pi->fasinfo.buf + received;
			pi->fasinfo.bufsize = msgLength+100;
		}
		/* now receive the rest of the message */
		while ((msgLength > 0)&&(n>0))
		{
			n = recv(
					pi->fd,
					fi->recvptr,
					msgLength,
					0
				 	);
			if (n>0)
			{
				fi->recvptr += n;
				received += n;
			}
			msgLength -= n;
		}
		complete = isCompleteMessage(fi->buf,received);
		/*	complete >   0 : more than one message, complete is length of first
			complete ==  0 : no complete message (yet)
			complete == -1 : precisely one complete message
			complete == -2 : reset buffer, discard this garbage */
		if (complete == -1)
		{
			fi->lfap_msg = copyRawLFAPMessage((LFAPHeader*)fi->buf);
			if (fi->lfap_msg!=NULL)
			{
//	size = LFAP_HEADER_SIZE + ntohs(lfapsrc_p->messageLength);
				if ((lfap_info_level & LFAP_INFLVL_LOGHEX)!=0)
				dumphexfile( stdout,
							 fi->lfap_msg,
							 LFAP_HEADER_SIZE+\
							 ntohs(fi->lfap_msg->messageLength));
				if ((lfap_info_level & LFAP_INFLVL_LOGHEX_FILE)!=0)
				{
					FILE* log;
					log = fopen("LfapMet.loghexmessages","a");
					if (log != NULL)
					{
						dumphexfile(log,
									fi->lfap_msg,
						 			LFAP_HEADER_SIZE+ \
									ntohs(fi->lfap_msg->messageLength));
						fclose(log);
					}
				}
				normalizeHeaders(&(fi->lfap_msg));
				processMessage(pi);
				/* and free the memory again */
				free(fi->lfap_msg);
			}
		}
		fi->recvptr = fi->buf;
	}
	if (n==0)
	{
		int uptime;
		uptime = uptime_cs();
		printf("\tUptime:\t%i.%03i seconds\n", uptime/100, (uptime % 100)*10);
		printf("client closed connection, disconnecting...\n");
	}
	else if (n==-1) printf("read error, disconnecting...\n");
	if ((n==-1)||(n==0))
	{
		int i;
		FASInfo* fi;
		
		fi = &(pi->fasinfo);
		close(pi->fd);
		pi->fd = -1;
		fi->conn = -1;
		/* other cleaning up to do ... */
		for (i=0; i < fi->fs_len; i++) free(fi->fs_pp[i]);
		free(fi->fs_pp);
		fi->fs_pp = NULL;
		fi->fs_len = 0;
	}
}


unsigned int pkt_counts(int reset)
{
   int j;
   struct interface_info *pi;

   if (reset)
   {
      npackets_org = pkts;  lostpackets_org = drops;
   }
   pkts = drops = 0L;  /* Get total packets seen and dropped */
   for (j = 0; j != n_interfaces; ++j)
   {
      pi = pci[j];
	  pkts += pi->fasinfo.npackets;
   }
   npackets = pkts - npackets_org;
   lostpackets = drops - lostpackets_org;
   return 1;
} 


void tick(struct interface_info *pi)
{
	FASInfo*		fi;

	fi =&(pi->fasinfo);
/*	printf("tick#:%i (i/f:%i).\n",fi->ticks+1, pi->nbr-1);*/
	fi->ticks++;
	if (fi->ticks >= lfap_keepalive_interval*4)
	{
		fi->ticks = 0;
		if (fi->conn != -1)
		{
			sendKeepAlive(fi);
		}
	}
}

void memcpy_rev(char* dest, char* src, int len)
{
	int i;
	Bit16 test;

	test = 1;
	if (test == htons(test))
	{
		memcpy(dest, src, len);/*host order = nwk order*/
	}
	else
	{
		for (i=0;i<len;i++) *(dest + i) = *(src+len-i-1);/*host != nwk order*/
	}
}

void handleChangedFlows(struct interface_info *pi)
{
	FASInfo*		fi;
	int 			i;
	struct pkt  	pp;

	fi =&(pi->fasinfo);
	for (i=0; i < fi->fs_len; i++)
	{
		LFAPFlowStats* fs = fi->fs_pp[i];
/*		if ((fs->dPackets_recvd.lo|
			fs->dPackets_recvd.hi|
			fs->dPackets_sent.lo|
			fs->dPackets_sent.hi|
			fs->dOctets_recvd.lo|
			fs->dOctets_recvd.hi|
			fs->dOctets_sent.lo|
			fs->dOctets_sent.hi) != 0) */
		if (fs->changed_b)
		{
	if ((lfap_info_level & 5)!=0)
	{
		if ((lfap_info_level & 1)!=0) printf("---------- FlowStats Changed! ------------\n");
		dumpFlowStats(fi->fs_pp[i]);
	}
			/* check if these are all OK !!! */
			pp.ntm_interface	= pi->nbr;
			pp.Low.Interface	= fs->ingress_port;
			pp.High.Interface	= fs->egress_port;
			pp.PeerAddrType 	= fs->src_fam;
			memcpy(&(pp.Low.PeerAddress),&(fs->src_addr), fs->src_len);
			memcpy(&(pp.High.PeerAddress),&(fs->dest_addr), fs->dest_len);
			pp.TransAddrType	= fs->protocol;
			memcpy_rev( (Bit8*)&(pp.Low.TransAddress),
						(Bit8*)&(fs->src_port),
						TRANS_ADDR_LEN);
			memcpy_rev( (Bit8*)&(pp.High.TransAddress),
						(Bit8*)&(fs->dst_port),
						TRANS_ADDR_LEN);
                        pp.Low.AdjAddr_ms4 = 0;  pp.Low.AdjAddr_ls2 = 0;
                        pp.High.AdjAddr_ms4 = 0;  pp.High.AdjAddr_ls2 = 0;
			pp.Low.AdjType		= 0;
			pp.High.AdjType 	= 0;
			pp.dPackets_recvd 	= fs->dPackets_recvd.lo;
			pp.dPackets_sent 	= fs->dPackets_sent.lo;
			pp.dOctets_recvd 	= fs->dOctets_recvd.lo;
			pp.dOctets_sent 	= fs->dOctets_sent.lo;
			pp.FirstTime 		= fs->firstTime;
			pp.LastTime			= fs->lastTime;

			pp.DSCodePoint = (fs->tos & 0xFF);

			fi->npackets += pp.dPackets_recvd;
			fi->npackets += pp.dPackets_sent;

			if ((fs->dPackets_recvd.hi|
				fs->dPackets_sent.hi|
				fs->dOctets_recvd.hi|
				fs->dOctets_sent.hi) != 0)
				debug("handleChangedFlows(): loss of information occurred!\n",
					  LFAPMET_DBG_WARNINGS);
			pkt_monitor(&pp);

			fs->dPackets_recvd.lo = 
				fs->dPackets_recvd.hi = 
				fs->dPackets_sent.lo = 
				fs->dPackets_sent.hi = 
				fs->dOctets_recvd.lo = 
				fs->dOctets_recvd.hi = 
				fs->dOctets_sent.lo = 
				fs->dOctets_sent.hi = 0;

fs->changed_b = 0;

			if (fs->flowState == LFAP_FLOW_STATE_INACTIVE)
			{
				/* clean up this flowstats structure */
				removeFlow(pi, i);
				i--;
			}
		}
	}
}


void removeFlow(struct interface_info *pi, int flownr)
{
	FASInfo*		fi;
	LFAPFlowStats*	fs;
	int				i;

	debug("->removeFlow()\n",LFAPMET_DBG_FUNC);
	fi = &(pi->fasinfo);
	if (fi->fs_pp[flownr] != NULL) free(fi->fs_pp[flownr]);
	fi->fs_len--;
	for (i=flownr; i<fi->fs_len;i++) fi->fs_pp[i] = fi->fs_pp[i+1];
	debug("<-removeFlow()\n",LFAPMET_DBG_FUNC);
}


int start_lfap(struct utsname *name)
{
   printf(
      "\n"
      "\t---------------------------------\n"
      "\t         LfapMet (v1.2)\n"
      "\t      An RTFM meter for LFAP\n"
      "\t \n"
      "\t  made as part of the ING project\n"
      "\t  http://ing.ctit.utwente.nl\n"
      "\t  for questions, remarks, bugs:\n"
      "\t          Remco Poortinga\n"
      "\t  e-mail: r.poortinga@home.nl\n"
      "\t          poortinga@telin.nl\n"
      "\t  This LfapMet software is \n"
      "\t  guaranteed to contain bugs!\n"
      "\t---------------------------------\n\n"
      );

   {  /* Get my IP address for use as FAS prefix */
      struct hostent *he;
      uname(name);
      he = gethostbyname(name->nodename);
      memcpy(&(fasPrefix.lo),he->h_addr_list[0],sizeof(fasPrefix.lo));
      memcpy(&(fasPrefix.hi),he->h_addr_list[0],sizeof(fasPrefix.hi));
      }
   lfap_server_socket = socket(AF_INET, SOCK_STREAM, 0);
   bzero((char *)&lfap_server_addr, sizeof(lfap_server_addr));
   lfap_server_addr.sin_family      = AF_INET;
   lfap_server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
   lfap_server_addr.sin_port        = htons(LFAP_PORT);
   if (bind(lfap_server_socket, (struct sockaddr*)&lfap_server_addr,
         sizeof(lfap_server_addr))<0) {
      perror("bind ERROR!\n");
      return 0; /* Fail */
      }
   listen(lfap_server_socket, 5);
   printf("Ready...\n");
   return 1;  /* OK */
   }
