root/usr/src/cmd/cmd-inet/usr.lib/in.ripngd/main.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*      Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T     */
/*        All Rights Reserved   */

/*
 * Portions of this source code were derived from Berkeley 4.3 BSD
 * under license from the Regents of the University of California.
 */

#include "defs.h"

struct          sockaddr_in6 allrouters;
char            *control;
boolean_t       dopoison = _B_TRUE;     /* Do poison reverse */
int             iocsoc;
struct          timeval lastfullupdate; /* last time full table multicast */
struct          timeval lastmcast;      /* last time all/changes multicast */
int             max_poll_ifs = START_POLL_SIZE;
struct          rip6 *msg;
boolean_t       needupdate;             /* true if need update at nextmcast */
struct          timeval nextmcast;      /* time to wait before changes mcast */
struct          timeval now;            /* current idea of time */
char            *packet;
struct          pollfd *poll_ifs = NULL;
int             poll_ifs_num = 0;
int             rip6_port;
boolean_t       supplier = _B_TRUE;     /* process should supply updates */

struct          in6_addr allrouters_in6 = {
/* BEGIN CSTYLED */
    { 0xff, 0x2, 0x0, 0x0,
      0x0,  0x0, 0x0, 0x0,
      0x0,  0x0, 0x0, 0x0,
      0x0,  0x0, 0x0, 0x9 }
/* END CSTYLED */
};

static void     timevalsub(struct timeval *t1, struct timeval *t2);

static void
usage(char *fname)
{
        (void) fprintf(stderr,
            "usage: "
            "%s [ -P ] [ -p port ] [ -q ] [ -s ] [ -t ] [ -v ] [<logfile>]\n",
            fname);
        exit(EXIT_FAILURE);
}

