]> git.saurik.com Git - apple/ipsec.git/blobdiff - ipsec-tools/racoon/handler.c
ipsec-146.1.tar.gz
[apple/ipsec.git] / ipsec-tools / racoon / handler.c
index 4082bb57054043e6591a609220f88345c1d7f483..f8bed5f8d110b6839c2b302af4fe9cd502d8194d 100644 (file)
@@ -1,4 +1,6 @@
-/* $Id: handler.c,v 1.13.4.4 2005/07/14 12:00:36 vanhu Exp $ */
+/*     $NetBSD: handler.c,v 1.9.6.6 2007/06/06 09:20:12 vanhu Exp $    */
+
+/* Id: handler.c,v 1.28 2006/05/26 12:17:29 manubsd Exp */
 
 /*
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
 #include "sockmisc.h"
 #include "debug.h"
 
+#ifdef ENABLE_HYBRID
+#include <resolv.h>
+#endif
+
 #include "schedule.h"
 #include "grabmyaddr.h"
 #include "algorithm.h"
 #include "handler.h"
 #include "gcmalloc.h"
 #include "nattraversal.h"
+#include "ike_session.h"
+
+#include "sainfo.h"
 
 #ifdef HAVE_GSSAPI
 #include "gssapi.h"
 #endif
+#include "power_mgmt.h"
 
 static LIST_HEAD(_ph1tree_, ph1handle) ph1tree;
 static LIST_HEAD(_ph2tree_, ph2handle) ph2tree;
@@ -138,14 +148,24 @@ getph1byaddr(local, remote)
 {
        struct ph1handle *p;
 
+       plog(LLV_DEBUG2, LOCATION, NULL, "getph1byaddr: start\n");
+       plog(LLV_DEBUG2, LOCATION, NULL, "local: %s\n", saddr2str(local));
+       plog(LLV_DEBUG2, LOCATION, NULL, "remote: %s\n", saddr2str(remote));
+
        LIST_FOREACH(p, &ph1tree, chain) {
                if (p->status == PHASE1ST_EXPIRED)
                        continue;
+               plog(LLV_DEBUG2, LOCATION, NULL, "p->local: %s\n", saddr2str(p->local));
+               plog(LLV_DEBUG2, LOCATION, NULL, "p->remote: %s\n", saddr2str(p->remote));
                if (CMPSADDR(local, p->local) == 0
-                && CMPSADDR(remote, p->remote) == 0)
+                       && CMPSADDR(remote, p->remote) == 0){
+                       plog(LLV_DEBUG2, LOCATION, NULL, "matched\n");
                        return p;
+               }
        }
 
+       plog(LLV_DEBUG2, LOCATION, NULL, "no match\n");
+
        return NULL;
 }
 
@@ -187,6 +207,24 @@ getph1bydstaddrwop(remote)
        return NULL;
 }
 
+int
+islast_ph1(ph1) 
+       struct ph1handle *ph1;
+{
+       struct ph1handle *p;
+
+       LIST_FOREACH(p, &ph1tree, chain) {
+               if (p->is_dying || p->status == PHASE1ST_EXPIRED)
+                       continue;
+               if (CMPSADDR(ph1->remote, p->remote) == 0) {
+                       if (p == ph1)
+                               continue;
+                       return 0;
+               }
+       }
+       return 1;
+}
+
 /*
  * dump isakmp-sa
  */
@@ -246,9 +284,13 @@ newph1()
        iph1->dpd_lastack = 0;
        iph1->dpd_seq = 0;
        iph1->dpd_fails = 0;
+    iph1->peer_sent_ike = 0;
        iph1->dpd_r_u = NULL;
 #endif
-
+#ifdef ENABLE_VPNCONTROL_PORT
+       iph1->ping_sched = NULL;
+#endif
+       iph1->is_dying = 0;
        return iph1;
 }
 
