xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.c (revision 7014882c6a3672fd0e5d60200af8643ae53c5928)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <stdio.h>
28 #include <unistd.h>
29 #include <stropts.h>
30 #include <string.h>
31 #include <stdlib.h>
32 #include <fcntl.h>
33 #include <stdarg.h>
34 #include <setjmp.h>
35 #include <string.h>
36 #include <errno.h>
37 #include <sys/types.h>
38 #include <sys/time.h>
39 #include <signal.h>
40 #include <sys/mman.h>
41 #include <assert.h>
42 #include <sys/sysmacros.h>
43 
44 #include <sys/socket.h>
45 #include <sys/pfmod.h>
46 #include <net/if.h>
47 #include <netinet/in_systm.h>
48 #include <netinet/in.h>
49 #include <netinet/if_ether.h>
50 #include <netdb.h>
51 
52 #include "snoop.h"
53 
54 static int snaplen;
55 
56 /* Global error recovery variables */
57 sigjmp_buf jmp_env, ojmp_env;		/* error recovery jmp buf */
58 int snoop_nrecover;			/* number of recoveries on curr pkt */
59 int quitting;				/* user termination flag */
60 
61 static struct snoop_handler *snoop_hp;		/* global alarm handler head */
62 static struct snoop_handler *snoop_tp;		/* global alarm handler tail */
63 static time_t snoop_nalarm;			/* time of next alarm */
64 
65 /* protected interpreter output areas */
66 #define	MAXSUM		8
67 #define	REDZONE		64
68 static char *sumline[MAXSUM];
69 static char *detail_line;
70 static char *line;
71 static char *encap;
72 
73 static int audio;
74 int maxcount;	/* maximum no of packets to capture */
75 int count;	/* count of packets captured */
76 static int sumcount;
77 int x_offset = -1;
78 int x_length = 0x7fffffff;
79 FILE *namefile;
80 boolean_t Pflg;
81 boolean_t Iflg;
82 boolean_t qflg;
83 boolean_t rflg;
84 #ifdef	DEBUG
85 boolean_t zflg;
86 #endif
87 struct Pf_ext_packetfilt pf;
88 
89 static int vlanid = 0;
90 
91 static void usage(void);
92 static void snoop_sigrecover(int sig, siginfo_t *info, void *p);
93 static char *protmalloc(size_t);
94 static void resetperm(void);
95 
96 int
97 main(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 
513 static int tone[] = {
514 0x076113, 0x153333, 0x147317, 0x144311, 0x147315, 0x050353, 0x037103, 0x051106,
515 0x157155, 0x142723, 0x133273, 0x134664, 0x051712, 0x024465, 0x026447, 0x072473,
516 0x136715, 0x126257, 0x135256, 0x047344, 0x034476, 0x027464, 0x036062, 0x133334,
517 0x127256, 0x130660, 0x136262, 0x040724, 0x016446, 0x025437, 0x137171, 0x127672,
518 0x124655, 0x134654, 0x032741, 0x021447, 0x037450, 0x125675, 0x127650, 0x077277,
519 0x046514, 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  */
529 void
530 click(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 */
542 void
543 show_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  */
561 void
562 show_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  */
654 char *
655 get_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*/
671 char *
672 get_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  */
697 void
698 set_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 */
712 void
713 pr_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  */
755 void
756 pr_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  */
774 static void
775 usage(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  */
830 static void
831 sdefault(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*/
849 int
850 snoop_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  */
936 void
937 snoop_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*/
971 static void
972 snoop_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  */
1091 static char *
1092 protmalloc(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  */
1120 void
1121 resetperm(void)
1122 {
1123 	if (geteuid() == 0) {
1124 		(void) setgid(GID_NOBODY);
1125 		(void) setuid(UID_NOBODY);
1126 	}
1127 }
1128