/*
 * pathchirp_snd.c
 * Copyright (c) 2003 Rice University
 * All Rights Reserved.
 *
 * Permission to use, copy, modify, distribute, and sell this software
 * and its documentation is hereby granted without
 * fee, provided that the above copyright notice appear in all copies
 * and that both that copyright notice and this permission notice
 * appear in supporting documentation, and that the name of Rice
 * University not be used in advertising or publicity pertaining to
 * distribution of the software without specific, written prior
 * permission.  Rice University makes no representations about the
 * suitability of this software for any purpose.  It is provided "as
 * is" without express or implied warranty.  
 *
 * RICE UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS, IN NO EVENT SHALL RICE UNIVERSITY BE LIABLE FOR ANY
 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 *
 * Author: Vinay Ribeiro, vinay@rice.edu.  */

/*
 * "pathchirp_snd" sends chirp packet trains to "pathchirp_rcv" based
 * on specified parameters.
 *
 *  pathchirp_snd.c is based on udpsend.c of the NetDyn tool.
 * */


/*
 * udpsend.c
 * Copyright (c) 1991 University of Maryland
 * All Rights Reserved.
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of U.M. not be used in advertising or
 * publicity pertaining to distribution of the software without specific,
 * written prior permission.  U.M. makes no representations about the
 * suitability of this software for any purpose.  It is provided "as is"
 * without express or implied warranty.
 *
 * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 * Author:  Dheeraj Sanghi, Department of Computer Science.
 */


#include "../config.h"
#include <string.h>
#include <strings.h>
#include <stdio.h>
#include <netdb.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <sys/param.h>
#include <sys/time.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <sched.h>
#include "delay.h"
#include <unistd.h>

#include "pathchirp.h"
#include "realtime.h"

#define PKTSIZE		1000		/* default packet size */
#define NPKT		1000		/* default number of packets */
#define MAXHNAME	100		/* maximum hostname size */

int dstPort=RECVPORT,tcpPort=READPORT;

/*### The following definition was not made on BSDish systems */
#ifndef HAVE_IN_ADDR_T
  typedef unsigned int    in_addr_t;
#endif

/*
 * Return the address (in network byte order) of the requested host.
 *
 * We return 0 if we can't find anything.
 */

in_addr_t gethostaddr(name)
     char *name;
{
   in_addr_t addr;
   register struct	hostent *hp;

   if ((addr = (in_addr_t)inet_addr (name)) != -1)
      return (addr);

   hp = gethostbyname(name);
   if (hp != NULL)
      return (*(in_addr_t *)hp->h_addr);
   else
      return (0);
}

/* Usage information */

int usage()
{
   (void) fprintf (stderr, "usage: pathchirp_snd [-h] <more options>\n");
   (void) fprintf (stderr, "The options are: \n");
   (void) fprintf (stderr, "\t -l \t lowest rate (Mbps) to probe at within chirp, default=1Mbps\n");
   (void) fprintf (stderr, "\t -u \t highest rate (Mbps) to probe at within chirp, default=120Mbps\n");
   (void) fprintf (stderr, "\t -d \t destination host\n");
   (void) fprintf (stderr, "\t -p \t packet size,default=1000 bytes\n");
   (void) fprintf (stderr, "\t -t \t duration of experiment(sec), default=600 sec \n");
   (void) fprintf (stderr, "\t -s \t spread factor: ratio of consecutive inter-arrivals within a chirp, default=1.2 \n");
   (void) fprintf (stderr, "\t -a \t average probing rate (Mbps), default=0.3Mbps \n");
   (void) fprintf (stderr, "\t -T <receiver port (TCP setup), default=%d>\n",READPORT);
   (void) fprintf (stderr, "\t -U <receiver port (chirp UDP), default=%d>\n",RECVPORT);
   (void) fprintf (stderr, "Single letter options are: \n");
   (void) fprintf (stderr, "\t -h \tHelp: produces this output\n");
   (void) exit (1);
}



/* main function  */

