diff options
author | Rene Mayrhofer <rene@mayrhofer.eu.org> | 2010-05-25 19:01:36 +0000 |
---|---|---|
committer | Rene Mayrhofer <rene@mayrhofer.eu.org> | 2010-05-25 19:01:36 +0000 |
commit | 1ac70afcc1f7d6d2738a34308810719b0976d29f (patch) | |
tree | 805f6ce2a15d1a717781d7cbceac8408a74b6b0c /src/libcharon/plugins/ha/ha_dispatcher.c | |
parent | ed7d79f96177044949744da10f4431c1d6242241 (diff) | |
download | vyos-strongswan-1ac70afcc1f7d6d2738a34308810719b0976d29f.tar.gz vyos-strongswan-1ac70afcc1f7d6d2738a34308810719b0976d29f.zip |
[svn-upgrade] Integrating new upstream version, strongswan (4.4.0)
Diffstat (limited to 'src/libcharon/plugins/ha/ha_dispatcher.c')
-rw-r--r-- | src/libcharon/plugins/ha/ha_dispatcher.c | 737 |
1 files changed, 737 insertions, 0 deletions
diff --git a/src/libcharon/plugins/ha/ha_dispatcher.c b/src/libcharon/plugins/ha/ha_dispatcher.c new file mode 100644 index 000000000..7df2f1fa8 --- /dev/null +++ b/src/libcharon/plugins/ha/ha_dispatcher.c @@ -0,0 +1,737 @@ +/* + * 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; + + /** + * 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); + 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); + 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); + } +} + +/** + * 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; + + 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_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); + 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; + case HA_INITIATE_MID: + ike_sa->set_message_id(ike_sa, TRUE, value.u32); + break; + case HA_RESPOND_MID: + ike_sa->set_message_id(ike_sa, FALSE, value.u32); + 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)) + { + ike_sa->set_state(ike_sa, IKE_PASSIVE); + } + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + } +} + +/** + * 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; + + 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); + if (ike_sa) + { + charon->ike_sa_manager->checkin_and_destroy( + charon->ike_sa_manager, ike_sa); + } + break; + default: + break; + } + } + enumerator->destroy(enumerator); +} + +/** + * 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; + 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); + initiator = value.ike_sa_id->is_initiator(value.ike_sa_id); + break; + case HA_CONFIG_NAME: + config_name = value.str; + 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_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"); + 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); + 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); + if (integ) + { + proposal->add_algorithm(proposal, INTEGRITY_ALGORITHM, integ, 0); + } + if (encr) + { + proposal->add_algorithm(proposal, ENCRYPTION_ALGORITHM, encr, len); + } + 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, local_ts, remote_ts) != SUCCESS || + child_sa->install(child_sa, encr_i, integ_i, outbound_spi, + outbound_cpi, FALSE, local_ts, remote_ts) != SUCCESS) + { + failed = TRUE; + } + } + else + { + if (child_sa->install(child_sa, encr_i, integ_i, inbound_spi, + inbound_cpi, TRUE, local_ts, remote_ts) != SUCCESS || + child_sa->install(child_sa, encr_r, integ_r, outbound_spi, + outbound_cpi, FALSE, 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); + return; + } + + 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); + 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; + + 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: + if (ike_sa) + { + ike_sa->destroy_child_sa(ike_sa, PROTO_ESP, value.u32); + } + break; + default: + break; + } + } + if (ike_sa) + { + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + } + enumerator->destroy(enumerator); +} + +/** + * 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); +} + +/** + * 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); +} + +/** + * 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->segments->resync(this->segments, value.u16); + break; + default: + break; + } + } + enumerator->destroy(enumerator); +} + +/** + * Dispatcher job function + */ +static job_requeue_t dispatch(private_ha_dispatcher_t *this) +{ + ha_message_t *message; + + message = this->socket->pull(this->socket); + switch (message->get_type(message)) + { + case HA_IKE_ADD: + process_ike_add(this, message); + break; + case HA_IKE_UPDATE: + process_ike_update(this, message); + 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", + message->get_type(message)); + break; + } + message->destroy(message); + + return JOB_REQUEUE_DIRECT; +} + +/** + * Implementation of ha_dispatcher_t.destroy. + */ +static void destroy(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) +{ + private_ha_dispatcher_t *this = malloc_thing(private_ha_dispatcher_t); + + this->public.destroy = (void(*)(ha_dispatcher_t*))destroy; + + this->socket = socket; + this->segments = segments; + this->job = callback_job_create((callback_job_cb_t)dispatch, + this, NULL, NULL); + charon->processor->queue_job(charon->processor, (job_t*)this->job); + + return &this->public; +} + |