xref: /plan9/sys/src/9/port/devbridge.c (revision aa72973a2891ccbd3fb042462446761159389e19)
1ac020a8fSDavid du Colombier /*
2ac020a8fSDavid du Colombier  * IPv4 Ethernet bridge
3ac020a8fSDavid du Colombier  */
47dd7cddfSDavid du Colombier #include "u.h"
57dd7cddfSDavid du Colombier #include "../port/lib.h"
67dd7cddfSDavid du Colombier #include "mem.h"
77dd7cddfSDavid du Colombier #include "dat.h"
87dd7cddfSDavid du Colombier #include "fns.h"
9ac020a8fSDavid du Colombier #include "../ip/ip.h"
107dd7cddfSDavid du Colombier #include "../port/netif.h"
117dd7cddfSDavid du Colombier #include "../port/error.h"
127dd7cddfSDavid du Colombier 
137dd7cddfSDavid du Colombier typedef struct Bridge 	Bridge;
147dd7cddfSDavid du Colombier typedef struct Port 	Port;
157dd7cddfSDavid du Colombier typedef struct Centry	Centry;
167dd7cddfSDavid du Colombier typedef struct Iphdr	Iphdr;
177dd7cddfSDavid du Colombier typedef struct Tcphdr	Tcphdr;
187dd7cddfSDavid du Colombier 
197dd7cddfSDavid du Colombier enum
207dd7cddfSDavid du Colombier {
217dd7cddfSDavid du Colombier 	Qtopdir=	1,		/* top level directory */
227dd7cddfSDavid du Colombier 
237dd7cddfSDavid du Colombier 	Qbridgedir,			/* bridge* directory */
247dd7cddfSDavid du Colombier 	Qbctl,
257dd7cddfSDavid du Colombier 	Qstats,
267dd7cddfSDavid du Colombier 	Qcache,
277dd7cddfSDavid du Colombier 	Qlog,
287dd7cddfSDavid du Colombier 
297dd7cddfSDavid du Colombier 	Qportdir,			/* directory for a protocol */
307dd7cddfSDavid du Colombier 	Qpctl,
317dd7cddfSDavid du Colombier 	Qlocal,
327dd7cddfSDavid du Colombier 	Qstatus,
337dd7cddfSDavid du Colombier 
347dd7cddfSDavid du Colombier 	MaxQ,
357dd7cddfSDavid du Colombier 
367dd7cddfSDavid du Colombier 	Maxbridge=	4,
379a747e4fSDavid du Colombier 	Maxport=	128,		// power of 2
387dd7cddfSDavid du Colombier 	CacheHash=	257,		// prime
397dd7cddfSDavid du Colombier 	CacheLook=	5,		// how many cache entries to examine
407dd7cddfSDavid du Colombier 	CacheSize=	(CacheHash+CacheLook-1),
417dd7cddfSDavid du Colombier 	CacheTimeout=	5*60,		// timeout for cache entry in seconds
427dd7cddfSDavid du Colombier 
437dd7cddfSDavid du Colombier 	TcpMssMax = 1300,		// max desirable Tcp MSS value
447dd7cddfSDavid du Colombier 	TunnelMtu = 1400,
457dd7cddfSDavid du Colombier };
467dd7cddfSDavid du Colombier 
477dd7cddfSDavid du Colombier static Dirtab bridgedirtab[]={
487dd7cddfSDavid du Colombier 	"ctl",		{Qbctl},	0,	0666,
497dd7cddfSDavid du Colombier 	"stats",	{Qstats},	0,	0444,
507dd7cddfSDavid du Colombier 	"cache",	{Qcache},	0,	0444,
517dd7cddfSDavid du Colombier 	"log",		{Qlog},		0,	0666,
527dd7cddfSDavid du Colombier };
537dd7cddfSDavid du Colombier 
547dd7cddfSDavid du Colombier static Dirtab portdirtab[]={
557dd7cddfSDavid du Colombier 	"ctl",		{Qpctl},	0,	0666,
567dd7cddfSDavid du Colombier 	"local",	{Qlocal},	0,	0444,
577dd7cddfSDavid du Colombier 	"status",	{Qstatus},	0,	0444,
587dd7cddfSDavid du Colombier };
597dd7cddfSDavid du Colombier 
607dd7cddfSDavid du Colombier enum {
617dd7cddfSDavid du Colombier 	Logcache=	(1<<0),
627dd7cddfSDavid du Colombier 	Logmcast=	(1<<1),
637dd7cddfSDavid du Colombier };
647dd7cddfSDavid du Colombier 
657dd7cddfSDavid du Colombier // types of interfaces
667dd7cddfSDavid du Colombier enum
677dd7cddfSDavid du Colombier {
687dd7cddfSDavid du Colombier 	Tether,
697dd7cddfSDavid du Colombier 	Ttun,
707dd7cddfSDavid du Colombier };
717dd7cddfSDavid du Colombier 
727dd7cddfSDavid du Colombier static Logflag logflags[] =
737dd7cddfSDavid du Colombier {
747dd7cddfSDavid du Colombier 	{ "cache",	Logcache, },
757dd7cddfSDavid du Colombier 	{ "multicast",	Logmcast, },
767dd7cddfSDavid du Colombier 	{ nil,		0, },
777dd7cddfSDavid du Colombier };
787dd7cddfSDavid du Colombier 
797dd7cddfSDavid du Colombier static Dirtab	*dirtab[MaxQ];
807dd7cddfSDavid du Colombier 
819a747e4fSDavid du Colombier #define TYPE(x) 	(((ulong)(x).path) & 0xff)
829a747e4fSDavid du Colombier #define PORT(x) 	((((ulong)(x).path) >> 8)&(Maxport-1))
837dd7cddfSDavid du Colombier #define QID(x, y) 	(((x)<<8) | (y))
847dd7cddfSDavid du Colombier 
857dd7cddfSDavid du Colombier struct Centry
867dd7cddfSDavid du Colombier {
877dd7cddfSDavid du Colombier 	uchar	d[Eaddrlen];
887dd7cddfSDavid du Colombier 	int	port;
89ac020a8fSDavid du Colombier 	long	expire;		// entry expires this many seconds after bootime
907dd7cddfSDavid du Colombier 	long	src;
917dd7cddfSDavid du Colombier 	long	dst;
927dd7cddfSDavid du Colombier };
937dd7cddfSDavid du Colombier 
947dd7cddfSDavid du Colombier struct Bridge
957dd7cddfSDavid du Colombier {
967dd7cddfSDavid du Colombier 	QLock;
977dd7cddfSDavid du Colombier 	int	nport;
987dd7cddfSDavid du Colombier 	Port	*port[Maxport];
997dd7cddfSDavid du Colombier 	Centry	cache[CacheSize];
1007dd7cddfSDavid du Colombier 	ulong	hit;
1017dd7cddfSDavid du Colombier 	ulong	miss;
1027dd7cddfSDavid du Colombier 	ulong	copy;
1037dd7cddfSDavid du Colombier 	long	delay0;		// constant microsecond delay per packet
1047dd7cddfSDavid du Colombier 	long	delayn;		// microsecond delay per byte
1057dd7cddfSDavid du Colombier 	int	tcpmss;		// modify tcpmss value
1067dd7cddfSDavid du Colombier 
1077dd7cddfSDavid du Colombier 	Log;
1087dd7cddfSDavid du Colombier };
1097dd7cddfSDavid du Colombier 
1107dd7cddfSDavid du Colombier struct Port
1117dd7cddfSDavid du Colombier {
1127dd7cddfSDavid du Colombier 	int	id;
1137dd7cddfSDavid du Colombier 	Bridge	*bridge;
1147dd7cddfSDavid du Colombier 	int	ref;
1157dd7cddfSDavid du Colombier 	int	closed;
1167dd7cddfSDavid du Colombier 
1177dd7cddfSDavid du Colombier 	Chan	*data[2];	// channel to data
1187dd7cddfSDavid du Colombier 
1197dd7cddfSDavid du Colombier 	Proc	*readp;		// read proc
1207dd7cddfSDavid du Colombier 
1217dd7cddfSDavid du Colombier 	// the following uniquely identifies the port
1227dd7cddfSDavid du Colombier 	int	type;
1239a747e4fSDavid du Colombier 	char	name[KNAMELEN];
1247dd7cddfSDavid du Colombier 
1257dd7cddfSDavid du Colombier 	// owner hash - avoids bind/unbind races
1267dd7cddfSDavid du Colombier 	ulong	ownhash;
1277dd7cddfSDavid du Colombier 
1287dd7cddfSDavid du Colombier 	// various stats
1297dd7cddfSDavid du Colombier 	int	in;		// number of packets read
1307dd7cddfSDavid du Colombier 	int	inmulti;	// multicast or broadcast
1317dd7cddfSDavid du Colombier 	int	inunknown;	// unknown address
1327dd7cddfSDavid du Colombier 	int	out;		// number of packets read
1337dd7cddfSDavid du Colombier 	int	outmulti;	// multicast or broadcast
1347dd7cddfSDavid du Colombier 	int	outunknown;	// unknown address
1357dd7cddfSDavid du Colombier 	int	outfrag;	// fragmented the packet
1367dd7cddfSDavid du Colombier 	int	nentry;		// number of cache entries for this port
1377dd7cddfSDavid du Colombier };
1387dd7cddfSDavid du Colombier 
1397dd7cddfSDavid du Colombier enum {
1407dd7cddfSDavid du Colombier 	IP_TCPPROTO	= 6,
1417dd7cddfSDavid du Colombier 	EOLOPT		= 0,
1427dd7cddfSDavid du Colombier 	NOOPOPT		= 1,
1437dd7cddfSDavid du Colombier 	MSSOPT		= 2,
1447dd7cddfSDavid du Colombier 	MSS_LENGTH	= 4,		/* Mean segment size */
1457dd7cddfSDavid du Colombier 	SYN		= 0x02,		/* Pkt. is synchronise */
1467dd7cddfSDavid du Colombier 	IPHDR		= 20,		/* sizeof(Iphdr) */
1477dd7cddfSDavid du Colombier };
1487dd7cddfSDavid du Colombier 
1497dd7cddfSDavid du Colombier struct Iphdr
1507dd7cddfSDavid du Colombier {
1517dd7cddfSDavid du Colombier 	uchar	vihl;		/* Version and header length */
1527dd7cddfSDavid du Colombier 	uchar	tos;		/* Type of service */
1537dd7cddfSDavid du Colombier 	uchar	length[2];	/* packet length */
1547dd7cddfSDavid du Colombier 	uchar	id[2];		/* ip->identification */
1557dd7cddfSDavid du Colombier 	uchar	frag[2];	/* Fragment information */
1567dd7cddfSDavid du Colombier 	uchar	ttl;		/* Time to live */
1577dd7cddfSDavid du Colombier 	uchar	proto;		/* Protocol */
1587dd7cddfSDavid du Colombier 	uchar	cksum[2];	/* Header checksum */
1597dd7cddfSDavid du Colombier 	uchar	src[4];		/* IP source */
1607dd7cddfSDavid du Colombier 	uchar	dst[4];		/* IP destination */
1617dd7cddfSDavid du Colombier };
1627dd7cddfSDavid du Colombier 
1637dd7cddfSDavid du Colombier struct Tcphdr
1647dd7cddfSDavid du Colombier {
1657dd7cddfSDavid du Colombier 	uchar	sport[2];
1667dd7cddfSDavid du Colombier 	uchar	dport[2];
1677dd7cddfSDavid du Colombier 	uchar	seq[4];
1687dd7cddfSDavid du Colombier 	uchar	ack[4];
1697dd7cddfSDavid du Colombier 	uchar	flag[2];
1707dd7cddfSDavid du Colombier 	uchar	win[2];
1717dd7cddfSDavid du Colombier 	uchar	cksum[2];
1727dd7cddfSDavid du Colombier 	uchar	urg[2];
1737dd7cddfSDavid du Colombier };
1747dd7cddfSDavid du Colombier 
1757dd7cddfSDavid du Colombier static Bridge bridgetab[Maxbridge];
1767dd7cddfSDavid du Colombier 
1777dd7cddfSDavid du Colombier static int m2p[] = {
1787dd7cddfSDavid du Colombier 	[OREAD]		4,
1797dd7cddfSDavid du Colombier 	[OWRITE]	2,
1807dd7cddfSDavid du Colombier 	[ORDWR]		6
1817dd7cddfSDavid du Colombier };
1827dd7cddfSDavid du Colombier 
1839a747e4fSDavid du Colombier static int	bridgegen(Chan *c, char*, Dirtab*, int, int s, Dir *dp);
1847dd7cddfSDavid du Colombier static void	portbind(Bridge *b, int argc, char *argv[]);
1857dd7cddfSDavid du Colombier static void	portunbind(Bridge *b, int argc, char *argv[]);
1867dd7cddfSDavid du Colombier static void	etherread(void *a);
1877dd7cddfSDavid du Colombier static char	*cachedump(Bridge *b);
1887dd7cddfSDavid du Colombier static void	portfree(Port *port);
1897dd7cddfSDavid du Colombier static void	cacheflushport(Bridge *b, int port);
1907dd7cddfSDavid du Colombier static void	etherwrite(Port *port, Block *bp);
1917dd7cddfSDavid du Colombier 
1927dd7cddfSDavid du Colombier static void
bridgeinit(void)1937dd7cddfSDavid du Colombier bridgeinit(void)
1947dd7cddfSDavid du Colombier {
1957dd7cddfSDavid du Colombier 	int i;
1967dd7cddfSDavid du Colombier 	Dirtab *dt;
197ac020a8fSDavid du Colombier 
1987dd7cddfSDavid du Colombier 	// setup dirtab with non directory entries
1997dd7cddfSDavid du Colombier 	for(i=0; i<nelem(bridgedirtab); i++) {
2007dd7cddfSDavid du Colombier 		dt = bridgedirtab + i;
2017dd7cddfSDavid du Colombier 		dirtab[TYPE(dt->qid)] = dt;
2027dd7cddfSDavid du Colombier 	}
2037dd7cddfSDavid du Colombier 	for(i=0; i<nelem(portdirtab); i++) {
2047dd7cddfSDavid du Colombier 		dt = portdirtab + i;
2057dd7cddfSDavid du Colombier 		dirtab[TYPE(dt->qid)] = dt;
2067dd7cddfSDavid du Colombier 	}
2077dd7cddfSDavid du Colombier }
2087dd7cddfSDavid du Colombier 
2097dd7cddfSDavid du Colombier static Chan*
bridgeattach(char * spec)2107dd7cddfSDavid du Colombier bridgeattach(char* spec)
2117dd7cddfSDavid du Colombier {
2127dd7cddfSDavid du Colombier 	Chan *c;
2137dd7cddfSDavid du Colombier 	int dev;
2147dd7cddfSDavid du Colombier 
2157dd7cddfSDavid du Colombier 	dev = atoi(spec);
2167dd7cddfSDavid du Colombier 	if(dev<0 || dev >= Maxbridge)
2177dd7cddfSDavid du Colombier 		error("bad specification");
2187dd7cddfSDavid du Colombier 
2197dd7cddfSDavid du Colombier 	c = devattach('B', spec);
2209a747e4fSDavid du Colombier 	mkqid(&c->qid, QID(0, Qtopdir), 0, QTDIR);
2217dd7cddfSDavid du Colombier 	c->dev = dev;
2227dd7cddfSDavid du Colombier 	return c;
2237dd7cddfSDavid du Colombier }
2247dd7cddfSDavid du Colombier 
2259a747e4fSDavid du Colombier static Walkqid*
bridgewalk(Chan * c,Chan * nc,char ** name,int nname)2269a747e4fSDavid du Colombier bridgewalk(Chan *c, Chan *nc, char **name, int nname)
2277dd7cddfSDavid du Colombier {
2289a747e4fSDavid du Colombier 	return devwalk(c, nc, name, nname, (Dirtab*)0, 0, bridgegen);
2297dd7cddfSDavid du Colombier }
2307dd7cddfSDavid du Colombier 
2319a747e4fSDavid du Colombier static int
bridgestat(Chan * c,uchar * db,int n)2329a747e4fSDavid du Colombier bridgestat(Chan* c, uchar* db, int n)
2337dd7cddfSDavid du Colombier {
2349a747e4fSDavid du Colombier 	return devstat(c, db, n, (Dirtab *)0, 0L, bridgegen);
2357dd7cddfSDavid du Colombier }
2367dd7cddfSDavid du Colombier 
2377dd7cddfSDavid du Colombier static Chan*
bridgeopen(Chan * c,int omode)2387dd7cddfSDavid du Colombier bridgeopen(Chan* c, int omode)
2397dd7cddfSDavid du Colombier {
2407dd7cddfSDavid du Colombier 	int perm;
2417dd7cddfSDavid du Colombier 	Bridge *b;
2427dd7cddfSDavid du Colombier 
2437dd7cddfSDavid du Colombier 	omode &= 3;
2447dd7cddfSDavid du Colombier 	perm = m2p[omode];
2457dd7cddfSDavid du Colombier 	USED(perm);
2467dd7cddfSDavid du Colombier 
2477dd7cddfSDavid du Colombier 	b = bridgetab + c->dev;
2487dd7cddfSDavid du Colombier 	USED(b);
2497dd7cddfSDavid du Colombier 
2507dd7cddfSDavid du Colombier 	switch(TYPE(c->qid)) {
2517dd7cddfSDavid du Colombier 	default:
2527dd7cddfSDavid du Colombier 		break;
2537dd7cddfSDavid du Colombier 	case Qlog:
2547dd7cddfSDavid du Colombier 		logopen(b);
2557dd7cddfSDavid du Colombier 		break;
2567dd7cddfSDavid du Colombier 	case Qcache:
2577dd7cddfSDavid du Colombier 		c->aux = cachedump(b);
2587dd7cddfSDavid du Colombier 		break;
2597dd7cddfSDavid du Colombier 	}
2607dd7cddfSDavid du Colombier 	c->mode = openmode(omode);
2617dd7cddfSDavid du Colombier 	c->flag |= COPEN;
2627dd7cddfSDavid du Colombier 	c->offset = 0;
2637dd7cddfSDavid du Colombier 	return c;
2647dd7cddfSDavid du Colombier }
2657dd7cddfSDavid du Colombier 
2667dd7cddfSDavid du Colombier static void
bridgeclose(Chan * c)2677dd7cddfSDavid du Colombier bridgeclose(Chan* c)
2687dd7cddfSDavid du Colombier {
2697dd7cddfSDavid du Colombier 	Bridge *b  = bridgetab + c->dev;
2707dd7cddfSDavid du Colombier 
2717dd7cddfSDavid du Colombier 	switch(TYPE(c->qid)) {
2727dd7cddfSDavid du Colombier 	case Qcache:
2737dd7cddfSDavid du Colombier 		if(c->flag & COPEN)
2747dd7cddfSDavid du Colombier 			free(c->aux);
2757dd7cddfSDavid du Colombier 		break;
2767dd7cddfSDavid du Colombier 	case Qlog:
2777dd7cddfSDavid du Colombier 		if(c->flag & COPEN)
2787dd7cddfSDavid du Colombier 			logclose(b);
2797dd7cddfSDavid du Colombier 		break;
2807dd7cddfSDavid du Colombier 	}
2817dd7cddfSDavid du Colombier }
2827dd7cddfSDavid du Colombier 
2837dd7cddfSDavid du Colombier static long
bridgeread(Chan * c,void * a,long n,vlong off)2847dd7cddfSDavid du Colombier bridgeread(Chan *c, void *a, long n, vlong off)
2857dd7cddfSDavid du Colombier {
2867dd7cddfSDavid du Colombier 	char buf[256];
2877dd7cddfSDavid du Colombier 	Bridge *b = bridgetab + c->dev;
2887dd7cddfSDavid du Colombier 	Port *port;
2897dd7cddfSDavid du Colombier 	int i, ingood, outgood;
2907dd7cddfSDavid du Colombier 
2917dd7cddfSDavid du Colombier 	USED(off);
2927dd7cddfSDavid du Colombier 	switch(TYPE(c->qid)) {
2937dd7cddfSDavid du Colombier 	default:
2947dd7cddfSDavid du Colombier 		error(Eperm);
2957dd7cddfSDavid du Colombier 	case Qtopdir:
2967dd7cddfSDavid du Colombier 	case Qbridgedir:
2977dd7cddfSDavid du Colombier 	case Qportdir:
2987dd7cddfSDavid du Colombier 		return devdirread(c, a, n, 0, 0, bridgegen);
2997dd7cddfSDavid du Colombier 	case Qlog:
3007dd7cddfSDavid du Colombier 		return logread(b, a, off, n);
3017dd7cddfSDavid du Colombier 	case Qstatus:
3027dd7cddfSDavid du Colombier 		qlock(b);
3037dd7cddfSDavid du Colombier 		port = b->port[PORT(c->qid)];
3047dd7cddfSDavid du Colombier 		if(port == 0)
3057dd7cddfSDavid du Colombier 			strcpy(buf, "unbound\n");
3067dd7cddfSDavid du Colombier 		else {
3077dd7cddfSDavid du Colombier 			i = 0;
3087dd7cddfSDavid du Colombier 			switch(port->type) {
309ac020a8fSDavid du Colombier 			default:
310ac020a8fSDavid du Colombier 				panic("bridgeread: unknown port type: %d",
311ac020a8fSDavid du Colombier 					port->type);
3127dd7cddfSDavid du Colombier 			case Tether:
3137dd7cddfSDavid du Colombier 				i += snprint(buf+i, sizeof(buf)-i, "ether %s: ", port->name);
3147dd7cddfSDavid du Colombier 				break;
3157dd7cddfSDavid du Colombier 			case Ttun:
3167dd7cddfSDavid du Colombier 				i += snprint(buf+i, sizeof(buf)-i, "tunnel %s: ", port->name);
3177dd7cddfSDavid du Colombier 				break;
3187dd7cddfSDavid du Colombier 			}
3197dd7cddfSDavid du Colombier 			ingood = port->in - port->inmulti - port->inunknown;
3207dd7cddfSDavid du Colombier 			outgood = port->out - port->outmulti - port->outunknown;
321ac020a8fSDavid du Colombier 			i += snprint(buf+i, sizeof(buf)-i,
322ac020a8fSDavid du Colombier 				"in=%d(%d:%d:%d) out=%d(%d:%d:%d:%d)\n",
3237dd7cddfSDavid du Colombier 				port->in, ingood, port->inmulti, port->inunknown,
324ac020a8fSDavid du Colombier 				port->out, outgood, port->outmulti,
325ac020a8fSDavid du Colombier 				port->outunknown, port->outfrag);
3267dd7cddfSDavid du Colombier 			USED(i);
3277dd7cddfSDavid du Colombier 		}
3287dd7cddfSDavid du Colombier 		n = readstr(off, a, n, buf);
3297dd7cddfSDavid du Colombier 		qunlock(b);
3307dd7cddfSDavid du Colombier 		return n;
3317dd7cddfSDavid du Colombier 	case Qbctl:
3326c0968e3SDavid du Colombier 		snprint(buf, sizeof(buf), "%s tcpmss\ndelay %ld %ld\n",
3336c0968e3SDavid du Colombier 			b->tcpmss ? "set" : "clear", b->delay0, b->delayn);
3347dd7cddfSDavid du Colombier 		n = readstr(off, a, n, buf);
3357dd7cddfSDavid du Colombier 		return n;
3367dd7cddfSDavid du Colombier 	case Qcache:
3377dd7cddfSDavid du Colombier 		n = readstr(off, a, n, c->aux);
3387dd7cddfSDavid du Colombier 		return n;
3397dd7cddfSDavid du Colombier 	case Qstats:
3407dd7cddfSDavid du Colombier 		snprint(buf, sizeof(buf), "hit=%uld miss=%uld copy=%uld\n",
3417dd7cddfSDavid du Colombier 			b->hit, b->miss, b->copy);
3427dd7cddfSDavid du Colombier 		n = readstr(off, a, n, buf);
3437dd7cddfSDavid du Colombier 		return n;
3447dd7cddfSDavid du Colombier 	}
3457dd7cddfSDavid du Colombier }
3467dd7cddfSDavid du Colombier 
3477dd7cddfSDavid du Colombier static void
bridgeoption(Bridge * b,char * option,int value)3487dd7cddfSDavid du Colombier bridgeoption(Bridge *b, char *option, int value)
3497dd7cddfSDavid du Colombier {
3507dd7cddfSDavid du Colombier 	if(strcmp(option, "tcpmss") == 0)
3517dd7cddfSDavid du Colombier 		b->tcpmss = value;
3527dd7cddfSDavid du Colombier 	else
3537dd7cddfSDavid du Colombier 		error("unknown bridge option");
3547dd7cddfSDavid du Colombier }
3557dd7cddfSDavid du Colombier 
3567dd7cddfSDavid du Colombier 
3577dd7cddfSDavid du Colombier static long
bridgewrite(Chan * c,void * a,long n,vlong off)3587dd7cddfSDavid du Colombier bridgewrite(Chan *c, void *a, long n, vlong off)
3597dd7cddfSDavid du Colombier {
3607dd7cddfSDavid du Colombier 	Bridge *b = bridgetab + c->dev;
3617dd7cddfSDavid du Colombier 	Cmdbuf *cb;
362ac020a8fSDavid du Colombier 	char *arg0, *p;
3637dd7cddfSDavid du Colombier 
3647dd7cddfSDavid du Colombier 	USED(off);
3657dd7cddfSDavid du Colombier 	switch(TYPE(c->qid)) {
3667dd7cddfSDavid du Colombier 	default:
3677dd7cddfSDavid du Colombier 		error(Eperm);
3687dd7cddfSDavid du Colombier 	case Qbctl:
3697dd7cddfSDavid du Colombier 		cb = parsecmd(a, n);
3707dd7cddfSDavid du Colombier 		qlock(b);
3717dd7cddfSDavid du Colombier 		if(waserror()) {
3727dd7cddfSDavid du Colombier 			qunlock(b);
3737dd7cddfSDavid du Colombier 			free(cb);
3747dd7cddfSDavid du Colombier 			nexterror();
3757dd7cddfSDavid du Colombier 		}
3767dd7cddfSDavid du Colombier 		if(cb->nf == 0)
3777dd7cddfSDavid du Colombier 			error("short write");
3787dd7cddfSDavid du Colombier 		arg0 = cb->f[0];
3797dd7cddfSDavid du Colombier 		if(strcmp(arg0, "bind") == 0) {
3807dd7cddfSDavid du Colombier 			portbind(b, cb->nf-1, cb->f+1);
3817dd7cddfSDavid du Colombier 		} else if(strcmp(arg0, "unbind") == 0) {
3827dd7cddfSDavid du Colombier 			portunbind(b, cb->nf-1, cb->f+1);
3837dd7cddfSDavid du Colombier 		} else if(strcmp(arg0, "cacheflush") == 0) {
3847dd7cddfSDavid du Colombier 			log(b, Logcache, "cache flush\n");
3857dd7cddfSDavid du Colombier 			memset(b->cache, 0, CacheSize*sizeof(Centry));
3867dd7cddfSDavid du Colombier 		} else if(strcmp(arg0, "set") == 0) {
3877dd7cddfSDavid du Colombier 			if(cb->nf != 2)
3887dd7cddfSDavid du Colombier 				error("usage: set option");
3897dd7cddfSDavid du Colombier 			bridgeoption(b, cb->f[1], 1);
3907dd7cddfSDavid du Colombier 		} else if(strcmp(arg0, "clear") == 0) {
3917dd7cddfSDavid du Colombier 			if(cb->nf != 2)
3927dd7cddfSDavid du Colombier 				error("usage: clear option");
3937dd7cddfSDavid du Colombier 			bridgeoption(b, cb->f[1], 0);
3947dd7cddfSDavid du Colombier 		} else if(strcmp(arg0, "delay") == 0) {
3957dd7cddfSDavid du Colombier 			if(cb->nf != 3)
3967dd7cddfSDavid du Colombier 				error("usage: delay delay0 delayn");
3977dd7cddfSDavid du Colombier 			b->delay0 = strtol(cb->f[1], nil, 10);
3987dd7cddfSDavid du Colombier 			b->delayn = strtol(cb->f[2], nil, 10);
3997dd7cddfSDavid du Colombier 		} else
4007dd7cddfSDavid du Colombier 			error("unknown control request");
4017dd7cddfSDavid du Colombier 		poperror();
4027dd7cddfSDavid du Colombier 		qunlock(b);
4037dd7cddfSDavid du Colombier 		free(cb);
4047dd7cddfSDavid du Colombier 		return n;
4057dd7cddfSDavid du Colombier 	case Qlog:
4067dd7cddfSDavid du Colombier 		cb = parsecmd(a, n);
4077dd7cddfSDavid du Colombier 		p = logctl(b, cb->nf, cb->f, logflags);
4087dd7cddfSDavid du Colombier 		free(cb);
4097dd7cddfSDavid du Colombier 		if(p != nil)
4107dd7cddfSDavid du Colombier 			error(p);
4117dd7cddfSDavid du Colombier 		return n;
4127dd7cddfSDavid du Colombier 	}
4137dd7cddfSDavid du Colombier }
4147dd7cddfSDavid du Colombier 
4157dd7cddfSDavid du Colombier static int
bridgegen(Chan * c,char *,Dirtab *,int,int s,Dir * dp)4169a747e4fSDavid du Colombier bridgegen(Chan *c, char *, Dirtab*, int, int s, Dir *dp)
4177dd7cddfSDavid du Colombier {
4187dd7cddfSDavid du Colombier 	Bridge *b = bridgetab + c->dev;
4197dd7cddfSDavid du Colombier 	int type = TYPE(c->qid);
4207dd7cddfSDavid du Colombier 	Dirtab *dt;
4217dd7cddfSDavid du Colombier 	Qid qid;
4227dd7cddfSDavid du Colombier 
4237dd7cddfSDavid du Colombier 	if(s  == DEVDOTDOT){
4247dd7cddfSDavid du Colombier 		switch(TYPE(c->qid)){
4257dd7cddfSDavid du Colombier 		case Qtopdir:
4267dd7cddfSDavid du Colombier 		case Qbridgedir:
4279a747e4fSDavid du Colombier 			snprint(up->genbuf, sizeof(up->genbuf), "#B%ld", c->dev);
4289a747e4fSDavid du Colombier 			mkqid(&qid, Qtopdir, 0, QTDIR);
4299a747e4fSDavid du Colombier 			devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
4307dd7cddfSDavid du Colombier 			break;
4317dd7cddfSDavid du Colombier 		case Qportdir:
4329a747e4fSDavid du Colombier 			snprint(up->genbuf, sizeof(up->genbuf), "bridge%ld", c->dev);
4339a747e4fSDavid du Colombier 			mkqid(&qid, Qbridgedir, 0, QTDIR);
4349a747e4fSDavid du Colombier 			devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
4357dd7cddfSDavid du Colombier 			break;
4367dd7cddfSDavid du Colombier 		default:
4379a747e4fSDavid du Colombier 			panic("bridgewalk %llux", c->qid.path);
4387dd7cddfSDavid du Colombier 		}
4397dd7cddfSDavid du Colombier 		return 1;
4407dd7cddfSDavid du Colombier 	}
4417dd7cddfSDavid du Colombier 
4427dd7cddfSDavid du Colombier 	switch(type) {
4437dd7cddfSDavid du Colombier 	default:
444ac020a8fSDavid du Colombier 		/* non-directory entries end up here */
4459a747e4fSDavid du Colombier 		if(c->qid.type & QTDIR)
4467dd7cddfSDavid du Colombier 			panic("bridgegen: unexpected directory");
4477dd7cddfSDavid du Colombier 		if(s != 0)
4487dd7cddfSDavid du Colombier 			return -1;
4497dd7cddfSDavid du Colombier 		dt = dirtab[TYPE(c->qid)];
4507dd7cddfSDavid du Colombier 		if(dt == nil)
4519a747e4fSDavid du Colombier 			panic("bridgegen: unknown type: %lud", TYPE(c->qid));
4527dd7cddfSDavid du Colombier 		devdir(c, c->qid, dt->name, dt->length, eve, dt->perm, dp);
4537dd7cddfSDavid du Colombier 		return 1;
4547dd7cddfSDavid du Colombier 	case Qtopdir:
4557dd7cddfSDavid du Colombier 		if(s != 0)
4567dd7cddfSDavid du Colombier 			return -1;
4579a747e4fSDavid du Colombier 		snprint(up->genbuf, sizeof(up->genbuf), "bridge%ld", c->dev);
4589a747e4fSDavid du Colombier 		mkqid(&qid, QID(0, Qbridgedir), 0, QTDIR);
4599a747e4fSDavid du Colombier 		devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
4607dd7cddfSDavid du Colombier 		return 1;
4617dd7cddfSDavid du Colombier 	case Qbridgedir:
4627dd7cddfSDavid du Colombier 		if(s<nelem(bridgedirtab)) {
4637dd7cddfSDavid du Colombier 			dt = bridgedirtab+s;
4647dd7cddfSDavid du Colombier 			devdir(c, dt->qid, dt->name, dt->length, eve, dt->perm, dp);
4657dd7cddfSDavid du Colombier 			return 1;
4667dd7cddfSDavid du Colombier 		}
4677dd7cddfSDavid du Colombier 		s -= nelem(bridgedirtab);
4687dd7cddfSDavid du Colombier 		if(s >= b->nport)
4697dd7cddfSDavid du Colombier 			return -1;
4709a747e4fSDavid du Colombier 		mkqid(&qid, QID(s, Qportdir), 0, QTDIR);
4719a747e4fSDavid du Colombier 		snprint(up->genbuf, sizeof(up->genbuf), "%d", s);
4729a747e4fSDavid du Colombier 		devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
4737dd7cddfSDavid du Colombier 		return 1;
4747dd7cddfSDavid du Colombier 	case Qportdir:
4757dd7cddfSDavid du Colombier 		if(s>=nelem(portdirtab))
4767dd7cddfSDavid du Colombier 			return -1;
4777dd7cddfSDavid du Colombier 		dt = portdirtab+s;
4789a747e4fSDavid du Colombier 		mkqid(&qid, QID(PORT(c->qid),TYPE(dt->qid)), 0, QTFILE);
4797dd7cddfSDavid du Colombier 		devdir(c, qid, dt->name, dt->length, eve, dt->perm, dp);
4807dd7cddfSDavid du Colombier 		return 1;
4817dd7cddfSDavid du Colombier 	}
4827dd7cddfSDavid du Colombier }
4837dd7cddfSDavid du Colombier 
484ac020a8fSDavid du Colombier // parse mac address; also in netif.c
4857dd7cddfSDavid du Colombier static int
parseaddr(uchar * to,char * from,int alen)4867dd7cddfSDavid du Colombier parseaddr(uchar *to, char *from, int alen)
4877dd7cddfSDavid du Colombier {
4887dd7cddfSDavid du Colombier 	char nip[4];
4897dd7cddfSDavid du Colombier 	char *p;
4907dd7cddfSDavid du Colombier 	int i;
4917dd7cddfSDavid du Colombier 
4927dd7cddfSDavid du Colombier 	p = from;
4937dd7cddfSDavid du Colombier 	for(i = 0; i < alen; i++){
4947dd7cddfSDavid du Colombier 		if(*p == 0)
4957dd7cddfSDavid du Colombier 			return -1;
4967dd7cddfSDavid du Colombier 		nip[0] = *p++;
4977dd7cddfSDavid du Colombier 		if(*p == 0)
4987dd7cddfSDavid du Colombier 			return -1;
4997dd7cddfSDavid du Colombier 		nip[1] = *p++;
5007dd7cddfSDavid du Colombier 		nip[2] = 0;
5017dd7cddfSDavid du Colombier 		to[i] = strtoul(nip, 0, 16);
5027dd7cddfSDavid du Colombier 		if(*p == ':')
5037dd7cddfSDavid du Colombier 			p++;
5047dd7cddfSDavid du Colombier 	}
5057dd7cddfSDavid du Colombier 	return 0;
5067dd7cddfSDavid du Colombier }
5077dd7cddfSDavid du Colombier 
5087dd7cddfSDavid du Colombier // assumes b is locked
5097dd7cddfSDavid du Colombier static void
portbind(Bridge * b,int argc,char * argv[])5107dd7cddfSDavid du Colombier portbind(Bridge *b, int argc, char *argv[])
5117dd7cddfSDavid du Colombier {
5127dd7cddfSDavid du Colombier 	Port *port;
5137dd7cddfSDavid du Colombier 	Chan *ctl;
5147dd7cddfSDavid du Colombier 	int type = 0, i, n;
5157dd7cddfSDavid du Colombier 	ulong ownhash;
516ac020a8fSDavid du Colombier 	char *dev, *dev2 = nil, *p;
517ac020a8fSDavid du Colombier 	char buf[100], name[KNAMELEN], path[8*KNAMELEN];
518ac020a8fSDavid du Colombier 	static char usage[] = "usage: bind ether|tunnel name ownhash dev [dev2]";
5197dd7cddfSDavid du Colombier 
5209a747e4fSDavid du Colombier 	memset(name, 0, KNAMELEN);
5217dd7cddfSDavid du Colombier 	if(argc < 4)
5227dd7cddfSDavid du Colombier 		error(usage);
5237dd7cddfSDavid du Colombier 	if(strcmp(argv[0], "ether") == 0) {
5247dd7cddfSDavid du Colombier 		if(argc != 4)
5257dd7cddfSDavid du Colombier 			error(usage);
5267dd7cddfSDavid du Colombier 		type = Tether;
5279a747e4fSDavid du Colombier 		strncpy(name, argv[1], KNAMELEN);
5289a747e4fSDavid du Colombier 		name[KNAMELEN-1] = 0;
5297dd7cddfSDavid du Colombier //		parseaddr(addr, argv[1], Eaddrlen);
5307dd7cddfSDavid du Colombier 	} else if(strcmp(argv[0], "tunnel") == 0) {
5317dd7cddfSDavid du Colombier 		if(argc != 5)
5327dd7cddfSDavid du Colombier 			error(usage);
5337dd7cddfSDavid du Colombier 		type = Ttun;
5349a747e4fSDavid du Colombier 		strncpy(name, argv[1], KNAMELEN);
5359a747e4fSDavid du Colombier 		name[KNAMELEN-1] = 0;
5367dd7cddfSDavid du Colombier //		parseip(addr, argv[1]);
5377dd7cddfSDavid du Colombier 		dev2 = argv[4];
5387dd7cddfSDavid du Colombier 	} else
5397dd7cddfSDavid du Colombier 		error(usage);
5407dd7cddfSDavid du Colombier 	ownhash = atoi(argv[2]);
5417dd7cddfSDavid du Colombier 	dev = argv[3];
5427dd7cddfSDavid du Colombier 	for(i=0; i<b->nport; i++) {
5437dd7cddfSDavid du Colombier 		port = b->port[i];
544ac020a8fSDavid du Colombier 		if(port != nil && port->type == type &&
545ac020a8fSDavid du Colombier 		    memcmp(port->name, name, KNAMELEN) == 0)
5467dd7cddfSDavid du Colombier 			error("port in use");
5477dd7cddfSDavid du Colombier 	}
5487dd7cddfSDavid du Colombier 	for(i=0; i<Maxport; i++)
5497dd7cddfSDavid du Colombier 		if(b->port[i] == nil)
5507dd7cddfSDavid du Colombier 			break;
5517dd7cddfSDavid du Colombier 	if(i == Maxport)
5527dd7cddfSDavid du Colombier 		error("no more ports");
5537dd7cddfSDavid du Colombier 	port = smalloc(sizeof(Port));
5547dd7cddfSDavid du Colombier 	port->ref = 1;
5557dd7cddfSDavid du Colombier 	port->id = i;
5567dd7cddfSDavid du Colombier 	port->ownhash = ownhash;
5577dd7cddfSDavid du Colombier 
5587dd7cddfSDavid du Colombier 	if(waserror()) {
5597dd7cddfSDavid du Colombier 		portfree(port);
5607dd7cddfSDavid du Colombier 		nexterror();
5617dd7cddfSDavid du Colombier 	}
5627dd7cddfSDavid du Colombier 	port->type = type;
5639a747e4fSDavid du Colombier 	memmove(port->name, name, KNAMELEN);
5647dd7cddfSDavid du Colombier 	switch(port->type) {
565ac020a8fSDavid du Colombier 	default:
566ac020a8fSDavid du Colombier 		panic("portbind: unknown port type: %d", type);
5677dd7cddfSDavid du Colombier 	case Tether:
5687dd7cddfSDavid du Colombier 		snprint(path, sizeof(path), "%s/clone", dev);
5697dd7cddfSDavid du Colombier 		ctl = namec(path, Aopen, ORDWR, 0);
5707dd7cddfSDavid du Colombier 		if(waserror()) {
5717dd7cddfSDavid du Colombier 			cclose(ctl);
5727dd7cddfSDavid du Colombier 			nexterror();
5737dd7cddfSDavid du Colombier 		}
5747dd7cddfSDavid du Colombier 		// check addr?
5757dd7cddfSDavid du Colombier 
5767dd7cddfSDavid du Colombier 		// get directory name
5777dd7cddfSDavid du Colombier 		n = devtab[ctl->type]->read(ctl, buf, sizeof(buf), 0);
5787dd7cddfSDavid du Colombier 		buf[n] = 0;
5797dd7cddfSDavid du Colombier 		for(p = buf; *p == ' '; p++)
5807dd7cddfSDavid du Colombier 			;
5817dd7cddfSDavid du Colombier 		snprint(path, sizeof(path), "%s/%lud/data", dev, strtoul(p, 0, 0));
5827dd7cddfSDavid du Colombier 
5837dd7cddfSDavid du Colombier 		// setup connection to be promiscuous
5847dd7cddfSDavid du Colombier 		snprint(buf, sizeof(buf), "connect -1");
5857dd7cddfSDavid du Colombier 		devtab[ctl->type]->write(ctl, buf, strlen(buf), 0);
5867dd7cddfSDavid du Colombier 		snprint(buf, sizeof(buf), "promiscuous");
5877dd7cddfSDavid du Colombier 		devtab[ctl->type]->write(ctl, buf, strlen(buf), 0);
5887dd7cddfSDavid du Colombier 		snprint(buf, sizeof(buf), "bridge");
5897dd7cddfSDavid du Colombier 		devtab[ctl->type]->write(ctl, buf, strlen(buf), 0);
5907dd7cddfSDavid du Colombier 
5917dd7cddfSDavid du Colombier 		// open data port
5927dd7cddfSDavid du Colombier 		port->data[0] = namec(path, Aopen, ORDWR, 0);
5937dd7cddfSDavid du Colombier 		// dup it
5947dd7cddfSDavid du Colombier 		incref(port->data[0]);
5957dd7cddfSDavid du Colombier 		port->data[1] = port->data[0];
5967dd7cddfSDavid du Colombier 
5977dd7cddfSDavid du Colombier 		poperror();
5987dd7cddfSDavid du Colombier 		cclose(ctl);
5997dd7cddfSDavid du Colombier 
6007dd7cddfSDavid du Colombier 		break;
6017dd7cddfSDavid du Colombier 	case Ttun:
6027dd7cddfSDavid du Colombier 		port->data[0] = namec(dev, Aopen, OREAD, 0);
6037dd7cddfSDavid du Colombier 		port->data[1] = namec(dev2, Aopen, OWRITE, 0);
6047dd7cddfSDavid du Colombier 		break;
6057dd7cddfSDavid du Colombier 	}
6067dd7cddfSDavid du Colombier 
6077dd7cddfSDavid du Colombier 	poperror();
6087dd7cddfSDavid du Colombier 
609ac020a8fSDavid du Colombier 	/* committed to binding port */
6107dd7cddfSDavid du Colombier 	b->port[port->id] = port;
6117dd7cddfSDavid du Colombier 	port->bridge = b;
6127dd7cddfSDavid du Colombier 	if(b->nport <= port->id)
6137dd7cddfSDavid du Colombier 		b->nport = port->id+1;
6147dd7cddfSDavid du Colombier 
6157dd7cddfSDavid du Colombier 	// assumes kproc always succeeds
6167dd7cddfSDavid du Colombier 	kproc("etherread", etherread, port);	// poperror must be next
6177dd7cddfSDavid du Colombier 	port->ref++;
6187dd7cddfSDavid du Colombier }
6197dd7cddfSDavid du Colombier 
6207dd7cddfSDavid du Colombier // assumes b is locked
6217dd7cddfSDavid du Colombier static void
portunbind(Bridge * b,int argc,char * argv[])6227dd7cddfSDavid du Colombier portunbind(Bridge *b, int argc, char *argv[])
6237dd7cddfSDavid du Colombier {
6247dd7cddfSDavid du Colombier 	int type = 0, i;
6259a747e4fSDavid du Colombier 	char name[KNAMELEN];
6267dd7cddfSDavid du Colombier 	ulong ownhash;
627ac020a8fSDavid du Colombier 	Port *port = nil;
628ac020a8fSDavid du Colombier 	static char usage[] = "usage: unbind ether|tunnel addr [ownhash]";
6297dd7cddfSDavid du Colombier 
6309a747e4fSDavid du Colombier 	memset(name, 0, KNAMELEN);
6317dd7cddfSDavid du Colombier 	if(argc < 2 || argc > 3)
6327dd7cddfSDavid du Colombier 		error(usage);
6337dd7cddfSDavid du Colombier 	if(strcmp(argv[0], "ether") == 0) {
6347dd7cddfSDavid du Colombier 		type = Tether;
6359a747e4fSDavid du Colombier 		strncpy(name, argv[1], KNAMELEN);
6369a747e4fSDavid du Colombier 		name[KNAMELEN-1] = 0;
6377dd7cddfSDavid du Colombier //		parseaddr(addr, argv[1], Eaddrlen);
6387dd7cddfSDavid du Colombier 	} else if(strcmp(argv[0], "tunnel") == 0) {
6397dd7cddfSDavid du Colombier 		type = Ttun;
6409a747e4fSDavid du Colombier 		strncpy(name, argv[1], KNAMELEN);
6419a747e4fSDavid du Colombier 		name[KNAMELEN-1] = 0;
6427dd7cddfSDavid du Colombier //		parseip(addr, argv[1]);
6437dd7cddfSDavid du Colombier 	} else
6447dd7cddfSDavid du Colombier 		error(usage);
6457dd7cddfSDavid du Colombier 	if(argc == 3)
6467dd7cddfSDavid du Colombier 		ownhash = atoi(argv[2]);
6477dd7cddfSDavid du Colombier 	else
6487dd7cddfSDavid du Colombier 		ownhash = 0;
6497dd7cddfSDavid du Colombier 	for(i=0; i<b->nport; i++) {
6507dd7cddfSDavid du Colombier 		port = b->port[i];
651ac020a8fSDavid du Colombier 		if(port != nil && port->type == type &&
652ac020a8fSDavid du Colombier 		    memcmp(port->name, name, KNAMELEN) == 0)
6537dd7cddfSDavid du Colombier 			break;
6547dd7cddfSDavid du Colombier 	}
6557dd7cddfSDavid du Colombier 	if(i == b->nport)
6567dd7cddfSDavid du Colombier 		error("port not found");
6577dd7cddfSDavid du Colombier 	if(ownhash != 0 && port->ownhash != 0 && ownhash != port->ownhash)
6587dd7cddfSDavid du Colombier 		error("bad owner hash");
6597dd7cddfSDavid du Colombier 
6607dd7cddfSDavid du Colombier 	port->closed = 1;
6617dd7cddfSDavid du Colombier 	b->port[i] = nil;	// port is now unbound
6627dd7cddfSDavid du Colombier 	cacheflushport(b, i);
6637dd7cddfSDavid du Colombier 
6647dd7cddfSDavid du Colombier 	// try and stop reader
6657dd7cddfSDavid du Colombier 	if(port->readp)
6667dd7cddfSDavid du Colombier 		postnote(port->readp, 1, "unbind", 0);
6677dd7cddfSDavid du Colombier 	portfree(port);
6687dd7cddfSDavid du Colombier }
6697dd7cddfSDavid du Colombier 
6707dd7cddfSDavid du Colombier // assumes b is locked
6717dd7cddfSDavid du Colombier static Centry *
cachelookup(Bridge * b,uchar d[Eaddrlen])6727dd7cddfSDavid du Colombier cachelookup(Bridge *b, uchar d[Eaddrlen])
6737dd7cddfSDavid du Colombier {
6747dd7cddfSDavid du Colombier 	int i;
6757dd7cddfSDavid du Colombier 	uint h;
6767dd7cddfSDavid du Colombier 	Centry *p;
6777dd7cddfSDavid du Colombier 	long sec;
6787dd7cddfSDavid du Colombier 
6797dd7cddfSDavid du Colombier 	// dont cache multicast or broadcast
6807dd7cddfSDavid du Colombier 	if(d[0] & 1)
6817dd7cddfSDavid du Colombier 		return 0;
6827dd7cddfSDavid du Colombier 
6837dd7cddfSDavid du Colombier 	h = 0;
6847dd7cddfSDavid du Colombier 	for(i=0; i<Eaddrlen; i++) {
6857dd7cddfSDavid du Colombier 		h *= 7;
6867dd7cddfSDavid du Colombier 		h += d[i];
6877dd7cddfSDavid du Colombier 	}
6887dd7cddfSDavid du Colombier 	h %= CacheHash;
6897dd7cddfSDavid du Colombier 	p = b->cache + h;
6907dd7cddfSDavid du Colombier 	sec = TK2SEC(m->ticks);
6917dd7cddfSDavid du Colombier 	for(i=0; i<CacheLook; i++,p++) {
6927dd7cddfSDavid du Colombier 		if(memcmp(d, p->d, Eaddrlen) == 0) {
6937dd7cddfSDavid du Colombier 			p->dst++;
6947dd7cddfSDavid du Colombier 			if(sec >= p->expire) {
6957dd7cddfSDavid du Colombier 				log(b, Logcache, "expired cache entry: %E %d\n",
6967dd7cddfSDavid du Colombier 					d, p->port);
6977dd7cddfSDavid du Colombier 				return nil;
6987dd7cddfSDavid du Colombier 			}
6997dd7cddfSDavid du Colombier 			p->expire = sec + CacheTimeout;
7007dd7cddfSDavid du Colombier 			return p;
7017dd7cddfSDavid du Colombier 		}
7027dd7cddfSDavid du Colombier 	}
7037dd7cddfSDavid du Colombier 	log(b, Logcache, "cache miss: %E\n", d);
7047dd7cddfSDavid du Colombier 	return nil;
7057dd7cddfSDavid du Colombier }
7067dd7cddfSDavid du Colombier 
7077dd7cddfSDavid du Colombier // assumes b is locked
7087dd7cddfSDavid du Colombier static void
cacheupdate(Bridge * b,uchar d[Eaddrlen],int port)7097dd7cddfSDavid du Colombier cacheupdate(Bridge *b, uchar d[Eaddrlen], int port)
7107dd7cddfSDavid du Colombier {
7117dd7cddfSDavid du Colombier 	int i;
7127dd7cddfSDavid du Colombier 	uint h;
7137dd7cddfSDavid du Colombier 	Centry *p, *pp;
7147dd7cddfSDavid du Colombier 	long sec;
7157dd7cddfSDavid du Colombier 
7167dd7cddfSDavid du Colombier 	// dont cache multicast or broadcast
7177dd7cddfSDavid du Colombier 	if(d[0] & 1) {
7187dd7cddfSDavid du Colombier 		log(b, Logcache, "bad source address: %E\n", d);
7197dd7cddfSDavid du Colombier 		return;
7207dd7cddfSDavid du Colombier 	}
7217dd7cddfSDavid du Colombier 
7227dd7cddfSDavid du Colombier 	h = 0;
7237dd7cddfSDavid du Colombier 	for(i=0; i<Eaddrlen; i++) {
7247dd7cddfSDavid du Colombier 		h *= 7;
7257dd7cddfSDavid du Colombier 		h += d[i];
7267dd7cddfSDavid du Colombier 	}
7277dd7cddfSDavid du Colombier 	h %= CacheHash;
7287dd7cddfSDavid du Colombier 	p = b->cache + h;
7297dd7cddfSDavid du Colombier 	pp = p;
7307dd7cddfSDavid du Colombier 	sec = p->expire;
7317dd7cddfSDavid du Colombier 
7327dd7cddfSDavid du Colombier 	// look for oldest entry
7337dd7cddfSDavid du Colombier 	for(i=0; i<CacheLook; i++,p++) {
7347dd7cddfSDavid du Colombier 		if(memcmp(p->d, d, Eaddrlen) == 0) {
7357dd7cddfSDavid du Colombier 			p->expire = TK2SEC(m->ticks) + CacheTimeout;
7367dd7cddfSDavid du Colombier 			if(p->port != port) {
7377dd7cddfSDavid du Colombier 				log(b, Logcache, "NIC changed port %d->%d: %E\n",
7387dd7cddfSDavid du Colombier 					p->port, port, d);
7397dd7cddfSDavid du Colombier 				p->port = port;
7407dd7cddfSDavid du Colombier 			}
7417dd7cddfSDavid du Colombier 			p->src++;
7427dd7cddfSDavid du Colombier 			return;
7437dd7cddfSDavid du Colombier 		}
7447dd7cddfSDavid du Colombier 		if(p->expire < sec) {
7457dd7cddfSDavid du Colombier 			sec = p->expire;
7467dd7cddfSDavid du Colombier 			pp = p;
7477dd7cddfSDavid du Colombier 		}
7487dd7cddfSDavid du Colombier 	}
7497dd7cddfSDavid du Colombier 	if(pp->expire != 0)
7507dd7cddfSDavid du Colombier 		log(b, Logcache, "bumping from cache: %E %d\n", pp->d, pp->port);
7517dd7cddfSDavid du Colombier 	pp->expire = TK2SEC(m->ticks) + CacheTimeout;
7527dd7cddfSDavid du Colombier 	memmove(pp->d, d, Eaddrlen);
7537dd7cddfSDavid du Colombier 	pp->port = port;
7547dd7cddfSDavid du Colombier 	pp->src = 1;
7557dd7cddfSDavid du Colombier 	pp->dst = 0;
7567dd7cddfSDavid du Colombier 	log(b, Logcache, "adding to cache: %E %d\n", pp->d, pp->port);
7577dd7cddfSDavid du Colombier }
7587dd7cddfSDavid du Colombier 
7597dd7cddfSDavid du Colombier // assumes b is locked
7607dd7cddfSDavid du Colombier static void
cacheflushport(Bridge * b,int port)7617dd7cddfSDavid du Colombier cacheflushport(Bridge *b, int port)
7627dd7cddfSDavid du Colombier {
7637dd7cddfSDavid du Colombier 	Centry *ce;
7647dd7cddfSDavid du Colombier 	int i;
7657dd7cddfSDavid du Colombier 
7667dd7cddfSDavid du Colombier 	ce = b->cache;
7677dd7cddfSDavid du Colombier 	for(i=0; i<CacheSize; i++,ce++) {
7687dd7cddfSDavid du Colombier 		if(ce->port != port)
7697dd7cddfSDavid du Colombier 			continue;
7707dd7cddfSDavid du Colombier 		memset(ce, 0, sizeof(Centry));
7717dd7cddfSDavid du Colombier 	}
7727dd7cddfSDavid du Colombier }
7737dd7cddfSDavid du Colombier 
7747dd7cddfSDavid du Colombier static char *
cachedump(Bridge * b)7757dd7cddfSDavid du Colombier cachedump(Bridge *b)
7767dd7cddfSDavid du Colombier {
7777dd7cddfSDavid du Colombier 	int i, n;
7787dd7cddfSDavid du Colombier 	long sec, off;
7797dd7cddfSDavid du Colombier 	char *buf, *p, *ep;
7807dd7cddfSDavid du Colombier 	Centry *ce;
7817dd7cddfSDavid du Colombier 	char c;
7827dd7cddfSDavid du Colombier 
7837dd7cddfSDavid du Colombier 	qlock(b);
7847dd7cddfSDavid du Colombier 	if(waserror()) {
7857dd7cddfSDavid du Colombier 		qunlock(b);
7867dd7cddfSDavid du Colombier 		nexterror();
7877dd7cddfSDavid du Colombier 	}
7887dd7cddfSDavid du Colombier 	sec = TK2SEC(m->ticks);
7897dd7cddfSDavid du Colombier 	n = 0;
7907dd7cddfSDavid du Colombier 	for(i=0; i<CacheSize; i++)
7917dd7cddfSDavid du Colombier 		if(b->cache[i].expire != 0)
7927dd7cddfSDavid du Colombier 			n++;
7937dd7cddfSDavid du Colombier 
7947dd7cddfSDavid du Colombier 	n *= 51;	// change if print format is changed
7957dd7cddfSDavid du Colombier 	n += 10;	// some slop at the end
7967dd7cddfSDavid du Colombier 	buf = malloc(n);
797*aa72973aSDavid du Colombier 	if(buf == nil)
798*aa72973aSDavid du Colombier 		error(Enomem);
7997dd7cddfSDavid du Colombier 	p = buf;
8007dd7cddfSDavid du Colombier 	ep = buf + n;
8017dd7cddfSDavid du Colombier 	ce = b->cache;
8027dd7cddfSDavid du Colombier 	off = seconds() - sec;
8037dd7cddfSDavid du Colombier 	for(i=0; i<CacheSize; i++,ce++) {
8047dd7cddfSDavid du Colombier 		if(ce->expire == 0)
8057dd7cddfSDavid du Colombier 			continue;
8067dd7cddfSDavid du Colombier 		c = (sec < ce->expire)?'v':'e';
8077dd7cddfSDavid du Colombier 		p += snprint(p, ep-p, "%E %2d %10ld %10ld %10ld %c\n", ce->d,
8087dd7cddfSDavid du Colombier 			ce->port, ce->src, ce->dst, ce->expire+off, c);
8097dd7cddfSDavid du Colombier 	}
8107dd7cddfSDavid du Colombier 	*p = 0;
8117dd7cddfSDavid du Colombier 	poperror();
8127dd7cddfSDavid du Colombier 	qunlock(b);
8137dd7cddfSDavid du Colombier 
8147dd7cddfSDavid du Colombier 	return buf;
8157dd7cddfSDavid du Colombier }
8167dd7cddfSDavid du Colombier 
8177dd7cddfSDavid du Colombier 
8187dd7cddfSDavid du Colombier 
8197dd7cddfSDavid du Colombier // assumes b is locked
8207dd7cddfSDavid du Colombier static void
ethermultiwrite(Bridge * b,Block * bp,Port * port)8217dd7cddfSDavid du Colombier ethermultiwrite(Bridge *b, Block *bp, Port *port)
8227dd7cddfSDavid du Colombier {
8237dd7cddfSDavid du Colombier 	Port *oport;
8247dd7cddfSDavid du Colombier 	Block *bp2;
8257dd7cddfSDavid du Colombier 	Etherpkt *ep;
8266c0968e3SDavid du Colombier 	int i, mcast;
8277dd7cddfSDavid du Colombier 
8287dd7cddfSDavid du Colombier 	if(waserror()) {
8297dd7cddfSDavid du Colombier 		if(bp)
8307dd7cddfSDavid du Colombier 			freeb(bp);
8317dd7cddfSDavid du Colombier 		nexterror();
8327dd7cddfSDavid du Colombier 	}
8337dd7cddfSDavid du Colombier 
8347dd7cddfSDavid du Colombier 	ep = (Etherpkt*)bp->rp;
8356c0968e3SDavid du Colombier 	mcast = ep->d[0] & 1;		/* multicast bit of ethernet address */
8367dd7cddfSDavid du Colombier 
8377dd7cddfSDavid du Colombier 	oport = nil;
8387dd7cddfSDavid du Colombier 	for(i=0; i<b->nport; i++) {
8397dd7cddfSDavid du Colombier 		if(i == port->id || b->port[i] == nil)
8407dd7cddfSDavid du Colombier 			continue;
8416c0968e3SDavid du Colombier 		/*
8426c0968e3SDavid du Colombier 		 * we need to forward multicast packets for ipv6,
8436c0968e3SDavid du Colombier 		 * so always do it.
8446c0968e3SDavid du Colombier 		 */
8457dd7cddfSDavid du Colombier 		if(mcast)
8467dd7cddfSDavid du Colombier 			b->port[i]->outmulti++;
8477dd7cddfSDavid du Colombier 		else
8487dd7cddfSDavid du Colombier 			b->port[i]->outunknown++;
8497dd7cddfSDavid du Colombier 
8507dd7cddfSDavid du Colombier 		// delay one so that the last write does not copy
8517dd7cddfSDavid du Colombier 		if(oport != nil) {
8527dd7cddfSDavid du Colombier 			b->copy++;
8537dd7cddfSDavid du Colombier 			bp2 = copyblock(bp, blocklen(bp));
8547dd7cddfSDavid du Colombier 			if(!waserror()) {
8557dd7cddfSDavid du Colombier 				etherwrite(oport, bp2);
8567dd7cddfSDavid du Colombier 				poperror();
8577dd7cddfSDavid du Colombier 			}
8587dd7cddfSDavid du Colombier 		}
8597dd7cddfSDavid du Colombier 		oport = b->port[i];
8607dd7cddfSDavid du Colombier 	}
8617dd7cddfSDavid du Colombier 
8627dd7cddfSDavid du Colombier 	// last write free block
8637dd7cddfSDavid du Colombier 	if(oport) {
8647dd7cddfSDavid du Colombier 		bp2 = bp; bp = nil; USED(bp);
8657dd7cddfSDavid du Colombier 		if(!waserror()) {
8667dd7cddfSDavid du Colombier 			etherwrite(oport, bp2);
8677dd7cddfSDavid du Colombier 			poperror();
8687dd7cddfSDavid du Colombier 		}
8697dd7cddfSDavid du Colombier 	} else
8707dd7cddfSDavid du Colombier 		freeb(bp);
8717dd7cddfSDavid du Colombier 
8727dd7cddfSDavid du Colombier 	poperror();
8737dd7cddfSDavid du Colombier }
8747dd7cddfSDavid du Colombier 
8757dd7cddfSDavid du Colombier static void
tcpmsshack(Etherpkt * epkt,int n)8767dd7cddfSDavid du Colombier tcpmsshack(Etherpkt *epkt, int n)
8777dd7cddfSDavid du Colombier {
878ac020a8fSDavid du Colombier 	int hl, optlen;
8797dd7cddfSDavid du Colombier 	Iphdr *iphdr;
8807dd7cddfSDavid du Colombier 	Tcphdr *tcphdr;
881ac020a8fSDavid du Colombier 	ulong mss, cksum;
8827dd7cddfSDavid du Colombier 	uchar *optr;
8837dd7cddfSDavid du Colombier 
884ac020a8fSDavid du Colombier 	/* ignore non-ipv4 packets */
885ac020a8fSDavid du Colombier 	if(nhgets(epkt->type) != ETIP4)
8867dd7cddfSDavid du Colombier 		return;
8877dd7cddfSDavid du Colombier 	iphdr = (Iphdr*)(epkt->data);
8887dd7cddfSDavid du Colombier 	n -= ETHERHDRSIZE;
8897dd7cddfSDavid du Colombier 	if(n < IPHDR)
8907dd7cddfSDavid du Colombier 		return;
8917dd7cddfSDavid du Colombier 
892ac020a8fSDavid du Colombier 	/* ignore bad packets */
893ac020a8fSDavid du Colombier 	if(iphdr->vihl != (IP_VER4|IP_HLEN4)) {
8947dd7cddfSDavid du Colombier 		hl = (iphdr->vihl&0xF)<<2;
895ac020a8fSDavid du Colombier 		if((iphdr->vihl&0xF0) != IP_VER4 || hl < (IP_HLEN4<<2))
8967dd7cddfSDavid du Colombier 			return;
8977dd7cddfSDavid du Colombier 	} else
898ac020a8fSDavid du Colombier 		hl = IP_HLEN4<<2;
8997dd7cddfSDavid du Colombier 
900ac020a8fSDavid du Colombier 	/* ignore non-tcp packets */
9017dd7cddfSDavid du Colombier 	if(iphdr->proto != IP_TCPPROTO)
9027dd7cddfSDavid du Colombier 		return;
9037dd7cddfSDavid du Colombier 	n -= hl;
9047dd7cddfSDavid du Colombier 	if(n < sizeof(Tcphdr))
9057dd7cddfSDavid du Colombier 		return;
9067dd7cddfSDavid du Colombier 	tcphdr = (Tcphdr*)((uchar*)(iphdr) + hl);
9077dd7cddfSDavid du Colombier 	// MSS can only appear in SYN packet
9087dd7cddfSDavid du Colombier 	if(!(tcphdr->flag[1] & SYN))
9097dd7cddfSDavid du Colombier 		return;
9107dd7cddfSDavid du Colombier 	hl = (tcphdr->flag[0] & 0xf0)>>2;
9117dd7cddfSDavid du Colombier 	if(n < hl)
9127dd7cddfSDavid du Colombier 		return;
9137dd7cddfSDavid du Colombier 
9147dd7cddfSDavid du Colombier 	// check for MSS option
915ac020a8fSDavid du Colombier 	optr = (uchar*)tcphdr + sizeof(Tcphdr);
9167dd7cddfSDavid du Colombier 	n = hl - sizeof(Tcphdr);
9177dd7cddfSDavid du Colombier 	for(;;) {
9187dd7cddfSDavid du Colombier 		if(n <= 0 || *optr == EOLOPT)
9197dd7cddfSDavid du Colombier 			return;
9207dd7cddfSDavid du Colombier 		if(*optr == NOOPOPT) {
9217dd7cddfSDavid du Colombier 			n--;
9227dd7cddfSDavid du Colombier 			optr++;
9237dd7cddfSDavid du Colombier 			continue;
9247dd7cddfSDavid du Colombier 		}
9257dd7cddfSDavid du Colombier 		optlen = optr[1];
9267dd7cddfSDavid du Colombier 		if(optlen < 2 || optlen > n)
9277dd7cddfSDavid du Colombier 			return;
9287dd7cddfSDavid du Colombier 		if(*optr == MSSOPT && optlen == MSS_LENGTH)
9297dd7cddfSDavid du Colombier 			break;
9307dd7cddfSDavid du Colombier 		n -= optlen;
9317dd7cddfSDavid du Colombier 		optr += optlen;
9327dd7cddfSDavid du Colombier 	}
9337dd7cddfSDavid du Colombier 
9347dd7cddfSDavid du Colombier 	mss = nhgets(optr+2);
9357dd7cddfSDavid du Colombier 	if(mss <= TcpMssMax)
9367dd7cddfSDavid du Colombier 		return;
9377dd7cddfSDavid du Colombier 	// fit checksum
9387dd7cddfSDavid du Colombier 	cksum = nhgets(tcphdr->cksum);
9397dd7cddfSDavid du Colombier 	if(optr-(uchar*)tcphdr & 1) {
9407dd7cddfSDavid du Colombier print("tcpmsshack: odd alignment!\n");
9417dd7cddfSDavid du Colombier 		// odd alignments are a pain
9427dd7cddfSDavid du Colombier 		cksum += nhgets(optr+1);
9437dd7cddfSDavid du Colombier 		cksum -= (optr[1]<<8)|(TcpMssMax>>8);
9447dd7cddfSDavid du Colombier 		cksum += (cksum>>16);
9457dd7cddfSDavid du Colombier 		cksum &= 0xffff;
9467dd7cddfSDavid du Colombier 		cksum += nhgets(optr+3);
9477dd7cddfSDavid du Colombier 		cksum -= ((TcpMssMax&0xff)<<8)|optr[4];
9487dd7cddfSDavid du Colombier 		cksum += (cksum>>16);
9497dd7cddfSDavid du Colombier 	} else {
9507dd7cddfSDavid du Colombier 		cksum += mss;
9517dd7cddfSDavid du Colombier 		cksum -= TcpMssMax;
9527dd7cddfSDavid du Colombier 		cksum += (cksum>>16);
9537dd7cddfSDavid du Colombier 	}
9547dd7cddfSDavid du Colombier 	hnputs(tcphdr->cksum, cksum);
9557dd7cddfSDavid du Colombier 	hnputs(optr+2, TcpMssMax);
9567dd7cddfSDavid du Colombier }
9577dd7cddfSDavid du Colombier 
9587dd7cddfSDavid du Colombier /*
9597dd7cddfSDavid du Colombier  *  process to read from the ethernet
9607dd7cddfSDavid du Colombier  */
9617dd7cddfSDavid du Colombier static void
etherread(void * a)9627dd7cddfSDavid du Colombier etherread(void *a)
9637dd7cddfSDavid du Colombier {
9647dd7cddfSDavid du Colombier 	Port *port = a;
9657dd7cddfSDavid du Colombier 	Bridge *b = port->bridge;
9667dd7cddfSDavid du Colombier 	Block *bp, *bp2;
9677dd7cddfSDavid du Colombier 	Etherpkt *ep;
9687dd7cddfSDavid du Colombier 	Centry *ce;
9697dd7cddfSDavid du Colombier 	long md;
9707dd7cddfSDavid du Colombier 
9717dd7cddfSDavid du Colombier 	qlock(b);
9727dd7cddfSDavid du Colombier 	port->readp = up;	/* hide identity under a rock for unbind */
9737dd7cddfSDavid du Colombier 
9747dd7cddfSDavid du Colombier 	while(!port->closed){
9757dd7cddfSDavid du Colombier 		// release lock to read - error means it is time to quit
9767dd7cddfSDavid du Colombier 		qunlock(b);
9777dd7cddfSDavid du Colombier 		if(waserror()) {
9783ff48bf5SDavid du Colombier 			print("etherread read error: %s\n", up->errstr);
9797dd7cddfSDavid du Colombier 			qlock(b);
9807dd7cddfSDavid du Colombier 			break;
9817dd7cddfSDavid du Colombier 		}
982ac020a8fSDavid du Colombier 		if(0)
983ac020a8fSDavid du Colombier 			print("devbridge: etherread: reading\n");
984ac020a8fSDavid du Colombier 		bp = devtab[port->data[0]->type]->bread(port->data[0],
985ac020a8fSDavid du Colombier 			ETHERMAXTU, 0);
986ac020a8fSDavid du Colombier 		if(0)
987ac020a8fSDavid du Colombier 			print("devbridge: etherread: blocklen = %d\n",
988ac020a8fSDavid du Colombier 				blocklen(bp));
9897dd7cddfSDavid du Colombier 		poperror();
9907dd7cddfSDavid du Colombier 		qlock(b);
9917dd7cddfSDavid du Colombier 		if(bp == nil || port->closed)
9927dd7cddfSDavid du Colombier 			break;
9937dd7cddfSDavid du Colombier 		if(waserror()) {
9947dd7cddfSDavid du Colombier //			print("etherread bridge error\n");
9957dd7cddfSDavid du Colombier 			if(bp)
9967dd7cddfSDavid du Colombier 				freeb(bp);
9977dd7cddfSDavid du Colombier 			continue;
9987dd7cddfSDavid du Colombier 		}
9997dd7cddfSDavid du Colombier 		if(blocklen(bp) < ETHERMINTU)
10007dd7cddfSDavid du Colombier 			error("short packet");
10017dd7cddfSDavid du Colombier 		port->in++;
10027dd7cddfSDavid du Colombier 
10037dd7cddfSDavid du Colombier 		ep = (Etherpkt*)bp->rp;
10047dd7cddfSDavid du Colombier 		cacheupdate(b, ep->s, port->id);
10057dd7cddfSDavid du Colombier 		if(b->tcpmss)
10067dd7cddfSDavid du Colombier 			tcpmsshack(ep, BLEN(bp));
10077dd7cddfSDavid du Colombier 
10087dd7cddfSDavid du Colombier 		/*
10097dd7cddfSDavid du Colombier 		 * delay packets to simulate a slow link
10107dd7cddfSDavid du Colombier 		 */
10117dd7cddfSDavid du Colombier 		if(b->delay0 || b->delayn){
10127dd7cddfSDavid du Colombier 			md = b->delay0 + b->delayn * BLEN(bp);
10137dd7cddfSDavid du Colombier 			if(md > 0)
10147dd7cddfSDavid du Colombier 				microdelay(md);
10157dd7cddfSDavid du Colombier 		}
10167dd7cddfSDavid du Colombier 
10177dd7cddfSDavid du Colombier 		if(ep->d[0] & 1) {
10186c0968e3SDavid du Colombier 			log(b, Logmcast, "multicast: port=%d src=%E dst=%E type=%#.4ux\n",
10196c0968e3SDavid du Colombier 				port->id, ep->s, ep->d, ep->type[0]<<8|ep->type[1]);
10207dd7cddfSDavid du Colombier 			port->inmulti++;
10217dd7cddfSDavid du Colombier 			bp2 = bp; bp = nil;
10227dd7cddfSDavid du Colombier 			ethermultiwrite(b, bp2, port);
10237dd7cddfSDavid du Colombier 		} else {
10247dd7cddfSDavid du Colombier 			ce = cachelookup(b, ep->d);
10257dd7cddfSDavid du Colombier 			if(ce == nil) {
10267dd7cddfSDavid du Colombier 				b->miss++;
10277dd7cddfSDavid du Colombier 				port->inunknown++;
10287dd7cddfSDavid du Colombier 				bp2 = bp; bp = nil;
10297dd7cddfSDavid du Colombier 				ethermultiwrite(b, bp2, port);
10307dd7cddfSDavid du Colombier 			}else if(ce->port != port->id){
10317dd7cddfSDavid du Colombier 				b->hit++;
10327dd7cddfSDavid du Colombier 				bp2 = bp; bp = nil;
10337dd7cddfSDavid du Colombier 				etherwrite(b->port[ce->port], bp2);
10347dd7cddfSDavid du Colombier 			}
10357dd7cddfSDavid du Colombier 		}
10367dd7cddfSDavid du Colombier 
10377dd7cddfSDavid du Colombier 		poperror();
10387dd7cddfSDavid du Colombier 		if(bp)
10397dd7cddfSDavid du Colombier 			freeb(bp);
10407dd7cddfSDavid du Colombier 	}
10417dd7cddfSDavid du Colombier //	print("etherread: trying to exit\n");
10427dd7cddfSDavid du Colombier 	port->readp = nil;
10437dd7cddfSDavid du Colombier 	portfree(port);
10447dd7cddfSDavid du Colombier 	qunlock(b);
10457dd7cddfSDavid du Colombier 	pexit("hangup", 1);
10467dd7cddfSDavid du Colombier }
10477dd7cddfSDavid du Colombier 
10487dd7cddfSDavid du Colombier static int
fragment(Etherpkt * epkt,int n)10497dd7cddfSDavid du Colombier fragment(Etherpkt *epkt, int n)
10507dd7cddfSDavid du Colombier {
10517dd7cddfSDavid du Colombier 	Iphdr *iphdr;
10527dd7cddfSDavid du Colombier 
10537dd7cddfSDavid du Colombier 	if(n <= TunnelMtu)
10547dd7cddfSDavid du Colombier 		return 0;
10557dd7cddfSDavid du Colombier 
1056ac020a8fSDavid du Colombier 	/* ignore non-ipv4 packets */
1057ac020a8fSDavid du Colombier 	if(nhgets(epkt->type) != ETIP4)
10587dd7cddfSDavid du Colombier 		return 0;
10597dd7cddfSDavid du Colombier 	iphdr = (Iphdr*)(epkt->data);
10607dd7cddfSDavid du Colombier 	n -= ETHERHDRSIZE;
1061ac020a8fSDavid du Colombier 	/*
1062ac020a8fSDavid du Colombier 	 * ignore: IP runt packets, bad packets (I don't handle IP
1063ac020a8fSDavid du Colombier 	 * options for the moment), packets with don't-fragment set,
1064ac020a8fSDavid du Colombier 	 * and short blocks.
1065ac020a8fSDavid du Colombier 	 */
1066ac020a8fSDavid du Colombier 	if(n < IPHDR || iphdr->vihl != (IP_VER4|IP_HLEN4) ||
1067ac020a8fSDavid du Colombier 	    iphdr->frag[0] & (IP_DF>>8) || nhgets(iphdr->length) > n)
10687dd7cddfSDavid du Colombier 		return 0;
10697dd7cddfSDavid du Colombier 
10707dd7cddfSDavid du Colombier 	return 1;
10717dd7cddfSDavid du Colombier }
10727dd7cddfSDavid du Colombier 
10737dd7cddfSDavid du Colombier 
10747dd7cddfSDavid du Colombier static void
etherwrite(Port * port,Block * bp)10757dd7cddfSDavid du Colombier etherwrite(Port *port, Block *bp)
10767dd7cddfSDavid du Colombier {
10777dd7cddfSDavid du Colombier 	Iphdr *eh, *feh;
10787dd7cddfSDavid du Colombier 	Etherpkt *epkt;
10797dd7cddfSDavid du Colombier 	int n, lid, len, seglen, chunk, dlen, blklen, offset, mf;
10807dd7cddfSDavid du Colombier 	Block *xp, *nb;
10817dd7cddfSDavid du Colombier 	ushort fragoff, frag;
10827dd7cddfSDavid du Colombier 
10837dd7cddfSDavid du Colombier 	port->out++;
10847dd7cddfSDavid du Colombier 	epkt = (Etherpkt*)bp->rp;
10857dd7cddfSDavid du Colombier 	n = blocklen(bp);
10867dd7cddfSDavid du Colombier 	if(port->type != Ttun || !fragment(epkt, n)) {
10877dd7cddfSDavid du Colombier 		devtab[port->data[1]->type]->bwrite(port->data[1], bp, 0);
10887dd7cddfSDavid du Colombier 		return;
10897dd7cddfSDavid du Colombier 	}
10907dd7cddfSDavid du Colombier 	port->outfrag++;
10917dd7cddfSDavid du Colombier 	if(waserror()){
10927dd7cddfSDavid du Colombier 		freeblist(bp);
10937dd7cddfSDavid du Colombier 		nexterror();
10947dd7cddfSDavid du Colombier 	}
10957dd7cddfSDavid du Colombier 
10967dd7cddfSDavid du Colombier 	seglen = (TunnelMtu - ETHERHDRSIZE - IPHDR) & ~7;
10977dd7cddfSDavid du Colombier 	eh = (Iphdr*)(epkt->data);
10987dd7cddfSDavid du Colombier 	len = nhgets(eh->length);
10997dd7cddfSDavid du Colombier 	frag = nhgets(eh->frag);
11007dd7cddfSDavid du Colombier 	mf = frag & IP_MF;
11017dd7cddfSDavid du Colombier 	frag <<= 3;
11027dd7cddfSDavid du Colombier 	dlen = len - IPHDR;
11037dd7cddfSDavid du Colombier 	xp = bp;
11047dd7cddfSDavid du Colombier 	lid = nhgets(eh->id);
11057dd7cddfSDavid du Colombier 	offset = ETHERHDRSIZE+IPHDR;
11067dd7cddfSDavid du Colombier 	while(xp != nil && offset && offset >= BLEN(xp)) {
11077dd7cddfSDavid du Colombier 		offset -= BLEN(xp);
11087dd7cddfSDavid du Colombier 		xp = xp->next;
11097dd7cddfSDavid du Colombier 	}
11107dd7cddfSDavid du Colombier 	xp->rp += offset;
11117dd7cddfSDavid du Colombier 
1112ac020a8fSDavid du Colombier 	if(0)
1113ac020a8fSDavid du Colombier 		print("seglen=%d, dlen=%d, mf=%x, frag=%d\n",
1114ac020a8fSDavid du Colombier 			seglen, dlen, mf, frag);
11157dd7cddfSDavid du Colombier 	for(fragoff = 0; fragoff < dlen; fragoff += seglen) {
11167dd7cddfSDavid du Colombier 		nb = allocb(ETHERHDRSIZE+IPHDR+seglen);
11177dd7cddfSDavid du Colombier 
11187dd7cddfSDavid du Colombier 		feh = (Iphdr*)(nb->wp+ETHERHDRSIZE);
11197dd7cddfSDavid du Colombier 
11207dd7cddfSDavid du Colombier 		memmove(nb->wp, epkt, ETHERHDRSIZE+IPHDR);
11217dd7cddfSDavid du Colombier 		nb->wp += ETHERHDRSIZE+IPHDR;
11227dd7cddfSDavid du Colombier 
11237dd7cddfSDavid du Colombier 		if((fragoff + seglen) >= dlen) {
11247dd7cddfSDavid du Colombier 			seglen = dlen - fragoff;
11257dd7cddfSDavid du Colombier 			hnputs(feh->frag, (frag+fragoff)>>3 | mf);
11267dd7cddfSDavid du Colombier 		}
11277dd7cddfSDavid du Colombier 		else
11287dd7cddfSDavid du Colombier 			hnputs(feh->frag, (frag+fragoff>>3) | IP_MF);
11297dd7cddfSDavid du Colombier 
11307dd7cddfSDavid du Colombier 		hnputs(feh->length, seglen + IPHDR);
11317dd7cddfSDavid du Colombier 		hnputs(feh->id, lid);
11327dd7cddfSDavid du Colombier 
11337dd7cddfSDavid du Colombier 		/* Copy up the data area */
11347dd7cddfSDavid du Colombier 		chunk = seglen;
11357dd7cddfSDavid du Colombier 		while(chunk) {
11367dd7cddfSDavid du Colombier 			blklen = chunk;
11377dd7cddfSDavid du Colombier 			if(BLEN(xp) < chunk)
11387dd7cddfSDavid du Colombier 				blklen = BLEN(xp);
11397dd7cddfSDavid du Colombier 			memmove(nb->wp, xp->rp, blklen);
11407dd7cddfSDavid du Colombier 			nb->wp += blklen;
11417dd7cddfSDavid du Colombier 			xp->rp += blklen;
11427dd7cddfSDavid du Colombier 			chunk -= blklen;
11437dd7cddfSDavid du Colombier 			if(xp->rp == xp->wp)
11447dd7cddfSDavid du Colombier 				xp = xp->next;
11457dd7cddfSDavid du Colombier 		}
11467dd7cddfSDavid du Colombier 
11477dd7cddfSDavid du Colombier 		feh->cksum[0] = 0;
11487dd7cddfSDavid du Colombier 		feh->cksum[1] = 0;
11497dd7cddfSDavid du Colombier 		hnputs(feh->cksum, ipcsum(&feh->vihl));
11507dd7cddfSDavid du Colombier 
1151ac020a8fSDavid du Colombier 		/* don't generate small packets */
11527dd7cddfSDavid du Colombier 		if(BLEN(nb) < ETHERMINTU)
11537dd7cddfSDavid du Colombier 			nb->wp = nb->rp + ETHERMINTU;
11547dd7cddfSDavid du Colombier 		devtab[port->data[1]->type]->bwrite(port->data[1], nb, 0);
11557dd7cddfSDavid du Colombier 	}
11567dd7cddfSDavid du Colombier 	poperror();
11577dd7cddfSDavid du Colombier 	freeblist(bp);
11587dd7cddfSDavid du Colombier }
11597dd7cddfSDavid du Colombier 
11607dd7cddfSDavid du Colombier // hold b lock
11617dd7cddfSDavid du Colombier static void
portfree(Port * port)11627dd7cddfSDavid du Colombier portfree(Port *port)
11637dd7cddfSDavid du Colombier {
11647dd7cddfSDavid du Colombier 	port->ref--;
11657dd7cddfSDavid du Colombier 	if(port->ref < 0)
11667dd7cddfSDavid du Colombier 		panic("portfree: bad ref");
11677dd7cddfSDavid du Colombier 	if(port->ref > 0)
11687dd7cddfSDavid du Colombier 		return;
11697dd7cddfSDavid du Colombier 
11707dd7cddfSDavid du Colombier 	if(port->data[0])
11717dd7cddfSDavid du Colombier 		cclose(port->data[0]);
11727dd7cddfSDavid du Colombier 	if(port->data[1])
11737dd7cddfSDavid du Colombier 		cclose(port->data[1]);
11747dd7cddfSDavid du Colombier 	memset(port, 0, sizeof(Port));
11757dd7cddfSDavid du Colombier 	free(port);
11767dd7cddfSDavid du Colombier }
11777dd7cddfSDavid du Colombier 
11787dd7cddfSDavid du Colombier Dev bridgedevtab = {
11797dd7cddfSDavid du Colombier 	'B',
11807dd7cddfSDavid du Colombier 	"bridge",
11817dd7cddfSDavid du Colombier 
11827dd7cddfSDavid du Colombier 	devreset,
11837dd7cddfSDavid du Colombier 	bridgeinit,
11849a747e4fSDavid du Colombier 	devshutdown,
11857dd7cddfSDavid du Colombier 	bridgeattach,
11867dd7cddfSDavid du Colombier 	bridgewalk,
11877dd7cddfSDavid du Colombier 	bridgestat,
11887dd7cddfSDavid du Colombier 	bridgeopen,
11897dd7cddfSDavid du Colombier 	devcreate,
11907dd7cddfSDavid du Colombier 	bridgeclose,
11917dd7cddfSDavid du Colombier 	bridgeread,
11927dd7cddfSDavid du Colombier 	devbread,
11937dd7cddfSDavid du Colombier 	bridgewrite,
11947dd7cddfSDavid du Colombier 	devbwrite,
11957dd7cddfSDavid du Colombier 	devremove,
11967dd7cddfSDavid du Colombier 	devwstat,
11977dd7cddfSDavid du Colombier };
1198