[BACK]Return to rtadvd.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / usr.sbin / rtadvd

Annotation of src/usr.sbin/rtadvd/rtadvd.c, Revision 1.68

1.68    ! christos    1: /*     $NetBSD: rtadvd.c,v 1.67 2019/01/11 20:41:53 christos Exp $     */
1.30      rpaulo      2: /*     $KAME: rtadvd.c,v 1.92 2005/10/17 14:40:02 suz Exp $    */
1.2       itojun      3:
1.1       itojun      4: /*
                      5:  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
                      6:  * All rights reserved.
1.61      roy         7:  *
1.1       itojun      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.
1.61      roy        19:  *
1.1       itojun     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/socket.h>
                     35: #include <sys/uio.h>
                     36: #include <sys/time.h>
1.11      itojun     37: #include <sys/queue.h>
1.1       itojun     38:
                     39: #include <net/if.h>
                     40: #include <net/route.h>
                     41: #include <net/if_dl.h>
                     42: #include <netinet/in.h>
                     43: #include <netinet/ip6.h>
                     44: #include <netinet6/ip6_var.h>
                     45: #include <netinet/icmp6.h>
                     46:
                     47: #include <arpa/inet.h>
                     48:
                     49: #include <time.h>
                     50: #include <unistd.h>
                     51: #include <stdio.h>
                     52: #include <err.h>
                     53: #include <errno.h>
                     54: #include <string.h>
                     55: #include <stdlib.h>
                     56: #include <syslog.h>
1.57      christos   57: #include <stdarg.h>
1.39      roy        58: #ifdef __NetBSD__
1.17      itojun     59: #include <util.h>
1.39      roy        60: #endif
1.25      itojun     61: #include <poll.h>
1.44      roy        62: #include <pwd.h>
1.17      itojun     63:
1.1       itojun     64: #include "rtadvd.h"
                     65: #include "rrenum.h"
                     66: #include "advcap.h"
                     67: #include "timer.h"
                     68: #include "if.h"
                     69: #include "config.h"
1.9       itojun     70: #include "dump.h"
1.57      christos   71: #include "logit.h"
1.51      ozaki-r    72: #include "prog_ops.h"
1.67      christos   73: #include "expandm.h"
1.1       itojun     74:
                     75: struct msghdr rcvmhdr;
1.36      roy        76: static unsigned char *rcvcmsgbuf;
1.6       itojun     77: static size_t rcvcmsgbuflen;
1.39      roy        78: static unsigned char *sndcmsgbuf;
1.6       itojun     79: static size_t sndcmsgbuflen;
1.18      itojun     80: volatile sig_atomic_t do_dump;
1.39      roy        81: volatile sig_atomic_t do_reconf;
1.18      itojun     82: volatile sig_atomic_t do_die;
1.1       itojun     83: struct msghdr sndmhdr;
                     84: struct iovec rcviov[2];
                     85: struct iovec sndiov[2];
1.30      rpaulo     86: struct sockaddr_in6 rcvfrom;
1.36      roy        87: static const char *dumpfilename = "/var/run/rtadvd.dump"; /* XXX configurable */
1.11      itojun     88: static char *mcastif;
1.12      itojun     89: int sock;
                     90: int rtsock = -1;
1.5       itojun     91: int accept_rr = 0;
1.57      christos   92: int dflag = 0, sflag = 0, Dflag;
1.1       itojun     93:
1.39      roy        94: static char **if_argv;
                     95: static int if_argc;
                     96:
1.31      mrg        97: char *conffile = NULL;
1.1       itojun     98:
1.36      roy        99: struct ralist_head_t ralist = TAILQ_HEAD_INITIALIZER(ralist);
                    100:
1.1       itojun    101: struct nd_optlist {
1.36      roy       102:        TAILQ_ENTRY(nd_optlist) next;
1.1       itojun    103:        struct nd_opt_hdr *opt;
                    104: };
                    105: union nd_opts {
1.18      itojun    106:        struct nd_opt_hdr *nd_opt_array[9];
1.1       itojun    107:        struct {
                    108:                struct nd_opt_hdr *zero;
                    109:                struct nd_opt_hdr *src_lladdr;
                    110:                struct nd_opt_hdr *tgt_lladdr;
                    111:                struct nd_opt_prefix_info *pi;
                    112:                struct nd_opt_rd_hdr *rh;
                    113:                struct nd_opt_mtu *mtu;
1.36      roy       114:                TAILQ_HEAD(, nd_optlist) list;
1.1       itojun    115:        } nd_opt_each;
                    116: };
                    117: #define nd_opts_src_lladdr     nd_opt_each.src_lladdr
                    118: #define nd_opts_tgt_lladdr     nd_opt_each.tgt_lladdr
                    119: #define nd_opts_pi             nd_opt_each.pi
                    120: #define nd_opts_rh             nd_opt_each.rh
                    121: #define nd_opts_mtu            nd_opt_each.mtu
                    122: #define nd_opts_list           nd_opt_each.list
                    123:
1.36      roy       124: #define NDOPT_FLAG_SRCLINKADDR (1 << 0)
                    125: #define NDOPT_FLAG_TGTLINKADDR (1 << 1)
                    126: #define NDOPT_FLAG_PREFIXINFO  (1 << 2)
                    127: #define NDOPT_FLAG_RDHDR       (1 << 3)
                    128: #define NDOPT_FLAG_MTU         (1 << 4)
                    129: #define NDOPT_FLAG_RDNSS       (1 << 5)
                    130: #define NDOPT_FLAG_DNSSL       (1 << 6)
                    131:
                    132: uint32_t ndopt_flags[] = {
                    133:        [ND_OPT_SOURCE_LINKADDR] =      NDOPT_FLAG_SRCLINKADDR,
                    134:        [ND_OPT_TARGET_LINKADDR] =      NDOPT_FLAG_TGTLINKADDR,
                    135:        [ND_OPT_PREFIX_INFORMATION] =   NDOPT_FLAG_PREFIXINFO,
                    136:        [ND_OPT_REDIRECTED_HEADER] =    NDOPT_FLAG_RDHDR,
                    137:        [ND_OPT_MTU] =                  NDOPT_FLAG_MTU,
                    138:        [ND_OPT_RDNSS] =                NDOPT_FLAG_RDNSS,
                    139:        [ND_OPT_DNSSL] =                NDOPT_FLAG_DNSSL,
                    140: };
                    141:
                    142: struct sockaddr_in6 sin6_linklocal_allnodes = {
                    143:        .sin6_len =     sizeof(sin6_linklocal_allnodes),
                    144:        .sin6_family =  AF_INET6,
                    145:        .sin6_addr =    IN6ADDR_LINKLOCAL_ALLNODES_INIT,
                    146: };
1.38      christos  147: #ifdef notdef
1.36      roy       148: struct sockaddr_in6 sin6_linklocal_allrouters = {
                    149:        .sin6_len =     sizeof(sin6_linklocal_allrouters),
                    150:        .sin6_family =  AF_INET6,
                    151:        .sin6_addr =    IN6ADDR_LINKLOCAL_ALLROUTERS_INIT,
                    152: };
1.38      christos  153: #endif
1.36      roy       154: struct sockaddr_in6 sin6_sitelocal_allrouters = {
                    155:        .sin6_len =     sizeof(sin6_sitelocal_allrouters),
                    156:        .sin6_family =  AF_INET6,
                    157:        .sin6_addr =    IN6ADDR_SITELOCAL_ALLROUTERS_INIT,
1.1       itojun    158: };
                    159:
1.36      roy       160: static void set_die(int);
1.41      roy       161: static void die(void);
1.39      roy       162: static void set_reconf(int);
1.36      roy       163: static void sock_open(void);
                    164: static void rtsock_open(void);
                    165: static void rtadvd_input(void);
                    166: static void rs_input(int, struct nd_router_solicit *,
                    167:     struct in6_pktinfo *, struct sockaddr_in6 *);
                    168: static void ra_input(int, struct nd_router_advert *,
                    169:     struct in6_pktinfo *, struct sockaddr_in6 *);
1.65      roy       170: static struct rainfo *ra_output(struct rainfo *, bool);
1.36      roy       171: static int prefix_check(struct nd_opt_prefix_info *, struct rainfo *,
                    172:     struct sockaddr_in6 *);
                    173: static int nd6_options(struct nd_opt_hdr *, int, union nd_opts *, uint32_t);
                    174: static void free_ndopts(union nd_opts *);
                    175: static void rtmsg_input(void);
                    176: static void rtadvd_set_dump_file(int);
1.1       itojun    177:
                    178: int
