1 /* $KAME: rtsold.c,v 1.31 2001/05/22 06:03:06 jinmei Exp $ */
4 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the project nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * $FreeBSD: src/usr.sbin/rtsold/rtsold.c,v 1.1.2.4 2002/04/04 11:07:19 ume Exp $
32 * $DragonFly: src/usr.sbin/rtsold/rtsold.c,v 1.7 2005/12/05 00:56:37 swildner Exp $
35 #include <sys/types.h>
37 #include <sys/socket.h>
40 #include <net/if_dl.h>
42 #include <netinet/in.h>
43 #include <netinet/icmp6.h>
57 struct ifinfo
*iflist
;
58 struct timeval tm_max
= {0x7fffffff, 0x7fffffff};
61 static int log_upto
= 999;
64 /* protocol constatns */
65 #define MAX_RTR_SOLICITATION_DELAY 1 /* second */
66 #define RTR_SOLICITATION_INTERVAL 4 /* seconds */
67 #define MAX_RTR_SOLICITATIONS 3 /* times */
69 /* implementation dependent constants */
70 #define PROBE_INTERVAL 60 /* secondes XXX: should be configurable */
74 #define TIMEVAL_LT(a, b) (((a).tv_sec < (b).tv_sec) ||\
75 (((a).tv_sec == (b).tv_sec) && \
76 ((a).tv_usec < (b).tv_usec)))
79 #define TIMEVAL_LEQ(a, b) (((a).tv_sec < (b).tv_sec) ||\
80 (((a).tv_sec == (b).tv_sec) &&\
81 ((a).tv_usec <= (b).tv_usec)))
84 #define TIMEVAL_EQ(a, b) (((a).tv_sec==(b).tv_sec) && ((a).tv_usec==(b).tv_usec))
86 int main(int argc
, char *argv
[]);
88 /* static variables and functions */
89 static int mobile_node
= 0;
92 * XXX: the following two values should be configurable
94 static const char *dumpfilename
= "/var/run/rtsold.dump";
95 static const char *pidfilename
= "/var/run/rtsold.pid";
97 static int ifconfig(char *ifname
);
99 static int ifreconfig(char *ifname
);
101 static int make_packet(struct ifinfo
*ifinfo
);
102 static struct timeval
*rtsol_check_timer(void);
103 static void TIMEVAL_ADD(struct timeval
*a
, struct timeval
*b
,
104 struct timeval
*result
);
105 static void TIMEVAL_SUB(struct timeval
*a
, struct timeval
*b
,
106 struct timeval
*result
);
108 static void rtsold_set_dump_file(int);
109 static void usage(char *progname
);
110 static char **autoifprobe(void);
113 main(int argc
, char *argv
[])
115 int s
, rtsock
, maxfd
, ch
;
117 struct timeval
*timeout
;
128 if (argv0
&& argv0
[strlen(argv0
) - 1] != 'd') {
135 while ((ch
= getopt(argc
, argv
, opts
)) != -1) {
171 argv
= autoifprobe();
173 errx(1, "could not autoprobe interface");
177 for (i
= 0; argv
[i
]; i
++)
188 log_upto
= LOG_NOTICE
;
191 ident
= strrchr(argv0
, '/');
196 openlog(ident
, LOG_NDELAY
|LOG_PID
, LOG_DAEMON
);
198 setlogmask(LOG_UPTO(log_upto
));
201 #ifndef HAVE_ARC4RANDOM
202 /* random value initilization */
203 srandom((u_long
)time(NULL
));
206 /* warn if accept_rtadv is down */
207 if (!getinet6sysctl(IPV6CTL_ACCEPT_RTADV
))
208 warnx("kernel is configured not to accept RAs");
209 /* warn if forwarding is up */
210 if (getinet6sysctl(IPV6CTL_FORWARDING
))
211 warnx("kernel is configured as a router, not a host");
213 /* initialization to dump internal status to a file */
214 if (signal(SIGUSR1
, rtsold_set_dump_file
) == SIG_ERR
) {
215 errx(1, "failed to set signal for dump status");
220 * Open a socket for sending RS and receiving RA.
221 * This should be done before calling ifinit(), since the function
224 if ((s
= sockopen()) < 0) {
225 errx(1, "failed to open a socket");
229 if ((rtsock
= rtsock_open()) < 0) {
230 errx(1, "failed to open a socket");
236 /* configuration per interface */
238 errx(1, "failed to initilizatoin interfaces");
242 if (ifconfig(*argv
)) {
243 errx(1, "failed to initialize %s", *argv
);
249 /* setup for probing default routers */
251 errx(1, "failed to setup for probing routers");
256 daemon(0, 0); /* act as a daemon */
258 /* dump the current pid */
260 pid_t pid
= getpid();
263 if ((fp
= fopen(pidfilename
, "w")) == NULL
)
264 warnmsg(LOG_ERR
, __func__
,
265 "failed to open a log file(%s): %s",
266 pidfilename
, strerror(errno
));
268 fprintf(fp
, "%d\n", pid
);
275 FD_SET(rtsock
, &fdset
);
276 while (1) { /* main loop */
278 struct fd_set select_fd
= fdset
;
280 if (do_dump
) { /* SIGUSR1 */
282 rtsold_dump_file(dumpfilename
);
285 timeout
= rtsol_check_timer();
290 /* if we have no timeout, we are done (or failed) */
294 /* if all interfaces have got RA packet, we are done */
295 for (ifi
= iflist
; ifi
; ifi
= ifi
->next
) {
296 if (ifi
->state
!= IFS_DOWN
&& ifi
->racnt
== 0)
302 e
= select(maxfd
+ 1, &select_fd
, NULL
, NULL
, timeout
);
304 if (e
< 0 && errno
!= EINTR
) {
305 warnmsg(LOG_ERR
, __func__
, "select: %s",
311 /* packet reception */
312 if (FD_ISSET(rtsock
, &select_fd
))
313 rtsock_input(rtsock
);
314 if (FD_ISSET(s
, &select_fd
))
323 ifconfig(char *ifname
)
325 struct ifinfo
*ifinfo
;
326 struct sockaddr_dl
*sdl
;
329 if ((sdl
= if_nametosdl(ifname
)) == NULL
) {
330 warnmsg(LOG_ERR
, __func__
,
331 "failed to get link layer information for %s", ifname
);
334 if (find_ifinfo(sdl
->sdl_index
)) {
335 warnmsg(LOG_ERR
, __func__
,
336 "interface %s was already configured", ifname
);
341 if ((ifinfo
= malloc(sizeof(*ifinfo
))) == NULL
) {
342 warnmsg(LOG_ERR
, __func__
, "memory allocation failed");
346 memset(ifinfo
, 0, sizeof(*ifinfo
));
349 strncpy(ifinfo
->ifname
, ifname
, sizeof(ifinfo
->ifname
));
351 /* construct a router solicitation message */
352 if (make_packet(ifinfo
))
356 * check if the interface is available.
357 * also check if SIOCGIFMEDIA ioctl is OK on the interface.
359 ifinfo
->mediareqok
= 1;
360 ifinfo
->active
= interface_status(ifinfo
);
361 if (!ifinfo
->mediareqok
) {
363 * probe routers periodically even if the link status
366 ifinfo
->probeinterval
= PROBE_INTERVAL
;
369 /* activate interface: interface_up returns 0 on success */
370 flags
= interface_up(ifinfo
->ifname
);
372 ifinfo
->state
= IFS_DELAY
;
373 else if (flags
== IFS_TENTATIVE
)
374 ifinfo
->state
= IFS_TENTATIVE
;
376 ifinfo
->state
= IFS_DOWN
;
378 rtsol_timer_update(ifinfo
);
380 /* link into chain */
382 ifinfo
->next
= iflist
;
395 ifreconfig(char *ifname
)
397 struct ifinfo
*ifi
, *prev
;
401 for (ifi
= iflist
; ifi
; ifi
= ifi
->next
) {
402 if (strncmp(ifi
->ifname
, ifname
, sizeof(ifi
->ifname
)) == 0)
406 prev
->next
= ifi
->next
;
408 rv
= ifconfig(ifname
);
410 /* reclaim it after ifconfig() in case ifname is pointer inside ifi */
421 find_ifinfo(int ifindex
)
425 for (ifi
= iflist
; ifi
; ifi
= ifi
->next
)
426 if (ifi
->sdl
->sdl_index
== ifindex
)
433 make_packet(struct ifinfo
*ifinfo
)
436 struct nd_router_solicit
*rs
;
437 size_t packlen
= sizeof(struct nd_router_solicit
), lladdroptlen
= 0;
439 if ((lladdroptlen
= lladdropt_length(ifinfo
->sdl
)) == 0) {
440 warnmsg(LOG_INFO
, __func__
,
441 "link-layer address option has null length"
442 " on %s. Treat as not included.", ifinfo
->ifname
);
444 packlen
+= lladdroptlen
;
445 ifinfo
->rs_datalen
= packlen
;
447 /* allocate buffer */
448 if ((buf
= malloc(packlen
)) == NULL
) {
449 warnmsg(LOG_ERR
, __func__
,
450 "memory allocation failed for %s", ifinfo
->ifname
);
453 ifinfo
->rs_data
= buf
;
455 /* fill in the message */
456 rs
= (struct nd_router_solicit
*)buf
;
457 rs
->nd_rs_type
= ND_ROUTER_SOLICIT
;
460 rs
->nd_rs_reserved
= 0;
463 /* fill in source link-layer address option */
465 lladdropt_fill(ifinfo
->sdl
, (struct nd_opt_hdr
*)buf
);
470 static struct timeval
*
471 rtsol_check_timer(void)
473 static struct timeval returnval
;
474 struct timeval now
, rtsol_timer
;
475 struct ifinfo
*ifinfo
;
478 gettimeofday(&now
, NULL
);
480 rtsol_timer
= tm_max
;
482 for (ifinfo
= iflist
; ifinfo
; ifinfo
= ifinfo
->next
) {
483 if (TIMEVAL_LEQ(ifinfo
->expire
, now
)) {
485 warnmsg(LOG_DEBUG
, __func__
,
486 "timer expiration on %s, "
487 "state = %d", ifinfo
->ifname
,
490 switch (ifinfo
->state
) {
493 /* interface_up returns 0 on success */
494 flags
= interface_up(ifinfo
->ifname
);
496 ifinfo
->state
= IFS_DELAY
;
497 else if (flags
== IFS_TENTATIVE
)
498 ifinfo
->state
= IFS_TENTATIVE
;
500 ifinfo
->state
= IFS_DOWN
;
504 int oldstatus
= ifinfo
->active
;
508 interface_status(ifinfo
);
510 if (oldstatus
!= ifinfo
->active
) {
511 warnmsg(LOG_DEBUG
, __func__
,
512 "%s status is changed"
515 oldstatus
, ifinfo
->active
);
517 ifinfo
->state
= IFS_DELAY
;
519 else if (ifinfo
->probeinterval
&&
520 (ifinfo
->probetimer
-=
521 ifinfo
->timer
.tv_sec
) <= 0) {
522 /* probe timer expired */
524 ifinfo
->probeinterval
;
526 ifinfo
->state
= IFS_PROBE
;
529 if (probe
&& mobile_node
)
530 defrouter_probe(ifinfo
->sdl
->sdl_index
);
534 ifinfo
->state
= IFS_PROBE
;
538 if (ifinfo
->probes
< MAX_RTR_SOLICITATIONS
)
541 warnmsg(LOG_INFO
, __func__
,
543 "after sending %d RSs",
546 ifinfo
->state
= IFS_IDLE
;
550 rtsol_timer_update(ifinfo
);
553 if (TIMEVAL_LT(ifinfo
->expire
, rtsol_timer
))
554 rtsol_timer
= ifinfo
->expire
;
557 if (TIMEVAL_EQ(rtsol_timer
, tm_max
)) {
558 warnmsg(LOG_DEBUG
, __func__
, "there is no timer");
561 else if (TIMEVAL_LT(rtsol_timer
, now
))
562 /* this may occur when the interval is too small */
563 returnval
.tv_sec
= returnval
.tv_usec
= 0;
565 TIMEVAL_SUB(&rtsol_timer
, &now
, &returnval
);
568 warnmsg(LOG_DEBUG
, __func__
, "New timer is %ld:%08ld",
569 (long)returnval
.tv_sec
, (long)returnval
.tv_usec
);
575 rtsol_timer_update(struct ifinfo
*ifinfo
)
577 #define MILLION 1000000
578 #define DADRETRY 10 /* XXX: adhoc */
582 bzero(&ifinfo
->timer
, sizeof(ifinfo
->timer
));
584 switch (ifinfo
->state
) {
587 if (++ifinfo
->dadcount
> DADRETRY
) {
588 ifinfo
->dadcount
= 0;
589 ifinfo
->timer
.tv_sec
= PROBE_INTERVAL
;
592 ifinfo
->timer
.tv_sec
= 1;
596 /* XXX should be configurable */
597 ifinfo
->timer
.tv_sec
= 3;
600 ifinfo
->timer
= tm_max
; /* stop timer(valid?) */
603 #ifndef HAVE_ARC4RANDOM
604 interval
= random() % (MAX_RTR_SOLICITATION_DELAY
* MILLION
);
606 interval
= arc4random() % (MAX_RTR_SOLICITATION_DELAY
* MILLION
);
608 ifinfo
->timer
.tv_sec
= interval
/ MILLION
;
609 ifinfo
->timer
.tv_usec
= interval
% MILLION
;
612 if (ifinfo
->probes
< MAX_RTR_SOLICITATIONS
)
613 ifinfo
->timer
.tv_sec
= RTR_SOLICITATION_INTERVAL
;
616 * After sending MAX_RTR_SOLICITATIONS solicitations,
617 * we're just waiting for possible replies; there
618 * will be no more solicatation. Thus, we change
619 * the timer value to MAX_RTR_SOLICITATION_DELAY based
620 * on RFC 2461, Section 6.3.7.
622 ifinfo
->timer
.tv_sec
= MAX_RTR_SOLICITATION_DELAY
;
626 warnmsg(LOG_ERR
, __func__
,
627 "illegal interface state(%d) on %s",
628 ifinfo
->state
, ifinfo
->ifname
);
632 /* reset the timer */
633 if (TIMEVAL_EQ(ifinfo
->timer
, tm_max
)) {
634 ifinfo
->expire
= tm_max
;
635 warnmsg(LOG_DEBUG
, __func__
,
636 "stop timer for %s", ifinfo
->ifname
);
639 gettimeofday(&now
, NULL
);
640 TIMEVAL_ADD(&now
, &ifinfo
->timer
, &ifinfo
->expire
);
643 warnmsg(LOG_DEBUG
, __func__
,
644 "set timer for %s to %d:%d", ifinfo
->ifname
,
645 (int)ifinfo
->timer
.tv_sec
,
646 (int)ifinfo
->timer
.tv_usec
);
652 /* timer related utility functions */
653 #define MILLION 1000000
657 TIMEVAL_ADD(struct timeval
*a
, struct timeval
*b
, struct timeval
*result
)
661 if ((l
= a
->tv_usec
+ b
->tv_usec
) < MILLION
) {
663 result
->tv_sec
= a
->tv_sec
+ b
->tv_sec
;
666 result
->tv_usec
= l
- MILLION
;
667 result
->tv_sec
= a
->tv_sec
+ b
->tv_sec
+ 1;
673 * XXX: this function assumes that a >= b.
676 TIMEVAL_SUB(struct timeval
*a
, struct timeval
*b
, struct timeval
*result
)
680 if ((l
= a
->tv_usec
- b
->tv_usec
) >= 0) {
682 result
->tv_sec
= a
->tv_sec
- b
->tv_sec
;
685 result
->tv_usec
= MILLION
+ l
;
686 result
->tv_sec
= a
->tv_sec
- b
->tv_sec
- 1;
691 rtsold_set_dump_file(int signo __unused
)
697 usage(char *progname
)
699 if (progname
&& progname
[strlen(progname
) - 1] != 'd') {
700 fprintf(stderr
, "usage: rtsol [-dD] interfaces...\n");
701 fprintf(stderr
, "usage: rtsol [-dD] -a\n");
703 fprintf(stderr
, "usage: rtsold [-adDfm1] interfaces...\n");
704 fprintf(stderr
, "usage: rtsold [-dDfm1] -a\n");
710 warnmsg(int priority
, const char *func
, const char *msg
, ...)
717 if (priority
<= log_upto
) {
718 vfprintf(stderr
, msg
, ap
);
719 fprintf(stderr
, "\n");
722 snprintf(buf
, sizeof(buf
), "<%s> %s", func
, msg
);
724 vsyslog(priority
, msg
, ap
);
732 #ifndef HAVE_GETIFADDRS
733 errx(1, "-a is not available with the configuration");
735 static char ifname
[IFNAMSIZ
+ 1];
736 static char *argv
[2];
737 struct ifaddrs
*ifap
, *ifa
, *target
;
739 if (getifaddrs(&ifap
) != 0)
743 /* find an ethernet */
744 for (ifa
= ifap
; ifa
; ifa
= ifa
->ifa_next
) {
745 if ((ifa
->ifa_flags
& IFF_UP
) == 0)
747 if ((ifa
->ifa_flags
& IFF_POINTOPOINT
) != 0)
749 if ((ifa
->ifa_flags
& IFF_LOOPBACK
) != 0)
751 if ((ifa
->ifa_flags
& IFF_MULTICAST
) == 0)
754 if (ifa
->ifa_addr
->sa_family
!= AF_INET6
)
757 if (target
&& strcmp(target
->ifa_name
, ifa
->ifa_name
) == 0)
763 /* if we find multiple candidates, failure. */
765 warnx("multiple interfaces found");
772 strncpy(ifname
, target
->ifa_name
, sizeof(ifname
) - 1);
773 ifname
[sizeof(ifname
) - 1] = '\0';
778 warnx("probing %s", argv
[0]);
784 return (char **)NULL
;