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