diff --git a/src/frontends/android/app/src/main/jni/libandroidbridge/kernel/android_ipsec.c b/src/frontends/android/app/src/main/jni/libandroidbridge/kernel/android_ipsec.c index 4b00527d6..7af027e75 100644 --- a/src/frontends/android/app/src/main/jni/libandroidbridge/kernel/android_ipsec.c +++ b/src/frontends/android/app/src/main/jni/libandroidbridge/kernel/android_ipsec.c @@ -46,6 +46,13 @@ static void expire(uint8_t protocol, uint32_t spi, host_t *dst, bool hard) charon->kernel->expire(charon->kernel, protocol, spi, dst, hard); } +METHOD(kernel_ipsec_t, get_features, kernel_feature_t, + private_kernel_android_ipsec_t *this) +{ + return KERNEL_REQUIRE_UDP_ENCAPSULATION | KERNEL_ESP_V3_TFC | + KERNEL_SA_USE_TIME; +} + METHOD(kernel_ipsec_t, get_spi, status_t, private_kernel_android_ipsec_t *this, host_t *src, host_t *dst, uint8_t protocol, uint32_t *spi) @@ -166,6 +173,7 @@ kernel_android_ipsec_t *kernel_android_ipsec_create() INIT(this, .public = { .interface = { + .get_features = _get_features, .get_spi = _get_spi, .get_cpi = _get_cpi, .add_sa = _add_sa, diff --git a/src/include/linux/xfrm.h b/src/include/linux/xfrm.h index 094772dfb..80630dfd4 100644 --- a/src/include/linux/xfrm.h +++ b/src/include/linux/xfrm.h @@ -288,7 +288,7 @@ enum xfrm_attr_type_t { XFRMA_ETIMER_THRESH, XFRMA_SRCADDR, /* xfrm_address_t */ XFRMA_COADDR, /* xfrm_address_t */ - XFRMA_LASTUSED, /* unsigned long */ + XFRMA_LASTUSED, /* __u64 */ XFRMA_POLICY_TYPE, /* struct xfrm_userpolicy_type */ XFRMA_MIGRATE, XFRMA_ALG_AEAD, /* struct xfrm_algo_aead */ diff --git a/src/libcharon/kernel/kernel_interface.h b/src/libcharon/kernel/kernel_interface.h index c11738b40..2bc9d8657 100644 --- a/src/libcharon/kernel/kernel_interface.h +++ b/src/libcharon/kernel/kernel_interface.h @@ -79,6 +79,8 @@ enum kernel_feature_t { KERNEL_NO_POLICY_UPDATES = (1<<3), /** IPsec backend supports installing SPIs on policies */ KERNEL_POLICY_SPI = (1<<4), + /** IPsec backend reports use time per SA via query_sa() */ + KERNEL_SA_USE_TIME = (1<<5), }; /** @@ -202,7 +204,11 @@ struct kernel_interface_t { kernel_ipsec_update_sa_t *data); /** - * Query the number of bytes processed by an SA from the SAD. + * Query the number of bytes and packets processed by an SA from the SAD. + * + * Some implementations may also return the last use time (as indicated by + * get_features()). This is a monotonic timestamp as returned by + * time_monotonic(). * * @param id data identifying this SA * @param data data to query the SA @@ -247,11 +253,12 @@ struct kernel_interface_t { * Query the use time of a policy. * * The use time of a policy is the time the policy was used - * for the last time. + * for the last time. This is a monotonic timestamp as returned by + * time_monotonic(). * * @param id data identifying this policy * @param data data to query the policy - * @param[out] use_time the monotonic timestamp of this SA's last use + * @param[out] use_time the monotonic timestamp of this policy's last use * @return SUCCESS if operation completed */ status_t (*query_policy)(kernel_interface_t *this, diff --git a/src/libcharon/kernel/kernel_ipsec.h b/src/libcharon/kernel/kernel_ipsec.h index 3e1c80e70..0c7fca0a7 100644 --- a/src/libcharon/kernel/kernel_ipsec.h +++ b/src/libcharon/kernel/kernel_ipsec.h @@ -269,7 +269,11 @@ struct kernel_ipsec_t { kernel_ipsec_update_sa_t *data); /** - * Query the number of bytes processed by an SA from the SAD. + * Query the number of bytes and packets processed by an SA from the SAD. + * + * Some implementations may also return the last use time (as indicated by + * get_features()). This is a monotonic timestamp as returned by + * time_monotonic(). * * @param id data identifying this SA * @param data data to query the SA @@ -314,12 +318,11 @@ struct kernel_ipsec_t { * Query the use time of a policy. * * The use time of a policy is the time the policy was used for the last - * time. It is not the system time, but a monotonic timestamp as returned - * by time_monotonic. + * time. This is a monotonic timestamp as returned by time_monotonic(). * * @param id data identifying this policy * @param data data to query the policy - * @param[out] use_time the monotonic timestamp of this SA's last use + * @param[out] use_time the monotonic timestamp of this policy's last use * @return SUCCESS if operation completed */ status_t (*query_policy)(kernel_ipsec_t *this, diff --git a/src/libcharon/plugins/kernel_libipsec/kernel_libipsec_ipsec.c b/src/libcharon/plugins/kernel_libipsec/kernel_libipsec_ipsec.c index b3aa75ef3..d067ed58e 100644 --- a/src/libcharon/plugins/kernel_libipsec/kernel_libipsec_ipsec.c +++ b/src/libcharon/plugins/kernel_libipsec/kernel_libipsec_ipsec.c @@ -231,7 +231,8 @@ static void expire(uint8_t protocol, uint32_t spi, host_t *dst, bool hard) METHOD(kernel_ipsec_t, get_features, kernel_feature_t, private_kernel_libipsec_ipsec_t *this) { - return KERNEL_REQUIRE_UDP_ENCAPSULATION | KERNEL_ESP_V3_TFC; + return KERNEL_REQUIRE_UDP_ENCAPSULATION | KERNEL_ESP_V3_TFC | + KERNEL_SA_USE_TIME; } METHOD(kernel_ipsec_t, get_spi, status_t, diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c index 1e7ecef17..6580c9138 100644 --- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c +++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -341,6 +342,11 @@ struct private_kernel_netlink_ipsec_t { */ netlink_event_socket_t *socket_xfrm_events; + /** + * Whether the kernel reports the last use time on SAs + */ + bool sa_lastused; + /** * Whether to install routes along policies */ @@ -1157,7 +1163,8 @@ CALLBACK(receive_events, void, METHOD(kernel_ipsec_t, get_features, kernel_feature_t, private_kernel_netlink_ipsec_t *this) { - return KERNEL_ESP_V3_TFC | KERNEL_POLICY_SPI; + return KERNEL_ESP_V3_TFC | KERNEL_POLICY_SPI | + (this->sa_lastused ? KERNEL_SA_USE_TIME : 0); } /** @@ -2210,10 +2217,33 @@ static void get_replay_state(private_kernel_netlink_ipsec_t *this, free(out); } +/** + * Get the last used time of an SA if provided by the kernel + */ +static bool get_lastused(struct nlmsghdr *hdr, uint64_t *lastused) +{ + struct rtattr *rta; + size_t rtasize; + + rta = XFRM_RTA(hdr, struct xfrm_usersa_info); + rtasize = XFRM_PAYLOAD(hdr, struct xfrm_usersa_info); + while (RTA_OK(rta, rtasize)) + { + if (rta->rta_type == XFRMA_LASTUSED && + RTA_PAYLOAD(rta) == sizeof(*lastused)) + { + *lastused = *(uint64_t*)RTA_DATA(rta); + return TRUE; + } + rta = RTA_NEXT(rta, rtasize); + } + return FALSE; +} + METHOD(kernel_ipsec_t, query_sa, status_t, private_kernel_netlink_ipsec_t *this, kernel_ipsec_sa_id_t *id, kernel_ipsec_query_sa_t *data, uint64_t *bytes, uint64_t *packets, - time_t *time) + time_t *use_time) { netlink_buf_t request; struct nlmsghdr *out = NULL, *hdr; @@ -2291,11 +2321,20 @@ METHOD(kernel_ipsec_t, query_sa, status_t, { *packets = sa->curlft.packets; } - if (time) - { /* curlft contains an "use" time, but that contains a timestamp - * of the first use, not the last. Last use time must be queried - * on the policy on Linux */ - *time = 0; + if (use_time) + { + uint64_t lastused = 0; + + /* curlft.use_time contains the timestamp of the SA's first use, not + * the last, but we might get the last use time in an attribute */ + if (this->sa_lastused && get_lastused(hdr, &lastused)) + { + *use_time = time_monotonic(NULL) - (time(NULL) - lastused); + } + else + { + *use_time = 0; + } } status = SUCCESS; } @@ -4037,6 +4076,30 @@ static void setup_spd_hash_thresh(private_kernel_netlink_ipsec_t *this, } } +/** + * Check for kernel features (currently only via version number) + */ +static void check_kernel_features(private_kernel_netlink_ipsec_t *this) +{ + struct utsname utsname; + int a, b, c; + + if (uname(&utsname) == 0) + { + switch(sscanf(utsname.release, "%d.%d.%d", &a, &b, &c)) + { + case 2: + case 3: + /* before 6.2 the kernel only provided the last used time for + * specific outbound IPv6 SAs */ + this->sa_lastused = a > 6 || (a == 6 && b >= 2); + break; + default: + break; + } + } +} + /* * Described in header. */ @@ -4084,6 +4147,8 @@ kernel_netlink_ipsec_t *kernel_netlink_ipsec_create() "%s.plugins.kernel-netlink.port_bypass", FALSE, lib->ns), ); + check_kernel_features(this); + this->socket_xfrm = netlink_socket_create(NETLINK_XFRM, xfrm_msg_names, lib->settings->get_bool(lib->settings, "%s.plugins.kernel-netlink.parallel_xfrm", FALSE, lib->ns)); diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.c index 71905e3c9..cb0944640 100644 --- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.c +++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.c @@ -359,7 +359,7 @@ static status_t send_once(private_netlink_socket_t *this, struct nlmsghdr *in, if (this->names) { - DBG3(DBG_KNL, "sending %N %u: %b", this->names, in->nlmsg_type, + DBG4(DBG_KNL, "sending %N %u: %b", this->names, in->nlmsg_type, (u_int)seq, in, in->nlmsg_len); } @@ -426,7 +426,7 @@ static status_t send_once(private_netlink_socket_t *this, struct nlmsghdr *in, { if (this->names) { - DBG3(DBG_KNL, "received %N %u: %b", this->names, hdr->nlmsg_type, + DBG4(DBG_KNL, "received %N %u: %b", this->names, hdr->nlmsg_type, hdr->nlmsg_seq, hdr, hdr->nlmsg_len); } memcpy(ptr, hdr, hdr->nlmsg_len); diff --git a/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c b/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c index e7046a704..aa4b0f162 100644 --- a/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c +++ b/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c @@ -1659,6 +1659,16 @@ static status_t get_spi_internal(private_kernel_pfkey_ipsec_t *this, return SUCCESS; } +METHOD(kernel_ipsec_t, get_features, kernel_feature_t, + private_kernel_pfkey_ipsec_t *this) +{ +#ifdef __APPLE__ + return KERNEL_SA_USE_TIME; +#else + return 0; +#endif +} + METHOD(kernel_ipsec_t, get_spi, status_t, private_kernel_pfkey_ipsec_t *this, host_t *src, host_t *dst, uint8_t protocol, uint32_t *spi) @@ -2198,9 +2208,9 @@ METHOD(kernel_ipsec_t, query_sa, status_t, /* OS X uses the "last" time of use in usetime */ *time = response.lft_current->sadb_lifetime_usetime; #else /* !__APPLE__ */ - /* on Linux, sadb_lifetime_usetime is set to the "first" time of use, - * which is actually correct according to PF_KEY. We have to query - * policies for the last usetime. */ + /* on Linux and FreeBSD, sadb_lifetime_usetime is set to the "first" + * time of use, which is actually correct according to PF_KEY. We have + * to query policies for the last usetime. */ *time = 0; #endif /* !__APPLE__ */ } @@ -3308,6 +3318,7 @@ kernel_pfkey_ipsec_t *kernel_pfkey_ipsec_create() INIT(this, .public = { .interface = { + .get_features = _get_features, .get_spi = _get_spi, .get_cpi = _get_cpi, .add_sa = _add_sa, diff --git a/src/libcharon/sa/child_sa.c b/src/libcharon/sa/child_sa.c index 8f101e1ff..d76d7aebc 100644 --- a/src/libcharon/sa/child_sa.c +++ b/src/libcharon/sa/child_sa.c @@ -759,19 +759,19 @@ METHOD(child_sa_t, get_usestats, void, private_child_sa_t *this, bool inbound, time_t *time, uint64_t *bytes, uint64_t *packets) { - if ((!bytes && !packets) || update_usebytes(this, inbound) != FAILED) + status_t status = NOT_SUPPORTED; + bool sa_use_time; + + sa_use_time = charon->kernel->get_features(charon->kernel) & KERNEL_SA_USE_TIME; + + if (bytes || packets || sa_use_time) { - /* there was traffic since last update or the kernel interface - * does not support querying the number of usebytes. - */ - if (time) - { - if (!update_usetime(this, inbound) && !bytes && !packets) - { - /* if policy query did not yield a usetime, query SAs instead */ - update_usebytes(this, inbound); - } - } + status = update_usebytes(this, inbound); + } + if (time && !sa_use_time && status != FAILED) + { /* query policies only if last use time is not available from SAs and + * there was either traffic or querying the SA wasn't supported */ + update_usetime(this, inbound); } if (time) {