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