@@ -259,27 +301,35 @@ void
 delph1(iph1)
        struct ph1handle *iph1;
 {
+       if (iph1 == NULL)
+               return;
+
        /* SA down shell script hook */
-       if (iph1 != NULL)
-               script_hook(iph1, SCRIPT_PHASE1_DOWN);
+       script_hook(iph1, SCRIPT_PHASE1_DOWN);
 
        EVT_PUSH(iph1->local, iph1->remote, EVTT_PHASE1_DOWN, NULL);
 
 #ifdef ENABLE_NATT
-#ifndef __APPLE__
-       if (iph1->natt_flags & NAT_KA_QUEUED)
-               natt_keepalive_remove (iph1->local, iph1->remote);
-#endif /* __APPLE__ */
        if (iph1->natt_options) {
                racoon_free(iph1->natt_options);
                iph1->natt_options = NULL;
        }
 #endif
 
+#ifdef ENABLE_HYBRID
+       if (iph1->mode_cfg)
+               isakmp_cfg_rmstate(iph1);
+       VPTRINIT(iph1->xauth_awaiting_userinput_msg);
+#endif
+
 #ifdef ENABLE_DPD
        if (iph1->dpd_r_u != NULL)
                SCHED_KILL(iph1->dpd_r_u);
 #endif
+#ifdef ENABLE_VPNCONTROL_PORT
+       if (iph1->ping_sched != NULL)
+               SCHED_KILL(iph1->ping_sched);
+#endif
 
        if (iph1->remote) {
                racoon_free(iph1->remote);
@@ -295,15 +345,11 @@ delph1(iph1)
                iph1->approval = NULL;
        }
 
-#ifdef ENABLE_HYBRID
-       if (iph1->mode_cfg)
-               isakmp_cfg_rmstate(iph1);
-#endif
-
        VPTRINIT(iph1->authstr);
 
        sched_scrub_param(iph1);
        iph1->sce = NULL;
+       iph1->sce_rekey = NULL;
        iph1->scr = NULL;
 
        VPTRINIT(iph1->sendbuf);
@@ -333,6 +379,9 @@ delph1(iph1)
        VPTRINIT(iph1->id);
        VPTRINIT(iph1->id_p);
 
+       if(iph1->approval != NULL)
+               delisakmpsa(iph1->approval);
+
        if (iph1->ivm) {
                oakley_delivm(iph1->ivm);
                iph1->ivm = NULL;
@@ -348,6 +397,14 @@ delph1(iph1)
        gssapi_free_state(iph1);
 #endif
 
+       if (iph1->parent_session) {
+               ike_session_unlink_ph1_from_session(iph1);
+       }
+       if (iph1->rmconf) {
+               unlink_rmconf_from_ph1(iph1->rmconf);
+               iph1->rmconf = NULL;
+       }
+       
        racoon_free(iph1);
 }
 
@@ -380,17 +437,41 @@ remph1(iph1)
  * flush isakmp-sa
  */
 void
-flushph1()
+flushph1(int ignore_estab_or_assert_handles)
 {
        struct ph1handle *p, *next;
+       
+       plog(LLV_DEBUG2, LOCATION, NULL,
+                "flushing ph1 handles: ignore_estab_or_assert %d...\n", ignore_estab_or_assert_handles);
 
        for (p = LIST_FIRST(&ph1tree); p; p = next) {
                next = LIST_NEXT(p, chain);
 
+               if (ignore_estab_or_assert_handles && p->parent_session && !p->parent_session->stopped_by_vpn_controller && p->parent_session->is_asserted) {
+                       plog(LLV_DEBUG2, LOCATION, NULL,
+                                "skipping phase1 %s that's asserted...\n",
+                                isakmp_pindex(&p->index, 0));
+                       continue;
+               }
+
                /* send delete information */
-               if (p->status == PHASE1ST_ESTABLISHED) 
+               if (p->status == PHASE1ST_ESTABLISHED) {
+                       if (ignore_estab_or_assert_handles &&
+                           ike_session_has_negoing_ph2(p->parent_session)) {
+                               plog(LLV_DEBUG2, LOCATION, NULL,
+                                        "skipping phase1 %s that's established... because it's needed by children phase2s\n",
+                                        isakmp_pindex(&p->index, 0));
+                           continue;
+                   }
+                       /* send delete information */
+                       plog(LLV_DEBUG2, LOCATION, NULL,
+                                "got a phase1 %s to flush...\n",
+                                isakmp_pindex(&p->index, 0));
                        isakmp_info_send_d1(p);
+               }
 
+               ike_session_stopped_by_controller(p->parent_session,
+                                                                                 ike_session_stopped_by_flush);
                remph1(p);
                delph1(p);
        }
@@ -469,8 +550,21 @@ getph2byid(src, dst, spid)
        LIST_FOREACH(p, &ph2tree, chain) {
                if (spid == p->spid &&
                    CMPSADDR(src, p->src) == 0 &&
-                   CMPSADDR(dst, p->dst) == 0)
-                       return p;
+                   CMPSADDR(dst, p->dst) == 0){
+                       /* Sanity check to detect zombie handlers
+                        * XXX Sould be done "somewhere" more interesting,
+                        * because we have lots of getph2byxxxx(), but this one
+                        * is called by pk_recvacquire(), so is the most important.
+                        */
+                       if(p->status < PHASE2ST_ESTABLISHED &&
+                          p->retry_counter == 0
+                          && p->sce == NULL && p->scr == NULL){
+                               plog(LLV_DEBUG, LOCATION, NULL,
+                                        "Zombie ph2 found, expiring it\n");
+                               isakmp_ph2expire(p);
+                       }else
+                               return p;
+               }
        }
 
        return NULL;
@@ -542,6 +636,7 @@ newph2()
                return NULL;
 
        iph2->status = PHASE1ST_SPAWN;
+       iph2->is_dying = 0;
 
        return iph2;
 }
