You need to enable JavaScript to fully utilise this page.
/*******************************************

   Active Probing Package:

    tcpdumptodt-rtt.c

    - Calculates the round trip time based on two tcpdump outputs, one for
      the packets sent and one for the packets received.

    - This is based off tcpdump from DAG family cards using the dagsnap utility,
      ie. for DAG 3.2E
      
      SENDER: dagbpf -e < tracefile.d3h | tcpdump -r - | grep udp <packetsize> > sent_tcpdumpfile.txt
      RECEIVER: dagbpf -e < tracefile.d3h | tcpdump icmp -r - > recv_tcpdumpfile.txt

    - However it can be used for ANY tcpdumpfile which is formatted so
      that the sent and received packet entries are matched in the tcpdumpfile. 

    - Converts the tcpdump files to a roundtriptime value and then stores
      that in the general timestamp file format '*.dt'
  
   Created by: Attila Pasztor, Campbell Skene and David Smith

   Version: 0.1

   Date: 19.3.2003

Copyright (C) 2003 EMULab

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

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, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

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

#include <stdio.h>
#include <string.h>
#include <unistd.h> /* getopt stuff */
#include <stdlib.h>

#define MAXDT 10000 /* Magic number: can compute up to XXXX round trip times */
#define MAXFILENAMELEN 200 /* Maximum filename length */

char* format_timestamp (char *);
void usage(char *);
int readin_timestamps (unsigned long long *, FILE *);
int post_process(long long *, long long *, int, int, int);
int str2val(const char *, const char *, int, int);

char fname[1000];
unsigned long long *dt_ptr;
char linebuff[1000];
short int raw_ts; /* are we using raw tcpdump timestamps? (via -tt option)*/
short int debug; /* print out debugging information? */
short int averages = 0; /* are we calculating the average rtts? */
extern int optind;
extern int opterr;
extern char *optarg;

int main(int argc, char *argv[]) {
  register int op;
  int recv_pcount, sent_pcount, lost_pcount, hops;
  unsigned long long dt_recv[MAXDT];
  unsigned long long dt_sent[MAXDT];
  char i;
  FILE *recv_datafile = NULL;
  FILE *sent_datafile = NULL;
 
  /* Syntax check */
  if (argc < 5 || argc > 6 ) {
    usage(argv[0]);
  }
  
  opterr = 0;
  while ((op = getopt(argc, argv, "dt")) != EOF)
    switch (op) {

    case 'd':
      debug++;
      break;

    case 't':
      raw_ts++;
      break;

    default:
      usage(argv[0]);
    }

  if ((argc - optind) > 5) {
    usage(argv[0]);
  } 

  /* read data from files */
  recv_datafile = fopen(argv[optind++], "r");
  fprintf(stderr,"recv_datafile is %s\n",argv[optind-1]);
  sent_datafile = fopen(argv[optind++], "r");
  fprintf(stderr,"sent_datafile is %s\n",argv[optind-1]);
    
  if (recv_datafile == NULL) {
    fprintf(stderr,"%s: Error - couldn't open recv file: %s\n",argv[0],argv[1]);
    exit(-1);
  }
  
  if (sent_datafile == NULL) {
    fprintf(stderr,"%s: Error - couldn't open sent file: %s\n",argv[0],argv[2]);
    exit(-1);
  }
  
  /* open output file */
  strcpy(fname, argv[optind++]);

  if (!strstr(fname,".dt")) {
    fprintf(stderr,"Error: output filename: '%s' must end in .dt\n", fname);
    exit(-1);
  }

  /* Number of hops to dt_file, defaults to 1 if there aren't 4 args */  
  if (argc - optind == 1) 
    hops = str2val(argv[optind], "hops", 0, 255); 
  else
    hops = 1;

  if (debug) 
    fprintf(stdout,"Hops are: %d", hops);

  i = '\0';

  /* Read received tcpdump file */
  recv_pcount=readin_timestamps(dt_recv,recv_datafile);
  /* Read sent tcpdump file */
  sent_pcount=readin_timestamps(dt_sent,sent_datafile);
  /* Remove lost packets */
  lost_pcount=post_process(dt_sent, dt_recv, recv_pcount, sent_pcount, hops);

  fprintf(stderr,"\n%d Round trip time values were found, with %d lost packets and written to %s\n",
	  recv_pcount, lost_pcount, fname);

  fclose(recv_datafile);
  fclose(sent_datafile);

  return 0;
}

