From 2a1c9e20bd38572ba32ba57436d7064192680729 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 15 Oct 2014 17:22:55 +0200 Subject: kernel-interface: Remove reqid parameter from get_spi/get_cpi() methods The reqid is not strictly required, as we set the reqid with the update call when installing the negotiated SA. If we don't need a reqid at this stage, we can later allocate the reqid in the kernel backend once the SA parameters have been fully negotaited. This allows us to assign the same reqid for the same selectors to avoid conflicts on backends this is necessary. --- src/libhydra/kernel/kernel_interface.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/libhydra/kernel/kernel_interface.c') diff --git a/src/libhydra/kernel/kernel_interface.c b/src/libhydra/kernel/kernel_interface.c index 3fa28e054..f479b8723 100644 --- a/src/libhydra/kernel/kernel_interface.c +++ b/src/libhydra/kernel/kernel_interface.c @@ -155,24 +155,24 @@ METHOD(kernel_interface_t, get_features, kernel_feature_t, METHOD(kernel_interface_t, get_spi, status_t, private_kernel_interface_t *this, host_t *src, host_t *dst, - u_int8_t protocol, u_int32_t reqid, u_int32_t *spi) + u_int8_t protocol, u_int32_t *spi) { if (!this->ipsec) { return NOT_SUPPORTED; } - return this->ipsec->get_spi(this->ipsec, src, dst, protocol, reqid, spi); + return this->ipsec->get_spi(this->ipsec, src, dst, protocol, spi); } METHOD(kernel_interface_t, get_cpi, status_t, private_kernel_interface_t *this, host_t *src, host_t *dst, - u_int32_t reqid, u_int16_t *cpi) + u_int16_t *cpi) { if (!this->ipsec) { return NOT_SUPPORTED; } - return this->ipsec->get_cpi(this->ipsec, src, dst, reqid, cpi); + return this->ipsec->get_cpi(this->ipsec, src, dst, cpi); } METHOD(kernel_interface_t, add_sa, status_t, -- cgit v1.2.3 From d05d85fe658321aff8d859eaf50704a21299b683 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 15 Oct 2014 18:03:31 +0200 Subject: kernel-interface: Pass full list of traffic selectors to add_sa() While we can handle the first selector only in BEET mode in kernel-netlink, passing the full list gives the backend more flexibility how to handle this information. --- src/libhydra/kernel/kernel_interface.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/libhydra/kernel/kernel_interface.c') diff --git a/src/libhydra/kernel/kernel_interface.c b/src/libhydra/kernel/kernel_interface.c index f479b8723..1cb001548 100644 --- a/src/libhydra/kernel/kernel_interface.c +++ b/src/libhydra/kernel/kernel_interface.c @@ -182,7 +182,7 @@ METHOD(kernel_interface_t, add_sa, status_t, u_int16_t int_alg, chunk_t int_key, ipsec_mode_t mode, u_int16_t ipcomp, u_int16_t cpi, u_int32_t replay_window, bool initiator, bool encap, bool esn, bool inbound, - traffic_selector_t *src_ts, traffic_selector_t *dst_ts) + linked_list_t *src_ts, linked_list_t *dst_ts) { if (!this->ipsec) { -- cgit v1.2.3 From cc08ce83f06d2158b5306b6f15a2701873fd8c3f Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 12 Nov 2014 17:22:45 +0100 Subject: kernel-interface: Add reqid allocation and release functions To reassign reqids where appropriate, we explicitly allocate or confirm them centrally on the kernel-interface. Currently the state is stored in the kernel-interface wrapper for all backends, but we may add appropriate methods to each backend to implement a custom reqid allocation logic, if required. --- src/libhydra/kernel/kernel_interface.c | 292 +++++++++++++++++++++++++++++++++ 1 file changed, 292 insertions(+) (limited to 'src/libhydra/kernel/kernel_interface.c') diff --git a/src/libhydra/kernel/kernel_interface.c b/src/libhydra/kernel/kernel_interface.c index 1cb001548..9452b8f84 100644 --- a/src/libhydra/kernel/kernel_interface.c +++ b/src/libhydra/kernel/kernel_interface.c @@ -43,6 +43,8 @@ #include #include #include +#include +#include typedef struct private_kernel_interface_t private_kernel_interface_t; @@ -114,6 +116,16 @@ struct private_kernel_interface_t { */ linked_list_t *listeners; + /** + * Reqid entries indexed by reqids + */ + hashtable_t *reqids; + + /** + * Reqid entries indexed by traffic selectors + */ + hashtable_t *reqids_by_ts; + /** * mutex for algorithm mappings */ @@ -175,6 +187,278 @@ METHOD(kernel_interface_t, get_cpi, status_t, return this->ipsec->get_cpi(this->ipsec, src, dst, cpi); } +/** + * Reqid mapping entry + */ +typedef struct { + /** allocated reqid */ + u_int32_t reqid; + /** references to this entry */ + u_int refs; + /** inbound mark used for SA */ + mark_t mark_in; + /** outbound mark used for SA */ + mark_t mark_out; + /** local traffic selectors */ + array_t *local; + /** remote traffic selectors */ + array_t *remote; +} reqid_entry_t; + +/** + * Destroy a reqid mapping entry + */ +static void reqid_entry_destroy(reqid_entry_t *entry) +{ + array_destroy_offset(entry->local, offsetof(traffic_selector_t, destroy)); + array_destroy_offset(entry->remote, offsetof(traffic_selector_t, destroy)); + free(entry); +} + +/** + * Hashtable hash function for reqid entries using reqid as key + */ +static u_int hash_reqid(reqid_entry_t *entry) +{ + return chunk_hash_inc(chunk_from_thing(entry->reqid), + chunk_hash_inc(chunk_from_thing(entry->mark_in), + chunk_hash(chunk_from_thing(entry->mark_out)))); +} + +/** + * Hashtable equals function for reqid entries using reqid as key + */ +static bool equals_reqid(reqid_entry_t *a, reqid_entry_t *b) +{ + return a->reqid == b->reqid && + a->mark_in.value == b->mark_in.value && + a->mark_in.mask == b->mark_in.mask && + a->mark_out.value == b->mark_out.value && + a->mark_out.mask == b->mark_out.mask; +} + +/** + * Hash an array of traffic selectors + */ +static u_int hash_ts_array(array_t *array, u_int hash) +{ + enumerator_t *enumerator; + traffic_selector_t *ts; + + enumerator = array_create_enumerator(array); + while (enumerator->enumerate(enumerator, &ts)) + { + hash = ts->hash(ts, hash); + } + enumerator->destroy(enumerator); + + return hash; +} + +/** + * Hashtable hash function for reqid entries using traffic selectors as key + */ +static u_int hash_reqid_by_ts(reqid_entry_t *entry) +{ + return hash_ts_array(entry->local, hash_ts_array(entry->remote, 0)); +} + +/** + * Compare two array with traffic selectors for equality + */ +static bool ts_array_equals(array_t *a, array_t *b) +{ + traffic_selector_t *tsa, *tsb; + enumerator_t *ae, *be; + bool equal = TRUE; + + if (array_count(a) != array_count(b)) + { + return FALSE; + } + + ae = array_create_enumerator(a); + be = array_create_enumerator(b); + while (equal && ae->enumerate(ae, &tsa) && be->enumerate(be, &tsb)) + { + equal = tsa->equals(tsa, tsb); + } + ae->destroy(ae); + be->destroy(be); + + return equal; +} + +/** + * Check if mark b matches to a, optionally with reqid match + */ +static bool mark_matches(mark_t a, mark_t b, u_int32_t reqid) +{ + if (a.value == b.value) + { + return TRUE; + } + if (a.value == MARK_REQID && b.value == reqid) + { + return TRUE; + } + return FALSE; +} + +/** + * Hashtable equals function for reqid entries using traffic selectors as key + */ +static bool equals_reqid_by_ts(reqid_entry_t *a, reqid_entry_t *b) +{ + if (ts_array_equals(a->local, b->local) && + ts_array_equals(a->remote, b->remote) && + a->mark_in.mask == b->mark_in.mask && + a->mark_out.mask == b->mark_out.mask) + { + if (mark_matches(a->mark_in, b->mark_in, a->reqid) && + mark_matches(a->mark_out, b->mark_out, a->reqid)) + { + return TRUE; + } + if (mark_matches(b->mark_in, a->mark_in, b->reqid) && + mark_matches(b->mark_out, a->mark_out, b->reqid)) + { + return TRUE; + } + } + return FALSE; +} + +/** + * Create an array from copied traffic selector list items + */ +static array_t *array_from_ts_list(linked_list_t *list) +{ + enumerator_t *enumerator; + traffic_selector_t *ts; + array_t *array; + + array = array_create(0, 0); + + enumerator = list->create_enumerator(list); + while (enumerator->enumerate(enumerator, &ts)) + { + array_insert(array, ARRAY_TAIL, ts->clone(ts)); + } + enumerator->destroy(enumerator); + + return array; +} + +METHOD(kernel_interface_t, alloc_reqid, status_t, + private_kernel_interface_t *this, + linked_list_t *local_ts, linked_list_t *remote_ts, + mark_t *mark_in, mark_t *mark_out, u_int32_t *reqid) +{ + static u_int32_t counter = 0; + reqid_entry_t *entry = NULL, *tmpl; + status_t status = SUCCESS; + + INIT(tmpl, + .local = array_from_ts_list(local_ts), + .remote = array_from_ts_list(remote_ts), + .mark_in = *mark_in, + .mark_out = *mark_out, + .reqid = *reqid, + ); + + this->mutex->lock(this->mutex); + if (tmpl->reqid) + { + /* search by reqid if given */ + if (tmpl->mark_in.value == MARK_REQID) + { + tmpl->mark_in.value = tmpl->reqid; + } + if (tmpl->mark_out.value == MARK_REQID) + { + tmpl->mark_out.value = tmpl->reqid; + } + entry = this->reqids->get(this->reqids, tmpl); + } + if (entry) + { + /* we don't require a traffic selector match for explicit reqids, + * as we wan't to reuse a reqid for trap-triggered policies that + * got narrowed during negotiation. */ + reqid_entry_destroy(tmpl); + } + else + { + /* search by traffic selectors. We do the search with MARK_REQID + * wildcards (if any), and update the marks if we find any match */ + entry = this->reqids_by_ts->get(this->reqids_by_ts, tmpl); + if (entry) + { + reqid_entry_destroy(tmpl); + } + else + { + /* none found, create a new entry, allocating a reqid */ + entry = tmpl; + entry->reqid = ++counter; + if (entry->mark_in.value == MARK_REQID) + { + entry->mark_in.value = entry->reqid; + } + if (entry->mark_out.value == MARK_REQID) + { + entry->mark_out.value = entry->reqid; + } + this->reqids_by_ts->put(this->reqids_by_ts, entry, entry); + this->reqids->put(this->reqids, entry, entry); + } + *reqid = entry->reqid; + } + *mark_in = entry->mark_in; + *mark_out = entry->mark_out; + entry->refs++; + this->mutex->unlock(this->mutex); + + return status; +} + +METHOD(kernel_interface_t, release_reqid, status_t, + private_kernel_interface_t *this, u_int32_t reqid, + mark_t mark_in, mark_t mark_out) +{ + reqid_entry_t *entry, tmpl = { + .reqid = reqid, + .mark_in = mark_in, + .mark_out = mark_out, + }; + + this->mutex->lock(this->mutex); + entry = this->reqids->remove(this->reqids, &tmpl); + if (entry) + { + if (--entry->refs == 0) + { + entry = this->reqids_by_ts->remove(this->reqids_by_ts, entry); + if (entry) + { + reqid_entry_destroy(entry); + } + } + else + { + this->reqids->put(this->reqids, entry, entry); + } + } + this->mutex->unlock(this->mutex); + + if (entry) + { + return SUCCESS; + } + return NOT_FOUND; +} + METHOD(kernel_interface_t, add_sa, status_t, private_kernel_interface_t *this, host_t *src, host_t *dst, u_int32_t spi, u_int8_t protocol, u_int32_t reqid, mark_t mark, @@ -733,6 +1017,8 @@ METHOD(kernel_interface_t, destroy, void, DESTROY_IF(this->ipsec); DESTROY_IF(this->net); DESTROY_FUNCTION_IF(this->ifaces_filter, (void*)free); + this->reqids->destroy(this->reqids); + this->reqids_by_ts->destroy(this->reqids_by_ts); this->listeners->destroy(this->listeners); this->mutex->destroy(this->mutex); free(this); @@ -751,6 +1037,8 @@ kernel_interface_t *kernel_interface_create() .get_features = _get_features, .get_spi = _get_spi, .get_cpi = _get_cpi, + .alloc_reqid = _alloc_reqid, + .release_reqid = _release_reqid, .add_sa = _add_sa, .update_sa = _update_sa, .query_sa = _query_sa, @@ -795,6 +1083,10 @@ kernel_interface_t *kernel_interface_create() .listeners = linked_list_create(), .mutex_algs = mutex_create(MUTEX_TYPE_DEFAULT), .algorithms = linked_list_create(), + .reqids = hashtable_create((hashtable_hash_t)hash_reqid, + (hashtable_equals_t)equals_reqid, 8), + .reqids_by_ts = hashtable_create((hashtable_hash_t)hash_reqid_by_ts, + (hashtable_equals_t)equals_reqid_by_ts, 8), ); ifaces = lib->settings->get_str(lib->settings, -- cgit v1.2.3 From 85b238887d01c030a7d9240db2031601211a6283 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Thu, 13 Nov 2014 15:26:10 +0100 Subject: child-sa: Replace reqid based marks by "unique" marks As we now use the same reqid for multiple CHILD_SAs with the same selectors, having marks based on the reqid makes not that much sense anymore. Instead we use unique marks that use a custom identifier. This identifier is reused during rekeying, keeping the marks constant for any rule relying on it (for example installed by updown). This also simplifies handling of reqid allocation, as we do not have to query the marks that is not yet assigned for an unknown reqid. --- src/libhydra/kernel/kernel_interface.c | 70 +++++++--------------------------- 1 file changed, 13 insertions(+), 57 deletions(-) (limited to 'src/libhydra/kernel/kernel_interface.c') diff --git a/src/libhydra/kernel/kernel_interface.c b/src/libhydra/kernel/kernel_interface.c index 9452b8f84..28821fc15 100644 --- a/src/libhydra/kernel/kernel_interface.c +++ b/src/libhydra/kernel/kernel_interface.c @@ -260,7 +260,9 @@ static u_int hash_ts_array(array_t *array, u_int hash) */ static u_int hash_reqid_by_ts(reqid_entry_t *entry) { - return hash_ts_array(entry->local, hash_ts_array(entry->remote, 0)); + return hash_ts_array(entry->local, hash_ts_array(entry->remote, + chunk_hash_inc(chunk_from_thing(entry->mark_in), + chunk_hash(chunk_from_thing(entry->mark_out))))); } /** @@ -289,44 +291,17 @@ static bool ts_array_equals(array_t *a, array_t *b) return equal; } -/** - * Check if mark b matches to a, optionally with reqid match - */ -static bool mark_matches(mark_t a, mark_t b, u_int32_t reqid) -{ - if (a.value == b.value) - { - return TRUE; - } - if (a.value == MARK_REQID && b.value == reqid) - { - return TRUE; - } - return FALSE; -} - /** * Hashtable equals function for reqid entries using traffic selectors as key */ static bool equals_reqid_by_ts(reqid_entry_t *a, reqid_entry_t *b) { - if (ts_array_equals(a->local, b->local) && - ts_array_equals(a->remote, b->remote) && - a->mark_in.mask == b->mark_in.mask && - a->mark_out.mask == b->mark_out.mask) - { - if (mark_matches(a->mark_in, b->mark_in, a->reqid) && - mark_matches(a->mark_out, b->mark_out, a->reqid)) - { - return TRUE; - } - if (mark_matches(b->mark_in, a->mark_in, b->reqid) && - mark_matches(b->mark_out, a->mark_out, b->reqid)) - { - return TRUE; - } - } - return FALSE; + return ts_array_equals(a->local, b->local) && + ts_array_equals(a->remote, b->remote) && + a->mark_in.value == b->mark_in.value && + a->mark_in.mask == b->mark_in.mask && + a->mark_out.value == b->mark_out.value && + a->mark_out.mask == b->mark_out.mask; } /** @@ -353,7 +328,7 @@ static array_t *array_from_ts_list(linked_list_t *list) METHOD(kernel_interface_t, alloc_reqid, status_t, private_kernel_interface_t *this, linked_list_t *local_ts, linked_list_t *remote_ts, - mark_t *mark_in, mark_t *mark_out, u_int32_t *reqid) + mark_t mark_in, mark_t mark_out, u_int32_t *reqid) { static u_int32_t counter = 0; reqid_entry_t *entry = NULL, *tmpl; @@ -362,8 +337,8 @@ METHOD(kernel_interface_t, alloc_reqid, status_t, INIT(tmpl, .local = array_from_ts_list(local_ts), .remote = array_from_ts_list(remote_ts), - .mark_in = *mark_in, - .mark_out = *mark_out, + .mark_in = mark_in, + .mark_out = mark_out, .reqid = *reqid, ); @@ -371,14 +346,6 @@ METHOD(kernel_interface_t, alloc_reqid, status_t, if (tmpl->reqid) { /* search by reqid if given */ - if (tmpl->mark_in.value == MARK_REQID) - { - tmpl->mark_in.value = tmpl->reqid; - } - if (tmpl->mark_out.value == MARK_REQID) - { - tmpl->mark_out.value = tmpl->reqid; - } entry = this->reqids->get(this->reqids, tmpl); } if (entry) @@ -390,8 +357,7 @@ METHOD(kernel_interface_t, alloc_reqid, status_t, } else { - /* search by traffic selectors. We do the search with MARK_REQID - * wildcards (if any), and update the marks if we find any match */ + /* search by traffic selectors */ entry = this->reqids_by_ts->get(this->reqids_by_ts, tmpl); if (entry) { @@ -402,21 +368,11 @@ METHOD(kernel_interface_t, alloc_reqid, status_t, /* none found, create a new entry, allocating a reqid */ entry = tmpl; entry->reqid = ++counter; - if (entry->mark_in.value == MARK_REQID) - { - entry->mark_in.value = entry->reqid; - } - if (entry->mark_out.value == MARK_REQID) - { - entry->mark_out.value = entry->reqid; - } this->reqids_by_ts->put(this->reqids_by_ts, entry, entry); this->reqids->put(this->reqids, entry, entry); } *reqid = entry->reqid; } - *mark_in = entry->mark_in; - *mark_out = entry->mark_out; entry->refs++; this->mutex->unlock(this->mutex); -- cgit v1.2.3 From f81a9497483a7282c11adf5705d9ea3e83f6fffd Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 27 Oct 2014 15:07:05 +0100 Subject: kernel-interface: Raise expires with a proto/SPI/dst tuple instead of reqid --- src/libhydra/kernel/kernel_interface.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src/libhydra/kernel/kernel_interface.c') diff --git a/src/libhydra/kernel/kernel_interface.c b/src/libhydra/kernel/kernel_interface.c index 28821fc15..b5ade37d1 100644 --- a/src/libhydra/kernel/kernel_interface.c +++ b/src/libhydra/kernel/kernel_interface.c @@ -815,17 +815,18 @@ METHOD(kernel_interface_t, acquire, void, } METHOD(kernel_interface_t, expire, void, - private_kernel_interface_t *this, u_int32_t reqid, u_int8_t protocol, - u_int32_t spi, bool hard) + private_kernel_interface_t *this, u_int8_t protocol, u_int32_t spi, + host_t *dst, bool hard) { kernel_listener_t *listener; enumerator_t *enumerator; + this->mutex->lock(this->mutex); enumerator = this->listeners->create_enumerator(this->listeners); while (enumerator->enumerate(enumerator, &listener)) { if (listener->expire && - !listener->expire(listener, reqid, protocol, spi, hard)) + !listener->expire(listener, protocol, spi, dst, hard)) { this->listeners->remove_at(this->listeners, enumerator); } -- cgit v1.2.3 From b125839a1a174936624ad99765ea451c55834a70 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 27 Oct 2014 15:38:47 +0100 Subject: kernel-interface: Raise mapping event with a proto/SPI/dst tuple --- src/libhydra/kernel/kernel_interface.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src/libhydra/kernel/kernel_interface.c') diff --git a/src/libhydra/kernel/kernel_interface.c b/src/libhydra/kernel/kernel_interface.c index b5ade37d1..943b513b2 100644 --- a/src/libhydra/kernel/kernel_interface.c +++ b/src/libhydra/kernel/kernel_interface.c @@ -836,17 +836,18 @@ METHOD(kernel_interface_t, expire, void, } METHOD(kernel_interface_t, mapping, void, - private_kernel_interface_t *this, u_int32_t reqid, u_int32_t spi, - host_t *remote) + private_kernel_interface_t *this, u_int8_t protocol, u_int32_t spi, + host_t *dst, host_t *remote) { kernel_listener_t *listener; enumerator_t *enumerator; + this->mutex->lock(this->mutex); enumerator = this->listeners->create_enumerator(this->listeners); while (enumerator->enumerate(enumerator, &listener)) { if (listener->mapping && - !listener->mapping(listener, reqid, spi, remote)) + !listener->mapping(listener, protocol, spi, dst, remote)) { this->listeners->remove_at(this->listeners, enumerator); } -- cgit v1.2.3