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