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