]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/wifi.c
pci: add intel qm67 pch
[plan9front.git] / sys / src / 9 / pc / wifi.c
1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "io.h"
7 #include "pool.h"
8 #include "ureg.h"
9 #include "../port/error.h"
10 #include "../port/netif.h"
11
12 #include "etherif.h"
13 #include "wifi.h"
14
15 typedef struct SNAP SNAP;
16 struct SNAP
17 {
18         uchar   dsap;
19         uchar   ssap;
20         uchar   control;
21         uchar   orgcode[3];
22         uchar   type[2];
23 };
24
25 enum {
26         SNAPHDRSIZE = 8,
27 };
28
29 static char Snone[] = "new";
30 static char Sconn[] = "connecting";
31 static char Sauth[] = "authenticated";
32 static char Sunauth[] = "unauthentictaed";
33 static char Sassoc[] = "associated";
34 static char Sunassoc[] = "unassociated";
35
36 void
37 wifiiq(Wifi *wifi, Block *b)
38 {
39         SNAP s;
40         Wifipkt w;
41         Etherpkt *e;
42
43         if(BLEN(b) < WIFIHDRSIZE)
44                 goto drop;
45         memmove(&w, b->rp, WIFIHDRSIZE);
46         switch(w.fc[0] & 0x0c){
47         case 0x00:      /* management */
48                 if((w.fc[1] & 3) != 0x00)       /* STA->STA */
49                         break;
50                 qpass(wifi->iq, b);
51                 return;
52         case 0x04:      /* control */
53                 break;
54         case 0x08:      /* data */
55                 b->rp += WIFIHDRSIZE;
56                 switch(w.fc[0] & 0xf0){
57                 case 0x80:
58                         b->rp += 2;
59                         if(w.fc[1] & 0x80)
60                                 b->rp += 4;
61                 case 0x00:
62                         break;
63                 default:
64                         goto drop;
65                 }
66                 if(BLEN(b) < SNAPHDRSIZE)
67                         break;
68                 memmove(&s, b->rp, SNAPHDRSIZE);
69                 if(s.dsap != 0xAA || s.ssap != 0xAA || s.control != 3)
70                         break;
71                 if(s.orgcode[0] != 0 || s.orgcode[1] != 0 || s.orgcode[2] != 0)
72                         break;
73                 b->rp += SNAPHDRSIZE-ETHERHDRSIZE;
74                 e = (Etherpkt*)b->rp;
75                 switch(w.fc[1] & 0x03){
76                 case 0x00:      /* STA->STA */
77                         memmove(e->d, w.a1, Eaddrlen);
78                         memmove(e->s, w.a2, Eaddrlen);
79                         break;
80                 case 0x01:      /* STA->AP */
81                         memmove(e->d, w.a3, Eaddrlen);
82                         memmove(e->s, w.a2, Eaddrlen);
83                         break;
84                 case 0x02:      /* AP->STA */
85                         memmove(e->d, w.a1, Eaddrlen);
86                         memmove(e->s, w.a3, Eaddrlen);
87                         break;
88                 case 0x03:      /* AP->AP */
89                         goto drop;
90                 }
91                 memmove(e->type, s.type, 2);
92                 etheriq(wifi->ether, b, 1);
93                 return;
94         }
95 drop:
96         freeb(b);
97 }
98
99 static void
100 wifitx(Wifi *wifi, Wnode *wn, Block *b)
101 {
102         Wifipkt *w;
103         uint seq;
104
105         seq = incref(&wifi->txseq);
106         seq <<= 4;
107
108         w = (Wifipkt*)b->rp;
109         w->dur[0] = 0;
110         w->dur[1] = 0;
111         w->seq[0] = seq;
112         w->seq[1] = seq>>8;
113
114         (*wifi->transmit)(wifi, wn, b);
115 }
116
117
118 static Wnode*
119 nodelookup(Wifi *wifi, uchar *bssid, int new)
120 {
121         Wnode *wn, *nn;
122
123         if(memcmp(bssid, wifi->ether->bcast, Eaddrlen) == 0)
124                 return nil;
125         if((wn = wifi->bss) != nil){
126                 if(memcmp(wn->bssid, bssid, Eaddrlen) == 0){
127                         wn->lastseen = MACHP(0)->ticks;
128                         return wn;
129                 }
130         }
131         if((nn = wifi->node) == wn)
132                 nn++;
133         for(wn = wifi->node; wn != &wifi->node[nelem(wifi->node)]; wn++){
134                 if(wn == wifi->bss)
135                         continue;
136                 if(memcmp(wn->bssid, bssid, Eaddrlen) == 0){
137                         wn->lastseen = MACHP(0)->ticks;
138                         return wn;
139                 }
140                 if(wn->lastseen < nn->lastseen)
141                         nn = wn;
142         }
143         if(!new)
144                 return nil;
145         memmove(nn->bssid, bssid, Eaddrlen);
146         nn->ssid[0] = 0;
147         nn->ival = 0;
148         nn->cap = 0;
149         nn->aid = 0;
150         nn->channel = 0;
151         nn->lastseen = MACHP(0)->ticks;
152         return nn;
153 }
154
155 static void
156 sendauth(Wifi *wifi, Wnode *bss)
157 {
158         Wifipkt *w;
159         Block *b;
160         uchar *p;
161
162         b = allocb(WIFIHDRSIZE + 3*2);
163         w = (Wifipkt*)b->wp;
164         w->fc[0] = 0xB0;        /* auth request */
165         w->fc[1] = 0x00;        /* STA->STA */
166         memmove(w->a1, bss->bssid, Eaddrlen);   /* ??? */
167         memmove(w->a2, wifi->ether->ea, Eaddrlen);
168         memmove(w->a3, bss->bssid, Eaddrlen);
169         b->wp += WIFIHDRSIZE;
170         p = b->wp;
171         *p++ = 0;       /* alg */
172         *p++ = 0;
173         *p++ = 1;       /* seq */
174         *p++ = 0;
175         *p++ = 0;       /* status */
176         *p++ = 0;
177         b->wp = p;
178         wifitx(wifi, bss, b);
179 }
180
181 static void
182 sendassoc(Wifi *wifi, Wnode *bss)
183 {
184         Wifipkt *w;
185         Block *b;
186         uchar *p;
187
188         b = allocb(WIFIHDRSIZE + 128);
189         w = (Wifipkt*)b->wp;
190         w->fc[0] = 0x00;        /* assoc request */
191         w->fc[1] = 0x00;        /* STA->STA */
192         memmove(w->a1, bss->bssid, Eaddrlen);   /* ??? */
193         memmove(w->a2, wifi->ether->ea, Eaddrlen);
194         memmove(w->a3, bss->bssid, Eaddrlen);
195         b->wp += WIFIHDRSIZE;
196         p = b->wp;
197         *p++ = 1;       /* capinfo */
198         *p++ = 0;
199         *p++ = 16;      /* interval */
200         *p++ = 16>>8;
201         *p++ = 0;       /* SSID */
202         *p = strlen(bss->ssid);
203         memmove(p+1, bss->ssid, *p);
204         p += 1+*p;
205         *p++ = 1;       /* RATES (BUG: these are all lies!) */
206         *p++ = 4;
207         *p++ = 0x82;
208         *p++ = 0x84;
209         *p++ = 0x8b;
210         *p++ = 0x96;
211         b->wp = p;
212         wifitx(wifi, bss, b);
213 }
214
215 static void
216 recvassoc(Wifi *wifi, Wnode *wn, uchar *d, int len)
217 {
218         uint s;
219
220         if(len < 2+2+2)
221                 return;
222
223         d += 2; /* caps */
224         s = d[0] | d[1]<<8;
225         d += 2;
226         switch(s){
227         case 0x00:
228                 wn->aid = d[0] | d[1]<<8;
229                 wifi->status = Sassoc;
230                 break;
231         default:
232                 wn->aid = 0;
233                 wifi->status = Sunassoc;
234                 return;
235         }
236 }
237
238 static void
239 recvbeacon(Wifi *, Wnode *wn, uchar *d, int len)
240 {
241         uchar *e, *x;
242         uchar t, m[256/8];
243
244         if(len < 8+2+2)
245                 return;
246
247         d += 8; /* timestamp */
248         wn->ival = d[0] | d[1]<<8;
249         d += 2;
250         wn->cap = d[0] | d[1]<<8;
251         d += 2;
252
253         memset(m, 0, sizeof(m));
254         for(e = d + len; d+2 <= e; d = x){
255                 d += 2;
256                 x = d + d[-1];
257                 t = d[-2];
258
259                 /* skip double entries */
260                 if(m[t/8] & 1<<(t%8))
261                         continue;
262                 m[t/8] |= 1<<(t%8);
263
264                 switch(t){
265                 case 0: /* SSID */
266                         len = 0;
267                         while(len < 32 && d+len < x && d[len] != 0)
268                                 len++;
269                         if(len == 0)
270                                 continue;
271                         if(len != strlen(wn->ssid) || strncmp(wn->ssid, (char*)d, len) != 0){
272                                 strncpy(wn->ssid, (char*)d, len);
273                                 wn->ssid[len] = 0;
274                         }
275                         break;
276                 case 3: /* DSPARAMS */
277                         if(d != x)
278                                 wn->channel = d[0];
279                         break;
280                 }
281         }
282 }
283
284 static void
285 wifiproc(void *arg)
286 {
287         Wifi *wifi;
288         Wifipkt *w;
289         Wnode *wn;
290         Block *b;
291
292         b = nil;
293         wifi = arg;
294         for(;;){
295                 if(b != nil)
296                         freeb(b);
297                 if((b = qbread(wifi->iq, 100000)) == nil)
298                         break;
299                 w = (Wifipkt*)b->rp;
300                 switch(w->fc[0] & 0xf0){
301                 case 0x50:      /* probe response */
302                 case 0x80:      /* beacon */
303                         if((wn = nodelookup(wifi, w->a3, 1)) == nil)
304                                 continue;
305                         b->rp += WIFIHDRSIZE;
306                         recvbeacon(wifi, wn, b->rp, BLEN(b));
307                         if(wifi->bss == nil && wifi->essid[0] != 0 && strcmp(wifi->essid, wn->ssid) == 0){
308                                 wifi->bss = wn;
309                                 wifi->status = Sconn;
310                                 sendauth(wifi, wn);
311                         }
312                         continue;
313                 }
314                 if(memcmp(w->a1, wifi->ether->ea, Eaddrlen))
315                         continue;
316                 if((wn = nodelookup(wifi, w->a3, 0)) == nil)
317                         continue;
318                 if(wn != wifi->bss)
319                         continue;
320                 switch(w->fc[0] & 0xf0){
321                 case 0x10:      /* assoc response */
322                 case 0x30:      /* reassoc response */
323                         b->rp += WIFIHDRSIZE;
324                         recvassoc(wifi, wn, b->rp, BLEN(b));
325                         break;
326                 case 0xb0:      /* auth */
327                         wifi->status = Sauth;
328                         sendassoc(wifi, wn);
329                         break;
330                 case 0xc0:      /* deauth */
331                         wn->aid = 0;
332                         wifi->status = Sunauth;
333                         sendauth(wifi, wn);
334                         break;
335                 }
336         }
337         pexit("wifi in queue closed", 0);
338 }
339
340 static void
341 wifietheroq(Wifi *wifi, Block *b)
342 {
343         Etherpkt e;
344         Wifipkt *w;
345         Wnode *bss;
346         SNAP *s;
347
348         bss = wifi->bss;
349         if(bss == nil || BLEN(b) < ETHERHDRSIZE){
350                 freeb(b);
351                 return;
352         }
353         memmove(&e, b->rp, ETHERHDRSIZE);
354
355         b->rp += ETHERHDRSIZE;
356         b = padblock(b, WIFIHDRSIZE + SNAPHDRSIZE);
357
358         w = (Wifipkt*)b->rp;
359         w->fc[0] = 0x08;        /* data */
360         w->fc[1] = 0x01;        /* STA->AP */
361         memmove(w->a1, bss->bssid, Eaddrlen);
362         memmove(w->a2, e.s, Eaddrlen);
363         memmove(w->a3, e.d, Eaddrlen);
364
365         s = (SNAP*)(b->rp + WIFIHDRSIZE);
366         s->dsap = s->ssap = 0xAA;
367         s->control = 0x03;
368         s->orgcode[0] = 0;
369         s->orgcode[1] = 0;
370         s->orgcode[2] = 0;
371         memmove(s->type, e.type, 2);
372
373         wifitx(wifi, bss, b);
374 }
375
376 static void
377 wifoproc(void *arg)
378 {
379         Ether *ether;
380         Wifi *wifi;
381         Block *b;
382
383         wifi = arg;
384         ether = wifi->ether;
385         while((b = qbread(ether->oq, 1000000)) != nil)
386                 wifietheroq(wifi, b);
387         pexit("ether out queue closed", 0);
388 }
389
390 Wifi*
391 wifiattach(Ether *ether, void (*transmit)(Wifi*, Wnode*, Block*))
392 {
393         char name[32];
394         Wifi *wifi;
395
396         wifi = malloc(sizeof(Wifi));
397         wifi->ether = ether;
398         wifi->iq = qopen(8*1024, 0, 0, 0);
399         wifi->transmit = transmit;
400         wifi->status = Snone;
401
402         snprint(name, sizeof(name), "#l%dwifi", ether->ctlrno);
403         kproc(name, wifiproc, wifi);
404         snprint(name, sizeof(name), "#l%dwifo", ether->ctlrno);
405         kproc(name, wifoproc, wifi);
406
407         return wifi;
408 }
409
410 long
411 wifictl(Wifi *wifi, void *buf, long n)
412 {
413         Cmdbuf *cb;
414         Wnode *wn;
415
416         cb = nil;
417         if(waserror()){
418                 free(cb);
419                 nexterror();
420         }
421         cb = parsecmd(buf, n);
422         if(cb->f[0] && strcmp(cb->f[0], "essid") == 0){
423                 if(cb->f[1] == nil){
424                         wifi->essid[0] = 0;
425                         wifi->bss = nil;
426                         wifi->status = Snone;
427                 } else {
428                         strncpy(wifi->essid, cb->f[1], 32);
429                         wifi->essid[32] = 0;
430                         for(wn = wifi->node; wn != &wifi->node[nelem(wifi->node)]; wn++)
431                                 if(strcmp(wifi->essid, wn->ssid) == 0){
432                                         wifi->bss = wn;
433                                         wifi->status = Sconn;
434                                         sendauth(wifi, wn);
435                                         break;
436                                 }
437                 }
438         }
439         poperror();
440         free(cb);
441         return n;
442 }
443
444 long
445 wifistat(Wifi *wifi, void *buf, long n, ulong off)
446 {
447         static uchar zeros[Eaddrlen];
448         char *s, *p, *e;
449         Wnode *wn;
450         long now;
451
452         p = s = smalloc(4096);
453         e = s + 4096;
454
455         p = seprint(p, e, "status: %s\n", wifi->status);
456         p = seprint(p, e, "essid: %s\n", wifi->essid);
457         wn = wifi->bss;
458         p = seprint(p, e, "bssid: %E\n", wn != nil ? wn->bssid : zeros);
459
460         now = MACHP(0)->ticks;
461         for(wn = wifi->node; wn != &wifi->node[nelem(wifi->node)]; wn++){
462                 if(wn->lastseen == 0)
463                         continue;
464                 p = seprint(p, e, "node: %E %.4x %d %ld %d %s\n",
465                         wn->bssid, wn->cap, wn->ival, TK2MS(now - wn->lastseen), wn->channel, wn->ssid);
466         }
467         n = readstr(off, buf, n, s);
468         free(s);
469         return n;
470 }