rtsold.c revision 119026
1119026Sume/* $KAME: rtsold.c,v 1.67 2003/05/17 18:16:15 itojun 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 119026 2003-08-17 11:11:32Z ume $ 3255163Sshin */ 3355163Sshin 3455163Sshin#include <sys/types.h> 3555163Sshin#include <sys/time.h> 3662632Skris#include <sys/socket.h> 37118909Sume#include <sys/param.h> 3855163Sshin 3962632Skris#include <net/if.h> 4055163Sshin#include <net/if_dl.h> 4155163Sshin 4255163Sshin#include <netinet/in.h> 4355163Sshin#include <netinet/icmp6.h> 4455163Sshin 4555163Sshin#include <signal.h> 4655163Sshin#include <unistd.h> 4755163Sshin#include <syslog.h> 4855163Sshin#include <string.h> 4955163Sshin#include <stdlib.h> 5055163Sshin#include <stdio.h> 5155163Sshin#include <errno.h> 5255163Sshin#include <err.h> 5355163Sshin#include <stdarg.h> 5466776Skris#include <ifaddrs.h> 55118916Sume#ifdef HAVE_POLL_H 56118916Sume#include <poll.h> 57118916Sume#endif 58118664Sume 5955163Sshin#include "rtsold.h" 6055163Sshin 6155163Sshinstruct ifinfo *iflist; 6255163Sshinstruct timeval tm_max = {0x7fffffff, 0x7fffffff}; 63118664Sumestatic int log_upto = 999; 64118664Sumestatic int fflag = 0; 65118664Sume 6666776Skrisint aflag = 0; 6766776Skrisint dflag = 0; 68118664Sume 69118661Sumechar *otherconf_script; 7055163Sshin 7155163Sshin/* protocol constatns */ 7262632Skris#define MAX_RTR_SOLICITATION_DELAY 1 /* second */ 7362632Skris#define RTR_SOLICITATION_INTERVAL 4 /* seconds */ 7462632Skris#define MAX_RTR_SOLICITATIONS 3 /* times */ 7555163Sshin 76118664Sume/* 77118910Sume * implementation dependent constants in seconds 78118664Sume * XXX: should be configurable 79118664Sume */ 80118664Sume#define PROBE_INTERVAL 60 8155163Sshin 8255163Sshin/* utility macros */ 8355163Sshin/* a < b */ 8462632Skris#define TIMEVAL_LT(a, b) (((a).tv_sec < (b).tv_sec) ||\ 8555163Sshin (((a).tv_sec == (b).tv_sec) && \ 8655163Sshin ((a).tv_usec < (b).tv_usec))) 8755163Sshin 8855163Sshin/* a <= b */ 8962632Skris#define TIMEVAL_LEQ(a, b) (((a).tv_sec < (b).tv_sec) ||\ 9055163Sshin (((a).tv_sec == (b).tv_sec) &&\ 91118664Sume ((a).tv_usec <= (b).tv_usec))) 9255163Sshin 9355163Sshin/* a == b */ 9462632Skris#define TIMEVAL_EQ(a, b) (((a).tv_sec==(b).tv_sec) && ((a).tv_usec==(b).tv_usec)) 9555163Sshin 96118664Sumeint main __P((int, char **)); 9755163Sshin 9855163Sshin/* static variables and functions */ 9955163Sshinstatic int mobile_node = 0; 10055163Sshinstatic int do_dump; 10162632Skrisstatic char *dumpfilename = "/var/run/rtsold.dump"; /* XXX: should be configurable */ 10255163Sshinstatic char *pidfilename = "/var/run/rtsold.pid"; /* should be configurable */ 10355163Sshin 10462632Skris#if 0 105118664Sumestatic int ifreconfig __P((char *)); 10662632Skris#endif 107118664Sumestatic int make_packet __P((struct ifinfo *)); 10855163Sshinstatic struct timeval *rtsol_check_timer __P((void)); 109118664Sumestatic void TIMEVAL_ADD __P((struct timeval *, struct timeval *, 110118664Sume struct timeval *)); 111118664Sumestatic void TIMEVAL_SUB __P((struct timeval *, struct timeval *, 112118664Sume struct timeval *)); 11355163Sshin 114118910Sumestatic void rtsold_set_dump_file __P((int)); 115118664Sumestatic void usage __P((char *)); 11655163Sshin 11755163Sshinint 11855163Sshinmain(argc, argv) 11955163Sshin int argc; 120118664Sume char **argv; 12155163Sshin{ 122118909Sume int s, ch, once = 0; 12355163Sshin struct timeval *timeout; 124118664Sume char *argv0, *opts; 125118916Sume#ifdef HAVE_POLL_H 126118916Sume struct pollfd set[2]; 127118916Sume#else 128118909Sume fd_set *fdsetp, *selectfdp; 129118909Sume int fdmasks; 130118909Sume int maxfd; 131118916Sume#endif 132118909Sume int rtsock; 13355163Sshin 13455163Sshin /* 13555163Sshin * Initialization 13655163Sshin */ 13755163Sshin argv0 = argv[0]; 13855163Sshin 13955163Sshin /* get option */ 14055163Sshin if (argv0 && argv0[strlen(argv0) - 1] != 'd') { 14155163Sshin fflag = 1; 14255163Sshin once = 1; 143118661Sume opts = "adDO:"; 14455163Sshin } else 145118661Sume opts = "adDfm1O:"; 14655163Sshin 14755163Sshin while ((ch = getopt(argc, argv, opts)) != -1) { 14866776Skris switch (ch) { 14966776Skris case 'a': 15066776Skris aflag = 1; 15166776Skris break; 15266776Skris case 'd': 15366776Skris dflag = 1; 15466776Skris break; 15566776Skris case 'D': 15666776Skris dflag = 2; 15766776Skris break; 15866776Skris case 'f': 15966776Skris fflag = 1; 16066776Skris break; 16166776Skris case 'm': 16266776Skris mobile_node = 1; 16366776Skris break; 16466776Skris case '1': 16566776Skris once = 1; 16666776Skris break; 167118661Sume case 'O': 168118661Sume otherconf_script = optarg; 169118661Sume break; 17066776Skris default: 17166776Skris usage(argv0); 17266776Skris /*NOTREACHED*/ 17355163Sshin } 17455163Sshin } 17555163Sshin argc -= optind; 17655163Sshin argv += optind; 17766776Skris 178119026Sume if ((!aflag && argc == 0) || (aflag && argc != 0)) { 17955163Sshin usage(argv0); 18066776Skris /*NOTREACHED*/ 18166776Skris } 18255163Sshin 18355163Sshin /* set log level */ 18455163Sshin if (dflag == 0) 18555163Sshin log_upto = LOG_NOTICE; 18655163Sshin if (!fflag) { 18755163Sshin char *ident; 188118664Sume 18955163Sshin ident = strrchr(argv0, '/'); 19055163Sshin if (!ident) 19155163Sshin ident = argv0; 19255163Sshin else 19355163Sshin ident++; 19455163Sshin openlog(ident, LOG_NDELAY|LOG_PID, LOG_DAEMON); 19555163Sshin if (log_upto >= 0) 19655163Sshin setlogmask(LOG_UPTO(log_upto)); 19755163Sshin } 19855163Sshin 199118661Sume if (otherconf_script && *otherconf_script != '/') { 200118661Sume errx(1, "configuration script (%s) must be an absolute path", 201118661Sume otherconf_script); 202118661Sume } 203118661Sume 20462632Skris#ifndef HAVE_ARC4RANDOM 205118664Sume /* random value initialization */ 20655163Sshin srandom((u_long)time(NULL)); 20762632Skris#endif 20855163Sshin 20955163Sshin /* warn if accept_rtadv is down */ 21055163Sshin if (!getinet6sysctl(IPV6CTL_ACCEPT_RTADV)) 21155163Sshin warnx("kernel is configured not to accept RAs"); 21266776Skris /* warn if forwarding is up */ 21366776Skris if (getinet6sysctl(IPV6CTL_FORWARDING)) 21466776Skris warnx("kernel is configured as a router, not a host"); 21555163Sshin 21655163Sshin /* initialization to dump internal status to a file */ 217118910Sume signal(SIGUSR1, rtsold_set_dump_file); 21855163Sshin 219118914Sume if (!fflag) 220118914Sume daemon(0, 0); /* act as a daemon */ 221118914Sume 22262632Skris /* 22362632Skris * Open a socket for sending RS and receiving RA. 22462632Skris * This should be done before calling ifinit(), since the function 22562632Skris * uses the socket. 22662632Skris */ 22766776Skris if ((s = sockopen()) < 0) { 228118914Sume warnmsg(LOG_ERR, __func__, "failed to open a socket"); 229118914Sume exit(1); 23066776Skris /*NOTREACHED*/ 23166776Skris } 232118916Sume#ifdef HAVE_POLL_H 233118916Sume set[0].fd = s; 234118916Sume set[0].events = POLLIN; 235118916Sume#else 23678064Sume maxfd = s; 237118916Sume#endif 238118916Sume 239118916Sume#ifdef HAVE_POLL_H 240118916Sume set[1].fd = -1; 241118916Sume#endif 242118916Sume 24378064Sume if ((rtsock = rtsock_open()) < 0) { 244118914Sume warnmsg(LOG_ERR, __func__, "failed to open a socket"); 245118914Sume exit(1); 24678064Sume /*NOTREACHED*/ 24778064Sume } 248118916Sume#ifdef HAVE_POLL_H 249118916Sume set[1].fd = rtsock; 250118916Sume set[1].events = POLLIN; 251118916Sume#else 25278064Sume if (rtsock > maxfd) 25378064Sume maxfd = rtsock; 254118916Sume#endif 25562632Skris 256118916Sume#ifndef HAVE_POLL_H 257118909Sume fdmasks = howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask); 258118909Sume if ((fdsetp = malloc(fdmasks)) == NULL) { 259118909Sume err(1, "malloc"); 260118909Sume /*NOTREACHED*/ 261118909Sume } 262118909Sume if ((selectfdp = malloc(fdmasks)) == NULL) { 263118909Sume err(1, "malloc"); 264118909Sume /*NOTREACHED*/ 265118909Sume } 266118916Sume#endif 267118909Sume 26855163Sshin /* configuration per interface */ 26966776Skris if (ifinit()) { 270118914Sume warnmsg(LOG_ERR, __func__, 271118914Sume "failed to initilizatoin interfaces"); 272118914Sume exit(1); 27366776Skris /*NOTREACHED*/ 27466776Skris } 275119026Sume if (aflag) 276119026Sume argv = autoifprobe(); 277119026Sume while (argv && *argv) { 27866776Skris if (ifconfig(*argv)) { 279118914Sume warnmsg(LOG_ERR, __func__, 280118914Sume "failed to initialize %s", *argv); 281118914Sume exit(1); 28266776Skris /*NOTREACHED*/ 28366776Skris } 28455163Sshin argv++; 28555163Sshin } 28655163Sshin 28755163Sshin /* setup for probing default routers */ 28866776Skris if (probe_init()) { 289118914Sume warnmsg(LOG_ERR, __func__, 290118914Sume "failed to setup for probing routers"); 291118914Sume exit(1); 29266776Skris /*NOTREACHED*/ 29366776Skris } 29455163Sshin 29555163Sshin /* dump the current pid */ 29655163Sshin if (!once) { 29755163Sshin pid_t pid = getpid(); 29855163Sshin FILE *fp; 29955163Sshin 30055163Sshin if ((fp = fopen(pidfilename, "w")) == NULL) 301118660Sume warnmsg(LOG_ERR, __func__, 302118664Sume "failed to open a pid log file(%s): %s", 303118664Sume pidfilename, strerror(errno)); 30455163Sshin else { 30555163Sshin fprintf(fp, "%d\n", pid); 30655163Sshin fclose(fp); 30755163Sshin } 30855163Sshin } 30955163Sshin 310118916Sume#ifndef HAVE_POLL_H 311118909Sume memset(fdsetp, 0, fdmasks); 312118909Sume FD_SET(s, fdsetp); 313118909Sume FD_SET(rtsock, fdsetp); 314118916Sume#endif 31555163Sshin while (1) { /* main loop */ 31655163Sshin int e; 31755163Sshin 318118916Sume#ifndef HAVE_POLL_H 319118909Sume memcpy(selectfdp, fdsetp, fdmasks); 320118916Sume#endif 321118909Sume 32255163Sshin if (do_dump) { /* SIGUSR1 */ 32355163Sshin do_dump = 0; 32455163Sshin rtsold_dump_file(dumpfilename); 32555163Sshin } 326118664Sume 32755163Sshin timeout = rtsol_check_timer(); 32855163Sshin 32955163Sshin if (once) { 33055163Sshin struct ifinfo *ifi; 33155163Sshin 33255163Sshin /* if we have no timeout, we are done (or failed) */ 33355163Sshin if (timeout == NULL) 33455163Sshin break; 33555163Sshin 33655163Sshin /* if all interfaces have got RA packet, we are done */ 33755163Sshin for (ifi = iflist; ifi; ifi = ifi->next) { 33855163Sshin if (ifi->state != IFS_DOWN && ifi->racnt == 0) 33955163Sshin break; 34055163Sshin } 34155163Sshin if (ifi == NULL) 34255163Sshin break; 34355163Sshin } 344118916Sume#ifdef HAVE_POLL_H 345118916Sume e = poll(set, 2, timeout ? (timeout->tv_sec * 1000 + timeout->tv_usec / 1000) : INFTIM); 346118916Sume#else 347118909Sume e = select(maxfd + 1, selectfdp, NULL, NULL, timeout); 348118916Sume#endif 34978064Sume if (e < 1) { 35055163Sshin if (e < 0 && errno != EINTR) { 351118660Sume warnmsg(LOG_ERR, __func__, "select: %s", 352118664Sume strerror(errno)); 35355163Sshin } 35455163Sshin continue; 35555163Sshin } 35655163Sshin 35755163Sshin /* packet reception */ 358118916Sume#ifdef HAVE_POLL_H 359118916Sume if (set[1].revents & POLLIN) 360118916Sume#else 361118909Sume if (FD_ISSET(rtsock, selectfdp)) 362118916Sume#endif 36378064Sume rtsock_input(rtsock); 364118916Sume#ifdef HAVE_POLL_H 365118916Sume if (set[0].revents & POLLIN) 366118916Sume#else 367118909Sume if (FD_ISSET(s, selectfdp)) 368118916Sume#endif 36955163Sshin rtsol_input(s); 37055163Sshin } 37155163Sshin /* NOTREACHED */ 37255163Sshin 37355163Sshin return 0; 37455163Sshin} 37555163Sshin 376119026Sumeint 37755163Sshinifconfig(char *ifname) 37855163Sshin{ 37955163Sshin struct ifinfo *ifinfo; 38055163Sshin struct sockaddr_dl *sdl; 38155163Sshin int flags; 38255163Sshin 38355163Sshin if ((sdl = if_nametosdl(ifname)) == NULL) { 384118660Sume warnmsg(LOG_ERR, __func__, 385118664Sume "failed to get link layer information for %s", ifname); 38655163Sshin return(-1); 38755163Sshin } 38855163Sshin if (find_ifinfo(sdl->sdl_index)) { 389118660Sume warnmsg(LOG_ERR, __func__, 390118664Sume "interface %s was already configured", ifname); 39162632Skris free(sdl); 39255163Sshin return(-1); 39355163Sshin } 39455163Sshin 39555163Sshin if ((ifinfo = malloc(sizeof(*ifinfo))) == NULL) { 396118660Sume warnmsg(LOG_ERR, __func__, "memory allocation failed"); 39762632Skris free(sdl); 39855163Sshin return(-1); 39955163Sshin } 40055163Sshin memset(ifinfo, 0, sizeof(*ifinfo)); 40155163Sshin ifinfo->sdl = sdl; 40255163Sshin 403118786Sume strlcpy(ifinfo->ifname, ifname, sizeof(ifinfo->ifname)); 40455163Sshin 40555163Sshin /* construct a router solicitation message */ 40655163Sshin if (make_packet(ifinfo)) 40755163Sshin goto bad; 40855163Sshin 409119026Sume /* set link ID of this interface. */ 410119026Sume#ifdef HAVE_SCOPELIB 411119026Sume if (inet_zoneid(AF_INET6, 2, ifname, &ifinfo->linkid)) 412119026Sume goto bad; 413119026Sume#else 414119026Sume /* XXX: assume interface IDs as link IDs */ 415119026Sume ifinfo->linkid = ifinfo->sdl->sdl_index; 416119026Sume#endif 417119026Sume 41855163Sshin /* 41955163Sshin * check if the interface is available. 42055163Sshin * also check if SIOCGIFMEDIA ioctl is OK on the interface. 42155163Sshin */ 42255163Sshin ifinfo->mediareqok = 1; 42355163Sshin ifinfo->active = interface_status(ifinfo); 42455163Sshin if (!ifinfo->mediareqok) { 42555163Sshin /* 42655163Sshin * probe routers periodically even if the link status 42755163Sshin * does not change. 42855163Sshin */ 42955163Sshin ifinfo->probeinterval = PROBE_INTERVAL; 43055163Sshin } 43155163Sshin 43255163Sshin /* activate interface: interface_up returns 0 on success */ 43355163Sshin flags = interface_up(ifinfo->ifname); 43455163Sshin if (flags == 0) 43555163Sshin ifinfo->state = IFS_DELAY; 43655163Sshin else if (flags == IFS_TENTATIVE) 43755163Sshin ifinfo->state = IFS_TENTATIVE; 43855163Sshin else 43955163Sshin ifinfo->state = IFS_DOWN; 44055163Sshin 44155163Sshin rtsol_timer_update(ifinfo); 44255163Sshin 44355163Sshin /* link into chain */ 44455163Sshin if (iflist) 44555163Sshin ifinfo->next = iflist; 44655163Sshin iflist = ifinfo; 44755163Sshin 44855163Sshin return(0); 44955163Sshin 450118664Sumebad: 45162632Skris free(ifinfo->sdl); 45255163Sshin free(ifinfo); 45355163Sshin return(-1); 45455163Sshin} 45555163Sshin 456119026Sumevoid 457119026Sumeiflist_init() 458119026Sume{ 459119026Sume struct ifinfo *ifi, *next; 460119026Sume 461119026Sume for (ifi = iflist; ifi; ifi = next) { 462119026Sume next = ifi->next; 463119026Sume if (ifi->sdl) 464119026Sume free(ifi->sdl); 465119026Sume if (ifi->rs_data) 466119026Sume free(ifi->rs_data); 467119026Sume free(ifi); 468119026Sume iflist = NULL; 469119026Sume } 470119026Sume} 471119026Sume 47262632Skris#if 0 47362632Skrisstatic int 47462632Skrisifreconfig(char *ifname) 47562632Skris{ 47662632Skris struct ifinfo *ifi, *prev; 47762632Skris int rv; 47862632Skris 47962632Skris prev = NULL; 48062632Skris for (ifi = iflist; ifi; ifi = ifi->next) { 48162632Skris if (strncmp(ifi->ifname, ifname, sizeof(ifi->ifname)) == 0) 48262632Skris break; 48362632Skris prev = ifi; 48462632Skris } 48562632Skris prev->next = ifi->next; 48662632Skris 48762632Skris rv = ifconfig(ifname); 48862632Skris 48962632Skris /* reclaim it after ifconfig() in case ifname is pointer inside ifi */ 49062632Skris if (ifi->rs_data) 49162632Skris free(ifi->rs_data); 49262632Skris free(ifi->sdl); 49362632Skris free(ifi); 49462632Skris return rv; 49562632Skris} 49662632Skris#endif 49762632Skris 49855163Sshinstruct ifinfo * 49955163Sshinfind_ifinfo(int ifindex) 50055163Sshin{ 50155163Sshin struct ifinfo *ifi; 50255163Sshin 50355163Sshin for (ifi = iflist; ifi; ifi = ifi->next) 50455163Sshin if (ifi->sdl->sdl_index == ifindex) 50555163Sshin return(ifi); 50655163Sshin return(NULL); 50755163Sshin} 50855163Sshin 50955163Sshinstatic int 51055163Sshinmake_packet(struct ifinfo *ifinfo) 51155163Sshin{ 512118664Sume size_t packlen = sizeof(struct nd_router_solicit), lladdroptlen = 0; 513118664Sume struct nd_router_solicit *rs; 51455163Sshin char *buf; 51555163Sshin 51655163Sshin if ((lladdroptlen = lladdropt_length(ifinfo->sdl)) == 0) { 517118660Sume warnmsg(LOG_INFO, __func__, 518118664Sume "link-layer address option has null length" 519118664Sume " on %s. Treat as not included.", ifinfo->ifname); 52055163Sshin } 52155163Sshin packlen += lladdroptlen; 52255163Sshin ifinfo->rs_datalen = packlen; 52355163Sshin 52455163Sshin /* allocate buffer */ 52555163Sshin if ((buf = malloc(packlen)) == NULL) { 526118660Sume warnmsg(LOG_ERR, __func__, 527118664Sume "memory allocation failed for %s", ifinfo->ifname); 52855163Sshin return(-1); 52955163Sshin } 53055163Sshin ifinfo->rs_data = buf; 53155163Sshin 53255163Sshin /* fill in the message */ 53355163Sshin rs = (struct nd_router_solicit *)buf; 53455163Sshin rs->nd_rs_type = ND_ROUTER_SOLICIT; 53555163Sshin rs->nd_rs_code = 0; 53655163Sshin rs->nd_rs_cksum = 0; 53755163Sshin rs->nd_rs_reserved = 0; 53855163Sshin buf += sizeof(*rs); 53955163Sshin 54055163Sshin /* fill in source link-layer address option */ 54155163Sshin if (lladdroptlen) 54255163Sshin lladdropt_fill(ifinfo->sdl, (struct nd_opt_hdr *)buf); 54355163Sshin 54455163Sshin return(0); 54555163Sshin} 54655163Sshin 54755163Sshinstatic struct timeval * 54855163Sshinrtsol_check_timer() 54955163Sshin{ 55055163Sshin static struct timeval returnval; 55155163Sshin struct timeval now, rtsol_timer; 55255163Sshin struct ifinfo *ifinfo; 55355163Sshin int flags; 55455163Sshin 55555163Sshin gettimeofday(&now, NULL); 55655163Sshin 55755163Sshin rtsol_timer = tm_max; 55855163Sshin 55955163Sshin for (ifinfo = iflist; ifinfo; ifinfo = ifinfo->next) { 56055163Sshin if (TIMEVAL_LEQ(ifinfo->expire, now)) { 56155163Sshin if (dflag > 1) 562118660Sume warnmsg(LOG_DEBUG, __func__, 563118664Sume "timer expiration on %s, " 564118664Sume "state = %d", ifinfo->ifname, 565118664Sume ifinfo->state); 56655163Sshin 56766776Skris switch (ifinfo->state) { 56855163Sshin case IFS_DOWN: 56955163Sshin case IFS_TENTATIVE: 57055163Sshin /* interface_up returns 0 on success */ 57155163Sshin flags = interface_up(ifinfo->ifname); 57255163Sshin if (flags == 0) 57355163Sshin ifinfo->state = IFS_DELAY; 57455163Sshin else if (flags == IFS_TENTATIVE) 57555163Sshin ifinfo->state = IFS_TENTATIVE; 57655163Sshin else 57755163Sshin ifinfo->state = IFS_DOWN; 57855163Sshin break; 57955163Sshin case IFS_IDLE: 58055163Sshin { 58155163Sshin int oldstatus = ifinfo->active; 58255163Sshin int probe = 0; 58355163Sshin 584118664Sume ifinfo->active = interface_status(ifinfo); 58555163Sshin 58655163Sshin if (oldstatus != ifinfo->active) { 587118660Sume warnmsg(LOG_DEBUG, __func__, 588118664Sume "%s status is changed" 589118664Sume " from %d to %d", 590118664Sume ifinfo->ifname, 591118664Sume oldstatus, ifinfo->active); 59255163Sshin probe = 1; 59355163Sshin ifinfo->state = IFS_DELAY; 594118664Sume } else if (ifinfo->probeinterval && 595118664Sume (ifinfo->probetimer -= 596118664Sume ifinfo->timer.tv_sec) <= 0) { 59755163Sshin /* probe timer expired */ 59855163Sshin ifinfo->probetimer = 599118664Sume ifinfo->probeinterval; 60055163Sshin probe = 1; 60155163Sshin ifinfo->state = IFS_PROBE; 60255163Sshin } 60355163Sshin 604118661Sume /* 605118661Sume * If we need a probe, clear the previous 606118661Sume * status wrt the "other" configuration. 607118661Sume */ 608118661Sume if (probe) 609118661Sume ifinfo->otherconfig = 0; 610118661Sume 61155163Sshin if (probe && mobile_node) 612119026Sume defrouter_probe(ifinfo); 61355163Sshin break; 61455163Sshin } 61555163Sshin case IFS_DELAY: 61655163Sshin ifinfo->state = IFS_PROBE; 61755163Sshin sendpacket(ifinfo); 61855163Sshin break; 61955163Sshin case IFS_PROBE: 62055163Sshin if (ifinfo->probes < MAX_RTR_SOLICITATIONS) 62155163Sshin sendpacket(ifinfo); 62255163Sshin else { 623118660Sume warnmsg(LOG_INFO, __func__, 624118664Sume "No answer after sending %d RSs", 625118664Sume ifinfo->probes); 62655163Sshin ifinfo->probes = 0; 62755163Sshin ifinfo->state = IFS_IDLE; 62855163Sshin } 62955163Sshin break; 63055163Sshin } 63155163Sshin rtsol_timer_update(ifinfo); 63255163Sshin } 63355163Sshin 63455163Sshin if (TIMEVAL_LT(ifinfo->expire, rtsol_timer)) 63555163Sshin rtsol_timer = ifinfo->expire; 63655163Sshin } 63755163Sshin 63855163Sshin if (TIMEVAL_EQ(rtsol_timer, tm_max)) { 639118660Sume warnmsg(LOG_DEBUG, __func__, "there is no timer"); 64055163Sshin return(NULL); 641118664Sume } else if (TIMEVAL_LT(rtsol_timer, now)) 64255163Sshin /* this may occur when the interval is too small */ 64355163Sshin returnval.tv_sec = returnval.tv_usec = 0; 64455163Sshin else 64555163Sshin TIMEVAL_SUB(&rtsol_timer, &now, &returnval); 64655163Sshin 64755163Sshin if (dflag > 1) 648118660Sume warnmsg(LOG_DEBUG, __func__, "New timer is %ld:%08ld", 649118664Sume (long)returnval.tv_sec, (long)returnval.tv_usec); 65055163Sshin 65155163Sshin return(&returnval); 65255163Sshin} 65355163Sshin 65455163Sshinvoid 65555163Sshinrtsol_timer_update(struct ifinfo *ifinfo) 65655163Sshin{ 65762632Skris#define MILLION 1000000 65862632Skris#define DADRETRY 10 /* XXX: adhoc */ 65955163Sshin long interval; 66055163Sshin struct timeval now; 66155163Sshin 66255163Sshin bzero(&ifinfo->timer, sizeof(ifinfo->timer)); 66355163Sshin 66455163Sshin switch (ifinfo->state) { 66555163Sshin case IFS_DOWN: 66655163Sshin case IFS_TENTATIVE: 66755163Sshin if (++ifinfo->dadcount > DADRETRY) { 66855163Sshin ifinfo->dadcount = 0; 66955163Sshin ifinfo->timer.tv_sec = PROBE_INTERVAL; 670118664Sume } else 67155163Sshin ifinfo->timer.tv_sec = 1; 67255163Sshin break; 67355163Sshin case IFS_IDLE: 67455163Sshin if (mobile_node) { 675118664Sume /* XXX should be configurable */ 67655163Sshin ifinfo->timer.tv_sec = 3; 67755163Sshin } 67855163Sshin else 67955163Sshin ifinfo->timer = tm_max; /* stop timer(valid?) */ 68055163Sshin break; 68155163Sshin case IFS_DELAY: 68262632Skris#ifndef HAVE_ARC4RANDOM 68355163Sshin interval = random() % (MAX_RTR_SOLICITATION_DELAY * MILLION); 68462632Skris#else 68562632Skris interval = arc4random() % (MAX_RTR_SOLICITATION_DELAY * MILLION); 68662632Skris#endif 68755163Sshin ifinfo->timer.tv_sec = interval / MILLION; 68855163Sshin ifinfo->timer.tv_usec = interval % MILLION; 68955163Sshin break; 69055163Sshin case IFS_PROBE: 69178064Sume if (ifinfo->probes < MAX_RTR_SOLICITATIONS) 69278064Sume ifinfo->timer.tv_sec = RTR_SOLICITATION_INTERVAL; 69378064Sume else { 69478064Sume /* 69578064Sume * After sending MAX_RTR_SOLICITATIONS solicitations, 69678064Sume * we're just waiting for possible replies; there 69778064Sume * will be no more solicatation. Thus, we change 69878064Sume * the timer value to MAX_RTR_SOLICITATION_DELAY based 69978064Sume * on RFC 2461, Section 6.3.7. 70078064Sume */ 70178064Sume ifinfo->timer.tv_sec = MAX_RTR_SOLICITATION_DELAY; 70278064Sume } 70355163Sshin break; 70455163Sshin default: 705118660Sume warnmsg(LOG_ERR, __func__, 706118664Sume "illegal interface state(%d) on %s", 707118664Sume ifinfo->state, ifinfo->ifname); 70855163Sshin return; 70955163Sshin } 71055163Sshin 71155163Sshin /* reset the timer */ 71255163Sshin if (TIMEVAL_EQ(ifinfo->timer, tm_max)) { 71355163Sshin ifinfo->expire = tm_max; 714118660Sume warnmsg(LOG_DEBUG, __func__, 715118664Sume "stop timer for %s", ifinfo->ifname); 716118664Sume } else { 71755163Sshin gettimeofday(&now, NULL); 71855163Sshin TIMEVAL_ADD(&now, &ifinfo->timer, &ifinfo->expire); 71955163Sshin 72055163Sshin if (dflag > 1) 721118660Sume warnmsg(LOG_DEBUG, __func__, 722118664Sume "set timer for %s to %d:%d", ifinfo->ifname, 723118664Sume (int)ifinfo->timer.tv_sec, 724118664Sume (int)ifinfo->timer.tv_usec); 72555163Sshin } 72655163Sshin 72755163Sshin#undef MILLION 72855163Sshin} 72955163Sshin 73055163Sshin/* timer related utility functions */ 73162632Skris#define MILLION 1000000 73255163Sshin 73355163Sshin/* result = a + b */ 73455163Sshinstatic void 73555163SshinTIMEVAL_ADD(struct timeval *a, struct timeval *b, struct timeval *result) 73655163Sshin{ 73755163Sshin long l; 73855163Sshin 73955163Sshin if ((l = a->tv_usec + b->tv_usec) < MILLION) { 74055163Sshin result->tv_usec = l; 74155163Sshin result->tv_sec = a->tv_sec + b->tv_sec; 742118664Sume } else { 74355163Sshin result->tv_usec = l - MILLION; 74455163Sshin result->tv_sec = a->tv_sec + b->tv_sec + 1; 74555163Sshin } 74655163Sshin} 74755163Sshin 74855163Sshin/* 74955163Sshin * result = a - b 75055163Sshin * XXX: this function assumes that a >= b. 75155163Sshin */ 75255163Sshinvoid 75355163SshinTIMEVAL_SUB(struct timeval *a, struct timeval *b, struct timeval *result) 75455163Sshin{ 75555163Sshin long l; 75655163Sshin 75755163Sshin if ((l = a->tv_usec - b->tv_usec) >= 0) { 75855163Sshin result->tv_usec = l; 75955163Sshin result->tv_sec = a->tv_sec - b->tv_sec; 760118664Sume } else { 76155163Sshin result->tv_usec = MILLION + l; 76255163Sshin result->tv_sec = a->tv_sec - b->tv_sec - 1; 76355163Sshin } 76455163Sshin} 76555163Sshin 76655163Sshinstatic void 767118910Sumertsold_set_dump_file(sig) 768118910Sume int sig; 76955163Sshin{ 77055163Sshin do_dump = 1; 77155163Sshin} 77255163Sshin 77355163Sshinstatic void 77455163Sshinusage(char *progname) 77555163Sshin{ 77666776Skris if (progname && progname[strlen(progname) - 1] != 'd') { 77766776Skris fprintf(stderr, "usage: rtsol [-dD] interfaces...\n"); 77866776Skris fprintf(stderr, "usage: rtsol [-dD] -a\n"); 77966776Skris } else { 78066776Skris fprintf(stderr, "usage: rtsold [-adDfm1] interfaces...\n"); 78166776Skris fprintf(stderr, "usage: rtsold [-dDfm1] -a\n"); 78266776Skris } 78355163Sshin exit(1); 78455163Sshin} 78555163Sshin 78655163Sshinvoid 78755163Sshin#if __STDC__ 78855163Sshinwarnmsg(int priority, const char *func, const char *msg, ...) 78955163Sshin#else 79055163Sshinwarnmsg(priority, func, msg, va_alist) 79155163Sshin int priority; 79255163Sshin const char *func; 79355163Sshin const char *msg; 79455163Sshin va_dcl 79555163Sshin#endif 79655163Sshin{ 79755163Sshin va_list ap; 79855163Sshin char buf[BUFSIZ]; 79955163Sshin 80055163Sshin va_start(ap, msg); 80155163Sshin if (fflag) { 80255163Sshin if (priority <= log_upto) { 80355163Sshin (void)vfprintf(stderr, msg, ap); 80455163Sshin (void)fprintf(stderr, "\n"); 80555163Sshin } 80655163Sshin } else { 80755163Sshin snprintf(buf, sizeof(buf), "<%s> %s", func, msg); 80866776Skris msg = buf; 80966776Skris vsyslog(priority, msg, ap); 81055163Sshin } 81155163Sshin va_end(ap); 81255163Sshin} 81366776Skris 814119026Sume/* 815119026Sume * return a list of interfaces which is suitable to sending an RS. 816119026Sume */ 817119026Sumechar ** 81866776Skrisautoifprobe() 81966776Skris{ 820119026Sume static char **argv = NULL; 821119026Sume static int n = 0; 822119026Sume char **a; 823119026Sume int i, found; 82466776Skris struct ifaddrs *ifap, *ifa, *target; 82566776Skris 826119026Sume /* initialize */ 827119026Sume while (n--) 828119026Sume free(argv[n]); 829119026Sume if (argv) { 830119026Sume free(argv); 831119026Sume argv = NULL; 832119026Sume } 833119026Sume n = 0; 834119026Sume 83566776Skris if (getifaddrs(&ifap) != 0) 83666776Skris return NULL; 83766776Skris 83866776Skris target = NULL; 83966776Skris /* find an ethernet */ 84066776Skris for (ifa = ifap; ifa; ifa = ifa->ifa_next) { 84166776Skris if ((ifa->ifa_flags & IFF_UP) == 0) 84266776Skris continue; 84366776Skris if ((ifa->ifa_flags & IFF_POINTOPOINT) != 0) 84466776Skris continue; 84566776Skris if ((ifa->ifa_flags & IFF_LOOPBACK) != 0) 84666776Skris continue; 84766776Skris if ((ifa->ifa_flags & IFF_MULTICAST) == 0) 84866776Skris continue; 84966776Skris 85066776Skris if (ifa->ifa_addr->sa_family != AF_INET6) 85166776Skris continue; 85266776Skris 853119026Sume found = 0; 854119026Sume for (i = 0; i < n; i++) { 855119026Sume if (strcmp(argv[i], ifa->ifa_name) == 0) { 856119026Sume found++; 857119026Sume break; 858119026Sume } 859119026Sume } 860119026Sume if (found) 86166776Skris continue; 86266776Skris 863119026Sume /* if we find multiple candidates, just warn. */ 864119026Sume if (n != 0 && dflag > 1) 865119026Sume warnx("multiple interfaces found"); 866119026Sume 867119026Sume a = (char **)realloc(argv, (n + 1) * sizeof(char **)); 868119026Sume if (a == NULL) 869119026Sume err(1, "realloc"); 870119026Sume argv = a; 871119026Sume argv[n] = strdup(ifa->ifa_name); 872119026Sume if (!argv[n]) 873119026Sume err(1, "malloc"); 874119026Sume n++; 875119026Sume argv[n] = NULL; 87666776Skris } 87766776Skris 878119026Sume if (n) { 879119026Sume a = (char **)realloc(argv, (n + 1) * sizeof(char **)); 880119026Sume if (a == NULL) 881119026Sume err(1, "realloc"); 882119026Sume argv = a; 883119026Sume argv[n] = NULL; 88466776Skris 885119026Sume if (dflag > 0) { 886119026Sume for (i = 0; i < n; i++) 887119026Sume warnx("probing %s", argv[i]); 888119026Sume } 88966776Skris } 89066776Skris freeifaddrs(ifap); 891119026Sume return argv; 89266776Skris} 893