xref: /plan9-contrib/sys/src/9/pc/wifi.c (revision 43f728cba48694b0a42c538a38fdf975a0ae5433)
1*43f728cbSDavid du Colombier #include "u.h"
2*43f728cbSDavid du Colombier #include "../port/lib.h"
3*43f728cbSDavid du Colombier #include "mem.h"
4*43f728cbSDavid du Colombier #include "dat.h"
5*43f728cbSDavid du Colombier #include "fns.h"
6*43f728cbSDavid du Colombier #include "io.h"
7*43f728cbSDavid du Colombier #include "ureg.h"
8*43f728cbSDavid du Colombier #include "../port/error.h"
9*43f728cbSDavid du Colombier #include "../port/netif.h"
10*43f728cbSDavid du Colombier 
11*43f728cbSDavid du Colombier #include "etherif.h"
12*43f728cbSDavid du Colombier #include "wifi.h"
13*43f728cbSDavid du Colombier 
14*43f728cbSDavid du Colombier #include <libsec.h>
15*43f728cbSDavid du Colombier 
16*43f728cbSDavid du Colombier typedef struct SNAP SNAP;
17*43f728cbSDavid du Colombier struct SNAP
18*43f728cbSDavid du Colombier {
19*43f728cbSDavid du Colombier 	uchar	dsap;
20*43f728cbSDavid du Colombier 	uchar	ssap;
21*43f728cbSDavid du Colombier 	uchar	control;
22*43f728cbSDavid du Colombier 	uchar	orgcode[3];
23*43f728cbSDavid du Colombier 	uchar	type[2];
24*43f728cbSDavid du Colombier };
25*43f728cbSDavid du Colombier 
26*43f728cbSDavid du Colombier enum {
27*43f728cbSDavid du Colombier 	WIFIHDRSIZE = 2+2+3*6+2,
28*43f728cbSDavid du Colombier 	SNAPHDRSIZE = 8,
29*43f728cbSDavid du Colombier };
30*43f728cbSDavid du Colombier 
31*43f728cbSDavid du Colombier static char Sconn[] = "connecting";
32*43f728cbSDavid du Colombier static char Sauth[] = "authenticated";
33*43f728cbSDavid du Colombier static char Sneedauth[] = "need authentication";
34*43f728cbSDavid du Colombier static char Sunauth[] = "unauthenticated";
35*43f728cbSDavid du Colombier 
36*43f728cbSDavid du Colombier static char Sassoc[] = "associated";
37*43f728cbSDavid du Colombier static char Sunassoc[] = "unassociated";
38*43f728cbSDavid du Colombier static char Sblocked[] = "blocked";	/* no keys negotiated. only pass EAPOL frames */
39*43f728cbSDavid du Colombier 
40*43f728cbSDavid du Colombier static uchar basicrates[] = {
41*43f728cbSDavid du Colombier 	0x80 | 2,	/* 1.0	Mb/s */
42*43f728cbSDavid du Colombier 	0x80 | 4,	/* 2.0	Mb/s */
43*43f728cbSDavid du Colombier 	0x80 | 11,	/* 5.5	Mb/s */
44*43f728cbSDavid du Colombier 	0x80 | 22,	/* 11.0	Mb/s */
45*43f728cbSDavid du Colombier 
46*43f728cbSDavid du Colombier 	0
47*43f728cbSDavid du Colombier };
48*43f728cbSDavid du Colombier 
49*43f728cbSDavid du Colombier static Block* wifidecrypt(Wifi *, Wnode *, Block *);
50*43f728cbSDavid du Colombier static Block* wifiencrypt(Wifi *, Wnode *, Block *);
51*43f728cbSDavid du Colombier static void freewifikeys(Wifi *, Wnode *);
52*43f728cbSDavid du Colombier 
53*43f728cbSDavid du Colombier static uchar*
srcaddr(Wifipkt * w)54*43f728cbSDavid du Colombier srcaddr(Wifipkt *w)
55*43f728cbSDavid du Colombier {
56*43f728cbSDavid du Colombier 	if((w->fc[1] & 0x02) == 0)
57*43f728cbSDavid du Colombier 		return w->a2;
58*43f728cbSDavid du Colombier 	if((w->fc[1] & 0x01) == 0)
59*43f728cbSDavid du Colombier 		return w->a3;
60*43f728cbSDavid du Colombier 	return w->a4;
61*43f728cbSDavid du Colombier }
62*43f728cbSDavid du Colombier static uchar*
dstaddr(Wifipkt * w)63*43f728cbSDavid du Colombier dstaddr(Wifipkt *w)
64*43f728cbSDavid du Colombier {
65*43f728cbSDavid du Colombier 	if((w->fc[1] & 0x01) != 0)
66*43f728cbSDavid du Colombier 		return w->a3;
67*43f728cbSDavid du Colombier 	return w->a1;
68*43f728cbSDavid du Colombier }
69*43f728cbSDavid du Colombier 
70*43f728cbSDavid du Colombier int
wifihdrlen(Wifipkt * w)71*43f728cbSDavid du Colombier wifihdrlen(Wifipkt *w)
72*43f728cbSDavid du Colombier {
73*43f728cbSDavid du Colombier 	int n;
74*43f728cbSDavid du Colombier 
75*43f728cbSDavid du Colombier 	n = WIFIHDRSIZE;
76*43f728cbSDavid du Colombier 	if((w->fc[0] & 0x0c) == 0x08)
77*43f728cbSDavid du Colombier 		if((w->fc[0] & 0xf0) == 0x80){	/* QOS */
78*43f728cbSDavid du Colombier 			n += 2;
79*43f728cbSDavid du Colombier 			if(w->fc[1] & 0x80)
80*43f728cbSDavid du Colombier 				n += 4;
81*43f728cbSDavid du Colombier 		}
82*43f728cbSDavid du Colombier 	if((w->fc[1] & 3) == 0x03)
83*43f728cbSDavid du Colombier 		n += Eaddrlen;
84*43f728cbSDavid du Colombier 	return n;
85*43f728cbSDavid du Colombier }
86*43f728cbSDavid du Colombier 
87*43f728cbSDavid du Colombier void
wifiiq(Wifi * wifi,Block * b)88*43f728cbSDavid du Colombier wifiiq(Wifi *wifi, Block *b)
89*43f728cbSDavid du Colombier {
90*43f728cbSDavid du Colombier 	SNAP s;
91*43f728cbSDavid du Colombier 	Wifipkt h, *w;
92*43f728cbSDavid du Colombier 	Etherpkt *e;
93*43f728cbSDavid du Colombier 	int hdrlen;
94*43f728cbSDavid du Colombier 
95*43f728cbSDavid du Colombier 	if(BLEN(b) < WIFIHDRSIZE)
96*43f728cbSDavid du Colombier 		goto drop;
97*43f728cbSDavid du Colombier 	w = (Wifipkt*)b->rp;
98*43f728cbSDavid du Colombier 	hdrlen = wifihdrlen(w);
99*43f728cbSDavid du Colombier 	if(BLEN(b) < hdrlen)
100*43f728cbSDavid du Colombier 		goto drop;
101*43f728cbSDavid du Colombier 	if(w->fc[1] & 0x40){
102*43f728cbSDavid du Colombier 		/* encrypted */
103*43f728cbSDavid du Colombier 		qpass(wifi->iq, b);
104*43f728cbSDavid du Colombier 		return;
105*43f728cbSDavid du Colombier 	}
106*43f728cbSDavid du Colombier 	switch(w->fc[0] & 0x0c){
107*43f728cbSDavid du Colombier 	case 0x00:	/* management */
108*43f728cbSDavid du Colombier 		if((w->fc[1] & 3) != 0x00)	/* STA->STA */
109*43f728cbSDavid du Colombier 			break;
110*43f728cbSDavid du Colombier 		qpass(wifi->iq, b);
111*43f728cbSDavid du Colombier 		return;
112*43f728cbSDavid du Colombier 	case 0x04:	/* control */
113*43f728cbSDavid du Colombier 		break;
114*43f728cbSDavid du Colombier 	case 0x08:	/* data */
115*43f728cbSDavid du Colombier 		b->rp += hdrlen;
116*43f728cbSDavid du Colombier 		switch(w->fc[0] & 0xf0){
117*43f728cbSDavid du Colombier 		default:
118*43f728cbSDavid du Colombier 			goto drop;
119*43f728cbSDavid du Colombier 		case 0x80:	/* QOS */
120*43f728cbSDavid du Colombier 		case 0x00:
121*43f728cbSDavid du Colombier 			break;
122*43f728cbSDavid du Colombier 		}
123*43f728cbSDavid du Colombier 		if(BLEN(b) < SNAPHDRSIZE)
124*43f728cbSDavid du Colombier 			break;
125*43f728cbSDavid du Colombier 		memmove(&s, b->rp, SNAPHDRSIZE);
126*43f728cbSDavid du Colombier 		if(s.dsap != 0xAA || s.ssap != 0xAA || s.control != 3)
127*43f728cbSDavid du Colombier 			break;
128*43f728cbSDavid du Colombier 		if(s.orgcode[0] != 0 || s.orgcode[1] != 0 || s.orgcode[2] != 0)
129*43f728cbSDavid du Colombier 			break;
130*43f728cbSDavid du Colombier 		b->rp += SNAPHDRSIZE-ETHERHDRSIZE;
131*43f728cbSDavid du Colombier 		h = *w;
132*43f728cbSDavid du Colombier 		e = (Etherpkt*)b->rp;
133*43f728cbSDavid du Colombier 		memmove(e->d, dstaddr(&h), Eaddrlen);
134*43f728cbSDavid du Colombier 		memmove(e->s, srcaddr(&h), Eaddrlen);
135*43f728cbSDavid du Colombier 		memmove(e->type, s.type, 2);
136*43f728cbSDavid du Colombier 		etheriq(wifi->ether, b, 1);
137*43f728cbSDavid du Colombier 		return;
138*43f728cbSDavid du Colombier 	}
139*43f728cbSDavid du Colombier drop:
140*43f728cbSDavid du Colombier 	freeb(b);
141*43f728cbSDavid du Colombier }
142*43f728cbSDavid du Colombier 
143*43f728cbSDavid du Colombier static void
wifitx(Wifi * wifi,Wnode * wn,Block * b)144*43f728cbSDavid du Colombier wifitx(Wifi *wifi, Wnode *wn, Block *b)
145*43f728cbSDavid du Colombier {
146*43f728cbSDavid du Colombier 	Wifipkt *w;
147*43f728cbSDavid du Colombier 	uint seq;
148*43f728cbSDavid du Colombier 
149*43f728cbSDavid du Colombier 	wn->lastsend = MACHP(0)->ticks;
150*43f728cbSDavid du Colombier 
151*43f728cbSDavid du Colombier 	seq = incref(&wifi->txseq);
152*43f728cbSDavid du Colombier 	seq <<= 4;
153*43f728cbSDavid du Colombier 
154*43f728cbSDavid du Colombier 	w = (Wifipkt*)b->rp;
155*43f728cbSDavid du Colombier 	w->dur[0] = 0;
156*43f728cbSDavid du Colombier 	w->dur[1] = 0;
157*43f728cbSDavid du Colombier 	w->seq[0] = seq;
158*43f728cbSDavid du Colombier 	w->seq[1] = seq>>8;
159*43f728cbSDavid du Colombier 
160*43f728cbSDavid du Colombier 	if((w->fc[0] & 0x0c) != 0x00){
161*43f728cbSDavid du Colombier 		b = wifiencrypt(wifi, wn, b);
162*43f728cbSDavid du Colombier 		if(b == nil)
163*43f728cbSDavid du Colombier 			return;
164*43f728cbSDavid du Colombier 	}
165*43f728cbSDavid du Colombier 
166*43f728cbSDavid du Colombier 	if((wn->txcount++ & 255) == 255){
167*43f728cbSDavid du Colombier 		if(wn->actrate != nil && wn->actrate < wn->maxrate)
168*43f728cbSDavid du Colombier 			wn->actrate++;
169*43f728cbSDavid du Colombier 	}
170*43f728cbSDavid du Colombier 
171*43f728cbSDavid du Colombier 	(*wifi->transmit)(wifi, wn, b);
172*43f728cbSDavid du Colombier }
173*43f728cbSDavid du Colombier 
174*43f728cbSDavid du Colombier static Wnode*
nodelookup(Wifi * wifi,uchar * bssid,int new)175*43f728cbSDavid du Colombier nodelookup(Wifi *wifi, uchar *bssid, int new)
176*43f728cbSDavid du Colombier {
177*43f728cbSDavid du Colombier 	Wnode *wn, *nn;
178*43f728cbSDavid du Colombier 
179*43f728cbSDavid du Colombier 	if(memcmp(bssid, wifi->ether->bcast, Eaddrlen) == 0)
180*43f728cbSDavid du Colombier 		return nil;
181*43f728cbSDavid du Colombier 	if((wn = wifi->bss) != nil){
182*43f728cbSDavid du Colombier 		if(memcmp(wn->bssid, bssid, Eaddrlen) == 0){
183*43f728cbSDavid du Colombier 			wn->lastseen = MACHP(0)->ticks;
184*43f728cbSDavid du Colombier 			return wn;
185*43f728cbSDavid du Colombier 		}
186*43f728cbSDavid du Colombier 	}
187*43f728cbSDavid du Colombier 	if((nn = wifi->node) == wn)
188*43f728cbSDavid du Colombier 		nn++;
189*43f728cbSDavid du Colombier 	for(wn = wifi->node; wn != &wifi->node[nelem(wifi->node)]; wn++){
190*43f728cbSDavid du Colombier 		if(wn == wifi->bss)
191*43f728cbSDavid du Colombier 			continue;
192*43f728cbSDavid du Colombier 		if(memcmp(wn->bssid, bssid, Eaddrlen) == 0){
193*43f728cbSDavid du Colombier 			wn->lastseen = MACHP(0)->ticks;
194*43f728cbSDavid du Colombier 			return wn;
195*43f728cbSDavid du Colombier 		}
196*43f728cbSDavid du Colombier 		if((long)(wn->lastseen - nn->lastseen) < 0)
197*43f728cbSDavid du Colombier 			nn = wn;
198*43f728cbSDavid du Colombier 	}
199*43f728cbSDavid du Colombier 	if(!new)
200*43f728cbSDavid du Colombier 		return nil;
201*43f728cbSDavid du Colombier 	freewifikeys(wifi, nn);
202*43f728cbSDavid du Colombier 	memset(nn, 0, sizeof(Wnode));
203*43f728cbSDavid du Colombier 	memmove(nn->bssid, bssid, Eaddrlen);
204*43f728cbSDavid du Colombier 	nn->lastseen = MACHP(0)->ticks;
205*43f728cbSDavid du Colombier 	return nn;
206*43f728cbSDavid du Colombier }
207*43f728cbSDavid du Colombier 
208*43f728cbSDavid du Colombier void
wifitxfail(Wifi * wifi,Block * b)209*43f728cbSDavid du Colombier wifitxfail(Wifi *wifi, Block *b)
210*43f728cbSDavid du Colombier {
211*43f728cbSDavid du Colombier 	Wifipkt *w;
212*43f728cbSDavid du Colombier 	Wnode *wn;
213*43f728cbSDavid du Colombier 
214*43f728cbSDavid du Colombier 	if(b == nil)
215*43f728cbSDavid du Colombier 		return;
216*43f728cbSDavid du Colombier 	w = (Wifipkt*)b->rp;
217*43f728cbSDavid du Colombier 	wn = nodelookup(wifi, w->a1, 0);
218*43f728cbSDavid du Colombier 	if(wn == nil)
219*43f728cbSDavid du Colombier 		return;
220*43f728cbSDavid du Colombier 	wn->txerror++;
221*43f728cbSDavid du Colombier 	if(wn->actrate != nil && wn->actrate > wn->minrate)
222*43f728cbSDavid du Colombier 		wn->actrate--;
223*43f728cbSDavid du Colombier }
224*43f728cbSDavid du Colombier 
225*43f728cbSDavid du Colombier static uchar*
putrates(uchar * p,uchar * rates)226*43f728cbSDavid du Colombier putrates(uchar *p, uchar *rates)
227*43f728cbSDavid du Colombier {
228*43f728cbSDavid du Colombier 	int n, m;
229*43f728cbSDavid du Colombier 
230*43f728cbSDavid du Colombier 	n = m = strlen((char*)rates);
231*43f728cbSDavid du Colombier 	if(n > 8)
232*43f728cbSDavid du Colombier 		n = 8;
233*43f728cbSDavid du Colombier 	/* supported rates */
234*43f728cbSDavid du Colombier 	*p++ = 1;
235*43f728cbSDavid du Colombier 	*p++ = n;
236*43f728cbSDavid du Colombier 	memmove(p, rates, n);
237*43f728cbSDavid du Colombier 	p += n;
238*43f728cbSDavid du Colombier 	if(m > 8){
239*43f728cbSDavid du Colombier 		/* extended supported rates */
240*43f728cbSDavid du Colombier 		*p++ = 50;
241*43f728cbSDavid du Colombier 		*p++ = m;
242*43f728cbSDavid du Colombier 		memmove(p, rates, m);
243*43f728cbSDavid du Colombier 		p += m;
244*43f728cbSDavid du Colombier 	}
245*43f728cbSDavid du Colombier 	return p;
246*43f728cbSDavid du Colombier }
247*43f728cbSDavid du Colombier 
248*43f728cbSDavid du Colombier static void
wifiprobe(Wifi * wifi,Wnode * wn)249*43f728cbSDavid du Colombier wifiprobe(Wifi *wifi, Wnode *wn)
250*43f728cbSDavid du Colombier {
251*43f728cbSDavid du Colombier 	Wifipkt *w;
252*43f728cbSDavid du Colombier 	Block *b;
253*43f728cbSDavid du Colombier 	uchar *p;
254*43f728cbSDavid du Colombier 	int n;
255*43f728cbSDavid du Colombier 
256*43f728cbSDavid du Colombier 	n = strlen(wifi->essid);
257*43f728cbSDavid du Colombier 	if(n == 0){
258*43f728cbSDavid du Colombier 		/* no specific essid, just tell driver to tune channel */
259*43f728cbSDavid du Colombier 		(*wifi->transmit)(wifi, wn, nil);
260*43f728cbSDavid du Colombier 		return;
261*43f728cbSDavid du Colombier 	}
262*43f728cbSDavid du Colombier 
263*43f728cbSDavid du Colombier 	b = allocb(WIFIHDRSIZE + 512);
264*43f728cbSDavid du Colombier 	w = (Wifipkt*)b->wp;
265*43f728cbSDavid du Colombier 	w->fc[0] = 0x40;	/* probe request */
266*43f728cbSDavid du Colombier 	w->fc[1] = 0x00;	/* STA->STA */
267*43f728cbSDavid du Colombier 	memmove(w->a1, wifi->ether->bcast, Eaddrlen);	/* ??? */
268*43f728cbSDavid du Colombier 	memmove(w->a2, wifi->ether->ea, Eaddrlen);
269*43f728cbSDavid du Colombier 	memmove(w->a3, wifi->ether->bcast, Eaddrlen);
270*43f728cbSDavid du Colombier 	b->wp += WIFIHDRSIZE;
271*43f728cbSDavid du Colombier 	p = b->wp;
272*43f728cbSDavid du Colombier 
273*43f728cbSDavid du Colombier 	*p++ = 0;	/* set */
274*43f728cbSDavid du Colombier 	*p++ = n;
275*43f728cbSDavid du Colombier 	memmove(p, wifi->essid, n);
276*43f728cbSDavid du Colombier 	p += n;
277*43f728cbSDavid du Colombier 
278*43f728cbSDavid du Colombier 	p = putrates(p, wifi->rates);
279*43f728cbSDavid du Colombier 
280*43f728cbSDavid du Colombier 	*p++ = 3;	/* ds parameter set */
281*43f728cbSDavid du Colombier 	*p++ = 1;
282*43f728cbSDavid du Colombier 	*p++ = wn->channel;
283*43f728cbSDavid du Colombier 
284*43f728cbSDavid du Colombier 	b->wp = p;
285*43f728cbSDavid du Colombier 	wifitx(wifi, wn, b);
286*43f728cbSDavid du Colombier }
287*43f728cbSDavid du Colombier 
288*43f728cbSDavid du Colombier static void
sendauth(Wifi * wifi,Wnode * bss)289*43f728cbSDavid du Colombier sendauth(Wifi *wifi, Wnode *bss)
290*43f728cbSDavid du Colombier {
291*43f728cbSDavid du Colombier 	Wifipkt *w;
292*43f728cbSDavid du Colombier 	Block *b;
293*43f728cbSDavid du Colombier 	uchar *p;
294*43f728cbSDavid du Colombier 
295*43f728cbSDavid du Colombier 	b = allocb(WIFIHDRSIZE + 3*2);
296*43f728cbSDavid du Colombier 	w = (Wifipkt*)b->wp;
297*43f728cbSDavid du Colombier 	w->fc[0] = 0xB0;	/* auth request */
298*43f728cbSDavid du Colombier 	w->fc[1] = 0x00;	/* STA->STA */
299*43f728cbSDavid du Colombier 	memmove(w->a1, bss->bssid, Eaddrlen);	/* ??? */
300*43f728cbSDavid du Colombier 	memmove(w->a2, wifi->ether->ea, Eaddrlen);
301*43f728cbSDavid du Colombier 	memmove(w->a3, bss->bssid, Eaddrlen);
302*43f728cbSDavid du Colombier 	b->wp += WIFIHDRSIZE;
303*43f728cbSDavid du Colombier 	p = b->wp;
304*43f728cbSDavid du Colombier 	*p++ = 0;	/* alg */
305*43f728cbSDavid du Colombier 	*p++ = 0;
306*43f728cbSDavid du Colombier 	*p++ = 1;	/* seq */
307*43f728cbSDavid du Colombier 	*p++ = 0;
308*43f728cbSDavid du Colombier 	*p++ = 0;	/* status */
309*43f728cbSDavid du Colombier 	*p++ = 0;
310*43f728cbSDavid du Colombier 	b->wp = p;
311*43f728cbSDavid du Colombier 
312*43f728cbSDavid du Colombier 	bss->aid = 0;
313*43f728cbSDavid du Colombier 
314*43f728cbSDavid du Colombier 	wifitx(wifi, bss, b);
315*43f728cbSDavid du Colombier }
316*43f728cbSDavid du Colombier 
317*43f728cbSDavid du Colombier static void
sendassoc(Wifi * wifi,Wnode * bss)318*43f728cbSDavid du Colombier sendassoc(Wifi *wifi, Wnode *bss)
319*43f728cbSDavid du Colombier {
320*43f728cbSDavid du Colombier 	Wifipkt *w;
321*43f728cbSDavid du Colombier 	Block *b;
322*43f728cbSDavid du Colombier 	uchar *p;
323*43f728cbSDavid du Colombier 	int cap, n;
324*43f728cbSDavid du Colombier 
325*43f728cbSDavid du Colombier 	b = allocb(WIFIHDRSIZE + 512);
326*43f728cbSDavid du Colombier 	w = (Wifipkt*)b->wp;
327*43f728cbSDavid du Colombier 	w->fc[0] = 0x00;	/* assoc request */
328*43f728cbSDavid du Colombier 	w->fc[1] = 0x00;	/* STA->STA */
329*43f728cbSDavid du Colombier 	memmove(w->a1, bss->bssid, Eaddrlen);	/* ??? */
330*43f728cbSDavid du Colombier 	memmove(w->a2, wifi->ether->ea, Eaddrlen);
331*43f728cbSDavid du Colombier 	memmove(w->a3, bss->bssid, Eaddrlen);
332*43f728cbSDavid du Colombier 	b->wp += WIFIHDRSIZE;
333*43f728cbSDavid du Colombier 	p = b->wp;
334*43f728cbSDavid du Colombier 
335*43f728cbSDavid du Colombier 	/* capinfo */
336*43f728cbSDavid du Colombier 	cap = 1;				// ESS
337*43f728cbSDavid du Colombier 	cap |= (1<<5);				// Short Preamble
338*43f728cbSDavid du Colombier 	cap |= (1<<10) & bss->cap;		// Short Slot Time
339*43f728cbSDavid du Colombier 	*p++ = cap;
340*43f728cbSDavid du Colombier 	*p++ = cap>>8;
341*43f728cbSDavid du Colombier 
342*43f728cbSDavid du Colombier 	/* interval */
343*43f728cbSDavid du Colombier 	*p++ = 16;
344*43f728cbSDavid du Colombier 	*p++ = 16>>8;
345*43f728cbSDavid du Colombier 
346*43f728cbSDavid du Colombier 	n = strlen(bss->ssid);
347*43f728cbSDavid du Colombier 	*p++ = 0;	/* SSID */
348*43f728cbSDavid du Colombier 	*p++ = n;
349*43f728cbSDavid du Colombier 	memmove(p, bss->ssid, n);
350*43f728cbSDavid du Colombier 	p += n;
351*43f728cbSDavid du Colombier 
352*43f728cbSDavid du Colombier 	p = putrates(p, wifi->rates);
353*43f728cbSDavid du Colombier 
354*43f728cbSDavid du Colombier 	n = bss->rsnelen;
355*43f728cbSDavid du Colombier 	if(n > 0){
356*43f728cbSDavid du Colombier 		memmove(p, bss->rsne, n);
357*43f728cbSDavid du Colombier 		p += n;
358*43f728cbSDavid du Colombier 	}
359*43f728cbSDavid du Colombier 
360*43f728cbSDavid du Colombier 	b->wp = p;
361*43f728cbSDavid du Colombier 	wifitx(wifi, bss, b);
362*43f728cbSDavid du Colombier }
363*43f728cbSDavid du Colombier 
364*43f728cbSDavid du Colombier static void
setstatus(Wifi * wifi,Wnode * wn,char * new)365*43f728cbSDavid du Colombier setstatus(Wifi *wifi, Wnode *wn, char *new)
366*43f728cbSDavid du Colombier {
367*43f728cbSDavid du Colombier 	char *old;
368*43f728cbSDavid du Colombier 
369*43f728cbSDavid du Colombier 	old = wn->status;
370*43f728cbSDavid du Colombier 	wn->status = new;
371*43f728cbSDavid du Colombier 	if(wifi->debug && new != old)
372*43f728cbSDavid du Colombier 		print("#l%d: status %E: %.12ld %.12ld: %s -> %s (from pc=%#p)\n",
373*43f728cbSDavid du Colombier 			wifi->ether->ctlrno,
374*43f728cbSDavid du Colombier 			wn->bssid,
375*43f728cbSDavid du Colombier 			TK2MS(MACHP(0)->ticks), TK2MS(MACHP(0)->ticks - wn->lastsend),
376*43f728cbSDavid du Colombier 			old, new,
377*43f728cbSDavid du Colombier 			getcallerpc(&wifi));
378*43f728cbSDavid du Colombier }
379*43f728cbSDavid du Colombier 
380*43f728cbSDavid du Colombier static void
recvassoc(Wifi * wifi,Wnode * wn,uchar * d,int len)381*43f728cbSDavid du Colombier recvassoc(Wifi *wifi, Wnode *wn, uchar *d, int len)
382*43f728cbSDavid du Colombier {
383*43f728cbSDavid du Colombier 	uint s;
384*43f728cbSDavid du Colombier 
385*43f728cbSDavid du Colombier 	if(len < 2+2+2)
386*43f728cbSDavid du Colombier 		return;
387*43f728cbSDavid du Colombier 
388*43f728cbSDavid du Colombier 	d += 2;	/* caps */
389*43f728cbSDavid du Colombier 	s = d[0] | d[1]<<8;
390*43f728cbSDavid du Colombier 	d += 2;
391*43f728cbSDavid du Colombier 	switch(s){
392*43f728cbSDavid du Colombier 	case 0x00:
393*43f728cbSDavid du Colombier 		wn->aid = d[0] | d[1]<<8;
394*43f728cbSDavid du Colombier 		if(wn->rsnelen > 0)
395*43f728cbSDavid du Colombier 			setstatus(wifi, wn, Sblocked);
396*43f728cbSDavid du Colombier 		else
397*43f728cbSDavid du Colombier 			setstatus(wifi, wn, Sassoc);
398*43f728cbSDavid du Colombier 		break;
399*43f728cbSDavid du Colombier 	default:
400*43f728cbSDavid du Colombier 		wn->aid = 0;
401*43f728cbSDavid du Colombier 		setstatus(wifi, wn, Sunassoc);
402*43f728cbSDavid du Colombier 	}
403*43f728cbSDavid du Colombier }
404*43f728cbSDavid du Colombier 
405*43f728cbSDavid du Colombier static void
recvbeacon(Wifi * wifi,Wnode * wn,uchar * d,int len)406*43f728cbSDavid du Colombier recvbeacon(Wifi *wifi, Wnode *wn, uchar *d, int len)
407*43f728cbSDavid du Colombier {
408*43f728cbSDavid du Colombier 	static uchar wpa1oui[4] = { 0x00, 0x50, 0xf2, 0x01 };
409*43f728cbSDavid du Colombier 	uchar *e, *x, *p, t;
410*43f728cbSDavid du Colombier 	int rsnset;
411*43f728cbSDavid du Colombier 
412*43f728cbSDavid du Colombier 	len -= 8+2+2;
413*43f728cbSDavid du Colombier 	if(len < 0)
414*43f728cbSDavid du Colombier 		return;
415*43f728cbSDavid du Colombier 
416*43f728cbSDavid du Colombier 	d += 8;	/* timestamp */
417*43f728cbSDavid du Colombier 	wn->ival = d[0] | d[1]<<8;
418*43f728cbSDavid du Colombier 	d += 2;
419*43f728cbSDavid du Colombier 	wn->cap = d[0] | d[1]<<8;
420*43f728cbSDavid du Colombier 	d += 2;
421*43f728cbSDavid du Colombier 
422*43f728cbSDavid du Colombier 	rsnset = 0;
423*43f728cbSDavid du Colombier 	for(e = d + len; d+2 <= e; d = x){
424*43f728cbSDavid du Colombier 		d += 2;
425*43f728cbSDavid du Colombier 		x = d + d[-1];
426*43f728cbSDavid du Colombier 		if(x > e)
427*43f728cbSDavid du Colombier 			break;	/* truncated */
428*43f728cbSDavid du Colombier 		t = d[-2];
429*43f728cbSDavid du Colombier 		switch(t){
430*43f728cbSDavid du Colombier 		case 0:		/* SSID */
431*43f728cbSDavid du Colombier 			len = 0;
432*43f728cbSDavid du Colombier 			while(len < Essidlen && d+len < x && d[len] != 0)
433*43f728cbSDavid du Colombier 				len++;
434*43f728cbSDavid du Colombier 			if(len == 0)
435*43f728cbSDavid du Colombier 				continue;
436*43f728cbSDavid du Colombier 			if(len != strlen(wn->ssid) || strncmp(wn->ssid, (char*)d, len) != 0){
437*43f728cbSDavid du Colombier 				strncpy(wn->ssid, (char*)d, len);
438*43f728cbSDavid du Colombier 				wn->ssid[len] = 0;
439*43f728cbSDavid du Colombier 			}
440*43f728cbSDavid du Colombier 			break;
441*43f728cbSDavid du Colombier 		case 1:		/* supported rates */
442*43f728cbSDavid du Colombier 		case 50:	/* extended rates */
443*43f728cbSDavid du Colombier 			if(wn->minrate != nil || wn->maxrate != nil || wifi->rates == nil)
444*43f728cbSDavid du Colombier 				break;	/* already set */
445*43f728cbSDavid du Colombier 			while(d < x){
446*43f728cbSDavid du Colombier 				t = *d++ & 0x7f;
447*43f728cbSDavid du Colombier 				for(p = wifi->rates; *p != 0; p++){
448*43f728cbSDavid du Colombier 					if((*p & 0x7f) == t){
449*43f728cbSDavid du Colombier 						if(wn->minrate == nil || t < (*wn->minrate & 0x7f))
450*43f728cbSDavid du Colombier 							wn->minrate = p;
451*43f728cbSDavid du Colombier 						if(wn->maxrate == nil || t > (*wn->maxrate & 0x7f))
452*43f728cbSDavid du Colombier 							wn->maxrate = p;
453*43f728cbSDavid du Colombier 						break;
454*43f728cbSDavid du Colombier 					}
455*43f728cbSDavid du Colombier 				}
456*43f728cbSDavid du Colombier 				wn->actrate = wn->maxrate;
457*43f728cbSDavid du Colombier 			}
458*43f728cbSDavid du Colombier 			break;
459*43f728cbSDavid du Colombier 		case 3:		/* DSPARAMS */
460*43f728cbSDavid du Colombier 			if(d != x)
461*43f728cbSDavid du Colombier 				wn->channel = d[0];
462*43f728cbSDavid du Colombier 			break;
463*43f728cbSDavid du Colombier 		case 221:	/* vendor specific */
464*43f728cbSDavid du Colombier 			len = x - d;
465*43f728cbSDavid du Colombier 			if(rsnset || len < sizeof(wpa1oui) || memcmp(d, wpa1oui, sizeof(wpa1oui)) != 0)
466*43f728cbSDavid du Colombier 				break;
467*43f728cbSDavid du Colombier 			/* no break */
468*43f728cbSDavid du Colombier 		case 48:	/* RSN information */
469*43f728cbSDavid du Colombier 			len = x - &d[-2];
470*43f728cbSDavid du Colombier 			memmove(wn->brsne, &d[-2], len);
471*43f728cbSDavid du Colombier 			wn->brsnelen = len;
472*43f728cbSDavid du Colombier 			rsnset = 1;
473*43f728cbSDavid du Colombier 			break;
474*43f728cbSDavid du Colombier 		}
475*43f728cbSDavid du Colombier 	}
476*43f728cbSDavid du Colombier }
477*43f728cbSDavid du Colombier 
478*43f728cbSDavid du Colombier static void
freewifikeys(Wifi * wifi,Wnode * wn)479*43f728cbSDavid du Colombier freewifikeys(Wifi *wifi, Wnode *wn)
480*43f728cbSDavid du Colombier {
481*43f728cbSDavid du Colombier 	int i;
482*43f728cbSDavid du Colombier 
483*43f728cbSDavid du Colombier 	wlock(&wifi->crypt);
484*43f728cbSDavid du Colombier 	for(i=0; i<nelem(wn->rxkey); i++){
485*43f728cbSDavid du Colombier 		free(wn->rxkey[i]);
486*43f728cbSDavid du Colombier 		wn->rxkey[i] = nil;
487*43f728cbSDavid du Colombier 	}
488*43f728cbSDavid du Colombier 	for(i=0; i<nelem(wn->txkey); i++){
489*43f728cbSDavid du Colombier 		free(wn->txkey[i]);
490*43f728cbSDavid du Colombier 		wn->txkey[i] = nil;
491*43f728cbSDavid du Colombier 	}
492*43f728cbSDavid du Colombier 	wunlock(&wifi->crypt);
493*43f728cbSDavid du Colombier }
494*43f728cbSDavid du Colombier 
495*43f728cbSDavid du Colombier static void
wifideauth(Wifi * wifi,Wnode * wn)496*43f728cbSDavid du Colombier wifideauth(Wifi *wifi, Wnode *wn)
497*43f728cbSDavid du Colombier {
498*43f728cbSDavid du Colombier 	Ether *ether;
499*43f728cbSDavid du Colombier 	Netfile *f;
500*43f728cbSDavid du Colombier 	int i;
501*43f728cbSDavid du Colombier 
502*43f728cbSDavid du Colombier 	/* deassociate node, clear keys */
503*43f728cbSDavid du Colombier 	setstatus(wifi, wn, Sunauth);
504*43f728cbSDavid du Colombier 	freewifikeys(wifi, wn);
505*43f728cbSDavid du Colombier 	wn->aid = 0;
506*43f728cbSDavid du Colombier 
507*43f728cbSDavid du Colombier 	if(wn == wifi->bss){
508*43f728cbSDavid du Colombier 		/* notify driver about node aid association */
509*43f728cbSDavid du Colombier 		(*wifi->transmit)(wifi, wn, nil);
510*43f728cbSDavid du Colombier 
511*43f728cbSDavid du Colombier 		/* notify aux/wpa with a zero length packet that we got deassociated from the ap */
512*43f728cbSDavid du Colombier 		ether = wifi->ether;
513*43f728cbSDavid du Colombier 		for(i=0; i<ether->nfile; i++){
514*43f728cbSDavid du Colombier 			f = ether->f[i];
515*43f728cbSDavid du Colombier 			if(f == nil || f->in == nil || f->inuse == 0 || f->type != 0x888e)
516*43f728cbSDavid du Colombier 				continue;
517*43f728cbSDavid du Colombier 			qflush(f->in);
518*43f728cbSDavid du Colombier 			qwrite(f->in, 0, 0);
519*43f728cbSDavid du Colombier 		}
520*43f728cbSDavid du Colombier 		qflush(ether->oq);
521*43f728cbSDavid du Colombier 	}
522*43f728cbSDavid du Colombier }
523*43f728cbSDavid du Colombier 
524*43f728cbSDavid du Colombier /* check if a node qualifies as our bss matching bssid and essid */
525*43f728cbSDavid du Colombier static int
goodbss(Wifi * wifi,Wnode * wn)526*43f728cbSDavid du Colombier goodbss(Wifi *wifi, Wnode *wn)
527*43f728cbSDavid du Colombier {
528*43f728cbSDavid du Colombier 	if(memcmp(wifi->bssid, wifi->ether->bcast, Eaddrlen) != 0){
529*43f728cbSDavid du Colombier 		if(memcmp(wifi->bssid, wn->bssid, Eaddrlen) != 0)
530*43f728cbSDavid du Colombier 			return 0;	/* bssid doesnt match */
531*43f728cbSDavid du Colombier 	} else if(wifi->essid[0] == 0)
532*43f728cbSDavid du Colombier 		return 0;	/* both bssid and essid unspecified */
533*43f728cbSDavid du Colombier 	if(wifi->essid[0] != 0 && strcmp(wifi->essid, wn->ssid) != 0)
534*43f728cbSDavid du Colombier 		return 0;	/* essid doesnt match */
535*43f728cbSDavid du Colombier 	return 1;
536*43f728cbSDavid du Colombier }
537*43f728cbSDavid du Colombier 
538*43f728cbSDavid du Colombier static void
wifiproc(void * arg)539*43f728cbSDavid du Colombier wifiproc(void *arg)
540*43f728cbSDavid du Colombier {
541*43f728cbSDavid du Colombier 	Wifi *wifi;
542*43f728cbSDavid du Colombier 	Wifipkt *w;
543*43f728cbSDavid du Colombier 	Wnode *wn;
544*43f728cbSDavid du Colombier 	Block *b;
545*43f728cbSDavid du Colombier 
546*43f728cbSDavid du Colombier 	b = nil;
547*43f728cbSDavid du Colombier 	wifi = arg;
548*43f728cbSDavid du Colombier 	while(waserror())
549*43f728cbSDavid du Colombier 		;
550*43f728cbSDavid du Colombier 	for(;;){
551*43f728cbSDavid du Colombier 		if(b != nil){
552*43f728cbSDavid du Colombier 			freeb(b);
553*43f728cbSDavid du Colombier 			b = nil;
554*43f728cbSDavid du Colombier 			continue;
555*43f728cbSDavid du Colombier 		}
556*43f728cbSDavid du Colombier 		if((b = qbread(wifi->iq, 100000)) == nil)
557*43f728cbSDavid du Colombier 			break;
558*43f728cbSDavid du Colombier 		w = (Wifipkt*)b->rp;
559*43f728cbSDavid du Colombier 		if(w->fc[1] & 0x40){
560*43f728cbSDavid du Colombier 			/* encrypted */
561*43f728cbSDavid du Colombier 			if((wn = nodelookup(wifi, w->a2, 0)) == nil)
562*43f728cbSDavid du Colombier 				continue;
563*43f728cbSDavid du Colombier 			if((b = wifidecrypt(wifi, wn, b)) != nil){
564*43f728cbSDavid du Colombier 				w = (Wifipkt*)b->rp;
565*43f728cbSDavid du Colombier 				if(w->fc[1] & 0x40)
566*43f728cbSDavid du Colombier 					continue;
567*43f728cbSDavid du Colombier 				wifiiq(wifi, b);
568*43f728cbSDavid du Colombier 				b = nil;
569*43f728cbSDavid du Colombier 			}
570*43f728cbSDavid du Colombier 			continue;
571*43f728cbSDavid du Colombier 		}
572*43f728cbSDavid du Colombier 		/* management */
573*43f728cbSDavid du Colombier 		if((w->fc[0] & 0x0c) != 0x00)
574*43f728cbSDavid du Colombier 			continue;
575*43f728cbSDavid du Colombier 
576*43f728cbSDavid du Colombier 		switch(w->fc[0] & 0xf0){
577*43f728cbSDavid du Colombier 		case 0x50:	/* probe response */
578*43f728cbSDavid du Colombier 			if(wifi->debug)
579*43f728cbSDavid du Colombier 				print("#l%d: got probe from %E\n", wifi->ether->ctlrno, w->a3);
580*43f728cbSDavid du Colombier 			/* no break */
581*43f728cbSDavid du Colombier 		case 0x80:	/* beacon */
582*43f728cbSDavid du Colombier 			if((wn = nodelookup(wifi, w->a3, 1)) == nil)
583*43f728cbSDavid du Colombier 				continue;
584*43f728cbSDavid du Colombier 			b->rp += wifihdrlen(w);
585*43f728cbSDavid du Colombier 			recvbeacon(wifi, wn, b->rp, BLEN(b));
586*43f728cbSDavid du Colombier 
587*43f728cbSDavid du Colombier 			if(wifi->bss == nil
588*43f728cbSDavid du Colombier 			&& TK2MS(MACHP(0)->ticks - wn->lastsend) > 1000
589*43f728cbSDavid du Colombier 			&& goodbss(wifi, wn)){
590*43f728cbSDavid du Colombier 				setstatus(wifi, wn, Sconn);
591*43f728cbSDavid du Colombier 				sendauth(wifi, wn);
592*43f728cbSDavid du Colombier 				wifi->lastauth = wn->lastsend;
593*43f728cbSDavid du Colombier 			}
594*43f728cbSDavid du Colombier 			continue;
595*43f728cbSDavid du Colombier 		}
596*43f728cbSDavid du Colombier 
597*43f728cbSDavid du Colombier 		if(memcmp(w->a1, wifi->ether->ea, Eaddrlen))
598*43f728cbSDavid du Colombier 			continue;
599*43f728cbSDavid du Colombier 		if((wn = nodelookup(wifi, w->a3, 0)) == nil)
600*43f728cbSDavid du Colombier 			continue;
601*43f728cbSDavid du Colombier 		switch(w->fc[0] & 0xf0){
602*43f728cbSDavid du Colombier 		case 0x10:	/* assoc response */
603*43f728cbSDavid du Colombier 		case 0x30:	/* reassoc response */
604*43f728cbSDavid du Colombier 			b->rp += wifihdrlen(w);
605*43f728cbSDavid du Colombier 			recvassoc(wifi, wn, b->rp, BLEN(b));
606*43f728cbSDavid du Colombier 			/* notify driver about node aid association */
607*43f728cbSDavid du Colombier 			if(wn == wifi->bss)
608*43f728cbSDavid du Colombier 				(*wifi->transmit)(wifi, wn, nil);
609*43f728cbSDavid du Colombier 			break;
610*43f728cbSDavid du Colombier 		case 0xb0:	/* auth */
611*43f728cbSDavid du Colombier 			if(wifi->debug)
612*43f728cbSDavid du Colombier 				print("#l%d: got auth from %E\n", wifi->ether->ctlrno, wn->bssid);
613*43f728cbSDavid du Colombier 			if(wn->brsnelen > 0 && wn->rsnelen == 0)
614*43f728cbSDavid du Colombier 				setstatus(wifi, wn, Sneedauth);
615*43f728cbSDavid du Colombier 			else
616*43f728cbSDavid du Colombier 				setstatus(wifi, wn, Sauth);
617*43f728cbSDavid du Colombier 			if(wifi->bss == nil && goodbss(wifi, wn)){
618*43f728cbSDavid du Colombier 				wifi->bss = wn;
619*43f728cbSDavid du Colombier 				if(wn->status == Sauth)
620*43f728cbSDavid du Colombier 					sendassoc(wifi, wn);
621*43f728cbSDavid du Colombier 			}
622*43f728cbSDavid du Colombier 			break;
623*43f728cbSDavid du Colombier 		case 0xc0:	/* deauth */
624*43f728cbSDavid du Colombier 			if(wifi->debug)
625*43f728cbSDavid du Colombier 				print("#l%d: got deauth from %E\n", wifi->ether->ctlrno, wn->bssid);
626*43f728cbSDavid du Colombier 			wifideauth(wifi, wn);
627*43f728cbSDavid du Colombier 			break;
628*43f728cbSDavid du Colombier 		}
629*43f728cbSDavid du Colombier 	}
630*43f728cbSDavid du Colombier 	pexit("wifi in queue closed", 1);
631*43f728cbSDavid du Colombier }
632*43f728cbSDavid du Colombier 
633*43f728cbSDavid du Colombier static void
wifietheroq(Wifi * wifi,Block * b)634*43f728cbSDavid du Colombier wifietheroq(Wifi *wifi, Block *b)
635*43f728cbSDavid du Colombier {
636*43f728cbSDavid du Colombier 	Etherpkt e;
637*43f728cbSDavid du Colombier 	Wifipkt h;
638*43f728cbSDavid du Colombier 	int hdrlen;
639*43f728cbSDavid du Colombier 	Wnode *wn;
640*43f728cbSDavid du Colombier 	SNAP *s;
641*43f728cbSDavid du Colombier 
642*43f728cbSDavid du Colombier 	if(BLEN(b) < ETHERHDRSIZE)
643*43f728cbSDavid du Colombier 		goto drop;
644*43f728cbSDavid du Colombier 	if((wn = wifi->bss) == nil)
645*43f728cbSDavid du Colombier 		goto drop;
646*43f728cbSDavid du Colombier 
647*43f728cbSDavid du Colombier 	memmove(&e, b->rp, ETHERHDRSIZE);
648*43f728cbSDavid du Colombier 	b->rp += ETHERHDRSIZE;
649*43f728cbSDavid du Colombier 
650*43f728cbSDavid du Colombier 	if(wn->status == Sblocked){
651*43f728cbSDavid du Colombier 		/* only pass EAPOL frames when port is blocked */
652*43f728cbSDavid du Colombier 		if((e.type[0]<<8 | e.type[1]) != 0x888e)
653*43f728cbSDavid du Colombier 			goto drop;
654*43f728cbSDavid du Colombier 	} else if(wn->status != Sassoc)
655*43f728cbSDavid du Colombier 		goto drop;
656*43f728cbSDavid du Colombier 
657*43f728cbSDavid du Colombier 	h.fc[0] = 0x08;	/* data */
658*43f728cbSDavid du Colombier 	memmove(h.a1, wn->bssid, Eaddrlen);
659*43f728cbSDavid du Colombier 	if(memcmp(e.s, wifi->ether->ea, Eaddrlen) == 0) {
660*43f728cbSDavid du Colombier 		h.fc[1] = 0x01;	/* STA->AP */
661*43f728cbSDavid du Colombier 	} else {
662*43f728cbSDavid du Colombier 		h.fc[1] = 0x03;	/* AP->AP (WDS) */
663*43f728cbSDavid du Colombier 		memmove(h.a2, wifi->ether->ea, Eaddrlen);
664*43f728cbSDavid du Colombier 	}
665*43f728cbSDavid du Colombier 	memmove(dstaddr(&h), e.d, Eaddrlen);
666*43f728cbSDavid du Colombier 	memmove(srcaddr(&h), e.s, Eaddrlen);
667*43f728cbSDavid du Colombier 
668*43f728cbSDavid du Colombier 	hdrlen = wifihdrlen(&h);
669*43f728cbSDavid du Colombier 	b = padblock(b, hdrlen + SNAPHDRSIZE);
670*43f728cbSDavid du Colombier 	memmove(b->rp, &h, hdrlen);
671*43f728cbSDavid du Colombier 	s = (SNAP*)(b->rp + hdrlen);
672*43f728cbSDavid du Colombier 	s->dsap = s->ssap = 0xAA;
673*43f728cbSDavid du Colombier 	s->control = 0x03;
674*43f728cbSDavid du Colombier 	s->orgcode[0] = 0;
675*43f728cbSDavid du Colombier 	s->orgcode[1] = 0;
676*43f728cbSDavid du Colombier 	s->orgcode[2] = 0;
677*43f728cbSDavid du Colombier 	memmove(s->type, e.type, 2);
678*43f728cbSDavid du Colombier 
679*43f728cbSDavid du Colombier 	wifitx(wifi, wn, b);
680*43f728cbSDavid du Colombier 	return;
681*43f728cbSDavid du Colombier drop:
682*43f728cbSDavid du Colombier 	freeb(b);
683*43f728cbSDavid du Colombier }
684*43f728cbSDavid du Colombier 
685*43f728cbSDavid du Colombier static void
wifoproc(void * arg)686*43f728cbSDavid du Colombier wifoproc(void *arg)
687*43f728cbSDavid du Colombier {
688*43f728cbSDavid du Colombier 	Ether *ether;
689*43f728cbSDavid du Colombier 	Wifi *wifi;
690*43f728cbSDavid du Colombier 	Block *b;
691*43f728cbSDavid du Colombier 
692*43f728cbSDavid du Colombier 	wifi = arg;
693*43f728cbSDavid du Colombier 	ether = wifi->ether;
694*43f728cbSDavid du Colombier 	while(waserror())
695*43f728cbSDavid du Colombier 		;
696*43f728cbSDavid du Colombier 	while((b = qbread(ether->oq, 1000000)) != nil)
697*43f728cbSDavid du Colombier 		wifietheroq(wifi, b);
698*43f728cbSDavid du Colombier 	pexit("ether out queue closed", 1);
699*43f728cbSDavid du Colombier }
700*43f728cbSDavid du Colombier 
701*43f728cbSDavid du Colombier static void
wifsproc(void * arg)702*43f728cbSDavid du Colombier wifsproc(void *arg)
703*43f728cbSDavid du Colombier {
704*43f728cbSDavid du Colombier 	Ether *ether;
705*43f728cbSDavid du Colombier 	Wifi *wifi;
706*43f728cbSDavid du Colombier 	Wnode wnscan;
707*43f728cbSDavid du Colombier 	Wnode *wn;
708*43f728cbSDavid du Colombier 	ulong now, tmout;
709*43f728cbSDavid du Colombier 	uchar *rate;
710*43f728cbSDavid du Colombier 
711*43f728cbSDavid du Colombier 	wifi = arg;
712*43f728cbSDavid du Colombier 	ether = wifi->ether;
713*43f728cbSDavid du Colombier 
714*43f728cbSDavid du Colombier 	wn = &wnscan;
715*43f728cbSDavid du Colombier 	memset(wn, 0, sizeof(*wn));
716*43f728cbSDavid du Colombier 	memmove(wn->bssid, ether->bcast, Eaddrlen);
717*43f728cbSDavid du Colombier 
718*43f728cbSDavid du Colombier 	while(waserror())
719*43f728cbSDavid du Colombier 		;
720*43f728cbSDavid du Colombier Scan:
721*43f728cbSDavid du Colombier 	/* scan for access point */
722*43f728cbSDavid du Colombier 	while(wifi->bss == nil){
723*43f728cbSDavid du Colombier 		ether->link = 0;
724*43f728cbSDavid du Colombier 		wnscan.channel = 1 + ((wnscan.channel+4) % 13);
725*43f728cbSDavid du Colombier 		wifiprobe(wifi, &wnscan);
726*43f728cbSDavid du Colombier 		do {
727*43f728cbSDavid du Colombier 			tsleep(&up->sleep, return0, 0, 200);
728*43f728cbSDavid du Colombier 			now = MACHP(0)->ticks;
729*43f728cbSDavid du Colombier 		} while(TK2MS(now-wifi->lastauth) < 1000);
730*43f728cbSDavid du Colombier 	}
731*43f728cbSDavid du Colombier 
732*43f728cbSDavid du Colombier 	/* maintain access point */
733*43f728cbSDavid du Colombier 	tmout = 0;
734*43f728cbSDavid du Colombier 	while((wn = wifi->bss) != nil){
735*43f728cbSDavid du Colombier 		ether->link = (wn->status == Sassoc) || (wn->status == Sblocked);
736*43f728cbSDavid du Colombier 		if(ether->link && (rate = wn->actrate) != nil)
737*43f728cbSDavid du Colombier 			ether->mbps = ((*rate & 0x7f)+1)/2;
738*43f728cbSDavid du Colombier 		now = MACHP(0)->ticks;
739*43f728cbSDavid du Colombier 		if(wn->status != Sneedauth && TK2SEC(now - wn->lastseen) > 20 || goodbss(wifi, wn) == 0){
740*43f728cbSDavid du Colombier 			wifideauth(wifi, wn);
741*43f728cbSDavid du Colombier 			wifi->bss = nil;
742*43f728cbSDavid du Colombier 			break;
743*43f728cbSDavid du Colombier 		}
744*43f728cbSDavid du Colombier 		if(TK2MS(now - wn->lastsend) > 1000){
745*43f728cbSDavid du Colombier 			if((wn->status == Sauth || wn->status == Sblocked) && (++tmout & 7) == 0)
746*43f728cbSDavid du Colombier 				wifideauth(wifi, wn);	/* stuck in auth, start over */
747*43f728cbSDavid du Colombier 			if(wn->status == Sconn || wn->status == Sunauth)
748*43f728cbSDavid du Colombier 				sendauth(wifi, wn);
749*43f728cbSDavid du Colombier 			if(wn->status == Sauth)
750*43f728cbSDavid du Colombier 				sendassoc(wifi, wn);
751*43f728cbSDavid du Colombier 		}
752*43f728cbSDavid du Colombier 		tsleep(&up->sleep, return0, 0, 500);
753*43f728cbSDavid du Colombier 	}
754*43f728cbSDavid du Colombier 	goto Scan;
755*43f728cbSDavid du Colombier }
756*43f728cbSDavid du Colombier 
757*43f728cbSDavid du Colombier Wifi*
wifiattach(Ether * ether,void (* transmit)(Wifi *,Wnode *,Block *))758*43f728cbSDavid du Colombier wifiattach(Ether *ether, void (*transmit)(Wifi*, Wnode*, Block*))
759*43f728cbSDavid du Colombier {
760*43f728cbSDavid du Colombier 	char name[32];
761*43f728cbSDavid du Colombier 	Wifi *wifi;
762*43f728cbSDavid du Colombier 
763*43f728cbSDavid du Colombier 	wifi = malloc(sizeof(Wifi));
764*43f728cbSDavid du Colombier 	if(wifi == nil)
765*43f728cbSDavid du Colombier 		error(Enomem);
766*43f728cbSDavid du Colombier 	wifi->iq = qopen(ether->limit, 0, 0, 0);
767*43f728cbSDavid du Colombier 	if(wifi->iq == nil){
768*43f728cbSDavid du Colombier 		free(wifi);
769*43f728cbSDavid du Colombier 		error(Enomem);
770*43f728cbSDavid du Colombier 	}
771*43f728cbSDavid du Colombier 	wifi->ether = ether;
772*43f728cbSDavid du Colombier 	wifi->transmit = transmit;
773*43f728cbSDavid du Colombier 
774*43f728cbSDavid du Colombier 	wifi->rates = basicrates;
775*43f728cbSDavid du Colombier 
776*43f728cbSDavid du Colombier 	wifi->essid[0] = 0;
777*43f728cbSDavid du Colombier 	memmove(wifi->bssid, ether->bcast, Eaddrlen);
778*43f728cbSDavid du Colombier 
779*43f728cbSDavid du Colombier 	wifi->lastauth = MACHP(0)->ticks;
780*43f728cbSDavid du Colombier 
781*43f728cbSDavid du Colombier 	snprint(name, sizeof(name), "#l%dwifi", ether->ctlrno);
782*43f728cbSDavid du Colombier 	kproc(name, wifiproc, wifi);
783*43f728cbSDavid du Colombier 	snprint(name, sizeof(name), "#l%dwifo", ether->ctlrno);
784*43f728cbSDavid du Colombier 	kproc(name, wifoproc, wifi);
785*43f728cbSDavid du Colombier 	snprint(name, sizeof(name), "#l%dwifs", ether->ctlrno);
786*43f728cbSDavid du Colombier 	kproc(name, wifsproc, wifi);
787*43f728cbSDavid du Colombier 
788*43f728cbSDavid du Colombier 	return wifi;
789*43f728cbSDavid du Colombier }
790*43f728cbSDavid du Colombier 
791*43f728cbSDavid du Colombier static int
hextob(char * s,char ** sp,uchar * b,int n)792*43f728cbSDavid du Colombier hextob(char *s, char **sp, uchar *b, int n)
793*43f728cbSDavid du Colombier {
794*43f728cbSDavid du Colombier 	int r;
795*43f728cbSDavid du Colombier 
796*43f728cbSDavid du Colombier 	n <<= 1;
797*43f728cbSDavid du Colombier 	for(r = 0; r < n && *s; s++){
798*43f728cbSDavid du Colombier 		*b <<= 4;
799*43f728cbSDavid du Colombier 		if(*s >= '0' && *s <= '9')
800*43f728cbSDavid du Colombier 			*b |= (*s - '0');
801*43f728cbSDavid du Colombier 		else if(*s >= 'a' && *s <= 'f')
802*43f728cbSDavid du Colombier 			*b |= 10+(*s - 'a');
803*43f728cbSDavid du Colombier 		else if(*s >= 'A' && *s <= 'F')
804*43f728cbSDavid du Colombier 			*b |= 10+(*s - 'A');
805*43f728cbSDavid du Colombier 		else break;
806*43f728cbSDavid du Colombier 		if((++r & 1) == 0)
807*43f728cbSDavid du Colombier 			b++;
808*43f728cbSDavid du Colombier 	}
809*43f728cbSDavid du Colombier 	if(sp != nil)
810*43f728cbSDavid du Colombier 		*sp = s;
811*43f728cbSDavid du Colombier 	return r >> 1;
812*43f728cbSDavid du Colombier }
813*43f728cbSDavid du Colombier 
814*43f728cbSDavid du Colombier static char *ciphers[] = {
815*43f728cbSDavid du Colombier 	[0]	"clear",
816*43f728cbSDavid du Colombier 	[TKIP]	"tkip",
817*43f728cbSDavid du Colombier 	[CCMP]	"ccmp",
818*43f728cbSDavid du Colombier };
819*43f728cbSDavid du Colombier 
820*43f728cbSDavid du Colombier static Wkey*
parsekey(char * s)821*43f728cbSDavid du Colombier parsekey(char *s)
822*43f728cbSDavid du Colombier {
823*43f728cbSDavid du Colombier 	char buf[256], *p;
824*43f728cbSDavid du Colombier 	uchar key[32];
825*43f728cbSDavid du Colombier 	int i, n;
826*43f728cbSDavid du Colombier 	Wkey *k;
827*43f728cbSDavid du Colombier 
828*43f728cbSDavid du Colombier 	strncpy(buf, s, sizeof(buf)-1);
829*43f728cbSDavid du Colombier 	buf[sizeof(buf)-1] = 0;
830*43f728cbSDavid du Colombier 	if((p = strchr(buf, ':')) != nil)
831*43f728cbSDavid du Colombier 		*p++ = 0;
832*43f728cbSDavid du Colombier 	else
833*43f728cbSDavid du Colombier 		p = buf;
834*43f728cbSDavid du Colombier 	n = hextob(p, &p, key, sizeof(key));
835*43f728cbSDavid du Colombier 	for(i=0; i<nelem(ciphers); i++)
836*43f728cbSDavid du Colombier 		if(strcmp(ciphers[i], buf) == 0)
837*43f728cbSDavid du Colombier 			break;
838*43f728cbSDavid du Colombier 	switch(i){
839*43f728cbSDavid du Colombier 	case 0:
840*43f728cbSDavid du Colombier 		k = malloc(sizeof(Wkey));
841*43f728cbSDavid du Colombier 		break;
842*43f728cbSDavid du Colombier 	case TKIP:
843*43f728cbSDavid du Colombier 		if(n != 32)
844*43f728cbSDavid du Colombier 			return nil;
845*43f728cbSDavid du Colombier 		k = malloc(sizeof(Wkey) + n);
846*43f728cbSDavid du Colombier 		memmove(k->key, key, n);
847*43f728cbSDavid du Colombier 		break;
848*43f728cbSDavid du Colombier 	case CCMP:
849*43f728cbSDavid du Colombier 		if(n != 16)
850*43f728cbSDavid du Colombier 			return nil;
851*43f728cbSDavid du Colombier 		k = malloc(sizeof(Wkey) + sizeof(AESstate));
852*43f728cbSDavid du Colombier 		setupAESstate((AESstate*)k->key, key, n, nil);
853*43f728cbSDavid du Colombier 		break;
854*43f728cbSDavid du Colombier 	default:
855*43f728cbSDavid du Colombier 		return nil;
856*43f728cbSDavid du Colombier 	}
857*43f728cbSDavid du Colombier 	memset(key, 0, sizeof(key));
858*43f728cbSDavid du Colombier 	if(*p == '@')
859*43f728cbSDavid du Colombier 		k->tsc = strtoull(++p, nil, 16);
860*43f728cbSDavid du Colombier 	k->len = n;
861*43f728cbSDavid du Colombier 	k->cipher = i;
862*43f728cbSDavid du Colombier 	return k;
863*43f728cbSDavid du Colombier }
864*43f728cbSDavid du Colombier 
865*43f728cbSDavid du Colombier void
wificfg(Wifi * wifi,char * opt)866*43f728cbSDavid du Colombier wificfg(Wifi *wifi, char *opt)
867*43f728cbSDavid du Colombier {
868*43f728cbSDavid du Colombier 	char *p, buf[64];
869*43f728cbSDavid du Colombier 	int n;
870*43f728cbSDavid du Colombier 
871*43f728cbSDavid du Colombier 	if(strncmp(opt, "debug=", 6))
872*43f728cbSDavid du Colombier 	if(strncmp(opt, "essid=", 6))
873*43f728cbSDavid du Colombier 	if(strncmp(opt, "bssid=", 6))
874*43f728cbSDavid du Colombier 		return;
875*43f728cbSDavid du Colombier 	if((p = strchr(opt, '=')) == nil)
876*43f728cbSDavid du Colombier 		return;
877*43f728cbSDavid du Colombier 	if(waserror())
878*43f728cbSDavid du Colombier 		return;
879*43f728cbSDavid du Colombier 	n = snprint(buf, sizeof(buf), "%.*s %q", (int)(p - opt), opt, p+1);
880*43f728cbSDavid du Colombier 	wifictl(wifi, buf, n);
881*43f728cbSDavid du Colombier 	poperror();
882*43f728cbSDavid du Colombier }
883*43f728cbSDavid du Colombier 
884*43f728cbSDavid du Colombier enum {
885*43f728cbSDavid du Colombier 	CMdebug,
886*43f728cbSDavid du Colombier 	CMessid,
887*43f728cbSDavid du Colombier 	CMauth,
888*43f728cbSDavid du Colombier 	CMbssid,
889*43f728cbSDavid du Colombier 	CMrxkey0,
890*43f728cbSDavid du Colombier 	CMrxkey1,
891*43f728cbSDavid du Colombier 	CMrxkey2,
892*43f728cbSDavid du Colombier 	CMrxkey3,
893*43f728cbSDavid du Colombier 	CMrxkey4,
894*43f728cbSDavid du Colombier 	CMtxkey0,
895*43f728cbSDavid du Colombier };
896*43f728cbSDavid du Colombier 
897*43f728cbSDavid du Colombier static Cmdtab wifictlmsg[] =
898*43f728cbSDavid du Colombier {
899*43f728cbSDavid du Colombier 	CMdebug,	"debug",	0,
900*43f728cbSDavid du Colombier 	CMessid,	"essid",	0,
901*43f728cbSDavid du Colombier 	CMauth,		"auth",		0,
902*43f728cbSDavid du Colombier 	CMbssid,	"bssid",	0,
903*43f728cbSDavid du Colombier 
904*43f728cbSDavid du Colombier 	CMrxkey0,	"rxkey0",	0,	/* group keys */
905*43f728cbSDavid du Colombier 	CMrxkey1,	"rxkey1",	0,
906*43f728cbSDavid du Colombier 	CMrxkey2,	"rxkey2",	0,
907*43f728cbSDavid du Colombier 	CMrxkey3,	"rxkey3",	0,
908*43f728cbSDavid du Colombier 
909*43f728cbSDavid du Colombier 	CMrxkey4,	"rxkey",	0,	/* peerwise keys */
910*43f728cbSDavid du Colombier 	CMtxkey0,	"txkey",	0,
911*43f728cbSDavid du Colombier 
912*43f728cbSDavid du Colombier 	CMtxkey0,	"txkey0",	0,
913*43f728cbSDavid du Colombier };
914*43f728cbSDavid du Colombier 
915*43f728cbSDavid du Colombier long
wifictl(Wifi * wifi,void * buf,long n)916*43f728cbSDavid du Colombier wifictl(Wifi *wifi, void *buf, long n)
917*43f728cbSDavid du Colombier {
918*43f728cbSDavid du Colombier 	uchar addr[Eaddrlen];
919*43f728cbSDavid du Colombier 	Cmdbuf *cb;
920*43f728cbSDavid du Colombier 	Cmdtab *ct;
921*43f728cbSDavid du Colombier 	Wnode *wn;
922*43f728cbSDavid du Colombier 	Wkey *k, **kk;
923*43f728cbSDavid du Colombier 
924*43f728cbSDavid du Colombier 	cb = nil;
925*43f728cbSDavid du Colombier 	if(waserror()){
926*43f728cbSDavid du Colombier 		free(cb);
927*43f728cbSDavid du Colombier 		nexterror();
928*43f728cbSDavid du Colombier 	}
929*43f728cbSDavid du Colombier 	if(wifi->debug)
930*43f728cbSDavid du Colombier 		print("#l%d: wifictl: %.*s\n", wifi->ether->ctlrno, (int)n, buf);
931*43f728cbSDavid du Colombier 	memmove(addr, wifi->ether->bcast, Eaddrlen);
932*43f728cbSDavid du Colombier 	wn = wifi->bss;
933*43f728cbSDavid du Colombier 	cb = parsecmd(buf, n);
934*43f728cbSDavid du Colombier 	ct = lookupcmd(cb, wifictlmsg, nelem(wifictlmsg));
935*43f728cbSDavid du Colombier 	if(ct->index >= CMauth){
936*43f728cbSDavid du Colombier 		if(cb->nf > 1 && (ct->index == CMbssid || ct->index >= CMrxkey0)){
937*43f728cbSDavid du Colombier 			if(parseether(addr, cb->f[1]) == 0){
938*43f728cbSDavid du Colombier 				cb->f++;
939*43f728cbSDavid du Colombier 				cb->nf--;
940*43f728cbSDavid du Colombier 				wn = nodelookup(wifi, addr, 0);
941*43f728cbSDavid du Colombier 			}
942*43f728cbSDavid du Colombier 		}
943*43f728cbSDavid du Colombier 		if(wn == nil && ct->index != CMbssid)
944*43f728cbSDavid du Colombier 			error("missing node");
945*43f728cbSDavid du Colombier 	}
946*43f728cbSDavid du Colombier 	switch(ct->index){
947*43f728cbSDavid du Colombier 	case CMdebug:
948*43f728cbSDavid du Colombier 		if(cb->f[1] != nil)
949*43f728cbSDavid du Colombier 			wifi->debug = atoi(cb->f[1]);
950*43f728cbSDavid du Colombier 		else
951*43f728cbSDavid du Colombier 			wifi->debug ^= 1;
952*43f728cbSDavid du Colombier 		print("#l%d: debug: %d\n", wifi->ether->ctlrno, wifi->debug);
953*43f728cbSDavid du Colombier 		break;
954*43f728cbSDavid du Colombier 	case CMessid:
955*43f728cbSDavid du Colombier 		if(cb->f[1] != nil)
956*43f728cbSDavid du Colombier 			strncpy(wifi->essid, cb->f[1], Essidlen);
957*43f728cbSDavid du Colombier 		else
958*43f728cbSDavid du Colombier 			wifi->essid[0] = 0;
959*43f728cbSDavid du Colombier 	Findbss:
960*43f728cbSDavid du Colombier 		wn = wifi->bss;
961*43f728cbSDavid du Colombier 		if(wn != nil){
962*43f728cbSDavid du Colombier 			if(goodbss(wifi, wn))
963*43f728cbSDavid du Colombier 				break;
964*43f728cbSDavid du Colombier 			wifideauth(wifi, wn);
965*43f728cbSDavid du Colombier 		}
966*43f728cbSDavid du Colombier 		wifi->bss = nil;
967*43f728cbSDavid du Colombier 		if(wifi->essid[0] == 0 && memcmp(wifi->bssid, wifi->ether->bcast, Eaddrlen) == 0)
968*43f728cbSDavid du Colombier 			break;
969*43f728cbSDavid du Colombier 		for(wn = wifi->node; wn != &wifi->node[nelem(wifi->node)]; wn++)
970*43f728cbSDavid du Colombier 			if(goodbss(wifi, wn)){
971*43f728cbSDavid du Colombier 				setstatus(wifi, wn, Sconn);
972*43f728cbSDavid du Colombier 				sendauth(wifi, wn);
973*43f728cbSDavid du Colombier 			}
974*43f728cbSDavid du Colombier 		break;
975*43f728cbSDavid du Colombier 	case CMbssid:
976*43f728cbSDavid du Colombier 		memmove(wifi->bssid, addr, Eaddrlen);
977*43f728cbSDavid du Colombier 		goto Findbss;
978*43f728cbSDavid du Colombier 	case CMauth:
979*43f728cbSDavid du Colombier 		freewifikeys(wifi, wn);
980*43f728cbSDavid du Colombier 		if(cb->f[1] == nil)
981*43f728cbSDavid du Colombier 			wn->rsnelen = 0;
982*43f728cbSDavid du Colombier 		else
983*43f728cbSDavid du Colombier 			wn->rsnelen = hextob(cb->f[1], nil, wn->rsne, sizeof(wn->rsne));
984*43f728cbSDavid du Colombier 		if(wn->aid == 0){
985*43f728cbSDavid du Colombier 			setstatus(wifi, wn, Sconn);
986*43f728cbSDavid du Colombier 			sendauth(wifi, wn);
987*43f728cbSDavid du Colombier 		} else {
988*43f728cbSDavid du Colombier 			setstatus(wifi, wn, Sauth);
989*43f728cbSDavid du Colombier 			sendassoc(wifi, wn);
990*43f728cbSDavid du Colombier 		}
991*43f728cbSDavid du Colombier 		break;
992*43f728cbSDavid du Colombier 	case CMrxkey0: case CMrxkey1: case CMrxkey2: case CMrxkey3: case CMrxkey4:
993*43f728cbSDavid du Colombier 	case CMtxkey0:
994*43f728cbSDavid du Colombier 		if(cb->f[1] == nil)
995*43f728cbSDavid du Colombier 			error(Ebadarg);
996*43f728cbSDavid du Colombier 		k = parsekey(cb->f[1]);
997*43f728cbSDavid du Colombier 		if(k == nil)
998*43f728cbSDavid du Colombier 			error("bad key");
999*43f728cbSDavid du Colombier 		memset(cb->f[1], 0, strlen(cb->f[1]));
1000*43f728cbSDavid du Colombier 		if(k->cipher == 0){
1001*43f728cbSDavid du Colombier 			free(k);
1002*43f728cbSDavid du Colombier 			k = nil;
1003*43f728cbSDavid du Colombier 		}
1004*43f728cbSDavid du Colombier 		if(ct->index < CMtxkey0)
1005*43f728cbSDavid du Colombier 			kk = &wn->rxkey[ct->index - CMrxkey0];
1006*43f728cbSDavid du Colombier 		else
1007*43f728cbSDavid du Colombier 			kk = &wn->txkey[ct->index - CMtxkey0];
1008*43f728cbSDavid du Colombier 		wlock(&wifi->crypt);
1009*43f728cbSDavid du Colombier 		free(*kk);
1010*43f728cbSDavid du Colombier 		*kk = k;
1011*43f728cbSDavid du Colombier 		wunlock(&wifi->crypt);
1012*43f728cbSDavid du Colombier 		if(ct->index >= CMtxkey0 && wn->status == Sblocked)
1013*43f728cbSDavid du Colombier 			setstatus(wifi, wn, Sassoc);
1014*43f728cbSDavid du Colombier 		break;
1015*43f728cbSDavid du Colombier 	}
1016*43f728cbSDavid du Colombier 	poperror();
1017*43f728cbSDavid du Colombier 	free(cb);
1018*43f728cbSDavid du Colombier 	return n;
1019*43f728cbSDavid du Colombier }
1020*43f728cbSDavid du Colombier 
1021*43f728cbSDavid du Colombier long
wifistat(Wifi * wifi,void * buf,long n,ulong off)1022*43f728cbSDavid du Colombier wifistat(Wifi *wifi, void *buf, long n, ulong off)
1023*43f728cbSDavid du Colombier {
1024*43f728cbSDavid du Colombier 	static uchar zeros[Eaddrlen];
1025*43f728cbSDavid du Colombier 	char essid[Essidlen+1];
1026*43f728cbSDavid du Colombier 	char *s, *p, *e;
1027*43f728cbSDavid du Colombier 	Wnode *wn;
1028*43f728cbSDavid du Colombier 	Wkey *k;
1029*43f728cbSDavid du Colombier 	long now;
1030*43f728cbSDavid du Colombier 	int i;
1031*43f728cbSDavid du Colombier 
1032*43f728cbSDavid du Colombier 	p = s = smalloc(4096);
1033*43f728cbSDavid du Colombier 	e = s + 4096;
1034*43f728cbSDavid du Colombier 
1035*43f728cbSDavid du Colombier 	wn = wifi->bss;
1036*43f728cbSDavid du Colombier 	if(wn != nil){
1037*43f728cbSDavid du Colombier 		strncpy(essid, wn->ssid, Essidlen);
1038*43f728cbSDavid du Colombier 		essid[Essidlen] = 0;
1039*43f728cbSDavid du Colombier 		p = seprint(p, e, "essid: %s\n", essid);
1040*43f728cbSDavid du Colombier 		p = seprint(p, e, "bssid: %E\n", wn->bssid);
1041*43f728cbSDavid du Colombier 		p = seprint(p, e, "status: %s\n", wn->status);
1042*43f728cbSDavid du Colombier 		p = seprint(p, e, "channel: %.2d\n", wn->channel);
1043*43f728cbSDavid du Colombier 
1044*43f728cbSDavid du Colombier 		/* only print key ciphers and key length */
1045*43f728cbSDavid du Colombier 		rlock(&wifi->crypt);
1046*43f728cbSDavid du Colombier 		for(i = 0; i<nelem(wn->rxkey); i++){
1047*43f728cbSDavid du Colombier 			if((k = wn->rxkey[i]) != nil)
1048*43f728cbSDavid du Colombier 				p = seprint(p, e, "rxkey%d: %s:[%d]\n", i,
1049*43f728cbSDavid du Colombier 					ciphers[k->cipher], k->len);
1050*43f728cbSDavid du Colombier 		}
1051*43f728cbSDavid du Colombier 		for(i = 0; i<nelem(wn->txkey); i++){
1052*43f728cbSDavid du Colombier 			if((k = wn->txkey[i]) != nil)
1053*43f728cbSDavid du Colombier 				p = seprint(p, e, "txkey%d: %s:[%d]\n", i,
1054*43f728cbSDavid du Colombier 					ciphers[k->cipher], k->len);
1055*43f728cbSDavid du Colombier 		}
1056*43f728cbSDavid du Colombier 		runlock(&wifi->crypt);
1057*43f728cbSDavid du Colombier 
1058*43f728cbSDavid du Colombier 		if(wn->brsnelen > 0){
1059*43f728cbSDavid du Colombier 			p = seprint(p, e, "brsne: ");
1060*43f728cbSDavid du Colombier 			for(i=0; i<wn->brsnelen; i++)
1061*43f728cbSDavid du Colombier 				p = seprint(p, e, "%.2X", wn->brsne[i]);
1062*43f728cbSDavid du Colombier 			p = seprint(p, e, "\n");
1063*43f728cbSDavid du Colombier 		}
1064*43f728cbSDavid du Colombier 	} else {
1065*43f728cbSDavid du Colombier 		p = seprint(p, e, "essid: %s\n", wifi->essid);
1066*43f728cbSDavid du Colombier 		p = seprint(p, e, "bssid: %E\n", wifi->bssid);
1067*43f728cbSDavid du Colombier 	}
1068*43f728cbSDavid du Colombier 
1069*43f728cbSDavid du Colombier 	now = MACHP(0)->ticks;
1070*43f728cbSDavid du Colombier 	for(wn = wifi->node; wn != &wifi->node[nelem(wifi->node)]; wn++){
1071*43f728cbSDavid du Colombier 		if(wn->lastseen == 0)
1072*43f728cbSDavid du Colombier 			continue;
1073*43f728cbSDavid du Colombier 		strncpy(essid, wn->ssid, Essidlen);
1074*43f728cbSDavid du Colombier 		essid[Essidlen] = 0;
1075*43f728cbSDavid du Colombier 		p = seprint(p, e, "node: %E %.4x %-11ld %.2d %s\n",
1076*43f728cbSDavid du Colombier 			wn->bssid, wn->cap, TK2MS(now - wn->lastseen), wn->channel, essid);
1077*43f728cbSDavid du Colombier 	}
1078*43f728cbSDavid du Colombier 	n = readstr(off, buf, n, s);
1079*43f728cbSDavid du Colombier 	free(s);
1080*43f728cbSDavid du Colombier 	return n;
1081*43f728cbSDavid du Colombier }
1082*43f728cbSDavid du Colombier 
1083*43f728cbSDavid du Colombier static void tkipencrypt(Wkey *k, Wifipkt *w, Block *b, uvlong tsc);
1084*43f728cbSDavid du Colombier static int tkipdecrypt(Wkey *k, Wifipkt *w, Block *b, uvlong tsc);
1085*43f728cbSDavid du Colombier static void ccmpencrypt(Wkey *k, Wifipkt *w, Block *b, uvlong tsc);
1086*43f728cbSDavid du Colombier static int ccmpdecrypt(Wkey *k, Wifipkt *w, Block *b, uvlong tsc);
1087*43f728cbSDavid du Colombier 
1088*43f728cbSDavid du Colombier static Block*
wifiencrypt(Wifi * wifi,Wnode * wn,Block * b)1089*43f728cbSDavid du Colombier wifiencrypt(Wifi *wifi, Wnode *wn, Block *b)
1090*43f728cbSDavid du Colombier {
1091*43f728cbSDavid du Colombier 	uvlong tsc;
1092*43f728cbSDavid du Colombier 	int n, kid;
1093*43f728cbSDavid du Colombier 	Wifipkt *w;
1094*43f728cbSDavid du Colombier 	Wkey *k;
1095*43f728cbSDavid du Colombier 
1096*43f728cbSDavid du Colombier 	rlock(&wifi->crypt);
1097*43f728cbSDavid du Colombier 
1098*43f728cbSDavid du Colombier 	kid = 0;
1099*43f728cbSDavid du Colombier 	k = wn->txkey[kid];
1100*43f728cbSDavid du Colombier 	if(k == nil){
1101*43f728cbSDavid du Colombier 		runlock(&wifi->crypt);
1102*43f728cbSDavid du Colombier 		return b;
1103*43f728cbSDavid du Colombier 	}
1104*43f728cbSDavid du Colombier 
1105*43f728cbSDavid du Colombier 	n = wifihdrlen((Wifipkt*)b->rp);
1106*43f728cbSDavid du Colombier 
1107*43f728cbSDavid du Colombier 	b = padblock(b, 8);
1108*43f728cbSDavid du Colombier 	b = padblock(b, -(8+4));
1109*43f728cbSDavid du Colombier 
1110*43f728cbSDavid du Colombier 	w = (Wifipkt*)b->rp;
1111*43f728cbSDavid du Colombier 	memmove(w, b->rp+8, n);
1112*43f728cbSDavid du Colombier 	b->rp += n;
1113*43f728cbSDavid du Colombier 
1114*43f728cbSDavid du Colombier 	tsc = ++k->tsc;
1115*43f728cbSDavid du Colombier 
1116*43f728cbSDavid du Colombier 	switch(k->cipher){
1117*43f728cbSDavid du Colombier 	case TKIP:
1118*43f728cbSDavid du Colombier 		b->rp[0] = tsc>>8;
1119*43f728cbSDavid du Colombier 		b->rp[1] = (b->rp[0] | 0x20) & 0x7f;
1120*43f728cbSDavid du Colombier 		b->rp[2] = tsc;
1121*43f728cbSDavid du Colombier 		b->rp[3] = kid<<6 | 0x20;
1122*43f728cbSDavid du Colombier 		b->rp[4] = tsc>>16;
1123*43f728cbSDavid du Colombier 		b->rp[5] = tsc>>24;
1124*43f728cbSDavid du Colombier 		b->rp[6] = tsc>>32;
1125*43f728cbSDavid du Colombier 		b->rp[7] = tsc>>40;
1126*43f728cbSDavid du Colombier 		b->rp += 8;
1127*43f728cbSDavid du Colombier 		tkipencrypt(k, w, b, tsc);
1128*43f728cbSDavid du Colombier 		break;
1129*43f728cbSDavid du Colombier 	case CCMP:
1130*43f728cbSDavid du Colombier 		b->rp[0] = tsc;
1131*43f728cbSDavid du Colombier 		b->rp[1] = tsc>>8;
1132*43f728cbSDavid du Colombier 		b->rp[2] = 0;
1133*43f728cbSDavid du Colombier 		b->rp[3] = kid<<6 | 0x20;
1134*43f728cbSDavid du Colombier 		b->rp[4] = tsc>>16;
1135*43f728cbSDavid du Colombier 		b->rp[5] = tsc>>24;
1136*43f728cbSDavid du Colombier 		b->rp[6] = tsc>>32;
1137*43f728cbSDavid du Colombier 		b->rp[7] = tsc>>40;
1138*43f728cbSDavid du Colombier 		b->rp += 8;
1139*43f728cbSDavid du Colombier 		ccmpencrypt(k, w, b, tsc);
1140*43f728cbSDavid du Colombier 		break;
1141*43f728cbSDavid du Colombier 	}
1142*43f728cbSDavid du Colombier 	runlock(&wifi->crypt);
1143*43f728cbSDavid du Colombier 
1144*43f728cbSDavid du Colombier 	b->rp = (uchar*)w;
1145*43f728cbSDavid du Colombier 	w->fc[1] |= 0x40;
1146*43f728cbSDavid du Colombier 	return b;
1147*43f728cbSDavid du Colombier }
1148*43f728cbSDavid du Colombier 
1149*43f728cbSDavid du Colombier static Block*
wifidecrypt(Wifi * wifi,Wnode * wn,Block * b)1150*43f728cbSDavid du Colombier wifidecrypt(Wifi *wifi, Wnode *wn, Block *b)
1151*43f728cbSDavid du Colombier {
1152*43f728cbSDavid du Colombier 	uvlong tsc;
1153*43f728cbSDavid du Colombier 	int n, kid;
1154*43f728cbSDavid du Colombier 	Wifipkt *w;
1155*43f728cbSDavid du Colombier 	Wkey *k;
1156*43f728cbSDavid du Colombier 
1157*43f728cbSDavid du Colombier 	rlock(&wifi->crypt);
1158*43f728cbSDavid du Colombier 
1159*43f728cbSDavid du Colombier 	w = (Wifipkt*)b->rp;
1160*43f728cbSDavid du Colombier 	n = wifihdrlen(w);
1161*43f728cbSDavid du Colombier 	b->rp += n;
1162*43f728cbSDavid du Colombier 	if(BLEN(b) < 8+8)
1163*43f728cbSDavid du Colombier 		goto drop;
1164*43f728cbSDavid du Colombier 
1165*43f728cbSDavid du Colombier 	kid = b->rp[3]>>6;
1166*43f728cbSDavid du Colombier 	if((b->rp[3] & 0x20) == 0)
1167*43f728cbSDavid du Colombier 		goto drop;
1168*43f728cbSDavid du Colombier 	if((w->a1[0] & 1) == 0)
1169*43f728cbSDavid du Colombier 		kid = 4;	/* use peerwise key for non-unicast */
1170*43f728cbSDavid du Colombier 
1171*43f728cbSDavid du Colombier 	k = wn->rxkey[kid];
1172*43f728cbSDavid du Colombier 	if(k == nil)
1173*43f728cbSDavid du Colombier 		goto drop;
1174*43f728cbSDavid du Colombier 	switch(k->cipher){
1175*43f728cbSDavid du Colombier 	case TKIP:
1176*43f728cbSDavid du Colombier 		tsc =	(uvlong)b->rp[7]<<40 |
1177*43f728cbSDavid du Colombier 			(uvlong)b->rp[6]<<32 |
1178*43f728cbSDavid du Colombier 			(uvlong)b->rp[5]<<24 |
1179*43f728cbSDavid du Colombier 			(uvlong)b->rp[4]<<16 |
1180*43f728cbSDavid du Colombier 			(uvlong)b->rp[0]<<8 |
1181*43f728cbSDavid du Colombier 			(uvlong)b->rp[2];
1182*43f728cbSDavid du Colombier 		b->rp += 8;
1183*43f728cbSDavid du Colombier 		if(tsc <= k->tsc)
1184*43f728cbSDavid du Colombier 			goto drop;
1185*43f728cbSDavid du Colombier 		if(tkipdecrypt(k, w, b, tsc) != 0)
1186*43f728cbSDavid du Colombier 			goto drop;
1187*43f728cbSDavid du Colombier 		break;
1188*43f728cbSDavid du Colombier 	case CCMP:
1189*43f728cbSDavid du Colombier 		tsc =	(uvlong)b->rp[7]<<40 |
1190*43f728cbSDavid du Colombier 			(uvlong)b->rp[6]<<32 |
1191*43f728cbSDavid du Colombier 			(uvlong)b->rp[5]<<24 |
1192*43f728cbSDavid du Colombier 			(uvlong)b->rp[4]<<16 |
1193*43f728cbSDavid du Colombier 			(uvlong)b->rp[1]<<8 |
1194*43f728cbSDavid du Colombier 			(uvlong)b->rp[0];
1195*43f728cbSDavid du Colombier 		b->rp += 8;
1196*43f728cbSDavid du Colombier 		if(tsc <= k->tsc)
1197*43f728cbSDavid du Colombier 			goto drop;
1198*43f728cbSDavid du Colombier 		if(ccmpdecrypt(k, w, b, tsc) != 0)
1199*43f728cbSDavid du Colombier 			goto drop;
1200*43f728cbSDavid du Colombier 		break;
1201*43f728cbSDavid du Colombier 	default:
1202*43f728cbSDavid du Colombier 	drop:
1203*43f728cbSDavid du Colombier 		runlock(&wifi->crypt);
1204*43f728cbSDavid du Colombier 		freeb(b);
1205*43f728cbSDavid du Colombier 		return nil;
1206*43f728cbSDavid du Colombier 	}
1207*43f728cbSDavid du Colombier 	runlock(&wifi->crypt);
1208*43f728cbSDavid du Colombier 
1209*43f728cbSDavid du Colombier 	k->tsc = tsc;
1210*43f728cbSDavid du Colombier 	b->rp -= n;
1211*43f728cbSDavid du Colombier 	memmove(b->rp, w, n);
1212*43f728cbSDavid du Colombier 	w = (Wifipkt*)b->rp;
1213*43f728cbSDavid du Colombier 	w->fc[1] &= ~0x40;
1214*43f728cbSDavid du Colombier 	return b;
1215*43f728cbSDavid du Colombier }
1216*43f728cbSDavid du Colombier 
1217*43f728cbSDavid du Colombier static u16int Sbox[256] = {
1218*43f728cbSDavid du Colombier 	0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154,
1219*43f728cbSDavid du Colombier 	0x6050, 0x0203, 0xCEA9, 0x567D, 0xE719, 0xB562, 0x4DE6, 0xEC9A,
1220*43f728cbSDavid du Colombier 	0x8F45, 0x1F9D, 0x8940, 0xFA87, 0xEF15, 0xB2EB, 0x8EC9, 0xFB0B,
1221*43f728cbSDavid du Colombier 	0x41EC, 0xB367, 0x5FFD, 0x45EA, 0x23BF, 0x53F7, 0xE496, 0x9B5B,
1222*43f728cbSDavid du Colombier 	0x75C2, 0xE11C, 0x3DAE, 0x4C6A, 0x6C5A, 0x7E41, 0xF502, 0x834F,
1223*43f728cbSDavid du Colombier 	0x685C, 0x51F4, 0xD134, 0xF908, 0xE293, 0xAB73, 0x6253, 0x2A3F,
1224*43f728cbSDavid du Colombier 	0x080C, 0x9552, 0x4665, 0x9D5E, 0x3028, 0x37A1, 0x0A0F, 0x2FB5,
1225*43f728cbSDavid du Colombier 	0x0E09, 0x2436, 0x1B9B, 0xDF3D, 0xCD26, 0x4E69, 0x7FCD, 0xEA9F,
1226*43f728cbSDavid du Colombier 	0x121B, 0x1D9E, 0x5874, 0x342E, 0x362D, 0xDCB2, 0xB4EE, 0x5BFB,
1227*43f728cbSDavid du Colombier 	0xA4F6, 0x764D, 0xB761, 0x7DCE, 0x527B, 0xDD3E, 0x5E71, 0x1397,
1228*43f728cbSDavid du Colombier 	0xA6F5, 0xB968, 0x0000, 0xC12C, 0x4060, 0xE31F, 0x79C8, 0xB6ED,
1229*43f728cbSDavid du Colombier 	0xD4BE, 0x8D46, 0x67D9, 0x724B, 0x94DE, 0x98D4, 0xB0E8, 0x854A,
1230*43f728cbSDavid du Colombier 	0xBB6B, 0xC52A, 0x4FE5, 0xED16, 0x86C5, 0x9AD7, 0x6655, 0x1194,
1231*43f728cbSDavid du Colombier 	0x8ACF, 0xE910, 0x0406, 0xFE81, 0xA0F0, 0x7844, 0x25BA, 0x4BE3,
1232*43f728cbSDavid du Colombier 	0xA2F3, 0x5DFE, 0x80C0, 0x058A, 0x3FAD, 0x21BC, 0x7048, 0xF104,
1233*43f728cbSDavid du Colombier 	0x63DF, 0x77C1, 0xAF75, 0x4263, 0x2030, 0xE51A, 0xFD0E, 0xBF6D,
1234*43f728cbSDavid du Colombier 	0x814C, 0x1814, 0x2635, 0xC32F, 0xBEE1, 0x35A2, 0x88CC, 0x2E39,
1235*43f728cbSDavid du Colombier 	0x9357, 0x55F2, 0xFC82, 0x7A47, 0xC8AC, 0xBAE7, 0x322B, 0xE695,
1236*43f728cbSDavid du Colombier 	0xC0A0, 0x1998, 0x9ED1, 0xA37F, 0x4466, 0x547E, 0x3BAB, 0x0B83,
1237*43f728cbSDavid du Colombier 	0x8CCA, 0xC729, 0x6BD3, 0x283C, 0xA779, 0xBCE2, 0x161D, 0xAD76,
1238*43f728cbSDavid du Colombier 	0xDB3B, 0x6456, 0x744E, 0x141E, 0x92DB, 0x0C0A, 0x486C, 0xB8E4,
1239*43f728cbSDavid du Colombier 	0x9F5D, 0xBD6E, 0x43EF, 0xC4A6, 0x39A8, 0x31A4, 0xD337, 0xF28B,
1240*43f728cbSDavid du Colombier 	0xD532, 0x8B43, 0x6E59, 0xDAB7, 0x018C, 0xB164, 0x9CD2, 0x49E0,
1241*43f728cbSDavid du Colombier 	0xD8B4, 0xACFA, 0xF307, 0xCF25, 0xCAAF, 0xF48E, 0x47E9, 0x1018,
1242*43f728cbSDavid du Colombier 	0x6FD5, 0xF088, 0x4A6F, 0x5C72, 0x3824, 0x57F1, 0x73C7, 0x9751,
1243*43f728cbSDavid du Colombier 	0xCB23, 0xA17C, 0xE89C, 0x3E21, 0x96DD, 0x61DC, 0x0D86, 0x0F85,
1244*43f728cbSDavid du Colombier 	0xE090, 0x7C42, 0x71C4, 0xCCAA, 0x90D8, 0x0605, 0xF701, 0x1C12,
1245*43f728cbSDavid du Colombier 	0xC2A3, 0x6A5F, 0xAEF9, 0x69D0, 0x1791, 0x9958, 0x3A27, 0x27B9,
1246*43f728cbSDavid du Colombier 	0xD938, 0xEB13, 0x2BB3, 0x2233, 0xD2BB, 0xA970, 0x0789, 0x33A7,
1247*43f728cbSDavid du Colombier 	0x2DB6, 0x3C22, 0x1592, 0xC920, 0x8749, 0xAAFF, 0x5078, 0xA57A,
1248*43f728cbSDavid du Colombier 	0x038F, 0x59F8, 0x0980, 0x1A17, 0x65DA, 0xD731, 0x84C6, 0xD0B8,
1249*43f728cbSDavid du Colombier 	0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A
1250*43f728cbSDavid du Colombier };
1251*43f728cbSDavid du Colombier 
1252*43f728cbSDavid du Colombier static void
tkipk2tk(uchar key[16],u16int tk[8])1253*43f728cbSDavid du Colombier tkipk2tk(uchar key[16], u16int tk[8])
1254*43f728cbSDavid du Colombier {
1255*43f728cbSDavid du Colombier 	tk[0] = (u16int)key[1]<<8 | key[0];
1256*43f728cbSDavid du Colombier 	tk[1] = (u16int)key[3]<<8 | key[2];
1257*43f728cbSDavid du Colombier 	tk[2] = (u16int)key[5]<<8 | key[4];
1258*43f728cbSDavid du Colombier 	tk[3] = (u16int)key[7]<<8 | key[6];
1259*43f728cbSDavid du Colombier 	tk[4] = (u16int)key[9]<<8 | key[8];
1260*43f728cbSDavid du Colombier 	tk[5] = (u16int)key[11]<<8 | key[10];
1261*43f728cbSDavid du Colombier 	tk[6] = (u16int)key[13]<<8 | key[12];
1262*43f728cbSDavid du Colombier 	tk[7] = (u16int)key[15]<<8 | key[14];
1263*43f728cbSDavid du Colombier }
1264*43f728cbSDavid du Colombier 
1265*43f728cbSDavid du Colombier static void
tkipphase1(u32int tscu,uchar ta[Eaddrlen],u16int tk[8],u16int p1k[5])1266*43f728cbSDavid du Colombier tkipphase1(u32int tscu, uchar ta[Eaddrlen], u16int tk[8], u16int p1k[5])
1267*43f728cbSDavid du Colombier {
1268*43f728cbSDavid du Colombier 	u16int *k, i, x0, x1, x2;
1269*43f728cbSDavid du Colombier 
1270*43f728cbSDavid du Colombier 	p1k[0] = tscu;
1271*43f728cbSDavid du Colombier 	p1k[1] = tscu>>16;
1272*43f728cbSDavid du Colombier 	p1k[2] = (u16int)ta[1]<<8 | ta[0];
1273*43f728cbSDavid du Colombier 	p1k[3] = (u16int)ta[3]<<8 | ta[2];
1274*43f728cbSDavid du Colombier 	p1k[4] = (u16int)ta[5]<<8 | ta[4];
1275*43f728cbSDavid du Colombier 
1276*43f728cbSDavid du Colombier 	for(i=0; i<8; i++){
1277*43f728cbSDavid du Colombier 		k = &tk[i & 1];
1278*43f728cbSDavid du Colombier 
1279*43f728cbSDavid du Colombier 		x0 = p1k[4] ^ k[0];
1280*43f728cbSDavid du Colombier 		x1 = Sbox[x0 >> 8];
1281*43f728cbSDavid du Colombier 		x2 = Sbox[x0 & 0xFF] ^ ((x1>>8) | (x1<<8));
1282*43f728cbSDavid du Colombier 		p1k[0] += x2;
1283*43f728cbSDavid du Colombier 		x0 = p1k[0] ^ k[2];
1284*43f728cbSDavid du Colombier 		x1 = Sbox[x0 >> 8];
1285*43f728cbSDavid du Colombier 		x2 = Sbox[x0 & 0xFF] ^ ((x1>>8) | (x1<<8));
1286*43f728cbSDavid du Colombier 		p1k[1] += x2;
1287*43f728cbSDavid du Colombier 		x0 = p1k[1] ^ k[4];
1288*43f728cbSDavid du Colombier 		x1 = Sbox[x0 >> 8];
1289*43f728cbSDavid du Colombier 		x2 = Sbox[x0 & 0xFF] ^ ((x1>>8) | (x1<<8));
1290*43f728cbSDavid du Colombier 		p1k[2] += x2;
1291*43f728cbSDavid du Colombier 		x0 = p1k[2] ^ k[6];
1292*43f728cbSDavid du Colombier 		x1 = Sbox[x0 >> 8];
1293*43f728cbSDavid du Colombier 		x2 = Sbox[x0 & 0xFF] ^ ((x1>>8) | (x1<<8));
1294*43f728cbSDavid du Colombier 		p1k[3] += x2;
1295*43f728cbSDavid du Colombier 		x0 = p1k[3] ^ k[0];
1296*43f728cbSDavid du Colombier 		x1 = Sbox[x0 >> 8];
1297*43f728cbSDavid du Colombier 		x2 = Sbox[x0 & 0xFF] ^ ((x1>>8) | (x1<<8));
1298*43f728cbSDavid du Colombier 		p1k[4] += x2;
1299*43f728cbSDavid du Colombier 
1300*43f728cbSDavid du Colombier 		p1k[4] += i;
1301*43f728cbSDavid du Colombier 	}
1302*43f728cbSDavid du Colombier }
1303*43f728cbSDavid du Colombier 
1304*43f728cbSDavid du Colombier static void
tkipphase2(u16int tscl,u16int p1k[5],u16int tk[8],uchar rc4key[16])1305*43f728cbSDavid du Colombier tkipphase2(u16int tscl, u16int p1k[5], u16int tk[8], uchar rc4key[16])
1306*43f728cbSDavid du Colombier {
1307*43f728cbSDavid du Colombier 	u16int ppk[6], x0, x1, x2;
1308*43f728cbSDavid du Colombier 
1309*43f728cbSDavid du Colombier 	ppk[0] = p1k[0];
1310*43f728cbSDavid du Colombier 	ppk[1] = p1k[1];
1311*43f728cbSDavid du Colombier 	ppk[2] = p1k[2];
1312*43f728cbSDavid du Colombier 	ppk[3] = p1k[3];
1313*43f728cbSDavid du Colombier 	ppk[4] = p1k[4];
1314*43f728cbSDavid du Colombier 	ppk[5] = p1k[4] + tscl;
1315*43f728cbSDavid du Colombier 
1316*43f728cbSDavid du Colombier 	x0 = ppk[5] ^ tk[0];
1317*43f728cbSDavid du Colombier 	x1 = Sbox[x0 >> 8];
1318*43f728cbSDavid du Colombier 	x2 = Sbox[x0 & 0xFF] ^ ((x1>>8) | (x1<<8));
1319*43f728cbSDavid du Colombier 	ppk[0] += x2;
1320*43f728cbSDavid du Colombier 	x0 = ppk[0] ^ tk[1];
1321*43f728cbSDavid du Colombier 	x1 = Sbox[x0 >> 8];
1322*43f728cbSDavid du Colombier 	x2 = Sbox[x0 & 0xFF] ^ ((x1>>8) | (x1<<8));
1323*43f728cbSDavid du Colombier 	ppk[1] += x2;
1324*43f728cbSDavid du Colombier 	x0 = ppk[1] ^ tk[2];
1325*43f728cbSDavid du Colombier 	x1 = Sbox[x0 >> 8];
1326*43f728cbSDavid du Colombier 	x2 = Sbox[x0 & 0xFF] ^ ((x1>>8) | (x1<<8));
1327*43f728cbSDavid du Colombier 	ppk[2] += x2;
1328*43f728cbSDavid du Colombier 	x0 = ppk[2] ^ tk[3];
1329*43f728cbSDavid du Colombier 	x1 = Sbox[x0 >> 8];
1330*43f728cbSDavid du Colombier 	x2 = Sbox[x0 & 0xFF] ^ ((x1>>8) | (x1<<8));
1331*43f728cbSDavid du Colombier 	ppk[3] += x2;
1332*43f728cbSDavid du Colombier 	x0 = ppk[3] ^ tk[4];
1333*43f728cbSDavid du Colombier 	x1 = Sbox[x0 >> 8];
1334*43f728cbSDavid du Colombier 	x2 = Sbox[x0 & 0xFF] ^ ((x1>>8) | (x1<<8));
1335*43f728cbSDavid du Colombier 	ppk[4] += x2;
1336*43f728cbSDavid du Colombier 	x0 = ppk[4] ^ tk[5];
1337*43f728cbSDavid du Colombier 	x1 = Sbox[x0 >> 8];
1338*43f728cbSDavid du Colombier 	x2 = Sbox[x0 & 0xFF] ^ ((x1>>8) | (x1<<8));
1339*43f728cbSDavid du Colombier 	ppk[5] += x2;
1340*43f728cbSDavid du Colombier 
1341*43f728cbSDavid du Colombier 	x2 = ppk[5] ^ tk[6];
1342*43f728cbSDavid du Colombier 	ppk[0] += (x2 >> 1) | (x2 << 15);
1343*43f728cbSDavid du Colombier 	x2 = ppk[0] ^ tk[7];
1344*43f728cbSDavid du Colombier 	ppk[1] += (x2 >> 1) | (x2 << 15);
1345*43f728cbSDavid du Colombier 
1346*43f728cbSDavid du Colombier 	x2 = ppk[1];
1347*43f728cbSDavid du Colombier 	ppk[2] += (x2 >> 1) | (x2 << 15);
1348*43f728cbSDavid du Colombier 	x2 = ppk[2];
1349*43f728cbSDavid du Colombier 	ppk[3] += (x2 >> 1) | (x2 << 15);
1350*43f728cbSDavid du Colombier 	x2 = ppk[3];
1351*43f728cbSDavid du Colombier 	ppk[4] += (x2 >> 1) | (x2 << 15);
1352*43f728cbSDavid du Colombier 	x2 = ppk[4];
1353*43f728cbSDavid du Colombier 	ppk[5] += (x2 >> 1) | (x2 << 15);
1354*43f728cbSDavid du Colombier 
1355*43f728cbSDavid du Colombier 	rc4key[0] = tscl >> 8;
1356*43f728cbSDavid du Colombier 	rc4key[1] = (rc4key[0] | 0x20) & 0x7F;
1357*43f728cbSDavid du Colombier 	rc4key[2] = tscl;
1358*43f728cbSDavid du Colombier 	rc4key[3] = (ppk[5] ^ tk[0]) >> 1;
1359*43f728cbSDavid du Colombier 	rc4key[4]  = ppk[0];
1360*43f728cbSDavid du Colombier 	rc4key[5]  = ppk[0] >> 8;
1361*43f728cbSDavid du Colombier 	rc4key[6]  = ppk[1];
1362*43f728cbSDavid du Colombier 	rc4key[7]  = ppk[1] >> 8;
1363*43f728cbSDavid du Colombier 	rc4key[8]  = ppk[2];
1364*43f728cbSDavid du Colombier 	rc4key[9]  = ppk[2] >> 8;
1365*43f728cbSDavid du Colombier 	rc4key[10] = ppk[3];
1366*43f728cbSDavid du Colombier 	rc4key[11] = ppk[3] >> 8;
1367*43f728cbSDavid du Colombier 	rc4key[12] = ppk[4];
1368*43f728cbSDavid du Colombier 	rc4key[13] = ppk[4] >> 8;
1369*43f728cbSDavid du Colombier 	rc4key[14] = ppk[5];
1370*43f728cbSDavid du Colombier 	rc4key[15] = ppk[5] >> 8;
1371*43f728cbSDavid du Colombier }
1372*43f728cbSDavid du Colombier 
1373*43f728cbSDavid du Colombier typedef struct MICstate MICstate;
1374*43f728cbSDavid du Colombier struct MICstate
1375*43f728cbSDavid du Colombier {
1376*43f728cbSDavid du Colombier 	u32int	l;
1377*43f728cbSDavid du Colombier 	u32int	r;
1378*43f728cbSDavid du Colombier 	u32int	m;
1379*43f728cbSDavid du Colombier 	u32int	n;
1380*43f728cbSDavid du Colombier };
1381*43f728cbSDavid du Colombier 
1382*43f728cbSDavid du Colombier static void
micsetup(MICstate * s,uchar key[8])1383*43f728cbSDavid du Colombier micsetup(MICstate *s, uchar key[8])
1384*43f728cbSDavid du Colombier {
1385*43f728cbSDavid du Colombier 	s->l =	(u32int)key[0] |
1386*43f728cbSDavid du Colombier 		(u32int)key[1]<<8 |
1387*43f728cbSDavid du Colombier 		(u32int)key[2]<<16 |
1388*43f728cbSDavid du Colombier 		(u32int)key[3]<<24;
1389*43f728cbSDavid du Colombier 	s->r =	(u32int)key[4] |
1390*43f728cbSDavid du Colombier 		(u32int)key[5]<<8 |
1391*43f728cbSDavid du Colombier 		(u32int)key[6]<<16 |
1392*43f728cbSDavid du Colombier 		(u32int)key[7]<<24;
1393*43f728cbSDavid du Colombier 	s->m = 0;
1394*43f728cbSDavid du Colombier 	s->n = 0;
1395*43f728cbSDavid du Colombier }
1396*43f728cbSDavid du Colombier 
1397*43f728cbSDavid du Colombier static void
micupdate(MICstate * s,uchar * data,ulong len)1398*43f728cbSDavid du Colombier micupdate(MICstate *s, uchar *data, ulong len)
1399*43f728cbSDavid du Colombier {
1400*43f728cbSDavid du Colombier 	u32int l, r, m, n, e;
1401*43f728cbSDavid du Colombier 
1402*43f728cbSDavid du Colombier 	l = s->l;
1403*43f728cbSDavid du Colombier 	r = s->r;
1404*43f728cbSDavid du Colombier 	m = s->m;
1405*43f728cbSDavid du Colombier 	n = s->n;
1406*43f728cbSDavid du Colombier 	e = n + len;
1407*43f728cbSDavid du Colombier 	while(n != e){
1408*43f728cbSDavid du Colombier 		m >>= 8;
1409*43f728cbSDavid du Colombier 		m |= (u32int)*data++ << 24;
1410*43f728cbSDavid du Colombier 		if(++n & 3)
1411*43f728cbSDavid du Colombier 			continue;
1412*43f728cbSDavid du Colombier 		l ^= m;
1413*43f728cbSDavid du Colombier 		r ^= (l << 17) | (l >> 15);
1414*43f728cbSDavid du Colombier 		l += r;
1415*43f728cbSDavid du Colombier 		r ^= ((l & 0x00FF00FFUL)<<8) | ((l & 0xFF00FF00UL)>>8);
1416*43f728cbSDavid du Colombier 		l += r;
1417*43f728cbSDavid du Colombier 		r ^= (l << 3) | (l >> 29);
1418*43f728cbSDavid du Colombier 		l += r;
1419*43f728cbSDavid du Colombier 		r ^= (l >> 2) | (l << 30);
1420*43f728cbSDavid du Colombier 		l += r;
1421*43f728cbSDavid du Colombier 	}
1422*43f728cbSDavid du Colombier 	s->l = l;
1423*43f728cbSDavid du Colombier 	s->r = r;
1424*43f728cbSDavid du Colombier 	s->m = m;
1425*43f728cbSDavid du Colombier 	s->n = n;
1426*43f728cbSDavid du Colombier }
1427*43f728cbSDavid du Colombier 
1428*43f728cbSDavid du Colombier static void
micfinish(MICstate * s,uchar mic[8])1429*43f728cbSDavid du Colombier micfinish(MICstate *s, uchar mic[8])
1430*43f728cbSDavid du Colombier {
1431*43f728cbSDavid du Colombier 	static uchar pad[8] = { 0x5a, 0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00, };
1432*43f728cbSDavid du Colombier 
1433*43f728cbSDavid du Colombier 	micupdate(s, pad, sizeof(pad));
1434*43f728cbSDavid du Colombier 
1435*43f728cbSDavid du Colombier 	mic[0] = s->l;
1436*43f728cbSDavid du Colombier 	mic[1] = s->l>>8;
1437*43f728cbSDavid du Colombier 	mic[2] = s->l>>16;
1438*43f728cbSDavid du Colombier 	mic[3] = s->l>>24;
1439*43f728cbSDavid du Colombier 	mic[4] = s->r;
1440*43f728cbSDavid du Colombier 	mic[5] = s->r>>8;
1441*43f728cbSDavid du Colombier 	mic[6] = s->r>>16;
1442*43f728cbSDavid du Colombier 	mic[7] = s->r>>24;
1443*43f728cbSDavid du Colombier }
1444*43f728cbSDavid du Colombier 
1445*43f728cbSDavid du Colombier static uchar pad4[4] = { 0x00, 0x00, 0x00, 0x00, };
1446*43f728cbSDavid du Colombier 
1447*43f728cbSDavid du Colombier static void
tkipencrypt(Wkey * k,Wifipkt * w,Block * b,uvlong tsc)1448*43f728cbSDavid du Colombier tkipencrypt(Wkey *k, Wifipkt *w, Block *b, uvlong tsc)
1449*43f728cbSDavid du Colombier {
1450*43f728cbSDavid du Colombier 	u16int tk[8], p1k[5];
1451*43f728cbSDavid du Colombier 	uchar seed[16];
1452*43f728cbSDavid du Colombier 	RC4state rs;
1453*43f728cbSDavid du Colombier 	MICstate ms;
1454*43f728cbSDavid du Colombier 	ulong crc;
1455*43f728cbSDavid du Colombier 
1456*43f728cbSDavid du Colombier 	micsetup(&ms, k->key+24);
1457*43f728cbSDavid du Colombier 	micupdate(&ms, dstaddr(w), Eaddrlen);
1458*43f728cbSDavid du Colombier 	micupdate(&ms, srcaddr(w), Eaddrlen);
1459*43f728cbSDavid du Colombier 	micupdate(&ms, pad4, 4);
1460*43f728cbSDavid du Colombier 	micupdate(&ms, b->rp, BLEN(b));
1461*43f728cbSDavid du Colombier 	micfinish(&ms, b->wp);
1462*43f728cbSDavid du Colombier 	b->wp += 8;
1463*43f728cbSDavid du Colombier 
1464*43f728cbSDavid du Colombier 	crc = ethercrc(b->rp, BLEN(b));
1465*43f728cbSDavid du Colombier 	crc = ~crc;
1466*43f728cbSDavid du Colombier 	b->wp[0] = crc;
1467*43f728cbSDavid du Colombier 	b->wp[1] = crc>>8;
1468*43f728cbSDavid du Colombier 	b->wp[2] = crc>>16;
1469*43f728cbSDavid du Colombier 	b->wp[3] = crc>>24;
1470*43f728cbSDavid du Colombier 	b->wp += 4;
1471*43f728cbSDavid du Colombier 
1472*43f728cbSDavid du Colombier 	tkipk2tk(k->key, tk);
1473*43f728cbSDavid du Colombier 	tkipphase1(tsc >> 16, w->a2, tk, p1k);
1474*43f728cbSDavid du Colombier 	tkipphase2(tsc & 0xFFFF, p1k, tk, seed);
1475*43f728cbSDavid du Colombier 	setupRC4state(&rs, seed, sizeof(seed));
1476*43f728cbSDavid du Colombier 	rc4(&rs, b->rp, BLEN(b));
1477*43f728cbSDavid du Colombier }
1478*43f728cbSDavid du Colombier 
1479*43f728cbSDavid du Colombier static int
tkipdecrypt(Wkey * k,Wifipkt * w,Block * b,uvlong tsc)1480*43f728cbSDavid du Colombier tkipdecrypt(Wkey *k, Wifipkt *w, Block *b, uvlong tsc)
1481*43f728cbSDavid du Colombier {
1482*43f728cbSDavid du Colombier 	uchar seed[16], mic[8];
1483*43f728cbSDavid du Colombier 	u16int tk[8], p1k[5];
1484*43f728cbSDavid du Colombier 	RC4state rs;
1485*43f728cbSDavid du Colombier 	MICstate ms;
1486*43f728cbSDavid du Colombier 	ulong crc;
1487*43f728cbSDavid du Colombier 
1488*43f728cbSDavid du Colombier 	if(BLEN(b) < 8+4)
1489*43f728cbSDavid du Colombier 		return -1;
1490*43f728cbSDavid du Colombier 
1491*43f728cbSDavid du Colombier 	tkipk2tk(k->key, tk);
1492*43f728cbSDavid du Colombier 	tkipphase1(tsc >> 16, w->a2, tk, p1k);
1493*43f728cbSDavid du Colombier 	tkipphase2(tsc & 0xFFFF, p1k, tk, seed);
1494*43f728cbSDavid du Colombier 	setupRC4state(&rs, seed, sizeof(seed));
1495*43f728cbSDavid du Colombier 	rc4(&rs, b->rp, BLEN(b));
1496*43f728cbSDavid du Colombier 
1497*43f728cbSDavid du Colombier 	b->wp -= 4;
1498*43f728cbSDavid du Colombier 	crc =	(ulong)b->wp[0] |
1499*43f728cbSDavid du Colombier 		(ulong)b->wp[1]<<8 |
1500*43f728cbSDavid du Colombier 		(ulong)b->wp[2]<<16 |
1501*43f728cbSDavid du Colombier 		(ulong)b->wp[3]<<24;
1502*43f728cbSDavid du Colombier 	crc = ~crc;
1503*43f728cbSDavid du Colombier 	crc ^= ethercrc(b->rp, BLEN(b));
1504*43f728cbSDavid du Colombier 
1505*43f728cbSDavid du Colombier 	b->wp -= 8;
1506*43f728cbSDavid du Colombier 	micsetup(&ms, k->key+16);
1507*43f728cbSDavid du Colombier 	micupdate(&ms, dstaddr(w), Eaddrlen);
1508*43f728cbSDavid du Colombier 	micupdate(&ms, srcaddr(w), Eaddrlen);
1509*43f728cbSDavid du Colombier 	micupdate(&ms, pad4, 4);
1510*43f728cbSDavid du Colombier 	micupdate(&ms, b->rp, BLEN(b));
1511*43f728cbSDavid du Colombier 	micfinish(&ms, mic);
1512*43f728cbSDavid du Colombier 
1513*43f728cbSDavid du Colombier 	return memcmp(b->wp, mic, 8) | crc;
1514*43f728cbSDavid du Colombier }
1515*43f728cbSDavid du Colombier 
1516*43f728cbSDavid du Colombier static uchar*
putbe(uchar * p,int L,uint v)1517*43f728cbSDavid du Colombier putbe(uchar *p, int L, uint v)
1518*43f728cbSDavid du Colombier {
1519*43f728cbSDavid du Colombier 	while(--L >= 0)
1520*43f728cbSDavid du Colombier 		*p++ = (v >> L*8) & 0xFF;
1521*43f728cbSDavid du Colombier 	return p;
1522*43f728cbSDavid du Colombier }
1523*43f728cbSDavid du Colombier 
1524*43f728cbSDavid du Colombier static void
xblock(int L,int M,uchar * N,uchar * a,int la,int lm,uchar t[16],AESstate * s)1525*43f728cbSDavid du Colombier xblock(int L, int M, uchar *N, uchar *a, int la, int lm, uchar t[16], AESstate *s)
1526*43f728cbSDavid du Colombier {
1527*43f728cbSDavid du Colombier 	uchar l[8], *p, *x, *e;
1528*43f728cbSDavid du Colombier 
1529*43f728cbSDavid du Colombier 	assert(M >= 4 && M <= 16);
1530*43f728cbSDavid du Colombier 	assert(L >= 2 && L <= 4);
1531*43f728cbSDavid du Colombier 
1532*43f728cbSDavid du Colombier 	t[0] = ((la > 0)<<6) | ((M-2)/2)<<3 | (L-1);	/* flags */
1533*43f728cbSDavid du Colombier 	memmove(&t[1], N, 15-L);
1534*43f728cbSDavid du Colombier 	putbe(&t[16-L], L, lm);
1535*43f728cbSDavid du Colombier 	aes_encrypt(s->ekey, s->rounds, t, t);
1536*43f728cbSDavid du Colombier 
1537*43f728cbSDavid du Colombier 	if(la > 0){
1538*43f728cbSDavid du Colombier 		assert(la < 0xFF00);
1539*43f728cbSDavid du Colombier 		for(p = l, e = putbe(l, 2, la), x = t; p < e; x++, p++)
1540*43f728cbSDavid du Colombier 			*x ^= *p;
1541*43f728cbSDavid du Colombier 		for(e = a + la; a < e; x = t){
1542*43f728cbSDavid du Colombier 			for(; a < e && x < &t[16]; x++, a++)
1543*43f728cbSDavid du Colombier 				*x ^= *a;
1544*43f728cbSDavid du Colombier 			aes_encrypt(s->ekey, s->rounds, t, t);
1545*43f728cbSDavid du Colombier 		}
1546*43f728cbSDavid du Colombier 	}
1547*43f728cbSDavid du Colombier }
1548*43f728cbSDavid du Colombier 
1549*43f728cbSDavid du Colombier static uchar*
sblock(int L,uchar * N,uint i,uchar b[16],AESstate * s)1550*43f728cbSDavid du Colombier sblock(int L, uchar *N, uint i, uchar b[16], AESstate *s)
1551*43f728cbSDavid du Colombier {
1552*43f728cbSDavid du Colombier 	b[0] = L-1;	/* flags */
1553*43f728cbSDavid du Colombier 	memmove(&b[1], N, 15-L);
1554*43f728cbSDavid du Colombier 	putbe(&b[16-L], L, i);
1555*43f728cbSDavid du Colombier 	aes_encrypt(s->ekey, s->rounds, b, b);
1556*43f728cbSDavid du Colombier 	return b;
1557*43f728cbSDavid du Colombier };
1558*43f728cbSDavid du Colombier 
1559*43f728cbSDavid du Colombier static void
aesCCMencrypt(int L,int M,uchar * N,uchar * a,int la,uchar * m,int lm,AESstate * s)1560*43f728cbSDavid du Colombier aesCCMencrypt(int L, int M, uchar *N /* N[15-L] */,
1561*43f728cbSDavid du Colombier 	uchar *a /* a[la] */, int la,
1562*43f728cbSDavid du Colombier 	uchar *m /* m[lm+M] */, int lm,
1563*43f728cbSDavid du Colombier 	AESstate *s)
1564*43f728cbSDavid du Colombier {
1565*43f728cbSDavid du Colombier 	uchar t[16], b[16], *p, *x;
1566*43f728cbSDavid du Colombier 	uint i;
1567*43f728cbSDavid du Colombier 
1568*43f728cbSDavid du Colombier 	xblock(L, M, N, a, la, lm, t, s);
1569*43f728cbSDavid du Colombier 
1570*43f728cbSDavid du Colombier 	for(i = 1; lm >= 16; i++, m += 16, lm -= 16){
1571*43f728cbSDavid du Colombier 		sblock(L, N, i, b, s);
1572*43f728cbSDavid du Colombier 
1573*43f728cbSDavid du Colombier 		*((u32int*)&t[0]) ^= *((u32int*)&m[0]);
1574*43f728cbSDavid du Colombier 		*((u32int*)&m[0]) ^= *((u32int*)&b[0]);
1575*43f728cbSDavid du Colombier 		*((u32int*)&t[4]) ^= *((u32int*)&m[4]);
1576*43f728cbSDavid du Colombier 		*((u32int*)&m[4]) ^= *((u32int*)&b[4]);
1577*43f728cbSDavid du Colombier 		*((u32int*)&t[8]) ^= *((u32int*)&m[8]);
1578*43f728cbSDavid du Colombier 		*((u32int*)&m[8]) ^= *((u32int*)&b[8]);
1579*43f728cbSDavid du Colombier 		*((u32int*)&t[12]) ^= *((u32int*)&m[12]);
1580*43f728cbSDavid du Colombier 		*((u32int*)&m[12]) ^= *((u32int*)&b[12]);
1581*43f728cbSDavid du Colombier 
1582*43f728cbSDavid du Colombier 		aes_encrypt(s->ekey, s->rounds, t, t);
1583*43f728cbSDavid du Colombier 	}
1584*43f728cbSDavid du Colombier 	if(lm > 0){
1585*43f728cbSDavid du Colombier 		for(p = sblock(L, N, i, b, s), x = t; p < &b[lm]; x++, m++, p++){
1586*43f728cbSDavid du Colombier 			*x ^= *m;
1587*43f728cbSDavid du Colombier 			*m ^= *p;
1588*43f728cbSDavid du Colombier 		}
1589*43f728cbSDavid du Colombier 		aes_encrypt(s->ekey, s->rounds, t, t);
1590*43f728cbSDavid du Colombier 	}
1591*43f728cbSDavid du Colombier 
1592*43f728cbSDavid du Colombier 	for(p = sblock(L, N, 0, b, s), x = t; p < &b[M]; x++, p++)
1593*43f728cbSDavid du Colombier 		*x ^= *p;
1594*43f728cbSDavid du Colombier 
1595*43f728cbSDavid du Colombier 	memmove(m, t, M);
1596*43f728cbSDavid du Colombier }
1597*43f728cbSDavid du Colombier 
1598*43f728cbSDavid du Colombier static int
aesCCMdecrypt(int L,int M,uchar * N,uchar * a,int la,uchar * m,int lm,AESstate * s)1599*43f728cbSDavid du Colombier aesCCMdecrypt(int L, int M, uchar *N /* N[15-L] */,
1600*43f728cbSDavid du Colombier 	uchar *a /* a[la] */, int la,
1601*43f728cbSDavid du Colombier 	uchar *m /* m[lm+M] */, int lm,
1602*43f728cbSDavid du Colombier 	AESstate *s)
1603*43f728cbSDavid du Colombier {
1604*43f728cbSDavid du Colombier 	uchar t[16], b[16], *p, *x;
1605*43f728cbSDavid du Colombier 	uint i;
1606*43f728cbSDavid du Colombier 
1607*43f728cbSDavid du Colombier 	xblock(L, M, N, a, la, lm, t, s);
1608*43f728cbSDavid du Colombier 
1609*43f728cbSDavid du Colombier 	for(i = 1; lm >= 16; i++, m += 16, lm -= 16){
1610*43f728cbSDavid du Colombier 		sblock(L, N, i, b, s);
1611*43f728cbSDavid du Colombier 
1612*43f728cbSDavid du Colombier 		*((u32int*)&m[0]) ^= *((u32int*)&b[0]);
1613*43f728cbSDavid du Colombier 		*((u32int*)&t[0]) ^= *((u32int*)&m[0]);
1614*43f728cbSDavid du Colombier 		*((u32int*)&m[4]) ^= *((u32int*)&b[4]);
1615*43f728cbSDavid du Colombier 		*((u32int*)&t[4]) ^= *((u32int*)&m[4]);
1616*43f728cbSDavid du Colombier 		*((u32int*)&m[8]) ^= *((u32int*)&b[8]);
1617*43f728cbSDavid du Colombier 		*((u32int*)&t[8]) ^= *((u32int*)&m[8]);
1618*43f728cbSDavid du Colombier 		*((u32int*)&m[12]) ^= *((u32int*)&b[12]);
1619*43f728cbSDavid du Colombier 		*((u32int*)&t[12]) ^= *((u32int*)&m[12]);
1620*43f728cbSDavid du Colombier 
1621*43f728cbSDavid du Colombier 		aes_encrypt(s->ekey, s->rounds, t, t);
1622*43f728cbSDavid du Colombier 	}
1623*43f728cbSDavid du Colombier 	if(lm > 0){
1624*43f728cbSDavid du Colombier 		for(p = sblock(L, N, i, b, s), x = t; p < &b[lm]; x++, m++, p++){
1625*43f728cbSDavid du Colombier 			*m ^= *p;
1626*43f728cbSDavid du Colombier 			*x ^= *m;
1627*43f728cbSDavid du Colombier 		}
1628*43f728cbSDavid du Colombier 		aes_encrypt(s->ekey, s->rounds, t, t);
1629*43f728cbSDavid du Colombier 	}
1630*43f728cbSDavid du Colombier 
1631*43f728cbSDavid du Colombier 	for(p = sblock(L, N, 0, b, s), x = t; p < &b[M]; x++, p++)
1632*43f728cbSDavid du Colombier 		*x ^= *p;
1633*43f728cbSDavid du Colombier 
1634*43f728cbSDavid du Colombier 	return memcmp(m, t, M);
1635*43f728cbSDavid du Colombier }
1636*43f728cbSDavid du Colombier 
1637*43f728cbSDavid du Colombier static int
setupCCMP(Wifipkt * w,uvlong tsc,uchar nonce[13],uchar auth[32])1638*43f728cbSDavid du Colombier setupCCMP(Wifipkt *w, uvlong tsc, uchar nonce[13], uchar auth[32])
1639*43f728cbSDavid du Colombier {
1640*43f728cbSDavid du Colombier 	uchar *p;
1641*43f728cbSDavid du Colombier 
1642*43f728cbSDavid du Colombier 	nonce[0] = ((w->fc[0] & 0x0c) == 0x00) << 4;
1643*43f728cbSDavid du Colombier 	memmove(&nonce[1], w->a2, Eaddrlen);
1644*43f728cbSDavid du Colombier 	nonce[7]  = tsc >> 40;
1645*43f728cbSDavid du Colombier 	nonce[8]  = tsc >> 32;
1646*43f728cbSDavid du Colombier 	nonce[9]  = tsc >> 24;
1647*43f728cbSDavid du Colombier 	nonce[10] = tsc >> 16;
1648*43f728cbSDavid du Colombier 	nonce[11] = tsc >> 8;
1649*43f728cbSDavid du Colombier 	nonce[12] = tsc;
1650*43f728cbSDavid du Colombier 
1651*43f728cbSDavid du Colombier 	p = auth;
1652*43f728cbSDavid du Colombier 	*p++ = (w->fc[0] & (((w->fc[0] & 0x0c) == 0x08) ? 0x0f : 0xff));
1653*43f728cbSDavid du Colombier 	*p++ = (w->fc[1] & ~0x38) | 0x40;
1654*43f728cbSDavid du Colombier 	memmove(p, w->a1, Eaddrlen); p += Eaddrlen;
1655*43f728cbSDavid du Colombier 	memmove(p, w->a2, Eaddrlen); p += Eaddrlen;
1656*43f728cbSDavid du Colombier 	memmove(p, w->a3, Eaddrlen); p += Eaddrlen;
1657*43f728cbSDavid du Colombier 	*p++ = w->seq[0] & 0x0f;
1658*43f728cbSDavid du Colombier 	*p++ = 0;
1659*43f728cbSDavid du Colombier 	if((w->fc[1] & 3) == 0x03) {
1660*43f728cbSDavid du Colombier 		memmove(p, w->a4, Eaddrlen);
1661*43f728cbSDavid du Colombier 		p += Eaddrlen;
1662*43f728cbSDavid du Colombier 	}
1663*43f728cbSDavid du Colombier 
1664*43f728cbSDavid du Colombier 	return p - auth;
1665*43f728cbSDavid du Colombier }
1666*43f728cbSDavid du Colombier 
1667*43f728cbSDavid du Colombier static void
ccmpencrypt(Wkey * k,Wifipkt * w,Block * b,uvlong tsc)1668*43f728cbSDavid du Colombier ccmpencrypt(Wkey *k, Wifipkt *w, Block *b, uvlong tsc)
1669*43f728cbSDavid du Colombier {
1670*43f728cbSDavid du Colombier 	uchar auth[32], nonce[13];
1671*43f728cbSDavid du Colombier 
1672*43f728cbSDavid du Colombier 	aesCCMencrypt(2, 8, nonce, auth,
1673*43f728cbSDavid du Colombier 		setupCCMP(w, tsc, nonce, auth),
1674*43f728cbSDavid du Colombier 		b->rp, BLEN(b), (AESstate*)k->key);
1675*43f728cbSDavid du Colombier 	b->wp += 8;
1676*43f728cbSDavid du Colombier }
1677*43f728cbSDavid du Colombier 
1678*43f728cbSDavid du Colombier static int
ccmpdecrypt(Wkey * k,Wifipkt * w,Block * b,uvlong tsc)1679*43f728cbSDavid du Colombier ccmpdecrypt(Wkey *k, Wifipkt *w, Block *b, uvlong tsc)
1680*43f728cbSDavid du Colombier {
1681*43f728cbSDavid du Colombier 	uchar auth[32], nonce[13];
1682*43f728cbSDavid du Colombier 
1683*43f728cbSDavid du Colombier 	if(BLEN(b) < 8)
1684*43f728cbSDavid du Colombier 		return -1;
1685*43f728cbSDavid du Colombier 
1686*43f728cbSDavid du Colombier 	b->wp -= 8;
1687*43f728cbSDavid du Colombier 	return aesCCMdecrypt(2, 8, nonce, auth,
1688*43f728cbSDavid du Colombier 		setupCCMP(w, tsc, nonce, auth),
1689*43f728cbSDavid du Colombier 		b->rp, BLEN(b), (AESstate*)k->key);
1690*43f728cbSDavid du Colombier }
1691