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))) "
100 "ORDER BY l.acquire 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 release = 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 int pool
, identity
= 0;
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 */
145 e
= this->db
->query(this->db
,
146 "SELECT pool, address FROM leases "
147 "JOIN pools ON leases.pool = pools.id "
149 "GROUP BY address HAVING release NOTNULL "
150 "AND MAX(release) < ? + pools.timeout LIMIT 1",
151 DB_TEXT
, name
, DB_UINT
, time(NULL
),
154 if (!e
|| !e
->enumerate(e
, &pool
, &address
))
157 /* no outdated lease found, acquire new address */
158 e
= this->db
->query(this->db
,
159 "SELECT id, next FROM pools WHERE name = ? AND next <= end",
162 if (!e
|| !e
->enumerate(e
, &pool
, &address
))
164 /* pool seems full */
166 this->mutex
->unlock(this->mutex
);
171 address
= chunk_clonea(address
);
174 /* look for peer identity in the identities table */
175 e
= this->db
->query(this->db
,
176 "SELECT id FROM identities WHERE type = ? AND data = ?",
177 DB_INT
, id
->get_type(id
), DB_BLOB
, id
->get_encoding(id
),
179 if (!e
|| !e
->enumerate(e
, &identity
))
182 /* not found, insert new one */
183 this->db
->execute(this->db
, &identity
,
184 "INSERT INTO identities (type, data) VALUES (?, ?)",
185 DB_INT
, id
->get_type(id
), DB_BLOB
, id
->get_encoding(id
));
191 /* if we have an identity, insert a new lease */
194 if (this->db
->execute(this->db
, NULL
,
195 "INSERT INTO leases (pool, address, identity, acquire) "
196 "VALUES (?, ?, ?, ?)",
197 DB_INT
, pool
, DB_BLOB
, address
, DB_INT
, identity
,
198 DB_UINT
, time(NULL
)) > 0)
200 ip
= ip_from_chunk(address
);
202 { /* update next address, as we have consumed one */
203 increment_chunk(address
);
204 this->db
->execute(this->db
, NULL
,
205 "UPDATE pools set next = ? WHERE id = ?",
206 DB_BLOB
, address
, DB_INT
, pool
);
207 DBG1(DBG_CFG
, "assigning lease with new address "
208 "from pool %s", name
);
212 DBG1(DBG_CFG
, "reassigning address from expired lease "
213 "from pool %s", name
);
217 this->mutex
->unlock(this->mutex
);
222 * Implementation of attribute_provider_t.acquire_address
224 static host_t
* acquire_address(private_sql_attribute_t
*this,
225 char *name
, identification_t
*id
,
226 auth_info_t
*auth
, host_t
*requested
)
230 ip
= get_lease(this, name
, id
);
233 ip
= create_lease(this, name
, id
);
239 * Implementation of attribute_provider_t.release_address
241 static bool release_address(private_sql_attribute_t
*this,
242 char *name
, host_t
*address
)
244 if (this->db
->execute(this->db
, NULL
,
245 "UPDATE leases SET release = ? WHERE "
246 "pool IN (SELECT id FROM pools WHERE name = ?) AND "
247 "address = ? AND release ISNULL",
249 DB_TEXT
, name
, DB_BLOB
, address
->get_address(address
)) > 0)
257 * Implementation of sql_attribute_t.destroy
259 static void destroy(private_sql_attribute_t
*this)
261 this->mutex
->destroy(this->mutex
);
268 sql_attribute_t
*sql_attribute_create(database_t
*db
)
270 private_sql_attribute_t
*this = malloc_thing(private_sql_attribute_t
);
272 this->public.provider
.acquire_address
= (host_t
*(*)(attribute_provider_t
*this, char*, identification_t
*,auth_info_t
*, host_t
*))acquire_address
;
273 this->public.provider
.release_address
= (bool(*)(attribute_provider_t
*this, char*,host_t
*))release_address
;
274 this->public.destroy
= (void(*)(sql_attribute_t
*))destroy
;
277 this->mutex
= mutex_create(MUTEX_DEFAULT
);
279 return &this->public;