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