1.36      roy       179: main(int argc, char *argv[])
1.1       itojun    180: {
1.23      mycroft   181:        struct pollfd set[2];
1.47      roy       182:        struct timespec *timeout;
1.1       itojun    183:        int i, ch;
1.14      itojun    184:        int fflag = 0, logopt;
1.44      roy       185:        struct passwd *pw;
1.52      ozaki-r   186:        const char *pidfilepath = NULL;
1.63      roy       187:        pid_t pid;
1.1       itojun    188:
                    189:        /* get command line options and arguments */
1.52      ozaki-r   190: #define OPTIONS "c:dDfM:p:Rs"
1.7       itojun    191:        while ((ch = getopt(argc, argv, OPTIONS)) != -1) {
                    192: #undef OPTIONS
1.14      itojun    193:                switch (ch) {
                    194:                case 'c':
                    195:                        conffile = optarg;
                    196:                        break;
                    197:                case 'd':
1.57      christos  198:                        dflag++;
1.14      itojun    199:                        break;
                    200:                case 'D':
1.57      christos  201:                        Dflag++;
1.14      itojun    202:                        break;
                    203:                case 'f':
                    204:                        fflag = 1;
                    205:                        break;
1.11      itojun    206:                case 'M':
                    207:                        mcastif = optarg;
                    208:                        break;
1.52      ozaki-r   209:                case 'p':
                    210:                        pidfilepath = optarg;
                    211:                        break;
1.14      itojun    212:                case 'R':
                    213:                        fprintf(stderr, "rtadvd: "
                    214:                                "the -R option is currently ignored.\n");
                    215:                        /* accept_rr = 1; */
                    216:                        /* run anyway... */
                    217:                        break;
                    218:                case 's':
                    219:                        sflag = 1;
                    220:                        break;
1.1       itojun    221:                }
                    222:        }
                    223:        argc -= optind;
                    224:        argv += optind;
                    225:        if (argc == 0) {
1.54      christos  226:                fprintf(stderr, "Ysage: %s [-DdfRs] [-c conffile]"
                    227:                    " [-M ifname] [-p pidfile] interface ...\n", getprogname());
                    228:                return EXIT_FAILURE;
1.1       itojun    229:        }
                    230:
1.63      roy       231:        if ((pid = pidfile_lock(pidfilepath)) != 0) {
                    232:                if (pid == -1)
                    233:                        logit(LOG_ERR, "pidfile_lock: %m");
                    234:                        /* Continue */
                    235:                else {
                    236:                        logit(LOG_ERR, "Another instance of `%s' is running "
                    237:                            "(pid %d); exiting.", getprogname(), pid);
                    238:                        return EXIT_FAILURE;
                    239:                }
                    240:        }
                    241:
                    242:        if (prog_init && prog_init() == -1)
1.54      christos  243:                err(EXIT_FAILURE, "init failed");
1.51      ozaki-r   244:
1.14      itojun    245:        logopt = LOG_NDELAY | LOG_PID;
                    246:        if (fflag)
                    247:                logopt |= LOG_PERROR;
                    248:        openlog("rtadvd", logopt, LOG_DAEMON);
                    249:
1.1       itojun    250:        /* set log level */
                    251:        if (dflag == 0)
                    252:                (void)setlogmask(LOG_UPTO(LOG_ERR));
                    253:        if (dflag == 1)
                    254:                (void)setlogmask(LOG_UPTO(LOG_INFO));
                    255:
1.44      roy       256:        errno = 0; /* Ensure errno is 0 so we know if getpwnam errors or not */
                    257:        if ((pw = getpwnam(RTADVD_USER)) == NULL) {
                    258:                if (errno == 0)
1.57      christos  259:                        logit(LOG_ERR,
1.44      roy       260:                            "user %s does not exist, aborting",
                    261:                            RTADVD_USER);
                    262:                else
1.57      christos  263:                        logit(LOG_ERR, "getpwnam: %s: %m", RTADVD_USER);
1.54      christos  264:                return EXIT_FAILURE;
1.44      roy       265:        }
                    266:
1.1       itojun    267:        /* timer initialization */
                    268:        rtadvd_timer_init();
                    269:
1.39      roy       270:        if_argc = argc;
                    271:        if_argv = argv;
1.1       itojun    272:        while (argc--)
1.39      roy       273:                getconfig(*argv++, 1);
1.1       itojun    274:
1.63      roy       275:        if (!fflag) {
1.51      ozaki-r   276:                prog_daemon(1, 0);
1.63      roy       277:                if (pidfile_lock(pidfilepath) != 0)
                    278:                        logit(LOG_ERR, " pidfile_lock: %m");
                    279:        }
1.13      itojun    280:
                    281:        sock_open();
1.1       itojun    282:
1.23      mycroft   283:        set[0].fd = sock;
                    284:        set[0].events = POLLIN;
1.12      itojun    285:        if (sflag == 0) {
                    286:                rtsock_open();
1.23      mycroft   287:                set[1].fd = rtsock;
                    288:                set[1].events = POLLIN;
1.12      itojun    289:        } else
1.24      mycroft   290:                set[1].fd = -1;
1.19      itojun    291:
1.57      christos  292:        logit(LOG_INFO, "dropping privileges to %s", RTADVD_USER);
1.51      ozaki-r   293:        if (prog_chroot(pw->pw_dir) == -1) {
1.57      christos  294:                logit(LOG_ERR, "chroot: %s: %m", pw->pw_dir);
1.54      christos  295:                return EXIT_FAILURE;
1.44      roy       296:        }
1.51      ozaki-r   297:        if (prog_chdir("/") == -1) {
1.57      christos  298:                logit(LOG_ERR, "chdir: /: %m");
1.54      christos  299:                return EXIT_FAILURE;
1.44      roy       300:        }
1.51      ozaki-r   301:        if (prog_setgroups(1, &pw->pw_gid) == -1 ||
                    302:            prog_setgid(pw->pw_gid) == -1 ||
                    303:            prog_setuid(pw->pw_uid) == -1)
1.44      roy       304:        {
1.57      christos  305:                logit(LOG_ERR, "failed to drop privileges: %m");
1.54      christos  306:                return EXIT_FAILURE;
1.44      roy       307:        }
                    308:
1.41      roy       309:        signal(SIGINT, set_die);
1.22      itojun    310:        signal(SIGTERM, set_die);
1.39      roy       311:        signal(SIGHUP, set_reconf);
1.22      itojun    312:        signal(SIGUSR1, rtadvd_set_dump_file);
1.5       itojun    313:
1.23      mycroft   314:        for (;;) {
1.9       itojun    315:                if (do_dump) {  /* SIGUSR1 */
                    316:                        do_dump = 0;
                    317:                        rtadvd_dump_file(dumpfilename);
                    318:                }
                    319:
1.39      roy       320:                if (do_reconf) { /* SIGHUP */
                    321:                        do_reconf = 0;
1.57      christos  322:                        logit(LOG_INFO, "%s: reloading config on SIGHUP",
1.39      roy       323:                               __func__);
                    324:                        argc = if_argc;
                    325:                        argv = if_argv;
                    326:                        while (argc--)
                    327:                                getconfig(*argv++, 0);
                    328:                }
                    329:
1.47      roy       330:                /* timer expiration check and reset the timer */
                    331:                timeout = rtadvd_check_timer();
                    332:
1.11      itojun    333:                if (do_die) {
                    334:                        die();
                    335:                        /*NOTREACHED*/
                    336:                }
                    337:
1.10      itojun    338:                if (timeout != NULL) {
1.57      christos  339:                        logit(LOG_DEBUG,
1.56      christos  340:                            "%s: set timer to %jd:%jd. waiting for "
1.21      itojun    341:                            "inputs or timeout", __func__,
1.54      christos  342:                            (intmax_t)timeout->tv_sec,
                    343:                            (intmax_t)timeout->tv_nsec);
1.10      itojun    344:                } else {
1.57      christos  345:                        logit(LOG_DEBUG,
1.56      christos  346:                            "%s: there's no timer. waiting for inputs",
1.21      itojun    347:                            __func__);
1.10      itojun    348:                }
1.1       itojun    349:
1.51      ozaki-r   350:                if ((i = prog_poll(set, 2, timeout ? (timeout->tv_sec * 1000 +
1.56      christos  351:                    (timeout->tv_nsec + 999999) / 1000000) : INFTIM)) == -1)
1.47      roy       352:                {
1.9       itojun    353:                        /* EINTR would occur upon SIGUSR1 for status dump */
1.57      christos  354:                        if (errno == EINTR)
                    355:                                continue;
                    356:
                    357:                        logit(LOG_ERR, "%s: poll: %m", __func__);
                    358:                        if (Dflag)
                    359:                                exit(1);
1.1       itojun    360:                }
                    361:                if (i == 0)     /* timeout */
                    362:                        continue;
1.23      mycroft   363:                if (rtsock != -1 && set[1].revents & POLLIN)
1.1       itojun    364:                        rtmsg_input();
1.23      mycroft   365:                if (set[0].revents & POLLIN)
1.1       itojun    366:                        rtadvd_input();
                    367:        }
1.54      christos  368:        return EXIT_SUCCESS;    /* NOTREACHED */
1.1       itojun    369: }
                    370:
                    371: static void
1.39      roy       372: rtadvd_set_dump_file(__unused int sig)
1.9       itojun    373: {
1.39      roy       374:
1.9       itojun    375:        do_dump = 1;
                    376: }
                    377:
                    378: static void
1.39      roy       379: set_reconf(__unused int sig)
1.5       itojun    380: {
1.39      roy       381:
                    382:        do_reconf = 1;
                    383: }
                    384:
                    385: static void
                    386: set_die(__unused int sig)
                    387: {
                    388:
1.11      itojun    389:        do_die = 1;
                    390: }
                    391:
                    392: static void
1.36      roy       393: die(void)
1.11      itojun    394: {
1.41      roy       395:        static int waiting;
                    396:        struct rainfo *rai, *ran;
1.39      roy       397:        struct rdnss *rdnss;
                    398:        struct dnssl *dnssl;
1.5       itojun    399:
1.41      roy       400:        if (waiting) {
                    401:                if (TAILQ_FIRST(&ralist)) {
1.57      christos  402:                        logit(LOG_INFO,
1.56      christos  403:                               "%s: waiting for expiration of all RA timers",
1.41      roy       404:                               __func__);
                    405:                        return;
                    406:                }
1.57      christos  407:                logit(LOG_NOTICE, "%s: gracefully terminated", __func__);
1.41      roy       408:                free(rcvcmsgbuf);
                    409:                free(sndcmsgbuf);
1.54      christos  410:                exit(EXIT_SUCCESS);
1.41      roy       411:                /* NOT REACHED */
                    412:        }
                    413:
1.43      roy       414:        if (TAILQ_FIRST(&ralist) == NULL) {
1.57      christos  415:                logit(LOG_NOTICE, "%s: gracefully terminated", __func__);
1.54      christos  416:                exit(EXIT_SUCCESS);
1.43      roy       417:                /* NOT REACHED */
                    418:        }
                    419:
1.41      roy       420:        waiting = 1;
1.57      christos  421:        logit(LOG_NOTICE, "%s: final RA transmission started", __func__);
1.41      roy       422:
                    423:        TAILQ_FOREACH_SAFE(rai, &ralist, next, ran) {
                    424:                if (rai->leaving) {
                    425:                        TAILQ_REMOVE(&ralist, rai, next);
                    426:                        TAILQ_INSERT_HEAD(&ralist, rai->leaving, next);
                    427:                        rai->leaving->leaving = rai->leaving;
                    428:                        rai->leaving->leaving_for = rai->leaving;
                    429:                        free_rainfo(rai);
                    430:                        continue;
                    431:                }
1.39      roy       432:                rai->lifetime = 0;
                    433:                TAILQ_FOREACH(rdnss, &rai->rdnss, next)
                    434:                        rdnss->lifetime = 0;
                    435:                TAILQ_FOREACH(dnssl, &rai->dnssl, next)
                    436:                        dnssl->lifetime = 0;
                    437:                make_packet(rai);
1.41      roy       438:                rai->leaving = rai;
                    439:                rai->leaving_for = rai;
                    440:                rai->initcounter = MAX_INITIAL_RTR_ADVERTISEMENTS;
                    441:                rai->mininterval = MIN_DELAY_BETWEEN_RAS;
                    442:                rai->maxinterval = MIN_DELAY_BETWEEN_RAS;
                    443:                rai->leaving_adv = MAX_FINAL_RTR_ADVERTISEMENTS;
1.65      roy       444:                ra_output(rai, false);
1.54      christos  445:                ra_timer_update(rai, &rai->timer->tm);
1.41      roy       446:                rtadvd_set_timer(&rai->timer->tm, rai->timer);
1.5       itojun    447:        }
                    448: }
                    449:
                    450: static void
