From: Tobias Brunner Date: Thu, 23 Jun 2022 14:15:45 +0000 (+0200) Subject: kernel-netlink: Read last use time from SA if possible X-Git-Tag: 5.9.10rc1~8^2~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e21290ec30c7aa9941a9860d496b4786677802c2;p=thirdparty%2Fstrongswan.git 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. --- diff --git a/src/include/linux/xfrm.h b/src/include/linux/xfrm.h index 094772dfbc..80630dfd4a 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 1e7ecef172..6580c91384 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));