From: Tobias Brunner Date: Tue, 20 Dec 2022 16:36:51 +0000 (+0100) Subject: kernel-netlink: Log extended ACK error/warning messages X-Git-Tag: 5.9.9rc1~3 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=7988aea7d80a87f91a37f3a79f46d8b1945d985c;p=thirdparty%2Fstrongswan.git 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. --- diff --git a/src/include/linux/netlink.h b/src/include/linux/netlink.h index 777a1b7da9..b2da318b64 100644 --- a/src/include/linux/netlink.h +++ b/src/include/linux/netlink.h @@ -67,6 +67,14 @@ struct nlmsghdr { #define NLM_F_CREATE 0x400 /* Create, if it does not exist */ #define NLM_F_APPEND 0x800 /* Add to end of list */ +/* Modifiers to DELETE request */ +#define NLM_F_NONREC 0x100 /* Do not delete recursively */ +#define NLM_F_BULK 0x200 /* Delete multiple objects */ + +/* Flags for ACK message */ +#define NLM_F_CAPPED 0x100 /* request was capped */ +#define NLM_F_ACK_TLVS 0x200 /* extended ACK TVLs were included */ + /* 4.4BSD ADD NLM_F_CREATE|NLM_F_EXCL 4.4BSD CHANGE NLM_F_REPLACE @@ -99,6 +107,45 @@ struct nlmsghdr { struct nlmsgerr { int error; struct nlmsghdr msg; + /* + * followed by the message contents unless NETLINK_CAP_ACK was set + * or the ACK indicates success (error == 0) + * message length is aligned with NLMSG_ALIGN() + */ + /* + * followed by TLVs defined in enum nlmsgerr_attrs + * if NETLINK_EXT_ACK was set + */ +}; + +/** + * enum nlmsgerr_attrs - nlmsgerr attributes + * @NLMSGERR_ATTR_UNUSED: unused + * @NLMSGERR_ATTR_MSG: error message string (string) + * @NLMSGERR_ATTR_OFFS: offset of the invalid attribute in the original + * message, counting from the beginning of the header (u32) + * @NLMSGERR_ATTR_COOKIE: arbitrary subsystem specific cookie to + * be used - in the success case - to identify a created + * object or operation or similar (binary) + * @NLMSGERR_ATTR_POLICY: policy for a rejected attribute + * @NLMSGERR_ATTR_MISS_TYPE: type of a missing required attribute, + * %NLMSGERR_ATTR_MISS_NEST will not be present if the attribute was + * missing at the message level + * @NLMSGERR_ATTR_MISS_NEST: offset of the nest where attribute was missing + * @__NLMSGERR_ATTR_MAX: number of attributes + * @NLMSGERR_ATTR_MAX: highest attribute number + */ +enum nlmsgerr_attrs { + NLMSGERR_ATTR_UNUSED, + NLMSGERR_ATTR_MSG, + NLMSGERR_ATTR_OFFS, + NLMSGERR_ATTR_COOKIE, + NLMSGERR_ATTR_POLICY, + NLMSGERR_ATTR_MISS_TYPE, + NLMSGERR_ATTR_MISS_NEST, + + __NLMSGERR_ATTR_MAX, + NLMSGERR_ATTR_MAX = __NLMSGERR_ATTR_MAX - 1 }; #define NETLINK_ADD_MEMBERSHIP 1 @@ -110,6 +157,8 @@ struct nlmsgerr { #define NETLINK_TX_RING 7 #define NETLINK_LISTEN_ALL_NSID 8 #define NETLINK_LIST_MEMBERSHIPS 9 +#define NETLINK_CAP_ACK 10 +#define NETLINK_EXT_ACK 11 struct nl_pktinfo { __u32 group; diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c index 6f7b50fb7a..38670c41d7 100644 --- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c +++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c @@ -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: diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.c index 4baf457939..95cb00f4c2 100644 --- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.c +++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.c @@ -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 */ diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.h b/src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.h index 816b53de73..cec3e5e5b9 100644 --- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.h +++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.h @@ -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).