1.36      roy       451: rtmsg_input(void)
1.1       itojun    452: {
1.9       itojun    453:        int n, type, ifindex = 0, plen;
1.2       itojun    454:        size_t len;
1.32      tron      455:        union rt_msghdr_buf {
                    456:                struct rt_msghdr        rt_msghdr;
                    457:                char                    data[2048];
                    458:        } buffer;
1.39      roy       459:        char *msg, *next, *lim, **argv;
1.31      mrg       460:        char ifname[IF_NAMESIZE];
1.1       itojun    461:        struct prefix *prefix;
                    462:        struct rainfo *rai;
                    463:        struct in6_addr *addr;
                    464:        char addrbuf[INET6_ADDRSTRLEN];
1.39      roy       465:        int prefixchange = 0, argc;
1.1       itojun    466:
1.39      roy       467:        memset(&buffer, 0, sizeof(buffer));
1.51      ozaki-r   468:        n = prog_read(rtsock, &buffer, sizeof(buffer));
1.41      roy       469:
                    470:        /* We read the buffer first to clear the FD */
                    471:        if (do_die)
                    472:                return;
                    473:
1.32      tron      474:        msg = buffer.data;
1.1       itojun    475:        if (dflag > 1) {
1.57      christos  476:                logit(LOG_DEBUG, "%s: received a routing message "
1.39      roy       477:                    "(type = %d, len = %d)", __func__, rtmsg_type(msg),
                    478:                    rtmsg_len(msg));
1.1       itojun    479:        }
                    480:        if (n > rtmsg_len(msg)) {
                    481:                /*
                    482:                 * This usually won't happen for messages received on
1.10      itojun    483:                 * a routing socket.
1.1       itojun    484:                 */
                    485:                if (dflag > 1)
1.57      christos  486:                        logit(LOG_DEBUG,
1.56      christos  487:                            "%s: received data length is larger than "
1.14      itojun    488:                            "1st routing message len. multiple messages? "
                    489:                            "read %d bytes, but 1st msg len = %d",
1.21      itojun    490:                            __func__, n, rtmsg_len(msg));
1.1       itojun    491: #if 0
                    492:                /* adjust length */
                    493:                n = rtmsg_len(msg);
                    494: #endif
                    495:        }
                    496:
                    497:        lim = msg + n;
                    498:        for (next = msg; next < lim; next += len) {
1.10      itojun    499:                int oldifflags;
                    500:
1.1       itojun    501:                next = get_next_msg(next, lim, 0, &len,
                    502:                                    RTADV_TYPE2BITMASK(RTM_ADD) |
                    503:                                    RTADV_TYPE2BITMASK(RTM_DELETE) |
                    504:                                    RTADV_TYPE2BITMASK(RTM_NEWADDR) |
                    505:                                    RTADV_TYPE2BITMASK(RTM_DELADDR) |
1.39      roy       506: #ifdef RTM_IFANNOUNCE
                    507:                                    RTADV_TYPE2BITMASK(RTM_IFANNOUNCE) |
                    508: #endif
1.1       itojun    509:                                    RTADV_TYPE2BITMASK(RTM_IFINFO));
                    510:                if (len == 0)
                    511:                        break;
                    512:                type = rtmsg_type(next);
                    513:                switch (type) {
                    514:                case RTM_ADD:
                    515:                case RTM_DELETE:
                    516:                        ifindex = get_rtm_ifindex(next);
                    517:                        break;
                    518:                case RTM_NEWADDR:
                    519:                case RTM_DELADDR:
                    520:                        ifindex = get_ifam_ifindex(next);
                    521:                        break;
1.39      roy       522: #ifdef RTM_IFANNOUNCE
                    523:                case RTM_IFANNOUNCE:
                    524:                        ifindex = get_ifan_ifindex(next);
                    525:                        if (get_ifan_what(next) == IFAN_ARRIVAL) {
1.57      christos  526:                                logit(LOG_DEBUG,
1.61      roy       527:                                       "%s: interface %s arrived",
1.39      roy       528:                                       __func__,
                    529:                                       if_indextoname(ifindex, ifname));
                    530:                                if (if_argc == 0) {
                    531:                                        getconfig(ifname, 0);
                    532:                                        continue;
                    533:                                }
                    534:                                argc = if_argc;
                    535:                                argv = if_argv;
                    536:                                while (argc--) {
                    537:                                        if (strcmp(ifname, *argv++) == 0) {
                    538:                                                getconfig(ifname, 0);
                    539:                                                break;
                    540:                                        }
                    541:                                }
                    542:                                continue;
                    543:                        }
                    544:                        break;
                    545: #endif
1.1       itojun    546:                case RTM_IFINFO:
                    547:                        ifindex = get_ifm_ifindex(next);
                    548:                        break;
                    549:                default:
                    550:                        /* should not reach here */
                    551:                        if (dflag > 1) {
1.57      christos  552:                                logit(LOG_DEBUG, "%s: unknown rtmsg %d on %s",
1.56      christos  553:                                       __func__, type,
1.1       itojun    554:                                       if_indextoname(ifindex, ifname));
                    555:                        }
1.10      itojun    556:                        continue;
1.1       itojun    557:                }
                    558:
                    559:                if ((rai = if_indextorainfo(ifindex)) == NULL) {
                    560:                        if (dflag > 1) {
1.57      christos  561:                                logit(LOG_DEBUG,
1.56      christos  562:                                       "%s: route changed on "
1.39      roy       563:                                       "non advertising interface %s (%d)",
1.21      itojun    564:                                       __func__,
1.39      roy       565:                                       if_indextoname(ifindex, ifname),
                    566:                                       ifindex);
1.1       itojun    567:                        }
1.10      itojun    568:                        continue;
1.1       itojun    569:                }
1.39      roy       570:                oldifflags = rai->ifflags;
1.1       itojun    571:
1.14      itojun    572:                switch (type) {
                    573:                case RTM_ADD:
                    574:                        /* init ifflags because it may have changed */
1.39      roy       575:                        rai->ifflags = if_getflags(ifindex, rai->ifflags);
1.14      itojun    576:
                    577:                        if (sflag)
                    578:                                break;  /* we aren't interested in prefixes  */
                    579:
                    580:                        addr = get_addr(msg);
                    581:                        plen = get_prefixlen(msg);
                    582:                        /* sanity check for plen */
                    583:                        /* as RFC2373, prefixlen is at least 4 */
                    584:                        if (plen < 4 || plen > 127) {
1.57      christos  585:                                logit(LOG_INFO, "%s: new interface route's"
1.14      itojun    586:                                    "plen %d is invalid for a prefix",
1.21      itojun    587:                                    __func__, plen);
1.14      itojun    588:                                break;
                    589:                        }
                    590:                        prefix = find_prefix(rai, addr, plen);
                    591:                        if (prefix) {
1.30      rpaulo    592:                                if (prefix->timer) {
                    593:                                        /*
                    594:                                         * If the prefix has been invalidated,
                    595:                                         * make it available again.
                    596:                                         */
                    597:                                        update_prefix(prefix);
                    598:                                        prefixchange = 1;
                    599:                                } else if (dflag > 1) {
1.57      christos  600:                                        logit(LOG_DEBUG,
1.56      christos  601:                                            "%s: new prefix(%s/%d) "
1.14      itojun    602:                                            "added on %s, "
                    603:                                            "but it was already in list",
1.21      itojun    604:                                            __func__,
1.14      itojun    605:                                            inet_ntop(AF_INET6, addr,
                    606:                                            (char *)addrbuf, INET6_ADDRSTRLEN),
                    607:                                            plen, rai->ifname);
                    608:                                }
1.10      itojun    609:                                break;
1.14      itojun    610:                        }
                    611:                        make_prefix(rai, ifindex, addr, plen);
1.30      rpaulo    612:                        prefixchange = 1;
1.14      itojun    613:                        break;
                    614:                case RTM_DELETE:
                    615:                        /* init ifflags because it may have changed */
1.39      roy       616:                        rai->ifflags = if_getflags(ifindex, rai->ifflags);
1.14      itojun    617:
                    618:                        if (sflag)
1.10      itojun    619:                                break;
1.14      itojun    620:
                    621:                        addr = get_addr(msg);
                    622:                        plen = get_prefixlen(msg);
                    623:                        /* sanity check for plen */
                    624:                        /* as RFC2373, prefixlen is at least 4 */
                    625:                        if (plen < 4 || plen > 127) {
1.57      christos  626:                                logit(LOG_INFO,
1.56      christos  627:                                    "%s: deleted interface route's "
1.14      itojun    628:                                    "plen %d is invalid for a prefix",
1.21      itojun    629:                                    __func__, plen);
1.14      itojun    630:                                break;
                    631:                        }
                    632:                        prefix = find_prefix(rai, addr, plen);
                    633:                        if (prefix == NULL) {
                    634:                                if (dflag > 1) {
1.57      christos  635:                                        logit(LOG_DEBUG,
1.56      christos  636:                                            "%s: prefix(%s/%d) was "
1.14      itojun    637:                                            "deleted on %s, "
                    638:                                            "but it was not in list",
1.21      itojun    639:                                            __func__,
1.14      itojun    640:                                            inet_ntop(AF_INET6, addr,
                    641:                                            (char *)addrbuf, INET6_ADDRSTRLEN),
                    642:                                            plen, rai->ifname);
                    643:                                }
                    644:                                break;
                    645:                        }
1.30      rpaulo    646:                        invalidate_prefix(prefix);
                    647:                        prefixchange = 1;
1.14      itojun    648:                        break;
1.1       itojun    649:                case RTM_NEWADDR:
                    650:                case RTM_DELADDR:
1.14      itojun    651:                        /* init ifflags because it may have changed */
1.39      roy       652:                        rai->ifflags = if_getflags(ifindex, rai->ifflags);
1.14      itojun    653:                        break;
1.1       itojun    654:                case RTM_IFINFO:
1.39      roy       655:                        rai->ifflags = get_ifm_flags(next);
1.14      itojun    656:                        break;
1.39      roy       657: #ifdef RTM_IFANNOUNCE
                    658:                case RTM_IFANNOUNCE:
                    659:                        if (get_ifan_what(next) == IFAN_DEPARTURE) {
1.57      christos  660:                                logit(LOG_DEBUG,
1.61      roy       661:                                       "%s: interface %s departed",
1.39      roy       662:                                       __func__, rai->ifname);
                    663:                                TAILQ_REMOVE(&ralist, rai, next);
                    664:                                if (rai->leaving)
                    665:                                        free_rainfo(rai->leaving);
                    666:                                free_rainfo(rai);
                    667:                                continue;
                    668:                        }
                    669:                        break;
                    670: #endif
1.1       itojun    671:                default:
                    672:                        /* should not reach here */
                    673:                        if (dflag > 1) {
1.57      christos  674:                                logit(LOG_DEBUG,
1.56      christos  675:                                    "%s: unknown rtmsg %d on %s",
                    676:                                    __func__, type,
1.14      itojun    677:                                    if_indextoname(ifindex, ifname));
1.1       itojun    678:                        }
                    679:                        return;
                    680:                }
1.10      itojun    681:
                    682:                /* check if an interface flag is changed */
                    683:                if ((oldifflags & IFF_UP) != 0 &&       /* UP to DOWN */
1.39      roy       684:                    (rai->ifflags & IFF_UP) == 0) {
1.57      christos  685:                        logit(LOG_INFO,
1.56      christos  686:                            "%s: interface %s becomes down. stop timer.",
1.21      itojun    687:                            __func__, rai->ifname);
1.10      itojun    688:                        rtadvd_remove_timer(&rai->timer);
1.18      itojun    689:                } else if ((oldifflags & IFF_UP) == 0 && /* DOWN to UP */
1.39      roy       690:                         (rai->ifflags & IFF_UP) != 0) {
1.57      christos  691:                        logit(LOG_INFO,
1.56      christos  692:                            "%s: interface %s becomes up. restart timer.",
1.21      itojun    693:                            __func__, rai->ifname);
1.10      itojun    694:
                    695:                        rai->initcounter = 0; /* reset the counter */
                    696:                        rai->waiting = 0; /* XXX */
1.39      roy       697:                        rtadvd_remove_timer(&rai->timer);
1.10      itojun    698:                        rai->timer = rtadvd_add_timer(ra_timeout,
1.14      itojun    699:                            ra_timer_update, rai, rai);
1.54      christos  700:                        ra_timer_update(rai, &rai->timer->tm);
1.10      itojun    701:                        rtadvd_set_timer(&rai->timer->tm, rai->timer);
1.65      roy       702:                        rtadvd_remove_timer(&rai->timer_sol);
                    703:                        rai->timer_sol = rtadvd_add_timer(ra_timeout_sol,
                    704:                            NULL, rai, NULL);
1.39      roy       705:                } else if (prefixchange && rai->ifflags & IFF_UP) {
1.30      rpaulo    706:                        /*
                    707:                         * An advertised prefix has been added or invalidated.
                    708:                         * Will notice the change in a short delay.
                    709:                         */
                    710:                        rai->initcounter = 0;
1.65      roy       711:                        ra_timer_set_short_delay(rai, rai->timer);
1.10      itojun    712:                }
1.1       itojun    713:        }
                    714:
                    715:        return;
                    716: }
                    717:
                    718: void
1.36      roy       719: rtadvd_input(void)
1.1       itojun    720: {
1.36      roy       721:        ssize_t i;
1.1       itojun    722:        int *hlimp = NULL;
                    723:        struct icmp6_hdr *icp;
                    724:        int ifindex = 0;
                    725:        struct cmsghdr *cm;
                    726:        struct in6_pktinfo *pi = NULL;
1.31      mrg       727:        char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
1.1       itojun    728:        struct in6_addr dst = in6addr_any;
1.39      roy       729:        struct rainfo *rai;
1.1       itojun    730:
                    731:        /*
                    732:         * Get message. We reset msg_controllen since the field could
                    733:         * be modified if we had received a message before setting
                    734:         * receive options.
                    735:         */
1.6       itojun    736:        rcvmhdr.msg_controllen = rcvcmsgbuflen;
1.56      christos  737:        if ((i = prog_recvmsg(sock, &rcvmhdr, 0)) == -1)
1.1       itojun    738:                return;
                    739:
1.41      roy       740:        /* We read the buffer first to clear the FD */
                    741:        if (do_die)
                    742:                return;
                    743:
1.1       itojun    744:        /* extract optional information via Advanced API */
                    745:        for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&rcvmhdr);
                    746:             cm;
                    747:             cm = (struct cmsghdr *)CMSG_NXTHDR(&rcvmhdr, cm)) {
                    748:                if (cm->cmsg_level == IPPROTO_IPV6 &&
                    749:                    cm->cmsg_type == IPV6_PKTINFO &&
                    750:                    cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) {
                    751:                        pi = (struct in6_pktinfo *)(CMSG_DATA(cm));
                    752:                        ifindex = pi->ipi6_ifindex;
                    753:                        dst = pi->ipi6_addr;
                    754:                }
                    755:                if (cm->cmsg_level == IPPROTO_IPV6 &&
                    756:                    cm->cmsg_type == IPV6_HOPLIMIT &&
                    757:                    cm->cmsg_len == CMSG_LEN(sizeof(int)))
                    758:                        hlimp = (int *)CMSG_DATA(cm);
                    759:        }
                    760:        if (ifindex == 0) {
1.57      christos  761:                logit(LOG_ERR,
1.56      christos  762:                       "%s: failed to get receiving interface",
1.21      itojun    763:                       __func__);
1.1       itojun    764:                return;
                    765:        }
                    766:        if (hlimp == NULL) {
1.57      christos  767:                logit(LOG_ERR,
1.56      christos  768:                       "%s: failed to get receiving hop limit",
1.21      itojun    769:                       __func__);
1.1       itojun    770:                return;
                    771:        }
                    772:
