Annotation of embedaddon/strongswan/src/libcharon/plugins/attr_sql/attr_sql_provider.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 <time.h>
! 17:
! 18: #include <utils/debug.h>
! 19: #include <library.h>
! 20:
! 21: #include "attr_sql_provider.h"
! 22:
! 23: typedef struct private_attr_sql_provider_t private_attr_sql_provider_t;
! 24:
! 25: /**
! 26: * private data of attr_sql_provider
! 27: */
! 28: struct private_attr_sql_provider_t {
! 29:
! 30: /**
! 31: * public functions
! 32: */
! 33: attr_sql_provider_t public;
! 34:
! 35: /**
! 36: * database connection
! 37: */
! 38: database_t *db;
! 39:
! 40: /**
! 41: * whether to record lease history in lease table
! 42: */
! 43: bool history;
! 44: };
! 45:
! 46: /**
! 47: * lookup/insert an identity
! 48: */
! 49: static u_int get_identity(private_attr_sql_provider_t *this, ike_sa_t *ike_sa)
! 50: {
! 51: identification_t *id;
! 52: enumerator_t *e;
! 53: u_int row;
! 54:
! 55: id = ike_sa->get_other_eap_id(ike_sa);
! 56:
! 57: this->db->transaction(this->db, TRUE);
! 58: /* look for peer identity in the identities table */
! 59: e = this->db->query(this->db,
! 60: "SELECT id FROM identities WHERE type = ? AND data = ?",
! 61: DB_INT, id->get_type(id), DB_BLOB, id->get_encoding(id),
! 62: DB_UINT);
! 63: if (e && e->enumerate(e, &row))
! 64: {
! 65: e->destroy(e);
! 66: this->db->commit(this->db);
! 67: return row;
! 68: }
! 69: DESTROY_IF(e);
! 70: /* not found, insert new one */
! 71: if (this->db->execute(this->db, &row,
! 72: "INSERT INTO identities (type, data) VALUES (?, ?)",
! 73: DB_INT, id->get_type(id), DB_BLOB, id->get_encoding(id)) == 1)
! 74: {
! 75: this->db->commit(this->db);
! 76: return row;
! 77: }
! 78: this->db->rollback(this->db);
! 79: return 0;
! 80: }
! 81:
! 82: /**
! 83: * Lookup an attribute pool by name
! 84: */
! 85: static u_int get_attr_pool(private_attr_sql_provider_t *this, char *name)
! 86: {
! 87: enumerator_t *e;
! 88: u_int row = 0;
! 89:
! 90: e = this->db->query(this->db,
! 91: "SELECT id FROM attribute_pools WHERE name = ?",
! 92: DB_TEXT, name, DB_UINT);
! 93: if (e)
! 94: {
! 95: e->enumerate(e, &row);
! 96: }
! 97: DESTROY_IF(e);
! 98:
! 99: return row;
! 100: }
! 101:
! 102: /**
! 103: * Lookup pool by name and address family
! 104: */
! 105: static u_int get_pool(private_attr_sql_provider_t *this, char *name, int family,
! 106: u_int *timeout)
! 107: {
! 108: enumerator_t *e;
! 109: chunk_t start;
! 110: u_int pool;
! 111:
! 112: e = this->db->query(this->db,
! 113: "SELECT id, start, timeout FROM pools WHERE name = ?",
! 114: DB_TEXT, name, DB_UINT, DB_BLOB, DB_UINT);
! 115: if (e && e->enumerate(e, &pool, &start, timeout))
! 116: {
! 117: if ((family == AF_INET && start.len == 4) ||
! 118: (family == AF_INET6 && start.len == 16))
! 119: {
! 120: e->destroy(e);
! 121: return pool;
! 122: }
! 123: }
! 124: DESTROY_IF(e);
! 125: return 0;
! 126: }
! 127:
! 128: /**
! 129: * Look up an existing lease
! 130: */
! 131: static host_t* check_lease(private_attr_sql_provider_t *this, char *name,
! 132: u_int pool, u_int identity)
! 133: {
! 134: while (TRUE)
! 135: {
! 136: u_int id;
! 137: chunk_t address;
! 138: enumerator_t *e;
! 139: time_t now = time(NULL);
! 140:
! 141: e = this->db->query(this->db,
! 142: "SELECT id, address FROM addresses "
! 143: "WHERE pool = ? AND identity = ? AND released != 0 LIMIT 1",
! 144: DB_UINT, pool, DB_UINT, identity, DB_UINT, DB_BLOB);
! 145: if (!e || !e->enumerate(e, &id, &address))
! 146: {
! 147: DESTROY_IF(e);
! 148: break;
! 149: }
! 150: address = chunk_clonea(address);
! 151: e->destroy(e);
! 152:
! 153: if (this->db->execute(this->db, NULL,
! 154: "UPDATE addresses SET acquired = ?, released = 0 "
! 155: "WHERE id = ? AND identity = ? AND released != 0",
! 156: DB_UINT, now, DB_UINT, id, DB_UINT, identity) > 0)
! 157: {
! 158: host_t *host;
! 159:
! 160: host = host_create_from_chunk(AF_UNSPEC, address, 0);
! 161: if (host)
! 162: {
! 163: DBG1(DBG_CFG, "acquired existing lease for address %H in"
! 164: " pool '%s'", host, name);
! 165: return host;
! 166: }
! 167: }
! 168: }
! 169: return NULL;
! 170: }
! 171:
! 172: /**
! 173: * We check for unallocated addresses or expired leases. First we select an
! 174: * address as a candidate, but double check later on if it is still available
! 175: * during the update operation. This allows us to work without locking.
! 176: */
! 177: static host_t* get_lease(private_attr_sql_provider_t *this, char *name,
! 178: u_int pool, u_int timeout, u_int identity)
! 179: {
! 180: while (TRUE)
! 181: {
! 182: u_int id;
! 183: chunk_t address;
! 184: enumerator_t *e;
! 185: time_t now = time(NULL);
! 186: int hits;
! 187:
! 188: if (timeout)
! 189: {
! 190: /* check for an expired lease */
! 191: e = this->db->query(this->db,
! 192: "SELECT id, address FROM addresses "
! 193: "WHERE pool = ? AND released != 0 AND released < ? LIMIT 1",
! 194: DB_UINT, pool, DB_UINT, now - timeout, DB_UINT, DB_BLOB);
! 195: }
! 196: else
! 197: {
! 198: /* with static leases, check for an unallocated address */
! 199: e = this->db->query(this->db,
! 200: "SELECT id, address FROM addresses "
! 201: "WHERE pool = ? AND identity = 0 LIMIT 1",
! 202: DB_UINT, pool, DB_UINT, DB_BLOB);
! 203: }
! 204:
! 205: if (!e || !e->enumerate(e, &id, &address))
! 206: {
! 207: DESTROY_IF(e);
! 208: break;
! 209: }
! 210: address = chunk_clonea(address);
! 211: e->destroy(e);
! 212:
! 213: if (timeout)
! 214: {
! 215: hits = this->db->execute(this->db, NULL,
! 216: "UPDATE addresses SET "
! 217: "acquired = ?, released = 0, identity = ? "
! 218: "WHERE id = ? AND released != 0 AND released < ?",
! 219: DB_UINT, now, DB_UINT, identity,
! 220: DB_UINT, id, DB_UINT, now - timeout);
! 221: }
! 222: else
! 223: {
! 224: hits = this->db->execute(this->db, NULL,
! 225: "UPDATE addresses SET "
! 226: "acquired = ?, released = 0, identity = ? "
! 227: "WHERE id = ? AND identity = 0",
! 228: DB_UINT, now, DB_UINT, identity, DB_UINT, id);
! 229: }
! 230: if (hits > 0)
! 231: {
! 232: host_t *host;
! 233:
! 234: host = host_create_from_chunk(AF_UNSPEC, address, 0);
! 235: if (host)
! 236: {
! 237: DBG1(DBG_CFG, "acquired new lease for address %H in pool '%s'",
! 238: host, name);
! 239: return host;
! 240: }
! 241: }
! 242: }
! 243: DBG1(DBG_CFG, "no available address found in pool '%s'", name);
! 244: return NULL;
! 245: }
! 246:
! 247: METHOD(attribute_provider_t, acquire_address, host_t*,
! 248: private_attr_sql_provider_t *this, linked_list_t *pools, ike_sa_t *ike_sa,
! 249: host_t *requested)
! 250: {
! 251: enumerator_t *enumerator;
! 252: host_t *address = NULL;
! 253: u_int identity, pool, timeout;
! 254: char *name;
! 255: int family;
! 256:
! 257: identity = get_identity(this, ike_sa);
! 258: if (identity)
! 259: {
! 260: family = requested->get_family(requested);
! 261: /* check for an existing lease in all pools */
! 262: enumerator = pools->create_enumerator(pools);
! 263: while (enumerator->enumerate(enumerator, &name))
! 264: {
! 265: pool = get_pool(this, name, family, &timeout);
! 266: if (pool)
! 267: {
! 268: address = check_lease(this, name, pool, identity);
! 269: if (address)
! 270: {
! 271: break;
! 272: }
! 273: }
! 274: }
! 275: enumerator->destroy(enumerator);
! 276:
! 277: if (!address)
! 278: {
! 279: /* get an unallocated address or expired lease */
! 280: enumerator = pools->create_enumerator(pools);
! 281: while (enumerator->enumerate(enumerator, &name))
! 282: {
! 283: pool = get_pool(this, name, family, &timeout);
! 284: if (pool)
! 285: {
! 286: address = get_lease(this, name, pool, timeout, identity);
! 287: if (address)
! 288: {
! 289: break;
! 290: }
! 291: }
! 292: }
! 293: enumerator->destroy(enumerator);
! 294: }
! 295: }
! 296: return address;
! 297: }
! 298:
! 299: METHOD(attribute_provider_t, release_address, bool,
! 300: private_attr_sql_provider_t *this, linked_list_t *pools, host_t *address,
! 301: ike_sa_t *ike_sa)
! 302: {
! 303: enumerator_t *enumerator;
! 304: u_int pool, timeout;
! 305: time_t now = time(NULL);
! 306: bool found = FALSE;
! 307: char *name;
! 308: int family;
! 309:
! 310: family = address->get_family(address);
! 311: enumerator = pools->create_enumerator(pools);
! 312: while (enumerator->enumerate(enumerator, &name))
! 313: {
! 314: pool = get_pool(this, name, family, &timeout);
! 315: if (!pool)
! 316: {
! 317: continue;
! 318: }
! 319: if (this->db->execute(this->db, NULL,
! 320: "UPDATE addresses SET released = ? WHERE "
! 321: "pool = ? AND address = ?", DB_UINT, time(NULL),
! 322: DB_UINT, pool, DB_BLOB, address->get_address(address)) > 0)
! 323: {
! 324: if (this->history)
! 325: {
! 326: this->db->execute(this->db, NULL,
! 327: "INSERT INTO leases (address, identity, acquired, released)"
! 328: " SELECT id, identity, acquired, ? FROM addresses "
! 329: " WHERE pool = ? AND address = ?",
! 330: DB_UINT, now, DB_UINT, pool,
! 331: DB_BLOB, address->get_address(address));
! 332: }
! 333: found = TRUE;
! 334: break;
! 335: }
! 336: }
! 337: enumerator->destroy(enumerator);
! 338:
! 339: return found;
! 340: }
! 341:
! 342: METHOD(attribute_provider_t, create_attribute_enumerator, enumerator_t*,
! 343: private_attr_sql_provider_t *this, linked_list_t *pools, ike_sa_t *ike_sa,
! 344: linked_list_t *vips)
! 345: {
! 346: enumerator_t *attr_enumerator = NULL;
! 347:
! 348: if (vips->get_count(vips))
! 349: {
! 350: enumerator_t *pool_enumerator;
! 351: u_int count;
! 352: char *name;
! 353:
! 354: /* in a first step check for attributes that match name and id */
! 355: if (ike_sa)
! 356: {
! 357: u_int identity = get_identity(this, ike_sa);
! 358:
! 359: pool_enumerator = pools->create_enumerator(pools);
! 360: while (pool_enumerator->enumerate(pool_enumerator, &name))
! 361: {
! 362: u_int attr_pool = get_attr_pool(this, name);
! 363: if (!attr_pool)
! 364: {
! 365: continue;
! 366: }
! 367:
! 368: attr_enumerator = this->db->query(this->db,
! 369: "SELECT count(*) FROM attributes "
! 370: "WHERE pool = ? AND identity = ?",
! 371: DB_UINT, attr_pool, DB_UINT, identity, DB_UINT);
! 372:
! 373: if (attr_enumerator &&
! 374: attr_enumerator->enumerate(attr_enumerator, &count) &&
! 375: count != 0)
! 376: {
! 377: attr_enumerator->destroy(attr_enumerator);
! 378: attr_enumerator = this->db->query(this->db,
! 379: "SELECT type, value FROM attributes "
! 380: "WHERE pool = ? AND identity = ?", DB_UINT,
! 381: attr_pool, DB_UINT, identity, DB_INT, DB_BLOB);
! 382: break;
! 383: }
! 384: DESTROY_IF(attr_enumerator);
! 385: attr_enumerator = NULL;
! 386: }
! 387: pool_enumerator->destroy(pool_enumerator);
! 388: }
! 389:
! 390: /* in a second step check for attributes that match name */
! 391: if (!attr_enumerator)
! 392: {
! 393: pool_enumerator = pools->create_enumerator(pools);
! 394: while (pool_enumerator->enumerate(pool_enumerator, &name))
! 395: {
! 396: u_int attr_pool = get_attr_pool(this, name);
! 397: if (!attr_pool)
! 398: {
! 399: continue;
! 400: }
! 401:
! 402: attr_enumerator = this->db->query(this->db,
! 403: "SELECT count(*) FROM attributes "
! 404: "WHERE pool = ? AND identity = 0",
! 405: DB_UINT, attr_pool, DB_UINT);
! 406:
! 407: if (attr_enumerator &&
! 408: attr_enumerator->enumerate(attr_enumerator, &count) &&
! 409: count != 0)
! 410: {
! 411: attr_enumerator->destroy(attr_enumerator);
! 412: attr_enumerator = this->db->query(this->db,
! 413: "SELECT type, value FROM attributes "
! 414: "WHERE pool = ? AND identity = 0",
! 415: DB_UINT, attr_pool, DB_INT, DB_BLOB);
! 416: break;
! 417: }
! 418: DESTROY_IF(attr_enumerator);
! 419: attr_enumerator = NULL;
! 420: }
! 421: pool_enumerator->destroy(pool_enumerator);
! 422: }
! 423:
! 424: /* lastly try to find global attributes */
! 425: if (!attr_enumerator)
! 426: {
! 427: attr_enumerator = this->db->query(this->db,
! 428: "SELECT type, value FROM attributes "
! 429: "WHERE pool = 0 AND identity = 0",
! 430: DB_INT, DB_BLOB);
! 431: }
! 432: }
! 433:
! 434: return (attr_enumerator ? attr_enumerator : enumerator_create_empty());
! 435: }
! 436:
! 437: METHOD(attr_sql_provider_t, destroy, void,
! 438: private_attr_sql_provider_t *this)
! 439: {
! 440: free(this);
! 441: }
! 442:
! 443: /*
! 444: * see header file
! 445: */
! 446: attr_sql_provider_t *attr_sql_provider_create(database_t *db)
! 447: {
! 448: private_attr_sql_provider_t *this;
! 449:
! 450: INIT(this,
! 451: .public = {
! 452: .provider = {
! 453: .acquire_address = _acquire_address,
! 454: .release_address = _release_address,
! 455: .create_attribute_enumerator = _create_attribute_enumerator,
! 456: },
! 457: .destroy = _destroy,
! 458: },
! 459: .db = db,
! 460: .history = lib->settings->get_bool(lib->settings,
! 461: "%s.plugins.attr-sql.lease_history", TRUE, lib->ns),
! 462: );
! 463:
! 464: if (lib->settings->get_bool(lib->settings,
! 465: "%s.plugins.attr-sql.crash_recovery", TRUE, lib->ns))
! 466: {
! 467: time_t now = time(NULL);
! 468:
! 469: /* close any "online" leases in the case we crashed */
! 470: if (this->history)
! 471: {
! 472: this->db->execute(this->db, NULL,
! 473: "INSERT INTO leases (address, identity, acquired, released)"
! 474: " SELECT id, identity, acquired, ? FROM addresses "
! 475: " WHERE released = 0", DB_UINT, now);
! 476: }
! 477: this->db->execute(this->db, NULL,
! 478: "UPDATE addresses SET released = ? WHERE released = 0",
! 479: DB_UINT, now);
! 480: }
! 481: return &this->public;
! 482: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>