Merge branch 'kernel-netlink-sa-lastused'

Adds support for a change in Linux kernel 6.2 that allows retrieving
the last use time of an SA from the SA itself instead of having to query
the policies.
This commit is contained in:
Tobias Brunner
2023-02-22 13:21:31 +01:00
9 changed files with 128 additions and 33 deletions
@@ -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,
+1 -1
View File
@@ -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 */
+10 -3
View File
@@ -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,
+7 -4
View File
@@ -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,
@@ -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,
@@ -44,6 +44,7 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/utsname.h>
#include <stdint.h>
#include <linux/ipsec.h>
#include <linux/netlink.h>
@@ -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));
@@ -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);
@@ -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,
+12 -12
View File
@@ -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)
{