1.39      roy       773:        if ((rai = if_indextorainfo(pi->ipi6_ifindex)) == NULL) {
                    774:                if (dflag > 1) {
1.57      christos  775:                        logit(LOG_DEBUG,
1.56      christos  776:                               "%s: received data for non advertising "
1.39      roy       777:                               "interface (%s)",
                    778:                               __func__,
                    779:                               if_indextoname(pi->ipi6_ifindex, ifnamebuf));
                    780:                }
                    781:                return;
                    782:        }
1.10      itojun    783:        /*
                    784:         * If we happen to receive data on an interface which is now down,
                    785:         * just discard the data.
                    786:         */
1.39      roy       787:        if ((rai->ifflags & IFF_UP) == 0) {
1.57      christos  788:                logit(LOG_INFO,
1.56      christos  789:                       "%s: received data on a disabled interface (%s)",
1.21      itojun    790:                       __func__,
1.10      itojun    791:                       if_indextoname(pi->ipi6_ifindex, ifnamebuf));
                    792:                return;
                    793:        }
                    794:
1.36      roy       795:        if ((size_t)i < sizeof(struct icmp6_hdr)) {
1.57      christos  796:                logit(LOG_ERR,
1.56      christos  797:                       "%s: packet size(%zd) is too short",
1.21      itojun    798:                       __func__, i);
1.1       itojun    799:                return;
                    800:        }
                    801:
                    802:        icp = (struct icmp6_hdr *)rcvmhdr.msg_iov[0].iov_base;
                    803:
1.14      itojun    804:        switch (icp->icmp6_type) {
                    805:        case ND_ROUTER_SOLICIT:
                    806:                /*
                    807:                 * Message verification - RFC-2461 6.1.1
                    808:                 * XXX: these checks must be done in the kernel as well,
                    809:                 *      but we can't completely rely on them.
                    810:                 */
                    811:                if (*hlimp != 255) {
1.57      christos  812:                        logit(LOG_NOTICE,
1.56      christos  813:                            "%s: RS with invalid hop limit(%d) "
1.14      itojun    814:                            "received from %s on %s",
1.21      itojun    815:                            __func__, *hlimp,
1.30      rpaulo    816:                            inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf,
1.14      itojun    817:                            INET6_ADDRSTRLEN),
                    818:                            if_indextoname(pi->ipi6_ifindex, ifnamebuf));
                    819:                        return;
                    820:                }
                    821:                if (icp->icmp6_code) {
1.57      christos  822:                        logit(LOG_NOTICE,
1.56      christos  823:                            "%s: RS with invalid ICMP6 code(%d) "
1.14      itojun    824:                            "received from %s on %s",
1.21      itojun    825:                            __func__, icp->icmp6_code,
1.30      rpaulo    826:                            inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf,
1.14      itojun    827:                            INET6_ADDRSTRLEN),
                    828:                            if_indextoname(pi->ipi6_ifindex, ifnamebuf));
                    829:                        return;
                    830:                }
1.36      roy       831:                if ((size_t)i < sizeof(struct nd_router_solicit)) {
1.57      christos  832:                        logit(LOG_NOTICE,
1.56      christos  833:                            "%s: RS from %s on %s does not have enough "
1.36      roy       834:                            "length (len = %zd)",
1.21      itojun    835:                            __func__,
1.30      rpaulo    836:                            inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf,
1.14      itojun    837:                            INET6_ADDRSTRLEN),
                    838:                            if_indextoname(pi->ipi6_ifindex, ifnamebuf), i);
                    839:                        return;
                    840:                }
1.30      rpaulo    841:                rs_input(i, (struct nd_router_solicit *)icp, pi, &rcvfrom);
1.14      itojun    842:                break;
                    843:        case ND_ROUTER_ADVERT:
                    844:                /*
                    845:                 * Message verification - RFC-2461 6.1.2
                    846:                 * XXX: there's a same dilemma as above...
                    847:                 */
                    848:                if (*hlimp != 255) {
1.57      christos  849:                        logit(LOG_NOTICE,
1.56      christos  850:                            "%s: RA with invalid hop limit(%d) "
1.14      itojun    851:                            "received from %s on %s",
1.21      itojun    852:                            __func__, *hlimp,
1.30      rpaulo    853:                            inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf,
1.14      itojun    854:                            INET6_ADDRSTRLEN),
                    855:                            if_indextoname(pi->ipi6_ifindex, ifnamebuf));
                    856:                        return;
                    857:                }
                    858:                if (icp->icmp6_code) {
1.57      christos  859:                        logit(LOG_NOTICE,
1.56      christos  860:                            "%s: RA with invalid ICMP6 code(%d) "
1.14      itojun    861:                            "received from %s on %s",
1.21      itojun    862:                            __func__, icp->icmp6_code,
1.30      rpaulo    863:                            inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf,
1.14      itojun    864:                            INET6_ADDRSTRLEN),
                    865:                            if_indextoname(pi->ipi6_ifindex, ifnamebuf));
                    866:                        return;
                    867:                }
1.36      roy       868:                if ((size_t)i < sizeof(struct nd_router_advert)) {
1.57      christos  869:                        logit(LOG_NOTICE,
1.56      christos  870:                            "%s: RA from %s on %s does not have enough "
1.36      roy       871:                            "length (len = %zd)",
1.21      itojun    872:                            __func__,
1.30      rpaulo    873:                            inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf,
1.14      itojun    874:                            INET6_ADDRSTRLEN),
                    875:                            if_indextoname(pi->ipi6_ifindex, ifnamebuf), i);
                    876:                        return;
                    877:                }
1.30      rpaulo    878:                ra_input(i, (struct nd_router_advert *)icp, pi, &rcvfrom);
1.14      itojun    879:                break;
                    880:        case ICMP6_ROUTER_RENUMBERING:
                    881:                if (accept_rr == 0) {
1.57      christos  882:                        logit(LOG_ERR, "%s: received a router renumbering "
1.14      itojun    883:                            "message, but not allowed to be accepted",
1.21      itojun    884:                            __func__);
1.14      itojun    885:                        break;
                    886:                }
1.30      rpaulo    887:                rr_input(i, (struct icmp6_router_renum *)icp, pi, &rcvfrom,
1.14      itojun    888:                         &dst);
                    889:                break;
                    890:        default:
                    891:                /*
                    892:                 * Note that this case is POSSIBLE, especially just
                    893:                 * after invocation of the daemon. This is because we
                    894:                 * could receive message after opening the socket and
                    895:                 * before setting ICMP6 type filter(see sock_open()).
                    896:                 */
1.57      christos  897:                logit(LOG_ERR, "%s: invalid icmp type(%d)",
1.21      itojun    898:                    __func__, icp->icmp6_type);
1.14      itojun    899:                return;
1.1       itojun    900:        }
                    901: }
                    902:
                    903: static void
                    904: rs_input(int len, struct nd_router_solicit *rs,
                    905:         struct in6_pktinfo *pi, struct sockaddr_in6 *from)
                    906: {
1.31      mrg       907:        char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
1.1       itojun    908:        union nd_opts ndopts;
1.39      roy       909:        struct rainfo *rai;
1.30      rpaulo    910:        struct soliciter *sol;
1.1       itojun    911:
1.57      christos  912:        logit(LOG_DEBUG,
1.56      christos  913:               "%s: RS received from %s on %s",
1.21      itojun    914:               __func__,
1.1       itojun    915:               inet_ntop(AF_INET6, &from->sin6_addr,
                    916:                         ntopbuf, INET6_ADDRSTRLEN),
                    917:               if_indextoname(pi->ipi6_ifindex, ifnamebuf));
                    918:
                    919:        /* ND option check */
                    920:        memset(&ndopts, 0, sizeof(ndopts));
1.36      roy       921:        TAILQ_INIT(&ndopts.nd_opts_list);
1.1       itojun    922:        if (nd6_options((struct nd_opt_hdr *)(rs + 1),
                    923:                        len - sizeof(struct nd_router_solicit),
1.18      itojun    924:                        &ndopts, NDOPT_FLAG_SRCLINKADDR)) {
1.57      christos  925:                logit(LOG_INFO,
1.56      christos  926:                       "%s: ND option check failed for an RS from %s on %s",
1.21      itojun    927:                       __func__,
1.1       itojun    928:                       inet_ntop(AF_INET6, &from->sin6_addr,
                    929:                                 ntopbuf, INET6_ADDRSTRLEN),
                    930:                       if_indextoname(pi->ipi6_ifindex, ifnamebuf));
                    931:                return;
                    932:        }
                    933:
                    934:        /*
                    935:         * If the IP source address is the unspecified address, there
                    936:         * must be no source link-layer address option in the message.
                    937:         * (RFC-2461 6.1.1)
                    938:         */
                    939:        if (IN6_IS_ADDR_UNSPECIFIED(&from->sin6_addr) &&
                    940:            ndopts.nd_opts_src_lladdr) {
1.57      christos  941:                logit(LOG_INFO,
1.56      christos  942:                       "%s: RS from unspecified src on %s has a link-layer"
1.1       itojun    943:                       " address option",
1.21      itojun    944:                       __func__,
1.1       itojun    945:                       if_indextoname(pi->ipi6_ifindex, ifnamebuf));
                    946:                goto done;
                    947:        }
                    948:
1.39      roy       949:        if ((rai = if_indextorainfo(pi->ipi6_ifindex)) == NULL) {
1.57      christos  950:                logit(LOG_INFO,
1.56      christos  951:                       "%s: RS received on non advertising interface(%s)",
1.21      itojun    952:                       __func__,
1.1       itojun    953:                       if_indextoname(pi->ipi6_ifindex, ifnamebuf));
                    954:                goto done;
                    955:        }
                    956:
1.39      roy       957:        if (rai->leaving) {
1.57      christos  958:                logit(LOG_INFO,
1.56      christos  959:                       "%s: RS received on reconfiguring advertising interface(%s)",
1.39      roy       960:                       __func__, rai->ifname);
                    961:                goto done;
                    962:        }
                    963:
                    964:        rai->rsinput++;         /* increment statistics */
1.9       itojun    965:
1.1       itojun    966:        /*
                    967:         * Decide whether to send RA according to the rate-limit
                    968:         * consideration.
                    969:         */
1.10      itojun    970:
1.30      rpaulo    971:        /* record sockaddr waiting for RA, if possible */
1.65      roy       972:        TAILQ_FOREACH(sol, &rai->soliciter, next) {
                    973:                if (IN6_ARE_ADDR_EQUAL(&sol->addr.sin6_addr, &from->sin6_addr))
                    974:                        break;
                    975:        }
                    976:        if (sol == NULL) {
                    977:                sol = malloc(sizeof(*sol));
                    978:                if (sol == NULL) {
                    979:                        logit(LOG_ERR, "%s: malloc: %m", __func__);
                    980:                } else {
                    981:                        sol->addr = *from;
                    982:                        /* XXX RFC2553 need clarification on flowinfo */
                    983:                        sol->addr.sin6_flowinfo = 0;
                    984:                        TAILQ_INSERT_TAIL(&rai->soliciter, sol, next);
                    985:                }
1.30      rpaulo    986:        }
                    987:
                    988:        /*
                    989:         * If there is already a waiting RS packet, don't
                    990:         * update the timer.
                    991:         */
1.39      roy       992:        if (rai->waiting++)
1.30      rpaulo    993:                goto done;
                    994:
1.65      roy       995:        ra_timer_set_short_delay(rai, rai->timer_sol);
1.1       itojun    996:
1.30      rpaulo    997: done:
                    998:        free_ndopts(&ndopts);
                    999: }