@@ -635,6 +730,22 @@ delph2(iph2)
                iph2->proposal = NULL;
        }
 
+       if (iph2->parent_session) {
+               ike_session_unlink_ph2_from_session(iph2);
+       }
+       if (iph2->sainfo) {
+               unlink_sainfo_from_ph2(iph2->sainfo);
+               iph2->sainfo = NULL;
+       }
+       if (iph2->ext_nat_id) {
+               vfree(iph2->ext_nat_id);
+               iph2->ext_nat_id = NULL;
+       }
+       if (iph2->ext_nat_id_p) {
+               vfree(iph2->ext_nat_id_p);
+               iph2->ext_nat_id_p = NULL;
+       }
+
        racoon_free(iph2);
 }
 
@@ -664,17 +775,40 @@ initph2tree()
 }
 
 void
-flushph2()
+flushph2(int ignore_estab_or_assert_handles)
 {
        struct ph2handle *p, *next;
 
+       plog(LLV_DEBUG2, LOCATION, NULL,
+                "flushing ph2 handles: ignore_estab_or_assert %d...\n", ignore_estab_or_assert_handles);
+
        for (p = LIST_FIRST(&ph2tree); p; p = next) {
                next = LIST_NEXT(p, chain);
-
-               /* send delete information */
-               if (p->status == PHASE2ST_ESTABLISHED) 
+               if (p->is_dying || p->status == PHASE2ST_EXPIRED) {
+                       continue;
+               }
+               if (ignore_estab_or_assert_handles && p->parent_session && !p->parent_session->stopped_by_vpn_controller && p->parent_session->is_asserted) {
+                       plog(LLV_DEBUG2, LOCATION, NULL,
+                                "skipping phase2 handle that's asserted...\n");
+                       continue;
+               }
+               if (p->status == PHASE2ST_ESTABLISHED){
+                       if (ignore_estab_or_assert_handles) {
+                               plog(LLV_DEBUG2, LOCATION, NULL,
+                                        "skipping ph2 handler that's established...\n");
+                           continue;
+                   }
+                       /* send delete information */
+                       plog(LLV_DEBUG2, LOCATION, NULL,
+                                "got an established ph2 handler to flush...\n");
                        isakmp_info_send_d2(p);
-
+               }else{
+                       plog(LLV_DEBUG2, LOCATION, NULL,
+                                "got a ph2 handler to flush (state %d)\n", p->status);
+               }
+               
+               ike_session_stopped_by_controller(p->parent_session,
+                                                                                 ike_session_stopped_by_flush);
                delete_spd(p);
                unbindph12(p);
                remph2(p);
@@ -697,9 +831,16 @@ deleteallph2(src, dst, proto_id)
 
        for (iph2 = LIST_FIRST(&ph2tree); iph2 != NULL; iph2 = next) {
                next = LIST_NEXT(iph2, chain);
+               if (iph2->is_dying || iph2->status == PHASE2ST_EXPIRED) {
+                       continue;
+               }
                if (iph2->proposal == NULL && iph2->approval == NULL)
                        continue;
-               if (iph2->approval != NULL) {
+               if (cmpsaddrwop(src, iph2->src) != 0 ||
+                   cmpsaddrwop(dst, iph2->dst) != 0) {
+            continue;
+        }
+        if (iph2->approval != NULL) {
                        for (pr = iph2->approval->head; pr != NULL;
                             pr = pr->next) {
                                if (proto_id == pr->proto_id)
@@ -714,18 +855,54 @@ deleteallph2(src, dst, proto_id)
                }
                continue;
  zap_it:
+        plog(LLV_DEBUG2, LOCATION, NULL,
+             "deleteallph2: got a ph2 handler...\n");
+        if (iph2->status == PHASE2ST_ESTABLISHED)
+            isakmp_info_send_d2(iph2);
+        ike_session_stopped_by_controller(iph2->parent_session,
+                                          ike_session_stopped_by_flush);
                unbindph12(iph2);
                remph2(iph2);
                delph2(iph2);
        }
 }
 
+/*
+ * Delete all Phase 1 handlers for this src/dst.
+ */
+void
+deleteallph1(src, dst)
+struct sockaddr *src, *dst;
+{
+       struct ph1handle *iph1, *next;
+
+       for (iph1 = LIST_FIRST(&ph1tree); iph1 != NULL; iph1 = next) {
+               next = LIST_NEXT(iph1, chain);
+               if (cmpsaddrwop(src, iph1->local) != 0 ||
+                   cmpsaddrwop(dst, iph1->remote) != 0) {
+                       continue;
+        }
+        plog(LLV_DEBUG2, LOCATION, NULL,
+             "deleteallph1: got a ph1 handler...\n");
+        if (iph1->status == PHASE2ST_ESTABLISHED)
+               isakmp_info_send_d1(iph1);
+
+               ike_session_stopped_by_controller(iph1->parent_session,
+                                                 ike_session_stopped_by_flush);
+               remph1(iph1);
+               delph1(iph1);
+       }
+}
+
 /* %%% */
 void
 bindph12(iph1, iph2)
        struct ph1handle *iph1;
        struct ph2handle *iph2;
 {
+       if (iph2->ph1 && (struct ph1handle *)iph2->ph1bind.le_next == iph1) {
+               plog(LLV_ERROR, LOCATION, NULL, "duplicate %s.\n", __FUNCTION__);               
+       }
        iph2->ph1 = iph1;
        LIST_INSERT_HEAD(&iph1->ph2tree, iph2, ph1bind);
 }
@@ -735,11 +912,37 @@ unbindph12(iph2)
        struct ph2handle *iph2;
 {
        if (iph2->ph1 != NULL) {
+               plog(LLV_DEBUG, LOCATION, NULL, "unbindph12.\n");
                iph2->ph1 = NULL;
                LIST_REMOVE(iph2, ph1bind);
        }
 }
 
+void
+rebindph12(new_ph1, iph2)
+struct ph1handle *new_ph1;
+struct ph2handle *iph2;
+{
+       if (!new_ph1) {
+               return;
+       }
+
+       // reconcile the ph1-to-ph2 binding
+       plog(LLV_DEBUG, LOCATION, NULL, "rebindph12.\n");
+       unbindph12(iph2);
+       bindph12(new_ph1, iph2);
+       // recalculate ivm since ph1 binding has changed
+       if (iph2->ivm != NULL) {
+               oakley_delivm(iph2->ivm);
+               if (new_ph1->status == PHASE1ST_ESTABLISHED) {
+                       iph2->ivm = oakley_newiv2(new_ph1, iph2->msgid);
+                       plog(LLV_DEBUG, LOCATION, NULL, "ph12 binding changed... recalculated ivm.\n");
+               } else {
+                       iph2->ivm = NULL;
+               }
+       }
+}
+
 /* %%% management contacted list */
 /*
  * search contacted list.
@@ -773,6 +976,12 @@ inscontacted(remote)
                return -1;
 
        new->remote = dupsaddr(remote);
+       if (new->remote == NULL) {
+               plog(LLV_ERROR, LOCATION, NULL,
+                       "failed to allocate buffer.\n");
+               racoon_free(new);
+               return -1;
+       }
 
        LIST_INSERT_HEAD(&ctdtree, new, chain);
 
@@ -799,6 +1008,17 @@ initctdtree()
        LIST_INIT(&ctdtree);
 }
 
+time_t
+get_exp_retx_interval (int num_retries, int fixed_retry_interval)
+{
+       // first 3 retries aren't exponential
+       if (num_retries <= 3) {
+               return (time_t)fixed_retry_interval;
+       } else {
+               return (time_t)(num_retries * fixed_retry_interval);
+       }
+}
+
 /*
  * check the response has been sent to the peer.  when not, simply reply
  * the buffered packet to the peer.
@@ -815,7 +1035,7 @@ check_recvdpkt(remote, local, rbuf)
 {
        vchar_t *hash;
        struct recvdpkt *r;
-       time_t t;
+       time_t t, d;
        int len, s;
 
        /* set current time */
@@ -840,9 +1060,11 @@ check_recvdpkt(remote, local, rbuf)
 
        /*
         * the packet was processed before, but the remote address mismatches.
+         * ignore the port to accomodate port changes (e.g. floating).
         */
-       if (cmpsaddrstrict(remote, r->remote) != 0)
-               return 2;
+       if (cmpsaddrwop(remote, r->remote) != 0) {
+               return 2;
+        }
 
        /*
         * it should not check the local address because the packet
@@ -862,9 +1084,33 @@ check_recvdpkt(remote, local, rbuf)
        if (s == -1)
                return -1;
 
+       // don't send if we recently sent a response.
+       if (r->time_send && t > r->time_send) {
+               d = t - r->time_send;
+               if (d  < r->retry_interval) {
+                       plog(LLV_ERROR, LOCATION, NULL, "already responded within the past %ld secs\n", d);
+                       return 1;
+               }
+       }
+
+#ifdef ENABLE_FRAG
+       if (r->frag_flags && r->sendbuf->l > ISAKMP_FRAG_MAXLEN) {
+               /* resend the packet if needed */
+               plog(LLV_ERROR, LOCATION, NULL, "!!! retransmitting frags\n");
+               len = sendfragsfromto(s, r->sendbuf,
+                                                         r->local, r->remote, lcconf->count_persend,
+                                                         r->frag_flags);
+       } else {
+               plog(LLV_ERROR, LOCATION, NULL, "!!! skipped retransmitting frags: frag_flags %x, r->sendbuf->l %d, max %d\n", r->frag_flags, r->sendbuf->l, ISAKMP_FRAG_MAXLEN);
+               /* resend the packet if needed */
+               len = sendfromto(s, r->sendbuf->v, r->sendbuf->l,
+                                                r->local, r->remote, lcconf->count_persend);
+       }
+#else
        /* resend the packet if needed */
        len = sendfromto(s, r->sendbuf->v, r->sendbuf->l,
                        r->local, r->remote, lcconf->count_persend);
+#endif
        if (len == -1) {
                plog(LLV_ERROR, LOCATION, NULL, "sendfromto failed\n");
                return -1;
@@ -878,8 +1124,11 @@ check_recvdpkt(remote, local, rbuf)
                plog(LLV_DEBUG, LOCATION, NULL,
                        "deleted the retransmission packet to %s.\n",
                        saddr2str(remote));
-       } else
+       } else {
                r->time_send = t;
+               r->retry_interval = get_exp_retx_interval((lcconf->retry_counter - r->retry_counter),
+                                                                                                 lcconf->retry_interval);
+       }
 
        return 1;
 }
