/*******************************************************************************

	M O D - C A P

   Modify and manipulate PCAP file for lab/test purpouses,
   See the following references:
   - http://wiki.wireshark.org/Development/LibpcapFileFormat
   - http://en.wikipedia.org/wiki/Transmission_Control_Protocol
   - http://en.wikipedia.org/wiki/User_Datagram_Protocol
   - http://en.wikipedia.org/wiki/Internet_Protocol
   - http://en.wikipedia.org/wiki/IPv4
   - http://en.wikipedia.org/wiki/IPv6
   You can change time, speed and rate of the file (not it's content)

   Contatcs:
   ---------
   marco . crotta (at) gmail . com

   Latest version:
   ---------------
   http://www.emcy.it/Linux/files/modcap.c


   Compilation:
   ------------
   Compile with "g++ -O3 -omodcap modcap.c".
   This code makes use of STL libray, so check it first
   For the same reason it will NOT compile with gcc or other similar

   Note:
   -----
   Just a fast job to get things going, nothing you can trust too much
   even if test with wireshark resuted good enought.

   (C) Copyright 2007 Marco Crotta, Released under GPL(2) Licence.
   This is free software

   Version:
   --------                                                                    */
#define VERSION "V 0.0.5 (2008/05/22)"
/*
   Next Steps:
   -----------
	* complete it!
	* adjust overall speed
	* debug... debug... debug... debug...

   History:
   --------
   V 0.0.1 (2007/12/18) First experimental Release
   V 0.0.2 (2008/03/20) Lot of Debug, more is likely to come
   V 0.0.3 (2008/03/28) Still more debug... and more to come
			Added "-b" option: minimum Mbps
			Added "-i" option: modify ip addres
			Added "-m" option: motify MAC address
   v 0.0.4 (2008/04/05) Added "-d" option: modify IPv4/UDP port
   v 0.0.5 (2008/04/05) Debug in IP/TCP Checksum
			Debug in MAC address change

*******************************************************************************/



/*******************************************************************************
	D E F I N E S  &  I N C L U D E S
*******************************************************************************/

#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <limits.h>
#include <errno.h>
#include <math.h>
#include <pthread.h>

#define guint32		unsigned int
#define guint16		unsigned short int
#define gint32		int
#define gint16		short int
#define byte		unsigned char

#define STD_IN		0
#define STD_OUT		1
#define STD_ERR		2

#define PACKLEN		65535
#define MICRO		1000000

#define BUFFSIZE	1024
#define DEBUG		1
#define INFO		2
#define WARNING		3
#define ERROR		4
#define CRITICAL	5

#define	ETHEADER	14


// Macros
#define MIN(a,b)        (((a)<(b))?(a):(b))
#define MAX(a,b)        (((a)>(b))?(a):(b))


/*******************************************************************************
	S T R U C T S
*******************************************************************************/

// Global FILE header
typedef struct pcap_hdr_s {
        guint32 magic_number;   /* magic number */
        guint16 version_major;  /* major version number */
        guint16 version_minor;  /* minor version number */
        gint32  thiszone;       /* GMT to local correction */
        guint32 sigfigs;        /* accuracy of timestamps */
        guint32 snaplen;        /* max length of captured packets, in octets */
        guint32 network;        /* data link type */
} pcap_hdr_t;

// Single PCAP record Header
typedef struct pcaprec_hdr_s {
        guint32 ts_sec;         /* timestamp seconds */
        guint32 ts_usec;        /* timestamp microseconds */
        guint32 incl_len;       /* number of octets of packet saved in file */
        guint32 orig_len;       /* actual length of packet */
} pcaprec_hdr_t;

// Single PCAP record = Header + Data
typedef struct pcaprec{
		pcaprec_hdr_t 	header;
		byte		body[PACKLEN];
} pcaprec_t;


/*******************************************************************************
	G L O B A L   V A R S
*******************************************************************************/

// Command line parameters values
long double	Param=0.0;
char		StrParam[BUFFSIZE];
int		Grane=1;


// Logging Vars
int      	Log_File = STD_OUT;
int      	Log_RecordDate=1;
int      	Log_RecordPid=0;
int		Log_RecordLevel=1;
int		Log_Trigger=DEBUG;
//int		Log_Trigger=INFO;

// Files
int		InFile=0;
int		OutFile=0;


/*******************************************************************************
	F U N C T I O N  S
*******************************************************************************/

void DateStr (char* buff,char* append)
{
        /// Put's actual Date in 'YYYY-MM-DD HH:mm:ss' format into buff and append an extra
	/// string to it (if provided)
	time_t  now;
        struct  tm now_str;
        char    e=0,*A;

        time(&now);
        gmtime_r(&now, &now_str);
        A = (append)? append:&e;
        sprintf(buff,"%04d-%02d-%02d %02d:%02d:%02d%s", 1900+now_str.tm_year, 1+now_str.tm_mon, now_str.tm_mday,
                now_str.tm_hour, now_str.tm_min, now_str.tm_sec, A);
}


