diff options
Diffstat (limited to 'src/libcharon/sa/child_sa.c')
-rw-r--r-- | src/libcharon/sa/child_sa.c | 143 |
1 files changed, 93 insertions, 50 deletions
diff --git a/src/libcharon/sa/child_sa.c b/src/libcharon/sa/child_sa.c index 3d9f6133b..4133d9182 100644 --- a/src/libcharon/sa/child_sa.c +++ b/src/libcharon/sa/child_sa.c @@ -40,10 +40,10 @@ ENUM(child_sa_state_names, CHILD_CREATED, CHILD_DESTROYING, "DESTROYING", ); -ENUM(child_sa_outbound_state_names, CHILD_OUTBOUND_NONE, CHILD_OUTBOUND_INSTALLED, - "NONE", +ENUM_FLAGS(child_sa_outbound_state_names, CHILD_OUTBOUND_REGISTERED, CHILD_OUTBOUND_POLICIES, "REGISTERED", - "INSTALLED", + "SA", + "POLICIES", ); typedef struct private_child_sa_t private_child_sa_t; @@ -296,12 +296,15 @@ METHOD(child_sa_t, get_config, child_cfg_t*, METHOD(child_sa_t, set_state, void, private_child_sa_t *this, child_sa_state_t state) { - DBG2(DBG_CHD, "CHILD_SA %s{%d} state change: %N => %N", - get_name(this), this->unique_id, - child_sa_state_names, this->state, - child_sa_state_names, state); - charon->bus->child_state_change(charon->bus, &this->public, state); - this->state = state; + if (this->state != state) + { + DBG2(DBG_CHD, "CHILD_SA %s{%d} state change: %N => %N", + get_name(this), this->unique_id, + child_sa_state_names, this->state, + child_sa_state_names, state); + charon->bus->child_state_change(charon->bus, &this->public, state); + this->state = state; + } } METHOD(child_sa_t, get_state, child_sa_state_t, @@ -547,7 +550,7 @@ static status_t update_usebytes(private_child_sa_t *this, bool inbound) } else { - if (this->other_spi && this->outbound_state == CHILD_OUTBOUND_INSTALLED) + if (this->other_spi && (this->outbound_state & CHILD_OUTBOUND_SA)) { kernel_ipsec_sa_id_t id = { .src = this->my_addr, @@ -788,7 +791,7 @@ static status_t install_internal(private_child_sa_t *this, chunk_t encr, { tfc = this->config->get_tfc(this->config); } - this->outbound_state = CHILD_OUTBOUND_INSTALLED; + this->outbound_state |= CHILD_OUTBOUND_SA; } DBG2(DBG_CHD, "adding %s %N SA", inbound ? "inbound" : "outbound", @@ -1188,6 +1191,7 @@ METHOD(child_sa_t, install_policies, status_t, linked_list_t *my_ts_list, *other_ts_list; traffic_selector_t *my_ts, *other_ts; status_t status = SUCCESS; + bool install_outbound = FALSE; if (!this->reqid_allocated && !this->static_reqid) { @@ -1207,12 +1211,17 @@ METHOD(child_sa_t, install_policies, status_t, this->reqid_allocated = TRUE; } + if (!(this->outbound_state & CHILD_OUTBOUND_REGISTERED)) + { + install_outbound = TRUE; + this->outbound_state |= CHILD_OUTBOUND_POLICIES; + } + if (!this->config->has_option(this->config, OPT_NO_POLICIES)) { policy_priority_t priority; ipsec_sa_cfg_t my_sa, other_sa; uint32_t manual_prio; - bool install_outbound; prepare_sa_cfg(this, &my_sa, &other_sa); manual_prio = this->config->get_manual_prio(this->config); @@ -1222,7 +1231,6 @@ METHOD(child_sa_t, install_policies, status_t, this->trap = this->state == CHILD_CREATED; priority = this->trap ? POLICY_PRIORITY_ROUTED : POLICY_PRIORITY_DEFAULT; - install_outbound = this->outbound_state != CHILD_OUTBOUND_REGISTERED; /* enumerate pairs of traffic selectors */ enumerator = create_policy_enumerator(this); @@ -1250,7 +1258,6 @@ METHOD(child_sa_t, install_policies, status_t, this->other_addr, my_ts, other_ts, &my_sa, &other_sa, POLICY_IPSEC, priority, manual_prio); - } if (status != SUCCESS) { @@ -1267,21 +1274,35 @@ METHOD(child_sa_t, install_policies, status_t, return status; } -METHOD(child_sa_t, register_outbound, void, +METHOD(child_sa_t, register_outbound, status_t, private_child_sa_t *this, chunk_t encr, chunk_t integ, uint32_t spi, uint16_t cpi, bool tfcv3) { - DBG2(DBG_CHD, "registering outbound %N SA", protocol_id_names, - this->protocol); - DBG2(DBG_CHD, " SPI 0x%.8x, src %H dst %H", ntohl(spi), this->my_addr, - this->other_addr); - - this->other_spi = spi; - this->other_cpi = cpi; - this->encr_r = chunk_clone(encr); - this->integ_r = chunk_clone(integ); - this->tfcv3 = tfcv3; - this->outbound_state = CHILD_OUTBOUND_REGISTERED; + status_t status; + + /* if the kernel supports installing SPIs with policies we install the + * SA immediately as it will only be used once we update the policies */ + if (charon->kernel->get_features(charon->kernel) & KERNEL_POLICY_SPI) + { + status = install_internal(this, encr, integ, spi, cpi, FALSE, FALSE, + tfcv3); + } + else + { + DBG2(DBG_CHD, "registering outbound %N SA", protocol_id_names, + this->protocol); + DBG2(DBG_CHD, " SPI 0x%.8x, src %H dst %H", ntohl(spi), this->my_addr, + this->other_addr); + + this->other_spi = spi; + this->other_cpi = cpi; + this->encr_r = chunk_clone(encr); + this->integ_r = chunk_clone(integ); + this->tfcv3 = tfcv3; + status = SUCCESS; + } + this->outbound_state |= CHILD_OUTBOUND_REGISTERED; + return status; } METHOD(child_sa_t, install_outbound, status_t, @@ -1289,18 +1310,23 @@ METHOD(child_sa_t, install_outbound, status_t, { enumerator_t *enumerator; traffic_selector_t *my_ts, *other_ts; - status_t status; + status_t status = SUCCESS; - status = install_internal(this, this->encr_r, this->integ_r, - this->other_spi, this->other_cpi, FALSE, FALSE, - this->tfcv3); - chunk_clear(&this->encr_r); - chunk_clear(&this->integ_r); + if (!(this->outbound_state & CHILD_OUTBOUND_SA)) + { + status = install_internal(this, this->encr_r, this->integ_r, + this->other_spi, this->other_cpi, FALSE, + FALSE, this->tfcv3); + chunk_clear(&this->encr_r); + chunk_clear(&this->integ_r); + } + this->outbound_state &= ~CHILD_OUTBOUND_REGISTERED; if (status != SUCCESS) { return status; } - if (!this->config->has_option(this->config, OPT_NO_POLICIES)) + if (!this->config->has_option(this->config, OPT_NO_POLICIES) && + !(this->outbound_state & CHILD_OUTBOUND_POLICIES)) { ipsec_sa_cfg_t my_sa, other_sa; uint32_t manual_prio; @@ -1331,6 +1357,7 @@ METHOD(child_sa_t, install_outbound, status_t, } enumerator->destroy(enumerator); } + this->outbound_state |= CHILD_OUTBOUND_POLICIES; return status; } @@ -1340,20 +1367,19 @@ METHOD(child_sa_t, remove_outbound, void, enumerator_t *enumerator; traffic_selector_t *my_ts, *other_ts; - switch (this->outbound_state) + if (!(this->outbound_state & CHILD_OUTBOUND_SA)) { - case CHILD_OUTBOUND_INSTALLED: - break; - case CHILD_OUTBOUND_REGISTERED: + if (this->outbound_state & CHILD_OUTBOUND_REGISTERED) + { chunk_clear(&this->encr_r); chunk_clear(&this->integ_r); this->outbound_state = CHILD_OUTBOUND_NONE; - /* fall-through */ - case CHILD_OUTBOUND_NONE: - return; + } + return; } - if (!this->config->has_option(this->config, OPT_NO_POLICIES)) + if (!this->config->has_option(this->config, OPT_NO_POLICIES) && + (this->outbound_state & CHILD_OUTBOUND_POLICIES)) { ipsec_sa_cfg_t my_sa, other_sa; uint32_t manual_prio; @@ -1598,8 +1624,8 @@ METHOD(child_sa_t, destroy, void, prepare_sa_cfg(this, &my_sa, &other_sa); manual_prio = this->config->get_manual_prio(this->config); - del_outbound = this->trap || - this->outbound_state == CHILD_OUTBOUND_INSTALLED; + del_outbound = (this->outbound_state & CHILD_OUTBOUND_POLICIES) || + this->trap; /* delete all policies in the kernel */ enumerator = create_policy_enumerator(this); @@ -1640,7 +1666,7 @@ METHOD(child_sa_t, destroy, void, }; charon->kernel->del_sa(charon->kernel, &id, &sa); } - if (this->other_spi && this->outbound_state == CHILD_OUTBOUND_INSTALLED) + if (this->other_spi && (this->outbound_state & CHILD_OUTBOUND_SA)) { kernel_ipsec_sa_id_t id = { .src = this->my_addr, @@ -1719,7 +1745,7 @@ child_sa_t * child_sa_create(host_t *me, host_t* other, { private_child_sa_t *this; static refcount_t unique_id = 0, unique_mark = 0; - refcount_t mark; + refcount_t mark = 0; INIT(this, .public = { @@ -1792,16 +1818,33 @@ child_sa_t * child_sa_create(host_t *me, host_t* other, { this->mark_out.value = mark_out; } - if (this->mark_in.value == MARK_UNIQUE || - this->mark_out.value == MARK_UNIQUE) + + if (MARK_IS_UNIQUE(this->mark_in.value) || + MARK_IS_UNIQUE(this->mark_out.value)) { - mark = ref_get(&unique_mark); - if (this->mark_in.value == MARK_UNIQUE) + bool unique_dir; + + unique_dir = this->mark_in.value == MARK_UNIQUE_DIR || + this->mark_out.value == MARK_UNIQUE_DIR; + + if (!unique_dir) + { + mark = ref_get(&unique_mark); + } + if (MARK_IS_UNIQUE(this->mark_in.value)) { + if (unique_dir) + { + mark = ref_get(&unique_mark); + } this->mark_in.value = mark; } - if (this->mark_out.value == MARK_UNIQUE) + if (MARK_IS_UNIQUE(this->mark_out.value)) { + if (unique_dir) + { + mark = ref_get(&unique_mark); + } this->mark_out.value = mark; } } |