diff options
Diffstat (limited to 'src/libhydra')
-rw-r--r-- | src/libhydra/kernel/kernel_interface.c | 272 | ||||
-rw-r--r-- | src/libhydra/kernel/kernel_interface.h | 71 | ||||
-rw-r--r-- | src/libhydra/kernel/kernel_ipsec.h | 23 | ||||
-rw-r--r-- | src/libhydra/kernel/kernel_listener.h | 13 | ||||
-rw-r--r-- | src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c | 72 | ||||
-rw-r--r-- | src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c | 89 |
6 files changed, 416 insertions, 124 deletions
diff --git a/src/libhydra/kernel/kernel_interface.c b/src/libhydra/kernel/kernel_interface.c index 3fa28e054..943b513b2 100644 --- a/src/libhydra/kernel/kernel_interface.c +++ b/src/libhydra/kernel/kernel_interface.c @@ -43,6 +43,8 @@ #include <utils/debug.h> #include <threading/mutex.h> #include <collections/linked_list.h> +#include <collections/hashtable.h> +#include <collections/array.h> typedef struct private_kernel_interface_t private_kernel_interface_t; @@ -115,6 +117,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 */ mutex_t *mutex_algs; @@ -155,24 +167,252 @@ 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); +} + +/** + * 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, + chunk_hash_inc(chunk_from_thing(entry->mark_in), + chunk_hash(chunk_from_thing(entry->mark_out))))); +} + +/** + * 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; +} + +/** + * 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) +{ + 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; +} + +/** + * 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 */ + 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 */ + 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; + this->reqids_by_ts->put(this->reqids_by_ts, entry, entry); + this->reqids->put(this->reqids, entry, entry); + } + *reqid = entry->reqid; + } + 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, @@ -182,7 +422,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) { @@ -575,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); } @@ -595,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); } @@ -733,6 +975,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 +995,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 +1041,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, diff --git a/src/libhydra/kernel/kernel_interface.h b/src/libhydra/kernel/kernel_interface.h index cd550383c..2d484251f 100644 --- a/src/libhydra/kernel/kernel_interface.h +++ b/src/libhydra/kernel/kernel_interface.h @@ -104,39 +104,67 @@ struct kernel_interface_t { * @param src source address of SA * @param dst destination address of SA * @param protocol protocol for SA (ESP/AH) - * @param reqid unique ID for this SA * @param spi allocated spi - * @return SUCCESS if operation completed + * @return SUCCESS if operation completed */ status_t (*get_spi)(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); /** * Get a Compression Parameter Index (CPI) from the kernel. * * @param src source address of SA * @param dst destination address of SA - * @param reqid unique ID for the corresponding SA * @param cpi allocated cpi - * @return SUCCESS if operation completed + * @return SUCCESS if operation completed */ status_t (*get_cpi)(kernel_interface_t *this, host_t *src, host_t *dst, - u_int32_t reqid, u_int16_t *cpi); + u_int16_t *cpi); + + /** + * Allocate or confirm a reqid to use for a given SA pair. + * + * Each returned reqid by a successful call to alloc_reqid() must be + * released using release_reqid(). + * + * The reqid parameter is an in/out parameter. If it points to non-zero, + * the reqid is confirmed and registered for use. If it points to zero, + * a reqid is allocated for the given selectors, and returned to reqid. + * + * @param local_ts traffic selectors of local side for SA + * @param remote_ts traffic selectors of remote side for SA + * @param mark_in inbound mark on SA + * @param mark_out outbound mark on SA + * @param reqid allocated reqid + * @return SUCCESS if reqid allocated + */ + status_t (*alloc_reqid)(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); + + /** + * Release a previously allocated reqid. + * + * @param reqid reqid to release + * @param mark_in inbound mark on SA + * @param mark_out outbound mark on SA + * @return SUCCESS if reqid released + */ + status_t (*release_reqid)(kernel_interface_t *this, u_int32_t reqid, + mark_t mark_in, mark_t mark_out); /** * Add an SA to the SAD. * - * add_sa() may update an already allocated - * SPI (via get_spi). In this case, the replace - * flag must be set. - * This function does install a single SA for a - * single protocol in one direction. + * This function does install a single SA for a single protocol in one + * direction. * * @param src source address for this SA * @param dst destination address for this SA * @param spi SPI allocated by us or remote peer * @param protocol protocol for this SA (ESP/AH) - * @param reqid unique ID for this SA + * @param reqid reqid for this SA * @param mark optional mark for this SA * @param tfc Traffic Flow Confidentiality padding for this SA * @param lifetime lifetime_cfg_t for this SA @@ -152,8 +180,8 @@ struct kernel_interface_t { * @param encap enable UDP encapsulation for NAT traversal * @param esn TRUE to use Extended Sequence Numbers * @param inbound TRUE if this is an inbound SA - * @param src_ts traffic selector with BEET source address - * @param dst_ts traffic selector with BEET destination address + * @param src_ts list of source traffic selectors + * @param dst_ts list of destination traffic selectors * @return SUCCESS if operation completed */ status_t (*add_sa) (kernel_interface_t *this, @@ -165,7 +193,7 @@ struct kernel_interface_t { 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); /** * Update the hosts on an installed SA. @@ -531,23 +559,24 @@ struct kernel_interface_t { /** * Raise an expire event. * - * @param reqid reqid of the expired SA * @param protocol protocol of the expired SA * @param spi spi of the expired SA + * @param dst destination address of expired SA * @param hard TRUE if it is a hard expire, FALSE otherwise */ - void (*expire)(kernel_interface_t *this, u_int32_t reqid, - u_int8_t protocol, u_int32_t spi, bool hard); + void (*expire)(kernel_interface_t *this, u_int8_t protocol, u_int32_t spi, + host_t *dst, bool hard); /** * Raise a mapping event. * - * @param reqid reqid of the SA + * @param protocol protocol of affected SA * @param spi spi of the SA + * @param dst original destination address of SA * @param remote new remote host */ - void (*mapping)(kernel_interface_t *this, u_int32_t reqid, u_int32_t spi, - host_t *remote); + void (*mapping)(kernel_interface_t *this, u_int8_t protocol, u_int32_t spi, + host_t *dst, host_t *remote); /** * Raise a migrate event. diff --git a/src/libhydra/kernel/kernel_ipsec.h b/src/libhydra/kernel/kernel_ipsec.h index eec7401e9..f6705ff88 100644 --- a/src/libhydra/kernel/kernel_ipsec.h +++ b/src/libhydra/kernel/kernel_ipsec.h @@ -58,33 +58,28 @@ struct kernel_ipsec_t { * @param src source address of SA * @param dst destination address of SA * @param protocol protocol for SA (ESP/AH) - * @param reqid unique ID for this SA * @param spi allocated spi - * @return SUCCESS if operation completed + * @return SUCCESS if operation completed */ status_t (*get_spi)(kernel_ipsec_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); /** * Get a Compression Parameter Index (CPI) from the kernel. * * @param src source address of SA * @param dst destination address of SA - * @param reqid unique ID for the corresponding SA * @param cpi allocated cpi - * @return SUCCESS if operation completed + * @return SUCCESS if operation completed */ status_t (*get_cpi)(kernel_ipsec_t *this, host_t *src, host_t *dst, - u_int32_t reqid, u_int16_t *cpi); + u_int16_t *cpi); /** * Add an SA to the SAD. * - * add_sa() may update an already allocated - * SPI (via get_spi). In this case, the replace - * flag must be set. - * This function does install a single SA for a - * single protocol in one direction. + * This function does install a single SA for a single protocol in one + * direction. * * @param src source address for this SA * @param dst destination address for this SA @@ -106,8 +101,8 @@ struct kernel_ipsec_t { * @param encap enable UDP encapsulation for NAT traversal * @param esn TRUE to use Extended Sequence Numbers * @param inbound TRUE if this is an inbound SA - * @param src_ts traffic selector with BEET source address - * @param dst_ts traffic selector with BEET destination address + * @param src_ts list of source traffic selectors + * @param dst_ts list of destination traffic selectors * @return SUCCESS if operation completed */ status_t (*add_sa) (kernel_ipsec_t *this, @@ -119,7 +114,7 @@ struct kernel_ipsec_t { 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); /** * Update the hosts on an installed SA. diff --git a/src/libhydra/kernel/kernel_listener.h b/src/libhydra/kernel/kernel_listener.h index 4382a43fd..8074356a4 100644 --- a/src/libhydra/kernel/kernel_listener.h +++ b/src/libhydra/kernel/kernel_listener.h @@ -49,25 +49,26 @@ struct kernel_listener_t { /** * Hook called if an exire event for an IPsec SA is received. * - * @param reqid reqid of the expired SA * @param protocol protocol of the expired SA * @param spi spi of the expired SA + * @param dst destination address of expired SA * @param hard TRUE if it is a hard expire, FALSE otherwise * @return TRUE to remain registered, FALSE to unregister */ - bool (*expire)(kernel_listener_t *this, u_int32_t reqid, - u_int8_t protocol, u_int32_t spi, bool hard); + bool (*expire)(kernel_listener_t *this, u_int8_t protocol, u_int32_t spi, + host_t *dst, bool hard); /** * Hook called if the NAT mappings of an IPsec SA changed. * - * @param reqid reqid of the SA + * @param protocol IPsec protocol of affected SA * @param spi spi of the SA + * @param dst old destinatino address of SA * @param remote new remote host * @return TRUE to remain registered, FALSE to unregister */ - bool (*mapping)(kernel_listener_t *this, u_int32_t reqid, u_int32_t spi, - host_t *remote); + bool (*mapping)(kernel_listener_t *this, u_int8_t protocol, u_int32_t spi, + host_t *dst, host_t *remote); /** * Hook called if a migrate event for a policy is received. diff --git a/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c b/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c index cfe58bc6b..b4875ba58 100644 --- a/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c +++ b/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c @@ -870,25 +870,26 @@ static void process_expire(private_kernel_netlink_ipsec_t *this, struct nlmsghdr *hdr) { struct xfrm_user_expire *expire; - u_int32_t spi, reqid; + u_int32_t spi; u_int8_t protocol; + host_t *dst; expire = NLMSG_DATA(hdr); protocol = expire->state.id.proto; spi = expire->state.id.spi; - reqid = expire->state.reqid; DBG2(DBG_KNL, "received a XFRM_MSG_EXPIRE"); - if (protocol != IPPROTO_ESP && protocol != IPPROTO_AH) + if (protocol == IPPROTO_ESP || protocol == IPPROTO_AH) { - DBG2(DBG_KNL, "ignoring XFRM_MSG_EXPIRE for SA with SPI %.8x and " - "reqid {%u} which is not a CHILD_SA", ntohl(spi), reqid); - return; + dst = xfrm2host(expire->state.family, &expire->state.id.daddr, 0); + if (dst) + { + hydra->kernel_interface->expire(hydra->kernel_interface, protocol, + spi, dst, expire->hard != 0); + dst->destroy(dst); + } } - - hydra->kernel_interface->expire(hydra->kernel_interface, reqid, protocol, - spi, expire->hard != 0); } /** @@ -972,23 +973,29 @@ static void process_mapping(private_kernel_netlink_ipsec_t *this, struct nlmsghdr *hdr) { struct xfrm_user_mapping *mapping; - u_int32_t spi, reqid; + u_int32_t spi; mapping = NLMSG_DATA(hdr); spi = mapping->id.spi; - reqid = mapping->reqid; DBG2(DBG_KNL, "received a XFRM_MSG_MAPPING"); if (mapping->id.proto == IPPROTO_ESP) { - host_t *host; - host = xfrm2host(mapping->id.family, &mapping->new_saddr, - mapping->new_sport); - if (host) + host_t *dst, *new; + + dst = xfrm2host(mapping->id.family, &mapping->id.daddr, 0); + if (dst) { - hydra->kernel_interface->mapping(hydra->kernel_interface, reqid, - spi, host); + new = xfrm2host(mapping->id.family, &mapping->new_saddr, + mapping->new_sport); + if (new) + { + hydra->kernel_interface->mapping(hydra->kernel_interface, + IPPROTO_ESP, spi, dst, new); + new->destroy(new); + } + dst->destroy(dst); } } } @@ -1066,7 +1073,7 @@ METHOD(kernel_ipsec_t, get_features, kernel_feature_t, */ static status_t get_spi_internal(private_kernel_netlink_ipsec_t *this, host_t *src, host_t *dst, u_int8_t proto, u_int32_t min, u_int32_t max, - u_int32_t reqid, u_int32_t *spi) + u_int32_t *spi) { netlink_buf_t request; struct nlmsghdr *hdr, *out; @@ -1086,7 +1093,6 @@ static status_t get_spi_internal(private_kernel_netlink_ipsec_t *this, host2xfrm(dst, &userspi->info.id.daddr); userspi->info.id.proto = proto; userspi->info.mode = XFRM_MODE_TUNNEL; - userspi->info.reqid = reqid; userspi->info.family = src->get_family(src); userspi->min = min; userspi->max = max; @@ -1133,39 +1139,35 @@ static status_t get_spi_internal(private_kernel_netlink_ipsec_t *this, METHOD(kernel_ipsec_t, get_spi, status_t, private_kernel_netlink_ipsec_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) { - DBG2(DBG_KNL, "getting SPI for reqid {%u}", reqid); - if (get_spi_internal(this, src, dst, protocol, - 0xc0000000, 0xcFFFFFFF, reqid, spi) != SUCCESS) + 0xc0000000, 0xcFFFFFFF, spi) != SUCCESS) { - DBG1(DBG_KNL, "unable to get SPI for reqid {%u}", reqid); + DBG1(DBG_KNL, "unable to get SPI"); return FAILED; } - DBG2(DBG_KNL, "got SPI %.8x for reqid {%u}", ntohl(*spi), reqid); + DBG2(DBG_KNL, "got SPI %.8x", ntohl(*spi)); return SUCCESS; } METHOD(kernel_ipsec_t, get_cpi, status_t, private_kernel_netlink_ipsec_t *this, host_t *src, host_t *dst, - u_int32_t reqid, u_int16_t *cpi) + u_int16_t *cpi) { u_int32_t received_spi = 0; - DBG2(DBG_KNL, "getting CPI for reqid {%u}", reqid); - if (get_spi_internal(this, src, dst, IPPROTO_COMP, - 0x100, 0xEFFF, reqid, &received_spi) != SUCCESS) + 0x100, 0xEFFF, &received_spi) != SUCCESS) { - DBG1(DBG_KNL, "unable to get CPI for reqid {%u}", reqid); + DBG1(DBG_KNL, "unable to get CPI"); return FAILED; } *cpi = htons((u_int16_t)ntohl(received_spi)); - DBG2(DBG_KNL, "got CPI %.4x for reqid {%u}", ntohs(*cpi), reqid); + DBG2(DBG_KNL, "got CPI %.4x", ntohs(*cpi)); return SUCCESS; } @@ -1196,7 +1198,7 @@ METHOD(kernel_ipsec_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) { netlink_buf_t request; char *alg_name; @@ -1204,6 +1206,7 @@ METHOD(kernel_ipsec_t, add_sa, status_t, struct xfrm_usersa_info *sa; u_int16_t icv_size = 64; ipsec_mode_t original_mode = mode; + traffic_selector_t *first_src_ts, *first_dst_ts; status_t status = FAILED; /* if IPComp is used, we install an additional IPComp SA. if the cpi is 0 @@ -1249,9 +1252,10 @@ METHOD(kernel_ipsec_t, add_sa, status_t, * selector can be installed other traffic would get dropped */ break; } - if (src_ts && dst_ts) + if (src_ts->get_first(src_ts, (void**)&first_src_ts) == SUCCESS && + dst_ts->get_first(dst_ts, (void**)&first_dst_ts) == SUCCESS) { - sa->sel = ts2selector(src_ts, dst_ts); + sa->sel = ts2selector(first_src_ts, first_dst_ts); if (!this->proto_port_transport) { /* don't install proto/port on SA. This would break diff --git a/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c index 6b5678270..8b893f485 100644 --- a/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c +++ b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c @@ -1296,7 +1296,8 @@ static void process_expire(private_kernel_pfkey_ipsec_t *this, { pfkey_msg_t response; u_int8_t protocol; - u_int32_t spi, reqid; + u_int32_t spi; + host_t *dst; bool hard; DBG2(DBG_KNL, "received an SADB_EXPIRE"); @@ -1309,18 +1310,18 @@ static void process_expire(private_kernel_pfkey_ipsec_t *this, protocol = satype2proto(msg->sadb_msg_satype); spi = response.sa->sadb_sa_spi; - reqid = response.x_sa2->sadb_x_sa2_reqid; hard = response.lft_hard != NULL; - if (protocol != IPPROTO_ESP && protocol != IPPROTO_AH) + if (protocol == IPPROTO_ESP || protocol == IPPROTO_AH) { - DBG2(DBG_KNL, "ignoring SADB_EXPIRE for SA with SPI %.8x and " - "reqid {%u} which is not a CHILD_SA", ntohl(spi), reqid); - return; + dst = host_create_from_sockaddr((sockaddr_t*)(response.dst + 1)); + if (dst) + { + hydra->kernel_interface->expire(hydra->kernel_interface, protocol, + spi, dst, hard); + dst->destroy(dst); + } } - - hydra->kernel_interface->expire(hydra->kernel_interface, reqid, protocol, - spi, hard); } #ifdef SADB_X_MIGRATE @@ -1387,9 +1388,9 @@ static void process_mapping(private_kernel_pfkey_ipsec_t *this, struct sadb_msg* msg) { pfkey_msg_t response; - u_int32_t spi, reqid; + u_int32_t spi; sockaddr_t *sa; - host_t *host; + host_t *dst, *new; DBG2(DBG_KNL, "received an SADB_X_NAT_T_NEW_MAPPING"); @@ -1407,7 +1408,6 @@ static void process_mapping(private_kernel_pfkey_ipsec_t *this, } spi = response.sa->sadb_sa_spi; - reqid = response.x_sa2->sadb_x_sa2_reqid; if (satype2proto(msg->sadb_msg_satype) != IPPROTO_ESP) { @@ -1415,6 +1415,7 @@ static void process_mapping(private_kernel_pfkey_ipsec_t *this, } sa = (sockaddr_t*)(response.dst + 1); + dst = host_create_from_sockaddr(sa); switch (sa->sa_family) { case AF_INET: @@ -1432,12 +1433,16 @@ static void process_mapping(private_kernel_pfkey_ipsec_t *this, default: break; } - - host = host_create_from_sockaddr(sa); - if (host) + if (dst) { - hydra->kernel_interface->mapping(hydra->kernel_interface, reqid, - spi, host); + new = host_create_from_sockaddr(sa); + if (new) + { + hydra->kernel_interface->mapping(hydra->kernel_interface, + IPPROTO_ESP, spi, dst, new); + new->destroy(new); + } + dst->destroy(dst); } } #endif /*SADB_X_NAT_T_NEW_MAPPING*/ @@ -1518,11 +1523,10 @@ static bool receive_events(private_kernel_pfkey_ipsec_t *this, int fd, static status_t get_spi_internal(private_kernel_pfkey_ipsec_t *this, host_t *src, host_t *dst, u_int8_t proto, u_int32_t min, u_int32_t max, - u_int32_t reqid, u_int32_t *spi) + u_int32_t *spi) { unsigned char request[PFKEY_BUFFER_SIZE]; struct sadb_msg *msg, *out; - struct sadb_x_sa2 *sa2; struct sadb_spirange *range; pfkey_msg_t response; u_int32_t received_spi = 0; @@ -1536,12 +1540,6 @@ static status_t get_spi_internal(private_kernel_pfkey_ipsec_t *this, msg->sadb_msg_satype = proto2satype(proto); msg->sadb_msg_len = PFKEY_LEN(sizeof(struct sadb_msg)); - sa2 = (struct sadb_x_sa2*)PFKEY_EXT_ADD_NEXT(msg); - sa2->sadb_x_sa2_exttype = SADB_X_EXT_SA2; - sa2->sadb_x_sa2_len = PFKEY_LEN(sizeof(struct sadb_spirange)); - sa2->sadb_x_sa2_reqid = reqid; - PFKEY_EXT_ADD(msg, sa2); - add_addr_ext(msg, src, SADB_EXT_ADDRESS_SRC, 0, 0, FALSE); add_addr_ext(msg, dst, SADB_EXT_ADDRESS_DST, 0, 0, FALSE); @@ -1577,39 +1575,37 @@ static status_t get_spi_internal(private_kernel_pfkey_ipsec_t *this, METHOD(kernel_ipsec_t, get_spi, status_t, private_kernel_pfkey_ipsec_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) { - DBG2(DBG_KNL, "getting SPI for reqid {%u}", reqid); - if (get_spi_internal(this, src, dst, protocol, - 0xc0000000, 0xcFFFFFFF, reqid, spi) != SUCCESS) + 0xc0000000, 0xcFFFFFFF, spi) != SUCCESS) { - DBG1(DBG_KNL, "unable to get SPI for reqid {%u}", reqid); + DBG1(DBG_KNL, "unable to get SPI"); return FAILED; } - DBG2(DBG_KNL, "got SPI %.8x for reqid {%u}", ntohl(*spi), reqid); + DBG2(DBG_KNL, "got SPI %.8x", ntohl(*spi)); return SUCCESS; } METHOD(kernel_ipsec_t, get_cpi, status_t, private_kernel_pfkey_ipsec_t *this, host_t *src, host_t *dst, - u_int32_t reqid, u_int16_t *cpi) + u_int16_t *cpi) { u_int32_t received_spi = 0; - DBG2(DBG_KNL, "getting CPI for reqid {%u}", reqid); + DBG2(DBG_KNL, "getting CPI"); if (get_spi_internal(this, src, dst, IPPROTO_COMP, - 0x100, 0xEFFF, reqid, &received_spi) != SUCCESS) + 0x100, 0xEFFF, &received_spi) != SUCCESS) { - DBG1(DBG_KNL, "unable to get CPI for reqid {%u}", reqid); + DBG1(DBG_KNL, "unable to get CPI"); return FAILED; } *cpi = htons((u_int16_t)ntohl(received_spi)); - DBG2(DBG_KNL, "got CPI %.4x for reqid {%u}", ntohs(*cpi), reqid); + DBG2(DBG_KNL, "got CPI %.4x", ntohs(*cpi)); return SUCCESS; } @@ -1620,7 +1616,7 @@ METHOD(kernel_ipsec_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) { unsigned char request[PFKEY_BUFFER_SIZE]; struct sadb_msg *msg, *out; @@ -1644,6 +1640,23 @@ METHOD(kernel_ipsec_t, add_sa, status_t, mode = MODE_TRANSPORT; } + if (inbound) + { + /* As we didn't know the reqid during SPI allocation, we used reqid + * zero. Unfortunately we can't SADB_UPDATE to the new reqid, hence we + * have to delete the SPI allocation state manually. The reqid + * selector does not count for that, therefore we have to delete + * that state before installing the new SA to avoid deleting the + * the new state after installing it. */ + mark_t zeromark = {0, 0}; + + if (this->public.interface.del_sa(&this->public.interface, + src, dst, spi, protocol, 0, zeromark) != SUCCESS) + { + DBG1(DBG_KNL, "deleting SPI allocation SA failed"); + } + } + memset(&request, 0, sizeof(request)); DBG2(DBG_KNL, "adding SAD entry with SPI %.8x and reqid {%u}", @@ -1651,7 +1664,7 @@ METHOD(kernel_ipsec_t, add_sa, status_t, msg = (struct sadb_msg*)request; msg->sadb_msg_version = PF_KEY_V2; - msg->sadb_msg_type = inbound ? SADB_UPDATE : SADB_ADD; + msg->sadb_msg_type = SADB_ADD; msg->sadb_msg_satype = proto2satype(protocol); msg->sadb_msg_len = PFKEY_LEN(sizeof(struct sadb_msg)); |