~kris/9p

9hist

2a10e87cea7981243b71a597ec462a96d0a4ef82 — David du Colombier 27 years ago c0e72a1
Plan 9 from Bell Labs 1999-03-16
M boot/boot.c => boot/boot.c +5 -1
@@ 117,10 117,14 @@ boot(int argc, char *argv[])
	 */
	if(bind("/", "/", MREPL) < 0)
		fatal("bind /");
	if(mount(fd, "/", MAFTER|MCREATE, "") < 0)
	if(mount(fd, "/root", MREPL|MCREATE, "") < 0)
		fatal("mount /");
	if(bind(rootdir, "/", MAFTER|MCREATE) < 0)
		fatal("bind /");
	close(fd);

	setenv("rootdir", rootdir);

	/*
	 *  if a local file server exists and it's not
	 *  running, start it and mount it onto /n/kfs

M boot/boot.h => boot/boot.h +1 -0
@@ 9,6 9,7 @@ struct Method
};

extern char*	bootdisk;
extern char*	rootdir;
extern int	(*cfs)(int);
extern int	cpuflag;
extern char	cputype[];

M ip/devip.c => ip/devip.c +9 -2
@@ 565,8 565,13 @@ ipread(Chan *ch, void *a, long n, vlong off)
		return rv;
	case Qremote:
		buf = smalloc(Statelen);
		c = f->p[PROTO(ch->qid)]->conv[CONV(ch->qid)];
		sprint(buf, "%I!%d\n", c->raddr, c->rport);
		x = f->p[PROTO(ch->qid)];
		c = x->conv[CONV(ch->qid)];
		if(x->remote == nil) {
			sprint(buf, "%I!%d\n", c->raddr, c->rport);
		} else {
			(*x->remote)(c, buf, Statelen-2);
		}
		rv = readstr(offset, p, n, buf);
		free(buf);
		return rv;


@@ 905,6 910,8 @@ ipwrite(Chan* ch, char* a, long n, vlong)
			free(cb);
			nexterror();
		}
		if(cb->nf < 1)
			error("short control request");
		if(strcmp(cb->f[0], "connect") == 0)
			connectctlmsg(x, c, cb);
		else if(strcmp(cb->f[0], "announce") == 0)

A ip/esp.c => ip/esp.c +645 -0
@@ 0,0 1,645 @@
#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"../port/error.h"

#include	"ip.h"

#include	"libcrypt.h"

typedef struct Esphdr Esphdr;
typedef struct Esptail Esptail;
typedef struct Userhdr Userhdr;
typedef struct Esppriv Esppriv;
typedef struct Espcb Espcb;
typedef struct Algorithm Algorithm;

#define DPRINT if(0)print

enum
{
	IP_ESPPROTO	= 50,
	EsphdrSize	= 28,	// includes IP header
	IphdrSize	= 20,	// options have been striped
	EsptailSize	= 2,	// does not include pad or auth data
	UserhdrSize	= 4,	// user visable header size - if enabled
};

struct Esphdr
{
	/* ip header */
	uchar	vihl;		/* Version and header length */
	uchar	tos;		/* Type of service */
	uchar	length[2];	/* packet length */
	uchar	id[2];		/* Identification */
	uchar	frag[2];	/* Fragment information */
	uchar	Unused;	
	uchar	espproto;	/* Protocol */
	uchar	espplen[2];	/* Header plus data length */
	uchar	espsrc[4];	/* Ip source */
	uchar	espdst[4];	/* Ip destination */

	/* esp header */
	uchar	espspi[4];	/* Security parameter index */
	uchar	espseq[4];	/* Sequence number */
};

struct Esptail
{
	uchar	pad;
	uchar	nexthdr;
};

/* header as seen by the user */
struct Userhdr
{
	uchar	nexthdr;	// next protocol
	uchar	unused[3];
};

struct Esppriv
{
	ulong	in;
	ulong	inerrors;
};

/*
 *  protocol specific part of Conv
 */
struct Espcb
{
	int	incoming;
	int	header;		// user user level header
	ulong	spi;
	ulong	seq;		// last seq sent
	ulong	window;		// for replay attacks
	char	*espalg;
	void	*espstate;	// other state for esp
	int	espivlen;	// in bytes
	int	espblklen;
	int	(*cipher)(Espcb*, uchar *buf, int len);
	char	*ahalg;
	void	*ahstate;	// other state for esp
	int	ahlen;		// auth data length in bytes
	int	ahblklen;
	int	(*auth)(Espcb*, uchar *buf, int len, uchar *hash);
};

struct Algorithm
{
	char 	*name;
	int	keylen;		// in bits
	void	(*init)(Espcb*, char* name, uchar *key, int keylen);
};

static	Conv* convlookup(Proto *esp, ulong spi);
static	char *setalg(Espcb *ecb, char **f, int n, Algorithm *alg);
static	void nullespinit(Espcb*, char*, uchar *key, int keylen);
static	void nullahinit(Espcb*, char*, uchar *key, int keylen);
static	void desespinit(Espcb *ecb, char *name, uchar *k, int n);

static Algorithm espalg[] =
{
	"null",			0,	nullespinit,
	"des_56_cbc",		64,	desespinit,
	nil,			0,	nil,
};

static Algorithm ahalg[] =
{
	"null",			0,	nullahinit,
	nil,			0,	nil,
};

static char*
espconnect(Conv *c, char **argv, int argc)
{
	char *p, *pp;
	char *e = nil;
	ulong spi;
	Espcb *ecb = (Espcb*)c->ptcl;

	qlock(c);
	switch(argc) {
	default:
		e = "bad args to connect";
		break;
	case 2:
		p = strchr(argv[1], '!');
		if(p == nil){
			e = "malformed address";
			break;
		}
		*p++ = 0;
		parseip(c->raddr, argv[1]);
		findlocalip(c->p->f, c->laddr, c->raddr);
		ecb->incoming = 0;
		ecb->seq = 0;
		if(strcmp(p, "*") == 0) {
			qlock(c->p);
			for(;;) {
				spi = nrand(1<<16) + 256;
				if(convlookup(c->p, spi) == nil)
					break;
			}
			qunlock(c->p);
			ecb->spi = spi;
			ecb->incoming = 1;
			qhangup(c->wq, nil);
		} else {
			spi = strtoul(p, &pp, 10);
			if(pp == p) {
				e = "malformed address";
				break;
			}
			ecb->spi = spi;
			qhangup(c->rq, nil);
		}
		nullespinit(ecb, "null", nil, 0);
		nullahinit(ecb, "null", nil, 0);
	}
	qunlock(c);
	Fsconnected(c, e);

	return e;
}


static int
espstate(Conv *c, char *state, int n)
{
	return snprint(state, n, "%s", c->inuse?"Open\n":"Closed\n");
}

static void
espcreate(Conv *c)
{
	c->rq = qopen(64*1024, 1, 0, 0);
	c->wq = qopen(64*1024, 0, 0, 0);
}

