Return to duplicheck_listener.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / strongswan / src / libcharon / plugins / duplicheck |
1.1 misho 1: /* 2: * Copyright (C) 2011 Martin Willi 3: * Copyright (C) 2011 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 "duplicheck_listener.h" 17: 18: #include <daemon.h> 19: #include <threading/mutex.h> 20: #include <collections/hashtable.h> 21: #include <encoding/payloads/delete_payload.h> 22: #include <processing/jobs/delete_ike_sa_job.h> 23: 24: typedef struct private_duplicheck_listener_t private_duplicheck_listener_t; 25: 26: /** 27: * Private data of an duplicheck_listener_t object. 28: */ 29: struct private_duplicheck_listener_t { 30: 31: /** 32: * Public duplicheck_listener_t interface. 33: */ 34: duplicheck_listener_t public; 35: 36: /** 37: * Socket to send notifications to 38: */ 39: duplicheck_notify_t *notify; 40: 41: /** 42: * Mutex to lock hashtables 43: */ 44: mutex_t *mutex; 45: 46: /** 47: * Hashtable of active IKE_SAs, identification_t => entry_t 48: */ 49: hashtable_t *active; 50: 51: /** 52: * Hashtable with active liveness checks, identification_t => entry_t 53: */ 54: hashtable_t *checking; 55: }; 56: 57: /** 58: * Entry for hashtables 59: */ 60: typedef struct { 61: /** peer identity */ 62: identification_t *id; 63: /** list of IKE_SA identifiers, ike_sa_id_t */ 64: linked_list_t *sas; 65: } entry_t; 66: 67: /** 68: * Destroy a hashtable entry 69: */ 70: static void entry_destroy(entry_t *this) 71: { 72: this->id->destroy(this->id); 73: this->sas->destroy_offset(this->sas, offsetof(ike_sa_id_t, destroy)); 74: free(this); 75: } 76: 77: /** 78: * Hashtable hash function 79: */ 80: static u_int hash(identification_t *key) 81: { 82: return chunk_hash(key->get_encoding(key)); 83: } 84: 85: /** 86: * Hashtable equals function 87: */ 88: static bool equals(identification_t *a, identification_t *b) 89: { 90: return a->equals(a, b); 91: } 92: 93: /** 94: * Put an IKE_SA identifier to hashtable 95: */ 96: static void put(hashtable_t *table, identification_t *id, ike_sa_id_t *sa) 97: { 98: entry_t *entry; 99: 100: entry = table->get(table, id); 101: if (!entry) 102: { 103: INIT(entry, 104: .id = id->clone(id), 105: .sas = linked_list_create(), 106: ); 107: table->put(table, entry->id, entry); 108: } 109: entry->sas->insert_last(entry->sas, sa->clone(sa)); 110: } 111: 112: /** 113: * Purge an entry from table if it has no IKE_SA identifiers 114: */ 115: static void remove_if_empty(hashtable_t *table, entry_t *entry) 116: { 117: if (entry->sas->get_count(entry->sas) == 0) 118: { 119: entry = table->remove(table, entry->id); 120: if (entry) 121: { 122: entry_destroy(entry); 123: } 124: } 125: } 126: 127: /** 128: * Remove the first entry found in the table for the given id 129: */ 130: static ike_sa_id_t *remove_first(hashtable_t *table, identification_t *id) 131: { 132: ike_sa_id_t *sa = NULL; 133: entry_t *entry; 134: 135: entry = table->get(table, id); 136: if (entry) 137: { 138: entry->sas->remove_first(entry->sas, (void**)&sa); 139: remove_if_empty(table, entry); 140: } 141: return sa; 142: } 143: 144: /** 145: * Remove a specific IKE_SA ID for the given identity 146: */ 147: static bool remove_specific(hashtable_t *table, identification_t *id, 148: ike_sa_id_t *sa) 149: { 150: enumerator_t *enumerator; 151: bool found = FALSE; 152: entry_t *entry; 153: ike_sa_id_t *current; 154: 155: entry = table->get(table, id); 156: if (entry) 157: { 158: enumerator = entry->sas->create_enumerator(entry->sas); 159: while (enumerator->enumerate(enumerator, ¤t)) 160: { 161: if (sa->equals(sa, current)) 162: { 163: entry->sas->remove_at(entry->sas, enumerator); 164: current->destroy(current); 165: found = TRUE; 166: break; 167: } 168: } 169: enumerator->destroy(enumerator); 170: if (found) 171: { 172: remove_if_empty(table, entry); 173: } 174: } 175: return found; 176: } 177: 178: METHOD(listener_t, ike_rekey, bool, 179: private_duplicheck_listener_t *this, ike_sa_t *old, ike_sa_t *new) 180: { 181: this->mutex->lock(this->mutex); 182: 183: remove_specific(this->active, old->get_other_id(old), old->get_id(old)); 184: put(this->active, new->get_other_id(new), new->get_id(new)); 185: 186: this->mutex->unlock(this->mutex); 187: 188: return TRUE; 189: } 190: 191: METHOD(listener_t, ike_updown, bool, 192: private_duplicheck_listener_t *this, ike_sa_t *ike_sa, bool up) 193: { 194: identification_t *id; 195: ike_sa_id_t *sa; 196: 197: id = ike_sa->get_other_id(ike_sa); 198: 199: this->mutex->lock(this->mutex); 200: if (up) 201: { 202: /* another IKE_SA for this identity active? */ 203: sa = remove_first(this->active, id); 204: if (sa) 205: { 206: DBG1(DBG_CFG, "detected duplicate IKE_SA for '%Y', " 207: "triggering delete for old IKE_SA", id); 208: put(this->checking, id, sa); 209: lib->processor->queue_job(lib->processor, 210: (job_t*)delete_ike_sa_job_create(sa, TRUE)); 211: sa->destroy(sa); 212: } 213: /* register IKE_SA as the new active */ 214: sa = ike_sa->get_id(ike_sa); 215: put(this->active, id, sa); 216: } 217: else 218: { 219: sa = ike_sa->get_id(ike_sa); 220: /* check if closing an IKE_SA currently in checking state */ 221: if (remove_specific(this->checking, id, sa)) 222: { 223: DBG1(DBG_CFG, "delete for duplicate IKE_SA '%Y' timed out, " 224: "keeping new IKE_SA", id); 225: } 226: /* check normal close of IKE_SA */ 227: remove_specific(this->active, id, sa); 228: } 229: this->mutex->unlock(this->mutex); 230: 231: return TRUE; 232: } 233: 234: METHOD(listener_t, message_hook, bool, 235: private_duplicheck_listener_t *this, ike_sa_t *ike_sa, 236: message_t *message, bool incoming, bool plain) 237: { 238: if (incoming && plain && !message->get_request(message)) 239: { 240: identification_t *id; 241: ike_sa_id_t *sa; 242: 243: id = ike_sa->get_other_id(ike_sa); 244: sa = ike_sa->get_id(ike_sa); 245: 246: this->mutex->lock(this->mutex); 247: if (remove_specific(this->checking, id, sa)) 248: { 249: DBG1(DBG_CFG, "got a response on a duplicate IKE_SA for '%Y', " 250: "deleting new IKE_SA", id); 251: charon->bus->alert(charon->bus, ALERT_UNIQUE_KEEP); 252: sa = remove_first(this->active, id); 253: if (sa) 254: { 255: lib->processor->queue_job(lib->processor, 256: (job_t*)delete_ike_sa_job_create(sa, TRUE)); 257: sa->destroy(sa); 258: } 259: this->mutex->unlock(this->mutex); 260: 261: this->notify->send(this->notify, id); 262: } 263: else 264: { 265: this->mutex->unlock(this->mutex); 266: } 267: } 268: return TRUE; 269: } 270: 271: METHOD(duplicheck_listener_t, destroy, void, 272: private_duplicheck_listener_t *this) 273: { 274: enumerator_t *enumerator; 275: identification_t *key; 276: entry_t *value; 277: 278: enumerator = this->active->create_enumerator(this->active); 279: while (enumerator->enumerate(enumerator, &key, &value)) 280: { 281: entry_destroy(value); 282: } 283: enumerator->destroy(enumerator); 284: 285: enumerator = this->checking->create_enumerator(this->checking); 286: while (enumerator->enumerate(enumerator, &key, &value)) 287: { 288: entry_destroy(value); 289: } 290: enumerator->destroy(enumerator); 291: 292: this->active->destroy(this->active); 293: this->checking->destroy(this->checking); 294: this->mutex->destroy(this->mutex); 295: free(this); 296: } 297: 298: /** 299: * See header 300: */ 301: duplicheck_listener_t *duplicheck_listener_create(duplicheck_notify_t *notify) 302: { 303: private_duplicheck_listener_t *this; 304: 305: INIT(this, 306: .public = { 307: .listener = { 308: .ike_rekey = _ike_rekey, 309: .ike_updown = _ike_updown, 310: .message = _message_hook, 311: }, 312: .destroy = _destroy, 313: }, 314: .notify = notify, 315: .mutex = mutex_create(MUTEX_TYPE_DEFAULT), 316: .active = hashtable_create((hashtable_hash_t)hash, 317: (hashtable_equals_t)equals, 32), 318: .checking = hashtable_create((hashtable_hash_t)hash, 319: (hashtable_equals_t)equals, 2), 320: ); 321: 322: return &this->public; 323: }