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