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