first merge of NATT code
This commit is contained in:
+235
-54
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger
|
||||
* Copyright (C) 2005 Jan Hutter, Martin Willi
|
||||
* Hochschule fuer Technik Rapperswil
|
||||
*
|
||||
@@ -111,6 +112,11 @@ struct private_child_sa_t {
|
||||
* has this CHILD_SA been rekeyed?
|
||||
*/
|
||||
bool rekeyed;
|
||||
|
||||
/**
|
||||
* Specifies if NAT traversal is used
|
||||
*/
|
||||
bool use_natt;
|
||||
|
||||
/**
|
||||
* CHILD_SAs own logger
|
||||
@@ -146,12 +152,54 @@ protocol_id_t get_protocol(private_child_sa_t *this)
|
||||
return this->protocol;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate SPI for a single proposal
|
||||
*/
|
||||
static status_t alloc_proposal(private_child_sa_t *this, proposal_t *proposal)
|
||||
{
|
||||
protocol_id_t protocol = proposal->get_protocol(proposal);
|
||||
|
||||
if (protocol == PROTO_AH)
|
||||
{
|
||||
/* get a new spi for AH, if not already done */
|
||||
if (this->alloc_ah_spi == 0)
|
||||
{
|
||||
if (charon->kernel_interface->get_spi(
|
||||
charon->kernel_interface,
|
||||
this->other.addr, this->me.addr,
|
||||
PROTO_AH, this->reqid,
|
||||
&this->alloc_ah_spi) != SUCCESS)
|
||||
{
|
||||
return FAILED;
|
||||
}
|
||||
}
|
||||
proposal->set_spi(proposal, this->alloc_ah_spi);
|
||||
}
|
||||
if (protocol == PROTO_ESP)
|
||||
{
|
||||
/* get a new spi for ESP, if not already done */
|
||||
if (this->alloc_esp_spi == 0)
|
||||
{
|
||||
if (charon->kernel_interface->get_spi(
|
||||
charon->kernel_interface,
|
||||
this->other.addr, this->me.addr,
|
||||
PROTO_ESP, this->reqid,
|
||||
&this->alloc_esp_spi) != SUCCESS)
|
||||
{
|
||||
return FAILED;
|
||||
}
|
||||
}
|
||||
proposal->set_spi(proposal, this->alloc_esp_spi);
|
||||
}
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Implements child_sa_t.alloc
|
||||
*/
|
||||
static status_t alloc(private_child_sa_t *this, linked_list_t *proposals)
|
||||
{
|
||||
protocol_id_t protocol;
|
||||
iterator_t *iterator;
|
||||
proposal_t *proposal;
|
||||
|
||||
@@ -160,41 +208,11 @@ static status_t alloc(private_child_sa_t *this, linked_list_t *proposals)
|
||||
while(iterator->has_next(iterator))
|
||||
{
|
||||
iterator->current(iterator, (void**)&proposal);
|
||||
protocol = proposal->get_protocol(proposal);
|
||||
|
||||
if (protocol == PROTO_AH)
|
||||
if (alloc_proposal(this, proposal) != SUCCESS)
|
||||
{
|
||||
/* get a new spi for AH, if not already done */
|
||||
if (this->alloc_ah_spi == 0)
|
||||
{
|
||||
if (charon->kernel_interface->get_spi(
|
||||
charon->kernel_interface,
|
||||
this->other.addr, this->me.addr,
|
||||
PROTO_AH, this->reqid,
|
||||
&this->alloc_ah_spi) != SUCCESS)
|
||||
{
|
||||
return FAILED;
|
||||
}
|
||||
}
|
||||
proposal->set_spi(proposal, this->alloc_ah_spi);
|
||||
iterator->destroy(iterator);
|
||||
return FAILED;
|
||||
}
|
||||
if (protocol == PROTO_ESP)
|
||||
{
|
||||
/* get a new spi for ESP, if not already done */
|
||||
if (this->alloc_esp_spi == 0)
|
||||
{
|
||||
if (charon->kernel_interface->get_spi(
|
||||
charon->kernel_interface,
|
||||
this->other.addr, this->me.addr,
|
||||
PROTO_ESP, this->reqid,
|
||||
&this->alloc_esp_spi) != SUCCESS)
|
||||
{
|
||||
return FAILED;
|
||||
}
|
||||
}
|
||||
proposal->set_spi(proposal, this->alloc_esp_spi);
|
||||
}
|
||||
|
||||
}
|
||||
iterator->destroy(iterator);
|
||||
return SUCCESS;
|
||||
@@ -208,6 +226,7 @@ static status_t install(private_child_sa_t *this, proposal_t *proposal, prf_plus
|
||||
algorithm_t int_algo_none = {AUTH_UNDEFINED, 0};
|
||||
host_t *src;
|
||||
host_t *dst;
|
||||
natt_conf_t *natt;
|
||||
status_t status;
|
||||
|
||||
this->protocol = proposal->get_protocol(proposal);
|
||||
@@ -275,17 +294,31 @@ static status_t install(private_child_sa_t *this, proposal_t *proposal, prf_plus
|
||||
int_algo = &int_algo_none;
|
||||
}
|
||||
|
||||
/* setup nat-t */
|
||||
if (this->use_natt)
|
||||
{
|
||||
natt = alloca(sizeof(natt_conf_t));
|
||||
natt->sport = src->get_port(src);
|
||||
natt->dport = dst->get_port(dst);
|
||||
}
|
||||
else
|
||||
{
|
||||
natt = NULL;
|
||||
}
|
||||
|
||||
|
||||
/* send SA down to the kernel */
|
||||
this->logger->log(this->logger, CONTROL|LEVEL2,
|
||||
" SPI 0x%.8x, src %s dst %s",
|
||||
ntohl(spi), src->get_address(src), dst->get_address(dst));
|
||||
status = charon->kernel_interface->add_sa(charon->kernel_interface,
|
||||
src, dst,
|
||||
spi, this->protocol,
|
||||
this->reqid,
|
||||
mine ? 0 : this->soft_lifetime,
|
||||
this->hard_lifetime,
|
||||
enc_algo, int_algo, prf_plus, mine);
|
||||
src, dst,
|
||||
spi, this->protocol,
|
||||
this->reqid,
|
||||
mine ? 0 : this->soft_lifetime,
|
||||
this->hard_lifetime,
|
||||
enc_algo, int_algo,
|
||||
prf_plus, natt, mine);
|
||||
|
||||
this->install_time = time(NULL);
|
||||
|
||||
@@ -301,14 +334,10 @@ static status_t add(private_child_sa_t *this, proposal_t *proposal, prf_plus_t *
|
||||
outbound_spi = proposal->get_spi(proposal);
|
||||
|
||||
/* get SPIs inbound SAs */
|
||||
list = linked_list_create();
|
||||
list->insert_last(list, proposal);
|
||||
if (alloc(this, list) != SUCCESS)
|
||||
if (alloc_proposal(this, proposal) != SUCCESS)
|
||||
{
|
||||
list->destroy(list);
|
||||
return FAILED;
|
||||
}
|
||||
list->destroy(list);
|
||||
inbound_spi = proposal->get_spi(proposal);
|
||||
|
||||
/* install inbound SAs */
|
||||
@@ -409,24 +438,21 @@ static status_t add_policies(private_child_sa_t *this, linked_list_t *my_ts_list
|
||||
policy->me.net, policy->other.net,
|
||||
policy->me.net_mask, policy->other.net_mask,
|
||||
XFRM_POLICY_OUT, policy->upper_proto,
|
||||
this->protocol,
|
||||
this->reqid);
|
||||
this->protocol, this->reqid);
|
||||
|
||||
status |= charon->kernel_interface->add_policy(charon->kernel_interface,
|
||||
this->other.addr, this->me.addr,
|
||||
policy->other.net, policy->me.net,
|
||||
policy->other.net_mask, policy->me.net_mask,
|
||||
XFRM_POLICY_IN, policy->upper_proto,
|
||||
this->protocol,
|
||||
this->reqid);
|
||||
this->protocol, this->reqid);
|
||||
|
||||
status |= charon->kernel_interface->add_policy(charon->kernel_interface,
|
||||
this->other.addr, this->me.addr,
|
||||
policy->other.net, policy->me.net,
|
||||
policy->other.net_mask, policy->me.net_mask,
|
||||
XFRM_POLICY_FWD, policy->upper_proto,
|
||||
this->protocol,
|
||||
this->reqid);
|
||||
this->protocol, this->reqid);
|
||||
|
||||
if (status != SUCCESS)
|
||||
{
|
||||
@@ -513,6 +539,159 @@ static void log_status(private_child_sa_t *this, logger_t *logger, char* name)
|
||||
iterator->destroy(iterator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the host adress/port of a SA
|
||||
*/
|
||||
static status_t update_sa_hosts(private_child_sa_t *this, host_t *new_me, host_t *new_other,
|
||||
int my_changes, int other_changes, bool mine)
|
||||
{
|
||||
host_t *src, *dst, *new_src, *new_dst;
|
||||
int src_changes, dst_changes;
|
||||
status_t status;
|
||||
u_int32_t spi;
|
||||
|
||||
if (mine)
|
||||
{
|
||||
src = this->me.addr;
|
||||
dst = this->other.addr;
|
||||
new_src = new_me;
|
||||
new_dst = new_other;
|
||||
src_changes = my_changes;
|
||||
dst_changes = other_changes;
|
||||
spi = this->me.spi;
|
||||
}
|
||||
else
|
||||
{
|
||||
src = this->other.addr;
|
||||
dst = this->me.addr;
|
||||
new_src = new_other;
|
||||
new_dst = new_me;
|
||||
src_changes = other_changes;
|
||||
dst_changes = my_changes;
|
||||
spi = this->other.spi;
|
||||
}
|
||||
|
||||
this->logger->log(this->logger, CONTROL|LEVEL1,
|
||||
"updating %s SA 0x%x, from %s:%d..%s:%d to %s:%d..%s:%d",
|
||||
mapping_find(protocol_id_m, this->protocol), ntohl(spi),
|
||||
src->get_address(src), src->get_port(src),
|
||||
dst->get_address(dst), dst->get_port(dst),
|
||||
new_src->get_address(new_src), new_src->get_port(new_src),
|
||||
new_dst->get_address(new_dst), new_dst->get_port(new_dst));
|
||||
|
||||
status = charon->kernel_interface->update_sa_hosts(
|
||||
charon->kernel_interface,
|
||||
src, dst, new_src, new_dst,
|
||||
src_changes, dst_changes,
|
||||
spi, this->protocol);
|
||||
|
||||
if (status != SUCCESS)
|
||||
{
|
||||
return FAILED;
|
||||
}
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the host adress/port of a policy
|
||||
*/
|
||||
static status_t update_policy_hosts(private_child_sa_t *this, host_t *new_me, host_t *new_other)
|
||||
{
|
||||
iterator_t *iterator;
|
||||
sa_policy_t *policy;
|
||||
status_t status;
|
||||
|
||||
iterator = this->policies->create_iterator(this->policies, TRUE);
|
||||
while (iterator->iterate(iterator, (void**)&policy))
|
||||
{
|
||||
this->logger->log(this->logger, CONTROL|LEVEL1,
|
||||
"updating policy: %s/%d====%s/%d",
|
||||
policy->me.net->get_address(policy->me.net), policy->me.net_mask,
|
||||
policy->other.net->get_address(policy->other.net), policy->other.net_mask);
|
||||
|
||||
status = charon->kernel_interface->add_policy(
|
||||
charon->kernel_interface,
|
||||
new_me, new_other,
|
||||
policy->me.net, policy->other.net,
|
||||
policy->me.net_mask, policy->other.net_mask,
|
||||
XFRM_POLICY_OUT, policy->upper_proto,
|
||||
this->protocol, this->reqid);
|
||||
|
||||
status |= charon->kernel_interface->add_policy(
|
||||
charon->kernel_interface,
|
||||
new_other, new_me,
|
||||
policy->other.net, policy->me.net,
|
||||
policy->other.net_mask, policy->me.net_mask,
|
||||
XFRM_POLICY_IN, policy->upper_proto,
|
||||
this->protocol, this->reqid);
|
||||
|
||||
status |= charon->kernel_interface->add_policy(
|
||||
charon->kernel_interface,
|
||||
new_other, new_me,
|
||||
policy->other.net, policy->me.net,
|
||||
policy->other.net_mask, policy->me.net_mask,
|
||||
XFRM_POLICY_FWD, policy->upper_proto,
|
||||
this->protocol, this->reqid);
|
||||
|
||||
if (status != SUCCESS)
|
||||
{
|
||||
iterator->destroy(iterator);
|
||||
return FAILED;
|
||||
}
|
||||
}
|
||||
iterator->destroy(iterator);
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of child_sa_t.update_hosts.
|
||||
*/
|
||||
static status_t update_hosts(private_child_sa_t *this, host_t *new_me, host_t *new_other,
|
||||
int my_changes, int other_changes)
|
||||
{
|
||||
if (!my_changes || !other_changes)
|
||||
{
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
/* update our (initator) SAs */
|
||||
if (update_sa_hosts(this, new_me, new_other, my_changes, other_changes, TRUE) != SUCCESS)
|
||||
{
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
/* update his (responder) SAs */
|
||||
if (update_sa_hosts(this, new_me, new_other, my_changes, other_changes, FALSE) != SUCCESS)
|
||||
{
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
/* update policies */
|
||||
if (my_changes & HOST_DIFF_ADDR || other_changes & HOST_DIFF_ADDR)
|
||||
{
|
||||
if (update_policy_hosts(this, new_me, new_other) != SUCCESS)
|
||||
{
|
||||
return FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
/* update hosts */
|
||||
if (my_changes)
|
||||
{
|
||||
this->me.addr->destroy(this->me.addr);
|
||||
this->me.addr = new_me->clone(new_me);
|
||||
}
|
||||
|
||||
if (other_changes)
|
||||
{
|
||||
this->other.addr->destroy(this->other.addr);
|
||||
this->other.addr = new_other->clone(new_other);
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of child_sa_t.destroy.
|
||||
*/
|
||||
@@ -579,9 +758,10 @@ static void destroy(private_child_sa_t *this)
|
||||
* Described in header.
|
||||
*/
|
||||
child_sa_t * child_sa_create(u_int32_t rekey, host_t *me, host_t* other,
|
||||
u_int32_t soft_lifetime, u_int32_t hard_lifetime)
|
||||
u_int32_t soft_lifetime, u_int32_t hard_lifetime,
|
||||
bool use_natt)
|
||||
{
|
||||
static u_int32_t reqid = 2000000000;
|
||||
static u_int32_t reqid = REQID_START;
|
||||
private_child_sa_t *this = malloc_thing(private_child_sa_t);
|
||||
|
||||
/* public functions */
|
||||
@@ -604,6 +784,7 @@ child_sa_t * child_sa_create(u_int32_t rekey, host_t *me, host_t* other,
|
||||
this->other.spi = 0;
|
||||
this->alloc_ah_spi = 0;
|
||||
this->alloc_esp_spi = 0;
|
||||
this->use_natt = use_natt;
|
||||
this->soft_lifetime = soft_lifetime;
|
||||
this->hard_lifetime = hard_lifetime;
|
||||
/* reuse old reqid if we are rekeying an existing CHILD_SA */
|
||||
@@ -612,5 +793,5 @@ child_sa_t * child_sa_create(u_int32_t rekey, host_t *me, host_t* other,
|
||||
this->protocol = PROTO_NONE;
|
||||
this->rekeyed = FALSE;
|
||||
|
||||
return (&this->public);
|
||||
return &this->public;
|
||||
}
|
||||
|
||||
+35
-21
@@ -6,7 +6,8 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2005 Martin Willi
|
||||
* Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger
|
||||
* Copyright (C) 2006 Martin Willi
|
||||
* Hochschule fuer Technik Rapperswil
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@@ -30,16 +31,19 @@
|
||||
#include <config/proposal.h>
|
||||
#include <utils/logger.h>
|
||||
|
||||
/**
|
||||
* Where we should start with reqid enumeration
|
||||
*/
|
||||
#define REQID_START 2000000000
|
||||
|
||||
typedef struct child_sa_t child_sa_t;
|
||||
|
||||
/**
|
||||
* @brief Represents multiple IPsec SAs between two hosts.
|
||||
*
|
||||
* A child_sa_t contains multiple SAs. SAs for both
|
||||
* directions are managed in one child_sa_t object, and
|
||||
* if both AH and ESP is set up, both protocols are managed
|
||||
* by one child_sa_t. This means we can have two or
|
||||
* in the AH+ESP case four IPsec-SAs in one child_sa_t.
|
||||
* A child_sa_t contains two SAs. SAs for both
|
||||
* directions are managed in one child_sa_t object. Both
|
||||
* SAs and the policies have the same reqid.
|
||||
*
|
||||
* The procedure for child sa setup is as follows:
|
||||
* - A gets SPIs for a proposal via child_sa_t.alloc
|
||||
@@ -92,45 +96,53 @@ struct child_sa_t {
|
||||
protocol_id_t (*get_protocol) (child_sa_t *this);
|
||||
|
||||
/**
|
||||
* @brief Allocate SPIs for a given proposals.
|
||||
* @brief Allocate SPIs for given proposals.
|
||||
*
|
||||
* Since the kernel manages SPIs for us, we need
|
||||
* to allocate them. If the proposal contains more
|
||||
* to allocate them. If a proposal contains more
|
||||
* than one protocol, for each protocol an SPI is
|
||||
* allocated. SPIs are stored internally and written
|
||||
* back to the proposal.
|
||||
*
|
||||
* @param this calling object
|
||||
* @param proposal proposal for which SPIs are allocated
|
||||
* @param proposals list of proposals for which SPIs are allocated
|
||||
*/
|
||||
status_t (*alloc)(child_sa_t *this, linked_list_t* proposals);
|
||||
|
||||
/**
|
||||
* @brief Install the kernel SAs for a proposal.
|
||||
*
|
||||
* Since the kernel manages SPIs for us, we need
|
||||
* to allocate them. If the proposal contains more
|
||||
* than one protocol, for each protocol an SPI is
|
||||
* allocated. SPIs are stored internally and written
|
||||
* back to the proposal.
|
||||
* @brief Install the kernel SAs for a proposal, without previous SPI allocation.
|
||||
*
|
||||
* @param this calling object
|
||||
* @param proposal proposal for which SPIs are allocated
|
||||
* @param prf_plus key material to use for key derivation
|
||||
* @return SUCCESS or FAILED
|
||||
*/
|
||||
status_t (*add)(child_sa_t *this, proposal_t *proposal, prf_plus_t *prf_plus);
|
||||
|
||||
/**
|
||||
* @brief Install the kernel SAs for a proposal, if SPIs already allocated.
|
||||
*
|
||||
* This one updates the SAs in the kernel, which are
|
||||
* allocated via alloc, with a selected proposals.
|
||||
* @brief Install the kernel SAs for a proposal, after SPIs have been allocated.
|
||||
*
|
||||
* Updates an SA, for which SPIs are already allocated via alloc().
|
||||
*
|
||||
* @param this calling object
|
||||
* @param proposal proposal for which SPIs are allocated
|
||||
* @param prf_plus key material to use for key derivation
|
||||
* @return SUCCESS or FAILED
|
||||
*/
|
||||
status_t (*update)(child_sa_t *this, proposal_t *proposal, prf_plus_t *prf_plus);
|
||||
|
||||
/**
|
||||
* @brief Update the hosts in the kernel SAs and policies
|
||||
*
|
||||
* @warning only call this after update() has been called.
|
||||
*
|
||||
* @param this calling object
|
||||
* @param new_me the new local host
|
||||
* @param new_other the new remote host
|
||||
* @return SUCCESS or FAILED
|
||||
*/
|
||||
status_t (*update_hosts) (child_sa_t *this, host_t *new_me, host_t *new_other,
|
||||
int my_changes, int other_changes);
|
||||
|
||||
/**
|
||||
* @brief Install the policies using some traffic selectors.
|
||||
@@ -187,11 +199,13 @@ struct child_sa_t {
|
||||
* @param other remote address
|
||||
* @param soft_lifetime time before rekeying
|
||||
* @param hard_lifteime time before delete
|
||||
* @param use_natt TRUE if NAT traversal is used
|
||||
* @return child_sa_t object
|
||||
*
|
||||
* @ingroup sa
|
||||
*/
|
||||
child_sa_t * child_sa_create(u_int32_t rekey_reqid, host_t *me, host_t *other,
|
||||
u_int32_t soft_lifetime, u_int32_t hard_lifetime);
|
||||
u_int32_t soft_lifetime, u_int32_t hard_lifetime,
|
||||
bool use_natt);
|
||||
|
||||
#endif /*CHILD_SA_H_*/
|
||||
|
||||
+273
-16
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger
|
||||
* Copyright (C) 2005 Jan Hutter, Martin Willi
|
||||
* Hochschule fuer Technik Rapperswil
|
||||
*
|
||||
@@ -19,6 +20,8 @@
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ike_sa.h"
|
||||
@@ -32,6 +35,7 @@
|
||||
#include <crypto/diffie_hellman.h>
|
||||
#include <crypto/prf_plus.h>
|
||||
#include <crypto/crypters/crypter.h>
|
||||
#include <crypto/hashers/hasher.h>
|
||||
#include <encoding/payloads/sa_payload.h>
|
||||
#include <encoding/payloads/nonce_payload.h>
|
||||
#include <encoding/payloads/ke_payload.h>
|
||||
@@ -173,6 +177,31 @@ struct private_ike_sa_t {
|
||||
* A logger for this IKE_SA.
|
||||
*/
|
||||
logger_t *logger;
|
||||
|
||||
/**
|
||||
* NAT hasher.
|
||||
*/
|
||||
hasher_t *nat_hasher;
|
||||
|
||||
/**
|
||||
* NAT status of local host.
|
||||
*/
|
||||
bool nat_here;
|
||||
|
||||
/**
|
||||
* NAT status of remote host.
|
||||
*/
|
||||
bool nat_there;
|
||||
|
||||
/**
|
||||
* Timestamp of last IKE message sent or received on this SA
|
||||
*/
|
||||
struct timeval last_msg_tv;
|
||||
|
||||
/*
|
||||
* Message ID of last DPD message
|
||||
*/
|
||||
u_int32_t last_dpd_message_id;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -320,24 +349,25 @@ static identification_t* get_other_id(private_ike_sa_t *this)
|
||||
return this->policy->get_other_id(this->policy);;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of ike_sa_t.retransmit_possible.
|
||||
*/
|
||||
static bool retransmit_possible(private_ike_sa_t *this, u_int32_t message_id)
|
||||
{
|
||||
return ((this->last_requested_message)
|
||||
&& (message_id != this->last_replied_message_id)
|
||||
&& (message_id == this->last_requested_message->get_message_id(
|
||||
this->last_requested_message)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of ike_sa_t.retransmit_request.
|
||||
*/
|
||||
status_t retransmit_request (private_ike_sa_t *this, u_int32_t message_id)
|
||||
static status_t retransmit_request(private_ike_sa_t *this, u_int32_t message_id)
|
||||
{
|
||||
packet_t *packet;
|
||||
|
||||
if (this->last_requested_message == NULL)
|
||||
{
|
||||
return NOT_FOUND;
|
||||
}
|
||||
|
||||
if (message_id == this->last_replied_message_id)
|
||||
{
|
||||
return NOT_FOUND;
|
||||
}
|
||||
|
||||
if ((this->last_requested_message->get_message_id(this->last_requested_message)) != message_id)
|
||||
|
||||
if (!this->protected.public.retransmit_possible(&this->protected.public, message_id))
|
||||
{
|
||||
return NOT_FOUND;
|
||||
}
|
||||
@@ -349,7 +379,6 @@ status_t retransmit_request (private_ike_sa_t *this, u_int32_t message_id)
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Implementation of protected_ike_sa_t.build_transforms.
|
||||
*/
|
||||
@@ -648,6 +677,12 @@ static status_t send_request(private_ike_sa_t *this, message_t *message)
|
||||
"Increase message counter for outgoing messages from %d",
|
||||
this->message_id_out);
|
||||
this->message_id_out++;
|
||||
|
||||
/* bump last message sent timestamp */
|
||||
if (gettimeofday(&this->last_msg_tv, NULL) < 0)
|
||||
{
|
||||
this->logger->log(this->logger, ERROR|LEVEL1, "failed to get time of day");
|
||||
}
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
@@ -869,6 +904,158 @@ static status_t initiate_connection(private_ike_sa_t *this, connection_t *connec
|
||||
return current_state->initiate_connection(current_state, connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of protected_ike_sa_t.update_connection_hosts.
|
||||
*
|
||||
* Quoting RFC 4306:
|
||||
*
|
||||
* 2.11. Address and Port Agility
|
||||
*
|
||||
* IKE runs over UDP ports 500 and 4500, and implicitly sets up ESP and
|
||||
* AH associations for the same IP addresses it runs over. The IP
|
||||
* addresses and ports in the outer header are, however, not themselves
|
||||
* cryptographically protected, and IKE is designed to work even through
|
||||
* Network Address Translation (NAT) boxes. An implementation MUST
|
||||
* accept incoming requests even if the source port is not 500 or 4500,
|
||||
* and MUST respond to the address and port from which the request was
|
||||
* received. It MUST specify the address and port at which the request
|
||||
* was received as the source address and port in the response. IKE
|
||||
* functions identically over IPv4 or IPv6.
|
||||
*
|
||||
* [...]
|
||||
*
|
||||
* There are cases where a NAT box decides to remove mappings that
|
||||
* are still alive (for example, the keepalive interval is too long,
|
||||
* or the NAT box is rebooted). To recover in these cases, hosts
|
||||
* that are not behind a NAT SHOULD send all packets (including
|
||||
* retransmission packets) to the IP address and port from the last
|
||||
* valid authenticated packet from the other end (i.e., dynamically
|
||||
* update the address). A host behind a NAT SHOULD NOT do this
|
||||
* because it opens a DoS attack possibility. Any authenticated IKE
|
||||
* packet or any authenticated UDP-encapsulated ESP packet can be
|
||||
* used to detect that the IP address or the port has changed.
|
||||
*/
|
||||
static status_t update_connection_hosts(private_ike_sa_t *this, host_t *me, host_t *other)
|
||||
{
|
||||
host_t *old_other = NULL;
|
||||
iterator_t *iterator = NULL;
|
||||
child_sa_t *child_sa = NULL;
|
||||
int my_changes, other_changes;
|
||||
ike_sa_state_t s;
|
||||
|
||||
my_changes = me->get_differences(me, this->connection->get_my_host(this->connection));
|
||||
|
||||
old_other = this->connection->get_other_host(this->connection);
|
||||
other_changes = other->get_differences(other, old_other);
|
||||
|
||||
if (!my_changes && !other_changes) {
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
if (my_changes)
|
||||
{
|
||||
this->connection->update_my_host(this->connection, me->clone(me));
|
||||
}
|
||||
|
||||
s = this->protected.public.get_state(&this->protected.public);
|
||||
|
||||
if (s == RESPONDER_INIT || s == IKE_SA_INIT_REQUESTED || !this->nat_here)
|
||||
{
|
||||
if (other_changes)
|
||||
{
|
||||
this->connection->update_other_host(this->connection, other->clone(other));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (other_changes & HOST_DIFF_ADDR)
|
||||
{
|
||||
this->logger->log(this->logger, ERROR|LEVEL1,
|
||||
"Destination ip changed from %s to %s. As we are NATed this is not allowed!",
|
||||
old_other->get_address(old_other), other->get_address(other));
|
||||
return DESTROY_ME;
|
||||
}
|
||||
else if (other_changes & HOST_DIFF_PORT)
|
||||
{
|
||||
old_other->set_port(old_other, other->get_port(other));
|
||||
}
|
||||
}
|
||||
|
||||
iterator = this->child_sas->create_iterator(this->child_sas, TRUE);
|
||||
while (iterator->iterate(iterator, (void**)&child_sa))
|
||||
{
|
||||
child_sa->update_hosts(child_sa,
|
||||
this->connection->get_my_host(this->connection),
|
||||
this->connection->get_other_host(this->connection),
|
||||
my_changes, other_changes);
|
||||
/* XXX error handling */
|
||||
}
|
||||
iterator->destroy(iterator);
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of protected_ike_sa_t.build_transforms.
|
||||
* TODO: IPv6 support.
|
||||
*/
|
||||
static chunk_t generate_natd_hash(private_ike_sa_t *this, u_int64_t spi_i, u_int64_t spi_r, host_t *host)
|
||||
{
|
||||
chunk_t natd_string;
|
||||
chunk_t natd_hash;
|
||||
void *p;
|
||||
struct sockaddr_in* sai;
|
||||
char buf[512];
|
||||
|
||||
natd_hash = chunk_alloc(this->nat_hasher->get_hash_size(this->nat_hasher));
|
||||
natd_string = chunk_alloc(8 + 8 + 4 + 2);
|
||||
|
||||
sai = (struct sockaddr_in*)host->get_sockaddr(host);
|
||||
p = natd_string.ptr;
|
||||
*(u_int64_t*)p = spi_i; p += sizeof(spi_i);
|
||||
*(u_int64_t*)p = spi_r; p += sizeof(spi_r);
|
||||
*(u_int32_t*)p = sai->sin_addr.s_addr; p += sizeof(sai->sin_addr.s_addr);
|
||||
*(u_int16_t*)p = sai->sin_port; p += sizeof(sai->sin_port);
|
||||
|
||||
this->nat_hasher->get_hash(this->nat_hasher, natd_string, natd_hash.ptr);
|
||||
this->nat_hasher->reset(this->nat_hasher);
|
||||
|
||||
sprintf(buf, "natd_hash(%016llx %016llx %s:%d)\n == SHA1(", spi_i, spi_r,
|
||||
host->get_address(host), host->get_port(host));
|
||||
chunk_to_hex(buf + strlen(buf), sizeof(buf) - strlen(buf), natd_string);
|
||||
strcat(buf, ") == ");
|
||||
chunk_to_hex(buf + strlen(buf), sizeof(buf) - strlen(buf), natd_hash);
|
||||
this->logger->log(this->logger, CONTROL|LEVEL3, buf);
|
||||
|
||||
chunk_free(&natd_string);
|
||||
return natd_hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of ike_sa_t.send_dpd_request.
|
||||
*/
|
||||
static status_t send_dpd_request(private_ike_sa_t *this)
|
||||
{
|
||||
message_t *dpd_msg;
|
||||
status_t status;
|
||||
this->protected.build_message(&this->protected, INFORMATIONAL, TRUE, &dpd_msg);
|
||||
status = this->protected.send_request(&this->protected, dpd_msg);
|
||||
if (status != SUCCESS)
|
||||
{
|
||||
dpd_msg->destroy(dpd_msg);
|
||||
}
|
||||
this->last_dpd_message_id = dpd_msg->get_message_id(dpd_msg);
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of ike_sa_t.get_last_dpd_message_id
|
||||
*/
|
||||
static u_int32_t get_last_dpd_message_id(private_ike_sa_t *this)
|
||||
{
|
||||
return this->last_dpd_message_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of ike_sa_t.get_child_sa.
|
||||
*/
|
||||
@@ -1026,7 +1213,8 @@ static status_t rekey_child_sa(private_ike_sa_t *this, u_int32_t reqid)
|
||||
this->connection->get_my_host(this->connection),
|
||||
this->connection->get_other_host(this->connection),
|
||||
this->policy->get_soft_lifetime(this->policy),
|
||||
this->policy->get_hard_lifetime(this->policy));
|
||||
this->policy->get_hard_lifetime(this->policy),
|
||||
this->nat_here || this->nat_there);
|
||||
child_sa->alloc(child_sa, proposals);
|
||||
sa_payload = sa_payload_create_from_proposal_list(proposals);
|
||||
request->add_payload(request, (payload_t*)sa_payload);
|
||||
@@ -1183,6 +1371,57 @@ static status_t delete_(private_ike_sa_t *this)
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of ike_sa_t.is_my_host_behind_nat.
|
||||
*/
|
||||
static bool is_my_host_behind_nat (private_ike_sa_t *this)
|
||||
{
|
||||
return this->nat_here;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of ike_sa_t.is_other_host_behind_nat.
|
||||
*/
|
||||
static bool is_other_host_behind_nat (private_ike_sa_t *this)
|
||||
{
|
||||
return this->nat_there;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of ike_sa_t.is_any_host_behind_nat.
|
||||
*/
|
||||
static bool is_any_host_behind_nat (private_ike_sa_t *this)
|
||||
{
|
||||
return this->nat_here || this->nat_there;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of protected_ike_sa_t.set_my_host_behind_nat.
|
||||
*/
|
||||
static void set_my_host_behind_nat (private_ike_sa_t *this, bool nat)
|
||||
{
|
||||
this->nat_here = nat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of protected_ike_sa_t.set_other_host_behind_nat.
|
||||
*/
|
||||
static void set_other_host_behind_nat (private_ike_sa_t *this, bool nat)
|
||||
{
|
||||
this->nat_there = nat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of ike_sa_t.get_last_msg_tv.
|
||||
*/
|
||||
static struct timeval get_last_msg_tv (private_ike_sa_t *this)
|
||||
{
|
||||
/*
|
||||
* XXX: query kernel for last activity time
|
||||
*/
|
||||
return this->last_msg_tv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of protected_ike_sa_t.destroy.
|
||||
*/
|
||||
@@ -1269,6 +1508,7 @@ static void destroy(private_ike_sa_t *this)
|
||||
{
|
||||
this->last_responded_message->destroy(this->last_responded_message);
|
||||
}
|
||||
this->nat_hasher->destroy(this->nat_hasher);
|
||||
this->ike_sa_id->destroy(this->ike_sa_id);
|
||||
this->randomizer->destroy(this->randomizer);
|
||||
this->current_state->destroy(this->current_state);
|
||||
@@ -1294,11 +1534,17 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id)
|
||||
this->protected.public.get_my_id = (identification_t*(*)(ike_sa_t*)) get_my_id;
|
||||
this->protected.public.get_other_id = (identification_t*(*)(ike_sa_t*)) get_other_id;
|
||||
this->protected.public.get_connection = (connection_t*(*)(ike_sa_t*)) get_connection;
|
||||
this->protected.public.retransmit_possible = (bool (*) (ike_sa_t *, u_int32_t)) retransmit_possible;
|
||||
this->protected.public.retransmit_request = (status_t (*) (ike_sa_t *, u_int32_t)) retransmit_request;
|
||||
this->protected.public.get_state = (ike_sa_state_t (*) (ike_sa_t *this)) get_state;
|
||||
this->protected.public.log_status = (void (*) (ike_sa_t*,logger_t*,char*))log_status;
|
||||
this->protected.public.delete = (status_t(*)(ike_sa_t*))delete_;
|
||||
this->protected.public.destroy = (void(*)(ike_sa_t*))destroy;
|
||||
this->protected.public.is_my_host_behind_nat = (bool(*)(ike_sa_t*)) is_my_host_behind_nat;
|
||||
this->protected.public.is_other_host_behind_nat = (bool(*)(ike_sa_t*)) is_other_host_behind_nat;
|
||||
this->protected.public.is_any_host_behind_nat = (bool(*)(ike_sa_t*)) is_any_host_behind_nat;
|
||||
this->protected.public.get_last_msg_tv = (struct timeval (*)(ike_sa_t*)) get_last_msg_tv;
|
||||
this->protected.public.send_dpd_request = (status_t (*)(ike_sa_t*)) send_dpd_request;
|
||||
|
||||
/* protected functions */
|
||||
this->protected.build_message = (void (*) (protected_ike_sa_t *, exchange_type_t,bool,message_t**)) build_message;
|
||||
@@ -1327,6 +1573,11 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id)
|
||||
this->protected.set_last_replied_message_id = (void (*) (protected_ike_sa_t *,u_int32_t)) set_last_replied_message_id;
|
||||
this->protected.destroy_child_sa = (u_int32_t (*)(protected_ike_sa_t*,u_int32_t))destroy_child_sa;
|
||||
this->protected.get_child_sa = (child_sa_t* (*)(protected_ike_sa_t*,u_int32_t))get_child_sa_by_spi;
|
||||
this->protected.set_my_host_behind_nat = (void(*)(protected_ike_sa_t*, bool)) set_my_host_behind_nat;
|
||||
this->protected.set_other_host_behind_nat = (void(*)(protected_ike_sa_t*, bool)) set_other_host_behind_nat;
|
||||
this->protected.generate_natd_hash = (chunk_t (*) (protected_ike_sa_t *, u_int64_t, u_int64_t, host_t*)) generate_natd_hash;
|
||||
this->protected.get_last_dpd_message_id = (u_int32_t (*) (protected_ike_sa_t*)) get_last_dpd_message_id;
|
||||
this->protected.update_connection_hosts = (status_t (*) (protected_ike_sa_t *, host_t*, host_t*)) update_connection_hosts;
|
||||
|
||||
/* initialize private fields */
|
||||
this->logger = logger_manager->get_logger(logger_manager, IKE_SA);
|
||||
@@ -1350,7 +1601,13 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id)
|
||||
this->child_prf = NULL;
|
||||
this->connection = NULL;
|
||||
this->policy = NULL;
|
||||
|
||||
this->nat_hasher = hasher_create(HASH_SHA1);
|
||||
this->nat_here = FALSE;
|
||||
this->nat_there = FALSE;
|
||||
this->last_msg_tv.tv_sec = 0;
|
||||
this->last_msg_tv.tv_usec = 0;
|
||||
this->last_dpd_message_id = 0;
|
||||
|
||||
/* at creation time, IKE_SA is in a initiator state */
|
||||
if (ike_sa_id->is_initiator(ike_sa_id))
|
||||
{
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger
|
||||
* Copyright (C) 2005 Jan Hutter, Martin Willi
|
||||
* Hochschule fuer Technik Rapperswil
|
||||
*
|
||||
@@ -92,6 +93,17 @@ struct ike_sa_t {
|
||||
*/
|
||||
status_t (*initiate_connection) (ike_sa_t *this, connection_t *connection);
|
||||
|
||||
/**
|
||||
* @brief Checks whether retransmission is possible.
|
||||
*
|
||||
* @param this calling object
|
||||
* @param message_id ID of the request to retransmit
|
||||
* @return
|
||||
* - TRUE if retransmit is possible
|
||||
* - FALSE if not
|
||||
*/
|
||||
bool (*retransmit_possible) (ike_sa_t *this, u_int32_t message_id);
|
||||
|
||||
/**
|
||||
* @brief Retransmits a request.
|
||||
*
|
||||
@@ -199,6 +211,38 @@ struct ike_sa_t {
|
||||
*/
|
||||
connection_t* (*get_connection) (ike_sa_t *this);
|
||||
|
||||
/**
|
||||
* @brief Query NAT detection status for local host.
|
||||
*
|
||||
* @param this calling object
|
||||
* @return TRUE if this host is behind NAT
|
||||
*/
|
||||
bool (*is_my_host_behind_nat) (ike_sa_t *this);
|
||||
|
||||
/**
|
||||
* @brief Query NAT detection status for remote host.
|
||||
*
|
||||
* @param this calling object
|
||||
* @return TRUE if other host is behind NAT
|
||||
*/
|
||||
bool (*is_other_host_behind_nat) (ike_sa_t *this);
|
||||
|
||||
/**
|
||||
* @brief Query NAT detection status for any host.
|
||||
*
|
||||
* @param this calling object
|
||||
* @return TRUE if this or other host is behind NAT
|
||||
*/
|
||||
bool (*is_any_host_behind_nat) (ike_sa_t *this);
|
||||
|
||||
/**
|
||||
* @brief Query timeval of last message sent.
|
||||
*
|
||||
* @param this calling object
|
||||
* @return time when the last message was sent
|
||||
*/
|
||||
struct timeval (*get_last_msg_tv) (ike_sa_t *this);
|
||||
|
||||
/**
|
||||
* @brief Get the state of type of associated state object.
|
||||
*
|
||||
@@ -207,6 +251,13 @@ struct ike_sa_t {
|
||||
*/
|
||||
ike_sa_state_t (*get_state) (ike_sa_t *this);
|
||||
|
||||
/**
|
||||
* @brief Sends a DPD request to the peer.
|
||||
*
|
||||
* @param this calling object
|
||||
*/
|
||||
status_t (*send_dpd_request) (ike_sa_t *this);
|
||||
|
||||
/**
|
||||
* @brief Log the status of a the ike sa to a logger.
|
||||
*
|
||||
@@ -507,6 +558,52 @@ struct protected_ike_sa_t {
|
||||
* @param this calling object
|
||||
*/
|
||||
void (*reset_message_buffers) (protected_ike_sa_t *this);
|
||||
|
||||
/**
|
||||
* @brief Set NAT detection status for local host.
|
||||
*
|
||||
* @param this calling object
|
||||
* @param nat if TRUE, local host is behing NAT
|
||||
*/
|
||||
void (*set_my_host_behind_nat) (protected_ike_sa_t *this, bool nat);
|
||||
|
||||
/**
|
||||
* @brief Set NAT detection status for remote host.
|
||||
*
|
||||
* @param this calling object
|
||||
* @param nat if TRUE, remote host is behing NAT
|
||||
*/
|
||||
void (*set_other_host_behind_nat) (protected_ike_sa_t *this, bool nat);
|
||||
|
||||
/**
|
||||
* @brief Generate NAT-D payload hash.
|
||||
*
|
||||
* @param this calling object
|
||||
* @param spi_i IKE SPI of initiator
|
||||
* @param spi_r IKE SPI of responder
|
||||
* @param host address and port of the host/interface
|
||||
* @return chunk containing calculated NAT-D hash
|
||||
*/
|
||||
chunk_t (*generate_natd_hash) (protected_ike_sa_t *this, u_int64_t spi_i, u_int64_t spi_r, host_t *host);
|
||||
|
||||
/**
|
||||
* @brief Dynamically update hosts on the associated connection.
|
||||
*
|
||||
* Warning: me and other host are cloned.
|
||||
*
|
||||
* @param this calling object
|
||||
* @param me local address and port
|
||||
* @param other remote address and port
|
||||
*/
|
||||
status_t (*update_connection_hosts) (protected_ike_sa_t *this, host_t *me, host_t *other);
|
||||
|
||||
/**
|
||||
* @brief Return the message id of the last DPD message
|
||||
*
|
||||
* @param this calling object
|
||||
* @return the messages id
|
||||
*/
|
||||
u_int32_t (*get_last_dpd_message_id) (protected_ike_sa_t *this);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger
|
||||
* Copyright (C) 2005 Jan Hutter, Martin Willi
|
||||
* Hochschule fuer Technik Rapperswil
|
||||
*
|
||||
@@ -286,6 +287,14 @@ static status_t process_message(private_ike_auth_requested_t *this, message_t *i
|
||||
return DESTROY_ME;
|
||||
}
|
||||
|
||||
status = this->ike_sa->update_connection_hosts(this->ike_sa,
|
||||
ike_auth_reply->get_destination(ike_auth_reply),
|
||||
ike_auth_reply->get_source(ike_auth_reply));
|
||||
if (status != SUCCESS)
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
/* process all payloads */
|
||||
status = this->process_idr_payload(this, idr_payload);
|
||||
if (status != SUCCESS)
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger
|
||||
* Copyright (C) 2005 Jan Hutter, Martin Willi
|
||||
* Hochschule fuer Technik Rapperswil
|
||||
*
|
||||
@@ -31,7 +32,7 @@
|
||||
#include <encoding/payloads/nonce_payload.h>
|
||||
#include <sa/child_sa.h>
|
||||
#include <sa/states/delete_ike_sa_requested.h>
|
||||
|
||||
#include <queues/jobs/send_dpd_job.h>
|
||||
|
||||
typedef struct private_ike_sa_established_t private_ike_sa_established_t;
|
||||
|
||||
@@ -85,6 +86,21 @@ struct private_ike_sa_established_t {
|
||||
logger_t *logger;
|
||||
};
|
||||
|
||||
/**
|
||||
* Schedule send dpd job
|
||||
*/
|
||||
static void schedule_dpd_job(private_ike_sa_established_t *this)
|
||||
{
|
||||
u_int32_t interval = charon->configuration->get_dpd_interval(charon->configuration);
|
||||
|
||||
if (interval)
|
||||
{
|
||||
charon->event_queue->add_relative(charon->event_queue,
|
||||
(job_t*)send_dpd_job_create(this->ike_sa->public.get_id(&this->ike_sa->public)),
|
||||
interval);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of private_ike_sa_established_t.build_sa_payload.
|
||||
*/
|
||||
@@ -99,6 +115,7 @@ static status_t build_sa_payload(private_ike_sa_established_t *this, sa_payload_
|
||||
connection_t *connection;
|
||||
policy_t *policy;
|
||||
u_int32_t reqid = 0;
|
||||
bool use_natt;
|
||||
|
||||
/* prepare reply */
|
||||
sa_response = sa_payload_create();
|
||||
@@ -142,11 +159,13 @@ static status_t build_sa_payload(private_ike_sa_established_t *this, sa_payload_
|
||||
{ /* reuse old reqid if we are rekeying */
|
||||
reqid = this->old_child_sa->get_reqid(this->old_child_sa);
|
||||
}
|
||||
use_natt = this->ike_sa->public.is_any_host_behind_nat(&this->ike_sa->public);
|
||||
this->child_sa = child_sa_create(reqid,
|
||||
connection->get_my_host(connection),
|
||||
connection->get_other_host(connection),
|
||||
policy->get_soft_lifetime(policy),
|
||||
policy->get_hard_lifetime(policy));
|
||||
policy->get_hard_lifetime(policy),
|
||||
use_natt);
|
||||
|
||||
status = this->child_sa->add(this->child_sa, proposal, prf_plus);
|
||||
prf_plus->destroy(prf_plus);
|
||||
@@ -404,6 +423,11 @@ static status_t process_informational(private_ike_sa_established_t *this, messag
|
||||
{
|
||||
delete_payload_t *delete_request = NULL;
|
||||
iterator_t *payloads = request->get_payload_iterator(request);
|
||||
|
||||
if (!payloads->get_count(payloads))
|
||||
{
|
||||
this->logger->log(this->logger, CONTROL, "DPD request received.");
|
||||
}
|
||||
|
||||
while (payloads->has_next(payloads))
|
||||
{
|
||||
@@ -434,7 +458,7 @@ static status_t process_informational(private_ike_sa_established_t *this, messag
|
||||
if (delete_request->get_protocol_id(delete_request) == PROTO_IKE)
|
||||
{
|
||||
this->logger->log(this->logger, CONTROL, "DELETE request for IKE_SA received");
|
||||
/* switch to delete_ike_sa_requested. This is not absolutly correct, but we
|
||||
/* switch to delete_ike_sa_requested. This is not absolutely correct, but we
|
||||
* allow the clean destruction of an SA only in this state. */
|
||||
this->ike_sa->set_new_state(this->ike_sa, (state_t*)delete_ike_sa_requested_create(this->ike_sa));
|
||||
this->public.state_interface.destroy(&(this->public.state_interface));
|
||||
@@ -473,6 +497,53 @@ static status_t process_informational(private_ike_sa_established_t *this, messag
|
||||
}
|
||||
|
||||
/**
|
||||
* Process an informational response
|
||||
*/
|
||||
static status_t process_informational_response(private_ike_sa_established_t *this, message_t *message)
|
||||
{
|
||||
iterator_t *payloads = message->get_payload_iterator(message);
|
||||
|
||||
if (!payloads->get_count(payloads))
|
||||
{
|
||||
if (message->get_message_id(message)
|
||||
!= this->ike_sa->get_last_dpd_message_id(this->ike_sa))
|
||||
{
|
||||
this->logger->log(this->logger, ERROR|LEVEL1, "DPD response received that does not match our last sent dpd message.");
|
||||
payloads->destroy(payloads);
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
this->logger->log(this->logger, CONTROL, "DPD response received. Schedule job.");
|
||||
schedule_dpd_job(this);
|
||||
|
||||
payloads->destroy(payloads);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
while (payloads->has_next(payloads))
|
||||
{
|
||||
payload_t *payload;
|
||||
payloads->current(payloads, (void**)&payload);
|
||||
|
||||
switch (payload->get_type(payload))
|
||||
{
|
||||
default:
|
||||
{
|
||||
this->logger->log(this->logger, ERROR|LEVEL1, "Ignoring Payload %s (%d)",
|
||||
mapping_find(payload_type_m, payload->get_type(payload)),
|
||||
payload->get_type(payload));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* iterator can be destroyed */
|
||||
payloads->destroy(payloads);
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements state_t.get_state
|
||||
* Implements state_t.process_message
|
||||
*/
|
||||
static status_t process_message(private_ike_sa_established_t *this, message_t *message)
|
||||
@@ -513,6 +584,13 @@ static status_t process_message(private_ike_sa_established_t *this, message_t *m
|
||||
return status;
|
||||
}
|
||||
|
||||
status = this->ike_sa->update_connection_hosts(this->ike_sa,
|
||||
message->get_destination(message), message->get_source(message));
|
||||
if (status != SUCCESS)
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
/* prepare a reply of the same type */
|
||||
this->ike_sa->build_message(this->ike_sa, message->get_exchange_type(message), FALSE, &response);
|
||||
|
||||
@@ -570,6 +648,9 @@ ike_sa_established_t *ike_sa_established_create(protected_ike_sa_t *ike_sa)
|
||||
this->nonce_i = CHUNK_INITIALIZER;
|
||||
this->nonce_r = CHUNK_INITIALIZER;
|
||||
this->old_child_sa = NULL;
|
||||
|
||||
/* schedule initial dpd job */
|
||||
schedule_dpd_job(this);
|
||||
|
||||
return &(this->public);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger
|
||||
* Copyright (C) 2005 Jan Hutter, Martin Willi
|
||||
* Hochschule fuer Technik Rapperswil
|
||||
*
|
||||
@@ -90,6 +91,36 @@ struct private_ike_sa_init_requested_t {
|
||||
*/
|
||||
logger_t *logger;
|
||||
|
||||
/**
|
||||
* Precomputed NAT-D hash for initiator.
|
||||
*/
|
||||
chunk_t natd_hash_i;
|
||||
|
||||
/**
|
||||
* Flag indicating that an initiator NAT-D hash matched.
|
||||
*/
|
||||
bool natd_hash_i_matched;
|
||||
|
||||
/**
|
||||
* NAT-D payload count for NAT_DETECTION_SOURCE_IP.
|
||||
*/
|
||||
int natd_seen_i;
|
||||
|
||||
/**
|
||||
* Precomputed NAT-D hash of responder.
|
||||
*/
|
||||
chunk_t natd_hash_r;
|
||||
|
||||
/**
|
||||
* Flag indicating that a responder NAT-D hash matched.
|
||||
*/
|
||||
bool natd_hash_r_matched;
|
||||
|
||||
/**
|
||||
* NAT-D payload count for NAT_DETECTION_DESTINATION_IP.
|
||||
*/
|
||||
int natd_seen_r;
|
||||
|
||||
|
||||
/**
|
||||
* Process NONCE payload of IKE_SA_INIT response.
|
||||
@@ -271,6 +302,26 @@ static status_t process_message(private_ike_sa_init_requested_t *this, message_t
|
||||
ike_sa_id = this->ike_sa->public.get_id(&(this->ike_sa->public));
|
||||
ike_sa_id->set_responder_spi(ike_sa_id,responder_spi);
|
||||
|
||||
/*
|
||||
* Precompute NAT-D hashes.
|
||||
* Even though there SHOULD only be a single payload of each
|
||||
* Notify type, we precompute both hashes.
|
||||
*/
|
||||
this->natd_hash_i = this->ike_sa->generate_natd_hash(this->ike_sa,
|
||||
ike_sa_init_reply->get_initiator_spi(ike_sa_init_reply),
|
||||
ike_sa_init_reply->get_responder_spi(ike_sa_init_reply),
|
||||
ike_sa_init_reply->get_source(ike_sa_init_reply));
|
||||
this->natd_hash_i_matched = FALSE;
|
||||
this->natd_seen_i = 0;
|
||||
this->natd_hash_r = this->ike_sa->generate_natd_hash(this->ike_sa,
|
||||
ike_sa_init_reply->get_initiator_spi(ike_sa_init_reply),
|
||||
ike_sa_init_reply->get_responder_spi(ike_sa_init_reply),
|
||||
ike_sa_init_reply->get_destination(ike_sa_init_reply));
|
||||
this->natd_hash_r_matched = FALSE;
|
||||
this->natd_seen_r = 0;
|
||||
this->ike_sa->set_my_host_behind_nat(this->ike_sa, FALSE);
|
||||
this->ike_sa->set_other_host_behind_nat(this->ike_sa, FALSE);
|
||||
|
||||
/* Iterate over all payloads.
|
||||
*
|
||||
* The message is already checked for the right payload types.
|
||||
@@ -354,12 +405,59 @@ static status_t process_message(private_ike_sa_init_requested_t *this, message_t
|
||||
return DESTROY_ME;
|
||||
}
|
||||
|
||||
/* apply the address on wich we really received the packet */
|
||||
/* NAT-D */
|
||||
if ((!this->natd_seen_i && this->natd_seen_r > 0)
|
||||
|| (this->natd_seen_i > 0 && !this->natd_seen_r))
|
||||
{
|
||||
this->logger->log(this->logger, AUDIT, "IKE_SA_INIT request contained wrong number of NAT-D payloads. Deleting IKE_SA");
|
||||
return DESTROY_ME;
|
||||
}
|
||||
if (this->natd_seen_r > 1)
|
||||
{
|
||||
this->logger->log(this->logger, AUDIT, "Warning: IKE_SA_INIT request contained multiple Notify(NAT_DETECTION_DESTINATION_IP) payloads.");
|
||||
}
|
||||
if (this->natd_seen_i > 0 && !this->natd_hash_i_matched)
|
||||
{
|
||||
this->logger->log(this->logger, AUDIT, "Remote host is behind NAT, using NAT-T.");
|
||||
this->ike_sa->set_other_host_behind_nat(this->ike_sa, TRUE);
|
||||
}
|
||||
if (this->natd_seen_r > 0 && !this->natd_hash_r_matched)
|
||||
{
|
||||
this->logger->log(this->logger, AUDIT, "Local host is behind NAT, using NAT-T.");
|
||||
this->ike_sa->set_my_host_behind_nat(this->ike_sa, TRUE);
|
||||
}
|
||||
|
||||
/* apply the address on wich we really received the packet,
|
||||
* and switch to port 4500 when using NAT-T and NAT was detected.
|
||||
*/
|
||||
connection = this->ike_sa->get_connection(this->ike_sa);
|
||||
me = ike_sa_init_reply->get_destination(ike_sa_init_reply);
|
||||
other = ike_sa_init_reply->get_source(ike_sa_init_reply);
|
||||
connection->update_my_host(connection, me->clone(me));
|
||||
connection->update_other_host(connection, other->clone(other));
|
||||
|
||||
if (this->ike_sa->public.is_any_host_behind_nat((ike_sa_t*)this->ike_sa))
|
||||
{
|
||||
me->set_port(me, IKEV2_NATT_PORT);
|
||||
other->set_port(other, IKEV2_NATT_PORT);
|
||||
this->logger->log(this->logger, AUDIT, "Switching to port %d.", IKEV2_NATT_PORT);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->logger->log(this->logger, AUDIT, "No NAT detected, not using NAT-T.");
|
||||
}
|
||||
|
||||
if (this->ike_sa->public.is_my_host_behind_nat(&this->ike_sa->public))
|
||||
{
|
||||
charon->event_queue->add_relative(charon->event_queue,
|
||||
(job_t*)send_keepalive_job_create(this->ike_sa->public.get_id((ike_sa_t*)this->ike_sa)),
|
||||
charon->configuration->get_keepalive_interval(charon->configuration));
|
||||
}
|
||||
|
||||
status = this->ike_sa->update_connection_hosts(this->ike_sa, me, other);
|
||||
if (status != SUCCESS)
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
policy = this->ike_sa->get_policy(this->ike_sa);
|
||||
policy->update_my_ts(policy, me);
|
||||
policy->update_other_ts(policy, other);
|
||||
@@ -575,7 +673,8 @@ static status_t build_sa_payload (private_ike_sa_init_requested_t *this, message
|
||||
connection->get_my_host(connection),
|
||||
connection->get_other_host(connection),
|
||||
policy->get_soft_lifetime(policy),
|
||||
policy->get_hard_lifetime(policy));
|
||||
policy->get_hard_lifetime(policy),
|
||||
this->ike_sa->public.is_any_host_behind_nat(&this->ike_sa->public));
|
||||
if (this->child_sa->alloc(this->child_sa, proposal_list) != SUCCESS)
|
||||
{
|
||||
this->logger->log(this->logger, AUDIT, "Could not install CHILD_SA! Deleting IKE_SA");
|
||||
@@ -633,6 +732,7 @@ static status_t build_tsr_payload (private_ike_sa_init_requested_t *this, messag
|
||||
*/
|
||||
static status_t process_notify_payload(private_ike_sa_init_requested_t *this, notify_payload_t *notify_payload)
|
||||
{
|
||||
chunk_t notification_data;
|
||||
notify_message_type_t notify_message_type = notify_payload->get_notify_message_type(notify_payload);
|
||||
|
||||
this->logger->log(this->logger, CONTROL|LEVEL1, "Process notify type %s",
|
||||
@@ -701,6 +801,44 @@ static status_t process_notify_payload(private_ike_sa_init_requested_t *this, no
|
||||
}
|
||||
return FAILED;
|
||||
}
|
||||
case NAT_DETECTION_DESTINATION_IP:
|
||||
{
|
||||
this->natd_seen_r++;
|
||||
if (this->natd_hash_r_matched)
|
||||
return SUCCESS;
|
||||
|
||||
notification_data = notify_payload->get_notification_data(notify_payload);
|
||||
if (chunk_equals(notification_data, this->natd_hash_r))
|
||||
{
|
||||
this->natd_hash_r_matched = TRUE;
|
||||
this->logger->log(this->logger, CONTROL|LEVEL3, "NAT-D hash match");
|
||||
}
|
||||
else
|
||||
{
|
||||
this->logger->log(this->logger, CONTROL|LEVEL3, "NAT-D hash mismatch");
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
case NAT_DETECTION_SOURCE_IP:
|
||||
{
|
||||
this->natd_seen_i++;
|
||||
if (this->natd_hash_i_matched)
|
||||
return SUCCESS;
|
||||
|
||||
notification_data = notify_payload->get_notification_data(notify_payload);
|
||||
if (chunk_equals(notification_data, this->natd_hash_i))
|
||||
{
|
||||
this->natd_hash_i_matched = TRUE;
|
||||
this->logger->log(this->logger, CONTROL|LEVEL3, "NAT-D hash match");
|
||||
}
|
||||
else
|
||||
{
|
||||
this->logger->log(this->logger, CONTROL|LEVEL3, "NAT-D hash mismatch");
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
default:
|
||||
{
|
||||
/*
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger
|
||||
* Copyright (C) 2005 Jan Hutter, Martin Willi
|
||||
* Hochschule fuer Technik Rapperswil
|
||||
*
|
||||
@@ -295,7 +296,14 @@ static status_t process_message(private_ike_sa_init_responded_t *this, message_t
|
||||
this->logger->log(this->logger, AUDIT, "IKE_AUTH reply did not contain all required payloads. Deleting IKE_SA");
|
||||
return DESTROY_ME;
|
||||
}
|
||||
|
||||
|
||||
status = this->ike_sa->update_connection_hosts(this->ike_sa,
|
||||
request->get_destination(request), request->get_source(request));
|
||||
if (status != SUCCESS)
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
/* build response */
|
||||
this->ike_sa->build_message(this->ike_sa, IKE_AUTH, FALSE, &response);
|
||||
|
||||
@@ -442,6 +450,7 @@ static status_t build_sa_payload(private_ike_sa_init_responded_t *this, sa_paylo
|
||||
status_t status;
|
||||
connection_t *connection;
|
||||
policy_t *policy;
|
||||
bool use_natt;
|
||||
|
||||
/* prepare reply */
|
||||
sa_response = sa_payload_create();
|
||||
@@ -477,11 +486,13 @@ static status_t build_sa_payload(private_ike_sa_init_responded_t *this, sa_paylo
|
||||
|
||||
policy = this->ike_sa->get_policy(this->ike_sa);
|
||||
connection = this->ike_sa->get_connection(this->ike_sa);
|
||||
use_natt = this->ike_sa->public.is_any_host_behind_nat(&this->ike_sa->public);
|
||||
this->child_sa = child_sa_create(0,
|
||||
connection->get_my_host(connection),
|
||||
connection->get_other_host(connection),
|
||||
policy->get_soft_lifetime(policy),
|
||||
policy->get_hard_lifetime(policy));
|
||||
policy->get_hard_lifetime(policy),
|
||||
use_natt);
|
||||
|
||||
status = this->child_sa->add(this->child_sa, proposal, prf_plus);
|
||||
prf_plus->destroy(prf_plus);
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger
|
||||
* Copyright (C) 2005 Jan Hutter, Martin Willi
|
||||
* Hochschule fuer Technik Rapperswil
|
||||
*
|
||||
@@ -28,6 +29,7 @@
|
||||
#include <sa/states/ike_sa_init_requested.h>
|
||||
#include <queues/jobs/retransmit_request_job.h>
|
||||
#include <crypto/diffie_hellman.h>
|
||||
#include <crypto/hashers/hasher.h>
|
||||
#include <encoding/payloads/sa_payload.h>
|
||||
#include <encoding/payloads/ke_payload.h>
|
||||
#include <encoding/payloads/nonce_payload.h>
|
||||
@@ -92,7 +94,24 @@ struct private_initiator_init_t {
|
||||
* @param request message_t object to add the NONCE payload
|
||||
*/
|
||||
status_t (*build_nonce_payload) (private_initiator_init_t *this,message_t *request);
|
||||
|
||||
/**
|
||||
* Builds the NAT-T Notify(NAT_DETECTION_SOURCE_IP) and
|
||||
* Notify(NAT_DETECTION_DESTINATION_IP) payloads for this state.
|
||||
*
|
||||
* @param this calling object
|
||||
* @param request message_t object to add the Notify payloads
|
||||
*/
|
||||
void (*build_natd_payload) (private_initiator_init_t *this, message_t *request, notify_message_type_t type, host_t *host);
|
||||
|
||||
/**
|
||||
* Builds the NAT-T Notify(NAT_DETECTION_SOURCE_IP) and
|
||||
* Notify(NAT_DETECTION_DESTINATION_IP) payloads for this state.
|
||||
*
|
||||
* @param this calling object
|
||||
* @param request message_t object to add the Notify payloads
|
||||
*/
|
||||
void (*build_natd_payloads) (private_initiator_init_t *this, message_t *request);
|
||||
|
||||
/**
|
||||
* Destroy function called internally of this class after state change to state
|
||||
* IKE_SA_INIT_REQUESTED succeeded.
|
||||
@@ -187,6 +206,10 @@ status_t retry_initiate_connection (private_initiator_init_t *this, diffie_hellm
|
||||
message->destroy(message);
|
||||
return DESTROY_ME;
|
||||
}
|
||||
|
||||
/* build Notify(NAT-D) payloads */
|
||||
this->build_natd_payloads(this, message);
|
||||
|
||||
/* message can now be sent (must not be destroyed) */
|
||||
status = this->ike_sa->send_request(this->ike_sa, message);
|
||||
if (status != SUCCESS)
|
||||
@@ -286,6 +309,57 @@ static status_t build_nonce_payload(private_initiator_init_t *this, message_t *r
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of private_initiator_init_t.build_natd_payload.
|
||||
*/
|
||||
static void build_natd_payload(private_initiator_init_t *this, message_t *request, notify_message_type_t type, host_t *host)
|
||||
{
|
||||
chunk_t hash;
|
||||
this->logger->log(this->logger, CONTROL|LEVEL1, "Building Notify(NAT-D) payload");
|
||||
notify_payload_t *notify_payload;
|
||||
notify_payload = notify_payload_create();
|
||||
/*notify_payload->set_protocol_id(notify_payload, NULL);*/
|
||||
/*notify_payload->set_spi(notify_payload, NULL);*/
|
||||
notify_payload->set_notify_message_type(notify_payload, type);
|
||||
hash = this->ike_sa->generate_natd_hash(this->ike_sa,
|
||||
request->get_initiator_spi(request),
|
||||
request->get_responder_spi(request),
|
||||
host);
|
||||
notify_payload->set_notification_data(notify_payload, hash);
|
||||
chunk_free(&hash);
|
||||
this->logger->log(this->logger, CONTROL|LEVEL2, "Add Notify(NAT-D) payload to message");
|
||||
request->add_payload(request, (payload_t *) notify_payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of private_initiator_init_t.build_natd_payloads.
|
||||
*/
|
||||
static void build_natd_payloads(private_initiator_init_t *this, message_t *request)
|
||||
{
|
||||
connection_t *connection;
|
||||
linked_list_t *hostlist;
|
||||
iterator_t *hostiter;
|
||||
host_t *host;
|
||||
|
||||
/*
|
||||
* N(NAT_DETECTION_SOURCE_IP)+
|
||||
*/
|
||||
hostlist = charon->interfaces->get_addresses(charon->interfaces);
|
||||
hostiter = hostlist->create_iterator(hostlist, TRUE);
|
||||
while(hostiter->iterate(hostiter, (void**)&host)) {
|
||||
this->build_natd_payload(this, request, NAT_DETECTION_SOURCE_IP,
|
||||
host);
|
||||
}
|
||||
hostiter->destroy(hostiter);
|
||||
|
||||
/*
|
||||
* N(NAT_DETECTION_DESTINATION_IP)
|
||||
*/
|
||||
connection = this->ike_sa->get_connection(this->ike_sa);
|
||||
this->build_natd_payload(this, request, NAT_DETECTION_DESTINATION_IP,
|
||||
connection->get_other_host(connection));
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of state_t.process_message.
|
||||
*/
|
||||
@@ -352,6 +426,8 @@ initiator_init_t *initiator_init_create(protected_ike_sa_t *ike_sa)
|
||||
this->build_nonce_payload = build_nonce_payload;
|
||||
this->build_sa_payload = build_sa_payload;
|
||||
this->build_ke_payload = build_ke_payload;
|
||||
this->build_natd_payload = build_natd_payload;
|
||||
this->build_natd_payloads = build_natd_payloads;
|
||||
|
||||
/* private data */
|
||||
this->ike_sa = ike_sa;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger
|
||||
* Copyright (C) 2005 Jan Hutter, Martin Willi
|
||||
* Hochschule fuer Technik Rapperswil
|
||||
*
|
||||
@@ -30,6 +31,7 @@
|
||||
#include <encoding/payloads/nonce_payload.h>
|
||||
#include <encoding/payloads/notify_payload.h>
|
||||
#include <crypto/diffie_hellman.h>
|
||||
#include <queues/jobs/send_keepalive_job.h>
|
||||
|
||||
|
||||
typedef struct private_responder_init_t private_responder_init_t;
|
||||
@@ -90,6 +92,37 @@ struct private_responder_init_t {
|
||||
*/
|
||||
logger_t *logger;
|
||||
|
||||
/**
|
||||
* Precomputed NAT-D hash for initiator.
|
||||
*/
|
||||
chunk_t natd_hash_i;
|
||||
|
||||
/**
|
||||
* Flag indicating that an initiator NAT-D hash matched.
|
||||
*/
|
||||
bool natd_hash_i_matched;
|
||||
|
||||
/**
|
||||
* NAT-D payload count for NAT_DETECTION_SOURCE_IP.
|
||||
*/
|
||||
int natd_seen_i;
|
||||
|
||||
/**
|
||||
* Precomputed NAT-D hash of responder.
|
||||
*/
|
||||
chunk_t natd_hash_r;
|
||||
|
||||
/**
|
||||
* Flag indicating that a responder NAT-D hash matched.
|
||||
*/
|
||||
bool natd_hash_r_matched;
|
||||
|
||||
/**
|
||||
* NAT-D payload count for NAT_DETECTION_DESTINATION_IP.
|
||||
*/
|
||||
int natd_seen_r;
|
||||
|
||||
|
||||
/**
|
||||
* Handles received SA payload and builds the SA payload for the response.
|
||||
*
|
||||
@@ -124,6 +157,24 @@ struct private_responder_init_t {
|
||||
*/
|
||||
status_t (*build_nonce_payload) (private_responder_init_t *this,nonce_payload_t *nonce_request, message_t *response);
|
||||
|
||||
/**
|
||||
* Builds the NAT-T Notify(NAT_DETECTION_SOURCE_IP) and
|
||||
* Notify(NAT_DETECTION_DESTINATION_IP) payloads for this state.
|
||||
*
|
||||
* @param this calling object
|
||||
* @param request message_t object to add the Notify payloads
|
||||
*/
|
||||
void (*build_natd_payload) (private_responder_init_t *this, message_t *request, notify_message_type_t type, host_t *host);
|
||||
|
||||
/**
|
||||
* Builds the NAT-T Notify(NAT_DETECTION_SOURCE_IP) and
|
||||
* Notify(NAT_DETECTION_DESTINATION_IP) payloads for this state.
|
||||
*
|
||||
* @param this calling object
|
||||
* @param request message_t object to add the Notify payloads
|
||||
*/
|
||||
void (*build_natd_payloads) (private_responder_init_t *this, message_t *request);
|
||||
|
||||
/**
|
||||
* Sends a IKE_SA_INIT reply containing a notify payload.
|
||||
*
|
||||
@@ -185,7 +236,13 @@ static status_t process_message(private_responder_init_t *this, message_t *messa
|
||||
/* TODO: inform requestor */
|
||||
return DESTROY_ME;
|
||||
}
|
||||
this->ike_sa->set_connection(this->ike_sa,connection);
|
||||
this->ike_sa->set_connection(this->ike_sa, connection);
|
||||
status = this->ike_sa->update_connection_hosts(this->ike_sa,
|
||||
destination, source);
|
||||
if (status != SUCCESS)
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
/* parse incoming message */
|
||||
status = message->parse_body(message, NULL, NULL);
|
||||
@@ -204,7 +261,31 @@ static status_t process_message(private_responder_init_t *this, message_t *messa
|
||||
return DESTROY_ME;
|
||||
}
|
||||
|
||||
payloads = message->get_payload_iterator(message);
|
||||
/*
|
||||
* Precompute NAT-D hashes.
|
||||
* Even though there SHOULD only be a single payload of Notify type
|
||||
* NAT_DETECTION_DESTINATION_IP we precompute both hashes.
|
||||
*/
|
||||
this->natd_hash_i = this->ike_sa->generate_natd_hash(this->ike_sa,
|
||||
message->get_initiator_spi(message),
|
||||
message->get_responder_spi(message),
|
||||
message->get_source(message));
|
||||
this->natd_hash_i_matched = FALSE;
|
||||
this->natd_seen_i = 0;
|
||||
this->natd_hash_r = this->ike_sa->generate_natd_hash(this->ike_sa,
|
||||
message->get_initiator_spi(message),
|
||||
message->get_responder_spi(message),
|
||||
message->get_destination(message));
|
||||
this->natd_hash_r_matched = FALSE;
|
||||
this->natd_seen_r = 0;
|
||||
this->ike_sa->set_my_host_behind_nat(this->ike_sa, FALSE);
|
||||
this->ike_sa->set_other_host_behind_nat(this->ike_sa, FALSE);
|
||||
|
||||
/* Iterate over all payloads.
|
||||
*
|
||||
* The message is already checked for the right payload types.
|
||||
*/
|
||||
payloads = message->get_payload_iterator(message);
|
||||
while (payloads->has_next(payloads))
|
||||
{
|
||||
payload_t *payload;
|
||||
@@ -237,6 +318,7 @@ static status_t process_message(private_responder_init_t *this, message_t *messa
|
||||
payloads->destroy(payloads);
|
||||
return status;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
@@ -251,10 +333,39 @@ static status_t process_message(private_responder_init_t *this, message_t *messa
|
||||
/* check if we have all payloads */
|
||||
if (!(sa_request && ke_request && nonce_request))
|
||||
{
|
||||
this->logger->log(this->logger, AUDIT, "IKE_SA_INIT request did not contain all required payloads. deleting IKE_SA");
|
||||
this->logger->log(this->logger, AUDIT, "IKE_SA_INIT request did not contain all required payloads. Deleting IKE_SA");
|
||||
return DESTROY_ME;
|
||||
}
|
||||
|
||||
/* NAT-D */
|
||||
if ((!this->natd_seen_i && this->natd_seen_r > 0)
|
||||
|| (this->natd_seen_i > 0 && !this->natd_seen_r))
|
||||
{
|
||||
this->logger->log(this->logger, AUDIT, "IKE_SA_INIT request contained wrong number of NAT-D payloads. Deleting IKE_SA");
|
||||
return DESTROY_ME;
|
||||
}
|
||||
if (this->natd_seen_r > 1)
|
||||
{
|
||||
this->logger->log(this->logger, AUDIT, "Warning: IKE_SA_INIT request contained multiple Notify(NAT_DETECTION_DESTINATION_IP) payloads.");
|
||||
}
|
||||
if (this->natd_seen_i > 0 && !this->natd_hash_i_matched)
|
||||
{
|
||||
this->logger->log(this->logger, AUDIT, "Remote host is behind NAT, using NAT-T.");
|
||||
this->ike_sa->set_other_host_behind_nat(this->ike_sa, TRUE);
|
||||
}
|
||||
if (this->natd_seen_r > 0 && !this->natd_hash_r_matched)
|
||||
{
|
||||
this->logger->log(this->logger, AUDIT, "Local host is behind NAT, using NAT-T.");
|
||||
this->ike_sa->set_my_host_behind_nat(this->ike_sa, TRUE);
|
||||
charon->event_queue->add_relative(charon->event_queue,
|
||||
(job_t*)send_keepalive_job_create(this->ike_sa->public.get_id((ike_sa_t*)this->ike_sa)),
|
||||
charon->configuration->get_keepalive_interval(charon->configuration));
|
||||
}
|
||||
if (!this->ike_sa->public.is_any_host_behind_nat((ike_sa_t*)this->ike_sa))
|
||||
{
|
||||
this->logger->log(this->logger, AUDIT, "No NAT detected, not using NAT-T.");
|
||||
}
|
||||
|
||||
this->ike_sa->build_message(this->ike_sa, IKE_SA_INIT, FALSE, &response);
|
||||
|
||||
status = this->build_sa_payload(this, sa_request, response);
|
||||
@@ -277,7 +388,9 @@ static status_t process_message(private_responder_init_t *this, message_t *messa
|
||||
response->destroy(response);
|
||||
return status;
|
||||
}
|
||||
|
||||
/* build Notify(NAT-D) payloads */
|
||||
this->build_natd_payloads(this, response);
|
||||
|
||||
/* derive all the keys used in the IKE_SA */
|
||||
status = this->ike_sa->build_transforms(this->ike_sa, this->proposal, this->diffie_hellman, this->received_nonce, this->sent_nonce);
|
||||
if (status != SUCCESS)
|
||||
@@ -458,27 +571,95 @@ static status_t build_nonce_payload(private_responder_init_t *this,nonce_payload
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of private_initiator_init_t.build_natd_payload.
|
||||
*/
|
||||
static void build_natd_payload(private_responder_init_t *this, message_t *request, notify_message_type_t type, host_t *host)
|
||||
{
|
||||
chunk_t hash;
|
||||
this->logger->log(this->logger, CONTROL|LEVEL1, "Building Notify(NAT-D) payload");
|
||||
notify_payload_t *notify_payload;
|
||||
notify_payload = notify_payload_create();
|
||||
/*notify_payload->set_protocol_id(notify_payload, NULL);*/
|
||||
/*notify_payload->set_spi(notify_payload, NULL);*/
|
||||
notify_payload->set_notify_message_type(notify_payload, type);
|
||||
hash = this->ike_sa->generate_natd_hash(this->ike_sa,
|
||||
request->get_initiator_spi(request),
|
||||
request->get_responder_spi(request),
|
||||
host);
|
||||
notify_payload->set_notification_data(notify_payload, hash);
|
||||
chunk_free(&hash);
|
||||
this->logger->log(this->logger, CONTROL|LEVEL2, "Add Notify(NAT-D) payload to message");
|
||||
request->add_payload(request, (payload_t *) notify_payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of private_initiator_init_t.build_natd_payloads.
|
||||
*/
|
||||
static void build_natd_payloads(private_responder_init_t *this, message_t *request)
|
||||
{
|
||||
connection_t *connection;
|
||||
connection = this->ike_sa->get_connection(this->ike_sa);
|
||||
this->build_natd_payload(this, request, NAT_DETECTION_SOURCE_IP,
|
||||
connection->get_my_host(connection));
|
||||
this->build_natd_payload(this, request, NAT_DETECTION_DESTINATION_IP,
|
||||
connection->get_other_host(connection));
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of private_responder_init_t.process_notify_payload.
|
||||
*/
|
||||
static status_t process_notify_payload(private_responder_init_t *this, notify_payload_t *notify_payload)
|
||||
{
|
||||
chunk_t notification_data;
|
||||
notify_message_type_t notify_message_type = notify_payload->get_notify_message_type(notify_payload);
|
||||
|
||||
this->logger->log(this->logger, CONTROL|LEVEL1, "process notify type %s",
|
||||
mapping_find(notify_message_type_m, notify_message_type));
|
||||
|
||||
if (notify_payload->get_protocol_id(notify_payload) != PROTO_IKE)
|
||||
{
|
||||
this->logger->log(this->logger, ERROR | LEVEL1, "notify reply not for IKE protocol.");
|
||||
return FAILED;
|
||||
}
|
||||
switch (notify_message_type)
|
||||
{
|
||||
case NAT_DETECTION_DESTINATION_IP:
|
||||
{
|
||||
this->natd_seen_r++;
|
||||
if (this->natd_hash_r_matched)
|
||||
return SUCCESS;
|
||||
|
||||
notification_data = notify_payload->get_notification_data(notify_payload);
|
||||
if (chunk_equals(notification_data, this->natd_hash_r))
|
||||
{
|
||||
this->natd_hash_r_matched = TRUE;
|
||||
this->logger->log(this->logger, CONTROL|LEVEL3, "NAT-D hash match");
|
||||
}
|
||||
else
|
||||
{
|
||||
this->logger->log(this->logger, CONTROL|LEVEL3, "NAT-D hash mismatch");
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
case NAT_DETECTION_SOURCE_IP:
|
||||
{
|
||||
this->natd_seen_i++;
|
||||
if (this->natd_hash_i_matched)
|
||||
return SUCCESS;
|
||||
|
||||
notification_data = notify_payload->get_notification_data(notify_payload);
|
||||
if (chunk_equals(notification_data, this->natd_hash_i))
|
||||
{
|
||||
this->natd_hash_i_matched = TRUE;
|
||||
this->logger->log(this->logger, CONTROL|LEVEL3, "NAT-D hash match");
|
||||
}
|
||||
else
|
||||
{
|
||||
this->logger->log(this->logger, CONTROL|LEVEL3, "NAT-D hash mismatch");
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
default:
|
||||
{
|
||||
this->logger->log(this->logger, CONTROL, "IKE_SA_INIT request contained a notify (%d), ignored.",
|
||||
notify_message_type);
|
||||
this->logger->log(this->logger, CONTROL, "IKE_SA_INIT request contained a notify (%d), ignored.",
|
||||
notify_message_type);
|
||||
return SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -501,8 +682,12 @@ static void destroy(private_responder_init_t *this)
|
||||
|
||||
this->logger->log(this->logger, CONTROL | LEVEL2, "destroy nonces");
|
||||
chunk_free(&(this->sent_nonce));
|
||||
this->logger->log(this->logger, CONTROL | LEVEL2, "destroy received nonce");
|
||||
chunk_free(&(this->received_nonce));
|
||||
|
||||
chunk_free(&(this->natd_hash_i));
|
||||
chunk_free(&(this->natd_hash_r));
|
||||
|
||||
if (this->diffie_hellman != NULL)
|
||||
{
|
||||
this->logger->log(this->logger, CONTROL | LEVEL2, "destroy diffie_hellman_t hellman object");
|
||||
@@ -521,7 +706,10 @@ static void destroy(private_responder_init_t *this)
|
||||
*/
|
||||
static void destroy_after_state_change (private_responder_init_t *this)
|
||||
{
|
||||
this->logger->log(this->logger, CONTROL | LEVEL1, "going to destroy responder_init_t state object");
|
||||
this->logger->log(this->logger, CONTROL | LEVEL1, "Going to destroy responder_init_t state object");
|
||||
|
||||
chunk_free(&(this->natd_hash_i));
|
||||
chunk_free(&(this->natd_hash_r));
|
||||
|
||||
/* destroy diffie hellman object */
|
||||
if (this->diffie_hellman != NULL)
|
||||
@@ -556,6 +744,8 @@ responder_init_t *responder_init_create(protected_ike_sa_t *ike_sa)
|
||||
this->build_nonce_payload = build_nonce_payload;
|
||||
this->destroy_after_state_change = destroy_after_state_change;
|
||||
this->process_notify_payload = process_notify_payload;
|
||||
this->build_natd_payload = build_natd_payload;
|
||||
this->build_natd_payloads = build_natd_payloads;
|
||||
|
||||
/* private data */
|
||||
this->ike_sa = ike_sa;
|
||||
@@ -565,6 +755,12 @@ responder_init_t *responder_init_create(protected_ike_sa_t *ike_sa)
|
||||
this->dh_group_number = MODP_NONE;
|
||||
this->diffie_hellman = NULL;
|
||||
this->proposal = NULL;
|
||||
this->natd_hash_i = CHUNK_INITIALIZER;
|
||||
this->natd_hash_i_matched = FALSE;
|
||||
this->natd_seen_i = 0;
|
||||
this->natd_hash_r = CHUNK_INITIALIZER;
|
||||
this->natd_hash_r_matched = FALSE;
|
||||
this->natd_seen_r = 0;
|
||||
|
||||
return &(this->public);
|
||||
}
|
||||
|
||||
@@ -38,3 +38,4 @@ mapping_t ike_sa_state_m[] = {
|
||||
{DELETE_CHILD_SA_REQUESTED, "DELETE_CHILD_SA_REQUESTED"},
|
||||
{MAPPING_END, NULL}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user