kernel-wfp: Triggering expire events for SAs to rekey/delete
[strongswan.git] / src / libcharon / plugins / kernel_wfp / kernel_wfp_ipsec.c
index 0b5f27e..e8ef277 100644 (file)
 #include "kernel_wfp_ipsec.h"
 
 #include <daemon.h>
+#include <hydra.h>
 #include <threading/mutex.h>
 #include <collections/array.h>
 #include <collections/hashtable.h>
+#include <processing/jobs/callback_job.h>
 
 
 typedef struct private_kernel_wfp_ipsec_t private_kernel_wfp_ipsec_t;
@@ -1012,6 +1014,103 @@ METHOD(kernel_ipsec_t, get_cpi, status_t,
        return NOT_SUPPORTED;
 }
 
+/**
+ * Data for an expire callback job
+ */
+typedef struct {
+       /* backref to kernel backend */
+       private_kernel_wfp_ipsec_t *this;
+       /* SPI of expiring SA */
+       u_int32_t spi;
+       /* destination address of expiring SA */
+       host_t *dst;
+       /* is this a hard expire, or a rekey request? */
+       bool hard;
+} expire_data_t;
+
+/**
+ * Clean up expire data
+ */
+static void expire_data_destroy(expire_data_t *data)
+{
+       data->dst->destroy(data->dst);
+       free(data);
+}
+
+/**
+ * Callback job for SA expiration
+ */
+static job_requeue_t expire_job(expire_data_t *data)
+{
+       private_kernel_wfp_ipsec_t *this = data->this;
+       u_int32_t reqid = 0;
+       u_int8_t protocol;
+       entry_t *entry;
+       sa_entry_t key = {
+               .spi = data->spi,
+               .dst = data->dst,
+       };
+
+       if (data->hard)
+       {
+               this->mutex->lock(this->mutex);
+               entry = this->isas->remove(this->isas, &key);
+               this->mutex->unlock(this->mutex);
+               if (entry)
+               {
+                       protocol = entry->isa.protocol;
+                       reqid = entry->reqid;
+                       if (entry->osa.dst)
+                       {
+                               key.dst = entry->osa.dst;
+                               key.spi = entry->osa.spi;
+                               this->osas->remove(this->osas, &key);
+                       }
+                       entry_destroy(this, entry);
+               }
+       }
+       else
+       {
+               this->mutex->lock(this->mutex);
+               entry = this->isas->get(this->isas, &key);
+               if (entry)
+               {
+                       protocol = entry->isa.protocol;
+                       reqid = entry->reqid;
+               }
+               this->mutex->unlock(this->mutex);
+       }
+
+       if (reqid)
+       {
+               hydra->kernel_interface->expire(hydra->kernel_interface,
+                                                                               reqid, protocol, data->spi, data->hard);
+       }
+
+       return JOB_REQUEUE_NONE;
+}
+
+/**
+ * Schedule an expire event for an SA
+ */
+static void schedule_expire(private_kernel_wfp_ipsec_t *this, u_int32_t spi,
+                                                       host_t *dst, u_int32_t lifetime, bool hard)
+{
+       expire_data_t *data;
+
+       INIT(data,
+               .this = this,
+               .spi = spi,
+               .dst = dst->clone(dst),
+               .hard = hard,
+       );
+
+       lib->scheduler->schedule_job(lib->scheduler, (job_t*)
+                                               callback_job_create((void*)expire_job, data,
+                                                                                       (void*)expire_data_destroy, NULL),
+                                               lifetime);
+}
+
 METHOD(kernel_ipsec_t, add_sa, status_t,
        private_kernel_wfp_ipsec_t *this, host_t *src, host_t *dst,
        u_int32_t spi, u_int8_t protocol, u_int32_t reqid, mark_t mark,
@@ -1052,6 +1151,15 @@ METHOD(kernel_ipsec_t, add_sa, status_t,
                        .encap = encap,
                );
 
+               if (lifetime->time.life)
+               {
+                       schedule_expire(this, spi, local, lifetime->time.life, TRUE);
+               }
+               if (lifetime->time.rekey && lifetime->time.rekey != lifetime->time.life)
+               {
+                       schedule_expire(this, spi, local, lifetime->time.rekey, FALSE);
+               }
+
                this->mutex->lock(this->mutex);
                this->tsas->put(this->tsas, (void*)(uintptr_t)reqid, entry);
                this->isas->put(this->isas, &entry->isa, entry);