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