1.1       itojun   1000:
1.39      roy      1001: void
1.65      roy      1002: ra_timer_set_short_delay(struct rainfo *rai, struct rtadvd_timer *timer)
1.30      rpaulo   1003: {
                   1004:        long delay;     /* must not be greater than 1000000 */
1.47      roy      1005:        struct timespec interval, now, min_delay, tm_tmp, *rest;
1.1       itojun   1006:
1.30      rpaulo   1007:        /*
                   1008:         * Compute a random delay. If the computed value
                   1009:         * corresponds to a time later than the time the next
                   1010:         * multicast RA is scheduled to be sent, ignore the random
                   1011:         * delay and send the advertisement at the
                   1012:         * already-scheduled time. RFC2461 6.2.6
                   1013:         */
                   1014:        delay = arc4random() % MAX_RA_DELAY_TIME;
                   1015:        interval.tv_sec = 0;
1.47      roy      1016:        interval.tv_nsec = delay;
1.30      rpaulo   1017:        rest = rtadvd_timer_rest(rai->timer);
1.47      roy      1018:        if (timespeccmp(rest, &interval, <)) {
1.57      christos 1019:                logit(LOG_DEBUG, "%s: random delay is larger than "
1.30      rpaulo   1020:                    "the rest of current timer", __func__);
                   1021:                interval = *rest;
1.1       itojun   1022:        }
                   1023:
1.30      rpaulo   1024:        /*
                   1025:         * If we sent a multicast Router Advertisement within
                   1026:         * the last MIN_DELAY_BETWEEN_RAS seconds, schedule
                   1027:         * the advertisement to be sent at a time corresponding to
                   1028:         * MIN_DELAY_BETWEEN_RAS plus the random value after the
                   1029:         * previous advertisement was sent.
                   1030:         */
1.51      ozaki-r  1031:        prog_clock_gettime(CLOCK_MONOTONIC, &now);
1.47      roy      1032:        timespecsub(&now, &rai->lastsent, &tm_tmp);
1.30      rpaulo   1033:        min_delay.tv_sec = MIN_DELAY_BETWEEN_RAS;
1.47      roy      1034:        min_delay.tv_nsec = 0;
                   1035:        if (timespeccmp(&tm_tmp, &min_delay, <)) {
                   1036:                timespecsub(&min_delay, &tm_tmp, &min_delay);
                   1037:                timespecadd(&min_delay, &interval, &interval);
1.30      rpaulo   1038:        }
1.65      roy      1039:        rtadvd_set_timer(&interval, timer);
1.1       itojun   1040: }
                   1041:
                   1042: static void
                   1043: ra_input(int len, struct nd_router_advert *ra,
                   1044:         struct in6_pktinfo *pi, struct sockaddr_in6 *from)
                   1045: {
                   1046:        struct rainfo *rai;
1.31      mrg      1047:        char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
1.1       itojun   1048:        union nd_opts ndopts;
1.36      roy      1049:        const char *on_off[] = {"OFF", "ON"};
                   1050:        uint32_t reachabletime, retranstimer, mtu;
                   1051:        struct nd_optlist *optp;
1.9       itojun   1052:        int inconsistent = 0;
1.1       itojun   1053:
1.57      christos 1054:        logit(LOG_DEBUG,
1.56      christos 1055:               "%s: RA received from %s on %s",
1.21      itojun   1056:               __func__,
1.1       itojun   1057:               inet_ntop(AF_INET6, &from->sin6_addr,
                   1058:                         ntopbuf, INET6_ADDRSTRLEN),
                   1059:               if_indextoname(pi->ipi6_ifindex, ifnamebuf));
1.36      roy      1060:
1.1       itojun   1061:        /* ND option check */
                   1062:        memset(&ndopts, 0, sizeof(ndopts));
1.36      roy      1063:        TAILQ_INIT(&ndopts.nd_opts_list);
1.1       itojun   1064:        if (nd6_options((struct nd_opt_hdr *)(ra + 1),
1.36      roy      1065:            len - sizeof(struct nd_router_advert),
                   1066:            &ndopts, NDOPT_FLAG_SRCLINKADDR |
                   1067:            NDOPT_FLAG_PREFIXINFO | NDOPT_FLAG_MTU |
                   1068:            NDOPT_FLAG_RDNSS | NDOPT_FLAG_DNSSL))
                   1069:        {
1.57      christos 1070:                logit(LOG_INFO,
1.56      christos 1071:                    "%s: ND option check failed for an RA from %s on %s",
1.36      roy      1072:                    __func__,
                   1073:                    inet_ntop(AF_INET6, &from->sin6_addr,
                   1074:                        ntopbuf, INET6_ADDRSTRLEN),
                   1075:                        if_indextoname(pi->ipi6_ifindex, ifnamebuf));
1.1       itojun   1076:                return;
                   1077:        }
                   1078:
                   1079:        /*
                   1080:         * RA consistency check according to RFC-2461 6.2.7
                   1081:         */
                   1082:        if ((rai = if_indextorainfo(pi->ipi6_ifindex)) == 0) {
1.57      christos 1083:                logit(LOG_INFO,
1.56      christos 1084:                       "%s: received RA from %s on non-advertising"
1.1       itojun   1085:                       " interface(%s)",
1.21      itojun   1086:                       __func__,
1.1       itojun   1087:                       inet_ntop(AF_INET6, &from->sin6_addr,
                   1088:                                 ntopbuf, INET6_ADDRSTRLEN),
                   1089:                       if_indextoname(pi->ipi6_ifindex, ifnamebuf));
                   1090:                goto done;
                   1091:        }
1.39      roy      1092:        if (rai->leaving) {
1.57      christos 1093:                logit(LOG_DEBUG,
1.56      christos 1094:                       "%s: received RA on re-configuring interface (%s)",
1.39      roy      1095:                        __func__, rai->ifname);
                   1096:                goto done;
                   1097:        }
1.9       itojun   1098:        rai->rainput++;         /* increment statistics */
1.61      roy      1099:
1.1       itojun   1100:        /* Cur Hop Limit value */
                   1101:        if (ra->nd_ra_curhoplimit && rai->hoplimit &&
                   1102:            ra->nd_ra_curhoplimit != rai->hoplimit) {
1.57      christos 1103:                logit(LOG_INFO,
1.56      christos 1104:                       "%s: CurHopLimit inconsistent on %s:"
1.1       itojun   1105:                       " %d from %s, %d from us",
1.21      itojun   1106:                       __func__,
1.1       itojun   1107:                       rai->ifname,
                   1108:                       ra->nd_ra_curhoplimit,
                   1109:                       inet_ntop(AF_INET6, &from->sin6_addr,
                   1110:                                 ntopbuf, INET6_ADDRSTRLEN),
                   1111:                       rai->hoplimit);
1.9       itojun   1112:                inconsistent++;
1.1       itojun   1113:        }
                   1114:        /* M flag */
                   1115:        if ((ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) !=
                   1116:            rai->managedflg) {
1.57      christos 1117:                logit(LOG_INFO,
1.56      christos 1118:                       "%s: M flag inconsistent on %s:"
1.1       itojun   1119:                       " %s from %s, %s from us",
1.21      itojun   1120:                       __func__,
1.1       itojun   1121:                       rai->ifname,
                   1122:                       on_off[!rai->managedflg],
                   1123:                       inet_ntop(AF_INET6, &from->sin6_addr,
                   1124:                                 ntopbuf, INET6_ADDRSTRLEN),
                   1125:                       on_off[rai->managedflg]);
1.9       itojun   1126:                inconsistent++;
1.1       itojun   1127:        }
                   1128:        /* O flag */
                   1129:        if ((ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER) !=
                   1130:            rai->otherflg) {
1.57      christos 1131:                logit(LOG_INFO,
1.56      christos 1132:                       "%s: O flag inconsistent on %s:"
1.1       itojun   1133:                       " %s from %s, %s from us",
1.21      itojun   1134:                       __func__,
1.1       itojun   1135:                       rai->ifname,
                   1136:                       on_off[!rai->otherflg],
                   1137:                       inet_ntop(AF_INET6, &from->sin6_addr,
                   1138:                                 ntopbuf, INET6_ADDRSTRLEN),
                   1139:                       on_off[rai->otherflg]);
1.9       itojun   1140:                inconsistent++;
1.1       itojun   1141:        }
                   1142:        /* Reachable Time */
                   1143:        reachabletime = ntohl(ra->nd_ra_reachable);
                   1144:        if (reachabletime && rai->reachabletime &&
                   1145:            reachabletime != rai->reachabletime) {
1.57      christos 1146:                logit(LOG_INFO,
1.56      christos 1147:                       "%s: ReachableTime inconsistent on %s:"
1.1       itojun   1148:                       " %d from %s, %d from us",
1.21      itojun   1149:                       __func__,
1.1       itojun   1150:                       rai->ifname,
                   1151:                       reachabletime,
                   1152:                       inet_ntop(AF_INET6, &from->sin6_addr,
                   1153:                                 ntopbuf, INET6_ADDRSTRLEN),
                   1154:                       rai->reachabletime);
1.9       itojun   1155:                inconsistent++;
1.1       itojun   1156:        }
                   1157:        /* Retrans Timer */
                   1158:        retranstimer = ntohl(ra->nd_ra_retransmit);
                   1159:        if (retranstimer && rai->retranstimer &&
                   1160:            retranstimer != rai->retranstimer) {
1.57      christos 1161:                logit(LOG_INFO,
1.56      christos 1162:                       "%s: RetranceTimer inconsistent on %s:"
1.1       itojun   1163:                       " %d from %s, %d from us",
1.21      itojun   1164:                       __func__,
1.1       itojun   1165:                       rai->ifname,
                   1166:                       retranstimer,
                   1167:                       inet_ntop(AF_INET6, &from->sin6_addr,
                   1168:                                 ntopbuf, INET6_ADDRSTRLEN),
                   1169:                       rai->retranstimer);
1.9       itojun   1170:                inconsistent++;
1.1       itojun   1171:        }
                   1172:        /* Values in the MTU options */
                   1173:        if (ndopts.nd_opts_mtu) {
                   1174:                mtu = ntohl(ndopts.nd_opts_mtu->nd_opt_mtu_mtu);
                   1175:                if (mtu && rai->linkmtu && mtu != rai->linkmtu) {
1.57      christos 1176:                        logit(LOG_INFO,
1.56      christos 1177:                               "%s: MTU option value inconsistent on %s:"
1.1       itojun   1178:                               " %d from %s, %d from us",
1.21      itojun   1179:                               __func__,
1.1       itojun   1180:                               rai->ifname, mtu,
                   1181:                               inet_ntop(AF_INET6, &from->sin6_addr,
                   1182:                                         ntopbuf, INET6_ADDRSTRLEN),
                   1183:                               rai->linkmtu);
1.9       itojun   1184:                        inconsistent++;
1.1       itojun   1185:                }
                   1186:        }
                   1187:        /* Preferred and Valid Lifetimes for prefixes */
1.36      roy      1188:        if (ndopts.nd_opts_pi)
                   1189:                if (prefix_check(ndopts.nd_opts_pi, rai, from))
                   1190:                        inconsistent++;
                   1191:        TAILQ_FOREACH(optp, &ndopts.nd_opts_list, next)
                   1192:                if (prefix_check((struct nd_opt_prefix_info *)optp->opt,
                   1193:                    rai, from))
                   1194:                        inconsistent++;
1.9       itojun   1195:
1.11      itojun   1196:        if (inconsistent)
1.9       itojun   1197:                rai->rainconsistent++;
1.61      roy      1198:
1.30      rpaulo   1199: done:
1.1       itojun   1200:        free_ndopts(&ndopts);
                   1201: }
                   1202:
1.9       itojun   1203: /* return a non-zero value if the received prefix is inconsitent with ours */
                   1204: static int
