]> git.lizzy.rs Git - plan9front.git/blobdiff - sys/src/9/ip/ipifc.c
devip: fix use after free in ipifcremmulti()
[plan9front.git] / sys / src / 9 / ip / ipifc.c
index ffb1ad225d1d52414278ed288c1641b13483ac65..29b82e61a003cad8a87164ad2c172512c5bae356 100644 (file)
@@ -61,10 +61,46 @@ static char tifc[] = "ifc ";
 
 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    ipifcregisteraddr(Fs*, Ipifc*, uchar *, uchar *);
+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 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
@@ -174,33 +210,26 @@ ipifcunbind(Ipifc *ifc)
 {
        char *err;
 
+       wlock(ifc);
        if(waserror()){
                wunlock(ifc);
                nexterror();
        }
-       wlock(ifc);
-
-       /* dissociate routes */
-       if(ifc->m != nil && ifc->m->unbindonclose == 0)
-               ifc->conv->inuse--;
-       ifc->ifcid++;
 
        /* disassociate logical interfaces (before zeroing ifc->arg) */
-       while(ifc->lifc){
-               err = ipifcremlifc(ifc, ifc->lifc);
-               /*
-                * note: err non-zero means lifc not found,
-                * which can't happen in this case.
-                */
-               if(err)
+       while(ifc->lifc != nil){
+               err = ipifcremlifc(ifc, &ifc->lifc);
+               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;
+
+       ifc->reflect = 0;
        ifc->reassemble = 0;
 
        /* close queues to stop queuing of packets */
@@ -208,7 +237,12 @@ ipifcunbind(Ipifc *ifc)
        qclose(ifc->conv->wq);
        qclose(ifc->conv->sq);
 
+       /* dissociate routes */
+       ifc->ifcid++;
+       if(ifc->m != nil && ifc->m->unbindonclose == 0)
+               ifc->conv->inuse--;
        ifc->m = nil;
+
        wunlock(ifc);
        poperror();
        return nil;
@@ -216,7 +250,7 @@ ipifcunbind(Ipifc *ifc)
 
 char sfixedformat[] = "device %s maxtu %d sendra %d recvra %d mflag %d oflag"
 " %d maxraint %d minraint %d linkmtu %d reachtime %d rxmitra %d ttl %d routerlt"
-" %d pktin %lud pktout %lud errin %lud errout %lud\n";
+" %d pktin %lud pktout %lud errin %lud errout %lud speed %d delay %d\n";
 
 char slineformat[] = " %-40I %-10M %-40I %-12lud %-12lud\n";
 
@@ -233,10 +267,11 @@ ipifcstate(Conv *c, char *state, int n)
                ifc->rp.mflag, ifc->rp.oflag, ifc->rp.maxraint,
                ifc->rp.minraint, ifc->rp.linkmtu, ifc->rp.reachtime,
                ifc->rp.rxmitra, ifc->rp.ttl, ifc->rp.routerlt,
-               ifc->in, ifc->out, ifc->inerr, ifc->outerr);
+               ifc->in, ifc->out, ifc->inerr, ifc->outerr,
+               ifc->speed, ifc->delay);
 
        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)
@@ -254,12 +289,11 @@ ipifclocal(Conv *c, char *state, int n)
        int m;
 
        ifc = (Ipifc*)c->ptcl;
-       m = 0;
-
        rlock(ifc);
-       for(lifc = ifc->lifc; lifc; lifc = lifc->next){
+       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; 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");
        }
@@ -276,6 +310,50 @@ ipifcinuse(Conv *c)
        return ifc->m != nil;
 }
 
