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(
170 charon
->ike_sa_manager
, TRUE
);
171 while (enumerator
->enumerate(enumerator
, &ike_sa
))
173 if (ike_sa
->get_state(ike_sa
) != old
)
177 if (this->tunnel
&& this->tunnel
->is_sa(this->tunnel
, ike_sa
))
181 if (this->kernel
->get_segment(this->kernel
,
182 ike_sa
->get_other_host(ike_sa
)) == segment
)
184 ike_sa
->set_state(ike_sa
, new);
187 enumerator
->destroy(enumerator
);
188 log_segments(this, enable
, segment
);
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
);
201 * Enable/Disable all or a specific segment, do locking
203 static void enable_disable_all(private_ha_segments_t
*this, u_int segment
,
204 bool enable
, bool notify
)
208 this->mutex
->lock(this->mutex
);
211 for (i
= 1; i
<= this->count
; i
++)
213 enable_disable(this, i
, enable
, notify
);
218 enable_disable(this, segment
, enable
, notify
);
220 this->mutex
->unlock(this->mutex
);
223 METHOD(ha_segments_t
, activate
, void,
224 private_ha_segments_t
*this, u_int segment
, bool notify
)
226 enable_disable_all(this, segment
, TRUE
, notify
);
229 METHOD(ha_segments_t
, deactivate
, void,
230 private_ha_segments_t
*this, u_int segment
, bool notify
)
232 enable_disable_all(this, segment
, FALSE
, notify
);
235 METHOD(listener_t
, alert_hook
, bool,
236 private_ha_segments_t
*this, ike_sa_t
*ike_sa
, alert_t alert
, va_list args
)
238 if (alert
== ALERT_SHUTDOWN_SIGNAL
)
242 DBG1(DBG_CFG
, "HA heartbeat active, dropping all segments");
243 deactivate(this, 0, TRUE
);
247 DBG1(DBG_CFG
, "no HA heartbeat active, closing IKE_SAs");
254 * Monitor heartbeat activity of remote node
256 static job_requeue_t
watchdog(private_ha_segments_t
*this)
258 bool timeout
, oldstate
;
260 this->mutex
->lock(this->mutex
);
261 thread_cleanup_push((void*)this->mutex
->unlock
, this->mutex
);
262 oldstate
= thread_cancelability(TRUE
);
263 timeout
= this->condvar
->timed_wait(this->condvar
, this->mutex
,
264 this->heartbeat_timeout
);
265 thread_cancelability(oldstate
);
266 thread_cleanup_pop(TRUE
);
269 DBG1(DBG_CFG
, "no heartbeat received, taking all segments");
270 activate(this, 0, TRUE
);
271 /* disable heartbeat detection util we get one */
273 return JOB_REQUEUE_NONE
;
275 return JOB_REQUEUE_DIRECT
;
279 * Start the heartbeat detection thread
281 static void start_watchdog(private_ha_segments_t
*this)
283 this->job
= callback_job_create((callback_job_cb_t
)watchdog
,
285 lib
->processor
->queue_job(lib
->processor
, (job_t
*)this->job
);
288 METHOD(ha_segments_t
, handle_status
, void,
289 private_ha_segments_t
*this, segment_mask_t mask
)
291 segment_mask_t missing
;
294 this->mutex
->lock(this->mutex
);
296 missing
= ~(this->active
| mask
);
298 for (i
= 1; i
<= this->count
; i
++)
300 if (missing
& SEGMENTS_BIT(i
))
302 if (this->node
== i
% 2)
304 DBG1(DBG_CFG
, "HA segment %d was not handled, taking", i
);
305 enable_disable(this, i
, TRUE
, TRUE
);
309 DBG1(DBG_CFG
, "HA segment %d was not handled, dropping", i
);
310 enable_disable(this, i
, FALSE
, TRUE
);
315 this->mutex
->unlock(this->mutex
);
316 this->condvar
->signal(this->condvar
);
320 DBG1(DBG_CFG
, "received heartbeat, reenabling watchdog");
321 start_watchdog(this);
326 * Send a status message with our active segments
328 static job_requeue_t
send_status(private_ha_segments_t
*this)
330 ha_message_t
*message
;
333 message
= ha_message_create(HA_STATUS
);
335 for (i
= 1; i
<= this->count
; i
++)
337 if (this->active
& SEGMENTS_BIT(i
))
339 message
->add_attribute(message
, HA_SEGMENT
, i
);
343 this->socket
->push(this->socket
, message
);
344 message
->destroy(message
);
346 /* schedule next invocation */
347 lib
->scheduler
->schedule_job_ms(lib
->scheduler
, (job_t
*)
348 callback_job_create((callback_job_cb_t
)
349 send_status
, this, NULL
, NULL
),
350 this->heartbeat_delay
);
352 return JOB_REQUEUE_NONE
;
355 METHOD(ha_segments_t
, is_active
, bool,
356 private_ha_segments_t
*this, u_int segment
)
358 return (this->active
& SEGMENTS_BIT(segment
)) != 0;
361 METHOD(ha_segments_t
, destroy
, void,
362 private_ha_segments_t
*this)
366 this->job
->cancel(this->job
);
368 this->mutex
->destroy(this->mutex
);
369 this->condvar
->destroy(this->condvar
);
376 ha_segments_t
*ha_segments_create(ha_socket_t
*socket
, ha_kernel_t
*kernel
,
377 ha_tunnel_t
*tunnel
, u_int count
, u_int node
,
380 private_ha_segments_t
*this;
385 .alert
= _alert_hook
,
387 .activate
= _activate
,
388 .deactivate
= _deactivate
,
389 .handle_status
= _handle_status
,
390 .is_active
= _is_active
,
398 .mutex
= mutex_create(MUTEX_TYPE_DEFAULT
),
399 .condvar
= condvar_create(CONDVAR_TYPE_DEFAULT
),
400 .heartbeat_delay
= lib
->settings
->get_int(lib
->settings
,
401 "charon.plugins.ha.heartbeat_delay", DEFAULT_HEARTBEAT_DELAY
),
402 .heartbeat_timeout
= lib
->settings
->get_int(lib
->settings
,
403 "charon.plugins.ha.heartbeat_timeout", DEFAULT_HEARTBEAT_TIMEOUT
),
408 DBG1(DBG_CFG
, "starting HA heartbeat, delay %dms, timeout %dms",
409 this->heartbeat_delay
, this->heartbeat_timeout
);
411 start_watchdog(this);
414 return &this->public;