rtsold.c revision 118664
178064Sume/* $KAME: rtsold.c,v 1.31 2001/05/22 06:03:06 jinmei Exp $ */ 266776Skris 355163Sshin/* 455163Sshin * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 555163Sshin * All rights reserved. 662632Skris * 755163Sshin * Redistribution and use in source and binary forms, with or without 855163Sshin * modification, are permitted provided that the following conditions 955163Sshin * are met: 1055163Sshin * 1. Redistributions of source code must retain the above copyright 1155163Sshin * notice, this list of conditions and the following disclaimer. 1255163Sshin * 2. Redistributions in binary form must reproduce the above copyright 1355163Sshin * notice, this list of conditions and the following disclaimer in the 1455163Sshin * documentation and/or other materials provided with the distribution. 1555163Sshin * 3. Neither the name of the project nor the names of its contributors 1655163Sshin * may be used to endorse or promote products derived from this software 1755163Sshin * without specific prior written permission. 1862632Skris * 1955163Sshin * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 2055163Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2155163Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2255163Sshin * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 2355163Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2455163Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2555163Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2655163Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2755163Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2855163Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2955163Sshin * SUCH DAMAGE. 3055163Sshin * 3155163Sshin * $FreeBSD: head/usr.sbin/rtsold/rtsold.c 118664 2003-08-08 16:56:01Z ume $ 3255163Sshin */ 3355163Sshin 3455163Sshin#include <sys/types.h> 3555163Sshin#include <sys/time.h> 3662632Skris#include <sys/socket.h> 3755163Sshin 3862632Skris#include <net/if.h> 3955163Sshin#include <net/if_dl.h> 4055163Sshin 4155163Sshin#include <netinet/in.h> 4255163Sshin#include <netinet/icmp6.h> 4355163Sshin 4455163Sshin#include <signal.h> 4555163Sshin#include <unistd.h> 4655163Sshin#include <syslog.h> 4755163Sshin#include <string.h> 4855163Sshin#include <stdlib.h> 4955163Sshin#include <stdio.h> 5055163Sshin#include <errno.h> 5155163Sshin#include <err.h> 5255163Sshin#include <stdarg.h> 5366776Skris#include <ifaddrs.h> 54118664Sume 5555163Sshin#include "rtsold.h" 5655163Sshin 5755163Sshinstruct ifinfo *iflist; 5855163Sshinstruct timeval tm_max = {0x7fffffff, 0x7fffffff}; 59118664Sumestatic int log_upto = 999; 60118664Sumestatic int fflag = 0; 61118664Sume 6266776Skrisint aflag = 0; 6366776Skrisint dflag = 0; 64118664Sume 65118661Sumechar *otherconf_script; 6655163Sshin 6755163Sshin/* protocol constatns */ 6862632Skris#define MAX_RTR_SOLICITATION_DELAY 1 /* second */ 6962632Skris#define RTR_SOLICITATION_INTERVAL 4 /* seconds */ 7062632Skris#define MAX_RTR_SOLICITATIONS 3 /* times */ 7155163Sshin 72118664Sume/* 73118664Sume * implementation dependent constants in secondes 74118664Sume * XXX: should be configurable 75118664Sume */ 76118664Sume#define PROBE_INTERVAL 60 7755163Sshin 7855163Sshin/* utility macros */ 7955163Sshin/* a < b */ 8062632Skris#define TIMEVAL_LT(a, b) (((a).tv_sec < (b).tv_sec) ||\ 8155163Sshin (((a).tv_sec == (b).tv_sec) && \ 8255163Sshin ((a).tv_usec < (b).tv_usec))) 8355163Sshin 8455163Sshin/* a <= b */ 8562632Skris#define TIMEVAL_LEQ(a, b) (((a).tv_sec < (b).tv_sec) ||\ 8655163Sshin (((a).tv_sec == (b).tv_sec) &&\ 87118664Sume ((a).tv_usec <= (b).tv_usec))) 8855163Sshin 8955163Sshin/* a == b */ 9062632Skris#define TIMEVAL_EQ(a, b) (((a).tv_sec==(b).tv_sec) && ((a).tv_usec==(b).tv_usec)) 9155163Sshin 92118664Sumeint main __P((int, char **)); 9355163Sshin 9455163Sshin/* static variables and functions */ 9555163Sshinstatic int mobile_node = 0; 9655163Sshinstatic int do_dump; 9762632Skrisstatic char *dumpfilename = "/var/run/rtsold.dump"; /* XXX: should be configurable */ 9855163Sshinstatic char *pidfilename = "/var/run/rtsold.pid"; /* should be configurable */ 9955163Sshin 100118664Sumestatic int ifconfig __P((char *)); 10162632Skris#if 0 102118664Sumestatic int ifreconfig __P((char *)); 10362632Skris#endif 104118664Sumestatic int make_packet __P((struct ifinfo *)); 10555163Sshinstatic struct timeval *rtsol_check_timer __P((void)); 106118664Sumestatic void TIMEVAL_ADD __P((struct timeval *, struct timeval *, 107118664Sume struct timeval *)); 108118664Sumestatic void TIMEVAL_SUB __P((struct timeval *, struct timeval *, 109118664Sume struct timeval *)); 11055163Sshin 11162632Skrisstatic void rtsold_set_dump_file __P((void)); 112118664Sumestatic void usage __P((char *)); 11366776Skrisstatic char **autoifprobe __P((void)); 11455163Sshin 11555163Sshinint 11655163Sshinmain(argc, argv) 11755163Sshin int argc; 118118664Sume char **argv; 11955163Sshin{ 12078064Sume int s, rtsock, maxfd, ch; 12155163Sshin int once = 0; 12255163Sshin struct timeval *timeout; 12355163Sshin struct fd_set fdset; 124118664Sume char *argv0, *opts; 12555163Sshin 12655163Sshin /* 12755163Sshin * Initialization 12855163Sshin */ 12955163Sshin argv0 = argv[0]; 13055163Sshin 13155163Sshin /* get option */ 13255163Sshin if (argv0 && argv0[strlen(argv0) - 1] != 'd') { 13355163Sshin fflag = 1; 13455163Sshin once = 1; 135118661Sume opts = "adDO:"; 13655163Sshin } else 137118661Sume opts = "adDfm1O:"; 13855163Sshin 13955163Sshin while ((ch = getopt(argc, argv, opts)) != -1) { 14066776Skris switch (ch) { 14166776Skris case 'a': 14266776Skris aflag = 1; 14366776Skris break; 14466776Skris case 'd': 14566776Skris dflag = 1; 14666776Skris break; 14766776Skris case 'D': 14866776Skris dflag = 2; 14966776Skris break; 15066776Skris case 'f': 15166776Skris fflag = 1; 15266776Skris break; 15366776Skris case 'm': 15466776Skris mobile_node = 1; 15566776Skris break; 15666776Skris case '1': 15766776Skris once = 1; 15866776Skris break; 159118661Sume case 'O': 160118661Sume otherconf_script = optarg; 161118661Sume break; 16266776Skris default: 16366776Skris usage(argv0); 16466776Skris /*NOTREACHED*/ 16555163Sshin } 16655163Sshin } 16755163Sshin argc -= optind; 16855163Sshin argv += optind; 16966776Skris 17066776Skris if (aflag) { 17166776Skris int i; 17266776Skris 17366776Skris if (argc != 0) { 17466776Skris usage(argv0); 17566776Skris /*NOTREACHED*/ 17666776Skris } 17766776Skris 17866776Skris argv = autoifprobe(); 17966776Skris if (!argv) { 18066776Skris errx(1, "could not autoprobe interface"); 18166776Skris /*NOTREACHED*/ 18266776Skris } 18366776Skris 18466776Skris for (i = 0; argv[i]; i++) 18566776Skris ; 18666776Skris argc = i; 18766776Skris } 18866776Skris if (argc == 0) { 18955163Sshin usage(argv0); 19066776Skris /*NOTREACHED*/ 19166776Skris } 19255163Sshin 19355163Sshin /* set log level */ 19455163Sshin if (dflag == 0) 19555163Sshin log_upto = LOG_NOTICE; 19655163Sshin if (!fflag) { 19755163Sshin char *ident; 198118664Sume 19955163Sshin ident = strrchr(argv0, '/'); 20055163Sshin if (!ident) 20155163Sshin ident = argv0; 20255163Sshin else 20355163Sshin ident++; 20455163Sshin openlog(ident, LOG_NDELAY|LOG_PID, LOG_DAEMON); 20555163Sshin if (log_upto >= 0) 20655163Sshin setlogmask(LOG_UPTO(log_upto)); 20755163Sshin } 20855163Sshin 209118661Sume if (otherconf_script && *otherconf_script != '/') { 210118661Sume errx(1, "configuration script (%s) must be an absolute path", 211118661Sume otherconf_script); 212118661Sume } 213118661Sume 21462632Skris#ifndef HAVE_ARC4RANDOM 215118664Sume /* random value initialization */ 21655163Sshin srandom((u_long)time(NULL)); 21762632Skris#endif 21855163Sshin 21955163Sshin /* warn if accept_rtadv is down */ 22055163Sshin if (!getinet6sysctl(IPV6CTL_ACCEPT_RTADV)) 22155163Sshin warnx("kernel is configured not to accept RAs"); 22266776Skris /* warn if forwarding is up */ 22366776Skris if (getinet6sysctl(IPV6CTL_FORWARDING)) 22466776Skris warnx("kernel is configured as a router, not a host"); 22555163Sshin 22655163Sshin /* initialization to dump internal status to a file */ 22766776Skris if (signal(SIGUSR1, (void *)rtsold_set_dump_file) < 0) { 22855163Sshin errx(1, "failed to set signal for dump status"); 22966776Skris /*NOTREACHED*/ 23066776Skris } 23155163Sshin 23262632Skris /* 23362632Skris * Open a socket for sending RS and receiving RA. 23462632Skris * This should be done before calling ifinit(), since the function 23562632Skris * uses the socket. 23662632Skris */ 23766776Skris if ((s = sockopen()) < 0) { 23862632Skris errx(1, "failed to open a socket"); 23966776Skris /*NOTREACHED*/ 24066776Skris } 24178064Sume maxfd = s; 24278064Sume if ((rtsock = rtsock_open()) < 0) { 24378064Sume errx(1, "failed to open a socket"); 24478064Sume /*NOTREACHED*/ 24578064Sume } 24678064Sume if (rtsock > maxfd) 24778064Sume maxfd = rtsock; 24862632Skris 24955163Sshin /* configuration per interface */ 25066776Skris if (ifinit()) { 25155163Sshin errx(1, "failed to initilizatoin interfaces"); 25266776Skris /*NOTREACHED*/ 25366776Skris } 25455163Sshin while (argc--) { 25566776Skris if (ifconfig(*argv)) { 25662632Skris errx(1, "failed to initialize %s", *argv); 25766776Skris /*NOTREACHED*/ 25866776Skris } 25955163Sshin argv++; 26055163Sshin } 26155163Sshin 26255163Sshin /* setup for probing default routers */ 26366776Skris if (probe_init()) { 26455163Sshin errx(1, "failed to setup for probing routers"); 26566776Skris /*NOTREACHED*/ 26666776Skris } 26755163Sshin 26855163Sshin if (!fflag) 26955163Sshin daemon(0, 0); /* act as a daemon */ 27055163Sshin 27155163Sshin /* dump the current pid */ 27255163Sshin if (!once) { 27355163Sshin pid_t pid = getpid(); 27455163Sshin FILE *fp; 27555163Sshin 27655163Sshin if ((fp = fopen(pidfilename, "w")) == NULL) 277118660Sume warnmsg(LOG_ERR, __func__, 278118664Sume "failed to open a pid log file(%s): %s", 279118664Sume pidfilename, strerror(errno)); 28055163Sshin else { 28155163Sshin fprintf(fp, "%d\n", pid); 28255163Sshin fclose(fp); 28355163Sshin } 28455163Sshin } 28555163Sshin 28655163Sshin FD_ZERO(&fdset); 28755163Sshin FD_SET(s, &fdset); 28878064Sume FD_SET(rtsock, &fdset); 28955163Sshin while (1) { /* main loop */ 29055163Sshin int e; 29155163Sshin struct fd_set select_fd = fdset; 29255163Sshin 29355163Sshin if (do_dump) { /* SIGUSR1 */ 29455163Sshin do_dump = 0; 29555163Sshin rtsold_dump_file(dumpfilename); 29655163Sshin } 297118664Sume 29855163Sshin timeout = rtsol_check_timer(); 29955163Sshin 30055163Sshin if (once) { 30155163Sshin struct ifinfo *ifi; 30255163Sshin 30355163Sshin /* if we have no timeout, we are done (or failed) */ 30455163Sshin if (timeout == NULL) 30555163Sshin break; 30655163Sshin 30755163Sshin /* if all interfaces have got RA packet, we are done */ 30855163Sshin for (ifi = iflist; ifi; ifi = ifi->next) { 30955163Sshin if (ifi->state != IFS_DOWN && ifi->racnt == 0) 31055163Sshin break; 31155163Sshin } 31255163Sshin if (ifi == NULL) 31355163Sshin break; 31455163Sshin } 31578064Sume e = select(maxfd + 1, &select_fd, NULL, NULL, timeout); 31678064Sume if (e < 1) { 31755163Sshin if (e < 0 && errno != EINTR) { 318118660Sume warnmsg(LOG_ERR, __func__, "select: %s", 319118664Sume strerror(errno)); 32055163Sshin } 32155163Sshin continue; 32255163Sshin } 32355163Sshin 32455163Sshin /* packet reception */ 32578064Sume if (FD_ISSET(rtsock, &select_fd)) 32678064Sume rtsock_input(rtsock); 32778064Sume if (FD_ISSET(s, &select_fd)) 32855163Sshin rtsol_input(s); 32955163Sshin } 33055163Sshin /* NOTREACHED */ 33155163Sshin 33255163Sshin return 0; 33355163Sshin} 33455163Sshin 33555163Sshinstatic int 33655163Sshinifconfig(char *ifname) 33755163Sshin{ 33855163Sshin struct ifinfo *ifinfo; 33955163Sshin struct sockaddr_dl *sdl; 34055163Sshin int flags; 34155163Sshin 34255163Sshin if ((sdl = if_nametosdl(ifname)) == NULL) { 343118660Sume warnmsg(LOG_ERR, __func__, 344118664Sume "failed to get link layer information for %s", ifname); 34555163Sshin return(-1); 34655163Sshin } 34755163Sshin if (find_ifinfo(sdl->sdl_index)) { 348118660Sume warnmsg(LOG_ERR, __func__, 349118664Sume "interface %s was already configured", ifname); 35062632Skris free(sdl); 35155163Sshin return(-1); 35255163Sshin } 35355163Sshin 35455163Sshin if ((ifinfo = malloc(sizeof(*ifinfo))) == NULL) { 355118660Sume warnmsg(LOG_ERR, __func__, "memory allocation failed"); 35662632Skris free(sdl); 35755163Sshin return(-1); 35855163Sshin } 35955163Sshin memset(ifinfo, 0, sizeof(*ifinfo)); 36055163Sshin ifinfo->sdl = sdl; 36155163Sshin 36255163Sshin strncpy(ifinfo->ifname, ifname, sizeof(ifinfo->ifname)); 36355163Sshin 36455163Sshin /* construct a router solicitation message */ 36555163Sshin if (make_packet(ifinfo)) 36655163Sshin goto bad; 36755163Sshin 36855163Sshin /* 36955163Sshin * check if the interface is available. 37055163Sshin * also check if SIOCGIFMEDIA ioctl is OK on the interface. 37155163Sshin */ 37255163Sshin ifinfo->mediareqok = 1; 37355163Sshin ifinfo->active = interface_status(ifinfo); 37455163Sshin if (!ifinfo->mediareqok) { 37555163Sshin /* 37655163Sshin * probe routers periodically even if the link status 37755163Sshin * does not change. 37855163Sshin */ 37955163Sshin ifinfo->probeinterval = PROBE_INTERVAL; 38055163Sshin } 38155163Sshin 38255163Sshin /* activate interface: interface_up returns 0 on success */ 38355163Sshin flags = interface_up(ifinfo->ifname); 38455163Sshin if (flags == 0) 38555163Sshin ifinfo->state = IFS_DELAY; 38655163Sshin else if (flags == IFS_TENTATIVE) 38755163Sshin ifinfo->state = IFS_TENTATIVE; 38855163Sshin else 38955163Sshin ifinfo->state = IFS_DOWN; 39055163Sshin 39155163Sshin rtsol_timer_update(ifinfo); 39255163Sshin 39355163Sshin /* link into chain */ 39455163Sshin if (iflist) 39555163Sshin ifinfo->next = iflist; 39655163Sshin iflist = ifinfo; 39755163Sshin 39855163Sshin return(0); 39955163Sshin 400118664Sumebad: 40162632Skris free(ifinfo->sdl); 40255163Sshin free(ifinfo); 40355163Sshin return(-1); 40455163Sshin} 40555163Sshin 40662632Skris#if 0 40762632Skrisstatic int 40862632Skrisifreconfig(char *ifname) 40962632Skris{ 41062632Skris struct ifinfo *ifi, *prev; 41162632Skris int rv; 41262632Skris 41362632Skris prev = NULL; 41462632Skris for (ifi = iflist; ifi; ifi = ifi->next) { 41562632Skris if (strncmp(ifi->ifname, ifname, sizeof(ifi->ifname)) == 0) 41662632Skris break; 41762632Skris prev = ifi; 41862632Skris } 41962632Skris prev->next = ifi->next; 42062632Skris 42162632Skris rv = ifconfig(ifname); 42262632Skris 42362632Skris /* reclaim it after ifconfig() in case ifname is pointer inside ifi */ 42462632Skris if (ifi->rs_data) 42562632Skris free(ifi->rs_data); 42662632Skris free(ifi->sdl); 42762632Skris free(ifi); 42862632Skris return rv; 42962632Skris} 43062632Skris#endif 43162632Skris 43255163Sshinstruct ifinfo * 43355163Sshinfind_ifinfo(int ifindex) 43455163Sshin{ 43555163Sshin struct ifinfo *ifi; 43655163Sshin 43755163Sshin for (ifi = iflist; ifi; ifi = ifi->next) 43855163Sshin if (ifi->sdl->sdl_index == ifindex) 43955163Sshin return(ifi); 44055163Sshin return(NULL); 44155163Sshin} 44255163Sshin 44355163Sshinstatic int 44455163Sshinmake_packet(struct ifinfo *ifinfo) 44555163Sshin{ 446118664Sume size_t packlen = sizeof(struct nd_router_solicit), lladdroptlen = 0; 447118664Sume struct nd_router_solicit *rs; 44855163Sshin char *buf; 44955163Sshin 45055163Sshin if ((lladdroptlen = lladdropt_length(ifinfo->sdl)) == 0) { 451118660Sume warnmsg(LOG_INFO, __func__, 452118664Sume "link-layer address option has null length" 453118664Sume " on %s. Treat as not included.", ifinfo->ifname); 45455163Sshin } 45555163Sshin packlen += lladdroptlen; 45655163Sshin ifinfo->rs_datalen = packlen; 45755163Sshin 45855163Sshin /* allocate buffer */ 45955163Sshin if ((buf = malloc(packlen)) == NULL) { 460118660Sume warnmsg(LOG_ERR, __func__, 461118664Sume "memory allocation failed for %s", ifinfo->ifname); 46255163Sshin return(-1); 46355163Sshin } 46455163Sshin ifinfo->rs_data = buf; 46555163Sshin 46655163Sshin /* fill in the message */ 46755163Sshin rs = (struct nd_router_solicit *)buf; 46855163Sshin rs->nd_rs_type = ND_ROUTER_SOLICIT; 46955163Sshin rs->nd_rs_code = 0; 47055163Sshin rs->nd_rs_cksum = 0; 47155163Sshin rs->nd_rs_reserved = 0; 47255163Sshin buf += sizeof(*rs); 47355163Sshin 47455163Sshin /* fill in source link-layer address option */ 47555163Sshin if (lladdroptlen) 47655163Sshin lladdropt_fill(ifinfo->sdl, (struct nd_opt_hdr *)buf); 47755163Sshin 47855163Sshin return(0); 47955163Sshin} 48055163Sshin 48155163Sshinstatic struct timeval * 48255163Sshinrtsol_check_timer() 48355163Sshin{ 48455163Sshin static struct timeval returnval; 48555163Sshin struct timeval now, rtsol_timer; 48655163Sshin struct ifinfo *ifinfo; 48755163Sshin int flags; 48855163Sshin 48955163Sshin gettimeofday(&now, NULL); 49055163Sshin 49155163Sshin rtsol_timer = tm_max; 49255163Sshin 49355163Sshin for (ifinfo = iflist; ifinfo; ifinfo = ifinfo->next) { 49455163Sshin if (TIMEVAL_LEQ(ifinfo->expire, now)) { 49555163Sshin if (dflag > 1) 496118660Sume warnmsg(LOG_DEBUG, __func__, 497118664Sume "timer expiration on %s, " 498118664Sume "state = %d", ifinfo->ifname, 499118664Sume ifinfo->state); 50055163Sshin 50166776Skris switch (ifinfo->state) { 50255163Sshin case IFS_DOWN: 50355163Sshin case IFS_TENTATIVE: 50455163Sshin /* interface_up returns 0 on success */ 50555163Sshin flags = interface_up(ifinfo->ifname); 50655163Sshin if (flags == 0) 50755163Sshin ifinfo->state = IFS_DELAY; 50855163Sshin else if (flags == IFS_TENTATIVE) 50955163Sshin ifinfo->state = IFS_TENTATIVE; 51055163Sshin else 51155163Sshin ifinfo->state = IFS_DOWN; 51255163Sshin break; 51355163Sshin case IFS_IDLE: 51455163Sshin { 51555163Sshin int oldstatus = ifinfo->active; 51655163Sshin int probe = 0; 51755163Sshin 518118664Sume ifinfo->active = interface_status(ifinfo); 51955163Sshin 52055163Sshin if (oldstatus != ifinfo->active) { 521118660Sume warnmsg(LOG_DEBUG, __func__, 522118664Sume "%s status is changed" 523118664Sume " from %d to %d", 524118664Sume ifinfo->ifname, 525118664Sume oldstatus, ifinfo->active); 52655163Sshin probe = 1; 52755163Sshin ifinfo->state = IFS_DELAY; 528118664Sume } else if (ifinfo->probeinterval && 529118664Sume (ifinfo->probetimer -= 530118664Sume ifinfo->timer.tv_sec) <= 0) { 53155163Sshin /* probe timer expired */ 53255163Sshin ifinfo->probetimer = 533118664Sume ifinfo->probeinterval; 53455163Sshin probe = 1; 53555163Sshin ifinfo->state = IFS_PROBE; 53655163Sshin } 53755163Sshin 538118661Sume /* 539118661Sume * If we need a probe, clear the previous 540118661Sume * status wrt the "other" configuration. 541118661Sume */ 542118661Sume if (probe) 543118661Sume ifinfo->otherconfig = 0; 544118661Sume 54555163Sshin if (probe && mobile_node) 54655163Sshin defrouter_probe(ifinfo->sdl->sdl_index); 54755163Sshin break; 54855163Sshin } 54955163Sshin case IFS_DELAY: 55055163Sshin ifinfo->state = IFS_PROBE; 55155163Sshin sendpacket(ifinfo); 55255163Sshin break; 55355163Sshin case IFS_PROBE: 55455163Sshin if (ifinfo->probes < MAX_RTR_SOLICITATIONS) 55555163Sshin sendpacket(ifinfo); 55655163Sshin else { 557118660Sume warnmsg(LOG_INFO, __func__, 558118664Sume "No answer after sending %d RSs", 559118664Sume ifinfo->probes); 56055163Sshin ifinfo->probes = 0; 56155163Sshin ifinfo->state = IFS_IDLE; 56255163Sshin } 56355163Sshin break; 56455163Sshin } 56555163Sshin rtsol_timer_update(ifinfo); 56655163Sshin } 56755163Sshin 56855163Sshin if (TIMEVAL_LT(ifinfo->expire, rtsol_timer)) 56955163Sshin rtsol_timer = ifinfo->expire; 57055163Sshin } 57155163Sshin 57255163Sshin if (TIMEVAL_EQ(rtsol_timer, tm_max)) { 573118660Sume warnmsg(LOG_DEBUG, __func__, "there is no timer"); 57455163Sshin return(NULL); 575118664Sume } else if (TIMEVAL_LT(rtsol_timer, now)) 57655163Sshin /* this may occur when the interval is too small */ 57755163Sshin returnval.tv_sec = returnval.tv_usec = 0; 57855163Sshin else 57955163Sshin TIMEVAL_SUB(&rtsol_timer, &now, &returnval); 58055163Sshin 58155163Sshin if (dflag > 1) 582118660Sume warnmsg(LOG_DEBUG, __func__, "New timer is %ld:%08ld", 583118664Sume (long)returnval.tv_sec, (long)returnval.tv_usec); 58455163Sshin 58555163Sshin return(&returnval); 58655163Sshin} 58755163Sshin 58855163Sshinvoid 58955163Sshinrtsol_timer_update(struct ifinfo *ifinfo) 59055163Sshin{ 59162632Skris#define MILLION 1000000 59262632Skris#define DADRETRY 10 /* XXX: adhoc */ 59355163Sshin long interval; 59455163Sshin struct timeval now; 59555163Sshin 59655163Sshin bzero(&ifinfo->timer, sizeof(ifinfo->timer)); 59755163Sshin 59855163Sshin switch (ifinfo->state) { 59955163Sshin case IFS_DOWN: 60055163Sshin case IFS_TENTATIVE: 60155163Sshin if (++ifinfo->dadcount > DADRETRY) { 60255163Sshin ifinfo->dadcount = 0; 60355163Sshin ifinfo->timer.tv_sec = PROBE_INTERVAL; 604118664Sume } else 60555163Sshin ifinfo->timer.tv_sec = 1; 60655163Sshin break; 60755163Sshin case IFS_IDLE: 60855163Sshin if (mobile_node) { 609118664Sume /* XXX should be configurable */ 61055163Sshin ifinfo->timer.tv_sec = 3; 61155163Sshin } 61255163Sshin else 61355163Sshin ifinfo->timer = tm_max; /* stop timer(valid?) */ 61455163Sshin break; 61555163Sshin case IFS_DELAY: 61662632Skris#ifndef HAVE_ARC4RANDOM 61755163Sshin interval = random() % (MAX_RTR_SOLICITATION_DELAY * MILLION); 61862632Skris#else 61962632Skris interval = arc4random() % (MAX_RTR_SOLICITATION_DELAY * MILLION); 62062632Skris#endif 62155163Sshin ifinfo->timer.tv_sec = interval / MILLION; 62255163Sshin ifinfo->timer.tv_usec = interval % MILLION; 62355163Sshin break; 62455163Sshin case IFS_PROBE: 62578064Sume if (ifinfo->probes < MAX_RTR_SOLICITATIONS) 62678064Sume ifinfo->timer.tv_sec = RTR_SOLICITATION_INTERVAL; 62778064Sume else { 62878064Sume /* 62978064Sume * After sending MAX_RTR_SOLICITATIONS solicitations, 63078064Sume * we're just waiting for possible replies; there 63178064Sume * will be no more solicatation. Thus, we change 63278064Sume * the timer value to MAX_RTR_SOLICITATION_DELAY based 63378064Sume * on RFC 2461, Section 6.3.7. 63478064Sume */ 63578064Sume ifinfo->timer.tv_sec = MAX_RTR_SOLICITATION_DELAY; 63678064Sume } 63755163Sshin break; 63855163Sshin default: 639118660Sume warnmsg(LOG_ERR, __func__, 640118664Sume "illegal interface state(%d) on %s", 641118664Sume ifinfo->state, ifinfo->ifname); 64255163Sshin return; 64355163Sshin } 64455163Sshin 64555163Sshin /* reset the timer */ 64655163Sshin if (TIMEVAL_EQ(ifinfo->timer, tm_max)) { 64755163Sshin ifinfo->expire = tm_max; 648118660Sume warnmsg(LOG_DEBUG, __func__, 649118664Sume "stop timer for %s", ifinfo->ifname); 650118664Sume } else { 65155163Sshin gettimeofday(&now, NULL); 65255163Sshin TIMEVAL_ADD(&now, &ifinfo->timer, &ifinfo->expire); 65355163Sshin 65455163Sshin if (dflag > 1) 655118660Sume warnmsg(LOG_DEBUG, __func__, 656118664Sume "set timer for %s to %d:%d", ifinfo->ifname, 657118664Sume (int)ifinfo->timer.tv_sec, 658118664Sume (int)ifinfo->timer.tv_usec); 65955163Sshin } 66055163Sshin 66155163Sshin#undef MILLION 66255163Sshin} 66355163Sshin 66455163Sshin/* timer related utility functions */ 66562632Skris#define MILLION 1000000 66655163Sshin 66755163Sshin/* result = a + b */ 66855163Sshinstatic void 66955163SshinTIMEVAL_ADD(struct timeval *a, struct timeval *b, struct timeval *result) 67055163Sshin{ 67155163Sshin long l; 67255163Sshin 67355163Sshin if ((l = a->tv_usec + b->tv_usec) < MILLION) { 67455163Sshin result->tv_usec = l; 67555163Sshin result->tv_sec = a->tv_sec + b->tv_sec; 676118664Sume } else { 67755163Sshin result->tv_usec = l - MILLION; 67855163Sshin result->tv_sec = a->tv_sec + b->tv_sec + 1; 67955163Sshin } 68055163Sshin} 68155163Sshin 68255163Sshin/* 68355163Sshin * result = a - b 68455163Sshin * XXX: this function assumes that a >= b. 68555163Sshin */ 68655163Sshinvoid 68755163SshinTIMEVAL_SUB(struct timeval *a, struct timeval *b, struct timeval *result) 68855163Sshin{ 68955163Sshin long l; 69055163Sshin 69155163Sshin if ((l = a->tv_usec - b->tv_usec) >= 0) { 69255163Sshin result->tv_usec = l; 69355163Sshin result->tv_sec = a->tv_sec - b->tv_sec; 694118664Sume } else { 69555163Sshin result->tv_usec = MILLION + l; 69655163Sshin result->tv_sec = a->tv_sec - b->tv_sec - 1; 69755163Sshin } 69855163Sshin} 69955163Sshin 70055163Sshinstatic void 70155163Sshinrtsold_set_dump_file() 70255163Sshin{ 70355163Sshin do_dump = 1; 70455163Sshin} 70555163Sshin 70655163Sshinstatic void 70755163Sshinusage(char *progname) 70855163Sshin{ 70966776Skris if (progname && progname[strlen(progname) - 1] != 'd') { 71066776Skris fprintf(stderr, "usage: rtsol [-dD] interfaces...\n"); 71166776Skris fprintf(stderr, "usage: rtsol [-dD] -a\n"); 71266776Skris } else { 71366776Skris fprintf(stderr, "usage: rtsold [-adDfm1] interfaces...\n"); 71466776Skris fprintf(stderr, "usage: rtsold [-dDfm1] -a\n"); 71566776Skris } 71655163Sshin exit(1); 71755163Sshin} 71855163Sshin 71955163Sshinvoid 72055163Sshin#if __STDC__ 72155163Sshinwarnmsg(int priority, const char *func, const char *msg, ...) 72255163Sshin#else 72355163Sshinwarnmsg(priority, func, msg, va_alist) 72455163Sshin int priority; 72555163Sshin const char *func; 72655163Sshin const char *msg; 72755163Sshin va_dcl 72855163Sshin#endif 72955163Sshin{ 73055163Sshin va_list ap; 73155163Sshin char buf[BUFSIZ]; 73255163Sshin 73355163Sshin va_start(ap, msg); 73455163Sshin if (fflag) { 73555163Sshin if (priority <= log_upto) { 73655163Sshin (void)vfprintf(stderr, msg, ap); 73755163Sshin (void)fprintf(stderr, "\n"); 73855163Sshin } 73955163Sshin } else { 74055163Sshin snprintf(buf, sizeof(buf), "<%s> %s", func, msg); 74166776Skris msg = buf; 74266776Skris vsyslog(priority, msg, ap); 74355163Sshin } 74455163Sshin va_end(ap); 74555163Sshin} 74666776Skris 74766776Skrisstatic char ** 74866776Skrisautoifprobe() 74966776Skris{ 75066776Skris static char ifname[IFNAMSIZ + 1]; 75166776Skris static char *argv[2]; 75266776Skris struct ifaddrs *ifap, *ifa, *target; 75366776Skris 75466776Skris if (getifaddrs(&ifap) != 0) 75566776Skris return NULL; 75666776Skris 75766776Skris target = NULL; 75866776Skris /* find an ethernet */ 75966776Skris for (ifa = ifap; ifa; ifa = ifa->ifa_next) { 76066776Skris if ((ifa->ifa_flags & IFF_UP) == 0) 76166776Skris continue; 76266776Skris if ((ifa->ifa_flags & IFF_POINTOPOINT) != 0) 76366776Skris continue; 76466776Skris if ((ifa->ifa_flags & IFF_LOOPBACK) != 0) 76566776Skris continue; 76666776Skris if ((ifa->ifa_flags & IFF_MULTICAST) == 0) 76766776Skris continue; 76866776Skris 76966776Skris if (ifa->ifa_addr->sa_family != AF_INET6) 77066776Skris continue; 77166776Skris 77266776Skris if (target && strcmp(target->ifa_name, ifa->ifa_name) == 0) 77366776Skris continue; 77466776Skris 77566776Skris if (!target) 77666776Skris target = ifa; 77766776Skris else { 77866776Skris /* if we find multiple candidates, failure. */ 77966776Skris if (dflag > 1) 78066776Skris warnx("multiple interfaces found"); 78166776Skris target = NULL; 78266776Skris break; 78366776Skris } 78466776Skris } 78566776Skris 78666776Skris if (target) { 78766776Skris strncpy(ifname, target->ifa_name, sizeof(ifname) - 1); 78866776Skris ifname[sizeof(ifname) - 1] = '\0'; 78966776Skris argv[0] = ifname; 79066776Skris argv[1] = NULL; 79166776Skris 79266776Skris if (dflag > 0) 79366776Skris warnx("probing %s", argv[0]); 79466776Skris } 79566776Skris freeifaddrs(ifap); 79666776Skris if (target) 79766776Skris return argv; 79866776Skris else 79966776Skris return (char **)NULL; 80066776Skris} 801