*** empty log message ***
[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.15 1996/08/28 06:43:30 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 #include <net/ppp_defs.h>
51
52 #ifndef STREAMS
53 #include <sys/socket.h>         /* *BSD, Linux, NeXT, Ultrix etc. */
54 #include <net/if.h>
55 #include <net/if_ppp.h>
56
57 #else   /* STREAMS */
58 #include <sys/stropts.h>        /* SVR4, Solaris 2, SunOS 4, OSF/1, etc. */
59 #include <net/pppio.h>
60
61 #endif  /* STREAMS */
62
63 int     vflag, rflag, zflag;    /* select type of display */
64 int     aflag;                  /* print absolute values, not deltas */
65 int     dflag;                  /* print data rates, not bytes */
66 int     interval, count;
67 int     infinite;
68 int     unit;
69 int     s;                      /* socket or /dev/ppp file descriptor */
70 int     signalled;              /* set if alarm goes off "early" */
71 char    *progname;
72 char    *interface;
73
74 void
75 usage()
76 {
77     fprintf(stderr, "Usage: %s [-a|-d] [-v|-r|-z] [-c count] [-w wait] [interface]\n",
78             progname);
79     exit(1);
80 }
81
82 /*
83  * Called if an interval expires before intpr has completed a loop.
84  * Sets a flag to not wait for the alarm.
85  */
86 void
87 catchalarm(arg)
88     int arg;
89 {
90     signalled = 1;
91 }
92
93
94 #ifndef STREAMS
95 void
96 get_ppp_stats(curp)
97     struct ppp_stats *curp;
98 {
99     struct ifpppstatsreq req;
100
101     memset (&req, 0, sizeof (req));
102
103 #ifdef _linux_
104     req.stats_ptr = (caddr_t) &req.stats;
105 #undef ifr_name
106 #define ifr_name ifr__name
107 #endif
108
109     strncpy(req.ifr_name, interface, sizeof(req.ifr_name));
110     if (ioctl(s, SIOCGPPPSTATS, &req) < 0) {
111         fprintf(stderr, "%s: ", progname);
112         if (errno == ENOTTY)
113             fprintf(stderr, "kernel support missing\n");
114         else
115             perror("couldn't get PPP statistics");
116         exit(1);
117     }
118     *curp = req.stats;
119 }
120
121 void
122 get_ppp_cstats(csp)
123     struct ppp_comp_stats *csp;
124 {
125     struct ifpppcstatsreq creq;
126
127     memset (&creq, 0, sizeof (creq));
128
129 #ifdef _linux_
130     creq.stats_ptr = (caddr_t) &creq.stats;
131 #undef  ifr_name
132 #define ifr_name ifr__name
133 #endif
134
135     strncpy(creq.ifr_name, interface, sizeof(creq.ifr_name));
136     if (ioctl(s, SIOCGPPPCSTATS, &creq) < 0) {
137         fprintf(stderr, "%s: ", progname);
138         if (errno == ENOTTY) {
139             fprintf(stderr, "no kernel compression support\n");
140             if (zflag)
141                 exit(1);
142             rflag = 0;
143         } else {
144             perror("couldn't get PPP compression stats");
145             exit(1);
146         }
147     }
148
149 #ifdef _linux_
150     if (creq.stats.c.bytes_out == 0)
151         creq.stats.c.ratio = 0.0;
152     else
153         creq.stats.c.ratio = (double) creq.stats.c.in_count /
154                              (double) creq.stats.c.bytes_out;
155
156     if (creq.stats.d.bytes_out == 0)
157         creq.stats.d.ratio = 0.0;
158     else
159         creq.stats.d.ratio = (double) creq.stats.d.in_count /
160                              (double) creq.stats.d.bytes_out;
161 #endif
162
163     *csp = creq.stats;
164 }
165
166 #else   /* STREAMS */
167
168 int
169 strioctl(fd, cmd, ptr, ilen, olen)
170     int fd, cmd, ilen, olen;
171     char *ptr;
172 {
173     struct strioctl str;
174
175     str.ic_cmd = cmd;
176     str.ic_timout = 0;
177     str.ic_len = ilen;
178     str.ic_dp = ptr;
179     if (ioctl(fd, I_STR, &str) == -1)
180         return -1;
181     if (str.ic_len != olen)
182         fprintf(stderr, "strioctl: expected %d bytes, got %d for cmd %x\n",
183                olen, str.ic_len, cmd);
184     return 0;
185 }
186
187 void
188 get_ppp_stats(curp)
189     struct ppp_stats *curp;
190 {
191     if (strioctl(s, PPPIO_GETSTAT, curp, 0, sizeof(*curp)) < 0) {
192         fprintf(stderr, "%s: ", progname);
193         if (errno == EINVAL)
194             fprintf(stderr, "kernel support missing\n");
195         else
196             perror("couldn't get PPP statistics");
197         exit(1);
198     }
199 }
200
201 void
202 get_ppp_cstats(csp)
203     struct ppp_comp_stats *csp;
204 {
205     if (strioctl(s, PPPIO_GETCSTAT, csp, 0, sizeof(*csp)) < 0) {
206         fprintf(stderr, "%s: ", progname);
207         if (errno == ENOTTY) {
208             fprintf(stderr, "no kernel compression support\n");
209             if (zflag)
210                 exit(1);
211             rflag = 0;
212         } else {
213             perror("couldn't get PPP compression statistics");
214             exit(1);
215         }
216     }
217 }
218
219 #endif /* STREAMS */
220
221 #define MAX0(a)         ((int)(a) > 0? (a): 0)
222 #define V(offset)       MAX0(cur.offset - old.offset)
223 #define W(offset)       MAX0(ccs.offset - ocs.offset)
224
225 #define RATIO(c, i, u)  ((c) == 0? 1.0: (u) / ((double)(c) + (i)))
226 #define CRATE(x)        RATIO(W(x.comp_bytes), W(x.inc_bytes), W(x.unc_bytes))
227
228 #define KBPS(n)         ((n) / (interval * 1000.0))
229
230 /*
231  * Print a running summary of interface statistics.
232  * Repeat display every interval seconds, showing statistics
233  * collected over that interval.  Assumes that interval is non-zero.
234  * First line printed is cumulative.
235  */
236 void
237 intpr()
238 {
239     register int line = 0;
240     sigset_t oldmask, mask;
241     char *bunit;
242     int ratef = 0;
243     struct ppp_stats cur, old;
244     struct ppp_comp_stats ccs, ocs;
245
246     memset(&old, 0, sizeof(old));
247     memset(&ocs, 0, sizeof(ocs));
248
249     while (1) {
250         get_ppp_stats(&cur);
251         if (zflag || rflag)
252             get_ppp_cstats(&ccs);
253
254         (void)signal(SIGALRM, catchalarm);
255         signalled = 0;
256         (void)alarm(interval);
257
258         if ((line % 20) == 0) {
259             if (zflag) {
260                 printf("IN:  COMPRESSED  INCOMPRESSIBLE   COMP | ");
261                 printf("OUT: COMPRESSED  INCOMPRESSIBLE   COMP\n");
262                 bunit = dflag? "KB/S": "BYTE";
263                 printf("    %s   PACK     %s   PACK  RATIO | ", bunit, bunit);
264                 printf("    %s   PACK     %s   PACK  RATIO", bunit, bunit);
265             } else {
266                 printf("%8.8s %6.6s %6.6s",
267                        "IN", "PACK", "VJCOMP");
268
269                 if (!rflag)
270                     printf(" %6.6s %6.6s", "VJUNC", "VJERR");
271                 if (vflag)
272                     printf(" %6.6s %6.6s", "VJTOSS", "NON-VJ");
273                 if (rflag)
274                     printf(" %6.6s %6.6s", "RATIO", "UBYTE");
275                 printf("  | %8.8s %6.6s %6.6s",
276                        "OUT", "PACK", "VJCOMP");
277
278                 if (!rflag)
279                     printf(" %6.6s %6.6s", "VJUNC", "NON-VJ");
280                 if (vflag)
281                     printf(" %6.6s %6.6s", "VJSRCH", "VJMISS");
282                 if (rflag)
283                     printf(" %6.6s %6.6s", "RATIO", "UBYTE");
284             }
285             putchar('\n');
286         }
287
288         if (zflag) {
289             if (ratef) {
290                 printf("%8.3f %6u %8.3f %6u %6.2f",
291                        KBPS(W(d.comp_bytes)),
292                        W(d.comp_packets),
293                        KBPS(W(d.inc_bytes)),
294                        W(d.inc_packets),
295                        ccs.d.ratio * 256.0);
296                 printf(" | %8.3f %6u %8.3f %6u %6.2f",
297                        KBPS(W(c.comp_bytes)),
298                        W(c.comp_packets),
299                        KBPS(W(c.inc_bytes)),
300                        W(c.inc_packets),
301                        ccs.c.ratio * 256.0);
302             } else {
303                 printf("%8u %6u %8u %6u %6.2f",
304                        W(d.comp_bytes),
305                        W(d.comp_packets),
306                        W(d.inc_bytes),
307                        W(d.inc_packets),
308                        ccs.d.ratio * 256.0);
309                 printf(" | %8u %6u %8u %6u %6.2f",
310                        W(c.comp_bytes),
311                        W(c.comp_packets),
312                        W(c.inc_bytes),
313                        W(c.inc_packets),
314                        ccs.c.ratio * 256.0);
315             }
316         
317         } else {
318             if (ratef)
319                 printf("%8.3f", KBPS(V(p.ppp_ibytes)));
320             else
321                 printf("%8u", V(p.ppp_ibytes));
322             printf(" %6u %6u",
323                    V(p.ppp_ipackets),
324                    V(vj.vjs_compressedin));
325             if (!rflag)
326                 printf(" %6u %6u",
327                        V(vj.vjs_uncompressedin),
328                        V(vj.vjs_errorin));
329             if (vflag)
330                 printf(" %6u %6u",
331                        V(vj.vjs_tossed),
332                        V(p.ppp_ipackets) - V(vj.vjs_compressedin)
333                        - V(vj.vjs_uncompressedin) - V(vj.vjs_errorin));
334             if (rflag) {
335                 printf(" %6.2f ", CRATE(d));
336                 if (ratef)
337                     printf("%6.2f", KBPS(W(d.unc_bytes)));
338                 else
339                     printf("%6u", W(d.unc_bytes));
340             }
341             if (ratef)
342                 printf("  | %8.3f", KBPS(V(p.ppp_obytes)));
343             else
344                 printf("  | %8u", V(p.ppp_obytes));
345             printf(" %6u %6u",
346                    V(p.ppp_obytes),
347                    V(p.ppp_opackets),
348                    V(vj.vjs_compressed));
349             if (!rflag)
350                 printf(" %6u %6u",
351                        V(vj.vjs_packets) - V(vj.vjs_compressed),
352                        V(p.ppp_opackets) - V(vj.vjs_packets));
353             if (vflag)
354                 printf(" %6u %6u",
355                        V(vj.vjs_searches),
356                        V(vj.vjs_misses));
357             if (rflag) {
358                 printf(" %6.2f ", CRATE(c));
359                 if (ratef)
360                     printf("%6.2f", KBPS(W(c.unc_bytes)));
361                 else
362                     printf("%6u", W(c.unc_bytes));
363             }
364
365         }
366
367         putchar('\n');
368         fflush(stdout);
369         line++;
370
371         count--;
372         if (!infinite && !count)
373             break;
374
375         sigemptyset(&mask);
376         sigaddset(&mask, SIGALRM);
377         sigprocmask(SIG_BLOCK, &mask, &oldmask);
378         if (!signalled) {
379             sigemptyset(&mask);
380             sigsuspend(&mask);
381         }
382         sigprocmask(SIG_SETMASK, &oldmask, NULL);
383         signalled = 0;
384         (void)alarm(interval);
385
386         if (!aflag) {
387             old = cur;
388             ocs = ccs;
389             ratef = dflag;
390         }
391     }
392 }
393
394 int
395 main(argc, argv)
396     int argc;
397     char *argv[];
398 {
399     int c;
400 #ifdef STREAMS
401     char *dev;
402 #endif
403
404     interface = "ppp0";
405     if ((progname = strrchr(argv[0], '/')) == NULL)
406         progname = argv[0];
407     else
408         ++progname;
409
410     while ((c = getopt(argc, argv, "advrzc:w:")) != -1) {
411         switch (c) {
412         case 'a':
413             ++aflag;
414             break;
415         case 'd':
416             ++dflag;
417             break;
418         case 'v':
419             ++vflag;
420             break;
421         case 'r':
422             ++rflag;
423             break;
424         case 'z':
425             ++zflag;
426             break;
427         case 'c':
428             count = atoi(optarg);
429             if (count <= 0)
430                 usage();
431             break;
432         case 'w':
433             interval = atoi(optarg);
434             if (interval <= 0)
435                 usage();
436             break;
437         default:
438             usage();
439         }
440     }
441     argc -= optind;
442     argv += optind;
443
444     if (!interval && count)
445         interval = 5;
446     if (interval && !count)
447         infinite = 1;
448     if (!interval && !count)
449         count = 1;
450     if (aflag)
451         dflag = 0;
452
453     if (argc > 1)
454         usage();
455     if (argc > 0)
456         interface = argv[0];
457
458     if (sscanf(interface, "ppp%d", &unit) != 1) {
459         fprintf(stderr, "%s: invalid interface '%s' specified\n",
460                 progname, interface);
461     }
462
463 #ifndef STREAMS
464     {
465         struct ifreq ifr;
466
467         s = socket(AF_INET, SOCK_DGRAM, 0);
468         if (s < 0) {
469             fprintf(stderr, "%s: ", progname);
470             perror("couldn't create IP socket");
471             exit(1);
472         }
473
474 #ifdef _linux_
475 #undef  ifr_name
476 #define ifr_name ifr_ifrn.ifrn_name
477 #endif
478         strncpy(ifr.ifr_name, interface, sizeof(ifr.ifr_name));
479         if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
480             fprintf(stderr, "%s: nonexistent interface '%s' specified\n",
481                     progname, interface);
482             exit(1);
483         }
484     }
485
486 #else   /* STREAMS */
487 #ifdef __osf__
488     dev = "/dev/streams/ppp";
489 #else
490     dev = "/dev/ppp";
491 #endif
492     if ((s = open(dev, O_RDONLY)) < 0) {
493         fprintf(stderr, "%s: couldn't open ", progname);
494         perror(dev);
495         exit(1);
496     }
497     if (strioctl(s, PPPIO_ATTACH, &unit, sizeof(int), 0) < 0) {
498         fprintf(stderr, "%s: ppp%d is not available\n", progname, unit);
499         exit(1);
500     }
501
502 #endif  /* STREAMS */
503
504     intpr();
505     exit(0);
506 }