plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

main.c (16062B)


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