From b9131c34d3d99000800a7b7115d4162029602dfd Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 23 Jun 2022 15:59:56 +0200 Subject: [PATCH 1/5] kernel-interface: Add feature to indicate if query_sa() returns last use time Currently supported by libipsec and PF_KEY on macOS (FreeBSD, like Linux, reports the time the SA was first used in sadb_lifetime_usetime - it also triggers rekeyings based on that, which Linux doesn't, it also triggers them if an SA is never used). --- .../jni/libandroidbridge/kernel/android_ipsec.c | 7 +++++++ src/libcharon/kernel/kernel_interface.h | 13 ++++++++++--- src/libcharon/kernel/kernel_ipsec.h | 11 +++++++---- .../kernel_libipsec/kernel_libipsec_ipsec.c | 3 ++- .../plugins/kernel_pfkey/kernel_pfkey_ipsec.c | 17 ++++++++++++++--- 5 files changed, 40 insertions(+), 11 deletions(-) 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..1a47f6d8d 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,12 @@ 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_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 +172,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/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_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, From 6301b880df0ac3ed8d1dbf656252cc18d38f3090 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 23 Jun 2022 16:13:02 +0200 Subject: [PATCH 2/5] child-sa: Query policies only if querying SAs doesn't update the use time --- src/libcharon/sa/child_sa.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) 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) { From e21290ec30c7aa9941a9860d496b4786677802c2 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 23 Jun 2022 16:15:45 +0200 Subject: [PATCH 3/5] kernel-netlink: Read last use time from SA if possible Since 6.2 the Linux kernel updates the last use time per SA. In previous releases the attribute was only updated and reported for specific outbound IPv6 SAs. Using this reduces the number of kernel queries per CHILD_SA: for DPDs from two policy queries (IN/FWD) to a single query of the inbound SA, and for status reports the three policy queries (IN/FWD/OUT) can be omitted and only the two SAs have to be queried. For NAT keepalives the number of queries doesn't change but a policy query (OUT) is replaced by a query for the outbound SA. While we could use the existence of the attribute as indicator for its support, we don't know this until we queried an SA. By using a version check we can announce the feature from the start. --- src/include/linux/xfrm.h | 2 +- .../kernel_netlink/kernel_netlink_ipsec.c | 79 +++++++++++++++++-- 2 files changed, 73 insertions(+), 8 deletions(-) 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/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)); From 1138b629fb06328492782e5e7610b34f695ebc9e Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 23 Jun 2022 16:38:02 +0200 Subject: [PATCH 4/5] android: Announce support for TFC padding Has been supported by libipsec for a long time (since 5.1.1). UDP encap is already enforced via config, this just makes the flags the same as in kernel-libipsec. --- .../app/src/main/jni/libandroidbridge/kernel/android_ipsec.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 1a47f6d8d..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 @@ -49,7 +49,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_android_ipsec_t *this) { - return KERNEL_SA_USE_TIME; + return KERNEL_REQUIRE_UDP_ENCAPSULATION | KERNEL_ESP_V3_TFC | + KERNEL_SA_USE_TIME; } METHOD(kernel_ipsec_t, get_spi, status_t, From 346a050c3637d06a74376066f16c5e61e3b23774 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 11 Jan 2023 17:05:37 +0100 Subject: [PATCH 5/5] kernel-netlink: Increase log level for dumped Netlink messages Some of these contain key material so they should be logged on level 4. --- src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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);