xref: /plan9-contrib/sys/src/9/ip/ethermedium.c (revision 3ff48bf5ed603850fcd251ddf13025d23d693782)
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 "ipv6.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	etherread4(void *a);
20 static void	etherread6(void *a);
21 static void	etherbind(Ipifc *ifc, int argc, char **argv);
22 static void	etherunbind(Ipifc *ifc);
23 static void	etherbwrite(Ipifc *ifc, Block *bp, int version, uchar *ip);
24 static void	etheraddmulti(Ipifc *ifc, uchar *a, uchar *ia);
25 static void	etherremmulti(Ipifc *ifc, uchar *a, uchar *ia);
26 static Block*	multicastarp(Fs *f, Arpent *a, Medium*, uchar *mac);
27 static void	sendarp(Ipifc *ifc, Arpent *a);
28 static void	sendgarp(Ipifc *ifc, uchar*);
29 static int	multicastea(uchar *ea, uchar *ip);
30 static void	recvarpproc(void*);
31 static void	resolveaddr6(Ipifc *ifc, Arpent *a);
32 static void	etherpref2addr(uchar *pref, uchar *ea);
33 
34 Medium ethermedium =
35 {
36 .name=		"ether",
37 .hsize=		14,
38 .minmtu=	60,
39 .maxmtu=	1514,
40 .maclen=	6,
41 .bind=		etherbind,
42 .unbind=	etherunbind,
43 .bwrite=	etherbwrite,
44 .addmulti=	etheraddmulti,
45 .remmulti=	etherremmulti,
46 .ares=		arpenter,
47 .areg=		sendgarp,
48 .pref2addr=	etherpref2addr,
49 };
50 
51 Medium gbemedium =
52 {
53 .name=		"gbe",
54 .hsize=		14,
55 .minmtu=	60,
56 .maxmtu=	9014,
57 .maclen=	6,
58 .bind=		etherbind,
59 .unbind=	etherunbind,
60 .bwrite=	etherbwrite,
61 .addmulti=	etheraddmulti,
62 .remmulti=	etherremmulti,
63 .ares=		arpenter,
64 .areg=		sendgarp,
65 .pref2addr=	etherpref2addr,
66 };
67 
68 typedef struct	Etherrock Etherrock;
69 struct Etherrock
70 {
71 	Fs	*f;		/* file system we belong to */
72 	Proc	*arpp;		/* arp process */
73 	Proc	*read4p;	/* reading process (v4)*/
74 	Proc	*read6p;	/* reading process (v6)*/
75 	Chan	*mchan4;	/* Data channel for v4 */
76 	Chan	*achan;		/* Arp channel */
77 	Chan	*cchan4;	/* Control channel for v4 */
78 	Chan	*mchan6;	/* Data channel for v6 */
79 	Chan	*cchan6;	/* Control channel for v6 */
80 };
81 
82 /*
83  *  ethernet arp request
84  */
85 enum
86 {
87 	ETARP		= 0x0806,
88 	ETIP4		= 0x0800,
89 	ETIP6		= 0x86DD,
90 	ARPREQUEST	= 1,
91 	ARPREPLY	= 2,
92 };
93 
94 typedef struct Etherarp Etherarp;
95 struct Etherarp
96 {
97 	uchar	d[6];
98 	uchar	s[6];
99 	uchar	type[2];
100 	uchar	hrd[2];
101 	uchar	pro[2];
102 	uchar	hln;
103 	uchar	pln;
104 	uchar	op[2];
105 	uchar	sha[6];
106 	uchar	spa[4];
107 	uchar	tha[6];
108 	uchar	tpa[4];
109 };
110 
111 static char *nbmsg = "nonblocking";
112 
113 /*
114  *  called to bind an IP ifc to an ethernet device
115  *  called with ifc wlock'd
116  */
117 static void
118 etherbind(Ipifc *ifc, int argc, char **argv)
119 {
120 	Chan *mchan4, *cchan4, *achan, *mchan6, *cchan6, *schan;
121 	char addr[Maxpath];	//char addr[2*KNAMELEN];
122 	char dir[Maxpath];	//char dir[2*KNAMELEN];
123 	char *buf;
124 	int n;
125 	char *ptr;
126 	Etherrock *er;
127 
128 	if(argc < 2)
129 		error(Ebadarg);
130 
131 	mchan4 = cchan4 = achan = mchan6 = cchan6 = nil;
132 	buf = nil;
133 	if(waserror()){
134 		if(mchan4 != nil)
135 			cclose(mchan4);
136 		if(cchan4 != nil)
137 			cclose(cchan4);
138 		if(achan != nil)
139 			cclose(achan);
140 		if(mchan6 != nil)
141 			cclose(mchan6);
142 		if(cchan6 != nil)
143 			cclose(cchan6);
144 		if(buf != nil)
145 			free(buf);
146 		nexterror();
147 	}
148 
149 	/*
150 	 *  open ip converstation
151 	 *
152 	 *  the dial will fail if the type is already open on
153 	 *  this device.
154 	 */
155 	snprint(addr, sizeof(addr), "%s!0x800", argv[2]);
156 	mchan4 = chandial(addr, nil, dir, &cchan4);
157 
158 	/*
159 	 *  make it non-blocking
160 	 */
161 	devtab[cchan4->type]->write(cchan4, nbmsg, strlen(nbmsg), 0);
162 
163 	/*
164 	 *  get mac address
165 	 */
166 	snprint(addr, sizeof(addr), "%s/stats", dir);
167 	buf = smalloc(512);
168 	schan = namec(addr, Aopen, OREAD, 0);
169 	if(waserror()){
170 		cclose(schan);
171 		nexterror();
172 	}
173 	n = devtab[schan->type]->read(schan, buf, 511, 0);
174 	cclose(schan);
175 	poperror();
176 	buf[n] = 0;
177 
178 	ptr = strstr(buf, "addr: ");
179 	if(!ptr)
180 		error(Eio);
181 	ptr += 6;
182 
183 	parsemac(ifc->mac, ptr, 6);
184 
185 	/*
186  	 *  open arp conversation
187 	 */
188 	snprint(addr, sizeof(addr), "%s!0x806", argv[2]);
189 	achan = chandial(addr, nil, nil, nil);
190 
191 	/*
192 	 *  open ip conversation
193 	 *
194 	 *  the dial will fail if the type is already open on
195 	 *  this device.
196 	 */
197 	snprint(addr, sizeof(addr), "%s!0x86DD", argv[2]);
198 	mchan6 = chandial(addr, nil, dir, &cchan6);
199 
200 	/*
201 	 *  make it non-blocking
202 	 */
203 	devtab[cchan6->type]->write(cchan6, nbmsg, strlen(nbmsg), 0);
204 
205 	er = smalloc(sizeof(*er));
206 	er->mchan4 = mchan4;
207 	er->cchan4 = cchan4;
208 	er->achan = achan;
209 	er->mchan6 = mchan6;
210 	er->cchan6 = cchan6;
211 	er->f = ifc->conv->p->f;
212 	ifc->arg = er;
213 
214 	free(buf);
215 	poperror();
216 
217 	kproc("etherread4", etherread4, ifc);
218 	kproc("recvarpproc", recvarpproc, ifc);
219 	kproc("etherread6", etherread6, ifc);
220 }
221 
222 /*
223  *  called with ifc wlock'd
224  */
225 static void
226 etherunbind(Ipifc *ifc)
227 {
228 	Etherrock *er = ifc->arg;
229 
230 	if(er->read4p)
231 		postnote(er->read4p, 1, "unbind", 0);
232 	if(er->read6p)
233 		postnote(er->read6p, 1, "unbind", 0);
234 	if(er->arpp)
235 		postnote(er->arpp, 1, "unbind", 0);
236 
237 	/* wait for readers to die */
238 	while(er->arpp != 0 || er->read4p != 0 || er->read6p != 0)
239 		tsleep(&up->sleep, return0, 0, 300);
240 
241 	if(er->mchan4 != nil)
242 		cclose(er->mchan4);
243 	if(er->achan != nil)
244 		cclose(er->achan);
245 	if(er->cchan4 != nil)
246 		cclose(er->cchan4);
247 	if(er->mchan6 != nil)
248 		cclose(er->mchan6);
249 	if(er->cchan6 != nil)
250 		cclose(er->cchan6);
251 
252 	free(er);
253 }
254 
255 /*
256  *  called by ipoput with a single block to write with ifc rlock'd
257  */
258 static void
259 etherbwrite(Ipifc *ifc, Block *bp, int version, uchar *ip)
260 {
261 	Etherhdr *eh;
262 	Arpent *a;
263 	uchar mac[6];
264 	Etherrock *er = ifc->arg;
265 
266 	/* get mac address of destination */
267 	a = arpget(er->f->arp, bp, version, ifc, ip, mac);
268 	if(a){
269 		/* check for broadcast or multicast */
270 		bp = multicastarp(er->f, a, ifc->m, mac);
271 		if(bp==nil){
272 			switch(version){
273 			case V4:
274 				sendarp(ifc, a);
275 				break;
276 			case V6:
277 				resolveaddr6(ifc, a);
278 				break;
279 			default:
280 				panic("etherbwrite: version %d", version);
281 			}
282 			return;
283 		}
284 	}
285 
286 	/* make it a single block with space for the ether header */
287 	bp = padblock(bp, ifc->m->hsize);
288 	if(bp->next)
289 		bp = concatblock(bp);
290 	if(BLEN(bp) < ifc->minmtu)
291 		bp = adjustblock(bp, ifc->minmtu);
292 	eh = (Etherhdr*)bp->rp;
293 
294 	/* copy in mac addresses and ether type */
295 	memmove(eh->s, ifc->mac, sizeof(eh->s));
296 	memmove(eh->d, mac, sizeof(eh->d));
297 
298  	switch(version){
299 	case V4:
300 		eh->t[0] = 0x08;
301 		eh->t[1] = 0x00;
302 		devtab[er->mchan4->type]->bwrite(er->mchan4, bp, 0);
303 		break;
304 	case V6:
305 		eh->t[0] = 0x86;
306 		eh->t[1] = 0xDD;
307 		devtab[er->mchan6->type]->bwrite(er->mchan6, bp, 0);
308 		break;
309 	default:
310 		panic("etherbwrite2: version %d", version);
311 	}
312 	ifc->out++;
313 }
314 
315 
316 /*
317  *  process to read from the ethernet
318  */
319 static void
320 etherread4(void *a)
321 {
322 	Ipifc *ifc;
323 	Block *bp;
324 	Etherrock *er;
325 
326 	ifc = a;
327 	er = ifc->arg;
328 	er->read4p = up;	/* hide identity under a rock for unbind */
329 	if(waserror()){
330 		er->read4p = 0;
331 		pexit("hangup", 1);
332 	}
333 	for(;;){
334 		bp = devtab[er->mchan4->type]->bread(er->mchan4, ifc->maxmtu, 0);
335 		if(!canrlock(ifc)){
336 			freeb(bp);
337 			continue;
338 		}
339 		if(waserror()){
340 			runlock(ifc);
341 			nexterror();
342 		}
343 		ifc->in++;
344 		bp->rp += ifc->m->hsize;
345 		if(ifc->lifc == nil)
346 			freeb(bp);
347 		else
348 			ipiput4(er->f, ifc, bp);
349 		runlock(ifc);
350 		poperror();
351 	}
352 }
353 
354 
355 /*
356  *  process to read from the ethernet, IPv6
357  */
358 static void
359 etherread6(void *a)
360 {
361 	Ipifc *ifc;
362 	Block *bp;
363 	Etherrock *er;
364 
365 	ifc = a;
366 	er = ifc->arg;
367 	er->read6p = up;	/* hide identity under a rock for unbind */
368 	if(waserror()){
369 		er->read6p = 0;
370 		pexit("hangup", 1);
371 	}
372 	for(;;){
373 		bp = devtab[er->mchan6->type]->bread(er->mchan6, ifc->maxmtu, 0);
374 		if(!canrlock(ifc)){
375 			freeb(bp);
376 			continue;
377 		}
378 		if(waserror()){
379 			runlock(ifc);
380 			nexterror();
381 		}
382 		ifc->in++;
383 		bp->rp += ifc->m->hsize;
384 		if(ifc->lifc == nil)
385 			freeb(bp);
386 		else
387 			ipiput6(er->f, ifc, bp);
388 		runlock(ifc);
389 		poperror();
390 	}
391 }
392 
393 static void
394 etheraddmulti(Ipifc *ifc, uchar *a, uchar *)
395 {
396 	uchar mac[6];
397 	char buf[64];
398 	Etherrock *er = ifc->arg;
399 	int version;
400 
401 	version = multicastea(mac, a);
402 	sprint(buf, "addmulti %E", mac);
403 	switch(version){
404 	case V4:
405 		devtab[er->cchan4->type]->write(er->cchan4, buf, strlen(buf), 0);
406 		break;
407 	case V6:
408 		devtab[er->cchan6->type]->write(er->cchan6, buf, strlen(buf), 0);
409 		break;
410 	default:
411 		panic("etheraddmulti: version %d", version);
412 	}
413 }
414 
415 static void
416 etherremmulti(Ipifc *ifc, uchar *a, uchar *)
417 {
418 	uchar mac[6];
419 	char buf[64];
420 	Etherrock *er = ifc->arg;
421 	int version;
422 
423 	version = multicastea(mac, a);
424 	sprint(buf, "remmulti %E", mac);
425 	switch(version){
426 	case V4:
427 		devtab[er->cchan4->type]->write(er->cchan4, buf, strlen(buf), 0);
428 		break;
429 	case V6:
430 		devtab[er->cchan6->type]->write(er->cchan6, buf, strlen(buf), 0);
431 		break;
432 	default:
433 		panic("etherremmulti: version %d", version);
434 	}
435 }
436 
437 /*
438  *  send an ethernet arp
439  *  (only v4, v6 uses the neighbor discovery, rfc1970)
440  */
441 static void
442 sendarp(Ipifc *ifc, Arpent *a)
443 {
444 	int n;
445 	Block *bp;
446 	Etherarp *e;
447 	Etherrock *er = ifc->arg;
448 
449 	/* don't do anything if it's been less than a second since the last */
450 	if(NOW - a->time < 1000){
451 		arprelease(er->f->arp, a);
452 		return;
453 	}
454 
455 	/* remove all but the last message */
456 	while((bp = a->hold) != nil){
457 		if(bp == a->last)
458 			break;
459 		a->hold = bp->list;
460 		freeblist(bp);
461 	}
462 
463 	/* try to keep it around for a second more */
464 	a->time = NOW;
465 	arprelease(er->f->arp, a);
466 
467 	n = sizeof(Etherarp);
468 	if(n < a->type->minmtu)
469 		n = a->type->minmtu;
470 	bp = allocb(n);
471 	memset(bp->rp, 0, n);
472 	e = (Etherarp*)bp->rp;
473 	memmove(e->tpa, a->ip+IPv4off, sizeof(e->tpa));
474 	ipv4local(ifc, e->spa);
475 	memmove(e->sha, ifc->mac, sizeof(e->sha));
476 	memset(e->d, 0xff, sizeof(e->d));		/* ethernet broadcast */
477 	memmove(e->s, ifc->mac, sizeof(e->s));
478 
479 	hnputs(e->type, ETARP);
480 	hnputs(e->hrd, 1);
481 	hnputs(e->pro, ETIP4);
482 	e->hln = sizeof(e->sha);
483 	e->pln = sizeof(e->spa);
484 	hnputs(e->op, ARPREQUEST);
485 	bp->wp += n;
486 
487 	n = devtab[er->achan->type]->bwrite(er->achan, bp, 0);
488 	if(n < 0)
489 		print("arp: send: %r\n");
490 }
491 
492 static void
493 resolveaddr6(Ipifc *ifc, Arpent *a)
494 {
495 	int sflag;
496 	Block *bp;
497 	Etherrock *er = ifc->arg;
498 	uchar ipsrc[IPaddrlen];
499 
500 	/* don't do anything if it's been less than a second since the last */
501 	if(NOW - a->time < ReTransTimer){
502 		arprelease(er->f->arp, a);
503 		return;
504 	}
505 
506 	/* remove all but the last message */
507 	while((bp = a->hold) != nil){
508 		if(bp == a->last)
509 			break;
510 		a->hold = bp->list;
511 		freeblist(bp);
512 	}
513 
514 	/* try to keep it around for a second more */
515 	a->time = NOW;
516 	a->rxtat = NOW + ReTransTimer;
517 	if(a->rxtsrem <= 0) {
518 		arprelease(er->f->arp, a);
519 		return;
520 	}
521 
522 	a->rxtsrem--;
523 	arprelease(er->f->arp, a);
524 
525 	if(sflag = ipv6anylocal(ifc, ipsrc))
526 		icmpns(er->f, ipsrc, sflag, a->ip, TARG_MULTI, ifc->mac);
527 }
528 
529 /*
530  *  send a gratuitous arp to refresh arp caches
531  */
532 static void
533 sendgarp(Ipifc *ifc, uchar *ip)
534 {
535 	int n;
536 	Block *bp;
537 	Etherarp *e;
538 	Etherrock *er = ifc->arg;
539 
540 	/* don't arp for our initial non address */
541 	if(ipcmp(ip, IPnoaddr) == 0)
542 		return;
543 
544 	n = sizeof(Etherarp);
545 	if(n < ifc->m->minmtu)
546 		n = ifc->m->minmtu;
547 	bp = allocb(n);
548 	memset(bp->rp, 0, n);
549 	e = (Etherarp*)bp->rp;
550 	memmove(e->tpa, ip+IPv4off, sizeof(e->tpa));
551 	memmove(e->spa, ip+IPv4off, sizeof(e->spa));
552 	memmove(e->sha, ifc->mac, sizeof(e->sha));
553 	memset(e->d, 0xff, sizeof(e->d));		/* ethernet broadcast */
554 	memmove(e->s, ifc->mac, sizeof(e->s));
555 
556 	hnputs(e->type, ETARP);
557 	hnputs(e->hrd, 1);
558 	hnputs(e->pro, ETIP4);
559 	e->hln = sizeof(e->sha);
560 	e->pln = sizeof(e->spa);
561 	hnputs(e->op, ARPREQUEST);
562 	bp->wp += n;
563 
564 	n = devtab[er->achan->type]->bwrite(er->achan, bp, 0);
565 	if(n < 0)
566 		print("garp: send: %r\n");
567 }
568 
569 static void
570 recvarp(Ipifc *ifc)
571 {
572 	int n;
573 	Block *ebp, *rbp;
574 	Etherarp *e, *r;
575 	uchar ip[IPaddrlen];
576 	Etherrock *er = ifc->arg;
577 
578 	ebp = devtab[er->achan->type]->bread(er->achan, ifc->maxmtu, 0);
579 	if(ebp == nil) {
580 		print("arp: rcv: %r\n");
581 		return;
582 	}
583 
584 	e = (Etherarp*)ebp->rp;
585 	switch(nhgets(e->op)) {
586 	default:
587 		break;
588 
589 	case ARPREPLY:
590 		/* check for machine using my ip address */
591 		v4tov6(ip, e->spa);
592 		if(iplocalonifc(ifc, ip) || ipproxyifc(er->f, ifc, ip)){
593 			if(memcmp(e->sha, ifc->mac, sizeof(e->sha)) != 0){
594 				print("arprep: 0x%E/0x%E also has ip addr %V\n",
595 					e->s, e->sha, e->spa);
596 				break;
597 			}
598 		}
599 
600 		arpenter(er->f, V4, e->spa, e->sha, sizeof(e->sha), 0);
601 		break;
602 
603 	case ARPREQUEST:
604 		/* don't answer arps till we know who we are */
605 		if(ifc->lifc == 0)
606 			break;
607 
608 		/* check for machine using my ip or ether address */
609 		v4tov6(ip, e->spa);
610 		if(iplocalonifc(ifc, ip) || ipproxyifc(er->f, ifc, ip)){
611 			if(memcmp(e->sha, ifc->mac, sizeof(e->sha)) != 0)
612 				print("arpreq: 0x%E also has ip addr %V\n", e->sha, e->spa);
613 		} else {
614 			if(memcmp(e->sha, ifc->mac, sizeof(e->sha)) == 0){
615 				print("arpreq: %V also has ether addr %E\n", e->spa, e->sha);
616 				break;
617 			}
618 		}
619 
620 		/* refresh what we know about sender */
621 		arpenter(er->f, V4, e->spa, e->sha, sizeof(e->sha), 1);
622 
623 		/* answer only requests for our address or systems we're proxying for */
624 		v4tov6(ip, e->tpa);
625 		if(!iplocalonifc(ifc, ip))
626 		if(!ipproxyifc(er->f, ifc, ip))
627 			break;
628 
629 		n = sizeof(Etherarp);
630 		if(n < ifc->minmtu)
631 			n = ifc->minmtu;
632 		rbp = allocb(n);
633 		r = (Etherarp*)rbp->rp;
634 		memset(r, 0, sizeof(Etherarp));
635 		hnputs(r->type, ETARP);
636 		hnputs(r->hrd, 1);
637 		hnputs(r->pro, ETIP4);
638 		r->hln = sizeof(r->sha);
639 		r->pln = sizeof(r->spa);
640 		hnputs(r->op, ARPREPLY);
641 		memmove(r->tha, e->sha, sizeof(r->tha));
642 		memmove(r->tpa, e->spa, sizeof(r->tpa));
643 		memmove(r->sha, ifc->mac, sizeof(r->sha));
644 		memmove(r->spa, e->tpa, sizeof(r->spa));
645 		memmove(r->d, e->sha, sizeof(r->d));
646 		memmove(r->s, ifc->mac, sizeof(r->s));
647 		rbp->wp += n;
648 
649 		n = devtab[er->achan->type]->bwrite(er->achan, rbp, 0);
650 		if(n < 0)
651 			print("arp: write: %r\n");
652 	}
653 	freeb(ebp);
654 }
655 
656 static void
657 recvarpproc(void *v)
658 {
659 	Ipifc *ifc = v;
660 	Etherrock *er = ifc->arg;
661 
662 	er->arpp = up;
663 	if(waserror()){
664 		er->arpp = 0;
665 		pexit("hangup", 1);
666 	}
667 	for(;;)
668 		recvarp(ifc);
669 }
670 
671 static int
672 multicastea(uchar *ea, uchar *ip)
673 {
674 	int x;
675 
676 	switch(x = ipismulticast(ip)){
677 	case V4:
678 		ea[0] = 0x01;
679 		ea[1] = 0x00;
680 		ea[2] = 0x5e;
681 		ea[3] = ip[13] & 0x7f;
682 		ea[4] = ip[14];
683 		ea[5] = ip[15];
684 		break;
685  	case V6:
686  		ea[0] = 0x33;
687  		ea[1] = 0x33;
688  		ea[2] = ip[12];
689 		ea[3] = ip[13];
690  		ea[4] = ip[14];
691  		ea[5] = ip[15];
692  		break;
693 	}
694 	return x;
695 }
696 
697 /*
698  *  fill in an arp entry for broadcast or multicast
699  *  addresses.  Return the first queued packet for the
700  *  IP address.
701  */
702 static Block*
703 multicastarp(Fs *f, Arpent *a, Medium *medium, uchar *mac)
704 {
705 	/* is it broadcast? */
706 	switch(ipforme(f, a->ip)){
707 	case Runi:
708 		return nil;
709 	case Rbcast:
710 		memset(mac, 0xff, 6);
711 		return arpresolve(f->arp, a, medium, mac);
712 	default:
713 		break;
714 	}
715 
716 	/* if multicast, fill in mac */
717 	switch(multicastea(mac, a->ip)){
718 	case V4:
719 	case V6:
720 		return arpresolve(f->arp, a, medium, mac);
721 	}
722 
723 	/* let arp take care of it */
724 	return nil;
725 }
726 
727 void
728 ethermediumlink(void)
729 {
730 	addipmedium(&ethermedium);
731 	addipmedium(&gbemedium);
732 }
733 
734 
735 static void
736 etherpref2addr(uchar *pref, uchar *ea)
737 {
738 	pref[8]  = ea[0] | 0x2;
739 	pref[9]  = ea[1];
740 	pref[10] = ea[2];
741 	pref[11] = 0xFF;
742 	pref[12] = 0xFE;
743 	pref[13] = ea[3];
744 	pref[14] = ea[4];
745 	pref[15] = ea[5];
746 }
747