xref: /plan9/sys/src/9/ip/ethermedium.c (revision 80ee5cbfe36716af62da8896207e9763b8e3d760)
17dd7cddfSDavid du Colombier #include "u.h"
27dd7cddfSDavid du Colombier #include "../port/lib.h"
37dd7cddfSDavid du Colombier #include "mem.h"
47dd7cddfSDavid du Colombier #include "dat.h"
57dd7cddfSDavid du Colombier #include "fns.h"
67dd7cddfSDavid du Colombier #include "../port/error.h"
77dd7cddfSDavid du Colombier 
87dd7cddfSDavid du Colombier #include "ip.h"
97dd7cddfSDavid du Colombier 
107dd7cddfSDavid du Colombier typedef struct Etherhdr Etherhdr;
117dd7cddfSDavid du Colombier struct Etherhdr
127dd7cddfSDavid du Colombier {
137dd7cddfSDavid du Colombier 	uchar	d[6];
147dd7cddfSDavid du Colombier 	uchar	s[6];
157dd7cddfSDavid du Colombier 	uchar	t[2];
167dd7cddfSDavid du Colombier };
177dd7cddfSDavid du Colombier 
187dd7cddfSDavid du Colombier static void	etherread(void *a);
197dd7cddfSDavid du Colombier static void	etherbind(Ipifc *ifc, int argc, char **argv);
207dd7cddfSDavid du Colombier static void	etherunbind(Ipifc *ifc);
217dd7cddfSDavid du Colombier static void	etherbwrite(Ipifc *ifc, Block *bp, int version, uchar *ip);
227dd7cddfSDavid du Colombier static void	etheraddmulti(Ipifc *ifc, uchar *a, uchar *ia);
237dd7cddfSDavid du Colombier static void	etherremmulti(Ipifc *ifc, uchar *a, uchar *ia);
2459cc4ca5SDavid du Colombier static Block*	multicastarp(Fs *f, Arpent *a, Medium*, uchar *mac);
257dd7cddfSDavid du Colombier static void	sendarp(Ipifc *ifc, Arpent *a);
267dd7cddfSDavid du Colombier static void	sendgarp(Ipifc *ifc, uchar*);
277dd7cddfSDavid du Colombier static int	multicastea(uchar *ea, uchar *ip);
287dd7cddfSDavid du Colombier static void	recvarpproc(void*);
297dd7cddfSDavid du Colombier 
307dd7cddfSDavid du Colombier Medium ethermedium =
317dd7cddfSDavid du Colombier {
327dd7cddfSDavid du Colombier .name=		"ether",
337dd7cddfSDavid du Colombier .hsize=		14,
347dd7cddfSDavid du Colombier .minmtu=	60,
357dd7cddfSDavid du Colombier .maxmtu=	1514,
367dd7cddfSDavid du Colombier .maclen=	6,
377dd7cddfSDavid du Colombier .bind=		etherbind,
387dd7cddfSDavid du Colombier .unbind=	etherunbind,
397dd7cddfSDavid du Colombier .bwrite=	etherbwrite,
407dd7cddfSDavid du Colombier .addmulti=	etheraddmulti,
417dd7cddfSDavid du Colombier .remmulti=	etherremmulti,
427dd7cddfSDavid du Colombier .ares=		arpenter,
437dd7cddfSDavid du Colombier .areg=		sendgarp,
447dd7cddfSDavid du Colombier };
457dd7cddfSDavid du Colombier 
4659cc4ca5SDavid du Colombier Medium gbemedium =
4759cc4ca5SDavid du Colombier {
4859cc4ca5SDavid du Colombier .name=		"gbe",
4959cc4ca5SDavid du Colombier .hsize=		14,
5059cc4ca5SDavid du Colombier .minmtu=	60,
5159cc4ca5SDavid du Colombier .maxmtu=	9014,
5259cc4ca5SDavid du Colombier .maclen=	6,
5359cc4ca5SDavid du Colombier .bind=		etherbind,
5459cc4ca5SDavid du Colombier .unbind=	etherunbind,
5559cc4ca5SDavid du Colombier .bwrite=	etherbwrite,
5659cc4ca5SDavid du Colombier .addmulti=	etheraddmulti,
5759cc4ca5SDavid du Colombier .remmulti=	etherremmulti,
5859cc4ca5SDavid du Colombier .ares=		arpenter,
5959cc4ca5SDavid du Colombier .areg=		sendgarp,
6059cc4ca5SDavid du Colombier };
6159cc4ca5SDavid du Colombier 
627dd7cddfSDavid du Colombier typedef struct	Etherrock Etherrock;
637dd7cddfSDavid du Colombier struct Etherrock
647dd7cddfSDavid du Colombier {
657dd7cddfSDavid du Colombier 	Fs	*f;		/* file system we belong to */
667dd7cddfSDavid du Colombier 	Proc	*arpp;		/* arp process */
677dd7cddfSDavid du Colombier 	Proc	*readp;		/* reading process */
687dd7cddfSDavid du Colombier 	Chan	*mchan;		/* Data channel */
697dd7cddfSDavid du Colombier 	Chan	*achan;		/* Arp channel */
707dd7cddfSDavid du Colombier 	Chan	*cchan;		/* Control channel */
717dd7cddfSDavid du Colombier };
727dd7cddfSDavid du Colombier 
737dd7cddfSDavid du Colombier /*
747dd7cddfSDavid du Colombier  *  ethernet arp request
757dd7cddfSDavid du Colombier  */
767dd7cddfSDavid du Colombier enum
777dd7cddfSDavid du Colombier {
787dd7cddfSDavid du Colombier 	ETARP		= 0x0806,
797dd7cddfSDavid du Colombier 	ETIP		= 0x0800,
807dd7cddfSDavid du Colombier 	ARPREQUEST	= 1,
817dd7cddfSDavid du Colombier 	ARPREPLY	= 2,
827dd7cddfSDavid du Colombier };
837dd7cddfSDavid du Colombier typedef struct Etherarp Etherarp;
847dd7cddfSDavid du Colombier struct Etherarp
857dd7cddfSDavid du Colombier {
867dd7cddfSDavid du Colombier 	uchar	d[6];
877dd7cddfSDavid du Colombier 	uchar	s[6];
887dd7cddfSDavid du Colombier 	uchar	type[2];
897dd7cddfSDavid du Colombier 	uchar	hrd[2];
907dd7cddfSDavid du Colombier 	uchar	pro[2];
917dd7cddfSDavid du Colombier 	uchar	hln;
927dd7cddfSDavid du Colombier 	uchar	pln;
937dd7cddfSDavid du Colombier 	uchar	op[2];
947dd7cddfSDavid du Colombier 	uchar	sha[6];
957dd7cddfSDavid du Colombier 	uchar	spa[4];
967dd7cddfSDavid du Colombier 	uchar	tha[6];
977dd7cddfSDavid du Colombier 	uchar	tpa[4];
987dd7cddfSDavid du Colombier };
997dd7cddfSDavid du Colombier 
1007dd7cddfSDavid du Colombier 
1017dd7cddfSDavid du Colombier /*
1027dd7cddfSDavid du Colombier  *  called to bind an IP ifc to an ethernet device
1037dd7cddfSDavid du Colombier  *  called with ifc wlock'd
1047dd7cddfSDavid du Colombier  */
1057dd7cddfSDavid du Colombier static void
1067dd7cddfSDavid du Colombier etherbind(Ipifc *ifc, int argc, char **argv)
1077dd7cddfSDavid du Colombier {
1087dd7cddfSDavid du Colombier 	Chan *mchan, *cchan, *achan;
1097dd7cddfSDavid du Colombier 	char addr[2*NAMELEN];
1107dd7cddfSDavid du Colombier 	char dir[2*NAMELEN];
1117dd7cddfSDavid du Colombier 	char *buf;
112*80ee5cbfSDavid du Colombier 	int n;
1137dd7cddfSDavid du Colombier 	char *ptr;
1147dd7cddfSDavid du Colombier 	Etherrock *er;
1157dd7cddfSDavid du Colombier 
1167dd7cddfSDavid du Colombier 	if(argc < 2)
1177dd7cddfSDavid du Colombier 		error(Ebadarg);
1187dd7cddfSDavid du Colombier 
1197dd7cddfSDavid du Colombier 	mchan = cchan = achan = nil;
1207dd7cddfSDavid du Colombier 	buf = nil;
1217dd7cddfSDavid du Colombier 	if(waserror()){
1227dd7cddfSDavid du Colombier 		if(mchan != nil)
1237dd7cddfSDavid du Colombier 			cclose(mchan);
1247dd7cddfSDavid du Colombier 		if(cchan != nil)
1257dd7cddfSDavid du Colombier 			cclose(cchan);
1267dd7cddfSDavid du Colombier 		if(achan != nil)
1277dd7cddfSDavid du Colombier 			cclose(achan);
1287dd7cddfSDavid du Colombier 		if(buf != nil)
1297dd7cddfSDavid du Colombier 			free(buf);
1307dd7cddfSDavid du Colombier 		nexterror();
1317dd7cddfSDavid du Colombier 	}
1327dd7cddfSDavid du Colombier 
1337dd7cddfSDavid du Colombier 	/*
1347dd7cddfSDavid du Colombier 	 *  open ip conversation
1357dd7cddfSDavid du Colombier 	 *
1367dd7cddfSDavid du Colombier 	 *  the dial will fail if the type is already open on
1377dd7cddfSDavid du Colombier 	 *  this device.
1387dd7cddfSDavid du Colombier 	 */
1397dd7cddfSDavid du Colombier 	snprint(addr, sizeof(addr), "%s!0x800", argv[2]);
140*80ee5cbfSDavid du Colombier 	mchan = chandial(addr, nil, dir, &cchan);
1417dd7cddfSDavid du Colombier 
1427dd7cddfSDavid du Colombier 	/*
1437dd7cddfSDavid du Colombier 	 *  get mac address
1447dd7cddfSDavid du Colombier 	 */
1457dd7cddfSDavid du Colombier 	snprint(addr, sizeof(addr), "%s/stats", dir);
1467dd7cddfSDavid du Colombier 	buf = smalloc(512);
147*80ee5cbfSDavid du Colombier 	achan = namec(addr, Aopen, OREAD, 0);
148*80ee5cbfSDavid du Colombier 	n = devtab[achan->type]->read(achan, buf, 511, 0);
149*80ee5cbfSDavid du Colombier 	cclose(achan);
1507dd7cddfSDavid du Colombier 	buf[n] = 0;
1517dd7cddfSDavid du Colombier 
1527dd7cddfSDavid du Colombier 	ptr = strstr(buf, "addr: ");
1537dd7cddfSDavid du Colombier 	if(!ptr)
1547dd7cddfSDavid du Colombier 		error(Eio);
1557dd7cddfSDavid du Colombier 	ptr += 6;
1567dd7cddfSDavid du Colombier 
1577dd7cddfSDavid du Colombier 	parsemac(ifc->mac, ptr, 6);
1587dd7cddfSDavid du Colombier 
1597dd7cddfSDavid du Colombier 	/*
1607dd7cddfSDavid du Colombier  	 *  open arp conversation
1617dd7cddfSDavid du Colombier 	 */
1627dd7cddfSDavid du Colombier 	snprint(addr, sizeof(addr), "%s!0x806", argv[2]);
163*80ee5cbfSDavid du Colombier 	achan = chandial(addr, nil, nil, nil);
1647dd7cddfSDavid du Colombier 
1657dd7cddfSDavid du Colombier 	er = smalloc(sizeof(*er));
1667dd7cddfSDavid du Colombier 	er->mchan = mchan;
1677dd7cddfSDavid du Colombier 	er->cchan = cchan;
1687dd7cddfSDavid du Colombier 	er->achan = achan;
1697dd7cddfSDavid du Colombier 	er->f = ifc->conv->p->f;
1707dd7cddfSDavid du Colombier 	ifc->arg = er;
1717dd7cddfSDavid du Colombier 
1727dd7cddfSDavid du Colombier 	free(buf);
1737dd7cddfSDavid du Colombier 	poperror();
1747dd7cddfSDavid du Colombier 
1757dd7cddfSDavid du Colombier 	kproc("etherread", etherread, ifc);
1767dd7cddfSDavid du Colombier 	kproc("recvarpproc", recvarpproc, ifc);
1777dd7cddfSDavid du Colombier }
1787dd7cddfSDavid du Colombier 
1797dd7cddfSDavid du Colombier /*
1807dd7cddfSDavid du Colombier  *  called with ifc wlock'd
1817dd7cddfSDavid du Colombier  */
1827dd7cddfSDavid du Colombier static void
1837dd7cddfSDavid du Colombier etherunbind(Ipifc *ifc)
1847dd7cddfSDavid du Colombier {
1857dd7cddfSDavid du Colombier 	Etherrock *er = ifc->arg;
1867dd7cddfSDavid du Colombier 
1877dd7cddfSDavid du Colombier 	if(er->readp)
1887dd7cddfSDavid du Colombier 		postnote(er->readp, 1, "unbind", 0);
1897dd7cddfSDavid du Colombier 	if(er->arpp)
1907dd7cddfSDavid du Colombier 		postnote(er->arpp, 1, "unbind", 0);
1917dd7cddfSDavid du Colombier 
1927dd7cddfSDavid du Colombier 	/* wait for readers to die */
1937dd7cddfSDavid du Colombier 	while(er->arpp != 0 || er->readp != 0)
1947dd7cddfSDavid du Colombier 		tsleep(&up->sleep, return0, 0, 300);
1957dd7cddfSDavid du Colombier 
1967dd7cddfSDavid du Colombier 	if(er->mchan != nil)
1977dd7cddfSDavid du Colombier 		cclose(er->mchan);
1987dd7cddfSDavid du Colombier 	if(er->achan != nil)
1997dd7cddfSDavid du Colombier 		cclose(er->achan);
2007dd7cddfSDavid du Colombier 	if(er->cchan != nil)
2017dd7cddfSDavid du Colombier 		cclose(er->cchan);
2027dd7cddfSDavid du Colombier 
2037dd7cddfSDavid du Colombier 	free(er);
2047dd7cddfSDavid du Colombier }
2057dd7cddfSDavid du Colombier 
2067dd7cddfSDavid du Colombier /*
2077dd7cddfSDavid du Colombier  *  called by ipoput with a single block to write with ifc rlock'd
2087dd7cddfSDavid du Colombier  */
2097dd7cddfSDavid du Colombier static void
2107dd7cddfSDavid du Colombier etherbwrite(Ipifc *ifc, Block *bp, int version, uchar *ip)
2117dd7cddfSDavid du Colombier {
2127dd7cddfSDavid du Colombier 	Etherhdr *eh;
2137dd7cddfSDavid du Colombier 	Arpent *a;
2147dd7cddfSDavid du Colombier 	uchar mac[6];
2157dd7cddfSDavid du Colombier 	Etherrock *er = ifc->arg;
2167dd7cddfSDavid du Colombier 
2177dd7cddfSDavid du Colombier 	/* get mac address of destination */
21859cc4ca5SDavid du Colombier 	a = arpget(er->f->arp, bp, version, ifc->m, ip, mac);
2197dd7cddfSDavid du Colombier 	if(a){
2207dd7cddfSDavid du Colombier 		/* check for broadcast or multicast */
22159cc4ca5SDavid du Colombier 		bp = multicastarp(er->f, a, ifc->m, mac);
2227dd7cddfSDavid du Colombier 		if(bp == nil){
2237dd7cddfSDavid du Colombier 			sendarp(ifc, a);
2247dd7cddfSDavid du Colombier 			return;
2257dd7cddfSDavid du Colombier 		}
2267dd7cddfSDavid du Colombier 	}
2277dd7cddfSDavid du Colombier 
2287dd7cddfSDavid du Colombier 	/* make it a single block with space for the ether header */
2297dd7cddfSDavid du Colombier 	bp = padblock(bp, ifc->m->hsize);
2307dd7cddfSDavid du Colombier 	if(bp->next)
2317dd7cddfSDavid du Colombier 		bp = concatblock(bp);
2327dd7cddfSDavid du Colombier 	if(BLEN(bp) < ifc->minmtu)
2337dd7cddfSDavid du Colombier 		bp = adjustblock(bp, ifc->minmtu);
2347dd7cddfSDavid du Colombier 	eh = (Etherhdr*)bp->rp;
2357dd7cddfSDavid du Colombier 
2367dd7cddfSDavid du Colombier 	/* copy in mac addresses and ether type */
2377dd7cddfSDavid du Colombier 	memmove(eh->s, ifc->mac, sizeof(eh->s));
2387dd7cddfSDavid du Colombier 	memmove(eh->d, mac, sizeof(eh->d));
2397dd7cddfSDavid du Colombier 	switch(version){
2407dd7cddfSDavid du Colombier 	case V4:
2417dd7cddfSDavid du Colombier 		eh->t[0] = 0x08;
2427dd7cddfSDavid du Colombier 		eh->t[1] = 0x00;
2437dd7cddfSDavid du Colombier 		break;
2447dd7cddfSDavid du Colombier 	case V6:
2457dd7cddfSDavid du Colombier 		eh->t[0] = 0x86;
2467dd7cddfSDavid du Colombier 		eh->t[1] = 0xDD;
2477dd7cddfSDavid du Colombier 		break;
2487dd7cddfSDavid du Colombier 	}
2497dd7cddfSDavid du Colombier 
2507dd7cddfSDavid du Colombier 	devtab[er->mchan->type]->bwrite(er->mchan, bp, 0);
2517dd7cddfSDavid du Colombier 	ifc->out++;
2527dd7cddfSDavid du Colombier }
2537dd7cddfSDavid du Colombier 
2547dd7cddfSDavid du Colombier /*
2557dd7cddfSDavid du Colombier  *  process to read from the ethernet
2567dd7cddfSDavid du Colombier  */
2577dd7cddfSDavid du Colombier static void
2587dd7cddfSDavid du Colombier etherread(void *a)
2597dd7cddfSDavid du Colombier {
2607dd7cddfSDavid du Colombier 	Ipifc *ifc;
2617dd7cddfSDavid du Colombier 	Block *bp;
2627dd7cddfSDavid du Colombier 	Etherrock *er;
2637dd7cddfSDavid du Colombier 
2647dd7cddfSDavid du Colombier 	ifc = a;
2657dd7cddfSDavid du Colombier 	er = ifc->arg;
2667dd7cddfSDavid du Colombier 	er->readp = up;	/* hide identity under a rock for unbind */
2677dd7cddfSDavid du Colombier 	if(waserror()){
2687dd7cddfSDavid du Colombier 		er->readp = 0;
2697dd7cddfSDavid du Colombier 		pexit("hangup", 1);
2707dd7cddfSDavid du Colombier 	}
2717dd7cddfSDavid du Colombier 	for(;;){
2727dd7cddfSDavid du Colombier 		bp = devtab[er->mchan->type]->bread(er->mchan, ifc->maxmtu, 0);
2737dd7cddfSDavid du Colombier 		if(!canrlock(ifc)){
2747dd7cddfSDavid du Colombier 			freeb(bp);
2757dd7cddfSDavid du Colombier 			continue;
2767dd7cddfSDavid du Colombier 		}
2777dd7cddfSDavid du Colombier 		if(waserror()){
2787dd7cddfSDavid du Colombier 			runlock(ifc);
2797dd7cddfSDavid du Colombier 			nexterror();
2807dd7cddfSDavid du Colombier 		}
2817dd7cddfSDavid du Colombier 		ifc->in++;
2827dd7cddfSDavid du Colombier 		bp->rp += ifc->m->hsize;
2837dd7cddfSDavid du Colombier 		if(ifc->lifc == nil)
2847dd7cddfSDavid du Colombier 			freeb(bp);
2857dd7cddfSDavid du Colombier 		else
2867dd7cddfSDavid du Colombier 			ipiput(er->f, ifc->lifc->local, bp);
2877dd7cddfSDavid du Colombier 		runlock(ifc);
2887dd7cddfSDavid du Colombier 		poperror();
2897dd7cddfSDavid du Colombier 	}
2907dd7cddfSDavid du Colombier }
2917dd7cddfSDavid du Colombier 
2927dd7cddfSDavid du Colombier static void
2937dd7cddfSDavid du Colombier etheraddmulti(Ipifc *ifc, uchar *a, uchar *)
2947dd7cddfSDavid du Colombier {
2957dd7cddfSDavid du Colombier 	uchar mac[6];
2967dd7cddfSDavid du Colombier 	char buf[64];
2977dd7cddfSDavid du Colombier 	Etherrock *er = ifc->arg;
2987dd7cddfSDavid du Colombier 
2997dd7cddfSDavid du Colombier 	multicastea(mac, a);
3007dd7cddfSDavid du Colombier 	sprint(buf, "addmulti %E", mac);
3017dd7cddfSDavid du Colombier 	devtab[er->cchan->type]->write(er->cchan, buf, strlen(buf), 0);
3027dd7cddfSDavid du Colombier }
3037dd7cddfSDavid du Colombier 
3047dd7cddfSDavid du Colombier static void
3057dd7cddfSDavid du Colombier etherremmulti(Ipifc *ifc, uchar *a, uchar *)
3067dd7cddfSDavid du Colombier {
3077dd7cddfSDavid du Colombier 	uchar mac[6];
3087dd7cddfSDavid du Colombier 	char buf[64];
3097dd7cddfSDavid du Colombier 	Etherrock *er = ifc->arg;
3107dd7cddfSDavid du Colombier 
3117dd7cddfSDavid du Colombier 	multicastea(mac, a);
3127dd7cddfSDavid du Colombier 	sprint(buf, "remmulti %E", mac);
3137dd7cddfSDavid du Colombier 	devtab[er->cchan->type]->write(er->cchan, buf, strlen(buf), 0);
3147dd7cddfSDavid du Colombier }
3157dd7cddfSDavid du Colombier 
3167dd7cddfSDavid du Colombier /*
3177dd7cddfSDavid du Colombier  *  send an ethernet arp
3187dd7cddfSDavid du Colombier  *  (only v4, v6 uses the neighbor discovery, rfc1970)
3197dd7cddfSDavid du Colombier  */
3207dd7cddfSDavid du Colombier static void
3217dd7cddfSDavid du Colombier sendarp(Ipifc *ifc, Arpent *a)
3227dd7cddfSDavid du Colombier {
3237dd7cddfSDavid du Colombier 	int n;
3247dd7cddfSDavid du Colombier 	Block *bp;
3257dd7cddfSDavid du Colombier 	Etherarp *e;
3267dd7cddfSDavid du Colombier 	Etherrock *er = ifc->arg;
3277dd7cddfSDavid du Colombier 
3287dd7cddfSDavid du Colombier 	/* don't do anything if it's been less than a second since the last */
3297dd7cddfSDavid du Colombier 	if(msec - a->time < 1000){
3307dd7cddfSDavid du Colombier 		arprelease(er->f->arp, a);
3317dd7cddfSDavid du Colombier 		return;
3327dd7cddfSDavid du Colombier 	}
3337dd7cddfSDavid du Colombier 
3347dd7cddfSDavid du Colombier 	/* remove all but the last message */
3357dd7cddfSDavid du Colombier 	while((bp = a->hold) != nil){
3367dd7cddfSDavid du Colombier 		if(bp == a->last)
3377dd7cddfSDavid du Colombier 			break;
3387dd7cddfSDavid du Colombier 		a->hold = bp->list;
3397dd7cddfSDavid du Colombier 		freeblist(bp);
3407dd7cddfSDavid du Colombier 	}
3417dd7cddfSDavid du Colombier 
3427dd7cddfSDavid du Colombier 	/* try to keep it around for a second more */
3437dd7cddfSDavid du Colombier 	a->time = msec;
3447dd7cddfSDavid du Colombier 	arprelease(er->f->arp, a);
3457dd7cddfSDavid du Colombier 
3467dd7cddfSDavid du Colombier 	n = sizeof(Etherarp);
3477dd7cddfSDavid du Colombier 	if(n < a->type->minmtu)
3487dd7cddfSDavid du Colombier 		n = a->type->minmtu;
3497dd7cddfSDavid du Colombier 	bp = allocb(n);
3507dd7cddfSDavid du Colombier 	memset(bp->rp, 0, n);
3517dd7cddfSDavid du Colombier 	e = (Etherarp*)bp->rp;
3527dd7cddfSDavid du Colombier 	memmove(e->tpa, a->ip+IPv4off, sizeof(e->tpa));
3537dd7cddfSDavid du Colombier 	ipv4local(ifc, e->spa);
3547dd7cddfSDavid du Colombier 	memmove(e->sha, ifc->mac, sizeof(e->sha));
3557dd7cddfSDavid du Colombier 	memset(e->d, 0xff, sizeof(e->d));		/* ethernet broadcast */
3567dd7cddfSDavid du Colombier 	memmove(e->s, ifc->mac, sizeof(e->s));
3577dd7cddfSDavid du Colombier 
3587dd7cddfSDavid du Colombier 	hnputs(e->type, ETARP);
3597dd7cddfSDavid du Colombier 	hnputs(e->hrd, 1);
3607dd7cddfSDavid du Colombier 	hnputs(e->pro, ETIP);
3617dd7cddfSDavid du Colombier 	e->hln = sizeof(e->sha);
3627dd7cddfSDavid du Colombier 	e->pln = sizeof(e->spa);
3637dd7cddfSDavid du Colombier 	hnputs(e->op, ARPREQUEST);
3647dd7cddfSDavid du Colombier 	bp->wp += n;
3657dd7cddfSDavid du Colombier 
3667dd7cddfSDavid du Colombier 	n = devtab[er->achan->type]->bwrite(er->achan, bp, 0);
3677dd7cddfSDavid du Colombier 	if(n < 0)
3687dd7cddfSDavid du Colombier 		print("arp: send: %r\n");
3697dd7cddfSDavid du Colombier }
3707dd7cddfSDavid du Colombier 
3717dd7cddfSDavid du Colombier /*
3727dd7cddfSDavid du Colombier  *  send a gratuitous arp to refresh arp caches
3737dd7cddfSDavid du Colombier  */
3747dd7cddfSDavid du Colombier static void
3757dd7cddfSDavid du Colombier sendgarp(Ipifc *ifc, uchar *ip)
3767dd7cddfSDavid du Colombier {
3777dd7cddfSDavid du Colombier 	int n;
3787dd7cddfSDavid du Colombier 	Block *bp;
3797dd7cddfSDavid du Colombier 	Etherarp *e;
3807dd7cddfSDavid du Colombier 	Etherrock *er = ifc->arg;
3817dd7cddfSDavid du Colombier 
3827dd7cddfSDavid du Colombier 	/* don't arp for our initial non address */
3837dd7cddfSDavid du Colombier 	if(ipcmp(ip, IPnoaddr) == 0)
3847dd7cddfSDavid du Colombier 		return;
3857dd7cddfSDavid du Colombier 
3867dd7cddfSDavid du Colombier 	n = sizeof(Etherarp);
38759cc4ca5SDavid du Colombier 	if(n < ifc->m->minmtu)
38859cc4ca5SDavid du Colombier 		n = ifc->m->minmtu;
3897dd7cddfSDavid du Colombier 	bp = allocb(n);
3907dd7cddfSDavid du Colombier 	memset(bp->rp, 0, n);
3917dd7cddfSDavid du Colombier 	e = (Etherarp*)bp->rp;
3927dd7cddfSDavid du Colombier 	memmove(e->tpa, ip+IPv4off, sizeof(e->tpa));
3937dd7cddfSDavid du Colombier 	memmove(e->spa, ip+IPv4off, sizeof(e->spa));
3947dd7cddfSDavid du Colombier 	memmove(e->sha, ifc->mac, sizeof(e->sha));
3957dd7cddfSDavid du Colombier 	memset(e->d, 0xff, sizeof(e->d));		/* ethernet broadcast */
3967dd7cddfSDavid du Colombier 	memmove(e->s, ifc->mac, sizeof(e->s));
3977dd7cddfSDavid du Colombier 
3987dd7cddfSDavid du Colombier 	hnputs(e->type, ETARP);
3997dd7cddfSDavid du Colombier 	hnputs(e->hrd, 1);
4007dd7cddfSDavid du Colombier 	hnputs(e->pro, ETIP);
4017dd7cddfSDavid du Colombier 	e->hln = sizeof(e->sha);
4027dd7cddfSDavid du Colombier 	e->pln = sizeof(e->spa);
4037dd7cddfSDavid du Colombier 	hnputs(e->op, ARPREQUEST);
4047dd7cddfSDavid du Colombier 	bp->wp += n;
4057dd7cddfSDavid du Colombier 
4067dd7cddfSDavid du Colombier 	n = devtab[er->achan->type]->bwrite(er->achan, bp, 0);
4077dd7cddfSDavid du Colombier 	if(n < 0)
4087dd7cddfSDavid du Colombier 		print("garp: send: %r\n");
4097dd7cddfSDavid du Colombier }
4107dd7cddfSDavid du Colombier 
4117dd7cddfSDavid du Colombier static void
4127dd7cddfSDavid du Colombier recvarp(Ipifc *ifc)
4137dd7cddfSDavid du Colombier {
4147dd7cddfSDavid du Colombier 	int n;
4157dd7cddfSDavid du Colombier 	Block *ebp, *rbp;
4167dd7cddfSDavid du Colombier 	Etherarp *e, *r;
4177dd7cddfSDavid du Colombier 	uchar ip[IPaddrlen];
4187dd7cddfSDavid du Colombier 	Etherrock *er = ifc->arg;
4197dd7cddfSDavid du Colombier 
4207dd7cddfSDavid du Colombier 	ebp = devtab[er->achan->type]->bread(er->achan, ifc->maxmtu, 0);
4217dd7cddfSDavid du Colombier 	if(ebp == nil) {
4227dd7cddfSDavid du Colombier 		print("arp: rcv: %r\n");
4237dd7cddfSDavid du Colombier 		return;
4247dd7cddfSDavid du Colombier 	}
4257dd7cddfSDavid du Colombier 
4267dd7cddfSDavid du Colombier 	e = (Etherarp*)ebp->rp;
4277dd7cddfSDavid du Colombier 	switch(nhgets(e->op)) {
4287dd7cddfSDavid du Colombier 	default:
4297dd7cddfSDavid du Colombier 		break;
4307dd7cddfSDavid du Colombier 
4317dd7cddfSDavid du Colombier 	case ARPREPLY:
4327dd7cddfSDavid du Colombier 		arpenter(er->f, V4, e->spa, e->sha, sizeof(e->sha), 0);
4337dd7cddfSDavid du Colombier 		break;
4347dd7cddfSDavid du Colombier 
4357dd7cddfSDavid du Colombier 	case ARPREQUEST:
4367dd7cddfSDavid du Colombier 		/* don't answer arps till we know who we are */
4377dd7cddfSDavid du Colombier 		if(ifc->lifc == 0)
4387dd7cddfSDavid du Colombier 			break;
4397dd7cddfSDavid du Colombier 
4407dd7cddfSDavid du Colombier 		/* check for someone that think's they're me */
4417dd7cddfSDavid du Colombier 		v4tov6(ip, e->spa);
4427dd7cddfSDavid du Colombier 		if(iplocalonifc(ifc, ip) || ipproxyifc(er->f, ifc, ip)){
4437dd7cddfSDavid du Colombier 			if(memcmp(e->sha, ifc->mac, sizeof(e->sha)) != 0)
4447dd7cddfSDavid du Colombier 				print("arp: 0x%E also has ip addr %V\n", e->sha, e->spa);
4457dd7cddfSDavid du Colombier 		} else {
4467dd7cddfSDavid du Colombier 			if(memcmp(e->sha, ifc->mac, sizeof(e->sha)) == 0){
4477dd7cddfSDavid du Colombier 				print("arp: %V also has ether addr %E\n", e->spa, e->sha);
4487dd7cddfSDavid du Colombier 				break;
4497dd7cddfSDavid du Colombier 			}
4507dd7cddfSDavid du Colombier 		}
4517dd7cddfSDavid du Colombier 
4527dd7cddfSDavid du Colombier 		/* refresh what we know about sender */
4537dd7cddfSDavid du Colombier 		arpenter(er->f, V4, e->spa, e->sha, sizeof(e->sha), 1);
4547dd7cddfSDavid du Colombier 
4557dd7cddfSDavid du Colombier 		/* answer only requests for our address or systems we're proxying for */
4567dd7cddfSDavid du Colombier 		v4tov6(ip, e->tpa);
4577dd7cddfSDavid du Colombier 		if(!iplocalonifc(ifc, ip))
4587dd7cddfSDavid du Colombier 		if(!ipproxyifc(er->f, ifc, ip))
4597dd7cddfSDavid du Colombier 			break;
4607dd7cddfSDavid du Colombier 
4617dd7cddfSDavid du Colombier /* print("arp: rem %I %E (for %I)\n", e->spa, e->sha, e->tpa); /**/
4627dd7cddfSDavid du Colombier 
4637dd7cddfSDavid du Colombier 		n = sizeof(Etherarp);
4647dd7cddfSDavid du Colombier 		if(n < ifc->minmtu)
4657dd7cddfSDavid du Colombier 			n = ifc->minmtu;
4667dd7cddfSDavid du Colombier 		rbp = allocb(n);
4677dd7cddfSDavid du Colombier 		r = (Etherarp*)rbp->rp;
4687dd7cddfSDavid du Colombier 		memset(r, 0, sizeof(Etherarp));
4697dd7cddfSDavid du Colombier 		hnputs(r->type, ETARP);
4707dd7cddfSDavid du Colombier 		hnputs(r->hrd, 1);
4717dd7cddfSDavid du Colombier 		hnputs(r->pro, ETIP);
4727dd7cddfSDavid du Colombier 		r->hln = sizeof(r->sha);
4737dd7cddfSDavid du Colombier 		r->pln = sizeof(r->spa);
4747dd7cddfSDavid du Colombier 		hnputs(r->op, ARPREPLY);
4757dd7cddfSDavid du Colombier 		memmove(r->tha, e->sha, sizeof(r->tha));
4767dd7cddfSDavid du Colombier 		memmove(r->tpa, e->spa, sizeof(r->tpa));
4777dd7cddfSDavid du Colombier 		memmove(r->sha, ifc->mac, sizeof(r->sha));
4787dd7cddfSDavid du Colombier 		memmove(r->spa, e->tpa, sizeof(r->spa));
4797dd7cddfSDavid du Colombier 		memmove(r->d, e->sha, sizeof(r->d));
4807dd7cddfSDavid du Colombier 		memmove(r->s, ifc->mac, sizeof(r->s));
4817dd7cddfSDavid du Colombier 		rbp->wp += n;
4827dd7cddfSDavid du Colombier 
4837dd7cddfSDavid du Colombier 		n = devtab[er->achan->type]->bwrite(er->achan, rbp, 0);
4847dd7cddfSDavid du Colombier 		if(n < 0)
4857dd7cddfSDavid du Colombier 			print("arp: write: %r\n");
4867dd7cddfSDavid du Colombier 	}
4877dd7cddfSDavid du Colombier 	freeb(ebp);
4887dd7cddfSDavid du Colombier }
4897dd7cddfSDavid du Colombier 
4907dd7cddfSDavid du Colombier static void
4917dd7cddfSDavid du Colombier recvarpproc(void *v)
4927dd7cddfSDavid du Colombier {
4937dd7cddfSDavid du Colombier 	Ipifc *ifc = v;
4947dd7cddfSDavid du Colombier 	Etherrock *er = ifc->arg;
4957dd7cddfSDavid du Colombier 
4967dd7cddfSDavid du Colombier 	er->arpp = up;
4977dd7cddfSDavid du Colombier 	if(waserror()){
4987dd7cddfSDavid du Colombier 		er->arpp = 0;
4997dd7cddfSDavid du Colombier 		pexit("hangup", 1);
5007dd7cddfSDavid du Colombier 	}
5017dd7cddfSDavid du Colombier 	for(;;)
5027dd7cddfSDavid du Colombier 		recvarp(ifc);
5037dd7cddfSDavid du Colombier }
5047dd7cddfSDavid du Colombier 
5057dd7cddfSDavid du Colombier static int
5067dd7cddfSDavid du Colombier multicastea(uchar *ea, uchar *ip)
5077dd7cddfSDavid du Colombier {
5087dd7cddfSDavid du Colombier 	int x;
5097dd7cddfSDavid du Colombier 
5107dd7cddfSDavid du Colombier 	switch(x = ipismulticast(ip)){
5117dd7cddfSDavid du Colombier 	case V4:
5127dd7cddfSDavid du Colombier 		ea[0] = 0x01;
5137dd7cddfSDavid du Colombier 		ea[1] = 0x00;
5147dd7cddfSDavid du Colombier 		ea[2] = 0x5e;
5157dd7cddfSDavid du Colombier 		ea[3] = ip[13] & 0x7f;
5167dd7cddfSDavid du Colombier 		ea[4] = ip[14];
5177dd7cddfSDavid du Colombier 		ea[5] = ip[15];
5187dd7cddfSDavid du Colombier 		break;
5197dd7cddfSDavid du Colombier 	case V6:
5207dd7cddfSDavid du Colombier 		ea[0] = 0x33;
5217dd7cddfSDavid du Colombier 		ea[1] = 0x33;
5227dd7cddfSDavid du Colombier 		ea[2] = ip[12];
5237dd7cddfSDavid du Colombier 		ea[3] = ip[13];
5247dd7cddfSDavid du Colombier 		ea[4] = ip[14];
5257dd7cddfSDavid du Colombier 		ea[5] = ip[15];
5267dd7cddfSDavid du Colombier 		break;
5277dd7cddfSDavid du Colombier 	}
5287dd7cddfSDavid du Colombier 	return x;
5297dd7cddfSDavid du Colombier }
5307dd7cddfSDavid du Colombier 
5317dd7cddfSDavid du Colombier /*
5327dd7cddfSDavid du Colombier  *  fill in an arp entry for broadcast or multicast
5337dd7cddfSDavid du Colombier  *  addresses
5347dd7cddfSDavid du Colombier  */
5357dd7cddfSDavid du Colombier static Block*
53659cc4ca5SDavid du Colombier multicastarp(Fs *f, Arpent *a, Medium *medium, uchar *mac)
5377dd7cddfSDavid du Colombier {
5387dd7cddfSDavid du Colombier 	/* is it broadcast? */
5397dd7cddfSDavid du Colombier 	switch(ipforme(f, a->ip)){
5407dd7cddfSDavid du Colombier 	case Runi:
5417dd7cddfSDavid du Colombier 		return nil;
5427dd7cddfSDavid du Colombier 	case Rbcast:
5437dd7cddfSDavid du Colombier 		memset(mac, 0xff, 6);
54459cc4ca5SDavid du Colombier 		return arpresolve(f->arp, a, medium, mac);
5457dd7cddfSDavid du Colombier 	default:
5467dd7cddfSDavid du Colombier 		break;
5477dd7cddfSDavid du Colombier 	}
5487dd7cddfSDavid du Colombier 
5497dd7cddfSDavid du Colombier 	/* if multicast, fill in mac */
5507dd7cddfSDavid du Colombier 	switch(multicastea(mac, a->ip)){
5517dd7cddfSDavid du Colombier 	case V4:
5527dd7cddfSDavid du Colombier 	case V6:
55359cc4ca5SDavid du Colombier 		return arpresolve(f->arp, a, medium, mac);
5547dd7cddfSDavid du Colombier 	}
5557dd7cddfSDavid du Colombier 
5567dd7cddfSDavid du Colombier 	/* let arp take care of it */
5577dd7cddfSDavid du Colombier 	return nil;
5587dd7cddfSDavid du Colombier }
5597dd7cddfSDavid du Colombier 
5607dd7cddfSDavid du Colombier void
5617dd7cddfSDavid du Colombier ethermediumlink(void)
5627dd7cddfSDavid du Colombier {
5637dd7cddfSDavid du Colombier 	addipmedium(&ethermedium);
56459cc4ca5SDavid du Colombier 	addipmedium(&gbemedium);
5657dd7cddfSDavid du Colombier }
566