~kris/9p

9hist

b044a1ea8abe3fbfca8c413c7326d79f9890a8f7 — David du Colombier 24 years ago fe99aed
Plan 9 from Bell Labs 2002-05-07
M ip/arp.c => ip/arp.c +377 -71
@@ 6,6 6,7 @@
#include "../port/error.h"

#include "ip.h"
#include "ipv6.h"

/*
 *  address resolution tables


@@ 27,12 28,6 @@ char *arpstate[] =
	"WAIT",
};

enum
{
	CMadd,
	CMflush,
};

/*
 *  one per Fs
 */


@@ 42,24 37,27 @@ struct Arp
	Fs	*f;
	Arpent	*hash[NHASH];
	Arpent	cache[NCACHE];
};

static
Cmdtab arpcmd[] =
{
	CMadd,	"add",	0,
	CMflush,	"flush",	1,
	Arpent	*rxmt;
	Proc	*rxmitp;	/* neib sol re-transmit proc */
	Rendez	rxmtq;
	Block 	*dropf, *dropl;
};

char *Ebadarp = "bad arp";

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

extern int 	ReTransTimer = RETRANS_TIMER;
static void 	rxmitproc(void *v);

void
arpinit(Fs *f)
{
	f->arp = smalloc(sizeof(Arp));
	f->arp->f = f;
	f->arp->rxmt = nil;
	f->arp->dropf = f->arp->dropl = nil;
	kproc("rxmitproc", rxmitproc, f->arp);
}

static Arpent*


