summaryrefslogtreecommitdiff
path: root/src/libhydra/plugins/kernel_pfkey
diff options
context:
space:
mode:
authorYves-Alexis Perez <corsac@corsac.net>2012-06-28 21:16:07 +0200
committerYves-Alexis Perez <corsac@corsac.net>2012-06-28 21:16:07 +0200
commitb34738ed08c2227300d554b139e2495ca5da97d6 (patch)
tree62f33b52820f2e49f0e53c0f8c636312037c8054 /src/libhydra/plugins/kernel_pfkey
parent0a9d51a49042a68daa15b0c74a2b7f152f52606b (diff)
downloadvyos-strongswan-b34738ed08c2227300d554b139e2495ca5da97d6.tar.gz
vyos-strongswan-b34738ed08c2227300d554b139e2495ca5da97d6.zip
Imported Upstream version 4.6.4
Diffstat (limited to 'src/libhydra/plugins/kernel_pfkey')
-rw-r--r--src/libhydra/plugins/kernel_pfkey/Makefile.in7
-rw-r--r--src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c917
-rw-r--r--src/libhydra/plugins/kernel_pfkey/kernel_pfkey_plugin.c17
3 files changed, 686 insertions, 255 deletions
diff --git a/src/libhydra/plugins/kernel_pfkey/Makefile.in b/src/libhydra/plugins/kernel_pfkey/Makefile.in
index 251483017..14c924b6f 100644
--- a/src/libhydra/plugins/kernel_pfkey/Makefile.in
+++ b/src/libhydra/plugins/kernel_pfkey/Makefile.in
@@ -195,6 +195,9 @@ am__leading_dot = @am__leading_dot@
am__quote = @am__quote@
am__tar = @am__tar@
am__untar = @am__untar@
+attest_plugins = @attest_plugins@
+axis2c_CFLAGS = @axis2c_CFLAGS@
+axis2c_LIBS = @axis2c_LIBS@
bindir = @bindir@
build = @build@
build_alias = @build_alias@
@@ -203,6 +206,7 @@ build_os = @build_os@
build_vendor = @build_vendor@
builddir = @builddir@
c_plugins = @c_plugins@
+clearsilver_LIBS = @clearsilver_LIBS@
datadir = @datadir@
datarootdir = @datarootdir@
dbusservicedir = @dbusservicedir@
@@ -219,11 +223,13 @@ host_cpu = @host_cpu@
host_os = @host_os@
host_vendor = @host_vendor@
htmldir = @htmldir@
+imcvdir = @imcvdir@
includedir = @includedir@
infodir = @infodir@
install_sh = @install_sh@
ipsecdir = @ipsecdir@
ipsecgroup = @ipsecgroup@
+ipseclibdir = @ipseclibdir@
ipsecuser = @ipsecuser@
libcharon_plugins = @libcharon_plugins@
libdir = @libdir@
@@ -267,6 +273,7 @@ sharedstatedir = @sharedstatedir@
soup_CFLAGS = @soup_CFLAGS@
soup_LIBS = @soup_LIBS@
srcdir = @srcdir@
+starter_plugins = @starter_plugins@
strongswan_conf = @strongswan_conf@
sysconfdir = @sysconfdir@
systemdsystemunitdir = @systemdsystemunitdir@
diff --git a/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c
index b252b7092..da10edffe 100644
--- a/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c
+++ b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008-2010 Tobias Brunner
+ * Copyright (C) 2008-2011 Tobias Brunner
* Copyright (C) 2008 Andreas Steffen
* Hochschule fuer Technik Rapperswil
*
@@ -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>
@@ -99,8 +100,7 @@
#endif
/** default priority of installed policies */
-#define PRIO_LOW 1024
-#define PRIO_HIGH 512
+#define PRIO_BASE 512
#ifdef __APPLE__
/** from xnu/bsd/net/pfkeyv2.h */
@@ -163,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
*/
bool install_routes;
@@ -199,19 +204,19 @@ typedef struct route_entry_t route_entry_t;
* installed routing entry
*/
struct route_entry_t {
- /** Name of the interface the route is bound to */
+ /** name of the interface the route is bound to */
char *if_name;
- /** Source ip of the route */
+ /** source ip of the route */
host_t *src_ip;
/** gateway for this route */
host_t *gateway;
- /** Destination net */
+ /** destination net */
chunk_t dst_net;
- /** Destination net prefixlen */
+ /** destination net prefixlen */
u_int8_t prefixlen;
};
@@ -227,57 +232,222 @@ 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, .priority = 0);
+ }
+ 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_SA's 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 = malloc_thing(policy_entry_t);
- policy->reqid = reqid;
- policy->index = 0;
- policy->direction = dir;
- policy->route = NULL;
- policy->refcount = 0;
+ policy_entry_t *policy;
+ INIT(policy,
+ .direction = dir,
+ );
src_ts->to_subnet(src_ts, &policy->src.net, &policy->src.mask);
dst_ts->to_subnet(dst_ts, &policy->dst.net, &policy->dst.mask);
/* src or dest proto may be "any" (0), use more restrictive one */
- policy->src.proto = max(src_ts->get_protocol(src_ts), dst_ts->get_protocol(dst_ts));
+ policy->src.proto = max(src_ts->get_protocol(src_ts),
+ dst_ts->get_protocol(dst_ts));
policy->src.proto = policy->src.proto ? policy->src.proto : IPSEC_PROTO_ANY;
policy->dst.proto = policy->src.proto;
@@ -285,23 +455,32 @@ 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);
}
/**
* compares two policy_entry_t
*/
-static inline bool policy_entry_equals(policy_entry_t *current, policy_entry_t *policy)
+static inline bool policy_entry_equals(policy_entry_t *current,
+ policy_entry_t *policy)
{
return current->direction == policy->direction &&
current->src.proto == policy->src.proto &&
@@ -315,11 +494,41 @@ static inline bool policy_entry_equals(policy_entry_t *current, policy_entry_t *
/**
* compare the given kernel index with that of a policy
*/
-static inline bool policy_entry_match_byindex(policy_entry_t *current, u_int32_t *index)
+static inline bool policy_entry_match_byindex(policy_entry_t *current,
+ u_int32_t *index)
{
return current->index == *index;
}
+/**
+ * Calculate the priority of a policy
+ */
+static inline u_int32_t get_priority(policy_entry_t *policy,
+ policy_priority_t prio)
+{
+ u_int32_t priority = PRIO_BASE;
+ switch (prio)
+ {
+ 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 */
+ priority -= policy->src.mask;
+ priority -= policy->dst.mask;
+ priority <<= 2; /* make some room for the two flags */
+ priority += policy->src.net->get_port(policy->src.net) ||
+ policy->dst.net->get_port(policy->dst.net) ?
+ 0 : 2;
+ priority += policy->src.proto != IPSEC_PROTO_ANY ? 0 : 1;
+ return priority;
+}
+
typedef struct pfkey_msg_t pfkey_msg_t;
struct pfkey_msg_t
@@ -470,6 +679,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.
@@ -665,19 +891,19 @@ static void add_encap_ext(struct sadb_msg *msg, host_t *src, host_t *dst)
nat_type = (struct sadb_x_nat_t_type*)PFKEY_EXT_ADD_NEXT(msg);
nat_type->sadb_x_nat_t_type_exttype = SADB_X_EXT_NAT_T_TYPE;
- nat_type->sadb_x_nat_t_type_len = PFKEY_LEN(sizeof(struct sadb_x_nat_t_type));
+ nat_type->sadb_x_nat_t_type_len = PFKEY_LEN(sizeof(*nat_type));
nat_type->sadb_x_nat_t_type_type = UDP_ENCAP_ESPINUDP;
PFKEY_EXT_ADD(msg, nat_type);
nat_port = (struct sadb_x_nat_t_port*)PFKEY_EXT_ADD_NEXT(msg);
nat_port->sadb_x_nat_t_port_exttype = SADB_X_EXT_NAT_T_SPORT;
- nat_port->sadb_x_nat_t_port_len = PFKEY_LEN(sizeof(struct sadb_x_nat_t_port));
+ nat_port->sadb_x_nat_t_port_len = PFKEY_LEN(sizeof(*nat_port));
nat_port->sadb_x_nat_t_port_port = htons(src->get_port(src));
PFKEY_EXT_ADD(msg, nat_port);
nat_port = (struct sadb_x_nat_t_port*)PFKEY_EXT_ADD_NEXT(msg);
nat_port->sadb_x_nat_t_port_exttype = SADB_X_EXT_NAT_T_DPORT;
- nat_port->sadb_x_nat_t_port_len = PFKEY_LEN(sizeof(struct sadb_x_nat_t_port));
+ nat_port->sadb_x_nat_t_port_len = PFKEY_LEN(sizeof(*nat_port));
nat_port->sadb_x_nat_t_port_port = htons(dst->get_port(dst));
PFKEY_EXT_ADD(msg, nat_port);
}
@@ -694,9 +920,11 @@ static traffic_selector_t* sadb_address2ts(struct sadb_address *address)
/* The Linux 2.6 kernel does not set the protocol and port information
* in the src and dst sadb_address extensions of the SADB_ACQUIRE message.
*/
- host = host_create_from_sockaddr((sockaddr_t*)&address[1]) ;
- ts = traffic_selector_create_from_subnet(host, address->sadb_address_prefixlen,
- address->sadb_address_proto, host->get_port(host));
+ host = host_create_from_sockaddr((sockaddr_t*)&address[1]);
+ ts = traffic_selector_create_from_subnet(host,
+ address->sadb_address_prefixlen,
+ address->sadb_address_proto,
+ host->get_port(host));
return ts;
}
@@ -729,7 +957,8 @@ static status_t parse_pfkey_message(struct sadb_msg *msg, pfkey_msg_t *out)
if ((ext->sadb_ext_type > SADB_EXT_MAX) || (!ext->sadb_ext_type))
{
- DBG1(DBG_KNL, "type of PF_KEY extension (%d) is invalid", ext->sadb_ext_type);
+ DBG1(DBG_KNL, "type of PF_KEY extension (%d) is invalid",
+ ext->sadb_ext_type);
break;
}
@@ -784,7 +1013,8 @@ static status_t pfkey_send_socket(private_kernel_pfkey_ipsec_t *this, int socket
continue;
}
this->mutex_pfkey->unlock(this->mutex_pfkey);
- DBG1(DBG_KNL, "error sending to PF_KEY socket: %s", strerror(errno));
+ DBG1(DBG_KNL, "error sending to PF_KEY socket: %s",
+ strerror(errno));
return FAILED;
}
break;
@@ -804,7 +1034,8 @@ static status_t pfkey_send_socket(private_kernel_pfkey_ipsec_t *this, int socket
/* interrupted, try again */
continue;
}
- DBG1(DBG_KNL, "error reading from PF_KEY socket: %s", strerror(errno));
+ DBG1(DBG_KNL, "error reading from PF_KEY socket: %s",
+ strerror(errno));
this->mutex_pfkey->unlock(this->mutex_pfkey);
return FAILED;
}
@@ -817,7 +1048,8 @@ static status_t pfkey_send_socket(private_kernel_pfkey_ipsec_t *this, int socket
}
if (msg->sadb_msg_len > len / PFKEY_ALIGNMENT)
{
- DBG1(DBG_KNL, "buffer was too small to receive the complete PF_KEY message");
+ DBG1(DBG_KNL, "buffer was too small to receive the complete PF_KEY "
+ "message");
this->mutex_pfkey->unlock(this->mutex_pfkey);
return FAILED;
}
@@ -829,7 +1061,8 @@ static status_t pfkey_send_socket(private_kernel_pfkey_ipsec_t *this, int socket
if (msg->sadb_msg_seq != this->seq)
{
DBG1(DBG_KNL, "received PF_KEY message with unexpected sequence "
- "number, was %d expected %d", msg->sadb_msg_seq, this->seq);
+ "number, was %d expected %d", msg->sadb_msg_seq,
+ this->seq);
if (msg->sadb_msg_seq == 0)
{
/* FreeBSD and Mac OS X do this for the response to
@@ -849,8 +1082,8 @@ static status_t pfkey_send_socket(private_kernel_pfkey_ipsec_t *this, int socket
if (msg->sadb_msg_type != in->sadb_msg_type)
{
DBG2(DBG_KNL, "received PF_KEY message of wrong type, "
- "was %d expected %d, ignoring",
- msg->sadb_msg_type, in->sadb_msg_type);
+ "was %d expected %d, ignoring", msg->sadb_msg_type,
+ in->sadb_msg_type);
}
break;
}
@@ -860,7 +1093,6 @@ static status_t pfkey_send_socket(private_kernel_pfkey_ipsec_t *this, int socket
memcpy(*out, buf, len);
this->mutex_pfkey->unlock(this->mutex_pfkey);
-
return SUCCESS;
}
@@ -868,7 +1100,8 @@ static status_t pfkey_send_socket(private_kernel_pfkey_ipsec_t *this, int socket
* Send a message to the default PF_KEY socket and handle the response.
*/
static status_t pfkey_send(private_kernel_pfkey_ipsec_t *this,
- struct sadb_msg *in, struct sadb_msg **out, size_t *out_len)
+ struct sadb_msg *in, struct sadb_msg **out,
+ size_t *out_len)
{
return pfkey_send_socket(this, this->socket, in, out, out_len);
}
@@ -876,12 +1109,14 @@ static status_t pfkey_send(private_kernel_pfkey_ipsec_t *this,
/**
* Process a SADB_ACQUIRE message from the kernel
*/
-static void process_acquire(private_kernel_pfkey_ipsec_t *this, struct sadb_msg* msg)
+static void process_acquire(private_kernel_pfkey_ipsec_t *this,
+ struct sadb_msg* msg)
{
pfkey_msg_t response;
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)
{
@@ -904,18 +1139,21 @@ static void process_acquire(private_kernel_pfkey_ipsec_t *this, struct sadb_msg*
index = response.x_policy->sadb_x_policy_id;
this->mutex->lock(this->mutex);
if (this->policies->find_first(this->policies,
- (linked_list_match_t)policy_entry_match_byindex, (void**)&policy, &index) == SUCCESS)
+ (linked_list_match_t)policy_entry_match_byindex,
+ (void**)&policy, &index) == SUCCESS &&
+ policy->used_by->get_first(policy->used_by, (void**)&sa) == SUCCESS)
{
- reqid = policy->reqid;
+ reqid = sa->sa->cfg.reqid;
}
else
{
- DBG1(DBG_KNL, "received an SADB_ACQUIRE with policy id %d but no"
- " matching policy found", index);
+ DBG1(DBG_KNL, "received an SADB_ACQUIRE with policy id %d but no "
+ "matching policy found", index);
}
+ this->mutex->unlock(this->mutex);
+
src_ts = sadb_address2ts(response.src);
dst_ts = sadb_address2ts(response.dst);
- this->mutex->unlock(this->mutex);
hydra->kernel_interface->acquire(hydra->kernel_interface, reqid, src_ts,
dst_ts);
@@ -924,7 +1162,8 @@ static void process_acquire(private_kernel_pfkey_ipsec_t *this, struct sadb_msg*
/**
* Process a SADB_EXPIRE message from the kernel
*/
-static void process_expire(private_kernel_pfkey_ipsec_t *this, struct sadb_msg* msg)
+static void process_expire(private_kernel_pfkey_ipsec_t *this,
+ struct sadb_msg* msg)
{
pfkey_msg_t response;
u_int8_t protocol;
@@ -946,8 +1185,8 @@ static void process_expire(private_kernel_pfkey_ipsec_t *this, struct sadb_msg*
if (protocol != IPPROTO_ESP && protocol != IPPROTO_AH)
{
- DBG2(DBG_KNL, "ignoring SADB_EXPIRE for SA with SPI %.8x and reqid {%u} "
- "which is not a CHILD_SA", ntohl(spi), reqid);
+ DBG2(DBG_KNL, "ignoring SADB_EXPIRE for SA with SPI %.8x and "
+ "reqid {%u} which is not a CHILD_SA", ntohl(spi), reqid);
return;
}
@@ -959,7 +1198,8 @@ static void process_expire(private_kernel_pfkey_ipsec_t *this, struct sadb_msg*
/**
* Process a SADB_X_MIGRATE message from the kernel
*/
-static void process_migrate(private_kernel_pfkey_ipsec_t *this, struct sadb_msg* msg)
+static void process_migrate(private_kernel_pfkey_ipsec_t *this,
+ struct sadb_msg* msg)
{
pfkey_msg_t response;
traffic_selector_t *src_ts, *dst_ts;
@@ -1014,10 +1254,12 @@ static void process_migrate(private_kernel_pfkey_ipsec_t *this, struct sadb_msg*
/**
* Process a SADB_X_NAT_T_NEW_MAPPING message from the kernel
*/
-static void process_mapping(private_kernel_pfkey_ipsec_t *this, struct sadb_msg* msg)
+static void process_mapping(private_kernel_pfkey_ipsec_t *this,
+ struct sadb_msg* msg)
{
pfkey_msg_t response;
u_int32_t spi, reqid;
+ sockaddr_t *sa;
host_t *host;
DBG2(DBG_KNL, "received an SADB_X_NAT_T_NEW_MAPPING");
@@ -1038,30 +1280,33 @@ static void process_mapping(private_kernel_pfkey_ipsec_t *this, struct sadb_msg*
spi = response.sa->sadb_sa_spi;
reqid = response.x_sa2->sadb_x_sa2_reqid;
- if (satype2proto(msg->sadb_msg_satype) == IPPROTO_ESP)
+ if (satype2proto(msg->sadb_msg_satype) != IPPROTO_ESP)
+ {
+ return;
+ }
+
+ sa = (sockaddr_t*)(response.dst + 1);
+ switch (sa->sa_family)
{
- sockaddr_t *sa = (sockaddr_t*)(response.dst + 1);
- switch (sa->sa_family)
+ case AF_INET:
{
- case AF_INET:
- {
- struct sockaddr_in *sin = (struct sockaddr_in*)sa;
- sin->sin_port = htons(response.x_natt_dport->sadb_x_nat_t_port_port);
- }
- case AF_INET6:
- {
- struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)sa;
- sin6->sin6_port = htons(response.x_natt_dport->sadb_x_nat_t_port_port);
- }
- default:
- break;
+ struct sockaddr_in *sin = (struct sockaddr_in*)sa;
+ sin->sin_port = htons(response.x_natt_dport->sadb_x_nat_t_port_port);
}
- host = host_create_from_sockaddr(sa);
- if (host)
+ case AF_INET6:
{
- hydra->kernel_interface->mapping(hydra->kernel_interface, reqid,
- spi, host);
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)sa;
+ sin6->sin6_port = htons(response.x_natt_dport->sadb_x_nat_t_port_port);
}
+ default:
+ break;
+ }
+
+ host = host_create_from_sockaddr(sa);
+ if (host)
+ {
+ hydra->kernel_interface->mapping(hydra->kernel_interface, reqid,
+ spi, host);
}
}
#endif /*SADB_X_NAT_T_NEW_MAPPING*/
@@ -1073,8 +1318,8 @@ static job_requeue_t receive_events(private_kernel_pfkey_ipsec_t *this)
{
unsigned char buf[PFKEY_BUFFER_SIZE];
struct sadb_msg *msg = (struct sadb_msg*)buf;
- int len;
bool oldstate;
+ int len;
oldstate = thread_cancelability(TRUE);
len = recvfrom(this->socket_events, buf, sizeof(buf), 0, NULL, 0);
@@ -1109,7 +1354,8 @@ static job_requeue_t receive_events(private_kernel_pfkey_ipsec_t *this)
}
if (msg->sadb_msg_len > len / PFKEY_ALIGNMENT)
{
- DBG1(DBG_KNL, "buffer was too small to receive the complete PF_KEY message");
+ DBG1(DBG_KNL, "buffer was too small to receive the complete "
+ "PF_KEY message");
return JOB_REQUEUE_DIRECT;
}
@@ -1179,7 +1425,7 @@ METHOD(kernel_ipsec_t, get_spi, status_t,
if (out->sadb_msg_errno)
{
DBG1(DBG_KNL, "allocating SPI failed: %s (%d)",
- strerror(out->sadb_msg_errno), out->sadb_msg_errno);
+ strerror(out->sadb_msg_errno), out->sadb_msg_errno);
}
else if (parse_pfkey_message(out, &response) == SUCCESS)
{
@@ -1222,7 +1468,8 @@ METHOD(kernel_ipsec_t, add_sa, status_t,
memset(&request, 0, sizeof(request));
- DBG2(DBG_KNL, "adding SAD entry with SPI %.8x and reqid {%u}", ntohl(spi), reqid);
+ DBG2(DBG_KNL, "adding SAD entry with SPI %.8x and reqid {%u}",
+ ntohl(spi), reqid);
msg = (struct sadb_msg*)request;
msg->sadb_msg_version = PF_KEY_V2;
@@ -1287,11 +1534,11 @@ METHOD(kernel_ipsec_t, add_sa, status_t,
if (!sa->sadb_sa_encrypt)
{
DBG1(DBG_KNL, "algorithm %N not supported by kernel!",
- encryption_algorithm_names, enc_alg);
+ encryption_algorithm_names, enc_alg);
return FAILED;
}
DBG2(DBG_KNL, " using encryption algorithm %N with key size %d",
- encryption_algorithm_names, enc_alg, enc_key.len * 8);
+ encryption_algorithm_names, enc_alg, enc_key.len * 8);
key = (struct sadb_key*)PFKEY_EXT_ADD_NEXT(msg);
key->sadb_key_exttype = SADB_EXT_KEY_ENCRYPT;
@@ -1307,11 +1554,11 @@ METHOD(kernel_ipsec_t, add_sa, status_t,
if (!sa->sadb_sa_auth)
{
DBG1(DBG_KNL, "algorithm %N not supported by kernel!",
- integrity_algorithm_names, int_alg);
+ integrity_algorithm_names, int_alg);
return FAILED;
}
DBG2(DBG_KNL, " using integrity algorithm %N with key size %d",
- integrity_algorithm_names, int_alg, int_key.len * 8);
+ integrity_algorithm_names, int_alg, int_key.len * 8);
key = (struct sadb_key*)PFKEY_EXT_ADD_NEXT(msg);
key->sadb_key_exttype = SADB_EXT_KEY_AUTH;
@@ -1368,8 +1615,8 @@ METHOD(kernel_ipsec_t, update_sa, status_t,
if (!src->ip_equals(src, new_src) ||
!dst->ip_equals(dst, new_dst))
{
- DBG1(DBG_KNL, "unable to update SAD entry with SPI %.8x: address changes"
- " are not supported", ntohl(spi));
+ DBG1(DBG_KNL, "unable to update SAD entry with SPI %.8x: address "
+ "changes are not supported", ntohl(spi));
return NOT_SUPPORTED;
}
@@ -1396,27 +1643,27 @@ METHOD(kernel_ipsec_t, update_sa, status_t,
if (pfkey_send(this, msg, &out, &len) != SUCCESS)
{
- DBG1(DBG_KNL, "unable to query SAD entry with SPI %.8x",
- ntohl(spi));
+ DBG1(DBG_KNL, "unable to query SAD entry with SPI %.8x", ntohl(spi));
return FAILED;
}
else if (out->sadb_msg_errno)
{
DBG1(DBG_KNL, "unable to query SAD entry with SPI %.8x: %s (%d)",
- ntohl(spi), strerror(out->sadb_msg_errno), out->sadb_msg_errno);
+ ntohl(spi), 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 query SAD entry with SPI %.8x: parsing response "
- "from kernel failed", ntohl(spi));
+ DBG1(DBG_KNL, "unable to query SAD entry with SPI %.8x: parsing "
+ "response from kernel failed", ntohl(spi));
free(out);
return FAILED;
}
DBG2(DBG_KNL, "updating SAD entry with SPI %.8x from %#H..%#H to %#H..%#H",
- ntohl(spi), src, dst, new_src, new_dst);
+ ntohl(spi), src, dst, new_src, new_dst);
memset(&request, 0, sizeof(request));
@@ -1476,7 +1723,8 @@ METHOD(kernel_ipsec_t, update_sa, status_t,
else if (out->sadb_msg_errno)
{
DBG1(DBG_KNL, "unable to update SAD entry with SPI %.8x: %s (%d)",
- ntohl(spi), strerror(out->sadb_msg_errno), out->sadb_msg_errno);
+ ntohl(spi), strerror(out->sadb_msg_errno),
+ out->sadb_msg_errno);
free(out);
return FAILED;
}
@@ -1525,7 +1773,8 @@ METHOD(kernel_ipsec_t, query_sa, status_t,
else if (out->sadb_msg_errno)
{
DBG1(DBG_KNL, "unable to query SAD entry with SPI %.8x: %s (%d)",
- ntohl(spi), strerror(out->sadb_msg_errno), out->sadb_msg_errno);
+ ntohl(spi), strerror(out->sadb_msg_errno),
+ out->sadb_msg_errno);
free(out);
return FAILED;
}
@@ -1580,7 +1829,8 @@ METHOD(kernel_ipsec_t, del_sa, status_t,
else if (out->sadb_msg_errno)
{
DBG1(DBG_KNL, "unable to delete SAD entry with SPI %.8x: %s (%d)",
- ntohl(spi), strerror(out->sadb_msg_errno), out->sadb_msg_errno);
+ ntohl(spi), strerror(out->sadb_msg_errno),
+ out->sadb_msg_errno);
free(out);
return FAILED;
}
@@ -1590,57 +1840,60 @@ 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)
+METHOD(kernel_ipsec_t, flush_sas, status_t,
+ private_kernel_pfkey_ipsec_t *this)
{
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;
- pfkey_msg_t response;
size_t len;
- if (dir2kernel(direction) == IPSEC_DIR_INVALID)
- {
- /* FWD policies are not supported on all platforms */
- return SUCCESS;
- }
+ memset(&request, 0, sizeof(request));
- /* create a policy */
- policy = create_policy_entry(src_ts, dst_ts, direction, sa->reqid);
+ DBG2(DBG_KNL, "flushing all SAD entries");
- /* 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)
+ msg = (struct sadb_msg*)request;
+ msg->sadb_msg_version = PF_KEY_V2;
+ msg->sadb_msg_type = SADB_FLUSH;
+ msg->sadb_msg_satype = SADB_SATYPE_UNSPEC;
+ msg->sadb_msg_len = PFKEY_LEN(sizeof(struct sadb_msg));
+
+ if (pfkey_send(this, msg, &out, &len) != SUCCESS)
{
- /* use existing policy */
- found->refcount++;
- 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;
+ DBG1(DBG_KNL, "unable to flush SAD entries");
+ return FAILED;
}
- else
+ else if (out->sadb_msg_errno)
{
- /* apply the new one, if we have no such policy */
- this->policies->insert_last(this->policies, policy);
- policy->refcount = 1;
+ DBG1(DBG_KNL, "unable to flush SAD entries: %s (%d)",
+ strerror(out->sadb_msg_errno), out->sadb_msg_errno);
+ free(out);
+ return FAILED;
}
+ free(out);
+ return SUCCESS;
+}
- memset(&request, 0, sizeof(request));
+/**
+ * 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;
+ ipsec_sa_t *ipsec = mapping->sa;
+ pfkey_msg_t response;
+ size_t len;
- DBG2(DBG_KNL, "adding policy %R === %R %N", src_ts, dst_ts,
- policy_dir_names, direction);
+ memset(&request, 0, sizeof(request));
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));
@@ -1648,32 +1901,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);
- pol->sadb_x_policy_type = IPSEC_POLICY_IPSEC;
+ 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 */
+ /* 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;
- /* !!! the length of this struct MUST be in octets instead of 64 bit words */
+ 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;
}
@@ -1701,33 +1949,31 @@ 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;
}
- this->mutex->lock(this->mutex);
-
/* we try to find the policy again and update the kernel index */
- if (this->policies->find_last(this->policies, NULL, (void**)&policy) != SUCCESS)
+ this->mutex->lock(this->mutex);
+ 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;
@@ -1736,53 +1982,83 @@ 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);
- route->dst_net = chunk_clone(policy->src.net->get_address(policy->src.net));
+ 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)
+ 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))
+ 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. since it might have
+ * still been removed by an address change, we install it
+ * again but ignore the result */
+ hydra->kernel_interface->add_route(hydra->kernel_interface,
+ route->dst_net, route->prefixlen, route->gateway,
+ route->src_ip, route->if_name);
+ 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)
{
- 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;
+ 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;
}
- else
+
+ 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))
{
- route_entry_destroy(route);
+ 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
@@ -1790,9 +2066,82 @@ METHOD(kernel_ipsec_t, add_policy, status_t,
free(route);
}
}
-
this->mutex->unlock(this->mutex);
+ 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, policy_priority_t priority)
+{
+ 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);
+ assigned_sa->priority = get_priority(policy, priority);
+
+ /* 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;
}
@@ -1809,8 +2158,7 @@ METHOD(kernel_ipsec_t, query_policy, status_t,
size_t len;
if (dir2kernel(direction) == IPSEC_DIR_INVALID)
- {
- /* FWD policies are not supported on all platforms */
+ { /* FWD policies are not supported on all platforms */
return NOT_FOUND;
}
@@ -1818,20 +2166,21 @@ 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);
if (this->policies->find_first(this->policies,
- (linked_list_match_t)policy_entry_equals, (void**)&found, policy) != SUCCESS)
+ (linked_list_match_t)policy_entry_equals,
+ (void**)&found, policy) != SUCCESS)
{
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));
@@ -1874,17 +2223,19 @@ METHOD(kernel_ipsec_t, query_policy, status_t,
else if (parse_pfkey_message(out, &response) != SUCCESS)
{
DBG1(DBG_KNL, "unable to query policy %R === %R %N: parsing response "
- "from kernel failed", src_ts, dst_ts, policy_dir_names, direction);
+ "from kernel failed", src_ts, dst_ts, policy_dir_names,
+ direction);
free(out);
return FAILED;
}
else if (response.lft_current == NULL)
{
DBG1(DBG_KNL, "unable to query policy %R === %R %N: kernel reports no "
- "use time", src_ts, dst_ts, policy_dir_names, direction);
+ "use time", src_ts, dst_ts, policy_dir_names, direction);
free(out);
return FAILED;
}
+
/* we need the monotonic time, but the kernel returns system time. */
if (response.lft_current->sadb_lifetime_usetime)
{
@@ -1896,25 +2247,26 @@ METHOD(kernel_ipsec_t, query_policy, status_t,
*use_time = 0;
}
free(out);
-
return SUCCESS;
}
METHOD(kernel_ipsec_t, del_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,
- bool unrouted)
+ traffic_selector_t *dst_ts, policy_dir_t direction, u_int32_t reqid,
+ mark_t mark, policy_priority_t prio)
{
unsigned char request[PFKEY_BUFFER_SIZE];
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;
+ u_int32_t priority;
size_t len;
if (dir2kernel(direction) == IPSEC_DIR_INVALID)
- {
- /* FWD policies are not supported on all platforms */
+ { /* FWD policies are not supported on all platforms */
return SUCCESS;
}
@@ -1922,35 +2274,59 @@ 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
+ (linked_list_match_t)policy_entry_equals,
+ (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 and priority */
+ priority = get_priority(policy, prio);
+ enumerator = policy->used_by->create_enumerator(policy->used_by);
+ while (enumerator->enumerate(enumerator, (void**)&mapping))
+ {
+ if (reqid == mapping->sa->cfg.reqid && priority == mapping->priority)
+ {
+ 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));
@@ -1964,7 +2340,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,
@@ -1972,9 +2348,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)
{
@@ -1991,25 +2381,44 @@ METHOD(kernel_ipsec_t, del_policy, status_t,
return FAILED;
}
free(out);
+ return SUCCESS;
+}
+
+METHOD(kernel_ipsec_t, flush_policies, status_t,
+ private_kernel_pfkey_ipsec_t *this)
+{
+ unsigned char request[PFKEY_BUFFER_SIZE];
+ struct sadb_msg *msg, *out;
+ size_t len;
+
+ memset(&request, 0, sizeof(request));
+
+ DBG2(DBG_KNL, "flushing all policies from SPD");
- if (route)
+ msg = (struct sadb_msg*)request;
+ msg->sadb_msg_version = PF_KEY_V2;
+ msg->sadb_msg_type = SADB_X_SPDFLUSH;
+ msg->sadb_msg_satype = SADB_SATYPE_UNSPEC;
+ msg->sadb_msg_len = PFKEY_LEN(sizeof(struct sadb_msg));
+
+ if (pfkey_send(this, msg, &out, &len) != SUCCESS)
{
- 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);
+ DBG1(DBG_KNL, "unable to flush SPD entries");
+ return FAILED;
}
-
+ else if (out->sadb_msg_errno)
+ {
+ DBG1(DBG_KNL, "unable to flush SPD entries: %s (%d)",
+ strerror(out->sadb_msg_errno), out->sadb_msg_errno);
+ free(out);
+ return FAILED;
+ }
+ free(out);
return SUCCESS;
}
/**
- * Register a socket for AQUIRE/EXPIRE messages
+ * Register a socket for ACQUIRE/EXPIRE messages
*/
static status_t register_pfkey_socket(private_kernel_pfkey_ipsec_t *this,
u_int8_t satype)
@@ -2075,14 +2484,14 @@ METHOD(kernel_ipsec_t, bypass_socket, bool,
if (setsockopt(fd, sol, ipsec_policy, &policy, sizeof(policy)) < 0)
{
DBG1(DBG_KNL, "unable to set IPSEC_POLICY on socket: %s",
- strerror(errno));
+ strerror(errno));
return FALSE;
}
policy.sadb_x_policy_dir = IPSEC_DIR_INBOUND;
if (setsockopt(fd, sol, ipsec_policy, &policy, sizeof(policy)) < 0)
{
DBG1(DBG_KNL, "unable to set IPSEC_POLICY on socket: %s",
- strerror(errno));
+ strerror(errno));
return FALSE;
}
return TRUE;
@@ -2103,7 +2512,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);
@@ -2125,14 +2538,18 @@ kernel_pfkey_ipsec_t *kernel_pfkey_ipsec_create()
.update_sa = _update_sa,
.query_sa = _query_sa,
.del_sa = _del_sa,
+ .flush_sas = _flush_sas,
.add_policy = _add_policy,
.query_policy = _query_policy,
.del_policy = _del_policy,
+ .flush_policies = _flush_policies,
.bypass_socket = _bypass_socket,
.destroy = _destroy,
},
},
.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,
@@ -2172,8 +2589,8 @@ kernel_pfkey_ipsec_t *kernel_pfkey_ipsec_create()
return NULL;
}
- this->job = callback_job_create((callback_job_cb_t)receive_events,
- this, NULL, NULL);
+ this->job = callback_job_create_with_prio((callback_job_cb_t)receive_events,
+ this, NULL, NULL, JOB_PRIO_CRITICAL);
lib->processor->queue_job(lib->processor, (job_t*)this->job);
return &this->public;
diff --git a/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_plugin.c b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_plugin.c
index 842511181..894175402 100644
--- a/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_plugin.c
+++ b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_plugin.c
@@ -38,11 +38,20 @@ METHOD(plugin_t, get_name, char*,
return "kernel-pfkey";
}
+METHOD(plugin_t, get_features, int,
+ private_kernel_pfkey_plugin_t *this, plugin_feature_t *features[])
+{
+ static plugin_feature_t f[] = {
+ PLUGIN_CALLBACK(kernel_ipsec_register, kernel_pfkey_ipsec_create),
+ PLUGIN_PROVIDE(CUSTOM, "kernel-ipsec"),
+ };
+ *features = f;
+ return countof(f);
+}
+
METHOD(plugin_t, destroy, void,
private_kernel_pfkey_plugin_t *this)
{
- hydra->kernel_interface->remove_ipsec_interface(hydra->kernel_interface,
- (kernel_ipsec_constructor_t)kernel_pfkey_ipsec_create);
free(this);
}
@@ -57,13 +66,11 @@ plugin_t *kernel_pfkey_plugin_create()
.public = {
.plugin = {
.get_name = _get_name,
- .reload = (void*)return_false,
+ .get_features = _get_features,
.destroy = _destroy,
},
},
);
- hydra->kernel_interface->add_ipsec_interface(hydra->kernel_interface,
- (kernel_ipsec_constructor_t)kernel_pfkey_ipsec_create);
return &this->public.plugin;
}