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