diff --git a/src/libcharon/sa/trap_manager.c b/src/libcharon/sa/trap_manager.c index b00f7edc9..7b6e2abdf 100644 --- a/src/libcharon/sa/trap_manager.c +++ b/src/libcharon/sa/trap_manager.c @@ -106,6 +106,8 @@ typedef struct { child_sa_t *child_sa; /** TRUE in case of wildcard Transport Mode SA */ bool wildcard; + /** TRUE for CHILD_SAs that are externally managed */ + bool external; } entry_t; /** @@ -125,7 +127,10 @@ typedef struct { */ static void destroy_entry(entry_t *this) { - this->child_sa->destroy(this->child_sa); + if (!this->external) + { + this->child_sa->destroy(this->child_sa); + } this->peer_cfg->destroy(this->peer_cfg); free(this->name); free(this); @@ -183,6 +188,41 @@ static bool dynamic_remote_ts(child_cfg_t *child) return found; } +/** + * Install the given trap + */ +static status_t install_trap(child_sa_t *child_sa, linked_list_t *local, + linked_list_t *remote) +{ + linked_list_t *my_ts, *other_ts, *proposals; + proposal_t *proposal; + child_cfg_t *child; + protocol_id_t proto = PROTO_ESP; + + child = child_sa->get_config(child_sa); + + my_ts = child->get_traffic_selectors(child, TRUE, NULL, local, FALSE); + other_ts = child->get_traffic_selectors(child, FALSE, NULL, remote, FALSE); + + /* we don't know the finally negotiated protocol (ESP|AH), we install + * the SA with the protocol of the first proposal */ + proposals = child->get_proposals(child, TRUE); + if (proposals->get_first(proposals, (void**)&proposal) == SUCCESS) + { + proto = proposal->get_protocol(proposal); + } + proposals->destroy_offset(proposals, offsetof(proposal_t, destroy)); + + child_sa->set_protocol(child_sa, proto); + child_sa->set_mode(child_sa, child->get_mode(child)); + + child_sa->set_policies(child_sa, my_ts, other_ts); + my_ts->destroy_offset(my_ts, offsetof(traffic_selector_t, destroy)); + other_ts->destroy_offset(other_ts, offsetof(traffic_selector_t, destroy)); + + return child_sa->install_policies(child_sa); +} + METHOD(trap_manager_t, install, bool, private_trap_manager_t *this, peer_cfg_t *peer, child_cfg_t *child) { @@ -190,12 +230,9 @@ METHOD(trap_manager_t, install, bool, ike_cfg_t *ike_cfg; child_sa_t *child_sa; host_t *me, *other; - linked_list_t *my_ts, *other_ts, *list; + linked_list_t *local, *remote; enumerator_t *enumerator; status_t status; - linked_list_t *proposals; - proposal_t *proposal; - protocol_id_t proto = PROTO_ESP; bool result = FALSE, wildcard = FALSE; /* try to resolve addresses */ @@ -254,13 +291,14 @@ METHOD(trap_manager_t, install, bool, enumerator = this->traps->create_enumerator(this->traps); while (enumerator->enumerate(enumerator, &entry)) { - if (streq(entry->name, child->get_name(child)) && + if (!entry->external && + streq(entry->name, child->get_name(child)) && streq(entry->peer_cfg->get_name(entry->peer_cfg), peer->get_name(peer))) { found = entry; if (entry->child_sa) - { /* replace it with an updated version, if already installed */ + { /* replace it with an updated version if already installed */ this->traps->remove_at(this->traps, enumerator); } break; @@ -302,28 +340,13 @@ METHOD(trap_manager_t, install, bool, }; child_sa = child_sa_create(me, other, child, &child_data); - list = linked_list_create_with_items(me, NULL); - my_ts = child->get_traffic_selectors(child, TRUE, NULL, list, FALSE); - list->destroy_offset(list, offsetof(host_t, destroy)); + local = linked_list_create_with_items(me, NULL); + remote = linked_list_create_with_items(other, NULL); - list = linked_list_create_with_items(other, NULL); - other_ts = child->get_traffic_selectors(child, FALSE, NULL, list, FALSE); - list->destroy_offset(list, offsetof(host_t, destroy)); + status = install_trap(child_sa, local, remote); - /* We don't know the finally negotiated protocol (ESP|AH), we install - * the SA with the protocol of the first proposal */ - proposals = child->get_proposals(child, TRUE); - if (proposals->get_first(proposals, (void**)&proposal) == SUCCESS) - { - proto = proposal->get_protocol(proposal); - } - proposals->destroy_offset(proposals, offsetof(proposal_t, destroy)); - child_sa->set_protocol(child_sa, proto); - child_sa->set_mode(child_sa, child->get_mode(child)); - child_sa->set_policies(child_sa, my_ts, other_ts); - status = child_sa->install_policies(child_sa); - my_ts->destroy_offset(my_ts, offsetof(traffic_selector_t, destroy)); - other_ts->destroy_offset(other_ts, offsetof(traffic_selector_t, destroy)); + local->destroy_offset(local, offsetof(host_t, destroy)); + remote->destroy_offset(remote, offsetof(host_t, destroy)); if (status != SUCCESS) { DBG1(DBG_CFG, "installing trap failed"); @@ -366,8 +389,71 @@ METHOD(trap_manager_t, uninstall, bool, enumerator = this->traps->create_enumerator(this->traps); while (enumerator->enumerate(enumerator, &entry)) { - if (streq(entry->name, child) && - (!peer || streq(peer, entry->peer_cfg->get_name(entry->peer_cfg)))) + if (!entry->external && + streq(entry->name, child) && + (!peer || streq(peer, entry->peer_cfg->get_name(entry->peer_cfg)))) + { + this->traps->remove_at(this->traps, enumerator); + found = entry; + break; + } + } + enumerator->destroy(enumerator); + this->lock->unlock(this->lock); + + if (!found) + { + return FALSE; + } + destroy_entry(found); + return TRUE; +} + +METHOD(trap_manager_t, install_external, bool, + private_trap_manager_t *this, peer_cfg_t *peer, child_sa_t *child, + linked_list_t *local, linked_list_t *remote) +{ + entry_t *entry; + + this->lock->write_lock(this->lock); + if (this->installing == INSTALL_DISABLED) + { /* flush() has been called */ + this->lock->unlock(this->lock); + return FALSE; + } + + INIT(entry, + .name = strdup(child->get_name(child)), + .peer_cfg = peer->get_ref(peer), + .child_sa = child, + .external = TRUE, + ); + this->traps->insert_first(this->traps, entry); + this->lock->unlock(this->lock); + + if (install_trap(child, local, remote) != SUCCESS) + { + DBG1(DBG_CFG, "installing trap failed"); + this->lock->write_lock(this->lock); + this->traps->remove(this->traps, entry, NULL); + this->lock->unlock(this->lock); + destroy_entry(entry); + return FALSE; + } + return TRUE; +} + +METHOD(trap_manager_t, remove_external, bool, + private_trap_manager_t *this, child_sa_t *child) +{ + enumerator_t *enumerator; + entry_t *entry, *found = NULL; + + this->lock->write_lock(this->lock); + enumerator = this->traps->create_enumerator(this->traps); + while (enumerator->enumerate(enumerator, &entry)) + { + if (entry->external && entry->child_sa == child) { this->traps->remove_at(this->traps, enumerator); found = entry; @@ -396,8 +482,9 @@ CALLBACK(trap_filter, bool, while (orig->enumerate(orig, &entry)) { - if (!entry->child_sa) - { /* skip entries that are currently being installed */ + if (!entry->child_sa || entry->external) + { /* skip entries that are currently being installed or are managed + * externally */ continue; } if (peer_cfg) @@ -678,6 +765,8 @@ trap_manager_t *trap_manager_create(void) .public = { .install = _install, .uninstall = _uninstall, + .install_external = _install_external, + .remove_external = _remove_external, .create_enumerator = _create_enumerator, .acquire = _acquire, .flush = _flush, diff --git a/src/libcharon/sa/trap_manager.h b/src/libcharon/sa/trap_manager.h index d9b4906f7..60dbc2f12 100644 --- a/src/libcharon/sa/trap_manager.h +++ b/src/libcharon/sa/trap_manager.h @@ -25,6 +25,7 @@ #include #include #include +#include typedef struct trap_manager_t trap_manager_t; @@ -55,7 +56,31 @@ struct trap_manager_t { bool (*uninstall)(trap_manager_t *this, char *peer, char *child); /** - * Create an enumerator over all installed traps. + * Install and register an externally managed trap policy using the two + * lists of local and remote addresses when deriving traffic selectors. + * + * @param peer peer configuration to register + * @param child CHILD_SA to install and register + * @param local list of local addresses (virtual or physical) + * @param remote list of remote addresses (virtual or physical) + * @return TRUE if successfully installed and registered + */ + bool (*install_external)(trap_manager_t *this, peer_cfg_t *peer, + child_sa_t *child, linked_list_t *local, + linked_list_t *remote); + + /** + * Remove and uninstall a previously registered externally managed trap + * policy. + * + * @param child CHILD_SA to remove + * @return TRUE if successfully removed + */ + bool (*remove_external)(trap_manager_t *this, child_sa_t *child); + + /** + * Create an enumerator over all installed traps (does not include + * externally managed trap policies). * * @return enumerator over (peer_cfg_t, child_sa_t) */