IPsec policy manager added
[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.h"
19 #include "ipsec_sa_mgr.h"
20
21 #include <debug.h>
22 #include <library.h>
23 #include <processing/jobs/callback_job.h>
24 #include <threading/mutex.h>
25 #include <utils/hashtable.h>
26 #include <utils/linked_list.h>
27
28 typedef struct private_ipsec_sa_mgr_t private_ipsec_sa_mgr_t;
29
30 /**
31 * Private additions to ipsec_sa_mgr_t.
32 */
33 struct private_ipsec_sa_mgr_t {
34
35 /**
36 * Public members of ipsec_sa_mgr_t.
37 */
38 ipsec_sa_mgr_t public;
39
40 /**
41 * Installed SAs
42 */
43 linked_list_t *sas;
44
45 /**
46 * SPIs allocated using get_spi()
47 */
48 hashtable_t *allocated_spis;
49
50 /**
51 * Mutex used to synchronize access to the SA manager
52 */
53 mutex_t *mutex;
54
55 /**
56 * RNG used to generate SPIs
57 */
58 rng_t *rng;
59 };
60
61 /**
62 * Helper struct for expiration events
63 */
64 typedef struct {
65
66 /**
67 * IPsec SA manager
68 */
69 private_ipsec_sa_mgr_t *manager;
70
71 /**
72 * SA that expired
73 */
74 ipsec_sa_t *sa;
75
76 /**
77 * 0 if this is a hard expire, otherwise the offset in s (soft->hard)
78 */
79 u_int32_t hard_offset;
80
81 } ipsec_sa_expired_t;
82
83 /*
84 * Used for the hash table of allocated SPIs
85 */
86 static bool spi_equals(u_int32_t *spi, u_int32_t *other_spi)
87 {
88 return *spi == *other_spi;
89 }
90
91 static u_int spi_hash(u_int32_t *spi)
92 {
93 return chunk_hash(chunk_from_thing(*spi));
94 }
95
96 /**
97 * Flushes all entries
98 * Must be called with this->mutex held.
99 */
100 static void flush_entries(private_ipsec_sa_mgr_t *this)
101 {
102 enumerator_t *enumerator;
103 ipsec_sa_t *current;
104
105 DBG2(DBG_ESP, "flushing SAD");
106
107 enumerator = this->sas->create_enumerator(this->sas);
108 while (enumerator->enumerate(enumerator, (void**)&current))
109 {
110 this->sas->remove_at(this->sas, enumerator);
111 current->destroy(current);
112 }
113 enumerator->destroy(enumerator);
114 }
115
116 /*
117 * Different match functions to find SAs in the linked list
118 */
119 static bool match_entry_by_ptr(ipsec_sa_t *sa, ipsec_sa_t *other)
120 {
121 return sa == other;
122 }
123
124 static bool match_entry_by_spi_inbound(ipsec_sa_t *sa, u_int32_t spi,
125 bool inbound)
126 {
127 return sa->get_spi(sa) == spi && sa->is_inbound(sa) == inbound;
128 }
129
130 static bool match_entry_by_spi_src_dst(ipsec_sa_t *sa, u_int32_t spi,
131 host_t *src, host_t *dst)
132 {
133 return sa->match_by_spi_src_dst(sa, spi, src, dst);
134 }
135
136 /**
137 * Callback for expiration events
138 */
139 static job_requeue_t sa_expired(ipsec_sa_expired_t *expired)
140 {
141 private_ipsec_sa_mgr_t *this = expired->manager;
142
143 this->mutex->lock(this->mutex);
144 if (this->sas->find_first(this->sas, (void*)match_entry_by_ptr,
145 NULL, expired->sa) == SUCCESS)
146 {
147 u_int32_t hard_offset = expired->hard_offset;
148 ipsec_sa_t *sa = expired->sa;
149
150 ipsec->events->expire(ipsec->events, sa->get_reqid(sa),
151 sa->get_protocol(sa), sa->get_spi(sa),
152 hard_offset == 0);
153 if (hard_offset)
154 { /* soft limit reached, schedule hard expire */
155 expired->hard_offset = 0;
156 this->mutex->unlock(this->mutex);
157 return JOB_RESCHEDULE(hard_offset);
158 }
159 /* hard limit reached */
160 this->sas->remove(this->sas, sa, NULL);
161 sa->destroy(sa);
162 }
163 this->mutex->unlock(this->mutex);
164 return JOB_REQUEUE_NONE;
165 }
166
167 /**
168 * Schedule a job to handle IPsec SA expiration
169 */
170 static void schedule_expiration(private_ipsec_sa_mgr_t *this,
171 ipsec_sa_t *sa)
172 {
173 lifetime_cfg_t *lifetime = sa->get_lifetime(sa);
174 ipsec_sa_expired_t *expired;
175 callback_job_t *job;
176 u_int32_t timeout;
177
178 INIT(expired,
179 .manager = this,
180 .sa = sa,
181 );
182
183 /* schedule a rekey first, a hard timeout will be scheduled then, if any */
184 expired->hard_offset = lifetime->time.life - lifetime->time.rekey;
185 timeout = lifetime->time.rekey;
186
187 if (lifetime->time.life <= lifetime->time.rekey ||
188 lifetime->time.rekey == 0)
189 { /* no rekey, schedule hard timeout */
190 expired->hard_offset = 0;
191 timeout = lifetime->time.life;
192 }
193
194 job = callback_job_create((callback_job_cb_t)sa_expired, expired,
195 (callback_job_cleanup_t)free, NULL);
196 lib->scheduler->schedule_job(lib->scheduler, (job_t*)job, timeout);
197 }
198
199 /**
200 * Remove all allocated SPIs
201 */
202 static void flush_allocated_spis(private_ipsec_sa_mgr_t *this)
203 {
204 enumerator_t *enumerator;
205 u_int32_t *current;
206
207 DBG2(DBG_ESP, "flushing allocated SPIs");
208 enumerator = this->allocated_spis->create_enumerator(this->allocated_spis);
209 while (enumerator->enumerate(enumerator, NULL, (void**)&current))
210 {
211 this->allocated_spis->remove_at(this->allocated_spis, enumerator);
212 DBG2(DBG_ESP, " removed allocated SPI %.8x", ntohl(*current));
213 free(current);
214 }
215 enumerator->destroy(enumerator);
216 }
217
218 /**
219 * Pre-allocate an SPI for an inbound SA
220 */
221 static bool allocate_spi(private_ipsec_sa_mgr_t *this, u_int32_t spi)
222 {
223 u_int32_t *spi_alloc;
224
225 if (this->allocated_spis->get(this->allocated_spis, &spi) ||
226 this->sas->find_first(this->sas, (void*)match_entry_by_spi_inbound,
227 NULL, spi, TRUE) == SUCCESS)
228 {
229 return FALSE;
230 }
231 spi_alloc = malloc_thing(u_int32_t);
232 *spi_alloc = spi;
233 this->allocated_spis->put(this->allocated_spis, spi_alloc, spi_alloc);
234 return TRUE;
235 }
236
237 METHOD(ipsec_sa_mgr_t, get_spi, status_t,
238 private_ipsec_sa_mgr_t *this, host_t *src, host_t *dst, u_int8_t protocol,
239 u_int32_t reqid, u_int32_t *spi)
240 {
241 u_int32_t spi_new;
242
243 DBG2(DBG_ESP, "allocating SPI for reqid {%u}", reqid);
244
245 this->mutex->lock(this->mutex);
246 if (!this->rng)
247 {
248 this->rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
249 if (!this->rng)
250 {
251 this->mutex->unlock(this->mutex);
252 DBG1(DBG_ESP, "failed to create RNG for SPI generation");
253 return FAILED;
254 }
255 }
256
257 do
258 {
259 if (!this->rng->get_bytes(this->rng, sizeof(spi_new),
260 (u_int8_t*)&spi_new))
261 {
262 this->mutex->unlock(this->mutex);
263 DBG1(DBG_ESP, "failed to allocate SPI for reqid {%u}", reqid);
264 return FAILED;
265 }
266 /* make sure the SPI is valid (not in range 0-255) */
267 spi_new |= 0x00000100;
268 spi_new = htonl(spi_new);
269 }
270 while (!allocate_spi(this, spi_new));
271 this->mutex->unlock(this->mutex);
272
273 *spi = spi_new;
274
275 DBG2(DBG_ESP, "allocated SPI %.8x for reqid {%u}", ntohl(*spi), reqid);
276 return SUCCESS;
277 }
278
279 METHOD(ipsec_sa_mgr_t, add_sa, status_t,
280 private_ipsec_sa_mgr_t *this, host_t *src, host_t *dst, u_int32_t spi,
281 u_int8_t protocol, u_int32_t reqid, mark_t mark, u_int32_t tfc,
282 lifetime_cfg_t *lifetime, u_int16_t enc_alg, chunk_t enc_key,
283 u_int16_t int_alg, chunk_t int_key, ipsec_mode_t mode, u_int16_t ipcomp,
284 u_int16_t cpi, bool encap, bool esn, bool inbound,
285 traffic_selector_t *src_ts, traffic_selector_t *dst_ts)
286 {
287 ipsec_sa_t *sa_new;
288
289 DBG2(DBG_ESP, "adding SAD entry with SPI %.8x and reqid {%u}",
290 ntohl(spi), reqid);
291 DBG2(DBG_ESP, " using encryption algorithm %N with key size %d",
292 encryption_algorithm_names, enc_alg, enc_key.len * 8);
293 DBG2(DBG_ESP, " using integrity algorithm %N with key size %d",
294 integrity_algorithm_names, int_alg, int_key.len * 8);
295
296 sa_new = ipsec_sa_create(spi, src, dst, protocol, reqid, mark, tfc,
297 lifetime, enc_alg, enc_key, int_alg, int_key, mode,
298 ipcomp, cpi, encap, esn, inbound, src_ts, dst_ts);
299 if (!sa_new)
300 {
301 DBG1(DBG_ESP, "failed to create SAD entry");
302 return FAILED;
303 }
304
305 this->mutex->lock(this->mutex);
306
307 if (inbound)
308 { /* remove any pre-allocated SPIs */
309 u_int32_t *spi_alloc;
310
311 spi_alloc = this->allocated_spis->remove(this->allocated_spis, &spi);
312 free(spi_alloc);
313 }
314
315 if (this->sas->find_first(this->sas, (void*)match_entry_by_spi_src_dst,
316 NULL, spi, src, dst) == SUCCESS)
317 {
318 this->mutex->unlock(this->mutex);
319 DBG1(DBG_ESP, "failed to install SAD entry: already installed");
320 sa_new->destroy(sa_new);
321 return FAILED;
322 }
323
324 schedule_expiration(this, sa_new);
325 this->sas->insert_last(this->sas, sa_new);
326
327 this->mutex->unlock(this->mutex);
328 return SUCCESS;
329 }
330
331 METHOD(ipsec_sa_mgr_t, del_sa, status_t,
332 private_ipsec_sa_mgr_t *this, host_t *src, host_t *dst, u_int32_t spi,
333 u_int8_t protocol, u_int16_t cpi, mark_t mark)
334 {
335 ipsec_sa_t *current, *found = NULL;
336 enumerator_t *enumerator;
337
338 this->mutex->lock(this->mutex);
339 enumerator = this->sas->create_enumerator(this->sas);
340 while (enumerator->enumerate(enumerator, (void**)&current))
341 {
342 if (match_entry_by_spi_src_dst(current, spi, src, dst))
343 {
344 this->sas->remove_at(this->sas, enumerator);
345 found = current;
346 break;
347 }
348 }
349 enumerator->destroy(enumerator);
350 this->mutex->unlock(this->mutex);
351
352 if (found)
353 {
354 DBG2(DBG_ESP, "deleted %sbound SAD entry with SPI %.8x",
355 found->is_inbound(found) ? "in" : "out", ntohl(spi));
356 found->destroy(found);
357 return SUCCESS;
358 }
359 return FAILED;
360 }
361
362 METHOD(ipsec_sa_mgr_t, flush_sas, status_t,
363 private_ipsec_sa_mgr_t *this)
364 {
365 this->mutex->lock(this->mutex);
366 flush_entries(this);
367 this->mutex->unlock(this->mutex);
368 return SUCCESS;
369 }
370
371 METHOD(ipsec_sa_mgr_t, destroy, void,
372 private_ipsec_sa_mgr_t *this)
373 {
374 this->mutex->lock(this->mutex);
375 flush_entries(this);
376 flush_allocated_spis(this);
377 this->mutex->unlock(this->mutex);
378
379 this->allocated_spis->destroy(this->allocated_spis);
380 this->sas->destroy(this->sas);
381
382 this->mutex->destroy(this->mutex);
383 DESTROY_IF(this->rng);
384 free(this);
385 }
386
387 /**
388 * Described in header.
389 */
390 ipsec_sa_mgr_t *ipsec_sa_mgr_create()
391 {
392 private_ipsec_sa_mgr_t *this;
393
394 INIT(this,
395 .public = {
396 .get_spi = _get_spi,
397 .add_sa = _add_sa,
398 .del_sa = _del_sa,
399 .flush_sas = _flush_sas,
400 .destroy = _destroy,
401 },
402 .sas = linked_list_create(),
403 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
404 .allocated_spis = hashtable_create((hashtable_hash_t)spi_hash,
405 (hashtable_equals_t)spi_equals, 16),
406 );
407
408 return &this->public;
409 }