xref: /plan9-contrib/sys/src/cmd/ip/snoopy/main.c (revision fc0298df87d7acef09ee64da1c2d2ec31d02cef5)
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
printusage(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
usage(void)55 usage(void)
56 {
57 	printusage();
58 	exits("usage");
59 }
60 
61 void
main(int argc,char ** argv)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: %r", 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*
newfilter(void)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
_filterpkt(Filter * f,Msg * m)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
filterpkt(Filter * f,uchar * ps,uchar * pe,Proto * pr,int needroot)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 	ulong	ts_sec;		/* time stamp seconds */
290 	ulong	ts_µsec;	/* time stamp microseconds */
291 	ulong	caplen;		/* length of portion present */
292 	ulong	len;		/* length this packet (off wire) */
293 };
294 
295 /*
296  *  pcap trace header
297  */
298 void
pcaphdr(void)299 pcaphdr(void)
300 {
301 	struct pcap_file_header hdr;
302 
303 	hdr.magic = TCPDUMP_MAGIC;
304 	hdr.version_major = PCAP_VERSION_MAJOR;
305 	hdr.version_minor = PCAP_VERSION_MINOR;
306 
307 	hdr.thiszone = 0;
308 	hdr.snaplen = 1500;
309 	hdr.sigfigs = 0;
310 	hdr.linktype = 1;
311 
312 	write(1, &hdr, sizeof(hdr));
313 }
314 
315 /*
316  *  write out a packet trace
317  */
318 void
tracepkt(uchar * ps,int len)319 tracepkt(uchar *ps, int len)
320 {
321 	struct pcap_pkthdr *goo;
322 
323 	if(Mflag && len > Mflag)
324 		len = Mflag;
325 	if(pcap){
326 		goo = (struct pcap_pkthdr*)(ps-16);
327 		goo->ts_sec = (uvlong)pkttime / 1000000000;
328 		goo->ts_µsec = ((uvlong)pkttime % 1000000000) / 1000;
329 		goo->caplen = len;
330 		goo->len = len;
331 		write(1, goo, len+16);
332 	} else {
333 		hnputs(ps-10, len);
334 		hnputl(ps-8, pkttime>>32);
335 		hnputl(ps-4, pkttime);
336 		write(1, ps-10, len+10);
337 	}
338 }
339 
340 /*
341  *  format and print a packet
342  */
343 void
printpkt(char * p,char * e,uchar * ps,uchar * pe)344 printpkt(char *p, char *e, uchar *ps, uchar *pe)
345 {
346 	Msg m;
347 	ulong dt;
348 
349 	dt = (pkttime-starttime)/1000000LL;
350 	m.p = seprint(p, e, "%6.6uld ms ", dt);
351 	m.ps = ps;
352 	m.pe = pe;
353 	m.e = e;
354 	m.pr = root;
355 	while(m.p < m.e){
356 		if(!sflag)
357 			m.p = seprint(m.p, m.e, "\n\t");
358 		m.p = seprint(m.p, m.e, "%s(", m.pr->name);
359 		if((*m.pr->seprint)(&m) < 0){
360 			m.p = seprint(m.p, m.e, "TOO SHORT");
361 			m.ps = m.pe;
362 		}
363 		m.p = seprint(m.p, m.e, ")");
364 		if(m.pr == nil || m.ps >= m.pe)
365 			break;
366 	}
367 	*m.p++ = '\n';
368 
369 	if(write(1, p, m.p - p) < 0)
370 		sysfatal("stdout: %r");
371 }
372 
373 Proto **xprotos;
374 int nprotos;
375 
376 /* look up a protocol by its name */
377 Proto*
findproto(char * name)378 findproto(char *name)
379 {
380 	int i;
381 
382 	for(i = 0; i < nprotos; i++)
383 		if(strcmp(xprotos[i]->name, name) == 0)
384 			return xprotos[i];
385 	return nil;
386 }
387 
388 /*
389  *  add an undefined protocol to protos[]
390  */
391 Proto*
addproto(char * name)392 addproto(char *name)
393 {
394 	Proto *pr;
395 
396 	xprotos = realloc(xprotos, (nprotos+1)*sizeof(Proto*));
397 	pr = malloc(sizeof *pr);
398 	*pr = dump;
399 	pr->name = name;
400 	xprotos[nprotos++] = pr;
401 	return pr;
402 }
403 
404 /*
405  *  build a graph of protocols, this could easily be circular.  This
406  *  links together all the multiplexing in the protocol modules.
407  */
408 void
mkprotograph(void)409 mkprotograph(void)
410 {
411 	Proto **l;
412 	Proto *pr;
413 	Mux *m;
414 
415 	/* copy protos into a reallocable area */
416 	for(nprotos = 0; protos[nprotos] != nil; nprotos++)
417 		;
418 	xprotos = malloc(nprotos*sizeof(Proto*));
419 	memmove(xprotos, protos, nprotos*sizeof(Proto*));
420 
421 	for(l = protos; *l != nil; l++){
422 		pr = *l;
423 		for(m = pr->mux; m != nil && m->name != nil; m++){
424 			m->pr = findproto(m->name);
425 			if(m->pr == nil)
426 				m->pr = addproto(m->name);
427 		}
428 	}
429 }
430 
431 /*
432  *  add in a protocol node
433  */
434 static Filter*
addnode(Filter * f,Proto * pr)435 addnode(Filter *f, Proto *pr)
436 {
437 	Filter *nf;
438 	nf = newfilter();
439 	nf->pr = pr;
440 	nf->s = pr->name;
441 	nf->l = f;
442 	nf->op = WORD;
443 	return nf;
444 }
445 
446 /*
447  *  recurse through the protocol graph adding missing nodes
448  *  to the filter if we reach the filter's protocol
449  */
450 static Filter*
_fillin(Filter * f,Proto * last,int depth)451 _fillin(Filter *f, Proto *last, int depth)
452 {
453 	Mux *m;
454 	Filter *nf;
455 
456 	if(depth-- <= 0)
457 		return nil;
458 
459 	for(m = last->mux; m != nil && m->name != nil; m++){
460 		if(m->pr == nil)
461 			continue;
462 		if(f->pr == m->pr)
463 			return f;
464 		nf = _fillin(f, m->pr, depth);
465 		if(nf != nil)
466 			return addnode(nf, m->pr);
467 	}
468 	return nil;
469 }
470 
471 static Filter*
fillin(Filter * f,Proto * last)472 fillin(Filter *f, Proto *last)
473 {
474 	int i;
475 	Filter *nf;
476 
477 	/* hack to make sure top level node is the root */
478 	if(last == nil){
479 		if(f->pr == root)
480 			return f;
481 		f = fillin(f, root);
482 		if(f == nil)
483 			return nil;
484 		return addnode(f, root);
485 	}
486 
487 	/* breadth first search though the protocol graph */
488 	nf = f;
489 	for(i = 1; i < 20; i++){
490 		nf = _fillin(f, last, i);
491 		if(nf != nil)
492 			break;
493 	}
494 	return nf;
495 }
496 
497 /*
498  *  massage tree so that all paths from the root to a leaf
499  *  contain a filter node for each header.
500  *
501  *  also, set f->pr where possible
502  */
503 Filter*
complete(Filter * f,Proto * last)504 complete(Filter *f, Proto *last)
505 {
506 	Proto *pr;
507 
508 	if(f == nil)
509 		return f;
510 
511 	/* do a depth first traversal of the filter tree */
512 	switch(f->op){
513 	case '!':
514 		f->l = complete(f->l, last);
515 		break;
516 	case LAND:
517 	case LOR:
518 		f->l = complete(f->l, last);
519 		f->r = complete(f->r, last);
520 		break;
521 	case '=':
522 		break;
523 	case WORD:
524 		pr = findproto(f->s);
525 		f->pr = pr;
526 		if(pr == nil){
527 			if(f->l != nil){
528 				fprint(2, "%s unknown proto, ignoring params\n",
529 					f->s);
530 				f->l = nil;
531 			}
532 		} else {
533 			f->l = complete(f->l, pr);
534 			f = fillin(f, last);
535 			if(f == nil)
536 				sysfatal("internal error: can't get to %s", pr->name);
537 		}
538 		break;
539 	}
540 	return f;
541 }
542 
543 /*
544  *  merge common nodes under | and & moving the merged node
545  *  above the | or &.
546  *
547  *  do some constant foldong, e.g. `true & x' becomes x and
548  *  'true | x' becomes true.
549  */
550 static int changed;
551 
552 static Filter*
_optimize(Filter * f)553 _optimize(Filter *f)
554 {
555 	Filter *l;
556 
557 	if(f == nil)
558 		return f;
559 
560 	switch(f->op){
561 	case '!':
562 		/* is child also a not */
563 		if(f->l->op == '!'){
564 			changed = 1;
565 			return f->l->l;
566 		}
567 		break;
568 	case LOR:
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 */
573 
574 		changed = 1;
575 
576 		/* constant folding */
577 		/* if either child is childless, just return that */
578 		if(f->l->l == nil)
579 			return f->l;
580 		else if(f->r->l == nil)
581 			return f->r;
582 
583 		/* move the common node up, thow away one node */
584 		l = f->l;
585 		f->l = l->l;
586 		f->r = f->r->l;
587 		l->l = f;
588 		return l;
589 	case LAND:
590 		/* are two children the same protocol? */
591 		if(f->l->op != f->r->op || f->r->op != WORD
592 		|| f->l->pr != f->r->pr || f->l->pr == nil)
593 			break;	/* no optimization */
594 
595 		changed = 1;
596 
597 		/* constant folding */
598 		/* if either child is childless, ignore it */
599 		if(f->l->l == nil)
600 			return f->r;
601 		else if(f->r->l == nil)
602 			return f->l;
603 
604 		/* move the common node up, thow away one node */
605 		l = f->l;
606 		f->l = _optimize(l->l);
607 		f->r = _optimize(f->r->l);
608 		l->l = f;
609 		return l;
610 	}
611 	f->l = _optimize(f->l);
612 	f->r = _optimize(f->r);
613 	return f;
614 }
615 
616 Filter*
optimize(Filter * f)617 optimize(Filter *f)
618 {
619 	do{
620 		changed = 0;
621 		f = _optimize(f);
622 	}while(changed);
623 
624 	return f;
625 }
626 
627 /*
628  *  find any top level nodes that aren't the root
629  */
630 int
findbogus(Filter * f)631 findbogus(Filter *f)
632 {
633 	int rv;
634 
635 	if(f->op != WORD){
636 		rv = findbogus(f->l);
637 		if(f->r)
638 			rv |= findbogus(f->r);
639 		return rv;
640 	} else if(f->pr != root){
641 		fprint(2, "bad top-level protocol: %s\n", f->s);
642 		return 1;
643 	}
644 	return 0;
645 }
646 
647 /*
648  *  compile the filter
649  */
650 static void
_compile(Filter * f,Proto * last)651 _compile(Filter *f, Proto *last)
652 {
653 	if(f == nil)
654 		return;
655 
656 	switch(f->op){
657 	case '!':
658 		_compile(f->l, last);
659 		break;
660 	case LOR:
661 	case LAND:
662 		_compile(f->l, last);
663 		_compile(f->r, last);
664 		break;
665 	case WORD:
666 		if(last != nil){
667 			if(last->compile == nil)
668 				sysfatal("unknown %s subprotocol: %s", f->pr->name, f->s);
669 			(*last->compile)(f);
670 		}
671 		if(f->l)
672 			_compile(f->l, f->pr);
673 		break;
674 	case '=':
675 		if(last == nil)
676 			sysfatal("internal error: compilewalk: badly formed tree");
677 
678 		if(last->compile == nil)
679 			sysfatal("unknown %s field: %s", f->pr->name, f->s);
680 		(*last->compile)(f);
681 		break;
682 	default:
683 		sysfatal("internal error: compilewalk op: %d", f->op);
684 	}
685 }
686 
687 Filter*
compile(Filter * f)688 compile(Filter *f)
689 {
690 	if(f == nil)
691 		return f;
692 
693 	/* fill in the missing header filters */
694 	f = complete(f, nil);
695 
696 	/* constant folding */
697 	f = optimize(f);
698 	if(!toflag)
699 		printfilter(f, "after optimize");
700 
701 	/* protocol specific compilations */
702 	_compile(f, nil);
703 
704 	/* at this point, the root had better be the root proto */
705 	if(findbogus(f)){
706 		fprint(2, "bogus filter\n");
707 		exits("bad filter");
708 	}
709 
710 	return f;
711 }
712 
713 /*
714  *  parse a byte array
715  */
716 int
parseba(uchar * to,char * from)717 parseba(uchar *to, char *from)
718 {
719 	char nip[4];
720 	char *p;
721 	int i;
722 
723 	p = from;
724 	for(i = 0; i < 16; i++){
725 		if(*p == 0)
726 			return -1;
727 		nip[0] = *p++;
728 		if(*p == 0)
729 			return -1;
730 		nip[1] = *p++;
731 		nip[2] = 0;
732 		to[i] = strtoul(nip, 0, 16);
733 	}
734 	return i;
735 }
736 
737 /*
738  *  compile WORD = WORD, becomes a single node with a subop
739  */
740 void
compile_cmp(char * proto,Filter * f,Field * fld)741 compile_cmp(char *proto, Filter *f, Field *fld)
742 {
743 	uchar x[IPaddrlen];
744 	char *v;
745 
746 	if(f->op != '=')
747 		sysfatal("internal error: compile_cmp %s: not a cmp", proto);
748 
749 	for(; fld->name != nil; fld++){
750 		if(strcmp(f->l->s, fld->name) == 0){
751 			f->op = WORD;
752 			f->subop = fld->subop;
753 			switch(fld->ftype){
754 			case Fnum:
755 				f->ulv = atoi(f->r->s);
756 				break;
757 			case Fether:
758 				v = csgetvalue(nil, "sys", (char*)f->r->s,
759 					"ether", 0);
760 				if(v){
761 					parseether(f->a, v);
762 					free(v);
763 				} else
764 					parseether(f->a, f->r->s);
765 				break;
766 			case Fv4ip:
767 				v = csgetvalue(nil, "sys", (char*)f->r->s,
768 					"ip", 0);
769 				if(v){
770 					f->ulv = parseip(x, v);
771 					free(v);
772 				}else
773 					f->ulv = parseip(x, f->r->s);
774 				break;
775 			case Fv6ip:
776 				v = csgetvalue(nil, "sys", (char*)f->r->s,
777 					"ipv6", 0);
778 				if(v){
779 					parseip(f->a, v);
780 					free(v);
781 				}else
782 					parseip(f->a, f->r->s);
783 				break;
784 			case Fba:
785 				parseba(f->a, f->r->s);
786 				break;
787 			default:
788 				sysfatal("internal error: compile_cmp %s: %d",
789 					proto, fld->ftype);
790 			}
791 			f->l = f->r = nil;
792 			return;
793 		}
794 	}
795 	sysfatal("unknown %s field in: %s = %s", proto, f->l->s, f->r->s);
796 }
797 
798 void
_pf(Filter * f)799 _pf(Filter *f)
800 {
801 	char *s;
802 
803 	if(f == nil)
804 		return;
805 
806 	s = nil;
807 	switch(f->op){
808 	case '!':
809 		fprint(2, "!");
810 		_pf(f->l);
811 		break;
812 	case WORD:
813 		fprint(2, "%s", f->s);
814 		if(f->l != nil){
815 			fprint(2, "(");
816 			_pf(f->l);
817 			fprint(2, ")");
818 		}
819 		break;
820 	case LAND:
821 		s = "&&";
822 		goto print;
823 	case LOR:
824 		s = "||";
825 		goto print;
826 	case '=':
827 	print:
828 		_pf(f->l);
829 		if(s)
830 			fprint(2, " %s ", s);
831 		else
832 			fprint(2, " %c ", f->op);
833 		_pf(f->r);
834 		break;
835 	default:
836 		fprint(2, "???");
837 		break;
838 	}
839 }
840 
841 void
printfilter(Filter * f,char * tag)842 printfilter(Filter *f, char *tag)
843 {
844 	fprint(2, "%s: ", tag);
845 	_pf(f);
846 	fprint(2, "\n");
847 }
848 
849 void
cat(void)850 cat(void)
851 {
852 	char buf[1024];
853 	int n;
854 
855 	while((n = read(0, buf, sizeof buf)) > 0)
856 		write(1, buf, n);
857 }
858 
859 static int fd1 = -1;
860 void
startmc(void)861 startmc(void)
862 {
863 	int p[2];
864 
865 	if(fd1 == -1)
866 		fd1 = dup(1, -1);
867 
868 	if(pipe(p) < 0)
869 		return;
870 	switch(fork()){
871 	case -1:
872 		return;
873 	default:
874 		close(p[0]);
875 		dup(p[1], 1);
876 		if(p[1] != 1)
877 			close(p[1]);
878 		return;
879 	case 0:
880 		close(p[1]);
881 		dup(p[0], 0);
882 		if(p[0] != 0)
883 			close(p[0]);
884 		execl("/bin/mc", "mc", nil);
885 		cat();
886 		_exits(0);
887 	}
888 }
889 
890 void
stopmc(void)891 stopmc(void)
892 {
893 	close(1);
894 	dup(fd1, 1);
895 	waitpid();
896 }
897 
898 void
printhelp(char * name)899 printhelp(char *name)
900 {
901 	int len;
902 	Proto *pr, **l;
903 	Mux *m;
904 	Field *f;
905 	char fmt[40];
906 
907 	if(name == nil){
908 		print("protocols:\n");
909 		startmc();
910 		for(l=protos; (pr=*l) != nil; l++)
911 			print("  %s\n", pr->name);
912 		stopmc();
913 		return;
914 	}
915 
916 	pr = findproto(name);
917 	if(pr == nil){
918 		print("unknown protocol %s\n", name);
919 		return;
920 	}
921 
922 	if(pr->field){
923 		print("%s's filter attributes:\n", pr->name);
924 		len = 0;
925 		for(f=pr->field; f->name; f++)
926 			if(len < strlen(f->name))
927 				len = strlen(f->name);
928 		startmc();
929 		for(f=pr->field; f->name; f++)
930 			print("  %-*s - %s\n", len, f->name, f->help);
931 		stopmc();
932 	}
933 	if(pr->mux){
934 		print("%s's subprotos:\n", pr->name);
935 		startmc();
936 		snprint(fmt, sizeof fmt, "  %s %%s\n", pr->valfmt);
937 		for(m=pr->mux; m->name != nil; m++)
938 			print(fmt, m->val, m->name);
939 		stopmc();
940 	}
941 }
942 
943 /*
944  *  demultiplex to next prototol header
945  */
946 void
demux(Mux * mx,ulong val1,ulong val2,Msg * m,Proto * def)947 demux(Mux *mx, ulong val1, ulong val2, Msg *m, Proto *def)
948 {
949 	m->pr = def;
950 	for(mx = mx; mx->name != nil; mx++){
951 		if(val1 == mx->val || val2 == mx->val){
952 			m->pr = mx->pr;
953 			break;
954 		}
955 	}
956 }
957 
958 /*
959  *  default framer just assumes the input packet is
960  *  a single read
961  */
962 int
defaultframer(int fd,uchar * pkt,int pktlen)963 defaultframer(int fd, uchar *pkt, int pktlen)
964 {
965 	return read(fd, pkt, pktlen);
966 }
967