void Log(int level, const char* string, ...)
{
        char 	newstring[BUFFSIZE];
        va_list	ap;
        int 	r;

        /// Logging is done only if the level is higher the the defined LogLevel parameter
        if (level < Log_Trigger)
                return;
        /// If a LogFile is not defined simply return without doing anything
        if (Log_File < 0)
                return;
        /// Otherwise write to the file
        bzero(newstring,1024);
        if (Log_RecordDate)
                DateStr(newstring,(char*) " ");
        if (Log_RecordPid)
                sprintf(newstring,"%s%06u ",newstring,(unsigned long)pthread_self());
        if (Log_RecordLevel)
        {
                switch (level)
                {
                        case 1: sprintf(newstring,"%s%s ",newstring,"DEBUG   "); break;
                        case 2: sprintf(newstring,"%s%s ",newstring,"INFO    "); break;
                        case 3: sprintf(newstring,"%s%s ",newstring,"WARNING "); break;
                        case 4: sprintf(newstring,"%s%s ",newstring,"ERROR   "); break;
                        case 5: sprintf(newstring,"%s%s ",newstring,"CRITICAL"); break;
                        default:sprintf(newstring,"%s%s ",newstring,"--*?*-- "); break;
                }
        }

        va_start(ap, string);
        r = vsprintf(&newstring[strlen(newstring)],string, ap);
        va_end(ap);
        sprintf(newstring,"%s\n",newstring);

        write (Log_File, newstring,  MIN(strlen(newstring),BUFFSIZE)  );
	sync();
}

void Usage()
{
	printf("modcap: modify a PCAP file in \"shape\" speed, time and content.\n");
        printf("Version: %s\n",VERSION);
	printf("Usage: modcap [operation] [parameter] <[] []> [infile]\n");
	printf("	one operation only and it's parameter should be supplied at the time\n");
	printf("	otherwise result could be messy\n");
	printf("Operatoins:\n");
	printf("time:   -t <secs>:        set capture time to secs afther EPOC (gen 1st 1970, 00:00:00)\n");
	printf("	-s <secs>:        shift capture time by secs\n");
	printf("speed:	-P <pps>:         Maximum packet per seconds rate (slowdown faster parts)\n");
	printf("	-p <pps>:         Minimum packet per seconds rate (speedup slower parts)\n");
	printf("	-B <pps>:         Maximum MBits per seconds rate  (slowdown faster parts)\n");
	printf("	-b <pps>:         Minimum MBits per seconds rate  (speedup slower parts)\n");
	printf("file:	-o <name>:        Output file name\n");
	printf("address:-i <ip1,ip2>:     change any occurrence of ip1 (in IP headers) to ip2\n");
	printf("                          (do NOT use spaces in the parameter!)\n");
	printf("	-m <mac1,mac2>:   change any occurence of MAC address mac1 to mac2\n");
	printf("	                  (do NOT use spaces in the parameter!)\n");
        printf("        -d <port1,port2>: change any occurrence of TCP/UDP port port1 to port2\n");
	printf("	                  (do NOT use spaces in the parameter!)\n");
	printf("\n");
	_exit(0);
}


int CheckIPAddress(char* IP)
{
	struct in_addr	dst;
	int		r=inet_aton(IP, &dst);

	Log(DEBUG,"IP = %x",dst.s_addr);
	return ((r==0)?	-1 : 1);
}

int CheckMACAddress(char* MAC)
{
	guint32 hex[6];
	char  	check[20];

	memset(check,':',20);
	sscanf (MAC,"%02x:%02x:%02x:%02x:%02x:%02x",&hex[0],&hex[1],&hex[2],&hex[3],&hex[4],&hex[5]);
	sprintf(check,"%02x:%02x:%02x:%02x:%02x:%02x",hex[0],hex[1],hex[2],hex[3],hex[4],hex[5]);
        Log(DEBUG,"Checking %s -> %s", MAC, check);
	if (strcmp(MAC,check)==0)
		return 1;
	else
		return -1;
}

/*******************************************************************************
	PCAP HELPER FUNCTIONS
*******************************************************************************/

void CopyFileHeader()
{
	/// Copy the PCAP Header from source to dest
	int i;
	pcap_hdr_t FH;

        lseek(InFile,0,SEEK_SET);
        lseek(OutFile,0,SEEK_SET);
	Log(DEBUG,"Out file ok");
	i=read(InFile,&FH, sizeof(FH));
	Log(DEBUG,"Pcap File Header read %i bytes",i);
	i=write(OutFile,&FH,sizeof(FH));
	Log(DEBUG,"Pcap File Header write %i bytes",i);
}

