2 * Copyright (C) 2008 Martin Willi
3 * Hochschule fuer Technik Rapperswil
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>.
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
16 #include "ha_segments.h"
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>
24 #define DEFAULT_HEARTBEAT_DELAY 1000
25 #define DEFAULT_HEARTBEAT_TIMEOUT 2100
27 typedef struct private_ha_segments_t private_ha_segments_t
;
30 * Private data of an ha_segments_t object.
32 struct private_ha_segments_t
{
35 * Public ha_segments_t interface.
40 * communication socket
50 * Interface to control segments at kernel level
55 * Mutex to lock segment manipulation
60 * Condvar to wait for heartbeats
65 * Job checking for heartbeats
70 * Total number of ClusterIP segments
75 * mask of active segments
77 segment_mask_t active
;
85 * Interval we send hearbeats
90 * Timeout for heartbeats received from other node
92 int heartbeat_timeout
;
96 * Log currently active segments
98 static void log_segments(private_ha_segments_t
*this, bool activated
,
101 char buf
[64] = "none", *pos
= buf
;
105 for (i
= 1; i
<= this->count
; i
++)
107 if (this->active
& SEGMENTS_BIT(i
))
115 pos
+= snprintf(pos
, buf
+ sizeof(buf
) - pos
, ",");
117 pos
+= snprintf(pos
, buf
+ sizeof(buf
) - pos
, "%d", i
);
120 DBG1(DBG_CFG
, "HA segment %d %sactivated, now active: %s",
121 segment
, activated ?
"" : "de", buf
);
125 * Enable/Disable a specific segment
127 static void enable_disable(private_ha_segments_t
*this, u_int segment
,
128 bool enable
, bool notify
)
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
;
137 if (segment
> this->count
)
145 new = IKE_ESTABLISHED
;
146 type
= HA_SEGMENT_TAKE
;
147 if (!(this->active
& SEGMENTS_BIT(segment
)))
149 this->active
|= SEGMENTS_BIT(segment
);
150 this->kernel
->activate(this->kernel
, segment
);
156 old
= IKE_ESTABLISHED
;
158 type
= HA_SEGMENT_DROP
;
159 if (this->active
& SEGMENTS_BIT(segment
))
161 this->active
&= ~SEGMENTS_BIT(segment
);
162 this->kernel
->deactivate(this->kernel
, segment
);
169 enumerator
= charon
->ike_sa_manager
->create_enumerator(charon
->ike_sa_manager
);
170 while (enumerator
->enumerate(enumerator
, &ike_sa
))
172 if (ike_sa
->get_state(ike_sa
) != old
)
176 if (this->tunnel
&& this->tunnel
->is_sa(this->tunnel
, ike_sa
))
180 if (this->kernel
->get_segment(this->kernel
,
181 ike_sa
->get_other_host(ike_sa
)) == segment
)
183 ike_sa
->set_state(ike_sa
, new);
186 enumerator
->destroy(enumerator
);
187 log_segments(this, enable
, segment
);
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
);
200 * Enable/Disable all or a specific segment, do locking
202 static void enable_disable_all(private_ha_segments_t
*this, u_int segment
,
203 bool enable
, bool notify
)
207 this->mutex
->lock(this->mutex
);
210 for (i
= 1; i
<= this->count
; i
++)
212 enable_disable(this, i
, enable
, notify
);
217 enable_disable(this, segment
, enable
, notify
);
219 this->mutex
->unlock(this->mutex
);
222 METHOD(ha_segments_t
, activate
, void,
223 private_ha_segments_t
*this, u_int segment
, bool notify
)
225 enable_disable_all(this, segment
, TRUE
, notify
);
228 METHOD(ha_segments_t
, deactivate
, void,
229 private_ha_segments_t
*this, u_int segment
, bool notify
)
231 enable_disable_all(this, segment
, FALSE
, notify
);
234 METHOD(listener_t
, alert_hook
, bool,
235 private_ha_segments_t
*this, ike_sa_t
*ike_sa
, alert_t alert
, va_list args
)
237 if (alert
== ALERT_SHUTDOWN_SIGNAL
)
241 DBG1(DBG_CFG
, "HA heartbeat active, dropping all segments");
242 deactivate(this, 0, TRUE
);
246 DBG1(DBG_CFG
, "no HA heartbeat active, closing IKE_SAs");
253 * Monitor heartbeat activity of remote node
255 static job_requeue_t
watchdog(private_ha_segments_t
*this)
257 bool timeout
, oldstate
;
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
);
268 DBG1(DBG_CFG
, "no heartbeat received, taking all segments");
269 activate(this, 0, TRUE
);
270 /* disable heartbeat detection util we get one */
272 return JOB_REQUEUE_NONE
;
274 return JOB_REQUEUE_DIRECT
;
278 * Start the heartbeat detection thread
280 static void start_watchdog(private_ha_segments_t
*this)
282 this->job
= callback_job_create((callback_job_cb_t
)watchdog
,
284 lib
->processor
->queue_job(lib
->processor
, (job_t
*)this->job
);
287 METHOD(ha_segments_t
, handle_status
, void,
288 private_ha_segments_t
*this, segment_mask_t mask
)
290 segment_mask_t missing
;
293 this->mutex
->lock(this->mutex
);
295 missing
= ~(this->active
| mask
);
297 for (i
= 1; i
<= this->count
; i
++)
299 if (missing
& SEGMENTS_BIT(i
))
301 if (this->node
== i
% 2)
303 DBG1(DBG_CFG
, "HA segment %d was not handled, taking", i
);
304 enable_disable(this, i
, TRUE
, TRUE
);
308 DBG1(DBG_CFG
, "HA segment %d was not handled, dropping", i
);
309 enable_disable(this, i
, FALSE
, TRUE
);
314 this->mutex
->unlock(this->mutex
);
315 this->condvar
->signal(this->condvar
);
319 DBG1(DBG_CFG
, "received heartbeat, reenabling watchdog");
320 start_watchdog(this);
325 * Send a status message with our active segments
327 static job_requeue_t
send_status(private_ha_segments_t
*this)
329 ha_message_t
*message
;
332 message
= ha_message_create(HA_STATUS
);
334 for (i
= 1; i
<= this->count
; i
++)
336 if (this->active
& SEGMENTS_BIT(i
))
338 message
->add_attribute(message
, HA_SEGMENT
, i
);
342 this->socket
->push(this->socket
, message
);
343 message
->destroy(message
);
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
);
351 return JOB_REQUEUE_NONE
;
354 METHOD(ha_segments_t
, is_active
, bool,
355 private_ha_segments_t
*this, u_int segment
)
357 return (this->active
& SEGMENTS_BIT(segment
)) != 0;
360 METHOD(ha_segments_t
, destroy
, void,
361 private_ha_segments_t
*this)
365 this->job
->cancel(this->job
);
367 this->mutex
->destroy(this->mutex
);
368 this->condvar
->destroy(this->condvar
);
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
,
379 private_ha_segments_t
*this;
384 .alert
= _alert_hook
,
386 .activate
= _activate
,
387 .deactivate
= _deactivate
,
388 .handle_status
= _handle_status
,
389 .is_active
= _is_active
,
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
),
407 DBG1(DBG_CFG
, "starting HA heartbeat, delay %dms, timeout %dms",
408 this->heartbeat_delay
, this->heartbeat_timeout
);
410 start_watchdog(this);
413 return &this->public;