
#define MULTICAST

#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h>

#ifdef LINUX
#include <linux/socket.h>
#else
#include<socket.h>
#endif
#include <net/if.h>
//#include <sys/ioctl.h>
#include <netinet/in.h>

#ifdef LYNXOS
#include <bsd/in.h>
#include <arpa/inet.h>
#ifdef vmpc
#include  <cetiatimer.h>
#endif //vmpc
#endif//LYNXOS

#include <sys/time.h>
unsigned int tvsub(struct timeval * t1, struct timeval * t0);
int read_timeo(int fd, int usec);
main(int argc,char ** argv )
{
#ifdef LYNXOS
  const unsigned char TTL=64;
#else
  const int TTL=64;
#endif
  char buffer[4096];
  char str[4096];
#ifdef LYNXOS
 u_char v_off=0, v_on=1;
#else
 const int v_off=0, v_on=1;
#endif
  struct sockaddr_in to;
  struct sockaddr_in from;
  struct ip_mreq multiaddr;
  int m, lost=0, recv=0;
  float through, msgsec;
  long addr;
  u_long deltaT;
  struct in_addr in;
  struct timeval start, stop, g_start, g_stop;   
  char c_addr[16];
  int sock = socket(AF_INET, SOCK_DGRAM, 0);
  int sock_rcv = socket(AF_INET, SOCK_DGRAM, 0);
  u_int iter = atoi(argv[2]);
  u_int mean, min,max;
#ifdef vmpc
    FILE *fdt;
    int fd;
    struct itimerval interval;
#endif
  if (argc < 4) {
    printf("Usage: %s <Multicast IP address> <num of iterations> <packet len>\n", argv[0]);
    exit(0);	
  } /* end if */

#ifdef LYNXOS
   addr=inet_addr( argv[1]);
   in.s_addr = addr;
   multiaddr.imr_multiaddr.s_addr =htonl(addr);
#else
  inet_pton(AF_INET, argv[1], &addr);
  multiaddr.imr_multiaddr.s_addr =addr;
#endif
#ifdef vmpc  
    if ((fd=open("/dev/tim0",O_RDWR)) < 0) {
           printf("Open failed0\n");
            return(-1);
    }
//    fdt = fopen("./testtimer.txt","w"); 
#endif

  multiaddr.imr_interface.s_addr = htonl(INADDR_ANY);
  if(setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &multiaddr, 
	     sizeof(struct ip_mreq)) < 0) 
    perror("IP_ADD_MEMBERSHIP: ");
  if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, &TTL, sizeof(TTL)) < 0)
     perror("IP_MULTICAST_TTL ");

  if(setsockopt(sock_rcv, IPPROTO_IP, IP_ADD_MEMBERSHIP, &multiaddr, 
	     sizeof(struct ip_mreq)) < 0) 
    perror("IP_ADD_MEMBERSHIP: ");
  if (setsockopt(sock_rcv, IPPROTO_IP, IP_MULTICAST_TTL, &TTL, sizeof(TTL)) < 0)
     perror("IP_MULTICAST_TTL ");

  to.sin_family=AF_INET;
#ifdef LYNXOS
  to.sin_addr.s_addr=htonl(addr);
#else
  to.sin_addr.s_addr=addr;
#endif
  to.sin_port=htons(12345);
#ifdef LYNXOS
  sprintf(c_addr, inet_ntoa(in));
#else
  inet_ntop(AF_INET, &to.sin_addr.s_addr, &c_addr, 16);
#endif

  if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP, &v_off,
		 sizeof(v_off)) < 0)
     perror("IP_MULTICAST_LOOP sock");
  if (setsockopt(sock_rcv, IPPROTO_IP, IP_MULTICAST_LOOP, &v_off,
		 sizeof(v_off)) < 0)
     perror("IP_MULTICAST_LOOP sock_rcv");

  memcpy(&from, &to, sizeof(to));
