~kris/9p

9hist

01c8a087da9151c754a485d64bea5dd0cfaf4cdf — David du Colombier 28 years ago 0999edd
Plan 9 from Bell Labs 1998-03-06
A ip/arp.c => ip/arp.c +314 -0
@@ 0,0 1,314 @@
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"

#include "ip.h"

/*
 *  address resolution tables
 */

enum
{
	NHASH		= (1<<6),
	NCACHE		= 256,

	AOK		= 1,
	AWAIT		= 2,
};

char *arpstate[] =
{
	"UNUSED",
	"OK",
	"WAIT",
};

typedef struct Arpcache Arpcache;
struct Arpcache
{
	QLock;
	Arpent*	hash[NHASH];
	Arpent	cache[NCACHE];
};

Arpcache	arp;

char *Ebadarp = "bad arp";

#define haship(s) ((s)[IPaddrlen-1]%NHASH)

static Arpent*
newarp(uchar *ip, Medium *m)
{
	uint t;
	Block *next, *xp;
	Arpent *a, *e, *f, **l;

	/* find oldest entry */
	e = &arp.cache[NCACHE];
	a = arp.cache;
	t = a->used;
	for(f = a; f < e; f++){
		if(f->used < t){
			t = f->used;
			a = f;
		}
	}

	/* dump waiting packets */
	xp = a->hold;
	a->hold = nil;
	while(xp){
		next = xp->list;
		freeblist(xp);
		xp = next;
	}

	/* take out of current chain */
	l = &arp.hash[haship(a->ip)];
	for(f = *l; f; f = f->hash){
		if(f == a) {
			*l = a->hash;
			break;
		}
		l = &f->hash;
	}

	/* insert into new chain */
	l = &arp.hash[haship(ip)];
	a->hash = *l;
	*l = a;
	memmove(a->ip, ip, sizeof(a->ip));
	a->used = msec;
	a->time = 0;
	a->type = m;

	return a;
}

Arpent*
arpget(Block *bp, int version, Medium *type, uchar *ip, uchar *mac)
{
	int hash;
	Arpent *a;
	uchar v6ip[IPaddrlen];

	if(version == V4) {
		v4tov6(v6ip, ip);
		ip = v6ip;
	}

	qlock(&arp);
	hash = haship(ip);
	for(a = arp.hash[hash]; a; a = a->hash) {
		if(memcmp(ip, a->ip, sizeof(a->ip)) == 0)
		if(type == a->type)
			break;
	}

	if(a == nil){
		a = newarp(ip, type);
		a->state = AWAIT;
	}
	a->used = msec;
	if(a->state == AWAIT){
		if(a->hold)
			a->last->list = bp;
		else
			a->hold = bp;
		a->last = bp;
		bp->list = nil; 
		return a;		/* return with arp qlocked */
	}

	memmove(mac, a->mac, a->type->maclen);
	qunlock(&arp);
	return nil;
}

/*
 * called with arp locked
 */
void
arprelease(Arpent*)
{
	qunlock(&arp);
}

/*
 * called with arp locked
 */
Block*
arpresolve(Arpent *a, Medium *type, uchar *mac)
{
	Block *bp;

	memmove(a->mac, mac, type->maclen);
	a->type = type;
	a->state = AOK;
	a->used = msec;
	bp = a->hold;
	a->hold = nil;
	qunlock(&arp);

	return bp;
}

void
arpenter(Ipifc *ifc, int version, uchar *ip, uchar *mac, Medium *type, int refresh)
{
	Arpent *a;
	Block *bp, *next;
	uchar v6ip[IPaddrlen];

	if(version == V4){
		v4tov6(v6ip, ip);
		ip = v6ip;
	}

	qlock(&arp);
	for(a = arp.hash[haship(ip)]; a; a = a->hash) {
		if(a->type != type || (a->state != AWAIT && a->state != AOK))
			continue;

		if(ipcmp(a->ip, ip) == 0) {
			a->state = AOK;
			memmove(a->mac, mac, type->maclen);
			bp = a->hold;
			a->hold = nil;
			if(version == V4)
				ip += IPv4off;
			qunlock(&arp);
			while(bp) {
				next = bp->list;
				if(ifc != nil){
					if(waserror()){
						runlock(ifc);
						nexterror();
					}
					rlock(ifc);
					if(ifc->m != nil)
						ifc->m->bwrite(ifc, bp, version, ip);
					else
						freeb(bp);
					runlock(ifc);
					poperror();
				} else
					freeb(bp);
				bp = next;
			}
			a->used = msec;
			return;
		}
	}

	/* if nil, we're adding a new entry */
	if(refresh == 0){
		a = newarp(ip, type);
		a->state = AOK;
		a->type = type;
		memmove(a->mac, mac, type->maclen);
	}

	qunlock(&arp);
}

int
arpwrite(char *s, int len)
{
	int n;
	Block *bp;
	Arpent *a;
	char *f[4], buf[256];
	uchar ip[], mac[MAClen];
	Medium *m;

	if(len == 0)
		error(Ebadarp);
	if(len >= sizeof(buf))
		len = sizeof(buf)-1;
	strncpy(buf, s, len);
	buf[len] = 0;
	if(len > 0 && buf[len-1] == '\n')
		buf[len-1] = 0;

	n = parsefields(buf, f, 4, " ");
	if(strcmp(f[0], "flush") == 0){
		qlock(&arp);
		for(a = arp.cache; a < &arp.cache[NCACHE]; a++){
			memset(a->ip, 0, sizeof(a->ip));
			memset(a->mac, 0, sizeof(a->mac));
			a->hash = nil;
			a->state = 0;
			a->used = 0;
			while(a->hold != nil) {
				bp = a->hold->list;
				freeblist(a->hold);
				a->hold = bp;
			}
		}
		memset(arp.hash, 0, sizeof(arp.hash));
		qunlock(&arp);
	} else if(strcmp(f[0], "add") == 0){
		if(n != 4)
			error(Ebadarp);
		m = ipfindmedium(f[1]);
		if(m == nil)
			error(Ebadarp);
		parseip(ip, f[2]);
		parsemac(mac, f[3], m->maclen);
		arpenter(nil, V6, ip, mac, m, 0);
	} else
		error(Ebadarp);

	return len;
}

enum
{
	Alinelen=	66,
};

char *aformat = "%-6.6s %-8.8s %-16.16I %-32.32s\n";

static void
convmac(char *p, uchar *mac, int n)
{
	while(n-- > 0)
		p += sprint(p, "%2.2ux", *mac++);
}

int
arpread(char *p, ulong offset, int len)
{
	Arpent *a;
	int n;
	char mac[2*MAClen+1];

	if(offset % Alinelen)
		return 0;

	offset = offset/Alinelen;
	len = len/Alinelen;

	n = 0;
	for(a = arp.cache; len > 0 && a < &arp.cache[NCACHE]; a++){
		if(a->state == 0)
			continue;
		if(offset > 0){
			offset--;
			continue;
		}
		len--;
		qlock(&arp);
		convmac(mac, a->mac, a->type->maclen);
		n += sprint(p+n, aformat, a->type->name, arpstate[a->state], a->ip, mac);
		qunlock(&arp);
	}

	return n;
}

M ip/devip.c => ip/devip.c +311 -244
@@ 14,13 14,14 @@ enum
	Qtopdir=	1,		/* top level directory */
	Qtopbase,
	Qarp=		Qtopbase,
	Qipifc,
	Qiproute,
	Qiprouter,
	Qlog,

	Qprotodir,			/* directory for a protocol */
	Qprotobase,
	Qclone=		Qprotobase,
	Qstats,

	Qconvdir,			/* directory for a conversation */
	Qconvbase,


@@ 79,7 80,7 @@ ip3gen(Chan *c, int i, Dir *dp)
		q = (Qid){QID(PROTO(c->qid), CONV(c->qid), Qstatus), 0};
		break;
	}
	devdir(c, q, p, 0, cv->owner, 0444, dp);
	devdir(c, q, p, 0, cv->owner, 0666, dp);
	return 1;
}



@@ 91,7 92,11 @@ ip2gen(Chan *c, int i, Dir *dp)
	switch(i) {
	case Qclone:
		q = (Qid){QID(PROTO(c->qid), 0, Qclone), 0};
		devdir(c, q, "clone", 0, network, 0444, dp);
		devdir(c, q, "clone", 0, network, 0666, dp);
		return 1;
	case Qstats:
		q = (Qid){QID(PROTO(c->qid), 0, Qstats), 0};
		devdir(c, q, "stats", 0, network, 0444, dp);
		return 1;
	}	
	return -1;


@@ 110,20 115,20 @@ ip1gen(Chan *c, int i, Dir *dp)
		p = "arp";
		q = (Qid){QID(0, 0, Qarp), 0};
		break;
	case Qipifc:
		p = "ipifc";
		q = (Qid){QID(0, 0, Qipifc), 0};
		break;
	case Qiproute:
		p = "iproute";
		q = (Qid){QID(0, 0, Qiproute), 0};
		break;
	case Qiprouter:
		p = "iprouter";
		q = (Qid){QID(0, 0, Qiprouter), 0};
		break;
	case Qlog:
		p = "log";
		q = (Qid){QID(0, 0, Qlog), 0};
		break;
	}
	devdir(c, q, p, 0, network, 0444, dp);
	devdir(c, q, p, 0, network, 0666, dp);
	return 1;
}



@@ 146,9 151,9 @@ ipgen(Chan *c, Dirtab*, int, int s, Dir *dp)
		s -= fs.np;
		return ip1gen(c, s+Qtopbase, dp);
	case Qarp:
	case Qipifc:
	case Qlog:
	case Qiproute:
	case Qiprouter:
		return ip1gen(c, TYPE(c->qid), dp);
	case Qprotodir:
		if(s < fs.p[PROTO(c->qid)]->ac) {


@@ 161,6 166,7 @@ ipgen(Chan *c, Dirtab*, int, int s, Dir *dp)
		s -= fs.p[PROTO(c->qid)]->ac;
		return ip2gen(c, s+Qprotobase, dp);
	case Qclone:
	case Qstats:
		return ip2gen(c, TYPE(c->qid), dp);
	case Qconvdir:
		return ip3gen(c, s+Qconvbase, dp);


@@ 193,6 199,8 @@ ipinit(void)
	fmtinstall('i', eipconv);
	fmtinstall('I', eipconv);
	fmtinstall('E', eipconv);
	fmtinstall('V', eipconv);
	fmtinstall('M', eipconv);
}

static Chan*


@@ 217,25 225,24 @@ ipwalk(Chan* c, char* name)
{
	Path *op;

	if(strcmp(name, "..") == 0){
		switch(TYPE(c->qid)){
		case Qtopdir:
		case Qprotodir:
			c->qid = (Qid){QID(0, 0, Qtopdir)|CHDIR, 0};
			break;
		case Qconvdir:
			c->qid = (Qid){QID(PROTO(c->qid), 0, Qprotodir)|CHDIR, 0};
			break;
		default:
			panic("ipwalk %lux", c->qid.path);
		}
		op = c->path;
		c->path = ptenter(&syspt, op, name);
		decref(op);
		return 1;
	}
	if(strcmp(name, "..") != 0)
		return devwalk(c, name, nil, 0, ipgen);

	return devwalk(c, name, nil, 0, ipgen);
	switch(TYPE(c->qid)){
	case Qtopdir:
	case Qprotodir:
		c->qid = (Qid){QID(0, 0, Qtopdir)|CHDIR, 0};
		break;
	case Qconvdir:
		c->qid = (Qid){QID(PROTO(c->qid), 0, Qprotodir)|CHDIR, 0};
		break;
	default:
		panic("ipwalk %lux", c->qid.path);
	}
	op = c->path;
	c->path = ptenter(&syspt, op, name);
	decref(op);
	return 1;
}

static void


@@ 272,18 279,22 @@ ipopen(Chan* c, int omode)
	switch(TYPE(c->qid)) {
	default:
		break;
	case Qipifc:
		c->aux = newifcconv();
		break;
	case Qlog:
		netlogopen();
		break;
	case Qiprouter:
		iprouteropen();
		break;
	case Qiproute:
		memmove(c->tag, "none", sizeof(c->tag));
		break;
	case Qtopdir:
	case Qprotodir:
	case Qconvdir:
	case Qstatus:
	case Qremote:
	case Qlocal:
	case Qstats:
		if(omode != OREAD)
			error(Eperm);
		break;


@@ 300,12 311,12 @@ ipopen(Chan* c, int omode)
	case Qctl:
	case Qerr:
		p = fs.p[PROTO(c->qid)];
		lock(p);
		qlock(p);
		cv = p->conv[CONV(c->qid)];
		lock(cv);
		if(waserror()) {
			unlock(cv);
			unlock(p);
			qunlock(p);
			nexterror();
		}
		if((perm & (cv->perm>>6)) != perm) {


@@ 321,7 332,7 @@ ipopen(Chan* c, int omode)
			cv->perm = 0660;
		}
		unlock(cv);
		unlock(p);
		qunlock(p);
		poperror();
		break;
	case Qlisten:


@@ 381,8 392,10 @@ static void
closeconv(Conv *cv)
{
	Conv *nc;
	Ipmulti *mp;

	lock(cv);

	if(--cv->inuse > 0) {
		unlock(cv);
		return;


@@ 397,6 410,9 @@ closeconv(Conv *cv)
	strcpy(cv->owner, network);
	cv->perm = 0660;

	while((mp = cv->multi) != nil)
		ipifcremmulti(cv, mp->ma, mp->ia);

	/* The close routine will unlock the conv */
	cv->p->close(cv);
}


@@ 407,13 423,13 @@ ipclose(Chan* c)
	switch(TYPE(c->qid)) {
	default:
		break;
	case Qipifc:
	case Qlog:
		if(c->flag & COPEN)
			closeifcconv(c->aux);
		c->aux = nil;
			netlogclose();
		break;
	case Qlog:
		netlogclose();
	case Qiprouter:
		if(c->flag & COPEN)
			iprouterclose();
		break;
	case Qdata:
	case Qctl:


@@ 423,13 439,18 @@ ipclose(Chan* c)
	}
}

enum
{
	Statelen=	1024,
};

static long
ipread(Chan *ch, void *a, long n, ulong offset)
{
	Conv *c;
	Proto *x;
	byte ip[4];
	char buf[256], *p, *statename;
	char *buf, *p;
	long m, rv;

	p = a;
	switch(TYPE(ch->qid)) {


@@ 441,41 462,61 @@ ipread(Chan *ch, void *a, long n, ulong offset)
		return devdirread(ch, a, n, 0, 0, ipgen);
	case Qarp:
		return arpread(a, offset, n);
	case Qipifc:
		return Mediaifcread(a, offset, n);
	case Qiproute:
		return routeread(a, offset, n);
	case Qiprouter:
		return iprouterread(a, n);
	case Qlog:
		return netlogread(a, offset, n);
	case Qctl:
		buf = smalloc(16);
		sprint(buf, "%d", CONV(ch->qid));
		return readstr(offset, p, n, buf);
		rv = readstr(offset, p, n, buf);
		free(buf);
		return rv;
	case Qremote:
		buf = smalloc(Statelen);
		c = fs.p[PROTO(ch->qid)]->conv[CONV(ch->qid)];
		hnputl(ip, c->raddr);
		sprint(buf, "%I!%d\n", ip, c->rport);
		return readstr(offset, p, n, buf);
		sprint(buf, "%I!%d\n", c->raddr, c->rport);
		rv = readstr(offset, p, n, buf);
		free(buf);
		return rv;
	case Qlocal:
		c = fs.p[PROTO(ch->qid)]->conv[CONV(ch->qid)];
		if(media != nil && c->laddr == 0)
			hnputl(ip, Mediagetaddr(media));
		else
			hnputl(ip, c->laddr);
		sprint(buf, "%I!%d\n", ip, c->lport);
		return readstr(offset, p, n, buf);
		buf = smalloc(Statelen);
		x = fs.p[PROTO(ch->qid)];
		c = x->conv[CONV(ch->qid)];
		if(x->local == nil) {
			sprint(buf, "%I!%d\n", c->laddr, c->lport);
		} else {
			(*x->local)(c, buf, Statelen-2);
		}
		rv = readstr(offset, p, n, buf);
		free(buf);
		return rv;
	case Qstatus:
		buf = smalloc(Statelen);
		x = fs.p[PROTO(ch->qid)];
		c = x->conv[CONV(ch->qid)];
		x->state(&statename, c);
		sprint(buf, "%s/%d %d %s \n", c->p->name, c->x, c->inuse, statename);
		return readstr(offset, p, n, buf);
		m = sprint(buf, "%s/%d %d ", c->p->name, c->x, c->inuse);
		(*x->state)(c, buf, Statelen-m-2);
		rv = readstr(offset, p, n, buf);
		free(buf);
		return rv;
	case Qdata:
		c = fs.p[PROTO(ch->qid)]->conv[CONV(ch->qid)];
		return qread(c->rq, a, n);

	case Qerr:
		c = fs.p[PROTO(ch->qid)]->conv[CONV(ch->qid)];
		return qread(c->eq, a, n);
	case Qstats:
		x = fs.p[PROTO(ch->qid)];
		if(x->stats == nil)
			error("stats not implemented");
		buf = smalloc(Statelen);
		(*x->stats)(buf, Statelen);
		rv = readstr(offset, p, n, buf);
		free(buf);
		return rv;
	}
}



@@ 485,15 526,18 @@ ipbread(Chan* c, long n, ulong offset)
	return devbread(c, n, offset);
}

/*
 *  set local address to be that of the ifc closest to remote address
 */
static void
setladdr(Conv* c)
{
	byte rem[4];

	hnputl(rem, c->raddr);
	c->laddr = Mediagetsrc(rem);
	findlocalip(c->laddr, c->raddr);
}

/*
 *  pick a local port and set it
 */
