xref: /plan9-contrib/sys/src/9/port/netif.c (revision 75184bd408f1c14b5ab3ed7d519e0377b9761a74)
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 || t == Nstatqid || t == Nifstatqid){
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-1);
383 		f->owner[KNAMELEN-1] = 0;
384 	}
385 	if(dir[0].mode != ~0UL)
386 		f->mode = dir[0].mode;
387 	free(dir);
388 	return m;
389 }
390 
391 int
netifstat(Netif * nif,Chan * c,uchar * db,int n)392 netifstat(Netif *nif, Chan *c, uchar *db, int n)
393 {
394 	return devstat(c, db, n, (Dirtab *)nif, 0, netifgen);
395 }
396 
397 void
netifclose(Netif * nif,Chan * c)398 netifclose(Netif *nif, Chan *c)
399 {
400 	Netfile *f;
401 	int t;
402 	Netaddr *ap;
403 
404 	if((c->flag & COPEN) == 0)
405 		return;
406 
407 	t = NETTYPE(c->qid.path);
408 	if(t != Ndataqid && t != Nctlqid)
409 		return;
410 
411 	f = nif->f[NETID(c->qid.path)];
412 	qlock(f);
413 	if(--(f->inuse) == 0){
414 		if(f->prom){
415 			qlock(nif);
416 			if(--(nif->prom) == 0 && nif->promiscuous != nil)
417 				nif->promiscuous(nif->arg, 0);
418 			qunlock(nif);
419 			f->prom = 0;
420 		}
421 		if(f->scan){
422 			qlock(nif);
423 			if(--(nif->scan) == 0 && nif->scanbs != nil)
424 				nif->scanbs(nif->arg, 0);
425 			qunlock(nif);
426 			f->prom = 0;
427 			f->scan = 0;
428 		}
429 		if(f->nmaddr){
430 			qlock(nif);
431 			t = 0;
432 			for(ap = nif->maddr; ap; ap = ap->next){
433 				if(f->maddr[t/8] & (1<<(t%8)))
434 					netmulti(nif, f, ap->addr, 0);
435 			}
436 			qunlock(nif);
437 			f->nmaddr = 0;
438 		}
439 		if(f->type < 0){
440 			qlock(nif);
441 			--(nif->all);
442 			qunlock(nif);
443 		}
444 		f->owner[0] = 0;
445 		f->type = 0;
446 		f->bridge = 0;
447 		f->headersonly = 0;
448 		qclose(f->in);
449 	}
450 	qunlock(f);
451 }
452 
453 Lock netlock;
454 
455 static int
netown(Netfile * p,char * o,int omode)456 netown(Netfile *p, char *o, int omode)
457 {
458 	static int access[] = { 0400, 0200, 0600, 0100 };
459 	int mode;
460 	int t;
461 
462 	lock(&netlock);
463 	if(*p->owner){
464 		if(strncmp(o, p->owner, KNAMELEN) == 0)	/* User */
465 			mode = p->mode;
466 		else if(strncmp(o, eve, KNAMELEN) == 0)	/* Bootes is group */
467 			mode = p->mode<<3;
468 		else
469 			mode = p->mode<<6;		/* Other */
470 
471 		t = access[omode&3];
472 		if((t & mode) == t){
473 			unlock(&netlock);
474 			return 0;
475 		} else {
476 			unlock(&netlock);
477 			return -1;
478 		}
479 	}
480 	strncpy(p->owner, o, KNAMELEN-1);
481 	p->owner[KNAMELEN-1] = 0;
482 	p->mode = 0660;
483 	unlock(&netlock);
484 	return 0;
485 }
486 
487 /*
488  *  Increment the reference count of a network device.
489  *  If id < 0, return an unused ether device.
490  */
491 static int
openfile(Netif * nif,int id)492 openfile(Netif *nif, int id)
493 {
494 	Netfile *f, **fp, **efp;
495 
496 	if(id >= 0){
497 		f = nif->f[id];
498 		if(f == 0)
499 			error(Enodev);
500 		qlock(f);
501 		qreopen(f->in);
502 		f->inuse++;
503 		qunlock(f);
504 		return id;
505 	}
506 
507 	qlock(nif);
508 	if(waserror()){
509 		qunlock(nif);
510 		nexterror();
511 	}
512 	efp = &nif->f[nif->nfile];
513 	for(fp = nif->f; fp < efp; fp++){
514 		f = *fp;
515 		if(f == 0){
516 			f = malloc(sizeof(Netfile));
517 			if(f == 0)
518 				exhausted("memory");
519 			f->in = qopen(nif->limit, Qmsg, 0, 0);
520 			if(f->in == nil){
521 				free(f);
522 				exhausted("memory");
523 			}
524 			*fp = f;
525 			qlock(f);
526 		} else {
527 			qlock(f);
528 			if(f->inuse){
529 				qunlock(f);
530 				continue;
531 			}
532 		}
533 		f->inuse = 1;
534 		qreopen(f->in);
535 		netown(f, up->user, 0);
536 		qunlock(f);
537 		qunlock(nif);
538 		poperror();
539 		return fp - nif->f;
540 	}
541 	error(Enodev);
542 	return -1;	/* not reached */
543 }
544 
545 /*
546  *  look for a token starting a string,
547  *  return a pointer to first non-space char after it
548  */
549 static char*
matchtoken(char * p,char * token)550 matchtoken(char *p, char *token)
551 {
552 	int n;
553 
554 	n = strlen(token);
555 	if(strncmp(p, token, n))
556 		return 0;
557 	p += n;
558 	if(*p == 0)
559 		return p;
560 	if(*p != ' ' && *p != '\t' && *p != '\n')
561 		return 0;
562 	while(*p == ' ' || *p == '\t' || *p == '\n')
563 		p++;
564 	return p;
565 }
566 
567 void
hnputv(void * p,uvlong v)568 hnputv(void *p, uvlong v)
569 {
570 	uchar *a;
571 
572 	a = p;
573 	hnputl(a, v>>32);
574 	hnputl(a+4, v);
575 }
576 
577 void
hnputl(void * p,uint v)578 hnputl(void *p, uint v)
579 {
580 	uchar *a;
581 
582 	a = p;
583 	a[0] = v>>24;
584 	a[1] = v>>16;
585 	a[2] = v>>8;
586 	a[3] = v;
587 }
588 
589 void
hnputs(void * p,ushort v)590 hnputs(void *p, ushort v)
591 {
592 	uchar *a;
593 
594 	a = p;
595 	a[0] = v>>8;
596 	a[1] = v;
597 }
598 
599 uvlong
nhgetv(void * p)600 nhgetv(void *p)
601 {
602 	uchar *a;
603 
604 	a = p;
605 	return ((vlong)nhgetl(a) << 32) | nhgetl(a+4);
606 }
607 
608 uint
nhgetl(void * p)609 nhgetl(void *p)
610 {
611 	uchar *a;
612 
613 	a = p;
614 	return (a[0]<<24)|(a[1]<<16)|(a[2]<<8)|(a[3]<<0);
615 }
616 
617 ushort
nhgets(void * p)618 nhgets(void *p)
619 {
620 	uchar *a;
621 
622 	a = p;
623 	return (a[0]<<8)|(a[1]<<0);
624 }
625 
626 static ulong
hash(uchar * a,int len)627 hash(uchar *a, int len)
628 {
629 	ulong sum = 0;
630 
631 	while(len-- > 0)
632 		sum = (sum << 1) + *a++;
633 	return sum%Nmhash;
634 }
635 
636 int
activemulti(Netif * nif,uchar * addr,int alen)637 activemulti(Netif *nif, uchar *addr, int alen)
638 {
639 	Netaddr *hp;
640 
641 	for(hp = nif->mhash[hash(addr, alen)]; hp; hp = hp->hnext)
642 		if(memcmp(addr, hp->addr, alen) == 0){
643 			if(hp->ref)
644 				return 1;
645 			else
646 				break;
647 		}
648 	return 0;
649 }
650 
651 static int
parseaddr(uchar * to,char * from,int alen)652 parseaddr(uchar *to, char *from, int alen)
653 {
654 	char nip[4];
655 	char *p;
656 	int i;
657 
658 	p = from;
659 	for(i = 0; i < alen; i++){
660 		if(*p == 0)
661 			return -1;
662 		nip[0] = *p++;
663 		if(*p == 0)
664 			return -1;
665 		nip[1] = *p++;
666 		nip[2] = 0;
667 		to[i] = strtoul(nip, 0, 16);
668 		if(*p == ':')
669 			p++;
670 	}
671 	return 0;
672 }
673 
674 /*
675  *  keep track of multicast addresses
676  */
677 static char*
netmulti(Netif * nif,Netfile * f,uchar * addr,int add)678 netmulti(Netif *nif, Netfile *f, uchar *addr, int add)
679 {
680 	Netaddr **l, *ap;
681 	int i;
682 	ulong h;
683 
684 	if(nif->multicast == nil)
685 		return "interface does not support multicast";
686 
687 	l = &nif->maddr;
688 	i = 0;
689 	for(ap = *l; ap; ap = *l){
690 		if(memcmp(addr, ap->addr, nif->alen) == 0)
691 			break;
692 		i++;
693 		l = &ap->next;
694 	}
695 
696 	if(add){
697 		if(ap == 0){
698 			*l = ap = smalloc(sizeof(*ap));
699 			memmove(ap->addr, addr, nif->alen);
700 			ap->next = 0;
701 			ap->ref = 1;
702 			h = hash(addr, nif->alen);
703 			ap->hnext = nif->mhash[h];
704 			nif->mhash[h] = ap;
705 		} else {
706 			ap->ref++;
707 		}
708 		if(ap->ref == 1){
709 			nif->nmaddr++;
710 			nif->multicast(nif->arg, addr, 1);
711 		}
712 		if(i < 8*sizeof(f->maddr)){
713 			if((f->maddr[i/8] & (1<<(i%8))) == 0)
714 				f->nmaddr++;
715 			f->maddr[i/8] |= 1<<(i%8);
716 		}
717 	} else {
718 		if(ap == 0 || ap->ref == 0)
719 			return 0;
720 		ap->ref--;
721 		if(ap->ref == 0){
722 			nif->nmaddr--;
723 			nif->multicast(nif->arg, addr, 0);
724 		}
725 		if(i < 8*sizeof(f->maddr)){
726 			if((f->maddr[i/8] & (1<<(i%8))) != 0)
727 				f->nmaddr--;
728 			f->maddr[i/8] &= ~(1<<(i%8));
729 		}
730 	}
731 	return 0;
732 }
733