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.release ISNULL OR p.timeout ISNULL "
99 " OR (l.release < (p.timeout + l.acquire))) "
100 "ORDER BY l.acquire LIMIT 1", DB_TEXT
, name
,
101 DB_INT
, id
->get_type(id
), DB_BLOB
, id
->get_encoding(id
),
105 if (e
->enumerate(e
, &lease
, &address
))
107 /* found one, set the lease to active */
108 if (this->db
->execute(this->db
, NULL
,
109 "UPDATE leases SET release = NULL WHERE id = ?",
112 ip
= ip_from_chunk(address
);
113 DBG1(DBG_CFG
, "reassigning address from valid lease "
114 "from pool %s", name
);
119 this->mutex
->unlock(this->mutex
);
124 * Create a new lease entry for client
126 static host_t
* create_lease(private_sql_attribute_t
*this,
127 char *name
, identification_t
*id
)
132 int pool
, identity
= 0;
135 /* we currently do not use database transactions. While this would be
136 * the clean way, there is no real advantage, but some disadvantages:
137 * - we would require InnoDB for mysql, as MyISAM does not support trans.
138 * - the mysql plugin uses connection pooling, and we would need a
139 * mechanism to lock transactions to a single connection.
141 this->mutex
->lock(this->mutex
);
143 /* find an address which has outdated leases only */
144 e
= this->db
->query(this->db
,
145 "SELECT pool, address FROM leases "
146 "JOIN pools ON leases.pool = pools.id "
148 "GROUP BY address HAVING release NOTNULL "
149 "AND MAX(release) < ? + pools.timeout LIMIT 1",
150 DB_TEXT
, name
, DB_UINT
, time(NULL
),
153 if (!e
|| !e
->enumerate(e
, &pool
, &address
))
156 /* no outdated lease found, acquire new address */
157 e
= this->db
->query(this->db
,
158 "SELECT id, next FROM pools WHERE name = ? AND next <= end",
161 if (!e
|| !e
->enumerate(e
, &pool
, &address
))
163 /* pool seems full */
165 this->mutex
->unlock(this->mutex
);
170 address
= chunk_clonea(address
);
173 /* look for peer identity in the identities table */
174 e
= this->db
->query(this->db
,
175 "SELECT id FROM identities WHERE type = ? AND data = ?",
176 DB_INT
, id
->get_type(id
), DB_BLOB
, id
->get_encoding(id
),
178 if (!e
|| !e
->enumerate(e
, &identity
))
181 /* not found, insert new one */
182 this->db
->execute(this->db
, &identity
,
183 "INSERT INTO identities (type, data) VALUES (?, ?)",
184 DB_INT
, id
->get_type(id
), DB_BLOB
, id
->get_encoding(id
));
190 /* if we have an identity, insert a new lease */
193 if (this->db
->execute(this->db
, NULL
,
194 "INSERT INTO leases (pool, address, identity, acquire) "
195 "VALUES (?, ?, ?, ?)",
196 DB_INT
, pool
, DB_BLOB
, address
, DB_INT
, identity
,
197 DB_UINT
, time(NULL
)) > 0)
199 ip
= ip_from_chunk(address
);
201 { /* update next address, as we have consumed one */
202 increment_chunk(address
);
203 this->db
->execute(this->db
, NULL
,
204 "UPDATE pools set next = ? WHERE id = ?",
205 DB_BLOB
, address
, DB_INT
, pool
);
206 DBG1(DBG_CFG
, "assigning lease with new address "
207 "from pool %s", name
);
211 DBG1(DBG_CFG
, "reassigning address from expired lease "
212 "from pool %s", name
);
216 this->mutex
->unlock(this->mutex
);
221 * Implementation of attribute_provider_t.acquire_address
223 static host_t
* acquire_address(private_sql_attribute_t
*this,
224 char *name
, identification_t
*id
,
225 auth_info_t
*auth
, host_t
*requested
)
229 ip
= get_lease(this, name
, id
);
232 ip
= create_lease(this, name
, id
);
238 * Implementation of attribute_provider_t.release_address
240 static bool release_address(private_sql_attribute_t
*this,
241 char *name
, host_t
*address
)
243 if (this->db
->execute(this->db
, NULL
,
244 "UPDATE leases SET release = ? WHERE "
245 "pool IN (SELECT id FROM pools WHERE name = ?) AND "
246 "address = ? AND release ISNULL",
248 DB_TEXT
, name
, DB_BLOB
, address
->get_address(address
)) > 0)
256 * Implementation of sql_attribute_t.destroy
258 static void destroy(private_sql_attribute_t
*this)
260 this->mutex
->destroy(this->mutex
);
267 sql_attribute_t
*sql_attribute_create(database_t
*db
)
269 private_sql_attribute_t
*this = malloc_thing(private_sql_attribute_t
);
271 this->public.provider
.acquire_address
= (host_t
*(*)(attribute_provider_t
*this, char*, identification_t
*,auth_info_t
*, host_t
*))acquire_address
;
272 this->public.provider
.release_address
= (bool(*)(attribute_provider_t
*this, char*,host_t
*))release_address
;
273 this->public.destroy
= (void(*)(sql_attribute_t
*))destroy
;
276 this->mutex
= mutex_create(MUTEX_DEFAULT
);
278 return &this->public;