From 7000cf11b1b4e3b2c504951812b48396258e55e2 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 13 Jul 2012 14:27:41 +0200 Subject: IPsec policy manager added This version only provides the very simplest management functions. --- src/libipsec/ipsec_policy_mgr.c | 162 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 src/libipsec/ipsec_policy_mgr.c (limited to 'src/libipsec/ipsec_policy_mgr.c') diff --git a/src/libipsec/ipsec_policy_mgr.c b/src/libipsec/ipsec_policy_mgr.c new file mode 100644 index 000000000..40e69894d --- /dev/null +++ b/src/libipsec/ipsec_policy_mgr.c @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2012 Tobias Brunner + * Copyright (C) 2012 Giuliano Grassi + * Copyright (C) 2012 Ralf Sager + * 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 "ipsec_policy_mgr.h" +#include "ipsec_policy.h" + +#include +#include +#include +#include +#include +#include +#include + +typedef struct private_ipsec_policy_mgr_t private_ipsec_policy_mgr_t; + +/** + * Private additions to ipsec_policy_mgr_t. + */ +struct private_ipsec_policy_mgr_t { + + /** + * Public members of ipsec_policy_mgr_t. + */ + ipsec_policy_mgr_t public; + + /** + * Installed policies + */ + linked_list_t *policies; + + /** + * Lock to safely access policies + */ + rwlock_t *lock; + +}; + +static bool match_policy(ipsec_policy_t *policy, ipsec_policy_t *other_policy) +{ + return policy->match(policy, other_policy->get_source_ts(other_policy), + other_policy->get_destination_ts(other_policy), + other_policy->get_direction(other_policy), + other_policy->get_reqid(other_policy), + (mark_t){ .value = 0, }, + other_policy->get_priority(other_policy)); +} + +METHOD(ipsec_policy_mgr_t, add_policy, status_t, + private_ipsec_policy_mgr_t *this, host_t *src, host_t *dst, + traffic_selector_t *src_ts, traffic_selector_t *dst_ts, + policy_dir_t direction, policy_type_t type, ipsec_sa_cfg_t *sa, mark_t mark, + policy_priority_t priority) +{ + ipsec_policy_t *policy; + + policy = ipsec_policy_create(src, dst, src_ts, dst_ts, direction, type, sa, + mark, priority); + this->lock->write_lock(this->lock); + if (this->policies->find_first(this->policies, (void*)match_policy, + NULL, policy) != SUCCESS) + { + this->policies->insert_last(this->policies, policy); + } + else + { + policy->destroy(policy); + } + this->lock->unlock(this->lock); + return SUCCESS; +} + +METHOD(ipsec_policy_mgr_t, del_policy, status_t, + private_ipsec_policy_mgr_t *this, traffic_selector_t *src_ts, + traffic_selector_t *dst_ts, policy_dir_t direction, u_int32_t reqid, + mark_t mark, policy_priority_t priority) +{ + enumerator_t *enumerator; + ipsec_policy_t *current, *found = NULL; + + this->lock->write_lock(this->lock); + enumerator = this->policies->create_enumerator(this->policies); + while (enumerator->enumerate(enumerator, (void**)¤t)) + { + if (current->match(current, src_ts, dst_ts, direction, reqid, + mark, priority)) + { + this->policies->remove_at(this->policies, enumerator); + found = current; + break; + } + } + enumerator->destroy(enumerator); + this->lock->unlock(this->lock); + if (found) + { + found->destroy(found); + return SUCCESS; + } + return FAILED; +} + +METHOD(ipsec_policy_mgr_t, flush_policies, status_t, + private_ipsec_policy_mgr_t *this) +{ + ipsec_policy_t *policy; + + DBG2(DBG_ESP, "flushing policies"); + + this->lock->write_lock(this->lock); + while (this->policies->remove_last(this->policies, + (void**)&policy) == SUCCESS) + { + policy->destroy(policy); + } + this->lock->unlock(this->lock); + return SUCCESS; +} + +METHOD(ipsec_policy_mgr_t, destroy, void, + private_ipsec_policy_mgr_t *this) +{ + flush_policies(this); + this->policies->destroy(this->policies); + this->lock->destroy(this->lock); + free(this); +} + +/** + * Described in header. + */ +ipsec_policy_mgr_t *ipsec_policy_mgr_create() +{ + private_ipsec_policy_mgr_t *this; + + INIT(this, + .public = { + .add_policy = _add_policy, + .del_policy = _del_policy, + .flush_policies = _flush_policies, + .destroy = _destroy, + }, + .policies = linked_list_create(), + .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), + ); + + return &this->public; +} -- cgit v1.2.3 From ed1f0c234fbf8990f8cb55110944ebca0bb4f10f Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 13 Jul 2012 14:41:45 +0200 Subject: Order IPsec policies by a pseudo-priority based on the traffic selectors This allows a simple lookup, i.e. just use the first policy that matches a given IP packet. --- src/libipsec/ipsec_policy_mgr.c | 155 ++++++++++++++++++++++++++++++++-------- 1 file changed, 127 insertions(+), 28 deletions(-) (limited to 'src/libipsec/ipsec_policy_mgr.c') diff --git a/src/libipsec/ipsec_policy_mgr.c b/src/libipsec/ipsec_policy_mgr.c index 40e69894d..70447b237 100644 --- a/src/libipsec/ipsec_policy_mgr.c +++ b/src/libipsec/ipsec_policy_mgr.c @@ -19,13 +19,12 @@ #include "ipsec_policy.h" #include -#include -#include -#include #include -#include #include +/** Base priority for installed policies */ +#define PRIO_BASE 512 + typedef struct private_ipsec_policy_mgr_t private_ipsec_policy_mgr_t; /** @@ -39,25 +38,101 @@ struct private_ipsec_policy_mgr_t { ipsec_policy_mgr_t public; /** - * Installed policies + * Installed policies (ipsec_policy_entry_t*) */ linked_list_t *policies; /** - * Lock to safely access policies + * Lock to safely access the list of policies */ rwlock_t *lock; }; -static bool match_policy(ipsec_policy_t *policy, ipsec_policy_t *other_policy) +/** + * Helper struct to store policies in a list sorted by the same pseudo-priority + * used by the NETLINK kernel interface. + */ +typedef struct { + + /** + * Priority used to sort policies + */ + u_int32_t priority; + + /** + * The policy + */ + ipsec_policy_t *policy; + +} ipsec_policy_entry_t; + +/** + * Calculate the pseudo-priority to sort policies. This is the same algorithm + * used by the NETLINK kernel interface (i.e. high priority -> low value). + */ +static u_int32_t calculate_priority(policy_priority_t policy_priority, + traffic_selector_t *src, + traffic_selector_t *dst) { - return policy->match(policy, other_policy->get_source_ts(other_policy), - other_policy->get_destination_ts(other_policy), - other_policy->get_direction(other_policy), - other_policy->get_reqid(other_policy), - (mark_t){ .value = 0, }, - other_policy->get_priority(other_policy)); + u_int32_t priority = PRIO_BASE; + u_int16_t port; + u_int8_t mask, proto; + host_t *net; + + switch (policy_priority) + { + case POLICY_PRIORITY_FALLBACK: + priority <<= 1; + /* fall-through */ + case POLICY_PRIORITY_ROUTED: + priority <<= 1; + /* fall-through */ + case POLICY_PRIORITY_DEFAULT: + break; + } + /* calculate priority based on selector size, small size = high prio */ + src->to_subnet(src, &net, &mask); + priority -= mask; + proto = src->get_protocol(src); + port = net->get_port(net); + net->destroy(net); + + dst->to_subnet(dst, &net, &mask); + priority -= mask; + proto = max(proto, dst->get_protocol(dst)); + port = max(port, net->get_port(net)); + net->destroy(net); + + priority <<= 2; /* make some room for the two flags */ + priority += port ? 0 : 2; + priority += proto ? 0 : 1; + return priority; +} + +/** + * Create a policy entry + */ +static ipsec_policy_entry_t *policy_entry_create(ipsec_policy_t *policy) +{ + ipsec_policy_entry_t *this; + + INIT(this, + .policy = policy, + .priority = calculate_priority(policy->get_priority(policy), + policy->get_source_ts(policy), + policy->get_destination_ts(policy)), + ); + return this; +} + +/** + * Destroy a policy entry + */ +static void policy_entry_destroy(ipsec_policy_entry_t *this) +{ + this->policy->destroy(this->policy); + free(this); } METHOD(ipsec_policy_mgr_t, add_policy, status_t, @@ -66,20 +141,33 @@ METHOD(ipsec_policy_mgr_t, add_policy, status_t, policy_dir_t direction, policy_type_t type, ipsec_sa_cfg_t *sa, mark_t mark, policy_priority_t priority) { + enumerator_t *enumerator; + ipsec_policy_entry_t *entry, *current; ipsec_policy_t *policy; + if (type != POLICY_IPSEC || direction == POLICY_FWD) + { /* we ignore these policies as we currently have no use for them */ + return SUCCESS; + } + + DBG2(DBG_ESP, "adding policy %R === %R %N", src_ts, dst_ts, + policy_dir_names, direction); + policy = ipsec_policy_create(src, dst, src_ts, dst_ts, direction, type, sa, mark, priority); + entry = policy_entry_create(policy); + this->lock->write_lock(this->lock); - if (this->policies->find_first(this->policies, (void*)match_policy, - NULL, policy) != SUCCESS) - { - this->policies->insert_last(this->policies, policy); - } - else + enumerator = this->policies->create_enumerator(this->policies); + while (enumerator->enumerate(enumerator, (void**)¤t)) { - policy->destroy(policy); + if (current->priority >= entry->priority) + { + break; + } } + this->policies->insert_before(this->policies, enumerator, entry); + enumerator->destroy(enumerator); this->lock->unlock(this->lock); return SUCCESS; } @@ -87,17 +175,28 @@ METHOD(ipsec_policy_mgr_t, add_policy, status_t, METHOD(ipsec_policy_mgr_t, del_policy, status_t, private_ipsec_policy_mgr_t *this, traffic_selector_t *src_ts, traffic_selector_t *dst_ts, policy_dir_t direction, u_int32_t reqid, - mark_t mark, policy_priority_t priority) + mark_t mark, policy_priority_t policy_priority) { enumerator_t *enumerator; - ipsec_policy_t *current, *found = NULL; + ipsec_policy_entry_t *current, *found = NULL; + u_int32_t priority; + + if (direction == POLICY_FWD) + { /* we ignore these policies as we currently have no use for them */ + return SUCCESS; + } + DBG2(DBG_ESP, "deleting policy %R === %R %N", src_ts, dst_ts, + policy_dir_names, direction); + + priority = calculate_priority(policy_priority, src_ts, dst_ts); this->lock->write_lock(this->lock); enumerator = this->policies->create_enumerator(this->policies); while (enumerator->enumerate(enumerator, (void**)¤t)) { - if (current->match(current, src_ts, dst_ts, direction, reqid, - mark, priority)) + if (current->priority == priority && + current->policy->match(current->policy, src_ts, dst_ts, direction, + reqid, mark, policy_priority)) { this->policies->remove_at(this->policies, enumerator); found = current; @@ -108,7 +207,7 @@ METHOD(ipsec_policy_mgr_t, del_policy, status_t, this->lock->unlock(this->lock); if (found) { - found->destroy(found); + policy_entry_destroy(found); return SUCCESS; } return FAILED; @@ -117,15 +216,15 @@ METHOD(ipsec_policy_mgr_t, del_policy, status_t, METHOD(ipsec_policy_mgr_t, flush_policies, status_t, private_ipsec_policy_mgr_t *this) { - ipsec_policy_t *policy; + ipsec_policy_entry_t *entry; DBG2(DBG_ESP, "flushing policies"); this->lock->write_lock(this->lock); while (this->policies->remove_last(this->policies, - (void**)&policy) == SUCCESS) + (void**)&entry) == SUCCESS) { - policy->destroy(policy); + policy_entry_destroy(entry); } this->lock->unlock(this->lock); return SUCCESS; -- cgit v1.2.3 From 2e1a19136d8123e5a8c9aa99afbb4a51d92ec2a6 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 13 Jul 2012 15:18:07 +0200 Subject: IPsec policies can be looked up based on an IP packet --- src/libipsec/ipsec_policy_mgr.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) (limited to 'src/libipsec/ipsec_policy_mgr.c') diff --git a/src/libipsec/ipsec_policy_mgr.c b/src/libipsec/ipsec_policy_mgr.c index 70447b237..41ba792c3 100644 --- a/src/libipsec/ipsec_policy_mgr.c +++ b/src/libipsec/ipsec_policy_mgr.c @@ -16,7 +16,6 @@ */ #include "ipsec_policy_mgr.h" -#include "ipsec_policy.h" #include #include @@ -230,6 +229,31 @@ METHOD(ipsec_policy_mgr_t, flush_policies, status_t, return SUCCESS; } +METHOD(ipsec_policy_mgr_t, find_by_packet, ipsec_policy_t*, + private_ipsec_policy_mgr_t *this, ip_packet_t *packet, bool inbound) +{ + enumerator_t *enumerator; + ipsec_policy_entry_t *current; + ipsec_policy_t *found = NULL; + + this->lock->read_lock(this->lock); + enumerator = this->policies->create_enumerator(this->policies); + while (enumerator->enumerate(enumerator, (void**)¤t)) + { + ipsec_policy_t *policy = current->policy; + + if ((inbound == (policy->get_direction(policy) == POLICY_IN)) && + policy->match_packet(policy, packet)) + { + found = policy->get_ref(policy); + break; + } + } + enumerator->destroy(enumerator); + this->lock->unlock(this->lock); + return found; +} + METHOD(ipsec_policy_mgr_t, destroy, void, private_ipsec_policy_mgr_t *this) { @@ -251,6 +275,7 @@ ipsec_policy_mgr_t *ipsec_policy_mgr_create() .add_policy = _add_policy, .del_policy = _del_policy, .flush_policies = _flush_policies, + .find_by_packet = _find_by_packet, .destroy = _destroy, }, .policies = linked_list_create(), -- cgit v1.2.3