return ALREADY_DONE;
}
+/**
+ * Schedule a timeout for the IKE_SA should it not establish
+ */
+static void schedule_timeout(ike_sa_t *ike_sa)
+{
+ job_t *job;
+
+ job = (job_t*)delete_ike_sa_job_create(ike_sa->get_id(ike_sa), FALSE);
+ lib->scheduler->schedule_job(lib->scheduler, job, HALF_OPEN_IKE_SA_TIMEOUT);
+}
+
METHOD(task_t, build_i, status_t,
private_aggressive_mode_t *this, message_t *message)
{
case AUTH_XAUTH_INIT_PSK:
case AUTH_XAUTH_INIT_RSA:
case AUTH_HYBRID_INIT_RSA:
- { /* wait for XAUTH request, since this may never come,
- * we queue a timeout */
- job_t *job = (job_t*)delete_ike_sa_job_create(
- this->ike_sa->get_id(this->ike_sa), FALSE);
- lib->scheduler->schedule_job(lib->scheduler, job,
- HALF_OPEN_IKE_SA_TIMEOUT);
+ /* wait for XAUTH request */
+ schedule_timeout(this->ike_sa);
break;
- }
case AUTH_XAUTH_RESP_PSK:
case AUTH_XAUTH_RESP_RSA:
case AUTH_HYBRID_RESP_RSA:
this->ike_sa->queue_task(this->ike_sa,
(task_t*)xauth_create(this->ike_sa, TRUE));
- return SUCCESS;
+ break;
default:
if (charon->ike_sa_manager->check_uniqueness(
charon->ike_sa_manager, this->ike_sa, FALSE))
}
break;
}
+ /* check for and prepare mode config push/pull */
if (this->ph1->has_virtual_ip(this->ph1, this->peer_cfg))
{
- this->ike_sa->queue_task(this->ike_sa,
- (task_t*)mode_config_create(this->ike_sa, TRUE));
+ if (this->peer_cfg->use_pull_mode(this->peer_cfg))
+ {
+ this->ike_sa->queue_task(this->ike_sa,
+ (task_t*)mode_config_create(this->ike_sa, TRUE, TRUE));
+ }
+ else
+ {
+ schedule_timeout(this->ike_sa);
+ }
+ }
+ else if (this->ph1->has_pool(this->ph1, this->peer_cfg))
+ {
+ if (this->peer_cfg->use_pull_mode(this->peer_cfg))
+ {
+ schedule_timeout(this->ike_sa);
+ }
+ else
+ {
+ this->ike_sa->queue_task(this->ike_sa,
+ (task_t*)mode_config_create(this->ike_sa, TRUE, FALSE));
+ }
}
return SUCCESS;
}
case AUTH_HYBRID_INIT_RSA:
this->ike_sa->queue_task(this->ike_sa,
(task_t*)xauth_create(this->ike_sa, TRUE));
- return SUCCESS;
+ break;
case AUTH_XAUTH_RESP_PSK:
case AUTH_XAUTH_RESP_RSA:
case AUTH_HYBRID_RESP_RSA:
this->ike_sa->get_id(this->ike_sa)));
break;
}
- if (!this->ph1->has_pool(this->ph1, this->peer_cfg) &&
- this->ph1->has_virtual_ip(this->ph1, this->peer_cfg))
+ /* check for and prepare mode config push/pull */
+ if (this->ph1->has_virtual_ip(this->ph1, this->peer_cfg))
{
- this->ike_sa->queue_task(this->ike_sa,
- (task_t*)mode_config_create(this->ike_sa, TRUE));
+ if (this->peer_cfg->use_pull_mode(this->peer_cfg))
+ {
+ this->ike_sa->queue_task(this->ike_sa,
+ (task_t*)mode_config_create(this->ike_sa, TRUE, TRUE));
+ }
+ }
+ else if (this->ph1->has_pool(this->ph1, this->peer_cfg))
+ {
+ if (!this->peer_cfg->use_pull_mode(this->peer_cfg))
+ {
+ this->ike_sa->queue_task(this->ike_sa,
+ (task_t*)mode_config_create(this->ike_sa, TRUE, FALSE));
+ }
}
return SUCCESS;
}
case AUTH_HYBRID_INIT_RSA:
this->ike_sa->queue_task(this->ike_sa,
(task_t*)xauth_create(this->ike_sa, TRUE));
- return SUCCESS;
+ break;
case AUTH_XAUTH_RESP_PSK:
case AUTH_XAUTH_RESP_RSA:
case AUTH_HYBRID_RESP_RSA:
this->ike_sa->get_id(this->ike_sa)));
break;
}
- if (!this->ph1->has_pool(this->ph1, this->peer_cfg) &&
- this->ph1->has_virtual_ip(this->ph1, this->peer_cfg))
+ if (this->ph1->has_virtual_ip(this->ph1, this->peer_cfg))
+ {
+ if (this->peer_cfg->use_pull_mode(this->peer_cfg))
+ {
+ this->ike_sa->queue_task(this->ike_sa,
+ (task_t*)mode_config_create(this->ike_sa, TRUE, TRUE));
+ }
+ }
+ else if (this->ph1->has_pool(this->ph1, this->peer_cfg))
{
- this->ike_sa->queue_task(this->ike_sa,
- (task_t*)mode_config_create(this->ike_sa, TRUE));
+ if (!this->peer_cfg->use_pull_mode(this->peer_cfg))
+ {
+ this->ike_sa->queue_task(this->ike_sa,
+ (task_t*)mode_config_create(this->ike_sa, TRUE, FALSE));
+ }
}
return SUCCESS;
}
}
}
+/**
+ * Schedule a timeout for the IKE_SA should it not establish
+ */
+static void schedule_timeout(ike_sa_t *ike_sa)
+{
+ job_t *job;
+
+ job = (job_t*)delete_ike_sa_job_create(ike_sa->get_id(ike_sa), FALSE);
+ lib->scheduler->schedule_job(lib->scheduler, job, HALF_OPEN_IKE_SA_TIMEOUT);
+}
+
METHOD(task_t, process_i, status_t,
private_main_mode_t *this, message_t *message)
{
case AUTH_XAUTH_INIT_PSK:
case AUTH_XAUTH_INIT_RSA:
case AUTH_HYBRID_INIT_RSA:
- { /* wait for XAUTH request, since this may never come,
- * we queue a timeout */
- job_t *job = (job_t*)delete_ike_sa_job_create(
- this->ike_sa->get_id(this->ike_sa), FALSE);
- lib->scheduler->schedule_job(lib->scheduler, job,
- HALF_OPEN_IKE_SA_TIMEOUT);
+ /* wait for XAUTH request */
+ schedule_timeout(this->ike_sa);
break;
- }
case AUTH_XAUTH_RESP_PSK:
case AUTH_XAUTH_RESP_RSA:
case AUTH_HYBRID_RESP_RSA:
this->ike_sa->queue_task(this->ike_sa,
(task_t*)xauth_create(this->ike_sa, TRUE));
- return SUCCESS;
+ break;
default:
if (charon->ike_sa_manager->check_uniqueness(
charon->ike_sa_manager, this->ike_sa, FALSE))
}
break;
}
+ /* check for and prepare mode config push/pull */
if (this->ph1->has_virtual_ip(this->ph1, this->peer_cfg))
{
- this->ike_sa->queue_task(this->ike_sa,
- (task_t*)mode_config_create(this->ike_sa, TRUE));
+ if (this->peer_cfg->use_pull_mode(this->peer_cfg))
+ {
+ this->ike_sa->queue_task(this->ike_sa,
+ (task_t*)mode_config_create(this->ike_sa, TRUE, TRUE));
+ }
+ else
+ {
+ schedule_timeout(this->ike_sa);
+ }
+ }
+ else if (this->ph1->has_pool(this->ph1, this->peer_cfg))
+ {
+ if (this->peer_cfg->use_pull_mode(this->peer_cfg))
+ {
+ schedule_timeout(this->ike_sa);
+ }
+ else
+ {
+ this->ike_sa->queue_task(this->ike_sa,
+ (task_t*)mode_config_create(this->ike_sa, TRUE, FALSE));
+ }
}
return SUCCESS;
}
*/
bool initiator;
+ /**
+ * Use pull (CFG_REQUEST/RESPONSE) or push (CFG_SET/ACK)?
+ */
+ bool pull;
+
/**
* Received list of virtual IPs, host_t*
*/
linked_list_t *vips;
/**
- * list of attributes requested and its handler, entry_t
+ * Requested/received list of attributes, entry_t
*/
- linked_list_t *requested;
+ linked_list_t *attributes;
/**
* Identifier to include in response
};
/**
- * Entry for a requested attribute and the requesting handler
+ * Entry for a attribute and associated handler
*/
typedef struct {
- /** attribute requested */
+ /** attribute type */
configuration_attribute_type_t type;
- /** handler requesting this attribute */
+ /** handler for this attribute */
attribute_handler_t *handler;
} entry_t;
entry_t *entry;
/* find the handler which requested this attribute */
- enumerator = this->requested->create_enumerator(this->requested);
+ enumerator = this->attributes->create_enumerator(this->attributes);
while (enumerator->enumerate(enumerator, &entry))
{
if (entry->type == ca->get_type(ca))
{
handler = entry->handler;
- this->requested->remove_at(this->requested, enumerator);
+ this->attributes->remove_at(this->attributes, enumerator);
free(entry);
break;
}
}
default:
{
- if (this->initiator)
+ if (this->initiator == this->pull)
{
handle_attribute(this, ca);
}
}
}
+/**
+ * Check if config allows push mode when acting as task responder
+ */
+static bool accept_push(private_mode_config_t *this)
+{
+ enumerator_t *enumerator;
+ peer_cfg_t *config;
+ bool vip;
+ host_t *host;
+
+ config = this->ike_sa->get_peer_cfg(this->ike_sa);
+ enumerator = config->create_virtual_ip_enumerator(config);
+ vip = enumerator->enumerate(enumerator, &host);
+ enumerator->destroy(enumerator);
+
+ return vip && !config->use_pull_mode(config);
+}
+
/**
* Scan for configuration payloads and attributes
*/
switch (cp->get_type(cp))
{
+ case CFG_SET:
+ /* when acting as a responder, we detect the mode using
+ * the type of configuration payload. But we should double
+ * check the peer is allowed to use push mode on us. */
+ if (!this->initiator && accept_push(this))
+ {
+ this->pull = FALSE;
+ }
+ /* FALL */
case CFG_REQUEST:
this->identifier = cp->get_identifier(cp);
/* FALL */
}
attributes->destroy(attributes);
break;
+ case CFG_ACK:
+ break;
default:
DBG1(DBG_IKE, "ignoring %N config payload",
config_type_names, cp->get_type(cp));
enumerator->destroy(enumerator);
}
-METHOD(task_t, build_i, status_t,
- private_mode_config_t *this, message_t *message)
+/**
+ * Add an attribute to a configuration payload, and store it in task
+ */
+static void add_attribute(private_mode_config_t *this, cp_payload_t *cp,
+ configuration_attribute_type_t type, chunk_t data,
+ attribute_handler_t *handler)
+{
+ entry_t *entry;
+
+ cp->add_attribute(cp,
+ configuration_attribute_create_chunk(CONFIGURATION_ATTRIBUTE_V1,
+ type, data));
+ INIT(entry,
+ .type = type,
+ .handler = handler,
+ );
+ this->attributes->insert_last(this->attributes, entry);
+}
+
+/**
+ * Build a CFG_REQUEST as initiator
+ */
+static status_t build_request(private_mode_config_t *this, message_t *message)
{
cp_payload_t *cp;
enumerator_t *enumerator;
this->ike_sa->get_other_id(this->ike_sa), vips);
while (enumerator->enumerate(enumerator, &handler, &type, &data))
{
- entry_t *entry;
-
- DBG2(DBG_IKE, "building %N attribute",
- configuration_attribute_type_names, type);
- cp->add_attribute(cp,
- configuration_attribute_create_chunk(CONFIGURATION_ATTRIBUTE_V1,
- type, data));
- INIT(entry,
- .type = type,
- .handler = handler,
- );
- this->requested->insert_last(this->requested, entry);
+ add_attribute(this, cp, type, data, handler);
}
enumerator->destroy(enumerator);
return NEED_MORE;
}
+/**
+ * Build a CFG_SET as initiator
+ */
+static status_t build_set(private_mode_config_t *this, message_t *message)
+{
+ enumerator_t *enumerator;
+ configuration_attribute_type_t type;
+ chunk_t value;
+ cp_payload_t *cp;
+ peer_cfg_t *config;
+ identification_t *id;
+ linked_list_t *pools;
+ host_t *any4, *any6, *found;
+ char *name;
+
+ cp = cp_payload_create_type(CONFIGURATION_V1, CFG_SET);
+
+ id = this->ike_sa->get_other_eap_id(this->ike_sa);
+ config = this->ike_sa->get_peer_cfg(this->ike_sa);
+ any4 = host_create_any(AF_INET);
+ any6 = host_create_any(AF_INET6);
+
+ this->ike_sa->clear_virtual_ips(this->ike_sa, FALSE);
+
+ /* in push mode, we ask each configured pool for an address */
+ enumerator = config->create_pool_enumerator(config);
+ while (enumerator->enumerate(enumerator, &name))
+ {
+ pools = linked_list_create_with_items(name, NULL);
+ /* try IPv4, then IPv6 */
+ found = hydra->attributes->acquire_address(hydra->attributes,
+ pools, id, any4);
+ if (!found)
+ {
+ found = hydra->attributes->acquire_address(hydra->attributes,
+ pools, id, any6);
+ }
+ pools->destroy(pools);
+ if (found)
+ {
+ DBG1(DBG_IKE, "assigning virtual IP %H to peer '%Y'", found, id);
+ this->ike_sa->add_virtual_ip(this->ike_sa, FALSE, found);
+ cp->add_attribute(cp, build_vip(found));
+ this->vips->insert_last(this->vips, found);
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ any4->destroy(any4);
+ any6->destroy(any6);
+
+ /* query registered providers for additional attributes to include */
+ pools = linked_list_create_from_enumerator(
+ config->create_pool_enumerator(config));
+ enumerator = hydra->attributes->create_responder_enumerator(
+ hydra->attributes, pools, id, this->vips);
+ while (enumerator->enumerate(enumerator, &type, &value))
+ {
+ add_attribute(this, cp, type, value, NULL);
+ }
+ enumerator->destroy(enumerator);
+ pools->destroy(pools);
+
+ message->add_payload(message, (payload_t*)cp);
+
+ return SUCCESS;
+}
+
+METHOD(task_t, build_i, status_t,
+ private_mode_config_t *this, message_t *message)
+{
+ if (this->pull)
+ {
+ return build_request(this, message);
+ }
+ return build_set(this, message);
+}
+
+/**
+ * Store received virtual IPs to the IKE_SA, install them
+ */
+static void install_vips(private_mode_config_t *this)
+{
+ enumerator_t *enumerator;
+ host_t *host;
+
+ this->ike_sa->clear_virtual_ips(this->ike_sa, TRUE);
+
+ enumerator = this->vips->create_enumerator(this->vips);
+ while (enumerator->enumerate(enumerator, &host))
+ {
+ if (!host->is_anyaddr(host))
+ {
+ this->ike_sa->add_virtual_ip(this->ike_sa, TRUE, host);
+ }
+ }
+ enumerator->destroy(enumerator);
+}
+
METHOD(task_t, process_r, status_t,
private_mode_config_t *this, message_t *message)
{
process_payloads(this, message);
+
+ if (!this->pull)
+ {
+ install_vips(this);
+ }
return NEED_MORE;
}
-METHOD(task_t, build_r, status_t,
- private_mode_config_t *this, message_t *message)
+/**
+ * Build CFG_REPLY message after receiving CFG_REQUEST
+ */
+static status_t build_reply(private_mode_config_t *this, message_t *message)
{
enumerator_t *enumerator;
configuration_attribute_type_t type;
hydra->attributes, pools, id, vips);
while (enumerator->enumerate(enumerator, &type, &value))
{
- DBG2(DBG_IKE, "building %N attribute",
- configuration_attribute_type_names, type);
cp->add_attribute(cp,
configuration_attribute_create_chunk(CONFIGURATION_ATTRIBUTE_V1,
type, value));
return SUCCESS;
}
-METHOD(task_t, process_i, status_t,
- private_mode_config_t *this, message_t *message)
+/**
+ * Build CFG_ACK for a received CFG_SET
+ */
+static status_t build_ack(private_mode_config_t *this, message_t *message)
{
+ cp_payload_t *cp;
enumerator_t *enumerator;
host_t *host;
+ configuration_attribute_type_t type;
+ entry_t *entry;
- process_payloads(this, message);
+ cp = cp_payload_create_type(CONFIGURATION_V1, CFG_ACK);
- this->ike_sa->clear_virtual_ips(this->ike_sa, TRUE);
+ /* return empty attributes for installed IPs */
enumerator = this->vips->create_enumerator(this->vips);
while (enumerator->enumerate(enumerator, &host))
{
- if (!host->is_anyaddr(host))
+ type = INTERNAL_IP6_ADDRESS;
+ if (host->get_family(host) == AF_INET6)
{
- this->ike_sa->add_virtual_ip(this->ike_sa, TRUE, host);
+ type = INTERNAL_IP6_ADDRESS;
}
+ else
+ {
+ type = INTERNAL_IP4_ADDRESS;
+ }
+ cp->add_attribute(cp, configuration_attribute_create_chunk(
+ CONFIGURATION_ATTRIBUTE_V1, type, chunk_empty));
}
enumerator->destroy(enumerator);
+ enumerator = this->attributes->create_enumerator(this->attributes);
+ while (enumerator->enumerate(enumerator, &entry))
+ {
+ cp->add_attribute(cp,
+ configuration_attribute_create_chunk(CONFIGURATION_ATTRIBUTE_V1,
+ entry->type, chunk_empty));
+ }
+ enumerator->destroy(enumerator);
+
+ cp->set_identifier(cp, this->identifier);
+ message->add_payload(message, (payload_t*)cp);
+
+ return SUCCESS;
+}
+
+METHOD(task_t, build_r, status_t,
+ private_mode_config_t *this, message_t *message)
+{
+ if (this->pull)
+ {
+ return build_reply(this, message);
+ }
+ return build_ack(this, message);
+}
+
+METHOD(task_t, process_i, status_t,
+ private_mode_config_t *this, message_t *message)
+{
+ process_payloads(this, message);
+
+ if (this->pull)
+ {
+ install_vips(this);
+ }
return SUCCESS;
}
this->ike_sa = ike_sa;
this->vips->destroy_offset(this->vips, offsetof(host_t, destroy));
this->vips = linked_list_create();
- this->requested->destroy_function(this->requested, free);
- this->requested = linked_list_create();
+ this->attributes->destroy_function(this->attributes, free);
+ this->attributes = linked_list_create();
}
METHOD(task_t, destroy, void,
private_mode_config_t *this)
{
this->vips->destroy_offset(this->vips, offsetof(host_t, destroy));
- this->requested->destroy_function(this->requested, free);
+ this->attributes->destroy_function(this->attributes, free);
free(this);
}
/*
* Described in header.
*/
-mode_config_t *mode_config_create(ike_sa_t *ike_sa, bool initiator)
+mode_config_t *mode_config_create(ike_sa_t *ike_sa, bool initiator, bool pull)
{
private_mode_config_t *this;
},
},
.initiator = initiator,
+ .pull = initiator ? pull : TRUE,
.ike_sa = ike_sa,
- .requested = linked_list_create(),
+ .attributes = linked_list_create(),
.vips = linked_list_create(),
);