+/*
+ * Copyright (c) 2008 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * The contents of this file constitute Original Code as defined in and
+ * are subject to the Apple Public Source License Version 1.1 (the
+ * "License"). You may not use this file except in compliance with the
+ * License. Please obtain a copy of the License at
+ * http://www.apple.com/publicsource and read it before using this file.
+ *
+ * This Original Code and all software distributed under the License are
+ * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
+ * License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <string.h>
+
+#include "var.h"
+#include "misc.h"
+#include "vmbuf.h"
+#include "plog.h"
+#include "sockmisc.h"
+#include "debug.h"
+
+#include "isakmp_var.h"
+#include "isakmp.h"
+#include "ike_session.h"
+#include "handler.h"
+#include "gcmalloc.h"
+#include "nattraversal.h"
+#include "schedule.h"
+#include "pfkey.h"
+#include "ipsec_doi.h"
+#include "ipsecSessionTracer.h"
+#include "ipsecMessageTracer.h"
+#include "isakmp_inf.h"
+#include "localconf.h"
+#include "remoteconf.h"
+#include "vpn_control.h"
+
+const char *ike_session_stopped_by_vpn_disconnect = "Stopped by VPN disconnect";
+const char *ike_session_stopped_by_flush = "Stopped by Flush";
+const char *ike_session_stopped_by_idle = "Stopped by Idle";
+const char *ike_session_stopped_by_xauth_timeout = "Stopped by XAUTH timeout";
+
+static LIST_HEAD(_ike_session_tree_, ike_session) ike_session_tree;
+
+static ike_session_t *
+new_ike_session (ike_session_id_t *id)
+{
+ ike_session_t *session;
+
+ if (!id) {
+ plog(LLV_DEBUG2, LOCATION, NULL, "invalid parameters in %s.\n", __FUNCTION__);
+ return NULL;
+ }
+
+ plog(LLV_DEBUG, LOCATION, NULL, "new parent session.\n");
+ session = racoon_calloc(1, sizeof(*session));
+ if (session) {
+ bzero(session, sizeof(*session));
+ memcpy(&session->session_id, id, sizeof(*id));
+ LIST_INIT(&session->ikev1_state.ph1tree);
+ LIST_INIT(&session->ikev1_state.ph2tree);
+ LIST_INSERT_HEAD(&ike_session_tree, session, chain);
+ session->version = IKE_VERSION_1; // hard-coded for now
+ IPSECSESSIONTRACERSTART(session);
+ }
+ return session;
+}
+
+static void
+free_ike_session (ike_session_t *session)
+{
+ int is_failure = TRUE;
+ if (session) {
+ SCHED_KILL(session->traffic_monitor.sc_mon);
+ SCHED_KILL(session->traffic_monitor.sc_idle);
+ SCHED_KILL(session->sc_xauth);
+ if (session->start_timestamp.tv_sec || session->start_timestamp.tv_usec) {
+ if (!(session->stop_timestamp.tv_sec || session->start_timestamp.tv_usec)) {
+ gettimeofday(&session->stop_timestamp, NULL);
+ }
+ if (session->term_reason != ike_session_stopped_by_vpn_disconnect ||
+ session->term_reason != ike_session_stopped_by_flush ||
+ session->term_reason != ike_session_stopped_by_idle) {
+ is_failure = FALSE;
+ }
+ IPSECSESSIONTRACERSTOP(session,
+ is_failure,
+ session->term_reason);
+ }
+ // do MessageTracer cleanup here
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "Freeing IKE-Session to %s.\n",
+ saddr2str((struct sockaddr *)&session->session_id.remote));
+ LIST_REMOVE(session, chain);
+ racoon_free(session);
+ }
+}
+
+struct ph1handle *
+ike_session_get_established_or_negoing_ph1 (ike_session_t *session)
+{
+ struct ph1handle *p, *iph1 = NULL;
+
+ if (!session) {
+ plog(LLV_DEBUG2, LOCATION, NULL, "invalid parameters in %s.\n", __FUNCTION__);
+ return NULL;
+ }
+
+ // look for the most mature ph1 under the session
+ for (p = LIST_FIRST(&session->ikev1_state.ph1tree); p; p = LIST_NEXT(p, ph1ofsession_chain)) {
+ if (!p->is_dying && p->status >= PHASE1ST_START && p->status <= PHASE1ST_ESTABLISHED) {
+ if (!iph1 || p->status > iph1->status) {
+ iph1 = p;
+ } else if (iph1 && p->status == iph1->status) {
+ // TODO: pick better one based on farthest rekey/expiry remaining
+ }
+ }
+ }
+
+ return iph1;
+}
+
+struct ph1handle *
+ike_session_get_established_ph1 (ike_session_t *session)
+{
+ struct ph1handle *p;
+
+ if (!session) {
+ plog(LLV_DEBUG2, LOCATION, NULL, "invalid parameters in %s.\n", __FUNCTION__);
+ return NULL;
+ }
+
+ for (p = LIST_FIRST(&session->ikev1_state.ph1tree); p; p = LIST_NEXT(p, ph1ofsession_chain)) {
+ if (!p->is_dying && p->status == PHASE1ST_ESTABLISHED) {
+ return p;
+ }
+ }
+
+ return NULL;
+}
+
+void
+ike_session_init (void)
+{
+ LIST_INIT(&ike_session_tree);
+}
+
+u_int
+ike_session_get_rekey_lifetime (int local_spi_is_higher, u_int expiry_lifetime)
+{
+ u_int rekey_lifetime = expiry_lifetime / 10;
+
+ if (rekey_lifetime) {
+ if (local_spi_is_higher) {
+ return (rekey_lifetime * 9);
+ } else {
+ return (rekey_lifetime * 8);
+ }
+ } else {
+ if (local_spi_is_higher) {
+ rekey_lifetime = expiry_lifetime - 1;
+ } else {
+ rekey_lifetime = expiry_lifetime - 2;
+ }
+ }
+ if (rekey_lifetime < expiry_lifetime) {
+ return (rekey_lifetime);
+ }
+ return(0);
+}
+
+// TODO: optimize this mess later
+ike_session_t *
+ike_session_get_session (struct sockaddr *local,
+ struct sockaddr *remote,
+ int alloc_if_absent)
+{
+ ike_session_t *p;
+ ike_session_id_t id;
+ ike_session_id_t id_default;
+ ike_session_id_t id_floated_default;
+ ike_session_id_t id_wop;
+ ike_session_t *best_match = NULL;
+ u_int16_t remote_port;
+ int is_isakmp_remote_port;
+
+ if (!local || !remote) {
+ plog(LLV_DEBUG2, LOCATION, NULL, "invalid parameters in %s.\n", __FUNCTION__);
+ return NULL;
+ }
+
+ remote_port = extract_port(remote);
+ if (remote_port && remote_port != PORT_ISAKMP && remote_port != PORT_ISAKMP_NATT) {
+ is_isakmp_remote_port = 0;
+ } else {
+ is_isakmp_remote_port = 1;
+ }
+
+ /* we will try a couple of matches first: if the exact id isn't found, then we'll try for an id that has zero'd ports */
+ bzero(&id, sizeof(id));
+ bzero(&id_default, sizeof(id_default));
+ bzero(&id_floated_default, sizeof(id_floated_default));
+ bzero(&id_wop, sizeof(id_wop));
+ if (local->sa_family == AF_INET) {
+ memcpy(&id.local, local, sizeof(struct sockaddr_in));
+ memcpy(&id_default.local, local, sizeof(struct sockaddr_in));
+ memcpy(&id_floated_default.local, local, sizeof(struct sockaddr_in));
+ memcpy(&id_wop.local, local, sizeof(struct sockaddr_in));
+ } else if (local->sa_family == AF_INET6) {
+ memcpy(&id.local, local, sizeof(struct sockaddr_in6));
+ memcpy(&id_default.local, local, sizeof(struct sockaddr_in6));
+ memcpy(&id_floated_default.local, local, sizeof(struct sockaddr_in6));
+ memcpy(&id_wop.local, local, sizeof(struct sockaddr_in6));
+ }
+ set_port((struct sockaddr *)&id_default.local, PORT_ISAKMP);
+ set_port((struct sockaddr *)&id_floated_default.local, PORT_ISAKMP_NATT);
+ set_port((struct sockaddr *)&id_wop.local, 0);
+ if (remote->sa_family == AF_INET) {
+ memcpy(&id.remote, remote, sizeof(struct sockaddr_in));
+ memcpy(&id_default.remote, remote, sizeof(struct sockaddr_in));
+ memcpy(&id_floated_default.remote, remote, sizeof(struct sockaddr_in));
+ memcpy(&id_wop.remote, remote, sizeof(struct sockaddr_in));
+ } else if (remote->sa_family == AF_INET6) {
+ memcpy(&id.remote, remote, sizeof(struct sockaddr_in6));
+ memcpy(&id_default.remote, remote, sizeof(struct sockaddr_in6));
+ memcpy(&id_floated_default.remote, remote, sizeof(struct sockaddr_in6));
+ memcpy(&id_wop.remote, remote, sizeof(struct sockaddr_in6));
+ }
+ set_port((struct sockaddr *)&id_default.remote, PORT_ISAKMP);
+ set_port((struct sockaddr *)&id_floated_default.remote, PORT_ISAKMP_NATT);
+ set_port((struct sockaddr *)&id_wop.remote, 0);
+
+ plog(LLV_DEBUG, LOCATION, local,
+ "start search for IKE-Session. target %s.\n",
+ saddr2str(remote));
+
+ for (p = LIST_FIRST(&ike_session_tree); p; p = LIST_NEXT(p, chain)) {
+ plog(LLV_DEBUG, LOCATION, local,
+ "still search for IKE-Session. this %s.\n",
+ saddr2str((struct sockaddr *)&p->session_id.remote));
+
+ if (memcmp(&p->session_id, &id, sizeof(id)) == 0) {
+ plog(LLV_DEBUG, LOCATION, local,
+ "Pre-existing IKE-Session to %s. case 1.\n",
+ saddr2str(remote));
+ return p;
+ } else if (is_isakmp_remote_port && memcmp(&p->session_id, &id_default, sizeof(id_default)) == 0) {
+ plog(LLV_DEBUG, LOCATION, local,
+ "Pre-existing IKE-Session to %s. case 2.\n",
+ saddr2str(remote));
+ return p;
+ } else if (is_isakmp_remote_port && p->ports_floated && memcmp(&p->session_id, &id_floated_default, sizeof(id_floated_default)) == 0) {
+ plog(LLV_DEBUG, LOCATION, local,
+ "Pre-existing IKE-Session to %s. case 3.\n",
+ saddr2str(remote));
+ return p;
+ } else if (is_isakmp_remote_port && memcmp(&p->session_id, &id_wop, sizeof(id_wop)) == 0) {
+ best_match = p;
+ }
+ }
+ if (best_match) {
+ plog(LLV_DEBUG, LOCATION, local,
+ "Best-match IKE-Session to %s.\n",
+ saddr2str((struct sockaddr *)&best_match->session_id.remote));
+ return best_match;
+ }
+ if (alloc_if_absent) {
+ plog(LLV_DEBUG, LOCATION, local,
+ "New IKE-Session to %s.\n",
+ saddr2str((struct sockaddr *)&id.remote));
+ return new_ike_session(&id);
+ } else {
+ return NULL;
+ }
+}
+
+void
+ike_session_init_traffic_cop_params (struct ph1handle *iph1)
+{
+ if (!iph1 ||
+ !iph1->rmconf ||
+ (!iph1->rmconf->idle_timeout && !iph1->rmconf->dpd_interval)) {
+ return;
+ }
+
+ if (!iph1->parent_session->traffic_monitor.interv_idle) {
+ iph1->parent_session->traffic_monitor.interv_idle = iph1->rmconf->idle_timeout;
+ }
+ if (!iph1->parent_session->traffic_monitor.dir_idle) {
+ iph1->parent_session->traffic_monitor.dir_idle = iph1->rmconf->idle_timeout_dir;
+ }
+
+ if (!iph1->parent_session->traffic_monitor.interv_mon) {
+ int min_period, max_period, sample_period = 0;
+
+ /* calculate the sampling interval... half the smaller interval */
+ if (iph1->rmconf->dpd_interval &&
+ (iph1->rmconf->dpd_algo == DPD_ALGO_INBOUND_DETECT ||
+ iph1->rmconf->dpd_algo == DPD_ALGO_BLACKHOLE_DETECT)) {
+ // when certain types of dpd are enabled
+ min_period = MIN(iph1->rmconf->dpd_interval, iph1->rmconf->idle_timeout);
+ max_period = MAX(iph1->rmconf->dpd_interval, iph1->rmconf->idle_timeout);
+ } else if (iph1->rmconf->idle_timeout) {
+ min_period = max_period = MIN(0, iph1->rmconf->idle_timeout);
+ } else {
+ // DPD_ALGO_DEFAULT is configured and there's no idle timeout... we don't need to monitor traffic
+ return;
+ }
+ if (min_period) {
+ sample_period = min_period >> 1;
+ if (!sample_period)
+ sample_period = 1; // bad
+ } else {
+ sample_period = max_period >> 1;
+ if (!sample_period)
+ sample_period = 1; // bad
+ }
+ iph1->parent_session->traffic_monitor.interv_mon = sample_period;
+ }
+}
+
+int
+ike_session_link_ph1_to_session (struct ph1handle *iph1)
+{
+ ike_session_t *session;
+
+ if (!iph1) {
+ plog(LLV_DEBUG2, LOCATION, NULL, "invalid parameters in %s.\n", __FUNCTION__);
+ return -1;
+ }
+
+ session = ike_session_get_session(iph1->local, iph1->remote, TRUE);
+ if (!session) {
+ plog(LLV_DEBUG2, LOCATION, NULL, "failed to get session in %s.\n", __FUNCTION__);
+ return -1;
+ }
+
+ // already linked
+ if (iph1->parent_session) {
+ if (session == iph1->parent_session) {
+ return 0;
+ }
+ // undo previous session
+ if (ike_session_unlink_ph1_from_session(iph1) == 0) {
+ plog(LLV_DEBUG2, LOCATION, NULL, "failed to unlink ph1 in %s.\n", __FUNCTION__);
+ free_ike_session(session);
+ return -1;
+ }
+ } else {
+ gettimeofday(&session->start_timestamp, NULL);
+ }
+
+
+ if (iph1->started_by_api) {
+ session->is_cisco_ipsec = 1;
+ session->is_l2tpvpn_ipsec = 0;
+ session->is_btmm_ipsec = 0;
+ }
+ iph1->parent_session = session;
+ LIST_INSERT_HEAD(&session->ikev1_state.ph1tree, iph1, ph1ofsession_chain);
+ session->ikev1_state.active_ph1cnt++;
+ if ((!session->ikev1_state.ph1cnt &&
+ iph1->side == INITIATOR) ||
+ iph1->started_by_api) {
+ // client initiates the first phase1 or, is started by controller api
+ session->is_client = 1;
+ }
+ if (session->established &&
+ session->ikev1_state.ph1cnt) {
+ iph1->is_rekey = 1;
+ }
+ session->ikev1_state.ph1cnt++;
+ ike_session_init_traffic_cop_params(iph1);
+
+ return 0;
+}
+
+void
+ike_session_update_mode (struct ph2handle *iph2)
+{
+ if (!iph2 || !iph2->parent_session) {
+ return;
+ }
+
+ // exit early if we already detected cisco-ipsec
+ if (iph2->parent_session->is_cisco_ipsec) {
+ return;
+ }
+
+ if (iph2->approval) {
+ if (!ipsecdoi_any_transportmode(iph2->approval)) {
+ // cisco & btmm ipsec are pure tunnel-mode (but cisco ipsec is detected by ph1)
+ iph2->parent_session->is_cisco_ipsec = 0;
+ iph2->parent_session->is_l2tpvpn_ipsec = 0;
+ iph2->parent_session->is_btmm_ipsec = 1;
+ return;
+ } else if (ipsecdoi_transportmode(iph2->approval)) {
+ iph2->parent_session->is_cisco_ipsec = 0;
+ iph2->parent_session->is_l2tpvpn_ipsec = 1;
+ iph2->parent_session->is_btmm_ipsec = 0;
+ return;
+ }
+ } else if (iph2->proposal) {
+ if (!ipsecdoi_any_transportmode(iph2->proposal)) {
+ // cisco & btmm ipsec are pure tunnel-mode (but cisco ipsec is detected by ph1)
+ iph2->parent_session->is_cisco_ipsec = 0;
+ iph2->parent_session->is_l2tpvpn_ipsec = 0;
+ iph2->parent_session->is_btmm_ipsec = 1;
+ return;
+ } else if (ipsecdoi_transportmode(iph2->proposal)) {
+ iph2->parent_session->is_cisco_ipsec = 0;
+ iph2->parent_session->is_l2tpvpn_ipsec = 1;
+ iph2->parent_session->is_btmm_ipsec = 0;
+ return;
+ }
+ }
+}
+
+static void
+ike_session_cleanup_xauth_timeout (void *arg)
+{
+ ike_session_t *session = (ike_session_t *)arg;
+
+ SCHED_KILL(session->sc_xauth);
+ // if there are no more established ph2s, start a timer to teardown the session
+ if (!ike_session_has_established_ph2(session)) {
+ ike_session_cleanup(session, ike_session_stopped_by_xauth_timeout);
+ } else {
+ session->sc_xauth = sched_new(300 /* 5 mins */,
+ ike_session_cleanup_xauth_timeout,
+ session);
+ }
+}
+
+int
+ike_session_link_ph2_to_session (struct ph2handle *iph2)
+{
+ struct sockaddr *local;
+ struct sockaddr *remote;
+ ike_session_t *session;
+
+ if (!iph2) {
+ plog(LLV_DEBUG2, LOCATION, NULL, "invalid parameters in %s.\n", __FUNCTION__);
+ return -1;
+ }
+
+ local = iph2->src;
+ remote = iph2->dst;
+
+ session = ike_session_get_session(local, remote, TRUE);
+ if (!session) {
+ plog(LLV_DEBUG2, LOCATION, NULL, "failed to get session in %s.\n", __FUNCTION__);
+ return -1;
+ }
+
+ // already linked
+ if (iph2->parent_session) {
+ if (session == iph2->parent_session) {
+ return 0;
+ }
+ // undo previous session
+ if (ike_session_unlink_ph2_from_session(iph2) == 0) {
+ plog(LLV_DEBUG2, LOCATION, NULL, "failed to unlink ph2 in %s.\n", __FUNCTION__);
+ free_ike_session(session);
+ return -1;
+ }
+ }
+
+ iph2->parent_session = session;
+ LIST_INSERT_HEAD(&session->ikev1_state.ph2tree, iph2, ph2ofsession_chain);
+ session->ikev1_state.active_ph2cnt++;
+ if (!session->ikev1_state.ph2cnt &&
+ iph2->side == INITIATOR) {
+ // client initiates the first phase2
+ session->is_client = 1;
+ }
+ if (session->established &&
+ session->ikev1_state.ph2cnt) {
+ iph2->is_rekey = 1;
+ }
+ session->ikev1_state.ph2cnt++;
+
+ ike_session_update_mode(iph2);
+
+ return 0;
+}
+
+int
+ike_session_unlink_ph1_from_session (struct ph1handle *iph1)
+{
+ ike_session_t *session;
+
+ if (!iph1 || !iph1->parent_session) {
+ plog(LLV_DEBUG2, LOCATION, NULL, "invalid parameters in %s.\n", __FUNCTION__);
+ return -1;
+ }
+
+ if (LIST_FIRST(&iph1->ph2tree)) {
+ // reparent any phase2 that may be hanging on to this phase1
+ ike_session_update_ph1_ph2tree(iph1);
+ }
+
+ session = iph1->parent_session;
+ LIST_REMOVE(iph1, ph1ofsession_chain);
+ iph1->parent_session = NULL;
+ session->ikev1_state.active_ph1cnt--;
+ if (session->ikev1_state.active_ph1cnt == 0 && session->ikev1_state.active_ph2cnt == 0) {
+ free_ike_session(session);
+ }
+
+ return 0;
+}
+
+int
+ike_session_unlink_ph2_from_session (struct ph2handle *iph2)
+{
+ ike_session_t *session;
+
+ if (!iph2 || !iph2->parent_session) {
+ plog(LLV_DEBUG2, LOCATION, NULL, "invalid parameters in %s.\n", __FUNCTION__);
+ return -1;
+ }
+
+ LIST_REMOVE(iph2, ph2ofsession_chain);
+ session = iph2->parent_session;
+ iph2->parent_session = NULL;
+ session->ikev1_state.active_ph2cnt--;
+ if (session->ikev1_state.active_ph1cnt == 0 && session->ikev1_state.active_ph2cnt == 0) {
+ free_ike_session(session);
+ }
+
+ return 0;
+}
+
+int
+ike_session_has_other_established_ph1 (ike_session_t *session, struct ph1handle *iph1)
+{
+ struct ph1handle *p;
+
+ if (!session) {
+ return 0;
+ }
+
+ for (p = LIST_FIRST(&session->ikev1_state.ph1tree); p; p = LIST_NEXT(p, ph1ofsession_chain)) {
+ if (iph1 != p && !p->is_dying) {
+ if (p->status == PHASE1ST_ESTABLISHED && p->sce_rekey) {
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int
+ike_session_has_other_negoing_ph1 (ike_session_t *session, struct ph1handle *iph1)
+{
+ struct ph1handle *p;
+
+ if (!session) {
+ plog(LLV_DEBUG2, LOCATION, NULL, "invalid parameters in %s.\n", __FUNCTION__);
+ return 0;
+ }
+
+ for (p = LIST_FIRST(&session->ikev1_state.ph1tree); p; p = LIST_NEXT(p, ph1ofsession_chain)) {
+ if (iph1 != p && !p->is_dying) {
+ if (p->status >= PHASE1ST_START && p->status <= PHASE1ST_ESTABLISHED) {
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int
+ike_session_has_other_established_ph2 (ike_session_t *session, struct ph2handle *iph2)
+{
+ struct ph2handle *p;
+
+ if (!session) {
+ plog(LLV_DEBUG2, LOCATION, NULL, "invalid parameters in %s.\n", __FUNCTION__);
+ return 0;
+ }
+
+ for (p = LIST_FIRST(&session->ikev1_state.ph2tree); p; p = LIST_NEXT(p, ph2ofsession_chain)) {
+ if (iph2 != p && !p->is_dying && iph2->spid == p->spid) {
+ if (p->status == PHASE2ST_ESTABLISHED) {
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int
+ike_session_has_other_negoing_ph2 (ike_session_t *session, struct ph2handle *iph2)
+{
+ struct ph2handle *p;
+
+ if (!session) {
+ plog(LLV_DEBUG2, LOCATION, NULL, "invalid parameters in %s.\n", __FUNCTION__);
+ return 0;
+ }
+
+ for (p = LIST_FIRST(&session->ikev1_state.ph2tree); p; p = LIST_NEXT(p, ph2ofsession_chain)) {
+ if (iph2 != p && !p->is_dying && iph2->spid == p->spid) {
+ if (p->status >= PHASE2ST_START && p->status < PHASE2ST_ESTABLISHED) {
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static void
+ike_session_unbindph12_from_ph1 (struct ph1handle *iph1)
+{
+ struct ph2handle *p, *next;
+
+ for (p = LIST_FIRST(&iph1->ph2tree); p; p = next) {
+ // take next pointer now, since unbind and rebind may change the underlying ph2tree list
+ next = LIST_NEXT(p, ph1bind);
+ unbindph12(p);
+ }
+}
+
+static void
+ike_session_rebindph12_from_old_ph1_to_new_ph1 (struct ph1handle *old_iph1,
+ struct ph1handle *new_iph1)
+{
+ struct ph2handle *p, *next;
+
+ if (old_iph1 == new_iph1 || !old_iph1 || !new_iph1) {
+ plog(LLV_DEBUG2, LOCATION, NULL, "invalid parameters in %s.\n", __FUNCTION__);
+ return;
+ }
+
+ if (old_iph1->parent_session != new_iph1->parent_session) {
+ plog(LLV_DEBUG2, LOCATION, NULL, "invalid parent sessions in %s.\n", __FUNCTION__);
+ return;
+ }
+
+ for (p = LIST_FIRST(&old_iph1->ph2tree); p; p = next) {
+ // take next pointer now, since rebind may change the underlying ph2tree list
+ next = LIST_NEXT(p, ph1bind);
+ if (p->parent_session != new_iph1->parent_session) {
+ plog(LLV_ERROR, LOCATION, NULL, "mismatched parent session in ph1bind replacement.\n");
+ }
+ if (p->ph1 == new_iph1) {
+ plog(LLV_ERROR, LOCATION, NULL, "same phase1 in ph1bind replacement in %s.\n",__FUNCTION__);
+ }
+ rebindph12(new_iph1, p);
+ }
+}
+
+int
+ike_session_verify_ph2_parent_session (struct ph2handle *iph2)
+{
+ if (!iph2) {
+ plog(LLV_DEBUG2, LOCATION, NULL, "invalid parameters in %s.\n", __FUNCTION__);
+ return -1;
+ }
+
+ if (!iph2->parent_session) {
+ plog(LLV_DEBUG, LOCATION, NULL, "NULL parent session.\n");
+ if (ike_session_link_ph2_to_session(iph2)) {
+ plog(LLV_DEBUG, LOCATION, NULL, "NULL parent session... still failed to link to session.\n");
+ // failed to bind ph2 to session
+ return 1;
+ }
+ }
+ return 0;
+}
+
+struct ph1handle *
+ike_session_update_ph1_ph2tree (struct ph1handle *iph1)
+{
+ struct ph1handle *new_iph1 = NULL;
+
+ if (!iph1) {
+ plog(LLV_DEBUG2, LOCATION, NULL, "invalid parameters in %s.\n", __FUNCTION__);
+ return NULL;
+ }
+
+ if (iph1->parent_session) {
+ new_iph1 = ike_session_get_established_ph1(iph1->parent_session);
+
+ if (!new_iph1) {
+ plog(LLV_DEBUG2, LOCATION, NULL, "no ph1bind replacement found. NULL ph1.\n");
+ ike_session_unbindph12_from_ph1(iph1);
+ } else if (iph1 == new_iph1) {
+ plog(LLV_DEBUG2, LOCATION, NULL, "no ph1bind replacement found. same ph1.\n");
+ ike_session_unbindph12_from_ph1(iph1);
+ } else {
+ ike_session_rebindph12_from_old_ph1_to_new_ph1(iph1, new_iph1);
+ }
+ } else {
+ plog(LLV_DEBUG2, LOCATION, NULL, "invalid parent session in %s.\n", __FUNCTION__);
+ }
+ return new_iph1;
+}
+
+struct ph1handle *
+ike_session_update_ph2_ph1bind (struct ph2handle *iph2)
+{
+ struct ph1handle *iph1;
+
+ if (!iph2) {
+ plog(LLV_DEBUG2, LOCATION, NULL, "invalid parameters in %s.\n", __FUNCTION__);
+ return NULL;
+ }
+
+ iph1 = ike_session_get_established_ph1(iph2->parent_session);
+ if (iph1 && iph2->ph1 && iph1 != iph2->ph1) {
+ rebindph12(iph1, iph2);
+ } else if (iph1 && !iph2->ph1) {
+ bindph12(iph1, iph2);
+ }
+
+ return iph1;
+}
+
+void
+ike_session_ikev1_float_ports (struct ph1handle *iph1)
+{
+ struct sockaddr *local, *remote;
+ struct ph2handle *p;
+
+ if (iph1->parent_session) {
+ local = (struct sockaddr *)&iph1->parent_session->session_id.local;
+ remote = (struct sockaddr *)&iph1->parent_session->session_id.remote;
+
+ set_port(local, extract_port(iph1->local));
+ set_port(remote, extract_port(iph1->remote));
+ iph1->parent_session->ports_floated = 1;
+
+ for (p = LIST_FIRST(&iph1->parent_session->ikev1_state.ph2tree); p; p = LIST_NEXT(p, ph2ofsession_chain)) {
+
+ local = p->src;
+ remote = p->dst;
+
+ set_port(local, extract_port(iph1->local));
+ set_port(remote, extract_port(iph1->remote));
+ }
+ } else {
+ plog(LLV_DEBUG2, LOCATION, NULL, "invalid parent session in %s.\n", __FUNCTION__);
+ }
+}
+
+static void
+ike_session_traffic_cop (void *arg)
+{
+ ike_session_t *session = (__typeof__(session))arg;
+
+ if (session) {
+ SCHED_KILL(session->traffic_monitor.sc_mon);
+ /* get traffic query from kernel */
+ if (pk_sendget_inbound_sastats(session) < 0) {
+ // log message
+ plog(LLV_DEBUG2, LOCATION, NULL, "pk_sendget_inbound_sastats failed in %s.\n", __FUNCTION__);
+ }
+ if (pk_sendget_outbound_sastats(session) < 0) {
+ // log message
+ plog(LLV_DEBUG2, LOCATION, NULL, "pk_sendget_outbound_sastats failed in %s.\n", __FUNCTION__);
+ }
+ session->traffic_monitor.sc_mon = sched_new(session->traffic_monitor.interv_mon,
+ ike_session_traffic_cop,
+ session);
+ } else {
+ // log message
+ plog(LLV_DEBUG2, LOCATION, NULL, "invalid parameters in %s.\n", __FUNCTION__);
+ }
+}
+
+static void
+ike_session_cleanup_idle (void *arg)
+{
+ ike_session_t *session = (ike_session_t *)arg;
+
+ if (session->traffic_monitor.dir_idle == IPSEC_DIR_INBOUND ||
+ session->traffic_monitor.dir_idle == IPSEC_DIR_ANY) {
+ if (session->peer_sent_data_sc_idle) {
+ SCHED_KILL(session->traffic_monitor.sc_idle);
+ session->traffic_monitor.sc_idle = sched_new(session->traffic_monitor.interv_idle,
+ ike_session_cleanup_idle,
+ session);
+ session->peer_sent_data_sc_idle = 0;
+ session->i_sent_data_sc_idle = 0;
+ return;
+ }
+ }
+ if (session->traffic_monitor.dir_idle == IPSEC_DIR_OUTBOUND ||
+ session->traffic_monitor.dir_idle == IPSEC_DIR_ANY) {
+ if (session->i_sent_data_sc_idle) {
+ SCHED_KILL(session->traffic_monitor.sc_idle);
+ session->traffic_monitor.sc_idle = sched_new(session->traffic_monitor.interv_idle,
+ ike_session_cleanup_idle,
+ session);
+ session->peer_sent_data_sc_idle = 0;
+ session->i_sent_data_sc_idle = 0;
+ return;
+ }
+ }
+
+ ike_session_cleanup((ike_session_t *)arg, ike_session_stopped_by_idle);
+}
+
+void
+ike_session_ph2_established (struct ph2handle *iph2)
+{
+ if (!iph2->parent_session) {
+ plog(LLV_DEBUG2, LOCATION, NULL, "invalid parameters in %s.\n", __FUNCTION__);
+ return;
+ }
+ SCHED_KILL(iph2->parent_session->sc_xauth);
+ if (!iph2->parent_session->established) {
+ gettimeofday(&iph2->parent_session->estab_timestamp, NULL);
+ iph2->parent_session->established = 1;
+ IPSECSESSIONTRACERESTABLISHED(iph2->parent_session);
+ if (iph2->parent_session->traffic_monitor.interv_mon) {
+ iph2->parent_session->traffic_monitor.sc_mon = sched_new(iph2->parent_session->traffic_monitor.interv_mon,
+ ike_session_traffic_cop,
+ iph2->parent_session);
+ }
+ if (iph2->parent_session->traffic_monitor.interv_idle) {
+ iph2->parent_session->traffic_monitor.sc_idle = sched_new(iph2->parent_session->traffic_monitor.interv_idle,
+ ike_session_cleanup_idle,
+ iph2->parent_session);
+ }
+ }
+ // nothing happening to this session
+ iph2->parent_session->term_reason = NULL;
+
+ ike_session_update_mode(iph2);
+}
+
+void
+ike_session_cleanup_ph1 (struct ph1handle *iph1)
+{
+ if (iph1->status == PHASE1ST_EXPIRED) {
+ // since this got here via ike_session_cleanup_other_established_ph1s, assumes LIST_FIRST(&iph1->ph2tree) == NULL
+ iph1->sce = sched_new(1, isakmp_ph1delete_stub, iph1);
+ return;
+ }
+
+ /* send delete information */
+ if (iph1->status == PHASE1ST_ESTABLISHED) {
+ isakmp_info_send_d1(iph1);
+ }
+
+ isakmp_ph1expire(iph1);
+}
+
+void
+ike_session_cleanup_ph1_stub (void *p)
+{
+
+ ike_session_cleanup_ph1((struct ph1handle *)p);
+}
+
+void
+ike_session_cleanup_other_established_ph1s (ike_session_t *session,
+ struct ph1handle *new_iph1)
+{
+ struct ph1handle *p, *next;
+ char *local, *remote;
+
+ if (!session || !new_iph1 || session != new_iph1->parent_session) {
+ plog(LLV_DEBUG2, LOCATION, NULL, "invalid parameters in %s.\n", __FUNCTION__);
+ return;
+ }
+
+ for (p = LIST_FIRST(&session->ikev1_state.ph1tree); p; p = next) {
+ // take next pointer now, since delete change the underlying ph1tree list
+ next = LIST_NEXT(p, ph1ofsession_chain);
+ /*
+ * TODO: currently, most recently established SA wins. Need to revisit to see if
+ * alternative selections is better (e.g. largest p->index stays).
+ */
+ if (p != new_iph1) {
+ SCHED_KILL(p->sce);
+ SCHED_KILL(p->sce_rekey);
+ p->is_dying = 1;
+
+ //log deletion
+ local = racoon_strdup(saddr2str(p->local));
+ remote = racoon_strdup(saddr2str(p->remote));
+ STRDUP_FATAL(local);
+ STRDUP_FATAL(remote);
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "ISAKMP-SA needs to be deleted %s-%s spi:%s\n",
+ local, remote, isakmp_pindex(&p->index, 0));
+ racoon_free(local);
+ racoon_free(remote);
+
+ // first rebind the children ph2s of this dying ph1 to the new ph1.
+ ike_session_rebindph12_from_old_ph1_to_new_ph1 (p, new_iph1);
+
+ if (p->side == INITIATOR) {
+ /* everyone deletes old outbound SA */
+ p->sce = sched_new(5, ike_session_cleanup_ph1_stub, p);
+ } else {
+ /* responder sets up timer to delete old inbound SAs... say 7 secs later and flags them as rekeyed */
+ p->sce = sched_new(7, ike_session_cleanup_ph1_stub, p);
+ }
+ }
+ }
+}
+
+void
+ike_session_cleanup_ph2 (struct ph2handle *iph2)
+{
+ if (iph2->status == PHASE2ST_EXPIRED) {
+ return;
+ }
+
+ SCHED_KILL(iph2->sce);
+
+ /* send delete information */
+ if (iph2->status == PHASE2ST_ESTABLISHED) {
+ isakmp_info_send_d2(iph2);
+ }
+
+ // delete outgoing SAs
+ if (iph2->approval) {
+ struct saproto *pr;
+
+ for (pr = iph2->approval->head; pr != NULL; pr = pr->next) {
+ if (pr->ok) {
+ pfkey_send_delete(lcconf->sock_pfkey,
+ ipsecdoi2pfkey_proto(pr->proto_id),
+ IPSEC_MODE_ANY,
+ iph2->src, iph2->dst, pr->spi_p /* pr->reqid_out */);
+ }
+ }
+ }
+
+ delete_spd(iph2);
+ unbindph12(iph2);
+ remph2(iph2);
+ delph2(iph2);
+}
+
+void
+ike_session_cleanup_ph2_stub (void *p)
+{
+
+ ike_session_cleanup_ph2((struct ph2handle *)p);
+}
+
+void
+ike_session_cleanup_other_established_ph2s (ike_session_t *session,
+ struct ph2handle *new_iph2)
+{
+ struct ph2handle *p, *next;
+
+ if (!session || !new_iph2 || session != new_iph2->parent_session) {
+ plog(LLV_DEBUG2, LOCATION, NULL, "invalid parameters in %s.\n", __FUNCTION__);
+ return;
+ }
+
+ for (p = LIST_FIRST(&session->ikev1_state.ph2tree); p; p = next) {
+ // take next pointer now, since delete change the underlying ph2tree list
+ next = LIST_NEXT(p, ph2ofsession_chain);
+ /*
+ * TODO: currently, most recently established SA wins. Need to revisit to see if
+ * alternative selections is better.
+ */
+ if (p != new_iph2 && p->spid == new_iph2->spid) {
+ SCHED_KILL(p->sce);
+ p->is_dying = 1;
+
+ //log deletion
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "IPsec-SA needs to be deleted: %s\n",
+ sadbsecas2str(p->src, p->dst,
+ p->satype, p->spid, 0));
+
+ if (p->side == INITIATOR) {
+ /* responder sets up timer to delete old inbound SAs... say 5 secs later and flags them as rekeyed */
+ p->sce = sched_new(3, ike_session_cleanup_ph2_stub, p);
+ } else {
+ /* responder sets up timer to delete old inbound SAs... say 5 secs later and flags them as rekeyed */
+ p->sce = sched_new(5, ike_session_cleanup_ph2_stub, p);
+ }
+ }
+ }
+}
+
+void
+ike_session_stopped_by_controller (ike_session_t *session,
+ const char *reason)
+{
+ if (!session) {
+ plog(LLV_DEBUG2, LOCATION, NULL, "invalid parameters in %s.\n", __FUNCTION__);
+ return;
+ }
+ if (session->stop_timestamp.tv_sec ||
+ session->stop_timestamp.tv_usec) {
+ plog(LLV_DEBUG2, LOCATION, NULL, "already stopped %s.\n", __FUNCTION__);
+ return;
+ }
+ session->stopped_by_vpn_controller = 1;
+ gettimeofday(&session->stop_timestamp, NULL);
+ if (!session->term_reason) {
+ session->term_reason = reason;
+ }
+}
+
+void
+ike_sessions_stopped_by_controller (struct sockaddr *remote,
+ int withport,
+ const char *reason)
+{
+ ike_session_t *p = NULL;
+
+ if (!remote) {
+ plog(LLV_DEBUG2, LOCATION, NULL, "invalid parameters in %s.\n", __FUNCTION__);
+ return;
+ }
+
+ for (p = LIST_FIRST(&ike_session_tree); p; p = LIST_NEXT(p, chain)) {
+ if (withport && cmpsaddrstrict(&p->session_id.remote, remote) == 0 ||
+ !withport && cmpsaddrwop(&p->session_id.remote, remote) == 0) {
+ ike_session_stopped_by_controller(p, reason);
+ }
+ }
+}
+
+void
+ike_session_purge_ph2s_by_ph1 (struct ph1handle *iph1)
+{
+ struct ph2handle *p, *next;
+
+ if (!iph1 || !iph1->parent_session) {
+ plog(LLV_DEBUG2, LOCATION, NULL, "invalid parameters in %s.\n", __FUNCTION__);
+ return;
+ }
+
+ for (p = LIST_FIRST(&iph1->parent_session->ikev1_state.ph2tree); p; p = next) {
+ // take next pointer now, since delete change the underlying ph2tree list
+ next = LIST_NEXT(p, ph2ofsession_chain);
+ SCHED_KILL(p->sce);
+ p->is_dying = 1;
+
+ //log deletion
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "IPsec-SA needs to be purged: %s\n",
+ sadbsecas2str(p->src, p->dst,
+ p->satype, p->spid, 0));
+
+ ike_session_cleanup_ph2(p);
+ }
+}
+
+void
+ike_session_update_ph2_ports (struct ph2handle *iph2)
+{
+ struct sockaddr *local;
+ struct sockaddr *remote;
+
+ if (iph2->parent_session) {
+ local = (struct sockaddr *)&iph2->parent_session->session_id.local;
+ remote = (struct sockaddr *)&iph2->parent_session->session_id.remote;
+
+ set_port(iph2->src, extract_port(local));
+ set_port(iph2->dst, extract_port(remote));
+ } else {
+ plog(LLV_DEBUG2, LOCATION, NULL, "invalid parent session in %s.\n", __FUNCTION__);
+ }
+}
+
+u_int32_t
+ike_session_get_sas_for_stats (ike_session_t *session,
+ u_int8_t dir,
+ u_int32_t *seq,
+ struct sastat *stats,
+ u_int32_t max_stats)
+{
+ int found = 0;
+ struct ph2handle *iph2;
+
+ if (!session || !seq || !stats || !max_stats || (dir != IPSEC_DIR_INBOUND && dir != IPSEC_DIR_OUTBOUND)) {
+ plog(LLV_DEBUG2, LOCATION, NULL, "invalid args in %s.\n", __FUNCTION__);
+ return found;
+ }
+
+ *seq = 0;
+ for (iph2 = LIST_FIRST(&session->ikev1_state.ph2tree); iph2; iph2 = LIST_NEXT(iph2, ph2ofsession_chain)) {
+
+ if (iph2->approval) {
+ struct saproto *pr;
+
+ for (pr = iph2->approval->head; pr != NULL; pr = pr->next) {
+ if (pr->ok && pr->proto_id == IPSECDOI_PROTO_IPSEC_ESP) {
+ if (!*seq) {
+ *seq = iph2->seq;
+ }
+ if (dir == IPSEC_DIR_INBOUND) {
+ stats[found].spi = pr->spi;
+ } else {
+ stats[found].spi = pr->spi_p;
+ }
+ if (++found == max_stats) {
+ return found;
+ }
+ }
+ }
+ }
+ }
+ return found;
+}
+
+void
+ike_session_update_traffic_idle_status (ike_session_t *session,
+ u_int32_t dir,
+ struct sastat *new_stats,
+ u_int32_t max_stats)
+{
+ int i, j, found = 0, idle = 1;
+
+ if (!session || !new_stats || (dir != IPSEC_DIR_INBOUND && dir != IPSEC_DIR_OUTBOUND)) {
+ plog(LLV_DEBUG2, LOCATION, NULL, "invalid args in %s.\n", __FUNCTION__);
+ return;
+ }
+
+ for (i = 0; i < max_stats; i++) {
+ if (dir == IPSEC_DIR_INBOUND) {
+ for (j = 0; j < session->traffic_monitor.num_in_last_poll; j++) {
+ if (new_stats[i].spi != session->traffic_monitor.in_last_poll[j].spi) {
+ continue;
+ }
+ found = 1;
+ if (new_stats[i].lft_c.sadb_lifetime_bytes != session->traffic_monitor.in_last_poll[j].lft_c.sadb_lifetime_bytes) {
+ idle = 0;
+ }
+ }
+ } else {
+ for (j = 0; j < session->traffic_monitor.num_out_last_poll; j++) {
+ if (new_stats[i].spi != session->traffic_monitor.out_last_poll[j].spi) {
+ continue;
+ }
+ found = 1;
+ if (new_stats[i].lft_c.sadb_lifetime_bytes != session->traffic_monitor.out_last_poll[j].lft_c.sadb_lifetime_bytes) {
+ idle = 0;
+ }
+ }
+ }
+ // new SA.... check for any activity
+ if (!found) {
+ if (new_stats[i].lft_c.sadb_lifetime_bytes) {
+ plog(LLV_DEBUG, LOCATION, NULL, "new SA: dir %d....\n", dir);
+ idle = 0;
+ }
+ }
+ }
+ if (dir == IPSEC_DIR_INBOUND) {
+ // overwrite old stats
+ bzero(session->traffic_monitor.in_last_poll, sizeof(session->traffic_monitor.in_last_poll));
+ bcopy(new_stats, session->traffic_monitor.in_last_poll, (max_stats * sizeof(*new_stats)));
+ session->traffic_monitor.num_in_last_poll = max_stats;
+ if (!idle) {
+ plog(LLV_DEBUG, LOCATION, NULL, "peer sent data....\n");
+ session->peer_sent_data_sc_dpd = 1;
+ session->peer_sent_data_sc_idle = 1;
+ }
+ } else {
+ // overwrite old stats
+ bzero(session->traffic_monitor.out_last_poll, sizeof(session->traffic_monitor.out_last_poll));
+ bcopy(new_stats, session->traffic_monitor.out_last_poll, (max_stats * sizeof(*new_stats)));
+ session->traffic_monitor.num_out_last_poll = max_stats;
+ if (!idle) {
+ plog(LLV_DEBUG, LOCATION, NULL, "i sent data....\n");
+ session->i_sent_data_sc_dpd = 1;
+ session->i_sent_data_sc_idle = 1;
+ }
+ }
+}
+
+void
+ike_session_cleanup (ike_session_t *session,
+ const char *reason)
+{
+ struct ph2handle *iph2;
+ struct ph1handle *iph1;
+
+ if (!session)
+ return;
+
+ // do ph2's first... we need the ph1s for notifications
+ for (iph2 = LIST_FIRST(&session->ikev1_state.ph2tree); iph2; iph2 = LIST_NEXT(iph2, ph2ofsession_chain)) {
+ if (iph2->status == PHASE2ST_ESTABLISHED) {
+ isakmp_info_send_d2(iph2);
+ }
+ isakmp_ph2expire(iph2); // iph2 will go down 1 second later.
+ ike_session_stopped_by_controller(session, reason);
+ }
+
+ // do the ph1s last.
+ for (iph1 = LIST_FIRST(&session->ikev1_state.ph1tree); iph1; iph1 = LIST_NEXT(iph1, ph1ofsession_chain)) {
+ if (iph1->status == PHASE1ST_ESTABLISHED) {
+ isakmp_info_send_d1(iph1);
+ }
+ isakmp_ph1expire(iph1);
+ }
+
+ // send ipsecManager a notification
+ if (reason == ike_session_stopped_by_idle) {
+ u_int32_t address;
+ if (((struct sockaddr *)&session->session_id.remote)->sa_family == AF_INET) {
+ address = ((struct sockaddr_in *)&session->session_id.remote)->sin_addr.s_addr;
+ } else {
+ address = 0;
+ }
+ (void)vpncontrol_notify_ike_failed(VPNCTL_NTYPE_IDLE_TIMEOUT, FROM_LOCAL, address, 0, NULL);
+ }
+}
+
+int
+ike_session_has_negoing_ph1 (ike_session_t *session)
+{
+ struct ph1handle *p;
+
+ if (!session) {
+ plog(LLV_DEBUG2, LOCATION, NULL, "invalid parameters in %s.\n", __FUNCTION__);
+ return 0;
+ }
+
+ for (p = LIST_FIRST(&session->ikev1_state.ph1tree); p; p = LIST_NEXT(p, ph1ofsession_chain)) {
+ if (!p->is_dying && p->status >= PHASE1ST_START && p->status <= PHASE1ST_ESTABLISHED) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+int
+ike_session_has_negoing_ph2 (ike_session_t *session)
+{
+ struct ph2handle *p;
+
+ if (!session) {
+ plog(LLV_DEBUG2, LOCATION, NULL, "invalid parameters in %s.\n", __FUNCTION__);
+ return 0;
+ }
+
+ for (p = LIST_FIRST(&session->ikev1_state.ph2tree); p; p = LIST_NEXT(p, ph2ofsession_chain)) {
+ if (!p->is_dying && p->status >= PHASE2ST_START && p->status <= PHASE2ST_ESTABLISHED) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+int
+ike_session_has_established_ph2 (ike_session_t *session)
+{
+ struct ph2handle *p;
+
+ if (!session) {
+ plog(LLV_DEBUG2, LOCATION, NULL, "invalid parameters in %s.\n", __FUNCTION__);
+ return 0;
+ }
+
+ for (p = LIST_FIRST(&session->ikev1_state.ph2tree); p; p = LIST_NEXT(p, ph2ofsession_chain)) {
+ if (!p->is_dying && p->status == PHASE2ST_ESTABLISHED) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+void
+ike_session_cleanup_ph1s_by_ph2 (struct ph2handle *iph2)
+{
+ struct ph1handle *iph1;
+
+ if (!iph2 || !iph2->parent_session) {
+ plog(LLV_DEBUG2, LOCATION, NULL, "invalid parameters in %s.\n", __FUNCTION__);
+ return;
+ }
+
+ // phase1 is no longer useful
+ for (iph1 = LIST_FIRST(&iph2->parent_session->ikev1_state.ph1tree); iph1; iph1 = LIST_NEXT(iph1, ph1ofsession_chain)) {
+ if (iph1->status == PHASE1ST_ESTABLISHED) {
+ isakmp_info_send_d1(iph1);
+ }
+ isakmp_ph1expire(iph1);
+ }
+}
+
+int
+ike_session_is_client_ph2_rekey (struct ph2handle *iph2)
+{
+ if (iph2->parent_session &&
+ iph2->parent_session->is_client &&
+ iph2->is_rekey &&
+ iph2->parent_session->is_cisco_ipsec) {
+ return 1;
+ }
+ return 0;
+}
+
+int
+ike_session_is_client_ph1_rekey (struct ph1handle *iph1)
+{
+ if (iph1->parent_session &&
+ iph1->parent_session->is_client &&
+ iph1->is_rekey &&
+ iph1->parent_session->is_cisco_ipsec) {
+ return 1;
+ }
+ return 0;
+}
+
+void
+ike_session_start_xauth_timer (struct ph1handle *iph1)
+{
+ // if there are no more established ph2s, start a timer to teardown the session
+ if (iph1->parent_session &&
+ iph1->parent_session->is_client &&
+ iph1->parent_session->is_cisco_ipsec &&
+ !iph1->parent_session->sc_xauth) {
+ iph1->parent_session->sc_xauth = sched_new(300 /* 5 mins */,
+ ike_session_cleanup_xauth_timeout,
+ iph1->parent_session);
+ }
+}
+
+void
+ike_session_stop_xauth_timer (struct ph1handle *iph1)
+{
+ if (iph1->parent_session) {
+ SCHED_KILL(iph1->parent_session->sc_xauth);
+ }
+}
\ No newline at end of file