static void
setlport(Conv* c)
{


@@ 506,7 550,7 @@ setlport(Conv* c)
		pp = &p->nextrport;
	else
		pp = &p->nextport;
	lock(p);
	qlock(p);
	for(;;(*pp)++){
		/*
		 * Fsproto initialises p->nextport to 0 and the restricted


@@ 536,14 580,21 @@ setlport(Conv* c)
			break;
	}
	c->lport = (*pp)++;
	unlock(p);
	qunlock(p);
}

/*
 *  set a local address and port from a string of the form
 *	address!port
 *  if address is missing and not announcing, pick one
 *  if address is missing and announcing, leave address as zero (i.e. matches anything)
 *  if port is 0, pick a free one
 *  if port is '*', leave port as zero (i.e. matches anything)
 */
static void
setladdrport(Conv* c, char* str, int nodefault)
setladdrport(Conv* c, char* str, int announcing)
{
	char *p;
	uchar addr[IPaddrlen];

	/*
	 *  ignore restricted part if it exists.  it's


@@ 552,19 603,16 @@ setladdrport(Conv* c, char* str, int nodefault)
	p = strchr(str, '!');
	if(p == nil || strcmp(p, "!r") == 0) {
		p = str;
		if(nodefault)
		memset(c->laddr, 0, sizeof(c->laddr));
		if(!announcing)
			setladdr(c);
		else
			c->laddr = 0;
	}
	else {
	} else {
		*p++ = 0;
		parseip(addr, str);
		c->laddr = nhgetl(addr);
		parseip(c->laddr, str);
	}
	if(*p == '*')
		c->lport = 0;
	else {

	c->lport = 0;
	if(*p != '*'){
		c->lport = atoi(p);
		if(c->lport == 0)
			setlport(c);


@@ 575,14 623,12 @@ static char*
setraddrport(Conv* c, char* str)
{
	char *p;
	uchar addr[IPaddrlen];

	p = strchr(str, '!');
	if(p == nil)
		return "malformed address";
	*p++ = 0;
	parseip(addr, str);
	c->raddr = nhgetl(addr);
	parseip(c->raddr, str);
	c->rport = atoi(p);
	p = strchr(p, '!');
	if(p){


@@ 592,44 638,163 @@ setraddrport(Conv* c, char* str)
	return nil;
}

/*
 *  called by protocol connect routine to set addresses
 */
char*
Fsstdconnect(Conv *c, char *argv[], int argc)
{
	char *p;

	switch(argc) {
	default:
		return "bad args to connect";
	case 2:
		p = setraddrport(c, argv[1]);
		if(p != nil)
			return p;
		setladdr(c);
		setlport(c);
		break;
	case 3:
		p = setraddrport(c, argv[1]);
		if(p != nil)
			return p;
		setladdrport(c, argv[2], 0);
		break;
	}
	return nil;
}
/*
 *  initiate connection and sleep till its set up
 */
static int
connected(void* a)
{
	return ((Conv*)a)->state == Connected;
}
static void
connectctlmsg(Proto *x, Conv *c, Cmdbuf *cb)
{
	char *p;

	c->state = Connecting;
	c->cerr[0] = '\0';
	if(x->connect == nil)
		error("connect not supported");
	p = x->connect(c, cb->f, cb->nf);
	if(p != nil)
		error(p);
	sleep(&c->cr, connected, c);
	if(c->cerr[0] != '\0')
		error(c->cerr);
}

/*
 *  called by protocol announce routine to set addresses
 */
char*
Fsstdannounce(Conv* c, char* argv[], int argc)
{
	switch(argc){
	default:
		return "bad args to announce";
	case 2:
		setladdrport(c, argv[1], 1);
		break;
	}
	return nil;
}

/*
 *  initiate announcement and sleep till its set up
 */
static int
announced(void* a)
{
	return ((Conv*)a)->state == Announced;
}
static void
announcectlmsg(Proto *x, Conv *c, Cmdbuf *cb)
{
	char *p;

	c->state = Announcing;
	c->cerr[0] = '\0';
	if(x->announce == nil)
		error("announce not supported");
	p = x->announce(c, cb->f, cb->nf);
	if(p != nil)
		error(p);
	sleep(&c->cr, announced, c);
	if(c->cerr[0] != '\0')
		error(c->cerr);
}

/*
 *  called by protocol bide routine to set addresses
 */
char*
Fsstdbind(Conv* c, char* argv[], int argc)
{
	switch(argc){
	default:
		return "bad args to bind";
	case 2:
		setladdrport(c, argv[1], 0);
		break;
	}
	return nil;
}

static void
bindctlmsg(Proto *x, Conv *c, Cmdbuf *cb)
{
	char *p;

	if(x->bind == nil)
		p = Fsstdbind(c, cb->f, cb->nf);
	else
		p = x->bind(c, cb->f, cb->nf);
	if(p != nil)
		error(p);
}

static void
ttlctlmsg(Conv *c, Cmdbuf *cb)
{
	if(cb->nf < 2)
		c->ttl = MAXTTL;
	else
		c->ttl = atoi(cb->f[1]);
}

static long
ipwrite(Chan* ch, char* a, long n, ulong)
{
	Conv *c;
	Proto *x;
	int nfield;
	char *p, *fields[10], buf[128];
	char *p;
	Cmdbuf *cb;
	uchar ia[IPaddrlen];

	switch(TYPE(ch->qid)){
	default:
		error(Eperm);
	case Qdata:
		x = fs.p[PROTO(ch->qid)];
		c = x->conv[CONV(ch->qid)];

		if(c->wq == nil)
			error(Eperm);

		qwrite(c->wq, a, n);
		x->kick(c, n);
		break;
	case Qarp:
		p = arpwrite(a, n);
		if(p != nil)
			error(p);
		return n;
	case Qipifc:
		p = Mediaifcwrite(ch->aux, a, n);
		if(p != nil)
			error(p);
		return n;
		return arpwrite(a, n);
	case Qiproute:
		p = routewrite(a, n);
		if(p != nil)
			error(p);
		return n;
		return routewrite(ch, a, n);
	case Qlog:
		p = netlogctl(a, n);
		if(p != nil)


@@ 638,90 803,48 @@ ipwrite(Chan* ch, char* a, long n, ulong)
	case Qctl:
		x = fs.p[PROTO(ch->qid)];
		c = x->conv[CONV(ch->qid)];
		if(n > sizeof(buf)-1)
			n = sizeof(buf)-1;
		memmove(buf, a, n);
		buf[n] = '\0';
		nfield = parsefields(buf, fields, 10, " ");
		if(strcmp(fields[0], "connect") == 0){
			if(canqlock(&c->car) == 0)
				error("connect/announce in progress");
			if(waserror()) {
				qunlock(&c->car);
				nexterror();
			}
			c->state = Connecting;
			c->cerr[0] = '\0';
			p = x->connect(c, fields, nfield);
			if(p != nil)
				error(p);
			sleep(&c->cr, connected, c);
			if(c->cerr[0] != '\0')
				error(c->cerr);
			qunlock(&c->car);
			poperror();
			return n;
		}
		if(strcmp(fields[0], "announce") == 0){
			if(canqlock(&c->car) == 0)
				error("connect/announce in progress");
			if(waserror()) {
				qunlock(&c->car);
				nexterror();
			}
			c->state = Announcing;
			c->cerr[0] = '\0';
			p = x->announce(c, fields, nfield);
			if(p != nil)
				error(p);
			sleep(&c->cr, announced, c);
			if(c->cerr[0] != '\0')
				error(c->cerr);
			qunlock(&c->car);
			poperror();
			return n;
		cb = parsecmd(a, n);

		if(canqlock(&c->car) == 0){
			free(cb);
			error("connect/announce in progress");
		}
		if(strcmp(fields[0], "bind") == 0){
			if(canqlock(&c->car) == 0)
				error("connect/announce in progress");
			if(waserror()) {
				qunlock(&c->car);
				nexterror();
			}
			switch(nfield){
			default:
				error("bad args to bind");
			case 2:
				setladdr(c);
				c->lport = atoi(fields[1]);
				if(c->lport == 0)
					setlport(c);
				break;
			}
		if(waserror()) {
			qunlock(&c->car);
			poperror();
			return n;
		}
		if(strcmp(fields[0], "ttl") == 0){
			if(nfield < 2)
				c->ttl = MAXTTL;
			else
				c->ttl = atoi(fields[1]);
			return n;
			free(cb);
			nexterror();
		}
		if(x->ctl != nil) {
			p = x->ctl(c, fields, nfield);
		if(strcmp(cb->f[0], "connect") == 0)
			connectctlmsg(x, c, cb);
		else if(strcmp(cb->f[0], "announce") == 0)
			announcectlmsg(x, c, cb);
		else if(strcmp(cb->f[0], "bind") == 0)
			bindctlmsg(x, c, cb);
		else if(strcmp(cb->f[0], "ttl") == 0)
			ttlctlmsg(c, cb);
		else if(strcmp(cb->f[0], "addmulti") == 0){
			if(cb->nf < 2)
				error("addmulti needs interface address");
			if(!ipismulticast(c->raddr))
				error("addmulti for a non multicast address");
			parseip(ia, cb->f[1]);
			ipifcaddmulti(c, c->raddr, ia);
		} else if(strcmp(cb->f[0], "remmulti") == 0){
			if(cb->nf < 2)
				error("remmulti needs interface address");
			if(!ipismulticast(c->raddr))
				error("remmulti for a non multicast address");
			parseip(ia, cb->f[1]);
			ipifcremmulti(c, c->raddr, ia);
		} else if(x->ctl != nil) {
			p = x->ctl(c, cb->f, cb->nf);
			if(p != nil)
				error(p);
			return n;
		}
		error("unknown control request");
	case Qdata:
		x = fs.p[PROTO(ch->qid)];
		c = x->conv[CONV(ch->qid)];

		qwrite(c->wq, a, n);
		x->kick(c, n);
		} else
			error("unknown control request");
		qunlock(&c->car);
		free(cb);
		poperror();
	}
	return n;
}


@@ 782,7 905,7 @@ Fsproto(Fs *fs, Proto *p)
 *  built in
 */
int
Fsbuiltinproto(Fs* fs, byte proto)
Fsbuiltinproto(Fs* fs, uchar proto)
{
	return fs->t2p[proto] != nil;
}


@@ 790,14 913,12 @@ Fsbuiltinproto(Fs* fs, byte proto)
Conv*
Fsprotoclone(Proto *p, char *user)
{
	char *junk;
	int unused;
	Conv *c, **pp, **ep;

	c = nil;
	lock(p);
	qlock(p);
	if(waserror()) {
		unlock(p);
		qunlock(p);
		nexterror();
	}
	ep = &p->conv[p->nc];


@@ 812,7 933,6 @@ Fsprotoclone(Proto *p, char *user)
			c->x = pp - p->conv;
			c->ptcl = malloc(p->ptclsize);
			if(c->ptcl == nil) {
				unlock(c);
				free(c);
				error(Enomem);
			}


@@ 823,15 943,18 @@ Fsprotoclone(Proto *p, char *user)
			break;
		}
		if(canlock(c)){
			unused = p->state(&junk, c);
			if(c->inuse == 0 && unused)
			/*
			 *  make sure both processes and protocol
			 *  are done with this Conv
			 */
			if(c->inuse == 0 && (p->inuse == nil || (*p->inuse)(c) == 0))
				break;

			unlock(c);
		}
	}
	if(pp >= ep) {
		unlock(p);
		qunlock(p);
		poperror();
		return nil;
	}


@@ 840,8 963,8 @@ Fsprotoclone(Proto *p, char *user)
	strcpy(c->owner, user);
	c->perm = 0660;
	c->state = 0;
	c->laddr = 0;
	c->raddr = 0;
	ipmove(c->laddr, IPnoaddr);
	ipmove(c->raddr, IPnoaddr);
	c->lport = 0;
	c->rport = 0;
	c->restricted = 0;


@@ 851,7 974,7 @@ Fsprotoclone(Proto *p, char *user)
	qreopen(c->eq);

	unlock(c);
	unlock(p);
	qunlock(p);
	poperror();
	return c;
}


@@ 878,13 1001,13 @@ Fsconnected(Fs*, Conv* c, char* msg)
}

Proto*
Fsrcvpcol(Fs* fs, byte proto)
Fsrcvpcol(Fs* fs, uchar proto)
{
	return fs->t2p[proto];
}

Conv*
Fsnewcall(Fs*, Conv *c, Ipaddr raddr, ushort rport, Ipaddr laddr, ushort lport)
Fsnewcall(Fs*, Conv *c, uchar *raddr, ushort rport, uchar *laddr, ushort lport)
{
	Conv *nc;
	Conv **l;


@@ 905,9 1028,9 @@ Fsnewcall(Fs*, Conv *c, Ipaddr raddr, ushort rport, Ipaddr laddr, ushort lport)
		unlock(c);
		return nil;
	}
	nc->raddr = raddr;
	ipmove(nc->raddr, raddr);
	nc->rport = rport;
	nc->laddr = laddr;
	ipmove(nc->laddr, laddr);
	nc->lport = lport;
	nc->next = nil;
	*l = nc;


@@ 917,59 1040,3 @@ Fsnewcall(Fs*, Conv *c, Ipaddr raddr, ushort rport, Ipaddr laddr, ushort lport)

	return nc;
}

int
Fspcolstats(char *buf, int len)
{
	Proto **p;
	int n;

	n = 0;
	for(p = fs.p; *p; p++){
		n += snprint(buf + n, len - n,
			"%s: csum %d hlen %d len %d order %d rexmit %d\n",
			(*p)->name, (*p)->csumerr, (*p)->hlenerr, (*p)->lenerr,
			(*p)->order, (*p)->rexmit);
		if((*p)->stats)
			n += (*(*p)->stats)(buf + n, len - n);
	}
	return n;
}

char*
Fsstdconnect(Conv *c, char *argv[], int argc)
{
	char *p;

	switch(argc) {
	default:
		return "bad args to connect";
	case 2:
		p = setraddrport(c, argv[1]);
		if(p != nil)
			return p;
		setladdr(c);
		setlport(c);
		break;
	case 3:
		p = setraddrport(c, argv[1]);
		if(p != nil)
			return p;
		setladdrport(c, argv[2], 1);
		break;
	}
	return nil;
}

char*
Fsstdannounce(Conv* c, char* argv[], int argc)
{
	switch(argc){
	default:
		return "bad args to announce";
	case 2:
		setladdrport(c, argv[1], 0);
		break;
	}
	return nil;
}

A ip/eipconvtest.c => ip/eipconvtest.c +152 -0
@@ 0,0 1,152 @@
#include <u.h>
#include <libc.h>

enum
{
	Isprefix= 16,
};

uchar prefixvals[256] =
{
[0x00] 0 | Isprefix,
[0x80] 1 | Isprefix,
[0xC0] 2 | Isprefix,
[0xE0] 3 | Isprefix,
[0xF0] 4 | Isprefix,
[0xF8] 5 | Isprefix,
[0xFC] 6 | Isprefix,
[0xFE] 7 | Isprefix,
[0xFF] 8 | Isprefix,
};

uchar v4prefix[16] = {
	0, 0, 0, 0,
	0, 0, 0, 0,
	0, 0, 0xff, 0xff,
	0, 0, 0, 0
};

void
hnputl(void *p, ulong v)
{
	uchar *a;

	a = p;
	a[0] = v>>24;
	a[1] = v>>16;
	a[2] = v>>8;
	a[3] = v;
}

int
eipconv(va_list *arg, Fconv *f)
{
	char buf[8*5];
	static char *efmt = "%.2lux%.2lux%.2lux%.2lux%.2lux%.2lux";
	static char *ifmt = "%d.%d.%d.%d";
	uchar *p, ip[16];
	ulong *lp;
	ushort s;
	int i, j, n, eln, eli;

	switch(f->chr) {
	case 'E':		/* Ethernet address */
		p = va_arg(*arg, uchar*);
		sprint(buf, efmt, p[0], p[1], p[2], p[3], p[4], p[5]);
		break;
	case 'I':		/* Ip address */
		p = va_arg(*arg, uchar*);
common:
		if(memcmp(p, v4prefix, 12) == 0)
			sprint(buf, ifmt, p[12], p[13], p[14], p[15]);
		else {
			/* find longest elision */
			eln = eli = -1;
			for(i = 0; i < 16; i += 2){
				for(j = i; j < 16; j += 2)
					if(p[j] != 0 || p[j+1] != 0)
						break;
				if(j > i && j - i > eln){
					eli = i;
					eln = j - i;
				}
			}

			/* print with possible elision */
			n = 0;
			for(i = 0; i < 16; i += 2){
				if(i == eli){
					n += sprint(buf+n, "::");
					i += eln;
					if(i >= 16)
						break;
				} else if(i != 0)
					n += sprint(buf+n, ":");
				s = (p[i]<<8) + p[i+1];
				n += sprint(buf+n, "%ux", s);
			}
		}
		break;
	case 'i':		/* v6 address as 4 longs */
		lp = va_arg(*arg, ulong*);
		for(i = 0; i < 4; i++)
			hnputl(ip+4*i, *lp++);
		p = ip;
		goto common;
	case 'V':		/* v4 ip address */
		p = va_arg(*arg, uchar*);
		sprint(buf, ifmt, p[0], p[1], p[2], p[3]);
		break;
	case 'M':		/* ip mask */
		p = va_arg(*arg, uchar*);

		/* look for a prefix mask */
		for(i = 0; i < 16; i++)
			if(p[i] != 0xff)
				break;
		if(i < 16){
			if((prefixvals[p[i]] & Isprefix) == 0)
				goto common;
			for(j = i+1; j < 16; j++)
				if(p[j] != 0)
					goto common;
			n = 8*i + (prefixvals[p[i]] & ~Isprefix);
		} else
			n = 8*16;

		/* got one, use /xx format */
		sprint(buf, "/%d", n);
		break;
	default:
		strcpy(buf, "(eipconv)");
	}
	strconv(buf, f);
	return sizeof(uchar*);
}

uchar testvec[11][16] =
{
 { 0,0,0,0, 0,0,0,0, 0,0,0xff,0xff, 1,3,4,5, },
 { 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, },
 { 0xff,0xff,0x80,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, },
 { 0xff,0xff,0xff,0xc0, 0,0,0,0, 0,0,0,0, 0,0,0,0, },
 { 0xff,0xff,0xff,0xff, 0xe0,0,0,0, 0,0,0,0, 0,0,0,0, },
 { 0xff,0xff,0xff,0xff, 0xff,0xf0,0,0, 0,0,0,0, 0,0,0,0, },
 { 0xff,0xff,0xff,0xff, 0xff,0xff,0xf8,0, 0,0,0,0, 0,0,0,0, },
 { 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, },
 { 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, },
 { 0,0,0,0, 0,0x11,0,0, 0,0,0,0, 0,0,0,0, },
 { 0,0,0,0x11, 0,0,0,0, 0,0,0,0, 0,0,0,0x12, },
};

void
main(void)
{
	int i;

	fmtinstall('I', eipconv);
	fmtinstall('M', eipconv);
	for(i = 0; i < 11; i++)
		print("%I\n%M\n", testvec[i], testvec[i]);
	exits(0);
}

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

#include "ip.h"
#include "kernel.h"

typedef struct Etherhdr Etherhdr;
struct Etherhdr
{
	uchar	d[6];
	uchar	s[6];
	uchar	t[2];
};

static void	etherread(void *a);
static void	etherbind(Ipifc *ifc, int argc, char **argv);
static void	etherunbind(Ipifc *ifc);
static void	etherbwrite(Ipifc *ifc, Block *bp, int version, uchar *ip);
static void	etheraddmulti(Ipifc *ifc, uchar *a, uchar *ia);
static void	etherremmulti(Ipifc *ifc, uchar *a, uchar *ia);
static Block*	multicastarp(Arpent *a, uchar *mac);
static void	sendarp(Ipifc *ifc, Arpent *a);
static int	multicastea(uchar *ea, uchar *ip);
static void	recvarpproc(Ipifc *ifc);

Medium ethermedium =
{
	"ether",
	14,
	60,
	1514,
	6,
	etherbind,
	etherunbind,
	etherbwrite,
	etheraddmulti,
	etherremmulti,
	nil,			/* pktin */
	nil,			/* addroute */
	nil,			/* remroute */
	nil,			/* flushroute */
	nil,			/* joinmulti */
	nil,			/* leavemulti */
};

typedef struct	Etherrock Etherrock;
struct Etherrock
{
	Proc	*arpp;		/* arp process */
	Proc	*readp;		/* reading process */
	Chan	*mchan;		/* Data channel */
	Chan	*achan;		/* Arp channel */
	Chan	*cchan;		/* Control channel */
};

/*
 *  ethernet arp request
 */
enum
{
	ETARP		= 0x0806,
	ETIP		= 0x0800,
	ARPREQUEST	= 1,
	ARPREPLY	= 2,
};
typedef struct Etherarp Etherarp;
struct Etherarp
{
	uchar	d[6];
	uchar	s[6];
	uchar	type[2];
	uchar	hrd[2];
	uchar	pro[2];
	uchar	hln;
	uchar	pln;
	uchar	op[2];
	uchar	sha[6];
	uchar	spa[4];
	uchar	tha[6];
	uchar	tpa[4];
};


/*
 *  called to bind an IP ifc to an ethernet device
 *  called with ifc wlock'd
 */
static void
etherbind(Ipifc *ifc, int argc, char **argv)
{
	Chan *mchan, *cchan, *achan;
	char addr[2*NAMELEN];
	char dir[2*NAMELEN];
	char *buf;
	int fd, cfd, n;
	char *ptr;
	Etherrock *er;

	if(argc < 2)
		error(Ebadarg);

	mchan = cchan = achan = nil;
	buf = nil;
	if(waserror()){
		if(mchan != nil)
			cclose(mchan);
		if(cchan != nil)
			cclose(cchan);
		if(achan != nil)
			cclose(achan);
		if(buf != nil)
			free(buf);
		nexterror(); 
	}

	/*
	 *  open ip conversation
	 *
	 *  the dial will fail if the type is already open on
	 *  this device.
	 */
	snprint(addr, sizeof(addr), "%s!0x800", argv[2]);
	fd = kdial(addr, nil, dir, &cfd);
	if(fd < 0)
		error("dial 0x800 failed");
	mchan = fdtochan(fd, ORDWR, 0, 1);
	cchan = fdtochan(cfd, ORDWR, 0, 1);
	kclose(fd);
	kclose(cfd);

	/*
	 *  get mac address
	 */
	snprint(addr, sizeof(addr), "%s/stats", dir);
	fd = kopen(addr, OREAD);
	if(fd < 0)
		error("can't read ether stats");

	buf = smalloc(512);
	n = kread(fd, buf, 511);
	kclose(fd);
	if(n <= 0)
		error(Eio);
	buf[n] = 0;

	ptr = strstr(buf, "addr: ");
	if(!ptr)
		error(Eio);
	ptr += 6;

	parsemac(ifc->mac, ptr, 6);

	/*
 	 *  open arp conversation
	 */
	snprint(addr, sizeof(addr), "%s!0x806", argv[2]);
	fd = kdial(addr, nil, nil, nil);
	if(fd < 0)
		error("dial 0x806 failed");
	achan = fdtochan(fd, ORDWR, 0, 1);
	kclose(fd);

	er = smalloc(sizeof(*er));
	er->mchan = mchan;
	er->cchan = cchan;
	er->achan = achan;
	ifc->arg = er;

	free(buf);
	poperror();

	kproc("etherread", etherread, ifc);
	kproc("recvarpproc", recvarpproc, ifc);
}

/*
 *  called with ifc wlock'd
 */
static void
etherunbind(Ipifc *ifc)
{
	Etherrock *er = ifc->arg;

	if(er->readp)
		postnote(er->readp, 1, "unbind", 0);
	if(er->arpp)
		postnote(er->arpp, 1, "unbind", 0);

	/* wait for readers to die */
	while(er->arpp != 0 || er->readp != 0)
		tsleep(&up->sleep, return0, 0, 300);

	if(er->mchan != nil)
		cclose(er->mchan);
	if(er->achan != nil)
		cclose(er->achan);
	if(er->cchan != nil)
		cclose(er->cchan);

	free(er);
}

/*
 *  called by ipoput with a single block to write
 */
static void
etherbwrite(Ipifc *ifc, Block *bp, int version, uchar *ip)
{
	Etherhdr *eh;
	Arpent *a;
	uchar mac[6];
	Etherrock *er = ifc->arg;

	if(waserror()) {
		print("etherbwrite failed\n");
		ipifccheckout(ifc);
		return;
	}
	if(ipifccheckin(ifc, &ethermedium) < 0){
		freeb(bp);
		poperror();
		return;
	}

	/* get mac address of destination */
	a = arpget(bp, version, &ethermedium, ip, mac);
	if(a){
		/* check for broadcast or multicast */
		bp = multicastarp(a, mac);
		if(bp == nil){
			sendarp(ifc, a);
			goto out;
		}
	}

	/* make it a single block with space for the ether header */
	bp = padblock(bp, ifc->m->hsize);
	if(bp->next)
		bp = concatblock(bp);
	if(BLEN(bp) < ifc->minmtu)
		bp = adjustblock(bp, ifc->minmtu);
	eh = (Etherhdr*)bp->rp;

	/* copy in mac addresses and ether type */
	memmove(eh->s, ifc->mac, sizeof(eh->s));
	memmove(eh->d, mac, sizeof(eh->d));
	switch(version){
	case V4:
		eh->t[0] = 0x08;
		eh->t[1] = 0x00;
		break;
	case V6:
		eh->t[0] = 0x86;
		eh->t[1] = 0xDD;
		break;
	}

	devtab[er->mchan->type]->bwrite(er->mchan, bp, 0);
	ifc->out++;

out:
	ipifccheckout(ifc);
	poperror();
}

/*
 *  process to read from the ethernet
 */
static void
etherread(void *a)
{
	Ipifc *ifc;
	Block *bp;
	Etherrock *er;

	ifc = a;
	er = ifc->arg;
	er->readp = up;	/* hide identity under a rock for unbind */
	if(waserror()){
		er->readp = 0;
		pexit("hangup", 1);
	}
	for(;;){
		bp = devtab[er->mchan->type]->bread(er->mchan, ifc->maxmtu, 0);
		ifc->in++;
		bp->rp += ifc->m->hsize;
		if(ifc->lifc == nil)
			freeb(bp);
		else
			ipiput(ifc->lifc->local, bp);
	}
}

static void
etheraddmulti(Ipifc *ifc, uchar *a, uchar *)
{
	uchar mac[6];
	char buf[64];
	Etherrock *er = ifc->arg;

	multicastea(mac, a);
	sprint(buf, "addmulti %E", mac);
	devtab[er->cchan->type]->write(er->cchan, buf, strlen(buf), 0);
}

static void
etherremmulti(Ipifc *ifc, uchar *a, uchar *)
{
	uchar mac[6];
	char buf[64];
	Etherrock *er = ifc->arg;

	multicastea(mac, a);
	sprint(buf, "remmulti %E", mac);
	devtab[er->cchan->type]->write(er->cchan, buf, strlen(buf), 0);
}

/*
 *  send an ethernet arp
 *  (only v4, v6 uses the neighbor discovery, rfc1970)
 */
static void
sendarp(Ipifc *ifc, Arpent *a)
{
	int n;
	Block *bp;
	Etherarp *e;
	Etherrock *er = ifc->arg;

	/* don't do anything if it's been less than a second since the last */
	if(msec - a->time < 1000){
		arprelease(a);
		return;
	}

	/* remove all but the last message */
	while((bp = a->hold) != nil){
		if(bp == a->last)
			break;
		a->hold = bp->list;
		freeblist(bp);
	}

	/* try to keep it around for a second more */
	a->time = msec;
	arprelease(a);

	n = sizeof(Etherarp);
	if(n < a->type->minmtu)
		n = a->type->minmtu;
	bp = allocb(n);
	memset(bp->rp, 0, n);
	e = (Etherarp*)bp->rp;
	memmove(e->tpa, a->ip+IPv4off, sizeof(e->tpa));
	ipv4local(ifc, e->spa);
	memmove(e->sha, ifc->mac, sizeof(e->sha));
	memset(e->d, 0xff, sizeof(e->d));		/* ethernet broadcast */
	memmove(e->s, ifc->mac, sizeof(e->s));

	hnputs(e->type, ETARP);
	hnputs(e->hrd, 1);
	hnputs(e->pro, ETIP);
	e->hln = sizeof(e->sha);
	e->pln = sizeof(e->spa);
	hnputs(e->op, ARPREQUEST);
	bp->wp += n;

	n = devtab[er->achan->type]->bwrite(er->achan, bp, 0);
	if(n < 0)
		print("arp: send: %r\n");
}

static void
recvarp(Ipifc *ifc)
{
	int n;
	Block *ebp, *rbp;
	Etherarp *e, *r;
	uchar ip[IPaddrlen];
	Etherrock *er = ifc->arg;

	ebp = devtab[er->achan->type]->bread(er->achan, ifc->maxmtu, 0);
	if(ebp == nil) {
		print("arp: rcv: %r\n");
		return;
	}

	e = (Etherarp*)ebp->rp;
	switch(nhgets(e->op)) {
	default:
		break;

	case ARPREPLY:
		arpenter(ifc, V4, e->spa, e->sha, &ethermedium, 0);
		break;

	case ARPREQUEST:
		/* don't answer arps till we know who we are */
		if(ifc->lifc == 0)
			break;

		/* check for someone that think's they're me */
		v4tov6(ip, e->spa);
		if(iplocalonifc(ifc, ip)){
			if(memcmp(e->sha, ifc->mac, sizeof(e->sha)) != 0)
				print("arp: 0x%E also has ip addr %V\n", e->sha, e->spa);
		} else {
			if(memcmp(e->sha, ifc->mac, sizeof(e->sha)) == 0){
				print("arp: %V also has ether addr %E\n", e->spa, e->sha);
				break;
			}
		}

		/* refresh what we know about sender */
		arpenter(ifc, V4, e->spa, e->sha, &ethermedium, 1);

		/* answer only requests for our address or systems we're proxying for */
		v4tov6(ip, e->tpa);
		if(!iplocalonifc(ifc, ip))
		if(ipproxyifc(ifc, ip) == 0)
			break;

/* print("arp: rem %I %E (for %I)\n", e->spa, e->sha, e->tpa); /**/

		n = sizeof(Etherarp);
		if(n < ifc->minmtu)
			n = ifc->minmtu;
		rbp = allocb(n);
		r = (Etherarp*)rbp->rp;
		memset(r, 0, sizeof(Etherarp));
		hnputs(r->type, ETARP);
		hnputs(r->hrd, 1);
		hnputs(r->pro, ETIP);
		r->hln = sizeof(r->sha);
		r->pln = sizeof(r->spa);
		hnputs(r->op, ARPREPLY);
		memmove(r->tha, e->sha, sizeof(r->tha));
		memmove(r->tpa, e->spa, sizeof(r->tpa));
		memmove(r->sha, ifc->mac, sizeof(r->sha));
		memmove(r->spa, e->tpa, sizeof(r->spa));
		memmove(r->d, e->sha, sizeof(r->d));
		memmove(r->s, ifc->mac, sizeof(r->s));
		rbp->wp += n;

		n = devtab[er->achan->type]->bwrite(er->achan, rbp, 0);
		if(n < 0)
			print("arp: write: %r\n");
	}
	freeb(ebp);
}

static void
recvarpproc(Ipifc *ifc)
{
	Etherrock *er = ifc->arg;

	er->arpp = up;
	if(waserror()){
		er->arpp = 0;
		pexit("hangup", 1);
	}
	for(;;)
		recvarp(ifc);
}

static int
multicastea(uchar *ea, uchar *ip)
{
	int x;

	switch(x = ipismulticast(ip)){
	case V4:
		ea[0] = 0x01;
		ea[1] = 0x00;
		ea[2] = 0x5e;
		ea[3] = ip[13] & 0x7f;
		ea[4] = ip[14];
		ea[5] = ip[15];
		break;
	case V6:
		ea[0] = 0x33;
		ea[1] = 0x33;
		ea[2] = ip[12];
		ea[3] = ip[13];
		ea[4] = ip[14];
		ea[5] = ip[15];
		break;
	}
	return x;
}

/*
 *  fill in an arp entry for broadcast or multicast
 *  addresses
 */
static Block*
multicastarp(Arpent *a, uchar *mac)
{
	/* is it broadcast? */
	switch(ipforme(a->ip)){
	case Runi:
		return nil;
	case Rbcast:
		memset(mac, 0xff, 6);
		return arpresolve(a, &ethermedium, mac);
	default:
		break;
	}

	/* if multicast, fill in mac */
	switch(multicastea(mac, a->ip)){
	case V4:
	case V6:
		return arpresolve(a, &ethermedium, mac);
	}

	/* let arp take care of it */
	return nil;
}

M ip/gre.c => ip/gre.c +43 -35
@@ 23,20 23,20 @@ enum
typedef struct GREhdr
{
	/* ip header */
	byte	vihl;		/* Version and header length */
	byte	tos;		/* Type of service */
	byte	len[2];		/* packet length (including headers) */
	byte	id[2];		/* Identification */
	byte	frag[2];	/* Fragment information */
	byte	Unused;	
	byte	proto;		/* Protocol */
	byte	cksum[2];	/* checksum */
	byte	src[4];		/* Ip source */
	byte	dst[4];		/* Ip destination */
	uchar	vihl;		/* Version and header length */
	uchar	tos;		/* Type of service */
	uchar	len[2];		/* packet length (including headers) */
	uchar	id[2];		/* Identification */
	uchar	frag[2];	/* Fragment information */
	uchar	Unused;	
	uchar	proto;		/* Protocol */
	uchar	cksum[2];	/* checksum */
	uchar	src[4];		/* Ip source */
	uchar	dst[4];		/* Ip destination */

	/* gre header */
	byte	flags[2];
	byte	eproto[2];	/* encapsulation protocol */
	uchar	flags[2];
	uchar	eproto[2];	/* encapsulation protocol */
} GREhdr;

	Proto	gre;


@@ 56,7 56,7 @@ greconnect(Conv *c, char **argv, int argc)

	/* make sure noone's already connected to this other sys */
	p = c->p;
	lock(p);
	qlock(p);
	ecp = &p->conv[p->nc];
	for(cp = p->conv; cp < ecp; cp++){
		tc = *cp;


@@ 66,12 66,12 @@ greconnect(Conv *c, char **argv, int argc)
			continue;
		if(tc->rport == c->rport && tc->raddr == c->raddr){
			err = "already connected to that addr/proto";
			c->rport = 0;
			c->raddr = 0;
			ipmove(c->laddr, IPnoaddr);
			ipmove(c->raddr, IPnoaddr);
			break;
		}
	}
	unlock(p);
	qunlock(p);

	if(err != nil)
		return err;


@@ 81,11 81,10 @@ greconnect(Conv *c, char **argv, int argc)
}

