xref: /openbsd-src/usr.sbin/pppd/pppstats/pppstats.c (revision 6b5bf2e84b5595420244220f1e6b22c62a3dc89b)
1 /*	$OpenBSD: pppstats.c,v 1.15 2024/11/04 11:12:52 deraadt 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  * Contributed by Van Jacobson (van@ee.lbl.gov), Dec 31, 1989.
21  *
22  * Copyright (c) 1989, 1990, 1991, 1992 Regents of the University of
23  * California. All rights reserved.
24  *
25  * Redistribution and use in source and binary forms, with or without
26  * modification, are permitted provided that the following conditions
27  * are met:
28  * 1. Redistributions of source code must retain the above copyright
29  *    notice, this list of conditions and the following disclaimer.
30  * 2. Redistributions in binary form must reproduce the above copyright
31  *    notice, this list of conditions and the following disclaimer in the
32  *    documentation and/or other materials provided with the distribution.
33  * 3. Neither the name of the University nor the names of its contributors
34  *    may be used to endorse or promote products derived from this software
35  *    without specific prior written permission.
36  *
37  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
38  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
39  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
40  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
41  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
42  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
43  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
44  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
45  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
46  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47  * SUCH DAMAGE.
48  */
49 
50 #include <sys/types.h>
51 #include <sys/ioctl.h>
52 #include <sys/socket.h>
53 #include <net/ppp_defs.h>
54 #include <net/if.h>
55 #include <net/if_ppp.h>
56 #include <stdio.h>
57 #include <stddef.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #include <ctype.h>
61 #include <errno.h>
62 #include <signal.h>
63 #include <fcntl.h>
64 #include <err.h>
65 #include <unistd.h>
66 
67 int	vflag, rflag, zflag;	/* select type of display */
68 int	aflag;			/* print absolute values, not deltas */
69 int	dflag;			/* print data rates, not bytes */
70 int	interval, count;
71 int	infinite;
72 int	unit;
73 int	s;			/* socket file descriptor */
74 int	signalled;		/* set if alarm goes off "early" */
75 char	interface[IFNAMSIZ];
76 
77 void usage(void);
78 void catchalarm(int);
79 void get_ppp_stats(struct ppp_stats *);
80 void get_ppp_cstats(struct ppp_comp_stats *);
81 void intpr(void);
82 int main(int, char *argv[]);
83 
84 void
85 usage(void)
86 {
87 	extern char *__progname;
88 
89 	fprintf(stderr,
90 	    "usage: %s [-adrvz] [-c count] [-w wait] [interface]\n",
91 	    __progname);
92 	exit(1);
93 }
94 
95 /*
96  * Called if an interval expires before intpr has completed a loop.
97  * Sets a flag to not wait for the alarm.
98  */
99 void
100 catchalarm(int arg)
101 {
102 	signalled = 1;
103 }
104 
105 void
106 get_ppp_stats(struct ppp_stats *curp)
107 {
108 	struct ifpppstatsreq req;
109 
110 	memset(&req, 0, sizeof(req));
111 	(void)strlcpy(req.ifr_name, interface, sizeof(req.ifr_name));
112 
113 	if (ioctl(s, SIOCGPPPSTATS, &req) < 0) {
114 		if (errno == ENOTTY)
115 			errx(1, "kernel support missing");
116 		else
117 			err(1, "couldn't get PPP statistics");
118 	}
119 	*curp = req.stats;
120 }
121 
122 void
123 get_ppp_cstats(struct ppp_comp_stats *csp)
124 {
125 	struct ifpppcstatsreq creq;
126 
127 	memset(&creq, 0, sizeof(creq));
128 	(void)strlcpy(creq.ifr_name, interface, sizeof(creq.ifr_name));
129 
130 	if (ioctl(s, SIOCGPPPCSTATS, &creq) < 0) {
131 		if (errno == ENOTTY) {
132 			warnx("no kernel compression support");
133 			if (zflag)
134 				exit(1);
135 			rflag = 0;
136 		} else
137 			err(1, "couldn't get PPP compression stats");
138 	}
139 	*csp = creq.stats;
140 }
141 
142 #define MAX0(a)		((int)(a) > 0? (a): 0)
143 #define V(offset)	MAX0(cur.offset - old.offset)
144 #define W(offset)	MAX0(ccs.offset - ocs.offset)
145 
146 #define RATIO(c, i, u)	((c) == 0? 1.0: (u) / ((double)(c) + (i)))
147 #define CRATE(x)	RATIO(W(x.comp_bytes), W(x.inc_bytes), W(x.unc_bytes))
148 
149 #define KBPS(n)		((n) / (interval * 1000.0))
150 
151 /*
152  * Print a running summary of interface statistics.
153  * Repeat display every interval seconds, showing statistics
154  * collected over that interval.  Assumes that interval is non-zero.
155  * First line printed is cumulative.
156  */
157 void
158 intpr(void)
159 {
160 	register int line = 0;
161 	sigset_t oldmask, mask;
162 	char *bunit;
163 	int ratef = 0;
164 	struct ppp_stats cur, old;
165 	struct ppp_comp_stats ccs, ocs;
166 
167 	memset(&old, 0, sizeof(old));
168 	memset(&ocs, 0, sizeof(ocs));
169 
170 	for (;;) {
171 		get_ppp_stats(&cur);
172 		if (zflag || rflag)
173 			get_ppp_cstats(&ccs);
174 
175 		(void)signal(SIGALRM, catchalarm);
176 		signalled = 0;
177 		(void)alarm(interval);
178 
179 		if ((line % 20) == 0) {
180 			if (zflag) {
181 				printf("IN:  COMPRESSED  INCOMPRESSIBLE   COMP | ");
182 				printf("OUT: COMPRESSED  INCOMPRESSIBLE   COMP\n");
183 				bunit = dflag? "KB/S": "BYTE";
184 				printf("    %s   PACK     %s   PACK  RATIO | ",
185 				    bunit, bunit);
186 				printf("    %s   PACK     %s   PACK  RATIO",
187 				    bunit, bunit);
188 			} else {
189 				printf("%8.8s %6.6s %6.6s",
190 					"IN", "PACK", "VJCOMP");
191 
192 				if (!rflag)
193 					printf(" %6.6s %6.6s", "VJUNC", "VJERR");
194 				if (vflag)
195 					printf(" %6.6s %6.6s", "VJTOSS", "NON-VJ");
196 				if (rflag)
197 					printf(" %6.6s %6.6s", "RATIO", "UBYTE");
198 				printf("  | %8.8s %6.6s %6.6s",
199 					"OUT", "PACK", "VJCOMP");
200 
201 				if (!rflag)
202 					printf(" %6.6s %6.6s", "VJUNC", "NON-VJ");
203 				if (vflag)
204 					printf(" %6.6s %6.6s", "VJSRCH", "VJMISS");
205 				if (rflag)
206 					printf(" %6.6s %6.6s", "RATIO", "UBYTE");
207 			}
208 			putchar('\n');
209 		}
210 
211 		if (zflag) {
212 			if (ratef) {
213 				printf("%8.3f %6u %8.3f %6u %6.2f",
214 				    KBPS(W(d.comp_bytes)), W(d.comp_packets),
215 				    KBPS(W(d.inc_bytes)), W(d.inc_packets),
216 				    ccs.d.ratio * 256.0);
217 
218 				printf(" | %8.3f %6u %8.3f %6u %6.2f",
219 				    KBPS(W(c.comp_bytes)), W(c.comp_packets),
220 				    KBPS(W(c.inc_bytes)), W(c.inc_packets),
221 				    ccs.c.ratio * 256.0);
222 			} else {
223 				printf("%8u %6u %8u %6u %6.2f",
224 				   W(d.comp_bytes), W(d.comp_packets),
225 				   W(d.inc_bytes), W(d.inc_packets),
226 				   ccs.d.ratio * 256.0);
227 
228 				printf(" | %8u %6u %8u %6u %6.2f",
229 				   W(c.comp_bytes), W(c.comp_packets),
230 				   W(c.inc_bytes), W(c.inc_packets),
231 				   ccs.c.ratio * 256.0);
232 			}
233 		} else {
234 			if (ratef)
235 				printf("%8.3f", KBPS(V(p.ppp_ibytes)));
236 			else
237 				printf("%8u", V(p.ppp_ibytes));
238 			printf(" %6u %6u", V(p.ppp_ipackets),
239 			    V(vj.vjs_compressedin));
240 			if (!rflag)
241 				printf(" %6u %6u", V(vj.vjs_uncompressedin),
242 				   V(vj.vjs_errorin));
243 			if (vflag)
244 				printf(" %6u %6u", V(vj.vjs_tossed),
245 				   V(p.ppp_ipackets) -
246 				   V(vj.vjs_compressedin) -
247 				   V(vj.vjs_uncompressedin) -
248 				   V(vj.vjs_errorin));
249 			if (rflag) {
250 				printf(" %6.2f ", CRATE(d));
251 				if (ratef)
252 					printf("%6.2f", KBPS(W(d.unc_bytes)));
253 				else
254 					printf("%6u", W(d.unc_bytes));
255 			}
256 			if (ratef)
257 				printf("  | %8.3f", KBPS(V(p.ppp_obytes)));
258 			else
259 				printf("  | %8u", V(p.ppp_obytes));
260 
261 			printf(" %6u %6u", V(p.ppp_opackets),
262 			    V(vj.vjs_compressed));
263 			if (!rflag)
264 				printf(" %6u %6u",
265 				   V(vj.vjs_packets) - V(vj.vjs_compressed),
266 				   V(p.ppp_opackets) - V(vj.vjs_packets));
267 			if (vflag)
268 				printf(" %6u %6u", V(vj.vjs_searches),
269 				   V(vj.vjs_misses));
270 			if (rflag) {
271 				printf(" %6.2f ", CRATE(c));
272 				if (ratef)
273 					printf("%6.2f", KBPS(W(c.unc_bytes)));
274 				else
275 					printf("%6u", W(c.unc_bytes));
276 			}
277 		}
278 
279 		putchar('\n');
280 		fflush(stdout);
281 		line++;
282 
283 		count--;
284 		if (!infinite && !count)
285 			break;
286 
287 		sigemptyset(&mask);
288 		sigaddset(&mask, SIGALRM);
289 		sigprocmask(SIG_BLOCK, &mask, &oldmask);
290 		if (signalled == 0) {
291 			sigemptyset(&mask);
292 			sigsuspend(&mask);
293 		}
294 		sigprocmask(SIG_SETMASK, &oldmask, NULL);
295 		signalled = 0;
296 		(void)alarm(interval);
297 		if (!aflag) {
298 			old = cur;
299 			ocs = ccs;
300 			ratef = dflag;
301 		}
302 	}
303 }
304 
305 int
306 main(int argc, char *argv[])
307 {
308 	const char *errstr;
309 	int c;
310 	struct ifreq ifr;
311 
312 	(void)strlcpy(interface, "ppp0", sizeof(interface));
313 
314 	while ((c = getopt(argc, argv, "advrzc:w:")) != -1) {
315 		switch (c) {
316 		case 'a':
317 			aflag = 1;
318 			break;
319 		case 'd':
320 			dflag = 1;
321 			break;
322 		case 'v':
323 			vflag = 1;
324 			break;
325 		case 'r':
326 			rflag = 1;
327 			break;
328 		case 'z':
329 			zflag = 1;
330 			break;
331 		case 'c':
332 			count = strtonum(optarg, 1, 1000, &errstr);
333 			if (errstr)
334 				usage();
335 			break;
336 		case 'w':
337 			interval = strtonum(optarg, 1, 1000, &errstr);
338 			if (errstr)
339 				usage();
340 			break;
341 		default:
342 			usage();
343 		}
344 	}
345 	argc -= optind;
346 	argv += optind;
347 
348 	if (!interval && count)
349 		interval = 5;
350 	if (interval && !count)
351 		infinite = 1;
352 	if (!interval && !count)
353 		count = 1;
354 	if (aflag)
355 		dflag = 0;
356 
357 	if (argc > 1)
358 		usage();
359 	if (argc > 0)
360 		(void)strlcpy(interface, argv[0], sizeof(interface));
361 
362 	if (sscanf(interface, "ppp%d", &unit) != 1 || unit < 0)
363 		errx(1, "invalid interface '%s' specified", interface);
364 
365 	s = socket(AF_INET, SOCK_DGRAM, 0);
366 	if (s < 0)
367 		err(1, "couldn't create IP socket");
368 	(void)strlcpy(ifr.ifr_name, interface, sizeof(ifr.ifr_name));
369 	if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0)
370 		errx(1, "nonexistent interface '%s' specified", interface);
371 
372 	intpr();
373 	exit(0);
374 }
375