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
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 * wheter 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)
79 static u_int
get_pool(private_sql_attribute_t
*this, char *name
, u_int
*timeout
)
84 e
= this->db
->query(this->db
, "SELECT id, timeout FROM pools WHERE name = ?",
85 DB_TEXT
, name
, DB_UINT
, DB_UINT
);
86 if (e
&& e
->enumerate(e
, &pool
, timeout
))
96 * Look up an existing lease
98 static host_t
* check_lease(private_sql_attribute_t
*this, char *name
,
99 u_int pool
, u_int identity
)
106 time_t now
= time(NULL
);
108 e
= this->db
->query(this->db
,
109 "SELECT id, address FROM addresses "
110 "WHERE pool = ? AND identity = ? AND released != 0 LIMIT 1",
111 DB_UINT
, pool
, DB_UINT
, identity
, DB_UINT
, DB_BLOB
);
112 if (!e
|| !e
->enumerate(e
, &id
, &address
))
117 address
= chunk_clonea(address
);
120 if (this->db
->execute(this->db
, NULL
,
121 "UPDATE addresses SET acquired = ?, released = 0 "
122 "WHERE id = ? AND identity = ? AND released != 0",
123 DB_UINT
, now
, DB_UINT
, id
, DB_UINT
, identity
) > 0)
127 host
= host_create_from_chunk(AF_UNSPEC
, address
, 0);
130 DBG1(DBG_CFG
, "acquired existing lease for address %H in"
131 " pool '%s'", host
, name
);
140 * We check for unallocated addresses or expired leases. First we select an
141 * address as a candidate, but double check later on if it is still available
142 * during the update operation. This allows us to work without locking.
144 static host_t
* get_lease(private_sql_attribute_t
*this, char *name
,
145 u_int pool
, u_int timeout
, u_int identity
)
152 time_t now
= time(NULL
);
157 /* check for an expired lease */
158 e
= this->db
->query(this->db
,
159 "SELECT id, address FROM addresses "
160 "WHERE pool = ? AND released != 0 AND released < ? LIMIT 1",
161 DB_UINT
, pool
, DB_UINT
, now
- timeout
, DB_UINT
, DB_BLOB
);
165 /* with static leases, check for an unallocated address */
166 e
= this->db
->query(this->db
,
167 "SELECT id, address FROM addresses "
168 "WHERE pool = ? AND identity = 0 LIMIT 1",
169 DB_UINT
, pool
, DB_UINT
, DB_BLOB
);
173 if (!e
|| !e
->enumerate(e
, &id
, &address
))
178 address
= chunk_clonea(address
);
183 hits
= this->db
->execute(this->db
, NULL
,
184 "UPDATE addresses SET "
185 "acquired = ?, released = 0, identity = ? "
186 "WHERE id = ? AND released != 0 AND released < ?",
187 DB_UINT
, now
, DB_UINT
, identity
,
188 DB_UINT
, id
, DB_UINT
, now
- timeout
);
192 hits
= this->db
->execute(this->db
, NULL
,
193 "UPDATE addresses SET "
194 "acquired = ?, released = 0, identity = ? "
195 "WHERE id = ? AND identity = 0",
196 DB_UINT
, now
, DB_UINT
, identity
, DB_UINT
, id
);
202 host
= host_create_from_chunk(AF_UNSPEC
, address
, 0);
205 DBG1(DBG_CFG
, "acquired new lease for address %H in pool '%s'",
211 DBG1(DBG_CFG
, "no available address found in pool '%s'", name
);
216 * Implementation of attribute_provider_t.acquire_address
218 static host_t
* acquire_address(private_sql_attribute_t
*this,
219 char *names
, identification_t
*id
,
222 host_t
*address
= NULL
;
223 u_int identity
, pool
, timeout
;
225 identity
= get_identity(this, id
);
228 /* check for a single pool first (no concatenation and enumeration) */
229 if (strchr(names
, ',') == NULL
)
231 pool
= get_pool(this, names
, &timeout
);
234 /* check for an existing lease */
235 address
= check_lease(this, names
, pool
, identity
);
238 /* get an unallocated address or expired lease */
239 address
= get_lease(this, names
, pool
, timeout
, identity
);
245 enumerator_t
*enumerator
;
248 /* in a first step check for an existing lease over all pools */
249 enumerator
= enumerator_create_token(names
, ",", " ");
250 while (enumerator
->enumerate(enumerator
, &name
))
252 pool
= get_pool(this, name
, &timeout
);
255 address
= check_lease(this, name
, pool
, identity
);
258 enumerator
->destroy(enumerator
);
263 enumerator
->destroy(enumerator
);
265 /* in a second step get an unallocated address or expired lease */
266 enumerator
= enumerator_create_token(names
, ",", " ");
267 while (enumerator
->enumerate(enumerator
, &name
))
269 pool
= get_pool(this, name
, &timeout
);
272 address
= get_lease(this, name
, pool
, timeout
, identity
);
279 enumerator
->destroy(enumerator
);
286 * Implementation of attribute_provider_t.release_address
288 static bool release_address(private_sql_attribute_t
*this,
289 char *name
, host_t
*address
, identification_t
*id
)
291 enumerator_t
*enumerator
;
293 time_t now
= time(NULL
);
295 enumerator
= enumerator_create_token(name
, ",", " ");
296 while (enumerator
->enumerate(enumerator
, &name
))
300 pool
= get_pool(this, name
, &timeout
);
305 this->db
->execute(this->db
, NULL
,
306 "INSERT INTO leases (address, identity, acquired, released)"
307 " SELECT id, identity, acquired, ? FROM addresses "
308 " WHERE pool = ? AND address = ?",
309 DB_UINT
, now
, DB_UINT
, pool
,
310 DB_BLOB
, address
->get_address(address
));
312 if (this->db
->execute(this->db
, NULL
,
313 "UPDATE addresses SET released = ? WHERE "
314 "pool = ? AND address = ?", DB_UINT
, time(NULL
),
315 DB_UINT
, pool
, DB_BLOB
, address
->get_address(address
)) > 0)
322 enumerator
->destroy(enumerator
);
327 * Implementation of sql_attribute_t.create_attribute_enumerator
329 static enumerator_t
* create_attribute_enumerator(private_sql_attribute_t
*this,
330 identification_t
*id
, host_t
*vip
)
334 enumerator_t
*enumerator
;
336 enumerator
= this->db
->query(this->db
,
337 "SELECT type, value FROM attributes", DB_INT
, DB_BLOB
);
343 return enumerator_create_empty();
347 * Implementation of sql_attribute_t.destroy
349 static void destroy(private_sql_attribute_t
*this)
357 sql_attribute_t
*sql_attribute_create(database_t
*db
)
359 private_sql_attribute_t
*this = malloc_thing(private_sql_attribute_t
);
360 time_t now
= time(NULL
);
362 this->public.provider
.acquire_address
= (host_t
*(*)(attribute_provider_t
*this, char*, identification_t
*, host_t
*))acquire_address
;
363 this->public.provider
.release_address
= (bool(*)(attribute_provider_t
*this, char*,host_t
*, identification_t
*))release_address
;
364 this->public.provider
.create_attribute_enumerator
= (enumerator_t
*(*)(attribute_provider_t
*, identification_t
*id
, host_t
*host
))create_attribute_enumerator
;
365 this->public.destroy
= (void(*)(sql_attribute_t
*))destroy
;
368 this->history
= lib
->settings
->get_bool(lib
->settings
,
369 "libhydra.plugins.attr-sql.lease_history", TRUE
);
371 /* close any "online" leases in the case we crashed */
374 this->db
->execute(this->db
, NULL
,
375 "INSERT INTO leases (address, identity, acquired, released)"
376 " SELECT id, identity, acquired, ? FROM addresses "
377 " WHERE released = 0", DB_UINT
, now
);
379 this->db
->execute(this->db
, NULL
,
380 "UPDATE addresses SET released = ? WHERE released = 0",
382 return &this->public;