| ---
netif.c (14502B)
---
1 #include "u.h"
2 #include "lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "error.h"
7 #include "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 int netifdebug;
16 #define dprint(...) if(netifdebug)print(__VA_ARGS__); else USED(netifdebug)
17
18 /*
19 * set up a new network interface
20 */
21 void
22 netifinit(Netif *nif, char *name, int nfile, ulong limit)
23 {
24 strncpy(nif->name, name, KNAMELEN-1);
25 nif->name[KNAMELEN-1] = 0;
26 nif->nfile = nfile;
27 nif->f = xalloc(nfile*sizeof(Netfile*));
28 if (nif->f == nil)
29 panic("netifinit: no memory");
30 nif->limit = limit;
31 }
32
33 #define DD(c,q,nam,n,owner,perm,dp) dprint("%lux.%llux %s\n", q.type, q.path, nam); devdir(c,q,nam,n,owner,perm,dp)
34
35 /*
36 * generate a 3 level directory
37 */
38 static int
39 netifgen(Chan *c, char *dummy, Dirtab *vp, int dummy1, int i, Dir *dp)
40 {
41 Qid q;
42 Netif *nif = (Netif*)vp;
43 Netfile *f;
44 int t, perm;
45 char *o;
46
47 memset(&q, 0, sizeof q);
48 q.type = QTFILE;
49 q.vers = 0;
50
51 dprint("gen %d %llud %.2d ", c->dri, c->qid.path, i);
52 /* top level directory contains the name of the network */
53 if(c->qid.path == 0){
54 switch(i){
55 case DEVDOTDOT:
56 q.path = 0;
57 q.type = QTDIR;
58 DD(c, q, ".", 0, eve, 0555, dp);
59 break;
60 case 0:
61 q.path = N2ndqid;
62 q.type = QTDIR;
63 strcpy(up->genbuf, nif->name);
64 DD(c, q, up->genbuf, 0, eve, 0555, dp);
65 break;
66 default:
67 dprint("-> -1 (top)\n");
68 return -1;
69 }
70 return 1;
71 }
72
73 /* second level contains clone plus all the conversations */
74 t = NETTYPE(c->qid.path);
75 if(t == N2ndqid || t == Ncloneqid || t == Naddrqid || t == Nstatqid || t == Nifstatqid){
76 switch(i){
77 case DEVDOTDOT:
78 q.type = QTDIR;
79 q.path = 0;
80 DD(c, q, ".", 0, eve, DMDIR|0555, dp);
81 break;
82 case 0:
83 q.path = Ncloneqid;
84 DD(c, q, "clone", 0, eve, 0666, dp);
85 break;
86 case 1:
87 q.path = Naddrqid;
88 DD(c, q, "addr", 0, eve, 0666, dp);
89 break;
90 case 2:
91 q.path = Nstatqid;
92 DD(c, q, "stats", 0, eve, 0444, dp);
93 break;
94 case 3:
95 q.path = Nifstatqid;
96 DD(c, q, "ifstats", 0, eve, 0444, dp);
97 break;
98 default:
99 i -= 4;
100 if(i >= nif->nfile){
101 dprint("-> -1 (2d): %d %d\n", i, nif->nfile);
102 return -1;
103 }
104 if(nif->f[i] == 0){
105 dprint("nif->f[%d] -> 0\n", i);
106 return 0;
107 }
108 q.type = QTDIR;
109 q.path = NETQID(i, N3rdqid);
110 sprint(up->genbuf, "%d", i);
111 DD(c, q, up->genbuf, 0, eve, DMDIR|0555, dp);
112 break;
113 }
114 return 1;
115 }
116
117 /* third level */
118 f = nif->f[NETID(c->qid.path)];
119 if(f == 0){
120 dprint("->f 0\n");
121 return -1;
122 }
123 if(*f->owner){
124 o = f->owner;
125 perm = f->mode;
126 } else {
127 o = eve;
128 perm = 0666;
129 }
130 switch(i){
131 case DEVDOTDOT:
132 q.type = QTDIR;
133 q.path = N2ndqid;
134 strcpy(up->genbuf, nif->name);
135 DD(c, q, up->genbuf, 0, eve, DMDIR|0555, dp);
136 break;
137 case 0:
138 q.path = NETQID(NETID(c->qid.path), Ndataqid);
139 DD(c, q, "data", 0, o, perm, dp);
140 break;
141 case 1:
142 q.path = NETQID(NETID(c->qid.path), Nctlqid);
143 DD(c, q, "ctl", 0, o, perm, dp);
144 break;
145 case 2:
146 q.path = NETQID(NETID(c->qid.path), Nstatqid);
147 DD(c, q, "stats", 0, eve, 0444, dp);
148 break;
149 case 3:
150 q.path = NETQID(NETID(c->qid.path), Ntypeqid);
151 DD(c, q, "type", 0, eve, 0444, dp);
152 break;
153 case 4:
154 q.path = NETQID(NETID(c->qid.path), Nifstatqid);
155 DD(c, q, "ifstats", 0, eve, 0444, dp);
156 break;
157 default:
158 dprint("-> -1 (third)\n");
159 return -1;
160 }
161 return 1;
162 }
163
164 static void
165 prwalk(Netif *nif, Chan *c, Chan *nc, char **name, int nname)
166 {
167 char buf[512], *e, *p;
168
169 if(netifdebug == 0)
170 return;
171 p = buf;
172 e = p + sizeof buf;
173 for(int i = 0; i < nname; i++)
174 p = seprint(p, e, "%s ", name[i]);
175 if(p > buf)
176 p--;
177 *p = 0;
178 print("netifwalk %lld [%s]\n", c->qid.path, buf);
179 }
180
181 Walkqid*
182 netifwalk(Netif *nif, Chan *c, Chan *nc, char **name, int nname)
183 {
184 prwalk(nif, c, nc, name, nname);
185 return devwalk(c, nc, name, nname, (Dirtab *)nif, 0, netifgen);
186 }
187
188 Chan*
189 netifopen(Netif *nif, Chan *c, int omode)
190 {
191 int id;
192 Netfile *f;
193
194 dprint("netifopen %p %d\n", nif, c? c->qid.path: -1);
195 id = 0;
196 if(c->qid.type & QTDIR){
197 if(omode != OREAD)
198 error(Eperm);
199 } else {
200 switch(NETTYPE(c->qid.path)){
201 case Ndataqid:
202 case Nctlqid:
203 id = NETID(c->qid.path);
204 openfile(nif, id);
205 break;
206 case Ncloneqid:
207 id = openfile(nif, -1);
208 c->qid.path = NETQID(id, Nctlqid);
209 break;
210 default:
211 if(omode != OREAD)
212 error(Ebadarg);
213 }
214 switch(NETTYPE(c->qid.path)){
215 case Ndataqid:
216 case Nctlqid:
217 f = nif->f[id];
218 if(netown(f, up->user, omode&7) < 0)
219 error(Eperm);
220 break;
221 }
222 }
223 c->mode = openmode(omode);
224 c->flag |= COPEN;
225 c->offset = 0;
226 c->iounit = qiomaxatomic;
227 return c;
228 }
229
230 long
231 netifread(Netif *nif, Chan *c, void *a, long n, ulong offset)
232 {
233 int i, j;
234 Netfile *f;
235 char *p;
236
237 dprint("netifread %lud %lud\n", c->qid.path, NETTYPE(c->qid.path));
238 if(c->qid.type&QTDIR)
239 return devdirread(c, a, n, (Dirtab*)nif, 0, netifgen);
240
241 switch(NETTYPE(c->qid.path)){
242 case Ndataqid:
243 f = nif->f[NETID(c->qid.path)];
244 return qread(f->in, a, n);
245 case Nctlqid:
246 return readnum(offset, a, n, NETID(c->qid.path), NUMSIZE);
247 case Nstatqid:
248 dprint("netstatqid\n");
249 p = smalloc(READSTR);
250 j = snprint(p, READSTR, "in: %llud\n", nif->inpackets);
251 j += snprint(p+j, READSTR-j, "link: %d\n", nif->link);
252 j += snprint(p+j, READSTR-j, "out: %llud\n", nif->outpackets);
253 j += snprint(p+j, READSTR-j, "crc errs: %d\n", nif->crcs);
254 j += snprint(p+j, READSTR-j, "overflows: %d\n", nif->overflows);
255 j += snprint(p+j, READSTR-j, "soft overflows: %d\n", nif->soverflows);
256 j += snprint(p+j, READSTR-j, "framing errs: %d\n", nif->frames);
257 j += snprint(p+j, READSTR-j, "buffer errs: %d\n", nif->buffs);
258 j += snprint(p+j, READSTR-j, "output errs: %d\n", nif->oerrs);
259 j += snprint(p+j, READSTR-j, "prom: %d\n", nif->prom);
260 j += snprint(p+j, READSTR-j, "mbps: %d\n", nif->mbps);
261 j += snprint(p+j, READSTR-j, "addr: ");
262 for(i = 0; i < nif->alen; i++)
263 j += snprint(p+j, READSTR-j, "%2.2ux", nif->addr[i]);
264 snprint(p+j, READSTR-j, "\n");
265 n = readstr(offset, a, n, p);
266 free(p);
267 return n;
268 case Naddrqid:
269 p = malloc(READSTR);
270 j = 0;
271 for(i = 0; i < nif->alen; i++)
272 j += snprint(p+j, READSTR-j, "%2.2ux", nif->addr[i]);
273 n = readstr(offset, a, n, p);
274 free(p);
275 return n;
276 case Ntypeqid:
277 f = nif->f[NETID(c->qid.path)];
278 return readnum(offset, a, n, f->type, NUMSIZE);
279 case Nifstatqid:
280 return 0;
281 }
282 error(Ebadarg);
283 return -1; /* not reached */
284 }
285
286 Block*
287 netifbread(Netif *nif, Chan *c, long n, ulong offset)
288 {
289 if((c->qid.type & QTDIR) || NETTYPE(c->qid.path) != Ndataqid)
290 return devbread(c, n, offset);
291
292 return qbread(nif->f[NETID(c->qid.path)]->in, n);
293 }
294
295 /*
296 * make sure this type isn't already in use on this device
297 */
298 static int
299 typeinuse(Netif *nif, int type)
300 {
301 Netfile *f, **fp, **efp;
302
303 if(type <= 0)
304 return 0;
305
306 efp = &nif->f[nif->nfile];
307 for(fp = nif->f; fp < efp; fp++){
308 f = *fp;
309 if(f == 0)
310 continue;
311 if(f->type == type)
312 return 1;
313 }
314 return 0;
315 }
316
317 /*
318 * the devxxx.c that calls us handles writing data, it knows best
319 */
320 long
321 netifwrite(Netif *nif, Chan *c, void *a, long n)
322 {
323 Netfile *f;
324 int type;
325 char *p, buf[64];
326 uchar binaddr[Nmaxaddr];
327
328 if(NETTYPE(c->qid.path) != Nctlqid)
329 error(Eperm);
330
331 if(n >= sizeof(buf))
332 n = sizeof(buf)-1;
333 memmove(buf, a, n);
334 buf[n] = 0;
335
336 if(waserror()){
337 QUNLOCK(nif);
338 nexterror();
339 }
340
341 QLOCK(nif);
342 f = nif->f[NETID(c->qid.path)];
343 if((p = matchtoken(buf, "connect")) != 0){
344 type = atoi(p);
345 if(typeinuse(nif, type))
346 error(Einuse);
347 f->type = type;
348 if(f->type < 0)
349 nif->all++;
350 } else if(matchtoken(buf, "promiscuous")){
351 if(f->prom == 0){
352 if(nif->prom == 0 && nif->promiscuous != nil)
353 nif->promiscuous(nif->arg, 1);
354 f->prom = 1;
355 nif->prom++;
356 }
357 } else if((p = matchtoken(buf, "scanbs")) != 0){
358 /* scan for base stations */
359 if(f->scan == 0){
360 type = atoi(p);
361 if(type < 5)
362 type = 5;
363 if(nif->scanbs != nil)
364 nif->scanbs(nif->arg, type);
365 f->scan = type;
366 nif->scan++;
367 }
368 } else if(matchtoken(buf, "bridge")){
369 f->bridge = 1;
370 } else if(matchtoken(buf, "headersonly")){
371 f->headersonly = 1;
372 } else if((p = matchtoken(buf, "addmulti")) != 0){
373 if(parseaddr(binaddr, p, nif->alen) < 0)
374 error("bad address");
375 p = netmulti(nif, f, binaddr, 1);
376 if(p)
377 error(p);
378 } else if((p = matchtoken(buf, "remmulti")) != 0){
379 if(parseaddr(binaddr, p, nif->alen) < 0)
380 error("bad address");
381 p = netmulti(nif, f, binaddr, 0);
382 if(p)
383 error(p);
384 } else
385 n = -1;
386 QUNLOCK(nif);
387 poperror();
388 return n;
389 }
390
391 int
392 netifwstat(Netif *nif, Chan *c, uchar *db, int n)
393 {
394 Dir *dir;
395 Netfile *f;
396 int m;
397
398 f = nif->f[NETID(c->qid.path)];
399 if(f == 0)
400 error(Enonexist);
401
402 if(netown(f, up->user, OWRITE) < 0)
403 error(Eperm);
404
405 dir = smalloc(sizeof(Dir)+n);
406 m = convM2D(db, n, &dir[0], (char*)&dir[1]);
407 if(m == 0){
408 free(dir);
409 error(Eshortstat);
410 }
411 if(!emptystr(dir[0].uid))
412 strncpy(f->owner, dir[0].uid, KNAMELEN);
413 if(dir[0].mode != ~0UL)
414 f->mode = dir[0].mode;
415 free(dir);
416 return m;
417 }
418
419 int
420 netifstat(Netif *nif, Chan *c, uchar *db, int n)
421 {
422 dprint("netifstat %s nfile %d %lld type=%d\n", nif->name, nif->nfile, c->qid.path, c->type);
423 return devstat(c, db, n, (Dirtab *)nif, 0, netifgen);
424 }
425
426 void
427 netifclose(Netif *nif, Chan *c)
428 {
429 Netfile *f;
430 int t;
431 Netaddr *ap;
432
433 if((c->flag & COPEN) == 0)
434 return;
435
436 t = NETTYPE(c->qid.path);
437 if(t != Ndataqid && t != Nctlqid)
438 return;
439
440 f = nif->f[NETID(c->qid.path)];
441 QLOCK(f);
442 if(--(f->inuse) == 0){
443 if(f->prom){
444 QLOCK(nif);
445 if(--(nif->prom) == 0 && nif->promiscuous != nil)
446 nif->promiscuous(nif->arg, 0);
447 QUNLOCK(nif);
448 f->prom = 0;
449 }
450 if(f->scan){
451 QLOCK(nif);
452 if(--(nif->scan) == 0 && nif->scanbs != nil)
453 nif->scanbs(nif->arg, 0);
454 QUNLOCK(nif);
455 f->prom = 0;
456 f->scan = 0;
457 }
458 if(f->nmaddr){
459 QLOCK(nif);
460 t = 0;
461 for(ap = nif->maddr; ap; ap = ap->next){
462 if(f->maddr[t/8] & (1<<(t%8)))
463 netmulti(nif, f, ap->addr, 0);
464 }
465 QUNLOCK(nif);
466 f->nmaddr = 0;
467 }
468 if(f->type < 0){
469 QLOCK(nif);
470 --(nif->all);
471 QUNLOCK(nif);
472 }
473 f->owner[0] = 0;
474 print("drop type %.4ux\n", f->type);
475 f->type = 0;
476 f->bridge = 0;
477 f->headersonly = 0;
478 qclose(f->in);
479 }
480 QUNLOCK(f);
481 }
482
483 Lock netlock;
484
485 static int
486 netown(Netfile *p, char *o, int omode)
487 {
488 static int access[] = { 0400, 0200, 0600, 0100 };
489 int mode;
490 int t;
491
492 lock(&netlock);
493 if(*p->owner){
494 if(strncmp(o, p->owner, KNAMELEN) == 0) /* User */
495 mode = p->mode;
496 else if(strncmp(o, eve, KNAMELEN) == 0) /* Bootes is group */
497 mode = p->mode<<3;
498 else
499 mode = p->mode<<6; /* Other */
500
501 t = access[omode&3];
502 if((t & mode) == t){
503 unlock(&netlock);
504 return 0;
505 } else {
506 unlock(&netlock);
507 return -1;
508 }
509 }
510 strncpy(p->owner, o, KNAMELEN);
511 p->mode = 0660;
512 unlock(&netlock);
513 return 0;
514 }
515
516 /*
517 * Increment the reference count of a network device.
518 * If id < 0, return an unused ether device.
519 */
520 static int
521 openfile(Netif *nif, int id)
522 {
523 Netfile *f, **fp, **efp;
524
525 if(id >= 0){
526 f = nif->f[id];
527 if(f == 0)
528 error(Enodev);
529 QLOCK(f);
530 qreopen(f->in);
531 f->inuse++;
532 QUNLOCK(f);
533 return id;
534 }
535
536 QLOCK(nif);
537 if(waserror()){
538 QUNLOCK(nif);
539 nexterror();
540 }
541 efp = &nif->f[nif->nfile];
542 for(fp = nif->f; fp < efp; fp++){
543 f = *fp;
544 if(f == 0){
545 f = malloc(sizeof(Netfile));
546 if(f == 0)
547 exhausted("memory");
548 f->in = qopen(nif->limit, Qmsg, 0, 0);
549 if(f->in == nil){
550 free(f);
551 exhausted("memory");
552 }
553 *fp = f;
554 QLOCK(f);
555 } else {
556 QLOCK(f);
557 if(f->inuse){
558 QUNLOCK(f);
559 continue;
560 }
561 }
562 f->inuse = 1;
563 qreopen(f->in);
564 netown(f, up->user, 0);
565 QUNLOCK(f);
566 QUNLOCK(nif);
567 poperror();
568 return fp - nif->f;
569 }
570 error(Enodev);
571 return -1; /* not reached */
572 }
573
574 /*
575 * look for a token starting a string,
576 * return a pointer to first non-space char after it
577 */
578 static char*
579 matchtoken(char *p, char *token)
580 {
581 int n;
582
583 n = strlen(token);
584 if(strncmp(p, token, n))
585 return 0;
586 p += n;
587 if(*p == 0)
588 return p;
589 if(*p != ' ' && *p != '\t' && *p != '\n')
590 return 0;
591 while(*p == ' ' || *p == '\t' || *p == '\n')
592 p++;
593 return p;
594 }
595
596 void
597 hnputv(void *p, uvlong v)
598 {
599 uchar *a;
600
601 a = p;
602 hnputl(a, v>>32);
603 hnputl(a+4, v);
604 }
605
606 void
607 hnputl(void *p, uint v)
608 {
609 uchar *a;
610
611 a = p;
612 a[0] = v>>24;
613 a[1] = v>>16;
614 a[2] = v>>8;
615 a[3] = v;
616 }
617
618 void
619 hnputs(void *p, ushort v)
620 {
621 uchar *a;
622
623 a = p;
624 a[0] = v>>8;
625 a[1] = v;
626 }
627
628 uvlong
629 nhgetv(void *p)
630 {
631 uchar *a;
632
633 a = p;
634 return ((vlong)nhgetl(a) << 32) | nhgetl(a+4);
635 }
636
637 uint
638 nhgetl(void *p)
639 {
640 uchar *a;
641
642 a = p;
643 return (a[0]<<24)|(a[1]<<16)|(a[2]<<8)|(a[3]<<0);
644 }
645
646 ushort
647 nhgets(void *p)
648 {
649 uchar *a;
650
651 a = p;
652 return (a[0]<<8)|(a[1]<<0);
653 }
654
655 static ulong
656 hash(uchar *a, int len)
657 {
658 ulong sum = 0;
659
660 while(len-- > 0)
661 sum = (sum << 1) + *a++;
662 return sum%Nmhash;
663 }
664
665 int
666 activemulti(Netif *nif, uchar *addr, int alen)
667 {
668 Netaddr *hp;
669
670 for(hp = nif->mhash[hash(addr, alen)]; hp; hp = hp->hnext)
671 if(memcmp(addr, hp->addr, alen) == 0){
672 if(hp->ref)
673 return 1;
674 else
675 break;
676 }
677 return 0;
678 }
679
680 static int
681 parseaddr(uchar *to, char *from, int alen)
682 {
683 char nip[4];
684 char *p;
685 int i;
686
687 p = from;
688 for(i = 0; i < alen; i++){
689 if(*p == 0)
690 return -1;
691 nip[0] = *p++;
692 if(*p == 0)
693 return -1;
694 nip[1] = *p++;
695 nip[2] = 0;
696 to[i] = strtoul(nip, 0, 16);
697 if(*p == ':')
698 p++;
699 }
700 return 0;
701 }
702
703 /*
704 * keep track of multicast addresses
705 */
706 static char*
707 netmulti(Netif *nif, Netfile *f, uchar *addr, int add)
708 {
709 Netaddr **l, *ap;
710 int i;
711 ulong h;
712
713 if(nif->multicast == nil)
714 return "interface does not support multicast";
715
716 l = &nif->maddr;
717 i = 0;
718 for(ap = *l; ap; ap = *l){
719 if(memcmp(addr, ap->addr, nif->alen) == 0)
720 break;
721 i++;
722 l = &ap->next;
723 }
724
725 if(add){
726 if(ap == 0){
727 *l = ap = smalloc(sizeof(*ap));
728 memmove(ap->addr, addr, nif->alen);
729 ap->next = 0;
730 ap->ref = 1;
731 h = hash(addr, nif->alen);
732 ap->hnext = nif->mhash[h];
733 nif->mhash[h] = ap;
734 } else {
735 ap->ref++;
736 }
737 if(ap->ref == 1){
738 nif->nmaddr++;
739 nif->multicast(nif->arg, addr, 1);
740 }
741 if(i < 8*sizeof(f->maddr)){
742 if((f->maddr[i/8] & (1<<(i%8))) == 0)
743 f->nmaddr++;
744 f->maddr[i/8] |= 1<<(i%8);
745 }
746 } else {
747 if(ap == 0 || ap->ref == 0)
748 return 0;
749 ap->ref--;
750 if(ap->ref == 0){
751 nif->nmaddr--;
752 nif->multicast(nif->arg, addr, 0);
753 }
754 if(i < 8*sizeof(f->maddr)){
755 if((f->maddr[i/8] & (1<<(i%8))) != 0)
756 f->nmaddr--;
757 f->maddr[i/8] &= ~(1<<(i%8));
758 }
759 }
760 return 0;
761 } |