1.1       itojun   1205: prefix_check(struct nd_opt_prefix_info *pinfo,
                   1206:             struct rainfo *rai, struct sockaddr_in6 *from)
                   1207: {
1.36      roy      1208:        uint32_t preferred_time, valid_time;
1.1       itojun   1209:        struct prefix *pp;
1.9       itojun   1210:        int inconsistent = 0;
1.31      mrg      1211:        char ntopbuf[INET6_ADDRSTRLEN], prefixbuf[INET6_ADDRSTRLEN];
1.47      roy      1212:        struct timespec now;
1.60      roy      1213:        struct in6_addr prefix;
1.1       itojun   1214:
                   1215: #if 0                          /* impossible */
                   1216:        if (pinfo->nd_opt_pi_type != ND_OPT_PREFIX_INFORMATION)
1.54      christos 1217:                return 0;
1.1       itojun   1218: #endif
                   1219:
1.60      roy      1220:        memcpy(&prefix, &pinfo->nd_opt_pi_prefix, sizeof(prefix));
                   1221:
1.1       itojun   1222:        /*
                   1223:         * log if the adveritsed prefix has link-local scope(sanity check?)
                   1224:         */
1.60      roy      1225:        if (IN6_IS_ADDR_LINKLOCAL(&prefix)) {
1.57      christos 1226:                logit(LOG_INFO,
1.56      christos 1227:                       "%s: link-local prefix %s/%d is advertised "
1.1       itojun   1228:                       "from %s on %s",
1.21      itojun   1229:                       __func__,
1.60      roy      1230:                       inet_ntop(AF_INET6, &prefix,
1.1       itojun   1231:                                 prefixbuf, INET6_ADDRSTRLEN),
                   1232:                       pinfo->nd_opt_pi_prefix_len,
                   1233:                       inet_ntop(AF_INET6, &from->sin6_addr,
                   1234:                                 ntopbuf, INET6_ADDRSTRLEN),
                   1235:                       rai->ifname);
                   1236:        }
                   1237:
1.60      roy      1238:        if ((pp = find_prefix(rai, &prefix,
1.1       itojun   1239:                              pinfo->nd_opt_pi_prefix_len)) == NULL) {
1.57      christos 1240:                logit(LOG_INFO,
1.56      christos 1241:                       "%s: prefix %s/%d from %s on %s is not in our list",
1.21      itojun   1242:                       __func__,
1.60      roy      1243:                       inet_ntop(AF_INET6, &prefix,
1.1       itojun   1244:                                 prefixbuf, INET6_ADDRSTRLEN),
                   1245:                       pinfo->nd_opt_pi_prefix_len,
                   1246:                       inet_ntop(AF_INET6, &from->sin6_addr,
                   1247:                                 ntopbuf, INET6_ADDRSTRLEN),
                   1248:                       rai->ifname);
1.54      christos 1249:                return 0;
1.1       itojun   1250:        }
                   1251:
                   1252:        preferred_time = ntohl(pinfo->nd_opt_pi_preferred_time);
1.11      itojun   1253:        if (pp->pltimeexpire) {
                   1254:                /*
                   1255:                 * The lifetime is decremented in real time, so we should
                   1256:                 * compare the expiration time.
                   1257:                 * (RFC 2461 Section 6.2.7.)
                   1258:                 * XXX: can we really expect that all routers on the link
                   1259:                 * have synchronized clocks?
                   1260:                 */
1.51      ozaki-r  1261:                prog_clock_gettime(CLOCK_MONOTONIC, &now);
1.11      itojun   1262:                preferred_time += now.tv_sec;
                   1263:
1.30      rpaulo   1264:                if (!pp->timer && rai->clockskew &&
1.46      joerg    1265:                    llabs((long long)preferred_time - pp->pltimeexpire) > rai->clockskew) {
1.57      christos 1266:                        logit(LOG_INFO,
1.56      christos 1267:                               "%s: preferred lifetime for %s/%d"
1.11      itojun   1268:                               " (decr. in real time) inconsistent on %s:"
                   1269:                               " %d from %s, %ld from us",
1.21      itojun   1270:                               __func__,
1.60      roy      1271:                               inet_ntop(AF_INET6, &prefix,
1.11      itojun   1272:                                         prefixbuf, INET6_ADDRSTRLEN),
                   1273:                               pinfo->nd_opt_pi_prefix_len,
                   1274:                               rai->ifname, preferred_time,
                   1275:                               inet_ntop(AF_INET6, &from->sin6_addr,
                   1276:                                         ntopbuf, INET6_ADDRSTRLEN),
                   1277:                               pp->pltimeexpire);
                   1278:                        inconsistent++;
                   1279:                }
1.30      rpaulo   1280:        } else if (!pp->timer && preferred_time != pp->preflifetime) {
1.57      christos 1281:                logit(LOG_INFO,
1.56      christos 1282:                       "%s: preferred lifetime for %s/%d"
1.1       itojun   1283:                       " inconsistent on %s:"
                   1284:                       " %d from %s, %d from us",
1.21      itojun   1285:                       __func__,
1.60      roy      1286:                       inet_ntop(AF_INET6, &prefix,
1.1       itojun   1287:                                 prefixbuf, INET6_ADDRSTRLEN),
                   1288:                       pinfo->nd_opt_pi_prefix_len,
                   1289:                       rai->ifname, preferred_time,
                   1290:                       inet_ntop(AF_INET6, &from->sin6_addr,
                   1291:                                 ntopbuf, INET6_ADDRSTRLEN),
                   1292:                       pp->preflifetime);
1.9       itojun   1293:        }
1.1       itojun   1294:
                   1295:        valid_time = ntohl(pinfo->nd_opt_pi_valid_time);
1.11      itojun   1296:        if (pp->vltimeexpire) {
1.51      ozaki-r  1297:                prog_clock_gettime(CLOCK_MONOTONIC, &now);
1.11      itojun   1298:                valid_time += now.tv_sec;
                   1299:
1.30      rpaulo   1300:                if (!pp->timer && rai->clockskew &&
1.46      joerg    1301:                    llabs((long long)valid_time - pp->vltimeexpire) > rai->clockskew) {
1.57      christos 1302:                        logit(LOG_INFO,
1.56      christos 1303:                               "%s: valid lifetime for %s/%d"
1.11      itojun   1304:                               " (decr. in real time) inconsistent on %s:"
                   1305:                               " %d from %s, %ld from us",
1.21      itojun   1306:                               __func__,
1.60      roy      1307:                               inet_ntop(AF_INET6, &prefix,
1.11      itojun   1308:                                         prefixbuf, INET6_ADDRSTRLEN),
                   1309:                               pinfo->nd_opt_pi_prefix_len,
                   1310:                               rai->ifname, preferred_time,
                   1311:                               inet_ntop(AF_INET6, &from->sin6_addr,
                   1312:                                         ntopbuf, INET6_ADDRSTRLEN),
                   1313:                               pp->vltimeexpire);
                   1314:                        inconsistent++;
                   1315:                }
1.30      rpaulo   1316:        } else if (!pp->timer && valid_time != pp->validlifetime) {
1.57      christos 1317:                logit(LOG_INFO,
1.56      christos 1318:                       "%s: valid lifetime for %s/%d"
1.1       itojun   1319:                       " inconsistent on %s:"
                   1320:                       " %d from %s, %d from us",
1.21      itojun   1321:                       __func__,
1.60      roy      1322:                       inet_ntop(AF_INET6, &prefix,
1.1       itojun   1323:                                 prefixbuf, INET6_ADDRSTRLEN),
                   1324:                       pinfo->nd_opt_pi_prefix_len,
                   1325:                       rai->ifname, valid_time,
                   1326:                       inet_ntop(AF_INET6, &from->sin6_addr,
                   1327:                                 ntopbuf, INET6_ADDRSTRLEN),
                   1328:                       pp->validlifetime);
1.9       itojun   1329:                inconsistent++;
                   1330:        }
                   1331:
1.54      christos 1332:        return inconsistent;
1.1       itojun   1333: }
                   1334:
                   1335: struct prefix *
                   1336: find_prefix(struct rainfo *rai, struct in6_addr *prefix, int plen)
                   1337: {
                   1338:        struct prefix *pp;
                   1339:        int bytelen, bitlen;
1.36      roy      1340:        unsigned char bitmask;
1.1       itojun   1341:
1.36      roy      1342:        TAILQ_FOREACH(pp, &rai->prefix, next) {
1.1       itojun   1343:                if (plen != pp->prefixlen)
                   1344:                        continue;
                   1345:                bytelen = plen / 8;
                   1346:                bitlen = plen % 8;
1.15      itojun   1347:                bitmask = 0xff << (8 - bitlen);
1.54      christos 1348:                if (memcmp(prefix, &pp->prefix, bytelen))
1.1       itojun   1349:                        continue;
1.15      itojun   1350:                if (bitlen == 0 ||
1.61      roy      1351:                    ((prefix->s6_addr[bytelen] & bitmask) ==
1.15      itojun   1352:                     (pp->prefix.s6_addr[bytelen] & bitmask))) {
1.54      christos 1353:                        return pp;
1.15      itojun   1354:                }
1.1       itojun   1355:        }
                   1356:
1.54      christos 1357:        return NULL;
1.1       itojun   1358: }
                   1359:
1.11      itojun   1360: /* check if p0/plen0 matches p1/plen1; return 1 if matches, otherwise 0. */
                   1361: int
                   1362: prefix_match(struct in6_addr *p0, int plen0,
                   1363:             struct in6_addr *p1, int plen1)
                   1364: {
                   1365:        int bytelen, bitlen;
1.36      roy      1366:        unsigned char bitmask;
1.11      itojun   1367:
                   1368:        if (plen0 < plen1)
1.54      christos 1369:                return 0;
1.11      itojun   1370:        bytelen = plen1 / 8;
                   1371:        bitlen = plen1 % 8;
1.15      itojun   1372:        bitmask = 0xff << (8 - bitlen);
1.54      christos 1373:        if (memcmp(p0, p1, bytelen))
                   1374:                return 0;
1.15      itojun   1375:        if (bitlen == 0 ||
                   1376:            ((p0->s6_addr[bytelen] & bitmask) ==
1.61      roy      1377:             (p1->s6_addr[bytelen] & bitmask))) {
1.54      christos 1378:                return 1;
1.15      itojun   1379:        }
1.11      itojun   1380:
1.54      christos 1381:        return 0;
1.11      itojun   1382: }
                   1383:
