]> git.ipfire.org Git - people/ms/strongswan.git/blame - src/libstrongswan/plugins/attr_sql/sql_attribute.c
cosmetics
[people/ms/strongswan.git] / src / libstrongswan / plugins / attr_sql / sql_attribute.c
CommitLineData
c4ec8c9d
MW
1/*
2 * Copyright (C) 2008 Martin Willi
3 * 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.
c4ec8c9d
MW
14 */
15
479f2950
MW
16#include <time.h>
17
24779482
AS
18#include <debug.h>
19#include <library.h>
20
21#include "sql_attribute.h"
c4ec8c9d
MW
22
23typedef struct private_sql_attribute_t private_sql_attribute_t;
24
25/**
26 * private data of sql_attribute
27 */
28struct private_sql_attribute_t {
29
30 /**
31 * public functions
32 */
33 sql_attribute_t public;
7daf5226 34
c4ec8c9d
MW
35 /**
36 * database connection
37 */
38 database_t *db;
7daf5226 39
3bc5a137
MW
40 /**
41 * wheter to record lease history in lease table
42 */
43 bool history;
c4ec8c9d
MW
44};
45
c4ec8c9d 46/**
f7198e7e 47 * lookup/insert an identity
c4ec8c9d 48 */
f7198e7e 49static u_int get_identity(private_sql_attribute_t *this, identification_t *id)
c4ec8c9d 50{
f7198e7e
MW
51 enumerator_t *e;
52 u_int row;
7daf5226 53
f7198e7e
MW
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),
58 DB_UINT);
7daf5226 59
f7198e7e 60 if (e && e->enumerate(e, &row))
c4ec8c9d 61 {
f7198e7e
MW
62 e->destroy(e);
63 return row;
64 }
65 DESTROY_IF(e);
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)
70 {
71 return row;
c4ec8c9d 72 }
f7198e7e 73 return 0;
c4ec8c9d
MW
74}
75
76/**
f7198e7e 77 * Lookup pool by name
c4ec8c9d 78 */
f7198e7e 79static u_int get_pool(private_sql_attribute_t *this, char *name, u_int *timeout)
c4ec8c9d
MW
80{
81 enumerator_t *e;
f7198e7e
MW
82 u_int pool;
83
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))
c4ec8c9d 87 {
c4ec8c9d 88 e->destroy(e);
f7198e7e 89 return pool;
c4ec8c9d 90 }
d9ad73d8 91 DESTROY_IF(e);
f7198e7e 92 return 0;
c4ec8c9d
MW
93}
94
95/**
7f522b5f 96 * Look up an existing lease
c4ec8c9d 97 */
7f522b5f
AS
98static host_t* check_lease(private_sql_attribute_t *this, char *name,
99 u_int pool, u_int identity)
c4ec8c9d 100{
f7198e7e 101 while (TRUE)
c4ec8c9d 102 {
7f522b5f
AS
103 u_int id;
104 chunk_t address;
105 enumerator_t *e;
106 time_t now = time(NULL);
107
fa3fe3c1 108 e = this->db->query(this->db,
f7198e7e
MW
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))
fa3fe3c1 113 {
fa3fe3c1 114 DESTROY_IF(e);
7daf5226 115 break;
f7198e7e
MW
116 }
117 address = chunk_clonea(address);
118 e->destroy(e);
7f522b5f 119
f7198e7e
MW
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)
124 {
7f522b5f
AS
125 host_t *host;
126
b8cbb645 127 host = host_create_from_chunk(AF_UNSPEC, address, 0);
f7198e7e
MW
128 if (host)
129 {
24779482
AS
130 DBG1("acquired existing lease for address %H in pool '%s'",
131 host, name);
f7198e7e
MW
132 return host;
133 }
fa3fe3c1 134 }
c4ec8c9d 135 }
7f522b5f
AS
136 return NULL;
137}
138
139/**
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.
143 */
144static host_t* get_lease(private_sql_attribute_t *this, char *name,
145 u_int pool, u_int timeout, u_int identity)
146{
f7198e7e 147 while (TRUE)
fa3fe3c1 148 {
7f522b5f
AS
149 u_int id;
150 chunk_t address;
151 enumerator_t *e;
152 time_t now = time(NULL);
07be083b
AS
153 int hits;
154
155 if (timeout)
156 {
157 /* check for an expired lease */
158 e = this->db->query(this->db,
f7198e7e
MW
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);
07be083b
AS
162 }
163 else
164 {
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);
170
171 }
172
f7198e7e
MW
173 if (!e || !e->enumerate(e, &id, &address))
174 {
175 DESTROY_IF(e);
7daf5226 176 break;
f7198e7e
MW
177 }
178 address = chunk_clonea(address);
fa3fe3c1 179 e->destroy(e);
7daf5226 180
07be083b
AS
181 if (timeout)
182 {
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);
189 }
190 else
191 {
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);
197 }
198 if (hits > 0)
c4ec8c9d 199 {
7f522b5f
AS
200 host_t *host;
201
b8cbb645 202 host = host_create_from_chunk(AF_UNSPEC, address, 0);
f7198e7e 203 if (host)
fa3fe3c1 204 {
24779482
AS
205 DBG1("acquired new lease for address %H in pool '%s'",
206 host, name);
f7198e7e 207 return host;
c4ec8c9d
MW
208 }
209 }
210 }
24779482 211 DBG1("no available address found in pool '%s'", name);
7f522b5f 212 return NULL;
c4ec8c9d
MW
213}
214
215/**
216 * Implementation of attribute_provider_t.acquire_address
217 */
218static host_t* acquire_address(private_sql_attribute_t *this,
7f522b5f 219 char *names, identification_t *id,
a44bb934 220 host_t *requested)
c4ec8c9d 221{
f7198e7e 222 host_t *address = NULL;
7f522b5f
AS
223 u_int identity, pool, timeout;
224
f7198e7e
MW
225 identity = get_identity(this, id);
226 if (identity)
c4ec8c9d 227 {
848133ff
AS
228 /* check for a single pool first (no concatenation and enumeration) */
229 if (strchr(names, ',') == NULL)
7f522b5f 230 {
848133ff 231 pool = get_pool(this, names, &timeout);
7f522b5f
AS
232 if (pool)
233 {
848133ff
AS
234 /* check for an existing lease */
235 address = check_lease(this, names, pool, identity);
236 if (address == NULL)
7f522b5f 237 {
848133ff
AS
238 /* get an unallocated address or expired lease */
239 address = get_lease(this, names, pool, timeout, identity);
7f522b5f
AS
240 }
241 }
242 }
848133ff 243 else
d932435e 244 {
848133ff
AS
245 enumerator_t *enumerator;
246 char *name;
247
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))
251 {
252 pool = get_pool(this, name, &timeout);
253 if (pool)
254 {
255 address = check_lease(this, name, pool, identity);
256 if (address)
257 {
258 enumerator->destroy(enumerator);
259 return address;
260 }
261 }
262 }
263 enumerator->destroy(enumerator);
264
265 /* in a second step get an unallocated address or expired lease */
266 enumerator = enumerator_create_token(names, ",", " ");
267 while (enumerator->enumerate(enumerator, &name))
f7198e7e 268 {
848133ff
AS
269 pool = get_pool(this, name, &timeout);
270 if (pool)
f7198e7e 271 {
848133ff
AS
272 address = get_lease(this, name, pool, timeout, identity);
273 if (address)
274 {
275 break;
276 }
f7198e7e
MW
277 }
278 }
848133ff 279 enumerator->destroy(enumerator);
d932435e 280 }
c4ec8c9d 281 }
f7198e7e 282 return address;
c4ec8c9d
MW
283}
284
285/**
286 * Implementation of attribute_provider_t.release_address
287 */
288static bool release_address(private_sql_attribute_t *this,
876d5c63 289 char *name, host_t *address, identification_t *id)
c4ec8c9d 290{
d932435e 291 enumerator_t *enumerator;
f7198e7e
MW
292 bool found = FALSE;
293 time_t now = time(NULL);
7daf5226 294
d932435e
MW
295 enumerator = enumerator_create_token(name, ",", " ");
296 while (enumerator->enumerate(enumerator, &name))
c4ec8c9d 297 {
f7198e7e 298 u_int pool, timeout;
7daf5226 299
f7198e7e
MW
300 pool = get_pool(this, name, &timeout);
301 if (pool)
d932435e 302 {
3bc5a137
MW
303 if (this->history)
304 {
305 this->db->execute(this->db, NULL,
f7198e7e
MW
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,
3bc5a137
MW
310 DB_BLOB, address->get_address(address));
311 }
312 if (this->db->execute(this->db, NULL,
f7198e7e
MW
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)
316 {
317 found = TRUE;
318 break;
319 }
d932435e 320 }
c4ec8c9d 321 }
d932435e 322 enumerator->destroy(enumerator);
f7198e7e 323 return found;
c4ec8c9d
MW
324}
325
a461e20d
AS
326/**
327 * Implementation of sql_attribute_t.create_attribute_enumerator
328 */
329static enumerator_t* create_attribute_enumerator(private_sql_attribute_t *this,
330 identification_t *id, host_t *vip)
331{
332 if (vip)
333 {
334 enumerator_t *enumerator;
335
336 enumerator = this->db->query(this->db,
3747f0f2 337 "SELECT type, value FROM attributes", DB_INT, DB_BLOB);
a461e20d
AS
338 if (enumerator)
339 {
340 return enumerator;
341 }
342 }
343 return enumerator_create_empty();
344}
345
c4ec8c9d
MW
346/**
347 * Implementation of sql_attribute_t.destroy
348 */
349static void destroy(private_sql_attribute_t *this)
350{
351 free(this);
352}
353
354/*
355 * see header file
356 */
357sql_attribute_t *sql_attribute_create(database_t *db)
358{
359 private_sql_attribute_t *this = malloc_thing(private_sql_attribute_t);
f7198e7e 360 time_t now = time(NULL);
7daf5226 361
a44bb934 362 this->public.provider.acquire_address = (host_t*(*)(attribute_provider_t *this, char*, identification_t *, host_t *))acquire_address;
876d5c63 363 this->public.provider.release_address = (bool(*)(attribute_provider_t *this, char*,host_t *, identification_t*))release_address;
a461e20d 364 this->public.provider.create_attribute_enumerator = (enumerator_t*(*)(attribute_provider_t*, identification_t *id, host_t *host))create_attribute_enumerator;
c4ec8c9d 365 this->public.destroy = (void(*)(sql_attribute_t*))destroy;
7daf5226 366
c4ec8c9d 367 this->db = db;
3bc5a137 368 this->history = lib->settings->get_bool(lib->settings,
d38eb335 369 "libstrongswan.plugins.attr-sql.lease_history", TRUE);
7daf5226 370
5373f2a6 371 /* close any "online" leases in the case we crashed */
3bc5a137
MW
372 if (this->history)
373 {
374 this->db->execute(this->db, NULL,
f7198e7e
MW
375 "INSERT INTO leases (address, identity, acquired, released)"
376 " SELECT id, identity, acquired, ? FROM addresses "
377 " WHERE released = 0", DB_UINT, now);
3bc5a137 378 }
f7198e7e
MW
379 this->db->execute(this->db, NULL,
380 "UPDATE addresses SET released = ? WHERE released = 0",
381 DB_UINT, now);
c4ec8c9d
MW
382 return &this->public;
383}
384