int main(argc, argv)
     int	argc;
     char	*argv[];
{
   struct	sockaddr_in dst;	/* destination internet address */
   struct	sockaddr_in dst_tcp;	/* destination internet address */
   struct	timeval	*tp, tp1;		/* used for getting timeofday */

   struct in_addr dst_addr;
   int	so;			/* socket to remote host */
   int cc;
   int	sotcp;			/* socket number for tcp conn. for parameter transfer */
   char	data[MAXMESG];		/* maximum size data packet */
   char	data_tcp[MAXMESG];		/* maximum size data packet */
    int	dsthost = NO;		/* is final destination given ? */
    double tcp_var; /* used in forming tcp packet */

   int	pktsize = PKTSIZE;	/* packet size */
   int	npkt = NPKT;		/* number of packets */
   int	ntrain = 1;		/* number of packet trains */
   int	i;

   double	gap = 0;		/* gap in us between 2 trains */
   double	sleeptime = 0;	/* sleep in us between 2 bulks */

   double   largest_inter_arrival=0;/* min/largest inter arrival in the chirp in us*/
   double   low_rate=1.0;/*lowest rate (Mbps) to probe at*/
   double high_rate=120.0;/*highest rate (Mbps) to probe at */
   double avg_rate=0.3;/*avg. rate (Mbps) to probe at */
   double duration=600.0;/* time in seconds for which to probe at */
   double chirp_duration=0.0;/* duration of a chirp */
   double spread_factor=1.2; /* decrease in spread of packets within the chirp*/
   int	pktsend = 0;		/* number of packets sent so far */
   int	write_error = NO;	/* error on sending a packet */
   char	hostname[MAXHNAME];	/* echo/final destination hostname */
   int	nc;			/* index variable for packet-count */
   int	nt;			/* index variable for chirp-count */

   char	*ptr;			/* pointer to options */
   struct	udprecord *udprecord;	/* udp packet content */
   struct	tcprecord *tcprecord;	/* tcp packet content */


   /* allocate space for local timestamp */
   tp = (struct timeval *)(malloc (sizeof (struct timeval)));

   argv++; argc--;

   /* option processing */
   while (argc > 0) {
     ptr = *argv;
     while (*ptr) switch (*ptr++) {
       
     case 'l':/* lowest rate to probe at */
       if (*ptr == 0) {
	 argc--; argv++;
	 if (*argv == 0) {
	   (void) fprintf (stderr,
			   "pathchirp_snd: no lowest rate '-l'.\n");
	   (void) exit (1);
	 }
	 low_rate = atof (*argv);
       }
       else {
	 low_rate = atof (*argv);
	 *ptr = 0;
       }
       fprintf(stderr,"low rate is %f \n",low_rate);
       break;
       
     case 'u':/* highest rate to probe at */
       if (*ptr == 0) {
	 argc--; argv++;
	 if (*argv == 0) {
	   (void) fprintf (stderr,
			   "pathchirp_snd: no highest rate '-u'.\n");
	   (void) exit (1);
	 }
	 high_rate = atof (*argv);
       }
       else {
	 high_rate = atof (*argv);
	 *ptr = 0;
       }
       fprintf(stderr,"high rate is %f \n",high_rate);
       break;
       
       
     case 'a':
       if (*ptr == 0) {
	 argc--; argv++;
	 if (*argv == 0) {
	   (void) fprintf (stderr,
			   "pathchirp_snd: no average rate '-a'.\n");
	   (void) exit (1);
	 }
	 avg_rate = atof (*argv);
       }
       else {
	 avg_rate = atof (*argv);
	 *ptr = 0;
       }
       fprintf(stderr,"avg_rate is %f \n",avg_rate);

       break;
       
     case 't':
       if (*ptr == 0) {
	      argc--; argv++;
	      if (*argv == 0) {
		(void) fprintf (stderr,
				"pathchirp_snd: no duration '-t'.\n");
		(void) exit (1);
	      }
	      duration = atof (*argv);
       }
       else {
	 duration = atof (*argv);
	 *ptr = 0;
       }

       break;
       
       
     case 's':
       if (*ptr == 0) {
	 argc--; argv++;
	 if (*argv == 0) {
	   (void) fprintf (stderr,
			   "pathchirp_snd: no spread_factor '-t'.\n");
	   (void) exit (1);
	 }
	 spread_factor = atof (*argv);
       }
       else {
	 spread_factor = atof (*argv);
	 *ptr = 0;
       }
       fprintf(stderr,"spread  is %f \n",spread_factor);

       break;
       
       
     case '-':
       break;
       
     case 'd':
     case 'D':
       dsthost = YES;	/* final host is given */
       if (*ptr == 0) {
	 argc--; argv++;
	 if (*argv == 0) {
	   (void) fprintf (stderr,
			   "pathchirp_snd: no destination host given with '-d|m'.\n");
	   (void) exit (1);
	 }
	 (void) strcpy (hostname, *argv);
       }
       else {
	 (void) strcpy (hostname, ptr);
	 *ptr = 0;
       }
       fprintf(stderr,"Destination host is:  %s \n",hostname);
       break;
              
 case 'T':		/* port */
       if( *ptr == 0) {
	 argc--; argv++;
	 if (*argv == 0) {
	   (void) fprintf (stderr,
			   "pathchirp_rcv: no port number given with '-T'.\n");
	   (void) exit (1);
	 }
	 tcpPort = atoi (*argv);
       }
       else {
	 tcpPort = atoi (ptr);
	 *ptr = 0;
       }
       break;
       

 case 'U':		/* port */
       if( *ptr == 0) {
	 argc--; argv++;
	 if (*argv == 0) {
	   (void) fprintf (stderr,
			   "pathchirp_rcv: no port number given with '-U'.\n");
	   (void) exit (1);
	 }
	 dstPort = atoi (*argv);
       }
       else {
	 dstPort = atoi (ptr);
	 *ptr = 0;
       }
       break;
       
 
     case 'h':		/* help */
     case 'H':
       usage();
       
       
     case 'p':		/* packet size */
     case 'P':
       if (*ptr == 0) {
	 argc--; argv++;
	 if (*argv == 0) {
	   (void) fprintf (stderr,
			   "pathchirp_snd: no packet size given with '-p'.\n");
	   (void) exit (1);
	 }
	 pktsize = atoi (*argv);
       }
       else {
	 pktsize = atoi (*argv);
	 *ptr = 0;
       }
       break;
       
       
     default:
       (void) fprintf (stderr,
		       "pathchirp_snd: Unknown option '%c'\n", ptr[-1]);
       (void) exit (1);
       
     }
     argc--; argv++;
   }

   /* checking for negative rates */
   if (high_rate<low_rate || low_rate<=0 || avg_rate<=0)
     {fprintf(stderr,"pathchirp_snd: rates invalid\n");
	 (void) exit (1);
     }

   /* checking for negative duration*/
   if (duration<=0)
     {perror ("pathchirp_snd: duration invalid");
	 (void) exit (1);
     }
   

   /* if no destination host given,
    * let the local host be the final destination */
   if (!dsthost)
     {fprintf(stderr,"No destination host given\n");
	 (void) exit (1);
     }

     /*
     Setting number of packet, packet interarrival times etc.
    */


       largest_inter_arrival=((double)pktsize)*8/high_rate;/*actually smallest time in us*/
       npkt=2;
       chirp_duration=largest_inter_arrival;
       while (largest_inter_arrival<((double)pktsize)*8/low_rate)
	 {
	   npkt++;
	   largest_inter_arrival*=spread_factor;
	   chirp_duration+=largest_inter_arrival;
	 }
       
       
       gap = ((double)(8*npkt*pktsize)/avg_rate)-chirp_duration;
       if (gap<0) gap=0.0;
       
       ntrain=(int)(1000000*duration/(chirp_duration+gap));
       if (ntrain<1) ntrain=1;
       


   /* set the destination as far as sender is concerned
    * i.e. echo host or the final destination host */
     dst_addr.s_addr = gethostaddr(hostname);
   if (dst_addr.s_addr == 0) {
      (void) fprintf (stderr, "pathchirp_snd: %s: unknown host\n",
		      hostname);
      (void) exit (1);
   }


   /* create a socket to send packets */
   so = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP);
   if (so < 0) {
      perror ("pathchirp_snd: socket");
      (void) exit (1);
   }

   /************ TCP **************/

 /*SEND PARAMETER INFORMATION TO DESTINATION VIA TCP*/
