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