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