1 /*
2 * OpenVPN -- An application to securely tunnel IP networks
3 * over a single TCP/UDP port, with support for SSL/TLS-based
4 * session authentication and key exchange,
5 * packet encryption, packet authentication, and
6 * packet compression.
7 *
8 * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2
12 * as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 */
23
24 /*
25 * The interval_ routines are designed to optimize the calling of a routine
26 * (normally tls_multi_process()) which can be called less frequently
27 * between triggers.
28 */
29
30 #ifndef INTERVAL_H
31 #define INTERVAL_H
32
33 #include "otime.h"
34
35 #define INTERVAL_DEBUG 0
36
37 /*
38 * Designed to limit calls to expensive functions that need to be called
39 * regularly.
40 */
41
42 struct interval
43 {
44 interval_t refresh;
45 interval_t horizon;
46 time_t future_trigger;
47 time_t last_action;
48 time_t last_test_true;
49 };
50
51 void interval_init(struct interval *top, int horizon, int refresh);
52
53 /*
54 * IF
55 * last_action less than horizon seconds ago
56 * OR last_test_true more than refresh seconds ago
57 * OR hit future_trigger
58 * THEN
59 * return true
60 * ELSE
61 * set wakeup to the number of seconds until a true return
62 * return false
63 */
64
65 static inline bool
interval_test(struct interval * top)66 interval_test(struct interval *top)
67 {
68 bool trigger = false;
69 const time_t local_now = now;
70
71 if (top->future_trigger && local_now >= top->future_trigger)
72 {
73 trigger = true;
74 top->future_trigger = 0;
75 }
76
77 if (top->last_action + top->horizon > local_now
78 || top->last_test_true + top->refresh <= local_now
79 || trigger)
80 {
81 top->last_test_true = local_now;
82 #if INTERVAL_DEBUG
83 dmsg(D_INTERVAL, "INTERVAL interval_test true");
84 #endif
85 return true;
86 }
87 else
88 {
89 return false;
90 }
91 }
92
93 static inline void
interval_schedule_wakeup(struct interval * top,interval_t * wakeup)94 interval_schedule_wakeup(struct interval *top, interval_t *wakeup)
95 {
96 const time_t local_now = now;
97 interval_earliest_wakeup(wakeup, top->last_test_true + top->refresh, local_now);
98 interval_earliest_wakeup(wakeup, top->future_trigger, local_now);
99 #if INTERVAL_DEBUG
100 dmsg(D_INTERVAL, "INTERVAL interval_schedule wakeup=%d", (int)*wakeup);
101 #endif
102 }
103
104 /*
105 * In wakeup seconds, interval_test will return true once.
106 */
107 static inline void
interval_future_trigger(struct interval * top,interval_t wakeup)108 interval_future_trigger(struct interval *top, interval_t wakeup)
109 {
110 if (wakeup)
111 {
112 #if INTERVAL_DEBUG
113 dmsg(D_INTERVAL, "INTERVAL interval_future_trigger %d", (int)wakeup);
114 #endif
115 top->future_trigger = now + wakeup;
116 }
117 }
118
119 /*
120 * Once an action is triggered, interval_test will remain true for
121 * horizon seconds.
122 */
123 static inline void
interval_action(struct interval * top)124 interval_action(struct interval *top)
125 {
126 #if INTERVAL_DEBUG
127 dmsg(D_INTERVAL, "INTERVAL action");
128 #endif
129 top->last_action = now;
130 }
131
132 /*
133 * Measure when n seconds beyond an event have elapsed
134 */
135
136 struct event_timeout
137 {
138 bool defined;
139 interval_t n;
140 time_t last; /* time of last event */
141 };
142
143 static inline bool
event_timeout_defined(const struct event_timeout * et)144 event_timeout_defined(const struct event_timeout *et)
145 {
146 return et->defined;
147 }
148
149 static inline void
event_timeout_clear(struct event_timeout * et)150 event_timeout_clear(struct event_timeout *et)
151 {
152 et->defined = false;
153 et->n = 0;
154 et->last = 0;
155 }
156
157 static inline struct event_timeout
event_timeout_clear_ret(void)158 event_timeout_clear_ret(void)
159 {
160 struct event_timeout ret;
161 event_timeout_clear(&ret);
162 return ret;
163 }
164
165 static inline void
event_timeout_init(struct event_timeout * et,interval_t n,const time_t local_now)166 event_timeout_init(struct event_timeout *et, interval_t n, const time_t local_now)
167 {
168 et->defined = true;
169 et->n = (n >= 0) ? n : 0;
170 et->last = local_now;
171 }
172
173 static inline void
event_timeout_reset(struct event_timeout * et)174 event_timeout_reset(struct event_timeout *et)
175 {
176 if (et->defined)
177 {
178 et->last = now;
179 }
180 }
181
182 static inline void
event_timeout_modify_wakeup(struct event_timeout * et,interval_t n)183 event_timeout_modify_wakeup(struct event_timeout *et, interval_t n)
184 {
185 /* note that you might need to call reset_coarse_timers after this */
186 if (et->defined)
187 {
188 et->n = (n >= 0) ? n : 0;
189 }
190 }
191
192 /*
193 * Will return the time left for a timeout, this function does not check
194 * if the timeout is actually valid
195 */
196 static inline interval_t
event_timeout_remaining(struct event_timeout * et)197 event_timeout_remaining(struct event_timeout *et)
198 {
199 return (interval_t) (et->last - now + et->n);
200 }
201
202 /*
203 * This is the principal function for testing and triggering recurring
204 * timers and will return true on a timer signal event.
205 * If et_const_retry == ETT_DEFAULT and a signal occurs,
206 * the function will return true and *et will be armed for the
207 * next event. If et_const_retry >= 0 and a signal occurs,
208 * *et will not be touched, but *tv will be set to
209 * minimum (*tv, et_const_retry) for a future re-test,
210 * and the function will return true.
211 */
212
213 #define ETT_DEFAULT (-1)
214
215 bool event_timeout_trigger(struct event_timeout *et,
216 struct timeval *tv,
217 const int et_const_retry);
218
219 /*
220 * Measure time intervals in microseconds
221 */
222
223 #define USEC_TIMER_MAX 60 /* maximum interval size in seconds */
224
225 #define USEC_TIMER_MAX_USEC (USEC_TIMER_MAX * 1000000)
226
227 struct usec_timer {
228 struct timeval start;
229 struct timeval end;
230 };
231
232 #ifdef HAVE_GETTIMEOFDAY
233
234 static inline void
usec_timer_start(struct usec_timer * obj)235 usec_timer_start(struct usec_timer *obj)
236 {
237 CLEAR(*obj);
238 openvpn_gettimeofday(&obj->start, NULL);
239 }
240
241 static inline void
usec_timer_end(struct usec_timer * obj)242 usec_timer_end(struct usec_timer *obj)
243 {
244 openvpn_gettimeofday(&obj->end, NULL);
245 }
246
247 #endif /* HAVE_GETTIMEOFDAY */
248
249 static inline bool
usec_timer_interval_defined(struct usec_timer * obj)250 usec_timer_interval_defined(struct usec_timer *obj)
251 {
252 return obj->start.tv_sec && obj->end.tv_sec;
253 }
254
255 static inline int
usec_timer_interval(struct usec_timer * obj)256 usec_timer_interval(struct usec_timer *obj)
257 {
258 return tv_subtract(&obj->end, &obj->start, USEC_TIMER_MAX);
259 }
260
261 #endif /* INTERVAL_H */
262