@@ 83,6 81,7 @@ newarp(Arp *arp, uchar *ip, Medium *m)
	/* dump waiting packets */
	xp = a->hold;
	a->hold = nil;

	while(xp){
		next = xp->list;
		freeblist(xp);


@@ 92,7 91,74 @@ newarp(Arp *arp, uchar *ip, Medium *m)
	/* take out of current chain */
	l = &arp->hash[haship(a->ip)];
	for(f = *l; f; f = f->hash){
		if(f == a) {
		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;
}

static Arpent*
newarp6(Arp *arp, uchar *ip, Ipifc *ifc, int addrxt)
{
	uint t;
	Block *next, *xp;
	Arpent *a, *e, *f, **l;
	Medium *m = ifc->m;
	int empty;

	/* 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;

	if(isv4(a->ip)){
		while(xp){
			next = xp->list;
			freeblist(xp);
			xp = next;
		}
	}
	else {	// queue icmp unreachable for rxmitproc later on, w/o arp lock
		if(xp){
			if(arp->dropl == nil) 
				arp->dropf = xp;
			else
				arp->dropl->list = xp;

			for(next = xp->list; next; next = next->list)
				xp = next;
			arp->dropl = xp;
			wakeup(&arp->rxmtq);
		}
	}

	/* 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;
		}


@@ 103,36 169,107 @@ newarp(Arp *arp, uchar *ip, Medium *m)
	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;

	a->rxtat = msec + ReTransTimer;
	a->rxtsrem = MAX_MULTICAST_SOLICIT;
	a->ifc = ifc;
	a->ifcid = ifc->ifcid;

	/* put to the end of re-transmit chain; addrxt is 0 when isv4(a->ip) */
	if(!ipismulticast(a->ip) && addrxt){
		l = &arp->rxmt;
		empty = (*l==nil);

		for(f = *l; f; f = f->nextrxt){
			if(f == a){
				*l = a->nextrxt;
				break;
			}
			l = &f->nextrxt;
		}
		for(f = *l; f; f = f->nextrxt){
			l = &f->nextrxt;
		}
		*l = a;
		if(empty) 
			wakeup(&arp->rxmtq);
	}

	a->nextrxt = nil;

	return a;
}

/* called with arp qlocked */

void
cleanarpent(Arp *arp, Arpent *a)
{
	Arpent *f, **l;

	a->used = 0;
	a->time = 0;
	a->type = 0;
	a->state = 0;
	
	/* 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;
	}

	/* take out of re-transmit chain */
	l = &arp->rxmt;
	for(f = *l; f; f = f->nextrxt){
		if(f == a){
			*l = a->nextrxt;
			break;
		}
		l = &f->nextrxt;
	}
	a->nextrxt = nil;
	a->hash = nil;
	a->hold = nil;
	a->last = nil;
	a->ifc = nil;
}

Arpent*
arpget(Arp *arp, Block *bp, int version, Medium *type, uchar *ip, uchar *mac)
arpget(Arp *arp, Block *bp, int version, Ipifc *ifc, uchar *ip, uchar *mac)
{
	int hash;
	Arpent *a;
	Medium *type = ifc->m;
	uchar v6ip[IPaddrlen];

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

	qlock(arp);
	hash = haship(ip);
	for(a = arp->hash[hash]; a; a = a->hash) {
	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(arp, ip, type);
		//a = newarp6(arp, ip, ifc, (version != V4));
		if(version == V4)
			a = newarp(arp, ip, type);
		else 
			a = newarp6(arp, ip, ifc, 1);
		a->state = AWAIT;
	}
	a->used = msec;


@@ 169,6 306,18 @@ Block*
arpresolve(Arp *arp, Arpent *a, Medium *type, uchar *mac)
{
	Block *bp;
	Arpent *f, **l;

	if(!isv4(a->ip)){
		l = &arp->rxmt;
		for(f = *l; f; f = f->nextrxt){
			if(f == a){
				*l = a->nextrxt;
				break;
			}
			l = &f->nextrxt;
		}
	}

	memmove(a->mac, mac, type->maclen);
	a->type = type;


@@ 186,7 335,7 @@ arpenter(Fs *fs, int version, uchar *ip, uchar *mac, int n, int refresh)
{
	Arp *arp;
	Route *r;
	Arpent *a;
	Arpent *a, *f, **l;
	Ipifc *ifc;
	Medium *type;
	Block *bp, *next;


@@ 194,12 343,12 @@ arpenter(Fs *fs, int version, uchar *ip, uchar *mac, int n, int refresh)

	arp = fs->arp;

	if(n != 6) {
	if(n != 6){
//		print("arp: len = %d\n", n);
		return;
	}

	if(version == V4) {
	if(version == V4){
		r = v4lookup(fs, ip);
		v4tov6(v6ip, ip);
		ip = v6ip;


@@ 207,7 356,7 @@ arpenter(Fs *fs, int version, uchar *ip, uchar *mac, int n, int refresh)
	else
		r = v6lookup(fs, ip);

	if(r == nil) {
	if(r == nil){
//		print("arp: no route for entry\n");
		return;
	}


@@ 216,19 365,35 @@ arpenter(Fs *fs, int version, uchar *ip, uchar *mac, int n, int refresh)
	type = ifc->m;

	qlock(arp);
	for(a = arp->hash[haship(ip)]; a; a = a->hash) {
	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) {
		if(ipcmp(a->ip, ip) == 0){
			a->state = AOK;
			memmove(a->mac, mac, type->maclen);

			if(version == V6){
				/* take out of re-transmit chain */
				l = &arp->rxmt;
				for(f = *l; f; f = f->nextrxt){
					if(f == a){
						*l = a->nextrxt;
						break;
					}
					l = &f->nextrxt;
				}
			}

			a->hold = nil;
			a->ifc = ifc;
			a->ifcid = ifc->ifcid;
			bp = a->hold;
			a->hold = nil;
			if(version == V4)
				ip += IPv4off;
			qunlock(arp);
			while(bp) {
			while(bp){
				next = bp->list;
				if(ifc != nil){
					if(waserror()){


@@ 252,7 417,12 @@ arpenter(Fs *fs, int version, uchar *ip, uchar *mac, int n, int refresh)
	}

	if(refresh == 0){
		a = newarp(arp, ip, type);
		//a = newarp6(arp, ip, ifc, 0);
		if(version == 4)
			a = newarp(arp, ip, type);
		else 
			a = newarp6(arp, ip, ifc, 0);

		a->state = AOK;
		a->type = type;
		memmove(a->mac, mac, type->maclen);


@@ 269,40 439,63 @@ arpwrite(Fs *fs, char *s, int len)
	Arp *arp;
	Block *bp;
	Arpent *a;
	Cmdbuf *cb;
	Cmdtab *ct;
	Medium *m;
	char *f[4], buf[256];
	uchar ip[IPaddrlen], mac[MAClen];

	arp = fs->arp;

	cb = parsecmd(s, len);
	if(waserror()){
		free(cb);
		nexterror();
	}

	ct = lookupcmd(cb, arpcmd, nelem(arpcmd));

	switch(ct->index){
	case CMadd:
		switch(cb->nf) {
	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 = getfields(buf, f, 4, 1, " ");
	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));
// clear all pkts on these lists (rxmt, dropf/l)
		arp->rxmt = nil;
		arp->dropf = nil;
		arp->dropl = nil;
		qunlock(arp);
	} else if(strcmp(f[0], "add") == 0){
		switch(n){
		default:
			cmderror(cb, Ecmdargs);
			error(Ebadarg);
		case 3:
			parseip(ip, cb->f[1]);
			r = v4lookup(fs, ip+IPv4off);
			parseip(ip, f[1]);
			if(isv4(ip))
				r = v4lookup(fs, ip+IPv4off);
			else
				r = v6lookup(fs, ip);
			if(r == nil)
				error("destination unreachable");
				error("Destination unreachable");
			m = r->ifc->m;
			n = parsemac(mac, cb->f[2], m->maclen);
			n = parsemac(mac, f[2], m->maclen);
			break;
		case 4:
			m = ipfindmedium(cb->f[1]);
			m = ipfindmedium(f[1]);
			if(m == nil)
				error(Ebadarp);
			parseip(ip, cb->f[2]);
			n = parsemac(mac, cb->f[3], m->maclen);
			parseip(ip, f[2]);
			n = parsemac(mac, f[3], m->maclen);
			break;
		}



@@ 310,38 503,18 @@ arpwrite(Fs *fs, char *s, int len)
			error(Ebadarp);

		m->ares(fs, V6, ip, mac, n, 0);
		break;

	case CMflush:
		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);
		break;
	}
	} else
		error(Ebadarp);

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

enum
{
	Alinelen=	66,
	Alinelen=	90,
};

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

static void
convmac(char *p, uchar *mac, int n)


@@ 380,3 553,136 @@ arpread(Arp *arp, char *p, ulong offset, int len)

	return n;
}

extern int
rxmitsols(Arp *arp)
{
	uint sflag;
	Block *next, *xp;
	Arpent *a, *b, **l;
	Fs *f;
	uchar ipsrc[IPaddrlen];
	Ipifc *ifc = nil;
	long nrxt;

	qlock(arp);
	f = arp->f;

	a = arp->rxmt;
	if(a==nil){
		nrxt = 0;
		goto dodrops; 		//return nrxt;
	}
	nrxt = a->rxtat - msec;
	if(nrxt > 3*ReTransTimer/4) 
		goto dodrops; 		//return nrxt;

	for(; a; a = a->nextrxt){
		ifc = a->ifc;
		assert(ifc != nil);
		if((a->rxtsrem <= 0) || !(canrlock(ifc)) || (a->ifcid != ifc->ifcid)){
			xp = a->hold;
			a->hold = nil;

			if(xp){
				if(arp->dropl == nil) 
					arp->dropf = xp;
				else
					arp->dropl->list = xp;
			}

			cleanarpent(arp, a);
		}
		else
			break;
	}

	 /* need to unlock arp, else will deadlock when icmpns 
	  * wants to lock arp later.
	  */
	
	qunlock(arp);

	if(a == nil) 
		goto dodrops; 		// return 0;

	if(sflag = ipv6anylocal(ifc, ipsrc)) 
		icmpns(f, ipsrc, sflag, a->ip, TARG_MULTI, ifc->mac); 

	runlock(ifc);

	/* grab lock on arp again */

	qlock(arp);	

	/* put to the end of re-transmit chain */
	l = &arp->rxmt;
	for(b = *l; b; b = b->nextrxt){
		if(b == a){
			*l = a->nextrxt;
			break;
		}
		l = &b->nextrxt;
	}
	for(b = *l; b; b = b->nextrxt){
		l = &b->nextrxt;
	}
	*l = a;
	a->rxtsrem--;
	a->nextrxt = nil;
	a->time = msec;
	a->rxtat = msec + ReTransTimer;

	a = arp->rxmt;
	if(a==nil)
		nrxt = 0;
	else 
		nrxt = a->rxtat - msec;

dodrops:
	xp = arp->dropf;
	arp->dropf = nil;
	arp->dropl = nil;
	qunlock(arp);

	for(; xp; xp = next){
		next = xp->list;
		icmphostunr(f, ifc, xp, icmp6_adr_unreach, 1);
	}

	return nrxt;

}

static int
rxready(void *v)
{
	Arp *arp = (Arp *) v;
	int x;

	x = ((arp->rxmt != nil) || (arp->dropf != nil));

	return x;
}

static void
rxmitproc(void *v)
{
	Arp *arp = v;
	long wakeupat;

	arp->rxmitp = up;
	print("arp rxmitproc started\n");
	if(waserror()){
		arp->rxmitp = 0;
		pexit("hangup", 1);
	}
	for(;;){
		wakeupat = rxmitsols(arp);
		if(wakeupat == 0) 
			sleep(&arp->rxmtq, rxready, v); 
		else if(wakeupat > ReTransTimer/4) 
			tsleep(&arp->rxmtq, return0, 0, wakeupat); 
	}
}


M ip/devip.c => ip/devip.c +51 -80
@@ 15,6 15,7 @@ enum
	Qndb,
	Qiproute,
	Qipselftab,
	Qipgate6,
	Qlog,

	Qprotodir,			/* directory for a protocol */


@@ 54,30 55,6 @@ QLock	fslock;
Fs	*ipfs[Nfs];	/* attached fs's */
Queue	*qlog;

enum
{
	CMaddmulti,
	CMannounce,
	CMbind,
	CMconnect,
	CMremmulti,
	CMtos,
	CMttl,
	CMwildcard,
};

Cmdtab ipctlmsg[] =
{
	CMaddmulti,	"addmulti",	0,
	CMannounce,	"announce",	0,
	CMbind,		"bind",		0,
	CMconnect,	"connect",	0,
	CMremmulti,	"remmulti",	0,
	CMtos,		"tos",		0,
	CMttl,		"ttl",			0,
	CMwildcard,	"*",			0,
};

extern	void nullmediumlink(void);
extern	void pktmediumlink(void);
	long ndbwrite(Fs *f, char *a, ulong off, int n);


@@ 166,7 143,6 @@ ip1gen(Chan *c, int i, Dir *dp)
	case Qndb:
		p = "ndb";
		len = strlen(f->ndb);
		q.vers = f->ndbvers;
		break;
	case Qiproute:
		p = "iproute";


@@ 175,13 151,15 @@ ip1gen(Chan *c, int i, Dir *dp)
		p = "ipselftab";
		prot = 0444;
		break;
	case Qipgate6:
		p = "ipgate6";
		prot = 0444;
		break;
	case Qlog:
		p = "log";
		break;
	}
	devdir(c, q, p, len, network, prot, dp);
	if(i == Qndb)
		dp->mtime = f->ndbmtime;
	return 1;
}



@@ 217,6 195,7 @@ ipgen(Chan *c, char*, Dirtab*, int, int s, Dir *dp)
	case Qlog:
	case Qiproute:
	case Qipselftab:
	case Qipgate6:
		return ip1gen(c, TYPE(c->qid), dp);
	case Qprotodir:
		if(s == DEVDOTDOT){


@@ 346,6 325,7 @@ ipwalk(Chan* c, Chan *nc, char **name, int nname)
	return w;
}


static int
ipstat(Chan* c, uchar* db, int n)
{


@@ 384,12 364,10 @@ ipopen(Chan* c, int omode)
	default:
		break;
	case Qndb:
		if((omode & (OWRITE|OTRUNC)) && !iseve())
		if(omode & (OWRITE|OTRUNC) && !iseve())
			error(Eperm);
		if((omode & (OWRITE|OTRUNC)) == (OWRITE|OTRUNC)){
		if((omode & (OWRITE|OTRUNC)) == (OWRITE|OTRUNC))
			f->ndb[0] = 0;
			f->ndbvers++;
		}
		break;
	case Qlog:
		netlogopen(f);


@@ 405,6 383,7 @@ ipopen(Chan* c, int omode)
	case Qstats:
	case Qbootp:
	case Qipselftab:
	case Qipgate6:
		if(omode != OREAD)
			error(Eperm);
		break;


@@ 497,14 476,25 @@ ipopen(Chan* c, int omode)
	c->mode = openmode(omode);
	c->flag |= COPEN;
	c->offset = 0;
	c->iounit = qiomaxatomic;
	return c;
}

static void
ipcreate(Chan*, char*, int, ulong)
{
	error(Eperm);
}

static void
ipremove(Chan*)
{
	error(Eperm);
}

static int
ipwstat(Chan *c, uchar *dp, int n)
{
	Dir *d;
	Dir d;
	Conv *cv;
	Fs *f;
	Proto *p;


@@ 519,26 509,16 @@ ipwstat(Chan *c, uchar *dp, int n)
		break;
	}

	d = smalloc(n + sizeof(Dir));
	if(waserror()){
		free(d);
		nexterror();
	n = convM2D(dp, n, &d, nil);
	if(n > 0){
		p = f->p[PROTO(c->qid)];
		cv = p->conv[CONV(c->qid)];
		if(!iseve() && strcmp(ATTACHER(c), cv->owner) != 0)
			error(Eperm);
		if(d.uid[0])
			kstrdup(&cv->owner, d.uid);
		cv->perm = d.mode & 0777;
	}
	n = convM2D(dp, n, d, nil);
	if(n == 0)
		error(Eshortstat);

	p = f->p[PROTO(c->qid)];
	cv = p->conv[CONV(c->qid)];
	if(!iseve() && strcmp(ATTACHER(c), cv->owner) != 0)
		error(Eperm);
	if(!emptystr(d->uid))
		kstrdup(&cv->owner, d->uid);
	if(d->mode != ~0UL)
		cv->perm = d->mode & 0777;

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



@@ 631,6 611,8 @@ ipread(Chan *ch, void *a, long n, vlong off)
		return routeread(f, a, offset, n);
	case Qipselftab:
		return ipselftabread(f, a, offset, n);
	case Qipgate6:
		return ipgateread6(f, a, offset, n);
	case Qlog:
		return netlogread(f, a, offset, n);
	case Qctl:


@@ 749,6 731,7 @@ setluniqueport(Conv* c, int lport)
	return nil;
}


/*
 *  pick a local port and set it
 */


@@ 1037,7 1020,6 @@ ipwrite(Chan* ch, void *v, long n, vlong off)
	Proto *x;
	char *p;
	Cmdbuf *cb;
	Cmdtab *ct;
	uchar ia[IPaddrlen], ma[IPaddrlen];
	Fs *f;
	char *a;


@@ 1080,26 1062,21 @@ ipwrite(Chan* ch, void *v, long n, vlong off)
			free(cb);
			nexterror();
		}
		ct = lookupcmd(cb, ipctlmsg, nelem(ipctlmsg));
		switch(ct->index){
		case CMconnect:
		if(cb->nf < 1)
			error("short control request");
		if(strcmp(cb->f[0], "connect") == 0)
			connectctlmsg(x, c, cb);
			break;
		case CMannounce:
		else if(strcmp(cb->f[0], "announce") == 0)
			announcectlmsg(x, c, cb);
			break;
		case CMbind:
		else if(strcmp(cb->f[0], "bind") == 0)
			bindctlmsg(x, c, cb);
			break;
		case CMttl:
		else if(strcmp(cb->f[0], "ttl") == 0)
			ttlctlmsg(c, cb);
			break;
		case CMtos:
		else if(strcmp(cb->f[0], "tos") == 0)
			tosctlmsg(c, cb);
			break;
		case CMaddmulti:
			if(cb->nf != 2 && cb->nf != 3)
				cmderror(cb, Ecmdargs);
		else if(strcmp(cb->f[0], "addmulti") == 0){
			if(cb->nf < 2)
				error("addmulti needs interface address");
			if(cb->nf == 2){
				if(!ipismulticast(c->raddr))
					error("addmulti for a non multicast address");


@@ 1112,23 1089,19 @@ ipwrite(Chan* ch, void *v, long n, vlong off)
				parseip(ia, cb->f[1]);
				ipifcaddmulti(c, ma, ia);
			}
			break;
		case CMremmulti:
		} 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);
			break;
		case CMwildcard:
			if(x->ctl == nil)
				cmderror(cb, "unknown control request");
		} else if(x->ctl != nil) {
			p = x->ctl(c, cb->f, cb->nf);
			if(p != nil)
				error(p);
		}

		} else 
			error("unknown control request");
		qunlock(c);
		free(cb);
		poperror();


@@ 1175,13 1148,13 @@ Dev ipdevtab = {
	ipwalk,
	ipstat,
	ipopen,
	devcreate,
	ipcreate,
	ipclose,
	ipread,
	ipbread,
	ipwrite,
	ipbwrite,
	devremove,
	ipremove,
	ipwstat,
};



@@ 1375,8 1348,6 @@ ndbwrite(Fs *f, char *a, ulong off, int n)
		error(Eio);
	memmove(f->ndb+off, a, n);
	f->ndb[off+n] = 0;
	f->ndbvers++;
	f->ndbmtime = seconds();
	return n;
}


M ip/esp.c => ip/esp.c +4 -3
@@ 23,7 23,7 @@ enum
{
	IP_ESPPROTO	= 50,
	EsphdrSize	= 28,	// includes IP header
	IphdrSize	= 20,	// options have been stripped
	IphdrSize	= 20,	// options have been striped
	EsptailSize	= 2,	// does not include pad or auth data
	UserhdrSize	= 4,	// user visable header size - if enabled
};


@@ 285,6 285,7 @@ espkick(Conv *c)
	auth = bp->rp + EsphdrSize + payload + pad + EsptailSize;

	// fill in head
	eh->vihl = IP_VER4;
	hnputl(eh->espspi, ecb->spi);
	hnputl(eh->espseq, ++ecb->seq);
	v6tov4(eh->espsrc, c->laddr);


@@ 296,8 297,8 @@ espkick(Conv *c)
	ecb->auth(ecb, bp->rp+IphdrSize, (EsphdrSize-IphdrSize)+payload+pad+EsptailSize, auth);

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

void

M ip/ethermedium.c => ip/ethermedium.c +187 -64
@@ 6,6 6,7 @@
#include "../port/error.h"

#include "ip.h"
#include "ipv6.h"

typedef struct Etherhdr Etherhdr;
struct Etherhdr


@@ 15,7 16,8 @@ struct Etherhdr
	uchar	t[2];
};

static void	etherread(void *a);
static void	etherread4(void *a);
static void	etherread6(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);


@@ 26,6 28,8 @@ static void	sendarp(Ipifc *ifc, Arpent *a);
static void	sendgarp(Ipifc *ifc, uchar*);
static int	multicastea(uchar *ea, uchar *ip);
static void	recvarpproc(void*);
static void	resolveaddr6(Ipifc *ifc, Arpent *a);
static void	etherpref2addr(uchar *pref, uchar *ea);

Medium ethermedium =
{


@@ 41,6 45,7 @@ Medium ethermedium =
.remmulti=	etherremmulti,
.ares=		arpenter,
.areg=		sendgarp,
.pref2addr=	etherpref2addr,
};

Medium gbemedium =


@@ 57,6 62,7 @@ Medium gbemedium =
.remmulti=	etherremmulti,
.ares=		arpenter,
.areg=		sendgarp,
.pref2addr=	etherpref2addr,
};

typedef struct	Etherrock Etherrock;


@@ 64,10 70,13 @@ struct Etherrock
{
	Fs	*f;		/* file system we belong to */
	Proc	*arpp;		/* arp process */
	Proc	*readp;		/* reading process */
	Chan	*mchan;		/* Data channel */
	Proc	*read4p;	/* reading process (v4)*/
	Proc	*read6p;	/* reading process (v6)*/
	Chan	*mchan4;	/* Data channel for v4 */
	Chan	*achan;		/* Arp channel */
	Chan	*cchan;		/* Control channel */
	Chan	*cchan4;	/* Control channel for v4 */
	Chan	*mchan6;	/* Data channel for v6 */
	Chan	*cchan6;	/* Control channel for v6 */
};

/*


@@ 76,10 85,12 @@ struct Etherrock
enum
{
	ETARP		= 0x0806,
	ETIP		= 0x0800,
	ETIP4		= 0x0800,
	ETIP6		= 0x86DD,
	ARPREQUEST	= 1,
	ARPREPLY	= 2,
};

typedef struct Etherarp Etherarp;
struct Etherarp
{


@@ 97,7 108,6 @@ struct Etherarp
	uchar	tpa[4];
};


/*
 *  called to bind an IP ifc to an ethernet device
 *  called with ifc wlock'd


@@ 105,9 115,9 @@ struct Etherarp
static void
etherbind(Ipifc *ifc, int argc, char **argv)
{
	Chan *mchan, *cchan, *achan;
	char addr[Maxpath];
	char dir[Maxpath];
	Chan *mchan4, *cchan4, *achan, *mchan6, *cchan6;
	char addr[Maxpath];	//char addr[2*KNAMELEN];
	char dir[Maxpath];	//char dir[2*KNAMELEN];
	char *buf;
	int n;
	char *ptr;


@@ 116,15 126,19 @@ etherbind(Ipifc *ifc, int argc, char **argv)
	if(argc < 2)
		error(Ebadarg);

	mchan = cchan = achan = nil;
	mchan4 = cchan4 = achan = mchan6 = cchan6 = nil;
	buf = nil;
	if(waserror()){
		if(mchan != nil)
			cclose(mchan);
		if(cchan != nil)
			cclose(cchan);
		if(mchan4 != nil)
			cclose(mchan4);
		if(cchan4 != nil)
			cclose(cchan4);
		if(achan != nil)
			cclose(achan);
		if(mchan6 != nil)
			cclose(mchan6);
		if(cchan6 != nil)
			cclose(cchan6);
		if(buf != nil)
			free(buf);
		nexterror(); 


@@ 137,7 151,7 @@ etherbind(Ipifc *ifc, int argc, char **argv)
	 *  this device.
	 */
	snprint(addr, sizeof(addr), "%s!0x800", argv[2]);
	mchan = chandial(addr, nil, dir, &cchan);
	mchan4 = chandial(addr, nil, dir, &cchan4);

	/*
	 *  get mac address


@@ 162,18 176,30 @@ etherbind(Ipifc *ifc, int argc, char **argv)
	snprint(addr, sizeof(addr), "%s!0x806", argv[2]);
	achan = chandial(addr, nil, nil, nil);

	/*
	 *  open ip conversation
	 *
	 *  the dial will fail if the type is already open on
	 *  this device.
	 */
	snprint(addr, sizeof(addr), "%s!0x86DD", argv[2]);
	mchan6 = chandial(addr, nil, dir, &cchan6);

	er = smalloc(sizeof(*er));
	er->mchan = mchan;
	er->cchan = cchan;
	er->mchan4 = mchan4;
	er->cchan4 = cchan4;
	er->achan = achan;
	er->mchan6 = mchan6;
	er->cchan6 = cchan6;
	er->f = ifc->conv->p->f;
	ifc->arg = er;

	free(buf);
	poperror();

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

/*


@@ 184,21 210,27 @@ etherunbind(Ipifc *ifc)
{
	Etherrock *er = ifc->arg;

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

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

	if(er->mchan != nil)
		cclose(er->mchan);
	if(er->mchan4 != nil)
		cclose(er->mchan4);
	if(er->achan != nil)
		cclose(er->achan);
	if(er->cchan != nil)
		cclose(er->cchan);
	if(er->cchan4 != nil)
		cclose(er->cchan4);
	if(er->mchan6 != nil)
		cclose(er->mchan6);
	if(er->cchan6 != nil)
		cclose(er->cchan6);

	free(er);
}


@@ 215,12 247,15 @@ etherbwrite(Ipifc *ifc, Block *bp, int version, uchar *ip)
	Etherrock *er = ifc->arg;

	/* get mac address of destination */
	a = arpget(er->f->arp, bp, version, ifc->m, ip, mac);
	a = arpget(er->f->arp, bp, version, ifc, ip, mac);
	if(a){
		/* check for broadcast or multicast */
		bp = multicastarp(er->f, a, ifc->m, mac);
		if(bp == nil){
			sendarp(ifc, a);
		if(bp==nil){
			if(version == V4) 
				sendarp(ifc, a);
			else 
				resolveaddr6(ifc, a);
			return;
		}
	}


@@ 236,26 271,67 @@ etherbwrite(Ipifc *ifc, Block *bp, int version, uchar *ip)
	/* copy in mac addresses and ether type */
	memmove(eh->s, ifc->mac, sizeof(eh->s));
	memmove(eh->d, mac, sizeof(eh->d));
	switch(version){

 	switch(version){
	case V4:
		eh->t[0] = 0x08;
		eh->t[1] = 0x00;
		devtab[er->mchan4->type]->bwrite(er->mchan4, bp, 0);
		break;
	case V6:
		eh->t[0] = 0x86;
		eh->t[1] = 0xDD;
		devtab[er->mchan6->type]->bwrite(er->mchan6, bp, 0);
		break;
	}

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


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

	ifc = a;
	er = ifc->arg;
	er->read4p = up;	/* hide identity under a rock for unbind */
	if(waserror()){
		er->read4p = 0;
		pexit("hangup", 1);
	}
	for(;;){
		bp = devtab[er->mchan4->type]->bread(er->mchan4, ifc->maxmtu, 0);
		if(!canrlock(ifc)){
			freeb(bp);
			continue;
		}
		if(waserror()){
			runlock(ifc);
			nexterror();
		}
		ifc->in++;
		bp->rp += ifc->m->hsize;
		if(ifc->lifc == nil)
			freeb(bp);
		else
			ipiput4(er->f, ifc, bp);
		runlock(ifc);
		poperror();
	}
}


/*
 *  process to read from the ethernet, IPv6
 */
static void
etherread6(void *a)
{
	Ipifc *ifc;
	Block *bp;


@@ 263,13 339,13 @@ etherread(void *a)

	ifc = a;
	er = ifc->arg;
	er->readp = up;	/* hide identity under a rock for unbind */
	er->read6p = up;	/* hide identity under a rock for unbind */
	if(waserror()){
		er->readp = 0;
		er->read6p = 0;
		pexit("hangup", 1);
	}
	for(;;){
		bp = devtab[er->mchan->type]->bread(er->mchan, ifc->maxmtu, 0);
		bp = devtab[er->mchan6->type]->bread(er->mchan6, ifc->maxmtu, 0);
		if(!canrlock(ifc)){
			freeb(bp);
			continue;


@@ 283,7 359,7 @@ etherread(void *a)
		if(ifc->lifc == nil)
			freeb(bp);
		else
			ipiput(er->f, ifc, bp);
			ipiput6(er->f, ifc, bp);
		runlock(ifc);
		poperror();
	}


@@ 295,10 371,14 @@ etheraddmulti(Ipifc *ifc, uchar *a, uchar *)
	uchar mac[6];
	char buf[64];
	Etherrock *er = ifc->arg;
	int type;

	multicastea(mac, a);
	type = multicastea(mac, a);
	sprint(buf, "addmulti %E", mac);
	devtab[er->cchan->type]->write(er->cchan, buf, strlen(buf), 0);
	if(type == V4) 
		devtab[er->cchan4->type]->write(er->cchan4, buf, strlen(buf), 0);
	else if(type == V6) 
		devtab[er->cchan6->type]->write(er->cchan6, buf, strlen(buf), 0);
}

static void


@@ 307,10 387,14 @@ etherremmulti(Ipifc *ifc, uchar *a, uchar *)
	uchar mac[6];
	char buf[64];
	Etherrock *er = ifc->arg;
	int type;

	multicastea(mac, a);
	type = multicastea(mac, a);
	sprint(buf, "remmulti %E", mac);
	devtab[er->cchan->type]->write(er->cchan, buf, strlen(buf), 0);
	if(type == V4) 
		devtab[er->cchan4->type]->write(er->cchan4, buf, strlen(buf), 0);
	else if(type == V6) 
		devtab[er->cchan6->type]->write(er->cchan6, buf, strlen(buf), 0);
}

/*


@@ 357,7 441,7 @@ sendarp(Ipifc *ifc, Arpent *a)

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


@@ 368,6 452,43 @@ sendarp(Ipifc *ifc, Arpent *a)
		print("arp: send: %r\n");
}

static void
resolveaddr6(Ipifc *ifc, Arpent *a)
{
	int sflag;
	Block *bp;
	Etherrock *er = ifc->arg;
	uchar ipsrc[IPaddrlen];

	/* don't do anything if it's been less than a second since the last */
	if(msec - a->time < ReTransTimer){
		arprelease(er->f->arp, 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;
	a->rxtat = msec + ReTransTimer;
	if(a->rxtsrem <= 0) {
		arprelease(er->f->arp, a);
		return;
	}

	a->rxtsrem--;
	arprelease(er->f->arp, a);

	if(sflag = ipv6anylocal(ifc, ipsrc)) 
		icmpns(er->f, ipsrc, sflag, a->ip, TARG_MULTI, ifc->mac);
}

/*
 *  send a gratuitous arp to refresh arp caches
 */


@@ 397,7 518,7 @@ sendgarp(Ipifc *ifc, uchar *ip)

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


@@ 429,13 550,6 @@ recvarp(Ipifc *ifc)
		break;

	case ARPREPLY:
		if(iplocalonifc(ifc, ip) || ipproxyifc(er->f, ifc, ip)){
			if(memcmp(e->sha, ifc->mac, sizeof(e->sha)) != 0){
				print("arprep: 0x%E/0x%E also has ip addr %V\n",
					e->s, e->sha, e->spa);
				break;
			}
		}
		arpenter(er->f, V4, e->spa, e->sha, sizeof(e->sha), 0);
		break;



@@ 447,11 561,8 @@ recvarp(Ipifc *ifc)
		/* check for someone that think's they're me */
		v4tov6(ip, e->spa);
		if(iplocalonifc(ifc, ip) || ipproxyifc(er->f, ifc, ip)){
			if(memcmp(e->sha, ifc->mac, sizeof(e->sha)) != 0){
				print("arpreq: 0x%E/0x%E also has ip addr %V\n",
					e->s, e->sha, e->spa);
				break;
			}
			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);


@@ 468,8 579,6 @@ recvarp(Ipifc *ifc)
		if(!ipproxyifc(er->f, ifc, ip))
			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;


@@ 478,7 587,7 @@ recvarp(Ipifc *ifc)
		memset(r, 0, sizeof(Etherarp));
		hnputs(r->type, ETARP);
		hnputs(r->hrd, 1);
		hnputs(r->pro, ETIP);
		hnputs(r->pro, ETIP4);
		r->hln = sizeof(r->sha);
		r->pln = sizeof(r->spa);
		hnputs(r->op, ARPREPLY);


@@ 526,14 635,14 @@ multicastea(uchar *ea, uchar *ip)
		ea[4] = ip[14];
		ea[5] = ip[15];
		break;
	case V6:
		ea[0] = 0x33;
		ea[1] = 0x33;
		ea[2] = ip[12];
 	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;
 		ea[4] = ip[14];
 		ea[5] = ip[15];
 		break;
	}
	return x;
}


@@ 573,3 682,17 @@ ethermediumlink(void)
	addipmedium(&ethermedium);
	addipmedium(&gbemedium);
}


static void 
etherpref2addr(uchar *pref, uchar *ea)
{
	pref[8]  = ea[0] | 0x2;
	pref[9]  = ea[1];
	pref[10] = ea[2];
	pref[11] = 0xFF;
	pref[12] = 0xFE;
	pref[13] = ea[3];
	pref[14] = ea[4];
	pref[15] = ea[5];
}

M ip/gre.c => ip/gre.c +2 -1
@@ 140,6 140,7 @@ grekick(Conv *c)
		return;

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

	v4tov6(raddr, ghp->dst);
	if(ipcmp(raddr, v4prefix) == 0)


@@ 156,7 157,7 @@ grekick(Conv *c)
	ghp->frag[0] = 0;
	ghp->frag[1] = 0;

	ipoput(c->p->f, bp, 0, c->ttl, c->tos);
	ipoput4(c->p->f, bp, 0, c->ttl, c->tos);
}

static void

M ip/icmp.c => ip/icmp.c +19 -38
@@ 28,7 28,6 @@ typedef struct Icmp {

enum {			/* Packet Types */
	EchoReply	= 0,
	UnreachableV6	= 1,
	Unreachable	= 3,
	SrcQuench	= 4,
	Redirect	= 5,


@@ 52,7 51,6 @@ enum

char *icmpnames[Maxtype+1] =
{
[UnreachableV6]		"UnreachableV6",
[EchoReply]		"EchoReply",
[Unreachable]		"Unreachable",
[SrcQuench]		"SrcQuench",


@@ 106,7 104,7 @@ struct Icmppriv
	ulong	out[Maxtype+1];
};

static char*
extern char*
icmpconnect(Conv *c, char **argv, int argc)
{
	char *e;


@@ 117,21 115,21 @@ icmpconnect(Conv *c, char **argv, int argc)
	return e;
}

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

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

static char*
extern char*
icmpannounce(Conv *c, char **argv, int argc)
{
	char *e;


@@ 144,7 142,7 @@ icmpannounce(Conv *c, char **argv, int argc)
	return nil;
}

static void
extern void
icmpclose(Conv *c)
{
	qclose(c->rq);


@@ 154,7 152,7 @@ icmpclose(Conv *c)
	c->lport = 0;
}

static void
extern void
icmpkick(Conv *c)
{
	Icmp *p;


@@ 170,6 168,7 @@ icmpkick(Conv *c)
		return;
	}
	p = (Icmp *)(bp->rp);
	p->vihl = IP_VER4;
	ipriv = c->p->priv;
	if(p->type <= Maxtype)	
		ipriv->out[p->type]++;


@@ 181,23 180,24 @@ icmpkick(Conv *c)
	memset(p->cksum, 0, sizeof(p->cksum));
	hnputs(p->cksum, ptclcsum(bp, ICMP_IPSIZE, blocklen(bp) - ICMP_IPSIZE));
	ipriv->stats[OutMsgs]++;
	ipoput(c->p->f, bp, 0, c->ttl, c->tos);
	ipoput4(c->p->f, bp, 0, c->ttl, c->tos);
}

extern void
icmpttlexceeded(Fs *f, Ipifc *ifc, Block *bp)
icmpttlexceeded(Fs *f, uchar *ia, Block *bp)
{
	Block	*nbp;
	Icmp	*p, *np;

	p = (Icmp *)bp->rp;
	p->vihl = IP_VER4;

	netlog(f, Logicmp, "sending icmpttlexceeded -> %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;
	memmove(np->dst, p->src, sizeof(np->dst));
	v6tov4(np->src, ifc->lifc->local);
	v6tov4(np->src, ia);
	memmove(np->data, bp->rp, ICMP_IPSIZE + 8);
	np->type = TimeExceed;
	np->code = 0;


@@ 206,7 206,7 @@ icmpttlexceeded(Fs *f, Ipifc *ifc, Block *bp)
	hnputs(np->seq, 0);
	memset(np->cksum, 0, sizeof(np->cksum));
	hnputs(np->cksum, ptclcsum(nbp, ICMP_IPSIZE, blocklen(nbp) - ICMP_IPSIZE));
	ipoput(f, nbp, 0, MAXTTL, DFLTTOS);
	ipoput4(f, nbp, 0, MAXTTL, DFLTTOS);

}



@@ 219,6 219,7 @@ icmpnoconv(Fs *f, Block *bp)
	uchar	addr[IPaddrlen];

	p = (Icmp *)bp->rp;
	p->vihl = IP_VER4;

	/* only do this for unicast sources and destinations */
	v4tov6(addr, p->dst);


@@ 244,7 245,7 @@ icmpnoconv(Fs *f, Block *bp)
	hnputs(np->seq, 0);
	memset(np->cksum, 0, sizeof(np->cksum));
	hnputs(np->cksum, ptclcsum(nbp, ICMP_IPSIZE, blocklen(nbp) - ICMP_IPSIZE));
	ipoput(f, nbp, 0, MAXTTL, DFLTTOS);
	ipoput4(f, nbp, 0, MAXTTL, DFLTTOS);
}

static void


@@ 279,6 280,7 @@ mkechoreply(Block *bp)
	uchar	ip[4];

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


@@ 295,7 297,7 @@ static char *unreachcode[] =
[1]	"host unreachable",
[2]	"protocol unreachable",
[3]	"port unreachable",
[4]	"unfragmentable",
[4]	"fragmentation needed and DF set",
[5]	"source route failed",
};



@@ 309,9 311,6 @@ icmpiput(Proto *icmp, Ipifc*, Block *bp)
	char	*msg;
	char	m2[128];
	Icmppriv *ipriv;
	int	code;
	ushort	x;
	uchar	dst[IPaddrlen];

	ipriv = icmp->priv;
	


@@ 346,27 345,13 @@ icmpiput(Proto *icmp, Ipifc*, Block *bp)
	case EchoRequest:
		r = mkechoreply(bp);
		ipriv->out[EchoReply]++;
		ipoput(icmp->f, r, 0, MAXTTL, DFLTTOS);
		ipoput4(icmp->f, r, 0, MAXTTL, DFLTTOS);
		break;
	case Unreachable:
		code = p->code;
		x = 0;
		switch(code){
		default:
		if(p->code > 5 || p->code < 0)
			msg = unreachcode[1];
			break;
		case 5:
			x = nhgets(p->seq);
			msg = unreachcode[p->code];
			break;
		case 0:
		case 1:
		case 2:
		case 3:
		case 4:
		else
			msg = unreachcode[p->code];
			break;
		}

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


@@ 374,10 359,6 @@ icmpiput(Proto *icmp, Ipifc*, Block *bp)
			goto raise;
		}
		p = (Icmp *)bp->rp;
		if(code == 5){
			v4tov6(dst, p->dst);
			update_mtucache(dst, x);
		}
		pr = Fsrcvpcolx(icmp->f, p->proto);
		if(pr != nil && pr->advise != nil) {
			(*pr->advise)(pr, bp, msg);

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

typedef struct ICMPpkt ICMPpkt;
typedef struct IPICMP IPICMP;
typedef struct Ndpkt Ndpkt;
typedef struct NdiscC NdiscC;

struct ICMPpkt {
	uchar	type;
	uchar	code;
	uchar	cksum[2];
	uchar	icmpid[2];
	uchar	seq[2];
};

struct IPICMP {
	Ip6hdr;
	ICMPpkt;
};

struct NdiscC
{
	IPICMP;
	uchar target[IPaddrlen];
};

struct Ndpkt
{
	NdiscC;
	uchar otype;
	uchar olen;	// length in units of 8 octets(incl type, code),
				// 1 for IEEE 802 addresses
	uchar lnaddr[6];	// link-layer address
};

enum {	
	// ICMPv6 types
	EchoReply	= 0,
	UnreachableV6	= 1,
	PacketTooBigV6	= 2,
	TimeExceedV6	= 3,
	SrcQuench	= 4,
	ParamProblemV6	= 4,
	Redirect	= 5,
	EchoRequest	= 8,
	TimeExceed	= 11,
	InParmProblem	= 12,
	Timestamp	= 13,
	TimestampReply	= 14,
	InfoRequest	= 15,
	InfoReply	= 16,
	AddrMaskRequest = 17,
	AddrMaskReply   = 18,
	EchoRequestV6	= 128,
	EchoReplyV6	= 129,
	RouterSolicit	= 133,
	RouterAdvert	= 134,
	NbrSolicit	= 135,
	NbrAdvert	= 136,
	RedirectV6	= 137,

	Maxtype6	= 137,
};

char *icmpnames6[Maxtype6+1] =
{
[EchoReply]		"EchoReply",
[UnreachableV6]		"UnreachableV6",
[PacketTooBigV6]	"PacketTooBigV6",
[TimeExceedV6]		"TimeExceedV6",
[SrcQuench]		"SrcQuench",
[Redirect]		"Redirect",
[EchoRequest]		"EchoRequest",
[TimeExceed]		"TimeExceed",
[InParmProblem]		"InParmProblem",
[Timestamp]		"Timestamp",
[TimestampReply]	"TimestampReply",
[InfoRequest]		"InfoRequest",
[InfoReply]		"InfoReply",
[AddrMaskRequest]	"AddrMaskRequest",
[AddrMaskReply]		"AddrMaskReply",
[EchoRequestV6]		"EchoRequestV6",
[EchoReplyV6]		"EchoReplyV6",
[RouterSolicit]		"RouterSolicit",
[RouterAdvert]		"RouterAdvert",
[NbrSolicit]		"NbrSolicit",
[NbrAdvert]		"NbrAdvert",
[RedirectV6]		"RedirectV6",
};

enum
{
	InMsgs6,
	InErrors6,
	OutMsgs6,
	CsumErrs6,
	LenErrs6,
	HlenErrs6,
	HoplimErrs6,
	IcmpCodeErrs6,
	TargetErrs6,
	OptlenErrs6,
	AddrmxpErrs6,
	RouterAddrErrs6,

	Nstats6,
};

static char *statnames6[Nstats6] =
{
[InMsgs6]	"InMsgs",
[InErrors6]	"InErrors",
[OutMsgs6]	"OutMsgs",
[CsumErrs6]	"CsumErrs",
[LenErrs6]	"LenErrs",
[HlenErrs6]	"HlenErrs",
[HoplimErrs6]	"HoplimErrs",
[IcmpCodeErrs6]	"IcmpCodeErrs",
[TargetErrs6]	"TargetErrs",
[OptlenErrs6]	"OptlenErrs",
[AddrmxpErrs6]	"AddrmxpErrs",
[RouterAddrErrs6]	"RouterAddrErrs",
};

typedef struct Icmppriv6
{
	ulong	stats[Nstats6];

	/* message counts */
	ulong	in[Maxtype6+1];
	ulong	out[Maxtype6+1];
} Icmppriv6;

typedef struct Icmpcb6 
{
	QLock;
	uchar headers;
} Icmpcb6;

static char *unreachcode[] =
{
[icmp6_no_route]	"no route to destination",
[icmp6_ad_prohib]	"comm with destination administratively prohibited",
[icmp6_unassigned]	"icmp unreachable: unassigned error code (2)",
[icmp6_adr_unreach]	"address unreachable",
[icmp6_port_unreach]	"port unreachable",
[icmp6_unkn_code]	"icmp unreachable: unknown code",
};

enum {
	ICMP_USEAD6	= 40,
};

enum {
	Oflag	= 1<<5,
	Sflag	= 1<<6,
	Rflag	= 1<<7,
};

enum {
	slladd	= 1,
	tlladd	= 2,
	prfinfo	= 3,
	redhdr	= 4,
	mtuopt	= 5,
};

static void
set_cksum(Block *bp)
{
	IPICMP *p = (IPICMP *)(bp->rp);

	hnputl(p->vcf, 0);  // borrow IP header as pseudoheader
	hnputs(p->ploadlen, blocklen(bp)-IPV6HDR_LEN);
	p->proto = 0;
	p->ttl = ICMPv6;	// ttl gets set later
	hnputs(p->cksum, 0);
	hnputs(p->cksum, ptclcsum(bp, 0, blocklen(bp)));
	p->proto = ICMPv6;
}

static Block *
newIPICMP(int packetlen)
{
	Block	*nbp;
	nbp = allocb(packetlen);
	nbp->wp += packetlen;
	memset(nbp->rp, 0, packetlen);
	return nbp;
}


char*
icmpconnect6(Conv *c, char **argv, int argc)
{
	char *e;

	e = Fsstdconnect(c, argv, argc);
	Fsconnected(c, e);

	return e;
}

void
icmpadvise6(Proto *icmp, Block *bp, char *msg)
{
	Conv	**c, *s;
	IPICMP	*p;
	ushort	recid;

	p = (IPICMP *) bp->rp;
	recid = nhgets(p->icmpid);

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

void
icmpkick6(Conv *c)
{
	IPICMP *p;
	Block *bp;
	uchar laddr[IPaddrlen], raddr[IPaddrlen];
	Icmppriv6 *ipriv = c->p->priv;
	Icmpcb6 *icb = (Icmpcb6*)c->ptcl;

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

	if(icb->headers==6) {
		/* get user specified addresses */
//print("icmpkick6: headers\n");
		bp = pullupblock(bp, ICMP_USEAD6);
		if(bp == nil)
			return;
		bp->rp += 8;
		ipmove(laddr, bp->rp);
		bp->rp += IPaddrlen;
		ipmove(raddr, bp->rp);
		bp->rp += IPaddrlen;
		bp = padblock(bp, sizeof(Ip6hdr));
	}

	if(blocklen(bp) < sizeof(IPICMP)){
		freeblist(bp);
		return;
	}
	p = (IPICMP *)(bp->rp);
	if(icb->headers == 6) {
		ipmove(p->dst, raddr);
		ipmove(p->src, laddr);
	} else {
		ipmove(p->dst, c->raddr);
		ipmove(p->src, c->laddr);
		hnputs(p->icmpid, c->lport);
	}

	set_cksum(bp);
	p->vcf[0] = 0x06 << 4;
	if(p->type <= Maxtype6)	
		ipriv->out[p->type]++;
//print("icmpkick6: src %I dst %I\n", p->src, p->dst);
	ipoput6(c->p->f, bp, 0, c->ttl, c->tos);
}

char*
icmpctl6(Conv *c, char **argv, int argc)
{
	Icmpcb6 *icb;

	icb = (Icmpcb6*) c->ptcl;

	if(argc==1) {
		if(strcmp(argv[0], "headers")==0) {
			icb->headers = 6;
			return nil;
		}
	}
	return "unknown control request";
}

static void
goticmpkt6(Proto *icmp, Block *bp, int muxkey)
{
	Conv	**c, *s;
	IPICMP	*p = (IPICMP *)bp->rp;
	ushort	recid; 
	uchar 	*addr;

	if(muxkey == 0) {
		recid = nhgets(p->icmpid);
		addr = p->src;
	}
	else {
		recid = muxkey;
		addr = p->dst;
	}

	for(c = icmp->conv; *c; c++){
		s = *c;
		if(s->lport == recid && ipcmp(s->raddr, addr) == 0){
			bp = concatblock(bp);
			if(bp != nil)
				qpass(s->rq, bp);
			return;
		}
	}

	freeblist(bp);
}

static Block *
mkechoreply6(Block *bp)
{
	IPICMP *p = (IPICMP *)(bp->rp);
	uchar	addr[IPaddrlen];

	ipmove(addr, p->src);
	ipmove(p->src, p->dst);
	ipmove(p->dst, addr);
	p->type = EchoReplyV6;
	set_cksum(bp);
	return bp;
}

/*
 * sends out an ICMPv6 neighbor solicitation
 * 	suni == SRC_UNSPEC or SRC_UNI, 
 *	tuni == TARG_MULTI => multicast for address resolution,
 * 	and tflag == TARG_UNI => neighbor reachability.
 */

extern void
icmpns(Fs *f, uchar* src, int suni, uchar* targ, int tuni, uchar* mac)
{
	Block	*nbp;
	Ndpkt *np;
	Proto *icmp = f->t2p[ICMPv6];
	Icmppriv6 *ipriv = icmp->priv;


	nbp = newIPICMP(sizeof(Ndpkt));
	np = (Ndpkt*) nbp->rp;


	if(suni == SRC_UNSPEC) 
		memmove(np->src, v6Unspecified, IPaddrlen);
	else 
		memmove(np->src, src, IPaddrlen);

	if(tuni == TARG_UNI)
		memmove(np->dst, targ, IPaddrlen);
	else
		ipv62smcast(np->dst, targ);

	np->type = NbrSolicit;
	np->code = 0;
	memmove(np->target, targ, IPaddrlen);
	if(suni != SRC_UNSPEC) {
		np->otype = SRC_LLADDRESS;
		np->olen = 1;	/* 1+1+6 = 8 = 1 8-octet */
		memmove(np->lnaddr, mac, sizeof(np->lnaddr));
	}
	else {
		int r = sizeof(Ndpkt)-sizeof(NdiscC);
		nbp->wp -= r;
	}

	set_cksum(nbp);
	np = (Ndpkt*) nbp->rp;
	np->ttl = HOP_LIMIT;
	np->vcf[0] = 0x06 << 4;
	ipriv->out[NbrSolicit]++;
	netlog(f, Logicmp, "sending neighbor solicitation %I\n", targ);
	ipoput6(f, nbp, 0, MAXTTL, DFLTTOS);
}

/*
 * sends out an ICMPv6 neighbor advertisement. pktflags == RSO flags.
 */
extern void
icmpna(Fs *f, uchar* src, uchar* dst, uchar* targ, uchar* mac, uchar flags)
{
	Block	*nbp;
	Ndpkt *np;
	Proto *icmp = f->t2p[ICMPv6];
	Icmppriv6 *ipriv = icmp->priv;

	nbp = newIPICMP(sizeof(Ndpkt));
	np = (Ndpkt*) nbp->rp;

	memmove(np->src, src, IPaddrlen);
	memmove(np->dst, dst, IPaddrlen);

	np->type = NbrAdvert;
	np->code = 0;
	np->icmpid[0] = flags;
	memmove(np->target, targ, IPaddrlen);

	np->otype = TARGET_LLADDRESS;
	np->olen = 1;	
	memmove(np->lnaddr, mac, sizeof(np->lnaddr));

	set_cksum(nbp);
	np = (Ndpkt*) nbp->rp;
	np->ttl = HOP_LIMIT;
	np->vcf[0] = 0x06 << 4;
	ipriv->out[NbrAdvert]++;
	netlog(f, Logicmp, "sending neighbor advertisement %I\n", src);
	ipoput6(f, nbp, 0, MAXTTL, DFLTTOS);
}

extern void
icmphostunr(Fs *f, Ipifc *ifc, Block *bp, int code, int free)
{
	Block *nbp;
	IPICMP *np;
	Ip6hdr	*p;
	int osz = BLEN(bp);
	int sz = MIN(sizeof(IPICMP) + osz, v6MINTU);
	Proto	*icmp = f->t2p[ICMPv6];
	Icmppriv6 *ipriv = icmp->priv;

	p = (Ip6hdr *) bp->rp;

	if(isv6mcast(p->src)) 
		goto clean;

	nbp = newIPICMP(sz);
	np = (IPICMP *) nbp->rp;

	rlock(ifc);
	if(ipv6anylocal(ifc, np->src)) {
		netlog(f, Logicmp, "send icmphostunr -> s%I d%I\n", p->src, p->dst);
	}
	else {
		netlog(f, Logicmp, "icmphostunr fail -> s%I d%I\n", p->src, p->dst);
		freeblist(nbp);
		if(free) 
			goto clean;
		else
			return;
	}

	memmove(np->dst, p->src, IPaddrlen);
	np->type = UnreachableV6;
	np->code = code;
	memmove(nbp->rp + sizeof(IPICMP), bp->rp, sz - sizeof(IPICMP));
	set_cksum(nbp);
	np->ttl = HOP_LIMIT;
	np->vcf[0] = 0x06 << 4;
	ipriv->out[UnreachableV6]++;

	if(free)
		ipiput6(f, ifc, nbp);
	else {
		ipoput6(f, nbp, 0, MAXTTL, DFLTTOS);
		return;
	}

clean:
	runlock(ifc);
	freeblist(bp);
}

extern void
icmpttlexceeded6(Fs *f, Ipifc *ifc, Block *bp)
{
	Block *nbp;
	IPICMP *np;
	Ip6hdr	*p;
	int osz = BLEN(bp);
	int sz = MIN(sizeof(IPICMP) + osz, v6MINTU);
	Proto	*icmp = f->t2p[ICMPv6];
	Icmppriv6 *ipriv = icmp->priv;

	p = (Ip6hdr *) bp->rp;

	if(isv6mcast(p->src)) 
		return;

	nbp = newIPICMP(sz);
	np = (IPICMP *) nbp->rp;

	if(ipv6anylocal(ifc, np->src)) {
		netlog(f, Logicmp, "send icmpttlexceeded6 -> s%I d%I\n", p->src, p->dst);
	}
	else {
		netlog(f, Logicmp, "icmpttlexceeded6 fail -> s%I d%I\n", p->src, p->dst);
		return;
	}

	memmove(np->dst, p->src, IPaddrlen);
	np->type = TimeExceedV6;
	np->code = 0;
	memmove(nbp->rp + sizeof(IPICMP), bp->rp, sz - sizeof(IPICMP));
	set_cksum(nbp);
	np->ttl = HOP_LIMIT;
	np->vcf[0] = 0x06 << 4;
	ipriv->out[TimeExceedV6]++;
	ipoput6(f, nbp, 0, MAXTTL, DFLTTOS);
}

extern void
icmppkttoobig6(Fs *f, Ipifc *ifc, Block *bp)
{
	Block *nbp;
	IPICMP *np;
	Ip6hdr	*p;
	int osz = BLEN(bp);
	int sz = MIN(sizeof(IPICMP) + osz, v6MINTU);
	Proto	*icmp = f->t2p[ICMPv6];
	Icmppriv6 *ipriv = icmp->priv;

	p = (Ip6hdr *) bp->rp;

	if(isv6mcast(p->src)) 
		return;

	nbp = newIPICMP(sz);
	np = (IPICMP *) nbp->rp;

	if(ipv6anylocal(ifc, np->src)) {
		netlog(f, Logicmp, "send icmppkttoobig6 -> s%I d%I\n", p->src, p->dst);
	}
	else {
		netlog(f, Logicmp, "icmppkttoobig6 fail -> s%I d%I\n", p->src, p->dst);
		return;
	}

	memmove(np->dst, p->src, IPaddrlen);
	np->type = PacketTooBigV6;
	np->code = 0;
	hnputl(np->icmpid, ifc->maxmtu - ifc->m->hsize);
	memmove(nbp->rp + sizeof(IPICMP), bp->rp, sz - sizeof(IPICMP));
	set_cksum(nbp);
	np->ttl = HOP_LIMIT;
	np->vcf[0] = 0x06 << 4;
	ipriv->out[PacketTooBigV6]++;
	ipoput6(f, nbp, 0, MAXTTL, DFLTTOS);
}

/*
 * RFC 2461, pages 39-40, pages 57-58.
 */
static int
valid(Proto *icmp, Ipifc *ifc, Block *bp, Icmppriv6 *ipriv) {
	int 	sz, osz, unsp, n, ttl, iplen;
	int 	pktsz = BLEN(bp);
	uchar	*packet = bp->rp;
	IPICMP	*p = (IPICMP *) packet;
	Ndpkt	*np;

	USED(ifc);
	n = blocklen(bp);
	if(n < sizeof(IPICMP)) {
		ipriv->stats[HlenErrs6]++;
		netlog(icmp->f, Logicmp, "icmp hlen %d\n", n);
		goto err;
	}

	iplen = nhgets(p->ploadlen);
	if(iplen > n-IPV6HDR_LEN || (iplen % 1)) {
		ipriv->stats[LenErrs6]++;
		netlog(icmp->f, Logicmp, "icmp length %d\n", iplen);
		goto err;
	}

	// Rather than construct explicit pseudoheader, overwrite IPv6 header
	if(p->proto != ICMPv6) {
		// This code assumes no extension headers!!!
		netlog(icmp->f, Logicmp, "icmp error: extension header\n");
		goto err;
	}
	memset(packet, 0, 4);
	ttl = p->ttl;
	p->ttl = p->proto;
	p->proto = 0;
	if(ptclcsum(bp, 0, iplen + IPV6HDR_LEN)) {
		ipriv->stats[CsumErrs6]++;
		netlog(icmp->f, Logicmp, "icmp checksum error\n");
		goto err;
	}
	p->proto = p->ttl;
	p->ttl = ttl;

	/* additional tests for some pkt types */
	if( (p->type == NbrSolicit) ||
		(p->type == NbrAdvert) ||
		(p->type == RouterAdvert) ||
		(p->type == RouterSolicit) ||
		(p->type == RedirectV6) ) {

		if(p->ttl != HOP_LIMIT) {
			ipriv->stats[HoplimErrs6]++; 
			goto err; 
		}
		if(p->code != 0) {
			ipriv->stats[IcmpCodeErrs6]++; 
			goto err; 
		}

		switch (p->type) {
		case NbrSolicit:
		case NbrAdvert:
			np = (Ndpkt*) p;
			if(isv6mcast(np->target)) {
				ipriv->stats[TargetErrs6]++; 
				goto err; 
			}
			if(optexsts(np) && (np->olen == 0)) {
				ipriv->stats[OptlenErrs6]++; 
				goto err; 
			}
		
			if(p->type == NbrSolicit) {
				if(ipcmp(np->src, v6Unspecified) == 0) { 
					if(!issmcast(np->dst) || optexsts(np))  {
						ipriv->stats[AddrmxpErrs6]++; 
						goto err;
					}
				}
			}
		
			if(p->type == NbrAdvert) {
				if((isv6mcast(np->dst))&&(nhgets(np->icmpid) & Sflag)){
					ipriv->stats[AddrmxpErrs6]++; 
					goto err; 
				}
			}
			break;
	
		case RouterAdvert:
			if(pktsz - sizeof(Ip6hdr) < 16) {
				ipriv->stats[HlenErrs6]++; 
				goto err; 
			}
			if(!islinklocal(p->src)) {
				ipriv->stats[RouterAddrErrs6]++; 
				goto err; 
			}
			sz = sizeof(IPICMP) + 8;
			while ((sz+1) < pktsz) {
				osz = *(packet+sz+1);
				if(osz <= 0) {
					ipriv->stats[OptlenErrs6]++; 
					goto err; 
				}	
				sz += 8*osz;
			}
			break;
	
		case RouterSolicit:
			if(pktsz - sizeof(Ip6hdr) < 8) {
				ipriv->stats[HlenErrs6]++; 
				goto err; 
			}
			unsp = (ipcmp(p->src, v6Unspecified) == 0);
			sz = sizeof(IPICMP) + 8;
			while ((sz+1) < pktsz) {
				osz = *(packet+sz+1);
				if((osz <= 0) ||
					(unsp && (*(packet+sz) == slladd)) ) {
					ipriv->stats[OptlenErrs6]++; 
					goto err; 
				}
				sz += 8*osz;
			}
			break;
	
		case RedirectV6:
			//to be filled in
			break;
	
		default:
			goto err;
		}
	}

	return 1;

err:
	ipriv->stats[InErrors6]++; 
	return 0;
}

static int
targettype(Fs *f, Ipifc *ifc, uchar *target)
{
	Iplifc *lifc;
	int t;

	rlock(ifc);
	if(ipproxyifc(f, ifc, target)) {
		runlock(ifc);
		return t_uniproxy;
	}

	for(lifc = ifc->lifc; lifc; lifc = lifc->next) {
		if(ipcmp(lifc->local, target) == 0) {
			t = (lifc->tentative) ? t_unitent : t_unirany; 
			runlock(ifc);
			return t;
		}
	}

	runlock(ifc);
	return 0;
}

static void
icmpiput6(Proto *icmp, Ipifc *ipifc, Block *bp)
{
	uchar	*packet = bp->rp;
	IPICMP	*p = (IPICMP *)packet;
	Icmppriv6 *ipriv = icmp->priv;
	Block	*r;
	Proto	*pr;
	char	*msg, m2[128];
	Ndpkt* np;
	uchar pktflags;
	uchar lsrc[IPaddrlen];
	int refresh = 1;
	Iplifc *lifc;

	if(!valid(icmp, ipifc, bp, ipriv)) 
		goto raise;

	if(p->type <= Maxtype6)
		ipriv->in[p->type]++;

	switch(p->type) {
	case EchoRequestV6:
		r = mkechoreply6(bp);
		ipriv->out[EchoReply]++;
		ipoput6(icmp->f, r, 0, MAXTTL, DFLTTOS);
		break;

	case UnreachableV6:
		if(p->code > 4 || p->code < 0)
			msg = unreachcode[icmp6_unkn_code];
		else
			msg = unreachcode[p->code];

		bp->rp += sizeof(IPICMP);
		if(blocklen(bp) < 8){
			ipriv->stats[LenErrs6]++;
			goto raise;
		}
		p = (IPICMP *)bp->rp;
		pr = Fsrcvpcolx(icmp->f, p->proto);
		if(pr != nil && pr->advise != nil) {
			(*pr->advise)(pr, bp, msg);
			return;
		}

		bp->rp -= sizeof(IPICMP);
		goticmpkt6(icmp, bp, 0);
		break;

	case TimeExceedV6:
		if(p->code == 0){
			sprint(m2, "ttl exceeded at %I", p->src);

			bp->rp += sizeof(IPICMP);
			if(blocklen(bp) < 8){
				ipriv->stats[LenErrs6]++;
				goto raise;
			}
			p = (IPICMP *)bp->rp;
			pr = Fsrcvpcolx(icmp->f, p->proto);
			if(pr != nil && pr->advise != nil) {
				(*pr->advise)(pr, bp, m2);
				return;
			}
			bp->rp -= sizeof(IPICMP);
		}

		goticmpkt6(icmp, bp, 0);
		break;

	case RouterAdvert:
	case RouterSolicit:
		/* using lsrc as a temp, munge hdr for goticmp6 
		memmove(lsrc, p->src, IPaddrlen);
		memmove(p->src, p->dst, IPaddrlen);
		memmove(p->dst, lsrc, IPaddrlen); */

		goticmpkt6(icmp, bp, p->type);
		break;

	case NbrSolicit:
		np = (Ndpkt*) p;
		pktflags = 0;
		switch (targettype(icmp->f, ipifc, np->target)) {
		case t_unirany:
			pktflags |= Oflag;
			/* fall through */

		case t_uniproxy: 
			if(ipcmp(np->src, v6Unspecified) != 0) {
				arpenter(icmp->f, V6, np->src, np->lnaddr, 8*np->olen-2, 0);
				pktflags |= Sflag;
			}
			if(ipv6local(ipifc, lsrc)) {
				icmpna(icmp->f, lsrc, 
				   (ipcmp(np->src, v6Unspecified)==0)?v6allnodesL:np->src,
				   np->target, ipifc->mac, pktflags); 
			}
			else
				freeblist(bp);
			break;

		case t_unitent:
			/* not clear what needs to be done. send up
			 * an icmp mesg saying don't use this address? */

		default:
			freeblist(bp);
		}

		ipriv->out[NbrSolicit]++;
		break;

	case NbrAdvert:
		np = (Ndpkt*) p;

		/* if the target address matches one of the local interface 
		 * address and the local interface address has tentative bit set, 
		 * then insert into ARP table. this is so the duplication address 
		 * detection part of ipconfig can discover duplication through 
		 * the arp table
		 */
		lifc = iplocalonifc(ipifc, np->target);
		if(lifc && lifc->tentative)
			refresh = 0;
		arpenter(icmp->f, V6, np->target, np->lnaddr, 8*np->olen-2, refresh);
		freeblist(bp);
		break;

	case PacketTooBigV6:

	default:
		goticmpkt6(icmp, bp, 0);
		break;
	}
	return;

raise:
	freeblist(bp);

}

int
icmpstats6(Proto *icmp6, char *buf, int len)
{
	Icmppriv6 *priv;
	char *p, *e;
	int i;

	priv = icmp6->priv;
	p = buf;
	e = p+len;
	for(i = 0; i < Nstats6; i++)
		p = seprint(p, e, "%s: %lud\n", statnames6[i], priv->stats[i]);
	for(i = 0; i <= Maxtype6; i++){
		if(icmpnames6[i])
			p = seprint(p, e, "%s: %lud %lud\n", icmpnames6[i], priv->in[i], priv->out[i]);
/*		else
			p = seprint(p, e, "%d: %lud %lud\n", i, priv->in[i], priv->out[i]);
*/
	}
	return p - buf;
}


// need to import from icmp.c
extern int	icmpstate(Conv *c, char *state, int n);
extern char*	icmpannounce(Conv *c, char **argv, int argc);
extern void	icmpcreate(Conv *c);
extern void	icmpclose(Conv *c);

void
icmp6init(Fs *fs)
{
	Proto *icmp6 = smalloc(sizeof(Proto));

	icmp6->priv = smalloc(sizeof(Icmppriv6));
	icmp6->name = "icmpv6";
	icmp6->kick = icmpkick6;
	icmp6->connect = icmpconnect6;
	icmp6->announce = icmpannounce;
	icmp6->state = icmpstate;
	icmp6->create = icmpcreate;
	icmp6->close = icmpclose;
	icmp6->rcv = icmpiput6;
	icmp6->stats = icmpstats6;
	icmp6->ctl = icmpctl6;
	icmp6->advise = icmpadvise6;
	icmp6->gc = nil;
	icmp6->ipproto = ICMPv6;
	icmp6->nc = 16;
	icmp6->ptclsize = sizeof(Icmpcb6);

	Fsproto(fs, icmp6);
}


M ip/igmp.c => ip/igmp.c +3 -2
@@ 86,6 86,7 @@ igmpsendreport(Media *m, byte *addr)
	if(bp == nil)
		return;
	p = (IGMPpkt*)bp->wp;
	p->vihl = IP_VER4;
	bp->wp += sizeof(IGMPpkt);
	memset(bp->rp, 0, sizeof(IGMPpkt));
	hnputl(p->src, Mediagetaddr(m));


@@ 96,7 97,7 @@ igmpsendreport(Media *m, byte *addr)
	hnputs(p->igmpcksum, ptclcsum(bp, IGMP_IPHDRSIZE, IGMP_HDRSIZE));
	netlog(Logigmp, "igmpreport %I\n", p->group);
	stats.outreports++;
	ipoput(bp, 0, 1, DFLTTOS);	/* TTL of 1 */
	ipoput4(bp, 0, 1, DFLTTOS);	/* TTL of 1 */
}

static int


@@ 166,7 167,7 @@ igmpproc(void *a)
}

