Home | History | Annotate | Line # | Download | only in pppstats
      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