ref: d1cebf608b05e45b148108ac6c16ae94f6862097
dir: /sys/src/9/port/wifi.c/
#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "ureg.h" #include "../port/error.h" #include "../port/netif.h" #include "../port/etherif.h" #include "../port/wifi.h" #include <libsec.h> typedef struct SNAP SNAP; struct SNAP { uchar dsap; uchar ssap; uchar control; uchar orgcode[3]; uchar type[2]; }; enum { WIFIHDRSIZE = 2+2+3*6+2, SNAPHDRSIZE = 8, }; static char Sconn[] = "connecting"; static char Sauth[] = "authenticated"; static char Sneedauth[] = "need authentication"; static char Sunauth[] = "unauthenticated"; static char Sassoc[] = "associated"; static char Sunassoc[] = "unassociated"; static char Sblocked[] = "blocked"; /* no keys negotiated. only pass EAPOL frames */ static uchar basicrates[] = { 0x80 | 2, /* 1.0 Mb/s */ 0x80 | 4, /* 2.0 Mb/s */ 0x80 | 11, /* 5.5 Mb/s */ 0x80 | 22, /* 11.0 Mb/s */ 0 }; static Block* wifidecrypt(Wifi *, Wnode *, Block *); static Block* wifiencrypt(Wifi *, Wnode *, Block *); static void freewifikeys(Wifi *, Wnode *); static void dmatproxy(Block *bp, int upstream, uchar proxy[Eaddrlen], DMAT *t); static uchar* srcaddr(Wifipkt *w) { if((w->fc[1] & 0x02) == 0) return w->a2; if((w->fc[1] & 0x01) == 0) return w->a3; return w->a4; } static uchar* dstaddr(Wifipkt *w) { if((w->fc[1] & 0x01) != 0) return w->a3; return w->a1; } int wifihdrlen(Wifipkt *w) { int n; n = WIFIHDRSIZE; if((w->fc[0] & 0x0c) == 0x08) if((w->fc[0] & 0xf0) == 0x80){ /* QOS */ n += 2; if(w->fc[1] & 0x80) n += 4; } if((w->fc[1] & 3) == 0x03) n += Eaddrlen; return n; } static uvlong getts(uchar *d) { return (uvlong)d[0] | (uvlong)d[1]<<8 | (uvlong)d[2]<<16 | (uvlong)d[3]<<24 | (uvlong)d[4]<<32 | (uvlong)d[5]<<40 | (uvlong)d[6]<<48 | (uvlong)d[7]<<56; } void wifiiq(Wifi *wifi, Block *b) { SNAP s; Wifipkt h, *w; Etherpkt *e; int hdrlen; if(b->flag & Btimestamp) assert(b->rp - b->base >= 8); if(BLEN(b) < WIFIHDRSIZE) goto drop; w = (Wifipkt*)b->rp; hdrlen = wifihdrlen(w); if(BLEN(b) < hdrlen) goto drop; if(memcmp(srcaddr(w), wifi->ether->ea, Eaddrlen) == 0) goto drop; if(w->fc[1] & 0x40){ /* encrypted */ qpass(wifi->iq, b); return; } switch(w->fc[0] & 0x0c){ case 0x00: /* management */ if((w->fc[1] & 3) != 0x00) /* STA->STA */ break; qpass(wifi->iq, b); return; case 0x04: /* control */ break; case 0x08: /* data */ b->flag &= ~Btimestamp; b->rp += hdrlen; switch(w->fc[0] & 0xf0){ default: goto drop; case 0x80: /* QOS */ case 0x00: break; } if(BLEN(b) < SNAPHDRSIZE) break; memmove(&s, b->rp, SNAPHDRSIZE); if(s.dsap != 0xAA || s.ssap != 0xAA || s.control != 3) break; if(s.orgcode[0] != 0 || s.orgcode[1] != 0 || s.orgcode[2] != 0) break; b->rp += SNAPHDRSIZE-ETHERHDRSIZE; h = *w; e = (Etherpkt*)b->rp; memmove(e->d, dstaddr(&h), Eaddrlen); memmove(e->s, srcaddr(&h), Eaddrlen); memmove(e->type, s.type, 2); dmatproxy(b, 0, wifi->ether->ea, &wifi->dmat); etheriq(wifi->ether, b); return; } drop: freeb(b); } static void wifitx(Wifi *wifi, Wnode *wn, Block *b) { Wifipkt *w; uint seq; wn->lastsend = MACHP(0)->ticks; seq = incref(&wifi->txseq); seq <<= 4; w = (Wifipkt*)b->rp; w->dur[0] = 0; w->dur[1] = 0; w->seq[0] = seq; w->seq[1] = seq>>8; if((w->fc[0] & 0x0c) != 0x00){ b = wifiencrypt(wifi, wn, b); if(b == nil) return; } if((wn->txcount++ & 255) == 255 && wn->actrate != nil && wn->actrate != wn->maxrate){ uchar *a, *p; for(a = wn->maxrate, p = wifi->rates; *p; p++){ if(*p < *a && *p > *wn->actrate && (wn->validrates & (1UL << p-wifi->rates)) != 0) a = p; } wn->actrate = a; } (*wifi->transmit)(wifi, wn, b); } static Wnode* nodelookup(Wifi *wifi, uchar *bssid, int new) { Wnode *wn, *nn; if(memcmp(bssid, wifi->ether->bcast, Eaddrlen) == 0) return nil; if((wn = wifi->bss) != nil){ if(memcmp(wn->bssid, bssid, Eaddrlen) == 0) return wn; } if((nn = wifi->node) == wn) nn++; for(wn = wifi->node; wn != &wifi->node[nelem(wifi->node)]; wn++){ if(wn == wifi->bss) continue; if(memcmp(wn->bssid, bssid, Eaddrlen) == 0) return wn; if((long)(wn->lastsend - nn->lastsend) < 0 || (long)(wn->lastseen - nn->lastseen) < 0) nn = wn; } if(!new) return nil; freewifikeys(wifi, nn); memset(nn, 0, sizeof(Wnode)); memmove(nn->bssid, bssid, Eaddrlen); return nn; } void wifitxfail(Wifi *wifi, Block *b) { Wifipkt *w; Wnode *wn; if(b == nil) return; w = (Wifipkt*)b->rp; wn = nodelookup(wifi, w->a1, 0); if(wn == nil) return; wn->txerror++; if(wn->actrate != nil && wn->minrate != wn->actrate){ uchar *a, *p; for(a = wn->minrate, p = wifi->rates; *p; p++){ if(*p > *a && *p < *wn->actrate && (wn->validrates & (1UL << p-wifi->rates)) != 0) a = p; } wn->actrate = a; } } static uchar* putrates(uchar *p, uchar *rates, ulong valid, ulong basic) { int n, i, j; valid |= basic; for(i = n = 0; i < 32 && rates[i] != 0; i++) if(valid & (1UL<<i)) n++; valid &= ~basic; if(n > 0){ /* supported rates */ *p++ = 1; *p++ = n; for(i = j = 0; j < n; i++){ if(basic & (1UL<<i)){ *p++ = rates[i] | 0x80; j++; } } for(i = 0; j < n; i++){ if(valid & (1UL<<i)){ *p++ = rates[i] & 0x7f; j++; } } } if(n > 8){ /* truncate supported rates element */ p -= n; p[-1] = 8; p += 8; /* extended supported rates */ *p++ = 50; *p++ = n; for(i = j = 0; j < n; i++){ if(basic & (1UL<<i)){ *p++ = rates[i] | 0x80; j++; } } for(i = 0; j < n; i++){ if(valid & (1UL<<i)){ *p++ = rates[i] & 0x7f; j++; } } } return p; } static void wifiprobe(Wifi *wifi, Wnode *wn) { Wifipkt *w; Block *b; uchar *p; int n; n = strlen(wifi->essid); if(n == 0){ /* no specific essid, just tell driver to tune channel */ (*wifi->transmit)(wifi, wn, nil); return; } b = allocb(WIFIHDRSIZE + 512); w = (Wifipkt*)b->wp; w->fc[0] = 0x40; /* probe request */ w->fc[1] = 0x00; /* STA->STA */ memmove(w->a1, wifi->ether->bcast, Eaddrlen); /* ??? */ memmove(w->a2, wifi->ether->ea, Eaddrlen); memmove(w->a3, wifi->ether->bcast, Eaddrlen); b->wp += WIFIHDRSIZE; p = b->wp; *p++ = 0; /* set */ *p++ = n; memmove(p, wifi->essid, n); p += n; p = putrates(p, wifi->rates, wn->validrates, wn->basicrates); *p++ = 3; /* ds parameter set */ *p++ = 1; *p++ = wn->channel; b->wp = p; wifitx(wifi, wn, b); } static void sendauth(Wifi *wifi, Wnode *bss) { Wifipkt *w; Block *b; uchar *p; b = allocb(WIFIHDRSIZE + 3*2); w = (Wifipkt*)b->wp; w->fc[0] = 0xB0; /* auth request */ w->fc[1] = 0x00; /* STA->STA */ memmove(w->a1, bss->bssid, Eaddrlen); /* ??? */ memmove(w->a2, wifi->ether->ea, Eaddrlen); memmove(w->a3, bss->bssid, Eaddrlen); b->wp += WIFIHDRSIZE; p = b->wp; *p++ = 0; /* alg */ *p++ = 0; *p++ = 1; /* seq */ *p++ = 0; *p++ = 0; /* status */ *p++ = 0; b->wp = p; bss->aid = 0; wifitx(wifi, bss, b); } static void sendassoc(Wifi *wifi, Wnode *bss) { Wifipkt *w; Block *b; uchar *p; int cap, n; b = allocb(WIFIHDRSIZE + 512); w = (Wifipkt*)b->wp; w->fc[0] = 0x00; /* assoc request */ w->fc[1] = 0x00; /* STA->STA */ memmove(w->a1, bss->bssid, Eaddrlen); /* ??? */ memmove(w->a2, wifi->ether->ea, Eaddrlen); memmove(w->a3, bss->bssid, Eaddrlen); b->wp += WIFIHDRSIZE; p = b->wp; /* capinfo */ cap = 1; // ESS cap |= (1<<5); // Short Preamble cap |= (1<<10) & bss->cap; // Short Slot Time *p++ = cap; *p++ = cap>>8; /* interval */ *p++ = 16; *p++ = 16>>8; n = strlen(bss->ssid); *p++ = 0; /* SSID */ *p++ = n; memmove(p, bss->ssid, n); p += n; p = putrates(p, wifi->rates, bss->validrates, bss->basicrates); n = bss->rsnelen; if(n > 0){ memmove(p, bss->rsne, n); p += n; } b->wp = p; wifitx(wifi, bss, b); } static void setstatus(Wifi *wifi, Wnode *wn, char *new) { char *old; old = wn->status; wn->status = new; if(wifi->debug && new != old) print("#l%d: status %E: %.12ld %.12ld: %s -> %s (from pc=%#p)\n", wifi->ether->ctlrno, wn->bssid, TK2MS(MACHP(0)->ticks), TK2MS(MACHP(0)->ticks - wn->lastsend), old, new, getcallerpc(&wifi)); } static void recvassoc(Wifi *wifi, Wnode *wn, uchar *d, int len) { uint s; if(len < 2+2+2) return; d += 2; /* caps */ s = d[0] | d[1]<<8; d += 2; switch(s){ case 0x00: wn->aid = d[0] | d[1]<<8; if(wn->rsnelen > 0) setstatus(wifi, wn, Sblocked); else setstatus(wifi, wn, Sassoc); break; default: wn->aid = 0; setstatus(wifi, wn, Sunassoc); } } static void recvbeacon(Wifi *wifi, Wnode *wn, uchar *d, int len) { static uchar wpa1oui[4] = { 0x00, 0x50, 0xf2, 0x01 }; uchar *e, *x, *p, t; int rsnset; len -= 8+2+2; if(len < 0) return; /* timestamp */ wn->ts = getts(d); d += 8; wn->ival = d[0] | d[1]<<8; d += 2; wn->cap = d[0] | d[1]<<8; d += 2; wn->dtimcount = 0; wn->dtimperiod = 1; rsnset = 0; for(e = d + len; d+2 <= e; d = x){ d += 2; x = d + d[-1]; if(x > e) break; /* truncated */ t = d[-2]; switch(t){ case 0: /* SSID */ len = 0; while(len < Essidlen && d+len < x && d[len] != 0) len++; if(len == 0) continue; if(len != strlen(wn->ssid) || strncmp(wn->ssid, (char*)d, len) != 0){ strncpy(wn->ssid, (char*)d, len); wn->ssid[len] = 0; } break; case 1: /* supported rates */ case 50: /* extended rates */ if(wifi->rates == nil) break; while(d < x){ t = *d | 0x80; for(p = wifi->rates; *p != 0; p++){ if(*p == t){ wn->validrates |= 1UL << p-wifi->rates; if(*d & 0x80) wn->basicrates |= 1UL << p-wifi->rates; if(wn->minrate == nil || t < *wn->minrate) wn->minrate = p; if(wn->maxrate == nil || t > *wn->maxrate) wn->maxrate = p; break; } } d++; } if(wn->actrate == nil) wn->actrate = wn->maxrate; break; case 3: /* DSPARAMS */ if(d != x) wn->channel = d[0]; break; case 5: if(x - d < 2) break; wn->dtimcount = d[0]; if(d[1] > 0) wn->dtimperiod = d[1]; break; case 221: /* vendor specific */ len = x - d; if(rsnset || len < sizeof(wpa1oui) || memcmp(d, wpa1oui, sizeof(wpa1oui)) != 0) break; /* no break */ case 48: /* RSN information */ len = x - &d[-2]; memmove(wn->brsne, &d[-2], len); wn->brsnelen = len; rsnset = 1; break; } } } static void freewifikeys(Wifi *wifi, Wnode *wn) { int i; wlock(&wifi->crypt); for(i=0; i<nelem(wn->rxkey); i++){ secfree(wn->rxkey[i]); wn->rxkey[i] = nil; } for(i=0; i<nelem(wn->txkey); i++){ secfree(wn->txkey[i]); wn->txkey[i] = nil; } wunlock(&wifi->crypt); } static void wifideauth(Wifi *wifi, Wnode *wn) { Ether *ether; Netfile *f; int i; /* deassociate node, clear keys */ setstatus(wifi, wn, Sunauth); freewifikeys(wifi, wn); memset(&wifi->dmat, 0, sizeof(wifi->dmat)); wn->aid = 0; if(wn == wifi->bss){ /* notify driver about node aid association */ (*wifi->transmit)(wifi, wn, nil); /* notify aux/wpa with a zero length packet that we got deassociated from the ap */ ether = wifi->ether; for(i=0; i<ether->nfile; i++){ f = ether->f[i]; if(f == nil || f->in == nil || f->inuse == 0 || f->type != 0x888e) continue; qflush(f->in); qwrite(f->in, 0, 0); } qflush(ether->oq); } } /* check if a node qualifies as our bss matching bssid and essid */ static int goodbss(Wifi *wifi, Wnode *wn) { if(memcmp(wifi->bssid, wifi->ether->bcast, Eaddrlen) != 0){ if(memcmp(wifi->bssid, wn->bssid, Eaddrlen) != 0) return 0; /* bssid doesnt match */ } else if(wifi->essid[0] == 0) return 0; /* both bssid and essid unspecified */ if(wifi->essid[0] != 0 && strcmp(wifi->essid, wn->ssid) != 0) return 0; /* essid doesnt match */ return 1; } static void wifiproc(void *arg) { Wifi *wifi; Wifipkt *w; Wnode *wn; Block *b; b = nil; wifi = arg; while(waserror()) ; for(;;){ if(b != nil){ freeb(b); b = nil; continue; } if((b = qbread(wifi->iq, 100000)) == nil) break; w = (Wifipkt*)b->rp; if(w->fc[1] & 0x40){ /* encrypted */ if((wn = nodelookup(wifi, w->a2, 0)) == nil) continue; wn->lastseen = MACHP(0)->ticks; if((b = wifidecrypt(wifi, wn, b)) != nil){ w = (Wifipkt*)b->rp; if(w->fc[1] & 0x40) continue; b->flag &= ~Btimestamp; wifiiq(wifi, b); b = nil; } continue; } /* management */ if((w->fc[0] & 0x0c) != 0x00) continue; switch(w->fc[0] & 0xf0){ case 0x50: /* probe response */ if(wifi->debug) print("#l%d: got probe from %E\n", wifi->ether->ctlrno, w->a3); /* no break */ case 0x80: /* beacon */ if((wn = nodelookup(wifi, w->a3, 1)) == nil) continue; wn->lastseen = MACHP(0)->ticks; if(b->flag & Btimestamp) wn->rs = getts(b->rp - 8); b->rp += wifihdrlen(w); recvbeacon(wifi, wn, b->rp, BLEN(b)); if(wifi->bss == nil && TK2MS(MACHP(0)->ticks - wn->lastsend) > 1000 && goodbss(wifi, wn)){ setstatus(wifi, wn, Sconn); sendauth(wifi, wn); wifi->lastauth = wn->lastsend; } continue; } if(memcmp(w->a1, wifi->ether->ea, Eaddrlen)) continue; if((wn = nodelookup(wifi, w->a3, 0)) == nil) continue; wn->lastseen = MACHP(0)->ticks; if(b->flag & Btimestamp) wn->rs = getts(b->rp - 8); switch(w->fc[0] & 0xf0){ case 0x10: /* assoc response */ case 0x30: /* reassoc response */ b->rp += wifihdrlen(w); recvassoc(wifi, wn, b->rp, BLEN(b)); /* notify driver about node aid association */ if(wn == wifi->bss) (*wifi->transmit)(wifi, wn, nil); break; case 0xb0: /* auth */ if(wifi->debug) print("#l%d: got auth from %E\n", wifi->ether->ctlrno, wn->bssid); if(wn->brsnelen > 0 && wn->rsnelen == 0) setstatus(wifi, wn, Sneedauth); else setstatus(wifi, wn, Sauth); if(wifi->bss == nil && goodbss(wifi, wn)){ wifi->bss = wn; if(wn->status == Sauth) sendassoc(wifi, wn); } break; case 0xc0: /* deauth */ if(wifi->debug) print("#l%d: got deauth from %E\n", wifi->ether->ctlrno, wn->bssid); wifideauth(wifi, wn); break; } } pexit("wifi in queue closed", 1); } static void wifietheroq(Wifi *wifi, Block *b) { Etherpkt e; Wifipkt h; int hdrlen; Wnode *wn; SNAP *s; if(BLEN(b) < ETHERHDRSIZE) goto drop; if((wn = wifi->bss) == nil) goto drop; dmatproxy(b, 1, wifi->ether->ea, &wifi->dmat); memmove(&e, b->rp, ETHERHDRSIZE); b->rp += ETHERHDRSIZE; if(wn->status == Sblocked){ /* only pass EAPOL frames when port is blocked */ if((e.type[0]<<8 | e.type[1]) != 0x888e) goto drop; } else if(wn->status != Sassoc) goto drop; h.fc[0] = 0x08; /* data */ memmove(h.a1, wn->bssid, Eaddrlen); if(memcmp(e.s, wifi->ether->ea, Eaddrlen) == 0) { h.fc[1] = 0x01; /* STA->AP */ } else { h.fc[1] = 0x03; /* AP->AP (WDS) */ memmove(h.a2, wifi->ether->ea, Eaddrlen); } memmove(dstaddr(&h), e.d, Eaddrlen); memmove(srcaddr(&h), e.s, Eaddrlen); hdrlen = wifihdrlen(&h); b = padblock(b, hdrlen + SNAPHDRSIZE); memmove(b->rp, &h, hdrlen); s = (SNAP*)(b->rp + hdrlen); s->dsap = s->ssap = 0xAA; s->control = 0x03; s->orgcode[0] = 0; s->orgcode[1] = 0; s->orgcode[2] = 0; memmove(s->type, e.type, 2); wifitx(wifi, wn, b); return; drop: freeb(b); } static void wifoproc(void *arg) { Ether *ether; Wifi *wifi; Block *b; wifi = arg; ether = wifi->ether; while(waserror()) ; while((b = qbread(ether->oq, 1000000)) != nil) wifietheroq(wifi, b); pexit("ether out queue closed", 1); } static void wifsproc(void *arg) { Ether *ether; Wifi *wifi; Wnode wnscan; Wnode *wn; ulong now, tmout; uchar *rate; wifi = arg; ether = wifi->ether; wn = &wnscan; memset(wn, 0, sizeof(*wn)); memmove(wn->bssid, ether->bcast, Eaddrlen); while(waserror()) ; Scan: /* scan for access point */ while(wifi->bss == nil){ ether->link = 0; wnscan.channel = 1 + ((wnscan.channel+4) % 13); wifiprobe(wifi, &wnscan); do { tsleep(&up->sleep, return0, 0, 200); now = MACHP(0)->ticks; } while(TK2MS(now-wifi->lastauth) < 1000); } /* maintain access point */ tmout = 0; while((wn = wifi->bss) != nil){ ether->link = (wn->status == Sassoc) || (wn->status == Sblocked); if(ether->link && (rate = wn->actrate) != nil) ether->mbps = ((*rate & 0x7f)+3)/4; now = MACHP(0)->ticks; if(wn->status != Sneedauth && TK2SEC(now - wn->lastseen) > 20 || goodbss(wifi, wn) == 0){ wifideauth(wifi, wn); wifi->bss = nil; break; } if(TK2MS(now - wn->lastsend) > 1000){ if((wn->status == Sauth || wn->status == Sblocked) && (++tmout & 7) == 0) wifideauth(wifi, wn); /* stuck in auth, start over */ if(wn->status == Sconn || wn->status == Sunauth) sendauth(wifi, wn); if(wn->status == Sauth){ sendassoc(wifi, wn); } } tsleep(&up->sleep, return0, 0, 500); } goto Scan; } Wifi* wifiattach(Ether *ether, void (*transmit)(Wifi*, Wnode*, Block*)) { char name[32]; Wifi *wifi; wifi = malloc(sizeof(Wifi)); if(wifi == nil) error(Enomem); wifi->iq = qopen(ether->limit, 0, 0, 0); if(wifi->iq == nil){ free(wifi); error(Enomem); } wifi->ether = ether; wifi->transmit = transmit; wifi->rates = basicrates; wifi->essid[0] = 0; memmove(wifi->bssid, ether->bcast, Eaddrlen); wifi->lastauth = MACHP(0)->ticks; snprint(name, sizeof(name), "#l%dwifi", ether->ctlrno); kproc(name, wifiproc, wifi); snprint(name, sizeof(name), "#l%dwifo", ether->ctlrno); kproc(name, wifoproc, wifi); snprint(name, sizeof(name), "#l%dwifs", ether->ctlrno); kproc(name, wifsproc, wifi); return wifi; } static char *ciphers[] = { [0] "clear", [TKIP] "tkip", [CCMP] "ccmp", }; static Wkey* parsekey(char *s) { static char Ebadkey[] = "bad key"; uchar key[32]; int len, cipher; char *e; Wkey *k; for(cipher=0; cipher<nelem(ciphers); cipher++){ if(strncmp(s, ciphers[cipher], len = strlen(ciphers[cipher])) == 0){ if(cipher == 0) /* clear */ return nil; if(s[len] == ':'){ s += len+1; break; } } } if(cipher >= nelem(ciphers)) error(Ebadkey); if((e = strchr(s, '@')) == nil) e = strchr(s, 0); len = dec16(key, sizeof(key), s, e - s); switch(cipher){ case TKIP: if(len != 32) error(Ebadkey); k = secalloc(sizeof(Wkey) + len); memmove(k->key, key, len); break; case CCMP: if(len != 16) error(Ebadkey); k = secalloc(sizeof(Wkey) + sizeof(AESstate)); setupAESstate((AESstate*)k->key, key, len, nil); break; default: error(Ebadkey); return nil; } memset(key, 0, sizeof(key)); if(*e++ == '@') k->tsc = strtoull(e, nil, 16); k->len = len; k->cipher = cipher; return k; } void wificfg(Wifi *wifi, char *opt) { char *p, buf[64]; int n; if(strncmp(opt, "debug=", 6)) if(strncmp(opt, "essid=", 6)) if(strncmp(opt, "bssid=", 6)) return; if((p = strchr(opt, '=')) == nil) return; if(waserror()) return; n = snprint(buf, sizeof(buf), "%.*s %q", utfnlen(opt, p - opt), opt, p+1); wifictl(wifi, buf, n); poperror(); } enum { CMdebug, CMessid, CMauth, CMbssid, CMrxkey0, CMrxkey1, CMrxkey2, CMrxkey3, CMrxkey4, CMtxkey0, }; static Cmdtab wifictlmsg[] = { CMdebug, "debug", 0, CMessid, "essid", 0, CMauth, "auth", 0, CMbssid, "bssid", 0, CMrxkey0, "rxkey0", 0, /* group keys */ CMrxkey1, "rxkey1", 0, CMrxkey2, "rxkey2", 0, CMrxkey3, "rxkey3", 0, CMrxkey4, "rxkey", 0, /* peerwise keys */ CMtxkey0, "txkey", 0, CMtxkey0, "txkey0", 0, }; long wifictl(Wifi *wifi, void *buf, long n) { uchar addr[Eaddrlen]; Cmdbuf *cb; Cmdtab *ct; Wnode *wn; Wkey *k, **kk; cb = nil; if(waserror()){ free(cb); nexterror(); } if(wifi->debug) print("#l%d: wifictl: %.*s\n", wifi->ether->ctlrno, utfnlen(buf, n), buf); memmove(addr, wifi->ether->bcast, Eaddrlen); wn = wifi->bss; cb = parsecmd(buf, n); ct = lookupcmd(cb, wifictlmsg, nelem(wifictlmsg)); if(ct->index >= CMauth){ if(cb->nf > 1 && (ct->index == CMbssid || ct->index >= CMrxkey0)){ if(parseether(addr, cb->f[1]) == 0){ cb->f++; cb->nf--; wn = nodelookup(wifi, addr, 0); } } if(wn == nil && ct->index != CMbssid) error("missing node"); } switch(ct->index){ case CMdebug: if(cb->f[1] != nil) wifi->debug = atoi(cb->f[1]); else wifi->debug ^= 1; print("#l%d: debug: %d\n", wifi->ether->ctlrno, wifi->debug); break; case CMessid: if(cb->f[1] != nil) strncpy(wifi->essid, cb->f[1], Essidlen); else wifi->essid[0] = 0; Findbss: wn = wifi->bss; if(wn != nil){ if(goodbss(wifi, wn)) break; wifideauth(wifi, wn); } wifi->bss = nil; if(wifi->essid[0] == 0 && memcmp(wifi->bssid, wifi->ether->bcast, Eaddrlen) == 0) break; for(wn = wifi->node; wn != &wifi->node[nelem(wifi->node)]; wn++) if(goodbss(wifi, wn)){ setstatus(wifi, wn, Sconn); sendauth(wifi, wn); } break; case CMbssid: memmove(wifi->bssid, addr, Eaddrlen); goto Findbss; case CMauth: freewifikeys(wifi, wn); if(cb->f[1] == nil) wn->rsnelen = 0; else wn->rsnelen = dec16(wn->rsne, sizeof(wn->rsne), cb->f[1], strlen(cb->f[1])); if(wn->aid == 0){ setstatus(wifi, wn, Sconn); sendauth(wifi, wn); } else { setstatus(wifi, wn, Sauth); sendassoc(wifi, wn); } break; case CMrxkey0: case CMrxkey1: case CMrxkey2: case CMrxkey3: case CMrxkey4: case CMtxkey0: if(cb->f[1] == nil) error(Ebadarg); k = parsekey(cb->f[1]); memset(cb->f[1], 0, strlen(cb->f[1])); if(ct->index < CMtxkey0) kk = &wn->rxkey[ct->index - CMrxkey0]; else kk = &wn->txkey[ct->index - CMtxkey0]; wlock(&wifi->crypt); secfree(*kk); *kk = k; wunlock(&wifi->crypt); if(ct->index >= CMtxkey0 && wn->status == Sblocked) setstatus(wifi, wn, Sassoc); break; } poperror(); free(cb); return n; } long wifistat(Wifi *wifi, void *buf, long n, ulong off) { static uchar zeros[Eaddrlen]; char essid[Essidlen+1]; char *s, *p, *e; Wnode *wn; Wkey *k; long now; int i; p = s = smalloc(4096); e = s + 4096; wn = wifi->bss; if(wn != nil){ strncpy(essid, wn->ssid, Essidlen); essid[Essidlen] = 0; p = seprint(p, e, "essid: %s\n", essid); p = seprint(p, e, "bssid: %E\n", wn->bssid); p = seprint(p, e, "status: %s\n", wn->status); p = seprint(p, e, "channel: %.2d\n", wn->channel); /* only print key ciphers and key length */ rlock(&wifi->crypt); for(i = 0; i<nelem(wn->rxkey); i++){ if((k = wn->rxkey[i]) != nil) p = seprint(p, e, "rxkey%d: %s:[%d]\n", i, ciphers[k->cipher], k->len); } for(i = 0; i<nelem(wn->txkey); i++){ if((k = wn->txkey[i]) != nil) p = seprint(p, e, "txkey%d: %s:[%d]\n", i, ciphers[k->cipher], k->len); } runlock(&wifi->crypt); if(wn->brsnelen > 0){ p = seprint(p, e, "brsne: "); for(i=0; i<wn->brsnelen; i++) p = seprint(p, e, "%.2X", wn->brsne[i]); p = seprint(p, e, "\n"); } } else { p = seprint(p, e, "essid: %s\n", wifi->essid); p = seprint(p, e, "bssid: %E\n", wifi->bssid); } now = MACHP(0)->ticks; for(wn = wifi->node; wn != &wifi->node[nelem(wifi->node)]; wn++){ if(wn->lastseen == 0) continue; strncpy(essid, wn->ssid, Essidlen); essid[Essidlen] = 0; p = seprint(p, e, "node: %E %.4x %-11ld %.2d %s\n", wn->bssid, wn->cap, TK2MS(now - wn->lastseen), wn->channel, essid); } n = readstr(off, buf, n, s); free(s); return n; } static void tkipencrypt(Wkey *k, Wifipkt *w, Block *b, uvlong tsc); static int tkipdecrypt(Wkey *k, Wifipkt *w, Block *b, uvlong tsc); static void ccmpencrypt(Wkey *k, Wifipkt *w, Block *b, uvlong tsc); static int ccmpdecrypt(Wkey *k, Wifipkt *w, Block *b, uvlong tsc); static Block* wifiencrypt(Wifi *wifi, Wnode *wn, Block *b) { uvlong tsc; int n, kid; Wifipkt *w; Wkey *k; rlock(&wifi->crypt); kid = 0; k = wn->txkey[kid]; if(k == nil){ runlock(&wifi->crypt); return b; } n = wifihdrlen((Wifipkt*)b->rp); b = padblock(b, 8); b = padblock(b, -(8+4)); w = (Wifipkt*)b->rp; memmove(w, b->rp+8, n); b->rp += n; tsc = ++k->tsc; switch(k->cipher){ case TKIP: b->rp[0] = tsc>>8; b->rp[1] = (b->rp[0] | 0x20) & 0x7f; b->rp[2] = tsc; b->rp[3] = kid<<6 | 0x20; b->rp[4] = tsc>>16; b->rp[5] = tsc>>24; b->rp[6] = tsc>>32; b->rp[7] = tsc>>40; b->rp += 8; tkipencrypt(k, w, b, tsc); break; case CCMP: b->rp[0] = tsc; b->rp[1] = tsc>>8; b->rp[2] = 0; b->rp[3] = kid<<6 | 0x20; b->rp[4] = tsc>>16; b->rp[5] = tsc>>24; b->rp[6] = tsc>>32; b->rp[7] = tsc>>40; b->rp += 8; ccmpencrypt(k, w, b, tsc); break; } runlock(&wifi->crypt); b->rp = (uchar*)w; w->fc[1] |= 0x40; return b; } static Block* wifidecrypt(Wifi *wifi, Wnode *wn, Block *b) { uvlong tsc; int n, kid; Wifipkt *w; Wkey *k; rlock(&wifi->crypt); w = (Wifipkt*)b->rp; n = wifihdrlen(w); b->rp += n; if(BLEN(b) < 8+8) goto drop; kid = b->rp[3]>>6; if((b->rp[3] & 0x20) == 0) goto drop; if((w->a1[0] & 1) == 0) kid = 4; /* use peerwise key for non-unicast */ k = wn->rxkey[kid]; if(k == nil) goto drop; switch(k->cipher){ case TKIP: tsc = (uvlong)b->rp[7]<<40 | (uvlong)b->rp[6]<<32 | (uvlong)b->rp[5]<<24 | (uvlong)b->rp[4]<<16 | (uvlong)b->rp[0]<<8 | (uvlong)b->rp[2]; b->rp += 8; if(tsc <= k->tsc) goto drop; if(tkipdecrypt(k, w, b, tsc) != 0) goto drop; break; case CCMP: tsc = (uvlong)b->rp[7]<<40 | (uvlong)b->rp[6]<<32 | (uvlong)b->rp[5]<<24 | (uvlong)b->rp[4]<<16 | (uvlong)b->rp[1]<<8 | (uvlong)b->rp[0]; b->rp += 8; if(tsc <= k->tsc) goto drop; if(ccmpdecrypt(k, w, b, tsc) != 0) goto drop; break; default: drop: runlock(&wifi->crypt); freeb(b); return nil; } runlock(&wifi->crypt); k->tsc = tsc; b->rp -= n; memmove(b->rp, w, n); w = (Wifipkt*)b->rp; w->fc[1] &= ~0x40; return b; } static u16int Sbox[256] = { 0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154, 0x6050, 0x0203, 0xCEA9, 0x567D, 0xE719, 0xB562, 0x4DE6, 0xEC9A, 0x8F45, 0x1F9D, 0x8940, 0xFA87, 0xEF15, 0xB2EB, 0x8EC9, 0xFB0B, 0x41EC, 0xB367, 0x5FFD, 0x45EA, 0x23BF, 0x53F7, 0xE496, 0x9B5B, 0x75C2, 0xE11C, 0x3DAE, 0x4C6A, 0x6C5A, 0x7E41, 0xF502, 0x834F, 0x685C, 0x51F4, 0xD134, 0xF908, 0xE293, 0xAB73, 0x6253, 0x2A3F, 0x080C, 0x9552, 0x4665, 0x9D5E, 0x3028, 0x37A1, 0x0A0F, 0x2FB5, 0x0E09, 0x2436, 0x1B9B, 0xDF3D, 0xCD26, 0x4E69, 0x7FCD, 0xEA9F, 0x121B, 0x1D9E, 0x5874, 0x342E, 0x362D, 0xDCB2, 0xB4EE, 0x5BFB, 0xA4F6, 0x764D, 0xB761, 0x7DCE, 0x527B, 0xDD3E, 0x5E71, 0x1397, 0xA6F5, 0xB968, 0x0000, 0xC12C, 0x4060, 0xE31F, 0x79C8, 0xB6ED, 0xD4BE, 0x8D46, 0x67D9, 0x724B, 0x94DE, 0x98D4, 0xB0E8, 0x854A, 0xBB6B, 0xC52A, 0x4FE5, 0xED16, 0x86C5, 0x9AD7, 0x6655, 0x1194, 0x8ACF, 0xE910, 0x0406, 0xFE81, 0xA0F0, 0x7844, 0x25BA, 0x4BE3, 0xA2F3, 0x5DFE, 0x80C0, 0x058A, 0x3FAD, 0x21BC, 0x7048, 0xF104, 0x63DF, 0x77C1, 0xAF75, 0x4263, 0x2030, 0xE51A, 0xFD0E, 0xBF6D, 0x814C, 0x1814, 0x2635, 0xC32F, 0xBEE1, 0x35A2, 0x88CC, 0x2E39, 0x9357, 0x55F2, 0xFC82, 0x7A47, 0xC8AC, 0xBAE7, 0x322B, 0xE695, 0xC0A0, 0x1998, 0x9ED1, 0xA37F, 0x4466, 0x547E, 0x3BAB, 0x0B83, 0x8CCA, 0xC729, 0x6BD3, 0x283C, 0xA779, 0xBCE2, 0x161D, 0xAD76, 0xDB3B, 0x6456, 0x744E, 0x141E, 0x92DB, 0x0C0A, 0x486C, 0xB8E4, 0x9F5D, 0xBD6E, 0x43EF, 0xC4A6, 0x39A8, 0x31A4, 0xD337, 0xF28B, 0xD532, 0x8B43, 0x6E59, 0xDAB7, 0x018C, 0xB164, 0x9CD2, 0x49E0, 0xD8B4, 0xACFA, 0xF307, 0xCF25, 0xCAAF, 0xF48E, 0x47E9, 0x1018, 0x6FD5, 0xF088, 0x4A6F, 0x5C72, 0x3824, 0x57F1, 0x73C7, 0x9751, 0xCB23, 0xA17C, 0xE89C, 0x3E21, 0x96DD, 0x61DC, 0x0D86, 0x0F85, 0xE090, 0x7C42, 0x71C4, 0xCCAA, 0x90D8, 0x0605, 0xF701, 0x1C12, 0xC2A3, 0x6A5F, 0xAEF9, 0x69D0, 0x1791, 0x9958, 0x3A27, 0x27B9, 0xD938, 0xEB13, 0x2BB3, 0x2233, 0xD2BB, 0xA970, 0x0789, 0x33A7, 0x2DB6, 0x3C22, 0x1592, 0xC920, 0x8749, 0xAAFF, 0x5078, 0xA57A, 0x038F, 0x59F8, 0x0980, 0x1A17, 0x65DA, 0xD731, 0x84C6, 0xD0B8, 0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A }; static void tkipk2tk(uchar key[16], u16int tk[8]) { tk[0] = (u16int)key[1]<<8 | key[0]; tk[1] = (u16int)key[3]<<8 | key[2]; tk[2] = (u16int)key[5]<<8 | key[4]; tk[3] = (u16int)key[7]<<8 | key[6]; tk[4] = (u16int)key[9]<<8 | key[8]; tk[5] = (u16int)key[11]<<8 | key[10]; tk[6] = (u16int)key[13]<<8 | key[12]; tk[7] = (u16int)key[15]<<8 | key[14]; } static void tkipphase1(u32int tscu, uchar ta[Eaddrlen], u16int tk[8], u16int p1k[5]) { u16int *k, i, x0, x1, x2; p1k[0] = tscu; p1k[1] = tscu>>16; p1k[2] = (u16int)ta[1]<<8 | ta[0]; p1k[3] = (u16int)ta[3]<<8 | ta[2]; p1k[4] = (u16int)ta[5]<<8 | ta[4]; for(i=0; i<8; i++){ k = &tk[i & 1]; x0 = p1k[4] ^ k[0]; x1 = Sbox[x0 >> 8]; x2 = Sbox[x0 & 0xFF] ^ ((x1>>8) | (x1<<8)); p1k[0] += x2; x0 = p1k[0] ^ k[2]; x1 = Sbox[x0 >> 8]; x2 = Sbox[x0 & 0xFF] ^ ((x1>>8) | (x1<<8)); p1k[1] += x2; x0 = p1k[1] ^ k[4]; x1 = Sbox[x0 >> 8]; x2 = Sbox[x0 & 0xFF] ^ ((x1>>8) | (x1<<8)); p1k[2] += x2; x0 = p1k[2] ^ k[6]; x1 = Sbox[x0 >> 8]; x2 = Sbox[x0 & 0xFF] ^ ((x1>>8) | (x1<<8)); p1k[3] += x2; x0 = p1k[3] ^ k[0]; x1 = Sbox[x0 >> 8]; x2 = Sbox[x0 & 0xFF] ^ ((x1>>8) | (x1<<8)); p1k[4] += x2; p1k[4] += i; } } static void tkipphase2(u16int tscl, u16int p1k[5], u16int tk[8], uchar rc4key[16]) { u16int ppk[6], x0, x1, x2; ppk[0] = p1k[0]; ppk[1] = p1k[1]; ppk[2] = p1k[2]; ppk[3] = p1k[3]; ppk[4] = p1k[4]; ppk[5] = p1k[4] + tscl; x0 = ppk[5] ^ tk[0]; x1 = Sbox[x0 >> 8]; x2 = Sbox[x0 & 0xFF] ^ ((x1>>8) | (x1<<8)); ppk[0] += x2; x0 = ppk[0] ^ tk[1]; x1 = Sbox[x0 >> 8]; x2 = Sbox[x0 & 0xFF] ^ ((x1>>8) | (x1<<8)); ppk[1] += x2; x0 = ppk[1] ^ tk[2]; x1 = Sbox[x0 >> 8]; x2 = Sbox[x0 & 0xFF] ^ ((x1>>8) | (x1<<8)); ppk[2] += x2; x0 = ppk[2] ^ tk[3]; x1 = Sbox[x0 >> 8]; x2 = Sbox[x0 & 0xFF] ^ ((x1>>8) | (x1<<8)); ppk[3] += x2; x0 = ppk[3] ^ tk[4]; x1 = Sbox[x0 >> 8]; x2 = Sbox[x0 & 0xFF] ^ ((x1>>8) | (x1<<8)); ppk[4] += x2; x0 = ppk[4] ^ tk[5]; x1 = Sbox[x0 >> 8]; x2 = Sbox[x0 & 0xFF] ^ ((x1>>8) | (x1<<8)); ppk[5] += x2; x2 = ppk[5] ^ tk[6]; ppk[0] += (x2 >> 1) | (x2 << 15); x2 = ppk[0] ^ tk[7]; ppk[1] += (x2 >> 1) | (x2 << 15); x2 = ppk[1]; ppk[2] += (x2 >> 1) | (x2 << 15); x2 = ppk[2]; ppk[3] += (x2 >> 1) | (x2 << 15); x2 = ppk[3]; ppk[4] += (x2 >> 1) | (x2 << 15); x2 = ppk[4]; ppk[5] += (x2 >> 1) | (x2 << 15); rc4key[0] = tscl >> 8; rc4key[1] = (rc4key[0] | 0x20) & 0x7F; rc4key[2] = tscl; rc4key[3] = (ppk[5] ^ tk[0]) >> 1; rc4key[4] = ppk[0]; rc4key[5] = ppk[0] >> 8; rc4key[6] = ppk[1]; rc4key[7] = ppk[1] >> 8; rc4key[8] = ppk[2]; rc4key[9] = ppk[2] >> 8; rc4key[10] = ppk[3]; rc4key[11] = ppk[3] >> 8; rc4key[12] = ppk[4]; rc4key[13] = ppk[4] >> 8; rc4key[14] = ppk[5]; rc4key[15] = ppk[5] >> 8; } typedef struct MICstate MICstate; struct MICstate { u32int l; u32int r; u32int m; u32int n; }; static void micsetup(MICstate *s, uchar key[8]) { s->l = (u32int)key[0] | (u32int)key[1]<<8 | (u32int)key[2]<<16 | (u32int)key[3]<<24; s->r = (u32int)key[4] | (u32int)key[5]<<8 | (u32int)key[6]<<16 | (u32int)key[7]<<24; s->m = 0; s->n = 0; } static void micupdate(MICstate *s, uchar *data, ulong len) { u32int l, r, m, n, e; l = s->l; r = s->r; m = s->m; n = s->n; e = n + len; while(n != e){ m >>= 8; m |= (u32int)*data++ << 24; if(++n & 3) continue; l ^= m; r ^= (l << 17) | (l >> 15); l += r; r ^= ((l & 0x00FF00FFUL)<<8) | ((l & 0xFF00FF00UL)>>8); l += r; r ^= (l << 3) | (l >> 29); l += r; r ^= (l >> 2) | (l << 30); l += r; } s->l = l; s->r = r; s->m = m; s->n = n; } static void micfinish(MICstate *s, uchar mic[8]) { static uchar pad[8] = { 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; micupdate(s, pad, sizeof(pad)); mic[0] = s->l; mic[1] = s->l>>8; mic[2] = s->l>>16; mic[3] = s->l>>24; mic[4] = s->r; mic[5] = s->r>>8; mic[6] = s->r>>16; mic[7] = s->r>>24; } static uchar pad4[4] = { 0x00, 0x00, 0x00, 0x00, }; static void tkipencrypt(Wkey *k, Wifipkt *w, Block *b, uvlong tsc) { u16int tk[8], p1k[5]; uchar seed[16]; RC4state rs; MICstate ms; ulong crc; micsetup(&ms, k->key+24); micupdate(&ms, dstaddr(w), Eaddrlen); micupdate(&ms, srcaddr(w), Eaddrlen); micupdate(&ms, pad4, 4); micupdate(&ms, b->rp, BLEN(b)); micfinish(&ms, b->wp); b->wp += 8; crc = ethercrc(b->rp, BLEN(b)); crc = ~crc; b->wp[0] = crc; b->wp[1] = crc>>8; b->wp[2] = crc>>16; b->wp[3] = crc>>24; b->wp += 4; tkipk2tk(k->key, tk); tkipphase1(tsc >> 16, w->a2, tk, p1k); tkipphase2(tsc & 0xFFFF, p1k, tk, seed); setupRC4state(&rs, seed, sizeof(seed)); rc4(&rs, b->rp, BLEN(b)); } static int tkipdecrypt(Wkey *k, Wifipkt *w, Block *b, uvlong tsc) { uchar seed[16], mic[8]; u16int tk[8], p1k[5]; RC4state rs; MICstate ms; ulong crc; if(BLEN(b) < 8+4) return -1; tkipk2tk(k->key, tk); tkipphase1(tsc >> 16, w->a2, tk, p1k); tkipphase2(tsc & 0xFFFF, p1k, tk, seed); setupRC4state(&rs, seed, sizeof(seed)); rc4(&rs, b->rp, BLEN(b)); b->wp -= 4; crc = (ulong)b->wp[0] | (ulong)b->wp[1]<<8 | (ulong)b->wp[2]<<16 | (ulong)b->wp[3]<<24; crc = ~crc; crc ^= ethercrc(b->rp, BLEN(b)); b->wp -= 8; micsetup(&ms, k->key+16); micupdate(&ms, dstaddr(w), Eaddrlen); micupdate(&ms, srcaddr(w), Eaddrlen); micupdate(&ms, pad4, 4); micupdate(&ms, b->rp, BLEN(b)); micfinish(&ms, mic); return tsmemcmp(b->wp, mic, 8) | crc; } static uchar* putbe(uchar *p, int L, uint v) { while(--L >= 0) *p++ = (v >> L*8) & 0xFF; return p; } static void xblock(int L, int M, uchar *N, uchar *a, int la, int lm, uchar t[16], AESstate *s) { uchar l[8], *p, *x, *e; assert(M >= 4 && M <= 16); assert(L >= 2 && L <= 4); t[0] = ((la > 0)<<6) | ((M-2)/2)<<3 | (L-1); /* flags */ memmove(&t[1], N, 15-L); putbe(&t[16-L], L, lm); aes_encrypt(s->ekey, s->rounds, t, t); if(la > 0){ assert(la < 0xFF00); for(p = l, e = putbe(l, 2, la), x = t; p < e; x++, p++) *x ^= *p; for(e = a + la; a < e; x = t){ for(; a < e && x < &t[16]; x++, a++) *x ^= *a; aes_encrypt(s->ekey, s->rounds, t, t); } } } static uchar* sblock(int L, uchar *N, uint i, uchar b[16], AESstate *s) { b[0] = L-1; /* flags */ memmove(&b[1], N, 15-L); putbe(&b[16-L], L, i); aes_encrypt(s->ekey, s->rounds, b, b); return b; }; static void aesCCMencrypt(int L, int M, uchar *N /* N[15-L] */, uchar *a /* a[la] */, int la, uchar *m /* m[lm+M] */, int lm, AESstate *s) { uchar t[16], b[16], *p, *x; uint i; xblock(L, M, N, a, la, lm, t, s); for(i = 1; lm >= 16; i++, m += 16, lm -= 16){ sblock(L, N, i, b, s); *((u32int*)&t[0]) ^= *((u32int*)&m[0]); *((u32int*)&m[0]) ^= *((u32int*)&b[0]); *((u32int*)&t[4]) ^= *((u32int*)&m[4]); *((u32int*)&m[4]) ^= *((u32int*)&b[4]); *((u32int*)&t[8]) ^= *((u32int*)&m[8]); *((u32int*)&m[8]) ^= *((u32int*)&b[8]); *((u32int*)&t[12]) ^= *((u32int*)&m[12]); *((u32int*)&m[12]) ^= *((u32int*)&b[12]); aes_encrypt(s->ekey, s->rounds, t, t); } if(lm > 0){ for(p = sblock(L, N, i, b, s), x = t; p < &b[lm]; x++, m++, p++){ *x ^= *m; *m ^= *p; } aes_encrypt(s->ekey, s->rounds, t, t); } for(p = sblock(L, N, 0, b, s), x = t; p < &b[M]; x++, p++) *x ^= *p; memmove(m, t, M); } static int aesCCMdecrypt(int L, int M, uchar *N /* N[15-L] */, uchar *a /* a[la] */, int la, uchar *m /* m[lm+M] */, int lm, AESstate *s) { uchar t[16], b[16], *p, *x; uint i; xblock(L, M, N, a, la, lm, t, s); for(i = 1; lm >= 16; i++, m += 16, lm -= 16){ sblock(L, N, i, b, s); *((u32int*)&m[0]) ^= *((u32int*)&b[0]); *((u32int*)&t[0]) ^= *((u32int*)&m[0]); *((u32int*)&m[4]) ^= *((u32int*)&b[4]); *((u32int*)&t[4]) ^= *((u32int*)&m[4]); *((u32int*)&m[8]) ^= *((u32int*)&b[8]); *((u32int*)&t[8]) ^= *((u32int*)&m[8]); *((u32int*)&m[12]) ^= *((u32int*)&b[12]); *((u32int*)&t[12]) ^= *((u32int*)&m[12]); aes_encrypt(s->ekey, s->rounds, t, t); } if(lm > 0){ for(p = sblock(L, N, i, b, s), x = t; p < &b[lm]; x++, m++, p++){ *m ^= *p; *x ^= *m; } aes_encrypt(s->ekey, s->rounds, t, t); } for(p = sblock(L, N, 0, b, s), x = t; p < &b[M]; x++, p++) *x ^= *p; return tsmemcmp(m, t, M); } static int setupCCMP(Wifipkt *w, uvlong tsc, uchar nonce[13], uchar auth[32]) { uchar *p; nonce[0] = ((w->fc[0] & 0x0c) == 0x00) << 4; memmove(&nonce[1], w->a2, Eaddrlen); nonce[7] = tsc >> 40; nonce[8] = tsc >> 32; nonce[9] = tsc >> 24; nonce[10] = tsc >> 16; nonce[11] = tsc >> 8; nonce[12] = tsc; p = auth; *p++ = (w->fc[0] & (((w->fc[0] & 0x0c) == 0x08) ? 0x0f : 0xff)); *p++ = (w->fc[1] & ~0x38) | 0x40; memmove(p, w->a1, Eaddrlen); p += Eaddrlen; memmove(p, w->a2, Eaddrlen); p += Eaddrlen; memmove(p, w->a3, Eaddrlen); p += Eaddrlen; *p++ = w->seq[0] & 0x0f; *p++ = 0; if((w->fc[1] & 3) == 0x03) { memmove(p, w->a4, Eaddrlen); p += Eaddrlen; } return p - auth; } static void ccmpencrypt(Wkey *k, Wifipkt *w, Block *b, uvlong tsc) { uchar auth[32], nonce[13]; aesCCMencrypt(2, 8, nonce, auth, setupCCMP(w, tsc, nonce, auth), b->rp, BLEN(b), (AESstate*)k->key); b->wp += 8; } static int ccmpdecrypt(Wkey *k, Wifipkt *w, Block *b, uvlong tsc) { uchar auth[32], nonce[13]; if(BLEN(b) < 8) return -1; b->wp -= 8; return aesCCMdecrypt(2, 8, nonce, auth, setupCCMP(w, tsc, nonce, auth), b->rp, BLEN(b), (AESstate*)k->key); } /* * Dynamic Mac Address Translation (DMAT) * * Wifi does not allow spoofing of the source mac which breaks * bridging. To solve this we proxy mac addresses, maintaining * a translation table from ip address to destination mac address. * Upstream ARP and NDP packets get ther source mac address changed * to proxy and a translation entry is added with the original mac * for downstream translation. The proxy does not appear in the * table. */ #include "../ip/ip.h" #include "../ip/ipv6.h" static void dmatproxy(Block *bp, int upstream, uchar proxy[Eaddrlen], DMAT *t) { static uchar arp4[] = { 0x00, 0x01, 0x08, 0x00, 0x06, 0x04, 0x00, }; uchar ip[IPaddrlen], mac[Eaddrlen], *targ, *end, *a, *o; ulong csum, c, h; Etherpkt *pkt; int proto, i; DMTE *te; end = bp->wp; pkt = (Etherpkt*)bp->rp; a = pkt->data; if(a >= end) return; if(upstream) memmove(pkt->s, proxy, Eaddrlen); else if(t->map == 0 || (pkt->d[0]&1) != 0 || memcmp(pkt->d, proxy, Eaddrlen) != 0) return; targ = nil; switch(pkt->type[0]<<8 | pkt->type[1]){ default: return; case ETIP4: case ETIP6: switch(a[0]&0xF0){ default: return; case IP_VER4: if(a+IP4HDR > end || (a[0]&15) < IP_HLEN4) return; v4tov6(ip, a+12+4*(upstream==0)); proto = a[9]; a += (a[0]&15)*4; break; case IP_VER6: if(a+IP6HDR > end) return; memmove(ip, a+8+16*(upstream==0), 16); proto = a[6]; a += IP6HDR; break; } if(!upstream) break; switch(proto){ case ICMPv6: if(a+8 > end) return; switch(a[0]){ default: return; case 133: /* Router Solicitation */ o = a+8; break; case 134: /* Router Advertisement */ o = a+8+8; break; case 136: /* Neighbor Advertisement */ targ = a+8; /* wet floor */ case 135: /* Neighbor Solicitation */ o = a+8+16; break; case 137: /* Redirect */ o = a+8+16+16; break; } memset(mac, 0xFF, Eaddrlen); csum = (a[2]<<8 | a[3])^0xFFFF; while(o+8 <= end && o[1] != 0){ switch(o[0]){ case SRC_LLADDR: case TARGET_LLADDR: for(i=0; i<Eaddrlen; i += 2) csum += (o[2+i]<<8 | o[3+i])^0xFFFF; memmove(mac, o+2, Eaddrlen); memmove(o+2, proxy, Eaddrlen); for(i=0; i<Eaddrlen; i += 2) csum += (o[2+i]<<8 | o[3+i]); break; } o += o[1]*8; } while((c = csum >> 16) != 0) csum = (csum & 0xFFFF) + c; csum ^= 0xFFFF; a[2] = csum>>8; a[3] = csum; break; case UDP: /* for BOOTP */ if(a+42 > end || (a[0]<<8 | a[1]) != 68 || (a[2]<<8 | a[3]) != 67 || a[8] != 1 || a[9] != 1 || a[10] != Eaddrlen || (a[18]&0x80) != 0 || memcmp(a+36, proxy, Eaddrlen) == 0) return; csum = (a[6]<<8 | a[7])^0xFFFF; /* set the broadcast flag so response reaches us */ csum += (a[18]<<8)^0xFFFF; a[18] |= 0x80; csum += (a[18]<<8); while((c = csum >> 16) != 0) csum = (csum & 0xFFFF) + c; csum ^= 0xFFFF; a[6] = csum>>8; a[7] = csum; default: return; } break; case ETARP: if(a+26 > end || memcmp(a, arp4, sizeof(arp4)) != 0 || (a[7] != 1 && a[7] != 2)) return; v4tov6(ip, a+14+10*(upstream==0)); if(upstream){ memmove(mac, a+8, Eaddrlen); memmove(a+8, proxy, Eaddrlen); } break; } Again: h = ( (ip[IPaddrlen-1] ^ proxy[2])<<24 | (ip[IPaddrlen-2] ^ proxy[3])<<16 | (ip[IPaddrlen-3] ^ proxy[4])<<8 | (ip[IPaddrlen-4] ^ proxy[5]) ) % nelem(t->tab); te = &t->tab[h]; h &= 63; if(upstream){ if((mac[0]&1) != 0 || memcmp(mac, proxy, Eaddrlen) == 0) return; for(i=0; te->valid && i<nelem(t->tab); i++){ if(memcmp(te->ip, ip, IPaddrlen) == 0) break; if(++te >= &t->tab[nelem(t->tab)]) te = t->tab; } memmove(te->mac, mac, Eaddrlen); memmove(te->ip, ip, IPaddrlen); te->valid = 1; t->map |= 1ULL<<h; if(targ != nil){ memmove(ip, targ, IPaddrlen); targ = nil; goto Again; } } else { if((t->map>>h & 1) == 0) return; for(i=0; te->valid && i<nelem(t->tab); i++){ if(memcmp(te->ip, ip, IPaddrlen) == 0){ memmove(pkt->d, te->mac, Eaddrlen); return; } if(++te >= &t->tab[nelem(t->tab)]) te = t->tab; } } }