kernel-pfkey: Remove latest IPsec SA mapping when deleting a policy
authorTobias Brunner <tobias@strongswan.org>
Wed, 10 Jul 2013 09:08:01 +0000 (11:08 +0200)
committerTobias Brunner <tobias@strongswan.org>
Wed, 17 Jul 2013 15:45:17 +0000 (17:45 +0200)
If IPsec SAs are rekeyed due to an address change (e.g. because
update_sa is not supported) the exact same policy with the same reqid
will be installed, but with different addresses.  After the rekeying the
old SA and its policies are removed, using the first matching mapping
breaks the mapping between the policies and the new SA (at least on
FreeBSD, the Linux kernel might only use the reqid for this).  Using the
oldest matching SA is still an approximation but it solves the above
issue.

src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c

index dd9d354..86853e6 100644 (file)
@@ -2479,9 +2479,9 @@ METHOD(kernel_ipsec_t, del_policy, status_t,
        struct sadb_msg *msg, *out;
        struct sadb_x_policy *pol;
        policy_entry_t *policy, *found = NULL;
-       policy_sa_t *mapping;
+       policy_sa_t *mapping, *to_remove = NULL;
        enumerator_t *enumerator;
-       bool is_installed = TRUE;
+       bool first = TRUE, is_installed = TRUE;
        u_int32_t priority;
        size_t len;
 
@@ -2511,19 +2511,26 @@ METHOD(kernel_ipsec_t, del_policy, status_t,
        policy_entry_destroy(policy, this);
        policy = found;
 
-       /* remove mapping to SA by reqid and priority */
+       /* remove mapping to SA by reqid and priority, if multiple match, which
+        * could happen when rekeying due to an address change, remove the oldest */
        priority = get_priority(policy, prio);
        enumerator = policy->used_by->create_enumerator(policy->used_by);
        while (enumerator->enumerate(enumerator, (void**)&mapping))
        {
                if (reqid == mapping->sa->cfg.reqid && priority == mapping->priority)
                {
-                       policy->used_by->remove_at(policy->used_by, enumerator);
+                       to_remove = mapping;
+                       is_installed = first;
+               }
+               else if (priority < mapping->priority)
+               {
                        break;
                }
-               is_installed = FALSE;
+               first = FALSE;
        }
        enumerator->destroy(enumerator);
+       policy->used_by->remove(policy->used_by, to_remove, NULL);
+       mapping = to_remove;
 
        if (policy->used_by->get_count(policy->used_by) > 0)
        {       /* policy is used by more SAs, keep in kernel */