/*
 * Mausezahn - A fast versatile traffic generator
 * Copyright (C) 2008,2009 Herbert Haas
 * 
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License version 2 as published by the 
 * Free Software Foundation.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT 
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 
 * details.
 * 
 * You should have received a copy of the GNU General Public License along with 
 * this program; if not, see http://www.gnu.org/licenses/gpl-2.0.html
 * 
*/


#include "mz.h"
#include "mops.h"

// Inserts value in 'flag' (up to 7 bits are useful) into the target
// with an optional left-shift. For example if flag contains a 4-bit value
// and should be placed within the target in bit positions 3-6 like:
// 
//   7  6  5  4  3  2  1  0   
// +--+--+--+--+--+--+--+--+
// |  |  FLAGS    |  |  |  |
// +--+--+--+--+--+--+--+--+
// 
// then simply call: 
// 
//    (void)  mops_flags ( &target, &flag, 3 );
// 
// Note that shift=0 means no shift. 
inline void mops_flags (u_int8_t *target, u_int8_t *flag, int shift)
{
   *target |= (*flag << shift);
}



inline void mops_hton2 (u_int16_t *host16, u_int8_t *net16)
{
   char *x;
   
   x = (char*) host16;
   
   *(net16++) = *(x+1);
   *net16 = *x;
}


inline void mops_hton4 (u_int32_t *host32, u_int8_t *net32)
{
   char *x;
   
   x = (char*) host32;
   
   *(net32++) = *(x+3);
   *(net32++) = *(x+2);
   *(net32++) = *(x+1);
   *(net32) = *x;
}




// returns new counter index for given packet
// or -1 if all counters used already
int mops_get_counter (struct mops *mp)
{
   int i=0;
   
   while (mp->counter[i].offset)
     {
	i++;
	if (i==MAX_MOPS_COUNTERS_PER_PACKET) // exceeded range
	  return -1;
     }
   return i;
}


// Adds single byte to msg
int mops_msg_add_byte (struct mops *mp, u_int8_t data)
{
   mp->msg[mp->msg_s++] = data;
   return 0;
}


// Adds bit field in *previous* msg-byte using optional left-shift
int mops_msg_add_field (struct mops *mp, u_int8_t data, int shift)
{
   mp->msg[mp->msg_s -1]  |= (data << shift);   
   return 0;
}


// Adds two bytes in network byte order to msg
int mops_msg_add_2bytes (struct mops *mp, u_int16_t data)
{
   char *x;
   x = (char*) &data;
   mp->msg[mp->msg_s++] = *(x+1);
   mp->msg[mp->msg_s++] = *(x);
   return 0;
}


// Adds four bytes in network byte order to msg
int mops_msg_add_4bytes (struct mops *mp, u_int32_t data)
{
   char *x;
   x = (char*) &data;
   mp->msg[mp->msg_s++] = *(x+3);
   mp->msg[mp->msg_s++] = *(x+2);
   mp->msg[mp->msg_s++] = *(x+1);
   mp->msg[mp->msg_s++] = *(x);
   return 0;
}

// Adds string of bytes with lenght len 
int mops_msg_add_string (struct mops *mp, u_int8_t *str, int len)
{
   memcpy((void *) &mp->msg[mp->msg_s], (void *) str, len);
   mp->msg_s += len;
   
   return 0;
}



// Add counter to message
int mops_msg_add_counter (struct mops *mp,
			  int         random,  // 1=random, 0=use start/stop/step
			  u_int32_t   start,   // HOST BYTE ORDER
			  u_int32_t   stop,    // HOST BYTE ORDER
			  u_int32_t   step,    // HOST BYTE ORDER
			  int         bytes   // number of bytes used (1|2|4) - selects hton2 or hton4
			  )
{
   
   int i;
   
   // check if unsupported byte count
   if ( (bytes!=1) &&
	(bytes!=2) &&
	(bytes!=4) ) 
     return 1;
   
   // get new counter 
   i = mops_get_counter(mp);
   if (i==-1) return 1;
   
   // configure counter values
   mp->counter[i].offset = mp->msg_s;
   mp->counter[i].random = random;
   mp->counter[i].start  = start;
   mp->counter[i].stop   = stop;
   mp->counter[i].step   = step;
   mp->counter[i].bytes  = bytes;
   mp->counter[i].cur    = start;
   mp->counter[i].use    = 1;
   
   
   // configure first pointer value
   switch (bytes)
     {
      case 1:
	mops_msg_add_byte(mp, (u_int8_t) start);
	break;
      case 2:
	mops_msg_add_2bytes(mp, (u_int16_t) start);
	break;
      case 4:
	mops_msg_add_4bytes(mp, start);
	break;
      default: // never be reached
	return 1;
     }
   
   return 0;
}



// Compares two IP addresses byte by byte
// returns 0 if identical, 1 if different
// 
// Note that this works independent of endianess
// as long as both addresses have same endianess.
// 
int compare_ip (u_int8_t *ip1, u_int8_t *ip2)
{
   if (*ip1 != *ip2) return 1; 
   if (*(ip1+1) != *(ip2+1)) return 1; 
   if (*(ip1+2) != *(ip2+2)) return 1; 
   if (*(ip1+3) != *(ip2+3)) return 1; 
   
   return 0;
}


// Compares two MAC addresses byte by byte
// returns 0 if identical, 1 if different
int compare_mac (u_int8_t *mac1, u_int8_t *mac2)
{
   if (*mac1 != *mac2) return 1; 
   if (*(mac1+1) != *(mac2+1)) return 1; 
   if (*(mac1+2) != *(mac2+2)) return 1; 
   if (*(mac1+3) != *(mac2+3)) return 1; 
   if (*(mac1+4) != *(mac2+4)) return 1; 
   if (*(mac1+5) != *(mac2+5)) return 1; 

   return 0;
}


// Converts a 'struct timespec' value into a human readable string
// This stringt is written into 'str' which must be at least a 32 byte
// array.
int timespec2str(struct timespec *t, char *str)
{
	unsigned int d=0, h, m, s;
	
	// zero delay
	if ((t->tv_sec==0) && (t->tv_nsec==0)) {
		sprintf(str, "(none)");
		return 0;
	}

	h = t->tv_sec/3600;
	m = (t->tv_sec - h*3600)/60;
	s = t->tv_sec - h*3600 - m*60;
	
	if (h>24) {
		d = h/24;
		h = h - d*24;
		sprintf(str, "%u days %02u:%02u:%02u", d, h, m, s);
		return 0;
	}
	
	if (h|m) 
		sprintf(str, "%02u:%02u:%02u", h, m, s); // ignore nanoseconds if delay is in order of hours
	else if (s)
		sprintf(str, "%u%s sec", s, (t->tv_nsec>1000000) ? "+" : "");
	else if (t->tv_nsec>1000000) 
		sprintf(str, "%u msec", (unsigned int) t->tv_nsec/1000000);
	else if (t->tv_nsec>1000)
		sprintf(str, "%u usec", (unsigned int) t->tv_nsec/1000);
	else
	       	sprintf(str, "%lu nsec", t->tv_nsec);

	return 0;
}