xref: /plan9/sys/src/cmd/ip/ipconfig/ipv6.c (revision e961f3039bf6d23366e7e2a4bf39abb552dedc4f)
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;
389 	char *p;
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 		if(cistrstr(p, buf) != 0) {
437 			warning("found dup entry in arp cache");
438 			dupfound = 1;
439 			break;
440 		}
441 	}
442 	Bterm(bp);
443 
444 	if (dupfound)
445 		doremove();
446 	else {
447 		n = sprint(buf, "add %I %M", conf.laddr, conf.mask);
448 		if(validip(conf.raddr)){
449 			n += snprint(buf+n, sizeof buf-n, " %I", conf.raddr);
450 			if(conf.mtu != 0)
451 				n += snprint(buf+n, sizeof buf-n, " %d",
452 					conf.mtu);
453 		}
454 		write(conf.cfd, buf, n);
455 	}
456 	return 0;
457 }
458 
459 static int
recvra6on(char * net,int conn)460 recvra6on(char *net, int conn)
461 {
462 	Ipifc* ifc;
463 
464 	ifc = readipifc(net, nil, conn);
465 	if (ifc == nil)
466 		return 0;
467 	else if (ifc->sendra6 > 0)
468 		return IsRouter;
469 	else if (ifc->recvra6 > 0)
470 		return IsHostRecv;
471 	else
472 		return IsHostNoRecv;
473 }
474 
475 /* send icmpv6 router solicitation to multicast address for all routers */
476 static void
sendrs(int fd)477 sendrs(int fd)
478 {
479 	Routersol *rs;
480 	uchar buff[sizeof *rs];
481 
482 	memset(buff, 0, sizeof buff);
483 	rs = (Routersol *)buff;
484 	memmove(rs->dst, v6allroutersL, IPaddrlen);
485 	memmove(rs->src, v6Unspecified, IPaddrlen);
486 	rs->type = ICMP6_RS;
487 
488 	if(write(fd, rs, sizeof buff) < sizeof buff)
489 		ralog("sendrs: write failed, pkt size %d", sizeof buff);
490 	else
491 		ralog("sendrs: sent solicitation to %I from %I on %s",
492 			rs->dst, rs->src, conf.dev);
493 }
494 
495 /*
496  * a router receiving a router adv from another
497  * router calls this; it is basically supposed to
498  * log the information in the ra and raise a flag
499  * if any parameter value is different from its configured values.
500  *
501  * doing nothing for now since I don't know where to log this yet.
502  */
503 static void
recvrarouter(uchar buf[],int pktlen)504 recvrarouter(uchar buf[], int pktlen)
505 {
506 	USED(buf, pktlen);
507 	ralog("i am a router and got a router advert");
508 }
509 
510 /* host receiving a router advertisement calls this */
511 
512 static void
ewrite(int fd,char * str)513 ewrite(int fd, char *str)
514 {
515 	int n;
516 
517 	n = strlen(str);
518 	if (write(fd, str, n) != n)
519 		ralog("write(%s) failed: %r", str);
520 }
521 
522 static void
issuebasera6(Conf * cf)523 issuebasera6(Conf *cf)
524 {
525 	char *cfg;
526 
527 	cfg = smprint("ra6 mflag %d oflag %d reachtime %d rxmitra %d "
528 		"ttl %d routerlt %d",
529 		cf->mflag, cf->oflag, cf->reachtime, cf->rxmitra,
530 		cf->ttl, cf->routerlt);
531 	ewrite(cf->cfd, cfg);
532 	free(cfg);
533 }
534 
535 static void
issuerara6(Conf * cf)536 issuerara6(Conf *cf)
537 {
538 	char *cfg;
539 
540 	cfg = smprint("ra6 sendra %d recvra %d maxraint %d minraint %d "
541 		"linkmtu %d",
542 		cf->sendra, cf->recvra, cf->maxraint, cf->minraint,
543 		cf->linkmtu);
544 	ewrite(cf->cfd, cfg);
545 	free(cfg);
546 }
547 
548 static void
issueadd6(Conf * cf)549 issueadd6(Conf *cf)
550 {
551 	char *cfg;
552 
553 	cfg = smprint("add6 %I %d %d %d %lud %lud", cf->v6pref, cf->prefixlen,
554 		cf->onlink, cf->autoflag, cf->validlt, cf->preflt);
555 	ewrite(cf->cfd, cfg);
556 	free(cfg);
557 }
558 
559 static void
recvrahost(uchar buf[],int pktlen)560 recvrahost(uchar buf[], int pktlen)
561 {
562 	int arpfd, m, n;
563 	char abuf[100];
564 	uchar optype;
565 	Lladdropt *llao;
566 	Mtuopt *mtuo;
567 	Prefixopt *prfo;
568 	Routeradv *ra;
569 	static int first = 1;
570 
571 	ra = (Routeradv*)buf;
572 //	memmove(conf.v6gaddr, ra->src, IPaddrlen);
573 	conf.ttl = ra->cttl;
574 	conf.mflag = (MFMASK & ra->mor);
575 	conf.oflag = (OCMASK & ra->mor);
576 	conf.routerlt =  nhgets(ra->routerlt);
577 	conf.reachtime = nhgetl(ra->rchbltime);
578 	conf.rxmitra =   nhgetl(ra->rxmtimer);
579 
580 //	issueadd6(&conf);		/* for conf.v6gaddr? */
581 	if (fprint(conf.cfd, "ra6 recvra 1") < 0)
582 		ralog("write(ra6 recvra 1) failed: %r");
583 	issuebasera6(&conf);
584 
585 	m = sizeof *ra;
586 	while (pktlen - m > 0) {
587 		optype = buf[m];
588 		switch (optype) {
589 		case V6nd_srclladdr:
590 			llao = (Lladdropt *)&buf[m];
591 			m += 8 * buf[m+1];
592 			if (llao->len != 1) {
593 				ralog("recvrahost: illegal len (%d) for source "
594 					"link layer address option", llao->len);
595 				return;
596 			}
597 			if (!ISIPV6LINKLOCAL(ra->src)) {
598 				ralog("recvrahost: non-link-local src addr for "
599 					"router adv %I", ra->src);
600 				return;
601 			}
602 
603 			snprint(abuf, sizeof abuf, "%s/arp", conf.mpoint);
604 			arpfd = open(abuf, OWRITE);
605 			if (arpfd < 0) {
606 				ralog("recvrahost: couldn't open %s to write: %r",
607 					abuf);
608 				return;
609 			}
610 
611 			n = snprint(abuf, sizeof abuf, "add ether %I %E",
612 				ra->src, llao->lladdr);
613 			if (write(arpfd, abuf, n) < n)
614 				ralog("recvrahost: couldn't write to %s/arp",
615 					conf.mpoint);
616 			close(arpfd);
617 			break;
618 		case V6nd_targlladdr:
619 		case V6nd_redirhdr:
620 			m += 8 * buf[m+1];
621 			ralog("ignoring unexpected option type `%s' in Routeradv",
622 				optname(optype));
623 			break;
624 		case V6nd_mtu:
625 			mtuo = (Mtuopt*)&buf[m];
626 			m += 8 * mtuo->len;
627 			conf.linkmtu = nhgetl(mtuo->mtu);
628 			break;
629 		case V6nd_pfxinfo:
630 			prfo = (Prefixopt*)&buf[m];
631 			m += 8 * prfo->len;
632 			if (prfo->len != 4) {
633 				ralog("illegal len (%d) for prefix option",
634 					prfo->len);
635 				return;
636 			}
637 			memmove(conf.v6pref, prfo->pref, IPaddrlen);
638 			conf.prefixlen = prfo->plen;
639 			conf.onlink =   ((prfo->lar & OLMASK) != 0);
640 			conf.autoflag = ((prfo->lar & AFMASK) != 0);
641 			conf.validlt = nhgetl(prfo->validlt);
642 			conf.preflt =  nhgetl(prfo->preflt);
643 			issueadd6(&conf);
644 			if (first) {
645 				first = 0;
646 				ralog("got initial RA from %I on %s; pfx %I",
647 					ra->src, conf.dev, prfo->pref);
648 			}
649 			break;
650 		default:
651 			if (debug)
652 				ralog("ignoring optype %d in Routeradv from %I",
653 					optype, ra->src);
654 			/* fall through */
655 		case V6nd_srcaddrs:
656 			/* netsbd sends this, so quietly ignore it for now */
657 			m += 8 * buf[m+1];
658 			break;
659 		}
660 	}
661 }
662 
663 /*
664  * daemon to receive router advertisements from routers
665  */
666 void
recvra6(void)667 recvra6(void)
668 {
669 	int fd, cfd, n, sendrscnt, sleepfor;
670 	uchar buf[4096];
671 
672 	/* TODO: why not v6allroutersL? */
673 	fd = dialicmp(v6allnodesL, ICMP6_RA, &cfd);
674 	if (fd < 0)
675 		sysfatal("can't open icmp_ra connection: %r");
676 
677 	notify(catch);
678 	sendrscnt = Maxv6rss;
679 
680 	switch(rfork(RFPROC|RFMEM|RFFDG|RFNOWAIT|RFNOTEG)){
681 	case -1:
682 		sysfatal("can't fork: %r");
683 	default:
684 		return;
685 	case 0:
686 		break;
687 	}
688 
689 	procsetname("recvra6 on %s", conf.dev);
690 	ralog("recvra6 on %s", conf.dev);
691 	sleepfor = jitter();
692 	for (;;) {
693 		/*
694 		 * We only get 3 (Maxv6rss) tries, so make sure we
695 		 * wait long enough to be certain that at least one RA
696 		 * will be transmitted.
697 		 */
698 		if (sleepfor < 7000)
699 			sleepfor = 7000;
700 		alarm(sleepfor);
701 		n = read(fd, buf, sizeof buf);
702 		alarm(0);
703 		if (n <= 0) {
704 			if (sendrscnt > 0) {
705 				sendrscnt--;
706 				if (recvra6on(conf.mpoint, myifc) == IsHostRecv)
707 					sendrs(fd);
708 				sleepfor = V6rsintvl + nrand(100);
709 			}
710 			if (sendrscnt == 0) {
711 				sendrscnt--;
712 				sleepfor = 0;
713 				ralog("recvra6: no router advs after %d sols on %s",
714 					Maxv6rss, conf.dev);
715 			}
716 			continue;
717 		}
718 
719 		sleepfor = 0;
720 		sendrscnt = -1;		/* got at least initial ra; no whining */
721 		switch (recvra6on(conf.mpoint, myifc)) {
722 		case IsRouter:
723 			recvrarouter(buf, n);
724 			break;
725 		case IsHostRecv:
726 			recvrahost(buf, n);
727 			break;
728 		case IsHostNoRecv:
729 			ralog("recvra6: recvra off, quitting on %s", conf.dev);
730 			close(fd);
731 			exits(0);
732 		default:
733 			ralog("recvra6: unable to read router status on %s",
734 				conf.dev);
735 			break;
736 		}
737 	}
738 }
739 
740 /*
741  * return -1 -- error, reading/writing some file,
742  *         0 -- no arp table updates
743  *         1 -- successful arp table update
744  */
745 int
recvrs(uchar * buf,int pktlen,uchar * sol)746 recvrs(uchar *buf, int pktlen, uchar *sol)
747 {
748 	int n, optsz, arpfd;
749 	char abuf[256];
750 	Routersol *rs;
751 	Lladdropt *llao;
752 
753 	rs = (Routersol *)buf;
754 	n = sizeof *rs;
755 	optsz = pktlen - n;
756 	pkt2str(buf, buf+pktlen, abuf, abuf+nelem(abuf));
757 
758 	if (optsz != sizeof *llao)
759 		return 0;
760 	if (buf[n] != V6nd_srclladdr || 8*buf[n+1] != sizeof *llao) {
761 		ralog("rs opt err %s", abuf);
762 		return -1;
763 	}
764 
765 	ralog("rs recv %s", abuf);
766 
767 	if (memcmp(rs->src, v6Unspecified, IPaddrlen) == 0)
768 		return 0;
769 
770 	snprint(abuf, sizeof abuf, "%s/arp", conf.mpoint);
771 	arpfd = open(abuf, OWRITE);
772 	if (arpfd < 0) {
773 		ralog("recvrs: can't open %s/arp to write: %r", conf.mpoint);
774 		return -1;
775 	}
776 
777 	llao = (Lladdropt *)buf[n];
778 	n = snprint(abuf, sizeof abuf, "add ether %I %E", rs->src, llao->lladdr);
779 	if (write(arpfd, abuf, n) < n) {
780 		ralog("recvrs: can't write to %s/arp: %r", conf.mpoint);
781 		close(arpfd);
782 		return -1;
783 	}
784 
785 	memmove(sol, rs->src, IPaddrlen);
786 	close(arpfd);
787 	return 1;
788 }
789 
790 void
sendra(int fd,uchar * dst,int rlt)791 sendra(int fd, uchar *dst, int rlt)
792 {
793 	int pktsz, preflen;
794 	char abuf[1024], tmp[40];
795 	uchar buf[1024], macaddr[6], src[IPaddrlen];
796 	Ipifc *ifc = nil;
797 	Iplifc *lifc, *nlifc;
798 	Lladdropt *llao;
799 	Prefixopt *prfo;
800 	Routeradv *ra;
801 
802 	memset(buf, 0, sizeof buf);
803 	ra = (Routeradv *)buf;
804 
805 	myetheraddr(macaddr, conf.dev);
806 	ea2lla(src, macaddr);
807 	memmove(ra->src, src, IPaddrlen);
808 	memmove(ra->dst, dst, IPaddrlen);
809 	ra->type = ICMP6_RA;
810 	ra->cttl = conf.ttl;
811 
812 	if (conf.mflag > 0)
813 		ra->mor |= MFMASK;
814 	if (conf.oflag > 0)
815 		ra->mor |= OCMASK;
816 	if (rlt > 0)
817 		hnputs(ra->routerlt, conf.routerlt);
818 	else
819 		hnputs(ra->routerlt, 0);
820 	hnputl(ra->rchbltime, conf.reachtime);
821 	hnputl(ra->rxmtimer, conf.rxmitra);
822 
823 	pktsz = sizeof *ra;
824 
825 	/* include all global unicast prefixes on interface in prefix options */
826 	ifc = readipifc(conf.mpoint, ifc, myifc);
827 	for (lifc = (ifc? ifc->lifc: nil); lifc; lifc = nlifc) {
828 		nlifc = lifc->next;
829 		prfo = (Prefixopt *)(buf + pktsz);
830 		/* global unicast address? */
831 		if (!ISIPV6LINKLOCAL(lifc->ip) && !ISIPV6MCAST(lifc->ip) &&
832 		    memcmp(lifc->ip, IPnoaddr, IPaddrlen) != 0 &&
833 		    memcmp(lifc->ip, v6loopback, IPaddrlen) != 0 &&
834 		    !isv4(lifc->ip)) {
835 			memmove(prfo->pref, lifc->net, IPaddrlen);
836 
837 			/* hack to find prefix length */
838 			snprint(tmp, sizeof tmp, "%M", lifc->mask);
839 			preflen = atoi(&tmp[1]);
840 			prfo->plen = preflen & 0xff;
841 			if (prfo->plen == 0)
842 				continue;
843 
844 			prfo->type = V6nd_pfxinfo;
845 			prfo->len = 4;
846 			prfo->lar = AFMASK;
847 			hnputl(prfo->validlt, lifc->validlt);
848 			hnputl(prfo->preflt, lifc->preflt);
849 			pktsz += sizeof *prfo;
850 		}
851 	}
852 	/*
853 	 * include link layer address (mac address for now) in
854 	 * link layer address option
855 	 */
856 	llao = (Lladdropt *)(buf + pktsz);
857 	llao->type = V6nd_srclladdr;
858 	llao->len = 1;
859 	memmove(llao->lladdr, macaddr, sizeof macaddr);
860 	pktsz += sizeof *llao;
861 
862 	pkt2str(buf+40, buf+pktsz, abuf, abuf+1024);
863 	if(write(fd, buf, pktsz) < pktsz)
864 		ralog("sendra fail %s: %r", abuf);
865 	else if (debug)
866 		ralog("sendra succ %s", abuf);
867 }
868 
869 /*
870  * daemon to send router advertisements to hosts
871  */
872 void
sendra6(void)873 sendra6(void)
874 {
875 	int fd, cfd, n, dstknown = 0, sendracnt, sleepfor, nquitmsgs;
876 	long lastra, now;
877 	uchar buf[4096], dst[IPaddrlen];
878 	Ipifc *ifc = nil;
879 
880 	fd = dialicmp(v6allnodesL, ICMP6_RS, &cfd);
881 	if (fd < 0)
882 		sysfatal("can't open icmp_rs connection: %r");
883 
884 	notify(catch);
885 	sendracnt = Maxv6initras;
886 	nquitmsgs = Maxv6finalras;
887 
888 	switch(rfork(RFPROC|RFMEM|RFFDG|RFNOWAIT|RFNOTEG)){
889 	case -1:
890 		sysfatal("can't fork: %r");
891 	default:
892 		return;
893 	case 0:
894 		break;
895 	}
896 
897 	procsetname("sendra6 on %s", conf.dev);
898 	ralog("sendra6 on %s", conf.dev);
899 	sleepfor = jitter();
900 	for (;;) {
901 		lastra = time(0);
902 		if (sleepfor < 0)
903 			sleepfor = 0;
904 		alarm(sleepfor);
905 		n = read(fd, buf, sizeof buf);
906 		alarm(0);
907 
908 		ifc = readipifc(conf.mpoint, ifc, myifc);
909 		if (ifc == nil) {
910 			ralog("sendra6: can't read router params on %s",
911 				conf.mpoint);
912 			continue;
913 		}
914 
915 		if (ifc->sendra6 <= 0)
916 			if (nquitmsgs > 0) {
917 				sendra(fd, v6allnodesL, 0);
918 				nquitmsgs--;
919 				sleepfor = Minv6interradelay + jitter();
920 				continue;
921 			} else {
922 				ralog("sendra6: sendra off, quitting on %s",
923 					conf.dev);
924 				exits(0);
925 			}
926 
927 		nquitmsgs = Maxv6finalras;
928 
929 		if (n <= 0) {			/* no RS */
930 			if (sendracnt > 0)
931 				sendracnt--;
932 		} else {			/* respond to RS */
933 			dstknown = recvrs(buf, n, dst);
934 			now = time(0);
935 
936 			if (now - lastra < Minv6interradelay) {
937 				/* too close, skip */
938 				sleepfor = lastra + Minv6interradelay +
939 					jitter() - now;
940 				continue;
941 			}
942 			sleep(jitter());
943 		}
944 		sleepfor = randint(ifc->rp.minraint, ifc->rp.maxraint);
945 		if (dstknown > 0)
946 			sendra(fd, dst, 1);
947 		else
948 			sendra(fd, v6allnodesL, 1);
949 	}
950 }
951 
952 void
startra6(void)953 startra6(void)
954 {
955 	static char routeon[] = "iprouting 1";
956 
957 	if (conf.recvra > 0)
958 		recvra6();
959 
960 	if (conf.sendra > 0) {
961 		if (write(conf.cfd, routeon, sizeof routeon - 1) < 0) {
962 			warning("write (iprouting 1) failed: %r");
963 			return;
964 		}
965 		sendra6();
966 		if (conf.recvra <= 0)
967 			recvra6();
968 	}
969 }
970 
971 void
doipv6(int what)972 doipv6(int what)
973 {
974 	nip = nipifcs(conf.mpoint);
975 	if(!noconfig){
976 		lookforip(conf.mpoint);
977 		controldevice();
978 		binddevice();
979 	}
980 
981 	switch (what) {
982 	default:
983 		sysfatal("unknown IPv6 verb");
984 	case Vaddpref6:
985 		issueadd6(&conf);
986 		break;
987 	case Vra6:
988 		issuebasera6(&conf);
989 		issuerara6(&conf);
990 		dolog = 1;
991 		startra6();
992 		break;
993 	}
994 }
995