Added explicit errno reporting from error() API.
[akaros.git] / kern / src / net / arp.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 /*
17  *  address resolution tables
18  */
19
20 enum {
21         NHASH = (1 << 6),
22         NCACHE = 256,
23
24         AOK = 1,
25         AWAIT = 2,
26 };
27
28 char *arpstate[] = {
29         "UNUSED",
30         "OK",
31         "WAIT",
32 };
33
34 /*
35  *  one per Fs
36  */
37 struct arp {
38         qlock_t qlock;
39         struct Fs *f;
40         struct arpent *hash[NHASH];
41         struct arpent cache[NCACHE];
42         struct arpent *rxmt;
43         struct proc *rxmitp;            /* neib sol re-transmit proc */
44         struct rendez rxmtq;
45         struct block *dropf, *dropl;
46 };
47
48 #define haship(s) ((s)[IPaddrlen-1]%NHASH)
49
50 int ReTransTimer = RETRANS_TIMER;
51 static void rxmitproc(void *v);
52
53 void arpinit(struct Fs *f)
54 {
55         f->arp = kzmalloc(sizeof(struct arp), KMALLOC_WAIT);
56         qlock_init(&f->arp->qlock);
57         rendez_init(&f->arp->rxmtq);
58         f->arp->f = f;
59         f->arp->rxmt = NULL;
60         f->arp->dropf = f->arp->dropl = NULL;
61         ktask("rxmitproc", rxmitproc, f->arp);
62 }
63
64 /*
65  *  create a new arp entry for an ip address.
66  */
67 static struct arpent *newarp6(struct arp *arp, uint8_t * ip, struct Ipifc *ifc,
68                                                           int addrxt)
69 {
70         unsigned int t;
71         struct block *next, *xp;
72         struct arpent *a, *e, *f, **l;
73         struct medium *m = ifc->m;
74         int empty;
75
76         /* find oldest entry */
77         e = &arp->cache[NCACHE];
78         a = arp->cache;
79         t = a->utime;
80         for (f = a; f < e; f++) {
81                 if (f->utime < t) {
82                         t = f->utime;
83                         a = f;
84                 }
85         }
86
87         /* dump waiting packets */
88         xp = a->hold;
89         a->hold = NULL;
90
91         if (isv4(a->ip)) {
92                 while (xp) {
93                         next = xp->list;
94                         freeblist(xp);
95                         xp = next;
96                 }
97         } else {        // queue icmp unreachable for rxmitproc later on, w/o arp lock
98                 if (xp) {
99                         if (arp->dropl == NULL)
100                                 arp->dropf = xp;
101                         else
102                                 arp->dropl->list = xp;
103
104                         for (next = xp->list; next; next = next->list)
105                                 xp = next;
106                         arp->dropl = xp;
107                         rendez_wakeup(&arp->rxmtq);
108                 }
109         }
110
111         /* take out of current chain */
112         l = &arp->hash[haship(a->ip)];
113         for (f = *l; f; f = f->hash) {
114                 if (f == a) {
115                         *l = a->hash;
116                         break;
117                 }
118                 l = &f->hash;
119         }
120
121         /* insert into new chain */
122         l = &arp->hash[haship(ip)];
123         a->hash = *l;
124         *l = a;
125
126         memmove(a->ip, ip, sizeof(a->ip));
127         a->utime = NOW;
128         a->ctime = 0;   /* somewhat of a "last sent time".  0, to trigger a send. */
129         a->type = m;
130
131         a->rtime = NOW + ReTransTimer;
132         a->rxtsrem = MAX_MULTICAST_SOLICIT;
133         a->ifc = ifc;
134         a->ifcid = ifc->ifcid;
135
136         /* put to the end of re-transmit chain; addrxt is 0 when isv4(a->ip) */
137         if (!ipismulticast(a->ip) && addrxt) {
138                 l = &arp->rxmt;
139                 empty = (*l == NULL);
140
141                 for (f = *l; f; f = f->nextrxt) {
142                         if (f == a) {
143                                 *l = a->nextrxt;
144                                 break;
145                         }
146                         l = &f->nextrxt;
147                 }
148                 for (f = *l; f; f = f->nextrxt) {
149                         l = &f->nextrxt;
150                 }
151                 *l = a;
152                 if (empty)
153                         rendez_wakeup(&arp->rxmtq);
154         }
155
156         a->nextrxt = NULL;
157
158         return a;
159 }
160
161 /* called with arp qlocked */
162
163 void cleanarpent(struct arp *arp, struct arpent *a)
164 {
165         struct arpent *f, **l;
166
167         a->utime = 0;
168         a->ctime = 0;
169         a->type = 0;
170         a->state = 0;
171
172         /* take out of current chain */
173         l = &arp->hash[haship(a->ip)];
174         for (f = *l; f; f = f->hash) {
175                 if (f == a) {
176                         *l = a->hash;
177                         break;
178                 }
179                 l = &f->hash;
180         }
181
182         /* take out of re-transmit chain */
183         l = &arp->rxmt;
184         for (f = *l; f; f = f->nextrxt) {
185                 if (f == a) {
186                         *l = a->nextrxt;
187                         break;
188                 }
189                 l = &f->nextrxt;
190         }
191         a->nextrxt = NULL;
192         a->hash = NULL;
193         a->hold = NULL;
194         a->last = NULL;
195         a->ifc = NULL;
196 }
197
198 /*
199  *  fill in the media address if we have it.  Otherwise return an
200  *  arpent that represents the state of the address resolution FSM
201  *  for ip.  Add the packet to be sent onto the list of packets
202  *  waiting for ip->mac to be resolved.
203  */
204 struct arpent *arpget(struct arp *arp, struct block *bp, int version,
205                                           struct Ipifc *ifc, uint8_t * ip, uint8_t * mac)
206 {
207         int hash, len;
208         struct arpent *a;
209         struct medium *type = ifc->m;
210         uint8_t v6ip[IPaddrlen];
211         uint16_t *s, *d;
212
213         if (version == V4) {
214                 v4tov6(v6ip, ip);
215                 ip = v6ip;
216         }
217
218         qlock(&arp->qlock);
219         hash = haship(ip);
220         for (a = arp->hash[hash]; a; a = a->hash) {
221                 if (ipcmp(ip, a->ip) == 0)
222                         if (type == a->type)
223                                 break;
224         }
225
226         if (a == NULL) {
227                 a = newarp6(arp, ip, ifc, (version != V4));
228                 a->state = AWAIT;
229         }
230         a->utime = NOW;
231         if (a->state == AWAIT) {
232                 if (bp != NULL) {
233                         if (a->hold)
234                                 a->last->list = bp;
235                         else
236                                 a->hold = bp;
237                         a->last = bp;
238                         bp->list = NULL;
239                 }
240                 return a;       /* return with arp qlocked */
241         }
242
243         s = (uint16_t *)a->mac;
244         d = (uint16_t *)mac;
245         len = a->type->maclen / 2;
246         while (len) {
247                 *d++ = *s++;
248                 len--;
249         }
250
251         /* remove old entries */
252         if (NOW - a->ctime > 15 * 60 * 1000)
253                 cleanarpent(arp, a);
254
255         qunlock(&arp->qlock);
256         return NULL;
257 }
258
259 /*
260  * called with arp locked
261  */
262 void arprelease(struct arp *arp, struct arpent *a)
263 {
264         qunlock(&arp->qlock);
265 }
266
267 /*
268  * Copy out the mac address from the arpent.  Return the
269  * block waiting to get sent to this mac address.
270  *
271  * called with arp locked
272  */
273 struct block *arpresolve(struct arp *arp, struct arpent *a, struct medium *type,
274                                                  uint8_t * mac)
275 {
276         struct block *bp;
277         struct arpent *f, **l;
278
279         if (!isv4(a->ip)) {
280                 l = &arp->rxmt;
281                 for (f = *l; f; f = f->nextrxt) {
282                         if (f == a) {
283                                 *l = a->nextrxt;
284                                 break;
285                         }
286                         l = &f->nextrxt;
287                 }
288         }
289
290         memmove(a->mac, mac, type->maclen);
291         a->type = type;
292         a->state = AOK;
293         a->utime = NOW;
294         bp = a->hold;
295         a->hold = NULL;
296         /* brho: it looks like we return the entire hold list, though it might be
297          * purged by now via some other crazy arp list management.  our callers
298          * can't handle the arp's b->list stuff. */
299         assert(!bp->list);
300         qunlock(&arp->qlock);
301
302         return bp;
303 }
304
305 void
306 arpenter(struct Fs *fs, int version, uint8_t * ip, uint8_t * mac, int n,
307                  int refresh)
308 {
309         ERRSTACK(1);
310         struct arp *arp;
311         struct route *r;
312         struct arpent *a, *f, **l;
313         struct Ipifc *ifc;
314         struct medium *type;
315         struct block *bp, *next;
316         uint8_t v6ip[IPaddrlen];
317
318         arp = fs->arp;
319
320         if (n != 6) {
321 //      print("arp: len = %d\n", n);
322                 return;
323         }
324
325         switch (version) {
326                 case V4:
327                         r = v4lookup(fs, ip, NULL);
328                         v4tov6(v6ip, ip);
329                         ip = v6ip;
330                         break;
331                 case V6:
332                         r = v6lookup(fs, ip, NULL);
333                         break;
334                 default:
335                         panic("arpenter: version %d", version);
336                         return; /* to supress warnings */
337         }
338
339         if (r == NULL) {
340 //      print("arp: no route for entry\n");
341                 return;
342         }
343
344         ifc = r->rt.ifc;
345         type = ifc->m;
346
347         qlock(&arp->qlock);
348         for (a = arp->hash[haship(ip)]; a; a = a->hash) {
349                 if (a->type != type || (a->state != AWAIT && a->state != AOK))
350                         continue;
351
352                 if (ipcmp(a->ip, ip) == 0) {
353                         a->state = AOK;
354                         memmove(a->mac, mac, type->maclen);
355
356                         if (version == V6) {
357                                 /* take out of re-transmit chain */
358                                 l = &arp->rxmt;
359                                 for (f = *l; f; f = f->nextrxt) {
360                                         if (f == a) {
361                                                 *l = a->nextrxt;
362                                                 break;
363                                         }
364                                         l = &f->nextrxt;
365                                 }
366                         }
367
368                         a->ifc = ifc;
369                         a->ifcid = ifc->ifcid;
370                         bp = a->hold;
371                         a->hold = NULL;
372                         if (version == V4)
373                                 ip += IPv4off;
374                         a->utime = NOW;
375                         a->ctime = a->utime;
376                         qunlock(&arp->qlock);
377
378                         while (bp) {
379                                 next = bp->list;
380                                 if (ifc != NULL) {
381                                         if (waserror()) {
382                                                 runlock(&ifc->rwlock);
383                                                 nexterror();
384                                         }
385                                         rlock(&ifc->rwlock);
386                                         if (ifc->m != NULL)
387                                                 ifc->m->bwrite(ifc, bp, version, ip);
388                                         else
389                                                 freeb(bp);
390                                         runlock(&ifc->rwlock);
391                                         poperror();
392                                 } else
393                                         freeb(bp);
394                                 bp = next;
395                         }
396                         return;
397                 }
398         }
399
400         if (refresh == 0) {
401                 a = newarp6(arp, ip, ifc, 0);
402                 a->state = AOK;
403                 a->type = type;
404                 a->ctime = NOW;
405                 memmove(a->mac, mac, type->maclen);
406         }
407
408         qunlock(&arp->qlock);
409 }
410
411 int arpwrite(struct Fs *fs, char *s, int len)
412 {
413         int n;
414         struct route *r;
415         struct arp *arp;
416         struct block *bp;
417         struct arpent *a, *fl, **l;
418         struct medium *m;
419         char *f[4], buf[256];
420         uint8_t ip[IPaddrlen], mac[MAClen];
421
422         arp = fs->arp;
423
424         if (len == 0)
425                 error(EINVAL, NULL);
426         if (len >= sizeof(buf))
427                 len = sizeof(buf) - 1;
428         strncpy(buf, s, len);
429         buf[len] = 0;
430         if (len > 0 && buf[len - 1] == '\n')
431                 buf[len - 1] = 0;
432
433         n = getfields(buf, f, 4, 1, " ");
434         if (strcmp(f[0], "flush") == 0) {
435                 qlock(&arp->qlock);
436                 for (a = arp->cache; a < &arp->cache[NCACHE]; a++) {
437                         memset(a->ip, 0, sizeof(a->ip));
438                         memset(a->mac, 0, sizeof(a->mac));
439                         a->hash = NULL;
440                         a->state = 0;
441                         a->utime = 0;
442                         while (a->hold != NULL) {
443                                 bp = a->hold->list;
444                                 freeblist(a->hold);
445                                 a->hold = bp;
446                         }
447                 }
448                 memset(arp->hash, 0, sizeof(arp->hash));
449 // clear all pkts on these lists (rxmt, dropf/l)
450                 arp->rxmt = NULL;
451                 arp->dropf = NULL;
452                 arp->dropl = NULL;
453                 qunlock(&arp->qlock);
454         } else if (strcmp(f[0], "add") == 0) {
455                 switch (n) {
456                         default:
457                                 error(EINVAL, NULL);
458                         case 3:
459                                 parseip(ip, f[1]);
460                                 if (isv4(ip))
461                                         r = v4lookup(fs, ip + IPv4off, NULL);
462                                 else
463                                         r = v6lookup(fs, ip, NULL);
464                                 if (r == NULL)
465                                         error(EFAIL, "Destination unreachable");
466                                 m = r->rt.ifc->m;
467                                 n = parsemac(mac, f[2], m->maclen);
468                                 break;
469                         case 4:
470                                 m = ipfindmedium(f[1]);
471                                 if (m == NULL)
472                                         error(EINVAL, NULL);
473                                 parseip(ip, f[2]);
474                                 n = parsemac(mac, f[3], m->maclen);
475                                 break;
476                 }
477
478                 if (m->ares == NULL)
479                         error(EINVAL, NULL);
480
481                 m->ares(fs, V6, ip, mac, n, 0);
482         } else if (strcmp(f[0], "del") == 0) {
483                 if (n != 2)
484                         error(EINVAL, NULL);
485
486                 parseip(ip, f[1]);
487                 qlock(&arp->qlock);
488
489                 l = &arp->hash[haship(ip)];
490                 for (a = *l; a; a = a->hash) {
491                         if (memcmp(ip, a->ip, sizeof(a->ip)) == 0) {
492                                 *l = a->hash;
493                                 break;
494                         }
495                         l = &a->hash;
496                 }
497
498                 if (a) {
499                         /* take out of re-transmit chain */
500                         l = &arp->rxmt;
501                         for (fl = *l; fl; fl = fl->nextrxt) {
502                                 if (fl == a) {
503                                         *l = a->nextrxt;
504                                         break;
505                                 }
506                                 l = &fl->nextrxt;
507                         }
508
509                         a->nextrxt = NULL;
510                         a->hash = NULL;
511                         a->hold = NULL;
512                         a->last = NULL;
513                         a->ifc = NULL;
514                         memset(a->ip, 0, sizeof(a->ip));
515                         memset(a->mac, 0, sizeof(a->mac));
516                 }
517                 qunlock(&arp->qlock);
518         } else
519                 error(EINVAL, NULL);
520
521         return len;
522 }
523
524 enum {
525         Alinelen = 90,
526 };
527
528 static char *aformat = "%-6.6s %-8.8s %-40.40I %E\n";
529
530 int arpread(struct arp *arp, char *p, uint32_t offset, int len)
531 {
532         struct arpent *a;
533         int n;
534         int left = len;
535         int amt;
536
537         if (offset % Alinelen)
538                 return 0;
539
540         offset = offset / Alinelen;
541         len = len / Alinelen;
542
543         n = 0;
544         for (a = arp->cache; len > 0 && a < &arp->cache[NCACHE]; a++) {
545                 if (a->state == 0)
546                         continue;
547                 if (offset > 0) {
548                         offset--;
549                         continue;
550                 }
551                 len--;
552                 left--;
553                 qlock(&arp->qlock);
554                 amt = snprintf(p + n, left, aformat, a->type->name, arpstate[a->state],
555                                a->ip, a->mac);
556                 n += amt;
557                 left -= amt;
558                 qunlock(&arp->qlock);
559         }
560
561         return n;
562 }
563
564 static uint64_t rxmitsols(struct arp *arp)
565 {
566         unsigned int sflag;
567         struct block *next, *xp;
568         struct arpent *a, *b, **l;
569         struct Fs *f;
570         uint8_t ipsrc[IPaddrlen];
571         struct Ipifc *ifc = NULL;
572         uint64_t nrxt;
573
574         qlock(&arp->qlock);
575         f = arp->f;
576
577         a = arp->rxmt;
578         if (a == NULL) {
579                 nrxt = 0;
580                 goto dodrops;   //return nrxt;
581         }
582         nrxt = a->rtime - NOW;
583         if (nrxt > 3 * ReTransTimer / 4)
584                 goto dodrops;   //return nrxt;
585
586         for (; a; a = a->nextrxt) {
587                 ifc = a->ifc;
588                 assert(ifc != NULL);
589                 if ((a->rxtsrem <= 0) || !(canrlock(&ifc->rwlock))
590                         || (a->ifcid != ifc->ifcid)) {
591                         xp = a->hold;
592                         a->hold = NULL;
593
594                         if (xp) {
595                                 if (arp->dropl == NULL)
596                                         arp->dropf = xp;
597                                 else
598                                         arp->dropl->list = xp;
599                         }
600
601                         cleanarpent(arp, a);
602                 } else
603                         break;
604         }
605         if (a == NULL)
606                 goto dodrops;
607
608         qunlock(&arp->qlock);   /* for icmpns */
609         if ((sflag = ipv6anylocal(ifc, ipsrc)) != SRC_UNSPEC)
610                 icmpns(f, ipsrc, sflag, a->ip, TARG_MULTI, ifc->mac);
611
612         runlock(&ifc->rwlock);
613         qlock(&arp->qlock);
614
615         /* put to the end of re-transmit chain */
616         l = &arp->rxmt;
617         for (b = *l; b; b = b->nextrxt) {
618                 if (b == a) {
619                         *l = a->nextrxt;
620                         break;
621                 }
622                 l = &b->nextrxt;
623         }
624         for (b = *l; b; b = b->nextrxt) {
625                 l = &b->nextrxt;
626         }
627         *l = a;
628         a->rxtsrem--;
629         a->nextrxt = NULL;
630         a->rtime = NOW + ReTransTimer;
631
632         a = arp->rxmt;
633         if (a == NULL)
634                 nrxt = 0;
635         else
636                 nrxt = a->rtime - NOW;
637
638 dodrops:
639         xp = arp->dropf;
640         arp->dropf = NULL;
641         arp->dropl = NULL;
642         qunlock(&arp->qlock);
643
644         for (; xp; xp = next) {
645                 next = xp->list;
646                 icmphostunr(f, ifc, xp, icmp6_adr_unreach, 1);
647         }
648
649         return nrxt;
650
651 }
652
653 static int rxready(void *v)
654 {
655         struct arp *arp = (struct arp *)v;
656         int x;
657
658         x = ((arp->rxmt != NULL) || (arp->dropf != NULL));
659
660         return x;
661 }
662
663 static void rxmitproc(void *v)
664 {
665         ERRSTACK(2);
666         struct arp *arp = v;
667         uint64_t wakeupat;
668
669         arp->rxmitp = current;
670         //print("arp rxmitproc started\n");
671         if (waserror()) {
672                 arp->rxmitp = 0;
673                 poperror();
674                 warn("arp rxmit ktask exited");
675                 return;
676         }
677         for (;;) {
678                 wakeupat = rxmitsols(arp);
679                 if (wakeupat == 0)
680                         rendez_sleep(&arp->rxmtq, rxready, v);
681                 else if (wakeupat > ReTransTimer / 4)
682                         kthread_usleep(wakeupat * 1000);
683         }
684         poperror();
685 }