int ReadRec(pcaprec_t *PR)
{
	/// Read 1 PCAP Record from Source, return the size of the record
	guint32 i,j;
	i=read(InFile, &(PR->header), sizeof(pcaprec_hdr_t));
	if (i!=sizeof(pcaprec_hdr_t))
	{
		Log(ERROR,"Header read resulted in %i bytes, expeted %i",i,sizeof(pcaprec_hdr_t));
		return -1;
	}
        bzero(PR->body, PACKLEN);
	j=read(InFile, &(PR->body),   PR->header.incl_len);

	if (j!=PR->header.incl_len)
	{
		Log(ERROR,"Body read resulted in %i bytes, expeted %i",i,sizeof(pcaprec_hdr_t));
		return -1;
	}
	return i+j;
}

int WriteRec(pcaprec_t *PR)
{
	/// Write the passed record to destination file.
	int w = sizeof(pcaprec_hdr_t) + PR->header.incl_len,
	    W = 0;

	W=write(OutFile, PR, w);
	if (W!=w)
	{
		Log(ERROR,"OutFile Write resulted in &i bytes, expeted %i",W,w);
		return -1;
	}
	return W;
}

guint32 CountNextSecondPackets()
{
	/// Count how many packet are in the next second of recorded data
	guint32 	Res=0;
	pcaprec_t	Rec;
	off_t 		InitialPos=lseek(InFile, 0, SEEK_CUR);	/// Get & Save File position
	guint32 	Delta      = 0;
	guint32 	Prev_sec   = 0;
	guint32		Prev_usec  = 0;

	if (InitialPos==-1)
		return -1;

        if (ReadRec(&Rec)==-1)
		return -1;
	Res++;
	Prev_sec   = Rec.header.ts_sec;
	Prev_usec  = Rec.header.ts_usec;

	while ( ReadRec(&Rec) > 0)
	{
                //Log (DEBUG,"looping in CountNextSecondPackets	");
                Delta += ((Rec.header.ts_sec - Prev_sec)*MICRO + (Rec.header.ts_usec - Prev_usec));
		if (Delta > MICRO)
			break;
		Res++;
		Prev_sec   = Rec.header.ts_sec;
		Prev_usec  = Rec.header.ts_usec;
	}

        /// Restore previous position
	lseek(InFile, InitialPos, SEEK_SET);
	return Res;
}

guint32 CountNextSecondBytes()
{
	/// Count how many bytes are in the next second of recorded data
	guint32 	Res=0;
	pcaprec_t	Rec;
	off_t 		InitialPos=lseek(InFile, 0, SEEK_CUR);	/// Get & Save File position
	guint32 	Delta      = 0;
	guint32 	Prev_sec   = 0;
	guint32		Prev_usec  = 0;

	if (InitialPos==-1)
		return -1;

        if (ReadRec(&Rec)==-1)
		return -1;
	Res+=Rec.header.incl_len;
	Prev_sec   = Rec.header.ts_sec;
	Prev_usec  = Rec.header.ts_usec;

	while ( ReadRec(&Rec) > 0)
	{
                //Log (DEBUG,"looping in CountNextSecondPackets	");
                Delta += ((Rec.header.ts_sec - Prev_sec)*MICRO + (Rec.header.ts_usec - Prev_usec));
		if (Delta > MICRO)
			break;
		Res+=Rec.header.incl_len;
		Prev_sec   = Rec.header.ts_sec;
		Prev_usec  = Rec.header.ts_usec;
	}

        /// Restore previous position
	lseek(InFile, InitialPos, SEEK_SET);
	return Res;
}

/*******************************************************************************
	COMMAND FUNCTIONS
*******************************************************************************/

void SetTime()
{
	/// Shift file time:
	pcaprec_t PR;
	gint32 Delta;


	/// Copy Header and calculate time difference
	CopyFileHeader();
	ReadRec(&PR);
	Delta=(PR.header.ts_sec - ((guint32)round(Param)));
	Log(DEBUG,"Delta is %i, Param is %Lf,Orig is %i", Delta,Param, PR.header.ts_sec);
	PR.header.ts_sec -= Delta;
	/// Iterate: read each record and change his time
	WriteRec(&PR);
	while (ReadRec(&PR) > 0)
	{
		PR.header.ts_sec -= Delta;
		WriteRec(&PR);
	}
}

void ShiftTime()
{
	pcaprec_t PR;
	CopyFileHeader();

        /// Blindly apply a shift in dime/date of each record
	Log(DEBUG,"Shift is %Lf (%i)",Param, ((gint32)round(Param)));
	while (ReadRec(&PR) > 0)
	{
		PR.header.ts_sec += ((gint32)round(Param));
		WriteRec(&PR);
	}
}

