1238384Sjkim/* $NetBSD: pppstats.c,v 1.2 2006/03/02 17:32:28 christos Exp $ */ 2238384Sjkim 3238384Sjkim/* 4238384Sjkim * print PPP statistics: 5238384Sjkim * pppstats [-a|-d] [-v|-r|-z] [-c count] [-w wait] [interface] 6238384Sjkim * 7238384Sjkim * -a Show absolute values rather than deltas 8238384Sjkim * -d Show data rate (kB/s) rather than bytes 9238384Sjkim * -v Show more stats for VJ TCP header compression 10238384Sjkim * -r Show compression ratio 11238384Sjkim * -z Show compression statistics instead of default display 12238384Sjkim * 13238384Sjkim * History: 14238384Sjkim * perkins@cps.msu.edu: Added compression statistics and alternate 15238384Sjkim * display. 11/94 16238384Sjkim * Brad Parker (brad@cayman.com) 6/92 17238384Sjkim * 18238384Sjkim * from the original "slstats" by Van Jacobson 19238384Sjkim * 20238384Sjkim * Copyright (c) 1989 Regents of the University of California. 21238384Sjkim * All rights reserved. 22238384Sjkim * 23238384Sjkim * Redistribution and use in source and binary forms are permitted 24238384Sjkim * provided that the above copyright notice and this paragraph are 25238384Sjkim * duplicated in all such forms and that any documentation, 26238384Sjkim * advertising materials, and other materials related to such 27238384Sjkim * distribution and use acknowledge that the software was developed 28238384Sjkim * by the University of California, Berkeley. The name of the 29238384Sjkim * University may not be used to endorse or promote products derived 30238384Sjkim * from this software without specific prior written permission. 31238384Sjkim * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 32238384Sjkim * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 33238384Sjkim * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 34238384Sjkim */ 35238384Sjkim 36238384Sjkim#ifndef __STDC__ 37238384Sjkim#define const 38238384Sjkim#endif 39238384Sjkim 40238384Sjkim#include <sys/cdefs.h> 41238384Sjkim#ifndef lint 42238384Sjkim#if 0 43238384Sjkimstatic const char rcsid[] = "Id: pppstats.c,v 1.29 2002/10/27 12:56:26 fcusack Exp"; 44238384Sjkim#else 45238384Sjkim__RCSID("$NetBSD: pppstats.c,v 1.2 2006/03/02 17:32:28 christos Exp $"); 46238384Sjkim#endif 47238384Sjkim#endif 48238384Sjkim 49238384Sjkim#include <stdio.h> 50238384Sjkim#include <stddef.h> 51238384Sjkim#include <stdlib.h> 52238384Sjkim#include <string.h> 53238384Sjkim#include <ctype.h> 54238384Sjkim#include <errno.h> 55238384Sjkim#include <signal.h> 56238384Sjkim#include <fcntl.h> 57238384Sjkim#include <unistd.h> 58238384Sjkim#include <sys/param.h> 59238384Sjkim#include <sys/types.h> 60238384Sjkim#include <sys/ioctl.h> 61238384Sjkim 62238384Sjkim#ifndef STREAMS 63238384Sjkim#if defined(__linux__) && defined(__powerpc__) \ 64238384Sjkim && (__GLIBC__ == 2 && __GLIBC_MINOR__ == 0) 65238384Sjkim/* kludge alert! */ 66238384Sjkim#undef __GLIBC__ 67238384Sjkim#endif 68238384Sjkim#include <sys/socket.h> /* *BSD, Linux, NeXT, Ultrix etc. */ 69238384Sjkim#ifndef __linux__ 70238384Sjkim#include <net/if.h> 71238384Sjkim#include <net/ppp_defs.h> 72238384Sjkim#include <net/if_ppp.h> 73238384Sjkim#else 74238384Sjkim/* Linux */ 75238384Sjkim#if __GLIBC__ >= 2 76238384Sjkim#include <asm/types.h> /* glibc 2 conflicts with linux/types.h */ 77238384Sjkim#include <net/if.h> 78238384Sjkim#else 79238384Sjkim#include <linux/types.h> 80238384Sjkim#include <linux/if.h> 81238384Sjkim#endif 82238384Sjkim#include <linux/ppp_defs.h> 83238384Sjkim#include <linux/if_ppp.h> 84238384Sjkim#endif /* __linux__ */ 85238384Sjkim 86238384Sjkim#else /* STREAMS */ 87238384Sjkim#include <sys/stropts.h> /* SVR4, Solaris 2, SunOS 4, OSF/1, etc. */ 88238384Sjkim#include <net/ppp_defs.h> 89238384Sjkim#include <net/pppio.h> 90238384Sjkim 91238384Sjkim#endif /* STREAMS */ 92238384Sjkim 93238384Sjkimint vflag, rflag, zflag; /* select type of display */ 94238384Sjkimint aflag; /* print absolute values, not deltas */ 95238384Sjkimint dflag; /* print data rates, not bytes */ 96238384Sjkimint interval, count; 97238384Sjkimint infinite; 98238384Sjkimint unit; 99238384Sjkimint s; /* socket or /dev/ppp file descriptor */ 100238384Sjkimint signalled; /* set if alarm goes off "early" */ 101238384Sjkimchar *progname; 102238384Sjkimchar *interface; 103238384Sjkim 104238384Sjkim#if defined(SUNOS4) || defined(ULTRIX) || defined(NeXT) 105238384Sjkimextern int optind; 106238384Sjkimextern char *optarg; 107238384Sjkim#endif 108238384Sjkim 109238384Sjkim/* 110238384Sjkim * If PPP_DRV_NAME is not defined, use the legacy "ppp" as the 111238384Sjkim * device name. 112238384Sjkim */ 113238384Sjkim#if !defined(PPP_DRV_NAME) 114238384Sjkim#define PPP_DRV_NAME "ppp" 115238384Sjkim#endif /* !defined(PPP_DRV_NAME) */ 116238384Sjkim#if !defined(SL_DRV_NAME) 117238384Sjkim#define SL_DRV_NAME "sl" 118238384Sjkim#endif /* !defined(SL_DRV_NAME) */ 119238384Sjkim 120238384Sjkimstatic void usage __P((void)); 121238384Sjkimstatic void catchalarm __P((int)); 122238384Sjkimstatic void get_ppp_stats __P((struct ppp_stats *)); 123238384Sjkimstatic void get_ppp_cstats __P((struct ppp_comp_stats *)); 124238384Sjkimstatic void intpr __P((void)); 125238384Sjkim 126238384Sjkimint main __P((int, char *argv[])); 127238384Sjkim 128238384Sjkimstatic void 129238384Sjkimusage() 130238384Sjkim{ 131238384Sjkim fprintf(stderr, "Usage: %s [-a|-d] [-v|-r|-z] [-c count] [-w wait] [interface]\n", 132238384Sjkim progname); 133238384Sjkim exit(1); 134238384Sjkim} 135238384Sjkim 136238384Sjkim/* 137238384Sjkim * Called if an interval expires before intpr has completed a loop. 138238384Sjkim * Sets a flag to not wait for the alarm. 139238384Sjkim */ 140238384Sjkimstatic void 141238384Sjkimcatchalarm(arg) 142238384Sjkim int arg; 143238384Sjkim{ 144238384Sjkim signalled = 1; 145238384Sjkim} 146238384Sjkim 147238384Sjkim 148238384Sjkim#ifndef STREAMS 149238384Sjkimstatic void 150238384Sjkimget_ppp_stats(curp) 151238384Sjkim struct ppp_stats *curp; 152238384Sjkim{ 153238384Sjkim struct ifpppstatsreq req; 154238384Sjkim 155238384Sjkim memset (&req, 0, sizeof (req)); 156238384Sjkim 157238384Sjkim#ifdef __linux__ 158238384Sjkim req.stats_ptr = (caddr_t) &req.stats; 159238384Sjkim#undef ifr_name 160238384Sjkim#define ifr_name ifr__name 161238384Sjkim#endif 162238384Sjkim 163238384Sjkim strncpy(req.ifr_name, interface, sizeof(req.ifr_name)); 164238384Sjkim if (ioctl(s, SIOCGPPPSTATS, &req) < 0) { 165238384Sjkim fprintf(stderr, "%s: ", progname); 166238384Sjkim if (errno == ENOTTY) 167238384Sjkim fprintf(stderr, "kernel support missing\n"); 168238384Sjkim else 169238384Sjkim perror("couldn't get PPP statistics"); 170238384Sjkim exit(1); 171238384Sjkim } 172238384Sjkim *curp = req.stats; 173238384Sjkim} 174238384Sjkim 175238384Sjkimstatic void 176238384Sjkimget_ppp_cstats(csp) 177238384Sjkim struct ppp_comp_stats *csp; 178238384Sjkim{ 179238384Sjkim struct ifpppcstatsreq creq; 180238384Sjkim 181238384Sjkim memset (&creq, 0, sizeof (creq)); 182238384Sjkim 183238384Sjkim#ifdef __linux__ 184238384Sjkim creq.stats_ptr = (caddr_t) &creq.stats; 185238384Sjkim#undef ifr_name 186238384Sjkim#define ifr_name ifr__name 187238384Sjkim#endif 188238384Sjkim 189238384Sjkim strncpy(creq.ifr_name, interface, sizeof(creq.ifr_name)); 190238384Sjkim if (ioctl(s, SIOCGPPPCSTATS, &creq) < 0) { 191238384Sjkim fprintf(stderr, "%s: ", progname); 192238384Sjkim if (errno == ENOTTY) { 193238384Sjkim fprintf(stderr, "no kernel compression support\n"); 194238384Sjkim if (zflag) 195238384Sjkim exit(1); 196238384Sjkim rflag = 0; 197238384Sjkim } else { 198238384Sjkim perror("couldn't get PPP compression stats"); 199238384Sjkim exit(1); 200238384Sjkim } 201238384Sjkim } 202238384Sjkim 203238384Sjkim#ifdef __linux__ 204238384Sjkim if (creq.stats.c.bytes_out == 0) { 205238384Sjkim creq.stats.c.bytes_out = creq.stats.c.comp_bytes + creq.stats.c.inc_bytes; 206238384Sjkim creq.stats.c.in_count = creq.stats.c.unc_bytes; 207238384Sjkim } 208238384Sjkim if (creq.stats.c.bytes_out == 0) 209238384Sjkim creq.stats.c.ratio = 0.0; 210238384Sjkim else 211238384Sjkim creq.stats.c.ratio = 256.0 * creq.stats.c.in_count / 212238384Sjkim creq.stats.c.bytes_out; 213238384Sjkim 214238384Sjkim if (creq.stats.d.bytes_out == 0) { 215238384Sjkim creq.stats.d.bytes_out = creq.stats.d.comp_bytes + creq.stats.d.inc_bytes; 216238384Sjkim creq.stats.d.in_count = creq.stats.d.unc_bytes; 217238384Sjkim } 218238384Sjkim if (creq.stats.d.bytes_out == 0) 219238384Sjkim creq.stats.d.ratio = 0.0; 220238384Sjkim else 221238384Sjkim creq.stats.d.ratio = 256.0 * creq.stats.d.in_count / 222238384Sjkim creq.stats.d.bytes_out; 223238384Sjkim#endif 224238384Sjkim 225238384Sjkim *csp = creq.stats; 226238384Sjkim} 227238384Sjkim 228238384Sjkim#else /* STREAMS */ 229238384Sjkim 230238384Sjkimint 231238384Sjkimstrioctl(fd, cmd, ptr, ilen, olen) 232238384Sjkim int fd, cmd, ilen, olen; 233238384Sjkim char *ptr; 234238384Sjkim{ 235238384Sjkim struct strioctl str; 236238384Sjkim 237238384Sjkim str.ic_cmd = cmd; 238238384Sjkim str.ic_timout = 0; 239238384Sjkim str.ic_len = ilen; 240238384Sjkim str.ic_dp = ptr; 241238384Sjkim if (ioctl(fd, I_STR, &str) == -1) 242238384Sjkim return -1; 243238384Sjkim if (str.ic_len != olen) 244238384Sjkim fprintf(stderr, "strioctl: expected %d bytes, got %d for cmd %x\n", 245238384Sjkim olen, str.ic_len, cmd); 246238384Sjkim return 0; 247238384Sjkim} 248238384Sjkim 249238384Sjkimstatic void 250238384Sjkimget_ppp_stats(curp) 251238384Sjkim struct ppp_stats *curp; 252238384Sjkim{ 253238384Sjkim if (strioctl(s, PPPIO_GETSTAT, curp, 0, sizeof(*curp)) < 0) { 254238384Sjkim fprintf(stderr, "%s: ", progname); 255238384Sjkim if (errno == EINVAL) 256238384Sjkim fprintf(stderr, "kernel support missing\n"); 257238384Sjkim else 258238384Sjkim perror("couldn't get PPP statistics"); 259238384Sjkim exit(1); 260238384Sjkim } 261238384Sjkim} 262238384Sjkim 263238384Sjkimstatic void 264238384Sjkimget_ppp_cstats(csp) 265238384Sjkim struct ppp_comp_stats *csp; 266238384Sjkim{ 267238384Sjkim if (strioctl(s, PPPIO_GETCSTAT, csp, 0, sizeof(*csp)) < 0) { 268238384Sjkim fprintf(stderr, "%s: ", progname); 269238384Sjkim if (errno == ENOTTY) { 270238384Sjkim fprintf(stderr, "no kernel compression support\n"); 271238384Sjkim if (zflag) 272238384Sjkim exit(1); 273238384Sjkim rflag = 0; 274238384Sjkim } else { 275238384Sjkim perror("couldn't get PPP compression statistics"); 276238384Sjkim exit(1); 277238384Sjkim } 278238384Sjkim } 279238384Sjkim} 280238384Sjkim 281238384Sjkim#endif /* STREAMS */ 282238384Sjkim 283238384Sjkim#define MAX0(a) ((int)(a) > 0? (a): 0) 284238384Sjkim#define V(offset) MAX0(cur.offset - old.offset) 285238384Sjkim#define W(offset) MAX0(ccs.offset - ocs.offset) 286238384Sjkim 287238384Sjkim#define RATIO(c, i, u) ((c) == 0? 1.0: (u) / ((double)(c) + (i))) 288238384Sjkim#define CRATE(x) RATIO(W(x.comp_bytes), W(x.inc_bytes), W(x.unc_bytes)) 289238384Sjkim 290238384Sjkim#define KBPS(n) ((n) / (interval * 1000.0)) 291238384Sjkim 292238384Sjkim/* 293238384Sjkim * Print a running summary of interface statistics. 294238384Sjkim * Repeat display every interval seconds, showing statistics 295238384Sjkim * collected over that interval. Assumes that interval is non-zero. 296238384Sjkim * First line printed is cumulative. 297238384Sjkim */ 298238384Sjkimstatic void 299238384Sjkimintpr() 300238384Sjkim{ 301238384Sjkim register int line = 0; 302238384Sjkim sigset_t oldmask, mask; 303238384Sjkim char *bunit; 304238384Sjkim int ratef = 0; 305238384Sjkim struct ppp_stats cur, old; 306238384Sjkim struct ppp_comp_stats ccs, ocs; 307238384Sjkim 308238384Sjkim memset(&ccs, 0, sizeof(ccs)); /* XXX gcc */ 309238384Sjkim memset(&old, 0, sizeof(old)); 310238384Sjkim memset(&ocs, 0, sizeof(ocs)); 311238384Sjkim 312238384Sjkim while (1) { 313238384Sjkim get_ppp_stats(&cur); 314238384Sjkim if (zflag || rflag) 315238384Sjkim get_ppp_cstats(&ccs); 316238384Sjkim 317238384Sjkim (void)signal(SIGALRM, catchalarm); 318238384Sjkim signalled = 0; 319238384Sjkim (void)alarm(interval); 320238384Sjkim 321238384Sjkim if ((line % 20) == 0) { 322238384Sjkim if (zflag) { 323238384Sjkim printf("IN: COMPRESSED INCOMPRESSIBLE COMP | "); 324238384Sjkim printf("OUT: COMPRESSED INCOMPRESSIBLE COMP\n"); 325238384Sjkim bunit = dflag? "KB/S": "BYTE"; 326238384Sjkim printf(" %s PACK %s PACK RATIO | ", bunit, bunit); 327238384Sjkim printf(" %s PACK %s PACK RATIO", bunit, bunit); 328 } else { 329 printf("%8.8s %6.6s %6.6s", 330 "IN", "PACK", "VJCOMP"); 331 332 if (!rflag) 333 printf(" %6.6s %6.6s", "VJUNC", "VJERR"); 334 if (vflag) 335 printf(" %6.6s %6.6s", "VJTOSS", "NON-VJ"); 336 if (rflag) 337 printf(" %6.6s %6.6s", "RATIO", "UBYTE"); 338 printf(" | %8.8s %6.6s %6.6s", 339 "OUT", "PACK", "VJCOMP"); 340 341 if (!rflag) 342 printf(" %6.6s %6.6s", "VJUNC", "NON-VJ"); 343 if (vflag) 344 printf(" %6.6s %6.6s", "VJSRCH", "VJMISS"); 345 if (rflag) 346 printf(" %6.6s %6.6s", "RATIO", "UBYTE"); 347 } 348 putchar('\n'); 349 } 350 351 if (zflag) { 352 if (ratef) { 353 printf("%8.3f %6u %8.3f %6u %6.2f", 354 KBPS(W(d.comp_bytes)), 355 W(d.comp_packets), 356 KBPS(W(d.inc_bytes)), 357 W(d.inc_packets), 358 ccs.d.ratio / 256.0); 359 printf(" | %8.3f %6u %8.3f %6u %6.2f", 360 KBPS(W(c.comp_bytes)), 361 W(c.comp_packets), 362 KBPS(W(c.inc_bytes)), 363 W(c.inc_packets), 364 ccs.c.ratio / 256.0); 365 } else { 366 printf("%8u %6u %8u %6u %6.2f", 367 W(d.comp_bytes), 368 W(d.comp_packets), 369 W(d.inc_bytes), 370 W(d.inc_packets), 371 ccs.d.ratio / 256.0); 372 printf(" | %8u %6u %8u %6u %6.2f", 373 W(c.comp_bytes), 374 W(c.comp_packets), 375 W(c.inc_bytes), 376 W(c.inc_packets), 377 ccs.c.ratio / 256.0); 378 } 379 380 } else { 381 if (ratef) 382 printf("%8.3f", KBPS(V(p.ppp_ibytes))); 383 else 384 printf("%8u", V(p.ppp_ibytes)); 385 printf(" %6u %6u", 386 V(p.ppp_ipackets), 387 V(vj.vjs_compressedin)); 388 if (!rflag) 389 printf(" %6u %6u", 390 V(vj.vjs_uncompressedin), 391 V(vj.vjs_errorin)); 392 if (vflag) 393 printf(" %6u %6u", 394 V(vj.vjs_tossed), 395 V(p.ppp_ipackets) - V(vj.vjs_compressedin) 396 - V(vj.vjs_uncompressedin) - V(vj.vjs_errorin)); 397 if (rflag) { 398 printf(" %6.2f ", CRATE(d)); 399 if (ratef) 400 printf("%6.2f", KBPS(W(d.unc_bytes))); 401 else 402 printf("%6u", W(d.unc_bytes)); 403 } 404 if (ratef) 405 printf(" | %8.3f", KBPS(V(p.ppp_obytes))); 406 else 407 printf(" | %8u", V(p.ppp_obytes)); 408 printf(" %6u %6u", 409 V(p.ppp_opackets), 410 V(vj.vjs_compressed)); 411 if (!rflag) 412 printf(" %6u %6u", 413 V(vj.vjs_packets) - V(vj.vjs_compressed), 414 V(p.ppp_opackets) - V(vj.vjs_packets)); 415 if (vflag) 416 printf(" %6u %6u", 417 V(vj.vjs_searches), 418 V(vj.vjs_misses)); 419 if (rflag) { 420 printf(" %6.2f ", CRATE(c)); 421 if (ratef) 422 printf("%6.2f", KBPS(W(c.unc_bytes))); 423 else 424 printf("%6u", W(c.unc_bytes)); 425 } 426 427 } 428 429 putchar('\n'); 430 fflush(stdout); 431 line++; 432 433 count--; 434 if (!infinite && !count) 435 break; 436 437 sigemptyset(&mask); 438 sigaddset(&mask, SIGALRM); 439 sigprocmask(SIG_BLOCK, &mask, &oldmask); 440 if (!signalled) { 441 sigemptyset(&mask); 442 sigsuspend(&mask); 443 } 444 sigprocmask(SIG_SETMASK, &oldmask, NULL); 445 signalled = 0; 446 (void)alarm(interval); 447 448 if (!aflag) { 449 old = cur; 450 ocs = ccs; 451 ratef = dflag; 452 } 453 } 454} 455 456int 457main(argc, argv) 458 int argc; 459 char *argv[]; 460{ 461 int c; 462#ifdef STREAMS 463 char *dev; 464#endif 465 const char *fmt; 466 467 if ((progname = strrchr(argv[0], '/')) == NULL) 468 progname = argv[0]; 469 else 470 ++progname; 471 472 if (strncmp(progname, SL_DRV_NAME, sizeof(SL_DRV_NAME) - 1) == 0) { 473 interface = SL_DRV_NAME "0"; 474 fmt = SL_DRV_NAME "%d"; 475 } else { 476 interface = PPP_DRV_NAME "0"; 477 fmt = PPP_DRV_NAME "%d"; 478 } 479 480 while ((c = getopt(argc, argv, "advrzc:w:")) != -1) { 481 switch (c) { 482 case 'a': 483 ++aflag; 484 break; 485 case 'd': 486 ++dflag; 487 break; 488 case 'v': 489 ++vflag; 490 break; 491 case 'r': 492 ++rflag; 493 break; 494 case 'z': 495 ++zflag; 496 break; 497 case 'c': 498 count = atoi(optarg); 499 if (count <= 0) 500 usage(); 501 break; 502 case 'w': 503 interval = atoi(optarg); 504 if (interval <= 0) 505 usage(); 506 break; 507 default: 508 usage(); 509 } 510 } 511 argc -= optind; 512 argv += optind; 513 514 if (!interval && count) 515 interval = 5; 516 if (interval && !count) 517 infinite = 1; 518 if (!interval && !count) 519 count = 1; 520 if (aflag) 521 dflag = 0; 522 523 if (argc > 1) 524 usage(); 525 if (argc > 0) 526 interface = argv[0]; 527 528 if (sscanf(interface, fmt, &unit) != 1) { 529 fprintf(stderr, "%s: invalid interface '%s' specified\n", 530 progname, interface); 531 } 532 533#ifndef STREAMS 534 { 535 struct ifreq ifr; 536 537 s = socket(AF_INET, SOCK_DGRAM, 0); 538 if (s < 0) { 539 fprintf(stderr, "%s: ", progname); 540 perror("couldn't create IP socket"); 541 exit(1); 542 } 543 544#ifdef __linux__ 545#undef ifr_name 546#define ifr_name ifr_ifrn.ifrn_name 547#endif 548 strncpy(ifr.ifr_name, interface, sizeof(ifr.ifr_name)); 549 if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) { 550 fprintf(stderr, "%s: nonexistent interface '%s' specified\n", 551 progname, interface); 552 exit(1); 553 } 554 } 555 556#else /* STREAMS */ 557#ifdef __osf__ 558 dev = "/dev/streams/ppp"; 559#else 560 dev = "/dev/" PPP_DRV_NAME; 561#endif 562 if ((s = open(dev, O_RDONLY)) < 0) { 563 fprintf(stderr, "%s: couldn't open ", progname); 564 perror(dev); 565 exit(1); 566 } 567 if (strioctl(s, PPPIO_ATTACH, &unit, sizeof(int), 0) < 0) { 568 fprintf(stderr, "%s: ppp%d is not available\n", progname, unit); 569 exit(1); 570 } 571 572#endif /* STREAMS */ 573 574 intpr(); 575 exit(0); 576} 577