home *** CD-ROM | disk | FTP | other *** search
/ ftp.ee.lbl.gov / 2014.05.ftp.ee.lbl.gov.tar / ftp.ee.lbl.gov / rtpqual.c < prev    next >
C/C++ Source or Header  |  1993-07-07  |  7KB  |  259 lines

  1. /*
  2.  * Simple Multiprotocol Multicast Signal Quality Meter
  3.  *
  4.  * (c) Pittsburgh Supercomputing Center, Matthew B Mathis, 1993.
  5.  *
  6.  * THIS SOFTWARE IS CURRENTLY UNDER DEVELOPMENT.
  7.  *
  8.  * This has only been tested with nv 3.1.  Vat is not supported....
  9.  * If someone would be kind enough to write getVATseq(...) it would be much
  10.  * appreciated.
  11.  */
  12.  
  13. /*
  14. Usage: rtpqual [<group> [<port> [<format>]]]
  15.  
  16. Each output row presents the statistics from one transmitter.
  17.  
  18. Column one is the seconds part of the system time,
  19.     such that loss events can be correlated with other events.
  20. Statistics from the current second are displayed in columns 2-6:
  21.     data packets received,
  22.     (presumed) lost data packets,
  23.     percentage loss, 
  24.     late (and non-sequenced) control packets,
  25.     bytes received (data, late data and control).
  26. The next 5 columns are cumulative totals for this transmitter
  27.     (except k Bytes instead of Bytes).
  28.  
  29. If there is more than one transmitter, cumulative total statistics are
  30. displayed for each every minute.
  31.  
  32. BUGS: Delay variance is not instrumented.
  33.  
  34. Each line appears only when there is input in a new second, therefor it is not
  35. possible to see the last second of a transmission.
  36. */
  37.  
  38. #include <stdio.h>
  39. #include <errno.h>
  40. #include <sys/types.h>
  41. #include <sys/socket.h>
  42. #include <sys/time.h>
  43. #include <netinet/in.h>
  44. #include <netdb.h>
  45.  
  46. #define MAXB 1500
  47. #define K 1024
  48. #define HEADER_FUDGE 40
  49.  
  50. #define DEFAULT_GROUP    "224.2.1.1"
  51. #define DEFAULT_PORT    4444
  52. #define DEFAULT_FORMAT    "rtp"
  53.  
  54. #define NOERROR(val,msg) {if (((int)(val)) < 0) {perror(msg);exit(1);}}
  55. #define NOTNULL(val,msg) {if (!(val)) {fprintf(stderr,msg);exit(1);}}
  56.  
  57. #define DO_SENDERS(x) {if (senders) {x = senders; do {
  58. #define UNTIL_SENDERS(x) x = x->next; } while (x != senders);}}
  59.  
  60. struct senderstat {
  61.   struct senderstat *next;        /* circular list */
  62.   int flags;
  63.   struct sockaddr_in from;
  64.   char from_name[20];            /* precomputed fromname */
  65.   int seq;                /* low byte of the sequence # */
  66.   int spkts, slost, npkts, bytes;    /* stats - this second */
  67.   int Tspkts, Tslost, Tnpkts, Tbytes;    /* stats - totals for this sender */
  68. };
  69. struct senderstat *findfrom();
  70. struct senderstat* senders=0;
  71.  
  72. int Tspkts, Tslost, Tnpkts, Tbytes;    /* stats - grand totals */
  73.  
  74. unsigned int getRTPseq();        /* Crack nv/RTP */
  75.  
  76. main(argc, argv)
  77. char *argv[];
  78. {
  79.   unsigned char buff[MAXB];
  80.   struct sockaddr_in from;
  81.   struct senderstat *ss, *s;
  82.   struct timeval now;
  83.   char *name, *form;
  84.   int port, assumed=0;
  85.   int fd, len, i, fromlen, tick;
  86.   int d, minute, first, sq;
  87.   unsigned int (*getseq)();
  88.   float f1, f2;
  89.  
  90.   /* XXX "parse" the arguments, and we use the term loosly.... */
  91.   name = (argc>1) ? argv[1]:DEFAULT_GROUP;
  92.   port = (argc>2) ? atoi(argv[2]):DEFAULT_PORT;
  93.   form = (argc>3) ? argv[3]:DEFAULT_FORMAT;
  94.   if (argc < 4) {
  95.     printf("Defaulting to: %s %s %d %s\n", argv[0], name, port, form);
  96.   }
  97.   if (!strcmp(form, "rtp"))    /* XXX */
  98.     getseq = getRTPseq;
  99.   else { 
  100.     printf("Currently only rtp is supported\n");
  101.     exit(1);
  102.   }
  103.  
  104.   fd = openMC(name, htons(port));
  105.  
  106.   gettimeofday(&now, 0);
  107.   tick=now.tv_sec;
  108.  
  109.   for (;;) {
  110.     fromlen =  sizeof(from);
  111.     NOERROR(len = recvfrom(fd, buff, MAXB, 0, &from, &fromlen),"recvfrom");
  112.  
  113.     /* display stats if this is a new second. */
  114.     gettimeofday(&now, 0);
  115.     if (tick != now.tv_sec) {
  116.       int cmin = tick/60;
  117.       if (cmin != minute) {
  118.     minute = cmin;
  119.     printf("\
  120. T Pkts Loss %%  Late Bytes |  Pkts Loss   %%    Late     kB Sender\n");
  121.     if (senders && (senders->next != senders)) {
  122.       f1 = (Tspkts+Tslost)?Tslost*100/(Tspkts+Tslost):0.0;
  123.       printf("\
  124.                           |%6d %4d %5.2f %4d %7d (All Senders)\n",
  125.          Tspkts, Tslost, f1, Tnpkts, Tbytes/K);
  126.     }
  127.       }
  128.       first=1;
  129.       DO_SENDERS(s) {
  130.     Tspkts += s->spkts; s->Tspkts += s->spkts;
  131.     Tslost += s->slost; s->Tslost += s->slost;
  132.     Tnpkts += s->npkts; s->Tnpkts += s->npkts;
  133.     Tbytes += s->bytes; s->Tbytes += s->bytes;
  134.     if (s->flags) {
  135.       if (first) {
  136.         printf("%2d ",tick%60); first=0;
  137.       } else
  138.         printf("   ");
  139.       f1 = (s->spkts+s->slost)?s->slost*100/(s->spkts+s->slost):0.0;
  140.       f2 = (s->Tspkts+s->Tslost)?s->Tslost*100/(s->Tspkts+s->Tslost):0.0;
  141.       printf("\
  142. %3d %2d %5.2f %2d %6d |%6d %4d %5.2f %4d %7d %s\n",
  143. s->spkts, s->slost, f1, s->npkts, s->bytes,
  144. s->Tspkts, s->Tslost, f2, s->Tnpkts, s->Tbytes/K,
  145.          s->from_name);
  146.     }
  147.     s->flags = s->spkts = s->slost = s->bytes = 0;
  148.       } UNTIL_SENDERS(s);
  149.     }
  150.  
  151.     /* tabulate current packet */
  152.     tick = now.tv_sec;
  153.     ss = findfrom(&from);
  154.     sq = (getseq)(buff, len, ss);
  155.     if (sq >= 0) {
  156.       ss->flags = 1;
  157.       ss->spkts++;
  158.       if (ss->seq >=0) {
  159.     if (0x80 & (sq - ss->seq - 1))
  160.       ss->npkts++;            /* late */
  161.     else {
  162.       ss->slost += 0xFF & ((unsigned int) (sq - ss->seq - 1));
  163.       ss->seq = sq;
  164.     }
  165.       } else
  166.     ss->seq = sq;
  167.     } else {
  168.       ss->npkts++;            /* non-sequenced */
  169.     }
  170.     ss->bytes += len + HEADER_FUDGE;
  171.     senders = ss;
  172.   }
  173. }
  174.  
  175. unsigned int getRTPseq(b, l, s)
  176. unsigned char *b;
  177. int l;
  178. struct senderstat *s;
  179. {
  180.   /* XXX We are clueless here, but this seems to work */
  181.   /* XXX We should parse RTP options and update s->from_name.... */
  182.   /* XXX if there is non-sequenced data, return(-1) */
  183.   /* XXX */ return(b[3]);
  184. }
  185.  
  186. struct senderstat *findfrom(f)
  187. struct sockaddr_in *f;
  188. {
  189.   struct senderstat* s=0;
  190.   unsigned char *fa = (unsigned char *) &f->sin_addr;
  191.  
  192.   DO_SENDERS(s) {
  193.     if (!strncmp((char *)&f->sin_addr, (char *)&s->from.sin_addr,
  194.          sizeof(struct sockaddr_in)))    return(s);
  195.   } UNTIL_SENDERS(s);
  196.   
  197.   NOTNULL(s = (struct senderstat *) malloc(sizeof(struct senderstat)),
  198.       "malloc\n");
  199.   bzero(s, sizeof(struct senderstat));
  200.   bcopy((char *)&f->sin_addr, (char *)&s->from.sin_addr,
  201.     sizeof(struct sockaddr_in));
  202.   sprintf(s->from_name, "%d.%d.%d.%d", fa[0], fa[1], fa[2], fa[3]);
  203.   s->seq = -1;
  204.   if (!senders) {
  205.     s->next = s;
  206.     senders = s;
  207.   } else {
  208.     s->next = senders->next;
  209.     senders->next = s;
  210.   }
  211.   return (s);
  212. }
  213.  
  214. int openMC(name, port)
  215. char *name;
  216. int port;
  217. {
  218.   struct sockaddr_in sin;
  219.   struct ip_mreq mreq;
  220.   struct hostent *hp;
  221.   int fd, one=1;
  222.  
  223.   bzero(&sin, sizeof(struct sockaddr_in));
  224.   if (isdigit(*name)) {
  225.     sin.sin_addr.s_addr = inet_addr(name);
  226.   }
  227.   else if (hp = gethostbyname(name)) {
  228.     bcopy(hp->h_addr, (char *)&sin.sin_addr, hp->h_length);
  229.   }
  230.   else {
  231.     printf("I Don't understand session name %s\n",name);
  232.     exit(1);
  233.   }
  234.   sin.sin_family = AF_INET;
  235.   sin.sin_port = port;
  236.  
  237.   if (!IN_MULTICAST(ntohl(sin.sin_addr.s_addr))) {
  238.     printf("%s is not a multicast session\n", name);
  239.     exit(1);
  240.   }
  241.   mreq.imr_multiaddr = sin.sin_addr;
  242.   mreq.imr_interface.s_addr = INADDR_ANY;
  243.  
  244.   NOERROR(fd = socket(AF_INET, SOCK_DGRAM, 0), "socket");
  245.   NOERROR(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)),
  246.       "SO_REUSEADDR");
  247.  
  248.   if (bind(fd, &sin, sizeof(sin)) == -1) {
  249.     perror("Using INADDR_ANY because");
  250.     sin.sin_addr.s_addr = INADDR_ANY;
  251.     NOERROR(bind(fd, &sin, sizeof(sin)), "bind");
  252.   }
  253.  
  254.   NOERROR(setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)),
  255.       "IP_ADD_MEMBERSHIP");
  256.  
  257.   return(fd);
  258. }
  259.