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