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