1.1       itojun   1384: static int
                   1385: nd6_options(struct nd_opt_hdr *hdr, int limit,
1.36      roy      1386:            union nd_opts *ndopts, uint32_t optflags)
1.1       itojun   1387: {
                   1388:        int optlen = 0;
                   1389:
                   1390:        for (; limit > 0; limit -= optlen) {
1.36      roy      1391:                if ((size_t)limit < sizeof(struct nd_opt_hdr)) {
1.57      christos 1392:                        logit(LOG_INFO, "%s: short option header", __func__);
1.27      itojun   1393:                        goto bad;
                   1394:                }
                   1395:
1.36      roy      1396:                hdr = (struct nd_opt_hdr *)((char *)hdr + optlen);
1.1       itojun   1397:                if (hdr->nd_opt_len == 0) {
1.57      christos 1398:                        logit(LOG_INFO,
1.56      christos 1399:                            "%s: bad ND option length(0) (type = %d)",
1.21      itojun   1400:                            __func__, hdr->nd_opt_type);
1.1       itojun   1401:                        goto bad;
                   1402:                }
1.27      itojun   1403:                optlen = hdr->nd_opt_len << 3;
                   1404:                if (optlen > limit) {
1.57      christos 1405:                        logit(LOG_INFO, "%s: short option", __func__);
1.27      itojun   1406:                        goto bad;
                   1407:                }
1.1       itojun   1408:
1.36      roy      1409:                if (hdr->nd_opt_type > ND_OPT_MTU &&
                   1410:                    hdr->nd_opt_type != ND_OPT_RDNSS &&
                   1411:                    hdr->nd_opt_type != ND_OPT_DNSSL)
1.17      itojun   1412:                {
1.57      christos 1413:                        logit(LOG_INFO, "%s: unknown ND option(type %d)",
1.21      itojun   1414:                            __func__, hdr->nd_opt_type);
1.1       itojun   1415:                        continue;
                   1416:                }
                   1417:
                   1418:                if ((ndopt_flags[hdr->nd_opt_type] & optflags) == 0) {
1.57      christos 1419:                        logit(LOG_INFO, "%s: unexpected ND option(type %d)",
1.21      itojun   1420:                            __func__, hdr->nd_opt_type);
1.1       itojun   1421:                        continue;
                   1422:                }
                   1423:
1.27      itojun   1424:                /*
                   1425:                 * Option length check.  Do it here for all fixed-length
                   1426:                 * options.
                   1427:                 */
                   1428:                if ((hdr->nd_opt_type == ND_OPT_MTU &&
                   1429:                    (optlen != sizeof(struct nd_opt_mtu))) ||
                   1430:                    ((hdr->nd_opt_type == ND_OPT_PREFIX_INFORMATION &&
1.42      roy      1431:                    optlen != sizeof(struct nd_opt_prefix_info))) ||
                   1432:                    (hdr->nd_opt_type == ND_OPT_RDNSS &&
                   1433:                    ((optlen < (int)sizeof(struct nd_opt_rdnss) ||
                   1434:                    (optlen - sizeof(struct nd_opt_rdnss)) % 16 != 0))) ||
                   1435:                    (hdr->nd_opt_type == ND_OPT_DNSSL &&
                   1436:                    optlen < (int)sizeof(struct nd_opt_dnssl)))
                   1437:                {
1.57      christos 1438:                        logit(LOG_INFO, "%s: invalid option length",
1.27      itojun   1439:                            __func__);
                   1440:                        continue;
                   1441:                }
                   1442:
1.14      itojun   1443:                switch (hdr->nd_opt_type) {
                   1444:                case ND_OPT_TARGET_LINKADDR:
                   1445:                case ND_OPT_REDIRECTED_HEADER:
1.42      roy      1446:                case ND_OPT_RDNSS:
                   1447:                case ND_OPT_DNSSL:
1.27      itojun   1448:                        break;  /* we don't care about these options */
1.30      rpaulo   1449:                case ND_OPT_SOURCE_LINKADDR:
1.14      itojun   1450:                case ND_OPT_MTU:
                   1451:                        if (ndopts->nd_opt_array[hdr->nd_opt_type]) {
1.57      christos 1452:                                logit(LOG_INFO,
1.56      christos 1453:                                    "%s: duplicated ND option (type = %d)",
1.21      itojun   1454:                                    __func__, hdr->nd_opt_type);
1.14      itojun   1455:                        }
                   1456:                        ndopts->nd_opt_array[hdr->nd_opt_type] = hdr;
                   1457:                        break;
                   1458:                case ND_OPT_PREFIX_INFORMATION:
                   1459:                {
                   1460:                        struct nd_optlist *pfxlist;
                   1461:
                   1462:                        if (ndopts->nd_opts_pi == 0) {
                   1463:                                ndopts->nd_opts_pi =
                   1464:                                    (struct nd_opt_prefix_info *)hdr;
                   1465:                                continue;
                   1466:                        }
                   1467:                        if ((pfxlist = malloc(sizeof(*pfxlist))) == NULL) {
1.57      christos 1468:                                logit(LOG_ERR, "%s: can't allocate memory",
1.21      itojun   1469:                                    __func__);
1.14      itojun   1470:                                goto bad;
                   1471:                        }
                   1472:                        pfxlist->opt = hdr;
1.36      roy      1473:                        TAILQ_INSERT_TAIL(&ndopts->nd_opts_list, pfxlist, next);
1.14      itojun   1474:
                   1475:                        break;
                   1476:                }
                   1477:                default:        /* impossible */
                   1478:                        break;
1.1       itojun   1479:                }
                   1480:        }
                   1481:
1.54      christos 1482:        return 0;
1.1       itojun   1483:
                   1484:   bad:
                   1485:        free_ndopts(ndopts);
1.54      christos 1486:        return -1;
1.1       itojun   1487: }
                   1488:
                   1489: static void
                   1490: free_ndopts(union nd_opts *ndopts)
                   1491: {
1.36      roy      1492:        struct nd_optlist *opt;
1.1       itojun   1493:
1.36      roy      1494:        while ((opt = TAILQ_FIRST(&ndopts->nd_opts_list)) != NULL) {
                   1495:                TAILQ_REMOVE(&ndopts->nd_opts_list, opt, next);
1.1       itojun   1496:                free(opt);
                   1497:        }
                   1498: }
                   1499:
                   1500: void
1.36      roy      1501: sock_open(void)
1.1       itojun   1502: {
                   1503:        struct icmp6_filter filt;
                   1504:        struct ipv6_mreq mreq;
1.36      roy      1505:        struct rainfo *ra;
1.1       itojun   1506:        int on;
                   1507:        /* XXX: should be max MTU attached to the node */
1.36      roy      1508:        static unsigned char answer[1500];
1.6       itojun   1509:
                   1510:        rcvcmsgbuflen = CMSG_SPACE(sizeof(struct in6_pktinfo)) +
                   1511:                                CMSG_SPACE(sizeof(int));
1.36      roy      1512:        rcvcmsgbuf = malloc(rcvcmsgbuflen);
1.6       itojun   1513:        if (rcvcmsgbuf == NULL) {
1.57      christos 1514:                logit(LOG_ERR, "%s: malloc: %m", __func__);
1.54      christos 1515:                exit(EXIT_FAILURE);
1.6       itojun   1516:        }
                   1517:
1.66      roy      1518:        sndcmsgbuflen = CMSG_SPACE(sizeof(struct in6_pktinfo));
1.36      roy      1519:        sndcmsgbuf = malloc(sndcmsgbuflen);
1.6       itojun   1520:        if (sndcmsgbuf == NULL) {
1.57      christos 1521:                logit(LOG_ERR, "%s: malloc: %m", __func__);
1.54      christos 1522:                exit(EXIT_FAILURE);
1.6       itojun   1523:        }
1.1       itojun   1524:
1.56      christos 1525:        if ((sock = prog_socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) == -1) {
1.57      christos 1526:                logit(LOG_ERR, "%s: socket: %m", __func__);
1.54      christos 1527:                exit(EXIT_FAILURE);
1.1       itojun   1528:        }
                   1529:
1.48      roy      1530:        /* RFC 4861 Section 4.2 */
                   1531:        on = 255;
1.51      ozaki-r  1532:        if (prog_setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &on,
1.48      roy      1533:                       sizeof(on)) == -1) {
1.57      christos 1534:                logit(LOG_ERR, "%s: IPV6_MULTICAST_HOPS: %m", __func__);
1.54      christos 1535:                exit(EXIT_FAILURE);
1.48      roy      1536:        }
1.65      roy      1537:        on = 255;
                   1538:        if (prog_setsockopt(sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &on,
                   1539:                       sizeof(on)) == -1) {
                   1540:                logit(LOG_ERR, "%s: IPV6_UNICAST_HOPS: %m", __func__);
                   1541:                exit(EXIT_FAILURE);
                   1542:        }
1.48      roy      1543:
1.1       itojun   1544:        /* specify to tell receiving interface */
                   1545:        on = 1;
1.51      ozaki-r  1546:        if (prog_setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on,
1.56      christos 1547:                       sizeof(on)) == -1) {
1.57      christos 1548:                logit(LOG_ERR, "%s: IPV6_RECVPKTINFO: %m", __func__);
1.54      christos 1549:                exit(EXIT_FAILURE);
1.5       itojun   1550:        }
1.1       itojun   1551:
                   1552:        on = 1;
                   1553:        /* specify to tell value of hoplimit field of received IP6 hdr */
1.51      ozaki-r  1554:        if (prog_setsockopt(sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on,
1.56      christos 1555:                       sizeof(on)) == -1) {
1.57      christos 1556:                logit(LOG_ERR, "%s: IPV6_RECVHOPLIMIT: %m", __func__);
1.54      christos 1557:                exit(EXIT_FAILURE);
1.5       itojun   1558:        }
1.1       itojun   1559:
                   1560:        ICMP6_FILTER_SETBLOCKALL(&filt);
                   1561:        ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filt);
                   1562:        ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt);
                   1563:        if (accept_rr)
                   1564:                ICMP6_FILTER_SETPASS(ICMP6_ROUTER_RENUMBERING, &filt);
1.51      ozaki-r  1565:        if (prog_setsockopt(sock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
1.56      christos 1566:                       sizeof(filt)) == -1) {
1.57      christos 1567:                logit(LOG_ERR, "%s: IICMP6_FILTER: %m", __func__);
1.54      christos 1568:                exit(EXIT_FAILURE);
1.1       itojun   1569:        }
                   1570:
                   1571:        /*
                   1572:         * join all routers multicast address on each advertising interface.
                   1573:         */
1.11      itojun   1574:        if (inet_pton(AF_INET6, ALLROUTERS_LINK,
1.36      roy      1575:            mreq.ipv6mr_multiaddr.s6_addr) != 1)
                   1576:        {
1.57      christos 1577:                logit(LOG_ERR, "%s: inet_pton failed(library bug?)",
1.36      roy      1578:                    __func__);
1.54      christos 1579:                exit(EXIT_FAILURE);
1.1       itojun   1580:        }
1.36      roy      1581:        TAILQ_FOREACH(ra, &ralist, next) {
1.1       itojun   1582:                mreq.ipv6mr_interface = ra->ifindex;
1.51      ozaki-r  1583:                if (prog_setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq,
1.56      christos 1584:                               sizeof(mreq)) == -1) {
1.57      christos 1585:                        logit(LOG_ERR, "%s: IPV6_JOIN_GROUP(link) on %s: %m",
1.49      roy      1586:                               __func__, ra->ifname);
1.56      christos 1587:                        continue;
1.1       itojun   1588:                }
                   1589:        }
1.11      itojun   1590:
                   1591:        /*
                   1592:         * When attending router renumbering, join all-routers site-local
1.61      roy      1593:         * multicast group.
1.11      itojun   1594:         */
                   1595:        if (accept_rr) {
                   1596:                if (inet_pton(AF_INET6, ALLROUTERS_SITE,
1.36      roy      1597:                     mreq.ipv6mr_multiaddr.s6_addr) != 1)
                   1598:                {
1.57      christos 1599:                        logit(LOG_ERR, "%s: inet_pton failed(library bug?)",
1.36      roy      1600:                            __func__);
1.54      christos 1601:                        exit(EXIT_FAILURE);
1.11      itojun   1602:                }
1.36      roy      1603:                ra = TAILQ_FIRST(&ralist);
1.11      itojun   1604:                if (mcastif) {
                   1605:                        if ((mreq.ipv6mr_interface = if_nametoindex(mcastif))
                   1606:                            == 0) {
1.57      christos 1607:                                logit(LOG_ERR,
1.56      christos 1608:                                       "%s: invalid interface: %s",
1.21      itojun   1609:                                       __func__, mcastif);
1.54      christos 1610:                                exit(EXIT_FAILURE);
1.11      itojun   1611:                        }
                   1612:                } else
1.36      roy      1613:                        mreq.ipv6mr_interface = ra->ifindex;
1.51      ozaki-r  1614:                if (prog_setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP,
1.56      christos 1615:                               &mreq, sizeof(mreq)) == -1) {
1.57      christos 1616:                        logit(LOG_ERR,
1.56      christos 1617:                               "%s: IPV6_JOIN_GROUP(site) on %s: %m",
1.21      itojun   1618:                               __func__,
1.49      roy      1619:                               mcastif ? mcastif : ra->ifname);
1.54      christos 1620:                        exit(EXIT_FAILURE);
1.11      itojun   1621:                }
                   1622:        }
1.61      roy      1623:
1.1       itojun   1624:        /* initialize msghdr for receiving packets */
1.36      roy      1625:        rcviov[0].iov_base = answer;
1.1       itojun   1626:        rcviov[0].iov_len = sizeof(answer);
1.36      roy      1627:        rcvmhdr.msg_name = &rcvfrom;
1.30      rpaulo   1628:        rcvmhdr.msg_namelen = sizeof(rcvfrom);
1.1       itojun   1629:        rcvmhdr.msg_iov = rcviov;
                   1630:        rcvmhdr.msg_iovlen = 1;
