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