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(int len)
531{
532	len /= 8;
533	len = len ? len : 4;
534
535	if (audio) {
536		(void) write(audio, tone, len);
537	}
538}
539
540/* Display a count of packets */
541void
542show_count()
543{
544	static int prev = -1;
545
546	if (count == prev)
547		return;
548
549	prev = count;
550	(void) fprintf(stderr, "\r%d ", count);
551}
552
553#define	ENCAP_LEN	16	/* Hold "(NN encap)" */
554
555/*
556 * Display data that's external to the packet.
557 * This constitutes the first half of the summary
558 * line display.
559 */
560void
561show_pktinfo(int flags, int num, char *src, char *dst, struct timeval *ptvp,
562    struct timeval *tvp, int drops, int len)
563{
564	struct tm *tm;
565	static struct timeval tvp0;
566	int sec, usec;
567	char *lp = line;
568	int i, start;
569
570	if (flags & F_NUM) {
571		(void) sprintf(lp, "%3d ", num);
572		lp += strlen(lp);
573	}
574	tm = localtime(&tvp->tv_sec);
575
576	if (flags & F_TIME) {
577		if (flags & F_ATIME) {
578			(void) sprintf(lp, "%02d:%02d:%02d.%05d ",
579			    tm->tm_hour, tm->tm_min, tm->tm_sec,
580			    (int)tvp->tv_usec / 10);
581			lp += strlen(lp);
582		} else {
583			if (flags & F_RTIME) {
584				if (tvp0.tv_sec == 0) {
585					tvp0.tv_sec = tvp->tv_sec;
586					tvp0.tv_usec = tvp->tv_usec;
587				}
588				ptvp = &tvp0;
589			}
590			sec  = tvp->tv_sec  - ptvp->tv_sec;
591			usec = tvp->tv_usec - ptvp->tv_usec;
592			if (usec < 0) {
593				usec += 1000000;
594				sec  -= 1;
595			}
596			(void) sprintf(lp, "%3d.%05d ", sec, usec / 10);
597			lp += strlen(lp);
598		}
599	}
600
601	if ((flags & F_SUM) && !(flags & F_ALLSUM) && (vlanid != 0)) {
602		(void) snprintf(lp, MAXLINE, "VLAN#%i: ", vlanid);
603		lp += strlen(lp);
604	}
605
606	if (flags & F_WHO) {
607		(void) sprintf(lp, "%12s -> %-12s ", src, dst);
608		lp += strlen(lp);
609	}
610
611	if (flags & F_DROPS) {
612		(void) sprintf(lp, "drops: %d ", drops);
613		lp += strlen(lp);
614	}
615
616	if (flags & F_LEN) {
617		(void) sprintf(lp, "length: %4d  ", len);
618		lp += strlen(lp);
619	}
620
621	if (flags & F_SUM) {
622		if (flags & F_ALLSUM)
623			(void) printf("________________________________\n");
624
625		start = flags & F_ALLSUM ? 0 : sumcount - 1;
626		(void) sprintf(encap, "  (%d encap)", total_encap_levels - 1);
627		(void) printf("%s%s%s\n", line, sumline[start],
628		    ((flags & F_ALLSUM) || (total_encap_levels == 1)) ? "" :
629		    encap);
630
631		for (i = start + 1; i < sumcount; i++)
632			(void) printf("%s%s\n", line, sumline[i]);
633
634		sumcount = 0;
635	}
636
637	if (flags & F_DTAIL) {
638		(void) printf("%s\n\n", detail_line);
639		detail_line[0] = '\0';
640	}
641}
642
643/*
644 * The following three routines are called back
645 * from the interpreters to display their stuff.
646 * The theory is that when snoop becomes a window
647 * based tool we can just supply a new version of
648 * get_sum_line and get_detail_line and not have
649 * to touch the interpreters at all.
650 */
651char *
652get_sum_line()
653{
654	int tsumcount = sumcount;
655
656	if (sumcount >= MAXSUM) {
657		sumcount = 0;			/* error recovery */
658		pr_err(
659		    "get_sum_line: sumline overflow (sumcount=%d, MAXSUM=%d)\n",
660		    tsumcount, MAXSUM);
661	}
662
663	sumline[sumcount][0] = '\0';
664	return (sumline[sumcount++]);
665}
666
667/*ARGSUSED*/
668char *
669get_detail_line(int off, int len)
670{
671	if (detail_line[0]) {
672		(void) printf("%s\n", detail_line);
673		detail_line[0] = '\0';
674	}
675	return (detail_line);
676}
677
678/*
679 * This function exists to make sure that VLAN information is
680 * prepended to summary lines displayed.  The problem this function
681 * solves is how to display VLAN information while in summary mode.
682 * Each interpretor uses the get_sum_line and get_detail_line functions
683 * to get a character buffer to display information to the user.
684 * get_sum_line is the important one here.  Each call to get_sum_line
685 * gets a buffer which stores one line of information.  In summary mode,
686 * the last line generated is the line printed.  Instead of changing each
687 * interpreter to add VLAN information to the summary line, the ethernet
688 * interpreter changes to call this function and set an ID.  If the ID is not
689 * zero and snoop is in default summary mode, snoop displays the
690 * VLAN information at the beginning of the output line.  Otherwise,
691 * no VLAN information is displayed.
692 */
693void
694set_vlan_id(int id)
695{
696	vlanid = id;
697}
698
699/*
700 * Print an error.
701 * Works like printf (fmt string and variable args)
702 * except that it will substitute an error message
703 * for a "%m" string (like syslog) and it calls
704 * long_jump - it doesn't return to where it was
705 * called from - it goes to the last setjmp().
706 */
707/* VARARGS1 */
708void
709pr_err(const char *fmt, ...)
710{
711	va_list ap;
712	char buf[1024], *p2;
713	const char *p1;
714
715	(void) strcpy(buf, "snoop: ");
716	p2 = buf + strlen(buf);
717
718	/*
719	 * Note that we terminate the buffer with '\n' and '\0'.
720	 */
721	for (p1 = fmt; *p1 != '\0' && p2 < buf + sizeof (buf) - 2; p1++) {
722		if (*p1 == '%' && *(p1+1) == 'm') {
723			const char *errstr;
724
725			if ((errstr = strerror(errno)) != NULL) {
726				*p2 = '\0';
727				(void) strlcat(buf, errstr, sizeof (buf));
728				p2 += strlen(p2);
729			}
730			p1++;
731		} else {
732			*p2++ = *p1;
733		}
734	}
735	if (p2 > buf && *(p2-1) != '\n')
736		*p2++ = '\n';
737	*p2 = '\0';
738
739	va_start(ap, fmt);
740	/* LINTED: E_SEC_PRINTF_VAR_FMT */
741	(void) vfprintf(stderr, buf, ap);
742	va_end(ap);
743	snoop_sigrecover(-1, NULL, NULL);	/* global error recovery */
744}
745
746/*
747 * Store a copy of linkname associated with the DLPI handle.
748 * Save errno before closing the dlpi handle so that the
749 * correct error value is used if 'err' is a system error.
750 */
751void
752pr_errdlpi(dlpi_handle_t dh, const char *cmd, int err)
753{
754	int save_errno = errno;
755	char linkname[DLPI_LINKNAME_MAX];
756
757	(void) strlcpy(linkname, dlpi_linkname(dh), sizeof (linkname));
758
759	dlpi_close(dh);
760	errno = save_errno;
761
762	pr_err("%s on \"%s\": %s", cmd, linkname, dlpi_strerror(err));
763}
764
765/*
766 * Ye olde usage proc
767 * PLEASE keep this up to date!
768 * Naive users *love* this stuff.
769 */
770static void
771usage(void)
772{
773	(void) fprintf(stderr, "\nUsage:  snoop\n");
774	(void) fprintf(stderr,
775	"\t[ -a ]			# Listen to packets on audio\n");
776	(void) fprintf(stderr,
777	"\t[ -d link ]		# Listen on named link\n");
778	(void) fprintf(stderr,
779	"\t[ -s snaplen ]		# Truncate packets\n");
780	(void) fprintf(stderr,
781	"\t[ -I IP interface ]		# Listen on named IP interface\n");
782	(void) fprintf(stderr,
783	"\t[ -c count ]		# Quit after count packets\n");
784	(void) fprintf(stderr,
785	"\t[ -P ]			# Turn OFF promiscuous mode\n");
786	(void) fprintf(stderr,
787	"\t[ -D ]			# Report dropped packets\n");
788	(void) fprintf(stderr,
789	"\t[ -S ]			# Report packet size\n");
790	(void) fprintf(stderr,
791	"\t[ -i file ]		# Read previously captured packets\n");
792	(void) fprintf(stderr,
793	"\t[ -o file ]		# Capture packets in file\n");
794	(void) fprintf(stderr,
795	"\t[ -n file ]		# Load addr-to-name table from file\n");
796	(void) fprintf(stderr,
797	"\t[ -N ]			# Create addr-to-name table\n");
798	(void) fprintf(stderr,
799	"\t[ -t  r|a|d ]		# Time: Relative, Absolute or Delta\n");
800	(void) fprintf(stderr,
801	"\t[ -v ]			# Verbose packet display\n");
802	(void) fprintf(stderr,
803	"\t[ -V ]			# Show all summary lines\n");
804	(void) fprintf(stderr,
805	"\t[ -p first[,last] ]	# Select packet(s) to display\n");
806	(void) fprintf(stderr,
807	"\t[ -x offset[,length] ]	# Hex dump from offset for length\n");
808	(void) fprintf(stderr,
809	"\t[ -C ]			# Print packet filter code\n");
810	(void) fprintf(stderr,
811	"\t[ -q ]			# Suppress printing packet count\n");
812	(void) fprintf(stderr,
813	"\t[ -r ]			# Do not resolve address to name\n");
814	(void) fprintf(stderr,
815	"\n\t[ filter expression ]\n");
816	(void) fprintf(stderr, "\nExample:\n");
817	(void) fprintf(stderr, "\tsnoop -o saved  host fred\n\n");
818	(void) fprintf(stderr, "\tsnoop -i saved -tr -v -p19\n");
819	exit(1);
820}
821
822/*
823 * sdefault: default global alarm handler. Causes the current packet
824 * to be skipped.
825 */
826static void
827sdefault(void)
828{
829	snoop_nrecover = SNOOP_MAXRECOVER;
830}
831
832/*
833 * snoop_alarm: register or unregister an alarm handler to be called after
834 * s_sec seconds. Because snoop wasn't written to tolerate random signal
835 * delivery, periodic SIGALRM delivery (or SA_RESTART) cannot be used.
836 *
837 * s_sec argument of 0 seconds unregisters the handler.
838 * s_handler argument of NULL registers default handler sdefault(), or
839 * unregisters all signal handlers (for error recovery).
840 *
841 * Variables must be volatile to force the compiler to not optimize
842 * out the signal blocking.
843 */
844/*ARGSUSED*/
845int
846snoop_alarm(int s_sec, void (*s_handler)())
847{
848	volatile time_t now;
849	volatile time_t nalarm = 0;
850	volatile struct snoop_handler *sh = NULL;
851	volatile struct snoop_handler *hp, *tp, *next;
852	volatile sigset_t s_mask;
853	volatile int ret = -1;
854
855	(void) sigemptyset((sigset_t *)&s_mask);
856	(void) sigaddset((sigset_t *)&s_mask, SIGALRM);
857	if (s_sec < 0)
858		return (-1);
859
860	/* register an alarm handler */
861	now = time(NULL);
862	if (s_sec) {
863		sh = malloc(sizeof (struct snoop_handler));
864		sh->s_time = now + s_sec;
865		if (s_handler == NULL)
866			s_handler = sdefault;
867		sh->s_handler = s_handler;
868		sh->s_next = NULL;
869		(void) sigprocmask(SIG_BLOCK, (sigset_t *)&s_mask, NULL);
870		if (snoop_hp == NULL) {
871			snoop_hp = snoop_tp = (struct snoop_handler *)sh;
872
873			snoop_nalarm = sh->s_time;
874			(void) alarm(sh->s_time - now);
875		} else {
876			snoop_tp->s_next = (struct snoop_handler *)sh;
877			snoop_tp = (struct snoop_handler *)sh;
878
879			if (sh->s_time < snoop_nalarm) {
880				snoop_nalarm = sh->s_time;
881				(void) alarm(sh->s_time - now);
882			}
883		}
884		(void) sigprocmask(SIG_UNBLOCK, (sigset_t *)&s_mask, NULL);
885
886		return (0);
887	}
888
889	/* unregister an alarm handler */
890	(void) sigprocmask(SIG_BLOCK, (sigset_t *)&s_mask, NULL);
891	tp = (struct snoop_handler *)&snoop_hp;
892	for (hp = snoop_hp; hp; hp = next) {
893		next = hp->s_next;
894		if (s_handler == NULL || hp->s_handler == s_handler) {
895			ret = 0;
896			tp->s_next = hp->s_next;
897			if (snoop_tp == hp) {
898				if (tp == (struct snoop_handler *)&snoop_hp)
899					snoop_tp = NULL;
900				else
901					snoop_tp = (struct snoop_handler *)tp;
902			}
903			free((void *)hp);
904		} else {
905			if (nalarm == 0 || nalarm > hp->s_time)
906				nalarm = now < hp->s_time ? hp->s_time :
907				    now + 1;
908			tp = hp;
909		}
910	}
911	/*
912	 * Stop or adjust timer
913	 */
914	if (snoop_hp == NULL) {
915		snoop_nalarm = 0;
916		(void) alarm(0);
917	} else if (nalarm > 0 && nalarm < snoop_nalarm) {
918		snoop_nalarm = nalarm;
919		(void) alarm(nalarm - now);
920	}
921
922	(void) sigprocmask(SIG_UNBLOCK, (sigset_t *)&s_mask, NULL);
923	return (ret);
924}
925
926/*
927 * snoop_recover: reset snoop's output area, and any internal variables,
928 * to allow continuation.
929 * XXX: make this an interface such that each interpreter can
930 * register a reset routine.
931 */
932void
933snoop_recover(void)
934{
935	int i;
936
937	/* Error recovery: reset output_area and associated variables */
938	for (i = 0; i < MAXSUM; i++)
939		sumline[i][0] = '\0';
940	detail_line[0] = '\0';
941	line[0] = '\0';
942	encap[0] = '\0';
943	sumcount = 0;
944
945	/* stacking/unstacking cannot be relied upon */
946	encap_levels = 0;
947	total_encap_levels = 1;
948
949	/* remove any pending timeouts */
950	(void) snoop_alarm(0, NULL);
951}
952
953/*
954 * snoop_sigrecover: global sigaction routine to manage recovery
955 * from catastrophic interpreter failures while interpreting
956 * corrupt trace files/packets. SIGALRM timeouts, program errors,
957 * and user termination are all handled. In the case of a corrupt
958 * packet or confused interpreter, the packet will be skipped, and
959 * execution will continue in scan().
960 *
961 * Global alarm handling (see snoop_alarm()) is managed here.
962 *
963 * Variables must be volatile to force the compiler to not optimize
964 * out the signal blocking.
965 */
966/*ARGSUSED*/
967static void
968snoop_sigrecover(int sig, siginfo_t *info, void *p)
969{
970	volatile time_t now;
971	volatile time_t nalarm = 0;
972	volatile struct snoop_handler *hp;
973
974	/*
975	 * Invoke any registered alarms. This involves first calculating
976	 * the time for the next alarm, setting it up, then progressing
977	 * through handler invocations. Note that since handlers may
978	 * use siglongjmp(), in the worst case handlers may be serviced
979	 * at a later time.
980	 */
981	if (sig == SIGALRM) {
982		now = time(NULL);
983		/* Calculate next alarm time */
984		for (hp = snoop_hp; hp; hp = hp->s_next) {
985			if (hp->s_time) {
986				if ((hp->s_time - now) > 0) {
987					if (nalarm == 0 || nalarm > hp->s_time)
988						nalarm = now < hp->s_time ?
989						    hp->s_time : now + 1;
990				}
991			}
992		}
993		/* Setup next alarm */
994		if (nalarm) {
995			snoop_nalarm = nalarm;
996			(void) alarm(nalarm - now);
997		} else {
998			snoop_nalarm = 0;
999		}
1000
1001		/* Invoke alarm handlers (may not return) */
1002		for (hp = snoop_hp; hp; hp = hp->s_next) {
1003			if (hp->s_time) {
1004				if ((now - hp->s_time) >= 0) {
1005					hp->s_time = 0;	/* only invoke once */
1006					if (hp->s_handler)
1007						hp->s_handler();
1008				}
1009			}
1010		}
1011	} else {
1012		snoop_nrecover++;
1013	}
1014
1015	/*
1016	 * Exit if a signal has occurred after snoop has begun the process
1017	 * of quitting.
1018	 */
1019	if (quitting)
1020		exit(1);
1021
1022	/*
1023	 * If an alarm handler has timed out, and snoop_nrecover has
1024	 * reached SNOOP_MAXRECOVER, skip to the next packet.
1025	 *
1026	 * If any other signal has occurred, and snoop_nrecover has
1027	 * reached SNOOP_MAXRECOVER, give up.
1028	 */
1029	if (sig == SIGALRM) {
1030		if (ioctl(STDOUT_FILENO, I_CANPUT, 0) == 0) {
1031			/*
1032			 * We've stalled on output, which is not a critical
1033			 * failure.  Reset the recovery counter so we do not
1034			 * consider this a persistent failure, and return so
1035			 * we do not skip this packet.
1036			 */
1037			snoop_nrecover = 0;
1038			return;
1039		}
1040		if (snoop_nrecover >= SNOOP_MAXRECOVER) {
1041			(void) fprintf(stderr,
1042			    "snoop: WARNING: skipping from packet %d\n",
1043			    count);
1044			snoop_nrecover = 0;
1045		} else {
1046			/* continue trying */
1047			return;
1048		}
1049	} else if (snoop_nrecover >= SNOOP_MAXRECOVER) {
1050		(void) fprintf(stderr,
1051		    "snoop: ERROR: cannot recover from packet %d\n", count);
1052		exit(1);
1053	}
1054
1055#ifdef DEBUG
1056	(void) fprintf(stderr, "snoop_sigrecover(%d, %p, %p)\n", sig, info, p);
1057#endif /* DEBUG */
1058
1059	/*
1060	 * Prepare to quit. This allows final processing to occur
1061	 * after first terminal interruption.
1062	 */
1063	if (sig == SIGTERM || sig == SIGHUP || sig == SIGINT) {
1064		quitting = 1;
1065		return;
1066	} else if (sig != -1 && sig != SIGALRM) {
1067		/* Inform user that snoop has taken a fault */
1068		(void) fprintf(stderr,
1069		    "WARNING: received signal %d from packet %d\n",
1070		    sig, count);
1071	}
1072
1073	/* Reset interpreter variables */
1074	snoop_recover();
1075
1076	/* Continue in scan() with the next packet */
1077	siglongjmp(jmp_env, 1);
1078	/*NOTREACHED*/
1079}
1080
1081/*
1082 * Protected malloc for global error recovery: prepare for interpreter
1083 * failures with corrupted packets or confused interpreters.  Dynamically
1084 * allocate `nbytes' bytes, and sandwich it between two PROT_NONE pages to
1085 * catch writes outside of the allocated region.
1086 */
1087static char *
1088protmalloc(size_t nbytes)
1089{
1090	caddr_t start;
1091	int psz = sysconf(_SC_PAGESIZE);
1092
1093	nbytes = P2ROUNDUP(nbytes, psz);
1094	start = mmap(NULL, nbytes + psz * 2, PROT_READ|PROT_WRITE,
1095	    MAP_PRIVATE|MAP_ANON, -1, 0);
1096	if (start == MAP_FAILED) {
1097		perror("Error: protmalloc: mmap");
1098		return (NULL);
1099	}
1100	assert(IS_P2ALIGNED(start, psz));
1101	if (mprotect(start, 1, PROT_NONE) == -1)
1102		perror("Warning: mprotect");
1103
1104	start += psz;
1105	if (mprotect(start + nbytes, 1, PROT_NONE) == -1)
1106		perror("Warning: mprotect");
1107
1108	return (start);
1109}
1110
1111/*
1112 * resetperm - reduce security vulnerabilities by resetting
1113 * owner/group/permissions. Always attempt setuid() - if we have
1114 * permission to drop our privilege level, do so.
1115 */
1116void
1117resetperm(void)
1118{
1119	if (geteuid() == 0) {
1120		(void) setgid(GID_NOBODY);
1121		(void) setuid(UID_NOBODY);
1122	}
1123}
1124