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