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