xref: /plan9-contrib/sys/src/9/port/devbridge.c (revision a129eb931c3e454c10d1dd5b175b8baa61325928)
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
42*a129eb93SDavid du Colombier 	MaxMTU=	IP_MAX,	// allow for jumbo frames and large UDP
437dd7cddfSDavid du Colombier 
447dd7cddfSDavid du Colombier 	TcpMssMax = 1300,		// max desirable Tcp MSS value
457dd7cddfSDavid du Colombier 	TunnelMtu = 1400,
467dd7cddfSDavid du Colombier };
477dd7cddfSDavid du Colombier 
487dd7cddfSDavid du Colombier static Dirtab bridgedirtab[]={
497dd7cddfSDavid du Colombier 	"ctl",		{Qbctl},	0,	0666,
507dd7cddfSDavid du Colombier 	"stats",	{Qstats},	0,	0444,
517dd7cddfSDavid du Colombier 	"cache",	{Qcache},	0,	0444,
527dd7cddfSDavid du Colombier 	"log",		{Qlog},		0,	0666,
537dd7cddfSDavid du Colombier };
547dd7cddfSDavid du Colombier 
557dd7cddfSDavid du Colombier static Dirtab portdirtab[]={
567dd7cddfSDavid du Colombier 	"ctl",		{Qpctl},	0,	0666,
577dd7cddfSDavid du Colombier 	"local",	{Qlocal},	0,	0444,
587dd7cddfSDavid du Colombier 	"status",	{Qstatus},	0,	0444,
597dd7cddfSDavid du Colombier };
607dd7cddfSDavid du Colombier 
617dd7cddfSDavid du Colombier enum {
627dd7cddfSDavid du Colombier 	Logcache=	(1<<0),
637dd7cddfSDavid du Colombier 	Logmcast=	(1<<1),
647dd7cddfSDavid du Colombier };
657dd7cddfSDavid du Colombier 
667dd7cddfSDavid du Colombier // types of interfaces
677dd7cddfSDavid du Colombier enum
687dd7cddfSDavid du Colombier {
697dd7cddfSDavid du Colombier 	Tether,
707dd7cddfSDavid du Colombier 	Ttun,
717dd7cddfSDavid du Colombier };
727dd7cddfSDavid du Colombier 
737dd7cddfSDavid du Colombier static Logflag logflags[] =
747dd7cddfSDavid du Colombier {
757dd7cddfSDavid du Colombier 	{ "cache",	Logcache, },
767dd7cddfSDavid du Colombier 	{ "multicast",	Logmcast, },
777dd7cddfSDavid du Colombier 	{ nil,		0, },
787dd7cddfSDavid du Colombier };
797dd7cddfSDavid du Colombier 
807dd7cddfSDavid du Colombier static Dirtab	*dirtab[MaxQ];
817dd7cddfSDavid du Colombier 
829a747e4fSDavid du Colombier #define TYPE(x) 	(((ulong)(x).path) & 0xff)
839a747e4fSDavid du Colombier #define PORT(x) 	((((ulong)(x).path) >> 8)&(Maxport-1))
847dd7cddfSDavid du Colombier #define QID(x, y) 	(((x)<<8) | (y))
857dd7cddfSDavid du Colombier 
867dd7cddfSDavid du Colombier struct Centry
877dd7cddfSDavid du Colombier {
887dd7cddfSDavid du Colombier 	uchar	d[Eaddrlen];
897dd7cddfSDavid du Colombier 	int	port;
90ac020a8fSDavid du Colombier 	long	expire;		// entry expires this many seconds after bootime
917dd7cddfSDavid du Colombier 	long	src;
927dd7cddfSDavid du Colombier 	long	dst;
937dd7cddfSDavid du Colombier };
947dd7cddfSDavid du Colombier 
957dd7cddfSDavid du Colombier struct Bridge
967dd7cddfSDavid du Colombier {
977dd7cddfSDavid du Colombier 	QLock;
987dd7cddfSDavid du Colombier 	int	nport;
997dd7cddfSDavid du Colombier 	Port	*port[Maxport];
1007dd7cddfSDavid du Colombier 	Centry	cache[CacheSize];
1017dd7cddfSDavid du Colombier 	ulong	hit;
1027dd7cddfSDavid du Colombier 	ulong	miss;
1037dd7cddfSDavid du Colombier 	ulong	copy;
1047dd7cddfSDavid du Colombier 	long	delay0;		// constant microsecond delay per packet
1057dd7cddfSDavid du Colombier 	long	delayn;		// microsecond delay per byte
1067dd7cddfSDavid du Colombier 	int	tcpmss;		// modify tcpmss value
1077dd7cddfSDavid du Colombier 
1087dd7cddfSDavid du Colombier 	Log;
1097dd7cddfSDavid du Colombier };
1107dd7cddfSDavid du Colombier 
1117dd7cddfSDavid du Colombier struct Port
1127dd7cddfSDavid du Colombier {
113*a129eb93SDavid du Colombier 	Ref;
1147dd7cddfSDavid du Colombier 	int	id;
1157dd7cddfSDavid du Colombier 	Bridge	*bridge;
1167dd7cddfSDavid du Colombier 	int	closed;
1177dd7cddfSDavid du Colombier 
1187dd7cddfSDavid du Colombier 	Chan	*data[2];	// channel to data
1197dd7cddfSDavid du Colombier 
1207dd7cddfSDavid du Colombier 	Proc	*readp;		// read proc
1217dd7cddfSDavid du Colombier 
1227dd7cddfSDavid du Colombier 	// the following uniquely identifies the port
1237dd7cddfSDavid du Colombier 	int	type;
1249a747e4fSDavid du Colombier 	char	name[KNAMELEN];
1257dd7cddfSDavid du Colombier 
1267dd7cddfSDavid du Colombier 	// owner hash - avoids bind/unbind races
1277dd7cddfSDavid du Colombier 	ulong	ownhash;
1287dd7cddfSDavid du Colombier 
1297dd7cddfSDavid du Colombier 	// various stats
1307dd7cddfSDavid du Colombier 	int	in;		// number of packets read
1317dd7cddfSDavid du Colombier 	int	inmulti;	// multicast or broadcast
1327dd7cddfSDavid du Colombier 	int	inunknown;	// unknown address
1337dd7cddfSDavid du Colombier 	int	out;		// number of packets read
1347dd7cddfSDavid du Colombier 	int	outmulti;	// multicast or broadcast
1357dd7cddfSDavid du Colombier 	int	outunknown;	// unknown address
1367dd7cddfSDavid du Colombier 	int	outfrag;	// fragmented the packet
1377dd7cddfSDavid du Colombier 	int	nentry;		// number of cache entries for this port
1387dd7cddfSDavid du Colombier };
1397dd7cddfSDavid du Colombier 
1407dd7cddfSDavid du Colombier enum {
1417dd7cddfSDavid du Colombier 	IP_TCPPROTO	= 6,
1427dd7cddfSDavid du Colombier 	EOLOPT		= 0,
1437dd7cddfSDavid du Colombier 	NOOPOPT		= 1,
1447dd7cddfSDavid du Colombier 	MSSOPT		= 2,
1457dd7cddfSDavid du Colombier 	MSS_LENGTH	= 4,		/* Mean segment size */
1467dd7cddfSDavid du Colombier 	SYN		= 0x02,		/* Pkt. is synchronise */
1477dd7cddfSDavid du Colombier 	IPHDR		= 20,		/* sizeof(Iphdr) */
1487dd7cddfSDavid du Colombier };
1497dd7cddfSDavid du Colombier 
1507dd7cddfSDavid du Colombier struct Iphdr
1517dd7cddfSDavid du Colombier {
1527dd7cddfSDavid du Colombier 	uchar	vihl;		/* Version and header length */
1537dd7cddfSDavid du Colombier 	uchar	tos;		/* Type of service */
1547dd7cddfSDavid du Colombier 	uchar	length[2];	/* packet length */
1557dd7cddfSDavid du Colombier 	uchar	id[2];		/* ip->identification */
1567dd7cddfSDavid du Colombier 	uchar	frag[2];	/* Fragment information */
1577dd7cddfSDavid du Colombier 	uchar	ttl;		/* Time to live */
1587dd7cddfSDavid du Colombier 	uchar	proto;		/* Protocol */
1597dd7cddfSDavid du Colombier 	uchar	cksum[2];	/* Header checksum */
1607dd7cddfSDavid du Colombier 	uchar	src[4];		/* IP source */
1617dd7cddfSDavid du Colombier 	uchar	dst[4];		/* IP destination */
1627dd7cddfSDavid du Colombier };
1637dd7cddfSDavid du Colombier 
1647dd7cddfSDavid du Colombier struct Tcphdr
1657dd7cddfSDavid du Colombier {
1667dd7cddfSDavid du Colombier 	uchar	sport[2];
1677dd7cddfSDavid du Colombier 	uchar	dport[2];
1687dd7cddfSDavid du Colombier 	uchar	seq[4];
1697dd7cddfSDavid du Colombier 	uchar	ack[4];
1707dd7cddfSDavid du Colombier 	uchar	flag[2];
1717dd7cddfSDavid du Colombier 	uchar	win[2];
1727dd7cddfSDavid du Colombier 	uchar	cksum[2];
1737dd7cddfSDavid du Colombier 	uchar	urg[2];
1747dd7cddfSDavid du Colombier };
1757dd7cddfSDavid du Colombier 
1767dd7cddfSDavid du Colombier static Bridge bridgetab[Maxbridge];
1777dd7cddfSDavid du Colombier 
1787dd7cddfSDavid du Colombier static int m2p[] = {
1797dd7cddfSDavid du Colombier 	[OREAD]		4,
1807dd7cddfSDavid du Colombier 	[OWRITE]	2,
1817dd7cddfSDavid du Colombier 	[ORDWR]		6
1827dd7cddfSDavid du Colombier };
1837dd7cddfSDavid du Colombier 
1849a747e4fSDavid du Colombier static int	bridgegen(Chan *c, char*, Dirtab*, int, int s, Dir *dp);
1857dd7cddfSDavid du Colombier static void	portbind(Bridge *b, int argc, char *argv[]);
1867dd7cddfSDavid du Colombier static void	portunbind(Bridge *b, int argc, char *argv[]);
1877dd7cddfSDavid du Colombier static void	etherread(void *a);
1887dd7cddfSDavid du Colombier static char	*cachedump(Bridge *b);
1897dd7cddfSDavid du Colombier static void	portfree(Port *port);
1907dd7cddfSDavid du Colombier static void	cacheflushport(Bridge *b, int port);
1917dd7cddfSDavid du Colombier static void	etherwrite(Port *port, Block *bp);
1927dd7cddfSDavid du Colombier 
1937dd7cddfSDavid du Colombier static void
bridgeinit(void)1947dd7cddfSDavid du Colombier bridgeinit(void)
1957dd7cddfSDavid du Colombier {
1967dd7cddfSDavid du Colombier 	int i;
1977dd7cddfSDavid du Colombier 	Dirtab *dt;
198ac020a8fSDavid du Colombier 
1997dd7cddfSDavid du Colombier 	// setup dirtab with non directory entries
2007dd7cddfSDavid du Colombier 	for(i=0; i<nelem(bridgedirtab); i++) {
2017dd7cddfSDavid du Colombier 		dt = bridgedirtab + i;
2027dd7cddfSDavid du Colombier 		dirtab[TYPE(dt->qid)] = dt;
2037dd7cddfSDavid du Colombier 	}
2047dd7cddfSDavid du Colombier 	for(i=0; i<nelem(portdirtab); i++) {
2057dd7cddfSDavid du Colombier 		dt = portdirtab + i;
2067dd7cddfSDavid du Colombier 		dirtab[TYPE(dt->qid)] = dt;
2077dd7cddfSDavid du Colombier 	}
2087dd7cddfSDavid du Colombier }
2097dd7cddfSDavid du Colombier 
2107dd7cddfSDavid du Colombier static Chan*
bridgeattach(char * spec)2117dd7cddfSDavid du Colombier bridgeattach(char* spec)
2127dd7cddfSDavid du Colombier {
2137dd7cddfSDavid du Colombier 	Chan *c;
2147dd7cddfSDavid du Colombier 	int dev;
2157dd7cddfSDavid du Colombier 
2167dd7cddfSDavid du Colombier 	dev = atoi(spec);
2177dd7cddfSDavid du Colombier 	if(dev<0 || dev >= Maxbridge)
2187dd7cddfSDavid du Colombier 		error("bad specification");
2197dd7cddfSDavid du Colombier 
2207dd7cddfSDavid du Colombier 	c = devattach('B', spec);
2219a747e4fSDavid du Colombier 	mkqid(&c->qid, QID(0, Qtopdir), 0, QTDIR);
2227dd7cddfSDavid du Colombier 	c->dev = dev;
2237dd7cddfSDavid du Colombier 	return c;
2247dd7cddfSDavid du Colombier }
2257dd7cddfSDavid du Colombier 
2269a747e4fSDavid du Colombier static Walkqid*
bridgewalk(Chan * c,Chan * nc,char ** name,int nname)2279a747e4fSDavid du Colombier bridgewalk(Chan *c, Chan *nc, char **name, int nname)
2287dd7cddfSDavid du Colombier {
2299a747e4fSDavid du Colombier 	return devwalk(c, nc, name, nname, (Dirtab*)0, 0, bridgegen);
2307dd7cddfSDavid du Colombier }
2317dd7cddfSDavid du Colombier 
2329a747e4fSDavid du Colombier static int
bridgestat(Chan * c,uchar * db,int n)2339a747e4fSDavid du Colombier bridgestat(Chan* c, uchar* db, int n)
2347dd7cddfSDavid du Colombier {
2359a747e4fSDavid du Colombier 	return devstat(c, db, n, (Dirtab *)0, 0L, bridgegen);
2367dd7cddfSDavid du Colombier }
2377dd7cddfSDavid du Colombier 
2387dd7cddfSDavid du Colombier static Chan*
bridgeopen(Chan * c,int omode)2397dd7cddfSDavid du Colombier bridgeopen(Chan* c, int omode)
2407dd7cddfSDavid du Colombier {
2417dd7cddfSDavid du Colombier 	int perm;
2427dd7cddfSDavid du Colombier 	Bridge *b;
2437dd7cddfSDavid du Colombier 
2447dd7cddfSDavid du Colombier 	omode &= 3;
2457dd7cddfSDavid du Colombier 	perm = m2p[omode];
2467dd7cddfSDavid du Colombier 	USED(perm);
2477dd7cddfSDavid du Colombier 
2487dd7cddfSDavid du Colombier 	b = bridgetab + c->dev;
2497dd7cddfSDavid du Colombier 	USED(b);
2507dd7cddfSDavid du Colombier 
2517dd7cddfSDavid du Colombier 	switch(TYPE(c->qid)) {
2527dd7cddfSDavid du Colombier 	default:
2537dd7cddfSDavid du Colombier 		break;
2547dd7cddfSDavid du Colombier 	case Qlog:
2557dd7cddfSDavid du Colombier 		logopen(b);
2567dd7cddfSDavid du Colombier 		break;
2577dd7cddfSDavid du Colombier 	case Qcache:
2587dd7cddfSDavid du Colombier 		c->aux = cachedump(b);
2597dd7cddfSDavid du Colombier 		break;
2607dd7cddfSDavid du Colombier 	}
2617dd7cddfSDavid du Colombier 	c->mode = openmode(omode);
2627dd7cddfSDavid du Colombier 	c->flag |= COPEN;
2637dd7cddfSDavid du Colombier 	c->offset = 0;
2647dd7cddfSDavid du Colombier 	return c;
2657dd7cddfSDavid du Colombier }
2667dd7cddfSDavid du Colombier 
2677dd7cddfSDavid du Colombier static void
bridgeclose(Chan * c)2687dd7cddfSDavid du Colombier bridgeclose(Chan* c)
2697dd7cddfSDavid du Colombier {
2707dd7cddfSDavid du Colombier 	Bridge *b  = bridgetab + c->dev;
2717dd7cddfSDavid du Colombier 
2727dd7cddfSDavid du Colombier 	switch(TYPE(c->qid)) {
2737dd7cddfSDavid du Colombier 	case Qcache:
2747dd7cddfSDavid du Colombier 		if(c->flag & COPEN)
2757dd7cddfSDavid du Colombier 			free(c->aux);
2767dd7cddfSDavid du Colombier 		break;
2777dd7cddfSDavid du Colombier 	case Qlog:
2787dd7cddfSDavid du Colombier 		if(c->flag & COPEN)
2797dd7cddfSDavid du Colombier 			logclose(b);
2807dd7cddfSDavid du Colombier 		break;
2817dd7cddfSDavid du Colombier 	}
2827dd7cddfSDavid du Colombier }
2837dd7cddfSDavid du Colombier 
2847dd7cddfSDavid du Colombier static long
bridgeread(Chan * c,void * a,long n,vlong off)2857dd7cddfSDavid du Colombier bridgeread(Chan *c, void *a, long n, vlong off)
2867dd7cddfSDavid du Colombier {
2877dd7cddfSDavid du Colombier 	char buf[256];
2887dd7cddfSDavid du Colombier 	Bridge *b = bridgetab + c->dev;
2897dd7cddfSDavid du Colombier 	Port *port;
2907dd7cddfSDavid du Colombier 	int i, ingood, outgood;
2917dd7cddfSDavid du Colombier 
2927dd7cddfSDavid du Colombier 	USED(off);
2937dd7cddfSDavid du Colombier 	switch(TYPE(c->qid)) {
2947dd7cddfSDavid du Colombier 	default:
295*a129eb93SDavid du Colombier 		error(Egreg);
2967dd7cddfSDavid du Colombier 	case Qtopdir:
2977dd7cddfSDavid du Colombier 	case Qbridgedir:
2987dd7cddfSDavid du Colombier 	case Qportdir:
2997dd7cddfSDavid du Colombier 		return devdirread(c, a, n, 0, 0, bridgegen);
3007dd7cddfSDavid du Colombier 	case Qlog:
3017dd7cddfSDavid du Colombier 		return logread(b, a, off, n);
302*a129eb93SDavid du Colombier 	case Qlocal:
303*a129eb93SDavid du Colombier 		return 0;	/* TO DO */
3047dd7cddfSDavid du Colombier 	case Qstatus:
3057dd7cddfSDavid du Colombier 		qlock(b);
306*a129eb93SDavid du Colombier 		if(waserror()){
307*a129eb93SDavid du Colombier 			qunlock(b);
308*a129eb93SDavid du Colombier 			nexterror();
309*a129eb93SDavid du Colombier 		}
3107dd7cddfSDavid du Colombier 		port = b->port[PORT(c->qid)];
3117dd7cddfSDavid du Colombier 		if(port == 0)
3127dd7cddfSDavid du Colombier 			strcpy(buf, "unbound\n");
3137dd7cddfSDavid du Colombier 		else {
3147dd7cddfSDavid du Colombier 			i = 0;
3157dd7cddfSDavid du Colombier 			switch(port->type) {
316ac020a8fSDavid du Colombier 			default:
317ac020a8fSDavid du Colombier 				panic("bridgeread: unknown port type: %d",
318ac020a8fSDavid du Colombier 					port->type);
3197dd7cddfSDavid du Colombier 			case Tether:
3207dd7cddfSDavid du Colombier 				i += snprint(buf+i, sizeof(buf)-i, "ether %s: ", port->name);
3217dd7cddfSDavid du Colombier 				break;
3227dd7cddfSDavid du Colombier 			case Ttun:
3237dd7cddfSDavid du Colombier 				i += snprint(buf+i, sizeof(buf)-i, "tunnel %s: ", port->name);
3247dd7cddfSDavid du Colombier 				break;
3257dd7cddfSDavid du Colombier 			}
3267dd7cddfSDavid du Colombier 			ingood = port->in - port->inmulti - port->inunknown;
3277dd7cddfSDavid du Colombier 			outgood = port->out - port->outmulti - port->outunknown;
328*a129eb93SDavid du Colombier 			snprint(buf+i, sizeof(buf)-i,
329ac020a8fSDavid du Colombier 				"in=%d(%d:%d:%d) out=%d(%d:%d:%d:%d)\n",
3307dd7cddfSDavid du Colombier 				port->in, ingood, port->inmulti, port->inunknown,
331ac020a8fSDavid du Colombier 				port->out, outgood, port->outmulti,
332ac020a8fSDavid du Colombier 				port->outunknown, port->outfrag);
3337dd7cddfSDavid du Colombier 		}
334*a129eb93SDavid du Colombier 		poperror();
3357dd7cddfSDavid du Colombier 		qunlock(b);
336*a129eb93SDavid du Colombier 		return readstr(off, a, n, buf);
3377dd7cddfSDavid du Colombier 	case Qbctl:
3386c0968e3SDavid du Colombier 		snprint(buf, sizeof(buf), "%s tcpmss\ndelay %ld %ld\n",
3396c0968e3SDavid du Colombier 			b->tcpmss ? "set" : "clear", b->delay0, b->delayn);
3407dd7cddfSDavid du Colombier 		n = readstr(off, a, n, buf);
3417dd7cddfSDavid du Colombier 		return n;
3427dd7cddfSDavid du Colombier 	case Qcache:
3437dd7cddfSDavid du Colombier 		n = readstr(off, a, n, c->aux);
3447dd7cddfSDavid du Colombier 		return n;
3457dd7cddfSDavid du Colombier 	case Qstats:
3467dd7cddfSDavid du Colombier 		snprint(buf, sizeof(buf), "hit=%uld miss=%uld copy=%uld\n",
3477dd7cddfSDavid du Colombier 			b->hit, b->miss, b->copy);
3487dd7cddfSDavid du Colombier 		n = readstr(off, a, n, buf);
3497dd7cddfSDavid du Colombier 		return n;
3507dd7cddfSDavid du Colombier 	}
3517dd7cddfSDavid du Colombier }
3527dd7cddfSDavid du Colombier 
3537dd7cddfSDavid du Colombier static void
bridgeoption(Bridge * b,char * option,int value)3547dd7cddfSDavid du Colombier bridgeoption(Bridge *b, char *option, int value)
3557dd7cddfSDavid du Colombier {
3567dd7cddfSDavid du Colombier 	if(strcmp(option, "tcpmss") == 0)
3577dd7cddfSDavid du Colombier 		b->tcpmss = value;
3587dd7cddfSDavid du Colombier 	else
3597dd7cddfSDavid du Colombier 		error("unknown bridge option");
3607dd7cddfSDavid du Colombier }
3617dd7cddfSDavid du Colombier 
3627dd7cddfSDavid du Colombier 
3637dd7cddfSDavid du Colombier static long
bridgewrite(Chan * c,void * a,long n,vlong off)3647dd7cddfSDavid du Colombier bridgewrite(Chan *c, void *a, long n, vlong off)
3657dd7cddfSDavid du Colombier {
3667dd7cddfSDavid du Colombier 	Bridge *b = bridgetab + c->dev;
3677dd7cddfSDavid du Colombier 	Cmdbuf *cb;
368ac020a8fSDavid du Colombier 	char *arg0, *p;
3697dd7cddfSDavid du Colombier 
3707dd7cddfSDavid du Colombier 	USED(off);
3717dd7cddfSDavid du Colombier 	switch(TYPE(c->qid)) {
3727dd7cddfSDavid du Colombier 	default:
3737dd7cddfSDavid du Colombier 		error(Eperm);
3747dd7cddfSDavid du Colombier 	case Qbctl:
3757dd7cddfSDavid du Colombier 		cb = parsecmd(a, n);
3767dd7cddfSDavid du Colombier 		qlock(b);
3777dd7cddfSDavid du Colombier 		if(waserror()) {
3787dd7cddfSDavid du Colombier 			qunlock(b);
3797dd7cddfSDavid du Colombier 			free(cb);
3807dd7cddfSDavid du Colombier 			nexterror();
3817dd7cddfSDavid du Colombier 		}
3827dd7cddfSDavid du Colombier 		if(cb->nf == 0)
3837dd7cddfSDavid du Colombier 			error("short write");
3847dd7cddfSDavid du Colombier 		arg0 = cb->f[0];
3857dd7cddfSDavid du Colombier 		if(strcmp(arg0, "bind") == 0) {
3867dd7cddfSDavid du Colombier 			portbind(b, cb->nf-1, cb->f+1);
3877dd7cddfSDavid du Colombier 		} else if(strcmp(arg0, "unbind") == 0) {
3887dd7cddfSDavid du Colombier 			portunbind(b, cb->nf-1, cb->f+1);
3897dd7cddfSDavid du Colombier 		} else if(strcmp(arg0, "cacheflush") == 0) {
3907dd7cddfSDavid du Colombier 			log(b, Logcache, "cache flush\n");
3917dd7cddfSDavid du Colombier 			memset(b->cache, 0, CacheSize*sizeof(Centry));
3927dd7cddfSDavid du Colombier 		} else if(strcmp(arg0, "set") == 0) {
3937dd7cddfSDavid du Colombier 			if(cb->nf != 2)
3947dd7cddfSDavid du Colombier 				error("usage: set option");
3957dd7cddfSDavid du Colombier 			bridgeoption(b, cb->f[1], 1);
3967dd7cddfSDavid du Colombier 		} else if(strcmp(arg0, "clear") == 0) {
3977dd7cddfSDavid du Colombier 			if(cb->nf != 2)
3987dd7cddfSDavid du Colombier 				error("usage: clear option");
3997dd7cddfSDavid du Colombier 			bridgeoption(b, cb->f[1], 0);
4007dd7cddfSDavid du Colombier 		} else if(strcmp(arg0, "delay") == 0) {
4017dd7cddfSDavid du Colombier 			if(cb->nf != 3)
4027dd7cddfSDavid du Colombier 				error("usage: delay delay0 delayn");
4037dd7cddfSDavid du Colombier 			b->delay0 = strtol(cb->f[1], nil, 10);
4047dd7cddfSDavid du Colombier 			b->delayn = strtol(cb->f[2], nil, 10);
4057dd7cddfSDavid du Colombier 		} else
4067dd7cddfSDavid du Colombier 			error("unknown control request");
4077dd7cddfSDavid du Colombier 		poperror();
4087dd7cddfSDavid du Colombier 		qunlock(b);
4097dd7cddfSDavid du Colombier 		free(cb);
4107dd7cddfSDavid du Colombier 		return n;
4117dd7cddfSDavid du Colombier 	case Qlog:
4127dd7cddfSDavid du Colombier 		cb = parsecmd(a, n);
4137dd7cddfSDavid du Colombier 		p = logctl(b, cb->nf, cb->f, logflags);
4147dd7cddfSDavid du Colombier 		free(cb);
4157dd7cddfSDavid du Colombier 		if(p != nil)
4167dd7cddfSDavid du Colombier 			error(p);
4177dd7cddfSDavid du Colombier 		return n;
4187dd7cddfSDavid du Colombier 	}
4197dd7cddfSDavid du Colombier }
4207dd7cddfSDavid du Colombier 
4217dd7cddfSDavid du Colombier static int
bridgegen(Chan * c,char *,Dirtab *,int,int s,Dir * dp)4229a747e4fSDavid du Colombier bridgegen(Chan *c, char *, Dirtab*, int, int s, Dir *dp)
4237dd7cddfSDavid du Colombier {
4247dd7cddfSDavid du Colombier 	Bridge *b = bridgetab + c->dev;
4257dd7cddfSDavid du Colombier 	int type = TYPE(c->qid);
4267dd7cddfSDavid du Colombier 	Dirtab *dt;
4277dd7cddfSDavid du Colombier 	Qid qid;
4287dd7cddfSDavid du Colombier 
4297dd7cddfSDavid du Colombier 	if(s  == DEVDOTDOT){
4307dd7cddfSDavid du Colombier 		switch(TYPE(c->qid)){
4317dd7cddfSDavid du Colombier 		case Qtopdir:
4327dd7cddfSDavid du Colombier 		case Qbridgedir:
4339a747e4fSDavid du Colombier 			snprint(up->genbuf, sizeof(up->genbuf), "#B%ld", c->dev);
4349a747e4fSDavid du Colombier 			mkqid(&qid, Qtopdir, 0, QTDIR);
4359a747e4fSDavid du Colombier 			devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
4367dd7cddfSDavid du Colombier 			break;
4377dd7cddfSDavid du Colombier 		case Qportdir:
4389a747e4fSDavid du Colombier 			snprint(up->genbuf, sizeof(up->genbuf), "bridge%ld", c->dev);
4399a747e4fSDavid du Colombier 			mkqid(&qid, Qbridgedir, 0, QTDIR);
4409a747e4fSDavid du Colombier 			devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
4417dd7cddfSDavid du Colombier 			break;
4427dd7cddfSDavid du Colombier 		default:
4439a747e4fSDavid du Colombier 			panic("bridgewalk %llux", c->qid.path);
4447dd7cddfSDavid du Colombier 		}
4457dd7cddfSDavid du Colombier 		return 1;
4467dd7cddfSDavid du Colombier 	}
4477dd7cddfSDavid du Colombier 
4487dd7cddfSDavid du Colombier 	switch(type) {
4497dd7cddfSDavid du Colombier 	default:
450ac020a8fSDavid du Colombier 		/* non-directory entries end up here */
4519a747e4fSDavid du Colombier 		if(c->qid.type & QTDIR)
4527dd7cddfSDavid du Colombier 			panic("bridgegen: unexpected directory");
4537dd7cddfSDavid du Colombier 		if(s != 0)
4547dd7cddfSDavid du Colombier 			return -1;
4557dd7cddfSDavid du Colombier 		dt = dirtab[TYPE(c->qid)];
4567dd7cddfSDavid du Colombier 		if(dt == nil)
4579a747e4fSDavid du Colombier 			panic("bridgegen: unknown type: %lud", TYPE(c->qid));
4587dd7cddfSDavid du Colombier 		devdir(c, c->qid, dt->name, dt->length, eve, dt->perm, dp);
4597dd7cddfSDavid du Colombier 		return 1;
4607dd7cddfSDavid du Colombier 	case Qtopdir:
4617dd7cddfSDavid du Colombier 		if(s != 0)
4627dd7cddfSDavid du Colombier 			return -1;
4639a747e4fSDavid du Colombier 		snprint(up->genbuf, sizeof(up->genbuf), "bridge%ld", c->dev);
4649a747e4fSDavid du Colombier 		mkqid(&qid, QID(0, Qbridgedir), 0, QTDIR);
4659a747e4fSDavid du Colombier 		devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
4667dd7cddfSDavid du Colombier 		return 1;
4677dd7cddfSDavid du Colombier 	case Qbridgedir:
4687dd7cddfSDavid du Colombier 		if(s<nelem(bridgedirtab)) {
4697dd7cddfSDavid du Colombier 			dt = bridgedirtab+s;
4707dd7cddfSDavid du Colombier 			devdir(c, dt->qid, dt->name, dt->length, eve, dt->perm, dp);
4717dd7cddfSDavid du Colombier 			return 1;
4727dd7cddfSDavid du Colombier 		}
4737dd7cddfSDavid du Colombier 		s -= nelem(bridgedirtab);
4747dd7cddfSDavid du Colombier 		if(s >= b->nport)
4757dd7cddfSDavid du Colombier 			return -1;
4769a747e4fSDavid du Colombier 		mkqid(&qid, QID(s, Qportdir), 0, QTDIR);
4779a747e4fSDavid du Colombier 		snprint(up->genbuf, sizeof(up->genbuf), "%d", s);
4789a747e4fSDavid du Colombier 		devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
4797dd7cddfSDavid du Colombier 		return 1;
4807dd7cddfSDavid du Colombier 	case Qportdir:
4817dd7cddfSDavid du Colombier 		if(s>=nelem(portdirtab))
4827dd7cddfSDavid du Colombier 			return -1;
4837dd7cddfSDavid du Colombier 		dt = portdirtab+s;
4849a747e4fSDavid du Colombier 		mkqid(&qid, QID(PORT(c->qid),TYPE(dt->qid)), 0, QTFILE);
4857dd7cddfSDavid du Colombier 		devdir(c, qid, dt->name, dt->length, eve, dt->perm, dp);
4867dd7cddfSDavid du Colombier 		return 1;
4877dd7cddfSDavid du Colombier 	}
4887dd7cddfSDavid du Colombier }
4897dd7cddfSDavid du Colombier 
490ac020a8fSDavid du Colombier // parse mac address; also in netif.c
4917dd7cddfSDavid du Colombier static int
parseaddr(uchar * to,char * from,int alen)4927dd7cddfSDavid du Colombier parseaddr(uchar *to, char *from, int alen)
4937dd7cddfSDavid du Colombier {
4947dd7cddfSDavid du Colombier 	char nip[4];
4957dd7cddfSDavid du Colombier 	char *p;
4967dd7cddfSDavid du Colombier 	int i;
4977dd7cddfSDavid du Colombier 
4987dd7cddfSDavid du Colombier 	p = from;
4997dd7cddfSDavid du Colombier 	for(i = 0; i < alen; i++){
5007dd7cddfSDavid du Colombier 		if(*p == 0)
5017dd7cddfSDavid du Colombier 			return -1;
5027dd7cddfSDavid du Colombier 		nip[0] = *p++;
5037dd7cddfSDavid du Colombier 		if(*p == 0)
5047dd7cddfSDavid du Colombier 			return -1;
5057dd7cddfSDavid du Colombier 		nip[1] = *p++;
5067dd7cddfSDavid du Colombier 		nip[2] = 0;
5077dd7cddfSDavid du Colombier 		to[i] = strtoul(nip, 0, 16);
5087dd7cddfSDavid du Colombier 		if(*p == ':')
5097dd7cddfSDavid du Colombier 			p++;
5107dd7cddfSDavid du Colombier 	}
5117dd7cddfSDavid du Colombier 	return 0;
5127dd7cddfSDavid du Colombier }
5137dd7cddfSDavid du Colombier 
5147dd7cddfSDavid du Colombier // assumes b is locked
5157dd7cddfSDavid du Colombier static void
portbind(Bridge * b,int argc,char * argv[])5167dd7cddfSDavid du Colombier portbind(Bridge *b, int argc, char *argv[])
5177dd7cddfSDavid du Colombier {
5187dd7cddfSDavid du Colombier 	Port *port;
5197dd7cddfSDavid du Colombier 	Chan *ctl;
5207dd7cddfSDavid du Colombier 	int type = 0, i, n;
5217dd7cddfSDavid du Colombier 	ulong ownhash;
522*a129eb93SDavid du Colombier 	char *dev, *dev2 = nil;
523ac020a8fSDavid du Colombier 	char buf[100], name[KNAMELEN], path[8*KNAMELEN];
524ac020a8fSDavid du Colombier 	static char usage[] = "usage: bind ether|tunnel name ownhash dev [dev2]";
5257dd7cddfSDavid du Colombier 
5269a747e4fSDavid du Colombier 	memset(name, 0, KNAMELEN);
5277dd7cddfSDavid du Colombier 	if(argc < 4)
5287dd7cddfSDavid du Colombier 		error(usage);
5297dd7cddfSDavid du Colombier 	if(strcmp(argv[0], "ether") == 0) {
5307dd7cddfSDavid du Colombier 		if(argc != 4)
5317dd7cddfSDavid du Colombier 			error(usage);
5327dd7cddfSDavid du Colombier 		type = Tether;
5339a747e4fSDavid du Colombier 		strncpy(name, argv[1], KNAMELEN);
5349a747e4fSDavid du Colombier 		name[KNAMELEN-1] = 0;
5357dd7cddfSDavid du Colombier //		parseaddr(addr, argv[1], Eaddrlen);
5367dd7cddfSDavid du Colombier 	} else if(strcmp(argv[0], "tunnel") == 0) {
5377dd7cddfSDavid du Colombier 		if(argc != 5)
5387dd7cddfSDavid du Colombier 			error(usage);
5397dd7cddfSDavid du Colombier 		type = Ttun;
5409a747e4fSDavid du Colombier 		strncpy(name, argv[1], KNAMELEN);
5419a747e4fSDavid du Colombier 		name[KNAMELEN-1] = 0;
5427dd7cddfSDavid du Colombier //		parseip(addr, argv[1]);
5437dd7cddfSDavid du Colombier 		dev2 = argv[4];
5447dd7cddfSDavid du Colombier 	} else
5457dd7cddfSDavid du Colombier 		error(usage);
5467dd7cddfSDavid du Colombier 	ownhash = atoi(argv[2]);
5477dd7cddfSDavid du Colombier 	dev = argv[3];
5487dd7cddfSDavid du Colombier 	for(i=0; i<b->nport; i++) {
5497dd7cddfSDavid du Colombier 		port = b->port[i];
550ac020a8fSDavid du Colombier 		if(port != nil && port->type == type &&
551ac020a8fSDavid du Colombier 		    memcmp(port->name, name, KNAMELEN) == 0)
5527dd7cddfSDavid du Colombier 			error("port in use");
5537dd7cddfSDavid du Colombier 	}
5547dd7cddfSDavid du Colombier 	for(i=0; i<Maxport; i++)
5557dd7cddfSDavid du Colombier 		if(b->port[i] == nil)
5567dd7cddfSDavid du Colombier 			break;
5577dd7cddfSDavid du Colombier 	if(i == Maxport)
5587dd7cddfSDavid du Colombier 		error("no more ports");
5597dd7cddfSDavid du Colombier 	port = smalloc(sizeof(Port));
5607dd7cddfSDavid du Colombier 	port->ref = 1;
5617dd7cddfSDavid du Colombier 	port->id = i;
5627dd7cddfSDavid du Colombier 	port->ownhash = ownhash;
5637dd7cddfSDavid du Colombier 
5647dd7cddfSDavid du Colombier 	if(waserror()) {
5657dd7cddfSDavid du Colombier 		portfree(port);
5667dd7cddfSDavid du Colombier 		nexterror();
5677dd7cddfSDavid du Colombier 	}
5687dd7cddfSDavid du Colombier 	port->type = type;
5699a747e4fSDavid du Colombier 	memmove(port->name, name, KNAMELEN);
5707dd7cddfSDavid du Colombier 	switch(port->type) {
571ac020a8fSDavid du Colombier 	default:
572ac020a8fSDavid du Colombier 		panic("portbind: unknown port type: %d", type);
5737dd7cddfSDavid du Colombier 	case Tether:
5747dd7cddfSDavid du Colombier 		snprint(path, sizeof(path), "%s/clone", dev);
5757dd7cddfSDavid du Colombier 		ctl = namec(path, Aopen, ORDWR, 0);
5767dd7cddfSDavid du Colombier 		if(waserror()) {
5777dd7cddfSDavid du Colombier 			cclose(ctl);
5787dd7cddfSDavid du Colombier 			nexterror();
5797dd7cddfSDavid du Colombier 		}
5807dd7cddfSDavid du Colombier 		// check addr?
5817dd7cddfSDavid du Colombier 
5827dd7cddfSDavid du Colombier 		// get directory name
583*a129eb93SDavid du Colombier 		n = devtab[ctl->type]->read(ctl, buf, sizeof(buf)-1, 0);
5847dd7cddfSDavid du Colombier 		buf[n] = 0;
585*a129eb93SDavid du Colombier 		snprint(path, sizeof(path), "%s/%lud/data", dev, strtoul(buf, 0, 0));
5867dd7cddfSDavid du Colombier 
5877dd7cddfSDavid du Colombier 		// setup connection to be promiscuous
5887dd7cddfSDavid du Colombier 		snprint(buf, sizeof(buf), "connect -1");
5897dd7cddfSDavid du Colombier 		devtab[ctl->type]->write(ctl, buf, strlen(buf), 0);
5907dd7cddfSDavid du Colombier 		snprint(buf, sizeof(buf), "promiscuous");
5917dd7cddfSDavid du Colombier 		devtab[ctl->type]->write(ctl, buf, strlen(buf), 0);
5927dd7cddfSDavid du Colombier 		snprint(buf, sizeof(buf), "bridge");
5937dd7cddfSDavid du Colombier 		devtab[ctl->type]->write(ctl, buf, strlen(buf), 0);
5947dd7cddfSDavid du Colombier 
5957dd7cddfSDavid du Colombier 		// open data port
5967dd7cddfSDavid du Colombier 		port->data[0] = namec(path, Aopen, ORDWR, 0);
5977dd7cddfSDavid du Colombier 		// dup it
5987dd7cddfSDavid du Colombier 		incref(port->data[0]);
5997dd7cddfSDavid du Colombier 		port->data[1] = port->data[0];
6007dd7cddfSDavid du Colombier 
6017dd7cddfSDavid du Colombier 		poperror();
6027dd7cddfSDavid du Colombier 		cclose(ctl);
6037dd7cddfSDavid du Colombier 
6047dd7cddfSDavid du Colombier 		break;
6057dd7cddfSDavid du Colombier 	case Ttun:
6067dd7cddfSDavid du Colombier 		port->data[0] = namec(dev, Aopen, OREAD, 0);
6077dd7cddfSDavid du Colombier 		port->data[1] = namec(dev2, Aopen, OWRITE, 0);
6087dd7cddfSDavid du Colombier 		break;
6097dd7cddfSDavid du Colombier 	}
6107dd7cddfSDavid du Colombier 
6117dd7cddfSDavid du Colombier 	poperror();
6127dd7cddfSDavid du Colombier 
613ac020a8fSDavid du Colombier 	/* committed to binding port */
6147dd7cddfSDavid du Colombier 	b->port[port->id] = port;
6157dd7cddfSDavid du Colombier 	port->bridge = b;
6167dd7cddfSDavid du Colombier 	if(b->nport <= port->id)
6177dd7cddfSDavid du Colombier 		b->nport = port->id+1;
6187dd7cddfSDavid du Colombier 
6197dd7cddfSDavid du Colombier 	// assumes kproc always succeeds
620*a129eb93SDavid du Colombier 	incref(port);
621*a129eb93SDavid du Colombier 	snprint(buf, sizeof(buf), "bridge:%s", dev);
622*a129eb93SDavid du Colombier 	kproc(buf, etherread, port);
6237dd7cddfSDavid du Colombier }
6247dd7cddfSDavid du Colombier 
6257dd7cddfSDavid du Colombier // assumes b is locked
6267dd7cddfSDavid du Colombier static void
portunbind(Bridge * b,int argc,char * argv[])6277dd7cddfSDavid du Colombier portunbind(Bridge *b, int argc, char *argv[])
6287dd7cddfSDavid du Colombier {
6297dd7cddfSDavid du Colombier 	int type = 0, i;
6309a747e4fSDavid du Colombier 	char name[KNAMELEN];
6317dd7cddfSDavid du Colombier 	ulong ownhash;
632ac020a8fSDavid du Colombier 	Port *port = nil;
633ac020a8fSDavid du Colombier 	static char usage[] = "usage: unbind ether|tunnel addr [ownhash]";
6347dd7cddfSDavid du Colombier 
6359a747e4fSDavid du Colombier 	memset(name, 0, KNAMELEN);
6367dd7cddfSDavid du Colombier 	if(argc < 2 || argc > 3)
6377dd7cddfSDavid du Colombier 		error(usage);
6387dd7cddfSDavid du Colombier 	if(strcmp(argv[0], "ether") == 0) {
6397dd7cddfSDavid du Colombier 		type = Tether;
6409a747e4fSDavid du Colombier 		strncpy(name, argv[1], KNAMELEN);
6419a747e4fSDavid du Colombier 		name[KNAMELEN-1] = 0;
6427dd7cddfSDavid du Colombier //		parseaddr(addr, argv[1], Eaddrlen);
6437dd7cddfSDavid du Colombier 	} else if(strcmp(argv[0], "tunnel") == 0) {
6447dd7cddfSDavid du Colombier 		type = Ttun;
6459a747e4fSDavid du Colombier 		strncpy(name, argv[1], KNAMELEN);
6469a747e4fSDavid du Colombier 		name[KNAMELEN-1] = 0;
6477dd7cddfSDavid du Colombier //		parseip(addr, argv[1]);
6487dd7cddfSDavid du Colombier 	} else
6497dd7cddfSDavid du Colombier 		error(usage);
6507dd7cddfSDavid du Colombier 	if(argc == 3)
6517dd7cddfSDavid du Colombier 		ownhash = atoi(argv[2]);
6527dd7cddfSDavid du Colombier 	else
6537dd7cddfSDavid du Colombier 		ownhash = 0;
6547dd7cddfSDavid du Colombier 	for(i=0; i<b->nport; i++) {
6557dd7cddfSDavid du Colombier 		port = b->port[i];
656ac020a8fSDavid du Colombier 		if(port != nil && port->type == type &&
657ac020a8fSDavid du Colombier 		    memcmp(port->name, name, KNAMELEN) == 0)
6587dd7cddfSDavid du Colombier 			break;
6597dd7cddfSDavid du Colombier 	}
6607dd7cddfSDavid du Colombier 	if(i == b->nport)
6617dd7cddfSDavid du Colombier 		error("port not found");
6627dd7cddfSDavid du Colombier 	if(ownhash != 0 && port->ownhash != 0 && ownhash != port->ownhash)
6637dd7cddfSDavid du Colombier 		error("bad owner hash");
6647dd7cddfSDavid du Colombier 
6657dd7cddfSDavid du Colombier 	port->closed = 1;
6667dd7cddfSDavid du Colombier 	b->port[i] = nil;	// port is now unbound
6677dd7cddfSDavid du Colombier 	cacheflushport(b, i);
6687dd7cddfSDavid du Colombier 
6697dd7cddfSDavid du Colombier 	// try and stop reader
6707dd7cddfSDavid du Colombier 	if(port->readp)
6717dd7cddfSDavid du Colombier 		postnote(port->readp, 1, "unbind", 0);
6727dd7cddfSDavid du Colombier 	portfree(port);
6737dd7cddfSDavid du Colombier }
6747dd7cddfSDavid du Colombier 
6757dd7cddfSDavid du Colombier // assumes b is locked
6767dd7cddfSDavid du Colombier static Centry *
cachelookup(Bridge * b,uchar d[Eaddrlen])6777dd7cddfSDavid du Colombier cachelookup(Bridge *b, uchar d[Eaddrlen])
6787dd7cddfSDavid du Colombier {
6797dd7cddfSDavid du Colombier 	int i;
6807dd7cddfSDavid du Colombier 	uint h;
6817dd7cddfSDavid du Colombier 	Centry *p;
6827dd7cddfSDavid du Colombier 	long sec;
6837dd7cddfSDavid du Colombier 
6847dd7cddfSDavid du Colombier 	// dont cache multicast or broadcast
6857dd7cddfSDavid du Colombier 	if(d[0] & 1)
6867dd7cddfSDavid du Colombier 		return 0;
6877dd7cddfSDavid du Colombier 
6887dd7cddfSDavid du Colombier 	h = 0;
6897dd7cddfSDavid du Colombier 	for(i=0; i<Eaddrlen; i++) {
6907dd7cddfSDavid du Colombier 		h *= 7;
6917dd7cddfSDavid du Colombier 		h += d[i];
6927dd7cddfSDavid du Colombier 	}
6937dd7cddfSDavid du Colombier 	h %= CacheHash;
6947dd7cddfSDavid du Colombier 	p = b->cache + h;
6957dd7cddfSDavid du Colombier 	sec = TK2SEC(m->ticks);
6967dd7cddfSDavid du Colombier 	for(i=0; i<CacheLook; i++,p++) {
6977dd7cddfSDavid du Colombier 		if(memcmp(d, p->d, Eaddrlen) == 0) {
6987dd7cddfSDavid du Colombier 			p->dst++;
6997dd7cddfSDavid du Colombier 			if(sec >= p->expire) {
7007dd7cddfSDavid du Colombier 				log(b, Logcache, "expired cache entry: %E %d\n",
7017dd7cddfSDavid du Colombier 					d, p->port);
7027dd7cddfSDavid du Colombier 				return nil;
7037dd7cddfSDavid du Colombier 			}
7047dd7cddfSDavid du Colombier 			p->expire = sec + CacheTimeout;
7057dd7cddfSDavid du Colombier 			return p;
7067dd7cddfSDavid du Colombier 		}
7077dd7cddfSDavid du Colombier 	}
7087dd7cddfSDavid du Colombier 	log(b, Logcache, "cache miss: %E\n", d);
7097dd7cddfSDavid du Colombier 	return nil;
7107dd7cddfSDavid du Colombier }
7117dd7cddfSDavid du Colombier 
7127dd7cddfSDavid du Colombier // assumes b is locked
7137dd7cddfSDavid du Colombier static void
cacheupdate(Bridge * b,uchar d[Eaddrlen],int port)7147dd7cddfSDavid du Colombier cacheupdate(Bridge *b, uchar d[Eaddrlen], int port)
7157dd7cddfSDavid du Colombier {
7167dd7cddfSDavid du Colombier 	int i;
7177dd7cddfSDavid du Colombier 	uint h;
7187dd7cddfSDavid du Colombier 	Centry *p, *pp;
7197dd7cddfSDavid du Colombier 	long sec;
7207dd7cddfSDavid du Colombier 
7217dd7cddfSDavid du Colombier 	// dont cache multicast or broadcast
7227dd7cddfSDavid du Colombier 	if(d[0] & 1) {
7237dd7cddfSDavid du Colombier 		log(b, Logcache, "bad source address: %E\n", d);
7247dd7cddfSDavid du Colombier 		return;
7257dd7cddfSDavid du Colombier 	}
7267dd7cddfSDavid du Colombier 
7277dd7cddfSDavid du Colombier 	h = 0;
7287dd7cddfSDavid du Colombier 	for(i=0; i<Eaddrlen; i++) {
7297dd7cddfSDavid du Colombier 		h *= 7;
7307dd7cddfSDavid du Colombier 		h += d[i];
7317dd7cddfSDavid du Colombier 	}
7327dd7cddfSDavid du Colombier 	h %= CacheHash;
7337dd7cddfSDavid du Colombier 	p = b->cache + h;
7347dd7cddfSDavid du Colombier 	pp = p;
7357dd7cddfSDavid du Colombier 	sec = p->expire;
7367dd7cddfSDavid du Colombier 
7377dd7cddfSDavid du Colombier 	// look for oldest entry
7387dd7cddfSDavid du Colombier 	for(i=0; i<CacheLook; i++,p++) {
7397dd7cddfSDavid du Colombier 		if(memcmp(p->d, d, Eaddrlen) == 0) {
7407dd7cddfSDavid du Colombier 			p->expire = TK2SEC(m->ticks) + CacheTimeout;
7417dd7cddfSDavid du Colombier 			if(p->port != port) {
7427dd7cddfSDavid du Colombier 				log(b, Logcache, "NIC changed port %d->%d: %E\n",
7437dd7cddfSDavid du Colombier 					p->port, port, d);
7447dd7cddfSDavid du Colombier 				p->port = port;
7457dd7cddfSDavid du Colombier 			}
7467dd7cddfSDavid du Colombier 			p->src++;
7477dd7cddfSDavid du Colombier 			return;
7487dd7cddfSDavid du Colombier 		}
7497dd7cddfSDavid du Colombier 		if(p->expire < sec) {
7507dd7cddfSDavid du Colombier 			sec = p->expire;
7517dd7cddfSDavid du Colombier 			pp = p;
7527dd7cddfSDavid du Colombier 		}
7537dd7cddfSDavid du Colombier 	}
7547dd7cddfSDavid du Colombier 	if(pp->expire != 0)
7557dd7cddfSDavid du Colombier 		log(b, Logcache, "bumping from cache: %E %d\n", pp->d, pp->port);
7567dd7cddfSDavid du Colombier 	pp->expire = TK2SEC(m->ticks) + CacheTimeout;
7577dd7cddfSDavid du Colombier 	memmove(pp->d, d, Eaddrlen);
7587dd7cddfSDavid du Colombier 	pp->port = port;
7597dd7cddfSDavid du Colombier 	pp->src = 1;
7607dd7cddfSDavid du Colombier 	pp->dst = 0;
7617dd7cddfSDavid du Colombier 	log(b, Logcache, "adding to cache: %E %d\n", pp->d, pp->port);
7627dd7cddfSDavid du Colombier }
7637dd7cddfSDavid du Colombier 
7647dd7cddfSDavid du Colombier // assumes b is locked
7657dd7cddfSDavid du Colombier static void
cacheflushport(Bridge * b,int port)7667dd7cddfSDavid du Colombier cacheflushport(Bridge *b, int port)
7677dd7cddfSDavid du Colombier {
7687dd7cddfSDavid du Colombier 	Centry *ce;
7697dd7cddfSDavid du Colombier 	int i;
7707dd7cddfSDavid du Colombier 
7717dd7cddfSDavid du Colombier 	ce = b->cache;
7727dd7cddfSDavid du Colombier 	for(i=0; i<CacheSize; i++,ce++) {
7737dd7cddfSDavid du Colombier 		if(ce->port != port)
7747dd7cddfSDavid du Colombier 			continue;
7757dd7cddfSDavid du Colombier 		memset(ce, 0, sizeof(Centry));
7767dd7cddfSDavid du Colombier 	}
7777dd7cddfSDavid du Colombier }
7787dd7cddfSDavid du Colombier 
7797dd7cddfSDavid du Colombier static char *
cachedump(Bridge * b)7807dd7cddfSDavid du Colombier cachedump(Bridge *b)
7817dd7cddfSDavid du Colombier {
7827dd7cddfSDavid du Colombier 	int i, n;
7837dd7cddfSDavid du Colombier 	long sec, off;
7847dd7cddfSDavid du Colombier 	char *buf, *p, *ep;
7857dd7cddfSDavid du Colombier 	Centry *ce;
7867dd7cddfSDavid du Colombier 	char c;
7877dd7cddfSDavid du Colombier 
7887dd7cddfSDavid du Colombier 	qlock(b);
7897dd7cddfSDavid du Colombier 	if(waserror()) {
7907dd7cddfSDavid du Colombier 		qunlock(b);
7917dd7cddfSDavid du Colombier 		nexterror();
7927dd7cddfSDavid du Colombier 	}
7937dd7cddfSDavid du Colombier 	sec = TK2SEC(m->ticks);
7947dd7cddfSDavid du Colombier 	n = 0;
7957dd7cddfSDavid du Colombier 	for(i=0; i<CacheSize; i++)
7967dd7cddfSDavid du Colombier 		if(b->cache[i].expire != 0)
7977dd7cddfSDavid du Colombier 			n++;
7987dd7cddfSDavid du Colombier 
7997dd7cddfSDavid du Colombier 	n *= 51;	// change if print format is changed
8007dd7cddfSDavid du Colombier 	n += 10;	// some slop at the end
8017dd7cddfSDavid du Colombier 	buf = malloc(n);
802aa72973aSDavid du Colombier 	if(buf == nil)
803aa72973aSDavid du Colombier 		error(Enomem);
8047dd7cddfSDavid du Colombier 	p = buf;
8057dd7cddfSDavid du Colombier 	ep = buf + n;
8067dd7cddfSDavid du Colombier 	ce = b->cache;
8077dd7cddfSDavid du Colombier 	off = seconds() - sec;
8087dd7cddfSDavid du Colombier 	for(i=0; i<CacheSize; i++,ce++) {
8097dd7cddfSDavid du Colombier 		if(ce->expire == 0)
8107dd7cddfSDavid du Colombier 			continue;
8117dd7cddfSDavid du Colombier 		c = (sec < ce->expire)?'v':'e';
8127dd7cddfSDavid du Colombier 		p += snprint(p, ep-p, "%E %2d %10ld %10ld %10ld %c\n", ce->d,
8137dd7cddfSDavid du Colombier 			ce->port, ce->src, ce->dst, ce->expire+off, c);
8147dd7cddfSDavid du Colombier 	}
8157dd7cddfSDavid du Colombier 	*p = 0;
8167dd7cddfSDavid du Colombier 	poperror();
8177dd7cddfSDavid du Colombier 	qunlock(b);
8187dd7cddfSDavid du Colombier 
8197dd7cddfSDavid du Colombier 	return buf;
8207dd7cddfSDavid du Colombier }
8217dd7cddfSDavid du Colombier 
8227dd7cddfSDavid du Colombier 
8237dd7cddfSDavid du Colombier 
824*a129eb93SDavid du Colombier // assumes b is locked, no error return
8257dd7cddfSDavid du Colombier static void
ethermultiwrite(Bridge * b,Block * bp,Port * port)8267dd7cddfSDavid du Colombier ethermultiwrite(Bridge *b, Block *bp, Port *port)
8277dd7cddfSDavid du Colombier {
8287dd7cddfSDavid du Colombier 	Port *oport;
8297dd7cddfSDavid du Colombier 	Etherpkt *ep;
8306c0968e3SDavid du Colombier 	int i, mcast;
8317dd7cddfSDavid du Colombier 
8327dd7cddfSDavid du Colombier 	ep = (Etherpkt*)bp->rp;
8336c0968e3SDavid du Colombier 	mcast = ep->d[0] & 1;		/* multicast bit of ethernet address */
8347dd7cddfSDavid du Colombier 
8357dd7cddfSDavid du Colombier 	oport = nil;
8367dd7cddfSDavid du Colombier 	for(i=0; i<b->nport; i++) {
8377dd7cddfSDavid du Colombier 		if(i == port->id || b->port[i] == nil)
8387dd7cddfSDavid du Colombier 			continue;
8396c0968e3SDavid du Colombier 		/*
8406c0968e3SDavid du Colombier 		 * we need to forward multicast packets for ipv6,
8416c0968e3SDavid du Colombier 		 * so always do it.
8426c0968e3SDavid du Colombier 		 */
8437dd7cddfSDavid du Colombier 		if(mcast)
8447dd7cddfSDavid du Colombier 			b->port[i]->outmulti++;
8457dd7cddfSDavid du Colombier 		else
8467dd7cddfSDavid du Colombier 			b->port[i]->outunknown++;
8477dd7cddfSDavid du Colombier 
8487dd7cddfSDavid du Colombier 		// delay one so that the last write does not copy
8497dd7cddfSDavid du Colombier 		if(oport != nil) {
8507dd7cddfSDavid du Colombier 			b->copy++;
851*a129eb93SDavid du Colombier 			etherwrite(oport, copyblock(bp, blocklen(bp)));
8527dd7cddfSDavid du Colombier 		}
8537dd7cddfSDavid du Colombier 		oport = b->port[i];
8547dd7cddfSDavid du Colombier 	}
8557dd7cddfSDavid du Colombier 
8567dd7cddfSDavid du Colombier 	// last write free block
857*a129eb93SDavid du Colombier 	if(oport)
858*a129eb93SDavid du Colombier 		etherwrite(oport, bp);
859*a129eb93SDavid du Colombier 	else
8607dd7cddfSDavid du Colombier 		freeb(bp);
8617dd7cddfSDavid du Colombier }
8627dd7cddfSDavid du Colombier 
8637dd7cddfSDavid du Colombier static void
tcpmsshack(Etherpkt * epkt,int n)8647dd7cddfSDavid du Colombier tcpmsshack(Etherpkt *epkt, int n)
8657dd7cddfSDavid du Colombier {
866ac020a8fSDavid du Colombier 	int hl, optlen;
8677dd7cddfSDavid du Colombier 	Iphdr *iphdr;
8687dd7cddfSDavid du Colombier 	Tcphdr *tcphdr;
869ac020a8fSDavid du Colombier 	ulong mss, cksum;
8707dd7cddfSDavid du Colombier 	uchar *optr;
8717dd7cddfSDavid du Colombier 
872ac020a8fSDavid du Colombier 	/* ignore non-ipv4 packets */
873ac020a8fSDavid du Colombier 	if(nhgets(epkt->type) != ETIP4)
8747dd7cddfSDavid du Colombier 		return;
8757dd7cddfSDavid du Colombier 	iphdr = (Iphdr*)(epkt->data);
8767dd7cddfSDavid du Colombier 	n -= ETHERHDRSIZE;
8777dd7cddfSDavid du Colombier 	if(n < IPHDR)
8787dd7cddfSDavid du Colombier 		return;
8797dd7cddfSDavid du Colombier 
880ac020a8fSDavid du Colombier 	/* ignore bad packets */
881ac020a8fSDavid du Colombier 	if(iphdr->vihl != (IP_VER4|IP_HLEN4)) {
8827dd7cddfSDavid du Colombier 		hl = (iphdr->vihl&0xF)<<2;
883ac020a8fSDavid du Colombier 		if((iphdr->vihl&0xF0) != IP_VER4 || hl < (IP_HLEN4<<2))
8847dd7cddfSDavid du Colombier 			return;
8857dd7cddfSDavid du Colombier 	} else
886ac020a8fSDavid du Colombier 		hl = IP_HLEN4<<2;
8877dd7cddfSDavid du Colombier 
888ac020a8fSDavid du Colombier 	/* ignore non-tcp packets */
8897dd7cddfSDavid du Colombier 	if(iphdr->proto != IP_TCPPROTO)
8907dd7cddfSDavid du Colombier 		return;
8917dd7cddfSDavid du Colombier 	n -= hl;
8927dd7cddfSDavid du Colombier 	if(n < sizeof(Tcphdr))
8937dd7cddfSDavid du Colombier 		return;
8947dd7cddfSDavid du Colombier 	tcphdr = (Tcphdr*)((uchar*)(iphdr) + hl);
8957dd7cddfSDavid du Colombier 	// MSS can only appear in SYN packet
8967dd7cddfSDavid du Colombier 	if(!(tcphdr->flag[1] & SYN))
8977dd7cddfSDavid du Colombier 		return;
8987dd7cddfSDavid du Colombier 	hl = (tcphdr->flag[0] & 0xf0)>>2;
8997dd7cddfSDavid du Colombier 	if(n < hl)
9007dd7cddfSDavid du Colombier 		return;
9017dd7cddfSDavid du Colombier 
9027dd7cddfSDavid du Colombier 	// check for MSS option
903ac020a8fSDavid du Colombier 	optr = (uchar*)tcphdr + sizeof(Tcphdr);
9047dd7cddfSDavid du Colombier 	n = hl - sizeof(Tcphdr);
9057dd7cddfSDavid du Colombier 	for(;;) {
9067dd7cddfSDavid du Colombier 		if(n <= 0 || *optr == EOLOPT)
9077dd7cddfSDavid du Colombier 			return;
9087dd7cddfSDavid du Colombier 		if(*optr == NOOPOPT) {
9097dd7cddfSDavid du Colombier 			n--;
9107dd7cddfSDavid du Colombier 			optr++;
9117dd7cddfSDavid du Colombier 			continue;
9127dd7cddfSDavid du Colombier 		}
9137dd7cddfSDavid du Colombier 		optlen = optr[1];
9147dd7cddfSDavid du Colombier 		if(optlen < 2 || optlen > n)
9157dd7cddfSDavid du Colombier 			return;
9167dd7cddfSDavid du Colombier 		if(*optr == MSSOPT && optlen == MSS_LENGTH)
9177dd7cddfSDavid du Colombier 			break;
9187dd7cddfSDavid du Colombier 		n -= optlen;
9197dd7cddfSDavid du Colombier 		optr += optlen;
9207dd7cddfSDavid du Colombier 	}
9217dd7cddfSDavid du Colombier 
9227dd7cddfSDavid du Colombier 	mss = nhgets(optr+2);
9237dd7cddfSDavid du Colombier 	if(mss <= TcpMssMax)
9247dd7cddfSDavid du Colombier 		return;
9257dd7cddfSDavid du Colombier 	// fit checksum
9267dd7cddfSDavid du Colombier 	cksum = nhgets(tcphdr->cksum);
9277dd7cddfSDavid du Colombier 	if(optr-(uchar*)tcphdr & 1) {
9287dd7cddfSDavid du Colombier print("tcpmsshack: odd alignment!\n");
9297dd7cddfSDavid du Colombier 		// odd alignments are a pain
9307dd7cddfSDavid du Colombier 		cksum += nhgets(optr+1);
9317dd7cddfSDavid du Colombier 		cksum -= (optr[1]<<8)|(TcpMssMax>>8);
9327dd7cddfSDavid du Colombier 		cksum += (cksum>>16);
9337dd7cddfSDavid du Colombier 		cksum &= 0xffff;
9347dd7cddfSDavid du Colombier 		cksum += nhgets(optr+3);
9357dd7cddfSDavid du Colombier 		cksum -= ((TcpMssMax&0xff)<<8)|optr[4];
9367dd7cddfSDavid du Colombier 		cksum += (cksum>>16);
9377dd7cddfSDavid du Colombier 	} else {
9387dd7cddfSDavid du Colombier 		cksum += mss;
9397dd7cddfSDavid du Colombier 		cksum -= TcpMssMax;
9407dd7cddfSDavid du Colombier 		cksum += (cksum>>16);
9417dd7cddfSDavid du Colombier 	}
9427dd7cddfSDavid du Colombier 	hnputs(tcphdr->cksum, cksum);
9437dd7cddfSDavid du Colombier 	hnputs(optr+2, TcpMssMax);
9447dd7cddfSDavid du Colombier }
9457dd7cddfSDavid du Colombier 
9467dd7cddfSDavid du Colombier /*
9477dd7cddfSDavid du Colombier  *  process to read from the ethernet
9487dd7cddfSDavid du Colombier  */
9497dd7cddfSDavid du Colombier static void
etherread(void * a)9507dd7cddfSDavid du Colombier etherread(void *a)
9517dd7cddfSDavid du Colombier {
9527dd7cddfSDavid du Colombier 	Port *port = a;
9537dd7cddfSDavid du Colombier 	Bridge *b = port->bridge;
954*a129eb93SDavid du Colombier 	Block *bp;
9557dd7cddfSDavid du Colombier 	Etherpkt *ep;
9567dd7cddfSDavid du Colombier 	Centry *ce;
957*a129eb93SDavid du Colombier 	long md, n;
9587dd7cddfSDavid du Colombier 
9597dd7cddfSDavid du Colombier 	qlock(b);
9607dd7cddfSDavid du Colombier 	port->readp = up;	/* hide identity under a rock for unbind */
9617dd7cddfSDavid du Colombier 
9627dd7cddfSDavid du Colombier 	while(!port->closed){
9637dd7cddfSDavid du Colombier 		// release lock to read - error means it is time to quit
9647dd7cddfSDavid du Colombier 		qunlock(b);
9657dd7cddfSDavid du Colombier 		if(waserror()) {
9663ff48bf5SDavid du Colombier 			print("etherread read error: %s\n", up->errstr);
9677dd7cddfSDavid du Colombier 			qlock(b);
9687dd7cddfSDavid du Colombier 			break;
9697dd7cddfSDavid du Colombier 		}
970*a129eb93SDavid du Colombier 		bp = devtab[port->data[0]->type]->bread(port->data[0], MaxMTU, 0);
9717dd7cddfSDavid du Colombier 		poperror();
9727dd7cddfSDavid du Colombier 		qlock(b);
973*a129eb93SDavid du Colombier 		if(bp == nil)
9747dd7cddfSDavid du Colombier 			break;
975*a129eb93SDavid du Colombier 		n = blocklen(bp);
976*a129eb93SDavid du Colombier 		if(port->closed || n < ETHERMINTU){
9777dd7cddfSDavid du Colombier 			freeb(bp);
9787dd7cddfSDavid du Colombier 			continue;
9797dd7cddfSDavid du Colombier 		}
980*a129eb93SDavid du Colombier 		if(waserror()) {
981*a129eb93SDavid du Colombier //			print("etherread bridge error\n");
982*a129eb93SDavid du Colombier 			freeb(bp);
983*a129eb93SDavid du Colombier 			continue;
984*a129eb93SDavid du Colombier 		}
9857dd7cddfSDavid du Colombier 		port->in++;
9867dd7cddfSDavid du Colombier 
9877dd7cddfSDavid du Colombier 		ep = (Etherpkt*)bp->rp;
9887dd7cddfSDavid du Colombier 		cacheupdate(b, ep->s, port->id);
9897dd7cddfSDavid du Colombier 		if(b->tcpmss)
990*a129eb93SDavid du Colombier 			tcpmsshack(ep, n);
9917dd7cddfSDavid du Colombier 
9927dd7cddfSDavid du Colombier 		/*
9937dd7cddfSDavid du Colombier 		 * delay packets to simulate a slow link
9947dd7cddfSDavid du Colombier 		 */
995*a129eb93SDavid du Colombier 		if(b->delay0 != 0 || b->delayn != 0){
996*a129eb93SDavid du Colombier 			md = b->delay0 + b->delayn * n;
9977dd7cddfSDavid du Colombier 			if(md > 0)
9987dd7cddfSDavid du Colombier 				microdelay(md);
9997dd7cddfSDavid du Colombier 		}
10007dd7cddfSDavid du Colombier 
1001*a129eb93SDavid du Colombier 		poperror();	/* must now dispose of bp */
1002*a129eb93SDavid du Colombier 
10037dd7cddfSDavid du Colombier 		if(ep->d[0] & 1) {
10046c0968e3SDavid du Colombier 			log(b, Logmcast, "multicast: port=%d src=%E dst=%E type=%#.4ux\n",
10056c0968e3SDavid du Colombier 				port->id, ep->s, ep->d, ep->type[0]<<8|ep->type[1]);
10067dd7cddfSDavid du Colombier 			port->inmulti++;
1007*a129eb93SDavid du Colombier 			ethermultiwrite(b, bp, port);
10087dd7cddfSDavid du Colombier 		} else {
10097dd7cddfSDavid du Colombier 			ce = cachelookup(b, ep->d);
10107dd7cddfSDavid du Colombier 			if(ce == nil) {
10117dd7cddfSDavid du Colombier 				b->miss++;
10127dd7cddfSDavid du Colombier 				port->inunknown++;
1013*a129eb93SDavid du Colombier 				ethermultiwrite(b, bp, port);
10147dd7cddfSDavid du Colombier 			}else if(ce->port != port->id){
10157dd7cddfSDavid du Colombier 				b->hit++;
1016*a129eb93SDavid du Colombier 				etherwrite(b->port[ce->port], bp);
1017*a129eb93SDavid du Colombier 			}else
10187dd7cddfSDavid du Colombier 				freeb(bp);
10197dd7cddfSDavid du Colombier 		}
1020*a129eb93SDavid du Colombier 	}
10217dd7cddfSDavid du Colombier //	print("etherread: trying to exit\n");
10227dd7cddfSDavid du Colombier 	port->readp = nil;
10237dd7cddfSDavid du Colombier 	portfree(port);
10247dd7cddfSDavid du Colombier 	qunlock(b);
10257dd7cddfSDavid du Colombier 	pexit("hangup", 1);
10267dd7cddfSDavid du Colombier }
10277dd7cddfSDavid du Colombier 
10287dd7cddfSDavid du Colombier static int
fragment(Etherpkt * epkt,int n)10297dd7cddfSDavid du Colombier fragment(Etherpkt *epkt, int n)
10307dd7cddfSDavid du Colombier {
10317dd7cddfSDavid du Colombier 	Iphdr *iphdr;
10327dd7cddfSDavid du Colombier 
10337dd7cddfSDavid du Colombier 	if(n <= TunnelMtu)
10347dd7cddfSDavid du Colombier 		return 0;
10357dd7cddfSDavid du Colombier 
1036ac020a8fSDavid du Colombier 	/* ignore non-ipv4 packets */
1037ac020a8fSDavid du Colombier 	if(nhgets(epkt->type) != ETIP4)
10387dd7cddfSDavid du Colombier 		return 0;
10397dd7cddfSDavid du Colombier 	iphdr = (Iphdr*)(epkt->data);
10407dd7cddfSDavid du Colombier 	n -= ETHERHDRSIZE;
1041ac020a8fSDavid du Colombier 	/*
1042ac020a8fSDavid du Colombier 	 * ignore: IP runt packets, bad packets (I don't handle IP
1043ac020a8fSDavid du Colombier 	 * options for the moment), packets with don't-fragment set,
1044ac020a8fSDavid du Colombier 	 * and short blocks.
1045ac020a8fSDavid du Colombier 	 */
1046ac020a8fSDavid du Colombier 	if(n < IPHDR || iphdr->vihl != (IP_VER4|IP_HLEN4) ||
1047ac020a8fSDavid du Colombier 	    iphdr->frag[0] & (IP_DF>>8) || nhgets(iphdr->length) > n)
10487dd7cddfSDavid du Colombier 		return 0;
10497dd7cddfSDavid du Colombier 
10507dd7cddfSDavid du Colombier 	return 1;
10517dd7cddfSDavid du Colombier }
10527dd7cddfSDavid du Colombier 
10537dd7cddfSDavid du Colombier static void
etherwrite(Port * port,Block * bp)10547dd7cddfSDavid du Colombier etherwrite(Port *port, Block *bp)
10557dd7cddfSDavid du Colombier {
10567dd7cddfSDavid du Colombier 	Iphdr *eh, *feh;
10577dd7cddfSDavid du Colombier 	Etherpkt *epkt;
10587dd7cddfSDavid du Colombier 	int n, lid, len, seglen, chunk, dlen, blklen, offset, mf;
10597dd7cddfSDavid du Colombier 	Block *xp, *nb;
10607dd7cddfSDavid du Colombier 	ushort fragoff, frag;
10617dd7cddfSDavid du Colombier 
10627dd7cddfSDavid du Colombier 	port->out++;
10637dd7cddfSDavid du Colombier 	epkt = (Etherpkt*)bp->rp;
10647dd7cddfSDavid du Colombier 	n = blocklen(bp);
10657dd7cddfSDavid du Colombier 	if(port->type != Ttun || !fragment(epkt, n)) {
1066*a129eb93SDavid du Colombier 		if(!waserror()){
10677dd7cddfSDavid du Colombier 			devtab[port->data[1]->type]->bwrite(port->data[1], bp, 0);
1068*a129eb93SDavid du Colombier 			poperror();
1069*a129eb93SDavid du Colombier 		}
10707dd7cddfSDavid du Colombier 		return;
10717dd7cddfSDavid du Colombier 	}
10727dd7cddfSDavid du Colombier 	port->outfrag++;
10737dd7cddfSDavid du Colombier 	if(waserror()){
10747dd7cddfSDavid du Colombier 		freeblist(bp);
1075*a129eb93SDavid du Colombier 		return;
10767dd7cddfSDavid du Colombier 	}
10777dd7cddfSDavid du Colombier 
10787dd7cddfSDavid du Colombier 	seglen = (TunnelMtu - ETHERHDRSIZE - IPHDR) & ~7;
10797dd7cddfSDavid du Colombier 	eh = (Iphdr*)(epkt->data);
10807dd7cddfSDavid du Colombier 	len = nhgets(eh->length);
10817dd7cddfSDavid du Colombier 	frag = nhgets(eh->frag);
10827dd7cddfSDavid du Colombier 	mf = frag & IP_MF;
10837dd7cddfSDavid du Colombier 	frag <<= 3;
10847dd7cddfSDavid du Colombier 	dlen = len - IPHDR;
10857dd7cddfSDavid du Colombier 	xp = bp;
10867dd7cddfSDavid du Colombier 	lid = nhgets(eh->id);
10877dd7cddfSDavid du Colombier 	offset = ETHERHDRSIZE+IPHDR;
10887dd7cddfSDavid du Colombier 	while(xp != nil && offset && offset >= BLEN(xp)) {
10897dd7cddfSDavid du Colombier 		offset -= BLEN(xp);
10907dd7cddfSDavid du Colombier 		xp = xp->next;
10917dd7cddfSDavid du Colombier 	}
10927dd7cddfSDavid du Colombier 	xp->rp += offset;
10937dd7cddfSDavid du Colombier 
1094ac020a8fSDavid du Colombier 	if(0)
1095ac020a8fSDavid du Colombier 		print("seglen=%d, dlen=%d, mf=%x, frag=%d\n",
1096ac020a8fSDavid du Colombier 			seglen, dlen, mf, frag);
10977dd7cddfSDavid du Colombier 	for(fragoff = 0; fragoff < dlen; fragoff += seglen) {
10987dd7cddfSDavid du Colombier 		nb = allocb(ETHERHDRSIZE+IPHDR+seglen);
10997dd7cddfSDavid du Colombier 
11007dd7cddfSDavid du Colombier 		feh = (Iphdr*)(nb->wp+ETHERHDRSIZE);
11017dd7cddfSDavid du Colombier 
11027dd7cddfSDavid du Colombier 		memmove(nb->wp, epkt, ETHERHDRSIZE+IPHDR);
11037dd7cddfSDavid du Colombier 		nb->wp += ETHERHDRSIZE+IPHDR;
11047dd7cddfSDavid du Colombier 
11057dd7cddfSDavid du Colombier 		if((fragoff + seglen) >= dlen) {
11067dd7cddfSDavid du Colombier 			seglen = dlen - fragoff;
11077dd7cddfSDavid du Colombier 			hnputs(feh->frag, (frag+fragoff)>>3 | mf);
11087dd7cddfSDavid du Colombier 		}
11097dd7cddfSDavid du Colombier 		else
11107dd7cddfSDavid du Colombier 			hnputs(feh->frag, (frag+fragoff>>3) | IP_MF);
11117dd7cddfSDavid du Colombier 
11127dd7cddfSDavid du Colombier 		hnputs(feh->length, seglen + IPHDR);
11137dd7cddfSDavid du Colombier 		hnputs(feh->id, lid);
11147dd7cddfSDavid du Colombier 
11157dd7cddfSDavid du Colombier 		/* Copy up the data area */
11167dd7cddfSDavid du Colombier 		chunk = seglen;
11177dd7cddfSDavid du Colombier 		while(chunk) {
11187dd7cddfSDavid du Colombier 			blklen = chunk;
11197dd7cddfSDavid du Colombier 			if(BLEN(xp) < chunk)
11207dd7cddfSDavid du Colombier 				blklen = BLEN(xp);
11217dd7cddfSDavid du Colombier 			memmove(nb->wp, xp->rp, blklen);
11227dd7cddfSDavid du Colombier 			nb->wp += blklen;
11237dd7cddfSDavid du Colombier 			xp->rp += blklen;
11247dd7cddfSDavid du Colombier 			chunk -= blklen;
11257dd7cddfSDavid du Colombier 			if(xp->rp == xp->wp)
11267dd7cddfSDavid du Colombier 				xp = xp->next;
11277dd7cddfSDavid du Colombier 		}
11287dd7cddfSDavid du Colombier 
11297dd7cddfSDavid du Colombier 		feh->cksum[0] = 0;
11307dd7cddfSDavid du Colombier 		feh->cksum[1] = 0;
11317dd7cddfSDavid du Colombier 		hnputs(feh->cksum, ipcsum(&feh->vihl));
11327dd7cddfSDavid du Colombier 
1133ac020a8fSDavid du Colombier 		/* don't generate small packets */
11347dd7cddfSDavid du Colombier 		if(BLEN(nb) < ETHERMINTU)
11357dd7cddfSDavid du Colombier 			nb->wp = nb->rp + ETHERMINTU;
11367dd7cddfSDavid du Colombier 		devtab[port->data[1]->type]->bwrite(port->data[1], nb, 0);
11377dd7cddfSDavid du Colombier 	}
11387dd7cddfSDavid du Colombier 	poperror();
11397dd7cddfSDavid du Colombier 	freeblist(bp);
11407dd7cddfSDavid du Colombier }
11417dd7cddfSDavid du Colombier 
11427dd7cddfSDavid du Colombier // hold b lock
11437dd7cddfSDavid du Colombier static void
portfree(Port * port)11447dd7cddfSDavid du Colombier portfree(Port *port)
11457dd7cddfSDavid du Colombier {
1146*a129eb93SDavid du Colombier 	if(decref(port) != 0)
11477dd7cddfSDavid du Colombier 		return;
11487dd7cddfSDavid du Colombier 
11497dd7cddfSDavid du Colombier 	if(port->data[0])
11507dd7cddfSDavid du Colombier 		cclose(port->data[0]);
11517dd7cddfSDavid du Colombier 	if(port->data[1])
11527dd7cddfSDavid du Colombier 		cclose(port->data[1]);
11537dd7cddfSDavid du Colombier 	memset(port, 0, sizeof(Port));
11547dd7cddfSDavid du Colombier 	free(port);
11557dd7cddfSDavid du Colombier }
11567dd7cddfSDavid du Colombier 
11577dd7cddfSDavid du Colombier Dev bridgedevtab = {
11587dd7cddfSDavid du Colombier 	'B',
11597dd7cddfSDavid du Colombier 	"bridge",
11607dd7cddfSDavid du Colombier 
11617dd7cddfSDavid du Colombier 	devreset,
11627dd7cddfSDavid du Colombier 	bridgeinit,
11639a747e4fSDavid du Colombier 	devshutdown,
11647dd7cddfSDavid du Colombier 	bridgeattach,
11657dd7cddfSDavid du Colombier 	bridgewalk,
11667dd7cddfSDavid du Colombier 	bridgestat,
11677dd7cddfSDavid du Colombier 	bridgeopen,
11687dd7cddfSDavid du Colombier 	devcreate,
11697dd7cddfSDavid du Colombier 	bridgeclose,
11707dd7cddfSDavid du Colombier 	bridgeread,
11717dd7cddfSDavid du Colombier 	devbread,
11727dd7cddfSDavid du Colombier 	bridgewrite,
11737dd7cddfSDavid du Colombier 	devbwrite,
11747dd7cddfSDavid du Colombier 	devremove,
11757dd7cddfSDavid du Colombier 	devwstat,
11767dd7cddfSDavid du Colombier };
1177