static int
grestate(char **msg, Conv *c)
grestate(Conv *c, char *state, int n)
{
	USED(c);
	*msg = "Datagram";
	return 1;
	return snprint(state, n, "%s", "Datagram");
}

static void


@@ 107,8 106,8 @@ greclose(Conv *c)
	qclose(c->rq);
	qclose(c->wq);
	qclose(c->eq);
	c->laddr = 0;
	c->raddr = 0;
	ipmove(c->laddr, IPnoaddr);
	ipmove(c->raddr, IPnoaddr);
	c->lport = 0;
	c->rport = 0;



@@ 122,7 121,7 @@ grekick(Conv *c, int l)
{
	GREhdr *ghp;
	Block *bp;
	ulong raddr, laddr;
	uchar laddr[IPaddrlen], raddr[IPaddrlen];

	USED(l);



@@ 142,16 141,17 @@ grekick(Conv *c, int l)

	ghp = (GREhdr *)(bp->rp);

	raddr = nhgetl(ghp->dst);
	laddr = nhgetl(ghp->src);
	if(raddr == 0)
		raddr = c->raddr;
	if(laddr == 0 || Mediaforme(ghp->src) <= 0)
		laddr = c->laddr;
	v4tov6(raddr, ghp->dst);
	if(ipcmp(raddr, v4prefix) == 0)
		memmove(ghp->dst, c->raddr + IPv4off, IPv4addrlen);
	v4tov6(laddr, ghp->src);
	if(ipcmp(laddr, v4prefix) == 0){
		if(ipcmp(c->laddr, IPnoaddr) == 0)
			findlocalip(c->laddr, raddr); /* pick interface closest to dest */
		memmove(ghp->src, c->laddr + IPv4off, IPv4addrlen);
	}

	ghp->proto = IP_GREPROTO;
	hnputl(ghp->dst, raddr);
	hnputl(ghp->src, laddr);
	hnputs(ghp->eproto, c->rport);
	ghp->frag[0] = 0;
	ghp->frag[1] = 0;


@@ 160,19 160,18 @@ grekick(Conv *c, int l)
}

static void
greiput(Media *m, Block *bp)
greiput(uchar*, Block *bp)
{
	int len;
	GREhdr *ghp;
	Ipaddr addr;
	Conv *c, **p;
	ushort eproto;
	uchar raddr[IPaddrlen];

	USED(m);
	ghp = (GREhdr*)(bp->rp);

	v4tov6(raddr, ghp->src);
	eproto = nhgets(ghp->eproto);
	addr = nhgetl(ghp->src);

	/* Look for a conversation structure for this port and address */
	c = nil;


@@ 180,7 179,7 @@ greiput(Media *m, Block *bp)
		c = *p;
		if(c->inuse == 0)
			continue;
		if(c->raddr == addr && c->rport == eproto)
		if(c->rport == eproto && ipcmp(c->raddr, raddr) == 0)
			break;
	}



@@ 216,6 215,14 @@ greiput(Media *m, Block *bp)
	}
}

int
grestats(char *buf, int len)
{
	return snprint(buf, len,
		"gre: csum %d hlen %d len %d order %d rexmit %d\n",
		gre.csumerr, gre.hlenerr, gre.lenerr, gre.order, gre.rexmit);
}

