1238384Sjkim/*	$NetBSD: pppstats.c,v 1.2 2006/03/02 17:32:28 christos Exp $	*/
2238384Sjkim
3238384Sjkim/*
4238384Sjkim * print PPP statistics:
5238384Sjkim * 	pppstats [-a|-d] [-v|-r|-z] [-c count] [-w wait] [interface]
6238384Sjkim *
7238384Sjkim *   -a Show absolute values rather than deltas
8238384Sjkim *   -d Show data rate (kB/s) rather than bytes
9238384Sjkim *   -v Show more stats for VJ TCP header compression
10238384Sjkim *   -r Show compression ratio
11238384Sjkim *   -z Show compression statistics instead of default display
12238384Sjkim *
13238384Sjkim * History:
14238384Sjkim *      perkins@cps.msu.edu: Added compression statistics and alternate
15238384Sjkim *                display. 11/94
16238384Sjkim *	Brad Parker (brad@cayman.com) 6/92
17238384Sjkim *
18238384Sjkim * from the original "slstats" by Van Jacobson
19238384Sjkim *
20238384Sjkim * Copyright (c) 1989 Regents of the University of California.
21238384Sjkim * All rights reserved.
22238384Sjkim *
23238384Sjkim * Redistribution and use in source and binary forms are permitted
24238384Sjkim * provided that the above copyright notice and this paragraph are
25238384Sjkim * duplicated in all such forms and that any documentation,
26238384Sjkim * advertising materials, and other materials related to such
27238384Sjkim * distribution and use acknowledge that the software was developed
28238384Sjkim * by the University of California, Berkeley.  The name of the
29238384Sjkim * University may not be used to endorse or promote products derived
30238384Sjkim * from this software without specific prior written permission.
31238384Sjkim * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
32238384Sjkim * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
33238384Sjkim * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
34238384Sjkim */
35238384Sjkim
36238384Sjkim#ifndef __STDC__
37238384Sjkim#define const
38238384Sjkim#endif
39238384Sjkim
40238384Sjkim#include <sys/cdefs.h>
41238384Sjkim#ifndef lint
42238384Sjkim#if 0
43238384Sjkimstatic const char rcsid[] = "Id: pppstats.c,v 1.29 2002/10/27 12:56:26 fcusack Exp";
44238384Sjkim#else
45238384Sjkim__RCSID("$NetBSD: pppstats.c,v 1.2 2006/03/02 17:32:28 christos Exp $");
46238384Sjkim#endif
47238384Sjkim#endif
48238384Sjkim
49238384Sjkim#include <stdio.h>
50238384Sjkim#include <stddef.h>
51238384Sjkim#include <stdlib.h>
52238384Sjkim#include <string.h>
53238384Sjkim#include <ctype.h>
54238384Sjkim#include <errno.h>
55238384Sjkim#include <signal.h>
56238384Sjkim#include <fcntl.h>
57238384Sjkim#include <unistd.h>
58238384Sjkim#include <sys/param.h>
59238384Sjkim#include <sys/types.h>
60238384Sjkim#include <sys/ioctl.h>
61238384Sjkim
62238384Sjkim#ifndef STREAMS
63238384Sjkim#if defined(__linux__) && defined(__powerpc__) \
64238384Sjkim    && (__GLIBC__ == 2 && __GLIBC_MINOR__ == 0)
65238384Sjkim/* kludge alert! */
66238384Sjkim#undef __GLIBC__
67238384Sjkim#endif
68238384Sjkim#include <sys/socket.h>		/* *BSD, Linux, NeXT, Ultrix etc. */
69238384Sjkim#ifndef __linux__
70238384Sjkim#include <net/if.h>
71238384Sjkim#include <net/ppp_defs.h>
72238384Sjkim#include <net/if_ppp.h>
73238384Sjkim#else
74238384Sjkim/* Linux */
75238384Sjkim#if __GLIBC__ >= 2
76238384Sjkim#include <asm/types.h>		/* glibc 2 conflicts with linux/types.h */
77238384Sjkim#include <net/if.h>
78238384Sjkim#else
79238384Sjkim#include <linux/types.h>
80238384Sjkim#include <linux/if.h>
81238384Sjkim#endif
82238384Sjkim#include <linux/ppp_defs.h>
83238384Sjkim#include <linux/if_ppp.h>
84238384Sjkim#endif /* __linux__ */
85238384Sjkim
86238384Sjkim#else	/* STREAMS */
87238384Sjkim#include <sys/stropts.h>	/* SVR4, Solaris 2, SunOS 4, OSF/1, etc. */
88238384Sjkim#include <net/ppp_defs.h>
89238384Sjkim#include <net/pppio.h>
90238384Sjkim
91238384Sjkim#endif	/* STREAMS */
92238384Sjkim
93238384Sjkimint	vflag, rflag, zflag;	/* select type of display */
94238384Sjkimint	aflag;			/* print absolute values, not deltas */
95238384Sjkimint	dflag;			/* print data rates, not bytes */
96238384Sjkimint	interval, count;
97238384Sjkimint	infinite;
98238384Sjkimint	unit;
99238384Sjkimint	s;			/* socket or /dev/ppp file descriptor */
100238384Sjkimint	signalled;		/* set if alarm goes off "early" */
101238384Sjkimchar	*progname;
102238384Sjkimchar	*interface;
103238384Sjkim
104238384Sjkim#if defined(SUNOS4) || defined(ULTRIX) || defined(NeXT)
105238384Sjkimextern int optind;
106238384Sjkimextern char *optarg;
107238384Sjkim#endif
108238384Sjkim
109238384Sjkim/*
110238384Sjkim * If PPP_DRV_NAME is not defined, use the legacy "ppp" as the
111238384Sjkim * device name.
112238384Sjkim */
113238384Sjkim#if !defined(PPP_DRV_NAME)
114238384Sjkim#define PPP_DRV_NAME    "ppp"
115238384Sjkim#endif /* !defined(PPP_DRV_NAME) */
116238384Sjkim#if !defined(SL_DRV_NAME)
117238384Sjkim#define SL_DRV_NAME    "sl"
118238384Sjkim#endif /* !defined(SL_DRV_NAME) */
119238384Sjkim
120238384Sjkimstatic void usage __P((void));
121238384Sjkimstatic void catchalarm __P((int));
122238384Sjkimstatic void get_ppp_stats __P((struct ppp_stats *));
123238384Sjkimstatic void get_ppp_cstats __P((struct ppp_comp_stats *));
124238384Sjkimstatic void intpr __P((void));
125238384Sjkim
126238384Sjkimint main __P((int, char *argv[]));
127238384Sjkim
128238384Sjkimstatic void
129238384Sjkimusage()
130238384Sjkim{
131238384Sjkim    fprintf(stderr, "Usage: %s [-a|-d] [-v|-r|-z] [-c count] [-w wait] [interface]\n",
132238384Sjkim	    progname);
133238384Sjkim    exit(1);
134238384Sjkim}
135238384Sjkim
136238384Sjkim/*
137238384Sjkim * Called if an interval expires before intpr has completed a loop.
138238384Sjkim * Sets a flag to not wait for the alarm.
139238384Sjkim */
140238384Sjkimstatic void
141238384Sjkimcatchalarm(arg)
142238384Sjkim    int arg;
143238384Sjkim{
144238384Sjkim    signalled = 1;
145238384Sjkim}
146238384Sjkim
147238384Sjkim
148238384Sjkim#ifndef STREAMS
149238384Sjkimstatic void
150238384Sjkimget_ppp_stats(curp)
151238384Sjkim    struct ppp_stats *curp;
152238384Sjkim{
153238384Sjkim    struct ifpppstatsreq req;
154238384Sjkim
155238384Sjkim    memset (&req, 0, sizeof (req));
156238384Sjkim
157238384Sjkim#ifdef __linux__
158238384Sjkim    req.stats_ptr = (caddr_t) &req.stats;
159238384Sjkim#undef ifr_name
160238384Sjkim#define ifr_name ifr__name
161238384Sjkim#endif
162238384Sjkim
163238384Sjkim    strncpy(req.ifr_name, interface, sizeof(req.ifr_name));
164238384Sjkim    if (ioctl(s, SIOCGPPPSTATS, &req) < 0) {
165238384Sjkim	fprintf(stderr, "%s: ", progname);
166238384Sjkim	if (errno == ENOTTY)
167238384Sjkim	    fprintf(stderr, "kernel support missing\n");
168238384Sjkim	else
169238384Sjkim	    perror("couldn't get PPP statistics");
170238384Sjkim	exit(1);
171238384Sjkim    }
172238384Sjkim    *curp = req.stats;
173238384Sjkim}
174238384Sjkim
175238384Sjkimstatic void
176238384Sjkimget_ppp_cstats(csp)
177238384Sjkim    struct ppp_comp_stats *csp;
178238384Sjkim{
179238384Sjkim    struct ifpppcstatsreq creq;
180238384Sjkim
181238384Sjkim    memset (&creq, 0, sizeof (creq));
182238384Sjkim
183238384Sjkim#ifdef __linux__
184238384Sjkim    creq.stats_ptr = (caddr_t) &creq.stats;
185238384Sjkim#undef  ifr_name
186238384Sjkim#define ifr_name ifr__name
187238384Sjkim#endif
188238384Sjkim
189238384Sjkim    strncpy(creq.ifr_name, interface, sizeof(creq.ifr_name));
190238384Sjkim    if (ioctl(s, SIOCGPPPCSTATS, &creq) < 0) {
191238384Sjkim	fprintf(stderr, "%s: ", progname);
192238384Sjkim	if (errno == ENOTTY) {
193238384Sjkim	    fprintf(stderr, "no kernel compression support\n");
194238384Sjkim	    if (zflag)
195238384Sjkim		exit(1);
196238384Sjkim	    rflag = 0;
197238384Sjkim	} else {
198238384Sjkim	    perror("couldn't get PPP compression stats");
199238384Sjkim	    exit(1);
200238384Sjkim	}
201238384Sjkim    }
202238384Sjkim
203238384Sjkim#ifdef __linux__
204238384Sjkim    if (creq.stats.c.bytes_out == 0) {
205238384Sjkim	creq.stats.c.bytes_out = creq.stats.c.comp_bytes + creq.stats.c.inc_bytes;
206238384Sjkim	creq.stats.c.in_count = creq.stats.c.unc_bytes;
207238384Sjkim    }
208238384Sjkim    if (creq.stats.c.bytes_out == 0)
209238384Sjkim	creq.stats.c.ratio = 0.0;
210238384Sjkim    else
211238384Sjkim	creq.stats.c.ratio = 256.0 * creq.stats.c.in_count /
212238384Sjkim			     creq.stats.c.bytes_out;
213238384Sjkim
214238384Sjkim    if (creq.stats.d.bytes_out == 0) {
215238384Sjkim	creq.stats.d.bytes_out = creq.stats.d.comp_bytes + creq.stats.d.inc_bytes;
216238384Sjkim	creq.stats.d.in_count = creq.stats.d.unc_bytes;
217238384Sjkim    }
218238384Sjkim    if (creq.stats.d.bytes_out == 0)
219238384Sjkim	creq.stats.d.ratio = 0.0;
220238384Sjkim    else
221238384Sjkim	creq.stats.d.ratio = 256.0 * creq.stats.d.in_count /
222238384Sjkim			     creq.stats.d.bytes_out;
223238384Sjkim#endif
224238384Sjkim
225238384Sjkim    *csp = creq.stats;
226238384Sjkim}
227238384Sjkim
228238384Sjkim#else	/* STREAMS */
229238384Sjkim
230238384Sjkimint
231238384Sjkimstrioctl(fd, cmd, ptr, ilen, olen)
232238384Sjkim    int fd, cmd, ilen, olen;
233238384Sjkim    char *ptr;
234238384Sjkim{
235238384Sjkim    struct strioctl str;
236238384Sjkim
237238384Sjkim    str.ic_cmd = cmd;
238238384Sjkim    str.ic_timout = 0;
239238384Sjkim    str.ic_len = ilen;
240238384Sjkim    str.ic_dp = ptr;
241238384Sjkim    if (ioctl(fd, I_STR, &str) == -1)
242238384Sjkim	return -1;
243238384Sjkim    if (str.ic_len != olen)
244238384Sjkim	fprintf(stderr, "strioctl: expected %d bytes, got %d for cmd %x\n",
245238384Sjkim	       olen, str.ic_len, cmd);
246238384Sjkim    return 0;
247238384Sjkim}
248238384Sjkim
249238384Sjkimstatic void
250238384Sjkimget_ppp_stats(curp)
251238384Sjkim    struct ppp_stats *curp;
252238384Sjkim{
253238384Sjkim    if (strioctl(s, PPPIO_GETSTAT, curp, 0, sizeof(*curp)) < 0) {
254238384Sjkim	fprintf(stderr, "%s: ", progname);
255238384Sjkim	if (errno == EINVAL)
256238384Sjkim	    fprintf(stderr, "kernel support missing\n");
257238384Sjkim	else
258238384Sjkim	    perror("couldn't get PPP statistics");
259238384Sjkim	exit(1);
260238384Sjkim    }
261238384Sjkim}
262238384Sjkim
263238384Sjkimstatic void
264238384Sjkimget_ppp_cstats(csp)
265238384Sjkim    struct ppp_comp_stats *csp;
266238384Sjkim{
267238384Sjkim    if (strioctl(s, PPPIO_GETCSTAT, csp, 0, sizeof(*csp)) < 0) {
268238384Sjkim	fprintf(stderr, "%s: ", progname);
269238384Sjkim	if (errno == ENOTTY) {
270238384Sjkim	    fprintf(stderr, "no kernel compression support\n");
271238384Sjkim	    if (zflag)
272238384Sjkim		exit(1);
273238384Sjkim	    rflag = 0;
274238384Sjkim	} else {
275238384Sjkim	    perror("couldn't get PPP compression statistics");
276238384Sjkim	    exit(1);
277238384Sjkim	}
278238384Sjkim    }
279238384Sjkim}
280238384Sjkim
281238384Sjkim#endif /* STREAMS */
282238384Sjkim
283238384Sjkim#define MAX0(a)		((int)(a) > 0? (a): 0)
284238384Sjkim#define V(offset)	MAX0(cur.offset - old.offset)
285238384Sjkim#define W(offset)	MAX0(ccs.offset - ocs.offset)
286238384Sjkim
287238384Sjkim#define RATIO(c, i, u)	((c) == 0? 1.0: (u) / ((double)(c) + (i)))
288238384Sjkim#define CRATE(x)	RATIO(W(x.comp_bytes), W(x.inc_bytes), W(x.unc_bytes))
289238384Sjkim
290238384Sjkim#define KBPS(n)		((n) / (interval * 1000.0))
291238384Sjkim
292238384Sjkim/*
293238384Sjkim * Print a running summary of interface statistics.
294238384Sjkim * Repeat display every interval seconds, showing statistics
295238384Sjkim * collected over that interval.  Assumes that interval is non-zero.
296238384Sjkim * First line printed is cumulative.
297238384Sjkim */
298238384Sjkimstatic void
299238384Sjkimintpr()
300238384Sjkim{
301238384Sjkim    register int line = 0;
302238384Sjkim    sigset_t oldmask, mask;
303238384Sjkim    char *bunit;
304238384Sjkim    int ratef = 0;
305238384Sjkim    struct ppp_stats cur, old;
306238384Sjkim    struct ppp_comp_stats ccs, ocs;
307238384Sjkim
308238384Sjkim    memset(&ccs, 0, sizeof(ccs));	/* XXX gcc */
309238384Sjkim    memset(&old, 0, sizeof(old));
310238384Sjkim    memset(&ocs, 0, sizeof(ocs));
311238384Sjkim
312238384Sjkim    while (1) {
313238384Sjkim	get_ppp_stats(&cur);
314238384Sjkim	if (zflag || rflag)
315238384Sjkim	    get_ppp_cstats(&ccs);
316238384Sjkim
317238384Sjkim	(void)signal(SIGALRM, catchalarm);
318238384Sjkim	signalled = 0;
319238384Sjkim	(void)alarm(interval);
320238384Sjkim
321238384Sjkim	if ((line % 20) == 0) {
322238384Sjkim	    if (zflag) {
323238384Sjkim		printf("IN:  COMPRESSED  INCOMPRESSIBLE   COMP | ");
324238384Sjkim		printf("OUT: COMPRESSED  INCOMPRESSIBLE   COMP\n");
325238384Sjkim		bunit = dflag? "KB/S": "BYTE";
326238384Sjkim		printf("    %s   PACK     %s   PACK  RATIO | ", bunit, bunit);
327238384Sjkim		printf("    %s   PACK     %s   PACK  RATIO", bunit, bunit);
328	    } else {
329		printf("%8.8s %6.6s %6.6s",
330		       "IN", "PACK", "VJCOMP");
331
332		if (!rflag)
333		    printf(" %6.6s %6.6s", "VJUNC", "VJERR");
334		if (vflag)
335		    printf(" %6.6s %6.6s", "VJTOSS", "NON-VJ");
336		if (rflag)
337		    printf(" %6.6s %6.6s", "RATIO", "UBYTE");
338		printf("  | %8.8s %6.6s %6.6s",
339		       "OUT", "PACK", "VJCOMP");
340
341		if (!rflag)
342		    printf(" %6.6s %6.6s", "VJUNC", "NON-VJ");
343		if (vflag)
344		    printf(" %6.6s %6.6s", "VJSRCH", "VJMISS");
345		if (rflag)
346		    printf(" %6.6s %6.6s", "RATIO", "UBYTE");
347	    }
348	    putchar('\n');
349	}
350
351	if (zflag) {
352	    if (ratef) {
353		printf("%8.3f %6u %8.3f %6u %6.2f",
354		       KBPS(W(d.comp_bytes)),
355		       W(d.comp_packets),
356		       KBPS(W(d.inc_bytes)),
357		       W(d.inc_packets),
358		       ccs.d.ratio / 256.0);
359		printf(" | %8.3f %6u %8.3f %6u %6.2f",
360		       KBPS(W(c.comp_bytes)),
361		       W(c.comp_packets),
362		       KBPS(W(c.inc_bytes)),
363		       W(c.inc_packets),
364		       ccs.c.ratio / 256.0);
365	    } else {
366		printf("%8u %6u %8u %6u %6.2f",
367		       W(d.comp_bytes),
368		       W(d.comp_packets),
369		       W(d.inc_bytes),
370		       W(d.inc_packets),
371		       ccs.d.ratio / 256.0);
372		printf(" | %8u %6u %8u %6u %6.2f",
373		       W(c.comp_bytes),
374		       W(c.comp_packets),
375		       W(c.inc_bytes),
376		       W(c.inc_packets),
377		       ccs.c.ratio / 256.0);
378	    }
379
380	} else {
381	    if (ratef)
382		printf("%8.3f", KBPS(V(p.ppp_ibytes)));
383	    else
384		printf("%8u", V(p.ppp_ibytes));
385	    printf(" %6u %6u",
386		   V(p.ppp_ipackets),
387		   V(vj.vjs_compressedin));
388	    if (!rflag)
389		printf(" %6u %6u",
390		       V(vj.vjs_uncompressedin),
391		       V(vj.vjs_errorin));
392	    if (vflag)
393		printf(" %6u %6u",
394		       V(vj.vjs_tossed),
395		       V(p.ppp_ipackets) - V(vj.vjs_compressedin)
396		       - V(vj.vjs_uncompressedin) - V(vj.vjs_errorin));
397	    if (rflag) {
398		printf(" %6.2f ", CRATE(d));
399		if (ratef)
400		    printf("%6.2f", KBPS(W(d.unc_bytes)));
401		else
402		    printf("%6u", W(d.unc_bytes));
403	    }
404	    if (ratef)
405		printf("  | %8.3f", KBPS(V(p.ppp_obytes)));
406	    else
407		printf("  | %8u", V(p.ppp_obytes));
408	    printf(" %6u %6u",
409		   V(p.ppp_opackets),
410		   V(vj.vjs_compressed));
411	    if (!rflag)
412		printf(" %6u %6u",
413		       V(vj.vjs_packets) - V(vj.vjs_compressed),
414		       V(p.ppp_opackets) - V(vj.vjs_packets));
415	    if (vflag)
416		printf(" %6u %6u",
417		       V(vj.vjs_searches),
418		       V(vj.vjs_misses));
419	    if (rflag) {
420		printf(" %6.2f ", CRATE(c));
421		if (ratef)
422		    printf("%6.2f", KBPS(W(c.unc_bytes)));
423		else
424		    printf("%6u", W(c.unc_bytes));
425	    }
426
427	}
428
429	putchar('\n');
430	fflush(stdout);
431	line++;
432
433	count--;
434	if (!infinite && !count)
435	    break;
436
437	sigemptyset(&mask);
438	sigaddset(&mask, SIGALRM);
439	sigprocmask(SIG_BLOCK, &mask, &oldmask);
440	if (!signalled) {
441	    sigemptyset(&mask);
442	    sigsuspend(&mask);
443	}
444	sigprocmask(SIG_SETMASK, &oldmask, NULL);
445	signalled = 0;
446	(void)alarm(interval);
447
448	if (!aflag) {
449	    old = cur;
450	    ocs = ccs;
451	    ratef = dflag;
452	}
453    }
454}
455
456int
457main(argc, argv)
458    int argc;
459    char *argv[];
460{
461    int c;
462#ifdef STREAMS
463    char *dev;
464#endif
465    const char *fmt;
466
467    if ((progname = strrchr(argv[0], '/')) == NULL)
468	progname = argv[0];
469    else
470	++progname;
471
472    if (strncmp(progname, SL_DRV_NAME, sizeof(SL_DRV_NAME) - 1) == 0) {
473	interface = SL_DRV_NAME "0";
474	fmt =  SL_DRV_NAME "%d";
475    } else {
476	interface = PPP_DRV_NAME "0";
477	fmt =  PPP_DRV_NAME "%d";
478    }
479
480    while ((c = getopt(argc, argv, "advrzc:w:")) != -1) {
481	switch (c) {
482	case 'a':
483	    ++aflag;
484	    break;
485	case 'd':
486	    ++dflag;
487	    break;
488	case 'v':
489	    ++vflag;
490	    break;
491	case 'r':
492	    ++rflag;
493	    break;
494	case 'z':
495	    ++zflag;
496	    break;
497	case 'c':
498	    count = atoi(optarg);
499	    if (count <= 0)
500		usage();
501	    break;
502	case 'w':
503	    interval = atoi(optarg);
504	    if (interval <= 0)
505		usage();
506	    break;
507	default:
508	    usage();
509	}
510    }
511    argc -= optind;
512    argv += optind;
513
514    if (!interval && count)
515	interval = 5;
516    if (interval && !count)
517	infinite = 1;
518    if (!interval && !count)
519	count = 1;
520    if (aflag)
521	dflag = 0;
522
523    if (argc > 1)
524	usage();
525    if (argc > 0)
526	interface = argv[0];
527
528    if (sscanf(interface, fmt, &unit) != 1) {
529	fprintf(stderr, "%s: invalid interface '%s' specified\n",
530		progname, interface);
531    }
532
533#ifndef STREAMS
534    {
535	struct ifreq ifr;
536
537	s = socket(AF_INET, SOCK_DGRAM, 0);
538	if (s < 0) {
539	    fprintf(stderr, "%s: ", progname);
540	    perror("couldn't create IP socket");
541	    exit(1);
542	}
543
544#ifdef __linux__
545#undef  ifr_name
546#define ifr_name ifr_ifrn.ifrn_name
547#endif
548	strncpy(ifr.ifr_name, interface, sizeof(ifr.ifr_name));
549	if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
550	    fprintf(stderr, "%s: nonexistent interface '%s' specified\n",
551		    progname, interface);
552	    exit(1);
553	}
554    }
555
556#else	/* STREAMS */
557#ifdef __osf__
558    dev = "/dev/streams/ppp";
559#else
560    dev = "/dev/" PPP_DRV_NAME;
561#endif
562    if ((s = open(dev, O_RDONLY)) < 0) {
563	fprintf(stderr, "%s: couldn't open ", progname);
564	perror(dev);
565	exit(1);
566    }
567    if (strioctl(s, PPPIO_ATTACH, &unit, sizeof(int), 0) < 0) {
568	fprintf(stderr, "%s: ppp%d is not available\n", progname, unit);
569	exit(1);
570    }
571
572#endif	/* STREAMS */
573
574    intpr();
575    exit(0);
576}
577