diff --git a/src/charon/plugins/kernel_netlink/kernel_netlink_ipsec.c b/src/charon/plugins/kernel_netlink/kernel_netlink_ipsec.c index 1b526e6f0..d5ff47954 100644 --- a/src/charon/plugins/kernel_netlink/kernel_netlink_ipsec.c +++ b/src/charon/plugins/kernel_netlink/kernel_netlink_ipsec.c @@ -381,6 +381,53 @@ static struct xfrm_selector ts2selector(traffic_selector_t *src, return sel; } +/** + * convert a xfrm_selector to a src|dst traffic_selector + */ +static traffic_selector_t* selector2ts(struct xfrm_selector *sel, bool src) +{ + ts_type_t type; + chunk_t addr; + u_int16_t port, port_mask, from_port, to_port; + + if (src) + { + addr.ptr = (u_char*)&sel->saddr; + port = sel->sport; + port_mask = sel->sport_mask; + } + else + { + addr.ptr = (u_char*)&sel->daddr; + port = sel->dport; + port_mask = sel->dport_mask; + } + /* The Linux 2.6 kernel does not set the selector's family field, + * so as a kludge we additionally test the prefix length. + */ + if (sel->family == AF_INET || sel->prefixlen_d == 32) + { + type = TS_IPV4_ADDR_RANGE; + addr.len = 4; + } + else + { + type = TS_IPV6_ADDR_RANGE; + addr.len = 16; + } + if (port_mask == 0) + { + from_port = 0; + to_port = 65535; + } + else + { + from_port = to_port = ntohs(port); + } + + return traffic_selector_create_from_bytes(sel->proto, type, + addr, from_port, addr, to_port); +} /** * process a XFRM_MSG_ACQUIRE from kernel @@ -389,15 +436,22 @@ static void process_acquire(private_kernel_netlink_ipsec_t *this, struct nlmsghd { u_int32_t reqid = 0; int proto = 0; + traffic_selector_t *src_ts, *dst_ts; + struct xfrm_user_acquire *acquire; + struct rtattr *rtattr; + size_t rtsize; job_t *job; - struct rtattr *rtattr = XFRM_RTA(hdr, struct xfrm_user_acquire); - size_t rtsize = XFRM_PAYLOAD(hdr, struct xfrm_user_tmpl); + rtattr = XFRM_RTA(hdr, struct xfrm_user_acquire); + rtsize = XFRM_PAYLOAD(hdr, struct xfrm_user_tmpl); + if (RTA_OK(rtattr, rtsize)) { if (rtattr->rta_type == XFRMA_TMPL) { - struct xfrm_user_tmpl* tmpl = (struct xfrm_user_tmpl*)RTA_DATA(rtattr); + struct xfrm_user_tmpl* tmpl; + + tmpl = (struct xfrm_user_tmpl*)RTA_DATA(rtattr); reqid = tmpl->reqid; proto = tmpl->id.proto; } @@ -412,14 +466,14 @@ static void process_acquire(private_kernel_netlink_ipsec_t *this, struct nlmsghd /* acquire for AH/ESP only, not for IPCOMP */ return; } - if (reqid == 0) - { - DBG1(DBG_KNL, "received a XFRM_MSG_ACQUIRE, but no reqid found"); - return; - } DBG2(DBG_KNL, "received a XFRM_MSG_ACQUIRE"); - DBG1(DBG_KNL, "creating acquire job for CHILD_SA with reqid {%d}", reqid); - job = (job_t*)acquire_job_create(reqid); + + acquire = (struct xfrm_user_acquire*)NLMSG_DATA(hdr); + src_ts = selector2ts(&acquire->sel, TRUE); + dst_ts = selector2ts(&acquire->sel, FALSE); + DBG1(DBG_KNL, "creating acquire job %R === %R for CHILD_SA with reqid {%d}", + src_ts, dst_ts, reqid); + job = (job_t*)acquire_job_create(reqid, src_ts, dst_ts); charon->processor->queue_job(charon->processor, job); } @@ -461,6 +515,14 @@ static void process_expire(private_kernel_netlink_ipsec_t *this, struct nlmsghdr charon->processor->queue_job(charon->processor, job); } +/** + * process a XFRM_MSG_MIGRATE from kernel + */ +static void process_migrate(private_kernel_netlink_ipsec_t *this, struct nlmsghdr *hdr) +{ + DBG2(DBG_KNL, "received a XFRM_MSG_MIGRATE"); +} + /** * process a XFRM_MSG_MAPPING from kernel */ @@ -540,6 +602,9 @@ static job_requeue_t receive_events(private_kernel_netlink_ipsec_t *this) case XFRM_MSG_EXPIRE: process_expire(this, hdr); break; + case XFRM_MSG_MIGRATE: + process_migrate(this, hdr); + break; case XFRM_MSG_MAPPING: process_mapping(this, hdr); break; diff --git a/src/charon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c b/src/charon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c index 50d6322a8..cb4d006ca 100644 --- a/src/charon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c +++ b/src/charon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c @@ -28,6 +28,7 @@ #include "kernel_pfkey_ipsec.h" #include +#include #include #include #include @@ -39,7 +40,7 @@ #define PRIO_HIGH 2000 /** buffer size for PF_KEY messages */ -#define PFKEY_BUFFER_SIZE 2048 +#define PFKEY_BUFFER_SIZE 4096 /** PF_KEY messages are 64 bit aligned */ #define PFKEY_ALIGNMENT 8 @@ -289,10 +290,39 @@ struct pfkey_msg_t struct sadb_x_nat_t_port *x_natt_dport; /* SADB_X_EXT_NAT_T_DPORT */ struct sadb_address *x_natt_oa; /* SADB_X_EXT_NAT_T_OA */ struct sadb_x_sec_ctx *x_sec_ctx; /* SADB_X_EXT_SEC_CTX */ + struct sadb_x_kmaddress *x_kmaddress; /* SADB_X_EXT_KMADDRESS */ } __attribute__((__packed__)); }; }; +ENUM(sadb_ext_type_names, SADB_EXT_RESERVED, SADB_X_EXT_KMADDRESS, + "SADB_EXT_RESERVED", + "SADB_EXT_SA", + "SADB_EXT_LIFETIME_CURRENT", + "SADB_EXT_LIFETIME_HARD", + "SADB_EXT_LIFETIME_SOFT", + "SADB_EXT_ADDRESS_SRC", + "SADB_EXT_ADDRESS_DST", + "SADB_EXT_ADDRESS_PROXY", + "SADB_EXT_KEY_AUTH", + "SADB_EXT_KEY_ENCRYPT", + "SADB_EXT_IDENTITY_SRC", + "SADB_EXT_IDENTITY_DST", + "SADB_EXT_SENSITIVITY", + "SADB_EXT_PROPOSAL", + "SADB_EXT_SUPPORTED_AUTH", + "SADB_EXT_SUPPORTED_ENCRYPT", + "SADB_EXT_SPIRANGE", + "SADB_X_EXT_KMPRIVATE", + "SADB_X_EXT_POLICY", + "SADB_X_EXT_SA2", + "SADB_X_EXT_NAT_T_TYPE", + "SADB_X_EXT_NAT_T_SPORT", + "SADB_X_EXT_NAT_T_DPORT", + "SADB_X_EXT_NAT_T_OA", + "SADB_X_EXT_SEC_CTX", + "SADB_X_EXT_KMADDRESS" +); /** * convert a IKEv2 specific protocol identifier to the PF_KEY sa type */ @@ -415,13 +445,13 @@ static kernel_algorithm_t encryption_algs[] = { /* {ENCR_DES_IV32, 0 }, */ {ENCR_NULL, SADB_EALG_NULL }, {ENCR_AES_CBC, SADB_X_EALG_AESCBC }, -/* {ENCR_AES_CTR, 0 }, */ -/* {ENCR_AES_CCM_ICV8, 0 }, */ -/* {ENCR_AES_CCM_ICV12, 0 }, */ -/* {ENCR_AES_CCM_ICV16, 0 }, */ -/* {ENCR_AES_GCM_ICV8, 0 }, */ -/* {ENCR_AES_GCM_ICV12, 0 }, */ -/* {ENCR_AES_GCM_ICV16, 0 }, */ +/* {ENCR_AES_CTR, SADB_X_EALG_AESCTR }, */ +/* {ENCR_AES_CCM_ICV8, SADB_X_EALG_AES_CCM_ICV8 }, */ +/* {ENCR_AES_CCM_ICV12, SADB_X_EALG_AES_CCM_ICV12 }, */ +/* {ENCR_AES_CCM_ICV16, SADB_X_EALG_AES_CCM_ICV16 }, */ +/* {ENCR_AES_GCM_ICV8, SADB_X_EALG_AES_GCM_ICV8 }, */ +/* {ENCR_AES_GCM_ICV12, SADB_X_EALG_AES_GCM_ICV12 }, */ +/* {ENCR_AES_GCM_ICV16, SADB_X_EALG_AES_GCM_ICV16 }, */ {END_OF_LIST, 0 }, }; @@ -505,6 +535,40 @@ static void add_encap_ext(struct sadb_msg *msg, host_t *src, host_t *dst) PFKEY_EXT_ADD(msg, nat_port); } +/** + * Convert a sadb_address to a traffic_selector + */ +static traffic_selector_t* sadb_address2ts(struct sadb_address *address) +{ + traffic_selector_t *ts; + ts_type_t type; + chunk_t addr; + host_t *host; + u_int16_t port, from_port, to_port; + + /* The Linux 2.6 kernel does not set the protocol and port information + * in the src and dst sadb_address extensions of the SADB_ACQUIRE message. + */ + host = host_create_from_sockaddr((sockaddr_t*)&address[1]) ; + type = (host->get_family(host) == AF_INET) ? TS_IPV4_ADDR_RANGE : + TS_IPV6_ADDR_RANGE; + addr = host->get_address(host); + port = host->get_port(host); + if (port == 0) + { + from_port = 0; + to_port = 65535; + } + else + { + from_port = to_port = port; + } + ts = traffic_selector_create_from_bytes(address->sadb_address_proto, type, + addr, from_port, addr, to_port); + host->destroy(host); + return ts; +} + /** * Parses a pfkey message received from the kernel */ @@ -523,10 +587,12 @@ static status_t parse_pfkey_message(struct sadb_msg *msg, pfkey_msg_t *out) while (len >= PFKEY_LEN(sizeof(struct sadb_ext))) { + DBG2(DBG_KNL, " %N", sadb_ext_type_names, ext->sadb_ext_type); if (ext->sadb_ext_len < PFKEY_LEN(sizeof(struct sadb_ext)) || ext->sadb_ext_len > len) { - DBG1(DBG_KNL, "length of PF_KEY extension (%d) is invalid", ext->sadb_ext_type); + DBG1(DBG_KNL, "length of %N extension is invalid", + sadb_ext_type_names, ext->sadb_ext_type); break; } @@ -538,7 +604,8 @@ static status_t parse_pfkey_message(struct sadb_msg *msg, pfkey_msg_t *out) if (out->ext[ext->sadb_ext_type]) { - DBG1(DBG_KNL, "duplicate PF_KEY extension of type (%d)", ext->sadb_ext_type); + DBG1(DBG_KNL, "duplicate %N extension", + sadb_ext_type_names, ext->sadb_ext_type); break; } @@ -670,7 +737,8 @@ static status_t pfkey_send(private_kernel_pfkey_ipsec_t *this, static void process_acquire(private_kernel_pfkey_ipsec_t *this, struct sadb_msg* msg) { pfkey_msg_t response; - u_int32_t index, reqid; + u_int32_t index, reqid = 0; + traffic_selector_t *src_ts, *dst_ts; policy_entry_t *policy; job_t *job; @@ -684,6 +752,7 @@ static void process_acquire(private_kernel_pfkey_ipsec_t *this, struct sadb_msg* /* acquire for AH/ESP only */ return; } + DBG2(DBG_KNL, "received an SADB_ACQUIRE"); if (parse_pfkey_message(msg, &response) != SUCCESS) { @@ -692,22 +761,24 @@ static void process_acquire(private_kernel_pfkey_ipsec_t *this, struct sadb_msg* } index = response.x_policy->sadb_x_policy_id; - DBG2(DBG_KNL, "received an SADB_ACQUIRE, %d", index); pthread_mutex_lock(&this->mutex); if (this->policies->find_first(this->policies, - (linked_list_match_t)policy_entry_match_byindex, (void**)&policy, &index) != SUCCESS) + (linked_list_match_t)policy_entry_match_byindex, (void**)&policy, &index) == SUCCESS) { - DBG1(DBG_KNL, "received an SADB_ACQUIRE, but found no matching policy"); - pthread_mutex_unlock(&this->mutex); - return; + reqid = policy->reqid; } - reqid = policy->reqid; - DBG2(DBG_KNL, "received an SADB_ACQUIRE, %d", reqid); + else + { + DBG1(DBG_KNL, "received an SADB_ACQUIRE with policy id %d but no matching policy found", + index); + } + src_ts = sadb_address2ts(response.src); + dst_ts = sadb_address2ts(response.dst); pthread_mutex_unlock(&this->mutex); - DBG2(DBG_KNL, "received an SADB_ACQUIRE"); - DBG1(DBG_KNL, "creating acquire job for CHILD_SA with reqid {%d}", reqid); - job = (job_t*)acquire_job_create(reqid); + DBG1(DBG_KNL, "creating acquire job %R === %R for CHILD_SA with reqid {%d}", + src_ts, dst_ts, reqid); + job = (job_t*)acquire_job_create(reqid, src_ts, dst_ts); charon->processor->queue_job(charon->processor, job); } @@ -756,6 +827,22 @@ static void process_expire(private_kernel_pfkey_ipsec_t *this, struct sadb_msg* charon->processor->queue_job(charon->processor, job); } +/** + * Process a SADB_MIGRATE message from the kernel + */ +static void process_migrate(private_kernel_pfkey_ipsec_t *this, struct sadb_msg* msg) +{ + pfkey_msg_t response; + + DBG2(DBG_KNL, "received an SADB_X_MIGRATE"); + + if (parse_pfkey_message(msg, &response) != SUCCESS) + { + DBG1(DBG_KNL, "parsing SADB_X_MIGRATE from kernel failed"); + return; + } +} + /** * Process a SADB_X_NAT_T_NEW_MAPPING message from the kernel */ @@ -866,6 +953,9 @@ static job_requeue_t receive_events(private_kernel_pfkey_ipsec_t *this) case SADB_EXPIRE: process_expire(this, msg); break; + case SADB_X_MIGRATE: + process_migrate(this, msg); + break; case SADB_X_NAT_T_NEW_MAPPING: process_mapping(this, msg); break; diff --git a/src/charon/processing/jobs/acquire_job.c b/src/charon/processing/jobs/acquire_job.c index e066cbac5..976749641 100644 --- a/src/charon/processing/jobs/acquire_job.c +++ b/src/charon/processing/jobs/acquire_job.c @@ -35,6 +35,16 @@ struct private_acquire_job_t { * reqid of the child to rekey */ u_int32_t reqid; + + /** + * acquired source traffic selector + */ + traffic_selector_t *src_ts; + + /** + * acquired destination traffic selector + */ + traffic_selector_t *dst_ts; }; /** @@ -42,6 +52,8 @@ struct private_acquire_job_t { */ static void destroy(private_acquire_job_t *this) { + DESTROY_IF(this->src_ts); + DESTROY_IF(this->dst_ts); free(this); } @@ -50,13 +62,16 @@ static void destroy(private_acquire_job_t *this) */ static void execute(private_acquire_job_t *this) { - ike_sa_t *ike_sa; + ike_sa_t *ike_sa = NULL; - ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager, - this->reqid, TRUE); + if (this->reqid) + { + ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager, + this->reqid, TRUE); + } if (ike_sa == NULL) { - DBG2(DBG_JOB, "CHILD_SA with reqid %d not found for acquiring", + DBG1(DBG_JOB, "acquire job found no CHILD_SA with reqid {%d}", this->reqid); } else @@ -71,7 +86,9 @@ static void execute(private_acquire_job_t *this) /* * Described in header */ -acquire_job_t *acquire_job_create(u_int32_t reqid) +acquire_job_t *acquire_job_create(u_int32_t reqid, + traffic_selector_t *src_ts, + traffic_selector_t *dst_ts) { private_acquire_job_t *this = malloc_thing(private_acquire_job_t); @@ -81,6 +98,8 @@ acquire_job_t *acquire_job_create(u_int32_t reqid) /* private variables */ this->reqid = reqid; + this->src_ts = src_ts; + this->dst_ts = dst_ts; return &this->public; } diff --git a/src/charon/processing/jobs/acquire_job.h b/src/charon/processing/jobs/acquire_job.h index b256c7fc1..edbd51d2d 100644 --- a/src/charon/processing/jobs/acquire_job.h +++ b/src/charon/processing/jobs/acquire_job.h @@ -26,6 +26,7 @@ typedef struct acquire_job_t acquire_job_t; #include +#include #include /** @@ -46,8 +47,12 @@ struct acquire_job_t { * We use the reqid to find the routed CHILD_SA. * * @param reqid reqid of the CHILD_SA to acquire + * @param src_ts source traffic selector + * @param dst_ts destination traffic selector * @return acquire_job_t object */ -acquire_job_t *acquire_job_create(u_int32_t reqid); +acquire_job_t *acquire_job_create(u_int32_t reqid, + traffic_selector_t *src_ts, + traffic_selector_t *dst_ts); #endif /* REKEY_CHILD_SA_JOB_H_ @} */