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