kernel-netlink: Log extended ACK error/warning messages

With newer kernels (basic support for extended ACKs is there since 4.12
but some messages for XFRM were only added with 6.1) this gives more
detailed error messages to the user than e.g. a generic EINVAL or ENOSYS
error would.

Also enabled omitting the request payload in NLMSG_ERROR messages.
This commit is contained in:
Tobias Brunner
2022-12-21 16:21:49 +01:00
parent 17fd304e60
commit 7988aea7d8
4 changed files with 143 additions and 27 deletions
@@ -1217,9 +1217,7 @@ static status_t get_spi_internal(private_kernel_netlink_ipsec_t *this,
}
case NLMSG_ERROR:
{
struct nlmsgerr *err = NLMSG_DATA(hdr);
DBG1(DBG_KNL, "allocating SPI failed: %s (%d)",
strerror(-err->error), -err->error);
netlink_log_error(hdr, "allocating SPI failed");
break;
}
default:
@@ -2099,9 +2097,8 @@ static void get_replay_state(private_kernel_netlink_ipsec_t *this,
}
case NLMSG_ERROR:
{
struct nlmsgerr *err = NLMSG_DATA(hdr);
DBG1(DBG_KNL, "querying replay state from SAD entry "
"failed: %s (%d)", strerror(-err->error), -err->error);
netlink_log_error(hdr, "querying replay state from SAD "
"entry failed");
break;
}
default:
@@ -2201,11 +2198,7 @@ METHOD(kernel_ipsec_t, query_sa, status_t,
}
case NLMSG_ERROR:
{
struct nlmsgerr *err = NLMSG_DATA(hdr);
DBG1(DBG_KNL, "querying SAD entry with SPI %.8x%s failed: "
"%s (%d)", ntohl(id->spi), markstr,
strerror(-err->error), -err->error);
netlink_log_error(hdr, "querying SAD entry failed");
break;
}
default:
@@ -2391,9 +2384,7 @@ METHOD(kernel_ipsec_t, update_sa, status_t,
}
case NLMSG_ERROR:
{
struct nlmsgerr *err = NLMSG_DATA(hdr);
DBG1(DBG_KNL, "querying SAD entry failed: %s (%d)",
strerror(-err->error), -err->error);
netlink_log_error(hdr, "querying SAD entry failed");
break;
}
default:
@@ -3086,9 +3077,7 @@ METHOD(kernel_ipsec_t, query_policy, status_t,
}
case NLMSG_ERROR:
{
struct nlmsgerr *err = NLMSG_DATA(hdr);
DBG1(DBG_KNL, "querying policy failed: %s (%d)",
strerror(-err->error), -err->error);
netlink_log_error(hdr, "querying policy failed");
break;
}
default:
@@ -3609,9 +3598,7 @@ static bool get_spd_hash_thresh(private_kernel_netlink_ipsec_t *this,
}
case NLMSG_ERROR:
{
struct nlmsgerr *err = NLMSG_DATA(hdr);
DBG1(DBG_KNL, "getting SPD hash threshold failed: %s (%d)",
strerror(-err->error), -err->error);
netlink_log_error(hdr, "getting SPD hash threshold failed");
break;
}
default:
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2008-2022 Tobias Brunner
* Copyright (C) 2014 Martin Willi
* Copyright (C) 2008-2020 Tobias Brunner
*
* Copyright (C) secunet Security Networks AG
*
@@ -535,7 +535,7 @@ METHOD(netlink_socket_t, netlink_send_ack, status_t,
{
case NLMSG_ERROR:
{
struct nlmsgerr* err = NLMSG_DATA(hdr);
struct nlmsgerr *err = NLMSG_DATA(hdr);
if (err->error)
{
@@ -549,11 +549,11 @@ METHOD(netlink_socket_t, netlink_send_ack, status_t,
free(out);
return NOT_FOUND;
}
DBG1(DBG_KNL, "received netlink error: %s (%d)",
strerror(-err->error), -err->error);
netlink_log_error(hdr, NULL);
free(out);
return FAILED;
}
netlink_log_error(hdr, NULL);
free(out);
return SUCCESS;
}
@@ -620,7 +620,7 @@ netlink_socket_t *netlink_socket_create(int protocol, enum_name_t *names,
.nl_family = AF_NETLINK,
};
bool force_buf = FALSE;
int rcvbuf_size = 0;
int on = 1, rcvbuf_size = 0;
INIT(this,
.public = {
@@ -659,6 +659,13 @@ netlink_socket_t *netlink_socket_create(int protocol, enum_name_t *names,
destroy(this);
return NULL;
}
/* don't echo back the request payload in error messages, might not be
* supported by older kernels, so don't check the result */
setsockopt(this->socket, SOL_NETLINK, NETLINK_CAP_ACK, &on, sizeof(on));
/* enable extended ACK attributes, might not be supported by older kernels */
setsockopt(this->socket, SOL_NETLINK, NETLINK_EXT_ACK, &on, sizeof(on));
rcvbuf_size = lib->settings->get_int(lib->settings,
"%s.plugins.kernel-netlink.receive_buffer_size",
rcvbuf_size, lib->ns);
@@ -766,6 +773,69 @@ void *netlink_reserve(struct nlmsghdr *hdr, int buflen, int type, int len)
return RTA_DATA(rta);
}
/*
* Described in header
*/
void netlink_log_error(struct nlmsghdr *hdr, const char *prefix)
{
struct nlmsgerr *err = NLMSG_DATA(hdr);
struct rtattr *rta;
size_t offset, rtasize;
const char *msg = NULL;
bool is_error = err->error != 0;
if (!prefix)
{
prefix = is_error ? "received netlink error"
: "received netlink warning";
}
if (hdr->nlmsg_flags & NLM_F_ACK_TLVS)
{
/* skip the headers, and the request payload for older kernels that
* don't support omitting it */
offset = sizeof(*err);
if (!(hdr->nlmsg_flags & NLM_F_CAPPED))
{
offset += err->msg.nlmsg_len - NLMSG_HDRLEN;
}
rta = (struct rtattr*)(NLMSG_DATA(hdr) + NLMSG_ALIGN(offset));
rtasize = NLMSG_PAYLOAD(hdr, offset);
while (RTA_OK(rta, rtasize))
{
if (rta->rta_type == NLMSGERR_ATTR_MSG)
{
msg = RTA_DATA(rta);
/* sanity check, strings from the kernel should be terminated */
if (!RTA_PAYLOAD(rta) || msg[RTA_PAYLOAD(rta)-1] != '\0')
{
msg = NULL;
}
break;
}
rta = RTA_NEXT(rta, rtasize);
}
}
if (msg && *msg)
{
if (is_error)
{
DBG1(DBG_KNL, "%s: %s (%d)", prefix, msg, -err->error);
}
else
{
DBG2(DBG_KNL, "%s: %s", prefix, msg);
}
}
else if (is_error)
{
DBG1(DBG_KNL, "%s: %s (%d)", prefix, strerror(-err->error),
-err->error);
}
}
/*
* Described in header
*/
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2008-2020 Tobias Brunner
* Copyright (C) 2008-2022 Tobias Brunner
*
* Copyright (C) secunet Security Networks AG
*
@@ -122,7 +122,17 @@ void netlink_nested_end(struct nlmsghdr *hdr, void *attr);
* @param len length of RTA data
* @return buffer to len bytes of attribute data, NULL on error
*/
void* netlink_reserve(struct nlmsghdr *hdr, int buflen, int type, int len);
void *netlink_reserve(struct nlmsghdr *hdr, int buflen, int type, int len);
/**
* Log extended ACK error/warning message in a NLMSG_ERROR message. In error
* messages (i.e. error != 0), the generic error message is logged if no
* extended ACK message is available.
*
* @param hdr netlink message
* @param prefix optional prefix to add before error message
*/
void netlink_log_error(struct nlmsghdr *hdr, const char *prefix);
/**
* Determine buffer size for received messages (e.g. events).