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