|  | /* | 
|  | * 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; | 
|  | } | 
|  | } |