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