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 = malloc(nfile*sizeof(Netfile*));
25 if(nif->f)
26 memset(nif->f, 0, nfile*sizeof(Netfile*));
27 else
28 nif->nfile = 0;
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 sprint(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->env->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,ulong offset)200 netifread(Netif *nif, Chan *c, void *a, long n, ulong offset)
201 {
202 int i, j;
203 Netfile *f;
204 char *p;
205
206 if(c->qid.type&QTDIR)
207 return devdirread(c, a, n, (Dirtab*)nif, 0, netifgen);
208
209 switch(NETTYPE(c->qid.path)){
210 case Ndataqid:
211 f = nif->f[NETID(c->qid.path)];
212 return qread(f->in, a, n);
213 case Nctlqid:
214 return readnum(offset, a, n, NETID(c->qid.path), NUMSIZE);
215 case Nstatqid:
216 p = malloc(READSTR);
217 if(p == nil)
218 return 0;
219 j = snprint(p, READSTR, "in: %d\n", nif->inpackets);
220 j += snprint(p+j, READSTR-j, "link: %d\n", nif->link);
221 j += snprint(p+j, READSTR-j, "out: %d\n", nif->outpackets);
222 j += snprint(p+j, READSTR-j, "crc errs: %d\n", nif->crcs);
223 j += snprint(p+j, READSTR-j, "overflows: %d\n", nif->overflows);
224 j += snprint(p+j, READSTR-j, "soft overflows: %d\n", nif->soverflows);
225 j += snprint(p+j, READSTR-j, "framing errs: %d\n", nif->frames);
226 j += snprint(p+j, READSTR-j, "buffer errs: %d\n", nif->buffs);
227 j += snprint(p+j, READSTR-j, "output errs: %d\n", nif->oerrs);
228 j += snprint(p+j, READSTR-j, "prom: %d\n", nif->prom);
229 j += snprint(p+j, READSTR-j, "mbps: %d\n", nif->mbps);
230 j += snprint(p+j, READSTR-j, "addr: ");
231 for(i = 0; i < nif->alen; i++)
232 j += snprint(p+j, READSTR-j, "%2.2ux", nif->addr[i]);
233 snprint(p+j, READSTR-j, "\n");
234 n = readstr(offset, a, n, p);
235 free(p);
236 return n;
237 case Naddrqid:
238 p = malloc(READSTR);
239 if(p == nil)
240 return 0;
241 j = 0;
242 for(i = 0; i < nif->alen; i++)
243 j += snprint(p+j, READSTR-j, "%2.2ux", nif->addr[i]);
244 n = readstr(offset, a, n, p);
245 free(p);
246 return n;
247 case Ntypeqid:
248 f = nif->f[NETID(c->qid.path)];
249 return readnum(offset, a, n, f->type, NUMSIZE);
250 case Nifstatqid:
251 return 0;
252 }
253 error(Ebadarg);
254 return -1; /* not reached */
255 }
256
257 Block*
netifbread(Netif * nif,Chan * c,long n,ulong offset)258 netifbread(Netif *nif, Chan *c, long n, ulong offset)
259 {
260 if((c->qid.type & QTDIR) || NETTYPE(c->qid.path) != Ndataqid)
261 return devbread(c, n, offset);
262
263 return qbread(nif->f[NETID(c->qid.path)]->in, n);
264 }
265
266 /*
267 * make sure this type isn't already in use on this device
268 */
269 static int
typeinuse(Netif * nif,int type)270 typeinuse(Netif *nif, int type)
271 {
272 Netfile *f, **fp, **efp;
273
274 if(type <= 0)
275 return 0;
276
277 efp = &nif->f[nif->nfile];
278 for(fp = nif->f; fp < efp; fp++){
279 f = *fp;
280 if(f == 0)
281 continue;
282 if(f->type == type)
283 return 1;
284 }
285 return 0;
286 }
287
288 /*
289 * the devxxx.c that calls us handles writing data, it knows best
290 */
291 long
netifwrite(Netif * nif,Chan * c,void * a,long n)292 netifwrite(Netif *nif, Chan *c, void *a, long n)
293 {
294 Netfile *f;
295 int type;
296 char *p, buf[64];
297 uchar binaddr[Nmaxaddr];
298
299 if(NETTYPE(c->qid.path) != Nctlqid)
300 error(Eperm);
301
302 if(n >= sizeof(buf))
303 n = sizeof(buf)-1;
304 memmove(buf, a, n);
305 buf[n] = 0;
306
307 if(waserror()){
308 qunlock(nif);
309 nexterror();
310 }
311
312 qlock(nif);
313 f = nif->f[NETID(c->qid.path)];
314 if((p = matchtoken(buf, "connect")) != 0){
315 type = atoi(p);
316 if(typeinuse(nif, type))
317 error(Einuse);
318 f->type = type;
319 if(f->type < 0)
320 nif->all++;
321 } else if(matchtoken(buf, "promiscuous")){
322 if(f->prom == 0){
323 if(nif->prom == 0 && nif->promiscuous != nil)
324 nif->promiscuous(nif->arg, 1);
325 f->prom = 1;
326 nif->prom++;
327 }
328 } else if((p = matchtoken(buf, "scanbs")) != 0){
329 /* scan for base stations */
330 if(f->scan == 0){
331 type = atoi(p);
332 if(type < 5)
333 type = 5;
334 if(nif->scanbs != nil)
335 nif->scanbs(nif->arg, type);
336 f->scan = type;
337 nif->scan++;
338 }
339 } else if(matchtoken(buf, "bridge")){
340 f->bridge = 1;
341 } else if(matchtoken(buf, "headersonly")){
342 f->headersonly = 1;
343 } else if((p = matchtoken(buf, "addmulti")) != 0){
344 if(parseaddr(binaddr, p, nif->alen) < 0)
345 error("bad address");
346 p = netmulti(nif, f, binaddr, 1);
347 if(p)
348 error(p);
349 } else if((p = matchtoken(buf, "remmulti")) != 0){
350 if(parseaddr(binaddr, p, nif->alen) < 0)
351 error("bad address");
352 p = netmulti(nif, f, binaddr, 0);
353 if(p)
354 error(p);
355 } else
356 n = -1;
357 qunlock(nif);
358 poperror();
359 return n;
360 }
361
362 int
netifwstat(Netif * nif,Chan * c,uchar * db,int n)363 netifwstat(Netif *nif, Chan *c, uchar *db, int n)
364 {
365 Dir *dir;
366 Netfile *f;
367 int m;
368
369 f = nif->f[NETID(c->qid.path)];
370 if(f == 0)
371 error(Enonexist);
372
373 if(netown(f, up->env->user, OWRITE) < 0)
374 error(Eperm);
375
376 dir = smalloc(sizeof(Dir)+n);
377 m = convM2D(db, n, &dir[0], (char*)&dir[1]);
378 if(m == 0){
379 free(dir);
380 error(Eshortstat);
381 }
382 if(!emptystr(dir[0].uid))
383 strncpy(f->owner, dir[0].uid, KNAMELEN);
384 if(dir[0].mode != ~0UL)
385 f->mode = dir[0].mode;
386 free(dir);
387 return m;
388 }
389
390 int
netifstat(Netif * nif,Chan * c,uchar * db,int n)391 netifstat(Netif *nif, Chan *c, uchar *db, int n)
392 {
393 return devstat(c, db, n, (Dirtab *)nif, 0, netifgen);
394 }
395
396 void
netifclose(Netif * nif,Chan * c)397 netifclose(Netif *nif, Chan *c)
398 {
399 Netfile *f;
400 int t;
401 Netaddr *ap;
402
403 if((c->flag & COPEN) == 0)
404 return;
405
406 t = NETTYPE(c->qid.path);
407 if(t != Ndataqid && t != Nctlqid)
408 return;
409
410 f = nif->f[NETID(c->qid.path)];
411 qlock(f);
412 if(--(f->inuse) == 0){
413 if(f->prom){
414 qlock(nif);
415 if(--(nif->prom) == 0 && nif->promiscuous != nil)
416 nif->promiscuous(nif->arg, 0);
417 qunlock(nif);
418 f->prom = 0;
419 }
420 if(f->scan){
421 qlock(nif);
422 if(--(nif->scan) == 0 && nif->scanbs != nil)
423 nif->scanbs(nif->arg, 0);
424 qunlock(nif);
425 f->prom = 0;
426 f->scan = 0;
427 }
428 if(f->nmaddr){
429 qlock(nif);
430 t = 0;
431 for(ap = nif->maddr; ap; ap = ap->next){
432 if(f->maddr[t/8] & (1<<(t%8)))
433 netmulti(nif, f, ap->addr, 0);
434 }
435 qunlock(nif);
436 f->nmaddr = 0;
437 }
438 if(f->type < 0){
439 qlock(nif);
440 --(nif->all);
441 qunlock(nif);
442 }
443 f->owner[0] = 0;
444 f->type = 0;
445 f->bridge = 0;
446 f->headersonly = 0;
447 qclose(f->in);
448 }
449 qunlock(f);
450 }
451
452 Lock netlock;
453
454 static int
netown(Netfile * p,char * o,int omode)455 netown(Netfile *p, char *o, int omode)
456 {
457 static int access[] = { 0400, 0200, 0600, 0100 };
458 int mode;
459 int t;
460
461 lock(&netlock);
462 if(*p->owner){
463 if(strncmp(o, p->owner, KNAMELEN) == 0) /* User */
464 mode = p->mode;
465 else if(strncmp(o, eve, KNAMELEN) == 0) /* Bootes is group */
466 mode = p->mode<<3;
467 else
468 mode = p->mode<<6; /* Other */
469
470 t = access[omode&3];
471 if((t & mode) == t){
472 unlock(&netlock);
473 return 0;
474 } else {
475 unlock(&netlock);
476 return -1;
477 }
478 }
479 strncpy(p->owner, o, KNAMELEN);
480 p->mode = 0660;
481 unlock(&netlock);
482 return 0;
483 }
484
485 /*
486 * Increment the reference count of a network device.
487 * If id < 0, return an unused ether device.
488 */
489 static int
openfile(Netif * nif,int id)490 openfile(Netif *nif, int id)
491 {
492 Netfile *f, **fp, **efp;
493
494 if(id >= 0){
495 f = nif->f[id];
496 if(f == 0)
497 error(Enodev);
498 qlock(f);
499 qreopen(f->in);
500 f->inuse++;
501 qunlock(f);
502 return id;
503 }
504
505 qlock(nif);
506 if(waserror()){
507 qunlock(nif);
508 nexterror();
509 }
510 efp = &nif->f[nif->nfile];
511 for(fp = nif->f; fp < efp; fp++){
512 f = *fp;
513 if(f == 0){
514 f = malloc(sizeof(Netfile));
515 if(f == 0)
516 exhausted("memory");
517 f->in = qopen(nif->limit, Qmsg, 0, 0);
518 if(f->in == nil){
519 free(f);
520 exhausted("memory");
521 }
522 *fp = f;
523 qlock(f);
524 } else {
525 qlock(f);
526 if(f->inuse){
527 qunlock(f);
528 continue;
529 }
530 }
531 f->inuse = 1;
532 qreopen(f->in);
533 netown(f, up->env->user, 0);
534 qunlock(f);
535 qunlock(nif);
536 poperror();
537 return fp - nif->f;
538 }
539 error(Enodev);
540 return -1; /* not reached */
541 }
542
543 /*
544 * look for a token starting a string,
545 * return a pointer to first non-space char after it
546 */
547 static char*
matchtoken(char * p,char * token)548 matchtoken(char *p, char *token)
549 {
550 int n;
551
552 n = strlen(token);
553 if(strncmp(p, token, n))
554 return 0;
555 p += n;
556 if(*p == 0)
557 return p;
558 if(*p != ' ' && *p != '\t' && *p != '\n')
559 return 0;
560 while(*p == ' ' || *p == '\t' || *p == '\n')
561 p++;
562 return p;
563 }
564
565 static ulong
hash(uchar * a,int len)566 hash(uchar *a, int len)
567 {
568 ulong sum = 0;
569
570 while(len-- > 0)
571 sum = (sum << 1) + *a++;
572 return sum%Nmhash;
573 }
574
575 int
activemulti(Netif * nif,uchar * addr,int alen)576 activemulti(Netif *nif, uchar *addr, int alen)
577 {
578 Netaddr *hp;
579
580 for(hp = nif->mhash[hash(addr, alen)]; hp; hp = hp->hnext)
581 if(memcmp(addr, hp->addr, alen) == 0){
582 if(hp->ref)
583 return 1;
584 else
585 break;
586 }
587 return 0;
588 }
589
590 static int
parseaddr(uchar * to,char * from,int alen)591 parseaddr(uchar *to, char *from, int alen)
592 {
593 char nip[4];
594 char *p;
595 int i;
596
597 p = from;
598 for(i = 0; i < alen; i++){
599 if(*p == 0)
600 return -1;
601 nip[0] = *p++;
602 if(*p == 0)
603 return -1;
604 nip[1] = *p++;
605 nip[2] = 0;
606 to[i] = strtoul(nip, 0, 16);
607 if(*p == ':')
608 p++;
609 }
610 return 0;
611 }
612
613 /*
614 * keep track of multicast addresses
615 */
616 static char*
netmulti(Netif * nif,Netfile * f,uchar * addr,int add)617 netmulti(Netif *nif, Netfile *f, uchar *addr, int add)
618 {
619 Netaddr **l, *ap;
620 int i;
621 ulong h;
622
623 if(nif->multicast == nil)
624 return "interface does not support multicast";
625
626 l = &nif->maddr;
627 i = 0;
628 for(ap = *l; ap; ap = *l){
629 if(memcmp(addr, ap->addr, nif->alen) == 0)
630 break;
631 i++;
632 l = &ap->next;
633 }
634
635 if(add){
636 if(ap == 0){
637 *l = ap = smalloc(sizeof(*ap));
638 memmove(ap->addr, addr, nif->alen);
639 ap->next = 0;
640 ap->ref = 1;
641 h = hash(addr, nif->alen);
642 ap->hnext = nif->mhash[h];
643 nif->mhash[h] = ap;
644 } else {
645 ap->ref++;
646 }
647 if(ap->ref == 1){
648 nif->nmaddr++;
649 nif->multicast(nif->arg, addr, 1);
650 }
651 if(i < 8*sizeof(f->maddr)){
652 if((f->maddr[i/8] & (1<<(i%8))) == 0)
653 f->nmaddr++;
654 f->maddr[i/8] |= 1<<(i%8);
655 }
656 } else {
657 if(ap == 0 || ap->ref == 0)
658 return 0;
659 ap->ref--;
660 if(ap->ref == 0){
661 nif->nmaddr--;
662 nif->multicast(nif->arg, addr, 0);
663 }
664 if(i < 8*sizeof(f->maddr)){
665 if((f->maddr[i/8] & (1<<(i%8))) != 0)
666 f->nmaddr--;
667 f->maddr[i/8] &= ~(1<<(i%8));
668 }
669 }
670 return 0;
671 }
672