+static void
+ipifcsetdelay(Ipifc *ifc, int delay)
+{
+       if(delay < 0)
+               delay = 0;
+       else if(delay > 1000)
+               delay = 1000;
+       ifc->delay = delay;
+       ifc->burst = ((vlong)delay * ifc->speed) / 8000;
+       if(ifc->burst < ifc->maxtu)
+               ifc->burst = ifc->maxtu;
+}
+
+static void
+ipifcsetspeed(Ipifc *ifc, int speed)
+{
+       if(speed < 0)
+               speed = 0;
+       ifc->speed = speed;
+       ifc->load = 0;
+       ipifcsetdelay(ifc, ifc->delay);
+}
+
+void
+ipifcoput(Ipifc *ifc, Block *bp, int version, uchar *ip)
+{
+       if(ifc->speed){
+               ulong now = MACHP(0)->ticks;
+               int dt = TK2MS(now - ifc->ticks);
+               ifc->ticks = now;
+               ifc->load -= ((vlong)dt * ifc->speed) / 8000;
+               if(ifc->load < 0 || dt < 0 || dt > 1000)
+                       ifc->load = 0;
+               else if(ifc->load > ifc->burst){
+                       freeblist(bp);
+                       return;
+               }
+       }
+       bp = concatblock(bp);
+       ifc->load += BLEN(bp);
+       ifc->m->bwrite(ifc, bp, version, ip);
+}
+
+
 /*
  *  called when a process writes to an interface's 'data'
  */
@@ -299,10 +377,10 @@ ipifckick(void *x)
                runlock(ifc);
                nexterror();
        }
-       if(ifc->m == nil || ifc->m->pktin == nil)
-               freeb(bp);
-       else
+       if(ifc->m != nil && ifc->m->pktin != nil)
                (*ifc->m->pktin)(c->p->f, ifc, bp);
+       else
+               freeb(bp);
        runlock(ifc);
        poperror();
 }
@@ -322,9 +400,11 @@ ipifccreate(Conv *c)
                error(Enomem);
        ifc = (Ipifc*)c->ptcl;
        ifc->conv = c;
-       ifc->unbinding = 0;
        ifc->m = nil;
+       ifc->reflect = 0;
        ifc->reassemble = 0;
+       ipifcsetspeed(ifc, 0);
+       ipifcsetdelay(ifc, 40);
 }
 
 /*
@@ -335,11 +415,9 @@ static void
 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,17 +444,13 @@ ipifcsetmtu(Ipifc *ifc, char **argv, int argc)
 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;
        Fs *f;
 
-       if(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);
@@ -388,8 +462,6 @@ ipifcadd(Ipifc *ifc, char **argv, int argc, int tentative, Iplifc *lifcp)
                /* fall through */
        case 5:
                mtu = strtoul(argv[4], 0, 0);
-               if(mtu >= ifc->m->mintu && mtu <= ifc->m->maxtu)
-                       ifc->maxtu = mtu;
                /* fall through */
        case 4:
                if (parseip(ip, argv[1]) == -1 || parseip(rem, argv[3]) == -1)
@@ -414,24 +486,46 @@ ipifcadd(Ipifc *ifc, char **argv, int argc, int tentative, Iplifc *lifcp)
        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(ifc->m == nil)
+               return "ipifc not yet bound to device";
+
+       f = ifc->conv->p->f;
+       if(waserror()){
+               wunlock(ifc);
+               return up->errstr;
+       }
+
+       if(mtu > 0 && mtu >= ifc->m->mintu && mtu <= ifc->m->maxtu)
+               ifc->maxtu = mtu;
 
        /* 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 */
@@ -440,8 +534,9 @@ ipifcadd(Ipifc *ifc, char **argv, int argc, int tentative, Iplifc *lifcp)
        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;
@@ -454,29 +549,25 @@ ipifcadd(Ipifc *ifc, char **argv, int argc, int tentative, Iplifc *lifcp)
        }
        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;
-
-       /* add local routes */
-       if(isv4(ip))
-               v4addroute(f, tifc, rem+IPv4off, mask+IPv4off, rem+IPv4off, type);
-       else
-               v6addroute(f, tifc, rem, mask, rem, type);
+       /* add route for this logical interface */
+       addroute(f, rem, mask, ip, IPallbits, rem, type, ifc, tifc);
+       if(v6addrtype(ip) != linklocalv6)
+               addroute(f, rem, mask, ip, IPnoaddr, rem, type, ifc, tifc);
 
        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];
@@ -500,39 +591,36 @@ ipifcadd(Ipifc *ifc, char **argv, int argc, int tentative, Iplifc *lifcp)
                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 */
                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 != nil)
-               (*ifc->m->areg)(ifc, ip);
-
-out:
+done:
        wunlock(ifc);