void SetMaxMBps()
{
	pcaprec_t PR;

	guint32 Delta      = 0;
	guint32 Delta_sec  = 0;
	guint32 Delta_usec = 0;
	guint32 Prev_sec   = 0;
	guint32 Prev_usec  = 0;

        /// GH! due to speed and values the resolution of the
	/// counters is not small enought !!
	long double MaxDelta   = (Param*1024*1024)/(8*MICRO); //Bytes each 1/1000000 sec
	guint32 MaxBytes   = 0;


	CopyFileHeader();

        /// Get First Packet and write it unhanged
	ReadRec(&PR);
	WriteRec(&PR);
	Prev_sec=PR.header.ts_sec;
	Prev_usec=PR.header.ts_usec;

	while (ReadRec(&PR) > 0)
	{
                /// Apply (commulative) shift forward (and normalyze)
		PR.header.ts_usec += Delta_usec;
		PR.header.ts_sec  += Delta_sec;
		while (PR.header.ts_usec >= MICRO)
		{
			PR.header.ts_usec -= MICRO;
			PR.header.ts_sec++;
		}

		/// Get usecs ( = 1/1000000 sec) between this new Record and previous one
		Delta    = (PR.header.ts_sec  - Prev_sec ) * MICRO;
                Delta   += (PR.header.ts_usec - Prev_usec);

		///Bytes allowed in this Delta
		MaxBytes = (guint32)(round(Delta*MaxDelta));
		//Log(DEBUG,"Delta is %i, MaxDelta is %Lf, MaxBytes is %Li",Delta,MaxDelta,MaxBytes);

                /// Compare Bytes read and Max allowed
		if (MaxBytes < PR.header.incl_len)
		{
			/// Too much! delay packet time further
			Delta_usec += ((guint32)ceil(  (PR.header.incl_len-MaxBytes)/MaxDelta  ));
			while (Delta_usec >= MICRO)
			{
				Delta_usec -= MICRO;
				Delta_sec++;
			}
			//Log(DEBUG,"Delta_sec is %i, Delta_usec is %i",Delta_sec, Delta_usec);
			/// Apply another shift forward (and normalyze)
			PR.header.ts_usec += ((guint32)ceil(  (PR.header.incl_len-MaxBytes)/MaxDelta  ));
			while (PR.header.ts_usec >= MICRO)
			{
				PR.header.ts_usec -= MICRO;
				PR.header.ts_sec++;
			}
		}
                /// Remember values for next Packet
		Prev_sec=PR.header.ts_sec;
		Prev_usec=PR.header.ts_usec;
		WriteRec(&PR);

	}

}

void SetMaxPps()
{
	pcaprec_t PR;

	guint32 Delta 		= 0;
	guint32 Delta_sec	= 0;
	guint32 Delta_usec	= 0;
	guint32 Prev_sec	= 0;
	guint32 Prev_usec	= 0;
	guint32 MinUDelta 	= MICRO / ((guint32)round(Param));

	if (Param > MICRO)
	{
		Log(ERROR,"Rate is too high !!");
		return;
	}
        /// Number of microseconds between two subsequent packets
	//Log (DEBUG,"MinUDelta is %i",MinUDelta);

	CopyFileHeader();

        /// Get First Packet
	ReadRec(&PR);
	WriteRec(&PR);
	Prev_sec=PR.header.ts_sec;
	Prev_usec=PR.header.ts_usec;

	while (ReadRec(&PR) > 0)
	{
                /// Apply (cummulative) time shift of packets
		PR.header.ts_usec += Delta_usec;
		PR.header.ts_sec  += Delta_sec;
		while (PR.header.ts_usec >= MICRO)
		{
			PR.header.ts_usec -= MICRO;
			PR.header.ts_sec++;
		}

                /// Microseconds between this read packet and previous
		Delta  = (PR.header.ts_sec-Prev_sec)*MICRO;
		Delta += PR.header.ts_usec - Prev_usec;
		//Log(DEBUG,"Delta is %i, MinUDelta is %i",Delta,MinUDelta);

		if (Delta  < MinUDelta)
		{
			Delta_usec +=(MinUDelta-Delta+1);
			while (Delta_usec >= MICRO)
			{
				Delta_usec -= MICRO;
				Delta_sec++;
			}
			//Log(DEBUG,"Delta_sec is %i, Delta_usec is %i",Delta_sec, Delta_usec);
			PR.header.ts_usec += (MinUDelta-Delta+1);
			while (PR.header.ts_usec >= MICRO)
			{
				PR.header.ts_usec -= MICRO;
				PR.header.ts_sec++;
			}
		}
		Prev_sec  = PR.header.ts_sec;
		Prev_usec = PR.header.ts_usec;
		WriteRec(&PR);
	}
}

