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 *
process_ocapfile(const char * arg,size_t * nfiles,off_t * limit)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
main(int argc,char ** argv)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
click(int len)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
show_count()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
show_pktinfo(int flags,int num,char * src,char * dst,struct timeval * ptvp,struct timeval * tvp,int drops,int len)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 *
get_sum_line()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 *
get_detail_line(int off,int len)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
set_vlan_id(int id)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
pr_err(const char * fmt,...)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
pr_errdlpi(dlpi_handle_t dh,const char * cmd,int err)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
usage(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
sdefault(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
snoop_alarm(int s_sec,void (* s_handler)())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
snoop_recover(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
snoop_sigrecover(int sig,siginfo_t * info,void * p)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 *
protmalloc(size_t nbytes)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
resetperm(void)1169 resetperm(void)
1170 {
1171 if (geteuid() == 0) {
1172 (void) setgid(GID_NOBODY);
1173 (void) setuid(UID_NOBODY);
1174 }
1175 }
1176