void
igmpiput(Media *m, Ipifc*, Block *bp)
igmpiput(Media *m, Ipifc *, Block *bp)
{
	int n;
	IGMPpkt *ghp;

M ip/il.c => ip/il.c +9 -4
@@ 367,6 367,7 @@ ilkick(Conv *c)
	/* Make space to fit il & ip */
	bp = padblock(bp, IL_IPSIZE+IL_HDRSIZE);
	ih = (Ilhdr *)(bp->rp);
	ih->vihl = IP_VER4;

	/* Ip fields */
	ih->frag[0] = 0;


@@ 408,7 409,7 @@ ilkick(Conv *c)

	if(later(msec, ic->timeout, nil))
		ilsettimeout(ic);
	ipoput(f, bp, 0, c->ttl, c->tos);
	ipoput4(f, bp, 0, c->ttl, c->tos);
	priv->stats[OutMsgs]++;
}



@@ 803,6 804,7 @@ ilrexmit(Ilcb *ic)
		return;

	h = (Ilhdr*)nb->rp;
	h->vihl = IP_VER4;

	h->iltype = Ildataquery;
	hnputl(h->ilack, ic->recvd);


@@ 819,7 821,7 @@ ilrexmit(Ilcb *ic)

	ilbackoff(ic);

	ipoput(c->p->f, nb, 0, ic->conv->ttl, ic->conv->tos);
	ipoput4(c->p->f, nb, 0, ic->conv->ttl, ic->conv->tos);

	/* statistics */
	ic->rxtot++;


@@ 979,6 981,7 @@ ilsendctl(Conv *ipc, Ilhdr *inih, int type, ulong id, ulong ack, int ilspec)
	bp->wp += IL_IPSIZE+IL_HDRSIZE;

	ih = (Ilhdr *)(bp->rp);
	ih->vihl = IP_VER4;

	/* Ip fields */
	ih->proto = IP_ILPROTO;


@@ 1015,6 1018,7 @@ ilsendctl(Conv *ipc, Ilhdr *inih, int type, ulong id, ulong ack, int ilspec)

	if(ilcksum)
		hnputs(ih->ilsum, ptclcsum(bp, IL_IPSIZE, IL_HDRSIZE));

if(ipc==nil)
	panic("ipc is nil caller is %.8lux", getcallerpc(&ipc));
if(ipc->p==nil)


@@ 1024,7 1028,7 @@ if(ipc->p==nil)
		iltype[ih->iltype], nhgetl(ih->ilid), nhgetl(ih->ilack), 
		nhgets(ih->ilsrc), nhgets(ih->ildst));

	ipoput(ipc->p->f, bp, 0, ttl, tos);
	ipoput4(ipc->p->f, bp, 0, ttl, tos);
}

void


@@ 1037,6 1041,7 @@ ilreject(Fs *f, Ilhdr *inih)
	bp->wp += IL_IPSIZE+IL_HDRSIZE;

	ih = (Ilhdr *)(bp->rp);
	ih->vihl = IP_VER4;

	/* Ip fields */
	ih->proto = IP_ILPROTO;


@@ 1057,7 1062,7 @@ ilreject(Fs *f, Ilhdr *inih)
	if(ilcksum)
		hnputs(ih->ilsum, ptclcsum(bp, IL_IPSIZE, IL_HDRSIZE));

	ipoput(f, bp, 0, MAXTTL, DFLTTOS);
	ipoput4(f, bp, 0, MAXTTL, DFLTTOS);
}

void

M ip/ip.c => ip/ip.c +200 -208
@@ 5,52 5,41 @@
#include	"fns.h"
#include	"../port/error.h"

#include	"../ip/ip.h"
#include	"ip.h"

typedef struct Iphdr	Iphdr;
typedef struct Fragment	Fragment;
typedef struct Ipfrag	Ipfrag;
typedef struct Ip4hdr		Ip4hdr;
typedef struct	IP	IP;
typedef struct	Fragment4	Fragment4;
typedef struct	Fragment6	Fragment6;
typedef struct	Ipfrag	Ipfrag;

enum
{
	IPHDR		= 20,		/* sizeof(Iphdr) */
	IP_VER		= 0x40,		/* Using IP version 4 */
	IP_HLEN		= 0x05,		/* Header length in words */
	IP4HDR		= 20,		/* sizeof(Ip4hdr) */
	IP6HDR		= 40,		/* sizeof(Ip6hdr) */
	IP_HLEN4	= 0x05,		/* Header length in words */
	IP_DF		= 0x4000,	/* Don't fragment */
	IP_MF		= 0x2000,	/* More fragments */
	IP6FHDR		= 8, 		/* sizeof(Fraghdr6) */
	IP_MAX		= (32*1024),	/* Maximum Internet packet size */
};

struct Iphdr
#define BLKIPVER(xp)	(((Ip4hdr*)((xp)->rp))->vihl&0xF0)

struct Ip4hdr
{
	uchar	vihl;		/* Version and header length */
	uchar	tos;		/* Type of service */
	uchar	length[2];	/* packet length */
	uchar	id[2];		/* ip->identification */
	uchar	frag[2];	/* Fragment information */
	uchar	ttl;		/* Time to live */
	uchar	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;
	ulong 	src;
	ulong 	dst;
	ushort	id;
	ulong 	age;
};

struct Ipfrag
{
	ushort	foff;
	ushort	flen;
};

/* MIB II counters */
enum
{


@@ 77,6 66,50 @@ enum
	Nstats,
};

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

struct Fragment6
{
	Block*	blist;
	Fragment6*	next;
	uchar 	src[IPaddrlen];
	uchar 	dst[IPaddrlen];
	uint	id;
	ulong 	age;
};

struct Ipfrag
{
	ushort	foff;
	ushort	flen;
};

/* an instance of IP */
struct IP
{
	ulong		stats[Nstats];

	QLock		fraglock4;
	Fragment4*	flisthead4;
	Fragment4*	fragfree4;
	Ref		id4;

	QLock		fraglock6;
	Fragment6*	flisthead6;
	Fragment6*	fragfree6;
	Ref		id6;

	int		iprouting;	/* true if we route like a gateway */
};

static char *statnames[] =
{
[Forwarding]	"Forwarding",


@@ 100,20 133,7 @@ static char *statnames[] =
[FragCreates]	"FragCreates",
};

/* an instance of IP */
struct IP
{
	ulong		stats[Nstats];

	QLock		fraglock;
	Fragment*	flisthead;
	Fragment*	fragfree;

	Ref		id;
	int		iprouting;			/* true if we route like a gateway */
};

#define BLKIP(xp)	((Iphdr*)((xp)->rp))
#define BLKIP(xp)	((Ip4hdr*)((xp)->rp))
/*
 * This sleazy macro relies on the media header size being
 * larger than sizeof(Ipfrag). ipreassemble checks this is true


@@ 121,9 141,62 @@ struct IP
#define BKFG(xp)	((Ipfrag*)((xp)->base))

ushort		ipcsum(uchar*);
Block*		ipreassemble(IP*, int, Block*, Iphdr*);
void		ipfragfree(IP*, Fragment*);
Fragment*	ipfragallo(IP*);
Block*		ip4reassemble(IP*, int, Block*, Ip4hdr*);
void		ipfragfree4(IP*, Fragment4*);
Fragment4*	ipfragallo4(IP*);


void
ip_init_6(Fs *f)
{
	v6params *v6p;

	v6p = smalloc(sizeof(v6params));
	
	v6p->rp.mflag		= 0;		// default not managed
	v6p->rp.oflag		= 0;
	v6p->rp.maxraint	= 600000;	// millisecs
	v6p->rp.minraint	= 200000;
	v6p->rp.linkmtu		= 0;		// no mtu sent
	v6p->rp.reachtime	= 0;
	v6p->rp.rxmitra		= 0;
	v6p->rp.ttl		= MAXTTL;
	v6p->rp.routerlt	= 3*(v6p->rp.maxraint);	

	v6p->hp.rxmithost	= 1000;		// v6 RETRANS_TIMER

	v6p->cdrouter 		= -1;

	f->v6p			= v6p;

}

void
initfrag(IP *ip, int size)
{
	Fragment4 *fq4, *eq4;
	Fragment6 *fq6, *eq6;

	ip->fragfree4 = (Fragment4*)malloc(sizeof(Fragment4) * size);
	if(ip->fragfree4 == nil)
		panic("initfrag");

	eq4 = &ip->fragfree4[size];
	for(fq4 = ip->fragfree4; fq4 < eq4; fq4++)
		fq4->next = fq4+1;

	ip->fragfree4[size-1].next = nil;

	ip->fragfree6 = (Fragment6*)malloc(sizeof(Fragment6) * size);
	if(ip->fragfree6 == nil)
		panic("initfrag");

	eq6 = &ip->fragfree6[size];
	for(fq6 = ip->fragfree6; fq6 < eq6; fq6++)
		fq6->next = fq6+1;

	ip->fragfree6[size-1].next = nil;
}

void
ip_init(Fs *f)


@@ 133,6 206,8 @@ ip_init(Fs *f)
	ip = smalloc(sizeof(IP));
	initfrag(ip, 100);
	f->ip = ip;

	ip_init_6(f);
}

void


@@ 146,13 221,13 @@ iprouting(Fs *f, int on)
}

void
ipoput(Fs *f, Block *bp, int gating, int ttl, int tos)
ipoput4(Fs *f, Block *bp, int gating, int ttl, int tos)
{
	Ipifc *ifc;
	uchar *gate;
	ulong fragoff;
	Block *xp, *nb;
	Iphdr *eh, *feh;
	Ip4hdr *eh, *feh;
	int lid, len, seglen, chunk, dlen, blklen, offset, medialen;
	Route *r, *sr;
	IP *ip;


@@ 160,7 235,7 @@ ipoput(Fs *f, Block *bp, int gating, int ttl, int tos)
	ip = f->ip;

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

	ip->stats[OutRequests]++;



@@ 204,7 279,7 @@ ipoput(Fs *f, Block *bp, int gating, int ttl, int tos)
		gate = r->v4.gate;

	if(!gating)
		eh->vihl = IP_VER|IP_HLEN;
		eh->vihl = IP_VER4|IP_HLEN4;
	eh->ttl = ttl;
	if(!gating)
		eh->tos = tos;


@@ 222,7 297,7 @@ ipoput(Fs *f, Block *bp, int gating, int ttl, int tos)
	medialen = ifc->maxmtu - ifc->m->hsize;
	if(len <= medialen) {
		if(!gating)
			hnputs(eh->id, incref(&ip->id));
			hnputs(eh->id, incref(&ip->id4));
		hnputs(eh->length, len);
		if(!gating){
			eh->frag[0] = 0;


@@ 231,8 306,6 @@ ipoput(Fs *f, Block *bp, int gating, int ttl, int tos)
		eh->cksum[0] = 0;
		eh->cksum[1] = 0;
		hnputs(eh->cksum, ipcsum(&eh->vihl));

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


@@ 246,7 319,7 @@ ipoput(Fs *f, Block *bp, int gating, int ttl, int tos)
		goto raise;
	}

	seglen = (medialen - IPHDR) & ~7;
	seglen = (medialen - IP4HDR) & ~7;
	if(seglen < 8){
		ip->stats[FragFails]++;
		ip->stats[OutDiscards]++;


@@ 254,14 327,14 @@ ipoput(Fs *f, Block *bp, int gating, int ttl, int tos)
		goto raise;
	}

	dlen = len - IPHDR;
	dlen = len - IP4HDR;
	xp = bp;
	if(gating)
		lid = nhgets(eh->id);
	else
		lid = incref(&ip->id);
		lid = incref(&ip->id4);

	offset = IPHDR;
	offset = IP4HDR;
	while(xp != nil && offset && offset >= BLEN(xp)) {
		offset -= BLEN(xp);
		xp = xp->next;


@@ 274,11 347,11 @@ ipoput(Fs *f, Block *bp, int gating, int ttl, int tos)
		fragoff = 0;
	dlen += fragoff;
	for(; fragoff < dlen; fragoff += seglen) {
		nb = allocb(IPHDR+seglen);
		feh = (Iphdr*)(nb->rp);
		nb = allocb(IP4HDR+seglen);
		feh = (Ip4hdr*)(nb->rp);

		memmove(nb->wp, eh, IPHDR);
		nb->wp += IPHDR;
		memmove(nb->wp, eh, IP4HDR);
		nb->wp += IP4HDR;

		if((fragoff + seglen) >= dlen) {
			seglen = dlen - fragoff;


@@ 287,7 360,7 @@ ipoput(Fs *f, Block *bp, int gating, int ttl, int tos)
		else	
			hnputs(feh->frag, (fragoff>>3)|IP_MF);

		hnputs(feh->length, seglen + IPHDR);
		hnputs(feh->length, seglen + IP4HDR);
		hnputs(feh->id, lid);

		/* Copy up the data area */


@@ 326,43 399,26 @@ free:
}

void
initfrag(IP *ip, int size)
{
	Fragment *fq, *eq;

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

	eq = &ip->fragfree[size];
	for(fq = ip->fragfree; fq < eq; fq++)
		fq->next = fq+1;

	ip->fragfree[size-1].next = nil;
}

#define DBG(x)	if((logmask & Logipmsg) && (iponly == 0 || x == iponly))netlog

