1 /* 2 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of the project nor the names of its contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * $FreeBSD$ 30 */ 31 32 #include <sys/types.h> 33 #include <sys/time.h> 34 #include <sys/socket.h> 35 36 #include <net/if.h> 37 #include <net/if_dl.h> 38 39 #include <netinet/in.h> 40 #include <netinet/icmp6.h> 41 42 #include <signal.h> 43 #include <unistd.h> 44 #include <syslog.h> 45 #include <string.h> 46 #include <stdlib.h> 47 #include <stdio.h> 48 #include <errno.h> 49 #include <err.h> 50 #include <stdarg.h> 51 #include "rtsold.h" 52 53 struct ifinfo *iflist; 54 struct timeval tm_max = {0x7fffffff, 0x7fffffff}; 55 int dflag; 56 static int log_upto = 999; 57 static int fflag = 0; 58 59 /* protocol constatns */ 60 #define MAX_RTR_SOLICITATION_DELAY 1 /* second */ 61 #define RTR_SOLICITATION_INTERVAL 4 /* seconds */ 62 #define MAX_RTR_SOLICITATIONS 3 /* times */ 63 64 /* implementation dependent constants */ 65 #define PROBE_INTERVAL 60 /* secondes XXX: should be configurable */ 66 67 /* utility macros */ 68 /* a < b */ 69 #define TIMEVAL_LT(a, b) (((a).tv_sec < (b).tv_sec) ||\ 70 (((a).tv_sec == (b).tv_sec) && \ 71 ((a).tv_usec < (b).tv_usec))) 72 73 /* a <= b */ 74 #define TIMEVAL_LEQ(a, b) (((a).tv_sec < (b).tv_sec) ||\ 75 (((a).tv_sec == (b).tv_sec) &&\ 76 ((a).tv_usec <= (b).tv_usec))) 77 78 /* a == b */ 79 #define TIMEVAL_EQ(a, b) (((a).tv_sec==(b).tv_sec) && ((a).tv_usec==(b).tv_usec)) 80 81 int main __P((int argc, char *argv[])); 82 83 /* static variables and functions */ 84 static int mobile_node = 0; 85 static int do_dump; 86 static char *dumpfilename = "/var/run/rtsold.dump"; /* XXX: should be configurable */ 87 static char *pidfilename = "/var/run/rtsold.pid"; /* should be configurable */ 88 89 static int ifconfig __P((char *ifname)); 90 #if 0 91 static int ifreconfig __P((char *ifname)); 92 #endif 93 static int make_packet __P((struct ifinfo *ifinfo)); 94 static struct timeval *rtsol_check_timer __P((void)); 95 static void TIMEVAL_ADD __P((struct timeval *a, struct timeval *b, 96 struct timeval *result)); 97 static void TIMEVAL_SUB __P((struct timeval *a, struct timeval *b, 98 struct timeval *result)); 99 100 static void rtsold_set_dump_file __P((void)); 101 static void usage __P((char *progname)); 102 103 int 104 main(argc, argv) 105 int argc; 106 char *argv[]; 107 { 108 int s, ch; 109 int once = 0; 110 struct timeval *timeout; 111 struct fd_set fdset; 112 char *argv0; 113 char *opts; 114 115 /* 116 * Initialization 117 */ 118 argv0 = argv[0]; 119 120 /* get option */ 121 if (argv0 && argv0[strlen(argv0) - 1] != 'd') { 122 fflag = 1; 123 once = 1; 124 opts = "dD"; 125 } else 126 opts = "dDfm1"; 127 128 while ((ch = getopt(argc, argv, opts)) != -1) { 129 switch(ch) { 130 case 'd': 131 dflag = 1; 132 break; 133 case 'D': 134 dflag = 2; 135 break; 136 case 'f': 137 fflag = 1; 138 break; 139 case 'm': 140 mobile_node = 1; 141 break; 142 case '1': 143 once = 1; 144 break; 145 default: 146 usage(argv0); 147 } 148 } 149 argc -= optind; 150 argv += optind; 151 if (argc == 0) 152 usage(argv0); 153 154 /* set log level */ 155 if (dflag == 0) 156 log_upto = LOG_NOTICE; 157 if (!fflag) { 158 char *ident; 159 ident = strrchr(argv0, '/'); 160 if (!ident) 161 ident = argv0; 162 else 163 ident++; 164 openlog(ident, LOG_NDELAY|LOG_PID, LOG_DAEMON); 165 if (log_upto >= 0) 166 setlogmask(LOG_UPTO(log_upto)); 167 } 168 169 #ifndef HAVE_ARC4RANDOM 170 /* random value initilization */ 171 srandom((u_long)time(NULL)); 172 #endif 173 174 /* warn if accept_rtadv is down */ 175 if (!getinet6sysctl(IPV6CTL_ACCEPT_RTADV)) 176 warnx("kernel is configured not to accept RAs"); 177 178 /* initialization to dump internal status to a file */ 179 if (signal(SIGUSR1, (void *)rtsold_set_dump_file) < 0) 180 errx(1, "failed to set signal for dump status"); 181 182 /* 183 * Open a socket for sending RS and receiving RA. 184 * This should be done before calling ifinit(), since the function 185 * uses the socket. 186 */ 187 if ((s = sockopen()) < 0) 188 errx(1, "failed to open a socket"); 189 190 /* configuration per interface */ 191 if (ifinit()) 192 errx(1, "failed to initilizatoin interfaces"); 193 while (argc--) { 194 if (ifconfig(*argv)) 195 errx(1, "failed to initialize %s", *argv); 196 argv++; 197 } 198 199 /* setup for probing default routers */ 200 if (probe_init()) 201 errx(1, "failed to setup for probing routers"); 202 203 if (!fflag) 204 daemon(0, 0); /* act as a daemon */ 205 206 /* dump the current pid */ 207 if (!once) { 208 pid_t pid = getpid(); 209 FILE *fp; 210 211 if ((fp = fopen(pidfilename, "w")) == NULL) 212 warnmsg(LOG_ERR, __FUNCTION__, 213 "failed to open a log file(%s)", 214 pidfilename, strerror(errno)); 215 else { 216 fprintf(fp, "%d\n", pid); 217 fclose(fp); 218 } 219 } 220 221 FD_ZERO(&fdset); 222 FD_SET(s, &fdset); 223 while (1) { /* main loop */ 224 int e; 225 struct fd_set select_fd = fdset; 226 227 if (do_dump) { /* SIGUSR1 */ 228 do_dump = 0; 229 rtsold_dump_file(dumpfilename); 230 } 231 232 timeout = rtsol_check_timer(); 233 234 if (once) { 235 struct ifinfo *ifi; 236 237 /* if we have no timeout, we are done (or failed) */ 238 if (timeout == NULL) 239 break; 240 241 /* if all interfaces have got RA packet, we are done */ 242 for (ifi = iflist; ifi; ifi = ifi->next) { 243 if (ifi->state != IFS_DOWN && ifi->racnt == 0) 244 break; 245 } 246 if (ifi == NULL) 247 break; 248 } 249 250 if ((e = select(s + 1, &select_fd, NULL, NULL, timeout)) < 1) { 251 if (e < 0 && errno != EINTR) { 252 warnmsg(LOG_ERR, __FUNCTION__, "select: %s", 253 strerror(errno)); 254 } 255 continue; 256 } 257 258 /* packet reception */ 259 if (FD_ISSET(s, &fdset)) 260 rtsol_input(s); 261 } 262 /* NOTREACHED */ 263 264 return 0; 265 } 266 267 static int 268 ifconfig(char *ifname) 269 { 270 struct ifinfo *ifinfo; 271 struct sockaddr_dl *sdl; 272 int flags; 273 274 if ((sdl = if_nametosdl(ifname)) == NULL) { 275 warnmsg(LOG_ERR, __FUNCTION__, 276 "failed to get link layer information for %s", ifname); 277 return(-1); 278 } 279 if (find_ifinfo(sdl->sdl_index)) { 280 warnmsg(LOG_ERR, __FUNCTION__, 281 "interface %s was already cofigured", ifname); 282 free(sdl); 283 return(-1); 284 } 285 286 if ((ifinfo = malloc(sizeof(*ifinfo))) == NULL) { 287 warnmsg(LOG_ERR, __FUNCTION__, "memory allocation failed"); 288 free(sdl); 289 return(-1); 290 } 291 memset(ifinfo, 0, sizeof(*ifinfo)); 292 ifinfo->sdl = sdl; 293 294 strncpy(ifinfo->ifname, ifname, sizeof(ifinfo->ifname)); 295 296 /* construct a router solicitation message */ 297 if (make_packet(ifinfo)) 298 goto bad; 299 300 /* 301 * check if the interface is available. 302 * also check if SIOCGIFMEDIA ioctl is OK on the interface. 303 */ 304 ifinfo->mediareqok = 1; 305 ifinfo->active = interface_status(ifinfo); 306 if (!ifinfo->mediareqok) { 307 /* 308 * probe routers periodically even if the link status 309 * does not change. 310 */ 311 ifinfo->probeinterval = PROBE_INTERVAL; 312 } 313 314 /* activate interface: interface_up returns 0 on success */ 315 flags = interface_up(ifinfo->ifname); 316 if (flags == 0) 317 ifinfo->state = IFS_DELAY; 318 else if (flags == IFS_TENTATIVE) 319 ifinfo->state = IFS_TENTATIVE; 320 else 321 ifinfo->state = IFS_DOWN; 322 323 rtsol_timer_update(ifinfo); 324 325 /* link into chain */ 326 if (iflist) 327 ifinfo->next = iflist; 328 iflist = ifinfo; 329 330 return(0); 331 332 bad: 333 free(ifinfo->sdl); 334 free(ifinfo); 335 return(-1); 336 } 337 338 #if 0 339 static int 340 ifreconfig(char *ifname) 341 { 342 struct ifinfo *ifi, *prev; 343 int rv; 344 345 prev = NULL; 346 for (ifi = iflist; ifi; ifi = ifi->next) { 347 if (strncmp(ifi->ifname, ifname, sizeof(ifi->ifname)) == 0) 348 break; 349 prev = ifi; 350 } 351 prev->next = ifi->next; 352 353 rv = ifconfig(ifname); 354 355 /* reclaim it after ifconfig() in case ifname is pointer inside ifi */ 356 if (ifi->rs_data) 357 free(ifi->rs_data); 358 free(ifi->sdl); 359 free(ifi); 360 361 return rv; 362 } 363 #endif 364 365 struct ifinfo * 366 find_ifinfo(int ifindex) 367 { 368 struct ifinfo *ifi; 369 370 for (ifi = iflist; ifi; ifi = ifi->next) 371 if (ifi->sdl->sdl_index == ifindex) 372 return(ifi); 373 374 return(NULL); 375 } 376 377 static int 378 make_packet(struct ifinfo *ifinfo) 379 { 380 char *buf; 381 struct nd_router_solicit *rs; 382 size_t packlen = sizeof(struct nd_router_solicit), lladdroptlen = 0; 383 384 if ((lladdroptlen = lladdropt_length(ifinfo->sdl)) == 0) { 385 warnmsg(LOG_INFO, __FUNCTION__, 386 "link-layer address option has null length" 387 " on %s. Treat as not included.", ifinfo->ifname); 388 } 389 packlen += lladdroptlen; 390 ifinfo->rs_datalen = packlen; 391 392 /* allocate buffer */ 393 if ((buf = malloc(packlen)) == NULL) { 394 warnmsg(LOG_ERR, __FUNCTION__, 395 "memory allocation failed for %s", ifinfo->ifname); 396 return(-1); 397 } 398 ifinfo->rs_data = buf; 399 400 /* fill in the message */ 401 rs = (struct nd_router_solicit *)buf; 402 rs->nd_rs_type = ND_ROUTER_SOLICIT; 403 rs->nd_rs_code = 0; 404 rs->nd_rs_cksum = 0; 405 rs->nd_rs_reserved = 0; 406 buf += sizeof(*rs); 407 408 /* fill in source link-layer address option */ 409 if (lladdroptlen) 410 lladdropt_fill(ifinfo->sdl, (struct nd_opt_hdr *)buf); 411 412 return(0); 413 } 414 415 static struct timeval * 416 rtsol_check_timer() 417 { 418 static struct timeval returnval; 419 struct timeval now, rtsol_timer; 420 struct ifinfo *ifinfo; 421 int flags; 422 423 gettimeofday(&now, NULL); 424 425 rtsol_timer = tm_max; 426 427 for (ifinfo = iflist; ifinfo; ifinfo = ifinfo->next) { 428 if (TIMEVAL_LEQ(ifinfo->expire, now)) { 429 if (dflag > 1) 430 warnmsg(LOG_DEBUG, __FUNCTION__, 431 "timer expiration on %s, " 432 "state = %d", ifinfo->ifname, 433 ifinfo->state); 434 435 switch(ifinfo->state) { 436 case IFS_DOWN: 437 case IFS_TENTATIVE: 438 /* interface_up returns 0 on success */ 439 flags = interface_up(ifinfo->ifname); 440 if (flags == 0) 441 ifinfo->state = IFS_DELAY; 442 else if (flags == IFS_TENTATIVE) 443 ifinfo->state = IFS_TENTATIVE; 444 else 445 ifinfo->state = IFS_DOWN; 446 break; 447 case IFS_IDLE: 448 { 449 int oldstatus = ifinfo->active; 450 int probe = 0; 451 452 ifinfo->active = 453 interface_status(ifinfo); 454 455 if (oldstatus != ifinfo->active) { 456 warnmsg(LOG_DEBUG, __FUNCTION__, 457 "%s status is changed" 458 " from %d to %d", 459 ifinfo->ifname, 460 oldstatus, ifinfo->active); 461 probe = 1; 462 ifinfo->state = IFS_DELAY; 463 } 464 else if (ifinfo->probeinterval && 465 (ifinfo->probetimer -= 466 ifinfo->timer.tv_sec) <= 0) { 467 /* probe timer expired */ 468 ifinfo->probetimer = 469 ifinfo->probeinterval; 470 probe = 1; 471 ifinfo->state = IFS_PROBE; 472 } 473 474 if (probe && mobile_node) 475 defrouter_probe(ifinfo->sdl->sdl_index); 476 break; 477 } 478 case IFS_DELAY: 479 ifinfo->state = IFS_PROBE; 480 sendpacket(ifinfo); 481 break; 482 case IFS_PROBE: 483 if (ifinfo->probes < MAX_RTR_SOLICITATIONS) 484 sendpacket(ifinfo); 485 else { 486 warnmsg(LOG_INFO, __FUNCTION__, 487 "No answer " 488 "after sending %d RSs", 489 ifinfo->probes); 490 ifinfo->probes = 0; 491 ifinfo->state = IFS_IDLE; 492 } 493 break; 494 } 495 rtsol_timer_update(ifinfo); 496 } 497 498 if (TIMEVAL_LT(ifinfo->expire, rtsol_timer)) 499 rtsol_timer = ifinfo->expire; 500 } 501 502 if (TIMEVAL_EQ(rtsol_timer, tm_max)) { 503 warnmsg(LOG_DEBUG, __FUNCTION__, "there is no timer"); 504 return(NULL); 505 } 506 else if (TIMEVAL_LT(rtsol_timer, now)) 507 /* this may occur when the interval is too small */ 508 returnval.tv_sec = returnval.tv_usec = 0; 509 else 510 TIMEVAL_SUB(&rtsol_timer, &now, &returnval); 511 512 if (dflag > 1) 513 warnmsg(LOG_DEBUG, __FUNCTION__, "New timer is %d:%08d", 514 returnval.tv_sec, returnval.tv_usec); 515 516 return(&returnval); 517 } 518 519 void 520 rtsol_timer_update(struct ifinfo *ifinfo) 521 { 522 #define MILLION 1000000 523 #define DADRETRY 10 /* XXX: adhoc */ 524 long interval; 525 struct timeval now; 526 527 bzero(&ifinfo->timer, sizeof(ifinfo->timer)); 528 529 switch (ifinfo->state) { 530 case IFS_DOWN: 531 case IFS_TENTATIVE: 532 if (++ifinfo->dadcount > DADRETRY) { 533 ifinfo->dadcount = 0; 534 ifinfo->timer.tv_sec = PROBE_INTERVAL; 535 } 536 else 537 ifinfo->timer.tv_sec = 1; 538 break; 539 case IFS_IDLE: 540 if (mobile_node) { 541 /* XXX should be configurable */ 542 ifinfo->timer.tv_sec = 3; 543 } 544 else 545 ifinfo->timer = tm_max; /* stop timer(valid?) */ 546 break; 547 case IFS_DELAY: 548 #ifndef HAVE_ARC4RANDOM 549 interval = random() % (MAX_RTR_SOLICITATION_DELAY * MILLION); 550 #else 551 interval = arc4random() % (MAX_RTR_SOLICITATION_DELAY * MILLION); 552 #endif 553 ifinfo->timer.tv_sec = interval / MILLION; 554 ifinfo->timer.tv_usec = interval % MILLION; 555 break; 556 case IFS_PROBE: 557 ifinfo->timer.tv_sec = RTR_SOLICITATION_INTERVAL; 558 break; 559 default: 560 warnmsg(LOG_ERR, __FUNCTION__, 561 "illegal interface state(%d) on %s", 562 ifinfo->state, ifinfo->ifname); 563 return; 564 } 565 566 /* reset the timer */ 567 if (TIMEVAL_EQ(ifinfo->timer, tm_max)) { 568 ifinfo->expire = tm_max; 569 warnmsg(LOG_DEBUG, __FUNCTION__, 570 "stop timer for %s", ifinfo->ifname); 571 } 572 else { 573 gettimeofday(&now, NULL); 574 TIMEVAL_ADD(&now, &ifinfo->timer, &ifinfo->expire); 575 576 if (dflag > 1) 577 warnmsg(LOG_DEBUG, __FUNCTION__, 578 "set timer for %s to %d:%d", ifinfo->ifname, 579 (int)ifinfo->timer.tv_sec, 580 (int)ifinfo->timer.tv_usec); 581 } 582 583 #undef MILLION 584 } 585 586 /* timer related utility functions */ 587 #define MILLION 1000000 588 589 /* result = a + b */ 590 static void 591 TIMEVAL_ADD(struct timeval *a, struct timeval *b, struct timeval *result) 592 { 593 long l; 594 595 if ((l = a->tv_usec + b->tv_usec) < MILLION) { 596 result->tv_usec = l; 597 result->tv_sec = a->tv_sec + b->tv_sec; 598 } 599 else { 600 result->tv_usec = l - MILLION; 601 result->tv_sec = a->tv_sec + b->tv_sec + 1; 602 } 603 } 604 605 /* 606 * result = a - b 607 * XXX: this function assumes that a >= b. 608 */ 609 void 610 TIMEVAL_SUB(struct timeval *a, struct timeval *b, struct timeval *result) 611 { 612 long l; 613 614 if ((l = a->tv_usec - b->tv_usec) >= 0) { 615 result->tv_usec = l; 616 result->tv_sec = a->tv_sec - b->tv_sec; 617 } 618 else { 619 result->tv_usec = MILLION + l; 620 result->tv_sec = a->tv_sec - b->tv_sec - 1; 621 } 622 } 623 624 static void 625 rtsold_set_dump_file() 626 { 627 do_dump = 1; 628 } 629 630 static void 631 usage(char *progname) 632 { 633 if (progname && progname[strlen(progname) - 1] != 'd') 634 fprintf(stderr, "usage: rtsol [-dD] interfaces\n"); 635 else 636 fprintf(stderr, "usage: rtsold [-dDfm1] interfaces\n"); 637 exit(1); 638 } 639 640 void 641 #if __STDC__ 642 warnmsg(int priority, const char *func, const char *msg, ...) 643 #else 644 warnmsg(priority, func, msg, va_alist) 645 int priority; 646 const char *func; 647 const char *msg; 648 va_dcl 649 #endif 650 { 651 va_list ap; 652 char buf[BUFSIZ]; 653 654 va_start(ap, msg); 655 if (fflag) { 656 if (priority <= log_upto) { 657 (void)vfprintf(stderr, msg, ap); 658 (void)fprintf(stderr, "\n"); 659 } 660 } else { 661 snprintf(buf, sizeof(buf), "<%s> %s", func, msg); 662 vsyslog(priority, buf, ap); 663 } 664 va_end(ap); 665 } 666