shithub: riscv

Download patch

ref: c2dd9b1da7659c9ac42e7612e5621e9426956c73
parent: dc8432d4593c7a4b0a187d0174d4e4e603e456f6
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Sun Apr 8 17:15:00 EDT 2018

devip: implement source specific routing

--- a/sys/src/9/ip/arp.c
+++ b/sys/src/9/ip/arp.c
@@ -74,7 +74,7 @@
 }
 
 /*
- *  create a new arp entry for an ip address.
+ *  create a new arp entry for an ip address on ifc.
  */
 static Arpent*
 newarp6(Arp *arp, uchar *ip, Ipifc *ifc, int addrxt)
@@ -82,7 +82,6 @@
 	uint t;
 	Block *xp;
 	Arpent *a, *e, *f, **l;
-	Medium *m = ifc->m;
 	int empty;
 
 	/* find oldest entry */
@@ -128,10 +127,9 @@
 	a->hash = *l;
 	*l = a;
 
-	memmove(a->ip, ip, sizeof(a->ip));
+	ipmove(a->ip, ip);
 	a->utime = NOW;
 	a->ctime = 0;
-	a->type = m;
 
 	a->rtime = NOW + ReTransTimer;
 	a->rxtsrem = MAX_MULTICAST_SOLICIT;
@@ -172,8 +170,10 @@
 
 	a->utime = 0;
 	a->ctime = 0;
-	a->type = 0;
 	a->state = 0;
+
+	a->ifc = nil;
+	a->ifcid = 0;
 	
 	/* take out of current chain */
 	l = &arp->hash[haship(a->ip)];
@@ -198,7 +198,6 @@
 	a->hash = nil;
 	freeblistchain(a->hold);
 	a->hold = a->last = nil;
-	a->ifc = nil;
 }
 
 /*
@@ -212,7 +211,6 @@
 {
 	int hash;
 	Arpent *a;
-	Medium *type = ifc->m;
 	uchar v6ip[IPaddrlen];
 
 	if(version == V4){
@@ -223,11 +221,9 @@
 	qlock(arp);
 	hash = haship(ip);
 	for(a = arp->hash[hash]; a != nil; a = a->hash){
-		if(ipcmp(ip, a->ip) == 0)
-		if(type == a->type)
+		if(a->ifc == ifc && a->ifcid == ifc->ifcid && ipcmp(ip, a->ip) == 0)
 			break;
 	}
-
 	if(a == nil){
 		a = newarp6(arp, ip, ifc, (version != V4));
 		a->state = AWAIT;
@@ -245,7 +241,7 @@
 		return a;		/* return with arp qlocked */
 	}
 
-	memmove(mac, a->mac, a->type->maclen);
+	memmove(mac, a->mac, ifc->m->maclen);
 
 	/* remove old entries */
 	if(NOW - a->ctime > 15*60*1000)
@@ -286,13 +282,10 @@
 			l = &f->nextrxt;
 		}
 	}
-
 	memmove(a->mac, mac, type->maclen);
-	a->type = type;
 	a->state = AOK;
 	a->utime = NOW;
 	bp = a->hold;
-	assert(bp->list == nil);
 	a->hold = a->last = nil;
 	qunlock(arp);
 
@@ -299,50 +292,43 @@
 	return bp;
 }
 
-void
-arpenter(Fs *fs, int version, uchar *ip, uchar *mac, int n, int refresh)
+int
+arpenter(Fs *fs, int version, uchar *ip, uchar *mac, int n, uchar *src, int refresh)
 {
 	Arp *arp;
 	Route *r;
 	Arpent *a, *f, **l;
 	Ipifc *ifc;
-	Medium *type;
 	Block *bp, *next;
+	Medium *m;
 	uchar v6ip[IPaddrlen];
 
 	arp = fs->arp;
-
-	if(n != 6)
-		return;
-
 	switch(version){
 	case V4:
-		r = v4lookup(fs, ip, nil);
+		r = v4lookup(fs, ip, src, nil);
 		v4tov6(v6ip, ip);
 		ip = v6ip;
 		break;
 	case V6:
-		r = v6lookup(fs, ip, nil);
+		r = v6lookup(fs, ip, src, nil);
 		break;
 	default:
 		panic("arpenter: version %d", version);
-		return;	/* to supress warnings */
+		return -1;	/* to supress warnings */
 	}
+	if(r == nil || (ifc = r->ifc) == nil || (m = ifc->m) == nil || m->maclen != n || m->maclen == 0)
+		return -1;
 
-	if(r == nil)
-		return;
-
-	ifc = r->ifc;
-	type = ifc->m;
-
 	qlock(arp);
 	for(a = arp->hash[haship(ip)]; a != nil; a = a->hash){
-		if(a->type != type || (a->state != AWAIT && a->state != AOK))
+		if(a->state != AWAIT && a->state != AOK)
 			continue;
-
+		if(a->ifc != ifc || a->ifcid != ifc->ifcid)
+			continue;
 		if(ipcmp(a->ip, ip) == 0){
 			a->state = AOK;
-			memmove(a->mac, mac, type->maclen);
+			memmove(a->mac, mac, n);
 
 			if(version == V6){
 				/* take out of re-transmit chain */
@@ -356,8 +342,6 @@
 				}
 			}
 
-			a->ifc = ifc;
-			a->ifcid = ifc->ifcid;
 			bp = a->hold;
 			a->hold = a->last = nil;
 			if(version == V4)
@@ -367,22 +351,29 @@
 			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()){
-					freeblistchain(next);
 					runlock(ifc);
-					nexterror();
+					freeblistchain(next);
+					break;
 				}
-				rlock(ifc);
-				if(ifc->m != nil)
-					ifc->m->bwrite(ifc, concatblock(bp), version, ip);
-				else
-					freeblist(bp);
+				m->bwrite(ifc, concatblock(bp), version, ip);
 				runlock(ifc);
 				poperror();
 				bp = next;
 			}
-			return;
+
+			return 1;
 		}
 	}
 
@@ -389,12 +380,12 @@
 	if(refresh == 0){
 		a = newarp6(arp, ip, ifc, 0);
 		a->state = AOK;
-		a->type = type;
 		a->ctime = NOW;
-		memmove(a->mac, mac, type->maclen);
+		memmove(a->mac, mac, n);
 	}
 
 	qunlock(arp);
+	return refresh == 0;
 }
 
 int
@@ -401,12 +392,11 @@
 arpwrite(Fs *fs, char *s, int len)
 {
 	int n;
-	Route *r;
 	Arp *arp;
-	Arpent *a;
+	Arpent *a, *x;
 	Medium *m;
-	char *f[4], buf[256];
-	uchar ip[IPaddrlen], mac[MAClen];
+	char *f[5], buf[256];
+	uchar ip[IPaddrlen], src[IPaddrlen], mac[MAClen];
 
 	arp = fs->arp;
 
@@ -419,7 +409,7 @@
 	if(len > 0 && buf[len-1] == '\n')
 		buf[len-1] = 0;
 
-	n = getfields(buf, f, 4, 1, " ");
+	n = getfields(buf, f, nelem(f), 1, " ");
 	if(strcmp(f[0], "flush") == 0){
 		qlock(arp);
 		for(a = arp->cache; a < &arp->cache[NCACHE]; a++){
@@ -428,6 +418,8 @@
 			a->hash = nil;
 			a->state = 0;
 			a->utime = 0;
+			a->ifc = nil;
+			a->ifcid = 0;
 			freeblistchain(a->hold);
 			a->hold = a->last = nil;
 		}
@@ -442,47 +434,49 @@
 		default:
 			error(Ebadarg);
 		case 3:
-			if (parseip(ip, f[1]) == -1)
+			if(parseip(ip, f[1]) == -1)
 				error(Ebadip);
-			if(isv4(ip))
-				r = v4lookup(fs, ip+IPv4off, nil);
-			else
-				r = v6lookup(fs, ip, nil);
-			if(r == nil)
-				error("Destination unreachable");
-			m = r->ifc->m;
-			n = parsemac(mac, f[2], m->maclen);
+			if((n = parsemac(mac, f[2], sizeof(mac))) <= 0)
+				error(Ebadarp);
+			findlocalip(fs, src, ip);
 			break;
 		case 4:
 			m = ipfindmedium(f[1]);
-			if(m == nil)
+			if(m == nil || m->maclen == 0)
 				error(Ebadarp);
-			if (parseip(ip, f[2]) == -1)
+			if(parseip(ip, f[2]) == -1)
 				error(Ebadip);
-			n = parsemac(mac, f[3], m->maclen);
+			if((n = parsemac(mac, f[3], sizeof(mac))) != m->maclen)
+				error(Ebadarp);
+			findlocalip(fs, src, ip);
 			break;
+		case 5:
+			m = ipfindmedium(f[1]);
+			if(m == nil || m->maclen == 0)
+				error(Ebadarp);
+			if(parseip(ip, f[2]) == -1)
+				error(Ebadip);
+			if((n = parsemac(mac, f[3], sizeof(mac))) != m->maclen)
+				error(Ebadarp);
+			if(parseip(src, f[4]) == -1)
+				error(Ebadip);
+			break;
 		}
-
-		if(m->ares == nil)
-			error(Ebadarp);
-
-		m->ares(fs, V6, ip, mac, n, 0);
+		if(arpenter(fs, V6, ip, mac, n, src, 0) <= 0)
+			error("destination unreachable");
 	} else if(strcmp(f[0], "del") == 0){
-		if(n != 2)
+		if (n != 2)
 			error(Ebadarg);
-
 		if (parseip(ip, f[1]) == -1)
 			error(Ebadip);
-
 		qlock(arp);
-		for(a = arp->hash[haship(ip)]; a != nil; a = a->hash)
-			if(memcmp(ip, a->ip, sizeof(a->ip)) == 0)
-				break;
-	
-		if(a != nil){
-			cleanarpent(arp, a);
-			memset(a->ip, 0, sizeof(a->ip));
-			memset(a->mac, 0, sizeof(a->mac));
+		for(a = arp->hash[haship(ip)]; a != nil; a = x){
+			x = a->hash;
+			if(ipcmp(ip, a->ip) == 0){
+				cleanarpent(arp, a);
+				memset(a->ip, 0, sizeof(a->ip));
+				memset(a->mac, 0, sizeof(a->mac));
+			}
 		}
 		qunlock(arp);
 	} else
@@ -491,13 +485,6 @@
 	return len;
 }
 
-enum
-{
-	Alinelen=	90,
-};
-
-char *aformat = "%-6.6s %-8.8s %-40.40I %-32.32s\n";
-
 static void
 convmac(char *p, uchar *mac, int n)
 {
@@ -506,34 +493,40 @@
 }
 
 int
-arpread(Arp *arp, char *p, ulong offset, int len)
+arpread(Arp *arp, char *s, ulong offset, int len)
 {
+	uchar ip[IPaddrlen], src[IPaddrlen];
+	char mac[2*MAClen+1], *p, *state;
+	Ipifc *ifc;
 	Arpent *a;
-	int n;
-	char mac[2*MAClen+1];
+	long n, o;
 
-	if(offset % Alinelen)
-		return 0;
-
-	offset = offset/Alinelen;
-	len = len/Alinelen;
-
-	n = 0;
+	p = s;
+	o = -offset;
 	for(a = arp->cache; len > 0 && a < &arp->cache[NCACHE]; a++){
-		if(a->state == 0)
+		if(a->state == 0 || (ifc = a->ifc) == nil || a->ifcid != ifc->ifcid)
 			continue;
-		if(offset > 0){
-			offset--;
-			continue;
-		}
-		len--;
+
 		qlock(arp);
-		convmac(mac, a->mac, a->type->maclen);
-		n += sprint(p+n, aformat, a->type->name, arpstate[a->state], a->ip, mac);
+		state = arpstate[a->state];
+		ipmove(ip, a->ip);
+		convmac(mac, a->mac, ifc->m->maclen);
 		qunlock(arp);
+
+		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);
+		if(o < 0) {
+			if(n > -o)
+				memmove(p, p-o, n+o);
+			o += n;
+		} else {
+			len -= n;
+			p += n;
+		}
 	}
 
-	return n;
+	return p - s;
 }
 
 void
@@ -547,7 +540,7 @@
 		ipmove(src, ((Ip6hdr*)a->last->rp)->src);
 		arprelease(f->arp, a);
 
-		if(iplocalonifc(ifc, src) || ipproxyifc(f, ifc, src))
+		if(iplocalonifc(ifc, src) != nil || ipproxyifc(f, ifc, src))
 			goto send;
 	} else {
 		arprelease(f->arp, a);
--- a/sys/src/9/ip/devip.c
+++ b/sys/src/9/ip/devip.c
@@ -848,7 +848,7 @@
 		else {
 			if(parseip(addr, str) == -1)
 				return Ebadip;
-			if(ipforme(c->p->f, addr) || ipismulticast(addr))
+			if(ipforme(c->p->f, addr) != 0 || ipismulticast(addr))
 				ipmove(c->laddr, addr);
 			else
 				return "not a local IP address";
--- a/sys/src/9/ip/ethermedium.c
+++ b/sys/src/9/ip/ethermedium.c
@@ -33,9 +33,9 @@
 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 Block*	multicastarp(Fs *f, Arpent *a, Medium*, uchar *mac);
 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);
@@ -53,8 +53,7 @@
 .bwrite=	etherbwrite,
 .addmulti=	etheraddmulti,
 .remmulti=	etherremmulti,
-.ares=		arpenter,
-.areg=		sendgarp,
+.areg=		etherareg,
 .pref2addr=	etherpref2addr,
 };
 
@@ -70,8 +69,7 @@
 .bwrite=	etherbwrite,
 .addmulti=	etheraddmulti,
 .remmulti=	etherremmulti,
-.ares=		arpenter,
-.areg=		sendgarp,
+.areg=		etherareg,
 .pref2addr=	etherpref2addr,
 };
 
@@ -271,7 +269,7 @@
 
 	/* get mac address of destination */
 	a = arpget(er->f->arp, bp, version, ifc, ip, mac);
