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