xref: /plan9/sys/src/9/ip/arp.c (revision 4e3613ab15c331a9ada113286cc0f2a35bc0373d)
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
arpinit(Fs * f)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*
newarp6(Arp * arp,uchar * ip,Ipifc * ifc,int addrxt)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
cleanarpent(Arp * arp,Arpent * a)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*
arpget(Arp * arp,Block * bp,int version,Ipifc * ifc,uchar * ip,uchar * mac)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
arprelease(Arp * arp,Arpent *)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*
arpresolve(Arp * arp,Arpent * a,Medium * type,uchar * mac)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
arpenter(Fs * fs,int version,uchar * ip,uchar * mac,int n,int refresh)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
arpwrite(Fs * fs,char * s,int len)402 arpwrite(Fs *fs, char *s, int len)
403 {
404 	int n;
405 	Route *r;
406 	Arp *arp;
407 	Block *bp;
408 	Arpent *a, *fl, **l;
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->dropf = nil;
443 		arp->dropl = nil;
444 		qunlock(arp);
445 	} else if(strcmp(f[0], "add") == 0){
446 		switch(n){
447 		default:
448 			error(Ebadarg);
449 		case 3:
450 			if (parseip(ip, f[1]) == -1)
451 				error(Ebadip);
452 			if(isv4(ip))
453 				r = v4lookup(fs, ip+IPv4off, nil);
454 			else
455 				r = v6lookup(fs, ip, nil);
456 			if(r == nil)
457 				error("Destination unreachable");
458 			m = r->ifc->m;
459 			n = parsemac(mac, f[2], m->maclen);
460 			break;
461 		case 4:
462 			m = ipfindmedium(f[1]);
463 			if(m == nil)
464 				error(Ebadarp);
465 			if (parseip(ip, f[2]) == -1)
466 				error(Ebadip);
467 			n = parsemac(mac, f[3], m->maclen);
468 			break;
469 		}
470 
471 		if(m->ares == nil)
472 			error(Ebadarp);
473 
474 		m->ares(fs, V6, ip, mac, n, 0);
475 	} else if(strcmp(f[0], "del") == 0){
476 		if(n != 2)
477 			error(Ebadarg);
478 
479 		if (parseip(ip, f[1]) == -1)
480 			error(Ebadip);
481 		qlock(arp);
482 
483 		l = &arp->hash[haship(ip)];
484 		for(a = *l; a; a = a->hash){
485 			if(memcmp(ip, a->ip, sizeof(a->ip)) == 0){
486 				*l = a->hash;
487 				break;
488 			}
489 			l = &a->hash;
490 		}
491 
492 		if(a){
493 			/* take out of re-transmit chain */
494 			l = &arp->rxmt;
495 			for(fl = *l; fl; fl = fl->nextrxt){
496 				if(fl == a){
497 					*l = a->nextrxt;
498 					break;
499 				}
500 				l = &fl->nextrxt;
501 			}
502 
503 			a->nextrxt = nil;
504 			a->hash = nil;
505 			a->hold = nil;
506 			a->last = nil;
507 			a->ifc = nil;
508 			memset(a->ip, 0, sizeof(a->ip));
509 			memset(a->mac, 0, sizeof(a->mac));
510 		}
511 		qunlock(arp);
512 	} else
513 		error(Ebadarp);
514 
515 	return len;
516 }
517 
518 enum
519 {
520 	Alinelen=	90,
521 };
522 
523 char *aformat = "%-6.6s %-8.8s %-40.40I %-32.32s\n";
524 
525 static void
convmac(char * p,char * ep,uchar * mac,int n)526 convmac(char *p, char *ep, uchar *mac, int n)
527 {
528 	while(n-- > 0)
529 		p = seprint(p, ep, "%2.2ux", *mac++);
530 }
531 
532 int
arpread(Arp * arp,char * p,ulong offset,int len)533 arpread(Arp *arp, char *p, ulong offset, int len)
534 {
535 	Arpent *a;
536 	int n;
537 	char mac[2*MAClen+1];
538 
539 	if(offset % Alinelen)
540 		return 0;
541 
542 	offset = offset/Alinelen;
543 	len = len/Alinelen;
544 
545 	n = 0;
546 	for(a = arp->cache; len > 0 && a < &arp->cache[NCACHE]; a++){
547 		if(a->state == 0)
548 			continue;
549 		if(offset > 0){
550 			offset--;
551 			continue;
552 		}
553 		len--;
554 		qlock(arp);
555 		convmac(mac, &mac[sizeof mac], a->mac, a->type->maclen);
556 		n += snprint(p+n, Alinelen+1, aformat, a->type->name,
557 			arpstate[a->state], a->ip, mac);	/* +1 for NUL */
558 		qunlock(arp);
559 	}
560 
561 	return n;
562 }
563 
564 extern int
rxmitsols(Arp * arp)565 rxmitsols(Arp *arp)
566 {
567 	uint sflag;
568 	Block *next, *xp;
569 	Arpent *a, *b, **l;
570 	Fs *f;
571 	uchar ipsrc[IPaddrlen];
572 	Ipifc *ifc = nil;
573 	long nrxt;
574 
575 	qlock(arp);
576 	f = arp->f;
577 
578 	a = arp->rxmt;
579 	if(a==nil){
580 		nrxt = 0;
581 		goto dodrops; 		/* return nrxt; */
582 	}
583 	nrxt = a->rtime - NOW;
584 	if(nrxt > 3*ReTransTimer/4)
585 		goto dodrops; 		/* return nrxt; */
586 
587 	for(; a; a = a->nextrxt){
588 		ifc = a->ifc;
589 		assert(ifc != nil);
590 		if((a->rxtsrem <= 0) || !(canrlock(ifc)) || (a->ifcid != ifc->ifcid)){
591 			xp = a->hold;
592 			a->hold = nil;
593 
594 			if(xp){
595 				if(arp->dropl == nil)
596 					arp->dropf = xp;
597 				else
598 					arp->dropl->list = xp;
599 			}
600 
601 			cleanarpent(arp, a);
602 		}
603 		else
604 			break;
605 	}
606 	if(a == nil)
607 		goto dodrops;
608 
609 
610 	qunlock(arp);	/* for icmpns */
611 	if((sflag = ipv6anylocal(ifc, ipsrc)) != SRC_UNSPEC)
612 		icmpns(f, ipsrc, sflag, a->ip, TARG_MULTI, ifc->mac);
613 
614 	runlock(ifc);
615 	qlock(arp);
616 
617 	/* put to the end of re-transmit chain */
618 	l = &arp->rxmt;
619 	for(b = *l; b; b = b->nextrxt){
620 		if(b == a){
621 			*l = a->nextrxt;
622 			break;
623 		}
624 		l = &b->nextrxt;
625 	}
626 	for(b = *l; b; b = b->nextrxt){
627 		l = &b->nextrxt;
628 	}
629 	*l = a;
630 	a->rxtsrem--;
631 	a->nextrxt = nil;
632 	a->rtime = NOW + ReTransTimer;
633 
634 	a = arp->rxmt;
635 	if(a==nil)
636 		nrxt = 0;
637 	else
638 		nrxt = a->rtime - NOW;
639 
640 dodrops:
641 	xp = arp->dropf;
642 	arp->dropf = nil;
643 	arp->dropl = nil;
644 	qunlock(arp);
645 
646 	for(; xp; xp = next){
647 		next = xp->list;
648 		icmphostunr(f, ifc, xp, Icmp6_adr_unreach, 1);
649 	}
650 
651 	return nrxt;
652 
653 }
654 
655 static int
rxready(void * v)656 rxready(void *v)
657 {
658 	Arp *arp = (Arp *) v;
659 	int x;
660 
661 	x = ((arp->rxmt != nil) || (arp->dropf != nil));
662 
663 	return x;
664 }
665 
666 static void
rxmitproc(void * v)667 rxmitproc(void *v)
668 {
669 	Arp *arp = v;
670 	long wakeupat;
671 
672 	arp->rxmitp = up;
673 	//print("arp rxmitproc started\n");
674 	if(waserror()){
675 		arp->rxmitp = 0;
676 		pexit("hangup", 1);
677 	}
678 	for(;;){
679 		wakeupat = rxmitsols(arp);
680 		if(wakeupat == 0)
681 			sleep(&arp->rxmtq, rxready, v);
682 		else if(wakeupat > ReTransTimer/4)
683 			tsleep(&arp->rxmtq, return0, 0, wakeupat);
684 	}
685 }
686 
687