shithub: riscv

Download patch

ref: 070a9ef753990bc37696f059c1751d25831b5c17
parent: 9fd8894fec87ccd326997b84a2be431af8764cce
author: cinap_lenrek <[email protected]>
date: Sat Dec 16 16:43:47 EST 2017

wifi: matt damon wifi bridging support

--- a/sys/src/9/pc/mkfile
+++ b/sys/src/9/pc/mkfile
@@ -125,7 +125,7 @@
 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
--- 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* 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 @@
 		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 @@
 	/* 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 @@
 	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)
@@ -1687,4 +1695,182 @@
 	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.
+ */
+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;
+		}
+	}
 }
--- a/sys/src/9/pc/wifi.h
+++ b/sys/src/9/pc/wifi.h
@@ -2,6 +2,8 @@
 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 @@
 	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 @@
 	Wnode	*bss;
 
 	Wnode	node[32];
+
+	DMAT	dmat;
 };
 
 struct Wifipkt
--- a/sys/src/9/pc64/mkfile
+++ b/sys/src/9/pc64/mkfile
@@ -125,7 +125,7 @@
 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