xref: /plan9-contrib/sys/src/9k/ip/ipmux.c (revision 9ef1f84b659abcb917c5c090acbce0772e494f21)
1*9ef1f84bSDavid du Colombier /*
2*9ef1f84bSDavid du Colombier  * IP packet filter
3*9ef1f84bSDavid du Colombier  */
4*9ef1f84bSDavid du Colombier #include "u.h"
5*9ef1f84bSDavid du Colombier #include "../port/lib.h"
6*9ef1f84bSDavid du Colombier #include "mem.h"
7*9ef1f84bSDavid du Colombier #include "dat.h"
8*9ef1f84bSDavid du Colombier #include "fns.h"
9*9ef1f84bSDavid du Colombier #include "../port/error.h"
10*9ef1f84bSDavid du Colombier 
11*9ef1f84bSDavid du Colombier #include "ip.h"
12*9ef1f84bSDavid du Colombier #include "ipv6.h"
13*9ef1f84bSDavid du Colombier 
14*9ef1f84bSDavid du Colombier typedef struct Ipmuxrock  Ipmuxrock;
15*9ef1f84bSDavid du Colombier typedef struct Ipmux      Ipmux;
16*9ef1f84bSDavid du Colombier 
17*9ef1f84bSDavid du Colombier typedef struct Myip4hdr Myip4hdr;
18*9ef1f84bSDavid du Colombier struct Myip4hdr
19*9ef1f84bSDavid du Colombier {
20*9ef1f84bSDavid du Colombier 	uchar	vihl;		/* Version and header length */
21*9ef1f84bSDavid du Colombier 	uchar	tos;		/* Type of service */
22*9ef1f84bSDavid du Colombier 	uchar	length[2];	/* packet length */
23*9ef1f84bSDavid du Colombier 	uchar	id[2];		/* ip->identification */
24*9ef1f84bSDavid du Colombier 	uchar	frag[2];	/* Fragment information */
25*9ef1f84bSDavid du Colombier 	uchar	ttl;		/* Time to live */
26*9ef1f84bSDavid du Colombier 	uchar	proto;		/* Protocol */
27*9ef1f84bSDavid du Colombier 	uchar	cksum[2];	/* Header checksum */
28*9ef1f84bSDavid du Colombier 	uchar	src[4];		/* IP source */
29*9ef1f84bSDavid du Colombier 	uchar	dst[4];		/* IP destination */
30*9ef1f84bSDavid du Colombier 
31*9ef1f84bSDavid du Colombier 	uchar	data[1];	/* start of data */
32*9ef1f84bSDavid du Colombier };
33*9ef1f84bSDavid du Colombier Myip4hdr *ipoff = 0;
34*9ef1f84bSDavid du Colombier 
35*9ef1f84bSDavid du Colombier enum
36*9ef1f84bSDavid du Colombier {
37*9ef1f84bSDavid du Colombier 	Tproto,
38*9ef1f84bSDavid du Colombier 	Tdata,
39*9ef1f84bSDavid du Colombier 	Tiph,
40*9ef1f84bSDavid du Colombier 	Tdst,
41*9ef1f84bSDavid du Colombier 	Tsrc,
42*9ef1f84bSDavid du Colombier 	Tifc,
43*9ef1f84bSDavid du Colombier 
44*9ef1f84bSDavid du Colombier 	Cother = 0,
45*9ef1f84bSDavid du Colombier 	Cbyte,		/* single byte */
46*9ef1f84bSDavid du Colombier 	Cmbyte,		/* single byte with mask */
47*9ef1f84bSDavid du Colombier 	Cshort,		/* single short */
48*9ef1f84bSDavid du Colombier 	Cmshort,	/* single short with mask */
49*9ef1f84bSDavid du Colombier 	Clong,		/* single long */
50*9ef1f84bSDavid du Colombier 	Cmlong,		/* single long with mask */
51*9ef1f84bSDavid du Colombier 	Cifc,
52*9ef1f84bSDavid du Colombier 	Cmifc,
53*9ef1f84bSDavid du Colombier };
54*9ef1f84bSDavid du Colombier 
55*9ef1f84bSDavid du Colombier char *ftname[] =
56*9ef1f84bSDavid du Colombier {
57*9ef1f84bSDavid du Colombier [Tproto]	"proto",
58*9ef1f84bSDavid du Colombier [Tdata]		"data",
59*9ef1f84bSDavid du Colombier [Tiph]	 	"iph",
60*9ef1f84bSDavid du Colombier [Tdst]		"dst",
61*9ef1f84bSDavid du Colombier [Tsrc]		"src",
62*9ef1f84bSDavid du Colombier [Tifc]		"ifc",
63*9ef1f84bSDavid du Colombier };
64*9ef1f84bSDavid du Colombier 
65*9ef1f84bSDavid du Colombier /*
66*9ef1f84bSDavid du Colombier  *  a node in the decision tree
67*9ef1f84bSDavid du Colombier  */
68*9ef1f84bSDavid du Colombier struct Ipmux
69*9ef1f84bSDavid du Colombier {
70*9ef1f84bSDavid du Colombier 	Ipmux	*yes;
71*9ef1f84bSDavid du Colombier 	Ipmux	*no;
72*9ef1f84bSDavid du Colombier 	uchar	type;		/* type of field(Txxxx) */
73*9ef1f84bSDavid du Colombier 	uchar	ctype;		/* tupe of comparison(Cxxxx) */
74*9ef1f84bSDavid du Colombier 	uchar	len;		/* length in bytes of item to compare */
75*9ef1f84bSDavid du Colombier 	uchar	n;		/* number of items val points to */
76*9ef1f84bSDavid du Colombier 	short	off;		/* offset of comparison */
77*9ef1f84bSDavid du Colombier 	short	eoff;		/* end offset of comparison */
78*9ef1f84bSDavid du Colombier 	uchar	skiphdr;	/* should offset start after ipheader */
79*9ef1f84bSDavid du Colombier 	uchar	*val;
80*9ef1f84bSDavid du Colombier 	uchar	*mask;
81*9ef1f84bSDavid du Colombier 	uchar	*e;		/* val+n*len*/
82*9ef1f84bSDavid du Colombier 
83*9ef1f84bSDavid du Colombier 	int	ref;		/* so we can garbage collect */
84*9ef1f84bSDavid du Colombier 	Conv	*conv;
85*9ef1f84bSDavid du Colombier };
86*9ef1f84bSDavid du Colombier 
87*9ef1f84bSDavid du Colombier /*
88*9ef1f84bSDavid du Colombier  *  someplace to hold per conversation data
89*9ef1f84bSDavid du Colombier  */
90*9ef1f84bSDavid du Colombier struct Ipmuxrock
91*9ef1f84bSDavid du Colombier {
92*9ef1f84bSDavid du Colombier 	Ipmux	*chain;
93*9ef1f84bSDavid du Colombier };
94*9ef1f84bSDavid du Colombier 
95*9ef1f84bSDavid du Colombier static int	ipmuxsprint(Ipmux*, int, char*, int);
96*9ef1f84bSDavid du Colombier static void	ipmuxkick(void *x);
97*9ef1f84bSDavid du Colombier 
98*9ef1f84bSDavid du Colombier static char*
skipwhite(char * p)99*9ef1f84bSDavid du Colombier skipwhite(char *p)
100*9ef1f84bSDavid du Colombier {
101*9ef1f84bSDavid du Colombier 	while(*p == ' ' || *p == '\t')
102*9ef1f84bSDavid du Colombier 		p++;
103*9ef1f84bSDavid du Colombier 	return p;
104*9ef1f84bSDavid du Colombier }
105*9ef1f84bSDavid du Colombier 
106*9ef1f84bSDavid du Colombier static char*
follows(char * p,char c)107*9ef1f84bSDavid du Colombier follows(char *p, char c)
108*9ef1f84bSDavid du Colombier {
109*9ef1f84bSDavid du Colombier 	char *f;
110*9ef1f84bSDavid du Colombier 
111*9ef1f84bSDavid du Colombier 	f = strchr(p, c);
112*9ef1f84bSDavid du Colombier 	if(f == nil)
113*9ef1f84bSDavid du Colombier 		return nil;
114*9ef1f84bSDavid du Colombier 	*f++ = 0;
115*9ef1f84bSDavid du Colombier 	f = skipwhite(f);
116*9ef1f84bSDavid du Colombier 	if(*f == 0)
117*9ef1f84bSDavid du Colombier 		return nil;
118*9ef1f84bSDavid du Colombier 	return f;
119*9ef1f84bSDavid du Colombier }
120*9ef1f84bSDavid du Colombier 
121*9ef1f84bSDavid du Colombier static Ipmux*
parseop(char ** pp)122*9ef1f84bSDavid du Colombier parseop(char **pp)
123*9ef1f84bSDavid du Colombier {
124*9ef1f84bSDavid du Colombier 	char *p = *pp;
125*9ef1f84bSDavid du Colombier 	int type, off, end, len;
126*9ef1f84bSDavid du Colombier 	Ipmux *f;
127*9ef1f84bSDavid du Colombier 
128*9ef1f84bSDavid du Colombier 	p = skipwhite(p);
129*9ef1f84bSDavid du Colombier 	if(strncmp(p, "dst", 3) == 0){
130*9ef1f84bSDavid du Colombier 		type = Tdst;
131*9ef1f84bSDavid du Colombier 		off = (uintptr)(ipoff->dst);
132*9ef1f84bSDavid du Colombier 		len = IPv4addrlen;
133*9ef1f84bSDavid du Colombier 		p += 3;
134*9ef1f84bSDavid du Colombier 	}
135*9ef1f84bSDavid du Colombier 	else if(strncmp(p, "src", 3) == 0){
136*9ef1f84bSDavid du Colombier 		type = Tsrc;
137*9ef1f84bSDavid du Colombier 		off = (uintptr)(ipoff->src);
138*9ef1f84bSDavid du Colombier 		len = IPv4addrlen;
139*9ef1f84bSDavid du Colombier 		p += 3;
140*9ef1f84bSDavid du Colombier 	}
141*9ef1f84bSDavid du Colombier 	else if(strncmp(p, "ifc", 3) == 0){
142*9ef1f84bSDavid du Colombier 		type = Tifc;
143*9ef1f84bSDavid du Colombier 		off = -IPv4addrlen;
144*9ef1f84bSDavid du Colombier 		len = IPv4addrlen;
145*9ef1f84bSDavid du Colombier 		p += 3;
146*9ef1f84bSDavid du Colombier 	}
147*9ef1f84bSDavid du Colombier 	else if(strncmp(p, "proto", 5) == 0){
148*9ef1f84bSDavid du Colombier 		type = Tproto;
149*9ef1f84bSDavid du Colombier 		off = (uintptr)&(ipoff->proto);
150*9ef1f84bSDavid du Colombier 		len = 1;
151*9ef1f84bSDavid du Colombier 		p += 5;
152*9ef1f84bSDavid du Colombier 	}
153*9ef1f84bSDavid du Colombier 	else if(strncmp(p, "data", 4) == 0 || strncmp(p, "iph", 3) == 0){
154*9ef1f84bSDavid du Colombier 		if(strncmp(p, "data", 4) == 0) {
155*9ef1f84bSDavid du Colombier 			type = Tdata;
156*9ef1f84bSDavid du Colombier 			p += 4;
157*9ef1f84bSDavid du Colombier 		}
158*9ef1f84bSDavid du Colombier 		else {
159*9ef1f84bSDavid du Colombier 			type = Tiph;
160*9ef1f84bSDavid du Colombier 			p += 3;
161*9ef1f84bSDavid du Colombier 		}
162*9ef1f84bSDavid du Colombier 		p = skipwhite(p);
163*9ef1f84bSDavid du Colombier 		if(*p != '[')
164*9ef1f84bSDavid du Colombier 			return nil;
165*9ef1f84bSDavid du Colombier 		p++;
166*9ef1f84bSDavid du Colombier 		off = strtoul(p, &p, 0);
167*9ef1f84bSDavid du Colombier 		if(off < 0 || off > (64-IP4HDR))
168*9ef1f84bSDavid du Colombier 			return nil;
169*9ef1f84bSDavid du Colombier 		p = skipwhite(p);
170*9ef1f84bSDavid du Colombier 		if(*p != ':')
171*9ef1f84bSDavid du Colombier 			end = off;
172*9ef1f84bSDavid du Colombier 		else {
173*9ef1f84bSDavid du Colombier 			p++;
174*9ef1f84bSDavid du Colombier 			p = skipwhite(p);
175*9ef1f84bSDavid du Colombier 			end = strtoul(p, &p, 0);
176*9ef1f84bSDavid du Colombier 			if(end < off)
177*9ef1f84bSDavid du Colombier 				return nil;
178*9ef1f84bSDavid du Colombier 			p = skipwhite(p);
179*9ef1f84bSDavid du Colombier 		}
180*9ef1f84bSDavid du Colombier 		if(*p != ']')
181*9ef1f84bSDavid du Colombier 			return nil;
182*9ef1f84bSDavid du Colombier 		p++;
183*9ef1f84bSDavid du Colombier 		len = end - off + 1;
184*9ef1f84bSDavid du Colombier 	}
185*9ef1f84bSDavid du Colombier 	else
186*9ef1f84bSDavid du Colombier 		return nil;
187*9ef1f84bSDavid du Colombier 
188*9ef1f84bSDavid du Colombier 	f = smalloc(sizeof(*f));
189*9ef1f84bSDavid du Colombier 	f->type = type;
190*9ef1f84bSDavid du Colombier 	f->len = len;
191*9ef1f84bSDavid du Colombier 	f->off = off;
192*9ef1f84bSDavid du Colombier 	f->val = nil;
193*9ef1f84bSDavid du Colombier 	f->mask = nil;
194*9ef1f84bSDavid du Colombier 	f->n = 1;
195*9ef1f84bSDavid du Colombier 	f->ref = 1;
196*9ef1f84bSDavid du Colombier 	if(type == Tdata)
197*9ef1f84bSDavid du Colombier 		f->skiphdr = 1;
198*9ef1f84bSDavid du Colombier 	else
199*9ef1f84bSDavid du Colombier 		f->skiphdr = 0;
200*9ef1f84bSDavid du Colombier 
201*9ef1f84bSDavid du Colombier 	return f;
202*9ef1f84bSDavid du Colombier }
203*9ef1f84bSDavid du Colombier 
204*9ef1f84bSDavid du Colombier static int
htoi(char x)205*9ef1f84bSDavid du Colombier htoi(char x)
206*9ef1f84bSDavid du Colombier {
207*9ef1f84bSDavid du Colombier 	if(x >= '0' && x <= '9')
208*9ef1f84bSDavid du Colombier 		x -= '0';
209*9ef1f84bSDavid du Colombier 	else if(x >= 'a' && x <= 'f')
210*9ef1f84bSDavid du Colombier 		x -= 'a' - 10;
211*9ef1f84bSDavid du Colombier 	else if(x >= 'A' && x <= 'F')
212*9ef1f84bSDavid du Colombier 		x -= 'A' - 10;
213*9ef1f84bSDavid du Colombier 	else
214*9ef1f84bSDavid du Colombier 		x = 0;
215*9ef1f84bSDavid du Colombier 	return x;
216*9ef1f84bSDavid du Colombier }
217*9ef1f84bSDavid du Colombier 
218*9ef1f84bSDavid du Colombier static int
hextoi(char * p)219*9ef1f84bSDavid du Colombier hextoi(char *p)
220*9ef1f84bSDavid du Colombier {
221*9ef1f84bSDavid du Colombier 	return (htoi(p[0])<<4) | htoi(p[1]);
222*9ef1f84bSDavid du Colombier }
223*9ef1f84bSDavid du Colombier 
224*9ef1f84bSDavid du Colombier static void
parseval(uchar * v,char * p,int len)225*9ef1f84bSDavid du Colombier parseval(uchar *v, char *p, int len)
226*9ef1f84bSDavid du Colombier {
227*9ef1f84bSDavid du Colombier 	while(*p && len-- > 0){
228*9ef1f84bSDavid du Colombier 		*v++ = hextoi(p);
229*9ef1f84bSDavid du Colombier 		p += 2;
230*9ef1f84bSDavid du Colombier 	}
231*9ef1f84bSDavid du Colombier }
232*9ef1f84bSDavid du Colombier 
233*9ef1f84bSDavid du Colombier static Ipmux*
parsemux(char * p)234*9ef1f84bSDavid du Colombier parsemux(char *p)
235*9ef1f84bSDavid du Colombier {
236*9ef1f84bSDavid du Colombier 	int n, nomask;
237*9ef1f84bSDavid du Colombier 	Ipmux *f;
238*9ef1f84bSDavid du Colombier 	char *val;
239*9ef1f84bSDavid du Colombier 	char *mask;
240*9ef1f84bSDavid du Colombier 	char *vals[20];
241*9ef1f84bSDavid du Colombier 	uchar *v;
242*9ef1f84bSDavid du Colombier 
243*9ef1f84bSDavid du Colombier 	/* parse operand */
244*9ef1f84bSDavid du Colombier 	f = parseop(&p);
245*9ef1f84bSDavid du Colombier 	if(f == nil)
246*9ef1f84bSDavid du Colombier 		return nil;
247*9ef1f84bSDavid du Colombier 
248*9ef1f84bSDavid du Colombier 	/* find value */
249*9ef1f84bSDavid du Colombier 	val = follows(p, '=');
250*9ef1f84bSDavid du Colombier 	if(val == nil)
251*9ef1f84bSDavid du Colombier 		goto parseerror;
252*9ef1f84bSDavid du Colombier 
253*9ef1f84bSDavid du Colombier 	/* parse mask */
254*9ef1f84bSDavid du Colombier 	mask = follows(p, '&');
255*9ef1f84bSDavid du Colombier 	if(mask != nil){
256*9ef1f84bSDavid du Colombier 		switch(f->type){
257*9ef1f84bSDavid du Colombier 		case Tsrc:
258*9ef1f84bSDavid du Colombier 		case Tdst:
259*9ef1f84bSDavid du Colombier 		case Tifc:
260*9ef1f84bSDavid du Colombier 			f->mask = smalloc(f->len);
261*9ef1f84bSDavid du Colombier 			v4parseip(f->mask, mask);
262*9ef1f84bSDavid du Colombier 			break;
263*9ef1f84bSDavid du Colombier 		case Tdata:
264*9ef1f84bSDavid du Colombier 		case Tiph:
265*9ef1f84bSDavid du Colombier 			f->mask = smalloc(f->len);
266*9ef1f84bSDavid du Colombier 			parseval(f->mask, mask, f->len);
267*9ef1f84bSDavid du Colombier 			break;
268*9ef1f84bSDavid du Colombier 		default:
269*9ef1f84bSDavid du Colombier 			goto parseerror;
270*9ef1f84bSDavid du Colombier 		}
271*9ef1f84bSDavid du Colombier 		nomask = 0;
272*9ef1f84bSDavid du Colombier 	} else {
273*9ef1f84bSDavid du Colombier 		nomask = 1;
274*9ef1f84bSDavid du Colombier 		f->mask = smalloc(f->len);
275*9ef1f84bSDavid du Colombier 		memset(f->mask, 0xff, f->len);
276*9ef1f84bSDavid du Colombier 	}
277*9ef1f84bSDavid du Colombier 
278*9ef1f84bSDavid du Colombier 	/* parse vals */
279*9ef1f84bSDavid du Colombier 	f->n = getfields(val, vals, sizeof(vals)/sizeof(char*), 1, "|");
280*9ef1f84bSDavid du Colombier 	if(f->n == 0)
281*9ef1f84bSDavid du Colombier 		goto parseerror;
282*9ef1f84bSDavid du Colombier 	f->val = smalloc(f->n*f->len);
283*9ef1f84bSDavid du Colombier 	v = f->val;
284*9ef1f84bSDavid du Colombier 	for(n = 0; n < f->n; n++){
285*9ef1f84bSDavid du Colombier 		switch(f->type){
286*9ef1f84bSDavid du Colombier 		case Tsrc:
287*9ef1f84bSDavid du Colombier 		case Tdst:
288*9ef1f84bSDavid du Colombier 		case Tifc:
289*9ef1f84bSDavid du Colombier 			v4parseip(v, vals[n]);
290*9ef1f84bSDavid du Colombier 			break;
291*9ef1f84bSDavid du Colombier 		case Tproto:
292*9ef1f84bSDavid du Colombier 		case Tdata:
293*9ef1f84bSDavid du Colombier 		case Tiph:
294*9ef1f84bSDavid du Colombier 			parseval(v, vals[n], f->len);
295*9ef1f84bSDavid du Colombier 			break;
296*9ef1f84bSDavid du Colombier 		}
297*9ef1f84bSDavid du Colombier 		v += f->len;
298*9ef1f84bSDavid du Colombier 	}
299*9ef1f84bSDavid du Colombier 
300*9ef1f84bSDavid du Colombier 	f->eoff = f->off + f->len;
301*9ef1f84bSDavid du Colombier 	f->e = f->val + f->n*f->len;
302*9ef1f84bSDavid du Colombier 	f->ctype = Cother;
303*9ef1f84bSDavid du Colombier 	if(f->n == 1){
304*9ef1f84bSDavid du Colombier 		switch(f->len){
305*9ef1f84bSDavid du Colombier 		case 1:
306*9ef1f84bSDavid du Colombier 			f->ctype = nomask ? Cbyte : Cmbyte;
307*9ef1f84bSDavid du Colombier 			break;
308*9ef1f84bSDavid du Colombier 		case 2:
309*9ef1f84bSDavid du Colombier 			f->ctype = nomask ? Cshort : Cmshort;
310*9ef1f84bSDavid du Colombier 			break;
311*9ef1f84bSDavid du Colombier 		case 4:
312*9ef1f84bSDavid du Colombier 			if(f->type == Tifc)
313*9ef1f84bSDavid du Colombier 				f->ctype = nomask ? Cifc : Cmifc;
314*9ef1f84bSDavid du Colombier 			else
315*9ef1f84bSDavid du Colombier 				f->ctype = nomask ? Clong : Cmlong;
316*9ef1f84bSDavid du Colombier 			break;
317*9ef1f84bSDavid du Colombier 		}
318*9ef1f84bSDavid du Colombier 	}
319*9ef1f84bSDavid du Colombier 	return f;
320*9ef1f84bSDavid du Colombier 
321*9ef1f84bSDavid du Colombier parseerror:
322*9ef1f84bSDavid du Colombier 	if(f->mask)
323*9ef1f84bSDavid du Colombier 		free(f->mask);
324*9ef1f84bSDavid du Colombier 	if(f->val)
325*9ef1f84bSDavid du Colombier 		free(f->val);
326*9ef1f84bSDavid du Colombier 	free(f);
327*9ef1f84bSDavid du Colombier 	return nil;
328*9ef1f84bSDavid du Colombier }
329*9ef1f84bSDavid du Colombier 
330*9ef1f84bSDavid du Colombier /*
331*9ef1f84bSDavid du Colombier  *  Compare relative ordering of two ipmuxs.  This doesn't compare the
332*9ef1f84bSDavid du Colombier  *  values, just the fields being looked at.
333*9ef1f84bSDavid du Colombier  *
334*9ef1f84bSDavid du Colombier  *  returns:	<0 if a is a more specific match
335*9ef1f84bSDavid du Colombier  *		 0 if a and b are matching on the same fields
336*9ef1f84bSDavid du Colombier  *		>0 if b is a more specific match
337*9ef1f84bSDavid du Colombier  */
338*9ef1f84bSDavid du Colombier static int
ipmuxcmp(Ipmux * a,Ipmux * b)339*9ef1f84bSDavid du Colombier ipmuxcmp(Ipmux *a, Ipmux *b)
340*9ef1f84bSDavid du Colombier {
341*9ef1f84bSDavid du Colombier 	int n;
342*9ef1f84bSDavid du Colombier 
343*9ef1f84bSDavid du Colombier 	/* compare types, lesser ones are more important */
344*9ef1f84bSDavid du Colombier 	n = a->type - b->type;
345*9ef1f84bSDavid du Colombier 	if(n != 0)
346*9ef1f84bSDavid du Colombier 		return n;
347*9ef1f84bSDavid du Colombier 
348*9ef1f84bSDavid du Colombier 	/* compare offsets, call earlier ones more specific */
349*9ef1f84bSDavid du Colombier 	n = (a->off+((int)a->skiphdr)*(uintptr)ipoff->data) -
350*9ef1f84bSDavid du Colombier 		(b->off+((int)b->skiphdr)*(uintptr)ipoff->data);
351*9ef1f84bSDavid du Colombier 	if(n != 0)
352*9ef1f84bSDavid du Colombier 		return n;
353*9ef1f84bSDavid du Colombier 
354*9ef1f84bSDavid du Colombier 	/* compare match lengths, longer ones are more specific */
355*9ef1f84bSDavid du Colombier 	n = b->len - a->len;
356*9ef1f84bSDavid du Colombier 	if(n != 0)
357*9ef1f84bSDavid du Colombier 		return n;
358*9ef1f84bSDavid du Colombier 
359*9ef1f84bSDavid du Colombier 	/*
360*9ef1f84bSDavid du Colombier 	 *  if we get here we have two entries matching
361*9ef1f84bSDavid du Colombier 	 *  the same bytes of the record.  Now check
362*9ef1f84bSDavid du Colombier 	 *  the mask for equality.  Longer masks are
363*9ef1f84bSDavid du Colombier 	 *  more specific.
364*9ef1f84bSDavid du Colombier 	 */
365*9ef1f84bSDavid du Colombier 	if(a->mask != nil && b->mask == nil)
366*9ef1f84bSDavid du Colombier 		return -1;
367*9ef1f84bSDavid du Colombier 	if(a->mask == nil && b->mask != nil)
368*9ef1f84bSDavid du Colombier 		return 1;
369*9ef1f84bSDavid du Colombier 	if(a->mask != nil && b->mask != nil){
370*9ef1f84bSDavid du Colombier 		n = memcmp(b->mask, a->mask, a->len);
371*9ef1f84bSDavid du Colombier 		if(n != 0)
372*9ef1f84bSDavid du Colombier 			return n;
373*9ef1f84bSDavid du Colombier 	}
374*9ef1f84bSDavid du Colombier 	return 0;
375*9ef1f84bSDavid du Colombier }
376*9ef1f84bSDavid du Colombier 
377*9ef1f84bSDavid du Colombier /*
378*9ef1f84bSDavid du Colombier  *  Compare the values of two ipmuxs.  We're assuming that ipmuxcmp
379*9ef1f84bSDavid du Colombier  *  returned 0 comparing them.
380*9ef1f84bSDavid du Colombier  */
381*9ef1f84bSDavid du Colombier static int
ipmuxvalcmp(Ipmux * a,Ipmux * b)382*9ef1f84bSDavid du Colombier ipmuxvalcmp(Ipmux *a, Ipmux *b)
383*9ef1f84bSDavid du Colombier {
384*9ef1f84bSDavid du Colombier 	int n;
385*9ef1f84bSDavid du Colombier 
386*9ef1f84bSDavid du Colombier 	n = b->len*b->n - a->len*a->n;
387*9ef1f84bSDavid du Colombier 	if(n != 0)
388*9ef1f84bSDavid du Colombier 		return n;
389*9ef1f84bSDavid du Colombier 	return memcmp(a->val, b->val, a->len*a->n);
390*9ef1f84bSDavid du Colombier }
391*9ef1f84bSDavid du Colombier 
392*9ef1f84bSDavid du Colombier /*
393*9ef1f84bSDavid du Colombier  *  add onto an existing ipmux chain in the canonical comparison
394*9ef1f84bSDavid du Colombier  *  order
395*9ef1f84bSDavid du Colombier  */
396*9ef1f84bSDavid du Colombier static void
ipmuxchain(Ipmux ** l,Ipmux * f)397*9ef1f84bSDavid du Colombier ipmuxchain(Ipmux **l, Ipmux *f)
398*9ef1f84bSDavid du Colombier {
399*9ef1f84bSDavid du Colombier 	for(; *l; l = &(*l)->yes)
400*9ef1f84bSDavid du Colombier 		if(ipmuxcmp(f, *l) < 0)
401*9ef1f84bSDavid du Colombier 			break;
402*9ef1f84bSDavid du Colombier 	f->yes = *l;
403*9ef1f84bSDavid du Colombier 	*l = f;
404*9ef1f84bSDavid du Colombier }
405*9ef1f84bSDavid du Colombier 
406*9ef1f84bSDavid du Colombier /*
407*9ef1f84bSDavid du Colombier  *  copy a tree
408*9ef1f84bSDavid du Colombier  */
409*9ef1f84bSDavid du Colombier static Ipmux*
ipmuxcopy(Ipmux * f)410*9ef1f84bSDavid du Colombier ipmuxcopy(Ipmux *f)
411*9ef1f84bSDavid du Colombier {
412*9ef1f84bSDavid du Colombier 	Ipmux *nf;
413*9ef1f84bSDavid du Colombier 
414*9ef1f84bSDavid du Colombier 	if(f == nil)
415*9ef1f84bSDavid du Colombier 		return nil;
416*9ef1f84bSDavid du Colombier 	nf = smalloc(sizeof *nf);
417*9ef1f84bSDavid du Colombier 	*nf = *f;
418*9ef1f84bSDavid du Colombier 	nf->no = ipmuxcopy(f->no);
419*9ef1f84bSDavid du Colombier 	nf->yes = ipmuxcopy(f->yes);
420*9ef1f84bSDavid du Colombier 	nf->val = smalloc(f->n*f->len);
421*9ef1f84bSDavid du Colombier 	nf->e = nf->val + f->len*f->n;
422*9ef1f84bSDavid du Colombier 	memmove(nf->val, f->val, f->n*f->len);
423*9ef1f84bSDavid du Colombier 	return nf;
424*9ef1f84bSDavid du Colombier }
425*9ef1f84bSDavid du Colombier 
426*9ef1f84bSDavid du Colombier static void
ipmuxfree(Ipmux * f)427*9ef1f84bSDavid du Colombier ipmuxfree(Ipmux *f)
428*9ef1f84bSDavid du Colombier {
429*9ef1f84bSDavid du Colombier 	if(f->val != nil)
430*9ef1f84bSDavid du Colombier 		free(f->val);
431*9ef1f84bSDavid du Colombier 	free(f);
432*9ef1f84bSDavid du Colombier }
433*9ef1f84bSDavid du Colombier 
434*9ef1f84bSDavid du Colombier static void
ipmuxtreefree(Ipmux * f)435*9ef1f84bSDavid du Colombier ipmuxtreefree(Ipmux *f)
436*9ef1f84bSDavid du Colombier {
437*9ef1f84bSDavid du Colombier 	if(f == nil)
438*9ef1f84bSDavid du Colombier 		return;
439*9ef1f84bSDavid du Colombier 	if(f->no != nil)
440*9ef1f84bSDavid du Colombier 		ipmuxfree(f->no);
441*9ef1f84bSDavid du Colombier 	if(f->yes != nil)
442*9ef1f84bSDavid du Colombier 		ipmuxfree(f->yes);
443*9ef1f84bSDavid du Colombier 	ipmuxfree(f);
444*9ef1f84bSDavid du Colombier }
445*9ef1f84bSDavid du Colombier 
446*9ef1f84bSDavid du Colombier /*
447*9ef1f84bSDavid du Colombier  *  merge two trees
448*9ef1f84bSDavid du Colombier  */
449*9ef1f84bSDavid du Colombier static Ipmux*
ipmuxmerge(Ipmux * a,Ipmux * b)450*9ef1f84bSDavid du Colombier ipmuxmerge(Ipmux *a, Ipmux *b)
451*9ef1f84bSDavid du Colombier {
452*9ef1f84bSDavid du Colombier 	int n;
453*9ef1f84bSDavid du Colombier 	Ipmux *f;
454*9ef1f84bSDavid du Colombier 
455*9ef1f84bSDavid du Colombier 	if(a == nil)
456*9ef1f84bSDavid du Colombier 		return b;
457*9ef1f84bSDavid du Colombier 	if(b == nil)
458*9ef1f84bSDavid du Colombier 		return a;
459*9ef1f84bSDavid du Colombier 	n = ipmuxcmp(a, b);
460*9ef1f84bSDavid du Colombier 	if(n < 0){
461*9ef1f84bSDavid du Colombier 		f = ipmuxcopy(b);
462*9ef1f84bSDavid du Colombier 		a->yes = ipmuxmerge(a->yes, b);
463*9ef1f84bSDavid du Colombier 		a->no = ipmuxmerge(a->no, f);
464*9ef1f84bSDavid du Colombier 		return a;
465*9ef1f84bSDavid du Colombier 	}
466*9ef1f84bSDavid du Colombier 	if(n > 0){
467*9ef1f84bSDavid du Colombier 		f = ipmuxcopy(a);
468*9ef1f84bSDavid du Colombier 		b->yes = ipmuxmerge(b->yes, a);
469*9ef1f84bSDavid du Colombier 		b->no = ipmuxmerge(b->no, f);
470*9ef1f84bSDavid du Colombier 		return b;
471*9ef1f84bSDavid du Colombier 	}
472*9ef1f84bSDavid du Colombier 	if(ipmuxvalcmp(a, b) == 0){
473*9ef1f84bSDavid du Colombier 		a->yes = ipmuxmerge(a->yes, b->yes);
474*9ef1f84bSDavid du Colombier 		a->no = ipmuxmerge(a->no, b->no);
475*9ef1f84bSDavid du Colombier 		a->ref++;
476*9ef1f84bSDavid du Colombier 		ipmuxfree(b);
477*9ef1f84bSDavid du Colombier 		return a;
478*9ef1f84bSDavid du Colombier 	}
479*9ef1f84bSDavid du Colombier 	a->no = ipmuxmerge(a->no, b);
480*9ef1f84bSDavid du Colombier 	return a;
481*9ef1f84bSDavid du Colombier }
482*9ef1f84bSDavid du Colombier 
483*9ef1f84bSDavid du Colombier /*
484*9ef1f84bSDavid du Colombier  *  remove a chain from a demux tree.  This is like merging accept that
485*9ef1f84bSDavid du Colombier  *  we remove instead of insert.
486*9ef1f84bSDavid du Colombier  */
487*9ef1f84bSDavid du Colombier static int
ipmuxremove(Ipmux ** l,Ipmux * f)488*9ef1f84bSDavid du Colombier ipmuxremove(Ipmux **l, Ipmux *f)
489*9ef1f84bSDavid du Colombier {
490*9ef1f84bSDavid du Colombier 	int n, rv;
491*9ef1f84bSDavid du Colombier 	Ipmux *ft;
492*9ef1f84bSDavid du Colombier 
493*9ef1f84bSDavid du Colombier 	if(f == nil)
494*9ef1f84bSDavid du Colombier 		return 0;		/* we've removed it all */
495*9ef1f84bSDavid du Colombier 	if(*l == nil)
496*9ef1f84bSDavid du Colombier 		return -1;
497*9ef1f84bSDavid du Colombier 
498*9ef1f84bSDavid du Colombier 	ft = *l;
499*9ef1f84bSDavid du Colombier 	n = ipmuxcmp(ft, f);
500*9ef1f84bSDavid du Colombier 	if(n < 0){
501*9ef1f84bSDavid du Colombier 		/* *l is maching an earlier field, descend both paths */
502*9ef1f84bSDavid du Colombier 		rv = ipmuxremove(&ft->yes, f);
503*9ef1f84bSDavid du Colombier 		rv += ipmuxremove(&ft->no, f);
504*9ef1f84bSDavid du Colombier 		return rv;
505*9ef1f84bSDavid du Colombier 	}
506*9ef1f84bSDavid du Colombier 	if(n > 0){
507*9ef1f84bSDavid du Colombier 		/* f represents an earlier field than *l, this should be impossible */
508*9ef1f84bSDavid du Colombier 		return -1;
509*9ef1f84bSDavid du Colombier 	}
510*9ef1f84bSDavid du Colombier 
511*9ef1f84bSDavid du Colombier 	/* if we get here f and *l are comparing the same fields */
512*9ef1f84bSDavid du Colombier 	if(ipmuxvalcmp(ft, f) != 0){
513*9ef1f84bSDavid du Colombier 		/* different values mean mutually exclusive */
514*9ef1f84bSDavid du Colombier 		return ipmuxremove(&ft->no, f);
515*9ef1f84bSDavid du Colombier 	}
516*9ef1f84bSDavid du Colombier 
517*9ef1f84bSDavid du Colombier 	/* we found a match */
518*9ef1f84bSDavid du Colombier 	if(--(ft->ref) == 0){
519*9ef1f84bSDavid du Colombier 		/*
520*9ef1f84bSDavid du Colombier 		 *  a dead node implies the whole yes side is also dead.
521*9ef1f84bSDavid du Colombier 		 *  since our chain is constrained to be on that side,
522*9ef1f84bSDavid du Colombier 		 *  we're done.
523*9ef1f84bSDavid du Colombier 		 */
524*9ef1f84bSDavid du Colombier 		ipmuxtreefree(ft->yes);
525*9ef1f84bSDavid du Colombier 		*l = ft->no;
526*9ef1f84bSDavid du Colombier 		ipmuxfree(ft);
527*9ef1f84bSDavid du Colombier 		return 0;
528*9ef1f84bSDavid du Colombier 	}
529*9ef1f84bSDavid du Colombier 
530*9ef1f84bSDavid du Colombier 	/*
531*9ef1f84bSDavid du Colombier 	 *  free the rest of the chain.  it is constrained to match the
532*9ef1f84bSDavid du Colombier 	 *  yes side.
533*9ef1f84bSDavid du Colombier 	 */
534*9ef1f84bSDavid du Colombier 	return ipmuxremove(&ft->yes, f->yes);
535*9ef1f84bSDavid du Colombier }
536*9ef1f84bSDavid du Colombier 
537*9ef1f84bSDavid du Colombier /*
538*9ef1f84bSDavid du Colombier  *  connection request is a semi separated list of filters
539*9ef1f84bSDavid du Colombier  *  e.g. proto=17;data[0:4]=11aa22bb;ifc=135.104.9.2&255.255.255.0
540*9ef1f84bSDavid du Colombier  *
541*9ef1f84bSDavid du Colombier  *  there's no protection against overlapping specs.
542*9ef1f84bSDavid du Colombier  */
543*9ef1f84bSDavid du Colombier static char*
ipmuxconnect(Conv * c,char ** argv,int argc)544*9ef1f84bSDavid du Colombier ipmuxconnect(Conv *c, char **argv, int argc)
545*9ef1f84bSDavid du Colombier {
546*9ef1f84bSDavid du Colombier 	int i, n;
547*9ef1f84bSDavid du Colombier 	char *field[10];
548*9ef1f84bSDavid du Colombier 	Ipmux *mux, *chain;
549*9ef1f84bSDavid du Colombier 	Ipmuxrock *r;
550*9ef1f84bSDavid du Colombier 	Fs *f;
551*9ef1f84bSDavid du Colombier 
552*9ef1f84bSDavid du Colombier 	f = c->p->f;
553*9ef1f84bSDavid du Colombier 
554*9ef1f84bSDavid du Colombier 	if(argc != 2)
555*9ef1f84bSDavid du Colombier 		return Ebadarg;
556*9ef1f84bSDavid du Colombier 
557*9ef1f84bSDavid du Colombier 	n = getfields(argv[1], field, nelem(field), 1, ";");
558*9ef1f84bSDavid du Colombier 	if(n <= 0)
559*9ef1f84bSDavid du Colombier 		return Ebadarg;
560*9ef1f84bSDavid du Colombier 
561*9ef1f84bSDavid du Colombier 	chain = nil;
562*9ef1f84bSDavid du Colombier 	mux = nil;
563*9ef1f84bSDavid du Colombier 	for(i = 0; i < n; i++){
564*9ef1f84bSDavid du Colombier 		mux = parsemux(field[i]);
565*9ef1f84bSDavid du Colombier 		if(mux == nil){
566*9ef1f84bSDavid du Colombier 			ipmuxtreefree(chain);
567*9ef1f84bSDavid du Colombier 			return Ebadarg;
568*9ef1f84bSDavid du Colombier 		}
569*9ef1f84bSDavid du Colombier 		ipmuxchain(&chain, mux);
570*9ef1f84bSDavid du Colombier 	}
571*9ef1f84bSDavid du Colombier 	if(chain == nil)
572*9ef1f84bSDavid du Colombier 		return Ebadarg;
573*9ef1f84bSDavid du Colombier 	mux->conv = c;
574*9ef1f84bSDavid du Colombier 
575*9ef1f84bSDavid du Colombier 	/* save a copy of the chain so we can later remove it */
576*9ef1f84bSDavid du Colombier 	mux = ipmuxcopy(chain);
577*9ef1f84bSDavid du Colombier 	r = (Ipmuxrock*)(c->ptcl);
578*9ef1f84bSDavid du Colombier 	r->chain = chain;
579*9ef1f84bSDavid du Colombier 
580*9ef1f84bSDavid du Colombier 	/* add the chain to the protocol demultiplexor tree */
581*9ef1f84bSDavid du Colombier 	wlock(f);
582*9ef1f84bSDavid du Colombier 	f->ipmux->priv = ipmuxmerge(f->ipmux->priv, mux);
583*9ef1f84bSDavid du Colombier 	wunlock(f);
584*9ef1f84bSDavid du Colombier 
585*9ef1f84bSDavid du Colombier 	Fsconnected(c, nil);
586*9ef1f84bSDavid du Colombier 	return nil;
587*9ef1f84bSDavid du Colombier }
588*9ef1f84bSDavid du Colombier 
589*9ef1f84bSDavid du Colombier static int
ipmuxstate(Conv * c,char * state,int n)590*9ef1f84bSDavid du Colombier ipmuxstate(Conv *c, char *state, int n)
591*9ef1f84bSDavid du Colombier {
592*9ef1f84bSDavid du Colombier 	Ipmuxrock *r;
593*9ef1f84bSDavid du Colombier 
594*9ef1f84bSDavid du Colombier 	r = (Ipmuxrock*)(c->ptcl);
595*9ef1f84bSDavid du Colombier 	return ipmuxsprint(r->chain, 0, state, n);
596*9ef1f84bSDavid du Colombier }
597*9ef1f84bSDavid du Colombier 
598*9ef1f84bSDavid du Colombier static void
ipmuxcreate(Conv * c)599*9ef1f84bSDavid du Colombier ipmuxcreate(Conv *c)
600*9ef1f84bSDavid du Colombier {
601*9ef1f84bSDavid du Colombier 	Ipmuxrock *r;
602*9ef1f84bSDavid du Colombier 
603*9ef1f84bSDavid du Colombier 	c->rq = qopen(64*1024, Qmsg, 0, c);
604*9ef1f84bSDavid du Colombier 	c->wq = qopen(64*1024, Qkick, ipmuxkick, c);
605*9ef1f84bSDavid du Colombier 	r = (Ipmuxrock*)(c->ptcl);
606*9ef1f84bSDavid du Colombier 	r->chain = nil;
607*9ef1f84bSDavid du Colombier }
608*9ef1f84bSDavid du Colombier 
609*9ef1f84bSDavid du Colombier static char*
ipmuxannounce(Conv *,char **,int)610*9ef1f84bSDavid du Colombier ipmuxannounce(Conv*, char**, int)
611*9ef1f84bSDavid du Colombier {
612*9ef1f84bSDavid du Colombier 	return "ipmux does not support announce";
613*9ef1f84bSDavid du Colombier }
614*9ef1f84bSDavid du Colombier 
615*9ef1f84bSDavid du Colombier static void
ipmuxclose(Conv * c)616*9ef1f84bSDavid du Colombier ipmuxclose(Conv *c)
617*9ef1f84bSDavid du Colombier {
618*9ef1f84bSDavid du Colombier 	Ipmuxrock *r;
619*9ef1f84bSDavid du Colombier 	Fs *f = c->p->f;
620*9ef1f84bSDavid du Colombier 
621*9ef1f84bSDavid du Colombier 	r = (Ipmuxrock*)(c->ptcl);
622*9ef1f84bSDavid du Colombier 
623*9ef1f84bSDavid du Colombier 	qclose(c->rq);
624*9ef1f84bSDavid du Colombier 	qclose(c->wq);
625*9ef1f84bSDavid du Colombier 	qclose(c->eq);
626*9ef1f84bSDavid du Colombier 	ipmove(c->laddr, IPnoaddr);
627*9ef1f84bSDavid du Colombier 	ipmove(c->raddr, IPnoaddr);
628*9ef1f84bSDavid du Colombier 	c->lport = 0;
629*9ef1f84bSDavid du Colombier 	c->rport = 0;
630*9ef1f84bSDavid du Colombier 
631*9ef1f84bSDavid du Colombier 	wlock(f);
632*9ef1f84bSDavid du Colombier 	ipmuxremove(&(c->p->priv), r->chain);
633*9ef1f84bSDavid du Colombier 	wunlock(f);
634*9ef1f84bSDavid du Colombier 	ipmuxtreefree(r->chain);
635*9ef1f84bSDavid du Colombier 	r->chain = nil;
636*9ef1f84bSDavid du Colombier }
637*9ef1f84bSDavid du Colombier 
638*9ef1f84bSDavid du Colombier /*
639*9ef1f84bSDavid du Colombier  *  takes a fully formed ip packet and just passes it down
640*9ef1f84bSDavid du Colombier  *  the stack
641*9ef1f84bSDavid du Colombier  */
642*9ef1f84bSDavid du Colombier static void
ipmuxkick(void * x)643*9ef1f84bSDavid du Colombier ipmuxkick(void *x)
644*9ef1f84bSDavid du Colombier {
645*9ef1f84bSDavid du Colombier 	Conv *c = x;
646*9ef1f84bSDavid du Colombier 	Block *bp;
647*9ef1f84bSDavid du Colombier 
648*9ef1f84bSDavid du Colombier 	bp = qget(c->wq);
649*9ef1f84bSDavid du Colombier 	if(bp != nil) {
650*9ef1f84bSDavid du Colombier 		Myip4hdr *ih4 = (Myip4hdr*)(bp->rp);
651*9ef1f84bSDavid du Colombier 
652*9ef1f84bSDavid du Colombier 		if((ih4->vihl & 0xF0) != IP_VER6)
653*9ef1f84bSDavid du Colombier 			ipoput4(c->p->f, bp, 0, ih4->ttl, ih4->tos, nil);
654*9ef1f84bSDavid du Colombier 		else
655*9ef1f84bSDavid du Colombier 			ipoput6(c->p->f, bp, 0, ((Ip6hdr*)ih4)->ttl, 0, nil);
656*9ef1f84bSDavid du Colombier 	}
657*9ef1f84bSDavid du Colombier }
658*9ef1f84bSDavid du Colombier 
659*9ef1f84bSDavid du Colombier static void
ipmuxiput(Proto * p,Ipifc * ifc,Block * bp)660*9ef1f84bSDavid du Colombier ipmuxiput(Proto *p, Ipifc *ifc, Block *bp)
661*9ef1f84bSDavid du Colombier {
662*9ef1f84bSDavid du Colombier 	int len, hl;
663*9ef1f84bSDavid du Colombier 	Fs *f = p->f;
664*9ef1f84bSDavid du Colombier 	uchar *m, *h, *v, *e, *ve, *hp;
665*9ef1f84bSDavid du Colombier 	Conv *c;
666*9ef1f84bSDavid du Colombier 	Ipmux *mux;
667*9ef1f84bSDavid du Colombier 	Myip4hdr *ip;
668*9ef1f84bSDavid du Colombier 	Ip6hdr *ip6;
669*9ef1f84bSDavid du Colombier 
670*9ef1f84bSDavid du Colombier 	ip = (Myip4hdr*)bp->rp;
671*9ef1f84bSDavid du Colombier 	hl = (ip->vihl&0x0F)<<2;
672*9ef1f84bSDavid du Colombier 
673*9ef1f84bSDavid du Colombier 	if(p->priv == nil)
674*9ef1f84bSDavid du Colombier 		goto nomatch;
675*9ef1f84bSDavid du Colombier 
676*9ef1f84bSDavid du Colombier 	h = bp->rp;
677*9ef1f84bSDavid du Colombier 	len = BLEN(bp);
678*9ef1f84bSDavid du Colombier 
679*9ef1f84bSDavid du Colombier 	/* run the v4 filter */
680*9ef1f84bSDavid du Colombier 	rlock(f);
681*9ef1f84bSDavid du Colombier 	c = nil;
682*9ef1f84bSDavid du Colombier 	mux = f->ipmux->priv;
683*9ef1f84bSDavid du Colombier 	while(mux != nil){
684*9ef1f84bSDavid du Colombier 		if(mux->eoff > len){
685*9ef1f84bSDavid du Colombier 			mux = mux->no;
686*9ef1f84bSDavid du Colombier 			continue;
687*9ef1f84bSDavid du Colombier 		}
688*9ef1f84bSDavid du Colombier 		hp = h + mux->off + ((int)mux->skiphdr)*hl;
689*9ef1f84bSDavid du Colombier 		switch(mux->ctype){
690*9ef1f84bSDavid du Colombier 		case Cbyte:
691*9ef1f84bSDavid du Colombier 			if(*mux->val == *hp)
692*9ef1f84bSDavid du Colombier 				goto yes;
693*9ef1f84bSDavid du Colombier 			break;
694*9ef1f84bSDavid du Colombier 		case Cmbyte:
695*9ef1f84bSDavid du Colombier 			if((*hp & *mux->mask) == *mux->val)
696*9ef1f84bSDavid du Colombier 				goto yes;
697*9ef1f84bSDavid du Colombier 			break;
698*9ef1f84bSDavid du Colombier 		case Cshort:
699*9ef1f84bSDavid du Colombier 			if(*((ushort*)mux->val) == *(ushort*)hp)
700*9ef1f84bSDavid du Colombier 				goto yes;
701*9ef1f84bSDavid du Colombier 			break;
702*9ef1f84bSDavid du Colombier 		case Cmshort:
703*9ef1f84bSDavid du Colombier 			if((*(ushort*)hp & (*((ushort*)mux->mask))) == *((ushort*)mux->val))
704*9ef1f84bSDavid du Colombier 				goto yes;
705*9ef1f84bSDavid du Colombier 			break;
706*9ef1f84bSDavid du Colombier 		case Clong:
707*9ef1f84bSDavid du Colombier 			if(*((ulong*)mux->val) == *(ulong*)hp)
708*9ef1f84bSDavid du Colombier 				goto yes;
709*9ef1f84bSDavid du Colombier 			break;
710*9ef1f84bSDavid du Colombier 		case Cmlong:
711*9ef1f84bSDavid du Colombier 			if((*(ulong*)hp & (*((ulong*)mux->mask))) == *((ulong*)mux->val))
712*9ef1f84bSDavid du Colombier 				goto yes;
713*9ef1f84bSDavid du Colombier 			break;
714*9ef1f84bSDavid du Colombier 		case Cifc:
715*9ef1f84bSDavid du Colombier 			if(*((ulong*)mux->val) == *(ulong*)(ifc->lifc->local + IPv4off))
716*9ef1f84bSDavid du Colombier 				goto yes;
717*9ef1f84bSDavid du Colombier 			break;
718*9ef1f84bSDavid du Colombier 		case Cmifc:
719*9ef1f84bSDavid du Colombier 			if((*(ulong*)(ifc->lifc->local + IPv4off) & (*((ulong*)mux->mask))) == *((ulong*)mux->val))
720*9ef1f84bSDavid du Colombier 				goto yes;
721*9ef1f84bSDavid du Colombier 			break;
722*9ef1f84bSDavid du Colombier 		default:
723*9ef1f84bSDavid du Colombier 			v = mux->val;
724*9ef1f84bSDavid du Colombier 			for(e = mux->e; v < e; v = ve){
725*9ef1f84bSDavid du Colombier 				m = mux->mask;
726*9ef1f84bSDavid du Colombier 				hp = h + mux->off;
727*9ef1f84bSDavid du Colombier 				for(ve = v + mux->len; v < ve; v++){
728*9ef1f84bSDavid du Colombier 					if((*hp++ & *m++) != *v)
729*9ef1f84bSDavid du Colombier 						break;
730*9ef1f84bSDavid du Colombier 				}
731*9ef1f84bSDavid du Colombier 				if(v == ve)
732*9ef1f84bSDavid du Colombier 					goto yes;
733*9ef1f84bSDavid du Colombier 			}
734*9ef1f84bSDavid du Colombier 		}
735*9ef1f84bSDavid du Colombier 		mux = mux->no;
736*9ef1f84bSDavid du Colombier 		continue;
737*9ef1f84bSDavid du Colombier yes:
738*9ef1f84bSDavid du Colombier 		if(mux->conv != nil)
739*9ef1f84bSDavid du Colombier 			c = mux->conv;
740*9ef1f84bSDavid du Colombier 		mux = mux->yes;
741*9ef1f84bSDavid du Colombier 	}
742*9ef1f84bSDavid du Colombier 	runlock(f);
743*9ef1f84bSDavid du Colombier 
744*9ef1f84bSDavid du Colombier 	if(c != nil){
745*9ef1f84bSDavid du Colombier 		/* tack on interface address */
746*9ef1f84bSDavid du Colombier 		bp = padblock(bp, IPaddrlen);
747*9ef1f84bSDavid du Colombier 		ipmove(bp->rp, ifc->lifc->local);
748*9ef1f84bSDavid du Colombier 		bp = concatblock(bp);
749*9ef1f84bSDavid du Colombier 		if(bp != nil)
750*9ef1f84bSDavid du Colombier 			if(qpass(c->rq, bp) < 0)
751*9ef1f84bSDavid du Colombier 				print("Q");
752*9ef1f84bSDavid du Colombier 		return;
753*9ef1f84bSDavid du Colombier 	}
754*9ef1f84bSDavid du Colombier 
755*9ef1f84bSDavid du Colombier nomatch:
756*9ef1f84bSDavid du Colombier 	/* doesn't match any filter, hand it to the specific protocol handler */
757*9ef1f84bSDavid du Colombier 	ip = (Myip4hdr*)bp->rp;
758*9ef1f84bSDavid du Colombier 	if((ip->vihl & 0xF0) == IP_VER4) {
759*9ef1f84bSDavid du Colombier 		p = f->t2p[ip->proto];
760*9ef1f84bSDavid du Colombier 	} else {
761*9ef1f84bSDavid du Colombier 		ip6 = (Ip6hdr*)bp->rp;
762*9ef1f84bSDavid du Colombier 		p = f->t2p[ip6->proto];
763*9ef1f84bSDavid du Colombier 	}
764*9ef1f84bSDavid du Colombier 	if(p && p->rcv)
765*9ef1f84bSDavid du Colombier 		(*p->rcv)(p, ifc, bp);
766*9ef1f84bSDavid du Colombier 	else
767*9ef1f84bSDavid du Colombier 		freeblist(bp);
768*9ef1f84bSDavid du Colombier 	return;
769*9ef1f84bSDavid du Colombier }
770*9ef1f84bSDavid du Colombier 
771*9ef1f84bSDavid du Colombier static int
ipmuxsprint(Ipmux * mux,int level,char * buf,int len)772*9ef1f84bSDavid du Colombier ipmuxsprint(Ipmux *mux, int level, char *buf, int len)
773*9ef1f84bSDavid du Colombier {
774*9ef1f84bSDavid du Colombier 	int i, j, n;
775*9ef1f84bSDavid du Colombier 	uchar *v;
776*9ef1f84bSDavid du Colombier 
777*9ef1f84bSDavid du Colombier 	n = 0;
778*9ef1f84bSDavid du Colombier 	for(i = 0; i < level; i++)
779*9ef1f84bSDavid du Colombier 		n += snprint(buf+n, len-n, " ");
780*9ef1f84bSDavid du Colombier 	if(mux == nil){
781*9ef1f84bSDavid du Colombier 		n += snprint(buf+n, len-n, "\n");
782*9ef1f84bSDavid du Colombier 		return n;
783*9ef1f84bSDavid du Colombier 	}
784*9ef1f84bSDavid du Colombier 	n += snprint(buf+n, len-n, "h[%lld:%lld]&",
785*9ef1f84bSDavid du Colombier                mux->off+((int)mux->skiphdr)*((uintptr)ipoff->data),
786*9ef1f84bSDavid du Colombier                mux->off+(((int)mux->skiphdr)*((uintptr)ipoff->data))+mux->len-1);
787*9ef1f84bSDavid du Colombier 	for(i = 0; i < mux->len; i++)
788*9ef1f84bSDavid du Colombier 		n += snprint(buf+n, len - n, "%2.2ux", mux->mask[i]);
789*9ef1f84bSDavid du Colombier 	n += snprint(buf+n, len-n, "=");
790*9ef1f84bSDavid du Colombier 	v = mux->val;
791*9ef1f84bSDavid du Colombier 	for(j = 0; j < mux->n; j++){
792*9ef1f84bSDavid du Colombier 		for(i = 0; i < mux->len; i++)
793*9ef1f84bSDavid du Colombier 			n += snprint(buf+n, len - n, "%2.2ux", *v++);
794*9ef1f84bSDavid du Colombier 		n += snprint(buf+n, len-n, "|");
795*9ef1f84bSDavid du Colombier 	}
796*9ef1f84bSDavid du Colombier 	n += snprint(buf+n, len-n, "\n");
797*9ef1f84bSDavid du Colombier 	level++;
798*9ef1f84bSDavid du Colombier 	n += ipmuxsprint(mux->no, level, buf+n, len-n);
799*9ef1f84bSDavid du Colombier 	n += ipmuxsprint(mux->yes, level, buf+n, len-n);
800*9ef1f84bSDavid du Colombier 	return n;
801*9ef1f84bSDavid du Colombier }
802*9ef1f84bSDavid du Colombier 
803*9ef1f84bSDavid du Colombier static int
ipmuxstats(Proto * p,char * buf,int len)804*9ef1f84bSDavid du Colombier ipmuxstats(Proto *p, char *buf, int len)
805*9ef1f84bSDavid du Colombier {
806*9ef1f84bSDavid du Colombier 	int n;
807*9ef1f84bSDavid du Colombier 	Fs *f = p->f;
808*9ef1f84bSDavid du Colombier 
809*9ef1f84bSDavid du Colombier 	rlock(f);
810*9ef1f84bSDavid du Colombier 	n = ipmuxsprint(p->priv, 0, buf, len);
811*9ef1f84bSDavid du Colombier 	runlock(f);
812*9ef1f84bSDavid du Colombier 
813*9ef1f84bSDavid du Colombier 	return n;
814*9ef1f84bSDavid du Colombier }
815*9ef1f84bSDavid du Colombier 
816*9ef1f84bSDavid du Colombier void
ipmuxinit(Fs * f)817*9ef1f84bSDavid du Colombier ipmuxinit(Fs *f)
818*9ef1f84bSDavid du Colombier {
819*9ef1f84bSDavid du Colombier 	Proto *ipmux;
820*9ef1f84bSDavid du Colombier 
821*9ef1f84bSDavid du Colombier 	ipmux = smalloc(sizeof(Proto));
822*9ef1f84bSDavid du Colombier 	ipmux->priv = nil;
823*9ef1f84bSDavid du Colombier 	ipmux->name = "ipmux";
824*9ef1f84bSDavid du Colombier 	ipmux->connect = ipmuxconnect;
825*9ef1f84bSDavid du Colombier 	ipmux->announce = ipmuxannounce;
826*9ef1f84bSDavid du Colombier 	ipmux->state = ipmuxstate;
827*9ef1f84bSDavid du Colombier 	ipmux->create = ipmuxcreate;
828*9ef1f84bSDavid du Colombier 	ipmux->close = ipmuxclose;
829*9ef1f84bSDavid du Colombier 	ipmux->rcv = ipmuxiput;
830*9ef1f84bSDavid du Colombier 	ipmux->ctl = nil;
831*9ef1f84bSDavid du Colombier 	ipmux->advise = nil;
832*9ef1f84bSDavid du Colombier 	ipmux->stats = ipmuxstats;
833*9ef1f84bSDavid du Colombier 	ipmux->ipproto = -1;
834*9ef1f84bSDavid du Colombier 	ipmux->nc = 64;
835*9ef1f84bSDavid du Colombier 	ipmux->ptclsize = sizeof(Ipmuxrock);
836*9ef1f84bSDavid du Colombier 
837*9ef1f84bSDavid du Colombier 	f->ipmux = ipmux;			/* hack for Fsrcvpcol */
838*9ef1f84bSDavid du Colombier 
839*9ef1f84bSDavid du Colombier 	Fsproto(f, ipmux);
840*9ef1f84bSDavid du Colombier }
841