]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/rtadvd/config.c
This commit was generated by cvs2svn to compensate for changes in r136647,
[FreeBSD/FreeBSD.git] / usr.sbin / rtadvd / config.c
1 /*      $FreeBSD$       */
2 /*      $KAME: config.c,v 1.84 2003/08/05 12:34:23 itojun Exp $ */
3
4 /*
5  * Copyright (C) 1998 WIDE Project.
6  * All rights reserved.
7  * 
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the project nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  * 
20  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32
33 #include <sys/param.h>
34 #include <sys/ioctl.h>
35 #include <sys/socket.h>
36 #include <sys/time.h>
37 #include <sys/sysctl.h>
38
39 #include <net/if.h>
40 #include <net/if_var.h>
41 #include <net/route.h>
42 #include <net/if_dl.h>
43
44 #include <netinet/in.h>
45 #include <netinet/in_var.h>
46 #include <netinet/ip6.h>
47 #include <netinet6/ip6_var.h>
48 #include <netinet/icmp6.h>
49
50 #include <arpa/inet.h>
51
52 #include <stdio.h>
53 #include <syslog.h>
54 #include <errno.h>
55 #include <string.h>
56 #include <stdlib.h>
57 #include <unistd.h>
58 #include <ifaddrs.h>
59
60 #include "rtadvd.h"
61 #include "advcap.h"
62 #include "timer.h"
63 #include "if.h"
64 #include "config.h"
65
66 static time_t prefix_timo = (60 * 120); /* 2 hours.
67                                          * XXX: should be configurable. */
68 extern struct rainfo *ralist;
69
70 static struct rtadvd_timer *prefix_timeout __P((void *));
71 static void makeentry __P((char *, size_t, int, char *));
72 static int getinet6sysctl __P((int));
73
74 void
75 getconfig(intface)
76         char *intface;
77 {
78         int stat, i;
79         char tbuf[BUFSIZ];
80         struct rainfo *tmp;
81         long val;
82         int64_t val64;
83         char buf[BUFSIZ];
84         char *bp = buf;
85         char *addr, *flagstr;
86         static int forwarding = -1;
87
88 #define MUSTHAVE(var, cap)      \
89     do {                                                                \
90         int64_t t;                                                      \
91         if ((t = agetnum(cap)) < 0) {                                   \
92                 fprintf(stderr, "rtadvd: need %s for interface %s\n",   \
93                         cap, intface);                                  \
94                 exit(1);                                                \
95         }                                                               \
96         var = t;                                                        \
97      } while (0)
98 #define MAYHAVE(var, cap, def)  \
99      do {                                                               \
100         if ((var = agetnum(cap)) < 0)                                   \
101                 var = def;                                              \
102      } while (0)
103
104         if ((stat = agetent(tbuf, intface)) <= 0) {
105                 memset(tbuf, 0, sizeof(tbuf));
106                 syslog(LOG_INFO,
107                        "<%s> %s isn't defined in the configuration file"
108                        " or the configuration file doesn't exist."
109                        " Treat it as default",
110                         __func__, intface);
111         }
112
113         tmp = (struct rainfo *)malloc(sizeof(*ralist));
114         if (tmp == NULL) {
115                 syslog(LOG_INFO, "<%s> %s: can't allocate enough memory",
116                     __func__, intface);
117                 exit(1);
118         }
119         memset(tmp, 0, sizeof(*tmp));
120         tmp->prefix.next = tmp->prefix.prev = &tmp->prefix;
121 #ifdef ROUTEINFO
122         tmp->route.next = tmp->route.prev = &tmp->route;
123 #endif
124
125         /* check if we are allowed to forward packets (if not determined) */
126         if (forwarding < 0) {
127                 if ((forwarding = getinet6sysctl(IPV6CTL_FORWARDING)) < 0)
128                         exit(1);
129         }
130
131         /* get interface information */
132         if (agetflag("nolladdr"))
133                 tmp->advlinkopt = 0;
134         else
135                 tmp->advlinkopt = 1;
136         if (tmp->advlinkopt) {
137                 if ((tmp->sdl = if_nametosdl(intface)) == NULL) {
138                         syslog(LOG_ERR,
139                                "<%s> can't get information of %s",
140                                __func__, intface);
141                         exit(1);
142                 }
143                 tmp->ifindex = tmp->sdl->sdl_index;
144         } else
145                 tmp->ifindex = if_nametoindex(intface);
146         strncpy(tmp->ifname, intface, sizeof(tmp->ifname));
147         if ((tmp->phymtu = if_getmtu(intface)) == 0) {
148                 tmp->phymtu = IPV6_MMTU;
149                 syslog(LOG_WARNING,
150                        "<%s> can't get interface mtu of %s. Treat as %d",
151                        __func__, intface, IPV6_MMTU);
152         }
153
154         /*
155          * set router configuration variables.
156          */
157         MAYHAVE(val, "maxinterval", DEF_MAXRTRADVINTERVAL);
158         if (val < MIN_MAXINTERVAL || val > MAX_MAXINTERVAL) {
159                 syslog(LOG_ERR,
160                        "<%s> maxinterval (%ld) on %s is invalid "
161                        "(must be between %u and %u)", __func__, val,
162                        intface, MIN_MAXINTERVAL, MAX_MAXINTERVAL);
163                 exit(1);
164         }
165         tmp->maxinterval = (u_int)val;
166         MAYHAVE(val, "mininterval", tmp->maxinterval/3);
167         if (val < MIN_MININTERVAL || val > (tmp->maxinterval * 3) / 4) {
168                 syslog(LOG_ERR,
169                        "<%s> mininterval (%ld) on %s is invalid "
170                        "(must be between %d and %d)",
171                        __func__, val, intface, MIN_MININTERVAL,
172                        (tmp->maxinterval * 3) / 4);
173                 exit(1);
174         }
175         tmp->mininterval = (u_int)val;
176
177         MAYHAVE(val, "chlim", DEF_ADVCURHOPLIMIT);
178         tmp->hoplimit = val & 0xff;
179
180         if ((flagstr = (char *)agetstr("raflags", &bp))) {
181                 val = 0;
182                 if (strchr(flagstr, 'm'))
183                         val |= ND_RA_FLAG_MANAGED;
184                 if (strchr(flagstr, 'o'))
185                         val |= ND_RA_FLAG_OTHER;
186                 if (strchr(flagstr, 'h'))
187                         val |= ND_RA_FLAG_RTPREF_HIGH;
188                 if (strchr(flagstr, 'l')) {
189                         if ((val & ND_RA_FLAG_RTPREF_HIGH)) {
190                                 syslog(LOG_ERR, "<%s> the \'h\' and \'l\'"
191                                     " router flags are exclusive", __func__);
192                                 exit(1);
193                         }
194                         val |= ND_RA_FLAG_RTPREF_LOW;
195                 }
196         } else {
197                 MAYHAVE(val, "raflags", 0);
198         }
199         tmp->managedflg = val & ND_RA_FLAG_MANAGED;
200         tmp->otherflg = val & ND_RA_FLAG_OTHER;
201 #ifndef ND_RA_FLAG_RTPREF_MASK
202 #define ND_RA_FLAG_RTPREF_MASK  0x18 /* 00011000 */
203 #define ND_RA_FLAG_RTPREF_RSV   0x10 /* 00010000 */
204 #endif
205         tmp->rtpref = val & ND_RA_FLAG_RTPREF_MASK;
206         if (tmp->rtpref == ND_RA_FLAG_RTPREF_RSV) {
207                 syslog(LOG_ERR, "<%s> invalid router preference (%02x) on %s",
208                        __func__, tmp->rtpref, intface);
209                 exit(1);
210         }
211
212         MAYHAVE(val, "rltime", tmp->maxinterval * 3);
213         if (val && (val < tmp->maxinterval || val > MAXROUTERLIFETIME)) {
214                 syslog(LOG_ERR,
215                        "<%s> router lifetime (%ld) on %s is invalid "
216                        "(must be 0 or between %d and %d)",
217                        __func__, val, intface,
218                        tmp->maxinterval,
219                        MAXROUTERLIFETIME);
220                 exit(1);
221         }
222         /*
223          * Basically, hosts MUST NOT send Router Advertisement messages at any
224          * time (RFC 2461, Section 6.2.3). However, it would sometimes be
225          * useful to allow hosts to advertise some parameters such as prefix
226          * information and link MTU. Thus, we allow hosts to invoke rtadvd
227          * only when router lifetime (on every advertising interface) is
228          * explicitly set zero. (see also the above section)
229          */
230         if (val && forwarding == 0) {
231                 syslog(LOG_ERR,
232                        "<%s> non zero router lifetime is specified for %s, "
233                        "which must not be allowed for hosts.  you must "
234                        "change router lifetime or enable IPv6 forwarding.",
235                        __func__, intface);
236                 exit(1);
237         }
238         tmp->lifetime = val & 0xffff;
239
240         MAYHAVE(val, "rtime", DEF_ADVREACHABLETIME);
241         if (val < 0 || val > MAXREACHABLETIME) {
242                 syslog(LOG_ERR,
243                        "<%s> reachable time (%ld) on %s is invalid "
244                        "(must be no greater than %d)",
245                        __func__, val, intface, MAXREACHABLETIME);
246                 exit(1);
247         }
248         tmp->reachabletime = (u_int32_t)val;
249
250         MAYHAVE(val64, "retrans", DEF_ADVRETRANSTIMER);
251         if (val64 < 0 || val64 > 0xffffffff) {
252                 syslog(LOG_ERR, "<%s> retrans time (%lld) on %s out of range",
253                        __func__, (long long)val64, intface);
254                 exit(1);
255         }
256         tmp->retranstimer = (u_int32_t)val64;
257
258         if (agetnum("hapref") != -1 || agetnum("hatime") != -1) {
259                 syslog(LOG_ERR,
260                        "<%s> mobile-ip6 configuration not supported",
261                        __func__);
262                 exit(1);
263         }
264         /* prefix information */
265
266         /*
267          * This is an implementation specific parameter to consider
268          * link propagation delays and poorly synchronized clocks when
269          * checking consistency of advertised lifetimes.
270          */
271         MAYHAVE(val, "clockskew", 0);
272         tmp->clockskew = val;
273
274         tmp->pfxs = 0;
275         for (i = -1; i < MAXPREFIX; i++) {
276                 struct prefix *pfx;
277                 char entbuf[256];
278
279                 makeentry(entbuf, sizeof(entbuf), i, "addr");
280                 addr = (char *)agetstr(entbuf, &bp);
281                 if (addr == NULL)
282                         continue;
283
284                 /* allocate memory to store prefix information */
285                 if ((pfx = malloc(sizeof(struct prefix))) == NULL) {
286                         syslog(LOG_ERR,
287                                "<%s> can't allocate enough memory",
288                                __func__);
289                         exit(1);
290                 }
291                 memset(pfx, 0, sizeof(*pfx));
292
293                 /* link into chain */
294                 insque(pfx, &tmp->prefix);
295                 tmp->pfxs++;
296                 pfx->rainfo = tmp;
297
298                 pfx->origin = PREFIX_FROM_CONFIG;
299
300                 if (inet_pton(AF_INET6, addr, &pfx->prefix) != 1) {
301                         syslog(LOG_ERR,
302                                "<%s> inet_pton failed for %s",
303                                __func__, addr);
304                         exit(1);
305                 }
306                 if (IN6_IS_ADDR_MULTICAST(&pfx->prefix)) {
307                         syslog(LOG_ERR,
308                                "<%s> multicast prefix (%s) must "
309                                "not be advertised on %s",
310                                __func__, addr, intface);
311                         exit(1);
312                 }
313                 if (IN6_IS_ADDR_LINKLOCAL(&pfx->prefix))
314                         syslog(LOG_NOTICE,
315                                "<%s> link-local prefix (%s) will be"
316                                " advertised on %s",
317                                __func__, addr, intface);
318
319                 makeentry(entbuf, sizeof(entbuf), i, "prefixlen");
320                 MAYHAVE(val, entbuf, 64);
321                 if (val < 0 || val > 128) {
322                         syslog(LOG_ERR, "<%s> prefixlen (%ld) for %s "
323                                "on %s out of range",
324                                __func__, val, addr, intface);
325                         exit(1);
326                 }
327                 pfx->prefixlen = (int)val;
328
329                 makeentry(entbuf, sizeof(entbuf), i, "pinfoflags");
330                 if ((flagstr = (char *)agetstr(entbuf, &bp))) {
331                         val = 0;
332                         if (strchr(flagstr, 'l'))
333                                 val |= ND_OPT_PI_FLAG_ONLINK;
334                         if (strchr(flagstr, 'a'))
335                                 val |= ND_OPT_PI_FLAG_AUTO;
336                 } else {
337                         MAYHAVE(val, entbuf,
338                             (ND_OPT_PI_FLAG_ONLINK|ND_OPT_PI_FLAG_AUTO));
339                 }
340                 pfx->onlinkflg = val & ND_OPT_PI_FLAG_ONLINK;
341                 pfx->autoconfflg = val & ND_OPT_PI_FLAG_AUTO;
342
343                 makeentry(entbuf, sizeof(entbuf), i, "vltime");
344                 MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME);
345                 if (val64 < 0 || val64 > 0xffffffff) {
346                         syslog(LOG_ERR, "<%s> vltime (%lld) for "
347                             "%s/%d on %s is out of range",
348                             __func__, (long long)val64,
349                             addr, pfx->prefixlen, intface);
350                         exit(1);
351                 }
352                 pfx->validlifetime = (u_int32_t)val64;
353
354                 makeentry(entbuf, sizeof(entbuf), i, "vltimedecr");
355                 if (agetflag(entbuf)) {
356                         struct timeval now;
357                         gettimeofday(&now, 0);
358                         pfx->vltimeexpire =
359                                 now.tv_sec + pfx->validlifetime;
360                 }
361
362                 makeentry(entbuf, sizeof(entbuf), i, "pltime");
363                 MAYHAVE(val64, entbuf, DEF_ADVPREFERREDLIFETIME);
364                 if (val64 < 0 || val64 > 0xffffffff) {
365                         syslog(LOG_ERR,
366                             "<%s> pltime (%lld) for %s/%d on %s "
367                             "is out of range",
368                             __func__, (long long)val64,
369                             addr, pfx->prefixlen, intface);
370                         exit(1);
371                 }
372                 pfx->preflifetime = (u_int32_t)val64;
373
374                 makeentry(entbuf, sizeof(entbuf), i, "pltimedecr");
375                 if (agetflag(entbuf)) {
376                         struct timeval now;
377                         gettimeofday(&now, 0);
378                         pfx->pltimeexpire =
379                                 now.tv_sec + pfx->preflifetime;
380                 }
381         }
382         if (tmp->pfxs == 0)
383                 get_prefix(tmp);
384
385         MAYHAVE(val, "mtu", 0);
386         if (val < 0 || val > 0xffffffff) {
387                 syslog(LOG_ERR,
388                        "<%s> mtu (%ld) on %s out of range",
389                        __func__, val, intface);
390                 exit(1);
391         }
392         tmp->linkmtu = (u_int32_t)val;
393         if (tmp->linkmtu == 0) {
394                 char *mtustr;
395
396                 if ((mtustr = (char *)agetstr("mtu", &bp)) &&
397                     strcmp(mtustr, "auto") == 0)
398                         tmp->linkmtu = tmp->phymtu;
399         }
400         else if (tmp->linkmtu < IPV6_MMTU || tmp->linkmtu > tmp->phymtu) {
401                 syslog(LOG_ERR,
402                        "<%s> advertised link mtu (%lu) on %s is invalid (must "
403                        "be between least MTU (%d) and physical link MTU (%d)",
404                        __func__, (unsigned long)tmp->linkmtu, intface,
405                        IPV6_MMTU, tmp->phymtu);
406                 exit(1);
407         }
408
409         /* route information */
410 #ifdef ROUTEINFO
411         tmp->routes = 0;
412         for (i = -1; i < MAXROUTE; i++) {
413                 struct rtinfo *rti;
414                 char entbuf[256], oentbuf[256];
415
416                 makeentry(entbuf, sizeof(entbuf), i, "rtprefix");
417                 addr = (char *)agetstr(entbuf, &bp);
418                 if (addr == NULL) {
419                         makeentry(oentbuf, sizeof(oentbuf), i, "rtrprefix");
420                         addr = (char *)agetstr(oentbuf, &bp);
421                         if (addr) {
422                                 fprintf(stderr, "%s was obsoleted.  Use %s.\n",
423                                         oentbuf, entbuf);
424                         }
425                 }
426                 if (addr == NULL)
427                         continue;
428
429                 /* allocate memory to store prefix information */
430                 if ((rti = malloc(sizeof(struct rtinfo))) == NULL) {
431                         syslog(LOG_ERR,
432                                "<%s> can't allocate enough memory",
433                                __func__);
434                         exit(1);
435                 }
436                 memset(rti, 0, sizeof(*rti));
437
438                 /* link into chain */
439                 insque(rti, &tmp->route);
440                 tmp->routes++;
441
442                 if (inet_pton(AF_INET6, addr, &rti->prefix) != 1) {
443                         syslog(LOG_ERR, "<%s> inet_pton failed for %s",
444                                __func__, addr);
445                         exit(1);
446                 }
447 #if 0
448                 /*
449                  * XXX: currently there's no restriction in route information
450                  * prefix according to
451                  * draft-ietf-ipngwg-router-selection-00.txt.
452                  * However, I think the similar restriction be necessary.
453                  */
454                 MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME);
455                 if (IN6_IS_ADDR_MULTICAST(&rti->prefix)) {
456                         syslog(LOG_ERR,
457                                "<%s> multicast route (%s) must "
458                                "not be advertised on %s",
459                                __func__, addr, intface);
460                         exit(1);
461                 }
462                 if (IN6_IS_ADDR_LINKLOCAL(&rti->prefix)) {
463                         syslog(LOG_NOTICE,
464                                "<%s> link-local route (%s) will "
465                                "be advertised on %s",
466                                __func__, addr, intface);
467                         exit(1);
468                 }
469 #endif
470
471                 makeentry(entbuf, sizeof(entbuf), i, "rtplen");
472                 /* XXX: 256 is a magic number for compatibility check. */
473                 MAYHAVE(val, entbuf, 256);
474                 if (val == 256) {
475                         makeentry(oentbuf, sizeof(oentbuf), i, "rtrplen");
476                         MAYHAVE(val, oentbuf, 256);
477                         if (val != 256) {
478                                 fprintf(stderr, "%s was obsoleted.  Use %s.\n",
479                                         oentbuf, entbuf);
480                         } else
481                                 val = 64;
482                 }
483                 if (val < 0 || val > 128) {
484                         syslog(LOG_ERR, "<%s> prefixlen (%ld) for %s on %s "
485                                "out of range",
486                                __func__, val, addr, intface);
487                         exit(1);
488                 }
489                 rti->prefixlen = (int)val;
490
491                 makeentry(entbuf, sizeof(entbuf), i, "rtflags");
492                 if ((flagstr = (char *)agetstr(entbuf, &bp))) {
493                         val = 0;
494                         if (strchr(flagstr, 'h'))
495                                 val |= ND_RA_FLAG_RTPREF_HIGH;
496                         if (strchr(flagstr, 'l')) {
497                                 if ((val & ND_RA_FLAG_RTPREF_HIGH)) {
498                                         syslog(LOG_ERR,
499                                             "<%s> the \'h\' and \'l\' route"
500                                             " preferences are exclusive",
501                                             __func__);
502                                         exit(1);
503                                 }
504                                 val |= ND_RA_FLAG_RTPREF_LOW;
505                         }
506                 } else
507                         MAYHAVE(val, entbuf, 256); /* XXX */
508                 if (val == 256) {
509                         makeentry(oentbuf, sizeof(oentbuf), i, "rtrflags");
510                         MAYHAVE(val, oentbuf, 256);
511                         if (val != 256) {
512                                 fprintf(stderr, "%s was obsoleted.  Use %s.\n",
513                                         oentbuf, entbuf);
514                         } else
515                                 val = 0;
516                 }
517                 rti->rtpref = val & ND_RA_FLAG_RTPREF_MASK;
518                 if (rti->rtpref == ND_RA_FLAG_RTPREF_RSV) {
519                         syslog(LOG_ERR, "<%s> invalid route preference (%02x) "
520                                "for %s/%d on %s",
521                                __func__, rti->rtpref, addr,
522                                rti->prefixlen, intface);
523                         exit(1);
524                 }
525
526                 /*
527                  * Since the spec does not a default value, we should make
528                  * this entry mandatory.  However, FreeBSD 4.4 has shipped
529                  * with this field being optional, we use the router lifetime
530                  * as an ad-hoc default value with a warning message.
531                  */
532                 makeentry(entbuf, sizeof(entbuf), i, "rtltime");
533                 MAYHAVE(val64, entbuf, -1);
534                 if (val64 == -1) {
535                         makeentry(oentbuf, sizeof(oentbuf), i, "rtrltime");
536                         MAYHAVE(val64, oentbuf, -1);
537                         if (val64 != -1) {
538                                 fprintf(stderr, "%s was obsoleted.  Use %s.\n",
539                                         oentbuf, entbuf);
540                         } else {
541                                 fprintf(stderr, "%s should be specified "
542                                         "for interface %s.\n",
543                                         entbuf, intface);
544                                 val64 = tmp->lifetime;
545                         }
546                 }
547                 if (val64 < 0 || val64 > 0xffffffff) {
548                         syslog(LOG_ERR, "<%s> route lifetime (%lld) for "
549                             "%s/%d on %s out of range", __func__,
550                             (long long)val64, addr, rti->prefixlen, intface);
551                         exit(1);
552                 }
553                 rti->ltime = (u_int32_t)val64;
554         }
555 #endif
556
557         /* okey */
558         tmp->next = ralist;
559         ralist = tmp;
560
561         /* construct the sending packet */
562         make_packet(tmp);
563
564         /* set timer */
565         tmp->timer = rtadvd_add_timer(ra_timeout, ra_timer_update,
566                                       tmp, tmp);
567         ra_timer_update((void *)tmp, &tmp->timer->tm);
568         rtadvd_set_timer(&tmp->timer->tm, tmp->timer);
569 }
570
571 void
572 get_prefix(struct rainfo *rai)
573 {
574         struct ifaddrs *ifap, *ifa;
575         struct prefix *pp;
576         struct in6_addr *a;
577         u_char *p, *ep, *m, *lim;
578         u_char ntopbuf[INET6_ADDRSTRLEN];
579
580         if (getifaddrs(&ifap) < 0) {
581                 syslog(LOG_ERR,
582                        "<%s> can't get interface addresses",
583                        __func__);
584                 exit(1);
585         }
586
587         for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
588                 int plen;
589
590                 if (strcmp(ifa->ifa_name, rai->ifname) != 0)
591                         continue;
592                 if (ifa->ifa_addr->sa_family != AF_INET6)
593                         continue;
594                 a = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
595                 if (IN6_IS_ADDR_LINKLOCAL(a))
596                         continue;
597                 /* get prefix length */
598                 m = (u_char *)&((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr;
599                 lim = (u_char *)(ifa->ifa_netmask) + ifa->ifa_netmask->sa_len;
600                 plen = prefixlen(m, lim);
601                 if (plen <= 0 || plen > 128) {
602                         syslog(LOG_ERR, "<%s> failed to get prefixlen "
603                                "or prefix is invalid",
604                                __func__);
605                         exit(1);
606                 }
607                 if (plen == 128)        /* XXX */
608                         continue;
609                 if (find_prefix(rai, a, plen)) {
610                         /* ignore a duplicated prefix. */
611                         continue;
612                 }
613
614                 /* allocate memory to store prefix info. */
615                 if ((pp = malloc(sizeof(*pp))) == NULL) {
616                         syslog(LOG_ERR,
617                                "<%s> can't get allocate buffer for prefix",
618                                __func__);
619                         exit(1);
620                 }
621                 memset(pp, 0, sizeof(*pp));
622
623                 /* set prefix, sweep bits outside of prefixlen */
624                 pp->prefixlen = plen;
625                 memcpy(&pp->prefix, a, sizeof(*a));
626                 p = (u_char *)&pp->prefix;
627                 ep = (u_char *)(&pp->prefix + 1);
628                 while (m < lim)
629                         *p++ &= *m++;
630                 while (p < ep)
631                         *p++ = 0x00;
632                 if (!inet_ntop(AF_INET6, &pp->prefix, ntopbuf,
633                     sizeof(ntopbuf))) {
634                         syslog(LOG_ERR, "<%s> inet_ntop failed", __func__);
635                         exit(1);
636                 }
637                 syslog(LOG_DEBUG,
638                        "<%s> add %s/%d to prefix list on %s",
639                        __func__, ntopbuf, pp->prefixlen, rai->ifname);
640
641                 /* set other fields with protocol defaults */
642                 pp->validlifetime = DEF_ADVVALIDLIFETIME;
643                 pp->preflifetime = DEF_ADVPREFERREDLIFETIME;
644                 pp->onlinkflg = 1;
645                 pp->autoconfflg = 1;
646                 pp->origin = PREFIX_FROM_KERNEL;
647                 pp->rainfo = rai;
648
649                 /* link into chain */
650                 insque(pp, &rai->prefix);
651
652                 /* counter increment */
653                 rai->pfxs++;
654         }
655
656         freeifaddrs(ifap);
657 }
658
659 static void
660 makeentry(buf, len, id, string)
661         char *buf;
662         size_t len;
663         int id;
664         char *string;
665 {
666
667         if (id < 0)
668                 strlcpy(buf, string, len);
669         else
670                 snprintf(buf, len, "%s%d", string, id);
671 }
672
673 /*
674  * Add a prefix to the list of specified interface and reconstruct
675  * the outgoing packet.
676  * The prefix must not be in the list.
677  * XXX: other parameters of the prefix (e.g. lifetime) shoule be
678  * able to be specified.
679  */
680 static void
681 add_prefix(struct rainfo *rai, struct in6_prefixreq *ipr)
682 {
683         struct prefix *prefix;
684         u_char ntopbuf[INET6_ADDRSTRLEN];
685
686         if ((prefix = malloc(sizeof(*prefix))) == NULL) {
687                 syslog(LOG_ERR, "<%s> memory allocation failed",
688                        __func__);
689                 return;         /* XXX: error or exit? */
690         }
691         memset(prefix, 0, sizeof(*prefix));
692         prefix->prefix = ipr->ipr_prefix.sin6_addr;
693         prefix->prefixlen = ipr->ipr_plen;
694         prefix->validlifetime = ipr->ipr_vltime;
695         prefix->preflifetime = ipr->ipr_pltime;
696         prefix->onlinkflg = ipr->ipr_raf_onlink;
697         prefix->autoconfflg = ipr->ipr_raf_auto;
698         prefix->origin = PREFIX_FROM_DYNAMIC;
699
700         insque(prefix, &rai->prefix);
701         prefix->rainfo = rai;
702
703         syslog(LOG_DEBUG, "<%s> new prefix %s/%d was added on %s",
704                __func__, inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr,
705                                        ntopbuf, INET6_ADDRSTRLEN),
706                ipr->ipr_plen, rai->ifname);
707
708         /* free the previous packet */
709         free(rai->ra_data);
710         rai->ra_data = NULL;
711
712         /* reconstruct the packet */
713         rai->pfxs++;
714         make_packet(rai);
715 }
716
717 /*
718  * Delete a prefix to the list of specified interface and reconstruct
719  * the outgoing packet.
720  * The prefix must be in the list.
721  */
722 void
723 delete_prefix(struct prefix *prefix)
724 {
725         u_char ntopbuf[INET6_ADDRSTRLEN];
726         struct rainfo *rai = prefix->rainfo;
727
728         remque(prefix);
729         syslog(LOG_DEBUG, "<%s> prefix %s/%d was deleted on %s",
730                __func__, inet_ntop(AF_INET6, &prefix->prefix,
731                                        ntopbuf, INET6_ADDRSTRLEN),
732                prefix->prefixlen, rai->ifname);
733         if (prefix->timer)
734                 rtadvd_remove_timer(&prefix->timer);
735         free(prefix);
736         rai->pfxs--;
737 }
738
739 void
740 invalidate_prefix(struct prefix *prefix)
741 {
742         u_char ntopbuf[INET6_ADDRSTRLEN];
743         struct timeval timo;
744         struct rainfo *rai = prefix->rainfo;
745
746         if (prefix->timer) {    /* sanity check */
747                 syslog(LOG_ERR,
748                     "<%s> assumption failure: timer already exists",
749                     __func__);
750                 exit(1);
751         }
752
753         syslog(LOG_DEBUG, "<%s> prefix %s/%d was invalidated on %s, "
754             "will expire in %ld seconds", __func__,
755             inet_ntop(AF_INET6, &prefix->prefix, ntopbuf, INET6_ADDRSTRLEN),
756             prefix->prefixlen, rai->ifname, (long)prefix_timo);
757
758         /* set the expiration timer */
759         prefix->timer = rtadvd_add_timer(prefix_timeout, NULL, prefix, NULL);
760         if (prefix->timer == NULL) {
761                 syslog(LOG_ERR, "<%s> failed to add a timer for a prefix. "
762                     "remove the prefix", __func__);
763                 delete_prefix(prefix);
764         }
765         timo.tv_sec = prefix_timo;
766         timo.tv_usec = 0;
767         rtadvd_set_timer(&timo, prefix->timer);
768 }
769
770 static struct rtadvd_timer *
771 prefix_timeout(void *arg)
772 {
773         struct prefix *prefix = (struct prefix *)arg;
774         
775         delete_prefix(prefix);
776
777         return(NULL);
778 }
779
780 void
781 update_prefix(struct prefix * prefix)
782 {
783         u_char ntopbuf[INET6_ADDRSTRLEN];
784         struct rainfo *rai = prefix->rainfo;
785
786         if (prefix->timer == NULL) { /* sanity check */
787                 syslog(LOG_ERR,
788                     "<%s> assumption failure: timer does not exist",
789                     __func__);
790                 exit(1);
791         }
792
793         syslog(LOG_DEBUG, "<%s> prefix %s/%d was re-enabled on %s",
794             __func__, inet_ntop(AF_INET6, &prefix->prefix, ntopbuf,
795             INET6_ADDRSTRLEN), prefix->prefixlen, rai->ifname);
796
797         /* stop the expiration timer */
798         rtadvd_remove_timer(&prefix->timer);
799 }
800
801 /*
802  * Try to get an in6_prefixreq contents for a prefix which matches
803  * ipr->ipr_prefix and ipr->ipr_plen and belongs to
804  * the interface whose name is ipr->ipr_name[].
805  */
806 static int
807 init_prefix(struct in6_prefixreq *ipr)
808 {
809 #if 0
810         int s;
811
812         if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
813                 syslog(LOG_ERR, "<%s> socket: %s", __func__,
814                        strerror(errno));
815                 exit(1);
816         }
817
818         if (ioctl(s, SIOCGIFPREFIX_IN6, (caddr_t)ipr) < 0) {
819                 syslog(LOG_INFO, "<%s> ioctl:SIOCGIFPREFIX %s", __func__,
820                        strerror(errno));
821
822                 ipr->ipr_vltime = DEF_ADVVALIDLIFETIME;
823                 ipr->ipr_pltime = DEF_ADVPREFERREDLIFETIME;
824                 ipr->ipr_raf_onlink = 1;
825                 ipr->ipr_raf_auto = 1;
826                 /* omit other field initialization */
827         }
828         else if (ipr->ipr_origin < PR_ORIG_RR) {
829                 u_char ntopbuf[INET6_ADDRSTRLEN];
830
831                 syslog(LOG_WARNING, "<%s> Added prefix(%s)'s origin %d is"
832                        "lower than PR_ORIG_RR(router renumbering)."
833                        "This should not happen if I am router", __func__,
834                        inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr, ntopbuf,
835                                  sizeof(ntopbuf)), ipr->ipr_origin);
836                 close(s);
837                 return 1;
838         }
839
840         close(s);
841         return 0;
842 #else
843         ipr->ipr_vltime = DEF_ADVVALIDLIFETIME;
844         ipr->ipr_pltime = DEF_ADVPREFERREDLIFETIME;
845         ipr->ipr_raf_onlink = 1;
846         ipr->ipr_raf_auto = 1;
847         return 0;
848 #endif
849 }
850
851 void
852 make_prefix(struct rainfo *rai, int ifindex, struct in6_addr *addr, int plen)
853 {
854         struct in6_prefixreq ipr;
855
856         memset(&ipr, 0, sizeof(ipr));
857         if (if_indextoname(ifindex, ipr.ipr_name) == NULL) {
858                 syslog(LOG_ERR, "<%s> Prefix added interface No.%d doesn't"
859                        "exist. This should not happen! %s", __func__,
860                        ifindex, strerror(errno));
861                 exit(1);
862         }
863         ipr.ipr_prefix.sin6_len = sizeof(ipr.ipr_prefix);
864         ipr.ipr_prefix.sin6_family = AF_INET6;
865         ipr.ipr_prefix.sin6_addr = *addr;
866         ipr.ipr_plen = plen;
867
868         if (init_prefix(&ipr))
869                 return; /* init failed by some error */
870         add_prefix(rai, &ipr);
871 }
872
873 void
874 make_packet(struct rainfo *rainfo)
875 {
876         size_t packlen, lladdroptlen = 0;
877         char *buf;
878         struct nd_router_advert *ra;
879         struct nd_opt_prefix_info *ndopt_pi;
880         struct nd_opt_mtu *ndopt_mtu;
881 #ifdef ROUTEINFO
882         struct nd_opt_route_info *ndopt_rti;
883         struct rtinfo *rti;
884 #endif
885         struct prefix *pfx;
886
887         /* calculate total length */
888         packlen = sizeof(struct nd_router_advert);
889         if (rainfo->advlinkopt) {
890                 if ((lladdroptlen = lladdropt_length(rainfo->sdl)) == 0) {
891                         syslog(LOG_INFO,
892                                "<%s> link-layer address option has"
893                                " null length on %s.  Treat as not included.",
894                                __func__, rainfo->ifname);
895                         rainfo->advlinkopt = 0;
896                 }
897                 packlen += lladdroptlen;
898         }
899         if (rainfo->pfxs)
900                 packlen += sizeof(struct nd_opt_prefix_info) * rainfo->pfxs;
901         if (rainfo->linkmtu)
902                 packlen += sizeof(struct nd_opt_mtu);
903 #ifdef ROUTEINFO
904         for (rti = rainfo->route.next; rti != &rainfo->route; rti = rti->next)
905                 packlen += sizeof(struct nd_opt_route_info) + 
906                            ((rti->prefixlen + 0x3f) >> 6) * 8;
907 #endif
908
909         /* allocate memory for the packet */
910         if ((buf = malloc(packlen)) == NULL) {
911                 syslog(LOG_ERR,
912                        "<%s> can't get enough memory for an RA packet",
913                        __func__);
914                 exit(1);
915         }
916         if (rainfo->ra_data) {
917                 /* free the previous packet */
918                 free(rainfo->ra_data);
919                 rainfo->ra_data = NULL;
920         }
921         rainfo->ra_data = buf;
922         /* XXX: what if packlen > 576? */
923         rainfo->ra_datalen = packlen;
924
925         /*
926          * construct the packet
927          */
928         ra = (struct nd_router_advert *)buf;
929         ra->nd_ra_type = ND_ROUTER_ADVERT;
930         ra->nd_ra_code = 0;
931         ra->nd_ra_cksum = 0;
932         ra->nd_ra_curhoplimit = (u_int8_t)(0xff & rainfo->hoplimit);
933         ra->nd_ra_flags_reserved = 0; /* just in case */
934         /*
935          * XXX: the router preference field, which is a 2-bit field, should be
936          * initialized before other fields.
937          */
938         ra->nd_ra_flags_reserved = 0xff & rainfo->rtpref;
939         ra->nd_ra_flags_reserved |=
940                 rainfo->managedflg ? ND_RA_FLAG_MANAGED : 0;
941         ra->nd_ra_flags_reserved |=
942                 rainfo->otherflg ? ND_RA_FLAG_OTHER : 0;
943         ra->nd_ra_router_lifetime = htons(rainfo->lifetime);
944         ra->nd_ra_reachable = htonl(rainfo->reachabletime);
945         ra->nd_ra_retransmit = htonl(rainfo->retranstimer);
946         buf += sizeof(*ra);
947
948         if (rainfo->advlinkopt) {
949                 lladdropt_fill(rainfo->sdl, (struct nd_opt_hdr *)buf);
950                 buf += lladdroptlen;
951         }
952
953         if (rainfo->linkmtu) {
954                 ndopt_mtu = (struct nd_opt_mtu *)buf;
955                 ndopt_mtu->nd_opt_mtu_type = ND_OPT_MTU;
956                 ndopt_mtu->nd_opt_mtu_len = 1;
957                 ndopt_mtu->nd_opt_mtu_reserved = 0;
958                 ndopt_mtu->nd_opt_mtu_mtu = htonl(rainfo->linkmtu);
959                 buf += sizeof(struct nd_opt_mtu);
960         }
961
962         for (pfx = rainfo->prefix.next;
963              pfx != &rainfo->prefix; pfx = pfx->next) {
964                 u_int32_t vltime, pltime;
965                 struct timeval now;
966
967                 ndopt_pi = (struct nd_opt_prefix_info *)buf;
968                 ndopt_pi->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION;
969                 ndopt_pi->nd_opt_pi_len = 4;
970                 ndopt_pi->nd_opt_pi_prefix_len = pfx->prefixlen;
971                 ndopt_pi->nd_opt_pi_flags_reserved = 0;
972                 if (pfx->onlinkflg)
973                         ndopt_pi->nd_opt_pi_flags_reserved |=
974                                 ND_OPT_PI_FLAG_ONLINK;
975                 if (pfx->autoconfflg)
976                         ndopt_pi->nd_opt_pi_flags_reserved |=
977                                 ND_OPT_PI_FLAG_AUTO;
978                 if (pfx->timer)
979                         vltime = 0;
980                 else {
981                         if (pfx->vltimeexpire || pfx->pltimeexpire)
982                                 gettimeofday(&now, NULL);
983                         if (pfx->vltimeexpire == 0)
984                                 vltime = pfx->validlifetime;
985                         else
986                                 vltime = (pfx->vltimeexpire > now.tv_sec) ?
987                                     pfx->vltimeexpire - now.tv_sec : 0;
988                 }
989                 if (pfx->timer)
990                         pltime = 0;
991                 else {
992                         if (pfx->pltimeexpire == 0)
993                                 pltime = pfx->preflifetime;
994                         else
995                                 pltime = (pfx->pltimeexpire > now.tv_sec) ? 
996                                     pfx->pltimeexpire - now.tv_sec : 0;
997                 }
998                 if (vltime < pltime) {
999                         /*
1000                          * this can happen if vltime is decrement but pltime
1001                          * is not.
1002                          */
1003                         pltime = vltime;
1004                 }
1005                 ndopt_pi->nd_opt_pi_valid_time = htonl(vltime);
1006                 ndopt_pi->nd_opt_pi_preferred_time = htonl(pltime);
1007                 ndopt_pi->nd_opt_pi_reserved2 = 0;
1008                 ndopt_pi->nd_opt_pi_prefix = pfx->prefix;
1009
1010                 buf += sizeof(struct nd_opt_prefix_info);
1011         }
1012
1013 #ifdef ROUTEINFO
1014         for (rti = rainfo->route.next; rti != &rainfo->route; rti = rti->next) {
1015                 u_int8_t psize = (rti->prefixlen + 0x3f) >> 6;
1016
1017                 ndopt_rti = (struct nd_opt_route_info *)buf;
1018                 ndopt_rti->nd_opt_rti_type = ND_OPT_ROUTE_INFO;
1019                 ndopt_rti->nd_opt_rti_len = 1 + psize;
1020                 ndopt_rti->nd_opt_rti_prefixlen = rti->prefixlen;
1021                 ndopt_rti->nd_opt_rti_flags = 0xff & rti->rtpref;
1022                 ndopt_rti->nd_opt_rti_lifetime = htonl(rti->ltime);
1023                 memcpy(ndopt_rti + 1, &rti->prefix, psize * 8);
1024                 buf += sizeof(struct nd_opt_route_info) + psize * 8;
1025         }
1026 #endif
1027
1028         return;
1029 }
1030
1031 static int
1032 getinet6sysctl(int code)
1033 {
1034         int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, 0 };
1035         int value;
1036         size_t size;
1037
1038         mib[3] = code;
1039         size = sizeof(value);
1040         if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), &value, &size, NULL, 0)
1041             < 0) {
1042                 syslog(LOG_ERR, "<%s>: failed to get ip6 sysctl(%d): %s",
1043                        __func__, code,
1044                        strerror(errno));
1045                 return(-1);
1046         }
1047         else
1048                 return(value);
1049 }