xref: /inferno-os/emu/port/devip.c (revision 4eb166cf184c1f102fb79e31b1465ea3e2021c39)
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 before connect/accept */
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 	ushort p;
630 
631 	so_bind(c->sfd, c->restricted, ip6w(c->laddr), c->lport);
632 	if(c->lport == 0  || ipcmp(c->laddr, IPnoaddr) == 0){
633 		so_getsockname(c->sfd, &laddr, &p);
634 		if(c->lport == 0)
635 			c->lport = p;
636 		if(ipcmp(c->laddr, IPnoaddr) == 0)
637 			ipw6(c->laddr, laddr);
638 	}
639 }
640 
641 static int
642 portno(char *p)
643 {
644 	long n;
645 	char *e;
646 
647 	n = strtoul(p, &e, 0);
648 	if(p == e)
649 		error("non-numeric port number");
650 	return n;
651 }
652 
653 /*
654  *  set a local address and port from a string of the form
655  *	[address!]port[!r]
656  */
657 static void
658 setladdrport(Conv *c, char *str, int announcing)
659 {
660 	char *p;
661 	int lport;
662 
663 	/*
664 	 *  ignore restricted part if it exists.  it's
665 	 *  meaningless on local ports.
666 	 */
667 	p = strchr(str, '!');
668 	if(p != nil){
669 		*p++ = 0;
670 		if(strcmp(p, "r") == 0)
671 			p = nil;
672 	}
673 
674 	c->lport = 0;
675 	if(p == nil){
676 		if(announcing)
677 			ipmove(c->laddr, IPnoaddr);
678 		else if(0)
679 			setladdr(c);
680 		p = str;
681 	} else {
682 		if(strcmp(str, "*") == 0)
683 			ipmove(c->laddr, IPnoaddr);
684 		else if(parseip(c->laddr, str) == 0)
685 			error("invalid IP address");
686 	}
687 
688 	if(announcing && strcmp(p, "*") == 0){
689 		if(!iseve())
690 			error(Eperm);
691 		c->lport = 0;
692 		setlport(c);
693 		return;
694 	}
695 
696 	lport = portno(p);
697 	if(lport <= 0)
698 		c->lport = 0;
699 	else
700 		c->lport = lport;
701 
702 	setlport(c);
703 }
704 
705 static char*
706 setraddrport(Conv *c, char *str)
707 {
708 	char *p;
709 
710 	p = strchr(str, '!');
711 	if(p == nil)
712 		return "malformed address";
713 	*p++ = 0;
714 	if(parseip(c->raddr, str) == 0)
715 		return "invalid IP address";
716 	c->rport = portno(p);
717 	p = strchr(p, '!');
718 	if(p){
719 		if(strstr(p, "!r") != nil)
720 			c->restricted = 1;
721 	}
722 	return nil;
723 }
724 
725 static void
726 connectctlmsg(Proto *x, Conv *c, Cmdbuf *cb)
727 {
728 	char *p;
729 
730 	USED(x);
731 	if(c->state != Idle)
732 		error(Econinuse);
733 	c->state = Connecting;
734 	c->cerr[0] = '\0';
735 	switch(cb->nf) {
736 	default:
737 		error("bad args to connect");
738 	case 2:
739 		p = setraddrport(c, cb->f[1]);
740 		if(p != nil)
741 			error(p);
742 		break;
743 	case 3:
744 		p = setraddrport(c, cb->f[1]);
745 		if(p != nil)
746 			error(p);
747 		c->lport = portno(cb->f[2]);
748 		setlport(c);
749 		break;
750 	}
751 	qunlock(&c->l);
752 	if(waserror()){
753 		qlock(&c->l);
754 		c->state = Connected;	/* sic */
755 		nexterror();
756 	}
757 	/* p = x->connect(c, cb->f, cb->nf); */
758 	so_connect(c->sfd, ip6w(c->raddr), c->rport);
759 	qlock(&c->l);
760 	poperror();
761 	setladdr(c);
762 	c->state = Connected;
763 }
764 
765 static void
766 announcectlmsg(Proto *x, Conv *c, Cmdbuf *cb)
767 {
768 	if(c->state != Idle)
769 		error(Econinuse);
770 	c->state = Announcing;
771 	c->cerr[0] = '\0';
772 	ipmove(c->raddr, IPnoaddr);
773 	c->rport = 0;
774 	switch(cb->nf){
775 	default:
776 		error("bad args to announce");
777 	case 2:
778 		setladdrport(c, cb->f[1], 1);
779 		break;
780 	}
781 	USED(x);
782 	/* p = x->announce(c, cb->f, cb->nf); */
783 	if(c->p->stype != S_UDP){
784 		qunlock(&c->l);
785 		if(waserror()){
786 			c->state = Announced;	/* sic */
787 			qlock(&c->l);
788 			nexterror();
789 		}
790 		so_listen(c->sfd);
791 		qlock(&c->l);
792 		poperror();
793 	}
794 	c->state = Announced;
795 }
796 
797 static void
798 bindctlmsg(Proto *x, Conv *c, Cmdbuf *cb)
799 {
800 	USED(x);
801 	switch(cb->nf){
802 	default:
803 		error("bad args to bind");
804 	case 2:
805 		setladdrport(c, cb->f[1], 0);
806 		break;
807 	}
808 }
809 
810 static long
811 ipwrite(Chan *ch, void *a, long n, vlong off)
812 {
813 	Conv *c;
814 	Proto *x;
815 	char *p;
816 	Cmdbuf *cb;
817 	Fs *f;
818 
819 	f = ipfs[ch->dev];
820 
821 	switch(TYPE(ch->qid)) {
822 	default:
823 		error(Eperm);
824 	case Qdata:
825 		x = f->p[PROTO(ch->qid)];
826 		c = x->conv[CONV(ch->qid)];
827 		if(c->sfd < 0)
828 			error(Ehungup);
829 		qlock(&c->wlock);
830 		if(waserror()){
831 			qunlock(&c->wlock);
832 			nexterror();
833 		}
834 		if(c->headers) {
835 			if(n < c->headers)
836 				error(Eshort);
837 			p = a;
838 			n = so_send(c->sfd, p + c->headers, n - c->headers, p, c->headers);
839 			if(n >= 0)
840 				n += c->headers;
841 		} else
842 			n = so_send(c->sfd, a, n, nil, 0);
843 		poperror();
844 		qunlock(&c->wlock);
845 		if(n < 0)
846 			oserror();
847 		break;
848 	case Qarp:
849 		return arpwrite(a, n);
850 	case Qndb:
851 		if(off > strlen(f->ndb))
852 			error(Eio);
853 		if(off+n >= sizeof(f->ndb)-1)
854 			error(Eio);
855 		memmove(f->ndb+off, a, n);
856 		f->ndb[off+n] = 0;
857 		f->ndbvers++;
858 		f->ndbmtime = seconds();
859 		break;
860 	case Qctl:
861 		x = f->p[PROTO(ch->qid)];
862 		c = x->conv[CONV(ch->qid)];
863 		cb = parsecmd(a, n);
864 		qlock(&c->l);
865 		if(waserror()){
866 			qunlock(&c->l);
867 			free(cb);
868 			nexterror();
869 		}
870 		if(cb->nf < 1)
871 			error("short control request");
872 		if(strcmp(cb->f[0], "connect") == 0)
873 			connectctlmsg(x, c, cb);
874 		else if(strcmp(cb->f[0], "announce") == 0)
875 			announcectlmsg(x, c, cb);
876 		else if(strcmp(cb->f[0], "bind") == 0)
877 			bindctlmsg(x, c, cb);
878 		else if(strcmp(cb->f[0], "ttl") == 0){
879 			/* ignored */
880 		} else if(strcmp(cb->f[0], "tos") == 0){
881 			/* ignored */
882 		} else if(strcmp(cb->f[0], "ignoreadvice") == 0){
883 			/* ignored */
884 		} else if(strcmp(cb->f[0], "headers4") == 0){
885 			if(c->p->stype != S_UDP)
886 				error(Enoctl);
887 			c->headers = OUdphdrlenv4;
888 		} else if(strcmp(cb->f[0], "oldheaders") == 0){
889 			if(c->p->stype != S_UDP)
890 				error(Enoctl);
891 			c->headers = OUdphdrlen;
892 		} else if(strcmp(cb->f[0], "headers") == 0){
893 			if(c->p->stype != S_UDP)
894 				error(Enoctl);
895 			c->headers = Udphdrlen;
896 		} else if(strcmp(cb->f[0], "hangup") == 0){
897 			if(c->p->stype != S_TCP)
898 				error(Enoctl);
899 			qunlock(&c->l);
900 			if(waserror()){
901 				qlock(&c->l);
902 				nexterror();
903 			}
904 			/* TO DO: check fd status if socket close/hangup interrupted */
905 			if(c->sfd >= 0 && so_hangup(c->sfd, 1) < 0)
906 				oserror();
907 			qlock(&c->l);
908 			poperror();
909 			c->sfd = -1;
910 			c->state = Hungup;
911 		} else if(strcmp(cb->f[0], "keepalive") == 0){
912 			if(c->p->stype != S_TCP)
913 				error(Enoctl);
914 			if(c->sfd < 0)
915 				error("not connected");
916 			so_keepalive(c->sfd, cb->nf>1? atoi(cb->f[1]): 0);
917 		} else
918 			error(Enoctl);
919 		poperror();
920 		qunlock(&c->l);
921 		free(cb);
922 		break;
923 	}
924 	return n;
925 }
926 
927 static int
928 ipwstat(Chan *c, uchar *dp, int n)
929 {
930 	Dir *d;
931 	Conv *cv;
932 	Proto *p;
933 	Fs *f;
934 
935 	f = ipfs[c->dev];
936 	switch(TYPE(c->qid)) {
937 	default:
938 		error(Eperm);
939 		break;
940 	case Qctl:
941 	case Qdata:
942 		break;
943 	}
944 
945 	d = smalloc(sizeof(*d)+n);
946 	if(waserror()){
947 		free(d);
948 		nexterror();
949 	}
950 	n = convM2D(dp, n, d, (char*)&d[1]);
951 	if(n == 0)
952 		error(Eshortstat);
953 	p = f->p[PROTO(c->qid)];
954 	cv = p->conv[CONV(c->qid)];
955 	if(!iseve() && strcmp(up->env->user, cv->owner) != 0)
956 		error(Eperm);
957 	if(!emptystr(d->uid))
958 		kstrdup(&cv->owner, d->uid);
959 	if(d->mode != ~0UL)
960 		cv->perm = d->mode & 0777;
961 	poperror();
962 	free(d);
963 	return n;
964 }
965 
966 static Conv*
967 protoclone(Proto *p, char *user, int nfd)
968 {
969 	Conv *c, **pp, **ep, **np;
970 	int maxconv;
971 
972 	c = 0;
973 	qlock(&p->l);
974 	if(waserror()) {
975 		qunlock(&p->l);
976 		nexterror();
977 	}
978 	ep = &p->conv[p->nc];
979 	for(pp = p->conv; pp < ep; pp++) {
980 		c = *pp;
981 		if(c == 0) {
982 			c = newconv(p, pp);
983 			break;
984 		}
985 		if(canqlock(&c->l)){
986 			if(c->inuse == 0)
987 				break;
988 			qunlock(&c->l);
989 		}
990 	}
991 	if(pp >= ep) {
992 		if(p->nc >= MAXCONV) {
993 			qunlock(&p->l);
994 			poperror();
995 			return 0;
996 		}
997 		maxconv = 2 * p->nc;
998 		if(maxconv > MAXCONV)
999 			maxconv = MAXCONV;
1000 		np = realloc(p->conv, sizeof(Conv*) * maxconv);
1001 		if(np == nil)
1002 			error(Enomem);
1003 		p->conv = np;
1004 		pp = &p->conv[p->nc];
1005 		memset(pp, 0, sizeof(Conv*)*(maxconv - p->nc));
1006 		p->nc = maxconv;
1007 		c = newconv(p, pp);
1008 	}
1009 
1010 	c->inuse = 1;
1011 	kstrdup(&c->owner, user);
1012 	c->perm = 0660;
1013 	c->state = Idle;
1014 	ipmove(c->laddr, IPnoaddr);
1015 	ipmove(c->raddr, IPnoaddr);
1016 	c->lport = 0;
1017 	c->rport = 0;
1018 	c->restricted = 0;
1019 	c->sfd = nfd;
1020 	if(nfd == -1)
1021 		c->sfd = so_socket(p->stype);
1022 
1023 	qunlock(&c->l);
1024 	qunlock(&p->l);
1025 	poperror();
1026 	return c;
1027 }
1028 
1029 static Conv*
1030 newconv(Proto *p, Conv **pp)
1031 {
1032 	Conv *c;
1033 
1034 	*pp = c = malloc(sizeof(Conv));
1035 	if(c == 0)
1036 		error(Enomem);
1037 	qlock(&c->l);
1038 	c->inuse = 1;
1039 	c->p = p;
1040 	c->x = pp - p->conv;
1041 	p->ac++;
1042 	return c;
1043 }
1044 
1045 int
1046 arpwrite(char *s, int len)
1047 {
1048 	int n;
1049 	char *f[4], buf[256];
1050 
1051 	if(len >= sizeof(buf))
1052 		len = sizeof(buf)-1;
1053 	memmove(buf, s, len);
1054 	buf[len] = 0;
1055 	if(len > 0 && buf[len-1] == '\n')
1056 		buf[len-1] = 0;
1057 
1058 	n = getfields(buf, f, 4, 1, " ");
1059 	if(strcmp(f[0], "add") == 0) {
1060 		if(n == 3) {
1061 			arpadd(f[1], f[2], n);
1062 			return len;
1063 		}
1064 	}
1065 	error("bad arp request");
1066 
1067 	return len;
1068 }
1069 
1070 Dev ipdevtab = {
1071 	'I',
1072 	"ip",
1073 
1074 	ipinit,
1075 	ipattach,
1076 	ipwalk,
1077 	ipstat,
1078 	ipopen,
1079 	devcreate,
1080 	ipclose,
1081 	ipread,
1082 	devbread,
1083 	ipwrite,
1084 	devbwrite,
1085 	devremove,
1086 	ipwstat
1087 };
1088 
1089 int
1090 Fsproto(Fs *f, Proto *p)
1091 {
1092 	if(f->np >= Maxproto)
1093 		return -1;
1094 
1095 	p->f = f;
1096 
1097 	if(p->ipproto > 0){
1098 		if(f->t2p[p->ipproto] != nil)
1099 			return -1;
1100 		f->t2p[p->ipproto] = p;
1101 	}
1102 
1103 	p->qid.type = QTDIR;
1104 	p->qid.path = QID(f->np, 0, Qprotodir);
1105 	p->conv = malloc(sizeof(Conv*)*(p->nc+1));
1106 	if(p->conv == nil)
1107 		panic("Fsproto");
1108 
1109 	p->x = f->np;
1110 	f->p[f->np++] = p;
1111 
1112 	return 0;
1113 }
1114 
1115 /*
1116  *  return true if this protocol is
1117  *  built in
1118  */
1119 int
1120 Fsbuiltinproto(Fs* f, uchar proto)
1121 {
1122 	return f->t2p[proto] != nil;
1123 }
1124 
1125 /*
1126  * temporarily convert ipv6 addresses to ipv4 as ulong for
1127  * ipif.c interface
1128  */
1129 static ulong
1130 ip6w(uchar *a)
1131 {
1132 	uchar v4[IPv4addrlen];
1133 
1134 	v6tov4(v4, a);
1135 	return (((((v4[0]<<8)|v4[1])<<8)|v4[2])<<8)|v4[3];
1136 }
1137 
1138 static void
1139 ipw6(uchar *a, ulong w)
1140 {
1141 	memmove(a, v4prefix, IPv4off);
1142 	hnputl(a+IPv4off, w);
1143 }
1144