void SetMinPps()
{
	pcaprec_t PR;

	guint32 Delta_sec=0;
	guint32 Delta_usec=0;
	guint32 Prev_sec=0;
	guint32 Prev_usec=0;
	int 	NSP;

	if (Param > MICRO)
	{
		Log(ERROR,"Rate is too high !!");
		return;
	}

	CopyFileHeader();
	while (1)
	{
                /// How many packets in next second of load?
		NSP=(int)CountNextSecondPackets();
                //Log (DEBUG,"looping in SetMinPps, NSP=%i",NSP);
		if (NSP<0)
			/// EOF already reached, exit loop
			break;
		if (NSP > Param)
			for (;NSP;NSP--)
			{
				/// Write Packet as they are, shifting backwards
				ReadRec(&PR);
				PR.header.ts_sec -= Delta_sec;
				if (PR.header.ts_usec >= Delta_usec)
					PR.header.ts_usec -= Delta_usec;
				else
				{
					PR.header.ts_sec--;
					PR.header.ts_usec += (MICRO-Delta_usec);
				}
				WriteRec(&PR);
			}
		else
		{
                        /// Read First Packet of this burst, shift back, write
			ReadRec(&PR);
			PR.header.ts_sec  -= Delta_sec;
                        if (PR.header.ts_usec >= Delta_usec)
				PR.header.ts_usec -= Delta_usec;
			else
			{
				PR.header.ts_sec--;
				PR.header.ts_usec += (MICRO-Delta_usec);
			}

			WriteRec(&PR);
			/// Store beginning of burst
			Prev_sec=PR.header.ts_sec;
			Prev_usec=PR.header.ts_usec;
			for (int i=1;i<=NSP;i++)
			{
				/// Read, shift
				ReadRec(&PR);
				/// Compress
				PR.header.ts_sec = Prev_sec;
				PR.header.ts_usec = (guint32)(Prev_usec + MICRO*i/Param);
				while (PR.header.ts_usec >= MICRO)
				{
					PR.header.ts_usec -= MICRO;
					PR.header.ts_sec++;
				}
				/// Write
				WriteRec(&PR);
			}
			/// Update Shift
			Delta_usec += (guint32)(MICRO*(Param-NSP)/Param);
			while (Delta_usec >= MICRO)
			{
				Delta_usec -= MICRO;
				Delta_sec++;
			}
			//Log (DEBUG,"Delta_sec %i, Delta_usec %i", Delta_sec, Delta_usec);
		}
        }
}

void SetMinMbps()
{
	pcaprec_t PR;

	guint32 Delta_sec=0;
	guint32 Delta_usec=0;
	guint32 Prev_sec=0;
	guint32 Prev_usec=0;
	guint32 NSB=0;
	guint32	NSP=0;

        /// From MegaBits/sec to Bytes/sec
	unsigned long LParam = (unsigned long)(Param*1024*1024/8);

	CopyFileHeader();
	while (1)
	{
                /// How many Bytes in next second of load?
		NSB=(int)CountNextSecondBytes();
                Log (DEBUG,"looping in SetMinPps, NSB=%i, NSP=%i, LPar=%i",NSB,NSP,LParam);
		if (NSB<0)
			/// EOF already reached, exit loop
			break;
		if (NSB > LParam)
		{
			NSP=CountNextSecondPackets();
			for (;NSP;NSP--)
			{
				/// Write Packet as they are, shifting backwards
				ReadRec(&PR);
				PR.header.ts_sec -= Delta_sec;
				if (PR.header.ts_usec >= Delta_usec)
					PR.header.ts_usec -= Delta_usec;
				else
				{
					PR.header.ts_sec--;
					PR.header.ts_usec += (MICRO-Delta_usec);
				}
				WriteRec(&PR);
			}
		}
		else
		{
			NSP=CountNextSecondPackets();
                        /// Read First Packet of this burst, shift, write
			ReadRec(&PR);
			PR.header.ts_sec  -= Delta_sec;
                        if (PR.header.ts_usec >= Delta_usec)
				PR.header.ts_usec -= Delta_usec;
			else
			{
				PR.header.ts_sec--;
				PR.header.ts_usec += (MICRO-Delta_usec);
			}
			WriteRec(&PR);

			/// Store beginning of burst
			Prev_sec=PR.header.ts_sec;
			Prev_usec=PR.header.ts_usec;
			for (guint32 i=1;i<=NSP;i++)
			{
				/// Read, shift
				ReadRec(&PR);
				/// Compress
				PR.header.ts_sec = Prev_sec;
				PR.header.ts_usec = (guint32)(Prev_usec + MICRO*((float)i*NSB)/((float)LParam*NSP));
				while (PR.header.ts_usec >= MICRO)
				{
					PR.header.ts_usec -= MICRO;
					PR.header.ts_sec++;
				}
				/// Write
				WriteRec(&PR);
			}
			/// Update Shift
			Delta_usec += (guint32)(MICRO*(float)(LParam-NSB)/(float)LParam);
			while (Delta_usec >= MICRO)
			{
				Delta_usec -= MICRO;
				Delta_sec++;
			}
			Log (DEBUG,"Delta_sec %i, Delta_usec %i", Delta_sec, Delta_usec);
		}
        }
}

