1 /* $KAME: timer.c,v 1.9 2002/06/10 19:59:47 itojun Exp $ */ 2 3 /*- 4 * SPDX-License-Identifier: BSD-3-Clause 5 * 6 * Copyright (C) 1998 WIDE Project. 7 * Copyright (C) 2011 Hiroki Sato <hrs@FreeBSD.org> 8 * All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. Neither the name of the project nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #include <sys/queue.h> 36 #include <sys/socket.h> 37 38 #include <net/if.h> 39 #include <net/if_dl.h> 40 #include <netinet/in.h> 41 42 #include <unistd.h> 43 #include <syslog.h> 44 #include <stdlib.h> 45 #include <string.h> 46 #include <search.h> 47 #include <time.h> 48 #include <netdb.h> 49 50 #include "rtadvd.h" 51 #include "timer_subr.h" 52 #include "timer.h" 53 54 struct rtadvd_timer_head_t ra_timer = 55 TAILQ_HEAD_INITIALIZER(ra_timer); 56 static struct timespec tm_limit; 57 static struct timespec tm_max; 58 59 void 60 rtadvd_timer_init(void) 61 { 62 /* Generate maximum time in timespec. */ 63 tm_limit.tv_sec = (-1) & ~((time_t)1 << ((sizeof(tm_max.tv_sec) * 8) - 1)); 64 tm_limit.tv_nsec = (-1) & ~((long)1 << ((sizeof(tm_max.tv_nsec) * 8) - 1)); 65 tm_max = tm_limit; 66 TAILQ_INIT(&ra_timer); 67 } 68 69 void 70 rtadvd_update_timeout_handler(void) 71 { 72 struct ifinfo *ifi; 73 74 TAILQ_FOREACH(ifi, &ifilist, ifi_next) { 75 switch (ifi->ifi_state) { 76 case IFI_STATE_CONFIGURED: 77 case IFI_STATE_TRANSITIVE: 78 if (ifi->ifi_ra_timer != NULL) 79 continue; 80 81 syslog(LOG_DEBUG, "<%s> add timer for %s (idx=%d)", 82 __func__, ifi->ifi_ifname, ifi->ifi_ifindex); 83 ifi->ifi_ra_timer = rtadvd_add_timer(ra_timeout, 84 ra_timer_update, ifi, ifi); 85 ra_timer_update((void *)ifi, 86 &ifi->ifi_ra_timer->rat_tm); 87 rtadvd_set_timer(&ifi->ifi_ra_timer->rat_tm, 88 ifi->ifi_ra_timer); 89 break; 90 case IFI_STATE_UNCONFIGURED: 91 if (ifi->ifi_ra_timer == NULL) 92 continue; 93 94 syslog(LOG_DEBUG, 95 "<%s> remove timer for %s (idx=%d)", __func__, 96 ifi->ifi_ifname, ifi->ifi_ifindex); 97 rtadvd_remove_timer(ifi->ifi_ra_timer); 98 ifi->ifi_ra_timer = NULL; 99 break; 100 } 101 } 102 103 return; 104 } 105 106 struct rtadvd_timer * 107 rtadvd_add_timer(struct rtadvd_timer *(*timeout)(void *), 108 void (*update)(void *, struct timespec *), 109 void *timeodata, void *updatedata) 110 { 111 struct rtadvd_timer *rat; 112 113 if (timeout == NULL) { 114 syslog(LOG_ERR, 115 "<%s> timeout function unspecified", __func__); 116 exit(1); 117 } 118 119 rat = malloc(sizeof(*rat)); 120 if (rat == NULL) { 121 syslog(LOG_ERR, 122 "<%s> can't allocate memory", __func__); 123 exit(1); 124 } 125 memset(rat, 0, sizeof(*rat)); 126 127 rat->rat_expire = timeout; 128 rat->rat_update = update; 129 rat->rat_expire_data = timeodata; 130 rat->rat_update_data = updatedata; 131 rat->rat_tm = tm_max; 132 133 /* link into chain */ 134 TAILQ_INSERT_TAIL(&ra_timer, rat, rat_next); 135 136 return (rat); 137 } 138 139 void 140 rtadvd_remove_timer(struct rtadvd_timer *rat) 141 { 142 143 if (rat == NULL) 144 return; 145 146 TAILQ_REMOVE(&ra_timer, rat, rat_next); 147 free(rat); 148 } 149 150 /* 151 * Check expiration for each timer. If a timer expires, 152 * call the expire function for the timer and update the timer. 153 * Return the next interval for select() call. 154 */ 155 struct timespec * 156 rtadvd_check_timer(void) 157 { 158 static struct timespec returnval; 159 struct timespec now; 160 struct rtadvd_timer *rat; 161 162 clock_gettime(CLOCK_MONOTONIC_FAST, &now); 163 tm_max = tm_limit; 164 TAILQ_FOREACH(rat, &ra_timer, rat_next) { 165 if (TS_CMP(&rat->rat_tm, &now, <=)) { 166 if (((*rat->rat_expire)(rat->rat_expire_data) == NULL)) 167 continue; /* the timer was removed */ 168 if (rat->rat_update) 169 (*rat->rat_update)(rat->rat_update_data, &rat->rat_tm); 170 TS_ADD(&rat->rat_tm, &now, &rat->rat_tm); 171 } 172 if (TS_CMP(&rat->rat_tm, &tm_max, <)) 173 tm_max = rat->rat_tm; 174 } 175 if (TS_CMP(&tm_max, &tm_limit, ==)) { 176 /* no need to timeout */ 177 return (NULL); 178 } else if (TS_CMP(&tm_max, &now, <)) { 179 /* this may occur when the interval is too small */ 180 returnval.tv_sec = returnval.tv_nsec = 0; 181 } else 182 TS_SUB(&tm_max, &now, &returnval); 183 return (&returnval); 184 } 185 186 void 187 rtadvd_set_timer(struct timespec *tm, struct rtadvd_timer *rat) 188 { 189 struct timespec now; 190 191 /* reset the timer */ 192 clock_gettime(CLOCK_MONOTONIC_FAST, &now); 193 TS_ADD(&now, tm, &rat->rat_tm); 194 195 /* update the next expiration time */ 196 if (TS_CMP(&rat->rat_tm, &tm_max, <)) 197 tm_max = rat->rat_tm; 198 199 return; 200 } 201