1.36      roy      1631:        rcvmhdr.msg_control = rcvcmsgbuf;
1.6       itojun   1632:        rcvmhdr.msg_controllen = rcvcmsgbuflen;
1.1       itojun   1633:
                   1634:        /* initialize msghdr for sending packets */
                   1635:        sndmhdr.msg_namelen = sizeof(struct sockaddr_in6);
                   1636:        sndmhdr.msg_iov = sndiov;
                   1637:        sndmhdr.msg_iovlen = 1;
1.54      christos 1638:        sndmhdr.msg_control = sndcmsgbuf;
1.6       itojun   1639:        sndmhdr.msg_controllen = sndcmsgbuflen;
1.1       itojun   1640: }
                   1641:
                   1642: /* open a routing socket to watch the routing table */
                   1643: static void
1.36      roy      1644: rtsock_open(void)
1.1       itojun   1645: {
1.53      roy      1646: #ifdef RO_MSGFILTER
                   1647:        unsigned char msgfilter[] = {
                   1648:                RTM_ADD, RTM_DELETE,
                   1649:                RTM_NEWADDR, RTM_DELADDR,
                   1650: #ifdef RTM_IFANNOUNCE
                   1651:                RTM_IFANNOUNCE,
                   1652: #endif
                   1653:                RTM_IFINFO,
                   1654:        };
                   1655: #endif
                   1656:
1.56      christos 1657:        if ((rtsock = prog_socket(PF_ROUTE, SOCK_RAW, 0)) == -1) {
1.57      christos 1658:                logit(LOG_ERR, "%s: socket: %m", __func__);
1.54      christos 1659:                exit(EXIT_FAILURE);
1.1       itojun   1660:        }
1.53      roy      1661: #ifdef RO_MSGFILTER
                   1662:        if (setsockopt(rtsock, PF_ROUTE, RO_MSGFILTER,
                   1663:            &msgfilter, sizeof(msgfilter) == -1))
1.57      christos 1664:                logit(LOG_ERR, "%s: RO_MSGFILTER: %m", __func__);
1.53      roy      1665: #endif
1.1       itojun   1666: }
                   1667:
1.11      itojun   1668: struct rainfo *
1.36      roy      1669: if_indextorainfo(unsigned int idx)
1.1       itojun   1670: {
1.36      roy      1671:        struct rainfo *rai;
1.1       itojun   1672:
1.36      roy      1673:        TAILQ_FOREACH(rai, &ralist, next) {
1.35      lukem    1674:                if (rai->ifindex == idx)
1.54      christos 1675:                        return rai;
1.1       itojun   1676:        }
                   1677:
1.54      christos 1678:        return NULL;            /* search failed */
1.1       itojun   1679: }
                   1680:
1.39      roy      1681: struct rainfo *
1.65      roy      1682: ra_output(struct rainfo *rai, bool solicited)
1.1       itojun   1683: {
                   1684:        int i;
                   1685:        struct cmsghdr *cm;
                   1686:        struct in6_pktinfo *pi;
1.36      roy      1687:        struct soliciter *sol;
1.10      itojun   1688:
1.39      roy      1689:        if ((rai->ifflags & IFF_UP) == 0) {
1.57      christos 1690:                logit(LOG_DEBUG, "%s: %s is not up, skip sending RA",
1.39      roy      1691:                       __func__, rai->ifname);
                   1692:                return NULL;
1.10      itojun   1693:        }
1.1       itojun   1694:
1.39      roy      1695:        make_packet(rai);       /* XXX: inefficient */
1.11      itojun   1696:
1.36      roy      1697:        sndmhdr.msg_name = (void *)&sin6_linklocal_allnodes;
1.39      roy      1698:        sndmhdr.msg_iov[0].iov_base = (void *)rai->ra_data;
                   1699:        sndmhdr.msg_iov[0].iov_len = rai->ra_datalen;
1.1       itojun   1700:
1.65      roy      1701:        /* specify the outgoing interface */
1.1       itojun   1702:        cm = CMSG_FIRSTHDR(&sndmhdr);
                   1703:        cm->cmsg_level = IPPROTO_IPV6;
                   1704:        cm->cmsg_type = IPV6_PKTINFO;
                   1705:        cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
                   1706:        pi = (struct in6_pktinfo *)CMSG_DATA(cm);
                   1707:        memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr));       /*XXX*/
1.39      roy      1708:        pi->ipi6_ifindex = rai->ifindex;
1.1       itojun   1709:
1.57      christos 1710:        logit(LOG_DEBUG,
1.56      christos 1711:               "%s: send RA on %s, # of waitings = %d",
1.39      roy      1712:               __func__, rai->ifname, rai->waiting);
1.1       itojun   1713:
1.65      roy      1714:        if (solicited) {
                   1715:                /* unicast solicited RA's as per RFC 7772 */
                   1716:                while ((sol = TAILQ_FIRST(&rai->soliciter)) != NULL) {
                   1717:                        sndmhdr.msg_name = (void *)&sol->addr;
                   1718:                        i = prog_sendmsg(sock, &sndmhdr, 0);
                   1719:                        if (i < 0 || (size_t)i != rai->ra_datalen)  {
                   1720:                                if (i < 0) {
                   1721:                                        logit(LOG_ERR,
                   1722:                                            "%s: unicast sendmsg on %s: %m",
                   1723:                                            __func__, rai->ifname);
                   1724:                                }
                   1725:                        }
                   1726:                        TAILQ_REMOVE(&rai->soliciter, sol, next);
                   1727:                        free(sol);
                   1728:                }
                   1729:
                   1730:                /* reset waiting conter */
                   1731:                rai->waiting = 0;
                   1732:
                   1733:                /* disable timer */
                   1734:                rai->timer_sol->enabled = false;
                   1735:
                   1736:                return rai;
                   1737:        }
                   1738:
1.51      ozaki-r  1739:        i = prog_sendmsg(sock, &sndmhdr, 0);
1.39      roy      1740:        if (i < 0 || (size_t)i != rai->ra_datalen)  {
1.1       itojun   1741:                if (i < 0) {
1.57      christos 1742:                        logit(LOG_ERR, "%s: sendmsg on %s: %m",
1.49      roy      1743:                               __func__, rai->ifname);
1.1       itojun   1744:                }
                   1745:        }
                   1746:
1.39      roy      1747:        if (rai->leaving_adv > 0) {
                   1748:                if (--(rai->leaving_adv) == 0) {
1.41      roy      1749:                        /* leaving for ourself means we're shutting down */
                   1750:                        if (rai->leaving_for == rai) {
                   1751:                                TAILQ_REMOVE(&ralist, rai, next);
                   1752:                                free_rainfo(rai);
                   1753:                                return NULL;
                   1754:                        }
1.57      christos 1755:                        logit(LOG_DEBUG,
1.56      christos 1756:                               "%s: expired RA,"
1.39      roy      1757:                               " new config active for interface (%s)",
                   1758:                               __func__, rai->ifname);
                   1759:                        rai->leaving_for->timer = rtadvd_add_timer(ra_timeout,
                   1760:                            ra_timer_update,
                   1761:                            rai->leaving_for, rai->leaving_for);
1.65      roy      1762:                        ra_timer_set_short_delay(rai->leaving_for, rai->timer);
1.39      roy      1763:                        rai->leaving_for->leaving = NULL;
                   1764:                        free_rainfo(rai);
                   1765:                        return NULL;
                   1766:                }
                   1767:        }
                   1768:
1.1       itojun   1769:        /* update counter */
1.39      roy      1770:        if (rai->initcounter < MAX_INITIAL_RTR_ADVERTISEMENTS)
                   1771:                rai->initcounter++;
                   1772:        rai->raoutput++;
1.1       itojun   1773:
                   1774:        /* update timestamp */
1.51      ozaki-r  1775:        prog_clock_gettime(CLOCK_MONOTONIC, &rai->lastsent);
1.39      roy      1776:        return rai;
1.1       itojun   1777: }
                   1778:
1.65      roy      1779: /* process unsolicited RA timer */
1.30      rpaulo   1780: struct rtadvd_timer *
1.1       itojun   1781: ra_timeout(void *data)
                   1782: {
                   1783:        struct rainfo *rai = (struct rainfo *)data;
                   1784:
1.65      roy      1785:        logit(LOG_DEBUG,
                   1786:               "%s: unsolicited RA timer on %s is expired",
                   1787:               __func__, rai->ifname);
                   1788:
                   1789:        if (ra_output(rai, false))
                   1790:                return rai->timer;
                   1791:        return NULL;
                   1792: }
                   1793:
                   1794: /* process solicited RA timer */
                   1795: struct rtadvd_timer *
                   1796: ra_timeout_sol(void *data)
                   1797: {
                   1798:        struct rainfo *rai = (struct rainfo *)data;
1.1       itojun   1799:
1.57      christos 1800:        logit(LOG_DEBUG,
1.65      roy      1801:               "%s: solicited RA timer on %s is expired",
1.21      itojun   1802:               __func__, rai->ifname);
1.1       itojun   1803:
1.65      roy      1804:        if (ra_output(rai, true))
                   1805:                return rai->timer_sol;
1.39      roy      1806:        return NULL;
1.1       itojun   1807: }
                   1808:
                   1809: /* update RA timer */
                   1810: void
1.47      roy      1811: ra_timer_update(void *data, struct timespec *tm)
1.1       itojun   1812: {
                   1813:        struct rainfo *rai = (struct rainfo *)data;
                   1814:        long interval;
                   1815:
                   1816:        /*
                   1817:         * Whenever a multicast advertisement is sent from an interface,
                   1818:         * the timer is reset to a uniformly-distributed random value
                   1819:         * between the interface's configured MinRtrAdvInterval and
1.11      itojun   1820:         * MaxRtrAdvInterval (RFC2461 6.2.4).
1.1       itojun   1821:         */
1.39      roy      1822:        interval = rai->mininterval;
                   1823:        if (rai->mininterval != rai->maxinterval)
                   1824:                interval += arc4random() % (rai->maxinterval-rai->mininterval);
1.1       itojun   1825:
                   1826:        /*
                   1827:         * For the first few advertisements (up to
                   1828:         * MAX_INITIAL_RTR_ADVERTISEMENTS), if the randomly chosen interval
                   1829:         * is greater than MAX_INITIAL_RTR_ADVERT_INTERVAL, the timer
                   1830:         * SHOULD be set to MAX_INITIAL_RTR_ADVERT_INTERVAL instead.
                   1831:         * (RFC-2461 6.2.4)
                   1832:         */
                   1833:        if (rai->initcounter < MAX_INITIAL_RTR_ADVERTISEMENTS &&
                   1834:            interval > MAX_INITIAL_RTR_ADVERT_INTERVAL)
                   1835:                interval = MAX_INITIAL_RTR_ADVERT_INTERVAL;
                   1836:
                   1837:        tm->tv_sec = interval;
1.47      roy      1838:        tm->tv_nsec = 0;
1.1       itojun   1839:
1.57      christos 1840:        logit(LOG_DEBUG,
1.56      christos 1841:               "%s: RA timer on %s is set to %jd:%jd",
1.21      itojun   1842:               __func__, rai->ifname,
1.54      christos 1843:               (intmax_t)tm->tv_sec, (intmax_t)tm->tv_nsec);
1.1       itojun   1844: }
1.57      christos 1845:
                   1846: void
                   1847: logit(int level, const char *fmt, ...)
                   1848: {
                   1849:        va_list ap;
1.67      christos 1850:        char *buf;
1.57      christos 1851:
                   1852:        va_start(ap, fmt);
                   1853:        if (!Dflag) {
                   1854:                vsyslog(level, fmt, ap);
                   1855:                va_end(ap);
                   1856:                return;
                   1857:        }
                   1858:
1.68    ! christos 1859:        vfprintf(stderr, expandm(fmt, "\n", &buf), ap);
        !          1860:        free(buf);
1.57      christos 1861:        va_end(ap);
                   1862: }

CVSweb <webmaster@jp.NetBSD.org>