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