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