-       if(tentative && sendnbrdisc)
-               icmpns(f, 0, SRC_UNSPEC, ip, TARG_MULTI, ifc->mac);
+       poperror();
+
+       ipifcregisteraddr(f, ifc, ip, ip);
+
        return nil;
 }
 
@@ -541,41 +629,48 @@ out:
  *  always called with ifc wlock'd
  */
 static char*
-ipifcremlifc(Ipifc *ifc, Iplifc *lifc)
+ipifcremlifc(Ipifc *ifc, Iplifc **l)
 {
-       Iplifc **l;
-       Fs *f;
-
-       f = ifc->conv->p->f;
+       Iplifc *lifc = *l;
+       Fs *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, IPallbits,
+               lifc->remote, lifc->type, ifc, tifc);
+       if(v6addrtype(lifc->local) != linklocalv6)
+               remroute(f, lifc->remote, lifc->mask,
+                       lifc->local, IPnoaddr,
+                       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;
 }
@@ -589,79 +684,37 @@ ipifcrem(Ipifc *ifc, char **argv, int argc)
 {
        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;
-
-       wlock(ifc);
+       else if(parseip(rem, argv[3]) == -1)
+               return Ebadip;
 
        /*
         *  find address on this interface and remove from chain.
         *  for pt to pt we actually specify the remote address as the
         *  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)
-                               m->remroute(ifc, vers, addr, mask);
-               }
-       }
-}
-
 /*
  *  associate an address with the interface.  This wipes out any previous
  *  addresses.  This is a macro that means, remove all the old interfaces
@@ -678,21 +731,18 @@ ipifcconnect(Conv* c, char **argv, int argc)
        if(ifc->m == nil)
                 return "ipifc not yet bound to device";
 
-       if(waserror()){
-               wunlock(ifc);
-               nexterror();
-       }
        wlock(ifc);
-       while(ifc->lifc){
-               err = ipifcremlifc(ifc, ifc->lifc);
-               if(err)
-                       error(err);
+       while(ifc->lifc != nil){
+               err = ipifcremlifc(ifc, &ifc->lifc);
+               if(err != nil){
+                       wunlock(ifc);
+                       return err;
+               }
        }
        wunlock(ifc);
-       poperror();
 
        err = ipifcadd(ifc, argv, argc, 0, nil);
-       if(err)
+       if(err != nil)
                return err;
 
        Fsconnected(c, nil);
@@ -754,10 +804,9 @@ ipifcra6(Ipifc *ifc, char **argv, int argc)
  *  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;
 
        ifc = (Ipifc*)c->ptcl;
        if(strcmp(argv[0], "add") == 0)
@@ -768,21 +817,26 @@ ipifcctl(Conv* c, char**argv, int argc)
                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){
-               ifc->reassemble = 1;
+       else if(strcmp(argv[0], "speed") == 0){
+               ipifcsetspeed(ifc, argc>1? atoi(argv[1]): 0);
+               return nil;
+       }
+       else if(strcmp(argv[0], "delay") == 0){
+               ipifcsetdelay(ifc, argc>1? atoi(argv[1]): 0);
                return nil;
        }
        else if(strcmp(argv[0], "iprouting") == 0){
-               i = 1;
-               if(argc > 1)
-                       i = atoi(argv[1]);
-               iprouting(c->p->f, i);
+               iprouting(c->p->f, argc>1? atoi(argv[1]): 1);
+               return nil;
+       }
+       else if(strcmp(argv[0], "reflect") == 0){
+               ifc->reflect = argc>1? atoi(argv[1]): 1;
+               return nil;
+       }
+       else if(strcmp(argv[0], "reassemble") == 0){
+               ifc->reassemble = argc>1? atoi(argv[1]): 1;
                return nil;
        }
        else if(strcmp(argv[0], "add6") == 0)
@@ -834,16 +888,22 @@ ipifcinit(Fs *f)
 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);
+               nexterror();
+       }
 
        /* 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 */
@@ -860,7 +920,7 @@ addselfcache(Fs *f, Ipifc *ifc, Iplifc *lifc, uchar *a, int type)
        }
 
        /* 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;
 
@@ -876,11 +936,11 @@ addselfcache(Fs *f, Ipifc *ifc, Iplifc *lifc, uchar *a, int type)
                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, 
+                       ((type & (Rbcast|Rmulti)) != 0 || v6addrtype(a) == linklocalv6) ?
+                               IPallbits : IPnoaddr,
+                       a, type, ifc, tifc);
 
                if((type & Rmulti) && ifc->m->addmulti != nil)
                        (*ifc->m->addmulti)(ifc, a, lifc->local);
@@ -888,6 +948,7 @@ addselfcache(Fs *f, Ipifc *ifc, Iplifc *lifc, uchar *a, int type)
                lp->ref++;
 
        qunlock(f->self);
+       poperror();
 }
 
 /*
@@ -906,8 +967,8 @@ iplinkfree(Iplink *p)
        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;
@@ -926,8 +987,8 @@ ipselffree(Ipself *p)
        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;
@@ -954,7 +1015,7 @@ remselfcache(Fs *f, Ipifc *ifc, Iplifc *lifc, uchar *a)
 
        /* 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;
@@ -968,7 +1029,7 @@ remselfcache(Fs *f, Ipifc *ifc, Iplifc *lifc, uchar *a)
         *  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;
@@ -982,7 +1043,7 @@ remselfcache(Fs *f, Ipifc *ifc, Iplifc *lifc, uchar *a)
         *  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;
@@ -994,8 +1055,19 @@ remselfcache(Fs *f, Ipifc *ifc, Iplifc *lifc, uchar *a)
        if(--(link->ref) != 0)
                goto out;
 
-       if((p->type & Rmulti) && ifc->m->remmulti != nil)
-               (*ifc->m->remmulti)(ifc, a, lifc->local);
+       /* remove from routing table */
+       remroute(f, a, IPallbits,
+               lifc->local, 
+               ((p->type & (Rbcast|Rmulti)) != 0 || v6addrtype(a) == linklocalv6) ?
+                       IPallbits : IPnoaddr,
+               a, p->type, ifc, tifc);
+
+       if((p->type & Rmulti) && ifc->m->remmulti != nil){
+               if(!waserror()){
+                       (*ifc->m->remmulti)(ifc, a, lifc->local);
+                       poperror();
+               }
+       }
 
        /* ref == 0, remove from both chains and free the link */
        *l_lifc = link->lifclink;
