Record usage history of policies in PF_KEY kernel interface.

The implementation is nearly the same as in the Netlink kernel interface.
This commit is contained in:
Tobias Brunner 2011-07-06 12:56:34 +02:00
parent 4e9e4372f5
commit 17927ca615
1 changed files with 460 additions and 172 deletions

View File

@ -58,6 +58,7 @@
#include <debug.h>
#include <utils/host.h>
#include <utils/linked_list.h>
#include <utils/hashtable.h>
#include <threading/thread.h>
#include <threading/mutex.h>
#include <processing/jobs/callback_job.h>
@ -162,6 +163,11 @@ struct private_kernel_pfkey_ipsec_t
*/
linked_list_t *policies;
/**
* Hash table of IPsec SAs using policies (ipsec_sa_t)
*/
hashtable_t *sas;
/**
* whether to install routes along policies
*/
@ -227,48 +233,213 @@ static void route_entry_destroy(route_entry_t *this)
free(this);
}
/**
* compare two route_entry_t objects
*/
static bool route_entry_equals(route_entry_t *a, route_entry_t *b)
{
return a->if_name && b->if_name && streq(a->if_name, b->if_name) &&
a->src_ip->equals(a->src_ip, b->src_ip) &&
a->gateway->equals(a->gateway, b->gateway) &&
chunk_equals(a->dst_net, b->dst_net) && a->prefixlen == b->prefixlen;
}
typedef struct ipsec_sa_t ipsec_sa_t;
/**
* IPsec SA assigned to a policy.
*/
struct ipsec_sa_t {
/** Source address of this SA */
host_t *src;
/** Destination address of this SA */
host_t *dst;
/** Description of this SA */
ipsec_sa_cfg_t cfg;
/** Reference count for this SA */
refcount_t refcount;
};
/**
* Hash function for ipsec_sa_t objects
*/
static u_int ipsec_sa_hash(ipsec_sa_t *sa)
{
return chunk_hash_inc(sa->src->get_address(sa->src),
chunk_hash_inc(sa->dst->get_address(sa->dst),
chunk_hash(chunk_from_thing(sa->cfg))));
}
/**
* Equality function for ipsec_sa_t objects
*/
static bool ipsec_sa_equals(ipsec_sa_t *sa, ipsec_sa_t *other_sa)
{
return sa->src->ip_equals(sa->src, other_sa->src) &&
sa->dst->ip_equals(sa->dst, other_sa->dst) &&
memeq(&sa->cfg, &other_sa->cfg, sizeof(ipsec_sa_cfg_t));
}
/**
* Allocate or reference an IPsec SA object
*/
static ipsec_sa_t *ipsec_sa_create(private_kernel_pfkey_ipsec_t *this,
host_t *src, host_t *dst,
ipsec_sa_cfg_t *cfg)
{
ipsec_sa_t *sa, *found;
INIT(sa,
.src = src,
.dst = dst,
.cfg = *cfg,
);
found = this->sas->get(this->sas, sa);
if (!found)
{
sa->src = src->clone(src);
sa->dst = dst->clone(dst);
this->sas->put(this->sas, sa, sa);
}
else
{
free(sa);
sa = found;
}
ref_get(&sa->refcount);
return sa;
}
/**
* Release and destroy an IPsec SA object
*/
static void ipsec_sa_destroy(private_kernel_pfkey_ipsec_t *this,
ipsec_sa_t *sa)
{
if (ref_put(&sa->refcount))
{
this->sas->remove(this->sas, sa);
DESTROY_IF(sa->src);
DESTROY_IF(sa->dst);
free(sa);
}
}
typedef struct policy_sa_t policy_sa_t;
typedef struct policy_sa_fwd_t policy_sa_fwd_t;
/**
* Mapping between a policy and an IPsec SA.
*/
struct policy_sa_t {
/** Priority assigned to the policy when installed with this SA */
u_int32_t priority;
/** Type of the policy */
policy_type_t type;
/** Assigned SA */
ipsec_sa_t *sa;
};
/**
* For forward policies we also cache the traffic selectors in order to install
* the route.
*/
struct policy_sa_fwd_t {
/** Generic interface */
policy_sa_t generic;
/** Source traffic selector of this policy */
traffic_selector_t *src_ts;
/** Destination traffic selector of this policy */
traffic_selector_t *dst_ts;
};
/**
* Create a policy_sa(_fwd)_t object
*/
static policy_sa_t *policy_sa_create(private_kernel_pfkey_ipsec_t *this,
policy_dir_t dir, policy_type_t type, host_t *src, host_t *dst,
traffic_selector_t *src_ts, traffic_selector_t *dst_ts, ipsec_sa_cfg_t *cfg)
{
policy_sa_t *policy;
if (dir == POLICY_FWD)
{
policy_sa_fwd_t *fwd;
INIT(fwd,
.src_ts = src_ts->clone(src_ts),
.dst_ts = dst_ts->clone(dst_ts),
);
policy = &fwd->generic;
}
else
{
INIT(policy);
}
policy->type = type;
policy->sa = ipsec_sa_create(this, src, dst, cfg);
return policy;
}
/**
* Destroy a policy_sa(_fwd)_t object
*/
static void policy_sa_destroy(policy_sa_t *policy, policy_dir_t *dir,
private_kernel_pfkey_ipsec_t *this)
{
if (*dir == POLICY_FWD)
{
policy_sa_fwd_t *fwd = (policy_sa_fwd_t*)policy;
fwd->src_ts->destroy(fwd->src_ts);
fwd->dst_ts->destroy(fwd->dst_ts);
}
ipsec_sa_destroy(this, policy->sa);
free(policy);
}
typedef struct policy_entry_t policy_entry_t;
/**
* installed kernel policy.
*/
struct policy_entry_t {
/** reqid of this policy */
u_int32_t reqid;
/** index assigned by the kernel */
/** Index assigned by the kernel */
u_int32_t index;
/** direction of this policy: in, out, forward */
/** Direction of this policy: in, out, forward */
u_int8_t direction;
/** parameters of installed policy */
/** Parameters of installed policy */
struct {
/** subnet and port */
/** Subnet and port */
host_t *net;
/** subnet mask */
/** Subnet mask */
u_int8_t mask;
/** protocol */
/** Protocol */
u_int8_t proto;
} src, dst;
/** associated route installed for this policy */
/** Associated route installed for this policy */
route_entry_t *route;
/** by how many CHILD_SAs this policy is used */
u_int refcount;
/** List of SAs this policy is used by, ordered by priority */
linked_list_t *used_by;
};
/**
* create a policy_entry_t object
* Create a policy_entry_t object
*/
static policy_entry_t *create_policy_entry(traffic_selector_t *src_ts,
traffic_selector_t *dst_ts, policy_dir_t dir, u_int32_t reqid)
traffic_selector_t *dst_ts,
policy_dir_t dir)
{
policy_entry_t *policy;
INIT(policy,
.reqid = reqid,
.direction = dir,
);
@ -285,17 +456,25 @@ static policy_entry_t *create_policy_entry(traffic_selector_t *src_ts,
}
/**
* destroy a policy_entry_t object
* Destroy a policy_entry_t object
*/
static void policy_entry_destroy(policy_entry_t *this)
static void policy_entry_destroy(policy_entry_t *policy,
private_kernel_pfkey_ipsec_t *this)
{
DESTROY_IF(this->src.net);
DESTROY_IF(this->dst.net);
if (this->route)
if (policy->route)
{
route_entry_destroy(this->route);
route_entry_destroy(policy->route);
}
free(this);
if (policy->used_by)
{
policy->used_by->invoke_function(policy->used_by,
(linked_list_invoke_t)policy_sa_destroy,
&policy->direction, this);
policy->used_by->destroy(policy->used_by);
}
DESTROY_IF(policy->src.net);
DESTROY_IF(policy->dst.net);
free(policy);
}
/**
@ -472,6 +651,23 @@ static u_int8_t dir2kernel(policy_dir_t dir)
}
}
/**
* convert the policy type to the one defined in ipsec.h
*/
static inline u_int16_t type2kernel(policy_type_t type)
{
switch (type)
{
case POLICY_IPSEC:
return IPSEC_POLICY_IPSEC;
case POLICY_PASS:
return IPSEC_POLICY_NONE;
case POLICY_DROP:
return IPSEC_POLICY_DISCARD;
}
return type;
}
#ifdef SADB_X_MIGRATE
/**
* convert the policy direction in ipsec.h to the general one.
@ -892,6 +1088,7 @@ static void process_acquire(private_kernel_pfkey_ipsec_t *this,
u_int32_t index, reqid = 0;
traffic_selector_t *src_ts, *dst_ts;
policy_entry_t *policy;
policy_sa_t *sa;
switch (msg->sadb_msg_satype)
{
@ -915,9 +1112,10 @@ static void process_acquire(private_kernel_pfkey_ipsec_t *this,
this->mutex->lock(this->mutex);
if (this->policies->find_first(this->policies,
(linked_list_match_t)policy_entry_match_byindex,
(void**)&policy, &index) == SUCCESS)
(void**)&policy, &index) == SUCCESS &&
policy->used_by->get_first(policy->used_by, (void**)&sa) == SUCCESS)
{
reqid = policy->reqid;
reqid = sa->sa->cfg.reqid;
}
else
{
@ -1614,55 +1812,27 @@ METHOD(kernel_ipsec_t, del_sa, status_t,
return SUCCESS;
}
METHOD(kernel_ipsec_t, add_policy, status_t,
private_kernel_pfkey_ipsec_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, bool routed)
/**
* Add or update a policy in the kernel.
*
* Note: The mutex has to be locked when entering this function.
*/
static status_t add_policy_internal(private_kernel_pfkey_ipsec_t *this,
policy_entry_t *policy, policy_sa_t *mapping, bool update)
{
unsigned char request[PFKEY_BUFFER_SIZE];
struct sadb_msg *msg, *out;
struct sadb_x_policy *pol;
struct sadb_x_ipsecrequest *req;
policy_entry_t *policy, *found = NULL;
ipsec_sa_t *ipsec = mapping->sa;
pfkey_msg_t response;
size_t len;
if (dir2kernel(direction) == IPSEC_DIR_INVALID)
{ /* FWD policies are not supported on all platforms */
return SUCCESS;
}
/* create a policy */
policy = create_policy_entry(src_ts, dst_ts, direction, sa->reqid);
/* find a matching policy */
this->mutex->lock(this->mutex);
if (this->policies->find_first(this->policies,
(linked_list_match_t)policy_entry_equals,
(void**)&found, policy) == SUCCESS)
{ /* use existing policy, but cache the most recent reqid */
found->refcount++;
found->reqid = policy->reqid;
DBG2(DBG_KNL, "policy %R === %R %N already exists, increasing "
"refcount", src_ts, dst_ts, policy_dir_names, direction);
policy_entry_destroy(policy);
policy = found;
}
else
{ /* use the new one, if we have no such policy */
this->policies->insert_last(this->policies, policy);
policy->refcount = 1;
}
memset(&request, 0, sizeof(request));
DBG2(DBG_KNL, "adding policy %R === %R %N", src_ts, dst_ts,
policy_dir_names, direction);
msg = (struct sadb_msg*)request;
msg->sadb_msg_version = PF_KEY_V2;
msg->sadb_msg_type = found ? SADB_X_SPDUPDATE : SADB_X_SPDADD;
msg->sadb_msg_type = update ? SADB_X_SPDUPDATE : SADB_X_SPDADD;
msg->sadb_msg_satype = 0;
msg->sadb_msg_len = PFKEY_LEN(sizeof(struct sadb_msg));
@ -1670,45 +1840,27 @@ METHOD(kernel_ipsec_t, add_policy, status_t,
pol->sadb_x_policy_exttype = SADB_X_EXT_POLICY;
pol->sadb_x_policy_len = PFKEY_LEN(sizeof(struct sadb_x_policy));
pol->sadb_x_policy_id = 0;
pol->sadb_x_policy_dir = dir2kernel(direction);
switch (type)
{
case POLICY_IPSEC:
pol->sadb_x_policy_type = IPSEC_POLICY_IPSEC;
break;
case POLICY_PASS:
pol->sadb_x_policy_type = IPSEC_POLICY_NONE;
break;
case POLICY_DROP:
pol->sadb_x_policy_type = IPSEC_POLICY_DISCARD;
break;
}
pol->sadb_x_policy_dir = dir2kernel(policy->direction);
pol->sadb_x_policy_type = type2kernel(mapping->type);
#ifdef HAVE_STRUCT_SADB_X_POLICY_SADB_X_POLICY_PRIORITY
/* calculate priority based on selector size, small size = high prio */
pol->sadb_x_policy_priority = routed ? PRIO_LOW : PRIO_HIGH;
pol->sadb_x_policy_priority -= policy->src.mask;
pol->sadb_x_policy_priority -= policy->dst.mask;
pol->sadb_x_policy_priority <<= 2; /* make some room for the flags */
pol->sadb_x_policy_priority += policy->src.net->get_port(policy->src.net) ||
policy->dst.net->get_port(policy->dst.net) ?
0 : 2;
pol->sadb_x_policy_priority += policy->src.proto != IPSEC_PROTO_ANY ? 0 : 1;
pol->sadb_x_policy_priority = mapping->priority;
#endif
/* one or more sadb_x_ipsecrequest extensions are added to the
* sadb_x_policy extension */
req = (struct sadb_x_ipsecrequest*)(pol + 1);
req->sadb_x_ipsecrequest_proto = sa->esp.use ? IPPROTO_ESP : IPPROTO_AH;
req->sadb_x_ipsecrequest_proto = ipsec->cfg.esp.use ? IPPROTO_ESP
: IPPROTO_AH;
/* !!! the length here MUST be in octets instead of 64 bit words */
req->sadb_x_ipsecrequest_len = sizeof(struct sadb_x_ipsecrequest);
req->sadb_x_ipsecrequest_mode = mode2kernel(sa->mode);
req->sadb_x_ipsecrequest_reqid = sa->reqid;
req->sadb_x_ipsecrequest_mode = mode2kernel(ipsec->cfg.mode);
req->sadb_x_ipsecrequest_reqid = ipsec->cfg.reqid;
req->sadb_x_ipsecrequest_level = IPSEC_LEVEL_UNIQUE;
if (sa->mode == MODE_TUNNEL)
if (ipsec->cfg.mode == MODE_TUNNEL)
{
len = hostcpy(req + 1, src);
len = hostcpy(req + 1, ipsec->src);
req->sadb_x_ipsecrequest_len += len;
len = hostcpy((char*)(req + 1) + len, dst);
len = hostcpy((char*)(req + 1) + len, ipsec->dst);
req->sadb_x_ipsecrequest_len += len;
}
@ -1736,23 +1888,20 @@ METHOD(kernel_ipsec_t, add_policy, status_t,
if (pfkey_send(this, msg, &out, &len) != SUCCESS)
{
DBG1(DBG_KNL, "unable to add policy %R === %R %N", src_ts, dst_ts,
policy_dir_names, direction);
return FAILED;
}
else if (out->sadb_msg_errno)
{
DBG1(DBG_KNL, "unable to add policy %R === %R %N: %s (%d)",
src_ts, dst_ts, policy_dir_names, direction,
strerror(out->sadb_msg_errno), out->sadb_msg_errno);
DBG1(DBG_KNL, "unable to %s policy: %s (%d)",
update ? "update" : "add", strerror(out->sadb_msg_errno),
out->sadb_msg_errno);
free(out);
return FAILED;
}
else if (parse_pfkey_message(out, &response) != SUCCESS)
{
DBG1(DBG_KNL, "unable to add policy %R === %R %N: parsing response "
"from kernel failed", src_ts, dst_ts, policy_dir_names,
direction);
DBG1(DBG_KNL, "unable to %s policy: parsing response from kernel "
"failed", update ? "update" : "add");
free(out);
return FAILED;
}
@ -1762,9 +1911,8 @@ METHOD(kernel_ipsec_t, add_policy, status_t,
if (this->policies->find_last(this->policies, NULL,
(void**)&policy) != SUCCESS)
{
DBG2(DBG_KNL, "unable to update index, the policy %R === %R %N is "
"already gone, ignoring", src_ts, dst_ts,
policy_dir_names, direction);
DBG2(DBG_KNL, "unable to update index, the policy is already gone, "
"ignoring");
this->mutex->unlock(this->mutex);
free(out);
return SUCCESS;
@ -1773,54 +1921,78 @@ METHOD(kernel_ipsec_t, add_policy, status_t,
free(out);
/* install a route, if:
* - we are NOT updating a policy
* - this is a forward policy (to just get one for each child)
* - we are in tunnel mode
* - we are not using IPv6 (does not work correctly yet!)
* - routing is not disabled via strongswan.conf
*/
if (policy->route == NULL && direction == POLICY_FWD &&
sa->mode != MODE_TRANSPORT && src->get_family(src) != AF_INET6 &&
this->install_routes)
if (policy->direction == POLICY_FWD &&
ipsec->cfg.mode != MODE_TRANSPORT && this->install_routes)
{
route_entry_t *route = malloc_thing(route_entry_t);
policy_sa_fwd_t *fwd = (policy_sa_fwd_t*)mapping;
if (hydra->kernel_interface->get_address_by_ts(hydra->kernel_interface,
dst_ts, &route->src_ip) == SUCCESS)
fwd->dst_ts, &route->src_ip) == SUCCESS)
{
/* get the nexthop to src (src as we are in POLICY_FWD).*/
route->gateway = hydra->kernel_interface->get_nexthop(
hydra->kernel_interface, src);
hydra->kernel_interface, ipsec->src);
/* install route via outgoing interface */
route->if_name = hydra->kernel_interface->get_interface(
hydra->kernel_interface, dst);
hydra->kernel_interface, ipsec->dst);
route->dst_net = chunk_clone(policy->src.net->get_address(
policy->src.net));
route->prefixlen = policy->src.mask;
if (route->if_name)
{
switch (hydra->kernel_interface->add_route(
hydra->kernel_interface, route->dst_net,
route->prefixlen, route->gateway,
route->src_ip, route->if_name))
{
default:
DBG1(DBG_KNL, "unable to install source route for %H",
route->src_ip);
/* FALL */
case ALREADY_DONE:
/* route exists, do not uninstall */
route_entry_destroy(route);
break;
case SUCCESS:
/* cache the installed route */
policy->route = route;
break;
}
}
else
if (!route->if_name)
{
this->mutex->unlock(this->mutex);
route_entry_destroy(route);
return SUCCESS;
}
if (policy->route)
{
route_entry_t *old = policy->route;
if (route_entry_equals(old, route))
{ /* keep previously installed route */
this->mutex->unlock(this->mutex);
route_entry_destroy(route);
return SUCCESS;
}
/* uninstall previously installed route */
if (hydra->kernel_interface->del_route(hydra->kernel_interface,
old->dst_net, old->prefixlen, old->gateway,
old->src_ip, old->if_name) != SUCCESS)
{
DBG1(DBG_KNL, "error uninstalling route installed with "
"policy %R === %R %N", fwd->src_ts,
fwd->dst_ts, policy_dir_names,
policy->direction);
}
route_entry_destroy(old);
policy->route = NULL;
}
DBG2(DBG_KNL, "installing route: %R via %H src %H dev %s",
fwd->src_ts, route->gateway, route->src_ip, route->if_name);
switch (hydra->kernel_interface->add_route(
hydra->kernel_interface, route->dst_net,
route->prefixlen, route->gateway,
route->src_ip, route->if_name))
{
default:
DBG1(DBG_KNL, "unable to install source route for %H",
route->src_ip);
/* FALL */
case ALREADY_DONE:
/* route exists, do not uninstall */
route_entry_destroy(route);
break;
case SUCCESS:
/* cache the installed route */
policy->route = route;
break;
}
}
else
@ -1832,6 +2004,91 @@ METHOD(kernel_ipsec_t, add_policy, status_t,
return SUCCESS;
}
METHOD(kernel_ipsec_t, add_policy, status_t,
private_kernel_pfkey_ipsec_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, bool routed)
{
policy_entry_t *policy, *found = NULL;
policy_sa_t *assigned_sa, *current_sa;
enumerator_t *enumerator;
bool update = TRUE;
if (dir2kernel(direction) == IPSEC_DIR_INVALID)
{ /* FWD policies are not supported on all platforms */
return SUCCESS;
}
/* create a policy */
policy = create_policy_entry(src_ts, dst_ts, direction);
/* find a matching policy */
this->mutex->lock(this->mutex);
if (this->policies->find_first(this->policies,
(linked_list_match_t)policy_entry_equals,
(void**)&found, policy) == SUCCESS)
{ /* use existing policy */
DBG2(DBG_KNL, "policy %R === %R %N already exists, increasing "
"refcount", src_ts, dst_ts, policy_dir_names, direction);
policy_entry_destroy(policy, this);
policy = found;
}
else
{ /* use the new one, if we have no such policy */
this->policies->insert_last(this->policies, policy);
policy->used_by = linked_list_create();
}
/* cache the assigned IPsec SA */
assigned_sa = policy_sa_create(this, direction, type, src, dst, src_ts,
dst_ts, sa);
/* calculate priority based on selector size, small size = high prio */
assigned_sa->priority = routed ? PRIO_LOW : PRIO_HIGH;
assigned_sa->priority -= policy->src.mask;
assigned_sa->priority -= policy->dst.mask;
assigned_sa->priority <<= 2; /* make some room for the two flags */
assigned_sa->priority += policy->src.net->get_port(policy->src.net) ||
policy->dst.net->get_port(policy->dst.net) ?
0 : 2;
assigned_sa->priority += policy->src.proto != IPSEC_PROTO_ANY ? 0 : 1;
/* insert the SA according to its priority */
enumerator = policy->used_by->create_enumerator(policy->used_by);
while (enumerator->enumerate(enumerator, (void**)&current_sa))
{
if (current_sa->priority >= assigned_sa->priority)
{
break;
}
update = FALSE;
}
policy->used_by->insert_before(policy->used_by, enumerator, assigned_sa);
enumerator->destroy(enumerator);
if (!update)
{ /* we don't update the policy if the priority is lower than that of the
* currently installed one */
this->mutex->unlock(this->mutex);
return SUCCESS;
}
DBG2(DBG_KNL, "%s policy %R === %R %N",
found ? "updating" : "adding", src_ts, dst_ts,
policy_dir_names, direction);
if (add_policy_internal(this, policy, assigned_sa, found) != SUCCESS)
{
DBG1(DBG_KNL, "unable to %s policy %R === %R %N",
found ? "update" : "add", src_ts, dst_ts,
policy_dir_names, direction);
return FAILED;
}
return SUCCESS;
}
METHOD(kernel_ipsec_t, query_policy, status_t,
private_kernel_pfkey_ipsec_t *this, traffic_selector_t *src_ts,
traffic_selector_t *dst_ts, policy_dir_t direction, mark_t mark,
@ -1853,7 +2110,7 @@ METHOD(kernel_ipsec_t, query_policy, status_t,
policy_dir_names, direction);
/* create a policy */
policy = create_policy_entry(src_ts, dst_ts, direction, 0);
policy = create_policy_entry(src_ts, dst_ts, direction);
/* find a matching policy */
this->mutex->lock(this->mutex);
@ -1863,11 +2120,11 @@ METHOD(kernel_ipsec_t, query_policy, status_t,
{
DBG1(DBG_KNL, "querying policy %R === %R %N failed, not found", src_ts,
dst_ts, policy_dir_names, direction);
policy_entry_destroy(policy);
policy_entry_destroy(policy, this);
this->mutex->unlock(this->mutex);
return NOT_FOUND;
}
policy_entry_destroy(policy);
policy_entry_destroy(policy, this);
policy = found;
memset(&request, 0, sizeof(request));
@ -1946,7 +2203,9 @@ METHOD(kernel_ipsec_t, del_policy, status_t,
struct sadb_msg *msg, *out;
struct sadb_x_policy *pol;
policy_entry_t *policy, *found = NULL;
route_entry_t *route;
policy_sa_t *mapping;
enumerator_t *enumerator;
bool is_installed = TRUE;
size_t len;
if (dir2kernel(direction) == IPSEC_DIR_INVALID)
@ -1958,35 +2217,58 @@ METHOD(kernel_ipsec_t, del_policy, status_t,
policy_dir_names, direction);
/* create a policy */
policy = create_policy_entry(src_ts, dst_ts, direction, 0);
policy = create_policy_entry(src_ts, dst_ts, direction);
/* find a matching policy */
this->mutex->lock(this->mutex);
if (this->policies->find_first(this->policies,
(linked_list_match_t)policy_entry_equals,
(void**)&found, policy) == SUCCESS)
{
if (--found->refcount > 0)
{ /* is used by more SAs, keep in kernel */
DBG2(DBG_KNL, "policy still used by another CHILD_SA, not removed");
policy_entry_destroy(policy);
this->mutex->unlock(this->mutex);
return SUCCESS;
}
/* remove if last reference */
this->policies->remove(this->policies, found, NULL);
policy_entry_destroy(policy);
policy = found;
}
else
(void**)&found, policy) != SUCCESS)
{
DBG1(DBG_KNL, "deleting policy %R === %R %N failed, not found", src_ts,
dst_ts, policy_dir_names, direction);
policy_entry_destroy(policy);
policy_entry_destroy(policy, this);
this->mutex->unlock(this->mutex);
return NOT_FOUND;
}
this->mutex->unlock(this->mutex);
policy_entry_destroy(policy, this);
policy = found;
/* remove mapping to SA by reqid */
enumerator = policy->used_by->create_enumerator(policy->used_by);
while (enumerator->enumerate(enumerator, (void**)&mapping))
{
if (reqid == mapping->sa->cfg.reqid)
{
policy->used_by->remove_at(policy->used_by, enumerator);
break;
}
is_installed = FALSE;
}
enumerator->destroy(enumerator);
if (policy->used_by->get_count(policy->used_by) > 0)
{ /* policy is used by more SAs, keep in kernel */
DBG2(DBG_KNL, "policy still used by another CHILD_SA, not removed");
policy_sa_destroy(mapping, &direction, this);
if (!is_installed)
{ /* no need to update as the policy was not installed for this SA */
this->mutex->unlock(this->mutex);
return SUCCESS;
}
DBG2(DBG_KNL, "updating policy %R === %R %N", src_ts, dst_ts,
policy_dir_names, direction);
policy->used_by->get_first(policy->used_by, (void**)&mapping);
if (add_policy_internal(this, policy, mapping, TRUE) != SUCCESS)
{
DBG1(DBG_KNL, "unable to update policy %R === %R %N",
src_ts, dst_ts, policy_dir_names, direction);
return FAILED;
}
return SUCCESS;
}
memset(&request, 0, sizeof(request));
@ -2000,7 +2282,7 @@ METHOD(kernel_ipsec_t, del_policy, status_t,
pol->sadb_x_policy_exttype = SADB_X_EXT_POLICY;
pol->sadb_x_policy_len = PFKEY_LEN(sizeof(struct sadb_x_policy));
pol->sadb_x_policy_dir = dir2kernel(direction);
pol->sadb_x_policy_type = IPSEC_POLICY_IPSEC;
pol->sadb_x_policy_type = type2kernel(mapping->type);
PFKEY_EXT_ADD(msg, pol);
add_addr_ext(msg, policy->src.net, SADB_EXT_ADDRESS_SRC, policy->src.proto,
@ -2008,9 +2290,23 @@ METHOD(kernel_ipsec_t, del_policy, status_t,
add_addr_ext(msg, policy->dst.net, SADB_EXT_ADDRESS_DST, policy->dst.proto,
policy->dst.mask);
route = policy->route;
policy->route = NULL;
policy_entry_destroy(policy);
if (policy->route)
{
route_entry_t *route = policy->route;
if (hydra->kernel_interface->del_route(hydra->kernel_interface,
route->dst_net, route->prefixlen, route->gateway,
route->src_ip, route->if_name) != SUCCESS)
{
DBG1(DBG_KNL, "error uninstalling route installed with "
"policy %R === %R %N", src_ts, dst_ts,
policy_dir_names, direction);
}
}
this->policies->remove(this->policies, found, NULL);
policy_sa_destroy(mapping, &direction, this);
policy_entry_destroy(policy, this);
this->mutex->unlock(this->mutex);
if (pfkey_send(this, msg, &out, &len) != SUCCESS)
{
@ -2027,19 +2323,6 @@ METHOD(kernel_ipsec_t, del_policy, status_t,
return FAILED;
}
free(out);
if (route)
{
if (hydra->kernel_interface->del_route(hydra->kernel_interface,
route->dst_net, route->prefixlen, route->gateway,
route->src_ip, route->if_name) != SUCCESS)
{
DBG1(DBG_KNL, "error uninstalling route installed with "
"policy %R === %R %N", src_ts, dst_ts,
policy_dir_names, direction);
}
route_entry_destroy(route);
}
return SUCCESS;
}
@ -2138,8 +2421,11 @@ METHOD(kernel_ipsec_t, destroy, void,
{
close(this->socket_events);
}
this->policies->destroy_function(this->policies,
(void*)policy_entry_destroy);
this->policies->invoke_function(this->policies,
(linked_list_invoke_t)policy_entry_destroy,
this);
this->policies->destroy(this->policies);
this->sas->destroy(this->sas);
this->mutex->destroy(this->mutex);
this->mutex_pfkey->destroy(this->mutex_pfkey);
free(this);
@ -2169,6 +2455,8 @@ kernel_pfkey_ipsec_t *kernel_pfkey_ipsec_create()
},
},
.policies = linked_list_create(),
.sas = hashtable_create((hashtable_hash_t)ipsec_sa_hash,
(hashtable_equals_t)ipsec_sa_equals, 32),
.mutex = mutex_create(MUTEX_TYPE_DEFAULT),
.mutex_pfkey = mutex_create(MUTEX_TYPE_DEFAULT),
.install_routes = lib->settings->get_bool(lib->settings,