]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
kernel-netlink: Log extended ACK error/warning messages
authorTobias Brunner <tobias@strongswan.org>
Tue, 20 Dec 2022 16:36:51 +0000 (17:36 +0100)
committerTobias Brunner <tobias@strongswan.org>
Wed, 21 Dec 2022 15:21:49 +0000 (16:21 +0100)
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.

src/include/linux/netlink.h
src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c
src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.c
src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.h

index 777a1b7da98efdb2b4569ef1a0c9e3347e9f56af..b2da318b6426903fd342f8c3f16f52e9a6205b0b 100644 (file)
@@ -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;
index 6f7b50fb7a258d678eb94c231fa94d143f40142d..38670c41d77cd0fd81e6d5e5f8a63e8d2521591e 100644 (file)
@@ -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:
index 4baf457939026332904fd31280787824da7e6239..95cb00f4c292de980cfeb09de00b316b0a0ab3f7 100644 (file)
@@ -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 nlmsgerrerr = 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
  */
index 816b53de7338d6ef6fd82c60d9d07210354c5a26..cec3e5e5b9263a019533de5a21a79ac6075e3af2 100644 (file)
@@ -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).