xref: /plan9/sys/src/9/ip/ethermedium.c (revision 9a747e4fd48b9f4522c70c07e8f882a15030f964)
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 
10 typedef struct Etherhdr Etherhdr;
11 struct Etherhdr
12 {
13 	uchar	d[6];
14 	uchar	s[6];
15 	uchar	t[2];
16 };
17 
18 static void	etherread(void *a);
19 static void	etherbind(Ipifc *ifc, int argc, char **argv);
20 static void	etherunbind(Ipifc *ifc);
21 static void	etherbwrite(Ipifc *ifc, Block *bp, int version, uchar *ip);
22 static void	etheraddmulti(Ipifc *ifc, uchar *a, uchar *ia);
23 static void	etherremmulti(Ipifc *ifc, uchar *a, uchar *ia);
24 static Block*	multicastarp(Fs *f, Arpent *a, Medium*, uchar *mac);
25 static void	sendarp(Ipifc *ifc, Arpent *a);
26 static void	sendgarp(Ipifc *ifc, uchar*);
27 static int	multicastea(uchar *ea, uchar *ip);
28 static void	recvarpproc(void*);
29 
30 Medium ethermedium =
31 {
32 .name=		"ether",
33 .hsize=		14,
34 .minmtu=	60,
35 .maxmtu=	1514,
36 .maclen=	6,
37 .bind=		etherbind,
38 .unbind=	etherunbind,
39 .bwrite=	etherbwrite,
40 .addmulti=	etheraddmulti,
41 .remmulti=	etherremmulti,
42 .ares=		arpenter,
43 .areg=		sendgarp,
44 };
45 
46 Medium gbemedium =
47 {
48 .name=		"gbe",
49 .hsize=		14,
50 .minmtu=	60,
51 .maxmtu=	9014,
52 .maclen=	6,
53 .bind=		etherbind,
54 .unbind=	etherunbind,
55 .bwrite=	etherbwrite,
56 .addmulti=	etheraddmulti,
57 .remmulti=	etherremmulti,
58 .ares=		arpenter,
59 .areg=		sendgarp,
60 };
61 
62 typedef struct	Etherrock Etherrock;
63 struct Etherrock
64 {
65 	Fs	*f;		/* file system we belong to */
66 	Proc	*arpp;		/* arp process */
67 	Proc	*readp;		/* reading process */
68 	Chan	*mchan;		/* Data channel */
69 	Chan	*achan;		/* Arp channel */
70 	Chan	*cchan;		/* Control channel */
71 };
72 
73 /*
74  *  ethernet arp request
75  */
76 enum
77 {
78 	ETARP		= 0x0806,
79 	ETIP		= 0x0800,
80 	ARPREQUEST	= 1,
81 	ARPREPLY	= 2,
82 };
83 typedef struct Etherarp Etherarp;
84 struct Etherarp
85 {
86 	uchar	d[6];
87 	uchar	s[6];
88 	uchar	type[2];
89 	uchar	hrd[2];
90 	uchar	pro[2];
91 	uchar	hln;
92 	uchar	pln;
93 	uchar	op[2];
94 	uchar	sha[6];
95 	uchar	spa[4];
96 	uchar	tha[6];
97 	uchar	tpa[4];
98 };
99 
100 
101 /*
102  *  called to bind an IP ifc to an ethernet device
103  *  called with ifc wlock'd
104  */
105 static void
106 etherbind(Ipifc *ifc, int argc, char **argv)
107 {
108 	Chan *mchan, *cchan, *achan;
109 	char addr[Maxpath];
110 	char dir[Maxpath];
111 	char *buf;
112 	int n;
113 	char *ptr;
114 	Etherrock *er;
115 
116 	if(argc < 2)
117 		error(Ebadarg);
118 
119 	mchan = cchan = achan = nil;
120 	buf = nil;
121 	if(waserror()){
122 		if(mchan != nil)
123 			cclose(mchan);
124 		if(cchan != nil)
125 			cclose(cchan);
126 		if(achan != nil)
127 			cclose(achan);
128 		if(buf != nil)
129 			free(buf);
130 		nexterror();
131 	}
132 
133 	/*
134 	 *  open ip conversation
135 	 *
136 	 *  the dial will fail if the type is already open on
137 	 *  this device.
138 	 */
139 	snprint(addr, sizeof(addr), "%s!0x800", argv[2]);
140 	mchan = chandial(addr, nil, dir, &cchan);
141 
142 	/*
143 	 *  get mac address
144 	 */
145 	snprint(addr, sizeof(addr), "%s/stats", dir);
146 	buf = smalloc(512);
147 	achan = namec(addr, Aopen, OREAD, 0);
148 	n = devtab[achan->type]->read(achan, buf, 511, 0);
149 	cclose(achan);
150 	buf[n] = 0;
151 
152 	ptr = strstr(buf, "addr: ");
153 	if(!ptr)
154 		error(Eio);
155 	ptr += 6;
156 
157 	parsemac(ifc->mac, ptr, 6);
158 
159 	/*
160  	 *  open arp conversation
161 	 */
162 	snprint(addr, sizeof(addr), "%s!0x806", argv[2]);
163 	achan = chandial(addr, nil, nil, nil);
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, ifc->m, ip, mac);
219 	if(a){
220 		/* check for broadcast or multicast */
221 		bp = multicastarp(er->f, a, ifc->m, 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, 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 < ifc->m->minmtu)
388 		n = ifc->m->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 		if(iplocalonifc(ifc, ip) || ipproxyifc(er->f, ifc, ip)){
433 			if(memcmp(e->sha, ifc->mac, sizeof(e->sha)) != 0){
434 				print("arprep: 0x%E/0x%E also has ip addr %V\n",
435 					e->s, e->sha, e->spa);
436 				break;
437 			}
438 		}
439 		arpenter(er->f, V4, e->spa, e->sha, sizeof(e->sha), 0);
440 		break;
441 
442 	case ARPREQUEST:
443 		/* don't answer arps till we know who we are */
444 		if(ifc->lifc == 0)
445 			break;
446 
447 		/* check for someone that think's they're me */
448 		v4tov6(ip, e->spa);
449 		if(iplocalonifc(ifc, ip) || ipproxyifc(er->f, ifc, ip)){
450 			if(memcmp(e->sha, ifc->mac, sizeof(e->sha)) != 0){
451 				print("arpreq: 0x%E/0x%E also has ip addr %V\n",
452 					e->s, e->sha, e->spa);
453 				break;
454 			}
455 		} else {
456 			if(memcmp(e->sha, ifc->mac, sizeof(e->sha)) == 0){
457 				print("arp: %V also has ether addr %E\n", e->spa, e->sha);
458 				break;
459 			}
460 		}
461 
462 		/* refresh what we know about sender */
463 		arpenter(er->f, V4, e->spa, e->sha, sizeof(e->sha), 1);
464 
465 		/* answer only requests for our address or systems we're proxying for */
466 		v4tov6(ip, e->tpa);
467 		if(!iplocalonifc(ifc, ip))
468 		if(!ipproxyifc(er->f, ifc, ip))
469 			break;
470 
471 /* print("arp: rem %I %E (for %I)\n", e->spa, e->sha, e->tpa); /**/
472 
473 		n = sizeof(Etherarp);
474 		if(n < ifc->minmtu)
475 			n = ifc->minmtu;
476 		rbp = allocb(n);
477 		r = (Etherarp*)rbp->rp;
478 		memset(r, 0, sizeof(Etherarp));
479 		hnputs(r->type, ETARP);
480 		hnputs(r->hrd, 1);
481 		hnputs(r->pro, ETIP);
482 		r->hln = sizeof(r->sha);
483 		r->pln = sizeof(r->spa);
484 		hnputs(r->op, ARPREPLY);
485 		memmove(r->tha, e->sha, sizeof(r->tha));
486 		memmove(r->tpa, e->spa, sizeof(r->tpa));
487 		memmove(r->sha, ifc->mac, sizeof(r->sha));
488 		memmove(r->spa, e->tpa, sizeof(r->spa));
489 		memmove(r->d, e->sha, sizeof(r->d));
490 		memmove(r->s, ifc->mac, sizeof(r->s));
491 		rbp->wp += n;
492 
493 		n = devtab[er->achan->type]->bwrite(er->achan, rbp, 0);
494 		if(n < 0)
495 			print("arp: write: %r\n");
496 	}
497 	freeb(ebp);
498 }
499 
500 static void
501 recvarpproc(void *v)
502 {
503 	Ipifc *ifc = v;
504 	Etherrock *er = ifc->arg;
505 
506 	er->arpp = up;
507 	if(waserror()){
508 		er->arpp = 0;
509 		pexit("hangup", 1);
510 	}
511 	for(;;)
512 		recvarp(ifc);
513 }
514 
515 static int
516 multicastea(uchar *ea, uchar *ip)
517 {
518 	int x;
519 
520 	switch(x = ipismulticast(ip)){
521 	case V4:
522 		ea[0] = 0x01;
523 		ea[1] = 0x00;
524 		ea[2] = 0x5e;
525 		ea[3] = ip[13] & 0x7f;
526 		ea[4] = ip[14];
527 		ea[5] = ip[15];
528 		break;
529 	case V6:
530 		ea[0] = 0x33;
531 		ea[1] = 0x33;
532 		ea[2] = ip[12];
533 		ea[3] = ip[13];
534 		ea[4] = ip[14];
535 		ea[5] = ip[15];
536 		break;
537 	}
538 	return x;
539 }
540 
541 /*
542  *  fill in an arp entry for broadcast or multicast
543  *  addresses
544  */
545 static Block*
546 multicastarp(Fs *f, Arpent *a, Medium *medium, uchar *mac)
547 {
548 	/* is it broadcast? */
549 	switch(ipforme(f, a->ip)){
550 	case Runi:
551 		return nil;
552 	case Rbcast:
553 		memset(mac, 0xff, 6);
554 		return arpresolve(f->arp, a, medium, mac);
555 	default:
556 		break;
557 	}
558 
559 	/* if multicast, fill in mac */
560 	switch(multicastea(mac, a->ip)){
561 	case V4:
562 	case V6:
563 		return arpresolve(f->arp, a, medium, mac);
564 	}
565 
566 	/* let arp take care of it */
567 	return nil;
568 }
569 
570 void
571 ethermediumlink(void)
572 {
573 	addipmedium(&ethermedium);
574 	addipmedium(&gbemedium);
575 }
576