]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/ip/arp.c
devip: pick less surprising interface address in header for incoming UDP packets
[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 /* take out of re-transmit chain */
77 static Arpent**
78 rxmtunchain(Arp *arp, Arpent *a)
79 {
80         Arpent **l;
81
82         for(l = &arp->rxmt; *l != nil; l = &((*l)->nextrxt)){
83                 if(*l == a){
84                         *l = a->nextrxt;
85                         break;
86                 }
87         }
88         a->nextrxt = nil;
89         return l;
90 }
91
92 static void
93 cleanarpent(Arp *arp, Arpent *a)
94 {
95         Arpent **l;
96         Block *bp;
97
98         /* take out of current chain */
99         for(l = &arp->hash[haship(a->ip)]; *l != nil; l = &((*l)->hash)){
100                 if(*l == a){
101                         *l = a->hash;
102                         break;
103                 }
104         }
105         a->hash = nil;
106
107         /* dump waiting packets */
108         bp = a->hold;
109         a->hold = nil;
110         if(isv4(a->ip))
111                 freeblistchain(bp);
112         else {
113                 rxmtunchain(arp, a);
114
115                 /* queue icmp unreachable for rxmitproc later on, w/o arp lock */
116                 if(bp != nil){
117                         if(arp->dropf == nil)
118                                 arp->dropf = bp;
119                         else
120                                 arp->dropl->list = bp;
121                         arp->dropl = a->last;
122
123                         if(bp == arp->dropf)
124                                 wakeup(&arp->rxmtq);
125                 }
126         }
127         a->last = nil;
128
129         a->ifc = nil;
130         a->ifcid = 0;
131
132         a->state = 0;
133         a->rxtsrem = 0;
134
135         a->utime = 0;
136         a->ctime = 0;
137
138         memset(a->ip, 0, sizeof(a->ip));
139         memset(a->mac, 0, sizeof(a->mac));
140 }
141
142 /*
143  *  create a new arp entry for an ip address on ifc.
144  */
145 static Arpent*
146 newarpent(Arp *arp, uchar *ip, Ipifc *ifc)
147 {
148         Arpent *a, *e, *f, **l;
149         ulong t;
150
151         /* find oldest entry */
152         e = &arp->cache[NCACHE];
153         a = arp->cache;
154         t = a->utime;
155         for(f = a; f < e; f++){
156                 if(f->utime < t){
157                         t = f->utime;
158                         a = f;
159                 }
160         }
161         cleanarpent(arp, a);
162
163         ipmove(a->ip, ip);
164         a->ifc = ifc;
165         a->ifcid = ifc->ifcid;
166
167         /* insert into new chain */
168         l = &arp->hash[haship(ip)];
169         a->hash = *l;
170         *l = a;
171
172         return a;
173 }
174
175
176 /*
177  *  fill in the media address if we have it.  Otherwise return an
178  *  Arpent that represents the state of the address resolution FSM
179  *  for ip.  Add the packet to be sent onto the list of packets
180  *  waiting for ip->mac to be resolved.
181  */
182 Arpent*
183 arpget(Arp *arp, Block *bp, int version, Ipifc *ifc, uchar *ip, uchar *mac)
184 {
185         int hash;
186         Arpent *a;
187         uchar v6ip[IPaddrlen];
188
189         if(version == V4){
190                 v4tov6(v6ip, ip);
191                 ip = v6ip;
192         }
193
194         qlock(arp);
195         hash = haship(ip);
196         for(a = arp->hash[hash]; a != nil; a = a->hash){
197                 if(a->ifc == ifc && a->ifcid == ifc->ifcid && ipcmp(ip, a->ip) == 0)
198                         break;
199         }
200         if(a == nil){
201                 a = newarpent(arp, ip, ifc);
202                 a->state = AWAIT;
203         }
204         a->utime = NOW;
205         if(a->state == AWAIT){
206                 if(bp != nil){
207                         bp->list = nil; 
208                         if(a->hold == nil)
209                                 a->hold = bp;
210                         else
211                                 a->last->list = bp;
212                         a->last = bp;
213                 }
214                 return a;               /* return with arp qlocked */
215         }
216
217         memmove(mac, a->mac, ifc->m->maclen);
218
219         /* remove old entries */
220         if(NOW - a->ctime > 15*60*1000)
221                 cleanarpent(arp, a);
222
223         qunlock(arp);
224         return nil;
225 }
226
227 /*
228  * called with arp locked
229  */
230 void
231 arprelease(Arp *arp, Arpent*)
232 {
233         qunlock(arp);
234 }
235
236 /*
237  * Copy out the mac address from the Arpent.  Return the
238  * block waiting to get sent to this mac address.
239  *
240  * called with arp locked
241  */
242 Block*
243 arpresolve(Arp *arp, Arpent *a, Medium *type, uchar *mac)
244 {
245         Block *bp;
246
247         memmove(a->mac, mac, type->maclen);
248         if(a->state == AWAIT && !isv4(a->ip)){
249                 rxmtunchain(arp, a);
250                 a->rxtsrem = 0;
251         }
252         a->state = AOK;
253         a->ctime = a->utime = NOW;
254         bp = a->hold;
255         a->hold = a->last = nil;
256         qunlock(arp);
257
258         return bp;
259 }
260
261 int
262 arpenter(Fs *fs, int version, uchar *ip, uchar *mac, int n, uchar *ia, Ipifc *ifc, int refresh)
263 {
264         uchar v6ip[IPaddrlen];
265         Block *bp, *next;
266         Arpent *a;
267         Route *r;
268         Arp *arp;
269
270         if(ifc->m == nil || ifc->m->maclen != n || ifc->m->maclen == 0)
271                 return -1;
272
273         switch(version){
274         case V4:
275                 r = v4lookup(fs, ip, ia, nil);
276                 v4tov6(v6ip, ip);
277                 ip = v6ip;
278                 break;
279         case V6:
280                 r = v6lookup(fs, ip, ia, nil);
281                 break;
282         default:
283                 panic("arpenter: version %d", version);
284                 return -1;      /* to supress warnings */
285         }
286
287         if(r == nil || r->ifc != ifc || (r->type & (Rbcast|Rmulti)) != 0)
288                 return -1;
289
290         arp = fs->arp;
291         qlock(arp);
292         for(a = arp->hash[haship(ip)]; a != nil; a = a->hash){
293                 if(a->ifc != ifc || a->ifcid != ifc->ifcid)
294                         continue;
295                 if(ipcmp(a->ip, ip) == 0){
296                         if(version == V4)
297                                 ip += IPv4off;
298                         bp = arpresolve(arp, a, ifc->m, mac);   /* unlocks arp */
299                         for(; bp != nil; bp = next){
300                                 next = bp->list;
301                                 bp->list = nil;
302                                 if(waserror()){
303                                         freeblistchain(next);
304                                         break;
305                                 }
306                                 ipifcoput(ifc, bp, version, ip);
307                                 poperror();
308                         }
309                         return 1;
310                 }
311         }
312
313         if(refresh == 0){
314                 a = newarpent(arp, ip, ifc);
315                 a->state = AOK;
316                 a->ctime = a->utime = NOW;
317                 memmove(a->mac, mac, n);
318         }
319         qunlock(arp);
320
321         return refresh == 0;
322 }
323
324 int
325 arpwrite(Fs *fs, char *s, int len)
326 {
327         int n;
328         Arp *arp;
329         Arpent *a, *x;
330         Medium *m;
331         Ipifc *ifc;
332         char *f[5], buf[256];
333         uchar ip[IPaddrlen], ia[IPaddrlen], mac[MAClen];
334
335         arp = fs->arp;
336
337         if(len == 0)
338                 error(Ebadarp);
339         if(len >= sizeof(buf))
340                 len = sizeof(buf)-1;
341         strncpy(buf, s, len);
342         buf[len] = 0;
343         if(len > 0 && buf[len-1] == '\n')
344                 buf[len-1] = 0;
345
346         n = getfields(buf, f, nelem(f), 1, " ");
347         if(strcmp(f[0], "flush") == 0){
348                 qlock(arp);
349                 for(a = arp->cache; a < &arp->cache[NCACHE]; a++){
350                         memset(a->ip, 0, sizeof(a->ip));
351                         memset(a->mac, 0, sizeof(a->mac));
352                         a->hash = nil;
353                         a->nextrxt = nil;
354                         a->ifc = nil;
355                         a->ifcid = 0;
356                         a->state = 0;
357                         a->rxtsrem = 0;
358                         a->ctime = 0;
359                         a->utime = 0;
360                         freeblistchain(a->hold);
361                         a->hold = a->last = nil;
362                 }
363                 memset(arp->hash, 0, sizeof(arp->hash));
364                 freeblistchain(arp->dropf);
365                 arp->dropf = arp->dropl = nil;
366                 arp->rxmt = nil;
367                 qunlock(arp);
368         } else if(strcmp(f[0], "add") == 0){
369                 switch(n){
370                 default:
371                         error(Ebadarg);
372                 case 3:
373                         if(parseip(ip, f[1]) == -1)
374                                 error(Ebadip);
375                         if((n = parsemac(mac, f[2], sizeof(mac))) <= 0)
376                                 error(Ebadarp);
377                         findlocalip(fs, ia, ip);
378                         break;
379                 case 4:
380                         m = ipfindmedium(f[1]);
381                         if(m == nil || m->maclen == 0)
382                                 error(Ebadarp);
383                         if(parseip(ip, f[2]) == -1)
384                                 error(Ebadip);
385                         if((n = parsemac(mac, f[3], sizeof(mac))) != m->maclen)
386                                 error(Ebadarp);
387                         findlocalip(fs, ia, ip);
388                         break;
389                 case 5:
390                         m = ipfindmedium(f[1]);
391                         if(m == nil || m->maclen == 0)
392                                 error(Ebadarp);
393                         if(parseip(ip, f[2]) == -1)
394                                 error(Ebadip);
395                         if((n = parsemac(mac, f[3], sizeof(mac))) != m->maclen)
396                                 error(Ebadarp);
397                         if(parseip(ia, f[4]) == -1)
398                                 error(Ebadip);
399                         break;
400                 }
401                 if((ifc = findipifc(fs, ia, ia, Runi)) == nil)
402                         error("no interface");
403                 rlock(ifc);
404                 if(!ipv6local(ifc, ia, 0, ip) || arpenter(fs, V6, ip, mac, n, ia, ifc, 0) < 0){
405                         runlock(ifc);
406                         error("destination unreachable");
407                 }
408                 runlock(ifc);
409         } else if(strcmp(f[0], "del") == 0){
410                 if (n != 2)
411                         error(Ebadarg);
412                 if (parseip(ip, f[1]) == -1)
413                         error(Ebadip);
414                 qlock(arp);
415                 for(a = arp->hash[haship(ip)]; a != nil; a = x){
416                         x = a->hash;
417                         if(ipcmp(ip, a->ip) == 0)
418                                 cleanarpent(arp, a);
419                 }
420                 qunlock(arp);
421         } else
422                 error(Ebadarp);
423
424         return len;
425 }
426
427 static void
428 convmac(char *p, uchar *mac, int n)
429 {
430         while(n-- > 0)
431                 p += sprint(p, "%2.2ux", *mac++);
432 }
433
434 int
435 arpread(Arp *arp, char *s, ulong offset, int len)
436 {
437         char mac[2*MAClen+1], *state, *mname, *p;
438         uchar ip[IPaddrlen], ia[IPaddrlen];
439         Ipifc *ifc;
440         Arpent *a;
441         long n, o;
442
443         p = s;
444         o = -offset;
445         for(a = arp->cache; len > 0 && a < &arp->cache[NCACHE]; a++){
446                 if(a->state == 0 || (ifc = a->ifc) == nil)
447                         continue;
448
449                 rlock(ifc);
450                 qlock(arp);
451                 state = arpstate[a->state];
452                 ipmove(ip, a->ip);
453                 if(ifc->m == nil || a->ifcid != ifc->ifcid || !ipv6local(ifc, ia, 0, ip)){
454                         qunlock(arp);
455                         runlock(ifc);
456                         continue;
457                 }
458                 mname = ifc->m->name;
459                 convmac(mac, a->mac, ifc->m->maclen);
460                 qunlock(arp);
461                 runlock(ifc);
462
463                 n = snprint(up->genbuf, sizeof up->genbuf,
464                         "%-6.6s %-4.4s %-40.40I %-16.16s %I\n",
465                         mname, state, ip, mac, ia);
466                 o += n;
467                 if(o <= 0)
468                         continue;
469                 if(n > len)
470                         break;
471                 memmove(p, up->genbuf, n);
472                 len -= n;
473                 p += n;
474         }
475
476         return p - s;
477 }
478
479 void
480 ndpsendsol(Fs *f, Ipifc *ifc, Arpent *a)
481 {
482         uchar targ[IPaddrlen], src[IPaddrlen];
483         Arpent **l;
484
485         a->ctime = NOW;
486         if(a->rxtsrem == 0)
487                 a->rxtsrem = MAX_MULTICAST_SOLICIT;
488         else
489                 a->rxtsrem--;
490
491         /* put on end of re-transmit chain */
492         for(l = rxmtunchain(f->arp, a); *l != nil; l = &(*l)->nextrxt)
493                 ;
494         *l = a;
495
496         if(l == &f->arp->rxmt)
497                 wakeup(&f->arp->rxmtq);
498
499         /* try to use source address of original packet */
500         ipmove(targ, a->ip);
501         if(a->last != nil){
502                 ipmove(src, ((Ip6hdr*)a->last->rp)->src);
503                 arprelease(f->arp, a);
504
505                 if(iplocalonifc(ifc, src) != nil || ipproxyifc(f, ifc, src))
506                         goto send;
507         } else {
508                 arprelease(f->arp, a);
509         }
510         if(!ipv6local(ifc, src, 0, targ))
511                 return;
512 send:
513         if(!waserror()){
514                 icmpns(f, src, SRC_UNI, targ, TARG_MULTI, ifc->mac);
515                 poperror();
516         }
517 }
518
519 static void
520 rxmitsols(Arp *arp)
521 {
522         Block *next, *bp;
523         Arpent *a;
524         Ipifc *ifc;
525         Route *r;
526
527         qlock(arp);
528         while((a = arp->rxmt) != nil && NOW - a->ctime > 3*ReTransTimer/4){
529                 if(a->rxtsrem > 0 && (ifc = a->ifc) != nil && canrlock(ifc)){
530                         if(a->ifcid == ifc->ifcid){
531                                 ndpsendsol(arp->f, ifc, a);     /* unlocks arp */
532                                 runlock(ifc);
533                                 qlock(arp);
534                                 continue;
535                         }
536                         runlock(ifc);
537                 }
538                 cleanarpent(arp, a);
539         }
540         bp = arp->dropf;
541         arp->dropf = arp->dropl = nil;
542         qunlock(arp);
543
544         for(; bp != nil; bp = next){
545                 next = bp->list;
546                 bp->list = nil;
547                 r = v6lookup(arp->f, ((Ip6hdr*)bp->rp)->src, ((Ip6hdr*)bp->rp)->dst, nil);
548                 if(r != nil && (ifc = r->ifc) != nil && canrlock(ifc)){
549                         if(!waserror()){
550                                 icmphostunr6(arp->f, ifc, bp, Icmp6_adr_unreach, (r->type & Runi) != 0);
551                                 poperror();
552                         }
553                         runlock(ifc);
554                 }
555                 freeblist(bp);
556         }
557 }
558
559 static int
560 rxready(void *v)
561 {
562         Arp *arp = (Arp *)v;
563
564         return arp->rxmt != nil || arp->dropf != nil;
565 }
566
567 static void
568 rxmitproc(void *v)
569 {
570         Arp *arp = v;
571
572         arp->rxmitp = up;
573         if(waserror()){
574                 arp->rxmitp = nil;
575                 pexit("hangup", 1);
576         }
577         for(;;){
578                 sleep(&arp->rxmtq, rxready, v);
579                 rxmitsols(arp);
580                 tsleep(&arp->rxmtq, return0, nil, ReTransTimer/4);
581         }
582 }