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