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 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 530 click(len) 531 int len; 532 { 533 len /= 8; 534 len = len ? len : 4; 535 536 if (audio) { 537 (void) write(audio, tone, len); 538 } 539 } 540 541 /* Display a count of packets */ 542 void 543 show_count() 544 { 545 static int prev = -1; 546 547 if (count == prev) 548 return; 549 550 prev = count; 551 (void) fprintf(stderr, "\r%d ", count); 552 } 553 554 #define ENCAP_LEN 16 /* Hold "(NN encap)" */ 555 556 /* 557 * Display data that's external to the packet. 558 * This constitutes the first half of the summary 559 * line display. 560 */ 561 void 562 show_pktinfo(flags, num, src, dst, ptvp, tvp, drops, len) 563 int flags, num, drops, len; 564 char *src, *dst; 565 struct timeval *ptvp, *tvp; 566 { 567 struct tm *tm; 568 static struct timeval tvp0; 569 int sec, usec; 570 char *lp = line; 571 int i, start; 572 573 if (flags & F_NUM) { 574 (void) sprintf(lp, "%3d ", num); 575 lp += strlen(lp); 576 } 577 tm = localtime(&tvp->tv_sec); 578 579 if (flags & F_TIME) { 580 if (flags & F_ATIME) { 581 (void) sprintf(lp, "%d:%02d:%d.%05d ", 582 tm->tm_hour, tm->tm_min, tm->tm_sec, 583 (int)tvp->tv_usec / 10); 584 lp += strlen(lp); 585 } else { 586 if (flags & F_RTIME) { 587 if (tvp0.tv_sec == 0) { 588 tvp0.tv_sec = tvp->tv_sec; 589 tvp0.tv_usec = tvp->tv_usec; 590 } 591 ptvp = &tvp0; 592 } 593 sec = tvp->tv_sec - ptvp->tv_sec; 594 usec = tvp->tv_usec - ptvp->tv_usec; 595 if (usec < 0) { 596 usec += 1000000; 597 sec -= 1; 598 } 599 (void) sprintf(lp, "%3d.%05d ", sec, usec / 10); 600 lp += strlen(lp); 601 } 602 } 603 604 if ((flags & F_SUM) && !(flags & F_ALLSUM) && (vlanid != 0)) { 605 (void) snprintf(lp, MAXLINE, "VLAN#%i: ", vlanid); 606 lp += strlen(lp); 607 } 608 609 if (flags & F_WHO) { 610 (void) sprintf(lp, "%12s -> %-12s ", src, dst); 611 lp += strlen(lp); 612 } 613 614 if (flags & F_DROPS) { 615 (void) sprintf(lp, "drops: %d ", drops); 616 lp += strlen(lp); 617 } 618 619 if (flags & F_LEN) { 620 (void) sprintf(lp, "length: %4d ", len); 621 lp += strlen(lp); 622 } 623 624 if (flags & F_SUM) { 625 if (flags & F_ALLSUM) 626 (void) printf("________________________________\n"); 627 628 start = flags & F_ALLSUM ? 0 : sumcount - 1; 629 (void) sprintf(encap, " (%d encap)", total_encap_levels - 1); 630 (void) printf("%s%s%s\n", line, sumline[start], 631 ((flags & F_ALLSUM) || (total_encap_levels == 1)) ? "" : 632 encap); 633 634 for (i = start + 1; i < sumcount; i++) 635 (void) printf("%s%s\n", line, sumline[i]); 636 637 sumcount = 0; 638 } 639 640 if (flags & F_DTAIL) { 641 (void) printf("%s\n\n", detail_line); 642 detail_line[0] = '\0'; 643 } 644 } 645 646 /* 647 * The following three routines are called back 648 * from the interpreters to display their stuff. 649 * The theory is that when snoop becomes a window 650 * based tool we can just supply a new version of 651 * get_sum_line and get_detail_line and not have 652 * to touch the interpreters at all. 653 */ 654 char * 655 get_sum_line() 656 { 657 int tsumcount = sumcount; 658 659 if (sumcount >= MAXSUM) { 660 sumcount = 0; /* error recovery */ 661 pr_err( 662 "get_sum_line: sumline overflow (sumcount=%d, MAXSUM=%d)\n", 663 tsumcount, MAXSUM); 664 } 665 666 sumline[sumcount][0] = '\0'; 667 return (sumline[sumcount++]); 668 } 669 670 /*ARGSUSED*/ 671 char * 672 get_detail_line(off, len) 673 int off, len; 674 { 675 if (detail_line[0]) { 676 (void) printf("%s\n", detail_line); 677 detail_line[0] = '\0'; 678 } 679 return (detail_line); 680 } 681 682 /* 683 * This function exists to make sure that VLAN information is 684 * prepended to summary lines displayed. The problem this function 685 * solves is how to display VLAN information while in summary mode. 686 * Each interpretor uses the get_sum_line and get_detail_line functions 687 * to get a character buffer to display information to the user. 688 * get_sum_line is the important one here. Each call to get_sum_line 689 * gets a buffer which stores one line of information. In summary mode, 690 * the last line generated is the line printed. Instead of changing each 691 * interpreter to add VLAN information to the summary line, the ethernet 692 * interpreter changes to call this function and set an ID. If the ID is not 693 * zero and snoop is in default summary mode, snoop displays the 694 * VLAN information at the beginning of the output line. Otherwise, 695 * no VLAN information is displayed. 696 */ 697 void 698 set_vlan_id(int id) 699 { 700 vlanid = id; 701 } 702 703 /* 704 * Print an error. 705 * Works like printf (fmt string and variable args) 706 * except that it will substitute an error message 707 * for a "%m" string (like syslog) and it calls 708 * long_jump - it doesn't return to where it was 709 * called from - it goes to the last setjmp(). 710 */ 711 /* VARARGS1 */ 712 void 713 pr_err(const char *fmt, ...) 714 { 715 va_list ap; 716 char buf[1024], *p2; 717 const char *p1; 718 719 (void) strcpy(buf, "snoop: "); 720 p2 = buf + strlen(buf); 721 722 /* 723 * Note that we terminate the buffer with '\n' and '\0'. 724 */ 725 for (p1 = fmt; *p1 != '\0' && p2 < buf + sizeof (buf) - 2; p1++) { 726 if (*p1 == '%' && *(p1+1) == 'm') { 727 const char *errstr; 728 729 if ((errstr = strerror(errno)) != NULL) { 730 *p2 = '\0'; 731 (void) strlcat(buf, errstr, sizeof (buf)); 732 p2 += strlen(p2); 733 } 734 p1++; 735 } else { 736 *p2++ = *p1; 737 } 738 } 739 if (p2 > buf && *(p2-1) != '\n') 740 *p2++ = '\n'; 741 *p2 = '\0'; 742 743 va_start(ap, fmt); 744 /* LINTED: E_SEC_PRINTF_VAR_FMT */ 745 (void) vfprintf(stderr, buf, ap); 746 va_end(ap); 747 snoop_sigrecover(-1, NULL, NULL); /* global error recovery */ 748 } 749 750 /* 751 * Store a copy of linkname associated with the DLPI handle. 752 * Save errno before closing the dlpi handle so that the 753 * correct error value is used if 'err' is a system error. 754 */ 755 void 756 pr_errdlpi(dlpi_handle_t dh, const char *cmd, int err) 757 { 758 int save_errno = errno; 759 char linkname[DLPI_LINKNAME_MAX]; 760 761 (void) strlcpy(linkname, dlpi_linkname(dh), sizeof (linkname)); 762 763 dlpi_close(dh); 764 errno = save_errno; 765 766 pr_err("%s on \"%s\": %s", cmd, linkname, dlpi_strerror(err)); 767 } 768 769 /* 770 * Ye olde usage proc 771 * PLEASE keep this up to date! 772 * Naive users *love* this stuff. 773 */ 774 static void 775 usage(void) 776 { 777 (void) fprintf(stderr, "\nUsage: snoop\n"); 778 (void) fprintf(stderr, 779 "\t[ -a ] # Listen to packets on audio\n"); 780 (void) fprintf(stderr, 781 "\t[ -d link ] # Listen on named link\n"); 782 (void) fprintf(stderr, 783 "\t[ -s snaplen ] # Truncate packets\n"); 784 (void) fprintf(stderr, 785 "\t[ -I IP interface ] # Listen on named IP interface\n"); 786 (void) fprintf(stderr, 787 "\t[ -c count ] # Quit after count packets\n"); 788 (void) fprintf(stderr, 789 "\t[ -P ] # Turn OFF promiscuous mode\n"); 790 (void) fprintf(stderr, 791 "\t[ -D ] # Report dropped packets\n"); 792 (void) fprintf(stderr, 793 "\t[ -S ] # Report packet size\n"); 794 (void) fprintf(stderr, 795 "\t[ -i file ] # Read previously captured packets\n"); 796 (void) fprintf(stderr, 797 "\t[ -o file ] # Capture packets in file\n"); 798 (void) fprintf(stderr, 799 "\t[ -n file ] # Load addr-to-name table from file\n"); 800 (void) fprintf(stderr, 801 "\t[ -N ] # Create addr-to-name table\n"); 802 (void) fprintf(stderr, 803 "\t[ -t r|a|d ] # Time: Relative, Absolute or Delta\n"); 804 (void) fprintf(stderr, 805 "\t[ -v ] # Verbose packet display\n"); 806 (void) fprintf(stderr, 807 "\t[ -V ] # Show all summary lines\n"); 808 (void) fprintf(stderr, 809 "\t[ -p first[,last] ] # Select packet(s) to display\n"); 810 (void) fprintf(stderr, 811 "\t[ -x offset[,length] ] # Hex dump from offset for length\n"); 812 (void) fprintf(stderr, 813 "\t[ -C ] # Print packet filter code\n"); 814 (void) fprintf(stderr, 815 "\t[ -q ] # Suppress printing packet count\n"); 816 (void) fprintf(stderr, 817 "\t[ -r ] # Do not resolve address to name\n"); 818 (void) fprintf(stderr, 819 "\n\t[ filter expression ]\n"); 820 (void) fprintf(stderr, "\nExample:\n"); 821 (void) fprintf(stderr, "\tsnoop -o saved host fred\n\n"); 822 (void) fprintf(stderr, "\tsnoop -i saved -tr -v -p19\n"); 823 exit(1); 824 } 825 826 /* 827 * sdefault: default global alarm handler. Causes the current packet 828 * to be skipped. 829 */ 830 static void 831 sdefault(void) 832 { 833 snoop_nrecover = SNOOP_MAXRECOVER; 834 } 835 836 /* 837 * snoop_alarm: register or unregister an alarm handler to be called after 838 * s_sec seconds. Because snoop wasn't written to tolerate random signal 839 * delivery, periodic SIGALRM delivery (or SA_RESTART) cannot be used. 840 * 841 * s_sec argument of 0 seconds unregisters the handler. 842 * s_handler argument of NULL registers default handler sdefault(), or 843 * unregisters all signal handlers (for error recovery). 844 * 845 * Variables must be volatile to force the compiler to not optimize 846 * out the signal blocking. 847 */ 848 /*ARGSUSED*/ 849 int 850 snoop_alarm(int s_sec, void (*s_handler)()) 851 { 852 volatile time_t now; 853 volatile time_t nalarm = 0; 854 volatile struct snoop_handler *sh = NULL; 855 volatile struct snoop_handler *hp, *tp, *next; 856 volatile sigset_t s_mask; 857 volatile int ret = -1; 858 859 (void) sigemptyset((sigset_t *)&s_mask); 860 (void) sigaddset((sigset_t *)&s_mask, SIGALRM); 861 if (s_sec < 0) 862 return (-1); 863 864 /* register an alarm handler */ 865 now = time(NULL); 866 if (s_sec) { 867 sh = malloc(sizeof (struct snoop_handler)); 868 sh->s_time = now + s_sec; 869 if (s_handler == NULL) 870 s_handler = sdefault; 871 sh->s_handler = s_handler; 872 sh->s_next = NULL; 873 (void) sigprocmask(SIG_BLOCK, (sigset_t *)&s_mask, NULL); 874 if (snoop_hp == NULL) { 875 snoop_hp = snoop_tp = (struct snoop_handler *)sh; 876 877 snoop_nalarm = sh->s_time; 878 (void) alarm(sh->s_time - now); 879 } else { 880 snoop_tp->s_next = (struct snoop_handler *)sh; 881 snoop_tp = (struct snoop_handler *)sh; 882 883 if (sh->s_time < snoop_nalarm) { 884 snoop_nalarm = sh->s_time; 885 (void) alarm(sh->s_time - now); 886 } 887 } 888 (void) sigprocmask(SIG_UNBLOCK, (sigset_t *)&s_mask, NULL); 889 890 return (0); 891 } 892 893 /* unregister an alarm handler */ 894 (void) sigprocmask(SIG_BLOCK, (sigset_t *)&s_mask, NULL); 895 tp = (struct snoop_handler *)&snoop_hp; 896 for (hp = snoop_hp; hp; hp = next) { 897 next = hp->s_next; 898 if (s_handler == NULL || hp->s_handler == s_handler) { 899 ret = 0; 900 tp->s_next = hp->s_next; 901 if (snoop_tp == hp) { 902 if (tp == (struct snoop_handler *)&snoop_hp) 903 snoop_tp = NULL; 904 else 905 snoop_tp = (struct snoop_handler *)tp; 906 } 907 free((void *)hp); 908 } else { 909 if (nalarm == 0 || nalarm > hp->s_time) 910 nalarm = now < hp->s_time ? hp->s_time : 911 now + 1; 912 tp = hp; 913 } 914 } 915 /* 916 * Stop or adjust timer 917 */ 918 if (snoop_hp == NULL) { 919 snoop_nalarm = 0; 920 (void) alarm(0); 921 } else if (nalarm > 0 && nalarm < snoop_nalarm) { 922 snoop_nalarm = nalarm; 923 (void) alarm(nalarm - now); 924 } 925 926 (void) sigprocmask(SIG_UNBLOCK, (sigset_t *)&s_mask, NULL); 927 return (ret); 928 } 929 930 /* 931 * snoop_recover: reset snoop's output area, and any internal variables, 932 * to allow continuation. 933 * XXX: make this an interface such that each interpreter can 934 * register a reset routine. 935 */ 936 void 937 snoop_recover(void) 938 { 939 int i; 940 941 /* Error recovery: reset output_area and associated variables */ 942 for (i = 0; i < MAXSUM; i++) 943 sumline[i][0] = '\0'; 944 detail_line[0] = '\0'; 945 line[0] = '\0'; 946 encap[0] = '\0'; 947 sumcount = 0; 948 949 /* stacking/unstacking cannot be relied upon */ 950 encap_levels = 0; 951 total_encap_levels = 1; 952 953 /* remove any pending timeouts */ 954 (void) snoop_alarm(0, NULL); 955 } 956 957 /* 958 * snoop_sigrecover: global sigaction routine to manage recovery 959 * from catastrophic interpreter failures while interpreting 960 * corrupt trace files/packets. SIGALRM timeouts, program errors, 961 * and user termination are all handled. In the case of a corrupt 962 * packet or confused interpreter, the packet will be skipped, and 963 * execution will continue in scan(). 964 * 965 * Global alarm handling (see snoop_alarm()) is managed here. 966 * 967 * Variables must be volatile to force the compiler to not optimize 968 * out the signal blocking. 969 */ 970 /*ARGSUSED*/ 971 static void 972 snoop_sigrecover(int sig, siginfo_t *info, void *p) 973 { 974 volatile time_t now; 975 volatile time_t nalarm = 0; 976 volatile struct snoop_handler *hp; 977 978 /* 979 * Invoke any registered alarms. This involves first calculating 980 * the time for the next alarm, setting it up, then progressing 981 * through handler invocations. Note that since handlers may 982 * use siglongjmp(), in the worst case handlers may be serviced 983 * at a later time. 984 */ 985 if (sig == SIGALRM) { 986 now = time(NULL); 987 /* Calculate next alarm time */ 988 for (hp = snoop_hp; hp; hp = hp->s_next) { 989 if (hp->s_time) { 990 if ((hp->s_time - now) > 0) { 991 if (nalarm == 0 || nalarm > hp->s_time) 992 nalarm = now < hp->s_time ? 993 hp->s_time : now + 1; 994 } 995 } 996 } 997 /* Setup next alarm */ 998 if (nalarm) { 999 snoop_nalarm = nalarm; 1000 (void) alarm(nalarm - now); 1001 } else { 1002 snoop_nalarm = 0; 1003 } 1004 1005 /* Invoke alarm handlers (may not return) */ 1006 for (hp = snoop_hp; hp; hp = hp->s_next) { 1007 if (hp->s_time) { 1008 if ((now - hp->s_time) >= 0) { 1009 hp->s_time = 0; /* only invoke once */ 1010 if (hp->s_handler) 1011 hp->s_handler(); 1012 } 1013 } 1014 } 1015 } else { 1016 snoop_nrecover++; 1017 } 1018 1019 /* 1020 * Exit if a signal has occurred after snoop has begun the process 1021 * of quitting. 1022 */ 1023 if (quitting) 1024 exit(1); 1025 1026 /* 1027 * If an alarm handler has timed out, and snoop_nrecover has 1028 * reached SNOOP_MAXRECOVER, skip to the next packet. 1029 * 1030 * If any other signal has occurred, and snoop_nrecover has 1031 * reached SNOOP_MAXRECOVER, give up. 1032 */ 1033 if (sig == SIGALRM) { 1034 if (ioctl(STDOUT_FILENO, I_CANPUT, 0) == 0) { 1035 /* 1036 * We've stalled on output, which is not a critical 1037 * failure. Reset the recovery counter so we do not 1038 * consider this a persistent failure, and return so 1039 * we do not skip this packet. 1040 */ 1041 snoop_nrecover = 0; 1042 return; 1043 } 1044 if (snoop_nrecover >= SNOOP_MAXRECOVER) { 1045 (void) fprintf(stderr, 1046 "snoop: WARNING: skipping from packet %d\n", 1047 count); 1048 snoop_nrecover = 0; 1049 } else { 1050 /* continue trying */ 1051 return; 1052 } 1053 } else if (snoop_nrecover >= SNOOP_MAXRECOVER) { 1054 (void) fprintf(stderr, 1055 "snoop: ERROR: cannot recover from packet %d\n", count); 1056 exit(1); 1057 } 1058 1059 #ifdef DEBUG 1060 (void) fprintf(stderr, "snoop_sigrecover(%d, %p, %p)\n", sig, info, p); 1061 #endif /* DEBUG */ 1062 1063 /* 1064 * Prepare to quit. This allows final processing to occur 1065 * after first terminal interruption. 1066 */ 1067 if (sig == SIGTERM || sig == SIGHUP || sig == SIGINT) { 1068 quitting = 1; 1069 return; 1070 } else if (sig != -1 && sig != SIGALRM) { 1071 /* Inform user that snoop has taken a fault */ 1072 (void) fprintf(stderr, 1073 "WARNING: received signal %d from packet %d\n", 1074 sig, count); 1075 } 1076 1077 /* Reset interpreter variables */ 1078 snoop_recover(); 1079 1080 /* Continue in scan() with the next packet */ 1081 siglongjmp(jmp_env, 1); 1082 /*NOTREACHED*/ 1083 } 1084 1085 /* 1086 * Protected malloc for global error recovery: prepare for interpreter 1087 * failures with corrupted packets or confused interpreters. Dynamically 1088 * allocate `nbytes' bytes, and sandwich it between two PROT_NONE pages to 1089 * catch writes outside of the allocated region. 1090 */ 1091 static char * 1092 protmalloc(size_t nbytes) 1093 { 1094 caddr_t start; 1095 int psz = sysconf(_SC_PAGESIZE); 1096 1097 nbytes = P2ROUNDUP(nbytes, psz); 1098 start = mmap(NULL, nbytes + psz * 2, PROT_READ|PROT_WRITE, 1099 MAP_PRIVATE|MAP_ANON, -1, 0); 1100 if (start == MAP_FAILED) { 1101 perror("Error: protmalloc: mmap"); 1102 return (NULL); 1103 } 1104 assert(IS_P2ALIGNED(start, psz)); 1105 if (mprotect(start, 1, PROT_NONE) == -1) 1106 perror("Warning: mprotect"); 1107 1108 start += psz; 1109 if (mprotect(start + nbytes, 1, PROT_NONE) == -1) 1110 perror("Warning: mprotect"); 1111 1112 return (start); 1113 } 1114 1115 /* 1116 * resetperm - reduce security vulnerabilities by resetting 1117 * owner/group/permissions. Always attempt setuid() - if we have 1118 * permission to drop our privilege level, do so. 1119 */ 1120 void 1121 resetperm(void) 1122 { 1123 if (geteuid() == 0) { 1124 (void) setgid(GID_NOBODY); 1125 (void) setuid(UID_NOBODY); 1126 } 1127 } 1128