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 2007 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" /* SunOS */ 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 char *device = NULL; 58 59 /* Global error recovery variables */ 60 sigjmp_buf jmp_env, ojmp_env; /* error recovery jmp buf */ 61 int snoop_nrecover; /* number of recoveries on curr pkt */ 62 int quitting; /* user termination flag */ 63 64 static struct snoop_handler *snoop_hp; /* global alarm handler head */ 65 static struct snoop_handler *snoop_tp; /* global alarm handler tail */ 66 static time_t snoop_nalarm; /* time of next alarm */ 67 68 /* protected interpreter output areas */ 69 #define MAXSUM 8 70 #define REDZONE 64 71 static char *sumline[MAXSUM]; 72 static char *detail_line; 73 static char *line; 74 static char *encap; 75 76 static int audio; 77 int maxcount; /* maximum no of packets to capture */ 78 int count; /* count of packets captured */ 79 static int sumcount; 80 int x_offset = -1; 81 int x_length = 0x7fffffff; 82 FILE *namefile; 83 int Pflg; 84 boolean_t qflg = B_FALSE; 85 boolean_t rflg = B_FALSE; 86 #ifdef DEBUG 87 boolean_t zflg = B_FALSE; /* debugging packet corrupt flag */ 88 #endif 89 struct Pf_ext_packetfilt pf; 90 91 static int vlanid = 0; 92 93 static void usage(void); 94 void show_count(); 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 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 int nflg = 0; 109 int Nflg = 0; 110 int Cflg = 0; 111 int first = 1; 112 int last = 0x7fffffff; 113 int use_kern_pf; 114 char *p, *p2; 115 char names[MAXPATHLEN + 1]; 116 char self[MAXHOSTNAMELEN + 1]; 117 char *argstr = NULL; 118 void (*proc)(); 119 char *audiodev; 120 int ret; 121 struct sigaction sigact; 122 stack_t sigstk; 123 char *output_area; 124 int nbytes; 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:vVp:f:c:x:?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 'P': 255 Pflg++; 256 break; 257 case 'D': 258 flags |= F_DROPS; 259 break; 260 case 'S': 261 flags |= F_LEN; 262 break; 263 case 'i': 264 icapfile = optarg; 265 break; 266 case 'o': 267 ocapfile = optarg; 268 break; 269 case 'N': 270 Nflg++; 271 break; 272 case 'n': 273 nflg++; 274 (void) strlcpy(names, optarg, MAXPATHLEN); 275 break; 276 case 's': 277 snaplen = atoi(optarg); 278 break; 279 case 'd': 280 device = optarg; 281 break; 282 case 'v': 283 flags &= ~(F_SUM); 284 flags |= F_DTAIL; 285 break; 286 case 'V': 287 flags |= F_ALLSUM; 288 break; 289 case 'p': 290 p = optarg; 291 p2 = strpbrk(p, ",:-"); 292 if (p2 == NULL) { 293 first = last = atoi(p); 294 } else { 295 *p2++ = '\0'; 296 first = atoi(p); 297 last = atoi(p2); 298 } 299 break; 300 case 'f': 301 (void) gethostname(self, MAXHOSTNAMELEN); 302 p = strchr(optarg, ':'); 303 if (p) { 304 *p = '\0'; 305 if (strcmp(optarg, self) == 0 || 306 strcmp(p+1, self) == 0) 307 (void) fprintf(stderr, 308 "Warning: cannot capture packets from %s\n", 309 self); 310 *p = ' '; 311 } else if (strcmp(optarg, self) == 0) 312 (void) fprintf(stderr, 313 "Warning: cannot capture packets from %s\n", 314 self); 315 argstr = optarg; 316 break; 317 case 'x': 318 p = optarg; 319 p2 = strpbrk(p, ",:-"); 320 if (p2 == NULL) { 321 x_offset = atoi(p); 322 x_length = -1; 323 } else { 324 *p2++ = '\0'; 325 x_offset = atoi(p); 326 x_length = atoi(p2); 327 } 328 break; 329 case 'c': 330 maxcount = atoi(optarg); 331 break; 332 case 'C': 333 Cflg++; 334 break; 335 case 'q': 336 qflg = B_TRUE; 337 break; 338 case 'r': 339 rflg = B_TRUE; 340 break; 341 #ifdef DEBUG 342 case 'z': 343 zflg = B_TRUE; 344 break; 345 #endif /* DEBUG */ 346 case '?': 347 default: 348 usage(); 349 } 350 } 351 352 if (argc > optind) 353 argstr = (char *)concat_args(&argv[optind], argc - optind); 354 355 /* 356 * Need to know before we decide on filtering method some things 357 * about the interface. So, go ahead and do part of the initialization 358 * now so we have that data. Note that if no device is specified, 359 * check_device selects one and returns it. In an ideal world, 360 * it might be nice if the "correct" interface for the filter 361 * requested was chosen, but that's too hard. 362 */ 363 if (!icapfile) { 364 use_kern_pf = check_device(&dh, &device); 365 } else { 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 /* attempt to read .names file if it exists before filtering */ 376 if ((!Nflg) && names[0] != '\0') { 377 if (access(names, F_OK) == 0) { 378 load_names(names); 379 } else if (nflg) { 380 (void) fprintf(stderr, "%s not found\n", names); 381 exit(1); 382 } 383 } 384 385 if (argstr) { 386 if (!icapfile && use_kern_pf) { 387 ret = pf_compile(argstr, Cflg); 388 switch (ret) { 389 case 0: 390 filter++; 391 compile(argstr, Cflg); 392 break; 393 case 1: 394 fp = &pf; 395 break; 396 case 2: 397 fp = &pf; 398 filter++; 399 break; 400 } 401 } else { 402 filter++; 403 compile(argstr, Cflg); 404 } 405 406 if (Cflg) 407 exit(0); 408 } 409 410 if (flags & F_SUM) 411 flags |= F_WHO; 412 413 /* 414 * If the -o flag is set then capture packets 415 * directly to a file. Don't attempt to 416 * interpret them on the fly (F_NOW). 417 * Note: capture to file is much less likely 418 * to drop packets since we don't spend cpu 419 * cycles running through the interpreters 420 * and possibly hanging in address-to-name 421 * mappings through the name service. 422 */ 423 if (ocapfile) { 424 cap_open_write(ocapfile); 425 proc = cap_write; 426 } else { 427 flags |= F_NOW; 428 proc = process_pkt; 429 } 430 431 432 /* 433 * If the -i flag is set then get packets from 434 * the log file which has been previously captured 435 * with the -o option. 436 */ 437 if (icapfile) { 438 names[0] = '\0'; 439 (void) strlcpy(names, icapfile, MAXPATHLEN); 440 (void) strlcat(names, ".names", MAXPATHLEN); 441 442 if (Nflg) { 443 namefile = fopen(names, "w"); 444 if (namefile == NULL) { 445 perror(names); 446 exit(1); 447 } 448 flags = 0; 449 (void) fprintf(stderr, 450 "Creating name file %s\n", names); 451 } 452 453 if (flags & F_DTAIL) 454 flags = F_DTAIL; 455 else 456 flags |= F_NUM | F_TIME; 457 458 resetperm(); 459 cap_read(first, last, filter, proc, flags); 460 461 if (Nflg) 462 (void) fclose(namefile); 463 464 } else { 465 const int chunksize = 8 * 8192; 466 struct timeval timeout; 467 468 /* 469 * If listening to packets on audio 470 * then set the buffer timeout down 471 * to 1/10 sec. A higher value 472 * makes the audio "bursty". 473 */ 474 if (audio) { 475 timeout.tv_sec = 0; 476 timeout.tv_usec = 100000; 477 } else { 478 timeout.tv_sec = 1; 479 timeout.tv_usec = 0; 480 } 481 482 initdevice(dh, snaplen, chunksize, &timeout, fp); 483 if (! qflg && ocapfile) 484 show_count(); 485 resetperm(); 486 net_read(dh, chunksize, filter, proc, flags); 487 dlpi_close(dh); 488 489 if (!(flags & F_NOW)) 490 (void) printf("\n"); 491 } 492 493 if (ocapfile) 494 cap_close(); 495 496 return (0); 497 } 498 499 static int tone[] = { 500 0x076113, 0x153333, 0x147317, 0x144311, 0x147315, 0x050353, 0x037103, 0x051106, 501 0x157155, 0x142723, 0x133273, 0x134664, 0x051712, 0x024465, 0x026447, 0x072473, 502 0x136715, 0x126257, 0x135256, 0x047344, 0x034476, 0x027464, 0x036062, 0x133334, 503 0x127256, 0x130660, 0x136262, 0x040724, 0x016446, 0x025437, 0x137171, 0x127672, 504 0x124655, 0x134654, 0x032741, 0x021447, 0x037450, 0x125675, 0x127650, 0x077277, 505 0x046514, 0x036077, 0x035471, 0x147131, 0x136272, 0x162720, 0x166151, 0x037527, 506 }; 507 508 /* 509 * Make a sound on /dev/audio according to the length of the packet. The 510 * tone data was ripped from /usr/share/audio/samples/au/bark.au. The 511 * amount of waveform used is a function of packet length e.g. a series 512 * of small packets is heard as clicks, whereas a series of NFS packets in 513 * an 8k read sounds like a "WHAAAARP". 514 */ 515 void 516 click(len) 517 int len; 518 { 519 len /= 8; 520 len = len ? len : 4; 521 522 if (audio) { 523 (void) write(audio, tone, len); 524 } 525 } 526 527 /* Display a count of packets */ 528 void 529 show_count() 530 { 531 static int prev = -1; 532 533 if (count == prev) 534 return; 535 536 prev = count; 537 (void) fprintf(stderr, "\r%d ", count); 538 } 539 540 #define ENCAP_LEN 16 /* Hold "(NN encap)" */ 541 542 /* 543 * Display data that's external to the packet. 544 * This constitutes the first half of the summary 545 * line display. 546 */ 547 void 548 show_pktinfo(flags, num, src, dst, ptvp, tvp, drops, len) 549 int flags, num, drops, len; 550 char *src, *dst; 551 struct timeval *ptvp, *tvp; 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, "%d:%02d:%d.%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 * 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 * 658 get_detail_line(off, len) 659 int off, len; 660 { 661 if (detail_line[0]) { 662 (void) printf("%s\n", detail_line); 663 detail_line[0] = '\0'; 664 } 665 return (detail_line); 666 } 667 668 /* 669 * This function exists to make sure that VLAN information is 670 * prepended to summary lines displayed. The problem this function 671 * solves is how to display VLAN information while in summary mode. 672 * Each interpretor uses the get_sum_line and get_detail_line functions 673 * to get a character buffer to display information to the user. 674 * get_sum_line is the important one here. Each call to get_sum_line 675 * gets a buffer which stores one line of information. In summary mode, 676 * the last line generated is the line printed. Instead of changing each 677 * interpreter to add VLAN information to the summary line, the ethernet 678 * interpreter changes to call this function and set an ID. If the ID is not 679 * zero and snoop is in default summary mode, snoop displays the 680 * VLAN information at the beginning of the output line. Otherwise, 681 * no VLAN information is displayed. 682 */ 683 void 684 set_vlan_id(int id) 685 { 686 vlanid = id; 687 } 688 689 /* 690 * Print an error. 691 * Works like printf (fmt string and variable args) 692 * except that it will substitute an error message 693 * for a "%m" string (like syslog) and it calls 694 * long_jump - it doesn't return to where it was 695 * called from - it goes to the last setjmp(). 696 */ 697 /* VARARGS1 */ 698 void 699 pr_err(const char *fmt, ...) 700 { 701 va_list ap; 702 char buf[1024], *p2; 703 const char *p1; 704 705 (void) strcpy(buf, "snoop: "); 706 p2 = buf + strlen(buf); 707 708 /* 709 * Note that we terminate the buffer with '\n' and '\0'. 710 */ 711 for (p1 = fmt; *p1 != '\0' && p2 < buf + sizeof (buf) - 2; p1++) { 712 if (*p1 == '%' && *(p1+1) == 'm') { 713 const char *errstr; 714 715 if ((errstr = strerror(errno)) != NULL) { 716 *p2 = '\0'; 717 (void) strlcat(buf, errstr, sizeof (buf)); 718 p2 += strlen(p2); 719 } 720 p1++; 721 } else { 722 *p2++ = *p1; 723 } 724 } 725 if (p2 > buf && *(p2-1) != '\n') 726 *p2++ = '\n'; 727 *p2 = '\0'; 728 729 va_start(ap, fmt); 730 /* LINTED: E_SEC_PRINTF_VAR_FMT */ 731 (void) vfprintf(stderr, buf, ap); 732 va_end(ap); 733 snoop_sigrecover(-1, NULL, NULL); /* global error recovery */ 734 } 735 736 /* 737 * Store a copy of linkname associated with the DLPI handle. 738 * Save errno before closing the dlpi handle so that the 739 * correct error value is used if 'err' is a system error. 740 */ 741 void 742 pr_errdlpi(dlpi_handle_t dh, const char *cmd, int err) 743 { 744 int save_errno = errno; 745 char linkname[DLPI_LINKNAME_MAX]; 746 747 (void) strlcpy(linkname, dlpi_linkname(dh), sizeof (linkname)); 748 749 dlpi_close(dh); 750 errno = save_errno; 751 752 pr_err("%s on \"%s\": %s", cmd, linkname, dlpi_strerror(err)); 753 } 754 755 /* 756 * Ye olde usage proc 757 * PLEASE keep this up to date! 758 * Naive users *love* this stuff. 759 */ 760 static void 761 usage(void) 762 { 763 (void) fprintf(stderr, "\nUsage: snoop\n"); 764 (void) fprintf(stderr, 765 "\t[ -a ] # Listen to packets on audio\n"); 766 (void) fprintf(stderr, 767 "\t[ -d device ] # Listen on interface named device\n"); 768 (void) fprintf(stderr, 769 "\t[ -s snaplen ] # Truncate packets\n"); 770 (void) fprintf(stderr, 771 "\t[ -c count ] # Quit after count packets\n"); 772 (void) fprintf(stderr, 773 "\t[ -P ] # Turn OFF promiscuous mode\n"); 774 (void) fprintf(stderr, 775 "\t[ -D ] # Report dropped packets\n"); 776 (void) fprintf(stderr, 777 "\t[ -S ] # Report packet size\n"); 778 (void) fprintf(stderr, 779 "\t[ -i file ] # Read previously captured packets\n"); 780 (void) fprintf(stderr, 781 "\t[ -o file ] # Capture packets in file\n"); 782 (void) fprintf(stderr, 783 "\t[ -n file ] # Load addr-to-name table from file\n"); 784 (void) fprintf(stderr, 785 "\t[ -N ] # Create addr-to-name table\n"); 786 (void) fprintf(stderr, 787 "\t[ -t r|a|d ] # Time: Relative, Absolute or Delta\n"); 788 (void) fprintf(stderr, 789 "\t[ -v ] # Verbose packet display\n"); 790 (void) fprintf(stderr, 791 "\t[ -V ] # Show all summary lines\n"); 792 (void) fprintf(stderr, 793 "\t[ -p first[,last] ] # Select packet(s) to display\n"); 794 (void) fprintf(stderr, 795 "\t[ -x offset[,length] ] # Hex dump from offset for length\n"); 796 (void) fprintf(stderr, 797 "\t[ -C ] # Print packet filter code\n"); 798 (void) fprintf(stderr, 799 "\t[ -q ] # Suppress printing packet count\n"); 800 (void) fprintf(stderr, 801 "\t[ -r ] # Do not resolve address to name\n"); 802 (void) fprintf(stderr, 803 "\n\t[ filter expression ]\n"); 804 (void) fprintf(stderr, "\nExample:\n"); 805 (void) fprintf(stderr, "\tsnoop -o saved host fred\n\n"); 806 (void) fprintf(stderr, "\tsnoop -i saved -tr -v -p19\n"); 807 exit(1); 808 } 809 810 /* 811 * sdefault: default global alarm handler. Causes the current packet 812 * to be skipped. 813 */ 814 static void 815 sdefault(void) 816 { 817 snoop_nrecover = SNOOP_MAXRECOVER; 818 } 819 820 /* 821 * snoop_alarm: register or unregister an alarm handler to be called after 822 * s_sec seconds. Because snoop wasn't written to tolerate random signal 823 * delivery, periodic SIGALRM delivery (or SA_RESTART) cannot be used. 824 * 825 * s_sec argument of 0 seconds unregisters the handler. 826 * s_handler argument of NULL registers default handler sdefault(), or 827 * unregisters all signal handlers (for error recovery). 828 * 829 * Variables must be volatile to force the compiler to not optimize 830 * out the signal blocking. 831 */ 832 /*ARGSUSED*/ 833 int 834 snoop_alarm(int s_sec, void (*s_handler)()) 835 { 836 volatile time_t now; 837 volatile time_t nalarm = 0; 838 volatile struct snoop_handler *sh = NULL; 839 volatile struct snoop_handler *hp, *tp, *next; 840 volatile sigset_t s_mask; 841 volatile int ret = -1; 842 843 (void) sigemptyset((sigset_t *)&s_mask); 844 (void) sigaddset((sigset_t *)&s_mask, SIGALRM); 845 if (s_sec < 0) 846 return (-1); 847 848 /* register an alarm handler */ 849 now = time(NULL); 850 if (s_sec) { 851 sh = malloc(sizeof (struct snoop_handler)); 852 sh->s_time = now + s_sec; 853 if (s_handler == NULL) 854 s_handler = sdefault; 855 sh->s_handler = s_handler; 856 sh->s_next = NULL; 857 (void) sigprocmask(SIG_BLOCK, (sigset_t *)&s_mask, NULL); 858 if (snoop_hp == NULL) { 859 snoop_hp = snoop_tp = (struct snoop_handler *)sh; 860 861 snoop_nalarm = sh->s_time; 862 (void) alarm(sh->s_time - now); 863 } else { 864 snoop_tp->s_next = (struct snoop_handler *)sh; 865 snoop_tp = (struct snoop_handler *)sh; 866 867 if (sh->s_time < snoop_nalarm) { 868 snoop_nalarm = sh->s_time; 869 (void) alarm(sh->s_time - now); 870 } 871 } 872 (void) sigprocmask(SIG_UNBLOCK, (sigset_t *)&s_mask, NULL); 873 874 return (0); 875 } 876 877 /* unregister an alarm handler */ 878 (void) sigprocmask(SIG_BLOCK, (sigset_t *)&s_mask, NULL); 879 tp = (struct snoop_handler *)&snoop_hp; 880 for (hp = snoop_hp; hp; hp = next) { 881 next = hp->s_next; 882 if (s_handler == NULL || hp->s_handler == s_handler) { 883 ret = 0; 884 tp->s_next = hp->s_next; 885 if (snoop_tp == hp) { 886 if (tp == (struct snoop_handler *)&snoop_hp) 887 snoop_tp = NULL; 888 else 889 snoop_tp = (struct snoop_handler *)tp; 890 } 891 free((void *)hp); 892 } else { 893 if (nalarm == 0 || nalarm > hp->s_time) 894 nalarm = now < hp->s_time ? hp->s_time : 895 now + 1; 896 tp = hp; 897 } 898 } 899 /* 900 * Stop or adjust timer 901 */ 902 if (snoop_hp == NULL) { 903 snoop_nalarm = 0; 904 (void) alarm(0); 905 } else if (nalarm > 0 && nalarm < snoop_nalarm) { 906 snoop_nalarm = nalarm; 907 (void) alarm(nalarm - now); 908 } 909 910 (void) sigprocmask(SIG_UNBLOCK, (sigset_t *)&s_mask, NULL); 911 return (ret); 912 } 913 914 /* 915 * snoop_recover: reset snoop's output area, and any internal variables, 916 * to allow continuation. 917 * XXX: make this an interface such that each interpreter can 918 * register a reset routine. 919 */ 920 void 921 snoop_recover(void) 922 { 923 int i; 924 925 /* Error recovery: reset output_area and associated variables */ 926 for (i = 0; i < MAXSUM; i++) 927 sumline[i][0] = '\0'; 928 detail_line[0] = '\0'; 929 line[0] = '\0'; 930 encap[0] = '\0'; 931 sumcount = 0; 932 933 /* stacking/unstacking cannot be relied upon */ 934 encap_levels = 0; 935 total_encap_levels = 1; 936 937 /* remove any pending timeouts */ 938 (void) snoop_alarm(0, NULL); 939 } 940 941 /* 942 * snoop_sigrecover: global sigaction routine to manage recovery 943 * from catastrophic interpreter failures while interpreting 944 * corrupt trace files/packets. SIGALRM timeouts, program errors, 945 * and user termination are all handled. In the case of a corrupt 946 * packet or confused interpreter, the packet will be skipped, and 947 * execution will continue in scan(). 948 * 949 * Global alarm handling (see snoop_alarm()) is managed here. 950 * 951 * Variables must be volatile to force the compiler to not optimize 952 * out the signal blocking. 953 */ 954 /*ARGSUSED*/ 955 static void 956 snoop_sigrecover(int sig, siginfo_t *info, void *p) 957 { 958 volatile time_t now; 959 volatile time_t nalarm = 0; 960 volatile struct snoop_handler *hp; 961 962 /* 963 * Invoke any registered alarms. This involves first calculating 964 * the time for the next alarm, setting it up, then progressing 965 * through handler invocations. Note that since handlers may 966 * use siglongjmp(), in the worst case handlers may be serviced 967 * at a later time. 968 */ 969 if (sig == SIGALRM) { 970 now = time(NULL); 971 /* Calculate next alarm time */ 972 for (hp = snoop_hp; hp; hp = hp->s_next) { 973 if (hp->s_time) { 974 if ((hp->s_time - now) > 0) { 975 if (nalarm == 0 || nalarm > hp->s_time) 976 nalarm = now < hp->s_time ? 977 hp->s_time : now + 1; 978 } 979 } 980 } 981 /* Setup next alarm */ 982 if (nalarm) { 983 snoop_nalarm = nalarm; 984 (void) alarm(nalarm - now); 985 } else { 986 snoop_nalarm = 0; 987 } 988 989 /* Invoke alarm handlers (may not return) */ 990 for (hp = snoop_hp; hp; hp = hp->s_next) { 991 if (hp->s_time) { 992 if ((now - hp->s_time) >= 0) { 993 hp->s_time = 0; /* only invoke once */ 994 if (hp->s_handler) 995 hp->s_handler(); 996 } 997 } 998 } 999 } else { 1000 snoop_nrecover++; 1001 } 1002 1003 /* 1004 * Exit if a signal has occurred after snoop has begun the process 1005 * of quitting. 1006 */ 1007 if (quitting) 1008 exit(1); 1009 1010 /* 1011 * If an alarm handler has timed out, and snoop_nrecover has 1012 * reached SNOOP_MAXRECOVER, skip to the next packet. 1013 * 1014 * If any other signal has occurred, and snoop_nrecover has 1015 * reached SNOOP_MAXRECOVER, give up. 1016 */ 1017 if (sig == SIGALRM) { 1018 if (ioctl(STDOUT_FILENO, I_CANPUT, 0) == 0) { 1019 /* 1020 * We've stalled on output, which is not a critical 1021 * failure. Reset the recovery counter so we do not 1022 * consider this a persistent failure, and return so 1023 * we do not skip this packet. 1024 */ 1025 snoop_nrecover = 0; 1026 return; 1027 } 1028 if (snoop_nrecover >= SNOOP_MAXRECOVER) { 1029 (void) fprintf(stderr, 1030 "snoop: WARNING: skipping from packet %d\n", 1031 count); 1032 snoop_nrecover = 0; 1033 } else { 1034 /* continue trying */ 1035 return; 1036 } 1037 } else if (snoop_nrecover >= SNOOP_MAXRECOVER) { 1038 (void) fprintf(stderr, 1039 "snoop: ERROR: cannot recover from packet %d\n", count); 1040 exit(1); 1041 } 1042 1043 #ifdef DEBUG 1044 (void) fprintf(stderr, "snoop_sigrecover(%d, %p, %p)\n", sig, info, p); 1045 #endif /* DEBUG */ 1046 1047 /* 1048 * Prepare to quit. This allows final processing to occur 1049 * after first terminal interruption. 1050 */ 1051 if (sig == SIGTERM || sig == SIGHUP || sig == SIGINT) { 1052 quitting = 1; 1053 return; 1054 } else if (sig != -1 && sig != SIGALRM) { 1055 /* Inform user that snoop has taken a fault */ 1056 (void) fprintf(stderr, 1057 "WARNING: received signal %d from packet %d\n", 1058 sig, count); 1059 } 1060 1061 /* Reset interpreter variables */ 1062 snoop_recover(); 1063 1064 /* Continue in scan() with the next packet */ 1065 siglongjmp(jmp_env, 1); 1066 /*NOTREACHED*/ 1067 } 1068 1069 /* 1070 * Protected malloc for global error recovery: prepare for interpreter 1071 * failures with corrupted packets or confused interpreters. Dynamically 1072 * allocate `nbytes' bytes, and sandwich it between two PROT_NONE pages to 1073 * catch writes outside of the allocated region. 1074 */ 1075 static char * 1076 protmalloc(size_t nbytes) 1077 { 1078 caddr_t start; 1079 int psz = sysconf(_SC_PAGESIZE); 1080 1081 nbytes = P2ROUNDUP(nbytes, psz); 1082 start = mmap(NULL, nbytes + psz * 2, PROT_READ|PROT_WRITE, 1083 MAP_PRIVATE|MAP_ANON, -1, 0); 1084 if (start == MAP_FAILED) { 1085 perror("Error: protmalloc: mmap"); 1086 return (NULL); 1087 } 1088 assert(IS_P2ALIGNED(start, psz)); 1089 if (mprotect(start, 1, PROT_NONE) == -1) 1090 perror("Warning: mprotect"); 1091 1092 start += psz; 1093 if (mprotect(start + nbytes, 1, PROT_NONE) == -1) 1094 perror("Warning: mprotect"); 1095 1096 return (start); 1097 } 1098 1099 /* 1100 * resetperm - reduce security vulnerabilities by resetting 1101 * owner/group/permissions. Always attempt setuid() - if we have 1102 * permission to drop our privilege level, do so. 1103 */ 1104 void 1105 resetperm(void) 1106 { 1107 if (geteuid() == 0) { 1108 (void) setgid(GID_NOBODY); 1109 (void) setuid(UID_NOBODY); 1110 } 1111 } 1112