static void
espclose(Conv *c)
{
	Espcb *ecb;

	qclose(c->rq);
	qclose(c->wq);
	qclose(c->eq);
	ipmove(c->laddr, IPnoaddr);
	ipmove(c->raddr, IPnoaddr);

	ecb = (Espcb*)c->ptcl;
	free(ecb->espstate);
	free(ecb->ahstate);
	memset(ecb, 0, sizeof(Espcb));
	
	qunlock(c);
}

void
espkick(Conv *c, int)
{
	Esphdr *eh;
	Esptail *et;
	Userhdr *uh;
	Espcb *ecb;
	Block *bp;
	int nexthdr;
	int payload;
	int pad;
	int align;
	uchar *auth;

	bp = qget(c->wq);
	if(bp == nil)
		return;

	qlock(c);
	ecb = c->ptcl;

	if(ecb->header) {
		/* make sure the message has a User header */
		bp = pullupblock(bp, UserhdrSize);
		if(bp == nil) {
			qunlock(c);
			return;
		}
		uh = (Userhdr*)bp->rp;
		nexthdr = uh->nexthdr;
		bp->rp += UserhdrSize;
	} else {
		nexthdr = 0;  // what should this be?
	}

	payload = BLEN(bp) + ecb->espivlen;

	/* Make space to fit ip header */
	bp = padblock(bp, EsphdrSize + ecb->espivlen);

	align = 4;
	if(ecb->espblklen > align)
		align = ecb->espblklen;
	if(align % ecb->ahblklen != 0)
		panic("espkick: ahblklen is important after all");
	pad = (align-1) - (payload + EsptailSize-1)%align;

	/*
	 * Make space for tail
	 * this is done by calling padblock with a negative size
	 * Padblock does not change bp->wp!
	 */
	bp = padblock(bp, -(pad+EsptailSize+ecb->ahlen));
	bp->wp += pad+EsptailSize+ecb->ahlen;

	eh = (Esphdr *)(bp->rp);
	et = (Esptail*)(bp->rp + EsphdrSize + payload + pad);

	// fill in tail
	et->pad = pad;
	et->nexthdr = nexthdr;

	ecb->cipher(ecb, bp->rp+EsphdrSize, payload+pad+EsptailSize);
	auth = bp->rp + EsphdrSize + payload + pad + EsptailSize;
	ecb->auth(ecb, bp->rp+IphdrSize, (EsphdrSize-IphdrSize)+payload+pad+EsptailSize, auth);

	// fill in head
	hnputl(eh->espspi, ecb->spi);
	hnputl(eh->espseq, ++ecb->seq);
	v6tov4(eh->espsrc, c->laddr);
	v6tov4(eh->espdst, c->raddr);
	eh->espproto = IP_ESPPROTO;
	eh->frag[0] = 0;
	eh->frag[1] = 0;

	qunlock(c);
//print("esp: pass down: %uld\n", BLEN(bp));
	ipoput(c->p->f, bp, 0, c->ttl);
}

void
espiput(Proto *esp, uchar*, Block *bp)
{
	Esphdr *eh;
	Esptail *et;
	Userhdr *uh;
	Conv *c;
	Espcb *ecb;
	uchar raddr[IPaddrlen], laddr[IPaddrlen];
	Fs *f;
	uchar *auth;
	ulong spi;
	int payload, nexthdr;

	f = esp->f;

	bp = pullupblock(bp, EsphdrSize+EsptailSize);
	if(bp == nil) {
		netlog(f, Logesp, "esp: short packet\n");
		return;
	}

	eh = (Esphdr*)(bp->rp);
	spi = nhgetl(eh->espspi);
	v4tov6(raddr, eh->espsrc);
	v4tov6(laddr, eh->espdst);

	qlock(esp);
	/* Look for a conversation structure for this port */
	c = convlookup(esp, spi);
	if(c == nil) {
		qunlock(esp);
		netlog(f, Logesp, "esp: no conv %I -> %I!%d\n", raddr,
			laddr, spi);
		icmpnoconv(f, bp);
		freeblist(bp);
		return;
	}

	qlock(c);
	qunlock(esp);

	ecb = c->ptcl;
	// too hard to do decryption/authentication on block lists
	if(bp->next)
		bp = concatblock(bp);

	if(BLEN(bp) < EsphdrSize + ecb->espivlen + EsptailSize + ecb->ahlen) {
		qunlock(esp);
		netlog(f, Logesp, "esp: short block %I -> %I!%d\n", raddr,
			laddr, spi);
		freeb(bp);
		return;
	}

	eh = (Esphdr*)(bp->rp);
	auth = bp->wp - ecb->ahlen;
	if(!ecb->auth(ecb, eh->espspi, auth-eh->espspi, auth)) {
		qunlock(esp);
		netlog(f, Logesp, "esp: bad auth %I -> %I!%d\n", raddr,
			laddr, spi);
		freeb(bp);
		return;
	}

	payload = BLEN(bp)-EsphdrSize-ecb->ahlen;
	if(payload<=0 || payload%4 != 0 || payload%ecb->espblklen!=0) {
		qunlock(esp);
		netlog(f, Logesp, "esp: bad length %I -> %I!%d payload=%d BLEN=%d\n", raddr,
			laddr, spi, payload, BLEN(bp));
		freeb(bp);
		return;
	}
	ecb->cipher(ecb, bp->rp+EsphdrSize, payload);

	payload -= EsptailSize;
	et = (Esptail*)(bp->rp + EsphdrSize + payload);
	payload -= et->pad + ecb->espivlen;
	nexthdr = et->nexthdr;
	if(payload <= 0) {
		qunlock(esp);
		netlog(f, Logesp, "esp: short packet after decrypt %I -> %I!%d\n", raddr,
			laddr, spi);
		freeb(bp);
		return;
	}

	// trim packet
	bp->rp += EsphdrSize + ecb->espivlen;
	bp->wp = bp->rp + payload;
	if(ecb->header) {
		// assume UserhdrSize < EsphdrSize
		bp->rp -= UserhdrSize;
		uh = (Userhdr*)bp->rp;
		memset(uh, 0, UserhdrSize);
		uh->nexthdr = nexthdr;
	}

	if(qfull(c->rq)){
		netlog(f, Logesp, "esp: qfull %I -> %I.%uld\n", raddr,
			laddr, spi);
		freeblist(bp);
	}else {
//print("esp: pass up: %uld\n", BLEN(bp));
		qpass(c->rq, bp);
	}

	qunlock(c);
}

char*
espctl(Conv *c, char **f, int n)
{
	Espcb *ecb = c->ptcl;
	char *e = nil;

	qlock(c);
	if(strcmp(f[0], "esp") == 0)
		e = setalg(ecb, f, n, espalg);
	else if(strcmp(f[0], "ah") == 0)
		e = setalg(ecb, f, n, ahalg);
	else if(strcmp(f[0], "header") == 0)
		ecb->header = 1;
	else if(strcmp(f[0], "noheader") == 0)
		ecb->header = 0;
	else
		e = "unknown control request";
	qunlock(c);
	return e;
}

