#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
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
#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;
}
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:
}
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:
}
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:
}
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:
}
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:
}
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:
/*
+ * Copyright (C) 2008-2022 Tobias Brunner
* Copyright (C) 2014 Martin Willi
- * Copyright (C) 2008-2020 Tobias Brunner
*
* Copyright (C) secunet Security Networks AG
*
{
case NLMSG_ERROR:
{
- struct nlmsgerr* err = NLMSG_DATA(hdr);
+ struct nlmsgerr *err = NLMSG_DATA(hdr);
if (err->error)
{
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;
}
.nl_family = AF_NETLINK,
};
bool force_buf = FALSE;
- int rcvbuf_size = 0;
+ int on = 1, rcvbuf_size = 0;
INIT(this,
.public = {
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);
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
*/
/*
- * Copyright (C) 2008-2020 Tobias Brunner
+ * Copyright (C) 2008-2022 Tobias Brunner
*
* Copyright (C) secunet Security Networks AG
*
* @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).