void
ipiput(Fs *f, Ipifc *ifc, Block *bp)
ipiput4(Fs *f, Ipifc *ifc, Block *bp)
{
	int hl;
	Iphdr *h;
	int hop, tos, proto, olen;
	Ip4hdr *h;
	Proto *p;
	ushort frag;
	int notforme;
	uchar *dp, v6dst[IPaddrlen];
	IP *ip;
	Route *r, *sr;
	int olen;

	if(BLKIPVER(bp) != IP_VER4) {
		ipiput6(f, ifc, bp);
		return;
	}

	ip = f->ip;
	ip->stats[InReceives]++;

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

	/*
	 *  Ensure we have all the header info in the first
	 *  block.  Make life easier for other protocols by


@@ 370,15 426,16 @@ ipiput(Fs *f, Ipifc *ifc, Block *bp)
	 */
	if(BLEN(bp) < 64) {
		hl = blocklen(bp);
		if(hl < IPHDR)
			hl = IPHDR;
		if(hl < IP4HDR)
			hl = IP4HDR;
		if(hl > 64)
			hl = 64;
		bp = pullupblock(bp, hl);
		if(bp == nil)
			return;
	}
	h = (Iphdr *)(bp->rp);

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

	/* dump anything that whose header doesn't checksum */
	if(ipcsum(&h->vihl)) {


@@ 387,28 444,27 @@ ipiput(Fs *f, Ipifc *ifc, Block *bp)
		freeblist(bp);
		return;
	}

	v4tov6(v6dst, h->dst);
	notforme = ipforme(f, v6dst) == 0;

	/* Check header length and version */
	if(h->vihl != (IP_VER|IP_HLEN)) {
	if((h->vihl&0x0F) != IP_HLEN4) {
		hl = (h->vihl&0xF)<<2;
		if((h->vihl&0xF0) != IP_VER || hl < (IP_HLEN<<2)) {
		if(hl < (IP_HLEN4<<2)) {
			ip->stats[InHdrErrors]++;
			netlog(f, Logip, "ip: %V bad hivl %ux\n", h->src, h->vihl);
			freeblist(bp);
			return;
		}
		/* If this is not routed strip off the options */
	  /* If this is not routed strip off the options */
		if(notforme == 0) {
			olen = nhgets(h->length);
			dp = bp->rp + (hl - (IP_HLEN<<2));
			memmove(dp, h, IP_HLEN<<2);
			dp = bp->rp + (hl - (IP_HLEN4<<2));
			memmove(dp, h, IP_HLEN4<<2);
			bp->rp = dp;
			h = (Iphdr *)(bp->rp);
			h->vihl = (IP_VER|IP_HLEN);
			hnputs(h->length, olen-hl+(IP_HLEN<<2));
			h = (Ip4hdr*)(bp->rp);
			h->vihl = (IP_VER4|IP_HLEN4);
			hnputs(h->length, olen-hl+(IP_HLEN4<<2));
		}
	}



@@ 418,10 474,10 @@ ipiput(Fs *f, Ipifc *ifc, Block *bp)
			freeb(bp);
			return;
		}

		/* don't forward to source's network */
		sr = v4lookup(f, h->src);
		r = v4lookup(f, h->dst);

		if(r == nil || sr == r){
			ip->stats[OutDiscards]++;
			freeblist(bp);


@@ 429,13 485,14 @@ ipiput(Fs *f, Ipifc *ifc, Block *bp)
		}

		/* don't forward if packet has timed out */
		if(h->ttl <= 1){
		hop = h->ttl;
		if(hop < 1) {
			ip->stats[InHdrErrors]++;
			icmpttlexceeded(f, ifc, bp);
			icmpttlexceeded(f, ifc->lifc->local, bp);
			freeblist(bp);
			return;
		}
		

		/* reassemble if the interface expects it */
		if(r->ifc->reassemble){
			frag = nhgets(h->frag);


@@ 443,32 500,33 @@ ipiput(Fs *f, Ipifc *ifc, Block *bp)
				h->tos = 0;
				if(frag & IP_MF)
					h->tos = 1;
				bp = ipreassemble(ip, frag, bp, h);
				bp = ip4reassemble(ip, frag, bp, h);
				if(bp == nil)
					return;
				h = (Iphdr *)(bp->rp);
				h = (Ip4hdr*)(bp->rp);
			}
		}

		ip->stats[ForwDatagrams]++;
		ipoput(f, bp, 1, h->ttl - 1, h->tos);

		tos = h->tos;
		hop = h->ttl;
		ipoput4(f, bp, 1, hop - 1, tos);
		return;
	}

	/* reassemble */
	frag = nhgets(h->frag);
	if(frag) {
		h->tos = 0;
		if(frag & IP_MF)
			h->tos = 1;
		bp = ipreassemble(ip, frag, bp, h);
		bp = ip4reassemble(ip, frag, bp, h);
		if(bp == nil)
			return;
		h = (Iphdr *)(bp->rp);
		h = (Ip4hdr*)(bp->rp);
	}

	p = Fsrcvpcol(f, h->proto);
	proto = h->proto;
	p = Fsrcvpcol(f, proto);
	if(p != nil && p->rcv != nil) {
		ip->stats[InDelivers]++;
		(*p->rcv)(p, ifc, bp);


@@ 497,11 555,11 @@ ipstats(Fs *f, char *buf, int len)
}

Block*
ipreassemble(IP *ip, int offset, Block *bp, Iphdr *ih)
ip4reassemble(IP *ip, int offset, Block *bp, Ip4hdr *ih)
{
	int fend;
	ushort id;
	Fragment *f, *fnext;
	Fragment4 *f, *fnext;
	ulong src, dst;
	Block *bl, **l, *last, *prev;
	int ovlap, len, fragsize, pktposn;


@@ 515,21 573,21 @@ ipreassemble(IP *ip, int offset, Block *bp, Iphdr *ih)
	 */
	if(bp->next){
		bp = pullupblock(bp, blocklen(bp));
		ih = (Iphdr *)(bp->rp);
		ih = (Ip4hdr*)(bp->rp);
	}

	qlock(&ip->fraglock);
	qlock(&ip->fraglock4);

	/*
	 *  find a reassembly queue for this fragment
	 */
	for(f = ip->flisthead; f; f = fnext){
		fnext = f->next;	/* because ipfragfree changes the list */
	for(f = ip->flisthead4; f; f = fnext){
		fnext = f->next;	/* because ipfragfree4 changes the list */
		if(f->src == src && f->dst == dst && f->id == id)
			break;
		if(f->age < msec){
			ip->stats[ReasmTimeout]++;
			ipfragfree(ip, f);
			ipfragfree4(ip, f);
		}
	}



@@ 540,10 598,10 @@ ipreassemble(IP *ip, int offset, Block *bp, Iphdr *ih)
	 */
	if(!ih->tos && (offset & ~(IP_MF|IP_DF)) == 0) {
		if(f != nil) {
			ipfragfree(ip, f);
			ipfragfree4(ip, f);
			ip->stats[ReasmFails]++;
		}
		qunlock(&ip->fraglock);
		qunlock(&ip->fraglock4);
		return bp;
	}



@@ 553,18 611,18 @@ ipreassemble(IP *ip, int offset, Block *bp, Iphdr *ih)
	}

	BKFG(bp)->foff = offset<<3;
	BKFG(bp)->flen = nhgets(ih->length)-IPHDR;
	BKFG(bp)->flen = nhgets(ih->length)-IP4HDR;

	/* First fragment allocates a reassembly queue */
	if(f == nil) {
		f = ipfragallo(ip);
		f = ipfragallo4(ip);
		f->id = id;
		f->src = src;
		f->dst = dst;

		f->blist = bp;

		qunlock(&ip->fraglock);
		qunlock(&ip->fraglock4);
		ip->stats[ReasmReqds]++;
		return nil;
	}


@@ 587,7 645,7 @@ ipreassemble(IP *ip, int offset, Block *bp, Iphdr *ih)
		if(ovlap > 0) {
			if(ovlap >= BKFG(bp)->flen) {
				freeblist(bp);
				qunlock(&ip->fraglock);
				qunlock(&ip->fraglock4);
				return nil;
			}
			BKFG(prev)->flen -= ovlap;


@@ 611,7 669,7 @@ ipreassemble(IP *ip, int offset, Block *bp, Iphdr *ih)
				BKFG(*l)->flen -= ovlap;
				BKFG(*l)->foff += ovlap;
				/* move up ih hdrs */
				memmove((*l)->rp + ovlap, (*l)->rp, IPHDR);
				memmove((*l)->rp + ovlap, (*l)->rp, IP4HDR);
				(*l)->rp += ovlap;
				break;
			}


@@ 641,32 699,32 @@ ipreassemble(IP *ip, int offset, Block *bp, Iphdr *ih)
			for(bl = bl->next; bl; bl = bl->next) {
				fragsize = BKFG(bl)->flen;
				len += fragsize;
				bl->rp += IPHDR;
				bl->rp += IP4HDR;
				bl->wp = bl->rp + fragsize;
			}

			bl = f->blist;
			f->blist = nil;
			ipfragfree(ip, f);
			ipfragfree4(ip, f);
			ih = BLKIP(bl);
			hnputs(ih->length, len);
			qunlock(&ip->fraglock);
			qunlock(&ip->fraglock4);
			ip->stats[ReasmOKs]++;
			return bl;		
		}
		pktposn += BKFG(bl)->flen;
	}
	qunlock(&ip->fraglock);
	qunlock(&ip->fraglock4);
	return nil;
}

/*
 * ipfragfree - Free a list of fragments - assume hold fraglock
 * ipfragfree4 - Free a list of fragments - assume hold fraglock4
 */
void
ipfragfree(IP *ip, Fragment *frag)
ipfragfree4(IP *ip, Fragment4 *frag)
{
	Fragment *fl, **l;
	Fragment4 *fl, **l;

	if(frag->blist)
		freeblist(frag->blist);


@@ 675,7 733,7 @@ ipfragfree(IP *ip, Fragment *frag)
	frag->id = 0;
	frag->blist = nil;

	l = &ip->flisthead;
	l = &ip->flisthead4;
	for(fl = *l; fl; fl = fl->next) {
		if(fl == frag) {
			*l = frag->next;


@@ 684,29 742,29 @@ ipfragfree(IP *ip, Fragment *frag)
		l = &fl->next;
	}

	frag->next = ip->fragfree;
	ip->fragfree = frag;
	frag->next = ip->fragfree4;
	ip->fragfree4 = frag;

}

/*
 * ipfragallo - allocate a reassembly queue - assume hold fraglock
 * ipfragallo4 - allocate a reassembly queue - assume hold fraglock4
 */
Fragment *
ipfragallo(IP *ip)
Fragment4 *
ipfragallo4(IP *ip)
{
	Fragment *f;
	Fragment4 *f;

	while(ip->fragfree == nil) {
	while(ip->fragfree4 == nil) {
		/* free last entry on fraglist */
		for(f = ip->flisthead; f->next; f = f->next)
		for(f = ip->flisthead4; f->next; f = f->next)
			;
		ipfragfree(ip, f);
		ipfragfree4(ip, f);
	}
	f = ip->fragfree;
	ip->fragfree = f->next;
	f->next = ip->flisthead;
	ip->flisthead = f;
	f = ip->fragfree4;
	ip->fragfree4 = f->next;
	f->next = ip->flisthead4;
	ip->flisthead4 = f;
	f->age = msec + 30000;

	return f;


@@ 733,70 791,4 @@ ipcsum(uchar *addr)
	return (sum^0xffff);
}

enum
{
	Nmtucache=	128,
};

typedef struct MTUcache MTUcache;

struct MTUcache
{
	uchar	ip[IPaddrlen];
	ulong	mtu;
	ulong	ms;
};

static struct {
	Lock;
	MTUcache c[Nmtucache];
} mc;

void
update_mtucache(uchar *ip, ulong mtu)
{
	MTUcache *oldest, *p;

	if(mtu < 512)
		return;

	lock(&mc);
	oldest = mc.c;
	for(p = mc.c; p < &mc.c[Nmtucache]; p++){
		if(ipcmp(ip, p->ip) == 0){
			p->mtu = mtu;
			p->ms = msec;
			break;
		}
		if(p->ms < oldest->ms)
			oldest = p;
	}
	if(p == &mc.c[Nmtucache]){
		ipmove(oldest->ip, ip);
		oldest->mtu = mtu;
		oldest->ms = msec;
	}
	unlock(&mc);
}

ulong
restrict_mtu(uchar *ip, ulong mtu)
{
	MTUcache *p;

	lock(&mc);
	for(p = mc.c; p < &mc.c[Nmtucache]; p++){
		if(p->ms + 1000*10*60 < msec){
			memset(p->ip, 0, sizeof(p->ip));
			p->ms = 0;
		}
		if(ipcmp(ip, p->ip) == 0){
			if(p->mtu < mtu)
				mtu = p->mtu;
			break;
		}
	}
	unlock(&mc);

	return mtu;
}

M ip/ip.h => ip/ip.h +84 -14
@@ 24,6 24,11 @@ typedef struct	Arpent	Arpent;
typedef struct	Arp Arp;
typedef struct	Route	Route;

typedef struct	Routerparams	Routerparams;
typedef struct 	Hostparams	Hostparams;
typedef struct 	v6router	v6router;
typedef struct	v6params	v6params;

enum
{
	Addrlen=	64,


@@ 44,9 49,11 @@ enum
	/* ip versions */
	V4=		4,
	V6=		6,
	IP_VER4= 	0x40,
	IP_VER6=	0x60,

	/* 2^Lroot trees in the root table */
	Lroot	= 10,
	Lroot=		10,

	Maxpath =	64,
};


@@ 78,7 85,7 @@ struct Conv
	uint	ttl;			/* max time to live */
	uint	tos;			/* type of service */

	char	*owner		;	/* protections */
	char	*owner;			/* protections */
	int	perm;
	int	inuse;			/* opens of listen/data/ctl */
	int	length;


@@ 138,6 145,9 @@ struct Medium
	void	(*ares)(Fs*, int, uchar*, uchar*, int, int);	/* resolve */
	void	(*areg)(Ipifc*, uchar*);			/* register */

	/* v6 address generation */
	void	(*pref2addr)(uchar *pref, uchar *ea);

	int	unbindonclose;	/* if non-zero, unbind on last close */
};



@@ 148,6 158,12 @@ struct Iplifc
	uchar	mask[IPaddrlen];
	uchar	remote[IPaddrlen];
	uchar	net[IPaddrlen];
	uchar	tentative;	/* =1 => v6 dup disc on, =0 => confirmed unique */
	uchar	onlink;		/* =1 => onlink, =0 offlink. */
	uchar	autoflag;	/* v6 autonomous flag */
	long 	validlt;	/* v6 valid lifetime */
	long 	preflt;		/* v6 preferred lifetime */
	long	origint;	/* time when addr was added */
	Iplink	*link;		/* addresses linked to this lifc */
	Iplifc	*next;
};


@@ 164,6 180,25 @@ struct Iplink
	int	ref;
};

/* rfc 2461, pp.40--43. */

/* default values, one per stack */
struct Routerparams {
	int	mflag;
	int	oflag;
	int 	maxraint;
	int	minraint;
	int	linkmtu;
	int	reachtime;
	int	rxmitra;
	int	ttl;
	int	routerlt;	
};

struct Hostparams {
	int	rxmithost;
};

struct Ipifc
{
	RWlock;


@@ 189,6 224,11 @@ struct Ipifc

	ulong	in, out;	/* message statistics */
	ulong	inerr, outerr;	/* ... */

	uchar	sendra6;	/* == 1 => send router advs on this ifc	*/
	uchar	recvra6;	/* == 1 => recv router advs on this ifc */
	Routerparams rp;	/* router parameters as in RFC 2461, pp.40--43. 
					used only if node is router */
};

/*


@@ 267,6 307,7 @@ struct Proto
	void		*priv;
};


/*
 *  one per IP protocol stack
 */


@@ 284,6 325,7 @@ struct Fs
	IP	*ip;
	Ipselftab	*self;
	Arp	*arp;
	v6params	*v6p;

	Route	*v4root[1<<Lroot];	/* v4 routing forest */
	Route	*v6root[1<<Lroot];	/* v6 routing forest */


@@ 293,10 335,28 @@ struct Fs
	Ifclog	*ilog;

	char	ndb[1024];		/* an ndb entry for this interface */
	int	ndbvers;
	long	ndbmtime;
};

/* one per default router known to host */
struct v6router {
	uchar	inuse;
	Ipifc	*ifc;
	int	ifcid;
	uchar	routeraddr[IPaddrlen];
	long	ltorigin;
	Routerparams	rp;
};

struct v6params
{
	Routerparams	rp;		/* v6 params, one copy per node now */
	Hostparams	hp;
	v6router	v6rlist[3];	/* max 3 default routers, currently */
	int		cdrouter;	/* uses only v6rlist[cdrouter] if   */ 
					/* cdrouter >= 0. */
};


int	Fsconnected(Conv*, char*);
Conv*	Fsnewcall(Conv*, uchar*, ushort, uchar*, ushort);
int	Fspcolstats(char*, int);


@@ 310,7 370,6 @@ char*	Fsstdannounce(Conv*, char**, int);
char*	Fsstdbind(Conv*, char**, int);
ulong	scalednconv(void);
void	closeconv(Conv*);

/* 
 *  logging
 */


@@ 449,19 508,24 @@ struct Arpent
{
	uchar	ip[IPaddrlen];
	uchar	mac[MAClen];
	Medium	*type;		/* media type */
	Medium	*type;			/* media type */
	Arpent*	hash;
	Block*	hold;
	Block*	last;
	uint	time;
	uint	used;
	uchar	state;
	Arpent	*nextrxt;		/* re-transmit chain */
	uint	rxtat;
	uchar	rxtsrem;
	Ipifc	*ifc;
	uchar	ifcid;			/* must match ifc->id */
};

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


@@ 509,6 573,7 @@ extern Medium	tripmedium;
extern Medium*	ipfindmedium(char *name);
extern void	addipmedium(Medium *med);
extern int	ipforme(Fs*, uchar *addr);
extern int	iptentative(Fs*, uchar *addr);
extern int	ipisbm(uchar *);
extern int	ipismulticast(uchar *);
extern Ipifc*	findipifc(Fs*, uchar *remote, int type);


@@ 516,6 581,7 @@ extern void	findprimaryip(Fs*, uchar*);
extern void	findlocalip(Fs*, uchar *local, uchar *remote);
extern int	ipv4local(Ipifc *ifc, uchar *addr);
extern int	ipv6local(Ipifc *ifc, uchar *addr);
extern int	ipv6anylocal(Ipifc *ifc, uchar *addr);
extern Iplifc*	iplocalonifc(Ipifc *ifc, uchar *ip);
extern int	ipproxyifc(Fs *f, Ipifc *ifc, uchar *ip);
extern int	ipismulticast(uchar *ip);


@@ 528,27 594,30 @@ extern void	ipifcremroute(Fs*, 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);
extern char*	ipifcadd(Ipifc *ifc, char **argv, int argc, int tentative, Iplifc *lifcp);
extern long	ipselftabread(Fs*, char *a, ulong offset, int n);

extern char*	ipifcaddgate6(Fs *f, Ipifc *ifc, char**argv, int argc);
extern char*	ipifcaddpref6(Ipifc *ifc, char**argv, int argc);
extern void	ipsendra6(Fs *f, int on);
extern long	ipgateread6(Fs *f, char *cp, ulong offset, int n);
/*
 *  ip.c
 */
extern void	iprouting(Fs*, int);
extern void	closeifcconv(Ifcconv*);
extern void	icmpnoconv(Fs*, Block*);
extern void	icmpttlexceeded(Fs*, Ipifc*, Block*);
extern void	initfrag(IP*, int);
extern void	icmpttlexceeded(Fs*, uchar*, Block*);
extern ushort	ipcsum(uchar*);
extern void	ipiput(Fs*, Ipifc*, Block*);
extern void	ipoput(Fs*, Block*, int, int, int);
extern void	ipiput4(Fs*, Ipifc*, Block*);
extern void	ipiput6(Fs*, Ipifc*, Block*);
extern void	ipoput4(Fs*, Block*, int, int, int);
extern void	ipoput6(Fs*, Block*, int, int, int);
extern int	ipstats(Fs*, char*, int);
extern ushort	ptclbsum(uchar*, int);
extern ushort	ptclcsum(Block*, int, int);
extern void	ip_init(Fs*);
extern void	update_mtucache(uchar*, ulong);
extern ulong	restrict_mtu(uchar*, ulong);

/*
 * bootp.c
 */


@@ 573,3 642,4 @@ extern Chan*	chandial(char*, char*, char*, Chan**);
extern int	debug;
extern Fs	fs;
extern void	(*igmpreportfn)(Ipifc*, uchar*);


M ip/ipaux.c => ip/ipaux.c +181 -13
@@ 5,6 5,7 @@
#include	"fns.h"
#include	"../port/error.h"
#include	"ip.h"
#include  "ipv6.h"

/*
 *  well known IP addresses


@@ 33,6 34,7 @@ uchar IPallbits[IPaddrlen] = {
	0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff
};

uchar IPnoaddr[IPaddrlen];

/*


@@ 46,6 48,151 @@ uchar v4prefix[IPaddrlen] = {
};


char *v6hdrtypes[Maxhdrtype] =
{
	[HBH]		"HopbyHop",
	[ICMP]		"ICMP",
	[IGMP]		"IGMP",
	[GGP]		"GGP",
	[IPINIP]		"IP",
	[ST]		"ST",
	[TCP]		"TCP",
	[UDP]		"UDP",
	[ISO_TP4]	"ISO_TP4",
	[RH]		"Routinghdr",
	[FH]		"Fraghdr",
	[IDRP]		"IDRP",
	[RSVP]		"RSVP",
	[AH]		"Authhdr",
	[ESP]		"ESP",
	[ICMPv6]	"ICMPv6",
	[NNH]		"Nonexthdr",
	[ISO_IP]	"ISO_IP",
	[IGRP]		"IGRP",
	[OSPF]		"OSPF",
};

/*
 *  well known IPv6 addresses
 */
uchar v6Unspecified[IPaddrlen] = {
	0, 0, 0, 0,
	0, 0, 0, 0,
	0, 0, 0, 0,
	0, 0, 0, 0
};
uchar v6loopback[IPaddrlen] = {
	0, 0, 0, 0,
	0, 0, 0, 0,
	0, 0, 0, 0,
	0, 0, 0, 0x01
};
uchar v6linklocal[IPaddrlen] = {
	0xfe, 0x80, 0, 0,
	0, 0, 0, 0,
	0, 0, 0, 0,
	0, 0, 0, 0
};
uchar v6linklocalmask[IPaddrlen] = {
	0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff,
	0, 0, 0, 0,
	0, 0, 0, 0
};
int v6linklocalprefix = 8;
uchar v6sitelocal[IPaddrlen] = {
	0xfe, 0xc0, 0, 0,
	0, 0, 0, 0,
	0, 0, 0, 0,
	0, 0, 0, 0
};
uchar v6sitelocalmask[IPaddrlen] = {
	0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff,
	0, 0, 0, 0,
	0, 0, 0, 0
};
int v6sitelocalprefix = 6;
uchar v6glunicast[IPaddrlen] = {
	0x08, 0, 0, 0,
	0, 0, 0, 0,
	0, 0, 0, 0,
	0, 0, 0, 0
};
uchar v6multicast[IPaddrlen] = {
	0xff, 0, 0, 0,
	0, 0, 0, 0,
	0, 0, 0, 0,
	0, 0, 0, 0
};
uchar v6multicastmask[IPaddrlen] = {
	0xff, 0, 0, 0,
	0, 0, 0, 0,
	0, 0, 0, 0,
	0, 0, 0, 0
};
int v6multicastprefix = 1;
uchar v6allnodesN[IPaddrlen] = {
	0xff, 0x01, 0, 0,
	0, 0, 0, 0,
	0, 0, 0, 0,
	0, 0, 0, 0x01
};
uchar v6allnodesNmask[IPaddrlen] = {
	0xff, 0xff, 0, 0,
	0, 0, 0, 0,
	0, 0, 0, 0,
	0, 0, 0, 0
};
int v6allnodesprefix = 2;
uchar v6allnodesL[IPaddrlen] = {
	0xff, 0x02, 0, 0,
	0, 0, 0, 0,
	0, 0, 0, 0,
	0, 0, 0, 0x01
};
uchar v6allnodesLmask[IPaddrlen] = {
	0xff, 0xff, 0, 0,
	0, 0, 0, 0,
	0, 0, 0, 0,
	0, 0, 0, 0
};
int v6allnodesLprefix = 2;
uchar v6allroutersN[IPaddrlen] = {
	0xff, 0x01, 0, 0,
	0, 0, 0, 0,
	0, 0, 0, 0,
	0, 0, 0, 0x02
};
uchar v6allroutersL[IPaddrlen] = {
	0xff, 0x02, 0, 0,
	0, 0, 0, 0,
	0, 0, 0, 0,
	0, 0, 0, 0x02
};
uchar v6allroutersS[IPaddrlen] = {
	0xff, 0x05, 0, 0,
	0, 0, 0, 0,
	0, 0, 0, 0,
	0, 0, 0, 0x02
};
uchar v6solicitednode[IPaddrlen] = {
	0xff, 0x02, 0, 0,
	0, 0, 0, 0,
	0, 0, 0, 0x01,
	0xff, 0, 0, 0
};
uchar v6solicitednodemask[IPaddrlen] = {
	0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff,
	0xff, 0x0, 0x0, 0x0
};
int v6solicitednodeprefix = 13;




ushort
ptclcsum(Block *bp, int offset, int len)
{


@@ 135,16 282,13 @@ eipfmt(Fmt *f)
	switch(f->r) {
	case 'E':		/* Ethernet address */
		p = va_arg(f->args, uchar*);
		snprint(buf, sizeof buf, efmt, p[0], p[1], p[2], p[3], p[4], p[5]);
		return fmtstrcpy(f, buf);
		return fmtprint(f, efmt, p[0], p[1], p[2], p[3], p[4], p[5]);

	case 'I':		/* Ip address */
		p = va_arg(f->args, uchar*);
common:
		if(memcmp(p, v4prefix, 12) == 0){
			snprint(buf, sizeof buf, ifmt, p[12], p[13], p[14], p[15]);
			return fmtstrcpy(f, buf);
		}
		if(memcmp(p, v4prefix, 12) == 0)
			return fmtprint(f, ifmt, p[12], p[13], p[14], p[15]);

		/* find longest elision */
		eln = eli = -1;


@@ 182,8 326,7 @@ common:

	case 'V':		/* v4 ip address */
		p = va_arg(f->args, uchar*);
		snprint(buf, sizeof buf, ifmt, p[0], p[1], p[2], p[3]);
		return fmtstrcpy(f, buf);
		return fmtprint(f, ifmt, p[0], p[1], p[2], p[3]);

	case 'M':		/* ip mask */
		p = va_arg(f->args, uchar*);


@@ 203,10 346,10 @@ common:
			n = 8*16;

		/* got one, use /xx format */
		snprint(buf, sizeof buf, "/%d", n);
		return fmtstrcpy(f, buf);
		return fmtprint(f, "/%d", n);

	}
	return fmtstrcpy(f, "(eipfmt)");
	return fmtprint(f, "(eipfmt)");
}

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


@@ 230,7 373,7 @@ v4parseip(uchar *to, char *from)
			to[3] = to[2];
			to[2] = to[1];
			to[1] = 0;
		} else if (i == 2){
		} else if(i == 2){
			to[3] = to[1];
			to[1] = 0;
		}


@@ 395,9 538,34 @@ uchar classmask[4][16] = {
uchar*
defmask(uchar *ip)
{
	return classmask[ip[IPv4off]>>6];
	if(isv4(ip))
		return classmask[ip[IPv4off]>>6];
	else {
		if(ipcmp(ip, v6loopback) == 0)
			return IPallbits;
		else if(memcmp(ip, v6linklocal, v6linklocalprefix) == 0)
			return v6linklocalmask;
		else if(memcmp(ip, v6sitelocal, v6sitelocalprefix) == 0)
			return v6sitelocalmask;
		else if(memcmp(ip, v6solicitednode, v6solicitednodeprefix) == 0)
			return v6solicitednodemask;
		else if(memcmp(ip, v6multicast, v6multicastprefix) == 0)
			return v6multicastmask;
		return IPallbits;
	}
}

void
ipv62smcast(uchar *smcast, uchar *a)
{
	assert(IPaddrlen == 16);
	memmove(smcast, v6solicitednode, IPaddrlen);
	smcast[13] = a[13];
	smcast[14] = a[14];
	smcast[15] = a[15];
}


/*
 *  parse a hex mac address
 */

M ip/ipifc.c => ip/ipifc.c +634 -63
@@ 6,6 6,7 @@
#include "../port/error.h"

#include "ip.h"
#include "ipv6.h"

#define DPRINT if(0)print



@@ 138,6 139,17 @@ ipifcbind(Conv *c, char **argv, int argc)
	ifc->maxmtu = ifc->m->maxmtu;
	if(ifc->m->unbindonclose == 0)
		ifc->conv->inuse++;

	ifc->rp.mflag	= 0;		// default not managed
	ifc->rp.oflag	= 0;
	ifc->rp.maxraint	= 600000;	// millisecs
	ifc->rp.minraint	= 200000;
	ifc->rp.linkmtu	= 0;		// no mtu sent
	ifc->rp.reachtime	= 0;
	ifc->rp.rxmitra	= 0;
	ifc->rp.ttl	= MAXTTL;
	ifc->rp.routerlt	= 3*(ifc->rp.maxraint);	

	ifc->ifcid++;

	wunlock(ifc);


@@ 199,6 211,13 @@ ipifcunbind(Ipifc *ifc)
	return nil;
}



char *sfixedformat = "device %-12s maxmtu %-5d sendra %-1d recvra %-1d mflag %-1d oflag %-1d maxraint %-10d minraint %-10d linkmtu %-10d reachtime %-10d rxmitra %-10d ttl %-10d routerlt %-10d pktin %-7lud pktout %-7lud errin %-7lud errout %-7lud\n";

char *slineformat = "	%-40.40I %-10.10M %-40.40I %-12lud %-12lud\n";


static int
ipifcstate(Conv *c, char *state, int n)
{


@@ 208,14 227,18 @@ ipifcstate(Conv *c, char *state, int n)

	ifc = (Ipifc*)c->ptcl;

	m = snprint(state, n, "%-12s %-5d", ifc->dev, ifc->maxmtu);
	m = snprint(state, n, sfixedformat, 
		ifc->dev, ifc->maxmtu, ifc->sendra6, ifc->recvra6,
		ifc->rp.mflag, ifc->rp.oflag, ifc->rp.maxraint,
		ifc->rp.minraint, ifc->rp.linkmtu, ifc->rp.reachtime,
		ifc->rp.rxmitra, ifc->rp.ttl, ifc->rp.routerlt,
		ifc->in, ifc->out, ifc->inerr, ifc->outerr);

	rlock(ifc);
	for(lifc = ifc->lifc; lifc && n > m; lifc = lifc->next)
		m += snprint(state+m, n - m,
			" %-20.20I %-20.20M %-20.20I %-7lud %-7lud %-7lud %-7lud\n",
				lifc->local, lifc->mask, lifc->remote,
				ifc->in, ifc->out, ifc->inerr, ifc->outerr);
		m += snprint(state+m, n - m, slineformat,
			lifc->local, lifc->mask, lifc->remote,
			lifc->validlt, lifc->preflt);
	if(ifc->lifc == nil)
		m += snprint(state+m, n - m, "\n");
	runlock(ifc);


@@ 236,9 259,9 @@ ipifclocal(Conv *c, char *state, int n)

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


@@ 331,13 354,14 @@ ipifcsetmtu(Ipifc *ifc, char **argv, int argc)
 *  add an address to an interface.
 */
char*
ipifcadd(Ipifc *ifc, char **argv, int argc)
ipifcadd(Ipifc *ifc, char **argv, int argc, int tentative, Iplifc *lifcp)
{
	uchar ip[IPaddrlen], mask[IPaddrlen], rem[IPaddrlen];
	uchar bcast[IPaddrlen], net[IPaddrlen];
	Iplifc *lifc, **l;
	int i, type, mtu;
	Fs *f;
	int sendnbrdisc = 0;

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


@@ 380,13 404,25 @@ ipifcadd(Ipifc *ifc, char **argv, int argc)
		return Ebadarg;
		break;
	}

	if(isv4(ip))
		tentative = 0;
	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)
	for(lifc = ifc->lifc; lifc; lifc = lifc->next) {
		if(ipcmp(lifc->local, ip) == 0) {
			if(lifc->tentative != tentative) 
				lifc->tentative = tentative;
			if(lifcp != nil) {
				lifc->onlink = lifcp->onlink;
				lifc->autoflag = lifcp->autoflag;
				lifc->validlt = lifcp->validlt;
				lifc->preflt = lifcp->preflt;
				lifc->origint = lifcp->origint;
			}
			goto out;
		}
	}

	/* add the address to the list of logical ifc's for this ifc */
	lifc = smalloc(sizeof(Iplifc));


