xref: /plan9/sys/src/cmd/ip/snoopy/main.c (revision 853458f38e7eb3a48cfa3a36aefdb799375e398a)
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         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
pcaphdr(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
tracepkt(uchar * ps,int len)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
printpkt(char * p,char * e,uchar * ps,uchar * pe)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*
findproto(char * name)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*
addproto(char * name)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
mkprotograph(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*
addnode(Filter * f,Proto * pr)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*
_fillin(Filter * f,Proto * last,int depth)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*
fillin(Filter * f,Proto * last)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*
complete(Filter * f,Proto * last)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*
_optimize(Filter * f)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*
optimize(Filter * f)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
findbogus(Filter * f)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
_compile(Filter * f,Proto * last)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*
compile(Filter * f)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
parseba(uchar * to,char * from)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
compile_cmp(char * proto,Filter * f,Field * fld)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
_pf(Filter * f)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
printfilter(Filter * f,char * tag)840 printfilter(Filter *f, char *tag)
841 {
842 	fprint(2, "%s: ", tag);
843 	_pf(f);
844 	fprint(2, "\n");
845 }
846 
847 void
cat(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
startmc(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
stopmc(void)889 stopmc(void)
890 {
891 	close(1);
892 	dup(fd1, 1);
893 	waitpid();
894 }
895 
896 void
printhelp(char * name)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
demux(Mux * mx,ulong val1,ulong val2,Msg * m,Proto * def)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
defaultframer(int fd,uchar * pkt,int pktlen)961 defaultframer(int fd, uchar *pkt, int pktlen)
962 {
963 	return read(fd, pkt, pktlen);
964 }
965