2 * snoopy - network sniffer
23 char *prom = "promiscuous";
34 vlong starttime, pkttime;
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);
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);
62 main(int argc, char **argv)
65 char *buf, *file, *p, *e;
69 Binit(&out, 1, OWRITE);
71 fmtinstall('E', eipfmt);
72 fmtinstall('V', eipfmt);
73 fmtinstall('I', eipfmt);
74 fmtinstall('H', encodefmt);
75 fmtinstall('F', fcallfmt);
77 pkt = malloc(Pktlen+16);
116 sysfatal("unknown protocol: %s", p);
140 file = "/net/ether0";
146 if((!tiflag) && strstr(file, "ether")){
149 snprint(buf, Blen, "%s!-1", file);
150 fd = dial(buf, 0, 0, &cfd);
152 sysfatal("dialing %s", buf);
153 if(pflag && fprint(cfd, prom, strlen(prom)) < 0)
154 sysfatal("setting %s", prom);
155 } else if((!tiflag) && strstr(file, "ipifc")){
158 snprint(buf, Blen, "%s/snoop", file);
159 fd = open(buf, OREAD);
161 sysfatal("opening %s: %r", buf);
165 fd = open(file, OREAD);
167 sysfatal("opening %s: %r", file);
169 filter = compile(filter);
172 /* read a trace file */
174 n = read(fd, pkt, 10);
177 pkttime = NetL(pkt+2);
178 pkttime = (pkttime<<32) | NetL(pkt+6);
182 if(readn(fd, pkt, n) != n)
184 if(filterpkt(filter, pkt, pkt+n, root, 1))
188 printpkt(buf, e, pkt, pkt+n);
191 /* read a real time stream */
194 n = root->framer(fd, pkt, Pktlen);
198 if(filterpkt(filter, pkt, pkt+n, root, 1))
202 printpkt(buf, e, pkt, pkt+n);
207 /* create a new filter node */
213 f = mallocz(sizeof(*f), 1);
215 sysfatal("newfilter: %r");
220 * apply filter to packet
223 _filterpkt(Filter *f, Msg *m)
232 return !_filterpkt(f->l, m);
235 return _filterpkt(f->l, &ma) && _filterpkt(f->r, m);
238 return _filterpkt(f->l, &ma) || _filterpkt(f->r, m);
245 if(m->pr && (m->pr->filter==nil || !(m->pr->filter)(f, m)))
251 return _filterpkt(f->l, m);
253 sysfatal("internal error: filterpkt op: %d", f->op);
257 filterpkt(Filter *f, uchar *ps, uchar *pe, Proto *pr, int needroot)
264 m.needroot = needroot;
268 return _filterpkt(f, &m);
272 * from the Unix world
274 #define PCAP_VERSION_MAJOR 2
275 #define PCAP_VERSION_MINOR 4
276 #define TCPDUMP_MAGIC 0xa1b2c3d4
278 struct pcap_file_header {
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_*) */
289 uvlong ts; /* time stamp */
290 ulong caplen; /* length of portion present */
291 ulong len; /* length this packet (off wire) */
300 struct pcap_file_header hdr;
302 hdr.magic = TCPDUMP_MAGIC;
303 hdr.version_major = PCAP_VERSION_MAJOR;
304 hdr.version_minor = PCAP_VERSION_MINOR;
311 write(1, &hdr, sizeof(hdr));
315 * write out a packet trace
318 tracepkt(uchar *ps, int len)
320 struct pcap_pkthdr *goo;
322 if(Mflag && len > Mflag)
325 goo = (struct pcap_pkthdr*)(ps-16);
329 write(1, goo, len+16);
332 hnputl(ps-8, pkttime>>32);
333 hnputl(ps-4, pkttime);
334 write(1, ps-10, len+10);
339 * format and print a packet
342 printpkt(char *p, char *e, uchar *ps, uchar *pe)
347 dt = (pkttime-starttime)/1000000LL;
348 m.p = seprint(p, e, "%6.6uld ms ", dt);
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");
361 m.p = seprint(m.p, m.e, ")");
362 if(m.pr == nil || m.ps >= m.pe)
367 if(write(1, p, m.p - p) < 0)
368 sysfatal("stdout: %r");
374 /* look up a protocol by its name */
376 findproto(char *name)
380 for(i = 0; i < nprotos; i++)
381 if(strcmp(xprotos[i]->name, name) == 0)
387 * add an undefined protocol to protos[]
394 xprotos = realloc(xprotos, (nprotos+1)*sizeof(Proto*));
395 pr = malloc(sizeof *pr);
398 xprotos[nprotos++] = pr;
403 * build a graph of protocols, this could easily be circular. This
404 * links together all the multiplexing in the protocol modules.
413 /* copy protos into a reallocable area */
414 for(nprotos = 0; protos[nprotos] != nil; nprotos++)
416 xprotos = malloc(nprotos*sizeof(Proto*));
417 memmove(xprotos, protos, nprotos*sizeof(Proto*));
419 for(l = protos; *l != nil; l++){
421 for(m = pr->mux; m != nil && m->name != nil; m++){
422 m->pr = findproto(m->name);
424 m->pr = addproto(m->name);
430 * add in a protocol node
433 addnode(Filter *f, Proto *pr)
445 * recurse through the protocol graph adding missing nodes
446 * to the filter if we reach the filter's protocol
449 _fillin(Filter *f, Proto *last, int depth)
457 for(m = last->mux; m != nil && m->name != nil; m++){
462 nf = _fillin(f, m->pr, depth);
464 return addnode(nf, m->pr);
470 fillin(Filter *f, Proto *last)
475 /* hack to make sure top level node is the root */
482 return addnode(f, root);
485 /* breadth first search though the protocol graph */
487 for(i = 1; i < 20; i++){
488 nf = _fillin(f, last, i);
496 * massage tree so that all paths from the root to a leaf
497 * contain a filter node for each header.
499 * also, set f->pr where possible
502 complete(Filter *f, Proto *last)
509 /* do a depth first traversal of the filter tree */
512 f->l = complete(f->l, last);
516 f->l = complete(f->l, last);
517 f->r = complete(f->r, last);
522 pr = findproto(f->s);
526 fprint(2, "%s unknown proto, ignoring params\n",
531 f->l = complete(f->l, pr);
534 sysfatal("internal error: can't get to %s", pr->name);
542 * merge common nodes under | and & moving the merged node
545 * do some constant foldong, e.g. `true & x' becomes x and
546 * 'true | x' becomes true.
560 /* is child also a not */
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 */
574 /* constant folding */
575 /* if either child is childless, just return that */
578 else if(f->r->l == nil)
581 /* move the common node up, thow away one node */
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 */
595 /* constant folding */
596 /* if either child is childless, ignore it */
599 else if(f->r->l == nil)
602 /* move the common node up, thow away one node */
604 f->l = _optimize(l->l);
605 f->r = _optimize(f->r->l);
609 f->l = _optimize(f->l);
610 f->r = _optimize(f->r);
626 * find any top level nodes that aren't the root
634 rv = findbogus(f->l);
636 rv |= findbogus(f->r);
638 } else if(f->pr != root){
639 fprint(2, "bad top-level protocol: %s\n", f->s);
649 _compile(Filter *f, Proto *last)
656 _compile(f->l, last);
660 _compile(f->l, last);
661 _compile(f->r, last);
665 if(last->compile == nil)
666 sysfatal("unknown %s subprotocol: %s", f->pr->name, f->s);
670 _compile(f->l, f->pr);
674 sysfatal("internal error: compilewalk: badly formed tree");
676 if(last->compile == nil)
677 sysfatal("unknown %s field: %s", f->pr->name, f->s);
681 sysfatal("internal error: compilewalk op: %d", f->op);
691 /* fill in the missing header filters */
692 f = complete(f, nil);
694 /* constant folding */
697 printfilter(f, "after optimize");
699 /* protocol specific compilations */
702 /* at this point, the root had better be the root proto */
704 fprint(2, "bogus filter\n");
715 parseba(uchar *to, char *from)
722 for(i = 0; i < 16; i++){
730 to[i] = strtoul(nip, 0, 16);
736 * compile WORD = WORD, becomes a single node with a subop
739 compile_cmp(char *proto, Filter *f, Field *fld)
745 sysfatal("internal error: compile_cmp %s: not a cmp", proto);
747 for(; fld->name != nil; fld++){
748 if(strcmp(f->l->s, fld->name) == 0){
750 f->subop = fld->subop;
753 f->ulv = atoi(f->r->s);
756 v = csgetvalue(nil, "sys", (char*)f->r->s,
762 parseether(f->a, f->r->s);
765 v = csgetvalue(nil, "sys", (char*)f->r->s,
768 f->ulv = parseip(x, v);
771 f->ulv = parseip(x, f->r->s);
774 v = csgetvalue(nil, "sys", (char*)f->r->s,
780 parseip(f->a, f->r->s);
783 parseba(f->a, f->r->s);
786 sysfatal("internal error: compile_cmp %s: %d",
793 sysfatal("unknown %s field in: %s = %s", proto, f->l->s, f->r->s);
811 fprint(2, "%s", f->s);
828 fprint(2, " %s ", s);
830 fprint(2, " %c ", f->op);
840 printfilter(Filter *f, char *tag)
842 fprint(2, "%s: ", tag);
853 while((n = read(0, buf, sizeof buf)) > 0)
882 execl("/bin/mc", "mc", nil);
897 printhelp(char *name)
906 print("protocols:\n");
908 for(l=protos; (pr=*l) != nil; l++)
909 print(" %s\n", pr->name);
914 pr = findproto(name);
916 print("unknown protocol %s\n", name);
921 print("%s's filter attributes:\n", pr->name);
923 for(f=pr->field; f->name; f++)
924 if(len < strlen(f->name))
925 len = strlen(f->name);
927 for(f=pr->field; f->name; f++)
928 print(" %-*s - %s\n", len, f->name, f->help);
932 print("%s's subprotos:\n", pr->name);
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);
942 * demultiplex to next prototol header
945 demux(Mux *mx, ulong val1, ulong val2, Msg *m, Proto *def)
948 for(mx = mx; mx->name != nil; mx++){
949 if(val1 == mx->val || val2 == mx->val){
957 * default framer just assumes the input packet is
961 defaultframer(int fd, uchar *pkt, int pktlen)
963 return read(fd, pkt, pktlen);