blob: cbf133ea46e2529d6b3415e4e8b96ae51ba33856 [file] [log] [blame]
/* Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
* Portions Copyright © 1997-1999 Vita Nuova Limited
* Portions Copyright © 2000-2007 Vita Nuova Holdings Limited
* (www.vitanuova.com)
* Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
*
* Modified for the Akaros operating system:
* Copyright (c) 2013-2014 The Regents of the University of California
* Copyright (c) 2013-2015 Google Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. */
#include <slab.h>
#include <kmalloc.h>
#include <kref.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <error.h>
#include <cpio.h>
#include <pmap.h>
#include <smp.h>
#include <net/ip.h>
#define DPRINT if(0)print
enum {
Maxmedia = 32,
Nself = Maxmedia * 5,
NHASH = (1 << 6),
NCACHE = 256,
QMAX = 64 * 1024 - 1,
};
struct medium *media[Maxmedia] = {
0
};
/*
* cache of local addresses (addresses we answer to)
*/
struct Ipself {
uint8_t a[IPaddrlen];
struct Ipself *hnext; /* next address in the hash table */
struct Iplink *link; /* binding twixt Ipself and Ipifc */
uint32_t expire;
uint8_t type; /* type of address */
int ref;
struct Ipself *next; /* free list */
};
struct Ipselftab {
qlock_t qlock;
int inited;
int acceptall; /* true if an interface has the null address */
struct 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;
uint8_t ma[IPaddrlen]; /* multicast address */
uint8_t 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(struct Fs *f, struct Ipifc *ifc, struct Iplifc *lifc,
uint8_t *a, int type);
static void remselfcache(struct Fs *f, struct Ipifc *ifc, struct Iplifc *lifc,
uint8_t *a);
static void ipifcjoinmulti(struct Ipifc *ifc, char **argv, int argc);
static void ipifcleavemulti(struct Ipifc *ifc, char **argv, int argc);
static void ipifcregisterproxy(struct Fs *, struct Ipifc *,
uint8_t *unused_uint8_p_t);
static void ipifcremlifc(struct Ipifc *, struct Iplifc *);
static void ipifcaddpref6(struct Ipifc *ifc, char **argv, int argc);
/*
* link in a new medium
*/
void addipmedium(struct medium *med)
{
int i;
for (i = 0; i < ARRAY_SIZE(media) - 1; i++)
if (media[i] == NULL) {
media[i] = med;
break;
}
}
/*
* find the medium with this name
*/
struct medium *ipfindmedium(char *name)
{
struct medium **mp;
for (mp = media; *mp != NULL; mp++)
if (strcmp((*mp)->name, name) == 0)
break;
return *mp;
}
/*
* attach a device (or pkt driver) to the interface.
* called with c locked
*/
static void ipifcbind(struct conv *c, char **argv, int argc)
{
ERRSTACK(1);
struct Ipifc *ifc;
struct medium *m;
if (argc < 2)
error(EINVAL, "Too few args (%d) to %s", argc, __func__);
ifc = (struct Ipifc *)c->ptcl;
/* bind the device to the interface */
m = ipfindmedium(argv[1]);
if (m == NULL)
error(EFAIL, "unknown interface type");
wlock(&ifc->rwlock);
if (ifc->m != NULL) {
wunlock(&ifc->rwlock);
error(EFAIL, "interfacr already bound");
}
if (waserror()) {
wunlock(&ifc->rwlock);
nexterror();
}
/* do medium specific binding */
(*m->bind) (ifc, argc, argv);
/* set the bound device name */
if (argc > 2)
strlcpy(ifc->dev, argv[2], sizeof(ifc->dev));
else
snprintf(ifc->dev, sizeof(ifc->dev), "%s%d", m->name, c->x);
/* 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->rwlock);
poperror();
}
/*
* detach a device from an interface, close the interface
* called with ifc->conv closed
*/
static void ipifcunbind(struct Ipifc *ifc)
{
ERRSTACK(1);
char *err;
wlock(&ifc->rwlock);
if (waserror()) {
wunlock(&ifc->rwlock);
nexterror();
}
/* dissociate routes */
if (ifc->m != NULL && ifc->m->unbindonclose == 0)
ifc->conv->inuse--;
ifc->ifcid++;
/* disassociate device */
if (ifc->m != NULL && ifc->m->unbind)
(*ifc->m->unbind) (ifc);
memset(ifc->dev, 0, sizeof(ifc->dev));
ifc->arg = NULL;
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)
ipifcremlifc(ifc, ifc->lifc);
ifc->m = NULL;
wunlock(&ifc->rwlock);
poperror();
}
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 %lu pktout %lu errin %lu errout %lu tracedrop %lu\n";
char slineformat[] = " %-40I %-10M %-40I %-12lu %-12lu\n";
static int ipifcstate(struct conv *c, char *state, int n)
{
struct Ipifc *ifc;
struct Iplifc *lifc;
int m;
ifc = (struct Ipifc *)c->ptcl;
m = snprintf(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, ifc->tracedrop);
rlock(&ifc->rwlock);
for (lifc = ifc->lifc; lifc && n > m; lifc = lifc->next)
m += snprintf(state + m, n - m, slineformat, lifc->local,
lifc->mask, lifc->remote, lifc->validlt,
lifc->preflt);
if (ifc->lifc == NULL)
m += snprintf(state + m, n - m, "\n");
runlock(&ifc->rwlock);
return m;
}
static int ipifclocal(struct conv *c, char *state, int n)
{
struct Ipifc *ifc;
struct Iplifc *lifc;
struct Iplink *link;
int m;
ifc = (struct Ipifc *)c->ptcl;
m = 0;
rlock(&ifc->rwlock);
for (lifc = ifc->lifc; lifc; lifc = lifc->next) {
m += snprintf(state + m, n - m, "%-40.40I ->", lifc->local);
for (link = lifc->link; link; link = link->lifclink)
m += snprintf(state + m, n - m, " %-40.40I",
link->self->a);
m += snprintf(state + m, n - m, "\n");
}
runlock(&ifc->rwlock);
return m;
}
static int ipifcinuse(struct conv *c)
{
struct Ipifc *ifc;
ifc = (struct Ipifc *)c->ptcl;
return ifc->m != NULL;
}
/*
* called when a process writes to an interface's 'data'
*/
static void ipifckick(void *x)
{
ERRSTACK(1);
struct conv *c = x;
struct block *bp;
struct Ipifc *ifc;
bp = qget(c->wq);
if (bp == NULL)
return;
ifc = (struct Ipifc *)c->ptcl;
if (!canrlock(&ifc->rwlock)) {
freeb(bp);
return;
}
if (waserror()) {
runlock(&ifc->rwlock);
nexterror();
}
if (ifc->m == NULL || ifc->m->pktin == NULL)
freeb(bp);
else
(*ifc->m->pktin) (c->p->f, ifc, bp);
runlock(&ifc->rwlock);
poperror();
}
/*
* called when a new ipifc structure is created
*/
static void ipifccreate(struct conv *c)
{
struct Ipifc *ifc;
c->rq = qopen(QMAX, 0, 0, 0);
c->sq = qopen(2 * QMAX, Qmsg | Qcoalesce, 0, 0);
c->wq = qopen(QMAX, Qkick, ipifckick, c);
ifc = (struct Ipifc *)c->ptcl;
ifc->conv = c;
ifc->unbinding = 0;
ifc->m = NULL;
ifc->reassemble = 0;
rwinit(&ifc->rwlock);
/* These are never used, but we might need them if we ever do "unbind on
* the fly" (see ip.h). Not sure where the code went that used these
* vars. */
spinlock_init(&ifc->idlock);
rendez_init(&ifc->wait);
}
/*
* called after last close of ipifc data or ctl
* called with c locked, we must unlock
*/
static void ipifcclose(struct conv *c)
{
struct Ipifc *ifc;
struct medium *m;
ifc = (struct Ipifc *)c->ptcl;
m = ifc->m;
if (m != NULL && m->unbindonclose)
ipifcunbind(ifc);
}
/*
* change an interface's mtu
*/
static void ipifcsetmtu(struct Ipifc *ifc, char **argv, int argc)
{
int mtu;
if (argc < 2)
error(EINVAL, "Too few args (%d) to %s", argc, __func__);
if (ifc->m == NULL)
error(EFAIL, "No medium on IFC");
mtu = strtoul(argv[1], 0, 0);
if (mtu < ifc->m->mintu || mtu > ifc->m->maxtu)
error(EFAIL, "Bad MTU size %d (%d, %d)", mtu, ifc->m->mintu,
ifc->m->maxtu);
ifc->maxtu = mtu;
}
/*
* add an address to an interface.
*/
static void ipifcadd(struct Ipifc *ifc, char **argv, int argc, int tentative,
struct Iplifc *lifcp)
{
ERRSTACK(1);
uint8_t ip[IPaddrlen], mask[IPaddrlen], rem[IPaddrlen];
uint8_t bcast[IPaddrlen], net[IPaddrlen];
struct Iplifc *lifc, **l;
int i, type, mtu;
struct Fs *f;
int sendnbrdisc = 0;
if (ifc->m == NULL)
error(EFAIL, "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:
error(EINVAL, "Bad arg num to %s", __func__);
}
if (isv4(ip))
tentative = 0;
wlock(&ifc->rwlock);
if (waserror()) {
warn("Unexpected error thrown: %s", current_errstr());
wunlock(&ifc->rwlock);
nexterror();
}
/* 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 != NULL) {
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 = kzmalloc(sizeof(struct Iplifc), 0);
ipmove(lifc->local, ip);
ipmove(lifc->mask, mask);
ipmove(lifc->remote, rem);
ipmove(lifc->net, net);
lifc->tentative = tentative;
if (lifcp != NULL) {
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 = UINT64_MAX;
lifc->preflt = UINT64_MAX;
lifc->origint = NOW / 10 ^ 3;
}
lifc->next = NULL;
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 != NULL)
(*ifc->m->areg) (ifc, ip);
out:
wunlock(&ifc->rwlock);
if (tentative && sendnbrdisc)
icmpns(f, 0, SRC_UNSPEC, ip, TARG_MULTI, ifc->mac);
poperror();
}
/*
* remove a logical interface from an ifc
* always called with ifc wlock'd
*/
static void ipifcremlifc(struct Ipifc *ifc, struct Iplifc *lifc)
{
struct Iplifc **l;
struct 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 != NULL && *l != lifc; l = &(*l)->next) ;
if (*l == NULL)
error(EFAIL, "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);
}
kfree(lifc);
}
/*
* remove an address from an interface.
* called with c locked
*/
static void ipifcrem(struct Ipifc *ifc, char **argv, int argc)
{
ERRSTACK(1);
uint8_t ip[IPaddrlen];
uint8_t mask[IPaddrlen];
uint8_t rem[IPaddrlen];
struct Iplifc *lifc;
if (argc < 3)
error(EINVAL, "Too few args (%d) to %s", argc, __func__);
parseip(ip, argv[1]);
parseipmask(mask, argv[2]);
if (argc < 4)
maskip(ip, mask, rem);
else
parseip(rem, argv[3]);
wlock(&ifc->rwlock);
if (waserror()) {
wunlock(&ifc->rwlock);
nexterror();
}
/*
* 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 != NULL; lifc = lifc->next) {
if (memcmp(ip, lifc->local, IPaddrlen) == 0
&& memcmp(mask, lifc->mask, IPaddrlen) == 0
&& memcmp(rem, lifc->remote, IPaddrlen) == 0)
break;
}
ipifcremlifc(ifc, lifc);
poperror();
wunlock(&ifc->rwlock);
}
/*
* distribute routes to active interfaces like the
* TRIP linecards
*/
void ipifcaddroute(struct Fs *f, int vers, uint8_t *addr, uint8_t *mask,
uint8_t *gate, int type)
{
struct medium *m;
struct conv **cp, **e;
struct Ipifc *ifc;
e = &f->ipifc->conv[f->ipifc->nc];
for (cp = f->ipifc->conv; cp < e; cp++) {
if (*cp != NULL) {
ifc = (struct Ipifc *)(*cp)->ptcl;
m = ifc->m;
if (m == NULL)
continue;
if (m->addroute != NULL)
m->addroute(ifc, vers, addr, mask, gate, type);
}
}
}
void ipifcremroute(struct Fs *f, int vers, uint8_t * addr, uint8_t * mask)
{
struct medium *m;
struct conv **cp, **e;
struct Ipifc *ifc;
e = &f->ipifc->conv[f->ipifc->nc];
for (cp = f->ipifc->conv; cp < e; cp++) {
if (*cp != NULL) {
ifc = (struct Ipifc *)(*cp)->ptcl;
m = ifc->m;
if (m == NULL)
continue;
if (m->remroute != NULL)
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 void ipifcconnect(struct conv *c, char **argv, int argc)
{
ERRSTACK(1);
char *err;
struct Ipifc *ifc;
ifc = (struct Ipifc *)c->ptcl;
if (ifc->m == NULL)
error(EFAIL, "ipifc not yet bound to device");
wlock(&ifc->rwlock);
if (waserror()) {
wunlock(&ifc->rwlock);
nexterror();
}
while (ifc->lifc)
ipifcremlifc(ifc, ifc->lifc);
wunlock(&ifc->rwlock);
poperror();
ipifcadd(ifc, argv, argc, 0, NULL);
Fsconnected(c, NULL);
}
static void ipifcsetpar6(struct 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)
error(EINVAL, "Non-even number of args (%d) to %s", argc,
__func__);
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
error(EINVAL, "unknown command to %s", __func__);
argsleft -= 2;
i += 2;
}
// consistency check
if (ifc->rp.maxraint < ifc->rp.minraint) {
ifc->rp.maxraint = vmax;
ifc->rp.minraint = vmin;
error(EINVAL, "inconsistent ifc->rp 'raint'");
}
}
static void ipifcsendra6(struct Ipifc *ifc, char **argv, int argc)
{
int i;
i = 0;
if (argc > 1)
i = atoi(argv[1]);
ifc->sendra6 = (i != 0);
}
static void ipifcrecvra6(struct Ipifc *ifc, char **argv, int argc)
{
int i;
i = 0;
if (argc > 1)
i = atoi(argv[1]);
ifc->recvra6 = (i != 0);
}
static void ipifc_iprouting(struct Fs *f, char **argv, int argc)
{
int i = 1;
if (argc > 1)
i = atoi(argv[1]);
iprouting(f, i);
}
/*
* non-standard control messages.
* called with c locked.
*/
static void ipifcctl(struct conv *c, char **argv, int argc)
{
struct Ipifc *ifc;
int i;
ifc = (struct Ipifc *)c->ptcl;
if (strcmp(argv[0], "add") == 0)
ipifcadd(ifc, argv, argc, 0, NULL);
else if (strcmp(argv[0], "try") == 0)
ipifcadd(ifc, argv, argc, 1, NULL);
else if (strcmp(argv[0], "remove") == 0)
ipifcrem(ifc, argv, argc);
else if (strcmp(argv[0], "unbind") == 0)
ipifcunbind(ifc);
else if (strcmp(argv[0], "joinmulti") == 0)
ipifcjoinmulti(ifc, argv, argc);
else if (strcmp(argv[0], "leavemulti") == 0)
ipifcleavemulti(ifc, argv, argc);
else if (strcmp(argv[0], "mtu") == 0)
ipifcsetmtu(ifc, argv, argc);
else if (strcmp(argv[0], "reassemble") == 0)
ifc->reassemble = 1;
else if (strcmp(argv[0], "iprouting") == 0)
ipifc_iprouting(c->p->f, argv, argc);
else if (strcmp(argv[0], "addpref6") == 0)
ipifcaddpref6(ifc, argv, argc);
else if (strcmp(argv[0], "setpar6") == 0)
ipifcsetpar6(ifc, argv, argc);
else if (strcmp(argv[0], "sendra6") == 0)
ipifcsendra6(ifc, argv, argc);
else if (strcmp(argv[0], "recvra6") == 0)
ipifcrecvra6(ifc, argv, argc);
else
error(EINVAL, "unknown command to %s", __func__);
}
int ipifcstats(struct Proto *ipifc, char *buf, int len)
{
return ipstats(ipifc->f, buf, len);
}
void ipifcinit(struct Fs *f)
{
struct Proto *ipifc;
ipifc = kzmalloc(sizeof(struct Proto), 0);
ipifc->name = "ipifc";
ipifc->connect = ipifcconnect;
ipifc->announce = NULL;
ipifc->bind = ipifcbind;
ipifc->state = ipifcstate;
ipifc->create = ipifccreate;
ipifc->close = ipifcclose;
ipifc->rcv = NULL;
ipifc->ctl = ipifcctl;
ipifc->advise = NULL;
ipifc->stats = ipifcstats;
ipifc->inuse = ipifcinuse;
ipifc->local = ipifclocal;
ipifc->ipproto = -1;
ipifc->nc = Maxmedia;
ipifc->ptclsize = sizeof(struct Ipifc);
f->ipifc = ipifc; /* hack for ipifcremroute, findipifc, ... */
f->self = kzmalloc(sizeof(struct Ipselftab), 0); /* hack for ipforme */
qlock_init(&f->self->qlock);
Fsproto(f, ipifc);
}
/*
* TODO: Change this to a proper release.
* At the moment, this is difficult since link removal
* requires access to more than just the kref/struct Iplink.
* E.g., the self and interface pointers.
*/
static void link_release(struct kref *kref)
{
(void)kref;
}
/*
* add to self routing cache
* called with c locked
*/
static void addselfcache(struct Fs *f, struct Ipifc *ifc,
struct Iplifc *lifc, uint8_t *a, int type)
{
struct Ipself *p;
struct Iplink *lp;
int h;
qlock(&f->self->qlock);
/* 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 == NULL) {
p = kzmalloc(sizeof(*p), 0);
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 == NULL) {
lp = kzmalloc(sizeof(*lp), 0);
kref_init(&lp->ref, link_release, 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 != NULL)
(*ifc->m->addmulti) (ifc, a, lifc->local);
} else {
kref_get(&lp->ref, 1);
}
qunlock(&f->self->qlock);
}
/*
* 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 struct Iplink *freeiplink;
static struct Ipself *freeipself;
static void iplinkfree(struct Iplink *p)
{
struct Iplink **l, *np;
uint64_t now = NOW;
l = &freeiplink;
for (np = *l; np; np = *l) {
if (np->expire > now) {
*l = np->next;
kfree(np);
continue;
}
l = &np->next;
}
p->expire = now + 5000; /* give other threads 5 secs to get out */
p->next = NULL;
*l = p;
}
static void ipselffree(struct Ipself *p)
{
struct Ipself **l, *np;
uint64_t now = NOW;
l = &freeipself;
for (np = *l; np; np = *l) {
if (np->expire > now) {
*l = np->next;
kfree(np);
continue;
}
l = &np->next;
}
p->expire = now + 5000; /* give other threads 5 secs to get out */
p->next = NULL;
*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(struct Fs *f, struct Ipifc *ifc, struct Iplifc *lifc,
uint8_t *a)
{
struct Ipself *p, **l;
struct Iplink *link, **l_self, **l_lifc;
qlock(&f->self->qlock);
/* 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 == NULL)
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 == NULL)
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 == NULL)
panic("remselfcache");
/*
* TODO: The check for 'refcnt > 0' here is awkward. It
* exists so that 'remselfcache' can be called concurrently.
* In the original plan 9 code, the 'goto out' branch was
* taken if the decremented reference count was exactly zero.
* In other threads this could become -1, which plan 9 didn't
* care about since the logic would be skipped over, and since
* 'iplinkfree' won't _actually_ free the link for five seconds
* (see comments in that function), and since all of the actual
* link manipulation happens in the thread where the reference
* is exactly equal to zero. But on Akaros, a negative kref
* will panic; hence checking for a positive ref count before
* decrementing. This entire mechanism is dubious. But Since
* this function is protected by a lock this is probably OK for
* the time being.
*
* However, it is a terrible design and we should fix it.
*/
if (kref_refcnt(&link->ref) > 0 && kref_put(&link->ref) != 0)
goto out;
if ((p->type & Rmulti) && ifc->m->remmulti != NULL)
(*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 != NULL)
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->qlock);
}
static char *stformat = "%-44.44I %2.2d %4.4s\n";
enum {
Nstformat = 41,
};
long ipselftabread(struct Fs *f, char *cp, uint32_t offset, int n)
{
int i, m, nifc, off;
struct Ipself *p;
struct Iplink *link;
char state[8];
m = 0;
off = offset;
qlock(&f->self->qlock);
for (i = 0; i < NHASH && m < n; i++) {
for (p = f->self->hash[i]; p != NULL && m < n; p = p->next) {
nifc = 0;
for (link = p->link; link; link = link->selflink)
nifc++;
routetype(p->type, state);
m += snprintf(cp + m, n - m, stformat, p->a, nifc, state);
if (off > 0) {
off -= m;
m = 0;
}
}
}
qunlock(&f->self->qlock);
return m;
}
int iptentative(struct Fs *f, uint8_t * addr)
{
struct 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(struct Fs *f, uint8_t * addr)
{
struct 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 NULL.
*/
struct Ipifc *findipifc(struct Fs *f, uint8_t * remote, int type)
{
struct Ipifc *ifc, *x;
struct Iplifc *lifc;
struct conv **cp, **e;
uint8_t gnet[IPaddrlen];
uint8_t xmask[IPaddrlen];
x = NULL;
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 = (struct Ipifc *)(*cp)->ptcl;
for (lifc = ifc->lifc; lifc; lifc = lifc->next) {
maskip(remote, lifc->mask, gnet);
if (ipcmp(gnet, lifc->net) == 0) {
if (x == NULL || ipcmp(lifc->mask, xmask) > 0) {
x = ifc;
ipmove(xmask, lifc->mask);
}
}
}
}
if (x != NULL)
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 = (struct Ipifc *)(*cp)->ptcl;
if (ifc->lifc != NULL)
return ifc;
}
}
return NULL;
}
enum {
unknownv6,
multicastv6,
unspecifiedv6,
linklocalv6,
sitelocalv6,
globalv6,
};
int v6addrtype(uint8_t * 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 == UINT64_MAX ))
static void findprimaryipv6(struct Fs *f, uint8_t * local)
{
struct conv **cp, **e;
struct Ipifc *ifc;
struct 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 = (struct 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
*/
static void findprimaryipv4(struct Fs *f, uint8_t * local)
{
struct conv **cp, **e;
struct Ipifc *ifc;
struct 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 = (struct Ipifc *)(*cp)->ptcl;
if ((lifc = ifc->lifc) != NULL) {
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(struct Fs *f, uint8_t * local, uint8_t * remote)
{
struct Ipifc *ifc;
struct Iplifc *lifc;
struct route *r;
uint8_t gate[IPaddrlen];
uint8_t gnet[IPaddrlen];
int version;
int atype = unspecifiedv6, atypel = unknownv6;
qlock(&f->ipifc->qlock);
r = v6lookup(f, remote, NULL);
version = isv4(remote) ? V4 : V6;
if (r != NULL) {
ifc = r->rt.ifc;
if (r->rt.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:
findprimaryipv4(f, local);
break;
case V6:
findprimaryipv6(f, local);
break;
default:
panic("findlocalip2: version %d", version);
}
out:
qunlock(&f->ipifc->qlock);
}
/*
* return first v4 address associated with an interface
*/
int ipv4local(struct Ipifc *ifc, uint8_t * addr)
{
struct 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(struct Ipifc *ifc, uint8_t * addr)
{
struct 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(struct Ipifc *ifc, uint8_t * addr)
{
struct 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
*/
struct Iplifc *iplocalonifc(struct Ipifc *ifc, uint8_t * ip)
{
struct Iplifc *lifc;
for (lifc = ifc->lifc; lifc; lifc = lifc->next)
if (ipcmp(ip, lifc->local) == 0)
return lifc;
return NULL;
}
/*
* See if we're proxying for this address on this interface
*/
int ipproxyifc(struct Fs *f, struct Ipifc *ifc, uint8_t * ip)
{
struct route *r;
uint8_t net[IPaddrlen];
struct Iplifc *lifc;
/* see if this is a direct connected pt to pt address */
r = v6lookup(f, ip, NULL);
if (r == NULL)
return 0;
if ((r->rt.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(uint8_t * ip)
{
if (isv4(ip)) {
if (ip[IPv4off] >= 0xe0 && ip[IPv4off] < 0xf0)
return V4;
} else {
if (ip[0] == 0xff)
return V6;
}
return 0;
}
int ipisbm(uint8_t * 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(struct conv *c, uint8_t * ma, uint8_t * ia)
{
ERRSTACK(1);
struct Ipifc *ifc;
struct Iplifc *lifc;
struct conv **p;
struct Ipmulti *multi, **l;
struct 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 = kzmalloc(sizeof(*multi), 0);
ipmove(multi->ma, ma);
ipmove(multi->ia, ia);
multi->next = NULL;
for (p = f->ipifc->conv; *p; p++) {
if ((*p)->inuse == 0)
continue;
ifc = (struct Ipifc *)(*p)->ptcl;
wlock(&ifc->rwlock);
if (waserror()) {
wunlock(&ifc->rwlock);
nexterror();
}
for (lifc = ifc->lifc; lifc; lifc = lifc->next)
if (ipcmp(ia, lifc->local) == 0)
addselfcache(f, ifc, lifc, ma, Rmulti);
wunlock(&ifc->rwlock);
poperror();
}
}
/* Trace a block @b that crosses the interface @ifc. */
void ipifc_trace_block(struct Ipifc *ifc, struct block *bp)
{
struct block *newb;
if (!atomic_read(&ifc->conv->snoopers))
return;
newb = copyblock(bp, MEM_ATOMIC);
if (!newb) {
ifc->tracedrop++;
return;
}
if (qpass(ifc->conv->sq, newb) < 0)
ifc->tracedrop++;
}
/*
* remove a multicast address from an interface, called with c locked
*/
void ipifcremmulti(struct conv *c, uint8_t * ma, uint8_t * ia)
{
ERRSTACK(1);
struct Ipmulti *multi, **l;
struct Iplifc *lifc;
struct conv **p;
struct Ipifc *ifc;
struct 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 == NULL)
return; /* we don't have it open */
*l = multi->next;
for (p = f->ipifc->conv; *p; p++) {
if ((*p)->inuse == 0)
continue;
ifc = (struct Ipifc *)(*p)->ptcl;
wlock(&ifc->rwlock);
if (waserror()) {
wunlock(&ifc->rwlock);
nexterror();
}
for (lifc = ifc->lifc; lifc; lifc = lifc->next)
if (ipcmp(ia, lifc->local) == 0)
remselfcache(f, ifc, lifc, ma);
wunlock(&ifc->rwlock);
poperror();
}
kfree(multi);
}
/*
* make lifc's join and leave multicast groups
*/
static void ipifcjoinmulti(struct Ipifc *ifc, char **argv, int argc)
{
warn_once("Not implemented, should it be?");
}
static void ipifcleavemulti(struct Ipifc *ifc, char **argv, int argc)
{
warn_once("Not implemented, should it be?");
}
static void ipifcregisterproxy(struct Fs *f, struct Ipifc *ifc, uint8_t * ip)
{
struct conv **cp, **e;
struct Ipifc *nifc;
struct Iplifc *lifc;
struct medium *m;
uint8_t 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 == NULL)
continue;
nifc = (struct Ipifc *)(*cp)->ptcl;
if (nifc == ifc)
continue;
rlock(&nifc->rwlock);
m = nifc->m;
if (m == NULL || m->addmulti == NULL) {
runlock(&nifc->rwlock);
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->rwlock);
}
return;
} else { // V4
for (cp = f->ipifc->conv; cp < e; cp++) {
if (*cp == NULL)
continue;
nifc = (struct Ipifc *)(*cp)->ptcl;
if (nifc == ifc)
continue;
rlock(&nifc->rwlock);
m = nifc->m;
if (m == NULL || m->areg == NULL) {
runlock(&nifc->rwlock);
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->rwlock);
}
}
}
// added for new v6 mesg types
static void adddefroute6(struct Fs *f, uint8_t * gate, int force)
{
struct route *r;
r = v6lookup(f, v6Unspecified, NULL);
if (r != NULL)
if (!(force) && (strcmp(r->rt.tag, "ra") != 0))
return;
// route entries generated by all other means take precedence over
// router annc
v6delroute(f, v6Unspecified, v6Unspecified, 1);
v6addroute(f, "ra", v6Unspecified, v6Unspecified, gate, 0);
}
enum {
Ngates = 3,
};
static void ipifcaddpref6(struct Ipifc *ifc, char **argv, int argc)
{
uint8_t onlink = 1;
uint8_t autoflag = 1;
uint64_t validlt = UINT64_MAX;
uint64_t preflt = UINT64_MAX;
uint64_t origint = NOW / 10 ^ 3;
uint8_t prefix[IPaddrlen];
int plen = 64;
struct 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:
error(EINVAL, "Bad arg num to %s", __func__);
}
if ((parseip(prefix, argv[1]) != 6) || (validlt < preflt) || (plen < 0)
|| (plen > 64) || (islinklocal(prefix)))
error(EFAIL, "IP parsing failed");
lifc = kzmalloc(sizeof(struct Iplifc), 0);
lifc->onlink = (onlink != 0);
lifc->autoflag = (autoflag != 0);
lifc->validlt = validlt;
lifc->preflt = preflt;
lifc->origint = origint;
if (ifc->m->pref2addr != NULL)
ifc->m->pref2addr(prefix, ifc->mac);
else
error(EFAIL, "Null IFC pref");
snprintf(addr, sizeof(addr), "%I", prefix);
snprintf(preflen, sizeof(preflen), "/%d", plen);
params[0] = "add";
params[1] = addr;
params[2] = preflen;
ipifcadd(ifc, params, 3, 0, lifc);
}