| /* |
| * Copyright (C) 2016-2017 Tobias Brunner |
| * Copyright (C) 2008 Martin Willi |
| * HSR Hochschule fuer Technik Rapperswil |
| * |
| * This program is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License as published by the |
| * Free Software Foundation; either version 2 of the License, or (at your |
| * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. |
| * |
| * This program is distributed in the hope that it will be useful, but |
| * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
| * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * for more details. |
| */ |
| |
| #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; |
| |
| /** |
| * Private data |
| */ |
| struct private_kernel_ipsec_t { |
| |
| /** |
| * Public interface |
| */ |
| 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; |
| } |
| |
| METHOD(kernel_ipsec_t, get_cpi, status_t, |
| private_kernel_ipsec_t *this, host_t *src, host_t *dst, uint16_t *cpi) |
| { |
| return FAILED; |
| } |
| |
| 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; |
| } |
| |
| METHOD(kernel_ipsec_t, update_sa, status_t, |
| private_kernel_ipsec_t *this, kernel_ipsec_sa_id_t *id, |
| kernel_ipsec_update_sa_t *data) |
| { |
| return SUCCESS; |
| } |
| |
| METHOD(kernel_ipsec_t, query_sa, status_t, |
| private_kernel_ipsec_t *this, kernel_ipsec_sa_id_t *id, |
| kernel_ipsec_query_sa_t *data, uint64_t *bytes, uint64_t *packets, |
| time_t *time) |
| { |
| return NOT_SUPPORTED; |
| } |
| |
| 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) |
| { |
| return SUCCESS; |
| } |
| |
| METHOD(kernel_ipsec_t, query_policy, status_t, |
| private_kernel_ipsec_t *this, kernel_ipsec_policy_id_t *id, |
| kernel_ipsec_query_policy_t *data, time_t *use_time) |
| { |
| *use_time = 1; |
| return SUCCESS; |
| } |
| |
| METHOD(kernel_ipsec_t, del_policy, status_t, |
| private_kernel_ipsec_t *this, kernel_ipsec_policy_id_t *id, |
| kernel_ipsec_manage_policy_t *data) |
| { |
| return SUCCESS; |
| } |
| |
| CALLBACK(destroy_spis, void, |
| entry_t *entry, const void* key) |
| { |
| /* only free allocated SPIs, other SAs that were not properly deleted will |
| * cause a leak */ |
| if (entry->alloc) |
| { |
| free(entry); |
| } |
| } |
| |
| METHOD(kernel_ipsec_t, destroy, void, |
| private_kernel_ipsec_t *this) |
| { |
| charon->bus->remove_listener(charon->bus, &this->listener); |
| this->sas->destroy_function(this->sas, destroy_spis); |
| free(this); |
| } |
| |
| /* |
| * Described in header |
| */ |
| kernel_ipsec_t *mock_ipsec_create() |
| { |
| private_kernel_ipsec_t *this; |
| |
| INIT(this, |
| .public = { |
| .get_spi = _get_spi, |
| .get_cpi = _get_cpi, |
| .add_sa = _add_sa, |
| .update_sa = _update_sa, |
| .query_sa = _query_sa, |
| .del_sa = _del_sa, |
| .flush_sas = (void*)return_failed, |
| .add_policy = _add_policy, |
| .query_policy = _query_policy, |
| .del_policy = _del_policy, |
| .flush_policies = (void*)return_failed, |
| .bypass_socket = (void*)return_true, |
| .enable_udp_decap = (void*)return_true, |
| .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); |
| } |