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 54static int snaplen; 55 56/* Global error recovery variables */ 57sigjmp_buf jmp_env, ojmp_env; /* error recovery jmp buf */ 58int snoop_nrecover; /* number of recoveries on curr pkt */ 59int quitting; /* user termination flag */ 60 61static struct snoop_handler *snoop_hp; /* global alarm handler head */ 62static struct snoop_handler *snoop_tp; /* global alarm handler tail */ 63static time_t snoop_nalarm; /* time of next alarm */ 64 65/* protected interpreter output areas */ 66#define MAXSUM 8 67#define REDZONE 64 68static char *sumline[MAXSUM]; 69static char *detail_line; 70static char *line; 71static char *encap; 72 73static int audio; 74int maxcount; /* maximum no of packets to capture */ 75int count; /* count of packets captured */ 76static int sumcount; 77int x_offset = -1; 78int x_length = 0x7fffffff; 79FILE *namefile; 80boolean_t Pflg; 81boolean_t Iflg; 82boolean_t qflg; 83boolean_t rflg; 84#ifdef DEBUG 85boolean_t zflg; 86#endif 87struct Pf_ext_packetfilt pf; 88 89static int vlanid = 0; 90 91static void usage(void); 92static void snoop_sigrecover(int sig, siginfo_t *info, void *p); 93static char *protmalloc(size_t); 94static void resetperm(void); 95 96int 97main(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 513static int tone[] = { 5140x076113, 0x153333, 0x147317, 0x144311, 0x147315, 0x050353, 0x037103, 0x051106, 5150x157155, 0x142723, 0x133273, 0x134664, 0x051712, 0x024465, 0x026447, 0x072473, 5160x136715, 0x126257, 0x135256, 0x047344, 0x034476, 0x027464, 0x036062, 0x133334, 5170x127256, 0x130660, 0x136262, 0x040724, 0x016446, 0x025437, 0x137171, 0x127672, 5180x124655, 0x134654, 0x032741, 0x021447, 0x037450, 0x125675, 0x127650, 0x077277, 5190x046514, 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 */ 529void 530click(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 */ 542void 543show_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 */ 561void 562show_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 */ 654char * 655get_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*/ 671char * 672get_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 */ 697void 698set_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 */ 712void 713pr_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 */ 755void 756pr_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 */ 774static void 775usage(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 */ 830static void 831sdefault(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*/ 849int 850snoop_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 */ 936void 937snoop_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*/ 971static void 972snoop_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 */ 1091static char * 1092protmalloc(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 */ 1120void 1121resetperm(void) 1122{ 1123 if (geteuid() == 0) { 1124 (void) setgid(GID_NOBODY); 1125 (void) setuid(UID_NOBODY); 1126 } 1127} 1128