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