if (action & ACTION_TRAP)
{
- DBG1(DBG_CFG, "installing '%s'", child_cfg->get_name(child_cfg));
+ DBG1(DBG_CFG, "vici installing '%s'", child_cfg->get_name(child_cfg));
switch (child_cfg->get_mode(child_cfg))
{
case MODE_PASS:
if (action & ACTION_START)
{
- DBG1(DBG_CFG, "initiating '%s'", child_cfg->get_name(child_cfg));
+ DBG1(DBG_CFG, "vici initiating '%s'", child_cfg->get_name(child_cfg));
charon->controller->initiate(charon->controller,
peer_cfg->get_ref(peer_cfg), child_cfg->get_ref(child_cfg),
NULL, NULL, 0, 0, FALSE);
}
/**
- * Undo start actions associated with a child config
+ * Type to keep track of unique IDs and names of CHILD_SAs to terminate.
*/
-static void clear_start_action(private_vici_config_t *this, char *peer_name,
- child_cfg_t *child_cfg)
+typedef struct {
+ uint32_t id;
+ char *name;
+} child_name_id_t;
+
+/**
+ * Terminate given CHILD_SAs and optionally terminate any IKE_SA without other
+ * children.
+ */
+static void terminate_for_action(private_vici_config_t *this, char *peer_name,
+ hashtable_t *to_terminate, bool delete_ike)
{
enumerator_t *enumerator, *children;
child_sa_t *child_sa;
ike_sa_t *ike_sa;
- uint32_t id = 0, others;
+ child_name_id_t child_id;
+ uint32_t id;
array_t *ids = NULL, *ikeids = NULL;
+ bool others;
+
+ enumerator = charon->controller->create_ike_sa_enumerator(
+ charon->controller, TRUE);
+ while (enumerator->enumerate(enumerator, &ike_sa))
+ {
+ if (!streq(ike_sa->get_name(ike_sa), peer_name))
+ {
+ continue;
+ }
+
+ others = FALSE;
+ children = ike_sa->create_child_sa_enumerator(ike_sa);
+ while (children->enumerate(children, &child_sa))
+ {
+ if (child_sa->get_state(child_sa) != CHILD_DELETING &&
+ child_sa->get_state(child_sa) != CHILD_DELETED &&
+ !to_terminate->get(to_terminate, child_sa->get_name(child_sa)))
+ {
+ others = TRUE;
+ break;
+ }
+ }
+ children->destroy(children);
+
+ if (delete_ike && (!others || !ike_sa->get_child_count(ike_sa)))
+ {
+ /* found no children or only matching, delete IKE_SA */
+ id = ike_sa->get_unique_id(ike_sa);
+ array_insert_create_value(&ikeids, sizeof(id),
+ ARRAY_TAIL, &id);
+ continue;
+ }
+
+ /* otherwise, delete only the matching CHILD_SAs */
+ children = ike_sa->create_child_sa_enumerator(ike_sa);
+ while (children->enumerate(children, &child_sa))
+ {
+ child_id.name = child_sa->get_name(child_sa);
+
+ if (child_sa->get_state(child_sa) != CHILD_DELETING &&
+ child_sa->get_state(child_sa) != CHILD_DELETED &&
+ to_terminate->get(to_terminate, child_id.name))
+ {
+ child_id.id = child_sa->get_unique_id(child_sa);
+ child_id.name = strdup(child_id.name);
+ array_insert_create_value(&ids, sizeof(child_id),
+ ARRAY_TAIL, &child_id);
+ }
+ }
+ children->destroy(children);
+ }
+ enumerator->destroy(enumerator);
+
+ while (array_remove(ids, ARRAY_HEAD, &child_id))
+ {
+ DBG1(DBG_CFG, "vici closing CHILD_SA '%s' #%u", child_id.name,
+ child_id.id);
+ charon->controller->terminate_child(charon->controller,
+ child_id.id, NULL, NULL, 0, 0);
+ free(child_id.name);
+ }
+ array_destroy(ids);
+
+ while (array_remove(ikeids, ARRAY_HEAD, &id))
+ {
+ DBG1(DBG_CFG, "vici closing IKE_SA '%s' #%u", peer_name, id);
+ charon->controller->terminate_ike(charon->controller, id,
+ FALSE, NULL, NULL, 0, 0);
+ }
+ array_destroy(ikeids);
+}
+
+/**
+ * Clear the start action associated with the given child config. To reduce the
+ * overhead when terminating active SAs, only collect the name.
+ *
+ * Note: The lock must be unlocked when calling this.
+ */
+static void clear_start_action(private_vici_config_t *this, char *peer_name,
+ child_cfg_t *child_cfg, hashtable_t *to_terminate)
+{
action_t action;
char *name;
name = child_cfg->get_name(child_cfg);
action = child_cfg->get_start_action(child_cfg);
-
if (action & ACTION_TRAP)
{
- DBG1(DBG_CFG, "uninstalling '%s'", name);
+ DBG1(DBG_CFG, "vici uninstalling '%s'", name);
switch (child_cfg->get_mode(child_cfg))
{
case MODE_PASS:
break;
}
}
-
if (action & ACTION_START)
{
- enumerator = charon->controller->create_ike_sa_enumerator(
- charon->controller, TRUE);
- while (enumerator->enumerate(enumerator, &ike_sa))
- {
- if (!streq(ike_sa->get_name(ike_sa), peer_name))
- {
- continue;
- }
- others = id = 0;
- children = ike_sa->create_child_sa_enumerator(ike_sa);
- while (children->enumerate(children, &child_sa))
- {
- if (child_sa->get_state(child_sa) != CHILD_DELETING &&
- child_sa->get_state(child_sa) != CHILD_DELETED)
- {
- if (streq(name, child_sa->get_name(child_sa)))
- {
- id = child_sa->get_unique_id(child_sa);
- }
- else
- {
- others++;
- }
- }
- }
- children->destroy(children);
-
- if (!ike_sa->get_child_count(ike_sa) || (id && !others))
- {
- /* found no children or only matching, delete IKE_SA */
- id = ike_sa->get_unique_id(ike_sa);
- array_insert_create_value(&ikeids, sizeof(id),
- ARRAY_TAIL, &id);
- }
- else
- {
- children = ike_sa->create_child_sa_enumerator(ike_sa);
- while (children->enumerate(children, &child_sa))
- {
- if (streq(name, child_sa->get_name(child_sa)))
- {
- id = child_sa->get_unique_id(child_sa);
- array_insert_create_value(&ids, sizeof(id),
- ARRAY_TAIL, &id);
- }
- }
- children->destroy(children);
- }
- }
- enumerator->destroy(enumerator);
-
- if (array_count(ids))
- {
- while (array_remove(ids, ARRAY_HEAD, &id))
- {
- DBG1(DBG_CFG, "closing '%s' #%u", name, id);
- charon->controller->terminate_child(charon->controller,
- id, NULL, NULL, 0, 0);
- }
- array_destroy(ids);
- }
- if (array_count(ikeids))
- {
- while (array_remove(ikeids, ARRAY_HEAD, &id))
- {
- DBG1(DBG_CFG, "closing IKE_SA #%u", id);
- charon->controller->terminate_ike(charon->controller, id,
- FALSE, NULL, NULL, 0, 0);
- }
- array_destroy(ikeids);
- }
+ to_terminate->put(to_terminate, name, name);
}
}
/**
- * Run or undo a start actions associated with a child config
+ * Clear start actions associated with a list of child configs, optionally
+ * deletes empty IKE_SAs.
*/
-static void handle_start_action(private_vici_config_t *this,
- peer_cfg_t *peer_cfg, child_cfg_t *child_cfg,
- bool undo)
+static void clear_start_actions(private_vici_config_t *this,
+ peer_cfg_t *peer_cfg, array_t *child_cfgs,
+ bool delete_ike)
{
+ enumerator_t *enumerator;
+ hashtable_t *to_terminate;
+ child_cfg_t *child_cfg;
+ char *peer_name;
+
this->handling_actions = TRUE;
this->lock->unlock(this->lock);
- if (undo)
- {
- clear_start_action(this, peer_cfg->get_name(peer_cfg), child_cfg);
- }
- else
+ to_terminate = hashtable_create(hashtable_hash_str,
+ hashtable_equals_str, 8);
+ peer_name = peer_cfg->get_name(peer_cfg);
+
+ enumerator = array_create_enumerator(child_cfgs);
+ while (enumerator->enumerate(enumerator, &child_cfg))
{
- run_start_action(this, peer_cfg, child_cfg);
+ clear_start_action(this, peer_name, child_cfg, to_terminate);
}
+ enumerator->destroy(enumerator);
+
+ terminate_for_action(this, peer_name, to_terminate, delete_ike);
+ to_terminate->destroy(to_terminate);
this->lock->write_lock(this->lock);
this->handling_actions = FALSE;
}
/**
- * Run or undo start actions associated with all child configs of a peer config
+ * Run start actions associated with a list of child configs.
*/
-static void handle_start_actions(private_vici_config_t *this,
- peer_cfg_t *peer_cfg, bool undo)
+static void run_start_actions(private_vici_config_t *this,
+ peer_cfg_t *peer_cfg, array_t *child_cfgs)
{
enumerator_t *enumerator;
child_cfg_t *child_cfg;
this->handling_actions = TRUE;
this->lock->unlock(this->lock);
- enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg);
+ enumerator = array_create_enumerator(child_cfgs);
while (enumerator->enumerate(enumerator, &child_cfg))
{
- if (undo)
- {
- clear_start_action(this, peer_cfg->get_name(peer_cfg), child_cfg);
- }
- else
- {
- run_start_action(this, peer_cfg, child_cfg);
- }
+ run_start_action(this, peer_cfg, child_cfg);
}
enumerator->destroy(enumerator);
this->handling_actions = FALSE;
}
+/**
+ * Run or undo start actions for all child configs of the given peer config
+ * after it has changed.
+ */
+static void handle_start_actions(private_vici_config_t *this,
+ peer_cfg_t *peer_cfg, bool undo)
+{
+ array_t *child_cfgs;
+
+ child_cfgs = array_create(0, 0);
+ array_insert_enumerator(child_cfgs, ARRAY_TAIL,
+ peer_cfg->create_child_cfg_enumerator(peer_cfg));
+ if (undo)
+ {
+ /* the peer config has changed, so allow IKE_SAs to get terminated */
+ clear_start_actions(this, peer_cfg, child_cfgs, TRUE);
+ }
+ else
+ {
+ run_start_actions(this, peer_cfg, child_cfgs);
+ }
+ array_destroy(child_cfgs);
+}
+
/**
* Replace children of a peer config by a new config
*/
{
enumerator_t *enumerator;
child_cfg_t *child;
- bool added;
+ array_t *to_run = NULL, *to_clear = NULL;
+ bool added, any_to_initiate = FALSE;
enumerator = to->replace_child_cfgs(to, from);
while (enumerator->enumerate(enumerator, &child, &added))
{
- handle_start_action(this, to, child, !added);
+ if (added)
+ {
+ array_insert_create(&to_run, ARRAY_TAIL, child);
+
+ if (child->get_start_action(child) & ACTION_START)
+ {
+ any_to_initiate = TRUE;
+ }
+ }
+ else
+ {
+ array_insert_create(&to_clear, ARRAY_TAIL, child);
+ }
}
+ /* keep empty IKE_SAs only if we are to initiate any CHILD_SAs */
+ clear_start_actions(this, to, to_clear, !any_to_initiate);
+ run_start_actions(this, to, to_run);
+ array_destroy(to_clear);
+ array_destroy(to_run);
enumerator->destroy(enumerator);
}
cfg = this->conns->remove(this->conns, conn_name);
if (cfg)
{
+ DBG1(DBG_CFG, "removed vici connection: %s", cfg->get_name(cfg));
handle_start_actions(this, cfg, TRUE);
cfg->destroy(cfg);
}