/* Function: format_timestamp */
/* Removes the hours and minutes from the timestamp generated by tcpdump */
char* format_timestamp (char *dtbuff) {
  int j = 0;

  /* Remove hour */
  while ( (dtbuff[j] != ':') && (dtbuff[j] != '\0')) {
    dtbuff[j] = ' ';
    j++;
  }

  /* Remove ':' */
  dtbuff[j] = ' ';
  j++;

  /* Remove Minutes */
  while ( (dtbuff[j] != ':') && (dtbuff[j] != '\0')) {
    dtbuff[j] = ' ';
    j++;
  }

  /* Remove ':' */
  dtbuff[j] = ' ';
  j++;
			
  /* Remove decimal point */
  while ( (dtbuff[j] != '.') && (dtbuff[j] != '\0') ) j++;
  dtbuff[j] = ' ';

  return dtbuff;
}

/* print a simple usage statement to the commandline */
void usage(char * program_name) {
  fprintf(stderr,"Usage: \n");
  fprintf(stderr,"\t%s -tt <received_filename> <sent filename> <dt_filename.dt> [num_hops]\n", program_name);
  fprintf(stderr,"\tie. To generate tcpdumpfile for received/sent packets monitored with DAG 3.2E card use:\n");
  fprintf(stderr,"\tReceived file: dagbpf -e < dagsnap.d3h | tcpdump icmp -r - > recv_filename.txt\n");
  fprintf(stderr,"\tSent file: dagbpf -e < dagsnap.d3h | tcpdump -r - | grep udp <packetsize> > sent_filename.txt\n");
  fprintf(stderr,"\t** If you use tcpdump -tt (raw timestamps) then use -tt\n");
  exit(-1);
}

/* read in the time stamps for sent and received packets */
int readin_timestamps (unsigned long long * dt_ptr, FILE * datafile)
{
  int pcount = 0;
  char * pbuff;
  
  pbuff = fgets(linebuff, 999, datafile);

  while (!feof(datafile) && pbuff != NULL) {
    int n;
    char i;
    char dtbuff[100];
    char tmpbuff[1000];
    double dtmp;
    unsigned long sec, usec;
    n = sscanf(linebuff,"%s %s %c",dtbuff,tmpbuff,&i);

    if(debug) fprintf(stdout,"dtbuff: %s, tmpbuff: %s, i: %c\n",dtbuff,tmpbuff,i);

    /* read the time stamp in the receiver file */
    if(!raw_ts) 
      /* ie. tcpdump format is: 15:18:24.108722 <sent machine name>... */
      format_timestamp(dtbuff);
    else {
      /* tcpdump has used raw timestamps, no need to format it */
      int j=0;
      while ( (dtbuff[j] != '.') && (dtbuff[j] != '\0') )
	j++;
      if ( dtbuff[j] == '.') {
	dtbuff[j] = ' ';
      }
    }
	  
    n = sscanf(dtbuff,"%lu %lu",&sec,&usec);
    if(debug) {
      fprintf(stdout,"sec: %lu, usec: %lu\n",sec,usec);
    }
    dt_ptr[pcount] = sec;
    dtmp = usec*(1.0e-6);
    dtmp *= (1 << 16);
    dtmp *= (1 << 16);
    usec = dtmp;
    dt_ptr[pcount] = ( dt_ptr[pcount] << 32 ) | usec;

    if ( i == '>' || ((tmpbuff[0] == '<') && tmpbuff[1] == '\0') ) i = '<';

    if ( n == 2 && i == '<' ) {
      /* this is really a time stamp */
      if (debug) {
	fprintf(stdout,"Read in timestamp: %Lu %c\n",dt_ptr[pcount],i);
      }
    } else {
      fprintf(stderr,"Parse error, check if the receiver input file was generated with the\n");
      fprintf(stderr,"proper tcpdump options.\n");
      exit(-1);
    }

    pcount++;
			
    /* find the next packet header */
    i = '.';
    while ( i!= '<' && !feof(datafile) ) {
      pbuff = fgets(linebuff, 999, datafile);
      if ( pbuff != 0 ) {
	sscanf(linebuff,"%s %s %c",dtbuff,tmpbuff,&i);
	if ( i == '>' || ((tmpbuff[0] == '<') && tmpbuff[1] == '\0') )
	  i = '<';   
      }
    }
  }
  return pcount;
}

