xref: /plan9-contrib/sys/src/9/ip/ipmux.c (revision 681a769e2eddd1ad53c76aa4a6c64957ff432c36)
10774058cSDavid du Colombier /*
20774058cSDavid du Colombier  * IP packet filter
30774058cSDavid du Colombier  */
47dd7cddfSDavid du Colombier #include "u.h"
57dd7cddfSDavid du Colombier #include "../port/lib.h"
67dd7cddfSDavid du Colombier #include "mem.h"
77dd7cddfSDavid du Colombier #include "dat.h"
87dd7cddfSDavid du Colombier #include "fns.h"
97dd7cddfSDavid du Colombier #include "../port/error.h"
107dd7cddfSDavid du Colombier 
117dd7cddfSDavid du Colombier #include "ip.h"
120774058cSDavid du Colombier #include "ipv6.h"
137dd7cddfSDavid du Colombier 
147dd7cddfSDavid du Colombier typedef struct Ipmuxrock  Ipmuxrock;
157dd7cddfSDavid du Colombier typedef struct Ipmux      Ipmux;
167dd7cddfSDavid du Colombier 
170774058cSDavid du Colombier typedef struct Myip4hdr Myip4hdr;
180774058cSDavid du Colombier struct Myip4hdr
197dd7cddfSDavid du Colombier {
207dd7cddfSDavid du Colombier 	uchar	vihl;		/* Version and header length */
217dd7cddfSDavid du Colombier 	uchar	tos;		/* Type of service */
227dd7cddfSDavid du Colombier 	uchar	length[2];	/* packet length */
237dd7cddfSDavid du Colombier 	uchar	id[2];		/* ip->identification */
247dd7cddfSDavid du Colombier 	uchar	frag[2];	/* Fragment information */
257dd7cddfSDavid du Colombier 	uchar	ttl;		/* Time to live */
267dd7cddfSDavid du Colombier 	uchar	proto;		/* Protocol */
277dd7cddfSDavid du Colombier 	uchar	cksum[2];	/* Header checksum */
287dd7cddfSDavid du Colombier 	uchar	src[4];		/* IP source */
297dd7cddfSDavid du Colombier 	uchar	dst[4];		/* IP destination */
300774058cSDavid du Colombier 
317dd7cddfSDavid du Colombier 	uchar	data[1];	/* start of data */
327dd7cddfSDavid du Colombier };
330774058cSDavid du Colombier Myip4hdr *ipoff = 0;
347dd7cddfSDavid du Colombier 
357dd7cddfSDavid du Colombier enum
367dd7cddfSDavid du Colombier {
377dd7cddfSDavid du Colombier 	Tproto,
387dd7cddfSDavid du Colombier 	Tdata,
393ff48bf5SDavid du Colombier 	Tiph,
407dd7cddfSDavid du Colombier 	Tdst,
417dd7cddfSDavid du Colombier 	Tsrc,
427dd7cddfSDavid du Colombier 	Tifc,
437dd7cddfSDavid du Colombier 
447dd7cddfSDavid du Colombier 	Cother = 0,
457dd7cddfSDavid du Colombier 	Cbyte,		/* single byte */
467dd7cddfSDavid du Colombier 	Cmbyte,		/* single byte with mask */
477dd7cddfSDavid du Colombier 	Cshort,		/* single short */
487dd7cddfSDavid du Colombier 	Cmshort,	/* single short with mask */
497dd7cddfSDavid du Colombier 	Clong,		/* single long */
507dd7cddfSDavid du Colombier 	Cmlong,		/* single long with mask */
517dd7cddfSDavid du Colombier 	Cifc,
527dd7cddfSDavid du Colombier 	Cmifc,
537dd7cddfSDavid du Colombier };
547dd7cddfSDavid du Colombier 
557dd7cddfSDavid du Colombier char *ftname[] =
567dd7cddfSDavid du Colombier {
577dd7cddfSDavid du Colombier [Tproto]	"proto",
587dd7cddfSDavid du Colombier [Tdata]		"data",
593ff48bf5SDavid du Colombier [Tiph]		"iph",
607dd7cddfSDavid du Colombier [Tdst]		"dst",
617dd7cddfSDavid du Colombier [Tsrc]		"src",
627dd7cddfSDavid du Colombier [Tifc]		"ifc",
637dd7cddfSDavid du Colombier };
647dd7cddfSDavid du Colombier 
657dd7cddfSDavid du Colombier /*
667dd7cddfSDavid du Colombier  *  a node in the decision tree
677dd7cddfSDavid du Colombier  */
687dd7cddfSDavid du Colombier struct Ipmux
697dd7cddfSDavid du Colombier {
707dd7cddfSDavid du Colombier 	Ipmux	*yes;
717dd7cddfSDavid du Colombier 	Ipmux	*no;
727dd7cddfSDavid du Colombier 	uchar	type;		/* type of field(Txxxx) */
737dd7cddfSDavid du Colombier 	uchar	ctype;		/* tupe of comparison(Cxxxx) */
747dd7cddfSDavid du Colombier 	uchar	len;		/* length in bytes of item to compare */
757dd7cddfSDavid du Colombier 	uchar	n;		/* number of items val points to */
767dd7cddfSDavid du Colombier 	short	off;		/* offset of comparison */
777dd7cddfSDavid du Colombier 	short	eoff;		/* end offset of comparison */
783ff48bf5SDavid du Colombier 	uchar	skiphdr;	/* should offset start after ipheader */
797dd7cddfSDavid du Colombier 	uchar	*val;
807dd7cddfSDavid du Colombier 	uchar	*mask;
817dd7cddfSDavid du Colombier 	uchar	*e;		/* val+n*len*/
827dd7cddfSDavid du Colombier 
837dd7cddfSDavid du Colombier 	int	ref;		/* so we can garbage collect */
847dd7cddfSDavid du Colombier 	Conv	*conv;
857dd7cddfSDavid du Colombier };
867dd7cddfSDavid du Colombier 
877dd7cddfSDavid du Colombier /*
887dd7cddfSDavid du Colombier  *  someplace to hold per conversation data
897dd7cddfSDavid du Colombier  */
907dd7cddfSDavid du Colombier struct Ipmuxrock
917dd7cddfSDavid du Colombier {
927dd7cddfSDavid du Colombier 	Ipmux	*chain;
937dd7cddfSDavid du Colombier };
947dd7cddfSDavid du Colombier 
957dd7cddfSDavid du Colombier static int	ipmuxsprint(Ipmux*, int, char*, int);
963ff48bf5SDavid du Colombier static void	ipmuxkick(void *x);
977dd7cddfSDavid du Colombier 
987dd7cddfSDavid du Colombier static char*
skipwhite(char * p)997dd7cddfSDavid du Colombier skipwhite(char *p)
1007dd7cddfSDavid du Colombier {
1017dd7cddfSDavid du Colombier 	while(*p == ' ' || *p == '\t')
1027dd7cddfSDavid du Colombier 		p++;
1037dd7cddfSDavid du Colombier 	return p;
1047dd7cddfSDavid du Colombier }
1057dd7cddfSDavid du Colombier 
1067dd7cddfSDavid du Colombier static char*
follows(char * p,char c)1077dd7cddfSDavid du Colombier follows(char *p, char c)
1087dd7cddfSDavid du Colombier {
1097dd7cddfSDavid du Colombier 	char *f;
1107dd7cddfSDavid du Colombier 
1117dd7cddfSDavid du Colombier 	f = strchr(p, c);
1127dd7cddfSDavid du Colombier 	if(f == nil)
1137dd7cddfSDavid du Colombier 		return nil;
1147dd7cddfSDavid du Colombier 	*f++ = 0;
1157dd7cddfSDavid du Colombier 	f = skipwhite(f);
1167dd7cddfSDavid du Colombier 	if(*f == 0)
1177dd7cddfSDavid du Colombier 		return nil;
1187dd7cddfSDavid du Colombier 	return f;
1197dd7cddfSDavid du Colombier }
1207dd7cddfSDavid du Colombier 
1217dd7cddfSDavid du Colombier static Ipmux*
parseop(char ** pp)1227dd7cddfSDavid du Colombier parseop(char **pp)
1237dd7cddfSDavid du Colombier {
1247dd7cddfSDavid du Colombier 	char *p = *pp;
1257dd7cddfSDavid du Colombier 	int type, off, end, len;
1267dd7cddfSDavid du Colombier 	Ipmux *f;
1277dd7cddfSDavid du Colombier 
1287dd7cddfSDavid du Colombier 	p = skipwhite(p);
1297dd7cddfSDavid du Colombier 	if(strncmp(p, "dst", 3) == 0){
1307dd7cddfSDavid du Colombier 		type = Tdst;
1317dd7cddfSDavid du Colombier 		off = (ulong)(ipoff->dst);
1327dd7cddfSDavid du Colombier 		len = IPv4addrlen;
1337dd7cddfSDavid du Colombier 		p += 3;
1347dd7cddfSDavid du Colombier 	}
1357dd7cddfSDavid du Colombier 	else if(strncmp(p, "src", 3) == 0){
1367dd7cddfSDavid du Colombier 		type = Tsrc;
1377dd7cddfSDavid du Colombier 		off = (ulong)(ipoff->src);
1387dd7cddfSDavid du Colombier 		len = IPv4addrlen;
1397dd7cddfSDavid du Colombier 		p += 3;
1407dd7cddfSDavid du Colombier 	}
1417dd7cddfSDavid du Colombier 	else if(strncmp(p, "ifc", 3) == 0){
1427dd7cddfSDavid du Colombier 		type = Tifc;
1437dd7cddfSDavid du Colombier 		off = -IPv4addrlen;
1447dd7cddfSDavid du Colombier 		len = IPv4addrlen;
1457dd7cddfSDavid du Colombier 		p += 3;
1467dd7cddfSDavid du Colombier 	}
1477dd7cddfSDavid du Colombier 	else if(strncmp(p, "proto", 5) == 0){
1487dd7cddfSDavid du Colombier 		type = Tproto;
1497dd7cddfSDavid du Colombier 		off = (ulong)&(ipoff->proto);
1507dd7cddfSDavid du Colombier 		len = 1;
1517dd7cddfSDavid du Colombier 		p += 5;
1527dd7cddfSDavid du Colombier 	}
1533ff48bf5SDavid du Colombier 	else if(strncmp(p, "data", 4) == 0 || strncmp(p, "iph", 3) == 0){
1543ff48bf5SDavid du Colombier 		if(strncmp(p, "data", 4) == 0) {
1557dd7cddfSDavid du Colombier 			type = Tdata;
1567dd7cddfSDavid du Colombier 			p += 4;
1573ff48bf5SDavid du Colombier 		}
1583ff48bf5SDavid du Colombier 		else {
1593ff48bf5SDavid du Colombier 			type = Tiph;
1603ff48bf5SDavid du Colombier 			p += 3;
1613ff48bf5SDavid du Colombier 		}
1627dd7cddfSDavid du Colombier 		p = skipwhite(p);
1637dd7cddfSDavid du Colombier 		if(*p != '[')
1647dd7cddfSDavid du Colombier 			return nil;
1657dd7cddfSDavid du Colombier 		p++;
1667dd7cddfSDavid du Colombier 		off = strtoul(p, &p, 0);
1670774058cSDavid du Colombier 		if(off < 0 || off > (64-IP4HDR))
1687dd7cddfSDavid du Colombier 			return nil;
1697dd7cddfSDavid du Colombier 		p = skipwhite(p);
1707dd7cddfSDavid du Colombier 		if(*p != ':')
1717dd7cddfSDavid du Colombier 			end = off;
1727dd7cddfSDavid du Colombier 		else {
1737dd7cddfSDavid du Colombier 			p++;
1747dd7cddfSDavid du Colombier 			p = skipwhite(p);
1757dd7cddfSDavid du Colombier 			end = strtoul(p, &p, 0);
1767dd7cddfSDavid du Colombier 			if(end < off)
1777dd7cddfSDavid du Colombier 				return nil;
1787dd7cddfSDavid du Colombier 			p = skipwhite(p);
1797dd7cddfSDavid du Colombier 		}
1807dd7cddfSDavid du Colombier 		if(*p != ']')
1817dd7cddfSDavid du Colombier 			return nil;
1827dd7cddfSDavid du Colombier 		p++;
1837dd7cddfSDavid du Colombier 		len = end - off + 1;
1847dd7cddfSDavid du Colombier 	}
1857dd7cddfSDavid du Colombier 	else
1867dd7cddfSDavid du Colombier 		return nil;
1877dd7cddfSDavid du Colombier 
1887dd7cddfSDavid du Colombier 	f = smalloc(sizeof(*f));
1897dd7cddfSDavid du Colombier 	f->type = type;
1907dd7cddfSDavid du Colombier 	f->len = len;
1917dd7cddfSDavid du Colombier 	f->off = off;
1927dd7cddfSDavid du Colombier 	f->val = nil;
1937dd7cddfSDavid du Colombier 	f->mask = nil;
1947dd7cddfSDavid du Colombier 	f->n = 1;
1957dd7cddfSDavid du Colombier 	f->ref = 1;
1963ff48bf5SDavid du Colombier 	if(type == Tdata)
1973ff48bf5SDavid du Colombier 		f->skiphdr = 1;
1983ff48bf5SDavid du Colombier 	else
1993ff48bf5SDavid du Colombier 		f->skiphdr = 0;
2007dd7cddfSDavid du Colombier 
2017dd7cddfSDavid du Colombier 	return f;
2027dd7cddfSDavid du Colombier }
2037dd7cddfSDavid du Colombier 
2047dd7cddfSDavid du Colombier static int
htoi(char x)2057dd7cddfSDavid du Colombier htoi(char x)
2067dd7cddfSDavid du Colombier {
2077dd7cddfSDavid du Colombier 	if(x >= '0' && x <= '9')
2087dd7cddfSDavid du Colombier 		x -= '0';
2097dd7cddfSDavid du Colombier 	else if(x >= 'a' && x <= 'f')
2107dd7cddfSDavid du Colombier 		x -= 'a' - 10;
2117dd7cddfSDavid du Colombier 	else if(x >= 'A' && x <= 'F')
2127dd7cddfSDavid du Colombier 		x -= 'A' - 10;
2137dd7cddfSDavid du Colombier 	else
2147dd7cddfSDavid du Colombier 		x = 0;
2157dd7cddfSDavid du Colombier 	return x;
2167dd7cddfSDavid du Colombier }
2177dd7cddfSDavid du Colombier 
2187dd7cddfSDavid du Colombier static int
hextoi(char * p)2197dd7cddfSDavid du Colombier hextoi(char *p)
2207dd7cddfSDavid du Colombier {
2217dd7cddfSDavid du Colombier 	return (htoi(p[0])<<4) | htoi(p[1]);
2227dd7cddfSDavid du Colombier }
2237dd7cddfSDavid du Colombier 
2247dd7cddfSDavid du Colombier static void
parseval(uchar * v,char * p,int len)2257dd7cddfSDavid du Colombier parseval(uchar *v, char *p, int len)
2267dd7cddfSDavid du Colombier {
2277dd7cddfSDavid du Colombier 	while(*p && len-- > 0){
2287dd7cddfSDavid du Colombier 		*v++ = hextoi(p);
2297dd7cddfSDavid du Colombier 		p += 2;
2307dd7cddfSDavid du Colombier 	}
2317dd7cddfSDavid du Colombier }
2327dd7cddfSDavid du Colombier 
2337dd7cddfSDavid du Colombier static Ipmux*
parsemux(char * p)2347dd7cddfSDavid du Colombier parsemux(char *p)
2357dd7cddfSDavid du Colombier {
2367dd7cddfSDavid du Colombier 	int n, nomask;
2377dd7cddfSDavid du Colombier 	Ipmux *f;
2387dd7cddfSDavid du Colombier 	char *val;
2397dd7cddfSDavid du Colombier 	char *mask;
2407dd7cddfSDavid du Colombier 	char *vals[20];
2417dd7cddfSDavid du Colombier 	uchar *v;
2427dd7cddfSDavid du Colombier 
2437dd7cddfSDavid du Colombier 	/* parse operand */
2447dd7cddfSDavid du Colombier 	f = parseop(&p);
2457dd7cddfSDavid du Colombier 	if(f == nil)
2467dd7cddfSDavid du Colombier 		return nil;
2477dd7cddfSDavid du Colombier 
2487dd7cddfSDavid du Colombier 	/* find value */
2497dd7cddfSDavid du Colombier 	val = follows(p, '=');
2507dd7cddfSDavid du Colombier 	if(val == nil)
2517dd7cddfSDavid du Colombier 		goto parseerror;
2527dd7cddfSDavid du Colombier 
2537dd7cddfSDavid du Colombier 	/* parse mask */
2547dd7cddfSDavid du Colombier 	mask = follows(p, '&');
2557dd7cddfSDavid du Colombier 	if(mask != nil){
2567dd7cddfSDavid du Colombier 		switch(f->type){
2577dd7cddfSDavid du Colombier 		case Tsrc:
2587dd7cddfSDavid du Colombier 		case Tdst:
2597dd7cddfSDavid du Colombier 		case Tifc:
2607dd7cddfSDavid du Colombier 			f->mask = smalloc(f->len);
2617dd7cddfSDavid du Colombier 			v4parseip(f->mask, mask);
2627dd7cddfSDavid du Colombier 			break;
2637dd7cddfSDavid du Colombier 		case Tdata:
2643ff48bf5SDavid du Colombier 		case Tiph:
2657dd7cddfSDavid du Colombier 			f->mask = smalloc(f->len);
2667dd7cddfSDavid du Colombier 			parseval(f->mask, mask, f->len);
2677dd7cddfSDavid du Colombier 			break;
2687dd7cddfSDavid du Colombier 		default:
2697dd7cddfSDavid du Colombier 			goto parseerror;
2707dd7cddfSDavid du Colombier 		}
2717dd7cddfSDavid du Colombier 		nomask = 0;
2727dd7cddfSDavid du Colombier 	} else {
2737dd7cddfSDavid du Colombier 		nomask = 1;
2747dd7cddfSDavid du Colombier 		f->mask = smalloc(f->len);
2757dd7cddfSDavid du Colombier 		memset(f->mask, 0xff, f->len);
2767dd7cddfSDavid du Colombier 	}
2777dd7cddfSDavid du Colombier 
2787dd7cddfSDavid du Colombier 	/* parse vals */
2797dd7cddfSDavid du Colombier 	f->n = getfields(val, vals, sizeof(vals)/sizeof(char*), 1, "|");
2807dd7cddfSDavid du Colombier 	if(f->n == 0)
2817dd7cddfSDavid du Colombier 		goto parseerror;
2827dd7cddfSDavid du Colombier 	f->val = smalloc(f->n*f->len);
2837dd7cddfSDavid du Colombier 	v = f->val;
2847dd7cddfSDavid du Colombier 	for(n = 0; n < f->n; n++){
2857dd7cddfSDavid du Colombier 		switch(f->type){
2867dd7cddfSDavid du Colombier 		case Tsrc:
2877dd7cddfSDavid du Colombier 		case Tdst:
2887dd7cddfSDavid du Colombier 		case Tifc:
2897dd7cddfSDavid du Colombier 			v4parseip(v, vals[n]);
2907dd7cddfSDavid du Colombier 			break;
2917dd7cddfSDavid du Colombier 		case Tproto:
2927dd7cddfSDavid du Colombier 		case Tdata:
2933ff48bf5SDavid du Colombier 		case Tiph:
2947dd7cddfSDavid du Colombier 			parseval(v, vals[n], f->len);
2957dd7cddfSDavid du Colombier 			break;
2967dd7cddfSDavid du Colombier 		}
2977dd7cddfSDavid du Colombier 		v += f->len;
2987dd7cddfSDavid du Colombier 	}
2997dd7cddfSDavid du Colombier 
3007dd7cddfSDavid du Colombier 	f->eoff = f->off + f->len;
3017dd7cddfSDavid du Colombier 	f->e = f->val + f->n*f->len;
3027dd7cddfSDavid du Colombier 	f->ctype = Cother;
3037dd7cddfSDavid du Colombier 	if(f->n == 1){
3047dd7cddfSDavid du Colombier 		switch(f->len){
3057dd7cddfSDavid du Colombier 		case 1:
3067dd7cddfSDavid du Colombier 			f->ctype = nomask ? Cbyte : Cmbyte;
3077dd7cddfSDavid du Colombier 			break;
3087dd7cddfSDavid du Colombier 		case 2:
3097dd7cddfSDavid du Colombier 			f->ctype = nomask ? Cshort : Cmshort;
3107dd7cddfSDavid du Colombier 			break;
3117dd7cddfSDavid du Colombier 		case 4:
3127dd7cddfSDavid du Colombier 			if(f->type == Tifc)
3137dd7cddfSDavid du Colombier 				f->ctype = nomask ? Cifc : Cmifc;
3147dd7cddfSDavid du Colombier 			else
3157dd7cddfSDavid du Colombier 				f->ctype = nomask ? Clong : Cmlong;
3167dd7cddfSDavid du Colombier 			break;
3177dd7cddfSDavid du Colombier 		}
3187dd7cddfSDavid du Colombier 	}
3197dd7cddfSDavid du Colombier 	return f;
3207dd7cddfSDavid du Colombier 
3217dd7cddfSDavid du Colombier parseerror:
3227dd7cddfSDavid du Colombier 	if(f->mask)
3237dd7cddfSDavid du Colombier 		free(f->mask);
3247dd7cddfSDavid du Colombier 	if(f->val)
3257dd7cddfSDavid du Colombier 		free(f->val);
3267dd7cddfSDavid du Colombier 	free(f);
3277dd7cddfSDavid du Colombier 	return nil;
3287dd7cddfSDavid du Colombier }
3297dd7cddfSDavid du Colombier 
3307dd7cddfSDavid du Colombier /*
3317dd7cddfSDavid du Colombier  *  Compare relative ordering of two ipmuxs.  This doesn't compare the
3327dd7cddfSDavid du Colombier  *  values, just the fields being looked at.
3337dd7cddfSDavid du Colombier  *
3347dd7cddfSDavid du Colombier  *  returns:	<0 if a is a more specific match
3357dd7cddfSDavid du Colombier  *		 0 if a and b are matching on the same fields
3367dd7cddfSDavid du Colombier  *		>0 if b is a more specific match
3377dd7cddfSDavid du Colombier  */
3387dd7cddfSDavid du Colombier static int
ipmuxcmp(Ipmux * a,Ipmux * b)3397dd7cddfSDavid du Colombier ipmuxcmp(Ipmux *a, Ipmux *b)
3407dd7cddfSDavid du Colombier {
3417dd7cddfSDavid du Colombier 	int n;
3427dd7cddfSDavid du Colombier 
3437dd7cddfSDavid du Colombier 	/* compare types, lesser ones are more important */
3447dd7cddfSDavid du Colombier 	n = a->type - b->type;
3457dd7cddfSDavid du Colombier 	if(n != 0)
3467dd7cddfSDavid du Colombier 		return n;
3477dd7cddfSDavid du Colombier 
3487dd7cddfSDavid du Colombier 	/* compare offsets, call earlier ones more specific */
3493ff48bf5SDavid du Colombier 	n = (a->off+((int)a->skiphdr)*(ulong)ipoff->data) -
3503ff48bf5SDavid du Colombier 		(b->off+((int)b->skiphdr)*(ulong)ipoff->data);
3517dd7cddfSDavid du Colombier 	if(n != 0)
3527dd7cddfSDavid du Colombier 		return n;
3537dd7cddfSDavid du Colombier 
3547dd7cddfSDavid du Colombier 	/* compare match lengths, longer ones are more specific */
3557dd7cddfSDavid du Colombier 	n = b->len - a->len;
3567dd7cddfSDavid du Colombier 	if(n != 0)
3577dd7cddfSDavid du Colombier 		return n;
3587dd7cddfSDavid du Colombier 
3597dd7cddfSDavid du Colombier 	/*
3607dd7cddfSDavid du Colombier 	 *  if we get here we have two entries matching
3617dd7cddfSDavid du Colombier 	 *  the same bytes of the record.  Now check
3627dd7cddfSDavid du Colombier 	 *  the mask for equality.  Longer masks are
3637dd7cddfSDavid du Colombier 	 *  more specific.
3647dd7cddfSDavid du Colombier 	 */
3657dd7cddfSDavid du Colombier 	if(a->mask != nil && b->mask == nil)
3667dd7cddfSDavid du Colombier 		return -1;
3677dd7cddfSDavid du Colombier 	if(a->mask == nil && b->mask != nil)
3687dd7cddfSDavid du Colombier 		return 1;
3697dd7cddfSDavid du Colombier 	if(a->mask != nil && b->mask != nil){
3707dd7cddfSDavid du Colombier 		n = memcmp(b->mask, a->mask, a->len);
3717dd7cddfSDavid du Colombier 		if(n != 0)
3727dd7cddfSDavid du Colombier 			return n;
3737dd7cddfSDavid du Colombier 	}
3747dd7cddfSDavid du Colombier 	return 0;
3757dd7cddfSDavid du Colombier }
3767dd7cddfSDavid du Colombier 
3777dd7cddfSDavid du Colombier /*
3787dd7cddfSDavid du Colombier  *  Compare the values of two ipmuxs.  We're assuming that ipmuxcmp
3797dd7cddfSDavid du Colombier  *  returned 0 comparing them.
3807dd7cddfSDavid du Colombier  */
3817dd7cddfSDavid du Colombier static int
ipmuxvalcmp(Ipmux * a,Ipmux * b)3827dd7cddfSDavid du Colombier ipmuxvalcmp(Ipmux *a, Ipmux *b)
3837dd7cddfSDavid du Colombier {
3847dd7cddfSDavid du Colombier 	int n;
3857dd7cddfSDavid du Colombier 
3867dd7cddfSDavid du Colombier 	n = b->len*b->n - a->len*a->n;
3877dd7cddfSDavid du Colombier 	if(n != 0)
3887dd7cddfSDavid du Colombier 		return n;
3897dd7cddfSDavid du Colombier 	return memcmp(a->val, b->val, a->len*a->n);
3907dd7cddfSDavid du Colombier }
3917dd7cddfSDavid du Colombier 
3927dd7cddfSDavid du Colombier /*
3937dd7cddfSDavid du Colombier  *  add onto an existing ipmux chain in the canonical comparison
3947dd7cddfSDavid du Colombier  *  order
3957dd7cddfSDavid du Colombier  */
3967dd7cddfSDavid du Colombier static void
ipmuxchain(Ipmux ** l,Ipmux * f)3977dd7cddfSDavid du Colombier ipmuxchain(Ipmux **l, Ipmux *f)
3987dd7cddfSDavid du Colombier {
3997dd7cddfSDavid du Colombier 	for(; *l; l = &(*l)->yes)
4007dd7cddfSDavid du Colombier 		if(ipmuxcmp(f, *l) < 0)
4017dd7cddfSDavid du Colombier 			break;
4027dd7cddfSDavid du Colombier 	f->yes = *l;
4037dd7cddfSDavid du Colombier 	*l = f;
4047dd7cddfSDavid du Colombier }
4057dd7cddfSDavid du Colombier 
4067dd7cddfSDavid du Colombier /*
4077dd7cddfSDavid du Colombier  *  copy a tree
4087dd7cddfSDavid du Colombier  */
4097dd7cddfSDavid du Colombier static Ipmux*
ipmuxcopy(Ipmux * f)4107dd7cddfSDavid du Colombier ipmuxcopy(Ipmux *f)
4117dd7cddfSDavid du Colombier {
4127dd7cddfSDavid du Colombier 	Ipmux *nf;
4137dd7cddfSDavid du Colombier 
4147dd7cddfSDavid du Colombier 	if(f == nil)
4157dd7cddfSDavid du Colombier 		return nil;
4167dd7cddfSDavid du Colombier 	nf = smalloc(sizeof *nf);
4177dd7cddfSDavid du Colombier 	*nf = *f;
4187dd7cddfSDavid du Colombier 	nf->no = ipmuxcopy(f->no);
4197dd7cddfSDavid du Colombier 	nf->yes = ipmuxcopy(f->yes);
4207dd7cddfSDavid du Colombier 	nf->val = smalloc(f->n*f->len);
4217dd7cddfSDavid du Colombier 	nf->e = nf->val + f->len*f->n;
4227dd7cddfSDavid du Colombier 	memmove(nf->val, f->val, f->n*f->len);
4237dd7cddfSDavid du Colombier 	return nf;
4247dd7cddfSDavid du Colombier }
4257dd7cddfSDavid du Colombier 
4267dd7cddfSDavid du Colombier static void
ipmuxfree(Ipmux * f)4277dd7cddfSDavid du Colombier ipmuxfree(Ipmux *f)
4287dd7cddfSDavid du Colombier {
4297dd7cddfSDavid du Colombier 	if(f->val != nil)
4307dd7cddfSDavid du Colombier 		free(f->val);
4317dd7cddfSDavid du Colombier 	free(f);
4327dd7cddfSDavid du Colombier }
4337dd7cddfSDavid du Colombier 
4347dd7cddfSDavid du Colombier static void
ipmuxtreefree(Ipmux * f)4357dd7cddfSDavid du Colombier ipmuxtreefree(Ipmux *f)
4367dd7cddfSDavid du Colombier {
4377dd7cddfSDavid du Colombier 	if(f == nil)
4387dd7cddfSDavid du Colombier 		return;
4397dd7cddfSDavid du Colombier 	if(f->no != nil)
4407dd7cddfSDavid du Colombier 		ipmuxfree(f->no);
4417dd7cddfSDavid du Colombier 	if(f->yes != nil)
4427dd7cddfSDavid du Colombier 		ipmuxfree(f->yes);
4437dd7cddfSDavid du Colombier 	ipmuxfree(f);
4447dd7cddfSDavid du Colombier }
4457dd7cddfSDavid du Colombier 
4467dd7cddfSDavid du Colombier /*
4477dd7cddfSDavid du Colombier  *  merge two trees
4487dd7cddfSDavid du Colombier  */
4497dd7cddfSDavid du Colombier static Ipmux*
ipmuxmerge(Ipmux * a,Ipmux * b)4507dd7cddfSDavid du Colombier ipmuxmerge(Ipmux *a, Ipmux *b)
4517dd7cddfSDavid du Colombier {
4527dd7cddfSDavid du Colombier 	int n;
4537dd7cddfSDavid du Colombier 	Ipmux *f;
4547dd7cddfSDavid du Colombier 
4557dd7cddfSDavid du Colombier 	if(a == nil)
4567dd7cddfSDavid du Colombier 		return b;
4577dd7cddfSDavid du Colombier 	if(b == nil)
4587dd7cddfSDavid du Colombier 		return a;
4597dd7cddfSDavid du Colombier 	n = ipmuxcmp(a, b);
4607dd7cddfSDavid du Colombier 	if(n < 0){
4617dd7cddfSDavid du Colombier 		f = ipmuxcopy(b);
4627dd7cddfSDavid du Colombier 		a->yes = ipmuxmerge(a->yes, b);
4637dd7cddfSDavid du Colombier 		a->no = ipmuxmerge(a->no, f);
4647dd7cddfSDavid du Colombier 		return a;
4657dd7cddfSDavid du Colombier 	}
4667dd7cddfSDavid du Colombier 	if(n > 0){
4677dd7cddfSDavid du Colombier 		f = ipmuxcopy(a);
4687dd7cddfSDavid du Colombier 		b->yes = ipmuxmerge(b->yes, a);
4697dd7cddfSDavid du Colombier 		b->no = ipmuxmerge(b->no, f);
4707dd7cddfSDavid du Colombier 		return b;
4717dd7cddfSDavid du Colombier 	}
4727dd7cddfSDavid du Colombier 	if(ipmuxvalcmp(a, b) == 0){
4737dd7cddfSDavid du Colombier 		a->yes = ipmuxmerge(a->yes, b->yes);
4747dd7cddfSDavid du Colombier 		a->no = ipmuxmerge(a->no, b->no);
4757dd7cddfSDavid du Colombier 		a->ref++;
4767dd7cddfSDavid du Colombier 		ipmuxfree(b);
4777dd7cddfSDavid du Colombier 		return a;
4787dd7cddfSDavid du Colombier 	}
4797dd7cddfSDavid du Colombier 	a->no = ipmuxmerge(a->no, b);
4807dd7cddfSDavid du Colombier 	return a;
4817dd7cddfSDavid du Colombier }
4827dd7cddfSDavid du Colombier 
4837dd7cddfSDavid du Colombier /*
4847dd7cddfSDavid du Colombier  *  remove a chain from a demux tree.  This is like merging accept that
4857dd7cddfSDavid du Colombier  *  we remove instead of insert.
4867dd7cddfSDavid du Colombier  */
4877dd7cddfSDavid du Colombier static int
ipmuxremove(Ipmux ** l,Ipmux * f)4887dd7cddfSDavid du Colombier ipmuxremove(Ipmux **l, Ipmux *f)
4897dd7cddfSDavid du Colombier {
4907dd7cddfSDavid du Colombier 	int n, rv;
4917dd7cddfSDavid du Colombier 	Ipmux *ft;
4927dd7cddfSDavid du Colombier 
4937dd7cddfSDavid du Colombier 	if(f == nil)
4947dd7cddfSDavid du Colombier 		return 0;		/* we've removed it all */
4957dd7cddfSDavid du Colombier 	if(*l == nil)
4967dd7cddfSDavid du Colombier 		return -1;
4977dd7cddfSDavid du Colombier 
4987dd7cddfSDavid du Colombier 	ft = *l;
4997dd7cddfSDavid du Colombier 	n = ipmuxcmp(ft, f);
5007dd7cddfSDavid du Colombier 	if(n < 0){
5017dd7cddfSDavid du Colombier 		/* *l is maching an earlier field, descend both paths */
5027dd7cddfSDavid du Colombier 		rv = ipmuxremove(&ft->yes, f);
5037dd7cddfSDavid du Colombier 		rv += ipmuxremove(&ft->no, f);
5047dd7cddfSDavid du Colombier 		return rv;
5057dd7cddfSDavid du Colombier 	}
5067dd7cddfSDavid du Colombier 	if(n > 0){
5077dd7cddfSDavid du Colombier 		/* f represents an earlier field than *l, this should be impossible */
5087dd7cddfSDavid du Colombier 		return -1;
5097dd7cddfSDavid du Colombier 	}
5107dd7cddfSDavid du Colombier 
5117dd7cddfSDavid du Colombier 	/* if we get here f and *l are comparing the same fields */
5127dd7cddfSDavid du Colombier 	if(ipmuxvalcmp(ft, f) != 0){
5137dd7cddfSDavid du Colombier 		/* different values mean mutually exclusive */
5147dd7cddfSDavid du Colombier 		return ipmuxremove(&ft->no, f);
5157dd7cddfSDavid du Colombier 	}
5167dd7cddfSDavid du Colombier 
5177dd7cddfSDavid du Colombier 	/* we found a match */
5187dd7cddfSDavid du Colombier 	if(--(ft->ref) == 0){
5197dd7cddfSDavid du Colombier 		/*
5207dd7cddfSDavid du Colombier 		 *  a dead node implies the whole yes side is also dead.
5217dd7cddfSDavid du Colombier 		 *  since our chain is constrained to be on that side,
5227dd7cddfSDavid du Colombier 		 *  we're done.
5237dd7cddfSDavid du Colombier 		 */
5247dd7cddfSDavid du Colombier 		ipmuxtreefree(ft->yes);
5257dd7cddfSDavid du Colombier 		*l = ft->no;
5267dd7cddfSDavid du Colombier 		ipmuxfree(ft);
5277dd7cddfSDavid du Colombier 		return 0;
5287dd7cddfSDavid du Colombier 	}
5297dd7cddfSDavid du Colombier 
5307dd7cddfSDavid du Colombier 	/*
5317dd7cddfSDavid du Colombier 	 *  free the rest of the chain.  it is constrained to match the
5327dd7cddfSDavid du Colombier 	 *  yes side.
5337dd7cddfSDavid du Colombier 	 */
5347dd7cddfSDavid du Colombier 	return ipmuxremove(&ft->yes, f->yes);
5357dd7cddfSDavid du Colombier }
5367dd7cddfSDavid du Colombier 
5377dd7cddfSDavid du Colombier /*
5387dd7cddfSDavid du Colombier  *  connection request is a semi separated list of filters
539d36a2583SDavid du Colombier  *  e.g. proto=17;data[0:4]=11aa22bb;ifc=135.104.9.2&255.255.255.0
5407dd7cddfSDavid du Colombier  *
5417dd7cddfSDavid du Colombier  *  there's no protection against overlapping specs.
5427dd7cddfSDavid du Colombier  */
5437dd7cddfSDavid du Colombier static char*
ipmuxconnect(Conv * c,char ** argv,int argc)5447dd7cddfSDavid du Colombier ipmuxconnect(Conv *c, char **argv, int argc)
5457dd7cddfSDavid du Colombier {
5467dd7cddfSDavid du Colombier 	int i, n;
5477dd7cddfSDavid du Colombier 	char *field[10];
5487dd7cddfSDavid du Colombier 	Ipmux *mux, *chain;
5497dd7cddfSDavid du Colombier 	Ipmuxrock *r;
5507dd7cddfSDavid du Colombier 	Fs *f;
5517dd7cddfSDavid du Colombier 
5527dd7cddfSDavid du Colombier 	f = c->p->f;
5537dd7cddfSDavid du Colombier 
5547dd7cddfSDavid du Colombier 	if(argc != 2)
5557dd7cddfSDavid du Colombier 		return Ebadarg;
5567dd7cddfSDavid du Colombier 
5577dd7cddfSDavid du Colombier 	n = getfields(argv[1], field, nelem(field), 1, ";");
5587dd7cddfSDavid du Colombier 	if(n <= 0)
5597dd7cddfSDavid du Colombier 		return Ebadarg;
5607dd7cddfSDavid du Colombier 
5617dd7cddfSDavid du Colombier 	chain = nil;
5627dd7cddfSDavid du Colombier 	mux = nil;
5637dd7cddfSDavid du Colombier 	for(i = 0; i < n; i++){
5647dd7cddfSDavid du Colombier 		mux = parsemux(field[i]);
5657dd7cddfSDavid du Colombier 		if(mux == nil){
5667dd7cddfSDavid du Colombier 			ipmuxtreefree(chain);
5677dd7cddfSDavid du Colombier 			return Ebadarg;
5687dd7cddfSDavid du Colombier 		}
5697dd7cddfSDavid du Colombier 		ipmuxchain(&chain, mux);
5707dd7cddfSDavid du Colombier 	}
5717dd7cddfSDavid du Colombier 	if(chain == nil)
5727dd7cddfSDavid du Colombier 		return Ebadarg;
5737dd7cddfSDavid du Colombier 	mux->conv = c;
5747dd7cddfSDavid du Colombier 
5757dd7cddfSDavid du Colombier 	/* save a copy of the chain so we can later remove it */
5767dd7cddfSDavid du Colombier 	mux = ipmuxcopy(chain);
5777dd7cddfSDavid du Colombier 	r = (Ipmuxrock*)(c->ptcl);
5787dd7cddfSDavid du Colombier 	r->chain = chain;
5797dd7cddfSDavid du Colombier 
5807dd7cddfSDavid du Colombier 	/* add the chain to the protocol demultiplexor tree */
5817dd7cddfSDavid du Colombier 	wlock(f);
5827dd7cddfSDavid du Colombier 	f->ipmux->priv = ipmuxmerge(f->ipmux->priv, mux);
5837dd7cddfSDavid du Colombier 	wunlock(f);
5847dd7cddfSDavid du Colombier 
5857dd7cddfSDavid du Colombier 	Fsconnected(c, nil);
5867dd7cddfSDavid du Colombier 	return nil;
5877dd7cddfSDavid du Colombier }
5887dd7cddfSDavid du Colombier 
5897dd7cddfSDavid du Colombier static int
ipmuxstate(Conv * c,char * state,int n)5907dd7cddfSDavid du Colombier ipmuxstate(Conv *c, char *state, int n)
5917dd7cddfSDavid du Colombier {
5927dd7cddfSDavid du Colombier 	Ipmuxrock *r;
5937dd7cddfSDavid du Colombier 
5947dd7cddfSDavid du Colombier 	r = (Ipmuxrock*)(c->ptcl);
5957dd7cddfSDavid du Colombier 	return ipmuxsprint(r->chain, 0, state, n);
5967dd7cddfSDavid du Colombier }
5977dd7cddfSDavid du Colombier 
5987dd7cddfSDavid du Colombier static void
ipmuxcreate(Conv * c)5997dd7cddfSDavid du Colombier ipmuxcreate(Conv *c)
6007dd7cddfSDavid du Colombier {
6017dd7cddfSDavid du Colombier 	Ipmuxrock *r;
6027dd7cddfSDavid du Colombier 
6033ff48bf5SDavid du Colombier 	c->rq = qopen(64*1024, Qmsg, 0, c);
6043ff48bf5SDavid du Colombier 	c->wq = qopen(64*1024, Qkick, ipmuxkick, c);
6057dd7cddfSDavid du Colombier 	r = (Ipmuxrock*)(c->ptcl);
6067dd7cddfSDavid du Colombier 	r->chain = nil;
6077dd7cddfSDavid du Colombier }
6087dd7cddfSDavid du Colombier 
6097dd7cddfSDavid du Colombier static char*
ipmuxannounce(Conv *,char **,int)6107dd7cddfSDavid du Colombier ipmuxannounce(Conv*, char**, int)
6117dd7cddfSDavid du Colombier {
6127dd7cddfSDavid du Colombier 	return "ipmux does not support announce";
6137dd7cddfSDavid du Colombier }
6147dd7cddfSDavid du Colombier 
6157dd7cddfSDavid du Colombier static void
ipmuxclose(Conv * c)6167dd7cddfSDavid du Colombier ipmuxclose(Conv *c)
6177dd7cddfSDavid du Colombier {
6187dd7cddfSDavid du Colombier 	Ipmuxrock *r;
6197dd7cddfSDavid du Colombier 	Fs *f = c->p->f;
6207dd7cddfSDavid du Colombier 
6217dd7cddfSDavid du Colombier 	r = (Ipmuxrock*)(c->ptcl);
6227dd7cddfSDavid du Colombier 
6237dd7cddfSDavid du Colombier 	qclose(c->rq);
6247dd7cddfSDavid du Colombier 	qclose(c->wq);
6257dd7cddfSDavid du Colombier 	qclose(c->eq);
6267dd7cddfSDavid du Colombier 	ipmove(c->laddr, IPnoaddr);
6277dd7cddfSDavid du Colombier 	ipmove(c->raddr, IPnoaddr);
6287dd7cddfSDavid du Colombier 	c->lport = 0;
6297dd7cddfSDavid du Colombier 	c->rport = 0;
6307dd7cddfSDavid du Colombier 
6317dd7cddfSDavid du Colombier 	wlock(f);
6327dd7cddfSDavid du Colombier 	ipmuxremove(&(c->p->priv), r->chain);
6337dd7cddfSDavid du Colombier 	wunlock(f);
6347dd7cddfSDavid du Colombier 	ipmuxtreefree(r->chain);
6357dd7cddfSDavid du Colombier 	r->chain = nil;
6367dd7cddfSDavid du Colombier }
6377dd7cddfSDavid du Colombier 
6387dd7cddfSDavid du Colombier /*
6397dd7cddfSDavid du Colombier  *  takes a fully formed ip packet and just passes it down
6407dd7cddfSDavid du Colombier  *  the stack
6417dd7cddfSDavid du Colombier  */
6427dd7cddfSDavid du Colombier static void
ipmuxkick(void * x)6433ff48bf5SDavid du Colombier ipmuxkick(void *x)
6447dd7cddfSDavid du Colombier {
6453ff48bf5SDavid du Colombier 	Conv *c = x;
6467dd7cddfSDavid du Colombier 	Block *bp;
6477dd7cddfSDavid du Colombier 
6487dd7cddfSDavid du Colombier 	bp = qget(c->wq);
6490774058cSDavid du Colombier 	if(bp != nil) {
6500774058cSDavid du Colombier 		Myip4hdr *ih4 = (Myip4hdr*)(bp->rp);
6510774058cSDavid du Colombier 
6520774058cSDavid du Colombier 		if((ih4->vihl & 0xF0) != IP_VER6)
653a6a9e072SDavid du Colombier 			ipoput4(c->p->f, bp, 0, ih4->ttl, ih4->tos, nil);
6540774058cSDavid du Colombier 		else
6550774058cSDavid du Colombier 			ipoput6(c->p->f, bp, 0, ((Ip6hdr*)ih4)->ttl, 0, nil);
6563ff48bf5SDavid du Colombier 	}
6577dd7cddfSDavid du Colombier }
6587dd7cddfSDavid du Colombier 
6597dd7cddfSDavid du Colombier static void
ipmuxiput(Proto * p,Ipifc * ifc,Block * bp)6609a747e4fSDavid du Colombier ipmuxiput(Proto *p, Ipifc *ifc, Block *bp)
6617dd7cddfSDavid du Colombier {
6623ff48bf5SDavid du Colombier 	int len, hl;
6637dd7cddfSDavid du Colombier 	Fs *f = p->f;
6647dd7cddfSDavid du Colombier 	uchar *m, *h, *v, *e, *ve, *hp;
6657dd7cddfSDavid du Colombier 	Conv *c;
6667dd7cddfSDavid du Colombier 	Ipmux *mux;
6670774058cSDavid du Colombier 	Myip4hdr *ip;
6683ff48bf5SDavid du Colombier 	Ip6hdr *ip6;
6699a747e4fSDavid du Colombier 
6700774058cSDavid du Colombier 	ip = (Myip4hdr*)bp->rp;
6713ff48bf5SDavid du Colombier 	hl = (ip->vihl&0x0F)<<2;
6727dd7cddfSDavid du Colombier 
6737dd7cddfSDavid du Colombier 	if(p->priv == nil)
6747dd7cddfSDavid du Colombier 		goto nomatch;
6757dd7cddfSDavid du Colombier 
6767dd7cddfSDavid du Colombier 	h = bp->rp;
6777dd7cddfSDavid du Colombier 	len = BLEN(bp);
6787dd7cddfSDavid du Colombier 
6797dd7cddfSDavid du Colombier 	/* run the v4 filter */
6807dd7cddfSDavid du Colombier 	rlock(f);
6817dd7cddfSDavid du Colombier 	c = nil;
6827dd7cddfSDavid du Colombier 	mux = f->ipmux->priv;
6837dd7cddfSDavid du Colombier 	while(mux != nil){
6847dd7cddfSDavid du Colombier 		if(mux->eoff > len){
6857dd7cddfSDavid du Colombier 			mux = mux->no;
6867dd7cddfSDavid du Colombier 			continue;
6877dd7cddfSDavid du Colombier 		}
6883ff48bf5SDavid du Colombier 		hp = h + mux->off + ((int)mux->skiphdr)*hl;
6897dd7cddfSDavid du Colombier 		switch(mux->ctype){
6907dd7cddfSDavid du Colombier 		case Cbyte:
6917dd7cddfSDavid du Colombier 			if(*mux->val == *hp)
6927dd7cddfSDavid du Colombier 				goto yes;
6937dd7cddfSDavid du Colombier 			break;
6947dd7cddfSDavid du Colombier 		case Cmbyte:
6957dd7cddfSDavid du Colombier 			if((*hp & *mux->mask) == *mux->val)
6967dd7cddfSDavid du Colombier 				goto yes;
6977dd7cddfSDavid du Colombier 			break;
6987dd7cddfSDavid du Colombier 		case Cshort:
6997dd7cddfSDavid du Colombier 			if(*((ushort*)mux->val) == *(ushort*)hp)
7007dd7cddfSDavid du Colombier 				goto yes;
7017dd7cddfSDavid du Colombier 			break;
7027dd7cddfSDavid du Colombier 		case Cmshort:
7037dd7cddfSDavid du Colombier 			if((*(ushort*)hp & (*((ushort*)mux->mask))) == *((ushort*)mux->val))
7047dd7cddfSDavid du Colombier 				goto yes;
7057dd7cddfSDavid du Colombier 			break;
7067dd7cddfSDavid du Colombier 		case Clong:
7077dd7cddfSDavid du Colombier 			if(*((ulong*)mux->val) == *(ulong*)hp)
7087dd7cddfSDavid du Colombier 				goto yes;
7097dd7cddfSDavid du Colombier 			break;
7107dd7cddfSDavid du Colombier 		case Cmlong:
7117dd7cddfSDavid du Colombier 			if((*(ulong*)hp & (*((ulong*)mux->mask))) == *((ulong*)mux->val))
7127dd7cddfSDavid du Colombier 				goto yes;
7137dd7cddfSDavid du Colombier 			break;
7147dd7cddfSDavid du Colombier 		case Cifc:
7153ff48bf5SDavid du Colombier 			if(*((ulong*)mux->val) == *(ulong*)(ifc->lifc->local + IPv4off))
7167dd7cddfSDavid du Colombier 				goto yes;
7177dd7cddfSDavid du Colombier 			break;
7187dd7cddfSDavid du Colombier 		case Cmifc:
7193ff48bf5SDavid du Colombier 			if((*(ulong*)(ifc->lifc->local + IPv4off) & (*((ulong*)mux->mask))) == *((ulong*)mux->val))
7207dd7cddfSDavid du Colombier 				goto yes;
7217dd7cddfSDavid du Colombier 			break;
7227dd7cddfSDavid du Colombier 		default:
7237dd7cddfSDavid du Colombier 			v = mux->val;
7247dd7cddfSDavid du Colombier 			for(e = mux->e; v < e; v = ve){
7257dd7cddfSDavid du Colombier 				m = mux->mask;
7267dd7cddfSDavid du Colombier 				hp = h + mux->off;
7277dd7cddfSDavid du Colombier 				for(ve = v + mux->len; v < ve; v++){
7287dd7cddfSDavid du Colombier 					if((*hp++ & *m++) != *v)
7297dd7cddfSDavid du Colombier 						break;
7307dd7cddfSDavid du Colombier 				}
7317dd7cddfSDavid du Colombier 				if(v == ve)
7327dd7cddfSDavid du Colombier 					goto yes;
7337dd7cddfSDavid du Colombier 			}
7347dd7cddfSDavid du Colombier 		}
7357dd7cddfSDavid du Colombier 		mux = mux->no;
7367dd7cddfSDavid du Colombier 		continue;
7377dd7cddfSDavid du Colombier yes:
7387dd7cddfSDavid du Colombier 		if(mux->conv != nil)
7397dd7cddfSDavid du Colombier 			c = mux->conv;
7407dd7cddfSDavid du Colombier 		mux = mux->yes;
7417dd7cddfSDavid du Colombier 	}
7427dd7cddfSDavid du Colombier 	runlock(f);
7437dd7cddfSDavid du Colombier 
7447dd7cddfSDavid du Colombier 	if(c != nil){
7457dd7cddfSDavid du Colombier 		/* tack on interface address */
7467dd7cddfSDavid du Colombier 		bp = padblock(bp, IPaddrlen);
7473ff48bf5SDavid du Colombier 		ipmove(bp->rp, ifc->lifc->local);
7487dd7cddfSDavid du Colombier 		bp = concatblock(bp);
7497dd7cddfSDavid du Colombier 		if(bp != nil)
7507dd7cddfSDavid du Colombier 			if(qpass(c->rq, bp) < 0)
751*6b7c5dceSDavid du Colombier 				print("ipmuxiput: qpass failed\n");
7527dd7cddfSDavid du Colombier 		return;
7537dd7cddfSDavid du Colombier 	}
7547dd7cddfSDavid du Colombier 
7557dd7cddfSDavid du Colombier nomatch:
7567dd7cddfSDavid du Colombier 	/* doesn't match any filter, hand it to the specific protocol handler */
7570774058cSDavid du Colombier 	ip = (Myip4hdr*)bp->rp;
7580774058cSDavid du Colombier 	if((ip->vihl & 0xF0) == IP_VER4) {
7597dd7cddfSDavid du Colombier 		p = f->t2p[ip->proto];
7603ff48bf5SDavid du Colombier 	} else {
7613ff48bf5SDavid du Colombier 		ip6 = (Ip6hdr*)bp->rp;
7623ff48bf5SDavid du Colombier 		p = f->t2p[ip6->proto];
7633ff48bf5SDavid du Colombier 	}
7643ff48bf5SDavid du Colombier 	if(p && p->rcv)
7659a747e4fSDavid du Colombier 		(*p->rcv)(p, ifc, bp);
7667dd7cddfSDavid du Colombier 	else
7677dd7cddfSDavid du Colombier 		freeblist(bp);
7687dd7cddfSDavid du Colombier 	return;
7697dd7cddfSDavid du Colombier }
7707dd7cddfSDavid du Colombier 
7717dd7cddfSDavid du Colombier static int
ipmuxsprint(Ipmux * mux,int level,char * buf,int len)7727dd7cddfSDavid du Colombier ipmuxsprint(Ipmux *mux, int level, char *buf, int len)
7737dd7cddfSDavid du Colombier {
7747dd7cddfSDavid du Colombier 	int i, j, n;
7757dd7cddfSDavid du Colombier 	uchar *v;
7767dd7cddfSDavid du Colombier 
7777dd7cddfSDavid du Colombier 	n = 0;
7787dd7cddfSDavid du Colombier 	for(i = 0; i < level; i++)
7797dd7cddfSDavid du Colombier 		n += snprint(buf+n, len-n, " ");
7807dd7cddfSDavid du Colombier 	if(mux == nil){
7817dd7cddfSDavid du Colombier 		n += snprint(buf+n, len-n, "\n");
7827dd7cddfSDavid du Colombier 		return n;
7837dd7cddfSDavid du Colombier 	}
7843ff48bf5SDavid du Colombier 	n += snprint(buf+n, len-n, "h[%d:%d]&",
7853ff48bf5SDavid du Colombier                mux->off+((int)mux->skiphdr)*((int)ipoff->data),
7863ff48bf5SDavid du Colombier                mux->off+(((int)mux->skiphdr)*((int)ipoff->data))+mux->len-1);
7877dd7cddfSDavid du Colombier 	for(i = 0; i < mux->len; i++)
7887dd7cddfSDavid du Colombier 		n += snprint(buf+n, len - n, "%2.2ux", mux->mask[i]);
7897dd7cddfSDavid du Colombier 	n += snprint(buf+n, len-n, "=");
7907dd7cddfSDavid du Colombier 	v = mux->val;
7917dd7cddfSDavid du Colombier 	for(j = 0; j < mux->n; j++){
7927dd7cddfSDavid du Colombier 		for(i = 0; i < mux->len; i++)
7937dd7cddfSDavid du Colombier 			n += snprint(buf+n, len - n, "%2.2ux", *v++);
7947dd7cddfSDavid du Colombier 		n += snprint(buf+n, len-n, "|");
7957dd7cddfSDavid du Colombier 	}
7967dd7cddfSDavid du Colombier 	n += snprint(buf+n, len-n, "\n");
7977dd7cddfSDavid du Colombier 	level++;
7987dd7cddfSDavid du Colombier 	n += ipmuxsprint(mux->no, level, buf+n, len-n);
7997dd7cddfSDavid du Colombier 	n += ipmuxsprint(mux->yes, level, buf+n, len-n);
8007dd7cddfSDavid du Colombier 	return n;
8017dd7cddfSDavid du Colombier }
8027dd7cddfSDavid du Colombier 
8037dd7cddfSDavid du Colombier static int
ipmuxstats(Proto * p,char * buf,int len)8047dd7cddfSDavid du Colombier ipmuxstats(Proto *p, char *buf, int len)
8057dd7cddfSDavid du Colombier {
8067dd7cddfSDavid du Colombier 	int n;
8077dd7cddfSDavid du Colombier 	Fs *f = p->f;
8087dd7cddfSDavid du Colombier 
8097dd7cddfSDavid du Colombier 	rlock(f);
8107dd7cddfSDavid du Colombier 	n = ipmuxsprint(p->priv, 0, buf, len);
8117dd7cddfSDavid du Colombier 	runlock(f);
8127dd7cddfSDavid du Colombier 
8137dd7cddfSDavid du Colombier 	return n;
8147dd7cddfSDavid du Colombier }
8157dd7cddfSDavid du Colombier 
8167dd7cddfSDavid du Colombier void
ipmuxinit(Fs * f)8177dd7cddfSDavid du Colombier ipmuxinit(Fs *f)
8187dd7cddfSDavid du Colombier {
8197dd7cddfSDavid du Colombier 	Proto *ipmux;
8207dd7cddfSDavid du Colombier 
8217dd7cddfSDavid du Colombier 	ipmux = smalloc(sizeof(Proto));
8227dd7cddfSDavid du Colombier 	ipmux->priv = nil;
8237dd7cddfSDavid du Colombier 	ipmux->name = "ipmux";
8247dd7cddfSDavid du Colombier 	ipmux->connect = ipmuxconnect;
8257dd7cddfSDavid du Colombier 	ipmux->announce = ipmuxannounce;
8267dd7cddfSDavid du Colombier 	ipmux->state = ipmuxstate;
8277dd7cddfSDavid du Colombier 	ipmux->create = ipmuxcreate;
8287dd7cddfSDavid du Colombier 	ipmux->close = ipmuxclose;
8297dd7cddfSDavid du Colombier 	ipmux->rcv = ipmuxiput;
8307dd7cddfSDavid du Colombier 	ipmux->ctl = nil;
8317dd7cddfSDavid du Colombier 	ipmux->advise = nil;
8327dd7cddfSDavid du Colombier 	ipmux->stats = ipmuxstats;
8337dd7cddfSDavid du Colombier 	ipmux->ipproto = -1;
8347dd7cddfSDavid du Colombier 	ipmux->nc = 64;
8357dd7cddfSDavid du Colombier 	ipmux->ptclsize = sizeof(Ipmuxrock);
8367dd7cddfSDavid du Colombier 
8377dd7cddfSDavid du Colombier 	f->ipmux = ipmux;			/* hack for Fsrcvpcol */
8387dd7cddfSDavid du Colombier 
8397dd7cddfSDavid du Colombier 	Fsproto(f, ipmux);
8407dd7cddfSDavid du Colombier }
841