#ifdef LYNXOS
  if(bind(sock_rcv, (struct sockaddr *)&from, sizeof(from)) <0)
#else
  if(bind(sock_rcv, &from, sizeof(from)) <0)
#endif
     perror("bind failed");

  mean = 0;
  min = 100000000;
  max = 0;
#ifdef vmpc
  interval.it_value.tv_sec = 0;
  interval.it_value.tv_usec = 500;
  interval.it_interval.tv_sec = 0;
  interval.it_interval.tv_usec = 500;

  ioctl(fd,TIMER_INIT,&interval); 
#endif

  gettimeofday(&g_start, 0);
  for (m =0; m < iter; m++)
    {

      int len = sizeof(from);
      u_int deltaT;
      buffer[0]=m;
      buffer[1] = 'r';
      gettimeofday(&start, 0);
      if(sendto(sock, buffer, atoi(argv[3]), 0, 
		(const struct sockaddr *)&to, sizeof(to))
	 == -1)
	perror("couldn't send");
      
      //      printf("sending %d to %s\n",buffer[0], c_addr);
      //      sleep(1);
      if(read_timeo(sock_rcv, 5000) == 0)
	{
	  printf(".");
	  fflush(stdout);
	  lost++;
	  m--;
	}
      else
	{
#ifdef LYNXOS
	  if(recvfrom(sock_rcv, str, 4096, 0, (struct sockaddr *)&from,
		      &len) <0)
#else
	    if(recvfrom(sock_rcv, str, 4096, 0, (struct sockaddr_in *)&from,
		  &len) <0)
#endif
	      perror("receivefrom");
	 
	  }
      recv++;
      gettimeofday(&stop, 0);
#ifdef vmpc
       ioctl(fd,TIMER_WAIT);
//       fprintf(fdt, "%lu %lu\n", end.tv_sec, end.tv_usec); fprintf(fdt, "%lu %lu\n", end.tv_sec, end.tv_usec);
#endif        

      deltaT=tvsub(&stop,&start);
      mean+=deltaT;
      deltaT < min ? min = deltaT : min;
      deltaT > max ? max= deltaT : max;
      //     printf("received %d from %x\n",str[0], from.sin_addr.s_addr);
	}
      gettimeofday(&g_stop, 0);
  
      mean = mean /(iter+lost);
      deltaT=tvsub(&g_stop, &g_start);
      msgsec =((float)(iter+lost)+(float)(recv))*1000000./deltaT;
      through = (msgsec * atoi(argv[3]))/1000000.;
      printf(" Lost %d packets\n", lost);
      printf(" Avg. %d min %d max %d (usec)\n", mean, min, max);
      printf(" Total %d (usec) for %d msgs\n", 
	     tvsub(&g_stop, &g_start), iter+lost);
      printf("Throughput: %f (msg/s) i.e. %f (MB/s)\n", 
	     msgsec, through);
    
  close(sock);
  close(sock_rcv);
#ifdef vmpc  
  close(fd);
//  fclose(fdt);
#endif

}

unsigned int tvsub(struct timeval * t1, 
                   struct timeval * t0)
{
  unsigned int res;
  unsigned int aux;
  
  aux = t1->tv_sec - t0->tv_sec;
  if (aux > 0) {
  /* res = (aux - 1) * 1000000 + t1->tv_usec + 1000000 - t0->tv_usec; */
    res = aux * 1000000 + t1->tv_usec - t0->tv_usec;
  }
  else {
    res = t1->tv_usec - t0->tv_usec;
  } /* end if */	
  
  return(res);              
} /* end of tvsub */


int read_timeo(int fd, int usec)
{
  fd_set rset;
  struct timeval timeo;
  FD_ZERO(&rset);
  FD_SET(fd, &rset);
  timeo.tv_sec = 0;
  timeo.tv_usec = usec;
  return(select(fd+1, &rset, NULL, NULL, &timeo));
} 
