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 "sql_attribute.h"
21 #include <utils/mutex.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 * mutex to simulate transactions
47 * convert a address blob to an ip of the correct family
49 static host_t
*ip_from_chunk(chunk_t address
)
54 return host_create_from_chunk(AF_INET
, address
, 0);
56 return host_create_from_chunk(AF_INET6
, address
, 0);
63 * increment a chunk, as it would reprensent a network order integer
65 static void increment_chunk(chunk_t chunk
)
69 for (i
= chunk
.len
- 1; i
>= 0; i
--)
71 if (++chunk
.ptr
[i
] != 0)
79 * Lookup if we have an existing lease
81 static host_t
* get_lease(private_sql_attribute_t
*this,
82 char *name
, identification_t
*id
)
89 /* transaction simulation, see create_lease() */
90 this->mutex
->lock(this->mutex
);
92 /* select a lease for "id" which still valid */
93 e
= this->db
->query(this->db
,
94 "SELECT l.id, l.address FROM leases AS l "
95 "JOIN pools AS p ON l.pool = p.id "
96 "JOIN identities AS i ON l.identity = i.id "
97 "WHERE p.name = ? AND i.type = ? AND i.data = ? "
98 "AND (l.released IS NULL OR p.timeout = 0 "
99 " OR (l.released >= (? - p.timeout))) "
100 "ORDER BY l.acquired LIMIT 1", DB_TEXT
, name
,
101 DB_INT
, id
->get_type(id
), DB_BLOB
, id
->get_encoding(id
),
106 if (e
->enumerate(e
, &lease
, &address
))
108 /* found one, set the lease to active */
109 if (this->db
->execute(this->db
, NULL
,
110 "UPDATE leases SET released = NULL WHERE id = ?",
113 ip
= ip_from_chunk(address
);
114 DBG1(DBG_CFG
, "reassigning address from valid lease "
115 "from pool '%s'", name
);
120 this->mutex
->unlock(this->mutex
);
125 * Create a new lease entry for client
127 static host_t
* create_lease(private_sql_attribute_t
*this,
128 char *name
, identification_t
*id
)
133 u_int pool
, identity
= 0, released
, timeout
;
136 /* we currently do not use database transactions. While this would be
137 * the clean way, there is no real advantage, but some disadvantages:
138 * - we would require InnoDB for mysql, as MyISAM does not support trans.
139 * - the mysql plugin uses connection pooling, and we would need a
140 * mechanism to lock transactions to a single connection.
142 this->mutex
->lock(this->mutex
);
144 /* find an address which has outdated leases only. The HAVING clause filters
145 * out leases which are active (released = NULL) or not expired */
146 e
= this->db
->query(this->db
,
147 "SELECT pool, address, released, timeout FROM leases "
148 "JOIN pools ON leases.pool = pools.id "
149 "WHERE name = ? and timeout > 0 "
150 "GROUP BY address HAVING COUNT(released) = COUNT(*) "
151 "AND MAX(released) < (? - timeout) LIMIT 1",
152 DB_TEXT
, name
, DB_UINT
, time(NULL
),
153 DB_UINT
, DB_BLOB
, DB_UINT
, DB_UINT
);
155 if (!e
|| !e
->enumerate(e
, &pool
, &address
, &released
, &timeout
))
158 /* no outdated lease found, acquire new address */
159 e
= this->db
->query(this->db
,
160 "SELECT id, next FROM pools WHERE name = ? AND next <= end",
163 if (!e
|| !e
->enumerate(e
, &pool
, &address
))
165 /* pool seems full */
167 this->mutex
->unlock(this->mutex
);
172 address
= chunk_clonea(address
);
175 /* look for peer identity in the identities table */
176 e
= this->db
->query(this->db
,
177 "SELECT id FROM identities WHERE type = ? AND data = ?",
178 DB_INT
, id
->get_type(id
), DB_BLOB
, id
->get_encoding(id
),
180 if (!e
|| !e
->enumerate(e
, &identity
))
183 /* not found, insert new one */
184 this->db
->execute(this->db
, &identity
,
185 "INSERT INTO identities (type, data) VALUES (?, ?)",
186 DB_INT
, id
->get_type(id
), DB_BLOB
, id
->get_encoding(id
));
192 /* if we have an identity, insert a new lease */
195 if (this->db
->execute(this->db
, NULL
,
196 "INSERT INTO leases (pool, address, identity, acquired) "
197 "VALUES (?, ?, ?, ?)",
198 DB_UINT
, pool
, DB_BLOB
, address
, DB_UINT
, identity
,
199 DB_UINT
, time(NULL
)) > 0)
201 ip
= ip_from_chunk(address
);
203 { /* update next address, as we have consumed one */
204 increment_chunk(address
);
205 this->db
->execute(this->db
, NULL
,
206 "UPDATE pools SET next = ? WHERE id = ?",
207 DB_BLOB
, address
, DB_UINT
, pool
);
208 DBG1(DBG_CFG
, "assigning lease with new address "
209 "from pool '%s'", name
);
213 DBG1(DBG_CFG
, "reassigning address from expired lease "
214 "from pool '%s'", name
);
218 this->mutex
->unlock(this->mutex
);
223 * Implementation of attribute_provider_t.acquire_address
225 static host_t
* acquire_address(private_sql_attribute_t
*this,
226 char *name
, identification_t
*id
,
227 auth_info_t
*auth
, host_t
*requested
)
229 enumerator_t
*enumerator
;
232 enumerator
= enumerator_create_token(name
, ",", " ");
233 while (enumerator
->enumerate(enumerator
, &name
))
235 ip
= get_lease(this, name
, id
);
240 ip
= create_lease(this, name
, id
);
246 enumerator
->destroy(enumerator
);
251 * Implementation of attribute_provider_t.release_address
253 static bool release_address(private_sql_attribute_t
*this,
254 char *name
, host_t
*address
)
256 enumerator_t
*enumerator
;
258 enumerator
= enumerator_create_token(name
, ",", " ");
259 while (enumerator
->enumerate(enumerator
, &name
))
261 if (this->db
->execute(this->db
, NULL
,
262 "UPDATE leases SET released = ? WHERE "
263 "pool IN (SELECT id FROM pools WHERE name = ?) AND "
264 "address = ? AND released IS NULL",
266 DB_TEXT
, name
, DB_BLOB
, address
->get_address(address
)) > 0)
268 enumerator
->destroy(enumerator
);
272 enumerator
->destroy(enumerator
);
277 * Implementation of sql_attribute_t.destroy
279 static void destroy(private_sql_attribute_t
*this)
281 this->mutex
->destroy(this->mutex
);
288 sql_attribute_t
*sql_attribute_create(database_t
*db
)
290 private_sql_attribute_t
*this = malloc_thing(private_sql_attribute_t
);
292 this->public.provider
.acquire_address
= (host_t
*(*)(attribute_provider_t
*this, char*, identification_t
*,auth_info_t
*, host_t
*))acquire_address
;
293 this->public.provider
.release_address
= (bool(*)(attribute_provider_t
*this, char*,host_t
*))release_address
;
294 this->public.destroy
= (void(*)(sql_attribute_t
*))destroy
;
297 this->mutex
= mutex_create(MUTEX_DEFAULT
);
299 /* close any "online" leases in the case we crashed */
300 this->db
->execute(this->db
, NULL
,
301 "UPDATE leases SET released = ? WHERE released IS NULL",
302 DB_UINT
, time(NULL
));
304 return &this->public;