|  | /* 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> | 
|  |  | 
|  | static int netown(struct netfile *, char *unused_char_p_t, int); | 
|  | static int openfile(struct ether *, int); | 
|  | static char *matchtoken(char *unused_char_p_t, char *); | 
|  | static char *netmulti(struct ether *, struct netfile *, | 
|  | uint8_t *unused_uint8_p_t, int); | 
|  | static int parseaddr(uint8_t *unused_uint8_p_t, char *unused_char_p_t, int); | 
|  |  | 
|  | /* | 
|  | *  set up a new network interface | 
|  | */ | 
|  | void netifinit(struct ether *nif, char *name, int nfile, uint32_t limit) | 
|  | { | 
|  | qlock_init(&nif->qlock); | 
|  | strlcpy(nif->name, name, KNAMELEN); | 
|  | nif->nfile = nfile; | 
|  | nif->f = kzmalloc(nfile * sizeof(struct netfile *), 0); | 
|  | if (nif->f) | 
|  | memset(nif->f, 0, nfile * sizeof(struct netfile *)); | 
|  | else | 
|  | nif->nfile = 0; | 
|  | nif->limit = limit; | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  generate a 3 level directory | 
|  | */ | 
|  | static int netifgen(struct chan *c, char *unused_char_p_t, struct dirtab *vp, | 
|  | int unused_int, int i, struct dir *dp) | 
|  | { | 
|  | struct qid q; | 
|  | struct ether *nif = (struct ether *)vp; | 
|  | struct netfile *f; | 
|  | 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.name, 0555, dp); | 
|  | break; | 
|  | case 0: | 
|  | q.path = N2ndqid; | 
|  | q.type = QTDIR; | 
|  | strlcpy(get_cur_genbuf(), nif->name, GENBUF_SZ); | 
|  | devdir(c, q, get_cur_genbuf(), 0, eve.name, 0555, dp); | 
|  | break; | 
|  | default: | 
|  | return -1; | 
|  | } | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | /* second level contains clone plus all the conversations. | 
|  | * | 
|  | * This ancient comment is from plan9.  Inferno and nxm both had issues | 
|  | * here.  You couldn't ls /net/ether0/ when it didn't have any convs. | 
|  | * There were also issues with nxm where you couldn't stat | 
|  | * ether0/x/stats properly. | 
|  | * | 
|  | * The issue is that if we handle things like Nstatqid, then we will | 
|  | * never pass it down to the third level. And since we just set the path | 
|  | * == Nstatqid, we won't have the NETID muxed in. If someone isn't | 
|  | * trying to generate a chan, but instead is looking it up (devwalk | 
|  | * generates, devstat already has the chan), then they are also looking | 
|  | * for a devdir with path containing ID << 5. So if you stat | 
|  | * ether0/1/ifstats, devstat is looking for path 41, but we return path | 
|  | * 9 (41 = 32 + 9). (these numbers are before we tracked NETID + 1). | 
|  | * | 
|  | * We (akaros and plan9) had a big if here, that would catch things that | 
|  | * do not exist in the subdirs of a netif. Things like clone make sense | 
|  | * here.  I guess addr too, though that seems to be added since the | 
|  | * original comment. You can see what the 3rd level was expecting to | 
|  | * parse by looking farther down in the code. | 
|  | * | 
|  | * The root of the problem was that the old code couldn't tell the | 
|  | * difference between no netid and netid 0. Now, we determine if we're | 
|  | * at the second level by the lack of a netid, instead of trying to | 
|  | * enumerate the qid types that the second level could have. The latter | 
|  | * approach allowed for something like ether0/1/stats, but we couldn't | 
|  | * actually devstat ether0/stats directly. It's worth noting that there | 
|  | * is no difference to the content of ether0/stats and ether0/x/stats | 
|  | * (when you read), but they have different chan qids. | 
|  | * | 
|  | * Here's the old if block: | 
|  | t = NETTYPE(c->qid.path); | 
|  | if (t == N2ndqid || t == Ncloneqid || t == Naddrqid) { | 
|  | */ | 
|  | if (NETID(c->qid.path) == -1) { | 
|  | switch (i) { | 
|  | case DEVDOTDOT: | 
|  | q.type = QTDIR; | 
|  | q.path = 0; | 
|  | devdir(c, q, ".", 0, eve.name, DMDIR | 0555, dp); | 
|  | break; | 
|  | case 0: | 
|  | q.path = Ncloneqid; | 
|  | devdir(c, q, "clone", 0, eve.name, 0666, dp); | 
|  | break; | 
|  | case 1: | 
|  | q.path = Naddrqid; | 
|  | devdir(c, q, "addr", 0, eve.name, 0666, dp); | 
|  | break; | 
|  | case 2: | 
|  | q.path = Nstatqid; | 
|  | devdir(c, q, "stats", 0, eve.name, 0444, dp); | 
|  | break; | 
|  | case 3: | 
|  | q.path = Nifstatqid; | 
|  | devdir(c, q, "ifstats", 0, eve.name, 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); | 
|  | snprintf(get_cur_genbuf(), GENBUF_SZ, "%d", i); | 
|  | devdir(c, q, get_cur_genbuf(), 0, eve.name, | 
|  | 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.name; | 
|  | perm = 0666; | 
|  | } | 
|  | switch (i) { | 
|  | case DEVDOTDOT: | 
|  | q.type = QTDIR; | 
|  | q.path = N2ndqid; | 
|  | strlcpy(get_cur_genbuf(), nif->name, GENBUF_SZ); | 
|  | devdir(c, q, get_cur_genbuf(), 0, eve.name, 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.name, 0444, dp); | 
|  | break; | 
|  | case 3: | 
|  | q.path = NETQID(NETID(c->qid.path), Ntypeqid); | 
|  | devdir(c, q, "type", 0, eve.name, 0444, dp); | 
|  | break; | 
|  | case 4: | 
|  | q.path = NETQID(NETID(c->qid.path), Nifstatqid); | 
|  | devdir(c, q, "ifstats", 0, eve.name, 0444, dp); | 
|  | break; | 
|  | default: | 
|  | return -1; | 
|  | } | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | struct walkqid *netifwalk(struct ether *nif, struct chan *c, struct chan *nc, | 
|  | char **name, int nname) | 
|  | { | 
|  | return devwalk(c, nc, name, nname, (struct dirtab *)nif, 0, netifgen); | 
|  | } | 
|  |  | 
|  | struct chan *netifopen(struct ether *nif, struct chan *c, int omode) | 
|  | { | 
|  | int id; | 
|  | struct netfile *f; | 
|  |  | 
|  | id = 0; | 
|  | if (c->qid.type & QTDIR) { | 
|  | if (omode & O_WRITE) | 
|  | error(EPERM, ERROR_FIXME); | 
|  | } 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 & O_WRITE) | 
|  | error(EINVAL, ERROR_FIXME); | 
|  | } | 
|  | switch (NETTYPE(c->qid.path)) { | 
|  | case Ndataqid: | 
|  | case Nctlqid: | 
|  | f = nif->f[id]; | 
|  | if (netown(f, current->user.name, omode & 7) < 0) | 
|  | error(EPERM, ERROR_FIXME); | 
|  | break; | 
|  | } | 
|  | } | 
|  | c->mode = openmode(omode); | 
|  | c->flag |= COPEN; | 
|  | c->offset = 0; | 
|  | c->iounit = qiomaxatomic; | 
|  | return c; | 
|  | } | 
|  |  | 
|  | /* Helper for building the features for netifread */ | 
|  | static int feature_appender(int features, char *p, int sofar) | 
|  | { | 
|  | if (features & NETF_IPCK) | 
|  | sofar += snprintf(p + sofar, READSTR - sofar, "ipck "); | 
|  | if (features & NETF_UDPCK) | 
|  | sofar += snprintf(p + sofar, READSTR - sofar, "udpck "); | 
|  | if (features & NETF_TCPCK) | 
|  | sofar += snprintf(p + sofar, READSTR - sofar, "tcpck "); | 
|  | if (features & NETF_PADMIN) | 
|  | sofar += snprintf(p + sofar, READSTR - sofar, "padmin "); | 
|  | if (features & NETF_SG) | 
|  | sofar += snprintf(p + sofar, READSTR - sofar, "sg "); | 
|  | if (features & NETF_TSO) | 
|  | sofar += snprintf(p + sofar, READSTR - sofar, "tso "); | 
|  | if (features & NETF_LRO) | 
|  | sofar += snprintf(p + sofar, READSTR - sofar, "lro "); | 
|  | if (features & NETF_RXCSUM) | 
|  | sofar += snprintf(p + sofar, READSTR - sofar, "rxcsum "); | 
|  | return sofar; | 
|  | } | 
|  |  | 
|  | long netifread(struct ether *nif, struct chan *c, void *a, long n, | 
|  | uint32_t offset) | 
|  | { | 
|  | int i, j; | 
|  | struct netfile *f; | 
|  | char *p; | 
|  |  | 
|  | if (c->qid.type & QTDIR) | 
|  | return devdirread(c, a, n, (struct 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 = kzmalloc(READSTR, 0); | 
|  | if (p == NULL) | 
|  | return 0; | 
|  | j = 0; | 
|  | j += snprintf(p + j, READSTR - j, "driver: %s\n", | 
|  | nif->drv_name); | 
|  | j += snprintf(p + j, READSTR - j, "in: %d\n", nif->inpackets); | 
|  | j += snprintf(p + j, READSTR - j, "link: %d\n", nif->link); | 
|  | j += snprintf(p + j, READSTR - j, "out: %d\n", nif->outpackets); | 
|  | j += snprintf(p + j, READSTR - j, "crc errs: %d\n", nif->crcs); | 
|  | j += snprintf(p + j, READSTR - j, "overflows: %d\n", | 
|  | nif->overflows); | 
|  | j += snprintf(p + j, READSTR - j, "soft overflows: %d\n", | 
|  | nif->soverflows); | 
|  | j += snprintf(p + j, READSTR - j, "framing errs: %d\n", | 
|  | nif->frames); | 
|  | j += snprintf(p + j, READSTR - j, "buffer errs: %d\n", | 
|  | nif->buffs); | 
|  | j += snprintf(p + j, READSTR - j, "output errs: %d\n", | 
|  | nif->oerrs); | 
|  | j += snprintf(p + j, READSTR - j, "prom: %d\n", nif->prom); | 
|  | j += snprintf(p + j, READSTR - j, "mbps: %d\n", nif->mbps); | 
|  | j += snprintf(p + j, READSTR - j, "addr: "); | 
|  | for (i = 0; i < nif->alen; i++) | 
|  | j += snprintf(p + j, READSTR - j, "%02.2x", | 
|  | nif->addr[i]); | 
|  | j += snprintf(p + j, READSTR - j, "\n"); | 
|  |  | 
|  | j += snprintf(p + j, READSTR - j, "feat: "); | 
|  | j = feature_appender(nif->feat, p, j); | 
|  | j += snprintf(p + j, READSTR - j, "\n"); | 
|  |  | 
|  | j += snprintf(p + j, READSTR - j, "hw_features: "); | 
|  | j = feature_appender(nif->hw_features, p, j); | 
|  | j += snprintf(p + j, READSTR - j, "\n"); | 
|  |  | 
|  | n = readstr(offset, a, n, p); | 
|  | kfree(p); | 
|  | return n; | 
|  | case Naddrqid: | 
|  | p = kzmalloc(READSTR, 0); | 
|  | if (p == NULL) | 
|  | return 0; | 
|  | j = 0; | 
|  | for (i = 0; i < nif->alen; i++) | 
|  | j += snprintf(p + j, READSTR - j, "%02.2x", | 
|  | nif->addr[i]); | 
|  | n = readstr(offset, a, n, p); | 
|  | kfree(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(EINVAL, ERROR_FIXME); | 
|  | return -1;	/* not reached */ | 
|  | } | 
|  |  | 
|  | struct block *netifbread(struct ether *nif, struct chan *c, long n, | 
|  | uint32_t 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(struct ether *nif, int type) | 
|  | { | 
|  | struct 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(struct ether *nif, struct chan *c, void *a, long n) | 
|  | { | 
|  | ERRSTACK(1); | 
|  | struct netfile *f; | 
|  | int type; | 
|  | char *p, buf[64]; | 
|  | uint8_t binaddr[Nmaxaddr]; | 
|  |  | 
|  | if (NETTYPE(c->qid.path) != Nctlqid) | 
|  | error(EPERM, ERROR_FIXME); | 
|  |  | 
|  | if (n >= sizeof(buf)) | 
|  | n = sizeof(buf) - 1; | 
|  | memmove(buf, a, n); | 
|  | buf[n] = 0; | 
|  |  | 
|  | qlock(&nif->qlock); | 
|  | if (waserror()) { | 
|  | qunlock(&nif->qlock); | 
|  | nexterror(); | 
|  | } | 
|  |  | 
|  | f = nif->f[NETID(c->qid.path)]; | 
|  | if ((p = matchtoken(buf, "connect")) != 0) { | 
|  | /* We'd like to not use the NIC until it has come up fully - | 
|  | * auto-negotiation is done and packets will get sent out.  This | 
|  | * is about the best place to do it. */ | 
|  | netif_wait_for_carrier(nif); | 
|  | type = strtol(p, 0, 0); | 
|  | if (typeinuse(nif, type)) | 
|  | error(EBUSY, ERROR_FIXME); | 
|  | f->type = type; | 
|  | if (f->type < 0) | 
|  | nif->all++; | 
|  | } else if (matchtoken(buf, "promiscuous")) { | 
|  | if (f->prom == 0) { | 
|  | /* Note that promisc has two meanings: put the NIC into | 
|  | * promisc mode, and record our outbound traffic.  See | 
|  | * etheroq(). */ | 
|  | /* TODO: consider porting linux's interface for | 
|  | * set_rx_mode. */ | 
|  | if (nif->prom == 0 && nif->promiscuous != NULL) | 
|  | 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 = strtol(p, 0, 0); | 
|  | if (type < 5) | 
|  | type = 5; | 
|  | if (nif->scanbs != NULL) | 
|  | 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(EFAIL, "bad address"); | 
|  | p = netmulti(nif, f, binaddr, 1); | 
|  | if (p) | 
|  | error(EFAIL, p); | 
|  | } else if ((p = matchtoken(buf, "remmulti")) != 0) { | 
|  | if (parseaddr(binaddr, p, nif->alen) < 0) | 
|  | error(EFAIL, "bad address"); | 
|  | p = netmulti(nif, f, binaddr, 0); | 
|  | if (p) | 
|  | error(EFAIL, p); | 
|  | } else if (matchtoken(buf, "oneblock")) { | 
|  | /* Qmsg + Qcoal = one block at a time. */ | 
|  | q_toggle_qmsg(f->in, TRUE); | 
|  | q_toggle_qcoalesce(f->in, TRUE); | 
|  | } else | 
|  | n = -1; | 
|  | qunlock(&nif->qlock); | 
|  | poperror(); | 
|  | return n; | 
|  | } | 
|  |  | 
|  | int netifwstat(struct ether *nif, struct chan *c, uint8_t * db, int n) | 
|  | { | 
|  | struct dir *dir; | 
|  | struct netfile *f; | 
|  | int m; | 
|  |  | 
|  | f = nif->f[NETID(c->qid.path)]; | 
|  | if (f == 0) | 
|  | error(ENOENT, ERROR_FIXME); | 
|  |  | 
|  | if (netown(f, current->user.name, O_WRITE) < 0) | 
|  | error(EPERM, ERROR_FIXME); | 
|  |  | 
|  | dir = kzmalloc(sizeof(struct dir) + n, 0); | 
|  | m = convM2D(db, n, &dir[0], (char *)&dir[1]); | 
|  | if (m == 0) { | 
|  | kfree(dir); | 
|  | error(ENODATA, ERROR_FIXME); | 
|  | } | 
|  | if (!emptystr(dir[0].uid)) | 
|  | strlcpy(f->owner, dir[0].uid, KNAMELEN); | 
|  | if (dir[0].mode != -1) | 
|  | f->mode = dir[0].mode; | 
|  | kfree(dir); | 
|  | return m; | 
|  | } | 
|  |  | 
|  | int netifstat(struct ether *nif, struct chan *c, uint8_t * db, int n) | 
|  | { | 
|  | return devstat(c, db, n, (struct dirtab *)nif, 0, netifgen); | 
|  | } | 
|  |  | 
|  | void netifclose(struct ether *nif, struct chan *c) | 
|  | { | 
|  | struct netfile *f; | 
|  | int t; | 
|  | struct 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->qlock); | 
|  | if (--(f->inuse) == 0) { | 
|  | if (f->prom) { | 
|  | qlock(&nif->qlock); | 
|  | if (--(nif->prom) == 0 && nif->promiscuous != NULL) | 
|  | nif->promiscuous(nif->arg, 0); | 
|  | qunlock(&nif->qlock); | 
|  | f->prom = 0; | 
|  | } | 
|  | if (f->scan) { | 
|  | qlock(&nif->qlock); | 
|  | if (--(nif->scan) == 0 && nif->scanbs != NULL) | 
|  | nif->scanbs(nif->arg, 0); | 
|  | qunlock(&nif->qlock); | 
|  | f->prom = 0; | 
|  | f->scan = 0; | 
|  | } | 
|  | if (f->nmaddr) { | 
|  | qlock(&nif->qlock); | 
|  | 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->qlock); | 
|  | f->nmaddr = 0; | 
|  | } | 
|  | if (f->type < 0) { | 
|  | qlock(&nif->qlock); | 
|  | --(nif->all); | 
|  | qunlock(&nif->qlock); | 
|  | } | 
|  | f->owner[0] = 0; | 
|  | f->type = 0; | 
|  | f->bridge = 0; | 
|  | f->headersonly = 0; | 
|  | qclose(f->in); | 
|  | } | 
|  | qunlock(&f->qlock); | 
|  | } | 
|  |  | 
|  | spinlock_t netlock = SPINLOCK_INITIALIZER; | 
|  |  | 
|  | static int netown(struct netfile *p, char *o, int omode) | 
|  | { | 
|  | int mode; | 
|  | int rwx; | 
|  |  | 
|  | spin_lock(&netlock); | 
|  | if (*p->owner) { | 
|  | if (strncmp(o, p->owner, KNAMELEN) == 0) | 
|  | mode = p->mode; | 
|  | else if (strncmp(o, eve.name, KNAMELEN) == 0) | 
|  | mode = p->mode << 3; | 
|  | else | 
|  | mode = p->mode << 6; | 
|  |  | 
|  | rwx = omode_to_rwx(omode); | 
|  | if ((rwx & mode) == rwx) { | 
|  | spin_unlock(&netlock); | 
|  | return 0; | 
|  | } else { | 
|  | spin_unlock(&netlock); | 
|  | return -1; | 
|  | } | 
|  | } | 
|  | strlcpy(p->owner, o, KNAMELEN); | 
|  | p->mode = 0660; | 
|  | spin_unlock(&netlock); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  Increment the reference count of a network device. | 
|  | *  If id < 0, return an unused ether device. | 
|  | */ | 
|  | static int openfile(struct ether *nif, int id) | 
|  | { | 
|  | ERRSTACK(1); | 
|  | struct netfile *f, **fp, **efp; | 
|  |  | 
|  | if (id >= 0) { | 
|  | f = nif->f[id]; | 
|  | if (f == 0) | 
|  | error(ENODEV, ERROR_FIXME); | 
|  | qlock(&f->qlock); | 
|  | qreopen(f->in); | 
|  | f->inuse++; | 
|  | qunlock(&f->qlock); | 
|  | return id; | 
|  | } | 
|  |  | 
|  | qlock(&nif->qlock); | 
|  | if (waserror()) { | 
|  | qunlock(&nif->qlock); | 
|  | nexterror(); | 
|  | } | 
|  | efp = &nif->f[nif->nfile]; | 
|  | for (fp = nif->f; fp < efp; fp++) { | 
|  | f = *fp; | 
|  | if (f == 0) { | 
|  | f = kzmalloc(sizeof(struct netfile), 0); | 
|  | if (f == 0) | 
|  | exhausted("memory"); | 
|  | /* since we lock before netifinit (if we ever call | 
|  | * that...) */ | 
|  | qlock_init(&f->qlock); | 
|  | f->in = qopen(nif->limit, Qmsg, 0, 0); | 
|  | if (f->in == NULL) { | 
|  | kfree(f); | 
|  | exhausted("memory"); | 
|  | } | 
|  | *fp = f; | 
|  | qlock(&f->qlock); | 
|  | } else { | 
|  | qlock(&f->qlock); | 
|  | if (f->inuse) { | 
|  | qunlock(&f->qlock); | 
|  | continue; | 
|  | } | 
|  | } | 
|  | f->inuse = 1; | 
|  | qreopen(f->in); | 
|  | netown(f, current->user.name, 0); | 
|  | qunlock(&f->qlock); | 
|  | qunlock(&nif->qlock); | 
|  | poperror(); | 
|  | return fp - nif->f; | 
|  | } | 
|  | error(ENODEV, ERROR_FIXME); | 
|  | 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; | 
|  | } | 
|  |  | 
|  | static uint32_t hash(uint8_t * a, int len) | 
|  | { | 
|  | uint32_t sum = 0; | 
|  |  | 
|  | while (len-- > 0) | 
|  | sum = (sum << 1) + *a++; | 
|  | return sum % Nmhash; | 
|  | } | 
|  |  | 
|  | int activemulti(struct ether *nif, uint8_t * addr, int alen) | 
|  | { | 
|  | struct 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(uint8_t *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(struct ether *nif, struct netfile *f, uint8_t *addr, | 
|  | int add) | 
|  | { | 
|  | struct netaddr **l, *ap; | 
|  | int i; | 
|  | uint32_t h; | 
|  |  | 
|  | if (nif->multicast == NULL) | 
|  | 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) { | 
|  | /* TODO: AFAIK, this never gets freed.  if we fix that, | 
|  | * we can use a kref too (instead of int ap->ref). */ | 
|  | *l = ap = kzmalloc(sizeof(*ap), 0); | 
|  | 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; | 
|  | } | 
|  |  | 
|  | /* Prints the contents of stats to [va + offset, va + offset + amt). */ | 
|  | ssize_t linux_ifstat(struct netif_stats *stats, void *va, size_t amt, | 
|  | off_t offset) | 
|  | { | 
|  | char *p; | 
|  | size_t sofar = 0; | 
|  | ssize_t ret; | 
|  |  | 
|  | p = kzmalloc(READSTR, MEM_WAIT); | 
|  | sofar += snprintf(p + sofar, READSTR - sofar, | 
|  | "rx pkts            : %lu\n", stats->rx_packets); | 
|  | sofar += snprintf(p + sofar, READSTR - sofar, | 
|  | "tx pkts            : %lu\n", stats->tx_packets); | 
|  | sofar += snprintf(p + sofar, READSTR - sofar, | 
|  | "rx bytes           : %lu\n", stats->rx_bytes); | 
|  | sofar += snprintf(p + sofar, READSTR - sofar, | 
|  | "tx bytes           : %lu\n", stats->tx_bytes); | 
|  | sofar += snprintf(p + sofar, READSTR - sofar, | 
|  | "rx errors          : %lu\n", stats->rx_errors); | 
|  | sofar += snprintf(p + sofar, READSTR - sofar, | 
|  | "tx errors          : %lu\n", stats->tx_errors); | 
|  | sofar += snprintf(p + sofar, READSTR - sofar, | 
|  | "rx dropped         : %lu\n", stats->rx_dropped); | 
|  | sofar += snprintf(p + sofar, READSTR - sofar, | 
|  | "tx dropped         : %lu\n", stats->tx_dropped); | 
|  | sofar += snprintf(p + sofar, READSTR - sofar, | 
|  | "multicast          : %lu\n", stats->multicast); | 
|  | sofar += snprintf(p + sofar, READSTR - sofar, | 
|  | "collisions         : %lu\n", stats->collisions); | 
|  | sofar += snprintf(p + sofar, READSTR - sofar, "\n"); | 
|  |  | 
|  | sofar += snprintf(p + sofar, READSTR - sofar, | 
|  | "rx length errors   : %lu\n", | 
|  | stats->rx_length_errors); | 
|  | sofar += snprintf(p + sofar, READSTR - sofar, | 
|  | "rx over errors     : %lu\n", stats->rx_over_errors); | 
|  | sofar += snprintf(p + sofar, READSTR - sofar, | 
|  | "rx crc errors      : %lu\n", stats->rx_crc_errors); | 
|  | sofar += snprintf(p + sofar, READSTR - sofar, | 
|  | "rx frame errors    : %lu\n", stats->rx_frame_errors); | 
|  | sofar += snprintf(p + sofar, READSTR - sofar, | 
|  | "rx fifo errors     : %lu\n", stats->rx_fifo_errors); | 
|  | sofar += snprintf(p + sofar, READSTR - sofar, | 
|  | "rx missed errors   : %lu\n", | 
|  | stats->rx_missed_errors); | 
|  | sofar += snprintf(p + sofar, READSTR - sofar, "\n"); | 
|  |  | 
|  | sofar += snprintf(p + sofar, READSTR - sofar, | 
|  | "tx aborted errors  : %lu\n", | 
|  | stats->tx_aborted_errors); | 
|  | sofar += snprintf(p + sofar, READSTR - sofar, | 
|  | "tx carrier errors  : %lu\n", | 
|  | stats->tx_carrier_errors); | 
|  | sofar += snprintf(p + sofar, READSTR - sofar, | 
|  | "tx fifo errors     : %lu\n", stats->tx_fifo_errors); | 
|  | sofar += snprintf(p + sofar, READSTR - sofar, | 
|  | "tx heartbeat errors: %lu\n", | 
|  | stats->tx_heartbeat_errors); | 
|  | sofar += snprintf(p + sofar, READSTR - sofar, | 
|  | "tx window errors   : %lu\n", | 
|  | stats->tx_window_errors); | 
|  | sofar += snprintf(p + sofar, READSTR - sofar, "\n"); | 
|  |  | 
|  | sofar += snprintf(p + sofar, READSTR - sofar, | 
|  | "rx compressed      : %lu\n", stats->rx_compressed); | 
|  | sofar += snprintf(p + sofar, READSTR - sofar, | 
|  | "tx compressed      : %lu\n", stats->tx_compressed); | 
|  | sofar += snprintf(p + sofar, READSTR - sofar, | 
|  | "rx nohandler       : %lu\n", stats->rx_nohandler); | 
|  | ret = readstr(offset, va, amt, p); | 
|  | kfree(p); | 
|  | return ret; | 
|  | } |