diff options
author | rsc <devnull@localhost> | 2005-12-26 04:48:52 +0000 |
---|---|---|
committer | rsc <devnull@localhost> | 2005-12-26 04:48:52 +0000 |
commit | 87a52e0485d3281ebea6bf4b725aa8023690e96f (patch) | |
tree | 0abc2d2ddb875196177231639d3cb4519e814b9d /src/cmd/ip/snoopy/main.c | |
parent | 35d26aa32167e84326cdb745c0e906393b8de71d (diff) | |
download | plan9port-87a52e0485d3281ebea6bf4b725aa8023690e96f.tar.gz plan9port-87a52e0485d3281ebea6bf4b725aa8023690e96f.tar.bz2 plan9port-87a52e0485d3281ebea6bf4b725aa8023690e96f.zip |
new goodies
Diffstat (limited to 'src/cmd/ip/snoopy/main.c')
-rwxr-xr-x | src/cmd/ip/snoopy/main.c | 841 |
1 files changed, 841 insertions, 0 deletions
diff --git a/src/cmd/ip/snoopy/main.c b/src/cmd/ip/snoopy/main.c new file mode 100755 index 00000000..73c1e27b --- /dev/null +++ b/src/cmd/ip/snoopy/main.c @@ -0,0 +1,841 @@ +#include <u.h> +#include <libc.h> +#include <ip.h> +#include <bio.h> +#include <fcall.h> +#include <libsec.h> +#include "dat.h" +#include "protos.h" +#include "y.tab.h" + +int Cflag; +int pflag; +int Nflag; +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(void); +void tracepkt(uchar*, int); +void pcaphdr(void); + +void +usage(void) +{ + fprint(2, "usage: %s [-std?] [-c] [-N n] [-f filter] [-h first-header] path", argv0); + exits("usage"); +} + +void +main(int argc, char **argv) +{ + uchar *pkt; + char *buf, *file, *p, *e; + int fd; + 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{ + case '?': + printhelp(); + exits(0); + break; + case 'N': + p = ARGF(); + if(p == nil) + usage(); + Nflag = atoi(p); + break; + case 'f': + p = ARGF(); + if(p == nil) + usage(); + yyinit(p); + yyparse(); + break; + case 's': + sflag = 1; + break; + case 'h': + p = ARGF(); + if(p == nil) + 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 = nil; + else + file = argv[0]; + + if(tiflag){ + fd = open(file, OREAD); + if(fd < 0) + sysfatal("opening %s: %r", file); + }else{ + fd = opendevice(file, pflag); + if(fd < 0) + sysfatal("opening device %s: %r", file ? file : "(all)"); + } + if(root == nil) + root = ðer; + 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 != 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(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; + uvlong dt; + Tm tm; + + tm = *localtime(pkttime/1000000000LL); + m.p = seprint(p, e, "%02d/%02d/%04d %02d:%02d:%02d.%09lld", + tm.mon+1, tm.mday, tm.year+1900, tm.hour, tm.min, tm.sec, + pkttime%1000000000LL); + 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) + (*last->compile)(f); + if(f->l) + _compile(f->l, f->pr); + break; + case '=': + if(last == nil) + sysfatal("internal error: compilewalk: badly formed tree"); + (*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]; + + 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: + parseether(f->a, f->r->s); + break; + case Fv4ip: + f->ulv = parseip(x, f->r->s); + break; + case Fv6ip: + 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 +printhelp(void) +{ + Proto *pr, **l; + Mux *m; + Field *f; + + for(l = protos; *l != nil; l++){ + pr = *l; + if(pr->field != nil){ + print("%s's filter attr:\n", pr->name); + for(f = pr->field; f->name != nil; f++) + print("\t%s\t- %s\n", f->name, f->help); + } + if(pr->mux != nil){ + print("%s's subprotos:\n", pr->name); + for(m = pr->mux; m->name != nil; m++) + print("\t%s\n", m->name); + } + } +} + +/* + * 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); +} |