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