3 * RS means Router Solicitation
4 * RA means Router Advertisement
14 #pragma varargck argpos ralog 1
16 #define RALOG "v6routeradv"
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])
26 typedef struct Hdr Hdr;
27 struct Hdr /* ICMP v4 & v6 header */
31 uchar cksum[2]; /* Checksum */
35 char *icmpmsg6[Maxtype6+1] =
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",
60 static char *icmp6opts[] =
63 [V6nd_srclladdr] "srcll_addr",
64 [V6nd_targlladdr] "targll_addr",
65 [V6nd_pfxinfo] "prefix",
66 [V6nd_redirhdr] "redirect",
69 [V6nd_srcaddrs] "src_addrs",
76 uchar v6allroutersL[IPaddrlen] = {
83 uchar v6allnodesL[IPaddrlen] = {
90 uchar v6Unspecified[IPaddrlen] = {
97 uchar v6loopback[IPaddrlen] = {
104 uchar v6glunicast[IPaddrlen] = {
111 uchar v6linklocal[IPaddrlen] = {
118 uchar v6solpfx[IPaddrlen] = {
122 /* last 3 bytes filled with low-order bytes of addr being solicited */
126 uchar v6defmask[IPaddrlen] = {
127 0xff, 0xff, 0xff, 0xff,
128 0xff, 0xff, 0xff, 0xff,
143 ralog(char *fmt, ...)
149 vseprint(msg, msg+sizeof msg, fmt, arg);
151 syslog(debug, RALOG, msg);
155 ea2lla(uchar *lla, uchar *ea)
157 assert(IPaddrlen == 16);
158 memset(lla, 0, IPaddrlen);
161 lla[8] = ea[0] | 0x2;
172 ipv62smcast(uchar *smcast, uchar *a)
174 assert(IPaddrlen == 16);
175 memset(smcast, 0, IPaddrlen);
186 v6paraminit(Conf *cf)
188 cf->sendra = cf->recvra = 0;
191 cf->maxraint = Maxv6initraintvl;
192 cf->minraint = Maxv6initraintvl / 4;
194 cf->reachtime = V6reachabletime;
195 cf->rxmitra = V6retranstimer;
203 cf->validlt = cf->preflt = ~0L;
207 optname(unsigned opt)
211 if (opt >= nelem(icmp6opts) || icmp6opts[opt] == nil) {
212 snprint(buf, sizeof buf, "unknown option %d", opt);
215 return icmp6opts[opt];
219 opt_seprint(uchar *ps, uchar *pe, char *sps, char *spe)
221 int otype, osz, pktsz;
223 char *p = sps, *e = spe;
226 for (pktsz = pe - ps; pktsz > 0; pktsz -= osz) {
232 return seprint(p, e, " option=%s ", optname(otype));
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),
242 if (pktsz < osz || osz != 32)
243 return seprint(p, e, " option=%s: bad size=%d",
244 optname(otype), osz);
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,
253 NetL(a+4), NetL(a+8), NetL(a+12)!=0);
262 pkt2str(uchar *ps, uchar *pe, char *sps, char *spe)
275 if(pktlen < ICMP6LEN) {
276 seprint(sps, spe, "short pkt");
280 tn = icmpmsg6[h->type];
282 p = seprint(p, e, "t=%ud c=%d ck=%4.4ux", h->type,
283 h->code, (ushort)NetS(h->cksum));
285 p = seprint(p, e, "t=%s c=%d ck=%4.4ux", tn,
286 h->code, (ushort)NetS(h->cksum));
291 p = seprint(p, e, " unused=%1.1d ", NetL(a)!=0);
292 opt_seprint(ps, pe, p, e);
296 p = seprint(p, e, " hoplim=%3.3d mflag=%1.1d oflag=%1.1d"
297 " unused=%1.1d routerlt=%d reachtime=%d rxmtimer=%d",
299 (*(a+1) & (1 << 7)) != 0,
300 (*(a+1) & (1 << 6)) != 0,
302 NetS(a+2), NetL(a+4), NetL(a+8));
303 opt_seprint(ps, pe, p, e);
306 seprint(p, e, " unexpected icmp6 pkt type");
312 catch(void *a, char *msg)
315 if(strstr(msg, "alarm"))
322 * based on libthread's threadsetname, but drags in less library code.
323 * actually just sets the arguments displayed.
326 procsetname(char *fmt, ...)
334 cmdname = vsmprint(fmt, arg);
338 snprint(buf, sizeof buf, "#p/%d/args", getpid());
339 if((fd = open(buf, OWRITE)) >= 0){
340 write(fd, cmdname, strlen(cmdname)+1);
347 dialicmp(uchar *dst, int dport, int *ctlfd)
350 char cmsg[100], name[128], connind[40];
351 char hdrs[] = "headers";
353 snprint(name, sizeof name, "%s/icmpv6/clone", conf.mpoint);
354 cfd = open(name, ORDWR);
356 sysfatal("dialicmp: can't open %s: %r", name);
358 n = snprint(cmsg, sizeof cmsg, "connect %I!%d!r %d", dst, dport, dport);
359 m = write(cfd, cmsg, n);
361 sysfatal("dialicmp: can't write %s to %s: %r", cmsg, name);
364 n = read(cfd, connind, sizeof connind);
367 else if (n < sizeof connind)
370 connind[sizeof connind - 1] = 0;
372 snprint(name, sizeof name, "%s/icmpv6/%s/data", conf.mpoint, connind);
373 fd = open(name, ORDWR);
375 sysfatal("dialicmp: can't open %s: %r", name);
378 if(write(cfd, hdrs, n) < n)
379 sysfatal("dialicmp: can't write `%s' to %s: %r", hdrs, name);
384 /* add ipv6 addr to an interface */
388 int tentative, dupfound = 0, n;
393 if (autoconf) { /* create link-local addr */
394 if (myetheraddr(ethaddr, conf.dev) < 0)
395 sysfatal("myetheraddr w/ %s failed: %r", conf.dev);
396 ea2lla(conf.laddr, ethaddr);
399 tentative = dupl_disc;
403 n = sprint(buf, "try");
405 n = sprint(buf, "add");
407 n += snprint(buf+n, sizeof buf-n, " %I", conf.laddr);
408 if(!validip(conf.mask))
409 ipmove(conf.mask, v6defmask);
410 n += snprint(buf+n, sizeof buf-n, " %M", conf.mask);
411 if(validip(conf.raddr)){
412 n += snprint(buf+n, sizeof buf-n, " %I", conf.raddr);
414 n += snprint(buf+n, sizeof buf-n, " %d", conf.mtu);
417 if(write(conf.cfd, buf, n) < 0){
418 warning("write(%s): %r", buf);
427 /* read arp table, look for addr duplication */
428 snprint(buf, sizeof buf, "%s/arp", conf.mpoint);
429 bp = Bopen(buf, OREAD);
431 warning("couldn't open %s: %r", buf);
435 snprint(buf, sizeof buf, "%I", conf.laddr);
436 while(p = Brdline(bp, '\n')){
437 p[Blinelen(bp)-1] = 0;
438 if(cistrstr(p, buf) != 0) {
450 warning("found dup entry in arp cache");
456 recvra6on(Ipifc *ifc)
460 else if (ifc->sendra6 > 0)
462 else if (ifc->recvra6 > 0)
468 /* send icmpv6 router solicitation to multicast address for all routers */
473 uchar buff[sizeof *rs];
475 memset(buff, 0, sizeof buff);
476 rs = (Routersol *)buff;
477 memmove(rs->dst, v6allroutersL, IPaddrlen);
478 memmove(rs->src, v6Unspecified, IPaddrlen);
481 if(write(fd, rs, sizeof buff) < sizeof buff)
482 ralog("sendrs: write failed, pkt size %d", sizeof buff);
484 ralog("sendrs: sent solicitation to %I from %I on %s",
485 rs->dst, rs->src, conf.dev);
489 * a router receiving a router adv from another
490 * router calls this; it is basically supposed to
491 * log the information in the ra and raise a flag
492 * if any parameter value is different from its configured values.
494 * doing nothing for now since I don't know where to log this yet.
497 recvrarouter(uchar buf[], int pktlen)
500 ralog("i am a router and got a router advert");
503 /* host receiving a router advertisement calls this */
506 ewrite(int fd, char *str)
511 if (write(fd, str, n) != n)
512 ralog("write(%s) failed: %r", str);
516 issuebasera6(Conf *cf)
520 cfg = smprint("ra6 mflag %d oflag %d reachtime %d rxmitra %d "
521 "ttl %d routerlt %d",
522 cf->mflag, cf->oflag, cf->reachtime, cf->rxmitra,
523 cf->ttl, cf->routerlt);
524 ewrite(cf->cfd, cfg);
533 cfg = smprint("ra6 sendra %d recvra %d maxraint %d minraint %d "
535 cf->sendra, cf->recvra, cf->maxraint, cf->minraint,
537 ewrite(cf->cfd, cfg);
546 cfg = smprint("add6 %I %d %d %d %lud %lud", cf->v6pref, cf->prefixlen,
547 cf->onlink, cf->autoflag, cf->validlt, cf->preflt);
548 ewrite(cf->cfd, cfg);
553 recvrahost(uchar buf[], int pktlen)
562 static int first = 1;
564 ra = (Routeradv*)buf;
565 // memmove(conf.v6gaddr, ra->src, IPaddrlen);
567 conf.mflag = (MFMASK & ra->mor);
568 conf.oflag = (OCMASK & ra->mor);
569 conf.routerlt = nhgets(ra->routerlt);
570 conf.reachtime = nhgetl(ra->rchbltime);
571 conf.rxmitra = nhgetl(ra->rxmtimer);
573 // issueadd6(&conf); /* for conf.v6gaddr? */
574 if (fprint(conf.cfd, "ra6 recvra 1") < 0)
575 ralog("write(ra6 recvra 1) failed: %r");
579 while (pktlen - m > 0) {
583 llao = (Lladdropt *)&buf[m];
585 if (llao->len != 1) {
586 ralog("recvrahost: illegal len (%d) for source "
587 "link layer address option", llao->len);
590 if (!ISIPV6LINKLOCAL(ra->src)) {
591 ralog("recvrahost: non-link-local src addr for "
592 "router adv %I", ra->src);
596 snprint(abuf, sizeof abuf, "%s/arp", conf.mpoint);
597 arpfd = open(abuf, OWRITE);
599 ralog("recvrahost: couldn't open %s to write: %r",
604 n = snprint(abuf, sizeof abuf, "add ether %I %E",
605 ra->src, llao->lladdr);
606 if (write(arpfd, abuf, n) < n)
607 ralog("recvrahost: couldn't write to %s/arp",
611 case V6nd_targlladdr:
614 ralog("ignoring unexpected option type `%s' in Routeradv",
618 mtuo = (Mtuopt*)&buf[m];
620 conf.linkmtu = nhgetl(mtuo->mtu);
623 prfo = (Prefixopt*)&buf[m];
625 if (prfo->len != 4) {
626 ralog("illegal len (%d) for prefix option",
630 memmove(conf.v6pref, prfo->pref, IPaddrlen);
631 conf.prefixlen = prfo->plen;
632 conf.onlink = ((prfo->lar & OLMASK) != 0);
633 conf.autoflag = ((prfo->lar & AFMASK) != 0);
634 conf.validlt = nhgetl(prfo->validlt);
635 conf.preflt = nhgetl(prfo->preflt);
639 ralog("got initial RA from %I on %s; pfx %I",
640 ra->src, conf.dev, prfo->pref);
644 /* netsbd sends this, so quietly ignore it for now */
649 ralog("ignoring optype %d in Routeradv", optype);
656 * daemon to receive router advertisements from routers
661 int fd, cfd, n, sendrscnt, sleepfor;
665 /* TODO: why not v6allroutersL? */
666 fd = dialicmp(v6allnodesL, ICMP6_RA, &cfd);
668 sysfatal("can't open icmp_ra connection: %r");
671 sendrscnt = Maxv6rss;
673 switch(rfork(RFPROC|RFMEM|RFFDG|RFNOWAIT|RFNOTEG)){
675 sysfatal("can't fork: %r");
682 procsetname("recvra6 on %s", conf.dev);
683 ralog("recvra6 on %s", conf.dev);
687 * We only get 3 (Maxv6rss) tries, so make sure we
688 * wait long enough to be certain that at least one RA
689 * will be transmitted.
694 n = read(fd, buf, sizeof buf);
697 ifc = readipifc(conf.mpoint, ifc, myifc);
699 ralog("recvra6: can't read router params on %s",
707 if (recvra6on(ifc) == IsHostRecv)
709 sleepfor = V6rsintvl + nrand(100);
711 if (sendrscnt == 0) {
714 ralog("recvra6: no router advs after %d sols on %s",
721 sendrscnt = -1; /* got at least initial ra; no whining */
722 switch (recvra6on(ifc)) {
724 recvrarouter(buf, n);
730 ralog("recvra6: recvra off, quitting on %s", conf.dev);
738 * return -1 -- error, reading/writing some file,
739 * 0 -- no arp table updates
740 * 1 -- successful arp table update
743 recvrs(uchar *buf, int pktlen, uchar *sol)
750 rs = (Routersol *)buf;
753 pkt2str(buf, buf+pktlen, abuf, abuf+nelem(abuf));
755 if (optsz != sizeof *llao)
757 if (buf[n] != V6nd_srclladdr || 8*buf[n+1] != sizeof *llao) {
758 ralog("rs opt err %s", abuf);
762 ralog("rs recv %s", abuf);
764 if (memcmp(rs->src, v6Unspecified, IPaddrlen) == 0)
767 snprint(abuf, sizeof abuf, "%s/arp", conf.mpoint);
768 arpfd = open(abuf, OWRITE);
770 ralog("recvrs: can't open %s/arp to write: %r", conf.mpoint);
774 llao = (Lladdropt *)buf[n];
775 n = snprint(abuf, sizeof abuf, "add ether %I %E", rs->src, llao->lladdr);
776 if (write(arpfd, abuf, n) < n) {
777 ralog("recvrs: can't write to %s/arp: %r", conf.mpoint);
782 memmove(sol, rs->src, IPaddrlen);
788 sendra(int fd, uchar *dst, int rlt, Ipifc *ifc)
791 char abuf[1024], tmp[40];
792 uchar buf[1024], macaddr[6], src[IPaddrlen];
793 Iplifc *lifc, *nlifc;
798 memset(buf, 0, sizeof buf);
799 ra = (Routeradv *)buf;
801 myetheraddr(macaddr, conf.dev);
802 ea2lla(src, macaddr);
803 memmove(ra->src, src, IPaddrlen);
804 memmove(ra->dst, dst, IPaddrlen);
813 hnputs(ra->routerlt, conf.routerlt);
815 hnputs(ra->routerlt, 0);
816 hnputl(ra->rchbltime, conf.reachtime);
817 hnputl(ra->rxmtimer, conf.rxmitra);
821 /* include all global unicast prefixes on interface in prefix options */
822 for (lifc = (ifc? ifc->lifc: nil); lifc; lifc = nlifc) {
824 prfo = (Prefixopt *)(buf + pktsz);
825 /* global unicast address? */
826 if (!ISIPV6LINKLOCAL(lifc->ip) && !ISIPV6MCAST(lifc->ip) &&
827 memcmp(lifc->ip, IPnoaddr, IPaddrlen) != 0 &&
828 memcmp(lifc->ip, v6loopback, IPaddrlen) != 0 &&
830 memmove(prfo->pref, lifc->net, IPaddrlen);
832 /* hack to find prefix length */
833 snprint(tmp, sizeof tmp, "%M", lifc->mask);
834 preflen = atoi(&tmp[1]);
835 prfo->plen = preflen & 0xff;
839 prfo->type = V6nd_pfxinfo;
842 hnputl(prfo->validlt, lifc->validlt);
843 hnputl(prfo->preflt, lifc->preflt);
844 pktsz += sizeof *prfo;
848 * include link layer address (mac address for now) in
849 * link layer address option
851 llao = (Lladdropt *)(buf + pktsz);
852 llao->type = V6nd_srclladdr;
854 memmove(llao->lladdr, macaddr, sizeof macaddr);
855 pktsz += sizeof *llao;
857 pkt2str(buf+40, buf+pktsz, abuf, abuf+1024);
858 if(write(fd, buf, pktsz) < pktsz)
859 ralog("sendra fail %s: %r", abuf);
861 ralog("sendra succ %s", abuf);
865 * daemon to send router advertisements to hosts
870 int fd, cfd, n, dstknown = 0, sendracnt, sleepfor, nquitmsgs;
872 uchar buf[4096], dst[IPaddrlen];
875 fd = dialicmp(v6allnodesL, ICMP6_RS, &cfd);
877 sysfatal("can't open icmp_rs connection: %r");
880 sendracnt = Maxv6initras;
881 nquitmsgs = Maxv6finalras;
883 switch(rfork(RFPROC|RFMEM|RFFDG|RFNOWAIT|RFNOTEG)){
885 sysfatal("can't fork: %r");
892 procsetname("sendra6 on %s", conf.dev);
893 ralog("sendra6 on %s", conf.dev);
900 n = read(fd, buf, sizeof buf);
903 ifc = readipifc(conf.mpoint, ifc, myifc);
905 ralog("sendra6: can't read router params on %s",
910 if (ifc->sendra6 <= 0)
912 sendra(fd, v6allnodesL, 0, ifc);
914 sleepfor = Minv6interradelay + jitter();
917 ralog("sendra6: sendra off, quitting on %s",
922 nquitmsgs = Maxv6finalras;
924 if (n <= 0) { /* no RS */
927 } else { /* respond to RS */
928 dstknown = recvrs(buf, n, dst);
931 if (now - lastra < Minv6interradelay) {
932 /* too close, skip */
933 sleepfor = lastra + Minv6interradelay +
939 sleepfor = randint(ifc->rp.minraint, ifc->rp.maxraint);
941 sendra(fd, dst, 1, ifc);
943 sendra(fd, v6allnodesL, 1, ifc);
950 static char routeon[] = "iprouting 1";
955 if (conf.sendra > 0) {
956 if (write(conf.cfd, routeon, sizeof routeon - 1) < 0) {
957 warning("write (iprouting 1) failed: %r");
961 if (conf.recvra <= 0)
969 nip = nipifcs(conf.mpoint);
971 lookforip(conf.mpoint);
978 sysfatal("unknown IPv6 verb");