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