shithub: riscv

Download patch

ref: 20b9326daddd52ffe534a968fb596af1674f53cc
parent: c80d94304d0b9946c822e073b637760a0894522a
author: cinap_lenrek <[email protected]>
date: Sun Apr 22 14:54:13 EDT 2018

devip: fix ipv6 icmp unreachable handling, fix retransmit, fix ifc locking, remove tentative check

--- a/sys/src/9/ip/arp.c
+++ b/sys/src/9/ip/arp.c
@@ -293,7 +293,7 @@
 }
 
 int
-arpenter(Fs *fs, int version, uchar *ip, uchar *mac, int n, uchar *src, int refresh)
+arpenter(Fs *fs, int version, uchar *ip, uchar *mac, int n, uchar *ia, int refresh)
 {
 	Arp *arp;
 	Route *r;
@@ -300,26 +300,31 @@
 	Arpent *a, *f, **l;
 	Ipifc *ifc;
 	Block *bp, *next;
-	Medium *m;
 	uchar v6ip[IPaddrlen];
 
 	arp = fs->arp;
 	switch(version){
 	case V4:
-		r = v4lookup(fs, ip, src, nil);
+		r = v4lookup(fs, ip, ia, nil);
 		v4tov6(v6ip, ip);
 		ip = v6ip;
 		break;
 	case V6:
-		r = v6lookup(fs, ip, src, nil);
+		r = v6lookup(fs, ip, ia, nil);
 		break;
 	default:
 		panic("arpenter: version %d", version);
 		return -1;	/* to supress warnings */
 	}
-	if(r == nil || (ifc = r->ifc) == nil || (m = ifc->m) == nil || m->maclen != n || m->maclen == 0)
+	if(r == nil || (ifc = r->ifc) == nil)
 		return -1;
 
+	rlock(ifc);
+	if(ifc->m == nil || ifc->m->maclen != n || ifc->m->maclen == 0){
+		runlock(ifc);
+		return -1;
+	}
+
 	qlock(arp);
 	for(a = arp->hash[haship(ip)]; a != nil; a = a->hash){
 		if(a->state != AWAIT && a->state != AOK)
@@ -351,28 +356,17 @@
 			qunlock(arp);
 
 			while(bp != nil){
-				if(!canrlock(ifc)){
-					freeblistchain(bp);
-					break;
-				}
-				if(ifc->m != m){
-					runlock(ifc);
-					freeblistchain(bp);
-					break;
-				}
 				next = bp->list;
 				bp->list = nil;
 				if(waserror()){
-					runlock(ifc);
 					freeblistchain(next);
 					break;
 				}
-				m->bwrite(ifc, concatblock(bp), version, ip);
-				runlock(ifc);
+				ifc->m->bwrite(ifc, concatblock(bp), version, ip);
 				poperror();
 				bp = next;
 			}
-
+			runlock(ifc);
 			return 1;
 		}
 	}
@@ -383,8 +377,9 @@
 		a->ctime = NOW;
 		memmove(a->mac, mac, n);
 	}
-
 	qunlock(arp);
+	runlock(ifc);
+
 	return refresh == 0;
 }
 
@@ -396,7 +391,7 @@
 	Arpent *a, *x;
 	Medium *m;
 	char *f[5], buf[256];
-	uchar ip[IPaddrlen], src[IPaddrlen], mac[MAClen];
+	uchar ip[IPaddrlen], ia[IPaddrlen], mac[MAClen];
 
 	arp = fs->arp;
 
@@ -438,7 +433,7 @@
 				error(Ebadip);
 			if((n = parsemac(mac, f[2], sizeof(mac))) <= 0)
 				error(Ebadarp);
-			findlocalip(fs, src, ip);
+			findlocalip(fs, ia, ip);
 			break;
 		case 4:
 			m = ipfindmedium(f[1]);
@@ -448,7 +443,7 @@
 				error(Ebadip);
 			if((n = parsemac(mac, f[3], sizeof(mac))) != m->maclen)
 				error(Ebadarp);
-			findlocalip(fs, src, ip);
+			findlocalip(fs, ia, ip);
 			break;
 		case 5:
 			m = ipfindmedium(f[1]);
@@ -458,11 +453,11 @@
 				error(Ebadip);
 			if((n = parsemac(mac, f[3], sizeof(mac))) != m->maclen)
 				error(Ebadarp);
-			if(parseip(src, f[4]) == -1)
+			if(parseip(ia, f[4]) == -1)
 				error(Ebadip);
 			break;
 		}
