1 /* 2 * snoopy - network sniffer 3 */ 4 #include <u.h> 5 #include <libc.h> 6 #include <ip.h> 7 #include <bio.h> 8 #include <fcall.h> 9 #include <libsec.h> 10 #include <ndb.h> 11 #include "dat.h" 12 #include "protos.h" 13 #include "y.tab.h" 14 15 int Cflag; 16 int pflag; 17 int Nflag; 18 int Mflag; 19 int sflag; 20 int tiflag; 21 int toflag; 22 23 char *prom = "promiscuous"; 24 25 enum 26 { 27 Pktlen= 64*1024, 28 Blen= 16*1024, 29 }; 30 31 Filter *filter; 32 Proto *root; 33 Biobuf out; 34 vlong starttime, pkttime; 35 int pcap; 36 37 int filterpkt(Filter *f, uchar *ps, uchar *pe, Proto *pr, int); 38 void printpkt(char *p, char *e, uchar *ps, uchar *pe); 39 void mkprotograph(void); 40 Proto* findproto(char *name); 41 Filter* compile(Filter *f); 42 void printfilter(Filter *f, char *tag); 43 void printhelp(char*); 44 void tracepkt(uchar*, int); 45 void pcaphdr(void); 46 47 void 48 printusage(void) 49 { 50 fprint(2, "usage: %s [-CDdpst] [-N n] [-f filter] [-h first-header] path\n", argv0); 51 fprint(2, " for protocol help: %s -? [proto]\n", argv0); 52 } 53 54 void 55 usage(void) 56 { 57 printusage(); 58 exits("usage"); 59 } 60 61 void 62 main(int argc, char **argv) 63 { 64 uchar *pkt; 65 char *buf, *file, *p, *e; 66 int fd, cfd; 67 int n; 68 69 Binit(&out, 1, OWRITE); 70 71 fmtinstall('E', eipfmt); 72 fmtinstall('V', eipfmt); 73 fmtinstall('I', eipfmt); 74 fmtinstall('H', encodefmt); 75 fmtinstall('F', fcallfmt); 76 77 pkt = malloc(Pktlen+16); 78 pkt += 16; 79 buf = malloc(Blen); 80 e = buf+Blen-1; 81 82 pflag = 1; 83 Nflag = 32; 84 sflag = 0; 85 86 mkprotograph(); 87 88 ARGBEGIN{ 89 default: 90 usage(); 91 case '?': 92 printusage(); 93 printhelp(ARGF()); 94 exits(0); 95 break; 96 case 'M': 97 p = EARGF(usage()); 98 Mflag = atoi(p); 99 break; 100 case 'N': 101 p = EARGF(usage()); 102 Nflag = atoi(p); 103 break; 104 case 'f': 105 p = EARGF(usage()); 106 yyinit(p); 107 yyparse(); 108 break; 109 case 's': 110 sflag = 1; 111 break; 112 case 'h': 113 p = EARGF(usage()); 114 root = findproto(p); 115 if(root == nil) 116 sysfatal("unknown protocol: %s", p); 117 break; 118 case 'd': 119 toflag = 1; 120 break; 121 case 'D': 122 toflag = 1; 123 pcap = 1; 124 break; 125 case 't': 126 tiflag = 1; 127 break; 128 case 'C': 129 Cflag = 1; 130 break; 131 case 'p': 132 pflag = 0; 133 break; 134 }ARGEND; 135 136 if(pcap) 137 pcaphdr(); 138 139 if(argc == 0){ 140 file = "/net/ether0"; 141 if(root != nil) 142 root = ðer; 143 } else 144 file = argv[0]; 145 146 if((!tiflag) && strstr(file, "ether")){ 147 if(root == nil) 148 root = ðer; 149 snprint(buf, Blen, "%s!-1", file); 150 fd = dial(buf, 0, 0, &cfd); 151 if(fd < 0) 152 sysfatal("dialing %s: %r", buf); 153 if(pflag && fprint(cfd, prom, strlen(prom)) < 0) 154 sysfatal("setting %s", prom); 155 } else if((!tiflag) && strstr(file, "ipifc")){ 156 if(root == nil) 157 root = &ip; 158 snprint(buf, Blen, "%s/snoop", file); 159 fd = open(buf, OREAD); 160 if(fd < 0) 161 sysfatal("opening %s: %r", buf); 162 } else { 163 if(root == nil) 164 root = ðer; 165 fd = open(file, OREAD); 166 if(fd < 0) 167 sysfatal("opening %s: %r", file); 168 } 169 filter = compile(filter); 170 171 if(tiflag){ 172 /* read a trace file */ 173 for(;;){ 174 n = read(fd, pkt, 10); 175 if(n != 10) 176 break; 177 pkttime = NetL(pkt+2); 178 pkttime = (pkttime<<32) | NetL(pkt+6); 179 if(starttime == 0LL) 180 starttime = pkttime; 181 n = NetS(pkt); 182 if(readn(fd, pkt, n) != n) 183 break; 184 if(filterpkt(filter, pkt, pkt+n, root, 1)) 185 if(toflag) 186 tracepkt(pkt, n); 187 else 188 printpkt(buf, e, pkt, pkt+n); 189 } 190 } else { 191 /* read a real time stream */ 192 starttime = nsec(); 193 for(;;){ 194 n = root->framer(fd, pkt, Pktlen); 195 if(n <= 0) 196 break; 197 pkttime = nsec(); 198 if(filterpkt(filter, pkt, pkt+n, root, 1)) 199 if(toflag) 200 tracepkt(pkt, n); 201 else 202 printpkt(buf, e, pkt, pkt+n); 203 } 204 } 205 } 206 207 /* create a new filter node */ 208 Filter* 209 newfilter(void) 210 { 211 Filter *f; 212 213 f = mallocz(sizeof(*f), 1); 214 if(f == nil) 215 sysfatal("newfilter: %r"); 216 return f; 217 } 218 219 /* 220 * apply filter to packet 221 */ 222 int 223 _filterpkt(Filter *f, Msg *m) 224 { 225 Msg ma; 226 227 if(f == nil) 228 return 1; 229 230 switch(f->op){ 231 case '!': 232 return !_filterpkt(f->l, m); 233 case LAND: 234 ma = *m; 235 return _filterpkt(f->l, &ma) && _filterpkt(f->r, m); 236 case LOR: 237 ma = *m; 238 return _filterpkt(f->l, &ma) || _filterpkt(f->r, m); 239 case WORD: 240 if(m->needroot){ 241 if(m->pr != f->pr) 242 return 0; 243 m->needroot = 0; 244 }else{ 245 if(m->pr && (m->pr->filter==nil || !(m->pr->filter)(f, m))) 246 return 0; 247 } 248 if(f->l == nil) 249 return 1; 250 m->pr = f->pr; 251 return _filterpkt(f->l, m); 252 } 253 sysfatal("internal error: filterpkt op: %d", f->op); 254 return 0; 255 } 256 int 257 filterpkt(Filter *f, uchar *ps, uchar *pe, Proto *pr, int needroot) 258 { 259 Msg m; 260 261 if(f == nil) 262 return 1; 263 264 m.needroot = needroot; 265 m.ps = ps; 266 m.pe = pe; 267 m.pr = pr; 268 return _filterpkt(f, &m); 269 } 270 271 /* 272 * from the Unix world 273 */ 274 #define PCAP_VERSION_MAJOR 2 275 #define PCAP_VERSION_MINOR 4 276 #define TCPDUMP_MAGIC 0xa1b2c3d4 277 278 struct pcap_file_header { 279 ulong magic; 280 ushort version_major; 281 ushort version_minor; 282 long thiszone; /* gmt to local correction */ 283 ulong sigfigs; /* accuracy of timestamps */ 284 ulong snaplen; /* max length saved portion of each pkt */ 285 ulong linktype; /* data link type (DLT_*) */ 286 }; 287 288 struct pcap_pkthdr { 289 uvlong ts; /* time stamp */ 290 ulong caplen; /* length of portion present */ 291 ulong len; /* length this packet (off wire) */ 292 }; 293 294 /* 295 * pcap trace header 296 */ 297 void 298 pcaphdr(void) 299 { 300 struct pcap_file_header hdr; 301 302 hdr.magic = TCPDUMP_MAGIC; 303 hdr.version_major = PCAP_VERSION_MAJOR; 304 hdr.version_minor = PCAP_VERSION_MINOR; 305 306 hdr.thiszone = 0; 307 hdr.snaplen = 1500; 308 hdr.sigfigs = 0; 309 hdr.linktype = 1; 310 311 write(1, &hdr, sizeof(hdr)); 312 } 313 314 /* 315 * write out a packet trace 316 */ 317 void 318 tracepkt(uchar *ps, int len) 319 { 320 struct pcap_pkthdr *goo; 321 322 if(Mflag && len > Mflag) 323 len = Mflag; 324 if(pcap){ 325 goo = (struct pcap_pkthdr*)(ps-16); 326 goo->ts = pkttime; 327 goo->caplen = len; 328 goo->len = len; 329 write(1, goo, len+16); 330 } else { 331 hnputs(ps-10, len); 332 hnputl(ps-8, pkttime>>32); 333 hnputl(ps-4, pkttime); 334 write(1, ps-10, len+10); 335 } 336 } 337 338 /* 339 * format and print a packet 340 */ 341 void 342 printpkt(char *p, char *e, uchar *ps, uchar *pe) 343 { 344 Msg m; 345 ulong dt; 346 347 dt = (pkttime-starttime)/1000000LL; 348 m.p = seprint(p, e, "%6.6uld ms ", dt); 349 m.ps = ps; 350 m.pe = pe; 351 m.e = e; 352 m.pr = root; 353 while(m.p < m.e){ 354 if(!sflag) 355 m.p = seprint(m.p, m.e, "\n\t"); 356 m.p = seprint(m.p, m.e, "%s(", m.pr->name); 357 if((*m.pr->seprint)(&m) < 0){ 358 m.p = seprint(m.p, m.e, "TOO SHORT"); 359 m.ps = m.pe; 360 } 361 m.p = seprint(m.p, m.e, ")"); 362 if(m.pr == nil || m.ps >= m.pe) 363 break; 364 } 365 *m.p++ = '\n'; 366 367 if(write(1, p, m.p - p) < 0) 368 sysfatal("stdout: %r"); 369 } 370 371 Proto **xprotos; 372 int nprotos; 373 374 /* look up a protocol by its name */ 375 Proto* 376 findproto(char *name) 377 { 378 int i; 379 380 for(i = 0; i < nprotos; i++) 381 if(strcmp(xprotos[i]->name, name) == 0) 382 return xprotos[i]; 383 return nil; 384 } 385 386 /* 387 * add an undefined protocol to protos[] 388 */ 389 Proto* 390 addproto(char *name) 391 { 392 Proto *pr; 393 394 xprotos = realloc(xprotos, (nprotos+1)*sizeof(Proto*)); 395 pr = malloc(sizeof *pr); 396 *pr = dump; 397 pr->name = name; 398 xprotos[nprotos++] = pr; 399 return pr; 400 } 401 402 /* 403 * build a graph of protocols, this could easily be circular. This 404 * links together all the multiplexing in the protocol modules. 405 */ 406 void 407 mkprotograph(void) 408 { 409 Proto **l; 410 Proto *pr; 411 Mux *m; 412 413 /* copy protos into a reallocable area */ 414 for(nprotos = 0; protos[nprotos] != nil; nprotos++) 415 ; 416 xprotos = malloc(nprotos*sizeof(Proto*)); 417 memmove(xprotos, protos, nprotos*sizeof(Proto*)); 418 419 for(l = protos; *l != nil; l++){ 420 pr = *l; 421 for(m = pr->mux; m != nil && m->name != nil; m++){ 422 m->pr = findproto(m->name); 423 if(m->pr == nil) 424 m->pr = addproto(m->name); 425 } 426 } 427 } 428 429 /* 430 * add in a protocol node 431 */ 432 static Filter* 433 addnode(Filter *f, Proto *pr) 434 { 435 Filter *nf; 436 nf = newfilter(); 437 nf->pr = pr; 438 nf->s = pr->name; 439 nf->l = f; 440 nf->op = WORD; 441 return nf; 442 } 443 444 /* 445 * recurse through the protocol graph adding missing nodes 446 * to the filter if we reach the filter's protocol 447 */ 448 static Filter* 449 _fillin(Filter *f, Proto *last, int depth) 450 { 451 Mux *m; 452 Filter *nf; 453 454 if(depth-- <= 0) 455 return nil; 456 457 for(m = last->mux; m != nil && m->name != nil; m++){ 458 if(m->pr == nil) 459 continue; 460 if(f->pr == m->pr) 461 return f; 462 nf = _fillin(f, m->pr, depth); 463 if(nf != nil) 464 return addnode(nf, m->pr); 465 } 466 return nil; 467 } 468 469 static Filter* 470 fillin(Filter *f, Proto *last) 471 { 472 int i; 473 Filter *nf; 474 475 /* hack to make sure top level node is the root */ 476 if(last == nil){ 477 if(f->pr == root) 478 return f; 479 f = fillin(f, root); 480 if(f == nil) 481 return nil; 482 return addnode(f, root); 483 } 484 485 /* breadth first search though the protocol graph */ 486 nf = f; 487 for(i = 1; i < 20; i++){ 488 nf = _fillin(f, last, i); 489 if(nf != nil) 490 break; 491 } 492 return nf; 493 } 494 495 /* 496 * massage tree so that all paths from the root to a leaf 497 * contain a filter node for each header. 498 * 499 * also, set f->pr where possible 500 */ 501 Filter* 502 complete(Filter *f, Proto *last) 503 { 504 Proto *pr; 505 506 if(f == nil) 507 return f; 508 509 /* do a depth first traversal of the filter tree */ 510 switch(f->op){ 511 case '!': 512 f->l = complete(f->l, last); 513 break; 514 case LAND: 515 case LOR: 516 f->l = complete(f->l, last); 517 f->r = complete(f->r, last); 518 break; 519 case '=': 520 break; 521 case WORD: 522 pr = findproto(f->s); 523 f->pr = pr; 524 if(pr == nil){ 525 if(f->l != nil){ 526 fprint(2, "%s unknown proto, ignoring params\n", 527 f->s); 528 f->l = nil; 529 } 530 } else { 531 f->l = complete(f->l, pr); 532 f = fillin(f, last); 533 if(f == nil) 534 sysfatal("internal error: can't get to %s", pr->name); 535 } 536 break; 537 } 538 return f; 539 } 540 541 /* 542 * merge common nodes under | and & moving the merged node 543 * above the | or &. 544 * 545 * do some constant foldong, e.g. `true & x' becomes x and 546 * 'true | x' becomes true. 547 */ 548 static int changed; 549 550 static Filter* 551 _optimize(Filter *f) 552 { 553 Filter *l; 554 555 if(f == nil) 556 return f; 557 558 switch(f->op){ 559 case '!': 560 /* is child also a not */ 561 if(f->l->op == '!'){ 562 changed = 1; 563 return f->l->l; 564 } 565 break; 566 case LOR: 567 /* are two children the same protocol? */ 568 if(f->l->op != f->r->op || f->r->op != WORD 569 || f->l->pr != f->r->pr || f->l->pr == nil) 570 break; /* no optimization */ 571 572 changed = 1; 573 574 /* constant folding */ 575 /* if either child is childless, just return that */ 576 if(f->l->l == nil) 577 return f->l; 578 else if(f->r->l == nil) 579 return f->r; 580 581 /* move the common node up, thow away one node */ 582 l = f->l; 583 f->l = l->l; 584 f->r = f->r->l; 585 l->l = f; 586 return l; 587 case LAND: 588 /* are two children the same protocol? */ 589 if(f->l->op != f->r->op || f->r->op != WORD 590 || f->l->pr != f->r->pr || f->l->pr == nil) 591 break; /* no optimization */ 592 593 changed = 1; 594 595 /* constant folding */ 596 /* if either child is childless, ignore it */ 597 if(f->l->l == nil) 598 return f->r; 599 else if(f->r->l == nil) 600 return f->l; 601 602 /* move the common node up, thow away one node */ 603 l = f->l; 604 f->l = _optimize(l->l); 605 f->r = _optimize(f->r->l); 606 l->l = f; 607 return l; 608 } 609 f->l = _optimize(f->l); 610 f->r = _optimize(f->r); 611 return f; 612 } 613 614 Filter* 615 optimize(Filter *f) 616 { 617 do{ 618 changed = 0; 619 f = _optimize(f); 620 }while(changed); 621 622 return f; 623 } 624 625 /* 626 * find any top level nodes that aren't the root 627 */ 628 int 629 findbogus(Filter *f) 630 { 631 int rv; 632 633 if(f->op != WORD){ 634 rv = findbogus(f->l); 635 if(f->r) 636 rv |= findbogus(f->r); 637 return rv; 638 } else if(f->pr != root){ 639 fprint(2, "bad top-level protocol: %s\n", f->s); 640 return 1; 641 } 642 return 0; 643 } 644 645 /* 646 * compile the filter 647 */ 648 static void 649 _compile(Filter *f, Proto *last) 650 { 651 if(f == nil) 652 return; 653 654 switch(f->op){ 655 case '!': 656 _compile(f->l, last); 657 break; 658 case LOR: 659 case LAND: 660 _compile(f->l, last); 661 _compile(f->r, last); 662 break; 663 case WORD: 664 if(last != nil){ 665 if(last->compile == nil) 666 sysfatal("unknown %s subprotocol: %s", f->pr->name, f->s); 667 (*last->compile)(f); 668 } 669 if(f->l) 670 _compile(f->l, f->pr); 671 break; 672 case '=': 673 if(last == nil) 674 sysfatal("internal error: compilewalk: badly formed tree"); 675 676 if(last->compile == nil) 677 sysfatal("unknown %s field: %s", f->pr->name, f->s); 678 (*last->compile)(f); 679 break; 680 default: 681 sysfatal("internal error: compilewalk op: %d", f->op); 682 } 683 } 684 685 Filter* 686 compile(Filter *f) 687 { 688 if(f == nil) 689 return f; 690 691 /* fill in the missing header filters */ 692 f = complete(f, nil); 693 694 /* constant folding */ 695 f = optimize(f); 696 if(!toflag) 697 printfilter(f, "after optimize"); 698 699 /* protocol specific compilations */ 700 _compile(f, nil); 701 702 /* at this point, the root had better be the root proto */ 703 if(findbogus(f)){ 704 fprint(2, "bogus filter\n"); 705 exits("bad filter"); 706 } 707 708 return f; 709 } 710 711 /* 712 * parse a byte array 713 */ 714 int 715 parseba(uchar *to, char *from) 716 { 717 char nip[4]; 718 char *p; 719 int i; 720 721 p = from; 722 for(i = 0; i < 16; i++){ 723 if(*p == 0) 724 return -1; 725 nip[0] = *p++; 726 if(*p == 0) 727 return -1; 728 nip[1] = *p++; 729 nip[2] = 0; 730 to[i] = strtoul(nip, 0, 16); 731 } 732 return i; 733 } 734 735 /* 736 * compile WORD = WORD, becomes a single node with a subop 737 */ 738 void 739 compile_cmp(char *proto, Filter *f, Field *fld) 740 { 741 uchar x[IPaddrlen]; 742 char *v; 743 744 if(f->op != '=') 745 sysfatal("internal error: compile_cmp %s: not a cmp", proto); 746 747 for(; fld->name != nil; fld++){ 748 if(strcmp(f->l->s, fld->name) == 0){ 749 f->op = WORD; 750 f->subop = fld->subop; 751 switch(fld->ftype){ 752 case Fnum: 753 f->ulv = atoi(f->r->s); 754 break; 755 case Fether: 756 v = csgetvalue(nil, "sys", (char*)f->r->s, 757 "ether", 0); 758 if(v){ 759 parseether(f->a, v); 760 free(v); 761 } else 762 parseether(f->a, f->r->s); 763 break; 764 case Fv4ip: 765 v = csgetvalue(nil, "sys", (char*)f->r->s, 766 "ip", 0); 767 if(v){ 768 f->ulv = parseip(x, v); 769 free(v); 770 }else 771 f->ulv = parseip(x, f->r->s); 772 break; 773 case Fv6ip: 774 v = csgetvalue(nil, "sys", (char*)f->r->s, 775 "ipv6", 0); 776 if(v){ 777 parseip(f->a, v); 778 free(v); 779 }else 780 parseip(f->a, f->r->s); 781 break; 782 case Fba: 783 parseba(f->a, f->r->s); 784 break; 785 default: 786 sysfatal("internal error: compile_cmp %s: %d", 787 proto, fld->ftype); 788 } 789 f->l = f->r = nil; 790 return; 791 } 792 } 793 sysfatal("unknown %s field in: %s = %s", proto, f->l->s, f->r->s); 794 } 795 796 void 797 _pf(Filter *f) 798 { 799 char *s; 800 801 if(f == nil) 802 return; 803 804 s = nil; 805 switch(f->op){ 806 case '!': 807 fprint(2, "!"); 808 _pf(f->l); 809 break; 810 case WORD: 811 fprint(2, "%s", f->s); 812 if(f->l != nil){ 813 fprint(2, "("); 814 _pf(f->l); 815 fprint(2, ")"); 816 } 817 break; 818 case LAND: 819 s = "&&"; 820 goto print; 821 case LOR: 822 s = "||"; 823 goto print; 824 case '=': 825 print: 826 _pf(f->l); 827 if(s) 828 fprint(2, " %s ", s); 829 else 830 fprint(2, " %c ", f->op); 831 _pf(f->r); 832 break; 833 default: 834 fprint(2, "???"); 835 break; 836 } 837 } 838 839 void 840 printfilter(Filter *f, char *tag) 841 { 842 fprint(2, "%s: ", tag); 843 _pf(f); 844 fprint(2, "\n"); 845 } 846 847 void 848 cat(void) 849 { 850 char buf[1024]; 851 int n; 852 853 while((n = read(0, buf, sizeof buf)) > 0) 854 write(1, buf, n); 855 } 856 857 static int fd1 = -1; 858 void 859 startmc(void) 860 { 861 int p[2]; 862 863 if(fd1 == -1) 864 fd1 = dup(1, -1); 865 866 if(pipe(p) < 0) 867 return; 868 switch(fork()){ 869 case -1: 870 return; 871 default: 872 close(p[0]); 873 dup(p[1], 1); 874 if(p[1] != 1) 875 close(p[1]); 876 return; 877 case 0: 878 close(p[1]); 879 dup(p[0], 0); 880 if(p[0] != 0) 881 close(p[0]); 882 execl("/bin/mc", "mc", nil); 883 cat(); 884 _exits(0); 885 } 886 } 887 888 void 889 stopmc(void) 890 { 891 close(1); 892 dup(fd1, 1); 893 waitpid(); 894 } 895 896 void 897 printhelp(char *name) 898 { 899 int len; 900 Proto *pr, **l; 901 Mux *m; 902 Field *f; 903 char fmt[40]; 904 905 if(name == nil){ 906 print("protocols:\n"); 907 startmc(); 908 for(l=protos; (pr=*l) != nil; l++) 909 print(" %s\n", pr->name); 910 stopmc(); 911 return; 912 } 913 914 pr = findproto(name); 915 if(pr == nil){ 916 print("unknown protocol %s\n", name); 917 return; 918 } 919 920 if(pr->field){ 921 print("%s's filter attributes:\n", pr->name); 922 len = 0; 923 for(f=pr->field; f->name; f++) 924 if(len < strlen(f->name)) 925 len = strlen(f->name); 926 startmc(); 927 for(f=pr->field; f->name; f++) 928 print(" %-*s - %s\n", len, f->name, f->help); 929 stopmc(); 930 } 931 if(pr->mux){ 932 print("%s's subprotos:\n", pr->name); 933 startmc(); 934 snprint(fmt, sizeof fmt, " %s %%s\n", pr->valfmt); 935 for(m=pr->mux; m->name != nil; m++) 936 print(fmt, m->val, m->name); 937 stopmc(); 938 } 939 } 940 941 /* 942 * demultiplex to next prototol header 943 */ 944 void 945 demux(Mux *mx, ulong val1, ulong val2, Msg *m, Proto *def) 946 { 947 m->pr = def; 948 for(mx = mx; mx->name != nil; mx++){ 949 if(val1 == mx->val || val2 == mx->val){ 950 m->pr = mx->pr; 951 break; 952 } 953 } 954 } 955 956 /* 957 * default framer just assumes the input packet is 958 * a single read 959 */ 960 int 961 defaultframer(int fd, uchar *pkt, int pktlen) 962 { 963 return read(fd, pkt, pktlen); 964 } 965