xref: /plan9-contrib/sys/src/9k/port/netif.c (revision 406c76facc4b13aa2a55454bf4091aab9f03da22)
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 
8 #include	"../port/netif.h"
9 
10 static int netown(Netfile*, char*, int);
11 static int openfile(Netif*, int);
12 static char* matchtoken(char*, char*);
13 static char* netmulti(Netif*, Netfile*, uchar*, int);
14 static int parseaddr(uchar*, char*, int);
15 
16 /*
17  *  set up a new network interface
18  */
19 void
netifinit(Netif * nif,char * name,int nfile,ulong limit)20 netifinit(Netif *nif, char *name, int nfile, ulong limit)
21 {
22 	strncpy(nif->name, name, KNAMELEN-1);
23 	nif->name[KNAMELEN-1] = 0;
24 	nif->nfile = nfile;
25 	nif->f = malloc(nfile*sizeof(Netfile*));
26 	if(nif->f == nil)
27 		panic("netifinit: no memory");
28 	memset(nif->f, 0, nfile*sizeof(Netfile*));
29 	nif->limit = limit;
30 }
31 
32 /*
33  *  generate a 3 level directory
34  */
35 static int
netifgen(Chan * c,char *,Dirtab * vp,int,int i,Dir * dp)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 			snprint(up->genbuf, sizeof 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*
netifwalk(Netif * nif,Chan * c,Chan * nc,char ** name,int nname)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*
netifopen(Netif * nif,Chan * c,int omode)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->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
netifread(Netif * nif,Chan * c,void * a,long n,vlong off)200 netifread(Netif *nif, Chan *c, void *a, long n, vlong off)
201 {
202 	int i, j;
203 	Netfile *f;
204 	char *p;
205 	long offset;
206 
207 	if(c->qid.type & QTDIR)
208 		return devdirread(c, a, n, (Dirtab*)nif, 0, netifgen);
209 
210 	offset = off;
211 	switch(NETTYPE(c->qid.path)){
212 	case Ndataqid:
213 		f = nif->f[NETID(c->qid.path)];
214 		return qread(f->iq, a, n);
215 	case Nctlqid:
216 		return readnum(offset, a, n, NETID(c->qid.path), NUMSIZE);
217 	case Nstatqid:
218 		p = malloc(READSTR);
219 		if(p == nil)
220 			error(Enomem);
221 		j = snprint(p, READSTR, "in: %llud\n", nif->inpackets);
222 		j += snprint(p+j, READSTR-j, "link: %d\n", nif->link);
223 		j += snprint(p+j, READSTR-j, "out: %llud\n", nif->outpackets);
224 		j += snprint(p+j, READSTR-j, "crc errs: %d\n", nif->crcs);
225 		j += snprint(p+j, READSTR-j, "overflows: %d\n", nif->overflows);
226 		j += snprint(p+j, READSTR-j, "soft overflows: %d\n", nif->soverflows);
227 		j += snprint(p+j, READSTR-j, "framing errs: %d\n", nif->frames);
228 		j += snprint(p+j, READSTR-j, "buffer errs: %d\n", nif->buffs);
229 		j += snprint(p+j, READSTR-j, "output errs: %d\n", nif->oerrs);
230 		j += snprint(p+j, READSTR-j, "prom: %d\n", nif->prom);
231 		j += snprint(p+j, READSTR-j, "mbps: %d\n", nif->mbps);
232 		j += snprint(p+j, READSTR-j, "addr: ");
233 		for(i = 0; i < nif->alen; i++)
234 			j += snprint(p+j, READSTR-j, "%2.2ux", nif->addr[i]);
235 		snprint(p+j, READSTR-j, "\n");
236 		n = readstr(offset, a, n, p);
237 		free(p);
238 		return n;
239 	case Naddrqid:
240 		p = malloc(READSTR);
241 		if(p == nil)
242 			error(Enomem);
243 		j = 0;
244 		for(i = 0; i < nif->alen; i++)
245 			j += snprint(p+j, READSTR-j, "%2.2ux", nif->addr[i]);
246 		n = readstr(offset, a, n, p);
247 		free(p);
248 		return n;
249 	case Ntypeqid:
250 		f = nif->f[NETID(c->qid.path)];
251 		return readnum(offset, a, n, f->type, NUMSIZE);
252 	case Nifstatqid:
253 		return 0;
254 	}
255 	error(Ebadarg);
256 	return -1;	/* not reached */
257 }
258 
259 Block*
netifbread(Netif * nif,Chan * c,long n,vlong offset)260 netifbread(Netif *nif, Chan *c, long n, vlong offset)
261 {
262 	if((c->qid.type & QTDIR) || NETTYPE(c->qid.path) != Ndataqid)
263 		return devbread(c, n, offset);
264 
265 	return qbread(nif->f[NETID(c->qid.path)]->iq, n);
266 }
267 
268 /*
269  *  make sure this type isn't already in use on this device
270  */
271 static int
typeinuse(Netif * nif,int type)272 typeinuse(Netif *nif, int type)
273 {
274 	Netfile *f, **fp, **efp;
275 
276 	if(type <= 0)
277 		return 0;
278 
279 	efp = &nif->f[nif->nfile];
280 	for(fp = nif->f; fp < efp; fp++){
281 		f = *fp;
282 		if(f == 0)
283 			continue;
284 		if(f->type == type)
285 			return 1;
286 	}
287 	return 0;
288 }
289 
290 /*
291  *  the devxxx.c that calls us handles writing data, it knows best
292  */
293 long
netifwrite(Netif * nif,Chan * c,void * a,long n)294 netifwrite(Netif *nif, Chan *c, void *a, long n)
295 {
296 	Netfile *f;
297 	int type;
298 	char *p, buf[64];
299 	uchar binaddr[Nmaxaddr];
300 
301 	if(NETTYPE(c->qid.path) != Nctlqid)
302 		error(Eperm);
303 
304 	if(n >= sizeof(buf))
305 		n = sizeof(buf)-1;
306 	memmove(buf, a, n);
307 	buf[n] = 0;
308 
309 	if(waserror()){
310 		qunlock(nif);
311 		nexterror();
312 	}
313 
314 	qlock(nif);
315 	f = nif->f[NETID(c->qid.path)];
316 	if((p = matchtoken(buf, "connect")) != 0){
317 		type = atoi(p);
318 		if(typeinuse(nif, type))
319 			error(Einuse);
320 		f->type = type;
321 		if(f->type < 0)
322 			nif->all++;
323 	} else if(matchtoken(buf, "promiscuous")){
324 		if(f->prom == 0){
325 			if(nif->prom == 0 && nif->promiscuous != nil)
326 				nif->promiscuous(nif->arg, 1);
327 			f->prom = 1;
328 			nif->prom++;
329 		}
330 	} else if((p = matchtoken(buf, "scanbs")) != 0){
331 		/* scan for base stations */
332 		if(f->scan == 0){
333 			type = atoi(p);
334 			if(type < 5)
335 				type = 5;
336 			if(nif->scanbs != nil)
337 				nif->scanbs(nif->arg, type);
338 			f->scan = type;
339 			nif->scan++;
340 		}
341 	} else if(matchtoken(buf, "bridge")){
342 		f->bridge = 1;
343 	} else if(matchtoken(buf, "headersonly")){
344 		f->headersonly = 1;
345 	} else if((p = matchtoken(buf, "addmulti")) != 0){
346 		if(parseaddr(binaddr, p, nif->alen) < 0)
347 			error("bad address");
348 		p = netmulti(nif, f, binaddr, 1);
349 		if(p)
350 			error(p);
351 	} else if((p = matchtoken(buf, "remmulti")) != 0){
352 		if(parseaddr(binaddr, p, nif->alen) < 0)
353 			error("bad address");
354 		p = netmulti(nif, f, binaddr, 0);
355 		if(p)
356 			error(p);
357 	} else
358 		n = -1;
359 	qunlock(nif);
360 	poperror();
361 	return n;
362 }
363 
364 long
netifwstat(Netif * nif,Chan * c,uchar * db,long n)365 netifwstat(Netif *nif, Chan *c, uchar *db, long n)
366 {
367 	Dir *dir;
368 	Netfile *f;
369 	int l;
370 
371 	f = nif->f[NETID(c->qid.path)];
372 	if(f == 0)
373 		error(Enonexist);
374 
375 	if(netown(f, up->user, OWRITE) < 0)
376 		error(Eperm);
377 
378 	dir = smalloc(sizeof(Dir)+n);
379 	l = convM2D(db, n, &dir[0], (char*)&dir[1]);
380 	if(l == 0){
381 		free(dir);
382 		error(Eshortstat);
383 	}
384 	if(!emptystr(dir[0].uid))
385 		strncpy(f->owner, dir[0].uid, KNAMELEN);
386 	if(dir[0].mode != ~0UL)
387 		f->mode = dir[0].mode;
388 	free(dir);
389 	return l;
390 }
391 
392 long
netifstat(Netif * nif,Chan * c,uchar * db,long n)393 netifstat(Netif *nif, Chan *c, uchar *db, long n)
394 {
395 	return devstat(c, db, n, (Dirtab *)nif, 0, netifgen);
396 }
397 
398 void
netifclose(Netif * nif,Chan * c)399 netifclose(Netif *nif, Chan *c)
400 {
401 	Netfile *f;
402 	int t;
403 	Netaddr *ap;
404 
405 	if((c->flag & COPEN) == 0)
406 		return;
407 
408 	t = NETTYPE(c->qid.path);
409 	if(t != Ndataqid && t != Nctlqid)
410 		return;
411 
412 	f = nif->f[NETID(c->qid.path)];
413 	qlock(f);
414 	if(--(f->inuse) == 0){
415 		if(f->prom){
416 			qlock(nif);
417 			if(--(nif->prom) == 0 && nif->promiscuous != nil)
418 				nif->promiscuous(nif->arg, 0);
419 			qunlock(nif);
420 			f->prom = 0;
421 		}
422 		if(f->scan){
423 			qlock(nif);
424 			if(--(nif->scan) == 0 && nif->scanbs != nil)
425 				nif->scanbs(nif->arg, 0);
426 			qunlock(nif);
427 			f->prom = 0;
428 			f->scan = 0;
429 		}
430 		if(f->nmaddr){
431 			qlock(nif);
432 			t = 0;
433 			for(ap = nif->maddr; ap; ap = ap->next){
434 				if(f->maddr[t/8] & (1<<(t%8)))
435 					netmulti(nif, f, ap->addr, 0);
436 			}
437 			qunlock(nif);
438 			f->nmaddr = 0;
439 		}
440 		if(f->type < 0){
441 			qlock(nif);
442 			--(nif->all);
443 			qunlock(nif);
444 		}
445 		f->owner[0] = 0;
446 		f->type = 0;
447 		f->bridge = 0;
448 		f->headersonly = 0;
449 		qclose(f->iq);
450 	}
451 	qunlock(f);
452 }
453 
454 Lock netlock;
455 
456 static int
netown(Netfile * p,char * o,int omode)457 netown(Netfile *p, char *o, int omode)
458 {
459 	static int access[] = { 0400, 0200, 0600, 0100 };
460 	int mode;
461 	int t;
462 
463 	lock(&netlock);
464 	if(*p->owner){
465 		if(strncmp(o, p->owner, KNAMELEN) == 0)	/* User */
466 			mode = p->mode;
467 		else if(strncmp(o, eve, KNAMELEN) == 0)	/* Bootes is group */
468 			mode = p->mode<<3;
469 		else
470 			mode = p->mode<<6;		/* Other */
471 
472 		t = access[omode&3];
473 		if((t & mode) == t){
474 			unlock(&netlock);
475 			return 0;
476 		} else {
477 			unlock(&netlock);
478 			return -1;
479 		}
480 	}
481 	strncpy(p->owner, o, KNAMELEN);
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->iq);
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->iq = qopen(nif->limit, Qmsg, 0, 0);
520 			if(f->iq == 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->iq);
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