Adds chaninfo()
[akaros.git] / kern / src / net / netif.c
1 // INFERNO
2 #include <vfs.h>
3 #include <kfs.h>
4 #include <slab.h>
5 #include <kmalloc.h>
6 #include <kref.h>
7 #include <string.h>
8 #include <stdio.h>
9 #include <assert.h>
10 #include <error.h>
11 #include <cpio.h>
12 #include <pmap.h>
13 #include <smp.h>
14 #include <ip.h>
15
16 static int netown(struct netfile*, char *unused_char_p_t, int);
17 static int openfile(struct netif*, int);
18 static char* matchtoken( char *unused_char_p_t, char*);
19 static char* netmulti(struct netif*, struct netfile*, uint8_t *unused_uint8_p_t, int);
20 static int parseaddr( uint8_t *unused_uint8_p_t, char *unused_char_p_t, int);
21
22 /*
23  *  set up a new network interface
24  */
25 void
26 netifinit(struct netif *nif, char *name, int nfile, uint32_t limit)
27 {
28         qlock_init(&nif->qlock);
29         strncpy(nif->name, name, KNAMELEN-1);
30         nif->name[KNAMELEN-1] = 0;
31         nif->nfile = nfile;
32         nif->f = kzmalloc(nfile * sizeof(struct netfile *), 0);
33         if(nif->f)
34                 memset(nif->f, 0, nfile*sizeof(struct netfile*));
35         else
36                 nif->nfile = 0;
37         nif->limit = limit;
38 }
39
40 /*
41  *  generate a 3 level directory
42  */
43 static int
44 netifgen(struct chan *c, char *unused_char_p_t, struct dirtab *vp, int unused_int, int i, struct dir *dp)
45 {
46         struct qid q;
47         struct netif *nif = (struct netif*)vp;
48         struct netfile *f;
49         int perm;
50         char *o;
51
52         q.type = QTFILE;
53         q.vers = 0;
54
55         /* top level directory contains the name of the network */
56         if(c->qid.path == 0){
57                 switch(i){
58                 case DEVDOTDOT:
59                         q.path = 0;
60                         q.type = QTDIR;
61                         devdir(c, q, ".", 0, eve, 0555, dp);
62                         break;
63                 case 0:
64                         q.path = N2ndqid;
65                         q.type = QTDIR;
66                         strncpy(get_cur_genbuf(),  nif->name, GENBUF_SZ);
67                         devdir(c, q, get_cur_genbuf(), 0, eve, 0555, dp);
68                         break;
69                 default:
70                         return -1;
71                 }
72                 return 1;
73         }
74
75         /* second level contains clone plus all the conversations.
76          *
77          * This ancient comment is from plan9.  Inferno and nxm both had issues
78          * here.  You couldn't ls /net/ether0/ when it didn't have any convs.  There
79          * were also issues with nxm where you couldn't stat ether0/x/stats
80          * properly.
81          *
82          * The issue is that if we handle things like Nstatqid, then we will never
83          * pass it down to the third level. And since we just set the path ==
84          * Nstatqid, we won't have the NETID muxed in. If someone isn't trying to
85          * generate a chan, but instead is looking it up (devwalk generates, devstat
86          * already has the chan), then they are also looking for a devdir with path
87          * containing ID << 5. So if you stat ether0/1/ifstats, devstat is looking
88          * for path 41, but we return path 9 (41 = 32 + 9). (these numbers are
89          * before we tracked NETID + 1).
90          *
91          * We (akaros and plan9) had a big if here, that would catch things that do
92          * not exist in the subdirs of a netif. Things like clone make sense here.
93          * I guess addr too, though that seems to be added since the original
94          * comment. You can see what the 3rd level was expecting to parse by looking
95          * farther down in the code.
96          *
97          * The root of the problem was that the old code couldn't tell the
98          * difference between no netid and netid 0. Now, we determine if we're at
99          * the second level by the lack of a netid, instead of trying to enumerate
100          * the qid types that the second level could have. The latter approach
101          * allowed for something like ether0/1/stats, but we couldn't actually
102          * devstat ether0/stats directly. It's worth noting that there is no
103          * difference to the content of ether0/stats and ether0/x/stats (when you
104          * read), but they have different chan qids.
105          *
106          * Here's the old if block:
107                 t = NETTYPE(c->qid.path);
108                 if (t == N2ndqid || t == Ncloneqid || t == Naddrqid) {
109          */
110         if (NETID(c->qid.path) == -1) {
111                 switch(i) {
112                 case DEVDOTDOT:
113                         q.type = QTDIR;
114                         q.path = 0;
115                         devdir(c, q, ".", 0, eve, DMDIR|0555, dp);
116                         break;
117                 case 0:
118                         q.path = Ncloneqid;
119                         devdir(c, q, "clone", 0, eve, 0666, dp);
120                         break;
121                 case 1:
122                         q.path = Naddrqid;
123                         devdir(c, q, "addr", 0, eve, 0666, dp);
124                         break;
125                 case 2:
126                         q.path = Nstatqid;
127                         devdir(c, q, "stats", 0, eve, 0444, dp);
128                         break;
129                 case 3:
130                         q.path = Nifstatqid;
131                         devdir(c, q, "ifstats", 0, eve, 0444, dp);
132                         break;
133                 default:
134                         i -= 4;
135                         if(i >= nif->nfile)
136                                 return -1;
137                         if(nif->f[i] == 0)
138                                 return 0;
139                         q.type = QTDIR;
140                         q.path = NETQID(i, N3rdqid);
141                         snprintf(get_cur_genbuf(), GENBUF_SZ, "%d", i);
142                         devdir(c, q, get_cur_genbuf(), 0, eve, DMDIR|0555, dp);
143                         break;
144                 }
145                 return 1;
146         }
147
148         /* third level */
149         f = nif->f[NETID(c->qid.path)];
150         if(f == 0)
151                 return 0;
152         if(*f->owner){
153                 o = f->owner;
154                 perm = f->mode;
155         } else {
156                 o = eve;
157                 perm = 0666;
158         }
159         switch(i){
160         case DEVDOTDOT:
161                 q.type = QTDIR;
162                 q.path = N2ndqid;
163                 strncpy(get_cur_genbuf(),  nif->name, GENBUF_SZ);
164                 devdir(c, q, get_cur_genbuf(), 0, eve, DMDIR|0555, dp);
165                 break;
166         case 0:
167                 q.path = NETQID(NETID(c->qid.path), Ndataqid);
168                 devdir(c, q, "data", 0, o, perm, dp);
169                 break;
170         case 1:
171                 q.path = NETQID(NETID(c->qid.path), Nctlqid);
172                 devdir(c, q, "ctl", 0, o, perm, dp);
173                 break;
174         case 2:
175                 q.path = NETQID(NETID(c->qid.path), Nstatqid);
176                 devdir(c, q, "stats", 0, eve, 0444, dp);
177                 break;
178         case 3:
179                 q.path = NETQID(NETID(c->qid.path), Ntypeqid);
180                 devdir(c, q, "type", 0, eve, 0444, dp);
181                 break;
182         case 4:
183                 q.path = NETQID(NETID(c->qid.path), Nifstatqid);
184                 devdir(c, q, "ifstats", 0, eve, 0444, dp);
185                 break;
186         default:
187                 return -1;
188         }
189         return 1;
190 }
191
192 struct walkqid*
193 netifwalk(struct netif *nif, struct chan *c, struct chan *nc, char **name, int nname)
194 {
195         return devwalk(c, nc, name, nname, (struct dirtab *)nif, 0, netifgen);
196 }
197
198 struct chan*
199 netifopen(struct netif *nif, struct chan *c, int omode)
200 {
201         int id;
202         struct netfile *f;
203
204         id = 0;
205         if(c->qid.type & QTDIR){
206                 if (!IS_RDONLY(omode))
207                         error(Eperm);
208         } else {
209                 switch(NETTYPE(c->qid.path)){
210                 case Ndataqid:
211                 case Nctlqid:
212                         id = NETID(c->qid.path);
213                         openfile(nif, id);
214                         break;
215                 case Ncloneqid:
216                         id = openfile(nif, -1);
217                         c->qid.path = NETQID(id, Nctlqid);
218                         break;
219                 default:
220                         if (!IS_RDONLY(omode))
221                                 error(Ebadarg);
222                 }
223                 switch(NETTYPE(c->qid.path)){
224                 case Ndataqid:
225                 case Nctlqid:
226                         f = nif->f[id];
227                         if(netown(f, current->user, omode&7) < 0)
228                                 error(Eperm);
229                         break;
230                 }
231         }
232         c->mode = openmode(omode);
233         c->flag |= COPEN;
234         c->offset = 0;
235         c->iounit = qiomaxatomic;
236         return c;
237 }
238
239 long
240 netifread(struct netif *nif, struct chan *c, void *a, long n, uint32_t offset)
241 {
242         int i, j;
243         struct netfile *f;
244         char *p;
245
246         if(c->qid.type&QTDIR)
247                 return devdirread(c, a, n, (struct dirtab*)nif, 0, netifgen);
248
249         switch(NETTYPE(c->qid.path)){
250         case Ndataqid:
251                 f = nif->f[NETID(c->qid.path)];
252                 return qread(f->in, a, n);
253         case Nctlqid:
254                 return readnum(offset, a, n, NETID(c->qid.path), NUMSIZE);
255         case Nstatqid:
256                 p = kzmalloc(READSTR, 0);
257                 if(p == NULL)
258                         return 0;
259                 j = snprintf(p, READSTR, "in: %d\n", nif->inpackets);
260                 j += snprintf(p+j, READSTR-j, "link: %d\n", nif->link);
261                 j += snprintf(p+j, READSTR-j, "out: %d\n", nif->outpackets);
262                 j += snprintf(p+j, READSTR-j, "crc errs: %d\n", nif->crcs);
263                 j += snprintf(p+j, READSTR-j, "overflows: %d\n", nif->overflows);
264                 j += snprintf(p+j, READSTR-j, "soft overflows: %d\n", nif->soverflows);
265                 j += snprintf(p+j, READSTR-j, "framing errs: %d\n", nif->frames);
266                 j += snprintf(p+j, READSTR-j, "buffer errs: %d\n", nif->buffs);
267                 j += snprintf(p+j, READSTR-j, "output errs: %d\n", nif->oerrs);
268                 j += snprintf(p+j, READSTR-j, "prom: %d\n", nif->prom);
269                 j += snprintf(p+j, READSTR-j, "mbps: %d\n", nif->mbps);
270                 j += snprintf(p+j, READSTR-j, "addr: ");
271                 for(i = 0; i < nif->alen; i++)
272                         j += snprintf(p+j, READSTR-j, "%02.2x", nif->addr[i]);
273                 snprintf(p+j, READSTR-j, "\n");
274                 n = readstr(offset, a, n, p);
275                 kfree(p);
276                 return n;
277         case Naddrqid:
278                 p = kzmalloc(READSTR, 0);
279                 if(p == NULL)
280                         return 0;
281                 j = 0;
282                 for(i = 0; i < nif->alen; i++)
283                         j += snprintf(p+j, READSTR-j, "%02.2x", nif->addr[i]);
284                 n = readstr(offset, a, n, p);
285                 kfree(p);
286                 return n;
287         case Ntypeqid:
288                 f = nif->f[NETID(c->qid.path)];
289                 return readnum(offset, a, n, f->type, NUMSIZE);
290         case Nifstatqid:
291                 return 0;
292         }
293         error(Ebadarg);
294         return -1;      /* not reached */
295 }
296
297 struct block*
298 netifbread(struct netif *nif, struct chan *c, long n, uint32_t offset)
299 {
300         if((c->qid.type & QTDIR) || NETTYPE(c->qid.path) != Ndataqid)
301                 return devbread(c, n, offset);
302
303         return qbread(nif->f[NETID(c->qid.path)]->in, n);
304 }
305
306 /*
307  *  make sure this type isn't already in use on this device
308  */
309 static int
310 typeinuse(struct netif *nif, int type)
311 {
312         struct netfile *f, **fp, **efp;
313
314         if(type <= 0)
315                 return 0;
316
317         efp = &nif->f[nif->nfile];
318         for(fp = nif->f; fp < efp; fp++){
319                 f = *fp;
320                 if(f == 0)
321                         continue;
322                 if(f->type == type)
323                         return 1;
324         }
325         return 0;
326 }
327
328 /*
329  *  the devxxx.c that calls us handles writing data, it knows best
330  */
331 long
332 netifwrite(struct netif *nif, struct chan *c, void *a, long n)
333 {
334         ERRSTACK(1);
335         struct netfile *f;
336         int type;
337         char *p, buf[64];
338         uint8_t binaddr[Nmaxaddr];
339
340         if(NETTYPE(c->qid.path) != Nctlqid)
341                 error(Eperm);
342
343         if(n >= sizeof(buf))
344                 n = sizeof(buf)-1;
345         memmove(buf, a, n);
346         buf[n] = 0;
347
348         if(waserror()){
349                 qunlock(&nif->qlock);
350                 nexterror();
351         }
352
353         qlock(&nif->qlock);
354         f = nif->f[NETID(c->qid.path)];
355         if((p = matchtoken(buf, "connect")) != 0){
356                 type = strtol(p, 0, 0); /* allows any base, though usually hex */
357                 if(typeinuse(nif, type))
358                         error(Einuse);
359                 f->type = type;
360                 if(f->type < 0)
361                         nif->all++;
362         } else if(matchtoken(buf, "promiscuous")){
363                 if(f->prom == 0){
364                         if(nif->prom == 0 && nif->promiscuous != NULL)
365                                 nif->promiscuous(nif->arg, 1);
366                         f->prom = 1;
367                         nif->prom++;
368                 }
369         } else if((p = matchtoken(buf, "scanbs")) != 0){
370                 /* scan for base stations */
371                 if(f->scan == 0){
372                         type = strtol(p, 0, 0); /* allows any base, though usually hex */
373                         if(type < 5)
374                                 type = 5;
375                         if(nif->scanbs != NULL)
376                                 nif->scanbs(nif->arg, type);
377                         f->scan = type;
378                         nif->scan++;
379                 }
380         } else if(matchtoken(buf, "bridge")){
381                 f->bridge = 1;
382         } else if(matchtoken(buf, "headersonly")){
383                 f->headersonly = 1;
384         } else if((p = matchtoken(buf, "addmulti")) != 0){
385                 if(parseaddr(binaddr, p, nif->alen) < 0)
386                         error("bad address");
387                 p = netmulti(nif, f, binaddr, 1);
388                 if(p)
389                         error(p);
390         } else if((p = matchtoken(buf, "remmulti")) != 0){
391                 if(parseaddr(binaddr, p, nif->alen) < 0)
392                         error("bad address");
393                 p = netmulti(nif, f, binaddr, 0);
394                 if(p)
395                         error(p);
396         } else
397                 n = -1;
398         qunlock(&nif->qlock);
399         poperror();
400         return n;
401 }
402
403 int
404 netifwstat(struct netif *nif, struct chan *c, uint8_t *db, int n)
405 {
406         struct dir *dir;
407         struct netfile *f;
408         int m;
409
410         f = nif->f[NETID(c->qid.path)];
411         if(f == 0) {
412                 set_errno(ENOENT);
413                 error(Enonexist);
414         }
415
416         if(netown(f, current->user, OWRITE) < 0)
417                 error(Eperm);
418
419         dir = kzmalloc(sizeof(struct dir) + n, 0);
420         m = convM2D(db, n, &dir[0], (char*)&dir[1]);
421         if(m == 0){
422                 kfree(dir);
423                 error(Eshortstat);
424         }
425         if(!emptystr(dir[0].uid))
426                 strncpy(f->owner, dir[0].uid, KNAMELEN);
427         if(dir[0].mode != ~0UL)
428                 f->mode = dir[0].mode;
429         kfree(dir);
430         return m;
431 }
432
433 int
434 netifstat(struct netif *nif, struct chan *c, uint8_t *db, int n)
435 {
436         return devstat(c, db, n, (struct dirtab *)nif, 0, netifgen);
437 }
438
439 void
440 netifclose(struct netif *nif, struct chan *c)
441 {
442         struct netfile *f;
443         int t;
444         struct netaddr *ap;
445
446         if((c->flag & COPEN) == 0)
447                 return;
448
449         t = NETTYPE(c->qid.path);
450         if(t != Ndataqid && t != Nctlqid)
451                 return;
452
453         f = nif->f[NETID(c->qid.path)];
454         qlock(&f->qlock);
455         if(--(f->inuse) == 0){
456                 if(f->prom){
457                         qlock(&nif->qlock);
458                         if(--(nif->prom) == 0 && nif->promiscuous != NULL)
459                                 nif->promiscuous(nif->arg, 0);
460                         qunlock(&nif->qlock);
461                         f->prom = 0;
462                 }
463                 if(f->scan){
464                         qlock(&nif->qlock);
465                         if(--(nif->scan) == 0 && nif->scanbs != NULL)
466                                 nif->scanbs(nif->arg, 0);
467                         qunlock(&nif->qlock);
468                         f->prom = 0;
469                         f->scan = 0;
470                 }
471                 if(f->nmaddr){
472                         qlock(&nif->qlock);
473                         t = 0;
474                         for(ap = nif->maddr; ap; ap = ap->next){
475                                 if(f->maddr[t/8] & (1<<(t%8)))
476                                         netmulti(nif, f, ap->addr, 0);
477                         }
478                         qunlock(&nif->qlock);
479                         f->nmaddr = 0;
480                 }
481                 if(f->type < 0){
482                         qlock(&nif->qlock);
483                         --(nif->all);
484                         qunlock(&nif->qlock);
485                 }
486                 f->owner[0] = 0;
487                 f->type = 0;
488                 f->bridge = 0;
489                 f->headersonly = 0;
490                 qclose(f->in);
491         }
492         qunlock(&f->qlock);
493 }
494
495 spinlock_t netlock = SPINLOCK_INITIALIZER;
496
497 static int
498 netown(struct netfile *p, char *o, int omode)
499 {
500         static int access[] = { 0400, 0200, 0600, 0100 };
501         int mode;
502         int t;
503
504         spin_lock(&netlock);
505         if(*p->owner){
506                 if(strncmp(o, p->owner, KNAMELEN) == 0) /* User */
507                         mode = p->mode;
508                 else if(strncmp(o, eve, KNAMELEN) == 0) /* Bootes is group */
509                         mode = p->mode<<3;
510                 else
511                         mode = p->mode<<6;              /* Other */
512
513                 t = access[omode&3];
514                 if((t & mode) == t){
515                         spin_unlock(&netlock);
516                         return 0;
517                 } else {
518                         spin_unlock(&netlock);
519                         return -1;
520                 }
521         }
522         strncpy(p->owner, o, KNAMELEN);
523         p->mode = 0660;
524         spin_unlock(&netlock);
525         return 0;
526 }
527
528 /*
529  *  Increment the reference count of a network device.
530  *  If id < 0, return an unused ether device.
531  */
532 static int
533 openfile(struct netif *nif, int id)
534 {
535         ERRSTACK(1);
536         struct netfile *f, **fp, **efp;
537
538         if(id >= 0){
539                 f = nif->f[id];
540                 if(f == 0)
541                         error(Enodev);
542                 qlock(&f->qlock);
543                 qreopen(f->in);
544                 f->inuse++;
545                 qunlock(&f->qlock);
546                 return id;
547         }
548
549         qlock(&nif->qlock);
550         if(waserror()){
551                 qunlock(&nif->qlock);
552                 nexterror();
553         }
554         efp = &nif->f[nif->nfile];
555         for(fp = nif->f; fp < efp; fp++){
556                 f = *fp;
557                 if(f == 0){
558                         f = kzmalloc(sizeof(struct netfile), 0);
559                         if(f == 0)
560                                 exhausted("memory");
561                         /* since we lock before netifinit (if we ever call that...) */
562                         qlock_init(&f->qlock);
563                         f->in = qopen(nif->limit, Qmsg, 0, 0);
564                         if(f->in == NULL){
565                                 kfree(f);
566                                 exhausted("memory");
567                         }
568                         *fp = f;
569                         qlock(&f->qlock);
570                 } else {
571                         qlock(&f->qlock);
572                         if(f->inuse){
573                                 qunlock(&f->qlock);
574                                 continue;
575                         }
576                 }
577                 f->inuse = 1;
578                 qreopen(f->in);
579                 netown(f, current->user, 0);
580                 qunlock(&f->qlock);
581                 qunlock(&nif->qlock);
582                 poperror();
583                 return fp - nif->f;
584         }
585         error(Enodev);
586         return -1;      /* not reached */
587 }
588
589 /*
590  *  look for a token starting a string,
591  *  return a pointer to first non-space char after it
592  */
593 static char*
594 matchtoken(char *p, char *token)
595 {
596         int n;
597
598         n = strlen(token);
599         if(strncmp(p, token, n))
600                 return 0;
601         p += n;
602         if(*p == 0)
603                 return p;
604         if(*p != ' ' && *p != '\t' && *p != '\n')
605                 return 0;
606         while(*p == ' ' || *p == '\t' || *p == '\n')
607                 p++;
608         return p;
609 }
610
611 static uint32_t
612 hash(uint8_t *a, int len)
613 {
614         uint32_t sum = 0;
615
616         while(len-- > 0)
617                 sum = (sum << 1) + *a++;
618         return sum%Nmhash;
619 }
620
621 int
622 activemulti(struct netif *nif, uint8_t *addr, int alen)
623 {
624         struct netaddr *hp;
625
626         for(hp = nif->mhash[hash(addr, alen)]; hp; hp = hp->hnext)
627                 if(memcmp(addr, hp->addr, alen) == 0){
628                         if(hp->ref)
629                                 return 1;
630                         else
631                                 break;
632                 }
633         return 0;
634 }
635
636 static int
637 parseaddr(uint8_t *to, char *from, int alen)
638 {
639         char nip[4];
640         char *p;
641         int i;
642
643         p = from;
644         for(i = 0; i < alen; i++){
645                 if(*p == 0)
646                         return -1;
647                 nip[0] = *p++;
648                 if(*p == 0)
649                         return -1;
650                 nip[1] = *p++;
651                 nip[2] = 0;
652                 to[i] = strtoul(nip, 0, 16);
653                 if(*p == ':')
654                         p++;
655         }
656         return 0;
657 }
658
659 /*
660  *  keep track of multicast addresses
661  */
662 static char*
663 netmulti(struct netif *nif, struct netfile *f, uint8_t *addr, int add)
664 {
665         struct netaddr **l, *ap;
666         int i;
667         uint32_t h;
668
669         if(nif->multicast == NULL)
670                 return "interface does not support multicast";
671
672         l = &nif->maddr;
673         i = 0;
674         for(ap = *l; ap; ap = *l){
675                 if(memcmp(addr, ap->addr, nif->alen) == 0)
676                         break;
677                 i++;
678                 l = &ap->next;
679         }
680
681         if(add){
682                 if(ap == 0){
683                         /* TODO: AFAIK, this never gets freed.  if we fix that, we can use a
684                          * kref too (instead of int ap->ref). */
685                         *l = ap = kzmalloc(sizeof(*ap), 0);
686                         memmove(ap->addr, addr, nif->alen);
687                         ap->next = 0;
688                         ap->ref = 1;
689                         h = hash(addr, nif->alen);
690                         ap->hnext = nif->mhash[h];
691                         nif->mhash[h] = ap;
692                 } else {
693                         ap->ref++;
694                 }
695                 if(ap->ref == 1){
696                         nif->nmaddr++;
697                         nif->multicast(nif->arg, addr, 1);
698                 }
699                 if(i < 8*sizeof(f->maddr)){
700                         if((f->maddr[i/8] & (1<<(i%8))) == 0)
701                                 f->nmaddr++;
702                         f->maddr[i/8] |= 1<<(i%8);
703                 }
704         } else {
705                 if(ap == 0 || ap->ref == 0)
706                         return 0;
707                 ap->ref--;
708                 if(ap->ref == 0){
709                         nif->nmaddr--;
710                         nif->multicast(nif->arg, addr, 0);
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         }
718         return 0;
719 }