957d930f2697df1fcda1112e3f7a66726086be23
[strongswan.git] / src / libipsec / ipsec_sa_mgr.c
1 /*
2 * Copyright (C) 2012-2017 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 <utils/debug.h>
22 #include <library.h>
23 #include <processing/jobs/callback_job.h>
24 #include <threading/condvar.h>
25 #include <threading/mutex.h>
26 #include <collections/hashtable.h>
27 #include <collections/linked_list.h>
28
29 typedef struct private_ipsec_sa_mgr_t private_ipsec_sa_mgr_t;
30
31 /**
32 * Private additions to ipsec_sa_mgr_t.
33 */
34 struct private_ipsec_sa_mgr_t {
35
36 /**
37 * Public members of ipsec_sa_mgr_t.
38 */
39 ipsec_sa_mgr_t public;
40
41 /**
42 * Installed SAs
43 */
44 linked_list_t *sas;
45
46 /**
47 * SPIs allocated using get_spi()
48 */
49 hashtable_t *allocated_spis;
50
51 /**
52 * Mutex used to synchronize access to the SA manager
53 */
54 mutex_t *mutex;
55
56 /**
57 * RNG used to generate SPIs
58 */
59 rng_t *rng;
60 };
61
62 /**
63 * Struct to keep track of locked IPsec SAs
64 */
65 typedef struct {
66
67 /**
68 * IPsec SA
69 */
70 ipsec_sa_t *sa;
71
72 /**
73 * Set if this SA is currently in use by a thread
74 */
75 bool locked;
76
77 /**
78 * Condvar used by threads to wait for this entry
79 */
80 condvar_t *condvar;
81
82 /**
83 * Number of threads waiting for this entry
84 */
85 u_int waiting_threads;
86
87 /**
88 * Set if this entry is awaiting deletion
89 */
90 bool awaits_deletion;
91
92 } ipsec_sa_entry_t;
93
94 /**
95 * Helper struct for expiration events
96 */
97 typedef struct {
98
99 /**
100 * IPsec SA manager
101 */
102 private_ipsec_sa_mgr_t *manager;
103
104 /**
105 * Entry that expired
106 */
107 ipsec_sa_entry_t *entry;
108
109 /**
110 * 0 if this is a hard expire, otherwise the offset in s (soft->hard)
111 */
112 uint32_t hard_offset;
113
114 } ipsec_sa_expired_t;
115
116 /*
117 * Used for the hash table of allocated SPIs
118 */
119 static bool spi_equals(uint32_t *spi, uint32_t *other_spi)
120 {
121 return *spi == *other_spi;
122 }
123
124 static u_int spi_hash(uint32_t *spi)
125 {
126 return chunk_hash(chunk_from_thing(*spi));
127 }
128
129 /**
130 * Create an SA entry
131 */
132 static ipsec_sa_entry_t *create_entry(ipsec_sa_t *sa)
133 {
134 ipsec_sa_entry_t *this;
135
136 INIT(this,
137 .condvar = condvar_create(CONDVAR_TYPE_DEFAULT),
138 .sa = sa,
139 );
140 return this;
141 }
142
143 /**
144 * Destroy an SA entry
145 */
146 static void destroy_entry(ipsec_sa_entry_t *entry)
147 {
148 entry->condvar->destroy(entry->condvar);
149 entry->sa->destroy(entry->sa);
150 free(entry);
151 }
152
153 /**
154 * Makes sure an entry is safe to remove
155 * Must be called with this->mutex held.
156 *
157 * @return TRUE if entry can be removed, FALSE if entry is already
158 * being removed by another thread
159 */
160 static bool wait_remove_entry(private_ipsec_sa_mgr_t *this,
161 ipsec_sa_entry_t *entry)
162 {
163 if (entry->awaits_deletion)
164 {
165 /* this will be deleted by another thread already */
166 return FALSE;
167 }
168 entry->awaits_deletion = TRUE;
169 while (entry->locked)
170 {
171 entry->condvar->wait(entry->condvar, this->mutex);
172 }
173 while (entry->waiting_threads > 0)
174 {
175 entry->condvar->broadcast(entry->condvar);
176 entry->condvar->wait(entry->condvar, this->mutex);
177 }
178 return TRUE;
179 }
180
181 /**
182 * Waits until an is available and then locks it.
183 * Must only be called with this->mutex held
184 */
185 static bool wait_for_entry(private_ipsec_sa_mgr_t *this,
186 ipsec_sa_entry_t *entry)
187 {
188 while (entry->locked && !entry->awaits_deletion)
189 {
190 entry->waiting_threads++;
191 entry->condvar->wait(entry->condvar, this->mutex);
192 entry->waiting_threads--;
193 }
194 if (entry->awaits_deletion)
195 {
196 /* others may still be waiting, */
197 entry->condvar->signal(entry->condvar);
198 return FALSE;
199 }
200 entry->locked = TRUE;
201 return TRUE;
202 }
203
204 /**
205 * Flushes all entries
206 * Must be called with this->mutex held.
207 */
208 static void flush_entries(private_ipsec_sa_mgr_t *this)
209 {
210 ipsec_sa_entry_t *current;
211 enumerator_t *enumerator;
212
213 DBG2(DBG_ESP, "flushing SAD");
214
215 enumerator = this->sas->create_enumerator(this->sas);
216 while (enumerator->enumerate(enumerator, (void**)&current))
217 {
218 if (wait_remove_entry(this, current))
219 {
220 this->sas->remove_at(this->sas, enumerator);
221 destroy_entry(current);
222 }
223 }
224 enumerator->destroy(enumerator);
225 }
226
227 CALLBACK(match_entry_by_sa_ptr, bool,
228 ipsec_sa_entry_t *item, va_list args)
229 {
230 ipsec_sa_t *sa;
231
232 VA_ARGS_VGET(args, sa);
233 return item->sa == sa;
234 }
235
236 CALLBACK(match_entry_by_spi_inbound, bool,
237 ipsec_sa_entry_t *item, va_list args)
238 {
239 uint32_t spi;
240 int inbound;
241
242 VA_ARGS_VGET(args, spi, inbound);
243 return item->sa->get_spi(item->sa) == spi &&
244 item->sa->is_inbound(item->sa) == inbound;
245 }
246
247 static bool match_entry_by_spi_src_dst(ipsec_sa_entry_t *item, uint32_t spi,
248 host_t *src, host_t *dst)
249 {
250 return item->sa->match_by_spi_src_dst(item->sa, spi, src, dst);
251 }
252
253 CALLBACK(match_entry_by_spi_src_dst_cb, bool,
254 ipsec_sa_entry_t *item, va_list args)
255 {
256 host_t *src, *dst;
257 uint32_t spi;
258
259 VA_ARGS_VGET(args, spi, src, dst);
260 return match_entry_by_spi_src_dst(item, spi, src, dst);
261 }
262
263 CALLBACK(match_entry_by_reqid_inbound, bool,
264 ipsec_sa_entry_t *item, va_list args)
265 {
266 uint32_t reqid;
267 int inbound;
268
269 VA_ARGS_VGET(args, reqid, inbound);
270 return item->sa->match_by_reqid(item->sa, reqid, inbound);
271 }
272
273 CALLBACK(match_entry_by_spi_dst, bool,
274 ipsec_sa_entry_t *item, va_list args)
275 {
276 host_t *dst;
277 uint32_t spi;
278
279 VA_ARGS_VGET(args, spi, dst);
280 return item->sa->match_by_spi_dst(item->sa, spi, dst);
281 }
282
283 /**
284 * Remove an entry
285 */
286 static bool remove_entry(private_ipsec_sa_mgr_t *this, ipsec_sa_entry_t *entry)
287 {
288 ipsec_sa_entry_t *current;
289 enumerator_t *enumerator;
290 bool removed = FALSE;
291
292 enumerator = this->sas->create_enumerator(this->sas);
293 while (enumerator->enumerate(enumerator, (void**)&current))
294 {
295 if (current == entry)
296 {
297 if (wait_remove_entry(this, current))
298 {
299 this->sas->remove_at(this->sas, enumerator);
300 removed = TRUE;
301 }
302 break;
303 }
304 }
305 enumerator->destroy(enumerator);
306 return removed;
307 }
308
309 /**
310 * Callback for expiration events
311 */
312 static job_requeue_t sa_expired(ipsec_sa_expired_t *expired)
313 {
314 private_ipsec_sa_mgr_t *this = expired->manager;
315
316 this->mutex->lock(this->mutex);
317 if (this->sas->find_first(this->sas, NULL, (void**)&expired->entry))
318 {
319 uint32_t hard_offset;
320
321 hard_offset = expired->hard_offset;
322 expired->entry->sa->expire(expired->entry->sa, hard_offset == 0);
323 if (hard_offset)
324 { /* soft limit reached, schedule hard expire */
325 expired->hard_offset = 0;
326 this->mutex->unlock(this->mutex);
327 return JOB_RESCHEDULE(hard_offset);
328 }
329 /* hard limit reached */
330 if (remove_entry(this, expired->entry))
331 {
332 destroy_entry(expired->entry);
333 }
334 }
335 this->mutex->unlock(this->mutex);
336 return JOB_REQUEUE_NONE;
337 }
338
339 /**
340 * Schedule a job to handle IPsec SA expiration
341 */
342 static void schedule_expiration(private_ipsec_sa_mgr_t *this,
343 ipsec_sa_entry_t *entry)
344 {
345 lifetime_cfg_t *lifetime = entry->sa->get_lifetime(entry->sa);
346 ipsec_sa_expired_t *expired;
347 callback_job_t *job;
348 uint32_t timeout;
349
350 if (!lifetime->time.life)
351 { /* no expiration at all */
352 return;
353 }
354
355 INIT(expired,
356 .manager = this,
357 .entry = entry,
358 );
359
360 /* schedule a rekey first, a hard timeout will be scheduled then, if any */
361 expired->hard_offset = lifetime->time.life - lifetime->time.rekey;
362 timeout = lifetime->time.rekey;
363
364 if (lifetime->time.life <= lifetime->time.rekey ||
365 lifetime->time.rekey == 0)
366 { /* no rekey, schedule hard timeout */
367 expired->hard_offset = 0;
368 timeout = lifetime->time.life;
369 }
370
371 job = callback_job_create((callback_job_cb_t)sa_expired, expired,
372 (callback_job_cleanup_t)free, NULL);
373 lib->scheduler->schedule_job(lib->scheduler, (job_t*)job, timeout);
374 }
375
376 /**
377 * Remove all allocated SPIs
378 */
379 static void flush_allocated_spis(private_ipsec_sa_mgr_t *this)
380 {
381 enumerator_t *enumerator;
382 uint32_t *current;
383
384 DBG2(DBG_ESP, "flushing allocated SPIs");
385 enumerator = this->allocated_spis->create_enumerator(this->allocated_spis);
386 while (enumerator->enumerate(enumerator, NULL, (void**)&current))
387 {
388 this->allocated_spis->remove_at(this->allocated_spis, enumerator);
389 DBG2(DBG_ESP, " removed allocated SPI %.8x", ntohl(*current));
390 free(current);
391 }
392 enumerator->destroy(enumerator);
393 }
394
395 /**
396 * Pre-allocate an SPI for an inbound SA
397 */
398 static bool allocate_spi(private_ipsec_sa_mgr_t *this, uint32_t spi)
399 {
400 uint32_t *spi_alloc;
401
402 if (this->allocated_spis->get(this->allocated_spis, &spi) ||
403 this->sas->find_first(this->sas, match_entry_by_spi_inbound,
404 NULL, spi, TRUE))
405 {
406 return FALSE;
407 }
408 spi_alloc = malloc_thing(uint32_t);
409 *spi_alloc = spi;
410 this->allocated_spis->put(this->allocated_spis, spi_alloc, spi_alloc);
411 return TRUE;
412 }
413
414 METHOD(ipsec_sa_mgr_t, get_spi, status_t,
415 private_ipsec_sa_mgr_t *this, host_t *src, host_t *dst, uint8_t protocol,
416 uint32_t *spi)
417 {
418 uint32_t spi_min, spi_max, spi_new;
419
420 spi_min = lib->settings->get_int(lib->settings, "%s.spi_min",
421 0x00000100, lib->ns);
422 spi_max = lib->settings->get_int(lib->settings, "%s.spi_max",
423 0xffffffff, lib->ns);
424 if (spi_min > spi_max)
425 {
426 spi_new = spi_min;
427 spi_min = spi_max;
428 spi_max = spi_new;
429 }
430 /* make sure the SPI is valid (not in range 0-255) */
431 spi_min = max(spi_min, 0x00000100);
432 spi_max = max(spi_max, 0x00000100);
433
434 this->mutex->lock(this->mutex);
435 if (!this->rng)
436 {
437 this->rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
438 if (!this->rng)
439 {
440 this->mutex->unlock(this->mutex);
441 DBG1(DBG_ESP, "failed to create RNG for SPI generation");
442 return FAILED;
443 }
444 }
445
446 do
447 {
448 if (!this->rng->get_bytes(this->rng, sizeof(spi_new),
449 (uint8_t*)&spi_new))
450 {
451 this->mutex->unlock(this->mutex);
452 DBG1(DBG_ESP, "failed to allocate SPI");
453 return FAILED;
454 }
455 spi_new = spi_min + spi_new % (spi_max - spi_min + 1);
456 spi_new = htonl(spi_new);
457 }
458 while (!allocate_spi(this, spi_new));
459 this->mutex->unlock(this->mutex);
460
461 *spi = spi_new;
462
463 DBG2(DBG_ESP, "allocated SPI %.8x", ntohl(*spi));
464 return SUCCESS;
465 }
466
467 METHOD(ipsec_sa_mgr_t, add_sa, status_t,
468 private_ipsec_sa_mgr_t *this, host_t *src, host_t *dst, uint32_t spi,
469 uint8_t protocol, uint32_t reqid, mark_t mark, uint32_t tfc,
470 lifetime_cfg_t *lifetime, uint16_t enc_alg, chunk_t enc_key,
471 uint16_t int_alg, chunk_t int_key, ipsec_mode_t mode, uint16_t ipcomp,
472 uint16_t cpi, bool initiator, bool encap, bool esn, bool inbound,
473 bool update)
474 {
475 ipsec_sa_entry_t *entry;
476 ipsec_sa_t *sa_new;
477
478 DBG2(DBG_ESP, "adding SAD entry with SPI %.8x and reqid {%u}",
479 ntohl(spi), reqid);
480 DBG2(DBG_ESP, " using encryption algorithm %N with key size %d",
481 encryption_algorithm_names, enc_alg, enc_key.len * 8);
482 DBG2(DBG_ESP, " using integrity algorithm %N with key size %d",
483 integrity_algorithm_names, int_alg, int_key.len * 8);
484
485 sa_new = ipsec_sa_create(spi, src, dst, protocol, reqid, mark, tfc,
486 lifetime, enc_alg, enc_key, int_alg, int_key, mode,
487 ipcomp, cpi, encap, esn, inbound);
488 if (!sa_new)
489 {
490 DBG1(DBG_ESP, "failed to create SAD entry");
491 return FAILED;
492 }
493
494 this->mutex->lock(this->mutex);
495
496 if (update)
497 { /* remove any pre-allocated SPIs */
498 uint32_t *spi_alloc;
499
500 spi_alloc = this->allocated_spis->remove(this->allocated_spis, &spi);
501 free(spi_alloc);
502 }
503
504 if (this->sas->find_first(this->sas, match_entry_by_spi_src_dst_cb, NULL,
505 spi, src, dst))
506 {
507 this->mutex->unlock(this->mutex);
508 DBG1(DBG_ESP, "failed to install SAD entry: already installed");
509 sa_new->destroy(sa_new);
510 return FAILED;
511 }
512
513 entry = create_entry(sa_new);
514 schedule_expiration(this, entry);
515 this->sas->insert_first(this->sas, entry);
516
517 this->mutex->unlock(this->mutex);
518 return SUCCESS;
519 }
520
521 METHOD(ipsec_sa_mgr_t, update_sa, status_t,
522 private_ipsec_sa_mgr_t *this, uint32_t spi, uint8_t protocol,
523 uint16_t cpi, host_t *src, host_t *dst, host_t *new_src, host_t *new_dst,
524 bool encap, bool new_encap, mark_t mark)
525 {
526 ipsec_sa_entry_t *entry = NULL;
527
528 DBG2(DBG_ESP, "updating SAD entry with SPI %.8x from %#H..%#H to %#H..%#H",
529 ntohl(spi), src, dst, new_src, new_dst);
530
531 if (!new_encap)
532 {
533 DBG1(DBG_ESP, "failed to update SAD entry: can't deactivate UDP "
534 "encapsulation");
535 return NOT_SUPPORTED;
536 }
537
538 this->mutex->lock(this->mutex);
539 if (this->sas->find_first(this->sas, match_entry_by_spi_src_dst_cb,
540 (void**)&entry, spi, src, dst) &&
541 wait_for_entry(this, entry))
542 {
543 entry->sa->set_source(entry->sa, new_src);
544 entry->sa->set_destination(entry->sa, new_dst);
545 /* checkin the entry */
546 entry->locked = FALSE;
547 entry->condvar->signal(entry->condvar);
548 }
549 this->mutex->unlock(this->mutex);
550
551 if (!entry)
552 {
553 DBG1(DBG_ESP, "failed to update SAD entry: not found");
554 return FAILED;
555 }
556 return SUCCESS;
557 }
558
559 METHOD(ipsec_sa_mgr_t, query_sa, status_t,
560 private_ipsec_sa_mgr_t *this, host_t *src, host_t *dst,
561 uint32_t spi, uint8_t protocol, mark_t mark,
562 uint64_t *bytes, uint64_t *packets, time_t *time)
563 {
564 ipsec_sa_entry_t *entry = NULL;
565
566 this->mutex->lock(this->mutex);
567 if (this->sas->find_first(this->sas, match_entry_by_spi_src_dst_cb,
568 (void**)&entry, spi, src, dst) &&
569 wait_for_entry(this, entry))
570 {
571 entry->sa->get_usestats(entry->sa, bytes, packets, time);
572 /* checkin the entry */
573 entry->locked = FALSE;
574 entry->condvar->signal(entry->condvar);
575 }
576 this->mutex->unlock(this->mutex);
577
578 return entry ? SUCCESS : NOT_FOUND;
579 }
580
581 METHOD(ipsec_sa_mgr_t, del_sa, status_t,
582 private_ipsec_sa_mgr_t *this, host_t *src, host_t *dst, uint32_t spi,
583 uint8_t protocol, uint16_t cpi, mark_t mark)
584 {
585 ipsec_sa_entry_t *current, *found = NULL;
586 enumerator_t *enumerator;
587
588 this->mutex->lock(this->mutex);
589 enumerator = this->sas->create_enumerator(this->sas);
590 while (enumerator->enumerate(enumerator, (void**)&current))
591 {
592 if (match_entry_by_spi_src_dst(current, spi, src, dst))
593 {
594 if (wait_remove_entry(this, current))
595 {
596 this->sas->remove_at(this->sas, enumerator);
597 found = current;
598 }
599 break;
600 }
601 }
602 enumerator->destroy(enumerator);
603 this->mutex->unlock(this->mutex);
604
605 if (found)
606 {
607 DBG2(DBG_ESP, "deleted %sbound SAD entry with SPI %.8x",
608 found->sa->is_inbound(found->sa) ? "in" : "out", ntohl(spi));
609 destroy_entry(found);
610 return SUCCESS;
611 }
612 return FAILED;
613 }
614
615 METHOD(ipsec_sa_mgr_t, checkout_by_reqid, ipsec_sa_t*,
616 private_ipsec_sa_mgr_t *this, uint32_t reqid, bool inbound)
617 {
618 ipsec_sa_entry_t *entry;
619 ipsec_sa_t *sa = NULL;
620
621 this->mutex->lock(this->mutex);
622 if (this->sas->find_first(this->sas, match_entry_by_reqid_inbound,
623 (void**)&entry, reqid, inbound) &&
624 wait_for_entry(this, entry))
625 {
626 sa = entry->sa;
627 }
628 this->mutex->unlock(this->mutex);
629 return sa;
630 }
631
632 METHOD(ipsec_sa_mgr_t, checkout_by_spi, ipsec_sa_t*,
633 private_ipsec_sa_mgr_t *this, uint32_t spi, host_t *dst)
634 {
635 ipsec_sa_entry_t *entry;
636 ipsec_sa_t *sa = NULL;
637
638 this->mutex->lock(this->mutex);
639 if (this->sas->find_first(this->sas, match_entry_by_spi_dst,
640 (void**)&entry, spi, dst) &&
641 wait_for_entry(this, entry))
642 {
643 sa = entry->sa;
644 }
645 this->mutex->unlock(this->mutex);
646 return sa;
647 }
648
649 METHOD(ipsec_sa_mgr_t, checkin, void,
650 private_ipsec_sa_mgr_t *this, ipsec_sa_t *sa)
651 {
652 ipsec_sa_entry_t *entry;
653
654 this->mutex->lock(this->mutex);
655 if (this->sas->find_first(this->sas, match_entry_by_sa_ptr,
656 (void**)&entry, sa))
657 {
658 if (entry->locked)
659 {
660 entry->locked = FALSE;
661 entry->condvar->signal(entry->condvar);
662 }
663 }
664 this->mutex->unlock(this->mutex);
665 }
666
667 METHOD(ipsec_sa_mgr_t, flush_sas, status_t,
668 private_ipsec_sa_mgr_t *this)
669 {
670 this->mutex->lock(this->mutex);
671 flush_entries(this);
672 this->mutex->unlock(this->mutex);
673 return SUCCESS;
674 }
675
676 METHOD(ipsec_sa_mgr_t, destroy, void,
677 private_ipsec_sa_mgr_t *this)
678 {
679 this->mutex->lock(this->mutex);
680 flush_entries(this);
681 flush_allocated_spis(this);
682 this->mutex->unlock(this->mutex);
683
684 this->allocated_spis->destroy(this->allocated_spis);
685 this->sas->destroy(this->sas);
686
687 this->mutex->destroy(this->mutex);
688 DESTROY_IF(this->rng);
689 free(this);
690 }
691
692 /**
693 * Described in header.
694 */
695 ipsec_sa_mgr_t *ipsec_sa_mgr_create()
696 {
697 private_ipsec_sa_mgr_t *this;
698
699 INIT(this,
700 .public = {
701 .get_spi = _get_spi,
702 .add_sa = _add_sa,
703 .update_sa = _update_sa,
704 .query_sa = _query_sa,
705 .del_sa = _del_sa,
706 .checkout_by_spi = _checkout_by_spi,
707 .checkout_by_reqid = _checkout_by_reqid,
708 .checkin = _checkin,
709 .flush_sas = _flush_sas,
710 .destroy = _destroy,
711 },
712 .sas = linked_list_create(),
713 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
714 .allocated_spis = hashtable_create((hashtable_hash_t)spi_hash,
715 (hashtable_equals_t)spi_equals, 16),
716 );
717
718 return &this->public;
719 }