summaryrefslogtreecommitdiff
path: root/sys/src/9/port/netif.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/9/port/netif.c
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/9/port/netif.c')
-rwxr-xr-xsys/src/9/port/netif.c725
1 files changed, 725 insertions, 0 deletions
diff --git a/sys/src/9/port/netif.c b/sys/src/9/port/netif.c
new file mode 100755
index 000000000..3260a65b2
--- /dev/null
+++ b/sys/src/9/port/netif.c
@@ -0,0 +1,725 @@
+#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){
+ 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;
+ default:
+ i -= 4;
+ if(i >= nif->nfile)
+ return -1;
+ if(nif->f[i] == 0)
+ return 0;
+ q.type = QTDIR;
+ q.path = NETQID(i, N3rdqid);
+ snprint(up->genbuf, sizeof 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: %llud\n", nif->inpackets);
+ j += snprint(p+j, READSTR-j, "link: %d\n", nif->link);
+ j += snprint(p+j, READSTR-j, "out: %llud\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;
+ }
+ 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;
+ 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(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, uvlong v)
+{
+ uchar *a;
+
+ a = p;
+ hnputl(a, v>>32);
+ hnputl(a+4, v);
+}
+
+void
+hnputl(void *p, uint 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;
+}
+
+uvlong
+nhgetv(void *p)
+{
+ uchar *a;
+
+ a = p;
+ return ((vlong)nhgetl(a) << 32) | nhgetl(a+4);
+}
+
+uint
+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;
+}