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