| #include <vfs.h> | 
 | #include <kfs.h> | 
 | #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> | 
 |  | 
 | enum { | 
 | 	Qtopdir = 1,				/* top level directory */ | 
 | 	Qtopbase, | 
 | 	Qarp = Qtopbase, | 
 | 	Qndb, | 
 | 	Qiproute, | 
 | 	Qipselftab, | 
 | 	Qlog, | 
 |  | 
 | 	Qprotodir,	/* directory for a protocol */ | 
 | 	Qprotobase, | 
 | 	Qclone = Qprotobase, | 
 | 	Qstats, | 
 |  | 
 | 	Qconvdir,	/* directory for a conversation */ | 
 | 	Qconvbase, | 
 | 	Qctl = Qconvbase, | 
 | 	Qdata, | 
 | 	Qerr, | 
 | 	Qlisten, | 
 | 	Qlocal, | 
 | 	Qremote, | 
 | 	Qstatus, | 
 | 	Qsnoop, | 
 |  | 
 | 	Logtype = 5, | 
 | 	Masktype = (1 << Logtype) - 1, | 
 | 	Logconv = 12, | 
 | 	Maskconv = (1 << Logconv) - 1, | 
 | 	Shiftconv = Logtype, | 
 | 	Logproto = 8, | 
 | 	Maskproto = (1 << Logproto) - 1, | 
 | 	Shiftproto = Logtype + Logconv, | 
 |  | 
 | 	Nfs = 128, | 
 | }; | 
 | #define TYPE(x)		( ((uint32_t)(x).path) & Masktype ) | 
 | #define CONV(x)		( (((uint32_t)(x).path) >> Shiftconv) & Maskconv ) | 
 | #define PROTO(x)	( (((uint32_t)(x).path) >> Shiftproto) & Maskproto ) | 
 | #define QID(p, c, y)	( ((p)<<(Shiftproto)) | ((c)<<Shiftconv) | (y) ) | 
 |  | 
 | static char network[] = "network"; | 
 |  | 
 | qlock_t fslock; | 
 | struct fs *ipfs[Nfs];			/* attached fs's */ | 
 | struct queue *qlog; | 
 |  | 
 | extern void nullmediumlink(void); | 
 | extern void pktmediumlink(void); | 
 | long ndbwrite(struct fs *f, char *a, uint32_t off, int n); | 
 |  | 
 | static int ip3gen(struct chan *c, int i, struct dir *dp) | 
 | { | 
 | 	struct qid q; | 
 | 	struct conv *cv; | 
 | 	char *p; | 
 |  | 
 | 	cv = ipfs[c->devno]->p[PROTO(c->qid)]->conv[CONV(c->qid)]; | 
 | 	if (cv->owner == NULL) | 
 | 		kstrdup(&cv->owner, eve); | 
 | 	mkqid(&q, QID(PROTO(c->qid), CONV(c->qid), i), 0, QTFILE); | 
 |  | 
 | 	switch (i) { | 
 | 		default: | 
 | 			return -1; | 
 | 		case Qctl: | 
 | 			devdir(c, q, "ctl", 0, cv->owner, cv->perm, dp); | 
 | 			return 1; | 
 | 		case Qdata: | 
 | 			devdir(c, q, "data", qlen(cv->rq), cv->owner, cv->perm, dp); | 
 | 			return 1; | 
 | 		case Qerr: | 
 | 			devdir(c, q, "err", qlen(cv->eq), cv->owner, cv->perm, dp); | 
 | 			return 1; | 
 | 		case Qlisten: | 
 | 			devdir(c, q, "listen", 0, cv->owner, cv->perm, dp); | 
 | 			return 1; | 
 | 		case Qlocal: | 
 | 			p = "local"; | 
 | 			break; | 
 | 		case Qremote: | 
 | 			p = "remote"; | 
 | 			break; | 
 | 		case Qsnoop: | 
 | 			if (strcmp(cv->p->name, "ipifc") != 0) | 
 | 				return -1; | 
 | 			devdir(c, q, "snoop", qlen(cv->sq), cv->owner, 0400, dp); | 
 | 			return 1; | 
 | 		case Qstatus: | 
 | 			p = "status"; | 
 | 			break; | 
 | 	} | 
 | 	devdir(c, q, p, 0, cv->owner, 0444, dp); | 
 | 	return 1; | 
 | } | 
 |  | 
 | static int ip2gen(struct chan *c, int i, struct dir *dp) | 
 | { | 
 | 	struct qid q; | 
 |  | 
 | 	switch (i) { | 
 | 		case Qclone: | 
 | 			mkqid(&q, QID(PROTO(c->qid), 0, Qclone), 0, QTFILE); | 
 | 			devdir(c, q, "clone", 0, network, 0666, dp); | 
 | 			return 1; | 
 | 		case Qstats: | 
 | 			mkqid(&q, QID(PROTO(c->qid), 0, Qstats), 0, QTFILE); | 
 | 			devdir(c, q, "stats", 0, network, 0444, dp); | 
 | 			return 1; | 
 | 	} | 
 | 	return -1; | 
 | } | 
 |  | 
 | static int ip1gen(struct chan *c, int i, struct dir *dp) | 
 | { | 
 | 	struct qid q; | 
 | 	char *p; | 
 | 	int prot; | 
 | 	int len = 0; | 
 | 	struct fs *f; | 
 | 	extern uint32_t kerndate; | 
 |  | 
 | 	f = ipfs[c->devno]; | 
 |  | 
 | 	prot = 0666; | 
 | 	mkqid(&q, QID(0, 0, i), 0, QTFILE); | 
 | 	switch (i) { | 
 | 		default: | 
 | 			return -1; | 
 | 		case Qarp: | 
 | 			p = "arp"; | 
 | 			prot = 0664; | 
 | 			break; | 
 | 		case Qndb: | 
 | 			p = "ndb"; | 
 | 			len = strlen(f->ndb); | 
 | 			q.vers = f->ndbvers; | 
 | 			break; | 
 | 		case Qiproute: | 
 | 			p = "iproute"; | 
 | 			prot = 0664; | 
 | 			break; | 
 | 		case Qipselftab: | 
 | 			p = "ipselftab"; | 
 | 			prot = 0444; | 
 | 			break; | 
 | 		case Qlog: | 
 | 			p = "log"; | 
 | 			break; | 
 | 	} | 
 | 	devdir(c, q, p, len, network, prot, dp); | 
 | 	if (i == Qndb && f->ndbmtime > kerndate) | 
 | 		dp->mtime = f->ndbmtime; | 
 | 	return 1; | 
 | } | 
 |  | 
 | static int | 
 | ipgen(struct chan *c, char *unused_char_p_t, struct dirtab *unused_dirtab, | 
 | 	  int unused_int, int s, struct dir *dp) | 
 | { | 
 | 	struct qid q; | 
 | 	struct conv *cv; | 
 | 	struct fs *f; | 
 |  | 
 | 	f = ipfs[c->devno]; | 
 |  | 
 | 	switch (TYPE(c->qid)) { | 
 | 		case Qtopdir: | 
 | 			if (s == DEVDOTDOT) { | 
 | 				mkqid(&q, QID(0, 0, Qtopdir), 0, QTDIR); | 
 | 				snprintf(get_cur_genbuf(), GENBUF_SZ, "#I%u", c->devno); | 
 | 				devdir(c, q, get_cur_genbuf(), 0, network, 0555, dp); | 
 | 				return 1; | 
 | 			} | 
 | 			if (s < f->np) { | 
 | 				if (f->p[s]->connect == NULL) | 
 | 					return 0;	/* protocol with no user interface */ | 
 | 				mkqid(&q, QID(s, 0, Qprotodir), 0, QTDIR); | 
 | 				devdir(c, q, f->p[s]->name, 0, network, 0555, dp); | 
 | 				return 1; | 
 | 			} | 
 | 			s -= f->np; | 
 | 			return ip1gen(c, s + Qtopbase, dp); | 
 | 		case Qarp: | 
 | 		case Qndb: | 
 | 		case Qlog: | 
 | 		case Qiproute: | 
 | 		case Qipselftab: | 
 | 			return ip1gen(c, TYPE(c->qid), dp); | 
 | 		case Qprotodir: | 
 | 			if (s == DEVDOTDOT) { | 
 | 				mkqid(&q, QID(0, 0, Qtopdir), 0, QTDIR); | 
 | 				snprintf(get_cur_genbuf(), GENBUF_SZ, "#I%u", c->devno); | 
 | 				devdir(c, q, get_cur_genbuf(), 0, network, 0555, dp); | 
 | 				return 1; | 
 | 			} | 
 | 			if (s < f->p[PROTO(c->qid)]->ac) { | 
 | 				cv = f->p[PROTO(c->qid)]->conv[s]; | 
 | 				snprintf(get_cur_genbuf(), GENBUF_SZ, "%d", s); | 
 | 				mkqid(&q, QID(PROTO(c->qid), s, Qconvdir), 0, QTDIR); | 
 | 				devdir(c, q, get_cur_genbuf(), 0, cv->owner, 0555, dp); | 
 | 				return 1; | 
 | 			} | 
 | 			s -= f->p[PROTO(c->qid)]->ac; | 
 | 			return ip2gen(c, s + Qprotobase, dp); | 
 | 		case Qclone: | 
 | 		case Qstats: | 
 | 			return ip2gen(c, TYPE(c->qid), dp); | 
 | 		case Qconvdir: | 
 | 			if (s == DEVDOTDOT) { | 
 | 				s = PROTO(c->qid); | 
 | 				mkqid(&q, QID(s, 0, Qprotodir), 0, QTDIR); | 
 | 				devdir(c, q, f->p[s]->name, 0, network, 0555, dp); | 
 | 				return 1; | 
 | 			} | 
 | 			return ip3gen(c, s + Qconvbase, dp); | 
 | 		case Qctl: | 
 | 		case Qdata: | 
 | 		case Qerr: | 
 | 		case Qlisten: | 
 | 		case Qlocal: | 
 | 		case Qremote: | 
 | 		case Qstatus: | 
 | 		case Qsnoop: | 
 | 			return ip3gen(c, TYPE(c->qid), dp); | 
 | 	} | 
 | 	return -1; | 
 | } | 
 |  | 
 | static void ipreset(void) | 
 | { | 
 | 	nullmediumlink(); | 
 | 	pktmediumlink(); | 
 |  | 
 | /* | 
 | 	fmtinstall('i', eipfmt); | 
 | 	fmtinstall('I', eipfmt); | 
 | 	fmtinstall('E', eipfmt); | 
 | 	fmtinstall('V', eipfmt); | 
 | 	fmtinstall('M', eipfmt); | 
 | */ | 
 | } | 
 |  | 
 | static struct fs *ipgetfs(int dev) | 
 | { | 
 | 	extern void (*ipprotoinit[]) (struct fs *); | 
 | 	struct fs *f; | 
 | 	int i; | 
 |  | 
 | 	if (dev >= Nfs) | 
 | 		return NULL; | 
 |  | 
 | 	qlock(&fslock); | 
 | 	if (ipfs[dev] == NULL) { | 
 | 		f = kzmalloc(sizeof(struct fs), 0); | 
 | 		ip_init(f); | 
 | 		arpinit(f); | 
 | 		netloginit(f); | 
 | 		for (i = 0; ipprotoinit[i]; i++) | 
 | 			ipprotoinit[i] (f); | 
 | 		f->dev = dev; | 
 | 		ipfs[dev] = f; | 
 | 	} | 
 | 	qunlock(&fslock); | 
 |  | 
 | 	return ipfs[dev]; | 
 | } | 
 |  | 
 | struct IPaux *newipaux(char *owner, char *tag) | 
 | { | 
 | 	struct IPaux *a; | 
 | 	int n; | 
 |  | 
 | 	a = kzmalloc(sizeof(*a), 0); | 
 | 	kstrdup(&a->owner, owner); | 
 | 	memset(a->tag, ' ', sizeof(a->tag)); | 
 | 	n = strlen(tag); | 
 | 	if (n > sizeof(a->tag)) | 
 | 		n = sizeof(a->tag); | 
 | 	memmove(a->tag, tag, n); | 
 | 	return a; | 
 | } | 
 |  | 
 | #define ATTACHER(c) (((struct IPaux*)((c)->aux))->owner) | 
 |  | 
 | static struct chan *ipattach(char *spec) | 
 | { | 
 | 	struct chan *c; | 
 | 	int devno; | 
 |  | 
 | 	devno = atoi(spec); | 
 | 	if (devno >= Nfs) | 
 | 		error("bad specification"); | 
 |  | 
 | 	ipgetfs(devno); | 
 | 	c = devattach('I', spec); | 
 | 	mkqid(&c->qid, QID(0, 0, Qtopdir), 0, QTDIR); | 
 | 	c->devno = devno; | 
 |  | 
 | 	c->aux = newipaux(current->user, "none"); | 
 |  | 
 | 	return c; | 
 | } | 
 |  | 
 | static struct walkqid *ipwalk(struct chan *c, struct chan *nc, char **name, | 
 | 							  int nname) | 
 | { | 
 | 	struct IPaux *a = c->aux; | 
 | 	struct walkqid *w; | 
 |  | 
 | 	w = devwalk(c, nc, name, nname, NULL, 0, ipgen); | 
 | 	if (w != NULL && w->clone != NULL) | 
 | 		w->clone->aux = newipaux(a->owner, a->tag); | 
 | 	return w; | 
 | } | 
 |  | 
 | static long ipstat(struct chan *c, uint8_t * db, long n) | 
 | { | 
 | 	return devstat(c, db, n, NULL, 0, ipgen); | 
 | } | 
 |  | 
 | static int incoming(void *arg) | 
 | { | 
 | 	struct conv *conv; | 
 |  | 
 | 	conv = arg; | 
 | 	return conv->incall != NULL; | 
 | } | 
 |  | 
 | static int m2p[] = { | 
 | 	[OREAD] 4, | 
 | 	[OWRITE] 2, | 
 | 	[ORDWR] 6 | 
 | }; | 
 |  | 
 | static struct chan *ipopen(struct chan *c, int omode) | 
 | { | 
 | 	ERRSTACK(2); | 
 | 	struct conv *cv, *nc; | 
 | 	struct proto *p; | 
 | 	int perm; | 
 | 	struct fs *f; | 
 |  | 
 | 	perm = m2p[omode & 3]; | 
 |  | 
 | 	f = ipfs[c->devno]; | 
 |  | 
 | 	switch (TYPE(c->qid)) { | 
 | 		default: | 
 | 			break; | 
 | 		case Qndb: | 
 | 			if (omode & (OWRITE | OTRUNC) && !iseve()) | 
 | 				error(Eperm); | 
 | 			if ((omode & (OWRITE | OTRUNC)) == (OWRITE | OTRUNC)) | 
 | 				f->ndb[0] = 0; | 
 | 			break; | 
 | 		case Qlog: | 
 | 			netlogopen(f); | 
 | 			break; | 
 | 		case Qiproute: | 
 | 		case Qarp: | 
 | 			if (omode != OREAD && !iseve()) | 
 | 				error(Eperm); | 
 | 			break; | 
 | 		case Qtopdir: | 
 | 		case Qprotodir: | 
 | 		case Qconvdir: | 
 | 		case Qstatus: | 
 | 		case Qremote: | 
 | 		case Qlocal: | 
 | 		case Qstats: | 
 | 		case Qipselftab: | 
 | 			if (omode != OREAD) | 
 | 				error(Eperm); | 
 | 			break; | 
 | 		case Qsnoop: | 
 | 			if (omode != OREAD) | 
 | 				error(Eperm); | 
 | 			p = f->p[PROTO(c->qid)]; | 
 | 			cv = p->conv[CONV(c->qid)]; | 
 | 			if (strcmp(ATTACHER(c), cv->owner) != 0 && !iseve()) | 
 | 				error(Eperm); | 
 | 			kref_get(&cv->snoopers, 1); | 
 | 			break; | 
 | 		case Qclone: | 
 | 			p = f->p[PROTO(c->qid)]; | 
 | 			qlock(&p->qlock); | 
 | 			if (waserror()) { | 
 | 				qunlock(&p->qlock); | 
 | 				nexterror(); | 
 | 			} | 
 | 			cv = Fsprotoclone(p, ATTACHER(c)); | 
 | 			qunlock(&p->qlock); | 
 | 			poperror(); | 
 | 			if (cv == NULL) { | 
 | 				error(Enodev); | 
 | 				break; | 
 | 			} | 
 | 			mkqid(&c->qid, QID(p->x, cv->x, Qctl), 0, QTFILE); | 
 | 			break; | 
 | 		case Qdata: | 
 | 		case Qctl: | 
 | 		case Qerr: | 
 | 			p = f->p[PROTO(c->qid)]; | 
 | 			qlock(&p->qlock); | 
 | 			cv = p->conv[CONV(c->qid)]; | 
 | 			qlock(&cv->qlock); | 
 | 			if (waserror()) { | 
 | 				qunlock(&cv->qlock); | 
 | 				qunlock(&p->qlock); | 
 | 				nexterror(); | 
 | 			} | 
 | 			if ((perm & (cv->perm >> 6)) != perm) { | 
 | 				if (strcmp(ATTACHER(c), cv->owner) != 0) | 
 | 					error(Eperm); | 
 | 				if ((perm & cv->perm) != perm) | 
 | 					error(Eperm); | 
 |  | 
 | 			} | 
 | 			cv->inuse++; | 
 | 			/* this may seem a little odd, since a conv's refcnt is 1 when you | 
 | 			 * create it (Fsprotoclone).  it is possible you clone, then close, | 
 | 			 * then open again.  consider that clone (opening proto/clone) is | 
 | 			 * equivalent to opening proto/conv/ctl (as far as results go), so | 
 | 			 * both should set the refcnt to 1. */ | 
 | 			if (cv->inuse == 1) { | 
 | 				kstrdup(&cv->owner, ATTACHER(c)); | 
 | 				cv->perm = 0660; | 
 | 			} | 
 | 			qunlock(&cv->qlock); | 
 | 			qunlock(&p->qlock); | 
 | 			poperror(); | 
 | 			break; | 
 | 		case Qlisten: | 
 | 			cv = f->p[PROTO(c->qid)]->conv[CONV(c->qid)]; | 
 | 			if ((perm & (cv->perm >> 6)) != perm) { | 
 | 				if (strcmp(ATTACHER(c), cv->owner) != 0) | 
 | 					error(Eperm); | 
 | 				if ((perm & cv->perm) != perm) | 
 | 					error(Eperm); | 
 |  | 
 | 			} | 
 |  | 
 | 			if (cv->state != Announced) | 
 | 				error("not announced"); | 
 |  | 
 | 			if (waserror()) { | 
 | 				closeconv(cv); | 
 | 				nexterror(); | 
 | 			} | 
 | 			qlock(&cv->qlock); | 
 | 			cv->inuse++; | 
 | 			qunlock(&cv->qlock); | 
 |  | 
 | 			nc = NULL; | 
 | 			while (nc == NULL) { | 
 | 				/* give up if we got a hangup */ | 
 | 				if (qisclosed(cv->rq)) | 
 | 					error("listen hungup"); | 
 |  | 
 | 				qlock(&cv->listenq); | 
 | 				if (waserror()) { | 
 | 					qunlock(&cv->listenq); | 
 | 					nexterror(); | 
 | 				} | 
 |  | 
 | 				/* wait for a connect */ | 
 | 				rendez_sleep(&cv->listenr, incoming, cv); | 
 |  | 
 | 				qlock(&cv->qlock); | 
 | 				nc = cv->incall; | 
 | 				if (nc != NULL) { | 
 | 					cv->incall = nc->next; | 
 | 					mkqid(&c->qid, QID(PROTO(c->qid), nc->x, Qctl), 0, QTFILE); | 
 | 					kstrdup(&cv->owner, ATTACHER(c)); | 
 | 				} | 
 | 				qunlock(&cv->qlock); | 
 |  | 
 | 				qunlock(&cv->listenq); | 
 | 				poperror(); | 
 | 			} | 
 | 			closeconv(cv); | 
 | 			poperror(); | 
 | 			break; | 
 | 	} | 
 | 	c->mode = openmode(omode); | 
 | 	c->flag |= COPEN; | 
 | 	c->offset = 0; | 
 | 	return c; | 
 | } | 
 |  | 
 | static void | 
 | ipcreate(struct chan *c, char *unused_char_p_t, int unused_int, int unused_int2) | 
 | { | 
 | 	error(Eperm); | 
 | } | 
 |  | 
 | static void ipremove(struct chan *c) | 
 | { | 
 | 	error(Eperm); | 
 | } | 
 |  | 
 | static long ipwstat(struct chan *c, uint8_t * dp, long n) | 
 | { | 
 | 	struct dir d; | 
 | 	struct conv *cv; | 
 | 	struct fs *f; | 
 | 	struct proto *p; | 
 |  | 
 | 	f = ipfs[c->devno]; | 
 | 	switch (TYPE(c->qid)) { | 
 | 		default: | 
 | 			error(Eperm); | 
 | 			break; | 
 | 		case Qctl: | 
 | 		case Qdata: | 
 | 			break; | 
 | 	} | 
 |  | 
 | 	n = convM2D(dp, n, &d, NULL); | 
 | 	if (n > 0) { | 
 | 		p = f->p[PROTO(c->qid)]; | 
 | 		cv = p->conv[CONV(c->qid)]; | 
 | 		if (!iseve() && strcmp(ATTACHER(c), cv->owner) != 0) | 
 | 			error(Eperm); | 
 | 		if (d.uid[0]) | 
 | 			kstrdup(&cv->owner, d.uid); | 
 | 		cv->perm = d.mode & 0777; | 
 | 	} | 
 | 	return n; | 
 | } | 
 |  | 
 | /* Should be able to handle any file type chan.  Feel free to extend it. */ | 
 | static char *ipchaninfo(struct chan *ch, char *ret, size_t ret_l) | 
 | { | 
 | 	struct conv *conv; | 
 | 	struct proto *proto; | 
 | 	char *p; | 
 | 	struct fs *f; | 
 |  | 
 | 	f = ipfs[ch->devno]; | 
 |  | 
 | 	switch (TYPE(ch->qid)) { | 
 | 		default: | 
 | 			ret = "Unknown type"; | 
 | 			break; | 
 | 		case Qdata: | 
 | 			proto = f->p[PROTO(ch->qid)]; | 
 | 			conv = proto->conv[CONV(ch->qid)]; | 
 | 			snprintf(ret, ret_l, "Qdata, proto %s, conv idx %d", proto->name, | 
 | 			         conv->x); | 
 | 			break; | 
 | 		case Qarp: | 
 | 			ret = "Qarp"; | 
 | 			break; | 
 | 		case Qiproute: | 
 | 			ret = "Qiproute"; | 
 | 			break; | 
 | 		case Qlog: | 
 | 			ret = "Qlog"; | 
 | 			break; | 
 | 		case Qndb: | 
 | 			ret = "Qndb"; | 
 | 			break; | 
 | 		case Qctl: | 
 | 			proto = f->p[PROTO(ch->qid)]; | 
 | 			conv = proto->conv[CONV(ch->qid)]; | 
 | 			snprintf(ret, ret_l, "Qctl, proto %s, conv idx %d", proto->name, | 
 | 			         conv->x); | 
 | 			break; | 
 | 	} | 
 | 	return ret; | 
 | } | 
 |  | 
 | void closeconv(struct conv *cv) | 
 | { | 
 | 	struct conv *nc; | 
 | 	struct Ipmulti *mp; | 
 |  | 
 | 	qlock(&cv->qlock); | 
 |  | 
 | 	if (--cv->inuse > 0) { | 
 | 		qunlock(&cv->qlock); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	/* close all incoming calls since no listen will ever happen */ | 
 | 	for (nc = cv->incall; nc; nc = cv->incall) { | 
 | 		cv->incall = nc->next; | 
 | 		closeconv(nc); | 
 | 	} | 
 | 	cv->incall = NULL; | 
 |  | 
 | 	kstrdup(&cv->owner, network); | 
 | 	cv->perm = 0660; | 
 |  | 
 | 	while ((mp = cv->multi) != NULL) | 
 | 		ipifcremmulti(cv, mp->ma, mp->ia); | 
 |  | 
 | 	cv->r = NULL; | 
 | 	cv->rgen = 0; | 
 | 	cv->p->close(cv); | 
 | 	cv->state = Idle; | 
 | 	qunlock(&cv->qlock); | 
 | } | 
 |  | 
 | static void ipclose(struct chan *c) | 
 | { | 
 | 	struct fs *f; | 
 |  | 
 | 	f = ipfs[c->devno]; | 
 | 	switch (TYPE(c->qid)) { | 
 | 		default: | 
 | 			break; | 
 | 		case Qlog: | 
 | 			if (c->flag & COPEN) | 
 | 				netlogclose(f); | 
 | 			break; | 
 | 		case Qdata: | 
 | 		case Qctl: | 
 | 		case Qerr: | 
 | 			if (c->flag & COPEN) | 
 | 				closeconv(f->p[PROTO(c->qid)]->conv[CONV(c->qid)]); | 
 | 			break; | 
 | 		case Qsnoop: | 
 | 			if (c->flag & COPEN) | 
 | 				kref_put(&f->p[PROTO(c->qid)]->conv[CONV(c->qid)]->snoopers); | 
 | 			break; | 
 | 	} | 
 | 	kfree(((struct IPaux *)c->aux)->owner); | 
 | 	kfree(c->aux); | 
 | } | 
 |  | 
 | enum { | 
 | 	Statelen = 32 * 1024, | 
 | }; | 
 |  | 
 | static long ipread(struct chan *ch, void *a, long n, int64_t off) | 
 | { | 
 | 	struct conv *c; | 
 | 	struct proto *x; | 
 | 	char *buf, *p; | 
 | 	long rv; | 
 | 	struct fs *f; | 
 | 	uint32_t offset = off; | 
 |  | 
 | 	f = ipfs[ch->devno]; | 
 |  | 
 | 	p = a; | 
 | 	switch (TYPE(ch->qid)) { | 
 | 		default: | 
 | 			error(Eperm); | 
 | 		case Qtopdir: | 
 | 		case Qprotodir: | 
 | 		case Qconvdir: | 
 | 			return devdirread(ch, a, n, 0, 0, ipgen); | 
 | 		case Qarp: | 
 | 			return arpread(f->arp, a, offset, n); | 
 | 		case Qndb: | 
 | 			return readstr(offset, a, n, f->ndb); | 
 | 		case Qiproute: | 
 | 			return routeread(f, a, offset, n); | 
 | 		case Qipselftab: | 
 | 			return ipselftabread(f, a, offset, n); | 
 | 		case Qlog: | 
 | 			return netlogread(f, a, offset, n); | 
 | 		case Qctl: | 
 | 			buf = kzmalloc(16, 0); | 
 | 			snprintf(buf, 16, "%lu", CONV(ch->qid)); | 
 | 			rv = readstr(offset, p, n, buf); | 
 | 			kfree(buf); | 
 | 			return rv; | 
 | 		case Qremote: | 
 | 			buf = kzmalloc(Statelen, 0); | 
 | 			x = f->p[PROTO(ch->qid)]; | 
 | 			c = x->conv[CONV(ch->qid)]; | 
 | 			if (x->remote == NULL) { | 
 | 				snprintf(buf, Statelen, "%I!%d\n", c->raddr, c->rport); | 
 | 			} else { | 
 | 				(*x->remote) (c, buf, Statelen - 2); | 
 | 			} | 
 | 			rv = readstr(offset, p, n, buf); | 
 | 			kfree(buf); | 
 | 			return rv; | 
 | 		case Qlocal: | 
 | 			buf = kzmalloc(Statelen, 0); | 
 | 			x = f->p[PROTO(ch->qid)]; | 
 | 			c = x->conv[CONV(ch->qid)]; | 
 | 			if (x->local == NULL) { | 
 | 				snprintf(buf, Statelen, "%I!%d\n", c->laddr, c->lport); | 
 | 			} else { | 
 | 				(*x->local) (c, buf, Statelen - 2); | 
 | 			} | 
 | 			rv = readstr(offset, p, n, buf); | 
 | 			kfree(buf); | 
 | 			return rv; | 
 | 		case Qstatus: | 
 | 			buf = kzmalloc(Statelen, 0); | 
 | 			x = f->p[PROTO(ch->qid)]; | 
 | 			c = x->conv[CONV(ch->qid)]; | 
 | 			(*x->state) (c, buf, Statelen - 2); | 
 | 			rv = readstr(offset, p, n, buf); | 
 | 			kfree(buf); | 
 | 			return rv; | 
 | 		case Qdata: | 
 | 			c = f->p[PROTO(ch->qid)]->conv[CONV(ch->qid)]; | 
 | 			return qread(c->rq, a, n); | 
 | 		case Qerr: | 
 | 			c = f->p[PROTO(ch->qid)]->conv[CONV(ch->qid)]; | 
 | 			return qread(c->eq, a, n); | 
 | 		case Qsnoop: | 
 | 			c = f->p[PROTO(ch->qid)]->conv[CONV(ch->qid)]; | 
 | 			return qread(c->sq, a, n); | 
 | 		case Qstats: | 
 | 			x = f->p[PROTO(ch->qid)]; | 
 | 			if (x->stats == NULL) | 
 | 				error("stats not implemented"); | 
 | 			buf = kzmalloc(Statelen, 0); | 
 | 			(*x->stats) (x, buf, Statelen); | 
 | 			rv = readstr(offset, p, n, buf); | 
 | 			kfree(buf); | 
 | 			return rv; | 
 | 	} | 
 | } | 
 |  | 
 | static struct block *ipbread(struct chan *ch, long n, int64_t offset) | 
 | { | 
 | 	struct conv *c; | 
 | 	struct proto *x; | 
 | 	struct fs *f; | 
 |  | 
 | 	switch (TYPE(ch->qid)) { | 
 | 		case Qdata: | 
 | 			f = ipfs[ch->devno]; | 
 | 			x = f->p[PROTO(ch->qid)]; | 
 | 			c = x->conv[CONV(ch->qid)]; | 
 | 			return qbread(c->rq, n); | 
 | 		default: | 
 | 			return devbread(ch, n, offset); | 
 | 	} | 
 | } | 
 |  | 
 | /* | 
 |  *  set local address to be that of the ifc closest to remote address | 
 |  */ | 
 | static void setladdr(struct conv *c) | 
 | { | 
 | 	findlocalip(c->p->f, c->laddr, c->raddr); | 
 | } | 
 |  | 
 | /* | 
 |  *  set a local port making sure the quad of raddr,rport,laddr,lport is unique | 
 |  */ | 
 | char *setluniqueport(struct conv *c, int lport) | 
 | { | 
 | 	struct proto *p; | 
 | 	struct conv *xp; | 
 | 	int x; | 
 |  | 
 | 	p = c->p; | 
 |  | 
 | 	qlock(&p->qlock); | 
 | 	for (x = 0; x < p->nc; x++) { | 
 | 		xp = p->conv[x]; | 
 | 		if (xp == NULL) | 
 | 			break; | 
 | 		if (xp == c) | 
 | 			continue; | 
 | 		if ((xp->state == Connected || xp->state == Announced) | 
 | 			&& xp->lport == lport | 
 | 			&& xp->rport == c->rport | 
 | 			&& ipcmp(xp->raddr, c->raddr) == 0 | 
 | 			&& ipcmp(xp->laddr, c->laddr) == 0) { | 
 | 			qunlock(&p->qlock); | 
 | 			return "address in use"; | 
 | 		} | 
 | 	} | 
 | 	c->lport = lport; | 
 | 	qunlock(&p->qlock); | 
 | 	return NULL; | 
 | } | 
 |  | 
 | /* | 
 |  * is lport in use by anyone? | 
 |  */ | 
 | static int lportinuse(struct proto *p, uint16_t lport) | 
 | { | 
 | 	int x; | 
 |  | 
 | 	for (x = 0; x < p->nc && p->conv[x]; x++) | 
 | 		if (p->conv[x]->lport == lport) | 
 | 			return 1; | 
 | 	return 0; | 
 | } | 
 |  | 
 | /* | 
 |  *  pick a local port and set it | 
 |  */ | 
 | char *setlport(struct conv *c) | 
 | { | 
 | 	struct proto *p; | 
 | 	int i, port; | 
 |  | 
 | 	p = c->p; | 
 | 	qlock(&p->qlock); | 
 | 	if (c->restricted) { | 
 | 		/* Restricted ports cycle between 600 and 1024. */ | 
 | 		for (i = 0; i < 1024 - 600; i++) { | 
 | 			if (p->nextrport >= 1024 || p->nextrport < 600) | 
 | 				p->nextrport = 600; | 
 | 			port = p->nextrport++; | 
 | 			if (!lportinuse(p, port)) | 
 | 				goto chosen; | 
 | 		} | 
 | 	} else { | 
 | 		/* | 
 | 		 * Unrestricted ports are chosen randomly | 
 | 		 * between 2^15 and 2^16.  There are at most | 
 | 		 * 4*Nchan = 4096 ports in use at any given time, | 
 | 		 * so even in the worst case, a random probe has a | 
 | 		 * 1 - 4096/2^15 = 87% chance of success. | 
 | 		 * If 64 successive probes fail, there is a bug somewhere | 
 | 		 * (or a once in 10^58 event has happened, but that's | 
 | 		 * less likely than a venti collision). | 
 | 		 */ | 
 | 		for (i = 0; i < 64; i++) { | 
 | 			port = (1 << 15) + nrand(1 << 15); | 
 | 			if (!lportinuse(p, port)) | 
 | 				goto chosen; | 
 | 		} | 
 | 	} | 
 | 	qunlock(&p->qlock); | 
 | 	/* | 
 | 	 * debugging: let's see if we ever get this. | 
 | 	 * if we do (and we're a cpu server), we might as well restart | 
 | 	 * since we're now unable to service new connections. | 
 | 	 */ | 
 | 	panic("setlport: out of ports"); | 
 | 	return "no ports available"; | 
 |  | 
 | chosen: | 
 | 	c->lport = port; | 
 | 	qunlock(&p->qlock); | 
 | 	return NULL; | 
 | } | 
 |  | 
 | /* | 
 |  *  set a local address and port from a string of the form | 
 |  *	[address!]port[!r] | 
 |  */ | 
 | char *setladdrport(struct conv *c, char *str, int announcing) | 
 | { | 
 | 	char *p; | 
 | 	char *rv; | 
 | 	uint16_t lport; | 
 | 	uint8_t addr[IPaddrlen]; | 
 |  | 
 | 	/* | 
 | 	 *  ignore restricted part if it exists.  it's | 
 | 	 *  meaningless on local ports. | 
 | 	 */ | 
 | 	p = strchr(str, '!'); | 
 | 	if (p != NULL) { | 
 | 		*p++ = 0; | 
 | 		if (strcmp(p, "r") == 0) | 
 | 			p = NULL; | 
 | 	} | 
 |  | 
 | 	c->lport = 0; | 
 | 	if (p == NULL) { | 
 | 		if (announcing) | 
 | 			ipmove(c->laddr, IPnoaddr); | 
 | 		else | 
 | 			setladdr(c); | 
 | 		p = str; | 
 | 	} else { | 
 | 		if (strcmp(str, "*") == 0) | 
 | 			ipmove(c->laddr, IPnoaddr); | 
 | 		else { | 
 | 			if (parseip(addr, str) == -1) | 
 | 				return Ebadip; | 
 | 			if (ipforme(c->p->f, addr)) | 
 | 				ipmove(c->laddr, addr); | 
 | 			else | 
 | 				return "not a local IP address"; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	/* one process can get all connections */ | 
 | 	if (announcing && strcmp(p, "*") == 0) { | 
 | 		if (!iseve()) | 
 | 			error(Eperm); | 
 | 		return setluniqueport(c, 0); | 
 | 	} | 
 |  | 
 | 	lport = atoi(p); | 
 | 	if (lport <= 0) | 
 | 		rv = setlport(c); | 
 | 	else | 
 | 		rv = setluniqueport(c, lport); | 
 | 	return rv; | 
 | } | 
 |  | 
 | static char *setraddrport(struct conv *c, char *str) | 
 | { | 
 | 	char *p; | 
 |  | 
 | 	p = strchr(str, '!'); | 
 | 	if (p == NULL) | 
 | 		return "malformed address"; | 
 | 	*p++ = 0; | 
 | 	if (parseip(c->raddr, str) == -1) | 
 | 		return Ebadip; | 
 | 	c->rport = atoi(p); | 
 | 	p = strchr(p, '!'); | 
 | 	if (p) { | 
 | 		if (strstr(p, "!r") != NULL) | 
 | 			c->restricted = 1; | 
 | 	} | 
 | 	return NULL; | 
 | } | 
 |  | 
 | /* | 
 |  *  called by protocol connect routine to set addresses | 
 |  */ | 
 | char *Fsstdconnect(struct conv *c, char *argv[], int argc) | 
 | { | 
 | 	char *p; | 
 |  | 
 | 	switch (argc) { | 
 | 		default: | 
 | 			return "bad args to connect"; | 
 | 		case 2: | 
 | 			p = setraddrport(c, argv[1]); | 
 | 			if (p != NULL) | 
 | 				return p; | 
 | 			setladdr(c); | 
 | 			p = setlport(c); | 
 | 			if (p != NULL) | 
 | 				return p; | 
 | 			break; | 
 | 		case 3: | 
 | 			p = setraddrport(c, argv[1]); | 
 | 			if (p != NULL) | 
 | 				return p; | 
 | 			p = setladdrport(c, argv[2], 0); | 
 | 			if (p != NULL) | 
 | 				return p; | 
 | 	} | 
 |  | 
 | 	if ((memcmp(c->raddr, v4prefix, IPv4off) == 0 && | 
 | 		 memcmp(c->laddr, v4prefix, IPv4off) == 0) | 
 | 		|| ipcmp(c->raddr, IPnoaddr) == 0) | 
 | 		c->ipversion = V4; | 
 | 	else | 
 | 		c->ipversion = V6; | 
 |  | 
 | 	return NULL; | 
 | } | 
 |  | 
 | /* | 
 |  *  initiate connection and sleep till its set up | 
 |  */ | 
 | static int connected(void *a) | 
 | { | 
 | 	return ((struct conv *)a)->state == Connected; | 
 | } | 
 |  | 
 | static void connectctlmsg(struct proto *x, struct conv *c, struct cmdbuf *cb) | 
 | { | 
 | 	ERRSTACK(2); | 
 | 	char *p; | 
 |  | 
 | 	if (c->state != 0) | 
 | 		error(Econinuse); | 
 | 	c->state = Connecting; | 
 | 	c->cerr[0] = '\0'; | 
 | 	if (x->connect == NULL) | 
 | 		error("connect not supported"); | 
 | 	p = x->connect(c, cb->f, cb->nf); | 
 | 	if (p != NULL) | 
 | 		error(p); | 
 |  | 
 | 	qunlock(&c->qlock); | 
 | 	if (waserror()) { | 
 | 		qlock(&c->qlock); | 
 | 		nexterror(); | 
 | 	} | 
 | 	rendez_sleep(&c->cr, connected, c); | 
 | 	qlock(&c->qlock); | 
 | 	poperror(); | 
 |  | 
 | 	if (c->cerr[0] != '\0') | 
 | 		error(c->cerr); | 
 | } | 
 |  | 
 | /* | 
 |  *  called by protocol announce routine to set addresses | 
 |  */ | 
 | char *Fsstdannounce(struct conv *c, char *argv[], int argc) | 
 | { | 
 | 	memset(c->raddr, 0, sizeof(c->raddr)); | 
 | 	c->rport = 0; | 
 | 	switch (argc) { | 
 | 		default: | 
 | 			break; | 
 | 		case 2: | 
 | 			return setladdrport(c, argv[1], 1); | 
 | 	} | 
 | 	return "bad args to announce"; | 
 | } | 
 |  | 
 | /* | 
 |  *  initiate announcement and sleep till its set up | 
 |  */ | 
 | static int announced(void *a) | 
 | { | 
 | 	return ((struct conv *)a)->state == Announced; | 
 | } | 
 |  | 
 | static void announcectlmsg(struct proto *x, struct conv *c, struct cmdbuf *cb) | 
 | { | 
 | 	ERRSTACK(2); | 
 | 	char *p; | 
 |  | 
 | 	if (c->state != 0) | 
 | 		error(Econinuse); | 
 | 	c->state = Announcing; | 
 | 	c->cerr[0] = '\0'; | 
 | 	if (x->announce == NULL) | 
 | 		error("announce not supported"); | 
 | 	p = x->announce(c, cb->f, cb->nf); | 
 | 	if (p != NULL) | 
 | 		error(p); | 
 |  | 
 | 	qunlock(&c->qlock); | 
 | 	if (waserror()) { | 
 | 		qlock(&c->qlock); | 
 | 		nexterror(); | 
 | 	} | 
 | 	rendez_sleep(&c->cr, announced, c); | 
 | 	qlock(&c->qlock); | 
 | 	poperror(); | 
 |  | 
 | 	if (c->cerr[0] != '\0') | 
 | 		error(c->cerr); | 
 | } | 
 |  | 
 | /* | 
 |  *  called by protocol bind routine to set addresses | 
 |  */ | 
 | char *Fsstdbind(struct conv *c, char *argv[], int argc) | 
 | { | 
 | 	switch (argc) { | 
 | 		default: | 
 | 			break; | 
 | 		case 2: | 
 | 			return setladdrport(c, argv[1], 0); | 
 | 	} | 
 | 	return "bad args to bind"; | 
 | } | 
 |  | 
 | static void bindctlmsg(struct proto *x, struct conv *c, struct cmdbuf *cb) | 
 | { | 
 | 	char *p; | 
 |  | 
 | 	if (x->bind == NULL) | 
 | 		p = Fsstdbind(c, cb->f, cb->nf); | 
 | 	else | 
 | 		p = x->bind(c, cb->f, cb->nf); | 
 | 	if (p != NULL) | 
 | 		error(p); | 
 | } | 
 |  | 
 | static void tosctlmsg(struct conv *c, struct cmdbuf *cb) | 
 | { | 
 | 	if (cb->nf < 2) | 
 | 		c->tos = 0; | 
 | 	else | 
 | 		c->tos = atoi(cb->f[1]); | 
 | } | 
 |  | 
 | static void ttlctlmsg(struct conv *c, struct cmdbuf *cb) | 
 | { | 
 | 	if (cb->nf < 2) | 
 | 		c->ttl = MAXTTL; | 
 | 	else | 
 | 		c->ttl = atoi(cb->f[1]); | 
 | } | 
 |  | 
 | static long ipwrite(struct chan *ch, void *v, long n, int64_t off) | 
 | { | 
 | 	ERRSTACK(2); | 
 | 	struct conv *c; | 
 | 	struct proto *x; | 
 | 	char *p; | 
 | 	struct cmdbuf *cb; | 
 | 	uint8_t ia[IPaddrlen], ma[IPaddrlen]; | 
 | 	struct fs *f; | 
 | 	char *a; | 
 | 	uint32_t offset = off; | 
 |  | 
 | 	a = v; | 
 | 	f = ipfs[ch->devno]; | 
 |  | 
 | 	switch (TYPE(ch->qid)) { | 
 | 		default: | 
 | 			error(Eperm); | 
 | 		case Qdata: | 
 | 			x = f->p[PROTO(ch->qid)]; | 
 | 			c = x->conv[CONV(ch->qid)]; | 
 |  | 
 | 			if (c->wq == NULL) | 
 | 				error(Eperm); | 
 |  | 
 | 			qwrite(c->wq, a, n); | 
 | 			break; | 
 | 		case Qarp: | 
 | 			return arpwrite(f, a, n); | 
 | 		case Qiproute: | 
 | 			return routewrite(f, ch, a, n); | 
 | 		case Qlog: | 
 | 			netlogctl(f, a, n); | 
 | 			return n; | 
 | 		case Qndb: | 
 | 			return ndbwrite(f, a, offset, n); | 
 | 		case Qctl: | 
 | 			x = f->p[PROTO(ch->qid)]; | 
 | 			c = x->conv[CONV(ch->qid)]; | 
 | 			cb = parsecmd(a, n); | 
 |  | 
 | 			qlock(&c->qlock); | 
 | 			if (waserror()) { | 
 | 				qunlock(&c->qlock); | 
 | 				kfree(cb); | 
 | 				nexterror(); | 
 | 			} | 
 | 			if (cb->nf < 1) | 
 | 				error("%s: short control request",a); | 
 | 			if (strcmp(cb->f[0], "connect") == 0) | 
 | 				connectctlmsg(x, c, cb); | 
 | 			else if (strcmp(cb->f[0], "announce") == 0) | 
 | 				announcectlmsg(x, c, cb); | 
 | 			else if (strcmp(cb->f[0], "bind") == 0) | 
 | 				bindctlmsg(x, c, cb); | 
 | 			else if (strcmp(cb->f[0], "ttl") == 0) | 
 | 				ttlctlmsg(c, cb); | 
 | 			else if (strcmp(cb->f[0], "tos") == 0) | 
 | 				tosctlmsg(c, cb); | 
 | 			else if (strcmp(cb->f[0], "ignoreadvice") == 0) | 
 | 				c->ignoreadvice = 1; | 
 | 			else if (strcmp(cb->f[0], "addmulti") == 0) { | 
 | 				if (cb->nf < 2) | 
 | 					error("addmulti needs interface address"); | 
 | 				if (cb->nf == 2) { | 
 | 					if (!ipismulticast(c->raddr)) | 
 | 						error("addmulti for a non multicast address"); | 
 | 					if (parseip(ia, cb->f[1]) == -1) | 
 | 						error(Ebadip); | 
 | 					ipifcaddmulti(c, c->raddr, ia); | 
 | 				} else { | 
 | 					if (parseip(ia, cb->f[1]) == -1 || | 
 | 						parseip(ma, cb->f[2]) == -1) | 
 | 						error(Ebadip); | 
 | 					if (!ipismulticast(ma)) | 
 | 						error("addmulti for a non multicast address"); | 
 | 					ipifcaddmulti(c, ma, ia); | 
 | 				} | 
 | 			} else if (strcmp(cb->f[0], "remmulti") == 0) { | 
 | 				if (cb->nf < 2) | 
 | 					error("remmulti needs interface address"); | 
 | 				if (!ipismulticast(c->raddr)) | 
 | 					error("remmulti for a non multicast address"); | 
 | 				if (parseip(ia, cb->f[1]) == -1) | 
 | 					error(Ebadip); | 
 | 				ipifcremmulti(c, c->raddr, ia); | 
 | 			} else if (strcmp(cb->f[0], "maxfragsize") == 0) { | 
 | 				if (cb->nf < 2) | 
 | 					error("maxfragsize needs size"); | 
 |  | 
 | 				c->maxfragsize = (int)strtol(cb->f[1], NULL, 0); | 
 |  | 
 | 			} else if (x->ctl != NULL) { | 
 | 				p = x->ctl(c, cb->f, cb->nf); | 
 | 				if (p != NULL) | 
 | 					error(p); | 
 | 			} else | 
 | 				error("%s: not implemented", cb->f[0]); | 
 | 			qunlock(&c->qlock); | 
 | 			kfree(cb); | 
 | 			poperror(); | 
 | 	} | 
 | 	return n; | 
 | } | 
 |  | 
 | static long ipbwrite(struct chan *ch, struct block *bp, int64_t offset) | 
 | { | 
 | 	struct conv *c; | 
 | 	struct proto *x; | 
 | 	struct fs *f; | 
 | 	int n; | 
 |  | 
 | 	switch (TYPE(ch->qid)) { | 
 | 		case Qdata: | 
 | 			f = ipfs[ch->devno]; | 
 | 			x = f->p[PROTO(ch->qid)]; | 
 | 			c = x->conv[CONV(ch->qid)]; | 
 |  | 
 | 			if (c->wq == NULL) | 
 | 				error(Eperm); | 
 |  | 
 | 			if (bp->next) | 
 | 				bp = concatblock(bp); | 
 | 			n = BLEN(bp); | 
 | 			qbwrite(c->wq, bp); | 
 | 			return n; | 
 | 		default: | 
 | 			return devbwrite(ch, bp, offset); | 
 | 	} | 
 | } | 
 |  | 
 | static void ipinit(void) | 
 | { | 
 | 	qlock_init(&fslock); | 
 | } | 
 |  | 
 | struct dev ipdevtab = { | 
 | 	'I', | 
 | 	"ip", | 
 |  | 
 | 	ipreset, | 
 | 	ipinit, | 
 | 	devshutdown, | 
 | 	ipattach, | 
 | 	ipwalk, | 
 | 	ipstat, | 
 | 	ipopen, | 
 | 	ipcreate, | 
 | 	ipclose, | 
 | 	ipread, | 
 | 	ipbread, | 
 | 	ipwrite, | 
 | 	ipbwrite, | 
 | 	ipremove, | 
 | 	ipwstat, | 
 | 	devpower, | 
 | 	devconfig, | 
 | 	ipchaninfo, | 
 | }; | 
 |  | 
 | /* All protocols call this to finish their alloc and bind to f */ | 
 | int Fsproto(struct fs *f, struct proto *p) | 
 | { | 
 | 	qlock_init(&p->qlock); | 
 | 	if (f->np >= Maxproto) | 
 | 		return -1; | 
 |  | 
 | 	p->f = f; | 
 |  | 
 | 	if (p->ipproto > 0) { | 
 | 		if (f->t2p[p->ipproto] != NULL) | 
 | 			return -1; | 
 | 		f->t2p[p->ipproto] = p; | 
 | 	} | 
 |  | 
 | 	p->qid.type = QTDIR; | 
 | 	p->qid.path = QID(f->np, 0, Qprotodir); | 
 | 	p->conv = kzmalloc(sizeof(struct conv *) * (p->nc + 1), 0); | 
 | 	if (p->conv == NULL) | 
 | 		panic("Fsproto"); | 
 |  | 
 | 	p->x = f->np; | 
 | 	p->nextrport = 600; | 
 | 	f->p[f->np++] = p; | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | /* | 
 |  *  return true if this protocol is | 
 |  *  built in | 
 |  */ | 
 | int Fsbuiltinproto(struct fs *f, uint8_t proto) | 
 | { | 
 | 	return f->t2p[proto] != NULL; | 
 | } | 
 |  | 
 | /* | 
 |  *  called with protocol locked | 
 |  */ | 
 | struct conv *Fsprotoclone(struct proto *p, char *user) | 
 | { | 
 | 	struct conv *c, **pp, **ep; | 
 |  | 
 | retry: | 
 | 	c = NULL; | 
 | 	ep = &p->conv[p->nc]; | 
 | 	for (pp = p->conv; pp < ep; pp++) { | 
 | 		c = *pp; | 
 | 		if (c == NULL) { | 
 | 			c = kzmalloc(sizeof(struct conv), 0); | 
 | 			if (c == NULL) | 
 | 				error(Enomem); | 
 | 			rendez_init(&c->cr); | 
 | 			rendez_init(&c->listenr); | 
 | 			qlock_init(&c->qlock); | 
 | 			qlock_init(&c->car); | 
 | 			qlock_init(&c->listenq); | 
 | 			/* We will always get this lock, since we just alloc'd c.  Remember, | 
 | 			 * we can't sleep in here. */ | 
 | 			qlock(&c->qlock); | 
 | 			c->p = p; | 
 | 			c->x = pp - p->conv; | 
 | 			if (p->ptclsize != 0) { | 
 | 				/* man this is shitty - it makes it so you can't do per-proto | 
 | 				 * alloc/init on their private structures... */ | 
 | 				c->ptcl = kzmalloc(p->ptclsize, 0); | 
 | 				if (c->ptcl == NULL) { | 
 | 					/* Technically, no one knows about this, so the old code | 
 | 					 * didn't bother unlocking - just freeing.  But that could | 
 | 					 * break lock checkers and other reuses of the structure. */ | 
 | 					qunlock(&c->qlock); | 
 | 					kfree(c); | 
 | 					error(Enomem); | 
 | 				} | 
 | 				/* For now, we'll have an optional proto op to init private | 
 | 				 * proto structs.  Prob should put the alloc in here too. (TODO: | 
 | 				 * revise this once we stabilize all of our protos). */ | 
 | 				if (p->newconv) | 
 | 					p->newconv(p, c); | 
 | 			} | 
 | 			*pp = c; | 
 | 			p->ac++; | 
 | 			c->eq = qopen(1024, Qmsg, 0, 0); | 
 | 			(*p->create) (c); | 
 | 			break; | 
 | 		} | 
 | 		if (canqlock(&c->qlock)) { | 
 | 			/* | 
 | 			 *  make sure both processes and protocol | 
 | 			 *  are done with this Conv | 
 | 			 */ | 
 | 			if (c->inuse == 0 && (p->inuse == NULL || (*p->inuse) (c) == 0)) | 
 | 				break; | 
 |  | 
 | 			qunlock(&c->qlock); | 
 | 		} | 
 | 	} | 
 | 	/* if we're here, and we broke from the loop, then we hold the qlock | 
 | 	 * this next check is for when we didn't break, and naturally left the foor | 
 | 	 * loop (where we don't hold it). */ | 
 | 	if (pp >= ep) { | 
 | 		if (p->gc) | 
 | 			printd("Fsprotoclone: garbage collecting Convs\n"); | 
 | 		if (p->gc != NULL && (*p->gc) (p)) | 
 | 			goto retry; | 
 | 		/* debugging: do we ever get here? */ | 
 | 		panic("Fsprotoclone: all conversations in use"); | 
 | 		return NULL; | 
 | 	} | 
 |  | 
 | 	c->inuse = 1; | 
 | 	kstrdup(&c->owner, user); | 
 | 	c->perm = 0660; | 
 | 	c->state = Idle; | 
 | 	ipmove(c->laddr, IPnoaddr); | 
 | 	ipmove(c->raddr, IPnoaddr); | 
 | 	c->r = NULL; | 
 | 	c->rgen = 0; | 
 | 	c->lport = 0; | 
 | 	c->rport = 0; | 
 | 	c->restricted = 0; | 
 | 	c->maxfragsize = 0; | 
 | 	c->ttl = MAXTTL; | 
 | 	qreopen(c->rq); | 
 | 	qreopen(c->wq); | 
 | 	qreopen(c->eq); | 
 |  | 
 | 	qunlock(&c->qlock); | 
 | 	return c; | 
 | } | 
 |  | 
 | int Fsconnected(struct conv *c, char *msg) | 
 | { | 
 | 	if (msg != NULL && *msg != '\0') | 
 | 		strncpy(c->cerr, msg, ERRMAX - 1); | 
 |  | 
 | 	switch (c->state) { | 
 |  | 
 | 		case Announcing: | 
 | 			c->state = Announced; | 
 | 			break; | 
 |  | 
 | 		case Connecting: | 
 | 			c->state = Connected; | 
 | 			break; | 
 | 	} | 
 |  | 
 | 	rendez_wakeup(&c->cr); | 
 | 	return 0; | 
 | } | 
 |  | 
 | struct proto *Fsrcvpcol(struct fs *f, uint8_t proto) | 
 | { | 
 | 	if (f->ipmux) | 
 | 		return f->ipmux; | 
 | 	else | 
 | 		return f->t2p[proto]; | 
 | } | 
 |  | 
 | struct proto *Fsrcvpcolx(struct fs *f, uint8_t proto) | 
 | { | 
 | 	return f->t2p[proto]; | 
 | } | 
 |  | 
 | /* | 
 |  *  called with protocol locked | 
 |  */ | 
 | struct conv *Fsnewcall(struct conv *c, uint8_t * raddr, uint16_t rport, | 
 | 					   uint8_t * laddr, uint16_t lport, uint8_t version) | 
 | { | 
 | 	struct conv *nc; | 
 | 	struct conv **l; | 
 | 	int i; | 
 |  | 
 | 	qlock(&c->qlock); | 
 | 	i = 0; | 
 | 	for (l = &c->incall; *l; l = &(*l)->next) | 
 | 		i++; | 
 | 	if (i >= Maxincall) { | 
 | 		static int beenhere; | 
 |  | 
 | 		qunlock(&c->qlock); | 
 | 		if (!beenhere) { | 
 | 			beenhere = 1; | 
 | 			printd("Fsnewcall: incall queue full (%d) on port %d\n", | 
 | 				   i, c->lport); | 
 | 		} | 
 | 		return NULL; | 
 | 	} | 
 |  | 
 | 	/* find a free conversation */ | 
 | 	nc = Fsprotoclone(c->p, network); | 
 | 	if (nc == NULL) { | 
 | 		qunlock(&c->qlock); | 
 | 		return NULL; | 
 | 	} | 
 | 	ipmove(nc->raddr, raddr); | 
 | 	nc->rport = rport; | 
 | 	ipmove(nc->laddr, laddr); | 
 | 	nc->lport = lport; | 
 | 	nc->next = NULL; | 
 | 	*l = nc; | 
 | 	nc->state = Connected; | 
 | 	nc->ipversion = version; | 
 |  | 
 | 	qunlock(&c->qlock); | 
 |  | 
 | 	rendez_wakeup(&c->listenr); | 
 |  | 
 | 	return nc; | 
 | } | 
 |  | 
 | long ndbwrite(struct fs *f, char *a, uint32_t off, int n) | 
 | { | 
 | 	if (off > strlen(f->ndb)) | 
 | 		error(Eio); | 
 | 	if (off + n >= sizeof(f->ndb)) | 
 | 		error(Eio); | 
 | 	memmove(f->ndb + off, a, n); | 
 | 	f->ndb[off + n] = 0; | 
 | 	f->ndbvers++; | 
 | 	f->ndbmtime = seconds(); | 
 | 	return n; | 
 | } | 
 |  | 
 | uint32_t scalednconv(void) | 
 | { | 
 | 	return Nchans * 4; | 
 | } |