@@ 394,18 430,37 @@ ipifcadd(Ipifc *ifc, char **argv, int argc)
	ipmove(lifc->mask, mask);
	ipmove(lifc->remote, rem);
	ipmove(lifc->net, net);
	lifc->tentative = tentative;
	if(lifcp != nil) {
		lifc->onlink = lifcp->onlink;
		lifc->autoflag = lifcp->autoflag;
		lifc->validlt = lifcp->validlt;
		lifc->preflt = lifcp->preflt;
		lifc->origint = lifcp->origint;
	}
	else {		// default values
		lifc->onlink = 1;
		lifc->autoflag = 1;
		lifc->validlt = 0xffffffff;
		lifc->preflt = 0xffffffff;
		lifc->origint = msec / 10^3;
	}
	lifc->next = nil;

	for(l = &ifc->lifc; *l; l = &(*l)->next)
		;
	*l = lifc;

	/* add a route for the local network */
	/* check for point-to-point interface */
	if(ipcmp(ip, v6loopback))  /* skip v6 loopback, it's a special address */
	if(ipcmp(mask, IPallbits) == 0){
		/* point to point networks are a hack */
		if(ipcmp(ip, rem) == 0)
			findprimaryip(f, lifc->local);
		type |= Rptpt;
	}

	/* add local routes */
	if(isv4(ip))
		v4addroute(f, tifc, rem+IPv4off, mask+IPv4off, rem+IPv4off, type);
	else


@@ 418,36 473,63 @@ ipifcadd(Ipifc *ifc, char **argv, int argc)
		goto out;
	}

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

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

	/* add old subnet directed broadcast addresses to the self cache */
	for(i = 0; i < IPaddrlen; i++)
		bcast[i] = (ip[i] & mask[i]) & mask[i];
	addselfcache(f, ifc, lifc, bcast, Rbcast);
		/* add network directed broadcast address to the self cache */
		memmove(mask, defmask(ip), IPaddrlen);
		for(i = 0; i < IPaddrlen; i++)
			bcast[i] = (ip[i] & mask[i]) | ~mask[i];
		addselfcache(f, 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(f, ifc, lifc, bcast, Rbcast);
		/* add network directed network address to the self cache */
		memmove(mask, defmask(ip), IPaddrlen);
		for(i = 0; i < IPaddrlen; i++)
			bcast[i] = (ip[i] & mask[i]) & mask[i];
		addselfcache(f, ifc, lifc, bcast, Rbcast);
		
		addselfcache(f, ifc, lifc, IPv4bcast, Rbcast);
	} 
	else {
		if(ipcmp(ip, v6loopback) == 0) {
			/* add node-local mcast address */
			addselfcache(f, ifc, lifc, v6allnodesN, Rmulti);

			/* add route for all node multicast */
			v6addroute(f, tifc, v6allnodesN, v6allnodesNmask, v6allnodesN, Rmulti);
		}

	/* add old 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(f, ifc, lifc, bcast, Rbcast);
		else if(memcmp(ip, v6linklocal, v6linklocalprefix) == 0) {
			/* add link-local mcast address */
			addselfcache(f, ifc, lifc, v6allnodesL, Rmulti);
			
			/* add route for all link multicast */
			v6addroute(f, tifc, v6allnodesL, v6allnodesLmask, v6allnodesL, Rmulti);
			
			/* add solicited-node multicast address */
			ipv62smcast(bcast, ip);
			addselfcache(f, ifc, lifc, bcast, Rmulti);
		}

	addselfcache(f, ifc, lifc, IPv4bcast, Rbcast);
		sendnbrdisc = 1;
	}

	/* register the address on this network for address resolution */
	if(ifc->m->areg != nil)
	if(isv4(ip) && ifc->m->areg != nil)
		(*ifc->m->areg)(ifc, ip);

out:
	wunlock(ifc);
	if(tentative && sendnbrdisc) 
		icmpns(f, 0, SRC_UNSPEC, ip, TARG_MULTI, ifc->mac);
	return nil;
}



@@ 505,8 587,6 @@ ipifcrem(Ipifc *ifc, char **argv, int argc, int dolock)
		return "address not on this interface";
	}

	ifc->ifcid++;

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


@@ 514,8 594,15 @@ ipifcrem(Ipifc *ifc, char **argv, int argc, int dolock)
	/* remove the route for this logical interface */
	if(isv4(ip))
		v4delroute(f, lifc->remote+IPv4off, lifc->mask+IPv4off, 1);
	else
	else {
		v6delroute(f, lifc->remote, lifc->mask, 1);
		if(ipcmp(ip, v6loopback) == 0)
			/* remove route for all node multicast */
			v6delroute(f, v6allnodesN, v6allnodesNmask, 1);
		else if(memcmp(ip, v6linklocal, v6linklocalprefix) == 0)
			/* remove route for all link multicast */
			v6delroute(f, v6allnodesL, v6allnodesLmask, 1);
	}

	free(lifc);



@@ 525,7 612,7 @@ ipifcrem(Ipifc *ifc, char **argv, int argc, int dolock)
}

/*
 * distrbute routes to active interfaces like the
 * distribute routes to active interfaces like the
 * TRIP linecards
 */
void


@@ 606,7 693,7 @@ ipifcconnect(Conv* c, char **argv, int argc)
	wunlock(ifc);
	poperror();

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



@@ 615,6 702,81 @@ ipifcconnect(Conv* c, char **argv, int argc)
	return nil;
}

char*
ipifcsetpar6(Ipifc *ifc, char **argv, int argc)
{
	int i, argsleft, vmax = ifc->rp.maxraint, vmin = ifc->rp.minraint;

	argsleft = argc - 1;
	i = 1;

	if(argsleft % 2 != 0)
		return Ebadarg;

	while (argsleft > 1) {
		if(strcmp(argv[i],"recvra")==0) 
			ifc->recvra6 = (atoi(argv[i+1]) != 0);
		else if(strcmp(argv[i],"sendra")==0) 
			ifc->sendra6 = (atoi(argv[i+1]) != 0);
		else if(strcmp(argv[i],"mflag")==0) 
			ifc->rp.mflag = (atoi(argv[i+1]) != 0);
		else if(strcmp(argv[i],"oflag")==0) 
			ifc->rp.oflag = (atoi(argv[i+1]) != 0);
		else if(strcmp(argv[i],"maxraint")==0)
			ifc->rp.maxraint = atoi(argv[i+1]);
		else if(strcmp(argv[i],"minraint")==0)
			ifc->rp.minraint = atoi(argv[i+1]);
		else if(strcmp(argv[i],"linkmtu")==0) 
			ifc->rp.linkmtu = atoi(argv[i+1]);
		else if(strcmp(argv[i],"reachtime")==0) 
			ifc->rp.reachtime = atoi(argv[i+1]);
		else if(strcmp(argv[i],"rxmitra")==0) 
			ifc->rp.rxmitra = atoi(argv[i+1]);
		else if(strcmp(argv[i],"ttl")==0) 
			ifc->rp.ttl = atoi(argv[i+1]);
		else if(strcmp(argv[i],"routerlt")==0) 
			ifc->rp.routerlt = atoi(argv[i+1]);
		else 
			return Ebadarg;	

		argsleft -= 2;
		i += 2;
	}

	// consistency check
	if(ifc->rp.maxraint < ifc->rp.minraint) {
		ifc->rp.maxraint = vmax;
		ifc->rp.minraint = vmin;
		return Ebadarg;
	}

	return nil;
}

char*
ipifcsendra6(Ipifc *ifc, char **argv, int argc)
{
	int i;
	
	i = 0;
	if(argc > 1)
		i = atoi(argv[1]);
	ifc->sendra6 = (i!=0);
	return nil;
}

char*
ipifcrecvra6(Ipifc *ifc, char **argv, int argc)
{
	int i;
	
	i = 0;
	if(argc > 1) 
		i = atoi(argv[1]);
	ifc->recvra6 = (i!=0);	
	return nil;
}

/*
 *  non-standard control messages.
 *  called with c->car locked.


@@ 627,7 789,9 @@ ipifcctl(Conv* c, char**argv, int argc)

	ifc = (Ipifc*)c->ptcl;
	if(strcmp(argv[0], "add") == 0)
		return ipifcadd(ifc, argv, argc);
		return ipifcadd(ifc, argv, argc, 0, nil);
	else if(strcmp(argv[0], "try") == 0)
		return ipifcadd(ifc, argv, argc, 1, nil);
	else if(strcmp(argv[0], "remove") == 0)
		return ipifcrem(ifc, argv, argc, 1);
	else if(strcmp(argv[0], "unbind") == 0)


@@ 641,13 805,24 @@ ipifcctl(Conv* c, char**argv, int argc)
	else if(strcmp(argv[0], "reassemble") == 0){
		ifc->reassemble = 1;
		return nil;
	} else if(strcmp(argv[0], "iprouting") == 0){
	}
	else if(strcmp(argv[0], "iprouting") == 0){
		i = 1;
		if(argc > 1)
			i = atoi(argv[1]);
		iprouting(c->p->f, i);
		return nil;
	}
	else if(strcmp(argv[0], "gate6") == 0)
		return ipifcaddgate6(c->p->f, ifc, argv, argc);
	else if(strcmp(argv[0], "addpref6") == 0)
		return ipifcaddpref6(ifc, argv, argc);
	else if(strcmp(argv[0], "setpar6") == 0)
		return ipifcsetpar6(ifc, argv, argc);
	else if(strcmp(argv[0], "sendra6") == 0) 
		return ipifcsendra6(ifc, argv, argc);
	else if(strcmp(argv[0], "recvra6") == 0)
		return ipifcrecvra6(ifc, argv, argc);
	return "unsupported ctl";
}



@@ 882,6 1057,10 @@ out:
}

static char *stformat = "%-32.32I %2.2d %4.4s\n";
enum
{
	Nstformat= 41,
};

long
ipselftabread(Fs *f, char *cp, ulong offset, int n)


@@ 911,6 1090,19 @@ ipselftabread(Fs *f, char *cp, ulong offset, int n)
	return m;
}

int
iptentative(Fs *f, uchar *addr) 
{
 	Ipself *p;

	p = f->self->hash[hashipa(addr)];
	for(; p; p = p->next){
		if(ipcmp(addr, p->a) == 0) {
			return p->link->lifc->tentative;
		}
	}
	return 0;
}

/*
 *  returns


@@ 950,14 1142,16 @@ findipifc(Fs *f, uchar *remote, int type)
	uchar gnet[IPaddrlen];
	uchar xmask[IPaddrlen];

	x = nil;
	x = nil; memset(xmask, 0, IPaddrlen);

	/* find most specific match */
	e = &f->ipifc->conv[f->ipifc->nc];
	for(cp = f->ipifc->conv; cp < e; 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){


@@ 985,8 1179,65 @@ findipifc(Fs *f, uchar *remote, int type)
	return nil;
}

enum {
	unknownv6,
	multicastv6,
	unspecifiedv6,
	linklocalv6,
	sitelocalv6,
	globalv6,
};

int
v6addrtype(uchar *addr)
{
	if(isv6global(addr))
		return globalv6;
	if(islinklocal(addr))
		return linklocalv6;
	if(isv6mcast(addr))
		return multicastv6;
	if(issitelocal(addr))
		return sitelocalv6;
	return unknownv6;
}

#define v6addrcurr(lifc) (( (lifc)->origint + (lifc)->preflt >= (msec/10^3) ) || ( (lifc)->preflt == 0xffffffff ))

void
findprimaryip6(Fs *f, uchar *local)
{
	Conv **cp, **e;
	Ipifc *ifc;
	Iplifc *lifc;
	int atype, atypel;

	ipmove(local, v6Unspecified);
	atype = unspecifiedv6;

	/* find "best" (global > sitelocal > link local > unspecified)
	 * local address; address must be current */

	e = &f->ipifc->conv[f->ipifc->nc];
	for(cp = f->ipifc->conv; cp < e; cp++){
		if(*cp == 0)
			continue;
		ifc = (Ipifc*)(*cp)->ptcl;
		for(lifc = ifc->lifc; lifc; lifc = lifc->next){
			atypel = v6addrtype(lifc->local);
			if(atypel > atype) 
			if(v6addrcurr(lifc)) {
				ipmove(local, lifc->local);
				atype = atypel;
				if(atype == globalv6)
					return;
			}
		}
	}
}

/*
 *  returns first ip address configured
 *  returns first ip address configured 
 */
void
findprimaryip(Fs *f, uchar *local)