@@ -1005,30 +1077,18 @@ remselfcache(Fs *f, Ipifc *ifc, Iplifc *lifc, uchar *a)
        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)
 {
@@ -1043,10 +1103,11 @@ ipselftabread(Fs *f, char *cp, ulong offset, int n)
        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;
@@ -1057,40 +1118,26 @@ ipselftabread(Fs *f, char *cp, ulong offset, int n)
        return m;
 }
 
-int
-iptentative(Fs *f, uchar *addr)
-{
-       Ipself *p;
-
-       p = f->self->hash[hashipa(addr)];
-       for(; p; p = p->next){
-               if(ipcmp(addr, p->a) == 0)
-                       return p->link->lifc->tentative;
-       }
-       return 0;
-}
-
 /*
  *  returns
  *     0               - no match
  *     Runi
  *     Rbcast
- *     Rmcast
+ *     Rmulti
  */
 int
 ipforme(Fs *f, uchar *addr)
 {
        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;
 }
 
@@ -1099,260 +1146,254 @@ ipforme(Fs *f, uchar *addr)
  *  return nil.
  */
 Ipifc*
-findipifc(Fs *f, uchar *remote, int type)
+findipifc(Fs *f, uchar *local, uchar *remote, int type)
 {
+       uchar gnet[IPaddrlen];
+       int spec, xspec;
        Ipifc *ifc, *x;
        Iplifc *lifc;
-       Conv **cp, **e;
-       uchar gnet[IPaddrlen], xmask[IPaddrlen];
+       Conv **cp;
 
        x = nil;
-       memset(xmask, 0, IPaddrlen);
-
-       /* find most specific match */
-       e = &f->ipifc->conv[f->ipifc->nc];
-       for(cp = f->ipifc->conv; cp < e; cp++){
-               if(*cp == 0)
-                       continue;
+       xspec = 0;
+       for(cp = f->ipifc->conv; *cp != nil; cp++){
                ifc = (Ipifc*)(*cp)->ptcl;
-               for(lifc = ifc->lifc; lifc; lifc = lifc->next){
+               rlock(ifc);
+               for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
+                       if(type & Runi){
+                               if(ipcmp(remote, lifc->local) == 0){
+                               Found:
+                                       runlock(ifc);
+                                       return ifc;
+                               }
+                       } else if(type & (Rbcast|Rmulti)) {
+                               if(ipcmp(local, lifc->local) == 0)
+                                       goto Found;
+                       }
                        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;
                                }
                        }
                }
+               runlock(ifc);
        }