void ChangeIP()
{
	char 		*ip1=StrParam;
	char 		*ip0=strstr(StrParam,",");
	char 		*ip2=ip0+1;
	struct in_addr	dst;
	guint32 	Lip1,Lip2,Lip1H,Lip2H;
	pcaprec_t       Rec;
	int		match;
	int		RunLen=0;
	int		DeltaCks;
	guint16		cks;
	guint32		Checksum=0;

	*ip0 = 0;
	inet_aton(ip1, &dst);
	Lip1 = dst.s_addr;
	inet_aton(ip2, &dst);
	Lip2 = dst.s_addr;
        Log(DEBUG,"IPs are %x , %x",Lip1,Lip2);

	// Pre calculate part of the Checksum difference
	Lip1H=ntohl(Lip1);
	Lip2H=ntohl(Lip2);
	DeltaCks  = ( ((Lip2H>>16)&0xFFFF) + (Lip2H&0xFFFF) );
	DeltaCks -= ( ((Lip1H>>16)&0xFFFF) + (Lip1H&0xFFFF) );
	DeltaCks  = ( DeltaCks>>16 )       + (DeltaCks & 0xFFFF);
	//Log(DEBUG,"DeltaCks=%04x",DeltaCks);

	CopyFileHeader();
	while(ReadRec(&Rec)>0)
	{
                // Find Match (can it be both???)
		match=-1;

		if (memcmp(&Lip1,&Rec.body[ETHEADER+12],4)==0)	match=12;
		if (memcmp(&Lip1,&Rec.body[ETHEADER+16],4)==0)	match=16;
                if (match>=0)
		{
                        // lenth of IP Header
			guint32 IPHeader=(Rec.body[ETHEADER+0] & 0x0F)<<2;

			/// Change the address
			memcpy(&Rec.body[ETHEADER+match],&Lip2,4);

			/// FIX IP Header Checksum
			RunLen = IPHeader;
			bzero (&Rec.body[ETHEADER+10],2);
			Checksum=0;
			for (int i=0; i<RunLen; i+=2)
				Checksum+=(guint16)((Rec.body[ETHEADER+i]<<8)+Rec.body[ETHEADER+i+1]);
                        Checksum = (Checksum >> 16) + (Checksum & 0xFFFF);
			Checksum+= (Checksum >> 16);
			cks=(signed short)htons(~Checksum);
			memcpy(&Rec.body[ETHEADER+10],&cks,2);

			/// If TCP fix UDP/TCP Header Checksum too
			if ( (Rec.body[ETHEADER+9]==0x06) || (Rec.body[ETHEADER+9]==0x11) )
			{
				guint32 L4Header=ETHEADER+IPHeader;
				Checksum=0;

                                // Clear old checksum
				switch (Rec.body[ETHEADER+9])
				{
				case 0x06 :
					Checksum  = (Rec.body[L4Header+16]<<8)+Rec.body[L4Header+17];
					break;
				case 0x11 :
					Checksum  = (Rec.body[L4Header+6]<<8)+Rec.body[L4Header+7];
					break;
				}

                                /// Since I Know 'what' changed I also Know How that made the Chesum change
				/// so don't compute all bach, just the difference ;-)
				Checksum  = (~Checksum)&0xFFFF;
				Checksum += DeltaCks;
				//Log (DEBUG,"Chsum=%06x",Checksum);
				Checksum = (Checksum >> 16) + (Checksum & 0xFFFF);
				cks=(signed short)htons(~Checksum);

                                // Write Checksum
				switch (Rec.body[ETHEADER+9])
				{
				case 0x06 :
					memcpy(&Rec.body[L4Header+16],&cks,2);
					break;
				case 0x11 :
					memcpy(&Rec.body[L4Header+6],&cks,2);
					break;
				}
			} // TCP or UDP
		} // if (match)
		WriteRec(&Rec);
	} // while read
}


void ChangeMAC ()
{
	char 		*mac1=StrParam;
	char 		*mac0=strstr(StrParam,",");
	char 		*mac2=mac0+1;
        byte		Mac1[6],Mac2[6];
	pcaprec_t       Rec;
	int		match;

	*mac0 = 0;
	sscanf (mac2,"%02x:%02x:%02x:%02x:%02x:%02x",&Mac2[0],&Mac2[1],&Mac2[2],&Mac2[3],&Mac2[4],&Mac2[5]);
        sscanf (mac1,"%02x:%02x:%02x:%02x:%02x:%02x",&Mac1[0],&Mac1[1],&Mac1[2],&Mac1[3],&Mac1[4],&Mac1[5]);
	CopyFileHeader();
	while(ReadRec(&Rec)>0)
	{
		// Find Match (can it be both???)
		match=-1;
		if (memcmp(Mac1,&Rec.body[0],6)==0)	match=0;
		if (memcmp(Mac1,&Rec.body[6],6)==0)	match=6;
                if (match>=0)
		{
			// Repalce Address
			memcpy(&Rec.body[match],Mac2,6);
			// Log(DEBUG,"m=%i, M1=%012h, M2=%012h",match,Mac1,Mac2);
		}
		WriteRec(&Rec);
	} // while read

}

