Blob


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