summaryrefslogtreecommitdiff
path: root/os/ip/ipifc.c
diff options
context:
space:
mode:
Diffstat (limited to 'os/ip/ipifc.c')
-rw-r--r--os/ip/ipifc.c1721
1 files changed, 1721 insertions, 0 deletions
diff --git a/os/ip/ipifc.c b/os/ip/ipifc.c
new file mode 100644
index 00000000..345c7404
--- /dev/null
+++ b/os/ip/ipifc.c
@@ -0,0 +1,1721 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+#include "ip.h"
+#include "ipv6.h"
+
+#define DPRINT if(0)print
+
+enum {
+ Maxmedia = 32,
+ Nself = Maxmedia*5,
+ NHASH = (1<<6),
+ NCACHE = 256,
+ QMAX = 64*1024-1,
+};
+
+Medium *media[Maxmedia] =
+{
+ 0
+};
+
+/*
+ * cache of local addresses (addresses we answer to)
+ */
+struct Ipself
+{
+ uchar a[IPaddrlen];
+ Ipself *hnext; /* next address in the hash table */
+ Iplink *link; /* binding twixt Ipself and Ipifc */
+ ulong expire;
+ uchar type; /* type of address */
+ int ref;
+ Ipself *next; /* free list */
+};
+
+struct Ipselftab
+{
+ QLock;
+ int inited;
+ int acceptall; /* true if an interface has the null address */
+ Ipself *hash[NHASH]; /* hash chains */
+};
+
+/*
+ * Multicast addresses are chained onto a Chan so that
+ * we can remove them when the Chan is closed.
+ */
+typedef struct Ipmcast Ipmcast;
+struct Ipmcast
+{
+ Ipmcast *next;
+ uchar ma[IPaddrlen]; /* multicast address */
+ uchar ia[IPaddrlen]; /* interface address */
+};
+
+/* quick hash for ip addresses */
+#define hashipa(a) ( ( ((a)[IPaddrlen-2]<<8) | (a)[IPaddrlen-1] )%NHASH )
+
+static char tifc[] = "ifc ";
+
+static void addselfcache(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*);
+
+/*
+ * link in a new medium
+ */
+void
+addipmedium(Medium *med)
+{
+ int i;
+
+ for(i = 0; i < nelem(media)-1; i++)
+ if(media[i] == nil){
+ media[i] = med;
+ break;
+ }
+}
+
+/*
+ * find the medium with this name
+ */
+Medium*
+ipfindmedium(char *name)
+{
+ Medium **mp;
+
+ for(mp = media; *mp != nil; mp++)
+ if(strcmp((*mp)->name, name) == 0)
+ break;
+ return *mp;
+}
+
+/*
+ * attach a device (or pkt driver) to the interface.
+ * called with c locked
+ */
+static char*
+ipifcbind(Conv *c, char **argv, int argc)
+{
+ Ipifc *ifc;
+ Medium *m;
+
+ if(argc < 2)
+ return Ebadarg;
+
+ ifc = (Ipifc*)c->ptcl;
+
+ /* bind the device to the interface */
+ m = ipfindmedium(argv[1]);
+ if(m == nil)
+ return "unknown interface type";
+
+ wlock(ifc);
+ if(ifc->m != nil){
+ wunlock(ifc);
+ return "interface already bound";
+ }
+ if(waserror()){
+ wunlock(ifc);
+ nexterror();
+ }
+
+ /* do medium specific binding */
+ (*m->bind)(ifc, argc, argv);
+
+ /* set the bound device name */
+ if(argc > 2)
+ strncpy(ifc->dev, argv[2], sizeof(ifc->dev));
+ else
+ sprint(ifc->dev, "%s%d", m->name, c->x);
+ ifc->dev[sizeof(ifc->dev)-1] = 0;
+
+ /* set up parameters */
+ ifc->m = m;
+ ifc->mintu = ifc->m->mintu;
+ ifc->maxtu = ifc->m->maxtu;
+ if(ifc->m->unbindonclose == 0)
+ ifc->conv->inuse++;
+ ifc->rp.mflag = 0; // default not managed
+ ifc->rp.oflag = 0;
+ ifc->rp.maxraint = 600000; // millisecs
+ ifc->rp.minraint = 200000;
+ ifc->rp.linkmtu = 0; // no mtu sent
+ ifc->rp.reachtime = 0;
+ ifc->rp.rxmitra = 0;
+ ifc->rp.ttl = MAXTTL;
+ ifc->rp.routerlt = 3*(ifc->rp.maxraint);
+
+ /* any ancillary structures (like routes) no longer pertain */
+ ifc->ifcid++;
+
+ /* reopen all the queues closed by a previous unbind */
+ qreopen(c->rq);
+ qreopen(c->eq);
+ qreopen(c->sq);
+
+ wunlock(ifc);
+ poperror();
+
+ return nil;
+}
+
+/*
+ * detach a device from an interface, close the interface
+ * called with ifc->conv closed
+ */
+static char*
+ipifcunbind(Ipifc *ifc)
+{
+ char *err;
+
+ if(waserror()){
+ wunlock(ifc);
+ nexterror();
+ }
+ wlock(ifc);
+
+ /* dissociate routes */
+ if(ifc->m != nil && ifc->m->unbindonclose == 0)
+ ifc->conv->inuse--;
+ ifc->ifcid++;
+
+ /* disassociate device */
+ if(ifc->m != nil && ifc->m->unbind)
+ (*ifc->m->unbind)(ifc);
+ memset(ifc->dev, 0, sizeof(ifc->dev));
+ ifc->arg = nil;
+ ifc->reassemble = 0;
+
+ /* close queues to stop queuing of packets */
+ qclose(ifc->conv->rq);
+ qclose(ifc->conv->wq);
+ qclose(ifc->conv->sq);
+
+ /* disassociate logical interfaces */
+ while(ifc->lifc){
+ err = ipifcremlifc(ifc, ifc->lifc);
+ if(err)
+ error(err);
+ }
+
+ ifc->m = nil;
+ wunlock(ifc);
+ poperror();
+ return nil;
+}
+
+
+
+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";
+
+char slineformat[] = " %-40I %-10M %-40I %-12lud %-12lud\n";
+
+
+static int
+ipifcstate(Conv *c, char *state, int n)
+{
+ Ipifc *ifc;
+ Iplifc *lifc;
+ int m;
+
+ ifc = (Ipifc*)c->ptcl;
+
+ m = snprint(state, n, sfixedformat,
+ ifc->dev, ifc->maxtu, ifc->sendra6, ifc->recvra6,
+ ifc->rp.mflag, ifc->rp.oflag, ifc->rp.maxraint,
+ ifc->rp.minraint, ifc->rp.linkmtu, ifc->rp.reachtime,
+ ifc->rp.rxmitra, ifc->rp.ttl, ifc->rp.routerlt,
+ ifc->in, ifc->out, ifc->inerr, ifc->outerr);
+
+ rlock(ifc);
+ for(lifc = ifc->lifc; lifc && n > m; lifc = lifc->next)
+ m += snprint(state+m, n - m, slineformat,
+ lifc->local, lifc->mask, lifc->remote,
+ lifc->validlt, lifc->preflt);
+ if(ifc->lifc == nil)
+ m += snprint(state+m, n - m, "\n");
+ runlock(ifc);
+ return m;
+}
+
+static int
+ipifclocal(Conv *c, char *state, int n)
+{
+ Ipifc *ifc;
+ Iplifc *lifc;
+ Iplink *link;
+ int m;
+
+ ifc = (Ipifc*)c->ptcl;
+
+ m = 0;
+
+ rlock(ifc);
+ for(lifc = ifc->lifc; lifc; lifc = lifc->next){
+ m += snprint(state+m, n - m, "%-40.40I ->", lifc->local);
+ for(link = lifc->link; link; link = link->lifclink)
+ m += snprint(state+m, n - m, " %-40.40I", link->self->a);
+ m += snprint(state+m, n - m, "\n");
+ }
+ runlock(ifc);
+ return m;
+}
+
+static int
+ipifcinuse(Conv *c)
+{
+ Ipifc *ifc;
+
+ ifc = (Ipifc*)c->ptcl;
+ return ifc->m != nil;
+}
+
+/*
+ * called when a process writes to an interface's 'data'
+ */
+static void
+ipifckick(void *x)
+{
+ Conv *c = x;
+ Block *bp;
+ Ipifc *ifc;
+
+ bp = qget(c->wq);
+ if(bp == nil)
+ return;
+
+ ifc = (Ipifc*)c->ptcl;
+ if(!canrlock(ifc)){
+ freeb(bp);
+ return;
+ }
+ if(waserror()){
+ runlock(ifc);
+ nexterror();
+ }
+ if(ifc->m == nil || ifc->m->pktin == nil)
+ freeb(bp);
+ else
+ (*ifc->m->pktin)(c->p->f, ifc, bp);
+ runlock(ifc);
+ poperror();
+}
+
+/*
+ * called when a new ipifc structure is created
+ */
+static void
+ipifccreate(Conv *c)
+{
+ Ipifc *ifc;
+
+ c->rq = qopen(QMAX, 0, 0, 0);
+ c->sq = qopen(2*QMAX, 0, 0, 0);
+ c->wq = qopen(QMAX, Qkick, ipifckick, c);
+ ifc = (Ipifc*)c->ptcl;
+ ifc->conv = c;
+ ifc->unbinding = 0;
+ ifc->m = nil;
+ ifc->reassemble = 0;
+}
+
+/*
+ * called after last close of ipifc data or ctl
+ * called with c locked, we must unlock
+ */
+static void
+ipifcclose(Conv *c)
+{
+ Ipifc *ifc;
+ Medium *m;
+
+ ifc = (Ipifc*)c->ptcl;
+ m = ifc->m;
+ if(m != nil && m->unbindonclose)
+ ipifcunbind(ifc);
+}
+
+/*
+ * change an interface's mtu
+ */
+char*
+ipifcsetmtu(Ipifc *ifc, char **argv, int argc)
+{
+ int mtu;
+
+ if(argc < 2)
+ return Ebadarg;
+ if(ifc->m == nil)
+ return Ebadarg;
+ mtu = strtoul(argv[1], 0, 0);
+ if(mtu < ifc->m->mintu || mtu > ifc->m->maxtu)
+ return Ebadarg;
+ ifc->maxtu = mtu;
+ return nil;
+}
+
+/*
+ * add an address to an interface.
+ */
+char*
+ipifcadd(Ipifc *ifc, char **argv, int argc, int tentative, Iplifc *lifcp)
+{
+ uchar ip[IPaddrlen], mask[IPaddrlen], rem[IPaddrlen];
+ uchar bcast[IPaddrlen], net[IPaddrlen];
+ Iplifc *lifc, **l;
+ int i, type, mtu;
+ Fs *f;
+ int sendnbrdisc = 0;
+
+ if(ifc->m == nil)
+ return "ipifc not yet bound to device";
+
+ f = ifc->conv->p->f;
+
+ type = Rifc;
+ memset(ip, 0, IPaddrlen);
+ memset(mask, 0, IPaddrlen);
+ memset(rem, 0, IPaddrlen);
+ switch(argc){
+ case 6:
+ if(strcmp(argv[5], "proxy") == 0)
+ type |= Rproxy;
+ /* 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:
+ parseip(ip, argv[1]);
+ parseipmask(mask, argv[2]);
+ parseip(rem, argv[3]);
+ maskip(rem, mask, net);
+ break;
+ case 3:
+ parseip(ip, argv[1]);
+ parseipmask(mask, argv[2]);
+ maskip(ip, mask, rem);
+ maskip(rem, mask, net);
+ break;
+ case 2:
+ parseip(ip, argv[1]);
+ memmove(mask, defmask(ip), IPaddrlen);
+ maskip(ip, mask, rem);
+ maskip(rem, mask, net);
+ break;
+ default:
+ return Ebadarg;
+ break;
+ }
+ if(isv4(ip))
+ tentative = 0;
+ wlock(ifc);
+
+ /* ignore if this is already a local address for this ifc */
+ for(lifc = ifc->lifc; lifc; lifc = lifc->next) {
+ if(ipcmp(lifc->local, ip) == 0) {
+ if(lifc->tentative != tentative)
+ lifc->tentative = tentative;
+ if(lifcp != nil) {
+ lifc->onlink = lifcp->onlink;
+ lifc->autoflag = lifcp->autoflag;
+ lifc->validlt = lifcp->validlt;
+ lifc->preflt = lifcp->preflt;
+ lifc->origint = lifcp->origint;
+ }
+ goto out;
+ }
+ }
+
+ /* add the address to the list of logical ifc's for this ifc */
+ lifc = smalloc(sizeof(Iplifc));
+ ipmove(lifc->local, ip);
+ ipmove(lifc->mask, mask);
+ ipmove(lifc->remote, rem);
+ ipmove(lifc->net, net);
+ lifc->tentative = tentative;
+ if(lifcp != nil) {
+ lifc->onlink = lifcp->onlink;
+ lifc->autoflag = lifcp->autoflag;
+ lifc->validlt = lifcp->validlt;
+ lifc->preflt = lifcp->preflt;
+ lifc->origint = lifcp->origint;
+ }
+ else { // default values
+ lifc->onlink = 1;
+ lifc->autoflag = 1;
+ lifc->validlt = 0xffffffff;
+ lifc->preflt = 0xffffffff;
+ lifc->origint = NOW / 10^3;
+ }
+ lifc->next = nil;
+
+ for(l = &ifc->lifc; *l; 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);
+
+ addselfcache(f, ifc, lifc, ip, Runi);
+
+ if((type & (Rproxy|Rptpt)) == (Rproxy|Rptpt)){
+ ipifcregisterproxy(f, ifc, rem);
+ goto out;
+ }
+
+ if(isv4(ip) || ipcmp(ip, IPnoaddr) == 0) {
+ /* add subnet directed broadcast address to the self cache */
+ for(i = 0; i < IPaddrlen; i++)
+ bcast[i] = (ip[i] & mask[i]) | ~mask[i];
+ addselfcache(f, ifc, lifc, bcast, Rbcast);
+
+ /* add subnet directed network address to the self cache */
+ for(i = 0; i < IPaddrlen; i++)
+ bcast[i] = (ip[i] & mask[i]) & mask[i];
+ addselfcache(f, ifc, lifc, bcast, Rbcast);
+
+ /* add network directed broadcast address to the self cache */
+ memmove(mask, defmask(ip), IPaddrlen);
+ for(i = 0; i < IPaddrlen; i++)
+ bcast[i] = (ip[i] & mask[i]) | ~mask[i];
+ addselfcache(f, ifc, lifc, bcast, Rbcast);
+
+ /* add network directed network address to the self cache */
+ memmove(mask, defmask(ip), IPaddrlen);
+ for(i = 0; i < IPaddrlen; i++)
+ bcast[i] = (ip[i] & mask[i]) & mask[i];
+ addselfcache(f, ifc, lifc, bcast, Rbcast);
+
+ addselfcache(f, ifc, lifc, IPv4bcast, Rbcast);
+ }
+ else {
+ if(ipcmp(ip, v6loopback) == 0) {
+ /* add node-local mcast address */
+ addselfcache(f, ifc, lifc, v6allnodesN, Rmulti);
+
+ /* add route for all node multicast */
+ v6addroute(f, tifc, v6allnodesN, v6allnodesNmask, v6allnodesN, Rmulti);
+ }
+
+ /* add all nodes multicast address */
+ addselfcache(f, ifc, lifc, v6allnodesL, Rmulti);
+
+ /* add route for all nodes multicast */
+ v6addroute(f, tifc, v6allnodesL, v6allnodesLmask, v6allnodesL, Rmulti);
+
+ /* 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:
+ wunlock(ifc);
+ if(tentative && sendnbrdisc)
+ icmpns(f, 0, SRC_UNSPEC, ip, TARG_MULTI, ifc->mac);
+ return nil;
+}
+
+/*
+ * remove a logical interface from an ifc
+ * always called with ifc wlock'd
+ */
+static char*
+ipifcremlifc(Ipifc *ifc, Iplifc *lifc)
+{
+ Iplifc **l;
+ Fs *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)
+ return "address not on this interface";
+ *l = lifc->next;
+
+ /* disassociate any addresses */
+ while(lifc->link)
+ 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);
+ 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);
+ }
+
+ free(lifc);
+ return nil;
+
+}
+
+/*
+ * remove an address from an interface.
+ * called with c locked
+ */
+char*
+ipifcrem(Ipifc *ifc, char **argv, int argc)
+{
+ uchar ip[IPaddrlen];
+ uchar mask[IPaddrlen];
+ uchar rem[IPaddrlen];
+ Iplifc *lifc;
+ char *rv;
+
+ if(argc < 3)
+ return Ebadarg;
+
+ parseip(ip, argv[1]);
+ parseipmask(mask, argv[2]);
+ if(argc < 4)
+ maskip(ip, mask, rem);
+ else
+ parseip(rem, argv[3]);
+
+ wlock(ifc);
+
+ /*
+ * 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(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)
+ break;
+ }
+
+ rv = ipifcremlifc(ifc, lifc);
+ 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 == nil)
+ continue;
+ if(m->addroute != nil)
+ 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 == nil)
+ continue;
+ if(m->remroute != nil)
+ m->remroute(ifc, vers, addr, mask);
+ }
+ }
+}
+
+/*
+ * associate an address with the interface. This wipes out any previous
+ * addresses. This is a macro that means, remove all the old interfaces
+ * and add a new one.
+ */
+static char*
+ipifcconnect(Conv* c, char **argv, int argc)
+{
+ char *err;
+ Ipifc *ifc;
+
+ ifc = (Ipifc*)c->ptcl;
+
+ 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);
+ }
+ wunlock(ifc);
+ poperror();
+
+ err = ipifcadd(ifc, argv, argc, 0, nil);
+ if(err)
+ return err;
+
+ Fsconnected(c, nil);
+
+ return nil;
+}
+
+char*
+ipifcsetpar6(Ipifc *ifc, char **argv, int argc)
+{
+ int i, argsleft, vmax = ifc->rp.maxraint, vmin = ifc->rp.minraint;
+
+ argsleft = argc - 1;
+ i = 1;
+
+ if(argsleft % 2 != 0)
+ return Ebadarg;
+
+ while (argsleft > 1) {
+ if(strcmp(argv[i],"recvra")==0)
+ ifc->recvra6 = (atoi(argv[i+1]) != 0);
+ else if(strcmp(argv[i],"sendra")==0)
+ ifc->sendra6 = (atoi(argv[i+1]) != 0);
+ else if(strcmp(argv[i],"mflag")==0)
+ ifc->rp.mflag = (atoi(argv[i+1]) != 0);
+ else if(strcmp(argv[i],"oflag")==0)
+ ifc->rp.oflag = (atoi(argv[i+1]) != 0);
+ else if(strcmp(argv[i],"maxraint")==0)
+ ifc->rp.maxraint = atoi(argv[i+1]);
+ else if(strcmp(argv[i],"minraint")==0)
+ ifc->rp.minraint = atoi(argv[i+1]);
+ else if(strcmp(argv[i],"linkmtu")==0)
+ ifc->rp.linkmtu = atoi(argv[i+1]);
+ else if(strcmp(argv[i],"reachtime")==0)
+ ifc->rp.reachtime = atoi(argv[i+1]);
+ else if(strcmp(argv[i],"rxmitra")==0)
+ ifc->rp.rxmitra = atoi(argv[i+1]);
+ else if(strcmp(argv[i],"ttl")==0)
+ ifc->rp.ttl = atoi(argv[i+1]);
+ else if(strcmp(argv[i],"routerlt")==0)
+ ifc->rp.routerlt = atoi(argv[i+1]);
+ else
+ return Ebadarg;
+
+ argsleft -= 2;
+ i += 2;
+ }
+
+ // consistency check
+ if(ifc->rp.maxraint < ifc->rp.minraint) {
+ ifc->rp.maxraint = vmax;
+ ifc->rp.minraint = vmin;
+ return Ebadarg;
+ }
+
+ return nil;
+}
+
+char*
+ipifcsendra6(Ipifc *ifc, char **argv, int argc)
+{
+ int i;
+
+ i = 0;
+ if(argc > 1)
+ i = atoi(argv[1]);
+ ifc->sendra6 = (i!=0);
+ return nil;
+}
+
+char*
+ipifcrecvra6(Ipifc *ifc, char **argv, int argc)
+{
+ int i;
+
+ i = 0;
+ if(argc > 1)
+ i = atoi(argv[1]);
+ ifc->recvra6 = (i!=0);
+ return nil;
+}
+
+/*
+ * non-standard control messages.
+ * called with c locked.
+ */
+static char*
+ipifcctl(Conv* c, char**argv, int argc)
+{
+ Ipifc *ifc;
+ int i;
+
+ ifc = (Ipifc*)c->ptcl;
+ if(strcmp(argv[0], "add") == 0)
+ return ipifcadd(ifc, argv, argc, 0, nil);
+ else if(strcmp(argv[0], "bootp") == 0)
+ return bootp(ifc);
+ else if(strcmp(argv[0], "try") == 0)
+ return ipifcadd(ifc, argv, argc, 1, nil);
+ else if(strcmp(argv[0], "remove") == 0)
+ return ipifcrem(ifc, argv, argc);
+ 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;
+ return nil;
+ }
+ else if(strcmp(argv[0], "iprouting") == 0){
+ i = 1;
+ if(argc > 1)
+ i = atoi(argv[1]);
+ iprouting(c->p->f, i);
+ return nil;
+ }
+ else if(strcmp(argv[0], "addpref6") == 0)
+ return ipifcaddpref6(ifc, argv, argc);
+ else if(strcmp(argv[0], "setpar6") == 0)
+ return ipifcsetpar6(ifc, argv, argc);
+ else if(strcmp(argv[0], "sendra6") == 0)
+ return ipifcsendra6(ifc, argv, argc);
+ else if(strcmp(argv[0], "recvra6") == 0)
+ return ipifcrecvra6(ifc, argv, argc);
+ return "unsupported ctl";
+}
+
+ipifcstats(Proto *ipifc, char *buf, int len)
+{
+ return ipstats(ipifc->f, buf, len);
+}
+
+void
+ipifcinit(Fs *f)
+{
+ Proto *ipifc;
+
+ ipifc = smalloc(sizeof(Proto));
+ ipifc->name = "ipifc";
+ ipifc->connect = ipifcconnect;
+ ipifc->announce = nil;
+ ipifc->bind = ipifcbind;
+ ipifc->state = ipifcstate;
+ ipifc->create = ipifccreate;
+ ipifc->close = ipifcclose;
+ ipifc->rcv = nil;
+ ipifc->ctl = ipifcctl;
+ ipifc->advise = nil;
+ ipifc->stats = ipifcstats;
+ ipifc->inuse = ipifcinuse;
+ ipifc->local = ipifclocal;
+ ipifc->ipproto = -1;
+ ipifc->nc = Maxmedia;
+ ipifc->ptclsize = sizeof(Ipifc);
+
+ f->ipifc = ipifc; /* hack for ipifcremroute, findipifc, ... */
+ f->self = smalloc(sizeof(Ipselftab)); /* hack for ipforme */
+
+ Fsproto(f, ipifc);
+}
+
+/*
+ * add to self routing cache
+ * called with c locked
+ */
+static void
+addselfcache(Fs *f, Ipifc *ifc, Iplifc *lifc, uchar *a, int type)
+{
+ Ipself *p;
+ Iplink *lp;
+ int h;
+
+ qlock(f->self);
+
+ /* 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)
+ break;
+
+ /* allocate a local address and add to hash chain */
+ if(p == nil){
+ p = smalloc(sizeof(*p));
+ ipmove(p->a, a);
+ p->type = type;
+ p->next = f->self->hash[h];
+ f->self->hash[h] = p;
+
+ /* if the null address, accept all packets */
+ if(ipcmp(a, v4prefix) == 0 || ipcmp(a, IPnoaddr) == 0)
+ f->self->acceptall = 1;
+ }
+
+ /* look for a link for this lifc */
+ for(lp = p->link; lp; lp = lp->selflink)
+ if(lp->lifc == lifc)
+ break;
+
+ /* allocate a lifc-to-local link and link to both */
+ if(lp == nil){
+ lp = smalloc(sizeof(*lp));
+ lp->ref = 1;
+ lp->lifc = lifc;
+ lp->self = p;
+ lp->selflink = p->link;
+ p->link = lp;
+ lp->lifclink = lifc->link;
+ 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);
+
+ if((type & Rmulti) && ifc->m->addmulti != nil)
+ (*ifc->m->addmulti)(ifc, a, lifc->local);
+ } else {
+ lp->ref++;
+ }
+
+ qunlock(f->self);
+}
+
+/*
+ * These structures are unlinked from their chains while
+ * other threads may be using them. To avoid excessive locking,
+ * just put them aside for a while before freeing them.
+ * called with f->self locked
+ */
+static Iplink *freeiplink;
+static Ipself *freeipself;
+
+static void
+iplinkfree(Iplink *p)
+{
+ Iplink **l, *np;
+ ulong now = NOW;
+
+ l = &freeiplink;
+ for(np = *l; np; np = *l){
+ if(np->expire > now){
+ *l = np->next;
+ free(np);
+ continue;
+ }
+ l = &np->next;
+ }
+ p->expire = now + 5000; /* give other threads 5 secs to get out */
+ p->next = nil;
+ *l = p;
+}
+static void
+ipselffree(Ipself *p)
+{
+ Ipself **l, *np;
+ ulong now = NOW;
+
+ l = &freeipself;
+ for(np = *l; np; np = *l){
+ if(np->expire > now){
+ *l = np->next;
+ free(np);
+ continue;
+ }
+ l = &np->next;
+ }
+ p->expire = now + 5000; /* give other threads 5 secs to get out */
+ p->next = nil;
+ *l = p;
+}
+
+/*
+ * Decrement reference for this address on this link.
+ * Unlink from selftab if this is the last ref.
+ * called with c locked
+ */
+static void
+remselfcache(Fs *f, Ipifc *ifc, Iplifc *lifc, uchar *a)
+{
+ Ipself *p, **l;
+ Iplink *link, **l_self, **l_lifc;
+
+ qlock(f->self);
+
+ /* find the unique selftab entry */
+ l = &f->self->hash[hashipa(a)];
+ for(p = *l; p; p = *l){
+ if(ipcmp(p->a, a) == 0)
+ break;
+ l = &p->next;
+ }
+
+ if(p == nil)
+ goto out;
+
+ /*
+ * walk down links from an ifc looking for one
+ * that matches the selftab entry
+ */
+ l_lifc = &lifc->link;
+ for(link = *l_lifc; link; link = *l_lifc){
+ if(link->self == p)
+ break;
+ l_lifc = &link->lifclink;
+ }
+
+ if(link == nil)
+ goto out;
+
+ /*
+ * walk down the links from the selftab looking for
+ * the one we just found
+ */
+ l_self = &p->link;
+ for(link = *l_self; link; link = *l_self){
+ if(link == *(l_lifc))
+ break;
+ l_self = &link->selflink;
+ }
+
+ if(link == nil)
+ panic("remselfcache");
+
+ if(--(link->ref) != 0)
+ goto out;
+
+ if((p->type & Rmulti) && ifc->m->remmulti != nil)
+ (*ifc->m->remmulti)(ifc, a, lifc->local);
+
+ /* ref == 0, remove from both chains and free the link */
+ *l_lifc = link->lifclink;
+ *l_self = link->selflink;
+ iplinkfree(link);
+
+ 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);
+
+ /* 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)
+{
+ int i, m, nifc, off;
+ Ipself *p;
+ Iplink *link;
+ char state[8];
+
+ m = 0;
+ off = offset;
+ qlock(f->self);
+ 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)
+ nifc++;
+ routetype(p->type, state);
+ m += snprint(cp + m, n - m, stformat, p->a, nifc, state);
+ if(off > 0){
+ off -= m;
+ m = 0;
+ }
+ }
+ }
+ qunlock(f->self);
+ 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
+ */
+int
+ipforme(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->type;
+ }
+
+ /* hack to say accept anything */
+ if(f->self->acceptall)
+ return Runi;
+
+ return 0;
+}
+
+/*
+ * find the ifc on same net as the remote system. If none,
+ * return nil.
+ */
+Ipifc*
+findipifc(Fs *f, uchar *remote, int type)
+{
+ Ipifc *ifc, *x;
+ Iplifc *lifc;
+ Conv **cp, **e;
+ uchar gnet[IPaddrlen];
+ uchar xmask[IPaddrlen];
+
+ x = nil; memset(xmask, 0, IPaddrlen);
+
+ /* find most specific match */
+ e = &f->ipifc->conv[f->ipifc->nc];
+ for(cp = f->ipifc->conv; cp < e; cp++){
+ if(*cp == 0)
+ continue;
+
+ ifc = (Ipifc*)(*cp)->ptcl;
+
+ for(lifc = ifc->lifc; lifc; lifc = lifc->next){
+ maskip(remote, lifc->mask, gnet);
+ if(ipcmp(gnet, lifc->net) == 0){
+ if(x == nil || ipcmp(lifc->mask, xmask) > 0){
+ x = ifc;
+ ipmove(xmask, lifc->mask);
+ }
+ }
+ }
+ }
+ 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;
+}
+
+enum {
+ unknownv6,
+ multicastv6,
+ unspecifiedv6,
+ linklocalv6,
+ sitelocalv6,
+ globalv6,
+};
+
+int
+v6addrtype(uchar *addr)
+{
+ if(isv6global(addr))
+ return globalv6;
+ if(islinklocal(addr))
+ return linklocalv6;
+ if(isv6mcast(addr))
+ return multicastv6;
+ if(issitelocal(addr))
+ return sitelocalv6;
+ return unknownv6;
+}
+
+#define v6addrcurr(lifc) (( (lifc)->origint + (lifc)->preflt >= (NOW/10^3) ) || ( (lifc)->preflt == 0xffffffff ))
+
+void
+findprimaryip6(Fs *f, uchar *local)
+{
+ Conv **cp, **e;
+ Ipifc *ifc;
+ Iplifc *lifc;
+ int atype, atypel;
+
+ ipmove(local, v6Unspecified);
+ atype = unspecifiedv6;
+
+ /* find "best" (global > sitelocal > link local > unspecified)
+ * local address; address must be current */
+
+ e = &f->ipifc->conv[f->ipifc->nc];
+ for(cp = f->ipifc->conv; cp < e; cp++){
+ if(*cp == 0)
+ continue;
+ ifc = (Ipifc*)(*cp)->ptcl;
+ for(lifc = ifc->lifc; lifc; lifc = lifc->next){
+ atypel = v6addrtype(lifc->local);
+ if(atypel > atype)
+ if(v6addrcurr(lifc)) {
+ ipmove(local, lifc->local);
+ atype = atypel;
+ if(atype == globalv6)
+ return;
+ }
+ }
+ }
+}
+
+/*
+ * returns first ip address configured
+ */
+void
+findprimaryip(Fs *f, uchar *local)
+{
+ Conv **cp, **e;
+ Ipifc *ifc;
+ Iplifc *lifc;
+
+ /* find first ifc local address */
+ e = &f->ipifc->conv[f->ipifc->nc];
+ for(cp = f->ipifc->conv; cp < e; cp++){
+ if(*cp == 0)
+ continue;
+ ifc = (Ipifc*)(*cp)->ptcl;
+ for(lifc = ifc->lifc; lifc; lifc = lifc->next){
+ ipmove(local, lifc->local);
+ return;
+ }
+ }
+}
+
+/*
+ * find the local address 'closest' to the remote system, copy it to
+ * local and return the ifc for that address
+ */
+void
+findlocalip(Fs *f, uchar *local, uchar *remote)
+{
+ Ipifc *ifc;
+ Iplifc *lifc;
+ Route *r;
+ uchar gate[IPaddrlen];
+ uchar gnet[IPaddrlen];
+ int version;
+ int atype = unspecifiedv6, atypel = unknownv6;
+
+ USED(atype);
+ USED(atypel);
+ qlock(f->ipifc);
+ r = v6lookup(f, remote, 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);
+ }
+
+ /* find ifc address closest to the gateway to use */
+ switch(version) {
+ case V4:
+ 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:
+ for(lifc = ifc->lifc; lifc; lifc = lifc->next){
+ atypel = v6addrtype(lifc->local);
+ maskip(gate, lifc->mask, gnet);
+ if(ipcmp(gnet, lifc->net) == 0)
+ if(atypel > atype)
+ if(v6addrcurr(lifc)) {
+ ipmove(local, lifc->local);
+ atype = atypel;
+ if(atype == globalv6)
+ break;
+ }
+ }
+ if(atype > unspecifiedv6)
+ goto out;
+ break;
+ default:
+ panic("findlocalip: version %d", version);
+ }
+ }
+
+ switch(version){
+ case V4:
+ findprimaryip(f, local);
+ break;
+ case V6:
+ findprimaryip6(f, local);
+ break;
+ default:
+ panic("findlocalip2: version %d", version);
+ }
+
+out:
+ qunlock(f->ipifc);
+}
+
+/*
+ * return first v4 address associated with an interface
+ */
+int
+ipv4local(Ipifc *ifc, uchar *addr)
+{
+ Iplifc *lifc;
+
+ for(lifc = ifc->lifc; lifc; lifc = lifc->next){
+ if(isv4(lifc->local)){
+ memmove(addr, lifc->local+IPv4off, IPv4addrlen);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * return first v6 address associated with an interface
+ */
+int
+ipv6local(Ipifc *ifc, uchar *addr)
+{
+ Iplifc *lifc;
+
+ for(lifc = ifc->lifc; lifc; lifc = lifc->next){
+ if(!isv4(lifc->local) && !(lifc->tentative)){
+ ipmove(addr, lifc->local);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int
+ipv6anylocal(Ipifc *ifc, uchar *addr)
+{
+ Iplifc *lifc;
+
+ for(lifc = ifc->lifc; lifc; lifc = lifc->next){
+ if(!isv4(lifc->local)){
+ ipmove(addr, lifc->local);
+ return SRC_UNI;
+ }
+ }
+ return SRC_UNSPEC;
+}
+
+/*
+ * see if this address is bound to the interface
+ */
+Iplifc*
+iplocalonifc(Ipifc *ifc, uchar *ip)
+{
+ Iplifc *lifc;
+
+ for(lifc = ifc->lifc; lifc; lifc = lifc->next)
+ if(ipcmp(ip, lifc->local) == 0)
+ return lifc;
+ return nil;
+}
+
+
+/*
+ * See if we're proxying for this address on this interface
+ */
+int
+ipproxyifc(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);
+ if(r == nil)
+ return 0;
+ if((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 multicast version if any
+ */
+int
+ipismulticast(uchar *ip)
+{
+ if(isv4(ip)){
+ if(ip[IPv4off] >= 0xe0 && ip[IPv4off] < 0xf0)
+ return V4;
+ } else {
+ if(ip[0] == 0xff)
+ return V6;
+ }
+ return 0;
+}
+
+int
+ipisbm(uchar *ip)
+{
+ if(isv4(ip)){
+ if(ip[IPv4off] >= 0xe0 && ip[IPv4off] < 0xf0)
+ return V4;
+ 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 locked
+ */
+void
+ipifcaddmulti(Conv *c, uchar *ma, uchar *ia)
+{
+ Ipifc *ifc;
+ Iplifc *lifc;
+ Conv **p;
+ Ipmulti *multi, **l;
+ Fs *f;
+
+ f = c->p->f;
+
+ for(l = &c->multi; *l; l = &(*l)->next)
+ if(ipcmp(ma, (*l)->ma) == 0)
+ if(ipcmp(ia, (*l)->ia) == 0)
+ return; /* it's already there */
+
+ multi = *l = smalloc(sizeof(*multi));
+ ipmove(multi->ma, ma);
+ ipmove(multi->ia, ia);
+ multi->next = nil;
+
+ for(p = f->ipifc->conv; *p; p++){
+ if((*p)->inuse == 0)
+ continue;
+ ifc = (Ipifc*)(*p)->ptcl;
+ if(waserror()){
+ wunlock(ifc);
+ nexterror();
+ }
+ wlock(ifc);
+ for(lifc = ifc->lifc; lifc; lifc = lifc->next)
+ if(ipcmp(ia, lifc->local) == 0)
+ addselfcache(f, ifc, lifc, ma, Rmulti);
+ wunlock(ifc);
+ poperror();
+ }
+}
+
+
+/*
+ * remove a multicast address from an interface, called with c locked
+ */
+void
+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)
+ if(ipcmp(ma, (*l)->ma) == 0)
+ if(ipcmp(ia, (*l)->ia) == 0)
+ break;
+
+ multi = *l;
+ if(multi == nil)
+ return; /* we don't have it open */
+
+ *l = multi->next;
+
+ for(p = f->ipifc->conv; *p; p++){
+ if((*p)->inuse == 0)
+ continue;
+
+ ifc = (Ipifc*)(*p)->ptcl;
+ if(waserror()){
+ wunlock(ifc);
+ nexterror();
+ }
+ wlock(ifc);
+ for(lifc = ifc->lifc; lifc; lifc = lifc->next)
+ if(ipcmp(ia, lifc->local) == 0)
+ remselfcache(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)
+{
+ Conv **cp, **e;
+ Ipifc *nifc;
+ Iplifc *lifc;
+ Medium *m;
+ uchar net[IPaddrlen];
+
+ /* register the address on any network that will proxy for us */
+ e = &f->ipifc->conv[f->ipifc->nc];
+
+ if(!isv4(ip)) { // V6
+ for(cp = f->ipifc->conv; cp < e; cp++){
+ if(*cp == nil)
+ continue;
+ nifc = (Ipifc*)(*cp)->ptcl;
+ if(nifc == ifc)
+ continue;
+
+ rlock(nifc);
+ m = nifc->m;
+ if(m == nil || m->addmulti == nil) {
+ runlock(nifc);
+ continue;
+ }
+ for(lifc = nifc->lifc; lifc; lifc = lifc->next){
+ maskip(ip, lifc->mask, net);
+ if(ipcmp(net, lifc->remote) == 0) { /* add solicited-node multicast address */
+ ipv62smcast(net, ip);
+ addselfcache(f, nifc, lifc, net, Rmulti);
+ arpenter(f, V6, ip, nifc->mac, 6, 0);
+ //(*m->addmulti)(nifc, net, ip);
+ break;
+ }
+ }
+ runlock(nifc);
+ }
+ return;
+ }
+ else { // V4
+ for(cp = f->ipifc->conv; cp < e; cp++){
+ if(*cp == nil)
+ continue;
+ nifc = (Ipifc*)(*cp)->ptcl;
+ if(nifc == ifc)
+ continue;
+
+ rlock(nifc);
+ m = nifc->m;
+ if(m == nil || m->areg == nil){
+ runlock(nifc);
+ continue;
+ }
+ for(lifc = nifc->lifc; lifc; lifc = lifc->next){
+ maskip(ip, lifc->mask, net);
+ if(ipcmp(net, lifc->remote) == 0){
+ (*m->areg)(nifc, ip);
+ break;
+ }
+ }
+ runlock(nifc);
+ }
+ }
+}
+
+
+// added for new v6 mesg types
+static void
+adddefroute6(Fs *f, uchar *gate, int force)
+{
+ Route *r;
+
+ r = v6lookup(f, v6Unspecified, nil);
+ if(r!=nil)
+ if(!(force) && (strcmp(r->tag,"ra")!=0)) // route entries generated
+ return; // by all other means take
+ // precedence over router annc
+
+ v6delroute(f, v6Unspecified, v6Unspecified, 1);
+ v6addroute(f, "ra", v6Unspecified, v6Unspecified, gate, 0);
+}
+
+enum
+{
+ Ngates = 3,
+};
+
+char*
+ipifcaddpref6(Ipifc *ifc, char**argv, int argc)
+{
+ uchar onlink = 1;
+ uchar autoflag = 1;
+ long validlt = 0xffffffff;
+ long preflt = 0xffffffff;
+ long origint = NOW / 10^3;
+ uchar prefix[IPaddrlen];
+ int plen = 64;
+ Iplifc *lifc;
+ char addr[40], preflen[6];
+ char *params[3];
+
+ switch(argc) {
+ case 7:
+ preflt = atoi(argv[6]);
+ /* fall through */
+ case 6:
+ validlt = atoi(argv[5]);
+ /* fall through */
+ case 5:
+ autoflag = atoi(argv[4]);
+ /* fall through */
+ case 4:
+ onlink = atoi(argv[3]);
+ /* fall through */
+ case 3:
+ plen = atoi(argv[2]);
+ case 2:
+ break;
+ default:
+ return Ebadarg;
+ }
+
+ if((parseip(prefix, argv[1])!=6) ||
+ (validlt < preflt) ||
+ (plen < 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;
+
+ if(ifc->m->pref2addr!=nil)
+ ifc->m->pref2addr(prefix, ifc->mac);
+ else
+ return Ebadarg;
+
+ sprint(addr, "%I", prefix);
+ sprint(preflen, "/%d", plen);
+ params[0] = "add";
+ params[1] = addr;
+ params[2] = preflen;
+
+ return ipifcadd(ifc, params, 3, 0, lifc);
+}
+