void ChangePort ()
{
	char *Port1=optarg;
	char *Port0=strstr(optarg,",");
	char *Port2=Port0+1;

	pcaprec_t       Rec;
	gint32		match;
	guint16		port1;
	guint16		port2;
	guint16		port;


	*Port0=0;
	port1=strtol(Port1,NULL,10);
	port2=strtol(Port2,NULL,10);

	CopyFileHeader();
	while(ReadRec(&Rec)>0)
	{
		guint32 IPHeader=(Rec.body[ETHEADER+0] & 0x0F)<<2;

		/// Find Match (can it be both???)
		match=-1;
		port=(Rec.body[ETHEADER+IPHeader]<<8) +Rec.body[ETHEADER+IPHeader+1];
		if (port==port1) match=ETHEADER+IPHeader;
		port=(Rec.body[ETHEADER+IPHeader+sizeof(guint16)]<<8) +Rec.body[ETHEADER+IPHeader+sizeof(guint16)+1];
		if (port==port1) match=ETHEADER+IPHeader+sizeof(guint16);
                if (match>=0)
		{
                        guint32 Checksum=0;
			guint16 cks=0;
			/// lenth of IP Header
			guint32 IPHeader=(Rec.body[ETHEADER+0] & 0x0F)<<2;
			guint32 TCPHeader= IPHeader+ETHEADER;
                        guint32 RunLen = (Rec.body[ETHEADER+2]<<8) + (Rec.body[ETHEADER+3]) - IPHeader;

			/// Repalce PORT
			Rec.body[match]	  = (port2 & 0xFF00)>>8;
			Rec.body[match+1] = (port2 & 0x00FF);

			/// Clear Previous Checksum    a Bytes
			bzero (&Rec.body[TCPHeader+16],2);
			/// Summ Header and Dat
			for (int i=0; i<RunLen; i+=2)
				Checksum+=(guint16)((Rec.body[TCPHeader+i]<<8)+Rec.body[TCPHeader+i+1]);
			/// Add PseudoHeader Fields
			Checksum+=(guint16) ((Rec.body[ETHEADER+12]<<8) + (Rec.body[ETHEADER+13]));
			Checksum+=(guint16) ((Rec.body[ETHEADER+14]<<8) + (Rec.body[ETHEADER+15]));
			Checksum+=(guint16) ((Rec.body[ETHEADER+16]<<8) + (Rec.body[ETHEADER+17]));
			Checksum+=(guint16) ((Rec.body[ETHEADER+18]<<8) + (Rec.body[ETHEADER+19]));
			Checksum+=RunLen;
			Checksum+=Rec.body[ETHEADER+9];

			/// Complement & Store
                        Checksum = (Checksum >> 16) + (Checksum & 0xFFFF);
			Checksum+= (Checksum >> 16);
			cks=(signed short)htons(~Checksum);
			memcpy(&Rec.body[TCPHeader+16],&cks,2);
		}
		WriteRec(&Rec);
	} // while read
}

/*******************************************************************************
	M A I N
*******************************************************************************/

