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 0xa1b23c4d
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_*) */
291 ulong caplen; /* length of portion present */
292 ulong len; /* length this packet (off wire) */
301 struct pcap_file_header hdr;
303 hdr.magic = TCPDUMP_MAGIC;
304 hdr.version_major = PCAP_VERSION_MAJOR;
305 hdr.version_minor = PCAP_VERSION_MINOR;
312 write(1, &hdr, sizeof(hdr));
316 * write out a packet trace
319 tracepkt(uchar *ps, int len)
321 struct pcap_pkthdr *goo;
323 if(Mflag && len > Mflag)
326 goo = (struct pcap_pkthdr*)(ps-16);
327 goo->ts_sec = (uvlong)pkttime / 1000000000;
328 goo->ts_nsec = (uvlong)pkttime % 1000000000;
331 write(1, goo, len+16);
334 hnputl(ps-8, pkttime>>32);
335 hnputl(ps-4, pkttime);
336 write(1, ps-10, len+10);
341 * format and print a packet
344 printpkt(char *p, char *e, uchar *ps, uchar *pe)
349 dt = (pkttime-starttime)/1000000LL;
350 m.p = seprint(p, e, "%6.6uld ms ", dt);
357 m.p = seprint(m.p, m.e, "\n\t");
358 m.p = seprint(m.p, m.e, "%s(", m.pr->name);
359 if((*m.pr->seprint)(&m) < 0){
360 m.p = seprint(m.p, m.e, "TOO SHORT");
363 m.p = seprint(m.p, m.e, ")");
364 if(m.pr == nil || m.ps >= m.pe)
369 if(write(1, p, m.p - p) < 0)
370 sysfatal("stdout: %r");
376 /* look up a protocol by its name */
378 findproto(char *name)
382 for(i = 0; i < nprotos; i++)
383 if(strcmp(xprotos[i]->name, name) == 0)
389 * add an undefined protocol to protos[]
396 xprotos = realloc(xprotos, (nprotos+1)*sizeof(Proto*));
397 pr = malloc(sizeof *pr);
400 xprotos[nprotos++] = pr;
405 * build a graph of protocols, this could easily be circular. This
406 * links together all the multiplexing in the protocol modules.
415 /* copy protos into a reallocable area */
416 for(nprotos = 0; protos[nprotos] != nil; nprotos++)
418 xprotos = malloc(nprotos*sizeof(Proto*));
419 memmove(xprotos, protos, nprotos*sizeof(Proto*));
421 for(l = protos; *l != nil; l++){
423 for(m = pr->mux; m != nil && m->name != nil; m++){
424 m->pr = findproto(m->name);
426 m->pr = addproto(m->name);
432 * add in a protocol node
435 addnode(Filter *f, Proto *pr)
447 * recurse through the protocol graph adding missing nodes
448 * to the filter if we reach the filter's protocol
451 _fillin(Filter *f, Proto *last, int depth)
459 for(m = last->mux; m != nil && m->name != nil; m++){
464 nf = _fillin(f, m->pr, depth);
466 return addnode(nf, m->pr);
472 fillin(Filter *f, Proto *last)
477 /* hack to make sure top level node is the root */
484 return addnode(f, root);
487 /* breadth first search though the protocol graph */
489 for(i = 1; i < 20; i++){
490 nf = _fillin(f, last, i);
498 * massage tree so that all paths from the root to a leaf
499 * contain a filter node for each header.
501 * also, set f->pr where possible
504 complete(Filter *f, Proto *last)
511 /* do a depth first traversal of the filter tree */
514 f->l = complete(f->l, last);
518 f->l = complete(f->l, last);
519 f->r = complete(f->r, last);
524 pr = findproto(f->s);
528 fprint(2, "%s unknown proto, ignoring params\n",
533 f->l = complete(f->l, pr);
536 sysfatal("internal error: can't get to %s", pr->name);
544 * merge common nodes under | and & moving the merged node
547 * do some constant foldong, e.g. `true & x' becomes x and
548 * 'true | x' becomes true.
562 /* is child also a not */
569 /* are two children the same protocol? */
570 if(f->l->op != f->r->op || f->r->op != WORD
571 || f->l->pr != f->r->pr || f->l->pr == nil)
572 break; /* no optimization */
576 /* constant folding */
577 /* if either child is childless, just return that */
580 else if(f->r->l == nil)
583 /* move the common node up, thow away one node */
590 /* are two children the same protocol? */
591 if(f->l->op != f->r->op || f->r->op != WORD
592 || f->l->pr != f->r->pr || f->l->pr == nil)
593 break; /* no optimization */
597 /* constant folding */
598 /* if either child is childless, ignore it */
601 else if(f->r->l == nil)
604 /* move the common node up, thow away one node */
606 f->l = _optimize(l->l);
607 f->r = _optimize(f->r->l);
611 f->l = _optimize(f->l);
612 f->r = _optimize(f->r);
628 * find any top level nodes that aren't the root
636 rv = findbogus(f->l);
638 rv |= findbogus(f->r);
640 } else if(f->pr != root){
641 fprint(2, "bad top-level protocol: %s\n", f->s);
651 _compile(Filter *f, Proto *last)
658 _compile(f->l, last);
662 _compile(f->l, last);
663 _compile(f->r, last);
667 if(last->compile == nil)
668 sysfatal("unknown %s subprotocol: %s", f->pr->name, f->s);
672 _compile(f->l, f->pr);
676 sysfatal("internal error: compilewalk: badly formed tree");
678 if(last->compile == nil)
679 sysfatal("unknown %s field: %s", f->pr->name, f->s);
683 sysfatal("internal error: compilewalk op: %d", f->op);
693 /* fill in the missing header filters */
694 f = complete(f, nil);
696 /* constant folding */
699 printfilter(f, "after optimize");
701 /* protocol specific compilations */
704 /* at this point, the root had better be the root proto */
706 fprint(2, "bogus filter\n");
717 parseba(uchar *to, char *from)
724 for(i = 0; i < 16; i++){
732 to[i] = strtoul(nip, 0, 16);
738 * compile WORD = WORD, becomes a single node with a subop
741 compile_cmp(char *proto, Filter *f, Field *fld)
747 sysfatal("internal error: compile_cmp %s: not a cmp", proto);
749 for(; fld->name != nil; fld++){
750 if(strcmp(f->l->s, fld->name) == 0){
752 f->subop = fld->subop;
755 f->ulv = strtoul(f->r->s, 0, 0);
758 v = csgetvalue(nil, "sys", (char*)f->r->s,
764 parseether(f->a, f->r->s);
767 v = csgetvalue(nil, "sys", (char*)f->r->s,
770 f->ulv = parseip(x, v);
773 f->ulv = parseip(x, f->r->s);
776 v = csgetvalue(nil, "sys", (char*)f->r->s,
782 parseip(f->a, f->r->s);
785 parseba(f->a, f->r->s);
788 sysfatal("internal error: compile_cmp %s: %d",
795 sysfatal("unknown %s field in: %s = %s", proto, f->l->s, f->r->s);
813 fprint(2, "%s", f->s);
830 fprint(2, " %s ", s);
832 fprint(2, " %c ", f->op);
842 printfilter(Filter *f, char *tag)
844 fprint(2, "%s: ", tag);
855 while((n = read(0, buf, sizeof buf)) > 0)
884 execl("/bin/mc", "mc", nil);
899 printhelp(char *name)
908 print("protocols:\n");
910 for(l=protos; (pr=*l) != nil; l++)
911 print(" %s\n", pr->name);
916 pr = findproto(name);
918 print("unknown protocol %s\n", name);
923 print("%s's filter attributes:\n", pr->name);
925 for(f=pr->field; f->name; f++)
926 if(len < strlen(f->name))
927 len = strlen(f->name);
929 for(f=pr->field; f->name; f++)
930 print(" %-*s - %s\n", len, f->name, f->help);
934 print("%s's subprotos:\n", pr->name);
936 snprint(fmt, sizeof fmt, " %s %%s\n", pr->valfmt);
937 for(m=pr->mux; m->name != nil; m++)
938 print(fmt, m->val, m->name);
944 * demultiplex to next prototol header
947 demux(Mux *mx, ulong val1, ulong val2, Msg *m, Proto *def)
950 for(mx = mx; mx->name != nil; mx++){
951 if(val1 == mx->val || val2 == mx->val){
959 * default framer just assumes the input packet is
963 defaultframer(int fd, uchar *pkt, int pktlen)
965 return read(fd, pkt, pktlen);