]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ip/ipconfig/ipv6.c
ip/ipconfig: always refresh ndb/cs and ndb/dns when adding or removing ip addresses
[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 "ipconfig.h"
12 #include "../icmp.h"
13
14 #include <libsec.h>
15
16 #pragma varargck argpos ralog 1
17
18 #define RALOG "v6routeradv"
19
20 #define NetS(x) (((uchar*)x)[0]<< 8 | ((uchar*)x)[1])
21 #define NetL(x) (((uchar*)x)[0]<<24 | ((uchar*)x)[1]<<16 | \
22                  ((uchar*)x)[2]<< 8 | ((uchar*)x)[3])
23
24 enum {
25         ICMP6LEN=       4,
26 };
27
28 typedef struct Hdr Hdr;
29 struct Hdr                      /* ICMP v4 & v6 header */
30 {
31         uchar   type;
32         uchar   code;
33         uchar   cksum[2];       /* Checksum */
34         uchar   data[];
35 };
36
37 char *icmpmsg6[Maxtype6+1] =
38 {
39 [EchoReply]             "EchoReply",
40 [UnreachableV6]         "UnreachableV6",
41 [PacketTooBigV6]        "PacketTooBigV6",
42 [TimeExceedV6]          "TimeExceedV6",
43 [Redirect]              "Redirect",
44 [EchoRequest]           "EchoRequest",
45 [TimeExceed]            "TimeExceed",
46 [InParmProblem]         "InParmProblem",
47 [Timestamp]             "Timestamp",
48 [TimestampReply]        "TimestampReply",
49 [InfoRequest]           "InfoRequest",
50 [InfoReply]             "InfoReply",
51 [AddrMaskRequest]       "AddrMaskRequest",
52 [AddrMaskReply]         "AddrMaskReply",
53 [EchoRequestV6]         "EchoRequestV6",
54 [EchoReplyV6]           "EchoReplyV6",
55 [RouterSolicit]         "RouterSolicit",
56 [RouterAdvert]          "RouterAdvert",
57 [NbrSolicit]            "NbrSolicit",
58 [NbrAdvert]             "NbrAdvert",
59 [RedirectV6]            "RedirectV6",
60 };
61
62 static char *icmp6opts[] =
63 {
64 [0]                     "unknown option",
65 [V6nd_srclladdr]        "srcll_addr",
66 [V6nd_targlladdr]       "targll_addr",
67 [V6nd_pfxinfo]          "prefix",
68 [V6nd_redirhdr]         "redirect",
69 [V6nd_mtu]              "mtu",
70 [V6nd_home]             "home",
71 [V6nd_srcaddrs]         "src_addrs",
72 [V6nd_ip]               "ip",
73 [V6nd_rdns]             "rdns",
74 [V6nd_9fs]              "9fs",
75 [V6nd_9auth]            "9auth",
76 };
77
78 uchar v6allroutersL[IPaddrlen] = {
79         0xff, 0x02, 0, 0,
80         0, 0, 0, 0,
81         0, 0, 0, 0,
82         0, 0, 0, 0x02
83 };
84
85 uchar v6allnodesL[IPaddrlen] = {
86         0xff, 0x02, 0, 0,
87         0, 0, 0, 0,
88         0, 0, 0, 0,
89         0, 0, 0, 0x01
90 };
91
92 uchar v6Unspecified[IPaddrlen] = {
93         0, 0, 0, 0,
94         0, 0, 0, 0,
95         0, 0, 0, 0,
96         0, 0, 0, 0
97 };
98
99 uchar v6loopback[IPaddrlen] = {
100         0, 0, 0, 0,
101         0, 0, 0, 0,
102         0, 0, 0, 0,
103         0, 0, 0, 1
104 };
105
106 uchar v6glunicast[IPaddrlen] = {
107         0x08, 0, 0, 0,
108         0, 0, 0, 0,
109         0, 0, 0, 0,
110         0, 0, 0, 0
111 };
112
113 uchar v6linklocal[IPaddrlen] = {
114         0xfe, 0x80, 0, 0,
115         0, 0, 0, 0,
116         0, 0, 0, 0,
117         0, 0, 0, 0
118 };
119
120 uchar v6solpfx[IPaddrlen] = {
121         0xff, 0x02, 0, 0,
122         0, 0, 0, 0,
123         0, 0, 0, 1,
124         /* last 3 bytes filled with low-order bytes of addr being solicited */
125         0xff, 0, 0, 0,
126 };
127
128 uchar v6defmask[IPaddrlen] = {
129         0xff, 0xff, 0xff, 0xff,
130         0xff, 0xff, 0xff, 0xff,
131         0, 0, 0, 0,
132         0, 0, 0, 0
133 };
134
135 enum
136 {
137         Vadd,
138         Vremove,
139         Vunbind,
140         Vaddpref6,
141         Vra6,
142 };
143
144 static void
145 ralog(char *fmt, ...)
146 {
147         char msg[512];
148         va_list arg;
149
150         va_start(arg, fmt);
151         vseprint(msg, msg+sizeof msg, fmt, arg);
152         va_end(arg);
153         syslog(debug, RALOG, msg);
154 }
155
156 void
157 ea2lla(uchar *lla, uchar *ea)
158 {
159         assert(IPaddrlen == 16);
160         memset(lla, 0, IPaddrlen);
161         lla[0]  = 0xFE;
162         lla[1]  = 0x80;
163         lla[8]  = ea[0] ^ 0x2;
164         lla[9]  = ea[1];
165         lla[10] = ea[2];
166         lla[11] = 0xFF;
167         lla[12] = 0xFE;
168         lla[13] = ea[3];
169         lla[14] = ea[4];
170         lla[15] = ea[5];
171 }
172
173 void
174 ipv62smcast(uchar *smcast, uchar *a)
175 {
176         assert(IPaddrlen == 16);
177         memset(smcast, 0, IPaddrlen);
178         smcast[0]  = 0xFF;
179         smcast[1]  = 0x02;
180         smcast[11] = 0x1;
181         smcast[12] = 0xFF;
182         smcast[13] = a[13];
183         smcast[14] = a[14];
184         smcast[15] = a[15];
185 }
186
187 void
188 v6paraminit(Conf *cf)
189 {
190         cf->sendra = cf->recvra = 0;
191         cf->mflag = 0;
192         cf->oflag = 0;
193         cf->maxraint = Maxv6initraintvl;
194         cf->minraint = Maxv6initraintvl / 4;
195         cf->linkmtu = 1500;
196         cf->reachtime = V6reachabletime;
197         cf->rxmitra = V6retranstimer;
198         cf->ttl = MAXTTL;
199
200         cf->routerlt = 0;
201
202         cf->prefixlen = 64;
203         cf->onlink = 0;
204         cf->autoflag = 0;
205         cf->validlt = cf->preflt = ~0L;
206 }
207
208 static char *
209 optname(unsigned opt)
210 {
211         static char buf[32];
212
213         if(opt >= nelem(icmp6opts) || icmp6opts[opt] == nil) {
214                 snprint(buf, sizeof buf, "unknown option %d", opt);
215                 return buf;
216         } else
217                 return icmp6opts[opt];
218 }
219
220 static char*
221 opt_seprint(uchar *ps, uchar *pe, char *sps, char *spe)
222 {
223         int otype, osz, pktlen;
224         uchar *a;
225         char *p = sps, *e = spe;
226
227         a = ps;
228         for (pktlen = pe - ps; pktlen > 0; pktlen -= osz) {
229                 otype = a[0];
230                 osz = a[1] * 8;
231
232                 switch (otype) {
233                 default:
234                         return seprint(p, e, " option=%s ", optname(otype));
235                 case V6nd_srclladdr:
236                 case V6nd_targlladdr:
237                         if(pktlen < osz || osz != 8)
238                                 return seprint(p, e, " option=%s bad size=%d",
239                                         optname(otype), osz);
240                         p = seprint(p, e, " option=%s maddr=%E", optname(otype),
241                                 a+2);
242                         break;
243                 case V6nd_pfxinfo:
244                         if(pktlen < osz || osz != 32)
245                                 return seprint(p, e, " option=%s: bad size=%d",
246                                         optname(otype), osz);
247
248                         p = seprint(p, e, " option=%s pref=%I preflen=%3.3d"
249                                 " lflag=%1.1d aflag=%1.1d unused1=%1.1d"
250                                 " validlt=%ud preflt=%ud unused2=%1.1d",
251                                 optname(otype), a+16, (int)(*(a+2)),
252                                 (*(a+3) & (1 << 7)) != 0,
253                                 (*(a+3) & (1 << 6)) != 0,
254                                 (*(a+3) & 63) != 0,
255                                 NetL(a+4), NetL(a+8), NetL(a+12)!=0);
256                         break;
257                 }
258                 a += osz;
259         }
260         return p;
261 }
262
263 static void
264 catch(void *a, char *msg)
265 {
266         USED(a);
267         if(strstr(msg, "alarm"))
268                 noted(NCONT);
269         else
270                 noted(NDFLT);
271 }
272
273 static int
274 dialicmpv6(uchar *ip, int port)
275 {
276         char addr[128], local[128];
277         int fd, cfd;
278
279         snprint(addr, sizeof(addr), "%s/icmpv6!%I!%d!r", conf.mpoint, ip, port);
280         snprint(local, sizeof(local), "%I!%d", conf.laddr, port);
281         if((fd = dial(addr, local, nil, &cfd)) < 0)
282                 sysfatal("dialicmp6: %r");
283         fprint(cfd, "headers");
284         fprint(cfd, "ignoreadvice");
285         if(ISIPV6MCAST(ip))
286                 fprint(cfd, "addmulti %I", conf.laddr);
287         close(cfd);
288         return fd;
289 }
290
291 static int
292 arpenter(uchar *ip, uchar *mac)
293 {
294         char buf[256];
295         int fd, n;
296
297         if(!validip(ip))
298                 return 0;
299         snprint(buf, sizeof buf, "%s/arp", conf.mpoint);
300         if((fd = open(buf, OWRITE)) < 0){
301                 warning("couldn't open %s: %r", buf);
302                 return -1;
303         }
304         n = snprint(buf, sizeof buf, "add %s %I %E %I\n", conf.type, ip, mac, conf.laddr);
305         if(write(fd, buf, n) != n) {
306                 warning("arpenter: %s: %r", buf);
307                 close(fd);
308                 return 0;
309         }
310         close(fd);
311         return 1;
312 }
313
314 static int
315 arpcheck(uchar *ip)
316 {
317         char buf[256], *f[5], *p;
318         uchar addr[IPaddrlen];
319         Biobuf *bp;
320         int rv;
321
322         snprint(buf, sizeof buf, "%s/arp", conf.mpoint);
323         bp = Bopen(buf, OREAD);
324         if(bp == nil){
325                 warning("couldn't open %s: %r", buf);
326                 return -1;
327         }
328         rv = 0;
329         while((p = Brdline(bp, '\n')) != nil){
330                 p[Blinelen(bp)-1] = 0;
331                 if(tokenize(p, f, nelem(f)) < 3)
332                         continue;
333                 if(parseip(addr, f[2]) != -1)
334                         continue;
335                 if(ipcmp(addr, ip) == 0){
336                         rv = 1;
337                         break;
338                 }
339         }
340         Bterm(bp);
341         return rv;
342 }
343
344 static int
345 masklen(uchar *mask)
346 {
347         int len;
348
349         for(len=0; len < 128; len += 8){
350                 if(*mask != 255)
351                         break;
352                 mask++;
353         }
354         while(len < 128 && (*mask & (0x80 >> (len & 7))) != 0)
355                 len++;
356         return len;
357 }
358
359 static void
360 genipmkask(uchar *mask, int len)
361 {
362         memset(mask, 0, IPaddrlen);
363         if(len < 0)
364                 len = 0;
365         else if(len > 128)
366                 len = 128;
367         for(; len >= 8; len -= 8)
368                 *mask++ = 255;
369         if(len > 0)
370                 *mask = ~((1<<(8-len))-1);
371 }
372
373 /* add ipv6 addr to an interface */
374 int
375 ip6cfg(void)
376 {
377         int tentative, n;
378         char buf[256];
379
380         if(!validip(conf.laddr) || isv4(conf.laddr))
381                 return -1;
382
383         tentative = dupl_disc;
384
385 Again:
386         if(tentative)
387                 n = sprint(buf, "try");
388         else
389                 n = sprint(buf, "add");
390
391         n += snprint(buf+n, sizeof buf-n, " %I", conf.laddr);
392         if(!validip(conf.mask))
393                 ipmove(conf.mask, v6defmask);
394         n += snprint(buf+n, sizeof buf-n, " %M", conf.mask);
395         if(validip(conf.raddr)){
396                 n += snprint(buf+n, sizeof buf-n, " %I", conf.raddr);
397                 if(conf.mtu != 0)
398                         n += snprint(buf+n, sizeof buf-n, " %d", conf.mtu);
399         }
400
401         if(write(conf.cfd, buf, n) < 0){
402                 warning("write(%s): %r", buf);
403                 return -1;
404         }
405
406         if(!tentative){
407                 if(validip(conf.gaddr) && !isv4(conf.gaddr))
408                         adddefroute(conf.gaddr, conf.laddr, conf.laddr, conf.mask);
409                 return 0;
410         }
411
412         sleep(1000);
413
414         if(arpcheck(conf.laddr) <= 0) {
415                 tentative = 0;
416                 goto Again;
417         }
418
419         warning("found dup entry in arp cache");
420         doremove();
421         return 0;
422 }
423
424 static int
425 recvra6on(Ipifc *ifc)
426 {
427         if(ifc == nil)
428                 return 0;
429         else if(ifc->sendra6 > 0)
430                 return IsRouter;
431         else if(ifc->recvra6 > 0)
432                 return IsHostRecv;
433         else
434                 return IsHostNoRecv;
435 }
436
437 static int
438 findllip(uchar *ip, Ipifc *ifc)
439 {
440         Iplifc *lifc;
441
442         for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
443                 if(ISIPV6LINKLOCAL(lifc->ip)){
444                         ipmove(ip, lifc->ip);
445                         return 1;
446                 }
447         }
448         ipmove(ip, v6Unspecified);
449         return 0;
450 }
451
452 static void
453 sendrs(int fd, uchar *dst)
454 {
455         Routersol *rs;
456         Lladdropt *llao;
457         uchar buf[1024];
458         int pktlen;
459
460         memset(buf, 0, sizeof buf);
461
462         rs = (Routersol *)buf;
463         rs->type = ICMP6_RS;
464         ipmove(rs->dst, dst);
465         ipmove(rs->src, conf.laddr);
466         pktlen = sizeof *rs;
467
468         if(conf.hwalen > 0){
469                 llao = (Lladdropt *)&buf[pktlen];
470                 llao->type = V6nd_srclladdr;
471                 llao->len = (2+7+conf.hwalen)/8;
472                 memmove(llao->lladdr, conf.hwa, conf.hwalen);
473                 pktlen += 8 * llao->len;
474         }
475
476         if(write(fd, rs, pktlen) != pktlen)
477                 ralog("sendrs: write failed, pkt size %d", pktlen);
478         else
479                 ralog("sendrs: sent solicitation to %I from %I on %s",
480                         rs->dst, rs->src, conf.dev);
481 }
482
483 /*
484  * a router receiving a router adv from another
485  * router calls this; it is basically supposed to
486  * log the information in the ra and raise a flag
487  * if any parameter value is different from its configured values.
488  *
489  * doing nothing for now since I don't know where to log this yet.
490  */
491 static void
492 recvrarouter(uchar buf[], int pktlen)
493 {
494         USED(buf, pktlen);
495 }
496
497 /* host receiving a router advertisement calls this */
498
499 static void
500 ewrite(int fd, char *str)
501 {
502         int n;
503
504         if(fd < 0)
505                 return;
506
507         n = strlen(str);
508         if(write(fd, str, n) != n)
509                 ralog("write(%s) failed: %r", str);
510 }
511
512 static void
513 issuebasera6(Conf *cf)
514 {
515         char *cfg;
516
517         cfg = smprint("ra6 mflag %d oflag %d reachtime %d rxmitra %d "
518                 "ttl %d routerlt %d",
519                 cf->mflag, cf->oflag, cf->reachtime, cf->rxmitra,
520                 cf->ttl, cf->routerlt);
521         ewrite(cf->cfd, cfg);
522         free(cfg);
523 }
524
525 static void
526 issuerara6(Conf *cf)
527 {
528         char *cfg;
529
530         cfg = smprint("ra6 sendra %d recvra %d maxraint %d minraint %d "
531                 "linkmtu %d",
532                 cf->sendra, cf->recvra, cf->maxraint, cf->minraint,
533                 cf->linkmtu);
534         ewrite(cf->cfd, cfg);
535         free(cfg);
536 }
537
538 static void
539 issueadd6(Conf *cf)
540 {
541         char *cfg;
542
543         cfg = smprint("add6 %I %d %d %d %lud %lud", cf->v6pref, cf->prefixlen,
544                 cf->onlink, cf->autoflag, cf->validlt, cf->preflt);
545         ewrite(cf->cfd, cfg);
546         free(cfg);
547 }
548
549 static int
550 seen(Conf *cf)
551 {
552         static uchar tab[SHA1dlen*100], *w;
553         uchar hash[SHA1dlen], *r;
554
555         sha1((uchar*)cf, sizeof(*cf), hash, nil);
556         if(w == nil || w == &tab[sizeof(tab)])
557                 w = tab;
558         for(r = tab; r < w; r += SHA1dlen)
559                 if(memcmp(r, hash, SHA1dlen) == 0)
560                         return 1;
561         memmove(w, hash, SHA1dlen);
562         w += SHA1dlen;
563         return 0;
564 }
565
566 static void
567 recvrahost(uchar buf[], int pktlen)
568 {
569         int m, n, optype, needrefresh;
570         uchar src[IPaddrlen];
571         Lladdropt *llao;
572         Mtuopt *mtuo;
573         Prefixopt *prfo;
574         Routeradv *ra;
575
576         m = sizeof *ra;
577         ra = (Routeradv*)buf;
578         if(pktlen < m)
579                 return;
580
581         if(!ISIPV6LINKLOCAL(ra->src))
582                 return;
583
584         conf.ttl = ra->cttl;
585         conf.mflag = (MFMASK & ra->mor);
586         conf.oflag = (OCMASK & ra->mor);
587         conf.routerlt =  nhgets(ra->routerlt);
588         conf.reachtime = nhgetl(ra->rchbltime);
589         conf.rxmitra =   nhgetl(ra->rxmtimer);
590         issuebasera6(&conf);
591
592         needrefresh = 0;
593         while(pktlen - m >= 8) {
594                 n = m;
595                 optype = buf[n];
596                 m += 8 * buf[n+1];
597                 if(pktlen < m)
598                         return;
599
600                 switch (optype) {
601                 case V6nd_srclladdr:
602                         llao = (Lladdropt *)&buf[n];
603                         if(llao->len == 1 && conf.hwalen == 6)
604                                 arpenter(ra->src, llao->lladdr);
605                         break;
606                 case V6nd_mtu:
607                         mtuo = (Mtuopt*)&buf[n];
608                         conf.linkmtu = nhgetl(mtuo->mtu);
609                         break;
610                 case V6nd_pfxinfo:
611                         prfo = (Prefixopt*)&buf[n];
612                         if(prfo->len != 4)
613                                 break;
614
615                         conf.prefixlen = prfo->plen & 127;
616                         genipmkask(conf.mask, conf.prefixlen);
617                         maskip(prfo->pref, conf.mask, conf.v6pref);
618                         conf.onlink =   ((prfo->lar & OLMASK) != 0);
619                         conf.autoflag = ((prfo->lar & AFMASK) != 0);
620                         conf.validlt = nhgetl(prfo->validlt);
621                         conf.preflt =  nhgetl(prfo->preflt);
622                         issueadd6(&conf);
623                         
624                         if(conf.routerlt == 0)
625                                 ipmove(conf.gaddr, IPnoaddr);
626                         else if((prfo->lar & RFMASK) != 0)
627                                 ipmove(conf.gaddr, prfo->pref);
628                         else
629                                 ipmove(conf.gaddr, ra->src);
630
631                         /* report prefix only once */
632                         if(seen(&conf))
633                                 break;
634
635                         if(conf.prefixlen  == 0
636                         || !validip(conf.v6pref)
637                         || isv4(conf.v6pref)
638                         || ipcmp(conf.v6pref, v6loopback) == 0
639                         || ISIPV6MCAST(conf.v6pref)
640                         || ISIPV6LINKLOCAL(conf.v6pref)){
641                                 ralog("igoring bogus prefix from %I on %s; pfx %I %M",
642                                         ra->src, conf.dev, conf.v6pref, conf.mask);
643                                 break;
644                         }
645
646                         ralog("got initial RA from %I on %s; pfx %I %M",
647                                 ra->src, conf.dev, conf.v6pref, conf.mask);
648
649                         if(validip(conf.gaddr)){
650                                 memmove(src, conf.v6pref, 8);
651                                 memmove(src+8, conf.laddr+8, 8);
652                                 adddefroute(conf.gaddr, conf.laddr, src, conf.mask);
653                         }
654                         needrefresh = 1;
655                         break;
656                 }
657         }
658
659         if(needrefresh)
660                 refresh();
661 }
662
663 /*
664  * daemon to receive router advertisements from routers
665  */
666 void
667 recvra6(void)
668 {
669         int fd, n, sendrscnt, recvracnt, sleepfor;
670         uchar buf[4096];
671         Ipifc *ifc;
672
673         ifc = readipifc(conf.mpoint, nil, myifc);
674         if(ifc == nil)
675                 sysfatal("can't read ipifc: %r");
676
677         if(!findllip(conf.laddr, ifc))
678                 sysfatal("no link local address");
679
680         fd = dialicmpv6(v6allnodesL, ICMP6_RA);
681         if(fd < 0)
682                 sysfatal("can't open icmp_ra connection: %r");
683
684         notify(catch);
685         sendrscnt = Maxv6rss;
686         recvracnt = 0;
687
688         switch(rfork(RFPROC|RFMEM|RFFDG|RFNOWAIT|RFNOTEG)){
689         case -1:
690                 sysfatal("can't fork: %r");
691         default:
692                 close(fd);
693                 return;
694         case 0:
695                 break;
696         }
697
698         procsetname("recvra6 on %s %I", conf.dev, conf.laddr);
699         ralog("recvra6 on %s", conf.dev);
700         sleepfor = Minv6interradelay;
701         for (;;) {
702                 alarm(sleepfor);
703                 n = read(fd, buf, sizeof buf);
704                 sleepfor = alarm(0);
705
706                 /* wait for alarm to expire */
707                 if(sendrscnt < 0 && sleepfor > 100)
708                         continue;
709
710                 ifc = readipifc(conf.mpoint, ifc, myifc);
711                 if(ifc == nil) {
712                         ralog("recvra6: can't read router params on %s, quitting on %s",
713                                 conf.mpoint, conf.dev);
714                         exits(nil);
715                 }
716                 
717                 if(n <= 0) {
718                         if(sendrscnt > 0) {
719                                 sendrscnt--;
720                                 if(recvra6on(ifc) == IsHostRecv)
721                                         sendrs(fd, v6allroutersL);
722                                 sleepfor = V6rsintvl + nrand(100);
723                         }
724                         if(sendrscnt == 0) {
725                                 sendrscnt--;
726                                 sleepfor = 0;
727                                 ralog("recvra6: no router advs after %d sols on %s",
728                                         Maxv6rss, conf.dev);
729                         }
730                         continue;
731                 }
732
733                 /* got at least initial ra; no whining */
734                 sendrscnt = -1;
735                 sleepfor = 0;
736
737                 if(++recvracnt >= Maxv6initras){
738                         recvracnt = 0;
739                         sleepfor = Maxv6radelay;
740                 }
741
742                 switch (recvra6on(ifc)) {
743                 case IsRouter:
744                         recvrarouter(buf, n);
745                         break;
746                 case IsHostRecv:
747                         recvrahost(buf, n);
748                         break;
749                 case IsHostNoRecv:
750                         ralog("recvra6: recvra off, quitting on %s", conf.dev);
751                         exits(nil);
752                 }
753         }
754 }
755
756 /*
757  * return -1 -- error, reading/writing some file,
758  *         0 -- no arp table updates
759  *         1 -- successful arp table update
760  */
761 int
762 recvrs(uchar *buf, int pktlen, uchar *sol)
763 {
764         int n;
765         Routersol *rs;
766         Lladdropt *llao;
767
768         n = sizeof *rs + sizeof *llao;
769         rs = (Routersol *)buf;
770         if(pktlen < n)
771                 return 0;
772
773         llao = (Lladdropt *)&buf[sizeof *rs];
774         if(llao->type != V6nd_srclladdr || llao->len != 1 || conf.hwalen != 6)
775                 return 0;
776
777         if(!validip(rs->src)
778         || isv4(rs->src)
779         || ipcmp(rs->src, v6loopback) == 0
780         || ISIPV6MCAST(rs->src))
781                 return 0;
782
783         if((n = arpenter(rs->src, llao->lladdr)) <= 0)
784                 return n;
785
786         ipmove(sol, rs->src);
787         return 1;
788 }
789
790 void
791 sendra(int fd, uchar *dst, int rlt, Ipifc *ifc)
792 {
793         uchar buf[1024];
794         Iplifc *lifc;
795         Lladdropt *llao;
796         Prefixopt *prfo;
797         Routeradv *ra;
798         int pktlen;
799
800         memset(buf, 0, sizeof buf);
801
802         ra = (Routeradv *)buf;
803         ipmove(ra->dst, dst);
804         ipmove(ra->src, conf.laddr);
805         ra->type = ICMP6_RA;
806         ra->cttl = conf.ttl;
807         if(conf.mflag > 0)
808                 ra->mor |= MFMASK;
809         if(conf.oflag > 0)
810                 ra->mor |= OCMASK;
811         if(rlt > 0)
812                 hnputs(ra->routerlt, conf.routerlt);
813         else
814                 hnputs(ra->routerlt, 0);
815         hnputl(ra->rchbltime, conf.reachtime);
816         hnputl(ra->rxmtimer, conf.rxmitra);
817         pktlen = sizeof *ra;
818
819         /*
820          * include link layer address (mac address for now) in
821          * link layer address option
822          */
823         if(conf.hwalen > 0){
824                 llao = (Lladdropt *)&buf[pktlen];
825                 llao->type = V6nd_srclladdr;
826                 llao->len = (2+7+conf.hwalen)/8;
827                 memmove(llao->lladdr, conf.hwa, conf.hwalen);
828                 pktlen += 8 * llao->len;
829         }
830
831         /* include all global unicast prefixes on interface in prefix options */
832         for (lifc = (ifc != nil? ifc->lifc: nil); lifc != nil; lifc = lifc->next) {
833                 if(pktlen > sizeof buf - 4*8)
834                         break;
835                 if(!validip(lifc->ip)
836                 || isv4(lifc->ip)
837                 || ipcmp(lifc->ip, v6loopback) == 0
838                 || ISIPV6MCAST(lifc->ip)
839                 || ISIPV6LINKLOCAL(lifc->ip))
840                         continue;
841                 prfo = (Prefixopt *)&buf[pktlen];
842                 prfo->type = V6nd_pfxinfo;
843                 prfo->len = 4;
844                 prfo->plen = masklen(lifc->mask) & 127;
845                 if(prfo->plen == 0)
846                         continue;
847                 ipmove(prfo->pref, lifc->net);
848                 prfo->lar = AFMASK|OLMASK;
849                 hnputl(prfo->validlt, lifc->validlt);
850                 hnputl(prfo->preflt, lifc->preflt);
851                 pktlen += 8 * prfo->len;
852         }
853
854         write(fd, buf, pktlen);
855 }
856
857 /*
858  * daemon to send router advertisements to hosts
859  */
860 void
861 sendra6(void)
862 {
863         int fd, n, sleepfor, nquitmsgs;
864         uchar buf[4096], dst[IPaddrlen];
865         Ipifc *ifc;
866
867         ifc = readipifc(conf.mpoint, nil, myifc);
868         if(ifc == nil)
869                 sysfatal("can't read ipifc: %r");
870
871         if(!findllip(conf.laddr, ifc))
872                 sysfatal("no link local address");
873
874         fd = dialicmpv6(v6allroutersL, ICMP6_RS);
875         if(fd < 0)
876                 sysfatal("can't open icmp_rs connection: %r");
877
878         notify(catch);
879         nquitmsgs = Maxv6finalras;
880
881         switch(rfork(RFPROC|RFMEM|RFFDG|RFNOWAIT|RFNOTEG)){
882         case -1:
883                 sysfatal("can't fork: %r");
884         default:
885                 close(fd);
886                 return;
887         case 0:
888                 break;
889         }
890
891         procsetname("sendra6 on %s %I", conf.dev, conf.laddr);
892         ralog("sendra6 on %s", conf.dev);
893         sleepfor = 100 + jitter();
894         for (;;) {
895                 alarm(sleepfor);
896                 n = read(fd, buf, sizeof buf);
897                 sleepfor = alarm(0);
898
899                 if(n > 0 && recvrs(buf, n, dst) > 0)
900                         sendra(fd, dst, 1, ifc);
901
902                 /* wait for alarm to expire */
903                 if(sleepfor > 100)
904                         continue;
905                 sleepfor = Minv6interradelay;
906
907                 ifc = readipifc(conf.mpoint, ifc, myifc);
908                 if(ifc == nil) {
909                         ralog("sendra6: can't read router params on %s, quitting on %s",
910                                 conf.mpoint, conf.dev);
911                         exits(nil);
912                 }
913                 if(ifc->sendra6 <= 0){
914                         if(nquitmsgs > 0) {
915                                 nquitmsgs--;
916                                 sendra(fd, v6allnodesL, 0, ifc);
917                                 continue;
918                         } else {
919                                 ralog("sendra6: sendra off, quitting on %s", conf.dev);
920                                 exits(nil);
921                         }
922                 }
923                 sendra(fd, v6allnodesL, 1, ifc);
924                 sleepfor = randint(ifc->rp.minraint, ifc->rp.maxraint);
925         }
926 }
927
928 void
929 startra6(void)
930 {
931         static char routeon[] = "iprouting 1";
932
933         mklladdr();
934
935         if(conf.recvra > 0)
936                 recvra6();
937
938         if(conf.sendra > 0) {
939                 if(write(conf.cfd, routeon, sizeof routeon - 1) < 0) {
940                         warning("write (iprouting 1) failed: %r");
941                         return;
942                 }
943                 sendra6();
944                 if(conf.recvra <= 0)
945                         recvra6();
946         }
947 }
948
949 void
950 doipv6(int what)
951 {
952         fprint(conf.rfd, "tag ra6");
953
954         switch (what) {
955         default:
956                 sysfatal("unknown IPv6 verb");
957         case Vaddpref6:
958                 issueadd6(&conf);
959                 refresh();
960                 break;
961         case Vra6:
962                 issuebasera6(&conf);
963                 issuerara6(&conf);
964                 dolog = 1;
965                 startra6();
966                 break;
967         }
968 }