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