rtsold.c revision 173412
1295016Sjkim/* $KAME: rtsold.c,v 1.67 2003/05/17 18:16:15 itojun Exp $ */ 2238384Sjkim 3238384Sjkim/* 4238384Sjkim * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 5238384Sjkim * All rights reserved. 6238384Sjkim * 7238384Sjkim * Redistribution and use in source and binary forms, with or without 8238384Sjkim * modification, are permitted provided that the following conditions 9238384Sjkim * are met: 10238384Sjkim * 1. Redistributions of source code must retain the above copyright 11238384Sjkim * notice, this list of conditions and the following disclaimer. 12238384Sjkim * 2. Redistributions in binary form must reproduce the above copyright 13238384Sjkim * notice, this list of conditions and the following disclaimer in the 14238384Sjkim * documentation and/or other materials provided with the distribution. 15238384Sjkim * 3. Neither the name of the project nor the names of its contributors 16238384Sjkim * may be used to endorse or promote products derived from this software 17238384Sjkim * without specific prior written permission. 18238384Sjkim * 19238384Sjkim * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 20238384Sjkim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21238384Sjkim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22238384Sjkim * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 23238384Sjkim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24238384Sjkim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25238384Sjkim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26238384Sjkim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27238384Sjkim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28238384Sjkim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29238384Sjkim * SUCH DAMAGE. 30238384Sjkim * 31238384Sjkim * $FreeBSD: head/usr.sbin/rtsold/rtsold.c 173412 2007-11-07 10:53:41Z kevlo $ 32238384Sjkim */ 33238384Sjkim 34280304Sjkim#include <sys/types.h> 35238384Sjkim#include <sys/time.h> 36238384Sjkim#include <sys/socket.h> 37238384Sjkim#include <sys/param.h> 38238384Sjkim 39238384Sjkim#include <net/if.h> 40238384Sjkim#include <net/if_dl.h> 41238384Sjkim 42238384Sjkim#include <netinet/in.h> 43238384Sjkim#include <netinet/icmp6.h> 44238384Sjkim 45238384Sjkim#include <signal.h> 46238384Sjkim#include <unistd.h> 47238384Sjkim#include <syslog.h> 48238384Sjkim#include <string.h> 49238384Sjkim#include <stdlib.h> 50238384Sjkim#include <stdio.h> 51238384Sjkim#include <errno.h> 52238384Sjkim#include <err.h> 53238384Sjkim#include <stdarg.h> 54238384Sjkim#include <ifaddrs.h> 55238384Sjkim#ifdef HAVE_POLL_H 56238384Sjkim#include <poll.h> 57238384Sjkim#endif 58238384Sjkim 59238384Sjkim#include "rtsold.h" 60238384Sjkim 61238384Sjkimstruct ifinfo *iflist; 62238384Sjkimstruct timeval tm_max = {0x7fffffff, 0x7fffffff}; 63238384Sjkimstatic int log_upto = 999; 64280304Sjkimstatic int fflag = 0; 65280304Sjkimstatic int Fflag = 0; /* force setting sysctl parameters */ 66280304Sjkim 67238384Sjkimint aflag = 0; 68280304Sjkimint dflag = 0; 69280304Sjkim 70238384Sjkimchar *otherconf_script; 71280304Sjkim 72238384Sjkim/* protocol constants */ 73238384Sjkim#define MAX_RTR_SOLICITATION_DELAY 1 /* second */ 74238384Sjkim#define RTR_SOLICITATION_INTERVAL 4 /* seconds */ 75238384Sjkim#define MAX_RTR_SOLICITATIONS 3 /* times */ 76238384Sjkim 77238384Sjkim/* 78238384Sjkim * implementation dependent constants in seconds 79238384Sjkim * XXX: should be configurable 80238384Sjkim */ 81238384Sjkim#define PROBE_INTERVAL 60 82280304Sjkim 83238384Sjkimint main(int, char **); 84238384Sjkim 85280304Sjkim/* static variables and functions */ 86238384Sjkimstatic int mobile_node = 0; 87238384Sjkim#ifndef SMALL 88280304Sjkimstatic int do_dump; 89238384Sjkimstatic char *dumpfilename = "/var/run/rtsold.dump"; /* XXX: should be configurable */ 90238384Sjkim#endif 91238384Sjkim#if 1 92238384Sjkimstatic char *pidfilename = "/var/run/rtsold.pid"; /* should be configurable */ 93238384Sjkim#endif 94238384Sjkim 95238384Sjkim#if 0 96280304Sjkimstatic int ifreconfig(char *); 97280304Sjkim#endif 98280304Sjkimstatic int make_packet(struct ifinfo *); 99280304Sjkimstatic struct timeval *rtsol_check_timer(void); 100280304Sjkim 101280304Sjkim#ifndef SMALL 102280304Sjkimstatic void rtsold_set_dump_file(int); 103280304Sjkim#endif 104238384Sjkimstatic void usage(char *); 105238384Sjkim 106280304Sjkimint 107238384Sjkimmain(int argc, char **argv) 108238384Sjkim{ 109238384Sjkim int s, ch, once = 0; 110238384Sjkim struct timeval *timeout; 111238384Sjkim char *argv0, *opts; 112238384Sjkim#ifdef HAVE_POLL_H 113238384Sjkim struct pollfd set[2]; 114238384Sjkim#else 115238384Sjkim fd_set *fdsetp, *selectfdp; 116238384Sjkim int fdmasks; 117280304Sjkim int maxfd; 118238384Sjkim#endif 119238384Sjkim int rtsock; 120238384Sjkim 121238384Sjkim /* 122238384Sjkim * Initialization 123238384Sjkim */ 124238384Sjkim argv0 = argv[0]; 125238384Sjkim 126238384Sjkim /* get option */ 127238384Sjkim if (argv0 && argv0[strlen(argv0) - 1] != 'd') { 128238384Sjkim fflag = 1; 129238384Sjkim once = 1; 130238384Sjkim opts = "adDFO:"; 131238384Sjkim } else 132238384Sjkim opts = "adDfFm1O:"; 133238384Sjkim 134238384Sjkim while ((ch = getopt(argc, argv, opts)) != -1) { 135238384Sjkim switch (ch) { 136238384Sjkim case 'a': 137238384Sjkim aflag = 1; 138238384Sjkim break; 139238384Sjkim case 'd': 140238384Sjkim dflag = 1; 141238384Sjkim break; 142238384Sjkim case 'D': 143238384Sjkim dflag = 2; 144238384Sjkim break; 145238384Sjkim case 'f': 146238384Sjkim fflag = 1; 147238384Sjkim break; 148238384Sjkim case 'F': 149238384Sjkim Fflag = 1; 150238384Sjkim break; 151238384Sjkim case 'm': 152238384Sjkim mobile_node = 1; 153238384Sjkim break; 154238384Sjkim case '1': 155238384Sjkim once = 1; 156238384Sjkim break; 157238384Sjkim case 'O': 158238384Sjkim otherconf_script = optarg; 159238384Sjkim break; 160238384Sjkim default: 161238384Sjkim usage(argv0); 162238384Sjkim /*NOTREACHED*/ 163238384Sjkim } 164238384Sjkim } 165238384Sjkim argc -= optind; 166238384Sjkim argv += optind; 167238384Sjkim 168238384Sjkim if ((!aflag && argc == 0) || (aflag && argc != 0)) { 169238384Sjkim usage(argv0); 170238384Sjkim /*NOTREACHED*/ 171238384Sjkim } 172238384Sjkim 173238384Sjkim /* set log level */ 174238384Sjkim if (dflag == 0) 175238384Sjkim log_upto = LOG_NOTICE; 176238384Sjkim if (!fflag) { 177238384Sjkim char *ident; 178238384Sjkim 179238384Sjkim ident = strrchr(argv0, '/'); 180238384Sjkim if (!ident) 181238384Sjkim ident = argv0; 182238384Sjkim else 183238384Sjkim ident++; 184238384Sjkim openlog(ident, LOG_NDELAY|LOG_PID, LOG_DAEMON); 185238384Sjkim if (log_upto >= 0) 186238384Sjkim setlogmask(LOG_UPTO(log_upto)); 187238384Sjkim } 188238384Sjkim 189238384Sjkim if (otherconf_script && *otherconf_script != '/') { 190238384Sjkim errx(1, "configuration script (%s) must be an absolute path", 191238384Sjkim otherconf_script); 192238384Sjkim } 193238384Sjkim 194238384Sjkim#ifndef HAVE_ARC4RANDOM 195238384Sjkim /* random value initialization */ 196238384Sjkim srandom((u_long)time(NULL)); 197238384Sjkim#endif 198238384Sjkim 199238384Sjkim if (Fflag) { 200238384Sjkim setinet6sysctl(IPV6CTL_ACCEPT_RTADV, 1); 201238384Sjkim setinet6sysctl(IPV6CTL_FORWARDING, 0); 202238384Sjkim } else { 203238384Sjkim /* warn if accept_rtadv is down */ 204238384Sjkim if (!getinet6sysctl(IPV6CTL_ACCEPT_RTADV)) 205238384Sjkim warnx("kernel is configured not to accept RAs"); 206238384Sjkim /* warn if forwarding is up */ 207238384Sjkim if (getinet6sysctl(IPV6CTL_FORWARDING)) 208238384Sjkim warnx("kernel is configured as a router, not a host"); 209238384Sjkim } 210238384Sjkim 211238384Sjkim#ifndef SMALL 212238384Sjkim /* initialization to dump internal status to a file */ 213238384Sjkim signal(SIGUSR1, rtsold_set_dump_file); 214238384Sjkim#endif 215238384Sjkim 216238384Sjkim if (!fflag) 217238384Sjkim daemon(0, 0); /* act as a daemon */ 218238384Sjkim 219238384Sjkim /* 220238384Sjkim * Open a socket for sending RS and receiving RA. 221238384Sjkim * This should be done before calling ifinit(), since the function 222238384Sjkim * uses the socket. 223238384Sjkim */ 224238384Sjkim if ((s = sockopen()) < 0) { 225238384Sjkim warnmsg(LOG_ERR, __func__, "failed to open a socket"); 226238384Sjkim exit(1); 227238384Sjkim /*NOTREACHED*/ 228238384Sjkim } 229238384Sjkim#ifdef HAVE_POLL_H 230238384Sjkim set[0].fd = s; 231238384Sjkim set[0].events = POLLIN; 232238384Sjkim#else 233238384Sjkim maxfd = s; 234238384Sjkim#endif 235238384Sjkim 236238384Sjkim#ifdef HAVE_POLL_H 237238384Sjkim set[1].fd = -1; 238238384Sjkim#endif 239238384Sjkim 240238384Sjkim if ((rtsock = rtsock_open()) < 0) { 241238384Sjkim warnmsg(LOG_ERR, __func__, "failed to open a socket"); 242238384Sjkim exit(1); 243238384Sjkim /*NOTREACHED*/ 244238384Sjkim } 245238384Sjkim#ifdef HAVE_POLL_H 246238384Sjkim set[1].fd = rtsock; 247238384Sjkim set[1].events = POLLIN; 248238384Sjkim#else 249238384Sjkim if (rtsock > maxfd) 250238384Sjkim maxfd = rtsock; 251238384Sjkim#endif 252238384Sjkim 253238384Sjkim#ifndef HAVE_POLL_H 254238384Sjkim fdmasks = howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask); 255238384Sjkim if ((fdsetp = malloc(fdmasks)) == NULL) { 256238384Sjkim err(1, "malloc"); 257238384Sjkim /*NOTREACHED*/ 258238384Sjkim } 259238384Sjkim if ((selectfdp = malloc(fdmasks)) == NULL) { 260238384Sjkim err(1, "malloc"); 261238384Sjkim /*NOTREACHED*/ 262238384Sjkim } 263238384Sjkim#endif 264238384Sjkim 265238384Sjkim /* configuration per interface */ 266238384Sjkim if (ifinit()) { 267238384Sjkim warnmsg(LOG_ERR, __func__, 268238384Sjkim "failed to initialize interfaces"); 269238384Sjkim exit(1); 270238384Sjkim /*NOTREACHED*/ 271238384Sjkim } 272238384Sjkim if (aflag) 273238384Sjkim argv = autoifprobe(); 274238384Sjkim while (argv && *argv) { 275238384Sjkim if (ifconfig(*argv)) { 276238384Sjkim warnmsg(LOG_ERR, __func__, 277238384Sjkim "failed to initialize %s", *argv); 278238384Sjkim exit(1); 279238384Sjkim /*NOTREACHED*/ 280238384Sjkim } 281238384Sjkim argv++; 282238384Sjkim } 283238384Sjkim 284238384Sjkim /* setup for probing default routers */ 285238384Sjkim if (probe_init()) { 286238384Sjkim warnmsg(LOG_ERR, __func__, 287238384Sjkim "failed to setup for probing routers"); 288238384Sjkim exit(1); 289238384Sjkim /*NOTREACHED*/ 290238384Sjkim } 291238384Sjkim 292238384Sjkim#if 1 293238384Sjkim /* dump the current pid */ 294238384Sjkim if (!once) { 295238384Sjkim pid_t pid = getpid(); 296238384Sjkim FILE *fp; 297238384Sjkim 298238384Sjkim if ((fp = fopen(pidfilename, "w")) == NULL) 299238384Sjkim warnmsg(LOG_ERR, __func__, 300238384Sjkim "failed to open a pid log file(%s): %s", 301238384Sjkim pidfilename, strerror(errno)); 302238384Sjkim else { 303238384Sjkim fprintf(fp, "%d\n", pid); 304238384Sjkim fclose(fp); 305238384Sjkim } 306238384Sjkim } 307238384Sjkim#endif 308238384Sjkim 309238384Sjkim#ifndef HAVE_POLL_H 310238384Sjkim memset(fdsetp, 0, fdmasks); 311238384Sjkim FD_SET(s, fdsetp); 312238384Sjkim FD_SET(rtsock, fdsetp); 313238384Sjkim#endif 314238384Sjkim while (1) { /* main loop */ 315238384Sjkim int e; 316238384Sjkim 317238384Sjkim#ifndef HAVE_POLL_H 318238384Sjkim memcpy(selectfdp, fdsetp, fdmasks); 319238384Sjkim#endif 320238384Sjkim 321238384Sjkim#ifndef SMALL 322238384Sjkim if (do_dump) { /* SIGUSR1 */ 323238384Sjkim do_dump = 0; 324238384Sjkim rtsold_dump_file(dumpfilename); 325238384Sjkim } 326238384Sjkim#endif 327238384Sjkim 328238384Sjkim timeout = rtsol_check_timer(); 329238384Sjkim 330238384Sjkim if (once) { 331238384Sjkim struct ifinfo *ifi; 332238384Sjkim 333238384Sjkim /* if we have no timeout, we are done (or failed) */ 334238384Sjkim if (timeout == NULL) 335238384Sjkim break; 336238384Sjkim 337238384Sjkim /* if all interfaces have got RA packet, we are done */ 338238384Sjkim for (ifi = iflist; ifi; ifi = ifi->next) { 339238384Sjkim if (ifi->state != IFS_DOWN && ifi->racnt == 0) 340238384Sjkim break; 341238384Sjkim } 342238384Sjkim if (ifi == NULL) 343238384Sjkim break; 344238384Sjkim } 345238384Sjkim#ifdef HAVE_POLL_H 346238384Sjkim e = poll(set, 2, timeout ? (timeout->tv_sec * 1000 + timeout->tv_usec / 1000) : INFTIM); 347238384Sjkim#else 348238384Sjkim e = select(maxfd + 1, selectfdp, NULL, NULL, timeout); 349238384Sjkim#endif 350238384Sjkim if (e < 1) { 351238384Sjkim if (e < 0 && errno != EINTR) { 352238384Sjkim warnmsg(LOG_ERR, __func__, "select: %s", 353238384Sjkim strerror(errno)); 354238384Sjkim } 355238384Sjkim continue; 356238384Sjkim } 357238384Sjkim 358238384Sjkim /* packet reception */ 359238384Sjkim#ifdef HAVE_POLL_H 360238384Sjkim if (set[1].revents & POLLIN) 361238384Sjkim#else 362238384Sjkim if (FD_ISSET(rtsock, selectfdp)) 363238384Sjkim#endif 364238384Sjkim rtsock_input(rtsock); 365238384Sjkim#ifdef HAVE_POLL_H 366238384Sjkim if (set[0].revents & POLLIN) 367238384Sjkim#else 368238384Sjkim if (FD_ISSET(s, selectfdp)) 369238384Sjkim#endif 370238384Sjkim rtsol_input(s); 371238384Sjkim } 372238384Sjkim /* NOTREACHED */ 373238384Sjkim 374238384Sjkim return 0; 375238384Sjkim} 376238384Sjkim 377238384Sjkimint 378238384Sjkimifconfig(char *ifname) 379238384Sjkim{ 380238384Sjkim struct ifinfo *ifinfo; 381238384Sjkim struct sockaddr_dl *sdl; 382238384Sjkim int flags; 383238384Sjkim 384238384Sjkim if ((sdl = if_nametosdl(ifname)) == NULL) { 385238384Sjkim warnmsg(LOG_ERR, __func__, 386238384Sjkim "failed to get link layer information for %s", ifname); 387238384Sjkim return(-1); 388238384Sjkim } 389238384Sjkim if (find_ifinfo(sdl->sdl_index)) { 390238384Sjkim warnmsg(LOG_ERR, __func__, 391238384Sjkim "interface %s was already configured", ifname); 392238384Sjkim free(sdl); 393238384Sjkim return(-1); 394238384Sjkim } 395238384Sjkim 396238384Sjkim if ((ifinfo = malloc(sizeof(*ifinfo))) == NULL) { 397238384Sjkim warnmsg(LOG_ERR, __func__, "memory allocation failed"); 398238384Sjkim free(sdl); 399238384Sjkim return(-1); 400238384Sjkim } 401238384Sjkim memset(ifinfo, 0, sizeof(*ifinfo)); 402238384Sjkim ifinfo->sdl = sdl; 403238384Sjkim 404238384Sjkim strlcpy(ifinfo->ifname, ifname, sizeof(ifinfo->ifname)); 405238384Sjkim 406238384Sjkim /* construct a router solicitation message */ 407238384Sjkim if (make_packet(ifinfo)) 408238384Sjkim goto bad; 409238384Sjkim 410238384Sjkim /* set link ID of this interface. */ 411238384Sjkim#ifdef HAVE_SCOPELIB 412238384Sjkim if (inet_zoneid(AF_INET6, 2, ifname, &ifinfo->linkid)) 413238384Sjkim goto bad; 414238384Sjkim#else 415238384Sjkim /* XXX: assume interface IDs as link IDs */ 416238384Sjkim ifinfo->linkid = ifinfo->sdl->sdl_index; 417238384Sjkim#endif 418238384Sjkim 419238384Sjkim /* 420238384Sjkim * check if the interface is available. 421238384Sjkim * also check if SIOCGIFMEDIA ioctl is OK on the interface. 422238384Sjkim */ 423238384Sjkim ifinfo->mediareqok = 1; 424238384Sjkim ifinfo->active = interface_status(ifinfo); 425238384Sjkim if (!ifinfo->mediareqok) { 426238384Sjkim /* 427238384Sjkim * probe routers periodically even if the link status 428238384Sjkim * does not change. 429238384Sjkim */ 430238384Sjkim ifinfo->probeinterval = PROBE_INTERVAL; 431238384Sjkim } 432238384Sjkim 433238384Sjkim /* activate interface: interface_up returns 0 on success */ 434238384Sjkim flags = interface_up(ifinfo->ifname); 435238384Sjkim if (flags == 0) 436238384Sjkim ifinfo->state = IFS_DELAY; 437238384Sjkim else if (flags == IFS_TENTATIVE) 438238384Sjkim ifinfo->state = IFS_TENTATIVE; 439238384Sjkim else 440238384Sjkim ifinfo->state = IFS_DOWN; 441238384Sjkim 442238384Sjkim rtsol_timer_update(ifinfo); 443238384Sjkim 444238384Sjkim /* link into chain */ 445238384Sjkim if (iflist) 446238384Sjkim ifinfo->next = iflist; 447238384Sjkim iflist = ifinfo; 448238384Sjkim 449238384Sjkim return(0); 450238384Sjkim 451238384Sjkimbad: 452238384Sjkim free(ifinfo->sdl); 453238384Sjkim free(ifinfo); 454238384Sjkim return(-1); 455238384Sjkim} 456238384Sjkim 457238384Sjkimvoid 458238384Sjkimiflist_init(void) 459238384Sjkim{ 460238384Sjkim struct ifinfo *ifi, *next; 461238384Sjkim 462238384Sjkim for (ifi = iflist; ifi; ifi = next) { 463238384Sjkim next = ifi->next; 464238384Sjkim if (ifi->sdl) 465238384Sjkim free(ifi->sdl); 466238384Sjkim if (ifi->rs_data) 467238384Sjkim free(ifi->rs_data); 468238384Sjkim free(ifi); 469238384Sjkim iflist = NULL; 470238384Sjkim } 471280304Sjkim} 472280304Sjkim 473238384Sjkim#if 0 474280304Sjkimstatic int 475280304Sjkimifreconfig(char *ifname) 476280304Sjkim{ 477238384Sjkim struct ifinfo *ifi, *prev; 478280304Sjkim int rv; 479280304Sjkim 480280304Sjkim prev = NULL; 481280304Sjkim for (ifi = iflist; ifi; ifi = ifi->next) { 482238384Sjkim if (strncmp(ifi->ifname, ifname, sizeof(ifi->ifname)) == 0) 483280304Sjkim break; 484238384Sjkim prev = ifi; 485280304Sjkim } 486280304Sjkim prev->next = ifi->next; 487280304Sjkim 488280304Sjkim rv = ifconfig(ifname); 489280304Sjkim 490280304Sjkim /* reclaim it after ifconfig() in case ifname is pointer inside ifi */ 491238384Sjkim if (ifi->rs_data) 492280304Sjkim free(ifi->rs_data); 493280304Sjkim free(ifi->sdl); 494280304Sjkim free(ifi); 495280304Sjkim return rv; 496280304Sjkim} 497280304Sjkim#endif 498280304Sjkim 499280304Sjkimstruct ifinfo * 500280304Sjkimfind_ifinfo(int ifindex) 501280304Sjkim{ 502280304Sjkim struct ifinfo *ifi; 503280304Sjkim 504280304Sjkim for (ifi = iflist; ifi; ifi = ifi->next) 505280304Sjkim if (ifi->sdl->sdl_index == ifindex) 506280304Sjkim return(ifi); 507280304Sjkim return(NULL); 508280304Sjkim} 509280304Sjkim 510280304Sjkimstatic int 511280304Sjkimmake_packet(struct ifinfo *ifinfo) 512280304Sjkim{ 513280304Sjkim size_t packlen = sizeof(struct nd_router_solicit), lladdroptlen = 0; 514280304Sjkim struct nd_router_solicit *rs; 515280304Sjkim char *buf; 516280304Sjkim 517280304Sjkim if ((lladdroptlen = lladdropt_length(ifinfo->sdl)) == 0) { 518280304Sjkim warnmsg(LOG_INFO, __func__, 519280304Sjkim "link-layer address option has null length" 520280304Sjkim " on %s. Treat as not included.", ifinfo->ifname); 521280304Sjkim } 522280304Sjkim packlen += lladdroptlen; 523280304Sjkim ifinfo->rs_datalen = packlen; 524280304Sjkim 525280304Sjkim /* allocate buffer */ 526280304Sjkim if ((buf = malloc(packlen)) == NULL) { 527280304Sjkim warnmsg(LOG_ERR, __func__, 528280304Sjkim "memory allocation failed for %s", ifinfo->ifname); 529280304Sjkim return(-1); 530280304Sjkim } 531280304Sjkim ifinfo->rs_data = buf; 532280304Sjkim 533280304Sjkim /* fill in the message */ 534280304Sjkim rs = (struct nd_router_solicit *)buf; 535280304Sjkim rs->nd_rs_type = ND_ROUTER_SOLICIT; 536280304Sjkim rs->nd_rs_code = 0; 537280304Sjkim rs->nd_rs_cksum = 0; 538280304Sjkim rs->nd_rs_reserved = 0; 539280304Sjkim buf += sizeof(*rs); 540280304Sjkim 541280304Sjkim /* fill in source link-layer address option */ 542280304Sjkim if (lladdroptlen) 543280304Sjkim lladdropt_fill(ifinfo->sdl, (struct nd_opt_hdr *)buf); 544280304Sjkim 545280304Sjkim return(0); 546280304Sjkim} 547280304Sjkim 548280304Sjkimstatic struct timeval * 549280304Sjkimrtsol_check_timer(void) 550280304Sjkim{ 551280304Sjkim static struct timeval returnval; 552280304Sjkim struct timeval now, rtsol_timer; 553280304Sjkim struct ifinfo *ifinfo; 554280304Sjkim int flags; 555280304Sjkim 556280304Sjkim gettimeofday(&now, NULL); 557280304Sjkim 558280304Sjkim rtsol_timer = tm_max; 559280304Sjkim 560280304Sjkim for (ifinfo = iflist; ifinfo; ifinfo = ifinfo->next) { 561280304Sjkim if (timercmp(&ifinfo->expire, &now, <=)) { 562238384Sjkim if (dflag > 1) 563280304Sjkim warnmsg(LOG_DEBUG, __func__, 564280304Sjkim "timer expiration on %s, " 565280304Sjkim "state = %d", ifinfo->ifname, 566280304Sjkim ifinfo->state); 567238384Sjkim 568238384Sjkim switch (ifinfo->state) { 569238384Sjkim case IFS_DOWN: 570238384Sjkim case IFS_TENTATIVE: 571238384Sjkim /* interface_up returns 0 on success */ 572238384Sjkim flags = interface_up(ifinfo->ifname); 573280304Sjkim if (flags == 0) 574280304Sjkim ifinfo->state = IFS_DELAY; 575238384Sjkim else if (flags == IFS_TENTATIVE) 576280304Sjkim ifinfo->state = IFS_TENTATIVE; 577280304Sjkim else 578280304Sjkim ifinfo->state = IFS_DOWN; 579238384Sjkim break; 580280304Sjkim case IFS_IDLE: 581280304Sjkim { 582280304Sjkim int oldstatus = ifinfo->active; 583280304Sjkim int probe = 0; 584238384Sjkim 585280304Sjkim ifinfo->active = interface_status(ifinfo); 586238384Sjkim 587280304Sjkim if (oldstatus != ifinfo->active) { 588280304Sjkim warnmsg(LOG_DEBUG, __func__, 589280304Sjkim "%s status is changed" 590280304Sjkim " from %d to %d", 591280304Sjkim ifinfo->ifname, 592280304Sjkim oldstatus, ifinfo->active); 593280304Sjkim probe = 1; 594280304Sjkim ifinfo->state = IFS_DELAY; 595280304Sjkim } else if (ifinfo->probeinterval && 596280304Sjkim (ifinfo->probetimer -= 597238384Sjkim ifinfo->timer.tv_sec) <= 0) { 598280304Sjkim /* probe timer expired */ 599280304Sjkim ifinfo->probetimer = 600238384Sjkim ifinfo->probeinterval; 601280304Sjkim probe = 1; 602280304Sjkim ifinfo->state = IFS_PROBE; 603280304Sjkim } 604280304Sjkim 605280304Sjkim /* 606280304Sjkim * If we need a probe, clear the previous 607280304Sjkim * status wrt the "other" configuration. 608280304Sjkim */ 609280304Sjkim if (probe) 610280304Sjkim ifinfo->otherconfig = 0; 611280304Sjkim 612280304Sjkim if (probe && mobile_node) 613280304Sjkim defrouter_probe(ifinfo); 614280304Sjkim break; 615238384Sjkim } 616280304Sjkim case IFS_DELAY: 617280304Sjkim ifinfo->state = IFS_PROBE; 618238384Sjkim sendpacket(ifinfo); 619280304Sjkim break; 620280304Sjkim case IFS_PROBE: 621280304Sjkim if (ifinfo->probes < MAX_RTR_SOLICITATIONS) 622238384Sjkim sendpacket(ifinfo); 623280304Sjkim else { 624238384Sjkim warnmsg(LOG_INFO, __func__, 625280304Sjkim "No answer after sending %d RSs", 626280304Sjkim ifinfo->probes); 627280304Sjkim ifinfo->probes = 0; 628280304Sjkim ifinfo->state = IFS_IDLE; 629280304Sjkim } 630280304Sjkim break; 631280304Sjkim } 632280304Sjkim rtsol_timer_update(ifinfo); 633280304Sjkim } 634280304Sjkim 635280304Sjkim if (timercmp(&ifinfo->expire, &rtsol_timer, <)) 636280304Sjkim rtsol_timer = ifinfo->expire; 637280304Sjkim } 638280304Sjkim 639280304Sjkim if (timercmp(&rtsol_timer, &tm_max, ==)) { 640280304Sjkim warnmsg(LOG_DEBUG, __func__, "there is no timer"); 641280304Sjkim return(NULL); 642280304Sjkim } else if (timercmp(&rtsol_timer, &now, <)) 643280304Sjkim /* this may occur when the interval is too small */ 644280304Sjkim returnval.tv_sec = returnval.tv_usec = 0; 645238384Sjkim else 646280304Sjkim timersub(&rtsol_timer, &now, &returnval); 647280304Sjkim 648238384Sjkim if (dflag > 1) 649238384Sjkim warnmsg(LOG_DEBUG, __func__, "New timer is %ld:%08ld", 650238384Sjkim (long)returnval.tv_sec, (long)returnval.tv_usec); 651238384Sjkim 652238384Sjkim return(&returnval); 653238384Sjkim} 654238384Sjkim 655280304Sjkimvoid 656280304Sjkimrtsol_timer_update(struct ifinfo *ifinfo) 657238384Sjkim{ 658280304Sjkim#define MILLION 1000000 659280304Sjkim#define DADRETRY 10 /* XXX: adhoc */ 660280304Sjkim long interval; 661238384Sjkim struct timeval now; 662280304Sjkim 663280304Sjkim bzero(&ifinfo->timer, sizeof(ifinfo->timer)); 664238384Sjkim 665280304Sjkim switch (ifinfo->state) { 666280304Sjkim case IFS_DOWN: 667280304Sjkim case IFS_TENTATIVE: 668280304Sjkim if (++ifinfo->dadcount > DADRETRY) { 669280304Sjkim ifinfo->dadcount = 0; 670280304Sjkim ifinfo->timer.tv_sec = PROBE_INTERVAL; 671280304Sjkim } else 672280304Sjkim ifinfo->timer.tv_sec = 1; 673238384Sjkim break; 674238384Sjkim case IFS_IDLE: 675280304Sjkim if (mobile_node) { 676238384Sjkim /* XXX should be configurable */ 677280304Sjkim ifinfo->timer.tv_sec = 3; 678280304Sjkim } 679280304Sjkim else 680280304Sjkim ifinfo->timer = tm_max; /* stop timer(valid?) */ 681280304Sjkim break; 682280304Sjkim case IFS_DELAY: 683280304Sjkim#ifndef HAVE_ARC4RANDOM 684280304Sjkim interval = random() % (MAX_RTR_SOLICITATION_DELAY * MILLION); 685280304Sjkim#else 686280304Sjkim interval = arc4random() % (MAX_RTR_SOLICITATION_DELAY * MILLION); 687280304Sjkim#endif 688280304Sjkim ifinfo->timer.tv_sec = interval / MILLION; 689280304Sjkim ifinfo->timer.tv_usec = interval % MILLION; 690280304Sjkim break; 691280304Sjkim case IFS_PROBE: 692280304Sjkim if (ifinfo->probes < MAX_RTR_SOLICITATIONS) 693238384Sjkim ifinfo->timer.tv_sec = RTR_SOLICITATION_INTERVAL; 694280304Sjkim else { 695280304Sjkim /* 696280304Sjkim * After sending MAX_RTR_SOLICITATIONS solicitations, 697238384Sjkim * we're just waiting for possible replies; there 698280304Sjkim * will be no more solicitation. Thus, we change 699280304Sjkim * the timer value to MAX_RTR_SOLICITATION_DELAY based 700280304Sjkim * on RFC 2461, Section 6.3.7. 701280304Sjkim */ 702280304Sjkim ifinfo->timer.tv_sec = MAX_RTR_SOLICITATION_DELAY; 703238384Sjkim } 704280304Sjkim break; 705280304Sjkim default: 706238384Sjkim warnmsg(LOG_ERR, __func__, 707280304Sjkim "illegal interface state(%d) on %s", 708280304Sjkim ifinfo->state, ifinfo->ifname); 709280304Sjkim return; 710238384Sjkim } 711280304Sjkim 712280304Sjkim /* reset the timer */ 713280304Sjkim if (timercmp(&ifinfo->timer, &tm_max, ==)) { 714238384Sjkim ifinfo->expire = tm_max; 715280304Sjkim warnmsg(LOG_DEBUG, __func__, 716280304Sjkim "stop timer for %s", ifinfo->ifname); 717280304Sjkim } else { 718280304Sjkim gettimeofday(&now, NULL); 719280304Sjkim timeradd(&now, &ifinfo->timer, &ifinfo->expire); 720280304Sjkim 721280304Sjkim if (dflag > 1) 722280304Sjkim warnmsg(LOG_DEBUG, __func__, 723280304Sjkim "set timer for %s to %d:%d", ifinfo->ifname, 724280304Sjkim (int)ifinfo->timer.tv_sec, 725280304Sjkim (int)ifinfo->timer.tv_usec); 726280304Sjkim } 727280304Sjkim 728280304Sjkim#undef MILLION 729280304Sjkim} 730280304Sjkim 731280304Sjkim/* timer related utility functions */ 732280304Sjkim#define MILLION 1000000 733280304Sjkim 734280304Sjkim#ifndef SMALL 735238384Sjkimstatic void 736280304Sjkimrtsold_set_dump_file(int sig) 737238384Sjkim{ 738238384Sjkim do_dump = 1; 739238384Sjkim} 740238384Sjkim#endif 741238384Sjkim 742238384Sjkimstatic void 743280304Sjkimusage(char *progname) 744280304Sjkim{ 745280304Sjkim if (progname && progname[strlen(progname) - 1] != 'd') { 746280304Sjkim fprintf(stderr, "usage: rtsol [-dDF] interfaces...\n"); 747280304Sjkim fprintf(stderr, "usage: rtsol [-dDF] -a\n"); 748280304Sjkim } else { 749280304Sjkim fprintf(stderr, "usage: rtsold [-adDfFm1] interfaces...\n"); 750280304Sjkim fprintf(stderr, "usage: rtsold [-dDfFm1] -a\n"); 751280304Sjkim } 752280304Sjkim exit(1); 753280304Sjkim} 754280304Sjkim 755280304Sjkimvoid 756280304Sjkim#if __STDC__ 757280304Sjkimwarnmsg(int priority, const char *func, const char *msg, ...) 758280304Sjkim#else 759238384Sjkimwarnmsg(priority, func, msg, va_alist) 760280304Sjkim int priority; 761280304Sjkim const char *func; 762280304Sjkim const char *msg; 763280304Sjkim va_dcl 764238384Sjkim#endif 765280304Sjkim{ 766280304Sjkim va_list ap; 767280304Sjkim char buf[BUFSIZ]; 768280304Sjkim 769280304Sjkim va_start(ap, msg); 770238384Sjkim if (fflag) { 771280304Sjkim if (priority <= log_upto) { 772280304Sjkim (void)vfprintf(stderr, msg, ap); 773238384Sjkim (void)fprintf(stderr, "\n"); 774280304Sjkim } 775280304Sjkim } else { 776280304Sjkim snprintf(buf, sizeof(buf), "<%s> %s", func, msg); 777238384Sjkim msg = buf; 778280304Sjkim vsyslog(priority, msg, ap); 779280304Sjkim } 780280304Sjkim va_end(ap); 781238384Sjkim} 782280304Sjkim 783280304Sjkim/* 784280304Sjkim * return a list of interfaces which is suitable to sending an RS. 785280304Sjkim */ 786280304Sjkimchar ** 787280304Sjkimautoifprobe(void) 788280304Sjkim{ 789280304Sjkim static char **argv = NULL; 790280304Sjkim static int n = 0; 791280304Sjkim char **a; 792280304Sjkim int i, found; 793280304Sjkim struct ifaddrs *ifap, *ifa, *target; 794280304Sjkim 795280304Sjkim /* initialize */ 796280304Sjkim while (n--) 797280304Sjkim free(argv[n]); 798280304Sjkim if (argv) { 799280304Sjkim free(argv); 800280304Sjkim argv = NULL; 801280304Sjkim } 802238384Sjkim n = 0; 803280304Sjkim 804238384Sjkim if (getifaddrs(&ifap) != 0) 805238384Sjkim return NULL; 806280304Sjkim 807280304Sjkim target = NULL; 808280304Sjkim /* find an ethernet */ 809238384Sjkim for (ifa = ifap; ifa; ifa = ifa->ifa_next) { 810280304Sjkim if ((ifa->ifa_flags & IFF_UP) == 0) 811238384Sjkim continue; 812280304Sjkim if ((ifa->ifa_flags & IFF_POINTOPOINT) != 0) 813280304Sjkim continue; 814280304Sjkim if ((ifa->ifa_flags & IFF_LOOPBACK) != 0) 815280304Sjkim continue; 816280304Sjkim if ((ifa->ifa_flags & IFF_MULTICAST) == 0) 817280304Sjkim continue; 818280304Sjkim 819280304Sjkim if (ifa->ifa_addr->sa_family != AF_INET6) 820280304Sjkim continue; 821280304Sjkim 822280304Sjkim found = 0; 823280304Sjkim for (i = 0; i < n; i++) { 824280304Sjkim if (strcmp(argv[i], ifa->ifa_name) == 0) { 825280304Sjkim found++; 826280304Sjkim break; 827280304Sjkim } 828280304Sjkim } 829280304Sjkim if (found) 830280304Sjkim continue; 831280304Sjkim 832280304Sjkim /* if we find multiple candidates, just warn. */ 833280304Sjkim if (n != 0 && dflag > 1) 834280304Sjkim warnx("multiple interfaces found"); 835280304Sjkim 836238384Sjkim a = (char **)realloc(argv, (n + 1) * sizeof(char **)); 837280304Sjkim if (a == NULL) 838280304Sjkim err(1, "realloc"); 839280304Sjkim argv = a; 840280304Sjkim argv[n] = strdup(ifa->ifa_name); 841280304Sjkim if (!argv[n]) 842280304Sjkim err(1, "malloc"); 843280304Sjkim n++; 844280304Sjkim argv[n] = NULL; 845280304Sjkim } 846280304Sjkim 847280304Sjkim if (n) { 848280304Sjkim a = (char **)realloc(argv, (n + 1) * sizeof(char **)); 849280304Sjkim if (a == NULL) 850280304Sjkim err(1, "realloc"); 851280304Sjkim argv = a; 852280304Sjkim argv[n] = NULL; 853280304Sjkim 854280304Sjkim if (dflag > 0) { 855280304Sjkim for (i = 0; i < n; i++) 856280304Sjkim warnx("probing %s", argv[i]); 857280304Sjkim } 858280304Sjkim } 859280304Sjkim freeifaddrs(ifap); 860280304Sjkim return argv; 861238384Sjkim} 862238384Sjkim