int
main(int argc, char *argv[])
{
        int i, n;
        struct interface *ifp;
        int c;
        struct timeval waittime;
        int timeout;
        boolean_t daemon = _B_TRUE;     /* Fork off a detached daemon */
        FILE *pidfp;
        mode_t pidmode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); /* 0644 */

        rip6_port = htons(IPPORT_ROUTESERVER6);
        allrouters.sin6_family = AF_INET6;
        allrouters.sin6_port = rip6_port;
        allrouters.sin6_addr = allrouters_in6;

        while ((c = getopt(argc, argv, "nsqvTtdgPp:")) != EOF) {
                switch (c) {
                case 'n':
                        install = _B_FALSE;
                        break;
                case 's':
                        supplier = _B_TRUE;
                        break;
                case 'q':
                        supplier = _B_FALSE;
                        break;
                case 'v':
                        tracing |= ACTION_BIT;
                        break;
                case 'T':
                        daemon = _B_FALSE;
                        break;
                case 't':
                        tracepackets = _B_TRUE;
                        daemon = _B_FALSE;
                        tracing |= (INPUT_BIT | OUTPUT_BIT);
                        break;
                case 'd':
                        break;
                case 'P':
                        dopoison = _B_FALSE;
                        break;
                case 'p':
                        rip6_port = htons(atoi(optarg));
                        allrouters.sin6_port = rip6_port;
                        break;
                default:
                        usage(argv[0]);
                        /* NOTREACHED */
                }
        }

        /*
         * Any extra argument is considered
         * a tracing log file.
         */
        if (optind < argc) {
                traceon(argv[optind]);
        } else if (tracing && !daemon) {
                traceonfp(stdout);
        } else if (tracing) {
                (void) fprintf(stderr, "Need logfile with -v\n");
                usage(argv[0]);
                /* NOTREACHED */
        }

        if (daemon) {
                int t;

                if (fork())
                        exit(EXIT_SUCCESS);
                for (t = 0; t < 20; t++) {
                        if (!tracing || (t != fileno(ftrace)))
                                (void) close(t);
                }
                (void) open("/", 0);
                (void) dup2(0, 1);
                (void) dup2(0, 2);
                (void) setsid();
        }

        /* Store our process id, blow away any existing file if it exists. */
        if ((pidfp = fopen(PATH_PID, "w")) == NULL) {
                (void) fprintf(stderr, "%s: unable to open " PATH_PID ": %s\n",
                    argv[0], strerror(errno));
        } else {
                (void) fprintf(pidfp, "%ld\n", getpid());
                (void) fclose(pidfp);
                (void) chmod(PATH_PID, pidmode);
        }


        iocsoc = socket(AF_INET6, SOCK_DGRAM, 0);
        if (iocsoc < 0) {
                syslog(LOG_ERR, "main: socket: %m");
                exit(EXIT_FAILURE);
        }

        setup_rtsock();

        /*
         * Allocate the buffer to hold the RIPng packet.  In reality, it will be
         * smaller than IPV6_MAX_PACKET octets due to (at least) the IPv6 and
         * UDP headers but IPV6_MAX_PACKET is a convenient size.
         */
        packet = (char *)malloc(IPV6_MAX_PACKET);
        if (packet == NULL) {
                syslog(LOG_ERR, "main: malloc: %m");
                exit(EXIT_FAILURE);
        }
        msg = (struct rip6 *)packet;

        /*
         * Allocate the buffer to hold the ancillary data.  This data is used to
         * insure that the incoming hop count of a RIPCMD6_RESPONSE message is
         * IPV6_MAX_HOPS which indicates that it came from a direct neighbor
         * (namely, no intervening router decremented it).
         */
        control = (char *)malloc(IPV6_MAX_PACKET);
        if (control == NULL) {
                syslog(LOG_ERR, "main: malloc: %m");
                exit(EXIT_FAILURE);
        }

        openlog("in.ripngd", LOG_PID | LOG_CONS, LOG_DAEMON);

        (void) gettimeofday(&now, (struct timezone *)NULL);

        initifs();
        solicitall(&allrouters);

        if (supplier)
                supplyall(&allrouters, 0, (struct interface *)NULL, _B_TRUE);

        (void) sigset(SIGALRM, (void (*)(int))timer);
        (void) sigset(SIGHUP, (void (*)(int))initifs);
        (void) sigset(SIGTERM, (void (*)(int))term);
        (void) sigset(SIGUSR1, (void (*)(int))if_dump);
        (void) sigset(SIGUSR2, (void (*)(int))rtdump);

        /*
         * Seed the pseudo-random number generator for GET_RANDOM().
         */
        srandom((uint_t)gethostid());

        timer();

        for (;;) {
                if (needupdate) {
                        waittime = nextmcast;
                        timevalsub(&waittime, &now);
                        if (waittime.tv_sec < 0) {
                                timeout = 0;
                        } else {
                                timeout = TIME_TO_MSECS(waittime);
                        }
                        if (tracing & ACTION_BIT) {
                                (void) fprintf(ftrace,
                                    "poll until dynamic update in %d msec\n",
                                    timeout);
                                (void) fflush(ftrace);
                        }
                } else {
                        timeout = INFTIM;
                }

                if ((n = poll(poll_ifs, poll_ifs_num, timeout)) < 0) {
                        if (errno == EINTR)
                                continue;
                        syslog(LOG_ERR, "main: poll: %m");
                        exit(EXIT_FAILURE);
                }
                (void) sighold(SIGALRM);
                (void) sighold(SIGHUP);
                /*
                 * Poll timed out.
                 */
                if (n == 0) {
                        if (needupdate) {
                                TRACE_ACTION("send delayed dynamic update",
                                    (struct rt_entry *)NULL);
                                (void) gettimeofday(&now,
                                    (struct timezone *)NULL);
                                supplyall(&allrouters, RTS_CHANGED,
                                    (struct interface *)NULL, _B_TRUE);
                                lastmcast = now;
                                needupdate = _B_FALSE;
                                nextmcast.tv_sec = 0;
                        }
                        (void) sigrelse(SIGHUP);
                        (void) sigrelse(SIGALRM);
                        continue;
                }
                (void) gettimeofday(&now, (struct timezone *)NULL);
                for (i = 0; i < poll_ifs_num; i++) {
                        /*
                         * This case should never happen.
                         */
                        if (poll_ifs[i].revents & POLLERR) {
                                syslog(LOG_ERR,
                                    "main: poll returned a POLLERR event");
                                continue;
                        }
                        if (poll_ifs[i].revents & POLLIN) {
                                for (ifp = ifnet; ifp != NULL;
                                    ifp = ifp->int_next) {
                                        if (poll_ifs[i].fd == ifp->int_sock)
                                                in_data(ifp);
                                }
                        }
                }
                (void) sigrelse(SIGHUP);
                (void) sigrelse(SIGALRM);
        }

        return (0);
}

void
timevaladd(struct timeval *t1, struct timeval *t2)
{
        t1->tv_sec += t2->tv_sec;
        if ((t1->tv_usec += t2->tv_usec) > 1000000) {
                t1->tv_sec++;
                t1->tv_usec -= 1000000;
        }
}

void
timevalsub(struct timeval *t1, struct timeval *t2)
{
        t1->tv_sec -= t2->tv_sec;
        if ((t1->tv_usec -= t2->tv_usec) < 0) {
                t1->tv_sec--;
                t1->tv_usec += 1000000;
        }
}