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