| /* | 
 |  * This file is part of the UCB release of Plan 9. It is subject to the license | 
 |  * terms in the LICENSE file found in the top-level directory of this | 
 |  * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No | 
 |  * part of the UCB release of Plan 9, including this file, may be copied, | 
 |  * modified, propagated, or distributed except according to the terms contained | 
 |  * in the LICENSE file. | 
 |  */ | 
 |  | 
 | // | 
 | // ipconfig for IPv6 | 
 | //	RS means Router Solicitation | 
 | //	RA means Router Advertisement | 
 | // | 
 |  | 
 | #include <parlib/alarm.h> | 
 | #include <iplib/iplib.h> | 
 | #include <parlib/common.h> | 
 | #include <parlib/parlib.h> | 
 | #include <parlib/uthread.h> | 
 |  | 
 | #include <ctype.h> | 
 | #include <fcntl.h> | 
 | #include <pthread.h> | 
 | #include <stdio.h> | 
 | #include <stdlib.h> | 
 | #include <string.h> | 
 | #include <time.h> | 
 | #include <unistd.h> | 
 | #include <stdarg.h> | 
 |  | 
 | #include "icmp.h" | 
 | #include "ipconfig.h" | 
 |  | 
 | #define RALOG "v6routeradv" | 
 |  | 
 | #define NetS(x) (((uint8_t *)x)[0] << 8 | ((uint8_t *)x)[1]) | 
 | #define NetL(x) \ | 
 | 	(((uint8_t *)x)[0] << 24 | ((uint8_t *)x)[1] << 16 | \ | 
 | 	 ((uint8_t *)x)[2] << 8 | ((uint8_t *)x)[3]) | 
 |  | 
 | enum { | 
 | 	ICMP6LEN = 4, | 
 | }; | 
 |  | 
 | // ICMP v4 & v6 header | 
 | struct icmphdr { | 
 | 	uint8_t type; | 
 | 	uint8_t code; | 
 | 	uint8_t cksum[2];    // Checksum | 
 | 	uint8_t data[]; | 
 | }; | 
 |  | 
 | char *icmpmsg6[Maxtype6 + 1] = { | 
 |         [EchoReply] "EchoReply", | 
 |         [UnreachableV6] "UnreachableV6", | 
 |         [PacketTooBigV6] "PacketTooBigV6", | 
 |         [TimeExceedV6] "TimeExceedV6", | 
 |         [Redirect] "Redirect", | 
 |         [EchoRequest] "EchoRequest", | 
 |         [TimeExceed] "TimeExceed", | 
 |         [InParmProblem] "InParmProblem", | 
 |         [Timestamp] "Timestamp", | 
 |         [TimestampReply] "TimestampReply", | 
 |         [InfoRequest] "InfoRequest", | 
 |         [InfoReply] "InfoReply", | 
 |         [AddrMaskRequest] "AddrMaskRequest", | 
 |         [AddrMaskReply] "AddrMaskReply", | 
 |         [EchoRequestV6] "EchoRequestV6", | 
 |         [EchoReplyV6] "EchoReplyV6", | 
 |         [RouterSolicit] "RouterSolicit", | 
 |         [RouterAdvert] "RouterAdvert", | 
 |         [NbrSolicit] "NbrSolicit", | 
 |         [NbrAdvert] "NbrAdvert", | 
 |         [RedirectV6] "RedirectV6", | 
 | }; | 
 |  | 
 | static char *icmp6opts[] = { | 
 |         [0] "unknown option", | 
 |         [V6nd_srclladdr] "srcll_addr", | 
 |         [V6nd_targlladdr] "targll_addr", | 
 |         [V6nd_pfxinfo] "prefix", | 
 |         [V6nd_redirhdr] "redirect", | 
 |         [V6nd_mtu] "mtu", | 
 |         [V6nd_home] "home", | 
 |         [V6nd_srcaddrs] "src_addrs", | 
 |         [V6nd_ip] "ip", | 
 |         [V6nd_rdns] "rdns", | 
 |         [V6nd_9fs] "9fs", | 
 |         [V6nd_9auth] "9auth", | 
 | }; | 
 |  | 
 | uint8_t v6allroutersL[IPaddrlen] = { | 
 |     0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x02 | 
 | }; | 
 |  | 
 | uint8_t v6allnodesL[IPaddrlen] = { | 
 |     0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01 | 
 | }; | 
 |  | 
 | uint8_t v6Unspecified[IPaddrlen] = { | 
 |     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 | 
 | }; | 
 |  | 
 | uint8_t v6loopback[IPaddrlen] = { | 
 |     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 | 
 | }; | 
 |  | 
 | uint8_t v6glunicast[IPaddrlen] = { | 
 |     0x08, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 | 
 | }; | 
 |  | 
 | uint8_t v6linklocal[IPaddrlen] = { | 
 |     0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 | 
 | }; | 
 |  | 
 | uint8_t v6solpfx[IPaddrlen] = { | 
 |     0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, | 
 |     // last 3 bytes filled with low-order bytes of addr being solicited | 
 |     0xff, 0, 0, 0, | 
 | }; | 
 |  | 
 | uint8_t v6defmask[IPaddrlen] = { | 
 |     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0 | 
 | }; | 
 |  | 
 | enum { | 
 | 	Vadd, | 
 | 	Vremove, | 
 | 	Vunbind, | 
 | 	Vaddpref6, | 
 | 	Vra6, | 
 | }; | 
 |  | 
 | static void ralog(char *fmt, ...) | 
 | { | 
 | 	char msg[512]; | 
 | 	va_list arg; | 
 |  | 
 | 	va_start(arg, fmt); | 
 | 	vsnprintf(msg, sizeof(msg), fmt, arg); | 
 | 	va_end(arg); | 
 | 	fprintf(stderr, RALOG ": %s\n", msg); | 
 | } | 
 |  | 
 | void ea2lla(uint8_t *lla, uint8_t *ea) | 
 | { | 
 | 	assert(IPaddrlen == 16); | 
 | 	memset(lla, 0, IPaddrlen); | 
 | 	lla[0] = 0xFE; | 
 | 	lla[1] = 0x80; | 
 | 	lla[8] = ea[0] | 0x2; | 
 | 	lla[9] = ea[1]; | 
 | 	lla[10] = ea[2]; | 
 | 	lla[11] = 0xFF; | 
 | 	lla[12] = 0xFE; | 
 | 	lla[13] = ea[3]; | 
 | 	lla[14] = ea[4]; | 
 | 	lla[15] = ea[5]; | 
 | } | 
 |  | 
 | void ipv62smcast(uint8_t *smcast, uint8_t *a) | 
 | { | 
 | 	assert(IPaddrlen == 16); | 
 | 	memset(smcast, 0, IPaddrlen); | 
 | 	smcast[0] = 0xFF; | 
 | 	smcast[1] = 0x02; | 
 | 	smcast[11] = 0x1; | 
 | 	smcast[12] = 0xFF; | 
 | 	smcast[13] = a[13]; | 
 | 	smcast[14] = a[14]; | 
 | 	smcast[15] = a[15]; | 
 | } | 
 |  | 
 | void v6paraminit(struct conf *cf) | 
 | { | 
 | 	cf->sendra = cf->recvra = 0; | 
 | 	cf->mflag = 0; | 
 | 	cf->oflag = 0; | 
 | 	cf->maxraint = Maxv6initraintvl; | 
 | 	cf->minraint = Maxv6initraintvl / 4; | 
 | 	cf->linkmtu = 1500; | 
 | 	cf->reachtime = V6reachabletime; | 
 | 	cf->rxmitra = V6retranstimer; | 
 | 	cf->ttl = MAXTTL; | 
 |  | 
 | 	cf->routerlt = 0; | 
 |  | 
 | 	cf->prefixlen = 64; | 
 | 	cf->onlink = 0; | 
 | 	cf->autoflag = 0; | 
 | 	cf->validlt = cf->preflt = ~0L; | 
 | } | 
 |  | 
 | static char *optname(unsigned opt) | 
 | { | 
 | 	static char buf[32]; | 
 |  | 
 | 	if (opt >= COUNT_OF(icmp6opts) || icmp6opts[opt] == NULL) { | 
 | 		snprintf(buf, sizeof(buf), "unknown option %d", opt); | 
 | 		return buf; | 
 | 	} | 
 |  | 
 | 	return icmp6opts[opt]; | 
 | } | 
 |  | 
 | size_t opt_sprint(uint8_t *ps, uint8_t *pe, char *buf, size_t size) | 
 | { | 
 | 	int otype, osz, pktsz; | 
 | 	uint8_t *a; | 
 | 	size_t n; | 
 |  | 
 | 	a = ps; | 
 | 	n = 0; | 
 | 	for (pktsz = pe - ps; pktsz > 0; pktsz -= osz) { | 
 | 		otype = a[0]; | 
 | 		osz = a[1] * 8; | 
 |  | 
 | 		switch (otype) { | 
 | 		default: | 
 | 			n += snprintf(buf + n, size - n, " option=%s ", | 
 | 				      optname(otype)); | 
 | 		case V6nd_srclladdr: | 
 | 		case V6nd_targlladdr: | 
 | 			if (pktsz < osz || osz != 8) { | 
 | 				n += snprintf(buf + n, size - n, | 
 | 				              " option=%s bad size=%d", | 
 | 				              optname(otype), osz); | 
 | 				return n; | 
 | 			} | 
 | 			n += snprintf(buf + n, size - n, " option=%s maddr=%E", | 
 | 			              optname(otype), a + 2); | 
 | 			break; | 
 | 		case V6nd_pfxinfo: | 
 | 			if (pktsz < osz || osz != 32) { | 
 | 				n += snprintf(buf + n, size - n, | 
 | 				              " option=%s: bad size=%d", | 
 | 				              optname(otype), osz); | 
 | 				return n; | 
 | 			} | 
 | 			n += snprintf(buf, size - n, | 
 | 			              " option=%s pref=%R preflen=%3.3d lflag=%1.1d aflag=%1.1d unused1=%1.1d validlt=%ud preflt=%ud unused2=%1.1d", | 
 | 			              optname(otype), | 
 | 			              a + 16, | 
 | 			              (int)(*(a + 2)), | 
 | 			              (*(a + 3) & (1 << 7)) != 0, | 
 | 			              (*(a + 3) & (1 << 6)) != 0, | 
 | 			              (*(a + 3) & 63) != 0, | 
 | 			              NetL(a + 4), | 
 | 			              NetL(a + 8), | 
 | 			              NetL(a + 12) != 0); | 
 | 			break; | 
 | 		} | 
 | 		a += osz; | 
 | 	} | 
 |  | 
 | 	return n; | 
 | } | 
 |  | 
 | static void pkt2str(uint8_t *ps, uint8_t *pe, char *buf, size_t size) | 
 | { | 
 | 	int pktlen; | 
 | 	char *tn; | 
 | 	uint8_t *a; | 
 | 	struct icmphdr *h; | 
 | 	size_t n; | 
 |  | 
 | 	h = (struct icmphdr *)ps; | 
 | 	a = ps + 4; | 
 |  | 
 | 	pktlen = pe - ps; | 
 | 	if (pktlen < ICMP6LEN) { | 
 | 		snprintf(buf, size, "short pkt"); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	tn = icmpmsg6[h->type]; | 
 | 	if (tn == NULL) | 
 | 		n = snprintf(buf, size, "t=%ud c=%d ck=%4.4ux", | 
 | 		             h->type, h->code, (uint16_t)NetS(h->cksum)); | 
 | 	else | 
 | 		n = snprintf(buf, size, "t=%s c=%d ck=%4.4ux", | 
 | 		             tn, h->code, (uint16_t)NetS(h->cksum)); | 
 |  | 
 | 	switch (h->type) { | 
 | 	case RouterSolicit: | 
 | 		ps += 8; | 
 | 		n += snprintf(buf + n, size - n, " unused=%1.1d ", | 
 | 			      NetL(a) != 0); | 
 | 		opt_sprint(ps, pe, buf + n, size - n); | 
 | 		break; | 
 | 	case RouterAdvert: | 
 | 		ps += 16; | 
 | 		n += snprintf(buf + n, size - n, | 
 | 		              " hoplim=%3.3d mflag=%1.1d oflag=%1.1d unused=%1.1d routerlt=%d reachtime=%d rxmtimer=%d", | 
 | 		              a[0], | 
 | 		              (*(a + 1) & (1 << 7)) != 0, | 
 | 		              (*(a + 1) & (1 << 6)) != 0, | 
 | 		              (*(a + 1) & 63) != 0, | 
 | 		              NetS(a + 2), | 
 | 		              NetL(a + 4), | 
 | 		              NetL(a + 8)); | 
 | 		opt_sprint(ps, pe, buf + n, size - n); | 
 | 		break; | 
 | 	default: | 
 | 		snprintf(buf + n, size - n, " unexpected icmp6 pkt type"); | 
 | 		break; | 
 | 	} | 
 | } | 
 |  | 
 | int dialicmp(uint8_t *dst, int dport, int *ctlfd) | 
 | { | 
 | 	int fd, cfd, n, m; | 
 | 	char cmsg[100], name[128], connind[40]; | 
 | 	char hdrs[] = "headers"; | 
 |  | 
 | 	snprintf(name, sizeof(name), "%s/icmpv6/clone", conf.mpoint); | 
 | 	cfd = open(name, O_RDWR); | 
 | 	if (cfd < 0) { | 
 | 		fprintf(stderr, "dialicmp: can't open %s: %r", name); | 
 | 		evexit(-1); | 
 | 	} | 
 |  | 
 | 	n = snprintf(cmsg, sizeof(cmsg), "connect %R!%d!r %d", dst, dport, | 
 | 		     dport); | 
 | 	m = write(cfd, cmsg, n); | 
 | 	if (m < n) { | 
 | 		fprintf(stderr, "dialicmp: can't write %s to %s: %r", cmsg, | 
 | 			name); | 
 | 		evexit(-1); | 
 | 	} | 
 |  | 
 | 	lseek(cfd, 0, 0); | 
 | 	n = read(cfd, connind, sizeof(connind)); | 
 | 	if (n < 0) | 
 | 		connind[0] = 0; | 
 | 	else if (n < sizeof(connind)) | 
 | 		connind[n] = 0; | 
 | 	else | 
 | 		connind[sizeof(connind) - 1] = 0; | 
 |  | 
 | 	snprintf(name, sizeof(name), "%s/icmpv6/%s/data", conf.mpoint, connind); | 
 | 	fd = open(name, O_RDWR); | 
 | 	if (fd < 0) { | 
 | 		fprintf(stderr, "dialicmp: can't open %s: %r", name); | 
 | 		evexit(-1); | 
 | 	} | 
 |  | 
 | 	n = sizeof(hdrs) - 1; | 
 | 	if (write(cfd, hdrs, n) < n) { | 
 | 		fprintf(stderr, "dialicmp: can't write `%s' to %s: %r", hdrs, | 
 | 			name); | 
 | 		evexit(-1); | 
 | 	} | 
 | 	*ctlfd = cfd; | 
 | 	return fd; | 
 | } | 
 |  | 
 | // Add an IPv6 address to an interface. | 
 | int ip6cfg(int autoconf) | 
 | { | 
 | 	int dupfound = 0, n; | 
 | 	char *p, *s; | 
 | 	char buf[256], line[1024]; | 
 | 	uint8_t ethaddr[6]; | 
 | 	FILE *bp; | 
 |  | 
 | 	if (autoconf) { | 
 | 		// create link-local addr | 
 | 		if (myetheraddr(ethaddr, conf.dev) < 0) { | 
 | 			fprintf(stderr, "myetheraddr w/ %s failed: %r", | 
 | 				conf.dev); | 
 | 			evexit(-1); | 
 | 		} | 
 | 		ea2lla(conf.laddr, ethaddr); | 
 | 	} | 
 |  | 
 | 	if (dupl_disc) | 
 | 		n = snprintf(buf, sizeof(buf), "try"); | 
 | 	else | 
 | 		n = snprintf(buf, sizeof(buf), "add"); | 
 |  | 
 | 	n += snprintf(buf + n, sizeof(buf) - n, " %R", conf.laddr); | 
 | 	if (!validip(conf.mask)) | 
 | 		ipmove(conf.mask, v6defmask); | 
 | 	n += snprintf(buf + n, sizeof(buf) - n, " %M", conf.mask); | 
 | 	if (validip(conf.raddr)) { | 
 | 		n += snprintf(buf + n, sizeof(buf) - n, " %R", conf.raddr); | 
 | 		if (conf.mtu != 0) | 
 | 			n += snprintf(buf + n, sizeof(buf) - n, " %d", | 
 | 				      conf.mtu); | 
 | 	} | 
 |  | 
 | 	if (write(conf.cfd, buf, n) < 0) { | 
 | 		warning("write(%s): %r", buf); | 
 | 		return -1; | 
 | 	} | 
 |  | 
 | 	if (!dupl_disc) | 
 | 		return 0; | 
 |  | 
 | 	usleep(3000 * 1000); | 
 |  | 
 | 	/* read arp table, look for addr duplication */ | 
 | 	snprintf(buf, sizeof(buf), "%s/arp", conf.mpoint); | 
 | 	bp = fopen(buf, "r"); | 
 | 	if (bp == NULL) { | 
 | 		warning("couldn't open %s: %r", buf); | 
 | 		return -1; | 
 | 	} | 
 |  | 
 | 	snprintf(buf, sizeof(buf), "%R", conf.laddr); | 
 | 	for (p = buf; *p != '\0'; p++) | 
 | 		if (isascii(*p) && isalpha(*p) && isupper(*p)) | 
 | 			*p = tolower(*p); | 
 | 	while ((s = fgets(line, sizeof(line), bp)) != NULL) { | 
 | 		s[strlen(line) - 1] = 0; | 
 | 		for (p = buf; *p != '\0'; p++) | 
 | 			if (isascii(*p) && isalpha(*p) && isupper(*p)) | 
 | 				*p = tolower(*p); | 
 | 		if (strstr(s, buf) != 0) { | 
 | 			warning("found dup entry in arp cache"); | 
 | 			dupfound = 1; | 
 | 			break; | 
 | 		} | 
 | 	} | 
 | 	fclose(bp); | 
 |  | 
 | 	if (dupfound) | 
 | 		doremove(); | 
 | 	else { | 
 | 		n = snprintf(buf, sizeof(buf), "add %R %M", conf.laddr, | 
 | 			     conf.mask); | 
 | 		if (validip(conf.raddr)) { | 
 | 			n += snprintf(buf + n, sizeof(buf) - n, " %R", | 
 | 				      conf.raddr); | 
 | 			if (conf.mtu != 0) | 
 | 				n += snprintf(buf + n, sizeof(buf) - n, " %d", | 
 | 					      conf.mtu); | 
 | 		} | 
 | 		write(conf.cfd, buf, n); | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int recvra6on(char *net, int conn) | 
 | { | 
 | 	struct ipifc *ifc; | 
 |  | 
 | 	ifc = readipifc(net, NULL, conn); | 
 | 	if (ifc == NULL) | 
 | 		return 0; | 
 | 	else if (ifc->sendra6 > 0) | 
 | 		return IsRouter; | 
 | 	else if (ifc->recvra6 > 0) | 
 | 		return IsHostRecv; | 
 | 	else | 
 | 		return IsHostNoRecv; | 
 | } | 
 |  | 
 | /* send icmpv6 router solicitation to multicast address for all routers */ | 
 | static void sendrs(int fd) | 
 | { | 
 | 	struct routersol *rs; | 
 | 	uint8_t buff[sizeof(*rs)]; | 
 |  | 
 | 	memset(buff, 0, sizeof(buff)); | 
 | 	rs = (struct routersol *)buff; | 
 | 	memmove(rs->dst, v6allroutersL, IPaddrlen); | 
 | 	memmove(rs->src, v6Unspecified, IPaddrlen); | 
 | 	rs->type = ICMP6_RS; | 
 |  | 
 | 	if (write(fd, rs, sizeof(buff)) < sizeof(buff)) | 
 | 		ralog("sendrs: write failed, pkt size %d", sizeof(buff)); | 
 | 	else | 
 | 		ralog("sendrs: sent solicitation to %R from %R on %s", rs->dst, | 
 | 		      rs->src, conf.dev); | 
 | } | 
 |  | 
 | /* | 
 |  * a router receiving a router adv from another | 
 |  * router calls this; it is basically supposed to | 
 |  * log the information in the ra and raise a flag | 
 |  * if any parameter value is different from its configured values. | 
 |  * | 
 |  * doing nothing for now since I don't know where to log this yet. | 
 |  */ | 
 | static void recvrarouter(uint8_t buf[], int pktlen) | 
 | { | 
 | 	ralog("i am a router and got a router advert"); | 
 | } | 
 |  | 
 | // host receiving a router advertisement calls this | 
 | static void ewrite(int fd, char *str) | 
 | { | 
 | 	int n; | 
 |  | 
 | 	n = strlen(str); | 
 | 	if (write(fd, str, n) != n) | 
 | 		ralog("write(%s) failed: %r", str); | 
 | } | 
 |  | 
 | static void issuebasera6(struct conf *cf) | 
 | { | 
 | 	char cfg[256]; | 
 |  | 
 | 	snprintf(cfg, sizeof(cfg), | 
 | 	         "ra6 mflag %d oflag %d reachtime %d rxmitra %d ttl %d routerlt %d", | 
 | 	         cf->mflag, cf->oflag, cf->reachtime, cf->rxmitra, | 
 | 	         cf->ttl, cf->routerlt); | 
 | 	ewrite(cf->cfd, cfg); | 
 | } | 
 |  | 
 | static void issuerara6(struct conf *cf) | 
 | { | 
 | 	char cfg[256]; | 
 |  | 
 | 	snprintf(cfg, sizeof(cfg), | 
 | 	         "ra6 sendra %d recvra %d maxraint %d minraint %d linkmtu %d", | 
 | 		 cf->sendra, cf->recvra, cf->maxraint, cf->minraint, | 
 | 		 cf->linkmtu); | 
 | 	ewrite(cf->cfd, cfg); | 
 | } | 
 |  | 
 | static void | 
 | issueadd6(struct conf *cf) | 
 | { | 
 | 	char cfg[256]; | 
 |  | 
 | 	snprintf(cfg, sizeof(cfg), | 
 | 	         "add6 %R %d %d %d %lud %lud", cf->v6pref, cf->prefixlen, | 
 | 	         cf->onlink, cf->autoflag, cf->validlt, cf->preflt); | 
 | 	ewrite(cf->cfd, cfg); | 
 | } | 
 |  | 
 | static void | 
 | recvrahost(uint8_t buf[], int pktlen) | 
 | { | 
 | 	int arpfd, m, n; | 
 | 	char abuf[100], *msg; | 
 | 	uint8_t optype; | 
 | 	struct lladdropt *llao; | 
 | 	struct mtuopt *mtuo; | 
 | 	struct prefixopt *prfo; | 
 | 	struct routeradv *ra; | 
 | 	static int first = 1; | 
 |  | 
 | 	ra = (struct routeradv *)buf; | 
 | 	//	memmove(conf.v6gaddr, ra->src, IPaddrlen); | 
 | 	conf.ttl = ra->cttl; | 
 | 	conf.mflag = (MFMASK & ra->mor); | 
 | 	conf.oflag = (OCMASK & ra->mor); | 
 | 	conf.routerlt = nhgets(ra->routerlt); | 
 | 	conf.reachtime = nhgetl(ra->rchbltime); | 
 | 	conf.rxmitra = nhgetl(ra->rxmtimer); | 
 |  | 
 | 	//	issueadd6(&conf);		/* for conf.v6gaddr? */ | 
 | 	msg = "ra6 recvra 1"; | 
 | 	if (write(conf.cfd, msg, strlen(msg)) < 0) | 
 | 		ralog("write(ra6 recvra 1) failed: %r"); | 
 | 	issuebasera6(&conf); | 
 |  | 
 | 	m = sizeof(*ra); | 
 | 	while (pktlen - m > 0) { | 
 | 		optype = buf[m]; | 
 | 		switch (optype) { | 
 | 		case V6nd_srclladdr: | 
 | 			llao = (struct lladdropt *)&buf[m]; | 
 | 			m += 8 * buf[m + 1]; | 
 | 			if (llao->len != 1) { | 
 | 				ralog( | 
 | 				    "recvrahost: illegal len (%d) for source link layer address option", | 
 | 				    llao->len); | 
 | 				return; | 
 | 			} | 
 | 			if (!ISIPV6LINKLOCAL(ra->src)) { | 
 | 				ralog("recvrahost: non-link-local src addr for router adv %R", | 
 | 				      ra->src); | 
 | 				return; | 
 | 			} | 
 |  | 
 | 			snprintf(abuf, sizeof(abuf), "%s/arp", conf.mpoint); | 
 | 			arpfd = open(abuf, O_WRONLY); | 
 | 			if (arpfd < 0) { | 
 | 				ralog("recvrahost: couldn't open %s to write: %r", | 
 | 				      abuf); | 
 | 				return; | 
 | 			} | 
 |  | 
 | 			n = snprintf(abuf, sizeof(abuf), "add ether %R %E", | 
 | 				     ra->src, llao->lladdr); | 
 | 			if (write(arpfd, abuf, n) < n) | 
 | 				ralog("recvrahost: couldn't write to %s/arp", | 
 | 				      conf.mpoint); close(arpfd); | 
 | 			break; | 
 | 		case V6nd_targlladdr: | 
 | 		case V6nd_redirhdr: | 
 | 			m += 8 * buf[m + 1]; | 
 | 			ralog("ignoring unexpected option type `%s' in Routeradv", | 
 | 			      optname(optype)); | 
 | 			break; | 
 | 		case V6nd_mtu: | 
 | 			mtuo = (struct mtuopt *)&buf[m]; | 
 | 			m += 8 * mtuo->len; | 
 | 			conf.linkmtu = nhgetl(mtuo->mtu); | 
 | 			break; | 
 | 		case V6nd_pfxinfo: | 
 | 			prfo = (struct prefixopt *)&buf[m]; | 
 | 			m += 8 * prfo->len; | 
 | 			if (prfo->len != 4) { | 
 | 				ralog("illegal len (%d) for prefix option", | 
 | 				      prfo->len); | 
 | 				return; | 
 | 			} | 
 | 			memmove(conf.v6pref, prfo->pref, IPaddrlen); | 
 | 			conf.prefixlen = prfo->plen; | 
 | 			conf.onlink = ((prfo->lar & OLMASK) != 0); | 
 | 			conf.autoflag = ((prfo->lar & AFMASK) != 0); | 
 | 			conf.validlt = nhgetl(prfo->validlt); | 
 | 			conf.preflt = nhgetl(prfo->preflt); | 
 | 			issueadd6(&conf); | 
 | 			if (first) { | 
 | 				first = 0; | 
 | 				ralog("got initial RA from %R on %s; pfx %R", | 
 | 				      ra->src, conf.dev, prfo->pref); | 
 | 			} | 
 | 			break; | 
 | 		default: | 
 | 			if (debug) | 
 | 				ralog("ignoring optype %d in Routeradv from %R", | 
 | 				      optype, ra->src); | 
 | 		/* fall through */ | 
 | 		case V6nd_srcaddrs: | 
 | 			/* netsbd sends this, so quietly ignore it for now */ | 
 | 			m += 8 * buf[m + 1]; | 
 | 			break; | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | /* | 
 |  * daemon to receive router advertisements from routers | 
 |  */ | 
 | static void *recvra6thr(void *unused_arg); | 
 |  | 
 | void recvra6(void) | 
 | { | 
 | 	pthread_t tid; | 
 |  | 
 | 	pthread_create(&tid, NULL, recvra6thr, NULL); | 
 | } | 
 |  | 
 | static void *recvra6thr(void *unused_arg) | 
 | { | 
 | 	int fd, cfd, n, sendrscnt, sleepfor; | 
 | 	uint8_t buf[4096]; | 
 |  | 
 | 	(void)unused_arg; | 
 |  | 
 | 	/* TODO: why not v6allroutersL? */ | 
 | 	fd = dialicmp(v6allnodesL, ICMP6_RA, &cfd); | 
 | 	if (fd < 0) { | 
 | 		fprintf(stderr, "can't open icmp_ra connection: %r"); | 
 | 		evexit(-1); | 
 | 	} | 
 |  | 
 | 	sendrscnt = Maxv6rss; | 
 |  | 
 | 	ralog("recvra6 on %s", conf.dev); | 
 | 	sleepfor = jitter(); | 
 | 	for (;;) { | 
 | 		// | 
 | 		// We only get 3 (Maxv6rss) tries, so make sure we | 
 | 		// wait long enough to be certain that at least one RA | 
 | 		// will be transmitted. | 
 | 		// | 
 | 		struct alarm_waiter waiter; | 
 |  | 
 | 		init_awaiter(&waiter, alarm_abort_sysc); | 
 | 		waiter.data = current_uthread; | 
 | 		set_awaiter_rel(&waiter, 1000 * MAX(sleepfor, 7000)); | 
 | 		set_alarm(&waiter); | 
 | 		n = read(fd, buf, sizeof(buf)); | 
 | 		unset_alarm(&waiter); | 
 |  | 
 | 		if (n <= 0) { | 
 | 			if (sendrscnt > 0) { | 
 | 				sendrscnt--; | 
 | 				if (recvra6on(conf.mpoint, myifc) == IsHostRecv) | 
 | 					sendrs(fd); | 
 | 				sleepfor = V6rsintvl + (lrand48() % 100); | 
 | 			} | 
 | 			if (sendrscnt == 0) { | 
 | 				sendrscnt--; | 
 | 				sleepfor = 0; | 
 | 				ralog("recvra6: no router advs after %d sols on %s", | 
 | 				      Maxv6rss, conf.dev); | 
 | 			} | 
 | 			continue; | 
 | 		} | 
 |  | 
 | 		sleepfor = 0; | 
 | 		sendrscnt = -1; /* got at least initial ra; no whining */ | 
 | 		switch (recvra6on(conf.mpoint, myifc)) { | 
 | 		case IsRouter: | 
 | 			recvrarouter(buf, n); | 
 | 			break; | 
 | 		case IsHostRecv: | 
 | 			recvrahost(buf, n); | 
 | 			break; | 
 | 		case IsHostNoRecv: | 
 | 			ralog("recvra6: recvra off, quitting on %s", conf.dev); | 
 | 			close(fd); | 
 | 			evexit(0); | 
 | 		default: | 
 | 			ralog("recvra6: unable to read router status on %s", | 
 | 			      conf.dev); | 
 | 			break; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return NULL; | 
 | } | 
 |  | 
 | /* | 
 |  * return -1 -- error, reading/writing some file, | 
 |  *         0 -- no arp table updates | 
 |  *         1 -- successful arp table update | 
 |  */ | 
 | int recvrs(uint8_t *buf, int pktlen, uint8_t *sol) | 
 | { | 
 | 	int n, optsz, arpfd; | 
 | 	char abuf[256]; | 
 | 	struct routersol *rs; | 
 | 	struct lladdropt *llao; | 
 |  | 
 | 	rs = (struct routersol *)buf; | 
 | 	n = sizeof(*rs); | 
 | 	optsz = pktlen - n; | 
 | 	pkt2str(buf, buf + pktlen, abuf, sizeof(abuf)); | 
 |  | 
 | 	if (optsz != sizeof(*llao)) | 
 | 		return 0; | 
 | 	if (buf[n] != V6nd_srclladdr || 8 * buf[n + 1] != sizeof(*llao)) { | 
 | 		ralog("rs opt err %s", abuf); | 
 | 		return -1; | 
 | 	} | 
 |  | 
 | 	ralog("rs recv %s", abuf); | 
 |  | 
 | 	if (memcmp(rs->src, v6Unspecified, IPaddrlen) == 0) | 
 | 		return 0; | 
 |  | 
 | 	snprintf(abuf, sizeof(abuf), "%s/arp", conf.mpoint); | 
 | 	arpfd = open(abuf, O_WRONLY); | 
 | 	if (arpfd < 0) { | 
 | 		ralog("recvrs: can't open %s/arp to write: %r", conf.mpoint); | 
 | 		return -1; | 
 | 	} | 
 |  | 
 | 	llao = (struct lladdropt *)&buf[n]; | 
 | 	n = snprintf(abuf, sizeof(abuf), "add ether %R %E", rs->src, | 
 | 		     llao->lladdr); | 
 | 	if (write(arpfd, abuf, n) < n) { | 
 | 		ralog("recvrs: can't write to %s/arp: %r", conf.mpoint); | 
 | 		close(arpfd); | 
 | 		return -1; | 
 | 	} | 
 |  | 
 | 	memmove(sol, rs->src, IPaddrlen); | 
 | 	close(arpfd); | 
 | 	return 1; | 
 | } | 
 |  | 
 | void sendra(int fd, uint8_t *dst, int rlt) | 
 | { | 
 | 	int pktsz, preflen; | 
 | 	char abuf[1024], tmp[64]; | 
 | 	uint8_t buf[1024], macaddr[6], src[IPaddrlen]; | 
 | 	struct ipifc *ifc = NULL; | 
 | 	struct iplifc *lifc, *nlifc; | 
 | 	struct lladdropt *llao; | 
 | 	struct prefixopt *prfo; | 
 | 	struct routeradv *ra; | 
 |  | 
 | 	memset(buf, 0, sizeof(buf)); | 
 | 	ra = (struct routeradv *)buf; | 
 |  | 
 | 	myetheraddr(macaddr, conf.dev); | 
 | 	ea2lla(src, macaddr); | 
 | 	memmove(ra->src, src, IPaddrlen); | 
 | 	memmove(ra->dst, dst, IPaddrlen); | 
 | 	ra->type = ICMP6_RA; | 
 | 	ra->cttl = conf.ttl; | 
 |  | 
 | 	if (conf.mflag > 0) | 
 | 		ra->mor |= MFMASK; | 
 | 	if (conf.oflag > 0) | 
 | 		ra->mor |= OCMASK; | 
 | 	if (rlt > 0) | 
 | 		hnputs(ra->routerlt, conf.routerlt); | 
 | 	else | 
 | 		hnputs(ra->routerlt, 0); | 
 | 	hnputl(ra->rchbltime, conf.reachtime); | 
 | 	hnputl(ra->rxmtimer, conf.rxmitra); | 
 |  | 
 | 	pktsz = sizeof(*ra); | 
 |  | 
 | 	/* include all global unicast prefixes on interface in prefix options */ | 
 | 	ifc = readipifc(conf.mpoint, ifc, myifc); | 
 | 	for (lifc = (ifc ? ifc->lifc : NULL); lifc; lifc = nlifc) { | 
 | 		nlifc = lifc->next; | 
 | 		prfo = (struct prefixopt *)(buf + pktsz); | 
 | 		/* global unicast address? */ | 
 | 		if (!ISIPV6LINKLOCAL(lifc->ip) && !ISIPV6MCAST(lifc->ip) && | 
 | 		    memcmp(lifc->ip, IPnoaddr, IPaddrlen) != 0 && | 
 | 		    memcmp(lifc->ip, v6loopback, IPaddrlen) != 0 && | 
 | 		    !isv4(lifc->ip)) { | 
 | 			memmove(prfo->pref, lifc->net, IPaddrlen); | 
 |  | 
 | 			/* hack to find prefix length */ | 
 | 			snprintf(tmp, sizeof(tmp), "%M", lifc->mask); | 
 | 			preflen = atoi(&tmp[1]); | 
 | 			prfo->plen = preflen & 0xff; | 
 | 			if (prfo->plen == 0) | 
 | 				continue; | 
 |  | 
 | 			prfo->type = V6nd_pfxinfo; | 
 | 			prfo->len = 4; | 
 | 			prfo->lar = AFMASK; | 
 | 			hnputl(prfo->validlt, lifc->validlt); | 
 | 			hnputl(prfo->preflt, lifc->preflt); | 
 | 			pktsz += sizeof(*prfo); | 
 | 		} | 
 | 	} | 
 | 	/* | 
 | 	 * include link layer address (mac address for now) in | 
 | 	 * link layer address option | 
 | 	 */ | 
 | 	llao = (struct lladdropt *)(buf + pktsz); | 
 | 	llao->type = V6nd_srclladdr; | 
 | 	llao->len = 1; | 
 | 	memmove(llao->lladdr, macaddr, sizeof(macaddr)); | 
 | 	pktsz += sizeof(*llao); | 
 | 	pkt2str(buf + 40, buf + pktsz, abuf, sizeof(abuf)); | 
 | 	if (write(fd, buf, pktsz) < pktsz) | 
 | 		ralog("sendra fail %s: %r", abuf); | 
 | 	else if (debug) | 
 | 		ralog("sendra succ %s", abuf); | 
 | } | 
 |  | 
 | /* | 
 |  * daemon to send router advertisements to hosts | 
 |  */ | 
 | static void *sendra6thr(void *unused_arg); | 
 |  | 
 | void sendra6(void) | 
 | { | 
 | 	pthread_t tid; | 
 |  | 
 | 	pthread_create(&tid, NULL, sendra6thr, NULL); | 
 | } | 
 |  | 
 | void *sendra6thr(void *unused_arg) | 
 | { | 
 | 	int fd, cfd, n, dstknown = 0, sendracnt, sleepfor, nquitmsgs; | 
 | 	long lastra, now; | 
 | 	uint8_t buf[4096], dst[IPaddrlen]; | 
 | 	struct ipifc *ifc = NULL; | 
 |  | 
 | 	(void)unused_arg; | 
 |  | 
 | 	fd = dialicmp(v6allnodesL, ICMP6_RS, &cfd); | 
 | 	if (fd < 0) { | 
 | 		fprintf(stderr, "can't open icmp_rs connection: %r"); | 
 | 		evexit(-1); | 
 | 	} | 
 |  | 
 | 	sendracnt = Maxv6initras; | 
 | 	nquitmsgs = Maxv6finalras; | 
 |  | 
 | 	ralog("sendra6 on %s", conf.dev); | 
 | 	sleepfor = jitter(); | 
 | 	for (;;) { | 
 | 		struct alarm_waiter waiter; | 
 |  | 
 | 		init_awaiter(&waiter, alarm_abort_sysc); | 
 | 		set_awaiter_rel(&waiter, 1000 * MAX(sleepfor, 0)); | 
 | 		lastra = time(0); | 
 | 		set_alarm(&waiter); | 
 | 		n = read(fd, buf, sizeof(buf)); | 
 | 		unset_alarm(&waiter); | 
 |  | 
 | 		ifc = readipifc(conf.mpoint, ifc, myifc); | 
 | 		if (ifc == NULL) { | 
 | 			ralog("sendra6: can't read router params on %s", | 
 | 			      conf.mpoint); | 
 | 			continue; | 
 | 		} | 
 |  | 
 | 		if (ifc->sendra6 <= 0) { | 
 | 			if (nquitmsgs > 0) { | 
 | 				sendra(fd, v6allnodesL, 0); | 
 | 				nquitmsgs--; | 
 | 				sleepfor = Minv6interradelay + jitter(); | 
 | 				continue; | 
 | 			} else { | 
 | 				ralog("sendra6: sendra off, quitting on %s", | 
 | 				      conf.dev); | 
 | 				evexit(0); | 
 | 			} | 
 | 		} | 
 |  | 
 | 		nquitmsgs = Maxv6finalras; | 
 |  | 
 | 		if (n <= 0) { /* no RS */ | 
 | 			if (sendracnt > 0) | 
 | 				sendracnt--; | 
 | 		} else { /* respond to RS */ | 
 | 			dstknown = recvrs(buf, n, dst); | 
 | 			now = time(0); | 
 |  | 
 | 			if (now - lastra < Minv6interradelay) { | 
 | 				/* too close, skip */ | 
 | 				sleepfor = lastra + Minv6interradelay + jitter() | 
 | 					   - now; | 
 | 				continue; | 
 | 			} | 
 | 			usleep(jitter() * 1000); | 
 | 		} | 
 | 		sleepfor = randint(ifc->rp.minraint, ifc->rp.maxraint); | 
 | 		if (dstknown > 0) | 
 | 			sendra(fd, dst, 1); | 
 | 		else | 
 | 			sendra(fd, v6allnodesL, 1); | 
 | 	} | 
 |  | 
 | 	return NULL; | 
 | } | 
 |  | 
 | void startra6(void) | 
 | { | 
 | 	static const char routeon[] = "iprouting 1"; | 
 |  | 
 | 	if (conf.recvra > 0) | 
 | 		recvra6(); | 
 |  | 
 | 	if (conf.sendra > 0) { | 
 | 		if (write(conf.cfd, routeon, sizeof(routeon) - 1) < 0) { | 
 | 			warning("write (iprouting 1) failed: %r"); | 
 | 			return; | 
 | 		} | 
 | 		sendra6(); | 
 | 		if (conf.recvra <= 0) | 
 | 			recvra6(); | 
 | 	} | 
 | } | 
 |  | 
 | void doipv6(int what) | 
 | { | 
 | 	nip = nipifcs(conf.mpoint); | 
 | 	if (!noconfig) { | 
 | 		lookforip(conf.mpoint); | 
 | 		controldevice(); | 
 | 		binddevice(); | 
 | 	} | 
 |  | 
 | 	switch (what) { | 
 | 	default: | 
 | 		fprintf(stderr, "unknown IPv6 verb\n"); | 
 | 		evexit(-1); | 
 | 	case Vaddpref6: | 
 | 		issueadd6(&conf); | 
 | 		break; | 
 | 	case Vra6: | 
 | 		issuebasera6(&conf); | 
 | 		issuerara6(&conf); | 
 | 		startra6(); | 
 | 		break; | 
 | 	} | 
 | } |