700 lines
17 KiB
C
700 lines
17 KiB
C
/*
|
|
* Copyright (C) 2011-2017 Tobias Brunner
|
|
* Copyright (C) 2009 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 "trap_manager.h"
|
|
|
|
#include <daemon.h>
|
|
#include <threading/mutex.h>
|
|
#include <threading/rwlock.h>
|
|
#include <threading/rwlock_condvar.h>
|
|
#include <collections/linked_list.h>
|
|
|
|
#define INSTALL_DISABLED ((u_int)~0)
|
|
|
|
typedef struct private_trap_manager_t private_trap_manager_t;
|
|
typedef struct trap_listener_t trap_listener_t;
|
|
|
|
/**
|
|
* listener to track acquires
|
|
*/
|
|
struct trap_listener_t {
|
|
|
|
/**
|
|
* Implements listener interface
|
|
*/
|
|
listener_t listener;
|
|
|
|
/**
|
|
* points to trap_manager
|
|
*/
|
|
private_trap_manager_t *traps;
|
|
};
|
|
|
|
/**
|
|
* Private data of an trap_manager_t object.
|
|
*/
|
|
struct private_trap_manager_t {
|
|
|
|
/**
|
|
* Public trap_manager_t interface.
|
|
*/
|
|
trap_manager_t public;
|
|
|
|
/**
|
|
* Installed traps, as entry_t
|
|
*/
|
|
linked_list_t *traps;
|
|
|
|
/**
|
|
* read write lock for traps list
|
|
*/
|
|
rwlock_t *lock;
|
|
|
|
/**
|
|
* listener to track acquiring IKE_SAs
|
|
*/
|
|
trap_listener_t listener;
|
|
|
|
/**
|
|
* list of acquires we currently handle
|
|
*/
|
|
linked_list_t *acquires;
|
|
|
|
/**
|
|
* mutex for list of acquires
|
|
*/
|
|
mutex_t *mutex;
|
|
|
|
/**
|
|
* number of threads currently installing trap policies, or INSTALL_DISABLED
|
|
*/
|
|
u_int installing;
|
|
|
|
/**
|
|
* condvar to signal trap policy installation
|
|
*/
|
|
rwlock_condvar_t *condvar;
|
|
|
|
/**
|
|
* Whether to ignore traffic selectors from acquires
|
|
*/
|
|
bool ignore_acquire_ts;
|
|
};
|
|
|
|
/**
|
|
* A installed trap entry
|
|
*/
|
|
typedef struct {
|
|
/** name of the trapped CHILD_SA */
|
|
char *name;
|
|
/** ref to peer_cfg to initiate */
|
|
peer_cfg_t *peer_cfg;
|
|
/** ref to instantiated CHILD_SA (i.e the trap policy) */
|
|
child_sa_t *child_sa;
|
|
/** TRUE in case of wildcard Transport Mode SA */
|
|
bool wildcard;
|
|
} entry_t;
|
|
|
|
/**
|
|
* A handled acquire
|
|
*/
|
|
typedef struct {
|
|
/** pending IKE_SA connecting upon acquire */
|
|
ike_sa_t *ike_sa;
|
|
/** reqid of pending trap policy */
|
|
uint32_t reqid;
|
|
/** destination address (wildcard case) */
|
|
host_t *dst;
|
|
} acquire_t;
|
|
|
|
/**
|
|
* actually uninstall and destroy an installed entry
|
|
*/
|
|
static void destroy_entry(entry_t *this)
|
|
{
|
|
this->child_sa->destroy(this->child_sa);
|
|
this->peer_cfg->destroy(this->peer_cfg);
|
|
free(this->name);
|
|
free(this);
|
|
}
|
|
|
|
/**
|
|
* destroy a cached acquire entry
|
|
*/
|
|
static void destroy_acquire(acquire_t *this)
|
|
{
|
|
DESTROY_IF(this->dst);
|
|
free(this);
|
|
}
|
|
|
|
CALLBACK(acquire_by_reqid, bool,
|
|
acquire_t *this, va_list args)
|
|
{
|
|
uint32_t reqid;
|
|
|
|
VA_ARGS_VGET(args, reqid);
|
|
return this->reqid == reqid;
|
|
}
|
|
|
|
CALLBACK(acquire_by_dst, bool,
|
|
acquire_t *this, va_list args)
|
|
{
|
|
host_t *dst;
|
|
|
|
VA_ARGS_VGET(args, dst);
|
|
return this->dst && this->dst->ip_equals(this->dst, dst);
|
|
}
|
|
|
|
/**
|
|
* Check if any remote TS are dynamic
|
|
*/
|
|
static bool dynamic_remote_ts(child_cfg_t *child)
|
|
{
|
|
enumerator_t *enumerator;
|
|
linked_list_t *other_ts;
|
|
traffic_selector_t *ts;
|
|
bool found = FALSE;
|
|
|
|
other_ts = child->get_traffic_selectors(child, FALSE, NULL, NULL, FALSE);
|
|
enumerator = other_ts->create_enumerator(other_ts);
|
|
while (enumerator->enumerate(enumerator, &ts))
|
|
{
|
|
if (ts->is_dynamic(ts))
|
|
{
|
|
found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
other_ts->destroy_offset(other_ts, offsetof(traffic_selector_t, destroy));
|
|
return found;
|
|
}
|
|
|
|
METHOD(trap_manager_t, install, bool,
|
|
private_trap_manager_t *this, peer_cfg_t *peer, child_cfg_t *child)
|
|
{
|
|
entry_t *entry, *found = NULL;
|
|
ike_cfg_t *ike_cfg;
|
|
child_sa_t *child_sa;
|
|
host_t *me, *other;
|
|
linked_list_t *my_ts, *other_ts, *list;
|
|
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 */
|
|
ike_cfg = peer->get_ike_cfg(peer);
|
|
other = ike_cfg->resolve_other(ike_cfg, AF_UNSPEC);
|
|
if (other && other->is_anyaddr(other) &&
|
|
child->get_mode(child) == MODE_TRANSPORT)
|
|
{
|
|
/* allow wildcard for Transport Mode SAs */
|
|
me = host_create_any(other->get_family(other));
|
|
wildcard = TRUE;
|
|
}
|
|
else if (other && other->is_anyaddr(other))
|
|
{
|
|
other->destroy(other);
|
|
DBG1(DBG_CFG, "installing trap failed, remote address unknown");
|
|
return FALSE;
|
|
}
|
|
else
|
|
{ /* depending on the traffic selectors we don't really need a remote
|
|
* host yet, but we might fail later if no IP can be resolved */
|
|
if (!other && dynamic_remote_ts(child))
|
|
{ /* with dynamic TS we do need a host, otherwise 0.0.0.0/0 is used,
|
|
* which is probably not what users expect*/
|
|
DBG1(DBG_CFG, "installing trap failed, remote address unknown with "
|
|
"dynamic traffic selector");
|
|
return FALSE;
|
|
}
|
|
me = ike_cfg->resolve_me(ike_cfg, other ? other->get_family(other)
|
|
: AF_UNSPEC);
|
|
if (!other)
|
|
{
|
|
other = host_create_any(me ? me->get_family(me) : AF_INET);
|
|
}
|
|
other->set_port(other, ike_cfg->get_other_port(ike_cfg));
|
|
if ((!me || me->is_anyaddr(me)) && !other->is_anyaddr(other))
|
|
{
|
|
DESTROY_IF(me);
|
|
me = charon->kernel->get_source_addr(charon->kernel, other, NULL);
|
|
}
|
|
if (!me)
|
|
{
|
|
me = host_create_any(other->get_family(other));
|
|
}
|
|
me->set_port(me, ike_cfg->get_my_port(ike_cfg));
|
|
}
|
|
|
|
this->lock->write_lock(this->lock);
|
|
if (this->installing == INSTALL_DISABLED)
|
|
{ /* flush() has been called */
|
|
this->lock->unlock(this->lock);
|
|
other->destroy(other);
|
|
me->destroy(me);
|
|
return FALSE;
|
|
}
|
|
enumerator = this->traps->create_enumerator(this->traps);
|
|
while (enumerator->enumerate(enumerator, &entry))
|
|
{
|
|
if (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 */
|
|
this->traps->remove_at(this->traps, enumerator);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
|
|
if (found)
|
|
{
|
|
if (!found->child_sa)
|
|
{
|
|
DBG1(DBG_CFG, "CHILD_SA '%s' is already being routed", found->name);
|
|
this->lock->unlock(this->lock);
|
|
other->destroy(other);
|
|
me->destroy(me);
|
|
return FALSE;
|
|
}
|
|
/* config might have changed so update everything */
|
|
DBG1(DBG_CFG, "updating already routed CHILD_SA '%s'", found->name);
|
|
}
|
|
|
|
INIT(entry,
|
|
.name = strdup(child->get_name(child)),
|
|
.peer_cfg = peer->get_ref(peer),
|
|
.wildcard = wildcard,
|
|
);
|
|
this->traps->insert_first(this->traps, entry);
|
|
this->installing++;
|
|
/* don't hold lock while creating CHILD_SA and installing policies */
|
|
this->lock->unlock(this->lock);
|
|
|
|
/* create and route CHILD_SA */
|
|
child_sa_create_t child_data = {
|
|
/* TODO: no reason to allocate unique interface IDs, there is currently
|
|
* no event to use them upon trap installation and we'd also have to
|
|
* pass them in a later initiate() call */
|
|
.if_id_in_def = peer->get_if_id(peer, TRUE),
|
|
.if_id_out_def = peer->get_if_id(peer, FALSE),
|
|
};
|
|
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));
|
|
|
|
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));
|
|
|
|
/* 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));
|
|
if (status != 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);
|
|
entry->child_sa = child_sa;
|
|
destroy_entry(entry);
|
|
}
|
|
else
|
|
{
|
|
this->lock->write_lock(this->lock);
|
|
entry->child_sa = child_sa;
|
|
this->lock->unlock(this->lock);
|
|
result = TRUE;
|
|
}
|
|
if (found)
|
|
{
|
|
destroy_entry(found);
|
|
}
|
|
this->lock->write_lock(this->lock);
|
|
/* do this at the end, so entries created temporarily are also destroyed */
|
|
this->installing--;
|
|
this->condvar->signal(this->condvar);
|
|
this->lock->unlock(this->lock);
|
|
return result;
|
|
}
|
|
|
|
METHOD(trap_manager_t, uninstall, bool,
|
|
private_trap_manager_t *this, char *peer, char *child)
|
|
{
|
|
enumerator_t *enumerator;
|
|
entry_t *entry, *found = NULL;
|
|
|
|
this->lock->write_lock(this->lock);
|
|
while (this->installing)
|
|
{
|
|
this->condvar->wait(this->condvar, this->lock);
|
|
}
|
|
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))))
|
|
{
|
|
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;
|
|
}
|
|
|
|
CALLBACK(trap_filter, bool,
|
|
rwlock_t *lock, enumerator_t *orig, va_list args)
|
|
{
|
|
entry_t *entry;
|
|
peer_cfg_t **peer_cfg;
|
|
child_sa_t **child_sa;
|
|
|
|
VA_ARGS_VGET(args, peer_cfg, child_sa);
|
|
|
|
while (orig->enumerate(orig, &entry))
|
|
{
|
|
if (!entry->child_sa)
|
|
{ /* skip entries that are currently being installed */
|
|
continue;
|
|
}
|
|
if (peer_cfg)
|
|
{
|
|
*peer_cfg = entry->peer_cfg;
|
|
}
|
|
if (child_sa)
|
|
{
|
|
*child_sa = entry->child_sa;
|
|
}
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
METHOD(trap_manager_t, create_enumerator, enumerator_t*,
|
|
private_trap_manager_t *this)
|
|
{
|
|
this->lock->read_lock(this->lock);
|
|
return enumerator_create_filter(this->traps->create_enumerator(this->traps),
|
|
trap_filter, this->lock,
|
|
(void*)this->lock->unlock);
|
|
}
|
|
|
|
METHOD(trap_manager_t, acquire, void,
|
|
private_trap_manager_t *this, uint32_t reqid,
|
|
traffic_selector_t *src, traffic_selector_t *dst)
|
|
{
|
|
enumerator_t *enumerator;
|
|
entry_t *entry, *found = NULL;
|
|
acquire_t *acquire;
|
|
peer_cfg_t *peer;
|
|
child_cfg_t *child;
|
|
ike_sa_t *ike_sa;
|
|
host_t *host;
|
|
bool wildcard, ignore = FALSE;
|
|
|
|
this->lock->read_lock(this->lock);
|
|
enumerator = this->traps->create_enumerator(this->traps);
|
|
while (enumerator->enumerate(enumerator, &entry))
|
|
{
|
|
if (entry->child_sa &&
|
|
entry->child_sa->get_reqid(entry->child_sa) == reqid)
|
|
{
|
|
found = entry;
|
|
break;
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
|
|
if (!found)
|
|
{
|
|
DBG1(DBG_CFG, "trap not found, unable to acquire reqid %d", reqid);
|
|
this->lock->unlock(this->lock);
|
|
return;
|
|
}
|
|
reqid = found->child_sa->get_reqid(found->child_sa);
|
|
wildcard = found->wildcard;
|
|
|
|
this->mutex->lock(this->mutex);
|
|
if (wildcard)
|
|
{ /* for wildcard acquires we check that we don't have a pending acquire
|
|
* with the same peer */
|
|
uint8_t mask;
|
|
|
|
dst->to_subnet(dst, &host, &mask);
|
|
if (this->acquires->find_first(this->acquires, acquire_by_dst,
|
|
(void**)&acquire, host))
|
|
{
|
|
host->destroy(host);
|
|
ignore = TRUE;
|
|
}
|
|
else
|
|
{
|
|
INIT(acquire,
|
|
.dst = host,
|
|
.reqid = reqid,
|
|
);
|
|
this->acquires->insert_last(this->acquires, acquire);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (this->acquires->find_first(this->acquires, acquire_by_reqid,
|
|
(void**)&acquire, reqid))
|
|
{
|
|
ignore = TRUE;
|
|
}
|
|
else
|
|
{
|
|
INIT(acquire,
|
|
.reqid = reqid,
|
|
);
|
|
this->acquires->insert_last(this->acquires, acquire);
|
|
}
|
|
}
|
|
this->mutex->unlock(this->mutex);
|
|
if (ignore)
|
|
{
|
|
DBG1(DBG_CFG, "ignoring acquire, connection attempt pending");
|
|
this->lock->unlock(this->lock);
|
|
return;
|
|
}
|
|
peer = found->peer_cfg->get_ref(found->peer_cfg);
|
|
child = found->child_sa->get_config(found->child_sa);
|
|
child = child->get_ref(child);
|
|
/* don't hold the lock while checking out the IKE_SA */
|
|
this->lock->unlock(this->lock);
|
|
|
|
if (wildcard)
|
|
{ /* the peer config would match IKE_SAs with other peers */
|
|
ike_sa = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager,
|
|
peer->get_ike_version(peer), TRUE);
|
|
if (ike_sa)
|
|
{
|
|
ike_cfg_t *ike_cfg;
|
|
uint16_t port;
|
|
uint8_t mask;
|
|
|
|
ike_sa->set_peer_cfg(ike_sa, peer);
|
|
ike_cfg = ike_sa->get_ike_cfg(ike_sa);
|
|
|
|
port = ike_cfg->get_other_port(ike_cfg);
|
|
dst->to_subnet(dst, &host, &mask);
|
|
host->set_port(host, port);
|
|
ike_sa->set_other_host(ike_sa, host);
|
|
|
|
port = ike_cfg->get_my_port(ike_cfg);
|
|
src->to_subnet(src, &host, &mask);
|
|
host->set_port(host, port);
|
|
ike_sa->set_my_host(ike_sa, host);
|
|
|
|
charon->bus->set_sa(charon->bus, ike_sa);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ike_sa = charon->ike_sa_manager->checkout_by_config(
|
|
charon->ike_sa_manager, peer);
|
|
}
|
|
peer->destroy(peer);
|
|
|
|
if (ike_sa)
|
|
{
|
|
if (this->ignore_acquire_ts || ike_sa->get_version(ike_sa) == IKEV1)
|
|
{ /* in IKEv1, don't prepend the acquiring packet TS, as we only
|
|
* have a single TS that we can establish in a Quick Mode. */
|
|
src = dst = NULL;
|
|
}
|
|
|
|
this->mutex->lock(this->mutex);
|
|
acquire->ike_sa = ike_sa;
|
|
this->mutex->unlock(this->mutex);
|
|
|
|
if (ike_sa->initiate(ike_sa, child, reqid, src, dst) != DESTROY_ME)
|
|
{
|
|
charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
|
|
}
|
|
else
|
|
{
|
|
charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
|
|
ike_sa);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
this->mutex->lock(this->mutex);
|
|
this->acquires->remove(this->acquires, acquire, NULL);
|
|
this->mutex->unlock(this->mutex);
|
|
destroy_acquire(acquire);
|
|
child->destroy(child);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Complete the acquire, if successful or failed
|
|
*/
|
|
static void complete(private_trap_manager_t *this, ike_sa_t *ike_sa,
|
|
child_sa_t *child_sa)
|
|
{
|
|
enumerator_t *enumerator;
|
|
acquire_t *acquire;
|
|
|
|
this->mutex->lock(this->mutex);
|
|
enumerator = this->acquires->create_enumerator(this->acquires);
|
|
while (enumerator->enumerate(enumerator, &acquire))
|
|
{
|
|
if (!acquire->ike_sa || acquire->ike_sa != ike_sa)
|
|
{
|
|
continue;
|
|
}
|
|
if (child_sa)
|
|
{
|
|
if (acquire->dst)
|
|
{
|
|
/* since every wildcard acquire results in a separate IKE_SA
|
|
* there is no need to compare the destination address */
|
|
}
|
|
else if (child_sa->get_reqid(child_sa) != acquire->reqid)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
this->acquires->remove_at(this->acquires, enumerator);
|
|
destroy_acquire(acquire);
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
this->mutex->unlock(this->mutex);
|
|
}
|
|
|
|
METHOD(listener_t, ike_state_change, bool,
|
|
trap_listener_t *listener, ike_sa_t *ike_sa, ike_sa_state_t state)
|
|
{
|
|
switch (state)
|
|
{
|
|
case IKE_DESTROYING:
|
|
complete(listener->traps, ike_sa, NULL);
|
|
return TRUE;
|
|
default:
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
METHOD(listener_t, child_state_change, bool,
|
|
trap_listener_t *listener, ike_sa_t *ike_sa, child_sa_t *child_sa,
|
|
child_sa_state_t state)
|
|
{
|
|
switch (state)
|
|
{
|
|
case CHILD_INSTALLED:
|
|
case CHILD_DESTROYING:
|
|
complete(listener->traps, ike_sa, child_sa);
|
|
return TRUE;
|
|
default:
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
METHOD(trap_manager_t, flush, void,
|
|
private_trap_manager_t *this)
|
|
{
|
|
this->lock->write_lock(this->lock);
|
|
while (this->installing)
|
|
{
|
|
this->condvar->wait(this->condvar, this->lock);
|
|
}
|
|
this->traps->destroy_function(this->traps, (void*)destroy_entry);
|
|
this->traps = linked_list_create();
|
|
this->installing = INSTALL_DISABLED;
|
|
this->lock->unlock(this->lock);
|
|
}
|
|
|
|
METHOD(trap_manager_t, destroy, void,
|
|
private_trap_manager_t *this)
|
|
{
|
|
charon->bus->remove_listener(charon->bus, &this->listener.listener);
|
|
this->traps->destroy_function(this->traps, (void*)destroy_entry);
|
|
this->acquires->destroy_function(this->acquires, (void*)destroy_acquire);
|
|
this->condvar->destroy(this->condvar);
|
|
this->mutex->destroy(this->mutex);
|
|
this->lock->destroy(this->lock);
|
|
free(this);
|
|
}
|
|
|
|
/**
|
|
* See header
|
|
*/
|
|
trap_manager_t *trap_manager_create(void)
|
|
{
|
|
private_trap_manager_t *this;
|
|
|
|
INIT(this,
|
|
.public = {
|
|
.install = _install,
|
|
.uninstall = _uninstall,
|
|
.create_enumerator = _create_enumerator,
|
|
.acquire = _acquire,
|
|
.flush = _flush,
|
|
.destroy = _destroy,
|
|
},
|
|
.listener = {
|
|
.traps = this,
|
|
.listener = {
|
|
.ike_state_change = _ike_state_change,
|
|
.child_state_change = _child_state_change,
|
|
},
|
|
},
|
|
.traps = linked_list_create(),
|
|
.acquires = linked_list_create(),
|
|
.mutex = mutex_create(MUTEX_TYPE_DEFAULT),
|
|
.lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
|
|
.condvar = rwlock_condvar_create(),
|
|
.ignore_acquire_ts = lib->settings->get_bool(lib->settings,
|
|
"%s.ignore_acquire_ts", FALSE, lib->ns),
|
|
);
|
|
charon->bus->add_listener(charon->bus, &this->listener.listener);
|
|
|
|
return &this->public;
|
|
}
|