Merge branch 'ike-update-event'

This modifies the signature of the listener_t::ike_update() callback so
that both addresses are passed and it's only called once if both
addresses change (e.g. for an address family switch).

The callback is now also triggered for MOBIKE updates and the event is
exposed via vici.

Fixes #3602.
This commit is contained in:
Tobias Brunner
2021-01-18 13:33:26 +01:00
15 changed files with 182 additions and 164 deletions
+4 -3
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2011-2016 Tobias Brunner
* Copyright (C) 2011-2020 Tobias Brunner
* Copyright (C) 2006 Martin Willi
* HSR Hochschule fuer Technik Rapperswil
*
@@ -866,7 +866,7 @@ METHOD(bus_t, ike_rekey, void,
}
METHOD(bus_t, ike_update, void,
private_bus_t *this, ike_sa_t *ike_sa, bool local, host_t *new)
private_bus_t *this, ike_sa_t *ike_sa, host_t *local, host_t *remote)
{
enumerator_t *enumerator;
entry_t *entry;
@@ -881,7 +881,8 @@ METHOD(bus_t, ike_update, void,
continue;
}
entry->calling++;
keep = entry->listener->ike_update(entry->listener, ike_sa, local, new);
keep = entry->listener->ike_update(entry->listener, ike_sa, local,
remote);
entry->calling--;
if (!keep)
{
+5 -4
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2012-2016 Tobias Brunner
* Copyright (C) 2012-2020 Tobias Brunner
* Copyright (C) 2006-2009 Martin Willi
* HSR Hochschule fuer Technik Rapperswil
*
@@ -417,10 +417,11 @@ struct bus_t {
* IKE_SA peer endpoint update hook.
*
* @param ike_sa updated IKE_SA, having old endpoints set
* @param local TRUE if local endpoint gets updated, FALSE for remote
* @param new new endpoint address and port
* @param local new/current local endpoint address and port
* @param remote new/current remote endpoint address and port
*/
void (*ike_update)(bus_t *this, ike_sa_t *ike_sa, bool local, host_t *new);
void (*ike_update)(bus_t *this, ike_sa_t *ike_sa, host_t *local,
host_t *remote);
/**
* IKE_SA reestablishing hook (before resolving hosts).
+6 -4
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2011-2016 Tobias Brunner
* Copyright (C) 2011-2020 Tobias Brunner
* Copyright (C) 2009 Martin Willi
* HSR Hochschule fuer Technik Rapperswil
*
@@ -160,13 +160,15 @@ struct listener_t {
/**
* Hook called for IKE_SA peer endpoint updates.
*
* At least one endpoint has changed when this is invoked.
*
* @param ike_sa updated IKE_SA, having old endpoints set
* @param local TRUE if local endpoint gets updated, FALSE for remote
* @param new new endpoint address and port
* @param local new/current local endpoint address and port
* @param remote new/current remote endpoint address and port
* @return TRUE to stay registered, FALSE to unregister
*/
bool (*ike_update)(listener_t *this, ike_sa_t *ike_sa,
bool local, host_t *new);
host_t *local, host_t *remote);
/**
* Hook called when an initiator reestablishes an IKE_SA.
@@ -494,24 +494,13 @@ METHOD(listener_t, child_rekey, bool,
METHOD(listener_t, ike_update, bool,
private_connmark_listener_t *this, ike_sa_t *ike_sa,
bool local, host_t *new)
host_t *local, host_t *remote)
{
struct iptc_handle *ipth;
enumerator_t *enumerator;
child_sa_t *child_sa;
host_t *dst, *src;
bool oldencap, newencap;
if (local)
{
dst = new;
src = ike_sa->get_other_host(ike_sa);
}
else
{
dst = ike_sa->get_my_host(ike_sa);
src = new;
}
/* during ike_update(), has_encap() on the CHILD_SA has not yet been
* updated, but shows the old state. */
newencap = ike_sa->has_condition(ike_sa, COND_NAT_ANY);
@@ -525,9 +514,9 @@ METHOD(listener_t, ike_update, bool,
ipth = init_handle();
if (ipth)
{
if (manage_policies(this, ipth, dst, src, oldencap,
if (manage_policies(this, ipth, local, remote, oldencap,
child_sa, FALSE) &&
manage_policies(this, ipth, dst, src, newencap,
manage_policies(this, ipth, local, remote, newencap,
child_sa, TRUE))
{
commit_handle(ipth);
@@ -569,24 +569,13 @@ METHOD(listener_t, child_rekey, bool,
METHOD(listener_t, ike_update, bool,
private_forecast_listener_t *this, ike_sa_t *ike_sa,
bool local, host_t *new)
host_t *local, host_t *remote)
{
struct iptc_handle *ipth;
enumerator_t *enumerator;
child_sa_t *child_sa;
host_t *lhost, *rhost;
bool encap;
if (local)
{
lhost = new;
rhost = ike_sa->get_other_host(ike_sa);
}
else
{
lhost = ike_sa->get_my_host(ike_sa);
rhost = new;
}
/* during ike_update(), has_encap() on the CHILD_SA has not yet been
* updated, but shows the old state. */
encap = ike_sa->has_condition(ike_sa, COND_NAT_ANY);
@@ -600,7 +589,7 @@ METHOD(listener_t, ike_update, bool,
if (ipth)
{
if (remove_entry(this, ipth, child_sa) &&
add_entry(this, ipth, lhost, rhost, child_sa, encap))
add_entry(this, ipth, local, remote, child_sa, encap))
{
commit_handle(ipth);
}
+16
View File
@@ -976,6 +976,22 @@ The _ike-rekey_ event is issued when an IKE_SA is rekeyed.
}
}
### ike-update ###
The _ike-update_ event is issued when the local or remote endpoint address of an
IKE_SA is about to change (at least one address/port is different).
{
local-host = <new/current local IKE endpoint address>
local-port = <new/current local IKE endpoint port>
remote-host = <new/current remote IKE endpoint address>
remote-port = <new/current remote IKE endpoint port>
<IKE_SA config name> = {
<same data as in the list-sas event, but without child-sas section
and listing the old addresses/ports>
}
}
### child-updown ###
The _child-updown_ event is issued when a CHILD_SA is established or terminated.
+34 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2015-2017 Tobias Brunner
* Copyright (C) 2015-2020 Tobias Brunner
* Copyright (C) 2015-2018 Andreas Steffen
* HSR Hochschule fuer Technik Rapperswil
*
@@ -510,6 +510,7 @@ CALLBACK(list_sas, vici_message_t*,
bool bl;
char buf[BUF_LEN];
bl = request->get_str(request, NULL, "noblock") == NULL;
ike = request->get_str(request, NULL, "ike");
ike_id = request->get_int(request, 0, "ike-id");
@@ -1683,6 +1684,7 @@ static void manage_commands(private_vici_query_t *this, bool reg)
this->dispatcher->manage_event(this->dispatcher, "list-cert", reg);
this->dispatcher->manage_event(this->dispatcher, "ike-updown", reg);
this->dispatcher->manage_event(this->dispatcher, "ike-rekey", reg);
this->dispatcher->manage_event(this->dispatcher, "ike-update", reg);
this->dispatcher->manage_event(this->dispatcher, "child-updown", reg);
this->dispatcher->manage_event(this->dispatcher, "child-rekey", reg);
manage_command(this, "list-sas", list_sas, reg);
@@ -1755,6 +1757,36 @@ METHOD(listener_t, ike_rekey, bool,
return TRUE;
}
METHOD(listener_t, ike_update, bool,
private_vici_query_t *this, ike_sa_t *ike_sa, host_t *local, host_t *remote)
{
vici_builder_t *b;
time_t now;
if (!this->dispatcher->has_event_listeners(this->dispatcher, "ike-update"))
{
return TRUE;
}
now = time_monotonic(NULL);
b = vici_builder_create();
b->add_kv(b, "local-host", "%H", local);
b->add_kv(b, "local-port", "%d", local->get_port(local));
b->add_kv(b, "remote-host", "%H", remote);
b->add_kv(b, "remote-port", "%d", remote->get_port(remote));
b->begin_section(b, ike_sa->get_name(ike_sa));
list_ike(this, b, ike_sa, now);
b->end_section(b);
this->dispatcher->raise_event(this->dispatcher,
"ike-update", 0, b->finalize(b));
return TRUE;
}
METHOD(listener_t, child_updown, bool,
private_vici_query_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa, bool up)
{
@@ -1853,6 +1885,7 @@ vici_query_t *vici_query_create(vici_dispatcher_t *dispatcher)
.listener = {
.ike_updown = _ike_updown,
.ike_rekey = _ike_rekey,
.ike_update = _ike_update,
.child_updown = _child_updown,
.child_rekey = _child_rekey,
},
@@ -76,7 +76,7 @@ METHOD(job_t, execute, job_requeue_t,
}
else
{
ike_sa->update_hosts(ike_sa, NULL, this->new, FALSE);
ike_sa->update_hosts(ike_sa, NULL, this->new, 0);
charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
}
return JOB_REQUEUE_NONE;
+28 -17
View File
@@ -1114,9 +1114,10 @@ METHOD(ike_sa_t, float_ports, void,
}
METHOD(ike_sa_t, update_hosts, void,
private_ike_sa_t *this, host_t *me, host_t *other, bool force)
private_ike_sa_t *this, host_t *me, host_t *other, update_hosts_flag_t flags)
{
bool update = FALSE;
host_t *new_me = NULL, *new_other = NULL;
bool silent = FALSE;
if (me == NULL)
{
@@ -1131,43 +1132,53 @@ METHOD(ike_sa_t, update_hosts, void,
if (this->my_host->is_anyaddr(this->my_host) ||
this->other_host->is_anyaddr(this->other_host))
{
set_my_host(this, me->clone(me));
set_other_host(this, other->clone(other));
update = TRUE;
new_me = me;
new_other = other;
silent = TRUE;
}
else
{
/* update our address in any case */
if (force && !me->equals(me, this->my_host))
/* update our address only if forced */
if ((flags & UPDATE_HOSTS_FORCE_LOCAL) && !me->equals(me, this->my_host))
{
charon->bus->ike_update(charon->bus, &this->public, TRUE, me);
set_my_host(this, me->clone(me));
update = TRUE;
new_me = me;
}
if (!other->equals(other, this->other_host) &&
(force || has_condition(this, COND_NAT_THERE)))
((flags & UPDATE_HOSTS_FORCE_REMOTE) || has_condition(this, COND_NAT_THERE)))
{
/* only update other's address if we are behind a static NAT,
* which we assume is the case if we are not initiator */
if (force ||
if ((flags & UPDATE_HOSTS_FORCE_REMOTE) ||
(!has_condition(this, COND_NAT_HERE) ||
!has_condition(this, COND_ORIGINAL_INITIATOR)))
{
charon->bus->ike_update(charon->bus, &this->public, FALSE, other);
set_other_host(this, other->clone(other));
update = TRUE;
new_other = other;
}
}
}
/* update all associated CHILD_SAs, if required */
if (update)
if (new_me || new_other || (flags & UPDATE_HOSTS_FORCE_CHILDREN))
{
enumerator_t *enumerator;
child_sa_t *child_sa;
linked_list_t *vips;
if ((new_me || new_other) && !silent)
{
charon->bus->ike_update(charon->bus, &this->public,
new_me ?: this->my_host,
new_other ?: this->other_host);
}
if (new_me)
{
set_my_host(this, new_me->clone(new_me));
}
if (new_other)
{
set_other_host(this, new_other->clone(new_other));
}
vips = linked_list_create_from_enumerator(
array_create_enumerator(this->my_vips));
+24 -3
View File
@@ -28,6 +28,7 @@ typedef enum ike_extension_t ike_extension_t;
typedef enum ike_condition_t ike_condition_t;
typedef enum ike_sa_state_t ike_sa_state_t;
typedef enum statistic_t statistic_t;
typedef enum update_hosts_flag_t update_hosts_flag_t;
typedef struct ike_sa_t ike_sa_t;
#include <library.h>
@@ -264,6 +265,25 @@ enum statistic_t {
STAT_MAX
};
/**
* Flags used when updating addresses
*/
enum update_hosts_flag_t {
/** Force updating the local address (otherwise not updated if an address
* is already set). */
UPDATE_HOSTS_FORCE_LOCAL = (1<<0),
/** Force updating the remote address (otherwise only updated if peer is
* behind a NAT). */
UPDATE_HOSTS_FORCE_REMOTE = (1<<1),
/** Force updating both addresses. */
UPDATE_HOSTS_FORCE_ADDRS = UPDATE_HOSTS_FORCE_LOCAL|UPDATE_HOSTS_FORCE_REMOTE,
/** Force updating the CHILD_SAs even if no addresses changed, useful if
* NAT state may have changed. */
UPDATE_HOSTS_FORCE_CHILDREN = (1<<2),
/** Force updating everything. */
UPDATE_HOSTS_FORCE_ALL = UPDATE_HOSTS_FORCE_ADDRS|UPDATE_HOSTS_FORCE_CHILDREN,
};
/**
* State of an IKE_SA.
*
@@ -454,15 +474,16 @@ struct ike_sa_t {
void (*float_ports)(ike_sa_t *this);
/**
* Update the IKE_SAs host.
* Update the IKE_SAs host and CHILD_SAs.
*
* Hosts may be NULL to use current host.
*
* @param me new local host address, or NULL
* @param other new remote host address, or NULL
* @param force force update
* @param flags flags to force certain updates
*/
void (*update_hosts)(ike_sa_t *this, host_t *me, host_t *other, bool force);
void (*update_hosts)(ike_sa_t *this, host_t *me, host_t *other,
update_hosts_flag_t flags);
/**
* Get the own identification.
+2 -2
View File
@@ -1408,7 +1408,7 @@ METHOD(task_manager_t, process_message, status_t,
}
this->ike_sa->set_statistic(this->ike_sa, STAT_INBOUND,
time_monotonic(NULL));
this->ike_sa->update_hosts(this->ike_sa, me, other, TRUE);
this->ike_sa->update_hosts(this->ike_sa, me, other, UPDATE_HOSTS_FORCE_ADDRS);
charon->bus->message(charon->bus, msg, TRUE, TRUE);
if (process_response(this, msg) != SUCCESS)
{
@@ -1528,7 +1528,7 @@ METHOD(task_manager_t, process_message, status_t,
"%s.half_open_timeout", HALF_OPEN_IKE_SA_TIMEOUT,
lib->ns));
}
this->ike_sa->update_hosts(this->ike_sa, me, other, TRUE);
this->ike_sa->update_hosts(this->ike_sa, me, other, UPDATE_HOSTS_FORCE_ADDRS);
charon->bus->message(charon->bus, msg, TRUE, TRUE);
if (process_request(this, msg) != SUCCESS)
{
@@ -384,7 +384,8 @@ METHOD(task_t, process_r, status_t,
this->ike_sa->update_hosts(this->ike_sa,
message->get_destination(message),
message->get_source(message), TRUE);
message->get_source(message),
UPDATE_HOSTS_FORCE_ADDRS);
sa_payload = (sa_payload_t*)message->get_payload(message,
PLV1_SECURITY_ASSOCIATION);
+2 -1
View File
@@ -376,7 +376,8 @@ METHOD(task_t, process_r, status_t,
this->ike_sa->update_hosts(this->ike_sa,
message->get_destination(message),
message->get_source(message), TRUE);
message->get_source(message),
UPDATE_HOSTS_FORCE_ADDRS);
sa_payload = (sa_payload_t*)message->get_payload(message,
PLV1_SECURITY_ASSOCIATION);
+9 -7
View File
@@ -1649,8 +1649,10 @@ METHOD(task_manager_t, process_message, status_t,
return FAILED;
}
if (!this->ike_sa->supports_extension(this->ike_sa, EXT_MOBIKE))
{ /* with MOBIKE, we do no implicit updates */
this->ike_sa->update_hosts(this->ike_sa, me, other, mid == 1);
{ /* only do implicit updates without MOBIKE, and only force
* updates for IKE_AUTH (ports might change due to NAT-T) */
this->ike_sa->update_hosts(this->ike_sa, me, other,
mid == 1 ? UPDATE_HOSTS_FORCE_ADDRS : 0);
}
status = handle_fragment(this, &this->responding.defrag, msg);
if (status != SUCCESS)
@@ -1718,11 +1720,11 @@ METHOD(task_manager_t, process_message, status_t,
msg->get_exchange_type(msg) != IKE_SA_INIT)
{ /* only do updates based on verified messages (or initial ones) */
if (!this->ike_sa->supports_extension(this->ike_sa, EXT_MOBIKE))
{ /* with MOBIKE, we do no implicit updates. we force an
* update of the local address on IKE_SA_INIT, but never
* for the remote address */
this->ike_sa->update_hosts(this->ike_sa, me, NULL, mid == 0);
this->ike_sa->update_hosts(this->ike_sa, NULL, other, FALSE);
{ /* only do implicit updates without MOBIKE, we force an
* update of the local address on IKE_SA_INIT as we might
* not know it yet, but never for the remote address */
this->ike_sa->update_hosts(this->ike_sa, me, other,
mid == 0 ? UPDATE_HOSTS_FORCE_LOCAL : 0);
}
}
status = handle_fragment(this, &this->initiating.defrag, msg);
+44 -93
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2010-2018 Tobias Brunner
* Copyright (C) 2010-2020 Tobias Brunner
* Copyright (C) 2007 Martin Willi
* HSR Hochschule fuer Technik Rapperswil
*
@@ -269,55 +269,6 @@ static bool build_cookie(private_ike_mobike_t *this, message_t *message)
return TRUE;
}
/**
* update addresses of associated CHILD_SAs
*/
static void update_children(private_ike_mobike_t *this)
{
enumerator_t *enumerator;
child_sa_t *child_sa;
linked_list_t *vips;
status_t status;
host_t *host;
vips = linked_list_create();
enumerator = this->ike_sa->create_virtual_ip_enumerator(this->ike_sa, TRUE);
while (enumerator->enumerate(enumerator, &host))
{
vips->insert_last(vips, host);
}
enumerator->destroy(enumerator);
enumerator = this->ike_sa->create_child_sa_enumerator(this->ike_sa);
while (enumerator->enumerate(enumerator, (void**)&child_sa))
{
status = child_sa->update(child_sa,
this->ike_sa->get_my_host(this->ike_sa),
this->ike_sa->get_other_host(this->ike_sa), vips,
this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY));
switch (status)
{
case NOT_SUPPORTED:
this->ike_sa->rekey_child_sa(this->ike_sa,
child_sa->get_protocol(child_sa),
child_sa->get_spi(child_sa, TRUE));
break;
case SUCCESS:
charon->child_sa_manager->remove(charon->child_sa_manager,
child_sa);
charon->child_sa_manager->add(charon->child_sa_manager,
child_sa, this->ike_sa);
break;
default:
break;
}
}
enumerator->destroy(enumerator);
vips->destroy(vips);
}
/**
* Apply the port of the old host, if its ip equals the new, use port otherwise.
*/
@@ -448,7 +399,6 @@ METHOD(task_t, build_i, status_t,
{
return FAILED;
}
update_children(this);
}
if (this->address && !this->check)
{
@@ -472,34 +422,41 @@ METHOD(task_t, process_r, status_t,
}
else if (message->get_exchange_type(message) == INFORMATIONAL)
{
process_payloads(this, message);
if (this->update)
{
host_t *me, *other;
host_t *me_new = NULL, *other, *other_old, *other_new = NULL;
me = message->get_destination(message);
other = message->get_source(message);
this->ike_sa->set_my_host(this->ike_sa, me->clone(me));
this->ike_sa->set_other_host(this->ike_sa, other->clone(other));
}
process_payloads(this, message);
if (this->natd)
{
this->natd->task.process(&this->natd->task, message);
}
if (this->addresses_updated && this->ike_sa->has_condition(this->ike_sa,
COND_ORIGINAL_INITIATOR))
if (this->update)
{
host_t *other = message->get_source(message);
host_t *other_old = this->ike_sa->get_other_host(this->ike_sa);
me_new = message->get_destination(message);
other_new = message->get_source(message);
}
else if (this->addresses_updated &&
this->ike_sa->has_condition(this->ike_sa, COND_ORIGINAL_INITIATOR))
{
other = message->get_source(message);
other_old = this->ike_sa->get_other_host(this->ike_sa);
if (!other->equals(other, other_old))
{
DBG1(DBG_IKE, "remote address changed from %H to %H", other_old,
other);
this->ike_sa->set_other_host(this->ike_sa, other->clone(other));
this->update = TRUE;
other_new = other;
/* our address might have changed too if the responder used
* a different address from our list to reach us */
me_new = message->get_destination(message);
}
}
if (me_new || other_new)
{
this->ike_sa->update_hosts(this->ike_sa, me_new,
other_new, UPDATE_HOSTS_FORCE_ALL);
}
}
return NEED_MORE;
}
@@ -528,10 +485,6 @@ METHOD(task_t, build_r, status_t,
message->add_notify(message, FALSE, COOKIE2, this->cookie2);
chunk_free(&this->cookie2);
}
if (this->update)
{
update_children(this);
}
return SUCCESS;
}
return NEED_MORE;
@@ -574,49 +527,47 @@ METHOD(task_t, process_i, status_t,
if (this->natd)
{
this->natd->task.process(&this->natd->task, message);
if (!this->update && this->natd->has_mapping_changed(this->natd))
{
/* force an update if mappings have changed */
this->update = this->check = TRUE;
if (this->update)
{ /* update children again, as NAT state may have changed */
this->ike_sa->update_hosts(this->ike_sa, NULL, NULL,
UPDATE_HOSTS_FORCE_CHILDREN);
}
else if (this->natd->has_mapping_changed(this->natd))
{ /* force a check/update if mappings have changed during a DPD */
this->check = TRUE;
DBG1(DBG_IKE, "detected changes in NAT mappings, "
"initiating MOBIKE update");
}
}
if (this->update)
{
/* update again, as NAT state may have changed */
update_children(this);
}
if (this->check)
{
host_t *me_new, *me_old, *other_new, *other_old;
host_t *me, *me_new = NULL, *other, *other_new = NULL;
me_new = message->get_destination(message);
other_new = message->get_source(message);
me_old = this->ike_sa->get_my_host(this->ike_sa);
other_old = this->ike_sa->get_other_host(this->ike_sa);
me = message->get_destination(message);
other = message->get_source(message);
if (!me_new->equals(me_new, me_old))
if (!me->equals(me, this->ike_sa->get_my_host(this->ike_sa)))
{
this->update = TRUE;
this->ike_sa->set_my_host(this->ike_sa, me_new->clone(me_new));
me_new = me;
}
if (!other_new->equals(other_new, other_old))
if (!other->equals(other, this->ike_sa->get_other_host(this->ike_sa)))
{
this->update = TRUE;
this->ike_sa->set_other_host(this->ike_sa, other_new->clone(other_new));
other_new = other;
}
if (this->update)
if (me_new || other_new)
{
this->ike_sa->update_hosts(this->ike_sa, me_new, other_new,
UPDATE_HOSTS_FORCE_ALL);
/* use the same task to ... */
if (!this->ike_sa->has_condition(this->ike_sa,
COND_ORIGINAL_INITIATOR))
{ /*... send an updated list of addresses as responder */
update_children(this);
this->update = FALSE;
this->address = TRUE;
}
else
{ /* ... send the update as original initiator */
this->update = TRUE;
if (this->natd)
{
this->natd->task.destroy(&this->natd->task);