xref: /inferno-os/emu/port/devip.c (revision 50b0dbb170df61467e42c7ea4deb0b5692d15f4c)
1 #include	"dat.h"
2 #include	"fns.h"
3 #include	"error.h"
4 #include	"ip.h"
5 
6 enum
7 {
8 	Qtopdir		= 1,	/* top level directory */
9 	Qtopbase,
10 	Qarp=	Qtopbase,
11 /*	Qiproute, */
12 /*	Qipselftab,	*/
13 	Qndb,
14 
15 	Qprotodir,		/* directory for a protocol */
16 	Qprotobase,
17 	Qclone=	Qprotobase,
18 	Qstats,
19 
20 	Qconvdir,		/* directory for a conversation */
21 	Qconvbase,
22 	Qctl=	Qconvbase,
23 	Qdata,
24 	Qlisten,
25 	Qlocal,
26 	Qremote,
27 	Qstatus,
28 
29 	Logtype=	5,
30 	Masktype=	(1<<Logtype)-1,
31 	Logconv=	12,
32 	Maskconv=	(1<<Logconv)-1,
33 	Shiftconv=	Logtype,
34 	Logproto=	8,
35 	Maskproto=	(1<<Logproto)-1,
36 	Shiftproto=	Logtype + Logconv,
37 
38 	Statelen = 256,
39 
40 	Nfs=	1,
41 
42 	Maxproto	= 4,
43 	MAXCONV		= 4096
44 };
45 #define TYPE(x) 	( ((ulong)(x).path) & Masktype )
46 #define CONV(x) 	( (((ulong)(x).path) >> Shiftconv) & Maskconv )
47 #define PROTO(x) 	( (((ulong)(x).path) >> Shiftproto) & Maskproto )
48 #define QID(p, c, y) 	( ((p)<<(Shiftproto)) | ((c)<<Shiftconv) | (y) )
49 
50 enum
51 {
52 	Idle=		0,
53 	Announcing=	1,
54 	Announced=	2,
55 	Connecting=	3,
56 	Connected=	4,
57 	Hungup=	5,
58 };
59 
60 struct Conv
61 {
62 	QLock	l;
63 
64 	int	x;	/* conversation index */
65 	Proto*	p;
66 
67 	uchar	laddr[IPaddrlen];	/* local IP address */
68 	uchar	raddr[IPaddrlen];	/* remote IP address */
69 	int	restricted;	/* remote port is restricted */
70 	ushort	lport;	/* local port number */
71 	ushort	rport;	/* remote port number */
72 
73 	char*	owner;	/* protections */
74 	int	perm;
75 	int	inuse;	/* opens of listen/data/ctl */
76 	int	state;
77 
78 	/* udp specific */
79 	int	headers;	/* data src/dst headers in udp */
80 
81 	char	cerr[ERRMAX];
82 
83 	QLock	listenq;
84 
85 	void*	ptcl;	/* protocol specific stuff */
86 
87 	int	sfd;
88 	QLock	wlock;	/* prevent data from being split by concurrent writes */
89 };
90 
91 struct Proto
92 {
93 	QLock	l;
94 	int	x;
95 	int	ipproto;
96 	int	stype;
97 	char*	name;
98 	int	maxconv;
99 	Fs*	f;	/* file system this proto is part of */
100 	Conv**	conv;	/* array of conversations */
101 	int	pctlsize;	/* size of per protocol ctl block */
102 	int	nc;	/* number of conversations */
103 	int	ac;
104 	Qid	qid;	/* qid for protocol directory */
105 	/* port allocation isn't done here when hosted */
106 
107 	void*	priv;
108 };
109 
110 /*
111  * one per IP protocol stack
112  */
113 struct Fs
114 {
115 	RWlock	l;
116 	int	dev;
117 
118 	int	np;
119 	Proto*	p[Maxproto+1];	/* list of supported protocols */
120 	Proto*	t2p[256];	/* vector of all protocols */
121 
122 	char	ndb[1024];	/* an ndb entry for this interface */
123 	int	ndbvers;
124 	long	ndbmtime;
125 };
126 
127 static	Fs	*ipfs[Nfs];	/* attached fs's */
128 static	char	network[] = "network";
129 static	char* ipstates[] = {
130 	"Closed",	/* Idle */
131 	"Announcing",
132 	"Announced",
133 	"Connecting",
134 	"Established",	/* Connected */
135 	"Closed",	/* Hungup */
136 };
137 
138 static	Conv*	protoclone(Proto*, char*, int);
139 static	Conv*	newconv(Proto*, Conv **);
140 static	void	setladdr(Conv*);
141 
142 static int
143 ip3gen(Chan *c, int i, Dir *dp)
144 {
145 	Qid q;
146 	Conv *cv;
147 	char *p;
148 
149 	cv = ipfs[c->dev]->p[PROTO(c->qid)]->conv[CONV(c->qid)];
150 	if(cv->owner == nil)
151 		kstrdup(&cv->owner, eve);
152 	mkqid(&q, QID(PROTO(c->qid), CONV(c->qid), i), 0, QTFILE);
153 
154 	switch(i) {
155 	default:
156 		return -1;
157 	case Qctl:
158 		devdir(c, q, "ctl", 0, cv->owner, cv->perm, dp);
159 		return 1;
160 	case Qdata:
161 		devdir(c, q, "data", 0, cv->owner, cv->perm, dp);
162 		return 1;
163 	case Qlisten:
164 		devdir(c, q, "listen", 0, cv->owner, cv->perm, dp);
165 		return 1;
166 	case Qlocal:
167 		p = "local";
168 		break;
169 	case Qremote:
170 		p = "remote";
171 		break;
172 	case Qstatus:
173 		p = "status";
174 		break;
175 	}
176 	devdir(c, q, p, 0, cv->owner, 0444, dp);
177 	return 1;
178 }
179 
180 static int
181 ip2gen(Chan *c, int i, Dir *dp)
182 {
183 	Qid q;
184 
185 	switch(i) {
186 	case Qclone:
187 		mkqid(&q, QID(PROTO(c->qid), 0, Qclone), 0, QTFILE);
188 		devdir(c, q, "clone", 0, network, 0666, dp);
189 		return 1;
190 	case Qstats:
191 		mkqid(&q, QID(PROTO(c->qid), 0, Qstats), 0, QTFILE);
192 		devdir(c, q, "stats", 0, network, 0444, dp);
193 		return 1;
194 	}
195 	return -1;
196 }
197 
198 static int
199 ip1gen(Chan *c, int i, Dir *dp)
200 {
201 	Qid q;
202 	char *p;
203 	int prot;
204 	int len = 0;
205 	Fs *f;
206 	extern ulong	kerndate;
207 
208 	f = ipfs[c->dev];
209 
210 	prot = 0664;
211 	mkqid(&q, QID(0, 0, i), 0, QTFILE);
212 	switch(i) {
213 	default:
214 		return -1;
215 	case Qarp:
216 		p = "arp";
217 		break;
218 	case Qndb:
219 		p = "ndb";
220 		len = strlen(ipfs[c->dev]->ndb);
221 		break;
222 /*	case Qiproute:
223 		p = "iproute";
224 		break;
225 	case Qipselftab:
226 		p = "ipselftab";
227 		prot = 0444;
228 		break;
229 	case Qiprouter:
230 		p = "iprouter";
231 		break;
232 	case Qlog:
233 		p = "log";
234 		break;
235 */
236 	}
237 	devdir(c, q, p, len, network, prot, dp);
238 	if(i == Qndb && f->ndbmtime > kerndate)
239 		dp->mtime = f->ndbmtime;
240 	return 1;
241 }
242 
243 static int
244 ipgen(Chan *c, char *name, Dirtab *tab, int x, int s, Dir *dp)
245 {
246 	Qid q;
247 	Conv *cv;
248 	Fs *f;
249 
250 	USED(name);
251 	USED(tab);
252 	USED(x);
253 	f = ipfs[c->dev];
254 
255 	switch(TYPE(c->qid)) {
256 	case Qtopdir:
257 		if(s == DEVDOTDOT){
258 			mkqid(&q, QID(0, 0, Qtopdir), 0, QTDIR);
259 			sprint(up->genbuf, "#I%lud", c->dev);
260 			devdir(c, q, up->genbuf, 0, network, 0555, dp);
261 			return 1;
262 		}
263 		if(s < f->np) {
264 /*			if(f->p[s]->connect == nil)
265 				return 0;	/* protocol with no user interface */
266 			mkqid(&q, QID(s, 0, Qprotodir), 0, QTDIR);
267 			devdir(c, q, f->p[s]->name, 0, network, 0555, dp);
268 			return 1;
269 		}
270 		s -= f->np;
271 		return ip1gen(c, s+Qtopbase, dp);
272 	case Qarp:
273 	case Qndb:
274 /*	case Qiproute:
275 	case Qiprouter:
276 	case Qipselftab:	*/
277 		return ip1gen(c, TYPE(c->qid), dp);
278 	case Qprotodir:
279 		if(s == DEVDOTDOT){
280 			mkqid(&q, QID(0, 0, Qtopdir), 0, QTDIR);
281 			sprint(up->genbuf, "#I%lud", c->dev);
282 			devdir(c, q, up->genbuf, 0, network, 0555, dp);
283 			return 1;
284 		}
285 		if(s < f->p[PROTO(c->qid)]->ac) {
286 			cv = f->p[PROTO(c->qid)]->conv[s];
287 			sprint(up->genbuf, "%d", s);
288 			mkqid(&q, QID(PROTO(c->qid), s, Qconvdir), 0, QTDIR);
289 			devdir(c, q, up->genbuf, 0, cv->owner, 0555, dp);
290 			return 1;
291 		}
292 		s -= f->p[PROTO(c->qid)]->ac;
293 		return ip2gen(c, s+Qprotobase, dp);
294 	case Qclone:
295 	case Qstats:
296 		return ip2gen(c, TYPE(c->qid), dp);
297 	case Qconvdir:
298 		if(s == DEVDOTDOT){
299 			s = PROTO(c->qid);
300 			mkqid(&q, QID(s, 0, Qprotodir), 0, QTDIR);
301 			devdir(c, q, f->p[s]->name, 0, network, 0555, dp);
302 			return 1;
303 		}
304 		return ip3gen(c, s+Qconvbase, dp);
305 	case Qctl:
306 	case Qdata:
307 	case Qlisten:
308 	case Qlocal:
309 	case Qremote:
310 	case Qstatus:
311 		return ip3gen(c, TYPE(c->qid), dp);
312 	}
313 	return -1;
314 }
315 
316 static void
317 newproto(char *name, int type, int maxconv)
318 {
319 	Proto *p;
320 
321 	p = smalloc(sizeof(*p));
322 	p->name = name;
323 	p->stype = type;
324 	p->ipproto = type+1;	/* temporary */
325 	p->nc = maxconv;
326 	if(Fsproto(ipfs[0], p))
327 		panic("can't newproto %s", name);
328 }
329 
330 void
331 ipinit(void)
332 {
333 	ipfs[0] = malloc(sizeof(Fs));
334 	if(ipfs[0] == nil)
335 		panic("no memory for IP stack");
336 
337 	newproto("udp", S_UDP, 64);
338 	newproto("tcp", S_TCP, 2048);
339 
340 	fmtinstall('i', eipfmt);
341 	fmtinstall('I', eipfmt);
342 	fmtinstall('E', eipfmt);
343 	fmtinstall('V', eipfmt);
344 	fmtinstall('M', eipfmt);
345 }
346 
347 Chan *
348 ipattach(char *spec)
349 {
350 	Chan *c;
351 
352 	if(atoi(spec) != 0)
353 		error("bad specification");
354 
355 	c = devattach('I', spec);
356 	mkqid(&c->qid, QID(0, 0, Qtopdir), 0, QTDIR);
357 	c->dev = 0;
358 
359 	return c;
360 }
361 
362 static Walkqid*
363 ipwalk(Chan* c, Chan *nc, char **name, int nname)
364 {
365 	return devwalk(c, nc, name, nname, nil, 0, ipgen);
366 }
367 
368 static int
369 ipstat(Chan *c, uchar *db, int n)
370 {
371 	return devstat(c, db, n, 0, 0, ipgen);
372 }
373 
374 static int m2p[] = {
375 	4,
376 	2,
377 	6,
378 };
379 
380 static Chan *
381 ipopen(Chan *c, int omode)
382 {
383 	Conv *cv, *nc;
384 	Proto *p;
385 	uchar raddr[IPaddrlen];
386 	ushort rport;
387 	int perm, sfd;
388 	Fs *f;
389 
390 	perm = m2p[omode&3];
391 
392 	f = ipfs[c->dev];
393 
394 	switch(TYPE(c->qid)) {
395 	default:
396 		break;
397 	case Qtopdir:
398 	case Qprotodir:
399 	case Qconvdir:
400 	case Qstatus:
401 	case Qremote:
402 	case Qlocal:
403 	case Qstats:
404 	/* case Qipselftab: */
405 		if(omode != OREAD)
406 			error(Eperm);
407 		break;
408 	case Qndb:
409 		if(omode & (OWRITE|OTRUNC) && !iseve())
410 			error(Eperm);
411 		if((omode & (OWRITE|OTRUNC)) == (OWRITE|OTRUNC)){
412 			f->ndb[0] = 0;
413 			f->ndbvers++;
414 		}
415 		break;
416 	case Qclone:
417 		p = f->p[PROTO(c->qid)];
418 		cv = protoclone(p, up->env->user, -1);
419 		if(cv == 0)
420 			error(Enodev);
421 		mkqid(&c->qid, QID(p->x, cv->x, Qctl), 0, QTFILE);
422 		break;
423 	case Qdata:
424 	case Qctl:
425 		p = f->p[PROTO(c->qid)];
426 		qlock(&p->l);
427 		cv = p->conv[CONV(c->qid)];
428 		qlock(&cv->l);
429 		if(waserror()){
430 			qunlock(&cv->l);
431 			qunlock(&p->l);
432 			nexterror();
433 		}
434 		if((perm & (cv->perm>>6)) != perm) {
435 			if(strcmp(up->env->user, cv->owner) != 0)
436 				error(Eperm);
437 			if((perm & cv->perm) != perm)
438 				error(Eperm);
439 		}
440 		cv->inuse++;
441 		if(cv->inuse == 1) {
442 			kstrdup(&cv->owner, up->env->user);
443 			cv->perm = 0660;
444 			if(cv->sfd < 0)
445 				cv->sfd = so_socket(p->stype);
446 		}
447 		poperror();
448 		qunlock(&cv->l);
449 		qunlock(&p->l);
450 		break;
451 	case Qlisten:
452 		p = f->p[PROTO(c->qid)];
453 		cv = p->conv[CONV(c->qid)];
454 		if((perm & (cv->perm>>6)) != perm){
455 			if(strcmp(up->env->user, cv->owner) != 0)
456 				error(Eperm);
457 			if((perm & cv->perm) != perm)
458 				error(Eperm);
459 		}
460 
461 		if(cv->state != Announced)
462 			error("not announced");
463 
464 		qlock(&cv->listenq);
465 		if(waserror()){
466 			qunlock(&cv->listenq);
467 			nexterror();
468 		}
469 
470 		sfd = so_accept(cv->sfd, raddr, &rport);
471 
472 		nc = protoclone(p, up->env->user, sfd);
473 		if(nc == 0) {
474 			so_close(sfd);
475 			error(Enodev);
476 		}
477 		memmove(nc->raddr, raddr, IPaddrlen);
478 		nc->rport = rport;
479 		setladdr(nc);
480 		nc->state = Connected;
481 		mkqid(&c->qid, QID(PROTO(c->qid), nc->x, Qctl), 0, QTFILE);
482 
483 		poperror();
484 		qunlock(&cv->listenq);
485 		break;
486 	}
487 	c->mode = openmode(omode);
488 	c->flag |= COPEN;
489 	c->offset = 0;
490 	return c;
491 }
492 
493 static void
494 closeconv(Conv *cv)
495 {
496 	int fd;
497 
498 	qlock(&cv->l);
499 
500 	if(--cv->inuse > 0) {
501 		qunlock(&cv->l);
502 		return;
503 	}
504 
505 	if(waserror()){
506 		qunlock(&cv->l);
507 		return;
508 	}
509 	kstrdup(&cv->owner, network);
510 	cv->perm = 0660;
511 	/* cv->p->close(cv); */
512 	cv->state = Idle;
513 	cv->restricted = 0;
514 	fd = cv->sfd;
515 	cv->sfd = -1;
516 	if(fd >= 0)
517 		so_close(fd);
518 	poperror();
519 	qunlock(&cv->l);
520 }
521 
522 static void
523 ipclose(Chan *c)
524 {
525 	Fs *f;
526 
527 	f = ipfs[c->dev];
528 	switch(TYPE(c->qid)) {
529 	case Qdata:
530 	case Qctl:
531 		if(c->flag & COPEN)
532 			closeconv(f->p[PROTO(c->qid)]->conv[CONV(c->qid)]);
533 		break;
534 	}
535 }
536 
537 static long
538 ipread(Chan *ch, void *a, long n, vlong off)
539 {
540 	int r;
541 	Conv *c;
542 	Proto *x;
543 	char *p, *s;
544 	Fs *f;
545 	ulong offset = off;
546 
547 	f = ipfs[ch->dev];
548 
549 	p = a;
550 	switch(TYPE(ch->qid)) {
551 	default:
552 		error(Eperm);
553 	case Qprotodir:
554 	case Qtopdir:
555 	case Qconvdir:
556 		return devdirread(ch, a, n, 0, 0, ipgen);
557 	case Qarp:
558 		error(Eperm);	/* TO DO */
559 	case Qndb:
560 		return readstr(off, a, n, f->ndb);
561 	case Qctl:
562 		sprint(up->genbuf, "%lud", CONV(ch->qid));
563 		return readstr(offset, p, n, up->genbuf);
564 	case Qremote:
565 		x = f->p[PROTO(ch->qid)];
566 		c = x->conv[CONV(ch->qid)];
567 		sprint(up->genbuf, "%I!%d\n", c->raddr, c->rport);
568 		return readstr(offset, p, n, up->genbuf);
569 	case Qlocal:
570 		x = f->p[PROTO(ch->qid)];
571 		c = x->conv[CONV(ch->qid)];
572 		sprint(up->genbuf, "%I!%d\n", c->laddr, c->lport);
573 		return readstr(offset, p, n, up->genbuf);
574 	case Qstatus:
575 		x = f->p[PROTO(ch->qid)];
576 		c = x->conv[CONV(ch->qid)];
577 		s = smalloc(Statelen);
578 		if(waserror()){
579 			free(s);
580 			nexterror();
581 		}
582 		snprint(s, Statelen, "%s\n", ipstates[c->state]);
583 		n = readstr(offset, p, n, s);
584 		poperror();
585 		free(s);
586 		return n;
587 	case Qdata:
588 		x = f->p[PROTO(ch->qid)];
589 		c = x->conv[CONV(ch->qid)];
590 		if(c->sfd < 0)
591 			error(Ehungup);
592 		if(c->headers) {
593 			if(n < c->headers)
594 				error(Ebadarg);
595 			p = a;
596 			r = so_recv(c->sfd, p + c->headers, n - c->headers, p, c->headers);
597 			if(r > 0)
598 				r += c->headers;
599 		} else
600 			r = so_recv(c->sfd, a, n, nil, 0);
601 		if(r < 0)
602 			oserror();
603 		return r;
604 	case Qstats:
605 		error("stats not implemented");
606 		return n;
607 	}
608 }
609 
610 static void
611 setladdr(Conv *c)
612 {
613 	/* TO DO: this can't be right for hosts with several addresses before connect/accept */
614 	so_getsockname(c->sfd, c->laddr, &c->lport);
615 }
616 
617 /*
618  * pick a local port and set it
619  */
620 static void
621 setlport(Conv *c)
622 {
623 	uchar laddr[IPaddrlen];
624 	ushort p;
625 
626 	so_bind(c->sfd, c->restricted, c->laddr, c->lport);
627 	if(c->lport == 0 || ipcmp(c->laddr, IPnoaddr) == 0){
628 		so_getsockname(c->sfd, laddr, &p);
629 		if(c->lport == 0)
630 			c->lport = p;
631 		if(ipcmp(c->laddr, IPnoaddr) == 0)
632 			memmove(c->laddr, laddr, sizeof laddr);
633 	}
634 }
635 
636 static int
637 portno(char *p)
638 {
639 	long n;
640 	char *e;
641 
642 	n = strtoul(p, &e, 0);
643 	if(p == e)
644 		error("non-numeric port number");
645 	return n;
646 }
647 
648 /*
649  *  set a local address and port from a string of the form
650  *	[address!]port[!r]
651  */
652 static void
653 setladdrport(Conv *c, char *str, int announcing)
654 {
655 	char *p;
656 	int lport;
657 
658 	/*
659 	 *  ignore restricted part if it exists.  it's
660 	 *  meaningless on local ports.
661 	 */
662 	p = strchr(str, '!');
663 	if(p != nil){
664 		*p++ = 0;
665 		if(strcmp(p, "r") == 0)
666 			p = nil;
667 	}
668 
669 	c->lport = 0;
670 	if(p == nil){
671 		if(announcing)
672 			ipmove(c->laddr, IPnoaddr);
673 		else if(0)
674 			setladdr(c);
675 		p = str;
676 	} else {
677 		if(strcmp(str, "*") == 0)
678 			ipmove(c->laddr, IPnoaddr);
679 		else if(parseip(c->laddr, str) == 0)
680 			error("invalid IP address");
681 	}
682 
683 	if(announcing && strcmp(p, "*") == 0){
684 		if(!iseve())
685 			error(Eperm);
686 		c->lport = 0;
687 		setlport(c);
688 		return;
689 	}
690 
691 	lport = portno(p);
692 	if(lport <= 0)
693 		c->lport = 0;
694 	else
695 		c->lport = lport;
696 
697 	setlport(c);
698 }
699 
700 static char*
701 setraddrport(Conv *c, char *str)
702 {
703 	char *p;
704 
705 	p = strchr(str, '!');
706 	if(p == nil)
707 		return "malformed address";
708 	*p++ = 0;
709 	if(parseip(c->raddr, str) == 0)
710 		return "invalid IP address";
711 	c->rport = portno(p);
712 	p = strchr(p, '!');
713 	if(p){
714 		if(strstr(p, "!r") != nil)
715 			c->restricted = 1;
716 	}
717 	return nil;
718 }
719 
720 static void
721 connectctlmsg(Proto *x, Conv *c, Cmdbuf *cb)
722 {
723 	char *p;
724 
725 	USED(x);
726 	if(c->state != Idle)
727 		error(Econinuse);
728 	c->state = Connecting;
729 	c->cerr[0] = '\0';
730 	switch(cb->nf) {
731 	default:
732 		error("bad args to connect");
733 	case 2:
734 		p = setraddrport(c, cb->f[1]);
735 		if(p != nil)
736 			error(p);
737 		break;
738 	case 3:
739 		p = setraddrport(c, cb->f[1]);
740 		if(p != nil)
741 			error(p);
742 		c->lport = portno(cb->f[2]);
743 		setlport(c);
744 		break;
745 	}
746 	qunlock(&c->l);
747 	if(waserror()){
748 		qlock(&c->l);
749 		c->state = Connected;	/* sic */
750 		nexterror();
751 	}
752 	/* p = x->connect(c, cb->f, cb->nf); */
753 	so_connect(c->sfd, c->raddr, c->rport);
754 	qlock(&c->l);
755 	poperror();
756 	setladdr(c);
757 	c->state = Connected;
758 }
759 
760 static void
761 announcectlmsg(Proto *x, Conv *c, Cmdbuf *cb)
762 {
763 	if(c->state != Idle)
764 		error(Econinuse);
765 	c->state = Announcing;
766 	c->cerr[0] = '\0';
767 	ipmove(c->raddr, IPnoaddr);
768 	c->rport = 0;
769 	switch(cb->nf){
770 	default:
771 		error("bad args to announce");
772 	case 2:
773 		setladdrport(c, cb->f[1], 1);
774 		break;
775 	}
776 	USED(x);
777 	/* p = x->announce(c, cb->f, cb->nf); */
778 	if(c->p->stype != S_UDP){
779 		qunlock(&c->l);
780 		if(waserror()){
781 			c->state = Announced;	/* sic */
782 			qlock(&c->l);
783 			nexterror();
784 		}
785 		so_listen(c->sfd);
786 		qlock(&c->l);
787 		poperror();
788 	}
789 	c->state = Announced;
790 }
791 
792 static void
793 bindctlmsg(Proto *x, Conv *c, Cmdbuf *cb)
794 {
795 	USED(x);
796 	switch(cb->nf){
797 	default:
798 		error("bad args to bind");
799 	case 2:
800 		setladdrport(c, cb->f[1], 0);
801 		break;
802 	}
803 }
804 
805 static long
806 ipwrite(Chan *ch, void *a, long n, vlong off)
807 {
808 	Conv *c;
809 	Proto *x;
810 	char *p;
811 	Cmdbuf *cb;
812 	Fs *f;
813 
814 	f = ipfs[ch->dev];
815 
816 	switch(TYPE(ch->qid)) {
817 	default:
818 		error(Eperm);
819 	case Qdata:
820 		x = f->p[PROTO(ch->qid)];
821 		c = x->conv[CONV(ch->qid)];
822 		if(c->sfd < 0)
823 			error(Ehungup);
824 		qlock(&c->wlock);
825 		if(waserror()){
826 			qunlock(&c->wlock);
827 			nexterror();
828 		}
829 		if(c->headers) {
830 			if(n < c->headers)
831 				error(Eshort);
832 			p = a;
833 			n = so_send(c->sfd, p + c->headers, n - c->headers, p, c->headers);
834 			if(n >= 0)
835 				n += c->headers;
836 		} else
837 			n = so_send(c->sfd, a, n, nil, 0);
838 		poperror();
839 		qunlock(&c->wlock);
840 		if(n < 0)
841 			oserror();
842 		break;
843 	case Qarp:
844 		return arpwrite(a, n);
845 	case Qndb:
846 		if(off > strlen(f->ndb))
847 			error(Eio);
848 		if(off+n >= sizeof(f->ndb)-1)
849 			error(Eio);
850 		memmove(f->ndb+off, a, n);
851 		f->ndb[off+n] = 0;
852 		f->ndbvers++;
853 		f->ndbmtime = seconds();
854 		break;
855 	case Qctl:
856 		x = f->p[PROTO(ch->qid)];
857 		c = x->conv[CONV(ch->qid)];
858 		cb = parsecmd(a, n);
859 		qlock(&c->l);
860 		if(waserror()){
861 			qunlock(&c->l);
862 			free(cb);
863 			nexterror();
864 		}
865 		if(cb->nf < 1)
866 			error("short control request");
867 		if(strcmp(cb->f[0], "connect") == 0)
868 			connectctlmsg(x, c, cb);
869 		else if(strcmp(cb->f[0], "announce") == 0)
870 			announcectlmsg(x, c, cb);
871 		else if(strcmp(cb->f[0], "bind") == 0)
872 			bindctlmsg(x, c, cb);
873 		else if(strcmp(cb->f[0], "ttl") == 0){
874 			/* ignored */
875 		} else if(strcmp(cb->f[0], "tos") == 0){
876 			/* ignored */
877 		} else if(strcmp(cb->f[0], "ignoreadvice") == 0){
878 			/* ignored */
879 		} else if(strcmp(cb->f[0], "headers4") == 0){
880 			if(c->p->stype != S_UDP)
881 				error(Enoctl);
882 			c->headers = OUdphdrlenv4;
883 		} else if(strcmp(cb->f[0], "oldheaders") == 0){
884 			if(c->p->stype != S_UDP)
885 				error(Enoctl);
886 			c->headers = OUdphdrlen;
887 		} else if(strcmp(cb->f[0], "headers") == 0){
888 			if(c->p->stype != S_UDP)
889 				error(Enoctl);
890 			c->headers = Udphdrlen;
891 		} else if(strcmp(cb->f[0], "hangup") == 0){
892 			if(c->p->stype != S_TCP)
893 				error(Enoctl);
894 			qunlock(&c->l);
895 			if(waserror()){
896 				qlock(&c->l);
897 				nexterror();
898 			}
899 			/* TO DO: check fd status if socket close/hangup interrupted */
900 			if(c->sfd >= 0 && so_hangup(c->sfd, 1) < 0)
901 				oserror();
902 			qlock(&c->l);
903 			poperror();
904 			c->sfd = -1;
905 			c->state = Hungup;
906 		} else if(strcmp(cb->f[0], "keepalive") == 0){
907 			if(c->p->stype != S_TCP)
908 				error(Enoctl);
909 			if(c->sfd < 0)
910 				error("not connected");
911 			so_keepalive(c->sfd, cb->nf>1? atoi(cb->f[1]): 0);
912 		} else
913 			error(Enoctl);
914 		poperror();
915 		qunlock(&c->l);
916 		free(cb);
917 		break;
918 	}
919 	return n;
920 }
921 
922 static int
923 ipwstat(Chan *c, uchar *dp, int n)
924 {
925 	Dir *d;
926 	Conv *cv;
927 	Proto *p;
928 	Fs *f;
929 
930 	f = ipfs[c->dev];
931 	switch(TYPE(c->qid)) {
932 	default:
933 		error(Eperm);
934 		break;
935 	case Qctl:
936 	case Qdata:
937 		break;
938 	}
939 
940 	d = smalloc(sizeof(*d)+n);
941 	if(waserror()){
942 		free(d);
943 		nexterror();
944 	}
945 	n = convM2D(dp, n, d, (char*)&d[1]);
946 	if(n == 0)
947 		error(Eshortstat);
948 	p = f->p[PROTO(c->qid)];
949 	cv = p->conv[CONV(c->qid)];
950 	if(!iseve() && strcmp(up->env->user, cv->owner) != 0)
951 		error(Eperm);
952 	if(!emptystr(d->uid))
953 		kstrdup(&cv->owner, d->uid);
954 	if(d->mode != ~0UL)
955 		cv->perm = d->mode & 0777;
956 	poperror();
957 	free(d);
958 	return n;
959 }
960 
961 static Conv*
962 protoclone(Proto *p, char *user, int nfd)
963 {
964 	Conv *c, **pp, **ep, **np;
965 	int maxconv;
966 
967 	c = 0;
968 	qlock(&p->l);
969 	if(waserror()) {
970 		qunlock(&p->l);
971 		nexterror();
972 	}
973 	ep = &p->conv[p->nc];
974 	for(pp = p->conv; pp < ep; pp++) {
975 		c = *pp;
976 		if(c == 0) {
977 			c = newconv(p, pp);
978 			break;
979 		}
980 		if(canqlock(&c->l)){
981 			if(c->inuse == 0)
982 				break;
983 			qunlock(&c->l);
984 		}
985 	}
986 	if(pp >= ep) {
987 		if(p->nc >= MAXCONV) {
988 			qunlock(&p->l);
989 			poperror();
990 			return 0;
991 		}
992 		maxconv = 2 * p->nc;
993 		if(maxconv > MAXCONV)
994 			maxconv = MAXCONV;
995 		np = realloc(p->conv, sizeof(Conv*) * maxconv);
996 		if(np == nil)
997 			error(Enomem);
998 		p->conv = np;
999 		pp = &p->conv[p->nc];
1000 		memset(pp, 0, sizeof(Conv*)*(maxconv - p->nc));
1001 		p->nc = maxconv;
1002 		c = newconv(p, pp);
1003 	}
1004 
1005 	c->inuse = 1;
1006 	kstrdup(&c->owner, user);
1007 	c->perm = 0660;
1008 	c->state = Idle;
1009 	ipmove(c->laddr, IPnoaddr);
1010 	ipmove(c->raddr, IPnoaddr);
1011 	c->lport = 0;
1012 	c->rport = 0;
1013 	c->restricted = 0;
1014 	c->headers = 0;
1015 	c->sfd = nfd;
1016 	if(nfd == -1)
1017 		c->sfd = so_socket(p->stype);
1018 
1019 	qunlock(&c->l);
1020 	qunlock(&p->l);
1021 	poperror();
1022 	return c;
1023 }
1024 
1025 static Conv*
1026 newconv(Proto *p, Conv **pp)
1027 {
1028 	Conv *c;
1029 
1030 	*pp = c = malloc(sizeof(Conv));
1031 	if(c == 0)
1032 		error(Enomem);
1033 	qlock(&c->l);
1034 	c->inuse = 1;
1035 	c->p = p;
1036 	c->x = pp - p->conv;
1037 	p->ac++;
1038 	return c;
1039 }
1040 
1041 int
1042 arpwrite(char *s, int len)
1043 {
1044 	int n;
1045 	char *f[4], buf[256];
1046 
1047 	if(len >= sizeof(buf))
1048 		len = sizeof(buf)-1;
1049 	memmove(buf, s, len);
1050 	buf[len] = 0;
1051 	if(len > 0 && buf[len-1] == '\n')
1052 		buf[len-1] = 0;
1053 
1054 	n = getfields(buf, f, 4, 1, " ");
1055 	if(strcmp(f[0], "add") == 0) {
1056 		if(n == 3) {
1057 			arpadd(f[1], f[2], n);
1058 			return len;
1059 		}
1060 	}
1061 	error("bad arp request");
1062 
1063 	return len;
1064 }
1065 
1066 Dev ipdevtab = {
1067 	'I',
1068 	"ip",
1069 
1070 	ipinit,
1071 	ipattach,
1072 	ipwalk,
1073 	ipstat,
1074 	ipopen,
1075 	devcreate,
1076 	ipclose,
1077 	ipread,
1078 	devbread,
1079 	ipwrite,
1080 	devbwrite,
1081 	devremove,
1082 	ipwstat
1083 };
1084 
1085 int
1086 Fsproto(Fs *f, Proto *p)
1087 {
1088 	if(f->np >= Maxproto)
1089 		return -1;
1090 
1091 	p->f = f;
1092 
1093 	if(p->ipproto > 0){
1094 		if(f->t2p[p->ipproto] != nil)
1095 			return -1;
1096 		f->t2p[p->ipproto] = p;
1097 	}
1098 
1099 	p->qid.type = QTDIR;
1100 	p->qid.path = QID(f->np, 0, Qprotodir);
1101 	p->conv = malloc(sizeof(Conv*)*(p->nc+1));
1102 	if(p->conv == nil)
1103 		panic("Fsproto");
1104 
1105 	p->x = f->np;
1106 	f->p[f->np++] = p;
1107 
1108 	return 0;
1109 }
1110 
1111 /*
1112  *  return true if this protocol is
1113  *  built in
1114  */
1115 int
1116 Fsbuiltinproto(Fs* f, uchar proto)
1117 {
1118 	return f->t2p[proto] != nil;
1119 }
1120