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