734 lines · 15.1 KB · C
RawDownload
#include "u.h"#include "../port/lib.h"#include "mem.h"#include "dat.h"#include "fns.h"#include "../port/error.h" #include "../port/netif.h"#include "ip.h"#include "ipv6.h" typedef struct Etherhdr Etherhdr;struct Etherhdr{	uchar	d[6];	uchar	s[6];	uchar	t[2];}; static void	etherread4(void *a);static void	etherread6(void *a);static void	etherbind(Ipifc *ifc, int argc, char **argv);static void	etherunbind(Ipifc *ifc);static void	etherbwrite(Ipifc *ifc, Block *bp, int version, uchar *ip, Routehint *rh);static void	etheraddmulti(Ipifc *ifc, uchar *a, uchar *ia);static void	etherdelmulti(Ipifc *ifc, uchar *a, uchar *ia);static void	etherareg(Fs *f, Ipifc *ifc, Iplifc *lifc, uchar *ip);static Block*	multicastarp(Fs *f, Arpent *a, uchar *mac, Routehint *rh);static Block*	newEARP(void);static void	sendarpreq(Fs *f, Arpent *a);static int	multicastea(uchar *ea, uchar *ip);static void	recvarpproc(void*);static void	etherpref2addr(uchar *pref, uchar *ea); Medium ethermedium ={.name=		"ether",.hsize=		ETHERHDRSIZE,.mintu=		ETHERMINTU,.maxtu=		ETHERHDRSIZE+1500,.maclen=	6,.bind=		etherbind,.unbind=	etherunbind,.bwrite=	etherbwrite,.addmulti=	etheraddmulti,.delmulti=	etherdelmulti,.areg=		etherareg,.pref2addr=	etherpref2addr,}; Medium gbemedium ={.name=		"gbe",.hsize=		ETHERHDRSIZE,.mintu=		ETHERMINTU,.maxtu=		ETHERHDRSIZE+9000,.maclen=	6,.bind=		etherbind,.unbind=	etherunbind,.bwrite=	etherbwrite,.addmulti=	etheraddmulti,.delmulti=	etherdelmulti,.areg=		etherareg,.pref2addr=	etherpref2addr,}; typedef struct	Etherrock Etherrock;struct Etherrock{	Fs	*f;		/* file system we belong to */	Proc	*arpp;		/* arp process */	Proc	*read4p;	/* reading process (v4)*/	Proc	*read6p;	/* reading process (v6)*/	Chan	*mchan4;	/* Data channel for v4 */	Chan	*achan;		/* Arp channel */	Chan	*cchan4;	/* Control channel for v4 */	Chan	*mchan6;	/* Data channel for v6 */	Chan	*cchan6;	/* Control channel for v6 */}; /* *  ethernet arp request */enum{	ARPREQUEST	= 1,	ARPREPLY	= 2,}; typedef struct Etherarp Etherarp;struct Etherarp{	uchar	d[6];	uchar	s[6];	uchar	type[2];	uchar	hrd[2];	uchar	pro[2];	uchar	hln;	uchar	pln;	uchar	op[2];	uchar	sha[6];	uchar	spa[4];	uchar	tha[6];	uchar	tpa[4];}; static char *nbmsg = "nonblocking"; /* *  called to bind an IP ifc to an ethernet device */static voidetherbind(Ipifc *ifc, int argc, char **argv){	char addr[Maxpath], dir[Maxpath];	Etherrock *er;	Chan *c;	int n; 	if(argc < 2)		error(Ebadarg); 	/*	 *  get mac address	 */	snprint(addr, sizeof(addr), "%s/addr", argv[2]);	c = namec(addr, Aopen, OREAD, 0);	if(waserror()){		cclose(c);		nexterror();	}	n = devtab[c->type]->read(c, addr, sizeof(addr)-1, 0);	if(n < 0)		error(Eio);	addr[n] = 0;	if(parsemac(ifc->mac, addr, sizeof(ifc->mac)) != 6)		error("could not find mac address");	cclose(c);	poperror(); 	er = smalloc(sizeof(*er));	er->read4p = er->read6p = er->arpp = (void*)-1;	er->mchan4 = er->cchan4 = er->mchan6 = er->cchan6 = er->achan = nil;	er->f = ifc->conv->p->f; 	if(waserror()){		if(er->mchan4 != nil)			cclose(er->mchan4);		if(er->cchan4 != nil)			cclose(er->cchan4);		if(er->mchan6 != nil)			cclose(er->mchan6);		if(er->cchan6 != nil)			cclose(er->cchan6);		if(er->achan != nil)			cclose(er->achan);		free(er);		nexterror();	} 	/*	 *  open ipv4 conversation	 *	 *  the dial will fail if the type is already open on	 *  this device.	 */	snprint(addr, sizeof(addr), "%s!0x800", argv[2]);	/* ETIP4 */	er->mchan4 = chandial(addr, nil, dir, &er->cchan4); 	/*	 *  make it non-blocking	 */	devtab[er->cchan4->type]->write(er->cchan4, nbmsg, strlen(nbmsg), 0); 	/*	 *  open ipv6 conversation	 *	 *  the dial will fail if the type is already open on	 *  this device.	 */	snprint(addr, sizeof(addr), "%s!0x86DD", argv[2]);	/* ETIP6 */	er->mchan6 = chandial(addr, nil, dir, &er->cchan6); 	/*	 *  make it non-blocking	 */	devtab[er->cchan6->type]->write(er->cchan6, nbmsg, strlen(nbmsg), 0); 	/* 	 *  open arp conversation	 */	snprint(addr, sizeof(addr), "%s!0x806", argv[2]);	/* ETARP */	er->achan = chandial(addr, nil, nil, nil);	poperror(); 	ifc->arg = er; 	kproc("etherread4", etherread4, ifc);	kproc("etherread6", etherread6, ifc);	kproc("recvarpproc", recvarpproc, ifc);} /* *  called from devip due to *  manual unbind or from one of the reader procs *  due to read error from the ethernet device. *  this is called only once! */static voidetherunbind(Ipifc *ifc){	Etherrock *er = ifc->arg; 	while(waserror())		;	/* wait for readers to start */	while(er->arpp == (void*)-1 || er->read4p == (void*)-1 || er->read6p == (void*)-1)		tsleep(&up->sleep, return0, 0, 300); 	if(er->read4p != nil && er->read4p != up)		postnote(er->read4p, 1, "unbind", 0);	if(er->read6p != nil && er->read6p != up)		postnote(er->read6p, 1, "unbind", 0);	if(er->arpp != nil && er->arpp != up)		postnote(er->arpp, 1, "unbind", 0);	poperror(); 	while(waserror())		;	/* wait for readers to die */	while(er->arpp != nil && er->arpp != up	|| er->read4p != nil && er->read4p != up	|| er->read6p != nil && er->read6p != up)		tsleep(&up->sleep, return0, 0, 300);	poperror(); 	if(er->mchan4 != nil)		cclose(er->mchan4);	if(er->cchan4 != nil)		cclose(er->cchan4);	if(er->mchan6 != nil)		cclose(er->mchan6);	if(er->cchan6 != nil)		cclose(er->cchan6);	if(er->achan != nil)		cclose(er->achan); 	free(er);} /* *  called by ipoput with a single block to write with ifc rlock'd */static voidetherbwrite(Ipifc *ifc, Block *bp, int version, uchar *ip, Routehint *rh){	Etherhdr *eh;	Arpent *a;	uchar mac[6];	Etherrock *er = ifc->arg; 	/* get mac address of destination */	a = arpget(er->f->arp, bp, version, ifc, ip, mac, rh);	if(a != nil){		/* check for broadcast or multicast */		bp = multicastarp(er->f, a, mac, rh);		if(bp == nil){			/* don't do anything if it's been less than a second since the last */			if(a->utime - a->ctime < RETRANS_TIMER){				arprelease(er->f->arp, a);	/* unlocks arp */				return;			}			switch(version){			case V4:				sendarpreq(er->f, a);	/* unlocks arp */				break;			case V6:				ndpsendsol(er->f, a);	/* unlocks arp */				break;			default:				panic("etherbwrite: version %d", version);			}			return;		}	} 	/* need to have single block */	assert(bp->list == nil); 	/* make it a single block with space for the ether header */	bp = padblock(bp, ETHERHDRSIZE);	if(BLEN(bp) < ETHERMINTU)		bp = adjustblock(bp, ETHERMINTU);	eh = (Etherhdr*)bp->rp; 	/* copy in mac addresses and ether type */	memmove(eh->s, ifc->mac, sizeof(eh->s));	memmove(eh->d, mac, sizeof(eh->d));  	switch(version){	case V4:		eh->t[0] = 0x08;		eh->t[1] = 0x00;		devtab[er->mchan4->type]->bwrite(er->mchan4, bp, 0);		break;	case V6:		eh->t[0] = 0x86;		eh->t[1] = 0xDD;		devtab[er->mchan6->type]->bwrite(er->mchan6, bp, 0);		break;	default:		panic("etherbwrite2: version %d", version);	}}  /* *  process to read from the ethernet */static voidetherread4(void *a){	Ipifc *ifc;	Block *bp;	Etherrock *er; 	ifc = a;	er = ifc->arg;	er->read4p = up;	/* hide identity under a rock for unbind */	if(!waserror())	for(;;){		bp = devtab[er->mchan4->type]->bread(er->mchan4, ifc->maxtu, 0);		if(bp == nil){			poperror();			break;		}		rlock(ifc);		if(waserror()){			runlock(ifc);			continue;		}		if(BLEN(bp) <= ETHERHDRSIZE)			freeb(bp);		else {			bp->rp += ETHERHDRSIZE;			ipiput4(er->f, ifc, bp);		}		runlock(ifc);		poperror();	}	if(mediumunbindifc(ifc) != nil)		er->read4p = nil;	/* someone else is doing the unbind */	pexit("hangup", 1);}  /* *  process to read from the ethernet, IPv6 */static voidetherread6(void *a){	Ipifc *ifc;	Block *bp;	Etherrock *er; 	ifc = a;	er = ifc->arg;	er->read6p = up;	/* hide identity under a rock for unbind */	if(!waserror())	for(;;){		bp = devtab[er->mchan6->type]->bread(er->mchan6, ifc->maxtu, 0);		if(bp == nil){			poperror();			break;		}		rlock(ifc);		if(waserror()){			runlock(ifc);			continue;		}		if(BLEN(bp) <= ETHERHDRSIZE)			freeb(bp);		else {			bp->rp += ETHERHDRSIZE;			ipiput6(er->f, ifc, bp);		}		runlock(ifc);		poperror();	}	if(mediumunbindifc(ifc) != nil)		er->read6p = nil;	/* someone else is doing the unbind */	pexit("hangup", 1);} static voidetheraddmulti(Ipifc *ifc, uchar *a, uchar *){	uchar mac[6];	char buf[64];	Etherrock *er = ifc->arg;	int version; 	version = multicastea(mac, a);	sprint(buf, "addmulti %E", mac);	switch(version){	case V4:		devtab[er->cchan4->type]->write(er->cchan4, buf, strlen(buf), 0);		break;	case V6:		devtab[er->cchan6->type]->write(er->cchan6, buf, strlen(buf), 0);		break;	default:		panic("etheraddmulti: version %d", version);	}} static voidetherdelmulti(Ipifc *ifc, uchar *a, uchar *){	uchar mac[6];	char buf[64];	Etherrock *er = ifc->arg;	int version; 	version = multicastea(mac, a);	sprint(buf, "remmulti %E", mac);	switch(version){	case V4:		devtab[er->cchan4->type]->write(er->cchan4, buf, strlen(buf), 0);		break;	case V6:		devtab[er->cchan6->type]->write(er->cchan6, buf, strlen(buf), 0);		break;	default:		panic("etherdelmulti: version %d", version);	}} static Block*newEARP(void){	Block *bp; 	bp = allocb(ETHERMINTU);	bp->wp += ETHERMINTU;	memset(bp->rp, 0, ETHERMINTU);	return bp;} /* *  send an ethernet arp *  (only v4, v6 uses the neighbor discovery, rfc1970) */static voidsendarpreq(Fs *f, Arpent *a){	Block *bp;	Etherarp *e;	Ipifc *ifc = a->ifc;	Etherrock *er = ifc->arg;	uchar targ[IPv4addrlen], src[IPv4addrlen]; 	memmove(targ, a->ip+IPv4off, IPv4addrlen);	arpcontinue(f->arp, a); 	if(!ipv4local(ifc, src, 0, targ))		return; 	bp = newEARP();	e = (Etherarp*)bp->rp;	memmove(e->tpa, targ, sizeof(e->tpa));	memmove(e->spa, src, sizeof(e->spa));	memmove(e->sha, ifc->mac, sizeof(e->sha));	memset(e->d, 0xff, sizeof(e->d));		/* ethernet broadcast */	memmove(e->s, ifc->mac, sizeof(e->s)); 	hnputs(e->type, ETARP);	hnputs(e->hrd, 1);	hnputs(e->pro, ETIP4);	e->hln = sizeof(e->sha);	e->pln = sizeof(e->spa);	hnputs(e->op, ARPREQUEST); 	devtab[er->achan->type]->bwrite(er->achan, bp, 0);} /* *  send a gratuitous arp to refresh arp caches */static voidsendgarp(Ipifc *ifc, uchar *ip){	Block *bp;	Etherarp *e;	Etherrock *er = ifc->arg; 	bp = newEARP();	e = (Etherarp*)bp->rp;	memmove(e->tpa, ip+IPv4off, sizeof(e->tpa));

Lines 1–500 of 734.