Home | History | Annotate | Download | only in snoop
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 
     22 /*
     23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 #include <stdio.h>
     28 #include <unistd.h>
     29 #include <stropts.h>
     30 #include <string.h>
     31 #include <stdlib.h>
     32 #include <fcntl.h>
     33 #include <stdarg.h>
     34 #include <setjmp.h>
     35 #include <string.h>
     36 #include <errno.h>
     37 #include <sys/types.h>
     38 #include <sys/time.h>
     39 #include <signal.h>
     40 #include <sys/mman.h>
     41 #include <assert.h>
     42 #include <sys/sysmacros.h>
     43 
     44 #include <sys/socket.h>
     45 #include <sys/pfmod.h>
     46 #include <net/if.h>
     47 #include <netinet/in_systm.h>
     48 #include <netinet/in.h>
     49 #include <netinet/if_ether.h>
     50 #include <netdb.h>
     51 
     52 #include "snoop.h"
     53 
     54 static int snaplen;
     55 
     56 /* Global error recovery variables */
     57 sigjmp_buf jmp_env, ojmp_env;		/* error recovery jmp buf */
     58 int snoop_nrecover;			/* number of recoveries on curr pkt */
     59 int quitting;				/* user termination flag */
     60 
     61 static struct snoop_handler *snoop_hp;		/* global alarm handler head */
     62 static struct snoop_handler *snoop_tp;		/* global alarm handler tail */
     63 static time_t snoop_nalarm;			/* time of next alarm */
     64 
     65 /* protected interpreter output areas */
     66 #define	MAXSUM		8
     67 #define	REDZONE		64
     68 static char *sumline[MAXSUM];
     69 static char *detail_line;
     70 static char *line;
     71 static char *encap;
     72 
     73 static int audio;
     74 int maxcount;	/* maximum no of packets to capture */
     75 int count;	/* count of packets captured */
     76 static int sumcount;
     77 int x_offset = -1;
     78 int x_length = 0x7fffffff;
     79 FILE *namefile;
     80 boolean_t Pflg;
     81 boolean_t Iflg;
     82 boolean_t qflg;
     83 boolean_t rflg;
     84 #ifdef	DEBUG
     85 boolean_t zflg;
     86 #endif
     87 struct Pf_ext_packetfilt pf;
     88 
     89 static int vlanid = 0;
     90 
     91 static void usage(void);
     92 void show_count();
     93 static void snoop_sigrecover(int sig, siginfo_t *info, void *p);
     94 static char *protmalloc(size_t);
     95 static void resetperm(void);
     96 
     97 int
     98 main(int argc, char **argv)
     99 {
    100 	int c;
    101 	int filter = 0;
    102 	int flags = F_SUM;
    103 	struct Pf_ext_packetfilt *fp = NULL;
    104 	char *icapfile = NULL;
    105 	char *ocapfile = NULL;
    106 	boolean_t nflg = B_FALSE;
    107 	boolean_t Nflg = B_FALSE;
    108 	int Cflg = 0;
    109 	boolean_t Uflg = B_FALSE;
    110 	int first = 1;
    111 	int last  = 0x7fffffff;
    112 	boolean_t use_kern_pf;
    113 	char *p, *p2;
    114 	char names[MAXPATHLEN + 1];
    115 	char self[MAXHOSTNAMELEN + 1];
    116 	char *argstr = NULL;
    117 	void (*proc)();
    118 	char *audiodev;
    119 	int ret;
    120 	struct sigaction sigact;
    121 	stack_t sigstk;
    122 	char *output_area;
    123 	int nbytes;
    124 	char *datalink = NULL;
    125 	dlpi_handle_t dh;
    126 
    127 	names[0] = '\0';
    128 	/*
    129 	 * Global error recovery: Prepare for interpreter failures
    130 	 * with corrupted packets or confused interpreters.
    131 	 * Allocate protected output and stack areas, with generous
    132 	 * red-zones.
    133 	 */
    134 	nbytes = (MAXSUM + 3) * (MAXLINE + REDZONE);
    135 	output_area = protmalloc(nbytes);
    136 	if (output_area == NULL) {
    137 		perror("Warning: mmap");
    138 		exit(1);
    139 	}
    140 
    141 	/* Allocate protected output areas */
    142 	for (ret = 0; ret < MAXSUM; ret++) {
    143 		sumline[ret] = (char *)output_area;
    144 		output_area += (MAXLINE + REDZONE);
    145 	}
    146 	detail_line = output_area;
    147 	output_area += MAXLINE + REDZONE;
    148 	line = output_area;
    149 	output_area += MAXLINE + REDZONE;
    150 	encap = output_area;
    151 	output_area += MAXLINE + REDZONE;
    152 
    153 	/* Initialize an alternate signal stack to increase robustness */
    154 	if ((sigstk.ss_sp = (char *)malloc(SIGSTKSZ+REDZONE)) == NULL) {
    155 		perror("Warning: malloc");
    156 		exit(1);
    157 	}
    158 	sigstk.ss_size = SIGSTKSZ;
    159 	sigstk.ss_flags = 0;
    160 	if (sigaltstack(&sigstk, (stack_t *)NULL) < 0) {
    161 		perror("Warning: sigaltstack");
    162 		exit(1);
    163 	}
    164 
    165 	/* Initialize a master signal handler */
    166 	sigact.sa_handler = NULL;
    167 	sigact.sa_sigaction = snoop_sigrecover;
    168 	(void) sigemptyset(&sigact.sa_mask);
    169 	sigact.sa_flags = SA_ONSTACK|SA_SIGINFO;
    170 
    171 	/* Register master signal handler */
    172 	if (sigaction(SIGHUP, &sigact, (struct sigaction *)NULL) < 0) {
    173 		perror("Warning: sigaction");
    174 		exit(1);
    175 	}
    176 	if (sigaction(SIGINT, &sigact, (struct sigaction *)NULL) < 0) {
    177 		perror("Warning: sigaction");
    178 		exit(1);
    179 	}
    180 	if (sigaction(SIGQUIT, &sigact, (struct sigaction *)NULL) < 0) {
    181 		perror("Warning: sigaction");
    182 		exit(1);
    183 	}
    184 	if (sigaction(SIGILL, &sigact, (struct sigaction *)NULL) < 0) {
    185 		perror("Warning: sigaction");
    186 		exit(1);
    187 	}
    188 	if (sigaction(SIGTRAP, &sigact, (struct sigaction *)NULL) < 0) {
    189 		perror("Warning: sigaction");
    190 		exit(1);
    191 	}
    192 	if (sigaction(SIGIOT, &sigact, (struct sigaction *)NULL) < 0) {
    193 		perror("Warning: sigaction");
    194 		exit(1);
    195 	}
    196 	if (sigaction(SIGEMT, &sigact, (struct sigaction *)NULL) < 0) {
    197 		perror("Warning: sigaction");
    198 		exit(1);
    199 	}
    200 	if (sigaction(SIGFPE, &sigact, (struct sigaction *)NULL) < 0) {
    201 		perror("Warning: sigaction");
    202 		exit(1);
    203 	}
    204 	if (sigaction(SIGBUS, &sigact, (struct sigaction *)NULL) < 0) {
    205 		perror("Warning: sigaction");
    206 		exit(1);
    207 	}
    208 	if (sigaction(SIGSEGV, &sigact, (struct sigaction *)NULL) < 0) {
    209 		perror("Warning: sigaction");
    210 		exit(1);
    211 	}
    212 	if (sigaction(SIGSYS, &sigact, (struct sigaction *)NULL) < 0) {
    213 		perror("Warning: sigaction");
    214 		exit(1);
    215 	}
    216 	if (sigaction(SIGALRM, &sigact, (struct sigaction *)NULL) < 0) {
    217 		perror("Warning: sigaction");
    218 		exit(1);
    219 	}
    220 	if (sigaction(SIGTERM, &sigact, (struct sigaction *)NULL) < 0) {
    221 		perror("Warning: sigaction");
    222 		exit(1);
    223 	}
    224 
    225 	/* Prepare for failure during program initialization/exit */
    226 	if (sigsetjmp(jmp_env, 1)) {
    227 		exit(1);
    228 	}
    229 	(void) setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
    230 
    231 	while ((c = getopt(argc, argv, "at:CPDSi:o:Nn:s:d:I:vVp:f:c:x:U?rqz"))
    232 	    != EOF) {
    233 		switch (c) {
    234 		case 'a':
    235 			audiodev = getenv("AUDIODEV");
    236 			if (audiodev == NULL)
    237 				audiodev = "/dev/audio";
    238 			audio = open(audiodev, O_WRONLY);
    239 			if (audio < 0) {
    240 				pr_err("Audio device %s: %m",
    241 				    audiodev);
    242 				exit(1);
    243 			}
    244 			break;
    245 		case 't':
    246 			flags |= F_TIME;
    247 			switch (*optarg) {
    248 			case 'r':	flags |= F_RTIME; break;
    249 			case 'a':	flags |= F_ATIME; break;
    250 			case 'd':	break;
    251 			default:	usage();
    252 			}
    253 			break;
    254 		case 'I':
    255 			if (datalink != NULL)
    256 				usage();
    257 			Iflg = B_TRUE;
    258 			datalink = optarg;
    259 			break;
    260 		case 'P':
    261 			Pflg = B_TRUE;
    262 			break;
    263 		case 'D':
    264 			flags |= F_DROPS;
    265 			break;
    266 		case 'S':
    267 			flags |= F_LEN;
    268 			break;
    269 		case 'i':
    270 			icapfile = optarg;
    271 			break;
    272 		case 'o':
    273 			ocapfile = optarg;
    274 			break;
    275 		case 'N':
    276 			Nflg = B_TRUE;
    277 			break;
    278 		case 'n':
    279 			nflg = B_TRUE;
    280 			(void) strlcpy(names, optarg, MAXPATHLEN);
    281 			break;
    282 		case 's':
    283 			snaplen = atoi(optarg);
    284 			break;
    285 		case 'd':
    286 			if (Iflg)
    287 				usage();
    288 			datalink = optarg;
    289 			break;
    290 		case 'v':
    291 			flags &= ~(F_SUM);
    292 			flags |= F_DTAIL;
    293 			break;
    294 		case 'V':
    295 			flags |= F_ALLSUM;
    296 			break;
    297 		case 'p':
    298 			p = optarg;
    299 			p2 = strpbrk(p, ",:-");
    300 			if (p2 == NULL) {
    301 				first = last = atoi(p);
    302 			} else {
    303 				*p2++ = '\0';
    304 				first = atoi(p);
    305 				last = atoi(p2);
    306 			}
    307 			break;
    308 		case 'f':
    309 			(void) gethostname(self, MAXHOSTNAMELEN);
    310 			p = strchr(optarg, ':');
    311 			if (p) {
    312 				*p = '\0';
    313 				if (strcmp(optarg, self) == 0 ||
    314 				    strcmp(p+1, self) == 0)
    315 				(void) fprintf(stderr,
    316 				"Warning: cannot capture packets from %s\n",
    317 				    self);
    318 				*p = ' ';
    319 			} else if (strcmp(optarg, self) == 0)
    320 				(void) fprintf(stderr,
    321 				"Warning: cannot capture packets from %s\n",
    322 				    self);
    323 			argstr = optarg;
    324 			break;
    325 		case 'x':
    326 			p = optarg;
    327 			p2 = strpbrk(p, ",:-");
    328 			if (p2 == NULL) {
    329 				x_offset = atoi(p);
    330 				x_length = -1;
    331 			} else {
    332 				*p2++ = '\0';
    333 				x_offset = atoi(p);
    334 				x_length = atoi(p2);
    335 			}
    336 			break;
    337 		case 'c':
    338 			maxcount = atoi(optarg);
    339 			break;
    340 		case 'C':
    341 			Cflg = B_TRUE;
    342 			break;
    343 		case 'q':
    344 			qflg = B_TRUE;
    345 			break;
    346 		case 'r':
    347 			rflg = B_TRUE;
    348 			break;
    349 		case 'U':
    350 			Uflg = B_TRUE;
    351 			break;
    352 #ifdef	DEBUG
    353 		case 'z':
    354 			zflg = B_TRUE;
    355 			break;
    356 #endif	/* DEBUG */
    357 		case '?':
    358 		default:
    359 			usage();
    360 		}
    361 	}
    362 
    363 	if (argc > optind)
    364 		argstr = (char *)concat_args(&argv[optind], argc - optind);
    365 
    366 	/*
    367 	 * Need to know before we decide on filtering method some things
    368 	 * about the interface.  So, go ahead and do part of the initialization
    369 	 * now so we have that data.  Note that if no datalink is specified,
    370 	 * open_datalink() selects one and returns it.  In an ideal world,
    371 	 * it might be nice if the "correct" interface for the filter
    372 	 * requested was chosen, but that's too hard.
    373 	 */
    374 	if (!icapfile) {
    375 		use_kern_pf = open_datalink(&dh, datalink);
    376 	} else {
    377 		use_kern_pf = B_FALSE;
    378 		cap_open_read(icapfile);
    379 
    380 		if (!nflg) {
    381 			names[0] = '\0';
    382 			(void) strlcpy(names, icapfile, MAXPATHLEN);
    383 			(void) strlcat(names, ".names", MAXPATHLEN);
    384 		}
    385 	}
    386 
    387 	if (Uflg)
    388 		use_kern_pf = B_FALSE;
    389 
    390 	/* attempt to read .names file if it exists before filtering */
    391 	if ((!Nflg) && names[0] != '\0') {
    392 		if (access(names, F_OK) == 0) {
    393 			load_names(names);
    394 		} else if (nflg) {
    395 			(void) fprintf(stderr, "%s not found\n", names);
    396 			exit(1);
    397 		}
    398 	}
    399 
    400 	if (argstr) {
    401 		if (use_kern_pf) {
    402 			ret = pf_compile(argstr, Cflg);
    403 			switch (ret) {
    404 			case 0:
    405 				filter++;
    406 				compile(argstr, Cflg);
    407 				break;
    408 			case 1:
    409 				fp = &pf;
    410 				break;
    411 			case 2:
    412 				fp = &pf;
    413 				filter++;
    414 				break;
    415 			}
    416 		} else {
    417 			filter++;
    418 			compile(argstr, Cflg);
    419 		}
    420 
    421 		if (Cflg)
    422 			exit(0);
    423 	}
    424 
    425 	if (flags & F_SUM)
    426 		flags |= F_WHO;
    427 
    428 	/*
    429 	 * If the -o flag is set then capture packets
    430 	 * directly to a file.  Don't attempt to
    431 	 * interpret them on the fly (F_NOW).
    432 	 * Note: capture to file is much less likely
    433 	 * to drop packets since we don't spend cpu
    434 	 * cycles running through the interpreters
    435 	 * and possibly hanging in address-to-name
    436 	 * mappings through the name service.
    437 	 */
    438 	if (ocapfile) {
    439 		cap_open_write(ocapfile);
    440 		proc = cap_write;
    441 	} else {
    442 		flags |= F_NOW;
    443 		proc = process_pkt;
    444 	}
    445 
    446 
    447 	/*
    448 	 * If the -i flag is set then get packets from
    449 	 * the log file which has been previously captured
    450 	 * with the -o option.
    451 	 */
    452 	if (icapfile) {
    453 		names[0] = '\0';
    454 		(void) strlcpy(names, icapfile, MAXPATHLEN);
    455 		(void) strlcat(names, ".names", MAXPATHLEN);
    456 
    457 		if (Nflg) {
    458 			namefile = fopen(names, "w");
    459 			if (namefile == NULL) {
    460 				perror(names);
    461 				exit(1);
    462 			}
    463 			flags = 0;
    464 			(void) fprintf(stderr,
    465 			    "Creating name file %s\n", names);
    466 		}
    467 
    468 		if (flags & F_DTAIL)
    469 			flags = F_DTAIL;
    470 		else
    471 			flags |= F_NUM | F_TIME;
    472 
    473 		resetperm();
    474 		cap_read(first, last, filter, proc, flags);
    475 
    476 		if (Nflg)
    477 			(void) fclose(namefile);
    478 
    479 	} else {
    480 		const int chunksize = 8 * 8192;
    481 		struct timeval timeout;
    482 
    483 		/*
    484 		 * If listening to packets on audio
    485 		 * then set the buffer timeout down
    486 		 * to 1/10 sec.  A higher value
    487 		 * makes the audio "bursty".
    488 		 */
    489 		if (audio) {
    490 			timeout.tv_sec = 0;
    491 			timeout.tv_usec = 100000;
    492 		} else {
    493 			timeout.tv_sec = 1;
    494 			timeout.tv_usec = 0;
    495 		}
    496 
    497 		init_datalink(dh, snaplen, chunksize, &timeout, fp);
    498 		if (! qflg && ocapfile)
    499 			show_count();
    500 		resetperm();
    501 		net_read(dh, chunksize, filter, proc, flags);
    502 		dlpi_close(dh);
    503 
    504 		if (!(flags & F_NOW))
    505 			(void) printf("\n");
    506 	}
    507 
    508 	if (ocapfile)
    509 		cap_close();
    510 
    511 	return (0);
    512 }
    513 
    514 static int tone[] = {
    515 0x076113, 0x153333, 0x147317, 0x144311, 0x147315, 0x050353, 0x037103, 0x051106,
    516 0x157155, 0x142723, 0x133273, 0x134664, 0x051712, 0x024465, 0x026447, 0x072473,
    517 0x136715, 0x126257, 0x135256, 0x047344, 0x034476, 0x027464, 0x036062, 0x133334,
    518 0x127256, 0x130660, 0x136262, 0x040724, 0x016446, 0x025437, 0x137171, 0x127672,
    519 0x124655, 0x134654, 0x032741, 0x021447, 0x037450, 0x125675, 0x127650, 0x077277,
    520 0x046514, 0x036077, 0x035471, 0x147131, 0x136272, 0x162720, 0x166151, 0x037527,
    521 };
    522 
    523 /*
    524  * Make a sound on /dev/audio according to the length of the packet.  The
    525  * tone data was ripped from /usr/share/audio/samples/au/bark.au.  The
    526  * amount of waveform used is a function of packet length e.g.  a series
    527  * of small packets is heard as clicks, whereas a series of NFS packets in
    528  * an 8k read sounds like a "WHAAAARP".
    529  */
    530 void
    531 click(len)
    532 	int len;
    533 {
    534 	len /= 8;
    535 	len = len ? len : 4;
    536 
    537 	if (audio) {
    538 		(void) write(audio, tone, len);
    539 	}
    540 }
    541 
    542 /* Display a count of packets */
    543 void
    544 show_count()
    545 {
    546 	static int prev = -1;
    547 
    548 	if (count == prev)
    549 		return;
    550 
    551 	prev = count;
    552 	(void) fprintf(stderr, "\r%d ", count);
    553 }
    554 
    555 #define	ENCAP_LEN	16	/* Hold "(NN encap)" */
    556 
    557 /*
    558  * Display data that's external to the packet.
    559  * This constitutes the first half of the summary
    560  * line display.
    561  */
    562 void
    563 show_pktinfo(flags, num, src, dst, ptvp, tvp, drops, len)
    564 	int flags, num, drops, len;
    565 	char *src, *dst;
    566 	struct timeval *ptvp, *tvp;
    567 {
    568 	struct tm *tm;
    569 	static struct timeval tvp0;
    570 	int sec, usec;
    571 	char *lp = line;
    572 	int i, start;
    573 
    574 	if (flags & F_NUM) {
    575 		(void) sprintf(lp, "%3d ", num);
    576 		lp += strlen(lp);
    577 	}
    578 	tm = localtime(&tvp->tv_sec);
    579 
    580 	if (flags & F_TIME) {
    581 		if (flags & F_ATIME) {
    582 			(void) sprintf(lp, "%d:%02d:%d.%05d ",
    583 				tm->tm_hour, tm->tm_min, tm->tm_sec,
    584 				(int)tvp->tv_usec / 10);
    585 			lp += strlen(lp);
    586 		} else {
    587 			if (flags & F_RTIME) {
    588 				if (tvp0.tv_sec == 0) {
    589 					tvp0.tv_sec = tvp->tv_sec;
    590 					tvp0.tv_usec = tvp->tv_usec;
    591 				}
    592 				ptvp = &tvp0;
    593 			}
    594 			sec  = tvp->tv_sec  - ptvp->tv_sec;
    595 			usec = tvp->tv_usec - ptvp->tv_usec;
    596 			if (usec < 0) {
    597 				usec += 1000000;
    598 				sec  -= 1;
    599 			}
    600 			(void) sprintf(lp, "%3d.%05d ", sec, usec / 10);
    601 			lp += strlen(lp);
    602 		}
    603 	}
    604 
    605 	if ((flags & F_SUM) && !(flags & F_ALLSUM) && (vlanid != 0)) {
    606 		(void) snprintf(lp, MAXLINE, "VLAN#%i: ", vlanid);
    607 		lp += strlen(lp);
    608 	}
    609 
    610 	if (flags & F_WHO) {
    611 		(void) sprintf(lp, "%12s -> %-12s ", src, dst);
    612 		lp += strlen(lp);
    613 	}
    614 
    615 	if (flags & F_DROPS) {
    616 		(void) sprintf(lp, "drops: %d ", drops);
    617 		lp += strlen(lp);
    618 	}
    619 
    620 	if (flags & F_LEN) {
    621 		(void) sprintf(lp, "length: %4d  ", len);
    622 		lp += strlen(lp);
    623 	}
    624 
    625 	if (flags & F_SUM) {
    626 		if (flags & F_ALLSUM)
    627 			(void) printf("________________________________\n");
    628 
    629 		start = flags & F_ALLSUM ? 0 : sumcount - 1;
    630 		(void) sprintf(encap, "  (%d encap)", total_encap_levels - 1);
    631 		(void) printf("%s%s%s\n", line, sumline[start],
    632 		    ((flags & F_ALLSUM) || (total_encap_levels == 1)) ? "" :
    633 			encap);
    634 
    635 		for (i = start + 1; i < sumcount; i++)
    636 			(void) printf("%s%s\n", line, sumline[i]);
    637 
    638 		sumcount = 0;
    639 	}
    640 
    641 	if (flags & F_DTAIL) {
    642 		(void) printf("%s\n\n", detail_line);
    643 		detail_line[0] = '\0';
    644 	}
    645 }
    646 
    647 /*
    648  * The following three routines are called back
    649  * from the interpreters to display their stuff.
    650  * The theory is that when snoop becomes a window
    651  * based tool we can just supply a new version of
    652  * get_sum_line and get_detail_line and not have
    653  * to touch the interpreters at all.
    654  */
    655 char *
    656 get_sum_line()
    657 {
    658 	int tsumcount = sumcount;
    659 
    660 	if (sumcount >= MAXSUM) {
    661 		sumcount = 0;			/* error recovery */
    662 		pr_err(
    663 		    "get_sum_line: sumline overflow (sumcount=%d, MAXSUM=%d)\n",
    664 		    tsumcount, MAXSUM);
    665 	}
    666 
    667 	sumline[sumcount][0] = '\0';
    668 	return (sumline[sumcount++]);
    669 }
    670 
    671 /*ARGSUSED*/
    672 char *
    673 get_detail_line(off, len)
    674 	int off, len;
    675 {
    676 	if (detail_line[0]) {
    677 		(void) printf("%s\n", detail_line);
    678 		detail_line[0] = '\0';
    679 	}
    680 	return (detail_line);
    681 }
    682 
    683 /*
    684  * This function exists to make sure that VLAN information is
    685  * prepended to summary lines displayed.  The problem this function
    686  * solves is how to display VLAN information while in summary mode.
    687  * Each interpretor uses the get_sum_line and get_detail_line functions
    688  * to get a character buffer to display information to the user.
    689  * get_sum_line is the important one here.  Each call to get_sum_line
    690  * gets a buffer which stores one line of information.  In summary mode,
    691  * the last line generated is the line printed.  Instead of changing each
    692  * interpreter to add VLAN information to the summary line, the ethernet
    693  * interpreter changes to call this function and set an ID.  If the ID is not
    694  * zero and snoop is in default summary mode, snoop displays the
    695  * VLAN information at the beginning of the output line.  Otherwise,
    696  * no VLAN information is displayed.
    697  */
    698 void
    699 set_vlan_id(int id)
    700 {
    701 	vlanid = id;
    702 }
    703 
    704 /*
    705  * Print an error.
    706  * Works like printf (fmt string and variable args)
    707  * except that it will substitute an error message
    708  * for a "%m" string (like syslog) and it calls
    709  * long_jump - it doesn't return to where it was
    710  * called from - it goes to the last setjmp().
    711  */
    712 /* VARARGS1 */
    713 void
    714 pr_err(const char *fmt, ...)
    715 {
    716 	va_list ap;
    717 	char buf[1024], *p2;
    718 	const char *p1;
    719 
    720 	(void) strcpy(buf, "snoop: ");
    721 	p2 = buf + strlen(buf);
    722 
    723 	/*
    724 	 * Note that we terminate the buffer with '\n' and '\0'.
    725 	 */
    726 	for (p1 = fmt; *p1 != '\0' && p2 < buf + sizeof (buf) - 2; p1++) {
    727 		if (*p1 == '%' && *(p1+1) == 'm') {
    728 			const char *errstr;
    729 
    730 			if ((errstr = strerror(errno)) != NULL) {
    731 				*p2 = '\0';
    732 				(void) strlcat(buf, errstr, sizeof (buf));
    733 				p2 += strlen(p2);
    734 			}
    735 			p1++;
    736 		} else {
    737 			*p2++ = *p1;
    738 		}
    739 	}
    740 	if (p2 > buf && *(p2-1) != '\n')
    741 		*p2++ = '\n';
    742 	*p2 = '\0';
    743 
    744 	va_start(ap, fmt);
    745 	/* LINTED: E_SEC_PRINTF_VAR_FMT */
    746 	(void) vfprintf(stderr, buf, ap);
    747 	va_end(ap);
    748 	snoop_sigrecover(-1, NULL, NULL);	/* global error recovery */
    749 }
    750 
    751 /*
    752  * Store a copy of linkname associated with the DLPI handle.
    753  * Save errno before closing the dlpi handle so that the
    754  * correct error value is used if 'err' is a system error.
    755  */
    756 void
    757 pr_errdlpi(dlpi_handle_t dh, const char *cmd, int err)
    758 {
    759 	int save_errno = errno;
    760 	char linkname[DLPI_LINKNAME_MAX];
    761 
    762 	(void) strlcpy(linkname, dlpi_linkname(dh), sizeof (linkname));
    763 
    764 	dlpi_close(dh);
    765 	errno = save_errno;
    766 
    767 	pr_err("%s on \"%s\": %s", cmd, linkname, dlpi_strerror(err));
    768 }
    769 
    770 /*
    771  * Ye olde usage proc
    772  * PLEASE keep this up to date!
    773  * Naive users *love* this stuff.
    774  */
    775 static void
    776 usage(void)
    777 {
    778 	(void) fprintf(stderr, "\nUsage:  snoop\n");
    779 	(void) fprintf(stderr,
    780 	"\t[ -a ]			# Listen to packets on audio\n");
    781 	(void) fprintf(stderr,
    782 	"\t[ -d link ]		# Listen on named link\n");
    783 	(void) fprintf(stderr,
    784 	"\t[ -s snaplen ]		# Truncate packets\n");
    785 	(void) fprintf(stderr,
    786 	"\t[ -I IP interface ]		# Listen on named IP interface\n");
    787 	(void) fprintf(stderr,
    788 	"\t[ -c count ]		# Quit after count packets\n");
    789 	(void) fprintf(stderr,
    790 	"\t[ -P ]			# Turn OFF promiscuous mode\n");
    791 	(void) fprintf(stderr,
    792 	"\t[ -D ]			# Report dropped packets\n");
    793 	(void) fprintf(stderr,
    794 	"\t[ -S ]			# Report packet size\n");
    795 	(void) fprintf(stderr,
    796 	"\t[ -i file ]		# Read previously captured packets\n");
    797 	(void) fprintf(stderr,
    798 	"\t[ -o file ]		# Capture packets in file\n");
    799 	(void) fprintf(stderr,
    800 	"\t[ -n file ]		# Load addr-to-name table from file\n");
    801 	(void) fprintf(stderr,
    802 	"\t[ -N ]			# Create addr-to-name table\n");
    803 	(void) fprintf(stderr,
    804 	"\t[ -t  r|a|d ]		# Time: Relative, Absolute or Delta\n");
    805 	(void) fprintf(stderr,
    806 	"\t[ -v ]			# Verbose packet display\n");
    807 	(void) fprintf(stderr,
    808 	"\t[ -V ]			# Show all summary lines\n");
    809 	(void) fprintf(stderr,
    810 	"\t[ -p first[,last] ]	# Select packet(s) to display\n");
    811 	(void) fprintf(stderr,
    812 	"\t[ -x offset[,length] ]	# Hex dump from offset for length\n");
    813 	(void) fprintf(stderr,
    814 	"\t[ -C ]			# Print packet filter code\n");
    815 	(void) fprintf(stderr,
    816 	"\t[ -q ]			# Suppress printing packet count\n");
    817 	(void) fprintf(stderr,
    818 	"\t[ -r ]			# Do not resolve address to name\n");
    819 	(void) fprintf(stderr,
    820 	"\n\t[ filter expression ]\n");
    821 	(void) fprintf(stderr, "\nExample:\n");
    822 	(void) fprintf(stderr, "\tsnoop -o saved  host fred\n\n");
    823 	(void) fprintf(stderr, "\tsnoop -i saved -tr -v -p19\n");
    824 	exit(1);
    825 }
    826 
    827 /*
    828  * sdefault: default global alarm handler. Causes the current packet
    829  * to be skipped.
    830  */
    831 static void
    832 sdefault(void)
    833 {
    834 	snoop_nrecover = SNOOP_MAXRECOVER;
    835 }
    836 
    837 /*
    838  * snoop_alarm: register or unregister an alarm handler to be called after
    839  * s_sec seconds. Because snoop wasn't written to tolerate random signal
    840  * delivery, periodic SIGALRM delivery (or SA_RESTART) cannot be used.
    841  *
    842  * s_sec argument of 0 seconds unregisters the handler.
    843  * s_handler argument of NULL registers default handler sdefault(), or
    844  * unregisters all signal handlers (for error recovery).
    845  *
    846  * Variables must be volatile to force the compiler to not optimize
    847  * out the signal blocking.
    848  */
    849 /*ARGSUSED*/
    850 int
    851 snoop_alarm(int s_sec, void (*s_handler)())
    852 {
    853 	volatile time_t now;
    854 	volatile time_t nalarm = 0;
    855 	volatile struct snoop_handler *sh = NULL;
    856 	volatile struct snoop_handler *hp, *tp, *next;
    857 	volatile sigset_t s_mask;
    858 	volatile int ret = -1;
    859 
    860 	(void) sigemptyset((sigset_t *)&s_mask);
    861 	(void) sigaddset((sigset_t *)&s_mask, SIGALRM);
    862 	if (s_sec < 0)
    863 		return (-1);
    864 
    865 	/* register an alarm handler */
    866 	now = time(NULL);
    867 	if (s_sec) {
    868 		sh = malloc(sizeof (struct snoop_handler));
    869 		sh->s_time = now + s_sec;
    870 		if (s_handler == NULL)
    871 			s_handler = sdefault;
    872 		sh->s_handler = s_handler;
    873 		sh->s_next = NULL;
    874 		(void) sigprocmask(SIG_BLOCK, (sigset_t *)&s_mask, NULL);
    875 		if (snoop_hp == NULL) {
    876 			snoop_hp = snoop_tp = (struct snoop_handler *)sh;
    877 
    878 			snoop_nalarm = sh->s_time;
    879 			(void) alarm(sh->s_time - now);
    880 		} else {
    881 			snoop_tp->s_next = (struct snoop_handler *)sh;
    882 			snoop_tp = (struct snoop_handler *)sh;
    883 
    884 			if (sh->s_time < snoop_nalarm) {
    885 				snoop_nalarm = sh->s_time;
    886 				(void) alarm(sh->s_time - now);
    887 			}
    888 		}
    889 		(void) sigprocmask(SIG_UNBLOCK, (sigset_t *)&s_mask, NULL);
    890 
    891 		return (0);
    892 	}
    893 
    894 	/* unregister an alarm handler */
    895 	(void) sigprocmask(SIG_BLOCK, (sigset_t *)&s_mask, NULL);
    896 	tp = (struct snoop_handler *)&snoop_hp;
    897 	for (hp = snoop_hp; hp; hp = next) {
    898 		next = hp->s_next;
    899 		if (s_handler == NULL || hp->s_handler == s_handler) {
    900 			ret = 0;
    901 			tp->s_next = hp->s_next;
    902 			if (snoop_tp == hp) {
    903 				if (tp == (struct snoop_handler *)&snoop_hp)
    904 					snoop_tp = NULL;
    905 				else
    906 					snoop_tp = (struct snoop_handler *)tp;
    907 			}
    908 			free((void *)hp);
    909 		} else {
    910 			if (nalarm == 0 || nalarm > hp->s_time)
    911 				nalarm = now < hp->s_time ? hp->s_time :
    912 				    now + 1;
    913 			tp = hp;
    914 		}
    915 	}
    916 	/*
    917 	 * Stop or adjust timer
    918 	 */
    919 	if (snoop_hp == NULL) {
    920 		snoop_nalarm = 0;
    921 		(void) alarm(0);
    922 	} else if (nalarm > 0 && nalarm < snoop_nalarm) {
    923 		snoop_nalarm = nalarm;
    924 		(void) alarm(nalarm - now);
    925 	}
    926 
    927 	(void) sigprocmask(SIG_UNBLOCK, (sigset_t *)&s_mask, NULL);
    928 	return (ret);
    929 }
    930 
    931 /*
    932  * snoop_recover: reset snoop's output area, and any internal variables,
    933  * to allow continuation.
    934  * XXX: make this an interface such that each interpreter can
    935  * register a reset routine.
    936  */
    937 void
    938 snoop_recover(void)
    939 {
    940 	int i;
    941 
    942 	/* Error recovery: reset output_area and associated variables */
    943 	for (i = 0; i < MAXSUM; i++)
    944 		sumline[i][0] = '\0';
    945 	detail_line[0] = '\0';
    946 	line[0] = '\0';
    947 	encap[0] = '\0';
    948 	sumcount = 0;
    949 
    950 	/* stacking/unstacking cannot be relied upon */
    951 	encap_levels = 0;
    952 	total_encap_levels = 1;
    953 
    954 	/* remove any pending timeouts */
    955 	(void) snoop_alarm(0, NULL);
    956 }
    957 
    958 /*
    959  * snoop_sigrecover: global sigaction routine to manage recovery
    960  * from catastrophic interpreter failures while interpreting
    961  * corrupt trace files/packets. SIGALRM timeouts, program errors,
    962  * and user termination are all handled. In the case of a corrupt
    963  * packet or confused interpreter, the packet will be skipped, and
    964  * execution will continue in scan().
    965  *
    966  * Global alarm handling (see snoop_alarm()) is managed here.
    967  *
    968  * Variables must be volatile to force the compiler to not optimize
    969  * out the signal blocking.
    970  */
    971 /*ARGSUSED*/
    972 static void
    973 snoop_sigrecover(int sig, siginfo_t *info, void *p)
    974 {
    975 	volatile time_t now;
    976 	volatile time_t nalarm = 0;
    977 	volatile struct snoop_handler *hp;
    978 
    979 	/*
    980 	 * Invoke any registered alarms. This involves first calculating
    981 	 * the time for the next alarm, setting it up, then progressing
    982 	 * through handler invocations. Note that since handlers may
    983 	 * use siglongjmp(), in the worst case handlers may be serviced
    984 	 * at a later time.
    985 	 */
    986 	if (sig == SIGALRM) {
    987 		now = time(NULL);
    988 		/* Calculate next alarm time */
    989 		for (hp = snoop_hp; hp; hp = hp->s_next) {
    990 			if (hp->s_time) {
    991 				if ((hp->s_time - now) > 0) {
    992 					if (nalarm == 0 || nalarm > hp->s_time)
    993 						nalarm = now < hp->s_time ?
    994 						    hp->s_time : now + 1;
    995 				}
    996 			}
    997 		}
    998 		/* Setup next alarm */
    999 		if (nalarm) {
   1000 			snoop_nalarm = nalarm;
   1001 			(void) alarm(nalarm - now);
   1002 		} else {
   1003 			snoop_nalarm = 0;
   1004 		}
   1005 
   1006 		/* Invoke alarm handlers (may not return) */
   1007 		for (hp = snoop_hp; hp; hp = hp->s_next) {
   1008 			if (hp->s_time) {
   1009 				if ((now - hp->s_time) >= 0) {
   1010 					hp->s_time = 0;	/* only invoke once */
   1011 					if (hp->s_handler)
   1012 						hp->s_handler();
   1013 				}
   1014 			}
   1015 		}
   1016 	} else {
   1017 		snoop_nrecover++;
   1018 	}
   1019 
   1020 	/*
   1021 	 * Exit if a signal has occurred after snoop has begun the process
   1022 	 * of quitting.
   1023 	 */
   1024 	if (quitting)
   1025 		exit(1);
   1026 
   1027 	/*
   1028 	 * If an alarm handler has timed out, and snoop_nrecover has
   1029 	 * reached SNOOP_MAXRECOVER, skip to the next packet.
   1030 	 *
   1031 	 * If any other signal has occurred, and snoop_nrecover has
   1032 	 * reached SNOOP_MAXRECOVER, give up.
   1033 	 */
   1034 	if (sig == SIGALRM) {
   1035 		if (ioctl(STDOUT_FILENO, I_CANPUT, 0) == 0) {
   1036 			/*
   1037 			 * We've stalled on output, which is not a critical
   1038 			 * failure.  Reset the recovery counter so we do not
   1039 			 * consider this a persistent failure, and return so
   1040 			 * we do not skip this packet.
   1041 			 */
   1042 			snoop_nrecover = 0;
   1043 			return;
   1044 		}
   1045 		if (snoop_nrecover >= SNOOP_MAXRECOVER) {
   1046 			(void) fprintf(stderr,
   1047 			    "snoop: WARNING: skipping from packet %d\n",
   1048 			    count);
   1049 			snoop_nrecover = 0;
   1050 		} else {
   1051 			/* continue trying */
   1052 			return;
   1053 		}
   1054 	} else if (snoop_nrecover >= SNOOP_MAXRECOVER) {
   1055 		(void) fprintf(stderr,
   1056 		    "snoop: ERROR: cannot recover from packet %d\n", count);
   1057 		exit(1);
   1058 	}
   1059 
   1060 #ifdef DEBUG
   1061 	(void) fprintf(stderr, "snoop_sigrecover(%d, %p, %p)\n", sig, info, p);
   1062 #endif /* DEBUG */
   1063 
   1064 	/*
   1065 	 * Prepare to quit. This allows final processing to occur
   1066 	 * after first terminal interruption.
   1067 	 */
   1068 	if (sig == SIGTERM || sig == SIGHUP || sig == SIGINT) {
   1069 		quitting = 1;
   1070 		return;
   1071 	} else if (sig != -1 && sig != SIGALRM) {
   1072 		/* Inform user that snoop has taken a fault */
   1073 		(void) fprintf(stderr,
   1074 		    "WARNING: received signal %d from packet %d\n",
   1075 		    sig, count);
   1076 	}
   1077 
   1078 	/* Reset interpreter variables */
   1079 	snoop_recover();
   1080 
   1081 	/* Continue in scan() with the next packet */
   1082 	siglongjmp(jmp_env, 1);
   1083 	/*NOTREACHED*/
   1084 }
   1085 
   1086 /*
   1087  * Protected malloc for global error recovery: prepare for interpreter
   1088  * failures with corrupted packets or confused interpreters.  Dynamically
   1089  * allocate `nbytes' bytes, and sandwich it between two PROT_NONE pages to
   1090  * catch writes outside of the allocated region.
   1091  */
   1092 static char *
   1093 protmalloc(size_t nbytes)
   1094 {
   1095 	caddr_t start;
   1096 	int psz = sysconf(_SC_PAGESIZE);
   1097 
   1098 	nbytes = P2ROUNDUP(nbytes, psz);
   1099 	start = mmap(NULL, nbytes + psz * 2, PROT_READ|PROT_WRITE,
   1100 	    MAP_PRIVATE|MAP_ANON, -1, 0);
   1101 	if (start == MAP_FAILED) {
   1102 		perror("Error: protmalloc: mmap");
   1103 		return (NULL);
   1104 	}
   1105 	assert(IS_P2ALIGNED(start, psz));
   1106 	if (mprotect(start, 1, PROT_NONE) == -1)
   1107 		perror("Warning: mprotect");
   1108 
   1109 	start += psz;
   1110 	if (mprotect(start + nbytes, 1, PROT_NONE) == -1)
   1111 		perror("Warning: mprotect");
   1112 
   1113 	return (start);
   1114 }
   1115 
   1116 /*
   1117  * resetperm - reduce security vulnerabilities by resetting
   1118  * owner/group/permissions. Always attempt setuid() - if we have
   1119  * permission to drop our privilege level, do so.
   1120  */
   1121 void
   1122 resetperm(void)
   1123 {
   1124 	if (geteuid() == 0) {
   1125 		(void) setgid(GID_NOBODY);
   1126 		(void) setuid(UID_NOBODY);
   1127 	}
   1128 }
   1129