Return to ha_cache.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / strongswan / src / libcharon / plugins / ha |
1.1 misho 1: /* 2: * Copyright (C) 2010 Martin Willi 3: * Copyright (C) 2010 revosec AG 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_cache.h" 17: 18: #include <collections/hashtable.h> 19: #include <collections/linked_list.h> 20: #include <threading/mutex.h> 21: #include <processing/jobs/callback_job.h> 22: 23: typedef struct private_ha_cache_t private_ha_cache_t; 24: 25: /** 26: * Private data of an ha_cache_t object. 27: */ 28: struct private_ha_cache_t { 29: 30: /** 31: * Public ha_cache_t interface. 32: */ 33: ha_cache_t public; 34: 35: /** 36: * Kernel helper functions 37: */ 38: ha_kernel_t *kernel; 39: 40: /** 41: * Socket to send sync messages over 42: */ 43: ha_socket_t *socket; 44: 45: /** 46: * Tunnel securing sync messages 47: */ 48: ha_tunnel_t *tunnel; 49: 50: /** 51: * Total number of segments 52: */ 53: u_int count; 54: 55: /** 56: * cached entries (ike_sa_t, entry_t) 57: */ 58: hashtable_t *cache; 59: 60: /** 61: * Mutex to lock cache 62: */ 63: mutex_t *mutex; 64: }; 65: 66: /** 67: * Cache entry for an IKE_SA 68: */ 69: typedef struct { 70: /* segment this entry is associate to */ 71: u_int segment; 72: /* ADD message */ 73: ha_message_t *add; 74: /* list of updates UPDATE message */ 75: linked_list_t *updates; 76: /* last initiator mid */ 77: ha_message_t *midi; 78: /* last responder mid */ 79: ha_message_t *midr; 80: /* last IV update */ 81: ha_message_t *iv; 82: } entry_t; 83: 84: /** 85: * Create a entry with an add message 86: */ 87: static entry_t *entry_create(ha_message_t *add) 88: { 89: entry_t *entry; 90: 91: INIT(entry, 92: .add = add, 93: .updates = linked_list_create(), 94: ); 95: return entry; 96: } 97: 98: /** 99: * clean up a entry 100: */ 101: static void entry_destroy(entry_t *entry) 102: { 103: entry->updates->destroy_offset(entry->updates, 104: offsetof(ha_message_t, destroy)); 105: entry->add->destroy(entry->add); 106: DESTROY_IF(entry->midi); 107: DESTROY_IF(entry->midr); 108: DESTROY_IF(entry->iv); 109: free(entry); 110: } 111: 112: METHOD(ha_cache_t, cache, void, 113: private_ha_cache_t *this, ike_sa_t *ike_sa, ha_message_t *message) 114: { 115: entry_t *entry; 116: 117: this->mutex->lock(this->mutex); 118: switch (message->get_type(message)) 119: { 120: case HA_IKE_ADD: 121: entry = entry_create(message); 122: entry = this->cache->put(this->cache, ike_sa, entry); 123: if (entry) 124: { 125: entry_destroy(entry); 126: } 127: break; 128: case HA_IKE_UPDATE: 129: entry = this->cache->get(this->cache, ike_sa); 130: if (entry) 131: { 132: entry->segment = this->kernel->get_segment(this->kernel, 133: ike_sa->get_other_host(ike_sa)); 134: entry->updates->insert_last(entry->updates, message); 135: break; 136: } 137: message->destroy(message); 138: break; 139: case HA_IKE_MID_INITIATOR: 140: entry = this->cache->get(this->cache, ike_sa); 141: if (entry) 142: { 143: DESTROY_IF(entry->midi); 144: entry->midi = message; 145: break; 146: } 147: message->destroy(message); 148: break; 149: case HA_IKE_MID_RESPONDER: 150: entry = this->cache->get(this->cache, ike_sa); 151: if (entry) 152: { 153: DESTROY_IF(entry->midr); 154: entry->midr = message; 155: break; 156: } 157: message->destroy(message); 158: break; 159: case HA_IKE_IV: 160: entry = this->cache->get(this->cache, ike_sa); 161: if (entry) 162: { 163: DESTROY_IF(entry->iv); 164: entry->iv = message; 165: break; 166: } 167: message->destroy(message); 168: break; 169: case HA_IKE_DELETE: 170: entry = this->cache->remove(this->cache, ike_sa); 171: if (entry) 172: { 173: entry_destroy(entry); 174: } 175: message->destroy(message); 176: break; 177: default: 178: message->destroy(message); 179: break; 180: } 181: this->mutex->unlock(this->mutex); 182: } 183: 184: METHOD(ha_cache_t, delete_, void, 185: private_ha_cache_t *this, ike_sa_t *ike_sa) 186: { 187: entry_t *entry; 188: 189: this->mutex->lock(this->mutex); 190: entry = this->cache->remove(this->cache, ike_sa); 191: if (entry) 192: { 193: entry_destroy(entry); 194: } 195: this->mutex->unlock(this->mutex); 196: } 197: 198: /** 199: * Rekey all children of an IKE_SA 200: */ 201: static status_t rekey_children(ike_sa_t *ike_sa) 202: { 203: enumerator_t *enumerator; 204: child_sa_t *child_sa; 205: status_t status = SUCCESS; 206: linked_list_t *children; 207: struct { 208: protocol_id_t protocol; 209: uint32_t spi; 210: } *info; 211: 212: children = linked_list_create(); 213: enumerator = ike_sa->create_child_sa_enumerator(ike_sa); 214: while (enumerator->enumerate(enumerator, &child_sa)) 215: { 216: INIT(info, 217: .protocol = child_sa->get_protocol(child_sa), 218: .spi = child_sa->get_spi(child_sa, TRUE), 219: ); 220: children->insert_last(children, info); 221: } 222: enumerator->destroy(enumerator); 223: 224: enumerator = children->create_enumerator(children); 225: while (enumerator->enumerate(enumerator, &info)) 226: { 227: if (ike_sa->supports_extension(ike_sa, EXT_MS_WINDOWS) && 228: ike_sa->has_condition(ike_sa, COND_NAT_THERE)) 229: { 230: /* NATed Windows clients don't accept CHILD_SA rekeying, but fail 231: * with an "invalid situation" error. We just close the CHILD_SA, 232: * Windows will reestablish it immediately if required. */ 233: DBG1(DBG_CFG, "resyncing CHILD_SA using a delete"); 234: status = ike_sa->delete_child_sa(ike_sa, info->protocol, info->spi, 235: FALSE); 236: } 237: else 238: { 239: DBG1(DBG_CFG, "resyncing CHILD_SA using a rekey"); 240: status = ike_sa->rekey_child_sa(ike_sa, info->protocol, info->spi); 241: } 242: if (status == DESTROY_ME) 243: { 244: break; 245: } 246: } 247: enumerator->destroy(enumerator); 248: children->destroy_function(children, free); 249: 250: return status; 251: } 252: 253: /** 254: * Trigger rekeying of CHILD_SA in segment 255: */ 256: static void rekey_segment(private_ha_cache_t *this, u_int segment) 257: { 258: ike_sa_t *ike_sa; 259: enumerator_t *enumerator; 260: linked_list_t *list; 261: ike_sa_id_t *id; 262: 263: list = linked_list_create(); 264: 265: enumerator = charon->ike_sa_manager->create_enumerator( 266: charon->ike_sa_manager, TRUE); 267: while (enumerator->enumerate(enumerator, &ike_sa)) 268: { 269: if (this->tunnel && this->tunnel->is_sa(this->tunnel, ike_sa)) 270: { 271: continue; 272: } 273: if (ike_sa->get_state(ike_sa) == IKE_ESTABLISHED && 274: this->kernel->get_segment(this->kernel, 275: ike_sa->get_other_host(ike_sa)) == segment) 276: { 277: id = ike_sa->get_id(ike_sa); 278: list->insert_last(list, id->clone(id)); 279: } 280: } 281: enumerator->destroy(enumerator); 282: 283: while (list->remove_last(list, (void**)&id) == SUCCESS) 284: { 285: ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, id); 286: if (ike_sa) 287: { 288: if (rekey_children(ike_sa) != DESTROY_ME) 289: { 290: charon->ike_sa_manager->checkin( 291: charon->ike_sa_manager, ike_sa); 292: } 293: else 294: { 295: charon->ike_sa_manager->checkin_and_destroy( 296: charon->ike_sa_manager, ike_sa); 297: } 298: } 299: id->destroy(id); 300: } 301: list->destroy(list); 302: } 303: 304: METHOD(ha_cache_t, resync, void, 305: private_ha_cache_t *this, u_int segment) 306: { 307: enumerator_t *enumerator, *updates; 308: ike_sa_t *ike_sa; 309: entry_t *entry; 310: ha_message_t *message; 311: 312: DBG1(DBG_CFG, "resyncing HA segment %d", segment); 313: 314: this->mutex->lock(this->mutex); 315: enumerator = this->cache->create_enumerator(this->cache); 316: while (enumerator->enumerate(enumerator, &ike_sa, &entry)) 317: { 318: if (entry->segment == segment) 319: { 320: this->socket->push(this->socket, entry->add); 321: updates = entry->updates->create_enumerator(entry->updates); 322: while (updates->enumerate(updates, &message)) 323: { 324: this->socket->push(this->socket, message); 325: } 326: updates->destroy(updates); 327: if (entry->midi) 328: { 329: this->socket->push(this->socket, entry->midi); 330: } 331: if (entry->midr) 332: { 333: this->socket->push(this->socket, entry->midr); 334: } 335: if (entry->iv) 336: { 337: this->socket->push(this->socket, entry->iv); 338: } 339: } 340: } 341: enumerator->destroy(enumerator); 342: this->mutex->unlock(this->mutex); 343: 344: rekey_segment(this, segment); 345: } 346: 347: /** 348: * Request a resync of all segments 349: */ 350: static job_requeue_t request_resync(private_ha_cache_t *this) 351: { 352: ha_message_t *message; 353: int i; 354: 355: DBG1(DBG_CFG, "requesting HA resynchronization"); 356: 357: message = ha_message_create(HA_RESYNC); 358: for (i = 1; i <= this->count; i++) 359: { 360: message->add_attribute(message, HA_SEGMENT, i); 361: } 362: this->socket->push(this->socket, message); 363: message->destroy(message); 364: return JOB_REQUEUE_NONE; 365: } 366: 367: METHOD(ha_cache_t, destroy, void, 368: private_ha_cache_t *this) 369: { 370: this->cache->destroy(this->cache); 371: this->mutex->destroy(this->mutex); 372: free(this); 373: } 374: 375: /** 376: * See header 377: */ 378: ha_cache_t *ha_cache_create(ha_kernel_t *kernel, ha_socket_t *socket, 379: ha_tunnel_t *tunnel, bool sync, u_int count) 380: { 381: private_ha_cache_t *this; 382: 383: INIT(this, 384: .public = { 385: .cache = _cache, 386: .delete = _delete_, 387: .resync = _resync, 388: .destroy = _destroy, 389: }, 390: .count = count, 391: .kernel = kernel, 392: .socket = socket, 393: .tunnel = tunnel, 394: .cache = hashtable_create(hashtable_hash_ptr, hashtable_equals_ptr, 8), 395: .mutex = mutex_create(MUTEX_TYPE_DEFAULT), 396: ); 397: 398: if (sync) 399: { 400: /* request a resync as soon as we are up */ 401: lib->scheduler->schedule_job(lib->scheduler, (job_t*) 402: callback_job_create_with_prio((callback_job_cb_t)request_resync, 403: this, NULL, NULL, JOB_PRIO_CRITICAL), 1); 404: } 405: return &this->public; 406: }