added policy cache to kernel interface
authorMartin Willi <martin@strongswan.org>
Wed, 12 Jul 2006 11:42:36 +0000 (11:42 -0000)
committerMartin Willi <martin@strongswan.org>
Wed, 12 Jul 2006 11:42:36 +0000 (11:42 -0000)
allows refcounting of multiple installed policies
finally brings us stable simultaneous rekeying

src/charon/encoding/message.c
src/charon/sa/child_sa.c
src/charon/sa/child_sa.h
src/charon/sa/transactions/create_child_sa.c
src/charon/sa/transactions/delete_child_sa.c
src/charon/threads/kernel_interface.c
src/charon/threads/kernel_interface.h

index 8f2dadf..d66a4cd 100644 (file)
@@ -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},
index 0331141..4ff8b94 100644 (file)
@@ -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;
 }
index fba0c73..f373c56 100644 (file)
@@ -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
index 30ccdef..8f291d5 100644 (file)
@@ -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;
        }
index 621d2ab..6ea25e2 100644 (file)
@@ -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 */
index 0f4a473..c4966f4 100644 (file)
 #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;
 }
index bd63e58..5d385ca 100644 (file)
@@ -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.