xref: /inferno-os/os/port/netif.c (revision d0e1d143ef6f03c75c008c7ec648859dd260cbab)
1 #include	"u.h"
2 #include	"../port/lib.h"
3 #include	"mem.h"
4 #include	"dat.h"
5 #include	"fns.h"
6 #include	"../port/error.h"
7 #include	"../port/netif.h"
8 
9 static int netown(Netfile*, char*, int);
10 static int openfile(Netif*, int);
11 static char* matchtoken(char*, char*);
12 static char* netmulti(Netif*, Netfile*, uchar*, int);
13 static int parseaddr(uchar*, char*, int);
14 
15 /*
16  *  set up a new network interface
17  */
18 void
19 netifinit(Netif *nif, char *name, int nfile, ulong limit)
20 {
21 	strncpy(nif->name, name, KNAMELEN-1);
22 	nif->name[KNAMELEN-1] = 0;
23 	nif->nfile = nfile;
24 	nif->f = malloc(nfile*sizeof(Netfile*));
25 	if(nif->f)
26 		memset(nif->f, 0, nfile*sizeof(Netfile*));
27 	else
28 		nif->nfile = 0;
29 	nif->limit = limit;
30 }
31 
32 /*
33  *  generate a 3 level directory
34  */
35 static int
36 netifgen(Chan *c, char*, Dirtab *vp, int, int i, Dir *dp)
37 {
38 	Qid q;
39 	Netif *nif = (Netif*)vp;
40 	Netfile *f;
41 	int t;
42 	int perm;
43 	char *o;
44 
45 	q.type = QTFILE;
46 	q.vers = 0;
47 
48 	/* top level directory contains the name of the network */
49 	if(c->qid.path == 0){
50 		switch(i){
51 		case DEVDOTDOT:
52 			q.path = 0;
53 			q.type = QTDIR;
54 			devdir(c, q, ".", 0, eve, 0555, dp);
55 			break;
56 		case 0:
57 			q.path = N2ndqid;
58 			q.type = QTDIR;
59 			strcpy(up->genbuf, nif->name);
60 			devdir(c, q, up->genbuf, 0, eve, 0555, dp);
61 			break;
62 		default:
63 			return -1;
64 		}
65 		return 1;
66 	}
67 
68 	/* second level contains clone plus all the conversations */
69 	t = NETTYPE(c->qid.path);
70 	if(t == N2ndqid || t == Ncloneqid || t == Naddrqid){
71 		switch(i) {
72 		case DEVDOTDOT:
73 			q.type = QTDIR;
74 			q.path = 0;
75 			devdir(c, q, ".", 0, eve, DMDIR|0555, dp);
76 			break;
77 		case 0:
78 			q.path = Ncloneqid;
79 			devdir(c, q, "clone", 0, eve, 0666, dp);
80 			break;
81 		case 1:
82 			q.path = Naddrqid;
83 			devdir(c, q, "addr", 0, eve, 0666, dp);
84 			break;
85 		case 2:
86 			q.path = Nstatqid;
87 			devdir(c, q, "stats", 0, eve, 0444, dp);
88 			break;
89 		case 3:
90 			q.path = Nifstatqid;
91 			devdir(c, q, "ifstats", 0, eve, 0444, dp);
92 			break;
93 		default:
94 			i -= 4;
95 			if(i >= nif->nfile)
96 				return -1;
97 			if(nif->f[i] == 0)
98 				return 0;
99 			q.type = QTDIR;
100 			q.path = NETQID(i, N3rdqid);
101 			sprint(up->genbuf, "%d", i);
102 			devdir(c, q, up->genbuf, 0, eve, DMDIR|0555, dp);
103 			break;
104 		}
105 		return 1;
106 	}
107 
108 	/* third level */
109 	f = nif->f[NETID(c->qid.path)];
110 	if(f == 0)
111 		return 0;
112 	if(*f->owner){
113 		o = f->owner;
114 		perm = f->mode;
115 	} else {
116 		o = eve;
117 		perm = 0666;
118 	}
119 	switch(i){
120 	case DEVDOTDOT:
121 		q.type = QTDIR;
122 		q.path = N2ndqid;
123 		strcpy(up->genbuf, nif->name);
124 		devdir(c, q, up->genbuf, 0, eve, DMDIR|0555, dp);
125 		break;
126 	case 0:
127 		q.path = NETQID(NETID(c->qid.path), Ndataqid);
128 		devdir(c, q, "data", 0, o, perm, dp);
129 		break;
130 	case 1:
131 		q.path = NETQID(NETID(c->qid.path), Nctlqid);
132 		devdir(c, q, "ctl", 0, o, perm, dp);
133 		break;
134 	case 2:
135 		q.path = NETQID(NETID(c->qid.path), Nstatqid);
136 		devdir(c, q, "stats", 0, eve, 0444, dp);
137 		break;
138 	case 3:
139 		q.path = NETQID(NETID(c->qid.path), Ntypeqid);
140 		devdir(c, q, "type", 0, eve, 0444, dp);
141 		break;
142 	case 4:
143 		q.path = NETQID(NETID(c->qid.path), Nifstatqid);
144 		devdir(c, q, "ifstats", 0, eve, 0444, dp);
145 		break;
146 	default:
147 		return -1;
148 	}
149 	return 1;
150 }
151 
152 Walkqid*
153 netifwalk(Netif *nif, Chan *c, Chan *nc, char **name, int nname)
154 {
155 	return devwalk(c, nc, name, nname, (Dirtab *)nif, 0, netifgen);
156 }
157 
158 Chan*
159 netifopen(Netif *nif, Chan *c, int omode)
160 {
161 	int id;
162 	Netfile *f;
163 
164 	id = 0;
165 	if(c->qid.type & QTDIR){
166 		if(omode != OREAD)
167 			error(Eperm);
168 	} else {
169 		switch(NETTYPE(c->qid.path)){
170 		case Ndataqid:
171 		case Nctlqid:
172 			id = NETID(c->qid.path);
173 			openfile(nif, id);
174 			break;
175 		case Ncloneqid:
176 			id = openfile(nif, -1);
177 			c->qid.path = NETQID(id, Nctlqid);
178 			break;
179 		default:
180 			if(omode != OREAD)
181 				error(Ebadarg);
182 		}
183 		switch(NETTYPE(c->qid.path)){
184 		case Ndataqid:
185 		case Nctlqid:
186 			f = nif->f[id];
187 			if(netown(f, up->env->user, omode&7) < 0)
188 				error(Eperm);
189 			break;
190 		}
191 	}
192 	c->mode = openmode(omode);
193 	c->flag |= COPEN;
194 	c->offset = 0;
195 	c->iounit = qiomaxatomic;
196 	return c;
197 }
198 
199 long
200 netifread(Netif *nif, Chan *c, void *a, long n, ulong offset)
201 {
202 	int i, j;
203 	Netfile *f;
204 	char *p;
205 
206 	if(c->qid.type&QTDIR)
207 		return devdirread(c, a, n, (Dirtab*)nif, 0, netifgen);
208 
209 	switch(NETTYPE(c->qid.path)){
210 	case Ndataqid:
211 		f = nif->f[NETID(c->qid.path)];
212 		return qread(f->in, a, n);
213 	case Nctlqid:
214 		return readnum(offset, a, n, NETID(c->qid.path), NUMSIZE);
215 	case Nstatqid:
216 		p = malloc(READSTR);
217 		if(p == nil)
218 			return 0;
219 		j = snprint(p, READSTR, "in: %d\n", nif->inpackets);
220 		j += snprint(p+j, READSTR-j, "link: %d\n", nif->link);
221 		j += snprint(p+j, READSTR-j, "out: %d\n", nif->outpackets);
222 		j += snprint(p+j, READSTR-j, "crc errs: %d\n", nif->crcs);
223 		j += snprint(p+j, READSTR-j, "overflows: %d\n", nif->overflows);
224 		j += snprint(p+j, READSTR-j, "soft overflows: %d\n", nif->soverflows);
225 		j += snprint(p+j, READSTR-j, "framing errs: %d\n", nif->frames);
226 		j += snprint(p+j, READSTR-j, "buffer errs: %d\n", nif->buffs);
227 		j += snprint(p+j, READSTR-j, "output errs: %d\n", nif->oerrs);
228 		j += snprint(p+j, READSTR-j, "prom: %d\n", nif->prom);
229 		j += snprint(p+j, READSTR-j, "mbps: %d\n", nif->mbps);
230 		j += snprint(p+j, READSTR-j, "addr: ");
231 		for(i = 0; i < nif->alen; i++)
232 			j += snprint(p+j, READSTR-j, "%2.2ux", nif->addr[i]);
233 		snprint(p+j, READSTR-j, "\n");
234 		n = readstr(offset, a, n, p);
235 		free(p);
236 		return n;
237 	case Naddrqid:
238 		p = malloc(READSTR);
239 		if(p == nil)
240 			return 0;
241 		j = 0;
242 		for(i = 0; i < nif->alen; i++)
243 			j += snprint(p+j, READSTR-j, "%2.2ux", nif->addr[i]);
244 		n = readstr(offset, a, n, p);
245 		free(p);
246 		return n;
247 	case Ntypeqid:
248 		f = nif->f[NETID(c->qid.path)];
249 		return readnum(offset, a, n, f->type, NUMSIZE);
250 	case Nifstatqid:
251 		return 0;
252 	}
253 	error(Ebadarg);
254 	return -1;	/* not reached */
255 }
256 
257 Block*
258 netifbread(Netif *nif, Chan *c, long n, ulong offset)
259 {
260 	if((c->qid.type & QTDIR) || NETTYPE(c->qid.path) != Ndataqid)
261 		return devbread(c, n, offset);
262 
263 	return qbread(nif->f[NETID(c->qid.path)]->in, n);
264 }
265 
266 /*
267  *  make sure this type isn't already in use on this device
268  */
269 static int
270 typeinuse(Netif *nif, int type)
271 {
272 	Netfile *f, **fp, **efp;
273 
274 	if(type <= 0)
275 		return 0;
276 
277 	efp = &nif->f[nif->nfile];
278 	for(fp = nif->f; fp < efp; fp++){
279 		f = *fp;
280 		if(f == 0)
281 			continue;
282 		if(f->type == type)
283 			return 1;
284 	}
285 	return 0;
286 }
287 
288 /*
289  *  the devxxx.c that calls us handles writing data, it knows best
290  */
291 long
292 netifwrite(Netif *nif, Chan *c, void *a, long n)
293 {
294 	Netfile *f;
295 	int type;
296 	char *p, buf[64];
297 	uchar binaddr[Nmaxaddr];
298 
299 	if(NETTYPE(c->qid.path) != Nctlqid)
300 		error(Eperm);
301 
302 	if(n >= sizeof(buf))
303 		n = sizeof(buf)-1;
304 	memmove(buf, a, n);
305 	buf[n] = 0;
306 
307 	if(waserror()){
308 		qunlock(nif);
309 		nexterror();
310 	}
311 
312 	qlock(nif);
313 	f = nif->f[NETID(c->qid.path)];
314 	if((p = matchtoken(buf, "connect")) != 0){
315 		type = atoi(p);
316 		if(typeinuse(nif, type))
317 			error(Einuse);
318 		f->type = type;
319 		if(f->type < 0)
320 			nif->all++;
321 	} else if(matchtoken(buf, "promiscuous")){
322 		if(f->prom == 0){
323 			if(nif->prom == 0 && nif->promiscuous != nil)
324 				nif->promiscuous(nif->arg, 1);
325 			f->prom = 1;
326 			nif->prom++;
327 		}
328 	} else if((p = matchtoken(buf, "scanbs")) != 0){
329 		/* scan for base stations */
330 		if(f->scan == 0){
331 			type = atoi(p);
332 			if(type < 5)
333 				type = 5;
334 			if(nif->scanbs != nil)
335 				nif->scanbs(nif->arg, type);
336 			f->scan = type;
337 			nif->scan++;
338 		}
339 	} else if(matchtoken(buf, "bridge")){
340 		f->bridge = 1;
341 	} else if(matchtoken(buf, "headersonly")){
342 		f->headersonly = 1;
343 	} else if((p = matchtoken(buf, "addmulti")) != 0){
344 		if(parseaddr(binaddr, p, nif->alen) < 0)
345 			error("bad address");
346 		p = netmulti(nif, f, binaddr, 1);
347 		if(p)
348 			error(p);
349 	} else if((p = matchtoken(buf, "remmulti")) != 0){
350 		if(parseaddr(binaddr, p, nif->alen) < 0)
351 			error("bad address");
352 		p = netmulti(nif, f, binaddr, 0);
353 		if(p)
354 			error(p);
355 	} else
356 		n = -1;
357 	qunlock(nif);
358 	poperror();
359 	return n;
360 }
361 
362 int
363 netifwstat(Netif *nif, Chan *c, uchar *db, int n)
364 {
365 	Dir *dir;
366 	Netfile *f;
367 	int m;
368 
369 	f = nif->f[NETID(c->qid.path)];
370 	if(f == 0)
371 		error(Enonexist);
372 
373 	if(netown(f, up->env->user, OWRITE) < 0)
374 		error(Eperm);
375 
376 	dir = smalloc(sizeof(Dir)+n);
377 	m = convM2D(db, n, &dir[0], (char*)&dir[1]);
378 	if(m == 0){
379 		free(dir);
380 		error(Eshortstat);
381 	}
382 	if(!emptystr(dir[0].uid))
383 		strncpy(f->owner, dir[0].uid, KNAMELEN);
384 	if(dir[0].mode != ~0UL)
385 		f->mode = dir[0].mode;
386 	free(dir);
387 	return m;
388 }
389 
390 int
391 netifstat(Netif *nif, Chan *c, uchar *db, int n)
392 {
393 	return devstat(c, db, n, (Dirtab *)nif, 0, netifgen);
394 }
395 
396 void
397 netifclose(Netif *nif, Chan *c)
398 {
399 	Netfile *f;
400 	int t;
401 	Netaddr *ap;
402 
403 	if((c->flag & COPEN) == 0)
404 		return;
405 
406 	t = NETTYPE(c->qid.path);
407 	if(t != Ndataqid && t != Nctlqid)
408 		return;
409 
410 	f = nif->f[NETID(c->qid.path)];
411 	qlock(f);
412 	if(--(f->inuse) == 0){
413 		if(f->prom){
414 			qlock(nif);
415 			if(--(nif->prom) == 0 && nif->promiscuous != nil)
416 				nif->promiscuous(nif->arg, 0);
417 			qunlock(nif);
418 			f->prom = 0;
419 		}
420 		if(f->scan){
421 			qlock(nif);
422 			if(--(nif->scan) == 0 && nif->scanbs != nil)
423 				nif->scanbs(nif->arg, 0);
424 			qunlock(nif);
425 			f->prom = 0;
426 			f->scan = 0;
427 		}
428 		if(f->nmaddr){
429 			qlock(nif);
430 			t = 0;
431 			for(ap = nif->maddr; ap; ap = ap->next){
432 				if(f->maddr[t/8] & (1<<(t%8)))
433 					netmulti(nif, f, ap->addr, 0);
434 			}
435 			qunlock(nif);
436 			f->nmaddr = 0;
437 		}
438 		if(f->type < 0){
439 			qlock(nif);
440 			--(nif->all);
441 			qunlock(nif);
442 		}
443 		f->owner[0] = 0;
444 		f->type = 0;
445 		f->bridge = 0;
446 		f->headersonly = 0;
447 		qclose(f->in);
448 	}
449 	qunlock(f);
450 }
451 
452 Lock netlock;
453 
454 static int
455 netown(Netfile *p, char *o, int omode)
456 {
457 	static int access[] = { 0400, 0200, 0600, 0100 };
458 	int mode;
459 	int t;
460 
461 	lock(&netlock);
462 	if(*p->owner){
463 		if(strncmp(o, p->owner, KNAMELEN) == 0)	/* User */
464 			mode = p->mode;
465 		else if(strncmp(o, eve, KNAMELEN) == 0)	/* Bootes is group */
466 			mode = p->mode<<3;
467 		else
468 			mode = p->mode<<6;		/* Other */
469 
470 		t = access[omode&3];
471 		if((t & mode) == t){
472 			unlock(&netlock);
473 			return 0;
474 		} else {
475 			unlock(&netlock);
476 			return -1;
477 		}
478 	}
479 	strncpy(p->owner, o, KNAMELEN);
480 	p->mode = 0660;
481 	unlock(&netlock);
482 	return 0;
483 }
484 
485 /*
486  *  Increment the reference count of a network device.
487  *  If id < 0, return an unused ether device.
488  */
489 static int
490 openfile(Netif *nif, int id)
491 {
492 	Netfile *f, **fp, **efp;
493 
494 	if(id >= 0){
495 		f = nif->f[id];
496 		if(f == 0)
497 			error(Enodev);
498 		qlock(f);
499 		qreopen(f->in);
500 		f->inuse++;
501 		qunlock(f);
502 		return id;
503 	}
504 
505 	qlock(nif);
506 	if(waserror()){
507 		qunlock(nif);
508 		nexterror();
509 	}
510 	efp = &nif->f[nif->nfile];
511 	for(fp = nif->f; fp < efp; fp++){
512 		f = *fp;
513 		if(f == 0){
514 			f = malloc(sizeof(Netfile));
515 			if(f == 0)
516 				exhausted("memory");
517 			f->in = qopen(nif->limit, Qmsg, 0, 0);
518 			if(f->in == nil){
519 				free(f);
520 				exhausted("memory");
521 			}
522 			*fp = f;
523 			qlock(f);
524 		} else {
525 			qlock(f);
526 			if(f->inuse){
527 				qunlock(f);
528 				continue;
529 			}
530 		}
531 		f->inuse = 1;
532 		qreopen(f->in);
533 		netown(f, up->env->user, 0);
534 		qunlock(f);
535 		qunlock(nif);
536 		poperror();
537 		return fp - nif->f;
538 	}
539 	error(Enodev);
540 	return -1;	/* not reached */
541 }
542 
543 /*
544  *  look for a token starting a string,
545  *  return a pointer to first non-space char after it
546  */
547 static char*
548 matchtoken(char *p, char *token)
549 {
550 	int n;
551 
552 	n = strlen(token);
553 	if(strncmp(p, token, n))
554 		return 0;
555 	p += n;
556 	if(*p == 0)
557 		return p;
558 	if(*p != ' ' && *p != '\t' && *p != '\n')
559 		return 0;
560 	while(*p == ' ' || *p == '\t' || *p == '\n')
561 		p++;
562 	return p;
563 }
564 
565 static ulong
566 hash(uchar *a, int len)
567 {
568 	ulong sum = 0;
569 
570 	while(len-- > 0)
571 		sum = (sum << 1) + *a++;
572 	return sum%Nmhash;
573 }
574 
575 int
576 activemulti(Netif *nif, uchar *addr, int alen)
577 {
578 	Netaddr *hp;
579 
580 	for(hp = nif->mhash[hash(addr, alen)]; hp; hp = hp->hnext)
581 		if(memcmp(addr, hp->addr, alen) == 0){
582 			if(hp->ref)
583 				return 1;
584 			else
585 				break;
586 		}
587 	return 0;
588 }
589 
590 static int
591 parseaddr(uchar *to, char *from, int alen)
592 {
593 	char nip[4];
594 	char *p;
595 	int i;
596 
597 	p = from;
598 	for(i = 0; i < alen; i++){
599 		if(*p == 0)
600 			return -1;
601 		nip[0] = *p++;
602 		if(*p == 0)
603 			return -1;
604 		nip[1] = *p++;
605 		nip[2] = 0;
606 		to[i] = strtoul(nip, 0, 16);
607 		if(*p == ':')
608 			p++;
609 	}
610 	return 0;
611 }
612 
613 /*
614  *  keep track of multicast addresses
615  */
616 static char*
617 netmulti(Netif *nif, Netfile *f, uchar *addr, int add)
618 {
619 	Netaddr **l, *ap;
620 	int i;
621 	ulong h;
622 
623 	if(nif->multicast == nil)
624 		return "interface does not support multicast";
625 
626 	l = &nif->maddr;
627 	i = 0;
628 	for(ap = *l; ap; ap = *l){
629 		if(memcmp(addr, ap->addr, nif->alen) == 0)
630 			break;
631 		i++;
632 		l = &ap->next;
633 	}
634 
635 	if(add){
636 		if(ap == 0){
637 			*l = ap = smalloc(sizeof(*ap));
638 			memmove(ap->addr, addr, nif->alen);
639 			ap->next = 0;
640 			ap->ref = 1;
641 			h = hash(addr, nif->alen);
642 			ap->hnext = nif->mhash[h];
643 			nif->mhash[h] = ap;
644 		} else {
645 			ap->ref++;
646 		}
647 		if(ap->ref == 1){
648 			nif->nmaddr++;
649 			nif->multicast(nif->arg, addr, 1);
650 		}
651 		if(i < 8*sizeof(f->maddr)){
652 			if((f->maddr[i/8] & (1<<(i%8))) == 0)
653 				f->nmaddr++;
654 			f->maddr[i/8] |= 1<<(i%8);
655 		}
656 	} else {
657 		if(ap == 0 || ap->ref == 0)
658 			return 0;
659 		ap->ref--;
660 		if(ap->ref == 0){
661 			nif->nmaddr--;
662 			nif->multicast(nif->arg, addr, 0);
663 		}
664 		if(i < 8*sizeof(f->maddr)){
665 			if((f->maddr[i/8] & (1<<(i%8))) != 0)
666 				f->nmaddr--;
667 			f->maddr[i/8] &= ~(1<<(i%8));
668 		}
669 	}
670 	return 0;
671 }
672