-       if(x != nil)
-               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;
-               }
-       }
-       return nil;
+       return x;
 }
 
-enum {
-       unknownv6,              /* UGH */
-//     multicastv6,
-       unspecifiedv6,
-       linklocalv6,
-       globalv6,
-};
-
-int
-v6addrtype(uchar *addr)
+Ipifc*
+findipifcstr(Fs *f, char *s)
 {
-       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;
+       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 && ipifcinuse(c))
+                       return (Ipifc*)c->ptcl;
+       }
+       if(parseip(ip, s) != -1)
+               return findipifc(f, ip, ip, Runi);
+       return nil;
 }
 
-#define v6addrcurr(lifc) ((lifc)->preflt == ~0L || \
-                       (lifc)->origint + (lifc)->preflt >= NOW/1000)
-
+/*
+ *  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 == 0)
-                       continue;
+       for(cp = f->ipifc->conv; *cp != nil; cp++){
                ifc = (Ipifc*)(*cp)->ptcl;
-               for(lifc = ifc->lifc; lifc; lifc = lifc->next){
+               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);
        }
 }
 
 /*
- *  returns first ip address configured
+ *  returns first v4 address configured
  */
 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 == 0)
-                       continue;
+       for(cp = f->ipifc->conv; *cp != nil; cp++){
                ifc = (Ipifc*)(*cp)->ptcl;
-               if((lifc = ifc->lifc) != nil){
-                       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);
 }
 
 /*
- *  find the local address 'closest' to the remote system, copy it to
- *  local and return the ifc for that address
+ *  return v4 address associated with an interface close to remote
  */