void
espadvise(Proto *esp, Block *bp, char *msg)
{
	Esphdr *h;
	Conv *c;
	ulong spi;

	h = (Esphdr*)(bp->rp);

	spi = nhgets(h->espspi);
	qlock(esp);
	c = convlookup(esp, spi);
	if(c != nil) {
		qhangup(c->rq, msg);
		qhangup(c->wq, msg);
	}
	qunlock(esp);
	freeblist(bp);
}

int
espstats(Proto *esp, char *buf, int len)
{
	Esppriv *upriv;

	upriv = esp->priv;
	return snprint(buf, len, "%lud %lud\n",
		upriv->in,
		upriv->inerrors);
}

static int
esplocal(Conv *c, char *buf, int len)
{
	Espcb *ecb = c->ptcl;
	int n;

	qlock(c);
	if(ecb->incoming)
		n = snprint(buf, len, "%I!%uld\n", c->laddr, ecb->spi);
	else
		n = snprint(buf, len, "%I\n", c->laddr);
	qunlock(c);
	return n;
}

static int
espremote(Conv *c, char *buf, int len)
{
	Espcb *ecb = c->ptcl;
	int n;

	qlock(c);
	if(ecb->incoming)
		n = snprint(buf, len, "%I\n", c->raddr);
	else
		n = snprint(buf, len, "%I!%uld\n", c->raddr, ecb->spi);
	qunlock(c);
	return n;
}

// should have esp locked
static	Conv*
convlookup(Proto *esp, ulong spi)
{
	Conv *c, **p;
	Espcb *ecb;

	for(p=esp->conv; *p; p++){
		c = *p;
		ecb = c->ptcl;
		if(ecb->incoming && ecb->spi == spi)
			return c;
	}
}

static char *
setalg(Espcb *ecb, char **f, int n, Algorithm *alg)
{
	uchar *key;
	int i, nbyte, nchar;
	int c;

	if(n < 2)
		return "bad format";
	for(; alg->name; alg++)
		if(strcmp(f[1], alg->name) == 0)
			break;
	if(alg->name == nil)
		return "unknown algorithm";

	if(n != 3)
		return "bad format";
	nbyte = (alg->keylen + 7) >> 3;
	nchar = strlen(f[2]);
	for(i=0; i<nchar; i++) {
		c = f[2][i];
		if(c >= '0' && c <= '9')
			f[2][i] -= '0';
		else if(c >= 'a' && c <= 'f')
			f[2][i] -= 'a'-10;
		else if(c >= 'A' && c <= 'F')
			f[2][i] -= 'A'-10;
		else
			return "bad character in key";
	}
	key = smalloc(nbyte);
	for(i=0; i<nchar && i*2<nbyte; i++) {
		c = f[2][nchar-i-1];
		if(i&1)
			c <<= 4;
		key[i>>1] |= c;
	}

	alg->init(ecb, alg->name, key, alg->keylen);
	free(key);
	return nil;
}

static int
nullcipher(Espcb*, uchar*, int)
{
	return 1;
}

static void
nullespinit(Espcb *ecb, char *name, uchar*, int)
{
	ecb->espalg = name;
	ecb->espblklen = 1;
	ecb->espivlen = 0;
	ecb->cipher = nullcipher;
}

static int
nullauth(Espcb*, uchar*, int, uchar*)
{
	return 1;
}

static void
nullahinit(Espcb *ecb, char *name, uchar*, int)
{
	ecb->ahalg = name;
	ecb->ahblklen = 1;
	ecb->ahlen = 0;
	ecb->auth = nullauth;
}

static int
descipher(Espcb *ecb, uchar *p, int n)
{
	uchar tmp[8];
	uchar *pp, *tp, *ip, *eip, *ep;
	DESstate *ds = ecb->espstate;

	ep = p + n;
	if(ecb->incoming) {
		memmove(ds->ivec, p, 8);
		p += 8;
		while(p < ep){
			memmove(tmp, p, 8);
			block_cipher(ds->expanded, p, 1);
			tp = tmp;
			ip = ds->ivec;
			for(eip = ip+8; ip < eip; ){
				*p++ ^= *ip;
				*ip++ = *tp++;
			}
		}
	} else {
		memmove(p, ds->ivec, 8);
		for(p += 8; p < ep; p += 8){
			pp = p;
			ip = ds->ivec;
			for(eip = ip+8; ip < eip; )
				*pp++ ^= *ip++;
			block_cipher(ds->expanded, p, 0);
			memmove(ds->ivec, p, 8);
		}
	}
	return 1;
}
	
static void
desespinit(Espcb *ecb, char *name, uchar *k, int n)
{
	uchar key[8];
	uchar ivec[8];
	int i;
	
	// bits to bytes
	n = (n+7)>>3;
	if(n > 8)
		n = 8;
	memset(key, 0, sizeof(key));
	memmove(key, k, n);
	for(i=0; i<8; i++)
		ivec[i] = nrand(256);
	ecb->espalg = name;
	ecb->espblklen = 8;
	ecb->espivlen = 8;
	ecb->cipher = descipher;
	ecb->espstate = smalloc(sizeof(DESstate));
	setupDESstate(ecb->espstate, key, ivec);
}
	
void
espinit(Fs *fs)
{
	Proto *esp;

	esp = smalloc(sizeof(Proto));
	esp->priv = smalloc(sizeof(Esppriv));
	esp->name = "esp";
	esp->kick = espkick;
	esp->connect = espconnect;
	esp->announce = nil;
	esp->ctl = espctl;
	esp->state = espstate;
	esp->create = espcreate;
	esp->close = espclose;
	esp->rcv = espiput;
	esp->advise = espadvise;
	esp->stats = espstats;
	esp->local = esplocal;
	esp->remote = espremote;
	esp->ipproto = IP_ESPPROTO;
	esp->nc = Nchans;
	esp->ptclsize = sizeof(Espcb);

	Fsproto(fs, esp);
}

M ip/ip.h => ip/ip.h +4 -13
@@ 11,7 11,7 @@ typedef struct	Ipmulti	Ipmulti;
typedef struct	Ipmux	Ipmux;
typedef struct	IProuter IProuter;
typedef struct	Ipifc	Ipifc;
typedef struct	Log	Log;
typedef struct	Netlog	Netlog;
typedef struct	Ifclog	Ifclog;
typedef struct	Medium	Medium;
typedef struct	Proto	Proto;


@@ 212,6 212,7 @@ struct Proto
	void		(*advise)(Proto*, Block*, char*);
	int		(*stats)(Proto*, char*, int);
	int		(*local)(Conv*, char*, int);
	int		(*remote)(Conv*, char*, int);
	int		(*inuse)(Conv*);

	Fs		*f;		/* file system this proto is part of */


@@ 258,7 259,7 @@ struct Fs
	Route	*v6root[1<<Lroot];	/* v6 routing forest */
	Route	*queue;			/* used as temp when reinjecting routes */

	Log	*alog;
	Netlog	*alog;
	Ifclog	*ilog;
};



