diff options
Diffstat (limited to 'src/libcharon/sa/child_sa.c')
-rw-r--r-- | src/libcharon/sa/child_sa.c | 128 |
1 files changed, 96 insertions, 32 deletions
diff --git a/src/libcharon/sa/child_sa.c b/src/libcharon/sa/child_sa.c index 66be5fe61..fdeb605ee 100644 --- a/src/libcharon/sa/child_sa.c +++ b/src/libcharon/sa/child_sa.c @@ -101,6 +101,16 @@ struct private_child_sa_t { u_int32_t reqid; /** + * Did we allocate/confirm and must release the reqid? + */ + bool reqid_allocated; + + /* + * Unique CHILD_SA identifier + */ + u_int32_t unique_id; + + /** * inbound mark used for this child_sa */ mark_t mark_in; @@ -229,6 +239,12 @@ METHOD(child_sa_t, get_reqid, u_int32_t, return this->reqid; } +METHOD(child_sa_t, get_unique_id, u_int32_t, + private_child_sa_t *this) +{ + return this->unique_id; +} + METHOD(child_sa_t, get_config, child_cfg_t*, private_child_sa_t *this) { @@ -603,7 +619,7 @@ METHOD(child_sa_t, alloc_spi, u_int32_t, { if (hydra->kernel_interface->get_spi(hydra->kernel_interface, this->other_addr, this->my_addr, - proto_ike2ip(protocol), this->reqid, + proto_ike2ip(protocol), &this->my_spi) == SUCCESS) { /* if we allocate a SPI, but then are unable to establish the SA, we @@ -619,7 +635,7 @@ METHOD(child_sa_t, alloc_cpi, u_int16_t, { if (hydra->kernel_interface->get_cpi(hydra->kernel_interface, this->other_addr, this->my_addr, - this->reqid, &this->my_cpi) == SUCCESS) + &this->my_cpi) == SUCCESS) { return this->my_cpi; } @@ -633,13 +649,12 @@ METHOD(child_sa_t, install, status_t, { u_int16_t enc_alg = ENCR_UNDEFINED, int_alg = AUTH_UNDEFINED, size; u_int16_t esn = NO_EXT_SEQ_NUMBERS; - traffic_selector_t *src_ts = NULL, *dst_ts = NULL; + linked_list_t *src_ts = NULL, *dst_ts = NULL; time_t now; lifetime_cfg_t *lifetime; u_int32_t tfc = 0; host_t *src, *dst; status_t status; - bool update = FALSE; /* now we have to decide which spi to use. Use self allocated, if "in", * or the one in the proposal, if not "in" (others). Additionally, @@ -648,10 +663,6 @@ METHOD(child_sa_t, install, status_t, { dst = this->my_addr; src = this->other_addr; - if (this->my_spi == spi) - { /* alloc_spi has been called, do an SA update */ - update = TRUE; - } this->my_spi = spi; this->my_cpi = cpi; } @@ -681,6 +692,18 @@ METHOD(child_sa_t, install, status_t, this->proposal->get_algorithm(this->proposal, EXTENDED_SEQUENCE_NUMBERS, &esn, NULL); + if (!this->reqid_allocated) + { + status = hydra->kernel_interface->alloc_reqid(hydra->kernel_interface, + my_ts, other_ts, this->mark_in, this->mark_out, + &this->reqid); + if (status != SUCCESS) + { + return status; + } + this->reqid_allocated = TRUE; + } + lifetime = this->config->get_lifetime(this->config); now = time_monotonic(NULL); @@ -705,18 +728,16 @@ METHOD(child_sa_t, install, status_t, lifetime->time.rekey = 0; } - /* BEET requires the bound address from the traffic selectors. - * TODO: We add just the first traffic selector for now, as the - * kernel accepts a single TS per SA only */ + /* BEET requires the bound address from the traffic selectors */ if (inbound) { - my_ts->get_first(my_ts, (void**)&dst_ts); - other_ts->get_first(other_ts, (void**)&src_ts); + dst_ts = my_ts; + src_ts = other_ts; } else { - my_ts->get_first(my_ts, (void**)&src_ts); - other_ts->get_first(other_ts, (void**)&dst_ts); + src_ts = my_ts; + dst_ts = other_ts; } status = hydra->kernel_interface->add_sa(hydra->kernel_interface, @@ -724,7 +745,7 @@ METHOD(child_sa_t, install, status_t, inbound ? this->mark_in : this->mark_out, tfc, lifetime, enc_alg, encr, int_alg, integ, this->mode, this->ipcomp, cpi, this->config->get_replay_window(this->config), - initiator, this->encap, esn, update, src_ts, dst_ts); + initiator, this->encap, esn, inbound, src_ts, dst_ts); free(lifetime); @@ -799,6 +820,19 @@ METHOD(child_sa_t, add_policies, status_t, traffic_selector_t *my_ts, *other_ts; status_t status = SUCCESS; + if (!this->reqid_allocated) + { + /* trap policy, get or confirm reqid */ + status = hydra->kernel_interface->alloc_reqid( + hydra->kernel_interface, my_ts_list, other_ts_list, + this->mark_in, this->mark_out, &this->reqid); + if (status != SUCCESS) + { + return status; + } + this->reqid_allocated = TRUE; + } + /* apply traffic selectors */ enumerator = my_ts_list->create_enumerator(my_ts_list); while (enumerator->enumerate(enumerator, &my_ts)) @@ -806,12 +840,15 @@ METHOD(child_sa_t, add_policies, status_t, array_insert(this->my_ts, ARRAY_TAIL, my_ts->clone(my_ts)); } enumerator->destroy(enumerator); + array_sort(this->my_ts, (void*)traffic_selector_cmp, NULL); + enumerator = other_ts_list->create_enumerator(other_ts_list); while (enumerator->enumerate(enumerator, &other_ts)) { array_insert(this->other_ts, ARRAY_TAIL, other_ts->clone(other_ts)); } enumerator->destroy(enumerator); + array_sort(this->other_ts, (void*)traffic_selector_cmp, NULL); if (this->config->install_policy(this->config)) { @@ -1104,6 +1141,15 @@ METHOD(child_sa_t, destroy, void, enumerator->destroy(enumerator); } + if (this->reqid_allocated) + { + if (hydra->kernel_interface->release_reqid(hydra->kernel_interface, + this->reqid, this->mark_in, this->mark_out) != SUCCESS) + { + DBG1(DBG_CHD, "releasing reqid %u failed", this->reqid); + } + } + array_destroy_offset(this->my_ts, offsetof(traffic_selector_t, destroy)); array_destroy_offset(this->other_ts, offsetof(traffic_selector_t, destroy)); this->my_addr->destroy(this->my_addr); @@ -1152,15 +1198,17 @@ static host_t* get_proxy_addr(child_cfg_t *config, host_t *ike, bool local) * Described in header. */ child_sa_t * child_sa_create(host_t *me, host_t* other, - child_cfg_t *config, u_int32_t rekey, bool encap) + child_cfg_t *config, u_int32_t rekey, bool encap, + u_int mark_in, u_int mark_out) { - static refcount_t reqid = 0; private_child_sa_t *this; + static refcount_t unique_id = 0, unique_mark = 0, mark; INIT(this, .public = { .get_name = _get_name, .get_reqid = _get_reqid, + .get_unique_id = _get_unique_id, .get_config = _get_config, .get_state = _get_state, .set_state = _set_state, @@ -1202,6 +1250,7 @@ child_sa_t * child_sa_create(host_t *me, host_t* other, .close_action = config->get_close_action(config), .dpd_action = config->get_dpd_action(config), .reqid = config->get_reqid(config), + .unique_id = ref_get(&unique_id), .mark_in = config->get_mark(config, TRUE), .mark_out = config->get_mark(config, FALSE), .install_time = time_monotonic(NULL), @@ -1210,9 +1259,37 @@ child_sa_t * child_sa_create(host_t *me, host_t* other, this->config = config; config->get_ref(config); + if (mark_in) + { + this->mark_in.value = mark_in; + } + if (mark_out) + { + this->mark_out.value = mark_out; + } + if (this->mark_in.value == MARK_UNIQUE || + this->mark_out.value == MARK_UNIQUE) + { + mark = ref_get(&unique_mark); + if (this->mark_in.value == MARK_UNIQUE) + { + this->mark_in.value = mark; + } + if (this->mark_out.value == MARK_UNIQUE) + { + this->mark_out.value = mark; + } + } + if (!this->reqid) { - /* reuse old reqid if we are rekeying an existing CHILD_SA */ + /* reuse old reqid if we are rekeying an existing CHILD_SA. While the + * reqid cache would find the same reqid for our selectors, this does + * not work in a special case: If an SA is triggered by a trap policy, + * but the negotiated SA gets narrowed, we still must reuse the same + * reqid to succesfully "trigger" the SA on the kernel level. Rekeying + * such an SA requires an explicit reqid, as the cache currently knows + * the original selectors only for that reqid. */ if (rekey) { this->reqid = rekey; @@ -1220,22 +1297,9 @@ child_sa_t * child_sa_create(host_t *me, host_t* other, else { this->reqid = charon->traps->find_reqid(charon->traps, config); - if (!this->reqid) - { - this->reqid = ref_get(&reqid); - } } } - if (this->mark_in.value == MARK_REQID) - { - this->mark_in.value = this->reqid; - } - if (this->mark_out.value == MARK_REQID) - { - this->mark_out.value = this->reqid; - } - /* MIPv6 proxy transport mode sets SA endpoints to TS hosts */ if (config->get_mode(config) == MODE_TRANSPORT && config->use_proxy_mode(config)) |