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$ 3255163Sshin */ 3355163Sshin 3455163Sshin#include <sys/types.h> 35203387Sume#include <sys/ioctl.h> 3662632Skris#include <sys/socket.h> 37118909Sume#include <sys/param.h> 3855163Sshin 3962632Skris#include <net/if.h> 4055163Sshin#include <net/if_dl.h> 41203387Sume#include <net/if_var.h> 4255163Sshin 4355163Sshin#include <netinet/in.h> 4455163Sshin#include <netinet/icmp6.h> 45203387Sume#include <netinet/in_var.h> 46222861Shrs#include <arpa/inet.h> 4755163Sshin 48203387Sume#include <netinet6/nd6.h> 49203387Sume 5055163Sshin#include <signal.h> 5155163Sshin#include <unistd.h> 5255163Sshin#include <syslog.h> 5355163Sshin#include <string.h> 5455163Sshin#include <stdlib.h> 5555163Sshin#include <stdio.h> 56253970Shrs#include <time.h> 5755163Sshin#include <errno.h> 5855163Sshin#include <err.h> 5955163Sshin#include <stdarg.h> 6066776Skris#include <ifaddrs.h> 61118916Sume#ifdef HAVE_POLL_H 62118916Sume#include <poll.h> 63118916Sume#endif 64118664Sume 6555163Sshin#include "rtsold.h" 6655163Sshin 67222732Shrs#define RTSOL_DUMPFILE "/var/run/rtsold.dump"; 68222732Shrs#define RTSOL_PIDFILE "/var/run/rtsold.pid"; 69222732Shrs 70253970Shrsstruct timespec tm_max; 71118664Sumestatic int log_upto = 999; 72118664Sumestatic int fflag = 0; 73118664Sume 74197141Shrsint Fflag = 0; /* force setting sysctl parameters */ 7566776Skrisint aflag = 0; 7666776Skrisint dflag = 0; 77225520Shrsint uflag = 0; 78118664Sume 79222732Shrsconst char *otherconf_script; 80222732Shrsconst char *resolvconf_script = "/sbin/resolvconf"; 8155163Sshin 82147150Ssuz/* protocol constants */ 8362632Skris#define MAX_RTR_SOLICITATION_DELAY 1 /* second */ 8462632Skris#define RTR_SOLICITATION_INTERVAL 4 /* seconds */ 8562632Skris#define MAX_RTR_SOLICITATIONS 3 /* times */ 8655163Sshin 87118664Sume/* 88118910Sume * implementation dependent constants in seconds 89118664Sume * XXX: should be configurable 90118664Sume */ 91118664Sume#define PROBE_INTERVAL 60 9255163Sshin 9355163Sshin/* static variables and functions */ 9455163Sshinstatic int mobile_node = 0; 95222732Shrsstatic const char *pidfilename = RTSOL_PIDFILE; 96222732Shrs 97124526Sume#ifndef SMALL 9855163Sshinstatic int do_dump; 99222732Shrsstatic const char *dumpfilename = RTSOL_DUMPFILE; 100124526Sume#endif 10155163Sshin 10262632Skris#if 0 103173412Skevlostatic int ifreconfig(char *); 10462632Skris#endif 105222732Shrs 106173412Skevlostatic int make_packet(struct ifinfo *); 107253970Shrsstatic struct timespec *rtsol_check_timer(void); 10855163Sshin 109124526Sume#ifndef SMALL 110173412Skevlostatic void rtsold_set_dump_file(int); 111124526Sume#endif 112222732Shrsstatic void usage(void); 11355163Sshin 11455163Sshinint 115124524Sumemain(int argc, char **argv) 11655163Sshin{ 117118909Sume int s, ch, once = 0; 118253970Shrs struct timespec *timeout; 119204407Suqs const char *opts; 120118916Sume#ifdef HAVE_POLL_H 121118916Sume struct pollfd set[2]; 122118916Sume#else 123118909Sume fd_set *fdsetp, *selectfdp; 124118909Sume int fdmasks; 125118909Sume int maxfd; 126118916Sume#endif 127118909Sume int rtsock; 128222848Shrs char *argv0; 12955163Sshin 130222732Shrs#ifndef SMALL 131222732Shrs /* rtsold */ 132225520Shrs opts = "adDfFm1O:p:R:u"; 133222732Shrs#else 134222732Shrs /* rtsol */ 135225520Shrs opts = "adDFO:R:u"; 136222732Shrs fflag = 1; 137222732Shrs once = 1; 138222732Shrs#endif 139222848Shrs argv0 = argv[0]; 140222848Shrs 14155163Sshin while ((ch = getopt(argc, argv, opts)) != -1) { 14266776Skris switch (ch) { 14366776Skris case 'a': 14466776Skris aflag = 1; 14566776Skris break; 14666776Skris case 'd': 147225520Shrs dflag += 1; 14866776Skris break; 14966776Skris case 'D': 150225520Shrs dflag += 2; 15166776Skris break; 15266776Skris case 'f': 15366776Skris fflag = 1; 15466776Skris break; 155124525Sume case 'F': 156124525Sume Fflag = 1; 157124525Sume break; 15866776Skris case 'm': 15966776Skris mobile_node = 1; 16066776Skris break; 16166776Skris case '1': 16266776Skris once = 1; 16366776Skris break; 164118661Sume case 'O': 165118661Sume otherconf_script = optarg; 166118661Sume break; 167225520Shrs case 'p': 168222732Shrs pidfilename = optarg; 169222732Shrs break; 170222732Shrs case 'R': 171222732Shrs resolvconf_script = optarg; 172222732Shrs break; 173225520Shrs case 'u': 174225520Shrs uflag = 1; 175225520Shrs break; 17666776Skris default: 177222732Shrs usage(); 178222732Shrs exit(1); 17955163Sshin } 18055163Sshin } 18155163Sshin argc -= optind; 18255163Sshin argv += optind; 18366776Skris 184119026Sume if ((!aflag && argc == 0) || (aflag && argc != 0)) { 185222732Shrs usage(); 186222732Shrs exit(1); 18766776Skris } 18855163Sshin 189253970Shrs /* Generate maximum time in timespec. */ 190253995Shrs tm_max.tv_sec = (-1) & ~((time_t)1 << ((sizeof(tm_max.tv_sec) * 8) - 1)); 191253995Shrs tm_max.tv_nsec = (-1) & ~((long)1 << ((sizeof(tm_max.tv_nsec) * 8) - 1)); 192253970Shrs 19355163Sshin /* set log level */ 194225520Shrs if (dflag > 1) 195225520Shrs log_upto = LOG_DEBUG; 196225520Shrs else if (dflag > 0) 197225520Shrs log_upto = LOG_INFO; 198225520Shrs else 19955163Sshin log_upto = LOG_NOTICE; 200225520Shrs 20155163Sshin if (!fflag) { 20255163Sshin char *ident; 203118664Sume 204222848Shrs ident = strrchr(argv0, '/'); 20555163Sshin if (!ident) 206222848Shrs ident = argv0; 20755163Sshin else 20855163Sshin ident++; 20955163Sshin openlog(ident, LOG_NDELAY|LOG_PID, LOG_DAEMON); 21055163Sshin if (log_upto >= 0) 21155163Sshin setlogmask(LOG_UPTO(log_upto)); 21255163Sshin } 21355163Sshin 214118661Sume if (otherconf_script && *otherconf_script != '/') { 215118661Sume errx(1, "configuration script (%s) must be an absolute path", 216118661Sume otherconf_script); 217118661Sume } 218222732Shrs if (resolvconf_script && *resolvconf_script != '/') { 219222732Shrs errx(1, "configuration script (%s) must be an absolute path", 220222732Shrs resolvconf_script); 221222732Shrs } 222222732Shrs if (pidfilename && *pidfilename != '/') { 223222732Shrs errx(1, "pid filename (%s) must be an absolute path", 224222732Shrs pidfilename); 225222732Shrs } 22655163Sshin 227225520Shrs#if (__FreeBSD_version < 900000) 228124525Sume if (Fflag) { 229124525Sume setinet6sysctl(IPV6CTL_FORWARDING, 0); 230124525Sume } else { 231124525Sume /* warn if forwarding is up */ 232124525Sume if (getinet6sysctl(IPV6CTL_FORWARDING)) 233124525Sume warnx("kernel is configured as a router, not a host"); 234124525Sume } 235225520Shrs#endif 23655163Sshin 237124526Sume#ifndef SMALL 23855163Sshin /* initialization to dump internal status to a file */ 239118910Sume signal(SIGUSR1, rtsold_set_dump_file); 240124526Sume#endif 24155163Sshin 242118914Sume if (!fflag) 243118914Sume daemon(0, 0); /* act as a daemon */ 244118914Sume 24562632Skris /* 24662632Skris * Open a socket for sending RS and receiving RA. 24762632Skris * This should be done before calling ifinit(), since the function 24862632Skris * uses the socket. 24962632Skris */ 25066776Skris if ((s = sockopen()) < 0) { 251118914Sume warnmsg(LOG_ERR, __func__, "failed to open a socket"); 252118914Sume exit(1); 25366776Skris } 254118916Sume#ifdef HAVE_POLL_H 255118916Sume set[0].fd = s; 256118916Sume set[0].events = POLLIN; 257118916Sume#else 25878064Sume maxfd = s; 259118916Sume#endif 260118916Sume 261118916Sume#ifdef HAVE_POLL_H 262118916Sume set[1].fd = -1; 263118916Sume#endif 264118916Sume 26578064Sume if ((rtsock = rtsock_open()) < 0) { 266118914Sume warnmsg(LOG_ERR, __func__, "failed to open a socket"); 267118914Sume exit(1); 26878064Sume } 269118916Sume#ifdef HAVE_POLL_H 270118916Sume set[1].fd = rtsock; 271118916Sume set[1].events = POLLIN; 272118916Sume#else 27378064Sume if (rtsock > maxfd) 27478064Sume maxfd = rtsock; 275118916Sume#endif 27662632Skris 277118916Sume#ifndef HAVE_POLL_H 278118909Sume fdmasks = howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask); 279118909Sume if ((fdsetp = malloc(fdmasks)) == NULL) { 280222732Shrs warnmsg(LOG_ERR, __func__, "malloc"); 281222732Shrs exit(1); 282118909Sume } 283118909Sume if ((selectfdp = malloc(fdmasks)) == NULL) { 284222732Shrs warnmsg(LOG_ERR, __func__, "malloc"); 285222732Shrs exit(1); 286118909Sume } 287118916Sume#endif 288118909Sume 28955163Sshin /* configuration per interface */ 29066776Skris if (ifinit()) { 291118914Sume warnmsg(LOG_ERR, __func__, 292147161Ssuz "failed to initialize interfaces"); 293118914Sume exit(1); 29466776Skris } 295119026Sume if (aflag) 296119026Sume argv = autoifprobe(); 297119026Sume while (argv && *argv) { 29866776Skris if (ifconfig(*argv)) { 299118914Sume warnmsg(LOG_ERR, __func__, 300118914Sume "failed to initialize %s", *argv); 301118914Sume exit(1); 30266776Skris } 30355163Sshin argv++; 30455163Sshin } 30555163Sshin 30655163Sshin /* setup for probing default routers */ 30766776Skris if (probe_init()) { 308118914Sume warnmsg(LOG_ERR, __func__, 309118914Sume "failed to setup for probing routers"); 310118914Sume exit(1); 31166776Skris /*NOTREACHED*/ 31266776Skris } 31355163Sshin 31455163Sshin /* dump the current pid */ 31555163Sshin if (!once) { 31655163Sshin pid_t pid = getpid(); 31755163Sshin FILE *fp; 31855163Sshin 31955163Sshin if ((fp = fopen(pidfilename, "w")) == NULL) 320118660Sume warnmsg(LOG_ERR, __func__, 321118664Sume "failed to open a pid log file(%s): %s", 322118664Sume pidfilename, strerror(errno)); 32355163Sshin else { 32455163Sshin fprintf(fp, "%d\n", pid); 32555163Sshin fclose(fp); 32655163Sshin } 32755163Sshin } 328118916Sume#ifndef HAVE_POLL_H 329118909Sume memset(fdsetp, 0, fdmasks); 330118909Sume FD_SET(s, fdsetp); 331118909Sume FD_SET(rtsock, fdsetp); 332118916Sume#endif 33355163Sshin while (1) { /* main loop */ 33455163Sshin int e; 33555163Sshin 336118916Sume#ifndef HAVE_POLL_H 337118909Sume memcpy(selectfdp, fdsetp, fdmasks); 338118916Sume#endif 339118909Sume 340124526Sume#ifndef SMALL 34155163Sshin if (do_dump) { /* SIGUSR1 */ 34255163Sshin do_dump = 0; 34355163Sshin rtsold_dump_file(dumpfilename); 34455163Sshin } 345124526Sume#endif 346118664Sume 34755163Sshin timeout = rtsol_check_timer(); 34855163Sshin 34955163Sshin if (once) { 35055163Sshin struct ifinfo *ifi; 35155163Sshin 35255163Sshin /* if we have no timeout, we are done (or failed) */ 35355163Sshin if (timeout == NULL) 35455163Sshin break; 35555163Sshin 35655163Sshin /* if all interfaces have got RA packet, we are done */ 357222732Shrs TAILQ_FOREACH(ifi, &ifinfo_head, ifi_next) { 35855163Sshin if (ifi->state != IFS_DOWN && ifi->racnt == 0) 35955163Sshin break; 36055163Sshin } 36155163Sshin if (ifi == NULL) 36255163Sshin break; 36355163Sshin } 364118916Sume#ifdef HAVE_POLL_H 365253970Shrs e = poll(set, 2, timeout ? (timeout->tv_sec * 1000 + timeout->tv_nsec / 1000 / 1000) : INFTIM); 366118916Sume#else 367118909Sume e = select(maxfd + 1, selectfdp, NULL, NULL, timeout); 368118916Sume#endif 36978064Sume if (e < 1) { 37055163Sshin if (e < 0 && errno != EINTR) { 371118660Sume warnmsg(LOG_ERR, __func__, "select: %s", 372118664Sume strerror(errno)); 37355163Sshin } 37455163Sshin continue; 37555163Sshin } 37655163Sshin 37755163Sshin /* packet reception */ 378118916Sume#ifdef HAVE_POLL_H 379118916Sume if (set[1].revents & POLLIN) 380118916Sume#else 381118909Sume if (FD_ISSET(rtsock, selectfdp)) 382118916Sume#endif 38378064Sume rtsock_input(rtsock); 384118916Sume#ifdef HAVE_POLL_H 385118916Sume if (set[0].revents & POLLIN) 386118916Sume#else 387118909Sume if (FD_ISSET(s, selectfdp)) 388118916Sume#endif 38955163Sshin rtsol_input(s); 39055163Sshin } 39155163Sshin /* NOTREACHED */ 39255163Sshin 393222732Shrs return (0); 39455163Sshin} 39555163Sshin 396119026Sumeint 39755163Sshinifconfig(char *ifname) 39855163Sshin{ 399222732Shrs struct ifinfo *ifi; 40055163Sshin struct sockaddr_dl *sdl; 40155163Sshin int flags; 40255163Sshin 40355163Sshin if ((sdl = if_nametosdl(ifname)) == NULL) { 404118660Sume warnmsg(LOG_ERR, __func__, 405118664Sume "failed to get link layer information for %s", ifname); 406222732Shrs return (-1); 40755163Sshin } 40855163Sshin if (find_ifinfo(sdl->sdl_index)) { 409118660Sume warnmsg(LOG_ERR, __func__, 410118664Sume "interface %s was already configured", ifname); 41162632Skris free(sdl); 412222732Shrs return (-1); 41355163Sshin } 41455163Sshin 415225520Shrs if (Fflag) { 416225520Shrs struct in6_ndireq nd; 417225520Shrs int s; 418225520Shrs 419225520Shrs if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { 420225520Shrs warnmsg(LOG_ERR, __func__, "socket() failed."); 421225520Shrs return (-1); 422225520Shrs } 423225520Shrs memset(&nd, 0, sizeof(nd)); 424225520Shrs strlcpy(nd.ifname, ifname, sizeof(nd.ifname)); 425225520Shrs if (ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&nd) < 0) { 426225520Shrs warnmsg(LOG_ERR, __func__, 427225520Shrs "cannot get accept_rtadv flag"); 428225520Shrs close(s); 429225520Shrs return (-1); 430225520Shrs } 431225520Shrs nd.ndi.flags |= ND6_IFF_ACCEPT_RTADV; 432225520Shrs if (ioctl(s, SIOCSIFINFO_IN6, (caddr_t)&nd) < 0) { 433225520Shrs warnmsg(LOG_ERR, __func__, 434225520Shrs "cannot set accept_rtadv flag"); 435225520Shrs close(s); 436225520Shrs return (-1); 437225520Shrs } 438225520Shrs close(s); 439225520Shrs } 440225520Shrs 441222732Shrs if ((ifi = malloc(sizeof(*ifi))) == NULL) { 442118660Sume warnmsg(LOG_ERR, __func__, "memory allocation failed"); 44362632Skris free(sdl); 444222732Shrs return (-1); 44555163Sshin } 446222732Shrs memset(ifi, 0, sizeof(*ifi)); 447222732Shrs ifi->sdl = sdl; 448222861Shrs ifi->ifi_rdnss = IFI_DNSOPT_STATE_NOINFO; 449222861Shrs ifi->ifi_dnssl = IFI_DNSOPT_STATE_NOINFO; 450222861Shrs TAILQ_INIT(&ifi->ifi_rainfo); 451222732Shrs strlcpy(ifi->ifname, ifname, sizeof(ifi->ifname)); 45255163Sshin 45355163Sshin /* construct a router solicitation message */ 454222732Shrs if (make_packet(ifi)) 45555163Sshin goto bad; 45655163Sshin 457119026Sume /* set link ID of this interface. */ 458119026Sume#ifdef HAVE_SCOPELIB 459222732Shrs if (inet_zoneid(AF_INET6, 2, ifname, &ifi->linkid)) 460119026Sume goto bad; 461119026Sume#else 462119026Sume /* XXX: assume interface IDs as link IDs */ 463222732Shrs ifi->linkid = ifi->sdl->sdl_index; 464119026Sume#endif 465119026Sume 46655163Sshin /* 46755163Sshin * check if the interface is available. 46855163Sshin * also check if SIOCGIFMEDIA ioctl is OK on the interface. 46955163Sshin */ 470222732Shrs ifi->mediareqok = 1; 471222732Shrs ifi->active = interface_status(ifi); 472222732Shrs if (!ifi->mediareqok) { 47355163Sshin /* 47455163Sshin * probe routers periodically even if the link status 47555163Sshin * does not change. 47655163Sshin */ 477222732Shrs ifi->probeinterval = PROBE_INTERVAL; 47855163Sshin } 47955163Sshin 48055163Sshin /* activate interface: interface_up returns 0 on success */ 481222732Shrs flags = interface_up(ifi->ifname); 48255163Sshin if (flags == 0) 483222732Shrs ifi->state = IFS_DELAY; 48455163Sshin else if (flags == IFS_TENTATIVE) 485222732Shrs ifi->state = IFS_TENTATIVE; 48655163Sshin else 487222732Shrs ifi->state = IFS_DOWN; 48855163Sshin 489222732Shrs rtsol_timer_update(ifi); 49055163Sshin 491222732Shrs TAILQ_INSERT_TAIL(&ifinfo_head, ifi, ifi_next); 492222732Shrs return (0); 49355163Sshin 494118664Sumebad: 495222732Shrs free(ifi->sdl); 496222732Shrs free(ifi); 497222732Shrs return (-1); 49855163Sshin} 49955163Sshin 500119026Sumevoid 501124524Sumeiflist_init(void) 502119026Sume{ 503222732Shrs struct ifinfo *ifi; 504119026Sume 505222732Shrs while ((ifi = TAILQ_FIRST(&ifinfo_head)) != NULL) { 506222732Shrs TAILQ_REMOVE(&ifinfo_head, ifi, ifi_next); 507222732Shrs if (ifi->sdl != NULL) 508119026Sume free(ifi->sdl); 509222732Shrs if (ifi->rs_data != NULL) 510119026Sume free(ifi->rs_data); 511119026Sume free(ifi); 512119026Sume } 513119026Sume} 514119026Sume 51562632Skris#if 0 51662632Skrisstatic int 51762632Skrisifreconfig(char *ifname) 51862632Skris{ 51962632Skris struct ifinfo *ifi, *prev; 52062632Skris int rv; 52162632Skris 52262632Skris prev = NULL; 523222732Shrs TAILQ_FOREACH(ifi, &ifinfo_head, ifi_next) { 52462632Skris if (strncmp(ifi->ifname, ifname, sizeof(ifi->ifname)) == 0) 52562632Skris break; 52662632Skris prev = ifi; 52762632Skris } 52862632Skris prev->next = ifi->next; 52962632Skris 53062632Skris rv = ifconfig(ifname); 53162632Skris 53262632Skris /* reclaim it after ifconfig() in case ifname is pointer inside ifi */ 53362632Skris if (ifi->rs_data) 53462632Skris free(ifi->rs_data); 53562632Skris free(ifi->sdl); 53662632Skris free(ifi); 537222732Shrs 538222732Shrs return (rv); 53962632Skris} 54062632Skris#endif 54162632Skris 542222861Shrsstruct rainfo * 543222861Shrsfind_rainfo(struct ifinfo *ifi, struct sockaddr_in6 *sin6) 544222861Shrs{ 545222861Shrs struct rainfo *rai; 546222861Shrs 547222861Shrs TAILQ_FOREACH(rai, &ifi->ifi_rainfo, rai_next) 548222861Shrs if (memcmp(&rai->rai_saddr.sin6_addr, &sin6->sin6_addr, 549222861Shrs sizeof(rai->rai_saddr.sin6_addr)) == 0) 550222861Shrs return (rai); 551222861Shrs 552222861Shrs return (NULL); 553222861Shrs} 554222861Shrs 55555163Sshinstruct ifinfo * 55655163Sshinfind_ifinfo(int ifindex) 55755163Sshin{ 55855163Sshin struct ifinfo *ifi; 55955163Sshin 560222732Shrs TAILQ_FOREACH(ifi, &ifinfo_head, ifi_next) { 56155163Sshin if (ifi->sdl->sdl_index == ifindex) 562222732Shrs return (ifi); 563222732Shrs } 564222732Shrs return (NULL); 56555163Sshin} 56655163Sshin 56755163Sshinstatic int 568222732Shrsmake_packet(struct ifinfo *ifi) 56955163Sshin{ 570118664Sume size_t packlen = sizeof(struct nd_router_solicit), lladdroptlen = 0; 571118664Sume struct nd_router_solicit *rs; 57255163Sshin char *buf; 57355163Sshin 574222732Shrs if ((lladdroptlen = lladdropt_length(ifi->sdl)) == 0) { 575118660Sume warnmsg(LOG_INFO, __func__, 576118664Sume "link-layer address option has null length" 577222732Shrs " on %s. Treat as not included.", ifi->ifname); 57855163Sshin } 57955163Sshin packlen += lladdroptlen; 580222732Shrs ifi->rs_datalen = packlen; 58155163Sshin 58255163Sshin /* allocate buffer */ 58355163Sshin if ((buf = malloc(packlen)) == NULL) { 584118660Sume warnmsg(LOG_ERR, __func__, 585222732Shrs "memory allocation failed for %s", ifi->ifname); 586222732Shrs return (-1); 58755163Sshin } 588222732Shrs ifi->rs_data = buf; 58955163Sshin 59055163Sshin /* fill in the message */ 59155163Sshin rs = (struct nd_router_solicit *)buf; 59255163Sshin rs->nd_rs_type = ND_ROUTER_SOLICIT; 59355163Sshin rs->nd_rs_code = 0; 59455163Sshin rs->nd_rs_cksum = 0; 59555163Sshin rs->nd_rs_reserved = 0; 59655163Sshin buf += sizeof(*rs); 59755163Sshin 59855163Sshin /* fill in source link-layer address option */ 59955163Sshin if (lladdroptlen) 600222732Shrs lladdropt_fill(ifi->sdl, (struct nd_opt_hdr *)buf); 60155163Sshin 602222732Shrs return (0); 60355163Sshin} 60455163Sshin 605253970Shrsstatic struct timespec * 606124524Sumertsol_check_timer(void) 60755163Sshin{ 608253970Shrs static struct timespec returnval; 609253970Shrs struct timespec now, rtsol_timer; 610222732Shrs struct ifinfo *ifi; 611222861Shrs struct rainfo *rai; 612295898Smarkj struct ra_opt *rao, *raotmp; 61355163Sshin int flags; 61455163Sshin 615253970Shrs clock_gettime(CLOCK_MONOTONIC_FAST, &now); 61655163Sshin 61755163Sshin rtsol_timer = tm_max; 61855163Sshin 619222732Shrs TAILQ_FOREACH(ifi, &ifinfo_head, ifi_next) { 620253970Shrs if (TS_CMP(&ifi->expire, &now, <=)) { 621222861Shrs warnmsg(LOG_DEBUG, __func__, "timer expiration on %s, " 622222861Shrs "state = %d", ifi->ifname, ifi->state); 62355163Sshin 624222861Shrs while((rai = TAILQ_FIRST(&ifi->ifi_rainfo)) != NULL) { 625222861Shrs /* Remove all RA options. */ 626222861Shrs TAILQ_REMOVE(&ifi->ifi_rainfo, rai, rai_next); 627222861Shrs while ((rao = TAILQ_FIRST(&rai->rai_ra_opt)) != 628222861Shrs NULL) { 629222861Shrs TAILQ_REMOVE(&rai->rai_ra_opt, rao, 630222861Shrs rao_next); 631222861Shrs if (rao->rao_msg != NULL) 632222861Shrs free(rao->rao_msg); 633222861Shrs free(rao); 634222861Shrs } 635222861Shrs free(rai); 636222732Shrs } 637222732Shrs switch (ifi->state) { 63855163Sshin case IFS_DOWN: 63955163Sshin case IFS_TENTATIVE: 64055163Sshin /* interface_up returns 0 on success */ 641222732Shrs flags = interface_up(ifi->ifname); 64255163Sshin if (flags == 0) 643222732Shrs ifi->state = IFS_DELAY; 64455163Sshin else if (flags == IFS_TENTATIVE) 645222732Shrs ifi->state = IFS_TENTATIVE; 64655163Sshin else 647222732Shrs ifi->state = IFS_DOWN; 64855163Sshin break; 64955163Sshin case IFS_IDLE: 65055163Sshin { 651222732Shrs int oldstatus = ifi->active; 65255163Sshin int probe = 0; 65355163Sshin 654222732Shrs ifi->active = interface_status(ifi); 65555163Sshin 656222732Shrs if (oldstatus != ifi->active) { 657118660Sume warnmsg(LOG_DEBUG, __func__, 658118664Sume "%s status is changed" 659118664Sume " from %d to %d", 660222732Shrs ifi->ifname, 661222732Shrs oldstatus, ifi->active); 66255163Sshin probe = 1; 663222732Shrs ifi->state = IFS_DELAY; 664222732Shrs } else if (ifi->probeinterval && 665222732Shrs (ifi->probetimer -= 666222732Shrs ifi->timer.tv_sec) <= 0) { 66755163Sshin /* probe timer expired */ 668222732Shrs ifi->probetimer = 669222732Shrs ifi->probeinterval; 67055163Sshin probe = 1; 671222732Shrs ifi->state = IFS_PROBE; 67255163Sshin } 67355163Sshin 674118661Sume /* 675118661Sume * If we need a probe, clear the previous 676118661Sume * status wrt the "other" configuration. 677118661Sume */ 678118661Sume if (probe) 679222732Shrs ifi->otherconfig = 0; 680118661Sume 68155163Sshin if (probe && mobile_node) 682222732Shrs defrouter_probe(ifi); 68355163Sshin break; 68455163Sshin } 68555163Sshin case IFS_DELAY: 686222732Shrs ifi->state = IFS_PROBE; 687222732Shrs sendpacket(ifi); 68855163Sshin break; 68955163Sshin case IFS_PROBE: 690222732Shrs if (ifi->probes < MAX_RTR_SOLICITATIONS) 691222732Shrs sendpacket(ifi); 69255163Sshin else { 693118660Sume warnmsg(LOG_INFO, __func__, 694118664Sume "No answer after sending %d RSs", 695222732Shrs ifi->probes); 696222732Shrs ifi->probes = 0; 697222732Shrs ifi->state = IFS_IDLE; 69855163Sshin } 69955163Sshin break; 70055163Sshin } 701222732Shrs rtsol_timer_update(ifi); 702222732Shrs } else { 703222732Shrs /* Expiration check for RA options. */ 704222732Shrs int expire = 0; 705222732Shrs 706222861Shrs TAILQ_FOREACH(rai, &ifi->ifi_rainfo, rai_next) { 707295898Smarkj TAILQ_FOREACH_SAFE(rao, &rai->rai_ra_opt, 708295898Smarkj rao_next, raotmp) { 709222732Shrs warnmsg(LOG_DEBUG, __func__, 710222861Shrs "RA expiration timer: " 711222861Shrs "type=%d, msg=%s, expire=%s", 712222861Shrs rao->rao_type, (char *)rao->rao_msg, 713222861Shrs sec2str(&rao->rao_expire)); 714253970Shrs if (TS_CMP(&now, &rao->rao_expire, 715222861Shrs >=)) { 716222861Shrs warnmsg(LOG_DEBUG, __func__, 717222861Shrs "RA expiration timer: " 718222861Shrs "expired."); 719222861Shrs TAILQ_REMOVE(&rai->rai_ra_opt, 720222861Shrs rao, rao_next); 721222861Shrs if (rao->rao_msg != NULL) 722222861Shrs free(rao->rao_msg); 723222861Shrs free(rao); 724222861Shrs expire = 1; 725222861Shrs } 726222732Shrs } 727222732Shrs } 728222732Shrs if (expire) 729222732Shrs ra_opt_handler(ifi); 73055163Sshin } 731253970Shrs if (TS_CMP(&ifi->expire, &rtsol_timer, <)) 732222732Shrs rtsol_timer = ifi->expire; 73355163Sshin } 73455163Sshin 735253970Shrs if (TS_CMP(&rtsol_timer, &tm_max, ==)) { 736118660Sume warnmsg(LOG_DEBUG, __func__, "there is no timer"); 737222732Shrs return (NULL); 738253970Shrs } else if (TS_CMP(&rtsol_timer, &now, <)) 73955163Sshin /* this may occur when the interval is too small */ 740253970Shrs returnval.tv_sec = returnval.tv_nsec = 0; 74155163Sshin else 742253970Shrs TS_SUB(&rtsol_timer, &now, &returnval); 74355163Sshin 744222861Shrs now.tv_sec += returnval.tv_sec; 745253970Shrs now.tv_nsec += returnval.tv_nsec; 746222861Shrs warnmsg(LOG_DEBUG, __func__, "New timer is %s", 747222861Shrs sec2str(&now)); 74855163Sshin 749222732Shrs return (&returnval); 75055163Sshin} 75155163Sshin 75255163Sshinvoid 753222732Shrsrtsol_timer_update(struct ifinfo *ifi) 75455163Sshin{ 75562632Skris#define MILLION 1000000 75662632Skris#define DADRETRY 10 /* XXX: adhoc */ 75755163Sshin long interval; 758253970Shrs struct timespec now; 75955163Sshin 760222732Shrs bzero(&ifi->timer, sizeof(ifi->timer)); 76155163Sshin 762222732Shrs switch (ifi->state) { 76355163Sshin case IFS_DOWN: 76455163Sshin case IFS_TENTATIVE: 765222732Shrs if (++ifi->dadcount > DADRETRY) { 766222732Shrs ifi->dadcount = 0; 767222732Shrs ifi->timer.tv_sec = PROBE_INTERVAL; 768118664Sume } else 769222732Shrs ifi->timer.tv_sec = 1; 77055163Sshin break; 77155163Sshin case IFS_IDLE: 77255163Sshin if (mobile_node) { 773118664Sume /* XXX should be configurable */ 774222732Shrs ifi->timer.tv_sec = 3; 77555163Sshin } 77655163Sshin else 777222732Shrs ifi->timer = tm_max; /* stop timer(valid?) */ 77855163Sshin break; 77955163Sshin case IFS_DELAY: 780180824Sache interval = arc4random_uniform(MAX_RTR_SOLICITATION_DELAY * MILLION); 781222732Shrs ifi->timer.tv_sec = interval / MILLION; 782253970Shrs ifi->timer.tv_nsec = (interval % MILLION) * 1000; 78355163Sshin break; 78455163Sshin case IFS_PROBE: 785222732Shrs if (ifi->probes < MAX_RTR_SOLICITATIONS) 786222732Shrs ifi->timer.tv_sec = RTR_SOLICITATION_INTERVAL; 78778064Sume else { 78878064Sume /* 78978064Sume * After sending MAX_RTR_SOLICITATIONS solicitations, 79078064Sume * we're just waiting for possible replies; there 791147150Ssuz * will be no more solicitation. Thus, we change 79278064Sume * the timer value to MAX_RTR_SOLICITATION_DELAY based 79378064Sume * on RFC 2461, Section 6.3.7. 79478064Sume */ 795222732Shrs ifi->timer.tv_sec = MAX_RTR_SOLICITATION_DELAY; 79678064Sume } 79755163Sshin break; 79855163Sshin default: 799118660Sume warnmsg(LOG_ERR, __func__, 800118664Sume "illegal interface state(%d) on %s", 801222732Shrs ifi->state, ifi->ifname); 80255163Sshin return; 80355163Sshin } 80455163Sshin 80555163Sshin /* reset the timer */ 806253970Shrs if (TS_CMP(&ifi->timer, &tm_max, ==)) { 807222732Shrs ifi->expire = tm_max; 808118660Sume warnmsg(LOG_DEBUG, __func__, 809222732Shrs "stop timer for %s", ifi->ifname); 810118664Sume } else { 811253970Shrs clock_gettime(CLOCK_MONOTONIC_FAST, &now); 812253970Shrs TS_ADD(&now, &ifi->timer, &ifi->expire); 81355163Sshin 814222861Shrs now.tv_sec += ifi->timer.tv_sec; 815253970Shrs now.tv_nsec += ifi->timer.tv_nsec; 816222861Shrs warnmsg(LOG_DEBUG, __func__, "set timer for %s to %s", 817222861Shrs ifi->ifname, sec2str(&now)); 81855163Sshin } 81955163Sshin 82055163Sshin#undef MILLION 82155163Sshin} 82255163Sshin 82355163Sshin/* timer related utility functions */ 82462632Skris#define MILLION 1000000 82555163Sshin 826124526Sume#ifndef SMALL 82755163Sshinstatic void 828204407Suqsrtsold_set_dump_file(int sig __unused) 82955163Sshin{ 83055163Sshin do_dump = 1; 83155163Sshin} 832124526Sume#endif 83355163Sshin 83455163Sshinstatic void 835222732Shrsusage(void) 83655163Sshin{ 837222732Shrs#ifndef SMALL 838222732Shrs fprintf(stderr, "usage: rtsold [-dDfFm1] [-O script-name] " 839290576Sngie "[-p pidfile] [-R script-name] interface ...\n"); 840290576Sngie fprintf(stderr, "usage: rtsold [-dDfFm1] [-O script-name] " 841290576Sngie "[-p pidfile] [-R script-name] -a\n"); 842222732Shrs#else 843222732Shrs fprintf(stderr, "usage: rtsol [-dDF] [-O script-name] " 844290576Sngie "[-p pidfile] [-R script-name] interface ...\n"); 845222732Shrs fprintf(stderr, "usage: rtsol [-dDF] [-O script-name] " 846290576Sngie "[-p pidfile] [-R script-name] -a\n"); 847222732Shrs#endif 84855163Sshin} 84955163Sshin 85055163Sshinvoid 85155163Sshinwarnmsg(int priority, const char *func, const char *msg, ...) 85255163Sshin{ 85355163Sshin va_list ap; 85455163Sshin char buf[BUFSIZ]; 85555163Sshin 85655163Sshin va_start(ap, msg); 85755163Sshin if (fflag) { 85855163Sshin if (priority <= log_upto) { 85955163Sshin (void)vfprintf(stderr, msg, ap); 86055163Sshin (void)fprintf(stderr, "\n"); 86155163Sshin } 86255163Sshin } else { 86355163Sshin snprintf(buf, sizeof(buf), "<%s> %s", func, msg); 86466776Skris msg = buf; 86566776Skris vsyslog(priority, msg, ap); 86655163Sshin } 86755163Sshin va_end(ap); 86855163Sshin} 86966776Skris 870119026Sume/* 871119026Sume * return a list of interfaces which is suitable to sending an RS. 872119026Sume */ 873119026Sumechar ** 874124524Sumeautoifprobe(void) 87566776Skris{ 876119026Sume static char **argv = NULL; 877119026Sume static int n = 0; 878119026Sume char **a; 879204407Suqs int s = 0, i, found; 880230357Seadler struct ifaddrs *ifap, *ifa; 881203387Sume struct in6_ndireq nd; 88266776Skris 883119026Sume /* initialize */ 884119026Sume while (n--) 885119026Sume free(argv[n]); 886119026Sume if (argv) { 887119026Sume free(argv); 888119026Sume argv = NULL; 889119026Sume } 890119026Sume n = 0; 891119026Sume 89266776Skris if (getifaddrs(&ifap) != 0) 893222732Shrs return (NULL); 89466776Skris 895203387Sume if (!Fflag && (s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { 896222732Shrs warnmsg(LOG_ERR, __func__, "socket"); 897222732Shrs exit(1); 898203387Sume } 899203387Sume 90066776Skris /* find an ethernet */ 90166776Skris for (ifa = ifap; ifa; ifa = ifa->ifa_next) { 90266776Skris if ((ifa->ifa_flags & IFF_UP) == 0) 90366776Skris continue; 90466776Skris if ((ifa->ifa_flags & IFF_POINTOPOINT) != 0) 90566776Skris continue; 90666776Skris if ((ifa->ifa_flags & IFF_LOOPBACK) != 0) 90766776Skris continue; 90866776Skris if ((ifa->ifa_flags & IFF_MULTICAST) == 0) 90966776Skris continue; 91066776Skris 91166776Skris if (ifa->ifa_addr->sa_family != AF_INET6) 91266776Skris continue; 91366776Skris 914119026Sume found = 0; 915119026Sume for (i = 0; i < n; i++) { 916119026Sume if (strcmp(argv[i], ifa->ifa_name) == 0) { 917119026Sume found++; 918119026Sume break; 919119026Sume } 920119026Sume } 921119026Sume if (found) 92266776Skris continue; 92366776Skris 924203387Sume /* 925203387Sume * Skip the interfaces which IPv6 and/or accepting RA 926203387Sume * is disabled. 927203387Sume */ 928203387Sume if (!Fflag) { 929203387Sume memset(&nd, 0, sizeof(nd)); 930203387Sume strlcpy(nd.ifname, ifa->ifa_name, sizeof(nd.ifname)); 931203387Sume if (ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&nd) < 0) { 932222732Shrs warnmsg(LOG_ERR, __func__, 933222732Shrs "ioctl(SIOCGIFINFO_IN6)"); 934222732Shrs exit(1); 935203387Sume } 936203387Sume if ((nd.ndi.flags & ND6_IFF_IFDISABLED)) 937203387Sume continue; 938203387Sume if (!(nd.ndi.flags & ND6_IFF_ACCEPT_RTADV)) 939203387Sume continue; 940203387Sume } 941203387Sume 942119026Sume /* if we find multiple candidates, just warn. */ 943119026Sume if (n != 0 && dflag > 1) 944222732Shrs warnmsg(LOG_WARNING, __func__, 945222732Shrs "multiple interfaces found"); 946119026Sume 947119026Sume a = (char **)realloc(argv, (n + 1) * sizeof(char **)); 948222732Shrs if (a == NULL) { 949222732Shrs warnmsg(LOG_ERR, __func__, "realloc"); 950222732Shrs exit(1); 951222732Shrs } 952119026Sume argv = a; 953119026Sume argv[n] = strdup(ifa->ifa_name); 954222732Shrs if (!argv[n]) { 955222732Shrs warnmsg(LOG_ERR, __func__, "malloc"); 956222732Shrs exit(1); 957222732Shrs } 958119026Sume n++; 95966776Skris } 96066776Skris 961119026Sume if (n) { 962119026Sume a = (char **)realloc(argv, (n + 1) * sizeof(char **)); 963222732Shrs if (a == NULL) { 964222732Shrs warnmsg(LOG_ERR, __func__, "realloc"); 965222732Shrs exit(1); 966222732Shrs } 967119026Sume argv = a; 968119026Sume argv[n] = NULL; 96966776Skris 970119026Sume if (dflag > 0) { 971119026Sume for (i = 0; i < n; i++) 972222732Shrs warnmsg(LOG_WARNING, __func__, "probing %s", 973222732Shrs argv[i]); 974119026Sume } 97566776Skris } 976203387Sume if (!Fflag) 977203387Sume close(s); 97866776Skris freeifaddrs(ifap); 979222732Shrs return (argv); 98066776Skris} 981