]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ip/snoopy/main.c
Import sources from 2011-03-30 iso image - lib
[plan9front.git] / sys / src / cmd / ip / snoopy / main.c
1 /*
2  * snoopy - network sniffer
3  */
4 #include <u.h>
5 #include <libc.h>
6 #include <ip.h>
7 #include <bio.h>
8 #include <fcall.h>
9 #include <libsec.h>
10 #include <ndb.h>
11 #include "dat.h"
12 #include "protos.h"
13 #include "y.tab.h"
14
15 int Cflag;
16 int pflag;
17 int Nflag;
18 int Mflag;
19 int sflag;
20 int tiflag;
21 int toflag;
22
23 char *prom = "promiscuous";
24
25 enum
26 {
27         Pktlen= 64*1024,
28         Blen=   16*1024,
29 };
30
31 Filter *filter;
32 Proto *root;
33 Biobuf out;
34 vlong starttime, pkttime;
35 int pcap;
36
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);
45 void    pcaphdr(void);
46
47 void
48 printusage(void)
49 {
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);
52 }
53
54 void
55 usage(void)
56 {
57         printusage();
58         exits("usage");
59 }
60
61 void
62 main(int argc, char **argv)
63 {
64         uchar *pkt;
65         char *buf, *file, *p, *e;
66         int fd, cfd;
67         int n;
68
69         Binit(&out, 1, OWRITE);
70
71         fmtinstall('E', eipfmt);
72         fmtinstall('V', eipfmt);
73         fmtinstall('I', eipfmt);
74         fmtinstall('H', encodefmt);
75         fmtinstall('F', fcallfmt);
76
77         pkt = malloc(Pktlen+16);
78         pkt += 16;
79         buf = malloc(Blen);
80         e = buf+Blen-1;
81
82         pflag = 1;
83         Nflag = 32;
84         sflag = 0;
85
86         mkprotograph();
87
88         ARGBEGIN{
89         default:
90                 usage();
91         case '?':
92                 printusage();
93                 printhelp(ARGF());
94                 exits(0);
95                 break;
96         case 'M':
97                 p = EARGF(usage());
98                 Mflag = atoi(p);
99                 break;
100         case 'N':
101                 p = EARGF(usage());
102                 Nflag = atoi(p);
103                 break;
104         case 'f':
105                 p = EARGF(usage());
106                 yyinit(p);
107                 yyparse();
108                 break;
109         case 's':
110                 sflag = 1;
111                 break;
112         case 'h':
113                 p = EARGF(usage());
114                 root = findproto(p);
115                 if(root == nil)
116                         sysfatal("unknown protocol: %s", p);
117                 break;
118         case 'd':
119                 toflag = 1;
120                 break;
121         case 'D':
122                 toflag = 1;
123                 pcap = 1;
124                 break;
125         case 't':
126                 tiflag = 1;
127                 break;
128         case 'C':
129                 Cflag = 1;
130                 break;
131         case 'p':
132                 pflag = 0;
133                 break;
134         }ARGEND;
135
136         if(pcap)
137                 pcaphdr();
138
139         if(argc == 0){
140                 file = "/net/ether0";
141                 if(root != nil)
142                         root = &ether;
143         } else
144                 file = argv[0];
145
146         if((!tiflag) && strstr(file, "ether")){
147                 if(root == nil)
148                         root = &ether;
149                 snprint(buf, Blen, "%s!-1", file);
150                 fd = dial(buf, 0, 0, &cfd);
151                 if(fd < 0)
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")){
156                 if(root == nil)
157                         root = &ip;
158                 snprint(buf, Blen, "%s/snoop", file);
159                 fd = open(buf, OREAD);
160                 if(fd < 0)
161                         sysfatal("opening %s: %r", buf);
162         } else {
163                 if(root == nil)
164                         root = &ether;
165                 fd = open(file, OREAD);
166                 if(fd < 0)
167                         sysfatal("opening %s: %r", file);
168         }
169         filter = compile(filter);
170
171         if(tiflag){
172                 /* read a trace file */
173                 for(;;){
174                         n = read(fd, pkt, 10);
175                         if(n != 10)
176                                 break;
177                         pkttime = NetL(pkt+2);
178                         pkttime = (pkttime<<32) | NetL(pkt+6);
179                         if(starttime == 0LL)
180                                 starttime = pkttime;
181                         n = NetS(pkt);
182                         if(readn(fd, pkt, n) != n)
183                                 break;
184                         if(filterpkt(filter, pkt, pkt+n, root, 1))
185                                 if(toflag)
186                                         tracepkt(pkt, n);
187                                 else
188                                         printpkt(buf, e, pkt, pkt+n);
189                 }
190         } else {
191                 /* read a real time stream */
192                 starttime = nsec();
193                 for(;;){
194                         n = root->framer(fd, pkt, Pktlen);
195                         if(n <= 0)
196                                 break;
197                         pkttime = nsec();
198                         if(filterpkt(filter, pkt, pkt+n, root, 1))
199                                 if(toflag)
200                                         tracepkt(pkt, n);
201                                 else
202                                         printpkt(buf, e, pkt, pkt+n);
203                 }
204         }
205 }
206
207 /* create a new filter node */
208 Filter*
209 newfilter(void)
210 {
211         Filter *f;
212
213         f = mallocz(sizeof(*f), 1);
214         if(f == nil)
215                 sysfatal("newfilter: %r");
216         return f;
217 }
218
219 /*
220  *  apply filter to packet
221  */
222 int
223 _filterpkt(Filter *f, Msg *m)
224 {
225         Msg ma;
226
227         if(f == nil)
228                 return 1;
229
230         switch(f->op){
231         case '!':
232                 return !_filterpkt(f->l, m);
233         case LAND:
234                 ma = *m;
235                 return _filterpkt(f->l, &ma) && _filterpkt(f->r, m);
236         case LOR:
237                 ma = *m;
238                 return _filterpkt(f->l, &ma) || _filterpkt(f->r, m);
239         case WORD:
240                 if(m->needroot){
241                         if(m->pr != f->pr)
242                                 return 0;
243                         m->needroot = 0;
244                 }else{
245                         if(m->pr && (m->pr->filter==nil || !(m->pr->filter)(f, m)))
246                                 return 0;
247                 }
248                 if(f->l == nil)
249                         return 1;
250                 m->pr = f->pr;
251                 return _filterpkt(f->l, m);
252         }
253         sysfatal("internal error: filterpkt op: %d", f->op);
254         return 0;
255 }
256 int
257 filterpkt(Filter *f, uchar *ps, uchar *pe, Proto *pr, int needroot)
258 {
259         Msg m;
260
261         if(f == nil)
262                 return 1;
263
264         m.needroot = needroot;
265         m.ps = ps;
266         m.pe = pe;
267         m.pr = pr;
268         return _filterpkt(f, &m);
269 }
270
271 /*
272  *  from the Unix world
273  */
274 #define PCAP_VERSION_MAJOR 2
275 #define PCAP_VERSION_MINOR 4
276 #define TCPDUMP_MAGIC 0xa1b2c3d4
277
278 struct pcap_file_header {
279         ulong           magic;
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_*) */
286 };
287
288 struct pcap_pkthdr {
289         uvlong  ts;     /* time stamp */
290         ulong   caplen; /* length of portion present */
291         ulong   len;    /* length this packet (off wire) */
292 };
293
294 /*
295  *  pcap trace header 
296  */
297 void
298 pcaphdr(void)
299 {
300         struct pcap_file_header hdr;
301
302         hdr.magic = TCPDUMP_MAGIC;
303         hdr.version_major = PCAP_VERSION_MAJOR;
304         hdr.version_minor = PCAP_VERSION_MINOR;
305   
306         hdr.thiszone = 0;
307         hdr.snaplen = 1500;
308         hdr.sigfigs = 0;
309         hdr.linktype = 1;
310
311         write(1, &hdr, sizeof(hdr));
312 }
313
314 /*
315  *  write out a packet trace
316  */
317 void
318 tracepkt(uchar *ps, int len)
319 {
320         struct pcap_pkthdr *goo;
321
322         if(Mflag && len > Mflag)
323                 len = Mflag;
324         if(pcap){
325                 goo = (struct pcap_pkthdr*)(ps-16);
326                 goo->ts = pkttime;
327                 goo->caplen = len;
328                 goo->len = len;
329                 write(1, goo, len+16);
330         } else {
331                 hnputs(ps-10, len);
332                 hnputl(ps-8, pkttime>>32);
333                 hnputl(ps-4, pkttime);
334                 write(1, ps-10, len+10);
335         }
336 }
337
338 /*
339  *  format and print a packet
340  */
341 void
342 printpkt(char *p, char *e, uchar *ps, uchar *pe)
343 {
344         Msg m;
345         ulong dt;
346
347         dt = (pkttime-starttime)/1000000LL;
348         m.p = seprint(p, e, "%6.6uld ms ", dt);
349         m.ps = ps;
350         m.pe = pe;
351         m.e = e;
352         m.pr = root;
353         while(m.p < m.e){
354                 if(!sflag)
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");
359                         m.ps = m.pe;
360                 }
361                 m.p = seprint(m.p, m.e, ")");
362                 if(m.pr == nil || m.ps >= m.pe)
363                         break;
364         }
365         *m.p++ = '\n';
366
367         if(write(1, p, m.p - p) < 0)
368                 sysfatal("stdout: %r");
369 }
370
371 Proto **xprotos;
372 int nprotos;
373
374 /* look up a protocol by its name */
375 Proto*
376 findproto(char *name)
377 {
378         int i;
379
380         for(i = 0; i < nprotos; i++)
381                 if(strcmp(xprotos[i]->name, name) == 0)
382                         return xprotos[i];
383         return nil;
384 }
385
386 /*
387  *  add an undefined protocol to protos[]
388  */
389 Proto*
390 addproto(char *name)
391 {
392         Proto *pr;
393
394         xprotos = realloc(xprotos, (nprotos+1)*sizeof(Proto*));
395         pr = malloc(sizeof *pr);
396         *pr = dump;
397         pr->name = name;
398         xprotos[nprotos++] = pr;
399         return pr;
400 }
401
402 /*
403  *  build a graph of protocols, this could easily be circular.  This
404  *  links together all the multiplexing in the protocol modules.
405  */
406 void
407 mkprotograph(void)
408 {
409         Proto **l;
410         Proto *pr;
411         Mux *m;
412
413         /* copy protos into a reallocable area */
414         for(nprotos = 0; protos[nprotos] != nil; nprotos++)
415                 ;
416         xprotos = malloc(nprotos*sizeof(Proto*));
417         memmove(xprotos, protos, nprotos*sizeof(Proto*));
418
419         for(l = protos; *l != nil; l++){
420                 pr = *l;
421                 for(m = pr->mux; m != nil && m->name != nil; m++){
422                         m->pr = findproto(m->name);
423                         if(m->pr == nil)
424                                 m->pr = addproto(m->name);
425                 }
426         }
427 }
428
429 /*
430  *  add in a protocol node
431  */
432 static Filter*
433 addnode(Filter *f, Proto *pr)
434 {
435         Filter *nf;
436         nf = newfilter();
437         nf->pr = pr;
438         nf->s = pr->name;
439         nf->l = f;
440         nf->op = WORD;
441         return nf;
442 }
443
444 /*
445  *  recurse through the protocol graph adding missing nodes
446  *  to the filter if we reach the filter's protocol
447  */
448 static Filter*
449 _fillin(Filter *f, Proto *last, int depth)
450 {
451         Mux *m;
452         Filter *nf;
453
454         if(depth-- <= 0)
455                 return nil;
456
457         for(m = last->mux; m != nil && m->name != nil; m++){
458                 if(m->pr == nil)
459                         continue;
460                 if(f->pr == m->pr)
461                         return f;
462                 nf = _fillin(f, m->pr, depth);
463                 if(nf != nil)
464                         return addnode(nf, m->pr);
465         }
466         return nil;
467 }
468
469 static Filter*
470 fillin(Filter *f, Proto *last)
471 {
472         int i;
473         Filter *nf;
474
475         /* hack to make sure top level node is the root */
476         if(last == nil){
477                 if(f->pr == root)
478                         return f;
479                 f = fillin(f, root);
480                 if(f == nil)
481                         return nil;
482                 return addnode(f, root);
483         }
484
485         /* breadth first search though the protocol graph */
486         nf = f;
487         for(i = 1; i < 20; i++){
488                 nf = _fillin(f, last, i);
489                 if(nf != nil)
490                         break;
491         }
492         return nf;
493 }
494
495 /*
496  *  massage tree so that all paths from the root to a leaf
497  *  contain a filter node for each header.
498  *
499  *  also, set f->pr where possible
500  */
501 Filter*
502 complete(Filter *f, Proto *last)
503 {
504         Proto *pr;
505
506         if(f == nil)
507                 return f;
508
509         /* do a depth first traversal of the filter tree */
510         switch(f->op){
511         case '!':
512                 f->l = complete(f->l, last);
513                 break;
514         case LAND:
515         case LOR:
516                 f->l = complete(f->l, last);
517                 f->r = complete(f->r, last);
518                 break;
519         case '=':
520                 break;
521         case WORD:
522                 pr = findproto(f->s);
523                 f->pr = pr;
524                 if(pr == nil){
525                         if(f->l != nil){
526                                 fprint(2, "%s unknown proto, ignoring params\n",
527                                         f->s);
528                                 f->l = nil;
529                         }
530                 } else {
531                         f->l = complete(f->l, pr);
532                         f = fillin(f, last);
533                         if(f == nil)
534                                 sysfatal("internal error: can't get to %s", pr->name);
535                 }
536                 break;
537         }
538         return f;
539 }
540
541 /*
542  *  merge common nodes under | and & moving the merged node
543  *  above the | or &.
544  *
545  *  do some constant foldong, e.g. `true & x' becomes x and
546  *  'true | x' becomes true.
547  */
548 static int changed;
549
550 static Filter*
551 _optimize(Filter *f)
552 {
553         Filter *l;
554
555         if(f == nil)
556                 return f;
557
558         switch(f->op){
559         case '!':
560                 /* is child also a not */
561                 if(f->l->op == '!'){
562                         changed = 1;
563                         return f->l->l;
564                 }
565                 break;
566         case LOR:
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 */
571
572                 changed = 1;
573
574                 /* constant folding */
575                 /* if either child is childless, just return that */
576                 if(f->l->l == nil)
577                         return f->l;
578                 else if(f->r->l == nil)
579                         return f->r;
580
581                 /* move the common node up, thow away one node */
582                 l = f->l;
583                 f->l = l->l;
584                 f->r = f->r->l;
585                 l->l = f;
586                 return l;
587         case LAND:
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 */
592
593                 changed = 1;
594
595                 /* constant folding */
596                 /* if either child is childless, ignore it */
597                 if(f->l->l == nil)
598                         return f->r;
599                 else if(f->r->l == nil)
600                         return f->l;
601
602                 /* move the common node up, thow away one node */
603                 l = f->l;
604                 f->l = _optimize(l->l);
605                 f->r = _optimize(f->r->l);
606                 l->l = f;
607                 return l;
608         }
609         f->l = _optimize(f->l);
610         f->r = _optimize(f->r);
611         return f;
612 }
613
614 Filter*
615 optimize(Filter *f)
616 {
617         do{
618                 changed = 0;
619                 f = _optimize(f);
620         }while(changed);
621
622         return f;
623 }
624
625 /*
626  *  find any top level nodes that aren't the root
627  */
628 int
629 findbogus(Filter *f)
630 {
631         int rv;
632
633         if(f->op != WORD){
634                 rv = findbogus(f->l);
635                 if(f->r)
636                         rv |= findbogus(f->r);
637                 return rv;
638         } else if(f->pr != root){
639                 fprint(2, "bad top-level protocol: %s\n", f->s);
640                 return 1;
641         }
642         return 0;
643 }
644
645 /*
646  *  compile the filter
647  */
648 static void
649 _compile(Filter *f, Proto *last)
650 {
651         if(f == nil)
652                 return;
653
654         switch(f->op){
655         case '!':
656                 _compile(f->l, last);
657                 break;
658         case LOR:
659         case LAND:
660                 _compile(f->l, last);
661                 _compile(f->r, last);
662                 break;
663         case WORD:
664                 if(last != nil){
665                         if(last->compile == nil)
666                                 sysfatal("unknown %s subprotocol: %s", f->pr->name, f->s);
667                         (*last->compile)(f);
668                 }
669                 if(f->l)
670                         _compile(f->l, f->pr);
671                 break;
672         case '=':
673                 if(last == nil)
674                         sysfatal("internal error: compilewalk: badly formed tree");
675                 
676                 if(last->compile == nil)
677                         sysfatal("unknown %s field: %s", f->pr->name, f->s);
678                 (*last->compile)(f);
679                 break;
680         default:
681                 sysfatal("internal error: compilewalk op: %d", f->op);
682         }
683 }
684
685 Filter*
686 compile(Filter *f)
687 {
688         if(f == nil)
689                 return f;
690
691         /* fill in the missing header filters */
692         f = complete(f, nil);
693
694         /* constant folding */
695         f = optimize(f);
696         if(!toflag)
697                 printfilter(f, "after optimize");
698
699         /* protocol specific compilations */
700         _compile(f, nil);
701
702         /* at this point, the root had better be the root proto */
703         if(findbogus(f)){
704                 fprint(2, "bogus filter\n");
705                 exits("bad filter");
706         }
707
708         return f;
709 }
710
711 /*
712  *  parse a byte array
713  */
714 int
715 parseba(uchar *to, char *from)
716 {
717         char nip[4];
718         char *p;
719         int i;
720
721         p = from;
722         for(i = 0; i < 16; i++){
723                 if(*p == 0)
724                         return -1;
725                 nip[0] = *p++;
726                 if(*p == 0)
727                         return -1;
728                 nip[1] = *p++;
729                 nip[2] = 0;
730                 to[i] = strtoul(nip, 0, 16);
731         }
732         return i;
733 }
734
735 /*
736  *  compile WORD = WORD, becomes a single node with a subop
737  */
738 void
739 compile_cmp(char *proto, Filter *f, Field *fld)
740 {
741         uchar x[IPaddrlen];
742         char *v;
743
744         if(f->op != '=')
745                 sysfatal("internal error: compile_cmp %s: not a cmp", proto);
746
747         for(; fld->name != nil; fld++){
748                 if(strcmp(f->l->s, fld->name) == 0){
749                         f->op = WORD;
750                         f->subop = fld->subop;
751                         switch(fld->ftype){
752                         case Fnum:
753                                 f->ulv = atoi(f->r->s);
754                                 break;
755                         case Fether:
756                                 v = csgetvalue(nil, "sys", (char*)f->r->s,
757                                         "ether", 0);
758                                 if(v){
759                                         parseether(f->a, v);
760                                         free(v);
761                                 } else
762                                         parseether(f->a, f->r->s);
763                                 break;
764                         case Fv4ip:
765                                 v = csgetvalue(nil, "sys", (char*)f->r->s,
766                                         "ip", 0);
767                                 if(v){
768                                         f->ulv = parseip(x, v);
769                                         free(v);
770                                 }else
771                                         f->ulv = parseip(x, f->r->s);
772                                 break;
773                         case Fv6ip:
774                                 v = csgetvalue(nil, "sys", (char*)f->r->s,
775                                         "ipv6", 0);
776                                 if(v){
777                                         parseip(f->a, v);
778                                         free(v);
779                                 }else
780                                         parseip(f->a, f->r->s);
781                                 break;
782                         case Fba:
783                                 parseba(f->a, f->r->s);
784                                 break;
785                         default:
786                                 sysfatal("internal error: compile_cmp %s: %d",
787                                         proto, fld->ftype);
788                         }
789                         f->l = f->r = nil;
790                         return;
791                 }
792         }
793         sysfatal("unknown %s field in: %s = %s", proto, f->l->s, f->r->s);
794 }
795
796 void
797 _pf(Filter *f)
798 {
799         char *s;
800
801         if(f == nil)
802                 return;
803
804         s = nil;
805         switch(f->op){
806         case '!':
807                 fprint(2, "!");
808                 _pf(f->l);
809                 break;
810         case WORD:
811                 fprint(2, "%s", f->s);
812                 if(f->l != nil){
813                         fprint(2, "(");
814                         _pf(f->l);
815                         fprint(2, ")");
816                 }
817                 break;
818         case LAND:
819                 s = "&&";
820                 goto print;
821         case LOR:
822                 s = "||";
823                 goto print;
824         case '=':
825         print:
826                 _pf(f->l);
827                 if(s)
828                         fprint(2, " %s ", s);
829                 else
830                         fprint(2, " %c ", f->op);
831                 _pf(f->r);
832                 break;
833         default:
834                 fprint(2, "???");
835                 break;
836         }
837 }
838
839 void
840 printfilter(Filter *f, char *tag)
841 {
842         fprint(2, "%s: ", tag);
843         _pf(f);
844         fprint(2, "\n");
845 }
846
847 void
848 cat(void)
849 {
850         char buf[1024];
851         int n;
852         
853         while((n = read(0, buf, sizeof buf)) > 0)
854                 write(1, buf, n);
855 }
856
857 static int fd1 = -1;
858 void
859 startmc(void)
860 {
861         int p[2];
862         
863         if(fd1 == -1)
864                 fd1 = dup(1, -1);
865         
866         if(pipe(p) < 0)
867                 return;
868         switch(fork()){
869         case -1:
870                 return;
871         default:
872                 close(p[0]);
873                 dup(p[1], 1);
874                 if(p[1] != 1)
875                         close(p[1]);
876                 return;
877         case 0:
878                 close(p[1]);
879                 dup(p[0], 0);
880                 if(p[0] != 0)
881                         close(p[0]);
882                 execl("/bin/mc", "mc", nil);
883                 cat();
884                 _exits(0);
885         }
886 }
887
888 void
889 stopmc(void)
890 {
891         close(1);
892         dup(fd1, 1);
893         waitpid();
894 }
895
896 void
897 printhelp(char *name)
898 {
899         int len;
900         Proto *pr, **l;
901         Mux *m;
902         Field *f;
903         char fmt[40];
904         
905         if(name == nil){
906                 print("protocols:\n");
907                 startmc();
908                 for(l=protos; (pr=*l) != nil; l++)
909                         print("  %s\n", pr->name);
910                 stopmc();
911                 return;
912         }
913         
914         pr = findproto(name);
915         if(pr == nil){
916                 print("unknown protocol %s\n", name);
917                 return;
918         }
919         
920         if(pr->field){
921                 print("%s's filter attributes:\n", pr->name);
922                 len = 0;
923                 for(f=pr->field; f->name; f++)
924                         if(len < strlen(f->name))
925                                 len = strlen(f->name);
926                 startmc();
927                 for(f=pr->field; f->name; f++)
928                         print("  %-*s - %s\n", len, f->name, f->help);
929                 stopmc();
930         }
931         if(pr->mux){
932                 print("%s's subprotos:\n", pr->name);
933                 startmc();
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);
937                 stopmc();
938         }
939 }
940
941 /*
942  *  demultiplex to next prototol header
943  */
944 void
945 demux(Mux *mx, ulong val1, ulong val2, Msg *m, Proto *def)
946 {
947         m->pr = def;
948         for(mx = mx; mx->name != nil; mx++){
949                 if(val1 == mx->val || val2 == mx->val){
950                         m->pr = mx->pr;
951                         break;
952                 }
953         }
954 }
955
956 /*
957  *  default framer just assumes the input packet is
958  *  a single read
959  */
960 int
961 defaultframer(int fd, uchar *pkt, int pktlen)
962 {
963         return read(fd, pkt, pktlen);
964 }