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