123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964 |
- /*
- * snoopy - network sniffer
- */
- #include <u.h>
- #include <libc.h>
- #include <ip.h>
- #include <bio.h>
- #include <fcall.h>
- #include <libsec.h>
- #include <ndb.h>
- #include "dat.h"
- #include "protos.h"
- #include "y.tab.h"
- int Cflag;
- int pflag;
- int Nflag;
- int Mflag;
- int sflag;
- int tiflag;
- int toflag;
- char *prom = "promiscuous";
- enum
- {
- Pktlen= 64*1024,
- Blen= 16*1024,
- };
- Filter *filter;
- Proto *root;
- Biobuf out;
- vlong starttime, pkttime;
- int pcap;
- int filterpkt(Filter *f, uchar *ps, uchar *pe, Proto *pr, int);
- void printpkt(char *p, char *e, uchar *ps, uchar *pe);
- void mkprotograph(void);
- Proto* findproto(char *name);
- Filter* compile(Filter *f);
- void printfilter(Filter *f, char *tag);
- void printhelp(char*);
- void tracepkt(uchar*, int);
- void pcaphdr(void);
- void
- printusage(void)
- {
- fprint(2, "usage: %s [-CDdpst] [-N n] [-f filter] [-h first-header] path\n", argv0);
- fprint(2, " for protocol help: %s -? [proto]\n", argv0);
- }
- void
- usage(void)
- {
- printusage();
- exits("usage");
- }
- void
- main(int argc, char **argv)
- {
- uchar *pkt;
- char *buf, *file, *p, *e;
- int fd, cfd;
- int n;
- Binit(&out, 1, OWRITE);
- fmtinstall('E', eipfmt);
- fmtinstall('V', eipfmt);
- fmtinstall('I', eipfmt);
- fmtinstall('H', encodefmt);
- fmtinstall('F', fcallfmt);
- pkt = malloc(Pktlen+16);
- pkt += 16;
- buf = malloc(Blen);
- e = buf+Blen-1;
- pflag = 1;
- Nflag = 32;
- sflag = 0;
- mkprotograph();
- ARGBEGIN{
- default:
- usage();
- case '?':
- printusage();
- printhelp(ARGF());
- exits(0);
- break;
- case 'M':
- p = EARGF(usage());
- Mflag = atoi(p);
- break;
- case 'N':
- p = EARGF(usage());
- Nflag = atoi(p);
- break;
- case 'f':
- p = EARGF(usage());
- yyinit(p);
- yyparse();
- break;
- case 's':
- sflag = 1;
- break;
- case 'h':
- p = EARGF(usage());
- root = findproto(p);
- if(root == nil)
- sysfatal("unknown protocol: %s", p);
- break;
- case 'd':
- toflag = 1;
- break;
- case 'D':
- toflag = 1;
- pcap = 1;
- break;
- case 't':
- tiflag = 1;
- break;
- case 'C':
- Cflag = 1;
- break;
- case 'p':
- pflag = 0;
- break;
- }ARGEND;
- if(pcap)
- pcaphdr();
- if(argc == 0){
- file = "/net/ether0";
- if(root != nil)
- root = ðer;
- } else
- file = argv[0];
- if((!tiflag) && strstr(file, "ether")){
- if(root == nil)
- root = ðer;
- snprint(buf, Blen, "%s!-1", file);
- fd = dial(buf, 0, 0, &cfd);
- if(fd < 0)
- sysfatal("dialing %s: %r", buf);
- if(pflag && fprint(cfd, prom, strlen(prom)) < 0)
- sysfatal("setting %s", prom);
- } else if((!tiflag) && strstr(file, "ipifc")){
- if(root == nil)
- root = &ip;
- snprint(buf, Blen, "%s/snoop", file);
- fd = open(buf, OREAD);
- if(fd < 0)
- sysfatal("opening %s: %r", buf);
- } else {
- if(root == nil)
- root = ðer;
- fd = open(file, OREAD);
- if(fd < 0)
- sysfatal("opening %s: %r", file);
- }
- filter = compile(filter);
- if(tiflag){
- /* read a trace file */
- for(;;){
- n = read(fd, pkt, 10);
- if(n != 10)
- break;
- pkttime = NetL(pkt+2);
- pkttime = (pkttime<<32) | NetL(pkt+6);
- if(starttime == 0LL)
- starttime = pkttime;
- n = NetS(pkt);
- if(readn(fd, pkt, n) != n)
- break;
- if(filterpkt(filter, pkt, pkt+n, root, 1))
- if(toflag)
- tracepkt(pkt, n);
- else
- printpkt(buf, e, pkt, pkt+n);
- }
- } else {
- /* read a real time stream */
- starttime = nsec();
- for(;;){
- n = root->framer(fd, pkt, Pktlen);
- if(n <= 0)
- break;
- pkttime = nsec();
- if(filterpkt(filter, pkt, pkt+n, root, 1))
- if(toflag)
- tracepkt(pkt, n);
- else
- printpkt(buf, e, pkt, pkt+n);
- }
- }
- }
- /* create a new filter node */
- Filter*
- newfilter(void)
- {
- Filter *f;
- f = mallocz(sizeof(*f), 1);
- if(f == nil)
- sysfatal("newfilter: %r");
- return f;
- }
- /*
- * apply filter to packet
- */
- int
- _filterpkt(Filter *f, Msg *m)
- {
- Msg ma;
- if(f == nil)
- return 1;
- switch(f->op){
- case '!':
- return !_filterpkt(f->l, m);
- case LAND:
- ma = *m;
- return _filterpkt(f->l, &ma) && _filterpkt(f->r, m);
- case LOR:
- ma = *m;
- return _filterpkt(f->l, &ma) || _filterpkt(f->r, m);
- case WORD:
- if(m->needroot){
- if(m->pr != f->pr)
- return 0;
- m->needroot = 0;
- }else{
- if(m->pr && (m->pr->filter==nil || !(m->pr->filter)(f, m)))
- return 0;
- }
- if(f->l == nil)
- return 1;
- m->pr = f->pr;
- return _filterpkt(f->l, m);
- }
- sysfatal("internal error: filterpkt op: %d", f->op);
- return 0;
- }
- int
- filterpkt(Filter *f, uchar *ps, uchar *pe, Proto *pr, int needroot)
- {
- Msg m;
- if(f == nil)
- return 1;
- m.needroot = needroot;
- m.ps = ps;
- m.pe = pe;
- m.pr = pr;
- return _filterpkt(f, &m);
- }
- /*
- * from the Unix world
- */
- #define PCAP_VERSION_MAJOR 2
- #define PCAP_VERSION_MINOR 4
- #define TCPDUMP_MAGIC 0xa1b2c3d4
- struct pcap_file_header {
- ulong magic;
- ushort version_major;
- ushort version_minor;
- long thiszone; /* gmt to local correction */
- ulong sigfigs; /* accuracy of timestamps */
- ulong snaplen; /* max length saved portion of each pkt */
- ulong linktype; /* data link type (DLT_*) */
- };
- struct pcap_pkthdr {
- uvlong ts; /* time stamp */
- ulong caplen; /* length of portion present */
- ulong len; /* length this packet (off wire) */
- };
- /*
- * pcap trace header
- */
- void
- pcaphdr(void)
- {
- struct pcap_file_header hdr;
- hdr.magic = TCPDUMP_MAGIC;
- hdr.version_major = PCAP_VERSION_MAJOR;
- hdr.version_minor = PCAP_VERSION_MINOR;
-
- hdr.thiszone = 0;
- hdr.snaplen = 1500;
- hdr.sigfigs = 0;
- hdr.linktype = 1;
- write(1, &hdr, sizeof(hdr));
- }
- /*
- * write out a packet trace
- */
- void
- tracepkt(uchar *ps, int len)
- {
- struct pcap_pkthdr *goo;
- if(Mflag && len > Mflag)
- len = Mflag;
- if(pcap){
- goo = (struct pcap_pkthdr*)(ps-16);
- goo->ts = pkttime;
- goo->caplen = len;
- goo->len = len;
- write(1, goo, len+16);
- } else {
- hnputs(ps-10, len);
- hnputl(ps-8, pkttime>>32);
- hnputl(ps-4, pkttime);
- write(1, ps-10, len+10);
- }
- }
- /*
- * format and print a packet
- */
- void
- printpkt(char *p, char *e, uchar *ps, uchar *pe)
- {
- Msg m;
- ulong dt;
- dt = (pkttime-starttime)/1000000LL;
- m.p = seprint(p, e, "%6.6uld ms ", dt);
- m.ps = ps;
- m.pe = pe;
- m.e = e;
- m.pr = root;
- while(m.p < m.e){
- if(!sflag)
- m.p = seprint(m.p, m.e, "\n\t");
- m.p = seprint(m.p, m.e, "%s(", m.pr->name);
- if((*m.pr->seprint)(&m) < 0){
- m.p = seprint(m.p, m.e, "TOO SHORT");
- m.ps = m.pe;
- }
- m.p = seprint(m.p, m.e, ")");
- if(m.pr == nil || m.ps >= m.pe)
- break;
- }
- *m.p++ = '\n';
- if(write(1, p, m.p - p) < 0)
- sysfatal("stdout: %r");
- }
- Proto **xprotos;
- int nprotos;
- /* look up a protocol by its name */
- Proto*
- findproto(char *name)
- {
- int i;
- for(i = 0; i < nprotos; i++)
- if(strcmp(xprotos[i]->name, name) == 0)
- return xprotos[i];
- return nil;
- }
- /*
- * add an undefined protocol to protos[]
- */
- Proto*
- addproto(char *name)
- {
- Proto *pr;
- xprotos = realloc(xprotos, (nprotos+1)*sizeof(Proto*));
- pr = malloc(sizeof *pr);
- *pr = dump;
- pr->name = name;
- xprotos[nprotos++] = pr;
- return pr;
- }
- /*
- * build a graph of protocols, this could easily be circular. This
- * links together all the multiplexing in the protocol modules.
- */
- void
- mkprotograph(void)
- {
- Proto **l;
- Proto *pr;
- Mux *m;
- /* copy protos into a reallocable area */
- for(nprotos = 0; protos[nprotos] != nil; nprotos++)
- ;
- xprotos = malloc(nprotos*sizeof(Proto*));
- memmove(xprotos, protos, nprotos*sizeof(Proto*));
- for(l = protos; *l != nil; l++){
- pr = *l;
- for(m = pr->mux; m != nil && m->name != nil; m++){
- m->pr = findproto(m->name);
- if(m->pr == nil)
- m->pr = addproto(m->name);
- }
- }
- }
- /*
- * add in a protocol node
- */
- static Filter*
- addnode(Filter *f, Proto *pr)
- {
- Filter *nf;
- nf = newfilter();
- nf->pr = pr;
- nf->s = pr->name;
- nf->l = f;
- nf->op = WORD;
- return nf;
- }
- /*
- * recurse through the protocol graph adding missing nodes
- * to the filter if we reach the filter's protocol
- */
- static Filter*
- _fillin(Filter *f, Proto *last, int depth)
- {
- Mux *m;
- Filter *nf;
- if(depth-- <= 0)
- return nil;
- for(m = last->mux; m != nil && m->name != nil; m++){
- if(m->pr == nil)
- continue;
- if(f->pr == m->pr)
- return f;
- nf = _fillin(f, m->pr, depth);
- if(nf != nil)
- return addnode(nf, m->pr);
- }
- return nil;
- }
- static Filter*
- fillin(Filter *f, Proto *last)
- {
- int i;
- Filter *nf;
- /* hack to make sure top level node is the root */
- if(last == nil){
- if(f->pr == root)
- return f;
- f = fillin(f, root);
- if(f == nil)
- return nil;
- return addnode(f, root);
- }
- /* breadth first search though the protocol graph */
- nf = f;
- for(i = 1; i < 20; i++){
- nf = _fillin(f, last, i);
- if(nf != nil)
- break;
- }
- return nf;
- }
- /*
- * massage tree so that all paths from the root to a leaf
- * contain a filter node for each header.
- *
- * also, set f->pr where possible
- */
- Filter*
- complete(Filter *f, Proto *last)
- {
- Proto *pr;
- if(f == nil)
- return f;
- /* do a depth first traversal of the filter tree */
- switch(f->op){
- case '!':
- f->l = complete(f->l, last);
- break;
- case LAND:
- case LOR:
- f->l = complete(f->l, last);
- f->r = complete(f->r, last);
- break;
- case '=':
- break;
- case WORD:
- pr = findproto(f->s);
- f->pr = pr;
- if(pr == nil){
- if(f->l != nil){
- fprint(2, "%s unknown proto, ignoring params\n",
- f->s);
- f->l = nil;
- }
- } else {
- f->l = complete(f->l, pr);
- f = fillin(f, last);
- if(f == nil)
- sysfatal("internal error: can't get to %s", pr->name);
- }
- break;
- }
- return f;
- }
- /*
- * merge common nodes under | and & moving the merged node
- * above the | or &.
- *
- * do some constant foldong, e.g. `true & x' becomes x and
- * 'true | x' becomes true.
- */
- static int changed;
- static Filter*
- _optimize(Filter *f)
- {
- Filter *l;
- if(f == nil)
- return f;
- switch(f->op){
- case '!':
- /* is child also a not */
- if(f->l->op == '!'){
- changed = 1;
- return f->l->l;
- }
- break;
- case LOR:
- /* are two children the same protocol? */
- if(f->l->op != f->r->op || f->r->op != WORD
- || f->l->pr != f->r->pr || f->l->pr == nil)
- break; /* no optimization */
- changed = 1;
- /* constant folding */
- /* if either child is childless, just return that */
- if(f->l->l == nil)
- return f->l;
- else if(f->r->l == nil)
- return f->r;
- /* move the common node up, thow away one node */
- l = f->l;
- f->l = l->l;
- f->r = f->r->l;
- l->l = f;
- return l;
- case LAND:
- /* are two children the same protocol? */
- if(f->l->op != f->r->op || f->r->op != WORD
- || f->l->pr != f->r->pr || f->l->pr == nil)
- break; /* no optimization */
- changed = 1;
- /* constant folding */
- /* if either child is childless, ignore it */
- if(f->l->l == nil)
- return f->r;
- else if(f->r->l == nil)
- return f->l;
- /* move the common node up, thow away one node */
- l = f->l;
- f->l = _optimize(l->l);
- f->r = _optimize(f->r->l);
- l->l = f;
- return l;
- }
- f->l = _optimize(f->l);
- f->r = _optimize(f->r);
- return f;
- }
- Filter*
- optimize(Filter *f)
- {
- do{
- changed = 0;
- f = _optimize(f);
- }while(changed);
- return f;
- }
- /*
- * find any top level nodes that aren't the root
- */
- int
- findbogus(Filter *f)
- {
- int rv;
- if(f->op != WORD){
- rv = findbogus(f->l);
- if(f->r)
- rv |= findbogus(f->r);
- return rv;
- } else if(f->pr != root){
- fprint(2, "bad top-level protocol: %s\n", f->s);
- return 1;
- }
- return 0;
- }
- /*
- * compile the filter
- */
- static void
- _compile(Filter *f, Proto *last)
- {
- if(f == nil)
- return;
- switch(f->op){
- case '!':
- _compile(f->l, last);
- break;
- case LOR:
- case LAND:
- _compile(f->l, last);
- _compile(f->r, last);
- break;
- case WORD:
- if(last != nil){
- if(last->compile == nil)
- sysfatal("unknown %s subprotocol: %s", f->pr->name, f->s);
- (*last->compile)(f);
- }
- if(f->l)
- _compile(f->l, f->pr);
- break;
- case '=':
- if(last == nil)
- sysfatal("internal error: compilewalk: badly formed tree");
-
- if(last->compile == nil)
- sysfatal("unknown %s field: %s", f->pr->name, f->s);
- (*last->compile)(f);
- break;
- default:
- sysfatal("internal error: compilewalk op: %d", f->op);
- }
- }
- Filter*
- compile(Filter *f)
- {
- if(f == nil)
- return f;
- /* fill in the missing header filters */
- f = complete(f, nil);
- /* constant folding */
- f = optimize(f);
- if(!toflag)
- printfilter(f, "after optimize");
- /* protocol specific compilations */
- _compile(f, nil);
- /* at this point, the root had better be the root proto */
- if(findbogus(f)){
- fprint(2, "bogus filter\n");
- exits("bad filter");
- }
- return f;
- }
- /*
- * parse a byte array
- */
- int
- parseba(uchar *to, char *from)
- {
- char nip[4];
- char *p;
- int i;
- p = from;
- for(i = 0; i < 16; i++){
- if(*p == 0)
- return -1;
- nip[0] = *p++;
- if(*p == 0)
- return -1;
- nip[1] = *p++;
- nip[2] = 0;
- to[i] = strtoul(nip, 0, 16);
- }
- return i;
- }
- /*
- * compile WORD = WORD, becomes a single node with a subop
- */
- void
- compile_cmp(char *proto, Filter *f, Field *fld)
- {
- uchar x[IPaddrlen];
- char *v;
- if(f->op != '=')
- sysfatal("internal error: compile_cmp %s: not a cmp", proto);
- for(; fld->name != nil; fld++){
- if(strcmp(f->l->s, fld->name) == 0){
- f->op = WORD;
- f->subop = fld->subop;
- switch(fld->ftype){
- case Fnum:
- f->ulv = atoi(f->r->s);
- break;
- case Fether:
- v = csgetvalue(nil, "sys", (char*)f->r->s,
- "ether", 0);
- if(v){
- parseether(f->a, v);
- free(v);
- } else
- parseether(f->a, f->r->s);
- break;
- case Fv4ip:
- v = csgetvalue(nil, "sys", (char*)f->r->s,
- "ip", 0);
- if(v){
- f->ulv = parseip(x, v);
- free(v);
- }else
- f->ulv = parseip(x, f->r->s);
- break;
- case Fv6ip:
- v = csgetvalue(nil, "sys", (char*)f->r->s,
- "ipv6", 0);
- if(v){
- parseip(f->a, v);
- free(v);
- }else
- parseip(f->a, f->r->s);
- break;
- case Fba:
- parseba(f->a, f->r->s);
- break;
- default:
- sysfatal("internal error: compile_cmp %s: %d",
- proto, fld->ftype);
- }
- f->l = f->r = nil;
- return;
- }
- }
- sysfatal("unknown %s field in: %s = %s", proto, f->l->s, f->r->s);
- }
- void
- _pf(Filter *f)
- {
- char *s;
- if(f == nil)
- return;
- s = nil;
- switch(f->op){
- case '!':
- fprint(2, "!");
- _pf(f->l);
- break;
- case WORD:
- fprint(2, "%s", f->s);
- if(f->l != nil){
- fprint(2, "(");
- _pf(f->l);
- fprint(2, ")");
- }
- break;
- case LAND:
- s = "&&";
- goto print;
- case LOR:
- s = "||";
- goto print;
- case '=':
- print:
- _pf(f->l);
- if(s)
- fprint(2, " %s ", s);
- else
- fprint(2, " %c ", f->op);
- _pf(f->r);
- break;
- default:
- fprint(2, "???");
- break;
- }
- }
- void
- printfilter(Filter *f, char *tag)
- {
- fprint(2, "%s: ", tag);
- _pf(f);
- fprint(2, "\n");
- }
- void
- cat(void)
- {
- char buf[1024];
- int n;
-
- while((n = read(0, buf, sizeof buf)) > 0)
- write(1, buf, n);
- }
- static int fd1 = -1;
- void
- startmc(void)
- {
- int p[2];
-
- if(fd1 == -1)
- fd1 = dup(1, -1);
-
- if(pipe(p) < 0)
- return;
- switch(fork()){
- case -1:
- return;
- default:
- close(p[0]);
- dup(p[1], 1);
- if(p[1] != 1)
- close(p[1]);
- return;
- case 0:
- close(p[1]);
- dup(p[0], 0);
- if(p[0] != 0)
- close(p[0]);
- execl("/bin/mc", "mc", nil);
- cat();
- _exits(0);
- }
- }
- void
- stopmc(void)
- {
- close(1);
- dup(fd1, 1);
- waitpid();
- }
- void
- printhelp(char *name)
- {
- int len;
- Proto *pr, **l;
- Mux *m;
- Field *f;
- char fmt[40];
-
- if(name == nil){
- print("protocols:\n");
- startmc();
- for(l=protos; (pr=*l) != nil; l++)
- print(" %s\n", pr->name);
- stopmc();
- return;
- }
-
- pr = findproto(name);
- if(pr == nil){
- print("unknown protocol %s\n", name);
- return;
- }
-
- if(pr->field){
- print("%s's filter attributes:\n", pr->name);
- len = 0;
- for(f=pr->field; f->name; f++)
- if(len < strlen(f->name))
- len = strlen(f->name);
- startmc();
- for(f=pr->field; f->name; f++)
- print(" %-*s - %s\n", len, f->name, f->help);
- stopmc();
- }
- if(pr->mux){
- print("%s's subprotos:\n", pr->name);
- startmc();
- snprint(fmt, sizeof fmt, " %s %%s\n", pr->valfmt);
- for(m=pr->mux; m->name != nil; m++)
- print(fmt, m->val, m->name);
- stopmc();
- }
- }
- /*
- * demultiplex to next prototol header
- */
- void
- demux(Mux *mx, ulong val1, ulong val2, Msg *m, Proto *def)
- {
- m->pr = def;
- for(mx = mx; mx->name != nil; mx++){
- if(val1 == mx->val || val2 == mx->val){
- m->pr = mx->pr;
- break;
- }
- }
- }
- /*
- * default framer just assumes the input packet is
- * a single read
- */
- int
- defaultframer(int fd, uchar *pkt, int pktlen)
- {
- return read(fd, pkt, pktlen);
- }
|