[strongSwan-dev] [PATCH] Allow user to specify rekey policy for IPsec SA's

Sam Boyles Sam.Boyles at alliedtelesis.co.nz
Wed Jul 24 05:58:45 CEST 2019


Previously, IPsec SA and ISAKMP SA's would rekey indefinitely until one
peer goes down, a rekey fails, or one peer flushes their SA's. 

The purpose of this enhancement is to allow a user to select a policy
for the rekeying of CHILD_SA's (IPsec SA's). 

It gives the choice between:
 - always: always rekey CHILD_SA's (this is the default and current
behaviour)
 - never: never rekey CHILD_SA's
 - on-demand: only rekey CHILD_SA if the SA has been used since it's
last rekey (i.e bytes > 0 and packets > 0)

The reason for making these changes is to minimise redundant rekeying
of SA's when a link is not being used, using unneccesary resources. As
part of this change, the behaviour of ISAKMP SA's has been changed to
prevent an ISAKMP SA from rekeying when it has no associated children.

---
 src/libcharon/config/child_cfg.c              | 12 ++++++
 src/libcharon/config/child_cfg.h              | 22 ++++++++++
 .../processing/jobs/rekey_child_sa_job.c      | 40 +++++++++++++++++--
 src/libcharon/sa/ikev2/tasks/child_delete.c   | 25 +++++++++---
 src/libcharon/sa/ikev2/tasks/ike_rekey.c      | 13 +++++-
 5 files changed, 101 insertions(+), 11 deletions(-)

diff --git a/src/libcharon/config/child_cfg.c
b/src/libcharon/config/child_cfg.c
index 14148ed03..cacbe585c 100644
--- a/src/libcharon/config/child_cfg.c
+++ b/src/libcharon/config/child_cfg.c
@@ -162,6 +162,11 @@ struct private_child_cfg_t {
         * DS header field copy mode
         */
        dscp_copy_t copy_dscp;
+
+       /**
+        * Rekey policy for CHILD_SA
+        */
+       rekey_t rekey_policy;
 };
 
 METHOD(child_cfg_t, get_name, char*,
@@ -490,6 +495,11 @@ METHOD(child_cfg_t, get_mode, ipsec_mode_t,
        return this->mode;
 }
 
+METHOD (child_cfg_t, get_rekey_policy, rekey_t, private_child_cfg_t
*this)
+{
+       return this->rekey_policy;
+}
+
 METHOD(child_cfg_t, get_start_action, action_t,
        private_child_cfg_t *this)
 {
@@ -708,6 +718,7 @@ child_cfg_t *child_cfg_create(char *name,
child_cfg_create_t *data)
                        .destroy = _destroy,
                        .get_hw_offload = _get_hw_offload,
                        .get_copy_dscp = _get_copy_dscp,
+                       .get_rekey_policy = _get_rekey_policy,
                },
                .name = strdup(name),
                .options = data->options,
@@ -734,6 +745,7 @@ child_cfg_t *child_cfg_create(char *name,
child_cfg_create_t *data)
                                                        "%s.replay_wind
ow", DEFAULT_REPLAY_WINDOW, lib->ns),
                .hw_offload = data->hw_offload,
                .copy_dscp = data->copy_dscp,
+               .rekey_policy = data->rekey_policy,
        );
 
        return &this->public;
diff --git a/src/libcharon/config/child_cfg.h
b/src/libcharon/config/child_cfg.h
index e3b59e656..2f6f7a879 100644
--- a/src/libcharon/config/child_cfg.h
+++ b/src/libcharon/config/child_cfg.h
@@ -28,12 +28,25 @@ typedef enum action_t action_t;
 typedef enum child_cfg_option_t child_cfg_option_t;
 typedef struct child_cfg_t child_cfg_t;
 typedef struct child_cfg_create_t child_cfg_create_t;
+typedef enum rekey_t rekey_t;
 
 #include <library.h>
 #include <selectors/traffic_selector.h>
 #include <crypto/proposal/proposal.h>
 #include <kernel/kernel_ipsec.h>
 
+/**
+ * Policy to use when deciding whether or not to rekey a CHILD_SA
+ */
+enum rekey_t {
+       /** Always rekey this SA */
+       REKEY_ALWAYS,
+       /** Never rekey this SA */
+       REKEY_NEVER,
+       /** Rekey this SA only if it has been used since last rekey */
+       REKEY_ON_DEMAND,
+};
+
 /**
  * Action to take when connection is loaded, DPD is detected or
  * connection gets closed by peer.
@@ -277,6 +290,13 @@ struct child_cfg_t {
         */
        void (*set_replay_window)(child_cfg_t *this, uint32_t window);
 
+       /**
+        * Get the rekey policy of the CHILD_SA
+        *
+        * @return              rekey policy of SA
+        */
+       rekey_t (*get_rekey_policy)(child_cfg_t *this);
+
        /**
         * Check if an option flag is set.
         *
@@ -382,6 +402,8 @@ struct child_cfg_create_t {
        hw_offload_t hw_offload;
        /** How to handle the DS header field in tunnel mode */
        dscp_copy_t copy_dscp;