void
greinit(Fs *fs)
{


@@ 229,6 236,7 @@ greinit(Fs *fs)
	gre.rcv = greiput;
	gre.ctl = nil;
	gre.advise = nil;
	gre.stats = grestats;
	gre.ipproto = IP_GREPROTO;
	gre.nc = 64;
	gre.ptclsize = 0;

M ip/icmp.c => ip/icmp.c +44 -42
@@ 8,22 8,22 @@
#include "ip.h"

typedef struct Icmp {
	byte	vihl;		/* Version and header length */
	byte	tos;		/* Type of service */
	byte	length[2];	/* packet length */
	byte	id[2];		/* Identification */
	byte	frag[2];	/* Fragment information */
	byte	ttl;		/* Time to live */
	byte	proto;		/* Protocol */
	byte	ipcksum[2];	/* Header checksum */
	byte	src[4];		/* Ip source */
	byte	dst[4];		/* Ip destination */
	byte	type;
	byte	code;
	byte	cksum[2];
	byte	icmpid[2];
	byte	seq[2];
	byte	data[1];
	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	ttl;		/* Time to live */
	uchar	proto;		/* Protocol */
	uchar	ipcksum[2];	/* Header checksum */
	uchar	src[4];		/* Ip source */
	uchar	dst[4];		/* Ip destination */
	uchar	type;
	uchar	code;
	uchar	cksum[2];
	uchar	icmpid[2];
	uchar	seq[2];
	uchar	data[1];
} Icmp;

enum {			/* Packet Types */


@@ 67,11 67,10 @@ icmpconnect(Conv *c, char **argv, int argc)
}

static int
icmpstate(char **s, Conv *c)
icmpstate(Conv *c, char *state, int n)
{
	USED(c);
	*s = "Datagram";
	return 1;
	return snprint(state, n, "%s", "Datagram");
}

static void


@@ 99,7 98,8 @@ icmpclose(Conv *c)
{
	qclose(c->rq);
	qclose(c->wq);
	c->laddr = 0;
	ipmove(c->laddr, IPnoaddr);
	ipmove(c->raddr, IPnoaddr);
	c->lport = 0;
	unlock(c);
}


@@ 122,8 122,8 @@ icmpkick(Conv *c, int l)
	p = (Icmp *)(bp->rp);
	if(p->type <= Maxtype)
		stats.out[p->type]++;
	hnputl(p->dst, c->raddr);
	hnputl(p->src, c->laddr);
	v6tov4(p->dst, c->raddr);
	v6tov4(p->src, c->laddr);
	p->proto = IP_ICMPPROTO;
	hnputs(p->icmpid, c->lport);
	memset(p->cksum, 0, sizeof(p->cksum));


@@ 137,11 137,8 @@ icmpnoconv(Block *bp)
	Block	*nbp;
	Icmp	*p, *np;

	/* don't bother if we haven't gotten any links up yet */
	if(Mediabooting())
		return;

	p = (Icmp *)bp->rp;
	netlog(Logicmp, "sending icmpnoconv -> %V\n", p->src);
	nbp = allocb(ICMP_IPSIZE + ICMP_HDRSIZE + ICMP_IPSIZE + 8);
	nbp->wp += ICMP_IPSIZE + ICMP_HDRSIZE + ICMP_IPSIZE + 8;
	np = (Icmp *)nbp->rp;


@@ 164,20 161,17 @@ goticmpkt(Block *bp)
{
	Conv	**c, *s;
	Icmp	*p;
	Ipaddr	dst;
	uchar	dst[IPaddrlen];
	ushort	recid;

	p = (Icmp *) bp->rp;
	dst = nhgetl(p->src);
	v4tov6(dst, p->src);
	recid = nhgets(p->icmpid);
netlog(Logicmp, "goticmpkt from %i to %d\n", dst, recid);

	for(c = icmp.conv; *c; c++) {
		s = *c;
netlog(Logicmp, "conv %i %d %i %d\n", s->laddr, s->lport, s->raddr, s->rport);
		if(s->lport == recid && s->raddr == dst){
			if(bp->next)
				bp = concatblock(bp);
		if(s->lport == recid)
		if(ipcmp(s->raddr, dst) == 0){
			qpass(s->rq, bp);
			return;
		}


@@ 189,7 183,7 @@ static Block *
mkechoreply(Block *bp)
{
	Icmp	*q;
	byte	ip[4];
	uchar	ip[4];

	q = (Icmp *)bp->rp;
	memmove(ip, q->src, sizeof(q->dst));


@@ 198,6 192,7 @@ mkechoreply(Block *bp)
	q->type = EchoReply;
	memset(q->cksum, 0, sizeof(q->cksum));
	hnputs(q->cksum, ptclcsum(bp, ICMP_IPSIZE, blocklen(bp) - ICMP_IPSIZE));

	return bp;
}



@@ 212,7 207,7 @@ static char *unreachcode[] =
};

static void
icmpiput(Media *m, Block *bp)
icmpiput(uchar*, Block *bp)
{
	int	n, iplen;
	Icmp	*p;


@@ 221,21 216,23 @@ icmpiput(Media *m, Block *bp)
	char	*msg;
	char	m2[128];

	USED(m);
	p = (Icmp *)bp->rp;
	netlog(Logicmp, "icmpiput %d %d\n", p->type, p->code);
	n = blocklen(bp);
	if(n < ICMP_IPSIZE+ICMP_HDRSIZE){
		icmp.hlenerr++;
		netlog(Logicmp, "icmp hlen %d\n", n);
		goto raise;
	}
	iplen = nhgets(p->length);
	if(iplen > n || (iplen % 1)){
		icmp.lenerr++;
		netlog(Logicmp, "icmp length %d\n", iplen);
		goto raise;
	}
	if(ptclcsum(bp, ICMP_IPSIZE, iplen - ICMP_IPSIZE)){
		icmp.csumerr++;
		netlog(Logicmp, "icmp checksum error\n");
		goto raise;
	}
	if(p->type <= Maxtype)


@@ 269,7 266,7 @@ icmpiput(Media *m, Block *bp)
		break;
	case TimeExceed:
		if(p->code == 0){
			sprint(m2, "ttl exceeded at %I", p->src);
			sprint(m2, "ttl exceeded at %V", p->src);

			bp->rp += ICMP_IPSIZE+ICMP_HDRSIZE;
			if(blocklen(bp) < 8){


@@ 301,16 298,17 @@ icmpadvise(Block *bp, char *msg)
{
	Conv	**c, *s;
	Icmp	*p;
	Ipaddr	dst;
	uchar	dst[IPaddrlen];
	ushort	recid;

	p = (Icmp *) bp->rp;
	dst = nhgetl(p->dst);
	v4tov6(dst, p->dst);
	recid = nhgets(p->icmpid);

	for(c = icmp.conv; *c; c++) {
		s = *c;
		if(s->lport == recid && s->raddr == dst){
		if(s->lport == recid)
		if(ipcmp(s->raddr, dst) == 0){
			qhangup(s->rq, msg);
			qhangup(s->wq, msg);
			break;


@@ 324,10 322,14 @@ icmpstats(char *buf, int len)
{
	int i, n;

	n = snprint(buf, len, "\trcvd ");

	n = snprint(buf, len,
		"icmp: csum %d hlen %d len %d order %d rexmit %d\n",
		icmp.csumerr, icmp.hlenerr, icmp.lenerr, icmp.order, icmp.rexmit);
	n += snprint(buf+n, len-n, "\trcvd ");
	for(i = 0; i < Maxtype && len > n; i++)
		n += snprint(buf+n, len-n, " %d", stats.in[i]);	
	n += snprint(buf+n, len - n, "\n\tsent ");
	n += snprint(buf+n, len-n, "\n\tsent ");
	for(i = 0; i < Maxtype && len > n; i++)
		n += snprint(buf+n, len-n, " %d", stats.out[i]);
	if(n < len)

M ip/il.c => ip/il.c +77 -66
@@ 63,7 63,7 @@ struct Ilcb			/* Control block */
	QLock	ackq;		/* Unacknowledged queue */
	Block	*unacked;
	Block	*unackedtail;
	ulong	unackedbytes;
	ulong	unackeduchars;
	QLock	outo;		/* Out of order packet queue */
	Block	*outoforder;
	ulong	next;		/* Id of next to send */


@@ 77,7 77,7 @@ struct Ilcb			/* Control block */
	int	querytime;	/* Query timer */
	int	deathtime;	/* Time to kill connection */
	int	delay;		/* Average of the fixed rtt delay */
	int	rate;		/* Average byte rate */
	int	rate;		/* Average uchar rate */
	int	mdev;		/* Mean deviation of rtt */
	int	maxrtt;		/* largest rtt seen */
	ulong	rttack;		/* The ack we are waiting for */


@@ 87,7 87,6 @@ struct Ilcb			/* Control block */
	int	rexmit;		/* number of retransmits */
	ulong	qt[Nqt+1];	/* state table for query messages */
	int	qtx;		/* ... index into qt */
	char	buf[128];
};

enum


@@ 102,24 101,24 @@ enum
typedef struct Ilhdr Ilhdr;
struct Ilhdr
{
	byte	vihl;		/* Version and header length */
	byte	tos;		/* Type of service */
	byte	length[2];	/* packet length */
	byte	id[2];		/* Identification */
	byte	frag[2];	/* Fragment information */
	byte	ttl;		/* Time to live */
	byte	proto;		/* Protocol */
	byte	cksum[2];	/* Header checksum */
	byte	src[4];		/* Ip source */
	byte	dst[4];		/* Ip destination */
	byte	ilsum[2];	/* Checksum including header */
	byte	illen[2];	/* Packet length */
	byte	iltype;		/* Packet type */
	byte	ilspec;		/* Special */
	byte	ilsrc[2];	/* Src port */
	byte	ildst[2];	/* Dst port */
	byte	ilid[4];	/* Sequence id */
	byte	ilack[4];	/* Acked sequence */
	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	ttl;		/* Time to live */
	uchar	proto;		/* Protocol */
	uchar	cksum[2];	/* Header checksum */
	uchar	src[4];		/* Ip source */
	uchar	dst[4];		/* Ip destination */
	uchar	ilsum[2];	/* Checksum including header */
	uchar	illen[2];	/* Packet length */
	uchar	iltype;		/* Packet type */
	uchar	ilspec;		/* Special */
	uchar	ilsrc[2];	/* Src port */
	uchar	ildst[2];	/* Dst port */
	uchar	ilid[4];	/* Sequence id */
	uchar	ilack[4];	/* Acked sequence */
};

static struct Ilstats


@@ 167,7 166,7 @@ void	iltimers(Ilcb*);
char*	ilstart(Conv*, int, int);
void	ilackproc();
void	iloutoforder(Conv*, Ilhdr*, Block*);
void	iliput(Media*, Block*);
void	iliput(uchar*, Block*);
void	iladvise(Block*, char*);
int	ilnextqt(Ilcb*);



@@ 189,23 188,26 @@ ilconnect(Conv *c, char **argv, int argc)
	return ilstart(c, IL_CONNECT, 20);
}

int
ilstate(char **msg, Conv *c)
static int
ilstate(Conv *c, char *state, int n)
{
	Ilcb *ic;
	int isclose;

	ic = (Ilcb*)(c->ptcl);
	return snprint(state, n, "%14.14s del %5.5d Br %5.5d md %5.5d una %5.5d rex %5.5d max %5.5d",
		ilstates[ic->state],
		ic->delay>>LogAGain, ic->rate>>LogAGain, ic->mdev>>LogDGain,
		ic->unackeduchars, ic->rexmit, ic->maxrtt);
}

	isclose = 0;
	if(ic->state == Ilclosed) 
		isclose = 1;
static int
ilinuse(Conv *c)
{
	Ilcb *ic;

	ic = (Ilcb*)(c->ptcl);
	return ic->state != Ilclosed;

	sprint(ic->buf, "%s del %d Br %d md %d una %d rex %d max %d", ilstates[ic->state],
		ic->delay>>LogAGain, ic->rate>>LogAGain, ic->mdev>>LogDGain,
		ic->unackedbytes, ic->rexmit, ic->maxrtt);
	*msg = ic->buf;
	return isclose;
}

/* called with c locked */


@@ 248,7 250,7 @@ ilclose(Conv *c)
		break;
	case Illistening:
		ic->state = Ilclosed;
		c->laddr = 0;
		ipmove(c->laddr, IPnoaddr);
		c->lport = 0;
		break;
	}


@@ 291,8 293,8 @@ ilkick(Conv *c, int l)
	/* Ip fields */
	ih->frag[0] = 0;
	ih->frag[1] = 0;
	hnputl(ih->dst, c->raddr);
	hnputl(ih->src, c->laddr);
	v6tov4(ih->dst, c->raddr);
	v6tov4(ih->src, c->laddr);
	ih->proto = IP_ILPROTO;

	/* Il fields */


@@ 338,7 340,13 @@ ilcreate(Conv *c)
int
ilxstats(char *buf, int len)
{
	return snprint(buf, len, "\tdupp %d dupb %d\n", ilstats.dup, ilstats.dupb);
	int n;

	n = snprint(buf, len,
		"il: csum %d hlen %d len %d order %d rexmit %d",
		il.csumerr, il.hlenerr, il.lenerr, il.order, il.rexmit);
	n += snprint(buf+n, len-n, " dupp %d dupb %d\n",
		ilstats.dup, ilstats.dupb);
}

void


@@ 355,6 363,7 @@ ilinit(Fs *fs)
	il.ctl = nil;
	il.advise = iladvise;
	il.stats = ilxstats;
	il.inuse = ilinuse;
	il.ipproto = IP_ILPROTO;
	il.nc = Nchans;
	il.ptclsize = sizeof(Ilcb);


@@ 383,7 392,7 @@ ilackq(Ilcb *ic, Block *bp)
	}
	ic->unackedtail = np;
	np->list = nil;
	ic->unackedbytes += n;
	ic->unackeduchars += n;
}

static


@@ 450,24 459,24 @@ ilackto(Ilcb *ic, ulong ackto)
		bp = ic->unacked;
		ic->unacked = bp->list;
		bp->list = nil;
		ic->unackedbytes -= blocklen(bp);
		ic->unackeduchars -= blocklen(bp);
		freeblist(bp);
	}
	qunlock(&ic->ackq);
}

void
iliput(Media *m, Block *bp)
iliput(uchar*, Block *bp)
{
	char *st;
	Ilcb *ic;
	Ilhdr *ih;
	Ipaddr dst;
	uchar raddr[IPaddrlen];
	uchar laddr[IPaddrlen];
	ushort sp, dp, csum;
	int plen, illen;
	Conv *s, **p, *new, *spec, *gen;

	USED(m);
	ih = (Ilhdr *)bp->rp;
	plen = blocklen(bp);
	if(plen < IL_IPSIZE+IL_HDRSIZE){


@@ 485,7 494,7 @@ iliput(Media *m, Block *bp)

	sp = nhgets(ih->ildst);
	dp = nhgets(ih->ilsrc);
	dst = nhgetl(ih->src);
	v4tov6(raddr, ih->src);

	if(ilcksum && (csum = ptclcsum(bp, IL_IPSIZE, illen)) != 0) {
		if(ih->iltype < 0 || ih->iltype > Ilclose)


@@ 493,14 502,16 @@ iliput(Media *m, Block *bp)
		else
			st = iltype[ih->iltype];
		il.csumerr++;
		netlog(Logil, "il: cksum %ux %ux, pkt(%s id %lud ack %lud %i/%d->%d)\n",
			csum, st, nhgetl(ih->ilid), nhgetl(ih->ilack), dst, sp, dp);
		netlog(Logil, "il: cksum %ux %ux, pkt(%s id %lud ack %lud %I/%d->%d)\n",
			csum, st, nhgetl(ih->ilid), nhgetl(ih->ilack), raddr, sp, dp);
		goto raise;
	}

	for(p = il.conv; *p; p++) {
		s = *p;
		if(s->lport == sp && s->rport == dp && s->raddr == dst) {
		if(s->lport == sp)
		if(s->rport == dp)
		if(ipcmp(s->raddr, raddr) == 0) {
			ilprocess(s, ih, bp);
			return;
		}


@@ 511,8 522,8 @@ iliput(Media *m, Block *bp)
			st = "?";
		else
			st = iltype[ih->iltype];
		netlog(Logil, "il: no channel, pkt(%s id %lud ack %lud %i/%ud->%ud)\n",
			st, nhgetl(ih->ilid), nhgetl(ih->ilack), dst, sp, dp); 
		netlog(Logil, "il: no channel, pkt(%s id %lud ack %lud %I/%ud->%ud)\n",
			st, nhgetl(ih->ilid), nhgetl(ih->ilack), raddr, sp, dp); 
		goto raise;
	}



@@ 524,7 535,7 @@ iliput(Media *m, Block *bp)
		if(ic->state != Illistening)
			continue;

		if(s->rport == 0 && s->raddr == 0) {
		if(s->rport == 0 && ipcmp(s->raddr, IPnoaddr) == 0) {
			if(s->lport == sp) {
				spec = s;
				break;


@@ 542,9 553,10 @@ iliput(Media *m, Block *bp)
	else
		goto raise;

	new = Fsnewcall(&fs, s, nhgetl(ih->src), dp, nhgetl(ih->dst), sp);
	v4tov6(laddr, ih->dst);
	new = Fsnewcall(&fs, s, raddr, dp, laddr, sp);
	if(new == nil){
		netlog(Logil, "il: bad newcall %i/%ud->%ud\n", dst, sp, dp);
		netlog(Logil, "il: bad newcall %I/%ud->%ud\n", raddr, sp, dp);
		ilsendctl(nil, ih, Ilclose, 0, nhgetl(ih->ilid), 0);
		goto raise;
	}


@@ 797,7 809,7 @@ ilhangup(Conv *s, char *msg)
	Ilcb *ic;
	int callout;

	netlog(Logil, "il: hangup! %i %d/%d: %s\n", s->raddr, s->lport, s->rport, msg?msg:"no reason");
	netlog(Logil, "il: hangup! %I %d/%d: %s\n", s->raddr, s->lport, s->rport, msg?msg:"no reason");

	ic = (Ilcb*)s->ptcl;
	callout = ic->state == Ilsyncer;


@@ 859,7 871,7 @@ void
iloutoforder(Conv *s, Ilhdr *h, Block *bp)
{
	Ilcb *ic;
	byte *lid;
	uchar *lid;
	Block *f, **l;
	ulong id, newid;



@@ 932,8 944,8 @@ ilsendctl(Conv *ipc, Ilhdr *inih, int type, ulong id, ulong ack, int ilspec)
		ttl = MAXTTL;
	}
	else {
		hnputl(ih->dst, ipc->raddr);
		hnputl(ih->src, ipc->laddr);
		v6tov4(ih->dst, ipc->raddr);
		v6tov4(ih->src, ipc->laddr);
		hnputs(ih->ilsrc, ipc->lport);
		hnputs(ih->ildst, ipc->rport);
		hnputl(ih->ilid, id);


@@ 1015,9 1027,7 @@ loop:
				break;
			}
			if(ic->timeout >= ic->fasttime) {
				ilsendctl(p, nil, Ilquery, ic->next, ic->recvd, ilnextqt(ic));
				ic->querytime = Querytime;
				ic->timeout = 0;
				ilrexmit(ic);
				ilbackoff(ic);
			}
			if(ic->timeout >= ic->slowtime) {


@@ 1053,7 1063,7 @@ ilstart(Conv *c, int type, int window)

	ic->unacked = nil;
	ic->outoforder = nil;
	ic->unackedbytes = 0;
	ic->unackeduchars = 0;
	ic->delay = Iltickms<<LogAGain;
	ic->mdev = Iltickms<<LogDGain;
	ic->rate = DefByteRate<<LogAGain;


@@ 1111,8 1121,8 @@ iltimers(Ilcb *ic)
	int pt;

	ic->timeout = 0;
	pt = (ic->delay>>LogAGain) + ic->unackedbytes/(ic->rate>>LogAGain) + (ic->mdev>>(LogDGain-1));
	ic->fasttime = pt + Iltickms - 1;
	pt = (ic->delay>>LogAGain) + ic->unackeduchars/(ic->rate>>LogAGain) + ic->mdev;
	ic->fasttime = Acktime + pt + Iltickms - 1;
	if(ic->fasttime > Fasttime)
		ic->fasttime = Fasttime;
	ic->slowtime = (Slowtime/Seconds)*pt;


@@ 1125,22 1135,23 @@ iladvise(Block *bp, char *msg)
{
	Ilhdr *h;
	Ilcb *ic;		
	Ipaddr source, dest;
	uchar source[IPaddrlen], dest[IPaddrlen];
	ushort psource;
	Conv *s, **p;

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

	dest = nhgetl(h->dst);
	source = nhgetl(h->src);
	v4tov6(dest, h->dst);
	v4tov6(source, h->src);
	psource = nhgets(h->ilsrc);


	/* Look for a connection, unfortunately the destination port is missing */
	for(p = il.conv; *p; p++) {
		s = *p;
		if(s->laddr == source && s->lport == psource)
		if(s->raddr == dest){
		if(s->lport == psource)
		if(ipcmp(s->laddr, source) == 0)
		if(ipcmp(s->raddr, dest) == 0){
			ic = (Ilcb*)s->ptcl;
			switch(ic->state){
			case Ilsyncer:

M ip/ip.c => ip/ip.c +58 -58
@@ 18,29 18,29 @@ enum
	IP_HLEN		= 0x05,		/* Header length in characters */
	IP_DF		= 0x4000,	/* Don't fragment */
	IP_MF		= 0x2000,	/* More fragments */
	IP_MAX		= (64*1024),	/* Maximum Internet packet size */
	IP_MAX		= (32*1024),	/* Maximum Internet packet size */
};

struct Iphdr
{
	byte	vihl;		/* Version and header length */
	byte	tos;		/* Type of service */
	byte	length[2];	/* packet length */
	byte	id[2];		/* Identification */
	byte	frag[2];	/* Fragment information */
	byte	ttl;		/* Time to live */
	byte	proto;		/* Protocol */
	byte	cksum[2];	/* Header checksum */
	byte	src[4];		/* Ip source */
	byte	dst[4];		/* Ip destination */
	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	ttl;		/* Time to live */
	uchar	proto;		/* Protocol */
	uchar	cksum[2];	/* Header checksum */
	uchar	src[4];		/* Ip source */
	uchar	dst[4];		/* Ip destination */
};

struct Fragment
{
	Block*	blist;
	Fragment* next;
	Ipaddr 	src;
	Ipaddr 	dst;
	ulong 	src;
	ulong 	dst;
	ushort	id;
	ulong 	age;
};


@@ 57,8 57,8 @@ Fragment*	fragfree;
ulong		Id;
int		iprouting;	/* true if we route like a gateway */
ulong		ipcsumerr;
ulong		ipin, ippin;		/* bytes, packets in */
ulong		ipout, ippout;		/* bytes, packets out */
ulong		ipin, ippin;		/* uchars, packets in */
ulong		ipout, ippout;		/* uchars, packets out */

#define BLKIP(xp)	((Iphdr*)((xp)->rp))
/*


@@ 73,7 73,7 @@ static struct Stats
	ulong	droppedfrag;
} stats;

ushort		ipcsum(byte*);
ushort		ipcsum(uchar*);
Block*		ipreassemble(int, Block*, Iphdr*);
void		ipfragfree(Fragment*);
Fragment*	ipfragallo(void);


@@ 81,17 81,18 @@ Fragment*	ipfragallo(void);
void
ipoput(Block *bp, int gating, int ttl)
{
	Media *m;
	byte gate[4];
	Ipifc *ifc;
	uchar *gate;
	ushort fragoff;
	Block *xp, *nb;
	Iphdr *eh, *feh;
	int lid, len, seglen, chunk, dlen, blklen, offset, medialen;
	Route *r;

	/* Fill out the ip header */
	eh = (Iphdr *)(bp->rp);

	/* Number of bytes in data and ip header to write */
	/* Number of uchars in data and ip header to write */
	len = blocklen(bp);
	ipout += len;
	ippout++;


@@ 104,30 105,30 @@ ipoput(Block *bp, int gating, int ttl)
		if(chunk < len)
			len = chunk;
	}
	if(len >= IP_MAX) {
		netlog(Logip, "exceeded ip max size %I\n", eh->dst);
	if(len >= IP_MAX){
		netlog(Logip, "exceeded ip max size %V\n", eh->dst);
		goto raise;
	}

	if(isbmcast(eh->dst)){
		m = Mediaroute(eh->src, nil);
		memmove(gate, eh->dst, IPaddrlen);
	} else
		m = Mediaroute(eh->dst, gate);
	if(m == nil){
	r = v4lookup(eh->dst);
	if(r == nil){
		stats.noroute++;
		netlog(Logip, "no interface %I\n", eh->dst);
		netlog(Logip, "no interface %V\n", eh->dst);
		goto raise;
	}

	if(r->type & (Rifc|Runi|Rbcast|Rmulti))
		gate = eh->dst;
	else
		gate = r->v4.gate;
	if(!gating){
		eh->vihl = IP_VER|IP_HLEN;
		eh->tos = 0;
		eh->ttl = ttl;
	}
	ifc = r->ifc;

	/* If we dont need to fragment just send it */
	medialen = m->maxmtu-m->hsize;
	medialen = ifc->m->maxmtu - ifc->m->hsize;
	if(len <= medialen) {
		if(!gating)
			hnputs(eh->id, Id++);


@@ 138,18 139,19 @@ ipoput(Block *bp, int gating, int ttl)
		eh->cksum[1] = 0;
		hnputs(eh->cksum, ipcsum(&eh->vihl));

		Mediawrite(m, bp, gate);
/*print("ipoput %V->%V via %V\n", eh->src, eh->dst, gate);*/
		ifc->m->bwrite(ifc, bp, V4, gate);
		return;
	}

	if(eh->frag[0] & (IP_DF>>8)){
		netlog(Logip, "%I: eh->frag[0] & (IP_DF>>8)\n", eh->dst);
		netlog(Logip, "%V: eh->frag[0] & (IP_DF>>8)\n", eh->dst);
		goto raise;
	}

	seglen = (medialen - IPHDR) & ~7;
	if(seglen < 8){
		netlog(Logip, "%I seglen < 8\n", eh->dst);
		netlog(Logip, "%V seglen < 8\n", eh->dst);
		goto raise;
	}



@@ 206,7 208,7 @@ ipoput(Block *bp, int gating, int ttl)
		feh->cksum[0] = 0;
		feh->cksum[1] = 0;
		hnputs(feh->cksum, ipcsum(&feh->vihl));
		Mediawrite(m, nb, gate);
		ifc->m->bwrite(ifc, nb, V4, gate);
	}

raise:


@@ 218,7 220,7 @@ initfrag(int size)
{
	Fragment *fq, *eq;

	fragfree = malloc(sizeof(Fragment) * size);
	fragfree = (Fragment*)malloc(sizeof(Fragment) * size);
	if(fragfree == nil)
		panic("initfrag");



@@ 234,12 236,13 @@ void (*ipextprotoiput)(Block*);
//#define DBG(x)	if((logmask & Logipmsg) && (iponly == 0 || x == iponly))netlog

void
ipiput(Media *m, Block *bp)
ipiput(uchar *ia, Block *bp)
{
	Iphdr *h;
	Proto *p;
	ushort frag;
	int notforme;
	uchar v6dst[IPaddrlen];

//	h = (Iphdr *)(bp->rp);
//	DBG(nhgetl(h->src))(Logipmsg, "ipiput %I %I len %d proto %d\n", h->src, h->dst, BLEN(bp), h->proto);


@@ 252,14 255,7 @@ ipiput(Media *m, Block *bp)
	}
	h = (Iphdr *)(bp->rp);

	/* Look to see if its for me before we waste time checksumming it */
	notforme = Mediaforme(h->dst) == 0;
	if(notforme && !iprouting) {
		netlog(Logip, "ip: pkt not for me\n");
		freeblist(bp);
		return;
	}

	/* dump anything that whose header doesn't checksum */
	if(ipcsum(&h->vihl)) {
		ipcsumerr++;
		netlog(Logip, "ip: checksum error %I\n", h->src);


@@ 267,6 263,21 @@ ipiput(Media *m, Block *bp)
		return;
	}

	/* route */
	v4tov6(v6dst, h->dst);
	notforme = ipforme(v6dst) == 0;
	if(notforme) {
		if(iprouting) {
			/* gate */
			if(h->ttl <= 1)
				freeblist(bp);
			else
				ipoput(bp, 1, h->ttl - 1);
		} else
			useriprouter(ia, bp);
		return;
	}

	/* Check header length and version */
	if(h->vihl != (IP_VER|IP_HLEN)) {
		netlog(Logip, "ip: %I bad hivl %ux\n", h->src, h->vihl);


@@ 287,20 298,9 @@ ipiput(Media *m, Block *bp)
	ipin += blocklen(bp);
	ippin++;

	if(iprouting) {
		/* gate */
		if(notforme){
			if(h->ttl == 0)
				freeblist(bp);
			else
				ipoput(bp, 1, h->ttl - 1);
			return;
		}
	}

	p = Fsrcvpcol(&fs, h->proto);
	if(p != nil && p->rcv != nil)
		(*p->rcv)(m, bp);
		(*p->rcv)(ia, bp);
	else if(ipextprotoiput != nil)
		ipextprotoiput(bp);
	else


@@ 325,7 325,7 @@ ipreassemble(int offset, Block *bp, Iphdr *ip)
	int fend;
	ushort id;
	Fragment *f, *fnext;
	Ipaddr src, dst;
	ulong src, dst;
	Block *bl, **l, *last, *prev;
	int ovlap, len, fragsize, pktposn;



@@ 532,7 532,7 @@ ipfragallo(void)
}

ushort
ipcsum(byte *addr)
ipcsum(uchar *addr)
{
	int len;
	ulong sum;

M ip/ip.h => ip/ip.h +297 -115
@@ 1,15 1,19 @@
typedef	ulong	Ipaddr;
typedef uchar	byte;
typedef struct	Conv	Conv;
typedef struct	Fs	Fs;
typedef union	Hwaddr	Hwaddr;
typedef struct	Ifcconv	Ifcconv;
typedef struct	Ipself	Ipself;
typedef struct	Iplink	Iplink;
typedef struct	Iplifc	Iplifc;
typedef struct	Ipmulti	Ipmulti;
typedef struct	Iproute	Iproute;
typedef struct	Media	Media;
typedef struct	Multicast	Multicast;
typedef struct	Ipifc	Ipifc;
typedef struct	Medium	Medium;
typedef struct	Proto	Proto;
typedef struct	Pstate	Pstate;
typedef struct	Tcpc	Tcpc;
typedef struct	Arpent	Arpent;
typedef struct	Route	Route;

enum
{


@@ 18,14 22,18 @@ enum
	Nhash=		64,
	Maxincall=	5,
	Nchans=		256,
	MAClen=		16,		/* longest mac address */

	MAXTTL=		255,

	IPaddrlen=	4,
	Ipbcast=	0xffffffff,	/* ip broadcast address */
	Ipbcastobs=	0,		/* obsolete (but still used) ip broadcast addr */
	Ipallsys=	0xe0000001,	/* multicast for all systems */
	Ipallrouter=	0xe0000002,	/* multicast for all routers */
	IPaddrlen=	16,
	IPv4addrlen=	4,
	IPv4off=	12,
	IPllen=		4,

	/* ip versions */
	V4=		4,
	V6=		6,
};

enum


@@ 46,8 54,8 @@ struct Conv
	int	x;			/* conversation index */
	Proto*	p;

	Ipaddr	laddr;			/* local IP address */
	Ipaddr	raddr;			/* remote IP address */
	uchar	laddr[IPaddrlen];	/* local IP address */
	uchar	raddr[IPaddrlen];	/* remote IP address */
	int	restricted;		/* remote port is restricted */
	ushort	lport;			/* local port number */
	ushort	rport;			/* remote port number */


@@ 77,86 85,104 @@ struct Conv
	QLock	listenq;
	Rendez	listenr;

	Ipmulti	*multi;			/* multicast bindings for this interface */

	void*	ptcl;			/* protocol specific stuff */
};

union Hwaddr
struct Medium
{
	byte	ether[6];
	char	*name;
	int	hsize;		/* medium header size */
	int	minmtu;		/* default min mtu */
	int	maxmtu;		/* default max mtu */
	int	maclen;		/* mac address length  */
	void	(*bind)(Ipifc*, int, char**);
	void	(*unbind)(Ipifc*);
	void	(*bwrite)(Ipifc *ifc, Block *b, int version, uchar *ip);

	/* for arming interfaces to receive multicast */
	void	(*addmulti)(Ipifc *ifc, uchar *a, uchar *ia);
	void	(*remmulti)(Ipifc *ifc, uchar *a, uchar *ia);

	/* process packets written to 'data' */
	void	(*pktin)(Ipifc *ifc, Block *bp);

	/* routes for router boards */
	void	(*addroute)(Ipifc *ifc, int, uchar*, uchar*, uchar*, int);
	void	(*remroute)(Ipifc *ifc, int, uchar*, uchar*);
	void	(*flushroutes)(Ipifc *ifc);

	/* for routing multicast groups */
	void	(*joinmulti)(Ipifc *ifc, uchar *a, uchar *ia, uchar **iap);
	void	(*leavemulti)(Ipifc *ifc, uchar *a, uchar *ia);
};

enum
/* logical interface associated with a physical one */
struct Iplifc
{
	METHER,			/* Media types */
	MFDDI,
	MPACKET,
	uchar	local[IPaddrlen];
	uchar	mask[IPaddrlen];
	uchar	remote[IPaddrlen];
	uchar	net[IPaddrlen];
	Iplink	*link;		/* addresses linked to this lifc */
	Iplifc	*next;
};

/* one per multicast address per medium */
struct Multicast
/* binding twixt Ipself and Ipifc */
struct Iplink
{
	Ipaddr	addr;
	Ipself	*local;
	Iplifc	*lifc;
	Iplink	*locallink;	/* next link for this local address */
	Iplink	*lifclink;	/* next link for this ifc */
	ulong	expire;
	Iplink	*next;		/* free list */
	int	ref;
	int	timeout;
	Multicast *next;
};

struct Media
struct Ipifc
{
	int	type;		/* Media type */
	Chan*	mchan;		/* Data channel */
	Chan*	achan;		/* Arp channel */
	Chan*	cchan;		/* Control channel */
	char*	dev;		/* device mfd points to */
	Ipaddr	myip[5];
	Ipaddr	mymask;
	Ipaddr	mynetmask;
	Ipaddr	remip;		/* Address of remote side */
	byte	netmyip[4];	/* In Network byte order */
	int	arping;		/* true if we mus arp */
	RWlock;
	
	Conv	*conv;		/* link to its conversation structure */
	char	dev[64];	/* device we're attached to */
	Medium	*m;		/* Media pointer */
	int	maxmtu;		/* Maximum transfer unit */
	int	minmtu;		/* Minumum tranfer unit */
	int	hsize;		/* Media header size */
	Hwaddr;
	void	*arg;		/* medium specific */

	/* these are used so that we can unbind on the fly */
	Lock	idlock;
	uchar	ifcid;		/* incremented each 'bind/unbind/add/remove' */
	int	ref;		/* number of proc's using this ipifc */
	Rendez	wait;		/* where unbinder waits for ref == 0 */
	int	unbinding;

	uchar	mac[MAClen];	/* MAC address */

	Iplifc	*lifc;		/* logical interfaces on this physical one */

	ulong	in, out;	/* message statistics */
	ulong	inerr, outerr;	/* ... */
	int	inuse;
	Conv	*c;		/* for packet interface */

	QLock	mlock;		/* lock for changing *multi */
	Multicast *multi;	/* list of multicast addresses we're listening to */
	int	mactive;	/* number of active multicast addresses */
};

	Media*	link;
/*
 *  one per multicast-lifc pair used by a Conv
 */
struct Ipmulti
{
	uchar	ma[IPaddrlen];
	uchar	ia[IPaddrlen];
	Ipmulti	*next;
};
int	Mediabooting(void);
int	Mediaforme(byte*);
int	Mediaforpt2pt(byte*);
Ipaddr	Mediagetsrc(byte*);
void	Mediaclose(Media*);
char*	Mediaopen(int, char*, Conv*, Ipaddr, Ipaddr, Ipaddr, int, Media**);
Media*	Mediaroute(byte*, byte*);
void	Mediasetaddr(Media*, Ipaddr, Ipaddr);
void	Mediasetraddr(Media*, Ipaddr);
Ipaddr	Mediagetaddr(Media*);
Ipaddr	Mediagetraddr(Media*);
void	Mediawrite(Media*, Block*, byte*);
int	Mediaifcread(char*, ulong, int);
char*	Mediaifcwrite(Ifcconv*, char*, int);
void	Mediaresolver(Media*);
void	Mediaread(Media*);
int	Mediaarp(Media*, Block*, byte*, Hwaddr*);
Media*	Mediafind(Iproute*);
Multicast*	Mediacopymulti(Media*);
void	Mediamulticastadd(Media*, Ifcconv*, Ipaddr);
void	Mediamulticastrem(Media*, Ipaddr);

/*
 *  one per multiplexed protocol
 */
struct Proto
{
	Lock;
	QLock;
	char*		name;		/* protocol name */
	int		x;		/* protocol index */
	int		ipproto;	/* ip protocol type */


@@ 164,13 190,16 @@ struct Proto
	void		(*kick)(Conv*, int);
	char*		(*connect)(Conv*, char**, int);
	char*		(*announce)(Conv*, char**, int);
	int		(*state)(char**, Conv*);
	char*		(*bind)(Conv*, char**, int);
	int		(*state)(Conv*, char*, int);
	void		(*create)(Conv*);
	void		(*close)(Conv*);
	void		(*rcv)(Media*, Block*);
	void		(*rcv)(uchar*, Block*);
	char*		(*ctl)(Conv*, char**, int);
	void		(*advise)(Block*, char*);
	int		(*stats)(char*, int);
	int		(*local)(Conv*, char*, int);
	int		(*inuse)(Conv*);

	Conv		**conv;		/* array of conversations */
	int		ptclsize;	/* size of per protocol ctl block */


@@ 196,16 225,19 @@ struct Fs
	Proto*	t2p[256];		/* vector of all ip protocol handlers */
};
int	Fsconnected(Fs*, Conv*, char*);
Conv*	Fsnewcall(Fs*, Conv*, Ipaddr, ushort, Ipaddr, ushort);
Conv*	Fsnewcall(Fs*, Conv*, uchar*, ushort, uchar*, ushort);
int	Fspcolstats(char*, int);
int	Fsproto(Fs*, Proto*);
int	Fsbuiltinproto(Fs*, byte);
int	Fsbuiltinproto(Fs*, uchar);
Conv*	Fsprotoclone(Proto*, char*);
Proto*	Fsrcvpcol(Fs*, byte);
Proto*	Fsrcvpcol(Fs*, uchar);
char*	Fsstdconnect(Conv*, char**, int);
char*	Fsstdannounce(Conv*, char**, int);
char*	Fsstdbind(Conv*, char**, int);

/* log flags */
/* 
 *  logging
 */
enum
{
	Logip=		1<<1,


@@ 225,7 257,8 @@ enum
};

extern int	logmask;	/* mask of things to debug */
extern Ipaddr	iponly;		/* ip address to print debugging for */
extern uchar	iponly[IPaddrlen];		/* ip address to print debugging for */
extern int	iponlyset;

void netlogopen(void);
void netlogclose(void);


@@ 233,51 266,200 @@ char* netlogctl(char*, int);
long netlogread(void*, ulong, long);
void netlog(int, char*, ...);

#define	msec	TK2MS(MACHP(0)->ticks)
/*
 *  iproute.c
 */
typedef	struct RouteTree RouteTree;
typedef struct Routewalk Routewalk;
typedef struct V4route V4route;
typedef struct V6route V6route;

/* Globals */
extern int	debug;
extern Fs	fs;
extern Media*	media;
extern int	iprouting;	/* true if routing turned on */
extern void	(*igmpreportfn)(Media*, byte*);

int	arpread(byte*, ulong, int);
char*	arpwrite(char*, int);
void	closeifcconv(Ifcconv*);
Ipaddr	defmask(Ipaddr);
int	eipconv(va_list*, Fconv*);
int	equivip(byte*, byte*);
void	fatal(byte*, ...);
void	hnputl(byte*, ulong);
void	hnputs(byte*, ushort);
void	icmpnoconv(Block*);
void	initfrag(int);
ushort	ipcsum(byte*);
void	(*ipextprotoiput)(Block*);
Ipaddr	ipgetsrc(byte*);
void	ipiput(Media*, Block*);
void	ipoput(Block*, int, int);
int	ipstats(char*, int);
int	ismcast(byte*);
int	isbmcast(byte*);
byte*	logctl(byte*);
void	maskip(byte*, byte*, byte*);
Ifcconv* newifcconv(void);
ulong	nhgetl(byte*);
ushort	nhgets(byte*);
void	(*pktifcrcv)(Conv*, Block*);
ushort	ptclbsum(byte*, int);
ushort	ptclcsum(Block*, int, int);
int	pullblock(Block**, int);
Block*	pullupblock(Block*, int);
char*	routeadd(Ipaddr, Ipaddr, Ipaddr, Media *m);
void	routedelete(ulong, ulong, Media *m);
int	routeread(byte*, ulong, int);
char*	routewrite(char*, int);
enum
{

	/* type bits */
	Rv4=		(1<<0),		/* this is a version 4 route */
	Rifc=		(1<<1),		/* this route is a directly connected interface */
	Rptpt=		(1<<2),		/* this route is a pt to pt interface */
	Runi=		(1<<3),		/* a unicast self address */
	Rbcast=		(1<<4),		/* a broadcast self address */
	Rmulti=		(1<<5),		/* a multicast self address */
};

struct Routewalk {
	int	n;
	int	o;
	int	h;
	char*	p;
	void*	state;
	void	(*walk)(Route*, Routewalk*);
};

struct	RouteTree
{
	Route*	right;
	Route*	left;
	Route*	mid;
	uchar	depth;
	uchar	type;
	uchar	ifcid;		/* must match ifc->id */
	Ipifc	*ifc;
	char	tag[4];
};

struct V4route
{
	ulong	address;
	ulong	endaddress;
	uchar	gate[IPv4addrlen];
};

struct V6route
{
	ulong	address[IPllen];
	ulong	endaddress[IPllen];
	uchar	gate[IPaddrlen];
};

struct Route
{
	RouteTree;

	union {
		V6route	v6;
		V4route v4;
	};
};
extern void	v4addroute(char *tag, uchar *a, uchar *mask, uchar *gate, int type);
extern void	v6addroute(char *tag, uchar *a, uchar *mask, uchar *gate, int type);
extern void	v4delroute(uchar *a, uchar *mask);
extern void	v6delroute(uchar *a, uchar *mask);
extern Route*	v4lookup(uchar *a);
extern Route*	v6lookup(uchar *a);
extern long	routeread(char*, ulong, int);
extern long	routewrite(Chan*, char*, int);

/*
 *  arp.c
 */
struct Arpent
{
	uchar	ip[IPaddrlen];
	uchar	mac[MAClen];
	Medium	*type;		/* media type */
	Arpent*	hash;
	Block*	hold;
	Block*	last;
	uint	time;
	uint	used;
	uchar	state;
};

extern int	arpread(char*, ulong, int);
extern int	arpwrite(char*, int);
extern Arpent*	arpget(Block *bp, int version, Medium *type, uchar *ip, uchar *h);
extern void	arprelease(Arpent *a);
extern Block*	arpresolve(Arpent *a, Medium *type, uchar *mac);
extern void	arpenter(Ipifc *ifc, int version, uchar *ip, uchar *mac, Medium *type, int norefresh);

/*
 * ipaux.c
 */
int	myetheraddr(uchar*, char*);
ulong	parseip(uchar*, char*);

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*);
extern void	maskip(uchar *from, uchar *mask, uchar *to);
extern int	parsemac(uchar *to, char *from, int len);
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)
#define	ipmove(x, y) memmove(x, y, IPaddrlen)

extern uchar IPv4bcast[IPaddrlen];
extern uchar IPv4bcastobs[IPaddrlen];
extern uchar IPv4allsys[IPaddrlen];
extern uchar IPv4allrouter[IPaddrlen];
extern uchar IPnoaddr[IPaddrlen];
extern uchar v4prefix[IPaddrlen];
extern uchar IPallbits[IPaddrlen];

#define	msec	TK2MS(MACHP(0)->ticks)

/*
 *  media
 */
extern Medium	ethermedium;
extern Medium	nullmedium;
extern Medium	pktmedium;
extern Proto	ipifc;	

/*
 *  ipifc.c
 */
extern Medium*	ipfindmedium(char *name);
extern int	ipforme(uchar *addr);
extern int	ipismulticast(uchar *);
extern Ipifc*	findipifc(uchar *remote, int type);
extern void	findlocalip(uchar *local, uchar *remote);
extern int	ipv4local(Ipifc *ifc, uchar *addr);
extern int	ipv6local(Ipifc *ifc, uchar *addr);
extern Iplifc*	iplocalonifc(Ipifc *ifc, uchar *ip);
extern int	ipproxyifc(Ipifc *ifc, uchar *ip);
extern int	ipismulticast(uchar *ip);
extern int	ipisbooting(void);
extern int	ipifccheckin(Ipifc *ifc, Medium *med);
extern void	ipifccheckout(Ipifc *ifc);
extern int	ipifcgrab(Ipifc *ifc);
extern void	ipifcaddroute(int, uchar*, uchar*, uchar*, int);
extern void	ipifcremroute(int, uchar*, uchar*);
extern void	ipifcremmulti(Conv *c, uchar *ma, uchar *ia);
extern void	ipifcaddmulti(Conv *c, uchar *ma, uchar *ia);
extern char*	ipifcrem(Ipifc *ifc, char **argv, int argc, int dolock);
extern char*	ipifcadd(Ipifc *ifc, char **argv, int argc);

/*
 *  ip.c
 */
extern void	closeifcconv(Ifcconv*);
extern void	icmpnoconv(Block*);
extern void	initfrag(int);
extern ushort	ipcsum(uchar*);
extern void	(*ipextprotoiput)(Block*);
extern void	ipiput(uchar*, Block*);
extern void	ipoput(Block*, int, int);
extern int	ipstats(char*, int);
extern uchar*	logctl(uchar*);
extern Ifcconv* newifcconv(void);
extern void	(*pktifcrcv)(Conv*, Block*);
extern ushort	ptclbsum(uchar*, int);
extern ushort	ptclcsum(Block*, int, int);

/*
 *  iprouter.c
 */
void	useriprouter(uchar*, Block*);
void	iprouteropen(void);
void	iprouterclose(void);
long	iprouterread(void*, int);

/*
 *  global to all of the stack
 */
extern int	debug;
extern Fs	fs;
extern int	iprouting;	/* true if routing turned on */
extern void	(*igmpreportfn)(Ipifc*, uchar*);

M ip/ipaux.c => ip/ipaux.c +283 -60
@@ 7,10 7,50 @@
#include	"kernel.h"
#include	"ip.h"

/*
 *  well known IP addresses
 */
uchar IPv4bcast[IPaddrlen] = {
	0, 0, 0, 0,
	0, 0, 0, 0,
	0, 0, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff
};
uchar IPv4allsys[IPaddrlen] = {
	0, 0, 0, 0,
	0, 0, 0, 0,
	0, 0, 0xff, 0xff,
	0xe0, 0, 0, 0x01
};
uchar IPv4allrouter[IPaddrlen] = {
	0, 0, 0, 0,
	0, 0, 0, 0,
	0, 0, 0xff, 0xff,
	0xe0, 0, 0, 0x02
};
uchar IPallbits[IPaddrlen] = {
	0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff
};
uchar IPnoaddr[IPaddrlen];

/*
 *  prefix of all v4 addresses
 */
uchar v4prefix[IPaddrlen] = {
	0, 0, 0, 0,
	0, 0, 0, 0,
	0, 0, 0xff, 0xff,
	0, 0, 0, 0
};


ushort
ptclcsum(Block *bp, int offset, int len)
{
	byte *addr;
	uchar *addr;
	ulong losum, hisum;
	ushort csum;
	int odd, blocklen, x;


@@ 64,65 104,34 @@ ptclcsum(Block *bp, int offset, int len)
	return ~losum & 0xffff;
}

Ipaddr
defmask(Ipaddr ip)
enum
{
	switch(ip>>30){
	default:
		return 0xff000000;
	case 2:
		return 0xffff0000;
	case 3:
		return 0xffffff00;
	}
}
	Isprefix= 16,
};

int
myetheraddr(uchar *to, char *dev)
uchar prefixvals[256] =
{
	int n, fd;
	char buf[256], *ptr;

	/* Make one exist */
	if(*dev == '/' || *dev == '#')
		sprint(buf, "%s/clone", dev);
	else
		sprint(buf, "/net/%s/clone", dev);

	fd = kopen(buf, ORDWR);
	if(fd >= 0)
		kclose(fd);

	if(*dev == '/' || *dev == '#')
		sprint(buf, "%s/0/stats", dev);
	else
		sprint(buf, "/net/%s/0/stats", dev);
	fd = kopen(buf, OREAD);
	if(fd < 0)
		return -1;

	n = kread(fd, buf, sizeof(buf)-1);
	kclose(fd);
	if(n <= 0)
		return -1;
	buf[n] = 0;

	ptr = strstr(buf, "addr: ");
	if(!ptr)
		return -1;
	ptr += 6;

	parseether(to, ptr);
	return 0;
}
[0x00] 0 | Isprefix,
[0x80] 1 | Isprefix,
[0xC0] 2 | Isprefix,
[0xE0] 3 | Isprefix,
[0xF0] 4 | Isprefix,
[0xF8] 5 | Isprefix,
[0xFC] 6 | Isprefix,
[0xFE] 7 | Isprefix,
[0xFF] 8 | Isprefix,
};

int
eipconv(va_list *arg, Fconv *f)
{
	char buf[64];
	char buf[8*5];
	static char *efmt = "%.2lux%.2lux%.2lux%.2lux%.2lux%.2lux";
	static char *ifmt = "%d.%d.%d.%d";
	uchar *p, ip[4];
	uchar *p, ip[16];
	ulong *lp;
	ushort s;
	int i, j, n, eln, eli;

	switch(f->chr) {
	case 'E':		/* Ethernet address */


@@ 131,11 140,66 @@ eipconv(va_list *arg, Fconv *f)
		break;
	case 'I':		/* Ip address */
		p = va_arg(*arg, uchar*);
common:
		if(memcmp(p, v4prefix, 12) == 0)
			sprint(buf, ifmt, p[12], p[13], p[14], p[15]);
		else {
			/* find longest elision */
			eln = eli = -1;
			for(i = 0; i < 16; i += 2){
				for(j = i; j < 16; j += 2)
					if(p[j] != 0 || p[j+1] != 0)
						break;
				if(j > i && j - i > eln){
					eli = i;
					eln = j - i;
				}
			}

			/* print with possible elision */
			n = 0;
			for(i = 0; i < 16; i += 2){
				if(i == eli){
					n += sprint(buf+n, "::");
					i += eln;
					if(i >= 16)
						break;
				} else if(i != 0)
					n += sprint(buf+n, ":");
				s = (p[i]<<8) + p[i+1];
				n += sprint(buf+n, "%ux", s);
			}
		}
		break;
	case 'i':		/* v6 address as 4 longs */
		lp = va_arg(*arg, ulong*);
		for(i = 0; i < 4; i++)
			hnputl(ip+4*i, *lp++);
		p = ip;
		goto common;
	case 'V':		/* v4 ip address */
		p = va_arg(*arg, uchar*);
		sprint(buf, ifmt, p[0], p[1], p[2], p[3]);
		break;
	case 'i':
		hnputl(ip, va_arg(*arg, ulong));
		sprint(buf, ifmt, ip[0], ip[1], ip[2], ip[3]);
	case 'M':		/* ip mask */
		p = va_arg(*arg, uchar*);

		/* look for a prefix mask */
		for(i = 0; i < 16; i++)
			if(p[i] != 0xff)
				break;
		if(i < 16){
			if((prefixvals[p[i]] & Isprefix) == 0)
				goto common;
			for(j = i+1; j < 16; j++)
				if(p[j] != 0)
					goto common;
			n = 8*i + (prefixvals[p[i]] & ~Isprefix);
		} else
			n = 8*16;

		/* got one, use /xx format */
		sprint(buf, "/%d", n);
		break;
	default:
		strcpy(buf, "(eipconv)");


@@ 146,21 210,20 @@ eipconv(va_list *arg, Fconv *f)

#define CLASS(p) ((*(uchar*)(p))>>6)

ulong
parseip(uchar *to, char *from)
static char*
v4parseip(uchar *to, char *from)
{
	int i;
	char *p;

	p = from;
	memset(to, 0, 4);
	for(i = 0; i < 4 && *p; i++){
		to[i] = strtoul(p, &p, 0);
		if(*p == '.')
			p++;
	}
	switch(CLASS(to)){
	case 0:	/* class A - 1 byte net */
	case 0:	/* class A - 1 uchar net */
	case 1:
		if(i == 3){
			to[3] = to[2];


@@ 171,12 234,172 @@ parseip(uchar *to, char *from)
			to[1] = 0;
		}
		break;
	case 2:	/* class B - 2 byte net */
	case 2:	/* class B - 2 uchar net */
		if(i == 3){
			to[3] = to[2];
			to[2] = 0;
		}
		break;
	}
	return nhgetl(to);
	return p;
}

int
isv4(uchar *ip)
{
	return memcmp(ip, v4prefix, IPv4off) == 0;
}

void
v4tov6(uchar *v6, uchar *v4)
{
	memmove(v6, v4prefix, IPv4off);
	memmove(v6 + IPv4off, v4, IPv4addrlen);
}

int
v6tov4(uchar *v4, uchar *v6)
{
	if(memcmp(v6, v4prefix, IPv4off) != 0)
		return -1;
	memmove(v4, v6 + IPv4off, IPv4addrlen);
	return 0;
}

ulong
parseip(uchar *to, char *from)
{
	int i, elipsis = 0, v4 = 1;
	ulong x;
	char *p, *op;

	memset(to, 0, IPaddrlen);
	p = from;
	for(i = 0; i < 16 && *p; i+=2){
		op = p;
		x = strtoul(p, &p, 16);
		if(*p == '.' || (*p == 0 && i == 0)){
			p = v4parseip(to+i, op);
			i += 2;
		} else {
			to[i] = x>>8;
			to[i+1] = x;
		}
		if(*p == ':'){
			v4 = 0;
			if(*++p == ':'){
				elipsis = i+2;
				p++;
			}
		}
	}
	if(i < 16){
		memmove(&to[elipsis+16-i], &to[elipsis], i-elipsis);
		memset(&to[elipsis], 0, 16-i);
	}
	if(v4){
		to[10] = to[11] = 0xff;
		return nhgetl(to+12);
	} else
		return 6;
}

/*
 *  hack to allow ip v4 masks to be entered in the old
 *  style
 */
ulong
parseipmask(uchar *to, char *from)
{
	ulong x;
	int i;
	uchar *p;

	if(*from == '/'){
		/* as a number of prefix bits */
		i = atoi(from+1);
		if(i < 0)
			i = 0;
		if(i > 128)
			i = 128;
		memset(to, 0, IPaddrlen);
		for(p = to; i >= 8; i -= 8)
			*p++ = 0xff;
		if(i > 0)
			*p = ~((1<<(8-i))-1);
		x = nhgetl(to+IPv4off);
	} else {
		/* as a straight bit mask */
		x = parseip(to, from);
		if(memcmp(to, v4prefix, IPv4off) == 0)
			memset(to, 0xff, IPv4off);
	}
	return x;
}

void
maskip(uchar *from, uchar *mask, uchar *to)
{
	int i;

	for(i = 0; i < IPaddrlen; i++)
		to[i] = from[i] & mask[i];
}

uchar classmask[4][16] = {
	0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0xff,  0xff,0x00,0x00,0x00,
	0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0xff,  0xff,0x00,0x00,0x00,
	0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0xff,  0xff,0xff,0x00,0x00,
	0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0x00,
};

uchar*
defmask(uchar *ip)
{
	return classmask[ip[IPv4off]>>6];
}

/*
 *  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);
	cb->buf[n] = '\0';
	cb->nf = parsefields(cb->buf, cb->f, nelem(cb->f), " ");
	return cb;
}

/*
 *  parse a hex mac address 
 */
int
parsemac(uchar *to, char *from, int len)
{
	char nip[4];
	char *p;
	int i;

	p = from;
	memset(to, 0, len);
	for(i = 0; i < len; 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;
}

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

#include "ip.h"

#define DPRINT if(0)print

enum {
	Maxmedia	= 16,
	Nself		= Maxmedia*5,
	NHASH		= (1<<6),
	NCACHE		= 256,
	QMAX		= 64*1024-1,
};

	Proto	ipifc;
extern	Fs	fs;

Medium *media[] =
{
	&ethermedium,
	&nullmedium,
	0
};

/*
 *  cache of local addresses (addresses we answer to)
 */
typedef struct Ipself Ipself;
struct Ipself
{
	uchar	a[IPaddrlen];
	Ipself	*hnext;		/* next address in the hash table */
	Iplink	*link;		/* binding twixt Ipself and Ipifc */
	ulong	expire;
	uchar	type;		/* type of address */
	int	ref;
	Ipself	*next;		/* free list */
};

typedef struct Ipselftab Ipselftab;
struct Ipselftab
{
	QLock;
	int	inited;
	int	acceptall;	/* true if an interface has the null address */
	Ipself	*hash[NHASH];	/* hash chains */
};
Ipselftab	selftab;

/*
 *  Multicast addresses are chained onto a Chan so that
 *  we can remove them when the Chan is closed.
 */
typedef struct Ipmcast Ipmcast;
struct Ipmcast
{
	Ipmcast	*next;
	uchar	ma[IPaddrlen];	/* multicast address */
	uchar	ia[IPaddrlen];	/* interface address */
};

/* quick hash for ip addresses */
#define hashipa(a) ( ( ((a)[IPaddrlen-2]<<8) | (a)[IPaddrlen-1] )%NHASH )

static char tifc[] = "ifc ";

static void	addselfcache(Ipifc *ifc, Iplifc *lifc, uchar *a, int type);
static void	remselfcache(Ipifc *ifc, Iplifc *lifc, uchar *a);
static char*	ipifcjoinmulti(Ipifc *ifc, char **argv, int argc);
static char*	ipifcleavemulti(Ipifc *ifc, char **argv, int argc);

/*
 *  find the medium with this name
 */
Medium*
ipfindmedium(char *name)
{
	Medium **mp;

	for(mp = media; *mp != nil; mp++)
		if(strcmp((*mp)->name, name) == 0)
			break;
	return *mp;
}

/*
 *  attach a device (or pkt driver) to the interface.
 *  called with c->car locked
 */
static char*
ipifcbind(Conv *c, char **argv, int argc)
{
	Ipifc *ifc;
	Medium *m;

	if(argc < 2)
		return Ebadarg;

	ifc = (Ipifc*)c->ptcl;

	/* bind the device to the interface */
	m = ipfindmedium(argv[1]);
	if(m == nil)
		return "unknown interface type";

	wlock(ifc);
	if(ifc->m != nil){
		wunlock(ifc);
		return "interface already bound";	
	}
	if(waserror()){
		wunlock(ifc);
		nexterror();
	}

	(*m->bind)(ifc, argc, argv);
	if(argc > 2)
		strncpy(ifc->dev, argv[2], sizeof(ifc->dev));
	else
		sprint(ifc->dev, "%s%d", m->name, c->x);
	ifc->dev[sizeof(ifc->dev)-1] = 0;
	ifc->m = m;
	ifc->minmtu = ifc->m->minmtu;
	ifc->maxmtu = ifc->m->maxmtu;
	ifc->ifcid++;

	wunlock(ifc);
	poperror();

	return nil;
}

/*
 *  detach a device from an interface, close the interface
 */
static char*
ipifcunbind(Ipifc *ifc)
{
	char *av[4];
	char ip[32];
	char mask[32];

	if(waserror()){
		wunlock(ifc);
		nexterror();
	}
	wlock(ifc);

	if(ipifcgrab(ifc) == 0);
		goto out;

	/* hangup queues to stop queuing of packets */
	qhangup(ifc->conv->rq, "unbind");
	qhangup(ifc->conv->wq, "unbind");

	/* dissociate routes */
	ifc->ifcid++;

	/* disassociate logical interfaces */
	av[0] = "remove";
	av[1] = ip;
	av[2] = mask;
	av[3] = 0;
	while(ifc->lifc){
		sprint(ip, "%I", ifc->lifc->local);
		sprint(mask, "%M", ifc->lifc->mask);
		ipifcrem(ifc, av, 3, 0);
	}

	/* disassociate device */
	(*ifc->m->unbind)(ifc);
	memset(ifc->dev, 0, sizeof(ifc->dev));
	ifc->arg = nil;
	ifc->m = &nullmedium;
	ifc->unbinding = 0;

out:
	wunlock(ifc);
	poperror();
	return nil;
}

static int
ipifcstate(Conv *c, char *state, int n)
{
	Ipifc *ifc;
	Iplifc *lifc;
	int m;

	ifc = (Ipifc*)c->ptcl;

	m = snprint(state, n, "%-12.12s %-5d", ifc->dev, ifc->maxmtu);

	rlock(ifc);
	for(lifc = ifc->lifc; lifc; lifc = lifc->next)
		m += snprint(state+m, n - m,
			" %-20.20I %-20.20M %-20.20I %-7d %-7d %-7d %-7d",
				lifc->local, lifc->mask, lifc->remote,
				ifc->in, ifc->out, ifc->inerr, ifc->outerr);
	m += snprint(state+m, n - m, "\n");
	runlock(ifc);
	return m;
}

static int
ipifclocal(Conv *c, char *state, int n)
{
	Ipifc *ifc;
	Iplifc *lifc;
	Iplink *link;
	int m;

	ifc = (Ipifc*)c->ptcl;

	m = 0;

	rlock(ifc);
	for(lifc = ifc->lifc; lifc; lifc = lifc->next){
		m += snprint(state+m, n - m, "%-20.20I ->", lifc->local);
		for(link = lifc->link; link; link = link->lifclink)
			m += snprint(state+m, n - m, " %-20.20I", link->local->a);
		m += snprint(state+m, n - m, "\n");
	}
	runlock(ifc);
	return m;
}

static int
ipifcinuse(Conv *c)
{
	Ipifc *ifc;

	ifc = (Ipifc*)c->ptcl;
	return ifc->m != nil;
}

/*
 *  called when a process writes to an interface's 'data'
 */
static void
ipifckick(Conv *c, int)
{
	Block *bp;
	Ipifc *ifc;

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

	ifc = (Ipifc*)c->ptcl;
	if(ifc->m == nil || ifc->m->pktin == nil)
		freeb(bp);
	else
		(*ifc->m->pktin)(ifc, bp);
}

/*
 *  we'll have to have a kick routine at
 *  some point to deal with these
 */
static void
ipifccreate(Conv *c)
{
	Ipifc *ifc;

	c->rq = qopen(QMAX, 0, 0, 0);
	c->wq = qopen(QMAX, 0, 0, 0);
	ifc = (Ipifc*)c->ptcl;
	ifc->conv = c;
}

/* 
 *  called after last close of ipifc data or ctl
 *  called with c locked, we must unlock
 */
static void
ipifcclose(Conv *c)
{
	/*
	 *  nothing to do since conversation stays open
	 *  till the device is unbound.
	 */
	unlock(c);
}

/*
 *  add an address to an interface.
 */
char*
ipifcadd(Ipifc *ifc, char **argv, int argc)
{
	uchar ip[IPaddrlen], mask[IPaddrlen], rem[IPaddrlen];
	uchar bcast[IPaddrlen], net[IPaddrlen];
	Iplifc *lifc, **l;
	int i, type, mtu;

	memset(ip, 0, IPaddrlen);
	memset(mask, 0, IPaddrlen);
	memset(rem, 0, IPaddrlen);
	switch(argc){
	case 5:
		mtu = strtoul(argv[4], 0, 0);
		if(mtu >= ifc->m->minmtu && mtu <= ifc->m->maxmtu)
			ifc->maxmtu = mtu;
		/* fall through */
	case 4:
		parseip(ip, argv[1]);
		parseipmask(mask, argv[2]);
		parseip(rem, argv[3]);
		maskip(rem, mask, net);
		break;
	case 3:
		parseip(ip, argv[1]);
		parseipmask(mask, argv[2]);
		maskip(ip, mask, rem);
		maskip(rem, mask, net);
		break;
	case 2:
		parseip(ip, argv[1]);
		memmove(mask, defmask(ip), IPaddrlen);
		maskip(ip, mask, rem);
		maskip(rem, mask, net);
		break;
	default:
		return Ebadarg;
		break;
	}

	if(waserror()){
		wunlock(ifc);
		panic("ipifcadd");
	}
	wlock(ifc);

	/* ignore if this is already a local address for this ifc */
	for(lifc = ifc->lifc; lifc; lifc = lifc->next)
		if(ipcmp(lifc->local, ip) == 0)
			goto out;

	/* add the address to the list of logical ifc's for this ifc */
	lifc = smalloc(sizeof(Iplifc));
	ipmove(lifc->local, ip);
	ipmove(lifc->mask, mask);
	ipmove(lifc->remote, rem);
	ipmove(lifc->net, net);
	lifc->next = nil;
	for(l = &ifc->lifc; *l; l = &(*l)->next)
		;
	*l = lifc;

	/* add a route for the local network */
	type = Rifc;
	if(ipcmp(mask, IPallbits) == 0)
		type |= Rptpt;
	if(isv4(ip))
		v4addroute(tifc, rem+IPv4off, mask+IPv4off, ip+IPv4off, type);
	else
		v6addroute(tifc, ip, mask, rem, type);

	addselfcache(ifc, lifc, ip, Runi);

	/* add subnet directed broadcast addresses to the self cache */
	for(i = 0; i < IPaddrlen; i++)
		bcast[i] = (ip[i] & mask[i]) | ~mask[i];
	addselfcache(ifc, lifc, bcast, Rbcast);

	/* add network directed broadcast addresses to the self cache */
	memmove(mask, defmask(ip), IPaddrlen);
	for(i = 0; i < IPaddrlen; i++)
		bcast[i] = (ip[i] & mask[i]) | ~mask[i];
	addselfcache(ifc, lifc, bcast, Rbcast);

	addselfcache(ifc, lifc, IPv4bcast, Rbcast);

out:
	wunlock(ifc);
	poperror();
	return nil;
}

/*
 *  remove an address from an interface.
 *  called with c->car locked
 */
char*
ipifcrem(Ipifc *ifc, char **argv, int argc, int dolock)
{
	uchar ip[IPaddrlen];
	uchar mask[IPaddrlen];
	Iplifc *lifc, **l;

	if(argc < 3)
		return Ebadarg;

	parseip(ip, argv[1]);
	parseipmask(mask, argv[2]);

	if(dolock){
		if(waserror()){
			wunlock(ifc);
			nexterror();
		}
		wlock(ifc);
	}

	/* find address on this interface and remove from chain */
	lifc = nil;
	for(l = &ifc->lifc; *l; l = &(*l)->next)
		if(memcmp(ip, (*l)->local, IPaddrlen) == 0)
		if(memcmp(mask, (*l)->mask, IPaddrlen) == 0){
			lifc = *l;
			*l = lifc->next;
			break;
		}

	if(lifc == nil)
		return "address not on this interface";

	/* disassociate any addresses */
	while(lifc->link)
		remselfcache(ifc, lifc, lifc->link->local->a);

	/* remove the route for this logical interface */
	if(isv4(ip))
		v4delroute(lifc->remote+IPv4off, lifc->mask+IPv4off);
	else
		v6delroute(lifc->remote, lifc->mask);

	free(lifc);

out:
	if(dolock){
		wunlock(ifc);
		poperror();
	}
	return nil;
}

/*
 * distrbute routes to active interfaces like the
 * TRIP linecards
 */
void
ipifcaddroute(int vers, uchar *addr, uchar *mask, uchar *gate, int type)
{
	Medium *m;
	Conv **cp;
	Ipifc *ifc;

	for(cp = ipifc.conv; cp < &ipifc.conv[ipifc.nc]; cp++){
		if(*cp != nil) {
			ifc = (Ipifc*)(*cp)->ptcl;
			m = ifc->m;
			if(m->addroute != nil)
				m->addroute(ifc, vers, addr, mask, gate, type);
		}
	}
}

void
ipifcremroute(int vers, uchar *addr, uchar *mask)
{
	Medium *m;
	Conv **cp;
	Ipifc *ifc;

	for(cp = ipifc.conv; cp < &ipifc.conv[ipifc.nc]; cp++){
		if(*cp != nil) {
			ifc = (Ipifc*)(*cp)->ptcl;
			m = ifc->m;
			if(m->remroute != nil)
				m->remroute(ifc, vers, addr, mask);
		}
	}
}

/*
 *  associate an address with the interface.  This wipes out any previous
 *  addresses.  This is a macro that means, remove all the old interfaces
 *  and add a new one.
 */
static char*
ipifcconnect(Conv* c, char **argv, int argc)
{
	char *err;
	Ipifc *ifc;
	char *av[4];
	char ip[80], mask[80];

	ifc = (Ipifc*)c->ptcl;

	if(ifc->m == nil)
		 return "ipifc not yet bound to device";

	av[0] = "remove";
	av[1] = ip;
	av[2] = mask;
	av[3] = 0;
	if(waserror()){
		wunlock(ifc);
		nexterror();
	}
	wlock(ifc);
	while(ifc->lifc){
		sprint(ip, "%I", ifc->lifc->local);
		sprint(mask, "%I", ifc->lifc->mask);
		ipifcrem(ifc, av, 3, 0);
	}
	wunlock(ifc);
	poperror();

	err = ipifcadd(ifc, argv, argc);
	if(err)
		return err;

	Fsconnected(&fs, c, nil);

	return nil;
}

/*
 *  non-standard control messages.
 *  called with c->car locked.
 */
static char*
ipifcctl(Conv* c, char**argv, int argc)
{
	Ipifc *ifc;

	ifc = (Ipifc*)c->ptcl;
	if(strcmp(argv[0], "add") == 0)
		return ipifcadd(ifc, argv, argc);
	else if(strcmp(argv[0], "remove") == 0)
		return ipifcrem(ifc, argv, argc, 1);
	else if(strcmp(argv[0], "unbind") == 0)
		return ipifcunbind(ifc);
	else if(strcmp(argv[0], "joinmulti") == 0)
		return ipifcjoinmulti(ifc, argv, argc);
	else if(strcmp(argv[0], "leavemulti") == 0)
		return ipifcleavemulti(ifc, argv, argc);
	return "unsupported ctl";
}

void
ipifcinit(Fs *fs)
{
	ipifc.name = "ipifc";
	ipifc.kick = ipifckick;
	ipifc.connect = ipifcconnect;
	ipifc.announce = nil;
	ipifc.bind = ipifcbind;
	ipifc.state = ipifcstate;
	ipifc.create = ipifccreate;
	ipifc.close = ipifcclose;
	ipifc.rcv = nil;
	ipifc.ctl = ipifcctl;
	ipifc.advise = nil;
	ipifc.stats = ipstats;
	ipifc.inuse = ipifcinuse;
	ipifc.local = ipifclocal;
	ipifc.ipproto = -1;
	ipifc.nc = Maxmedia;
	ipifc.ptclsize = sizeof(Ipifc);

	Fsproto(fs, &ipifc);
}

/*
 *  add to self routing cache
 *	called with c->car locked
 */
static void
addselfcache(Ipifc *ifc, Iplifc *lifc, uchar *a, int type)
{
	Ipself *p;
	Iplink *lp;
	int h;

	qlock(&selftab);

	/* see if the address already exists */
	h = hashipa(a);
	for(p = selftab.hash[h]; p; p = p->next)
		if(memcmp(a, p->a, IPaddrlen) == 0)
			break;

	/* allocate a local address and add to hash chain */
	if(p == nil){
		p = smalloc(sizeof(*p));
		ipmove(p->a, a);
		p->type = type;
		p->next = selftab.hash[h];
		selftab.hash[h] = p;

		/* if the null address, accept all packets */
		if(ipcmp(a, v4prefix) == 0 || ipcmp(a, IPnoaddr) == 0)
			selftab.acceptall = 1;
	}

	/* look for a link for this lifc */
	for(lp = p->link; lp; lp = lp->locallink)
		if(lp->lifc == lifc)
			break;

	/* allocate a lifc-to-local link and link to both */
	if(lp == nil){
		lp = smalloc(sizeof(*lp));
		lp->ref = 1;
		lp->lifc = lifc;
		lp->local = p;
		lp->locallink = p->link;
		p->link = lp;
		lp->lifclink = lifc->link;
		lifc->link = lp;

		/* add to routing table */
		if(isv4(a))
			v4addroute(tifc, a+IPv4off, IPallbits+IPv4off, a+IPv4off, type);
		else
			v6addroute(tifc, a, IPallbits, a, type);

		if((type & Rmulti) && ifc->m->addmulti != nil)
			(*ifc->m->addmulti)(ifc, a, lifc->local);
	} else {
		lp->ref++;
	}

	qunlock(&selftab);
}

/*
 *  These structures are unlinked from their chains while
 *  other threads may be using them.  To avoid excessive locking,
 *  just put them aside for a while before freeing them.
 *	called with &selftab locked
 */
static Iplink *freeiplink;
static Ipself *freeipself;

static void
iplinkfree(Iplink *p)
{
	Iplink **l, *np;
	ulong now = msec;

	l = &freeiplink;
	for(np = *l; np; np = *l){
		if(np->expire > now){
			*l = np->next;
			free(np);
			continue;
		}
		l = &np->next;
	}
	p->expire = now + 5000;		/* give other threads 5 secs to get out */
	p->next = nil;
	*l = p;
}
static void
ipselffree(Ipself *p)
{
	Ipself **l, *np;
	ulong now = msec;

	l = &freeipself;
	for(np = *l; np; np = *l){
		if(np->expire > now){
			*l = np->next;
			free(np);
			continue;
		}
		l = &np->next;
	}
	p->expire = now + 5000;		/* give other threads 5 secs to get out */
	p->next = nil;
	*l = p;
}

/*
 *  Decrement reference for this address on this link.
 *  Unlink from selftab if this is the last ref.
 *	called with c->car locked
 */
static void
remselfcache(Ipifc *ifc, Iplifc *lifc, uchar *a)
{
	Ipself *p, **l;
	Iplink *lp, *llp, **ill, **lll;

	qlock(&selftab);

	/* find the unique selftab entry */
	l = &selftab.hash[hashipa(a)];
	for(p = *l; p; p = *l){
		if(ipcmp(p->a, a) == 0)
			break;
		l = &p->next;
	}

	if(p == nil)
		goto out;

	/*
	 *  walk down links from an ifc looking for one
	 *  that matches the selftab entry
	 */
	ill = &lifc->link;
	for(lp = *ill; lp; lp = *ill){
		if(lp->local == p)
			break;
		ill = &lp->lifclink;
	}

	if(lp == nil)
		goto out;

	/*
	 *  walk down the links from the selftab looking for
	 *  the one we just found
	 */
	lll = &p->link;
	for(llp = *lll; llp; llp = *lll){
		if(llp == lp)
			break;
		lll = &lp->locallink;
	}

	if(llp == nil)
		panic("remselfcache");

	if(--(llp->ref) != 0)
		goto out;

	if((p->type & Rmulti) && ifc->m->remmulti != nil)
		(*ifc->m->remmulti)(ifc, a, lifc->local);

	/* ref == 0, remove from both chains and free the link */
	*ill = lp->lifclink;
	*lll = llp->locallink;
	iplinkfree(lp);

	/* remove from routing table */
	if(isv4(a))
		v4delroute(a+IPv4off, IPallbits+IPv4off);
	else
		v6delroute(a, IPallbits);
	
	if(p->link != nil)
		goto out;

	/* no more links, remove from hash and free */
	*l = p->next;
	ipselffree(p);

	/* if IPnoaddr, forget */
	if(ipcmp(a, v4prefix) == 0 || ipcmp(a, IPnoaddr) == 0)
		selftab.acceptall = 0;

out:
	qunlock(&selftab);
}

static void
dumpselftab(void)
{
	int i, count;
	Ipself *p;

	qlock(&selftab);
	for(i = 0; i < NHASH; i++){
		p = selftab.hash[i];
		if(p == nil)
			continue;
		count = 0;
		for(; p != nil && count++ < 6; p = p->next)
			print("(%i %d %lux)", p->a, p->type, p);
		print("\n");
	}
	qunlock(&selftab);
}


/*
 *  returns
 *	0		- no match
 *	Runi
 *	Rbcast
 *	Rmcast
 */
int
ipforme(uchar *addr)
{
	Ipself *p;
	int count;

	p = selftab.hash[hashipa(addr)];
	count = 0;
	for(; p; p = p->next){
		if(count++ > 1000){	/* check for loops */
			dumpselftab();
			break;
		}
		if(ipcmp(addr, p->a) == 0)
			return p->type;
	}

	/* hack to say accept anything */
	if(selftab.acceptall)
		return Runi;

	return 0;
}

/*
 *  find the ifc on same net as the remote system.  If none,
 *  return nil.
 */
Ipifc*
findipifc(uchar *remote, int type)
{
	Ipifc *ifc;
	Iplifc *lifc;
	Conv **cp;
	uchar gnet[IPaddrlen];

	for(cp = ipifc.conv; cp < &ipifc.conv[ipifc.nc]; cp++){
		if(*cp == 0)
			continue;
		ifc = (Ipifc*)(*cp)->ptcl;
		for(lifc = ifc->lifc; lifc; lifc = lifc->next){
			maskip(remote, lifc->mask, gnet);
			if(ipcmp(gnet, lifc->net) == 0){
				qunlock(&ipifc);
				return ifc;
			}
		}
	}

	/* for now for broadcast and mutlicast, just use first interface */
	if(type & (Rbcast|Rmulti)){
		for(cp = ipifc.conv; cp < &ipifc.conv[ipifc.nc]; cp++){
			if(*cp == 0)
				continue;
			ifc = (Ipifc*)(*cp)->ptcl;
			if(ifc->lifc != nil)
				return ifc;
		}
	}
		
	return nil;
}

/*
 *  find the local address 'closest' to the remote system, copy it to
 *  local and return the ifc for that address
 */
void
findlocalip(uchar *local, uchar *remote)
{
	Ipifc *ifc;
	Iplifc *lifc;
	Conv **cp;
	Route *r;
	uchar gate[IPaddrlen];
	uchar gnet[IPaddrlen];

	qlock(&ipifc);
	r = v6lookup(remote);
	
	if(r != nil){
		ifc = r->ifc;
		if(r->type & Rv4)
			v4tov6(gate, r->v4.gate);
		else
			ipmove(gate, r->v6.gate);

		if(r->type & Rifc){
			ipmove(local, gate);
			goto out;
		}

		/* find ifc address closest to the gateway to use */
		for(lifc = ifc->lifc; lifc; lifc = lifc->next){
			maskip(gate, lifc->mask, gnet);
			if(ipcmp(gnet, lifc->net) == 0){
				ipmove(local, lifc->local);
				goto out;
			}
		}
	}
		
	/* no match, choose first ifc local address */
	for(cp = ipifc.conv; cp < &ipifc.conv[ipifc.nc]; cp++){
		if(*cp == 0)
			continue;
		ifc = (Ipifc*)(*cp)->ptcl;
		for(lifc = ifc->lifc; lifc; lifc = lifc->next){
			ipmove(local, lifc->local);
			goto out;
		}
	}

out:
	qunlock(&ipifc);
}

/*
 *  return first v4 address associated with an interface
 */
int
ipv4local(Ipifc *ifc, uchar *addr)
{
	Iplifc *lifc;

	for(lifc = ifc->lifc; lifc; lifc = lifc->next){
		if(isv4(lifc->local)){
			memmove(addr, lifc->local+IPv4off, IPv4addrlen);
			return 1;
		}
	}
	return 0;
}

/*
 *  return first v6 address associated with an interface
 */
int
ipv6local(Ipifc *ifc, uchar *addr)
{
	Iplifc *lifc;

	for(lifc = ifc->lifc; lifc; lifc = lifc->next){
		if(!isv4(lifc->local)){
			ipmove(addr, lifc->local);
			return 1;
		}
	}
	return 0;
}

/*
 *  see if this address is bound to the interface
 */
Iplifc*
iplocalonifc(Ipifc *ifc, uchar *ip)
{
	Iplifc *lifc;

	for(lifc = ifc->lifc; lifc; lifc = lifc->next)
		if(ipcmp(ip, lifc->local) == 0)
			return lifc;
	return nil;
}


/*
 *  See if we're proxying for this address on this interface
 */
int
ipproxyifc(Ipifc *ifc, uchar *ip)
{
	Route *r;
	uchar net[IPaddrlen];
	Iplifc *lifc;

	/* see if this is a direct connected pt to pt address */
	r = v6lookup(ip);
	if(r == nil)
		return 0;
	if((r->type & Rifc) == 0)
		return 0;
	if((r->type & Rptpt) == 0)
		return 0;

	/* see if this is on the right interface */
	for(lifc = ifc->lifc; lifc; lifc = lifc->next){
		maskip(ip, lifc->mask, net);
		if(ipcmp(net, lifc->remote) == 0)
			return 1;
	}

	return 0;
}

/*
 *  return multicast version if any
 */
int
ipismulticast(uchar *ip)
{
	if(isv4(ip)){
		if(ip[IPv4off] >= 0xe0 && ip[IPv4off] < 0xf0)
			return V4;
	} else {
		if(ip[0] == 0xff)
			return V6;
	}
	return 0;
}

/*
 *  used to allow on the fly unbinds, return -1 if interface unusable
 */
int
ipifccheckin(Ipifc *ifc, Medium *med)
{
	int rv;

	lock(&ifc->idlock);
	if(ifc->unbinding || ifc->m != med)
		rv = -1;
	else
		rv = ++(ifc->ref);
	if(ifc->ref < 0) panic("ipifccheckin");
	unlock(&ifc->idlock);
	return rv;
}

void
ipifccheckout(Ipifc *ifc)
{
	lock(&ifc->idlock);
	if(--(ifc->ref) == 0)
	if(ifc->unbinding)
		wakeup(&ifc->wait);
	if(ifc->ref < 0) panic("ipifccheckin");
	unlock(&ifc->idlock);
}

static int
allout(void *x)
{
	Ipifc *ifc = x;

	return ifc->ref == 0;
}

int
ipifcgrab(Ipifc *ifc)
{
	lock(&ifc->idlock);
	if(ifc->unbinding){
		unlock(&ifc->idlock);
		return 0;
	}
	ifc->unbinding = 1;		/* after this ref can only go down */
	unlock(&ifc->idlock);

	sleep(&ifc->wait, allout, ifc);

	return 1;
}

/*
 *  add a multicast address to an interface, called with c->car locked
 */
void
ipifcaddmulti(Conv *c, uchar *ma, uchar *ia)
{
	Ipifc *ifc;
	Iplifc *lifc;
	Conv **p;
	Ipmulti *multi, **l;
	
	for(l = &c->multi; *l; l = &(*l)->next)
		if(ipcmp(ma, (*l)->ma) == 0)
		if(ipcmp(ia, (*l)->ia) == 0)
			return;		/* it's already there */

	multi = *l = smalloc(sizeof(*multi));
	ipmove(multi->ma, ma);
	ipmove(multi->ia, ia);
	multi->next = nil;

	for(p = ipifc.conv; *p; p++){
		if((*p)->inuse == 0)
			continue;
		ifc = (Ipifc*)(*p)->ptcl;
		if(waserror()){
			wunlock(ifc);
			nexterror();
		}
		wlock(ifc);
		for(lifc = ifc->lifc; lifc; lifc = lifc->next)
			if(ipcmp(ia, lifc->local) == 0)
				addselfcache(ifc, lifc, ma, Rmulti);
		wunlock(ifc);
		poperror();
	}
}


/*
 *  remove a multicast address from an interface, called with c->car locked
 */
void
ipifcremmulti(Conv *c, uchar *ma, uchar *ia)
{
	Ipmulti *multi, **l;
	Iplifc *lifc;
	Conv **p;
	Ipifc *ifc;
	
	for(l = &c->multi; *l; l = &(*l)->next)
		if(ipcmp(ma, (*l)->ma) == 0)
		if(ipcmp(ia, (*l)->ia) == 0)
			break;

	multi = *l;
	if(multi == nil)
		return; 	/* we don't have it open */

	*l = multi->next;

	for(p = ipifc.conv; *p; p++){
		if((*p)->inuse == 0)
			continue;

		ifc = (Ipifc*)(*p)->ptcl;
		if(waserror()){
			wunlock(ifc);
			nexterror();
		}
		wlock(ifc);
		for(lifc = ifc->lifc; lifc; lifc = lifc->next)
			if(ipcmp(ia, lifc->local) == 0)
				remselfcache(ifc, lifc, ma);
		wunlock(ifc);
		poperror();
	}

	free(multi);
}

/*
 *  make lifc's join and leave multicast groups
 */
static char*
ipifcjoinmulti(Ipifc *ifc, char **argv, int argc)
{
	USED(ifc, argv, argc);
	return nil;
}

static char*
ipifcleavemulti(Ipifc *ifc, char **argv, int argc)
{
	USED(ifc, argv, argc);
	return nil;
}


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

#include	"ip.h"

enum
{
	Lroot	= 10,
};

Route *v4root[1<<Lroot], *v6root[1<<Lroot], *queue;

static void	walkadd(Route**, Route*);
static void	addnode(Route**, Route*);
static void	calcd(Route*);

Route*	v4freelist;
Route*	v6freelist;
RWlock	routelock;

static void
freeroute(Route *r)
{
	Route **l;

	r->left = nil;
	r->right = nil;
	if(r->type & Rv4)
		l = &v4freelist;
	else
		l = &v6freelist;
	r->mid = *l;
	*l = r;
}

static Route*
allocroute(int type)
{
	Route *r;
	int n;
	Route **l;

	if(type & Rv4){
		n = sizeof(RouteTree) + sizeof(V4route);
		l = &v4freelist;
	} else {
		n = sizeof(RouteTree) + sizeof(V6route);
		l = &v6freelist;
	}

	r = *l;
	if(r != nil){
		*l = r->mid;
	} else {
		r = malloc(n);
		if(r == nil)
			panic("out of routing nodes");
	}
	memset(r, 0, n);
	r->type = type;
	r->ifc = nil;

	return r;
}

static void
addqueue(Route **q, Route *r)
{
	Route *l;

	if(r == nil)
		return;

	l = allocroute(r->type);
	l->mid = *q;
	*q = l;
	l->left = r;
}

/*
 *   compare 2 v6 addresses
 */
static int
lcmp(ulong *a, ulong *b)
{
	int i;

	for(i = 0; i < IPllen; i++){
		if(a[i] > b[i])
			return 1;
		if(a[i] < b[i])
			return -1;
	}
	return 0;
}

/*
 *  compare 2 v4 or v6 ranges
 */
enum
{
	Rpreceeds,
	Rfollows,
	Requals,
	Rcontains,
	Rcontained,
};

static int
rangecompare(Route *a, Route *b)
{
	if(a->type & Rv4){
		if(a->v4.endaddress < b->v4.address)
			return Rpreceeds;

		if(a->v4.address > b->v4.endaddress)
			return Rfollows;

		if(a->v4.address <= b->v4.address
		&& a->v4.endaddress >= b->v4.endaddress){
			if(a->v4.address == b->v4.address
			&& a->v4.endaddress == b->v4.endaddress)
				return Requals;
			return Rcontains;
		}
		return Rcontained;
	}

	if(lcmp(a->v6.endaddress, b->v6.address) < 0)
		return Rpreceeds;

	if(lcmp(a->v6.address, b->v6.endaddress) > 0)
		return Rfollows;

	if(lcmp(a->v6.address, b->v6.address) <= 0
	&& lcmp(a->v6.endaddress, b->v6.endaddress) >= 0){
		if(lcmp(a->v6.address, b->v6.address) == 0
		&& lcmp(a->v6.endaddress, b->v6.endaddress) == 0)
				return Requals;
		return Rcontains;
	}

	return Rcontained;
}

static void
copygate(Route *old, Route *new)
{
	if(new->type & Rv4)
		memmove(old->v4.gate, new->v4.gate, IPv4addrlen);
	else
		memmove(old->v6.gate, new->v6.gate, IPaddrlen);
}

/*
 *  walk down a tree adding nodes back in
 */
static void
walkadd(Route **root, Route *p)
{
	Route *l, *r;

	l = p->left;
	r = p->right;
	p->left = 0;
	p->right = 0;
	addnode(root, p);
	if(l)
		walkadd(root, l);
	if(r)
		walkadd(root, r);
}

/*
 *  calculate depth
 */
static void
calcd(Route *p)
{
	Route *q;
	int d;

	if(p) {
		d = 0;
		q = p->left;
		if(q)
			d = q->depth;
		q = p->right;
		if(q && q->depth > d)
			d = q->depth;
		q = p->mid;
		if(q && q->depth > d)
			d = q->depth;
		p->depth = d+1;
	}
}

/*
 *  balance the tree at the current node
 */
static void
balancetree(Route **cur)
{
	Route *p, *l, *r;
	int dl, dr;

	/*
	 * if left and right are
	 * too out of balance,
	 * rotate tree node
	 */
	p = *cur;
	dl = 0; if(l = p->left) dl = l->depth;
	dr = 0; if(r = p->right) dr = r->depth;

	if(dl > dr+1) {
		p->left = l->right;
		l->right = p;
		*cur = l;
		calcd(p);
		calcd(l);
	} else
	if(dr > dl+1) {
		p->right = r->left;
		r->left = p;
		*cur = r;
		calcd(p);
		calcd(r);
	} else
		calcd(p);
}

/*
 *  add a new node to the tree
 */
static void
addnode(Route **cur, Route *new)
{
	Route *p;

	p = *cur;
	if(p == 0) {
		*cur = new;
		new->depth = 1;
		return;
	}

	switch(rangecompare(new, p)){
	case Rpreceeds:
		addnode(&p->left, new);
		break;
	case Rfollows:
		addnode(&p->right, new);
		break;
	case Rcontains:
		/*
		 *  if new node is superset
		 *  of tree node,
		 *  replace tree node and
		 *  queue tree node to be
		 *  merged into root.
		 */
		*cur = new;
		new->depth = 1;
		addqueue(&queue, p);
		break;
	case Requals:
		copygate(p, new);
		freeroute(new);
		break;
	case Rcontained:
		addnode(&p->mid, new);
		break;
	}
	
	balancetree(cur);
}

#define	V4H(a)	((a&0x07ffffff)>>(32-Lroot-5))

void
v4addroute(char *tag, uchar *a, uchar *mask, uchar *gate, int type)
{
	Route *p;
	ulong sa;
	ulong m;
	ulong ea;
	int h, eh;

	m = nhgetl(mask);
	sa = nhgetl(a) & m;
	ea = sa | ~m;

	eh = V4H(ea);
	for(h=V4H(sa); h<=eh; h++) {
		p = allocroute(Rv4 | type);
		p->v4.address = sa;
		p->v4.endaddress = ea;
		memmove(p->v4.gate, gate, sizeof(p->v4.gate));
		memmove(p->tag, tag, sizeof(p->tag));

		wlock(&routelock);
		addnode(&v4root[h], p);
		while(p = queue) {
			queue = p->mid;
			walkadd(&v4root[h], p->left);
			freeroute(p);
		}
		wunlock(&routelock);
	}

	ipifcaddroute(Rv4, a, mask, gate, type);
}

#define	V6H(a)	(((a)[IPllen-1] & 0x07ffffff)>>(32-Lroot-5))

void
v6addroute(char *tag, uchar *a, uchar *mask, uchar *gate, int type)
{
	Route *p;
	ulong sa[IPllen], ea[IPllen];
	ulong x, y;
	int h, eh;

	for(h = 0; h < IPllen; h++){
		x = nhgetl(a+4*h);
		y = nhgetl(mask+4*h);
		sa[h] = x & y;
		ea[h] = x | ~y;
	}

	eh = V6H(ea);
	for(h = V6H(sa); h <= eh; h++) {
		p = allocroute(type);
		memmove(p->v6.address, sa, IPaddrlen);
		memmove(p->v6.endaddress, ea, IPaddrlen);
		memmove(p->v6.gate, gate, IPaddrlen);
		memmove(p->tag, tag, sizeof(p->tag));

		wlock(&routelock);
		addnode(&v6root[h], p);
		while(p = queue) {
			queue = p->mid;
			walkadd(&v6root[h], p->left);
			freeroute(p);
		}
		wunlock(&routelock);
	}

	ipifcaddroute(0, a, mask, gate, type);
}

Route**
looknode(Route **cur, Route *r)
{
	Route *p;

	for(;;){
		p = *cur;
		if(p == 0)
			return 0;
	
		switch(rangecompare(r, p)){
		case Rcontains:
			return 0;
		case Rpreceeds:
			cur = &p->left;
			break;
		case Rfollows:
			cur = &p->right;
			break;
		case Rcontained:
			cur = &p->mid;
			break;
		case Requals:
			return cur;
		}
	}
}

void
v4delroute(uchar *a, uchar *mask)
{
	Route **r, *p;
	Route rt;
	int h, eh;
	ulong m;

	m = nhgetl(mask);
	rt.v4.address = nhgetl(a) & m;
	rt.v4.endaddress = rt.v4.address | ~m;
	rt.type = Rv4;

	eh = V4H(rt.v4.endaddress);
	for(h=V4H(rt.v4.address); h<=eh; h++) {
		wlock(&routelock);
		r = looknode(&v4root[h], &rt);
		if(r) {
			p = *r;
			*r = 0;
			addqueue(&queue, p->left);
			addqueue(&queue, p->mid);
			addqueue(&queue, p->right);
			freeroute(p);
			while(p = queue) {
				queue = p->mid;
				walkadd(&v4root[h], p->left);
				freeroute(p);
			}
		}
		wunlock(&routelock);
	}

	ipifcremroute(Rv4, a, mask);
}

void
v6delroute(uchar *a, uchar *mask)
{
	Route **r, *p;
	Route rt;
	int h, eh;
	ulong x, y;

	for(h = 0; h < IPllen; h++){
		x = nhgetl(a+4*h);
		y = nhgetl(mask+4*h);
		rt.v6.address[h] = x & y;
		rt.v6.endaddress[h] = x | ~y;
	}
	rt.type = 0;

	eh = V6H(rt.v6.endaddress);
	for(h=V6H(rt.v6.address); h<=eh; h++) {
		wlock(&routelock);
		r = looknode(&v6root[h], &rt);
		if(r) {
			p = *r;
			*r = 0;
			addqueue(&queue, p->left);
			addqueue(&queue, p->mid);
			addqueue(&queue, p->right);
			freeroute(p);
			while(p = queue) {
				queue = p->mid;
				walkadd(&v6root[h], p->left);
				freeroute(p);
			}
		}
		wunlock(&routelock);
	}

	ipifcremroute(0, a, mask);
}

Route*
v4lookup(uchar *a)
{
	Route *p, *q;
	ulong la;
	uchar gate[IPaddrlen];

	la = nhgetl(a);
	q = nil;
	for(p=v4root[V4H(la)]; p;)
		if(la >= p->v4.address) {
			if(la <= p->v4.endaddress) {
				q = p;
				p = p->mid;
			} else
				p = p->right;
		} else
			p = p->left;

	if(q && (q->ifc == nil || q->ifcid != q->ifc->ifcid)){
		v4tov6(gate, q->v4.gate);
		q->ifc = findipifc(gate, q->type);
		if(q->ifc == nil)
			return nil;
		q->ifcid = q->ifc->ifcid;
	}

	return q;
}

Route*
v6lookup(uchar *a)
{
	Route *p, *q;
	ulong la[IPllen];
	int h;
	ulong x, y;

	if(memcmp(a, v4prefix, 12) == 0){
		q = v4lookup(a+12);
		if(q != nil)
			return q;
	}

	for(h = 0; h < IPllen; h++)
		la[h] = nhgetl(a+4*h);

	q = 0;
	for(p=v6root[V6H(la)]; p;){
		for(h = 0; h < IPllen; h++){
			x = la[h];
			y = p->v6.address[h];
			if(x == y)
				continue;
			if(x < y){
				p = p->left;
				goto next;
			}
			break;
		}
		for(h = 0; h < IPllen; h++){
			x = la[h];
			y = p->v6.endaddress[h];
			if(x == y)
				continue;
			if(x > y){
				p = p->right;
				goto next;
			}
			break;
		}
		q = p;
		p = p->mid;
next:		;
	}

	if(q && q->ifc == nil){
		q->ifc = findipifc(q->v6.gate, q->type);
		if(q->ifc == nil)
			return nil;
		if(q->ifcid != q->ifc->ifcid)
			return nil;
	}
	
	return q;
}

enum
{
	Rlinelen=	89,
};

char *rformat = "%-24.24I %-24.24M %-24.24I %4.4s %4.4s %3s\n";

void
convroute(Route *r, uchar *addr, uchar *mask, uchar *gate, char *t, int *nifc)
{
	int i;
	char *p = t;

	if(r->type & Rv4){
		memmove(addr, v4prefix, IPv4off);
		hnputl(addr+IPv4off, r->v4.address);
		memset(mask, 0xff, IPv4off);
		hnputl(mask+IPv4off, ~(r->v4.endaddress ^ r->v4.address));
		memmove(gate, v4prefix, IPv4off);
		memmove(gate+IPv4off, r->v4.gate, IPv4addrlen);
	} else {
		for(i = 0; i < IPllen; i++){
			hnputl(addr + 4*i, r->v6.address[i]);
			hnputl(mask + 4*i, ~(r->v6.endaddress[i] ^ r->v6.address[i]));
		}
		memmove(gate, r->v6.gate, IPaddrlen);
	}

	memset(t, ' ', 4);
	t[4] = 0;
	if(r->type & Rv4)
		*p++ = '4';
	else
		*p++ = '6';
	if(r->type & Rifc)
		*p++ = 'i';
	if(r->type & Runi)
		*p++ = 'u';
	else if(r->type & Rbcast)
		*p++ = 'b';
	else if(r->type & Rmulti)
		*p++ = 'm';
	if(r->type & Rptpt)
		*p = 'p';

	if(r->ifc)
		*nifc = r->ifc->conv->x;
	else
		*nifc = -1;
}

/*
 *  this code is not in rr to reduce stack size
 */
static void
sprintroute(Route *r, Routewalk *rw)
{
	int nifc;
	char t[5], *iname, ifbuf[5];
	uchar addr[IPaddrlen], mask[IPaddrlen], gate[IPaddrlen];

	if(rw->o >= 0) {
		convroute(r, addr, mask, gate, t, &nifc);
		iname = "-";
		if(nifc != -1) {
			iname = ifbuf;
			sprint(ifbuf, "%d", nifc);
		}
		sprint(rw->p, rformat, addr, mask, gate, t, r->tag, iname);
		rw->p += Rlinelen;
	}
	rw->o++;
}

/*
 *  recurse descending tree, applying the function in Routewalk
 */
static int
rr(Route *r, Routewalk *rw)
{
	int h;

	if(rw->n <= rw->o)
		return 0;
	if(r == nil)
		return 1;

	if(rr(r->left, rw) == 0)
		return 0;

	if(r->type & Rv4)
		h = V4H(r->v4.address);
	else
		h = V6H(r->v6.address);

	if(h == rw->h)
		rw->walk(r, rw);

	if(rr(r->mid, rw) == 0)
		return 0;

	return rr(r->right, rw);
}

void
ipwalkroutes(Routewalk *rw)
{
	rlock(&routelock);
	if(rw->n > rw->o) {
		for(rw->h = 0; rw->h < nelem(v4root); rw->h++)
			if(rr(v4root[rw->h], rw) == 0)
				break;
	}
	if(rw->n > rw->o) {
		for(rw->h = 0; rw->h < nelem(v6root); rw->h++)
			if(rr(v6root[rw->h], rw) == 0)
				break;
	}
	runlock(&routelock);
}

long
routeread(char *p, ulong offset, int n)
{
	Routewalk rw;

	if(offset % Rlinelen)
		return 0;

	rw.p = p;
	rw.n = n/Rlinelen;
	rw.o = -(offset/Rlinelen);
	rw.walk = sprintroute;

	ipwalkroutes(&rw);

	if(rw.o < 0)
		rw.o = 0;

	return rw.o*Rlinelen;
}

/*
 *  this code is not in routeflush to reduce stack size
 */
void
delroute(Route *r)
{
	uchar addr[IPaddrlen];
	uchar mask[IPaddrlen];
	uchar gate[IPaddrlen];
	char t[5];
	int nifc;

	convroute(r, addr, mask, gate, t, &nifc);
	if(r->type & Rv4)
		v4delroute(addr+IPv4off, mask+IPv4off);
	else
		v6delroute(addr, mask);
}

/*
 *  recurse until one route is deleted
 *    returns 0 if nothing is deleted, 1 otherwise
 */
int
routeflush(Route *r)
{
	if(r == nil)
		return 0;
	if(routeflush(r->mid))
		return 1;
	if(routeflush(r->left))
		return 1;
	if(routeflush(r->right))
		return 1;
	if((r->type & Rifc) == 0){
		delroute(r);
		return 1;
	}
	return 0;
}

long
routewrite(Chan *c, char *p, int n)
{
	int h;
	char *tag;
	Cmdbuf *cb;
	uchar addr[IPaddrlen];
	uchar mask[IPaddrlen];
	uchar gate[IPaddrlen];

	cb = parsecmd(p, n);
	if(waserror()){
		free(cb);
		nexterror();
	}

	if(strcmp(cb->f[0], "flush") == 0){
		wlock(&routelock);
		for(h = 0; h < nelem(v4root); h++)
			while(routeflush(v4root[h]) == 1)
				;
		for(h = 0; h < nelem(v6root); h++)
			while(routeflush(v6root[h]) == 1)
				;
		wunlock(&routelock);
	} else if(strcmp(cb->f[0], "remove") == 0){
		if(cb->nf < 3)
			error(Ebadarg);
		parseip(addr, cb->f[1]);
		parseipmask(mask, cb->f[2]);
		if(memcmp(addr, v4prefix, IPv4off) == 0)
			v4delroute(addr+IPv4off, mask+IPv4off);
		else
			v6delroute(addr, mask);
	} else if(strcmp(cb->f[0], "add") == 0){
		if(cb->nf < 4)
			error(Ebadarg);
		parseip(addr, cb->f[1]);
		parseipmask(mask, cb->f[2]);
		parseip(gate, cb->f[3]);
		tag = "none";
		if(c != nil)
			tag = c->tag;
		if(memcmp(addr, v4prefix, IPv4off) == 0)
			v4addroute(tag, addr+IPv4off, mask+IPv4off, gate+IPv4off, 0);
		else
			v6addroute(tag, addr, mask, gate, 0);
	} else if(strcmp(cb->f[0], "tag") == 0) {
		if(cb->nf < 2)
			error(Ebadarg);

		h = strlen(cb->f[1]);
		if(h > sizeof(c->tag))
			h = sizeof(c->tag);
		strncpy(c->tag, cb->f[1], h);
		if(h < 4)
			memset(c->tag+h, ' ', sizeof(c->tag)-h);
	}

	poperror();
	free(cb);
	return n;
}

M ip/netlog.c => ip/netlog.c +11 -7
@@ 7,7 7,8 @@
#include	"../ip/ip.h"

int logmask;				/* mask of things to debug */
Ipaddr iponly;				/* ip address to print debugging for */
uchar iponly[IPaddrlen];		/* ip address to print debugging for */
int iponlyset;

enum {
	Nlog		= 4*1024,


@@ 62,11 63,8 @@ netlogopen(void)
		nexterror();
	}
	if(alog.opens == 0){
		if(alog.buf == nil){
		if(alog.buf == nil)
			alog.buf = malloc(Nlog);
			if(alog.buf == nil)
				error(Enomem);
		}
		alog.rptr = alog.buf;
		alog.end = alog.buf + Nlog;
	}


@@ 175,7 173,11 @@ netlogctl(char* s, int len)
	else if(strcmp("clear", fields[0]) == 0)
		set = 0;
	else if(strcmp("only", fields[0]) == 0){
		iponly = parseip(addr, fields[1]);
		parseip(iponly, fields[1]);
		if(ipcmp(iponly, IPnoaddr) == 0)
			iponlyset = 0;
		else
			iponlyset = 1;
		return nil;
	} else
		return Ebadnetctl;


@@ 206,13 208,15 @@ netlog(int mask, char *fmt, ...)
	int i, n;
	va_list arg;

	if(alog.opens == 0 || !(logmask & mask))
	if(!(logmask & mask))
		return;

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

print("%s", buf);

	if(alog.opens == 0)
		return;


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

#include "ip.h"
#include "kernel.h"

static void
nullbind(Ipifc*, int, char**)
{
	error("can't bind null device");
}

static void
nullunbind(Ipifc*)
{
}

static void
nullbwrite(Ipifc*, Block*, int, uchar*)
{
	error("nullbwrite");
}

Medium nullmedium =
{
	"null",
	0,		/* medium header size */
	0,		/* default min mtu */
	0,		/* default max mtu */
	0,		/* mac address length  */
	nullbind,
	nullunbind,
	nullbwrite,
	nil,		/* addmulti */
	nil,		/* remmulti */
	nil,		/* pktin */
	nil,		/* addroute */
	nil,		/* remroute */
	nil,		/* flushroute */
	nil,		/* joinmulti */
	nil,		/* leave multi */
};

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

#include "ip.h"
#include "kernel.h"


static void	pktbind(Ipifc *ifc, int argc, char **argv);
static void	pktunbind(Ipifc *ifc);
static void	pktbwrite(Ipifc *ifc, Block *bp, int version, uchar *ip);
static void	pktin(Ipifc *ifc, Block *bp);

Medium pktmedium =
{
	"pkt",
	14,
	60,
	1514,
	6,
	pktbind,
	pktunbind,
	pktbwrite,
	nil,		/* addmulti */
	nil,		/* remmulti */
	pktin,
	nil,		/* addroute */
	nil,		/* remroute */
	nil,		/* flushroute */
	nil,		/* joinmulti */
	nil,		/* leave multi */
};

/*
 *  called to bind an IP ifc to an ethernet device
 *  called with ifc wlock'd
 */
static void
pktbind(Ipifc*, int, char**)
{
}

/*
 *  called with ifc wlock'd
 */
static void
pktunbind(Ipifc*)
{
}

/*
 *  called by ipoput with a single packet to write
 */
static void
pktbwrite(Ipifc *ifc, Block *bp, int, uchar*)
{
	/* enqueue onto the conversation's rq */
	bp = concatblock(bp);
	qpass(ifc->conv->rq, bp);
}

/*
 *  called when someone write's to 'data' with ifc rlocked
 */
static void
pktin(Ipifc *ifc, Block *bp)
{
	if(ifc->lifc == nil)
		freeb(bp);
	else
		ipiput(ifc->lifc->local, bp);
}

M ip/ptclbsum.c => ip/ptclbsum.c +2 -2
@@ 8,11 8,11 @@
#include	"ip.h"

static	short	endian	= 1;
static	byte*	aendian	= (byte*)&endian;
static	uchar*	aendian	= (uchar*)&endian;
#define	LITTLE	*aendian

ushort
ptclbsum(byte *addr, int len)
ptclbsum(uchar *addr, int len)
{
	ulong losum, hisum, mdsum, x;
	ulong t1, t2;

M ip/tcp.c => ip/tcp.c +124 -128
@@ 14,6 14,7 @@ enum
	TCP_IPLEN	= 8,
	TCP_PHDRSIZE	= 12,
	TCP_HDRSIZE	= 20,
	TCP_TCBPHDRSZ	= 40,
	TCP_PKT		= TCP_IPLEN+TCP_PHDRSIZE,
	TimerOFF	= 0,
	TimerON		= 1,


@@ 85,27 86,27 @@ struct Timer
typedef struct Tcphdr Tcphdr;
struct Tcphdr
{
	byte	vihl;		/* Version and header length */
	byte	tos;		/* Type of service */
	byte	length[2];	/* packet length */
	byte	id[2];		/* Identification */
	byte	frag[2];	/* Fragment information */
	byte	Unused;
	byte	proto;
	byte	tcplen[2];
	byte	tcpsrc[4];
	byte	tcpdst[4];
	byte	tcpsport[2];
	byte	tcpdport[2];
	byte	tcpseq[4];
	byte	tcpack[4];
	byte	tcpflag[2];
	byte	tcpwin[2];
	byte	tcpcksum[2];
	byte	tcpurg[2];
	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	proto;
	uchar	tcplen[2];
	uchar	tcpsrc[4];
	uchar	tcpdst[4];
	uchar	tcpsport[2];
	uchar	tcpdport[2];
	uchar	tcpseq[4];
	uchar	tcpack[4];
	uchar	tcpflag[2];
	uchar	tcpwin[2];
	uchar	tcpcksum[2];
	uchar	tcpurg[2];
	/* Options segment */
	byte	tcpopt[2];
	byte	tcpmss[2];
	uchar	tcpopt[2];
	uchar	tcpmss[2];
};

typedef struct Tcp Tcp;


@@ 115,7 116,7 @@ struct	Tcp
	ushort	dest;
	ulong	seq;
	ulong	ack;
	byte	flags;
	uchar	flags;
	ushort	wnd;
	ushort	urg;
	ushort	mss;


@@ 134,9 135,9 @@ typedef struct Tcpctl Tcpctl;
struct Tcpctl
{
	QLock;
	byte	state;			/* Connection state */
	byte	type;			/* Listening or active connection */
	byte	code;			/* Icmp code */
	uchar	state;			/* Connection state */
	uchar	type;			/* Listening or active connection */
	uchar	code;			/* Icmp code */
	struct {
		ulong	una;		/* Unacked data pointer */
		ulong	nxt;		/* Next sequence expected */


@@ 147,7 148,7 @@ struct Tcpctl
		ulong	wl2;
	} snd;
	struct {
		ulong	nxt;		/* Receive pointer to next byte slot */
		ulong	nxt;		/* Receive pointer to next uchar slot */
		ushort	wnd;		/* Receive window incoming */
		ulong	urg;		/* Urgent pointer */
		int	blocked;


@@ 162,8 163,8 @@ struct Tcpctl
	ushort	window;			/* Recevive window */
	int	max_snd;		/* Max send */
	ulong	last_ack;		/* Last acknowledege received */
	byte	backoff;		/* Exponential backoff counter */
	byte	flags;			/* State flags */
	uchar	backoff;		/* Exponential backoff counter */
	uchar	flags;			/* State flags */
	ulong	sndcnt;			/* Amount of data in send queue */
	Reseq	*reseq;			/* Resequencing queue */
	Timer	timer;			/* Activity timer */


@@ 175,10 176,11 @@ struct Tcpctl
	int	kacounter;		/* count down for keep alive */
	int	f2counter;		/* count down for finwait2 state */
	uint	sndsyntime;		/* time syn sent */
	char	ascstate[128];

	Tcphdr	protohdr;		/* prototype header */
};

#define DBG(x)	if((logmask & Logtcpmsg) && (iponly == 0 || x == iponly))netlog
#define DBG(x)	if((logmask & Logtcpmsg) && (iponlyset == 0 || memcmp(x, iponly+12, 4) == 0))netlog

Proto	tcp;
int	tcp_irtt = DEF_RTT;	/* Initial guess at round trip time */


@@ 189,14 191,14 @@ QLock 	tl;			/* Protect timer list */
static struct Tcpstats
{
	ulong	dup;		/* (partially) duplicated packets */
	ulong	dupb;		/* duplicated bytes */
	ulong	dupb;		/* duplicated uchars */
} tstats;

void	addreseq(Tcpctl*, Tcp*, Block*, ushort);
void	getreseq(Tcpctl*, Tcp*, Block**, ushort*);
void	localclose(Conv*, char*);
void	procsyn(Conv*, Tcp*);
void	tcpiput(Media*, Block*);
void	tcpiput(uchar*, Block*);
void	tcpoutput(Conv*);
int	tcptrim(Tcpctl*, Tcp*, Block**, ushort*);
void	tcpstart(Conv*, int, ushort);


@@ 206,10 208,10 @@ void	tcprcvwin(Conv*);
void	tcpacktimer(Conv*);

void
tcpsetstate(Conv *s, byte newstate)
tcpsetstate(Conv *s, uchar newstate)
{
	Tcpctl *tcb;
	byte oldstate;
	uchar oldstate;

	tcb = (Tcpctl*)s->ptcl;



@@ 231,7 233,7 @@ tcpsetstate(Conv *s, byte newstate)
		qclose(s->eq);
		s->lport = 0;		/* This connection is toast */
		s->rport = 0;
		s->raddr = 0;
		ipmove(s->raddr, IPnoaddr);

	case Close_wait:		/* Remote closes */
		qhangup(s->rq, nil);


@@ 255,29 257,26 @@ tcpconnect(Conv *c, char **argv, int argc)
	return nil;
}

int
tcpstate(char **msg, Conv *c)
static int
tcpstate(Conv *c, char *state, int n)
{
	Tcpctl *s;
	int isclose;

	s = (Tcpctl*)(c->ptcl);

	isclose = 0;
	if(s->state == Closed)
		isclose = 1;

/*	snprint(s->ascstate, sizeof(s->ascstate),
		"tcnt %d tnxt %lux tuna %lux twnd %d rack %lux rnxt %lux rwnd %d",
		s->sndcnt, s->snd.nxt, s->snd.una, s->snd.wnd,
		s->last_ack, s->rcv.nxt, s->rcv.wnd);*/
	snprint(s->ascstate, sizeof(s->ascstate),
	return snprint(state, n,
		"%s srtt %d mdev %d timer.start %d timer.count %d\n",
		tcpstates[s->state], s->srtt, s->mdev,
		s->timer.start, s->timer.count);
}

	*msg = s->ascstate;
	return isclose;
static int
tcpinuse(Conv *c)
{
	Tcpctl *s;

	s = (Tcpctl*)(c->ptcl);
	return s->state != Closed;
}

static char*


@@ 533,6 532,7 @@ void
inittcpctl(Conv *s)
{
	Tcpctl *tcb;
	Tcphdr *h;

	tcb = (Tcpctl*)s->ptcl;



@@ 550,21 550,30 @@ inittcpctl(Conv *s)
	tcb->acktimer.start = TCP_ACK / MSPTICK;
	tcb->acktimer.func = tcpacktimer;
	tcb->acktimer.arg = s;

	/* create a prototype(pseudo) header */
	if(ipcmp(s->laddr, IPnoaddr) == 0)
		findlocalip(s->laddr, s->raddr);
	h = &tcb->protohdr;
	memset(h, 0, sizeof(*h));
	h->proto = IP_TCPPROTO;
	hnputs(h->tcpsport, s->lport);
	hnputs(h->tcpdport, s->rport);
	v6tov4(h->tcpsrc, s->laddr);
	v6tov4(h->tcpdst, s->raddr);
}

/* mtu (- TCP + IP hdr len) of 1st hop */
int
tcpmtu(Conv *s)
{
	Media *m;
	byte dst[4], dummy[4];
	Ipifc *ifc;
	int mtu;

	mtu = 0;
	hnputl(dst, s->raddr);
	m = Mediaroute(dst, dummy);
	if(m != nil)
		mtu = m->maxmtu - m->hsize - (TCP_PKT + TCP_HDRSIZE);
	ifc = findipifc(s->raddr, 0);
	if(ifc != nil)
		mtu = ifc->maxmtu - ifc->m->hsize - (TCP_PKT + TCP_HDRSIZE);
	if(mtu < 4)
		mtu = DEF_MSS;
	return mtu;


@@ 647,23 656,16 @@ htontcp(Tcp *tcph, Block *data, Tcphdr *ph)
		data->wp += hdrlen + TCP_PKT;
	}


	memmove(data->rp, ph, TCP_PKT);

	/* copy in pseudo ip header plus port numbers */
	h = (Tcphdr *)(data->rp);
	h->proto = IP_TCPPROTO;
	h->frag[0] = 0;
	h->frag[1] = 0;
	memmove(h, ph, TCP_TCBPHDRSZ);

	/* copy in variable bits */
	hnputs(h->tcplen, hdrlen + dlen);
	hnputs(h->tcpsport, tcph->source);
	hnputs(h->tcpdport, tcph->dest);
	hnputl(h->tcpseq, tcph->seq);
	hnputl(h->tcpack, tcph->ack);
	hnputs(h->tcpflag, (hdrlen<<10) | tcph->flags);
	hnputs(h->tcpwin, tcph->wnd);
	h->tcpcksum[0] = 0;
	h->tcpcksum[1] = 0;
	h->Unused = 0;
	hnputs(h->tcpurg, tcph->urg);

	if(tcph->mss != 0){


@@ 674,7 676,7 @@ htontcp(Tcp *tcph, Block *data, Tcphdr *ph)
	csum = ptclcsum(data, TCP_IPLEN, hdrlen+dlen+TCP_PHDRSIZE);
	hnputs(h->tcpcksum, csum);

	DBG(nhgetl(h->tcpdst))(Logtcpmsg, "%d > %d s %l8.8ux a %8.8lux %s w %.4ux l %d\n",
	DBG(h->tcpdst)(Logtcpmsg, "%d > %d s %l8.8ux a %8.8lux %s w %.4ux l %d\n",
		tcph->source, tcph->dest,
		tcph->seq, tcph->ack, tcpflag((hdrlen<<10)|tcph->flags),
		tcph->wnd, dlen);


@@ 686,7 688,7 @@ int
ntohtcp(Tcp *tcph, Block **bpp)
{
	Tcphdr *h;
	byte *optr;
	uchar *optr;
	ushort hdrlen;
	ushort i, optlen;



@@ 715,7 717,7 @@ ntohtcp(Tcp *tcph, Block **bpp)
	if(*bpp == nil)
		return -1;

	DBG(nhgetl(h->tcpsrc))(Logtcpmsg, "%d > %d s %l8.8ux a %8.8lux %s w %.4ux l %d\n",
	DBG(h->tcpsrc)(Logtcpmsg, "%d > %d s %l8.8ux a %8.8lux %s w %.4ux l %d\n",
		tcph->source, tcph->dest,
		tcph->seq, tcph->ack, tcpflag((hdrlen<<10)|tcph->flags),
		tcph->wnd, nhgets(h->length)-hdrlen-TCP_PKT);


@@ 754,27 756,28 @@ tcpsndsyn(Tcpctl *tcb)
	tcb->sndsyntime = msec;
}


/*
 *  called with v4 (4 byte) addresses
 */
void
sndrst(Ipaddr source, Ipaddr dest, ushort length, Tcp *seg)
sndrst(uchar *source, uchar *dest, ushort length, Tcp *seg)
{
	ushort tmp;
	Tcphdr ph;
	Block *hbp;
	byte rflags;
	uchar rflags;

	if(seg->flags & RST)
		return;


	hnputl(ph.tcpsrc, dest);
	hnputl(ph.tcpdst, source);
	/* make pseudo header */
	memset(&ph, 0, sizeof(ph));
	v6tov4(ph.tcpsrc, dest);
	v6tov4(ph.tcpdst, source);
	ph.proto = IP_TCPPROTO;
	hnputs(ph.tcplen, TCP_HDRSIZE);

	/* Swap port numbers */
	tmp = seg->dest;
	seg->dest = seg->source;
	seg->source = tmp;
	hnputs(ph.tcpsport, seg->dest);
	hnputs(ph.tcpdport, seg->source);

	rflags = RST;



@@ 811,7 814,6 @@ char*
tcphangup(Conv *s)
{
	Tcp seg;
	byte dst[4];
	Tcpctl *tcb;
	Tcphdr ph;
	Block *hbp;


@@ 823,8 825,6 @@ tcphangup(Conv *s)
	}
	qlock(tcb);
	if(s->raddr != 0) {
		seg.source = s->lport;
		seg.dest = s->rport;
		seg.flags = RST | ACK;
		seg.ack = tcb->rcv.nxt;
		seg.seq = tcb->snd.ptr;


@@ 832,15 832,8 @@ tcphangup(Conv *s)
		seg.urg = 0;
		seg.mss = 0;
		tcb->last_ack = tcb->rcv.nxt;
		if(s->laddr == 0) {
			hnputl(dst, s->raddr);
			s->laddr = Mediagetsrc(dst);
		}
		hnputl(ph.tcpsrc, s->laddr);
		hnputl(ph.tcpdst, s->raddr);
		ph.proto = IP_TCPPROTO;
		hnputs(ph.tcplen, TCP_HDRSIZE);
		hbp = htontcp(&seg, nil, &ph);
		hbp = htontcp(&seg, nil, &tcb->protohdr);
		ipoput(hbp, 0, s->ttl);
	}
	localclose(s, nil);


@@ 850,10 843,11 @@ tcphangup(Conv *s)
}

Conv*
tcpincoming(Conv *s, Tcp *segp, Ipaddr src, Ipaddr dst)
tcpincoming(Conv *s, Tcp *segp, uchar *src, uchar *dst)
{
	Conv *new;
	Tcpctl *tcb;
	Tcphdr *h;

	new = Fsnewcall(&fs, s, src, segp->source, dst, segp->dest);
	if(new == nil)


@@ 867,6 861,14 @@ tcpincoming(Conv *s, Tcp *segp, Ipaddr src, Ipaddr dst)
	tcb->acktimer.arg = new;
	tcb->acktimer.state = TimerOFF;

	h = &tcb->protohdr;
	memset(h, 0, sizeof(*h));
	h->proto = IP_TCPPROTO;
	hnputs(h->tcpsport, new->lport);
	hnputs(h->tcpdport, new->rport);
	v6tov4(h->tcpsrc, dst);
	v6tov4(h->tcpdst, src);

	return new;
}



@@ 1030,21 1032,20 @@ update(Conv *s, Tcp *seg)
}

void
tcpiput(Media *m, Block *bp)
tcpiput(uchar*, Block *bp)
{
	Tcp seg;
	Tcphdr *h;
	int hdrlen;
	Tcpctl *tcb;
	ushort length;
	Ipaddr source, dest;
	uchar source[IPaddrlen], dest[IPaddrlen];
	Conv *spec, *gen, *s, **p;

	USED(m);
	h = (Tcphdr*)(bp->rp);

	dest = nhgetl(h->tcpdst);
	source = nhgetl(h->tcpsrc);
	v4tov6(dest, h->tcpdst);
	v4tov6(source, h->tcpsrc);
	length = nhgets(h->length);

	h->Unused = 0;


@@ 1076,8 1077,9 @@ tcpiput(Media *m, Block *bp)
	/* Look for a connection. failing that look for a listener. */
	for(p = tcp.conv; *p; p++) {
		s = *p;
		if(s->rport == seg.source &&
		   s->lport == seg.dest && s->raddr == source)
		if(s->rport == seg.source)
		if(s->lport == seg.dest)
		if(ipcmp(s->raddr, source) == 0)
			break;
	}
	s = *p;


@@ 1116,7 1118,7 @@ tcpiput(Media *m, Block *bp)
				continue;
			if(tcb->state != Listen)
				continue;
			if(s->rport == 0 && s->raddr == 0) {
			if(s->rport == 0 && ipcmp(s->raddr, IPnoaddr) == 0) {
				if(s->lport == seg.dest){
					spec = s;
					break;


@@ 1414,7 1416,6 @@ tcpoutput(Conv *s)
	int x;
	Tcp seg;
	int msgs;
	Tcphdr ph;
	Tcpctl *tcb;
	Block *hbp, *bp;
	int sndcnt, n, first;


@@ 1547,17 1548,8 @@ tcpoutput(Conv *s)
		if(seq_gt(tcb->snd.ptr,tcb->snd.nxt))
			tcb->snd.nxt = tcb->snd.ptr;

		/* Fill in fields of pseudo IP header */
		hnputl(ph.tcpdst, s->raddr);
		if(s->laddr == 0)
			s->laddr = Mediagetsrc(ph.tcpdst);

		hnputl(ph.tcpsrc, s->laddr);
		hnputs(ph.tcpsport, s->lport);
		hnputs(ph.tcpdport, s->rport);

		/* Build header, link data and compute cksum */
		hbp = htontcp(&seg, bp, &ph);
		hbp = htontcp(&seg, bp, &tcb->protohdr);
		if(hbp == nil) {
			freeblist(bp);
			return;


@@ 1588,13 1580,12 @@ tcpoutput(Conv *s)
}

/*
 *  the BSD convention (hack?) for keep alives.  resend last byte acked.
 *  the BSD convention (hack?) for keep alives.  resend last uchar acked.
 */
void
tcpkeepalive(Conv *s)
{
	Tcp seg;
	Tcphdr ph;
	Tcpctl *tcb;
	Block *hbp,*dbp;



@@ 1618,16 1609,8 @@ tcpkeepalive(Conv *s)
		dbp->wp++;
	}

	/* Fill in fields of pseudo IP header */
	hnputl(ph.tcpdst, s->raddr);
	if(s->laddr == 0)
		s->laddr = Mediagetsrc(ph.tcpdst);
	hnputl(ph.tcpsrc, s->laddr);
	hnputs(ph.tcpsport, s->lport);
	hnputs(ph.tcpdport, s->rport);

	/* Build header, link data and compute cksum */
	hbp = htontcp(&seg, dbp, &ph);
	hbp = htontcp(&seg, dbp, &tcb->protohdr);
	if(hbp == nil) {
		freeblist(dbp);
		return;


@@ 1792,7 1775,7 @@ tcptrim(Tcpctl *tcb, Tcp *seg, Block **bp, ushort *length)
{
	ushort len;
	Block *nbp;
	byte accept;
	uchar accept;
	int dupcnt, excess;

	accept = 0;


@@ 1866,22 1849,25 @@ tcpadvise(Block *bp, char *msg)
{
	Tcphdr *h;
	Tcpctl *tcb;
	Ipaddr source, dest;
	uchar source[IPaddrlen];
	uchar dest[IPaddrlen];
	ushort psource, pdest;
	Conv *s, **p;

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

	dest = nhgetl(h->tcpdst);
	source = nhgetl(h->tcpsrc);
	v4tov6(dest, h->tcpdst);
	v4tov6(source, h->tcpsrc);
	psource = nhgets(h->tcpsport);
	pdest = nhgets(h->tcpdport);

	/* Look for a connection */
	for(p = tcp.conv; *p; p++) {
		s = *p;
		if(s->rport == pdest && s->lport == psource)
		if(s->raddr == dest && s->laddr == source){
		if(s->rport == pdest)
		if(s->lport == psource)
		if(ipcmp(s->raddr, dest) == 0)
		if(ipcmp(s->laddr, source) == 0){
			tcb = (Tcpctl*)s->ptcl;
			qlock(tcb);
			switch(tcb->state){


@@ 1896,6 1882,7 @@ tcpadvise(Block *bp, char *msg)
	freeblist(bp);
}

/* called with c->car qlocked */
char*
tcpctl(Conv* c, char** f, int n)
{


@@ 1907,7 1894,14 @@ tcpctl(Conv* c, char** f, int n)
int
tcpstats(char *buf, int len)
{
	return snprint(buf, len, "\tdupp %d dupb %d\n", tstats.dup, tstats.dupb);
	int n;

	n = snprint(buf, len,
		"tcp: csum %d hlen %d len %d order %d rexmit %d",
		tcp.csumerr, tcp.hlenerr, tcp.lenerr, tcp.order, tcp.rexmit);
	n += snprint(buf+n, len-n, " dupp %d dupb %d\n",
		tstats.dup, tstats.dupb);
	return n;
}

void


@@ 1924,6 1918,7 @@ tcpinit(Fs *fs)
	tcp.rcv = tcpiput;
	tcp.advise = tcpadvise;
	tcp.stats = tcpstats;
	tcp.inuse = tcpinuse;
	tcp.ipproto = IP_TCPPROTO;
	tcp.nc = Nchans;
	tcp.ptclsize = sizeof(Tcpctl);


@@ 1932,3 1927,4 @@ tcpinit(Fs *fs)

	Fsproto(fs, &tcp);
}


M ip/udp.c => ip/udp.c +77 -82
@@ 15,7 15,7 @@ enum
	UDP_HDRSIZE	= 20,
	UDP_IPHDR	= 8,
	IP_UDPPROTO	= 17,
	UDP_USEAD	= 12,
	UDP_USEAD	= 36,

	Udprxms		= 200,
	Udptickms	= 100,


@@ 26,22 26,22 @@ typedef struct Udphdr Udphdr;
struct Udphdr
{
	/* ip header */
	byte	vihl;		/* Version and header length */
	byte	tos;		/* Type of service */
	byte	length[2];	/* packet length */
	byte	id[2];		/* Identification */
	byte	frag[2];	/* Fragment information */
	byte	Unused;	
	byte	udpproto;	/* Protocol */
	byte	udpplen[2];	/* Header plus data length */
	byte	udpsrc[4];	/* Ip source */
	byte	udpdst[4];	/* Ip destination */
	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	udpproto;	/* Protocol */
	uchar	udpplen[2];	/* Header plus data length */
	uchar	udpsrc[4];	/* Ip source */
	uchar	udpdst[4];	/* Ip destination */

	/* udp header */
	byte	udpsport[2];	/* Source port */
	byte	udpdport[2];	/* Destination port */
	byte	udplen[2];	/* data length */
	byte	udpcksum[2];	/* Checksum */
	uchar	udpsport[2];	/* Source port */
	uchar	udpdport[2];	/* Destination port */
	uchar	udplen[2];	/* data length */
	uchar	udpcksum[2];	/* Checksum */
};

/*


@@ 51,7 51,7 @@ typedef struct Udpcb Udpcb;
struct Udpcb
{
	QLock;
	byte	headers;
	uchar	headers;
};

	Proto	udp;


@@ 71,12 71,11 @@ udpconnect(Conv *c, char **argv, int argc)
	return e;
}

int
udpstate(char **s, Conv *c)
static int
udpstate(Conv *c, char *state, int n)
{
	USED(c);
	*s = "Datagram";
	return 1;
	return snprint(state, n, "%s", "Datagram");
}

static char*


@@ 107,8 106,8 @@ udpclose(Conv *c)
	qclose(c->rq);
	qclose(c->wq);
	qclose(c->eq);
	c->laddr = 0;
	c->raddr = 0;
	ipmove(c->laddr, IPnoaddr);
	ipmove(c->raddr, IPnoaddr);
	c->lport = 0;
	c->rport = 0;



@@ 119,17 118,16 @@ udpclose(Conv *c)
}

void
udpkick(Conv *c, int l)
udpkick(Conv *c, int)
{
	Udphdr *uh;
	ushort rport;
	Ipaddr laddr, raddr;
	uchar laddr[IPaddrlen], raddr[IPaddrlen];
	Block *bp;
	Udpcb *ucb;
	int dlen, ptcllen;

	USED(l);

	netlog(Logudp, "udp: kick\n");
	bp = qget(c->wq);
	if(bp == nil)
		return;


@@ 138,24 136,20 @@ udpkick(Conv *c, int l)
	if(ucb->headers) {
		/* get user specified addresses */
		bp = pullupblock(bp, UDP_USEAD);
		if(bp == nil) {
			freeblist(bp);
		if(bp == nil)
			return;
		}
		raddr = nhgetl(bp->rp);
		bp->rp += 4;
		laddr = nhgetl(bp->rp);
		if(laddr != 0 && Mediaforme(bp->rp) <= 0)
			laddr = 0;
		bp->rp += 4;
		ipmove(raddr, bp->rp);
		bp->rp += IPaddrlen;
		ipmove(laddr, bp->rp);
		bp->rp += IPaddrlen;
		if(ipforme(laddr) != Runi)
			findlocalip(laddr, raddr);	/* pick interface closest to dest */
		rport = nhgets(bp->rp);
		bp->rp += 2;
		/* ignore local port number */
		bp->rp += 2;
	} else {
		raddr = 0;
		rport = 0;
		laddr = 0;
	}

	dlen = blocklen(bp);


@@ 174,16 168,15 @@ udpkick(Conv *c, int l)
	uh->frag[1] = 0;
	hnputs(uh->udpplen, ptcllen);
	if(ucb->headers) {
		hnputl(uh->udpdst, raddr);
		v6tov4(uh->udpdst, raddr);
		hnputs(uh->udpdport, rport);
		if(laddr)
			hnputl(uh->udpsrc, laddr);
		else
			hnputl(uh->udpsrc, Mediagetsrc(uh->udpdst));
		v6tov4(uh->udpsrc, laddr);
	} else {
		hnputl(uh->udpdst, c->raddr);
		v6tov4(uh->udpdst, c->raddr);
		hnputs(uh->udpdport, c->rport);
		hnputl(uh->udpsrc, c->laddr);
		if(ipcmp(c->laddr, IPnoaddr) == 0)
			findlocalip(c->laddr, c->raddr);	/* pick interface closest to dest */
		v6tov4(uh->udpsrc, c->laddr);
	}
	hnputs(uh->udpsport, c->lport);
	hnputs(uh->udplen, ptcllen);


@@ 196,18 189,15 @@ udpkick(Conv *c, int l)
}

void
udpiput(Media *m, Block *bp)
udpiput(uchar *ia, Block *bp)
{
	int len, olen, ottl;
	Udphdr *uh;
	Conv *c, **p;
	Udpcb *ucb;
	Ipaddr raddr, laddr;
	uchar raddr[IPaddrlen], laddr[IPaddrlen];
	ushort rport, lport;
	uchar dst[IPaddrlen];
	uchar src[IPaddrlen];

	USED(m);
	uh = (Udphdr*)(bp->rp);

	/* Put back pseudo header for checksum (remember old values for icmpnoconv()) */


@@ 217,17 207,15 @@ udpiput(Media *m, Block *bp)
	olen = nhgets(uh->udpplen);
	hnputs(uh->udpplen, len);

	raddr = nhgetl(uh->udpsrc);
	laddr = nhgetl(uh->udpdst);
	v4tov6(raddr, uh->udpsrc);
	v4tov6(laddr, uh->udpdst);
	lport = nhgets(uh->udpdport);
	rport = nhgets(uh->udpsport);
	memmove(src, uh->udpsrc, IPaddrlen);
	memmove(dst, uh->udpdst, IPaddrlen);

	if(udpsum && nhgets(uh->udpcksum)) {
		if(ptclcsum(bp, UDP_IPHDR, len+UDP_PHDRSIZE)) {
			udp.csumerr++;
			netlog(Logudp, "udp: checksum error %I\n", src);
			netlog(Logudp, "udp: checksum error %I\n", raddr);
			freeblist(bp);
			return;
		}


@@ 244,10 232,10 @@ udpiput(Media *m, Block *bp)
	}

	if(*p == nil) {
		netlog(Logudp, "udp: no conv %I.%d -> %I.%d\n", src, rport,
			dst, lport);
		netlog(Logudp, "udp: no conv %I.%d -> %I.%d\n", raddr, rport,
			laddr, lport);
		/* don't complain about broadcasts... */
		if(Mediaforme(dst) > 0){
		if(ipforme(raddr) == 0){
			uh->Unused = ottl;
			hnputs(uh->udpplen, olen);
			icmpnoconv(bp);


@@ 262,51 250,47 @@ udpiput(Media *m, Block *bp)
	len -= (UDP_HDRSIZE-UDP_PHDRSIZE);
	bp = trimblock(bp, UDP_IPHDR+UDP_HDRSIZE, len);
	if(bp == nil){
		netlog(Logudp, "udp: len err %I.%d -> %I.%d\n", src, rport,
			dst, lport);
		netlog(Logudp, "udp: len err %I.%d -> %I.%d\n", raddr, rport,
			laddr, lport);
		udp.lenerr++;
		return;
	}

	netlog(Logudpmsg, "udp: %I.%d -> %I.%d l %d\n", src, rport,
		dst, lport, len);
	netlog(Logudpmsg, "udp: %I.%d -> %I.%d l %d\n", raddr, rport,
		laddr, lport, len);

	ucb = (Udpcb*)c->ptcl;

	if(ucb->headers) {
		/* pass the src address */
		bp = padblock(bp, UDP_USEAD);
		hnputl(bp->rp, raddr);
		if(Mediaforme(dst) > 0)
			hnputl(bp->rp+4, laddr);
		else if(m != 0)
			hnputl(bp->rp+4, m->myip[0]);
		else if(media != 0)
			hnputl(bp->rp+4, media->myip[0]);
		ipmove(bp->rp, raddr);
		if(ipforme(laddr) == Runi)
			ipmove(bp->rp+IPaddrlen, laddr);
		else
			hnputl(bp->rp+4, laddr);
		hnputs(bp->rp+8, rport);
		hnputs(bp->rp+10, lport);
			ipmove(bp->rp+IPaddrlen, ia);
		hnputs(bp->rp+2*IPaddrlen, rport);
		hnputs(bp->rp+2*IPaddrlen+2, lport);
	} else {
		/* connection oriented udp */
		if(c->raddr == 0){
			/* save the src address in the conversation */
		 	c->raddr = raddr;
		 	ipmove(c->raddr, raddr);
			c->rport = rport;

			/* reply with the same ip address (if not broadcast) */
			if(Mediaforme(dst) > 0)
				c->laddr = laddr;
			if(ipforme(laddr) == Runi)
				ipmove(c->laddr, laddr);
			else
				c->laddr = m->myip[0];
				ipmove(c->laddr, ia);
		}
	}
	if(bp->next)
		bp = concatblock(bp);

	if(qfull(c->rq)){
		netlog(Logudp, "udp: qfull %I.%d -> %I.%d\n", src, rport,
			dst, lport);
		netlog(Logudp, "udp: qfull %I.%d -> %I.%d\n", raddr, rport,
			laddr, lport);
		freeblist(bp);
	}else
		qpass(c->rq, bp);


@@ 333,22 317,24 @@ void
udpadvise(Block *bp, char *msg)
{
	Udphdr *h;
	Ipaddr source, dest;
	uchar source[IPaddrlen], dest[IPaddrlen];
	ushort psource, pdest;
	Conv *s, **p;

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

	dest = nhgetl(h->udpdst);
	source = nhgetl(h->udpsrc);
	v4tov6(dest, h->udpdst);
	v4tov6(source, h->udpsrc);
	psource = nhgets(h->udpsport);
	pdest = nhgets(h->udpdport);

	/* Look for a connection */
	for(p = udp.conv; *p; p++) {
		s = *p;
		if(s->rport == pdest && s->lport == psource)
		if(s->raddr == dest && s->laddr == source){
		if(s->rport == pdest)
		if(s->lport == psource)
		if(ipcmp(s->raddr, dest) == 0)
		if(ipcmp(s->laddr, source) == 0){
			qhangup(s->rq, msg);
			qhangup(s->wq, msg);
			break;


@@ 357,6 343,14 @@ udpadvise(Block *bp, char *msg)
	freeblist(bp);
}

int
udpstats(char *buf, int len)
{
	return snprint(buf, len,
		"udp: csum %d hlen %d len %d order %d rexmit %d\n",
		udp.csumerr, udp.hlenerr, udp.lenerr, udp.order, udp.rexmit);
}

void
udpinit(Fs *fs)
{


@@ 370,6 364,7 @@ udpinit(Fs *fs)
	udp.close = udpclose;
	udp.rcv = udpiput;
	udp.advise = udpadvise;
	udp.stats = udpstats;
	udp.ipproto = IP_UDPPROTO;
	udp.nc = 16;
	udp.ptclsize = sizeof(Udpcb);

M port/portdat.h => port/portdat.h +1 -0
@@ 158,6 158,7 @@ struct Chan
		Qid	pgrpid;		/* for #p/notepg */
		Mnt*	mntptr;		/* for devmnt */
		ulong	mid;		/* for ns in devproc */
		char	tag[4];		/* for iproute */
	};
	Chan*	mchan;			/* channel to mounted server */
	Qid	mqid;			/* qid of root of mount point */