#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"
#include "proposal.h"
#include "sainfo.h"
#include "power_mgmt.h"
#define GET_SAMPLE_PERIOD(s,m) do { \
s = m / 20; \
if (s < 3) { \
s = 3; \
if (m < (s * 2)) { \
s = 1; \
} \
} \
} while(0);
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";
const char *ike_session_stopped_by_sleepwake = "Stopped by Sleep-Wake";
const char *ike_session_stopped_by_assert = "Stopped by Assert";
static LIST_HEAD(_ike_session_tree_, ike_session) ike_session_tree = { NULL };
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; 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->stop_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);
}
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;
}
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) {
}
}
}
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);
}
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;
}
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;
if (iph1->rmconf->dpd_interval &&
(iph1->rmconf->dpd_algo == DPD_ALGO_INBOUND_DETECT ||
iph1->rmconf->dpd_algo == DPD_ALGO_BLACKHOLE_DETECT)) {
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 = iph1->rmconf->idle_timeout;
} else {
return;
}
if (min_period) {
GET_SAMPLE_PERIOD(sample_period, min_period);
} else {
GET_SAMPLE_PERIOD(sample_period, max_period);
}
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;
}
if (iph1->parent_session) {
if (session == iph1->parent_session) {
return 0;
}
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) {
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;
}
if (iph2->parent_session->is_cisco_ipsec) {
return;
}
if (iph2->approval) {
if (!ipsecdoi_any_transportmode(iph2->approval)) {
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)) {
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 (!ike_session_has_established_ph2(session)) {
ike_session_cleanup(session, ike_session_stopped_by_xauth_timeout);
} else {
session->sc_xauth = sched_new(300 ,
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;
}
if (iph2->parent_session) {
if (session == iph2->parent_session) {
return 0;
}
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) {
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)) {
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) {
session->is_dying = 1;
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) {
session->is_dying = 1;
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)) {
plog(LLV_DEBUG2, LOCATION, NULL, "%s: ph2 sub spid %d, db spid %d\n", __FUNCTION__, iph2->spid, p->spid);
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) {
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) {
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");
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 &&
(session->established && !session->stopped_by_vpn_controller && !session->stop_timestamp.tv_sec && !session->stop_timestamp.tv_usec)) {
SCHED_KILL(session->traffic_monitor.sc_mon);
if (pk_sendget_inbound_sastats(session) < 0) {
plog(LLV_DEBUG2, LOCATION, NULL, "pk_sendget_inbound_sastats failed in %s.\n", __FUNCTION__);
}
if (pk_sendget_outbound_sastats(session) < 0) {
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 {
plog(LLV_DEBUG2, LOCATION, NULL, "invalid parameters in %s.\n", __FUNCTION__);
}
}
static void
ike_session_cleanup_idle (void *arg)
{
ike_session_cleanup((ike_session_t *)arg, ike_session_stopped_by_idle);
}
static void
ike_session_monitor_idle (ike_session_t *session)
{
if (!session)
return;
if (session->traffic_monitor.dir_idle == IPSEC_DIR_INBOUND ||
session->traffic_monitor.dir_idle == IPSEC_DIR_ANY) {
if (session->peer_sent_data_sc_idle) {
plog(LLV_DEBUG2, LOCATION, NULL, "%s: restart idle-timeout because peer sent data. monitoring dir %d.\n",
__FUNCTION__, session->traffic_monitor.dir_idle);
SCHED_KILL(session->traffic_monitor.sc_idle);
if (session->traffic_monitor.interv_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) {
plog(LLV_DEBUG2, LOCATION, NULL, "%s: restart idle-timeout because i sent data. monitoring dir %d.\n",
__FUNCTION__, session->traffic_monitor.dir_idle);
SCHED_KILL(session->traffic_monitor.sc_idle);
if (session->traffic_monitor.interv_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;
}
}
}
static void
ike_session_start_traffic_mon (ike_session_t *session)
{
if (session->traffic_monitor.interv_mon) {
session->traffic_monitor.sc_mon = sched_new(session->traffic_monitor.interv_mon,
ike_session_traffic_cop,
session);
}
if (session->traffic_monitor.interv_idle) {
session->traffic_monitor.sc_idle = sched_new(session->traffic_monitor.interv_idle,
ike_session_cleanup_idle,
session);
}
}
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);
ike_session_start_traffic_mon(iph2->parent_session);
} else if (iph2->parent_session->is_asserted) {
ike_session_start_traffic_mon(iph2->parent_session);
}
iph2->parent_session->is_asserted = 0;
iph2->parent_session->term_reason = NULL;
ike_session_update_mode(iph2);
#ifdef ENABLE_VPNCONTROL_PORT
vpncontrol_notify_peer_resp_ph2(1, iph2);
#endif
plog(LLV_DEBUG2, LOCATION, NULL, "%s: ph2 established, spid %d\n", __FUNCTION__, iph2->spid);
}
void
ike_session_cleanup_ph1 (struct ph1handle *iph1)
{
if (iph1->status == PHASE1ST_EXPIRED) {
iph1->sce = sched_new(1, isakmp_ph1delete_stub, iph1);
return;
}
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;
}
if (session->is_client && new_iph1->side == RESPONDER) {
return;
}
for (p = LIST_FIRST(&session->ikev1_state.ph1tree); p; p = next) {
next = LIST_NEXT(p, ph1ofsession_chain);
if (p != new_iph1 && !p->is_dying) {
SCHED_KILL(p->sce);
SCHED_KILL(p->sce_rekey);
p->is_dying = 1;
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);
ike_session_rebindph12_from_old_ph1_to_new_ph1 (p, new_iph1);
if (p->side == INITIATOR) {
p->sce = sched_new(5, ike_session_cleanup_ph1_stub, p);
} else {
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);
plog(LLV_ERROR, LOCATION, NULL,
"about to cleanup ph2: status %d, seq %d dying %d\n",
iph2->status, iph2->seq, iph2->is_dying);
if (iph2->status == PHASE2ST_ESTABLISHED) {
isakmp_info_send_d2(iph2);
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 );
}
}
}
}
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;
}
if (session->is_client && new_iph2->side == RESPONDER) {
return;
}
for (p = LIST_FIRST(&session->ikev1_state.ph2tree); p; p = next) {
next = LIST_NEXT(p, ph2ofsession_chain);
if (p != new_iph2 && p->spid == new_iph2->spid && !p->is_dying) {
SCHED_KILL(p->sce);
p->is_dying = 1;
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) {
p->sce = sched_new(3, ike_session_cleanup_ph2_stub, p);
} else {
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) {
next = LIST_NEXT(p, ph2ofsession_chain);
if (p->is_dying) {
continue;
}
SCHED_KILL(p->sce);
p->is_dying = 1;
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;
}
if (!session->established || session->stopped_by_vpn_controller || session->stop_timestamp.tv_sec || session->stop_timestamp.tv_usec) {
plog(LLV_DEBUG2, LOCATION, NULL, "dropping update on invalid session 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;
}
}
}
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) {
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 {
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;
}
}
if (!idle)
session->last_time_data_sc_detected = time(NULL);
ike_session_monitor_idle(session);
}
void
ike_session_cleanup (ike_session_t *session,
const char *reason)
{
struct ph2handle *iph2;
struct ph1handle *iph1;
if (!session)
return;
session->is_dying = 1;
SCHED_KILL(session->traffic_monitor.sc_idle);
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); ike_session_stopped_by_controller(session, reason);
}
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);
}
if (session->is_cisco_ipsec && reason && reason != ike_session_stopped_by_vpn_disconnect) {
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;
}
if (reason == ike_session_stopped_by_idle) {
(void)vpncontrol_notify_ike_failed(VPNCTL_NTYPE_IDLE_TIMEOUT, FROM_LOCAL, address, 0, NULL);
} else {
(void)vpncontrol_notify_ike_failed(VPNCTL_NTYPE_INTERNAL_ERROR, 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_established_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_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;
}
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 (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 ,
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);
}
}
static int
ike_session_is_id_ipany (vchar_t *ext_id)
{
struct id {
u_int8_t type;
u_int8_t proto_id;
u_int16_t port;
u_int32_t addr;
u_int32_t mask;
} *id_ptr;
id_ptr = (struct id *)ext_id->v;
if (id_ptr->type == IPSECDOI_ID_IPV4_ADDR &&
id_ptr->addr == 0) {
return 1;
} else if (id_ptr->type == IPSECDOI_ID_IPV4_ADDR_SUBNET &&
id_ptr->mask == 0 &&
id_ptr->addr == 0) {
return 1;
}
plog(LLV_DEBUG2, LOCATION, NULL, "not ipany_ids in %s: type %d, addr %x, mask %x.\n",
__FUNCTION__, id_ptr->type, id_ptr->addr, id_ptr->mask);
return 0;
}
static int
ike_session_is_id_portany (vchar_t *ext_id)
{
struct id {
u_int8_t type;
u_int8_t proto_id;
u_int16_t port;
u_int32_t addr;
u_int32_t mask;
} *id_ptr;
id_ptr = (struct id *)ext_id->v;
if (id_ptr->type == IPSECDOI_ID_IPV4_ADDR &&
id_ptr->port == 0) {
return 1;
}
plog(LLV_DEBUG2, LOCATION, NULL, "not portany_ids in %s: type %d, port %x.\n",
__FUNCTION__, id_ptr->type, id_ptr->port);
return 0;
}
static void
ike_session_set_id_portany (vchar_t *ext_id)
{
struct id {
u_int8_t type;
u_int8_t proto_id;
u_int16_t port;
u_int32_t addr;
u_int32_t mask;
} *id_ptr;
id_ptr = (struct id *)ext_id->v;
if (id_ptr->type == IPSECDOI_ID_IPV4_ADDR) {
id_ptr->port = 0;
return;
}
}
static int
ike_session_cmp_ph2_ids_ipany (vchar_t *ext_id,
vchar_t *ext_id_p)
{
if (ike_session_is_id_ipany(ext_id) &&
ike_session_is_id_ipany(ext_id_p)) {
return 1;
}
return 0;
}
int
ike_session_cmp_ph2_ids (struct ph2handle *iph2,
struct ph2handle *older_ph2)
{
vchar_t *portany_id = NULL;
vchar_t *portany_id_p = NULL;
if (iph2->id && older_ph2->id &&
iph2->id->l == older_ph2->id->l &&
memcmp(iph2->id->v, older_ph2->id->v, iph2->id->l) == 0 &&
iph2->id_p && older_ph2->id_p &&
iph2->id_p->l == older_ph2->id_p->l &&
memcmp(iph2->id_p->v, older_ph2->id_p->v, iph2->id_p->l) == 0) {
return 0;
}
if (iph2->ext_nat_id && older_ph2->ext_nat_id &&
iph2->ext_nat_id->l == older_ph2->ext_nat_id->l &&
memcmp(iph2->ext_nat_id->v, older_ph2->ext_nat_id->v, iph2->ext_nat_id->l) == 0 &&
iph2->ext_nat_id_p && older_ph2->ext_nat_id_p &&
iph2->ext_nat_id_p->l == older_ph2->ext_nat_id_p->l &&
memcmp(iph2->ext_nat_id_p->v, older_ph2->ext_nat_id_p->v, iph2->ext_nat_id_p->l) == 0) {
return 0;
}
if (iph2->id && older_ph2->ext_nat_id &&
iph2->id->l == older_ph2->ext_nat_id->l &&
memcmp(iph2->id->v, older_ph2->ext_nat_id->v, iph2->id->l) == 0 &&
iph2->id_p && older_ph2->ext_nat_id_p &&
iph2->id_p->l == older_ph2->ext_nat_id_p->l &&
memcmp(iph2->id_p->v, older_ph2->ext_nat_id_p->v, iph2->id_p->l) == 0) {
return 0;
}
if (iph2->id && older_ph2->ext_nat_id &&
iph2->id->l == older_ph2->ext_nat_id->l &&
memcmp(iph2->id->v, older_ph2->ext_nat_id->v, iph2->id->l) == 0 &&
iph2->id_p && older_ph2->id_p &&
iph2->id_p->l == older_ph2->id_p->l &&
memcmp(iph2->id_p->v, older_ph2->id_p->v, iph2->id_p->l) == 0) {
return 0;
}
if (iph2->id && older_ph2->id &&
iph2->id->l == older_ph2->id->l &&
memcmp(iph2->id->v, older_ph2->id->v, iph2->id->l) == 0 &&
iph2->id_p && older_ph2->ext_nat_id_p &&
iph2->id_p->l == older_ph2->ext_nat_id_p->l &&
memcmp(iph2->id_p->v, older_ph2->ext_nat_id_p->v, iph2->id_p->l) == 0) {
return 0;
}
if ((older_ph2->ext_nat_id && ike_session_is_id_portany(older_ph2->ext_nat_id)) ||
(older_ph2->ext_nat_id_p && ike_session_is_id_portany(older_ph2->ext_nat_id_p))) {
if (iph2->id && (portany_id = vdup(iph2->id))) {
ike_session_set_id_portany(portany_id);
}
if (iph2->id_p && (portany_id_p = vdup(iph2->id_p))) {
ike_session_set_id_portany(portany_id_p);
}
if (portany_id && older_ph2->ext_nat_id &&
portany_id->l == older_ph2->ext_nat_id->l &&
memcmp(portany_id->v, older_ph2->ext_nat_id->v, portany_id->l) == 0 &&
portany_id_p && older_ph2->ext_nat_id_p &&
portany_id_p->l == older_ph2->ext_nat_id_p->l &&
memcmp(portany_id_p->v, older_ph2->ext_nat_id_p->v, portany_id_p->l) == 0) {
if (portany_id) {
vfree(portany_id);
}
if (portany_id_p) {
vfree(portany_id_p);
}
return 0;
}
if (portany_id && iph2->id && older_ph2->ext_nat_id &&
iph2->id->l == older_ph2->ext_nat_id->l &&
memcmp(portany_id->v, older_ph2->ext_nat_id->v, portany_id->l) == 0 &&
iph2->id_p && older_ph2->id_p &&
iph2->id_p->l == older_ph2->id_p->l &&
memcmp(iph2->id_p->v, older_ph2->id_p->v, iph2->id_p->l) == 0) {
if (portany_id) {
vfree(portany_id);
}
if (portany_id_p) {
vfree(portany_id_p);
}
return 0;
}
if (portany_id_p && iph2->id && older_ph2->id &&
iph2->id->l == older_ph2->id->l &&
memcmp(iph2->id->v, older_ph2->id->v, iph2->id->l) == 0 &&
iph2->id_p && older_ph2->ext_nat_id_p &&
iph2->id_p->l == older_ph2->ext_nat_id_p->l &&
memcmp(portany_id_p->v, older_ph2->ext_nat_id_p->v, portany_id_p->l) == 0) {
if (portany_id) {
vfree(portany_id);
}
if (portany_id_p) {
vfree(portany_id_p);
}
return 0;
}
if (portany_id) {
vfree(portany_id);
}
if (portany_id_p) {
vfree(portany_id_p);
}
}
return -1;
}
int
ike_session_get_sainfo_r (struct ph2handle *iph2)
{
if (iph2->parent_session &&
iph2->parent_session->is_client &&
iph2->id && iph2->id_p) {
struct ph2handle *p;
int ipany_ids = ike_session_cmp_ph2_ids_ipany(iph2->id, iph2->id_p);
plog(LLV_DEBUG2, LOCATION, NULL, "ipany_ids %d in %s.\n", ipany_ids, __FUNCTION__);
for (p = LIST_FIRST(&iph2->parent_session->ikev1_state.ph2tree); p; p = LIST_NEXT(p, ph2ofsession_chain)) {
if (iph2 != p && !p->is_dying && p->status >= PHASE2ST_ESTABLISHED &&
p->sainfo && !p->sainfo->to_delete && !p->sainfo->to_remove) {
plog(LLV_DEBUG2, LOCATION, NULL, "candidate ph2 found in %s.\n", __FUNCTION__);
if (ipany_ids ||
ike_session_cmp_ph2_ids(iph2, p) == 0) {
plog(LLV_DEBUG2, LOCATION, NULL, "candidate ph2 matched in %s.\n", __FUNCTION__);
iph2->sainfo = p->sainfo;
if (!iph2->spid) {
iph2->spid = p->spid;
} else {
plog(LLV_DEBUG2, LOCATION, NULL, "%s: pre-assigned spid %d.\n", __FUNCTION__, iph2->spid);
}
if (p->ext_nat_id) {
if (iph2->ext_nat_id) {
vfree(iph2->ext_nat_id);
}
iph2->ext_nat_id = vdup(p->ext_nat_id);
}
if (p->ext_nat_id_p) {
if (iph2->ext_nat_id_p) {
vfree(iph2->ext_nat_id_p);
}
iph2->ext_nat_id_p = vdup(p->ext_nat_id_p);
}
return 0;
}
}
}
}
return -1;
}
int
ike_session_get_proposal_r (struct ph2handle *iph2)
{
if (iph2->parent_session &&
iph2->parent_session->is_client &&
iph2->id && iph2->id_p) {
struct ph2handle *p;
int ipany_ids = ike_session_cmp_ph2_ids_ipany(iph2->id, iph2->id_p);
plog(LLV_DEBUG2, LOCATION, NULL, "ipany_ids %d in %s.\n", ipany_ids, __FUNCTION__);
for (p = LIST_FIRST(&iph2->parent_session->ikev1_state.ph2tree); p; p = LIST_NEXT(p, ph2ofsession_chain)) {
if (iph2 != p && !p->is_dying && p->status >= PHASE2ST_ESTABLISHED &&
p->approval) {
plog(LLV_DEBUG2, LOCATION, NULL, "candidate ph2 found in %s.\n", __FUNCTION__);
if (ipany_ids ||
ike_session_cmp_ph2_ids(iph2, p) == 0) {
plog(LLV_DEBUG2, LOCATION, NULL, "candidate ph2 matched in %s.\n", __FUNCTION__);
iph2->proposal = dupsaprop(p->approval, 1);
if (!iph2->spid) {
iph2->spid = p->spid;
} else {
plog(LLV_DEBUG2, LOCATION, NULL, "%s: pre-assigned spid %d.\n", __FUNCTION__, iph2->spid);
}
return 0;
}
}
}
}
return -1;
}
void
ike_session_update_natt_version (struct ph1handle *iph1)
{
if (iph1->parent_session) {
if (iph1->natt_options) {
iph1->parent_session->natt_version = iph1->natt_options->version;
} else {
iph1->parent_session->natt_version = 0;
}
}
}
int
ike_session_get_natt_version (struct ph1handle *iph1)
{
if (iph1->parent_session) {
return(iph1->parent_session->natt_version);
}
return 0;
}
int
ike_session_drop_rekey (ike_session_t *session, ike_session_rekey_type_t rekey_type)
{
if (session) {
if (session->is_btmm_ipsec &&
session->last_time_data_sc_detected &&
session->traffic_monitor.interv_mon &&
session->traffic_monitor.interv_idle) {
time_t now = time(NULL);
if ((now - session->last_time_data_sc_detected) > (session->traffic_monitor.interv_mon << 1)) {
plog(LLV_DEBUG2, LOCATION, NULL, "btmm session is idle: drop ph%drekey.\n",
rekey_type);
return 1;
}
} else if (!session->is_btmm_ipsec) {
if (rekey_type == IKE_SESSION_REKEY_TYPE_PH1 &&
!ike_session_has_negoing_ph2(session)) {
plog(LLV_DEBUG2, LOCATION, NULL, "vpn session is idle: drop ph1 rekey.\n");
return 1;
}
}
}
return 0;
}
void
ike_session_sweep_sleepwake (void)
{
ike_session_t *p;
for (p = LIST_FIRST(&ike_session_tree); p; p = LIST_NEXT(p, chain)) {
if (p->is_dying) {
plog(LLV_DEBUG2, LOCATION, NULL, "skipping sweep of dying session.\n");
continue;
}
SCHED_KILL(p->sc_xauth);
if (p->is_asserted) {
SCHED_KILL(p->traffic_monitor.sc_mon);
SCHED_KILL(p->traffic_monitor.sc_idle);
plog(LLV_DEBUG2, LOCATION, NULL, "skipping sweep of asserted session.\n");
continue;
}
if (!ike_session_has_established_ph1(p) && !ike_session_has_established_ph2(p)) {
p->is_dying = 1;
plog(LLV_DEBUG2, LOCATION, NULL, "session died while sleeping.\n");
}
if (p->traffic_monitor.sc_mon) {
if (p->traffic_monitor.sc_mon->xtime <= swept_at) {
SCHED_KILL(p->traffic_monitor.sc_mon);
if (!p->is_dying && p->traffic_monitor.interv_mon) {
p->traffic_monitor.sc_mon = sched_new(p->traffic_monitor.interv_mon,
ike_session_traffic_cop,
p);
}
}
}
if (p->traffic_monitor.sc_idle) {
if (p->traffic_monitor.sc_idle->xtime <= swept_at) {
SCHED_KILL(p->traffic_monitor.sc_idle);
if (!p->is_dying && p->traffic_monitor.interv_idle) {
p->traffic_monitor.sc_idle = sched_new(p->traffic_monitor.interv_idle,
ike_session_cleanup_idle,
p);
}
}
}
}
}
int
ike_session_assert_session (ike_session_t *session)
{
struct ph2handle *iph2, *iph2_next;
struct ph1handle *iph1, *iph1_next;
if (!session || session->is_dying) {
plog(LLV_DEBUG2, LOCATION, NULL, "invalid parameters in %s.\n", __FUNCTION__);
return -1;
}
for (iph2 = LIST_FIRST(&session->ikev1_state.ph2tree); iph2; iph2 = iph2_next) {
iph2_next = LIST_NEXT(iph2, ph2ofsession_chain);
if (!iph2->is_dying && iph2->status < PHASE2ST_EXPIRED) {
SCHED_KILL(iph2->sce);
iph2->is_dying = 1;
if (iph2->status == PHASE2ST_ESTABLISHED && iph2->approval) {
struct saproto *pr;
for (pr = iph2->approval->head; pr != NULL; pr = pr->next) {
if (pr->ok) {
plog(LLV_DEBUG, LOCATION, NULL,
"assert: phase2 %s deleted\n",
sadbsecas2str(iph2->src, iph2->dst, iph2->satype, iph2->spid, ipsecdoi2pfkey_mode(pr->encmode)));
pfkey_send_delete(lcconf->sock_pfkey,
ipsecdoi2pfkey_proto(pr->proto_id),
ipsecdoi2pfkey_mode(pr->encmode),
iph2->src, iph2->dst, pr->spi_p);
}
}
}
iph2->status = PHASE2ST_EXPIRED; iph2->sce = sched_new(3, ike_session_cleanup_ph2_stub, iph2);
}
}
for (iph1 = LIST_FIRST(&session->ikev1_state.ph1tree); iph1; iph1 = iph1_next) {
iph1_next = LIST_NEXT(iph1, ph1ofsession_chain);
if (!iph1->is_dying && iph1->status < PHASE1ST_EXPIRED) {
SCHED_KILL(iph1->sce);
SCHED_KILL(iph1->sce_rekey);
iph1->is_dying = 1;
plog(LLV_DEBUG, LOCATION, NULL,
"assert: phase1 %s deleted\n",
isakmp_pindex(&iph1->index, 0));
ike_session_unbindph12_from_ph1(iph1);
iph1->status = PHASE1ST_EXPIRED;
iph1->sce = sched_new(5, ike_session_cleanup_ph1_stub, iph1);
}
}
session->is_asserted = 1;
return 0;
}
int
ike_session_assert (struct sockaddr *local,
struct sockaddr *remote)
{
ike_session_t *sess;
if (!local || !remote) {
plog(LLV_DEBUG2, LOCATION, NULL, "invalid parameters in %s.\n", __FUNCTION__);
return -1;
}
if ((sess = ike_session_get_session(local, remote, FALSE))) {
return(ike_session_assert_session(sess));
}
return -1;
}
void
ike_session_ph2_retransmits (struct ph2handle *iph2)
{
int num_retransmits;
if (!iph2->is_dying &&
iph2->is_rekey &&
iph2->ph1 &&
iph2->ph1->sce_rekey && !iph2->ph1->sce_rekey->dead &&
iph2->side == INITIATOR &&
iph2->parent_session &&
!iph2->parent_session->is_cisco_ipsec &&
iph2->parent_session->is_client) {
num_retransmits = iph2->ph1->rmconf->retry_counter - iph2->retry_counter;
if (num_retransmits == 3) {
plog(LLV_DEBUG2, LOCATION, NULL, "many phase2 retransmits: try phase1 rekey and this phase2 to quit earlier.\n");
isakmp_ph1rekeyexpire(iph2->ph1, TRUE);
iph2->retry_counter = 0;
}
}
}
void
ike_session_ph1_retransmits (struct ph1handle *iph1)
{
int num_retransmits;
if (!iph1->is_dying &&
iph1->is_rekey &&
!iph1->sce_rekey &&
iph1->status >= PHASE1ST_START && iph1->status < PHASE1ST_ESTABLISHED &&
iph1->side == INITIATOR &&
iph1->parent_session &&
iph1->parent_session->is_client &&
!ike_session_has_other_negoing_ph1(iph1->parent_session, iph1)) {
num_retransmits = iph1->rmconf->retry_counter - iph1->retry_counter;
if (num_retransmits == 3) {
plog(LLV_DEBUG2, LOCATION, NULL, "many phase1 retransmits: try quit earlier.\n");
iph1->retry_counter = 0;
}
}
}