@@ 1020,28 1271,57 @@ findlocalip(Fs *f, uchar *local, uchar *remote)
	Route *r;
	uchar gate[IPaddrlen];
	uchar gnet[IPaddrlen];
	int version;
	int atype = unspecifiedv6, atypel = unknownv6;

	USED(atype);
	USED(atypel);
	qlock(f->ipifc);
	r = v6lookup(f, remote);
 	version = (memcmp(remote, v4prefix, IPv4off) == 0) ? 4 : 6;
	
	if(r != nil){
		ifc = r->ifc;
		if(r->type & Rv4)
			v4tov6(gate, r->v4.gate);
		else
		else {
			ipmove(gate, r->v6.gate);
			ipmove(local, v6Unspecified);
		}

		/* 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;
		if(version == 4) {
			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;
				}
			}
		}
		else {
			for(lifc = ifc->lifc; lifc; lifc = lifc->next){
				atypel = v6addrtype(lifc->local);
				maskip(gate, lifc->mask, gnet);
				if(ipcmp(gnet, lifc->net) == 0)
				if(atypel > atype)
				if(v6addrcurr(lifc)) {
					ipmove(local, lifc->local);
					atype = atypel;
					if(atype == globalv6)
						break;
				}
			}
			if(atype > unspecifiedv6)
				goto out;
		}
	}

	findprimaryip(f, local);
	if(version == 4)
		findprimaryip(f, local);
	else
		findprimaryip6(f, local);


out:
	qunlock(f->ipifc);


@@ 1073,6 1353,20 @@ ipv6local(Ipifc *ifc, uchar *addr)
	Iplifc *lifc;

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

int
ipv6anylocal(Ipifc *ifc, uchar *addr)
{
	Iplifc *lifc;

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


@@ 1269,26 1563,303 @@ ipifcregisterproxy(Fs *f, Ipifc *ifc, uchar *ip)

	/* register the address on any network that will proxy for us */
	e = &f->ipifc->conv[f->ipifc->nc];
	for(cp = f->ipifc->conv; cp < e; cp++){
		if(*cp == nil)
			continue;
		nifc = (Ipifc*)(*cp)->ptcl;
		if(nifc == ifc)
			continue;

		rlock(nifc);
		m = nifc->m;
		if(m == nil || m->areg == nil){
	if(!isv4(ip)) { // V6
		for(cp = f->ipifc->conv; cp < e; cp++){
			if(*cp == nil)
				continue;
			nifc = (Ipifc*)(*cp)->ptcl;
			if(nifc == ifc)
				continue;
	
			rlock(nifc);
			m = nifc->m;
			if(m == nil || m->addmulti == nil) {
				runlock(nifc);
				continue;
			}
			for(lifc = nifc->lifc; lifc; lifc = lifc->next){
				maskip(ip, lifc->mask, net);
				if(ipcmp(net, lifc->remote) == 0) { /* add solicited-node multicast address */
					ipv62smcast(net, ip);
					addselfcache(f, nifc, lifc, net, Rmulti);
					arpenter(f, V6, ip, nifc->mac, 6, 0);
					//(*m->addmulti)(nifc, net, ip);
					break;
				}
			}
			runlock(nifc);
		}
		return;
	}
	else { // V4
		for(cp = f->ipifc->conv; cp < e; cp++){
			if(*cp == nil)
				continue;
			nifc = (Ipifc*)(*cp)->ptcl;
			if(nifc == ifc)
				continue;
	
			rlock(nifc);
			m = nifc->m;
			if(m == nil || m->areg == nil){
				runlock(nifc);
				continue;
			}
			for(lifc = nifc->lifc; lifc; lifc = lifc->next){
				maskip(ip, lifc->mask, net);
				if(ipcmp(net, lifc->remote) == 0){
					(*m->areg)(nifc, ip);
					break;
				}
			}
			runlock(nifc);
			continue;
		}
		for(lifc = nifc->lifc; lifc; lifc = lifc->next){
			maskip(ip, lifc->mask, net);
			if(ipcmp(net, lifc->remote) == 0){
				(*m->areg)(nifc, ip);
	}
}


// added for new v6 mesg types
static void
adddefroute6(Fs *f, uchar *gate, int force)
{
	Route *r;

	r = v6lookup(f, v6Unspecified);
	if(r!=nil) 
	if(!(force) && (strcmp(r->tag,"ra")!=0))	// route entries generated
		return;			// by all other means take
					// precedence over router annc

	v6delroute(f, v6Unspecified, v6Unspecified, 1);
	v6addroute(f, "ra", v6Unspecified, v6Unspecified, gate, 0);
}

enum
{
	Ngates = 3,
};

char*
ipifcaddgate6(Fs *f, Ipifc *ifc, char**argv, int argc)
{
	v6router	*r = f->v6p->v6rlist;
	uchar	routeraddr[IPaddrlen];
	int	mflag = f->v6p->rp.mflag;
	int	oflag = f->v6p->rp.oflag;
	int	reachtime = f->v6p->rp.reachtime;
	int	rxmitra = f->v6p->rp.rxmitra;
	int	ttl = MAXTTL;
	int	routerlt = f->v6p->rp.routerlt;
	int	force;		// force == 1 forces argv[1] to
				// be the default router.
	int	i, j;
	int	found = 0;

	if((argc<3)||(argc>9)) 
		return Ebadarg;
	if( (parseip(routeraddr, argv[1])!=6) || !(islinklocal(routeraddr)) ) 
		return Ebadarg;

	force = (atoi(argv[2])!=0);

	switch(argc){
	case 9:
		rxmitra = atoi(argv[8]);
		/* fall through */
	case 8:
		reachtime = atoi(argv[7]);
		/* fall through */
	case 7:
		routerlt =  atoi(argv[6]);
		/* fall through */
	case 6:
		oflag = atoi(argv[5]);
		/* fall through */
	case 5:
		mflag = atoi(argv[4]);
		/* fall through */
	case 4:
		ttl = atoi(argv[3]);
		/* fall through */
	}

	if((force) && (routerlt < 0))
		return Ebadarg;
	if((ttl < 0) || (255 < ttl))
		return Ebadarg;

	j = -1;
	for(i=0; i<Ngates; i++) {
		if(r[i].inuse) {
			if((ipcmp(routeraddr, r[i].routeraddr)==0) &&
			(r[i].ifc==ifc)) {
				j = i;
				found = 1;
				break;
			}
		}
		runlock(nifc);
		else {
			j = i;
		}
	}

	if(found==0) {
		if(routerlt <= 0)
			return nil;
		else if((force) && (j<0)) {
			j = f->v6p->cdrouter;
			f->v6p->cdrouter = -1;
		}
	}

	// assert((found && (j>=0))||(!found && routerlt>0));
	if(routerlt > 0) {
		memset(&r[j], 0, sizeof(v6router));
		r[j].inuse = 1;
		r[j].ifc = ifc;
		r[j].ifcid = ifc->ifcid;
		ipmove(r[j].routeraddr, routeraddr);
		r[j].ltorigin = msec / 10^3;
		r[j].rp.mflag = (mflag!=0);
		r[j].rp.oflag = (oflag!=0);
		r[j].rp.rxmitra = rxmitra;
		r[j].rp.reachtime = reachtime;
		r[j].rp.routerlt = routerlt;
		r[j].rp.ttl = ttl;

		if((f->v6p->cdrouter < 0) || (force==1)) {
			f->v6p->cdrouter = j;
			adddefroute6(f, routeraddr, force);
		}
	}
	else if(j >= 0) {		// remove router
		r[j].inuse = 0;
		if(f->v6p->cdrouter==j) {
			f->v6p->cdrouter = -1;
			v6delroute(f, v6Unspecified, v6Unspecified, 1);
			for(i=0; i<Ngates; i++) {
				if(r[i].inuse) {
					f->v6p->cdrouter = i;
					adddefroute6(f, r[i].routeraddr, 1);
					break;
				}
			}
		}
	}
	return nil;
}

char*
ipifcaddpref6(Ipifc *ifc, char**argv, int argc)
{
	uchar	onlink = 1;
	uchar	autoflag = 1;
	long 	validlt = 0xffffffff;
	long 	preflt = 0xffffffff;
	long	origint = msec / 10^3;
	uchar	prefix[IPaddrlen];
	int	plen = 64;
	Iplifc	*lifc;
	char	addr[40];
	char	*params[3];

	switch(argc) {
	case 7:
		preflt = atoi(argv[6]);
		/* fall through */
	case 6:
		validlt = atoi(argv[5]);
		/* fall through */
	case 5:
		autoflag =  atoi(argv[4]);
		/* fall through */
	case 4:
		onlink = atoi(argv[3]);
		/* fall through */
	case 3:
		plen = atoi(argv[2]);
	case 2:
		break;
	default:
		return Ebadarg;
	}

	if((parseip(prefix, argv[1])!=6) ||
	 	(validlt < preflt) ||
		(plen > 64) ||
		(islinklocal(prefix))
	)
		return Ebadarg;

	lifc = smalloc(sizeof(Iplifc));
	lifc->onlink = (onlink!=0);
	lifc->autoflag = (autoflag!=0);
	lifc->validlt = validlt;
	lifc->preflt = preflt;
	lifc->origint = origint;

	if(ifc->m->pref2addr!=nil) 
		ifc->m->pref2addr(prefix, ifc->mac);
	else
		return Ebadarg;
	
	sprint(addr, "%I", prefix);
	params[0] = "add";
	params[1] = addr;
	params[2] = "/64";

	return ipifcadd(ifc, params, 3, 0, lifc);
}

static char *gateformat = "%-1.1s %-40.40I %20ld %10d %-40.40s\n";
enum
{
	Ngateformat= 116,
};

static char *rtrstat[] =
{
[0]	"*",
[1]	"-",
};

/* line corresponding to current default router, if in f->v6p->v6rlist, 
   starts with a "*", others with a "-". */

long
ipgateread6(Fs *f, char *cp, ulong offset, int n)
{
	v6router	*r = f->v6p->v6rlist;
	int	i, j, k, l;
	long	m;


	if(offset % Ngateformat)
		return 0;

	offset = offset/Ngateformat;
	n = n/Ngateformat;

	i = f->v6p->cdrouter;
	k = i;
	if((i<0)||(i>2))
		i = 0;
	m = 0;
	for(j = 0; (n > 0) && (j < Ngates) ; j++){
		if(offset > 0){
			offset--;
			i = (i+1) % Ngates;
			continue;
		}
		n--;
		if(r[i].inuse) {
			l = (i==k) ? 0 : 1;				
			m += sprint(cp + m, gateformat, 
				rtrstat[l], r[i].routeraddr, r[i].ltorigin,  
				r[i].rp.routerlt, r[i].ifc->dev);
		}
		i = (i+1) % Ngates;
	}
	return m;
}

M ip/ipmux.c => ip/ipmux.c +76 -37
@@ 6,19 6,19 @@
#include "../port/error.h"

#include "ip.h"

#define DPRINT if(0)print

typedef struct Iphdr		Iphdr;
typedef struct Ipmuxrock	Ipmuxrock;
typedef struct Ipmux		Ipmux;
typedef struct Ipmuxrock  Ipmuxrock;
typedef struct Ipmux      Ipmux;
typedef struct Ip4hdr     Ip4hdr;
typedef struct Ip6hdr     Ip6hdr;

enum
{
	IPHDR		= 20,		/* sizeof(Iphdr) */
	IPHDR		= 20,		/* sizeof(Ip4hdr) */
};

struct Iphdr
struct Ip4hdr
{
	uchar	vihl;		/* Version and header length */
	uchar	tos;		/* Type of service */


@@ 32,12 32,24 @@ struct Iphdr
	uchar	dst[4];		/* IP destination */
	uchar	data[1];	/* start of data */
};
Iphdr *ipoff = 0;
Ip4hdr *ipoff = 0;

struct Ip6hdr
{
  uchar vcf[4];  /* version, class label, and flow label */ 
  uchar ploadlen[2];  /* payload length */
  uchar proto; /* next header, i.e. proto */
  uchar ttl;   /* hop limit, i.e. ttl */
  uchar src[16]; /* IP source */
  uchar dst[16]; /* IP destination */
};


enum
{
	Tproto,
	Tdata,
  Tiph,
	Tdst,
	Tsrc,
	Tifc,


@@ 57,6 69,7 @@ char *ftname[] =
{
[Tproto]	"proto",
[Tdata]		"data",
[Tiph]	  "iph",
[Tdst]		"dst",
[Tsrc]		"src",
[Tifc]		"ifc",


@@ 69,12 82,13 @@ struct Ipmux
{
	Ipmux	*yes;
	Ipmux	*no;
	uchar	type;		/* type of field (Txxxx) */
	uchar	ctype;		/* tupe of comparison (Cxxxx) */
	uchar	len;		/* length in bytes of item to compare */
	uchar	n;		/* number of items val points to */
	short	off;		/* offset of comparison */
	short	eoff;		/* end offset of comparison */
	uchar	type;		 /* type of field (Txxxx) */
	uchar	ctype;   /* tupe of comparison (Cxxxx) */
	uchar	len;		 /* length in bytes of item to compare */
	uchar	n;		   /* number of items val points to */
	short	off;		 /* offset of comparison */
	short	eoff;		 /* end offset of comparison */
  uchar skiphdr; /* should offset start after ip header */
	uchar	*val;
	uchar	*mask;
	uchar	*e;		/* val + n*len */


@@ 148,9 162,15 @@ parseop(char **pp)
		len = 1;
		p += 5;
	}
	else if(strncmp(p, "data", 4) == 0){
		type = Tdata;
		p += 4;
	else if(strncmp(p, "data", 4) == 0 || strncmp(p, "iph", 3) == 0){
    if(strncmp(p, "data", 4) == 0) {
		  type = Tdata;
      p += 4;
    }
    else {
      type = Tiph;
	    p += 3;
    }
		p = skipwhite(p);
		if(*p != '[')
			return nil;


@@ 173,9 193,8 @@ parseop(char **pp)
			return nil;
		p++;
		len = end - off + 1;
		off += (ulong)(ipoff->data);
	}
	else 
	else
		return nil;

	f = smalloc(sizeof(*f));


@@ 186,6 205,10 @@ parseop(char **pp)
	f->mask = nil;
	f->n = 1;
	f->ref = 1;
  if(type == Tdata)
    f->skiphdr = 1;
  else
    f->skiphdr = 0;

	return f;	
}


@@ 250,6 273,7 @@ parsemux(char *p)
			v4parseip(f->mask, mask);
			break;
		case Tdata:
    case Tiph:
			f->mask = smalloc(f->len);
			parseval(f->mask, mask, f->len);
			break;


@@ 278,6 302,7 @@ parsemux(char *p)
			break;
		case Tproto:
		case Tdata:
    case Tiph:
			parseval(v, vals[n], f->len);
			break;
		}


@@ 333,7 358,8 @@ ipmuxcmp(Ipmux *a, Ipmux *b)
		return n;

	/* compare offsets, call earlier ones more specific */
	n = a->off - b->off;
	n = (a->off+((int)a->skiphdr)*(ulong)ipoff->data) - 
      (b->off+((int)b->skiphdr)*(ulong)ipoff->data);
	if(n != 0)
		return n;



@@ 629,29 655,35 @@ static void
ipmuxkick(Conv *c)
{
	Block *bp;
	Iphdr *ih;
	struct Ip6hdr *ih6;

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

	ih = (Iphdr*)(bp->rp);

	ipoput(c->p->f, bp, 0, ih->ttl, ih->tos);
	else {
		Ip4hdr *ih4 = (Ip4hdr*)(bp->rp);
		if((ih4->vihl)&0xF0 != 0x60)
			ipoput4(c->p->f, bp, 0, ih4->ttl, ih4->tos);
		else {
			ih6 = (struct Ip6hdr*)ih4;
			ipoput6(c->p->f, bp, 0, ih6->ttl, 0);
		}
	}
}

static void
ipmuxiput(Proto *p, Ipifc *ifc, Block *bp)
{
	int len;
	int len, hl;
	Fs *f = p->f;
	uchar *m, *h, *v, *e, *ve, *hp;
	Conv *c;
	Ipmux *mux;
	Iphdr *ip;
	uchar *ia;
	Ip4hdr *ip;
	Ip6hdr *ip6;

	ia = ifc->lifc->local;
  ip = (Ip4hdr*)bp->rp;
  hl = (ip->vihl&0x0F)<<2;

	if(p->priv == nil)
		goto nomatch;


@@ 668,7 700,7 @@ ipmuxiput(Proto *p, Ipifc *ifc, Block *bp)
			mux = mux->no;
			continue;
		}
		hp = h + mux->off;
		hp = h + mux->off + ((int)mux->skiphdr)*hl;
		switch(mux->ctype){
		case Cbyte:
			if(*mux->val == *hp)


@@ 695,11 727,11 @@ ipmuxiput(Proto *p, Ipifc *ifc, Block *bp)
				goto yes;
			break;
		case Cifc:
			if(*((ulong*)mux->val) == *(ulong*)(ia + IPv4off))
			if(*((ulong*)mux->val) == *(ulong*)(ifc->lifc->local + IPv4off))
				goto yes;
			break;
		case Cmifc:
			if((*(ulong*)(ia + IPv4off) & (*((ulong*)mux->mask))) == *((ulong*)mux->val))
			if((*(ulong*)(ifc->lifc->local + IPv4off) & (*((ulong*)mux->mask))) == *((ulong*)mux->val))
				goto yes;
			break;
		default:


@@ 727,19 759,24 @@ yes:
	if(c != nil){
		/* tack on interface address */
		bp = padblock(bp, IPaddrlen);
		ipmove(bp->rp, ia);
		ipmove(bp->rp, ifc->lifc->local);
		bp = concatblock(bp);
		if(bp != nil)
			if (qpass(c->rq, bp) < 0)
			if(qpass(c->rq, bp) < 0)
				print("Q");
		return;
	}

nomatch:
	/* doesn't match any filter, hand it to the specific protocol handler */
	ip = (Iphdr*)bp->rp;
	p = f->t2p[ip->proto];
	if(p)
	ip = (Ip4hdr*)bp->rp;
	if((ip->vihl&0xF0)==0x40) {
		p = f->t2p[ip->proto];
	} else {
		ip6 = (Ip6hdr*)bp->rp;
		p = f->t2p[ip6->proto];
	}
	if(p && p->rcv)
		(*p->rcv)(p, ifc, bp);
	else
		freeblist(bp);


@@ 759,7 796,9 @@ ipmuxsprint(Ipmux *mux, int level, char *buf, int len)
		n += snprint(buf+n, len-n, "\n");
		return n;
	}
	n += snprint(buf+n, len-n, "h[%d:%d]&", mux->off, mux->off+mux->len-1);
	n += snprint(buf+n, len-n, "h[%d:%d]&", 
               mux->off+((int)mux->skiphdr)*((int)ipoff->data), 
               mux->off+(((int)mux->skiphdr)*((int)ipoff->data))+mux->len-1);
	for(i = 0; i < mux->len; i++)
		n += snprint(buf+n, len - n, "%2.2ux", mux->mask[i]);
	n += snprint(buf+n, len-n, "=");

M ip/iproute.c => ip/iproute.c +35 -49
@@ 16,22 16,6 @@ Route*	v4freelist;
Route*	v6freelist;
RWlock	routelock;

enum
{
	RWadd,
	RWflush,
	RWremove,
	RWtag,
};

static
Cmdtab routecmd[] = {
	RWadd,		"add",		4,
	RWflush,	"flush",	2,
	RWremove,	"remove",	3,
	RWtag,		"tag",		2,
};

static void
freeroute(Route *r)
{


@@ 337,6 321,7 @@ v4addroute(Fs *f, char *tag, uchar *a, uchar *mask, uchar *gate, int type)
}

#define	V6H(a)	(((a)[IPllen-1] & 0x07ffffff)>>(32-Lroot-5))
#define ISDFLT(a, mask, tag) ((ipcmp((a),v6Unspecified)==0) && (ipcmp((mask),v6Unspecified)==0) && (strcmp((tag), "ra")!=0))

void
v6addroute(Fs *f, char *tag, uchar *a, uchar *mask, uchar *gate, int type)


@@ 346,6 331,12 @@ v6addroute(Fs *f, char *tag, uchar *a, uchar *mask, uchar *gate, int type)
	ulong x, y;
	int h, eh;

	/*
	if(ISDFLT(a, mask, tag))
		f->v6p->cdrouter = -1;
	*/


	for(h = 0; h < IPllen; h++){
		x = nhgetl(a+4*h);
		y = nhgetl(mask+4*h);


@@ 528,8 519,8 @@ v6lookup(Fs *f, uchar *a)
	ulong x, y;
	uchar gate[IPaddrlen];

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


@@ 568,10 559,8 @@ next:		;

	if(q && (q->ifc == nil || q->ifcid != q->ifc->ifcid)){
		if(q->type & Rifc) {
			hnputl(gate, q->v6.gate[0]);
			hnputl(gate+4, q->v6.gate[1]);
			hnputl(gate+8, q->v6.gate[2]);
			hnputl(gate+12, q->v6.gate[3]);
			for(h = 0; h < IPllen; h++)
				hnputl(gate+4*h, q->v6.address[h]);
			q->ifc = findipifc(f, gate, q->type);
		} else
			q->ifc = findipifc(f, q->v6.gate, q->type);


@@ 606,10 595,10 @@ routetype(int type, char *p)

enum
{
	Rlinelen=	89,
	Rlinelen=	137,
};

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

void
convroute(Route *r, uchar *addr, uchar *mask, uchar *gate, char *t, int *nifc)


@@ 779,7 768,6 @@ routewrite(Fs *f, Chan *c, char *p, int n)
	int h, changed;
	char *tag;
	Cmdbuf *cb;
	Cmdtab *ct;
	uchar addr[IPaddrlen];
	uchar mask[IPaddrlen];
	uchar gate[IPaddrlen];


@@ 791,25 779,7 @@ routewrite(Fs *f, Chan *c, char *p, int n)
		nexterror();
	}

	ct = lookupcmd(cb, routecmd, nelem(routecmd));

	switch(ct->index){
	case RWadd:
		parseip(addr, cb->f[1]);
		parseipmask(mask, cb->f[2]);
		parseip(gate, cb->f[3]);
		tag = "none";
		if(c != nil){
			a = c->aux;
			tag = a->tag;
		}
		if(memcmp(addr, v4prefix, IPv4off) == 0)
			v4addroute(f, tag, addr+IPv4off, mask+IPv4off, gate+IPv4off, 0);
		else
			v6addroute(f, tag, addr, mask, gate, 0);
		break;

	case RWflush:
	if(strcmp(cb->f[0], "flush") == 0){
		tag = cb->f[1];
		for(h = 0; h < nelem(f->v4root); h++)
			for(changed = 1; changed;){


@@ 823,18 793,34 @@ routewrite(Fs *f, Chan *c, char *p, int n)
				changed = routeflush(f, f->v6root[h], tag);
				wunlock(&routelock);
			}
		break;

	case RWremove:
	} 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(f, addr+IPv4off, mask+IPv4off, 1);
		else
			v6delroute(f, addr, mask, 1);
		break;
	} 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){
			a = c->aux;
			tag = a->tag;
		}
		if(memcmp(addr, v4prefix, IPv4off) == 0)
			v4addroute(f, tag, addr+IPv4off, mask+IPv4off, gate+IPv4off, 0);
		else
			v6addroute(f, tag, addr, mask, gate, 0);
	} else if(strcmp(cb->f[0], "tag") == 0) {
		if(cb->nf < 2)
			error(Ebadarg);

	case RWtag:
		a = c->aux;
		na = newipaux(a->owner, cb->f[1]);
		c->aux = na;

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

#include	"ip.h"
#include	"ipv6.h"

enum
{
	IP4HDR		= 20,		/* sizeof(Ip4hdr) */
	IP6HDR		= 40,		/* sizeof(Ip6hdr) */
	IP_HLEN4	= 0x05,		/* Header length in words */
	IP_DF		= 0x4000,	/* Don't fragment */
	IP_MF		= 0x2000,	/* More fragments */
	IP6FHDR		= 8, 		/* sizeof(Fraghdr6) */
	IP_MAX		= (32*1024),	/* Maximum Internet packet size */
};

#define IPV6CLASS(hdr) ((hdr->vcf[0]&0x0F)<<2 | (hdr->vcf[1]&0xF0)>>2)
#define BLKIPVER(xp)	(((Ip6hdr*)((xp)->rp))->vcf[0]&0xF0)
/*
 * This sleazy macro is stolen shamelessly from ip.c, see comment there.
 */
#define BKFG(xp)	((Ipfrag*)((xp)->base))

typedef struct	IP	IP;
typedef struct	Fragment4	Fragment4;
typedef struct	Fragment6	Fragment6;
typedef struct	Ipfrag	Ipfrag;

Block*		ip6reassemble(IP*, int, Block*, Ip6hdr*);
void		ipfragfree6(IP*, Fragment6*);
Fragment6*	ipfragallo6(IP*);
static Block*		procxtns(IP *ip, Block *bp, int doreasm);
int		unfraglen(Block *bp, uchar *nexthdr, int setfh);
Block*		procopts(Block *bp);

/* MIB II counters */
enum
{
	Forwarding,
	DefaultTTL,
	InReceives,
	InHdrErrors,
	InAddrErrors,
	ForwDatagrams,
	InUnknownProtos,
	InDiscards,
	InDelivers,
	OutRequests,
	OutDiscards,
	OutNoRoutes,
	ReasmTimeout,
	ReasmReqds,
	ReasmOKs,
	ReasmFails,
	FragOKs,
	FragFails,
	FragCreates,

	Nstats,
};

static char *statnames[] =
{
[Forwarding]	"Forwarding",
[DefaultTTL]	"DefaultTTL",
[InReceives]	"InReceives",
[InHdrErrors]	"InHdrErrors",
[InAddrErrors]	"InAddrErrors",
[ForwDatagrams]	"ForwDatagrams",
[InUnknownProtos]	"InUnknownProtos",
[InDiscards]	"InDiscards",
[InDelivers]	"InDelivers",
[OutRequests]	"OutRequests",
[OutDiscards]	"OutDiscards",
[OutNoRoutes]	"OutNoRoutes",
[ReasmTimeout]	"ReasmTimeout",
[ReasmReqds]	"ReasmReqds",
[ReasmOKs]	"ReasmOKs",
[ReasmFails]	"ReasmFails",
[FragOKs]	"FragOKs",
[FragFails]	"FragFails",
[FragCreates]	"FragCreates",
};

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

struct Fragment6
{
	Block*	blist;
	Fragment6*	next;
	uchar 	src[IPaddrlen];
	uchar 	dst[IPaddrlen];
	uint	id;
	ulong 	age;
};

struct Ipfrag
{
	ushort	foff;
	ushort	flen;
};

/* an instance of IP */
struct IP
{
	ulong		stats[Nstats];

	QLock		fraglock4;
	Fragment4*	flisthead4;
	Fragment4*	fragfree4;
	Ref		id4;

	QLock		fraglock6;
	Fragment6*	flisthead6;
	Fragment6*	fragfree6;
	Ref		id6;

	int		iprouting;	/* true if we route like a gateway */
};

void
ipoput6(Fs *f, Block *bp, int gating, int ttl, int tos)
{
	int tentative;
	Ipifc *ifc;
	uchar *gate, nexthdr;
	Ip6hdr *eh;
	int medialen, len, chunk, uflen, flen, seglen, lid, offset, fragoff, morefrags, blklen;
	Route *r, *sr;
	Fraghdr6 fraghdr;
	Block *xp, *nb;
	IP *ip;


	ip = f->ip;

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

	ip->stats[OutRequests]++;

	/* Number of uchars in data and ip header to write */
	len = blocklen(bp);
	
	tentative = iptentative(f, eh->src);
	if(tentative){
		netlog(f, Logip, "reject tx of packet with tentative src address\n");
		goto free;
	}

	if(gating){
		chunk = nhgets(eh->ploadlen);
		if(chunk > len){
			ip->stats[OutDiscards]++;
			netlog(f, Logip, "short gated packet\n");
			goto free;
		}
		if(chunk + IPV6HDR_LEN < len)
			len = chunk + IPV6HDR_LEN;
	}

	if(len >= IP_MAX){
		print("len > IP_MAX, free\n");
		ip->stats[OutDiscards]++;
		netlog(f, Logip, "exceeded ip max size %I\n", eh->dst);
		goto free;
	}

	r = v6lookup(f, eh->dst);
	if(r == nil){
		print("no route for %I, src %I free\n", eh->dst, eh->src);
		ip->stats[OutNoRoutes]++;
		netlog(f, Logip, "no interface %I\n", eh->dst);
		goto free;
	}

	ifc = r->ifc;
	if(r->type & (Rifc|Runi))
		gate = eh->dst;
	else
	if(r->type & (Rbcast|Rmulti)) {
		gate = eh->dst;
		sr = v6lookup(f, eh->src);
		if(sr != nil && (sr->type & Runi))
			ifc = sr->ifc;
	}
	else
		gate = r->v6.gate;

	if(!gating)
		eh->vcf[0] = IP_VER6;
	eh->ttl = ttl;
	if(!gating) {
		eh->vcf[0] |= (tos >> 4);
		eh->vcf[1] = (tos << 4);
	}

	if(!canrlock(ifc)) {
		goto free;
	}

	if(waserror()){
		runlock(ifc);
		nexterror();
	}

	if(ifc->m == nil) {
		goto raise;
	}

	/* If we dont need to fragment just send it */
	medialen = ifc->maxmtu - ifc->m->hsize;
	if(len <= medialen) {
		hnputs(eh->ploadlen, len-IPV6HDR_LEN);
		ifc->m->bwrite(ifc, bp, V6, gate);
		runlock(ifc);
		poperror();
		return;
	}

	if(gating) 
	if(ifc->reassemble <= 0) {

		/* v6 intermediate nodes are not supposed to fragment pkts;
		   we fragment if ifc->reassemble is turned on; an exception
		   needed for nat.
		 */

		ip->stats[OutDiscards]++;
		icmppkttoobig6(f, ifc, bp);
		netlog(f, Logip, "%I: gated pkts not fragmented\n", eh->dst);
		goto raise;
	}
		
	/* start v6 fragmentation */
	uflen = unfraglen(bp, &nexthdr, 1);
	if(uflen > medialen) {
		ip->stats[FragFails]++;
		ip->stats[OutDiscards]++;
		netlog(f, Logip, "%I: unfragmentable part too big\n", eh->dst);
		goto raise;
	}

	flen = len - uflen;
	seglen = (medialen - (uflen + IP6FHDR)) & ~7;
	if(seglen < 8) {
		ip->stats[FragFails]++;
		ip->stats[OutDiscards]++;
		netlog(f, Logip, "%I: seglen < 8\n", eh->dst);
		goto raise;
	}

	lid = incref(&ip->id6);
	fraghdr.nexthdr = nexthdr;
	fraghdr.res = 0;
	hnputl(fraghdr.id, lid);

	xp = bp;
	offset = uflen;
	while (xp != nil && offset && offset >= BLEN(xp)) {
		offset -= BLEN(xp);
		xp = xp->next;
	}
	xp->rp += offset;

	fragoff = 0; 
	morefrags = 1;

	for(; fragoff < flen; fragoff += seglen) {
		nb = allocb(uflen + IP6FHDR + seglen);

		if(fragoff + seglen >= flen) {
			seglen = flen - fragoff;
			morefrags = 0;
		}

		hnputs(eh->ploadlen, seglen+IP6FHDR);
		memmove(nb->wp, eh, uflen);
		nb->wp += uflen;

		hnputs(fraghdr.offsetRM, fragoff); // last 3 bits must be 0
		fraghdr.offsetRM[1] |= morefrags;
		memmove(nb->wp, &fraghdr, IP6FHDR);
		nb->wp += IP6FHDR;

		/* Copy data */
		chunk = seglen;
		while (chunk) {
			if(!xp) {
				ip->stats[OutDiscards]++;
				ip->stats[FragFails]++;
				freeblist(nb);
				netlog(f, Logip, "!xp: chunk in v6%d\n", chunk);
				goto raise;
			}
			blklen = chunk;
			if(BLEN(xp) < chunk)
				blklen = BLEN(xp);
			memmove(nb->wp, xp->rp, blklen);

			nb->wp += blklen;
			xp->rp += blklen;
			chunk -= blklen;
			if(xp->rp == xp->wp)
				xp = xp->next; 
		}

		ifc->m->bwrite(ifc, nb, V6, gate);
		ip->stats[FragCreates]++;
	}
	ip->stats[FragOKs]++;

raise:
	runlock(ifc);
	poperror();
free:
	freeblist(bp);	

}

void
ipiput6(Fs *f, Ipifc *ifc, Block *bp)
{
	int hl;
	int hop, tos;
	uchar proto;
	Ip6hdr *h;
	Proto *p;
	int notforme;
	int tentative;
	uchar v6dst[IPaddrlen];
	IP *ip;
	Route *r, *sr;

	ip = f->ip;
	ip->stats[InReceives]++;

	/*
	 *  Ensure we have all the header info in the first
	 *  block.  Make life easier for other protocols by
	 *  collecting up to the first 64 bytes in the first block.
	 */
	if(BLEN(bp) < 64) {
		hl = blocklen(bp);
		if(hl < IP6HDR)
			hl = IP6HDR;
		if(hl > 64)
			hl = 64;
		bp = pullupblock(bp, hl);
		if(bp == nil)
			return;
	}

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

	memmove(&v6dst[0], &(h->dst)[0], IPaddrlen);
	notforme = ipforme(f, v6dst) == 0;
	tentative = iptentative(f, v6dst);
  
	if(tentative && (h->proto != ICMPv6)) {
		print("tentative addr, drop\n");
		freeblist(bp);
		return;
	}

	/* Check header version */
	if(BLKIPVER(bp) != IP_VER6) {
		ip->stats[InHdrErrors]++;
		netlog(f, Logip, "ip: bad version %ux\n", (h->vcf[0]&0xF0)>>2);
		freeblist(bp);
		return;
	}

	/* route */
	if(notforme) {
		if(!ip->iprouting){
			freeb(bp);
			return;
		}
		/* don't forward to source's network */
		sr = v6lookup(f, h->src);
		r = v6lookup(f, h->dst);

		if(r == nil || sr == r){
			ip->stats[OutDiscards]++;
			freeblist(bp);
			return;
		}

		/* don't forward if packet has timed out */
		hop = h->ttl;
		if(hop < 1) {
			ip->stats[InHdrErrors]++;
			icmpttlexceeded6(f, ifc, bp);
			freeblist(bp);
			return;
		}

		/* process headers & reassemble if the interface expects it */
		bp = procxtns(ip, bp, r->ifc->reassemble);

		if(bp == nil)
			return;

		ip->stats[ForwDatagrams]++;
		h = (Ip6hdr *) (bp->rp);
		tos = IPV6CLASS(h);
		hop = h->ttl;
		ipoput6(f, bp, 1, hop-1, tos);
		return;
	}

	/* reassemble & process headers if needed */
	bp = procxtns(ip, bp, 1);

	if(bp == nil)
		return;

	h = (Ip6hdr *) (bp->rp);
	proto = h->proto;
	p = Fsrcvpcol(f, proto);
	if(p != nil && p->rcv != nil) {
		ip->stats[InDelivers]++;
		(*p->rcv)(p, ifc, bp);
		return;
	}

	ip->stats[InDiscards]++;
	ip->stats[InUnknownProtos]++;
	freeblist(bp);
}

/*
 * ipfragfree6 - copied from ipfragfree4 - assume hold fraglock6
 */
void
ipfragfree6(IP *ip, Fragment6 *frag)
{
	Fragment6 *fl, **l;

	if(frag->blist)
		freeblist(frag->blist);

	memset(frag->src, 0, IPaddrlen);
	frag->id = 0;
	frag->blist = nil;

	l = &ip->flisthead6;
	for(fl = *l; fl; fl = fl->next) {
		if(fl == frag) {
			*l = frag->next;
			break;
		}
		l = &fl->next;
	}

	frag->next = ip->fragfree6;
	ip->fragfree6 = frag;

}

/*
 * ipfragallo6 - copied from ipfragalloc4
 */
Fragment6*
ipfragallo6(IP *ip)
{
	Fragment6 *f;

	while(ip->fragfree6 == nil) {
		/* free last entry on fraglist */
		for(f = ip->flisthead6; f->next; f = f->next)
			;
		ipfragfree6(ip, f);
	}
	f = ip->fragfree6;
	ip->fragfree6 = f->next;
	f->next = ip->flisthead6;
	ip->flisthead6 = f;
	f->age = msec + 30000;

	return f;
}

static Block*
procxtns(IP *ip, Block *bp, int doreasm) {

	int offset;
	uchar proto;
	Ip6hdr *h;

	h = (Ip6hdr *) (bp->rp);
	offset = unfraglen(bp, &proto, 0);

	if((proto == FH) && (doreasm != 0)) {
		bp = ip6reassemble(ip, offset, bp, h);
		if(bp == nil) 
			return nil; 
		offset = unfraglen(bp, &proto, 0);
	}

	if(proto == DOH || offset > IP6HDR) 
		bp = procopts(bp);

	return bp;
}


/*	returns length of "Unfragmentable part", i.e., sum of lengths of ipv6 hdr,
 *	hop-by-hop & routing headers if present; *nexthdr is set to nexthdr value
 *	of the last header in the "Unfragmentable part"; if setfh != 0, nexthdr
 *	field of the last header in the "Unfragmentable part" is set to FH.
 */
int
unfraglen(Block *bp, uchar *nexthdr, int setfh)
{
	uchar *p, *q;
	int ufl, hs;

	p = bp->rp;
	q = p+6;	/* proto, = p+sizeof(Ip6hdr.vcf)+sizeof(Ip6hdr.ploadlen) */
	*nexthdr = *q;
	ufl = IP6HDR;
	p += ufl;

	for(;;) {
		if(*nexthdr == HBH || *nexthdr == RH) {
			*nexthdr = *p;
			hs = ((int)*(p+1) + 1) * 8;
			ufl += hs;
			q = p;
			p += hs;
		}
		else
			break;
	}

	if(*nexthdr == FH)
		*q = *p;

	if(setfh)
		*q = FH;

	return ufl;
}

Block*
procopts(Block *bp)
{
	return bp;
}

Block*
ip6reassemble(IP* ip, int uflen, Block* bp, Ip6hdr* ih)
{

	int fend, offset;
	uint id;
	Fragment6 *f, *fnext;
	Fraghdr6 *fraghdr;
	uchar src[IPaddrlen], dst[IPaddrlen];
	Block *bl, **l, *last, *prev;
	int ovlap, len, fragsize, pktposn;

	fraghdr = (Fraghdr6 *) (bp->rp + uflen);
	memmove(src, ih->src, IPaddrlen);
	memmove(dst, ih->dst, IPaddrlen);
	id = nhgetl(fraghdr->id);
	offset = nhgets(fraghdr->offsetRM) & ~7;

	/*
	 *  block lists are too hard, pullupblock into a single block
	 */
	if(bp->next){
		bp = pullupblock(bp, blocklen(bp));
		ih = (Ip6hdr *)(bp->rp);
	}


	qlock(&ip->fraglock6);

	/*
	 *  find a reassembly queue for this fragment
	 */
	for(f = ip->flisthead6; f; f = fnext){
		fnext = f->next;
		if(ipcmp(f->src, src)==0 && ipcmp(f->dst, dst)==0 && f->id == id)
			break;
		if(f->age < msec){
			ip->stats[ReasmTimeout]++;
			ipfragfree6(ip, f);
		}
	}


	/*
	 *  if this isn't a fragmented packet, accept it
	 *  and get rid of any fragments that might go
	 *  with it.
	 */
	if(nhgets(fraghdr->offsetRM)==0) {	// first frag is also the last
		if(f != nil) {
			ipfragfree6(ip, f);
			ip->stats[ReasmFails]++;
		}
		qunlock(&ip->fraglock6);
		return bp;
	}

	if(bp->base+sizeof(Ipfrag) >= bp->rp){
		bp = padblock(bp, sizeof(Ipfrag));
		bp->rp += sizeof(Ipfrag);
	}

	BKFG(bp)->foff = offset;
	BKFG(bp)->flen = nhgets(ih->ploadlen) + IP6HDR - uflen - IP6FHDR;

	/* First fragment allocates a reassembly queue */
	if(f == nil) {
		f = ipfragallo6(ip);
		f->id = id;
		memmove(f->src, src, IPaddrlen);
		memmove(f->dst, dst, IPaddrlen);

		f->blist = bp;

		qunlock(&ip->fraglock6);
		ip->stats[ReasmReqds]++;
		return nil;
	}

	/*
	 *  find the new fragment's position in the queue
	 */
	prev = nil;
	l = &f->blist;
	bl = f->blist;
	while(bl != nil && BKFG(bp)->foff > BKFG(bl)->foff) {
		prev = bl;
		l = &bl->next;
		bl = bl->next;
	}

	/* Check overlap of a previous fragment - trim away as necessary */
	if(prev) {
		ovlap = BKFG(prev)->foff + BKFG(prev)->flen - BKFG(bp)->foff;
		if(ovlap > 0) {
			if(ovlap >= BKFG(bp)->flen) {
				freeblist(bp);
				qunlock(&ip->fraglock6);
				return nil;
			}
			BKFG(prev)->flen -= ovlap;
		}
	}

	/* Link onto assembly queue */
	bp->next = *l;
	*l = bp;

	/* Check to see if succeeding segments overlap */
	if(bp->next) {
		l = &bp->next;
		fend = BKFG(bp)->foff + BKFG(bp)->flen;

		/* Take completely covered segments out */

		while(*l) {
			ovlap = fend - BKFG(*l)->foff;

			if(ovlap <= 0) 
				break; 
			if(ovlap < BKFG(*l)->flen) {
				BKFG(*l)->flen -= ovlap;
				BKFG(*l)->foff += ovlap;
				/* move up ih hdrs */
				memmove((*l)->rp + ovlap, (*l)->rp, uflen);
				(*l)->rp += ovlap;
				break;
			}
			last = (*l)->next;
			(*l)->next = nil;
			freeblist(*l);
			*l = last;
		}
	}

	/*
	 *  look for a complete packet.  if we get to a fragment
	 *  with the trailing bit of fraghdr->offsetRM[1] set, we're done.
	 */
	pktposn = 0;
	for(bl = f->blist; bl; bl = bl->next) {
		if(BKFG(bl)->foff != pktposn)
			break;
	
		fraghdr = (Fraghdr6 *) (bl->rp + uflen);
		if((fraghdr->offsetRM[1] & 1) == 0) {

			bl = f->blist;

			/* get rid of frag header in first fragment */

			memmove(bl->rp + IP6FHDR, bl->rp, uflen);
			bl->rp += IP6FHDR;
			len = nhgets(((Ip6hdr*)(bl->rp))->ploadlen) - IP6FHDR;
			bl->wp = bl->rp + len + IP6HDR;

			/* Pullup all the fragment headers and
			 * return a complete packet
			 */
			for(bl = bl->next; bl; bl = bl->next) {
				fragsize = BKFG(bl)->flen;
				len += fragsize;
				bl->rp += uflen + IP6FHDR;
				bl->wp = bl->rp + fragsize;
			}

			bl = f->blist;
			f->blist = nil;
			ipfragfree6(ip, f);
			ih = (Ip6hdr*)(bl->rp);
			hnputs(ih->ploadlen, len);
			qunlock(&ip->fraglock6);
			ip->stats[ReasmOKs]++;
			return bl;		
		}
		pktposn += BKFG(bl)->flen;
	}
	qunlock(&ip->fraglock6);
	return nil;
}


A ip/ipv6.h => ip/ipv6.h +182 -0
@@ 0,0 1,182 @@
#define MIN(a, b) ((a) <= (b) ? (a) : (b))
#define isv6mcast(addr) ((addr)[0] == 0xff)
#define islinklocal(addr) ( ((addr)[0] == 0xfe) && ((addr)[1] == 0x80) )
#define issitelocal(addr) ( ((addr)[0] == 0xfe) && ((addr)[1] == 0xc0) )
#define isv6global(addr) ( (addr)[0] == 0x2 )
#define optexsts(np) (nhgets((np)->ploadlen) > 24)
#define issmcast(addr) (memcmp((addr), v6solicitednode, 13) == 0)

/* from RFC 2460 */

typedef struct Ip6hdr     Ip6hdr;
typedef struct Opthdr     Opthdr;
typedef struct Routinghdr Routinghdr;
typedef struct Fraghdr6    Fraghdr6;

struct Ip6hdr {
	uchar vcf[4];       	// version:4, traffic class:8, flow label:20
	uchar ploadlen[2];  	// payload length: packet length - 40
	uchar proto;		// next header type
	uchar ttl;          	// hop limit
	uchar src[IPaddrlen];
	uchar dst[IPaddrlen];
};

struct Opthdr {
	uchar nexthdr;
	uchar len;
};

struct Routinghdr {
	uchar nexthdr;
	uchar len;
	uchar rtetype;
	uchar segrem;
};

struct Fraghdr6 {
	uchar nexthdr;
	uchar res;
	uchar offsetRM[2];	// Offset, Res, M flag
	uchar id[4];
};


enum {			/* Header Types */
	HBH		= 0,	//?
	ICMP		= 1,
	IGMP		= 2,
	GGP		= 3,
	IPINIP		= 4,
	ST		= 5,
	TCP		= 6,
	UDP		= 17,
	ISO_TP4		= 29,
	RH		= 43,
	FH		= 44,
	IDRP		= 45,
	RSVP		= 46,
	AH		= 51,
	ESP		= 52,
	ICMPv6		= 58,	
	NNH		= 59,
	DOH		= 60,
	ISO_IP		= 80,
	IGRP		= 88,
	OSPF		= 89,	

	Maxhdrtype	= 256,
};


enum {
	//	multicast flgs and scop

	well_known_flg				= 0,
	transient_flg				= 1,

	node_local_scop 			= 1,
	link_local_scop 			= 2,
	site_local_scop 			= 5,
	org_local_scop				= 8,
	global_scop				= 14,

	//	various prefix lengths

	SOLN_PREF_LEN				= 13,

	//	icmpv6 unreach codes
	icmp6_no_route				= 0,
	icmp6_ad_prohib				= 1,
	icmp6_unassigned			= 2,
	icmp6_adr_unreach			= 3,
	icmp6_port_unreach			= 4,
	icmp6_unkn_code				= 5,

	// 	various flags & constants

	v6MINTU      				= 1280,	
	HOP_LIMIT    				= 255,
	ETHERHDR_LEN 				= 14,
	IPV6HDR_LEN  				= 40,
	IPV4HDR_LEN  				= 20,

	// 	option types

	SRC_LLADDRESS    			= 1,
	TARGET_LLADDRESS 			= 2,
	PREFIX_INFO      			= 3,
	REDIR_HEADER     			= 4,
	MTU_OPTION       			= 5,

	SRC_UNSPEC  				= 0,
	SRC_UNI     				= 1, 
	TARG_UNI    				= 2,
	TARG_MULTI  				= 3,

	t_unitent   				= 1,
	t_uniproxy  				= 2,
	t_unirany   				= 3,

	//	Router constants (all times in milliseconds)

	MAX_INITIAL_RTR_ADVERT_INTERVAL 	= 16000,
	MAX_INITIAL_RTR_ADVERTISEMENTS  	= 3, 
	MAX_FINAL_RTR_ADVERTISEMENTS    	= 3,
	MIN_DELAY_BETWEEN_RAS 			= 3000,
	MAX_RA_DELAY_TIME     			= 500,

	//	Host constants

	MAX_RTR_SOLICITATION_DELAY 		= 1000,
	RTR_SOLICITATION_INTERVAL  		= 4000,
	MAX_RTR_SOLICITATIONS      		= 3,

	//	Node constants

	MAX_MULTICAST_SOLICIT   		= 3,
	MAX_UNICAST_SOLICIT     		= 3,
	MAX_ANYCAST_DELAY_TIME  		= 1000,
	MAX_NEIGHBOR_ADVERTISEMENT 		= 3,	
	REACHABLE_TIME 				= 30000,
	RETRANS_TIMER  				= 1000,	
	DELAY_FIRST_PROBE_TIME 			= 5000,

};

extern void ipv62smcast(uchar *, uchar *);
extern void icmpns(Fs *f, uchar* src, int suni, uchar* targ, int tuni, uchar* mac);
extern void icmpna(Fs *f, uchar* src, uchar* dst, uchar* targ, uchar* mac, uchar flags);
extern void icmpttlexceeded6(Fs *f, Ipifc *ifc, Block *bp);
extern void icmppkttoobig6(Fs *f, Ipifc *ifc, Block *bp);
extern void icmphostunr(Fs *f, Ipifc *ifc, Block *bp, int code, int free);

extern uchar v6allnodesN[IPaddrlen];
extern uchar v6allnodesL[IPaddrlen];
extern uchar v6allroutersN[IPaddrlen];
extern uchar v6allroutersL[IPaddrlen];
extern uchar v6allnodesNmask[IPaddrlen];
extern uchar v6allnodesLmask[IPaddrlen];
extern uchar v6allroutersS[IPaddrlen];
extern uchar v6solicitednode[IPaddrlen];
extern uchar v6solicitednodemask[IPaddrlen];
extern uchar v6Unspecified[IPaddrlen];
extern uchar v6loopback[IPaddrlen];
extern uchar v6loopbackmask[IPaddrlen];
extern uchar v6linklocal[IPaddrlen];
extern uchar v6linklocalmask[IPaddrlen];
extern uchar v6sitelocal[IPaddrlen];
extern uchar v6sitelocalmask[IPaddrlen];
extern uchar v6glunicast[IPaddrlen];
extern uchar v6multicast[IPaddrlen];
extern uchar v6multicastmask[IPaddrlen];

extern int v6linklocalprefix;
extern int v6sitelocalprefix;
extern int v6loopbackprefix;
extern int v6multicastprefix;
extern int v6solicitednodeprefix;
extern int v6allnodesNprefix;
extern int v6allnodesLprefix;

extern int ReTransTimer;

M ip/loopbackmedium.c => ip/loopbackmedium.c +1 -1
@@ 94,7 94,7 @@ loopbackread(void *a)
		if(ifc->lifc == nil)
			freeb(bp);
		else
			ipiput(lb->f, ifc, bp);
			ipiput4(lb->f, ifc, bp);
		runlock(ifc);
		poperror();
	}

M ip/netdevmedium.c => ip/netdevmedium.c +1 -1
@@ 140,7 140,7 @@ ZZZ is this a good idea?
		if(ifc->lifc == nil)
			freeb(bp);
		else
			ipiput(er->f, ifc, bp);
			ipiput4(er->f, ifc, bp);
		runlock(ifc);
		poperror();
	}

M ip/netlog.c => ip/netlog.c +1 -1
@@ 207,7 207,7 @@ netlogctl(Fs *f, char* s, int n)
		cmderror(cb, "unknown ip control message");
	}

	for(i = 1; i < cb->nf; i++){
	for(i = 1; i < n; i++){
		for(fp = flags; fp->name; fp++)
			if(strcmp(fp->name, cb->f[i]) == 0)
				break;

M ip/pktmedium.c => ip/pktmedium.c +1 -1
@@ 64,7 64,7 @@ pktin(Fs *f, Ipifc *ifc, Block *bp)
	if(ifc->lifc == nil)
		freeb(bp);
	else
		ipiput(f, ifc, bp);
		ipiput4(f, ifc, bp);
}

void

M ip/rudp.c => ip/rudp.c +3 -1
@@ 321,7 321,7 @@ doipoput(Conv *c, Fs *f, Block *bp, int x, int ttl, int tos)
	if(ucb->randdrop && nrand(100) < ucb->randdrop)
		freeblist(bp);
	else
		ipoput(f, bp, x, ttl, tos);
		ipoput4(f, bp, x, ttl, tos);
}

int


@@ 402,6 402,7 @@ rudpkick(Conv *c)
		return;

	uh = (Udphdr *)(bp->rp);
	uh->vihl = IP_VER4;

	rh = (Rudphdr*)uh;



@@ 987,6 988,7 @@ relsendack(Conv *c, Reliable *r, int hangup)
	bp->wp += UDP_IPHDR + UDP_RHDRSIZE;
	f = c->p->f;
	uh = (Udphdr *)(bp->rp);
	uh->vihl = IP_VER4;
	rh = (Rudphdr*)uh;

	ptcllen = (UDP_RHDRSIZE-UDP_PHDRSIZE);

M ip/tcp.c => ip/tcp.c +454 -189
@@ 11,13 11,21 @@ enum
{
	QMAX		= 64*1024-1,
	IP_TCPPROTO	= 6,
	TCP_IPLEN	= 8,
	TCP_PHDRSIZE	= 12,
	TCP_HDRSIZE	= 20,
	TCP_TCBPHDRSZ	= 40,
	TCP_PKT		= TCP_IPLEN+TCP_PHDRSIZE,

	TCP4_IPLEN	= 8,
	TCP4_PHDRSIZE	= 12,
	TCP4_HDRSIZE	= 20,
	TCP4_TCBPHDRSZ	= 40,
	TCP4_PKT	= TCP4_IPLEN+TCP4_PHDRSIZE,

	TCP6_IPLEN	= 0,
	TCP6_PHDRSIZE	= 40,
	TCP6_HDRSIZE	= 20,
	TCP6_TCBPHDRSZ	= 60,
	TCP6_PKT	= TCP6_IPLEN+TCP6_PHDRSIZE,

	TcptimerOFF	= 0,
	TcptimerON		= 1,
	TcptimerON	= 1,
	TcptimerDONE	= 2,
	MAX_TIME 	= (1<<20),	/* Forever */
	TCP_ACK		= 200,		/* Timed ack sequence in ms */


@@ 37,6 45,7 @@ enum
	MSL2		= 10,
	MSPTICK		= 50,		/* Milliseconds per timer tick */
	DEF_MSS		= 1460,		/* Default mean segment */
	DEF_MSS6	= 1280,		/* Default mean segment (min) for v6 */
	DEF_RTT		= 500,		/* Default round trip */
	DEF_KAT		= 30000,	/* Default time ms) between keep alives */
	TCP_LISTEN	= 0,		/* Listen connection */


@@ 86,8 95,8 @@ struct Tcptimer
	void	*arg;
};

typedef struct Tcphdr Tcphdr;
struct Tcphdr
typedef struct Tcp4hdr Tcp4hdr;
struct Tcp4hdr
{
	uchar	vihl;		/* Version and header length */
	uchar	tos;		/* Type of service */


@@ 112,6 121,29 @@ struct Tcphdr
	uchar	tcpmss[2];
};

typedef struct Tcp6hdr Tcp6hdr;
struct Tcp6hdr
{
	uchar	vcf[4];
	uchar	ploadlen[2];
	uchar	proto;
	uchar	ttl;
	uchar	tcpsrc[IPaddrlen];
	uchar	tcpdst[IPaddrlen];
	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 */
	uchar	tcpopt[2];
	uchar	tcpmss[2];
};


typedef struct Tcp Tcp;
struct	Tcp
{


@@ 123,13 155,13 @@ struct	Tcp
	ushort	wnd;
	ushort	urg;
	ushort	mss;
        ushort  len;	/* size of data */
	ushort	len;	/* size of data */
};

typedef struct Reseq Reseq;
struct Reseq
{
	Reseq 	*next;
	Reseq	*next;
	Tcp	seg;
	Block	*bp;
	ushort	length;


@@ 148,14 180,13 @@ struct Tcpctl
		ulong	una;		/* Unacked data pointer */
		ulong	nxt;		/* Next sequence expected */
		ulong	ptr;		/* Data pointer */
		ushort	wnd;		/* Tcp send window */
		ushort wnd;		/* Tcp send window */
		ulong	urg;		/* Urgent data pointer */
		ulong	wl2;

                /* to implement tahoe and reno TCP */
                ulong dupacks;          /* number of duplicate acks rcvd */
	        int   recovery;         /* loss recovery flag */
	        ulong rxt;              /* right window marker for recovery */
		/* to implement tahoe and reno TCP */
		ulong dupacks;    /* number of duplicate acks rcvd */
		int   recovery;   /* loss recovery flag */
		ulong rxt;              /* right window marker for recovery */
	} snd;
	struct {
		ulong	nxt;		/* Receive pointer to next uchar slot */


@@ 190,7 221,10 @@ struct Tcpctl
	int	nochecksum;		/* non-zero means don't send checksums */
	int	flgcnt;			/* 1 when we're waiting for a SYN/FIN ACK */

	Tcphdr	protohdr;		/* prototype header */
	union {
		Tcp4hdr	tcp4hdr;
		Tcp6hdr	tcp6hdr;
	} protohdr;		/* prototype header */
};

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


@@ 241,7 275,7 @@ static char *statnames[] =
typedef struct Tcppriv Tcppriv;
struct Tcppriv
{
	Tcptimer 	*timers;		/* List of active timers */
	Tcptimer 	*timers;	/* List of active timers */
	QLock 	tl;			/* Protect timer list */
	Rendez	tcpr;			/* used by tcpackproc */



@@ 642,22 676,28 @@ tcpmtu(Conv *s)
{
	Ipifc *ifc;
	int mtu;
	int version = (isv4(s->raddr) && isv4(s->laddr)) ? 4 : 6;

	mtu = 0;
	ifc = findipifc(s->p->f, s->raddr, 0);
	if(ifc != nil)
		mtu = ifc->maxmtu - ifc->m->hsize - (TCP_PKT + TCP_HDRSIZE);
	if(mtu < 4)
		mtu = DEF_MSS;
	return restrict_mtu(s->raddr, mtu);
	if(ifc != nil) {
		if(version == 4)
			mtu = ifc->maxmtu - ifc->m->hsize - (TCP4_PKT + TCP4_HDRSIZE);
		else
			mtu = ifc->maxmtu - ifc->m->hsize - (TCP6_PKT + TCP6_HDRSIZE);
	}

	if(mtu < 32) {
		mtu = 1280;
	}

	return mtu;
}

void
inittcpctl(Conv *s, int mode)
{
	Tcpctl *tcb;
	Tcphdr *h;

	tcb = (Tcpctl*)s->ptcl;

	memset(tcb, 0, sizeof(Tcpctl));


@@ 682,13 722,26 @@ inittcpctl(Conv *s, int mode)
	if(mode != TCP_LISTEN)
	if(ipcmp(s->laddr, IPnoaddr) == 0)
		findlocalip(s->p->f, 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);

//	if(isv4(s->raddr)) {
	if(memcmp(s->raddr, v4prefix, IPv4off) == 0 &&
		memcmp(s->laddr, v4prefix, IPv4off) == 0) {
		Tcp4hdr* h4 = &tcb->protohdr.tcp4hdr;
		memset(h4, 0, sizeof(*h4));
		h4->proto = IP_TCPPROTO;
		hnputs(h4->tcpsport, s->lport);
		hnputs(h4->tcpdport, s->rport);
		v6tov4(h4->tcpsrc, s->laddr);
		v6tov4(h4->tcpdst, s->raddr);
	} else {
		Tcp6hdr* h6 = &tcb->protohdr.tcp6hdr;
		memset(h6, 0, sizeof(*h6));
		h6->proto = IP_TCPPROTO;
		hnputs(h6->tcpsport, s->lport);
		hnputs(h6->tcpdport, s->rport);
		ipmove(h6->tcpsrc, s->laddr);
		ipmove(h6->tcpdst, s->raddr);
	}

	tcb->mss = tcb->cwind = tcpmtu(s);
}


@@ 763,37 816,41 @@ tcpflag(ushort flag)
}

Block *
htontcp(Tcp *tcph, Block *data, Tcphdr *ph, Tcpctl *tcb)
htontcp6(Tcp *tcph, Block *data, Tcp6hdr *ph, Tcpctl *tcb)
{
	int dlen;
	Tcphdr *h;
	Tcp6hdr *h;
	ushort csum;
	ushort hdrlen;

	hdrlen = TCP_HDRSIZE;
	hdrlen = TCP6_HDRSIZE;
	if(tcph->mss)
		hdrlen += MSS_LENGTH;

	if(data) {
		dlen = blocklen(data);
		data = padblock(data, hdrlen + TCP_PKT);
		data = padblock(data, hdrlen + TCP6_PKT);
		if(data == nil)
			return nil;
	}
	else {
		dlen = 0;
		data = allocb(hdrlen + TCP_PKT + 64);	/* the 64 pad is to meet mintu's */
		data = allocb(hdrlen + TCP6_PKT + 64);	/* the 64 pad is to meet mintu's */
		if(data == nil)
			return nil;
		data->wp += hdrlen + TCP_PKT;
		data->wp += hdrlen + TCP6_PKT;
	}

	/* copy in pseudo ip header plus port numbers */
	h = (Tcphdr *)(data->rp);
	memmove(h, ph, TCP_TCBPHDRSZ);
	h = (Tcp6hdr *)(data->rp);
	memmove(h, ph, TCP6_TCBPHDRSZ);

	/* compose pseudo tcp header, do cksum calculation */
	hnputl(h->vcf, hdrlen + dlen);
	h->ploadlen[0] = h->ploadlen[1] = h->proto = 0;
	h->ttl = ph->proto;

	/* copy in variable bits */
	hnputs(h->tcplen, hdrlen + dlen);
	hnputl(h->tcpseq, tcph->seq);
	hnputl(h->tcpack, tcph->ack);
	hnputs(h->tcpflag, (hdrlen<<10) | tcph->flags);


@@ 808,39 865,92 @@ htontcp(Tcp *tcph, Block *data, Tcphdr *ph, Tcpctl *tcb)
	if(tcb != nil && tcb->nochecksum){
		h->tcpcksum[0] = h->tcpcksum[1] = 0;
	} else {
		csum = ptclcsum(data, TCP_IPLEN, hdrlen+dlen+TCP_PHDRSIZE);
		csum = ptclcsum(data, TCP6_IPLEN, hdrlen+dlen+TCP6_PHDRSIZE);
		hnputs(h->tcpcksum, csum);
	}

/*	netlog(f, 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); */
	/* move from pseudo header back to normal ip header */
	memset(h->vcf, 0, 4);
	h->vcf[0] = IP_VER6;
	hnputs(h->ploadlen, hdrlen+dlen);
	h->proto = ph->proto;

	return data;
}

Block *
htontcp4(Tcp *tcph, Block *data, Tcp4hdr *ph, Tcpctl *tcb)
{
	int dlen;
	Tcp4hdr *h;
	ushort csum;
	ushort hdrlen;

	hdrlen = TCP4_HDRSIZE;
	if(tcph->mss)
		hdrlen += MSS_LENGTH;

	if(data) {
		dlen = blocklen(data);
		data = padblock(data, hdrlen + TCP4_PKT);
		if(data == nil)
			return nil;
	}
	else {
		dlen = 0;
		data = allocb(hdrlen + TCP4_PKT + 64);	/* the 64 pad is to meet mintu's */
		if(data == nil)
			return nil;
		data->wp += hdrlen + TCP4_PKT;
	}

	/* copy in pseudo ip header plus port numbers */
	h = (Tcp4hdr *)(data->rp);
	memmove(h, ph, TCP4_TCBPHDRSZ);

	/* copy in variable bits */
	hnputs(h->tcplen, hdrlen + dlen);
	hnputl(h->tcpseq, tcph->seq);
	hnputl(h->tcpack, tcph->ack);
	hnputs(h->tcpflag, (hdrlen<<10) | tcph->flags);
	hnputs(h->tcpwin, tcph->wnd);
	hnputs(h->tcpurg, tcph->urg);

	if(tcph->mss != 0){
		h->tcpopt[0] = MSSOPT;
		h->tcpopt[1] = MSS_LENGTH;
		hnputs(h->tcpmss, tcph->mss);
	}
	if(tcb != nil && tcb->nochecksum){
		h->tcpcksum[0] = h->tcpcksum[1] = 0;
	} else {
		csum = ptclcsum(data, TCP4_IPLEN, hdrlen+dlen+TCP4_PHDRSIZE);
		hnputs(h->tcpcksum, csum);
	}

	return data;
}

int
ntohtcp(Tcp *tcph, Block **bpp)
ntohtcp6(Tcp *tcph, Block **bpp)
{
	Tcphdr *h;
	Tcp6hdr *h;
	uchar *optr;
	ushort hdrlen;
	ushort optlen;
	int n;

	*bpp = pullupblock(*bpp, TCP_PKT+TCP_HDRSIZE);
	*bpp = pullupblock(*bpp, TCP6_PKT+TCP6_HDRSIZE);
	if(*bpp == nil)
		return -1;

	h = (Tcphdr *)((*bpp)->rp);
	h = (Tcp6hdr *)((*bpp)->rp);
	tcph->source = nhgets(h->tcpsport);
	tcph->dest = nhgets(h->tcpdport);
	tcph->seq = nhgetl(h->tcpseq);
	tcph->ack = nhgetl(h->tcpack);

	hdrlen = (h->tcpflag[0] & 0xf0)>>2;
	if(hdrlen < TCP_HDRSIZE) {
	if(hdrlen < TCP6_HDRSIZE) {
		freeblist(*bpp);
		return -1;
	}


@@ 849,19 959,72 @@ ntohtcp(Tcp *tcph, Block **bpp)
	tcph->wnd = nhgets(h->tcpwin);
	tcph->urg = nhgets(h->tcpurg);
	tcph->mss = 0;
	tcph->len = nhgets(h->length) - (hdrlen + TCP_PKT);
	tcph->len = nhgets(h->ploadlen) - hdrlen;

	*bpp = pullupblock(*bpp, hdrlen+TCP_PKT);
	*bpp = pullupblock(*bpp, hdrlen+TCP6_PKT);
	if(*bpp == nil)
		return -1;

/*	netlog(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); */
	optr = h->tcpopt;
	n = hdrlen - TCP6_HDRSIZE;
	while(n > 0 && *optr != EOLOPT) {
		if(*optr == NOOPOPT) {
			n--;
			optr++;
			continue;
		}
		optlen = optr[1];
		if(optlen < 2 || optlen > n)
			break;
		switch(*optr) {
		case MSSOPT:
			if(optlen == MSS_LENGTH)
				tcph->mss = nhgets(optr+2);
			break;
		}
		n -= optlen;
		optr += optlen;
	}
	return hdrlen;
}

int
ntohtcp4(Tcp *tcph, Block **bpp)
{
	Tcp4hdr *h;
	uchar *optr;
	ushort hdrlen;
	ushort optlen;
	int n;

	*bpp = pullupblock(*bpp, TCP4_PKT+TCP4_HDRSIZE);
	if(*bpp == nil)
		return -1;

	h = (Tcp4hdr *)((*bpp)->rp);
	tcph->source = nhgets(h->tcpsport);
	tcph->dest = nhgets(h->tcpdport);
	tcph->seq = nhgetl(h->tcpseq);
	tcph->ack = nhgetl(h->tcpack);

	hdrlen = (h->tcpflag[0] & 0xf0)>>2;
	if(hdrlen < TCP4_HDRSIZE) {
		freeblist(*bpp);
		return -1;
	}

	tcph->flags = h->tcpflag[1];
	tcph->wnd = nhgets(h->tcpwin);
	tcph->urg = nhgets(h->tcpurg);
	tcph->mss = 0;
	tcph->len = nhgets(h->length) - (hdrlen + TCP4_PKT);

	*bpp = pullupblock(*bpp, hdrlen+TCP4_PKT);
	if(*bpp == nil)
		return -1;

	optr = h->tcpopt;
	n = hdrlen - TCP_HDRSIZE;
	n = hdrlen - TCP4_HDRSIZE;
	while(n > 0 && *optr != EOLOPT) {
		if(*optr == NOOPOPT) {
			n--;


@@ 871,7 1034,6 @@ ntohtcp(Tcp *tcph, Block **bpp)
		optlen = optr[1];
		if(optlen < 2 || optlen > n)
			break;
if(0) print("tcpopt %d %d\n", *optr, optlen);
		switch(*optr) {
		case MSSOPT:
			if(optlen == MSS_LENGTH)


@@ 899,17 1061,14 @@ tcpsndsyn(Tcpctl *tcb)
	tcb->sndsyntime = msec;
}


/*
 *  called with v4 (4 byte) addresses
 */
void
sndrst(Proto *tcp, uchar *source, uchar *dest, ushort length, Tcp *seg)
sndrst(Proto *tcp, uchar *source, uchar *dest, ushort length, Tcp *seg, int version)
{
	Tcphdr ph;
	Block *hbp;
	uchar rflags;
	Tcppriv *tpriv;
	Tcp4hdr ph4;
	Tcp6hdr ph6;

	tpriv = tcp->priv;



@@ 917,13 1076,26 @@ sndrst(Proto *tcp, uchar *source, uchar *dest, ushort length, Tcp *seg)
		return;

	/* 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);
	hnputs(ph.tcpsport, seg->dest);
	hnputs(ph.tcpdport, seg->source);
	if(version == 4) {
		memset(&ph4, 0, sizeof(ph4));
		ph4.vihl = IP_VER4;
		v6tov4(ph4.tcpsrc, dest);
		v6tov4(ph4.tcpdst, source);
		ph4.proto = IP_TCPPROTO;
		hnputs(ph4.tcplen, TCP4_HDRSIZE);
		hnputs(ph4.tcpsport, seg->dest);
		hnputs(ph4.tcpdport, seg->source);
	}
	else {
		memset(&ph6, 0, sizeof(ph6));
		ph6.vcf[0] = IP_VER6;
		ipmove(ph6.tcpsrc, dest);
		ipmove(ph6.tcpdst, source);
		ph6.proto = IP_TCPPROTO;
		hnputs(ph6.ploadlen, TCP6_HDRSIZE);
		hnputs(ph6.tcpsport, seg->dest);
		hnputs(ph6.tcpdport, seg->source);
	}

	tpriv->stats[OutRsts]++;
	rflags = RST;


@@ 947,11 1119,18 @@ sndrst(Proto *tcp, uchar *source, uchar *dest, ushort length, Tcp *seg)
	seg->wnd = 0;
	seg->urg = 0;
	seg->mss = 0;
	hbp = htontcp(seg, nil, &ph, nil);
	if(hbp == nil)
		return;

	ipoput(tcp->f, hbp, 0, MAXTTL, DFLTTOS);
	if(version == 4) {
		hbp = htontcp4(seg, nil, &ph4, nil);
		if(hbp == nil)
			return;
		ipoput4(tcp->f, hbp, 0, MAXTTL, DFLTTOS);
	}
	else {
		hbp = htontcp6(seg, nil, &ph6, nil);
		if(hbp == nil)
			return;
		ipoput6(tcp->f, hbp, 0, MAXTTL, DFLTTOS);
	}
}

/*


@@ 963,13 1142,13 @@ tcphangup(Conv *s)
{
	Tcp seg;
	Tcpctl *tcb;
	Tcphdr ph;
	Block *hbp;

	tcb = (Tcpctl*)s->ptcl;
	if(waserror())
		return commonerror();
	if(s->raddr != 0) {
		int version = isv4(s->raddr) ? 4 : 6;
		seg.flags = RST | ACK;
		seg.ack = tcb->rcv.nxt;
		seg.seq = tcb->snd.ptr;


@@ 977,9 1156,16 @@ tcphangup(Conv *s)
		seg.urg = 0;
		seg.mss = 0;
		tcb->last_ack = tcb->rcv.nxt;
		hnputs(ph.tcplen, TCP_HDRSIZE);
		hbp = htontcp(&seg, nil, &tcb->protohdr, tcb);
		ipoput(s->p->f, hbp, 0, s->ttl, s->tos);
		if(version == 4) {
			tcb->protohdr.tcp4hdr.vihl = IP_VER4;
			hbp = htontcp4(&seg, nil, &tcb->protohdr.tcp4hdr, tcb);
			ipoput4(s->p->f, hbp, 0, s->ttl, s->tos);
		}
		else {
			tcb->protohdr.tcp6hdr.vcf[0] = IP_VER6;
			hbp = htontcp6(&seg, nil, &tcb->protohdr.tcp6hdr, tcb);
			ipoput6(s->p->f, hbp, 0, s->ttl, s->tos);
		}
	}
	localclose(s, nil);
	poperror();


@@ 991,7 1177,6 @@ tcpincoming(Conv *s, Tcp *segp, uchar *src, uchar *dst)
{
	Conv *new;
	Tcpctl *tcb;
	Tcphdr *h;
	Tcppriv *tpriv;

	new = Fsnewcall(s, src, segp->source, dst, segp->dest);


@@ 1010,13 1195,24 @@ tcpincoming(Conv *s, Tcp *segp, uchar *src, uchar *dst)
	tcb->rtt_timer.arg = new;
	tcb->rtt_timer.state = TcptimerOFF;

	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);
	if(isv4(src)) {
		Tcp4hdr *h = &tcb->protohdr.tcp4hdr;
		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);
	}
	else {
		Tcp6hdr *h = &tcb->protohdr.tcp6hdr;
		memset(h, 0, sizeof(*h));
		h->proto = IP_TCPPROTO;
		hnputs(h->tcpsport, new->lport);
		hnputs(h->tcpdport, new->rport);
		ipmove(h->tcpsrc, dst);
		ipmove(h->tcpdst, src);
	}

	tpriv = new->p->priv;
	iphtadd(&tpriv->ht, new);


@@ 1195,7 1391,7 @@ update(Conv *s, Tcp *seg)
			if(rtt == 0)
				rtt = 1;	/* otherwise all close systems will rexmit in 0 time */
			rtt *= MSPTICK;
			if (tcb->srtt == 0) {
			if(tcb->srtt == 0) {
				tcb->srtt = rtt << LOGAGAIN;
				tcb->mdev = rtt << LOGDGAIN;
			} else {


@@ 1237,7 1433,8 @@ void
tcpiput(Proto *tcp, Ipifc*, Block *bp)
{
	Tcp seg;
	Tcphdr *h;
	Tcp4hdr *h4;
	Tcp6hdr *h6;
	int hdrlen;
	Tcpctl *tcb;
	ushort length;


@@ 1245,45 1442,92 @@ tcpiput(Proto *tcp, Ipifc*, Block *bp)
	Conv *s;
	Fs *f;
	Tcppriv *tpriv;
	int version;

	f = tcp->f;
	tpriv = tcp->priv;
	
	tpriv->stats[InSegs]++;

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

	length = nhgets(h->length);
	v4tov6(dest, h->tcpdst);
	v4tov6(source, h->tcpsrc);
	h4 = (Tcp4hdr*)(bp->rp);
	h6 = (Tcp6hdr*)(bp->rp);

	if((h4->vihl&0xF0)==IP_VER4) {
		version = 4;
		length = nhgets(h4->length);
		v4tov6(dest, h4->tcpdst);
		v4tov6(source, h4->tcpsrc);

		h4->Unused = 0;
		hnputs(h4->tcplen, length-TCP4_PKT);
		if((h4->tcpcksum[0] || h4->tcpcksum[1]) && 
			ptclcsum(bp, TCP4_IPLEN, length-TCP4_IPLEN)) {
			tpriv->stats[CsumErrs]++;
			tpriv->stats[InErrs]++;
			netlog(f, Logtcp, "bad tcp proto cksum\n");
			freeblist(bp);
			return;
		}

	h->Unused = 0;
	hnputs(h->tcplen, length-TCP_PKT);
	if((h->tcpcksum[0] || h->tcpcksum[1]) && 
	    ptclcsum(bp, TCP_IPLEN, length-TCP_IPLEN)) {
		tpriv->stats[CsumErrs]++;
		tpriv->stats[InErrs]++;
		netlog(f, Logtcp, "bad tcp proto cksum\n");
		freeblist(bp);
		return;
	}
		hdrlen = ntohtcp4(&seg, &bp);
		if(hdrlen < 0){
			tpriv->stats[HlenErrs]++;
			tpriv->stats[InErrs]++;
			netlog(f, Logtcp, "bad tcp hdr len\n");
			return;
		}

	hdrlen = ntohtcp(&seg, &bp);
	if(hdrlen < 0){
		tpriv->stats[HlenErrs]++;
		tpriv->stats[InErrs]++;
		netlog(f, Logtcp, "bad tcp hdr len\n");
		return;
		/* trim the packet to the size claimed by the datagram */
		length -= hdrlen+TCP4_PKT;
		bp = trimblock(bp, hdrlen+TCP4_PKT, length);
		if(bp == nil){
			tpriv->stats[LenErrs]++;
			tpriv->stats[InErrs]++;
			netlog(f, Logtcp, "tcp len < 0 after trim\n");
			return;
		}
	}
	else {
		int ttl = h6->ttl;
		int proto = h6->proto;

		version = 6;
		length = nhgets(h6->ploadlen);
		ipmove(dest, h6->tcpdst);
		ipmove(source, h6->tcpsrc);

		h6->ploadlen[0] = h6->ploadlen[1] = h6->proto = 0;
		h6->ttl = proto;
		hnputl(h6->vcf, length);
		if((h6->tcpcksum[0] || h6->tcpcksum[1]) && 
			ptclcsum(bp, TCP6_IPLEN, length+TCP6_PHDRSIZE)) {
			tpriv->stats[CsumErrs]++;
			tpriv->stats[InErrs]++;
			netlog(f, Logtcp, "bad tcp proto cksum\n");
			freeblist(bp);
			return;
		}
		h6->ttl = ttl;
		h6->proto = proto;
		hnputs(h6->ploadlen, length);

		hdrlen = ntohtcp6(&seg, &bp);
		if(hdrlen < 0){
			tpriv->stats[HlenErrs]++;
			tpriv->stats[InErrs]++;
			netlog(f, Logtcp, "bad tcp hdr len\n");
			return;
		}

	/* trim the packet to the size claimed by the datagram */
	length -= hdrlen+TCP_PKT;
	bp = trimblock(bp, hdrlen+TCP_PKT, length);
	if(bp == nil){
		tpriv->stats[LenErrs]++;
		tpriv->stats[InErrs]++;
		netlog(f, Logtcp, "tcp len < 0 after trim\n");
		return;
		/* trim the packet to the size claimed by the datagram */
		length -= hdrlen;
		bp = trimblock(bp, hdrlen+TCP6_PKT, length);
		if(bp == nil){
			tpriv->stats[LenErrs]++;
			tpriv->stats[InErrs]++;
			netlog(f, Logtcp, "tcp len < 0 after trim\n");
			return;
		}
	}

	/* lock protocol while searching for a conversation */


@@ 1294,7 1538,7 @@ tcpiput(Proto *tcp, Ipifc*, Block *bp)
	if(s == nil){
reset:
		qunlock(tcp);
		sndrst(tcp, source, dest, length, &seg);
		sndrst(tcp, source, dest, length, &seg, version);
		freeblist(bp);
		return;
	}


@@ 1334,7 1578,7 @@ reset:

	switch(tcb->state) {
	case Closed:
		sndrst(tcp, source, dest, length, &seg);
		sndrst(tcp, source, dest, length, &seg, version);
		goto raise;
	case Listen:
		if(seg.flags & SYN) {


@@ 1349,7 1593,7 @@ reset:
	case Syn_sent:
		if(seg.flags & ACK) {
			if(!seq_within(seg.ack, tcb->iss+1, tcb->snd.nxt)) {
				sndrst(tcp, source, dest, length, &seg);
				sndrst(tcp, source, dest, length, &seg, version);
				goto raise;
			}
		}


@@ 1413,7 1657,7 @@ reset:

	/* Cannot accept so answer with a rst */
	if(length && tcb->state == Closed) {
		sndrst(tcp, source, dest, length, &seg);
		sndrst(tcp, source, dest, length, &seg, version);
		goto raise;
	}



@@ 1448,7 1692,7 @@ reset:
		switch(tcb->state) {
		case Syn_received:
			if(!seq_within(seg.ack, tcb->snd.una+1, tcb->snd.nxt)){
				sndrst(tcp, source, dest, length, &seg);
				sndrst(tcp, source, dest, length, &seg, version);
				goto raise;
			}
			update(s, &seg);


@@ 1556,7 1800,7 @@ reset:
				/* no process to read the data, send a reset */
				if(bp != nil)
					freeblist(bp);
				sndrst(tcp, source, dest, length, &seg);
				sndrst(tcp, source, dest, length, &seg, version);
				qunlock(s);
				poperror();
				return;


@@ 1649,6 1893,15 @@ tcpoutput(Conv *s)
	ulong ssize, dsize, usable, sent;
	Fs *f;
	Tcppriv *tpriv;
	//int version = isv4(s->raddr) ? 4 : 6;
	int version;

	if( (memcmp(s->raddr, v4prefix, IPv4off) == 0 &&
		memcmp(s->laddr, v4prefix, IPv4off) == 0)
		|| ipcmp(s->raddr, IPnoaddr) == 0)
		version = 4;
	else
		version = 6;

	f = s->p->f;
	tpriv = s->p->priv;


@@ 1681,7 1934,7 @@ tcpoutput(Conv *s)
		 */
		if(tcb->snd.wnd == 0){
			if(sent != 0) {
				if ((tcb->flags&FORCE) == 0)
				if((tcb->flags&FORCE) == 0)
					break;
				tcb->snd.ptr = tcb->snd.una;
			}


@@ 1755,8 2008,6 @@ tcpoutput(Conv *s)
				seg.flags |= FIN;
				dsize--;
			}
/*			netlog(f, Logtcp, "qcopy: dlen %d blen %d sndcnt %d qlen %d sent %d rp[0] %d\n",
				dsize, BLEN(bp), sndcnt, qlen(s->wq), sent, bp->rp[0]); */
		}

		if(sent+dsize == sndcnt)


@@ 1782,10 2033,21 @@ tcpoutput(Conv *s)
			tcb->snd.nxt = tcb->snd.ptr;

		/* Build header, link data and compute cksum */
		hbp = htontcp(&seg, bp, &tcb->protohdr, tcb);
		if(hbp == nil) {
			freeblist(bp);
			return;
		if(version == 4) {
			tcb->protohdr.tcp4hdr.vihl = IP_VER4;
			hbp = htontcp4(&seg, bp, &tcb->protohdr.tcp4hdr, tcb);
			if(hbp == nil) {
				freeblist(bp);
				return;
			}
		}
		else {
			tcb->protohdr.tcp6hdr.vcf[0] = IP_VER6;
			hbp = htontcp6(&seg, bp, &tcb->protohdr.tcp6hdr, tcb);
			if(hbp == nil) {
				freeblist(bp);
				return;
			}
		}

		/* Start the transmission timers if there is new data and we


@@ 1814,7 2076,10 @@ tcpoutput(Conv *s)
			qlock(s);
			nexterror();
		}
		ipoput(f, hbp, 0, s->ttl, s->tos);
		if(version == 4)
			ipoput4(f, hbp, 0, s->ttl, s->tos);
		else
			ipoput6(f, hbp, 0, s->ttl, s->tos);
		qlock(s);
		poperror();
	}


@@ 1832,7 2097,6 @@ tcpsendka(Conv *s)

	tcb = (Tcpctl*)s->ptcl;


	dbp = nil;
	seg.urg = 0;
	seg.source = s->lport;


@@ 1850,14 2114,26 @@ tcpsendka(Conv *s)
		dbp->wp++;
	}

	/* Build header, link data and compute cksum */
	hbp = htontcp(&seg, dbp, &tcb->protohdr, tcb);
	if(hbp == nil) {
		freeblist(dbp);
		return;
	if(isv4(s->raddr)) {
		/* Build header, link data and compute cksum */
		tcb->protohdr.tcp4hdr.vihl = IP_VER4;
		hbp = htontcp4(&seg, dbp, &tcb->protohdr.tcp4hdr, tcb);
		if(hbp == nil) {
			freeblist(dbp);
			return;
		}
		ipoput4(s->p->f, hbp, 0, s->ttl, s->tos);
	}
	else {
		/* Build header, link data and compute cksum */
		tcb->protohdr.tcp6hdr.vcf[0] = IP_VER6;
		hbp = htontcp6(&seg, dbp, &tcb->protohdr.tcp6hdr, tcb);
		if(hbp == nil) {
			freeblist(dbp);
			return;
		}
		ipoput6(s->p->f, hbp, 0, s->ttl, s->tos);
	}

	ipoput(s->p->f, hbp, 0, s->ttl, s->tos);
}

/*


@@ 2133,7 2409,7 @@ tcptrim(Tcpctl *tcb, Tcp *seg, Block **bp, ushort *length)
			seg->flags &= ~SYN;
			seg->seq++;

			if (seg->urg > 1)
			if(seg->urg > 1)
				seg->urg--;
			else
				seg->flags &= ~URG;


@@ 2144,7 2420,7 @@ tcptrim(Tcpctl *tcb, Tcp *seg, Block **bp, ushort *length)
			seg->seq += dupcnt;
			*length -= dupcnt;

			if (seg->urg > dupcnt)
			if(seg->urg > dupcnt)
				seg->urg -= dupcnt;
			else {
				seg->flags &= ~URG;


@@ 2167,61 2443,50 @@ tcptrim(Tcpctl *tcb, Tcp *seg, Block **bp, ushort *length)
void
tcpadvise(Proto *tcp, Block *bp, char *msg)
{
	Tcphdr *h;
	Tcp4hdr *h4;
	Tcp6hdr *h6;
	Tcpctl *tcb;
	uchar source[IPaddrlen];
	uchar dest[IPaddrlen];
	ushort psource, pdest;
	Conv *s, **p;

	h = (Tcphdr*)(bp->rp);
	h4 = (Tcp4hdr*)(bp->rp);
	h6 = (Tcp6hdr*)(bp->rp);

	v4tov6(dest, h->tcpdst);
	v4tov6(source, h->tcpsrc);
	psource = nhgets(h->tcpsport);
	pdest = nhgets(h->tcpdport);
	if((h4->vihl&0xF0)==IP_VER4) {
		v4tov6(dest, h4->tcpdst);
		v4tov6(source, h4->tcpsrc);
		psource = nhgets(h4->tcpsport);
		pdest = nhgets(h4->tcpdport);
	} 
	else {
		ipmove(dest, h6->tcpdst);
		ipmove(source, h6->tcpsrc);
		psource = nhgets(h6->tcpsport);
		pdest = nhgets(h6->tcpdport);
	}

	/* Look for a connection */
	qlock(tcp);
	if(strcmp(msg, "unfragmentable") == 0){
		for(p = tcp->conv; *p; p++) {
			s = *p;
			tcb = (Tcpctl*)s->ptcl;
			if(tcb->state != Closed)
			if(ipcmp(s->raddr, dest) == 0)
			if(ipcmp(s->laddr, source) == 0){
				qlock(s);
				qunlock(tcp);
				switch(tcb->state){
				case Syn_sent:
					localclose(s, msg);
					break;
				}
				qunlock(s);
				freeblist(bp);
				return;
			}
		}
	} else {
		for(p = tcp->conv; *p; p++) {
			s = *p;
			tcb = (Tcpctl*)s->ptcl;
			if(s->rport == pdest)
			if(s->lport == psource)
			if(tcb->state != Closed)
			if(ipcmp(s->raddr, dest) == 0)
			if(ipcmp(s->laddr, source) == 0){
				qlock(s);
				qunlock(tcp);
				switch(tcb->state){
				case Syn_sent:
					localclose(s, msg);
					break;
				}
				qunlock(s);
				freeblist(bp);
				return;
	for(p = tcp->conv; *p; p++) {
		s = *p;
		tcb = (Tcpctl*)s->ptcl;
		if(s->rport == pdest)
		if(s->lport == psource)
		if(tcb->state != Closed)
		if(ipcmp(s->raddr, dest) == 0)
		if(ipcmp(s->laddr, source) == 0){
			qlock(s);
			qunlock(tcp);
			switch(tcb->state){
			case Syn_sent:
				localclose(s, msg);
				break;
			}
			qunlock(s);
			freeblist(bp);
			return;
		}
	}
	qunlock(tcp);

M ip/tripmedium.c => ip/tripmedium.c +1 -1
@@ 142,7 142,7 @@ tripread(void *a)
	for(;;) {
		bp = devtab[er->mchan->type]->bread(er->mchan, ifc->maxmtu, 0);
		ifc->in++;
		ipiput(er->fs, ifc, bp);
		ipiput4(er->fs, ifc, bp);
	}

	pexit("hangup", 1);

M ip/udp.c => ip/udp.c +220 -78
@@ 6,14 6,22 @@
#include	"../port/error.h"

#include	"ip.h"
#include	"ipv6.h"


#define DPRINT if(0)print

enum
{
	UDP_PHDRSIZE	= 12,
	UDP_HDRSIZE	= 20,
	UDP_IPHDR	= 8,
	UDP_UDPHDR_SZ	= 8,

	UDP4_PHDR_OFF = 8,
	UDP4_PHDR_SZ = 12,
	UDP4_IPHDR_SZ = 20,
	UDP6_IPHDR_SZ = 40,
	UDP6_PHDR_SZ = 40,
	UDP6_PHDR_OFF = 0,

	IP_UDPPROTO	= 17,
	UDP_USEAD6	= 36,
	UDP_USEAD4	= 12,


@@ 23,8 31,8 @@ enum
	Udpmaxxmit	= 10,
};

typedef struct Udphdr Udphdr;
struct Udphdr
typedef struct Udp4hdr Udp4hdr;
struct Udp4hdr
{
	/* ip header */
	uchar	vihl;		/* Version and header length */


@@ 35,8 43,24 @@ struct Udphdr
	uchar	Unused;	
	uchar	udpproto;	/* Protocol */
	uchar	udpplen[2];	/* Header plus data length */
	uchar	udpsrc[4];	/* Ip source */
	uchar	udpdst[4];	/* Ip destination */
	uchar	udpsrc[IPv4addrlen];	/* Ip source */
	uchar	udpdst[IPv4addrlen];	/* Ip destination */

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

typedef struct Udp6hdr Udp6hdr;
struct Udp6hdr {
	uchar viclfl[4];
	uchar len[2];
	uchar nextheader;
	uchar hoplimit;
	uchar udpsrc[IPaddrlen];
	uchar udpdst[IPaddrlen];

	/* udp header */
	uchar	udpsport[2];	/* Source port */


@@ 68,6 92,8 @@ struct Udppriv
	ulong		lenerr;			/* short packet */
};

void (*etherprofiler)(char *name, int qlen);

/*
 *  protocol specific part of Conv
 */


@@ 142,12 168,15 @@ udpclose(Conv *c)

	ucb = (Udpcb*)c->ptcl;
	ucb->headers = 0;

	qunlock(c);
}

void
udpkick(Conv *c)
{
	Udphdr *uh;
	Udp4hdr *uh4;
	Udp6hdr *uh6;
	ushort rport;
	uchar laddr[IPaddrlen], raddr[IPaddrlen];
	Block *bp;


@@ 155,6 184,7 @@ udpkick(Conv *c)
	int dlen, ptcllen;
	Udppriv *upriv;
	Fs *f;
	int version;

	upriv = c->p->priv;
	f = c->p->f;


@@ 179,7 209,7 @@ udpkick(Conv *c)
		if(ipforme(f, laddr) != Runi)
			findlocalip(f, laddr, raddr);
		rport = nhgets(bp->rp);
		bp->rp += 2+2;			/* Igonore local port */
		bp->rp += 2+2;			/* Ignore local port */
		break;
	case 4:
		bp = pullupblock(bp, UDP_USEAD4);


@@ 199,45 229,97 @@ udpkick(Conv *c)
		break;
	}

	dlen = blocklen(bp);
	if(ucb->headers == 6) {
		if(memcmp(laddr, v4prefix, IPv4off) == 0 ||
		    ipcmp(laddr, IPnoaddr) == 0)
			version = 4;
		else
			version = 6;
	}
	else if(ucb->headers == 4)
		version = 4;
	else {
		if( (memcmp(c->raddr, v4prefix, IPv4off) == 0 &&
			memcmp(c->laddr, v4prefix, IPv4off) == 0)
			|| ipcmp(c->raddr, IPnoaddr) == 0)
			version = 4;
		else
			version = 6;
	}

	dlen = blocklen(bp);
	/* Make space to fit udp & ip header */
	bp = padblock(bp, UDP_IPHDR+UDP_HDRSIZE);
	if(version == 4)
		bp = padblock(bp, UDP4_IPHDR_SZ+UDP_UDPHDR_SZ);
	else
		bp = padblock(bp, UDP6_IPHDR_SZ+UDP_UDPHDR_SZ);

	if(bp == nil)
		return;

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

	ptcllen = dlen + (UDP_HDRSIZE-UDP_PHDRSIZE);
	uh->Unused = 0;
	uh->udpproto = IP_UDPPROTO;
	uh->frag[0] = 0;
	uh->frag[1] = 0;
	hnputs(uh->udpplen, ptcllen);
	switch(ucb->headers){
	case 4:
	case 6:
		v6tov4(uh->udpdst, raddr);
		hnputs(uh->udpdport, rport);
		v6tov4(uh->udpsrc, laddr);
		break;
	default:
		v6tov4(uh->udpdst, c->raddr);
		hnputs(uh->udpdport, c->rport);
		if(ipcmp(c->laddr, IPnoaddr) == 0)
			findlocalip(f, c->laddr, c->raddr);
		v6tov4(uh->udpsrc, c->laddr);
		break;
	/* fill in pseudo header and compute checksum */
	if(version == 4) 
	{
		uh4 = (Udp4hdr *)(bp->rp);
		ptcllen = dlen + UDP_UDPHDR_SZ;
		uh4->Unused = 0;
		uh4->udpproto = IP_UDPPROTO;
		uh4->frag[0] = 0;
		uh4->frag[1] = 0;
		hnputs(uh4->udpplen, ptcllen);
		if(ucb->headers == 4 || ucb->headers == 6) {
			v6tov4(uh4->udpdst, raddr);
			hnputs(uh4->udpdport, rport);
			v6tov4(uh4->udpsrc, laddr);
		} else {
			v6tov4(uh4->udpdst, c->raddr);
			hnputs(uh4->udpdport, c->rport);
			if(ipcmp(c->laddr, IPnoaddr) == 0)
				findlocalip(f, c->laddr, c->raddr);
			v6tov4(uh4->udpsrc, c->laddr);
		}
		hnputs(uh4->udpsport, c->lport);
		hnputs(uh4->udplen, ptcllen);
		uh4->udpcksum[0] = 0;
		uh4->udpcksum[1] = 0;
		hnputs(uh4->udpcksum, 
		       ptclcsum(bp, UDP4_PHDR_OFF, dlen+UDP_UDPHDR_SZ+UDP4_PHDR_SZ));
		uh4->vihl = IP_VER4;
		ipoput4(f, bp, 0, c->ttl, c->tos);
	}
	else {
		// using the v6 ip header to create pseudo header 
		// first then reset it to the normal ip header
		uh6 = (Udp6hdr *)(bp->rp);
		memset(uh6, 0, 8);
		ptcllen = dlen + UDP_UDPHDR_SZ;
		hnputl(uh6->viclfl, ptcllen);
		uh6->hoplimit = IP_UDPPROTO;
		if(ucb->headers == 6) {
			ipmove(uh6->udpdst, raddr);
			hnputs(uh6->udpdport, rport);
			ipmove(uh6->udpsrc, laddr);
		} else {
			ipmove(uh6->udpdst, c->raddr);
			hnputs(uh6->udpdport, c->rport);
			if(ipcmp(c->laddr, IPnoaddr) == 0)
				findlocalip(f, c->laddr, c->raddr);
			ipmove(uh6->udpsrc, c->laddr);
		}
		hnputs(uh6->udpsport, c->lport);
		hnputs(uh6->udplen, ptcllen);
		uh6->udpcksum[0] = 0;
		uh6->udpcksum[1] = 0;
		hnputs(uh6->udpcksum, 
		       ptclcsum(bp, UDP6_PHDR_OFF, dlen+UDP_UDPHDR_SZ+UDP6_PHDR_SZ));
		memset(uh6, 0, 8);
		uh6->viclfl[0] = IP_VER6;
		hnputs(uh6->len, ptcllen);
		uh6->nextheader = IP_UDPPROTO;

		ipoput6(f, bp, 0, c->ttl, c->tos);
	}
	hnputs(uh->udpsport, c->lport);
	hnputs(uh->udplen, ptcllen);
	uh->udpcksum[0] = 0;
	uh->udpcksum[1] = 0;

	hnputs(uh->udpcksum, ptclcsum(bp, UDP_IPHDR, dlen+UDP_HDRSIZE));

	upriv->ustats.udpOutDatagrams++;
	ipoput(f, bp, 0, c->ttl, c->tos);
}

Conv*


@@ 253,42 335,76 @@ udpincoming(Conv *c, uchar *raddr, ushort rport, uchar *laddr, ushort lport)
void
udpiput(Proto *udp, Ipifc *ifc, Block *bp)
{
	int len, olen, ottl;
	Udphdr *uh;
	int len;
	Udp4hdr *uh4;
	Udp6hdr *uh6;
	Conv *c;
	Udpcb *ucb;
	uchar raddr[IPaddrlen], laddr[IPaddrlen];
	ushort rport, lport;
	Udppriv *upriv;
	Fs *f;
	int version;

	upriv = udp->priv;
	f = udp->f;

	upriv->ustats.udpInDatagrams++;

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

	/* Put back pseudo header for checksum (remember old values for icmpnoconv()) */
	ottl = uh->Unused;
	uh->Unused = 0;
	len = nhgets(uh->udplen);
	olen = nhgets(uh->udpplen);
	hnputs(uh->udpplen, len);

	v4tov6(raddr, uh->udpsrc);
	v4tov6(laddr, uh->udpdst);
	lport = nhgets(uh->udpdport);
	rport = nhgets(uh->udpsport);

	if(nhgets(uh->udpcksum)) {
		if(ptclcsum(bp, UDP_IPHDR, len+UDP_PHDRSIZE)) {
	uh4 = (Udp4hdr*)(bp->rp);
	version = ((uh4->vihl&0xF0)==IP_VER6) ? 6 : 4;

	/* Put back pseudo header for checksum 
	 * (remember old values for icmpnoconv()) */
	if(version == 4) {
		int ottl, olen;
		ottl = uh4->Unused;
		uh4->Unused = 0;
		len = nhgets(uh4->udplen);
		olen = nhgets(uh4->udpplen);
		hnputs(uh4->udpplen, len);

		v4tov6(raddr, uh4->udpsrc);
		v4tov6(laddr, uh4->udpdst);
		lport = nhgets(uh4->udpdport);
		rport = nhgets(uh4->udpsport);

		if(nhgets(uh4->udpcksum)) {
			if(ptclcsum(bp, UDP4_PHDR_OFF, len+UDP4_PHDR_SZ)) {
				upriv->ustats.udpInErrors++;
				netlog(f, Logudp, "udp: checksum error %I\n", raddr);
				DPRINT("udp: checksum error %I\n", raddr);
				freeblist(bp);
				return;
			}
		}
		uh4->Unused = ottl;
		hnputs(uh4->udpplen, olen);
	}
	else {
		int ottl, oviclfl, olen;
		uh6 = (Udp6hdr*)(bp->rp);
		len = nhgets(uh6->udplen);
		oviclfl = nhgetl(uh6->viclfl);
		olen = nhgets(uh6->len);
		ottl = uh6->hoplimit;
		ipmove(raddr, uh6->udpsrc);
		ipmove(laddr, uh6->udpdst);
		lport = nhgets(uh6->udpdport);
		rport = nhgets(uh6->udpsport);
		memset(uh6, 0, 8);
		hnputl(uh6->viclfl, len);
		uh6->hoplimit = IP_UDPPROTO;
		if(ptclcsum(bp, UDP6_PHDR_OFF, len+UDP6_PHDR_SZ)) {
			upriv->ustats.udpInErrors++;
			netlog(f, Logudp, "udp: checksum error %I\n", raddr);
			DPRINT("udp: checksum error %I\n", raddr);
			freeblist(bp);
			return;
		}
		hnputl(uh6->viclfl, oviclfl);
		hnputs(uh6->len, olen);
		uh6->nextheader = IP_UDPPROTO;
		uh6->hoplimit = ottl;
	}

	qlock(udp);


@@ 299,10 415,15 @@ udpiput(Proto *udp, Ipifc *ifc, Block *bp)
		upriv->ustats.udpNoPorts++;
		qunlock(udp);
		netlog(f, Logudp, "udp: no conv %I!%d -> %I!%d\n", raddr, rport,
			laddr, lport);
		uh->Unused = ottl;
		hnputs(uh->udpplen, olen);
		icmpnoconv(f, bp);
		       laddr, lport);

		if(version == 4) 
			icmpnoconv(f, bp);
		else {
print("udpiput: no conv %I!%d -> %I!%d\n", raddr, rport, laddr, lport);
			icmphostunr(f, ifc, bp, icmp6_port_unreach, 0);
		}

		freeblist(bp);
		return;
	}


@@ 311,8 432,12 @@ udpiput(Proto *udp, Ipifc *ifc, Block *bp)
	if(c->state == Announced){
		if(ucb->headers == 0){
			/* create a new conversation */
			if(ipforme(f, laddr) != Runi)
				v4tov6(laddr, ifc->lifc->local);
			if(ipforme(f, laddr) != Runi) {
				if(version == 4)
					v4tov6(laddr, ifc->lifc->local);
				else
					ipmove(laddr, ifc->lifc->local);
			}
			c = Fsnewcall(c, raddr, rport, laddr, lport);
			if(c == nil){
				qunlock(udp);


@@ 330,18 455,21 @@ udpiput(Proto *udp, Ipifc *ifc, Block *bp)
	/*
	 * Trim the packet down to data size
	 */
	len -= (UDP_HDRSIZE-UDP_PHDRSIZE);
	bp = trimblock(bp, UDP_IPHDR+UDP_HDRSIZE, len);
	len -= UDP_UDPHDR_SZ;
	if(version == 4)
		bp = trimblock(bp, UDP4_IPHDR_SZ+UDP_UDPHDR_SZ, len);
	else
		bp = trimblock(bp, UDP6_IPHDR_SZ+UDP_UDPHDR_SZ, len);
	if(bp == nil){
		qunlock(c);
		netlog(f, Logudp, "udp: len err %I.%d -> %I.%d\n", raddr, rport,
			laddr, lport);
		       laddr, lport);
		upriv->lenerr++;
		return;
	}

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

	switch(ucb->headers){
	case 6:


@@ 374,7 502,7 @@ udpiput(Proto *udp, Ipifc *ifc, Block *bp)
	if(qfull(c->rq)){
		qunlock(c);
		netlog(f, Logudp, "udp: qfull %I.%d -> %I.%d\n", raddr, rport,
			laddr, lport);
		       laddr, lport);
		freeblist(bp);
		return;
	}


@@ 405,17 533,30 @@ udpctl(Conv *c, char **f, int n)
void
udpadvise(Proto *udp, Block *bp, char *msg)
{
	Udphdr *h;
	Udp4hdr *h4;
	Udp6hdr *h6;
	uchar source[IPaddrlen], dest[IPaddrlen];
	ushort psource, pdest;
	Conv *s, **p;
	int version;

	h4 = (Udp4hdr*)(bp->rp);
	version = ((h4->vihl&0xF0)==IP_VER6) ? 6 : 4;

	if(version == 4) {
		v4tov6(dest, h4->udpdst);
		v4tov6(source, h4->udpsrc);
		psource = nhgets(h4->udpsport);
		pdest = nhgets(h4->udpdport);
	} else {
		h6 = (Udp6hdr*)(bp->rp);
		ipmove(dest, h6->udpdst);
		ipmove(source, h6->udpsrc);
		psource = nhgets(h6->udpsport);
		pdest = nhgets(h6->udpdport);
	}

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

	v4tov6(dest, h->udpdst);
	v4tov6(source, h->udpsrc);
	psource = nhgets(h->udpsport);
	pdest = nhgets(h->udpdport);
print("udpadvise: looking for s %I sp %d d %I dp %d\n", source, (int)psource, dest, (int)pdest);

	/* Look for a connection */
	qlock(udp);


@@ 427,6 568,7 @@ udpadvise(Proto *udp, Block *bp, char *msg)
		if(ipcmp(s->laddr, source) == 0){
			qlock(s);
			qunlock(udp);
print("udpadvise: found, hanging up\n");
			qhangup(s->rq, msg);
			qhangup(s->wq, msg);
			qunlock(s);

M port/devnmouse.c => port/devnmouse.c +0 -2
@@ 32,8 32,6 @@ enum{
	Qdir,
	Qcursor,
	Qmousepoint,
	Qmousein,
	Qmousectl,
};

static Dirtab mousedir[]={