xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.bin/pppstats/pppstats.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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
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
145 catchalarm(arg)
146     int arg;
147 {
148     signalled = 1;
149 }
150 
151 
152 #ifndef STREAMS
153 static void
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
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
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
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
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
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
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