-	if(a){
+	if(a != nil){
 		/* check for broadcast or multicast */
 		bp = multicastarp(er->f, a, ifc->m, mac);
 		if(bp==nil){
@@ -471,8 +469,8 @@
 	arprelease(er->f->arp, a);
 
 	n = sizeof(Etherarp);
-	if(n < a->type->mintu)
-		n = a->type->mintu;
+	if(n < ifc->m->mintu)
+		n = ifc->m->mintu;
 	bp = allocb(n);
 	memset(bp->rp, 0, n);
 	e = (Etherarp*)bp->rp;
@@ -536,10 +534,6 @@
 	Etherarp *e;
 	Etherrock *er = ifc->arg;
 
-	/* don't arp for our initial non address */
-	if(ipcmp(ip, IPnoaddr) == 0)
-		return;
-
 	n = sizeof(Etherarp);
 	if(n < ifc->m->mintu)
 		n = ifc->m->mintu;
@@ -585,7 +579,7 @@
 	case ARPREPLY:
 		/* check for machine using my ip address */
 		v4tov6(ip, e->spa);
-		if(iplocalonifc(ifc, ip) || ipproxyifc(er->f, ifc, ip)){
+		if(iplocalonifc(ifc, ip) != nil || 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);
@@ -594,14 +588,13 @@
 		}
 
 		/* make sure we're not entering broadcast addresses */
-		if(ipcmp(ip, ipbroadcast) == 0 ||
-			!memcmp(e->sha, etherbroadcast, sizeof(e->sha))){
+		if(ipcmp(ip, ipbroadcast) == 0 || memcmp(e->sha, etherbroadcast, sizeof(e->sha)) == 0){
 			print("arprep: 0x%E/0x%E cannot register broadcast address %I\n",
 				e->s, e->sha, e->spa);
 			break;
 		}
 
-		arpenter(er->f, V4, e->spa, e->sha, sizeof(e->sha), 0);
+		arpenter(er->f, V4, e->spa, e->sha, sizeof(e->sha), e->tpa, 0);
 		break;
 
 	case ARPREQUEST:
@@ -611,9 +604,9 @@
 
 		/* check for machine using my ip or ether address */
 		v4tov6(ip, e->spa);
-		if(iplocalonifc(ifc, ip) || ipproxyifc(er->f, ifc, ip)){
+		if(iplocalonifc(ifc, ip) != nil || ipproxyifc(er->f, ifc, ip)){
 			if(memcmp(e->sha, ifc->mac, sizeof(e->sha)) != 0){
-				if (memcmp(eprinted, e->spa, sizeof(e->spa))){
+				if(memcmp(eprinted, e->spa, sizeof(e->spa)) != 0){
 					/* print only once */
 					print("arpreq: 0x%E also has ip addr %V\n", e->sha, e->spa);
 					memmove(eprinted, e->spa, sizeof(e->spa));
@@ -627,12 +620,11 @@
 		}
 
 		/* refresh what we know about sender */
-		arpenter(er->f, V4, e->spa, e->sha, sizeof(e->sha), 1);
+		arpenter(er->f, V4, e->spa, e->sha, sizeof(e->sha), e->tpa, 1);
 
 		/* answer only requests for our address or systems we're proxying for */
 		v4tov6(ip, e->tpa);
-		if(!iplocalonifc(ifc, ip))
-		if(!ipproxyifc(er->f, ifc, ip))
+		if(iplocalonifc(ifc, ip) == nil && !ipproxyifc(er->f, ifc, ip))
 			break;
 
 		n = sizeof(Etherarp);
@@ -742,7 +734,7 @@
 static void
 etherpref2addr(uchar *pref, uchar *ea)
 {
-	pref[8] = ea[0] | 0x2;
+	pref[8] = ea[0] ^ 0x2;
 	pref[9] = ea[1];
 	pref[10] = ea[2];
 	pref[11] = 0xFF;
@@ -750,4 +742,32 @@
 	pref[13] = ea[3];
 	pref[14] = ea[4];
 	pref[15] = ea[5];
+}
+
+static void
+etherareg(Fs *f, Ipifc *ifc, uchar *ip, uchar *proxy)
+{
+	static char tdad[] = "dad6";
+	uchar mcast[IPaddrlen];
+
+	if(ipcmp(ip, IPnoaddr) == 0)
+		return;
+
+	if(isv4(ip)){
+		sendgarp(ifc, ip);
+		return;
+	}
+
+	if(!iptentative(f, ip)){
+		icmpna(f, proxy, 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);
+
+	icmpns(f, 0, SRC_UNSPEC, ip, TARG_MULTI, ifc->mac);
+
+	remroute(f, mcast, IPallbits, v6Unspecified, IPallbits, ip, Rmulti, ifc, tdad);
 }
--- a/sys/src/9/ip/icmp.c
+++ b/sys/src/9/ip/icmp.c
@@ -200,7 +200,7 @@
 	if(ipismulticast(addr))
 		return 0;
 	i = ipforme(f, addr);
-	return i == 0 || (i & Runi) != 0;
+	return i == 0 || i == Runi;
 }
 
 static int
@@ -211,7 +211,7 @@
 	v4tov6(addr, ip4);
 	if(ipismulticast(addr))
 		return 0;
-	return (ipforme(f, addr) & Runi) != 0;
+	return ipforme(f, addr) == Runi;
 }
 
 void
@@ -401,7 +401,7 @@
 		break;
 	case Unreachable:
 		if(p->code >= nelem(unreachcode)) {
-			snprint(m2, sizeof m2, "unreachable %V->%V code %d",
+			snprint(m2, sizeof m2, "unreachable %V -> %V code %d",
 				p->src, p->dst, p->code);
 			msg = m2;
 		} else
@@ -452,7 +452,7 @@
 	freeblist(bp);
 }
 
-void
+static void
 icmpadvise(Proto *icmp, Block *bp, char *msg)
 {
 	Conv	**c, *s;
@@ -478,7 +478,7 @@
 	freeblist(bp);
 }
 
-int
+static int
 icmpstats(Proto *icmp, char *buf, int len)
 {
 	Icmppriv *priv;
--- a/sys/src/9/ip/icmp6.c
+++ b/sys/src/9/ip/icmp6.c
@@ -211,7 +211,7 @@
 	return nbp;
 }
 
-void
+static void
 icmpadvise6(Proto *icmp, Block *bp, char *msg)
 {
 	ushort recid;
@@ -223,7 +223,8 @@
 
 	for(c = icmp->conv; *c; c++) {
 		s = *c;
-		if(s->lport == recid && ipcmp(s->raddr, p->dst) == 0){
+		if(s->lport == recid)
+		if(ipcmp(s->laddr, p->dst) == 0 || ipcmp(s->raddr, p->dst) == 0){
 			if(s->ignoreadvice)
 				break;
 			qhangup(s->rq, msg);
@@ -280,7 +281,7 @@
 	ipoput6(c->p->f, bp, 0, c->ttl, c->tos, nil);
 }
 
-char*
+static char*
 icmpctl6(Conv *c, char **argv, int argc)
 {
 	Icmpcb6 *icb;
@@ -311,10 +312,14 @@
 
 	for(c = icmp->conv; *c; c++){
 		s = *c;
-		if(s->lport == recid && ipcmp(s->raddr, addr) == 0){
+		if(s->lport != recid)
+			continue;
+		if(ipcmp(s->laddr, addr) == 0){
 			qpass(s->rq, concatblock(bp));
 			return;
 		}
+		if(ipcmp(s->raddr, addr) == 0)
+			qpass(s->rq, copyblock(bp, blocklen(bp)));
 	}
 
 	freeblist(bp);
@@ -416,7 +421,7 @@
 	np->ttl = HOP_LIMIT;
 	np->vcf[0] = 0x06 << 4;
 	ipriv->out[NbrAdvert]++;
-	netlog(f, Logicmp, "sending neighbor advertisement %I\n", src);
+	netlog(f, Logicmp, "sending neighbor advertisement %I\n", targ);
 	ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, nil);
 }
 
@@ -692,26 +697,19 @@
 	int t;
 
 	rlock(ifc);
-	if(ipproxyifc(f, ifc, target)) {
-		runlock(ifc);
-		return Tuniproxy;
-	}
-
-	for(lifc = ifc->lifc; lifc; lifc = lifc->next)
-		if(ipcmp(lifc->local, target) == 0) {
-			t = (lifc->tentative)? Tunitent: Tunirany;
-			runlock(ifc);
-			return t;
-		}
-
+	if((lifc = iplocalonifc(ifc, target)) != nil)
+		t = lifc->tentative? Tunitent: Tunirany;
+	else if(ipproxyifc(f, ifc, target))
+		t = Tuniproxy;
+	else
+		t = 0;
 	runlock(ifc);
-	return 0;
+	return t;
 }
 
 static void
 icmpiput6(Proto *icmp, Ipifc *ipifc, Block *bp)
 {
-	int refresh = 1;
 	char *msg, m2[128];
 	uchar pktflags;
 	uchar *packet = bp->rp;
@@ -797,16 +795,12 @@
 			/* fall through */
 
 		case Tuniproxy:
-			if(ipcmp(np->src, v6Unspecified) != 0) {
-				arpenter(icmp->f, V6, np->src, np->lnaddr,
-					8*np->olen-2, 0);
+			if(ipv6local(ipifc, lsrc, np->src)) {
+				arpenter(icmp->f, V6, np->src, np->lnaddr, 8*np->olen-2, lsrc, 0);
 				pktflags |= Sflag;
-			}
-			if(!ipv6local(ipifc, lsrc, np->src))
-				break;
-			icmpna(icmp->f, lsrc,
-				(ipcmp(np->src, v6Unspecified) == 0?
-					v6allnodesL: np->src),
+			} else
+				ipmove(lsrc, np->target);
+			icmpna(icmp->f, lsrc, (pktflags & Sflag) ? np->src : v6allnodesL,
 				np->target, ipifc->mac, pktflags);
 			break;
 		case Tunitent:
@@ -830,9 +824,10 @@
 		 * 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);
+		if(lifc != nil && lifc->tentative)
+			arpenter(icmp->f, V6, np->target, np->lnaddr, 8*np->olen-2, np->target, 0);
+		else if(ipv6local(ipifc, lsrc, np->target))
+			arpenter(icmp->f, V6, np->target, np->lnaddr, 8*np->olen-2, lsrc, 1);
 		freeblist(bp);
 		break;
 
@@ -846,7 +841,7 @@
 	freeblist(bp);
 }
 
-int
+static int
 icmpstats6(Proto *icmp6, char *buf, int len)
 {
 	Icmppriv6 *priv;
@@ -872,6 +867,14 @@
 extern char*	icmpconnect(Conv *c, char **argv, int argc);
 extern void	icmpclose(Conv *c);
 
+static void
+icmpclose6(Conv *c)
+{
+	Icmpcb6 *icb = (Icmpcb6*)c->ptcl;
+	icb->headers = 0;
+	icmpclose(c);
+}
+
 void
 icmp6init(Fs *fs)
 {
@@ -883,7 +886,7 @@
 	icmp6->announce = icmpannounce;
 	icmp6->state = icmpstate;
 	icmp6->create = icmpcreate6;
-	icmp6->close = icmpclose;
+	icmp6->close = icmpclose6;
 	icmp6->rcv = icmpiput6;
 	icmp6->stats = icmpstats6;
 	icmp6->ctl = icmpctl6;
--- a/sys/src/9/ip/ip.c
+++ b/sys/src/9/ip/ip.c
@@ -124,7 +124,7 @@
 	Block *xp, *nb;
 	Ip4hdr *eh, *feh;
 	int lid, len, seglen, chunk, dlen, blklen, offset, medialen;
-	Route *r, *sr;
+	Route *r;
 	IP *ip;
 	int rv = 0;
 
@@ -154,25 +154,17 @@
 		goto free;
 	}
 
-	r = v4lookup(f, eh->dst, rh);
-	if(r == nil){
+	r = v4lookup(f, eh->dst, eh->src, rh);
+	if(r == nil || (ifc = r->ifc) == nil){
 		ip->stats[OutNoRoutes]++;
-		netlog(f, Logip, "no interface %V\n", eh->dst);
+		netlog(f, Logip, "no interface %V -> %V\n", eh->src, eh->dst);
 		rv = -1;
 		goto free;
 	}
 
-	ifc = r->ifc;
-	if(r->type & (Rifc|Runi))
+	if(r->type & (Rifc|Runi|Rbcast|Rmulti))
 		gate = eh->dst;
 	else
-	if(r->type & (Rbcast|Rmulti)) {
-		gate = eh->dst;
-		sr = v4lookup(f, eh->src, nil);
-		if(sr != nil && (sr->type & Runi))
-			ifc = sr->ifc;
-	}
-	else
 		gate = r->v4.gate;
 
 	if(!gating)
@@ -380,7 +372,7 @@
 
 		/* don't forward to source's network */
 		rh.r = nil;
-		r = v4lookup(f, h->dst, &rh);
+		r = v4lookup(f, h->dst, h->src, &rh);
 		if(r == nil || r->ifc == ifc){
 			ip->stats[OutDiscards]++;
 			freeblist(bp);
--- a/sys/src/9/ip/ip.h
+++ b/sys/src/9/ip/ip.h
@@ -39,7 +39,7 @@
 	Maxproto=	20,
 	Maxincall=	10,
 	Nchans=		1024,
-	MAClen=		16,		/* longest mac address */
+	MAClen=		8,		/* longest mac address */
 
 	MAXTTL=		255,
 	DFLTTOS=	0,
@@ -242,18 +242,8 @@
 	/* process packets written to 'data' */
 	void	(*pktin)(Fs *f, Ipifc *ifc, Block *bp);
 
-	/* routes for router boards */
-	void	(*addroute)(Ipifc *ifc, int, uchar*, uchar*, uchar*, int);
-	void	(*remroute)(Ipifc *ifc, int, uchar*, uchar*);
-	void	(*flushroutes)(Ipifc *ifc);
-
-	/* for routing multicast groups */
-	void	(*joinmulti)(Ipifc *ifc, uchar *a, uchar *ia);
-	void	(*leavemulti)(Ipifc *ifc, uchar *a, uchar *ia);
-
 	/* address resolution */
-	void	(*ares)(Fs*, int, uchar*, uchar*, int, int);	/* resolve */
-	void	(*areg)(Ipifc*, uchar*);			/* register */
+	void	(*areg)(Fs *f, Ipifc *ifc, uchar *ip, uchar *proxy);			/* register */
 
 	/* v6 address generation */
 	void	(*pref2addr)(uchar *pref, uchar *ea);
@@ -268,6 +258,7 @@
 	uchar	mask[IPaddrlen];
 	uchar	remote[IPaddrlen];
 	uchar	net[IPaddrlen];
+	uchar	type;		/* ruoute type */
 	uchar	tentative;	/* =1 => v6 dup disc on, =0 => confirmed unique */
 	uchar	onlink;		/* =1 => onlink, =0 offlink. */
 	uchar	autoflag;	/* v6 autonomous flag */
@@ -509,13 +500,11 @@
  *  iproute.c
  */
 typedef	struct RouteTree RouteTree;
-typedef struct Routewalk Routewalk;
 typedef struct V4route V4route;
 typedef struct V6route V6route;
 
 enum
 {
-
 	/* type bits */
 	Rv4=		(1<<0),		/* this is a version 4 route */
 	Rifc=		(1<<1),		/* this route is a directly connected interface */
@@ -524,27 +513,18 @@
 	Rbcast=		(1<<4),		/* a broadcast self address */
 	Rmulti=		(1<<5),		/* a multicast self address */
 	Rproxy=		(1<<6),		/* this route should be proxied */
+	Rsrc=		(1<<7),		/* source specific route */
 };
 
-struct Routewalk
-{
-	int	o;
-	int	h;
-	char*	p;
-	char*	e;
-	void*	state;
-	void	(*walk)(Route*, Routewalk*);
-};
-
 struct	RouteTree
 {
-	Route*	right;
-	Route*	left;
-	Route*	mid;
+	Route	*mid;
+	Route	*left;
+	Route	*right;
+	Ipifc	*ifc;
+	uchar	ifcid;		/* must match ifc->id */
 	uchar	depth;
 	uchar	type;
-	uchar	ifcid;		/* must match ifc->id */
-	Ipifc	*ifc;
 	char	tag[4];
 	int	ref;
 };
@@ -553,6 +533,10 @@
 {
 	ulong	address;
 	ulong	endaddress;
+
+	ulong	source;
+	ulong	endsource;
+
 	uchar	gate[IPv4addrlen];
 };
 
@@ -560,6 +544,10 @@
 {
 	ulong	address[IPllen];
 	ulong	endaddress[IPllen];
+
+	ulong	source[IPllen];
+	ulong	endsource[IPllen];
+
 	uchar	gate[IPaddrlen];
 };
 
@@ -572,17 +560,14 @@
 		V4route v4;
 	};
 };
-extern void	v4addroute(Fs *f, char *tag, uchar *a, uchar *mask, uchar *gate, int type);
-extern void	v6addroute(Fs *f, char *tag, uchar *a, uchar *mask, uchar *gate, int type);
-extern void	v4delroute(Fs *f, uchar *a, uchar *mask, int dolock);
-extern void	v6delroute(Fs *f, uchar *a, uchar *mask, int dolock);
-extern Route*	v4lookup(Fs *f, uchar *a, Routehint *h);
-extern Route*	v6lookup(Fs *f, uchar *a, Routehint *h);
+
+extern void	addroute(Fs *f, uchar *a, uchar *mask, uchar *s, uchar *smask, uchar *gate, int type, Ipifc *ifc, char *tag);
+extern void	remroute(Fs *f, uchar *a, uchar *mask, uchar *s, uchar *smask, uchar *gate, int type, Ipifc *ifc, char *tag);
+extern Route*	v4lookup(Fs *f, uchar *a, uchar *s, Routehint *h);
+extern Route*	v6lookup(Fs *f, uchar *a, uchar *s, Routehint *h);
 extern long	routeread(Fs *f, char*, ulong, int);
 extern long	routewrite(Fs *f, Chan*, char*, int);
-extern void	routetype(int, char*);
-extern void	ipwalkroutes(Fs*, Routewalk*);
-extern void	convroute(Route*, uchar*, uchar*, uchar*, char*, int*);
+extern void	routetype(int type, char p[8]);
 
 /*
  *  devip.c
@@ -607,7 +592,6 @@
 {
 	uchar	ip[IPaddrlen];
 	uchar	mac[MAClen];
-	Medium	*type;			/* media type */
 	Arpent*	hash;
 	Block*	hold;
 	Block*	last;
@@ -627,7 +611,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 void	arpenter(Fs*, int version, uchar *ip, uchar *mac, int len, int norefresh);
+extern int	arpenter(Fs*, int version, uchar *ip, uchar *mac, int n, uchar *src, int norefresh);
 extern void	ndpsendsol(Fs*, Ipifc*, Arpent*);
 
 /*
@@ -674,21 +658,16 @@
 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);
+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);
 extern void	findlocalip(Fs*, uchar *local, uchar *remote);
 extern int	ipv4local(Ipifc *ifc, uchar *local, uchar *remote);
 extern int	ipv6local(Ipifc *ifc, uchar *local, uchar *remote);
 extern Iplifc*	iplocalonifc(Ipifc *ifc, uchar *ip);
+extern Iplifc*	ipremoteonifc(Ipifc *ifc, uchar *ip);
 extern int	ipproxyifc(Fs *f, Ipifc *ifc, uchar *ip);
-extern int	ipismulticast(uchar *ip);
-extern int	ipisbooting(void);
-extern int	ipifccheckin(Ipifc *ifc, Medium *med);
-extern void	ipifccheckout(Ipifc *ifc);
-extern int	ipifcgrab(Ipifc *ifc);
-extern void	ipifcaddroute(Fs*, int, uchar*, uchar*, uchar*, int);
-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);
--- a/sys/src/9/ip/ipifc.c
+++ b/sys/src/9/ip/ipifc.c
@@ -61,11 +61,54 @@
 
 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 char*	ipifcjoinmulti(Ipifc *ifc, char **argv, int argc);
-static char*	ipifcleavemulti(Ipifc *ifc, char **argv, int argc);
-static void	ipifcregisterproxy(Fs*, Ipifc*, uchar*);
-static char*	ipifcremlifc(Ipifc*, Iplifc*);
+static void	ipifcregisterproxy(Fs*, Ipifc*, uchar*, int);
+static char*	ipifcremlifc(Ipifc*, Iplifc**);
 
+enum {
+	unknownv6,		/* UGH */
+	unspecifiedv6,
+	linklocalv6,
+	globalv6,
+};
+
+static int
+v6addrtype(uchar *addr)
+{
+	if(isv4(addr) || ipcmp(addr, IPnoaddr) == 0)
+		return unknownv6;
+	else if(islinklocal(addr) ||
+	    isv6mcast(addr) && (addr[1] & 0xF) <= Link_local_scop)
+		return linklocalv6;
+	else
+		return globalv6;
+}
+
+#define v6addrcurr(lifc) ((lifc)->preflt == ~0L || \
+			(lifc)->origint + (lifc)->preflt >= NOW/1000)
+
+static uchar*
+defsmask(uchar *a)
+{
+	if(v6addrtype(a) == linklocalv6)
+		return IPallbits;
+	return IPnoaddr;
+}
+
+static int
+comprefixlen(uchar *a, uchar *b, int n)
+{
+	int i, c;
+
+	for(i = 0; i < n; i++){
+		if((c = a[i] ^ b[i]) == 0)
+			continue;
+		for(i <<= 3; (c & 0x80) == 0; i++)
+			c <<= 1;
+		return i;
+	}
+	return i << 3;
+}
+
 /*
  *  link in a new medium
  */
@@ -181,18 +224,18 @@
 	}
 
 	/* disassociate logical interfaces (before zeroing ifc->arg) */
-	while(ifc->lifc){
-		err = ipifcremlifc(ifc, ifc->lifc);
+	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)
+		if(err != nil)
 			error(err);
 	}
 
 	/* disassociate device */
-	if(ifc->m && ifc->m->unbind)
+	if(ifc->m != nil && ifc->m->unbind != nil)
 		(*ifc->m->unbind)(ifc);
 	memset(ifc->dev, 0, sizeof(ifc->dev));
 	ifc->arg = nil;
@@ -257,9 +300,9 @@
 	m = 0;
 
 	rlock(ifc);
-	for(lifc = ifc->lifc; lifc; lifc = lifc->next){
+	for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
 		m += snprint(state+m, n - m, "%-40.40I ->", lifc->local);
-		for(link = lifc->link; link; link = link->lifclink)
+		for(link = lifc->link; link != nil; link = link->lifclink)
 			m += snprint(state+m, n - m, " %-40.40I", link->self->a);
 		m += snprint(state+m, n - m, "\n");
 	}
@@ -299,7 +342,7 @@
 		runlock(ifc);
 		nexterror();
 	}
-	if(ifc->m && ifc->m->pktin)
+	if(ifc->m != nil && ifc->m->pktin != nil)
 		(*ifc->m->pktin)(c->p->f, ifc, bp);
 	else
 		freeb(bp);
@@ -335,11 +378,9 @@
 ipifcclose(Conv *c)
 {
 	Ipifc *ifc;
-	Medium *m;
 
 	ifc = (Ipifc*)c->ptcl;
-	m = ifc->m;
-	if(m && m->unbindonclose)
+	if(ifc->m != nil && ifc->m->unbindonclose)
 		ipifcunbind(ifc);
 }
 
@@ -366,13 +407,14 @@
 char*
 ipifcadd(Ipifc *ifc, char **argv, int argc, int tentative, Iplifc *lifcp)
 {
-	int i, type, mtu, sendnbrdisc = 0;
 	uchar ip[IPaddrlen], mask[IPaddrlen], rem[IPaddrlen];
 	uchar bcast[IPaddrlen], net[IPaddrlen];
 	Iplifc *lifc, **l;
+	int i, type, mtu;
+	Medium *m;
 	Fs *f;
 
-	if(ifc->m == nil)
+	if((m = ifc->m) == nil)
 		return "ipifc not yet bound to device";
 
 	f = ifc->conv->p->f;
@@ -388,7 +430,7 @@
 		/* fall through */
 	case 5:
 		mtu = strtoul(argv[4], 0, 0);
-		if(mtu >= ifc->m->mintu && mtu <= ifc->m->maxtu)
+		if(mtu >= m->mintu && mtu <= m->maxtu)
 			ifc->maxtu = mtu;
 		/* fall through */
 	case 4:
@@ -414,8 +456,16 @@
 	default:
 		return Ebadarg;
 	}
-	if(isv4(ip))
+
+	/* check for point-to-point interface */
+	if(ipcmp(ip, v6loopback) != 0) /* skip v6 loopback, it's a special address */
+	if(ipcmp(mask, IPallbits) == 0)
+		type |= Rptpt;
+
+	if(isv4(ip) || ipcmp(ip, IPnoaddr) == 0){
+		type |= Rv4;
 		tentative = 0;
+	}
 
 	wlock(ifc);
 	if(waserror()){
@@ -424,19 +474,21 @@
 	}
 
 	/* 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) {
-			if(lifc->tentative != tentative)
-				lifc->tentative = tentative;
-			if(lifcp) {
-				lifc->onlink = lifcp->onlink;
-				lifc->autoflag = lifcp->autoflag;
-				lifc->validlt = lifcp->validlt;
-				lifc->preflt = lifcp->preflt;
-				lifc->origint = lifcp->origint;
-			}
-			goto out;
+	if((lifc = iplocalonifc(ifc, ip)) != nil){
+		if(lifcp != nil) {
+			lifc->onlink = lifcp->onlink;
+			lifc->autoflag = lifcp->autoflag;
+			lifc->validlt = lifcp->validlt;
+			lifc->preflt = lifcp->preflt;
+			lifc->origint = lifcp->origint;
 		}
+		if(lifc->tentative != tentative){
+			lifc->tentative = tentative;
+			goto done;
+		}
+		wunlock(ifc);
+		poperror();
+		return nil;
 	}
 
 	/* add the address to the list of logical ifc's for this ifc */
@@ -445,8 +497,9 @@
 	ipmove(lifc->mask, mask);
 	ipmove(lifc->remote, rem);
 	ipmove(lifc->net, net);
+	lifc->type = type;
 	lifc->tentative = tentative;
-	if(lifcp) {
+	if(lifcp != nil) {
 		lifc->onlink = lifcp->onlink;
 		lifc->autoflag = lifcp->autoflag;
 		lifc->validlt = lifcp->validlt;
@@ -459,29 +512,22 @@
 	}
 	lifc->next = nil;
 
-	for(l = &ifc->lifc; *l; l = &(*l)->next)
+	for(l = &ifc->lifc; *l != nil; l = &(*l)->next)
 		;
 	*l = lifc;
 
-	/* check for point-to-point interface */
-	if(ipcmp(ip, v6loopback)) /* skip v6 loopback, it's a special address */
-	if(ipcmp(mask, IPallbits) == 0)
-		type |= Rptpt;
+	addroute(f, rem, mask, ip, defsmask(ip), rem, type, ifc, tifc);
 
-	/* add local routes */
-	if(isv4(ip))
-		v4addroute(f, tifc, rem+IPv4off, mask+IPv4off, rem+IPv4off, type);
-	else
-		v6addroute(f, tifc, rem, mask, rem, type);
-
 	addselfcache(f, ifc, lifc, ip, Runi);
 
-	if((type & (Rproxy|Rptpt)) == (Rproxy|Rptpt)){
-		ipifcregisterproxy(f, ifc, rem);
-		goto out;
+	/* register proxy */
+	if(type & Rptpt){
+		if(type & Rproxy)
+			ipifcregisterproxy(f, ifc, rem, 1);
+		goto done;
 	}
 
-	if(isv4(ip) || ipcmp(ip, IPnoaddr) == 0) {
+	if(type & Rv4) {
 		/* add subnet directed broadcast address to the self cache */
 		for(i = 0; i < IPaddrlen; i++)
 			bcast[i] = (ip[i] & mask[i]) | ~mask[i];
@@ -505,15 +551,15 @@
 		addselfcache(f, ifc, lifc, bcast, Rbcast);
 
 		addselfcache(f, ifc, lifc, IPv4bcast, Rbcast);
-	}
-	else {
+	} 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);
+			addroute(f, v6allnodesN, v6allnodesNmask,
+				ip, IPallbits,
+				v6allnodesN, Rmulti, ifc, tifc);
 		}
 
 		/* add all nodes multicast address */
@@ -520,30 +566,22 @@
 		addselfcache(f, ifc, lifc, v6allnodesL, Rmulti);
 
 		/* add route for all nodes multicast */
-		v6addroute(f, tifc, v6allnodesL, v6allnodesLmask, v6allnodesL,
-			Rmulti);
+		addroute(f, v6allnodesL, v6allnodesLmask,
+			ip, IPallbits,
+			v6allnodesL, Rmulti, ifc, tifc);
 
 		/* add solicited-node multicast address */
 		ipv62smcast(bcast, ip);
 		addselfcache(f, ifc, lifc, bcast, Rmulti);
-
-		sendnbrdisc = 1;
 	}
 
-	/* register the address on this network for address resolution */
-	if(isv4(ip) && ifc->m->areg)
-		(*ifc->m->areg)(ifc, ip);
-
-out:
+done:
 	wunlock(ifc);
 	poperror();
 
-	if(!isv4(ip) && ipcmp(ip, IPnoaddr) != 0){
-		if(!tentative)
-			arpenter(f, V6, ip, ifc->mac, 6, 0);
-		else if(sendnbrdisc)
-			icmpns(f, 0, SRC_UNSPEC, ip, TARG_MULTI, ifc->mac);
-	}
+	/* register the address on this network for address resolution */
+	if(m->areg != nil)
+		(*m->areg)(f, ifc, ip, ip);
 
 	return nil;
 }
@@ -553,41 +591,44 @@
  *  always called with ifc wlock'd
  */
 static char*
-ipifcremlifc(Ipifc *ifc, Iplifc *lifc)
+ipifcremlifc(Ipifc *ifc, Iplifc **l)
 {
-	Iplifc **l;
-	Fs *f;
+	Iplifc *lifc = *l;
+	Fs *f = ifc->conv->p->f;
 
-	f = ifc->conv->p->f;
-
-	/*
-	 *  find address on this interface and remove from chain.
-	 *  for pt to pt we actually specify the remote address as the
-	 *  addresss to remove.
-	 */
-	for(l = &ifc->lifc; *l != nil && *l != lifc; l = &(*l)->next)
-		;
-	if(*l == nil)
+	if(lifc == nil)
 		return "address not on this interface";
 	*l = lifc->next;
 
 	/* disassociate any addresses */
-	while(lifc->link)
+	while(lifc->link != nil)
 		remselfcache(f, ifc, lifc, lifc->link->self->a);
 
 	/* remove the route for this logical interface */
-	if(isv4(lifc->local))
-		v4delroute(f, lifc->remote+IPv4off, lifc->mask+IPv4off, 1);
-	else {
-		v6delroute(f, lifc->remote, lifc->mask, 1);
+	remroute(f, lifc->remote, lifc->mask,
+		lifc->local, defsmask(lifc->local),
+		lifc->remote, lifc->type, ifc, tifc);
+
+	/* unregister proxy */
+	if(lifc->type & Rptpt){
+		if(lifc->type & Rproxy)
+			ipifcregisterproxy(f, ifc, lifc->remote, 0);
+		goto done;
+	}
+
+	/* remove route for all nodes multicast */
+	if((lifc->type & Rv4) == 0){
 		if(ipcmp(lifc->local, v6loopback) == 0)
-			/* remove route for all node multicast */
-			v6delroute(f, v6allnodesN, v6allnodesNmask, 1);
-		else if(memcmp(lifc->local, v6linklocal, v6llpreflen) == 0)
-			/* remove route for all link multicast */
-			v6delroute(f, v6allnodesL, v6allnodesLmask, 1);
+			remroute(f, v6allnodesN, v6allnodesNmask,
+				lifc->local, IPallbits,
+				v6allnodesN, Rmulti, ifc, tifc);
+
+		remroute(f, v6allnodesL, v6allnodesLmask,
+			lifc->local, IPallbits,
+			v6allnodesL, Rmulti, ifc, tifc);
 	}
 
+done:
 	free(lifc);
 	return nil;
 }
@@ -601,19 +642,17 @@
 {
 	char *rv;
 	uchar ip[IPaddrlen], mask[IPaddrlen], rem[IPaddrlen];
-	Iplifc *lifc;
+	Iplifc *lifc, **l;
 
 	if(argc < 3)
 		return Ebadarg;
-
-	if (parseip(ip, argv[1]) == -1)
+	if(parseip(ip, argv[1]) == -1)
 		return Ebadip;
 	parseipmask(mask, argv[2]);
 	if(argc < 4)
 		maskip(ip, mask, rem);
-	else
-		if (parseip(rem, argv[3]) == -1)
-			return Ebadip;
+	else if(parseip(rem, argv[3]) == -1)
+		return Ebadip;
 
 	/*
 	 *  find address on this interface and remove from chain.
@@ -621,62 +660,20 @@
 	 *  addresss to remove.
 	 */
 	wlock(ifc);
+	l = &ifc->lifc;
 	for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next) {
-		if (memcmp(ip, lifc->local, IPaddrlen) == 0
-		&& memcmp(mask, lifc->mask, IPaddrlen) == 0
-		&& memcmp(rem, lifc->remote, IPaddrlen) == 0)
+		if(ipcmp(ip, lifc->local) == 0
+		&& ipcmp(mask, lifc->mask) == 0
+		&& ipcmp(rem, lifc->remote) == 0)
 			break;
+		l = &lifc->next;
 	}
-	rv = ipifcremlifc(ifc, lifc);
+	rv = ipifcremlifc(ifc, l);
 	wunlock(ifc);
 	return rv;
 }
 
 /*
- * distribute routes to active interfaces like the
- * TRIP linecards
- */
-void
-ipifcaddroute(Fs *f, int vers, uchar *addr, uchar *mask, uchar *gate, int type)
-{
-	Medium *m;
-	Conv **cp, **e;
-	Ipifc *ifc;
-
-	e = &f->ipifc->conv[f->ipifc->nc];
-	for(cp = f->ipifc->conv; cp < e; cp++){
-		if(*cp != nil) {
-			ifc = (Ipifc*)(*cp)->ptcl;
-			m = ifc->m;
-			if(m && m->addroute)
-				(*m->addroute)(ifc, vers, addr, mask, gate, type);
-		}
-	}
-}
-
-void
-ipifcremroute(Fs *f, int vers, uchar *addr, uchar *mask)
-{
-	Medium *m;
-	Conv **cp, **e;
-	Ipifc *ifc;
-
-	e = &f->ipifc->conv[f->ipifc->nc];
-	for(cp = f->ipifc->conv; cp < e; cp++){
-		if(*cp != nil) {
-			ifc = (Ipifc*)(*cp)->ptcl;
-			m = ifc->m;
-			if(m && m->remroute){
-				if(!waserror()){
-					(*m->remroute)(ifc, vers, addr, mask);
-					poperror();
-				}
-			}
-		}
-	}
-}
-
-/*
  *  associate an address with the interface.  This wipes out any previous
  *  addresses.  This is a macro that means, remove all the old interfaces
  *  and add a new one.
@@ -693,9 +690,9 @@
 		 return "ipifc not yet bound to device";
 
 	wlock(ifc);
-	while(ifc->lifc){
-		err = ipifcremlifc(ifc, ifc->lifc);
-		if(err){
+	while(ifc->lifc != nil){
+		err = ipifcremlifc(ifc, &ifc->lifc);
+		if(err != nil){
 			wunlock(ifc);
 			return err;
 		}
@@ -765,7 +762,7 @@
  *  called with c->car locked.
  */
 static char*
-ipifcctl(Conv* c, char**argv, int argc)
+ipifcctl(Conv* c, char **argv, int argc)
 {
 	Ipifc *ifc;
 	int i;
@@ -779,10 +776,6 @@
 		return ipifcrem(ifc, argv, argc);
 	else if(strcmp(argv[0], "unbind") == 0)
 		return ipifcunbind(ifc);
-	else if(strcmp(argv[0], "joinmulti") == 0)
-		return ipifcjoinmulti(ifc, argv, argc);
-	else if(strcmp(argv[0], "leavemulti") == 0)
-		return ipifcleavemulti(ifc, argv, argc);
 	else if(strcmp(argv[0], "mtu") == 0)
 		return ipifcsetmtu(ifc, argv, argc);
 	else if(strcmp(argv[0], "reassemble") == 0){
@@ -845,10 +838,12 @@
 static void
 addselfcache(Fs *f, Ipifc *ifc, Iplifc *lifc, uchar *a, int type)
 {
-	Ipself *p;
 	Iplink *lp;
+	Ipself *p;
 	int h;
 
+	type |= (lifc->type & Rv4);
+
 	qlock(f->self);
 	if(waserror()){
 		qunlock(f->self);
@@ -857,8 +852,8 @@
 
 	/* see if the address already exists */
 	h = hashipa(a);
-	for(p = f->self->hash[h]; p; p = p->next)
-		if(memcmp(a, p->a, IPaddrlen) == 0)
+	for(p = f->self->hash[h]; p != nil; p = p->next)
+		if(ipcmp(a, p->a) == 0)
 			break;
 
 	/* allocate a local address and add to hash chain */
@@ -875,7 +870,7 @@
 	}
 
 	/* look for a link for this lifc */
-	for(lp = p->link; lp; lp = lp->selflink)
+	for(lp = p->link; lp != nil; lp = lp->selflink)
 		if(lp->lifc == lifc)
 			break;
 
@@ -891,13 +886,11 @@
 		lifc->link = lp;
 
 		/* add to routing table */
-		if(isv4(a))
-			v4addroute(f, tifc, a+IPv4off, IPallbits+IPv4off,
-				a+IPv4off, type);
-		else
-			v6addroute(f, tifc, a, IPallbits, a, type);
+		addroute(f, a, IPallbits,
+			lifc->local, defsmask(a),
+			a, type, ifc, tifc);
 
-		if((type & Rmulti) && ifc->m->addmulti)
+		if((type & Rmulti) && ifc->m->addmulti != nil)
 			(*ifc->m->addmulti)(ifc, a, lifc->local);
 	} else
 		lp->ref++;
@@ -922,8 +915,8 @@
 	ulong now = NOW;
 
 	l = &freeiplink;
-	for(np = *l; np; np = *l){
-		if(np->expire > now){
+	for(np = *l; np != nil; np = *l){
+		if((long)(now - np->expire) >= 0){
 			*l = np->next;
 			free(np);
 			continue;
@@ -942,8 +935,8 @@
 	ulong now = NOW;
 
 	l = &freeipself;
-	for(np = *l; np; np = *l){
-		if(np->expire > now){
+	for(np = *l; np != nil; np = *l){
+		if((long)(now - np->expire) >= 0){
 			*l = np->next;
 			free(np);
 			continue;
@@ -970,7 +963,7 @@
 
 	/* find the unique selftab entry */
 	l = &f->self->hash[hashipa(a)];
-	for(p = *l; p; p = *l){
+	for(p = *l; p != nil; p = *l){
 		if(ipcmp(p->a, a) == 0)
 			break;
 		l = &p->next;
@@ -984,7 +977,7 @@
 	 *  that matches the selftab entry
 	 */
 	l_lifc = &lifc->link;
-	for(link = *l_lifc; link; link = *l_lifc){
+	for(link = *l_lifc; link != nil; link = *l_lifc){
 		if(link->self == p)
 			break;
 		l_lifc = &link->lifclink;
@@ -998,7 +991,7 @@
 	 *  the one we just found
 	 */
 	l_self = &p->link;
-	for(link = *l_self; link; link = *l_self){
+	for(link = *l_self; link != nil; link = *l_self){
 		if(link == *l_lifc)
 			break;
 		l_self = &link->selflink;
@@ -1010,7 +1003,12 @@
 	if(--(link->ref) != 0)
 		goto out;
 
-	if((p->type & Rmulti) && ifc->m->remmulti){
+	/* remove from routing table */
+	remroute(f, a, IPallbits,
+		lifc->local, defsmask(a),
+		a, p->type, ifc, tifc);
+
+	if((p->type & Rmulti) && ifc->m->remmulti != nil){
 		if(!waserror()){
 			(*ifc->m->remmulti)(ifc, a, lifc->local);
 			poperror();
@@ -1025,30 +1023,18 @@
 	if(p->link != nil)
 		goto out;
 
-	/* remove from routing table */
-	if(isv4(a))
-		v4delroute(f, a+IPv4off, IPallbits+IPv4off, 1);
-	else
-		v6delroute(f, a, IPallbits, 1);
+	/* if null address, forget */
+	if(ipcmp(a, v4prefix) == 0 || ipcmp(a, IPnoaddr) == 0)
+		f->self->acceptall = 0;
 
 	/* no more links, remove from hash and free */
 	*l = p->next;
 	ipselffree(p);
 
-	/* if IPnoaddr, forget */
-	if(ipcmp(a, v4prefix) == 0 || ipcmp(a, IPnoaddr) == 0)
-		f->self->acceptall = 0;
-
 out:
 	qunlock(f->self);
 }
 
-static char *stformat = "%-44.44I %2.2d %4.4s\n";
-enum
-{
-	Nstformat= 41,
-};
-
 long
 ipselftabread(Fs *f, char *cp, ulong offset, int n)
 {
@@ -1063,10 +1049,11 @@
 	for(i = 0; i < NHASH && m < n; i++){
 		for(p = f->self->hash[i]; p != nil && m < n; p = p->next){
 			nifc = 0;
-			for(link = p->link; link; link = link->selflink)
+			for(link = p->link; link != nil; link = link->selflink)
 				nifc++;
 			routetype(p->type, state);
-			m += snprint(cp + m, n - m, stformat, p->a, nifc, state);
+			m += snprint(cp + m, n - m, "%-44.44I %2.2d %4.4s\n",
+				p->a, nifc, state);
 			if(off > 0){
 				off -= m;
 				m = 0;
@@ -1082,11 +1069,10 @@
 {
  	Ipself *p;
 
-	p = f->self->hash[hashipa(addr)];
-	for(; p; p = p->next){
+	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;
 }
 
@@ -1102,15 +1088,14 @@
 {
 	Ipself *p;
 
-	p = f->self->hash[hashipa(addr)];
-	for(; p; p = p->next){
+	for(p = f->self->hash[hashipa(addr)]; p != nil; p = p->next)
 		if(ipcmp(addr, p->a) == 0)
-			return p->type;
-	}
+			return p->type & (Runi|Rbcast|Rmulti);
 
 	/* hack to say accept anything */
 	if(f->self->acceptall)
 		return Runi;
+
 	return 0;
 }
 
@@ -1119,70 +1104,64 @@
  *  return nil.
  */
 Ipifc*
-findipifc(Fs *f, uchar *remote, int type)
+findipifc(Fs *f, uchar *local, uchar *remote, int type)
 {
+	uchar gnet[IPaddrlen];
 	Ipifc *ifc, *x;
 	Iplifc *lifc;
 	Conv **cp, **e;
-	uchar gnet[IPaddrlen], xmask[IPaddrlen];
+	int spec, xspec;
 
 	x = nil;
-	memset(xmask, 0, IPaddrlen);
+	xspec = 0;
 
 	/* find most specific match */
 	e = &f->ipifc->conv[f->ipifc->nc];
 	for(cp = f->ipifc->conv; cp < e; cp++){
-		if(*cp == 0)
+		if(*cp == nil)
 			continue;
 		ifc = (Ipifc*)(*cp)->ptcl;
-		for(lifc = ifc->lifc; lifc; lifc = lifc->next){
+		for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
+			if(type & Runi){
+				if(ipcmp(remote, lifc->local) == 0)
+					return ifc;
+			} else if(type & (Rbcast|Rmulti)) {
+				if(ipcmp(local, lifc->local) == 0)
+					return ifc;
+			}
 			maskip(remote, lifc->mask, gnet);
 			if(ipcmp(gnet, lifc->net) == 0){
-				if(x == nil || ipcmp(lifc->mask, xmask) > 0){
+				spec = comprefixlen(remote, lifc->local, IPaddrlen);
+				if(spec > xspec){
 					x = ifc;
-					ipmove(xmask, lifc->mask);
+					xspec = spec;
 				}
 			}
 		}
 	}
-	if(x != nil)
-		return x;
+	return x;
+}
 
-	/* for now for broadcast and multicast, just use first interface */
-	if(type & (Rbcast|Rmulti)){
-		for(cp = f->ipifc->conv; cp < e; cp++){
-			if(*cp == 0)
-				continue;
-			ifc = (Ipifc*)(*cp)->ptcl;
-			if(ifc->lifc != nil)
-				return ifc;
-		}
+Ipifc*
+findipifcstr(Fs *f, char *s)
+{
+	uchar ip[IPaddrlen];
+	Conv *c;
+	char *p;
+	long x;
+
+	x = strtol(s, &p, 10);
+	if(p > s && *p == '\0'){
+		if(x < 0)
+			return nil;
+		if(x < f->ipifc->nc && (c = f->ipifc->conv[x]) != nil)
+			return (Ipifc*)c->ptcl;
 	}
+	if(parseip(ip, s) != -1)
+		return findipifc(f, IPnoaddr, ip, Runi);
 	return nil;
 }
 
-enum {
-	unknownv6,		/* UGH */
-	unspecifiedv6,
-	linklocalv6,
-	globalv6,
-};
-
-int
-v6addrtype(uchar *addr)
-{
-	if(isv4(addr) || ipcmp(addr, IPnoaddr) == 0)
-		return unknownv6;
-	else if(islinklocal(addr) ||
-	    isv6mcast(addr) && (addr[1] & 0xF) <= Link_local_scop)
-		return linklocalv6;
-	else
-		return globalv6;
-}
-
-#define v6addrcurr(lifc) ((lifc)->preflt == ~0L || \
-			(lifc)->origint + (lifc)->preflt >= NOW/1000)
-
 static void
 findprimaryipv6(Fs *f, uchar *local)
 {
@@ -1200,10 +1179,10 @@
 	 */
 	e = &f->ipifc->conv[f->ipifc->nc];
 	for(cp = f->ipifc->conv; cp < e; cp++){
-		if(*cp == 0)
+		if(*cp == nil)
 			continue;
 		ifc = (Ipifc*)(*cp)->ptcl;
-		for(lifc = ifc->lifc; lifc; lifc = lifc->next){
+		for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
 			atypel = v6addrtype(lifc->local);
 			if(atypel > atype && v6addrcurr(lifc)) {
 				ipmove(local, lifc->local);
@@ -1216,7 +1195,7 @@
 }
 
 /*
- *  returns first ip address configured
+ *  returns first v4 address configured
  */
 static void
 findprimaryipv4(Fs *f, uchar *local)
@@ -1228,10 +1207,10 @@
 	/* find first ifc local address */
 	e = &f->ipifc->conv[f->ipifc->nc];
 	for(cp = f->ipifc->conv; cp < e; cp++){
-		if(*cp == 0)
+		if(*cp == nil)
 			continue;
 		ifc = (Ipifc*)(*cp)->ptcl;
-		if((lifc = ifc->lifc) != nil){
+		if((lifc = ifc->lifc) != nil && (lifc->type & Rv4) != 0){
 			ipmove(local, lifc->local);
 			return;
 		}
@@ -1239,21 +1218,6 @@
 	ipmove(local, IPnoaddr);
 }
 
-static int
-comprefixlen(uchar *a, uchar *b, int n)
-{
-	int i, c;
-
-	for(i = 0; i < n; i++){
-		if((c = a[i] ^ b[i]) == 0)
-			continue;
-		for(i <<= 3; (c & 0x80) == 0; i++)
-			c <<= 1;
-		return i;
-	}
-	return i << 3;
-}
-
 /*
  *  return v4 address associated with an interface close to remote
  */
@@ -1265,7 +1229,7 @@
 
 	b = -1;
 	for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
-		if(!isv4(lifc->local))
+		if((lifc->type & Rv4) == 0)
 			continue;
 		a = comprefixlen(lifc->local+IPv4off, remote, IPv4addrlen);
 		if(a > b){
@@ -1337,27 +1301,38 @@
 	return b.atype >= atype;
 }
 
-/*
- *  find the local address 'closest' to the remote system, copy it to local
- */
 void
 findlocalip(Fs *f, uchar *local, uchar *remote)
 {
 	Route *r;
+	Ipifc *ifc;
+	Iplifc *lifc;
+	Conv **cp, **e;
 
 	qlock(f->ipifc);
-	if((r = v6lookup(f, remote, nil)) != nil){
-		if(r->type & Runi){
-			ipmove(local, remote);
-			goto out;
-		}
-		if((r->type & (Rifc|Rbcast|Rmulti|Rv4)) == Rv4){
-			ipmove(local, v4prefix);
-			if(ipv4local(r->ifc, local+IPv4off, r->v4.gate))
+	e = &f->ipifc->conv[f->ipifc->nc];
+	for(cp = f->ipifc->conv; cp < e; cp++){
+		if(*cp == nil)
+			continue;
+		ifc = (Ipifc*)(*cp)->ptcl;
+		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)
+				continue;
+			if(r->type & Runi){
+				ipmove(local, remote);
 				goto out;
+			}
+			if((r->type & (Rifc|Rbcast|Rmulti|Rv4)) == Rv4){
+				ipmove(local, v4prefix);
+				if(ipv4local(ifc, local+IPv4off, r->v4.gate))
+					goto out;
+			}
+			if(ipv6local(ifc, local, remote))
+				goto out;
 		}
-		if(ipv6local(r->ifc, local, remote))
-			goto out;
 	}
 	if(isv4(remote))
 		findprimaryipv4(f, local);
@@ -1367,6 +1342,7 @@
 	qunlock(f->ipifc);
 }
 
+
 /*
  *  see if this address is bound to the interface
  */
@@ -1375,13 +1351,28 @@
 {
 	Iplifc *lifc;
 
-	for(lifc = ifc->lifc; lifc; lifc = lifc->next)
+	for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next)
 		if(ipcmp(ip, lifc->local) == 0)
 			return lifc;
+
 	return nil;
 }
 
+Iplifc*
+ipremoteonifc(Ipifc *ifc, uchar *ip)
+{
+	uchar net[IPaddrlen];
+	Iplifc *lifc;
 
+	for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
+		maskip(ip, lifc->mask, net);
+		if(ipcmp(net, lifc->remote) == 0)
+			return lifc;
+	}
+	return nil;
+}
+
+
 /*
  *  See if we're proxying for this address on this interface
  */
@@ -1389,21 +1380,13 @@
 ipproxyifc(Fs *f, Ipifc *ifc, uchar *ip)
 {
 	Route *r;
-	uchar net[IPaddrlen];
-	Iplifc *lifc;
 
 	/* see if this is a direct connected pt to pt address */
-	r = v6lookup(f, ip, nil);
+	r = v6lookup(f, ip, ip, nil);
 	if(r == nil || (r->type & (Rifc|Rproxy)) != (Rifc|Rproxy))
 		return 0;
 
-	/* see if this is on the right interface */
-	for(lifc = ifc->lifc; lifc; lifc = lifc->next){
-		maskip(ip, lifc->mask, net);
-		if(ipcmp(net, lifc->remote) == 0)
-			return 1;
-	}
-	return 0;
+	return ipremoteonifc(ifc, ip) != nil;
 }
 
 /*
@@ -1441,15 +1424,15 @@
 void
 ipifcaddmulti(Conv *c, uchar *ma, uchar *ia)
 {
-	Ipifc *ifc;
-	Iplifc *lifc;
-	Conv **p;
 	Ipmulti *multi, **l;
+	Conv **cp, **e;
+	Iplifc *lifc;
+	Ipifc *ifc;
 	Fs *f;
 
 	f = c->p->f;
 
-	for(l = &c->multi; *l; l = &(*l)->next)
+	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 */
 
@@ -1458,18 +1441,18 @@
 	ipmove(multi->ia, ia);
 	multi->next = nil;
 
-	for(p = f->ipifc->conv; *p; p++){
-		if((*p)->inuse == 0)
+	e = &f->ipifc->conv[f->ipifc->nc];
+	for(cp = f->ipifc->conv; cp < e; cp++){
+		if((*cp) == nil || (*cp)->inuse == 0)
 			continue;
-		ifc = (Ipifc*)(*p)->ptcl;
+		ifc = (Ipifc*)(*cp)->ptcl;
 		wlock(ifc);
 		if(waserror()){
 			wunlock(ifc);
 			nexterror();
 		}
-		for(lifc = ifc->lifc; lifc; lifc = lifc->next)
-			if(ipcmp(ia, lifc->local) == 0)
-				addselfcache(f, ifc, lifc, ma, Rmulti);
+		if((lifc = iplocalonifc(ifc, ia)) != nil)
+			addselfcache(f, ifc, lifc, ma, Rmulti);
 		wunlock(ifc);
 		poperror();
 	}
@@ -1483,14 +1466,14 @@
 ipifcremmulti(Conv *c, uchar *ma, uchar *ia)
 {
 	Ipmulti *multi, **l;
+	Conv **cp, **e;
 	Iplifc *lifc;
-	Conv **p;
 	Ipifc *ifc;
 	Fs *f;
 
 	f = c->p->f;
 
-	for(l = &c->multi; *l; l = &(*l)->next)
+	for(l = &c->multi; *l != nil; l = &(*l)->next)
 		if(ipcmp(ma, (*l)->ma) == 0 && ipcmp(ia, (*l)->ia) == 0)
 			break;
 
@@ -1500,102 +1483,62 @@
 
 	*l = multi->next;
 
-	for(p = f->ipifc->conv; *p; p++){
-		if((*p)->inuse == 0)
+	e = &f->ipifc->conv[f->ipifc->nc];
+	for(cp = f->ipifc->conv; cp < e; cp++){
+		if((*cp) == nil || (*cp)->inuse == 0)
 			continue;
-
-		ifc = (Ipifc*)(*p)->ptcl;
+		ifc = (Ipifc*)(*cp)->ptcl;
 		wlock(ifc);
-		for(lifc = ifc->lifc; lifc; lifc = lifc->next)
-			if(ipcmp(ia, lifc->local) == 0)
-				remselfcache(f, ifc, lifc, ma);
+		if(waserror()){
+			wunlock(ifc);
+			nexterror();
+		}
+		if((lifc = iplocalonifc(ifc, ia)) != nil)
+			remselfcache(f, ifc, lifc, ma);
 		wunlock(ifc);
+		poperror();
 	}
 
 	free(multi);
 }
 
-/*
- *  make lifc's join and leave multicast groups
- */
-static char*
-ipifcjoinmulti(Ipifc *ifc, char **argv, int argc)
-{
-	USED(ifc, argv, argc);
-	return nil;
-}
-
-static char*
-ipifcleavemulti(Ipifc *ifc, char **argv, int argc)
-{
-	USED(ifc, argv, argc);
-	return nil;
-}
-
 static void
-ipifcregisterproxy(Fs *f, Ipifc *ifc, uchar *ip)
+ipifcregisterproxy(Fs *f, Ipifc *ifc, uchar *ip, int add)
 {
+	uchar proxy[IPaddrlen];
 	Conv **cp, **e;
-	Ipifc *nifc;
 	Iplifc *lifc;
+	Ipifc *nifc;
 	Medium *m;
-	uchar net[IPaddrlen];
 
-	/* register the address on any network that will proxy for us */
+	/* 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)
+			continue;
 
-	if(!isv4(ip)) {				/* V6 */
-		for(cp = f->ipifc->conv; cp < e; cp++){
-			if(*cp == nil || (nifc = (Ipifc*)(*cp)->ptcl) == ifc)
-				continue;
-			rlock(nifc);
-			m = nifc->m;
-			if(m == nil || m->addmulti == nil) {
-				runlock(nifc);
-				continue;
-			}
-			if(waserror()){
-				runlock(nifc);
-				nexterror();
-			}
-			for(lifc = nifc->lifc; lifc; lifc = lifc->next){
-				maskip(ip, lifc->mask, net);
-				if(ipcmp(net, lifc->remote) == 0) {
-					/* add solicited-node multicast addr */
-					ipv62smcast(net, ip);
-					addselfcache(f, nifc, lifc, net, Rmulti);
-					arpenter(f, V6, ip, nifc->mac, 6, 0);
-					break;
-				}
-			}
-			runlock(nifc);
-			poperror();
+		wlock(nifc);
+		m = nifc->m;
+		if(m == nil || m->areg == nil || waserror()){
+			wunlock(nifc);
+			continue;
 		}
-	}
-	else {					/* V4 */
-		for(cp = f->ipifc->conv; cp < e; cp++){
-			if(*cp == nil || (nifc = (Ipifc*)(*cp)->ptcl) == ifc)
-				continue;
-			rlock(nifc);
-			m = nifc->m;
-			if(m == nil || m->areg == nil){
-				runlock(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);
 			}
-			if(waserror()){
-				runlock(nifc);
-				nexterror();
-			}
-			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);
-			poperror();
+			ipmove(proxy, lifc->local);
 		}
+		wunlock(nifc);
+		poperror();
+
+		if(add && lifc != nil)
+			(*m->areg)(f, nifc, ip, proxy);
 	}
 }
 
--- a/sys/src/9/ip/iproute.c
+++ b/sys/src/9/ip/iproute.c
@@ -35,9 +35,8 @@
 static Route*
 allocroute(int type)
 {
-	Route *r;
+	Route *r, **l;
 	int n;
-	Route **l;
 
 	if(type & Rv4){
 		n = sizeof(RouteTree) + sizeof(V4route);
@@ -72,9 +71,9 @@
 		return;
 
 	l = allocroute(r->type);
+	l->left = r;
 	l->mid = *q;
 	*q = l;
-	l->left = r;
 }
 
 /*
@@ -99,11 +98,11 @@
  */
 enum
 {
-	Rpreceeds,
-	Rfollows,
-	Requals,
-	Rcontains,
-	Rcontained,
+	Rpreceeds,	/* a left of b */
+	Rfollows,	/* a right of b */
+	Requals,	/* a equals b */
+	Rcontains,	/* a contians b */
+	Roverlaps,	/* a overlaps b */
 };
 
 static int
@@ -112,44 +111,87 @@
 	if(a->type & Rv4){
 		if(a->v4.endaddress < b->v4.address)
 			return Rpreceeds;
-
 		if(a->v4.address > b->v4.endaddress)
 			return Rfollows;
-
 		if(a->v4.address <= b->v4.address
 		&& a->v4.endaddress >= b->v4.endaddress){
 			if(a->v4.address == b->v4.address
-			&& a->v4.endaddress == b->v4.endaddress)
-				return Requals;
+			&& a->v4.endaddress == b->v4.endaddress){
+				if(a->v4.source <= b->v4.source
+				&& a->v4.endsource >= b->v4.endsource){
+					if(a->v4.source == b->v4.source
+					&& a->v4.endsource == b->v4.endsource)
+						return Requals;
+					return Rcontains;
+				}
+				return Roverlaps;
+			}
 			return Rcontains;
 		}
-		return Rcontained;
+		return Roverlaps;
 	}
 
 	if(lcmp(a->v6.endaddress, b->v6.address) < 0)
 		return Rpreceeds;
-
 	if(lcmp(a->v6.address, b->v6.endaddress) > 0)
 		return Rfollows;
-
 	if(lcmp(a->v6.address, b->v6.address) <= 0
 	&& lcmp(a->v6.endaddress, b->v6.endaddress) >= 0){
 		if(lcmp(a->v6.address, b->v6.address) == 0
-		&& lcmp(a->v6.endaddress, b->v6.endaddress) == 0)
-				return Requals;
+		&& lcmp(a->v6.endaddress, b->v6.endaddress) == 0){
+			if(lcmp(a->v6.source, b->v6.source) <= 0
+			&& lcmp(a->v6.endsource, b->v6.endsource) >= 0){
+				if(lcmp(a->v6.source, b->v6.source) == 0
+				&& lcmp(a->v6.endsource, b->v6.endsource) == 0)
+					return Requals;
+				return Rcontains;
+			}
+			return Roverlaps;
+		}
 		return Rcontains;
 	}
+	return Roverlaps;
+}
 
-	return Rcontained;
+/* return 1 if a matches b, otherwise 0 */
+static int
+matchroute(Route *a, Route *b)
+{
+	if(a == b)
+		return 1;
+
+	if((a->type^b->type) & (Rifc|Runi|Rmulti|Rbcast))
+		return 0;
+
+	if(a->type & Rv4){
+		if(memcmp(a->v4.gate, IPnoaddr+IPv4off, IPv4addrlen) != 0
+		&& memcmp(a->v4.gate, b->v4.gate, IPv4addrlen) != 0)
+			return 0;
+	} else {
+		if(ipcmp(a->v6.gate, IPnoaddr) != 0
+		&& ipcmp(a->v6.gate, b->v6.gate) != 0)
+			return 0;
+	}
+
+	if(a->ifc != nil && b->ifc != nil && (a->ifc != b->ifc || a->ifcid != b->ifcid))
+		return 0;
+
+	if(*a->tag != 0 && strncmp(a->tag, b->tag, sizeof(a->tag)) != 0)
+		return 0;
+
+	return 1;
 }
 
 static void
 copygate(Route *old, Route *new)
 {
+	old->type = new->type;
+	old->ifc = new->ifc;
+	old->ifcid = new->ifcid;
 	if(new->type & Rv4)
 		memmove(old->v4.gate, new->v4.gate, IPv4addrlen);
 	else
-		memmove(old->v6.gate, new->v6.gate, IPaddrlen);
+		ipmove(old->v6.gate, new->v6.gate);
 }
 
 /*
@@ -162,12 +204,12 @@
 
 	l = p->left;
 	r = p->right;
-	p->left = 0;
-	p->right = 0;
+	p->left = nil;
+	p->right = nil;
 	addnode(f, root, p);
-	if(l)
+	if(l != nil)
 		walkadd(f, root, l);
-	if(r)
+	if(r != nil)
 		walkadd(f, root, r);
 }
 
@@ -180,16 +222,16 @@
 	Route *q;
 	int d;
 
-	if(p) {
+	if(p != nil) {
 		d = 0;
 		q = p->left;
-		if(q)
+		if(q != nil)
 			d = q->depth;
 		q = p->right;
-		if(q && q->depth > d)
+		if(q != nil && q->depth > d)
 			d = q->depth;
 		q = p->mid;
-		if(q && q->depth > d)
+		if(q != nil && q->depth > d)
 			d = q->depth;
 		p->depth = d+1;
 	}
@@ -210,8 +252,8 @@
 	 * rotate tree node
 	 */
 	p = *cur;
-	dl = 0; if(l = p->left) dl = l->depth;
-	dr = 0; if(r = p->right) dr = r->depth;
+	dl = 0; if((l = p->left) != nil) dl = l->depth;
+	dr = 0; if((r = p->right) != nil) dr = r->depth;
 
 	if(dl > dr+1) {
 		p->left = l->right;
@@ -239,7 +281,7 @@
 	Route *p;
 
 	p = *cur;
-	if(p == 0) {
+	if(p == nil) {
 		*cur = new;
 		new->depth = 1;
 		return;
@@ -269,15 +311,13 @@
 		 *  supercede the old entry if the old one isn't
 		 *  a local interface.
 		 */
-		if((p->type & Rifc) == 0){
-			p->type = new->type;
-			p->ifcid = -1;
+		if((p->type & Rifc) == 0)
 			copygate(p, new);
-		} else if(new->type & Rifc)
+		else if(new->type & Rifc)
 			p->ref++;
 		freeroute(new);
 		break;
-	case Rcontained:
+	case Roverlaps:
 		addnode(f, &p->mid, new);
 		break;
 	}
@@ -285,201 +325,220 @@
 	balancetree(cur);
 }
 
-#define	V4H(a)	((a&0x07ffffff)>>(32-Lroot-5))
-
-void
-v4addroute(Fs *f, char *tag, uchar *a, uchar *mask, uchar *gate, int type)
+/*
+ *  find node matching r
+ */
+static Route**
+looknode(Route **cur, Route *r)
 {
 	Route *p;
-	ulong sa;
-	ulong m;
-	ulong ea;
-	int h, eh;
 
-	m = nhgetl(mask);
-	sa = nhgetl(a) & m;
-	ea = sa | ~m;
+	for(;;){
+		p = *cur;
+		if(p == nil)
+			return nil;
+		switch(rangecompare(r, p)){
+		case Rcontains:
+			return nil;
+		case Rpreceeds:
+			cur = &p->left;
+			break;
+		case Rfollows:
+			cur = &p->right;
+			break;
+		case Roverlaps:
+			cur = &p->mid;
+			break;
+		case Requals:
+			if((p->type & Rifc) == 0 && !matchroute(r, p))
+				return nil;
+			return cur;
+		}
+	}
+}
 
-	eh = V4H(ea);
-	for(h=V4H(sa); h<=eh; h++) {
-		p = allocroute(Rv4 | type);
-		p->v4.address = sa;
-		p->v4.endaddress = ea;
-		memmove(p->v4.gate, gate, sizeof(p->v4.gate));
-		memmove(p->tag, tag, sizeof(p->tag));
+static Route*
+looknodetag(Route *r, char *tag)
+{
+	Route *x;
 
-		wlock(&routelock);
-		addnode(f, &f->v4root[h], p);
-		while(p = f->queue) {
-			f->queue = p->mid;
-			walkadd(f, &f->v4root[h], p->left);
-			freeroute(p);
-		}
-		wunlock(&routelock);
+	if(r == nil)
+		return nil;
+
+	if((x = looknodetag(r->mid, tag)) != nil)
+		return x;
+	if((x = looknodetag(r->left, tag)) != nil)
+		return x;
+	if((x = looknodetag(r->right, tag)) != nil)
+		return x;
+
+	if((r->type & Rifc) == 0){
+		if(tag == nil || strncmp(tag, r->tag, sizeof(r->tag)) == 0)
+			return r;
 	}
-	v4routegeneration++;
 
-	ipifcaddroute(f, Rv4, a, mask, gate, type);
+	return nil;
 }
 
-#define	V6H(a)	(((a)[IPllen-1] & 0x07ffffff)>>(32-Lroot-5))
+#define	V4H(a)	((a&0x07ffffff)>>(32-Lroot-5))
+#define	V6H(a)	(((a)[IPllen-1]&0x07ffffff)>>(32-Lroot-5))
 
-void
-v6addroute(Fs *f, char *tag, uchar *a, uchar *mask, uchar *gate, int type)
+static void
+routeadd(Fs *f, Route *r)
 {
-	Route *p;
-	ulong sa[IPllen], ea[IPllen];
-	ulong x, y;
-	int h, eh;
+	Route **h, **e, *p;
 
-	for(h = 0; h < IPllen; h++){
-		x = nhgetl(a+4*h);
-		y = nhgetl(mask+4*h);
-		sa[h] = x & y;
-		ea[h] = x | ~y;
+	if(r->type & Rv4){
+		h = &f->v4root[V4H(r->v4.address)];
+		e = &f->v4root[V4H(r->v4.endaddress)];
+	} else {
+		h = &f->v6root[V6H(r->v6.address)];
+		e = &f->v6root[V6H(r->v6.endaddress)];
 	}
 
-	eh = V6H(ea);
-	for(h = V6H(sa); h <= eh; h++) {
-		p = allocroute(type);
-		memmove(p->v6.address, sa, IPaddrlen);
-		memmove(p->v6.endaddress, ea, IPaddrlen);
-		memmove(p->v6.gate, gate, IPaddrlen);
-		memmove(p->tag, tag, sizeof(p->tag));
+	for(; h <= e; h++) {
+		p = allocroute(r->type);
 
-		wlock(&routelock);
-		addnode(f, &f->v6root[h], p);
-		while(p = f->queue) {
+		p->ifc = r->ifc;
+		p->ifcid = r->ifcid;
+
+		if(r->type & Rv4)
+			memmove(&p->v4, &r->v4, sizeof(r->v4));
+		else
+			memmove(&p->v6, &r->v6, sizeof(r->v6));
+
+		memmove(p->tag, r->tag, sizeof(r->tag));
+
+		addnode(f, h, p);
+		while((p = f->queue) != nil) {
 			f->queue = p->mid;
-			walkadd(f, &f->v6root[h], p->left);
+			walkadd(f, h, p->left);
 			freeroute(p);
 		}
-		wunlock(&routelock);
 	}
-	v6routegeneration++;
 
-	ipifcaddroute(f, 0, a, mask, gate, type);
+	if(r->type & Rv4)
+		v4routegeneration++;
+	else
+		v6routegeneration++;
 }
 
-Route**
-looknode(Route **cur, Route *r)
+static void
+routerem(Fs *f, Route *r)
 {
-	Route *p;
+	Route **h, **e, **l, *p;
 
-	for(;;){
-		p = *cur;
-		if(p == 0)
-			return 0;
-	
-		switch(rangecompare(r, p)){
-		case Rcontains:
-			return 0;
-		case Rpreceeds:
-			cur = &p->left;
-			break;
-		case Rfollows:
-			cur = &p->right;
-			break;
-		case Rcontained:
-			cur = &p->mid;
-			break;
-		case Requals:
-			return cur;
-		}
+	if(r->type & Rv4){
+		h = &f->v4root[V4H(r->v4.address)];
+		e = &f->v4root[V4H(r->v4.endaddress)];
+	} else {
+		h = &f->v6root[V6H(r->v6.address)];
+		e = &f->v6root[V6H(r->v6.endaddress)];
 	}
-}
 
-void
-v4delroute(Fs *f, uchar *a, uchar *mask, int dolock)
-{
-	Route **r, *p;
-	Route rt;
-	int h, eh;
-	ulong m;
+	for(; h <= e; h++) {
+		if((l = looknode(h, r)) == nil)
+			continue;
+		p = *l;
+		if(--(p->ref) != 0)
+			continue;
+		*l = nil;
+		addqueue(&f->queue, p->left);
+		addqueue(&f->queue, p->mid);
+		addqueue(&f->queue, p->right);
+		freeroute(p);
 
-	m = nhgetl(mask);
-	rt.v4.address = nhgetl(a) & m;
-	rt.v4.endaddress = rt.v4.address | ~m;
-	rt.type = Rv4;
-
-	eh = V4H(rt.v4.endaddress);
-	for(h=V4H(rt.v4.address); h<=eh; h++) {
-		if(dolock)
-			wlock(&routelock);
-		r = looknode(&f->v4root[h], &rt);
-		if(r) {
-			p = *r;
-			if(--(p->ref) == 0){
-				*r = 0;
-				addqueue(&f->queue, p->left);
-				addqueue(&f->queue, p->mid);
-				addqueue(&f->queue, p->right);
-				freeroute(p);
-				while(p = f->queue) {
-					f->queue = p->mid;
-					walkadd(f, &f->v4root[h], p->left);
-					freeroute(p);
-				}
-			}
+		while((p = f->queue) != nil) {
+			f->queue = p->mid;
+			walkadd(f, h, p->left);
+			freeroute(p);
 		}
-		if(dolock)
-			wunlock(&routelock);
 	}
-	v4routegeneration++;
 
-	ipifcremroute(f, Rv4, a, mask);
+	if(r->type & Rv4)
+		v4routegeneration++;
+	else
+		v6routegeneration++;
 }
 
-void
-v6delroute(Fs *f, uchar *a, uchar *mask, int dolock)
+static Route
+mkroute(uchar *a, uchar *mask, uchar *s, uchar *smask, uchar *gate, int type, Ipifc *ifc, char *tag)
 {
-	Route **r, *p;
-	Route rt;
-	int h, eh;
 	ulong x, y;
+	Route r;
+	int h;
 
-	for(h = 0; h < IPllen; h++){
-		x = nhgetl(a+4*h);
-		y = nhgetl(mask+4*h);
-		rt.v6.address[h] = x & y;
-		rt.v6.endaddress[h] = x | ~y;
-	}
-	rt.type = 0;
+	memset(&r, 0, sizeof(r));
 
-	eh = V6H(rt.v6.endaddress);
-	for(h=V6H(rt.v6.address); h<=eh; h++) {
-		if(dolock)
-			wlock(&routelock);
-		r = looknode(&f->v6root[h], &rt);
-		if(r) {
-			p = *r;
-			if(--(p->ref) == 0){
-				*r = 0;
-				addqueue(&f->queue, p->left);
-				addqueue(&f->queue, p->mid);
-				addqueue(&f->queue, p->right);
-				freeroute(p);
-				while(p = f->queue) {
-					f->queue = p->mid;
-					walkadd(f, &f->v6root[h], p->left);
-					freeroute(p);
-				}
-			}
+	r.type = type;
+
+	if(type & Rv4){
+		x = nhgetl(a+IPv4off);
+		y = nhgetl(mask+IPv4off);
+		r.v4.address = x & y;
+		r.v4.endaddress = x | ~y;
+
+		x = nhgetl(s+IPv4off);
+		y = nhgetl(smask+IPv4off);
+		if(y != 0)
+			r.type |= Rsrc;
+		r.v4.source = x & y;
+		r.v4.endsource = x | ~y;
+
+		memmove(r.v4.gate, gate+IPv4off, IPv4addrlen);
+	} else {
+		for(h = 0; h < IPllen; h++){
+			x = nhgetl(a+4*h);
+			y = nhgetl(mask+4*h);
+			r.v6.address[h] = x & y;
+			r.v6.endaddress[h] = x | ~y;
+
+			x = nhgetl(s+4*h);
+			y = nhgetl(smask+4*h);
+			if(y != 0)
+				r.type |= Rsrc;
+			r.v6.source[h] = x & y;
+			r.v6.endsource[h] = x | ~y;
 		}
-		if(dolock)
-			wunlock(&routelock);
+
+		memmove(r.v6.gate, gate, IPaddrlen);
 	}
-	v6routegeneration++;
 
-	ipifcremroute(f, 0, a, mask);
+	if(ifc != nil){
+		r.ifc = ifc;
+		r.ifcid = ifc->ifcid;
+	}
+
+	if(tag != nil)
+		strncpy(r.tag, tag, sizeof(r.tag));
+
+	return r;
 }
 
+void
+addroute(Fs *f, uchar *a, uchar *mask, uchar *s, uchar *smask, uchar *gate, int type, Ipifc *ifc, char *tag)
+{
+	Route r = mkroute(a, mask, s, smask, gate, type, ifc, tag);
+	wlock(&routelock);
+	routeadd(f, &r);
+	wunlock(&routelock);
+}
+
+void
+remroute(Fs *f, uchar *a, uchar *mask, uchar *s, uchar *smask, uchar *gate, int type, Ipifc *ifc, char *tag)
+{
+	Route r = mkroute(a, mask, s, smask, gate, type, ifc, tag);
+	wlock(&routelock);
+	routerem(f, &r);
+	wunlock(&routelock);
+}
+
 Route*
-v4lookup(Fs *f, uchar *a, Routehint *rh)
+v4lookup(Fs *f, uchar *a, uchar *s, Routehint *rh)
 {
+	uchar local[IPaddrlen], gate[IPaddrlen];
+	ulong la, ls;
 	Route *p, *q;
-	ulong la;
-	uchar gate[IPaddrlen];
 	Ipifc *ifc;
 
 	if(rh != nil && rh->r != nil && rh->r->ifc != nil && rh->rgen == v4routegeneration)
@@ -486,24 +545,39 @@
 		return rh->r;
 
 	la = nhgetl(a);
+	ls = nhgetl(s);
 	q = nil;
-	for(p=f->v4root[V4H(la)]; p;)
-		if(la >= p->v4.address) {
-			if(la <= p->v4.endaddress) {
-				q = p;
-				p = p->mid;
-			} else
-				p = p->right;
-		} else
+	for(p = f->v4root[V4H(la)]; p != nil;){
+		if(la < p->v4.address){
 			p = p->left;
+			continue;
+		}
+		if(la > p->v4.endaddress){
+			p = p->right;
+			continue;
+		}
+		if(p->type & Rsrc){
+			if(ls < p->v4.source){
+				p = p->mid;
+				continue;
+			}
+			if(ls > p->v4.endsource){
+				p = p->mid;
+				continue;
+			}
+		}
+		q = p;
+		p = p->mid;
+	}
 
-	if(q && (q->ifc == nil || q->ifcid != q->ifc->ifcid)){
+	if(q != nil && (q->ifc == nil || q->ifcid != q->ifc->ifcid)){
 		if(q->type & Rifc) {
 			hnputl(gate+IPv4off, q->v4.address);
 			memmove(gate, v4prefix, IPv4off);
 		} else
 			v4tov6(gate, q->v4.gate);
-		ifc = findipifc(f, gate, q->type);
+		v4tov6(local, s);
+		ifc = findipifc(f, local, gate, q->type);
 		if(ifc == nil)
 			return nil;
 		q->ifc = ifc;
@@ -519,29 +593,30 @@
 }
 
 Route*
-v6lookup(Fs *f, uchar *a, Routehint *rh)
+v6lookup(Fs *f, uchar *a, uchar *s, Routehint *rh)
 {
-	Route *p, *q;
-	ulong la[IPllen];
-	int h;
-	ulong x, y;
 	uchar gate[IPaddrlen];
+	ulong la[IPllen], ls[IPllen];
+	ulong x, y;
+	Route *p, *q;
 	Ipifc *ifc;
+	int h;
 
-	if(memcmp(a, v4prefix, IPv4off) == 0){
-		q = v4lookup(f, a+IPv4off, rh);
-		if(q != nil)
-			return q;
-	}
+	if(isv4(a) && isv4(s))
+		return v4lookup(f, a+IPv4off, s+IPv4off, rh);
+	if(isv4(s))
+		return nil;
 
 	if(rh != nil && rh->r != nil && rh->r->ifc != nil && rh->rgen == v6routegeneration)
 		return rh->r;
 
-	for(h = 0; h < IPllen; h++)
+	for(h = 0; h < IPllen; h++){
 		la[h] = nhgetl(a+4*h);
+		ls[h] = nhgetl(s+4*h);
+	}
 
 	q = nil;
-	for(p=f->v6root[V6H(la)]; p;){
+	for(p = f->v6root[V6H(la)]; p != nil;){
 		for(h = 0; h < IPllen; h++){
 			x = la[h];
 			y = p->v6.address[h];
@@ -564,18 +639,42 @@
 			}
 			break;
 		}
+		if(p->type & Rsrc){
+			for(h = 0; h < IPllen; h++){
+				x = ls[h];
+				y = p->v6.source[h];
+				if(x == y)
+					continue;
+				if(x < y){
+					p = p->mid;
+					goto next;
+				}
+				break;
+			}
+			for(h = 0; h < IPllen; h++){
+				x = ls[h];
+				y = p->v6.endsource[h];
+				if(x == y)
+					continue;
+				if(x > y){
+					p = p->mid;
+					goto next;
+				}
+				break;
+			}
+		}
 		q = p;
 		p = p->mid;
 next:		;
 	}
 
-	if(q && (q->ifc == nil || q->ifcid != q->ifc->ifcid)){
+	if(q != nil && (q->ifc == nil || q->ifcid != q->ifc->ifcid)){
 		if(q->type & Rifc) {
 			for(h = 0; h < IPllen; h++)
 				hnputl(gate+4*h, q->v6.address[h]);
-			ifc = findipifc(f, gate, q->type);
+			ifc = findipifc(f, s, gate, q->type);
 		} else
-			ifc = findipifc(f, q->v6.gate, q->type);
+			ifc = findipifc(f, s, q->v6.gate, q->type);
 		if(ifc == nil)
 			return nil;
 		q->ifc = ifc;
@@ -590,17 +689,49 @@
 	return q;
 }
 
+static int
+parseroutetype(char *p)
+{
+	int type = 0;
+	switch(*p++){
+	default:	return -1;	
+	case '4':	type |= Rv4;
+	case '6':	break;
+	}
+	for(;;) switch(*p++){
+	default: 
+		return -1;
+	case 'i':
+		if(((type ^= Rifc) & Rifc) != Rifc) return -1;
+		break;
+	case 'u':
+		if(((type ^= Runi) & (Runi|Rbcast|Rmulti)) != Runi) return -1;
+		break;
+	case 'b':
+		if(((type ^= Rbcast) & (Runi|Rbcast|Rmulti)) != Rbcast) return -1;
+		break;
+	case 'm':
+		if(((type ^= Rmulti) & (Runi|Rbcast|Rmulti)) != Rmulti) return -1;
+		break;
+	case 'p':
+		if(((type ^= Rptpt) & Rptpt) != Rptpt) return -1;
+		break;
+	case '\0':
+		return type;
+	}
+}
+
 void
-routetype(int type, char *p)
+routetype(int type, char p[8])
 {
-	memset(p, ' ', 4);
-	p[4] = 0;
 	if(type & Rv4)
 		*p++ = '4';
 	else
 		*p++ = '6';
+
 	if(type & Rifc)
 		*p++ = 'i';
+
 	if(type & Runi)
 		*p++ = 'u';
 	else if(type & Rbcast)
@@ -607,14 +738,14 @@
 		*p++ = 'b';
 	else if(type & Rmulti)
 		*p++ = 'm';
+
 	if(type & Rptpt)
-		*p = 'p';
+		*p++ = 'p';
+	*p = 0;
 }
 
-static char *rformat = "%-15I %-4M %-15I %4.4s %4.4s %3s\n";
-
-void
-convroute(Route *r, uchar *addr, uchar *mask, uchar *gate, char *t, int *nifc)
+static void
+convroute(Route *r, uchar *addr, uchar *mask, uchar *src, uchar *smask, uchar *gate)
 {
 	int i;
 
@@ -621,8 +752,16 @@
 	if(r->type & Rv4){
 		memmove(addr, v4prefix, IPv4off);
 		hnputl(addr+IPv4off, r->v4.address);
+
 		memset(mask, 0xff, IPv4off);
 		hnputl(mask+IPv4off, ~(r->v4.endaddress ^ r->v4.address));
+
+		memmove(src, v4prefix, IPv4off);
+		hnputl(src+IPv4off, r->v4.source);
+
+		memset(smask, 0xff, IPv4off);
+		hnputl(smask+IPv4off, ~(r->v4.endsource ^ r->v4.source));
+
 		memmove(gate, v4prefix, IPv4off);
 		memmove(gate+IPv4off, r->v4.gate, IPv4addrlen);
 	} else {
@@ -629,162 +768,187 @@
 		for(i = 0; i < IPllen; i++){
 			hnputl(addr + 4*i, r->v6.address[i]);
 			hnputl(mask + 4*i, ~(r->v6.endaddress[i] ^ r->v6.address[i]));
+			hnputl(src + 4*i, r->v6.source[i]);
+			hnputl(smask + 4*i, ~(r->v6.endsource[i] ^ r->v6.source[i]));
 		}
 		memmove(gate, r->v6.gate, IPaddrlen);
 	}
+}
 
-	routetype(r->type, t);
+static char*
+seprintroute(char *p, char *e, Route *r)
+{
+	uchar addr[IPaddrlen], mask[IPaddrlen], src[IPaddrlen], smask[IPaddrlen], gate[IPaddrlen];
+	char type[8], ifbuf[4], *iname;
 
-	if(r->ifc)
-		*nifc = r->ifc->conv->x;
+	convroute(r, addr, mask, src, smask, gate);
+	routetype(r->type, type);
+	if(r->ifc != nil && r->ifcid == r->ifc->ifcid)
+		snprint(iname = ifbuf, sizeof ifbuf, "%d", r->ifc->conv->x);
 	else
-		*nifc = -1;
+		iname = "-";
+	return seprint(p, e, "%-15I %-4M %-15I %-4s %4.4s %3s %-15I %-4M\n",
+		addr, mask, gate, type, r->tag, iname, src, smask);
 }
 
-/*
- *  this code is not in rr to reduce stack size
- */
-static void
-sprintroute(Route *r, Routewalk *rw)
+typedef struct Routewalk Routewalk;
+struct Routewalk
 {
-	int nifc, n;
-	char t[5], *iname, ifbuf[5];
-	uchar addr[IPaddrlen], mask[IPaddrlen], gate[IPaddrlen];
-	char *p;
+	int	o;
+	int	h;
+	char*	p;
+	char*	e;
+};
 
-	convroute(r, addr, mask, gate, t, &nifc);
-	iname = "-";
-	if(nifc != -1) {
-		iname = ifbuf;
-		snprint(ifbuf, sizeof ifbuf, "%d", nifc);
-	}
-	p = seprint(rw->p, rw->e, rformat, addr, mask, gate, t, r->tag, iname);
+static int
+rr1(Routewalk *rw, Route *r)
+{
+	int n = seprintroute(rw->p, rw->e, r) - rw->p;
 	if(rw->o < 0){
-		n = p - rw->p;
 		if(n > -rw->o){
-			memmove(rw->p, rw->p-rw->o, n+rw->o);
-			rw->p = p + rw->o;
+			memmove(rw->p, rw->p - rw->o, n + rw->o);
+			rw->p += n + rw->o;
 		}
 		rw->o += n;
 	} else
-		rw->p = p;
+		rw->p += n;
+	return rw->p < rw->e;
 }
 
-/*
- *  recurse descending tree, applying the function in Routewalk
- */
 static int
 rr(Route *r, Routewalk *rw)
 {
 	int h;
 
-	if(rw->e <= rw->p)
-		return 0;
 	if(r == nil)
 		return 1;
-
 	if(rr(r->left, rw) == 0)
 		return 0;
-
 	if(r->type & Rv4)
 		h = V4H(r->v4.address);
 	else
 		h = V6H(r->v6.address);
-
-	if(h == rw->h)
-		rw->walk(r, rw);
-
+	if(h == rw->h){
+		if(rr1(rw, r) == 0)
+			return 0;
+	}
 	if(rr(r->mid, rw) == 0)
 		return 0;
-
 	return rr(r->right, rw);
 }
 
-void
-ipwalkroutes(Fs *f, Routewalk *rw)
+long
+routeread(Fs *f, char *p, ulong offset, int n)
 {
+	Routewalk rw[1];
+
+	rw->p = p;
+	rw->e = p+n;
+	rw->o = -offset;
+	if(rw->o > 0)
+		return 0;
+
 	rlock(&routelock);
-	if(rw->e > rw->p) {
+	if(rw->p < rw->e) {
 		for(rw->h = 0; rw->h < nelem(f->v4root); rw->h++)
 			if(rr(f->v4root[rw->h], rw) == 0)
 				break;
 	}
-	if(rw->e > rw->p) {
+	if(rw->p < rw->e) {
 		for(rw->h = 0; rw->h < nelem(f->v6root); rw->h++)
 			if(rr(f->v6root[rw->h], rw) == 0)
 				break;
 	}
 	runlock(&routelock);
-}
 
-long
-routeread(Fs *f, char *p, ulong offset, int n)
-{
-	Routewalk rw;
-
-	rw.p = p;
-	rw.e = p+n;
-	rw.o = -offset;
-	rw.walk = sprintroute;
-
-	ipwalkroutes(f, &rw);
-
-	return rw.p - p;
+	return rw->p - p;
 }
 
 /*
- *  this code is not in routeflush to reduce stack size
+ *	4	add	addr	mask	gate
+ *	5	add	addr	mask	gate			ifc
+ *	6	add	addr	mask	gate				src	smask
+ *	7	add	addr	mask	gate			ifc	src	smask
+ *	8	add	addr	mask	gate		tag	ifc	src	smask
+ *	9	add	addr	mask	gate	type	tag	ifc	src	smask
+ *	3	remove	addr	mask
+ *	4	remove	addr	mask	gate
+ *	5	remove	addr	mask					src	smask
+ *	6	remove	addr	mask	gate				src	smask
+ *	7	remove	addr	mask	gate			ifc	src	smask
+ *	8	remove	addr	mask	gate		tag	ifc	src	smask
+ *	9	remove	addr	mask	gate	type	tag	ifc	src	smask
  */
-void
-delroute(Fs *f, Route *r, int dolock)
+static Route
+parseroute(Fs *f, char **argv, int argc)
 {
-	uchar addr[IPaddrlen];
-	uchar mask[IPaddrlen];
+	uchar addr[IPaddrlen], mask[IPaddrlen];
+	uchar src[IPaddrlen], smask[IPaddrlen];
 	uchar gate[IPaddrlen];
-	char t[5];
-	int nifc;
+	Ipifc *ifc;
+	char *tag;
+	int type;
 
-	convroute(r, addr, mask, gate, t, &nifc);
-	if(r->type & Rv4)
-		v4delroute(f, addr+IPv4off, mask+IPv4off, dolock);
-	else
-		v6delroute(f, addr, mask, dolock);
-}
+	type = 0;
+	tag = nil;
+	ifc = nil;
+	ipmove(gate, IPnoaddr);
+	ipmove(src, IPnoaddr);
+	ipmove(smask, IPnoaddr);
 
-/*
- *  recurse until one route is deleted
- *    returns 0 if nothing is deleted, 1 otherwise
- */
-int
-routeflush(Fs *f, Route *r, char *tag)
-{
-	if(r == nil)
-		return 0;
-	if(routeflush(f, r->mid, tag))
-		return 1;
-	if(routeflush(f, r->left, tag))
-		return 1;
-	if(routeflush(f, r->right, tag))
-		return 1;
-	if((r->type & Rifc) == 0){
-		if(tag == nil || strncmp(tag, r->tag, sizeof(r->tag)) == 0){
-			delroute(f, r, 0);
-			return 1;
-		}
+	if(argc < 3)
+		error(Ebadctl);
+	if(parseip(addr, argv[1]) == -1)
+		error(Ebadip);
+	parseipmask(mask, argv[2]);
+
+	if(strcmp(argv[0], "add") == 0 || (argc > 3 && argc != 5)){
+		if(argc < 4)
+			error(Ebadctl);
+		if(parseip(gate, argv[3]) == -1)
+			error(Ebadip);
 	}
-	return 0;
+	if(argc > 4 && (strcmp(argv[0], "add") != 0 || argc != 5)){
+		if(parseip(src, argv[argc-2]) == -1)
+			error(Ebadip);
+		parseipmask(smask, argv[argc-1]);
+	}
+	if(argc == 5 && strcmp(argv[0], "add") == 0)
+		ifc = findipifcstr(f, argv[4]);
+	if(argc > 6)
+		ifc = findipifcstr(f, argv[argc-3]);
+	if(argc > 7)
+		tag = argv[argc-4];
+	if(argc > 8){
+		if((type = parseroutetype(argv[argc-5])) < 0)
+			error(Ebadctl);
+	} else {
+		if(isv4(addr))
+			type |= Rv4;
+	}
+	if(argc > 9)
+		error(Ebadctl);
+
+	if(type & Rv4){
+		if(!isv4(addr))
+			error(Ebadip);
+		if(ipcmp(smask, IPnoaddr) != 0 && !isv4(src))
+			error(Ebadip);
+		if(ipcmp(gate, IPnoaddr) != 0 && !isv4(gate))
+			error(Ebadip);
+	} else {
+		if(isv4(addr))
+			error(Ebadip);
+	}
+
+	return mkroute(addr, mask, src, smask, gate, type, ifc, tag);	
 }
 
 long
 routewrite(Fs *f, Chan *c, char *p, int n)
 {
-	int h, changed;
-	char *tag;
 	Cmdbuf *cb;
-	uchar addr[IPaddrlen];
-	uchar mask[IPaddrlen];
-	uchar gate[IPaddrlen];
-	IPaux *a, *na;
+	IPaux *a;
 
 	cb = parsecmd(p, n);
 	if(waserror()){
@@ -794,52 +958,35 @@
 	if(cb->nf < 1)
 		error("short control request");
 	if(strcmp(cb->f[0], "flush") == 0){
-		tag = cb->f[1];
+		char *tag = cb->nf < 2 ? nil : cb->f[1];
+		Route *x;
+		int h;
+
+		wlock(&routelock);
 		for(h = 0; h < nelem(f->v4root); h++)
-			for(changed = 1; changed;){
-				wlock(&routelock);
-				changed = routeflush(f, f->v4root[h], tag);
-				wunlock(&routelock);
-			}
+			while((x = looknodetag(f->v4root[h], tag)) != nil)
+				routerem(f, x);
 		for(h = 0; h < nelem(f->v6root); h++)
-			for(changed = 1; changed;){
-				wlock(&routelock);
-				changed = routeflush(f, f->v6root[h], tag);
-				wunlock(&routelock);
-			}
-	} else if(strcmp(cb->f[0], "remove") == 0){
-		if(cb->nf < 3)
-			error(Ebadarg);
-		if (parseip(addr, cb->f[1]) == -1)
-			error(Ebadip);
-		parseipmask(mask, cb->f[2]);
-		if(memcmp(addr, v4prefix, IPv4off) == 0)
-			v4delroute(f, addr+IPv4off, mask+IPv4off, 1);
-		else
-			v6delroute(f, addr, mask, 1);
-	} else if(strcmp(cb->f[0], "add") == 0){
-		if(cb->nf < 4)
-			error(Ebadarg);
-		if(parseip(addr, cb->f[1]) == -1 ||
-		    parseip(gate, cb->f[3]) == -1)
-			error(Ebadip);
-		parseipmask(mask, cb->f[2]);
-		tag = "none";
-		if(c != nil){
+			while((x = looknodetag(f->v6root[h], tag)) != nil)
+				routerem(f, x);
+		wunlock(&routelock);
+	} else if(strcmp(cb->f[0], "add") == 0 || strcmp(cb->f[0], "remove") == 0){
+		Route r = parseroute(f, cb->f, cb->nf);
+		if(*r.tag == 0){
 			a = c->aux;
-			tag = a->tag;
+			strncpy(r.tag, a->tag, sizeof(r.tag));
 		}
-		if(memcmp(addr, v4prefix, IPv4off) == 0)
-			v4addroute(f, tag, addr+IPv4off, mask+IPv4off, gate+IPv4off, 0);
+		wlock(&routelock);
+		if(strcmp(cb->f[0], "add") == 0)
+			routeadd(f, &r);
 		else
-			v6addroute(f, tag, addr, mask, gate, 0);
+			routerem(f, &r);
+		wunlock(&routelock);
 	} else if(strcmp(cb->f[0], "tag") == 0) {
 		if(cb->nf < 2)
 			error(Ebadarg);
-
 		a = c->aux;
-		na = newipaux(a->owner, cb->f[1]);
-		c->aux = na;
+		c->aux = newipaux(a->owner, cb->f[1]);
 		free(a);
 	} else
 		error(Ebadctl);
--- a/sys/src/9/ip/ipv6.c
+++ b/sys/src/9/ip/ipv6.c
@@ -38,7 +38,7 @@
 	IP *ip;
 	Ip6hdr *eh;
 	Ipifc *ifc;
-	Route *r, *sr;
+	Route *r;
 
 	ip = f->ip;
 
@@ -74,23 +74,16 @@
 		goto free;
 	}
 
-	r = v6lookup(f, eh->dst, rh);
-	if(r == nil){
+	r = v6lookup(f, eh->dst, eh->src, rh);
+	if(r == nil || (r->type & Rv4) != 0 || (ifc = r->ifc) == nil){
 		ip->stats[OutNoRoutes]++;
-		netlog(f, Logip, "no interface %I\n", eh->dst);
+		netlog(f, Logip, "no interface %I -> %I\n", eh->src, eh->dst);
 		rv = -1;
 		goto free;
 	}
 
-	ifc = r->ifc;
-	if(r->type & (Rifc|Runi))
+	if(r->type & (Rifc|Runi|Rbcast|Rmulti))
 		gate = eh->dst;
-	else if(r->type & (Rbcast|Rmulti)) {
-		gate = eh->dst;
-		sr = v6lookup(f, eh->src, nil);
-		if(sr && (sr->type & Runi))
-			ifc = sr->ifc;
-	}
 	else
 		gate = r->v6.gate;
 
@@ -226,7 +219,6 @@
 {
 	int hl, hop, tos, notforme, tentative;
 	uchar proto;
-	uchar v6dst[IPaddrlen];
 	IP *ip;
 	Ip6hdr *h;
 	Proto *p;
@@ -251,11 +243,9 @@
 	}
 
 	h = (Ip6hdr *)bp->rp;
+	notforme = ipforme(f, h->dst) == 0;
+	tentative = iptentative(f, h->dst);
 
-	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);
@@ -290,8 +280,8 @@
 			
 		/* don't forward to source's network */
 		rh.r = nil;
-		r  = v6lookup(f, h->dst, &rh);
-		if(r == nil || r->ifc == ifc){
+		r  = v6lookup(f, h->dst, h->src, &rh);
+		if(r == nil || (r->type & Rv4) != 0 || r->ifc == ifc){
 			ip->stats[OutDiscards]++;
 			freeblist(bp);
 			return;
--- a/sys/src/9/ip/rudp.c
+++ b/sys/src/9/ip/rudp.c
@@ -564,10 +564,10 @@
 			c->rport = rport;
 
 			/* reply with the same ip address (if not broadcast) */
-			if(ipforme(f, laddr) == Runi)
-				ipmove(c->laddr, laddr);
-			else
+			if(ipforme(f, laddr) != Runi)
 				ipv6local(ifc, c->laddr, c->raddr);
+			else
+				ipmove(c->laddr, laddr);
 		}
 		break;
 	}
--- a/sys/src/9/ip/tcp.c
+++ b/sys/src/9/ip/tcp.c
@@ -864,8 +864,7 @@
 	 * otherwise, we use the default MSS which assumes a
 	 * safe minimum MTU of 1280 bytes for V6.
 	 */  
-	if(r != nil){
-		ifc = r->ifc;
+	if(r != nil && (ifc = r->ifc) != nil){
 		mtu = ifc->maxtu - ifc->m->hsize;
 		if(version == V4)
 			return mtu - (TCP4_PKT + TCP4_HDRSIZE);
@@ -1314,7 +1313,7 @@
 	tcb->sndsyntime = NOW;
 
 	/* set desired mss and scale */
-	tcb->mss = tcpmtu(v6lookup(s->p->f, s->raddr, s), s->ipversion, &tcb->scale);
+	tcb->mss = tcpmtu(v6lookup(s->p->f, s->raddr, s->laddr, s), s->ipversion, &tcb->scale);
 	tpriv = s->p->priv;
 	tpriv->stats[Mss] = tcb->mss;
 }
@@ -1492,7 +1491,7 @@
 	seg.ack = lp->irs+1;
 	seg.flags = SYN|ACK;
 	seg.urg = 0;
-	seg.mss = tcpmtu(v6lookup(tcp->f, lp->raddr, nil), lp->version, &scale);
+	seg.mss = tcpmtu(v6lookup(tcp->f, lp->raddr, lp->laddr, nil), lp->version, &scale);
 	seg.wnd = QMAX;
 
 	/* if the other side set scale, we should too */
@@ -1768,7 +1767,7 @@
 	tcb->flags |= SYNACK;
 
 	/* set desired mss and scale */
-	tcb->mss = tcpmtu(v6lookup(s->p->f, src, s), version, &tcb->scale);
+	tcb->mss = tcpmtu(v6lookup(s->p->f, src, dst, s), version, &tcb->scale);
 
 	/* our sending max segment size cannot be bigger than what he asked for */
 	if(lp->mss != 0 && lp->mss < tcb->mss)