/* create a socket to send result packets by TCP */
   tcprecord = (struct tcprecord *) data_tcp;
   bzero((char *) tcprecord, sizeof (struct tcprecord));
   tcprecord->num_interarrival=htonl ((long) npkt-1);

   tcp_var=chirp_duration+gap;
   tcprecord->inter_chirp_time=htonl ((long) tcp_var);/* in us*/
   tcp_var=low_rate*10000.0;
   tcprecord->low_rate=htonl ((long) tcp_var);
   tcp_var=high_rate*10000.0;
   tcprecord->high_rate=htonl ((long) tcp_var);
   tcprecord->spread_factor=htonl ((long) (spread_factor*10000.0));
   tcprecord->pktsize=htonl ((long ) pktsize);
   gethostname(tcprecord->localhost,sizeof(tcprecord->localhost));


   sotcp = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
   if (sotcp < 0) {
      perror("pathchirp_snd: tcp socket");
      exit(1);
   }

  /* initialize the socket address for outgoing connection */
   bzero ((char *)&dst_tcp, sizeof(dst_tcp));
   dst_tcp.sin_family = AF_INET;
   dst_tcp.sin_port = htons((u_short) tcpPort);

 if ( (dst_tcp.sin_addr.s_addr = gethostaddr(hostname)) == 0 ) {
	 (void) fprintf (stderr, "pathchirp_snd: %s: unknown host\n",
			 hostname);
	 (void) exit (1);
      }

   /* connect to the reader process */
   if (connect(sotcp, (struct sockaddr *)&dst_tcp, sizeof(dst_tcp)) < 0) {
      perror("pathchirp_snd: connect");
      exit (1);
   }


 /* send just the log record to the reader */
      cc = write (sotcp, (char *) tcprecord,
		  sizeof (struct tcprecord));

 if (cc < 0) {
	 perror("pathchirp_snd: tcp write");
	 exit(1);
      }

 (void) close (sotcp);
 fprintf(stderr,"Finished TCP parameter transfer, starting to send chirps \n");
 usleep(10000000);   /*delay before sending out UDP packets*/

 /*********************** end tcp *******************************/

   /* initialize address/port number */
   bzero ((char *) &dst, sizeof (dst));
   dst.sin_family = AF_INET;

   dst.sin_addr = dst_addr;

   dst.sin_port = htons ((u_short) dstPort);

   if (connect (so, (struct sockaddr *) &dst, sizeof (dst)) < 0) {
      perror ("pathchirp_snd: could not connect to the receiver");
      (void) exit (1);
   }

   lockMe();

   /* initial part of data is actually the log record */
   udprecord = (struct udprecord *) data;
   bzero((char *) udprecord, sizeof (struct udprecord));

   fprintf(stderr,"Chirp Number 0,");
   for (nt = 1; nt <= ntrain; nt++) {
     
     if (nt%100 ==0) fprintf(stderr," %d,",nt);


     /********* BEGIN CHIRP ********/
      sleeptime = largest_inter_arrival*spread_factor;
      /* Hack because sleeptime gets divided before the first sleep period*/
      (void) gettimeofday (&tp1, (struct timezone *) 0);
      tp1.tv_usec-=100000;  

/* Hack so smartwait doesn't wait on 1st packet Be careful, if you add
        too much, tv_usec will wrap around and result in a positive
        time difference */

      for (nc = 1; nc <= npkt; nc++) {
	static int cc;
	
	smartwait((unsigned)(sleeptime-.5), &tp1);

	/* Wait for smartwait-1+0.5
	   The -1 is to account for gettimeofday, the +0.5 is for rounding*/

	udprecord->snd.tv_sec = htonl ((u_long) tp1.tv_sec);
	udprecord->snd.tv_usec = htonl ((u_long) tp1.tv_usec);
	udprecord->num = htonl ((u_long) (pktsend+1));

	/* Send packet out*/

	cc = write (so, (char *)udprecord, (unsigned int)pktsize);

	/* Prepare for a shorter sleep next time */
	sleeptime = sleeptime/spread_factor;
	
	if (cc < 0) {
	  (void) fprintf (stderr,
			  "Packet number %d\n", nc);
	  perror("pathchirp_snd: write");
	  write_error = YES;
	  break;
	}
	pktsend++;		/* number of packets sent */
	
	if (write_error) break;
	
      }
   /********* END CHIRP ********/
      
      /* gap between two successive packet trains */
      if (gap>=0) 
	{
	  (void) gettimeofday (&tp1, (struct timezone *) 0);
	  smartwait((unsigned)gap, &tp1);
	}
      
   }

   /*
    * Send 5 packets with packet number to be 0. The receiver would
    * stop receiving packets and exit after it sees the first
    * packet with packet number 0. Other packets would be just
    * ignored.
    */
   
      for (i = 1; i <= 10; i++) {
	 int	cc;
	 udprecord->num = 0;
	 cc = write (so, data, pktsize);
	 if (cc < 0) break;
	 if (sleeptime < 10000) sleeptime = 10000;
	 usleep (sleeptime);
      }
      fprintf(stderr,"\nFinished sending chirps\n");   

   (void) close (so);

   return(0);
}
