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