@@ 295,7 296,7 @@ enum
	Logipmsg=	1<<14,
	Logrudp=	1<<15,
	Logrudpmsg=	1<<16,

	Logesp=		1<<17,
};

void	netloginit(Fs*);


@@ 415,14 416,6 @@ extern void	arpenter(Fs*, int version, uchar *ip, uchar *mac, int len, int noref
 * ipaux.c
 */

typedef struct Cmdbuf	Cmdbuf;
struct Cmdbuf
{
	char	buf[64];
	char	*f[16];
	int	nf;
};

extern int	myetheraddr(uchar*, char*);
extern ulong	parseip(uchar*, char*);
extern ulong	parseipmask(uchar*, char*);


@@ 433,7 426,6 @@ extern uchar*	defmask(uchar*);
extern int	isv4(uchar*);
extern void	v4tov6(uchar *v6, uchar *v4);
extern int	v6tov4(uchar *v4, uchar *v6);
extern Cmdbuf*	parsecmd(char *a, int n);
extern int	eipconv(va_list *arg, Fconv *f);

#define	ipcmp(x, y) memcmp(x, y, IPaddrlen)


@@ 498,7 490,6 @@ extern void	(*ipextprotoiput)(Block*);
extern void	ipiput(Fs*, uchar*, Block*);
extern void	ipoput(Fs*, Block*, int, int);
extern int	ipstats(Fs*, char*, int);
extern uchar*	logctl(uchar*);
extern ushort	ptclbsum(uchar*, int);
extern ushort	ptclcsum(Block*, int, int);
extern void	ip_init(Fs*);

M ip/ipaux.c => ip/ipaux.c +0 -20
@@ 361,26 361,6 @@ defmask(uchar *ip)
}

/*
 *  parse a command written to a device
 */
Cmdbuf*
parsecmd(char *p, int n)
{
	Cmdbuf *cb;

	cb = smalloc(sizeof(*cb));
	
	if(n > sizeof(cb->buf)-1)
		n = sizeof(cb->buf)-1;
	memmove(cb->buf, p, n);
	if(n > 0 && cb->buf[n-1] == '\n')
		n--;
	cb->buf[n] = '\0';
	cb->nf = parsefields(cb->buf, cb->f, nelem(cb->f), " ");
	return cb;
}

/*
 *  parse a hex mac address
 */
int

