diff options
author | cinap_lenrek <cinap_lenrek@felloff.net> | 2017-12-16 21:43:47 +0100 |
---|---|---|
committer | cinap_lenrek <cinap_lenrek@felloff.net> | 2017-12-16 21:43:47 +0100 |
commit | 070a9ef753990bc37696f059c1751d25831b5c17 (patch) | |
tree | acc1cba0b036e69efb9bcb741a438c4bcce09a78 /sys/src/9 | |
parent | 9fd8894fec87ccd326997b84a2be431af8764cce (diff) |
wifi: matt damon wifi bridging support
Diffstat (limited to 'sys/src/9')
-rw-r--r-- | sys/src/9/pc/mkfile | 2 | ||||
-rw-r--r-- | sys/src/9/pc/wifi.c | 188 | ||||
-rw-r--r-- | sys/src/9/pc/wifi.h | 17 | ||||
-rw-r--r-- | sys/src/9/pc64/mkfile | 2 |
4 files changed, 206 insertions, 3 deletions
diff --git a/sys/src/9/pc/mkfile b/sys/src/9/pc/mkfile index 63044be64..62b0eebf0 100644 --- a/sys/src/9/pc/mkfile +++ b/sys/src/9/pc/mkfile @@ -125,7 +125,7 @@ etherm10g.$O: etherm10g2k.i etherm10g4k.i etheriwl.$O: wifi.h etherwpi.$O: wifi.h etherrt2860.$O: wifi.h -wifi.$O: wifi.h +wifi.$O: wifi.h etherif.h ../port/netif.h ../ip/ip.h /sys/include/libsec.h init.h:D: ../port/initcode.c init9.c $CC ../port/initcode.c diff --git a/sys/src/9/pc/wifi.c b/sys/src/9/pc/wifi.c index f176883fd..9e4b5291a 100644 --- a/sys/src/9/pc/wifi.c +++ b/sys/src/9/pc/wifi.c @@ -7,6 +7,7 @@ #include "ureg.h" #include "../port/error.h" #include "../port/netif.h" +#include "../ip/ip.h" #include "etherif.h" #include "wifi.h" @@ -50,6 +51,8 @@ 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) { @@ -133,6 +136,9 @@ wifiiq(Wifi *wifi, Block *b) 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, 1); return; } @@ -502,6 +508,7 @@ wifideauth(Wifi *wifi, Wnode *wn) /* 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){ @@ -644,9 +651,10 @@ wifietheroq(Wifi *wifi, Block *b) 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) @@ -1688,3 +1696,181 @@ ccmpdecrypt(Wkey *k, Wifipkt *w, Block *b, uvlong tsc) 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. + */ +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], *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; + + switch(pkt->type[0]<<8 | pkt->type[1]){ + default: + return; + case 0x0800: /* IPv4 */ + case 0x86dd: /* IPv6 */ + switch(a[0]&0xF0){ + default: + return; + case 0x40: /* IPv4 */ + if(a+20 > end) + return; + v4tov6(ip, a+12+4*(upstream==0)); + proto = a[9]; + a += (a[0]&15)*4; + break; + case 0x60: /* IPv6 */ + if(a+40 > end) + return; + memmove(ip, a+8+16*(upstream==0), 16); + proto = a[6]; + a += 40; + break; + } + if(!upstream) + break; + switch(proto){ + case 58: /* 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 135: /* Neighbor Solicitation */ + case 136: /* Neighbor Advertisement */ + 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 1: /* SLLA, for RS, RA and NS */ + case 2: /* TLLA, for NA and RD */ + 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 17: /* UDP (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 0x0806: /* ARP */ + 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; + } + + 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; + } 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; + } + } +} diff --git a/sys/src/9/pc/wifi.h b/sys/src/9/pc/wifi.h index 2171afd21..3abf4d1b9 100644 --- a/sys/src/9/pc/wifi.h +++ b/sys/src/9/pc/wifi.h @@ -2,6 +2,8 @@ typedef struct Wkey Wkey; typedef struct Wnode Wnode; typedef struct Wifi Wifi; typedef struct Wifipkt Wifipkt; +typedef struct DMAT DMAT; +typedef struct DMTE DMTE; enum { Essidlen = 32, @@ -52,6 +54,19 @@ struct Wnode uchar brsne[258]; }; +struct DMTE +{ + uchar ip[16]; + uchar mac[6]; + uchar valid; +}; + +struct DMAT +{ + DMTE tab[127]; /* prime */ + uvlong map; +}; + struct Wifi { Ether *ether; @@ -76,6 +91,8 @@ struct Wifi Wnode *bss; Wnode node[32]; + + DMAT dmat; }; struct Wifipkt diff --git a/sys/src/9/pc64/mkfile b/sys/src/9/pc64/mkfile index 4d145dc27..23be3ed10 100644 --- a/sys/src/9/pc64/mkfile +++ b/sys/src/9/pc64/mkfile @@ -125,7 +125,7 @@ ethermii.$O: ethermii.h etheriwl.$O: wifi.h etherwpi.$O: wifi.h etherrt2860.$O: wifi.h -wifi.$O: wifi.h +wifi.$O: wifi.h etherif.h ../port/netif.h ../ip/ip.h /sys/include/libsec.h init.h:D: ../port/initcode.c ../pc/init9.c $CC ../port/initcode.c |