@@ -888,9 +1137,11 @@ check_recvdpkt(remote, local, rbuf)
  * adding a hash of received packet into the received list.
  */
 int
-add_recvdpkt(remote, local, sbuf, rbuf)
+add_recvdpkt(remote, local, sbuf, rbuf, non_esp, frag_flags)
        struct sockaddr *remote, *local;
        vchar_t *sbuf, *rbuf;
+    size_t non_esp;
+    u_int32_t frag_flags;
 {
        struct recvdpkt *new = NULL;
 
@@ -927,17 +1178,41 @@ add_recvdpkt(remote, local, sbuf, rbuf)
                del_recvdpkt(new);
                return -1;
        }
-       new->sendbuf = vdup(sbuf);
-       if (new->sendbuf == NULL) {
-               plog(LLV_ERROR, LOCATION, NULL,
-                       "failed to allocate buffer.\n");
-               del_recvdpkt(new);
-               return -1;
-       }
+
+       if (non_esp) {
+               plog (LLV_DEBUG, LOCATION, NULL, "Adding NON-ESP marker\n");
+
+        /* If NAT-T port floating is in use, 4 zero bytes (non-ESP marker) 
+         must added just before the packet itself. For this we must 
+         allocate a new buffer and release it at the end. */
+        if ((new->sendbuf = vmalloc (sbuf->l + non_esp)) == NULL) {
+            plog(LLV_ERROR, LOCATION, NULL, 
+                 "failed to allocate extra buf for non-esp\n");
+            del_recvdpkt(new);
+            return -1;
+        }
+        *(u_int32_t *)new->sendbuf->v = 0;
+        memcpy(new->sendbuf->v + non_esp, sbuf->v, sbuf->l);
+    } else {
+        new->sendbuf = vdup(sbuf);
+        if (new->sendbuf == NULL) {
+            plog(LLV_ERROR, LOCATION, NULL,
+                 "failed to allocate buffer.\n");
+            del_recvdpkt(new);
+            return -1;
+        }
+    }
 
        new->retry_counter = lcconf->retry_counter;
        new->time_send = 0;
        new->created = time(NULL);
