#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
#include "../port/netif.h"
static int netown(Netfile*, char*, int);
static int openfile(Netif*, int);
static char* matchtoken(char*, char*);
static char* netmulti(Netif*, Netfile*, uchar*, int);
static int parseaddr(uchar*, char*, int);
/*
* set up a new network interface
*/
void
netifinit(Netif *nif, char *name, int nfile, ulong limit)
{
strncpy(nif->name, name, KNAMELEN-1);
nif->name[KNAMELEN-1] = 0;
nif->nfile = nfile;
nif->f = xalloc(nfile*sizeof(Netfile*));
if (nif->f == nil)
panic("netifinit: no memory");
memset(nif->f, 0, nfile*sizeof(Netfile*));
nif->limit = limit;
}
/*
* generate a 3 level directory
*/
static int
netifgen(Chan *c, char*, Dirtab *vp, int, int i, Dir *dp)
{
Qid q;
Netif *nif = (Netif*)vp;
Netfile *f;
int t;
int perm;
char *o;
q.type = QTFILE;
q.vers = 0;
/* top level directory contains the name of the network */
if(c->qid.path == 0){
switch(i){
case DEVDOTDOT:
q.path = 0;
q.type = QTDIR;
devdir(c, q, ".", 0, eve, 0555, dp);
break;
case 0:
q.path = N2ndqid;
q.type = QTDIR;
strcpy(up->genbuf, nif->name);
devdir(c, q, up->genbuf, 0, eve, 0555, dp);
break;
default:
return -1;
}
return 1;
}
/* second level contains clone plus all the conversations */
t = NETTYPE(c->qid.path);
if(t == N2ndqid || t == Ncloneqid || t == Naddrqid || t == Nmtuqid){
switch(i) {
case DEVDOTDOT:
q.type = QTDIR;
q.path = 0;
devdir(c, q, ".", 0, eve, DMDIR|0555, dp);
break;
case 0:
q.path = Ncloneqid;
devdir(c, q, "clone", 0, eve, 0666, dp);
break;
case 1:
q.path = Naddrqid;
devdir(c, q, "addr", 0, eve, 0666, dp);
break;
case 2:
q.path = Nstatqid;
devdir(c, q, "stats", 0, eve, 0444, dp);
break;
case 3:
q.path = Nifstatqid;
devdir(c, q, "ifstats", 0, eve, 0444, dp);
break;
case 4:
q.path = Nmtuqid;
devdir(c, q, "mtu", 0, eve, 0444, dp);
break;
default:
i -= 5;
if(i >= nif->nfile)
return -1;
if(nif->f[i] == 0)
return 0;
q.type = QTDIR;
q.path = NETQID(i, N3rdqid);
sprint(up->genbuf, "%d", i);
devdir(c, q, up->genbuf, 0, eve, DMDIR|0555, dp);
break;
}
return 1;
}
/* third level */
f = nif->f[NETID(c->qid.path)];
if(f == 0)
return 0;
if(*f->owner){
o = f->owner;
perm = f->mode;
} else {
o = eve;
perm = 0666;
}
switch(i){
case DEVDOTDOT:
q.type = QTDIR;
q.path = N2ndqid;
strcpy(up->genbuf, nif->name);
devdir(c, q, up->genbuf, 0, eve, DMDIR|0555, dp);
break;
case 0:
q.path = NETQID(NETID(c->qid.path), Ndataqid);
devdir(c, q, "data", 0, o, perm, dp);
break;
case 1:
q.path = NETQID(NETID(c->qid.path), Nctlqid);
devdir(c, q, "ctl", 0, o, perm, dp);
break;
case 2:
q.path = NETQID(NETID(c->qid.path), Nstatqid);
devdir(c, q, "stats", 0, eve, 0444, dp);
break;
case 3:
q.path = NETQID(NETID(c->qid.path), Ntypeqid);
devdir(c, q, "type", 0, eve, 0444, dp);
break;
case 4:
q.path = NETQID(NETID(c->qid.path), Nifstatqid);
devdir(c, q, "ifstats", 0, eve, 0444, dp);
break;
default:
return -1;
}
return 1;
}
Walkqid*
netifwalk(Netif *nif, Chan *c, Chan *nc, char **name, int nname)
{
return devwalk(c, nc, name, nname, (Dirtab *)nif, 0, netifgen);
}
Chan*
netifopen(Netif *nif, Chan *c, int omode)
{
int id;
Netfile *f;
id = 0;
if(c->qid.type & QTDIR){
if(omode != OREAD)
error(Eperm);
} else {
switch(NETTYPE(c->qid.path)){
case Ndataqid:
case Nctlqid:
id = NETID(c->qid.path);
openfile(nif, id);
break;
case Ncloneqid:
id = openfile(nif, -1);
c->qid.path = NETQID(id, Nctlqid);
break;
default:
if(omode != OREAD)
error(Ebadarg);
}
switch(NETTYPE(c->qid.path)){
case Ndataqid:
case Nctlqid:
f = nif->f[id];
if(netown(f, up->user, omode&7) < 0)
error(Eperm);
break;
}
}
c->mode = openmode(omode);
c->flag |= COPEN;
c->offset = 0;
c->iounit = qiomaxatomic;
return c;
}
long
netifread(Netif *nif, Chan *c, void *a, long n, ulong offset)
{
int i, j;
Netfile *f;
char *p;
if(c->qid.type&QTDIR)
return devdirread(c, a, n, (Dirtab*)nif, 0, netifgen);
switch(NETTYPE(c->qid.path)){
case Ndataqid:
f = nif->f[NETID(c->qid.path)];
return qread(f->in, a, n);
case Nctlqid:
return readnum(offset, a, n, NETID(c->qid.path), NUMSIZE);
case Nstatqid:
p = malloc(READSTR);
j = snprint(p, READSTR, "in: %d\n", nif->inpackets);
j += snprint(p+j, READSTR-j, "link: %d\n", nif->link);
j += snprint(p+j, READSTR-j, "out: %d\n", nif->outpackets);
j += snprint(p+j, READSTR-j, "crc errs: %d\n", nif->crcs);
j += snprint(p+j, READSTR-j, "overflows: %d\n", nif->overflows);
j += snprint(p+j, READSTR-j, "soft overflows: %d\n", nif->soverflows);
j += snprint(p+j, READSTR-j, "framing errs: %d\n", nif->frames);
j += snprint(p+j, READSTR-j, "buffer errs: %d\n", nif->buffs);
j += snprint(p+j, READSTR-j, "output errs: %d\n", nif->oerrs);
j += snprint(p+j, READSTR-j, "prom: %d\n", nif->prom);
j += snprint(p+j, READSTR-j, "mbps: %d\n", nif->mbps);
j += snprint(p+j, READSTR-j, "addr: ");
for(i = 0; i < nif->alen; i++)
j += snprint(p+j, READSTR-j, "%2.2ux", nif->addr[i]);
snprint(p+j, READSTR-j, "\n");
n = readstr(offset, a, n, p);
free(p);
return n;
case Naddrqid:
p = malloc(READSTR);
j = 0;
for(i = 0; i < nif->alen; i++)
j += snprint(p+j, READSTR-j, "%2.2ux", nif->addr[i]);
n = readstr(offset, a, n, p);
free(p);
return n;
case Ntypeqid:
f = nif->f[NETID(c->qid.path)];
return readnum(offset, a, n, f->type, NUMSIZE);
case Nifstatqid:
return 0;
case Nmtuqid:
snprint(up->genbuf, sizeof up->genbuf, "%11.ud %11.ud %11.ud\n", nif->minmtu, nif->mtu, nif->maxmtu);
return readstr(offset, a, n, up->genbuf);
}
error(Ebadarg);
return -1; /* not reached */
}
Block*
netifbread(Netif *nif, Chan *c, long n, ulong offset)
{
if((c->qid.type & QTDIR) || NETTYPE(c->qid.path) != Ndataqid)
return devbread(c, n, offset);
return qbread(nif->f[NETID(c->qid.path)]->in, n);
}
/*
* make sure this type isn't already in use on this device
*/
static int
typeinuse(Netif *nif, int type)
{
Netfile *f, **fp, **efp;
if(type <= 0)
return 0;
efp = &nif->f[nif->nfile];
for(fp = nif->f; fp < efp; fp++){
f = *fp;
if(f == 0)
continue;
if(f->type == type)
return 1;
}
return 0;
}
/*
* the devxxx.c that calls us handles writing data, it knows best
*/
long
netifwrite(Netif *nif, Chan *c, void *a, long n)
{
Netfile *f;
int type, mtu;
char *p, buf[64];
uchar binaddr[Nmaxaddr];
if(NETTYPE(c->qid.path) != Nctlqid)
error(Eperm);
if(n >= sizeof(buf))
n = sizeof(buf)-1;
memmove(buf, a, n);
buf[n] = 0;
if(waserror()){
qunlock(nif);
nexterror();
}
qlock(nif);
f = nif->f[NETID(c->qid.path)];
if((p = matchtoken(buf, "connect")) != 0){
type = atoi(p);
if(typeinuse(nif, type))
error(Einuse);
f->type = type;
if(f->type < 0)
nif->all++;
} else if(matchtoken(buf, "promiscuous")){
if(f->prom == 0){
if(nif->prom == 0 && nif->promiscuous != nil)
nif->promiscuous(nif->arg, 1);
f->prom = 1;
nif->prom++;
}
} else if((p = matchtoken(buf, "scanbs")) != 0){
/* scan for base stations */
if(f->scan == 0){
type = atoi(p);
if(type < 5)
type = 5;
if(nif->scanbs != nil)
nif->scanbs(nif->arg, type);
f->scan = type;
nif->scan++;
}
} else if((p = matchtoken(buf, "mtu")) != 0){
mtu = atoi(p);
// zero resets default.
if(mtu != 0)
if(mtu < nif->minmtu || mtu > nif->maxmtu)
error(Ebadarg);
if(nif->hwmtu)
nif->mtu = nif->hwmtu(nif->arg, mtu);
else
nif->mtu = mtu;
} else if(matchtoken(buf, "bridge")){
f->bridge = 1;
} else if(matchtoken(buf, "headersonly")){
f->headersonly = 1;
} else if((p = matchtoken(buf, "addmulti")) != 0){
if(parseaddr(binaddr, p, nif->alen) < 0)
error("bad address");
p = netmulti(nif, f, binaddr, 1);
if(p)
error(p);
} else if((p = matchtoken(buf, "remmulti")) != 0){
if(parseaddr(binaddr, p, nif->alen) < 0)
error("bad address");
p = netmulti(nif, f, binaddr, 0);
if(p)
error(p);
} else
n = -1;
qunlock(nif);
poperror();
return n;
}
int
netifwstat(Netif *nif, Chan *c, uchar *db, int n)
{
Dir *dir;
Netfile *f;
int m;
f = nif->f[NETID(c->qid.path)];
if(f == 0)
error(Enonexist);
if(netown(f, up->user, OWRITE) < 0)
error(Eperm);
dir = smalloc(sizeof(Dir)+n);
m = convM2D(db, n, &dir[0], (char*)&dir[1]);
if(m == 0){
free(dir);
error(Eshortstat);
}
if(!emptystr(dir[0].uid))
strncpy(f->owner, dir[0].uid, KNAMELEN);
if(dir[0].mode != ~0UL)
f->mode = dir[0].mode;
free(dir);
return m;
}
int
netifstat(Netif *nif, Chan *c, uchar *db, int n)
{
return devstat(c, db, n, (Dirtab *)nif, 0, netifgen);
}
void
netifclose(Netif *nif, Chan *c)
{
Netfile *f;
int t;
Netaddr *ap;
if((c->flag & COPEN) == 0)
return;
t = NETTYPE(c->qid.path);
if(t != Ndataqid && t != Nctlqid)
return;
f = nif->f[NETID(c->qid.path)];
qlock(f);
if(--(f->inuse) == 0){
if(f->prom){
qlock(nif);
if(--(nif->prom) == 0 && nif->promiscuous != nil)
nif->promiscuous(nif->arg, 0);
qunlock(nif);
f->prom = 0;
}
if(f->scan){
qlock(nif);
if(--(nif->scan) == 0 && nif->scanbs != nil)
nif->scanbs(nif->arg, 0);
qunlock(nif);
f->prom = 0;
f->scan = 0;
}
if(f->nmaddr){
qlock(nif);
t = 0;
for(ap = nif->maddr; ap; ap = ap->next){
if(f->maddr[t/8] & (1<<(t%8)))
netmulti(nif, f, ap->addr, 0);
}
qunlock(nif);
f->nmaddr = 0;
}
if(f->type < 0){
qlock(nif);
--(nif->all);
qunlock(nif);
}
f->owner[0] = 0;
f->type = 0;
f->bridge = 0;
f->headersonly = 0;
qclose(f->in);
}
qunlock(f);
}
Lock netlock;
static int
netown(Netfile *p, char *o, int omode)
{
static int access[] = { 0400, 0200, 0600, 0100 };
int mode;
int t;
lock(&netlock);
if(*p->owner){
if(strncmp(o, p->owner, KNAMELEN) == 0) /* User */
mode = p->mode;
else if(strncmp(o, eve, KNAMELEN) == 0) /* Bootes is group */
mode = p->mode<<3;
else
mode = p->mode<<6; /* Other */
t = access[omode&3];
if((t & mode) == t){
unlock(&netlock);
return 0;
} else {
unlock(&netlock);
return -1;
}
}
strncpy(p->owner, o, KNAMELEN);
p->mode = 0660;
unlock(&netlock);
return 0;
}
/*
* Increment the reference count of a network device.
* If id < 0, return an unused ether device.
*/
static int
openfile(Netif *nif, int id)
{
Netfile *f, **fp, **efp;
if(id >= 0){
f = nif->f[id];
if(f == 0)
error(Enodev);
qlock(f);
qreopen(f->in);
f->inuse++;
qunlock(f);
return id;
}
qlock(nif);
if(waserror()){
qunlock(nif);
nexterror();
}
efp = &nif->f[nif->nfile];
for(fp = nif->f; fp < efp; fp++){
f = *fp;
if(f == 0){
f = malloc(sizeof(Netfile));
if(f == 0)
exhausted("memory");
f->in = qopen(nif->limit, Qmsg, 0, 0);
if(f->in == nil){
free(f);
exhausted("memory");
}
*fp = f;
qlock(f);
} else {
qlock(f);
if(f->inuse){
qunlock(f);
continue;
}
}
f->inuse = 1;
qreopen(f->in);
netown(f, up->user, 0);
qunlock(f);
qunlock(nif);
poperror();
return fp - nif->f;
}
error(Enodev);
return -1; /* not reached */
}
/*
* look for a token starting a string,
* return a pointer to first non-space char after it
*/
static char*
matchtoken(char *p, char *token)
{
int n;
n = strlen(token);
if(strncmp(p, token, n))
return 0;
p += n;
if(*p == 0)
return p;
if(*p != ' ' && *p != '\t' && *p != '\n')
return 0;
while(*p == ' ' || *p == '\t' || *p == '\n')
p++;
return p;
}
void
hnputv(void *p, vlong v)
{
uchar *a;
a = p;
hnputl(a, v>>32);
hnputl(a+4, v);
}
void
hnputl(void *p, ulong v)
{
uchar *a;
a = p;
a[0] = v>>24;
a[1] = v>>16;
a[2] = v>>8;
a[3] = v;
}
void
hnputs(void *p, ushort v)
{
uchar *a;
a = p;
a[0] = v>>8;
a[1] = v;
}
vlong
nhgetv(void *p)
{
uchar *a;
a = p;
return ((vlong)nhgetl(a) << 32) | nhgetl(a+4);
}
ulong
nhgetl(void *p)
{
uchar *a;
a = p;
return (a[0]<<24)|(a[1]<<16)|(a[2]<<8)|(a[3]<<0);
}
ushort
nhgets(void *p)
{
uchar *a;
a = p;
return (a[0]<<8)|(a[1]<<0);
}
static ulong
hash(uchar *a, int len)
{
ulong sum = 0;
while(len-- > 0)
sum = (sum << 1) + *a++;
return sum%Nmhash;
}
int
activemulti(Netif *nif, uchar *addr, int alen)
{
Netaddr *hp;
for(hp = nif->mhash[hash(addr, alen)]; hp; hp = hp->hnext)
if(memcmp(addr, hp->addr, alen) == 0){
if(hp->ref)
return 1;
else
break;
}
return 0;
}
static int
parseaddr(uchar *to, char *from, int alen)
{
char nip[4];
char *p;
int i;
p = from;
for(i = 0; i < alen; i++){
if(*p == 0)
return -1;
nip[0] = *p++;
if(*p == 0)
return -1;
nip[1] = *p++;
nip[2] = 0;
to[i] = strtoul(nip, 0, 16);
if(*p == ':')
p++;
}
return 0;
}
/*
* keep track of multicast addresses
*/
static char*
netmulti(Netif *nif, Netfile *f, uchar *addr, int add)
{
Netaddr **l, *ap;
int i;
ulong h;
if(nif->multicast == nil)
return "interface does not support multicast";
l = &nif->maddr;
i = 0;
for(ap = *l; ap; ap = *l){
if(memcmp(addr, ap->addr, nif->alen) == 0)
break;
i++;
l = &ap->next;
}
if(add){
if(ap == 0){
*l = ap = smalloc(sizeof(*ap));
memmove(ap->addr, addr, nif->alen);
ap->next = 0;
ap->ref = 1;
h = hash(addr, nif->alen);
ap->hnext = nif->mhash[h];
nif->mhash[h] = ap;
} else {
ap->ref++;
}
if(ap->ref == 1){
nif->nmaddr++;
nif->multicast(nif->arg, addr, 1);
}
if(i < 8*sizeof(f->maddr)){
if((f->maddr[i/8] & (1<<(i%8))) == 0)
f->nmaddr++;
f->maddr[i/8] |= 1<<(i%8);
}
} else {
if(ap == 0 || ap->ref == 0)
return 0;
ap->ref--;
if(ap->ref == 0){
nif->nmaddr--;
nif->multicast(nif->arg, addr, 0);
}
if(i < 8*sizeof(f->maddr)){
if((f->maddr[i/8] & (1<<(i%8))) != 0)
f->nmaddr--;
f->maddr[i/8] &= ~(1<<(i%8));
}
}
return 0;
}
|