Fixed compiler warnings.
[strongswan.git] / src / libcharon / plugins / ha / ha_segments.c
1 /*
2 * Copyright (C) 2008 Martin Willi
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "ha_segments.h"
17
18 #include <pthread.h>
19
20 #include <threading/mutex.h>
21 #include <threading/condvar.h>
22 #include <utils/linked_list.h>
23 #include <processing/jobs/callback_job.h>
24
25 #define DEFAULT_HEARTBEAT_DELAY 1000
26 #define DEFAULT_HEARTBEAT_TIMEOUT 2100
27
28 typedef struct private_ha_segments_t private_ha_segments_t;
29
30 /**
31 * Private data of an ha_segments_t object.
32 */
33 struct private_ha_segments_t {
34
35 /**
36 * Public ha_segments_t interface.
37 */
38 ha_segments_t public;
39
40 /**
41 * communication socket
42 */
43 ha_socket_t *socket;
44
45 /**
46 * Sync tunnel, if any
47 */
48 ha_tunnel_t *tunnel;
49
50 /**
51 * Interface to control segments at kernel level
52 */
53 ha_kernel_t *kernel;
54
55 /**
56 * Mutex to lock segment manipulation
57 */
58 mutex_t *mutex;
59
60 /**
61 * Condvar to wait for heartbeats
62 */
63 condvar_t *condvar;
64
65 /**
66 * Job checking for heartbeats
67 */
68 callback_job_t *job;
69
70 /**
71 * Total number of ClusterIP segments
72 */
73 u_int count;
74
75 /**
76 * mask of active segments
77 */
78 segment_mask_t active;
79
80 /**
81 * Node number
82 */
83 u_int node;
84
85 /**
86 * Interval we send hearbeats
87 */
88 int heartbeat_delay;
89
90 /**
91 * Timeout for heartbeats received from other node
92 */
93 int heartbeat_timeout;
94 };
95
96 /**
97 * Log currently active segments
98 */
99 static void log_segments(private_ha_segments_t *this, bool activated,
100 u_int segment)
101 {
102 char buf[64] = "none", *pos = buf;
103 int i;
104 bool first = TRUE;
105
106 for (i = 1; i <= this->count; i++)
107 {
108 if (this->active & SEGMENTS_BIT(i))
109 {
110 if (first)
111 {
112 first = FALSE;
113 }
114 else
115 {
116 pos += snprintf(pos, buf + sizeof(buf) - pos, ",");
117 }
118 pos += snprintf(pos, buf + sizeof(buf) - pos, "%d", i);
119 }
120 }
121 DBG1(DBG_CFG, "HA segment %d %sactivated, now active: %s",
122 segment, activated ? "" : "de", buf);
123 }
124
125 /**
126 * Enable/Disable a specific segment
127 */
128 static void enable_disable(private_ha_segments_t *this, u_int segment,
129 bool enable, bool notify)
130 {
131 ike_sa_t *ike_sa;
132 enumerator_t *enumerator;
133 ike_sa_state_t old, new;
134 ha_message_t *message = NULL;
135 ha_message_type_t type;
136 bool changes = FALSE;
137
138 if (segment > this->count)
139 {
140 return;
141 }
142
143 if (enable)
144 {
145 old = IKE_PASSIVE;
146 new = IKE_ESTABLISHED;
147 type = HA_SEGMENT_TAKE;
148 if (!(this->active & SEGMENTS_BIT(segment)))
149 {
150 this->active |= SEGMENTS_BIT(segment);
151 this->kernel->activate(this->kernel, segment);
152 changes = TRUE;
153 }
154 }
155 else
156 {
157 old = IKE_ESTABLISHED;
158 new = IKE_PASSIVE;
159 type = HA_SEGMENT_DROP;
160 if (this->active & SEGMENTS_BIT(segment))
161 {
162 this->active &= ~SEGMENTS_BIT(segment);
163 this->kernel->deactivate(this->kernel, segment);
164 changes = TRUE;
165 }
166 }
167
168 if (changes)
169 {
170 enumerator = charon->ike_sa_manager->create_enumerator(charon->ike_sa_manager);
171 while (enumerator->enumerate(enumerator, &ike_sa))
172 {
173 if (ike_sa->get_state(ike_sa) != old)
174 {
175 continue;
176 }
177 if (this->tunnel && this->tunnel->is_sa(this->tunnel, ike_sa))
178 {
179 continue;
180 }
181 if (this->kernel->get_segment(this->kernel,
182 ike_sa->get_other_host(ike_sa)) == segment)
183 {
184 ike_sa->set_state(ike_sa, new);
185 }
186 }
187 enumerator->destroy(enumerator);
188 log_segments(this, enable, segment);
189 }
190
191 if (notify)
192 {
193 message = ha_message_create(type);
194 message->add_attribute(message, HA_SEGMENT, segment);
195 this->socket->push(this->socket, message);
196 message->destroy(message);
197 }
198 }
199
200 /**
201 * Enable/Disable all or a specific segment, do locking
202 */
203 static void enable_disable_all(private_ha_segments_t *this, u_int segment,
204 bool enable, bool notify)
205 {
206 int i;
207
208 this->mutex->lock(this->mutex);
209 if (segment == 0)
210 {
211 for (i = 1; i <= this->count; i++)
212 {
213 enable_disable(this, i, enable, notify);
214 }
215 }
216 else
217 {
218 enable_disable(this, segment, enable, notify);
219 }
220 this->mutex->unlock(this->mutex);
221 }
222
223 METHOD(ha_segments_t, activate, void,
224 private_ha_segments_t *this, u_int segment, bool notify)
225 {
226 enable_disable_all(this, segment, TRUE, notify);
227 }
228
229 METHOD(ha_segments_t, deactivate, void,
230 private_ha_segments_t *this, u_int segment, bool notify)
231 {
232 enable_disable_all(this, segment, FALSE, notify);
233 }
234
235 METHOD(listener_t, alert_hook, bool,
236 private_ha_segments_t *this, ike_sa_t *ike_sa, alert_t alert, va_list args)
237 {
238 if (alert == ALERT_SHUTDOWN_SIGNAL)
239 {
240 if (this->job)
241 {
242 DBG1(DBG_CFG, "HA heartbeat active, dropping all segments");
243 deactivate(this, 0, TRUE);
244 }
245 else
246 {
247 DBG1(DBG_CFG, "no HA heartbeat active, closing IKE_SAs");
248 }
249 }
250 return TRUE;
251 }
252
253 /**
254 * Monitor heartbeat activity of remote node
255 */
256 static job_requeue_t watchdog(private_ha_segments_t *this)
257 {
258 int oldstate;
259 bool timeout;
260
261 this->mutex->lock(this->mutex);
262 pthread_cleanup_push((void*)this->mutex->unlock, this->mutex);
263 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
264 timeout = this->condvar->timed_wait(this->condvar, this->mutex,
265 this->heartbeat_timeout);
266 pthread_setcancelstate(oldstate, NULL);
267 pthread_cleanup_pop(TRUE);
268 if (timeout)
269 {
270 DBG1(DBG_CFG, "no heartbeat received, taking all segments");
271 activate(this, 0, TRUE);
272 /* disable heartbeat detection util we get one */
273 this->job = NULL;
274 return JOB_REQUEUE_NONE;
275 }
276 return JOB_REQUEUE_DIRECT;
277 }
278
279 /**
280 * Start the heartbeat detection thread
281 */
282 static void start_watchdog(private_ha_segments_t *this)
283 {
284 this->job = callback_job_create((callback_job_cb_t)watchdog,
285 this, NULL, NULL);
286 charon->processor->queue_job(charon->processor, (job_t*)this->job);
287 }
288
289 METHOD(ha_segments_t, handle_status, void,
290 private_ha_segments_t *this, segment_mask_t mask)
291 {
292 segment_mask_t missing;
293 int i;
294
295 this->mutex->lock(this->mutex);
296
297 missing = ~(this->active | mask);
298
299 for (i = 1; i <= this->count; i++)
300 {
301 if (missing & SEGMENTS_BIT(i))
302 {
303 if (this->node == i % 2)
304 {
305 DBG1(DBG_CFG, "HA segment %d was not handled, taking", i);
306 enable_disable(this, i, TRUE, TRUE);
307 }
308 else
309 {
310 DBG1(DBG_CFG, "HA segment %d was not handled, dropping", i);
311 enable_disable(this, i, FALSE, TRUE);
312 }
313 }
314 }
315
316 this->mutex->unlock(this->mutex);
317 this->condvar->signal(this->condvar);
318
319 if (!this->job)
320 {
321 DBG1(DBG_CFG, "received heartbeat, reenabling watchdog");
322 start_watchdog(this);
323 }
324 }
325
326 /**
327 * Send a status message with our active segments
328 */
329 static job_requeue_t send_status(private_ha_segments_t *this)
330 {
331 ha_message_t *message;
332 int i;
333
334 message = ha_message_create(HA_STATUS);
335
336 for (i = 1; i <= this->count; i++)
337 {
338 if (this->active & SEGMENTS_BIT(i))
339 {
340 message->add_attribute(message, HA_SEGMENT, i);
341 }
342 }
343
344 this->socket->push(this->socket, message);
345 message->destroy(message);
346
347 /* schedule next invocation */
348 charon->scheduler->schedule_job_ms(charon->scheduler, (job_t*)
349 callback_job_create((callback_job_cb_t)
350 send_status, this, NULL, NULL),
351 this->heartbeat_delay);
352
353 return JOB_REQUEUE_NONE;
354 }
355
356 METHOD(ha_segments_t, is_active, bool,
357 private_ha_segments_t *this, u_int segment)
358 {
359 return (this->active & SEGMENTS_BIT(segment)) != 0;
360 }
361
362 METHOD(ha_segments_t, destroy, void,
363 private_ha_segments_t *this)
364 {
365 if (this->job)
366 {
367 this->job->cancel(this->job);
368 }
369 this->mutex->destroy(this->mutex);
370 this->condvar->destroy(this->condvar);
371 free(this);
372 }
373
374 /**
375 * See header
376 */
377 ha_segments_t *ha_segments_create(ha_socket_t *socket, ha_kernel_t *kernel,
378 ha_tunnel_t *tunnel, u_int count, u_int node,
379 bool monitor)
380 {
381 private_ha_segments_t *this;
382
383 INIT(this,
384 .public = {
385 .listener = {
386 .alert = _alert_hook,
387 },
388 .activate = _activate,
389 .deactivate = _deactivate,
390 .handle_status = _handle_status,
391 .is_active = _is_active,
392 .destroy = _destroy,
393 },
394 .socket = socket,
395 .tunnel = tunnel,
396 .kernel = kernel,
397 .count = count,
398 .node = node,
399 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
400 .condvar = condvar_create(CONDVAR_TYPE_DEFAULT),
401 .heartbeat_delay = lib->settings->get_int(lib->settings,
402 "charon.plugins.ha.heartbeat_delay", DEFAULT_HEARTBEAT_DELAY),
403 .heartbeat_timeout = lib->settings->get_int(lib->settings,
404 "charon.plugins.ha.heartbeat_timeout", DEFAULT_HEARTBEAT_TIMEOUT),
405 );
406
407 if (monitor)
408 {
409 DBG1(DBG_CFG, "starting HA heartbeat, delay %dms, timeout %dms",
410 this->heartbeat_delay, this->heartbeat_timeout);
411 send_status(this);
412 start_watchdog(this);
413 }
414
415 return &this->public;
416 }
417