-		if(arpenter(fs, V6, ip, mac, n, src, 0) <= 0)
+		if(arpenter(fs, V6, ip, mac, n, ia, 0) <= 0)
 			error("destination unreachable");
 	} else if(strcmp(f[0], "del") == 0){
 		if (n != 2)
@@ -495,8 +490,8 @@
 int
 arpread(Arp *arp, char *s, ulong offset, int len)
 {
-	uchar ip[IPaddrlen], src[IPaddrlen];
-	char mac[2*MAClen+1], *p, *state;
+	char mac[2*MAClen+1], *state, *mname, *p;
+	uchar ip[IPaddrlen], ia[IPaddrlen];
 	Ipifc *ifc;
 	Arpent *a;
 	long n, o;
@@ -504,18 +499,25 @@
 	p = s;
 	o = -offset;
 	for(a = arp->cache; len > 0 && a < &arp->cache[NCACHE]; a++){
-		if(a->state == 0 || (ifc = a->ifc) == nil || a->ifcid != ifc->ifcid)
+		if(a->state == 0 || (ifc = a->ifc) == nil)
 			continue;
 
+		rlock(ifc);
 		qlock(arp);
 		state = arpstate[a->state];
 		ipmove(ip, a->ip);
+		if(ifc->m == nil || a->ifcid != ifc->ifcid || !ipv6local(ifc, ia, ip)){
+			qunlock(arp);
+			runlock(ifc);
+			continue;
+		}
+		mname = ifc->m->name;
 		convmac(mac, a->mac, ifc->m->maclen);
 		qunlock(arp);
+		runlock(ifc);
 
-		ipv6local(ifc, src, ip);
 		n = snprint(p, len, "%-6.6s %-4.4s %-40.40I %-16.16s %I\n",
-			ifc->m->name, state, ip, mac, src);
+			mname, state, ip, mac, ia);
 		if(o < 0) {
 			if(n > -o)
 				memmove(p, p-o, n+o);
@@ -549,17 +551,20 @@
 	if(!ipv6local(ifc, src, targ))
 		return;
 send:
-	icmpns(f, src, SRC_UNI, targ, TARG_MULTI, ifc->mac);
+	if(!waserror()){
+		icmpns(f, src, SRC_UNI, targ, TARG_MULTI, ifc->mac);
+		poperror();
+	}
 }
 
-int
+long
 rxmitsols(Arp *arp)
 {
 	Block *next, *xp;
 	Arpent *a, *b, **l;
-	Fs *f;
-	Ipifc *ifc = nil;
+	Ipifc *ifc;
 	long nrxt;
+	Fs *f;
 
 	qlock(arp);
 	f = arp->f;
@@ -573,6 +578,7 @@
 	if(nrxt > 3*ReTransTimer/4) 
 		goto dodrops; 		/* return nrxt; */
 
+	ifc = nil;
 	for(; a != nil; a = a->nextrxt){
 		ifc = a->ifc;
 		if(a->rxtsrem > 0 && ifc != nil && canrlock(ifc)){
@@ -628,8 +634,19 @@
 	qunlock(arp);
 
 	for(; xp != nil; xp = next){
+		Ip6hdr *eh;
+		Route *r;
+
 		next = xp->list;
-		icmphostunr6(f, ifc, xp, Icmp6_adr_unreach, 1);
+		eh = (Ip6hdr*)xp->rp;
+		r = v6lookup(f, eh->src, eh->dst, nil);
+		if(r != nil && (ifc = r->ifc) != nil && canrlock(ifc)){
+			if(!waserror()){
+				icmphostunr6(f, ifc, xp, Icmp6_adr_unreach, (r->type & Runi) != 0);
+				poperror();
+			}
+			runlock(ifc);
+		}
 		freeblist(xp);
 	}
 
--- a/sys/src/9/ip/esp.c
+++ b/sys/src/9/ip/esp.c
@@ -472,7 +472,7 @@
 
 	ecb = c->ptcl;
 	/* too hard to do decryption/authentication on block lists */
-	if(bp->next)
+	if(bp->next != nil)
 		bp = concatblock(bp);
 
 	if(BLEN(bp) < vers.hdrlen + ecb->espivlen + Esptaillen + ecb->ahlen) {
--- a/sys/src/9/ip/ethermedium.c
+++ b/sys/src/9/ip/ethermedium.c
@@ -33,7 +33,7 @@
 static void	etherbwrite(Ipifc *ifc, Block *bp, int version, uchar *ip);
 static void	etheraddmulti(Ipifc *ifc, uchar *a, uchar *ia);
 static void	etherremmulti(Ipifc *ifc, uchar *a, uchar *ia);
-static void	etherareg(Fs *f, Ipifc *ifc, uchar *ip, uchar *proxy);
+static void	etherareg(Fs *f, Ipifc *ifc, Iplifc *lifc, uchar *ip);
 static Block*	multicastarp(Fs *f, Arpent *a, Medium*, uchar *mac);
 static void	sendarp(Ipifc *ifc, Arpent *a);
 static int	multicastea(uchar *ea, uchar *ip);
@@ -219,8 +219,8 @@
 	poperror();
 
 	kproc("etherread4", etherread4, ifc);
-	kproc("recvarpproc", recvarpproc, ifc);
 	kproc("etherread6", etherread6, ifc);
+	kproc("recvarpproc", recvarpproc, ifc);
 }
 
 /*
@@ -231,21 +231,19 @@
 {
 	Etherrock *er = ifc->arg;
 
-	if(er->read4p)
+	if(er->read4p != nil)
 		postnote(er->read4p, 1, "unbind", 0);
-	if(er->read6p)
+	if(er->read6p != nil)
 		postnote(er->read6p, 1, "unbind", 0);
-	if(er->arpp)
+	if(er->arpp != nil)
 		postnote(er->arpp, 1, "unbind", 0);
 
 	/* wait for readers to die */
-	while(er->arpp != 0 || er->read4p != 0 || er->read6p != 0)
+	while(er->arpp != nil || er->read4p != nil || er->read6p != nil)
 		tsleep(&up->sleep, return0, 0, 300);
 
 	if(er->mchan4 != nil)
 		cclose(er->mchan4);
-	if(er->achan != nil)
-		cclose(er->achan);
 	if(er->cchan4 != nil)
 		cclose(er->cchan4);
 	if(er->mchan6 != nil)
@@ -252,6 +250,8 @@
 		cclose(er->mchan6);
 	if(er->cchan6 != nil)
 		cclose(er->cchan6);
+	if(er->achan != nil)
+		cclose(er->achan);
 
 	free(er);
 }
@@ -329,7 +329,7 @@
 	er = ifc->arg;
 	er->read4p = up;	/* hide identity under a rock for unbind */
 	if(waserror()){
-		er->read4p = 0;
+		er->read4p = nil;
 		pexit("hangup", 1);
 	}
 	for(;;){
@@ -369,7 +369,7 @@
 	er = ifc->arg;
 	er->read6p = up;	/* hide identity under a rock for unbind */
 	if(waserror()){
-		er->read6p = 0;
+		er->read6p = nil;
 		pexit("hangup", 1);
 	}
 	for(;;){
@@ -571,6 +571,11 @@
 	if(ebp == nil)
 		return;
 
+	if(!canrlock(ifc)){
+		freeb(ebp);
+		return;
+	}
+
 	e = (Etherarp*)ebp->rp;
 	switch(nhgets(e->op)) {
 	default:
@@ -647,8 +652,14 @@
 		memmove(r->s, ifc->mac, sizeof(r->s));
 		rbp->wp += n;
 
+		runlock(ifc);
+		freeb(ebp);
+
 		devtab[er->achan->type]->bwrite(er->achan, rbp, 0);
+		return;
 	}
+
+	runlock(ifc);
 	freeb(ebp);
 }
 
@@ -660,7 +671,7 @@
 
 	er->arpp = up;
 	if(waserror()){
-		er->arpp = 0;
+		er->arpp = nil;
 		pexit("hangup", 1);
 	}
 	for(;;)
@@ -745,10 +756,10 @@
 }
 
 static void
-etherareg(Fs *f, Ipifc *ifc, uchar *ip, uchar *proxy)
+etherareg(Fs *f, Ipifc *ifc, Iplifc *lifc, uchar *ip)
 {
 	static char tdad[] = "dad6";
-	uchar mcast[IPaddrlen];
+	uchar a[IPaddrlen];
 
 	if(ipcmp(ip, IPnoaddr) == 0)
 		return;
@@ -758,16 +769,25 @@
 		return;
 	}
 
-	if(!iptentative(f, ip)){
-		icmpna(f, proxy, v6allnodesL, ip, ifc->mac, 1<<5);
+	if((lifc->type&Rv4) != 0)
 		return;
+
+	if(!lifc->tentative){
+		icmpna(f, lifc->local, v6allnodesL, ip, ifc->mac, 1<<5);
+		return;
 	}
 
-	/* temporarily add route for duplicate address detection */
-	ipv62smcast(mcast, ip);
-	addroute(f, mcast, IPallbits, v6Unspecified, IPallbits, ip, Rmulti, ifc, tdad);
+	if(ipcmp(lifc->local, ip) != 0)
+		return;
 
+	/* temporarily add route for duplicate address detection */
+	ipv62smcast(a, ip);
+	addroute(f, a, IPallbits, v6Unspecified, IPallbits, ip, Rmulti, ifc, tdad);
+	if(waserror()){
+		remroute(f, a, IPallbits, v6Unspecified, IPallbits, ip, Rmulti, ifc, tdad);
+		nexterror();
+	}
 	icmpns(f, 0, SRC_UNSPEC, ip, TARG_MULTI, ifc->mac);
-
-	remroute(f, mcast, IPallbits, v6Unspecified, IPallbits, ip, Rmulti, ifc, tdad);
+	poperror();
+	remroute(f, a, IPallbits, v6Unspecified, IPallbits, ip, Rmulti, ifc, tdad);
 }
--- a/sys/src/9/ip/ip.c
+++ b/sys/src/9/ip/ip.c
@@ -99,6 +99,7 @@
 	IP *ip;
 
 	ip = smalloc(sizeof(IP));
+	ip->stats[DefaultTTL] = MAXTTL;
 	initfrag(ip, 100);
 	f->ip = ip;
 
@@ -362,7 +363,7 @@
 	if(notforme) {
 		Route *r;
 		Routehint rh;
-		Ipifc *toifc;
+		Ipifc *nifc;
 
 		if(!ip->iprouting)
 			goto drop;
@@ -370,8 +371,8 @@
 		/* don't forward to source's network */
 		rh.r = nil;
 		r = v4lookup(f, h->dst, h->src, &rh);
-		if(r == nil || (toifc = r->ifc) == nil
-		|| (toifc == ifc && !ifc->reflect)){
+		if(r == nil || (nifc = r->ifc) == nil
+		|| (nifc == ifc && !ifc->reflect)){
 			ip->stats[OutDiscards]++;
 			goto drop;
 		}
@@ -385,7 +386,7 @@
 		}
 
 		/* reassemble if the interface expects it */
-		if(toifc->reassemble){
+		if(nifc->reassemble){
 			frag = nhgets(h->frag);
 			if(frag & ~IP_DF) {
 				h->tos = 0;
@@ -441,8 +442,6 @@
 	int i;
 
 	ip = f->ip;
-	ip->stats[DefaultTTL] = MAXTTL;
-
 	p = buf;
 	e = p+len;
 	for(i = 0; i < Nipstats; i++)
@@ -467,7 +466,7 @@
 	/*
 	 *  block lists are too hard, pullupblock into a single block
 	 */
-	if(bp->next){
+	if(bp->next != nil){
 		bp = pullupblock(bp, blocklen(bp));
 		ih = (Ip4hdr*)(bp->rp);
 	}
@@ -477,7 +476,7 @@
 	/*
 	 *  find a reassembly queue for this fragment
 	 */
-	for(f = ip->flisthead4; f; f = fnext){
+	for(f = ip->flisthead4; f != nil; f = fnext){
 		fnext = f->next;	/* because ipfragfree4 changes the list */
 		if(f->src == src && f->dst == dst && f->id == id)
 			break;
@@ -536,7 +535,7 @@
 	}
 
 	/* Check overlap of a previous fragment - trim away as necessary */
-	if(prev) {
+	if(prev != nil) {
 		ovlap = BKFG(prev)->foff + BKFG(prev)->flen - BKFG(bp)->foff;
 		if(ovlap > 0) {
 			if(ovlap >= BKFG(bp)->flen) {
@@ -553,11 +552,11 @@
 	*l = bp;
 
 	/* Check to see if succeeding segments overlap */
-	if(bp->next) {
+	if(bp->next != nil) {
 		l = &bp->next;
 		fend = BKFG(bp)->foff + BKFG(bp)->flen;
 		/* Take completely covered segments out */
-		while(*l) {
+		while(*l != nil) {
 			ovlap = fend - BKFG(*l)->foff;
 			if(ovlap <= 0)
 				break;
@@ -581,7 +580,7 @@
 	 *  without IP_MF set, we're done.
 	 */
 	pktposn = 0;
-	for(bl = f->blist; bl; bl = bl->next) {
+	for(bl = f->blist; bl != nil; bl = bl->next) {
 		if(BKFG(bl)->foff != pktposn)
 			break;
 		if((BLKIP(bl)->frag[0]&(IP_MF>>8)) == 0) {
@@ -592,7 +591,7 @@
 			/* Pullup all the fragment headers and
 			 * return a complete packet
 			 */
-			for(bl = bl->next; bl; bl = bl->next) {
+			for(bl = bl->next; bl != nil; bl = bl->next) {
 				fragsize = BKFG(bl)->flen;
 				len += fragsize;
 				bl->rp += IP4HDR;
@@ -622,7 +621,7 @@
 {
 	Fragment4 *fl, **l;
 
-	if(frag->blist)
+	if(frag->blist != nil)
 		freeblist(frag->blist);
 
 	frag->src = 0;
@@ -630,7 +629,7 @@
 	frag->blist = nil;
 
 	l = &ip->flisthead4;
-	for(fl = *l; fl; fl = fl->next) {
+	for(fl = *l; fl != nil; fl = fl->next) {
 		if(fl == frag) {
 			*l = frag->next;
 			break;
@@ -653,7 +652,7 @@
 
 	while(ip->fragfree4 == nil) {
 		/* free last entry on fraglist */
-		for(f = ip->flisthead4; f->next; f = f->next)
+		for(f = ip->flisthead4; f->next != nil; f = f->next)
 			;
 		ipfragfree4(ip, f);
 	}
--- a/sys/src/9/ip/ip.h
+++ b/sys/src/9/ip/ip.h
@@ -243,7 +243,7 @@
 	void	(*pktin)(Fs *f, Ipifc *ifc, Block *bp);
 
 	/* address resolution */
-	void	(*areg)(Fs *f, Ipifc *ifc, uchar *ip, uchar *proxy);			/* register */
+	void	(*areg)(Fs *f, Ipifc *ifc, Iplifc *lifc, uchar *ip);
 
 	/* v6 address generation */
 	void	(*pref2addr)(uchar *pref, uchar *ea);
@@ -608,7 +608,7 @@
 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 int	arpenter(Fs*, int version, uchar *ip, uchar *mac, int n, uchar *src, int norefresh);
+extern int	arpenter(Fs*, int version, uchar *ip, uchar *mac, int n, uchar *ia, int norefresh);
 extern void	ndpsendsol(Fs*, Ipifc*, Arpent*);
 
 /*
@@ -654,8 +654,6 @@
 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 *ip);
 extern int	ipismulticast(uchar *ip);
 extern Ipifc*	findipifc(Fs*, uchar *local, uchar *remote, int type);
 extern Ipifc*	findipifcstr(Fs *f, char *s);
--- a/sys/src/9/ip/ipifc.c
+++ b/sys/src/9/ip/ipifc.c
@@ -61,6 +61,7 @@
 
 static void	addselfcache(Fs *f, Ipifc *ifc, Iplifc *lifc, uchar *a, int type);
 static void	remselfcache(Fs *f, Ipifc *ifc, Iplifc *lifc, uchar *a);
+static void	ipifcregisteraddr(Fs*, Ipifc*, uchar *, uchar *);
 static void	ipifcregisterproxy(Fs*, Ipifc*, uchar*, int);
 static char*	ipifcremlifc(Ipifc*, Iplifc**);
 
@@ -218,10 +219,6 @@
 	/* disassociate logical interfaces (before zeroing ifc->arg) */
 	while(ifc->lifc != nil){
 		err = ipifcremlifc(ifc, &ifc->lifc);
-		/*
-		 * note: err non-zero means lifc not found,
-		 * which can't happen in this case.
-		 */
 		if(err != nil)
 			error(err);
 	}
@@ -273,7 +270,7 @@
 		ifc->in, ifc->out, ifc->inerr, ifc->outerr);
 
 	rlock(ifc);
-	for(lifc = ifc->lifc; lifc && n > m; lifc = lifc->next)
+	for(lifc = ifc->lifc; lifc != nil && n > m; lifc = lifc->next)
 		m += snprint(state+m, n - m, slineformat, lifc->local,
 			lifc->mask, lifc->remote, lifc->validlt, lifc->preflt);
 	if(ifc->lifc == nil)
@@ -291,9 +288,8 @@
 	int m;
 
 	ifc = (Ipifc*)c->ptcl;
-	m = 0;
-
 	rlock(ifc);
+	m = 0;
 	for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
 		m += snprint(state+m, n - m, "%-40.40I ->", lifc->local);
 		for(link = lifc->link; link != nil; link = link->lifclink)
@@ -405,14 +401,9 @@
 	uchar bcast[IPaddrlen], net[IPaddrlen];
 	Iplifc *lifc, **l;
 	int i, type, mtu;
-	Medium *m;
 	Fs *f;
 
-	if((m = ifc->m) == nil)
-		return "ipifc not yet bound to device";
-
-	f = ifc->conv->p->f;
-
+	mtu = 0;
 	type = Rifc;
 	memset(ip, 0, IPaddrlen);
 	memset(mask, 0, IPaddrlen);
@@ -424,8 +415,6 @@
 		/* fall through */
 	case 5:
 		mtu = strtoul(argv[4], 0, 0);
-		if(mtu >= m->mintu && mtu <= m->maxtu)
-			ifc->maxtu = mtu;
 		/* fall through */
 	case 4:
 		if (parseip(ip, argv[1]) == -1 || parseip(rem, argv[3]) == -1)
@@ -462,11 +451,18 @@
 	}
 
 	wlock(ifc);
+	if(ifc->m == nil)
+		return "ipifc not yet bound to device";
+
+	f = ifc->conv->p->f;
 	if(waserror()){
 		wunlock(ifc);
-		nexterror();
+		return up->errstr;
 	}
 
+	if(mtu >= ifc->m->mintu && mtu <= ifc->m->maxtu)
+		ifc->maxtu = mtu;
+
 	/* ignore if this is already a local address for this ifc */
 	if((lifc = iplocalonifc(ifc, ip)) != nil){
 		if(lifcp != nil) {
@@ -576,9 +572,7 @@
 	wunlock(ifc);
 	poperror();
 
-	/* register the address on this network for address resolution */
-	if(m->areg != nil)
-		(*m->areg)(f, ifc, ip, ip);
+	ipifcregisteraddr(f, ifc, ip, ip);
 
 	return nil;
 }
@@ -701,7 +695,7 @@
 	wunlock(ifc);
 
 	err = ipifcadd(ifc, argv, argc, 0, nil);
-	if(err)
+	if(err != nil)
 		return err;
 
 	Fsconnected(c, nil);
@@ -1069,18 +1063,6 @@
 	return m;
 }
 
-int
-iptentative(Fs *f, uchar *addr)
-{
- 	Ipself *p;
-
-	for(p = f->self->hash[hashipa(addr)]; p != nil; p = p->next)
-		if(ipcmp(addr, p->a) == 0)
-			return p->link->lifc->tentative;
-
-	return 0;
-}
-
 /*
  *  returns
  *	0		- no match
@@ -1112,27 +1094,26 @@
 findipifc(Fs *f, uchar *local, uchar *remote, int type)
 {
 	uchar gnet[IPaddrlen];
+	int spec, xspec;
 	Ipifc *ifc, *x;
 	Iplifc *lifc;
-	Conv **cp, **e;
-	int spec, xspec;
+	Conv **cp;
 
 	x = nil;
 	xspec = 0;
-
-	/* find most specific match */
-	e = &f->ipifc->conv[f->ipifc->nc];
-	for(cp = f->ipifc->conv; cp < e; cp++){
-		if(*cp == nil)
-			continue;
+	for(cp = f->ipifc->conv; *cp != nil; cp++){
 		ifc = (Ipifc*)(*cp)->ptcl;
+		rlock(ifc);
 		for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
 			if(type & Runi){
-				if(ipcmp(remote, lifc->local) == 0)
+				if(ipcmp(remote, lifc->local) == 0){
+				Found:
+					runlock(ifc);
 					return ifc;
+				}
 			} else if(type & (Rbcast|Rmulti)) {
 				if(ipcmp(local, lifc->local) == 0)
-					return ifc;
+					goto Found;
 			}
 			maskip(remote, lifc->mask, gnet);
 			if(ipcmp(gnet, lifc->net) == 0){
@@ -1143,6 +1124,7 @@
 				}
 			}
 		}
+		runlock(ifc);
 	}
 	return x;
 }
@@ -1159,7 +1141,7 @@
 	if(p > s && *p == '\0'){
 		if(x < 0)
 			return nil;
-		if(x < f->ipifc->nc && (c = f->ipifc->conv[x]) != nil)
+		if(x < f->ipifc->nc && (c = f->ipifc->conv[x]) != nil && ipifcinuse(c))
 			return (Ipifc*)c->ptcl;
 	}
 	if(parseip(ip, s) != -1)
@@ -1167,35 +1149,36 @@
 	return nil;
 }
 
+/*
+ *  find "best" (global > link local > unspecified)
+ *  local address; address must be current.
+ */
 static void
 findprimaryipv6(Fs *f, uchar *local)
 {
 	int atype, atypel;
-	Conv **cp, **e;
-	Ipifc *ifc;
 	Iplifc *lifc;
+	Ipifc *ifc;
+	Conv **cp;
 
 	ipmove(local, v6Unspecified);
 	atype = unspecifiedv6;
 
-	/*
-	 * find "best" (global > 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 == nil)
-			continue;
+	for(cp = f->ipifc->conv; *cp != nil; cp++){
 		ifc = (Ipifc*)(*cp)->ptcl;
+		rlock(ifc);
 		for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
 			atypel = v6addrtype(lifc->local);
 			if(atypel > atype && v6addrcurr(lifc)) {
 				ipmove(local, lifc->local);
 				atype = atypel;
-				if(atype == globalv6)
+				if(atype == globalv6){
+					runlock(ifc);
 					return;
+				}
 			}
 		}
+		runlock(ifc);
 	}
 }
 
@@ -1205,20 +1188,22 @@
 static void
 findprimaryipv4(Fs *f, uchar *local)
 {
-	Conv **cp, **e;
-	Ipifc *ifc;
 	Iplifc *lifc;
+	Ipifc *ifc;
+	Conv **cp;
 
 	/* find first ifc local address */
-	e = &f->ipifc->conv[f->ipifc->nc];
-	for(cp = f->ipifc->conv; cp < e; cp++){
-		if(*cp == nil)
-			continue;
+	for(cp = f->ipifc->conv; *cp != nil; cp++){
 		ifc = (Ipifc*)(*cp)->ptcl;
-		if((lifc = ifc->lifc) != nil && (lifc->type & Rv4) != 0){
-			ipmove(local, lifc->local);
-			return;
+		rlock(ifc);
+		for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
+			if((lifc->type & Rv4) != 0){
+				ipmove(local, lifc->local);
+				runlock(ifc);
+				return;
+			}
 		}
+		runlock(ifc);
 	}
 	ipmove(local, IPnoaddr);
 }
@@ -1310,41 +1295,47 @@
 findlocalip(Fs *f, uchar *local, uchar *remote)
 {
 	Route *r;
-	Ipifc *ifc;
 	Iplifc *lifc;
-	Conv **cp, **e;
+	Ipifc *ifc, *nifc;
+	Conv **cp;
 
-	qlock(f->ipifc);
-	e = &f->ipifc->conv[f->ipifc->nc];
-	for(cp = f->ipifc->conv; cp < e; cp++){
-		if(*cp == nil)
-			continue;
+	for(cp = f->ipifc->conv; *cp != nil; cp++){
 		ifc = (Ipifc*)(*cp)->ptcl;
+		rlock(ifc);
 		for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
 			if(lifc->tentative)
 				continue;
+
 			r = v6lookup(f, remote, lifc->local, nil);
-			if(r == nil || (ifc = r->ifc) == nil)
+			if(r == nil || (nifc = r->ifc) == nil)
 				continue;
 			if(r->type & Runi){
 				ipmove(local, remote);
-				goto out;
+				runlock(ifc);
+				return;
 			}
+			if(nifc != ifc) rlock(nifc);
 			if((r->type & (Rifc|Rbcast|Rmulti|Rv4)) == Rv4){
 				ipmove(local, v4prefix);
-				if(ipv4local(ifc, local+IPv4off, r->v4.gate))
-					goto out;
+				if(ipv4local(nifc, local+IPv4off, r->v4.gate)){
+					if(nifc != ifc) runlock(nifc);
+					runlock(ifc);
+					return;
+				}
 			}
-			if(ipv6local(ifc, local, remote))
-				goto out;
+			if(ipv6local(nifc, local, remote)){
+				if(nifc != ifc) runlock(nifc);
+				runlock(ifc);
+				return;
+			}
+			if(nifc != ifc) runlock(nifc);
 		}
+		runlock(ifc);
 	}
 	if(isv4(remote))
 		findprimaryipv4(f, local);
 	else
 		findprimaryipv6(f, local);
-out:
-	qunlock(f->ipifc);
 }
 
 
@@ -1408,21 +1399,7 @@
 		return V6;
 	return 0;
 }
-int
-ipisbm(uchar *ip)
-{
-	if(isv4(ip)){
-		if(ip[IPv4off] >= 0xe0 && ip[IPv4off] < 0xf0)
-			return V4;
-		else if(ipcmp(ip, IPv4bcast) == 0)
-			return V4;
-	}
-	else if(ip[0] == 0xff)
-		return V6;
-	return 0;
-}
 
-
 /*
  *  add a multicast address to an interface, called with c->car locked
  */
@@ -1430,13 +1407,10 @@
 ipifcaddmulti(Conv *c, uchar *ma, uchar *ia)
 {
 	Ipmulti *multi, **l;
-	Conv **cp, **e;
 	Iplifc *lifc;
 	Ipifc *ifc;
 	Fs *f;
 
-	f = c->p->f;
-
 	for(l = &c->multi; *l != nil; l = &(*l)->next)
 		if(ipcmp(ma, (*l)->ma) == 0 && ipcmp(ia, (*l)->ia) == 0)
 			return;		/* it's already there */
@@ -1446,11 +1420,8 @@
 	ipmove(multi->ia, ia);
 	multi->next = nil;
 
-	e = &f->ipifc->conv[f->ipifc->nc];
-	for(cp = f->ipifc->conv; cp < e; cp++){
-		if((*cp) == nil || (*cp)->inuse == 0)
-			continue;
-		ifc = (Ipifc*)(*cp)->ptcl;
+	f = c->p->f;
+	if((ifc = findipifc(f, ia, ma, Rmulti)) != nil){
 		wlock(ifc);
 		if(waserror()){
 			wunlock(ifc);
@@ -1471,13 +1442,10 @@
 ipifcremmulti(Conv *c, uchar *ma, uchar *ia)
 {
 	Ipmulti *multi, **l;
-	Conv **cp, **e;
 	Iplifc *lifc;
 	Ipifc *ifc;
 	Fs *f;
 
-	f = c->p->f;
-
 	for(l = &c->multi; *l != nil; l = &(*l)->next)
 		if(ipcmp(ma, (*l)->ma) == 0 && ipcmp(ia, (*l)->ia) == 0)
 			break;
@@ -1487,12 +1455,10 @@
 		return; 	/* we don't have it open */
 
 	*l = multi->next;
+	free(multi);
 
-	e = &f->ipifc->conv[f->ipifc->nc];
-	for(cp = f->ipifc->conv; cp < e; cp++){
-		if((*cp) == nil || (*cp)->inuse == 0)
-			continue;
-		ifc = (Ipifc*)(*cp)->ptcl;
+	f = c->p->f;
+	if((ifc = findipifc(f, ia, ma, Rmulti)) != nil){
 		wlock(ifc);
 		if(waserror()){
 			wunlock(ifc);
@@ -1504,46 +1470,63 @@
 		poperror();
 	}
 
-	free(multi);
 }
 
+/* register the address on this network for address resolution */
 static void
+ipifcregisteraddr(Fs *f, Ipifc *ifc, uchar *ia, uchar *ip)
+{
+	Iplifc *lifc;
+
+	rlock(ifc);
+	if(waserror()){
+		runlock(ifc);
+		print("ipifcregisteraddr %s %I %I: %s\n", ifc->dev, ia, ip, up->errstr);
+		return;
+	}
+	lifc = iplocalonifc(ifc, ia);
+	if(lifc != nil && ifc->m != nil && ifc->m->areg != nil)
+		(*ifc->m->areg)(f, ifc, lifc, ip);
+	runlock(ifc);
+	poperror();
+}
+
+static void
 ipifcregisterproxy(Fs *f, Ipifc *ifc, uchar *ip, int add)
 {
-	uchar proxy[IPaddrlen];
-	Conv **cp, **e;
+	uchar a[IPaddrlen];
 	Iplifc *lifc;
 	Ipifc *nifc;
-	Medium *m;
+	Conv **cp;
 
 	/* register the address on any interface that will proxy for the ip */
-	e = &f->ipifc->conv[f->ipifc->nc];
-	for(cp = f->ipifc->conv; cp < e; cp++){
-		if(*cp == nil || (nifc = (Ipifc*)(*cp)->ptcl) == ifc)
+	for(cp = f->ipifc->conv; *cp != nil; cp++){
+		nifc = (Ipifc*)(*cp)->ptcl;
+		if(nifc == ifc)
 			continue;
 
 		wlock(nifc);
-		m = nifc->m;
-		if(m == nil || m->areg == nil || waserror()){
+		if(nifc->m == nil
+		|| (lifc = ipremoteonifc(nifc, ip)) == nil
+		|| (lifc->type & Rptpt) != 0
+		|| waserror()){
 			wunlock(nifc);
 			continue;
 		}
-		if((lifc = ipremoteonifc(nifc, ip)) != nil){
-			if((lifc->type & Rv4) == 0){
-				/* add solicited-node multicast addr */
-				ipv62smcast(proxy, ip);
-				if(add)
-					addselfcache(f, nifc, lifc, proxy, Rmulti);
-				else
-					remselfcache(f, nifc, lifc, proxy);
-			}
-			ipmove(proxy, lifc->local);
+		if((lifc->type & Rv4) == 0){
+			/* add solicited-node multicast addr */
+			ipv62smcast(a, ip);
+			if(add)
+				addselfcache(f, nifc, lifc, a, Rmulti);
+			else
+				remselfcache(f, nifc, lifc, a);
 		}
+		ipmove(a, lifc->local);
 		wunlock(nifc);
 		poperror();
 
-		if(add && lifc != nil)
-			(*m->areg)(f, nifc, ip, proxy);
+		if(add)
+			ipifcregisteraddr(f, nifc, a, ip);
 	}
 }
 
--- a/sys/src/9/ip/ipv6.c
+++ b/sys/src/9/ip/ipv6.c
@@ -31,7 +31,7 @@
 ipoput6(Fs *f, Block *bp, int gating, int ttl, int tos, Routehint *rh)
 {
 	int medialen, len, chunk, uflen, flen, seglen, lid, offset, fragoff;
-	int morefrags, blklen, rv = 0, tentative;
+	int morefrags, blklen, rv = 0;
 	uchar *gate, nexthdr;
 	Block *xp, *nb;
 	Fraghdr6 fraghdr;
@@ -50,13 +50,6 @@
 	/* 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 %I\n",
-			eh->src);
-		goto free;
-	}
-
 	if(gating){
 		chunk = nhgets(eh->ploadlen);
 		if(chunk > len){
@@ -217,7 +210,7 @@
 void
 ipiput6(Fs *f, Ipifc *ifc, Block *bp)
 {
-	int hl, hop, tos, notforme, tentative;
+	int hl, hop, tos;
 	uchar proto;
 	IP *ip;
 	Ip6hdr *h;
@@ -242,16 +235,8 @@
 			return;
 	}
 
-	h = (Ip6hdr *)bp->rp;
-	notforme = ipforme(f, h->dst) == 0;
-	tentative = iptentative(f, h->dst);
-
-	if(tentative && h->proto != ICMPv6) {
-		print("tentative addr, drop\n");
-		goto drop;
-	}
-
 	/* Check header version */
+	h = (Ip6hdr *)bp->rp;
 	if(BLKIPVER(bp) != IP_VER6) {
 		ip->stats[InHdrErrors]++;
 		netlog(f, Logip, "ip: bad version %ux\n", (h->vcf[0]&0xF0)>>2);
@@ -259,10 +244,10 @@
 	}
 
 	/* route */
-	if(notforme) {
+	if(ipforme(f, h->dst) == 0) {
 		Route *r;
 		Routehint rh;
-		Ipifc *toifc;
+		Ipifc *nifc;
 
 		if(!ip->iprouting)
 			goto drop;
@@ -277,8 +262,8 @@
 		/* don't forward to source's network */
 		rh.r = nil;
 		r  = v6lookup(f, h->dst, h->src, &rh);
-		if(r == nil || (toifc = r->ifc) == nil || (r->type & Rv4) != 0
-		|| (toifc == ifc && !ifc->reflect)){
+		if(r == nil || (nifc = r->ifc) == nil || (r->type & Rv4) != 0
+		|| (nifc == ifc && !ifc->reflect)){
 			ip->stats[OutDiscards]++;
 			goto drop;
 		}
@@ -292,7 +277,7 @@
 		}
 
 		/* process headers & reassemble if the interface expects it */
-		bp = procxtns(ip, bp, toifc->reassemble);
+		bp = procxtns(ip, bp, nifc->reassemble);
 		if(bp == nil)
 			return;
 
@@ -312,7 +297,7 @@
 	h = (Ip6hdr *) (bp->rp);
 	proto = h->proto;
 	p = Fsrcvpcol(f, proto);
-	if(p && p->rcv) {
+	if(p != nil && p->rcv != nil) {
 		ip->stats[InDelivers]++;
 		(*p->rcv)(p, ifc, bp);
 		return;
@@ -455,7 +440,7 @@
 	/*
 	 *  block lists are too hard, pullupblock into a single block
 	 */
-	if(bp->next){
+	if(bp->next != nil){
 		bp = pullupblock(bp, blocklen(bp));
 		ih = (Ip6hdr *)bp->rp;
 	}
@@ -465,7 +450,7 @@
 	/*
 	 *  find a reassembly queue for this fragment
 	 */
-	for(f = ip->flisthead6; f; f = fnext){
+	for(f = ip->flisthead6; f != nil; f = fnext){
 		fnext = f->next;
 		if(ipcmp(f->src, src)==0 && ipcmp(f->dst, dst)==0 && f->id == id)
 			break;
@@ -524,7 +509,7 @@
 	}
 
 	/* Check overlap of a previous fragment - trim away as necessary */
-	if(prev) {
+	if(prev != nil) {
 		ovlap = BKFG(prev)->foff + BKFG(prev)->flen - BKFG(bp)->foff;
 		if(ovlap > 0) {
 			if(ovlap >= BKFG(bp)->flen) {
@@ -541,12 +526,12 @@
 	*l = bp;
 
 	/* Check to see if succeeding segments overlap */
-	if(bp->next) {
+	if(bp->next != nil) {
 		l = &bp->next;
 		fend = BKFG(bp)->foff + BKFG(bp)->flen;
 
 		/* Take completely covered segments out */
-		while(*l) {
+		while(*l != nil) {
 			ovlap = fend - BKFG(*l)->foff;
 			if(ovlap <= 0)
 				break;
@@ -570,7 +555,7 @@
 	 *  with the trailing bit of fraghdr->offsetRM[1] set, we're done.
 	 */
 	pktposn = 0;
-	for(bl = f->blist; bl && BKFG(bl)->foff == pktposn; bl = bl->next) {
+	for(bl = f->blist; bl != nil && BKFG(bl)->foff == pktposn; bl = bl->next) {
 		fraghdr = (Fraghdr6 *)(bl->rp + uflen);
 		if((fraghdr->offsetRM[1] & 1) == 0) {
 			bl = f->blist;
@@ -584,7 +569,7 @@
 			 * Pullup all the fragment headers and
 			 * return a complete packet
 			 */
-			for(bl = bl->next; bl; bl = bl->next) {
+			for(bl = bl->next; bl != nil; bl = bl->next) {
 				fragsize = BKFG(bl)->flen;
 				len += fragsize;
 				bl->rp += uflen + IP6FHDR;