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