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