/* Function: post_process
** Searches for and removes sent packets that have received no correspond ICMP
** reply, outputs computed round trip times in the double .dt file format
*/

int post_process(long long *dt_sent, long long *dt_recv, int recv_pcount, int sent_pcount, int hops) {  
  FILE *dt_file = NULL;      
  int j, lost_pcount=0;
  unsigned long long dt = 0;
  unsigned long long hops_num = (unsigned long long) hops;
  int flag = 0;
  
  dt_file = fopen(fname, "w");
   
  /* save the number of hops as the first value of the output file */
  fprintf(stdout,"\n%d hops written\n",hops);
  fwrite(&hops_num, sizeof(double), 1, dt_file); 
  
  if (sent_pcount != recv_pcount) {
    fprintf(stdout,"Different amount of sent (%d) and received (%d) packets counted.\n", sent_pcount, recv_pcount);
  }
 
  if(debug) 
    fprintf(stdout, "***Round trip times are***\n");

  /* process both send and receiver arrays of timestamps */
  for (j=0; j < recv_pcount; j++) {

    /* if there is a lost packet, ie. there is a sent packet but no
    ** corresponding received packet then write -1 in the correct position
    ** into the .dt file indicating a lost packet, and remove the sent
    ** packet from the array as it cannot be used to calculate a rtt.
    */
    if ( ( j != recv_pcount-1 && dt_recv[j] > dt_sent[j+1] ) || 
	 ( j == recv_pcount-1 && dt_recv[j] < dt_sent[sent_pcount-1] ) ) {
      unsigned long long lost_dt = -1;
      int k;

      if ( j == recv_pcount-1) flag++;
      else 
	fwrite(&lost_dt, sizeof(long long), 1, dt_file); 
	
      /* Don't remove sent packet entry if you're at the end of the recv_pcount
	 array */

      if (j != recv_pcount -1) {	
	/* Remove sent packet entry that has no reply */
	for (k=j; k < sent_pcount; k++) { 
	  if (k != (sent_pcount - 1) ) { /* sent_pcount -1 */
	    dt_sent[k] = dt_sent[k+1];
	  }
	}
      }  
      j--;
      sent_pcount--;
      lost_pcount++;

    } else {

      /* No lost packets, just compute rtt */
      dt = dt_recv[j]-dt_sent[j];
      if(debug)
	fprintf(stdout,"%d: %Lu\n", j, dt);
      fwrite(&dt, sizeof(long long), 1, dt_file);
    }
  }

  /* Write lost last packet at the end if flag is set */
  if (flag) {
    unsigned long long lost_dt = -1;
    fwrite(&lost_dt, sizeof(long long), 1, dt_file);
  }
 
  fclose(dt_file);
  return lost_pcount;
}

/* String to value with optional min and max. Handles decimal and hex. */
int
str2val(register const char *str, register const char *what,
	register int mi, register int ma)
{
  register const char *cp;
  register int val;
  char *ep;

  if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) {
    cp = str + 2;
    val = (int)strtol(cp, &ep, 16);
  } else
    val = (int)strtol(str, &ep, 10);
  if (*ep != '\0') {
    fprintf(stderr, "%s bad value for %s \n",
	    str, what);
    exit(1);
  }
  if (val < mi && mi >= 0) {
    if (mi == 0)
      fprintf(stderr, "%s must be >= %d\n",
	      what, mi);
    else
      fprintf(stderr, "%s must be > %d\n",
	      what, mi - 1);
    exit(1);
  }
  if (val > ma && ma >= 0) {
    fprintf(stderr, "%s must be <= %d\n", what, ma);
    exit(1);
  }
  return (val);
}