]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ip/ipconfig/ipv6.c
merge
[plan9front.git] / sys / src / cmd / ip / ipconfig / ipv6.c
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 <ndb.h>
12 #include "ipconfig.h"
13 #include "../icmp.h"
14
15 #include <libsec.h>     /* for sha1 */
16
17 enum {
18         IsRouter        = 1,
19         IsHostRecv      = 2,
20         IsHostNoRecv    = 3,
21
22         ICMP6_RS        = 133,
23         ICMP6_RA        = 134,
24
25         MFMASK = 1 << 7,
26         OCMASK = 1 << 6,
27         OLMASK = 1 << 7,
28         AFMASK = 1 << 6,
29         RFMASK = 1 << 5,
30
31         MAXTTL          = 255,
32         DEFMTU          = 1500,
33 };
34
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;
41
42 struct Routersol {
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 */
47         uchar   src[16];
48         uchar   dst[16];
49         uchar   type;
50         uchar   code;
51         uchar   cksum[2];
52         uchar   res[4];
53 };
54
55 struct Routeradv {
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 */
60         uchar   src[16];
61         uchar   dst[16];
62         uchar   type;
63         uchar   code;
64         uchar   cksum[2];
65         uchar   cttl;
66         uchar   mor;
67         uchar   routerlt[2];
68         uchar   rchbltime[4];
69         uchar   rxmtimer[4];
70 };
71
72 struct Lladdropt {
73         uchar   type;
74         uchar   len;
75         uchar   lladdr[6];
76 };
77
78 struct Prefixopt {
79         uchar   type;
80         uchar   len;
81         uchar   plen;
82         uchar   lar;
83         uchar   validlt[4];
84         uchar   preflt[4];
85         uchar   reserv[4];
86         uchar   pref[16];
87 };
88
89 struct Mtuopt {
90         uchar   type;
91         uchar   len;
92         uchar   reserv[2];
93         uchar   mtu[4];
94 };
95
96 struct Ipaddrsopt {
97         uchar   type;
98         uchar   len;
99         uchar   reserv[2];
100         uchar   lifetime[4];
101         uchar   addrs[];
102 };
103
104 uchar v6allroutersL[IPaddrlen] = {
105         0xff, 0x02, 0, 0,
106         0, 0, 0, 0,
107         0, 0, 0, 0,
108         0, 0, 0, 0x02
109 };
110
111 uchar v6allnodesL[IPaddrlen] = {
112         0xff, 0x02, 0, 0,
113         0, 0, 0, 0,
114         0, 0, 0, 0,
115         0, 0, 0, 0x01
116 };
117
118 uchar v6Unspecified[IPaddrlen] = {
119         0, 0, 0, 0,
120         0, 0, 0, 0,
121         0, 0, 0, 0,
122         0, 0, 0, 0
123 };
124
125 uchar v6loopback[IPaddrlen] = {
126         0, 0, 0, 0,
127         0, 0, 0, 0,
128         0, 0, 0, 0,
129         0, 0, 0, 1
130 };
131
132 uchar v6glunicast[IPaddrlen] = {
133         0x08, 0, 0, 0,
134         0, 0, 0, 0,
135         0, 0, 0, 0,
136         0, 0, 0, 0
137 };
138
139 uchar v6linklocal[IPaddrlen] = {
140         0xfe, 0x80, 0, 0,
141         0, 0, 0, 0,
142         0, 0, 0, 0,
143         0, 0, 0, 0
144 };
145
146 uchar v6solpfx[IPaddrlen] = {
147         0xff, 0x02, 0, 0,
148         0, 0, 0, 0,
149         0, 0, 0, 1,
150         /* last 3 bytes filled with low-order bytes of addr being solicited */
151         0xff, 0, 0, 0,
152 };
153
154 uchar v6defmask[IPaddrlen] = {
155         0xff, 0xff, 0xff, 0xff,
156         0xff, 0xff, 0xff, 0xff,
157         0, 0, 0, 0,
158         0, 0, 0, 0
159 };
160
161 #pragma varargck argpos ralog 1
162
163 #define RALOG "v6routeradv"
164
165 static void
166 ralog(char *fmt, ...)
167 {
168         char msg[512];
169         va_list arg;
170
171         va_start(arg, fmt);
172         vseprint(msg, msg+sizeof msg, fmt, arg);
173         va_end(arg);
174         syslog(debug, RALOG, msg);
175 }
176
177 void
178 v6paraminit(Conf *cf)
179 {
180         cf->sendra = cf->recvra = 0;
181         cf->mflag = 0;
182         cf->oflag = 0;
183         cf->linkmtu = DEFMTU;
184         cf->maxraint = Maxv6initraintvl;
185         cf->minraint = Maxv6initraintvl / 4;
186         cf->reachtime = V6reachabletime;
187         cf->rxmitra = V6retranstimer;
188         cf->ttl = MAXTTL;
189
190         cf->routerlt = 0;
191
192         cf->prefixlen = 64;
193         cf->onlink = 0;
194         cf->autoflag = 0;
195         cf->validlt = cf->preflt = ~0L;
196 }
197
198 void
199 parse6pref(int argc, char **argv)
200 {
201         switch(argc){
202         case 6:
203                 conf.preflt = strtoul(argv[5], 0, 10);
204                 /* fall through */
205         case 5:
206                 conf.validlt = strtoul(argv[4], 0, 10);
207                 /* fall through */
208         case 4:
209                 conf.autoflag = (atoi(argv[3]) != 0);
210                 /* fall through */
211         case 3:
212                 conf.onlink = (atoi(argv[2]) != 0);
213                 /* fall through */
214         case 2:
215                 conf.prefixlen = atoi(argv[1]);
216                 /* fall through */
217         case 1:
218                 if (parseip(conf.v6pref, argv[0]) == -1)
219                         sysfatal("bad address %s", argv[0]);
220                 break;
221         }
222         DEBUG("parse6pref: pref %I len %d", conf.v6pref, conf.prefixlen);
223 }
224
225 /* parse router advertisement (keyword, value) pairs */
226 void
227 parse6ra(int argc, char **argv)
228 {
229         int i, argsleft;
230         char *kw, *val;
231
232         if (argc % 2 != 0)
233                 usage();
234
235         i = 0;
236         for (argsleft = argc; argsleft > 1; argsleft -= 2) {
237                 kw =  argv[i];
238                 val = argv[i+1];
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);
261                 else {
262                         warning("bad ra6 keyword %s", kw);
263                         usage();
264                 }
265                 i += 2;
266         }
267
268         /* consistency check */
269         if (conf.maxraint < conf.minraint)
270                 sysfatal("maxraint %d < minraint %d",
271                         conf.maxraint, conf.minraint);
272 }
273
274 void
275 ea2lla(uchar *lla, uchar *ea)
276 {
277         memset(lla, 0, IPaddrlen);
278         lla[0]  = 0xFE;
279         lla[1]  = 0x80;
280         lla[8]  = ea[0] ^ 0x2;
281         lla[9]  = ea[1];
282         lla[10] = ea[2];
283         lla[11] = 0xFF;
284         lla[12] = 0xFE;
285         lla[13] = ea[3];
286         lla[14] = ea[4];
287         lla[15] = ea[5];
288 }
289
290 int
291 findllip(uchar *ip, Ipifc *ifc)
292 {
293         Iplifc *lifc;
294
295         for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
296                 if(ISIPV6LINKLOCAL(lifc->ip)){
297                         ipmove(ip, lifc->ip);
298                         return 1;
299                 }
300         }
301         ipmove(ip, v6Unspecified);
302         return 0;
303 }
304
305 static int
306 dialicmpv6(uchar *ip, int port)
307 {
308         char addr[128], local[128];
309         int fd, cfd;
310
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");
317         if(ISIPV6MCAST(ip))
318                 fprint(cfd, "addmulti %I", conf.lladdr);
319         close(cfd);
320         return fd;
321 }
322
323 static int
324 arpenter(uchar *ip, uchar *mac)
325 {
326         char buf[256];
327         int fd, n;
328
329         if(!validip(ip))
330                 return 0;
331         snprint(buf, sizeof buf, "%s/arp", conf.mpoint);
332         if((fd = open(buf, OWRITE)) < 0){
333                 warning("couldn't open %s: %r", buf);
334                 return -1;
335         }
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);
339                 close(fd);
340                 return 0;
341         }
342         close(fd);
343         return 1;
344 }
345
346 static int
347 arpcheck(uchar *ip)
348 {
349         char buf[256], *f[5], *p;
350         uchar addr[IPaddrlen];
351         Biobuf *bp;
352         int rv;
353
354         snprint(buf, sizeof buf, "%s/arp", conf.mpoint);
355         bp = Bopen(buf, OREAD);
356         if(bp == nil){
357                 warning("couldn't open %s: %r", buf);
358                 return -1;
359         }
360         rv = 0;
361         while((p = Brdline(bp, '\n')) != nil){
362                 p[Blinelen(bp)-1] = 0;
363                 if(tokenize(p, f, nelem(f)) < 3)
364                         continue;
365                 if(parseip(addr, f[2]) != -1)
366                         continue;
367                 if(ipcmp(addr, ip) == 0){
368                         rv = 1;
369                         break;
370                 }
371         }
372         Bterm(bp);
373         return rv;
374 }
375
376 /* add ipv6 addr to an interface */
377 int
378 ip6cfg(void)
379 {
380         int tentative, n;
381         char buf[256];
382
383         if(!validip(conf.laddr) || isv4(conf.laddr))
384                 return -1;
385
386         tentative = dupl_disc;
387
388 Again:
389         if(tentative)
390                 n = sprint(buf, "try");
391         else
392                 n = sprint(buf, "add");
393
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);
400                 if(conf.mtu != 0)
401                         n += snprint(buf+n, sizeof buf-n, " %d", conf.mtu);
402         }
403
404         if(write(conf.cfd, buf, n) < 0){
405                 warning("write(%s): %r", buf);
406                 return -1;
407         }
408
409         if(!tentative){
410                 if(validip(conf.gaddr) && !isv4(conf.gaddr))
411                         adddefroute(conf.gaddr, conf.laddr, conf.laddr, conf.mask);
412                 return 0;
413         }
414
415         sleep(1000);
416
417         if(arpcheck(conf.laddr) <= 0) {
418                 tentative = 0;
419                 goto Again;
420         }
421
422         warning("found dup entry in arp cache");
423         ipunconfig();
424         return -1;
425 }
426
427 static int
428 recvra6on(Ipifc *ifc)
429 {
430         if(ifc == nil)
431                 return 0;
432         else if(ifc->sendra6 > 0)
433                 return IsRouter;
434         else if(ifc->recvra6 > 0)
435                 return IsHostRecv;
436         else
437                 return IsHostNoRecv;
438 }
439
440 static void
441 sendrs(int fd, uchar *dst)
442 {
443         Routersol *rs;
444         Lladdropt *llao;
445         uchar buf[1024];
446         int pktlen;
447
448         memset(buf, 0, sizeof buf);
449
450         rs = (Routersol*)buf;
451         rs->type = ICMP6_RS;
452         ipmove(rs->dst, dst);
453         ipmove(rs->src, conf.lladdr);
454         pktlen = sizeof *rs;
455
456         if(conf.hwalen > 0){
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;
462         }
463
464         if(write(fd, rs, pktlen) != pktlen)
465                 ralog("sendrs: write failed, pkt size %d", pktlen);
466         else
467                 ralog("sendrs: sent solicitation to %I from %I on %s",
468                         rs->dst, rs->src, conf.dev);
469 }
470
471 /*
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.
476  *
477  * doing nothing for now since I don't know where to log this yet.
478  */
479 static void
480 recvrarouter(uchar buf[], int pktlen)
481 {
482         USED(buf, pktlen);
483 }
484
485 static void
486 ewrite(int fd, char *str)
487 {
488         int n;
489
490         if(fd < 0)
491                 return;
492
493         n = strlen(str);
494         if(write(fd, str, n) != n)
495                 ralog("write(%s) failed: %r", str);
496 }
497
498 static void
499 issuebasera6(Conf *cf)
500 {
501         char *cfg;
502
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);
508         free(cfg);
509 }
510
511 static void
512 issuerara6(Conf *cf)
513 {
514         char *cfg;
515
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);
519         free(cfg);
520 }
521
522 static void
523 issueadd6(Conf *cf)
524 {
525         char *cfg;
526
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);
530         free(cfg);
531 }
532
533 static int
534 masklen(uchar *mask)
535 {
536         int len;
537
538         for(len=0; len < 128; len += 8){
539                 if(*mask != 255)
540                         break;
541                 mask++;
542         }
543         while(len < 128 && (*mask & (0x80 >> (len & 7))) != 0)
544                 len++;
545         return len;
546 }
547
548 static void
549 genipmkask(uchar *mask, int len)
550 {
551         memset(mask, 0, IPaddrlen);
552         if(len < 0)
553                 len = 0;
554         else if(len > 128)
555                 len = 128;
556         for(; len >= 8; len -= 8)
557                 *mask++ = 255;
558         if(len > 0)
559                 *mask = ~((1<<(8-len))-1);
560 }
561
562 static int
563 seen(Conf *cf)
564 {
565         static uchar tab[SHA1dlen*100], *w;
566         uchar hash[SHA1dlen], *r;
567
568         sha1((uchar*)cf, sizeof(*cf), hash, nil);
569         if(w == nil || w == &tab[sizeof(tab)])
570                 w = tab;
571         for(r = tab; r < w; r += SHA1dlen)
572                 if(memcmp(r, hash, SHA1dlen) == 0)
573                         return 1;
574         memmove(w, hash, SHA1dlen);
575         w += SHA1dlen;
576         return 0;
577 }
578
579 static int
580 pnames(uchar *d, int nd, char *s)
581 {
582         uchar *de = d + nd;
583         int l;
584
585         if(nd < 1)
586                 return -1;
587         for(; *s != 0; s++){
588                 for(l = 0; *s != 0 && *s != '.' && *s != ' '; l++)
589                         s++;
590
591                 d += l+1;
592                 if(d >= de || l > 077)
593                         return -1;
594
595                 d[-l-1] = l;
596                 memmove(d-l, s-l, l);
597
598                 if(*s != '.')
599                         *d++ = 0;
600         }
601         return d - (de - nd);
602 }
603
604 static int
605 gnames(char *d, int nd, uchar *s, int ns)
606 {
607         uchar *se = s + ns;
608         char  *de = d + nd;
609         int l;
610
611         if(nd < 1 || ns < 1)
612                 return -1;
613         l = *s++ & 077;
614         while(l > 0){
615                 if(d + l >= de || s + l >= se)
616                         return -1;
617
618                 memmove(d, s, l);
619                 d += l;
620                 s += l;
621
622                 l = *s++ & 077;
623                 if(l > 0)
624                         *d++ = '.';
625                 else {
626                         if(s >= se)
627                                 break;
628
629                         l = *s++ & 077;
630                         if(l == 0)
631                                 break;
632                         *d++ = ' ';
633                 }
634         }
635         *d = 0;
636         return d - (de - nd);
637 }
638
639 /*
640  * host receiving a router advertisement calls this
641  */
642 static void
643 recvrahost(uchar buf[], int pktlen)
644 {
645         char dnsdomain[sizeof(conf.dnsdomain)];
646         int m, n, optype;
647         Lladdropt *llao;
648         Mtuopt *mtuo;
649         Prefixopt *prfo;
650         Ipaddrsopt *addrso;
651         Routeradv *ra;
652
653         m = sizeof *ra;
654         ra = (Routeradv*)buf;
655         if(pktlen < m)
656                 return;
657
658         if(!ISIPV6LINKLOCAL(ra->src))
659                 return;
660
661         conf.ttl = ra->cttl;
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;
668
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));
673
674         /* process options */
675         while(pktlen - m >= 8) {
676                 n = m;
677                 optype = buf[n];
678                 m += 8 * buf[n+1];
679                 if(m <= n || pktlen < m)
680                         return;
681
682                 switch (optype) {
683                 case V6nd_srclladdr:
684                         llao = (Lladdropt*)&buf[n];
685                         if(llao->len == 1 && conf.hwalen == 6)
686                                 arpenter(ra->src, llao->lladdr);
687                         break;
688                 case V6nd_mtu:
689                         mtuo = (Mtuopt*)&buf[n];
690                         conf.linkmtu = nhgetl(mtuo->mtu);
691                         break;
692
693                 case V6nd_rdnssl:
694                         addrso = (Ipaddrsopt*)&buf[n];
695                         if(gnames(dnsdomain, sizeof(dnsdomain),
696                                 addrso->addrs, (addrso->len - 1)*8) <= 0)
697                                 break;
698                         addnames(conf.dnsdomain, dnsdomain, sizeof(conf.dnsdomain));
699                         break;
700
701                 case V6nd_rdns:
702                         addrso = (Ipaddrsopt*)&buf[n];
703                         n = (addrso->len - 1) * 8;
704                         if(n == 0 || n % IPaddrlen)
705                                 break;
706                         addaddrs(conf.dns, sizeof(conf.dns), addrso->addrs, n);
707                         break;
708
709                 case V6nd_9fs:
710                         addrso = (Ipaddrsopt*)&buf[n];
711                         n = (addrso->len - 1) * 8;
712                         if(n == 0 || n % IPaddrlen || !plan9)
713                                 break;
714                         addaddrs(conf.fs, sizeof(conf.fs), addrso->addrs, n);
715                         break;
716                 case V6nd_9auth:
717                         addrso = (Ipaddrsopt*)&buf[n];
718                         n = (addrso->len - 1) * 8;
719                         if(n == 0 || n % IPaddrlen || !plan9)
720                                 break;
721                         addaddrs(conf.auth, sizeof(conf.auth), addrso->addrs, n);
722                         break;
723                 }
724         }
725         issuebasera6(&conf);
726
727         /* process prefixes */
728         m = sizeof *ra;
729         while(pktlen - m >= 8) {
730                 n = m;
731                 optype = buf[n];
732                 m += 8 * buf[n+1];
733                 if(m <= n || pktlen < m)
734                         return;
735
736                 if(optype != V6nd_pfxinfo)
737                         continue;
738
739                 prfo = (Prefixopt*)&buf[n];
740                 if(prfo->len != 4)
741                         continue;
742
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);
752
753                 if(conf.routerlt == 0)
754                         ipmove(conf.gaddr, IPnoaddr);
755                 else if((prfo->lar & RFMASK) != 0)
756                         ipmove(conf.gaddr, prfo->pref);
757                 else
758                         ipmove(conf.gaddr, ra->src);
759         
760                 if(conf.prefixlen < 1
761                 || conf.prefixlen > 64
762                 || !validip(conf.v6pref)
763                 || isv4(conf.v6pref)
764                 || ipcmp(conf.v6pref, v6loopback) == 0
765                 || ISIPV6MCAST(conf.v6pref)
766                 || ISIPV6LINKLOCAL(conf.v6pref)){
767                         if(!seen(&conf))
768                                 ralog("igoring bogus prefix from %I on %s; pfx %I %M",
769                                         ra->src, conf.dev, conf.v6pref, conf.mask);
770                         continue;
771                 }
772
773                 /* add prefix and update parameters */
774                 issueadd6(&conf);
775
776                 /* report this prefix configuration only once */
777                 if(seen(&conf))
778                         continue;
779
780                 ralog("got RA from %I on %s; pfx %I %M",
781                         ra->src, conf.dev, conf.v6pref, conf.mask);
782
783                 if(validip(conf.gaddr))
784                         adddefroute(conf.gaddr, conf.lladdr, conf.laddr, conf.mask);
785
786                 if(beprimary)
787                         putndb();
788                 refresh();
789         }
790 }
791
792 /*
793  * daemon to receive router advertisements from routers
794  */
795 static int
796 recvra6(void)
797 {
798         int fd, n, sendrscnt, recvracnt, sleepfor;
799         uchar buf[4096];
800         Ipifc *ifc;
801
802         ifc = readipifc(conf.mpoint, nil, myifc);
803         if(ifc == nil)
804                 sysfatal("can't read ipifc: %r");
805
806         if(!findllip(conf.lladdr, ifc))
807                 sysfatal("no link local address");
808
809         fd = dialicmpv6(v6allnodesL, ICMP6_RA);
810         if(fd < 0)
811                 sysfatal("can't open icmp_ra connection: %r");
812
813         switch(rfork(RFPROC|RFMEM|RFFDG|RFNOWAIT|RFNOTEG)){
814         case -1:
815                 sysfatal("can't fork: %r");
816         default:
817                 close(fd);
818                 ralog("recvra6 on %s", conf.dev);
819
820                 /* wait for initial RA */
821                 return (int)(uintptr)rendezvous(recvra6, (void*)0);
822         case 0:
823                 break;
824         }
825         procsetname("recvra6 on %s %I", conf.dev, conf.lladdr);
826         notify(catch);
827
828         sendrscnt = 0;
829         if(recvra6on(ifc) == IsHostRecv){
830                 sendrs(fd, v6allroutersL);
831                 sendrscnt = Maxv6rss;
832         }
833
834         recvracnt = Maxv6initras;
835         sleepfor = Minv6interradelay;
836
837         for (;;) {
838                 alarm(sleepfor);
839                 n = read(fd, buf, sizeof buf);
840                 sleepfor = alarm(0);
841
842                 /* wait for alarm to expire */
843                 if(sendrscnt < 0 && sleepfor > 100)
844                         continue;
845
846                 ifc = readipifc(conf.mpoint, ifc, myifc);
847                 if(ifc == nil) {
848                         ralog("recvra6: can't read router params on %s, quitting on %s",
849                                 conf.mpoint, conf.dev);
850                         if(sendrscnt >= 0)
851                                 rendezvous(recvra6, (void*)-1);
852                         exits(nil);
853                 }
854
855                 if(n <= 0) {
856                         if(sendrscnt > 0) {
857                                 sendrscnt--;
858                                 sendrs(fd, v6allroutersL);
859                                 sleepfor = V6rsintvl + nrand(100);
860                         }
861                         if(sendrscnt == 0) {
862                                 sendrscnt--;
863                                 ralog("recvra6: no router advs after %d sols on %s",
864                                         Maxv6rss, conf.dev);
865                                 rendezvous(recvra6, (void*)0);
866                                 sleepfor = 0;
867                         }
868                         continue;
869                 }
870
871                 switch (recvra6on(ifc)) {
872                 case IsRouter:
873                         recvrarouter(buf, n);
874                         break;
875                 case IsHostRecv:
876                         recvrahost(buf, n);
877                         break;
878                 case IsHostNoRecv:
879                         ralog("recvra6: recvra off, quitting on %s", conf.dev);
880                         if(sendrscnt >= 0)
881                                 rendezvous(recvra6, (void*)-1);
882                         exits(nil);
883                 }
884
885                 /* got at least initial ra; no whining */
886                 if(sendrscnt >= 0)
887                         rendezvous(recvra6, (void*)1);
888                 sendrscnt = -1;
889                 sleepfor = 0;
890
891                 if(recvracnt > 0)
892                         recvracnt--;
893                 else {
894                         recvracnt = Maxv6initras;
895                         sleepfor = Maxv6radelay;
896                 }
897         }
898 }
899
900 /*
901  * return -1 -- error, reading/writing some file,
902  *         0 -- no arp table updates
903  *         1 -- successful arp table update
904  */
905 static int
906 recvrs(uchar *buf, int pktlen, uchar *sol)
907 {
908         int n;
909         Routersol *rs;
910         Lladdropt *llao;
911
912         n = sizeof *rs + sizeof *llao;
913         rs = (Routersol*)buf;
914         if(pktlen < n)
915                 return 0;
916
917         llao = (Lladdropt*)&buf[sizeof *rs];
918         if(llao->type != V6nd_srclladdr || llao->len != 1 || conf.hwalen != 6)
919                 return 0;
920
921         if(!validip(rs->src)
922         || isv4(rs->src)
923         || ipcmp(rs->src, v6loopback) == 0
924         || ISIPV6MCAST(rs->src))
925                 return 0;
926
927         if((n = arpenter(rs->src, llao->lladdr)) <= 0)
928                 return n;
929
930         ipmove(sol, rs->src);
931         return 1;
932 }
933
934 static void
935 sendra(int fd, uchar *dst, int rlt, Ipifc *ifc, Ndb *db)
936 {
937         uchar dns[sizeof(conf.dns)], fs[sizeof(conf.fs)], auth[sizeof(conf.auth)];
938         char dnsdomain[sizeof(conf.dnsdomain)];
939         Ipaddrsopt *addrso;
940         Prefixopt *prfo;
941         Iplifc *lifc;
942         Routeradv *ra;
943         uchar buf[1024];
944         int pktlen, n;
945
946         memset(dns, 0, sizeof(dns));
947         memset(fs, 0, sizeof(fs));
948         memset(auth, 0, sizeof(auth));
949         memset(dnsdomain, 0, sizeof(dnsdomain));
950
951         memset(buf, 0, sizeof buf);
952
953         ra = (Routeradv*)buf;
954         ipmove(ra->dst, dst);
955         ipmove(ra->src, conf.lladdr);
956         ra->type = ICMP6_RA;
957         ra->cttl = conf.ttl;
958         if(conf.mflag > 0)
959                 ra->mor |= MFMASK;
960         if(conf.oflag > 0)
961                 ra->mor |= OCMASK;
962         if(rlt > 0)
963                 hnputs(ra->routerlt, conf.routerlt);
964         else
965                 hnputs(ra->routerlt, 0);
966         hnputl(ra->rchbltime, conf.reachtime);
967         hnputl(ra->rxmtimer, conf.rxmitra);
968         pktlen = sizeof *ra;
969
970         /*
971          * include link layer address (mac address for now) in
972          * link layer address option
973          */
974         if(conf.hwalen > 0){
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;
980         }
981
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)
985                         break;
986
987                 if(!validip(lifc->ip)
988                 || isv4(lifc->ip)
989                 || ipcmp(lifc->ip, v6loopback) == 0
990                 || ISIPV6MCAST(lifc->ip)
991                 || ISIPV6LINKLOCAL(lifc->ip))
992                         continue;
993
994                 prfo = (Prefixopt*)&buf[pktlen];
995                 prfo->type = V6nd_pfxinfo;
996                 prfo->len = 4;
997                 prfo->plen = masklen(lifc->mask) & 127;
998                 if(prfo->plen == 0)
999                         continue;
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;
1005
1006                 /* get ndb configuration for this prefix */
1007                 ipmove(conf.laddr, lifc->ip);
1008                 ndb2conf(db, lifc->net);
1009
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));
1013
1014                 addnames(dnsdomain, conf.dnsdomain, sizeof(dnsdomain));
1015         }
1016
1017         addrso = (Ipaddrsopt*)&buf[pktlen];
1018         n = pnames(addrso->addrs, sizeof buf - 8 - pktlen, dnsdomain);
1019         if(n > 0){
1020                 addrso->type = V6nd_rdnssl;
1021                 addrso->len = 1 + ((n + 7) / 8);
1022                 hnputl(addrso->lifetime, ~0L);
1023                 pktlen += 8 * addrso->len;
1024         }
1025
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;
1033         }
1034
1035         if(!plan9)
1036                 goto send;
1037
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;
1046         }
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;
1054         }
1055
1056 send:
1057         write(fd, buf, pktlen);
1058 }
1059
1060 /*
1061  * daemon to send router advertisements to hosts
1062  */
1063 static void
1064 sendra6(void)
1065 {
1066         int fd, n, sleepfor, nquitmsgs;
1067         uchar buf[4096], dst[IPaddrlen];
1068         Ipifc *ifc;
1069         Ndb *db;
1070
1071         db = opendatabase();
1072         if(db == nil)
1073                 warning("couldn't open ndb: %r");
1074
1075         ifc = readipifc(conf.mpoint, nil, myifc);
1076         if(ifc == nil)
1077                 sysfatal("can't read ipifc: %r");
1078
1079         if(!findllip(conf.lladdr, ifc))
1080                 sysfatal("no link local address");
1081
1082         fd = dialicmpv6(v6allroutersL, ICMP6_RS);
1083         if(fd < 0)
1084                 sysfatal("can't open icmp_rs connection: %r");
1085
1086         switch(rfork(RFPROC|RFMEM|RFFDG|RFNOWAIT|RFNOTEG)){
1087         case -1:
1088                 sysfatal("can't fork: %r");
1089         default:
1090                 close(fd);
1091                 ralog("sendra6 on %s", conf.dev);
1092                 return;
1093         case 0:
1094                 break;
1095         }
1096         procsetname("sendra6 on %s %I", conf.dev, conf.lladdr);
1097         notify(catch);
1098
1099         nquitmsgs = Maxv6finalras;
1100         sleepfor = 100 + jitter();
1101
1102         for (;;) {
1103                 alarm(sleepfor);
1104                 n = read(fd, buf, sizeof buf);
1105                 sleepfor = alarm(0);
1106
1107                 if(ifc->sendra6 > 0 && n > 0 && recvrs(buf, n, dst) > 0)
1108                         sendra(fd, dst, 1, ifc, db);
1109
1110                 /* wait for alarm to expire */
1111                 if(sleepfor > 100)
1112                         continue;
1113                 sleepfor = Minv6interradelay;
1114
1115                 ifc = readipifc(conf.mpoint, ifc, myifc);
1116                 if(ifc == nil) {
1117                         ralog("sendra6: can't read router params on %s, quitting on %s",
1118                                 conf.mpoint, conf.dev);
1119                         exits(nil);
1120                 }
1121                 if(ifc->sendra6 <= 0){
1122                         if(nquitmsgs > 0) {
1123                                 nquitmsgs--;
1124                                 sendra(fd, v6allnodesL, 0, ifc, nil);
1125                                 continue;
1126                         }
1127                         ralog("sendra6: sendra off on %s, quitting on %s",
1128                                 conf.mpoint, conf.dev);
1129                         exits(nil);
1130                 }
1131                 db = opendatabase();
1132                 sendra(fd, v6allnodesL, 1, ifc, db);
1133                 sleepfor = randint(ifc->rp.minraint, ifc->rp.maxraint);
1134         }
1135 }
1136
1137 static void
1138 startra6(void)
1139 {
1140         static char routeon[] = "iprouting 1";
1141
1142         if(conf.recvra > 0)
1143                 recvra6();
1144
1145         if(conf.sendra > 0) {
1146                 if(write(conf.cfd, routeon, sizeof routeon - 1) < 0) {
1147                         warning("write (%s) failed: %r", routeon);
1148                         return;
1149                 }
1150                 sendra6();
1151                 if(conf.recvra <= 0)
1152                         recvra6();
1153         }
1154 }
1155
1156 void
1157 doipv6(int what)
1158 {
1159         fprint(conf.rfd, "tag ra6");
1160
1161         switch (what) {
1162         default:
1163                 sysfatal("unknown IPv6 verb");
1164         case Vaddpref6:
1165                 issueadd6(&conf);
1166                 refresh();
1167                 break;
1168         case Vra6:
1169                 issuebasera6(&conf);
1170                 issuerara6(&conf);
1171                 dolog = 1;
1172                 startra6();
1173                 break;
1174         }
1175 }