-void
-findlocalip(Fs *f, uchar *local, uchar *remote)
+int
+ipv4local(Ipifc *ifc, uchar *local, uchar *remote)
 {
-       int version, atype = unspecifiedv6, atypel = unknownv6;
-       int atyper, deprecated;
-       uchar gate[IPaddrlen], gnet[IPaddrlen];
-       Ipifc *ifc;
        Iplifc *lifc;
-       Route *r;
-
-       USED(atype);
-       USED(atypel);
-       qlock(f->ipifc);
-       r = v6lookup(f, remote, nil);
-       version = (memcmp(remote, v4prefix, IPv4off) == 0)? V4: V6;
-
-       if(r != nil){
-               ifc = r->ifc;
-               if(r->type & Rv4)
-                       v4tov6(gate, r->v4.gate);
-               else {
-                       ipmove(gate, r->v6.gate);
-                       ipmove(local, v6Unspecified);
-               }
+       int a, b;
 
-               switch(version) {
-               case V4:
-                       /* find ifc address closest to the gateway to use */
-                       for(lifc = ifc->lifc; lifc; lifc = lifc->next){
-                               maskip(gate, lifc->mask, gnet);
-                               if(ipcmp(gnet, lifc->net) == 0){
-                                       ipmove(local, lifc->local);
-                                       goto out;
-                               }
-                       }
-                       break;
-               case V6:
-                       /* find ifc address with scope matching the destination */
-                       atyper = v6addrtype(remote);
-                       deprecated = 0;
-                       for(lifc = ifc->lifc; lifc; lifc = lifc->next){
-                               atypel = v6addrtype(lifc->local);
-                               /* prefer appropriate scope */
-                               if(atypel > atype && atype < atyper ||
-                                  atypel < atype && atype > atyper){
-                                       ipmove(local, lifc->local);
-                                       deprecated = !v6addrcurr(lifc);
-                                       atype = atypel;
-                               } else if(atypel == atype){
-                                       /* avoid deprecated addresses */
-                                       if(deprecated && v6addrcurr(lifc)){
-                                               ipmove(local, lifc->local);
-                                               atype = atypel;
-                                               deprecated = 0;
-                                       }
-                               }
-                               if(atype == atyper && !deprecated)
-                                       goto out;
-                       }
-                       if(atype >= atyper)
-                               goto out;
-                       break;
-               default:
-                       panic("findlocalip: version %d", version);
+       b = -1;
+       for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
+               if((lifc->type & Rv4) == 0)
+                       continue;
+               a = comprefixlen(lifc->local+IPv4off, remote, IPv4addrlen);
+               if(a > b){
+                       b = a;
+                       memmove(local, lifc->local+IPv4off, IPv4addrlen);
                }
        }
-
-       switch(version){
-       case V4:
-               findprimaryipv4(f, local);
-               break;
-       case V6:
-               findprimaryipv6(f, local);
-               break;
-       default:
-               panic("findlocalip2: version %d", version);
-       }
-
-out:
-       qunlock(f->ipifc);
+       return b >= 0;
 }
 
 /*
- *  return first v4 address associated with an interface
+ *  return v6 address associated with an interface close to remote
  */
 int
-ipv4local(Ipifc *ifc, uchar *addr)
+ipv6local(Ipifc *ifc, uchar *local, uchar *remote)
 {
+       struct {
+               int     atype;
+               int     deprecated;
+               int     comprefixlen;
+       } a, b;
+       int atype;
        Iplifc *lifc;
 
-       for(lifc = ifc->lifc; lifc; lifc = lifc->next){
-               if(isv4(lifc->local)){
-                       memmove(addr, lifc->local+IPv4off, IPv4addrlen);
-                       return 1;
-               }
+       if(isv4(remote)){
+               ipmove(local, v4prefix);
+               return ipv4local(ifc, local+IPv4off, remote+IPv4off);
        }
-       return 0;
-}
 
-/*
- *  return first v6 address associated with an interface
- */
-int
-ipv6local(Ipifc *ifc, uchar *addr)
-{
-       Iplifc *lifc;
+       atype = v6addrtype(remote);
+       ipmove(local, v6Unspecified);
+       b.atype = unknownv6;
+       b.deprecated = 1;
+       b.comprefixlen = 0;
 
-       for(lifc = ifc->lifc; lifc; lifc = lifc->next){
-               if(!isv4(lifc->local) && !(lifc->tentative)){
-                       ipmove(addr, lifc->local);
-                       return 1;
+       for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
+               if(lifc->tentative)
+                       continue;
+
+               a.atype = v6addrtype(lifc->local);
+               a.deprecated = !v6addrcurr(lifc);
+               a.comprefixlen = comprefixlen(lifc->local, remote, IPaddrlen);
+
+               /* prefer appropriate scope */
+               if(a.atype != b.atype){
+                       if(a.atype > b.atype && b.atype < atype ||
+                          a.atype < b.atype && b.atype > atype)
+                               goto Good;
+                       continue;
                }
+               /* prefer non-deprecated addresses */
+               if(a.deprecated != b.deprecated){
+                       if(b.deprecated)
+                               goto Good;
+                       continue;
+               }
+               /* prefer longer common prefix */
+               if(a.comprefixlen != b.comprefixlen){
+                       if(a.comprefixlen > b.comprefixlen)
+                               goto Good;
+                       continue;
+               }
+               continue;
+       Good:
+               b = a;
+               ipmove(local, lifc->local);
        }
-       return 0;
+
+       return b.atype >= atype;
 }
 
-int
-ipv6anylocal(Ipifc *ifc, uchar *addr)
+void
+findlocalip(Fs *f, uchar *local, uchar *remote)
 {
+       Route *r;
        Iplifc *lifc;
+       Ipifc *ifc, *nifc;
+       Conv **cp;
+
+       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;
 
-       for(lifc = ifc->lifc; lifc; lifc = lifc->next){
-               if(!isv4(lifc->local)){
-                       ipmove(addr, lifc->local);
-                       return SRC_UNI;
+                       r = v6lookup(f, remote, lifc->local, nil);
+                       if(r == nil || (nifc = r->ifc) == nil)
+                               continue;
+                       if(r->type & Runi){
+                               ipmove(local, remote);
+                               runlock(ifc);
+                               return;
+                       }
+                       if(nifc != ifc) rlock(nifc);
+                       if((r->type & (Rifc|Rbcast|Rmulti|Rv4)) == Rv4){
+                               ipmove(local, v4prefix);
+                               if(ipv4local(nifc, local+IPv4off, r->v4.gate)){
+                                       if(nifc != ifc) runlock(nifc);
+                                       runlock(ifc);
+                                       return;
+                               }
+                       }
+                       if(ipv6local(nifc, local, remote)){
+                               if(nifc != ifc) runlock(nifc);
+                               runlock(ifc);
+                               return;
+                       }
+                       if(nifc != ifc) runlock(nifc);
                }
+               runlock(ifc);
        }
-       return SRC_UNSPEC;
+       if(isv4(remote))
+               findprimaryipv4(f, local);
+       else
+               findprimaryipv6(f, local);
 }
 
+
 /*
  *  see if this address is bound to the interface
  */
@@ -1361,9 +1402,24 @@ iplocalonifc(Ipifc *ifc, uchar *ip)
 {
        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;
 }
 
@@ -1375,21 +1431,13 @@ int
 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;
 }
 
 /*
@@ -1406,20 +1454,6 @@ ipismulticast(uchar *ip)
                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
@@ -1427,15 +1461,12 @@ ipisbm(uchar *ip)
 void
 ipifcaddmulti(Conv *c, uchar *ma, uchar *ia)
 {
-       Ipifc *ifc;
-       Iplifc *lifc;
-       Conv **p;
        Ipmulti *multi, **l;
+       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 */
 
@@ -1444,18 +1475,15 @@ ipifcaddmulti(Conv *c, uchar *ma, uchar *ia)
        ipmove(multi->ia, ia);
        multi->next = nil;
 
-       for(p = f->ipifc->conv; *p; p++){
-               if((*p)->inuse == 0)
-                       continue;
-               ifc = (Ipifc*)(*p)->ptcl;
+       f = c->p->f;
+       if((ifc = findipifc(f, ia, ma, Rmulti)) != nil){
+               wlock(ifc);
                if(waserror()){
                        wunlock(ifc);
                        nexterror();
                }
-               wlock(ifc);
-               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();
        }
@@ -1470,13 +1498,10 @@ ipifcremmulti(Conv *c, uchar *ma, uchar *ia)
 {
        Ipmulti *multi, **l;
        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;
 
@@ -1485,148 +1510,106 @@ ipifcremmulti(Conv *c, uchar *ma, uchar *ia)
                return;         /* we don't have it open */
 
        *l = multi->next;
+       multi->next = nil;
 
-       for(p = f->ipifc->conv; *p; p++){
-               if((*p)->inuse == 0)
-                       continue;
-
-               ifc = (Ipifc*)(*p)->ptcl;
-               if(waserror()){
-                       wunlock(ifc);
-                       nexterror();
-               }
+       f = c->p->f;
+       if((ifc = findipifc(f, ia, ma, Rmulti)) != nil){
                wlock(ifc);
-               for(lifc = ifc->lifc; lifc; lifc = lifc->next)
-                       if(ipcmp(ia, lifc->local) == 0)
+               if(!waserror()){
+                       if((lifc = iplocalonifc(ifc, ia)) != nil)
                                remselfcache(f, ifc, lifc, ma);
+                       poperror();
+               }
                wunlock(ifc);
-               poperror();
        }
-
        free(multi);
 }
 
-/*
- *  make lifc's join and leave multicast groups
- */
-static char*
-ipifcjoinmulti(Ipifc *ifc, char **argv, int argc)
+/* register the address on this network for address resolution */
+static void
+ipifcregisteraddr(Fs *f, Ipifc *ifc, uchar *ia, uchar *ip)
 {
-       USED(ifc, argv, argc);
-       return nil;
-}
+       Iplifc *lifc;
 
-static char*
-ipifcleavemulti(Ipifc *ifc, char **argv, int argc)
-{
-       USED(ifc, argv, argc);
-       return nil;
+       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)
+ipifcregisterproxy(Fs *f, Ipifc *ifc, uchar *ip, int add)
 {
-       Conv **cp, **e;
-       Ipifc *nifc;
+       uchar a[IPaddrlen];
        Iplifc *lifc;
-       Medium *m;
-       uchar net[IPaddrlen];
+       Ipifc *nifc;
+       Conv **cp;
 
-       /* register the address on any network that will proxy for us */
-       e = &f->ipifc->conv[f->ipifc->nc];
+       /* register the address on any interface that will proxy for the ip */
+       for(cp = f->ipifc->conv; *cp != nil; cp++){
+               nifc = (Ipifc*)(*cp)->ptcl;
+               if(nifc == 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;
-                       }
-                       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);
-                                       // (*m->addmulti)(nifc, net, ip);
-                                       break;
-                               }
-                       }
-                       runlock(nifc);
+               wlock(nifc);
+               if(nifc->m == nil
+               || (lifc = ipremoteonifc(nifc, ip)) == nil
+               || (lifc->type & Rptpt) != 0
+               || 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;
-                       }
-                       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);
+               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);
                }
