/* ttunnel.c test tunnel Laurent GUERBY 20160411 - GNU GPLv3 */

#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <string.h>
#include <linux/if.h>
#include <linux/if_tun.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/uio.h>
#include <time.h>

#define IOV_N 128
#define MSG_LEN 10000
#define SERVER_MODE 0
#define CLIENT_MODE 1

#define GIGA 1000000000

void main(int argc, char **argv) {
  int port;
  int tun_fd;
  int sock;
  int err;
  struct sockaddr_in server_addr;
  struct sockaddr_in src_addr;
  struct sockaddr_in dest_addr;
  fd_set rfds0;
  fd_set rfds;
  struct timespec ts,ts1,ts2,dt,dtr;
  int optval;
  int i;
  ssize_t count;
  ssize_t send_count;
  char msgbuf[MSG_LEN];
  socklen_t socklen;
  int mode = SERVER_MODE;
  int server_has_client = 0;
  int prev_send_count = 0;
  char *ip_addr_s;
  int msglen;
  double speed;
  long speed_msg_nano,max_nsec;
  int npkt,pkt_i;
  
  setbuf(stdout, NULL);

  if(argc<7) exit (1);

  printf("argc %d mode=%s ip=%s port=%s len=%s speed=%s npkt=%s\n", argc,argv[1],argv[2],argv[3],argv[4],argv[5],argv[6]);

  mode = SERVER_MODE;
  if(strncmp(argv[1],"client",6)==0) mode = CLIENT_MODE;
  ip_addr_s=argv[2];
  port=atoi(argv[3]);
  msglen=atoi(argv[4]);
  speed=atof(argv[5]);
  speed_msg_nano=(int)(double)((8.0e9/speed)*(20.0 + 8.0 + (double)msglen + 38.0));
  printf("speed_msg_nano = %ld\n",speed_msg_nano);
  npkt=atoi(argv[6]);

  for(i=0;i<MSG_LEN;i++) msgbuf[i]=i;

  sock = socket(AF_INET, SOCK_DGRAM, 0);
  if(sock<0) exit(1);

  optval = 1;
  setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
	     (const void *)&optval , sizeof(int));
  
  memset(&server_addr,0,sizeof(struct sockaddr_in));
  server_addr.sin_family = AF_INET;
  server_addr.sin_addr.s_addr = htonl(INADDR_ANY);

  if (mode==SERVER_MODE) {
    server_addr.sin_port = htons(port);
    err = inet_aton(ip_addr_s,&server_addr.sin_addr);
    err = bind(sock, (struct sockaddr *) &server_addr, sizeof(struct sockaddr_in));
  } else {
    server_addr.sin_port = 0;
    dest_addr.sin_family = AF_INET;
    dest_addr.sin_port = htons(port);
    err = inet_aton(ip_addr_s,&dest_addr.sin_addr);
    err = connect(sock, (struct sockaddr *) &dest_addr, sizeof(struct sockaddr_in));
  }
  if(err<0) {
	  printf("err = %d\n",err);
	  exit(2);
  }
  
  printf("sock %d\n", sock);

  pkt_i=0;
  clock_gettime(CLOCK_MONOTONIC_RAW,&ts2);
  dt.tv_sec=0;
  dt.tv_nsec=speed_msg_nano;
  max_nsec=0;
  while (1) {
    if(mode==CLIENT_MODE) {
	pkt_i+=1;
	*(int*)(&msgbuf[0])=pkt_i;
	sendto(sock,&msgbuf,msglen,0,NULL,0);
	//ts2.tv_nsec+=speed_msg_nano;
	//if(ts2.tv_nsec>=GIGA) {
	//	ts2.tv_nsec-=GIGA;
	//	ts2.tv_sec+=1;
	//}
	while(1) {
		clock_gettime(CLOCK_MONOTONIC_RAW,&ts);
		long nsec=(ts.tv_sec-ts2.tv_sec)*GIGA+ts.tv_nsec-ts2.tv_nsec-pkt_i*speed_msg_nano;
		if(nsec>0) {
			if(nsec>max_nsec) max_nsec=nsec;
			//printf("%ld\n",nsec);
			break;
		}
	}
	if(pkt_i>=npkt) break;
    
    } else {
        long spin_count=0;
	while (1) {
		count = recvfrom(sock,&msgbuf,MSG_LEN,MSG_DONTWAIT,(struct sockaddr *)&src_addr,&socklen);
		if(count>0) break;
		spin_count++;
	}
	clock_gettime(CLOCK_MONOTONIC_RAW,&ts);
	if(pkt_i==0) {
		ts2=ts;
		ts1=ts;
	}
	pkt_i+=1;
	int seq=*(int*)(&msgbuf[0]);
	if(seq==INT_MAX) {
		printf("done\n");
		break;
	}
	long nsec=(ts.tv_sec-ts2.tv_sec)*GIGA+ts.tv_nsec-ts2.tv_nsec;
	long nsec1=(ts.tv_sec-ts1.tv_sec)*GIGA+ts.tv_nsec-ts1.tv_nsec;
	ts1=ts;
	printf("%d %d %ld %ld %d %d\n",pkt_i,seq,nsec-(long)speed_msg_nano*((long)seq-1),nsec1,count,spin_count);
    }
/*
	socklen = sizeof(struct sockaddr_in);
	count = recvfrom(sock,&msgbuf,MSG_LEN,0,(struct sockaddr *)&src_addr,&socklen);
	if(mode == SERVER_MODE) {
	  server_has_client = 1;
	  dest_addr = src_addr;
	}
	if((mode == CLIENT_MODE) || server_has_client) {
	    send_count = sendto(sock,&msgbuf,1400,0,(struct sockaddr *)&dest_addr,sizeof(struct sockaddr_in));
	    send_count = sendto(sock,&msgbuf[1400],1400-count,0,(struct sockaddr *)&dest_addr,sizeof(struct sockaddr_in));
	  } else {
	    send_count = sendto(sock,&msgbuf,count,0,(struct sockaddr *)&dest_addr,sizeof(struct sockaddr_in));
	  }
	}
      }
    } else {
      printf("no data\n");
    }
*/
  }
  printf("max_nsec %ld\n",max_nsec);

  if(mode==CLIENT_MODE) {
	  for(i=0;i<10;i++) {
		  *(int*)(&msgbuf[0])=INT_MAX;
		  sendto(sock,&msgbuf,msglen,0,NULL,0);
		  usleep(1000);
	  }
  }

  
  exit(0);
}
