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