xref: /plan9/sys/src/9/ip/ethermedium.c (revision 7dd7cddf99dd7472612f1413b4da293630e6b1bc)
1*7dd7cddfSDavid du Colombier #include "u.h"
2*7dd7cddfSDavid du Colombier #include "../port/lib.h"
3*7dd7cddfSDavid du Colombier #include "mem.h"
4*7dd7cddfSDavid du Colombier #include "dat.h"
5*7dd7cddfSDavid du Colombier #include "fns.h"
6*7dd7cddfSDavid du Colombier #include "../port/error.h"
7*7dd7cddfSDavid du Colombier 
8*7dd7cddfSDavid du Colombier #include "ip.h"
9*7dd7cddfSDavid du Colombier #include "kernel.h"
10*7dd7cddfSDavid du Colombier 
11*7dd7cddfSDavid du Colombier typedef struct Etherhdr Etherhdr;
12*7dd7cddfSDavid du Colombier struct Etherhdr
13*7dd7cddfSDavid du Colombier {
14*7dd7cddfSDavid du Colombier 	uchar	d[6];
15*7dd7cddfSDavid du Colombier 	uchar	s[6];
16*7dd7cddfSDavid du Colombier 	uchar	t[2];
17*7dd7cddfSDavid du Colombier };
18*7dd7cddfSDavid du Colombier 
19*7dd7cddfSDavid du Colombier static void	etherread(void *a);
20*7dd7cddfSDavid du Colombier static void	etherbind(Ipifc *ifc, int argc, char **argv);
21*7dd7cddfSDavid du Colombier static void	etherunbind(Ipifc *ifc);
22*7dd7cddfSDavid du Colombier static void	etherbwrite(Ipifc *ifc, Block *bp, int version, uchar *ip);
23*7dd7cddfSDavid du Colombier static void	etheraddmulti(Ipifc *ifc, uchar *a, uchar *ia);
24*7dd7cddfSDavid du Colombier static void	etherremmulti(Ipifc *ifc, uchar *a, uchar *ia);
25*7dd7cddfSDavid du Colombier static Block*	multicastarp(Fs *f, Arpent *a, uchar *mac);
26*7dd7cddfSDavid du Colombier static void	sendarp(Ipifc *ifc, Arpent *a);
27*7dd7cddfSDavid du Colombier static void	sendgarp(Ipifc *ifc, uchar*);
28*7dd7cddfSDavid du Colombier static int	multicastea(uchar *ea, uchar *ip);
29*7dd7cddfSDavid du Colombier static void	recvarpproc(void*);
30*7dd7cddfSDavid du Colombier 
31*7dd7cddfSDavid du Colombier Medium ethermedium =
32*7dd7cddfSDavid du Colombier {
33*7dd7cddfSDavid du Colombier .name=		"ether",
34*7dd7cddfSDavid du Colombier .hsize=		14,
35*7dd7cddfSDavid du Colombier .minmtu=	60,
36*7dd7cddfSDavid du Colombier .maxmtu=	1514,
37*7dd7cddfSDavid du Colombier .maclen=	6,
38*7dd7cddfSDavid du Colombier .bind=		etherbind,
39*7dd7cddfSDavid du Colombier .unbind=	etherunbind,
40*7dd7cddfSDavid du Colombier .bwrite=	etherbwrite,
41*7dd7cddfSDavid du Colombier .addmulti=	etheraddmulti,
42*7dd7cddfSDavid du Colombier .remmulti=	etherremmulti,
43*7dd7cddfSDavid du Colombier .ares=		arpenter,
44*7dd7cddfSDavid du Colombier .areg=		sendgarp,
45*7dd7cddfSDavid du Colombier };
46*7dd7cddfSDavid du Colombier 
47*7dd7cddfSDavid du Colombier typedef struct	Etherrock Etherrock;
48*7dd7cddfSDavid du Colombier struct Etherrock
49*7dd7cddfSDavid du Colombier {
50*7dd7cddfSDavid du Colombier 	Fs	*f;		/* file system we belong to */
51*7dd7cddfSDavid du Colombier 	Proc	*arpp;		/* arp process */
52*7dd7cddfSDavid du Colombier 	Proc	*readp;		/* reading process */
53*7dd7cddfSDavid du Colombier 	Chan	*mchan;		/* Data channel */
54*7dd7cddfSDavid du Colombier 	Chan	*achan;		/* Arp channel */
55*7dd7cddfSDavid du Colombier 	Chan	*cchan;		/* Control channel */
56*7dd7cddfSDavid du Colombier };
57*7dd7cddfSDavid du Colombier 
58*7dd7cddfSDavid du Colombier /*
59*7dd7cddfSDavid du Colombier  *  ethernet arp request
60*7dd7cddfSDavid du Colombier  */
61*7dd7cddfSDavid du Colombier enum
62*7dd7cddfSDavid du Colombier {
63*7dd7cddfSDavid du Colombier 	ETARP		= 0x0806,
64*7dd7cddfSDavid du Colombier 	ETIP		= 0x0800,
65*7dd7cddfSDavid du Colombier 	ARPREQUEST	= 1,
66*7dd7cddfSDavid du Colombier 	ARPREPLY	= 2,
67*7dd7cddfSDavid du Colombier };
68*7dd7cddfSDavid du Colombier typedef struct Etherarp Etherarp;
69*7dd7cddfSDavid du Colombier struct Etherarp
70*7dd7cddfSDavid du Colombier {
71*7dd7cddfSDavid du Colombier 	uchar	d[6];
72*7dd7cddfSDavid du Colombier 	uchar	s[6];
73*7dd7cddfSDavid du Colombier 	uchar	type[2];
74*7dd7cddfSDavid du Colombier 	uchar	hrd[2];
75*7dd7cddfSDavid du Colombier 	uchar	pro[2];
76*7dd7cddfSDavid du Colombier 	uchar	hln;
77*7dd7cddfSDavid du Colombier 	uchar	pln;
78*7dd7cddfSDavid du Colombier 	uchar	op[2];
79*7dd7cddfSDavid du Colombier 	uchar	sha[6];
80*7dd7cddfSDavid du Colombier 	uchar	spa[4];
81*7dd7cddfSDavid du Colombier 	uchar	tha[6];
82*7dd7cddfSDavid du Colombier 	uchar	tpa[4];
83*7dd7cddfSDavid du Colombier };
84*7dd7cddfSDavid du Colombier 
85*7dd7cddfSDavid du Colombier 
86*7dd7cddfSDavid du Colombier /*
87*7dd7cddfSDavid du Colombier  *  called to bind an IP ifc to an ethernet device
88*7dd7cddfSDavid du Colombier  *  called with ifc wlock'd
89*7dd7cddfSDavid du Colombier  */
90*7dd7cddfSDavid du Colombier static void
91*7dd7cddfSDavid du Colombier etherbind(Ipifc *ifc, int argc, char **argv)
92*7dd7cddfSDavid du Colombier {
93*7dd7cddfSDavid du Colombier 	Chan *mchan, *cchan, *achan;
94*7dd7cddfSDavid du Colombier 	char addr[2*NAMELEN];
95*7dd7cddfSDavid du Colombier 	char dir[2*NAMELEN];
96*7dd7cddfSDavid du Colombier 	char *buf;
97*7dd7cddfSDavid du Colombier 	int fd, cfd, n;
98*7dd7cddfSDavid du Colombier 	char *ptr;
99*7dd7cddfSDavid du Colombier 	Etherrock *er;
100*7dd7cddfSDavid du Colombier 
101*7dd7cddfSDavid du Colombier 	if(argc < 2)
102*7dd7cddfSDavid du Colombier 		error(Ebadarg);
103*7dd7cddfSDavid du Colombier 
104*7dd7cddfSDavid du Colombier 	mchan = cchan = achan = nil;
105*7dd7cddfSDavid du Colombier 	buf = nil;
106*7dd7cddfSDavid du Colombier 	if(waserror()){
107*7dd7cddfSDavid du Colombier 		if(mchan != nil)
108*7dd7cddfSDavid du Colombier 			cclose(mchan);
109*7dd7cddfSDavid du Colombier 		if(cchan != nil)
110*7dd7cddfSDavid du Colombier 			cclose(cchan);
111*7dd7cddfSDavid du Colombier 		if(achan != nil)
112*7dd7cddfSDavid du Colombier 			cclose(achan);
113*7dd7cddfSDavid du Colombier 		if(buf != nil)
114*7dd7cddfSDavid du Colombier 			free(buf);
115*7dd7cddfSDavid du Colombier 		nexterror();
116*7dd7cddfSDavid du Colombier 	}
117*7dd7cddfSDavid du Colombier 
118*7dd7cddfSDavid du Colombier 	/*
119*7dd7cddfSDavid du Colombier 	 *  open ip conversation
120*7dd7cddfSDavid du Colombier 	 *
121*7dd7cddfSDavid du Colombier 	 *  the dial will fail if the type is already open on
122*7dd7cddfSDavid du Colombier 	 *  this device.
123*7dd7cddfSDavid du Colombier 	 */
124*7dd7cddfSDavid du Colombier 	snprint(addr, sizeof(addr), "%s!0x800", argv[2]);
125*7dd7cddfSDavid du Colombier 	fd = kdial(addr, nil, dir, &cfd);
126*7dd7cddfSDavid du Colombier 	if(fd < 0)
127*7dd7cddfSDavid du Colombier 		error("dial 0x800 failed");
128*7dd7cddfSDavid du Colombier 	mchan = commonfdtochan(fd, ORDWR, 0, 1);
129*7dd7cddfSDavid du Colombier 	cchan = commonfdtochan(cfd, ORDWR, 0, 1);
130*7dd7cddfSDavid du Colombier 	kclose(fd);
131*7dd7cddfSDavid du Colombier 	kclose(cfd);
132*7dd7cddfSDavid du Colombier 
133*7dd7cddfSDavid du Colombier 	/*
134*7dd7cddfSDavid du Colombier 	 *  get mac address
135*7dd7cddfSDavid du Colombier 	 */
136*7dd7cddfSDavid du Colombier 	snprint(addr, sizeof(addr), "%s/stats", dir);
137*7dd7cddfSDavid du Colombier 	fd = kopen(addr, OREAD);
138*7dd7cddfSDavid du Colombier 	if(fd < 0)
139*7dd7cddfSDavid du Colombier 		error("can't read ether stats");
140*7dd7cddfSDavid du Colombier 
141*7dd7cddfSDavid du Colombier 	buf = smalloc(512);
142*7dd7cddfSDavid du Colombier 	n = kread(fd, buf, 511);
143*7dd7cddfSDavid du Colombier 	kclose(fd);
144*7dd7cddfSDavid du Colombier 	if(n <= 0)
145*7dd7cddfSDavid du Colombier 		error(Eio);
146*7dd7cddfSDavid du Colombier 	buf[n] = 0;
147*7dd7cddfSDavid du Colombier 
148*7dd7cddfSDavid du Colombier 	ptr = strstr(buf, "addr: ");
149*7dd7cddfSDavid du Colombier 	if(!ptr)
150*7dd7cddfSDavid du Colombier 		error(Eio);
151*7dd7cddfSDavid du Colombier 	ptr += 6;
152*7dd7cddfSDavid du Colombier 
153*7dd7cddfSDavid du Colombier 	parsemac(ifc->mac, ptr, 6);
154*7dd7cddfSDavid du Colombier 
155*7dd7cddfSDavid du Colombier 	/*
156*7dd7cddfSDavid du Colombier  	 *  open arp conversation
157*7dd7cddfSDavid du Colombier 	 */
158*7dd7cddfSDavid du Colombier 	snprint(addr, sizeof(addr), "%s!0x806", argv[2]);
159*7dd7cddfSDavid du Colombier 	fd = kdial(addr, nil, nil, nil);
160*7dd7cddfSDavid du Colombier 	if(fd < 0)
161*7dd7cddfSDavid du Colombier 		error("dial 0x806 failed");
162*7dd7cddfSDavid du Colombier 	achan = commonfdtochan(fd, ORDWR, 0, 1);
163*7dd7cddfSDavid du Colombier 	kclose(fd);
164*7dd7cddfSDavid du Colombier 
165*7dd7cddfSDavid du Colombier 	er = smalloc(sizeof(*er));
166*7dd7cddfSDavid du Colombier 	er->mchan = mchan;
167*7dd7cddfSDavid du Colombier 	er->cchan = cchan;
168*7dd7cddfSDavid du Colombier 	er->achan = achan;
169*7dd7cddfSDavid du Colombier 	er->f = ifc->conv->p->f;
170*7dd7cddfSDavid du Colombier 	ifc->arg = er;
171*7dd7cddfSDavid du Colombier 
172*7dd7cddfSDavid du Colombier 	free(buf);
173*7dd7cddfSDavid du Colombier 	poperror();
174*7dd7cddfSDavid du Colombier 
175*7dd7cddfSDavid du Colombier 	kproc("etherread", etherread, ifc);
176*7dd7cddfSDavid du Colombier 	kproc("recvarpproc", recvarpproc, ifc);
177*7dd7cddfSDavid du Colombier }
178*7dd7cddfSDavid du Colombier 
179*7dd7cddfSDavid du Colombier /*
180*7dd7cddfSDavid du Colombier  *  called with ifc wlock'd
181*7dd7cddfSDavid du Colombier  */
182*7dd7cddfSDavid du Colombier static void
183*7dd7cddfSDavid du Colombier etherunbind(Ipifc *ifc)
184*7dd7cddfSDavid du Colombier {
185*7dd7cddfSDavid du Colombier 	Etherrock *er = ifc->arg;
186*7dd7cddfSDavid du Colombier 
187*7dd7cddfSDavid du Colombier 	if(er->readp)
188*7dd7cddfSDavid du Colombier 		postnote(er->readp, 1, "unbind", 0);
189*7dd7cddfSDavid du Colombier 	if(er->arpp)
190*7dd7cddfSDavid du Colombier 		postnote(er->arpp, 1, "unbind", 0);
191*7dd7cddfSDavid du Colombier 
192*7dd7cddfSDavid du Colombier 	/* wait for readers to die */
193*7dd7cddfSDavid du Colombier 	while(er->arpp != 0 || er->readp != 0)
194*7dd7cddfSDavid du Colombier 		tsleep(&up->sleep, return0, 0, 300);
195*7dd7cddfSDavid du Colombier 
196*7dd7cddfSDavid du Colombier 	if(er->mchan != nil)
197*7dd7cddfSDavid du Colombier 		cclose(er->mchan);
198*7dd7cddfSDavid du Colombier 	if(er->achan != nil)
199*7dd7cddfSDavid du Colombier 		cclose(er->achan);
200*7dd7cddfSDavid du Colombier 	if(er->cchan != nil)
201*7dd7cddfSDavid du Colombier 		cclose(er->cchan);
202*7dd7cddfSDavid du Colombier 
203*7dd7cddfSDavid du Colombier 	free(er);
204*7dd7cddfSDavid du Colombier }
205*7dd7cddfSDavid du Colombier 
206*7dd7cddfSDavid du Colombier /*
207*7dd7cddfSDavid du Colombier  *  called by ipoput with a single block to write with ifc rlock'd
208*7dd7cddfSDavid du Colombier  */
209*7dd7cddfSDavid du Colombier static void
210*7dd7cddfSDavid du Colombier etherbwrite(Ipifc *ifc, Block *bp, int version, uchar *ip)
211*7dd7cddfSDavid du Colombier {
212*7dd7cddfSDavid du Colombier 	Etherhdr *eh;
213*7dd7cddfSDavid du Colombier 	Arpent *a;
214*7dd7cddfSDavid du Colombier 	uchar mac[6];
215*7dd7cddfSDavid du Colombier 	Etherrock *er = ifc->arg;
216*7dd7cddfSDavid du Colombier 
217*7dd7cddfSDavid du Colombier 	/* get mac address of destination */
218*7dd7cddfSDavid du Colombier 	a = arpget(er->f->arp, bp, version, &ethermedium, ip, mac);
219*7dd7cddfSDavid du Colombier 	if(a){
220*7dd7cddfSDavid du Colombier 		/* check for broadcast or multicast */
221*7dd7cddfSDavid du Colombier 		bp = multicastarp(er->f, a, mac);
222*7dd7cddfSDavid du Colombier 		if(bp == nil){
223*7dd7cddfSDavid du Colombier 			sendarp(ifc, a);
224*7dd7cddfSDavid du Colombier 			return;
225*7dd7cddfSDavid du Colombier 		}
226*7dd7cddfSDavid du Colombier 	}
227*7dd7cddfSDavid du Colombier 
228*7dd7cddfSDavid du Colombier 	/* make it a single block with space for the ether header */
229*7dd7cddfSDavid du Colombier 	bp = padblock(bp, ifc->m->hsize);
230*7dd7cddfSDavid du Colombier 	if(bp->next)
231*7dd7cddfSDavid du Colombier 		bp = concatblock(bp);
232*7dd7cddfSDavid du Colombier 	if(BLEN(bp) < ifc->minmtu)
233*7dd7cddfSDavid du Colombier 		bp = adjustblock(bp, ifc->minmtu);
234*7dd7cddfSDavid du Colombier 	eh = (Etherhdr*)bp->rp;
235*7dd7cddfSDavid du Colombier 
236*7dd7cddfSDavid du Colombier 	/* copy in mac addresses and ether type */
237*7dd7cddfSDavid du Colombier 	memmove(eh->s, ifc->mac, sizeof(eh->s));
238*7dd7cddfSDavid du Colombier 	memmove(eh->d, mac, sizeof(eh->d));
239*7dd7cddfSDavid du Colombier 	switch(version){
240*7dd7cddfSDavid du Colombier 	case V4:
241*7dd7cddfSDavid du Colombier 		eh->t[0] = 0x08;
242*7dd7cddfSDavid du Colombier 		eh->t[1] = 0x00;
243*7dd7cddfSDavid du Colombier 		break;
244*7dd7cddfSDavid du Colombier 	case V6:
245*7dd7cddfSDavid du Colombier 		eh->t[0] = 0x86;
246*7dd7cddfSDavid du Colombier 		eh->t[1] = 0xDD;
247*7dd7cddfSDavid du Colombier 		break;
248*7dd7cddfSDavid du Colombier 	}
249*7dd7cddfSDavid du Colombier 
250*7dd7cddfSDavid du Colombier 	devtab[er->mchan->type]->bwrite(er->mchan, bp, 0);
251*7dd7cddfSDavid du Colombier 	ifc->out++;
252*7dd7cddfSDavid du Colombier }
253*7dd7cddfSDavid du Colombier 
254*7dd7cddfSDavid du Colombier /*
255*7dd7cddfSDavid du Colombier  *  process to read from the ethernet
256*7dd7cddfSDavid du Colombier  */
257*7dd7cddfSDavid du Colombier static void
258*7dd7cddfSDavid du Colombier etherread(void *a)
259*7dd7cddfSDavid du Colombier {
260*7dd7cddfSDavid du Colombier 	Ipifc *ifc;
261*7dd7cddfSDavid du Colombier 	Block *bp;
262*7dd7cddfSDavid du Colombier 	Etherrock *er;
263*7dd7cddfSDavid du Colombier 
264*7dd7cddfSDavid du Colombier 	ifc = a;
265*7dd7cddfSDavid du Colombier 	er = ifc->arg;
266*7dd7cddfSDavid du Colombier 	er->readp = up;	/* hide identity under a rock for unbind */
267*7dd7cddfSDavid du Colombier 	if(waserror()){
268*7dd7cddfSDavid du Colombier 		er->readp = 0;
269*7dd7cddfSDavid du Colombier 		pexit("hangup", 1);
270*7dd7cddfSDavid du Colombier 	}
271*7dd7cddfSDavid du Colombier 	for(;;){
272*7dd7cddfSDavid du Colombier 		bp = devtab[er->mchan->type]->bread(er->mchan, ifc->maxmtu, 0);
273*7dd7cddfSDavid du Colombier 		if(!canrlock(ifc)){
274*7dd7cddfSDavid du Colombier 			freeb(bp);
275*7dd7cddfSDavid du Colombier 			continue;
276*7dd7cddfSDavid du Colombier 		}
277*7dd7cddfSDavid du Colombier 		if(waserror()){
278*7dd7cddfSDavid du Colombier 			runlock(ifc);
279*7dd7cddfSDavid du Colombier 			nexterror();
280*7dd7cddfSDavid du Colombier 		}
281*7dd7cddfSDavid du Colombier 		ifc->in++;
282*7dd7cddfSDavid du Colombier 		bp->rp += ifc->m->hsize;
283*7dd7cddfSDavid du Colombier 		if(ifc->lifc == nil)
284*7dd7cddfSDavid du Colombier 			freeb(bp);
285*7dd7cddfSDavid du Colombier 		else
286*7dd7cddfSDavid du Colombier 			ipiput(er->f, ifc->lifc->local, bp);
287*7dd7cddfSDavid du Colombier 		runlock(ifc);
288*7dd7cddfSDavid du Colombier 		poperror();
289*7dd7cddfSDavid du Colombier 	}
290*7dd7cddfSDavid du Colombier }
291*7dd7cddfSDavid du Colombier 
292*7dd7cddfSDavid du Colombier static void
293*7dd7cddfSDavid du Colombier etheraddmulti(Ipifc *ifc, uchar *a, uchar *)
294*7dd7cddfSDavid du Colombier {
295*7dd7cddfSDavid du Colombier 	uchar mac[6];
296*7dd7cddfSDavid du Colombier 	char buf[64];
297*7dd7cddfSDavid du Colombier 	Etherrock *er = ifc->arg;
298*7dd7cddfSDavid du Colombier 
299*7dd7cddfSDavid du Colombier 	multicastea(mac, a);
300*7dd7cddfSDavid du Colombier 	sprint(buf, "addmulti %E", mac);
301*7dd7cddfSDavid du Colombier 	devtab[er->cchan->type]->write(er->cchan, buf, strlen(buf), 0);
302*7dd7cddfSDavid du Colombier }
303*7dd7cddfSDavid du Colombier 
304*7dd7cddfSDavid du Colombier static void
305*7dd7cddfSDavid du Colombier etherremmulti(Ipifc *ifc, uchar *a, uchar *)
306*7dd7cddfSDavid du Colombier {
307*7dd7cddfSDavid du Colombier 	uchar mac[6];
308*7dd7cddfSDavid du Colombier 	char buf[64];
309*7dd7cddfSDavid du Colombier 	Etherrock *er = ifc->arg;
310*7dd7cddfSDavid du Colombier 
311*7dd7cddfSDavid du Colombier 	multicastea(mac, a);
312*7dd7cddfSDavid du Colombier 	sprint(buf, "remmulti %E", mac);
313*7dd7cddfSDavid du Colombier 	devtab[er->cchan->type]->write(er->cchan, buf, strlen(buf), 0);
314*7dd7cddfSDavid du Colombier }
315*7dd7cddfSDavid du Colombier 
316*7dd7cddfSDavid du Colombier /*
317*7dd7cddfSDavid du Colombier  *  send an ethernet arp
318*7dd7cddfSDavid du Colombier  *  (only v4, v6 uses the neighbor discovery, rfc1970)
319*7dd7cddfSDavid du Colombier  */
320*7dd7cddfSDavid du Colombier static void
321*7dd7cddfSDavid du Colombier sendarp(Ipifc *ifc, Arpent *a)
322*7dd7cddfSDavid du Colombier {
323*7dd7cddfSDavid du Colombier 	int n;
324*7dd7cddfSDavid du Colombier 	Block *bp;
325*7dd7cddfSDavid du Colombier 	Etherarp *e;
326*7dd7cddfSDavid du Colombier 	Etherrock *er = ifc->arg;
327*7dd7cddfSDavid du Colombier 
328*7dd7cddfSDavid du Colombier 	/* don't do anything if it's been less than a second since the last */
329*7dd7cddfSDavid du Colombier 	if(msec - a->time < 1000){
330*7dd7cddfSDavid du Colombier 		arprelease(er->f->arp, a);
331*7dd7cddfSDavid du Colombier 		return;
332*7dd7cddfSDavid du Colombier 	}
333*7dd7cddfSDavid du Colombier 
334*7dd7cddfSDavid du Colombier 	/* remove all but the last message */
335*7dd7cddfSDavid du Colombier 	while((bp = a->hold) != nil){
336*7dd7cddfSDavid du Colombier 		if(bp == a->last)
337*7dd7cddfSDavid du Colombier 			break;
338*7dd7cddfSDavid du Colombier 		a->hold = bp->list;
339*7dd7cddfSDavid du Colombier 		freeblist(bp);
340*7dd7cddfSDavid du Colombier 	}
341*7dd7cddfSDavid du Colombier 
342*7dd7cddfSDavid du Colombier 	/* try to keep it around for a second more */
343*7dd7cddfSDavid du Colombier 	a->time = msec;
344*7dd7cddfSDavid du Colombier 	arprelease(er->f->arp, a);
345*7dd7cddfSDavid du Colombier 
346*7dd7cddfSDavid du Colombier 	n = sizeof(Etherarp);
347*7dd7cddfSDavid du Colombier 	if(n < a->type->minmtu)
348*7dd7cddfSDavid du Colombier 		n = a->type->minmtu;
349*7dd7cddfSDavid du Colombier 	bp = allocb(n);
350*7dd7cddfSDavid du Colombier 	memset(bp->rp, 0, n);
351*7dd7cddfSDavid du Colombier 	e = (Etherarp*)bp->rp;
352*7dd7cddfSDavid du Colombier 	memmove(e->tpa, a->ip+IPv4off, sizeof(e->tpa));
353*7dd7cddfSDavid du Colombier 	ipv4local(ifc, e->spa);
354*7dd7cddfSDavid du Colombier 	memmove(e->sha, ifc->mac, sizeof(e->sha));
355*7dd7cddfSDavid du Colombier 	memset(e->d, 0xff, sizeof(e->d));		/* ethernet broadcast */
356*7dd7cddfSDavid du Colombier 	memmove(e->s, ifc->mac, sizeof(e->s));
357*7dd7cddfSDavid du Colombier 
358*7dd7cddfSDavid du Colombier 	hnputs(e->type, ETARP);
359*7dd7cddfSDavid du Colombier 	hnputs(e->hrd, 1);
360*7dd7cddfSDavid du Colombier 	hnputs(e->pro, ETIP);
361*7dd7cddfSDavid du Colombier 	e->hln = sizeof(e->sha);
362*7dd7cddfSDavid du Colombier 	e->pln = sizeof(e->spa);
363*7dd7cddfSDavid du Colombier 	hnputs(e->op, ARPREQUEST);
364*7dd7cddfSDavid du Colombier 	bp->wp += n;
365*7dd7cddfSDavid du Colombier 
366*7dd7cddfSDavid du Colombier 	n = devtab[er->achan->type]->bwrite(er->achan, bp, 0);
367*7dd7cddfSDavid du Colombier 	if(n < 0)
368*7dd7cddfSDavid du Colombier 		print("arp: send: %r\n");
369*7dd7cddfSDavid du Colombier }
370*7dd7cddfSDavid du Colombier 
371*7dd7cddfSDavid du Colombier /*
372*7dd7cddfSDavid du Colombier  *  send a gratuitous arp to refresh arp caches
373*7dd7cddfSDavid du Colombier  */
374*7dd7cddfSDavid du Colombier static void
375*7dd7cddfSDavid du Colombier sendgarp(Ipifc *ifc, uchar *ip)
376*7dd7cddfSDavid du Colombier {
377*7dd7cddfSDavid du Colombier 	int n;
378*7dd7cddfSDavid du Colombier 	Block *bp;
379*7dd7cddfSDavid du Colombier 	Etherarp *e;
380*7dd7cddfSDavid du Colombier 	Etherrock *er = ifc->arg;
381*7dd7cddfSDavid du Colombier 
382*7dd7cddfSDavid du Colombier 	/* don't arp for our initial non address */
383*7dd7cddfSDavid du Colombier 	if(ipcmp(ip, IPnoaddr) == 0)
384*7dd7cddfSDavid du Colombier 		return;
385*7dd7cddfSDavid du Colombier 
386*7dd7cddfSDavid du Colombier 	n = sizeof(Etherarp);
387*7dd7cddfSDavid du Colombier 	if(n < ethermedium.minmtu)
388*7dd7cddfSDavid du Colombier 		n = ethermedium.minmtu;
389*7dd7cddfSDavid du Colombier 	bp = allocb(n);
390*7dd7cddfSDavid du Colombier 	memset(bp->rp, 0, n);
391*7dd7cddfSDavid du Colombier 	e = (Etherarp*)bp->rp;
392*7dd7cddfSDavid du Colombier 	memmove(e->tpa, ip+IPv4off, sizeof(e->tpa));
393*7dd7cddfSDavid du Colombier 	memmove(e->spa, ip+IPv4off, sizeof(e->spa));
394*7dd7cddfSDavid du Colombier 	memmove(e->sha, ifc->mac, sizeof(e->sha));
395*7dd7cddfSDavid du Colombier 	memset(e->d, 0xff, sizeof(e->d));		/* ethernet broadcast */
396*7dd7cddfSDavid du Colombier 	memmove(e->s, ifc->mac, sizeof(e->s));
397*7dd7cddfSDavid du Colombier 
398*7dd7cddfSDavid du Colombier 	hnputs(e->type, ETARP);
399*7dd7cddfSDavid du Colombier 	hnputs(e->hrd, 1);
400*7dd7cddfSDavid du Colombier 	hnputs(e->pro, ETIP);
401*7dd7cddfSDavid du Colombier 	e->hln = sizeof(e->sha);
402*7dd7cddfSDavid du Colombier 	e->pln = sizeof(e->spa);
403*7dd7cddfSDavid du Colombier 	hnputs(e->op, ARPREQUEST);
404*7dd7cddfSDavid du Colombier 	bp->wp += n;
405*7dd7cddfSDavid du Colombier 
406*7dd7cddfSDavid du Colombier 	n = devtab[er->achan->type]->bwrite(er->achan, bp, 0);
407*7dd7cddfSDavid du Colombier 	if(n < 0)
408*7dd7cddfSDavid du Colombier 		print("garp: send: %r\n");
409*7dd7cddfSDavid du Colombier }
410*7dd7cddfSDavid du Colombier 
411*7dd7cddfSDavid du Colombier static void
412*7dd7cddfSDavid du Colombier recvarp(Ipifc *ifc)
413*7dd7cddfSDavid du Colombier {
414*7dd7cddfSDavid du Colombier 	int n;
415*7dd7cddfSDavid du Colombier 	Block *ebp, *rbp;
416*7dd7cddfSDavid du Colombier 	Etherarp *e, *r;
417*7dd7cddfSDavid du Colombier 	uchar ip[IPaddrlen];
418*7dd7cddfSDavid du Colombier 	Etherrock *er = ifc->arg;
419*7dd7cddfSDavid du Colombier 
420*7dd7cddfSDavid du Colombier 	ebp = devtab[er->achan->type]->bread(er->achan, ifc->maxmtu, 0);
421*7dd7cddfSDavid du Colombier 	if(ebp == nil) {
422*7dd7cddfSDavid du Colombier 		print("arp: rcv: %r\n");
423*7dd7cddfSDavid du Colombier 		return;
424*7dd7cddfSDavid du Colombier 	}
425*7dd7cddfSDavid du Colombier 
426*7dd7cddfSDavid du Colombier 	e = (Etherarp*)ebp->rp;
427*7dd7cddfSDavid du Colombier 	switch(nhgets(e->op)) {
428*7dd7cddfSDavid du Colombier 	default:
429*7dd7cddfSDavid du Colombier 		break;
430*7dd7cddfSDavid du Colombier 
431*7dd7cddfSDavid du Colombier 	case ARPREPLY:
432*7dd7cddfSDavid du Colombier 		arpenter(er->f, V4, e->spa, e->sha, sizeof(e->sha), 0);
433*7dd7cddfSDavid du Colombier 		break;
434*7dd7cddfSDavid du Colombier 
435*7dd7cddfSDavid du Colombier 	case ARPREQUEST:
436*7dd7cddfSDavid du Colombier 		/* don't answer arps till we know who we are */
437*7dd7cddfSDavid du Colombier 		if(ifc->lifc == 0)
438*7dd7cddfSDavid du Colombier 			break;
439*7dd7cddfSDavid du Colombier 
440*7dd7cddfSDavid du Colombier 		/* check for someone that think's they're me */
441*7dd7cddfSDavid du Colombier 		v4tov6(ip, e->spa);
442*7dd7cddfSDavid du Colombier 		if(iplocalonifc(ifc, ip) || ipproxyifc(er->f, ifc, ip)){
443*7dd7cddfSDavid du Colombier 			if(memcmp(e->sha, ifc->mac, sizeof(e->sha)) != 0)
444*7dd7cddfSDavid du Colombier 				print("arp: 0x%E also has ip addr %V\n", e->sha, e->spa);
445*7dd7cddfSDavid du Colombier 		} else {
446*7dd7cddfSDavid du Colombier 			if(memcmp(e->sha, ifc->mac, sizeof(e->sha)) == 0){
447*7dd7cddfSDavid du Colombier 				print("arp: %V also has ether addr %E\n", e->spa, e->sha);
448*7dd7cddfSDavid du Colombier 				break;
449*7dd7cddfSDavid du Colombier 			}
450*7dd7cddfSDavid du Colombier 		}
451*7dd7cddfSDavid du Colombier 
452*7dd7cddfSDavid du Colombier 		/* refresh what we know about sender */
453*7dd7cddfSDavid du Colombier 		arpenter(er->f, V4, e->spa, e->sha, sizeof(e->sha), 1);
454*7dd7cddfSDavid du Colombier 
455*7dd7cddfSDavid du Colombier 		/* answer only requests for our address or systems we're proxying for */
456*7dd7cddfSDavid du Colombier 		v4tov6(ip, e->tpa);
457*7dd7cddfSDavid du Colombier 		if(!iplocalonifc(ifc, ip))
458*7dd7cddfSDavid du Colombier 		if(!ipproxyifc(er->f, ifc, ip))
459*7dd7cddfSDavid du Colombier 			break;
460*7dd7cddfSDavid du Colombier 
461*7dd7cddfSDavid du Colombier /* print("arp: rem %I %E (for %I)\n", e->spa, e->sha, e->tpa); /**/
462*7dd7cddfSDavid du Colombier 
463*7dd7cddfSDavid du Colombier 		n = sizeof(Etherarp);
464*7dd7cddfSDavid du Colombier 		if(n < ifc->minmtu)
465*7dd7cddfSDavid du Colombier 			n = ifc->minmtu;
466*7dd7cddfSDavid du Colombier 		rbp = allocb(n);
467*7dd7cddfSDavid du Colombier 		r = (Etherarp*)rbp->rp;
468*7dd7cddfSDavid du Colombier 		memset(r, 0, sizeof(Etherarp));
469*7dd7cddfSDavid du Colombier 		hnputs(r->type, ETARP);
470*7dd7cddfSDavid du Colombier 		hnputs(r->hrd, 1);
471*7dd7cddfSDavid du Colombier 		hnputs(r->pro, ETIP);
472*7dd7cddfSDavid du Colombier 		r->hln = sizeof(r->sha);
473*7dd7cddfSDavid du Colombier 		r->pln = sizeof(r->spa);
474*7dd7cddfSDavid du Colombier 		hnputs(r->op, ARPREPLY);
475*7dd7cddfSDavid du Colombier 		memmove(r->tha, e->sha, sizeof(r->tha));
476*7dd7cddfSDavid du Colombier 		memmove(r->tpa, e->spa, sizeof(r->tpa));
477*7dd7cddfSDavid du Colombier 		memmove(r->sha, ifc->mac, sizeof(r->sha));
478*7dd7cddfSDavid du Colombier 		memmove(r->spa, e->tpa, sizeof(r->spa));
479*7dd7cddfSDavid du Colombier 		memmove(r->d, e->sha, sizeof(r->d));
480*7dd7cddfSDavid du Colombier 		memmove(r->s, ifc->mac, sizeof(r->s));
481*7dd7cddfSDavid du Colombier 		rbp->wp += n;
482*7dd7cddfSDavid du Colombier 
483*7dd7cddfSDavid du Colombier 		n = devtab[er->achan->type]->bwrite(er->achan, rbp, 0);
484*7dd7cddfSDavid du Colombier 		if(n < 0)
485*7dd7cddfSDavid du Colombier 			print("arp: write: %r\n");
486*7dd7cddfSDavid du Colombier 	}
487*7dd7cddfSDavid du Colombier 	freeb(ebp);
488*7dd7cddfSDavid du Colombier }
489*7dd7cddfSDavid du Colombier 
490*7dd7cddfSDavid du Colombier static void
491*7dd7cddfSDavid du Colombier recvarpproc(void *v)
492*7dd7cddfSDavid du Colombier {
493*7dd7cddfSDavid du Colombier 	Ipifc *ifc = v;
494*7dd7cddfSDavid du Colombier 	Etherrock *er = ifc->arg;
495*7dd7cddfSDavid du Colombier 
496*7dd7cddfSDavid du Colombier 	er->arpp = up;
497*7dd7cddfSDavid du Colombier 	if(waserror()){
498*7dd7cddfSDavid du Colombier 		er->arpp = 0;
499*7dd7cddfSDavid du Colombier 		pexit("hangup", 1);
500*7dd7cddfSDavid du Colombier 	}
501*7dd7cddfSDavid du Colombier 	for(;;)
502*7dd7cddfSDavid du Colombier 		recvarp(ifc);
503*7dd7cddfSDavid du Colombier }
504*7dd7cddfSDavid du Colombier 
505*7dd7cddfSDavid du Colombier static int
506*7dd7cddfSDavid du Colombier multicastea(uchar *ea, uchar *ip)
507*7dd7cddfSDavid du Colombier {
508*7dd7cddfSDavid du Colombier 	int x;
509*7dd7cddfSDavid du Colombier 
510*7dd7cddfSDavid du Colombier 	switch(x = ipismulticast(ip)){
511*7dd7cddfSDavid du Colombier 	case V4:
512*7dd7cddfSDavid du Colombier 		ea[0] = 0x01;
513*7dd7cddfSDavid du Colombier 		ea[1] = 0x00;
514*7dd7cddfSDavid du Colombier 		ea[2] = 0x5e;
515*7dd7cddfSDavid du Colombier 		ea[3] = ip[13] & 0x7f;
516*7dd7cddfSDavid du Colombier 		ea[4] = ip[14];
517*7dd7cddfSDavid du Colombier 		ea[5] = ip[15];
518*7dd7cddfSDavid du Colombier 		break;
519*7dd7cddfSDavid du Colombier 	case V6:
520*7dd7cddfSDavid du Colombier 		ea[0] = 0x33;
521*7dd7cddfSDavid du Colombier 		ea[1] = 0x33;
522*7dd7cddfSDavid du Colombier 		ea[2] = ip[12];
523*7dd7cddfSDavid du Colombier 		ea[3] = ip[13];
524*7dd7cddfSDavid du Colombier 		ea[4] = ip[14];
525*7dd7cddfSDavid du Colombier 		ea[5] = ip[15];
526*7dd7cddfSDavid du Colombier 		break;
527*7dd7cddfSDavid du Colombier 	}
528*7dd7cddfSDavid du Colombier 	return x;
529*7dd7cddfSDavid du Colombier }
530*7dd7cddfSDavid du Colombier 
531*7dd7cddfSDavid du Colombier /*
532*7dd7cddfSDavid du Colombier  *  fill in an arp entry for broadcast or multicast
533*7dd7cddfSDavid du Colombier  *  addresses
534*7dd7cddfSDavid du Colombier  */
535*7dd7cddfSDavid du Colombier static Block*
536*7dd7cddfSDavid du Colombier multicastarp(Fs *f, Arpent *a, uchar *mac)
537*7dd7cddfSDavid du Colombier {
538*7dd7cddfSDavid du Colombier 	/* is it broadcast? */
539*7dd7cddfSDavid du Colombier 	switch(ipforme(f, a->ip)){
540*7dd7cddfSDavid du Colombier 	case Runi:
541*7dd7cddfSDavid du Colombier 		return nil;
542*7dd7cddfSDavid du Colombier 	case Rbcast:
543*7dd7cddfSDavid du Colombier 		memset(mac, 0xff, 6);
544*7dd7cddfSDavid du Colombier 		return arpresolve(f->arp, a, &ethermedium, mac);
545*7dd7cddfSDavid du Colombier 	default:
546*7dd7cddfSDavid du Colombier 		break;
547*7dd7cddfSDavid du Colombier 	}
548*7dd7cddfSDavid du Colombier 
549*7dd7cddfSDavid du Colombier 	/* if multicast, fill in mac */
550*7dd7cddfSDavid du Colombier 	switch(multicastea(mac, a->ip)){
551*7dd7cddfSDavid du Colombier 	case V4:
552*7dd7cddfSDavid du Colombier 	case V6:
553*7dd7cddfSDavid du Colombier 		return arpresolve(f->arp, a, &ethermedium, mac);
554*7dd7cddfSDavid du Colombier 	}
555*7dd7cddfSDavid du Colombier 
556*7dd7cddfSDavid du Colombier 	/* let arp take care of it */
557*7dd7cddfSDavid du Colombier 	return nil;
558*7dd7cddfSDavid du Colombier }
559*7dd7cddfSDavid du Colombier 
560*7dd7cddfSDavid du Colombier void
561*7dd7cddfSDavid du Colombier ethermediumlink(void)
562*7dd7cddfSDavid du Colombier {
563*7dd7cddfSDavid du Colombier 	addipmedium(&ethermedium);
564*7dd7cddfSDavid du Colombier }
565