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"
20 #include <utils/mutex.h>
21 #include <utils/linked_list.h>
22 #include <processing/jobs/callback_job.h>
24 #define HEARTBEAT_DELAY 1000
25 #define 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
;
80 * Are we the master node handling segment assignement?
86 * Log currently active segments
88 static void log_segments(private_ha_segments_t
*this, bool activated
,
91 char buf
[64] = "none", *pos
= buf
;
95 for (i
= 1; i
<= this->count
; i
++)
97 if (this->active
& SEGMENTS_BIT(i
))
105 pos
+= snprintf(pos
, buf
+ sizeof(buf
) - pos
, ",");
107 pos
+= snprintf(pos
, buf
+ sizeof(buf
) - pos
, "%d", i
);
110 DBG1(DBG_CFG
, "HA segment %d %sactivated, now active: %s",
111 segment
, activated ?
"" : "de", buf
);
115 * Enable/Disable a specific segment
117 static void enable_disable(private_ha_segments_t
*this, u_int segment
,
118 bool enable
, bool notify
)
121 enumerator_t
*enumerator
;
122 ike_sa_state_t old
, new;
123 ha_message_t
*message
= NULL
;
124 ha_message_type_t type
;
125 bool changes
= FALSE
;
127 if (segment
> this->count
)
135 new = IKE_ESTABLISHED
;
136 type
= HA_SEGMENT_TAKE
;
137 if (!(this->active
& SEGMENTS_BIT(segment
)))
139 this->active
|= SEGMENTS_BIT(segment
);
140 this->kernel
->activate(this->kernel
, segment
);
146 old
= IKE_ESTABLISHED
;
148 type
= HA_SEGMENT_DROP
;
149 if (this->active
& SEGMENTS_BIT(segment
))
151 this->active
&= ~SEGMENTS_BIT(segment
);
152 this->kernel
->deactivate(this->kernel
, segment
);
159 enumerator
= charon
->ike_sa_manager
->create_enumerator(charon
->ike_sa_manager
);
160 while (enumerator
->enumerate(enumerator
, &ike_sa
))
162 if (ike_sa
->get_state(ike_sa
) != old
)
166 if (this->tunnel
&& this->tunnel
->is_sa(this->tunnel
, ike_sa
))
170 if (this->kernel
->in_segment(this->kernel
,
171 ike_sa
->get_other_host(ike_sa
), segment
))
173 ike_sa
->set_state(ike_sa
, new);
176 enumerator
->destroy(enumerator
);
177 log_segments(this, enable
, segment
);
182 message
= ha_message_create(type
);
183 message
->add_attribute(message
, HA_SEGMENT
, segment
);
184 this->socket
->push(this->socket
, message
);
189 * Enable/Disable all or a specific segment, do locking
191 static void enable_disable_all(private_ha_segments_t
*this, u_int segment
,
192 bool enable
, bool notify
)
196 this->mutex
->lock(this->mutex
);
199 for (i
= 1; i
<= this->count
; i
++)
201 enable_disable(this, i
, enable
, notify
);
206 enable_disable(this, segment
, enable
, notify
);
208 this->mutex
->unlock(this->mutex
);
212 * Implementation of ha_segments_t.activate
214 static void activate(private_ha_segments_t
*this, u_int segment
, bool notify
)
216 enable_disable_all(this, segment
, TRUE
, notify
);
220 * Implementation of ha_segments_t.deactivate
222 static void deactivate(private_ha_segments_t
*this, u_int segment
, bool notify
)
224 enable_disable_all(this, segment
, FALSE
, notify
);
228 * Rekey all children of an IKE_SA
230 static status_t
rekey_children(ike_sa_t
*ike_sa
)
232 iterator_t
*iterator
;
233 child_sa_t
*child_sa
;
234 status_t status
= SUCCESS
;
236 iterator
= ike_sa
->create_child_sa_iterator(ike_sa
);
237 while (iterator
->iterate(iterator
, (void**)&child_sa
))
239 DBG1(DBG_CFG
, "resyncing CHILD_SA");
240 status
= ike_sa
->rekey_child_sa(ike_sa
, child_sa
->get_protocol(child_sa
),
241 child_sa
->get_spi(child_sa
, TRUE
));
242 if (status
== DESTROY_ME
)
247 iterator
->destroy(iterator
);
252 * Implementation of ha_segments_t.resync
254 static void resync(private_ha_segments_t
*this, u_int segment
)
257 enumerator_t
*enumerator
;
260 u_int16_t mask
= SEGMENTS_BIT(segment
);
262 list
= linked_list_create();
263 this->mutex
->lock(this->mutex
);
265 if (segment
> 0 && segment
<= this->count
&& (this->active
& mask
))
267 this->active
&= ~mask
;
269 DBG1(DBG_CFG
, "resyncing HA segment %d", segment
);
271 /* we do the actual rekeying in a seperate loop to avoid rekeying
273 enumerator
= charon
->ike_sa_manager
->create_enumerator(
274 charon
->ike_sa_manager
);
275 while (enumerator
->enumerate(enumerator
, &ike_sa
))
277 if (ike_sa
->get_state(ike_sa
) == IKE_ESTABLISHED
&&
278 this->kernel
->in_segment(this->kernel
,
279 ike_sa
->get_other_host(ike_sa
), segment
))
281 id
= ike_sa
->get_id(ike_sa
);
282 list
->insert_last(list
, id
->clone(id
));
285 enumerator
->destroy(enumerator
);
287 this->mutex
->unlock(this->mutex
);
289 while (list
->remove_last(list
, (void**)&id
) == SUCCESS
)
291 ike_sa
= charon
->ike_sa_manager
->checkout(charon
->ike_sa_manager
, id
);
295 DBG1(DBG_CFG
, "resyncing IKE_SA");
296 if (ike_sa
->rekey(ike_sa
) != DESTROY_ME
)
298 if (rekey_children(ike_sa
) != DESTROY_ME
)
300 charon
->ike_sa_manager
->checkin(
301 charon
->ike_sa_manager
, ike_sa
);
305 charon
->ike_sa_manager
->checkin_and_destroy(
306 charon
->ike_sa_manager
, ike_sa
);
313 * Implementation of listener_t.alert
315 static bool alert_hook(private_ha_segments_t
*this, ike_sa_t
*ike_sa
,
316 alert_t alert
, va_list args
)
318 if (alert
== ALERT_SHUTDOWN_SIGNAL
)
320 deactivate(this, 0, TRUE
);
326 * Get the number of SAs in a segment.
328 static u_int
get_sa_count(private_ha_segments_t
*this)
330 enumerator_t
*enumerator
;
334 enumerator
= charon
->ike_sa_manager
->create_enumerator(charon
->ike_sa_manager
);
335 while (enumerator
->enumerate(enumerator
, &ike_sa
))
337 if (ike_sa
->get_state(ike_sa
) != IKE_ESTABLISHED
)
341 if (this->tunnel
&& this->tunnel
->is_sa(this->tunnel
, ike_sa
))
347 enumerator
->destroy(enumerator
);
352 * Implementation of ha_segments_t.handle_status
354 static void handle_status(private_ha_segments_t
*this, segment_mask_t mask
)
356 segment_mask_t missing
, overlap
;
359 this->mutex
->lock(this->mutex
);
361 missing
= ~(this->active
| mask
);
362 overlap
= this->active
& mask
;
364 /* Activate any missing segment. The master will disable overlapping
365 * segments if both nodes activate the missing segments simultaneously. */
366 for (i
= 1; i
<= this->count
; i
++)
368 if (missing
& SEGMENTS_BIT(i
))
370 DBG1(DBG_CFG
, "HA segment %d was not handled", i
);
371 enable_disable(this, i
, TRUE
, TRUE
);
374 if (this->master
&& overlap
)
376 /* Disable overlapping segment on one node, controlled by master */
377 for (i
= 1; i
<= this->count
; i
++)
379 if (overlap
& SEGMENTS_BIT(i
))
381 if (get_sa_count(this))
383 DBG1(DBG_CFG
, "HA segment %d overlaps, taking over", i
);
384 enable_disable(this, i
, TRUE
, TRUE
);
388 DBG1(DBG_CFG
, "HA segment %d overlaps, dropping", i
);
389 enable_disable(this, i
, FALSE
, TRUE
);
394 this->mutex
->unlock(this->mutex
);
395 this->condvar
->signal(this->condvar
);
399 * Send a status message with our active segments
401 static job_requeue_t
send_status(private_ha_segments_t
*this)
403 ha_message_t
*message
;
406 message
= ha_message_create(HA_STATUS
);
408 for (i
= 1; i
<= this->count
; i
++)
410 if (this->active
& SEGMENTS_BIT(i
))
412 message
->add_attribute(message
, HA_SEGMENT
, i
);
416 this->socket
->push(this->socket
, message
);
418 /* schedule next invocation */
419 charon
->scheduler
->schedule_job_ms(charon
->scheduler
, (job_t
*)
420 callback_job_create((callback_job_cb_t
)
421 send_status
, this, NULL
, NULL
),
424 return JOB_REQUEUE_NONE
;
428 * Monitor heartbeat activity of remote node
430 static job_requeue_t
watchdog(private_ha_segments_t
*this)
435 this->mutex
->lock(this->mutex
);
436 pthread_cleanup_push((void*)this->mutex
->unlock
, this->mutex
);
437 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE
, &oldstate
);
438 timeout
= this->condvar
->timed_wait(this->condvar
, this->mutex
,
440 pthread_setcancelstate(oldstate
, NULL
);
441 pthread_cleanup_pop(TRUE
);
443 { /* didn't get a heartbeat, take all segments */
444 activate(this, 0, TRUE
);
446 return JOB_REQUEUE_DIRECT
;
450 * Implementation of ha_segments_t.destroy.
452 static void destroy(private_ha_segments_t
*this)
454 this->job
->cancel(this->job
);
455 this->mutex
->destroy(this->mutex
);
456 this->condvar
->destroy(this->condvar
);
463 ha_segments_t
*ha_segments_create(ha_socket_t
*socket
, ha_kernel_t
*kernel
,
464 ha_tunnel_t
*tunnel
, char *local
, char *remote
, u_int count
)
466 private_ha_segments_t
*this = malloc_thing(private_ha_segments_t
);
469 memset(&this->public.listener
, 0, sizeof(listener_t
));
470 this->public.listener
.alert
= (bool(*)(listener_t
*, ike_sa_t
*, alert_t
, va_list))alert_hook
;
471 this->public.activate
= (void(*)(ha_segments_t
*, u_int segment
,bool))activate
;
472 this->public.deactivate
= (void(*)(ha_segments_t
*, u_int segment
,bool))deactivate
;
473 this->public.resync
= (void(*)(ha_segments_t
*, u_int segment
))resync
;
474 this->public.handle_status
= (void(*)(ha_segments_t
*, segment_mask_t mask
))handle_status
;
475 this->public.destroy
= (void(*)(ha_segments_t
*))destroy
;
477 this->socket
= socket
;
478 this->tunnel
= tunnel
;
479 this->kernel
= kernel
;
480 this->mutex
= mutex_create(MUTEX_TYPE_DEFAULT
);
481 this->condvar
= condvar_create(CONDVAR_TYPE_DEFAULT
);
483 this->master
= strcmp(local
, remote
) > 0;
485 /* initially all segments are active */
487 for (i
= 1; i
<= count
; i
++)
489 this->active
|= SEGMENTS_BIT(i
);
494 /* start heartbeat detection thread */
495 this->job
= callback_job_create((callback_job_cb_t
)watchdog
,
497 charon
->processor
->queue_job(charon
->processor
, (job_t
*)this->job
);
499 return &this->public;