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