-       }
-}
-
-
-/* added for new v6 mesg types */
-static void
-adddefroute6(Fs *f, uchar *gate, int force)
-{
-       Route *r;
-
-       r = v6lookup(f, v6Unspecified, nil);
-       /*
-        * route entries generated by all other means take precedence
-        * over router announcements.
-        */
-       if (r && !force && strcmp(r->tag, "ra") != 0)
-               return;
+               ipmove(a, lifc->local);
+               wunlock(nifc);
+               poperror();
 
-       v6delroute(f, v6Unspecified, v6Unspecified, 1);
-       v6addroute(f, "ra", v6Unspecified, v6Unspecified, gate, 0);
+               if(add)
+                       ipifcregisteraddr(f, nifc, a, ip);
+       }
 }
 
-enum {
-       Ngates = 3,
-};
-
 char*
 ipifcadd6(Ipifc *ifc, char**argv, int argc)
 {
        int plen = 64;
-       long origint = NOW / 1000, preflt = ~0L, validlt = ~0L;
        char addr[40], preflen[6];
        char *params[3];
-       uchar autoflag = 1, onlink = 1;
        uchar prefix[IPaddrlen];
-       Iplifc *lifc;
+       Iplifc lifc;
+
+       lifc.onlink = 1;
+       lifc.autoflag = 1;
+       lifc.validlt = ~0L;
+       lifc.preflt = ~0L;
+       lifc.origint = NOW / 1000;
 
        switch(argc) {
        case 7:
-               preflt = atoi(argv[6]);
+               lifc.preflt = atoi(argv[6]);
                /* fall through */
        case 6:
-               validlt = atoi(argv[5]);
+               lifc.validlt = atoi(argv[5]);
                /* fall through */
        case 5:
-               autoflag = atoi(argv[4]);
+               lifc.autoflag = atoi(argv[4]) != 0;
                /* fall through */
        case 4:
-               onlink = atoi(argv[3]);
+               lifc.onlink = atoi(argv[3]) != 0;
                /* fall through */
        case 3:
                plen = atoi(argv[2]);
@@ -1637,26 +1620,20 @@ ipifcadd6(Ipifc *ifc, char**argv, int argc)
                return Ebadarg;
        }
 
-       if (parseip(prefix, argv[1]) != 6 || validlt < preflt || plen < 0 ||
+       if (parseip(prefix, argv[1]) != 6 || lifc.validlt < lifc.preflt || plen < 0 ||
            plen > 64 || islinklocal(prefix))
                return Ebadarg;
 
-       lifc = smalloc(sizeof(Iplifc));
-       lifc->onlink = (onlink != 0);
-       lifc->autoflag = (autoflag != 0);
-       lifc->validlt = validlt;
-       lifc->preflt = preflt;
-       lifc->origint = origint;
-
        /* issue "add" ctl msg for v6 link-local addr and prefix len */
-       if(!ifc->m->pref2addr)
+       if(ifc->m->pref2addr == nil)
                return Ebadarg;
-       ifc->m->pref2addr(prefix, ifc->mac);    /* mac → v6 link-local addr */
+       (*ifc->m->pref2addr)(prefix, ifc->mac); /* mac → v6 link-local addr */
+
        sprint(addr, "%I", prefix);
        sprint(preflen, "/%d", plen);
        params[0] = "add";
        params[1] = addr;
        params[2] = preflen;
 
-       return ipifcadd(ifc, params, 3, 0, lifc);
+       return ipifcadd(ifc, params, 3, 0, &lifc);
 }