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