aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/charon/encoding/message.c2
-rw-r--r--src/charon/sa/child_sa.c159
-rw-r--r--src/charon/sa/child_sa.h17
-rw-r--r--src/charon/sa/transactions/create_child_sa.c23
-rw-r--r--src/charon/sa/transactions/delete_child_sa.c7
-rw-r--r--src/charon/threads/kernel_interface.c785
-rw-r--r--src/charon/threads/kernel_interface.h128
7 files changed, 553 insertions, 568 deletions
diff --git a/src/charon/encoding/message.c b/src/charon/encoding/message.c
index 8f2dadfa0..d66a4cd74 100644
--- a/src/charon/encoding/message.c
+++ b/src/charon/encoding/message.c
@@ -204,7 +204,7 @@ static payload_rule_t create_child_sa_i_payload_rules[] = {
* Message rule for CREATE_CHILD_SA from responder.
*/
static payload_rule_t create_child_sa_r_payload_rules[] = {
- {NOTIFY,0,MAX_NOTIFY_PAYLOADS,TRUE,FALSE},
+ {NOTIFY,0,MAX_NOTIFY_PAYLOADS,TRUE,TRUE},
{SECURITY_ASSOCIATION,1,1,TRUE,FALSE},
{NONCE,1,1,TRUE,FALSE},
{KEY_EXCHANGE,0,1,TRUE,FALSE},
diff --git a/src/charon/sa/child_sa.c b/src/charon/sa/child_sa.c
index 033114154..4ff8b945b 100644
--- a/src/charon/sa/child_sa.c
+++ b/src/charon/sa/child_sa.c
@@ -113,11 +113,6 @@ struct private_child_sa_t {
* transaction which is rekeying this CHILD_SA
*/
void *rekeying_transaction;
-
- /**
- * has this child SA been rekeyed/is rekeying?
- */
- bool is_rekeying;
/**
* Specifies if NAT traversal is used
@@ -446,21 +441,21 @@ static status_t add_policies(private_child_sa_t *this, linked_list_t *my_ts_list
policy->me.net, policy->other.net,
policy->me.net_mask, policy->other.net_mask,
XFRM_POLICY_OUT, policy->upper_proto,
- this->protocol, this->reqid);
+ this->protocol, this->reqid, FALSE);
status |= charon->kernel_interface->add_policy(charon->kernel_interface,
this->other.addr, this->me.addr,
policy->other.net, policy->me.net,
policy->other.net_mask, policy->me.net_mask,
XFRM_POLICY_IN, policy->upper_proto,
- this->protocol, this->reqid);
+ this->protocol, this->reqid, FALSE);
status |= charon->kernel_interface->add_policy(charon->kernel_interface,
this->other.addr, this->me.addr,
policy->other.net, policy->me.net,
policy->other.net_mask, policy->me.net_mask,
XFRM_POLICY_FWD, policy->upper_proto,
- this->protocol, this->reqid);
+ this->protocol, this->reqid, FALSE);
if (status != SUCCESS)
{
@@ -487,7 +482,6 @@ static status_t add_policies(private_child_sa_t *this, linked_list_t *my_ts_list
static void set_rekeying_transaction(private_child_sa_t *this, void *transaction)
{
this->rekeying_transaction = transaction;
- this->is_rekeying = TRUE;
}
/**
@@ -499,99 +493,27 @@ static void* get_rekeying_transaction(private_child_sa_t *this)
}
/**
- * Implementation of child_sa_t.is_rekeying.
- */
-static bool is_rekeying(private_child_sa_t *this)
-{
- return this->is_rekeying;
-}
-
-/**
* Implementation of child_sa_t.get_use_time
*/
static status_t get_use_time(private_child_sa_t *this, bool inbound, time_t *use_time)
{
- iterator_t *iterator;
- sa_policy_t *policy;
- struct protoent *proto;
- char proto_buf[8] = "";
- char *proto_name = proto_buf;
status_t status;
*use_time = UNDEFINED_TIME;
-
- iterator = this->policies->create_iterator(this->policies, TRUE);
- while (iterator->iterate(iterator, (void**)&policy))
+
+ if (inbound)
{
- time_t ut;
-
- if (policy->upper_proto)
- {
- proto = getprotobynumber(policy->upper_proto);
- if (proto)
- {
- proto_name = proto->p_name;
- }
- else
- {
- snprintf(proto_buf, sizeof(proto_buf), "<%d>", policy->upper_proto);
- }
- }
-
- this->logger->log(this->logger, CONTROL|LEVEL1,
- "querying policy: %s/%d==%s==%s/%d",
- policy->me.net->get_address(policy->me.net), policy->me.net_mask,
- proto_name,
- policy->other.net->get_address(policy->other.net), policy->other.net_mask);
-
- if (inbound)
- {
- status = charon->kernel_interface->query_policy(charon->kernel_interface,
- this->other.addr, this->me.addr,
- policy->other.net, policy->me.net,
- policy->other.net_mask, policy->me.net_mask,
- XFRM_POLICY_IN, policy->upper_proto,
- &ut);
-
- /* also check forward policy in tunnel mode */
- if (status == SUCCESS /*&& mode == TUNNEL XXX */)
- {
- time_t fwd;
-
- status = charon->kernel_interface->query_policy(charon->kernel_interface,
- this->other.addr, this->me.addr,
- policy->other.net, policy->me.net,
- policy->other.net_mask, policy->me.net_mask,
- XFRM_POLICY_FWD, policy->upper_proto,
- &fwd);
-
- if (status == SUCCESS)
- {
- ut = max(ut, fwd);
- }
- }
- }
- else
- {
- status = charon->kernel_interface->query_policy(charon->kernel_interface,
- this->me.addr, this->other.addr,
- policy->me.net, policy->other.net,
- policy->me.net_mask, policy->other.net_mask,
- XFRM_POLICY_OUT, policy->upper_proto,
- &ut);
- }
-
- if (status != SUCCESS)
- {
- iterator->destroy(iterator);
- return FAILED;
- }
-
- *use_time = max(*use_time, ut);
+ status = charon->kernel_interface->query_sa(charon->kernel_interface,
+ this->me.addr, this->me.spi,
+ this->protocol, use_time);
}
- iterator->destroy(iterator);
-
- return SUCCESS;
+ else
+ {
+ status = charon->kernel_interface->query_sa(charon->kernel_interface,
+ this->other.addr, this->other.spi,
+ this->protocol, use_time);
+ }
+ return status;
}
/**
@@ -759,18 +681,13 @@ static status_t update_policy_hosts(private_child_sa_t *this, host_t *new_me, ho
iterator = this->policies->create_iterator(this->policies, TRUE);
while (iterator->iterate(iterator, (void**)&policy))
{
- this->logger->log(this->logger, CONTROL|LEVEL1,
- "updating policy: %s/%d====%s/%d",
- policy->me.net->get_address(policy->me.net), policy->me.net_mask,
- policy->other.net->get_address(policy->other.net), policy->other.net_mask);
-
status = charon->kernel_interface->add_policy(
charon->kernel_interface,
new_me, new_other,
policy->me.net, policy->other.net,
policy->me.net_mask, policy->other.net_mask,
XFRM_POLICY_OUT, policy->upper_proto,
- this->protocol, this->reqid);
+ this->protocol, this->reqid, TRUE);
status |= charon->kernel_interface->add_policy(
charon->kernel_interface,
@@ -778,7 +695,7 @@ static status_t update_policy_hosts(private_child_sa_t *this, host_t *new_me, ho
policy->other.net, policy->me.net,
policy->other.net_mask, policy->me.net_mask,
XFRM_POLICY_IN, policy->upper_proto,
- this->protocol, this->reqid);
+ this->protocol, this->reqid, TRUE);
status |= charon->kernel_interface->add_policy(
charon->kernel_interface,
@@ -786,8 +703,8 @@ static status_t update_policy_hosts(private_child_sa_t *this, host_t *new_me, ho
policy->other.net, policy->me.net,
policy->other.net_mask, policy->me.net_mask,
XFRM_POLICY_FWD, policy->upper_proto,
- this->protocol, this->reqid);
-
+ this->protocol, this->reqid, TRUE);
+
if (status != SUCCESS)
{
iterator->destroy(iterator);
@@ -879,27 +796,21 @@ static void destroy(private_child_sa_t *this)
/* delete all policies in the kernel */
while (this->policies->remove_last(this->policies, (void**)&policy) == SUCCESS)
{
- if (!this->is_rekeying)
- {
- /* let rekeyed policies, as they are used by another child_sa */
- charon->kernel_interface->del_policy(charon->kernel_interface,
- this->me.addr, this->other.addr,
- policy->me.net, policy->other.net,
- policy->me.net_mask, policy->other.net_mask,
- XFRM_POLICY_OUT, policy->upper_proto);
-
- charon->kernel_interface->del_policy(charon->kernel_interface,
- this->other.addr, this->me.addr,
- policy->other.net, policy->me.net,
- policy->other.net_mask, policy->me.net_mask,
- XFRM_POLICY_IN, policy->upper_proto);
-
- charon->kernel_interface->del_policy(charon->kernel_interface,
- this->other.addr, this->me.addr,
- policy->other.net, policy->me.net,
- policy->other.net_mask, policy->me.net_mask,
- XFRM_POLICY_FWD, policy->upper_proto);
- }
+ /* let rekeyed policies, as they are used by another child_sa */
+ charon->kernel_interface->del_policy(charon->kernel_interface,
+ policy->me.net, policy->other.net,
+ policy->me.net_mask, policy->other.net_mask,
+ XFRM_POLICY_OUT, policy->upper_proto);
+
+ charon->kernel_interface->del_policy(charon->kernel_interface,
+ policy->other.net, policy->me.net,
+ policy->other.net_mask, policy->me.net_mask,
+ XFRM_POLICY_IN, policy->upper_proto);
+
+ charon->kernel_interface->del_policy(charon->kernel_interface,
+ policy->other.net, policy->me.net,
+ policy->other.net_mask, policy->me.net_mask,
+ XFRM_POLICY_FWD, policy->upper_proto);
policy->me.net->destroy(policy->me.net);
policy->other.net->destroy(policy->other.net);
free(policy);
@@ -933,7 +844,6 @@ child_sa_t * child_sa_create(u_int32_t rekey, host_t *me, host_t* other,
this->public.get_use_time = (status_t (*)(child_sa_t*,bool,time_t*))get_use_time;
this->public.set_rekeying_transaction = (void (*)(child_sa_t*,void*))set_rekeying_transaction;
this->public.get_rekeying_transaction = (void* (*)(child_sa_t*))get_rekeying_transaction;
- this->public.is_rekeying = (bool (*)(child_sa_t*))is_rekeying;
this->public.log_status = (void (*)(child_sa_t*, logger_t*, char*))log_status;
this->public.destroy = (void(*)(child_sa_t*))destroy;
@@ -953,7 +863,6 @@ child_sa_t * child_sa_create(u_int32_t rekey, host_t *me, host_t* other,
this->policies = linked_list_create();
this->protocol = PROTO_NONE;
this->rekeying_transaction = NULL;
- this->is_rekeying = FALSE;
return &this->public;
}
diff --git a/src/charon/sa/child_sa.h b/src/charon/sa/child_sa.h
index fba0c73a5..f373c5615 100644
--- a/src/charon/sa/child_sa.h
+++ b/src/charon/sa/child_sa.h
@@ -174,6 +174,9 @@ struct child_sa_t {
* such situations to handle them cleanly. A rekeying transaction
* registers itself to the CHILD_SA, and checks later if another
* transaction is in progress of a rekey.
+ *
+ * @todo Fix include problematics to allow inclusion of
+ * the create_child_sa_t transaction.
*
* @param this calling object
*/
@@ -182,23 +185,13 @@ struct child_sa_t {
/**
* @brief Get the transaction which rekeys this CHILD_SA.
*
- * See set_rekeying_transactoin
+ * @see set_rekeying_transactoin().
*
* @param this calling object
*/
void* (*get_rekeying_transaction) (child_sa_t *this);
/**
- * @brief Is the CHILD SA rekeying/in progress of rekeying?
- *
- * This is a readonly parameter. It is set whenever the
- * set_rekeying_transaction() method is called.
- *
- * @param this calling object
- */
- bool (*is_rekeying) (child_sa_t *this);
-
- /**
* @brief Log the status of a child_sa to a logger.
*
* The status of ESP/AH SAs is logged with the supplied logger in
@@ -223,7 +216,7 @@ struct child_sa_t {
/**
* @brief Constructor to create a new child_sa_t.
- *
+ *
* @param rekey_reqid reqid of old CHILD_SA when rekeying, 0 otherwise
* @param me own address
* @param other remote address
diff --git a/src/charon/sa/transactions/create_child_sa.c b/src/charon/sa/transactions/create_child_sa.c
index 30ccdef06..8f291d5b9 100644
--- a/src/charon/sa/transactions/create_child_sa.c
+++ b/src/charon/sa/transactions/create_child_sa.c
@@ -172,8 +172,8 @@ static status_t get_request(private_create_child_sa_t *this, message_t **result)
host_t *me, *other;
/* check if we are not already rekeying */
- if (this->rekeyed_sa &&
- this->rekeyed_sa->is_rekeying(this->rekeyed_sa))
+ if (this->rekeyed_sa &&
+ this->rekeyed_sa->get_rekeying_transaction(this->rekeyed_sa))
{
this->logger->log(this->logger, ERROR,
"rekeying a CHILD_SA which is already rekeying, aborted");
@@ -628,20 +628,11 @@ static status_t get_response(private_create_child_sa_t *this, message_t *request
response->add_payload(response, (payload_t*)ts_response);
}
/* CHILD_SA successfully created. We set us as the rekeying transaction of
- * this SA. If we already initiated rekeying of the same SA, we will detect
+ * the rekeyed SA. If we already initiated rekeying of the same SA, we will detect
* this later in the conclude() call. */
if (this->rekeyed_sa)
{
- if (this->rekeyed_sa->is_rekeying(this->rekeyed_sa))
- {
- /* rekeying already in progress, register us, too */
- this->rekeyed_sa->set_rekeying_transaction(this->rekeyed_sa, &this->public);
- }
- else
- {
- /* no rekeying in progress. mark SA as rekeyed, but not conflicted */
- this->rekeyed_sa->set_rekeying_transaction(this->rekeyed_sa, NULL);
- }
+ this->rekeyed_sa->set_rekeying_transaction(this->rekeyed_sa, &this->public);
}
return SUCCESS;
}
@@ -779,14 +770,14 @@ static status_t conclude(private_create_child_sa_t *this, message_t *response,
other = (private_create_child_sa_t*)
this->rekeyed_sa->get_rekeying_transaction(this->rekeyed_sa);
- /* we are not rekeying anymore, unregister us */
+ /* rekeying finished, update SA status */
this->rekeyed_sa->set_rekeying_transaction(this->rekeyed_sa, NULL);
if (other != this)
{ /* simlutaneous rekeying is going on, not so good */
chunk_t this_lowest, other_lowest;
- /* check if this has a lower nonce the other */
+ /* check if this has a lower nonce than other */
if (memcmp(this->nonce_i.ptr, this->nonce_r.ptr,
min(this->nonce_i.len, this->nonce_r.len)) < 0)
{
@@ -822,7 +813,6 @@ static status_t conclude(private_create_child_sa_t *this, message_t *response,
/* delete the old SA if we have won the rekeying nonce compare*/
if (!this->lost)
{
- other->rekeyed_sa->set_rekeying_transaction(other->rekeyed_sa, NULL);
delete_child_sa = delete_child_sa_create(this->ike_sa, this->message_id + 1);
delete_child_sa->set_child_sa(delete_child_sa, this->rekeyed_sa);
*next = (transaction_t*)delete_child_sa;
@@ -832,7 +822,6 @@ static status_t conclude(private_create_child_sa_t *this, message_t *response,
{
/* we have lost simlutaneous rekeying, delete the CHILD_SA we just have created */
delete_child_sa = delete_child_sa_create(this->ike_sa, this->message_id + 1);
- new_child->set_rekeying_transaction(new_child, NULL);
delete_child_sa->set_child_sa(delete_child_sa, new_child);
*next = (transaction_t*)delete_child_sa;
}
diff --git a/src/charon/sa/transactions/delete_child_sa.c b/src/charon/sa/transactions/delete_child_sa.c
index 621d2ab4c..6ea25e22e 100644
--- a/src/charon/sa/transactions/delete_child_sa.c
+++ b/src/charon/sa/transactions/delete_child_sa.c
@@ -192,8 +192,11 @@ static status_t process_delete(private_delete_child_sa_t *this, delete_payload_t
* this means we have lost the nonce comparison, and the rekeying
* will fail. We set a flag in the transaction for this special case.
*/
- this->logger->log(this->logger, CONTROL,
- "DELETE received while rekeying, rekeying cancelled");
+ if (!response)
+ { /* only whine as initiator */
+ this->logger->log(this->logger, CONTROL,
+ "DELETE received while rekeying, rekeying cancelled");
+ }
rekey->cancel(rekey);
}
/* delete it, with inbound spi */
diff --git a/src/charon/threads/kernel_interface.c b/src/charon/threads/kernel_interface.c
index 0f4a4735a..c4966f4d3 100644
--- a/src/charon/threads/kernel_interface.c
+++ b/src/charon/threads/kernel_interface.c
@@ -43,20 +43,29 @@
#include <queues/jobs/delete_child_sa_job.h>
#include <queues/jobs/rekey_child_sa_job.h>
-
+/** kernel level protocol identifiers */
#define KERNEL_ESP 50
#define KERNEL_AH 51
+/** default priority of installed policies */
#define SPD_PRIORITY 1024
#define BUFFER_SIZE 1024
-/* returns a pointer to the first rtattr following the nlmsghdr *nlh and the 'usual' netlink data x like 'struct xfrm_usersa_info' */
+/**
+ * returns a pointer to the first rtattr following the nlmsghdr *nlh and the
+ * 'usual' netlink data x like 'struct xfrm_usersa_info'
+ */
#define XFRM_RTA(nlh, x) ((struct rtattr*)(NLMSG_DATA(nlh) + NLMSG_ALIGN(sizeof(x))))
-/* returns a pointer to the next rtattr following rta.
- * !!! do not use this to parse messages. use RTA_NEXT and RTA_OK instead !!! */
+/**
+ * returns a pointer to the next rtattr following rta.
+ * !!! do not use this to parse messages. use RTA_NEXT and RTA_OK instead !!!
+ */
#define XFRM_RTA_NEXT(rta) ((struct rtattr*)(((char*)(rta)) + RTA_ALIGN((rta)->rta_len)))
-/* returns the total size of attached rta data (after 'usual' netlink data x like 'struct xfrm_usersa_info') */
+/**
+ * returns the total size of attached rta data
+ * (after 'usual' netlink data x like 'struct xfrm_usersa_info')
+ */
#define XFRM_PAYLOAD(nlh, x) NLMSG_PAYLOAD(nlh, sizeof(x))
typedef struct kernel_algorithm_t kernel_algorithm_t;
@@ -117,7 +126,8 @@ kernel_algorithm_t integrity_algs[] = {
/**
* Look up a kernel algorithm name and its key size
*/
-char* lookup_algorithm(kernel_algorithm_t *kernel_algo, algorithm_t *ikev2_algo, u_int *key_size)
+char* lookup_algorithm(kernel_algorithm_t *kernel_algo,
+ algorithm_t *ikev2_algo, u_int *key_size)
{
while (kernel_algo->ikev2_id != END_OF_LIST)
{
@@ -139,11 +149,33 @@ char* lookup_algorithm(kernel_algorithm_t *kernel_algo, algorithm_t *ikev2_algo,
return NULL;
}
+
+
+typedef struct kernel_policy_t kernel_policy_t;
+
+/**
+ * Installed kernel policy.
+ */
+struct kernel_policy_t {
+
+ /** direction of this policy: in, out, forward */
+ u_int8_t direction;
+
+ /** reqid of the policy */
+ u_int32_t reqid;
+
+ /** parameters of installed policy */
+ struct xfrm_selector sel;
+
+ /** by how many CHILD_SA's this policy is used */
+ u_int refcount;
+};
+
+
typedef struct private_kernel_interface_t private_kernel_interface_t;
- /**
- * @brief Private Variables and Functions of kernel_interface class.
- *
+/**
+ * Private Variables and Functions of kernel_interface class.
*/
struct private_kernel_interface_t {
/**
@@ -152,6 +184,16 @@ struct private_kernel_interface_t {
kernel_interface_t public;
/**
+ * List of installed policies (kernel_policy_t)
+ */
+ linked_list_t *policies;
+
+ /**
+ * Mutex locks access to policies list.
+ */
+ pthread_mutex_t pol_mutex;
+
+ /**
* Netlink communication socket.
*/
int socket;
@@ -179,7 +221,7 @@ struct private_kernel_interface_t {
/**
* Mutex locks access to replies list.
*/
- pthread_mutex_t mutex;
+ pthread_mutex_t rep_mutex;
/**
* Condvar allows signaling of threads waiting for a reply.
@@ -190,17 +232,181 @@ struct private_kernel_interface_t {
* Logger for XFRM stuff
*/
logger_t *logger;
+};
+
+
+/**
+ * Send a message down to the kernel and wait for its response
+ */
+static status_t send_message(private_kernel_interface_t *this,
+ struct nlmsghdr *request, struct nlmsghdr **response)
+{
+ size_t length;
+ struct sockaddr_nl addr;
- /**
- * Function for the thread, receives messages.
- */
- void (*receive_messages) (private_kernel_interface_t *this);
+ request->nlmsg_seq = ++this->seq;
+ request->nlmsg_pid = 0;
- /**
- * Sends a netlink_message_t down to the kernel and wait for reply.
- */
- status_t (*send_message) (private_kernel_interface_t *this, struct nlmsghdr *request, struct nlmsghdr **response);
-};
+ memset(&addr, 0, sizeof(struct sockaddr_nl));
+ addr.nl_family = AF_NETLINK;
+ addr.nl_pid = 0;
+ addr.nl_groups = 0;
+
+ length = sendto(this->socket,(void *)request, request->nlmsg_len, 0,
+ (struct sockaddr *)&addr, sizeof(addr));
+
+ if (length < 0)
+ {
+ return FAILED;
+ }
+ else if (length != request->nlmsg_len)
+ {
+ return FAILED;
+ }
+
+ pthread_mutex_lock(&(this->rep_mutex));
+
+ while (TRUE)
+ {
+ iterator_t *iterator;
+ bool found = FALSE;
+ /* search list, break if found */
+ iterator = this->responses->create_iterator(this->responses, TRUE);
+ while (iterator->has_next(iterator))
+ {
+ struct nlmsghdr *listed_response;
+ iterator->current(iterator, (void**)&listed_response);
+ if (listed_response->nlmsg_seq == request->nlmsg_seq)
+ {
+ /* matches our request, this is the reply */
+ *response = listed_response;
+ iterator->remove(iterator);
+ found = TRUE;
+ break;
+ }
+ }
+ iterator->destroy(iterator);
+
+ if (found)
+ {
+ break;
+ }
+ /* TODO: we should time out, if something goes wrong!??? */
+ pthread_cond_wait(&(this->condvar), &(this->rep_mutex));
+ }
+
+ pthread_mutex_unlock(&(this->rep_mutex));
+
+ return SUCCESS;
+}
+
+/**
+ * Implementation of private_kernel_interface_t.receive_messages.
+ */
+static void receive_messages(private_kernel_interface_t *this)
+{
+ while(TRUE)
+ {
+ unsigned char response[BUFFER_SIZE];
+ struct nlmsghdr *hdr, *listed_response;
+ while (TRUE)
+ {
+ struct sockaddr_nl addr;
+ socklen_t addr_length;
+ size_t length;
+
+ addr_length = sizeof(addr);
+
+ length = recvfrom(this->socket, &response, sizeof(response), 0, (struct sockaddr*)&addr, &addr_length);
+ if (length < 0)
+ {
+ if (errno == EINTR)
+ {
+ /* interrupted, try again */
+ continue;
+ }
+ charon->kill(charon, "receiving from netlink socket failed");
+ }
+ if (!NLMSG_OK((struct nlmsghdr *)response, length))
+ {
+ /* bad netlink message */
+ continue;
+ }
+ if (addr.nl_pid != 0)
+ {
+ /* not from kernel. not interested, try another one */
+ continue;
+ }
+ /* good message, handle it */
+ break;
+ }
+
+ /* we handle ACQUIRE and EXPIRE messages directly
+ */
+ hdr = (struct nlmsghdr*)response;
+ if (hdr->nlmsg_type == XFRM_MSG_ACQUIRE)
+ {
+ struct xfrm_user_acquire *acquire;
+
+ acquire = (struct xfrm_user_acquire*)NLMSG_DATA(hdr);
+ this->logger->log(this->logger, CONTROL,
+ "Received a XFRM_MSG_ACQUIRE with index %d. Ignored",
+ acquire->policy.index);
+
+ }
+ else if (hdr->nlmsg_type == XFRM_MSG_EXPIRE)
+ {
+ job_t *job;
+ protocol_id_t protocol;
+ u_int32_t spi;
+ struct xfrm_user_expire *expire;
+
+ expire = (struct xfrm_user_expire*)NLMSG_DATA(hdr);
+ protocol = expire->state.id.proto == KERNEL_ESP ?
+ PROTO_ESP : PROTO_AH;
+ spi = expire->state.id.spi;
+
+ this->logger->log(this->logger, CONTROL|LEVEL1,
+ "Received a XFRM_MSG_EXPIRE");
+ this->logger->log(this->logger, CONTROL,
+ "creating %s job for %s CHILD_SA 0x%x",
+ expire->hard ? "delete" : "rekey",
+ mapping_find(protocol_id_m, protocol), ntohl(spi));
+ if (expire->hard)
+ {
+ job = (job_t*)delete_child_sa_job_create(protocol, spi);
+ }
+ else
+ {
+ job = (job_t*)rekey_child_sa_job_create(protocol, spi);
+ }
+ charon->job_queue->add(charon->job_queue, job);
+ }
+ /* NLMSG_ERROR is sent back for acknowledge (or on error), an
+ * XFRM_MSG_NEWSA is returned when we alloc spis and when
+ * updating SAs.
+ * XFRM_MSG_NEWPOLICY is returned when we query a policy.
+ * list these responses for the sender
+ */
+ else if (hdr->nlmsg_type == NLMSG_ERROR ||
+ hdr->nlmsg_type == XFRM_MSG_NEWSA ||
+ hdr->nlmsg_type == XFRM_MSG_NEWPOLICY)
+ {
+ /* add response to queue */
+ listed_response = malloc(hdr->nlmsg_len);
+ memcpy(listed_response, &response, hdr->nlmsg_len);
+
+ pthread_mutex_lock(&(this->rep_mutex));
+ this->responses->insert_last(this->responses, (void*)listed_response);
+ pthread_mutex_unlock(&(this->rep_mutex));
+ /* signal ALL waiting threads */
+ pthread_cond_broadcast(&(this->condvar));
+ }
+ /* we are not interested in anything other.
+ * anyway, move on to the next message */
+ continue;
+ }
+}
/**
* Implementation of kernel_interface_t.get_spi.
@@ -212,18 +418,20 @@ static status_t get_spi(private_kernel_interface_t *this,
{
unsigned char request[BUFFER_SIZE];
struct nlmsghdr *response;
-
+ struct nlmsghdr *hdr;
+ struct xfrm_userspi_info *userspi;
+
memset(&request, 0, sizeof(request));
status_t status = SUCCESS;
this->logger->log(this->logger, CONTROL|LEVEL2, "getting spi");
- struct nlmsghdr *hdr = (struct nlmsghdr*)request;
+ hdr = (struct nlmsghdr*)request;
hdr->nlmsg_flags = NLM_F_REQUEST;
hdr->nlmsg_type = XFRM_MSG_ALLOCSPI;
hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_userspi_info));
- struct xfrm_userspi_info *userspi = (struct xfrm_userspi_info*)NLMSG_DATA(hdr);
+ userspi = (struct xfrm_userspi_info*)NLMSG_DATA(hdr);
userspi->info.saddr = src->get_xfrm_addr(src);
userspi->info.id.daddr = dest->get_xfrm_addr(dest);
userspi->info.id.proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH;
@@ -233,7 +441,7 @@ static status_t get_spi(private_kernel_interface_t *this,
userspi->min = 0xc0000000;
userspi->max = 0xcFFFFFFF;
- if (this->send_message(this, hdr, &response) != SUCCESS)
+ if (send_message(this, hdr, &response) != SUCCESS)
{
this->logger->log(this->logger, ERROR, "netlink communication failed");
return FAILED;
@@ -280,18 +488,20 @@ static status_t add_sa(private_kernel_interface_t *this,
struct nlmsghdr *response;
char *alg_name;
size_t key_size;
+ struct nlmsghdr *hdr;
+ struct xfrm_usersa_info *sa;
memset(&request, 0, sizeof(request));
status_t status = SUCCESS;
this->logger->log(this->logger, CONTROL|LEVEL2, "adding SA");
- struct nlmsghdr *hdr = (struct nlmsghdr*)request;
+ hdr = (struct nlmsghdr*)request;
hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
hdr->nlmsg_type = replace ? XFRM_MSG_UPDSA : XFRM_MSG_NEWSA;
hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_usersa_info));
- struct xfrm_usersa_info *sa = (struct xfrm_usersa_info*)NLMSG_DATA(hdr);
+ sa = (struct xfrm_usersa_info*)NLMSG_DATA(hdr);
sa->saddr = me->get_xfrm_addr(me);
sa->id.daddr = other->get_xfrm_addr(other);
@@ -401,7 +611,7 @@ static status_t add_sa(private_kernel_interface_t *this,
rthdr = XFRM_RTA_NEXT(rthdr);
}
- if (this->send_message(this, hdr, &response) != SUCCESS)
+ if (send_message(this, hdr, &response) != SUCCESS)
{
this->logger->log(this->logger, ERROR, "netlink communication failed");
return FAILED;
@@ -422,6 +632,9 @@ static status_t add_sa(private_kernel_interface_t *this,
return status;
}
+/**
+ * Implementation of kernel_interface_t.update_sa_hosts.
+ */
static status_t update_sa_hosts(
private_kernel_interface_t *this,
host_t *src, host_t *dst,
@@ -431,24 +644,26 @@ static status_t update_sa_hosts(
{
unsigned char request[BUFFER_SIZE];
struct nlmsghdr *update, *response;
+ struct nlmsghdr *hdr;
+ struct xfrm_usersa_id *sa_id;
memset(&request, 0, sizeof(request));
status_t status = SUCCESS;
this->logger->log(this->logger, CONTROL|LEVEL2, "getting SA");
- struct nlmsghdr *hdr = (struct nlmsghdr*)request;
+ hdr = (struct nlmsghdr*)request;
hdr->nlmsg_flags = NLM_F_REQUEST;
hdr->nlmsg_type = XFRM_MSG_GETSA;
hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_usersa_id));
- struct xfrm_usersa_id *sa_id = (struct xfrm_usersa_id*)NLMSG_DATA(hdr);
+ sa_id = (struct xfrm_usersa_id*)NLMSG_DATA(hdr);
sa_id->daddr = dst->get_xfrm_addr(dst);
sa_id->spi = spi;
sa_id->proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH;
sa_id->family = dst->get_family(dst);
- if (this->send_message(this, hdr, &update) != SUCCESS)
+ if (send_message(this, hdr, &update) != SUCCESS)
{
this->logger->log(this->logger, ERROR, "netlink communication failed");
return FAILED;
@@ -508,7 +723,7 @@ static status_t update_sa_hosts(
}
}
- if (this->send_message(this, update, &response) != SUCCESS)
+ if (send_message(this, update, &response) != SUCCESS)
{
this->logger->log(this->logger, ERROR, "netlink communication failed");
free(update);
@@ -535,32 +750,86 @@ static status_t update_sa_hosts(
free(response);
return status;
}
+
+/**
+ * Implementation of kernel_interface_t.query_sa.
+ */
+static status_t query_sa(private_kernel_interface_t *this, host_t *dst,
+ u_int32_t spi, protocol_id_t protocol, time_t *use_time)
+{
+ unsigned char request[BUFFER_SIZE];
+ struct nlmsghdr *response;
+ struct nlmsghdr *hdr;
+ struct xfrm_usersa_id *sa_id;
+ struct xfrm_usersa_info *sa_info;
+
+ this->logger->log(this->logger, CONTROL|LEVEL2, "querying SA");
+ memset(&request, 0, sizeof(request));
+
+ hdr = (struct nlmsghdr*)request;
+ hdr->nlmsg_flags = NLM_F_REQUEST;
+ hdr->nlmsg_type = XFRM_MSG_GETSA;
+ hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_usersa_info));
+
+ sa_id = (struct xfrm_usersa_id*)NLMSG_DATA(hdr);
+ sa_id->daddr = dst->get_xfrm_addr(dst);
+ sa_id->spi = spi;
+ sa_id->proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH;
+ sa_id->family = dst->get_family(dst);
+
+ if (send_message(this, hdr, &response) != SUCCESS)
+ {
+ this->logger->log(this->logger, ERROR, "netlink communication failed");
+ return FAILED;
+ }
+ else if (response->nlmsg_type != XFRM_MSG_NEWSA)
+ {
+ this->logger->log(this->logger, ERROR, "netlink request XFRM_MSG_GETSA not acknowledged");
+ free(response);
+ return FAILED;
+ }
+ else if (response->nlmsg_len < NLMSG_LENGTH(sizeof(struct xfrm_usersa_info)))
+ {
+ this->logger->log(this->logger, ERROR, "netlink request XFRM_MSG_GETSA got an invalid reply");
+ free(response);
+ return FAILED;
+ }
+
+ sa_info = (struct xfrm_usersa_info*)NLMSG_DATA(response);
+ *use_time = (time_t)sa_info->curlft.use_time;
-static status_t del_sa( private_kernel_interface_t *this,
- host_t *dst,
- u_int32_t spi,
- protocol_id_t protocol)
+ free(response);
+ return SUCCESS;
+}
+
+/**
+ * Implementation of kernel_interface_t.del_sa.
+ */
+static status_t del_sa(private_kernel_interface_t *this, host_t *dst,
+ u_int32_t spi, protocol_id_t protocol)
{
unsigned char request[BUFFER_SIZE];
struct nlmsghdr *response;
+ struct nlmsghdr *hdr;
+ struct xfrm_usersa_id *sa_id;
memset(&request, 0, sizeof(request));
status_t status = SUCCESS;
this->logger->log(this->logger, CONTROL|LEVEL2, "deleting SA");
-
- struct nlmsghdr *hdr = (struct nlmsghdr*)request;
+
+ hdr = (struct nlmsghdr*)request;
hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
hdr->nlmsg_type = XFRM_MSG_DELSA;
hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_usersa_id));
-
- struct xfrm_usersa_id *sa_id = (struct xfrm_usersa_id*)NLMSG_DATA(hdr);
+
+ sa_id = (struct xfrm_usersa_id*)NLMSG_DATA(hdr);
sa_id->daddr = dst->get_xfrm_addr(dst);
sa_id->spi = spi;
sa_id->proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH;
sa_id->family = dst->get_family(dst);
- if (this->send_message(this, hdr, &response) != SUCCESS)
+ if (send_message(this, hdr, &response) != SUCCESS)
{
this->logger->log(this->logger, ERROR, "netlink communication failed");
return FAILED;
@@ -588,52 +857,85 @@ static status_t add_policy(private_kernel_interface_t *this,
host_t *me, host_t *other,
host_t *src, host_t *dst,
u_int8_t src_hostbits, u_int8_t dst_hostbits,
- int direction, int upper_proto,
+ u_int8_t direction, u_int8_t upper_proto,
protocol_id_t protocol,
- u_int32_t reqid)
+ u_int32_t reqid, bool update)
{
+ iterator_t *iterator;
+ kernel_policy_t *current, *policy;
+ bool found = FALSE;
unsigned char request[BUFFER_SIZE];
struct nlmsghdr *response;
-
- memset(&request, 0, sizeof(request));
+ struct xfrm_userpolicy_info *policy_info;
+ struct nlmsghdr *hdr;
status_t status = SUCCESS;
- this->logger->log(this->logger, CONTROL|LEVEL2, "adding policy");
-
- struct nlmsghdr *hdr = (struct nlmsghdr*)request;
- hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
- hdr->nlmsg_type = XFRM_MSG_UPDPOLICY;
- hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_userpolicy_info));
-
- struct xfrm_userpolicy_info *policy = (struct xfrm_userpolicy_info*)NLMSG_DATA(hdr);
-
- policy->sel.sport = htons(src->get_port(src));
- policy->sel.sport_mask = (policy->sel.sport) ? ~0 : 0;
+ /* create a policy */
+ policy = malloc_thing(kernel_policy_t);
+ memset(policy, 0, sizeof(kernel_policy_t));
policy->sel.saddr = src->get_xfrm_addr(src);
policy->sel.prefixlen_s = src_hostbits;
-
- policy->sel.dport = htons(dst->get_port(dst));
- policy->sel.dport_mask = (policy->sel.dport) ? ~0 : 0;
+ policy->sel.sport = htons(src->get_port(src));
+ policy->sel.sport_mask = (policy->sel.sport) ? ~0 : 0;
policy->sel.daddr = dst->get_xfrm_addr(dst);
policy->sel.prefixlen_d = dst_hostbits;
-
+ policy->sel.dport = htons(dst->get_port(dst));
+ policy->sel.dport_mask = (policy->sel.dport) ? ~0 : 0;
policy->sel.proto = upper_proto;
policy->sel.family = src->get_family(src);
+ policy->direction = direction;
- policy->dir = direction;
- policy->priority = SPD_PRIORITY;
- policy->action = XFRM_POLICY_ALLOW;
- policy->share = XFRM_SHARE_ANY;
-
- /* policies currently don't expire */
- policy->lft.soft_byte_limit = XFRM_INF;
- policy->lft.soft_packet_limit = XFRM_INF;
- policy->lft.hard_byte_limit = XFRM_INF;
- policy->lft.hard_packet_limit = XFRM_INF;
- policy->lft.soft_add_expires_seconds = 0;
- policy->lft.hard_add_expires_seconds = 0;
- policy->lft.soft_use_expires_seconds = 0;
- policy->lft.hard_use_expires_seconds = 0;
+ /* find the policy, which matches EXACTLY */
+ pthread_mutex_lock(&this->pol_mutex);
+ iterator = this->policies->create_iterator(this->policies, TRUE);
+ while (iterator->iterate(iterator, (void**)&current))
+ {
+ if (memcmp(current, policy, sizeof(struct xfrm_selector)) == 0 &&
+ policy->direction == current->direction)
+ {
+ /* use existing policy */
+ if (!update)
+ {
+ current->refcount++;
+ }
+ free(policy);
+ policy = current;
+ found = TRUE;
+ break;
+ }
+ }
+ iterator->destroy(iterator);
+ if (!found)
+ { /* apply the new one, if we have no such policy */
+ this->policies->insert_last(this->policies, policy);
+ policy->refcount = 1;
+ }
+
+ this->logger->log(this->logger, CONTROL|LEVEL2, "adding policy");
+
+ memset(&request, 0, sizeof(request));
+ hdr = (struct nlmsghdr*)request;
+ hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+ hdr->nlmsg_type = XFRM_MSG_UPDPOLICY;
+ hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_userpolicy_info));
+
+ policy_info = (struct xfrm_userpolicy_info*)NLMSG_DATA(hdr);
+ policy_info->sel = policy->sel;
+ policy_info->dir = direction;
+ policy_info->priority = SPD_PRIORITY;
+ policy_info->action = XFRM_POLICY_ALLOW;
+ policy_info->share = XFRM_SHARE_ANY;
+ pthread_mutex_unlock(&this->pol_mutex);
+
+ /* policies don't expire */
+ policy_info->lft.soft_byte_limit = XFRM_INF;
+ policy_info->lft.soft_packet_limit = XFRM_INF;
+ policy_info->lft.hard_byte_limit = XFRM_INF;
+ policy_info->lft.hard_packet_limit = XFRM_INF;
+ policy_info->lft.soft_add_expires_seconds = 0;
+ policy_info->lft.hard_add_expires_seconds = 0;
+ policy_info->lft.soft_use_expires_seconds = 0;
+ policy_info->lft.hard_use_expires_seconds = 0;
struct rtattr *rthdr = XFRM_RTA(hdr, struct xfrm_userpolicy_info);
rthdr->rta_type = XFRMA_TMPL;
@@ -656,19 +958,21 @@ static status_t add_policy(private_kernel_interface_t *this,
tmpl->saddr = me->get_xfrm_addr(me);
tmpl->id.daddr = other->get_xfrm_addr(other);
- if (this->send_message(this, hdr, &response) != SUCCESS)
+ if (send_message(this, hdr, &response) != SUCCESS)
{
this->logger->log(this->logger, ERROR, "netlink communication failed");
return FAILED;
}
else if (response->nlmsg_type != NLMSG_ERROR)
{
- this->logger->log(this->logger, ERROR, "netlink request XFRM_MSG_NEWPOLICY not acknowledged");
+ this->logger->log(this->logger, ERROR,
+ "netlink request XFRM_MSG_UPDPOLICY not acknowledged");
status = FAILED;
}
else if (((struct nlmsgerr*)NLMSG_DATA(response))->error)
{
- this->logger->log(this->logger, ERROR, "netlink request XFRM_MSG_NEWPOLICY got an error: %s",
+ this->logger->log(this->logger, ERROR,
+ "netlink request XFRM_MSG_UPDPOLICY got an error: %s",
strerror(-((struct nlmsgerr*)NLMSG_DATA(response))->error));
status = FAILED;
}
@@ -677,114 +981,84 @@ static status_t add_policy(private_kernel_interface_t *this,
return status;
}
-static status_t query_policy(private_kernel_interface_t *this,
- host_t *me, host_t *other,
- host_t *src, host_t *dst,
- u_int8_t src_hostbits, u_int8_t dst_hostbits,
- int direction, int upper_proto,
- time_t *use_time)
+/**
+ * Implementation of kernel_interface_t.del_policy.
+ */
+static status_t del_policy(private_kernel_interface_t *this,
+ host_t *src, host_t *dst,
+ u_int8_t src_hostbits, u_int8_t dst_hostbits,
+ u_int8_t direction, u_int8_t upper_proto)
{
+ kernel_policy_t *current, policy, *to_delete = NULL;
unsigned char request[BUFFER_SIZE];
struct nlmsghdr *response;
-
- memset(&request, 0, sizeof(request));
+ struct nlmsghdr *hdr;
+ struct xfrm_userpolicy_id *policy_id;
+ iterator_t *iterator;
status_t status = SUCCESS;
- this->logger->log(this->logger, CONTROL|LEVEL2, "querying policy");
-
- struct nlmsghdr *hdr = (struct nlmsghdr*)request;
- hdr->nlmsg_flags = NLM_F_REQUEST;
- hdr->nlmsg_type = XFRM_MSG_GETPOLICY;
- hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_userpolicy_id));
-
- struct xfrm_userpolicy_id *policy_id = (struct xfrm_userpolicy_id*)NLMSG_DATA(hdr);
- policy_id->sel.sport = htons(src->get_port(src));
- policy_id->sel.sport_mask = (policy_id->sel.sport) ? ~0 : 0;
- policy_id->sel.saddr = src->get_xfrm_addr(src);
- policy_id->sel.prefixlen_s = src_hostbits;
-
- policy_id->sel.dport = htons(dst->get_port(dst));
- policy_id->sel.dport_mask = (policy_id->sel.dport) ? ~0 : 0;
- policy_id->sel.daddr = dst->get_xfrm_addr(dst);
- policy_id->sel.prefixlen_d = dst_hostbits;
-
- policy_id->sel.proto = upper_proto;
- policy_id->sel.family = src->get_family(src);
+ this->logger->log(this->logger, CONTROL|LEVEL2, "deleting policy");
- policy_id->dir = direction;
-
- if (this->send_message(this, hdr, &response) != SUCCESS)
- {
- this->logger->log(this->logger, ERROR, "netlink communication failed");
- return FAILED;
- }
- else if (response->nlmsg_type == NLMSG_ERROR)
- {
- this->logger->log(this->logger, ERROR, "netlink request XFRM_MSG_GETPOLICY got an error: %s",
- strerror(-((struct nlmsgerr*)NLMSG_DATA(response))->error));
- free(response);
- return FAILED;
- }
- else if (response->nlmsg_type != XFRM_MSG_NEWPOLICY)
+ /* create a policy */
+ memset(&policy, 0, sizeof(kernel_policy_t));
+ policy.sel.saddr = src->get_xfrm_addr(src);
+ policy.sel.prefixlen_s = src_hostbits;
+ policy.sel.sport = htons(src->get_port(src));
+ policy.sel.sport_mask = (policy.sel.sport) ? ~0 : 0;
+ policy.sel.daddr = dst->get_xfrm_addr(dst);
+ policy.sel.prefixlen_d = dst_hostbits;
+ policy.sel.dport = htons(dst->get_port(dst));
+ policy.sel.dport_mask = (policy.sel.dport) ? ~0 : 0;
+ policy.sel.proto = upper_proto;
+ policy.sel.family = src->get_family(src);
+ policy.direction = direction;
+
+ /* find the policy */
+ pthread_mutex_lock(&this->pol_mutex);
+ iterator = this->policies->create_iterator(this->policies, TRUE);
+ while (iterator->iterate(iterator, (void**)&current))
{
- this->logger->log(this->logger, ERROR, "netlink request XFRM_MSG_GETPOLICY got an unknown reply");
- free(response);
- return FAILED;
+ if (memcmp(&current->sel, &policy.sel, sizeof(struct xfrm_selector)) == 0 &&
+ policy.direction == current->direction)
+ {
+ to_delete = current;
+ if (--to_delete->refcount > 0)
+ {
+ /* is used by more SAs, keep in kernel */
+ this->logger->log(this->logger, CONTROL|LEVEL2,
+ "is used by other SAs, not removed");
+ iterator->destroy(iterator);
+ pthread_mutex_unlock(&this->pol_mutex);
+ return SUCCESS;
+ }
+ /* remove if last reference */
+ iterator->remove(iterator);
+ break;
+ }
}
- else if (response->nlmsg_len < NLMSG_LENGTH(sizeof(struct xfrm_userpolicy_info)))
+ iterator->destroy(iterator);
+ pthread_mutex_unlock(&this->pol_mutex);
+ if (!to_delete)
{
- this->logger->log(this->logger, ERROR, "netlink request XFRM_MSG_GETPOLICY got an invalid reply");
- free(response);
- return FAILED;
+ this->logger->log(this->logger, CONTROL|LEVEL2,
+ "no such policy found");
+ return NOT_FOUND;
}
-
- struct xfrm_userpolicy_info *policy = (struct xfrm_userpolicy_info*)NLMSG_DATA(response);
-
- *use_time = (time_t)policy->curlft.use_time;
-
- free(response);
- return status;
-}
-
-/**
- * Implementation of kernel_interface_t.del_policy.
- */
-static status_t del_policy(private_kernel_interface_t *this,
- host_t *me, host_t *other,
- host_t *src, host_t *dst,
- u_int8_t src_hostbits, u_int8_t dst_hostbits,
- int direction, int upper_proto)
-{
- unsigned char request[BUFFER_SIZE];
- struct nlmsghdr *response;
memset(&request, 0, sizeof(request));
- status_t status = SUCCESS;
- this->logger->log(this->logger, CONTROL|LEVEL2, "deleting policy");
-
- struct nlmsghdr *hdr = (struct nlmsghdr*)request;
+ hdr = (struct nlmsghdr*)request;
hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
hdr->nlmsg_type = XFRM_MSG_DELPOLICY;
hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_userpolicy_id));
- struct xfrm_userpolicy_id *policy_id = (struct xfrm_userpolicy_id*)NLMSG_DATA(hdr);
- policy_id->sel.sport = htons(src->get_port(src));
- policy_id->sel.sport_mask = (policy_id->sel.sport) ? ~0 : 0;
- policy_id->sel.saddr = src->get_xfrm_addr(src);
- policy_id->sel.prefixlen_s = src_hostbits;
-
- policy_id->sel.dport = htons(dst->get_port(dst));
- policy_id->sel.dport_mask = (policy_id->sel.dport) ? ~0 : 0;
- policy_id->sel.daddr = dst->get_xfrm_addr(dst);
- policy_id->sel.prefixlen_d = dst_hostbits;
+ policy_id = (struct xfrm_userpolicy_id*)NLMSG_DATA(hdr);
+ policy_id->sel = to_delete->sel;
+ policy_id->dir = direction;
- policy_id->sel.proto = upper_proto;
- policy_id->sel.family = src->get_family(src);
+ free(to_delete);
- policy_id->dir = direction;
-
- if (this->send_message(this, hdr, &response) != SUCCESS)
+ if (send_message(this, hdr, &response) != SUCCESS)
{
this->logger->log(this->logger, ERROR, "netlink communication failed");
return FAILED;
@@ -806,172 +1080,6 @@ static status_t del_policy(private_kernel_interface_t *this,
}
/**
- * Implementation of private_kernel_interface_t.send_message.
- */
-static status_t send_message(private_kernel_interface_t *this, struct nlmsghdr *request, struct nlmsghdr **response)
-{
- size_t length;
- struct sockaddr_nl addr;
-
- request->nlmsg_seq = ++this->seq;
- request->nlmsg_pid = 0;
-
- memset(&addr, 0, sizeof(struct sockaddr_nl));
- addr.nl_family = AF_NETLINK;
- addr.nl_pid = 0;
- addr.nl_groups = 0;
-
- length = sendto(this->socket,(void *)request, request->nlmsg_len, 0, (struct sockaddr *)&addr, sizeof(addr));
-
- if (length < 0)
- {
- return FAILED;
- }
- else if (length != request->nlmsg_len)
- {
- return FAILED;
- }
-
- pthread_mutex_lock(&(this->mutex));
-
- while (TRUE)
- {
- iterator_t *iterator;
- bool found = FALSE;
- /* search list, break if found */
- iterator = this->responses->create_iterator(this->responses, TRUE);
- while (iterator->has_next(iterator))
- {
- struct nlmsghdr *listed_response;
- iterator->current(iterator, (void**)&listed_response);
- if (listed_response->nlmsg_seq == request->nlmsg_seq)
- {
- /* matches our request, this is the reply */
- *response = listed_response;
- iterator->remove(iterator);
- found = TRUE;
- break;
- }
- }
- iterator->destroy(iterator);
-
- if (found)
- {
- break;
- }
- /* TODO: we should time out, if something goes wrong!??? */
- pthread_cond_wait(&(this->condvar), &(this->mutex));
- }
-
- pthread_mutex_unlock(&(this->mutex));
-
- return SUCCESS;
-}
-
-/**
- * Implementation of private_kernel_interface_t.receive_messages.
- */
-static void receive_messages(private_kernel_interface_t *this)
-{
- while(TRUE)
- {
- unsigned char response[BUFFER_SIZE];
- struct nlmsghdr *hdr, *listed_response;
- while (TRUE)
- {
- struct sockaddr_nl addr;
- socklen_t addr_length;
- size_t length;
-
- addr_length = sizeof(addr);
-
- length = recvfrom(this->socket, &response, sizeof(response), 0, (struct sockaddr*)&addr, &addr_length);
- if (length < 0)
- {
- if (errno == EINTR)
- {
- /* interrupted, try again */
- continue;
- }
- charon->kill(charon, "receiving from netlink socket failed");
- }
- if (!NLMSG_OK((struct nlmsghdr *)response, length))
- {
- /* bad netlink message */
- continue;
- }
- if (addr.nl_pid != 0)
- {
- /* not from kernel. not interested, try another one */
- continue;
- }
- /* good message, handle it */
- break;
- }
-
- /* we handle ACQUIRE and EXPIRE messages directly
- */
- hdr = (struct nlmsghdr*)response;
- if (hdr->nlmsg_type == XFRM_MSG_ACQUIRE)
- {
- this->logger->log(this->logger, CONTROL,
- "Received a XFRM_MSG_ACQUIRE. Ignored");
- }
- else if (hdr->nlmsg_type == XFRM_MSG_EXPIRE)
- {
- job_t *job;
- protocol_id_t protocol;
- u_int32_t spi;
- struct xfrm_user_expire *expire;
-
- expire = (struct xfrm_user_expire*)NLMSG_DATA(hdr);
- protocol = expire->state.id.proto == KERNEL_ESP ?
- PROTO_ESP : PROTO_AH;
- spi = expire->state.id.spi;
-
- this->logger->log(this->logger, CONTROL|LEVEL1,
- "Received a XFRM_MSG_EXPIRE");
- this->logger->log(this->logger, CONTROL,
- "creating %s job for %s CHILD_SA 0x%x",
- expire->hard ? "delete" : "rekey",
- mapping_find(protocol_id_m, protocol), ntohl(spi));
- if (expire->hard)
- {
- job = (job_t*)delete_child_sa_job_create(protocol, spi);
- }
- else
- {
- job = (job_t*)rekey_child_sa_job_create(protocol, spi);
- }
- charon->job_queue->add(charon->job_queue, job);
- }
- /* NLMSG_ERROR is sent back for acknowledge (or on error), an
- * XFRM_MSG_NEWSA is returned when we alloc spis and when
- * updating SAs.
- * XFRM_MSG_NEWPOLICY is returned when we query a policy.
- * list these responses for the sender
- */
- else if (hdr->nlmsg_type == NLMSG_ERROR ||
- hdr->nlmsg_type == XFRM_MSG_NEWSA ||
- hdr->nlmsg_type == XFRM_MSG_NEWPOLICY)
- {
- /* add response to queue */
- listed_response = malloc(hdr->nlmsg_len);
- memcpy(listed_response, &response, hdr->nlmsg_len);
-
- pthread_mutex_lock(&(this->mutex));
- this->responses->insert_last(this->responses, (void*)listed_response);
- pthread_mutex_unlock(&(this->mutex));
- /* signal ALL waiting threads */
- pthread_cond_broadcast(&(this->condvar));
- }
- /* we are not interested in anything other.
- * anyway, move on to the next message */
- continue;
- }
-}
-
-/**
* Implementation of kernel_interface_t.destroy.
*/
static void destroy(private_kernel_interface_t *this)
@@ -980,6 +1088,7 @@ static void destroy(private_kernel_interface_t *this)
pthread_join(this->thread, NULL);
close(this->socket);
this->responses->destroy(this->responses);
+ this->policies->destroy(this->policies);
free(this);
}
@@ -995,19 +1104,19 @@ kernel_interface_t *kernel_interface_create()
this->public.get_spi = (status_t(*)(kernel_interface_t*,host_t*,host_t*,protocol_id_t,u_int32_t,u_int32_t*))get_spi;
this->public.add_sa = (status_t(*)(kernel_interface_t *,host_t*,host_t*,u_int32_t,protocol_id_t,u_int32_t,u_int64_t,u_int64_t,algorithm_t*,algorithm_t*,prf_plus_t*,natt_conf_t*,bool))add_sa;
this->public.update_sa_hosts = (status_t(*)(kernel_interface_t*,host_t*,host_t*,host_t*,host_t*,int,int,u_int32_t,protocol_id_t))update_sa_hosts;
+ this->public.query_sa = (status_t(*)(kernel_interface_t*,host_t*,u_int32_t,protocol_id_t,time_t*))query_sa;
this->public.del_sa = (status_t(*)(kernel_interface_t*,host_t*,u_int32_t,protocol_id_t))del_sa;
- this->public.add_policy = (status_t(*)(kernel_interface_t*,host_t*, host_t*,host_t*,host_t*,u_int8_t,u_int8_t,int,int,protocol_id_t,u_int32_t))add_policy;
- this->public.query_policy = (status_t(*)(kernel_interface_t*,host_t*,host_t*,host_t*,host_t*,u_int8_t,u_int8_t,int,int,time_t*))query_policy;
- this->public.del_policy = (status_t(*)(kernel_interface_t*,host_t*,host_t*,host_t*,host_t*,u_int8_t,u_int8_t,int,int))del_policy;
+ this->public.add_policy = (status_t(*)(kernel_interface_t*,host_t*,host_t*,host_t*,host_t*,u_int8_t,u_int8_t,u_int8_t,u_int8_t,protocol_id_t,u_int32_t,bool))add_policy;
+ this->public.del_policy = (status_t(*)(kernel_interface_t*,host_t*,host_t*,u_int8_t,u_int8_t,u_int8_t,u_int8_t))del_policy;
this->public.destroy = (void(*)(kernel_interface_t*)) destroy;
/* private members */
- this->receive_messages = receive_messages;
- this->send_message = send_message;
this->pid = getpid();
this->responses = linked_list_create();
+ this->policies = linked_list_create();
this->logger = logger_manager->get_logger(logger_manager, XFRM);
- pthread_mutex_init(&(this->mutex),NULL);
+ pthread_mutex_init(&(this->rep_mutex),NULL);
+ pthread_mutex_init(&(this->pol_mutex),NULL);
pthread_cond_init(&(this->condvar),NULL);
this->seq = 0;
@@ -1032,7 +1141,7 @@ kernel_interface_t *kernel_interface_create()
charon->kill(charon, "Unable to bind netlink socket");
}
- if (pthread_create(&(this->thread), NULL, (void*(*)(void*))this->receive_messages, this) != 0)
+ if (pthread_create(&this->thread, NULL, (void*(*)(void*))receive_messages, this) != 0)
{
this->responses->destroy(this->responses);
close(this->socket);
@@ -1040,5 +1149,5 @@ kernel_interface_t *kernel_interface_create()
charon->kill(charon, "Unable to create netlink thread");
}
- return (&this->public);
+ return &this->public;
}
diff --git a/src/charon/threads/kernel_interface.h b/src/charon/threads/kernel_interface.h
index bd63e5896..5d385caeb 100644
--- a/src/charon/threads/kernel_interface.h
+++ b/src/charon/threads/kernel_interface.h
@@ -48,6 +48,10 @@ typedef struct kernel_interface_t kernel_interface_t;
* The kernel interface handles the communication with the kernel
* for SA and policy management. It allows setup of these, and provides
* further the handling of kernel events.
+ * Policy information are cached in the interface. This is necessary to do
+ * reference counting. The Linux kernel does not allow the same policy
+ * installed twice, but we need this as CHILD_SA exist multiple times
+ * when rekeying. Thats why we do reference counting of policies.
*
* @b Constructors:
* - kernel_interface_create()
@@ -73,11 +77,8 @@ struct kernel_interface_t {
* - SUCCESS
* - FAILED if kernel comm failed
*/
- status_t (*get_spi) (kernel_interface_t *this,
- host_t *src, host_t *dst,
- protocol_id_t protocol,
- u_int32_t reqid,
- u_int32_t *spi);
+ status_t (*get_spi)(kernel_interface_t *this, host_t *src, host_t *dst,
+ protocol_id_t protocol, u_int32_t reqid, u_int32_t *spi);
/**
* @brief Add an SA to the SAD.
@@ -107,18 +108,12 @@ struct kernel_interface_t {
* - SUCCESS
* - FAILED if kernel comm failed
*/
- status_t (*add_sa)(kernel_interface_t *this,
- host_t *src, host_t *dst,
- u_int32_t spi,
- protocol_id_t protocol,
- u_int32_t reqid,
- u_int64_t expire_soft,
- u_int64_t expire_hard,
- algorithm_t *enc_alg,
- algorithm_t *int_alg,
- prf_plus_t *prf_plus,
- natt_conf_t *natt,
- bool replace);
+ status_t (*add_sa) (kernel_interface_t *this,
+ host_t *src, host_t *dst, u_int32_t spi,
+ protocol_id_t protocol, u_int32_t reqid,
+ u_int64_t expire_soft, u_int64_t expire_hard,
+ algorithm_t *enc_alg, algorithm_t *int_alg,
+ prf_plus_t *prf_plus, natt_conf_t *natt, bool replace);
/**
* @brief Update the hosts on an installed SA. Encapsulation ports are also updated.
@@ -127,40 +122,52 @@ struct kernel_interface_t {
* the protocol AND the destination address (and family) to identify SAs. Therefore if the
* destination address changed we create a new SA and delete the old one.
*
- * @param this calling object
- * @param src source address for this SA
- * @param dst destination address for this SA
- * @param new_src new source address for this SA
- * @param new_dst new destination address for this SA
+ * @param this calling object
+ * @param src source address for this SA
+ * @param dst destination address for this SA
+ * @param new_src new source address for this SA
+ * @param new_dst new destination address for this SA
* @param src_changes changes in src
* @param dst_changes changes in dst
- * @param spi SPI allocated by us or remote peer
- * @param protocol protocol for this SA (ESP/AH)
+ * @param spi SPI allocated by us or remote peer
+ * @param protocol protocol for this SA (ESP/AH)
* @return
- * - SUCCESS
- * - FAILED if kernel comm failed
+ * - SUCCESS
+ * - FAILED if kernel comm failed
*/
status_t (*update_sa_hosts)(kernel_interface_t *this,
host_t *src, host_t *dst,
host_t *new_src, host_t *new_dst,
int src_changes, int dst_changes,
u_int32_t spi, protocol_id_t protocol);
+ /**
+ * @brief Query the use time of an SA.
+ *
+ * @param this calling object
+ * @param dst destination address for this SA
+ * @param spi SPI allocated by us or remote peer
+ * @param protocol protocol for this SA (ESP/AH)
+ * @param[out] use_time the time of this SA's last use
+ * @return
+ * - SUCCESS
+ * - FAILED if kernel comm failed
+ */
+ status_t (*query_sa) (kernel_interface_t *this, host_t *dst, u_int32_t spi,
+ protocol_id_t protocol, time_t *use_time);
/**
* @brief Delete a previusly installed SA from the SAD.
*
- * @param this calling object
- * @param dst destination address for this SA
- * @param spi SPI allocated by us or remote peer
- * @param protocol protocol for this SA (ESP/AH)
+ * @param this calling object
+ * @param dst destination address for this SA
+ * @param spi SPI allocated by us or remote peer
+ * @param protocol protocol for this SA (ESP/AH)
* @return
- * - SUCCESS
- * - FAILED if kernel comm failed
+ * - SUCCESS
+ * - FAILED if kernel comm failed
*/
- status_t (*del_sa) (kernel_interface_t *this,
- host_t *dst,
- u_int32_t spi,
- protocol_id_t protocol);
+ status_t (*del_sa) (kernel_interface_t *this, host_t *dst, u_int32_t spi,
+ protocol_id_t protocol);
/**
* @brief Add a policy to the SPD.
@@ -181,47 +188,23 @@ struct kernel_interface_t {
* @param upper_proto upper layer protocol of traffic for this policy (TCP, UDP, ICMP, ...)
* @param protocol protocol to use to protect traffic (AH/ESP)
* @param reqid uniqe ID of an SA to use to enforce policy
+ * @param update update an existing policy, if TRUE
* @return
* - SUCCESS
* - FAILED if kernel comm failed
*/
status_t (*add_policy) (kernel_interface_t *this,
- host_t *me, host_t *other,
- host_t *src, host_t *dst,
- u_int8_t src_hostbits, u_int8_t dst_hostbits,
- int direction, int upper_proto,
- protocol_id_t protocol,
- u_int32_t reqid);
- /**
- * @brief Query the use time of a policy
- *
- * @param this calling object
- * @param me address of local peer
- * @param other address of remote peer
- * @param src src address of traffic this policy applies
- * @param dst dest address of traffic this policy applies
- * @param src_hostbits subnetmask to use for src address
- * @param dst_hostbits subnetmask to use for dst address
- * @param direction direction of traffic, XFRM_POLICY_OUT, XFRM_POLICY_IN, XFRM_POLICY_FWD
- * @param upper_proto upper layer protocol of traffic for this policy (TCP, UDP, ICMP, ...)
- * @param use_time the time of this policy's last use
- * @return
- * - SUCCESS
- * - FAILED if kernel comm failed
- */
- status_t (*query_policy) (kernel_interface_t *this,
- host_t *me, host_t *other,
- host_t *src, host_t *dst,
- u_int8_t src_hostbits, u_int8_t dst_hostbits,
- int direction, int upper_proto,
- time_t *use_time);
+ host_t *me, host_t *other,
+ host_t *src, host_t *dst,
+ u_int8_t src_hostbits, u_int8_t dst_hostbits,
+ u_int8_t direction, u_int8_t upper_proto,
+ protocol_id_t protocol,
+ u_int32_t reqid, bool update);
/**
* @brief Remove a policy from the SPD.
- *
+ *
* @param this calling object
- * @param me address of local peer
- * @param other address of remote peer
* @param src src address of traffic this policy applies
* @param dst dest address of traffic this policy applies
* @param src_hostbits subnetmask to use for src address
@@ -232,11 +215,10 @@ struct kernel_interface_t {
* - SUCCESS
* - FAILED if kernel comm failed
*/
- status_t (*del_policy) (kernel_interface_t *this,
- host_t *me, host_t *other,
- host_t *src, host_t *dst,
- u_int8_t src_hostbits, u_int8_t dst_hostbits,
- int direction, int upper_proto);
+ status_t (*del_policy) (kernel_interface_t *this,
+ host_t *src, host_t *dst,
+ u_int8_t src_hostbits, u_int8_t dst_hostbits,
+ u_int8_t direction, u_int8_t upper_proto);
/**
* @brief Destroys a kernel_interface object.