int main (int argc, char **argv)
{
	int 		opt,firstrun=1;
	char		InFileName[BUFFSIZE];
	char		OutFileName[BUFFSIZE];
	char		TmpFileName[BUFFSIZE];
	char 		*endptr;
	pcaprec_t       mover;

        /// Check Parameters Number
	if (argc < 4)
	{
		Log(ERROR,"Not enought Parameters Provided");
		Usage();
	}

	/// Input/Output Files Operations
	bzero(InFileName,BUFFSIZE);
	bzero(OutFileName,BUFFSIZE);

	/// Check Command & Parameter
	while ((opt = getopt(argc, argv, "t:s:P:p:B:b:o:i:m:d:")) != -1)
	{
		Param=-1;
		switch (opt)
		{
		case 't':
		case 's':
		case 'B':
		case 'b':
		case 'P':
		case 'p':
			/// Param is numeric only for commands stated above
			Param = strtold(optarg, &endptr);
			if (endptr==optarg)
			{
				Log(ERROR,"No digits found in parameter!");
				Usage();
			}
			if ((errno == ERANGE && (Param == LONG_MAX || Param == LONG_MIN)) || (errno != 0 && Param == 0))
			{
				Log(ERROR,"Wrong parameter (value or type)");
				Usage();
			}
			/// Command Specific Parameter Checks
                        if (opt != 's')
				Param=(Param<0)?-1.0*Param:Param;

		case 'o':
			/// Other commands may allow a strign (non numeric) parameter
			if (opt == 'o')
				strcat(OutFileName,optarg);
			break;
		case 'i':
			/// Change IP address in IP Packets
			if (strstr(optarg,",")==NULL)
			{
				Log(ERROR,"Wrong parameter for IP parameter");
				Usage();
			}
			else
			{
				char *ip1=optarg;
				char *ip0=strstr(optarg,",");
				char *ip2=ip0+1;
				*ip0=0;
				if (CheckIPAddress(ip1)<0)
				{
					Log(ERROR,"Parameter %s is not a valid IPv4 address",ip1);
					Usage();
				}
				if (CheckIPAddress(ip2)<0)
				{
					Log(ERROR,"Parameter %s is not a valid IPv4 address",ip2);
					Usage();
				}
				*ip0=',';
				sprintf(StrParam,"%s",optarg);
			}
			break;
		case 'm':
			if (strstr(optarg,",")==NULL)
			{
				Log(ERROR,"Wrong parameter for MAC parameter");
				Usage();
			}
			else
			{
				char *mac1=optarg;
				char *mac0=strstr(optarg,",");
				char *mac2=mac0+1;
				*mac0=0;
				if (CheckMACAddress(mac1)<0)
				{
					Log(ERROR,"Parameter %s is not a valid MAC address",mac1);
					Usage();
				}
				if (CheckMACAddress(mac2)<0)
				{
					Log(ERROR,"Parameter %s is not a valid MAC address",mac2);
					Usage();
				}
				*mac0=',';
				sprintf(StrParam,"%s",optarg);
			}
			break;
		case 'd':
			if (strstr(optarg,",")==NULL)
			{
				Log(ERROR,"Wrong parameter for Port parameter");
				Usage();
			}
			else
			{
				char *port1=optarg;
				char *port0=strstr(optarg,",");
				char *port2=port0+1;
				gint32	port=-1;
				*port0=0;

				port=-1;port=strtol(port1,NULL,10);
				if (port<0 || port>0xFFFF)
				{
					Log(ERROR,"Parameter %s is not a valid Port address",port1);
					Usage();
				}
				port=-1;port=strtol(port2,NULL,10);
				if (port<0 || port>0xFFFF)
				{
					Log(ERROR,"Parameter %s is not a valid Port address",port2);
					Usage();
				}
				*port0=',';
				sprintf(StrParam,"%s",optarg);
			}
			break;
		default:
			Usage();
			break;
		} // switch (opt...1)
		Log(DEBUG,"Command: %c",opt);
                // No operation should be performed here
		if (opt == 'o') continue;


                /// Get / Open Input file
		if (firstrun)
			InFile=open(argv[argc-1],O_RDONLY);
		else
			InFile=open(TmpFileName,O_RDONLY);
		if (InFile==-1)
		{
			Log(ERROR,"Can't open IN file for reading");
			Usage();
		}
		Log(DEBUG,"IN file ok");


                /// Play with temp(out) file for operations
		memcpy(InFileName,TmpFileName,BUFFSIZE);
		bzero(TmpFileName,BUFFSIZE);
                sprintf(TmpFileName,"/tmp/modcap_tmp_XXXXXX");
		OutFile=mkstemp(TmpFileName);
		if (!strlen(TmpFileName))
		{
			Log(ERROR,"Can't create tmp file %s",TmpFileName);
			close(InFile);
			_exit(0);
		}
		//OutFile=open(TmpFileName,O_WRONLY|O_CREAT|O_TRUNC,S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
		if (OutFile==-1)
		{
			Log(ERROR,"Can't open file %s for writing",TmpFileName);
			close(InFile);
			_exit(0);
		}
		Log(DEBUG,"Tmp file %s ok",TmpFileName);


		/// Perform actual operation (if any)
		switch (opt)
		{
		case 't':
			SetTime();
			break;
		case 's':
			ShiftTime();
			break;
		case 'B':
			SetMaxMBps();
			break;
		case 'b':
			SetMinMbps();
			break;
		case 'P':
			SetMaxPps();
			break;
		case 'p':
			SetMinPps();
			break;
		case 'i':
			ChangeIP();
			break;
		case 'm':
			ChangeMAC();
			break;
		case 'd':
			ChangePort();
			break;
		}

		/// Close all involved files
		close (InFile);
		close (OutFile);
		sync();

		/// Delete "InputFile", but not on first loop
                if (!firstrun)
		{
			unlink (InFileName);
			bzero  (InFileName,BUFFSIZE);
		}

                /// Go on
		firstrun=0;

	} // while (getopt())


        /// Check/Make final Output file name
	if (OutFileName[0]==0)
	{
	        /// Creates new file name (appending a '_' to the name of the source file)
		strcat(OutFileName,argv[argc-1]);
		strcat(OutFileName,"_");
	}
	OutFile=open(OutFileName,O_WRONLY|O_CREAT|O_TRUNC,S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
	if (OutFile==-1)
	{
		Log(ERROR,"Can't open file %s for writing",OutFileName);
		close(InFile);
		_exit(0);
	}
	Log (DEBUG,"Using %s as output file name",OutFileName);

	/// Open Last TMP file for reading
	InFile=open(TmpFileName,O_RDONLY);
	/// Copy
	CopyFileHeader();
	while(ReadRec(&mover)>0)
	{
		WriteRec(&mover);
	}
	Log(DEBUG,"Out file ok");

	/// Finally remove last TMP file
	unlink (TmpFileName);
}
