#include "config.h"
#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include "var.h"
#include "misc.h"
#include "vmbuf.h"
#include "plog.h"
#include "sockmisc.h"
#include "debug.h"
#include "fsm.h"
#include "schedule.h"
#include "grabmyaddr.h"
#include "algorithm.h"
#include "crypto_openssl.h"
#include "policy.h"
#include "proposal.h"
#include "isakmp_var.h"
#include "isakmp.h"
#ifdef ENABLE_HYBRID
#include "isakmp_xauth.h"
#include "isakmp_cfg.h"
#endif
#include "isakmp_inf.h"
#include "oakley.h"
#include "remoteconf.h"
#include "localconf.h"
#include "handler.h"
#include "gcmalloc.h"
#include "nattraversal.h"
#include "ike_session.h"
#include "isakmp_frag.h"
#include "sainfo.h"
#include "power_mgmt.h"
extern LIST_HEAD(_ike_session_tree_, ike_session) ike_session_tree;
static LIST_HEAD(_ctdtree_, contacted) ctdtree;
static LIST_HEAD(_rcptree_, recvdpkt) rcptree;
static void ike_session_del_recvdpkt (struct recvdpkt *);
static void ike_session_rem_recvdpkt (struct recvdpkt *);
static void sweep_recvdpkt (void *);
extern caddr_t val2str (const char *, size_t);
static phase1_handle_t *
getph1byindex(ike_session_t *session, isakmp_index *index)
{
phase1_handle_t *p = NULL;
LIST_FOREACH(p, &session->ph1tree, ph1ofsession_chain) {
if (FSM_STATE_IS_EXPIRED(p->status))
continue;
if (memcmp(&p->index, index, sizeof(*index)) == 0)
return p;
}
return NULL;
}
phase1_handle_t *
ike_session_getph1byindex(ike_session_t *session, isakmp_index *index)
{
phase1_handle_t *p;
ike_session_t *cur_session = NULL;
if (session)
return getph1byindex(session, index);
LIST_FOREACH(cur_session, &ike_session_tree, chain) {
if ((p = getph1byindex(cur_session, index)) != NULL)
return p;
}
return NULL;
}
static phase1_handle_t *
getph1byindex0 (ike_session_t *session, isakmp_index *index)
{
phase1_handle_t *p = NULL;
LIST_FOREACH(p, &session->ph1tree, ph1ofsession_chain) {
if (FSM_STATE_IS_EXPIRED(p->status))
continue;
if (memcmp(&p->index.i_ck, &index->i_ck, sizeof(cookie_t)) == 0)
return p;
}
return NULL;
}
phase1_handle_t *
ike_session_getph1byindex0(ike_session_t *session, isakmp_index *index)
{
phase1_handle_t *p = NULL;
ike_session_t *cur_session = NULL;
if (session)
return getph1byindex0(session, index);
LIST_FOREACH(cur_session, &ike_session_tree, chain) {
if ((p = getph1byindex0(cur_session, index)) != NULL)
return p;
}
return NULL;
}
phase1_handle_t *
ike_session_getph1byaddr(ike_session_t *session, struct sockaddr_storage *local, struct sockaddr_storage *remote)
{
phase1_handle_t *p = NULL;
plog(ASL_LEVEL_DEBUG, "getph1byaddr: start\n");
plog(ASL_LEVEL_DEBUG, "local: %s\n", saddr2str((struct sockaddr *)local));
plog(ASL_LEVEL_DEBUG, "remote: %s\n", saddr2str((struct sockaddr *)remote));
LIST_FOREACH(p, &session->ph1tree, ph1ofsession_chain) {
if (FSM_STATE_IS_EXPIRED(p->status))
continue;
plog(ASL_LEVEL_DEBUG, "p->local: %s\n", saddr2str((struct sockaddr *)p->local));
plog(ASL_LEVEL_DEBUG, "p->remote: %s\n", saddr2str((struct sockaddr *)p->remote));
if (CMPSADDR(local, p->local) == 0
&& CMPSADDR(remote, p->remote) == 0){
plog(ASL_LEVEL_DEBUG, "matched\n");
return p;
}
}
plog(ASL_LEVEL_DEBUG, "no match\n");
return NULL;
}
static phase1_handle_t *
sgetph1byaddrwop(ike_session_t *session, struct sockaddr_storage *local, struct sockaddr_storage *remote)
{
phase1_handle_t *p = NULL;
LIST_FOREACH(p, &session->ph1tree, ph1ofsession_chain) {
if (FSM_STATE_IS_EXPIRED(p->status))
continue;
if (cmpsaddrwop(local, p->local) == 0
&& cmpsaddrwop(remote, p->remote) == 0)
return p;
}
return NULL;
}
phase1_handle_t *
ike_session_getph1byaddrwop(ike_session_t *session, struct sockaddr_storage *local, struct sockaddr_storage *remote)
{
phase1_handle_t *p;
ike_session_t *cur_session = NULL;
if (session)
return sgetph1byaddrwop(session, local, remote);
LIST_FOREACH(cur_session, &ike_session_tree, chain) {
if ((p = sgetph1byaddrwop(cur_session, local, remote)) != NULL)
return p;
}
return NULL;
}
phase1_handle_t *
sike_session_getph1bydstaddrwop(ike_session_t *session, struct sockaddr_storage *remote)
{
phase1_handle_t *p = NULL;
LIST_FOREACH(p, &session->ph1tree, ph1ofsession_chain) {
if (FSM_STATE_IS_EXPIRED(p->status)) {
continue;
}
if (remote->ss_family == AF_INET &&
p->nat64_prefix.length) {
struct in_addr address;
nw_nat64_extract_v4(&p->nat64_prefix, &((struct sockaddr_in6 *)p->remote)->sin6_addr, &address);
if (((struct sockaddr_in *)remote)->sin_addr.s_addr == address.s_addr) {
return p;
}
} else if (cmpsaddrwop(remote, p->remote) == 0) {
return p;
}
}
return NULL;
}
phase1_handle_t *
ike_session_getph1bydstaddrwop(ike_session_t *session, struct sockaddr_storage *remote)
{
phase1_handle_t *p;
ike_session_t *cur_session = NULL;
if (session)
return sike_session_getph1bydstaddrwop(session, remote);
else {
LIST_FOREACH(cur_session, &ike_session_tree, chain) {
if ((p = sike_session_getph1bydstaddrwop(cur_session, remote)) != NULL)
return p;
}
}
return NULL;
}
int
ike_session_islast_ph1(phase1_handle_t *ph1)
{
phase1_handle_t *p = NULL;
LIST_FOREACH(p, &ph1->parent_session->ph1tree, ph1ofsession_chain) {
if (p->is_dying || FSM_STATE_IS_EXPIRED(p->status))
continue;
if (CMPSADDR(ph1->remote, p->remote) == 0) {
if (p == ph1)
continue;
return 0;
}
}
return 1;
}
phase1_handle_t *
ike_session_newph1(unsigned int version)
{
phase1_handle_t *iph1;
iph1 = racoon_calloc(1, sizeof(*iph1));
if (iph1 == NULL)
return NULL;
iph1->version = version;
#ifdef ENABLE_DPD
iph1->dpd_support = 0;
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;
plog(ASL_LEVEL_NOTICE, "New Phase 1\n");
return iph1;
}
void
ike_session_delph1(phase1_handle_t *iph1)
{
if (iph1 == NULL)
return;
#ifdef ENABLE_NATT
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)
SCHED_KILL(iph1->dpd_r_u);
#endif
#ifdef ENABLE_VPNCONTROL_PORT
if (iph1->ping_sched)
SCHED_KILL(iph1->ping_sched);
#endif
if (iph1->remote) {
racoon_free(iph1->remote);
iph1->remote = NULL;
}
if (iph1->local) {
racoon_free(iph1->local);
iph1->local = NULL;
}
if (iph1->approval) {
delisakmpsa(iph1->approval);
iph1->approval = NULL;
}
sched_scrub_param(iph1);
if (iph1->sce)
SCHED_KILL(iph1->sce);
if (iph1->sce_rekey)
SCHED_KILL(iph1->sce_rekey);
if (iph1->scr)
SCHED_KILL(iph1->scr);
VPTRINIT(iph1->sendbuf);
VPTRINIT(iph1->dhpriv);
VPTRINIT(iph1->dhpub);
VPTRINIT(iph1->dhpub_p);
VPTRINIT(iph1->dhgxy);
VPTRINIT(iph1->nonce);
VPTRINIT(iph1->nonce_p);
VPTRINIT(iph1->skeyid);
VPTRINIT(iph1->skeyid_d);
VPTRINIT(iph1->skeyid_a);
VPTRINIT(iph1->skeyid_a_p);
VPTRINIT(iph1->skeyid_e);
VPTRINIT(iph1->skeyid_e_p);
VPTRINIT(iph1->key);
VPTRINIT(iph1->key_p);
VPTRINIT(iph1->hash);
VPTRINIT(iph1->sig);
VPTRINIT(iph1->sig_p);
oakley_delcert(iph1->cert);
iph1->cert = NULL;
oakley_delcert(iph1->cert_p);
iph1->cert_p = NULL;
oakley_delcert(iph1->crl_p);
iph1->crl_p = NULL;
oakley_delcert(iph1->cr_p);
iph1->cr_p = NULL;
VPTRINIT(iph1->id);
VPTRINIT(iph1->id_p);
if(iph1->approval != NULL)
delisakmpsa(iph1->approval);
if (iph1->ivm) {
oakley_delivm(iph1->ivm);
iph1->ivm = NULL;
}
VPTRINIT(iph1->sa);
VPTRINIT(iph1->sa_ret);
if (iph1->rmconf) {
release_rmconf(iph1->rmconf);
iph1->rmconf = NULL;
}
racoon_free(iph1);
}
void
ike_session_flush_all_phase1_for_session(ike_session_t *session, int ignore_estab_or_assert_handles)
{
phase1_handle_t *p, *next;
LIST_FOREACH_SAFE(p, &session->ph1tree, ph1ofsession_chain, next) {
if (ignore_estab_or_assert_handles && p->parent_session && !p->parent_session->stopped_by_vpn_controller && p->parent_session->is_asserted) {
plog(ASL_LEVEL_NOTICE,
"Skipping Phase 1 %s that's asserted...\n",
isakmp_pindex(&p->index, 0));
continue;
}
if (FSM_STATE_IS_ESTABLISHED(p->status)) {
if (ignore_estab_or_assert_handles &&
(ike_session_has_negoing_ph2(p->parent_session) || ike_session_has_established_ph2(p->parent_session))) {
plog(ASL_LEVEL_NOTICE,
"Skipping Phase 1 %s that's established... because it's needed by children Phase 2s\n",
isakmp_pindex(&p->index, 0));
continue;
}
plog(ASL_LEVEL_NOTICE,
"Got a Phase 1 %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);
ike_session_unlink_phase1(p);
}
}
void
ike_session_flush_all_phase1(int ignore_estab_or_assert_handles)
{
ike_session_t *session = NULL;
ike_session_t *next_session = NULL;
plog(ASL_LEVEL_NOTICE,
"Flushing Phase 1 handles: ignore_estab_or_assert %d...\n", ignore_estab_or_assert_handles);
LIST_FOREACH_SAFE(session, &ike_session_tree, chain, next_session) {
ike_session_flush_all_phase1_for_session(session, ignore_estab_or_assert_handles);
}
}
phase2_handle_t *
ike_session_getph2byspid(u_int32_t spid)
{
ike_session_t *session = NULL;
phase2_handle_t *p;
LIST_FOREACH(session, &ike_session_tree, chain) {
LIST_FOREACH(p, &session->ph2tree, ph2ofsession_chain) {
if (p->spid == spid)
return p;
}
}
return NULL;
}
phase2_handle_t *
ike_session_getph2byseq(u_int32_t seq)
{
ike_session_t *session;
phase2_handle_t *p;
LIST_FOREACH(session, &ike_session_tree, chain) {
LIST_FOREACH(p, &session->ph2tree, ph2ofsession_chain) {
if (p->seq == seq)
return p;
}
}
return NULL;
}
phase2_handle_t *
ike_session_getph2bymsgid(phase1_handle_t *iph1, u_int32_t msgid)
{
phase2_handle_t *p;
LIST_FOREACH(p, &iph1->parent_session->ph2tree, ph2ofsession_chain) {
if (p->msgid == msgid && !p->is_defunct)
return p;
}
return NULL;
}
phase2_handle_t *
ike_session_getonlyph2(phase1_handle_t *iph1)
{
phase2_handle_t *only_ph2 = NULL;
phase2_handle_t *p = NULL;
LIST_FOREACH(p, &iph1->bound_ph2tree, ph2ofsession_chain) {
if (only_ph2) return NULL;
only_ph2 = p;
}
return only_ph2;
}
phase2_handle_t *
ike_session_getph2byid(struct sockaddr_storage *src, struct sockaddr_storage *dst, u_int32_t spid)
{
ike_session_t *session = NULL;
ike_session_t *next_session = NULL;
phase2_handle_t *p;
phase2_handle_t *next_iph2;
LIST_FOREACH_SAFE(session, &ike_session_tree, chain, next_session) {
LIST_FOREACH_SAFE(p, &session->ph2tree, ph2ofsession_chain, next_iph2) {
if (spid == p->spid &&
CMPSADDR(src, p->src) == 0 &&
CMPSADDR(dst, p->dst) == 0){
if(!FSM_STATE_IS_ESTABLISHED_OR_EXPIRED(p->status) &&
p->retry_counter == 0
&& p->sce == 0 && p->scr == 0 &&
p->retry_checkph1 == 0){
plog(ASL_LEVEL_NOTICE,
"Zombie ph2 found, expiring it\n");
isakmp_ph2expire(p);
}else
return p;
}
}
}
return NULL;
}
#ifdef NOT_USED
phase2_handle_t *
ike_session_getph2bysaddr(struct sockaddr_storage *src, struct sockaddr_storage *dst)
{
ike_session_t *session;
phase2_handle_t *p;
LIST_FOREACH(session, &ike_session_tree, chain) {
LIST_FOREACH(p, &session->ph2tree, chain) {
if (cmpsaddrstrict(src, p->src) == 0 &&
cmpsaddrstrict(dst, p->dst) == 0)
return p;
}
}
return NULL;
}
#endif
phase2_handle_t *
ike_session_getph2bysaidx(struct sockaddr_storage *src, struct sockaddr_storage *dst, u_int proto_id, u_int32_t spi)
{
ike_session_t *session;
phase2_handle_t *iph2;
struct saproto *pr;
LIST_FOREACH(session, &ike_session_tree, chain) {
LIST_FOREACH(iph2, &session->ph2tree, ph2ofsession_chain) {
if (iph2->proposal == NULL && iph2->approval == NULL)
continue;
if (iph2->approval != NULL) {
for (pr = iph2->approval->head; pr != NULL;
pr = pr->next) {
if (proto_id != pr->proto_id)
break;
if (spi == pr->spi || spi == pr->spi_p)
return iph2;
}
} else if (iph2->proposal != NULL) {
for (pr = iph2->proposal->head; pr != NULL;
pr = pr->next) {
if (proto_id != pr->proto_id)
break;
if (spi == pr->spi)
return iph2;
}
}
}
}
return NULL;
}
phase2_handle_t *
ike_session_getph2bysaidx2(struct sockaddr_storage *src, struct sockaddr_storage *dst, u_int proto_id, u_int32_t spi, u_int32_t *opposite_spi)
{
ike_session_t *session;
phase2_handle_t *iph2;
struct saproto *pr;
LIST_FOREACH(session, &ike_session_tree, chain) {
LIST_FOREACH(iph2, &session->ph2tree, ph2ofsession_chain) {
if (iph2->proposal == NULL && iph2->approval == NULL)
continue;
if (iph2->approval != NULL) {
for (pr = iph2->approval->head; pr != NULL;
pr = pr->next) {
if (proto_id != pr->proto_id)
break;
if (spi == pr->spi || spi == pr->spi_p) {
if (opposite_spi) {
*opposite_spi = (spi == pr->spi)? pr->spi_p : pr->spi;
}
return iph2;
}
}
} else if (iph2->proposal != NULL) {
for (pr = iph2->proposal->head; pr != NULL;
pr = pr->next) {
if (proto_id != pr->proto_id)
break;
if (spi == pr->spi || spi == pr->spi_p) {
if (opposite_spi) {
*opposite_spi = (spi == pr->spi)? pr->spi_p : pr->spi;
}
return iph2;
}
}
}
}
}
return NULL;
}
phase2_handle_t *
ike_session_newph2(unsigned int version, int type)
{
phase2_handle_t *iph2 = NULL;
iph2 = racoon_calloc(1, sizeof(*iph2));
if (iph2 == NULL)
return NULL;
iph2->version = version;
iph2->phase2_type = type;
iph2->is_dying = 0;
plog(ASL_LEVEL_NOTICE, "New Phase 2\n");
return iph2;
}
void
ike_session_initph2(phase2_handle_t *iph2)
{
sched_scrub_param(iph2);
iph2->sce = NULL;
iph2->scr = NULL;
VPTRINIT(iph2->sendbuf);
VPTRINIT(iph2->msg1);
if (iph2->proposal) {
struct saproto *pr;
for (pr = iph2->proposal->head; pr != NULL; pr = pr->next)
pr->spi = 0;
}
if (iph2->approval) {
flushsaprop(iph2->approval);
iph2->approval = NULL;
}
if (iph2->spidx_gen) {
delsp_bothdir(iph2->spidx_gen);
racoon_free(iph2->spidx_gen);
iph2->spidx_gen = NULL;
}
if (iph2->pfsgrp) {
oakley_dhgrp_free(iph2->pfsgrp);
iph2->pfsgrp = NULL;
}
VPTRINIT(iph2->dhpriv);
VPTRINIT(iph2->dhpub);
VPTRINIT(iph2->dhpub_p);
VPTRINIT(iph2->dhgxy);
VPTRINIT(iph2->id);
VPTRINIT(iph2->id_p);
VPTRINIT(iph2->nonce);
VPTRINIT(iph2->nonce_p);
VPTRINIT(iph2->sa);
VPTRINIT(iph2->sa_ret);
if (iph2->ivm) {
oakley_delivm(iph2->ivm);
iph2->ivm = NULL;
}
}
void
ike_session_delph2(phase2_handle_t *iph2)
{
ike_session_initph2(iph2);
if (iph2->src) {
racoon_free(iph2->src);
iph2->src = NULL;
}
if (iph2->dst) {
racoon_free(iph2->dst);
iph2->dst = NULL;
}
if (iph2->src_id) {
racoon_free(iph2->src_id);
iph2->src_id = NULL;
}
if (iph2->dst_id) {
racoon_free(iph2->dst_id);
iph2->dst_id = NULL;
}
if (iph2->proposal) {
flushsaprop(iph2->proposal);
iph2->proposal = NULL;
}
if (iph2->sainfo) {
release_sainfo(iph2->sainfo);
iph2->sainfo = NULL;
}
VPTRINIT(iph2->id);
VPTRINIT(iph2->id_p);
VPTRINIT(iph2->ext_nat_id);
VPTRINIT(iph2->ext_nat_id_p);
if (iph2->sce)
SCHED_KILL(iph2->sce);
if (iph2->scr)
SCHED_KILL(iph2->scr);
racoon_free(iph2);
}
void
ike_session_flush_all_phase2_for_session(ike_session_t *session, int ignore_estab_or_assert_handles)
{
phase2_handle_t *p = NULL;
phase2_handle_t *next = NULL;
LIST_FOREACH_SAFE(p, &session->ph2tree, ph2ofsession_chain, next) {
if (p->is_dying || FSM_STATE_IS_EXPIRED(p->status)) {
continue;
}
if (ignore_estab_or_assert_handles && p->parent_session && !p->parent_session->stopped_by_vpn_controller && p->parent_session->is_asserted) {
plog(ASL_LEVEL_NOTICE,
"skipping phase2 handle that's asserted...\n");
continue;
}
if (FSM_STATE_IS_ESTABLISHED(p->status)){
if (ignore_estab_or_assert_handles) {
plog(ASL_LEVEL_NOTICE,
"skipping ph2 handler that's established...\n");
continue;
}
plog(ASL_LEVEL_NOTICE,
"got an established ph2 handler to flush...\n");
isakmp_info_send_d2(p);
}else{
plog(ASL_LEVEL_NOTICE,
"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);
ike_session_unlink_phase2(p);
}
}
void
ike_session_flush_all_phase2(int ignore_estab_or_assert_handles)
{
ike_session_t *session = NULL;
ike_session_t *next_session = NULL;
plog(ASL_LEVEL_NOTICE,
"flushing ph2 handles: ignore_estab_or_assert %d...\n", ignore_estab_or_assert_handles);
LIST_FOREACH_SAFE(session, &ike_session_tree, chain, next_session) {
ike_session_flush_all_phase2_for_session(session, ignore_estab_or_assert_handles);
}
}
void
ike_session_deleteallph2(struct sockaddr_storage *src, struct sockaddr_storage *dst, u_int proto_id)
{
ike_session_t *session = NULL;
ike_session_t *next_session = NULL;
phase2_handle_t *iph2 = NULL;
phase2_handle_t *next_iph2 = NULL;
struct saproto *pr;
LIST_FOREACH_SAFE(session, &ike_session_tree, chain, next_session) {
LIST_FOREACH_SAFE(iph2, &session->ph2tree, ph2ofsession_chain, next_iph2) {
if (iph2->is_dying || FSM_STATE_IS_EXPIRED(iph2->status)) {
continue;
}
if (iph2->proposal == NULL && iph2->approval == NULL)
continue;
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)
goto zap_it;
}
} else if (iph2->proposal != NULL) {
for (pr = iph2->proposal->head; pr != NULL;
pr = pr->next) {
if (proto_id == pr->proto_id)
goto zap_it;
}
}
continue;
zap_it:
plog(ASL_LEVEL_NOTICE,
"deleteallph2: got a ph2 handler...\n");
if (FSM_STATE_IS_ESTABLISHED(iph2->status))
isakmp_info_send_d2(iph2);
ike_session_stopped_by_controller(iph2->parent_session,
ike_session_stopped_by_flush);
ike_session_unlink_phase2(iph2);
}
}
}
void
ike_session_deleteallph1(struct sockaddr_storage *src, struct sockaddr_storage *dst)
{
ike_session_t *session = NULL;
ike_session_t *next_session = NULL;
phase1_handle_t *iph1 = NULL;
phase1_handle_t *next_iph1 = NULL;
LIST_FOREACH_SAFE(session, &ike_session_tree, chain, next_session) {
LIST_FOREACH_SAFE(iph1, &session->ph1tree, ph1ofsession_chain, next_iph1) {
if (cmpsaddrwop(src, iph1->local) != 0 ||
cmpsaddrwop(dst, iph1->remote) != 0) {
continue;
}
plog(ASL_LEVEL_NOTICE,
"deleteallph1: got a ph1 handler...\n");
if (FSM_STATE_IS_ESTABLISHED(iph1->status))
isakmp_info_send_d1(iph1);
ike_session_stopped_by_controller(iph1->parent_session, ike_session_stopped_by_flush);
ike_session_unlink_phase1(iph1);
}
}
}
struct contacted *
ike_session_getcontacted(remote)
struct sockaddr_storage *remote;
{
struct contacted *p;
LIST_FOREACH(p, &ctdtree, chain) {
if (cmpsaddrstrict(remote, p->remote) == 0)
return p;
}
return NULL;
}
int
ike_session_inscontacted(remote)
struct sockaddr_storage *remote;
{
struct contacted *new;
new = racoon_calloc(1, sizeof(*new));
if (new == NULL)
return -1;
new->remote = dupsaddr(remote);
if (new->remote == NULL) {
plog(ASL_LEVEL_ERR,
"failed to allocate buffer.\n");
racoon_free(new);
return -1;
}
LIST_INSERT_HEAD(&ctdtree, new, chain);
return 0;
}
void
ike_session_clear_contacted()
{
struct contacted *c, *next;
LIST_FOREACH_SAFE(c, &ctdtree, chain, next) {
LIST_REMOVE(c, chain);
racoon_free(c->remote);
racoon_free(c);
}
}
void
ike_session_initctdtree()
{
LIST_INIT(&ctdtree);
}
time_t
ike_session_get_exp_retx_interval (int num_retries, int fixed_retry_interval)
{
if (num_retries <= 3) {
return (time_t)fixed_retry_interval;
} else {
return (time_t)(num_retries * fixed_retry_interval);
}
}
int
ike_session_check_recvdpkt(remote, local, rbuf)
struct sockaddr_storage *remote, *local;
vchar_t *rbuf;
{
vchar_t *hash;
struct recvdpkt *r;
time_t t, d;
int len, s;
t = time(NULL);
hash = eay_md5_one(rbuf);
if (!hash) {
plog(ASL_LEVEL_ERR,
"failed to allocate buffer.\n");
return -1;
}
LIST_FOREACH(r, &rcptree, chain) {
if (memcmp(hash->v, r->hash->v, r->hash->l) == 0)
break;
}
vfree(hash);
if (r == NULL)
return 0;
if (cmpsaddrwop(remote, r->remote) != 0) {
return 2;
}
if (t - r->time_send < 1) {
plog(ASL_LEVEL_WARNING,
"the packet retransmitted in a short time from %s\n",
saddr2str((struct sockaddr *)remote));
}
s = getsockmyaddr((struct sockaddr *)r->local);
if (s == -1)
return -1;
if (r->time_send && t > r->time_send) {
d = t - r->time_send;
if (d < r->retry_interval) {
plog(ASL_LEVEL_ERR, "already responded within the past %ld secs\n", d);
return 1;
}
}
#ifdef ENABLE_FRAG
if (r->frag_flags && r->sendbuf->l > ISAKMP_FRAG_MAXLEN) {
plog(ASL_LEVEL_ERR, "!!! retransmitting frags\n");
len = sendfragsfromto(s, r->sendbuf,
r->local, r->remote, lcconf->count_persend,
r->frag_flags);
} else {
plog(ASL_LEVEL_ERR, "!!! skipped retransmitting frags: frag_flags %x, r->sendbuf->l %zu, max %d\n", r->frag_flags, r->sendbuf->l, ISAKMP_FRAG_MAXLEN);
len = sendfromto(s, r->sendbuf->v, r->sendbuf->l,
r->local, r->remote, lcconf->count_persend);
}
#else
len = sendfromto(s, r->sendbuf->v, r->sendbuf->l,
r->local, r->remote, lcconf->count_persend);
#endif
if (len == -1) {
plog(ASL_LEVEL_ERR, "sendfromto failed\n");
return -1;
}
r->retry_counter--;
if (r->retry_counter <= 0) {
ike_session_rem_recvdpkt(r);
ike_session_del_recvdpkt(r);
plog(ASL_LEVEL_NOTICE,
"deleted the retransmission packet to %s.\n",
saddr2str((struct sockaddr *)remote));
} else {
r->time_send = t;
r->retry_interval = ike_session_get_exp_retx_interval((lcconf->retry_counter - r->retry_counter),
lcconf->retry_interval);
}
return 1;
}
int
ike_session_add_recvdpkt(remote, local, sbuf, rbuf, non_esp, frag_flags)
struct sockaddr_storage *remote, *local;
vchar_t *sbuf, *rbuf;
size_t non_esp;
u_int32_t frag_flags;
{
struct recvdpkt *new = NULL;
if (lcconf->retry_counter == 0) {
return 0;
}
new = racoon_calloc(1, sizeof(*new));
if (!new) {
plog(ASL_LEVEL_ERR,
"failed to allocate buffer.\n");
return -1;
}
new->hash = eay_md5_one(rbuf);
if (!new->hash) {
plog(ASL_LEVEL_ERR,
"failed to allocate buffer.\n");
ike_session_del_recvdpkt(new);
return -1;
}
new->remote = dupsaddr(remote);
if (new->remote == NULL) {
plog(ASL_LEVEL_ERR,
"failed to allocate buffer.\n");
ike_session_del_recvdpkt(new);
return -1;
}
new->local = dupsaddr(local);
if (new->local == NULL) {
plog(ASL_LEVEL_ERR,
"failed to allocate buffer.\n");
ike_session_del_recvdpkt(new);
return -1;
}
if (non_esp) {
plog (ASL_LEVEL_DEBUG, "Adding NON-ESP marker\n");
if ((new->sendbuf = vmalloc (sbuf->l + non_esp)) == NULL) {
plog(ASL_LEVEL_ERR,
"failed to allocate extra buf for non-esp\n");
ike_session_del_recvdpkt(new);
return -1;
}
*ALIGNED_CAST(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(ASL_LEVEL_ERR,
"failed to allocate buffer.\n");
ike_session_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 = ike_session_get_exp_retx_interval((lcconf->retry_counter - new->retry_counter),
lcconf->retry_interval);
LIST_INSERT_HEAD(&rcptree, new, chain);
return 0;
}
void
ike_session_del_recvdpkt(r)
struct recvdpkt *r;
{
if (r->remote)
racoon_free(r->remote);
if (r->local)
racoon_free(r->local);
if (r->hash)
vfree(r->hash);
if (r->sendbuf)
vfree(r->sendbuf);
racoon_free(r);
}
void
ike_session_rem_recvdpkt(r)
struct recvdpkt *r;
{
LIST_REMOVE(r, chain);
}
void
sweep_recvdpkt(dummy)
void *dummy;
{
struct recvdpkt *r, *next;
time_t t, lt;
t = time(NULL);
lt = lcconf->retry_counter * lcconf->retry_interval;
LIST_FOREACH_SAFE(r, &rcptree, chain, next) {
if (t - r->created > lt) {
ike_session_rem_recvdpkt(r);
ike_session_del_recvdpkt(r);
}
}
sched_new(lt, sweep_recvdpkt, &rcptree);
}
void
ike_session_clear_recvdpkt()
{
struct recvdpkt *r, *next;
LIST_FOREACH_SAFE(r, &rcptree, chain, next) {
ike_session_rem_recvdpkt(r);
ike_session_del_recvdpkt(r);
}
sched_scrub_param(&rcptree);
}
void
ike_session_init_recvdpkt()
{
time_t lt = lcconf->retry_counter * lcconf->retry_interval;
LIST_INIT(&rcptree);
sched_new(lt, sweep_recvdpkt, &rcptree);
}
#ifdef NOT_USED
#ifdef ENABLE_HYBRID
int
exclude_cfg_addr(const struct sockaddr_storage *addr)
{
ike_session_t *session;
phase1_handle_t *p;
struct sockaddr_in *sin;
LIST_FOREACH(session, &ike_session_tree, chain) {
LIST_FOREACH(p, &session->ph1tree, chain) {
if ((p->mode_cfg != NULL) &&
(p->mode_cfg->flags & ISAKMP_CFG_GOT_ADDR4) &&
(addr->ss_family == AF_INET)) {
sin = (struct sockaddr_in *)addr;
if (sin->sin_addr.s_addr == p->mode_cfg->addr4.s_addr)
return 0;
}
}
}
return 1;
}
#endif
#endif
int
ike_session_expire_session(ike_session_t *session)
{
int found = 0;
phase1_handle_t *p;
phase1_handle_t *next;
phase2_handle_t *p2;
if (session == NULL)
return 0;
LIST_FOREACH(p2, &session->ph2tree, ph2ofsession_chain) {
if (p2->is_dying || FSM_STATE_IS_EXPIRED(p2->status)) {
continue;
}
isakmp_ph2expire(p2);
found++;
}
LIST_FOREACH_SAFE(p, &session->ph1tree, ph1ofsession_chain, next) {
if (p->is_dying || FSM_STATE_IS_EXPIRED(p->status)) {
continue;
}
ike_session_purge_ph2s_by_ph1(p);
if (FSM_STATE_IS_ESTABLISHED(p->status))
isakmp_info_send_d1(p);
isakmp_ph1expire(p);
found++;
}
return found;
}
#ifdef ENABLE_HYBRID
int
ike_session_purgephXbydstaddrwop(struct sockaddr_storage *remote)
{
int found = 0;
ike_session_t *session = NULL;
ike_session_t *next_session = NULL;
phase1_handle_t *p;
phase2_handle_t *p2;
LIST_FOREACH_SAFE(session, &ike_session_tree, chain, next_session) {
LIST_FOREACH(p2, &session->ph2tree, ph2ofsession_chain) {
if (p2->is_dying || FSM_STATE_IS_EXPIRED(p2->status)) {
continue;
}
if (cmpsaddrwop(remote, p2->dst) == 0) {
plog(ASL_LEVEL_NOTICE,
"in %s... purging Phase 2 structures\n", __FUNCTION__);
if (FSM_STATE_IS_ESTABLISHED(p2->status))
isakmp_info_send_d2(p2);
isakmp_ph2expire(p2);
found++;
}
}
LIST_FOREACH(p, &session->ph1tree, ph1ofsession_chain) {
if (p->is_dying || FSM_STATE_IS_EXPIRED(p->status)) {
continue;
}
if (cmpsaddrwop(remote, p->remote) == 0) {
plog(ASL_LEVEL_NOTICE,
"in %s... purging Phase 1 and related Phase 2 structures\n", __FUNCTION__);
ike_session_purge_ph2s_by_ph1(p);
if (FSM_STATE_IS_ESTABLISHED(p->status))
isakmp_info_send_d1(p);
isakmp_ph1expire(p);
found++;
}
}
}
return found;
}
void
ike_session_purgephXbyspid(u_int32_t spid, int del_boundph1)
{
ike_session_t *session = NULL;
ike_session_t *next_session = NULL;
phase2_handle_t *iph2 = NULL;
phase2_handle_t *next_iph2 = NULL;
phase1_handle_t *iph1 = NULL;
phase1_handle_t *next_iph1 = NULL;
LIST_FOREACH_SAFE(session, &ike_session_tree, chain, next_session) {
LIST_FOREACH_SAFE(iph2, &session->ph2tree, ph2ofsession_chain, next_iph2) {
if (spid == iph2->spid) {
if (iph2->is_dying || FSM_STATE_IS_EXPIRED(iph2->status)) {
continue;
}
if (FSM_STATE_IS_ESTABLISHED(iph2->status)) {
isakmp_info_send_d2(iph2);
}
ike_session_stopped_by_controller(iph2->parent_session,
ike_session_stopped_by_flush);
isakmp_ph2expire(iph2); }
}
LIST_FOREACH_SAFE(iph2, &session->ph2tree, ph2ofsession_chain, next_iph2) {
if (spid == iph2->spid) {
if (del_boundph1 && iph2->parent_session) {
LIST_FOREACH_SAFE(iph1, &iph2->parent_session->ph1tree, ph1ofsession_chain, next_iph1) {
if (iph1->is_dying || FSM_STATE_IS_EXPIRED(iph1->status)) {
continue;
}
if (FSM_STATE_IS_ESTABLISHED(iph1->status)) {
isakmp_info_send_d1(iph1);
}
isakmp_ph1expire(iph1);
}
}
}
}
}
}
#endif
#ifdef ENABLE_DPD
int
ike_session_ph1_force_dpd (struct sockaddr_storage *remote)
{
int status = -1;
ike_session_t *session = NULL;
phase1_handle_t *p = NULL;
LIST_FOREACH(session, &ike_session_tree, chain) {
LIST_FOREACH(p, &session->ph1tree, ph1ofsession_chain) {
if (cmpsaddrwop(remote, p->remote) == 0) {
if (FSM_STATE_IS_ESTABLISHED(p->status) &&
!p->is_dying &&
p->dpd_support &&
p->rmconf->dpd_interval) {
if(!p->dpd_fails) {
isakmp_info_send_r_u(p);
status = 0;
} else {
plog(ASL_LEVEL_NOTICE, "Skipping forced-DPD for Phase 1 (dpd already in progress).\n");
}
if (p->parent_session) {
p->parent_session->controller_awaiting_peer_resp = 1;
}
} else {
plog(ASL_LEVEL_NOTICE, "Skipping forced-DPD for Phase 1 (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)
{
ike_session_t *session = NULL;
ike_session_t *next_session = NULL;
phase2_handle_t *iph2 = NULL;
phase2_handle_t *next_iph2 = NULL;
phase1_handle_t *iph1 = NULL;
phase1_handle_t *next_iph1 = NULL;
LIST_FOREACH_SAFE(session, &ike_session_tree, chain, next_session) {
LIST_FOREACH_SAFE(iph1, &session->ph1tree, ph1ofsession_chain, next_iph1) {
if (iph1->parent_session && iph1->parent_session->is_asserted) {
plog(ASL_LEVEL_NOTICE, "Skipping sweep of Phase 1 %s because it's been asserted.\n",
isakmp_pindex(&iph1->index, 0));
continue;
}
if (iph1->is_dying || FSM_STATE_IS_EXPIRED(iph1->status)) {
plog(ASL_LEVEL_NOTICE, "Skipping sweep of Phase 1 %s because it's already expired.\n",
isakmp_pindex(&iph1->index, 0));
continue;
}
if (iph1->sce) {
time_t xtime;
if (sched_get_time(iph1->sce, &xtime)) {
if (xtime <= swept_at) {
SCHED_KILL(iph1->sce);
SCHED_KILL(iph1->sce_rekey);
iph1->is_dying = 1;
fsm_set_state(&iph1->status, IKEV1_STATE_PHASE1_EXPIRED);
ike_session_update_ph1_ph2tree(iph1); iph1->sce = sched_new(1, isakmp_ph1delete_stub, iph1);
plog(ASL_LEVEL_NOTICE, "Phase 1 %s expired while sleeping: quick deletion.\n",
isakmp_pindex(&iph1->index, 0));
}
}
}
if (iph1->sce_rekey) {
time_t xtime;
if (sched_get_time(iph1->sce_rekey, &xtime)) {
if (FSM_STATE_IS_EXPIRED(iph1->status) || xtime <= swept_at) {
SCHED_KILL(iph1->sce_rekey);
}
}
}
if (iph1->scr) {
time_t xtime;
if (sched_get_time(iph1->scr, &xtime)) {
if (FSM_STATE_IS_EXPIRED(iph1->status) || xtime <= swept_at) {
SCHED_KILL(iph1->scr);
}
}
}
#ifdef ENABLE_DPD
if (iph1->dpd_r_u) {
time_t xtime;
if (sched_get_time(iph1->dpd_r_u, &xtime)) {
if (FSM_STATE_IS_EXPIRED(iph1->status) || xtime <= swept_at) {
SCHED_KILL(iph1->dpd_r_u);
}
}
}
#endif
}
LIST_FOREACH_SAFE(iph2, &session->ph2tree, ph2ofsession_chain, next_iph2) {
if (iph2->parent_session && iph2->parent_session->is_asserted) {
plog(ASL_LEVEL_NOTICE, "Skipping sweep of Phase 2 because it's been asserted.\n");
continue;
}
if (iph2->is_dying || FSM_STATE_IS_EXPIRED(iph2->status)) {
plog(ASL_LEVEL_NOTICE, "Skipping sweep of Phase 2 because it's already expired.\n");
continue;
}
if (iph2->sce) {
time_t xtime;
if (sched_get_time(iph2->sce, &xtime)) {
if (xtime <= swept_at) {
fsm_set_state(&iph2->status, IKEV1_STATE_PHASE2_EXPIRED);
iph2->is_dying = 1;
isakmp_ph2expire(iph2); ike_session_stopped_by_controller(iph2->parent_session,
ike_session_stopped_by_sleepwake);
plog(ASL_LEVEL_NOTICE, "Phase 2 expired while sleeping: quick deletion.\n");
}
}
}
if (iph2->scr) {
time_t xtime;
if (sched_get_time(iph2->scr, &xtime)) {
if (FSM_STATE_IS_EXPIRED(iph2->status) || xtime <= swept_at) {
SCHED_KILL(iph2->scr);
}
}
}
}
}
ike_session_sweep_sleepwake();
}
uint32_t
iph1_get_remote_v4_address(phase1_handle_t *iph1)
{
uint32_t address = 0;
if (iph1->remote->ss_family == AF_INET) {
address = ((struct sockaddr_in *)iph1->remote)->sin_addr.s_addr;
} else if (iph1->remote->ss_family == AF_INET6 &&
iph1->nat64_prefix.length) {
if (!nw_nat64_extract_v4(&iph1->nat64_prefix, &((struct sockaddr_in6 *)iph1->remote)->sin6_addr, (struct in_addr *)&address)) {
plog(ASL_LEVEL_ERR, "Failed to extract IPv4 from Phase 1 IPv6 address.\n");
}
} else {
plog(ASL_LEVEL_ERR, "Failed to get IPv4 address for Phase 1 (family=%u, NAT64Prefix=%u)\n",
iph1->remote->ss_family,
iph1->nat64_prefix.length);
}
return address;
}
uint32_t
iph2_get_remote_v4_address(phase2_handle_t *iph2)
{
uint32_t address = 0;
if (iph2->dst->ss_family == AF_INET) {
address = ((struct sockaddr_in *)iph2->dst)->sin_addr.s_addr;
} else if (iph2->dst->ss_family == AF_INET6 &&
iph2->nat64_prefix.length) {
if (!nw_nat64_extract_v4(&iph2->nat64_prefix, &((struct sockaddr_in6 *)iph2->dst)->sin6_addr, (struct in_addr *)&address)) {
plog(ASL_LEVEL_ERR, "Failed to extract IPv4 from Phase 2 IPv6 address.\n");
}
} else {
plog(ASL_LEVEL_ERR, "Failed to get IPv4 address for Phase 2 (family=%u, NAT64Prefix=%u)\n",
iph2->dst->ss_family,
iph2->nat64_prefix.length);
}
return address;
}