/* * Copyright (C) 2012-2014 Reto Buerki * Copyright (C) 2012 Adrian-Ken Rueegsegger * 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 . * * 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 #include #include #include "tkm_kernel_sad.h" typedef struct private_tkm_kernel_sad_t private_tkm_kernel_sad_t; /** * Private data of tkm_kernel_sad. */ struct private_tkm_kernel_sad_t { /** * Public functions. */ tkm_kernel_sad_t public; /** * Linked list of SAD entries. */ linked_list_t *data; /** * Lock used to protect SA data. */ mutex_t *mutex; }; typedef struct sad_entry_t sad_entry_t; /** * Data structure holding all information of an SAD entry. */ struct sad_entry_t { /** * ESA identifier. */ esa_id_type esa_id; /** * Reqid. */ uint32_t reqid; /** * Source address of CHILD SA. */ host_t *src; /** * Destination address of CHILD SA. */ host_t *dst; /** * Local SPI of CHILD SA. */ uint32_t spi_loc; /** * Remote SPI of CHILD SA. */ uint32_t spi_rem; /** * Protocol of CHILD SA (ESP/AH). */ uint8_t proto; }; /** * Destroy an sad_entry_t object. */ static void sad_entry_destroy(sad_entry_t *entry) { if (entry) { DESTROY_IF(entry->src); DESTROY_IF(entry->dst); free(entry); } } CALLBACK(sad_entry_match, bool, sad_entry_t * const entry, va_list args) { const host_t *src, *dst; const uint32_t *spi; const uint8_t *proto; const bool *local; VA_ARGS_VGET(args, src, dst, spi, proto, local); if (entry->src == NULL || entry->dst == NULL || entry->proto != *proto) { return FALSE; } if (*local) { return entry->src->ip_equals(entry->src, (host_t *)dst) && entry->dst->ip_equals(entry->dst, (host_t *)src) && entry->spi_loc == *spi; } return entry->src->ip_equals(entry->src, (host_t *)src) && entry->dst->ip_equals(entry->dst, (host_t *)dst) && entry->spi_rem == *spi; } CALLBACK(sad_entry_match_dst, bool, sad_entry_t * const entry, va_list args) { const uint32_t *reqid, *spi; const uint8_t *proto; VA_ARGS_VGET(args, reqid, spi, proto); return entry->reqid == *reqid && entry->spi_rem == *spi && entry->proto == *proto; } CALLBACK(sad_entry_equal, bool, sad_entry_t * const left, va_list args) { sad_entry_t *right; VA_ARGS_VGET(args, right); if (left->src == NULL || left->dst == NULL || right->src == NULL || right->dst == NULL) { return FALSE; } return left->esa_id == right->esa_id && left->reqid == right->reqid && left->src->ip_equals(left->src, right->src) && left->dst->ip_equals(left->dst, right->dst) && left->spi_loc == right->spi_loc && left->spi_rem == right->spi_rem && left->proto == right->proto; } METHOD(tkm_kernel_sad_t, insert, bool, private_tkm_kernel_sad_t * const this, const esa_id_type esa_id, const uint32_t reqid, const host_t * const src, const host_t * const dst, const uint32_t spi_loc, const uint32_t spi_rem, const uint8_t proto) { sad_entry_t *new_entry; bool found; INIT(new_entry, .esa_id = esa_id, .reqid = reqid, .src = (host_t *)src, .dst = (host_t *)dst, .spi_loc = spi_loc, .spi_rem = spi_rem, .proto = proto, ); this->mutex->lock(this->mutex); found = this->data->find_first(this->data, sad_entry_equal, NULL, new_entry); if (!found) { DBG3(DBG_KNL, "inserting SAD entry (esa: %llu, reqid: %u, src: %H, " "dst: %H, spi_loc: %x, spi_rem: %x,proto: %u)", esa_id, reqid, src, dst, ntohl(spi_loc), ntohl(spi_rem), proto); new_entry->src = src->clone((host_t *)src); new_entry->dst = dst->clone((host_t *)dst); this->data->insert_last(this->data, new_entry); } else { DBG1(DBG_KNL, "SAD entry with esa id %llu already exists!", esa_id); free(new_entry); } this->mutex->unlock(this->mutex); return !found; } METHOD(tkm_kernel_sad_t, get_esa_id, esa_id_type, private_tkm_kernel_sad_t * const this, const host_t * const src, const host_t * const dst, const uint32_t spi, const uint8_t proto, const bool local) { esa_id_type id = 0; sad_entry_t *entry = NULL; this->mutex->lock(this->mutex); const bool res = this->data->find_first(this->data, sad_entry_match, (void**)&entry, src, dst, &spi, &proto, &local); if (res && entry) { id = entry->esa_id; DBG3(DBG_KNL, "returning ESA id %llu of SAD entry (src: %H, dst: %H, " "%sbound spi: %x, proto: %u)", id, src, dst, local ? "in" : "out", ntohl(spi), proto); } else { DBG3(DBG_KNL, "no SAD entry found for src %H, dst %H, %sbound spi %x, " "proto %u", src, dst, local ? "in" : "out", ntohl(spi), proto); } this->mutex->unlock(this->mutex); return id; } METHOD(tkm_kernel_sad_t, get_dst_host, host_t *, private_tkm_kernel_sad_t * const this, const uint32_t reqid, const uint32_t spi, const uint8_t proto) { host_t *dst = NULL; sad_entry_t *entry = NULL; this->mutex->lock(this->mutex); const bool res = this->data->find_first(this->data, sad_entry_match_dst, (void**)&entry, &reqid, &spi, &proto); if (res && entry) { dst = entry->dst->clone(entry->dst); DBG3(DBG_KNL, "returning destination host %H of SAD entry (reqid: %u," " spi: %x, proto: %u)", dst, reqid, ntohl(spi), proto); } else { DBG3(DBG_KNL, "no SAD entry found for reqid %u, spi %x, proto: %u", reqid, ntohl(spi), proto); } this->mutex->unlock(this->mutex); return dst; } METHOD(tkm_kernel_sad_t, _remove, bool, private_tkm_kernel_sad_t * const this, const esa_id_type esa_id) { sad_entry_t *current; bool removed = FALSE; enumerator_t *enumerator; this->mutex->lock(this->mutex); enumerator = this->data->create_enumerator(this->data); while (enumerator->enumerate(enumerator, (void **)¤t)) { if (current->esa_id == esa_id) { this->data->remove_at(this->data, enumerator); sad_entry_destroy(current); removed = TRUE; break; } } enumerator->destroy(enumerator); if (removed) { DBG3(DBG_KNL, "removed SAD entry (esa: %llu)", esa_id); } else { DBG1(DBG_KNL, "no SAD entry with ESA id %llu found!", esa_id); } this->mutex->unlock(this->mutex); return removed; } METHOD(tkm_kernel_sad_t, destroy, void, private_tkm_kernel_sad_t *this) { this->mutex->destroy(this->mutex); this->data->destroy_function(this->data, (void*)sad_entry_destroy); free(this); } /* * see header file */ tkm_kernel_sad_t *tkm_kernel_sad_create() { private_tkm_kernel_sad_t *this; INIT(this, .public = { .insert = _insert, .get_esa_id = _get_esa_id, .get_dst_host = _get_dst_host, .remove = __remove, .destroy = _destroy, }, .mutex = mutex_create(MUTEX_TYPE_DEFAULT), .data = linked_list_create(), ); return &this->public; }