Annotation of embedaddon/strongswan/src/libcharon/plugins/ha/ha_segments.c, revision 1.1
1.1 ! misho 1: /*
! 2: * Copyright (C) 2008 Martin Willi
! 3: * HSR 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 <collections/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: * Total number of ClusterIP segments
! 66: */
! 67: u_int count;
! 68:
! 69: /**
! 70: * mask of active segments
! 71: */
! 72: segment_mask_t active;
! 73:
! 74: /**
! 75: * Node number
! 76: */
! 77: u_int node;
! 78:
! 79: /**
! 80: * Are we checking for heartbeats?
! 81: */
! 82: bool heartbeat_active;
! 83:
! 84: /**
! 85: * Interval we send heartbeats
! 86: */
! 87: int heartbeat_delay;
! 88:
! 89: /**
! 90: * Timeout for heartbeats received from other node
! 91: */
! 92: int heartbeat_timeout;
! 93:
! 94: /**
! 95: * Interval to check for autobalance, 0 to disable
! 96: */
! 97: int autobalance;
! 98: };
! 99:
! 100: /**
! 101: * Log currently active segments
! 102: */
! 103: static void log_segments(private_ha_segments_t *this, bool activated,
! 104: u_int segment)
! 105: {
! 106: char buf[64] = "none", *pos = buf;
! 107: int i;
! 108: bool first = TRUE;
! 109:
! 110: for (i = 1; i <= this->count; i++)
! 111: {
! 112: if (this->active & SEGMENTS_BIT(i))
! 113: {
! 114: if (first)
! 115: {
! 116: first = FALSE;
! 117: }
! 118: else
! 119: {
! 120: pos += snprintf(pos, buf + sizeof(buf) - pos, ",");
! 121: }
! 122: pos += snprintf(pos, buf + sizeof(buf) - pos, "%d", i);
! 123: }
! 124: }
! 125: DBG1(DBG_CFG, "HA segment %d %sactivated, now active: %s",
! 126: segment, activated ? "" : "de", buf);
! 127: }
! 128:
! 129: /**
! 130: * Enable/Disable a specific segment
! 131: */
! 132: static void enable_disable(private_ha_segments_t *this, u_int segment,
! 133: bool enable, bool notify)
! 134: {
! 135: ike_sa_t *ike_sa;
! 136: enumerator_t *enumerator;
! 137: ike_sa_state_t old, new;
! 138: ha_message_t *message = NULL;
! 139: ha_message_type_t type;
! 140: bool changes = FALSE;
! 141:
! 142: if (segment > this->count)
! 143: {
! 144: return;
! 145: }
! 146:
! 147: if (enable)
! 148: {
! 149: old = IKE_PASSIVE;
! 150: new = IKE_ESTABLISHED;
! 151: type = HA_SEGMENT_TAKE;
! 152: if (!(this->active & SEGMENTS_BIT(segment)))
! 153: {
! 154: this->active |= SEGMENTS_BIT(segment);
! 155: this->kernel->activate(this->kernel, segment);
! 156: changes = TRUE;
! 157: }
! 158: }
! 159: else
! 160: {
! 161: old = IKE_ESTABLISHED;
! 162: new = IKE_PASSIVE;
! 163: type = HA_SEGMENT_DROP;
! 164: if (this->active & SEGMENTS_BIT(segment))
! 165: {
! 166: this->active &= ~SEGMENTS_BIT(segment);
! 167: this->kernel->deactivate(this->kernel, segment);
! 168: changes = TRUE;
! 169: }
! 170: }
! 171:
! 172: if (changes)
! 173: {
! 174: enumerator = charon->ike_sa_manager->create_enumerator(
! 175: charon->ike_sa_manager, TRUE);
! 176: while (enumerator->enumerate(enumerator, &ike_sa))
! 177: {
! 178: if (ike_sa->get_state(ike_sa) != old)
! 179: {
! 180: continue;
! 181: }
! 182: if (this->tunnel && this->tunnel->is_sa(this->tunnel, ike_sa))
! 183: {
! 184: continue;
! 185: }
! 186: if (this->kernel->get_segment(this->kernel,
! 187: ike_sa->get_other_host(ike_sa)) == segment)
! 188: {
! 189: ike_sa->set_state(ike_sa, new);
! 190: }
! 191: }
! 192: enumerator->destroy(enumerator);
! 193: log_segments(this, enable, segment);
! 194: }
! 195:
! 196: if (notify)
! 197: {
! 198: message = ha_message_create(type);
! 199: message->add_attribute(message, HA_SEGMENT, segment);
! 200: this->socket->push(this->socket, message);
! 201: message->destroy(message);
! 202: }
! 203: }
! 204:
! 205: /**
! 206: * Enable/Disable all or a specific segment, do locking
! 207: */
! 208: static void enable_disable_all(private_ha_segments_t *this, u_int segment,
! 209: bool enable, bool notify)
! 210: {
! 211: int i;
! 212:
! 213: this->mutex->lock(this->mutex);
! 214: if (segment == 0)
! 215: {
! 216: for (i = 1; i <= this->count; i++)
! 217: {
! 218: enable_disable(this, i, enable, notify);
! 219: }
! 220: }
! 221: else
! 222: {
! 223: enable_disable(this, segment, enable, notify);
! 224: }
! 225: this->mutex->unlock(this->mutex);
! 226: }
! 227:
! 228: METHOD(ha_segments_t, activate, void,
! 229: private_ha_segments_t *this, u_int segment, bool notify)
! 230: {
! 231: enable_disable_all(this, segment, TRUE, notify);
! 232: }
! 233:
! 234: METHOD(ha_segments_t, deactivate, void,
! 235: private_ha_segments_t *this, u_int segment, bool notify)
! 236: {
! 237: enable_disable_all(this, segment, FALSE, notify);
! 238: }
! 239:
! 240: METHOD(listener_t, alert_hook, bool,
! 241: private_ha_segments_t *this, ike_sa_t *ike_sa, alert_t alert, va_list args)
! 242: {
! 243: if (alert == ALERT_SHUTDOWN_SIGNAL)
! 244: {
! 245: if (this->heartbeat_active)
! 246: {
! 247: DBG1(DBG_CFG, "HA heartbeat active, dropping all segments");
! 248: deactivate(this, 0, TRUE);
! 249: }
! 250: else
! 251: {
! 252: DBG1(DBG_CFG, "no HA heartbeat active, closing IKE_SAs");
! 253: }
! 254: }
! 255: return TRUE;
! 256: }
! 257:
! 258: /**
! 259: * Monitor heartbeat activity of remote node
! 260: */
! 261: static job_requeue_t watchdog(private_ha_segments_t *this)
! 262: {
! 263: bool timeout, oldstate;
! 264:
! 265: this->mutex->lock(this->mutex);
! 266: thread_cleanup_push((void*)this->mutex->unlock, this->mutex);
! 267: oldstate = thread_cancelability(TRUE);
! 268: timeout = this->condvar->timed_wait(this->condvar, this->mutex,
! 269: this->heartbeat_timeout);
! 270: thread_cancelability(oldstate);
! 271: thread_cleanup_pop(TRUE);
! 272: if (timeout)
! 273: {
! 274: DBG1(DBG_CFG, "no heartbeat received, taking all segments");
! 275: activate(this, 0, TRUE);
! 276: /* disable heartbeat detection util we get one */
! 277: this->heartbeat_active = FALSE;
! 278: return JOB_REQUEUE_NONE;
! 279: }
! 280: return JOB_REQUEUE_DIRECT;
! 281: }
! 282:
! 283: /**
! 284: * Start the heartbeat detection thread
! 285: */
! 286: static void start_watchdog(private_ha_segments_t *this)
! 287: {
! 288: this->heartbeat_active = TRUE;
! 289: lib->processor->queue_job(lib->processor,
! 290: (job_t*)callback_job_create_with_prio((callback_job_cb_t)watchdog, this,
! 291: NULL, (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL));
! 292: }
! 293:
! 294: METHOD(ha_segments_t, handle_status, void,
! 295: private_ha_segments_t *this, segment_mask_t mask)
! 296: {
! 297: segment_mask_t missing, twice;
! 298: int i;
! 299:
! 300: this->mutex->lock(this->mutex);
! 301:
! 302: missing = ~(this->active | mask);
! 303: twice = this->active & mask;
! 304:
! 305: for (i = 1; i <= this->count; i++)
! 306: {
! 307: if (missing & SEGMENTS_BIT(i))
! 308: {
! 309: if (this->node == i % 2)
! 310: {
! 311: DBG1(DBG_CFG, "HA segment %d was not handled, taking", i);
! 312: enable_disable(this, i, TRUE, TRUE);
! 313: }
! 314: else
! 315: {
! 316: DBG1(DBG_CFG, "HA segment %d was not handled, dropping", i);
! 317: enable_disable(this, i, FALSE, TRUE);
! 318: }
! 319: }
! 320: if (twice & SEGMENTS_BIT(i))
! 321: {
! 322: if (this->node == i % 2)
! 323: {
! 324: DBG1(DBG_CFG, "HA segment %d was handled twice, taking", i);
! 325: enable_disable(this, i, TRUE, TRUE);
! 326: }
! 327: else
! 328: {
! 329: DBG1(DBG_CFG, "HA segment %d was handled twice, dropping", i);
! 330: enable_disable(this, i, FALSE, TRUE);
! 331: }
! 332: }
! 333: }
! 334:
! 335: this->condvar->signal(this->condvar);
! 336: this->mutex->unlock(this->mutex);
! 337:
! 338: if (!this->heartbeat_active)
! 339: {
! 340: DBG1(DBG_CFG, "received heartbeat, reenabling watchdog");
! 341: start_watchdog(this);
! 342: }
! 343: }
! 344:
! 345: /**
! 346: * Send a status message with our active segments
! 347: */
! 348: static job_requeue_t send_status(private_ha_segments_t *this)
! 349: {
! 350: ha_message_t *message;
! 351: int i;
! 352:
! 353: message = ha_message_create(HA_STATUS);
! 354:
! 355: this->mutex->lock(this->mutex);
! 356: for (i = 1; i <= this->count; i++)
! 357: {
! 358: if (this->active & SEGMENTS_BIT(i))
! 359: {
! 360: message->add_attribute(message, HA_SEGMENT, i);
! 361: }
! 362: }
! 363: this->mutex->unlock(this->mutex);
! 364:
! 365: this->socket->push(this->socket, message);
! 366: message->destroy(message);
! 367:
! 368: /* schedule next invocation */
! 369: return JOB_RESCHEDULE_MS(this->heartbeat_delay);
! 370: }
! 371:
! 372: /**
! 373: * Start the heartbeat sending task
! 374: */
! 375: static void start_heartbeat(private_ha_segments_t *this)
! 376: {
! 377: lib->processor->queue_job(lib->processor,
! 378: (job_t*)callback_job_create_with_prio((callback_job_cb_t)send_status,
! 379: this, NULL, (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL));
! 380: }
! 381:
! 382: /**
! 383: * Take a segment if we are handling less than half of segments
! 384: */
! 385: static job_requeue_t autobalance(private_ha_segments_t *this)
! 386: {
! 387: int i, active = 0;
! 388:
! 389: this->mutex->lock(this->mutex);
! 390:
! 391: for (i = 1; i <= this->count; i++)
! 392: {
! 393: if (this->active & SEGMENTS_BIT(i))
! 394: {
! 395: active++;
! 396: }
! 397: }
! 398: if (active < this->count / 2)
! 399: {
! 400: for (i = 1; i <= this->count; i++)
! 401: {
! 402: if (!(this->active & SEGMENTS_BIT(i)))
! 403: {
! 404: DBG1(DBG_CFG, "autobalancing HA (%d/%d active), taking %d",
! 405: active, this->count, i);
! 406: enable_disable(this, i, TRUE, TRUE);
! 407: /* we claim only one in each interval */
! 408: break;
! 409: }
! 410: }
! 411: }
! 412:
! 413: this->mutex->unlock(this->mutex);
! 414:
! 415: return JOB_RESCHEDULE(this->autobalance);
! 416: }
! 417:
! 418: /**
! 419: * Schedule autobalancing
! 420: */
! 421: static void start_autobalance(private_ha_segments_t *this)
! 422: {
! 423: DBG1(DBG_CFG, "scheduling HA autobalance every %ds", this->autobalance);
! 424: lib->scheduler->schedule_job(lib->scheduler,
! 425: (job_t*)callback_job_create_with_prio((callback_job_cb_t)autobalance,
! 426: this, NULL, (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL),
! 427: this->autobalance);
! 428: }
! 429:
! 430: METHOD(ha_segments_t, is_active, bool,
! 431: private_ha_segments_t *this, u_int segment)
! 432: {
! 433: return (this->active & SEGMENTS_BIT(segment)) != 0;
! 434: }
! 435:
! 436: METHOD(ha_segments_t, count, u_int,
! 437: private_ha_segments_t *this)
! 438: {
! 439: return this->count;
! 440: }
! 441:
! 442: METHOD(ha_segments_t, destroy, void,
! 443: private_ha_segments_t *this)
! 444: {
! 445: this->mutex->destroy(this->mutex);
! 446: this->condvar->destroy(this->condvar);
! 447: free(this);
! 448: }
! 449:
! 450: /**
! 451: * See header
! 452: */
! 453: ha_segments_t *ha_segments_create(ha_socket_t *socket, ha_kernel_t *kernel,
! 454: ha_tunnel_t *tunnel, u_int count, u_int node,
! 455: bool monitor)
! 456: {
! 457: private_ha_segments_t *this;
! 458:
! 459: INIT(this,
! 460: .public = {
! 461: .listener = {
! 462: .alert = _alert_hook,
! 463: },
! 464: .activate = _activate,
! 465: .deactivate = _deactivate,
! 466: .handle_status = _handle_status,
! 467: .is_active = _is_active,
! 468: .count = _count,
! 469: .destroy = _destroy,
! 470: },
! 471: .socket = socket,
! 472: .tunnel = tunnel,
! 473: .kernel = kernel,
! 474: .count = count,
! 475: .node = node,
! 476: .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
! 477: .condvar = condvar_create(CONDVAR_TYPE_DEFAULT),
! 478: .heartbeat_delay = lib->settings->get_int(lib->settings,
! 479: "%s.plugins.ha.heartbeat_delay", DEFAULT_HEARTBEAT_DELAY,
! 480: lib->ns),
! 481: .heartbeat_timeout = lib->settings->get_int(lib->settings,
! 482: "%s.plugins.ha.heartbeat_timeout", DEFAULT_HEARTBEAT_TIMEOUT,
! 483: lib->ns),
! 484: .autobalance = lib->settings->get_int(lib->settings,
! 485: "%s.plugins.ha.autobalance", 0, lib->ns),
! 486: );
! 487:
! 488: if (monitor)
! 489: {
! 490: DBG1(DBG_CFG, "starting HA heartbeat, delay %dms, timeout %dms",
! 491: this->heartbeat_delay, this->heartbeat_timeout);
! 492: start_heartbeat(this);
! 493: start_watchdog(this);
! 494: }
! 495: if (this->autobalance)
! 496: {
! 497: start_autobalance(this);
! 498: }
! 499:
! 500: return &this->public;
! 501: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>