M ip/netlog.c => ip/netlog.c +7 -6
@@ 13,7 13,7 @@ enum {
/*
 *  action log
 */
struct Log {
struct Netlog {
	Lock;
	int	opens;
	char*	buf;


@@ 29,12 29,12 @@ struct Log {
	Rendez;
};

typedef struct Logflag {
typedef struct Netlogflag {
	char*	name;
	int	mask;
} Logflag;
} Netlogflag;

static Logflag flags[] =
static Netlogflag flags[] =
{
	{ "ppp",	Logppp, },
	{ "ip",		Logip, },


@@ 49,6 49,7 @@ static Logflag flags[] =
	{ "tcpmsg",	Logtcp|Logtcpmsg, },
	{ "udpmsg",	Logudp|Logudpmsg, },
	{ "ipmsg",	Logip|Logipmsg, },
	{ "esp",	Logesp, },
	{ nil,		0, },
};



@@ 57,7 58,7 @@ static char Ebadnetctl[] = "unknown netlog ctl message";
void
netloginit(Fs *f)
{
	f->alog = smalloc(sizeof(Log));
	f->alog = smalloc(sizeof(Netlog));
}

void


@@ 158,7 159,7 @@ char*
netlogctl(Fs *f, char* s, int len)
{
	int i, n, set;
	Logflag *fp;
	Netlogflag *fp;
	char *fields[10], *p, buf[256];

	if(len == 0)

M pc/devether.c => pc/devether.c +12 -4
@@ 134,7 134,7 @@ etheriq(Ether* ether, Block* bp, int freebp)
{
	Etherpkt *pkt;
	ushort type;
	int len;
	int len, multi, forme;
	Netfile **ep, *f, **fp, *fx;
	Block *xbp;



@@ 146,8 146,9 @@ etheriq(Ether* ether, Block* bp, int freebp)
	fx = 0;
	ep = &ether->f[Ntypes];

	multi = pkt->d[0] & 1;
	/* check for valid multcast addresses */
	if((pkt->d[0] & 1) && memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) && ether->prom == 0){
	if(multi && memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) && ether->prom == 0){
		if(!activemulti(ether, pkt->d, sizeof(pkt->d))){
			if(freebp){
				freeb(bp);


@@ 157,6 158,9 @@ etheriq(Ether* ether, Block* bp, int freebp)
		}
	}

	// is it for me?
	forme = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0;

	/*
	 * Multiplex the packet to all the connections which want it.
	 * If the packet is not to be used subsequently (freebp != 0),


@@ 164,7 168,7 @@ etheriq(Ether* ether, Block* bp, int freebp)
	 * saving a copy of the data (usual case hopefully).
	 */
	for(fp = ether->f; fp < ep; fp++){
		if((f = *fp) && (f->type == type || f->type < 0)){
		if((f = *fp) && (f->type == type || f->type < 0) && (forme || multi || f->prom)){
			if(f->type > -2){
				if(freebp && fx == 0)
					fx = f;


@@ 197,7 201,7 @@ etheriq(Ether* ether, Block* bp, int freebp)
static int
etheroq(Ether* ether, Block* bp)
{
	int len, loopback, s;
	int len, loopback, s, mine;
	Etherpkt *pkt;

	ether->outpackets++;


@@ 208,10 212,14 @@ etheroq(Ether* ether, Block* bp)
	 * in promiscuous mode.
	 * If it's a loopback packet indicate to etheriq that the data isn't
	 * needed and return, etheriq will pass-on or free the block.
	 * To enable bridging to work, only packets that were originated
	 * by this interface are feedback
	 */
	pkt = (Etherpkt*)bp->rp;
	len = BLEN(bp);
	loopback = (memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0);
	mine = memcmp(pkt->s, ether->ea, sizeof(pkt->s)) == 0;
	if(mine)
	if(loopback || memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) == 0 || ether->prom){
		s = splhi();
		etheriq(ether, bp, loopback);

A port/devbridge.c => port/devbridge.c +907 -0
@@ 0,0 1,907 @@
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/netif.h"
#include "../port/error.h"

typedef struct Bridge 	Bridge;
typedef struct Port 	Port;
typedef struct Centry	Centry;

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=	16,		// 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

	Addrlen=	16,		// must be long enough of IP addr and ether addr
};

Dirtab bridgedirtab[]={
	"ctl",		{Qbctl},	0,	0666,
	"stats",	{Qstats},	0,	0444,
	"cache",	{Qcache},	0,	0444,
	"log",		{Qlog},		0,	0666,
};

Dirtab portdirtab[]={
	"ctl",		{Qpctl},	0,	0666,
	"local",	{Qlocal},	0,	0444,
	"status",	{Qstatus},	0,	0444,
};

enum {
	Logcache=	(1<<0),
	Logmcast=	(1<<1),
};

// types of interfaces
enum
{
	Tether,
	Ttunnel,
};

static Logflag logflags[] =
{
	{ "cache",	Logcache, },
	{ "multicast",	Logmcast, },
	{ nil,		0, },
};

static Dirtab	*dirtab[MaxQ];

#define TYPE(x) 	((x).path & 0xff)
#define PORT(x) 	(((x).path >> 8)&(Maxport-1))
#define QID(x, y) 	(((x)<<8) | (y))

struct Centry
{
	uchar	d[Eaddrlen];
	int	port;
	long	expire;		// entry expires this number of seconds after bootime
	long	src;
	long	dst;
};

struct Bridge
{
	QLock;
	int	nport;
	Port	*port[Maxport];
	Centry	cache[CacheSize];
	int	hit;
	int	miss;

	Log;
};

struct Port
{
	int	id;
	Bridge	*bridge;
	int	ref;
	int	closed;

	Chan	*data[2];	// channel to data

	int	mcast;		// send multi cast packets
	Proc	*readp;		// read proc
	
	// the following uniquely identifies the port
	int	type;
	uchar	addr[Addrlen];
	
	// 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	nentry;		// number of cache entries for this port
};

static Bridge bridgetab[Maxbridge];

static int m2p[] = {
	[OREAD]		4,
	[OWRITE]	2,
	[ORDWR]		6
};

static int	bridgegen(Chan *c, 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);

extern ulong	parseip(uchar*, char*);

static void
bridgeinit(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);
	c->qid = (Qid){QID(0, Qtopdir)|CHDIR, 0};
	c->dev = dev;

	return c;
}

static int
bridgewalk(Chan *c, char *name)
{
	Path *op;

	if(strcmp(name, "..") == 0){
		switch(TYPE(c->qid)){
		case Qtopdir:
		case Qbridgedir:
			c->qid = (Qid){CHDIR|Qtopdir, 0};
			break;
		case Qportdir:
			c->qid = (Qid){CHDIR|Qbridgedir, 0};
			break;
		default:
			panic("bridgewalk %lux", c->qid.path);
		}
		op = c->path;
		c->path = ptenter(&syspt, op, name);
		decref(op);
		return 1;
	}

	return devwalk(c, name, 0, 0, bridgegen);
}

static void
bridgestat(Chan* c, char* db)
{
	devstat(c, db, nil, 0, 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 Qtopdir:
	case Qbridgedir:
	case Qportdir:
	case Qstatus:
	case Qlocal:
	case Qstats:
		if(omode != OREAD)
			error(Eperm);
		break;
	case Qlog:
		logopen(b);
		break;
	case Qcache:
		if(omode != OREAD)
			error(Eperm);
		c->aux = cachedump(b);
		break;
	}
	c->mode = openmode(omode);
	c->flag |= COPEN;
	c->offset = 0;
	return c;
}

static void
bridgeclose(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 long
bridgeread(Chan *c, void *a, long n, vlong off)
{
	char buf[256];
	Bridge *b = bridgetab + c->dev;
	Port *port;
	int i;

	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 %E: ", port->addr);
				break;
			case Ttunnel:
				i += snprint(buf+i, sizeof(buf)-i, "tunnel %I: ", port->addr);
				break;
			}
			i += snprint(buf+i, sizeof(buf)-i, "in=%d/%d/%d out=%d/%d/%d\n",
				port->in, port->inmulti, port->inunknown,
				port->out, port->outmulti, port->outunknown);
			USED(i);
		}
		n = readstr(off, a, n, buf);
		qunlock(b);
		return n;
	case Qcache:
		n = readstr(off, a, n, c->aux);
		return n;
	}
}

static long
bridgewrite(Chan *c, void *a, long n, vlong off)
{
	Bridge *b = bridgetab + c->dev;
	Cmdbuf *cb;
	char *arg0;
	char *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
			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 int
bridgegen(Chan *c, Dirtab*, int, int s, Dir *dp)
{
	Bridge *b = bridgetab + c->dev;
	int type = TYPE(c->qid);
	char buf[32];
	Dirtab *dt;
	Qid qid;

	switch(type) {
	default:
		// non directory entries end up here
		if(c->qid.path & CHDIR)
			panic("bridgegen: unexpected directory");	
		if(s != 0)
			return -1;
		dt = dirtab[TYPE(c->qid)];
		if(dt == nil)
			panic("bridgegen: unknown type: %d", TYPE(c->qid));
		devdir(c, c->qid, dt->name, dt->length, eve, dt->perm, dp);
		return 1;
	case Qtopdir:
		if(s != 0)
			return -1;
		sprint(buf, "bridge%ld", c->dev);
		devdir(c, (Qid){QID(0,Qbridgedir)|CHDIR,0}, buf, 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;
		qid = (Qid){QID(s,Qportdir)|CHDIR, 0};
		snprint(buf, sizeof(buf), "%d", s);
		devdir(c, qid, buf, 0, eve, 0555, dp);
		return 1;
	case Qportdir:
		if(s>=nelem(portdirtab))
			return -1;
		dt = portdirtab+s;
		qid = (Qid){QID(PORT(c->qid),TYPE(dt->qid)),0};
		devdir(c, qid, dt->name, dt->length, eve, dt->perm, dp);
		return 1;
	}
}

// also in netif.c
static int
parseaddr(uchar *to, char *from, int alen)
{
	char nip[4];
	char *p;
	int i;

	p = from;
	for(i = 0; i < alen; i++){
		if(*p == 0)
			return -1;
		nip[0] = *p++;
		if(*p == 0)
			return -1;
		nip[1] = *p++;
		nip[2] = 0;
		to[i] = strtoul(nip, 0, 16);
		if(*p == ':')
			p++;
	}
	return 0;
}

// assumes b is locked
static void
portbind(Bridge *b, int argc, char *argv[])
{
	Port *port;
	char path[8*NAMELEN];
	char buf[100];
	char *dev, *dev2=nil, *p;
	Chan *ctl;
	int type=0, i, n;
	char *usage = "usage: bind ether|tunnel addr ownhash dev [dev2]";
	uchar addr[Addrlen];
	ulong ownhash;

	memset(addr, 0, Addrlen);
	if(argc < 4)
		error(usage);
	if(strcmp(argv[0], "ether") == 0) {
		if(argc != 4)
			error(usage);
		type = Tether;
		parseaddr(addr, argv[1], Eaddrlen);
	} else if(strcmp(argv[0], "tunnel") == 0) {
		if(argc != 5)
			error(usage);
		type = Ttunnel;
		parseip(addr, argv[1]);
		dev2 = argv[4];
	} else
		error(usage);
	ownhash = atoi(argv[2]);
	dev = argv[3];
	for(i=0; i<b->nport; i++) {
		port = b->port[i];
		if(port != nil)
		if(port->type == type)
		if(memcmp(port->addr, addr, Addrlen) == 0)
			error("port in use");
	}
	for(i=0; i<Maxport; i++)
		if(b->port[i] == nil)
			break;
	if(i == Maxport)
		error("no more ports");
	port = smalloc(sizeof(Port));
	port->ref = 1;
	port->id = i;
	port->ownhash = ownhash;

	if(waserror()) {
		portfree(port);
		nexterror();
	}

	port->type = type;
	memmove(port->addr, addr, Addrlen);
	switch(port->type) {
	default: panic("portbind: unknown port type: %d", type);
	case Tether:
		snprint(path, sizeof(path), "%s/clone", dev);
		ctl = namec(path, Aopen, ORDWR, 0);
		if(waserror()) {
			cclose(ctl);
			nexterror();
		}
		// check addr?

		// get directory name
		n = devtab[ctl->type]->read(ctl, buf, sizeof(buf), 0);
		buf[n] = 0;
		for(p = buf; *p == ' '; p++)
			;
		snprint(path, sizeof(path), "%s/%lud/data", dev, strtoul(p, 0, 0));

		// setup connection to be promiscuous
		snprint(buf, sizeof(buf), "connect -1");
		devtab[ctl->type]->write(ctl, buf, strlen(buf), 0);
		snprint(buf, sizeof(buf), "promiscuous");
		devtab[ctl->type]->write(ctl, buf, strlen(buf), 0);

		// open data port
		port->data[0] = namec(path, Aopen, ORDWR, 0);
		// dup it
		incref(port->data[0]);
		port->data[1] = port->data[0];

		poperror();
		cclose(ctl);		

		break;
	case Ttunnel:
		port->data[0] = namec(dev, Aopen, OREAD, 0);
		port->data[1] = namec(dev2, Aopen, OWRITE, 0);
		break;
	}

	poperror();

	// commited to binding port
	b->port[port->id] = port;
	port->bridge = b;
	if(b->nport <= port->id)
		b->nport = port->id+1;

	// assumes kproc always succeeds
	port->ref++;
	kproc("etherread", etherread, port);	// poperror must be next
}

// assumes b is locked
static void
portunbind(Bridge *b, int argc, char *argv[])
{
	Port *port=nil;
	int type=0, i;
	char *usage = "usage: unbind ether|tunnel addr [ownhash]";
	uchar addr[Addrlen];
	ulong ownhash;

	memset(addr, 0, Addrlen);
	if(argc < 2 || argc > 3)
		error(usage);
	if(strcmp(argv[0], "ether") == 0) {
		type = Tether;
		parseaddr(addr, argv[1], Eaddrlen);
	} else if(strcmp(argv[0], "tunnel") == 0) {
		type = Ttunnel;
		parseip(addr, argv[1]);
	} else
		error(usage);
	if(argc == 3)
		ownhash = atoi(argv[2]);
	else
		ownhash = 0;
	for(i=0; i<b->nport; i++) {
		port = b->port[i];
		if(port != nil)
		if(port->type == type)
		if(memcmp(port->addr, addr, Addrlen) == 0)
			break;
	}
	if(i == b->nport)
		error("port not found");
	if(ownhash != 0 && port->ownhash != 0 && ownhash != port->ownhash)
		error("bad owner hash");

	port->closed = 1;
	b->port[i] = nil;	// port is now unbound
	cacheflushport(b, i);

	// try and stop reader
	if(port->readp)
		postnote(port->readp, 1, "unbind", 0);
	portfree(port);
}

// assumes b is locked
static Centry *
cachelookup(Bridge *b, uchar d[Eaddrlen])
{
	int i;
	uint h;
	Centry *p;
	long sec;

	// dont cache multicast or broadcast
	if(d[0] & 1)
		return 0;

	h = 0;
	for(i=0; i<Eaddrlen; i++) {
		h *= 7;
		h += d[i];
	}
	h %= CacheHash;
	p = b->cache + h;
	sec = TK2SEC(m->ticks);
	for(i=0; i<CacheLook; i++,p++) {
		if(memcmp(d, p->d, Eaddrlen) == 0) {
			p->dst++;
			if(sec >= p->expire) {
				log(b, Logcache, "expired cache entry: %E %d\n",
					d, p->port);
				return nil;
			}
			p->expire = sec + CacheTimeout;
			return p;
		}
	}
	log(b, Logcache, "cache miss: %E\n", d);
	return nil;
}

// assumes b is locked
static void
cacheupdate(Bridge *b, uchar d[Eaddrlen], int port)
{
	int i;
	uint h;
	Centry *p, *pp;
	long sec;

	// dont cache multicast or broadcast
	if(d[0] & 1) {
		log(b, Logcache, "bad source address: %E\n", d);
		return;
	}
	
	h = 0;
	for(i=0; i<Eaddrlen; i++) {
		h *= 7;
		h += d[i];
	}
	h %= CacheHash;
	p = b->cache + h;
	pp = p;
	sec = p->expire;

	// look for oldest entry
	for(i=0; i<CacheLook; i++,p++) {
		if(memcmp(p->d, d, Eaddrlen) == 0) {
			p->expire = TK2SEC(m->ticks) + CacheTimeout;
			if(p->port != port) {
				log(b, Logcache, "NIC changed port %d->%d: %E\n",
					p->port, port, d);
				p->port = port;
			}
			p->src++;
			return;
		}
		if(p->expire < sec) {
			sec = p->expire;
			pp = p;
		}
	}
	if(pp->expire != 0)
		log(b, Logcache, "bumping from cache: %E %d\n", pp->d, pp->port);
	pp->expire = TK2SEC(m->ticks) + CacheTimeout;
	memmove(pp->d, d, Eaddrlen);
	pp->port = port;
	pp->src = 1;
	pp->dst = 0;
	log(b, Logcache, "adding to cache: %E %d\n", pp->d, pp->port);
}

// assumes b is locked
static void
cacheflushport(Bridge *b, int port)
{
	Centry *ce;
	int i;

	ce = b->cache;
	for(i=0; i<CacheSize; i++,ce++) {
		if(ce->port != port)
			continue;
		memset(ce, 0, sizeof(Centry));
	}
}

static char *
cachedump(Bridge *b)
{
	int i, n;
	long sec, off;
	char *buf, *p, *ep;
	Centry *ce;
	char c;

	qlock(b);
	if(waserror()) {
		qunlock(b);
		nexterror();
	}
	sec = TK2SEC(m->ticks);
	n = 0;
	for(i=0; i<CacheSize; i++)
		if(b->cache[i].expire != 0)
			n++;
	
	n *= 51;	// change if print format is changed
	n += 10;	// some slop at the end
	buf = malloc(n);
	p = buf;
	ep = buf + n;
	ce = b->cache;
	off = seconds() - sec;
	for(i=0; i<CacheSize; i++,ce++) {
		if(ce->expire == 0)
			continue;	
		c = (sec < ce->expire)?'v':'e';
		p += snprint(p, ep-p, "%E %2d %10ld %10ld %10ld %c\n", ce->d,
			ce->port, ce->src, ce->dst, ce->expire+off, c);
	}
	*p = 0;
	poperror();
	qunlock(b);

	return buf;
}



// assumes b is locked
static void
ethermultiwrite(Bridge *b, Block *bp, Port *port)
{
	Chan *c;
	Block *bp2;
	Etherpkt *ep;
	int i, mcast, bcast;
	static uchar bcastaddr[Eaddrlen] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};

	if(waserror()) {
		if(bp)
			freeb(bp);
		nexterror();
	}
	
	ep = (Etherpkt*)bp->rp;
	mcast = ep->d[0] & 1;
	if(mcast)
		bcast = memcmp(ep->d, bcastaddr, Eaddrlen) == 0;
	else
		bcast = 0;

	c = nil;
	for(i=0; i<b->nport; i++) {
		if(i == port->id || b->port[i] == nil)
			continue;
		if(mcast && !bcast && !b->port[i]->mcast)
			continue;
		b->port[i]->out++;
		if(mcast)
			b->port[i]->outmulti++;
		else
			b->port[i]->outunknown++;

		// delay one so that the last write does not copy
		if(c != nil) {
			// can not use write since it changes scr addr
			bp2 = copyblock(bp, blocklen(bp));
			devtab[c->type]->bwrite(c, bp2, 0);
		}
		c = b->port[i]->data[1];
	}

	// last write free block
	if(c) {
		bp2 = bp; bp = nil; USED(bp);
		devtab[c->type]->bwrite(c, bp2, 0);
	} else
		freeb(bp);

	poperror();
}

/*
 *  process to read from the ethernet
 */
static void
etherread(void *a)
{
	Port *port = a, *oport;
	Bridge *b = port->bridge;
	Block *bp, *bp2;
	Etherpkt *ep;
	Centry *ce;

	
	qlock(b);
	port->readp = up;	/* hide identity under a rock for unbind */

	while(!port->closed){
		// release lock to read - error means it is time to quit
		qunlock(b);
		if(waserror()) {
			qlock(b);
			break;
		}
//print("devbridge: etherread: reading\n");
		bp = devtab[port->data[0]->type]->bread(port->data[0], ETHERMAXTU, 0);
//print("devbridge: etherread: blocklen = %d\n", blocklen(bp));
		poperror();
		qlock(b);

		if(port->closed)
			break;
		if(waserror()) {
			if(bp)
				freeb(bp);
print("devbridge: etherread: %r\n");
			continue;
		}
		if(blocklen(bp) < ETHERMINTU)
			error("short packet");
		port->in++;
		ep = (Etherpkt*)bp->rp;
		cacheupdate(b, ep->s, port->id);

		if(ep->d[0] & 1) {
			log(b, Logmcast, "mulitcast: port=%d src=%E dst=%E type=%#.4ux\n",
				port->id, ep->s, ep->d, (ep->type[0]<<8)|ep->type[1] );
			port->inmulti++;
			bp2 = bp; bp = nil;
			ethermultiwrite(b, bp2, port);
		} else {
			ce = cachelookup(b, ep->d);
			if(ce == nil) {
				b->miss++;
				port->inunknown++;
				bp2 = bp; bp = nil;
				ethermultiwrite(b, bp2, port);
			} else if (ce->port != port->id) {
				b->hit++;
				bp2 = bp; bp = nil;
				oport = b->port[ce->port];
				oport->out++;
				devtab[oport->data[1]->type]->bwrite(oport->data[1], bp2, 0);
			}
		}

		poperror();
		if(bp)
			freeb(bp);
	}
print("etherread: trying to exit\n");
	port->readp = nil;
	portfree(port);
	qunlock(b);
	pexit("hangup", 1);
}

// hold b lock
static void
portfree(Port *port)
{
	port->ref--;
	if(port->ref < 0)
		panic("portfree: bad ref");
	if(port->ref > 0)
		return;

	if(port->data[0])
		cclose(port->data[0]);
	if(port->data[1])
		cclose(port->data[1]);
	memset(port, 0, sizeof(Port));
	free(port);
}

Dev bridgedevtab = {
	'B',
	"bridge",

	devreset,
	bridgeinit,
	bridgeattach,
	devclone,
	bridgewalk,
	bridgestat,
	bridgeopen,
	devcreate,
	bridgeclose,
	bridgeread,
	devbread,
	bridgewrite,
	devbwrite,
	devremove,
	devwstat,
};

M port/devroot.c => port/devroot.c +4 -0
@@ 14,6 14,9 @@ enum{
	Qnet,
	Qnetalt,
	Qrecover,
	Qroot,		/* boot root */

	// add new entries above this
	Qboot,		/* readable files */

	Nfiles=13,	/* max root files */


@@ 30,6 33,7 @@ Dirtab rootdir[Nfiles]={
	"net",		{Qnet|CHDIR},	0,	0777,
	"net.alt",	{Qnetalt|CHDIR},	0,	0777,
	"recover",	{Qrecover},	0,	0777,
	"root",		{Qroot|CHDIR},	0,	0777,
};

static uchar	*rootdata[Nfiles];

A port/log.c => port/log.c +175 -0
@@ 0,0 1,175 @@
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"

static char Ebadlogctl[] = "unknown log ctl message";

enum {
	Nlog		= 4*1024,
};

void
logopen(Log *alog)
{
	lock(alog);
	if(waserror()){
		unlock(alog);
		nexterror();
	}
	if(alog->opens == 0){
		if(alog->buf == nil)
			alog->buf = malloc(Nlog);
		alog->rptr = alog->buf;
		alog->end = alog->buf + Nlog;
	}
	alog->opens++;
	unlock(alog);
	poperror();
}

void
logclose(Log *alog)
{
	lock(alog);
	if(waserror()){
		unlock(alog);
		nexterror();
	}
	alog->opens--;
	if(alog->opens == 0){
		free(alog->buf);
		alog->buf = nil;
	}
	unlock(alog);
	poperror();
}

static int
logready(void *a)
{
	Log *alog = a;

	return alog->len;
}

long
logread(Log *alog, void *a, ulong, long n)
{
	int i, d;
	char *p, *rptr;

	qlock(&alog->readq);
	if(waserror()){
		qunlock(&alog->readq);
		nexterror();
	}

	for(;;){
		lock(alog);
		if(alog->len){
			if(n > alog->len)
				n = alog->len;
			d = 0;
			rptr = alog->rptr;
			alog->rptr += n;
			if(alog->rptr >= alog->end){
				d = alog->rptr - alog->end;
				alog->rptr = alog->buf + d;
			}
			alog->len -= n;
			unlock(alog);

			i = n;
			p = a;
			if(d){
				memmove(p, rptr, d);
				i -= d;
				p += d;
				rptr = alog->buf;
			}
			memmove(p, rptr, i);
			break;
		}
		else
			unlock(alog);

		sleep(&alog->readr, logready, alog);
	}

	qunlock(&alog->readq);
	poperror();

	return n;
}

char*
logctl(Log *alog, int argc, char *argv[], Logflag *flags)
{
	int i, n, set;
	Logflag *fp;

	if(argc < 2)
		return Ebadlogctl;

	if(strcmp("set", argv[0]) == 0)
		set = 1;
	else if(strcmp("clear", argv[0]) == 0)
		set = 0;
	else
		return Ebadlogctl;

	for(i = 1; i < argc; i++){
		for(fp = flags; fp->name; fp++)
			if(strcmp(fp->name, argv[i]) == 0)
				break;
		if(fp->name == nil)
			continue;
		if(set)
			alog->logmask |= fp->mask;
		else
			alog->logmask &= ~fp->mask;
	}

	return nil;
}

void
log(Log *alog, int mask, char *fmt, ...)
{
	char buf[128], *t, *fp;
	int i, n;
	va_list arg;

	if(!(alog->logmask & mask))
		return;

	va_start(arg, fmt);
	n = doprint(buf, buf+sizeof(buf), fmt, arg) - buf;
	va_end(arg);

	if(alog->opens == 0)
		return;

	lock(alog);
	i = alog->len + n - Nlog;
	if(i > 0){
		alog->len -= i;
		alog->rptr += i;
		if(alog->rptr >= alog->end)
			alog->rptr = alog->buf + (alog->rptr - alog->end);
	}
	t = alog->rptr + alog->len;
	fp = buf;
	alog->len += n;
	while(n-- > 0){
		if(t >= alog->end)
			t = alog->buf + (t - alog->end);
		*t++ = *fp++;
	}
	unlock(alog);

	wakeup(&alog->readr);
}

M port/netif.c => port/netif.c +24 -7
@@ 59,17 59,26 @@ netifgen(Chan *c, void *vp, int, int i, Dir *dp)
	/* second level contains clone plus all the conversations */
	t = NETTYPE(c->qid.path);
	if(t == N2ndqid || t == Ncloneqid){
		if(i == 0){
		switch(i) {
		case 0:
			q.path = Ncloneqid;
			devdir(c, q, "clone", 0, eve, 0666, dp);
		}else if(i <= nif->nfile){
			if(nif->f[i-1] == 0)
			break;
		case 1:
			q.path = Naddrqid;
			devdir(c, q, "addr", 0, eve, 0666, dp);
			break;
		default:
			i -= 2;
			if(i >= nif->nfile)
				return -1;
			if(nif->f[i] == 0)
				return 0;
			q.path = CHDIR|NETQID(i-1, N3rdqid);
			sprint(buf, "%d", i-1);
			q.path = CHDIR|NETQID(i, N3rdqid);
			sprint(buf, "%d", i);
			devdir(c, q, buf, 0, eve, 0555, dp);
		}else
			return -1;
			break;
		}
		return 1;
	}



@@ 192,6 201,14 @@ netifread(Netif *nif, Chan *c, void *a, long n, ulong offset)
		n = readstr(offset, a, n, p);
		free(p);
		return n;
	case Naddrqid:
		p = malloc(READSTR);
		j = 0;
		for(i = 0; i < nif->alen; i++)
			j += snprint(p+j, READSTR-j, "%2.2ux", nif->addr[i]);
		n = readstr(offset, a, n, p);
		free(p);
		return n;
	case Ntypeqid:
		f = nif->f[NETID(c->qid.path)];
		return readnum(offset, a, n, f->type, NUMSIZE);

M port/netif.h => port/netif.h +1 -0
@@ 9,6 9,7 @@ enum
	Nmhash=		31,

	Ncloneqid=	1,
	Naddrqid,
	N2ndqid,
	N3rdqid,
	Ndataqid,

A port/parse.c => port/parse.c +44 -0
@@ 0,0 1,44 @@
#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"../port/error.h"

int
parsefields(char *lp, char **fields, int n, char *sep)
{
	int i;

	for(i=0; lp && *lp && i<n; i++){
		while(*lp && strchr(sep, *lp) != 0)
			*lp++=0;
		if(*lp == 0)
			break;
		fields[i]=lp;
		while(*lp && strchr(sep, *lp) == 0)
			lp++;
	}
	return i;
}

/*
 *  parse a command written to a device
 */
Cmdbuf*
parsecmd(char *p, int n)
{
	Cmdbuf *cb;

	cb = smalloc(sizeof(*cb));
	
	if(n > sizeof(cb->buf)-1)
		n = sizeof(cb->buf)-1;
	memmove(cb->buf, p, n);
	if(n > 0 && cb->buf[n-1] == '\n')
		n--;
	cb->buf[n] = '\0';
	cb->nf = parsefields(cb->buf, cb->f, nelem(cb->f), " ");
	return cb;
}


M port/portdat.h => port/portdat.h +33 -0
@@ 1,6 1,7 @@
typedef struct Alarms	Alarms;
typedef struct Block	Block;
typedef struct Chan	Chan;
typedef struct Cmdbuf	Cmdbuf;
typedef struct Crypt	Crypt;
typedef struct Dev	Dev;
typedef struct Dirtab	Dirtab;


@@ 9,6 10,8 @@ typedef struct Evalue	Evalue;
typedef struct Fgrp	Fgrp;
typedef struct Image	Image;
typedef struct List	List;
typedef struct Log	Log;
typedef struct Logflag	Logflag;
typedef struct Mntcache Mntcache;
typedef struct Mount	Mount;
typedef struct Mntrpc	Mntrpc;


@@ 39,6 42,7 @@ typedef struct Target	Target;
typedef struct Waitq	Waitq;
typedef int    Devgen(Chan*, Dirtab*, int, int, Dir*);


#include <auth.h>
#include <fcall.h>



@@ 716,6 720,35 @@ enum
	LRESPROF	= 3,
};

/*
 *  action log
 */
struct Log {
	Lock;
	int	opens;
	char*	buf;
	char	*end;
	char	*rptr;
	int	len;

	int	logmask;	/* mask of things to debug */

	QLock	readq;
	Rendez	readr;
};

struct Logflag {
	char*	name;
	int	mask;
};

struct Cmdbuf
{
	char	buf[64];
	char	*f[16];
	int	nf;
};

extern int nsyscall;

#pragma	varargck	argpos	print	1

M port/portfns.h => port/portfns.h +6 -0
@@ 142,6 142,11 @@ void		ksetenv(char*, char*);
long		latin1(uchar*, int);
void		lock(Lock*);
void		lockinit(void);
void		logopen(Log*);
void		logclose(Log*);
char*		logctl(Log*, int, char**, Logflag*);
long		logread(Log*, void*, ulong, long);
void		log(Log*, int, char*, ...);
Page*		lookpage(Image*, ulong);
void		machinit(void);
void*		mallocz(ulong, int);


@@ 179,6 184,7 @@ void		pagersummary(void);
void		panic(char*, ...);
int		parseether(uchar*, char*);
int		parsefields(char*, char**, int, char*);
Cmdbuf*		parsecmd(char *a, int n);
void		pexit(char*, int);
void		pgrpcpy(Pgrp*, Pgrp*);
void		pgrpnote(ulong, char*, long, int);