2 #include "../port/lib.h"
6 #include "../port/error.h"
12 * address resolution tables
41 Proc *rxmitp; /* neib sol re-transmit proc */
46 char *Ebadarp = "bad arp";
48 #define haship(s) ((s)[IPaddrlen-1]%NHASH)
50 int ReTransTimer = RETRANS_TIMER;
52 static void rxmitproc(void *v);
57 f->arp = smalloc(sizeof(Arp));
60 f->arp->dropf = f->arp->dropl = nil;
61 kproc("rxmitproc", rxmitproc, f->arp);
65 freeblistchain(Block *bp)
76 /* take out of re-transmit chain */
78 rxmtunchain(Arp *arp, Arpent *a)
82 for(l = &arp->rxmt; *l != nil; l = &((*l)->nextrxt)){
93 cleanarpent(Arp *arp, Arpent *a)
98 /* take out of current chain */
99 for(l = &arp->hash[haship(a->ip)]; *l != nil; l = &((*l)->hash)){
107 /* dump waiting packets */
115 /* queue icmp unreachable for rxmitproc later on, w/o arp lock */
117 if(arp->dropf == nil)
120 arp->dropl->list = bp;
121 arp->dropl = a->last;
138 memset(a->ip, 0, sizeof(a->ip));
139 memset(a->mac, 0, sizeof(a->mac));
143 * create a new arp entry for an ip address on ifc.
146 newarpent(Arp *arp, uchar *ip, Ipifc *ifc)
148 Arpent *a, *e, *f, **l;
151 /* find oldest entry */
152 e = &arp->cache[NCACHE];
155 for(f = a; f < e; f++){
165 a->ifcid = ifc->ifcid;
167 /* insert into new chain */
168 l = &arp->hash[haship(ip)];
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.
183 arpget(Arp *arp, Block *bp, int version, Ipifc *ifc, uchar *ip, uchar *mac)
187 uchar v6ip[IPaddrlen];
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)
201 a = newarpent(arp, ip, ifc);
205 if(a->state == AWAIT){
214 return a; /* return with arp qlocked */
217 memmove(mac, a->mac, ifc->m->maclen);
219 /* remove old entries */
220 if(NOW - a->ctime > 15*60*1000)
228 * called with arp locked
231 arprelease(Arp *arp, Arpent*)
237 * Copy out the mac address from the Arpent. Return the
238 * block waiting to get sent to this mac address.
240 * called with arp locked
243 arpresolve(Arp *arp, Arpent *a, Medium *type, uchar *mac)
247 memmove(a->mac, mac, type->maclen);
248 if(a->state == AWAIT && !isv4(a->ip)){
253 a->ctime = a->utime = NOW;
255 a->hold = a->last = nil;
262 arpenter(Fs *fs, int version, uchar *ip, uchar *mac, int n, uchar *ia, Ipifc *ifc, int refresh)
264 uchar v6ip[IPaddrlen];
270 if(ifc->m == nil || ifc->m->maclen != n || ifc->m->maclen == 0)
275 r = v4lookup(fs, ip, ia, nil);
280 r = v6lookup(fs, ip, ia, nil);
283 panic("arpenter: version %d", version);
284 return -1; /* to supress warnings */
287 if(r == nil || r->ifc != ifc || (r->type & (Rbcast|Rmulti)) != 0)
292 for(a = arp->hash[haship(ip)]; a != nil; a = a->hash){
293 if(a->ifc != ifc || a->ifcid != ifc->ifcid)
295 if(ipcmp(a->ip, ip) == 0){
298 bp = arpresolve(arp, a, ifc->m, mac); /* unlocks arp */
299 for(; bp != nil; bp = next){
303 freeblistchain(next);
306 ipifcoput(ifc, bp, version, ip);
314 a = newarpent(arp, ip, ifc);
316 a->ctime = a->utime = NOW;
317 memmove(a->mac, mac, n);
325 arpwrite(Fs *fs, char *s, int len)
332 char *f[5], buf[256];
333 uchar ip[IPaddrlen], ia[IPaddrlen], mac[MAClen];
339 if(len >= sizeof(buf))
341 strncpy(buf, s, len);
343 if(len > 0 && buf[len-1] == '\n')
346 n = getfields(buf, f, nelem(f), 1, " ");
347 if(strcmp(f[0], "flush") == 0){
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));
360 freeblistchain(a->hold);
361 a->hold = a->last = nil;
363 memset(arp->hash, 0, sizeof(arp->hash));
364 freeblistchain(arp->dropf);
365 arp->dropf = arp->dropl = nil;
368 } else if(strcmp(f[0], "add") == 0){
373 if(parseip(ip, f[1]) == -1)
375 if((n = parsemac(mac, f[2], sizeof(mac))) <= 0)
377 findlocalip(fs, ia, ip);
380 m = ipfindmedium(f[1]);
381 if(m == nil || m->maclen == 0)
383 if(parseip(ip, f[2]) == -1)
385 if((n = parsemac(mac, f[3], sizeof(mac))) != m->maclen)
387 findlocalip(fs, ia, ip);
390 m = ipfindmedium(f[1]);
391 if(m == nil || m->maclen == 0)
393 if(parseip(ip, f[2]) == -1)
395 if((n = parsemac(mac, f[3], sizeof(mac))) != m->maclen)
397 if(parseip(ia, f[4]) == -1)
401 if((ifc = findipifc(fs, ia, ia, Runi)) == nil)
402 error("no interface");
404 if(!ipv6local(ifc, ia, 0, ip) || arpenter(fs, V6, ip, mac, n, ia, ifc, 0) < 0){
406 error("destination unreachable");
409 } else if(strcmp(f[0], "del") == 0){
412 if (parseip(ip, f[1]) == -1)
415 for(a = arp->hash[haship(ip)]; a != nil; a = x){
417 if(ipcmp(ip, a->ip) == 0)
428 convmac(char *p, uchar *mac, int n)
431 p += sprint(p, "%2.2ux", *mac++);
435 arpread(Arp *arp, char *s, ulong offset, int len)
437 char mac[2*MAClen+1], *state, *mname, *p;
438 uchar ip[IPaddrlen], ia[IPaddrlen];
445 for(a = arp->cache; len > 0 && a < &arp->cache[NCACHE]; a++){
446 if(a->state == 0 || (ifc = a->ifc) == nil)
451 state = arpstate[a->state];
453 if(ifc->m == nil || a->ifcid != ifc->ifcid || !ipv6local(ifc, ia, 0, ip)){
458 mname = ifc->m->name;
459 convmac(mac, a->mac, ifc->m->maclen);
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);
471 memmove(p, up->genbuf, n);
480 ndpsendsol(Fs *f, Ipifc *ifc, Arpent *a)
482 uchar targ[IPaddrlen], src[IPaddrlen];
487 a->rxtsrem = MAX_MULTICAST_SOLICIT;
491 /* put on end of re-transmit chain */
492 for(l = rxmtunchain(f->arp, a); *l != nil; l = &(*l)->nextrxt)
496 if(l == &f->arp->rxmt)
497 wakeup(&f->arp->rxmtq);
499 /* try to use source address of original packet */
502 ipmove(src, ((Ip6hdr*)a->last->rp)->src);
503 arprelease(f->arp, a);
505 if(iplocalonifc(ifc, src) != nil || ipproxyifc(f, ifc, src))
508 arprelease(f->arp, a);
510 if(!ipv6local(ifc, src, 0, targ))
514 icmpns(f, src, SRC_UNI, targ, TARG_MULTI, ifc->mac);
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 */
541 arp->dropf = arp->dropl = nil;
544 for(; bp != nil; bp = next){
547 r = v6lookup(arp->f, ((Ip6hdr*)bp->rp)->src, ((Ip6hdr*)bp->rp)->dst, nil);
548 if(r != nil && (ifc = r->ifc) != nil && canrlock(ifc)){
550 icmphostunr6(arp->f, ifc, bp, Icmp6_adr_unreach, (r->type & Runi) != 0);
564 return arp->rxmt != nil || arp->dropf != nil;
578 sleep(&arp->rxmtq, rxready, v);
580 tsleep(&arp->rxmtq, return0, nil, ReTransTimer/4);