| /* 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; | 
 | } |