xref: /netbsd-src/external/bsd/ppp/dist/pppstats/pppstats.c (revision 4b004442778f1201b2161e87fd65ba87aae6601a)
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
43 static 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 
93 int	vflag, rflag, zflag;	/* select type of display */
94 int	aflag;			/* print absolute values, not deltas */
95 int	dflag;			/* print data rates, not bytes */
96 int	interval, count;
97 int	infinite;
98 int	s;			/* socket or /dev/ppp file descriptor */
99 int	signalled;		/* set if alarm goes off "early" */
100 char	*progname;
101 char	*interface;
102 char	*fmt;
103 
104 #if defined(SUNOS4) || defined(ULTRIX) || defined(NeXT)
105 extern int optind;
106 extern 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 
120 static void usage(void);
121 static void catchalarm(int);
122 static void get_ppp_stats(struct ppp_stats *);
123 static void get_ppp_cstats(struct ppp_comp_stats *);
124 static void intpr(void);
125 
126 int main(int, char *argv[]);
127 
128 static void
129 usage(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  */
140 static void
141 catchalarm(int arg)
142 {
143     signalled = 1;
144 }
145 
146 
147 #ifndef STREAMS
148 static void
149 get_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 
174 static void
175 get_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 
229 int
230 strioctl(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 
246 static void
247 get_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 
259 static void
260 get_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  */
293 static void
294 intpr(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 
452 int
453 main(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