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