1206 lines · 23.9 KB · C
RawDownload
/*  * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* * IPv4 Ethernet bridge */#include "u.h"#include "../port/lib.h"#include "mem.h"#include "dat.h"#include "fns.h"#include "../ip/ip.h"#include "../port/netif.h"#include "../port/error.h" typedef struct Bridge 	Bridge;typedef struct Port 	Port;typedef struct Centry	Centry;typedef struct Iphdr	Iphdr;typedef struct Tcphdr	Tcphdr; enum{	Qtopdir=	1,		/* top level directory */ 	Qbridgedir,			/* bridge* directory */	Qbctl,	Qstats,	Qcache,	Qlog, 	Qportdir,			/* directory for a protocol */	Qpctl,	Qlocal,	Qstatus, 	MaxQ, 	Maxbridge=	4,	Maxport=	128,		// power of 2	CacheHash=	257,		// prime	CacheLook=	5,		// how many cache entries to examine	CacheSize=	(CacheHash+CacheLook-1),	CacheTimeout=	5*60,		// timeout for cache entry in seconds 	TcpMssMax = 1300,		// max desirable Tcp MSS value	TunnelMtu = 1400,}; static Dirtab bridgedirtab[]={	"ctl",		{Qbctl},	0,	0666,	"stats",	{Qstats},	0,	0444,	"cache",	{Qcache},	0,	0444,	"log",		{Qlog},		0,	0666,}; static Dirtab portdirtab[]={	"ctl",		{Qpctl},	0,	0666,	"local",	{Qlocal},	0,	0444,	"status",	{Qstatus},	0,	0444,}; enum {	Logcache=	(1<<0),	Logmcast=	(1<<1),}; // types of interfacesenum{	Tether,	Ttun,}; static Logflag logflags[] ={	{ "cache",	Logcache, },	{ "multicast",	Logmcast, },	{ nil,		0, },}; static Dirtab	*dirtab[MaxQ]; #define TYPE(x) 	(((ulong)(x).path) & 0xff)#define PORT(x) 	((((ulong)(x).path) >> 8)&(Maxport-1))#define QID(x, y) 	(((x)<<8) | (y)) struct Centry{	uchar	d[Eaddrlen];	int	port;	long	expire;		// entry expires this many seconds after bootime	long	src;	long	dst;}; struct Bridge{	QLock;	int	nport;	Port	*port[Maxport];	Centry	cache[CacheSize];	ulong	hit;	ulong	miss;	ulong	copy;	long	delay0;		// constant microsecond delay per packet	long	delayn;		// microsecond delay per byte	int	tcpmss;		// modify tcpmss value 	Log;}; struct Port{	int	id;	Bridge	*bridge;	int	ref;	int	closed; 	Chan	*data[2];	// channel to data 	Proc	*readp;		// read proc		// the following uniquely identifies the port	int	type;	char	name[KNAMELEN];		// owner hash - avoids bind/unbind races	ulong	ownhash; 	// various stats	int	in;		// number of packets read	int	inmulti;	// multicast or broadcast	int	inunknown;	// unknown address	int	out;		// number of packets read	int	outmulti;	// multicast or broadcast	int	outunknown;	// unknown address	int	outfrag;	// fragmented the packet	int	nentry;		// number of cache entries for this port}; enum {	IP_TCPPROTO	= 6,	EOLOPT		= 0,	NOOPOPT		= 1,	MSSOPT		= 2,	MSS_LENGTH	= 4,		/* Mean segment size */	SYN		= 0x02,		/* Pkt. is synchronise */	IPHDR		= 20,		/* sizeof(Iphdr) */}; struct Iphdr{	uchar	vihl;		/* Version and header length */	uchar	tos;		/* Type of service */	uchar	length[2];	/* packet length */	uchar	id[2];		/* ip->identification */	uchar	frag[2];	/* Fragment information */	uchar	ttl;		/* Time to live */	uchar	proto;		/* Protocol */	uchar	cksum[2];	/* Header checksum */	uchar	src[4];		/* IP source */	uchar	dst[4];		/* IP destination */}; struct Tcphdr{	uchar	sport[2];	uchar	dport[2];	uchar	seq[4];	uchar	ack[4];	uchar	flag[2];	uchar	win[2];	uchar	cksum[2];	uchar	urg[2];}; static Bridge bridgetab[Maxbridge]; static int m2p[] = {	[OREAD]		4,	[OWRITE]	2,	[ORDWR]		6}; static int	bridgegen(Chan *c, char*, Dirtab*, int, int s, Dir *dp);static void	portbind(Bridge *b, int argc, char *argv[]);static void	portunbind(Bridge *b, int argc, char *argv[]);static void	etherread(void *a);static char	*cachedump(Bridge *b);static void	portfree(Port *port);static void	cacheflushport(Bridge *b, int port);static void	etherwrite(Port *port, Block *bp); static voidbridgeinit(void){	int i;	Dirtab *dt; 	// setup dirtab with non directory entries	for(i=0; i<nelem(bridgedirtab); i++) {		dt = bridgedirtab + i;		dirtab[TYPE(dt->qid)] = dt;	}	for(i=0; i<nelem(portdirtab); i++) {		dt = portdirtab + i;		dirtab[TYPE(dt->qid)] = dt;	}} static Chan*bridgeattach(char* spec){	Chan *c;	int dev; 	dev = atoi(spec);	if(dev<0 || dev >= Maxbridge)		error("bad specification"); 	c = devattach('B', spec);	mkqid(&c->qid, QID(0, Qtopdir), 0, QTDIR);	c->dev = dev;	return c;} static Walkqid*bridgewalk(Chan *c, Chan *nc, char **name, int nname){	return devwalk(c, nc, name, nname, (Dirtab*)0, 0, bridgegen);} static intbridgestat(Chan* c, uchar* db, int n){	return devstat(c, db, n, (Dirtab *)0, 0L, bridgegen);} static Chan*bridgeopen(Chan* c, int omode){	int perm;	Bridge *b; 	omode &= 3;	perm = m2p[omode];	USED(perm); 	b = bridgetab + c->dev;	USED(b); 	switch(TYPE(c->qid)) {	default:		break;	case Qlog:		logopen(b);		break;	case Qcache:		c->aux = cachedump(b);		break;	}	c->mode = openmode(omode);	c->flag |= COPEN;	c->offset = 0;	return c;} static voidbridgeclose(Chan* c){	Bridge *b  = bridgetab + c->dev; 	switch(TYPE(c->qid)) {	case Qcache:		if(c->flag & COPEN)			free(c->aux);		break;	case Qlog:		if(c->flag & COPEN)			logclose(b);		break;	}} static longbridgeread(Chan *c, void *a, long n, vlong off){	char buf[256];	Bridge *b = bridgetab + c->dev;	Port *port;	int i, ingood, outgood; 	USED(off);	switch(TYPE(c->qid)) {	default:		error(Eperm);	case Qtopdir:	case Qbridgedir:	case Qportdir:		return devdirread(c, a, n, 0, 0, bridgegen);	case Qlog:		return logread(b, a, off, n);	case Qstatus:		qlock(b);		port = b->port[PORT(c->qid)];		if(port == 0)			strcpy(buf, "unbound\n");		else {			i = 0;			switch(port->type) {			default:				panic("bridgeread: unknown port type: %d",					port->type);			case Tether:				i += snprint(buf+i, sizeof(buf)-i, "ether %s: ", port->name);				break;			case Ttun:				i += snprint(buf+i, sizeof(buf)-i, "tunnel %s: ", port->name);				break;			}			ingood = port->in - port->inmulti - port->inunknown;			outgood = port->out - port->outmulti - port->outunknown;			i += snprint(buf+i, sizeof(buf)-i,				"in=%d(%d:%d:%d) out=%d(%d:%d:%d:%d)\n",				port->in, ingood, port->inmulti, port->inunknown,				port->out, outgood, port->outmulti,				port->outunknown, port->outfrag);			USED(i);		}		n = readstr(off, a, n, buf);		qunlock(b);		return n;	case Qbctl:		snprint(buf, sizeof(buf), "%s tcpmss\ndelay %ld %ld\n",			b->tcpmss ? "set" : "clear", b->delay0, b->delayn);		n = readstr(off, a, n, buf);		return n;	case Qcache:		n = readstr(off, a, n, c->aux);		return n;	case Qstats:		snprint(buf, sizeof(buf), "hit=%uld miss=%uld copy=%uld\n",			b->hit, b->miss, b->copy);		n = readstr(off, a, n, buf);		return n;	}} static voidbridgeoption(Bridge *b, char *option, int value){	if(strcmp(option, "tcpmss") == 0)		b->tcpmss = value;	else		error("unknown bridge option");}  static longbridgewrite(Chan *c, void *a, long n, vlong off){	Bridge *b = bridgetab + c->dev;	Cmdbuf *cb;	char *arg0, *p;		USED(off);	switch(TYPE(c->qid)) {	default:		error(Eperm);	case Qbctl:		cb = parsecmd(a, n);		qlock(b);		if(waserror()) {			qunlock(b);			free(cb);			nexterror();		}		if(cb->nf == 0)			error("short write");		arg0 = cb->f[0];		if(strcmp(arg0, "bind") == 0) {			portbind(b, cb->nf-1, cb->f+1);		} else if(strcmp(arg0, "unbind") == 0) {			portunbind(b, cb->nf-1, cb->f+1);		} else if(strcmp(arg0, "cacheflush") == 0) {			log(b, Logcache, "cache flush\n");			memset(b->cache, 0, CacheSize*sizeof(Centry));		} else if(strcmp(arg0, "set") == 0) {			if(cb->nf != 2)				error("usage: set option");			bridgeoption(b, cb->f[1], 1);		} else if(strcmp(arg0, "clear") == 0) {			if(cb->nf != 2)				error("usage: clear option");			bridgeoption(b, cb->f[1], 0);		} else if(strcmp(arg0, "delay") == 0) {			if(cb->nf != 3)				error("usage: delay delay0 delayn");			b->delay0 = strtol(cb->f[1], nil, 10);			b->delayn = strtol(cb->f[2], nil, 10);		} else			error("unknown control request");		poperror();		qunlock(b);		free(cb);		return n;	case Qlog:		cb = parsecmd(a, n);		p = logctl(b, cb->nf, cb->f, logflags);		free(cb);		if(p != nil)			error(p);		return n;	}} static intbridgegen(Chan *c, char *, Dirtab*, int, int s, Dir *dp){	Bridge *b = bridgetab + c->dev;	int type = TYPE(c->qid);	Dirtab *dt;	Qid qid; 	if(s  == DEVDOTDOT){		switch(TYPE(c->qid)){		case Qtopdir:		case Qbridgedir:			snprint(up->genbuf, sizeof(up->genbuf), "#B%ld", c->dev);			mkqid(&qid, Qtopdir, 0, QTDIR);			devdir(c, qid, up->genbuf, 0, eve, 0555, dp);			break;		case Qportdir:			snprint(up->genbuf, sizeof(up->genbuf), "bridge%ld", c->dev);			mkqid(&qid, Qbridgedir, 0, QTDIR);			devdir(c, qid, up->genbuf, 0, eve, 0555, dp);			break;		default:			panic("bridgewalk %llux", c->qid.path);		}		return 1;	} 	switch(type) {	default:		/* non-directory entries end up here */		if(c->qid.type & QTDIR)			panic("bridgegen: unexpected directory");			if(s != 0)			return -1;		dt = dirtab[TYPE(c->qid)];		if(dt == nil)			panic("bridgegen: unknown type: %lud", TYPE(c->qid));		devdir(c, c->qid, dt->name, dt->length, eve, dt->perm, dp);		return 1;	case Qtopdir:		if(s != 0)			return -1;		snprint(up->genbuf, sizeof(up->genbuf), "bridge%ld", c->dev);		mkqid(&qid, QID(0, Qbridgedir), 0, QTDIR);		devdir(c, qid, up->genbuf, 0, eve, 0555, dp);		return 1;	case Qbridgedir:		if(s<nelem(bridgedirtab)) {			dt = bridgedirtab+s;			devdir(c, dt->qid, dt->name, dt->length, eve, dt->perm, dp);			return 1;		}		s -= nelem(bridgedirtab);		if(s >= b->nport)			return -1;		mkqid(&qid, QID(s, Qportdir), 0, QTDIR);		snprint(up->genbuf, sizeof(up->genbuf), "%d", s);		devdir(c, qid, up->genbuf, 0, eve, 0555, dp);		return 1;	case Qportdir:		if(s>=nelem(portdirtab))			return -1;		dt = portdirtab+s;		mkqid(&qid, QID(PORT(c->qid),TYPE(dt->qid)), 0, QTFILE);		devdir(c, qid, dt->name, dt->length, eve, dt->perm, dp);		return 1;	}} // parse mac address; also in netif.cstatic intparseaddr(uchar *to, char *from, int alen){	char nip[4];	char *p;	int i; 

Lines 1–500 of 1206.