diff options
Diffstat (limited to 'src/libcharon/tests/utils')
-rw-r--r-- | src/libcharon/tests/utils/exchange_test_asserts.c | 57 | ||||
-rw-r--r-- | src/libcharon/tests/utils/exchange_test_asserts.h | 61 | ||||
-rw-r--r-- | src/libcharon/tests/utils/mock_ipsec.c | 179 | ||||
-rw-r--r-- | src/libcharon/tests/utils/mock_ipsec.h | 11 | ||||
-rw-r--r-- | src/libcharon/tests/utils/sa_asserts.h | 32 |
5 files changed, 331 insertions, 9 deletions
diff --git a/src/libcharon/tests/utils/exchange_test_asserts.c b/src/libcharon/tests/utils/exchange_test_asserts.c index 2602b97b7..8042d0b63 100644 --- a/src/libcharon/tests/utils/exchange_test_asserts.c +++ b/src/libcharon/tests/utils/exchange_test_asserts.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Tobias Brunner + * Copyright (C) 2016-2017 Tobias Brunner * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -18,6 +18,7 @@ #include <test_suite.h> #include "exchange_test_asserts.h" +#include "mock_ipsec.h" /* * Described in header @@ -180,3 +181,57 @@ bool exchange_test_asserts_message(listener_t *listener, ike_sa_t *ike_sa, } return TRUE; } + +/** + * Compare two SPIs + */ +static int spis_cmp(const void *a, const void *b) +{ + return *(const uint32_t*)a - *(const uint32_t*)b; +} + +/** + * Compare two SPIs to sort them + */ +static int spis_sort(const void *a, const void *b, void *data) +{ + return spis_cmp(a, b); +} + + +/* + * Described in header + */ +void exchange_test_asserts_ipsec_sas(ipsec_sas_assert_t *sas) +{ + enumerator_t *enumerator; + array_t *spis; + ike_sa_t *ike_sa; + uint32_t spi; + int i; + + spis = array_create(sizeof(uint32_t), 0); + for (i = 0; i < sas->count; i++) + { + array_insert(spis, ARRAY_TAIL, &sas->spis[i]); + } + array_sort(spis, spis_sort, NULL); + + enumerator = mock_ipsec_create_sa_enumerator(); + while (enumerator->enumerate(enumerator, &ike_sa, &spi)) + { + if (ike_sa == sas->ike_sa) + { + i = array_bsearch(spis, &spi, spis_cmp, NULL); + assert_listener_msg(i != -1, sas, "unexpected IPsec SA %.8x", spi); + array_remove(spis, i, NULL); + } + } + enumerator->destroy(enumerator); + for (i = 0; i < array_count(spis); i++) + { + array_get(spis, i, &spi); + assert_listener_msg(!spi, sas, "expected IPsec SA %.8x not found", spi); + } + array_destroy(spis); +} diff --git a/src/libcharon/tests/utils/exchange_test_asserts.h b/src/libcharon/tests/utils/exchange_test_asserts.h index 32afcc2e4..4d363edfd 100644 --- a/src/libcharon/tests/utils/exchange_test_asserts.h +++ b/src/libcharon/tests/utils/exchange_test_asserts.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Tobias Brunner + * Copyright (C) 2016-2017 Tobias Brunner * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -14,7 +14,7 @@ */ /** - * Special assertions using listener_t. + * Special assertions using listener_t etc. * * @defgroup exchange_test_asserts exchange_test_asserts * @{ @ingroup test_utils_c @@ -28,6 +28,7 @@ typedef struct listener_hook_assert_t listener_hook_assert_t; typedef struct listener_message_assert_t listener_message_assert_t; typedef struct listener_message_rule_t listener_message_rule_t; +typedef struct ipsec_sas_assert_t ipsec_sas_assert_t; struct listener_hook_assert_t { @@ -340,4 +341,60 @@ bool exchange_test_asserts_message(listener_t *this, ike_sa_t *ike_sa, exchange_test_helper->add_listener(exchange_test_helper, &_listener.listener); \ }) +/** + * Data used to check IPsec SAs + */ +struct ipsec_sas_assert_t { + + /** + * Original source file + */ + const char *file; + + /** + * Source line + */ + int line; + + /** + * IKE_SA that installed the IPsec SAs + */ + ike_sa_t *ike_sa; + + /** + * SPIs to check + */ + uint32_t *spis; + + /** + * Number of SPIs for IPsec SAs to check + */ + int count; +}; + +/** + * Assert that all given IPsec SAs (and only these) are installed for the given + * IKE_SA. + */ +void exchange_test_asserts_ipsec_sas(ipsec_sas_assert_t *sas); + +/** + * Assert that the IPsec SAs with the given SPIs (and none other) are currently + * installed by the given IKE_SA. + * + * @param sa IKE_SA + * @param ... list of SPIs + */ +#define assert_ipsec_sas_installed(sa, ...) ({ \ + uint32_t _spis[] = { __VA_ARGS__ }; \ + ipsec_sas_assert_t _sas_assert = { \ + .file = __FILE__, \ + .line = __LINE__, \ + .ike_sa = sa, \ + .spis = _spis, \ + .count = countof(_spis), \ + }; \ + exchange_test_asserts_ipsec_sas(&_sas_assert); \ +}) + #endif /** EXCHANGE_TEST_ASSERTS_H_ @}*/ diff --git a/src/libcharon/tests/utils/mock_ipsec.c b/src/libcharon/tests/utils/mock_ipsec.c index d57a26a87..d6172f5bd 100644 --- a/src/libcharon/tests/utils/mock_ipsec.c +++ b/src/libcharon/tests/utils/mock_ipsec.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Tobias Brunner + * Copyright (C) 2016-2017 Tobias Brunner * Copyright (C) 2008 Martin Willi * HSR Hochschule fuer Technik Rapperswil * @@ -16,6 +16,12 @@ #include "mock_ipsec.h" +#include <daemon.h> +#include <collections/hashtable.h> +#include <collections/array.h> + +#include <assert.h> + typedef struct private_kernel_ipsec_t private_kernel_ipsec_t; /** @@ -29,16 +35,80 @@ struct private_kernel_ipsec_t { kernel_ipsec_t public; /** + * Rekey listener + */ + listener_t listener; + + /** * Allocated SPI */ refcount_t spi; + + /** + * Installed SAs + */ + hashtable_t *sas; }; +/** + * Global instance + */ +static private_kernel_ipsec_t *instance; + +/** + * Data about installed IPsec SAs + */ +typedef struct { + /** + * SPI of the SA + */ + uint32_t spi; + + /** + * Associated IKE_SA + */ + ike_sa_t *ike_sa; + + /** + * TRUE if this was an allocated SPI + */ + bool alloc; + +} entry_t; + +/** + * Hash an IPsec SA entry + */ +static u_int entry_hash(const void *key) +{ + entry_t *entry = (entry_t*)key; + return chunk_hash_inc(chunk_from_thing(entry->spi), + chunk_hash(chunk_from_thing(entry->ike_sa))); +} + +/** + * Compare an IPsec SA entry + */ +static bool entry_equals(const void *key, const void *other_key) +{ + entry_t *a = (entry_t*)key, *b = (entry_t*)other_key; + return a->spi == b->spi && a->ike_sa == b->ike_sa; +} + METHOD(kernel_ipsec_t, get_spi, status_t, private_kernel_ipsec_t *this, host_t *src, host_t *dst, uint8_t protocol, uint32_t *spi) { + entry_t *entry; + *spi = (uint32_t)ref_get(&this->spi); + INIT(entry, + .spi = *spi, + .ike_sa = charon->bus->get_sa(charon->bus), + .alloc = TRUE, + ); + entry = this->sas->put(this->sas, entry, entry); + assert(!entry); return SUCCESS; } @@ -52,6 +122,23 @@ METHOD(kernel_ipsec_t, add_sa, status_t, private_kernel_ipsec_t *this, kernel_ipsec_sa_id_t *id, kernel_ipsec_add_sa_t *data) { + entry_t *entry; + + INIT(entry, + .spi = id->spi, + .ike_sa = charon->bus->get_sa(charon->bus), + ); + if (data->inbound) + { + entry = this->sas->put(this->sas, entry, entry); + assert(entry && entry->alloc); + free(entry); + } + else + { + entry = this->sas->put(this->sas, entry, entry); + assert(!entry); + } return SUCCESS; } @@ -74,9 +161,47 @@ METHOD(kernel_ipsec_t, del_sa, status_t, private_kernel_ipsec_t *this, kernel_ipsec_sa_id_t *id, kernel_ipsec_del_sa_t *data) { + entry_t *entry, lookup = { + .spi = id->spi, + .ike_sa = charon->bus->get_sa(charon->bus), + }; + + entry = this->sas->remove(this->sas, &lookup); + assert(entry); + free(entry); return SUCCESS; } +METHOD(listener_t, ike_rekey, bool, + listener_t *listener, ike_sa_t *old, ike_sa_t *new) +{ + enumerator_t *enumerator; + array_t *sas = NULL; + entry_t *entry; + + enumerator = instance->sas->create_enumerator(instance->sas); + while (enumerator->enumerate(enumerator, &entry, NULL)) + { + if (entry->ike_sa == old) + { + instance->sas->remove_at(instance->sas, enumerator); + array_insert_create(&sas, ARRAY_TAIL, entry); + } + } + enumerator->destroy(enumerator); + enumerator = array_create_enumerator(sas); + while (enumerator->enumerate(enumerator, &entry)) + { + array_remove_at(sas, enumerator); + entry->ike_sa = new; + entry = instance->sas->put(instance->sas, entry, entry); + assert(!entry); + } + enumerator->destroy(enumerator); + array_destroy(sas); + return TRUE; +} + METHOD(kernel_ipsec_t, add_policy, status_t, private_kernel_ipsec_t *this, kernel_ipsec_policy_id_t *id, kernel_ipsec_manage_policy_t *data) @@ -99,6 +224,14 @@ METHOD(kernel_ipsec_t, del_policy, status_t, return SUCCESS; } +METHOD(kernel_ipsec_t, destroy, void, + private_kernel_ipsec_t *this) +{ + charon->bus->remove_listener(charon->bus, &this->listener); + this->sas->destroy(this->sas); + free(this); +} + /* * Described in header */ @@ -121,8 +254,50 @@ kernel_ipsec_t *mock_ipsec_create() .flush_policies = (void*)return_failed, .bypass_socket = (void*)return_true, .enable_udp_decap = (void*)return_true, - .destroy = (void*)free, + .destroy = _destroy, + }, + .listener = { + .ike_rekey = _ike_rekey, }, + .sas = hashtable_create(entry_hash, entry_equals, 8), ); + + instance = this; + + charon->bus->add_listener(charon->bus, &this->listener); + return &this->public; } + + +CALLBACK(filter_sas, bool, + void *data, enumerator_t *orig, va_list args) +{ + entry_t *entry; + ike_sa_t **ike_sa; + uint32_t *spi; + + VA_ARGS_VGET(args, ike_sa, spi); + + while (orig->enumerate(orig, &entry, NULL)) + { + if (entry->alloc) + { + continue; + } + *ike_sa = entry->ike_sa; + *spi = entry->spi; + return TRUE; + } + return FALSE; +} + +/* + * Described in header + */ +enumerator_t *mock_ipsec_create_sa_enumerator() +{ + return enumerator_create_filter( + instance->sas->create_enumerator(instance->sas), + filter_sas, NULL, NULL); +} diff --git a/src/libcharon/tests/utils/mock_ipsec.h b/src/libcharon/tests/utils/mock_ipsec.h index cbf21524a..95038a561 100644 --- a/src/libcharon/tests/utils/mock_ipsec.h +++ b/src/libcharon/tests/utils/mock_ipsec.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Tobias Brunner + * Copyright (C) 2016-2017 Tobias Brunner * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -15,7 +15,7 @@ /** * kernel_ipsec_t implementation used for exchange unit tests. Currently - * returns sequential SPIs, all other methods are noops. + * returns sequential SPIs, and keeps track of installed SAs. * * @defgroup mock_ipsec mock_ipsec * @{ @ingroup test_utils_c @@ -33,4 +33,11 @@ */ kernel_ipsec_t *mock_ipsec_create(); +/** + * Enumerate the installed SAs + * + * @return enumerator over (ike_sa_t*, uint32_t) + */ +enumerator_t *mock_ipsec_create_sa_enumerator(); + #endif /** MOCK_IPSEC_H_ @}*/ diff --git a/src/libcharon/tests/utils/sa_asserts.h b/src/libcharon/tests/utils/sa_asserts.h index 7afa3b55b..d23f724f1 100644 --- a/src/libcharon/tests/utils/sa_asserts.h +++ b/src/libcharon/tests/utils/sa_asserts.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Tobias Brunner + * Copyright (C) 2016-2017 Tobias Brunner * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -82,13 +82,38 @@ }) /** + * Check if the CHILD_SA with the given SPI is in the expected state, optionally + * check the state of the outbound SA. + */ +#define assert_child_sa_state(...) VA_ARGS_DISPATCH(assert_child_sa_state, __VA_ARGS__)(__VA_ARGS__) + +/** * Check if the CHILD_SA with the given SPI is in the expected state. */ -#define assert_child_sa_state(ike_sa, spi, state) \ +#define assert_child_sa_state3(ike_sa, spi, state) \ +({ \ + typeof(ike_sa) _sa = ike_sa; \ + typeof(spi) _spi = spi; \ + typeof(state) _state = state; \ + child_sa_t *_child = _sa->get_child_sa(_sa, PROTO_ESP, _spi, TRUE) ?: \ + _sa->get_child_sa(_sa, PROTO_ESP, _spi, FALSE); \ + test_assert_msg(_child, "CHILD_SA with SPI %.8x does not exist", \ + ntohl(_spi)); \ + test_assert_msg(_state == _child->get_state(_child), "%N != %N", \ + child_sa_state_names, _state, \ + child_sa_state_names, _child->get_state(_child)); \ +}) + +/** + * Check if the outbound SA of a CHILD_SA with the given SPI is in the + * expected state. + */ +#define assert_child_sa_state4(ike_sa, spi, state, outbound) \ ({ \ typeof(ike_sa) _sa = ike_sa; \ typeof(spi) _spi = spi; \ typeof(state) _state = state; \ + typeof(outbound) _outbound = outbound; \ child_sa_t *_child = _sa->get_child_sa(_sa, PROTO_ESP, _spi, TRUE) ?: \ _sa->get_child_sa(_sa, PROTO_ESP, _spi, FALSE); \ test_assert_msg(_child, "CHILD_SA with SPI %.8x does not exist", \ @@ -96,6 +121,9 @@ test_assert_msg(_state == _child->get_state(_child), "%N != %N", \ child_sa_state_names, _state, \ child_sa_state_names, _child->get_state(_child)); \ + test_assert_msg(_outbound == _child->get_outbound_state(_child), "%N != %N", \ + child_sa_outbound_state_names, _outbound, \ + child_sa_outbound_state_names, _child->get_outbound_state(_child)); \ }) /** |