3 * RS means Router Solicitation
4 * RA means Router Advertisement
15 #include <libsec.h> /* for sha1 */
35 typedef struct Routeradv Routeradv;
36 typedef struct Routersol Routersol;
37 typedef struct Lladdropt Lladdropt;
38 typedef struct Prefixopt Prefixopt;
39 typedef struct Mtuopt Mtuopt;
40 typedef struct Ipaddrsopt Ipaddrsopt;
43 uchar vcf[4]; /* version:4, traffic class:8, flow label:20 */
44 uchar ploadlen[2]; /* payload length: packet length - 40 */
45 uchar proto; /* next header type */
46 uchar ttl; /* hop limit */
56 uchar vcf[4]; /* version:4, traffic class:8, flow label:20 */
57 uchar ploadlen[2]; /* payload length: packet length - 40 */
58 uchar proto; /* next header type */
59 uchar ttl; /* hop limit */
104 uchar v6allroutersL[IPaddrlen] = {
111 uchar v6allnodesL[IPaddrlen] = {
118 uchar v6Unspecified[IPaddrlen] = {
125 uchar v6loopback[IPaddrlen] = {
132 uchar v6glunicast[IPaddrlen] = {
139 uchar v6linklocal[IPaddrlen] = {
146 uchar v6solpfx[IPaddrlen] = {
150 /* last 3 bytes filled with low-order bytes of addr being solicited */
154 uchar v6defmask[IPaddrlen] = {
155 0xff, 0xff, 0xff, 0xff,
156 0xff, 0xff, 0xff, 0xff,
161 #pragma varargck argpos ralog 1
163 #define RALOG "v6routeradv"
166 ralog(char *fmt, ...)
172 vseprint(msg, msg+sizeof msg, fmt, arg);
174 syslog(debug, RALOG, msg);
178 v6paraminit(Conf *cf)
180 cf->sendra = cf->recvra = 0;
183 cf->linkmtu = DEFMTU;
184 cf->maxraint = Maxv6initraintvl;
185 cf->minraint = Maxv6initraintvl / 4;
186 cf->reachtime = V6reachabletime;
187 cf->rxmitra = V6retranstimer;
195 cf->validlt = cf->preflt = ~0L;
199 parse6pref(int argc, char **argv)
203 conf.preflt = strtoul(argv[5], 0, 10);
206 conf.validlt = strtoul(argv[4], 0, 10);
209 conf.autoflag = (atoi(argv[3]) != 0);
212 conf.onlink = (atoi(argv[2]) != 0);
215 conf.prefixlen = atoi(argv[1]);
218 if (parseip(conf.v6pref, argv[0]) == -1)
219 sysfatal("bad address %s", argv[0]);
222 DEBUG("parse6pref: pref %I len %d", conf.v6pref, conf.prefixlen);
225 /* parse router advertisement (keyword, value) pairs */
227 parse6ra(int argc, char **argv)
236 for (argsleft = argc; argsleft > 1; argsleft -= 2) {
239 if (strcmp(kw, "recvra") == 0)
240 conf.recvra = (atoi(val) != 0);
241 else if (strcmp(kw, "sendra") == 0)
242 conf.sendra = (atoi(val) != 0);
243 else if (strcmp(kw, "mflag") == 0)
244 conf.mflag = (atoi(val) != 0);
245 else if (strcmp(kw, "oflag") == 0)
246 conf.oflag = (atoi(val) != 0);
247 else if (strcmp(kw, "maxraint") == 0)
248 conf.maxraint = atoi(val);
249 else if (strcmp(kw, "minraint") == 0)
250 conf.minraint = atoi(val);
251 else if (strcmp(kw, "linkmtu") == 0)
252 conf.linkmtu = atoi(val);
253 else if (strcmp(kw, "reachtime") == 0)
254 conf.reachtime = atoi(val);
255 else if (strcmp(kw, "rxmitra") == 0)
256 conf.rxmitra = atoi(val);
257 else if (strcmp(kw, "ttl") == 0)
258 conf.ttl = atoi(val);
259 else if (strcmp(kw, "routerlt") == 0)
260 conf.routerlt = atoi(val);
262 warning("bad ra6 keyword %s", kw);
268 /* consistency check */
269 if (conf.maxraint < conf.minraint)
270 sysfatal("maxraint %d < minraint %d",
271 conf.maxraint, conf.minraint);
275 ea2lla(uchar *lla, uchar *ea)
277 memset(lla, 0, IPaddrlen);
280 lla[8] = ea[0] ^ 0x2;
291 findllip(uchar *ip, Ipifc *ifc)
295 for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
296 if(ISIPV6LINKLOCAL(lifc->ip)){
297 ipmove(ip, lifc->ip);
301 ipmove(ip, v6Unspecified);
306 dialicmpv6(uchar *ip, int port)
308 char addr[128], local[128];
311 snprint(addr, sizeof(addr), "%s/icmpv6!%I!%d!r", conf.mpoint, ip, port);
312 snprint(local, sizeof(local), "%I!%d", conf.lladdr, port);
313 if((fd = dial(addr, local, nil, &cfd)) < 0)
314 sysfatal("dialicmp6: %r");
315 fprint(cfd, "headers");
316 fprint(cfd, "ignoreadvice");
318 fprint(cfd, "addmulti %I", conf.lladdr);
324 arpenter(uchar *ip, uchar *mac)
331 snprint(buf, sizeof buf, "%s/arp", conf.mpoint);
332 if((fd = open(buf, OWRITE)) < 0){
333 warning("couldn't open %s: %r", buf);
336 n = snprint(buf, sizeof buf, "add %s %I %E %I\n", conf.type, ip, mac, conf.lladdr);
337 if(write(fd, buf, n) != n) {
338 warning("arpenter: %s: %r", buf);
349 char buf[256], *f[5], *p;
350 uchar addr[IPaddrlen];
354 snprint(buf, sizeof buf, "%s/arp", conf.mpoint);
355 bp = Bopen(buf, OREAD);
357 warning("couldn't open %s: %r", buf);
361 while((p = Brdline(bp, '\n')) != nil){
362 p[Blinelen(bp)-1] = 0;
363 if(tokenize(p, f, nelem(f)) < 3)
365 if(parseip(addr, f[2]) != -1)
367 if(ipcmp(addr, ip) == 0){
376 /* add ipv6 addr to an interface */
383 if(!validip(conf.laddr) || isv4(conf.laddr))
386 tentative = dupl_disc;
390 n = sprint(buf, "try");
392 n = sprint(buf, "add");
394 n += snprint(buf+n, sizeof buf-n, " %I", conf.laddr);
395 if(!validip(conf.mask))
396 ipmove(conf.mask, v6defmask);
397 n += snprint(buf+n, sizeof buf-n, " %M", conf.mask);
398 if(validip(conf.raddr)){
399 n += snprint(buf+n, sizeof buf-n, " %I", conf.raddr);
401 n += snprint(buf+n, sizeof buf-n, " %d", conf.mtu);
404 if(write(conf.cfd, buf, n) < 0){
405 warning("write(%s): %r", buf);
410 if(validip(conf.gaddr) && !isv4(conf.gaddr))
411 adddefroute(conf.gaddr, conf.laddr, conf.laddr, conf.mask);
417 if(arpcheck(conf.laddr) <= 0) {
422 warning("found dup entry in arp cache");
428 recvra6on(Ipifc *ifc)
432 else if(ifc->sendra6 > 0)
434 else if(ifc->recvra6 > 0)
441 sendrs(int fd, uchar *dst)
448 memset(buf, 0, sizeof buf);
450 rs = (Routersol*)buf;
452 ipmove(rs->dst, dst);
453 ipmove(rs->src, conf.lladdr);
457 llao = (Lladdropt*)&buf[pktlen];
458 llao->type = V6nd_srclladdr;
459 llao->len = (2+7+conf.hwalen)/8;
460 memmove(llao->lladdr, conf.hwa, conf.hwalen);
461 pktlen += 8 * llao->len;
464 if(write(fd, rs, pktlen) != pktlen)
465 ralog("sendrs: write failed, pkt size %d", pktlen);
467 ralog("sendrs: sent solicitation to %I from %I on %s",
468 rs->dst, rs->src, conf.dev);
472 * a router receiving a router adv from another
473 * router calls this; it is basically supposed to
474 * log the information in the ra and raise a flag
475 * if any parameter value is different from its configured values.
477 * doing nothing for now since I don't know where to log this yet.
480 recvrarouter(uchar buf[], int pktlen)
486 ewrite(int fd, char *str)
494 if(write(fd, str, n) != n)
495 ralog("write(%s) failed: %r", str);
499 issuebasera6(Conf *cf)
503 cfg = smprint("ra6 mflag %d oflag %d reachtime %d rxmitra %d "
504 "ttl %d routerlt %d linkmtu %d",
505 cf->mflag, cf->oflag, cf->reachtime, cf->rxmitra,
506 cf->ttl, cf->routerlt, cf->linkmtu);
507 ewrite(cf->cfd, cfg);
516 cfg = smprint("ra6 sendra %d recvra %d maxraint %d minraint %d",
517 cf->sendra, cf->recvra, cf->maxraint, cf->minraint);
518 ewrite(cf->cfd, cfg);
527 cfg = smprint("add6 %I %d %d %d %lud %lud", cf->v6pref, cf->prefixlen,
528 cf->onlink, cf->autoflag, cf->validlt, cf->preflt);
529 ewrite(cf->cfd, cfg);
538 for(len=0; len < 128; len += 8){
543 while(len < 128 && (*mask & (0x80 >> (len & 7))) != 0)
549 genipmkask(uchar *mask, int len)
551 memset(mask, 0, IPaddrlen);
556 for(; len >= 8; len -= 8)
559 *mask = ~((1<<(8-len))-1);
565 static uchar tab[SHA1dlen*100], *w;
566 uchar hash[SHA1dlen], *r;
568 sha1((uchar*)cf, sizeof(*cf), hash, nil);
569 if(w == nil || w == &tab[sizeof(tab)])
571 for(r = tab; r < w; r += SHA1dlen)
572 if(memcmp(r, hash, SHA1dlen) == 0)
574 memmove(w, hash, SHA1dlen);
580 pnames(uchar *d, int nd, char *s)
588 for(l = 0; *s != 0 && *s != '.' && *s != ' '; l++)
592 if(d >= de || l > 077)
596 memmove(d-l, s-l, l);
601 return d - (de - nd);
605 gnames(char *d, int nd, uchar *s, int ns)
615 if(d + l >= de || s + l >= se)
636 return d - (de - nd);
640 * host receiving a router advertisement calls this
643 recvrahost(uchar buf[], int pktlen)
645 char dnsdomain[sizeof(conf.dnsdomain)];
654 ra = (Routeradv*)buf;
658 if(!ISIPV6LINKLOCAL(ra->src))
662 conf.mflag = (MFMASK & ra->mor);
663 conf.oflag = (OCMASK & ra->mor);
664 conf.routerlt = nhgets(ra->routerlt);
665 conf.reachtime = nhgetl(ra->rchbltime);
666 conf.rxmitra = nhgetl(ra->rxmtimer);
667 conf.linkmtu = DEFMTU;
669 memset(conf.dns, 0, sizeof(conf.dns));
670 memset(conf.fs, 0, sizeof(conf.fs));
671 memset(conf.auth, 0, sizeof(conf.auth));
672 memset(conf.dnsdomain, 0, sizeof(conf.dnsdomain));
674 /* process options */
675 while(pktlen - m >= 8) {
679 if(m <= n || pktlen < m)
684 llao = (Lladdropt*)&buf[n];
685 if(llao->len == 1 && conf.hwalen == 6)
686 arpenter(ra->src, llao->lladdr);
689 mtuo = (Mtuopt*)&buf[n];
690 conf.linkmtu = nhgetl(mtuo->mtu);
694 addrso = (Ipaddrsopt*)&buf[n];
695 if(gnames(dnsdomain, sizeof(dnsdomain),
696 addrso->addrs, (addrso->len - 1)*8) <= 0)
698 addnames(conf.dnsdomain, dnsdomain, sizeof(conf.dnsdomain));
702 addrso = (Ipaddrsopt*)&buf[n];
703 n = (addrso->len - 1) * 8;
704 if(n == 0 || n % IPaddrlen)
706 addaddrs(conf.dns, sizeof(conf.dns), addrso->addrs, n);
710 addrso = (Ipaddrsopt*)&buf[n];
711 n = (addrso->len - 1) * 8;
712 if(n == 0 || n % IPaddrlen || !plan9)
714 addaddrs(conf.fs, sizeof(conf.fs), addrso->addrs, n);
717 addrso = (Ipaddrsopt*)&buf[n];
718 n = (addrso->len - 1) * 8;
719 if(n == 0 || n % IPaddrlen || !plan9)
721 addaddrs(conf.auth, sizeof(conf.auth), addrso->addrs, n);
727 /* process prefixes */
729 while(pktlen - m >= 8) {
733 if(m <= n || pktlen < m)
736 if(optype != V6nd_pfxinfo)
739 prfo = (Prefixopt*)&buf[n];
743 conf.prefixlen = prfo->plen & 127;
744 genipmkask(conf.mask, conf.prefixlen);
745 maskip(prfo->pref, conf.mask, conf.v6pref);
746 memmove(conf.laddr, conf.v6pref, 8);
747 memmove(conf.laddr+8, conf.lladdr+8, 8);
748 conf.onlink = ((prfo->lar & OLMASK) != 0);
749 conf.autoflag = ((prfo->lar & AFMASK) != 0);
750 conf.validlt = nhgetl(prfo->validlt);
751 conf.preflt = nhgetl(prfo->preflt);
753 if(conf.routerlt == 0)
754 ipmove(conf.gaddr, IPnoaddr);
755 else if((prfo->lar & RFMASK) != 0)
756 ipmove(conf.gaddr, prfo->pref);
758 ipmove(conf.gaddr, ra->src);
760 if(conf.prefixlen < 1
761 || conf.prefixlen > 64
762 || !validip(conf.v6pref)
764 || ipcmp(conf.v6pref, v6loopback) == 0
765 || ISIPV6MCAST(conf.v6pref)
766 || ISIPV6LINKLOCAL(conf.v6pref)){
768 ralog("igoring bogus prefix from %I on %s; pfx %I %M",
769 ra->src, conf.dev, conf.v6pref, conf.mask);
773 /* add prefix and update parameters */
776 /* report this prefix configuration only once */
780 ralog("got RA from %I on %s; pfx %I %M",
781 ra->src, conf.dev, conf.v6pref, conf.mask);
783 if(validip(conf.gaddr))
784 adddefroute(conf.gaddr, conf.lladdr, conf.laddr, conf.mask);
793 * daemon to receive router advertisements from routers
798 int fd, n, sendrscnt, recvracnt, sleepfor;
802 ifc = readipifc(conf.mpoint, nil, myifc);
804 sysfatal("can't read ipifc: %r");
806 if(!findllip(conf.lladdr, ifc))
807 sysfatal("no link local address");
809 fd = dialicmpv6(v6allnodesL, ICMP6_RA);
811 sysfatal("can't open icmp_ra connection: %r");
813 switch(rfork(RFPROC|RFMEM|RFFDG|RFNOWAIT|RFNOTEG)){
815 sysfatal("can't fork: %r");
818 ralog("recvra6 on %s", conf.dev);
820 /* wait for initial RA */
821 return (int)(uintptr)rendezvous(recvra6, (void*)0);
825 procsetname("recvra6 on %s %I", conf.dev, conf.lladdr);
829 if(recvra6on(ifc) == IsHostRecv){
830 sendrs(fd, v6allroutersL);
831 sendrscnt = Maxv6rss;
834 recvracnt = Maxv6initras;
835 sleepfor = Minv6interradelay;
839 n = read(fd, buf, sizeof buf);
842 /* wait for alarm to expire */
843 if(sendrscnt < 0 && sleepfor > 100)
846 ifc = readipifc(conf.mpoint, ifc, myifc);
848 ralog("recvra6: can't read router params on %s, quitting on %s",
849 conf.mpoint, conf.dev);
851 rendezvous(recvra6, (void*)-1);
858 sendrs(fd, v6allroutersL);
859 sleepfor = V6rsintvl + nrand(100);
863 ralog("recvra6: no router advs after %d sols on %s",
865 rendezvous(recvra6, (void*)0);
871 switch (recvra6on(ifc)) {
873 recvrarouter(buf, n);
879 ralog("recvra6: recvra off, quitting on %s", conf.dev);
881 rendezvous(recvra6, (void*)-1);
885 /* got at least initial ra; no whining */
887 rendezvous(recvra6, (void*)1);
894 recvracnt = Maxv6initras;
895 sleepfor = Maxv6radelay;
901 * return -1 -- error, reading/writing some file,
902 * 0 -- no arp table updates
903 * 1 -- successful arp table update
906 recvrs(uchar *buf, int pktlen, uchar *sol)
912 n = sizeof *rs + sizeof *llao;
913 rs = (Routersol*)buf;
917 llao = (Lladdropt*)&buf[sizeof *rs];
918 if(llao->type != V6nd_srclladdr || llao->len != 1 || conf.hwalen != 6)
923 || ipcmp(rs->src, v6loopback) == 0
924 || ISIPV6MCAST(rs->src))
927 if((n = arpenter(rs->src, llao->lladdr)) <= 0)
930 ipmove(sol, rs->src);
935 sendra(int fd, uchar *dst, int rlt, Ipifc *ifc, Ndb *db)
937 uchar dns[sizeof(conf.dns)], fs[sizeof(conf.fs)], auth[sizeof(conf.auth)];
938 char dnsdomain[sizeof(conf.dnsdomain)];
946 memset(dns, 0, sizeof(dns));
947 memset(fs, 0, sizeof(fs));
948 memset(auth, 0, sizeof(auth));
949 memset(dnsdomain, 0, sizeof(dnsdomain));
951 memset(buf, 0, sizeof buf);
953 ra = (Routeradv*)buf;
954 ipmove(ra->dst, dst);
955 ipmove(ra->src, conf.lladdr);
963 hnputs(ra->routerlt, conf.routerlt);
965 hnputs(ra->routerlt, 0);
966 hnputl(ra->rchbltime, conf.reachtime);
967 hnputl(ra->rxmtimer, conf.rxmitra);
971 * include link layer address (mac address for now) in
972 * link layer address option
975 Lladdropt *llao = (Lladdropt *)&buf[pktlen];
976 llao->type = V6nd_srclladdr;
977 llao->len = (2+7+conf.hwalen)/8;
978 memmove(llao->lladdr, conf.hwa, conf.hwalen);
979 pktlen += 8 * llao->len;
982 /* include all global unicast prefixes on interface in prefix options */
983 for (lifc = (ifc != nil? ifc->lifc: nil); lifc != nil; lifc = lifc->next) {
984 if(pktlen > sizeof buf - 4*8)
987 if(!validip(lifc->ip)
989 || ipcmp(lifc->ip, v6loopback) == 0
990 || ISIPV6MCAST(lifc->ip)
991 || ISIPV6LINKLOCAL(lifc->ip))
994 prfo = (Prefixopt*)&buf[pktlen];
995 prfo->type = V6nd_pfxinfo;
997 prfo->plen = masklen(lifc->mask) & 127;
1000 ipmove(prfo->pref, lifc->net);
1001 prfo->lar = AFMASK|OLMASK;
1002 hnputl(prfo->validlt, lifc->validlt);
1003 hnputl(prfo->preflt, lifc->preflt);
1004 pktlen += 8 * prfo->len;
1006 /* get ndb configuration for this prefix */
1007 ipmove(conf.laddr, lifc->ip);
1008 ndb2conf(db, lifc->net);
1010 addaddrs(dns, sizeof(dns), conf.dns, sizeof(conf.dns));
1011 addaddrs(fs, sizeof(fs), conf.fs, sizeof(conf.fs));
1012 addaddrs(auth, sizeof(auth), conf.auth, sizeof(conf.auth));
1014 addnames(dnsdomain, conf.dnsdomain, sizeof(dnsdomain));
1017 addrso = (Ipaddrsopt*)&buf[pktlen];
1018 n = pnames(addrso->addrs, sizeof buf - 8 - pktlen, dnsdomain);
1020 addrso->type = V6nd_rdnssl;
1021 addrso->len = 1 + ((n + 7) / 8);
1022 hnputl(addrso->lifetime, ~0L);
1023 pktlen += 8 * addrso->len;
1026 if((n = countaddrs(dns, sizeof(dns))) > 0 && pktlen+8+n*IPaddrlen <= sizeof buf) {
1027 addrso = (Ipaddrsopt*)&buf[pktlen];
1028 addrso->type = V6nd_rdns;
1029 addrso->len = 1 + n*2;
1030 memmove(addrso->addrs, dns, n*IPaddrlen);
1031 hnputl(addrso->lifetime, ~0L);
1032 pktlen += 8 * addrso->len;
1038 /* send plan9 specific options */
1039 if((n = countaddrs(fs, sizeof(fs))) > 0 && pktlen+8+n*IPaddrlen <= sizeof buf) {
1040 addrso = (Ipaddrsopt*)&buf[pktlen];
1041 addrso->type = V6nd_9fs;
1042 addrso->len = 1 + n*2;
1043 memmove(addrso->addrs, fs, n*IPaddrlen);
1044 hnputl(addrso->lifetime, ~0L);
1045 pktlen += 8 * addrso->len;
1047 if((n = countaddrs(auth, sizeof(auth))) > 0 && pktlen+8+n*IPaddrlen <= sizeof buf) {
1048 addrso = (Ipaddrsopt*)&buf[pktlen];
1049 addrso->type = V6nd_9auth;
1050 addrso->len = 1 + n*2;
1051 memmove(addrso->addrs, auth, n*IPaddrlen);
1052 hnputl(addrso->lifetime, ~0L);
1053 pktlen += 8 * addrso->len;
1057 write(fd, buf, pktlen);
1061 * daemon to send router advertisements to hosts
1066 int fd, n, sleepfor, nquitmsgs;
1067 uchar buf[4096], dst[IPaddrlen];
1071 db = opendatabase();
1073 warning("couldn't open ndb: %r");
1075 ifc = readipifc(conf.mpoint, nil, myifc);
1077 sysfatal("can't read ipifc: %r");
1079 if(!findllip(conf.lladdr, ifc))
1080 sysfatal("no link local address");
1082 fd = dialicmpv6(v6allroutersL, ICMP6_RS);
1084 sysfatal("can't open icmp_rs connection: %r");
1086 switch(rfork(RFPROC|RFMEM|RFFDG|RFNOWAIT|RFNOTEG)){
1088 sysfatal("can't fork: %r");
1091 ralog("sendra6 on %s", conf.dev);
1096 procsetname("sendra6 on %s %I", conf.dev, conf.lladdr);
1099 nquitmsgs = Maxv6finalras;
1100 sleepfor = 100 + jitter();
1104 n = read(fd, buf, sizeof buf);
1105 sleepfor = alarm(0);
1107 if(ifc->sendra6 > 0 && n > 0 && recvrs(buf, n, dst) > 0)
1108 sendra(fd, dst, 1, ifc, db);
1110 /* wait for alarm to expire */
1113 sleepfor = Minv6interradelay;
1115 ifc = readipifc(conf.mpoint, ifc, myifc);
1117 ralog("sendra6: can't read router params on %s, quitting on %s",
1118 conf.mpoint, conf.dev);
1121 if(ifc->sendra6 <= 0){
1124 sendra(fd, v6allnodesL, 0, ifc, nil);
1127 ralog("sendra6: sendra off on %s, quitting on %s",
1128 conf.mpoint, conf.dev);
1131 db = opendatabase();
1132 sendra(fd, v6allnodesL, 1, ifc, db);
1133 sleepfor = randint(ifc->rp.minraint, ifc->rp.maxraint);
1140 static char routeon[] = "iprouting 1";
1145 if(conf.sendra > 0) {
1146 if(write(conf.cfd, routeon, sizeof routeon - 1) < 0) {
1147 warning("write (%s) failed: %r", routeon);
1151 if(conf.recvra <= 0)
1159 fprint(conf.rfd, "tag ra6");
1163 sysfatal("unknown IPv6 verb");
1169 issuebasera6(&conf);