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
18 #include <utils/debug.h>
21 #include "sql_attribute.h"
23 typedef struct private_sql_attribute_t private_sql_attribute_t
;
26 * private data of sql_attribute
28 struct private_sql_attribute_t
{
33 sql_attribute_t
public;
41 * whether to record lease history in lease table
47 * lookup/insert an identity
49 static u_int
get_identity(private_sql_attribute_t
*this, identification_t
*id
)
54 /* look for peer identity in the identities table */
55 e
= this->db
->query(this->db
,
56 "SELECT id FROM identities WHERE type = ? AND data = ?",
57 DB_INT
, id
->get_type(id
), DB_BLOB
, id
->get_encoding(id
),
60 if (e
&& e
->enumerate(e
, &row
))
66 /* not found, insert new one */
67 if (this->db
->execute(this->db
, &row
,
68 "INSERT INTO identities (type, data) VALUES (?, ?)",
69 DB_INT
, id
->get_type(id
), DB_BLOB
, id
->get_encoding(id
)) == 1)
77 * Lookup an attribute pool by name
79 static u_int
get_attr_pool(private_sql_attribute_t
*this, char *name
)
84 e
= this->db
->query(this->db
,
85 "SELECT id FROM attribute_pools WHERE name = ?",
86 DB_TEXT
, name
, DB_UINT
);
89 e
->enumerate(e
, &row
);
97 * Lookup pool by name and address family
99 static u_int
get_pool(private_sql_attribute_t
*this, char *name
, int family
,
106 e
= this->db
->query(this->db
,
107 "SELECT id, start, timeout FROM pools WHERE name = ?",
108 DB_TEXT
, name
, DB_UINT
, DB_BLOB
, DB_UINT
);
109 if (e
&& e
->enumerate(e
, &pool
, &start
, timeout
))
111 if ((family
== AF_INET
&& start
.len
== 4) ||
112 (family
== AF_INET6
&& start
.len
== 16))
123 * Look up an existing lease
125 static host_t
* check_lease(private_sql_attribute_t
*this, char *name
,
126 u_int pool
, u_int identity
)
133 time_t now
= time(NULL
);
135 e
= this->db
->query(this->db
,
136 "SELECT id, address FROM addresses "
137 "WHERE pool = ? AND identity = ? AND released != 0 LIMIT 1",
138 DB_UINT
, pool
, DB_UINT
, identity
, DB_UINT
, DB_BLOB
);
139 if (!e
|| !e
->enumerate(e
, &id
, &address
))
144 address
= chunk_clonea(address
);
147 if (this->db
->execute(this->db
, NULL
,
148 "UPDATE addresses SET acquired = ?, released = 0 "
149 "WHERE id = ? AND identity = ? AND released != 0",
150 DB_UINT
, now
, DB_UINT
, id
, DB_UINT
, identity
) > 0)
154 host
= host_create_from_chunk(AF_UNSPEC
, address
, 0);
157 DBG1(DBG_CFG
, "acquired existing lease for address %H in"
158 " pool '%s'", host
, name
);
167 * We check for unallocated addresses or expired leases. First we select an
168 * address as a candidate, but double check later on if it is still available
169 * during the update operation. This allows us to work without locking.
171 static host_t
* get_lease(private_sql_attribute_t
*this, char *name
,
172 u_int pool
, u_int timeout
, u_int identity
)
179 time_t now
= time(NULL
);
184 /* check for an expired lease */
185 e
= this->db
->query(this->db
,
186 "SELECT id, address FROM addresses "
187 "WHERE pool = ? AND released != 0 AND released < ? LIMIT 1",
188 DB_UINT
, pool
, DB_UINT
, now
- timeout
, DB_UINT
, DB_BLOB
);
192 /* with static leases, check for an unallocated address */
193 e
= this->db
->query(this->db
,
194 "SELECT id, address FROM addresses "
195 "WHERE pool = ? AND identity = 0 LIMIT 1",
196 DB_UINT
, pool
, DB_UINT
, DB_BLOB
);
200 if (!e
|| !e
->enumerate(e
, &id
, &address
))
205 address
= chunk_clonea(address
);
210 hits
= this->db
->execute(this->db
, NULL
,
211 "UPDATE addresses SET "
212 "acquired = ?, released = 0, identity = ? "
213 "WHERE id = ? AND released != 0 AND released < ?",
214 DB_UINT
, now
, DB_UINT
, identity
,
215 DB_UINT
, id
, DB_UINT
, now
- timeout
);
219 hits
= this->db
->execute(this->db
, NULL
,
220 "UPDATE addresses SET "
221 "acquired = ?, released = 0, identity = ? "
222 "WHERE id = ? AND identity = 0",
223 DB_UINT
, now
, DB_UINT
, identity
, DB_UINT
, id
);
229 host
= host_create_from_chunk(AF_UNSPEC
, address
, 0);
232 DBG1(DBG_CFG
, "acquired new lease for address %H in pool '%s'",
238 DBG1(DBG_CFG
, "no available address found in pool '%s'", name
);
242 METHOD(attribute_provider_t
, acquire_address
, host_t
*,
243 private_sql_attribute_t
*this, linked_list_t
*pools
, identification_t
*id
,
246 enumerator_t
*enumerator
;
247 host_t
*address
= NULL
;
248 u_int identity
, pool
, timeout
;
252 identity
= get_identity(this, id
);
255 family
= requested
->get_family(requested
);
256 /* check for an existing lease in all pools */
257 enumerator
= pools
->create_enumerator(pools
);
258 while (enumerator
->enumerate(enumerator
, &name
))
260 pool
= get_pool(this, name
, family
, &timeout
);
263 address
= check_lease(this, name
, pool
, identity
);
270 enumerator
->destroy(enumerator
);
274 /* get an unallocated address or expired lease */
275 enumerator
= pools
->create_enumerator(pools
);
276 while (enumerator
->enumerate(enumerator
, &name
))
278 pool
= get_pool(this, name
, family
, &timeout
);
281 address
= get_lease(this, name
, pool
, timeout
, identity
);
288 enumerator
->destroy(enumerator
);
294 METHOD(attribute_provider_t
, release_address
, bool,
295 private_sql_attribute_t
*this, linked_list_t
*pools
, host_t
*address
,
296 identification_t
*id
)
298 enumerator_t
*enumerator
;
300 time_t now
= time(NULL
);
305 family
= address
->get_family(address
);
306 enumerator
= pools
->create_enumerator(pools
);
307 while (enumerator
->enumerate(enumerator
, &name
))
309 pool
= get_pool(this, name
, family
, &timeout
);
314 if (this->db
->execute(this->db
, NULL
,
315 "UPDATE addresses SET released = ? WHERE "
316 "pool = ? AND address = ?", DB_UINT
, time(NULL
),
317 DB_UINT
, pool
, DB_BLOB
, address
->get_address(address
)) > 0)
321 this->db
->execute(this->db
, NULL
,
322 "INSERT INTO leases (address, identity, acquired, released)"
323 " SELECT id, identity, acquired, ? FROM addresses "
324 " WHERE pool = ? AND address = ?",
325 DB_UINT
, now
, DB_UINT
, pool
,
326 DB_BLOB
, address
->get_address(address
));
332 enumerator
->destroy(enumerator
);
337 METHOD(attribute_provider_t
, create_attribute_enumerator
, enumerator_t
*,
338 private_sql_attribute_t
*this, linked_list_t
*pools
, identification_t
*id
,
341 enumerator_t
*attr_enumerator
= NULL
;
343 if (vips
->get_count(vips
))
345 enumerator_t
*pool_enumerator
;
349 this->db
->execute(this->db
, NULL
, "BEGIN EXCLUSIVE TRANSACTION");
351 /* in a first step check for attributes that match name and id */
354 u_int identity
= get_identity(this, id
);
356 pool_enumerator
= pools
->create_enumerator(pools
);
357 while (pool_enumerator
->enumerate(pool_enumerator
, &name
))
359 u_int attr_pool
= get_attr_pool(this, name
);
365 attr_enumerator
= this->db
->query(this->db
,
366 "SELECT count(*) FROM attributes "
367 "WHERE pool = ? AND identity = ?",
368 DB_UINT
, attr_pool
, DB_UINT
, identity
, DB_UINT
);
370 if (attr_enumerator
&&
371 attr_enumerator
->enumerate(attr_enumerator
, &count
) &&
374 attr_enumerator
->destroy(attr_enumerator
);
375 attr_enumerator
= this->db
->query(this->db
,
376 "SELECT type, value FROM attributes "
377 "WHERE pool = ? AND identity = ?", DB_UINT
,
378 attr_pool
, DB_UINT
, identity
, DB_INT
, DB_BLOB
);
381 DESTROY_IF(attr_enumerator
);
382 attr_enumerator
= NULL
;
384 pool_enumerator
->destroy(pool_enumerator
);
387 /* in a second step check for attributes that match name */
388 if (!attr_enumerator
)
390 pool_enumerator
= pools
->create_enumerator(pools
);
391 while (pool_enumerator
->enumerate(pool_enumerator
, &name
))
393 u_int attr_pool
= get_attr_pool(this, name
);
399 attr_enumerator
= this->db
->query(this->db
,
400 "SELECT count(*) FROM attributes "
401 "WHERE pool = ? AND identity = 0",
402 DB_UINT
, attr_pool
, DB_UINT
);
404 if (attr_enumerator
&&
405 attr_enumerator
->enumerate(attr_enumerator
, &count
) &&
408 attr_enumerator
->destroy(attr_enumerator
);
409 attr_enumerator
= this->db
->query(this->db
,
410 "SELECT type, value FROM attributes "
411 "WHERE pool = ? AND identity = 0",
412 DB_UINT
, attr_pool
, DB_INT
, DB_BLOB
);
415 DESTROY_IF(attr_enumerator
);
416 attr_enumerator
= NULL
;
418 pool_enumerator
->destroy(pool_enumerator
);
421 this->db
->execute(this->db
, NULL
, "END TRANSACTION");
423 /* lastly try to find global attributes */
424 if (!attr_enumerator
)
426 attr_enumerator
= this->db
->query(this->db
,
427 "SELECT type, value FROM attributes "
428 "WHERE pool = 0 AND identity = 0",
433 return (attr_enumerator ? attr_enumerator
: enumerator_create_empty());
436 METHOD(sql_attribute_t
, destroy
, void,
437 private_sql_attribute_t
*this)
445 sql_attribute_t
*sql_attribute_create(database_t
*db
)
447 private_sql_attribute_t
*this;
448 time_t now
= time(NULL
);
453 .acquire_address
= _acquire_address
,
454 .release_address
= _release_address
,
455 .create_attribute_enumerator
= _create_attribute_enumerator
,
460 .history
= lib
->settings
->get_bool(lib
->settings
,
461 "libhydra.plugins.attr-sql.lease_history", TRUE
),
464 /* close any "online" leases in the case we crashed */
467 this->db
->execute(this->db
, NULL
,
468 "INSERT INTO leases (address, identity, acquired, released)"
469 " SELECT id, identity, acquired, ? FROM addresses "
470 " WHERE released = 0", DB_UINT
, now
);
472 this->db
->execute(this->db
, NULL
,
473 "UPDATE addresses SET released = ? WHERE released = 0",
475 return &this->public;