xref: /plan9/sys/src/9/port/netif.c (revision aa72973a2891ccbd3fb042462446761159389e19)
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
netifinit(Netif * nif,char * name,int nfile,ulong limit)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
netifgen(Chan * c,char *,Dirtab * vp,int,int i,Dir * dp)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 			snprint(up->genbuf, sizeof 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*
netifwalk(Netif * nif,Chan * c,Chan * nc,char ** name,int nname)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*
netifopen(Netif * nif,Chan * c,int omode)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
netifread(Netif * nif,Chan * c,void * a,long n,ulong offset)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 		if(p == nil)
217 			error(Enomem);
218 		j = snprint(p, READSTR, "in: %llud\n", nif->inpackets);
219 		j += snprint(p+j, READSTR-j, "link: %d\n", nif->link);
220 		j += snprint(p+j, READSTR-j, "out: %llud\n", nif->outpackets);
221 		j += snprint(p+j, READSTR-j, "crc errs: %d\n", nif->crcs);
222 		j += snprint(p+j, READSTR-j, "overflows: %d\n", nif->overflows);
223 		j += snprint(p+j, READSTR-j, "soft overflows: %d\n", nif->soverflows);
224 		j += snprint(p+j, READSTR-j, "framing errs: %d\n", nif->frames);
225 		j += snprint(p+j, READSTR-j, "buffer errs: %d\n", nif->buffs);
226 		j += snprint(p+j, READSTR-j, "output errs: %d\n", nif->oerrs);
227 		j += snprint(p+j, READSTR-j, "prom: %d\n", nif->prom);
228 		j += snprint(p+j, READSTR-j, "mbps: %d\n", nif->mbps);
229 		j += snprint(p+j, READSTR-j, "addr: ");
230 		for(i = 0; i < nif->alen; i++)
231 			j += snprint(p+j, READSTR-j, "%2.2ux", nif->addr[i]);
232 		snprint(p+j, READSTR-j, "\n");
233 		n = readstr(offset, a, n, p);
234 		free(p);
235 		return n;
236 	case Naddrqid:
237 		p = malloc(READSTR);
238 		if(p == nil)
239 			error(Enomem);
240 		j = 0;
241 		for(i = 0; i < nif->alen; i++)
242 			j += snprint(p+j, READSTR-j, "%2.2ux", nif->addr[i]);
243 		n = readstr(offset, a, n, p);
244 		free(p);
245 		return n;
246 	case Ntypeqid:
247 		f = nif->f[NETID(c->qid.path)];
248 		return readnum(offset, a, n, f->type, NUMSIZE);
249 	case Nifstatqid:
250 		return 0;
251 	}
252 	error(Ebadarg);
253 	return -1;	/* not reached */
254 }
255 
256 Block*
netifbread(Netif * nif,Chan * c,long n,ulong offset)257 netifbread(Netif *nif, Chan *c, long n, ulong offset)
258 {
259 	if((c->qid.type & QTDIR) || NETTYPE(c->qid.path) != Ndataqid)
260 		return devbread(c, n, offset);
261 
262 	return qbread(nif->f[NETID(c->qid.path)]->in, n);
263 }
264 
265 /*
266  *  make sure this type isn't already in use on this device
267  */
268 static int
typeinuse(Netif * nif,int type)269 typeinuse(Netif *nif, int type)
270 {
271 	Netfile *f, **fp, **efp;
272 
273 	if(type <= 0)
274 		return 0;
275 
276 	efp = &nif->f[nif->nfile];
277 	for(fp = nif->f; fp < efp; fp++){
278 		f = *fp;
279 		if(f == 0)
280 			continue;
281 		if(f->type == type)
282 			return 1;
283 	}
284 	return 0;
285 }
286 
287 /*
288  *  the devxxx.c that calls us handles writing data, it knows best
289  */
290 long
netifwrite(Netif * nif,Chan * c,void * a,long n)291 netifwrite(Netif *nif, Chan *c, void *a, long n)
292 {
293 	Netfile *f;
294 	int type;
295 	char *p, buf[64];
296 	uchar binaddr[Nmaxaddr];
297 
298 	if(NETTYPE(c->qid.path) != Nctlqid)
299 		error(Eperm);
300 
301 	if(n >= sizeof(buf))
302 		n = sizeof(buf)-1;
303 	memmove(buf, a, n);
304 	buf[n] = 0;
305 
306 	if(waserror()){
307 		qunlock(nif);
308 		nexterror();
309 	}
310 
311 	qlock(nif);
312 	f = nif->f[NETID(c->qid.path)];
313 	if((p = matchtoken(buf, "connect")) != 0){
314 		type = atoi(p);
315 		if(typeinuse(nif, type))
316 			error(Einuse);
317 		f->type = type;
318 		if(f->type < 0)
319 			nif->all++;
320 	} else if(matchtoken(buf, "promiscuous")){
321 		if(f->prom == 0){
322 			if(nif->prom == 0 && nif->promiscuous != nil)
323 				nif->promiscuous(nif->arg, 1);
324 			f->prom = 1;
325 			nif->prom++;
326 		}
327 	} else if((p = matchtoken(buf, "scanbs")) != 0){
328 		/* scan for base stations */
329 		if(f->scan == 0){
330 			type = atoi(p);
331 			if(type < 5)
332 				type = 5;
333 			if(nif->scanbs != nil)
334 				nif->scanbs(nif->arg, type);
335 			f->scan = type;
336 			nif->scan++;
337 		}
338 	} else if(matchtoken(buf, "bridge")){
339 		f->bridge = 1;
340 	} else if(matchtoken(buf, "headersonly")){
341 		f->headersonly = 1;
342 	} else if((p = matchtoken(buf, "addmulti")) != 0){
343 		if(parseaddr(binaddr, p, nif->alen) < 0)
344 			error("bad address");
345 		p = netmulti(nif, f, binaddr, 1);
346 		if(p)
347 			error(p);
348 	} else if((p = matchtoken(buf, "remmulti")) != 0){
349 		if(parseaddr(binaddr, p, nif->alen) < 0)
350 			error("bad address");
351 		p = netmulti(nif, f, binaddr, 0);
352 		if(p)
353 			error(p);
354 	} else
355 		n = -1;
356 	qunlock(nif);
357 	poperror();
358 	return n;
359 }
360 
361 int
netifwstat(Netif * nif,Chan * c,uchar * db,int n)362 netifwstat(Netif *nif, Chan *c, uchar *db, int n)
363 {
364 	Dir *dir;
365 	Netfile *f;
366 	int m;
367 
368 	f = nif->f[NETID(c->qid.path)];
369 	if(f == 0)
370 		error(Enonexist);
371 
372 	if(netown(f, up->user, OWRITE) < 0)
373 		error(Eperm);
374 
375 	dir = smalloc(sizeof(Dir)+n);
376 	m = convM2D(db, n, &dir[0], (char*)&dir[1]);
377 	if(m == 0){
378 		free(dir);
379 		error(Eshortstat);
380 	}
381 	if(!emptystr(dir[0].uid))
382 		strncpy(f->owner, dir[0].uid, KNAMELEN);
383 	if(dir[0].mode != ~0UL)
384 		f->mode = dir[0].mode;
385 	free(dir);
386 	return m;
387 }
388 
389 int
netifstat(Netif * nif,Chan * c,uchar * db,int n)390 netifstat(Netif *nif, Chan *c, uchar *db, int n)
391 {
392 	return devstat(c, db, n, (Dirtab *)nif, 0, netifgen);
393 }
394 
395 void
netifclose(Netif * nif,Chan * c)396 netifclose(Netif *nif, Chan *c)
397 {
398 	Netfile *f;
399 	int t;
400 	Netaddr *ap;
401 
402 	if((c->flag & COPEN) == 0)
403 		return;
404 
405 	t = NETTYPE(c->qid.path);
406 	if(t != Ndataqid && t != Nctlqid)
407 		return;
408 
409 	f = nif->f[NETID(c->qid.path)];
410 	qlock(f);
411 	if(--(f->inuse) == 0){
412 		if(f->prom){
413 			qlock(nif);
414 			if(--(nif->prom) == 0 && nif->promiscuous != nil)
415 				nif->promiscuous(nif->arg, 0);
416 			qunlock(nif);
417 			f->prom = 0;
418 		}
419 		if(f->scan){
420 			qlock(nif);
421 			if(--(nif->scan) == 0 && nif->scanbs != nil)
422 				nif->scanbs(nif->arg, 0);
423 			qunlock(nif);
424 			f->prom = 0;
425 			f->scan = 0;
426 		}
427 		if(f->nmaddr){
428 			qlock(nif);
429 			t = 0;
430 			for(ap = nif->maddr; ap; ap = ap->next){
431 				if(f->maddr[t/8] & (1<<(t%8)))
432 					netmulti(nif, f, ap->addr, 0);
433 			}
434 			qunlock(nif);
435 			f->nmaddr = 0;
436 		}
437 		if(f->type < 0){
438 			qlock(nif);
439 			--(nif->all);
440 			qunlock(nif);
441 		}
442 		f->owner[0] = 0;
443 		f->type = 0;
444 		f->bridge = 0;
445 		f->headersonly = 0;
446 		qclose(f->in);
447 	}
448 	qunlock(f);
449 }
450 
451 Lock netlock;
452 
453 static int
netown(Netfile * p,char * o,int omode)454 netown(Netfile *p, char *o, int omode)
455 {
456 	static int access[] = { 0400, 0200, 0600, 0100 };
457 	int mode;
458 	int t;
459 
460 	lock(&netlock);
461 	if(*p->owner){
462 		if(strncmp(o, p->owner, KNAMELEN) == 0)	/* User */
463 			mode = p->mode;
464 		else if(strncmp(o, eve, KNAMELEN) == 0)	/* Bootes is group */
465 			mode = p->mode<<3;
466 		else
467 			mode = p->mode<<6;		/* Other */
468 
469 		t = access[omode&3];
470 		if((t & mode) == t){
471 			unlock(&netlock);
472 			return 0;
473 		} else {
474 			unlock(&netlock);
475 			return -1;
476 		}
477 	}
478 	strncpy(p->owner, o, KNAMELEN);
479 	p->mode = 0660;
480 	unlock(&netlock);
481 	return 0;
482 }
483 
484 /*
485  *  Increment the reference count of a network device.
486  *  If id < 0, return an unused ether device.
487  */
488 static int
openfile(Netif * nif,int id)489 openfile(Netif *nif, int id)
490 {
491 	Netfile *f, **fp, **efp;
492 
493 	if(id >= 0){
494 		f = nif->f[id];
495 		if(f == 0)
496 			error(Enodev);
497 		qlock(f);
498 		qreopen(f->in);
499 		f->inuse++;
500 		qunlock(f);
501 		return id;
502 	}
503 
504 	qlock(nif);
505 	if(waserror()){
506 		qunlock(nif);
507 		nexterror();
508 	}
509 	efp = &nif->f[nif->nfile];
510 	for(fp = nif->f; fp < efp; fp++){
511 		f = *fp;
512 		if(f == 0){
513 			f = malloc(sizeof(Netfile));
514 			if(f == 0)
515 				exhausted("memory");
516 			f->in = qopen(nif->limit, Qmsg, 0, 0);
517 			if(f->in == nil){
518 				free(f);
519 				exhausted("memory");
520 			}
521 			*fp = f;
522 			qlock(f);
523 		} else {
524 			qlock(f);
525 			if(f->inuse){
526 				qunlock(f);
527 				continue;
528 			}
529 		}
530 		f->inuse = 1;
531 		qreopen(f->in);
532 		netown(f, up->user, 0);
533 		qunlock(f);
534 		qunlock(nif);
535 		poperror();
536 		return fp - nif->f;
537 	}
538 	error(Enodev);
539 	return -1;	/* not reached */
540 }
541 
542 /*
543  *  look for a token starting a string,
544  *  return a pointer to first non-space char after it
545  */
546 static char*
matchtoken(char * p,char * token)547 matchtoken(char *p, char *token)
548 {
549 	int n;
550 
551 	n = strlen(token);
552 	if(strncmp(p, token, n))
553 		return 0;
554 	p += n;
555 	if(*p == 0)
556 		return p;
557 	if(*p != ' ' && *p != '\t' && *p != '\n')
558 		return 0;
559 	while(*p == ' ' || *p == '\t' || *p == '\n')
560 		p++;
561 	return p;
562 }
563 
564 void
hnputv(void * p,uvlong v)565 hnputv(void *p, uvlong v)
566 {
567 	uchar *a;
568 
569 	a = p;
570 	hnputl(a, v>>32);
571 	hnputl(a+4, v);
572 }
573 
574 void
hnputl(void * p,uint v)575 hnputl(void *p, uint v)
576 {
577 	uchar *a;
578 
579 	a = p;
580 	a[0] = v>>24;
581 	a[1] = v>>16;
582 	a[2] = v>>8;
583 	a[3] = v;
584 }
585 
586 void
hnputs(void * p,ushort v)587 hnputs(void *p, ushort v)
588 {
589 	uchar *a;
590 
591 	a = p;
592 	a[0] = v>>8;
593 	a[1] = v;
594 }
595 
596 uvlong
nhgetv(void * p)597 nhgetv(void *p)
598 {
599 	uchar *a;
600 
601 	a = p;
602 	return ((vlong)nhgetl(a) << 32) | nhgetl(a+4);
603 }
604 
605 uint
nhgetl(void * p)606 nhgetl(void *p)
607 {
608 	uchar *a;
609 
610 	a = p;
611 	return (a[0]<<24)|(a[1]<<16)|(a[2]<<8)|(a[3]<<0);
612 }
613 
614 ushort
nhgets(void * p)615 nhgets(void *p)
616 {
617 	uchar *a;
618 
619 	a = p;
620 	return (a[0]<<8)|(a[1]<<0);
621 }
622 
623 static ulong
hash(uchar * a,int len)624 hash(uchar *a, int len)
625 {
626 	ulong sum = 0;
627 
628 	while(len-- > 0)
629 		sum = (sum << 1) + *a++;
630 	return sum%Nmhash;
631 }
632 
633 int
activemulti(Netif * nif,uchar * addr,int alen)634 activemulti(Netif *nif, uchar *addr, int alen)
635 {
636 	Netaddr *hp;
637 
638 	for(hp = nif->mhash[hash(addr, alen)]; hp; hp = hp->hnext)
639 		if(memcmp(addr, hp->addr, alen) == 0){
640 			if(hp->ref)
641 				return 1;
642 			else
643 				break;
644 		}
645 	return 0;
646 }
647 
648 static int
parseaddr(uchar * to,char * from,int alen)649 parseaddr(uchar *to, char *from, int alen)
650 {
651 	char nip[4];
652 	char *p;
653 	int i;
654 
655 	p = from;
656 	for(i = 0; i < alen; i++){
657 		if(*p == 0)
658 			return -1;
659 		nip[0] = *p++;
660 		if(*p == 0)
661 			return -1;
662 		nip[1] = *p++;
663 		nip[2] = 0;
664 		to[i] = strtoul(nip, 0, 16);
665 		if(*p == ':')
666 			p++;
667 	}
668 	return 0;
669 }
670 
671 /*
672  *  keep track of multicast addresses
673  */
674 static char*
netmulti(Netif * nif,Netfile * f,uchar * addr,int add)675 netmulti(Netif *nif, Netfile *f, uchar *addr, int add)
676 {
677 	Netaddr **l, *ap;
678 	int i;
679 	ulong h;
680 
681 	if(nif->multicast == nil)
682 		return "interface does not support multicast";
683 
684 	l = &nif->maddr;
685 	i = 0;
686 	for(ap = *l; ap; ap = *l){
687 		if(memcmp(addr, ap->addr, nif->alen) == 0)
688 			break;
689 		i++;
690 		l = &ap->next;
691 	}
692 
693 	if(add){
694 		if(ap == 0){
695 			*l = ap = smalloc(sizeof(*ap));
696 			memmove(ap->addr, addr, nif->alen);
697 			ap->next = 0;
698 			ap->ref = 1;
699 			h = hash(addr, nif->alen);
700 			ap->hnext = nif->mhash[h];
701 			nif->mhash[h] = ap;
702 		} else {
703 			ap->ref++;
704 		}
705 		if(ap->ref == 1){
706 			nif->nmaddr++;
707 			nif->multicast(nif->arg, addr, 1);
708 		}
709 		if(i < 8*sizeof(f->maddr)){
710 			if((f->maddr[i/8] & (1<<(i%8))) == 0)
711 				f->nmaddr++;
712 			f->maddr[i/8] |= 1<<(i%8);
713 		}
714 	} else {
715 		if(ap == 0 || ap->ref == 0)
716 			return 0;
717 		ap->ref--;
718 		if(ap->ref == 0){
719 			nif->nmaddr--;
720 			nif->multicast(nif->arg, addr, 0);
721 		}
722 		if(i < 8*sizeof(f->maddr)){
723 			if((f->maddr[i/8] & (1<<(i%8))) != 0)
724 				f->nmaddr--;
725 			f->maddr[i/8] &= ~(1<<(i%8));
726 		}
727 	}
728 	return 0;
729 }
730