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