882 lines
21 KiB
C
882 lines
21 KiB
C
/*
|
|
* Copyright (C) 2008 Martin Willi
|
|
* Hochschule fuer Technik Rapperswil
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation; either version 2 of the License, or (at your
|
|
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* for more details.
|
|
*/
|
|
|
|
#include "ha_dispatcher.h"
|
|
|
|
#include <daemon.h>
|
|
#include <processing/jobs/callback_job.h>
|
|
|
|
typedef struct private_ha_dispatcher_t private_ha_dispatcher_t;
|
|
|
|
/**
|
|
* Private data of an ha_dispatcher_t object.
|
|
*/
|
|
struct private_ha_dispatcher_t {
|
|
|
|
/**
|
|
* Public ha_dispatcher_t interface.
|
|
*/
|
|
ha_dispatcher_t public;
|
|
|
|
/**
|
|
* socket to pull messages from
|
|
*/
|
|
ha_socket_t *socket;
|
|
|
|
/**
|
|
* segments to control
|
|
*/
|
|
ha_segments_t *segments;
|
|
|
|
/**
|
|
* Cache for resync
|
|
*/
|
|
ha_cache_t *cache;
|
|
|
|
/**
|
|
* Kernel helper
|
|
*/
|
|
ha_kernel_t *kernel;
|
|
|
|
/**
|
|
* HA enabled pool
|
|
*/
|
|
ha_attribute_t *attr;
|
|
|
|
/**
|
|
* Dispatcher job
|
|
*/
|
|
callback_job_t *job;
|
|
};
|
|
|
|
/**
|
|
* Quick and dirty hack implementation of diffie_hellman_t.get_shared_secret
|
|
*/
|
|
static status_t get_shared_secret(diffie_hellman_t *this, chunk_t *secret)
|
|
{
|
|
*secret = chunk_clone((*(chunk_t*)this->destroy));
|
|
return SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* Process messages of type IKE_ADD
|
|
*/
|
|
static void process_ike_add(private_ha_dispatcher_t *this, ha_message_t *message)
|
|
{
|
|
ha_message_attribute_t attribute;
|
|
ha_message_value_t value;
|
|
enumerator_t *enumerator;
|
|
ike_sa_t *ike_sa = NULL, *old_sa = NULL;
|
|
u_int16_t encr = 0, len = 0, integ = 0, prf = 0, old_prf = PRF_UNDEFINED;
|
|
chunk_t nonce_i = chunk_empty, nonce_r = chunk_empty;
|
|
chunk_t secret = chunk_empty, old_skd = chunk_empty;
|
|
|
|
enumerator = message->create_attribute_enumerator(message);
|
|
while (enumerator->enumerate(enumerator, &attribute, &value))
|
|
{
|
|
switch (attribute)
|
|
{
|
|
case HA_IKE_ID:
|
|
ike_sa = ike_sa_create(value.ike_sa_id);
|
|
break;
|
|
case HA_IKE_REKEY_ID:
|
|
old_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager,
|
|
value.ike_sa_id);
|
|
break;
|
|
case HA_NONCE_I:
|
|
nonce_i = value.chunk;
|
|
break;
|
|
case HA_NONCE_R:
|
|
nonce_r = value.chunk;
|
|
break;
|
|
case HA_SECRET:
|
|
secret = value.chunk;
|
|
break;
|
|
case HA_OLD_SKD:
|
|
old_skd = value.chunk;
|
|
break;
|
|
case HA_ALG_ENCR:
|
|
encr = value.u16;
|
|
break;
|
|
case HA_ALG_ENCR_LEN:
|
|
len = value.u16;
|
|
break;
|
|
case HA_ALG_INTEG:
|
|
integ = value.u16;
|
|
break;
|
|
case HA_ALG_PRF:
|
|
prf = value.u16;
|
|
break;
|
|
case HA_ALG_OLD_PRF:
|
|
old_prf = value.u16;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
|
|
if (ike_sa)
|
|
{
|
|
proposal_t *proposal;
|
|
keymat_t *keymat;
|
|
/* quick and dirty hack of a DH implementation ;-) */
|
|
diffie_hellman_t dh = { .get_shared_secret = get_shared_secret,
|
|
.destroy = (void*)&secret };
|
|
|
|
proposal = proposal_create(PROTO_IKE, 0);
|
|
keymat = ike_sa->get_keymat(ike_sa);
|
|
if (integ)
|
|
{
|
|
proposal->add_algorithm(proposal, INTEGRITY_ALGORITHM, integ, 0);
|
|
}
|
|
if (encr)
|
|
{
|
|
proposal->add_algorithm(proposal, ENCRYPTION_ALGORITHM, encr, len);
|
|
}
|
|
if (prf)
|
|
{
|
|
proposal->add_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, prf, 0);
|
|
}
|
|
charon->bus->set_sa(charon->bus, ike_sa);
|
|
if (keymat->derive_ike_keys(keymat, proposal, &dh, nonce_i, nonce_r,
|
|
ike_sa->get_id(ike_sa), old_prf, old_skd))
|
|
{
|
|
if (old_sa)
|
|
{
|
|
peer_cfg_t *peer_cfg = old_sa->get_peer_cfg(old_sa);
|
|
|
|
if (peer_cfg)
|
|
{
|
|
ike_sa->set_peer_cfg(ike_sa, peer_cfg);
|
|
ike_sa->inherit(ike_sa, old_sa);
|
|
}
|
|
charon->ike_sa_manager->checkin_and_destroy(
|
|
charon->ike_sa_manager, old_sa);
|
|
old_sa = NULL;
|
|
}
|
|
ike_sa->set_state(ike_sa, IKE_CONNECTING);
|
|
this->cache->cache(this->cache, ike_sa, message);
|
|
message = NULL;
|
|
charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
|
|
}
|
|
else
|
|
{
|
|
DBG1(DBG_IKE, "HA keymat derivation failed");
|
|
ike_sa->destroy(ike_sa);
|
|
}
|
|
charon->bus->set_sa(charon->bus, NULL);
|
|
proposal->destroy(proposal);
|
|
}
|
|
if (old_sa)
|
|
{
|
|
charon->ike_sa_manager->checkin(charon->ike_sa_manager, old_sa);
|
|
}
|
|
DESTROY_IF(message);
|
|
}
|
|
|
|
/**
|
|
* Apply a condition flag to the IKE_SA if it is in set
|
|
*/
|
|
static void set_condition(ike_sa_t *ike_sa, ike_condition_t set,
|
|
ike_condition_t flag)
|
|
{
|
|
ike_sa->set_condition(ike_sa, flag, flag & set);
|
|
}
|
|
|
|
/**
|
|
* Apply a extension flag to the IKE_SA if it is in set
|
|
*/
|
|
static void set_extension(ike_sa_t *ike_sa, ike_extension_t set,
|
|
ike_extension_t flag)
|
|
{
|
|
if (flag & set)
|
|
{
|
|
ike_sa->enable_extension(ike_sa, flag);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Process messages of type IKE_UPDATE
|
|
*/
|
|
static void process_ike_update(private_ha_dispatcher_t *this,
|
|
ha_message_t *message)
|
|
{
|
|
ha_message_attribute_t attribute;
|
|
ha_message_value_t value;
|
|
enumerator_t *enumerator;
|
|
ike_sa_t *ike_sa = NULL;
|
|
peer_cfg_t *peer_cfg = NULL;
|
|
auth_cfg_t *auth;
|
|
bool received_vip = FALSE;
|
|
|
|
enumerator = message->create_attribute_enumerator(message);
|
|
while (enumerator->enumerate(enumerator, &attribute, &value))
|
|
{
|
|
if (attribute != HA_IKE_ID && ike_sa == NULL)
|
|
{
|
|
/* must be first attribute */
|
|
break;
|
|
}
|
|
switch (attribute)
|
|
{
|
|
case HA_IKE_ID:
|
|
ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager,
|
|
value.ike_sa_id);
|
|
break;
|
|
case HA_LOCAL_ID:
|
|
ike_sa->set_my_id(ike_sa, value.id->clone(value.id));
|
|
break;
|
|
case HA_REMOTE_ID:
|
|
ike_sa->set_other_id(ike_sa, value.id->clone(value.id));
|
|
break;
|
|
case HA_REMOTE_EAP_ID:
|
|
auth = auth_cfg_create();
|
|
auth->add(auth, AUTH_RULE_EAP_IDENTITY, value.id->clone(value.id));
|
|
ike_sa->add_auth_cfg(ike_sa, FALSE, auth);
|
|
break;
|
|
case HA_LOCAL_ADDR:
|
|
ike_sa->set_my_host(ike_sa, value.host->clone(value.host));
|
|
break;
|
|
case HA_REMOTE_ADDR:
|
|
ike_sa->set_other_host(ike_sa, value.host->clone(value.host));
|
|
break;
|
|
case HA_LOCAL_VIP:
|
|
ike_sa->set_virtual_ip(ike_sa, TRUE, value.host);
|
|
break;
|
|
case HA_REMOTE_VIP:
|
|
ike_sa->set_virtual_ip(ike_sa, FALSE, value.host);
|
|
received_vip = TRUE;
|
|
break;
|
|
case HA_ADDITIONAL_ADDR:
|
|
ike_sa->add_additional_address(ike_sa,
|
|
value.host->clone(value.host));
|
|
break;
|
|
case HA_CONFIG_NAME:
|
|
peer_cfg = charon->backends->get_peer_cfg_by_name(
|
|
charon->backends, value.str);
|
|
if (peer_cfg)
|
|
{
|
|
ike_sa->set_peer_cfg(ike_sa, peer_cfg);
|
|
peer_cfg->destroy(peer_cfg);
|
|
}
|
|
else
|
|
{
|
|
DBG1(DBG_IKE, "HA is missing nodes peer configuration");
|
|
}
|
|
break;
|
|
case HA_EXTENSIONS:
|
|
set_extension(ike_sa, value.u32, EXT_NATT);
|
|
set_extension(ike_sa, value.u32, EXT_MOBIKE);
|
|
set_extension(ike_sa, value.u32, EXT_HASH_AND_URL);
|
|
break;
|
|
case HA_CONDITIONS:
|
|
set_condition(ike_sa, value.u32, COND_NAT_ANY);
|
|
set_condition(ike_sa, value.u32, COND_NAT_HERE);
|
|
set_condition(ike_sa, value.u32, COND_NAT_THERE);
|
|
set_condition(ike_sa, value.u32, COND_NAT_FAKE);
|
|
set_condition(ike_sa, value.u32, COND_EAP_AUTHENTICATED);
|
|
set_condition(ike_sa, value.u32, COND_CERTREQ_SEEN);
|
|
set_condition(ike_sa, value.u32, COND_ORIGINAL_INITIATOR);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
|
|
if (ike_sa)
|
|
{
|
|
if (ike_sa->get_state(ike_sa) == IKE_CONNECTING &&
|
|
ike_sa->get_peer_cfg(ike_sa))
|
|
{
|
|
DBG1(DBG_CFG, "installed HA passive IKE_SA '%s' %H[%Y]...%H[%Y]",
|
|
ike_sa->get_name(ike_sa),
|
|
ike_sa->get_my_host(ike_sa), ike_sa->get_my_id(ike_sa),
|
|
ike_sa->get_other_host(ike_sa), ike_sa->get_other_id(ike_sa));
|
|
ike_sa->set_state(ike_sa, IKE_PASSIVE);
|
|
}
|
|
if (received_vip)
|
|
{
|
|
host_t *vip;
|
|
char *pool;
|
|
|
|
peer_cfg = ike_sa->get_peer_cfg(ike_sa);
|
|
vip = ike_sa->get_virtual_ip(ike_sa, FALSE);
|
|
if (peer_cfg && vip)
|
|
{
|
|
pool = peer_cfg->get_pool(peer_cfg);
|
|
if (pool)
|
|
{
|
|
this->attr->reserve(this->attr, pool, vip);
|
|
}
|
|
}
|
|
}
|
|
this->cache->cache(this->cache, ike_sa, message);
|
|
charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
|
|
}
|
|
else
|
|
{
|
|
DBG1(DBG_CFG, "passive HA IKE_SA to update not found");
|
|
message->destroy(message);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Process messages of type IKE_MID_INITIATOR/RESPONDER
|
|
*/
|
|
static void process_ike_mid(private_ha_dispatcher_t *this,
|
|
ha_message_t *message, bool initiator)
|
|
{
|
|
ha_message_attribute_t attribute;
|
|
ha_message_value_t value;
|
|
enumerator_t *enumerator;
|
|
ike_sa_t *ike_sa = NULL;
|
|
u_int32_t mid = 0;
|
|
|
|
enumerator = message->create_attribute_enumerator(message);
|
|
while (enumerator->enumerate(enumerator, &attribute, &value))
|
|
{
|
|
switch (attribute)
|
|
{
|
|
case HA_IKE_ID:
|
|
ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager,
|
|
value.ike_sa_id);
|
|
break;
|
|
case HA_MID:
|
|
mid = value.u32;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
|
|
if (ike_sa)
|
|
{
|
|
if (mid)
|
|
{
|
|
ike_sa->set_message_id(ike_sa, initiator, mid);
|
|
}
|
|
this->cache->cache(this->cache, ike_sa, message);
|
|
charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
|
|
}
|
|
else
|
|
{
|
|
message->destroy(message);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Process messages of type IKE_DELETE
|
|
*/
|
|
static void process_ike_delete(private_ha_dispatcher_t *this,
|
|
ha_message_t *message)
|
|
{
|
|
ha_message_attribute_t attribute;
|
|
ha_message_value_t value;
|
|
enumerator_t *enumerator;
|
|
ike_sa_t *ike_sa = NULL;
|
|
|
|
enumerator = message->create_attribute_enumerator(message);
|
|
while (enumerator->enumerate(enumerator, &attribute, &value))
|
|
{
|
|
switch (attribute)
|
|
{
|
|
case HA_IKE_ID:
|
|
ike_sa = charon->ike_sa_manager->checkout(
|
|
charon->ike_sa_manager, value.ike_sa_id);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
if (ike_sa)
|
|
{
|
|
this->cache->cache(this->cache, ike_sa, message);
|
|
charon->ike_sa_manager->checkin_and_destroy(
|
|
charon->ike_sa_manager, ike_sa);
|
|
}
|
|
else
|
|
{
|
|
message->destroy(message);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Lookup a child cfg from the peer cfg by name
|
|
*/
|
|
static child_cfg_t* find_child_cfg(ike_sa_t *ike_sa, char *name)
|
|
{
|
|
peer_cfg_t *peer_cfg;
|
|
child_cfg_t *current, *found = NULL;
|
|
enumerator_t *enumerator;
|
|
|
|
peer_cfg = ike_sa->get_peer_cfg(ike_sa);
|
|
if (peer_cfg)
|
|
{
|
|
enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg);
|
|
while (enumerator->enumerate(enumerator, ¤t))
|
|
{
|
|
if (streq(current->get_name(current), name))
|
|
{
|
|
found = current;
|
|
break;
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
}
|
|
return found;
|
|
}
|
|
|
|
/**
|
|
* Process messages of type CHILD_ADD
|
|
*/
|
|
static void process_child_add(private_ha_dispatcher_t *this,
|
|
ha_message_t *message)
|
|
{
|
|
ha_message_attribute_t attribute;
|
|
ha_message_value_t value;
|
|
enumerator_t *enumerator;
|
|
ike_sa_t *ike_sa = NULL;
|
|
char *config_name = "";
|
|
child_cfg_t *config = NULL;
|
|
child_sa_t *child_sa;
|
|
proposal_t *proposal;
|
|
keymat_t *keymat;
|
|
bool initiator = FALSE, failed = FALSE;
|
|
u_int32_t inbound_spi = 0, outbound_spi = 0;
|
|
u_int16_t inbound_cpi = 0, outbound_cpi = 0;
|
|
u_int8_t mode = MODE_TUNNEL, ipcomp = 0;
|
|
u_int16_t encr = ENCR_UNDEFINED, integ = AUTH_UNDEFINED, len = 0;
|
|
u_int16_t esn = NO_EXT_SEQ_NUMBERS;
|
|
u_int seg_i, seg_o;
|
|
chunk_t nonce_i = chunk_empty, nonce_r = chunk_empty, secret = chunk_empty;
|
|
chunk_t encr_i, integ_i, encr_r, integ_r;
|
|
linked_list_t *local_ts, *remote_ts;
|
|
/* quick and dirty hack of a DH implementation */
|
|
diffie_hellman_t dh = { .get_shared_secret = get_shared_secret,
|
|
.destroy = (void*)&secret };
|
|
|
|
enumerator = message->create_attribute_enumerator(message);
|
|
while (enumerator->enumerate(enumerator, &attribute, &value))
|
|
{
|
|
switch (attribute)
|
|
{
|
|
case HA_IKE_ID:
|
|
ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager,
|
|
value.ike_sa_id);
|
|
break;
|
|
case HA_CONFIG_NAME:
|
|
config_name = value.str;
|
|
break;
|
|
case HA_INITIATOR:
|
|
initiator = value.u8;
|
|
break;
|
|
case HA_INBOUND_SPI:
|
|
inbound_spi = value.u32;
|
|
break;
|
|
case HA_OUTBOUND_SPI:
|
|
outbound_spi = value.u32;
|
|
break;
|
|
case HA_INBOUND_CPI:
|
|
inbound_cpi = value.u32;
|
|
break;
|
|
case HA_OUTBOUND_CPI:
|
|
outbound_cpi = value.u32;
|
|
break;
|
|
case HA_IPSEC_MODE:
|
|
mode = value.u8;
|
|
break;
|
|
case HA_IPCOMP:
|
|
ipcomp = value.u8;
|
|
break;
|
|
case HA_ALG_ENCR:
|
|
encr = value.u16;
|
|
break;
|
|
case HA_ALG_ENCR_LEN:
|
|
len = value.u16;
|
|
break;
|
|
case HA_ALG_INTEG:
|
|
integ = value.u16;
|
|
break;
|
|
case HA_ESN:
|
|
esn = value.u16;
|
|
break;
|
|
case HA_NONCE_I:
|
|
nonce_i = value.chunk;
|
|
break;
|
|
case HA_NONCE_R:
|
|
nonce_r = value.chunk;
|
|
break;
|
|
case HA_SECRET:
|
|
secret = value.chunk;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
|
|
if (!ike_sa)
|
|
{
|
|
DBG1(DBG_CHD, "IKE_SA for HA CHILD_SA not found");
|
|
message->destroy(message);
|
|
return;
|
|
}
|
|
config = find_child_cfg(ike_sa, config_name);
|
|
if (!config)
|
|
{
|
|
DBG1(DBG_CHD, "HA is missing nodes child configuration");
|
|
charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
|
|
message->destroy(message);
|
|
return;
|
|
}
|
|
|
|
child_sa = child_sa_create(ike_sa->get_my_host(ike_sa),
|
|
ike_sa->get_other_host(ike_sa), config, 0,
|
|
ike_sa->has_condition(ike_sa, COND_NAT_ANY));
|
|
child_sa->set_mode(child_sa, mode);
|
|
child_sa->set_protocol(child_sa, PROTO_ESP);
|
|
child_sa->set_ipcomp(child_sa, ipcomp);
|
|
|
|
proposal = proposal_create(PROTO_ESP, 0);
|
|
if (integ)
|
|
{
|
|
proposal->add_algorithm(proposal, INTEGRITY_ALGORITHM, integ, 0);
|
|
}
|
|
if (encr)
|
|
{
|
|
proposal->add_algorithm(proposal, ENCRYPTION_ALGORITHM, encr, len);
|
|
}
|
|
proposal->add_algorithm(proposal, EXTENDED_SEQUENCE_NUMBERS, esn, 0);
|
|
keymat = ike_sa->get_keymat(ike_sa);
|
|
|
|
if (!keymat->derive_child_keys(keymat, proposal, secret.ptr ? &dh : NULL,
|
|
nonce_i, nonce_r, &encr_i, &integ_i, &encr_r, &integ_r))
|
|
{
|
|
DBG1(DBG_CHD, "HA CHILD_SA key derivation failed");
|
|
child_sa->destroy(child_sa);
|
|
proposal->destroy(proposal);
|
|
charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
|
|
return;
|
|
}
|
|
child_sa->set_proposal(child_sa, proposal);
|
|
child_sa->set_state(child_sa, CHILD_INSTALLING);
|
|
proposal->destroy(proposal);
|
|
|
|
/* TODO: Change CHILD_SA API to avoid cloning twice */
|
|
local_ts = linked_list_create();
|
|
remote_ts = linked_list_create();
|
|
enumerator = message->create_attribute_enumerator(message);
|
|
while (enumerator->enumerate(enumerator, &attribute, &value))
|
|
{
|
|
switch (attribute)
|
|
{
|
|
case HA_LOCAL_TS:
|
|
local_ts->insert_last(local_ts, value.ts->clone(value.ts));
|
|
break;
|
|
case HA_REMOTE_TS:
|
|
remote_ts->insert_last(remote_ts, value.ts->clone(value.ts));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
|
|
if (initiator)
|
|
{
|
|
if (child_sa->install(child_sa, encr_r, integ_r, inbound_spi,
|
|
inbound_cpi, TRUE, TRUE, local_ts, remote_ts) != SUCCESS ||
|
|
child_sa->install(child_sa, encr_i, integ_i, outbound_spi,
|
|
outbound_cpi, FALSE, TRUE, local_ts, remote_ts) != SUCCESS)
|
|
{
|
|
failed = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (child_sa->install(child_sa, encr_i, integ_i, inbound_spi,
|
|
inbound_cpi, TRUE, TRUE, local_ts, remote_ts) != SUCCESS ||
|
|
child_sa->install(child_sa, encr_r, integ_r, outbound_spi,
|
|
outbound_cpi, FALSE, TRUE, local_ts, remote_ts) != SUCCESS)
|
|
{
|
|
failed = TRUE;
|
|
}
|
|
}
|
|
chunk_clear(&encr_i);
|
|
chunk_clear(&integ_i);
|
|
chunk_clear(&encr_r);
|
|
chunk_clear(&integ_r);
|
|
|
|
if (failed)
|
|
{
|
|
DBG1(DBG_CHD, "HA CHILD_SA installation failed");
|
|
child_sa->destroy(child_sa);
|
|
local_ts->destroy_offset(local_ts, offsetof(traffic_selector_t, destroy));
|
|
remote_ts->destroy_offset(remote_ts, offsetof(traffic_selector_t, destroy));
|
|
charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
|
|
message->destroy(message);
|
|
return;
|
|
}
|
|
|
|
seg_i = this->kernel->get_segment_spi(this->kernel,
|
|
ike_sa->get_my_host(ike_sa), inbound_spi);
|
|
seg_o = this->kernel->get_segment_spi(this->kernel,
|
|
ike_sa->get_other_host(ike_sa), outbound_spi);
|
|
|
|
DBG1(DBG_CFG, "installed HA CHILD_SA %s{%d} %#R=== %#R "
|
|
"(segment in: %d%s, out: %d%s)", child_sa->get_name(child_sa),
|
|
child_sa->get_reqid(child_sa), local_ts, remote_ts,
|
|
seg_i, this->segments->is_active(this->segments, seg_i) ? "*" : "",
|
|
seg_o, this->segments->is_active(this->segments, seg_o) ? "*" : "");
|
|
child_sa->add_policies(child_sa, local_ts, remote_ts);
|
|
local_ts->destroy_offset(local_ts, offsetof(traffic_selector_t, destroy));
|
|
remote_ts->destroy_offset(remote_ts, offsetof(traffic_selector_t, destroy));
|
|
|
|
child_sa->set_state(child_sa, CHILD_INSTALLED);
|
|
ike_sa->add_child_sa(ike_sa, child_sa);
|
|
message->destroy(message);
|
|
charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
|
|
}
|
|
|
|
/**
|
|
* Process messages of type CHILD_DELETE
|
|
*/
|
|
static void process_child_delete(private_ha_dispatcher_t *this,
|
|
ha_message_t *message)
|
|
{
|
|
ha_message_attribute_t attribute;
|
|
ha_message_value_t value;
|
|
enumerator_t *enumerator;
|
|
ike_sa_t *ike_sa = NULL;
|
|
child_sa_t *child_sa;
|
|
u_int32_t spi = 0;
|
|
|
|
enumerator = message->create_attribute_enumerator(message);
|
|
while (enumerator->enumerate(enumerator, &attribute, &value))
|
|
{
|
|
switch (attribute)
|
|
{
|
|
case HA_IKE_ID:
|
|
ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager,
|
|
value.ike_sa_id);
|
|
break;
|
|
case HA_INBOUND_SPI:
|
|
spi = value.u32;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
|
|
if (ike_sa)
|
|
{
|
|
child_sa = ike_sa->get_child_sa(ike_sa, PROTO_ESP, spi, TRUE);
|
|
if (child_sa)
|
|
{
|
|
ike_sa->destroy_child_sa(ike_sa, PROTO_ESP, spi);
|
|
}
|
|
charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
|
|
}
|
|
message->destroy(message);
|
|
}
|
|
|
|
/**
|
|
* Process messages of type SEGMENT_TAKE/DROP
|
|
*/
|
|
static void process_segment(private_ha_dispatcher_t *this,
|
|
ha_message_t *message, bool take)
|
|
{
|
|
ha_message_attribute_t attribute;
|
|
ha_message_value_t value;
|
|
enumerator_t *enumerator;
|
|
|
|
enumerator = message->create_attribute_enumerator(message);
|
|
while (enumerator->enumerate(enumerator, &attribute, &value))
|
|
{
|
|
switch (attribute)
|
|
{
|
|
case HA_SEGMENT:
|
|
if (take)
|
|
{
|
|
DBG1(DBG_CFG, "remote node takes segment %d", value.u16);
|
|
this->segments->deactivate(this->segments, value.u16, FALSE);
|
|
}
|
|
else
|
|
{
|
|
DBG1(DBG_CFG, "remote node drops segment %d", value.u16);
|
|
this->segments->activate(this->segments, value.u16, FALSE);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
message->destroy(message);
|
|
}
|
|
|
|
/**
|
|
* Process messages of type STATUS
|
|
*/
|
|
static void process_status(private_ha_dispatcher_t *this,
|
|
ha_message_t *message)
|
|
{
|
|
ha_message_attribute_t attribute;
|
|
ha_message_value_t value;
|
|
enumerator_t *enumerator;
|
|
segment_mask_t mask = 0;
|
|
|
|
enumerator = message->create_attribute_enumerator(message);
|
|
while (enumerator->enumerate(enumerator, &attribute, &value))
|
|
{
|
|
switch (attribute)
|
|
{
|
|
case HA_SEGMENT:
|
|
mask |= SEGMENTS_BIT(value.u16);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
|
|
this->segments->handle_status(this->segments, mask);
|
|
message->destroy(message);
|
|
}
|
|
|
|
/**
|
|
* Process messages of type RESYNC
|
|
*/
|
|
static void process_resync(private_ha_dispatcher_t *this,
|
|
ha_message_t *message)
|
|
{
|
|
ha_message_attribute_t attribute;
|
|
ha_message_value_t value;
|
|
enumerator_t *enumerator;
|
|
|
|
enumerator = message->create_attribute_enumerator(message);
|
|
while (enumerator->enumerate(enumerator, &attribute, &value))
|
|
{
|
|
switch (attribute)
|
|
{
|
|
case HA_SEGMENT:
|
|
this->cache->resync(this->cache, value.u16);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
message->destroy(message);
|
|
}
|
|
|
|
/**
|
|
* Dispatcher job function
|
|
*/
|
|
static job_requeue_t dispatch(private_ha_dispatcher_t *this)
|
|
{
|
|
ha_message_t *message;
|
|
ha_message_type_t type;
|
|
|
|
message = this->socket->pull(this->socket);
|
|
type = message->get_type(message);
|
|
if (type != HA_STATUS)
|
|
{
|
|
DBG2(DBG_CFG, "received HA %N message", ha_message_type_names,
|
|
message->get_type(message));
|
|
}
|
|
switch (type)
|
|
{
|
|
case HA_IKE_ADD:
|
|
process_ike_add(this, message);
|
|
break;
|
|
case HA_IKE_UPDATE:
|
|
process_ike_update(this, message);
|
|
break;
|
|
case HA_IKE_MID_INITIATOR:
|
|
process_ike_mid(this, message, TRUE);
|
|
break;
|
|
case HA_IKE_MID_RESPONDER:
|
|
process_ike_mid(this, message, FALSE);
|
|
break;
|
|
case HA_IKE_DELETE:
|
|
process_ike_delete(this, message);
|
|
break;
|
|
case HA_CHILD_ADD:
|
|
process_child_add(this, message);
|
|
break;
|
|
case HA_CHILD_DELETE:
|
|
process_child_delete(this, message);
|
|
break;
|
|
case HA_SEGMENT_DROP:
|
|
process_segment(this, message, FALSE);
|
|
break;
|
|
case HA_SEGMENT_TAKE:
|
|
process_segment(this, message, TRUE);
|
|
break;
|
|
case HA_STATUS:
|
|
process_status(this, message);
|
|
break;
|
|
case HA_RESYNC:
|
|
process_resync(this, message);
|
|
break;
|
|
default:
|
|
DBG1(DBG_CFG, "received unknown HA message type %d", type);
|
|
message->destroy(message);
|
|
break;
|
|
}
|
|
return JOB_REQUEUE_DIRECT;
|
|
}
|
|
|
|
METHOD(ha_dispatcher_t, destroy, void,
|
|
private_ha_dispatcher_t *this)
|
|
{
|
|
this->job->cancel(this->job);
|
|
free(this);
|
|
}
|
|
|
|
/**
|
|
* See header
|
|
*/
|
|
ha_dispatcher_t *ha_dispatcher_create(ha_socket_t *socket,
|
|
ha_segments_t *segments, ha_cache_t *cache,
|
|
ha_kernel_t *kernel, ha_attribute_t *attr)
|
|
{
|
|
private_ha_dispatcher_t *this;
|
|
|
|
|
|
INIT(this,
|
|
.public = {
|
|
.destroy = _destroy,
|
|
},
|
|
.socket = socket,
|
|
.segments = segments,
|
|
.cache = cache,
|
|
.kernel = kernel,
|
|
.attr = attr,
|
|
);
|
|
this->job = callback_job_create_with_prio((callback_job_cb_t)dispatch,
|
|
this, NULL, NULL, JOB_PRIO_CRITICAL);
|
|
lib->processor->queue_job(lib->processor, (job_t*)this->job);
|
|
|
|
return &this->public;
|
|
}
|
|
|