+       /** The rekeying policy for the CHILD_SA */
+       rekey_t rekey_policy;
 };
 
 /**
diff --git a/src/libcharon/processing/jobs/rekey_child_sa_job.c
b/src/libcharon/processing/jobs/rekey_child_sa_job.c
index aa21717fa..5dca98d19 100644
--- a/src/libcharon/processing/jobs/rekey_child_sa_job.c
+++ b/src/libcharon/processing/jobs/rekey_child_sa_job.c
@@ -57,9 +57,15 @@ METHOD(job_t, execute, job_requeue_t,
        private_rekey_child_sa_job_t *this)
 {
        ike_sa_t *ike_sa;
+       child_cfg_t *old_config;
+       child_sa_t *old_child_sa;
+       uint64_t packets, bytes;
+       rekey_t rekey_policy;
+       int rekey_sa = true;
 
        ike_sa = charon->child_sa_manager->checkout(charon-
>child_sa_manager,
-                                                                      
 this->protocol, this->spi, this->dst, NULL);
+                                                   this->protocol,
this->spi, this->dst, &old_child_sa);
+
        if (ike_sa == NULL)
        {
                DBG1(DBG_JOB, "CHILD_SA %N/0x%08x/%H not found for
rekey",
@@ -69,10 +75,38 @@ METHOD(job_t, execute, job_requeue_t,
        {
                if (ike_sa->get_state(ike_sa) != IKE_PASSIVE)
                {
-                       ike_sa->rekey_child_sa(ike_sa, this->protocol,
this->spi);
+                       old_child_sa->get_usestats(old_child_sa, false,
NULL, &bytes, &packets);
+                       old_config = old_child_sa-
>get_config(old_child_sa);
+                       rekey_policy = old_config-
>get_rekey_policy(old_config);
+
+                       if (rekey_policy == REKEY_ON_DEMAND)
+                       {
+                               if (bytes == 0 && packets == 0)
+                               {
+                                       rekey_sa = false;
+                               }
+                       }
+                       else if (rekey_policy == REKEY_NEVER)
+                       {
+                               rekey_sa = false;
+                       }
+                       else
+                       {
+                               rekey_sa = true;
+                       }
+
+                       if (!rekey_sa)
+                       {
+                               DBG1 (DBG_JOB, "CHILD_SA not rekeying
due to rekey policy");
+                       }
+                       else
+                       {
+                               DBG1 (DBG_JOB, "CHILD_SA is rekeying
now");
+                               ike_sa->rekey_child_sa(ike_sa, this-
>protocol, this->spi);
+                       }
                }
-               charon->ike_sa_manager->checkin(charon->ike_sa_manager, 
ike_sa);
        }
+       charon->ike_sa_manager->checkin(charon->ike_sa_manager,
ike_sa);
        return JOB_REQUEUE_NONE;
 }
 
diff --git a/src/libcharon/sa/ikev2/tasks/child_delete.c
b/src/libcharon/sa/ikev2/tasks/child_delete.c
index 0e3711898..5712dbb9c 100644
--- a/src/libcharon/sa/ikev2/tasks/child_delete.c
+++ b/src/libcharon/sa/ikev2/tasks/child_delete.c
@@ -445,6 +445,9 @@ METHOD(task_t, build_i, status_t,
 {
        child_sa_t *child_sa;
        entry_t *entry;
+       uint64_t bytes, packets;
+       child_cfg_t *child_cfg;
+       rekey_t policy;
 
        child_sa = this->ike_sa->get_child_sa(this->ike_sa, this-
>protocol,
                                                                       
           this->spi, TRUE);
@@ -489,13 +492,23 @@ METHOD(task_t, build_i, status_t,
 
        if (!entry->rekeyed && this->expired)
        {
-               child_cfg_t *child_cfg;
-
-               DBG1(DBG_IKE, "scheduling CHILD_SA recreate after hard
expire");
                child_cfg = child_sa->get_config(child_sa);
-               this->ike_sa->queue_task(this->ike_sa, (task_t*)
-                               child_create_create(this->ike_sa,
child_cfg->get_ref(child_cfg),
-                                                                      
 FALSE, NULL, NULL));
+               policy = child_cfg->get_rekey_policy(child_cfg);
+               child_sa->get_usestats(child_sa, false, NULL, &bytes,
&packets);
+               /* If rekey policy is always, or on-demand and the SA
has seen traffic */
+               if (policy == REKEY_ALWAYS ||
+                   (policy == REKEY_ON_DEMAND && (bytes != 0 ||
packets != 0)))
+               {
+                       DBG1(DBG_IKE, "scheduling CHILD_SA recreate
after hard expire");
+                       this->ike_sa->queue_task(this->ike_sa, (task_t
*)
+                                                               child_c
reate_create(this->ike_sa,
+                                                               child_c
fg->get_ref(child_cfg),
+                                                               FALSE,
NULL, NULL));
+               }
+               else
+               {
+                       DBG1(DBG_IKE, "Not scheduling CHILD_SA recreate
after hard expire");
+               }
        }
        return NEED_MORE;
 }
diff --git a/src/libcharon/sa/ikev2/tasks/ike_rekey.c
b/src/libcharon/sa/ikev2/tasks/ike_rekey.c
index b281d81da..f7a2a827a 100644
--- a/src/libcharon/sa/ikev2/tasks/ike_rekey.c
+++ b/src/libcharon/sa/ikev2/tasks/ike_rekey.c
@@ -183,9 +183,18 @@ METHOD(task_t, build_i, status_t,
                this->ike_init = ike_init_create(this->new_sa, TRUE,
this->ike_sa);
                this->ike_sa->set_state(this->ike_sa, IKE_REKEYING);
        }
-       this->ike_init->task.build(&this->ike_init->task, message);
 
-       return NEED_MORE;
+       /* if there are no children, destroy the old SA */
+       if (this->ike_sa->get_child_count(this->ike_sa) == 0)
+       {
+               DBG1(DBG_IKE, "no CHILD_SA found, not rekeying
IKE_SA");
+               return SUCCESS;
+       }
+       else
+       {
+               this->ike_init->task.build(&this->ike_init->task,
message);
+               return NEED_MORE;
+       }
 }
 
 /**
-- 
2.22.0


More information about the Dev mailing list