Charles.Forsyth20060303644188019 years agoHistory
681 lines · 11.0 KB · C
RawDownload
#include "u.h"#include "../port/lib.h"#include "mem.h"#include "dat.h"#include "fns.h"#include "../port/error.h" #include "ip.h"#include "ipv6.h" /* *  address resolution tables */ enum{	NHASH		= (1<<6),	NCACHE		= 256, 	AOK		= 1,	AWAIT		= 2,}; char *arpstate[] ={	"UNUSED",	"OK",	"WAIT",}; /* *  one per Fs */struct Arp{	QLock;	Fs	*f;	Arpent	*hash[NHASH];	Arpent	cache[NCACHE];	Arpent	*rxmt;	Proc	*rxmitp;	/* neib sol re-transmit proc */	Rendez	rxmtq;	Block 	*dropf, *dropl;}; char *Ebadarp = "bad arp"; #define haship(s) ((s)[IPaddrlen-1]%NHASH) extern int 	ReTransTimer = RETRANS_TIMER;static void 	rxmitproc(void *v); voidarpinit(Fs *f){	f->arp = smalloc(sizeof(Arp));	f->arp->f = f;	f->arp->rxmt = nil;	f->arp->dropf = f->arp->dropl = nil;	kproc("rxmitproc", rxmitproc, f->arp, 0);} /* *  create a new arp entry for an ip address. */static Arpent*newarp6(Arp *arp, uchar *ip, Ipifc *ifc, int addrxt){	uint t;	Block *next, *xp;	Arpent *a, *e, *f, **l;	Medium *m = ifc->m;	int empty; 	/* find oldest entry */	e = &arp->cache[NCACHE];	a = arp->cache;	t = a->utime;	for(f = a; f < e; f++){		if(f->utime < t){			t = f->utime;			a = f;		}	} 	/* dump waiting packets */	xp = a->hold;	a->hold = nil; 	if(isv4(a->ip)){		while(xp){			next = xp->list;			freeblist(xp);			xp = next;		}	}	else {	// queue icmp unreachable for rxmitproc later on, w/o arp lock		if(xp){			if(arp->dropl == nil) 				arp->dropf = xp;			else				arp->dropl->list = xp; 			for(next = xp->list; next; next = next->list)				xp = next;			arp->dropl = xp;			wakeup(&arp->rxmtq);		}	} 	/* take out of current chain */	l = &arp->hash[haship(a->ip)];	for(f = *l; f; f = f->hash){		if(f == a){			*l = a->hash;			break;		}		l = &f->hash;	} 	/* insert into new chain */	l = &arp->hash[haship(ip)];	a->hash = *l;	*l = a; 	memmove(a->ip, ip, sizeof(a->ip));	a->utime = NOW;	a->ctime = 0;	a->type = m; 	a->rtime = NOW + ReTransTimer;	a->rxtsrem = MAX_MULTICAST_SOLICIT;	a->ifc = ifc;	a->ifcid = ifc->ifcid; 	/* put to the end of re-transmit chain; addrxt is 0 when isv4(a->ip) */	if(!ipismulticast(a->ip) && addrxt){		l = &arp->rxmt;		empty = (*l==nil); 		for(f = *l; f; f = f->nextrxt){			if(f == a){				*l = a->nextrxt;				break;			}			l = &f->nextrxt;		}		for(f = *l; f; f = f->nextrxt){			l = &f->nextrxt;		}		*l = a;		if(empty) 			wakeup(&arp->rxmtq);	} 	a->nextrxt = nil; 	return a;} /* called with arp qlocked */ voidcleanarpent(Arp *arp, Arpent *a){	Arpent *f, **l; 	a->utime = 0;	a->ctime = 0;	a->type = 0;	a->state = 0;		/* take out of current chain */	l = &arp->hash[haship(a->ip)];	for(f = *l; f; f = f->hash){		if(f == a){			*l = a->hash;			break;		}		l = &f->hash;	} 	/* take out of re-transmit chain */	l = &arp->rxmt;	for(f = *l; f; f = f->nextrxt){		if(f == a){			*l = a->nextrxt;			break;		}		l = &f->nextrxt;	}	a->nextrxt = nil;	a->hash = nil;	a->hold = nil;	a->last = nil;	a->ifc = nil;} /* *  fill in the media address if we have it.  Otherwise return an *  Arpent that represents the state of the address resolution FSM *  for ip.  Add the packet to be sent onto the list of packets *  waiting for ip->mac to be resolved. */Arpent*arpget(Arp *arp, Block *bp, int version, Ipifc *ifc, uchar *ip, uchar *mac){	int hash;	Arpent *a;	Medium *type = ifc->m;	uchar v6ip[IPaddrlen]; 	if(version == V4){		v4tov6(v6ip, ip);		ip = v6ip;	} 	qlock(arp);	hash = haship(ip);	for(a = arp->hash[hash]; a; a = a->hash){		if(memcmp(ip, a->ip, sizeof(a->ip)) == 0)		if(type == a->type)			break;	} 	if(a == nil){		a = newarp6(arp, ip, ifc, (version != V4));		a->state = AWAIT;	}	a->utime = NOW;	if(a->state == AWAIT){		if(bp != nil){			if(a->hold)				a->last->list = bp;			else				a->hold = bp;			a->last = bp;			bp->list = nil; 		}		return a;		/* return with arp qlocked */	} 	memmove(mac, a->mac, a->type->maclen); 	/* remove old entries */	if(NOW - a->ctime > 15*60*1000)		cleanarpent(arp, a); 	qunlock(arp);	return nil;} /* * called with arp locked */voidarprelease(Arp *arp, Arpent*){	qunlock(arp);} /* * Copy out the mac address from the Arpent.  Return the * block waiting to get sent to this mac address. * * called with arp locked */Block*arpresolve(Arp *arp, Arpent *a, Medium *type, uchar *mac){	Block *bp;	Arpent *f, **l; 	if(!isv4(a->ip)){		l = &arp->rxmt;		for(f = *l; f; f = f->nextrxt){			if(f == a){				*l = a->nextrxt;				break;			}			l = &f->nextrxt;		}	} 	memmove(a->mac, mac, type->maclen);	a->type = type;	a->state = AOK;	a->utime = NOW;	bp = a->hold;	a->hold = nil;	qunlock(arp); 	return bp;} voidarpenter(Fs *fs, int version, uchar *ip, uchar *mac, int n, int refresh){	Arp *arp;	Route *r;	Arpent *a, *f, **l;	Ipifc *ifc;	Medium *type;	Block *bp, *next;	uchar v6ip[IPaddrlen]; 	arp = fs->arp; 	if(n != 6){//		print("arp: len = %d\n", n);		return;	} 	switch(version){	case V4:		r = v4lookup(fs, ip, nil);		v4tov6(v6ip, ip);		ip = v6ip;		break;	case V6:		r = v6lookup(fs, ip, nil);		break;	default:		panic("arpenter: version %d", version);		return;	/* to supress warnings */	} 	if(r == nil){//		print("arp: no route for entry\n");		return;	} 	ifc = r->ifc;	type = ifc->m; 	qlock(arp);	for(a = arp->hash[haship(ip)]; a; a = a->hash){		if(a->type != type || (a->state != AWAIT && a->state != AOK))			continue; 		if(ipcmp(a->ip, ip) == 0){			a->state = AOK;			memmove(a->mac, mac, type->maclen); 			if(version == V6){				/* take out of re-transmit chain */				l = &arp->rxmt;				for(f = *l; f; f = f->nextrxt){					if(f == a){						*l = a->nextrxt;						break;					}					l = &f->nextrxt;				}			} 			a->ifc = ifc;			a->ifcid = ifc->ifcid;			bp = a->hold;			a->hold = nil;			if(version == V4)				ip += IPv4off;			a->utime = NOW;			a->ctime = a->utime;			qunlock(arp); 			while(bp){				next = bp->list;				if(ifc != nil){					if(waserror()){						runlock(ifc);						nexterror();					}					rlock(ifc);					if(ifc->m != nil)						ifc->m->bwrite(ifc, bp, version, ip);					else						freeb(bp);					runlock(ifc);					poperror();				} else					freeb(bp);				bp = next;			}			return;		}	} 	if(refresh == 0){		a = newarp6(arp, ip, ifc, 0);		a->state = AOK;		a->type = type;		a->ctime = NOW;		memmove(a->mac, mac, type->maclen);	} 	qunlock(arp);} intarpwrite(Fs *fs, char *s, int len){	int n;	Route *r;	Arp *arp;	Block *bp;	Arpent *a, *fl, **l;	Medium *m;	char *f[4], buf[256];	uchar ip[IPaddrlen], mac[MAClen]; 	arp = fs->arp; 	if(len == 0)		error(Ebadarp);	if(len >= sizeof(buf))		len = sizeof(buf)-1;	strncpy(buf, s, len);	buf[len] = 0;	if(len > 0 && buf[len-1] == '\n')		buf[len-1] = 0; 	n = getfields(buf, f, 4, 1, " ");	if(strcmp(f[0], "flush") == 0){		qlock(arp);		for(a = arp->cache; a < &arp->cache[NCACHE]; a++){			memset(a->ip, 0, sizeof(a->ip));			memset(a->mac, 0, sizeof(a->mac));			a->hash = nil;			a->state = 0;			a->utime = 0;			while(a->hold != nil){				bp = a->hold->list;				freeblist(a->hold);				a->hold = bp;			}		}		memset(arp->hash, 0, sizeof(arp->hash));// clear all pkts on these lists (rxmt, dropf/l)		arp->rxmt = nil;		arp->dropf = nil;		arp->dropl = nil;		qunlock(arp);	} else if(strcmp(f[0], "add") == 0){		switch(n){		default:			error(Ebadarg);		case 3:			parseip(ip, f[1]);			if(isv4(ip))				r = v4lookup(fs, ip+IPv4off, nil);			else				r = v6lookup(fs, ip, nil);			if(r == nil)				error("Destination unreachable");			m = r->ifc->m;			n = parsemac(mac, f[2], m->maclen);			break;		case 4:			m = ipfindmedium(f[1]);			if(m == nil)				error(Ebadarp);			parseip(ip, f[2]);			n = parsemac(mac, f[3], m->maclen);			break;		} 		if(m->ares == nil)			error(Ebadarp); 		m->ares(fs, V6, ip, mac, n, 0);	} else if(strcmp(f[0], "del") == 0){		if(n != 2)			error(Ebadarg); 		parseip(ip, f[1]);		qlock(arp); 		l = &arp->hash[haship(ip)];		for(a = *l; a; a = a->hash){			if(memcmp(ip, a->ip, sizeof(a->ip)) == 0){				*l = a->hash;				break;			}			l = &a->hash;		}			if(a){			/* take out of re-transmit chain */			l = &arp->rxmt;			for(fl = *l; fl; fl = fl->nextrxt){				if(fl == a){					*l = a->nextrxt;					break;				}				l = &fl->nextrxt;			} 			a->nextrxt = nil;			a->hash = nil;

Lines 1–500 of 681.