74fb2ac7e2713f82b11e93e2b735692dd5ff18c2
[strongswan.git] / src / libipsec / ipsec_sa_mgr.c
1 /*
2 * Copyright (C) 2012 Tobias Brunner
3 * Copyright (C) 2012 Giuliano Grassi
4 * Copyright (C) 2012 Ralf Sager
5 * Hochschule fuer Technik Rapperswil
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 * for more details.
16 */
17
18 #include "ipsec_sa_mgr.h"
19
20 #include <debug.h>
21 #include <library.h>
22 #include <threading/mutex.h>
23 #include <utils/hashtable.h>
24 #include <utils/linked_list.h>
25
26 typedef struct private_ipsec_sa_mgr_t private_ipsec_sa_mgr_t;
27
28 /**
29 * Private additions to ipsec_sa_mgr_t.
30 */
31 struct private_ipsec_sa_mgr_t {
32
33 /**
34 * Public members of ipsec_sa_mgr_t.
35 */
36 ipsec_sa_mgr_t public;
37
38 /**
39 * Installed SAs
40 */
41 linked_list_t *sas;
42
43 /**
44 * SPIs allocated using get_spi()
45 */
46 hashtable_t *allocated_spis;
47
48 /**
49 * Mutex used to synchronize access to the SA manager
50 */
51 mutex_t *mutex;
52
53 /**
54 * RNG used to generate SPIs
55 */
56 rng_t *rng;
57 };
58
59 /*
60 * Used for the hash table of allocated SPIs
61 */
62 static bool spi_equals(u_int32_t *spi, u_int32_t *other_spi)
63 {
64 return *spi == *other_spi;
65 }
66
67 static u_int spi_hash(u_int32_t *spi)
68 {
69 return chunk_hash(chunk_from_thing(*spi));
70 }
71
72 /**
73 * Flushes all entries
74 * Must be called with this->mutex held.
75 */
76 static void flush_entries(private_ipsec_sa_mgr_t *this)
77 {
78 enumerator_t *enumerator;
79 ipsec_sa_t *current;
80
81 DBG2(DBG_ESP, "flushing SAD");
82
83 enumerator = this->sas->create_enumerator(this->sas);
84 while (enumerator->enumerate(enumerator, (void**)&current))
85 {
86 this->sas->remove_at(this->sas, enumerator);
87 current->destroy(current);
88 }
89 enumerator->destroy(enumerator);
90 }
91
92 /*
93 * Different match functions to find SAs in the linked list
94 */
95 static bool match_entry_by_spi_inbound(ipsec_sa_t *sa, u_int32_t spi,
96 bool inbound)
97 {
98 return sa->get_spi(sa) == spi && sa->is_inbound(sa) == inbound;
99 }
100
101 static bool match_entry_by_spi_src_dst(ipsec_sa_t *sa, u_int32_t spi,
102 host_t *src, host_t *dst)
103 {
104 return sa->match_by_spi_src_dst(sa, spi, src, dst);
105 }
106
107 /**
108 * Remove all allocated SPIs
109 */
110 static void flush_allocated_spis(private_ipsec_sa_mgr_t *this)
111 {
112 enumerator_t *enumerator;
113 u_int32_t *current;
114
115 DBG2(DBG_ESP, "flushing allocated SPIs");
116 enumerator = this->allocated_spis->create_enumerator(this->allocated_spis);
117 while (enumerator->enumerate(enumerator, NULL, (void**)&current))
118 {
119 this->allocated_spis->remove_at(this->allocated_spis, enumerator);
120 DBG2(DBG_ESP, " removed allocated SPI %.8x", ntohl(*current));
121 free(current);
122 }
123 enumerator->destroy(enumerator);
124 }
125
126 /**
127 * Pre-allocate an SPI for an inbound SA
128 */
129 static bool allocate_spi(private_ipsec_sa_mgr_t *this, u_int32_t spi)
130 {
131 u_int32_t *spi_alloc;
132
133 if (this->allocated_spis->get(this->allocated_spis, &spi) ||
134 this->sas->find_first(this->sas, (void*)match_entry_by_spi_inbound,
135 NULL, spi, TRUE) == SUCCESS)
136 {
137 return FALSE;
138 }
139 spi_alloc = malloc_thing(u_int32_t);
140 *spi_alloc = spi;
141 this->allocated_spis->put(this->allocated_spis, spi_alloc, spi_alloc);
142 return TRUE;
143 }
144
145 METHOD(ipsec_sa_mgr_t, get_spi, status_t,
146 private_ipsec_sa_mgr_t *this, host_t *src, host_t *dst, u_int8_t protocol,
147 u_int32_t reqid, u_int32_t *spi)
148 {
149 u_int32_t spi_new;
150
151 DBG2(DBG_ESP, "allocating SPI for reqid {%u}", reqid);
152
153 this->mutex->lock(this->mutex);
154 if (!this->rng)
155 {
156 this->rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
157 if (!this->rng)
158 {
159 this->mutex->unlock(this->mutex);
160 DBG1(DBG_ESP, "failed to create RNG for SPI generation");
161 return FAILED;
162 }
163 }
164
165 do
166 {
167 if (!this->rng->get_bytes(this->rng, sizeof(spi_new),
168 (u_int8_t*)&spi_new))
169 {
170 this->mutex->unlock(this->mutex);
171 DBG1(DBG_ESP, "failed to allocate SPI for reqid {%u}", reqid);
172 return FAILED;
173 }
174 /* make sure the SPI is valid (not in range 0-255) */
175 spi_new |= 0x00000100;
176 spi_new = htonl(spi_new);
177 }
178 while (!allocate_spi(this, spi_new));
179 this->mutex->unlock(this->mutex);
180
181 *spi = spi_new;
182
183 DBG2(DBG_ESP, "allocated SPI %.8x for reqid {%u}", ntohl(*spi), reqid);
184 return SUCCESS;
185 }
186
187 METHOD(ipsec_sa_mgr_t, add_sa, status_t,
188 private_ipsec_sa_mgr_t *this, host_t *src, host_t *dst, u_int32_t spi,
189 u_int8_t protocol, u_int32_t reqid, mark_t mark, u_int32_t tfc,
190 lifetime_cfg_t *lifetime, u_int16_t enc_alg, chunk_t enc_key,
191 u_int16_t int_alg, chunk_t int_key, ipsec_mode_t mode, u_int16_t ipcomp,
192 u_int16_t cpi, bool encap, bool esn, bool inbound,
193 traffic_selector_t *src_ts, traffic_selector_t *dst_ts)
194 {
195 ipsec_sa_t *sa_new;
196
197 DBG2(DBG_ESP, "adding SAD entry with SPI %.8x and reqid {%u}",
198 ntohl(spi), reqid);
199 DBG2(DBG_ESP, " using encryption algorithm %N with key size %d",
200 encryption_algorithm_names, enc_alg, enc_key.len * 8);
201 DBG2(DBG_ESP, " using integrity algorithm %N with key size %d",
202 integrity_algorithm_names, int_alg, int_key.len * 8);
203
204 sa_new = ipsec_sa_create(spi, src, dst, protocol, reqid, mark, tfc,
205 lifetime, enc_alg, enc_key, int_alg, int_key, mode,
206 ipcomp, cpi, encap, esn, inbound, src_ts, dst_ts);
207 if (!sa_new)
208 {
209 DBG1(DBG_ESP, "failed to create SAD entry");
210 return FAILED;
211 }
212
213 this->mutex->lock(this->mutex);
214
215 if (inbound)
216 { /* remove any pre-allocated SPIs */
217 u_int32_t *spi_alloc;
218
219 spi_alloc = this->allocated_spis->remove(this->allocated_spis, &spi);
220 free(spi_alloc);
221 }
222
223 if (this->sas->find_first(this->sas, (void*)match_entry_by_spi_src_dst,
224 NULL, spi, src, dst) == SUCCESS)
225 {
226 this->mutex->unlock(this->mutex);
227 DBG1(DBG_ESP, "failed to install SAD entry: already installed");
228 sa_new->destroy(sa_new);
229 return FAILED;
230 }
231 this->sas->insert_last(this->sas, sa_new);
232 this->mutex->unlock(this->mutex);
233 return SUCCESS;
234 }
235
236 METHOD(ipsec_sa_mgr_t, del_sa, status_t,
237 private_ipsec_sa_mgr_t *this, host_t *src, host_t *dst, u_int32_t spi,
238 u_int8_t protocol, u_int16_t cpi, mark_t mark)
239 {
240 ipsec_sa_t *current, *found = NULL;
241 enumerator_t *enumerator;
242
243 this->mutex->lock(this->mutex);
244 enumerator = this->sas->create_enumerator(this->sas);
245 while (enumerator->enumerate(enumerator, (void**)&current))
246 {
247 if (match_entry_by_spi_src_dst(current, spi, src, dst))
248 {
249 this->sas->remove_at(this->sas, enumerator);
250 found = current;
251 break;
252 }
253 }
254 enumerator->destroy(enumerator);
255 this->mutex->unlock(this->mutex);
256
257 if (found)
258 {
259 DBG2(DBG_ESP, "deleted %sbound SAD entry with SPI %.8x",
260 found->is_inbound(found) ? "in" : "out", ntohl(spi));
261 found->destroy(found);
262 return SUCCESS;
263 }
264 return FAILED;
265 }
266
267 METHOD(ipsec_sa_mgr_t, flush_sas, status_t,
268 private_ipsec_sa_mgr_t *this)
269 {
270 this->mutex->lock(this->mutex);
271 flush_entries(this);
272 this->mutex->unlock(this->mutex);
273 return SUCCESS;
274 }
275
276 METHOD(ipsec_sa_mgr_t, destroy, void,
277 private_ipsec_sa_mgr_t *this)
278 {
279 this->mutex->lock(this->mutex);
280 flush_entries(this);
281 flush_allocated_spis(this);
282 this->mutex->unlock(this->mutex);
283
284 this->allocated_spis->destroy(this->allocated_spis);
285 this->sas->destroy(this->sas);
286
287 this->mutex->destroy(this->mutex);
288 DESTROY_IF(this->rng);
289 free(this);
290 }
291
292 /**
293 * Described in header.
294 */
295 ipsec_sa_mgr_t *ipsec_sa_mgr_create()
296 {
297 private_ipsec_sa_mgr_t *this;
298
299 INIT(this,
300 .public = {
301 .get_spi = _get_spi,
302 .add_sa = _add_sa,
303 .del_sa = _del_sa,
304 .flush_sas = _flush_sas,
305 .destroy = _destroy,
306 },
307 .sas = linked_list_create(),
308 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
309 .allocated_spis = hashtable_create((hashtable_hash_t)spi_hash,
310 (hashtable_equals_t)spi_equals, 16),
311 );
312
313 return &this->public;
314 }