+#ifdef ENABLE_FRAG
+       if (frag_flags) {
+               new->frag_flags = frag_flags;
+       }
+#endif
+       new->retry_interval = get_exp_retx_interval((lcconf->retry_counter - new->retry_counter),
+                                                                                               lcconf->retry_interval);
 
        LIST_INSERT_HEAD(&rcptree, new, chain);
 
@@ -988,7 +1263,7 @@ sweep_recvdpkt(dummy)
                }
        }
 
-       sched_new(lt, sweep_recvdpkt, NULL);
+       sched_new(lt, sweep_recvdpkt, &rcptree);
 }
 
 void
@@ -1001,6 +1276,7 @@ clear_recvdpkt()
                rem_recvdpkt(r);
                del_recvdpkt(r);
        }
+       sched_scrub_param(&rcptree);
 }
 
 void
@@ -1010,7 +1286,7 @@ init_recvdpkt()
 
        LIST_INIT(&rcptree);
 
-       sched_new(lt, sweep_recvdpkt, NULL);
+       sched_new(lt, sweep_recvdpkt, &rcptree);
 }
 
 #ifdef ENABLE_HYBRID
@@ -1038,3 +1314,234 @@ exclude_cfg_addr(addr)
        return 1;
 }
 #endif
+
+#ifdef ENABLE_HYBRID
+struct ph1handle *
+getph1bylogin(login)
+       char *login;
+{
+       struct ph1handle *p;
+
+       LIST_FOREACH(p, &ph1tree, chain) {
+               if (p->mode_cfg == NULL)
+                       continue;
+               if (strncmp(p->mode_cfg->login, login, LOGINLEN) == 0)
+                       return p;
+       }
+
+       return NULL;
+}
+
+int
+purgeph1bylogin(login)
+       char *login;
+{
+       struct ph1handle *p;
+       int found = 0;
+
+       LIST_FOREACH(p, &ph1tree, chain) {
+               if (p->mode_cfg == NULL)
+                       continue;
+               if (strncmp(p->mode_cfg->login, login, LOGINLEN) == 0) {
+                       if (p->status == PHASE1ST_ESTABLISHED)
+                               isakmp_info_send_d1(p);
+                       purge_remote(p);
+                       found++;
+               }
+       }
+
+       return found;
+}
+
+int
+purgephXbydstaddrwop(remote)
+struct sockaddr *remote;
+{
+       int    found = 0;
+       struct ph1handle *p;
+       struct ph2handle *p2;
+
+       LIST_FOREACH(p2, &ph2tree, chain) {
+               if (cmpsaddrwop(remote, p2->dst) == 0) {
+            plog(LLV_WARNING, LOCATION, NULL,
+                 "in %s... purging phase2s\n", __FUNCTION__);
+                       if (p2->status == PHASE2ST_ESTABLISHED)
+                               isakmp_info_send_d2(p2);
+                       if (p2->status < PHASE2ST_EXPIRED) {
+                               isakmp_ph2expire(p2);
+                       } else {
+                               isakmp_ph2delete(p2);
+                       }
+                       found++;
+               }
+       }
+
+       LIST_FOREACH(p, &ph1tree, chain) {
+               if (cmpsaddrwop(remote, p->remote) == 0) {
+            plog(LLV_WARNING, LOCATION, NULL,
+                 "in %s... purging phase1 and related phase2s\n", __FUNCTION__);
+            ike_session_purge_ph2s_by_ph1(p);
+                       if (p->status == PHASE1ST_ESTABLISHED)
+                               isakmp_info_send_d1(p);
+            isakmp_ph1expire(p);
+                       found++;
+               }
+       }
+
+       return found;
+}
+
+void
+purgephXbyspid(u_int32_t spid,
+               int       del_boundph1)
+{
+       struct ph2handle *iph2;
+    struct ph1handle *iph1;
+
+    // do ph2's first... we need the ph1s for notifications
+       LIST_FOREACH(iph2, &ph2tree, chain) {
+               if (spid == iph2->spid) {
+                       if (iph2->is_dying || iph2->status == PHASE2ST_EXPIRED) {
+                               continue;
+                       }
+            if (iph2->status == PHASE2ST_ESTABLISHED) {
+                isakmp_info_send_d2(iph2);
+            }
+            ike_session_stopped_by_controller(iph2->parent_session,
+                                              ike_session_stopped_by_flush);
+            isakmp_ph2expire(iph2); // iph2 will go down 1 second later.
+        }
+    }
+
+    // do the ph1s last.
+       LIST_FOREACH(iph2, &ph2tree, chain) {
+               if (spid == iph2->spid) {
+            if (del_boundph1 && iph2->parent_session) {
+                for (iph1 = LIST_FIRST(&iph2->parent_session->ikev1_state.ph1tree); iph1; iph1 = LIST_NEXT(iph1, ph1ofsession_chain)) {
+                                       if (iph1->is_dying || iph1->status == PHASE1ST_EXPIRED) {
+                                               continue;
+                                       }
+                    if (iph1->status == PHASE1ST_ESTABLISHED) {
+                        isakmp_info_send_d1(iph1);
+                    }
+                    isakmp_ph1expire(iph1);
+                }
+            }
+               }
+       }
+}
+
+#endif
+
+#ifdef ENABLE_DPD
+int
+ph1_force_dpd (struct sockaddr *remote)
+{
+    int status = -1;
+    struct ph1handle *p;
+
+    LIST_FOREACH(p, &ph1tree, chain) {
+        if (cmpsaddrwop(remote, p->remote) == 0) {
+            if (p->status == PHASE1ST_ESTABLISHED &&
+                !p->is_dying &&
+                p->dpd_support &&
+                p->rmconf->dpd_interval) {
+                if(!p->dpd_fails) {
+                    isakmp_info_send_r_u(p);
+                    status = 0;
+                } else {
+                    plog(LLV_DEBUG2, LOCATION, NULL, "skipping forced-DPD for phase1 (dpd already in progress).\n");
+                }
+                if (p->parent_session) {
+                    p->parent_session->controller_awaiting_peer_resp = 1;
+                }
+            } else {
+                plog(LLV_DEBUG2, LOCATION, NULL, "skipping forced-DPD for phase1 (status %d, dying %d, dpd-support %d, dpd-interval %d).\n",
+                     p->status, p->is_dying, p->dpd_support, p->rmconf->dpd_interval);
+            }
+        }
+    }
+
+       return status;
+}
+#endif
+
+void
+sweep_sleepwake(void)
+{
+       struct ph2handle *iph2;
+       struct ph1handle *iph1;
+
+       // do the ph1s.
+       LIST_FOREACH(iph1, &ph1tree, chain) {
+               if (iph1->parent_session && iph1->parent_session->is_asserted) {
+                       plog(LLV_DEBUG2, LOCATION, NULL, "skipping sweep of phase1 %s because it's been asserted.\n",
+                                isakmp_pindex(&iph1->index, 0));
+                       continue;
+               }
+               if (iph1->is_dying || iph1->status >= PHASE1ST_EXPIRED) {
+                       plog(LLV_DEBUG2, LOCATION, NULL, "skipping sweep of phase1 %s because it's already expired.\n",
+                                isakmp_pindex(&iph1->index, 0));
+                       continue;
+               }
+               if (iph1->sce) {
+                       if (iph1->sce->xtime <= swept_at) {
+                               SCHED_KILL(iph1->sce);
+                               SCHED_KILL(iph1->sce_rekey);
+                               iph1->is_dying = 1;
+                               iph1->status = PHASE1ST_EXPIRED;
+                               ike_session_update_ph1_ph2tree(iph1); // move unbind/rebind ph2s to from current ph1
+                               iph1->sce = sched_new(1, isakmp_ph1delete_stub, iph1);
+                               plog(LLV_DEBUG2, LOCATION, NULL, "phase1 %s expired while sleeping: quick deletion.\n",
+                                    isakmp_pindex(&iph1->index, 0));
+                       }
+               }
+               if (iph1->sce_rekey) {
+                       if (iph1->status == PHASE1ST_EXPIRED || iph1->sce_rekey->xtime <= swept_at) {
+                               SCHED_KILL(iph1->sce_rekey);
+                       }
+               }
+               if (iph1->scr) {
+                       if (iph1->status == PHASE1ST_EXPIRED || iph1->scr->xtime <= swept_at) {
+                               SCHED_KILL(iph1->scr);
+                       }
+               }
+#ifdef ENABLE_DPD
+               if (iph1->dpd_r_u) {
+                       if (iph1->status == PHASE1ST_EXPIRED || iph1->dpd_r_u->xtime <= swept_at) {
+                               SCHED_KILL(iph1->dpd_r_u);
+                       }
+               }
+#endif 
+       }
+
+       // do ph2's next
+       LIST_FOREACH(iph2, &ph2tree, chain) {
+               if (iph2->parent_session && iph2->parent_session->is_asserted) {
+                       plog(LLV_DEBUG2, LOCATION, NULL, "skipping sweep of phase2 because it's been asserted.\n");
+                       continue;
+               }
+               if (iph2->is_dying || iph2->status >= PHASE2ST_EXPIRED) {
+                       plog(LLV_DEBUG2, LOCATION, NULL, "skipping sweep of phase2 because it's already expired.\n");
+                       continue;
+               }
+               if (iph2->sce) {
+                       if (iph2->sce->xtime <= swept_at) {
+                               iph2->status = PHASE2ST_EXPIRED;
+                               iph2->is_dying = 1;
+                               isakmp_ph2expire(iph2); // iph2 will go down 1 second later.
+                               ike_session_stopped_by_controller(iph2->parent_session,
+                                                                 ike_session_stopped_by_sleepwake);
+                               plog(LLV_DEBUG2, LOCATION, NULL, "phase2 expired while sleeping: quick deletion.\n");
+                       }
+               }
+               if (iph2->scr) {
+                       if (iph2->status == PHASE2ST_EXPIRED || iph2->scr->xtime <= swept_at) {
+                               SCHED_KILL(iph2->scr);
+                       }
+               }
+       }
+
+       // do the ike_session last
+       ike_session_sweep_sleepwake();
+}