xref: /plan9-contrib/sys/src/cmd/ip/ipconfig/ipv6.c (revision fafe75d552bbcea7be972cac0a0a7720a586a83f)
1 /*
2  * ipconfig for IPv6
3  *	RS means Router Solicitation
4  *	RA means Router Advertisement
5  */
6 
7 #include <u.h>
8 #include <libc.h>
9 #include <bio.h>
10 #include <ip.h>
11 #include "ipconfig.h"
12 #include "../icmp.h"
13 
14 #pragma varargck argpos ralog 1
15 
16 #define RALOG "v6routeradv"
17 
18 #define NetS(x) (((uchar*)x)[0]<< 8 | ((uchar*)x)[1])
19 #define NetL(x) (((uchar*)x)[0]<<24 | ((uchar*)x)[1]<<16 | \
20 		 ((uchar*)x)[2]<< 8 | ((uchar*)x)[3])
21 
22 enum {
23 	ICMP6LEN=	4,
24 };
25 
26 typedef struct Hdr Hdr;
27 struct Hdr			/* ICMP v4 & v6 header */
28 {
29 	uchar	type;
30 	uchar	code;
31 	uchar	cksum[2];	/* Checksum */
32 	uchar	data[];
33 };
34 
35 char *icmpmsg6[Maxtype6+1] =
36 {
37 [EchoReply]		"EchoReply",
38 [UnreachableV6]		"UnreachableV6",
39 [PacketTooBigV6]	"PacketTooBigV6",
40 [TimeExceedV6]		"TimeExceedV6",
41 [Redirect]		"Redirect",
42 [EchoRequest]		"EchoRequest",
43 [TimeExceed]		"TimeExceed",
44 [InParmProblem]		"InParmProblem",
45 [Timestamp]		"Timestamp",
46 [TimestampReply]	"TimestampReply",
47 [InfoRequest]		"InfoRequest",
48 [InfoReply]		"InfoReply",
49 [AddrMaskRequest]	"AddrMaskRequest",
50 [AddrMaskReply]		"AddrMaskReply",
51 [EchoRequestV6]		"EchoRequestV6",
52 [EchoReplyV6]		"EchoReplyV6",
53 [RouterSolicit]		"RouterSolicit",
54 [RouterAdvert]		"RouterAdvert",
55 [NbrSolicit]		"NbrSolicit",
56 [NbrAdvert]		"NbrAdvert",
57 [RedirectV6]		"RedirectV6",
58 };
59 
60 static char *icmp6opts[] =
61 {
62 [0]			"unknown option",
63 [V6nd_srclladdr]	"srcll_addr",
64 [V6nd_targlladdr]	"targll_addr",
65 [V6nd_pfxinfo]		"prefix",
66 [V6nd_redirhdr]		"redirect",
67 [V6nd_mtu]		"mtu",
68 [V6nd_home]		"home",
69 [V6nd_srcaddrs]		"src_addrs",
70 [V6nd_ip]		"ip",
71 [V6nd_rdns]		"rdns",
72 [V6nd_9fs]		"9fs",
73 [V6nd_9auth]		"9auth",
74 };
75 
76 uchar v6allroutersL[IPaddrlen] = {
77 	0xff, 0x02, 0, 0,
78 	0, 0, 0, 0,
79 	0, 0, 0, 0,
80 	0, 0, 0, 0x02
81 };
82 
83 uchar v6allnodesL[IPaddrlen] = {
84 	0xff, 0x02, 0, 0,
85 	0, 0, 0, 0,
86 	0, 0, 0, 0,
87 	0, 0, 0, 0x01
88 };
89 
90 uchar v6Unspecified[IPaddrlen] = {
91 	0, 0, 0, 0,
92 	0, 0, 0, 0,
93 	0, 0, 0, 0,
94 	0, 0, 0, 0
95 };
96 
97 uchar v6loopback[IPaddrlen] = {
98 	0, 0, 0, 0,
99 	0, 0, 0, 0,
100 	0, 0, 0, 0,
101 	0, 0, 0, 1
102 };
103 
104 uchar v6glunicast[IPaddrlen] = {
105 	0x08, 0, 0, 0,
106 	0, 0, 0, 0,
107 	0, 0, 0, 0,
108 	0, 0, 0, 0
109 };
110 
111 uchar v6linklocal[IPaddrlen] = {
112 	0xfe, 0x80, 0, 0,
113 	0, 0, 0, 0,
114 	0, 0, 0, 0,
115 	0, 0, 0, 0
116 };
117 
118 uchar v6solpfx[IPaddrlen] = {
119 	0xff, 0x02, 0, 0,
120 	0, 0, 0, 0,
121 	0, 0, 0, 1,
122 	/* last 3 bytes filled with low-order bytes of addr being solicited */
123 	0xff, 0, 0, 0,
124 };
125 
126 uchar v6defmask[IPaddrlen] = {
127 	0xff, 0xff, 0xff, 0xff,
128 	0xff, 0xff, 0xff, 0xff,
129 	0, 0, 0, 0,
130 	0, 0, 0, 0
131 };
132 
133 enum
134 {
135 	Vadd,
136 	Vremove,
137 	Vunbind,
138 	Vaddpref6,
139 	Vra6,
140 };
141 
142 static void
ralog(char * fmt,...)143 ralog(char *fmt, ...)
144 {
145 	char msg[512];
146 	va_list arg;
147 
148 	va_start(arg, fmt);
149 	vseprint(msg, msg+sizeof msg, fmt, arg);
150 	va_end(arg);
151 	syslog(debug, RALOG, msg);
152 }
153 
154 extern void
ea2lla(uchar * lla,uchar * ea)155 ea2lla(uchar *lla, uchar *ea)
156 {
157 	assert(IPaddrlen == 16);
158 	memset(lla, 0, IPaddrlen);
159 	lla[0]  = 0xFE;
160 	lla[1]  = 0x80;
161 	lla[8]  = ea[0] | 0x2;
162 	lla[9]  = ea[1];
163 	lla[10] = ea[2];
164 	lla[11] = 0xFF;
165 	lla[12] = 0xFE;
166 	lla[13] = ea[3];
167 	lla[14] = ea[4];
168 	lla[15] = ea[5];
169 }
170 
171 extern void
ipv62smcast(uchar * smcast,uchar * a)172 ipv62smcast(uchar *smcast, uchar *a)
173 {
174 	assert(IPaddrlen == 16);
175 	memset(smcast, 0, IPaddrlen);
176 	smcast[0]  = 0xFF;
177 	smcast[1]  = 0x02;
178 	smcast[11] = 0x1;
179 	smcast[12] = 0xFF;
180 	smcast[13] = a[13];
181 	smcast[14] = a[14];
182 	smcast[15] = a[15];
183 }
184 
185 void
v6paraminit(Conf * cf)186 v6paraminit(Conf *cf)
187 {
188 	cf->sendra = cf->recvra = 0;
189 	cf->mflag = 0;
190 	cf->oflag = 0;
191 	cf->maxraint = Maxv6initraintvl;
192 	cf->minraint = Maxv6initraintvl / 4;
193 	cf->linkmtu = 1500;
194 	cf->reachtime = V6reachabletime;
195 	cf->rxmitra = V6retranstimer;
196 	cf->ttl = MAXTTL;
197 
198 	cf->routerlt = 0;
199 
200 	cf->prefixlen = 64;
201 	cf->onlink = 0;
202 	cf->autoflag = 0;
203 	cf->validlt = cf->preflt = ~0L;
204 }
205 
206 static char *
optname(unsigned opt)207 optname(unsigned opt)
208 {
209 	static char buf[32];
210 
211 	if (opt >= nelem(icmp6opts) || icmp6opts[opt] == nil) {
212 		snprint(buf, sizeof buf, "unknown option %d", opt);
213 		return buf;
214 	} else
215 		return icmp6opts[opt];
216 }
217 
218 static char*
opt_seprint(uchar * ps,uchar * pe,char * sps,char * spe)219 opt_seprint(uchar *ps, uchar *pe, char *sps, char *spe)
220 {
221 	int otype, osz, pktsz;
222 	uchar *a;
223 	char *p = sps, *e = spe;
224 
225 	a = ps;
226 	for (pktsz = pe - ps; pktsz > 0; pktsz -= osz) {
227 		otype = a[0];
228 		osz = a[1] * 8;
229 
230 		switch (otype) {
231 		default:
232 			return seprint(p, e, " option=%s ", optname(otype));
233 		case V6nd_srclladdr:
234 		case V6nd_targlladdr:
235 			if (pktsz < osz || osz != 8)
236 				return seprint(p, e, " option=%s bad size=%d",
237 					optname(otype), osz);
238 			p = seprint(p, e, " option=%s maddr=%E", optname(otype),
239 				a+2);
240 			break;
241 		case V6nd_pfxinfo:
242 			if (pktsz < osz || osz != 32)
243 				return seprint(p, e, " option=%s: bad size=%d",
244 					optname(otype), osz);
245 
246 			p = seprint(p, e, " option=%s pref=%I preflen=%3.3d"
247 				" lflag=%1.1d aflag=%1.1d unused1=%1.1d"
248 				" validlt=%ud preflt=%ud unused2=%1.1d",
249 				optname(otype), a+16, (int)(*(a+2)),
250 				(*(a+3) & (1 << 7)) != 0,
251 				(*(a+3) & (1 << 6)) != 0,
252 				(*(a+3) & 63) != 0,
253 				NetL(a+4), NetL(a+8), NetL(a+12)!=0);
254 			break;
255 		}
256 		a += osz;
257 	}
258 	return p;
259 }
260 
261 static void
pkt2str(uchar * ps,uchar * pe,char * sps,char * spe)262 pkt2str(uchar *ps, uchar *pe, char *sps, char *spe)
263 {
264 	int pktlen;
265 	char *tn, *p, *e;
266 	uchar *a;
267 	Hdr *h;
268 
269 	h = (Hdr*)ps;
270 	a = ps + 4;
271 	p = sps;
272 	e = spe;
273 
274 	pktlen = pe - ps;
275 	if(pktlen < ICMP6LEN) {
276 		seprint(sps, spe, "short pkt");
277 		return;
278 	}
279 
280 	tn = icmpmsg6[h->type];
281 	if(tn == nil)
282 		p = seprint(p, e, "t=%ud c=%d ck=%4.4ux", h->type,
283 			h->code, (ushort)NetS(h->cksum));
284 	else
285 		p = seprint(p, e, "t=%s c=%d ck=%4.4ux", tn,
286 			h->code, (ushort)NetS(h->cksum));
287 
288 	switch(h->type){
289 	case RouterSolicit:
290 		ps += 8;
291 		p = seprint(p, e, " unused=%1.1d ", NetL(a)!=0);
292 		opt_seprint(ps, pe, p, e);
293 		break;
294 	case RouterAdvert:
295 		ps += 16;
296 		p = seprint(p, e, " hoplim=%3.3d mflag=%1.1d oflag=%1.1d"
297 			" unused=%1.1d routerlt=%d reachtime=%d rxmtimer=%d",
298 			a[0],
299 			(*(a+1) & (1 << 7)) != 0,
300 			(*(a+1) & (1 << 6)) != 0,
301 			(*(a+1) & 63) != 0,
302 			NetS(a+2), NetL(a+4), NetL(a+8));
303 		opt_seprint(ps, pe, p, e);
304 		break;
305 	default:
306 		seprint(p, e, " unexpected icmp6 pkt type");
307 		break;
308 	}
309 }
310 
311 static void
catch(void * a,char * msg)312 catch(void *a, char *msg)
313 {
314 	USED(a);
315 	if(strstr(msg, "alarm"))
316 		noted(NCONT);
317 	else
318 		noted(NDFLT);
319 }
320 
321 /*
322  * based on libthread's threadsetname, but drags in less library code.
323  * actually just sets the arguments displayed.
324  */
325 void
procsetname(char * fmt,...)326 procsetname(char *fmt, ...)
327 {
328 	int fd;
329 	char *cmdname;
330 	char buf[128];
331 	va_list arg;
332 
333 	va_start(arg, fmt);
334 	cmdname = vsmprint(fmt, arg);
335 	va_end(arg);
336 	if (cmdname == nil)
337 		return;
338 	snprint(buf, sizeof buf, "#p/%d/args", getpid());
339 	if((fd = open(buf, OWRITE)) >= 0){
340 		write(fd, cmdname, strlen(cmdname)+1);
341 		close(fd);
342 	}
343 	free(cmdname);
344 }
345 
346 int
dialicmp(uchar * dst,int dport,int * ctlfd)347 dialicmp(uchar *dst, int dport, int *ctlfd)
348 {
349 	int fd, cfd, n, m;
350 	char cmsg[100], name[128], connind[40];
351 	char hdrs[] = "headers";
352 
353 	snprint(name, sizeof name, "%s/icmpv6/clone", conf.mpoint);
354 	cfd = open(name, ORDWR);
355 	if(cfd < 0)
356 		sysfatal("dialicmp: can't open %s: %r", name);
357 
358 	n = snprint(cmsg, sizeof cmsg, "connect %I!%d!r %d", dst, dport, dport);
359 	m = write(cfd, cmsg, n);
360 	if (m < n)
361 		sysfatal("dialicmp: can't write %s to %s: %r", cmsg, name);
362 
363 	seek(cfd, 0, 0);
364 	n = read(cfd, connind, sizeof connind);
365 	if (n < 0)
366 		connind[0] = 0;
367 	else if (n < sizeof connind)
368 		connind[n] = 0;
369 	else
370 		connind[sizeof connind - 1] = 0;
371 
372 	snprint(name, sizeof name, "%s/icmpv6/%s/data", conf.mpoint, connind);
373 	fd = open(name, ORDWR);
374 	if(fd < 0)
375 		sysfatal("dialicmp: can't open %s: %r", name);
376 
377 	n = sizeof hdrs - 1;
378 	if(write(cfd, hdrs, n) < n)
379 		sysfatal("dialicmp: can't write `%s' to %s: %r", hdrs, name);
380 	*ctlfd = cfd;
381 	return fd;
382 }
383 
384 /* add ipv6 addr to an interface */
385 int
ip6cfg(int autoconf)386 ip6cfg(int autoconf)
387 {
388 	int dupfound = 0, n, nf;
389 	char *p, *fields[4];
390 	char buf[256];
391 	uchar ethaddr[6];
392 	Biobuf *bp;
393 
394 	if (autoconf) {			/* create link-local addr */
395 		if (myetheraddr(ethaddr, conf.dev) < 0)
396 			sysfatal("myetheraddr w/ %s failed: %r", conf.dev);
397 		ea2lla(conf.laddr, ethaddr);
398 	}
399 
400 	if (dupl_disc)
401 		n = sprint(buf, "try");
402 	else
403 		n = sprint(buf, "add");
404 
405 	n += snprint(buf+n, sizeof buf-n, " %I", conf.laddr);
406 	if(!validip(conf.mask))
407 		ipmove(conf.mask, v6defmask);
408 	n += snprint(buf+n, sizeof buf-n, " %M", conf.mask);
409 	if(validip(conf.raddr)){
410 		n += snprint(buf+n, sizeof buf-n, " %I", conf.raddr);
411 		if(conf.mtu != 0)
412 			n += snprint(buf+n, sizeof buf-n, " %d", conf.mtu);
413 	}
414 
415 	if(write(conf.cfd, buf, n) < 0){
416 		warning("write(%s): %r", buf);
417 		return -1;
418 	}
419 
420 	if (!dupl_disc)
421 		return 0;
422 
423 	sleep(3000);
424 
425 	/* read arp table, look for addr duplication */
426 	snprint(buf, sizeof buf, "%s/arp", conf.mpoint);
427 	bp = Bopen(buf, OREAD);
428 	if (bp == 0) {
429 		warning("couldn't open %s: %r", buf);
430 		return -1;
431 	}
432 
433 	snprint(buf, sizeof buf, "%I", conf.laddr);
434 	while(p = Brdline(bp, '\n')){
435 		p[Blinelen(bp)-1] = 0;
436 		nf = tokenize(p, fields, nelem(fields));
437 		if(nf >= 4 && cistrcmp(fields[2], buf) == 0) {
438 			warning("found dup entry in arp cache");
439 			dupfound = 1;
440 			break;
441 		}
442 	}
443 	Bterm(bp);
444 
445 	if (dupfound)
446 		doremove();
447 	else {
448 		n = sprint(buf, "add %I %M", conf.laddr, conf.mask);
449 		if(validip(conf.raddr)){
450 			n += snprint(buf+n, sizeof buf-n, " %I", conf.raddr);
451 			if(conf.mtu != 0)
452 				n += snprint(buf+n, sizeof buf-n, " %d",
453 					conf.mtu);
454 		}
455 		write(conf.cfd, buf, n);
456 	}
457 	return 0;
458 }
459 
460 static int
recvra6on(char * net,int conn)461 recvra6on(char *net, int conn)
462 {
463 	Ipifc* ifc;
464 
465 	ifc = readipifc(net, nil, conn);
466 	if (ifc == nil)
467 		return 0;
468 	else if (ifc->sendra6 > 0)
469 		return IsRouter;
470 	else if (ifc->recvra6 > 0)
471 		return IsHostRecv;
472 	else
473 		return IsHostNoRecv;
474 }
475 
476 /* send icmpv6 router solicitation to multicast address for all routers */
477 static void
sendrs(int fd)478 sendrs(int fd)
479 {
480 	Routersol *rs;
481 	uchar buff[sizeof *rs];
482 
483 	memset(buff, 0, sizeof buff);
484 	rs = (Routersol *)buff;
485 	memmove(rs->dst, v6allroutersL, IPaddrlen);
486 	memmove(rs->src, v6Unspecified, IPaddrlen);
487 	rs->type = ICMP6_RS;
488 
489 	if(write(fd, rs, sizeof buff) < sizeof buff)
490 		ralog("sendrs: write failed, pkt size %d", sizeof buff);
491 	else
492 		ralog("sendrs: sent solicitation to %I from %I on %s",
493 			rs->dst, rs->src, conf.dev);
494 }
495 
496 /*
497  * a router receiving a router adv from another
498  * router calls this; it is basically supposed to
499  * log the information in the ra and raise a flag
500  * if any parameter value is different from its configured values.
501  *
502  * doing nothing for now since I don't know where to log this yet.
503  */
504 static void
recvrarouter(uchar buf[],int pktlen)505 recvrarouter(uchar buf[], int pktlen)
506 {
507 	USED(buf, pktlen);
508 	ralog("i am a router and got a router advert");
509 }
510 
511 /* host receiving a router advertisement calls this */
512 
513 static void
ewrite(int fd,char * str)514 ewrite(int fd, char *str)
515 {
516 	int n;
517 
518 	n = strlen(str);
519 	if (write(fd, str, n) != n)
520 		ralog("write(%s) failed: %r", str);
521 }
522 
523 static void
issuebasera6(Conf * cf)524 issuebasera6(Conf *cf)
525 {
526 	char *cfg;
527 
528 	cfg = smprint("ra6 mflag %d oflag %d reachtime %d rxmitra %d "
529 		"ttl %d routerlt %d",
530 		cf->mflag, cf->oflag, cf->reachtime, cf->rxmitra,
531 		cf->ttl, cf->routerlt);
532 	ewrite(cf->cfd, cfg);
533 	free(cfg);
534 }
535 
536 static void
issuerara6(Conf * cf)537 issuerara6(Conf *cf)
538 {
539 	char *cfg;
540 
541 	cfg = smprint("ra6 sendra %d recvra %d maxraint %d minraint %d "
542 		"linkmtu %d",
543 		cf->sendra, cf->recvra, cf->maxraint, cf->minraint,
544 		cf->linkmtu);
545 	ewrite(cf->cfd, cfg);
546 	free(cfg);
547 }
548 
549 static void
issueadd6(Conf * cf)550 issueadd6(Conf *cf)
551 {
552 	char *cfg;
553 
554 	cfg = smprint("add6 %I %d %d %d %lud %lud", cf->v6pref, cf->prefixlen,
555 		cf->onlink, cf->autoflag, cf->validlt, cf->preflt);
556 	ewrite(cf->cfd, cfg);
557 	free(cfg);
558 }
559 
560 static void
recvrahost(uchar buf[],int pktlen)561 recvrahost(uchar buf[], int pktlen)
562 {
563 	int arpfd, m, n;
564 	char abuf[100];
565 	uchar optype;
566 	Lladdropt *llao;
567 	Mtuopt *mtuo;
568 	Prefixopt *prfo;
569 	Routeradv *ra;
570 	static int first = 1;
571 
572 	ra = (Routeradv*)buf;
573 //	memmove(conf.v6gaddr, ra->src, IPaddrlen);
574 	conf.ttl = ra->cttl;
575 	conf.mflag = (MFMASK & ra->mor);
576 	conf.oflag = (OCMASK & ra->mor);
577 	conf.routerlt =  nhgets(ra->routerlt);
578 	conf.reachtime = nhgetl(ra->rchbltime);
579 	conf.rxmitra =   nhgetl(ra->rxmtimer);
580 
581 //	issueadd6(&conf);		/* for conf.v6gaddr? */
582 	if (fprint(conf.cfd, "ra6 recvra 1") < 0)
583 		ralog("write(ra6 recvra 1) failed: %r");
584 	issuebasera6(&conf);
585 
586 	m = sizeof *ra;
587 	while (pktlen - m > 0) {
588 		optype = buf[m];
589 		switch (optype) {
590 		case V6nd_srclladdr:
591 			llao = (Lladdropt *)&buf[m];
592 			m += 8 * buf[m+1];
593 			if (llao->len != 1) {
594 				ralog("recvrahost: illegal len (%d) for source "
595 					"link layer address option", llao->len);
596 				return;
597 			}
598 			if (!ISIPV6LINKLOCAL(ra->src)) {
599 				ralog("recvrahost: non-link-local src addr for "
600 					"router adv %I", ra->src);
601 				return;
602 			}
603 
604 			snprint(abuf, sizeof abuf, "%s/arp", conf.mpoint);
605 			arpfd = open(abuf, OWRITE);
606 			if (arpfd < 0) {
607 				ralog("recvrahost: couldn't open %s to write: %r",
608 					abuf);
609 				return;
610 			}
611 
612 			n = snprint(abuf, sizeof abuf, "add ether %I %E",
613 				ra->src, llao->lladdr);
614 			if (write(arpfd, abuf, n) < n)
615 				ralog("recvrahost: couldn't write to %s/arp",
616 					conf.mpoint);
617 			close(arpfd);
618 			break;
619 		case V6nd_targlladdr:
620 		case V6nd_redirhdr:
621 			m += 8 * buf[m+1];
622 			ralog("ignoring unexpected option type `%s' in Routeradv",
623 				optname(optype));
624 			break;
625 		case V6nd_mtu:
626 			mtuo = (Mtuopt*)&buf[m];
627 			m += 8 * mtuo->len;
628 			conf.linkmtu = nhgetl(mtuo->mtu);
629 			break;
630 		case V6nd_pfxinfo:
631 			prfo = (Prefixopt*)&buf[m];
632 			m += 8 * prfo->len;
633 			if (prfo->len != 4) {
634 				ralog("illegal len (%d) for prefix option",
635 					prfo->len);
636 				return;
637 			}
638 			memmove(conf.v6pref, prfo->pref, IPaddrlen);
639 			conf.prefixlen = prfo->plen;
640 			conf.onlink =   ((prfo->lar & OLMASK) != 0);
641 			conf.autoflag = ((prfo->lar & AFMASK) != 0);
642 			conf.validlt = nhgetl(prfo->validlt);
643 			conf.preflt =  nhgetl(prfo->preflt);
644 			issueadd6(&conf);
645 			if (first) {
646 				first = 0;
647 				ralog("got initial RA from %I on %s; pfx %I",
648 					ra->src, conf.dev, prfo->pref);
649 			}
650 			break;
651 		default:
652 			if (debug)
653 				ralog("ignoring optype %d in Routeradv from %I",
654 					optype, ra->src);
655 			/* fall through */
656 		case V6nd_srcaddrs:
657 			/* netsbd sends this, so quietly ignore it for now */
658 			m += 8 * buf[m+1];
659 			break;
660 		}
661 	}
662 }
663 
664 /*
665  * daemon to receive router advertisements from routers
666  */
667 void
recvra6(void)668 recvra6(void)
669 {
670 	int fd, cfd, n, sendrscnt, sleepfor;
671 	uchar buf[4096];
672 
673 	/* TODO: why not v6allroutersL? */
674 	fd = dialicmp(v6allnodesL, ICMP6_RA, &cfd);
675 	if (fd < 0)
676 		sysfatal("can't open icmp_ra connection: %r");
677 
678 	notify(catch);
679 	sendrscnt = Maxv6rss;
680 
681 	switch(rfork(RFPROC|RFMEM|RFFDG|RFNOWAIT|RFNOTEG)){
682 	case -1:
683 		sysfatal("can't fork: %r");
684 	default:
685 		return;
686 	case 0:
687 		break;
688 	}
689 
690 	procsetname("recvra6 on %s", conf.dev);
691 	ralog("recvra6 on %s", conf.dev);
692 	sleepfor = jitter();
693 	for (;;) {
694 		/*
695 		 * We only get 3 (Maxv6rss) tries, so make sure we
696 		 * wait long enough to be certain that at least one RA
697 		 * will be transmitted.
698 		 */
699 		if (sleepfor < 7000)
700 			sleepfor = 7000;
701 		alarm(sleepfor);
702 		n = read(fd, buf, sizeof buf);
703 		alarm(0);
704 		if (n <= 0) {
705 			if (sendrscnt > 0) {
706 				sendrscnt--;
707 				if (recvra6on(conf.mpoint, myifc) == IsHostRecv)
708 					sendrs(fd);
709 				sleepfor = V6rsintvl + nrand(100);
710 			}
711 			if (sendrscnt == 0) {
712 				sendrscnt--;
713 				sleepfor = 0;
714 				ralog("recvra6: no router advs after %d sols on %s",
715 					Maxv6rss, conf.dev);
716 			}
717 			continue;
718 		}
719 
720 		sleepfor = 0;
721 		sendrscnt = -1;		/* got at least initial ra; no whining */
722 		switch (recvra6on(conf.mpoint, myifc)) {
723 		case IsRouter:
724 			recvrarouter(buf, n);
725 			break;
726 		case IsHostRecv:
727 			recvrahost(buf, n);
728 			break;
729 		case IsHostNoRecv:
730 			ralog("recvra6: recvra off, quitting on %s", conf.dev);
731 			close(fd);
732 			exits(0);
733 		default:
734 			ralog("recvra6: unable to read router status on %s",
735 				conf.dev);
736 			break;
737 		}
738 	}
739 }
740 
741 /*
742  * return -1 -- error, reading/writing some file,
743  *         0 -- no arp table updates
744  *         1 -- successful arp table update
745  */
746 int
recvrs(uchar * buf,int pktlen,uchar * sol)747 recvrs(uchar *buf, int pktlen, uchar *sol)
748 {
749 	int n, optsz, arpfd;
750 	char abuf[256];
751 	Routersol *rs;
752 	Lladdropt *llao;
753 
754 	rs = (Routersol *)buf;
755 	n = sizeof *rs;
756 	optsz = pktlen - n;
757 	pkt2str(buf, buf+pktlen, abuf, abuf+nelem(abuf));
758 
759 	if (optsz != sizeof *llao)
760 		return 0;
761 	if (buf[n] != V6nd_srclladdr || 8*buf[n+1] != sizeof *llao) {
762 		ralog("rs opt err %s", abuf);
763 		return -1;
764 	}
765 
766 	ralog("rs recv %s", abuf);
767 
768 	if (memcmp(rs->src, v6Unspecified, IPaddrlen) == 0)
769 		return 0;
770 
771 	snprint(abuf, sizeof abuf, "%s/arp", conf.mpoint);
772 	arpfd = open(abuf, OWRITE);
773 	if (arpfd < 0) {
774 		ralog("recvrs: can't open %s/arp to write: %r", conf.mpoint);
775 		return -1;
776 	}
777 
778 	llao = (Lladdropt *)buf[n];
779 	n = snprint(abuf, sizeof abuf, "add ether %I %E", rs->src, llao->lladdr);
780 	if (write(arpfd, abuf, n) < n) {
781 		ralog("recvrs: can't write to %s/arp: %r", conf.mpoint);
782 		close(arpfd);
783 		return -1;
784 	}
785 
786 	memmove(sol, rs->src, IPaddrlen);
787 	close(arpfd);
788 	return 1;
789 }
790 
791 void
sendra(int fd,uchar * dst,int rlt)792 sendra(int fd, uchar *dst, int rlt)
793 {
794 	int pktsz, preflen;
795 	char abuf[1024], tmp[40];
796 	uchar buf[1024], macaddr[6], src[IPaddrlen];
797 	Ipifc *ifc = nil;
798 	Iplifc *lifc, *nlifc;
799 	Lladdropt *llao;
800 	Prefixopt *prfo;
801 	Routeradv *ra;
802 
803 	memset(buf, 0, sizeof buf);
804 	ra = (Routeradv *)buf;
805 
806 	myetheraddr(macaddr, conf.dev);
807 	ea2lla(src, macaddr);
808 	memmove(ra->src, src, IPaddrlen);
809 	memmove(ra->dst, dst, IPaddrlen);
810 	ra->type = ICMP6_RA;
811 	ra->cttl = conf.ttl;
812 
813 	if (conf.mflag > 0)
814 		ra->mor |= MFMASK;
815 	if (conf.oflag > 0)
816 		ra->mor |= OCMASK;
817 	if (rlt > 0)
818 		hnputs(ra->routerlt, conf.routerlt);
819 	else
820 		hnputs(ra->routerlt, 0);
821 	hnputl(ra->rchbltime, conf.reachtime);
822 	hnputl(ra->rxmtimer, conf.rxmitra);
823 
824 	pktsz = sizeof *ra;
825 
826 	/* include all global unicast prefixes on interface in prefix options */
827 	ifc = readipifc(conf.mpoint, ifc, myifc);
828 	for (lifc = (ifc? ifc->lifc: nil); lifc; lifc = nlifc) {
829 		nlifc = lifc->next;
830 		prfo = (Prefixopt *)(buf + pktsz);
831 		/* global unicast address? */
832 		if (!ISIPV6LINKLOCAL(lifc->ip) && !ISIPV6MCAST(lifc->ip) &&
833 		    memcmp(lifc->ip, IPnoaddr, IPaddrlen) != 0 &&
834 		    memcmp(lifc->ip, v6loopback, IPaddrlen) != 0 &&
835 		    !isv4(lifc->ip)) {
836 			memmove(prfo->pref, lifc->net, IPaddrlen);
837 
838 			/* hack to find prefix length */
839 			snprint(tmp, sizeof tmp, "%M", lifc->mask);
840 			preflen = atoi(&tmp[1]);
841 			prfo->plen = preflen & 0xff;
842 			if (prfo->plen == 0)
843 				continue;
844 
845 			prfo->type = V6nd_pfxinfo;
846 			prfo->len = 4;
847 			prfo->lar = AFMASK;
848 			hnputl(prfo->validlt, lifc->validlt);
849 			hnputl(prfo->preflt, lifc->preflt);
850 			pktsz += sizeof *prfo;
851 		}
852 	}
853 	/*
854 	 * include link layer address (mac address for now) in
855 	 * link layer address option
856 	 */
857 	llao = (Lladdropt *)(buf + pktsz);
858 	llao->type = V6nd_srclladdr;
859 	llao->len = 1;
860 	memmove(llao->lladdr, macaddr, sizeof macaddr);
861 	pktsz += sizeof *llao;
862 
863 	pkt2str(buf+40, buf+pktsz, abuf, abuf+1024);
864 	if(write(fd, buf, pktsz) < pktsz)
865 		ralog("sendra fail %s: %r", abuf);
866 	else if (debug)
867 		ralog("sendra succ %s", abuf);
868 }
869 
870 /*
871  * daemon to send router advertisements to hosts
872  */
873 void
sendra6(void)874 sendra6(void)
875 {
876 	int fd, cfd, n, dstknown = 0, sendracnt, sleepfor, nquitmsgs;
877 	long lastra, now;
878 	uchar buf[4096], dst[IPaddrlen];
879 	Ipifc *ifc = nil;
880 
881 	fd = dialicmp(v6allnodesL, ICMP6_RS, &cfd);
882 	if (fd < 0)
883 		sysfatal("can't open icmp_rs connection: %r");
884 
885 	notify(catch);
886 	sendracnt = Maxv6initras;
887 	nquitmsgs = Maxv6finalras;
888 
889 	switch(rfork(RFPROC|RFMEM|RFFDG|RFNOWAIT|RFNOTEG)){
890 	case -1:
891 		sysfatal("can't fork: %r");
892 	default:
893 		return;
894 	case 0:
895 		break;
896 	}
897 
898 	procsetname("sendra6 on %s", conf.dev);
899 	ralog("sendra6 on %s", conf.dev);
900 	sleepfor = jitter();
901 	for (;;) {
902 		lastra = time(0);
903 		if (sleepfor < 0)
904 			sleepfor = 0;
905 		alarm(sleepfor);
906 		n = read(fd, buf, sizeof buf);
907 		alarm(0);
908 
909 		ifc = readipifc(conf.mpoint, ifc, myifc);
910 		if (ifc == nil) {
911 			ralog("sendra6: can't read router params on %s",
912 				conf.mpoint);
913 			continue;
914 		}
915 
916 		if (ifc->sendra6 <= 0)
917 			if (nquitmsgs > 0) {
918 				sendra(fd, v6allnodesL, 0);
919 				nquitmsgs--;
920 				sleepfor = Minv6interradelay + jitter();
921 				continue;
922 			} else {
923 				ralog("sendra6: sendra off, quitting on %s",
924 					conf.dev);
925 				exits(0);
926 			}
927 
928 		nquitmsgs = Maxv6finalras;
929 
930 		if (n <= 0) {			/* no RS */
931 			if (sendracnt > 0)
932 				sendracnt--;
933 		} else {			/* respond to RS */
934 			dstknown = recvrs(buf, n, dst);
935 			now = time(0);
936 
937 			if (now - lastra < Minv6interradelay) {
938 				/* too close, skip */
939 				sleepfor = lastra + Minv6interradelay +
940 					jitter() - now;
941 				continue;
942 			}
943 			sleep(jitter());
944 		}
945 		sleepfor = randint(ifc->rp.minraint, ifc->rp.maxraint);
946 		if (dstknown > 0)
947 			sendra(fd, dst, 1);
948 		else
949 			sendra(fd, v6allnodesL, 1);
950 	}
951 }
952 
953 void
startra6(void)954 startra6(void)
955 {
956 	static char routeon[] = "iprouting 1";
957 
958 	if (conf.recvra > 0)
959 		recvra6();
960 
961 	if (conf.sendra > 0) {
962 		if (write(conf.cfd, routeon, sizeof routeon - 1) < 0) {
963 			warning("write (iprouting 1) failed: %r");
964 			return;
965 		}
966 		sendra6();
967 		if (conf.recvra <= 0)
968 			recvra6();
969 	}
970 }
971 
972 void
doipv6(int what)973 doipv6(int what)
974 {
975 	nip = nipifcs(conf.mpoint);
976 	if(!noconfig){
977 		lookforip(conf.mpoint);
978 		controldevice();
979 		binddevice();
980 	}
981 
982 	switch (what) {
983 	default:
984 		sysfatal("unknown IPv6 verb");
985 	case Vaddpref6:
986 		issueadd6(&conf);
987 		break;
988 	case Vra6:
989 		issuebasera6(&conf);
990 		issuerara6(&conf);
991 		dolog = 1;
992 		startra6();
993 		break;
994 	}
995 }
996