fix incorrect compression ratio printout on linux
[ppp.git] / pppstats / pppstats.c
1 /*
2  * print PPP statistics:
3  *      pppstats [-a|-d] [-v|-r|-z] [-c count] [-w wait] [interface]
4  *
5  *   -a Show absolute values rather than deltas
6  *   -d Show data rate (kB/s) rather than bytes
7  *   -v Show more stats for VJ TCP header compression
8  *   -r Show compression ratio
9  *   -z Show compression statistics instead of default display
10  *
11  * History:
12  *      perkins@cps.msu.edu: Added compression statistics and alternate 
13  *                display. 11/94
14  *      Brad Parker (brad@cayman.com) 6/92
15  *
16  * from the original "slstats" by Van Jacobson
17  *
18  * Copyright (c) 1989 Regents of the University of California.
19  * All rights reserved.
20  *
21  * Redistribution and use in source and binary forms are permitted
22  * provided that the above copyright notice and this paragraph are
23  * duplicated in all such forms and that any documentation,
24  * advertising materials, and other materials related to such
25  * distribution and use acknowledge that the software was developed
26  * by the University of California, Berkeley.  The name of the
27  * University may not be used to endorse or promote products derived
28  * from this software without specific prior written permission.
29  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
30  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
31  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
32  */
33
34 #ifndef lint
35 static char rcsid[] = "$Id: pppstats.c,v 1.23 1998/07/07 04:23:04 paulus Exp $";
36 #endif
37
38 #include <stdio.h>
39 #include <stddef.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <ctype.h>
43 #include <errno.h>
44 #include <signal.h>
45 #include <fcntl.h>
46 #include <unistd.h>
47 #include <sys/param.h>
48 #include <sys/types.h>
49 #include <sys/ioctl.h>
50
51 #ifndef STREAMS
52 #include <sys/socket.h>         /* *BSD, Linux, NeXT, Ultrix etc. */
53 #ifndef _linux_
54 #include <net/if.h>
55 #include <net/ppp_defs.h>
56 #include <net/if_ppp.h>
57 #else
58 /* Linux */
59 #if __GLIBC__ >= 2
60 #include <asm/types.h>          /* glibc 2 conflicts with linux/types.h */
61 #include <net/if.h>
62 #else
63 #include <linux/types.h>
64 #include <linux/if.h>
65 #endif
66 #include <linux/ppp_defs.h>
67 #include <linux/if_ppp.h>
68 #endif /* _linux_ */
69
70 #else   /* STREAMS */
71 #include <sys/stropts.h>        /* SVR4, Solaris 2, SunOS 4, OSF/1, etc. */
72 #include <net/ppp_defs.h>
73 #include <net/pppio.h>
74
75 #endif  /* STREAMS */
76
77 int     vflag, rflag, zflag;    /* select type of display */
78 int     aflag;                  /* print absolute values, not deltas */
79 int     dflag;                  /* print data rates, not bytes */
80 int     interval, count;
81 int     infinite;
82 int     unit;
83 int     s;                      /* socket or /dev/ppp file descriptor */
84 int     signalled;              /* set if alarm goes off "early" */
85 char    *progname;
86 char    *interface;
87
88 #if defined(SUNOS4) || defined(ULTRIX) || defined(NeXT)
89 extern int optind;
90 extern char *optarg;
91 #endif
92
93 static void usage __P((void));
94 static void catchalarm __P((int));
95 static void get_ppp_stats __P((struct ppp_stats *));
96 static void get_ppp_cstats __P((struct ppp_comp_stats *));
97 static void intpr __P((void));
98
99 int main __P((int, char *argv[]));
100
101 static void
102 usage()
103 {
104     fprintf(stderr, "Usage: %s [-a|-d] [-v|-r|-z] [-c count] [-w wait] [interface]\n",
105             progname);
106     exit(1);
107 }
108
109 /*
110  * Called if an interval expires before intpr has completed a loop.
111  * Sets a flag to not wait for the alarm.
112  */
113 static void
114 catchalarm(arg)
115     int arg;
116 {
117     signalled = 1;
118 }
119
120
121 #ifndef STREAMS
122 static void
123 get_ppp_stats(curp)
124     struct ppp_stats *curp;
125 {
126     struct ifpppstatsreq req;
127
128     memset (&req, 0, sizeof (req));
129
130 #ifdef _linux_
131     req.stats_ptr = (caddr_t) &req.stats;
132 #undef ifr_name
133 #define ifr_name ifr__name
134 #endif
135
136     strncpy(req.ifr_name, interface, sizeof(req.ifr_name));
137     if (ioctl(s, SIOCGPPPSTATS, &req) < 0) {
138         fprintf(stderr, "%s: ", progname);
139         if (errno == ENOTTY)
140             fprintf(stderr, "kernel support missing\n");
141         else
142             perror("couldn't get PPP statistics");
143         exit(1);
144     }
145     *curp = req.stats;
146 }
147
148 static void
149 get_ppp_cstats(csp)
150     struct ppp_comp_stats *csp;
151 {
152     struct ifpppcstatsreq creq;
153
154     memset (&creq, 0, sizeof (creq));
155
156 #ifdef _linux_
157     creq.stats_ptr = (caddr_t) &creq.stats;
158 #undef  ifr_name
159 #define ifr_name ifr__name
160 #endif
161
162     strncpy(creq.ifr_name, interface, sizeof(creq.ifr_name));
163     if (ioctl(s, SIOCGPPPCSTATS, &creq) < 0) {
164         fprintf(stderr, "%s: ", progname);
165         if (errno == ENOTTY) {
166             fprintf(stderr, "no kernel compression support\n");
167             if (zflag)
168                 exit(1);
169             rflag = 0;
170         } else {
171             perror("couldn't get PPP compression stats");
172             exit(1);
173         }
174     }
175
176 #ifdef _linux_
177     if (creq.stats.c.bytes_out == 0) {
178         creq.stats.c.bytes_out = creq.stats.c.comp_bytes + creq.stats.c.inc_bytes;
179         creq.stats.c.in_count = creq.stats.c.unc_bytes;
180     }
181     if (creq.stats.c.bytes_out == 0)
182         creq.stats.c.ratio = 0.0;
183     else
184         creq.stats.c.ratio = 256.0 * creq.stats.c.in_count /
185                              creq.stats.c.bytes_out;
186
187     if (creq.stats.d.bytes_out == 0) {
188         creq.stats.d.bytes_out = creq.stats.d.comp_bytes + creq.stats.d.inc_bytes;
189         creq.stats.d.in_count = creq.stats.d.unc_bytes;
190     }
191     if (creq.stats.d.bytes_out == 0)
192         creq.stats.d.ratio = 0.0;
193     else
194         creq.stats.d.ratio = 256.0 * creq.stats.d.in_count /
195                              creq.stats.d.bytes_out;
196 #endif
197
198     *csp = creq.stats;
199 }
200
201 #else   /* STREAMS */
202
203 int
204 strioctl(fd, cmd, ptr, ilen, olen)
205     int fd, cmd, ilen, olen;
206     char *ptr;
207 {
208     struct strioctl str;
209
210     str.ic_cmd = cmd;
211     str.ic_timout = 0;
212     str.ic_len = ilen;
213     str.ic_dp = ptr;
214     if (ioctl(fd, I_STR, &str) == -1)
215         return -1;
216     if (str.ic_len != olen)
217         fprintf(stderr, "strioctl: expected %d bytes, got %d for cmd %x\n",
218                olen, str.ic_len, cmd);
219     return 0;
220 }
221
222 static void
223 get_ppp_stats(curp)
224     struct ppp_stats *curp;
225 {
226     if (strioctl(s, PPPIO_GETSTAT, curp, 0, sizeof(*curp)) < 0) {
227         fprintf(stderr, "%s: ", progname);
228         if (errno == EINVAL)
229             fprintf(stderr, "kernel support missing\n");
230         else
231             perror("couldn't get PPP statistics");
232         exit(1);
233     }
234 }
235
236 static void
237 get_ppp_cstats(csp)
238     struct ppp_comp_stats *csp;
239 {
240     if (strioctl(s, PPPIO_GETCSTAT, csp, 0, sizeof(*csp)) < 0) {
241         fprintf(stderr, "%s: ", progname);
242         if (errno == ENOTTY) {
243             fprintf(stderr, "no kernel compression support\n");
244             if (zflag)
245                 exit(1);
246             rflag = 0;
247         } else {
248             perror("couldn't get PPP compression statistics");
249             exit(1);
250         }
251     }
252 }
253
254 #endif /* STREAMS */
255
256 #define MAX0(a)         ((int)(a) > 0? (a): 0)
257 #define V(offset)       MAX0(cur.offset - old.offset)
258 #define W(offset)       MAX0(ccs.offset - ocs.offset)
259
260 #define RATIO(c, i, u)  ((c) == 0? 1.0: (u) / ((double)(c) + (i)))
261 #define CRATE(x)        RATIO(W(x.comp_bytes), W(x.inc_bytes), W(x.unc_bytes))
262
263 #define KBPS(n)         ((n) / (interval * 1000.0))
264
265 /*
266  * Print a running summary of interface statistics.
267  * Repeat display every interval seconds, showing statistics
268  * collected over that interval.  Assumes that interval is non-zero.
269  * First line printed is cumulative.
270  */
271 static void
272 intpr()
273 {
274     register int line = 0;
275     sigset_t oldmask, mask;
276     char *bunit;
277     int ratef = 0;
278     struct ppp_stats cur, old;
279     struct ppp_comp_stats ccs, ocs;
280
281     memset(&old, 0, sizeof(old));
282     memset(&ocs, 0, sizeof(ocs));
283
284     while (1) {
285         get_ppp_stats(&cur);
286         if (zflag || rflag)
287             get_ppp_cstats(&ccs);
288
289         (void)signal(SIGALRM, catchalarm);
290         signalled = 0;
291         (void)alarm(interval);
292
293         if ((line % 20) == 0) {
294             if (zflag) {
295                 printf("IN:  COMPRESSED  INCOMPRESSIBLE   COMP | ");
296                 printf("OUT: COMPRESSED  INCOMPRESSIBLE   COMP\n");
297                 bunit = dflag? "KB/S": "BYTE";
298                 printf("    %s   PACK     %s   PACK  RATIO | ", bunit, bunit);
299                 printf("    %s   PACK     %s   PACK  RATIO", bunit, bunit);
300             } else {
301                 printf("%8.8s %6.6s %6.6s",
302                        "IN", "PACK", "VJCOMP");
303
304                 if (!rflag)
305                     printf(" %6.6s %6.6s", "VJUNC", "VJERR");
306                 if (vflag)
307                     printf(" %6.6s %6.6s", "VJTOSS", "NON-VJ");
308                 if (rflag)
309                     printf(" %6.6s %6.6s", "RATIO", "UBYTE");
310                 printf("  | %8.8s %6.6s %6.6s",
311                        "OUT", "PACK", "VJCOMP");
312
313                 if (!rflag)
314                     printf(" %6.6s %6.6s", "VJUNC", "NON-VJ");
315                 if (vflag)
316                     printf(" %6.6s %6.6s", "VJSRCH", "VJMISS");
317                 if (rflag)
318                     printf(" %6.6s %6.6s", "RATIO", "UBYTE");
319             }
320             putchar('\n');
321         }
322
323         if (zflag) {
324             if (ratef) {
325                 printf("%8.3f %6u %8.3f %6u %6.2f",
326                        KBPS(W(d.comp_bytes)),
327                        W(d.comp_packets),
328                        KBPS(W(d.inc_bytes)),
329                        W(d.inc_packets),
330                        ccs.d.ratio / 256.0);
331                 printf(" | %8.3f %6u %8.3f %6u %6.2f",
332                        KBPS(W(c.comp_bytes)),
333                        W(c.comp_packets),
334                        KBPS(W(c.inc_bytes)),
335                        W(c.inc_packets),
336                        ccs.c.ratio / 256.0);
337             } else {
338                 printf("%8u %6u %8u %6u %6.2f",
339                        W(d.comp_bytes),
340                        W(d.comp_packets),
341                        W(d.inc_bytes),
342                        W(d.inc_packets),
343                        ccs.d.ratio / 256.0);
344                 printf(" | %8u %6u %8u %6u %6.2f",
345                        W(c.comp_bytes),
346                        W(c.comp_packets),
347                        W(c.inc_bytes),
348                        W(c.inc_packets),
349                        ccs.c.ratio / 256.0);
350             }
351         
352         } else {
353             if (ratef)
354                 printf("%8.3f", KBPS(V(p.ppp_ibytes)));
355             else
356                 printf("%8u", V(p.ppp_ibytes));
357             printf(" %6u %6u",
358                    V(p.ppp_ipackets),
359                    V(vj.vjs_compressedin));
360             if (!rflag)
361                 printf(" %6u %6u",
362                        V(vj.vjs_uncompressedin),
363                        V(vj.vjs_errorin));
364             if (vflag)
365                 printf(" %6u %6u",
366                        V(vj.vjs_tossed),
367                        V(p.ppp_ipackets) - V(vj.vjs_compressedin)
368                        - V(vj.vjs_uncompressedin) - V(vj.vjs_errorin));
369             if (rflag) {
370                 printf(" %6.2f ", CRATE(d));
371                 if (ratef)
372                     printf("%6.2f", KBPS(W(d.unc_bytes)));
373                 else
374                     printf("%6u", W(d.unc_bytes));
375             }
376             if (ratef)
377                 printf("  | %8.3f", KBPS(V(p.ppp_obytes)));
378             else
379                 printf("  | %8u", V(p.ppp_obytes));
380             printf(" %6u %6u",
381                    V(p.ppp_opackets),
382                    V(vj.vjs_compressed));
383             if (!rflag)
384                 printf(" %6u %6u",
385                        V(vj.vjs_packets) - V(vj.vjs_compressed),
386                        V(p.ppp_opackets) - V(vj.vjs_packets));
387             if (vflag)
388                 printf(" %6u %6u",
389                        V(vj.vjs_searches),
390                        V(vj.vjs_misses));
391             if (rflag) {
392                 printf(" %6.2f ", CRATE(c));
393                 if (ratef)
394                     printf("%6.2f", KBPS(W(c.unc_bytes)));
395                 else
396                     printf("%6u", W(c.unc_bytes));
397             }
398
399         }
400
401         putchar('\n');
402         fflush(stdout);
403         line++;
404
405         count--;
406         if (!infinite && !count)
407             break;
408
409         sigemptyset(&mask);
410         sigaddset(&mask, SIGALRM);
411         sigprocmask(SIG_BLOCK, &mask, &oldmask);
412         if (!signalled) {
413             sigemptyset(&mask);
414             sigsuspend(&mask);
415         }
416         sigprocmask(SIG_SETMASK, &oldmask, NULL);
417         signalled = 0;
418         (void)alarm(interval);
419
420         if (!aflag) {
421             old = cur;
422             ocs = ccs;
423             ratef = dflag;
424         }
425     }
426 }
427
428 int
429 main(argc, argv)
430     int argc;
431     char *argv[];
432 {
433     int c;
434 #ifdef STREAMS
435     char *dev;
436 #endif
437
438     interface = "ppp0";
439     if ((progname = strrchr(argv[0], '/')) == NULL)
440         progname = argv[0];
441     else
442         ++progname;
443
444     while ((c = getopt(argc, argv, "advrzc:w:")) != -1) {
445         switch (c) {
446         case 'a':
447             ++aflag;
448             break;
449         case 'd':
450             ++dflag;
451             break;
452         case 'v':
453             ++vflag;
454             break;
455         case 'r':
456             ++rflag;
457             break;
458         case 'z':
459             ++zflag;
460             break;
461         case 'c':
462             count = atoi(optarg);
463             if (count <= 0)
464                 usage();
465             break;
466         case 'w':
467             interval = atoi(optarg);
468             if (interval <= 0)
469                 usage();
470             break;
471         default:
472             usage();
473         }
474     }
475     argc -= optind;
476     argv += optind;
477
478     if (!interval && count)
479         interval = 5;
480     if (interval && !count)
481         infinite = 1;
482     if (!interval && !count)
483         count = 1;
484     if (aflag)
485         dflag = 0;
486
487     if (argc > 1)
488         usage();
489     if (argc > 0)
490         interface = argv[0];
491
492     if (sscanf(interface, "ppp%d", &unit) != 1) {
493         fprintf(stderr, "%s: invalid interface '%s' specified\n",
494                 progname, interface);
495     }
496
497 #ifndef STREAMS
498     {
499         struct ifreq ifr;
500
501         s = socket(AF_INET, SOCK_DGRAM, 0);
502         if (s < 0) {
503             fprintf(stderr, "%s: ", progname);
504             perror("couldn't create IP socket");
505             exit(1);
506         }
507
508 #ifdef _linux_
509 #undef  ifr_name
510 #define ifr_name ifr_ifrn.ifrn_name
511 #endif
512         strncpy(ifr.ifr_name, interface, sizeof(ifr.ifr_name));
513         if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
514             fprintf(stderr, "%s: nonexistent interface '%s' specified\n",
515                     progname, interface);
516             exit(1);
517         }
518     }
519
520 #else   /* STREAMS */
521 #ifdef __osf__
522     dev = "/dev/streams/ppp";
523 #else
524     dev = "/dev/ppp";
525 #endif
526     if ((s = open(dev, O_RDONLY)) < 0) {
527         fprintf(stderr, "%s: couldn't open ", progname);
528         perror(dev);
529         exit(1);
530     }
531     if (strioctl(s, PPPIO_ATTACH, &unit, sizeof(int), 0) < 0) {
532         fprintf(stderr, "%s: ppp%d is not available\n", progname, unit);
533         exit(1);
534     }
535
536 #endif  /* STREAMS */
537
538     intpr();
539     exit(0);
540 }